From 07bcf88dcd506c4b8d4d35584c4dc7198c7f5bd9 Mon Sep 17 00:00:00 2001 From: Jack Hsu Date: Fri, 16 Feb 2024 14:26:52 -0500 Subject: [PATCH] feat(angular): use addExplicitTargets for Angular projects to avoid partial project config --- .../src/generators/add-linting/add-linting.ts | 2 +- .../src/generators/add-linting/schema.d.ts | 1 + .../src/generators/application/lib/add-e2e.ts | 8 ++-- .../application/lib/normalize-options.ts | 2 +- .../cypress-component-configuration.ts | 4 +- .../migrate-from-angular-cli.spec.ts.snap | 17 ++++---- .../angular/src/generators/utils/add-jest.ts | 2 +- .../configuration/configuration.spec.ts | 42 +++++++++++++++++++ .../generators/configuration/configuration.ts | 9 +++- packages/cypress/src/utils/add-linter.ts | 2 + .../lint-project/lint-project.spec.ts | 39 ++++++++++++++++- .../workspace/src/generators/preset/preset.ts | 2 + 12 files changed, 108 insertions(+), 22 deletions(-) diff --git a/packages/angular/src/generators/add-linting/add-linting.ts b/packages/angular/src/generators/add-linting/add-linting.ts index c07709450d908e..816f9be229b462 100755 --- a/packages/angular/src/generators/add-linting/add-linting.ts +++ b/packages/angular/src/generators/add-linting/add-linting.ts @@ -37,7 +37,7 @@ export async function addLintingGenerator( skipFormat: true, rootProject: rootProject, addPlugin: options.addPlugin, - addExplicitTargets: !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 1973f8276de7a3..1d9d5def326c6c 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 3ca335b87dc347..2c67c1a895b20d 100644 --- a/packages/angular/src/generators/application/lib/add-e2e.ts +++ b/packages/angular/src/generators/application/lib/add-e2e.ts @@ -14,9 +14,6 @@ import { getInstalledAngularVersionInfo } from '../../utils/version-utils'; import type { NormalizedSchema } from './normalized-schema'; export async function addE2e(tree: Tree, options: NormalizedSchema) { - // check for explicit false, separate e2e projects infer targets by default - const addPlugin = process.env.NX_ADD_PLUGINS !== 'false'; - if (options.e2eTestRunner === 'cypress') { // TODO: This can call `@nx/web:static-config` generator when ready addFileServerTarget(tree, options, 'serve-static'); @@ -37,7 +34,8 @@ export async function addE2e(tree: Tree, options: NormalizedSchema) { devServerTarget: `${options.name}:serve:development`, baseUrl: 'http://localhost:4200', rootProject: options.rootProject, - addPlugin: addPlugin, + addPlugin: options.addPlugin, + addExplicitTargets: false, // since e2e is a separate project, use inferred targets }); } else if (options.e2eTestRunner === 'playwright') { const { configurationGenerator: playwrightConfigurationGenerator } = @@ -65,7 +63,7 @@ export async function addE2e(tree: Tree, options: NormalizedSchema) { }`, webServerAddress: `http://localhost:${options.port ?? 4200}`, rootProject: options.rootProject, - addPlugin: addPlugin, + addPlugin: options.addPlugin, }); } } diff --git a/packages/angular/src/generators/application/lib/normalize-options.ts b/packages/angular/src/generators/application/lib/normalize-options.ts index c86e4d2043b226..75bc633e6f4958 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 99503584d13a18..c6bd1706f25a17 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 @@ -46,14 +46,14 @@ export async function cypressComponentConfigurationInternal( tree: Tree, options: CypressComponentConfigSchema ) { - options.addPlugin ??= process.env.NX_ADD_PLUGINS === 'true'; + options.addPlugin ??= process.env.NX_ADD_PLUGINS !== 'false'; const projectConfig = readProjectConfiguration(tree, options.project); const installTask = await baseCyCTConfig(tree, { project: options.project, skipFormat: true, addPlugin: options.addPlugin, - addExplicitTargets: !options.addPlugin, + addExplicitTargets: true, }); await configureCypressCT(tree, options); 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 1c3ec92a0748bd..0a05e6f56a312a 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/utils/add-jest.ts b/packages/angular/src/generators/utils/add-jest.ts index a7150fbca16a69..300b9984807a56 100644 --- a/packages/angular/src/generators/utils/add-jest.ts +++ b/packages/angular/src/generators/utils/add-jest.ts @@ -33,7 +33,7 @@ export async function addJest( skipPackageJson: options.skipPackageJson, skipFormat: true, addPlugin: options.addPlugin, - addExplicitTargets: !options.addPlugin, + addExplicitTargets: true, }); const setupFile = joinPathFragments( diff --git a/packages/cypress/src/generators/configuration/configuration.spec.ts b/packages/cypress/src/generators/configuration/configuration.spec.ts index ddd300ea499480..cd9928338142c6 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 dcfc9325dca1f4..9177cc22290634 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,7 +90,7 @@ export async function configurationGeneratorInternal( ); await addFiles(tree, opts, projectGraph, hasPlugin); - if (!hasPlugin) { + if (!hasPlugin || options.addExplicitTargets) { addTarget(tree, opts); } @@ -94,6 +98,7 @@ export async function configurationGeneratorInternal( ...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 821c9565a83b7f..ca66c9728130bf 100644 --- a/packages/cypress/src/utils/add-linter.ts +++ b/packages/cypress/src/utils/add-linter.ts @@ -40,6 +40,7 @@ export interface CyLinterOptions { **/ overwriteExisting?: boolean; addPlugin?: boolean; + addExplicitTargets?: boolean; } export async function addLinterToCyProject( @@ -65,6 +66,7 @@ export async function addLinterToCyProject( 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 da6d554e7d0ac2..342ac38ef0deee 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/workspace/src/generators/preset/preset.ts b/packages/workspace/src/generators/preset/preset.ts index 27dd003874c282..93b77fad063e16 100644 --- a/packages/workspace/src/generators/preset/preset.ts +++ b/packages/workspace/src/generators/preset/preset.ts @@ -34,6 +34,7 @@ async function createPreset(tree: Tree, options: Schema) { e2eTestRunner: options.e2eTestRunner ?? 'cypress', bundler: options.bundler, ssr: options.ssr, + addPlugin, }); } else if (options.preset === Preset.AngularStandalone) { const { @@ -52,6 +53,7 @@ async function createPreset(tree: Tree, options: Schema) { e2eTestRunner: options.e2eTestRunner ?? 'cypress', bundler: options.bundler, ssr: options.ssr, + addPlugin, }); } else if (options.preset === Preset.ReactMonorepo) { const { applicationGenerator: reactApplicationGenerator } = require('@nx' +