Skip to content

Commit

Permalink
feat(core): add commandExternalDependencies hash input (#16916)
Browse files Browse the repository at this point in the history
Co-authored-by: FrozenPandaz <[email protected]>
  • Loading branch information
meeroslav and FrozenPandaz authored May 15, 2023
1 parent 6ebfbbe commit f5ae995
Show file tree
Hide file tree
Showing 6 changed files with 271 additions and 20 deletions.
44 changes: 44 additions & 0 deletions docs/shared/reference/project-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,50 @@ Examples:

Note the result value is hashed, so it is never displayed.

_External Dependencies_

For official plugins, Nx intelligently finds a set of external dependencies which it hashes for the target. `nx:run-commands` is an exception to this.
Because you may specify any command to be run, it is not possible to determine which, if any, external dependencies are used by the target.
To be safe, Nx assumes that updating any external dependency invalidates the cache for the target.

> Note: Community plugins are also treated like `nx:run-commands`
This input type allows for you to override this cautious behavior by specifying a set of external dependencies to hash for the target.

Examples:

Targets that only use commands natively available in the terminal will not depend on any external dependencies. Specify an empty array to not hash any external dependencies.

```json
{
"copyFiles": {
"inputs": [
{
"externalDependencies": []
}
],
"executor": "nx:run-commands",
"command": "cp src/assets dist"
}
}
```

If a target uses a command from a npm package, that package should be listed.

```json
{
"copyFiles": {
"inputs": [
{
"externalDependencies": ["lerna"]
}
],
"executor": "nx:run-commands",
"command": "npx lerna publish"
}
}
```

_Named Inputs_

Examples:
Expand Down
10 changes: 10 additions & 0 deletions packages/nx/schemas/nx-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,16 @@
}
},
"additionalProperties": false
},
{
"type": "object",
"properties": {
"externalDependencies": {
"type": "string",
"description": "The list of external dependencies that our target depends on for `nx:run-commands` and community plugins."
}
},
"additionalProperties": false
}
]
}
Expand Down
10 changes: 10 additions & 0 deletions packages/nx/schemas/project-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,16 @@
}
},
"additionalProperties": false
},
{
"type": "object",
"properties": {
"externalDependencies": {
"type": "string",
"description": "The list of external dependencies that our target depends on for `nx:run-commands` and community plugins."
}
},
"additionalProperties": false
}
]
}
Expand Down
1 change: 1 addition & 0 deletions packages/nx/src/config/workspace-json-project-json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export type InputDefinition =
| { input: string }
| { fileset: string }
| { runtime: string }
| { externalDependencies: string[] }
| { env: string };

