fix(linter): add jest to root .eslintrc if selected as unit test runner (#11555)

This commit is contained in:
Miroslav Jonaš 2022-08-18 14:51:09 +02:00 committed by GitHub
parent 99a3563c8f
commit 187f5200c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 141 additions and 110 deletions

View File

@ -12,6 +12,7 @@ export async function addLintingGenerator(
): Promise<GeneratorCallback> {
const installTask = lintInitGenerator(tree, {
linter: Linter.EsLint,
unitTestRunner: options.unitTestRunner,
skipPackageJson: options.skipPackageJson,
});

View File

@ -5,4 +5,5 @@ export interface AddLintingGeneratorSchema {
setParserOptionsProject?: boolean;
skipFormat?: boolean;
skipPackageJson?: boolean;
unitTestRunner?: string;
}

View File

@ -48,6 +48,7 @@ export async function addE2e(tree: Tree, options: NormalizedSchema) {
eslintFilePatterns: [
joinPathFragments(options.e2eProjectRoot, '**/*.ts'),
],
unitTestRunner: options.unitTestRunner,
skipFormat: true,
setParserOptionsProject: options.setParserOptionsProject,
skipPackageJson: options.skipPackageJson,

View File

@ -14,5 +14,6 @@ export async function addLinting(host: Tree, options: NormalizedSchema) {
prefix: options.prefix,
setParserOptionsProject: options.setParserOptionsProject,
skipPackageJson: options.skipPackageJson,
unitTestRunner: options.unitTestRunner,
});
}

View File

@ -168,6 +168,7 @@ async function addLinting(host: Tree, options: NormalizedSchema) {
projectName: options.name,
projectRoot: options.projectRoot,
prefix: options.prefix,
unitTestRunner: options.unitTestRunner,
setParserOptionsProject: options.setParserOptionsProject,
skipFormat: true,
});

View File

@ -96,7 +96,7 @@ export async function migrateFromAngularCli(
createRootKarmaConfig(tree);
}
if (workspaceCapabilities.eslint) {
updateRootEsLintConfig(tree, eslintConfig);
updateRootEsLintConfig(tree, eslintConfig, options.unitTestRunner);
cleanupEsLintPackages(tree);
}

View File

