feat(core): add the outputs of a task onto the task (#19561)
Co-authored-by: Jonathan Cammisuli <jon@cammisuli.ca>
This commit is contained in:
parent
6f245be10c
commit
5740a73cd8
@ -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`
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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}`,
|
||||
},
|
||||
|
||||
@ -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 => {
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -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
|
||||
);
|
||||
|
||||
|
||||
@ -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
|
||||
);
|
||||
|
||||
|
||||
@ -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,
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
*/
|
||||
|
||||
@ -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",
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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 } =
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -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,
|
||||
};
|
||||
|
||||
@ -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 = {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@ function createMockTask(id: string): Task {
|
||||
project,
|
||||
target,
|
||||
},
|
||||
outputs: [],
|
||||
overrides: {},
|
||||
};
|
||||
}
|
||||
|
||||
@ -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']);
|
||||
|
||||
@ -81,9 +81,14 @@ export function expandDependencyConfigSyntaxSugar(
|
||||
|
||||
export function getOutputs(
|
||||
p: Record<string, ProjectGraphProjectNode>,
|
||||
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, 'target' | 'overrides'>,
|
||||
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) => {
|
||||
|
||||
@ -13,7 +13,8 @@ describe('<%=propertyName%>Hasher', () => {
|
||||
project: 'proj',
|
||||
target: 'target'
|
||||
},
|
||||
overrides: {}
|
||||
overrides: {},
|
||||
outputs: []
|
||||
}, {
|
||||
hasher: mockHasher
|
||||
} as unknown as HasherContext)
|
||||
|
||||
@ -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<string, string[]> = {
|
||||
'@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<string, string[]> = {
|
||||
'@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();
|
||||
});
|
||||
});
|
||||
@ -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<string, string[]>
|
||||
) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user