diff --git a/docs/generated/devkit/Task.md b/docs/generated/devkit/Task.md index bd068534e58e4..e4eb949a53056 100644 --- a/docs/generated/devkit/Task.md +++ b/docs/generated/devkit/Task.md @@ -11,6 +11,7 @@ A representation of the invocation of an Executor - [hash](../../devkit/documents/Task#hash): string - [hashDetails](../../devkit/documents/Task#hashdetails): Object - [id](../../devkit/documents/Task#id): string +- [outputs](../../devkit/documents/Task#outputs): string[] - [overrides](../../devkit/documents/Task#overrides): any - [projectRoot](../../devkit/documents/Task#projectroot): string - [startTime](../../devkit/documents/Task#starttime): number @@ -67,6 +68,14 @@ Unique ID --- +### outputs + +• **outputs**: `string`[] + +The outputs the task may produce + +--- + ### overrides • **overrides**: `any` diff --git a/docs/generated/devkit/getOutputsForTargetAndConfiguration.md b/docs/generated/devkit/getOutputsForTargetAndConfiguration.md index 80540c33b6829..a9d5a4abe02e7 100644 --- a/docs/generated/devkit/getOutputsForTargetAndConfiguration.md +++ b/docs/generated/devkit/getOutputsForTargetAndConfiguration.md @@ -2,14 +2,32 @@ ▸ **getOutputsForTargetAndConfiguration**(`task`, `node`): `string`[] +#### Parameters + +| Name | Type | +| :----- | :-------------------------------------------------------------------------- | +| `task` | [`Task`](../../devkit/documents/Task) | +| `node` | [`ProjectGraphProjectNode`](../../devkit/documents/ProjectGraphProjectNode) | + +#### Returns + +`string`[] + +**`Deprecated`** + +Pass the target and overrides instead. This will be removed in v18. + +▸ **getOutputsForTargetAndConfiguration**(`target`, `overrides`, `node`): `string`[] + Returns the list of outputs that will be cached. #### Parameters -| Name | Type | Description | -| :----- | :-------------------------------------------------------------------------- | :-------------------------------------------------------- | -| `task` | `Pick`<[`Task`](../../devkit/documents/Task), `"overrides"` \| `"target"`\> | target + overrides | -| `node` | [`ProjectGraphProjectNode`](../../devkit/documents/ProjectGraphProjectNode) | ProjectGraphProjectNode object that the task runs against | +| Name | Type | +| :---------- | :----------------------------------------------------------------------------------------------------------------- | +| `target` | [`Task`](../../devkit/documents/Task) \| { `configuration?`: `string` ; `project`: `string` ; `target`: `string` } | +| `overrides` | `any` | +| `node` | [`ProjectGraphProjectNode`](../../devkit/documents/ProjectGraphProjectNode) | #### Returns diff --git a/e2e/angular-core/src/config.test.ts b/e2e/angular-core/src/config.test.ts index 36f16520947d7..663bd42631fe0 100644 --- a/e2e/angular-core/src/config.test.ts +++ b/e2e/angular-core/src/config.test.ts @@ -130,7 +130,7 @@ const angularV1Json = (appName: string) => `{ }, "test": { "builder": "@nx/jest:jest", - "outputs": ["coverage${appName}"], + "outputs": ["{workspaceRoot}/coverage${appName}"], "options": { "jestConfig": "${appName}/jest.config.ts", "passWithNoTests": true diff --git a/e2e/next-core/src/next-webpack.test.ts b/e2e/next-core/src/next-webpack.test.ts index cc69d36acc15d..6881944affdc3 100644 --- a/e2e/next-core/src/next-webpack.test.ts +++ b/e2e/next-core/src/next-webpack.test.ts @@ -84,7 +84,7 @@ describe('Next.js Webpack', () => { updateJson(join('apps', appName, 'project.json'), (json) => { json.targets.build = { command: 'npx next build', - outputs: [`apps/${appName}/.next`], + outputs: [`{projectRoot}/.next`], options: { cwd: `apps/${appName}`, }, diff --git a/e2e/nx-run/src/invoke-runner.test.ts b/e2e/nx-run/src/invoke-runner.test.ts index 4f6f27073c5ea..c28d4285f2f16 100644 --- a/e2e/nx-run/src/invoke-runner.test.ts +++ b/e2e/nx-run/src/invoke-runner.test.ts @@ -35,8 +35,8 @@ describe('Invoke Runner', () => { async function main(){ const r = await initTasksRunner({}); - await r.invoke({tasks: [{id: '${mylib}:prebuild', target: {project: '${mylib}', target: 'prebuild'}, overrides: {__overrides_unparsed__: ''}}]}); - await r.invoke({tasks: [{id: '${mylib}:build', target: {project: '${mylib}', target: 'build'}, overrides: {__overrides_unparsed__: ''}}]}); + await r.invoke({tasks: [{id: '${mylib}:prebuild', target: {project: '${mylib}', target: 'prebuild'}, outputs: [], overrides: {__overrides_unparsed__: ''}}]}); + await r.invoke({tasks: [{id: '${mylib}:build', target: {project: '${mylib}', target: 'build'}, outputs: [], overrides: {__overrides_unparsed__: ''}}]}); } main().then(q => { diff --git a/packages/angular/src/generators/move/lib/update-ng-package.ts b/packages/angular/src/generators/move/lib/update-ng-package.ts index 01c158f410e75..221b8a40a6978 100644 --- a/packages/angular/src/generators/move/lib/update-ng-package.ts +++ b/packages/angular/src/generators/move/lib/update-ng-package.ts @@ -26,12 +26,10 @@ export function updateNgPackage(tree: Tree, schema: MoveImplOptions): void { ); const outputs = getOutputsForTargetAndConfiguration( { - target: { - project: schema.newProjectName, - target: 'build', - }, - overrides: {}, + project: schema.newProjectName, + target: 'build', }, + {}, { name: schema.newProjectName, type: 'lib', diff --git a/packages/js/src/utils/buildable-libs-utils.spec.ts b/packages/js/src/utils/buildable-libs-utils.spec.ts index 44590ae8a22c9..cf865b0c6dcef 100644 --- a/packages/js/src/utils/buildable-libs-utils.spec.ts +++ b/packages/js/src/utils/buildable-libs-utils.spec.ts @@ -293,26 +293,31 @@ describe('calculateDependenciesFromTaskGraph', () => { id: 'lib1:build', overrides: {}, target: { project: 'lib1', target: 'build' }, + outputs: [], }, 'lib2:build': { id: 'lib2:build', overrides: {}, target: { project: 'lib2', target: 'build' }, + outputs: [], }, 'lib2:build-base': { id: 'lib2:build-base', overrides: {}, target: { project: 'lib2', target: 'build-base' }, + outputs: [], }, 'lib3:build': { id: 'lib3:build', overrides: {}, target: { project: 'lib3', target: 'build' }, + outputs: [], }, 'lib4:build': { id: 'lib4:build', overrides: {}, target: { project: 'lib4', target: 'build' }, + outputs: [], }, }, }; @@ -458,41 +463,49 @@ describe('calculateDependenciesFromTaskGraph', () => { id: 'lib1:build', overrides: {}, target: { project: 'lib1', target: 'build' }, + outputs: [], }, 'lib1:build-base': { id: 'lib1:build-base', overrides: {}, target: { project: 'lib1', target: 'build-base' }, + outputs: [], }, 'lib2:build': { id: 'lib2:build', overrides: {}, target: { project: 'lib2', target: 'build' }, + outputs: [], }, 'lib2:build-base': { id: 'lib2:build-base', overrides: {}, target: { project: 'lib2', target: 'build-base' }, + outputs: [], }, 'lib3:build': { id: 'lib3:build', overrides: {}, target: { project: 'lib3', target: 'build' }, + outputs: [], }, 'lib3:build-base': { id: 'lib3:build-base', overrides: {}, target: { project: 'lib3', target: 'build-base' }, + outputs: [], }, 'lib4:build': { id: 'lib4:build', overrides: {}, target: { project: 'lib4', target: 'build' }, + outputs: [], }, 'lib4:build-base': { id: 'lib4:build-base', overrides: {}, target: { project: 'lib4', target: 'build-base' }, + outputs: [], }, }, }; diff --git a/packages/js/src/utils/buildable-libs-utils.ts b/packages/js/src/utils/buildable-libs-utils.ts index 862a529cbf95d..1615417c0a98d 100644 --- a/packages/js/src/utils/buildable-libs-utils.ts +++ b/packages/js/src/utils/buildable-libs-utils.ts @@ -124,13 +124,11 @@ export function calculateProjectDependencies( : dep, outputs: getOutputsForTargetAndConfiguration( { - overrides: {}, - target: { - project: projectName, - target: targetName, - configuration: configurationName, - }, + project: projectName, + target: targetName, + configuration: configurationName, }, + {}, depNode ), node: depNode, @@ -260,7 +258,11 @@ export function calculateDependenciesFromTaskGraph( return null; } - let outputs = getOutputsForTargetAndConfiguration(depTask, depProjectNode); + let outputs = getOutputsForTargetAndConfiguration( + depTask.target, + depTask.overrides, + depProjectNode + ); if (outputs.length === 0) { nonBuildableDependencies.push(depTask.target.project); @@ -558,13 +560,11 @@ export function updateBuildableProjectPackageJsonDependencies( ) { const outputs = getOutputsForTargetAndConfiguration( { - overrides: {}, - target: { - project: projectName, - target: targetName, - configuration: configurationName, - }, + project: projectName, + target: targetName, + configuration: configurationName, }, + {}, node ); @@ -598,13 +598,11 @@ export function updateBuildableProjectPackageJsonDependencies( if (entry.node.type === 'lib') { const outputs = getOutputsForTargetAndConfiguration( { - overrides: {}, - target: { - project: projectName, - target: targetName, - configuration: configurationName, - }, + project: projectName, + target: targetName, + configuration: configurationName, }, + {}, entry.node ); diff --git a/packages/js/src/utils/package-json/update-package-json.ts b/packages/js/src/utils/package-json/update-package-json.ts index 41630fc895be2..33ae926d5e64d 100644 --- a/packages/js/src/utils/package-json/update-package-json.ts +++ b/packages/js/src/utils/package-json/update-package-json.ts @@ -175,13 +175,11 @@ function addMissingDependencies( ) { const outputs = getOutputsForTargetAndConfiguration( { - overrides: {}, - target: { - project: projectName, - target: targetName, - configuration: configurationName, - }, + project: projectName, + target: targetName, + configuration: configurationName, }, + {}, entry.node ); diff --git a/packages/nx/src/command-line/affected/print-affected.ts b/packages/nx/src/command-line/affected/print-affected.ts index 17675c763cc63..d73ff952f4c1b 100644 --- a/packages/nx/src/command-line/affected/print-affected.ts +++ b/packages/nx/src/command-line/affected/print-affected.ts @@ -1,4 +1,4 @@ -import { getCommandAsString, getOutputs } from '../../tasks-runner/utils'; +import { getCommandAsString } from '../../tasks-runner/utils'; import * as yargs from 'yargs'; import type { NxArgs } from '../../utils/command-line-utils'; import { @@ -95,7 +95,7 @@ async function createTasks( target: task.target, hash: task.hash, command: getCommandAsString(execCommand, task), - outputs: getOutputs(projectGraph.nodes, task), + outputs: task.outputs, })); } diff --git a/packages/nx/src/config/task-graph.ts b/packages/nx/src/config/task-graph.ts index 00ec80f94ec28..573f691ccc220 100644 --- a/packages/nx/src/config/task-graph.ts +++ b/packages/nx/src/config/task-graph.ts @@ -27,6 +27,11 @@ export interface Task { * Overrides for the configured options of the target */ overrides: any; + + /** + * The outputs the task may produce + */ + outputs: string[]; /** * Root of the project the task belongs to */ diff --git a/packages/nx/src/hasher/__snapshots__/task-hasher.spec.ts.snap b/packages/nx/src/hasher/__snapshots__/task-hasher.spec.ts.snap index 69b4487325f25..72b319ea618af 100644 --- a/packages/nx/src/hasher/__snapshots__/task-hasher.spec.ts.snap +++ b/packages/nx/src/hasher/__snapshots__/task-hasher.spec.ts.snap @@ -7,7 +7,7 @@ exports[`TaskHasher dependentTasksOutputFiles should depend on dependent tasks o "implicitDeps": {}, "nodes": { "AllExternalDependencies": "3244421341483603138", - "ProjectConfiguration": "12802727827024321009", + "ProjectConfiguration": "16743571606168248002", "TsConfig": "8767608672024750088", "dist/libs/child/index.d.ts": "3244421341483603138", "dist/libs/grandchild/index.d.ts": "3244421341483603138", @@ -18,7 +18,7 @@ exports[`TaskHasher dependentTasksOutputFiles should depend on dependent tasks o }, "runtime": {}, }, - "value": "15526362704205124469", + "value": "14131423056691033336", } `; @@ -29,7 +29,7 @@ exports[`TaskHasher dependentTasksOutputFiles should work with dependent tasks w "implicitDeps": {}, "nodes": { "AllExternalDependencies": "3244421341483603138", - "ProjectConfiguration": "12802727827024321009", + "ProjectConfiguration": "16743571606168248002", "TsConfig": "8767608672024750088", "dist/libs/child/index.d.ts": "3244421341483603138", "dist/libs/grandchild/index.d.ts": "3244421341483603138", @@ -40,7 +40,7 @@ exports[`TaskHasher dependentTasksOutputFiles should work with dependent tasks w }, "runtime": {}, }, - "value": "15526362704205124469", + "value": "14131423056691033336", } `; diff --git a/packages/nx/src/hasher/task-hasher.spec.ts b/packages/nx/src/hasher/task-hasher.spec.ts index a0c5e9487b41c..bfb2dd472ae0f 100644 --- a/packages/nx/src/hasher/task-hasher.spec.ts +++ b/packages/nx/src/hasher/task-hasher.spec.ts @@ -117,6 +117,7 @@ describe('TaskHasher', () => { target: { project: 'parent', target: 'build' }, id: 'parent-build', overrides: { prop: 'prop-value' }, + outputs: [], }, { roots: ['parent-build'], @@ -125,6 +126,7 @@ describe('TaskHasher', () => { id: 'parent-build', target: { project: 'parent', target: 'build' }, overrides: {}, + outputs: [], }, }, dependencies: {}, @@ -181,6 +183,7 @@ describe('TaskHasher', () => { target: { project: 'parent', target: 'build' }, id: 'parent-build', overrides: { prop: 'prop-value' }, + outputs: [], }, { roots: ['child-build'], @@ -189,11 +192,13 @@ describe('TaskHasher', () => { id: 'parent-build', target: { project: 'parent', target: 'build' }, overrides: {}, + outputs: [], }, 'child-build': { id: 'child-build', target: { project: 'child', target: 'build' }, overrides: {}, + outputs: [], }, }, dependencies: { @@ -264,6 +269,7 @@ describe('TaskHasher', () => { target: { project: 'parent', target: 'build' }, id: 'parent-build', overrides: { prop: 'prop-value' }, + outputs: [], }, { roots: ['child-build'], @@ -272,11 +278,13 @@ describe('TaskHasher', () => { id: 'parent-build', target: { project: 'parent', target: 'build' }, overrides: {}, + outputs: [], }, 'child-build': { id: 'child-build', target: { project: 'child', target: 'build' }, overrides: {}, + outputs: [], }, }, dependencies: { @@ -339,6 +347,7 @@ describe('TaskHasher', () => { id: 'parent-test', target: { project: 'parent', target: 'test' }, overrides: {}, + outputs: [], }, }, dependencies: {}, @@ -349,6 +358,7 @@ describe('TaskHasher', () => { target: { project: 'parent', target: 'test' }, id: 'parent-test', overrides: { prop: 'prop-value' }, + outputs: [], }, taskGraph, {} @@ -361,6 +371,7 @@ describe('TaskHasher', () => { target: { project: 'parent', target: 'build' }, id: 'parent-build', overrides: { prop: 'prop-value' }, + outputs: [], }, taskGraph, {} @@ -440,11 +451,13 @@ describe('TaskHasher', () => { id: 'parent-test', target: { project: 'parent', target: 'test' }, overrides: {}, + outputs: [], }, 'child-test': { id: 'child-test', target: { project: 'child', target: 'test' }, overrides: {}, + outputs: [], }, }, dependencies: { @@ -457,6 +470,7 @@ describe('TaskHasher', () => { target: { project: 'parent', target: 'test' }, id: 'parent-test', overrides: { prop: 'prop-value' }, + outputs: [], }, taskGraph, { MY_TEST_HASH_ENV: 'MY_TEST_HASH_ENV_VALUE' } @@ -469,6 +483,7 @@ describe('TaskHasher', () => { target: { project: 'child', target: 'test' }, id: 'child-test', overrides: { prop: 'prop-value' }, + outputs: [], }, taskGraph, { MY_TEST_HASH_ENV: 'MY_TEST_HASH_ENV_VALUE' } @@ -535,6 +550,7 @@ describe('TaskHasher', () => { target: { project: 'parent', target: 'build' }, id: 'parent-build', overrides: { prop: 'prop-value' }, + outputs: [], }, { roots: ['child-build'], @@ -543,11 +559,13 @@ describe('TaskHasher', () => { id: 'parent-build', target: { project: 'parent', target: 'build' }, overrides: {}, + outputs: [], }, 'child-build': { id: 'child-build', target: { project: 'child', target: 'build' }, overrides: {}, + outputs: [], }, }, dependencies: { @@ -594,6 +612,7 @@ describe('TaskHasher', () => { target: { project: 'parent', target: 'build' }, id: 'parent-build', overrides: { prop: 'prop-value' }, + outputs: [], }, { roots: ['parent:build'], @@ -602,6 +621,7 @@ describe('TaskHasher', () => { id: 'parent-build', target: { project: 'parent', target: 'build' }, overrides: {}, + outputs: [], }, }, dependencies: {}, @@ -656,11 +676,13 @@ describe('TaskHasher', () => { id: 'parent-build', target: { project: 'parent', target: 'build' }, overrides: {}, + outputs: [], }, 'child-build': { id: 'child-build', target: { project: 'child', target: 'build' }, overrides: {}, + outputs: [], }, }, dependencies: { @@ -673,6 +695,7 @@ describe('TaskHasher', () => { target: { project: 'parent', target: 'build' }, id: 'parent-build', overrides: { prop: 'prop-value' }, + outputs: [], }, taskGraph, {} @@ -685,6 +708,7 @@ describe('TaskHasher', () => { target: { project: 'child', target: 'build' }, id: 'child-build', overrides: { prop: 'prop-value' }, + outputs: [], }, taskGraph, {} @@ -727,6 +751,7 @@ describe('TaskHasher', () => { target: { project: 'parent', target: 'build' }, id: 'parent-build', overrides: {}, + outputs: [], }, { roots: ['parent:build'], @@ -735,6 +760,7 @@ describe('TaskHasher', () => { id: 'parent-build', target: { project: 'parent', target: 'build' }, overrides: {}, + outputs: [], }, }, dependencies: {}, @@ -795,6 +821,7 @@ describe('TaskHasher', () => { target: { project: 'app', target: 'build' }, id: 'app-build', overrides: { prop: 'prop-value' }, + outputs: [], }, { roots: ['app-build'], @@ -803,6 +830,7 @@ describe('TaskHasher', () => { id: 'app-build', target: { project: 'app', target: 'build' }, overrides: {}, + outputs: [], }, }, dependencies: {}, @@ -860,6 +888,7 @@ describe('TaskHasher', () => { target: { project: 'app', target: 'build' }, id: 'app-build', overrides: { prop: 'prop-value' }, + outputs: [], }, { roots: ['app-build'], @@ -868,6 +897,7 @@ describe('TaskHasher', () => { id: 'app-build', target: { project: 'app', target: 'build' }, overrides: {}, + outputs: [], }, }, dependencies: {}, @@ -916,6 +946,7 @@ describe('TaskHasher', () => { target: { project: 'app', target: 'build' }, id: 'app-build', overrides: { prop: 'prop-value' }, + outputs: [], }, { roots: ['app-build'], @@ -924,6 +955,7 @@ describe('TaskHasher', () => { id: 'app-build', target: { project: 'app', target: 'build' }, overrides: {}, + outputs: [], }, }, dependencies: {}, @@ -1003,11 +1035,13 @@ describe('TaskHasher', () => { id: 'a-build', target: { project: 'a', target: 'build' }, overrides: {}, + outputs: [], }, 'b-build': { id: 'b-build', target: { project: 'b', target: 'build' }, overrides: {}, + outputs: [], }, }, dependencies: {}, @@ -1021,6 +1055,7 @@ describe('TaskHasher', () => { id: 'a-build', target: { project: 'a', target: 'build' }, overrides: {}, + outputs: [], }, taskGraph, {} @@ -1030,6 +1065,7 @@ describe('TaskHasher', () => { id: 'b-build', target: { project: 'b', target: 'build' }, overrides: {}, + outputs: [], }, taskGraph, {} @@ -1040,6 +1076,7 @@ describe('TaskHasher', () => { id: 'b-build', target: { project: 'b', target: 'build' }, overrides: {}, + outputs: [], }, taskGraph, {} @@ -1049,6 +1086,7 @@ describe('TaskHasher', () => { id: 'a-build', target: { project: 'a', target: 'build' }, overrides: {}, + outputs: [], }, taskGraph, {} @@ -1146,6 +1184,7 @@ describe('TaskHasher', () => { target: { project: 'app', target: 'build' }, id: 'app-build', overrides: { prop: 'prop-value' }, + outputs: [], }, { roots: ['app-build'], @@ -1154,6 +1193,7 @@ describe('TaskHasher', () => { id: 'app-build', target: { project: 'app', target: 'build' }, overrides: {}, + outputs: [], }, }, dependencies: {}, @@ -1359,6 +1399,7 @@ describe('TaskHasher', () => { target: { project: 'app', target: 'build' }, id: 'app-build', overrides: { prop: 'prop-value' }, + outputs: [], }, { roots: ['app-build'], @@ -1367,6 +1408,7 @@ describe('TaskHasher', () => { id: 'app-build', target: { project: 'app', target: 'build' }, overrides: {}, + outputs: [], }, }, dependencies: {}, @@ -1440,6 +1482,7 @@ describe('TaskHasher', () => { target: { project: 'app', target: 'build' }, id: 'app-build', overrides: { prop: 'prop-value' }, + outputs: [], }, { roots: ['app-build'], @@ -1448,6 +1491,7 @@ describe('TaskHasher', () => { id: 'app-build', target: { project: 'app', target: 'build' }, overrides: {}, + outputs: [], }, }, dependencies: {}, @@ -1519,6 +1563,7 @@ describe('TaskHasher', () => { target: { project: 'app', target: 'build' }, id: 'app-build', overrides: { prop: 'prop-value' }, + outputs: [], }, { roots: ['app-build'], @@ -1527,6 +1572,7 @@ describe('TaskHasher', () => { id: 'app-build', target: { project: 'app', target: 'build' }, overrides: {}, + outputs: [], }, }, dependencies: {}, @@ -1568,7 +1614,7 @@ describe('TaskHasher', () => { dependsOn: ['^build'], inputs: ['prod', 'deps'], executor: 'nx:run-commands', - outputs: ['dist/{projectRoot}'], + outputs: ['{workspaceRoot}/dist/{projectRoot}'], }, }, }, @@ -1583,7 +1629,7 @@ describe('TaskHasher', () => { dependsOn: ['^build'], inputs: ['prod', 'deps'], executor: 'nx:run-commands', - outputs: ['dist/{projectRoot}'], + outputs: ['{workspaceRoot}/dist/{projectRoot}'], }, }, }, @@ -1598,7 +1644,7 @@ describe('TaskHasher', () => { dependsOn: ['^build'], inputs: ['prod', 'deps'], executor: 'nx:run-commands', - outputs: ['dist/{projectRoot}'], + outputs: ['{workspaceRoot}/dist/{projectRoot}'], }, }, }, @@ -1642,6 +1688,7 @@ describe('TaskHasher', () => { target: { project: 'parent', target: 'build' }, id: 'parent-build', overrides: { prop: 'prop-value' }, + outputs: [], }, { roots: ['grandchild-build'], @@ -1650,16 +1697,19 @@ describe('TaskHasher', () => { id: 'parent-build', target: { project: 'parent', target: 'build' }, overrides: {}, + outputs: ['dist/libs/libs/parent'], }, 'child-build': { id: 'child-build', target: { project: 'child', target: 'build' }, overrides: {}, + outputs: ['dist/libs/libs/child'], }, 'grandchild-build': { id: 'grandchild-build', target: { project: 'grandchild', target: 'build' }, overrides: {}, + outputs: ['dist/libs/libs/grandchild'], }, }, dependencies: { @@ -1702,7 +1752,7 @@ describe('TaskHasher', () => { dependsOn: ['^build'], inputs: ['prod', 'deps'], executor: 'nx:run-commands', - outputs: ['dist/{projectRoot}'], + outputs: ['{workspaceRoot}/dist/{projectRoot}'], }, }, }, @@ -1717,7 +1767,7 @@ describe('TaskHasher', () => { dependsOn: ['^build'], inputs: ['prod', 'deps'], executor: 'nx:run-commands', - outputs: ['dist/{projectRoot}/**/*'], + outputs: ['{workspaceRoot}/dist/{projectRoot}/**/*'], }, }, }, @@ -1732,7 +1782,7 @@ describe('TaskHasher', () => { dependsOn: ['^build'], inputs: ['prod', 'deps'], executor: 'nx:run-commands', - outputs: ['dist/{projectRoot}'], + outputs: ['{workspaceRoot}/dist/{projectRoot}'], }, }, }, @@ -1777,6 +1827,7 @@ describe('TaskHasher', () => { target: { project: 'parent', target: 'build' }, id: 'parent-build', overrides: { prop: 'prop-value' }, + outputs: [], }, { roots: ['grandchild-build'], @@ -1785,16 +1836,19 @@ describe('TaskHasher', () => { id: 'parent-build', target: { project: 'parent', target: 'build' }, overrides: {}, + outputs: [], }, 'child-build': { id: 'child-build', target: { project: 'child', target: 'build' }, overrides: {}, + outputs: [], }, 'grandchild-build': { id: 'grandchild-build', target: { project: 'grandchild', target: 'build' }, overrides: {}, + outputs: [], }, }, dependencies: { diff --git a/packages/nx/src/hasher/task-hasher.ts b/packages/nx/src/hasher/task-hasher.ts index 9b53384e95e8f..b562895fa777b 100644 --- a/packages/nx/src/hasher/task-hasher.ts +++ b/packages/nx/src/hasher/task-hasher.ts @@ -459,7 +459,8 @@ class TaskHasherImpl { for (const d of taskGraph.dependencies[task.id]) { const childTask = taskGraph.tasks[d]; const outputs = getOutputsForTargetAndConfiguration( - childTask, + childTask.target, + childTask.overrides, this.projectGraph.nodes[childTask.target.project] ); const { getFilesForOutputs } = diff --git a/packages/nx/src/tasks-runner/create-task-graph.spec.ts b/packages/nx/src/tasks-runner/create-task-graph.spec.ts index 1fbfd8ac19daf..dde29f32678aa 100644 --- a/packages/nx/src/tasks-runner/create-task-graph.spec.ts +++ b/packages/nx/src/tasks-runner/create-task-graph.spec.ts @@ -13,25 +13,25 @@ describe('createTaskGraph', () => { data: { root: 'app1-root', targets: { - prebuild: { + precompile: { executor: 'nx:run-commands', }, - prebuild2: { + precompile2: { executor: 'nx:run-commands', }, - build: { + compile: { executor: 'nx:run-commands', dependsOn: [ { dependencies: true, - target: 'build', + target: 'compile', }, { - target: 'prebuild', + target: 'precompile', }, { projects: 'app1', - target: 'prebuild2', + target: 'precompile2', }, ], }, @@ -50,7 +50,7 @@ describe('createTaskGraph', () => { data: { root: 'lib1-root', targets: { - build: { + compile: { executor: 'nx:run-commands', }, test: { @@ -104,6 +104,7 @@ describe('createTaskGraph', () => { project: 'app1', target: 'test', }, + outputs: [], overrides: { a: 123 }, projectRoot: 'app1-root', }, @@ -133,6 +134,7 @@ describe('createTaskGraph', () => { project: 'app1', target: 'test', }, + outputs: [], overrides: { a: 123 }, projectRoot: 'app1-root', }, @@ -142,6 +144,7 @@ describe('createTaskGraph', () => { project: 'lib1', target: 'test', }, + outputs: [], overrides: { a: 123 }, projectRoot: 'lib1-root', }, @@ -153,6 +156,42 @@ describe('createTaskGraph', () => { }); }); + it('should return tasks with outputs', () => { + projectGraph.nodes.app1.data.targets.test.outputs = [ + '{workspaceRoot}/dist/app1', + ]; + const taskGraph = createTaskGraph( + projectGraph, + {}, + ['app1'], + ['test'], + 'development', + { + a: 123, + } + ); + + expect(taskGraph.tasks['app1:test'].outputs).toEqual(['dist/app1']); + }); + + it('should return tasks with interpolated outputs', () => { + projectGraph.nodes.app1.data.targets.test.outputs = [ + '{workspaceRoot}/dist/{projectRoot}', + ]; + const taskGraph = createTaskGraph( + projectGraph, + {}, + ['app1'], + ['test'], + 'development', + { + a: 123, + } + ); + + expect(taskGraph.tasks['app1:test'].outputs).toEqual(['dist/app1-root']); + }); + it('should correctly set default configuration', () => { const projectGraph = { nodes: { @@ -163,7 +202,7 @@ describe('createTaskGraph', () => { root: 'app1-root', files: [], targets: { - build: { + compile: { executor: 'my-executor', configurations: { ci: {}, @@ -171,7 +210,7 @@ describe('createTaskGraph', () => { dependsOn: [ { dependencies: true, - target: 'build', + target: 'compile', }, ], }, @@ -185,7 +224,7 @@ describe('createTaskGraph', () => { root: 'lib1-root', files: [], targets: { - build: { + compile: { executor: 'my-executor', configurations: { libDefault: {}, @@ -194,7 +233,7 @@ describe('createTaskGraph', () => { dependsOn: [ { dependencies: true, - target: 'build', + target: 'compile', }, ], }, @@ -208,7 +247,7 @@ describe('createTaskGraph', () => { root: 'lib2-root', files: [], targets: { - build: { + compile: { executor: 'my-executor', configurations: { ci: {}, @@ -225,34 +264,36 @@ describe('createTaskGraph', () => { }, } as any; - const buildLib = createTaskGraph( + const compileLib = createTaskGraph( projectGraph, {}, ['lib1'], - ['build'], + ['compile'], null, {} ); - expect(buildLib).toEqual({ - roots: ['lib2:build'], + expect(compileLib).toEqual({ + roots: ['lib2:compile'], tasks: { - 'lib1:build:libDefault': { - id: 'lib1:build:libDefault', + 'lib1:compile:libDefault': { + id: 'lib1:compile:libDefault', target: { project: 'lib1', - target: 'build', + target: 'compile', configuration: 'libDefault', }, + outputs: [], overrides: {}, projectRoot: 'lib1-root', }, - 'lib2:build': { - id: 'lib2:build', + 'lib2:compile': { + id: 'lib2:compile', target: { project: 'lib2', - target: 'build', + target: 'compile', }, + outputs: [], overrides: { __overrides_unparsed__: [], }, @@ -260,52 +301,55 @@ describe('createTaskGraph', () => { }, }, dependencies: { - 'lib1:build:libDefault': ['lib2:build'], - 'lib2:build': [], + 'lib1:compile:libDefault': ['lib2:compile'], + 'lib2:compile': [], }, }); - const buildApp = createTaskGraph( + const compileApp = createTaskGraph( projectGraph, {}, ['app1'], - ['build'], + ['compile'], 'ci', {} ); - expect(buildApp).toEqual({ - roots: ['lib2:build:ci'], + expect(compileApp).toEqual({ + roots: ['lib2:compile:ci'], tasks: { - 'app1:build:ci': { - id: 'app1:build:ci', + 'app1:compile:ci': { + id: 'app1:compile:ci', target: { project: 'app1', - target: 'build', + target: 'compile', configuration: 'ci', }, + outputs: [], overrides: {}, projectRoot: 'app1-root', }, - 'lib1:build:libDefault': { - id: 'lib1:build:libDefault', + 'lib1:compile:libDefault': { + id: 'lib1:compile:libDefault', target: { project: 'lib1', - target: 'build', + target: 'compile', configuration: 'libDefault', }, + outputs: [], overrides: { __overrides_unparsed__: [], }, projectRoot: 'lib1-root', }, - 'lib2:build:ci': { - id: 'lib2:build:ci', + 'lib2:compile:ci': { + id: 'lib2:compile:ci', target: { project: 'lib2', - target: 'build', + target: 'compile', configuration: 'ci', }, + outputs: [], overrides: { __overrides_unparsed__: [], }, @@ -313,9 +357,9 @@ describe('createTaskGraph', () => { }, }, dependencies: { - 'app1:build:ci': ['lib1:build:libDefault'], - 'lib1:build:libDefault': ['lib2:build:ci'], - 'lib2:build:ci': [], + 'app1:compile:ci': ['lib1:compile:libDefault'], + 'lib1:compile:libDefault': ['lib2:compile:ci'], + 'lib2:compile:ci': [], }, }); }); @@ -330,12 +374,12 @@ describe('createTaskGraph', () => { root: 'app1-root', files: [], targets: { - build: { + compile: { executor: 'my-executor', dependsOn: [ { dependencies: true, - target: 'build', + target: 'compile', }, ], }, @@ -367,7 +411,7 @@ describe('createTaskGraph', () => { root: 'lib3-root', files: [], targets: { - build: { + compile: { executor: 'my-executor', }, }, @@ -385,40 +429,42 @@ describe('createTaskGraph', () => { }, } as any; - const buildApp = createTaskGraph( + const compileApp = createTaskGraph( projectGraph, {}, ['app1'], - ['build'], + ['compile'], null, {} ); - expect(buildApp).toEqual({ + expect(compileApp).toEqual({ dependencies: { - 'app1:build': ['lib3:build'], - 'lib3:build': [], + 'app1:compile': ['lib3:compile'], + 'lib3:compile': [], }, - roots: ['lib3:build'], + roots: ['lib3:compile'], tasks: { - 'app1:build': { - id: 'app1:build', + 'app1:compile': { + id: 'app1:compile', + outputs: [], overrides: {}, projectRoot: 'app1-root', target: { project: 'app1', - target: 'build', + target: 'compile', }, }, - 'lib3:build': { - id: 'lib3:build', + 'lib3:compile': { + id: 'lib3:compile', + outputs: [], overrides: { __overrides_unparsed__: [], }, projectRoot: 'lib3-root', target: { project: 'lib3', - target: 'build', + target: 'compile', }, }, }, @@ -445,6 +491,7 @@ describe('createTaskGraph', () => { project: 'app1', target: 'test', }, + outputs: [], overrides: { a: '--value=app1-root' }, projectRoot: 'app1-root', }, @@ -475,6 +522,7 @@ describe('createTaskGraph', () => { project: 'app1', target: 'test', }, + outputs: [], overrides: { a: '--base-href=/app1-root${deploymentId}' }, projectRoot: 'app1-root', }, @@ -494,21 +542,21 @@ describe('createTaskGraph', () => { data: { root: 'app1-root', targets: { - 'prebuild-base': { + 'precompile-base': { executor: 'nx:run-commands', }, - prebuild: { + precompile: { executor: 'nx:run-commands', }, - build: { + compile: { executor: 'nx:run-commands', dependsOn: [ { dependencies: true, - target: 'build', + target: 'compile', params: 'forward', }, - { target: 'prebuild', params: 'forward' }, + { target: 'precompile', params: 'forward' }, ], }, test: { @@ -526,12 +574,12 @@ describe('createTaskGraph', () => { data: { root: 'lib1-root', targets: { - build: { + compile: { executor: 'nx:run-commands', dependsOn: [ { dependencies: true, - target: 'build', + target: 'compile', params: 'ignore', }, ], @@ -548,7 +596,7 @@ describe('createTaskGraph', () => { data: { root: 'lib2-root', targets: { - build: { + compile: { executor: 'nx:run-commands', }, test: { @@ -572,7 +620,7 @@ describe('createTaskGraph', () => { projectGraph, {}, ['app1'], - ['build'], + ['compile'], 'development', { myFlag: 'flag value', @@ -580,50 +628,54 @@ describe('createTaskGraph', () => { ); expect(taskResult).toEqual({ - roots: ['lib2:build', 'app1:prebuild'], + roots: ['lib2:compile', 'app1:precompile'], tasks: { - 'app1:build': { - id: 'app1:build', + 'app1:compile': { + id: 'app1:compile', target: { project: 'app1', - target: 'build', + target: 'compile', }, + outputs: [], overrides: { myFlag: 'flag value' }, projectRoot: 'app1-root', }, - 'app1:prebuild': { - id: 'app1:prebuild', + 'app1:precompile': { + id: 'app1:precompile', target: { project: 'app1', - target: 'prebuild', + target: 'precompile', }, + outputs: [], overrides: { myFlag: 'flag value' }, projectRoot: 'app1-root', }, - 'lib1:build': { - id: 'lib1:build', + 'lib1:compile': { + id: 'lib1:compile', target: { project: 'lib1', - target: 'build', + target: 'compile', }, + outputs: [], overrides: { myFlag: 'flag value' }, projectRoot: 'lib1-root', }, - 'lib2:build': { - id: 'lib2:build', + 'lib2:compile': { + id: 'lib2:compile', target: { project: 'lib2', - target: 'build', + target: 'compile', }, + outputs: [], overrides: { __overrides_unparsed__: [] }, projectRoot: 'lib2-root', }, }, dependencies: { - 'app1:build': ['lib1:build', 'lib2:build', 'app1:prebuild'], - 'app1:prebuild': [], - 'lib1:build': ['lib2:build'], - 'lib2:build': [], + 'app1:compile': ['lib1:compile', 'lib2:compile', 'app1:precompile'], + 'app1:precompile': [], + 'lib1:compile': ['lib2:compile'], + 'lib2:compile': [], }, }); }); @@ -633,55 +685,59 @@ describe('createTaskGraph', () => { projectGraph, {}, ['app1'], - ['build'], + ['compile'], 'development', { __overrides_unparsed__: [], } ); - // prebuild should also be in here + // precompile should also be in here expect(taskGraph).toEqual({ - roots: ['lib1:build', 'app1:prebuild', 'app1:prebuild2'], + roots: ['lib1:compile', 'app1:precompile', 'app1:precompile2'], tasks: { - 'app1:build': { - id: 'app1:build', + 'app1:compile': { + id: 'app1:compile', target: { project: 'app1', - target: 'build', + target: 'compile', }, + outputs: [], overrides: { __overrides_unparsed__: [], }, projectRoot: 'app1-root', }, - 'app1:prebuild': { - id: 'app1:prebuild', + 'app1:precompile': { + id: 'app1:precompile', target: { project: 'app1', - target: 'prebuild', + target: 'precompile', }, + outputs: [], overrides: { __overrides_unparsed__: [], }, projectRoot: 'app1-root', }, - 'app1:prebuild2': { - id: 'app1:prebuild2', + 'app1:precompile2': { + id: 'app1:precompile2', target: { project: 'app1', - target: 'prebuild2', + target: 'precompile2', }, + outputs: [], overrides: { __overrides_unparsed__: [], }, projectRoot: 'app1-root', }, - 'lib1:build': { - id: 'lib1:build', + 'lib1:compile': { + id: 'lib1:compile', target: { project: 'lib1', - target: 'build', + target: 'compile', }, + outputs: [], overrides: { __overrides_unparsed__: [], }, @@ -689,10 +745,10 @@ describe('createTaskGraph', () => { }, }, dependencies: { - 'app1:build': ['lib1:build', 'app1:prebuild', 'app1:prebuild2'], - 'app1:prebuild': [], - 'app1:prebuild2': [], - 'lib1:build': [], + 'app1:compile': ['lib1:compile', 'app1:precompile', 'app1:precompile2'], + 'app1:precompile': [], + 'app1:precompile2': [], + 'lib1:compile': [], }, }); }); @@ -702,55 +758,59 @@ describe('createTaskGraph', () => { projectGraph, {}, ['app1', 'lib1'], - ['build', 'prebuild'], + ['compile', 'precompile'], 'development', { __overrides_unparsed__: [], } ); - // prebuild should also be in here + // precompile should also be in here expect(taskGraph).toEqual({ - roots: ['app1:prebuild', 'lib1:build', 'app1:prebuild2'], + roots: ['app1:precompile', 'lib1:compile', 'app1:precompile2'], tasks: { - 'app1:build': { - id: 'app1:build', + 'app1:compile': { + id: 'app1:compile', target: { project: 'app1', - target: 'build', + target: 'compile', }, + outputs: [], overrides: { __overrides_unparsed__: [], }, projectRoot: 'app1-root', }, - 'app1:prebuild': { - id: 'app1:prebuild', + 'app1:precompile': { + id: 'app1:precompile', target: { project: 'app1', - target: 'prebuild', + target: 'precompile', }, + outputs: [], overrides: { __overrides_unparsed__: [], }, projectRoot: 'app1-root', }, - 'app1:prebuild2': { - id: 'app1:prebuild2', + 'app1:precompile2': { + id: 'app1:precompile2', target: { project: 'app1', - target: 'prebuild2', + target: 'precompile2', }, + outputs: [], overrides: { __overrides_unparsed__: [], }, projectRoot: 'app1-root', }, - 'lib1:build': { - id: 'lib1:build', + 'lib1:compile': { + id: 'lib1:compile', target: { project: 'lib1', - target: 'build', + target: 'compile', }, + outputs: [], overrides: { __overrides_unparsed__: [], }, @@ -758,10 +818,10 @@ describe('createTaskGraph', () => { }, }, dependencies: { - 'app1:build': ['lib1:build', 'app1:prebuild', 'app1:prebuild2'], - 'app1:prebuild': [], - 'app1:prebuild2': [], - 'lib1:build': [], + 'app1:compile': ['lib1:compile', 'app1:precompile', 'app1:precompile2'], + 'app1:precompile': [], + 'app1:precompile2': [], + 'lib1:compile': [], }, }); }); @@ -775,7 +835,7 @@ describe('createTaskGraph', () => { data: { root: 'app1-root', targets: { - build: { + compile: { executor: 'nx:run-commands', }, }, @@ -787,7 +847,7 @@ describe('createTaskGraph', () => { data: { root: 'lib1-root', targets: { - build: { + compile: { executor: 'nx:run-commands', }, }, @@ -799,7 +859,7 @@ describe('createTaskGraph', () => { data: { root: 'lib2-root', targets: { - build: { + compile: { executor: 'nx:run-commands', }, }, @@ -811,7 +871,7 @@ describe('createTaskGraph', () => { data: { root: 'lib3-root', targets: { - build: { + compile: { executor: 'nx:run-commands', }, }, @@ -832,63 +892,67 @@ describe('createTaskGraph', () => { const taskGraph = createTaskGraph( projectGraph, { - build: [ + compile: [ { dependencies: true, - target: 'build', + target: 'compile', }, ], }, ['app1'], - ['build'], + ['compile'], 'development', { __overrides_unparsed__: [], } ); - // prebuild should also be in here + // precompile should also be in here expect(taskGraph).toEqual({ - roots: ['lib3:build'], + roots: ['lib3:compile'], tasks: { - 'app1:build': { - id: 'app1:build', + 'app1:compile': { + id: 'app1:compile', target: { project: 'app1', - target: 'build', + target: 'compile', }, + outputs: [], overrides: { __overrides_unparsed__: [], }, projectRoot: 'app1-root', }, - 'lib1:build': { - id: 'lib1:build', + 'lib1:compile': { + id: 'lib1:compile', target: { project: 'lib1', - target: 'build', + target: 'compile', }, + outputs: [], overrides: { __overrides_unparsed__: [], }, projectRoot: 'lib1-root', }, - 'lib2:build': { - id: 'lib2:build', + 'lib2:compile': { + id: 'lib2:compile', target: { project: 'lib2', - target: 'build', + target: 'compile', }, + outputs: [], overrides: { __overrides_unparsed__: [], }, projectRoot: 'lib2-root', }, - 'lib3:build': { - id: 'lib3:build', + 'lib3:compile': { + id: 'lib3:compile', target: { project: 'lib3', - target: 'build', + target: 'compile', }, + outputs: [], overrides: { __overrides_unparsed__: [], }, @@ -896,10 +960,10 @@ describe('createTaskGraph', () => { }, }, dependencies: { - 'app1:build': ['lib1:build', 'lib2:build'], - 'lib1:build': ['lib3:build'], - 'lib2:build': ['lib3:build'], - 'lib3:build': [], + 'app1:compile': ['lib1:compile', 'lib2:compile'], + 'lib1:compile': ['lib3:compile'], + 'lib2:compile': ['lib3:compile'], + 'lib3:compile': [], }, }); }); @@ -913,7 +977,7 @@ describe('createTaskGraph', () => { data: { root: 'app1-root', targets: { - build: { + compile: { executor: 'nx:run-commands', }, }, @@ -925,7 +989,7 @@ describe('createTaskGraph', () => { data: { root: 'app2-root', targets: { - build: { + compile: { executor: 'nx:run-commands', }, }, @@ -988,9 +1052,9 @@ describe('createTaskGraph', () => { const taskGraph = createTaskGraph( projectGraph, { - build: ['^build'], + compile: ['^compile'], apply: [ - { dependencies: true, target: 'build' }, + { dependencies: true, target: 'compile' }, { dependencies: true, target: 'apply', @@ -1006,52 +1070,57 @@ describe('createTaskGraph', () => { } ); - // prebuild should also be in here + // precompile should also be in here expect(taskGraph).toEqual({ - roots: ['app2:build', 'coreInfra:apply', 'app1:build'], + roots: ['app2:compile', 'coreInfra:apply', 'app1:compile'], tasks: { 'infra1:apply': { id: 'infra1:apply', target: { project: 'infra1', target: 'apply' }, projectRoot: 'infra1-root', + outputs: [], overrides: { myFlag: 'flag value' }, }, - 'app2:build': { - id: 'app2:build', - target: { project: 'app2', target: 'build' }, + 'app2:compile': { + id: 'app2:compile', + target: { project: 'app2', target: 'compile' }, projectRoot: 'app2-root', + outputs: [], overrides: { __overrides_unparsed__: [] }, }, 'coreInfra:apply': { id: 'coreInfra:apply', target: { project: 'coreInfra', target: 'apply' }, projectRoot: 'infra3-root', + outputs: [], overrides: { myFlag: 'flag value' }, }, - 'app1:build': { - id: 'app1:build', - target: { project: 'app1', target: 'build' }, + 'app1:compile': { + id: 'app1:compile', + target: { project: 'app1', target: 'compile' }, projectRoot: 'app1-root', + outputs: [], overrides: { __overrides_unparsed__: [] }, }, 'infra2:apply': { id: 'infra2:apply', target: { project: 'infra2', target: 'apply' }, projectRoot: 'infra2-root', + outputs: [], overrides: { myFlag: 'flag value' }, }, }, dependencies: { 'infra1:apply': [ - 'app2:build', + 'app2:compile', 'coreInfra:apply', - 'app1:build', + 'app1:compile', 'infra2:apply', ], - 'app2:build': [], + 'app2:compile': [], 'coreInfra:apply': [], - 'app1:build': [], - 'infra2:apply': ['app2:build', 'coreInfra:apply'], + 'app1:compile': [], + 'infra2:apply': ['app2:compile', 'coreInfra:apply'], }, }); }); @@ -1065,13 +1134,13 @@ describe('createTaskGraph', () => { data: { root: 'app1-root', targets: { - build: { + compile: { executor: 'nx:run-commands', dependsOn: [{ target: 'test' }], }, test: { executor: 'nx:run-commands', - dependsOn: [{ target: 'build' }], + dependsOn: [{ target: 'compile' }], }, }, }, @@ -1084,22 +1153,23 @@ describe('createTaskGraph', () => { projectGraph, {}, ['app1'], - ['build'], + ['compile'], 'development', { __overrides_unparsed__: [], } ); - // prebuild should also be in here + // precompile should also be in here expect(taskGraph).toEqual({ roots: [], tasks: { - 'app1:build': { - id: 'app1:build', + 'app1:compile': { + id: 'app1:compile', target: { project: 'app1', - target: 'build', + target: 'compile', }, + outputs: [], overrides: { __overrides_unparsed__: [], }, @@ -1111,6 +1181,7 @@ describe('createTaskGraph', () => { project: 'app1', target: 'test', }, + outputs: [], overrides: { __overrides_unparsed__: [], }, @@ -1118,8 +1189,8 @@ describe('createTaskGraph', () => { }, }, dependencies: { - 'app1:build': ['app1:test'], - 'app1:test': ['app1:build'], + 'app1:compile': ['app1:test'], + 'app1:test': ['app1:compile'], }, }); }); @@ -1133,7 +1204,7 @@ describe('createTaskGraph', () => { data: { root: 'app1-root', targets: { - build: { + compile: { executor: 'nx:run-commands', }, }, @@ -1153,7 +1224,7 @@ describe('createTaskGraph', () => { data: { root: 'app3-root', targets: { - build: { + compile: { executor: 'nx:run-commands', }, }, @@ -1173,10 +1244,10 @@ describe('createTaskGraph', () => { const taskGraph = createTaskGraph( projectGraph, { - build: [{ target: 'build', dependencies: true }], + compile: [{ target: 'compile', dependencies: true }], }, ['app1'], - ['build'], + ['compile'], 'development', { __overrides_unparsed__: [], @@ -1185,23 +1256,25 @@ describe('createTaskGraph', () => { expect(taskGraph).toEqual({ roots: [], tasks: { - 'app1:build': { - id: 'app1:build', + 'app1:compile': { + id: 'app1:compile', target: { project: 'app1', - target: 'build', + target: 'compile', }, + outputs: [], overrides: { __overrides_unparsed__: [], }, projectRoot: 'app1-root', }, - 'app3:build': { - id: 'app3:build', + 'app3:compile': { + id: 'app3:compile', target: { project: 'app3', - target: 'build', + target: 'compile', }, + outputs: [], overrides: { __overrides_unparsed__: [], }, @@ -1209,8 +1282,8 @@ describe('createTaskGraph', () => { }, }, dependencies: { - 'app1:build': ['app3:build'], - 'app3:build': ['app1:build'], + 'app1:compile': ['app3:compile'], + 'app3:compile': ['app1:compile'], }, }); }); @@ -1224,7 +1297,7 @@ describe('createTaskGraph', () => { data: { root: 'app1-root', targets: { - build: { + compile: { executor: 'nx:run-commands', }, }, @@ -1244,7 +1317,7 @@ describe('createTaskGraph', () => { data: { root: 'app3-root', targets: { - build: { + compile: { executor: 'nx:run-commands', }, }, @@ -1261,35 +1334,37 @@ describe('createTaskGraph', () => { const taskGraph = createTaskGraph( projectGraph, { - build: [{ target: 'build', dependencies: true }], + compile: [{ target: 'compile', dependencies: true }], }, ['app1'], - ['build'], + ['compile'], 'development', { __overrides_unparsed__: [], } ); expect(taskGraph).toEqual({ - roots: ['app3:build'], + roots: ['app3:compile'], tasks: { - 'app1:build': { - id: 'app1:build', + 'app1:compile': { + id: 'app1:compile', target: { project: 'app1', - target: 'build', + target: 'compile', }, + outputs: [], overrides: { __overrides_unparsed__: [], }, projectRoot: 'app1-root', }, - 'app3:build': { - id: 'app3:build', + 'app3:compile': { + id: 'app3:compile', target: { project: 'app3', - target: 'build', + target: 'compile', }, + outputs: [], overrides: { __overrides_unparsed__: [], }, @@ -1297,8 +1372,8 @@ describe('createTaskGraph', () => { }, }, dependencies: { - 'app1:build': ['app3:build'], - 'app3:build': [], + 'app1:compile': ['app3:compile'], + 'app3:compile': [], }, }); }); @@ -1312,14 +1387,14 @@ describe('createTaskGraph', () => { data: { root: 'app1-root', targets: { - build: { + compile: { executor: 'nx:run-commands', dependsOn: [ - { target: 'prebuild' }, - { target: 'build', dependencies: true }, + { target: 'precompile' }, + { target: 'compile', dependencies: true }, ], }, - prebuild: { + precompile: { executor: 'nx:run-commands', }, }, @@ -1331,7 +1406,7 @@ describe('createTaskGraph', () => { data: { root: 'app2-root', targets: { - build: { + compile: { executor: 'nx:run-commands', }, }, @@ -1348,23 +1423,24 @@ describe('createTaskGraph', () => { projectGraph, {}, ['app1'], - ['build'], + ['compile'], 'development', { __overrides_unparsed__: [], }, true ); - // prebuild should also be in here + // precompile should also be in here expect(taskGraph).toEqual({ - roots: ['app1:build'], + roots: ['app1:compile'], tasks: { - 'app1:build': { - id: 'app1:build', + 'app1:compile': { + id: 'app1:compile', target: { project: 'app1', - target: 'build', + target: 'compile', }, + outputs: [], overrides: { __overrides_unparsed__: [], }, @@ -1372,7 +1448,7 @@ describe('createTaskGraph', () => { }, }, dependencies: { - 'app1:build': [], + 'app1:compile': [], }, }); @@ -1380,34 +1456,36 @@ describe('createTaskGraph', () => { projectGraph, {}, ['app1', 'app2'], - ['build'], + ['compile'], 'development', { __overrides_unparsed__: [], }, true ); - // prebuild should also be in here + // precompile should also be in here expect(taskGraph2).toEqual({ - roots: ['app2:build'], + roots: ['app2:compile'], tasks: { - 'app1:build': { - id: 'app1:build', + 'app1:compile': { + id: 'app1:compile', target: { project: 'app1', - target: 'build', + target: 'compile', }, + outputs: [], overrides: { __overrides_unparsed__: [], }, projectRoot: 'app1-root', }, - 'app2:build': { - id: 'app2:build', + 'app2:compile': { + id: 'app2:compile', target: { project: 'app2', - target: 'build', + target: 'compile', }, + outputs: [], overrides: { __overrides_unparsed__: [], }, @@ -1415,8 +1493,8 @@ describe('createTaskGraph', () => { }, }, dependencies: { - 'app1:build': ['app2:build'], - 'app2:build': [], + 'app1:compile': ['app2:compile'], + 'app2:compile': [], }, }); }); diff --git a/packages/nx/src/tasks-runner/create-task-graph.ts b/packages/nx/src/tasks-runner/create-task-graph.ts index 412bc2329f62c..8ed4e75c34114 100644 --- a/packages/nx/src/tasks-runner/create-task-graph.ts +++ b/packages/nx/src/tasks-runner/create-task-graph.ts @@ -1,5 +1,5 @@ import { ProjectGraph, ProjectGraphProjectNode } from '../config/project-graph'; -import { getDependencyConfigs, interpolate } from './utils'; +import { getDependencyConfigs, getOutputs, interpolate } from './utils'; import { projectHasTarget, projectHasTargetAndConfiguration, @@ -329,11 +329,22 @@ export class ProcessTasks { configuration: resolvedConfiguration, }; + const interpolatedOverrides = interpolateOverrides( + overrides, + project.name, + project.data + ); + return { id, target: qualifiedTarget, projectRoot: project.data.root, - overrides: interpolateOverrides(overrides, project.name, project.data), + overrides: interpolatedOverrides, + outputs: getOutputs( + this.projectGraph.nodes, + qualifiedTarget, + interpolatedOverrides + ), // TODO(v18): Remove cast here after typing is moved back onto TargetConfiguration cache: (project.data.targets[target] as any).cache, }; diff --git a/packages/nx/src/tasks-runner/init-tasks-runner.ts b/packages/nx/src/tasks-runner/init-tasks-runner.ts index ba4d5c4ad3a25..26e9bb956ec64 100644 --- a/packages/nx/src/tasks-runner/init-tasks-runner.ts +++ b/packages/nx/src/tasks-runner/init-tasks-runner.ts @@ -6,6 +6,7 @@ import { Task, TaskGraph } from '../config/task-graph'; import { invokeTasksRunner } from './run-command'; import { InvokeRunnerTerminalOutputLifeCycle } from './life-cycles/invoke-runner-terminal-output-life-cycle'; import { performance } from 'perf_hooks'; +import { getOutputs } from './utils'; export async function initTasksRunner(nxArgs: NxArgs) { performance.mark('init-local'); @@ -21,6 +22,14 @@ export async function initTasksRunner(nxArgs: NxArgs) { parallel: number; }): Promise<{ status: number; taskGraph: TaskGraph }> => { performance.mark('code-loading:end'); + + // TODO: This polyfills the outputs if someone doesn't pass a task with outputs. Remove this in Nx 18 + opts.tasks.forEach((t) => { + if (!t.outputs) { + t.outputs = getOutputs(projectGraph.nodes, t.target, t.overrides); + } + }); + const lifeCycle = new InvokeRunnerTerminalOutputLifeCycle(opts.tasks); const taskGraph = { diff --git a/packages/nx/src/tasks-runner/task-orchestrator.ts b/packages/nx/src/tasks-runner/task-orchestrator.ts index ac5395059efb4..9c25ce755f59a 100644 --- a/packages/nx/src/tasks-runner/task-orchestrator.ts +++ b/packages/nx/src/tasks-runner/task-orchestrator.ts @@ -8,7 +8,6 @@ import { TaskStatus } from './tasks-runner'; import { calculateReverseDeps, getExecutorForTask, - getOutputs, isCacheableTask, removeTasksFromTaskGraph, shouldStreamOutput, @@ -147,7 +146,7 @@ export class TaskOrchestrator { const cachedResult = await this.cache.get(task); if (!cachedResult || cachedResult.code !== 0) return null; - const outputs = getOutputs(this.projectGraph.nodes, task); + const outputs = task.outputs; const shouldCopyOutputsFromCache = !!outputs.length && (await this.shouldCopyOutputsFromCache(outputs, task.hash)); @@ -422,7 +421,7 @@ export class TaskOrchestrator { result.status === 'success' ? 0 : 1, - outputs: getOutputs(this.projectGraph.nodes, result.task), + outputs: result.task.outputs, })) .filter(({ task, code }) => this.shouldCacheTaskResult(task, code)) .filter(({ terminalOutput, outputs }) => terminalOutput || outputs) @@ -551,10 +550,7 @@ export class TaskOrchestrator { private async recordOutputsHash(task: Task) { if (this.daemon?.enabled()) { - return this.daemon.recordOutputsHash( - getOutputs(this.projectGraph.nodes, task), - task.hash - ); + return this.daemon.recordOutputsHash(task.outputs, task.hash); } } diff --git a/packages/nx/src/tasks-runner/tasks-schedule.spec.ts b/packages/nx/src/tasks-runner/tasks-schedule.spec.ts index 2839ca98525c1..ea8b6dc8be23b 100644 --- a/packages/nx/src/tasks-runner/tasks-schedule.spec.ts +++ b/packages/nx/src/tasks-runner/tasks-schedule.spec.ts @@ -13,6 +13,7 @@ function createMockTask(id: string): Task { project, target, }, + outputs: [], overrides: {}, }; } diff --git a/packages/nx/src/tasks-runner/utils.spec.ts b/packages/nx/src/tasks-runner/utils.spec.ts index 999be6a45bc08..438cbf0f36530 100644 --- a/packages/nx/src/tasks-runner/utils.spec.ts +++ b/packages/nx/src/tasks-runner/utils.spec.ts @@ -33,7 +33,8 @@ describe('utils', () => { it('should return empty arrays', () => { expect( getOutputsForTargetAndConfiguration( - task, + task.target, + task.overrides, getNode({ outputs: [], }) @@ -44,7 +45,8 @@ describe('utils', () => { it('should interpolate {workspaceRoot}, {projectRoot} and {projectName}', () => { expect( getOutputsForTargetAndConfiguration( - task, + task.target, + task.overrides, getNode({ outputs: [ '{workspaceRoot}/one', @@ -59,7 +61,8 @@ describe('utils', () => { it('should interpolate {projectRoot} when it is not at the beginning', () => { expect( getOutputsForTargetAndConfiguration( - task, + task.target, + task.overrides, getNode({ outputs: ['{workspaceRoot}/dist/{projectRoot}'], }) @@ -70,7 +73,8 @@ describe('utils', () => { it('should throw when {workspaceRoot} is used not at the beginning', () => { expect(() => getOutputsForTargetAndConfiguration( - task, + task.target, + task.overrides, getNode({ outputs: ['test/{workspaceRoot}/dist'], }) @@ -92,9 +96,13 @@ describe('utils', () => { files: [], }, }; - expect(getOutputsForTargetAndConfiguration(task, data as any)).toEqual([ - 'dist', - ]); + expect( + getOutputsForTargetAndConfiguration( + task.target, + task.overrides, + data as any + ) + ).toEqual(['dist']); }); it('should interpolate {workspaceRoot} when {projectRoot} = . by removing the slash after it', () => { @@ -111,9 +119,13 @@ describe('utils', () => { files: [], }, }; - expect(getOutputsForTargetAndConfiguration(task, data as any)).toEqual([ - 'dist', - ]); + expect( + getOutputsForTargetAndConfiguration( + task.target, + task.overrides, + data as any + ) + ).toEqual(['dist']); }); it('should throw when {projectRoot} is used not at the beginning and the value is .', () => { @@ -131,14 +143,19 @@ describe('utils', () => { }, }; expect(() => - getOutputsForTargetAndConfiguration(task, data as any) + getOutputsForTargetAndConfiguration( + task.target, + task.overrides, + data as any + ) ).toThrow(); }); it('should support interpolation based on options', () => { expect( getOutputsForTargetAndConfiguration( - task, + task.target, + task.overrides, getNode({ outputs: ['{workspaceRoot}/path/{options.myVar}'], options: { @@ -152,7 +169,8 @@ describe('utils', () => { it('should support nested interpolation based on options', () => { expect( getOutputsForTargetAndConfiguration( - task, + task.target, + task.overrides, getNode({ outputs: ['{options.nested.myVar}'], options: { @@ -168,7 +186,8 @@ describe('utils', () => { it('should support interpolation for non-existing options', () => { expect( getOutputsForTargetAndConfiguration( - task, + task.target, + task.overrides, getNode({ outputs: ['{options.outputFile}'], options: {}, @@ -180,7 +199,8 @@ describe('utils', () => { it('should support interpolation based on configuration-specific options', () => { expect( getOutputsForTargetAndConfiguration( - task, + task.target, + task.overrides, getNode({ outputs: ['{options.myVar}'], options: { @@ -199,11 +219,9 @@ describe('utils', () => { it('should support interpolation outputs from overrides', () => { expect( getOutputsForTargetAndConfiguration( + task.target, { - ...task, - overrides: { - myVar: 'value/override', - }, + myVar: 'value/override', }, getNode({ outputs: ['{options.myVar}'], @@ -224,7 +242,8 @@ describe('utils', () => { it('should return the outputPath option', () => { expect( getOutputsForTargetAndConfiguration( - task, + task.target, + task.overrides, getNode({ options: { outputPath: 'value', @@ -237,11 +256,9 @@ describe('utils', () => { it('should handle outputPath overrides', () => { expect( getOutputsForTargetAndConfiguration( + task.target, { - ...task, - overrides: { - outputPath: 'overrideOutputPath', - }, + outputPath: 'overrideOutputPath', }, getNode({ options: { @@ -255,7 +272,8 @@ describe('utils', () => { it('should return configuration-specific outputPath when defined', () => { expect( getOutputsForTargetAndConfiguration( - task, + task.target, + task.overrides, getNode({ options: { outputPath: 'value', @@ -273,7 +291,8 @@ describe('utils', () => { it('should return configuration-independent outputPath when defined', () => { expect( getOutputsForTargetAndConfiguration( - task, + task.target, + task.overrides, getNode({ options: { outputPath: 'value', @@ -288,7 +307,7 @@ describe('utils', () => { it('should return default output paths when nothing else is defined', () => { expect( - getOutputsForTargetAndConfiguration(task, { + getOutputsForTargetAndConfiguration(task.target, task.overrides, { name: 'myapp', type: 'app', data: { @@ -313,9 +332,10 @@ describe('utils', () => { it('should transform non-prefixed paths', () => { expect( getOutputsForTargetAndConfiguration( - task, + task.target, + task.overrides, getNode({ - outputs: ['dist'], + outputs: ['{workspaceRoot}/dist'], }) ) ).toEqual(['dist']); @@ -323,9 +343,10 @@ describe('utils', () => { it('should transform non-prefixed paths that use interpolation', () => { expect( getOutputsForTargetAndConfiguration( - task, + task.target, + task.overrides, getNode({ - outputs: ['dist/{projectRoot}'], + outputs: ['{workspaceRoot}/dist/{projectRoot}'], }) ) ).toEqual(['dist/myapp']); @@ -334,9 +355,10 @@ describe('utils', () => { it('should transform relative paths', () => { expect( getOutputsForTargetAndConfiguration( - task, + task.target, + task.overrides, getNode({ - outputs: ['./sub'], + outputs: ['{projectRoot}/sub'], }) ) ).toEqual(['myapp/sub']); @@ -345,9 +367,10 @@ describe('utils', () => { it('should transform unix-absolute paths', () => { expect( getOutputsForTargetAndConfiguration( - task, + task.target, + task.overrides, getNode({ - outputs: ['/dist'], + outputs: ['{workspaceRoot}/dist'], }) ) ).toEqual(['dist']); diff --git a/packages/nx/src/tasks-runner/utils.ts b/packages/nx/src/tasks-runner/utils.ts index 0be79f270f7c8..d709a094d0b82 100644 --- a/packages/nx/src/tasks-runner/utils.ts +++ b/packages/nx/src/tasks-runner/utils.ts @@ -81,9 +81,14 @@ export function expandDependencyConfigSyntaxSugar( export function getOutputs( p: Record, - task: Task + target: Task['target'], + overrides: Task['overrides'] ) { - return getOutputsForTargetAndConfiguration(task, p[task.target.project]); + return getOutputsForTargetAndConfiguration( + target, + overrides, + p[target.project] + ); } class InvalidOutputsError extends Error { @@ -133,39 +138,43 @@ export function transformLegacyOutputs( } /** - * Returns the list of outputs that will be cached. - * @param task target + overrides - * @param node ProjectGraphProjectNode object that the task runs against + * @deprecated Pass the target and overrides instead. This will be removed in v18. */ export function getOutputsForTargetAndConfiguration( - task: Pick, + task: Task, + node: ProjectGraphProjectNode +): string[]; +export function getOutputsForTargetAndConfiguration( + target: Task['target'] | Task, + overrides: Task['overrides'] | ProjectGraphProjectNode, node: ProjectGraphProjectNode +): string[]; +/** + * Returns the list of outputs that will be cached. + */ +export function getOutputsForTargetAndConfiguration( + taskTargetOrTask: Task['target'] | Task, + overridesOrNode: Task['overrides'] | ProjectGraphProjectNode, + node?: ProjectGraphProjectNode ): string[] { - const { target, configuration } = task.target; + const taskTarget = + 'id' in taskTargetOrTask ? taskTargetOrTask.target : taskTargetOrTask; + const overrides = + 'id' in taskTargetOrTask ? taskTargetOrTask.overrides : overridesOrNode; + node = 'id' in taskTargetOrTask ? overridesOrNode : node; + + const { target, configuration } = taskTarget; const targetConfiguration = node.data.targets[target]; const options = { ...targetConfiguration.options, ...targetConfiguration?.configurations?.[configuration], - ...task.overrides, + ...overrides, }; if (targetConfiguration?.outputs) { - try { - validateOutputs(targetConfiguration.outputs); - } catch (error) { - if (error instanceof InvalidOutputsError) { - // TODO(@FrozenPandaz): In v17, throw this error and do not transform. - console.warn(error.message); - targetConfiguration.outputs = transformLegacyOutputs( - node.data.root, - error - ); - } else { - throw error; - } - } + validateOutputs(targetConfiguration.outputs); return targetConfiguration.outputs .map((output: string) => { diff --git a/packages/plugin/src/generators/executor/files/hasher/__fileName__/hasher.spec.ts__tmpl__ b/packages/plugin/src/generators/executor/files/hasher/__fileName__/hasher.spec.ts__tmpl__ index d52999c71fda0..bcdaac89f331a 100644 --- a/packages/plugin/src/generators/executor/files/hasher/__fileName__/hasher.spec.ts__tmpl__ +++ b/packages/plugin/src/generators/executor/files/hasher/__fileName__/hasher.spec.ts__tmpl__ @@ -13,7 +13,8 @@ describe('<%=propertyName%>Hasher', () => { project: 'proj', target: 'target' }, - overrides: {} + overrides: {}, + outputs: [] }, { hasher: mockHasher } as unknown as HasherContext) diff --git a/packages/workspace/src/utilities/buildable-libs-utils.spec.ts b/packages/workspace/src/utilities/buildable-libs-utils.spec.ts deleted file mode 100644 index 6a44a9449443b..0000000000000 --- a/packages/workspace/src/utilities/buildable-libs-utils.spec.ts +++ /dev/null @@ -1,288 +0,0 @@ -import { DependencyType, ProjectGraph } from '@nx/devkit'; -import { - calculateProjectDependencies, - DependentBuildableProjectNode, - updatePaths, -} from './buildable-libs-utils'; - -describe('updatePaths', () => { - const deps: DependentBuildableProjectNode[] = [ - { name: '@proj/lib', node: {} as any, outputs: ['dist/libs/lib'] }, - ]; - - it('should add path', () => { - const paths: Record = { - '@proj/test': ['libs/test/src/index.ts'], - }; - updatePaths(deps, paths); - expect(paths).toEqual({ - '@proj/lib': ['dist/libs/lib'], - '@proj/test': ['libs/test/src/index.ts'], - }); - }); - - it('should replace paths', () => { - const paths: Record = { - '@proj/lib': ['libs/lib/src/index.ts'], - '@proj/lib/sub': ['libs/lib/sub/src/index.ts'], - }; - updatePaths(deps, paths); - expect(paths).toEqual({ - '@proj/lib': ['dist/libs/lib'], - '@proj/lib/sub': ['dist/libs/lib/sub'], - }); - }); -}); - -describe('calculateProjectDependencies', () => { - it('should include npm packages in dependency list', async () => { - const graph: ProjectGraph = { - nodes: { - example: { - type: 'lib', - name: 'example', - data: { - root: '/root/example', - }, - }, - }, - externalNodes: { - 'npm:formik': { - type: 'npm', - name: 'npm:formik', - data: { - packageName: 'formik', - version: '0.0.0', - }, - }, - }, - dependencies: { - example: [ - { - source: 'example', - target: 'npm:formik', - type: DependencyType.static, - }, - ], - }, - }; - - const results = calculateProjectDependencies( - graph, - 'root', - 'example', - 'build', - undefined - ); - expect(results).toMatchObject({ - target: { - type: 'lib', - name: 'example', - }, - dependencies: [{ name: 'formik' }], - }); - }); - - it('should include npm packages in dependency list and sort them correctly', async () => { - const graph: ProjectGraph = { - nodes: { - example: { - type: 'lib', - name: 'example', - data: { - root: '/root/example', - }, - }, - }, - externalNodes: { - 'npm:some-lib': { - type: 'npm', - name: 'npm:some-lib', - data: { - packageName: 'some-lib', - version: '0.0.0', - }, - }, - 'npm:formik': { - type: 'npm', - name: 'npm:formik', - data: { - packageName: 'formik', - version: '0.0.0', - }, - }, - 'npm:@prefixed-lib': { - type: 'npm', - name: 'npm:@prefixed-lib', - data: { - packageName: '@prefixed-lib', - version: '0.0.0', - }, - }, - }, - dependencies: { - example: [ - { - source: 'example', - target: 'npm:some-lib', - type: DependencyType.static, - }, - { - source: 'example', - target: 'npm:formik', - type: DependencyType.static, - }, - { - source: 'example', - target: 'npm:@prefixed-lib', - type: DependencyType.static, - }, - ], - }, - }; - - const results = await calculateProjectDependencies( - graph, - 'root', - 'example', - 'build', - undefined - ); - expect(results).toMatchObject({ - target: { - type: 'lib', - name: 'example', - }, - dependencies: [ - { name: '@prefixed-lib' }, - { name: 'formik' }, - { name: 'some-lib' }, - ], - }); - }); - - it('should include all top-level dependencies, even ones that are also transitive', async () => { - const graph: ProjectGraph = { - nodes: { - example: { - type: 'lib', - name: 'example', - data: { - root: '/root/example', - targets: { - build: { - executor: 'x', - }, - }, - }, - }, - example2: { - type: 'lib', - name: 'example2', - data: { - root: '/root/example2', - targets: { - build: { - executor: 'x', - }, - }, - }, - }, - }, - externalNodes: { - 'npm:formik': { - type: 'npm', - name: 'npm:formik', - data: { - packageName: 'formik', - version: '0.0.0', - }, - }, - 'npm:foo': { - type: 'npm', - name: 'npm:foo', - data: { - packageName: 'foo', - version: '0.0.0', - }, - }, - }, - dependencies: { - example: [ - // when example2 dependency is listed first - { - source: 'example', - target: 'example2', - type: DependencyType.static, - }, - { - source: 'example', - target: 'npm:formik', - type: DependencyType.static, - }, - ], - example2: [ - // and example2 also depends on npm:formik - { - source: 'example2', - target: 'npm:formik', - type: DependencyType.static, - }, - { - source: 'example2', - target: 'npm:foo', - type: DependencyType.static, - }, - ], - }, - }; - - const results = calculateProjectDependencies( - graph, - 'root', - 'example', - 'build', - undefined - ); - expect(results).toMatchObject({ - target: { - name: 'example', - }, - topLevelDependencies: [ - // expect example2 and formik as top-level deps, but not foo - expect.objectContaining({ name: 'example2' }), - expect.objectContaining({ name: 'formik' }), - ], - }); - }); -}); - -describe('missingDependencies', () => { - it('should throw an error if dependency is missing', async () => { - const graph: ProjectGraph = { - nodes: { - example: { - type: 'lib', - name: 'example', - data: { - root: '/root/example', - }, - }, - }, - externalNodes: {}, - dependencies: { - example: [ - { - source: 'example', - target: 'missing', - type: DependencyType.static, - }, - ], - }, - }; - - expect(() => - calculateProjectDependencies(graph, 'root', 'example', 'build', undefined) - ).toThrow(); - }); -}); diff --git a/packages/workspace/src/utilities/buildable-libs-utils.ts b/packages/workspace/src/utilities/buildable-libs-utils.ts deleted file mode 100644 index 17745dab7ff2f..0000000000000 --- a/packages/workspace/src/utilities/buildable-libs-utils.ts +++ /dev/null @@ -1,468 +0,0 @@ -import { dirname, join, relative } from 'path'; -import { directoryExists, fileExists } from './fileutils'; -import type { ProjectGraph, ProjectGraphProjectNode } from '@nx/devkit'; -import { - getOutputsForTargetAndConfiguration, - ProjectGraphExternalNode, - readJsonFile, - stripIndents, - writeJsonFile, -} from '@nx/devkit'; -import type * as ts from 'typescript'; -import { unlinkSync } from 'fs'; -import { output } from './output'; -import { isNpmProject } from 'nx/src/project-graph/operators'; -import { ensureTypescript } from './typescript'; - -let tsModule: typeof import('typescript'); - -function isBuildable(target: string, node: ProjectGraphProjectNode): boolean { - return ( - node.data.targets && - node.data.targets[target] && - node.data.targets[target].executor !== '' - ); -} - -/** - * @deprecated This type will be removed from @nx/workspace in version 17. Prefer importing from @nx/js. - */ -export type DependentBuildableProjectNode = { - name: string; - outputs: string[]; - node: ProjectGraphProjectNode | ProjectGraphExternalNode; -}; - -/** - * @deprecated This function will be removed from @nx/workspace in version 17. Prefer importing from @nx/js. - */ -export function calculateProjectDependencies( - projGraph: ProjectGraph, - root: string, - projectName: string, - targetName: string, - configurationName: string, - shallow?: boolean -): { - target: ProjectGraphProjectNode; - dependencies: DependentBuildableProjectNode[]; - nonBuildableDependencies: string[]; - topLevelDependencies: DependentBuildableProjectNode[]; -} { - const target = projGraph.nodes[projectName]; - // gather the library dependencies - const nonBuildableDependencies = []; - const topLevelDependencies: DependentBuildableProjectNode[] = []; - const collectedDeps = collectDependencies( - projectName, - projGraph, - [], - shallow - ); - const missing = collectedDeps.reduce( - (missing: string[] | undefined, { name: dep }) => { - const depNode = projGraph.nodes[dep] || projGraph.externalNodes[dep]; - if (!depNode) { - missing = missing || []; - missing.push(dep); - } - return missing; - }, - null - ); - if (missing) { - throw new Error(`Unable to find ${missing.join(', ')} in project graph.`); - } - const dependencies = collectedDeps - .map(({ name: dep, isTopLevel }) => { - let project: DependentBuildableProjectNode = null; - const depNode = projGraph.nodes[dep] || projGraph.externalNodes[dep]; - if (depNode.type === 'lib') { - if (isBuildable(targetName, depNode)) { - const libPackageJsonPath = join( - root, - depNode.data.root, - 'package.json' - ); - - project = { - name: fileExists(libPackageJsonPath) - ? readJsonFile(libPackageJsonPath).name // i.e. @workspace/mylib - : dep, - outputs: getOutputsForTargetAndConfiguration( - { - overrides: {}, - target: { - project: projectName, - target: targetName, - configuration: configurationName, - }, - }, - depNode - ), - node: depNode, - }; - } else { - nonBuildableDependencies.push(dep); - } - } else if (depNode.type === 'npm') { - project = { - name: depNode.data.packageName, - outputs: [], - node: depNode, - }; - } - - if (project && isTopLevel) { - topLevelDependencies.push(project); - } - - return project; - }) - .filter((x) => !!x); - - dependencies.sort((a, b) => (a.name > b.name ? 1 : b.name > a.name ? -1 : 0)); - - return { - target, - dependencies, - nonBuildableDependencies, - topLevelDependencies, - }; -} - -function collectDependencies( - project: string, - projGraph: ProjectGraph, - acc: { name: string; isTopLevel: boolean }[], - shallow?: boolean, - areTopLevelDeps = true -): { name: string; isTopLevel: boolean }[] { - (projGraph.dependencies[project] || []).forEach((dependency) => { - const existingEntry = acc.find((dep) => dep.name === dependency.target); - if (!existingEntry) { - // Temporary skip this. Currently the set of external nodes is built from package.json, not lock file. - // As a result, some nodes might be missing. This should not cause any issues, we can just skip them. - if ( - dependency.target.startsWith('npm:') && - !projGraph.externalNodes[dependency.target] - ) - return; - - acc.push({ name: dependency.target, isTopLevel: areTopLevelDeps }); - const isInternalTarget = projGraph.nodes[dependency.target]; - if (!shallow && isInternalTarget) { - collectDependencies(dependency.target, projGraph, acc, shallow, false); - } - } else if (areTopLevelDeps && !existingEntry.isTopLevel) { - existingEntry.isTopLevel = true; - } - }); - return acc; -} - -function readTsConfigWithRemappedPaths( - tsConfig: string, - generatedTsConfigPath: string, - dependencies: DependentBuildableProjectNode[] -) { - const generatedTsConfig: any = { compilerOptions: {} }; - generatedTsConfig.extends = relative( - dirname(generatedTsConfigPath), - tsConfig - ); - generatedTsConfig.compilerOptions.paths = computeCompilerOptionsPaths( - tsConfig, - dependencies - ); - - if (process.env.NX_VERBOSE_LOGGING_PATH_MAPPINGS === 'true') { - output.log({ - title: 'TypeScript path mappings have been rewritten.', - }); - console.log( - JSON.stringify(generatedTsConfig.compilerOptions.paths, null, 2) - ); - } - return generatedTsConfig; -} - -function computeCompilerOptionsPaths( - tsConfig: string | ts.ParsedCommandLine, - dependencies: DependentBuildableProjectNode[] -) { - const paths = readPaths(tsConfig) || {}; - updatePaths(dependencies, paths); - return paths; -} - -function readPaths(tsConfig: string | ts.ParsedCommandLine) { - if (!tsModule) { - tsModule = ensureTypescript(); - } - try { - let config: ts.ParsedCommandLine; - if (typeof tsConfig === 'string') { - const configFile = tsModule.readConfigFile( - tsConfig, - tsModule.sys.readFile - ); - config = tsModule.parseJsonConfigFileContent( - configFile.config, - tsModule.sys, - dirname(tsConfig) - ); - } else { - config = tsConfig; - } - if (config.options?.paths) { - return config.options.paths; - } else { - return null; - } - } catch (e) { - return null; - } -} - -/** - * @deprecated This function will be removed from @nx/workspace in version 17. Prefer importing from @nx/js. - */ -export function createTmpTsConfig( - tsconfigPath: string, - workspaceRoot: string, - projectRoot: string, - dependencies: DependentBuildableProjectNode[] -) { - const tmpTsConfigPath = join( - workspaceRoot, - 'tmp', - projectRoot, - 'tsconfig.generated.json' - ); - const parsedTSConfig = readTsConfigWithRemappedPaths( - tsconfigPath, - tmpTsConfigPath, - dependencies - ); - process.on('exit', () => cleanupTmpTsConfigFile(tmpTsConfigPath)); - writeJsonFile(tmpTsConfigPath, parsedTSConfig); - return join(tmpTsConfigPath); -} - -function cleanupTmpTsConfigFile(tmpTsConfigPath) { - try { - if (tmpTsConfigPath) { - unlinkSync(tmpTsConfigPath); - } - } catch (e) {} -} - -/** - * @deprecated This function will be removed from @nx/workspace in version 17. Prefer importing from @nx/js. - */ -export function checkDependentProjectsHaveBeenBuilt( - root: string, - projectName: string, - targetName: string, - projectDependencies: DependentBuildableProjectNode[] -): boolean { - const missing = findMissingBuildDependencies( - root, - projectName, - targetName, - projectDependencies - ); - if (missing.length > 0) { - console.error(stripIndents` - It looks like all of ${projectName}'s dependencies have not been built yet: - ${missing.map((x) => ` - ${x.node.name}`).join('\n')} - - You might be missing a "targetDefaults" configuration in your root nx.json (https://nx.dev/reference/project-configuration#target-defaults), - or "dependsOn" configured in ${projectName}'s project.json (https://nx.dev/reference/project-configuration#dependson) - `); - return false; - } else { - return true; - } -} - -/** - * @deprecated This function will be removed from @nx/workspace in version 17. Prefer importing from @nx/js. - */ -export function findMissingBuildDependencies( - root: string, - projectName: string, - targetName: string, - projectDependencies: DependentBuildableProjectNode[] -): DependentBuildableProjectNode[] { - const depLibsToBuildFirst: DependentBuildableProjectNode[] = []; - - // verify whether all dependent libraries have been built - projectDependencies.forEach((dep) => { - if (dep.node.type !== 'lib') { - return; - } - - const paths = dep.outputs.map((p) => join(root, p)); - - if (!paths.some(directoryExists)) { - depLibsToBuildFirst.push(dep); - } - }); - - return depLibsToBuildFirst; -} - -/** - * @deprecated This function will be removed from @nx/workspace in version 17. Prefer importing from @nx/js. - */ -export function updatePaths( - dependencies: DependentBuildableProjectNode[], - paths: Record -) { - const pathsKeys = Object.keys(paths); - // For each registered dependency - dependencies.forEach((dep) => { - // If there are outputs - if (dep.outputs && dep.outputs.length > 0) { - // Directly map the dependency name to the output paths (dist/packages/..., etc.) - paths[dep.name] = dep.outputs; - - // check for secondary entrypoints - // For each registered path - for (const path of pathsKeys) { - const nestedName = `${dep.name}/`; - - // If the path points to the current dependency and is nested (/) - if (path.startsWith(nestedName)) { - const nestedPart = path.slice(nestedName.length); - - // Bind secondary endpoints for ng-packagr projects - let mappedPaths = dep.outputs.map( - (output) => `${output}/${nestedPart}` - ); - - // Get the dependency's package name - const { root } = (dep.node?.data || {}) as any; - if (root) { - // Update nested mappings to point to the dependency's output paths - mappedPaths = mappedPaths.concat( - paths[path].flatMap((path) => - dep.outputs.map((output) => path.replace(root, output)) - ) - ); - } - - paths[path] = mappedPaths; - } - } - } - }); -} - -/** - * Updates the peerDependencies section in the `dist/lib/xyz/package.json` with - * the proper dependency and version - * @deprecated This function will be removed from @nx/workspace in version 17. Prefer importing from @nx/js. - */ -export function updateBuildableProjectPackageJsonDependencies( - root: string, - projectName: string, - targetName: string, - configurationName: string, - node: ProjectGraphProjectNode, - dependencies: DependentBuildableProjectNode[], - typeOfDependency: 'dependencies' | 'peerDependencies' = 'dependencies' -) { - const outputs = getOutputsForTargetAndConfiguration( - { - overrides: {}, - target: { - project: projectName, - target: targetName, - configuration: configurationName, - }, - }, - node - ); - - const packageJsonPath = `${outputs[0]}/package.json`; - let packageJson; - let workspacePackageJson; - try { - packageJson = readJsonFile(packageJsonPath); - workspacePackageJson = readJsonFile(`${root}/package.json`); - } catch (e) { - // cannot find or invalid package.json - return; - } - - packageJson.dependencies = packageJson.dependencies || {}; - packageJson.peerDependencies = packageJson.peerDependencies || {}; - - let updatePackageJson = false; - dependencies.forEach((entry) => { - const packageName = isNpmProject(entry.node) - ? entry.node.data.packageName - : entry.name; - - if ( - !hasDependency(packageJson, 'dependencies', packageName) && - !hasDependency(packageJson, 'devDependencies', packageName) && - !hasDependency(packageJson, 'peerDependencies', packageName) - ) { - try { - let depVersion; - if (entry.node.type === 'lib') { - const outputs = getOutputsForTargetAndConfiguration( - { - overrides: {}, - target: { - project: projectName, - target: targetName, - configuration: configurationName, - }, - }, - entry.node - ); - - const depPackageJsonPath = join(root, outputs[0], 'package.json'); - depVersion = readJsonFile(depPackageJsonPath).version; - - packageJson[typeOfDependency][packageName] = depVersion; - } else if (isNpmProject(entry.node)) { - // If an npm dep is part of the workspace devDependencies, do not include it the library - if ( - !!workspacePackageJson.devDependencies?.[ - entry.node.data.packageName - ] - ) { - return; - } - - depVersion = entry.node.data.version; - - packageJson[typeOfDependency][entry.node.data.packageName] = - depVersion; - } - updatePackageJson = true; - } catch (e) { - // skip if cannot find package.json - } - } - }); - - if (updatePackageJson) { - writeJsonFile(packageJsonPath, packageJson); - } -} - -// verify whether the package.json already specifies the dep -function hasDependency(outputJson, depConfigName: string, packageName: string) { - if (outputJson[depConfigName]) { - return outputJson[depConfigName][packageName]; - } else { - return false; - } -}