feat(core): add --affected to show only affected projects (#16970)
This commit is contained in:
parent
c71027fbfb
commit
b20e906f00
@ -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`
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -49,12 +49,6 @@ Prints the tasks.target.project property from the print-affected output:
|
||||
|
||||
## Options
|
||||
|
||||
### all
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
All projects
|
||||
|
||||
### base
|
||||
|
||||
Type: `string`
|
||||
|
||||
@ -10,11 +10,31 @@ Show information about the workspace (e.g., list of projects)
|
||||
## Usage
|
||||
|
||||
```shell
|
||||
nx show <object>
|
||||
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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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`
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -49,12 +49,6 @@ Prints the tasks.target.project property from the print-affected output:
|
||||
|
||||
## Options
|
||||
|
||||
### all
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
All projects
|
||||
|
||||
### base
|
||||
|
||||
Type: `string`
|
||||
|
||||
@ -10,11 +10,31 @@ Show information about the workspace (e.g., list of projects)
|
||||
## Usage
|
||||
|
||||
```shell
|
||||
nx show <object>
|
||||
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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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) =>
|
||||
|
||||
@ -339,6 +339,23 @@ export const examples: Record<string, Example[]> = {
|
||||
'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:
|
||||
|
||||
@ -39,6 +39,10 @@ function withFormatOptions(yargs: Argv): Argv {
|
||||
type: 'string',
|
||||
coerce: parseCSV,
|
||||
})
|
||||
.option('all', {
|
||||
describe: 'Format all projects',
|
||||
type: 'boolean',
|
||||
})
|
||||
.conflicts({
|
||||
all: 'projects',
|
||||
});
|
||||
|
||||
@ -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 <object>',
|
||||
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<string, unknown>,
|
||||
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)),
|
||||
};
|
||||
|
||||
@ -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<void> {
|
||||
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<void> {
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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;
|
||||
|
||||
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@ -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):
|
||||
|
||||
@ -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),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -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<ParsedCommandOption>;
|
||||
subcommands?: Array<ParsedCommand>;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user