diff --git a/packages/angular/src/generators/add-linting/add-linting.ts b/packages/angular/src/generators/add-linting/add-linting.ts index 9d53a7544c..f368e14ace 100755 --- a/packages/angular/src/generators/add-linting/add-linting.ts +++ b/packages/angular/src/generators/add-linting/add-linting.ts @@ -12,6 +12,7 @@ export async function addLintingGenerator( ): Promise { const installTask = lintInitGenerator(tree, { linter: Linter.EsLint, + unitTestRunner: options.unitTestRunner, skipPackageJson: options.skipPackageJson, }); diff --git a/packages/angular/src/generators/add-linting/schema.d.ts b/packages/angular/src/generators/add-linting/schema.d.ts index aa2b8d6f84..c471cf65b1 100644 --- a/packages/angular/src/generators/add-linting/schema.d.ts +++ b/packages/angular/src/generators/add-linting/schema.d.ts @@ -5,4 +5,5 @@ export interface AddLintingGeneratorSchema { setParserOptionsProject?: boolean; skipFormat?: boolean; skipPackageJson?: boolean; + unitTestRunner?: string; } diff --git a/packages/angular/src/generators/application/lib/add-e2e.ts b/packages/angular/src/generators/application/lib/add-e2e.ts index 8e7b9c3e60..d2aad93dfb 100644 --- a/packages/angular/src/generators/application/lib/add-e2e.ts +++ b/packages/angular/src/generators/application/lib/add-e2e.ts @@ -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, diff --git a/packages/angular/src/generators/application/lib/add-linting.ts b/packages/angular/src/generators/application/lib/add-linting.ts index 69bd2bb8e3..ab48337e78 100644 --- a/packages/angular/src/generators/application/lib/add-linting.ts +++ b/packages/angular/src/generators/application/lib/add-linting.ts @@ -14,5 +14,6 @@ export async function addLinting(host: Tree, options: NormalizedSchema) { prefix: options.prefix, setParserOptionsProject: options.setParserOptionsProject, skipPackageJson: options.skipPackageJson, + unitTestRunner: options.unitTestRunner, }); } diff --git a/packages/angular/src/generators/library/library.ts b/packages/angular/src/generators/library/library.ts index 13595de9b3..b502be4427 100644 --- a/packages/angular/src/generators/library/library.ts +++ b/packages/angular/src/generators/library/library.ts @@ -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, }); diff --git a/packages/angular/src/generators/ng-add/migrate-from-angular-cli.ts b/packages/angular/src/generators/ng-add/migrate-from-angular-cli.ts index af510f3dee..9d30fea4f6 100755 --- a/packages/angular/src/generators/ng-add/migrate-from-angular-cli.ts +++ b/packages/angular/src/generators/ng-add/migrate-from-angular-cli.ts @@ -96,7 +96,7 @@ export async function migrateFromAngularCli( createRootKarmaConfig(tree); } if (workspaceCapabilities.eslint) { - updateRootEsLintConfig(tree, eslintConfig); + updateRootEsLintConfig(tree, eslintConfig, options.unitTestRunner); cleanupEsLintPackages(tree); } diff --git a/packages/angular/src/generators/ng-add/utilities/e2e.migrator.ts b/packages/angular/src/generators/ng-add/utilities/e2e.migrator.ts index 9bdd77368a..c925e01045 100644 --- a/packages/angular/src/generators/ng-add/utilities/e2e.migrator.ts +++ b/packages/angular/src/generators/ng-add/utilities/e2e.migrator.ts @@ -310,6 +310,7 @@ export class E2eMigrator extends ProjectMigrator { project: this.project.name, linter: Linter.EsLint, eslintFilePatterns: [`${this.project.newRoot}/**/*.{js,ts}`], + unitTestRunner: this.options.unitTestRunner, tsConfigPaths: [ joinPathFragments(this.project.newRoot, 'tsconfig.json'), ], diff --git a/packages/angular/src/generators/ng-add/utilities/workspace.ts b/packages/angular/src/generators/ng-add/utilities/workspace.ts index eea05fcfae..7749472727 100644 --- a/packages/angular/src/generators/ng-add/utilities/workspace.ts +++ b/packages/angular/src/generators/ng-add/utilities/workspace.ts @@ -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. diff --git a/packages/cypress/src/generators/cypress-project/cypress-project.ts b/packages/cypress/src/generators/cypress-project/cypress-project.ts index 87db2c0862..f03005f285 100644 --- a/packages/cypress/src/generators/cypress-project/cypress-project.ts +++ b/packages/cypress/src/generators/cypress-project/cypress-project.ts @@ -110,7 +110,7 @@ function addProject(tree: Tree, options: CypressProjectSchema) { if (!project.targets) { logger.warn(stripIndents` - NOTE: Project, "${options.project}", does not have any targets defined and a baseUrl was not provided. Nx will use + NOTE: Project, "${options.project}", does not have any targets defined and a baseUrl was not provided. Nx will use "${options.project}:serve" as the devServerTarget. But you may need to define this target within the project, "${options.project}". `); } diff --git a/packages/js/src/generators/library/library.ts b/packages/js/src/generators/library/library.ts index ed312c8ff3..bd946eb40d 100644 --- a/packages/js/src/generators/library/library.ts +++ b/packages/js/src/generators/library/library.ts @@ -155,6 +155,7 @@ export function addLint( tsConfigPaths: [ joinPathFragments(options.projectRoot, 'tsconfig.lib.json'), ], + unitTestRunner: options.unitTestRunner, eslintFilePatterns: [ `${options.projectRoot}/**/*.${options.js ? 'js' : 'ts'}`, ], diff --git a/packages/linter/src/generators/init/init.ts b/packages/linter/src/generators/init/init.ts index f4ea0f565d..8f39e70593 100644 --- a/packages/linter/src/generators/init/init.ts +++ b/packages/linter/src/generators/init/init.ts @@ -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) => { diff --git a/packages/linter/src/generators/lint-project/lint-project.ts b/packages/linter/src/generators/lint-project/lint-project.ts index b7bdb98841..bea245a3d9 100644 --- a/packages/linter/src/generators/lint-project/lint-project.ts +++ b/packages/linter/src/generators/lint-project/lint-project.ts @@ -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); diff --git a/packages/next/src/generators/application/lib/add-linting.ts b/packages/next/src/generators/application/lib/add-linting.ts index 3394d277c6..86faa49ff2 100644 --- a/packages/next/src/generators/application/lib/add-linting.ts +++ b/packages/next/src/generators/application/lib/add-linting.ts @@ -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, }); diff --git a/packages/node/src/generators/application/application.ts b/packages/node/src/generators/application/application.ts index a90da3aeb7..ab21b852fb 100644 --- a/packages/node/src/generators/application/application.ts +++ b/packages/node/src/generators/application/application.ts @@ -187,6 +187,7 @@ export async function addLintingToApplication( eslintFilePatterns: [ `${options.appProjectRoot}/**/*.${options.js ? 'js' : 'ts'}`, ], + unitTestRunner: options.unitTestRunner, skipFormat: true, setParserOptionsProject: options.setParserOptionsProject, }); diff --git a/packages/react-native/src/generators/application/application.ts b/packages/react-native/src/generators/application/application.ts index 939c6fe86f..9a1e6e4410 100644 --- a/packages/react-native/src/generators/application/application.ts +++ b/packages/react-native/src/generators/application/application.ts @@ -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, diff --git a/packages/react-native/src/generators/library/library.ts b/packages/react-native/src/generators/library/library.ts index ff62556f9c..8f432fbdeb 100644 --- a/packages/react-native/src/generators/library/library.ts +++ b/packages/react-native/src/generators/library/library.ts @@ -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); diff --git a/packages/react-native/src/utils/add-linting.spec.ts b/packages/react-native/src/utils/add-linting.spec.ts index 459df6b114..f9ba126f4c 100644 --- a/packages/react-native/src/utils/add-linting.spec.ts +++ b/packages/react-native/src/utils/add-linting.spec.ts @@ -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(); diff --git a/packages/react-native/src/utils/add-linting.ts b/packages/react-native/src/utils/add-linting.ts index 8b9321a702..3519c866ba 100644 --- a/packages/react-native/src/utils/add-linting.ts +++ b/packages/react-native/src/utils/add-linting.ts @@ -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']; diff --git a/packages/react/src/generators/application/application.ts b/packages/react/src/generators/application/application.ts index 08631f3a0e..33aa01a8ad 100644 --- a/packages/react/src/generators/application/application.ts +++ b/packages/react/src/generators/application/application.ts @@ -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, }); diff --git a/packages/react/src/generators/library/library.ts b/packages/react/src/generators/library/library.ts index 3c641f103a..c23024950d 100644 --- a/packages/react/src/generators/library/library.ts +++ b/packages/react/src/generators/library/library.ts @@ -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, }); diff --git a/packages/web/src/generators/application/application.ts b/packages/web/src/generators/application/application.ts index f9b8310724..f2f7b3fe3a 100644 --- a/packages/web/src/generators/application/application.ts +++ b/packages/web/src/generators/application/application.ts @@ -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, diff --git a/packages/workspace/src/generators/library/library.ts b/packages/workspace/src/generators/library/library.ts index a230f6b500..0b19b198ae 100644 --- a/packages/workspace/src/generators/library/library.ts +++ b/packages/workspace/src/generators/library/library.ts @@ -81,6 +81,7 @@ export function addLint( tsConfigPaths: [ joinPathFragments(options.projectRoot, 'tsconfig.lib.json'), ], + unitTestRunner: options.unitTestRunner, eslintFilePatterns: [ `${options.projectRoot}/**/*.${options.js ? 'js' : 'ts'}`, ],