diff --git a/docs/generated/cli/affected-graph.md b/docs/generated/cli/affected-graph.md index d80a58705c..2dd17dcd52 100644 --- a/docs/generated/cli/affected-graph.md +++ b/docs/generated/cli/affected-graph.md @@ -55,12 +55,6 @@ Open the project graph of the workspace in the browser, highlight the projects a ## Options -### all - -Type: `boolean` - -All projects - ### base Type: `string` diff --git a/docs/generated/cli/affected.md b/docs/generated/cli/affected.md index 6bc744acdf..09a84bb588 100644 --- a/docs/generated/cli/affected.md +++ b/docs/generated/cli/affected.md @@ -73,11 +73,11 @@ Use the currently executing project name in your command.: ## Options -### all +### ~~all~~ Type: `boolean` -All projects +**Deprecated:** Use `nx run-many` instead ### base diff --git a/docs/generated/cli/format-check.md b/docs/generated/cli/format-check.md index d8517f03dc..66886623b6 100644 --- a/docs/generated/cli/format-check.md +++ b/docs/generated/cli/format-check.md @@ -21,7 +21,7 @@ Install `nx` globally to invoke the command directly using `nx`, or use `npx nx` Type: `boolean` -All projects +Format all projects ### base diff --git a/docs/generated/cli/format-write.md b/docs/generated/cli/format-write.md index 730e4125f0..c7bfcf03ef 100644 --- a/docs/generated/cli/format-write.md +++ b/docs/generated/cli/format-write.md @@ -21,7 +21,7 @@ Install `nx` globally to invoke the command directly using `nx`, or use `npx nx` Type: `boolean` -All projects +Format all projects ### base diff --git a/docs/generated/cli/print-affected.md b/docs/generated/cli/print-affected.md index b65d25d4ec..c60294a77b 100644 --- a/docs/generated/cli/print-affected.md +++ b/docs/generated/cli/print-affected.md @@ -49,12 +49,6 @@ Prints the tasks.target.project property from the print-affected output: ## Options -### all - -Type: `boolean` - -All projects - ### base Type: `string` diff --git a/docs/generated/cli/show.md b/docs/generated/cli/show.md index 5d577dd691..1d2ef98a27 100644 --- a/docs/generated/cli/show.md +++ b/docs/generated/cli/show.md @@ -10,11 +10,31 @@ Show information about the workspace (e.g., list of projects) ## Usage ```shell -nx show +nx show ``` Install `nx` globally to invoke the command directly using `nx`, or use `npx nx`, `yarn nx`, or `pnpm nx`. +### Examples + +Show all projects in the workspace: + +```shell + nx show projects +``` + +Show affected projects in the workspace: + +```shell + nx show projects --affected +``` + +Show affected projects in the workspace, excluding end-to-end projects: + +```shell + nx show projects --affected --exclude *-e2e +``` + ## Options ### help @@ -23,14 +43,74 @@ Type: `boolean` Show help -### object - -Choices: [projects] - -What to show (e.g., projects) - ### version Type: `boolean` Show version number + +## Subcommands + +### projects + +Show a list of projects in the workspace + +```shell +nx show projects +``` + +#### Options + +##### affected + +Type: `boolean` + +Show only affected projects + +##### base + +Type: `string` + +Base of the current branch (usually main) + +##### exclude + +Type: `string` + +Exclude certain projects from being processed + +##### files + +Type: `string` + +Change the way Nx is calculating the affected command by providing directly changed files, list of files delimited by commas or spaces + +##### head + +Type: `string` + +Latest commit of the current branch (usually HEAD) + +##### help + +Type: `boolean` + +Show help + +##### uncommitted + +Type: `boolean` + +Uncommitted changes + +##### untracked + +Type: `boolean` + +Untracked changes + +##### version + +Type: `boolean` + +Show version number diff --git a/docs/generated/cli/workspace-generator.md b/docs/generated/cli/workspace-generator.md index f4d5909738..e09c418438 100644 --- a/docs/generated/cli/workspace-generator.md +++ b/docs/generated/cli/workspace-generator.md @@ -5,9 +5,9 @@ description: 'Runs a workspace generator from the tools/generators directory' # workspace-generator - **Deprecated:** Use a local plugin instead. See: https://nx.dev/deprecated/workspace-generators +**Deprecated:** Use a local plugin instead. See: https://nx.dev/deprecated/workspace-generators - Runs a workspace generator from the tools/generators directory +Runs a workspace generator from the tools/generators directory ## Usage diff --git a/docs/generated/cli/workspace-lint.md b/docs/generated/cli/workspace-lint.md index c4f1ae20e7..4f5c9cb352 100644 --- a/docs/generated/cli/workspace-lint.md +++ b/docs/generated/cli/workspace-lint.md @@ -5,9 +5,9 @@ description: 'Lint nx specific workspace files (nx.json, workspace.json)' # workspace-lint - **Deprecated:** workspace-lint is deprecated, and will be removed in v17. The checks it used to perform are no longer relevant. +**Deprecated:** workspace-lint is deprecated, and will be removed in v17. The checks it used to perform are no longer relevant. - Lint nx specific workspace files (nx.json, workspace.json) +Lint nx specific workspace files (nx.json, workspace.json) ## Usage diff --git a/docs/generated/packages/nx/documents/affected-dep-graph.md b/docs/generated/packages/nx/documents/affected-dep-graph.md index d80a58705c..2dd17dcd52 100644 --- a/docs/generated/packages/nx/documents/affected-dep-graph.md +++ b/docs/generated/packages/nx/documents/affected-dep-graph.md @@ -55,12 +55,6 @@ Open the project graph of the workspace in the browser, highlight the projects a ## Options -### all - -Type: `boolean` - -All projects - ### base Type: `string` diff --git a/docs/generated/packages/nx/documents/affected.md b/docs/generated/packages/nx/documents/affected.md index 6bc744acdf..09a84bb588 100644 --- a/docs/generated/packages/nx/documents/affected.md +++ b/docs/generated/packages/nx/documents/affected.md @@ -73,11 +73,11 @@ Use the currently executing project name in your command.: ## Options -### all +### ~~all~~ Type: `boolean` -All projects +**Deprecated:** Use `nx run-many` instead ### base diff --git a/docs/generated/packages/nx/documents/format-check.md b/docs/generated/packages/nx/documents/format-check.md index d8517f03dc..66886623b6 100644 --- a/docs/generated/packages/nx/documents/format-check.md +++ b/docs/generated/packages/nx/documents/format-check.md @@ -21,7 +21,7 @@ Install `nx` globally to invoke the command directly using `nx`, or use `npx nx` Type: `boolean` -All projects +Format all projects ### base diff --git a/docs/generated/packages/nx/documents/format-write.md b/docs/generated/packages/nx/documents/format-write.md index 730e4125f0..c7bfcf03ef 100644 --- a/docs/generated/packages/nx/documents/format-write.md +++ b/docs/generated/packages/nx/documents/format-write.md @@ -21,7 +21,7 @@ Install `nx` globally to invoke the command directly using `nx`, or use `npx nx` Type: `boolean` -All projects +Format all projects ### base diff --git a/docs/generated/packages/nx/documents/print-affected.md b/docs/generated/packages/nx/documents/print-affected.md index b65d25d4ec..c60294a77b 100644 --- a/docs/generated/packages/nx/documents/print-affected.md +++ b/docs/generated/packages/nx/documents/print-affected.md @@ -49,12 +49,6 @@ Prints the tasks.target.project property from the print-affected output: ## Options -### all - -Type: `boolean` - -All projects - ### base Type: `string` diff --git a/docs/generated/packages/nx/documents/show.md b/docs/generated/packages/nx/documents/show.md index 5d577dd691..1d2ef98a27 100644 --- a/docs/generated/packages/nx/documents/show.md +++ b/docs/generated/packages/nx/documents/show.md @@ -10,11 +10,31 @@ Show information about the workspace (e.g., list of projects) ## Usage ```shell -nx show +nx show ``` Install `nx` globally to invoke the command directly using `nx`, or use `npx nx`, `yarn nx`, or `pnpm nx`. +### Examples + +Show all projects in the workspace: + +```shell + nx show projects +``` + +Show affected projects in the workspace: + +```shell + nx show projects --affected +``` + +Show affected projects in the workspace, excluding end-to-end projects: + +```shell + nx show projects --affected --exclude *-e2e +``` + ## Options ### help @@ -23,14 +43,74 @@ Type: `boolean` Show help -### object - -Choices: [projects] - -What to show (e.g., projects) - ### version Type: `boolean` Show version number + +## Subcommands + +### projects + +Show a list of projects in the workspace + +```shell +nx show projects +``` + +#### Options + +##### affected + +Type: `boolean` + +Show only affected projects + +##### base + +Type: `string` + +Base of the current branch (usually main) + +##### exclude + +Type: `string` + +Exclude certain projects from being processed + +##### files + +Type: `string` + +Change the way Nx is calculating the affected command by providing directly changed files, list of files delimited by commas or spaces + +##### head + +Type: `string` + +Latest commit of the current branch (usually HEAD) + +##### help + +Type: `boolean` + +Show help + +##### uncommitted + +Type: `boolean` + +Uncommitted changes + +##### untracked + +Type: `boolean` + +Untracked changes + +##### version + +Type: `boolean` + +Show version number diff --git a/docs/generated/packages/nx/documents/workspace-generator.md b/docs/generated/packages/nx/documents/workspace-generator.md index f4d5909738..e09c418438 100644 --- a/docs/generated/packages/nx/documents/workspace-generator.md +++ b/docs/generated/packages/nx/documents/workspace-generator.md @@ -5,9 +5,9 @@ description: 'Runs a workspace generator from the tools/generators directory' # workspace-generator - **Deprecated:** Use a local plugin instead. See: https://nx.dev/deprecated/workspace-generators +**Deprecated:** Use a local plugin instead. See: https://nx.dev/deprecated/workspace-generators - Runs a workspace generator from the tools/generators directory +Runs a workspace generator from the tools/generators directory ## Usage diff --git a/docs/generated/packages/nx/documents/workspace-lint.md b/docs/generated/packages/nx/documents/workspace-lint.md index c4f1ae20e7..4f5c9cb352 100644 --- a/docs/generated/packages/nx/documents/workspace-lint.md +++ b/docs/generated/packages/nx/documents/workspace-lint.md @@ -5,9 +5,9 @@ description: 'Lint nx specific workspace files (nx.json, workspace.json)' # workspace-lint - **Deprecated:** workspace-lint is deprecated, and will be removed in v17. The checks it used to perform are no longer relevant. +**Deprecated:** workspace-lint is deprecated, and will be removed in v17. The checks it used to perform are no longer relevant. - Lint nx specific workspace files (nx.json, workspace.json) +Lint nx specific workspace files (nx.json, workspace.json) ## Usage diff --git a/e2e/nx-run/src/affected-graph.test.ts b/e2e/nx-run/src/affected-graph.test.ts index 798a7e29a7..88e83a2d5d 100644 --- a/e2e/nx-run/src/affected-graph.test.ts +++ b/e2e/nx-run/src/affected-graph.test.ts @@ -65,46 +65,46 @@ describe('Nx Affected and Graph Tests', () => { ); const affectedApps = runCLI( - `print-affected --files="libs/${mylib}/src/index.ts" --select projects` + `show projects --affected --files="libs/${mylib}/src/index.ts"` ); expect(affectedApps).toContain(myapp); expect(affectedApps).not.toContain(myapp2); const implicitlyAffectedApps = runCLI( - 'print-affected --select projects --files="tsconfig.base.json"' + 'show projects --affected --files="tsconfig.base.json"' ); expect(implicitlyAffectedApps).toContain(myapp); expect(implicitlyAffectedApps).toContain(myapp2); const noAffectedApps = runCLI( - 'print-affected --select projects --files="README.md"' + 'show projects --affected projects --files="README.md"' ); expect(noAffectedApps).not.toContain(myapp); expect(noAffectedApps).not.toContain(myapp2); const affectedLibs = runCLI( - `print-affected --select projects --files="libs/${mylib}/src/index.ts"` + `show projects --affected --files="libs/${mylib}/src/index.ts"` ); expect(affectedLibs).toContain(mypublishablelib); expect(affectedLibs).toContain(mylib); expect(affectedLibs).not.toContain(mylib2); const implicitlyAffectedLibs = runCLI( - 'print-affected --select projects --files="tsconfig.base.json"' + 'show projects --affected --files="tsconfig.base.json"' ); expect(implicitlyAffectedLibs).toContain(mypublishablelib); expect(implicitlyAffectedLibs).toContain(mylib); expect(implicitlyAffectedLibs).toContain(mylib2); const noAffectedLibsNonExistentFile = runCLI( - 'print-affected --select projects --files="tsconfig.json"' + 'show projects --affected --files="tsconfig.json"' ); expect(noAffectedLibsNonExistentFile).not.toContain(mypublishablelib); expect(noAffectedLibsNonExistentFile).not.toContain(mylib); expect(noAffectedLibsNonExistentFile).not.toContain(mylib2); const noAffectedLibs = runCLI( - 'print-affected --select projects --files="README.md"' + 'show projects --affected --files="README.md"' ); expect(noAffectedLibs).not.toContain(mypublishablelib); expect(noAffectedLibs).not.toContain(mylib); diff --git a/package.json b/package.json index 1289e124f1..2bb933fb01 100644 --- a/package.json +++ b/package.json @@ -205,7 +205,7 @@ "lines-and-columns": "~2.0.3", "loader-utils": "2.0.3", "magic-string": "~0.26.2", - "markdown-factory": "^0.0.3", + "markdown-factory": "^0.0.5", "memfs": "^3.0.1", "metro-resolver": "^0.74.1", "mini-css-extract-plugin": "~2.4.7", diff --git a/packages/nx/src/command-line/affected/command-object.ts b/packages/nx/src/command-line/affected/command-object.ts index 055481328c..6547e80472 100644 --- a/packages/nx/src/command-line/affected/command-object.ts +++ b/packages/nx/src/command-line/affected/command-object.ts @@ -1,4 +1,4 @@ -import { CommandModule } from 'yargs'; +import { boolean, CommandModule, middleware } from 'yargs'; import { linkToNxDevAndExamples } from '../yargs-utils/documentation'; import { withAffectedOptions, @@ -19,7 +19,18 @@ export const yargsAffectedCommand: CommandModule = { withRunOptions( withOutputStyleOption(withTargetAndConfigurationOption(yargs)) ) - ), + ) + .option('all', { + type: 'boolean', + deprecated: 'Use `nx run-many` instead', + }) + .middleware((args) => { + if (args.all !== undefined) { + throw new Error( + "The '--all' option has been removed for `nx affected`. Use 'nx run-many' instead." + ); + } + }), 'affected' ), handler: async (args) => diff --git a/packages/nx/src/command-line/examples.ts b/packages/nx/src/command-line/examples.ts index 85c372c006..e70ddbb82f 100644 --- a/packages/nx/src/command-line/examples.ts +++ b/packages/nx/src/command-line/examples.ts @@ -339,6 +339,23 @@ export const examples: Record = { 'Create a dedicated commit for each successfully completed migration. You can customize the prefix used for each commit by additionally setting --commit-prefix="PREFIX_HERE "', }, ], + show: [ + { + command: 'show projects', + description: 'Show all projects in the workspace', + }, + + { + command: 'show projects --affected', + description: 'Show affected projects in the workspace', + }, + + { + command: 'show projects --affected --exclude *-e2e', + description: + 'Show affected projects in the workspace, excluding end-to-end projects', + }, + ], watch: [ { command: diff --git a/packages/nx/src/command-line/format/command-object.ts b/packages/nx/src/command-line/format/command-object.ts index 2b7dce2dce..e2801a6446 100644 --- a/packages/nx/src/command-line/format/command-object.ts +++ b/packages/nx/src/command-line/format/command-object.ts @@ -39,6 +39,10 @@ function withFormatOptions(yargs: Argv): Argv { type: 'string', coerce: parseCSV, }) + .option('all', { + describe: 'Format all projects', + type: 'boolean', + }) .conflicts({ all: 'projects', }); diff --git a/packages/nx/src/command-line/show/command-object.ts b/packages/nx/src/command-line/show/command-object.ts index 659e885141..5bae3fe0df 100644 --- a/packages/nx/src/command-line/show/command-object.ts +++ b/packages/nx/src/command-line/show/command-object.ts @@ -1,45 +1,47 @@ -import { Argv, CommandModule } from 'yargs'; +import { CommandModule } from 'yargs'; +import { withAffectedOptions } from '../yargs-utils/shared-options'; +import { ShowProjectOptions } from './show'; -const validObjectTypes = ['projects'] as const; -type NxObject = typeof validObjectTypes[number]; - -interface ShowCommandArguments { - object: NxObject; -} - -export const yargsShowCommand: CommandModule< - ShowCommandArguments, - ShowCommandArguments -> = { - command: 'show ', +export const yargsShowCommand: CommandModule = { + command: 'show', describe: 'Show information about the workspace (e.g., list of projects)', - builder: (yargs) => withShowOptions(yargs), + builder: (yargs) => + yargs + .command(showProjectsCommand) + .demandCommand() + .example( + '$0 show projects', + 'Show a list of all projects in the workspace' + ) + .example( + '$0 show projects --affected', + 'Show affected projects in the workspace' + ) + .example( + '$0 show projects --affected --exclude *-e2e', + 'Show affected projects in the workspace, excluding end-to-end projects' + ), handler: async (args) => { - if (!validObjectTypes.includes(args.object)) { - } - await import('./show').then((m) => m.show(args)); - process.exit(0); + // Noop, yargs will error if not in a subcommand. }, }; -function withShowOptions(yargs: Argv) { - return yargs - .positional('object', { - describe: 'What to show (e.g., projects)', - choices: ['projects'], - required: true, - }) - .coerce({ - object: (arg) => { - if (validObjectTypes.includes(arg)) { - return arg; - } else { - throw new Error( - `Invalid object type: ${arg}. Valid object types are: ${validObjectTypes.join( - ', ' - )}` - ); - } - }, - }); -} +const showProjectsCommand: CommandModule< + Record, + ShowProjectOptions +> = { + command: 'projects', + describe: 'Show a list of projects in the workspace', + builder: (yargs) => + withAffectedOptions(yargs) + .option('affected', { + type: 'boolean', + description: 'Show only affected projects', + }) + .implies('untracked', 'affected') + .implies('uncommitted', 'affected') + .implies('files', 'affected') + .implies('base', 'affected') + .implies('head', 'affected'), + handler: (args) => import('./show').then((m) => m.showProjectsHandler(args)), +}; diff --git a/packages/nx/src/command-line/show/show.ts b/packages/nx/src/command-line/show/show.ts index 478e4b7869..3f69bde3b7 100644 --- a/packages/nx/src/command-line/show/show.ts +++ b/packages/nx/src/command-line/show/show.ts @@ -1,13 +1,74 @@ +import { filterAffected } from '../../project-graph/affected/affected-project-graph'; +import { + calculateFileChanges, + readNxJson, +} from '../../project-graph/file-utils'; +import { + NxArgs, + parseFiles, + splitArgsIntoNxArgsAndOverrides, +} from '../../utils/command-line-utils'; import { createProjectGraphAsync } from '../../project-graph/project-graph'; +import { NxJsonConfiguration } from '../../config/nx-json'; +import { ProjectGraph } from '../../config/project-graph'; +import { findMatchingProjects } from '../../utils/find-matching-projects'; -export async function show(args: { object: 'projects' }): Promise { - if (args.object == 'projects') { - const graph = await createProjectGraphAsync(); - const projects = Object.keys(graph.nodes).join('\n'); - if (projects.length) { - console.log(projects); - } - } else { - throw new Error(`Unrecognized option: ${args.object}`); +export type ShowProjectOptions = { + exclude: string; + files: string; + uncommitted: any; + untracked: any; + base: string; + head: string; + affected: boolean; +}; + +export async function showProjectsHandler( + args: ShowProjectOptions +): Promise { + let graph = await createProjectGraphAsync(); + const nxJson = readNxJson(); + const { nxArgs } = splitArgsIntoNxArgsAndOverrides( + args, + 'affected', + { + printWarnings: false, + }, + nxJson + ); + + if (args.affected) { + graph = await getAffectedGraph(nxArgs, nxJson, graph); } + + const selectedProjects = new Set(Object.keys(graph.nodes)); + + if (args.exclude) { + const excludedProjects = findMatchingProjects(nxArgs.exclude, graph.nodes); + for (const excludedProject of excludedProjects) { + selectedProjects.delete(excludedProject); + } + } + + const projects = Array.from(selectedProjects).join('\n'); + if (projects.length) { + console.log(projects); + } + process.exit(0); +} + +function getAffectedGraph( + nxArgs: NxArgs, + nxJson: NxJsonConfiguration<'*' | string[]>, + graph: ProjectGraph +) { + return filterAffected( + graph, + calculateFileChanges( + parseFiles(nxArgs).files, + graph.allWorkspaceFiles, + nxArgs + ), + nxJson + ); } diff --git a/packages/nx/src/command-line/yargs-utils/shared-options.ts b/packages/nx/src/command-line/yargs-utils/shared-options.ts index 8256b658e9..c78e4fa92e 100644 --- a/packages/nx/src/command-line/yargs-utils/shared-options.ts +++ b/packages/nx/src/command-line/yargs-utils/shared-options.ts @@ -1,6 +1,6 @@ import { Argv } from 'yargs'; -export function withExcludeOption(yargs: Argv): Argv { +export function withExcludeOption(yargs: Argv) { return yargs.option('exclude', { describe: 'Exclude certain projects from being processed', type: 'string', @@ -8,7 +8,7 @@ export function withExcludeOption(yargs: Argv): Argv { }); } -export function withRunOptions(yargs: Argv): Argv { +export function withRunOptions(yargs: Argv) { return withExcludeOption(yargs) .option('parallel', { describe: 'Max number of parallel processes [default is 3]', @@ -74,7 +74,7 @@ export function withRunOptions(yargs: Argv): Argv { export function withTargetAndConfigurationOption( yargs: Argv, demandOption = true -): Argv { +) { return withConfiguration(yargs).option('targets', { describe: 'Tasks to run for affected projects', type: 'string', @@ -95,7 +95,7 @@ export function withConfiguration(yargs: Argv) { }); } -export function withAffectedOptions(yargs: Argv): Argv { +export function withAffectedOptions(yargs: Argv) { return withExcludeOption(yargs) .parserConfiguration({ 'strip-dashed': true, @@ -112,17 +112,10 @@ export function withAffectedOptions(yargs: Argv): Argv { .option('uncommitted', { describe: 'Uncommitted changes', type: 'boolean', - default: undefined, }) .option('untracked', { describe: 'Untracked changes', type: 'boolean', - default: undefined, - }) - .option('all', { - describe: 'All projects', - type: 'boolean', - default: undefined, }) .option('base', { describe: 'Base of the current branch (usually main)', @@ -145,14 +138,13 @@ export function withAffectedOptions(yargs: Argv): Argv { .group(['files', 'uncommitted', 'untracked'], 'or using:') .implies('head', 'base') .conflicts({ - files: ['uncommitted', 'untracked', 'base', 'head', 'all'], - untracked: ['uncommitted', 'files', 'base', 'head', 'all'], - uncommitted: ['files', 'untracked', 'base', 'head', 'all'], - all: ['files', 'untracked', 'uncommitted', 'base', 'head'], + files: ['uncommitted', 'untracked', 'base', 'head'], + untracked: ['uncommitted', 'files', 'base', 'head'], + uncommitted: ['files', 'untracked', 'base', 'head'], }); } -export function withRunManyOptions(yargs: Argv): Argv { +export function withRunManyOptions(yargs: Argv) { return withRunOptions(yargs) .parserConfiguration({ 'strip-dashed': true, @@ -185,7 +177,7 @@ export function withOverrides(args: any): any { export function withOutputStyleOption( yargs: Argv, choices = ['dynamic', 'static', 'stream', 'stream-without-prefixes'] -): Argv { +) { return yargs.option('output-style', { describe: 'Defines how Nx emits outputs tasks logs', type: 'string', @@ -193,7 +185,7 @@ export function withOutputStyleOption( }); } -export function withDepGraphOptions(yargs: Argv): Argv { +export function withDepGraphOptions(yargs: Argv) { return yargs .option('file', { describe: diff --git a/packages/nx/src/utils/command-line-utils.ts b/packages/nx/src/utils/command-line-utils.ts index f45c9f7264..8517e33287 100644 --- a/packages/nx/src/utils/command-line-utils.ts +++ b/packages/nx/src/utils/command-line-utils.ts @@ -111,21 +111,6 @@ export function splitArgsIntoNxArgsAndOverrides( }); } - if ( - !nxArgs.files && - !nxArgs.uncommitted && - !nxArgs.untracked && - !nxArgs.base && - !nxArgs.head && - !nxArgs.all && - overrides._ && - overrides._.length >= 2 - ) { - throw new Error( - `Nx no longer supports using positional arguments for base and head. Please use --base and --head instead.` - ); - } - // Allow setting base and head via environment variables (lower priority then direct command arguments) if (!nxArgs.base && process.env.NX_BASE) { nxArgs.base = process.env.NX_BASE; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9de4863623..71bd3980ab 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -665,8 +665,8 @@ devDependencies: specifier: ~0.26.2 version: 0.26.2 markdown-factory: - specifier: ^0.0.3 - version: 0.0.3 + specifier: ^0.0.5 + version: 0.0.5 memfs: specifier: ^3.0.1 version: 3.4.7 @@ -17673,8 +17673,8 @@ packages: resolution: {integrity: sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==} dev: true - /markdown-factory@0.0.3: - resolution: {integrity: sha512-y7XffnQ61exHdRdFmvp9MeJBGOpUIYJEdiNgj25VCCivA/oXs615ppGqH01lnG8zcZ9cEEPW1jIi1y/Xu4URPA==} + /markdown-factory@0.0.5: + resolution: {integrity: sha512-76bovWfQkv0Pd4qrq3h5sTiimoBUIi3sKac6CUKGPLgEhghrPGBgdXRhVH9sjDpbbyCspRPg940WVrcA9h+XPg==} dev: true /markdown-to-jsx@7.2.0(react@18.2.0): diff --git a/scripts/documentation/generators/generate-cli-data.ts b/scripts/documentation/generators/generate-cli-data.ts index 14b9edec38..0450723fc8 100644 --- a/scripts/documentation/generators/generate-cli-data.ts +++ b/scripts/documentation/generators/generate-cli-data.ts @@ -1,11 +1,13 @@ import * as chalk from 'chalk'; import { readFileSync } from 'fs'; import { readJsonSync } from 'fs-extra'; +import { codeBlock, h1, h2, h3, lines } from 'markdown-factory'; import { join } from 'path'; import { register as registerTsConfigPaths } from 'tsconfig-paths'; + import { examples } from '../../../packages/nx/src/command-line/examples'; import { - formatDeprecated, + formatDescription, generateMarkdownFile, generateOptionsMarkdown, getCommands, @@ -39,39 +41,52 @@ export async function generateCliDocumentation( ); function generateMarkdown(command: ParsedCommand) { - let template = ` + let templateLines = [ + ` --- title: "${command.name} - CLI command" description: "${command.description}" ---- - -# ${command.name} - -${formatDeprecated(command.description, command.deprecated)} - -## Usage - -\`\`\`shell -nx ${command.commandString} -\`\`\` - -Install \`nx\` globally to invoke the command directly using \`nx\`, or use \`npx nx\`, \`yarn nx\`, or \`pnpm nx\`.\n`; +---`, + h1(command.name), + formatDescription(command.description, command.deprecated), + h2('Usage'), + codeBlock(`nx ${command.commandString}`, 'shell'), + 'Install `nx` globally to invoke the command directly using `nx`, or use `npx nx`, `yarn nx`, or `pnpm nx`.', + ]; if (examples[command.name] && examples[command.name].length > 0) { - template += `\n### Examples\n`; + templateLines.push(h3('Examples')); examples[command.name].forEach((example) => { - template += `${example.description}:\n\`\`\`shell\n nx ${example.command}\n\`\`\`\n`; + templateLines.push( + example.description + ':', + codeBlock(` nx ${example.command}`, 'shell') + ); }); } - template += generateOptionsMarkdown(command); + templateLines.push(generateOptionsMarkdown(command)); + + if (command.subcommands?.length) { + templateLines.push(h2('Subcommands')); + for (const subcommand of command.subcommands) { + templateLines.push( + h3(subcommand.name), + formatDescription(subcommand.description, subcommand.deprecated), + codeBlock( + `nx ${command.commandString} ${subcommand.commandString}`, + 'shell' + ), + generateOptionsMarkdown(subcommand, 2) + ); + } + } return { name: command.name .replace(':', '-') .replace(' ', '-') .replace(/[\]\[.]+/gm, ''), - template, + template: lines(templateLines), }; } diff --git a/scripts/documentation/utils.ts b/scripts/documentation/utils.ts index 2bcf8b6923..d521edc2e7 100644 --- a/scripts/documentation/utils.ts +++ b/scripts/documentation/utils.ts @@ -1,4 +1,5 @@ import { outputFileSync } from 'fs-extra'; +import { bold, h, lines as mdLines, strikethrough } from 'markdown-factory'; import { join } from 'path'; import { format, resolveConfig } from 'prettier'; @@ -65,7 +66,7 @@ export async function formatWithPrettier(filePath: string, content: string) { return format(content, options); } -export function formatDeprecated( +export function formatDescription( description: string, deprecated: boolean | string ) { @@ -73,12 +74,8 @@ export function formatDeprecated( return description; } return deprecated === true - ? `**Deprecated:** ${description}` - : ` - **Deprecated:** ${deprecated} - - ${description} - `; + ? `${bold('Deprecated:')} ${description}` + : mdLines(`${bold('Deprecated:')} ${deprecated}`, description); } export function getCommands(command: any) { @@ -87,9 +84,12 @@ export function getCommands(command: any) { export interface ParsedCommandOption { name: string; + type: string; description: string; default: string; deprecated: boolean | string; + hidden: boolean; + choices?: string[]; } export interface ParsedCommand { @@ -98,6 +98,7 @@ export interface ParsedCommand { description: string; deprecated: string; options?: Array; + subcommands?: Array; } const YargsTypes = ['array', 'count', 'string', 'boolean', 'number']; @@ -142,6 +143,12 @@ export async function parseCommand( ); return acc; }, {}); + const subcommands = await Promise.all( + Object.entries(getCommands(builder)).map( + ([subCommandName, subCommandConfig]) => + parseCommand(subCommandName, subCommandConfig) + ) + ); return { name, @@ -160,39 +167,44 @@ export async function parseCommand( deprecated: builderDeprecatedOptions[key], hidden: builderOptions.hiddenOptions.includes(key), })) || null, + subcommands, }; } -export function generateOptionsMarkdown(command: any): string { - let response = ''; +export function generateOptionsMarkdown( + command: ParsedCommand, + extraHeadingLevels = 0 +): string { + const lines: string[] = []; if (Array.isArray(command.options) && !!command.options.length) { - response += '\n## Options\n'; + lines.push(h(2 + extraHeadingLevels, 'Options')); command.options - .sort((a: any, b: any) => sortAlphabeticallyFunction(a.name, b.name)) - .filter(({ hidden }: any) => !hidden) - .forEach((option: any) => { - response += `\n### ${ - option.deprecated ? `~~${option.name}~~` : option.name - }\n`; + .sort((a, b) => sortAlphabeticallyFunction(a.name, b.name)) + .filter(({ hidden }) => !hidden) + .forEach((option) => { + lines.push( + h( + 3 + extraHeadingLevels, + option.deprecated ? strikethrough(option.name) : option.name + ) + ); if (option.type !== undefined && option.type !== '') { - response += `\nType: \`${option.type}\`\n`; + lines.push(`Type: \`${option.type}\``); } if (option.choices !== undefined) { const choices = option.choices .map((c: any) => JSON.stringify(c).replace(/"/g, '')) .join(', '); - response += `\nChoices: [${choices}]\n`; + lines.push(`Choices: [${choices}]`); } if (option.default !== undefined && option.default !== '') { - response += `\nDefault: \`${JSON.stringify(option.default).replace( - /"/g, - '' - )}\`\n`; + lines.push( + `Default: \`${JSON.stringify(option.default).replace(/"/g, '')}\`` + ); } - response += - '\n' + formatDeprecated(option.description, option.deprecated); + lines.push(formatDescription(option.description, option.deprecated)); }); } - return response; + return mdLines(lines); }