diff --git a/packages/nx/src/plugins/target-defaults/target-defaults-plugin.spec.ts b/packages/nx/src/plugins/target-defaults/target-defaults-plugin.spec.ts index 504982bcd3..d27f1662ce 100644 --- a/packages/nx/src/plugins/target-defaults/target-defaults-plugin.spec.ts +++ b/packages/nx/src/plugins/target-defaults/target-defaults-plugin.spec.ts @@ -233,4 +233,67 @@ describe('target-defaults plugin', () => { } `); }); + + describe('executor key', () => { + it('should support multiple targets with the same executor', () => { + memfs.vol.fromJSON( + { + 'project.json': JSON.stringify({ + name: 'root', + targets: { + echo: { + executor: 'nx:run-commands', + options: { + command: 'echo 1', + }, + }, + echo2: { + executor: 'nx:run-commands', + options: { + command: 'echo 2', + }, + }, + }, + }), + }, + '/root' + ); + + context.nxJsonConfiguration.targetDefaults = { + 'nx:run-commands': { + options: { + cwd: '{projectRoot}', + }, + }, + }; + + expect(createNodesFn('project.json', undefined, context)) + .toMatchInlineSnapshot(` + { + "projects": { + ".": { + "targets": { + "echo": { + "options": { + "cwd": "{projectRoot}", + }, + }, + "echo2": { + "options": { + "cwd": "{projectRoot}", + }, + }, + "nx:run-commands": { + "options": { + "cwd": "{projectRoot}", + }, + Symbol(ONLY_MODIFIES_EXISTING_TARGET): true, + }, + }, + }, + }, + } + `); + }); + }); }); diff --git a/packages/nx/src/plugins/target-defaults/target-defaults-plugin.ts b/packages/nx/src/plugins/target-defaults/target-defaults-plugin.ts index 57fac4355a..32aecaa843 100644 --- a/packages/nx/src/plugins/target-defaults/target-defaults-plugin.ts +++ b/packages/nx/src/plugins/target-defaults/target-defaults-plugin.ts @@ -67,6 +67,7 @@ export const TargetDefaultsPlugin: NxPluginV2 = { return true; }), ...Object.keys(projectJson?.targets ?? {}), + ...Object.keys(packageJson?.nx?.targets ?? {}), ]); const executorToTargetMap = getExecutorToTargetMap( @@ -74,27 +75,31 @@ export const TargetDefaultsPlugin: NxPluginV2 = { projectJson ); - const newTargets: Record< + const modifiedTargets: Record< string, TargetConfiguration & { [ONLY_MODIFIES_EXISTING_TARGET]?: boolean } > = {}; for (const defaultSpecifier in targetDefaults) { - const targetName = - executorToTargetMap.get(defaultSpecifier) ?? defaultSpecifier; - newTargets[targetName] = structuredClone( - targetDefaults[defaultSpecifier] - ); - // TODO: Remove this after we figure out a way to define new targets - // in target defaults - if (!projectDefinedTargets.has(targetName)) { - newTargets[targetName][ONLY_MODIFIES_EXISTING_TARGET] = true; + const targetNames = + executorToTargetMap.get(defaultSpecifier) ?? new Set(); + targetNames.add(defaultSpecifier); + + for (const targetName of targetNames) { + modifiedTargets[targetName] = structuredClone( + targetDefaults[defaultSpecifier] + ); + // TODO: Remove this after we figure out a way to define new targets + // in target defaults + if (!projectDefinedTargets.has(targetName)) { + modifiedTargets[targetName][ONLY_MODIFIES_EXISTING_TARGET] = true; + } } } return { projects: { [root]: { - targets: newTargets, + targets: modifiedTargets, }, }, }; @@ -106,20 +111,20 @@ function getExecutorToTargetMap( packageJson: PackageJson, projectJson: ProjectConfiguration ) { - const executorToTargetMap = new Map(); - if (packageJson?.scripts) { - for (const script in packageJson.scripts) { - executorToTargetMap.set('nx:run-script', script); - } - } - if (projectJson?.targets) { - for (const target in projectJson.targets) { - if (projectJson.targets[target].executor) { - executorToTargetMap.set(projectJson.targets[target].executor, target); - } else if (projectJson.targets[target].command) { - executorToTargetMap.set('nx:run-commands', target); - } - } + const executorToTargetMap = new Map>(); + const targets = Object.keys({ + ...projectJson?.targets, + ...packageJson?.scripts, + ...packageJson?.nx?.targets, + }); + for (const target of targets) { + const executor = + projectJson?.targets?.[target]?.executor ?? + packageJson?.nx?.targets?.[target]?.executor ?? + 'nx:run-script'; + const targetsForExecutor = executorToTargetMap.get(executor) ?? new Set(); + targetsForExecutor.add(target); + executorToTargetMap.set(executor, targetsForExecutor); } return executorToTargetMap; } diff --git a/packages/nx/src/project-graph/utils/project-configuration-utils.spec.ts b/packages/nx/src/project-graph/utils/project-configuration-utils.spec.ts index 5374938276..852269e2dc 100644 --- a/packages/nx/src/project-graph/utils/project-configuration-utils.spec.ts +++ b/packages/nx/src/project-graph/utils/project-configuration-utils.spec.ts @@ -403,6 +403,21 @@ describe('project-configuration-utils', () => { `); }); }); + + describe('cache', () => { + it('should not be merged for incompatible targets', () => { + const result = mergeTargetConfigurations( + { + executor: 'foo', + }, + { + executor: 'bar', + cache: true, + } + ); + expect(result.cache).not.toBeDefined(); + }); + }); }); describe('mergeProjectConfigurationIntoRootMap', () => { diff --git a/packages/nx/src/utils/package-json.spec.ts b/packages/nx/src/utils/package-json.spec.ts index b4ea5a0e6d..5c42572a25 100644 --- a/packages/nx/src/utils/package-json.spec.ts +++ b/packages/nx/src/utils/package-json.spec.ts @@ -4,7 +4,6 @@ import { readJsonFile } from './fileutils'; import { buildTargetFromScript, PackageJson, - PackageJsonTargetConfiguration, readModulePackageJson, readTargetsFromPackageJson, } from './package-json'; @@ -36,7 +35,7 @@ describe('buildTargetFromScript', () => { script: 'other', }, executor: 'custom:execute', - } as PackageJsonTargetConfiguration, + }, }, }); diff --git a/packages/nx/src/utils/package-json.ts b/packages/nx/src/utils/package-json.ts index 5f1bd57f97..95487c4588 100644 --- a/packages/nx/src/utils/package-json.ts +++ b/packages/nx/src/utils/package-json.ts @@ -7,16 +7,11 @@ import { import { readJsonFile } from './fileutils'; import { getNxRequirePaths } from './installation-directory'; -export type PackageJsonTargetConfiguration = Omit< - TargetConfiguration, - 'executor' ->; - export interface NxProjectPackageJsonConfiguration { implicitDependencies?: string[]; tags?: string[]; namedInputs?: { [inputName: string]: (string | InputDefinition)[] }; - targets?: Record; + targets?: Record; includedScripts?: string[]; }