Skip to content

Commit

Permalink
feat(core): provide a way to handle get unparsed args in the executor
Browse files Browse the repository at this point in the history
  • Loading branch information
vsavkin committed Jun 10, 2022
1 parent 6132ad7 commit 5c49345
Show file tree
Hide file tree
Showing 31 changed files with 454 additions and 529 deletions.
16 changes: 14 additions & 2 deletions docs/generated/packages/nx.json
Expand Up @@ -139,10 +139,16 @@
"cwd": {
"type": "string",
"description": "Current working directory of the commands. If it's not specified the commands will run in the workspace root, if a relative path is specified the commands will run in that path relative to the workspace root and if it's an absolute path the commands will run in that path."
},
"__unparsed__": {
"hidden": true,
"type": "array",
"items": { "type": "string" },
"$default": { "$source": "unparsed" }
}
},
"additionalProperties": true,
"required": [],
"required": ["__unparsed__"],
"examplesFile": "`workspace.json`:\n\n```json\n//...\n\"frontend\": {\n \"targets\": {\n //...\n \"ls-project-root\": {\n \"executor\": \"nx:run-commands\",\n \"options\": {\n \"command\": \"ls apps/frontend/src\"\n }\n }\n }\n}\n```\n\n```bash\nnx run frontend:ls-project-root\n```\n\n##### Chaining commands, interpolating args and setting the cwd\n\nLet's say each of our workspace projects has some custom bash scripts in a `scripts` folder.\nWe want a simple way to create empty bash script files for a given project, that have the execute permissions already set.\n\nGiven that Nx knows our workspace structure, we should be able to give it a project and the name of our script, and it should take care of the rest.\n\nThe `commands` option accepts as many commands as you want. By default, they all run in parallel.\nYou can run them sequentially by setting `parallel: false`:\n\n```json\n\"create-script\": {\n \"executor\": \"nx:run-commands\",\n \"options\": {\n \"commands\": [\n \"mkdir -p scripts\",\n \"touch scripts/{args.name}.sh\",\n \"chmod +x scripts/{args.name}.sh\"\n ],\n \"cwd\": \"apps/frontend\",\n \"parallel\": false\n }\n}\n```\n\nBy setting the `cwd` option, each command will run in the `apps/frontend` folder.\n\nWe run the above with:\n\n```bash\nnx run frontend:create-script --args=\"--name=example\"\n```\n\nor simply with:\n\n```bash\nnx run frontend:create-script --name=example\n```\n\n##### Arguments forwarding\n\nWhen interpolation is not present in the command, all arguments are forwarded to the command by default.\n\nThis is useful when you need to pass raw argument strings to your command.\n\nFor example, when you run:\n\nnx run frontend:webpack --args=\"--config=example.config.js\"\n\n```json\n\"webpack\": {\n \"executor\": \"nx:run-commands\",\n \"options\": {\n \"command\": \"webpack\"\n }\n}\n```\n\nThe above command will execute: `webpack --config=example.config.js`\n\nThis functionality can be disabled by using `commands` and expanding each `command` into an object\nthat sets the `forwardAllArgs` option to `false` as shown below:\n\n```json\n\"webpack\": {\n \"executor\": \"nx:run-commands\",\n \"options\": {\n \"commands\": [\n {\n \"command\": \"webpack\",\n \"forwardAllArgs\": false\n }\n ]\n }\n}\n```\n\n##### Custom **done** conditions\n\nNormally, `run-commands` considers the commands done when all of them have finished running. If you don't need to wait until they're all done, you can set a special string that considers the commands finished the moment the string appears in `stdout` or `stderr`:\n\n```json\n\"finish-when-ready\": {\n \"executor\": \"nx:run-commands\",\n \"options\": {\n \"commands\": [\n \"sleep 5 && echo 'FINISHED'\",\n \"echo 'READY'\"\n ],\n \"readyWhen\": \"READY\",\n \"parallel\": true\n }\n}\n```\n\n```bash\nnx run frontend:finish-when-ready\n```\n\nThe above commands will finish immediately, instead of waiting for 5 seconds.\n\n##### Nx Affected\n\nThe true power of `run-commands` comes from the fact that it runs through `nx`, which knows about your project graph. So you can run **custom commands** only for the projects that have been affected by a change.\n\nWe can create some configurations to generate docs, and if run using `nx affected`, it will only generate documentation for the projects that have been changed:\n\n```bash\nnx affected --target=generate-docs\n```\n\n```json\n//...\n\"frontend\": {\n \"targets\": {\n //...\n \"generate-docs\": {\n \"executor\": \"nx:run-commands\",\n \"options\": {\n \"command\": \"npx compodoc -p apps/frontend/tsconfig.app.json\"\n }\n }\n }\n},\n\"api\": {\n \"targets\": {\n //...\n \"generate-docs\": {\n \"executor\": \"nx:run-commands\",\n \"options\": {\n \"command\": \"npx compodoc -p apps/api/tsconfig.app.json\"\n }\n }\n }\n}\n```\n"
},
"description": "Run any custom commands with Nx.",
Expand All @@ -163,10 +169,16 @@
"script": {
"type": "string",
"description": "An npm script name in the `package.json` file of the project (e.g., `build`)."
},
"__unparsed__": {
"hidden": true,
"type": "array",
"items": { "type": "string" },
"$default": { "$source": "unparsed" }
}
},
"additionalProperties": true,
"required": ["script"],
"required": ["script", "__unparsed__"],
"examplesFile": "`workspace.json`:\n\n```json\n\"frontend\": {\n \"root\": \"packages/frontend\",\n \"targets\": {\n \"build\": {\n \"executor\": \"nx:run-script\",\n \"options\": {\n \"script\": \"build-my-project\"\n }\n }\n }\n}\n```\n\n```bash\nnx run frontend:build\n```\n\nThe `build` target is going to run `npm run build-my-project` (or `yarn build-my-project`) in the `packages/frontend` directory.\n\n#### Caching Artifacts\n\nBy default, Nx is going to cache `dist/packages/frontend`, `packages/frontend/dist`, `packages/frontend/build`, `packages/frontend/public`. If your npm script writes files to other places, you can override the list of cached outputs as follows:\n\n```json\n\"frontend\": {\n \"root\": \"packages/frontend\",\n \"targets\": {\n \"build\": {\n \"executor\": \"nx:run-script\",\n \"outputs\": [\"packages/frontend/dist\", \"packaged/frontend/docs\"],\n \"options\": {\n \"script\": \"build-my-project\"\n }\n }\n }\n}\n```\n",
"presets": []
},
Expand Down
16 changes: 14 additions & 2 deletions docs/generated/packages/workspace.json
Expand Up @@ -823,10 +823,16 @@
"cwd": {
"type": "string",
"description": "Current working directory of the commands. If it's not specified the commands will run in the workspace root, if a relative path is specified the commands will run in that path relative to the workspace root and if it's an absolute path the commands will run in that path."
},
"__unparsed__": {
"hidden": true,
"type": "array",
"items": { "type": "string" },
"$default": { "$source": "unparsed" }
}
},
"additionalProperties": true,
"required": [],
"required": ["__unparsed__"],
"examplesFile": "`workspace.json`:\n\n```json\n//...\n\"frontend\": {\n \"targets\": {\n //...\n \"ls-project-root\": {\n \"executor\": \"nx:run-commands\",\n \"options\": {\n \"command\": \"ls apps/frontend/src\"\n }\n }\n }\n}\n```\n\n```bash\nnx run frontend:ls-project-root\n```\n\n##### Chaining commands, interpolating args and setting the cwd\n\nLet's say each of our workspace projects has some custom bash scripts in a `scripts` folder.\nWe want a simple way to create empty bash script files for a given project, that have the execute permissions already set.\n\nGiven that Nx knows our workspace structure, we should be able to give it a project and the name of our script, and it should take care of the rest.\n\nThe `commands` option accepts as many commands as you want. By default, they all run in parallel.\nYou can run them sequentially by setting `parallel: false`:\n\n```json\n\"create-script\": {\n \"executor\": \"nx:run-commands\",\n \"options\": {\n \"commands\": [\n \"mkdir -p scripts\",\n \"touch scripts/{args.name}.sh\",\n \"chmod +x scripts/{args.name}.sh\"\n ],\n \"cwd\": \"apps/frontend\",\n \"parallel\": false\n }\n}\n```\n\nBy setting the `cwd` option, each command will run in the `apps/frontend` folder.\n\nWe run the above with:\n\n```bash\nnx run frontend:create-script --args=\"--name=example\"\n```\n\nor simply with:\n\n```bash\nnx run frontend:create-script --name=example\n```\n\n##### Arguments forwarding\n\nWhen interpolation is not present in the command, all arguments are forwarded to the command by default.\n\nThis is useful when you need to pass raw argument strings to your command.\n\nFor example, when you run:\n\nnx run frontend:webpack --args=\"--config=example.config.js\"\n\n```json\n\"webpack\": {\n \"executor\": \"nx:run-commands\",\n \"options\": {\n \"command\": \"webpack\"\n }\n}\n```\n\nThe above command will execute: `webpack --config=example.config.js`\n\nThis functionality can be disabled by using `commands` and expanding each `command` into an object\nthat sets the `forwardAllArgs` option to `false` as shown below:\n\n```json\n\"webpack\": {\n \"executor\": \"nx:run-commands\",\n \"options\": {\n \"commands\": [\n {\n \"command\": \"webpack\",\n \"forwardAllArgs\": false\n }\n ]\n }\n}\n```\n\n##### Custom **done** conditions\n\nNormally, `run-commands` considers the commands done when all of them have finished running. If you don't need to wait until they're all done, you can set a special string that considers the commands finished the moment the string appears in `stdout` or `stderr`:\n\n```json\n\"finish-when-ready\": {\n \"executor\": \"nx:run-commands\",\n \"options\": {\n \"commands\": [\n \"sleep 5 && echo 'FINISHED'\",\n \"echo 'READY'\"\n ],\n \"readyWhen\": \"READY\",\n \"parallel\": true\n }\n}\n```\n\n```bash\nnx run frontend:finish-when-ready\n```\n\nThe above commands will finish immediately, instead of waiting for 5 seconds.\n\n##### Nx Affected\n\nThe true power of `run-commands` comes from the fact that it runs through `nx`, which knows about your project graph. So you can run **custom commands** only for the projects that have been affected by a change.\n\nWe can create some configurations to generate docs, and if run using `nx affected`, it will only generate documentation for the projects that have been changed:\n\n```bash\nnx affected --target=generate-docs\n```\n\n```json\n//...\n\"frontend\": {\n \"targets\": {\n //...\n \"generate-docs\": {\n \"executor\": \"nx:run-commands\",\n \"options\": {\n \"command\": \"npx compodoc -p apps/frontend/tsconfig.app.json\"\n }\n }\n }\n},\n\"api\": {\n \"targets\": {\n //...\n \"generate-docs\": {\n \"executor\": \"nx:run-commands\",\n \"options\": {\n \"command\": \"npx compodoc -p apps/api/tsconfig.app.json\"\n }\n }\n }\n}\n```\n"
},
"description": "Run any custom commands with Nx.",
Expand Down Expand Up @@ -870,10 +876,16 @@
"script": {
"type": "string",
"description": "An npm script name in the `package.json` file of the project (e.g., `build`)."
},
"__unparsed__": {
"hidden": true,
"type": "array",
"items": { "type": "string" },
"$default": { "$source": "unparsed" }
}
},
"additionalProperties": true,
"required": ["script"],
"required": ["script", "__unparsed__"],
"examplesFile": "`workspace.json`:\n\n```json\n\"frontend\": {\n \"root\": \"packages/frontend\",\n \"targets\": {\n \"build\": {\n \"executor\": \"nx:run-script\",\n \"options\": {\n \"script\": \"build-my-project\"\n }\n }\n }\n}\n```\n\n```bash\nnx run frontend:build\n```\n\nThe `build` target is going to run `npm run build-my-project` (or `yarn build-my-project`) in the `packages/frontend` directory.\n\n#### Caching Artifacts\n\nBy default, Nx is going to cache `dist/packages/frontend`, `packages/frontend/dist`, `packages/frontend/build`, `packages/frontend/public`. If your npm script writes files to other places, you can override the list of cached outputs as follows:\n\n```json\n\"frontend\": {\n \"root\": \"packages/frontend\",\n \"targets\": {\n \"build\": {\n \"executor\": \"nx:run-script\",\n \"outputs\": [\"packages/frontend/dist\", \"packaged/frontend/docs\"],\n \"options\": {\n \"script\": \"build-my-project\"\n }\n }\n }\n}\n```\n",
"presets": []
},
Expand Down
80 changes: 0 additions & 80 deletions e2e/cli/src/cli.test.ts → e2e/cli/src/misc.test.ts
@@ -1,96 +1,16 @@
import {
getPublishedVersion,
isNotWindows,
newProject,
readFile,
readJson,
runCLI,
runCLIAsync,
runCommand,
tmpProjPath,
uniq,
updateFile,
updateProjectConfig,
} from '@nrwl/e2e/utils';
import { renameSync } from 'fs';
import { packagesWeCareAbout } from 'nx/src/command-line/report';

