import * as fs from 'fs-extra'; import * as path from 'path'; import { dedent } from 'tslint/lib/utils'; import { commandsObject } from '../../packages/workspace'; import { generateFile, sortAlphabeticallyFunction } from './utils'; const importFresh = require('import-fresh'); const examples = { 'print-affected': [ { command: 'print-affected', description: 'Print information about affected projects and the dependency graph.' }, { command: 'print-affected --base=master --head=HEAD', description: 'Print information about the projects affected by the changes between master and HEAD (e.g,. PR).' }, { command: 'print-affected --target=test', description: 'Prints information about the affected projects and a list of tasks to test them.' }, { command: 'print-affected --target=build --with-deps', description: 'Prints information about the affected projects and a list of tasks to build them and their dependencies.' } ], affected: [ { command: 'affected --target=custom-target', description: 'Run custom target for all affected projects' }, { command: 'affected --target=test --parallel --maxParallel=5', description: 'Run tests in parallel' }, { command: 'affected --target=test --only-failed', description: 'Rerun the test target only for the projects that failed last time' }, { command: 'affected --target=test --all', description: 'Run the test target for all projects' }, { command: 'affected --target=test --with-deps', description: 'Run the test target for the affected projects and also all the projects the affected projects depend on.' }, { command: 'affected --target=test --files=libs/mylib/src/index.ts', description: 'Run tests for all the projects affected by changing the index.ts file' }, { command: 'affected --target=test --base=master --head=HEAD', description: 'Run tests for all the projects affected by the changes between master and HEAD (e.g., PR)' }, { command: 'affected --target=test --base=master~1 --head=master', description: 'Run tests for all the projects affected by the last commit on master' }, { command: 'affected --target=build --base=master~1 --head=master --with-deps', description: 'Run build for all the projects affected by the last commit on master and their dependencies' } ], 'affected:test': [ { command: 'affected:test --parallel --maxParallel=5', description: 'Run tests in parallel' }, { command: 'affected:test --only-failed', description: 'Rerun the test target only for the projects that failed last time' }, { command: 'affected:test --all', description: 'Run the test target for all projects' }, { command: 'affected:test --files=libs/mylib/src/index.ts', description: 'Run tests for all the projects affected by changing the index.ts file' }, { command: 'affected:test --base=master --head=HEAD', description: 'Run tests for all the projects affected by the changes between master and HEAD (e.g., PR)' }, { command: 'affected:test --base=master~1 --head=master', description: 'Run tests for all the projects affected by the last commit on master' } ], 'affected:build': [ { command: 'affected:build --parallel --maxParallel=5', description: 'Run build in parallel' }, { command: 'affected:build --only-failed', description: 'Rerun the build target only for the projects that failed last time' }, { command: 'affected:build --all', description: 'Run the build target for all projects' }, { command: 'affected:build --with-deps', description: 'Run the build target for the affected projects and also all the projects the affected projects depend on.' }, { command: 'affected:build --files=libs/mylib/src/index.ts', description: 'Run build for all the projects affected by changing the index.ts file' }, { command: 'affected:build --base=master --head=HEAD', description: 'Run build for all the projects affected by the changes between master and HEAD (e.g., PR)' }, { command: 'affected:build --base=master~1 --head=master', description: 'Run build for all the projects affected by the last commit on master' }, { command: 'affected:build --base=master~1 --head=master --with-deps', description: 'Run build for all the projects affected by the last commit on master and their dependencies' } ], 'affected:e2e': [ { command: 'affected:e2e --parallel --maxParallel=5', description: 'Run tests in parallel' }, { command: 'affected:e2e --only-failed', description: 'Rerun the test target only for the projects that failed last time' }, { command: 'affected:e2e --all', description: 'Run the test target for all projects' }, { command: 'affected:e2e --files=libs/mylib/src/index.ts', description: 'Run tests for all the projects affected by changing the index.ts file' }, { command: 'affected:e2e --base=master --head=HEAD', description: 'Run tests for all the projects affected by the changes between master and HEAD (e.g., PR)' }, { command: 'affected:e2e --base=master~1 --head=master', description: 'Run tests for all the projects affected by the last commit on master' } ], 'affected:lint': [ { command: 'affected:lint --parallel --maxParallel=5', description: 'Run lint in parallel' }, { command: 'affected:lint --only-failed', description: 'Rerun the lint target only for the projects that failed last time' }, { command: 'affected:lint --all', description: 'Run the lint target for all projects' }, { command: 'affected:lint --files=libs/mylib/src/index.ts', description: 'Run lint for all the projects affected by changing the index.ts file' }, { command: 'affected:lint --base=master --head=HEAD', description: 'Run lint for all the projects affected by the changes between master and HEAD (e.g., PR)' }, { command: 'affected:lint --base=master~1 --head=master', description: 'Run lint for all the projects affected by the last commit on master' } ], 'affected:apps': [ { command: 'affected:apps --files=libs/mylib/src/index.ts', description: 'Print the names of all the apps affected by changing the index.ts file' }, { command: 'affected:apps --base=master --head=HEAD', description: 'Print the names of all the apps affected by the changes between master and HEAD (e.g., PR)' }, { command: 'affected:apps --base=master~1 --head=master', description: 'Print the names of all the apps affected by the last commit on master' } ], 'affected:libs': [ { command: 'affected:libs --files=libs/mylib/src/index.ts', description: 'Print the names of all the libs affected by changing the index.ts file' }, { command: 'affected:libs --base=master --head=HEAD', description: 'Print the names of all the libs affected by the changes between master and HEAD (e.g., PR)' }, { command: 'affected:libs --base=master~1 --head=master', description: 'Print the names of all the libs affected by the last commit on master' } ], 'format:write': [], 'format:check': [], 'dep-graph': [ { command: 'dep-graph', description: 'Open the dep graph of the workspace in the browser' }, { command: 'dep-graph --file=output.json', description: 'Save the dep graph into a json file' }, { command: 'dep-graph --filter=todos-feature-main', description: 'Show the graph where every node is either an ancestor or a descendant of todos-feature-main.' }, { command: 'dep-graph --exclude=project-one,project-two', description: 'Exclude project-one and project-two from the dep graph.' } ], 'affected:dep-graph': [ { command: 'affected:dep-graph --files=libs/mylib/src/index.ts', description: 'Open the dep graph of the workspace in the browser, and highlight the projects affected by changing the index.ts file' }, { command: 'affected:dep-graph --base=master --head=HEAD', description: 'Open the dep graph of the workspace in the browser, and highlight the projects affected by the changes between master and HEAD (e.g., PR)' }, { command: 'affected:dep-graph --base=master --head=HEAD --file=output.json', description: 'Save the dep graph of the workspace in a json file, and highlight the projects affected by the changes between master and HEAD (e.g., PR)' }, { command: 'affected:dep-graph --base=master~1 --head=master', description: 'Open the dep graph of the workspace in the browser, and highlight the projects affected by the last commit on master' } ], 'workspace-schematic': [], 'list [plugin]': [ { command: 'list', description: 'List the plugins installed in the current workspace' }, { command: 'list @nrwl/web', description: 'List the schematics and builders available in the `@nrwl/web` plugin if it is installed (If the plugin is not installed `nx` will show advice on how to add it to your workspace)' } ], 'run-many': [ { command: 'run-many --target=test --all', description: 'Test all projects.' }, { command: 'run-many --target=test --projects=proj1,proj2', description: 'Test proj1 and proj2.' }, { command: 'run-many --target=test --projects=proj1,proj2 --parallel --maxParallel=2', description: 'Test proj1 and proj2 in parallel.' }, { command: 'run-many --target=test --projects=proj1,proj2 --with-deps', description: 'Build proj1 and proj2 and all their dependencies.' } ] }; ['web', 'angular', 'react'].forEach(framework => { const commandsOutputDirectory = path.join( __dirname, '../../docs/', framework, 'api-workspace/npmscripts' ); fs.removeSync(commandsOutputDirectory); function getCommands(command) { return command.getCommandInstance().getCommandHandlers(); } function parseCommandInstance(name, command) { const builder = command.builder(importFresh('yargs')().resetOptions()); const builderDescriptions = builder.getUsageInstance().getDescriptions(); const builderDefaultOptions = builder.getOptions().default; return { command: command['original'], description: command['description'], options: Object.keys(builderDescriptions).map(name => ({ command: '--'.concat(name), description: builderDescriptions[name] ? builderDescriptions[name].replace('__yargsString__:', '') : '', default: builderDefaultOptions[name] })) || null }; } function generateMarkdown(command) { let template = dedent` # ${command.command} ${command.description} ## Usage \`\`\`bash nx ${command.command} \`\`\` Install \`@nrwl/cli\` globally to invoke the command directly using \`nx\`, or use \`npm run nx\` or \`yarn nx\`.\n`; if (examples[command.command] && examples[command.command].length > 0) { template += `### Examples`; examples[command.command].forEach(example => { template += dedent` ${example.description}: \`\`\`bash nx ${example.command} \`\`\` `; }); } if (Array.isArray(command.options) && !!command.options.length) { template += '\n## Options'; command.options .sort((a, b) => sortAlphabeticallyFunction( a.command.replace('--', ''), b.command.replace('--', '') ) ) .forEach( option => (template += dedent` ### ${option.command.replace('--', '')} ${ option.default === undefined || option.default === '' ? '' : `Default: \`${option.default}\`\n` } ${option.description} `) ); } return { name: command.command .replace(':', '-') .replace(' ', '-') .replace(/[\]\[.]+/gm, ''), template }; } // TODO: Try to add option's type, examples, and group? // TODO: split one command per page / Create an index const npmscripts = getCommands(commandsObject); Object.keys(npmscripts) .filter(name => !name.startsWith('run') && !name.startsWith('generate')) .map(name => parseCommandInstance(name, npmscripts[name])) .map(command => generateMarkdown(command)) .forEach(templateObject => generateFile(commandsOutputDirectory, templateObject) ); });