From fbd62db2bf0cc7af215ecf6da7be06d8985e15c5 Mon Sep 17 00:00:00 2001 From: AgentEnder Date: Mon, 5 Feb 2024 14:02:41 -0500 Subject: [PATCH] fix(core): prevent target defaults from being discarded during merge process --- .../target-defaults-plugin.spec.ts | 108 +++++++++++++++++- .../target-defaults/target-defaults-plugin.ts | 96 +++++++++++++++- 2 files changed, 202 insertions(+), 2 deletions(-) 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 a13cd539042a3..5dfd18ddf023b 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 @@ -2,7 +2,7 @@ import * as memfs from 'memfs'; import '../../../src/internal-testing-utils/mock-fs'; -import { TargetDefaultsPlugin } from './target-defaults-plugin'; +import { getTargetInfo, TargetDefaultsPlugin } from './target-defaults-plugin'; import { CreateNodesContext } from '../../utils/nx-plugin'; const { createNodes: [, createNodesFn], @@ -74,6 +74,7 @@ describe('target-defaults plugin', () => { "dependsOn": [ "^build", ], + "executor": "nx:run-commands", }, }, }, @@ -114,6 +115,10 @@ describe('target-defaults plugin', () => { "targets": { "test": { "command": "jest", + "executor": "nx:run-script", + "options": { + "script": "test", + }, }, }, }, @@ -156,6 +161,10 @@ describe('target-defaults plugin', () => { "targets": { "test": { "command": "jest", + "executor": "nx:run-script", + "options": { + "script": "test", + }, }, }, }, @@ -225,6 +234,10 @@ describe('target-defaults plugin', () => { "targets": { "test": { "command": "jest", + "executor": "nx:run-script", + "options": { + "script": "test", + }, Symbol(ONLY_MODIFIES_EXISTING_TARGET): true, }, }, @@ -274,11 +287,13 @@ describe('target-defaults plugin', () => { ".": { "targets": { "echo": { + "executor": "nx:run-commands", "options": { "cwd": "{projectRoot}", }, }, "echo2": { + "executor": "nx:run-commands", "options": { "cwd": "{projectRoot}", }, @@ -336,11 +351,13 @@ describe('target-defaults plugin', () => { ".": { "targets": { "echo": { + "executor": "nx:run-commands", "options": { "cwd": "{projectRoot}", }, }, "echo2": { + "executor": "nx:run-commands", "options": { "cwd": "{projectRoot}", }, @@ -358,4 +375,93 @@ describe('target-defaults plugin', () => { `); }); }); + + describe('get target info', () => { + it('should include command for single command', () => { + const result = getTargetInfo( + 'echo', + { + targets: { + echo: { + command: 'echo hi', + }, + }, + }, + null + ); + expect(result).toMatchInlineSnapshot(` + { + "command": "echo hi", + } + `); + }); + + it('should include command for run-commands', () => { + const result = getTargetInfo( + 'echo', + { + targets: { + echo: { + executor: 'nx:run-commands', + options: { + command: 'echo hi', + cwd: '{projectRoot}', + }, + }, + }, + }, + null + ); + expect(result).toMatchInlineSnapshot(` + { + "executor": "nx:run-commands", + "options": { + "command": "echo hi", + }, + } + `); + }); + + it('should include script for run-script', () => { + expect( + getTargetInfo('build', null, { + scripts: { + build: 'echo hi', + }, + }) + ).toMatchInlineSnapshot(` + { + "executor": "nx:run-script", + "options": { + "script": "build", + }, + } + `); + + expect( + getTargetInfo('echo', null, { + scripts: { + build: 'echo hi', + }, + nx: { + targets: { + echo: { + executor: 'nx:run-script', + options: { + script: 'build', + }, + }, + }, + }, + }) + ).toMatchInlineSnapshot(` + { + "executor": "nx:run-script", + "options": { + "script": "build", + }, + } + `); + }); + }); }); 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 72d16da42604d..884140531a244 100644 --- a/packages/nx/src/plugins/target-defaults/target-defaults-plugin.ts +++ b/packages/nx/src/plugins/target-defaults/target-defaults-plugin.ts @@ -88,9 +88,13 @@ export const TargetDefaultsPlugin: NxPluginV2 = { // Prevents `build` from overwriting `@nx/js:tsc` if both are present // and build is specified later in the ordering. if (!modifiedTargets[targetName] || targetName !== defaultSpecifier) { - modifiedTargets[targetName] = JSON.parse( + const defaults = JSON.parse( JSON.stringify(targetDefaults[defaultSpecifier]) ); + modifiedTargets[targetName] = { + ...getTargetInfo(targetName, projectJson, packageJson), + ...defaults, + }; } // TODO: Remove this after we figure out a way to define new targets // in target defaults @@ -140,3 +144,93 @@ function readJsonOrNull(path: string) { return null; } } + +/** + * This fn gets target info that would make a target uniquely compatible + * with what is described by project.json or package.json. As the merge process + * for config happens, without this, the target defaults may be compatible + * with a config from a plugin and then that combined target be incompatible + * with the project json configuration resulting in the target default values + * being scrapped. By adding enough information from the project.json / package.json, + * we can make sure that the target after merging is compatible with the defined target. + */ +export function getTargetInfo( + target: string, + projectJson: Pick, + packageJson: Pick +) { + const projectJsonTarget = projectJson?.targets?.[target]; + const packageJsonTarget = packageJson?.nx?.targets?.[target]; + + const executor = getTargetExecutor(target, projectJson, packageJson); + const targetOptions = { + ...packageJsonTarget?.options, + ...projectJsonTarget?.options, + }; + + if (projectJsonTarget?.command) { + return { + command: projectJsonTarget?.command, + }; + } + + if (executor === 'nx:run-commands') { + if (targetOptions?.command) { + return { + executor: 'nx:run-commands', + options: { + command: projectJsonTarget.options?.command, + }, + }; + } else if (targetOptions?.commands) { + return { + executor: 'nx:run-commands', + options: { + commands: targetOptions.commands, + }, + }; + } + return { + executor: 'nx:run-commands', + }; + } + + if (executor === 'nx:run-script') { + return { + executor: 'nx:run-script', + options: { + script: targetOptions?.script ?? target, + }, + }; + } + + if (executor) { + return { executor }; + } + + return {}; +} + +function getTargetExecutor( + target: string, + projectJson: Pick, + packageJson: Pick +) { + const projectJsonTarget = projectJson?.targets?.[target]; + const packageJsonTarget = packageJson?.nx?.targets?.[target]; + const packageJsonScript = packageJson?.scripts?.[target]; + + if (projectJsonTarget?.command) { + return 'nx:run-commands'; + } + + if ( + !projectJsonTarget?.executor && + !packageJsonTarget?.executor && + packageJsonScript + ) { + return 'nx:run-script'; + } + + return projectJsonTarget?.executor ?? packageJsonTarget?.executor; +}