diff --git a/packages/angular/src/generators/add-linting/add-linting.ts b/packages/angular/src/generators/add-linting/add-linting.ts index 0cbe631879610..816f9be229b46 100755 --- a/packages/angular/src/generators/add-linting/add-linting.ts +++ b/packages/angular/src/generators/add-linting/add-linting.ts @@ -37,6 +37,7 @@ export async function addLintingGenerator( skipFormat: true, rootProject: rootProject, addPlugin: options.addPlugin, + addExplicitTargets: true, }); tasks.push(lintTask); diff --git a/packages/angular/src/generators/add-linting/schema.d.ts b/packages/angular/src/generators/add-linting/schema.d.ts index 1973f8276de7a..1d9d5def326c6 100644 --- a/packages/angular/src/generators/add-linting/schema.d.ts +++ b/packages/angular/src/generators/add-linting/schema.d.ts @@ -7,4 +7,5 @@ export interface AddLintingGeneratorSchema { skipPackageJson?: boolean; unitTestRunner?: string; addPlugin?: boolean; + addExplicitTargets?: boolean; } diff --git a/packages/angular/src/generators/application/lib/add-e2e.ts b/packages/angular/src/generators/application/lib/add-e2e.ts index 58a98c319242e..d271cd47810d7 100644 --- a/packages/angular/src/generators/application/lib/add-e2e.ts +++ b/packages/angular/src/generators/application/lib/add-e2e.ts @@ -34,7 +34,8 @@ export async function addE2e(tree: Tree, options: NormalizedSchema) { devServerTarget: `${options.name}:serve:development`, baseUrl: 'http://localhost:4200', rootProject: options.rootProject, - addPlugin: options.addPlugin, + addPlugin: false, + addExplicitTargets: false, // since e2e is a separate project, use inferred targets }); } else if (options.e2eTestRunner === 'playwright') { const { configurationGenerator: playwrightConfigurationGenerator } = @@ -62,7 +63,7 @@ export async function addE2e(tree: Tree, options: NormalizedSchema) { }`, webServerAddress: `http://localhost:${options.port ?? 4200}`, rootProject: options.rootProject, - addPlugin: options.addPlugin, + addPlugin: false, }); } } diff --git a/packages/angular/src/generators/application/lib/add-linting.ts b/packages/angular/src/generators/application/lib/add-linting.ts index 70da0750fdfea..072598d439c5b 100644 --- a/packages/angular/src/generators/application/lib/add-linting.ts +++ b/packages/angular/src/generators/application/lib/add-linting.ts @@ -16,6 +16,6 @@ export async function addLinting(host: Tree, options: NormalizedSchema) { skipPackageJson: options.skipPackageJson, unitTestRunner: options.unitTestRunner, skipFormat: true, - addPlugin: options.addPlugin, + addPlugin: false, }); } diff --git a/packages/angular/src/generators/application/lib/add-unit-test-runner.ts b/packages/angular/src/generators/application/lib/add-unit-test-runner.ts index 509f58934c2ba..5666204ff86a2 100644 --- a/packages/angular/src/generators/application/lib/add-unit-test-runner.ts +++ b/packages/angular/src/generators/application/lib/add-unit-test-runner.ts @@ -10,7 +10,7 @@ export async function addUnitTestRunner(host: Tree, options: NormalizedSchema) { projectRoot: options.appProjectRoot, skipPackageJson: options.skipPackageJson, strict: options.strict, - addPlugin: options.addPlugin, + addPlugin: false, }); } } diff --git a/packages/angular/src/generators/application/lib/normalize-options.ts b/packages/angular/src/generators/application/lib/normalize-options.ts index c86e4d2043b22..75bc633e6f495 100644 --- a/packages/angular/src/generators/application/lib/normalize-options.ts +++ b/packages/angular/src/generators/application/lib/normalize-options.ts @@ -26,7 +26,7 @@ export async function normalizeOptions( }); options.rootProject = appProjectRoot === '.'; options.projectNameAndRootFormat = projectNameAndRootFormat; - options.addPlugin ??= process.env.NX_ADD_PLUGINS === 'true'; + options.addPlugin ??= process.env.NX_ADD_PLUGINS !== 'false'; const e2eProjectName = options.rootProject ? 'e2e' : `${appProjectName}-e2e`; const e2eProjectRoot = options.rootProject ? 'e2e' : `${appProjectRoot}-e2e`; diff --git a/packages/angular/src/generators/cypress-component-configuration/cypress-component-configuration.ts b/packages/angular/src/generators/cypress-component-configuration/cypress-component-configuration.ts index 66b9627e82191..6a7a0654d8f63 100644 --- a/packages/angular/src/generators/cypress-component-configuration/cypress-component-configuration.ts +++ b/packages/angular/src/generators/cypress-component-configuration/cypress-component-configuration.ts @@ -52,7 +52,8 @@ export async function cypressComponentConfigurationInternal( const installTask = await baseCyCTConfig(tree, { project: options.project, skipFormat: true, - addPlugin: options.addPlugin, + addPlugin: false, + addExplicitTargets: true, }); await configureCypressCT(tree, options); diff --git a/packages/angular/src/generators/library/library.ts b/packages/angular/src/generators/library/library.ts index a2c8c95d3e52f..588a25caf5fba 100644 --- a/packages/angular/src/generators/library/library.ts +++ b/packages/angular/src/generators/library/library.ts @@ -134,7 +134,7 @@ async function addUnitTestRunner( projectRoot: options.projectRoot, skipPackageJson: options.skipPackageJson, strict: options.strict, - addPlugin: options.addPlugin, + addPlugin: false, }); } } diff --git a/packages/angular/src/generators/ng-add/__snapshots__/migrate-from-angular-cli.spec.ts.snap b/packages/angular/src/generators/ng-add/__snapshots__/migrate-from-angular-cli.spec.ts.snap index 1c3ec92a0748b..0a05e6f56a312 100644 --- a/packages/angular/src/generators/ng-add/__snapshots__/migrate-from-angular-cli.spec.ts.snap +++ b/packages/angular/src/generators/ng-add/__snapshots__/migrate-from-angular-cli.spec.ts.snap @@ -99,15 +99,16 @@ exports[`workspace move to nx layout should create nx.json 1`] = ` ], "sharedGlobals": [], }, - "plugins": [ - { - "options": { - "targetName": "lint", - }, - "plugin": "@nx/eslint/plugin", - }, - ], "targetDefaults": { + "@nx/eslint:lint": { + "cache": true, + "inputs": [ + "default", + "{workspaceRoot}/.eslintrc.json", + "{workspaceRoot}/.eslintignore", + "{workspaceRoot}/eslint.config.js", + ], + }, "build": { "cache": true, "dependsOn": [ diff --git a/packages/angular/src/generators/ng-add/utilities/workspace.ts b/packages/angular/src/generators/ng-add/utilities/workspace.ts index d81f8d2640d7a..905af4cc94a96 100644 --- a/packages/angular/src/generators/ng-add/utilities/workspace.ts +++ b/packages/angular/src/generators/ng-add/utilities/workspace.ts @@ -191,7 +191,7 @@ export async function updateRootEsLintConfig( unitTestRunner?: string ): Promise { await lintInitGenerator(tree, { - addPlugin: process.env.NX_ADD_PLUGINS !== 'false', + addPlugin: process.env.NX_ADD_PLUGINS === 'true', }); if (!existingEsLintConfig) { diff --git a/packages/angular/src/generators/storybook-configuration/lib/generate-storybook-configuration.ts b/packages/angular/src/generators/storybook-configuration/lib/generate-storybook-configuration.ts index 556b9c8784b40..5f825f2dbd458 100644 --- a/packages/angular/src/generators/storybook-configuration/lib/generate-storybook-configuration.ts +++ b/packages/angular/src/generators/storybook-configuration/lib/generate-storybook-configuration.ts @@ -6,7 +6,11 @@ export async function generateStorybookConfiguration( tree: Tree, options: StorybookConfigurationOptions ): Promise { - const { configurationGenerator } = ensurePackage('@nx/storybook', nxVersion); + const addPlugin = process.env.NX_ADD_PLUGINS === 'true'; + + const { configurationGenerator } = ensurePackage< + typeof import('@nx/storybook') + >('@nx/storybook', nxVersion); return await configurationGenerator(tree, { project: options.project, uiFramework: '@storybook/angular', @@ -17,5 +21,7 @@ export async function generateStorybookConfiguration( interactionTests: options.interactionTests, configureStaticServe: options.configureStaticServe, skipFormat: true, + addPlugin: addPlugin, + addExplicitTargets: !addPlugin, }); } diff --git a/packages/angular/src/generators/utils/add-jest.ts b/packages/angular/src/generators/utils/add-jest.ts index e2ecd6d25be0d..1535a97e9de96 100644 --- a/packages/angular/src/generators/utils/add-jest.ts +++ b/packages/angular/src/generators/utils/add-jest.ts @@ -32,7 +32,8 @@ export async function addJest( skipSerializers: false, skipPackageJson: options.skipPackageJson, skipFormat: true, - addPlugin: options.addPlugin, + addPlugin: false, + addExplicitTargets: true, }); const setupFile = joinPathFragments( diff --git a/packages/cypress/src/generators/component-configuration/component-configuration.ts b/packages/cypress/src/generators/component-configuration/component-configuration.ts index 0dc933855339b..6875aa38a0490 100644 --- a/packages/cypress/src/generators/component-configuration/component-configuration.ts +++ b/packages/cypress/src/generators/component-configuration/component-configuration.ts @@ -64,7 +64,7 @@ export async function componentConfigurationGeneratorInternal( tasks.push(updateDeps(tree, opts)); addProjectFiles(tree, projectConfig, opts); - if (!hasPlugin) { + if (!hasPlugin || opts.addExplicitTargets) { addTargetToProject(tree, projectConfig, opts); } updateNxJsonConfiguration(tree, hasPlugin); diff --git a/packages/cypress/src/generators/component-configuration/schema.d.ts b/packages/cypress/src/generators/component-configuration/schema.d.ts index 11430de45c51e..bd9aef559c8b2 100644 --- a/packages/cypress/src/generators/component-configuration/schema.d.ts +++ b/packages/cypress/src/generators/component-configuration/schema.d.ts @@ -5,4 +5,9 @@ export interface CypressComponentConfigurationSchema { bundler?: 'webpack' | 'vite'; jsx?: boolean; addPlugin?: boolean; + + /** + * @internal + */ + addExplicitTargets?: boolean; } diff --git a/packages/cypress/src/generators/configuration/configuration.spec.ts b/packages/cypress/src/generators/configuration/configuration.spec.ts index ddd300ea49948..cd9928338142c 100644 --- a/packages/cypress/src/generators/configuration/configuration.spec.ts +++ b/packages/cypress/src/generators/configuration/configuration.spec.ts @@ -554,6 +554,48 @@ export default defineConfig({ " `); }); + + it('should support generating explicit targets', async () => { + mockedInstalledCypressVersion.mockReturnValue(undefined); // ensure init is called + addProject(tree, { name: 'explicit-lib', type: 'apps' }); + addProject(tree, { name: 'inferred-lib', type: 'apps' }); + + await cypressE2EConfigurationGenerator(tree, { + project: 'explicit-lib', + baseUrl: 'http://localhost:4200', + addPlugin: true, + addExplicitTargets: true, + }); + await cypressE2EConfigurationGenerator(tree, { + project: 'inferred-lib', + baseUrl: 'http://localhost:4200', + addPlugin: true, + addExplicitTargets: false, + }); + + expect(readProjectConfiguration(tree, 'explicit-lib').targets.e2e) + .toMatchInlineSnapshot(` + { + "configurations": { + "ci": { + "devServerTarget": "explicit-lib:serve-static", + }, + "production": { + "devServerTarget": "explicit-lib:serve:production", + }, + }, + "executor": "@nx/cypress:cypress", + "options": { + "cypressConfig": "apps/explicit-lib/cypress.config.ts", + "devServerTarget": "explicit-lib:serve", + "testingType": "e2e", + }, + } + `); + expect( + readProjectConfiguration(tree, 'inferred-lib').targets.e2e + ).toBeUndefined(); + }); }); }); diff --git a/packages/cypress/src/generators/configuration/configuration.ts b/packages/cypress/src/generators/configuration/configuration.ts index 6c29bc895aaa5..9177cc2229063 100644 --- a/packages/cypress/src/generators/configuration/configuration.ts +++ b/packages/cypress/src/generators/configuration/configuration.ts @@ -48,6 +48,7 @@ export interface CypressE2EConfigSchema { webServerCommands?: Record; ciWebServerCommand?: string; addPlugin?: boolean; + addExplicitTargets?: boolean; } type NormalizedSchema = ReturnType; @@ -56,7 +57,10 @@ export function configurationGenerator( tree: Tree, options: CypressE2EConfigSchema ) { - return configurationGeneratorInternal(tree, { addPlugin: false, ...options }); + return configurationGeneratorInternal(tree, { + addPlugin: false, + ...options, + }); } export async function configurationGeneratorInternal( @@ -86,13 +90,15 @@ export async function configurationGeneratorInternal( ); await addFiles(tree, opts, projectGraph, hasPlugin); - if (!hasPlugin) { + if (!hasPlugin || options.addExplicitTargets) { addTarget(tree, opts); } const linterTask = await addLinterToCyProject(tree, { ...opts, cypressDir: opts.directory, + addPlugin: opts.addPlugin, + addExplicitTargets: opts.addExplicitTargets, }); tasks.push(linterTask); diff --git a/packages/cypress/src/utils/add-linter.ts b/packages/cypress/src/utils/add-linter.ts index 5679d65da15ed..ca66c9728130b 100644 --- a/packages/cypress/src/utils/add-linter.ts +++ b/packages/cypress/src/utils/add-linter.ts @@ -39,6 +39,8 @@ export interface CyLinterOptions { * This is useful when adding linting to a brand new project vs an existing one **/ overwriteExisting?: boolean; + addPlugin?: boolean; + addExplicitTargets?: boolean; } export async function addLinterToCyProject( @@ -63,6 +65,8 @@ export async function addLinterToCyProject( setParserOptionsProject: options.setParserOptionsProject, skipPackageJson: options.skipPackageJson, rootProject: options.rootProject, + addPlugin: options.addPlugin, + addExplicitTargets: options.addExplicitTargets, }) ); } diff --git a/packages/eslint/src/generators/lint-project/lint-project.spec.ts b/packages/eslint/src/generators/lint-project/lint-project.spec.ts index da6d554e7d0ac..342ac38ef0dee 100644 --- a/packages/eslint/src/generators/lint-project/lint-project.spec.ts +++ b/packages/eslint/src/generators/lint-project/lint-project.spec.ts @@ -1,9 +1,9 @@ import { addProjectConfiguration, + readJson, readProjectConfiguration, - updateJson, Tree, - readJson, + updateJson, } from '@nx/devkit'; import { Linter } from '../utils/linter'; @@ -298,4 +298,39 @@ describe('@nx/eslint:lint-project', () => { " `); }); + + it('should support generating explicit targets on project config', async () => { + addProjectConfiguration(tree, 'explicit-lib', { + root: 'libs/explicit-lib', + projectType: 'library', + targets: {}, + }); + addProjectConfiguration(tree, 'inferred-lib', { + root: 'libs/inferred-lib', + projectType: 'library', + targets: {}, + }); + + await lintProjectGenerator(tree, { + ...defaultOptions, + linter: Linter.EsLint, + project: 'explicit-lib', + addExplicitTargets: true, + }); + await lintProjectGenerator(tree, { + ...defaultOptions, + linter: Linter.EsLint, + project: 'inferred-lib', + addExplicitTargets: false, + }); + + const explicitCOnfig = readProjectConfiguration(tree, 'explicit-lib'); + expect(explicitCOnfig.targets.lint).toMatchInlineSnapshot(` + { + "executor": "@nx/eslint:lint", + } + `); + const inferredConfig = readProjectConfiguration(tree, 'inferred-lib'); + expect(inferredConfig.targets.lint).toBeUndefined(); + }); }); diff --git a/packages/eslint/src/generators/lint-project/lint-project.ts b/packages/eslint/src/generators/lint-project/lint-project.ts index 0a93999a88485..3e9acec2af810 100644 --- a/packages/eslint/src/generators/lint-project/lint-project.ts +++ b/packages/eslint/src/generators/lint-project/lint-project.ts @@ -52,6 +52,11 @@ interface LintProjectOptions { rootProject?: boolean; keepExistingVersions?: boolean; addPlugin?: boolean; + + /** + * @internal + */ + addExplicitTargets?: boolean; } export function lintProjectGenerator(tree: Tree, options: LintProjectOptions) { @@ -91,7 +96,7 @@ export async function lintProjectGeneratorInternal( } const hasPlugin = hasEslintPlugin(tree); - if (hasPlugin) { + if (hasPlugin && !options.addExplicitTargets) { if ( lintFilePatterns && lintFilePatterns.length && diff --git a/packages/jest/src/generators/configuration/configuration.ts b/packages/jest/src/generators/configuration/configuration.ts index a786f6f9ed9ba..41c25e7ef2ab8 100644 --- a/packages/jest/src/generators/configuration/configuration.ts +++ b/packages/jest/src/generators/configuration/configuration.ts @@ -105,7 +105,7 @@ export async function configurationGeneratorInternal( ); } }); - if (!hasPlugin) { + if (!hasPlugin || options.addExplicitTargets) { updateWorkspace(tree, options); } diff --git a/packages/jest/src/generators/configuration/schema.d.ts b/packages/jest/src/generators/configuration/schema.d.ts index e3021f1cb1d23..afc7659eae769 100644 --- a/packages/jest/src/generators/configuration/schema.d.ts +++ b/packages/jest/src/generators/configuration/schema.d.ts @@ -19,6 +19,11 @@ export interface JestProjectSchema { compiler?: 'tsc' | 'babel' | 'swc'; skipPackageJson?: boolean; js?: boolean; + + /** + * @internal + */ + addExplicitTargets?: boolean; } export type NormalizedJestProjectSchema = JestProjectSchema & { diff --git a/packages/storybook/src/generators/configuration/configuration.ts b/packages/storybook/src/generators/configuration/configuration.ts index be3d53e8ca84a..45fa2609c92cd 100644 --- a/packages/storybook/src/generators/configuration/configuration.ts +++ b/packages/storybook/src/generators/configuration/configuration.ts @@ -174,7 +174,7 @@ export async function configurationGeneratorInternal( let devDeps = {}; - if (!hasPlugin) { + if (!hasPlugin || schema.addExplicitTargets) { if (schema.uiFramework === '@storybook/angular') { addAngularStorybookTarget(tree, schema.project, schema.interactionTests); } else { diff --git a/packages/storybook/src/generators/configuration/schema.d.ts b/packages/storybook/src/generators/configuration/schema.d.ts index 81e9b5519cb0a..cc42b504cdcfa 100644 --- a/packages/storybook/src/generators/configuration/schema.d.ts +++ b/packages/storybook/src/generators/configuration/schema.d.ts @@ -20,4 +20,9 @@ export interface StorybookConfigureSchema { */ cypressDirectory?: string; addPlugin?: boolean; + + /** + * @internal + */ + addExplicitTargets?: boolean; }