describe('Cli', () => {
beforeEach(() => newProject());

it('should execute long running tasks', async () => {
const myapp = uniq('myapp');
runCLI(`generate @nrwl/web:app ${myapp}`);
updateProjectConfig(myapp, (c) => {
c.targets['counter'] = {
executor: '@nrwl/workspace:counter',
options: {
to: 2,
},
};
return c;
});

const success = runCLI(`counter ${myapp} --result=true`);
expect(success).toContain('0');
expect(success).toContain('1');

expect(() => runCLI(`counter ${myapp} --result=false`)).toThrowError();
});

it('should run npm scripts', async () => {
const mylib = uniq('mylib');
runCLI(`generate @nrwl/node:lib ${mylib}`);

updateProjectConfig(mylib, (j) => {
delete j.targets;
return j;
});

updateFile(
`libs/${mylib}/package.json`,
JSON.stringify({
name: 'mylib1',
scripts: { 'echo:dev': `echo ECHOED` },
})
);

const { stdout } = await runCLIAsync(`echo:dev ${mylib} --a=123`, {
silent: true,
});
expect(stdout).toMatch(/ECHOED "?--a=123"?/);
}, 1000000);

it('should show help', async () => {
const myapp = uniq('myapp');
runCLI(`generate @nrwl/web:app ${myapp}`);

let mainHelp = runCLI(`--help`);
expect(mainHelp).toContain('Run a target for a project');
expect(mainHelp).toContain('Run target for affected projects');

mainHelp = runCLI(`help`);
expect(mainHelp).toContain('Run a target for a project');
expect(mainHelp).toContain('Run target for affected projects');

const genHelp = runCLI(`g @nrwl/web:app --help`);
expect(genHelp).toContain(
'Find more information and examples at: https://nx.dev/packages/web/generators/application'
);

const buildHelp = runCLI(`build ${myapp} --help`);
expect(buildHelp).toContain(
'Find more information and examples at: https://nx.dev/packages/web/executors/webpack'
);

const affectedHelp = runCLI(`affected --help`);
expect(affectedHelp).toContain('Run target for affected projects');

const version = runCLI(`--version`);
expect(version).toContain(getPublishedVersion()); // stub value
}, 120000);
});