/**
Expand Down
154 changes: 144 additions & 10 deletions packages/nx/src/hasher/hasher.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ describe('Hasher', () => {
root: 'libs/parent',
targets: {
build: {
executor: 'unknown',
executor: 'nx:run-commands',
inputs: [
'default',
'^default',
Expand Down Expand Up @@ -136,8 +136,8 @@ describe('Hasher', () => {
expect(hash.details.command).toEqual('parent|build||{"prop":"prop-value"}');
expect(hash.details.nodes).toEqual({
'parent:{projectRoot}/**/*':
'/file|file.hash|{"root":"libs/parent","targets":{"build":{"executor":"unknown","inputs":["default","^default",{"runtime":"echo runtime123"},{"env":"TESTENV"},{"env":"NONEXISTENTENV"},{"input":"default","projects":["unrelated"]}]}}}|{"compilerOptions":{"paths":{"@nx/parent":["libs/parent/src/index.ts"],"@nx/child":["libs/child/src/index.ts"]}}}',
parent: 'unknown',
'/file|file.hash|{"root":"libs/parent","targets":{"build":{"executor":"nx:run-commands","inputs":["default","^default",{"runtime":"echo runtime123"},{"env":"TESTENV"},{"env":"NONEXISTENTENV"},{"input":"default","projects":["unrelated"]}]}}}|{"compilerOptions":{"paths":{"@nx/parent":["libs/parent/src/index.ts"],"@nx/child":["libs/child/src/index.ts"]}}}',
parent: 'nx:run-commands',
'unrelated:{projectRoot}/**/*':
'libs/unrelated/filec.ts|filec.hash|{"root":"libs/unrelated","targets":{"build":{}}}|{"compilerOptions":{"paths":{"@nx/parent":["libs/parent/src/index.ts"],"@nx/child":["libs/child/src/index.ts"]}}}',
'{workspaceRoot}/nx.json': 'nx.json.hash',
Expand All @@ -159,7 +159,7 @@ describe('Hasher', () => {
type: 'lib',
data: {
root: 'libs/parent',
targets: { build: { executor: 'unknown' } },
targets: { build: { executor: 'nx:run-commands' } },
files: [
{ file: '/filea.ts', hash: 'a.hash' },
{ file: '/filea.spec.ts', hash: 'a.spec.hash' },
Expand Down Expand Up @@ -219,7 +219,7 @@ describe('Hasher', () => {
targets: {
build: {
inputs: ['prod', '^prod'],
executor: 'unknown',
executor: 'nx:run-commands',
},
},
files: [
Expand All @@ -236,7 +236,7 @@ describe('Hasher', () => {
namedInputs: {
prod: ['default'],
},
targets: { build: { executor: 'unknown' } },
targets: { build: { executor: 'nx:run-commands' } },
files: [
{ file: 'libs/child/fileb.ts', hash: 'b.hash' },
{ file: 'libs/child/fileb.spec.ts', hash: 'b.spec.hash' },
Expand Down Expand Up @@ -288,12 +288,12 @@ describe('Hasher', () => {
targets: {
build: {
inputs: ['prod'],
executor: 'unknown',
executor: 'nx:run-commands',
},
test: {
inputs: ['default'],
dependsOn: ['build'],
executor: 'unknown',
executor: 'nx:run-commands',
},
},
files: [
Expand Down Expand Up @@ -356,7 +356,7 @@ describe('Hasher', () => {
targets: {
test: {
inputs: ['default', '^prod'],
executor: 'unknown',
executor: 'nx:run-commands',
},
},
files: [
Expand All @@ -380,7 +380,7 @@ describe('Hasher', () => {
targets: {
test: {
inputs: ['default'],
executor: 'unknown',
executor: 'nx:run-commands',
},
},
files: [
Expand Down Expand Up @@ -965,6 +965,140 @@ describe('Hasher', () => {
expect(hash.details.nodes['npm:nx']).not.toBeDefined();
expect(hash.details.nodes['app']).toEqual('nx:run-commands');
});

it('should use externalDependencies to override nx:run-commands', async () => {
const hasher = new Hasher(
{
nodes: {
app: {
name: 'app',
type: 'app',
data: {
root: 'apps/app',
targets: {
build: {
executor: 'nx:run-commands',
inputs: [
{ fileset: '{projectRoot}/**/*' },
{ externalDependencies: ['webpack', 'react'] },
],
},
},
files: [{ file: '/filea.ts', hash: 'a.hash' }],
},
},
},
externalNodes: {
'npm:nx': {
name: 'npm:nx',
type: 'npm',
data: {
packageName: 'nx',
version: '16.0.0',
},
},
'npm:webpack': {
name: 'npm:webpack',
type: 'npm',
data: {
packageName: 'webpack',
version: '5.0.0',
},
},
'npm:react': {
name: 'npm:react',
type: 'npm',
data: {
packageName: 'react',
version: '17.0.0',
},
},
},
dependencies: {},
allWorkspaceFiles,
},
{} as any,
{},
createHashing()
);

const hash = await hasher.hashTask({
target: { project: 'app', target: 'build' },
id: 'app-build',
overrides: { prop: 'prop-value' },
});

expect(hash.details.nodes['npm:nx']).not.toBeDefined();
expect(hash.details.nodes['app']).not.toBeDefined();
expect(hash.details.nodes['npm:webpack']).toEqual('5.0.0');
expect(hash.details.nodes['npm:react']).toEqual('17.0.0');
});

it('should use externalDependencies with empty array to ignore all deps', async () => {
const hasher = new Hasher(
{
nodes: {
app: {
name: 'app',
type: 'app',
data: {
root: 'apps/app',
targets: {
build: {
executor: 'nx:run-commands',
inputs: [
{ fileset: '{projectRoot}/**/*' },
{ externalDependencies: [] }, // intentionally empty
],
},
},
files: [{ file: '/filea.ts', hash: 'a.hash' }],
},
},
},
externalNodes: {
'npm:nx': {
name: 'npm:nx',
type: 'npm',
data: {
packageName: 'nx',
version: '16.0.0',
},
},
'npm:webpack': {
name: 'npm:webpack',
type: 'npm',
data: {
packageName: 'webpack',
version: '5.0.0',
},
},
'npm:react': {
name: 'npm:react',
type: 'npm',
data: {
packageName: 'react',
version: '17.0.0',
},
},
},
dependencies: {},
allWorkspaceFiles,
},
{} as any,
{},
createHashing()
);

const hash = await hasher.hashTask({
target: { project: 'app', target: 'build' },
id: 'app-build',
overrides: { prop: 'prop-value' },
});

expect(hash.details.nodes['npm:nx']).not.toBeDefined();
expect(hash.details.nodes['app']).not.toBeDefined();
});
});

describe('expandNamedInput', () => {
Expand Down
Loading

1 comment on commit f5ae995

@vercel
Copy link

@vercel vercel bot commented on f5ae995 May 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

nx-dev – ./

nx-five.vercel.app
nx.dev
nx-dev-nrwl.vercel.app
nx-dev-git-master-nrwl.vercel.app

Please sign in to comment.