fix(node): fix standalone linter setup (#15681)

This commit is contained in:
Miroslav Jonaš 2023-03-22 19:17:40 +01:00 committed by GitHub
parent e744f0429f
commit 4bf652d2e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 141 additions and 100 deletions

View File

@ -443,68 +443,81 @@ describe('Linter', () => {
beforeEach(() => newProject()); beforeEach(() => newProject());
afterEach(() => cleanupProject()); afterEach(() => cleanupProject());
function verifySuccessfulStandaloneSetup(myapp: string) {
expect(runCLI(`lint ${myapp}`, { silenceError: true })).toContain(
'All files pass linting'
);
expect(runCLI(`lint e2e`, { silenceError: true })).toContain(
'All files pass linting'
);
expect(() => checkFilesExist(`.eslintrc.base.json`)).toThrow();
const rootEslint = readJson('.eslintrc.json');
const e2eEslint = readJson('e2e/.eslintrc.json');
// should directly refer to nx plugin
expect(rootEslint.plugins).toEqual(['@nrwl/nx']);
expect(e2eEslint.plugins).toEqual(['@nrwl/nx']);
}
function verifySuccessfulMigratedSetup(myapp: string, mylib: string) {
expect(runCLI(`lint ${myapp}`, { silenceError: true })).toContain(
'All files pass linting'
);
expect(runCLI(`lint e2e`, { silenceError: true })).toContain(
'All files pass linting'
);
expect(runCLI(`lint ${mylib}`, { silenceError: true })).toContain(
'All files pass linting'
);
expect(() => checkFilesExist(`.eslintrc.base.json`)).not.toThrow();
const rootEslint = readJson('.eslintrc.base.json');
const appEslint = readJson('.eslintrc.json');
const e2eEslint = readJson('e2e/.eslintrc.json');
const libEslint = readJson(`libs/${mylib}/.eslintrc.json`);
// should directly refer to nx plugin
expect(rootEslint.plugins).toEqual(['@nrwl/nx']);
expect(appEslint.plugins).toBeUndefined();
expect(e2eEslint.plugins).toBeUndefined();
expect(libEslint.plugins).toBeUndefined();
// should extend base
expect(appEslint.extends.slice(-1)).toEqual(['./.eslintrc.base.json']);
expect(e2eEslint.extends.slice(-1)).toEqual(['../.eslintrc.base.json']);
expect(libEslint.extends.slice(-1)).toEqual([
'../../.eslintrc.base.json',
]);
}
it('(React standalone) should set root project config to app and e2e app and migrate when another lib is added', () => { it('(React standalone) should set root project config to app and e2e app and migrate when another lib is added', () => {
const myapp = uniq('myapp'); const myapp = uniq('myapp');
const mylib = uniq('mylib'); const mylib = uniq('mylib');
runCLI(`generate @nrwl/react:app ${myapp} --rootProject=true`); runCLI(`generate @nrwl/react:app ${myapp} --rootProject=true`);
verifySuccessfulStandaloneSetup(myapp);
let rootEslint = readJson('.eslintrc.json'); let appEslint = readJson('.eslintrc.json');
let e2eEslint = readJson('e2e/.eslintrc.json'); let e2eEslint = readJson('e2e/.eslintrc.json');
expect(() => checkFilesExist(`.eslintrc.base.json`)).toThrow();
// should directly refer to nx plugin
expect(rootEslint.plugins).toEqual(['@nrwl/nx']);
expect(e2eEslint.plugins).toEqual(['@nrwl/nx']);
// should only extend framework plugin
expect(rootEslint.extends).toEqual(['plugin:@nrwl/nx/react']);
expect(e2eEslint.extends).toEqual(['plugin:cypress/recommended']);
// should have plugin extends // should have plugin extends
expect(rootEslint.overrides[0].extends).toEqual([ expect(appEslint.overrides[0].extends).toBeDefined();
'plugin:@nrwl/nx/typescript', expect(appEslint.overrides[1].extends).toBeDefined();
]); expect(e2eEslint.overrides[0].extends).toBeDefined();
expect(rootEslint.overrides[1].extends).toEqual([ expect(e2eEslint.overrides[1].extends).toBeDefined();
'plugin:@nrwl/nx/javascript',
]);
expect(e2eEslint.overrides[0].extends).toEqual([
'plugin:@nrwl/nx/typescript',
]);
expect(e2eEslint.overrides[1].extends).toEqual([
'plugin:@nrwl/nx/javascript',
]);
runCLI(`generate @nrwl/react:lib ${mylib} --unitTestRunner=jest`); runCLI(`generate @nrwl/workspace:lib ${mylib} --unitTestRunner=jest`);
// should add new tslint verifySuccessfulMigratedSetup(myapp, mylib);
expect(() => checkFilesExist(`.eslintrc.base.json`)).not.toThrow();
const appEslint = readJson(`.eslintrc.json`); appEslint = readJson(`.eslintrc.json`);
rootEslint = readJson('.eslintrc.base.json');
e2eEslint = readJson('e2e/.eslintrc.json'); e2eEslint = readJson('e2e/.eslintrc.json');
const libEslint = readJson(`libs/${mylib}/.eslintrc.json`);
// should directly refer to nx plugin only in the root
expect(rootEslint.plugins).toEqual(['@nrwl/nx']);
expect(appEslint.plugins).toBeUndefined();
expect(e2eEslint.plugins).toBeUndefined();
// should extend framework plugin and root config
expect(appEslint.extends).toEqual([
'plugin:@nrwl/nx/react',
'./.eslintrc.base.json',
]);
expect(e2eEslint.extends).toEqual([
'plugin:cypress/recommended',
'../.eslintrc.base.json',
]);
expect(libEslint.extends).toEqual([
'plugin:@nrwl/nx/react',
'../../.eslintrc.base.json',
]);
// should have no plugin extends // should have no plugin extends
expect(appEslint.overrides[0].extends).toBeUndefined(); expect(appEslint.overrides[0].extends).toBeUndefined();
expect(appEslint.overrides[1].extends).toBeUndefined(); expect(appEslint.overrides[1].extends).toBeUndefined();
expect(e2eEslint.overrides[0].extends).toBeUndefined(); expect(e2eEslint.overrides[0].extends).toBeUndefined();
expect(e2eEslint.overrides[1].extends).toBeUndefined(); expect(e2eEslint.overrides[1].extends).toBeUndefined();
expect(libEslint.overrides[1].extends).toBeUndefined();
expect(libEslint.overrides[1].extends).toBeUndefined();
}); });
it('(Angular standalone) should set root project config to app and e2e app and migrate when another lib is added', () => { it('(Angular standalone) should set root project config to app and e2e app and migrate when another lib is added', () => {
@ -514,57 +527,23 @@ describe('Linter', () => {
runCLI( runCLI(
`generate @nrwl/angular:app ${myapp} --rootProject=true --no-interactive` `generate @nrwl/angular:app ${myapp} --rootProject=true --no-interactive`
); );
verifySuccessfulStandaloneSetup(myapp);
let rootEslint = readJson('.eslintrc.json'); let appEslint = readJson('.eslintrc.json');
let e2eEslint = readJson('e2e/.eslintrc.json'); let e2eEslint = readJson('e2e/.eslintrc.json');
expect(() => checkFilesExist(`.eslintrc.base.json`)).toThrow();
// should directly refer to nx plugin
expect(rootEslint.plugins).toEqual(['@nrwl/nx']);
expect(e2eEslint.plugins).toEqual(['@nrwl/nx']);
// should only extend framework plugin
expect(e2eEslint.extends).toEqual(['plugin:cypress/recommended']);
// should have plugin extends // should have plugin extends
expect(rootEslint.overrides[0].files).toEqual(['*.ts']); expect(appEslint.overrides[0].extends).toBeDefined();
expect(rootEslint.overrides[0].extends).toEqual([ expect(appEslint.overrides[1].extends).toBeDefined();
'plugin:@nrwl/nx/typescript', expect(e2eEslint.overrides[0].extends).toBeDefined();
'plugin:@nrwl/nx/angular', expect(e2eEslint.overrides[1].extends).toBeDefined();
'plugin:@angular-eslint/template/process-inline-templates',
]);
expect(Object.keys(rootEslint.overrides[0].rules)).toEqual([
'@angular-eslint/directive-selector',
'@angular-eslint/component-selector',
]);
expect(rootEslint.overrides[1].files).toEqual(['*.html']);
expect(rootEslint.overrides[1].extends).toEqual([
'plugin:@nrwl/nx/angular-template',
]);
expect(e2eEslint.overrides[0].extends).toEqual([
'plugin:@nrwl/nx/typescript',
]);
expect(e2eEslint.overrides[1].extends).toEqual([
'plugin:@nrwl/nx/javascript',
]);
runCLI(`generate @nrwl/angular:lib ${mylib} --no-interactive`); runCLI(`generate @nrwl/workspace:lib ${mylib} --no-interactive`);
// should add new tslint verifySuccessfulMigratedSetup(myapp, mylib);
expect(() => checkFilesExist(`.eslintrc.base.json`)).not.toThrow();
const appEslint = readJson(`.eslintrc.json`); appEslint = readJson(`.eslintrc.json`);
rootEslint = readJson('.eslintrc.base.json');
e2eEslint = readJson('e2e/.eslintrc.json'); e2eEslint = readJson('e2e/.eslintrc.json');
const libEslint = readJson(`libs/${mylib}/.eslintrc.json`);
// should directly refer to nx plugin only in the root
expect(rootEslint.plugins).toEqual(['@nrwl/nx']);
expect(appEslint.plugins).toBeUndefined();
expect(e2eEslint.plugins).toBeUndefined();
// should extend framework plugin and root config
expect(appEslint.extends).toEqual(['./.eslintrc.base.json']);
expect(e2eEslint.extends).toEqual([
'plugin:cypress/recommended',
'../.eslintrc.base.json',
]);
expect(libEslint.extends).toEqual(['../../.eslintrc.base.json']);
// should have no plugin extends // should have no plugin extends
expect(appEslint.overrides[0].extends).toEqual([ expect(appEslint.overrides[0].extends).toEqual([
'plugin:@nrwl/nx/angular', 'plugin:@nrwl/nx/angular',
@ -572,10 +551,37 @@ describe('Linter', () => {
]); ]);
expect(e2eEslint.overrides[0].extends).toBeUndefined(); expect(e2eEslint.overrides[0].extends).toBeUndefined();
expect(e2eEslint.overrides[1].extends).toBeUndefined(); expect(e2eEslint.overrides[1].extends).toBeUndefined();
expect(libEslint.overrides[0].extends).toEqual([ });
'plugin:@nrwl/nx/angular',
'plugin:@angular-eslint/template/process-inline-templates', it('(Node standalone) should set root project config to app and e2e app and migrate when another lib is added', () => {
]); const myapp = uniq('myapp');
const mylib = uniq('mylib');
runCLI(
`generate @nrwl/node:app ${myapp} --rootProject=true --no-interactive`
);
verifySuccessfulStandaloneSetup(myapp);
let appEslint = readJson('.eslintrc.json');
let e2eEslint = readJson('e2e/.eslintrc.json');
// should have plugin extends
expect(appEslint.overrides[0].extends).toBeDefined();
expect(appEslint.overrides[1].extends).toBeDefined();
expect(e2eEslint.overrides[0].extends).toBeDefined();
expect(e2eEslint.overrides[1].extends).toBeDefined();
runCLI(`generate @nrwl/workspace:lib ${mylib} --no-interactive`);
verifySuccessfulMigratedSetup(myapp, mylib);
appEslint = readJson(`.eslintrc.json`);
e2eEslint = readJson('e2e/.eslintrc.json');
// should have no plugin extends
expect(appEslint.overrides[0].extends).toBeUndefined();
expect(appEslint.overrides[1].extends).toBeUndefined();
expect(e2eEslint.overrides[0].extends).toBeUndefined();
expect(e2eEslint.overrides[1].extends).toBeUndefined();
}); });
}); });
}); });

