feat(core): finalize the input api
This commit is contained in:
parent
dc7301634a
commit
c52a8c2e9b
@ -204,9 +204,7 @@ jobs:
|
||||
mainmacos:
|
||||
executor: macos
|
||||
environment:
|
||||
NX_CLOUD_DISTRIBUTED_EXECUTION: 'true'
|
||||
NX_E2E_CI_CACHE_KEY: e2e-circleci-macos
|
||||
NX_CLOUD_DISTRIBUTED_EXECUTION_AGENT_COUNT: 2
|
||||
steps:
|
||||
- run:
|
||||
name: Set dynamic nx run variable
|
||||
@ -249,14 +247,6 @@ workflows:
|
||||
name: 'agent7'
|
||||
- agent:
|
||||
name: 'agent8'
|
||||
- agent:
|
||||
name: 'agent9'
|
||||
pm: 'npm'
|
||||
os: 'macos'
|
||||
- agent:
|
||||
name: 'agent10'
|
||||
pm: 'npm'
|
||||
os: 'macos'
|
||||
- main-linux
|
||||
- mainmacos:
|
||||
name: main-macos-e2e
|
||||
|
||||
@ -33,45 +33,6 @@ chunk {main} main.js, main.js.map (main) 4.17 KiB [entry] [rendered]
|
||||
|
||||
Based on the state of the source code and the environment, Nx was able to figure out that it had already run this exact command. Nx found the artifact in the local cache and replayed the output and restored the necessary files.
|
||||
|
||||
## --with-deps
|
||||
|
||||
Nx is smart, so it knows how applications and libraries in the workspace depend on each other.
|
||||
|
||||
**Run `nx lint todos --with-deps`, and you see that Nx lints both the `todos` app and the libraries it depends on.**
|
||||
|
||||
```bash
|
||||
> NX Running target lint for project todos and its 2 deps.
|
||||
|
||||
———————————————————————————————————————————————
|
||||
|
||||
> nx run data:lint
|
||||
|
||||
Linting "data"...
|
||||
|
||||
All files pass linting.
|
||||
|
||||
|
||||
> nx run auth:lint
|
||||
|
||||
Linting "auth"...
|
||||
|
||||
All files pass linting.
|
||||
|
||||
|
||||
> nx run todos:lint
|
||||
|
||||
Linting "todos"...
|
||||
|
||||
All files pass linting.
|
||||
|
||||
|
||||
———————————————————————————————————————————————
|
||||
|
||||
> NX SUCCESS Running target "lint" succeeded
|
||||
```
|
||||
|
||||
> Add --parallel to any command, and Nx does most of the work in parallel.
|
||||
|
||||
## What's Next
|
||||
|
||||
- Continue to [Step 7: Test affected projects](/node-tutorial/07-test-affected-projects)
|
||||
|
||||
@ -188,7 +188,7 @@ describe('Extra Nx Misc Tests', () => {
|
||||
expect(resultArgs).toContain('camel: d');
|
||||
}, 120000);
|
||||
|
||||
it('should fail when a process exits non-zero', () => {
|
||||
it('ttt should fail when a process exits non-zero', () => {
|
||||
updateProjectConfig(mylib, (config) => {
|
||||
config.targets.error = {
|
||||
executor: '@nrwl/workspace:run-commands',
|
||||
@ -203,7 +203,7 @@ describe('Extra Nx Misc Tests', () => {
|
||||
runCLI(`run ${mylib}:error`);
|
||||
fail('Should error if process errors');
|
||||
} catch (e) {
|
||||
expect(e.stderr.toString()).toContain(
|
||||
expect(e.stdout.toString()).toContain(
|
||||
'Something went wrong in run-commands - Command failed: exit 1'
|
||||
);
|
||||
}
|
||||
|
||||
@ -831,10 +831,10 @@ describe('Workspace Tests', () => {
|
||||
}
|
||||
|
||||
expect(error).toBeDefined();
|
||||
expect(error.stderr.toString()).toContain(
|
||||
expect(error.stdout.toString()).toContain(
|
||||
`${lib1} is still depended on by the following projects`
|
||||
);
|
||||
expect(error.stderr.toString()).toContain(lib2);
|
||||
expect(error.stdout.toString()).toContain(lib2);
|
||||
|
||||
/**
|
||||
* Try force removing the project
|
||||
|
||||
@ -339,44 +339,6 @@ describe('Nx Affected and Graph Tests', () => {
|
||||
});
|
||||
compareTwoArrays(resWithTarget.projects, [`${myapp}-e2e`, myapp]);
|
||||
|
||||
const resWithDeps = JSON.parse(
|
||||
(
|
||||
await runCLIAsync(
|
||||
`print-affected --files=apps/${myapp}/src/app/app.element.spec.ts --target=build --with-deps`,
|
||||
{ silent: true }
|
||||
)
|
||||
).stdout
|
||||
);
|
||||
|
||||
expect(resWithDeps.tasks[0]).toMatchObject({
|
||||
id: `${myapp}:build:production`,
|
||||
overrides: {},
|
||||
target: {
|
||||
project: myapp,
|
||||
target: 'build',
|
||||
},
|
||||
command: `${runNx} run ${myapp}:build:production`,
|
||||
outputs: [`dist/apps/${myapp}`],
|
||||
});
|
||||
|
||||
expect(resWithDeps.tasks[2]).toMatchObject({
|
||||
id: `${mypublishablelib}:build`,
|
||||
overrides: {},
|
||||
target: {
|
||||
project: mypublishablelib,
|
||||
target: 'build',
|
||||
},
|
||||
command: `${runNx} run ${mypublishablelib}:build`,
|
||||
outputs: [`dist/libs/${mypublishablelib}`],
|
||||
});
|
||||
|
||||
compareTwoArrays(resWithDeps.projects, [
|
||||
mylib,
|
||||
mypublishablelib,
|
||||
myapp,
|
||||
`${myapp}-e2e`,
|
||||
]);
|
||||
|
||||
const resWithTargetWithSelect1 = (
|
||||
await runCLIAsync(
|
||||
`print-affected --files=apps/${myapp}/src/app/app.element.spec.ts --target=test --select=projects`,
|
||||
|
||||
@ -172,7 +172,10 @@ describe('cache', () => {
|
||||
runCLI(`generate @nrwl/js:lib ${child1}`);
|
||||
runCLI(`generate @nrwl/js:lib ${child2}`);
|
||||
updateJson(`nx.json`, (c) => {
|
||||
c.namedInputs = { prod: ['!**/*.spec.ts'] };
|
||||
c.namedInputs = {
|
||||
default: ['{projectRoot}/**/*'],
|
||||
prod: ['!{projectRoot}/**/*.spec.ts'],
|
||||
};
|
||||
c.targetDefaults = {
|
||||
test: {
|
||||
inputs: ['default', '^prod'],
|
||||
@ -187,7 +190,7 @@ describe('cache', () => {
|
||||
});
|
||||
|
||||
updateJson(`libs/${child1}/project.json`, (c) => {
|
||||
c.namedInputs = { prod: ['**/*.ts'] };
|
||||
c.namedInputs = { prod: ['{projectRoot}/**/*.ts'] };
|
||||
return c;
|
||||
});
|
||||
|
||||
|
||||
@ -49,9 +49,6 @@ function getHttpServerArgs(options: Schema) {
|
||||
|
||||
function getBuildTargetCommand(options: Schema) {
|
||||
const cmd = ['nx', 'run', options.buildTarget];
|
||||
if (options.withDeps) {
|
||||
cmd.push(`--with-deps`);
|
||||
}
|
||||
if (options.parallel) {
|
||||
cmd.push(`--parallel`);
|
||||
}
|
||||
|
||||
@ -19,6 +19,6 @@ export function updateJestConfig(host: Tree, options: JestProjectSchema) {
|
||||
host,
|
||||
findRootJestConfig(host),
|
||||
'projects',
|
||||
`<rootDir>/${project.root}`
|
||||
`<rootDir>/$"14.4.0-beta.5"root}`
|
||||
);
|
||||
}
|
||||
|
||||
@ -24,8 +24,6 @@ export function assertDependentProjectsHaveBeenBuilt(
|
||||
|
||||
Please build these libraries first:
|
||||
${missing.map((x) => ` - ${x.node.name}`).join('\n')}
|
||||
|
||||
Try: ${chalk.bold(`nx build ${context.projectName} --with-deps`)}
|
||||
`)
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,7 +1,4 @@
|
||||
{
|
||||
"implicitDependencies": {
|
||||
"package.json": "*"
|
||||
},
|
||||
"tasksRunnerOptions": {
|
||||
"default": {
|
||||
"runner": "nx/tasks-runners/default",
|
||||
|
||||
@ -12,7 +12,6 @@ import {
|
||||
} from '../utils/command-line-utils';
|
||||
import { performance } from 'perf_hooks';
|
||||
import { createProjectGraphAsync } from '../project-graph/project-graph';
|
||||
import { withDeps } from '../project-graph/operators';
|
||||
import { ProjectGraph, ProjectGraphProjectNode } from '../config/project-graph';
|
||||
import { projectHasTarget } from '../utils/project-graph-utils';
|
||||
import { filterAffected } from '../project-graph/affected/affected-project-graph';
|
||||
@ -147,12 +146,6 @@ function projectsToRun(
|
||||
nxArgs
|
||||
)
|
||||
);
|
||||
if (!nxArgs.all && nxArgs.withDeps) {
|
||||
affectedGraph = withDeps(
|
||||
projectGraph,
|
||||
Object.values(affectedGraph.nodes) as ProjectGraphProjectNode[]
|
||||
);
|
||||
}
|
||||
|
||||
if (nxArgs.exclude) {
|
||||
const excludedProjects = new Set(nxArgs.exclude);
|
||||
|
||||
@ -8,7 +8,6 @@ jest.doMock('../utils/workspace-root', () => {
|
||||
});
|
||||
|
||||
jest.mock('fs', () => require('memfs').fs);
|
||||
require('fs').existsSync = () => true;
|
||||
jest.mock('../utils/typescript');
|
||||
|
||||
import { vol } from 'memfs';
|
||||
@ -28,22 +27,21 @@ describe('Hasher', () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
let hashes = {
|
||||
'/root/yarn.lock': 'yarn.lock.hash',
|
||||
'/root/nx.json': 'nx.json.hash',
|
||||
'/root/package-lock.json': 'package-lock.json.hash',
|
||||
'/root/package.json': 'package.json.hash',
|
||||
'/root/pnpm-lock.yaml': 'pnpm-lock.yaml.hash',
|
||||
'/root/tsconfig.base.json': tsConfigBaseJson,
|
||||
'/root/workspace.json': 'workspace.json.hash',
|
||||
'/root/global1': 'global1.hash',
|
||||
'/root/global2': 'global2.hash',
|
||||
};
|
||||
const allWorkspaceFiles = [
|
||||
{ file: 'yarn.lock', hash: 'yarn.lock.hash' },
|
||||
{ file: 'nx.json', hash: 'nx.json.hash' },
|
||||
{ file: 'package-lock.json', hash: 'package-lock.json.hash' },
|
||||
{ file: 'package.json', hash: 'package.json.hash' },
|
||||
{ file: 'pnpm-lock.yaml', hash: 'pnpm-lock.yaml.hash' },
|
||||
{ file: 'tsconfig.base.json', hash: tsConfigBaseJson },
|
||||
{ file: 'workspace.json', hash: 'workspace.json.hash' },
|
||||
{ file: 'global1', hash: 'global1.hash' },
|
||||
{ file: 'global2', hash: 'global2.hash' },
|
||||
];
|
||||
|
||||
function createHashing(): any {
|
||||
return {
|
||||
hashArray: (values: string[]) => values.join('|'),
|
||||
hashFile: (path: string) => hashes[path],
|
||||
};
|
||||
}
|
||||
|
||||
@ -100,6 +98,7 @@ describe('Hasher', () => {
|
||||
dependencies: {
|
||||
parent: [],
|
||||
},
|
||||
allWorkspaceFiles,
|
||||
},
|
||||
{} as any,
|
||||
{
|
||||
@ -172,6 +171,7 @@ describe('Hasher', () => {
|
||||
dependencies: {
|
||||
parent: [{ source: 'parent', target: 'child', type: 'static' }],
|
||||
},
|
||||
allWorkspaceFiles,
|
||||
},
|
||||
{} as any,
|
||||
{},
|
||||
@ -208,8 +208,8 @@ describe('Hasher', () => {
|
||||
},
|
||||
},
|
||||
files: [
|
||||
{ file: '/filea.ts', hash: 'a.hash' },
|
||||
{ file: '/filea.spec.ts', hash: 'a.spec.hash' },
|
||||
{ file: 'libs/parent/filea.ts', hash: 'a.hash' },
|
||||
{ file: 'libs/parent/filea.spec.ts', hash: 'a.spec.hash' },
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -223,8 +223,8 @@ describe('Hasher', () => {
|
||||
},
|
||||
targets: { build: {} },
|
||||
files: [
|
||||
{ file: '/fileb.ts', hash: 'b.hash' },
|
||||
{ file: '/fileb.spec.ts', hash: 'b.spec.hash' },
|
||||
{ file: 'libs/child/fileb.ts', hash: 'b.hash' },
|
||||
{ file: 'libs/child/fileb.spec.ts', hash: 'b.spec.hash' },
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -232,10 +232,11 @@ describe('Hasher', () => {
|
||||
dependencies: {
|
||||
parent: [{ source: 'parent', target: 'child', type: 'static' }],
|
||||
},
|
||||
allWorkspaceFiles,
|
||||
},
|
||||
{
|
||||
namedInputs: {
|
||||
prod: ['!**/*.spec.ts'],
|
||||
prod: ['!{projectRoot}/**/*.spec.ts'],
|
||||
},
|
||||
} as any,
|
||||
{},
|
||||
@ -250,13 +251,13 @@ describe('Hasher', () => {
|
||||
|
||||
expect(onlySourceNodes(hash.details.nodes)).toEqual({
|
||||
'child:$filesets':
|
||||
'/fileb.ts|/fileb.spec.ts|b.hash|b.spec.hash|{"root":"libs/child","namedInputs":{"prod":["default"]},"targets":{"build":{}}}|{"compilerOptions":{"paths":{"@nrwl/parent":["libs/parent/src/index.ts"],"@nrwl/child":["libs/child/src/index.ts"]}}}',
|
||||
'libs/child/fileb.ts|libs/child/fileb.spec.ts|b.hash|b.spec.hash|{"root":"libs/child","namedInputs":{"prod":["default"]},"targets":{"build":{}}}|{"compilerOptions":{"paths":{"@nrwl/parent":["libs/parent/src/index.ts"],"@nrwl/child":["libs/child/src/index.ts"]}}}',
|
||||
'parent:$filesets':
|
||||
'/filea.ts|a.hash|{"root":"libs/parent","targets":{"build":{"inputs":["prod","^prod"]}}}|{"compilerOptions":{"paths":{"@nrwl/parent":["libs/parent/src/index.ts"],"@nrwl/child":["libs/child/src/index.ts"]}}}',
|
||||
'libs/parent/filea.ts|a.hash|{"root":"libs/parent","targets":{"build":{"inputs":["prod","^prod"]}}}|{"compilerOptions":{"paths":{"@nrwl/parent":["libs/parent/src/index.ts"],"@nrwl/child":["libs/child/src/index.ts"]}}}',
|
||||
});
|
||||
});
|
||||
|
||||
it('should use targetDefaults from nx.json', async () => {
|
||||
it('should use targetdefaults from nx.json', async () => {
|
||||
const hasher = new Hasher(
|
||||
{
|
||||
nodes: {
|
||||
@ -269,8 +270,8 @@ describe('Hasher', () => {
|
||||
build: {},
|
||||
},
|
||||
files: [
|
||||
{ file: '/filea.ts', hash: 'a.hash' },
|
||||
{ file: '/filea.spec.ts', hash: 'a.spec.hash' },
|
||||
{ file: 'libs/parent/filea.ts', hash: 'a.hash' },
|
||||
{ file: 'libs/parent/filea.spec.ts', hash: 'a.spec.hash' },
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -281,8 +282,8 @@ describe('Hasher', () => {
|
||||
root: 'libs/child',
|
||||
targets: { build: {} },
|
||||
files: [
|
||||
{ file: '/fileb.ts', hash: 'b.hash' },
|
||||
{ file: '/fileb.spec.ts', hash: 'b.spec.hash' },
|
||||
{ file: 'libs/child/fileb.ts', hash: 'b.hash' },
|
||||
{ file: 'libs/child/fileb.spec.ts', hash: 'b.spec.hash' },
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -290,10 +291,11 @@ describe('Hasher', () => {
|
||||
dependencies: {
|
||||
parent: [{ source: 'parent', target: 'child', type: 'static' }],
|
||||
},
|
||||
allWorkspaceFiles,
|
||||
},
|
||||
{
|
||||
namedInputs: {
|
||||
prod: ['!**/*.spec.ts'],
|
||||
prod: ['!{projectRoot}/**/*.spec.ts'],
|
||||
},
|
||||
targetDefaults: {
|
||||
build: {
|
||||
@ -313,9 +315,9 @@ describe('Hasher', () => {
|
||||
|
||||
expect(onlySourceNodes(hash.details.nodes)).toEqual({
|
||||
'child:$filesets':
|
||||
'/fileb.ts|b.hash|{"root":"libs/child","targets":{"build":{}}}|{"compilerOptions":{"paths":{"@nrwl/parent":["libs/parent/src/index.ts"],"@nrwl/child":["libs/child/src/index.ts"]}}}',
|
||||
'libs/child/fileb.ts|b.hash|{"root":"libs/child","targets":{"build":{}}}|{"compilerOptions":{"paths":{"@nrwl/parent":["libs/parent/src/index.ts"],"@nrwl/child":["libs/child/src/index.ts"]}}}',
|
||||
'parent:$filesets':
|
||||
'/filea.ts|a.hash|{"root":"libs/parent","targets":{"build":{}}}|{"compilerOptions":{"paths":{"@nrwl/parent":["libs/parent/src/index.ts"],"@nrwl/child":["libs/child/src/index.ts"]}}}',
|
||||
'libs/parent/filea.ts|a.hash|{"root":"libs/parent","targets":{"build":{}}}|{"compilerOptions":{"paths":{"@nrwl/parent":["libs/parent/src/index.ts"],"@nrwl/child":["libs/child/src/index.ts"]}}}',
|
||||
});
|
||||
});
|
||||
|
||||
@ -336,6 +338,7 @@ describe('Hasher', () => {
|
||||
dependencies: {
|
||||
parent: [],
|
||||
},
|
||||
allWorkspaceFiles,
|
||||
},
|
||||
{ npmScope: 'nrwl' } as any,
|
||||
{
|
||||
@ -393,6 +396,7 @@ describe('Hasher', () => {
|
||||
parent: [{ source: 'parent', target: 'child', type: 'static' }],
|
||||
child: [{ source: 'child', target: 'parent', type: 'static' }],
|
||||
},
|
||||
allWorkspaceFiles,
|
||||
},
|
||||
{} as any,
|
||||
{},
|
||||
@ -455,6 +459,7 @@ describe('Hasher', () => {
|
||||
dependencies: {
|
||||
parent: [],
|
||||
},
|
||||
allWorkspaceFiles,
|
||||
},
|
||||
{} as any,
|
||||
{
|
||||
@ -494,16 +499,7 @@ describe('Hasher', () => {
|
||||
},
|
||||
},
|
||||
dependencies: {},
|
||||
allWorkspaceFiles: [
|
||||
{
|
||||
file: 'global1',
|
||||
hash: 'hash1',
|
||||
},
|
||||
{
|
||||
file: 'global2',
|
||||
hash: 'hash2',
|
||||
},
|
||||
],
|
||||
allWorkspaceFiles,
|
||||
},
|
||||
{
|
||||
implicitDependencies: {
|
||||
@ -554,6 +550,7 @@ describe('Hasher', () => {
|
||||
{ source: 'app', target: 'npm:react', type: DependencyType.static },
|
||||
],
|
||||
},
|
||||
allWorkspaceFiles,
|
||||
},
|
||||
{} as any,
|
||||
{},
|
||||
@ -599,6 +596,7 @@ describe('Hasher', () => {
|
||||
},
|
||||
],
|
||||
},
|
||||
allWorkspaceFiles,
|
||||
},
|
||||
{} as any,
|
||||
{},
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { exec } from 'child_process';
|
||||
import * as minimatch from 'minimatch';
|
||||
import { existsSync } from 'fs';
|
||||
import { getRootTsConfigFileName } from '../utils/typescript';
|
||||
import { defaultHashing, HashingImpl } from './hashing-impl';
|
||||
import {
|
||||
@ -14,8 +13,6 @@ import { Task } from '../config/task-graph';
|
||||
import { readJsonFile } from '../utils/fileutils';
|
||||
import { InputDefinition } from '../config/workspace-json-project-json';
|
||||
import { getImportPath } from '../utils/path';
|
||||
import { workspaceRoot } from '../utils/workspace-root';
|
||||
import { join } from 'path';
|
||||
|
||||
type ExpandedSelfInput =
|
||||
| { fileset: string }
|
||||
@ -183,7 +180,7 @@ export class Hasher {
|
||||
const DEFAULT_INPUTS = [
|
||||
{
|
||||
projects: 'self',
|
||||
fileset: 'default',
|
||||
fileset: '{projectRoot}/**/*',
|
||||
},
|
||||
{
|
||||
projects: 'dependencies',
|
||||
@ -283,13 +280,15 @@ class TaskHasher {
|
||||
task: Task,
|
||||
projectNode: ProjectGraphProjectNode<any>
|
||||
): { depsInputs: { input: string }[]; selfInputs: ExpandedSelfInput[] } {
|
||||
const namedInputs = {
|
||||
default: [{ fileset: '{projectRoot}/**/*' }],
|
||||
...this.nxJson.namedInputs,
|
||||
...projectNode.data.namedInputs,
|
||||
};
|
||||
if (task.target.target === '$input') {
|
||||
return {
|
||||
depsInputs: [{ input: task.target.configuration }],
|
||||
selfInputs: expandNamedInput(task.target.configuration, {
|
||||
...this.nxJson.namedInputs,
|
||||
...projectNode.data.namedInputs,
|
||||
}),
|
||||
selfInputs: expandNamedInput(task.target.configuration, namedInputs),
|
||||
};
|
||||
} else {
|
||||
const targetData = projectNode.data.targets[task.target.target];
|
||||
@ -299,7 +298,7 @@ class TaskHasher {
|
||||
// task from TaskGraph can be added here
|
||||
return splitInputsIntoSelfAndDependencies(
|
||||
targetData.inputs || targetDefaults?.inputs || DEFAULT_INPUTS,
|
||||
{ ...this.nxJson.namedInputs, ...projectNode.data.namedInputs }
|
||||
namedInputs
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -340,25 +339,42 @@ class TaskHasher {
|
||||
.filter((r) => !!r['fileset'])
|
||||
.map((r) => r['fileset']);
|
||||
|
||||
const projectFilesets = filesets.filter(
|
||||
(r) => !r.startsWith('{workspaceRoot}')
|
||||
);
|
||||
|
||||
const rootFilesets = filesets.filter((r) =>
|
||||
r.startsWith('{workspaceRoot}/')
|
||||
);
|
||||
const projectFilesets = [];
|
||||
const workspaceFilesets = [];
|
||||
let invalidFileset = null;
|
||||
|
||||
for (let f of filesets) {
|
||||
if (f.startsWith('{projectRoot}/') || f.startsWith('!{projectRoot}/')) {
|
||||
projectFilesets.push(f);
|
||||
} else if (
|
||||
f.startsWith('{workspaceRoot}/') ||
|
||||
f.startsWith('!{workspaceRoot}/')
|
||||
) {
|
||||
workspaceFilesets.push(f);
|
||||
} else {
|
||||
invalidFileset = f;
|
||||
}
|
||||
}
|
||||
if (invalidFileset) {
|
||||
throw new Error(
|
||||
[
|
||||
`"${invalidFileset}" is an invalid fileset.`,
|
||||
'All filesets have to start with either {workspaceRoot} or {projectRoot}.',
|
||||
'For instance: "!{projectRoot}/**/*.spec.ts" or "{workspaceRoot}/package.json".',
|
||||
`If "${invalidFileset}" is a named input, make sure it is defined in, for instance, nx.json.`,
|
||||
].join('\n')
|
||||
);
|
||||
}
|
||||
const notFilesets = inputs.filter((r) => !r['fileset']);
|
||||
return Promise.all([
|
||||
this.hashTaskFileset(task, projectFilesets),
|
||||
...[
|
||||
...rootFilesets,
|
||||
...workspaceFilesets,
|
||||
...this.legacyFilesetInputs.map((r) => r.fileset),
|
||||
].map((fileset) => this.hashRootFileset(fileset)),
|
||||
...[...inputs, ...this.legacyRuntimeInputs]
|
||||
.filter((r) => !r['fileset'])
|
||||
.map((r) =>
|
||||
r['runtime'] ? this.hashRuntime(r['runtime']) : this.hashEnv(r['env'])
|
||||
),
|
||||
...[...notFilesets, ...this.legacyRuntimeInputs].map((r) =>
|
||||
r['runtime'] ? this.hashRuntime(r['runtime']) : this.hashEnv(r['env'])
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -372,13 +388,14 @@ class TaskHasher {
|
||||
this.projectGraph.allWorkspaceFiles
|
||||
.filter((f) => minimatch(f.file, withoutWorkspaceRoot))
|
||||
.forEach((f) => {
|
||||
parts.push(this.hashing.hashFile(join(workspaceRoot, f.file)));
|
||||
parts.push(f.hash);
|
||||
});
|
||||
} else {
|
||||
if (existsSync(join(workspaceRoot, withoutWorkspaceRoot))) {
|
||||
parts.push(
|
||||
this.hashing.hashFile(join(workspaceRoot, withoutWorkspaceRoot))
|
||||
);
|
||||
const matchingFile = this.projectGraph.allWorkspaceFiles.find(
|
||||
(t) => t.file === withoutWorkspaceRoot
|
||||
);
|
||||
if (matchingFile) {
|
||||
parts.push(matchingFile.hash);
|
||||
}
|
||||
}
|
||||
const value = this.hashing.hashArray(parts);
|
||||
@ -399,7 +416,14 @@ class TaskHasher {
|
||||
if (!this.filesetHashes[mapKey]) {
|
||||
this.filesetHashes[mapKey] = new Promise(async (res) => {
|
||||
const p = this.projectGraph.nodes[task.target.project];
|
||||
const filteredFiles = this.filterFiles(p.data.files, filesetPatterns);
|
||||
const filesetWithExpandedProjectRoot = filesetPatterns.map((f) =>
|
||||
f.replace('{projectRoot}', p.data.root)
|
||||
);
|
||||
const filteredFiles = this.filterFiles(
|
||||
p.data.root,
|
||||
p.data.files,
|
||||
filesetWithExpandedProjectRoot
|
||||
);
|
||||
const fileNames = filteredFiles.map((f) => f.file);
|
||||
const values = filteredFiles.map((f) => f.hash);
|
||||
|
||||
@ -450,9 +474,12 @@ class TaskHasher {
|
||||
};
|
||||
}
|
||||
|
||||
private filterFiles(files: FileData[], patterns: string[]) {
|
||||
patterns = patterns.filter((p) => p !== 'default');
|
||||
if (patterns.length === 0) return files;
|
||||
private filterFiles(
|
||||
projectRoot: string,
|
||||
files: FileData[],
|
||||
patterns: string[]
|
||||
) {
|
||||
if (patterns.indexOf(`${projectRoot}/**/*`) > -1) return files;
|
||||
return files.filter(
|
||||
(f) => !!patterns.find((pattern) => minimatch(f.file, pattern))
|
||||
);
|
||||
@ -545,7 +572,6 @@ export function expandNamedInput(
|
||||
input: string,
|
||||
namedInputs: { [inputName: string]: (InputDefinition | string)[] }
|
||||
): ExpandedSelfInput[] {
|
||||
if (input === 'default') return [{ fileset: 'default' }];
|
||||
namedInputs ||= {};
|
||||
if (!namedInputs[input]) throw new Error(`Input '${input}' is not defined`);
|
||||
return expandSelfInputs(namedInputs[input], namedInputs);
|
||||
|
||||
@ -7,6 +7,7 @@ import { WholeFileChange } from '../../file-utils';
|
||||
import {
|
||||
getTouchedProjects,
|
||||
getImplicitlyTouchedProjects,
|
||||
extractGlobalFilesFromInputs,
|
||||
} from './workspace-projects';
|
||||
|
||||
function getFileChanges(files: string[]) {
|
||||
@ -147,6 +148,56 @@ describe('getImplicitlyTouchedProjects', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('extractGlobalFilesFromInputs', () => {
|
||||
it('should return list of global files from nx.json', () => {
|
||||
const globalFiles = extractGlobalFilesFromInputs(
|
||||
{
|
||||
namedInputs: {
|
||||
one: [
|
||||
'{workspaceRoot}/global1.txt',
|
||||
{ fileset: '{workspaceRoot}/global2.txt' },
|
||||
'{projectRoot}/local.txt',
|
||||
],
|
||||
},
|
||||
targetDefaults: {
|
||||
build: {
|
||||
inputs: ['{workspaceRoot}/global3.txt'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{}
|
||||
);
|
||||
expect(globalFiles).toEqual(['global1.txt', 'global2.txt', 'global3.txt']);
|
||||
});
|
||||
|
||||
it('should return list of global files from project configuration', () => {
|
||||
const globalFiles = extractGlobalFilesFromInputs(
|
||||
{},
|
||||
{
|
||||
one: {
|
||||
name: 'one',
|
||||
type: 'lib',
|
||||
data: {
|
||||
namedInputs: {
|
||||
one: [
|
||||
'{workspaceRoot}/global1.txt',
|
||||
{ fileset: '{workspaceRoot}/global2.txt' },
|
||||
'{projectRoot}/local.txt',
|
||||
],
|
||||
},
|
||||
targets: {
|
||||
build: {
|
||||
inputs: ['{workspaceRoot}/global3.txt'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
expect(globalFiles).toEqual(['global1.txt', 'global2.txt', 'global3.txt']);
|
||||
});
|
||||
});
|
||||
|
||||
function buildProjectGraphNodes(
|
||||
projects: Record<string, ProjectConfiguration>
|
||||
): ProjectGraph['nodes'] {
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import * as minimatch from 'minimatch';
|
||||
import { TouchedProjectLocator } from '../affected-project-graph-models';
|
||||
import { NxJsonConfiguration } from '../../../config/nx-json';
|
||||
import { ProjectGraphProjectNode } from '../../../config/project-graph';
|
||||
|
||||
export const getTouchedProjects: TouchedProjectLocator = (
|
||||
touchedFiles,
|
||||
@ -26,18 +28,22 @@ export const getTouchedProjects: TouchedProjectLocator = (
|
||||
|
||||
export const getImplicitlyTouchedProjects: TouchedProjectLocator = (
|
||||
fileChanges,
|
||||
workspaceJson,
|
||||
projectGraphNodes,
|
||||
nxJson
|
||||
): string[] => {
|
||||
if (!nxJson.implicitDependencies) {
|
||||
return [];
|
||||
}
|
||||
const implicits = { ...nxJson.implicitDependencies };
|
||||
const globalFiles = [
|
||||
...extractGlobalFilesFromInputs(nxJson, projectGraphNodes),
|
||||
'nx.json',
|
||||
'package.json',
|
||||
];
|
||||
globalFiles.forEach((file) => {
|
||||
implicits[file] = '*' as any;
|
||||
});
|
||||
|
||||
const touched = new Set<string>();
|
||||
|
||||
for (const [pattern, projects] of Object.entries(
|
||||
nxJson.implicitDependencies
|
||||
)) {
|
||||
for (const [pattern, projects] of Object.entries(implicits)) {
|
||||
const implicitDependencyWasChanged = fileChanges.some((f) =>
|
||||
minimatch(f.file, pattern)
|
||||
);
|
||||
@ -53,3 +59,49 @@ export const getImplicitlyTouchedProjects: TouchedProjectLocator = (
|
||||
|
||||
return Array.from(touched);
|
||||
};
|
||||
|
||||
export function extractGlobalFilesFromInputs(
|
||||
nxJson: NxJsonConfiguration,
|
||||
projectGraphNodes: Record<string, ProjectGraphProjectNode>
|
||||
) {
|
||||
const globalFiles = [];
|
||||
globalFiles.push(...extractGlobalFilesFromNamedInputs(nxJson.namedInputs));
|
||||
globalFiles.push(...extractGlobalFilesFromTargets(nxJson.targetDefaults));
|
||||
Object.values(projectGraphNodes || {}).forEach((node) => {
|
||||
globalFiles.push(
|
||||
...extractGlobalFilesFromNamedInputs(node.data.namedInputs)
|
||||
);
|
||||
globalFiles.push(...extractGlobalFilesFromTargets(node.data.targets));
|
||||
});
|
||||
return globalFiles;
|
||||
}
|
||||
|
||||
function extractGlobalFilesFromNamedInputs(namedInputs: any) {
|
||||
const globalFiles = [];
|
||||
for (const inputs of Object.values(namedInputs || {})) {
|
||||
globalFiles.push(...extractGlobalFiles(inputs));
|
||||
}
|
||||
return globalFiles;
|
||||
}
|
||||
|
||||
function extractGlobalFilesFromTargets(targets: any) {
|
||||
const globalFiles = [];
|
||||
for (const target of Object.values(targets || {})) {
|
||||
if ((target as any).inputs) {
|
||||
globalFiles.push(...extractGlobalFiles((target as any).inputs));
|
||||
}
|
||||
}
|
||||
return globalFiles;
|
||||
}
|
||||
|
||||
function extractGlobalFiles(inputs: any) {
|
||||
const globalFiles = [];
|
||||
for (const input of inputs) {
|
||||
if (typeof input === 'string' && input.startsWith('{workspaceRoot}/')) {
|
||||
globalFiles.push(input.substring('{workspaceRoot}/'.length));
|
||||
} else if (input.fileset && input.fileset.startsWith('{workspaceRoot}/')) {
|
||||
globalFiles.push(input.fileset.substring('{workspaceRoot}/'.length));
|
||||
}
|
||||
}
|
||||
return globalFiles;
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { reverse, withDeps, filterNodes } from './operators';
|
||||
import { reverse, filterNodes } from './operators';
|
||||
import {
|
||||
DependencyType,
|
||||
ProjectGraph,
|
||||
@ -163,144 +163,6 @@ describe('reverse', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('withDeps', () => {
|
||||
it('should return a new graph with all dependencies included from original', () => {
|
||||
const affectedNodes: ProjectGraphProjectNode[] = [
|
||||
{ name: 'app1-e2e', type: 'app', data: null },
|
||||
{ name: 'app1', type: 'app', data: null },
|
||||
{ name: 'lib1', type: 'lib', data: null },
|
||||
];
|
||||
|
||||
const result = withDeps(graph, affectedNodes);
|
||||
expect(result).toEqual({
|
||||
nodes: {
|
||||
lib3: {
|
||||
name: 'lib3',
|
||||
type: 'lib',
|
||||
data: null,
|
||||
},
|
||||
lib2: {
|
||||
name: 'lib2',
|
||||
type: 'lib',
|
||||
data: null,
|
||||
},
|
||||
lib1: {
|
||||
name: 'lib1',
|
||||
type: 'lib',
|
||||
data: null,
|
||||
},
|
||||
app1: {
|
||||
name: 'app1',
|
||||
type: 'app',
|
||||
data: null,
|
||||
},
|
||||
'app1-e2e': {
|
||||
name: 'app1-e2e',
|
||||
type: 'app',
|
||||
data: null,
|
||||
},
|
||||
},
|
||||
dependencies: {
|
||||
lib2: [
|
||||
{
|
||||
type: 'static',
|
||||
source: 'lib2',
|
||||
target: 'lib3',
|
||||
},
|
||||
],
|
||||
lib1: [
|
||||
{
|
||||
type: 'static',
|
||||
source: 'lib1',
|
||||
target: 'lib2',
|
||||
},
|
||||
{
|
||||
type: 'static',
|
||||
source: 'lib1',
|
||||
target: 'lib3',
|
||||
},
|
||||
],
|
||||
app1: [
|
||||
{
|
||||
type: 'static',
|
||||
source: 'app1',
|
||||
target: 'lib1',
|
||||
},
|
||||
],
|
||||
'app1-e2e': [
|
||||
{
|
||||
type: 'implicit',
|
||||
source: 'app1-e2e',
|
||||
target: 'app1',
|
||||
},
|
||||
],
|
||||
lib3: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle circular deps', () => {
|
||||
const graph: ProjectGraph = {
|
||||
nodes: {
|
||||
lib1: { name: 'lib1', type: 'lib', data: null },
|
||||
lib2: { name: 'lib2', type: 'lib', data: null },
|
||||
},
|
||||
dependencies: {
|
||||
lib1: [
|
||||
{
|
||||
type: DependencyType.static,
|
||||
source: 'lib1',
|
||||
target: 'lib2',
|
||||
},
|
||||
],
|
||||
lib2: [
|
||||
{
|
||||
type: DependencyType.static,
|
||||
source: 'lib2',
|
||||
target: 'lib1',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const affectedNodes: ProjectGraphProjectNode[] = [
|
||||
{ name: 'lib1', type: 'lib', data: null },
|
||||
];
|
||||
|
||||
const result = withDeps(graph, affectedNodes);
|
||||
expect(result).toEqual({
|
||||
nodes: {
|
||||
lib1: {
|
||||
name: 'lib1',
|
||||
type: 'lib',
|
||||
data: null,
|
||||
},
|
||||
lib2: {
|
||||
name: 'lib2',
|
||||
type: 'lib',
|
||||
data: null,
|
||||
},
|
||||
},
|
||||
dependencies: {
|
||||
lib2: [
|
||||
{
|
||||
type: 'static',
|
||||
source: 'lib2',
|
||||
target: 'lib1',
|
||||
},
|
||||
],
|
||||
lib1: [
|
||||
{
|
||||
type: 'static',
|
||||
source: 'lib1',
|
||||
target: 'lib2',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('filterNodes', () => {
|
||||
it('filters out nodes based on predicate', () => {
|
||||
const result = filterNodes((n) => n.type === 'app')(graph);
|
||||
|
||||
@ -219,7 +219,12 @@ function interpolateOverrides<T = any>(
|
||||
Object.entries(interpolatedArgs).forEach(([name, value]) => {
|
||||
interpolatedArgs[name] =
|
||||
typeof value === 'string'
|
||||
? interpolate(value, { project: { ...project, name: projectName } })
|
||||
? interpolate(value, {
|
||||
workspaceRoot: '',
|
||||
projectRoot: project.root,
|
||||
projectName: project.name,
|
||||
project: { ...project, name: projectName }, // this is legacy
|
||||
})
|
||||
: value;
|
||||
});
|
||||
return interpolatedArgs;
|
||||
|
||||
@ -55,10 +55,6 @@ export const defaultTasksRunner: TasksRunner<
|
||||
options.lifeCycle.startCommand();
|
||||
try {
|
||||
return await runAllTasks(tasks, options, context);
|
||||
} catch (e) {
|
||||
console.error('Unexpected error:');
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
} finally {
|
||||
options.lifeCycle.endCommand();
|
||||
}
|
||||
|
||||
@ -22,7 +22,8 @@ import {
|
||||
import { Task } from '../config/task-graph';
|
||||
import { createTaskGraph } from './create-task-graph';
|
||||
import { findCycle, makeAcyclic } from './task-graph-utils';
|
||||
import { TargetDependencyConfig } from 'nx/src/config/workspace-json-project-json';
|
||||
import { TargetDependencyConfig } from '../config/workspace-json-project-json';
|
||||
import { handleErrors } from '../utils/params';
|
||||
|
||||
async function getTerminalOutputLifeCycle(
|
||||
initiatingProject: string,
|
||||
@ -90,81 +91,80 @@ export async function runCommand(
|
||||
initiatingProject: string | null,
|
||||
extraTargetDependencies: Record<string, (TargetDependencyConfig | string)[]>
|
||||
) {
|
||||
const { tasksRunner, runnerOptions } = getRunner(nxArgs, nxJson);
|
||||
const status = await handleErrors(overrides['verbose'] === true, async () => {
|
||||
const { tasksRunner, runnerOptions } = getRunner(nxArgs, nxJson);
|
||||
|
||||
const defaultDependencyConfigs = mergeTargetDependencies(
|
||||
nxJson.targetDefaults,
|
||||
extraTargetDependencies
|
||||
);
|
||||
const projectNames = projectsToRun.map((t) => t.name);
|
||||
const taskGraph = createTaskGraph(
|
||||
projectGraph,
|
||||
defaultDependencyConfigs,
|
||||
projectNames,
|
||||
[nxArgs.target],
|
||||
nxArgs.configuration,
|
||||
overrides
|
||||
);
|
||||
|
||||
const cycle = findCycle(taskGraph);
|
||||
if (cycle) {
|
||||
if (nxArgs.nxIgnoreCycles) {
|
||||
output.warn({
|
||||
title: `The task graph has a circular dependency`,
|
||||
bodyLines: [`${cycle.join(' --> ')}`],
|
||||
});
|
||||
makeAcyclic(taskGraph);
|
||||
} else {
|
||||
output.error({
|
||||
title: `Could not execute command because the task graph has a circular dependency`,
|
||||
bodyLines: [`${cycle.join(' --> ')}`],
|
||||
});
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
const tasks = Object.values(taskGraph.tasks);
|
||||
if (nxArgs.outputStyle == 'stream') {
|
||||
process.env.NX_STREAM_OUTPUT = 'true';
|
||||
process.env.NX_PREFIX_OUTPUT = 'true';
|
||||
}
|
||||
if (nxArgs.outputStyle == 'stream-without-prefixes') {
|
||||
process.env.NX_STREAM_OUTPUT = 'true';
|
||||
}
|
||||
const { lifeCycle, renderIsDone } = await getTerminalOutputLifeCycle(
|
||||
initiatingProject,
|
||||
projectNames,
|
||||
tasks,
|
||||
nxArgs,
|
||||
overrides,
|
||||
runnerOptions
|
||||
);
|
||||
const lifeCycles = [lifeCycle] as LifeCycle[];
|
||||
|
||||
if (process.env.NX_PERF_LOGGING) {
|
||||
lifeCycles.push(new TaskTimingsLifeCycle());
|
||||
}
|
||||
|
||||
if (process.env.NX_PROFILE) {
|
||||
lifeCycles.push(new TaskProfilingLifeCycle(process.env.NX_PROFILE));
|
||||
}
|
||||
|
||||
const promiseOrObservable = tasksRunner(
|
||||
tasks,
|
||||
{ ...runnerOptions, lifeCycle: new CompositeLifeCycle(lifeCycles) },
|
||||
{
|
||||
initiatingProject:
|
||||
nxArgs.outputStyle === 'compact' ? null : initiatingProject,
|
||||
target: nxArgs.target,
|
||||
const defaultDependencyConfigs = mergeTargetDependencies(
|
||||
nxJson.targetDefaults,
|
||||
extraTargetDependencies
|
||||
);
|
||||
const projectNames = projectsToRun.map((t) => t.name);
|
||||
const taskGraph = createTaskGraph(
|
||||
projectGraph,
|
||||
nxJson,
|
||||
nxArgs,
|
||||
taskGraph,
|
||||
}
|
||||
);
|
||||
defaultDependencyConfigs,
|
||||
projectNames,
|
||||
[nxArgs.target],
|
||||
nxArgs.configuration,
|
||||
overrides
|
||||
);
|
||||
|
||||
let anyFailures;
|
||||
try {
|
||||
const cycle = findCycle(taskGraph);
|
||||
if (cycle) {
|
||||
if (nxArgs.nxIgnoreCycles) {
|
||||
output.warn({
|
||||
title: `The task graph has a circular dependency`,
|
||||
bodyLines: [`${cycle.join(' --> ')}`],
|
||||
});
|
||||
makeAcyclic(taskGraph);
|
||||
} else {
|
||||
output.error({
|
||||
title: `Could not execute command because the task graph has a circular dependency`,
|
||||
bodyLines: [`${cycle.join(' --> ')}`],
|
||||
});
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
const tasks = Object.values(taskGraph.tasks);
|
||||
if (nxArgs.outputStyle == 'stream') {
|
||||
process.env.NX_STREAM_OUTPUT = 'true';
|
||||
process.env.NX_PREFIX_OUTPUT = 'true';
|
||||
}
|
||||
if (nxArgs.outputStyle == 'stream-without-prefixes') {
|
||||
process.env.NX_STREAM_OUTPUT = 'true';
|
||||
}
|
||||
const { lifeCycle, renderIsDone } = await getTerminalOutputLifeCycle(
|
||||
initiatingProject,
|
||||
projectNames,
|
||||
tasks,
|
||||
nxArgs,
|
||||
overrides,
|
||||
runnerOptions
|
||||
);
|
||||
const lifeCycles = [lifeCycle] as LifeCycle[];
|
||||
|
||||
if (process.env.NX_PERF_LOGGING) {
|
||||
lifeCycles.push(new TaskTimingsLifeCycle());
|
||||
}
|
||||
|
||||
if (process.env.NX_PROFILE) {
|
||||
lifeCycles.push(new TaskProfilingLifeCycle(process.env.NX_PROFILE));
|
||||
}
|
||||
|
||||
const promiseOrObservable = tasksRunner(
|
||||
tasks,
|
||||
{ ...runnerOptions, lifeCycle: new CompositeLifeCycle(lifeCycles) },
|
||||
{
|
||||
initiatingProject:
|
||||
nxArgs.outputStyle === 'compact' ? null : initiatingProject,
|
||||
target: nxArgs.target,
|
||||
projectGraph,
|
||||
nxJson,
|
||||
nxArgs,
|
||||
taskGraph,
|
||||
}
|
||||
);
|
||||
let anyFailures;
|
||||
if ((promiseOrObservable as any).subscribe) {
|
||||
anyFailures = await anyFailuresInObservable(promiseOrObservable);
|
||||
} else {
|
||||
@ -172,18 +172,11 @@ export async function runCommand(
|
||||
anyFailures = await anyFailuresInPromise(promiseOrObservable as any);
|
||||
}
|
||||
await renderIsDone;
|
||||
} catch (e) {
|
||||
output.error({
|
||||
title: 'Unhandled error in task executor',
|
||||
});
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
return anyFailures ? 1 : 0;
|
||||
});
|
||||
// fix for https://github.com/nrwl/nx/issues/1666
|
||||
if (process.stdin['unref']) (process.stdin as any).unref();
|
||||
|
||||
process.exit(anyFailures ? 1 : 0);
|
||||
process.exit(status);
|
||||
}
|
||||
|
||||
function mergeTargetDependencies(
|
||||
@ -261,10 +254,7 @@ export function getRunner(
|
||||
let runner = nxArgs.runner;
|
||||
runner = runner || 'default';
|
||||
if (!nxJson.tasksRunnerOptions) {
|
||||
output.error({
|
||||
title: `Could not find any runner configurations in nx.json`,
|
||||
});
|
||||
process.exit(1);
|
||||
throw new Error(`Could not find any runner configurations in nx.json`);
|
||||
}
|
||||
if (nxJson.tasksRunnerOptions[runner]) {
|
||||
let modulePath: string = nxJson.tasksRunnerOptions[runner].runner;
|
||||
@ -292,9 +282,6 @@ export function getRunner(
|
||||
},
|
||||
};
|
||||
} else {
|
||||
output.error({
|
||||
title: `Could not find runner configuration for ${runner}`,
|
||||
});
|
||||
process.exit(1);
|
||||
throw new Error(`Could not find runner configuration for ${runner}`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ describe('utils', () => {
|
||||
getOutputsForTargetAndConfiguration(
|
||||
task,
|
||||
getNode({
|
||||
outputs: ['{project.root}/sub', 'two'],
|
||||
outputs: ['{projectRoot}/sub', 'two'],
|
||||
options: {
|
||||
myVar: 'one',
|
||||
},
|
||||
|
||||
@ -97,8 +97,11 @@ export function getOutputsForTargetAndConfiguration(
|
||||
return targets.outputs
|
||||
.map((output: string) => {
|
||||
const interpolated = interpolate(output, {
|
||||
workspaceRoot: '', // this is to make sure interpolation works
|
||||
projectRoot: node.data.root,
|
||||
projectName: node.data.name,
|
||||
project: { ...node.data, name: node.data.name }, // this is legacy
|
||||
options,
|
||||
project: { ...node.data, name: node.name },
|
||||
});
|
||||
return isRelativePath(interpolated)
|
||||
? joinPathFragments(node.data.root, interpolated)
|
||||
|
||||
@ -76,7 +76,6 @@ const runOne: string[] = [
|
||||
'exclude',
|
||||
'onlyFailed',
|
||||
'help',
|
||||
'withDeps',
|
||||
'skipNxCache',
|
||||
'scan',
|
||||
'outputStyle',
|
||||
@ -120,7 +119,6 @@ export interface NxArgs {
|
||||
help?: boolean;
|
||||
version?: boolean;
|
||||
plain?: boolean;
|
||||
withDeps?: boolean;
|
||||
projects?: string[];
|
||||
select?: string;
|
||||
skipNxCache?: boolean;
|
||||
|
||||
@ -4,7 +4,7 @@ import {
|
||||
TargetConfiguration,
|
||||
ProjectsConfigurations,
|
||||
} from '../config/workspace-json-project-json';
|
||||
import { execSync } from 'child_process';
|
||||
import { output } from './output';
|
||||
|
||||
type PropertyDescription = {
|
||||
type?: string | string[];
|
||||
@ -77,16 +77,21 @@ export async function handleErrors(isVerbose: boolean, fn: Function) {
|
||||
return await fn();
|
||||
} catch (err) {
|
||||
err ??= new Error('Unknown error caught');
|
||||
|
||||
if (err.constructor.name === 'UnsuccessfulWorkflowExecution') {
|
||||
logger.error('The generator workflow failed. See above.');
|
||||
} else if (err.message) {
|
||||
logger.error(err.message);
|
||||
} else {
|
||||
logger.error(err);
|
||||
}
|
||||
if (isVerbose && err.stack) {
|
||||
logger.info(err.stack);
|
||||
const lines = (err.message ? err.message : err.toString()).split('\n');
|
||||
const bodyLines = lines.slice(1);
|
||||
if (err.stack && !isVerbose) {
|
||||
bodyLines.push('Pass --verbose to see the stacktrace.');
|
||||
}
|
||||
output.error({
|
||||
title: lines[0],
|
||||
bodyLines,
|
||||
});
|
||||
if (err.stack && isVerbose) {
|
||||
logger.info(err.stack);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -47,9 +47,6 @@ function getHttpServerArgs(options: Schema) {
|
||||
|
||||
function getBuildTargetCommand(options: Schema) {
|
||||
const cmd = ['nx', 'run', options.buildTarget];
|
||||
if (options.withDeps) {
|
||||
cmd.push(`--with-deps`);
|
||||
}
|
||||
if (options.parallel) {
|
||||
cmd.push(`--parallel`);
|
||||
}
|
||||
|
||||
@ -212,20 +212,13 @@ export function checkDependentProjectsHaveBeenBuilt(
|
||||
targetName,
|
||||
projectDependencies
|
||||
);
|
||||
if (missing.length === projectDependencies.length && missing.length > 0) {
|
||||
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/configuration/packagejson#target-defaults),
|
||||
or "dependsOn" configured in ${projectName}'s angular.json/workspace.json record or project.json (https://nx.dev/configuration/packagejson#dependson)
|
||||
`);
|
||||
} else if (missing.length > 0) {
|
||||
console.error(stripIndents`
|
||||
Some of the project ${projectName}'s dependencies have not been built yet. Please build these libraries before:
|
||||
${missing.map((x) => ` - ${x.node.name}`).join('\n')}
|
||||
|
||||
Try: nx run ${projectName}:${targetName} --with-deps
|
||||
You might be missing a "targetDefaults" configuration in your root nx.json (https://nx.dev/configuration/projectjson#target-defaults),
|
||||
or "dependsOn" configured in ${projectName}'s project.json (https://nx.dev/configuration/projectjson#dependson)
|
||||
`);
|
||||
return false;
|
||||
} else {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user