@ -310,6 +310,7 @@ export class E2eMigrator extends ProjectMigrator<SupportedTargets> {
project: this.project.name,
linter: Linter.EsLint,
eslintFilePatterns: [`${this.project.newRoot}/**/*.{js,ts}`],
unitTestRunner: this.options.unitTestRunner,
tsConfigPaths: [
joinPathFragments(this.project.newRoot, 'tsconfig.json'),
],

View File

@ -163,7 +163,8 @@ export function updatePackageJson(tree: Tree): void {
export function updateRootEsLintConfig(
tree: Tree,
existingEsLintConfig: any | undefined
existingEsLintConfig: any | undefined,
unitTestRunner?: string
): void {
if (tree.exists('.eslintrc.json')) {
/**
@ -175,7 +176,7 @@ export function updateRootEsLintConfig(
tree.delete('.eslintrc.json');
}
lintInitGenerator(tree, { linter: Linter.EsLint });
lintInitGenerator(tree, { linter: Linter.EsLint, unitTestRunner });
if (!existingEsLintConfig) {
// There was no eslint config in the root, so we keep the generated one as-is.

View File

@ -155,6 +155,7 @@ export function addLint(
tsConfigPaths: [
joinPathFragments(options.projectRoot, 'tsconfig.lib.json'),
],
unitTestRunner: options.unitTestRunner,
eslintFilePatterns: [
`${options.projectRoot}/**/*.${options.js ? 'js' : 'ts'}`,
],

View File

@ -16,9 +16,11 @@ import {
import { Linter } from '../utils/linter';
import { containsEslint } from '../utils/eslint-file';
import { ESLint } from 'eslint';
export interface LinterInitOptions {
linter?: Linter;
unitTestRunner?: string;
skipPackageJson?: boolean;
}
@ -86,66 +88,78 @@ const globalTsLintConfiguration = {
},
};
const globalEsLintConfiguration = {
root: true,
ignorePatterns: ['**/*'],
plugins: ['@nrwl/nx'],
/**
* We leverage ESLint's "overrides" capability so that we can set up a root config which will support
* all permutations of Nx workspaces across all frameworks, libraries and tools.
*
* The key point is that we need entirely different ESLint config to apply to different types of files,
* but we still want to share common config where possible.
*/
overrides: [
const getGlobalEsLintConfiguration = (unitTestRunner?: string) => {
const config: ESLint.ConfigData = {
root: true,
ignorePatterns: ['**/*'],
plugins: ['@nrwl/nx'],
/**
* This configuration is intended to apply to all "source code" (but not
* markup like HTML, or other custom file types like GraphQL)
* We leverage ESLint's "overrides" capability so that we can set up a root config which will support
* all permutations of Nx workspaces across all frameworks, libraries and tools.
*
* The key point is that we need entirely different ESLint config to apply to different types of files,
* but we still want to share common config where possible.
*/
{
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
rules: {
'@nrwl/nx/enforce-module-boundaries': [
'error',
{
enforceBuildableLibDependency: true,
allow: [],
depConstraints: [
{ sourceTag: '*', onlyDependOnLibsWithTags: ['*'] },
],
},
],
overrides: [
/**
* This configuration is intended to apply to all "source code" (but not
* markup like HTML, or other custom file types like GraphQL)
*/
{
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
rules: {
'@nrwl/nx/enforce-module-boundaries': [
'error',
{
enforceBuildableLibDependency: true,
allow: [],
depConstraints: [
{ sourceTag: '*', onlyDependOnLibsWithTags: ['*'] },
],
},
],
},
},
},
/**
* This configuration is intended to apply to all TypeScript source files.
* See the eslint-plugin-nx package for what is in the referenced shareable config.
*/
{
files: ['*.ts', '*.tsx'],
extends: ['plugin:@nrwl/nx/typescript'],
/**
* Having an empty rules object present makes it more obvious to the user where they would
* extend things from if they needed to
* This configuration is intended to apply to all TypeScript source files.
* See the eslint-plugin-nx package for what is in the referenced shareable config.
*/
rules: {},
},
{
files: ['*.ts', '*.tsx'],
extends: ['plugin:@nrwl/nx/typescript'],
/**
* Having an empty rules object present makes it more obvious to the user where they would
* extend things from if they needed to
*/
rules: {},
},
/**
* This configuration is intended to apply to all JavaScript source files.
* See the eslint-plugin-nx package for what is in the referenced shareable config.
*/
{
files: ['*.js', '*.jsx'],
extends: ['plugin:@nrwl/nx/javascript'],
/**
* Having an empty rules object present makes it more obvious to the user where they would
* extend things from if they needed to
* This configuration is intended to apply to all JavaScript source files.
* See the eslint-plugin-nx package for what is in the referenced shareable config.
*/
{
files: ['*.js', '*.jsx'],
extends: ['plugin:@nrwl/nx/javascript'],
/**
* Having an empty rules object present makes it more obvious to the user where they would
* extend things from if they needed to
*/
rules: {},
},
],
};
if (unitTestRunner === 'jest') {
config.overrides.push({
files: ['*.spec.ts', '*.spec.tsx', '*.spec.js', '*.spec.jsx'],
env: {
jest: true,
},
rules: {},
},
],
});
}
return config;
};
function initTsLint(tree: Tree, options: LinterInitOptions): GeneratorCallback {
@ -175,7 +189,11 @@ function initEsLint(tree: Tree, options: LinterInitOptions): GeneratorCallback {
removeDependenciesFromPackageJson(tree, ['@nrwl/linter'], []);
}
writeJson(tree, '.eslintrc.json', globalEsLintConfiguration);
writeJson(
tree,
'.eslintrc.json',
getGlobalEsLintConfiguration(options.unitTestRunner)
);
if (tree.exists('.vscode/extensions.json')) {
updateJson(tree, '.vscode/extensions.json', (json) => {

View File

@ -20,6 +20,7 @@ interface LintProjectOptions {
skipFormat: boolean;
setParserOptionsProject?: boolean;
skipPackageJson?: boolean;
unitTestRunner?: string;
}
function createTsLintConfiguration(
@ -90,6 +91,7 @@ export async function lintProjectGenerator(
) {
const installTask = lintInitGenerator(tree, {
linter: options.linter,
unitTestRunner: options.unitTestRunner,
skipPackageJson: options.skipPackageJson,
});
const projectConfig = readProjectConfiguration(tree, options.project);

View File

@ -20,6 +20,7 @@ export async function addLinting(
tsConfigPaths: [
joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'),
],
unitTestRunner: options.unitTestRunner,
eslintFilePatterns: [`${options.appProjectRoot}/**/*.{ts,tsx,js,jsx}`],
skipFormat: true,
});

View File

@ -187,6 +187,7 @@ export async function addLintingToApplication(
eslintFilePatterns: [
`${options.appProjectRoot}/**/*.${options.js ? 'js' : 'ts'}`,
],
unitTestRunner: options.unitTestRunner,
skipFormat: true,
setParserOptionsProject: options.setParserOptionsProject,
});

View File

@ -9,8 +9,8 @@ import {
convertNxGenerator,
Tree,
formatFiles,
joinPathFragments,
GeneratorCallback,
joinPathFragments,
} from '@nrwl/devkit';
import { normalizeOptions } from './lib/normalize-options';
import initGenerator from '../init/init';
@ -29,14 +29,14 @@ export async function reactNativeApplicationGenerator(
addProject(host, options);
const initTask = await initGenerator(host, { ...options, skipFormat: true });
const lintTask = await addLinting(
host,
options.projectName,
options.appProjectRoot,
[joinPathFragments(options.appProjectRoot, 'tsconfig.app.json')],
options.linter,
options.setParserOptionsProject
);
const lintTask = await addLinting(host, {
...options,
projectRoot: options.appProjectRoot,
tsConfigPaths: [
joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'),
],
});
const jestTask = await addJest(
host,
options.unitTestRunner,

View File

@ -44,14 +44,13 @@ export async function reactNativeLibraryGenerator(
e2eTestRunner: 'none',
});
const lintTask = await addLinting(
host,
options.name,
options.projectRoot,
[joinPathFragments(options.projectRoot, 'tsconfig.lib.json')],
options.linter,
options.setParserOptionsProject
);
const lintTask = await addLinting(host, {
...options,
projectName: options.name,
tsConfigPaths: [
joinPathFragments(options.projectRoot, 'tsconfig.lib.json'),
],
});
if (!options.skipTsConfig) {
updateBaseTsConfig(host, options);

View File

@ -16,13 +16,12 @@ describe('Add Linting', () => {
});
it('should add update `workspace.json` file properly when eslint is passed', () => {
addLinting(
tree,
'my-lib',
'libs/my-lib',
['libs/my-lib/tsconfig.lib.json'],
Linter.EsLint
);
addLinting(tree, {
projectName: 'my-lib',
linter: Linter.EsLint,
tsConfigPaths: ['libs/my-lib/tsconfig.lib.json'],
projectRoot: 'libs/my-lib',
});
const project = readProjectConfiguration(tree, 'my-lib');
expect(project.targets.lint).toBeDefined();
@ -30,13 +29,12 @@ describe('Add Linting', () => {
});
it('should add update `workspace.json` file properly when tslint is passed', () => {
addLinting(
tree,
'my-lib',
'libs/my-lib',
['libs/my-lib/tsconfig.lib.json'],
Linter.TsLint
);
addLinting(tree, {
projectName: 'my-lib',
linter: Linter.TsLint,
tsConfigPaths: ['libs/my-lib/tsconfig.lib.json'],
projectRoot: 'libs/my-lib',
});
const project = readProjectConfiguration(tree, 'my-lib');
expect(project.targets.lint).toBeDefined();
@ -46,13 +44,12 @@ describe('Add Linting', () => {
});
it('should not add lint target when "none" is passed', async () => {
addLinting(
tree,
'my-lib',
'libs/my-lib',
['libs/my-lib/tsconfig.lib.json'],
Linter.None
);
addLinting(tree, {
projectName: 'my-lib',
linter: Linter.None,
tsConfigPaths: ['libs/my-lib/tsconfig.lib.json'],
projectRoot: 'libs/my-lib',
});
const project = readProjectConfiguration(tree, 'my-lib');
expect(project.targets.lint).toBeUndefined();

View File

@ -9,38 +9,39 @@ import {
import { extraEslintDependencies, createReactEslintJson } from '@nrwl/react';
import type { Linter as ESLintLinter } from 'eslint';
export async function addLinting(
host: Tree,
projectName: string,
appProjectRoot: string,
tsConfigPaths: string[],
linter: Linter,
setParserOptionsProject?: boolean
) {
if (linter === Linter.None) {
interface NormalizedSchema {
linter?: Linter;
projectName: string;
projectRoot: string;
setParserOptionsProject?: boolean;
tsConfigPaths: string[];
}
export async function addLinting(host: Tree, options: NormalizedSchema) {
if (options.linter === Linter.None) {
return () => {};
}
const lintTask = await lintProjectGenerator(host, {
linter,
project: projectName,
tsConfigPaths,
eslintFilePatterns: [`${appProjectRoot}/**/*.{ts,tsx,js,jsx}`],
linter: options.linter,
project: options.projectName,
tsConfigPaths: options.tsConfigPaths,
eslintFilePatterns: [`${options.projectRoot}/**/*.{ts,tsx,js,jsx}`],
skipFormat: true,
});
if (linter === Linter.TsLint) {
if (options.linter === Linter.TsLint) {
return () => {};
}
const reactEslintJson = createReactEslintJson(
appProjectRoot,
setParserOptionsProject
options.projectRoot,
options.setParserOptionsProject
);
updateJson(
host,
joinPathFragments(appProjectRoot, '.eslintrc.json'),
joinPathFragments(options.projectRoot, '.eslintrc.json'),
(json: ESLintLinter.Config) => {
json = reactEslintJson;
json.ignorePatterns = ['!**/*', 'public', '.cache', 'node_modules'];

View File

@ -35,6 +35,7 @@ async function addLinting(host: Tree, options: NormalizedSchema) {
tsConfigPaths: [
joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'),
],
unitTestRunner: options.unitTestRunner,
eslintFilePatterns: [`${options.appProjectRoot}/**/*.{ts,tsx,js,jsx}`],
skipFormat: true,
});

View File

@ -147,6 +147,7 @@ async function addLinting(host: Tree, options: NormalizedSchema) {
tsConfigPaths: [
joinPathFragments(options.projectRoot, 'tsconfig.lib.json'),
],
unitTestRunner: options.unitTestRunner,
eslintFilePatterns: [`${options.projectRoot}/**/*.{ts,tsx,js,jsx}`],
skipFormat: true,
});

View File

@ -206,6 +206,7 @@ export async function applicationGenerator(host: Tree, schema: Schema) {
tsConfigPaths: [
joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'),
],
unitTestRunner: options.unitTestRunner,
eslintFilePatterns: [`${options.appProjectRoot}/**/*.ts`],
skipFormat: true,
setParserOptionsProject: options.setParserOptionsProject,

View File

@ -81,6 +81,7 @@ export function addLint(
tsConfigPaths: [
joinPathFragments(options.projectRoot, 'tsconfig.lib.json'),
],
unitTestRunner: options.unitTestRunner,
eslintFilePatterns: [
`${options.projectRoot}/**/*.${options.js ? 'js' : 'ts'}`,
],