View File

@ -45,6 +45,7 @@ import { e2eProjectGenerator } from '../e2e-project/e2e-project';
import { setupDockerGenerator } from '../setup-docker/setup-docker'; import { setupDockerGenerator } from '../setup-docker/setup-docker';
import { Schema } from './schema'; import { Schema } from './schema';
import { mapLintPattern } from '@nrwl/linter/src/generators/lint-project/lint-project';
export interface NormalizedSchema extends Schema { export interface NormalizedSchema extends Schema {
appProjectRoot: string; appProjectRoot: string;
@ -273,7 +274,11 @@ export async function addLintingToApplication(
joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'), joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'),
], ],
eslintFilePatterns: [ eslintFilePatterns: [
`${options.appProjectRoot}/**/*.${options.js ? 'js' : 'ts'}`, mapLintPattern(
options.appProjectRoot,
options.js ? 'js' : 'ts',
options.rootProject
),
], ],
unitTestRunner: options.unitTestRunner, unitTestRunner: options.unitTestRunner,
skipFormat: true, skipFormat: true,
@ -382,11 +387,8 @@ export async function applicationGenerator(tree: Tree, schema: Schema) {
updateTsConfigOptions(tree, options); updateTsConfigOptions(tree, options);
if (options.linter !== Linter.None) { if (options.linter === Linter.EsLint) {
const lintTask = await addLintingToApplication(tree, { const lintTask = await addLintingToApplication(tree, options);
...options,
skipFormat: true,
});
tasks.push(lintTask); tasks.push(lintTask);
} }

View File

@ -14,11 +14,17 @@ import {
readProjectConfiguration, readProjectConfiguration,
runTasksInSerial, runTasksInSerial,
Tree, Tree,
updateJson,
} from '@nrwl/devkit'; } from '@nrwl/devkit';
import { Linter, lintProjectGenerator } from '@nrwl/linter'; import { Linter, lintProjectGenerator } from '@nrwl/linter';
import { Schema } from './schema'; import { Schema } from './schema';
import { axiosVersion } from '../../utils/versions'; import { axiosVersion } from '../../utils/versions';
import { join } from 'path';
import {
globalJavaScriptOverrides,
globalTypeScriptOverrides,
} from '@nrwl/linter/src/generators/init/global-eslint-config';
export async function e2eProjectGenerator(host: Tree, _options: Schema) { export async function e2eProjectGenerator(host: Tree, _options: Schema) {
const tasks: GeneratorCallback[] = []; const tasks: GeneratorCallback[] = [];
@ -90,7 +96,7 @@ export async function e2eProjectGenerator(host: Tree, _options: Schema) {
); );
tasks.push(installTask); tasks.push(installTask);
if (options.linter === 'eslint') { if (options.linter === Linter.EsLint) {
const linterTask = await lintProjectGenerator(host, { const linterTask = await lintProjectGenerator(host, {
project: options.e2eProjectName, project: options.e2eProjectName,
linter: Linter.EsLint, linter: Linter.EsLint,
@ -104,6 +110,33 @@ export async function e2eProjectGenerator(host: Tree, _options: Schema) {
rootProject: options.rootProject, rootProject: options.rootProject,
}); });
tasks.push(linterTask); tasks.push(linterTask);
updateJson(host, join(options.e2eProjectRoot, '.eslintrc.json'), (json) => {
if (options.rootProject) {
json.plugins = ['@nrwl/nx'];
json.extends = [];
}
json.overrides = [
...(options.rootProject
? [globalTypeScriptOverrides, globalJavaScriptOverrides]
: []),
/**
* In order to ensure maximum efficiency when typescript-eslint generates TypeScript Programs
* behind the scenes during lint runs, we need to make sure the project is configured to use its
* own specific tsconfigs, and not fall back to the ones in the root of the workspace.
*/
{
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
/**
* Having an empty rules object present makes it more obvious to the user where they would
* extend things from if they needed to
*/
rules: {},
},
];
return json;
});
} }
if (options.formatFile) { if (options.formatFile) {