describe('report', () => {
beforeEach(() => newProject());

Expand Down
67 changes: 67 additions & 0 deletions e2e/cli/src/run.test.ts
@@ -0,0 +1,67 @@
import {
getPublishedVersion,
isNotWindows,
newProject,
readFile,
readJson,
runCLI,
runCLIAsync,
runCommand,
tmpProjPath,
uniq,
updateFile,
updateProjectConfig,
} from '@nrwl/e2e/utils';
import { renameSync } from 'fs';
import { packagesWeCareAbout } from 'nx/src/command-line/report';

//
describe('Running targets', () => {
beforeEach(() => newProject());

it('should execute long running tasks', async () => {
const myapp = uniq('myapp');
runCLI(`generate @nrwl/web:app ${myapp}`);
updateProjectConfig(myapp, (c) => {
c.targets['counter'] = {
executor: '@nrwl/workspace:counter',
options: {
to: 2,
},
};
return c;
});

const success = runCLI(`counter ${myapp} --result=true`);
expect(success).toContain('0');
expect(success).toContain('1');

expect(() => runCLI(`counter ${myapp} --result=false`)).toThrowError();
});

it('should run npm scripts', async () => {
const mylib = uniq('mylib');
runCLI(`generate @nrwl/node:lib ${mylib}`);

updateProjectConfig(mylib, (j) => {
delete j.targets;
return j;
});

updateFile(
`libs/${mylib}/package.json`,
JSON.stringify({
name: 'mylib1',
scripts: { 'echo:dev': `echo ECHOED` },
})
);

const { stdout } = await runCLIAsync(
`echo:dev ${mylib} -- positional --a=123 --no-b`,
{
silent: true,
}
);
expect(stdout).toMatch(/ECHOED positional --a=123 --no-b/);
}, 1000000);
});
9 changes: 2 additions & 7 deletions e2e/workspace-core/src/run-commands.test.ts
Expand Up @@ -53,20 +53,15 @@ describe('Run Commands', () => {
config.targets.echo = {
executor: '@nrwl/workspace:run-commands',
options: {
command: 'echo',
command: 'echo --var1={args.var1}',
var1: 'a',
var2: 'b',
'var-hyphen': 'c',
varCamelCase: 'd',
},
};
return config;
});

const result = runCLI(`run ${mylib}:echo`, { silent: true });
expect(result).toContain(
'--var1=a --var2=b --var-hyphen=c --varCamelCase=d'
);
expect(result).toContain('--var1=a');
}, 120000);

it('should interpolate provided arguments', async () => {
Expand Down
4 changes: 2 additions & 2 deletions package.json
Expand Up @@ -23,7 +23,8 @@
"local-registry": "./scripts/local-registry.sh",
"documentation": "ts-node -P scripts/tsconfig.scripts.json ./scripts/documentation/documentation.ts && yarn check-documentation-map",
"submit-plugin": "node ./scripts/submit-plugin.js",
"prepare": "is-ci || husky install"
"prepare": "is-ci || husky install",
"echo": "echo 1234"
},
"devDependencies": {
"@angular-devkit/architect": "~0.1400.0",
Expand Down Expand Up @@ -305,4 +306,3 @@
"minimist": "^1.2.6"
}
}

1 comment on commit 5c49345

@vercel
Copy link

@vercel vercel bot commented on 5c49345 Jun 10, 2022

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.