diff --git a/e2e/linter/src/linter.test.ts b/e2e/linter/src/linter.test.ts index 3feb4006a0..93c9b8ab3b 100644 --- a/e2e/linter/src/linter.test.ts +++ b/e2e/linter/src/linter.test.ts @@ -443,68 +443,81 @@ describe('Linter', () => { beforeEach(() => newProject()); 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', () => { const myapp = uniq('myapp'); const mylib = uniq('mylib'); 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'); - 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 - expect(rootEslint.overrides[0].extends).toEqual([ - 'plugin:@nrwl/nx/typescript', - ]); - expect(rootEslint.overrides[1].extends).toEqual([ - 'plugin:@nrwl/nx/javascript', - ]); - expect(e2eEslint.overrides[0].extends).toEqual([ - 'plugin:@nrwl/nx/typescript', - ]); - expect(e2eEslint.overrides[1].extends).toEqual([ - 'plugin:@nrwl/nx/javascript', - ]); + 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/react:lib ${mylib} --unitTestRunner=jest`); - // should add new tslint - expect(() => checkFilesExist(`.eslintrc.base.json`)).not.toThrow(); - const appEslint = readJson(`.eslintrc.json`); - rootEslint = readJson('.eslintrc.base.json'); + runCLI(`generate @nrwl/workspace:lib ${mylib} --unitTestRunner=jest`); + verifySuccessfulMigratedSetup(myapp, mylib); + + appEslint = readJson(`.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 expect(appEslint.overrides[0].extends).toBeUndefined(); expect(appEslint.overrides[1].extends).toBeUndefined(); expect(e2eEslint.overrides[0].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', () => { @@ -514,57 +527,23 @@ describe('Linter', () => { runCLI( `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'); - 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 - expect(rootEslint.overrides[0].files).toEqual(['*.ts']); - expect(rootEslint.overrides[0].extends).toEqual([ - 'plugin:@nrwl/nx/typescript', - 'plugin:@nrwl/nx/angular', - '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', - ]); + 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/angular:lib ${mylib} --no-interactive`); - // should add new tslint - expect(() => checkFilesExist(`.eslintrc.base.json`)).not.toThrow(); - const appEslint = readJson(`.eslintrc.json`); - rootEslint = readJson('.eslintrc.base.json'); + runCLI(`generate @nrwl/workspace:lib ${mylib} --no-interactive`); + verifySuccessfulMigratedSetup(myapp, mylib); + + appEslint = readJson(`.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 expect(appEslint.overrides[0].extends).toEqual([ 'plugin:@nrwl/nx/angular', @@ -572,10 +551,37 @@ describe('Linter', () => { ]); expect(e2eEslint.overrides[0].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(); }); }); }); diff --git a/packages/node/src/generators/application/application.ts b/packages/node/src/generators/application/application.ts index 26eac2dbf7..6027a4bf55 100644 --- a/packages/node/src/generators/application/application.ts +++ b/packages/node/src/generators/application/application.ts @@ -45,6 +45,7 @@ import { e2eProjectGenerator } from '../e2e-project/e2e-project'; import { setupDockerGenerator } from '../setup-docker/setup-docker'; import { Schema } from './schema'; +import { mapLintPattern } from '@nrwl/linter/src/generators/lint-project/lint-project'; export interface NormalizedSchema extends Schema { appProjectRoot: string; @@ -273,7 +274,11 @@ export async function addLintingToApplication( joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'), ], eslintFilePatterns: [ - `${options.appProjectRoot}/**/*.${options.js ? 'js' : 'ts'}`, + mapLintPattern( + options.appProjectRoot, + options.js ? 'js' : 'ts', + options.rootProject + ), ], unitTestRunner: options.unitTestRunner, skipFormat: true, @@ -382,11 +387,8 @@ export async function applicationGenerator(tree: Tree, schema: Schema) { updateTsConfigOptions(tree, options); - if (options.linter !== Linter.None) { - const lintTask = await addLintingToApplication(tree, { - ...options, - skipFormat: true, - }); + if (options.linter === Linter.EsLint) { + const lintTask = await addLintingToApplication(tree, options); tasks.push(lintTask); } diff --git a/packages/node/src/generators/e2e-project/e2e-project.ts b/packages/node/src/generators/e2e-project/e2e-project.ts index cc0c5d8d13..9389e88038 100644 --- a/packages/node/src/generators/e2e-project/e2e-project.ts +++ b/packages/node/src/generators/e2e-project/e2e-project.ts @@ -14,11 +14,17 @@ import { readProjectConfiguration, runTasksInSerial, Tree, + updateJson, } from '@nrwl/devkit'; import { Linter, lintProjectGenerator } from '@nrwl/linter'; import { Schema } from './schema'; 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) { const tasks: GeneratorCallback[] = []; @@ -90,7 +96,7 @@ export async function e2eProjectGenerator(host: Tree, _options: Schema) { ); tasks.push(installTask); - if (options.linter === 'eslint') { + if (options.linter === Linter.EsLint) { const linterTask = await lintProjectGenerator(host, { project: options.e2eProjectName, linter: Linter.EsLint, @@ -104,6 +110,33 @@ export async function e2eProjectGenerator(host: Tree, _options: Schema) { rootProject: options.rootProject, }); 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) {