From ab8c9298bee85e1fd03c273b12592dce78ff28ed Mon Sep 17 00:00:00 2001 From: Jack Hsu Date: Wed, 6 Dec 2023 03:47:53 -0500 Subject: [PATCH] feat(core): targets inferred from plugins override targetDefaults (#20586) --- packages/cypress/src/plugins/plugin.spec.ts | 7 ++ packages/cypress/src/plugins/plugin.ts | 62 +++------------ packages/eslint/src/plugins/plugin.spec.ts | 7 ++ packages/eslint/src/plugins/plugin.ts | 11 +-- packages/nuxt/src/plugins/plugin.spec.ts | 7 ++ packages/nuxt/src/plugins/plugin.ts | 78 ++++--------------- packages/vite/src/plugins/plugin.spec.ts | 7 ++ packages/vite/src/plugins/plugin.ts | 78 ++++--------------- .../plugins/__snapshots__/plugin.spec.ts.snap | 50 ++++++++++++ packages/webpack/src/plugins/plugin.spec.ts | 60 ++++++++++++++ packages/webpack/src/plugins/plugin.ts | 29 ++----- 11 files changed, 190 insertions(+), 206 deletions(-) create mode 100644 packages/webpack/src/plugins/__snapshots__/plugin.spec.ts.snap create mode 100644 packages/webpack/src/plugins/plugin.spec.ts diff --git a/packages/cypress/src/plugins/plugin.spec.ts b/packages/cypress/src/plugins/plugin.spec.ts index 22838819b829e..e57776a2cbfa5 100644 --- a/packages/cypress/src/plugins/plugin.spec.ts +++ b/packages/cypress/src/plugins/plugin.spec.ts @@ -19,6 +19,13 @@ describe('@nx/cypress/plugin', () => { }); context = { nxJsonConfiguration: { + // These defaults should be overridden by plugin + targetDefaults: { + e2e: { + cache: false, + inputs: ['foo', '^foo'], + }, + }, namedInputs: { default: ['{projectRoot}/**/*'], production: ['!{projectRoot}/**/*.spec.ts'], diff --git a/packages/cypress/src/plugins/plugin.ts b/packages/cypress/src/plugins/plugin.ts index 9278cfd35c0d7..14f60ebeb1532 100644 --- a/packages/cypress/src/plugins/plugin.ts +++ b/packages/cypress/src/plugins/plugin.ts @@ -14,7 +14,6 @@ import { registerTsProject } from '@nx/js/src/internal'; import { getLockFileName, getRootTsConfigPath } from '@nx/js'; import { CypressExecutorOptions } from '../executors/cypress/cypress.impl'; -import { readTargetDefaultsForTarget } from 'nx/src/project-graph/utils/project-configuration-utils'; import { getNamedInputs } from '@nx/devkit/src/utils/get-named-inputs'; import { existsSync, readdirSync } from 'fs'; import { globWithWorkspaceContext } from 'nx/src/utils/workspace-context'; @@ -139,6 +138,7 @@ function getOutputs( return outputs; } + function buildCypressTargets( configFilePath: string, projectRoot: string, @@ -164,33 +164,12 @@ function buildCypressTargets( if ('e2e' in cypressConfig) { targets[options.targetName] = { command: `cypress run --config-file ${relativeConfigPath} --e2e`, - options: { - cwd: projectRoot, - }, + options: { cwd: projectRoot }, + cache: true, + inputs: getInputs(namedInputs), + outputs: getOutputs(projectRoot, cypressConfig, 'e2e'), }; - const e2eTargetDefaults = readTargetDefaultsForTarget( - options.targetName, - context.nxJsonConfiguration.targetDefaults, - 'run-commands' - ); - - if (e2eTargetDefaults?.cache === undefined) { - targets[options.targetName].cache = true; - } - - if (e2eTargetDefaults?.inputs === undefined) { - targets[options.targetName].inputs = getInputs(namedInputs); - } - - if (e2eTargetDefaults?.outputs === undefined) { - targets[options.targetName].outputs = getOutputs( - projectRoot, - cypressConfig, - 'e2e' - ); - } - if (webServerCommands?.default) { delete webServerCommands.default; } @@ -258,36 +237,14 @@ function buildCypressTargets( } if ('component' in cypressConfig) { - const componentTestingTargetDefaults = readTargetDefaultsForTarget( - options.componentTestingTargetName, - context.nxJsonConfiguration.targetDefaults, - 'nx:run-commands' - ); - // This will not override the e2e target if it is the same targets[options.componentTestingTargetName] ??= { command: `cypress open --config-file ${relativeConfigPath} --component`, - options: { - cwd: projectRoot, - }, + options: { cwd: projectRoot }, + cache: true, + inputs: getInputs(namedInputs), + outputs: getOutputs(projectRoot, cypressConfig, 'component'), }; - - if (componentTestingTargetDefaults?.cache === undefined) { - targets[options.componentTestingTargetName].cache = true; - } - - if (componentTestingTargetDefaults?.inputs === undefined) { - targets[options.componentTestingTargetName].inputs = - getInputs(namedInputs); - } - - if (componentTestingTargetDefaults?.outputs === undefined) { - targets[options.componentTestingTargetName].outputs = getOutputs( - projectRoot, - cypressConfig, - 'component' - ); - } } return targets; @@ -326,6 +283,7 @@ function normalizeOptions(options: CypressPluginOptions): CypressPluginOptions { options.ciTargetName ??= 'e2e-ci'; return options; } + function getInputs( namedInputs: NxJsonConfiguration['namedInputs'] ): TargetConfiguration['inputs'] { diff --git a/packages/eslint/src/plugins/plugin.spec.ts b/packages/eslint/src/plugins/plugin.spec.ts index bb974b875568c..5435bd5b47b45 100644 --- a/packages/eslint/src/plugins/plugin.spec.ts +++ b/packages/eslint/src/plugins/plugin.spec.ts @@ -17,6 +17,13 @@ describe('@nx/eslint/plugin', () => { beforeEach(async () => { context = { nxJsonConfiguration: { + // These defaults should be overridden by plugin + targetDefaults: { + lint: { + cache: false, + inputs: ['foo', '^foo'], + }, + }, namedInputs: { default: ['{projectRoot}/**/*'], production: ['!{projectRoot}/**/*.spec.ts'], diff --git a/packages/eslint/src/plugins/plugin.ts b/packages/eslint/src/plugins/plugin.ts index de683913c623f..68316f5463a34 100644 --- a/packages/eslint/src/plugins/plugin.ts +++ b/packages/eslint/src/plugins/plugin.ts @@ -4,7 +4,6 @@ import { TargetConfiguration, } from '@nx/devkit'; import { dirname, join } from 'path'; -import { readTargetDefaultsForTarget } from 'nx/src/project-graph/utils/project-configuration-utils'; import { readdirSync } from 'fs'; import { combineGlobPatterns } from 'nx/src/utils/globs'; import { @@ -103,12 +102,6 @@ function buildEslintTargets( options: EslintPluginOptions, context: CreateNodesContext ) { - const targetDefaults = readTargetDefaultsForTarget( - options.targetName, - context.nxJsonConfiguration.targetDefaults, - '@nx/eslint:lint' - ); - const isRootProject = projectRoot === '.'; const targets: Record = {}; @@ -127,8 +120,8 @@ function buildEslintTargets( targets[options.targetName] = { ...baseTargetConfig, - cache: targetDefaults?.cache ?? true, - inputs: targetDefaults?.inputs ?? [ + cache: true, + inputs: [ 'default', ...eslintConfigs.map((config) => `{workspaceRoot}/${config}`), '{workspaceRoot}/tools/eslint-rules/**/*', diff --git a/packages/nuxt/src/plugins/plugin.spec.ts b/packages/nuxt/src/plugins/plugin.spec.ts index c7f03138d0328..a7774aff08044 100644 --- a/packages/nuxt/src/plugins/plugin.spec.ts +++ b/packages/nuxt/src/plugins/plugin.spec.ts @@ -29,6 +29,13 @@ describe('@nx/nuxt/plugin', () => { beforeEach(async () => { context = { nxJsonConfiguration: { + // These defaults should be overridden by plugin + targetDefaults: { + build: { + cache: false, + inputs: ['foo', '^foo'], + }, + }, namedInputs: { default: ['{projectRoot}/**/*'], production: ['!{projectRoot}/**/*.spec.ts'], diff --git a/packages/nuxt/src/plugins/plugin.ts b/packages/nuxt/src/plugins/plugin.ts index 4313473c1e2ec..4b7d2cb14673c 100644 --- a/packages/nuxt/src/plugins/plugin.ts +++ b/packages/nuxt/src/plugins/plugin.ts @@ -2,16 +2,15 @@ import { CreateDependencies, CreateNodes, CreateNodesContext, - TargetConfiguration, detectPackageManager, joinPathFragments, offsetFromRoot, readJsonFile, + TargetConfiguration, writeJsonFile, } from '@nx/devkit'; import { basename, dirname, join } from 'path'; import { projectGraphCacheDirectory } from 'nx/src/utils/cache-directory'; -import { readTargetDefaultsForTarget } from 'nx/src/project-graph/utils/project-configuration-utils'; import { getNamedInputs } from '@nx/devkit/src/utils/get-named-inputs'; import { existsSync, readdirSync } from 'fs'; import { loadNuxtKitDynamicImport } from '../utils/executor-utils'; @@ -102,20 +101,16 @@ async function buildNuxtTargets( const targets: Record = {}; targets[options.buildTargetName] = buildTarget( - context, namedInputs, buildOutputs, - projectRoot, - options + projectRoot ); targets[options.serveTargetName] = serveTarget(projectRoot); targets[options.testTargetName] = testTarget( - context, namedInputs, testOutputs, - options, projectRoot ); @@ -123,36 +118,17 @@ async function buildNuxtTargets( } function buildTarget( - context: CreateNodesContext, namedInputs: { [inputName: string]: any[]; }, buildOutputs: string[], - projectRoot: string, - options: NuxtPluginOptions + projectRoot: string ) { - const targetDefaults = readTargetDefaultsForTarget( - options.buildTargetName, - context.nxJsonConfiguration.targetDefaults - ); - - const targetConfig: TargetConfiguration = { + return { command: `nuxi build`, - options: { - cwd: projectRoot, - }, - }; - - if (targetDefaults?.outputs === undefined) { - targetConfig.outputs = buildOutputs; - } - - if (targetDefaults?.cache === undefined) { - targetConfig.cache = true; - } - - if (targetDefaults?.inputs === undefined) { - targetConfig.inputs = [ + options: { cwd: projectRoot }, + cache: true, + inputs: [ ...('production' in namedInputs ? ['default', '^production'] : ['default', '^default']), @@ -160,9 +136,9 @@ function buildTarget( { externalDependencies: ['nuxi'], }, - ]; - } - return targetConfig; + ], + outputs: buildOutputs, + }; } function serveTarget(projectRoot: string) { @@ -177,36 +153,17 @@ function serveTarget(projectRoot: string) { } function testTarget( - context: CreateNodesContext, namedInputs: { [inputName: string]: any[]; }, outputs: string[], - options: NuxtPluginOptions, projectRoot: string ) { - const targetDefaults = readTargetDefaultsForTarget( - options.testTargetName, - context.nxJsonConfiguration.targetDefaults - ); - - const targetConfig: TargetConfiguration = { + return { command: `vitest run`, - options: { - cwd: projectRoot, - }, - }; - - if (targetDefaults?.outputs === undefined) { - targetConfig.outputs = outputs; - } - - if (targetDefaults?.cache === undefined) { - targetConfig.cache = true; - } - - if (targetDefaults?.inputs === undefined) { - targetConfig.inputs = [ + options: { cwd: projectRoot }, + cache: true, + inputs: [ ...('production' in namedInputs ? ['default', '^production'] : ['default', '^default']), @@ -214,10 +171,9 @@ function testTarget( { externalDependencies: ['vitest'], }, - ]; - } - - return targetConfig; + ], + outputs, + }; } async function getInfoFromNuxtConfig( diff --git a/packages/vite/src/plugins/plugin.spec.ts b/packages/vite/src/plugins/plugin.spec.ts index 67c6763014786..de1d217c674d0 100644 --- a/packages/vite/src/plugins/plugin.spec.ts +++ b/packages/vite/src/plugins/plugin.spec.ts @@ -19,6 +19,13 @@ describe('@nx/vite/plugin', () => { beforeEach(async () => { context = { nxJsonConfiguration: { + // These defaults should be overridden by plugin + targetDefaults: { + build: { + cache: false, + inputs: ['foo', '^foo'], + }, + }, namedInputs: { default: ['{projectRoot}/**/*'], production: ['!{projectRoot}/**/*.spec.ts'], diff --git a/packages/vite/src/plugins/plugin.ts b/packages/vite/src/plugins/plugin.ts index 4d07ed9b2bd61..90bcd0e1f25c1 100644 --- a/packages/vite/src/plugins/plugin.ts +++ b/packages/vite/src/plugins/plugin.ts @@ -2,22 +2,21 @@ import { CreateDependencies, CreateNodes, CreateNodesContext, - TargetConfiguration, detectPackageManager, joinPathFragments, readJsonFile, + TargetConfiguration, workspaceRoot, writeJsonFile, } from '@nx/devkit'; import { dirname, isAbsolute, join, relative, resolve } from 'path'; - -import { readTargetDefaultsForTarget } from 'nx/src/project-graph/utils/project-configuration-utils'; import { getNamedInputs } from '@nx/devkit/src/utils/get-named-inputs'; -import { UserConfig, loadConfigFromFile } from 'vite'; +import { loadConfigFromFile, UserConfig } from 'vite'; import { existsSync, readdirSync } from 'fs'; import { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes'; import { projectGraphCacheDirectory } from 'nx/src/utils/cache-directory'; import { getLockFileName } from '@nx/js'; + export interface VitePluginOptions { buildTargetName?: string; testTargetName?: string; @@ -111,10 +110,8 @@ async function buildViteTargets( const targets: Record = {}; targets[options.buildTargetName] = await buildTarget( - context, namedInputs, buildOutputs, - options, projectRoot ); @@ -123,10 +120,8 @@ async function buildViteTargets( targets[options.previewTargetName] = previewTarget(projectRoot); targets[options.testTargetName] = await testTarget( - context, namedInputs, testOutputs, - options, projectRoot ); @@ -136,46 +131,26 @@ async function buildViteTargets( } async function buildTarget( - context: CreateNodesContext, namedInputs: { [inputName: string]: any[]; }, outputs: string[], - options: VitePluginOptions, projectRoot: string ) { - const targetDefaults = readTargetDefaultsForTarget( - options.buildTargetName, - context.nxJsonConfiguration.targetDefaults - ); - - const targetConfig: TargetConfiguration = { + return { command: `vite build`, - options: { - cwd: joinPathFragments(projectRoot), - }, - }; - - if (targetDefaults?.outputs === undefined) { - targetConfig.outputs = outputs; - } - - if (targetDefaults?.cache === undefined) { - targetConfig.cache = true; - } - - if (targetDefaults?.inputs === undefined) { - targetConfig.inputs = [ + options: { cwd: joinPathFragments(projectRoot) }, + cache: true, + inputs: [ ...('production' in namedInputs ? ['production', '^production'] : ['default', '^default']), { externalDependencies: ['vite'], }, - ]; - } - - return targetConfig; + ], + outputs, + }; } function serveTarget(projectRoot: string) { @@ -201,45 +176,26 @@ function previewTarget(projectRoot: string) { } async function testTarget( - context: CreateNodesContext, namedInputs: { [inputName: string]: any[]; }, outputs: string[], - options: VitePluginOptions, projectRoot: string ) { - const targetDefaults = readTargetDefaultsForTarget( - options.testTargetName, - context.nxJsonConfiguration.targetDefaults - ); - - const targetConfig: TargetConfiguration = { + return { command: `vitest run`, - options: { - cwd: joinPathFragments(projectRoot), - }, - }; - - if (targetDefaults?.outputs === undefined) { - targetConfig.outputs = outputs; - } - - if (targetDefaults?.cache === undefined) { - targetConfig.cache = true; - } - - if (targetDefaults?.inputs === undefined) { - targetConfig.inputs = [ + options: { cwd: joinPathFragments(projectRoot) }, + cache: true, + inputs: [ ...('production' in namedInputs ? ['production', '^production'] : ['default', '^default']), { externalDependencies: ['vitest'], }, - ]; - } - return targetConfig; + ], + outputs, + }; } function serveStaticTarget(options: VitePluginOptions) { diff --git a/packages/webpack/src/plugins/__snapshots__/plugin.spec.ts.snap b/packages/webpack/src/plugins/__snapshots__/plugin.spec.ts.snap new file mode 100644 index 0000000000000..48d125ede9dd3 --- /dev/null +++ b/packages/webpack/src/plugins/__snapshots__/plugin.spec.ts.snap @@ -0,0 +1,50 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`@nx/webpack/plugin should create nodes 1`] = ` +{ + "projects": { + "my-app": { + "projectType": "application", + "targets": { + "build-something": { + "cache": true, + "command": "webpack -c webpack.config.js --node-env=production", + "inputs": [ + "default", + "^production", + { + "externalDependencies": [ + "webpack-cli", + ], + }, + ], + "options": { + "cwd": "my-app", + }, + "outputs": [ + "dist/foo", + ], + }, + "my-serve": { + "command": "webpack serve -c webpack.config.js --node-env=development", + "options": { + "cwd": "my-app", + }, + }, + "preview-site": { + "command": "webpack serve -c webpack.config.js --node-env=production", + "options": { + "cwd": "my-app", + }, + }, + "serve-static": { + "executor": "@nx/web:file-server", + "options": { + "buildTarget": "build-something", + }, + }, + }, + }, + }, +} +`; diff --git a/packages/webpack/src/plugins/plugin.spec.ts b/packages/webpack/src/plugins/plugin.spec.ts new file mode 100644 index 0000000000000..12e087e0071db --- /dev/null +++ b/packages/webpack/src/plugins/plugin.spec.ts @@ -0,0 +1,60 @@ +import { CreateNodesContext } from '@nx/devkit'; +import { createNodes } from './plugin'; +import { TempFs } from 'nx/src/internal-testing-utils/temp-fs'; +import { join } from 'path'; + +describe('@nx/webpack/plugin', () => { + let createNodesFunction = createNodes[1]; + let context: CreateNodesContext; + let tempFs: TempFs; + + beforeEach(() => { + tempFs = new TempFs('webpack-plugin'); + + context = { + nxJsonConfiguration: { + namedInputs: { + default: ['{projectRoot}/**/*'], + production: ['!{projectRoot}/**/*.spec.ts'], + }, + }, + workspaceRoot: tempFs.tempDir, + }; + + tempFs.createFileSync( + 'my-app/project.json', + JSON.stringify({ name: 'my-app' }) + ); + tempFs.createFileSync('my-app/webpack.config.js', ''); + }); + + afterEach(() => { + jest.resetModules(); + }); + + it('should create nodes', async () => { + mockWebpackConfig({ + output: { + path: 'dist/foo', + }, + }); + const nodes = await createNodesFunction( + 'my-app/webpack.config.js', + { + buildTargetName: 'build-something', + serveTargetName: 'my-serve', + previewTargetName: 'preview-site', + serveStaticTargetName: 'serve-static', + }, + context + ); + + expect(nodes).toMatchSnapshot(); + }); + + function mockWebpackConfig(config: any) { + jest.mock(join(tempFs.tempDir, 'my-app/webpack.config.js'), () => config, { + virtual: true, + }); + } +}); diff --git a/packages/webpack/src/plugins/plugin.ts b/packages/webpack/src/plugins/plugin.ts index 1977fa674ef95..7cd126692a52d 100644 --- a/packages/webpack/src/plugins/plugin.ts +++ b/packages/webpack/src/plugins/plugin.ts @@ -10,7 +10,6 @@ import { } from '@nx/devkit'; import { basename, dirname, isAbsolute, join, relative } from 'path'; import { getNamedInputs } from '@nx/devkit/src/utils/get-named-inputs'; -import { readTargetDefaultsForTarget } from 'nx/src/project-graph/utils/project-configuration-utils'; import { WebpackExecutorOptions } from '../executors/webpack/schema'; import { WebDevServerOptions } from '../executors/dev-server/schema'; import { existsSync, readdirSync } from 'fs'; @@ -124,22 +123,9 @@ async function createWebpackTargets( targets[options.buildTargetName] = { command: `webpack -c ${configBasename} --node-env=production`, - options: { - cwd: projectRoot, - }, - }; - - const buildTargetDefaults = readTargetDefaultsForTarget( - options.buildTargetName, - context.nxJsonConfiguration.targetDefaults - ); - - if (buildTargetDefaults?.cache === undefined) { - targets[options.buildTargetName].cache = true; - } - - if (buildTargetDefaults?.inputs === undefined) { - targets[options.buildTargetName].inputs = + options: { cwd: projectRoot }, + cache: true, + inputs: 'production' in namedInputs ? [ 'default', @@ -154,12 +140,9 @@ async function createWebpackTargets( { externalDependencies: ['webpack-cli'], }, - ]; - } - - if (buildTargetDefaults?.outputs === undefined) { - targets[options.buildTargetName].outputs = [outputPath]; - } + ], + outputs: [outputPath], + }; targets[options.serveTargetName] = { command: `webpack serve -c ${configBasename} --node-env=development`,