chore(core): improve help output for generators and executors (#9800)

This commit is contained in:
James Henry 2022-04-12 22:05:55 +04:00 committed by GitHub
parent d4682be058
commit 46e7b4d49c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
73 changed files with 664 additions and 129 deletions

View File

@ -5,10 +5,18 @@
"node": true
},
"ignorePatterns": ["**/*.ts"],
"plugins": ["@typescript-eslint"],
"plugins": ["@typescript-eslint", "@nrwl/nx"],
"extends": [],
"rules": {
"@typescript-eslint/explicit-module-boundary-types": "off"
},
"overrides": []
"overrides": [
{
"files": ["**/executors/**/schema.json", "**/generators/**/schema.json"],
"parser": "jsonc-eslint-parser",
"rules": {
"@nrwl/nx/workspace/valid-schema-description": "error"
}
}
]
}

View File

@ -12,6 +12,7 @@
"$schema": "http://json-schema.org/schema",
"$id": "NxAngularAddLintingGenerator",
"title": "Add linting to an Angular project.",
"description": "Adds linting configuration to an Angular project.",
"cli": "nx",
"type": "object",
"properties": {
@ -61,6 +62,7 @@
"$schema": "http://json-schema.org/schema",
"$id": "GeneratorNxApp",
"title": "Creates an Angular application.",
"description": "Creates an Angular application.",
"type": "object",
"cli": "nx",
"properties": {
@ -245,7 +247,7 @@
"title": "Angular Component Schema",
"cli": "nx",
"type": "object",
"description": "Creates a new, generic component definition in the given or default project.",
"description": "Creates a new, generic Angular component definition in the given or default project.",
"additionalProperties": false,
"properties": {
"path": {
@ -355,6 +357,7 @@
"$id": "NxAngularComponentCypressSpecGenerator",
"type": "object",
"cli": "nx",
"description": "Creates a Cypress spec for a UI component that has a story.",
"properties": {
"projectName": {
"type": "string",
@ -410,6 +413,7 @@
"$id": "NxAngularComponentStoryGenerator",
"type": "object",
"cli": "nx",
"description": "Creates a `stories.ts` file for an Angular component.",
"properties": {
"projectPath": {
"type": "string",
@ -455,7 +459,7 @@
"$id": "NxAngularConvertTSLintToESLintGenerator",
"cli": "nx",
"title": "Convert an Angular project from TSLint to ESLint",
"description": "Convert an Angular project from TSLint to ESLint. NOTE: Does not work in `--dry-run mode`",
"description": "Convert an Angular project from TSLint to ESLint. NOTE: Does not work in `--dry-run mode`.",
"examples": [
{
"command": "nx g convert-tslint-to-eslint myapp",
@ -513,7 +517,7 @@
"$schema": "http://json-schema.org/schema",
"$id": "NxDowngradeModuleGenerator",
"title": "Generates downgradeModule setup.",
"description": "Sets up a Downgrade Module.",
"description": "Sets up a Downgrade Module for using AngularJS and Angular.",
"cli": "nx",
"type": "object",
"properties": {
@ -559,7 +563,7 @@
"$id": "SchematicsAngularModuleInit",
"cli": "nx",
"title": "Init Angular Plugin",
"description": "Initializes the `@nrwl/angular` plugin. NOTE: Does not work in the `--dry-run` mode",
"description": "Initializes the `@nrwl/angular` plugin. NOTE: Does not work in the `--dry-run` mode.",
"type": "object",
"properties": {
"unitTestRunner": {
@ -1087,7 +1091,7 @@
"cli": "nx",
"title": "Convert to withModuleFederation Generator Options Schema",
"type": "object",
"description": "Converts an old micro frontend configuration to use the new withModuleFederation helper. It will run successfully if the following conditions are met: \n - Is either a host or remote application \n - Shared npm package configurations have not been modified \n - Name used to identify the Micro Frontend application matches the project name \n\n _**Note:** This generator will overwrite your webpack config. If you have additional custom configuration in your config file, it will be lost!_",
"description": "Converts an old micro frontend configuration to use the new withModuleFederation helper. It will run successfully if the following conditions are met: \n - Is either a host or remote application \n - Shared npm package configurations have not been modified \n - Name used to identify the Micro Frontend application matches the project name \n\n _**Note:** This generator will overwrite your webpack config. If you have additional custom configuration in your config file, it will be lost!_.",
"additionalProperties": false,
"properties": {
"project": {
@ -1113,7 +1117,7 @@
"$id": "NxMFEHost",
"cli": "nx",
"title": "Nx MFE Host Application",
"description": "Create an Angular Host Micro Frontend Application",
"description": "Create an Angular Host Micro Frontend Application.",
"type": "object",
"examples": [
{
@ -1433,7 +1437,7 @@
"cli": "nx",
"title": "SCAM Generator Options Schema",
"type": "object",
"description": "Creates a new, generic component definition in the given or default project.",
"description": "Creates a new, generic Angular component definition in the given or default project.",
"additionalProperties": false,
"properties": {
"path": {
@ -1553,7 +1557,7 @@
"cli": "nx",
"title": "SCAM Directive Generator Options Schema",
"type": "object",
"description": "Creates a new, generic directive definition in the given or default project.",
"description": "Creates a new, generic Angular directive definition in the given or default project.",
"additionalProperties": false,
"properties": {
"path": {
@ -1626,7 +1630,7 @@
"cli": "nx",
"title": "SCAM Pipe Generator Options Schema",
"type": "object",
"description": "Creates a new, generic pipe definition in the given or default project.",
"description": "Creates a new, generic Angular pipe definition in the given or default project.",
"additionalProperties": false,
"properties": {
"path": {
@ -1798,7 +1802,7 @@
"$schema": "http://json-schema.org/schema",
"$id": "NxAngularStorybookStoriesGenerator",
"title": "Create Storybook stories/specs",
"description": "Creates Storybook stories/specs for all components declared in a project.",
"description": "Creates Storybook stories/specs for all Angular components declared in a project.",
"cli": "nx",
"type": "object",
"properties": {

View File

@ -45,7 +45,7 @@
"$id": "NxJestProject",
"cli": "nx",
"title": "Add Jest Configuration to a project",
"description": "Add Jest Configuration to a project",
"description": "Add Jest Configuration to a project.",
"type": "object",
"properties": {
"project": {

View File

@ -136,7 +136,7 @@
"$id": "NxTypescriptInit",
"cli": "nx",
"title": "Init nrwl/js",
"description": "Init generator placeholder for nrwl/js",
"description": "Init generator placeholder for nrwl/js.",
"presets": []
},
"aliases": ["lib"],

View File

@ -12,7 +12,7 @@
"$schema": "http://json-schema.org/schema",
"$id": "NxNestApplicationGenerator",
"title": "Nx Application Options Schema",
"description": "Nx Application Options Schema",
"description": "Nx Application Options Schema.",
"cli": "nx",
"type": "object",
"properties": {

View File

@ -51,6 +51,7 @@
"cli": "nx",
"$id": "NxNextApp",
"title": "Create a Next.js Application for Nx",
"description": "Create a Next.js Application for Nx.",
"examples": [
{
"command": "nx g app myapp --directory=myorg",
@ -287,6 +288,7 @@
"cli": "nx",
"$id": "NxNextReactComponent",
"title": "Create a React Component for Next",
"description": "Create a React Component for Next.",
"type": "object",
"examples": [
{
@ -560,7 +562,7 @@
"$schema": "http://json-schema.org/schema",
"cli": "nx",
"title": "Next Build",
"description": "Build a Next.js app",
"description": "Build a Next.js app.",
"type": "object",
"properties": {
"root": { "description": "The source root", "type": "string" },
@ -612,7 +614,7 @@
"schema": {
"cli": "nx",
"title": "Next Serve",
"description": "Serve a Next.js app",
"description": "Serve a Next.js app.",
"type": "object",
"properties": {
"dev": {

View File

@ -205,7 +205,6 @@
},
"rootDir": {
"type": "string",
"alias": "srcRootForCompilationRoot",
"description": "Sets the `rootDir` for TypeScript compilation. When not defined, it uses the project's root property, or `srcRootForCompilationRoot` if it is defined."
},
"testEnvironment": {

View File

@ -325,7 +325,7 @@
"cli": "nx",
"$id": "NxReactNativeStorybookConfigure",
"title": "React native Storybook configuration",
"description": "Set up Storybook for a React-Native app or library",
"description": "Set up Storybook for a React-Native app or library.",
"type": "object",
"properties": {
"name": {

View File

@ -12,7 +12,7 @@
"$schema": "http://json-schema.org/schema",
"$id": "NxReactNgInit",
"title": "Init React Plugin",
"description": "Initialize a React Plugin",
"description": "Initialize a React Plugin.",
"cli": "nx",
"type": "object",
"properties": {
@ -218,7 +218,7 @@
"cli": "nx",
"$id": "NxReactLibrary",
"title": "Create a React Library",
"description": "Create a React Library for an Nx workspace",
"description": "Create a React Library for an Nx workspace.",
"type": "object",
"examples": [
{

View File

@ -13,6 +13,7 @@
"$id": "NxWebInit",
"cli": "nx",
"title": "Init Web Plugin",
"description": "Init Web Plugin.",
"type": "object",
"properties": {
"unitTestRunner": {
@ -148,7 +149,7 @@
"implementation": "/packages/web/src/executors/webpack/webpack.impl.ts",
"schema": {
"title": "Webpack Executor",
"description": "Builds web applications using webpack",
"description": "Builds web applications using webpack.",
"cli": "nx",
"type": "object",
"properties": {

View File

@ -72,11 +72,13 @@ describe('Cli', () => {
const genHelp = runCLI(`g @nrwl/web:app --help`);
expect(genHelp).toContain(
'The file extension to be used for style files. (default: css)'
'Find more information and examples at: https://nx.dev/packages/web/generators/application'
);
const buildHelp = runCLI(`build ${myapp} --help`);
expect(buildHelp).toContain('The name of the main entry-point file.');
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');

View File

@ -3,7 +3,8 @@
"package.json": "*",
".eslintrc.json": "*",
"scripts/vercel/*": ["nx-dev"],
".circleci/config.yml": "*"
".circleci/config.yml": "*",
"tools/eslint-rules/**/*": "*"
},
"affected": {
"defaultBase": "master"

View File

@ -166,6 +166,7 @@
"jest": "27.2.3",
"jest-circus": "27.2.3",
"jest-preset-angular": "11.1.1",
"jsonc-eslint-parser": "^2.1.0",
"jsonc-parser": "3.0.0",
"karma": "~4.0.0",
"karma-chrome-launcher": "~2.2.0",

View File

@ -85,7 +85,9 @@
"packages/angular/**/*.spec.tsx",
"packages/angular/**/*.spec.js",
"packages/angular/**/*.spec.jsx",
"packages/angular/**/*.d.ts"
"packages/angular/**/*.d.ts",
"packages/angular/**/executors/**/schema.json",
"packages/angular/**/generators/**/schema.json"
]
},
"outputs": ["{options.outputFile}"]

View File

@ -2,6 +2,7 @@
"$schema": "http://json-schema.org/schema",
"$id": "NxAngularAddLintingGenerator",
"title": "Add linting to an Angular project.",
"description": "Adds linting configuration to an Angular project.",
"cli": "nx",
"type": "object",
"properties": {

View File

@ -2,6 +2,7 @@
"$schema": "http://json-schema.org/schema",
"$id": "GeneratorNxApp",
"title": "Creates an Angular application.",
"description": "Creates an Angular application.",
"type": "object",
"cli": "nx",
"properties": {

View File

@ -3,6 +3,7 @@
"$id": "NxAngularComponentCypressSpecGenerator",
"type": "object",
"cli": "nx",
"description": "Creates a Cypress spec for a UI component that has a story.",
"properties": {
"projectName": {
"type": "string",

View File

@ -3,6 +3,7 @@
"$id": "NxAngularComponentStoryGenerator",
"type": "object",
"cli": "nx",
"description": "Creates a `stories.ts` file for an Angular component.",
"properties": {
"projectPath": {
"type": "string",

View File

@ -4,7 +4,7 @@
"title": "Angular Component Schema",
"cli": "nx",
"type": "object",
"description": "Creates a new, generic component definition in the given or default project.",
"description": "Creates a new, generic Angular component definition in the given or default project.",
"additionalProperties": false,
"properties": {
"path": {

View File

@ -4,7 +4,7 @@
"cli": "nx",
"title": "Convert to withModuleFederation Generator Options Schema",
"type": "object",
"description": "Converts an old micro frontend configuration to use the new withModuleFederation helper. It will run successfully if the following conditions are met: \n - Is either a host or remote application \n - Shared npm package configurations have not been modified \n - Name used to identify the Micro Frontend application matches the project name \n\n _**Note:** This generator will overwrite your webpack config. If you have additional custom configuration in your config file, it will be lost!_",
"description": "Converts an old micro frontend configuration to use the new withModuleFederation helper. It will run successfully if the following conditions are met: \n - Is either a host or remote application \n - Shared npm package configurations have not been modified \n - Name used to identify the Micro Frontend application matches the project name \n\n _**Note:** This generator will overwrite your webpack config. If you have additional custom configuration in your config file, it will be lost!_.",
"additionalProperties": false,
"properties": {
"project": {

View File

@ -3,7 +3,7 @@
"$id": "NxAngularConvertTSLintToESLintGenerator",
"cli": "nx",
"title": "Convert an Angular project from TSLint to ESLint",
"description": "Convert an Angular project from TSLint to ESLint. NOTE: Does not work in `--dry-run mode`",
"description": "Convert an Angular project from TSLint to ESLint. NOTE: Does not work in `--dry-run mode`.",
"examples": [
{
"command": "nx g convert-tslint-to-eslint myapp",

View File

@ -2,7 +2,7 @@
"$schema": "http://json-schema.org/schema",
"$id": "NxDowngradeModuleGenerator",
"title": "Generates downgradeModule setup.",
"description": "Sets up a Downgrade Module.",
"description": "Sets up a Downgrade Module for using AngularJS and Angular.",
"cli": "nx",
"type": "object",
"properties": {

View File

@ -3,7 +3,7 @@
"$id": "SchematicsAngularModuleInit",
"cli": "nx",
"title": "Init Angular Plugin",
"description": "Initializes the `@nrwl/angular` plugin. NOTE: Does not work in the `--dry-run` mode",
"description": "Initializes the `@nrwl/angular` plugin. NOTE: Does not work in the `--dry-run` mode.",
"type": "object",
"properties": {
"unitTestRunner": {

View File

@ -3,7 +3,7 @@
"$id": "NxMFEHost",
"cli": "nx",
"title": "Nx MFE Host Application",
"description": "Create an Angular Host Micro Frontend Application",
"description": "Create an Angular Host Micro Frontend Application.",
"type": "object",
"examples": [
{

View File

@ -4,7 +4,7 @@
"cli": "nx",
"title": "SCAM Directive Generator Options Schema",
"type": "object",
"description": "Creates a new, generic directive definition in the given or default project.",
"description": "Creates a new, generic Angular directive definition in the given or default project.",
"additionalProperties": false,
"properties": {
"path": {

View File

@ -4,7 +4,7 @@
"cli": "nx",
"title": "SCAM Pipe Generator Options Schema",
"type": "object",
"description": "Creates a new, generic pipe definition in the given or default project.",
"description": "Creates a new, generic Angular pipe definition in the given or default project.",
"additionalProperties": false,
"properties": {
"path": {

View File

@ -4,7 +4,7 @@
"cli": "nx",
"title": "SCAM Generator Options Schema",
"type": "object",
"description": "Creates a new, generic component definition in the given or default project.",
"description": "Creates a new, generic Angular component definition in the given or default project.",
"additionalProperties": false,
"properties": {
"path": {

View File

@ -2,7 +2,7 @@
"$schema": "http://json-schema.org/schema",
"$id": "NxAngularStorybookStoriesGenerator",
"title": "Create Storybook stories/specs",
"description": "Creates Storybook stories/specs for all components declared in a project.",
"description": "Creates Storybook stories/specs for all Angular components declared in a project.",
"cli": "nx",
"type": "object",
"properties": {

View File

@ -71,7 +71,9 @@
"packages/cypress/**/*.spec.tsx",
"packages/cypress/**/*.spec.js",
"packages/cypress/**/*.spec.jsx",
"packages/cypress/**/*.d.ts"
"packages/cypress/**/*.d.ts",
"packages/cypress/**/executors/**/schema.json",
"packages/cypress/**/generators/**/schema.json"
]
},
"outputs": ["{options.outputFile}"]

View File

@ -12,7 +12,9 @@
"packages/detox/**/*.spec.tsx",
"packages/detox/**/*.spec.js",
"packages/detox/**/*.spec.jsx",
"packages/detox/**/*.d.ts"
"packages/detox/**/*.d.ts",
"packages/detox/**/executors/**/schema.json",
"packages/detox/**/generators/**/schema.json"
]
},
"outputs": ["{options.outputFile}"]

View File

@ -71,7 +71,9 @@
"packages/devkit/**/*.spec.tsx",
"packages/devkit/**/*.spec.js",
"packages/devkit/**/*.spec.jsx",
"packages/devkit/**/*.d.ts"
"packages/devkit/**/*.d.ts",
"packages/devkit/**/executors/**/schema.json",
"packages/devkit/**/generators/**/schema.json"
]
},
"outputs": ["{options.outputFile}"]

View File

@ -72,7 +72,9 @@
"packages/express/**/*.spec.tsx",
"packages/express/**/*.spec.js",
"packages/express/**/*.spec.jsx",
"packages/express/**/*.d.ts"
"packages/express/**/*.d.ts",
"packages/express/**/executors/**/schema.json",
"packages/express/**/generators/**/schema.json"
]
},
"outputs": ["{options.outputFile}"]

View File

@ -71,7 +71,9 @@
"packages/jest/**/*.spec.tsx",
"packages/jest/**/*.spec.js",
"packages/jest/**/*.spec.jsx",
"packages/jest/**/*.d.ts"
"packages/jest/**/*.d.ts",
"packages/jest/**/executors/**/schema.json",
"packages/jest/**/generators/**/schema.json"
]
},
"outputs": ["{options.outputFile}"]

View File

@ -3,7 +3,7 @@
"$id": "NxJestProject",
"cli": "nx",
"title": "Add Jest Configuration to a project",
"description": "Add Jest Configuration to a project",
"description": "Add Jest Configuration to a project.",
"type": "object",
"properties": {
"project": {

View File

@ -7,7 +7,11 @@
"executor": "@nrwl/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["packages/js/**/*.ts"]
"lintFilePatterns": [
"packages/js/**/*.ts",
"packages/js/**/executors/**/schema.json",
"packages/js/**/generators/**/schema.json"
]
}
},
"test": {

View File

@ -3,5 +3,5 @@
"$id": "NxTypescriptInit",
"cli": "nx",
"title": "Init nrwl/js",
"description": "Init generator placeholder for nrwl/js"
"description": "Init generator placeholder for nrwl/js."
}

View File

@ -72,7 +72,9 @@
"packages/linter/**/*.spec.tsx",
"packages/linter/**/*.spec.js",
"packages/linter/**/*.spec.jsx",
"packages/linter/**/*.d.ts"
"packages/linter/**/*.d.ts",
"packages/linter/**/executors/**/schema.json",
"packages/linter/**/generators/**/schema.json"
]
},
"outputs": ["{options.outputFile}"]

View File

@ -72,7 +72,9 @@
"packages/nest/**/*.spec.tsx",
"packages/nest/**/*.spec.js",
"packages/nest/**/*.spec.jsx",
"packages/nest/**/*.d.ts"
"packages/nest/**/*.d.ts",
"packages/nest/**/executors/**/schema.json",
"packages/nest/**/generators/**/schema.json"
]
},
"outputs": ["{options.outputFile}"]

View File

@ -2,7 +2,7 @@
"$schema": "http://json-schema.org/schema",
"$id": "NxNestApplicationGenerator",
"title": "Nx Application Options Schema",
"description": "Nx Application Options Schema",
"description": "Nx Application Options Schema.",
"cli": "nx",
"type": "object",
"properties": {

View File

@ -77,7 +77,9 @@
"packages/next/**/*.spec.tsx",
"packages/next/**/*.spec.js",
"packages/next/**/*.spec.jsx",
"packages/next/**/*.d.ts"
"packages/next/**/*.d.ts",
"packages/next/**/executors/**/schema.json",
"packages/next/**/generators/**/schema.json"
]
},
"outputs": ["{options.outputFile}"]

View File

@ -2,7 +2,7 @@
"$schema": "http://json-schema.org/schema",
"cli": "nx",
"title": "Next Build",
"description": "Build a Next.js app",
"description": "Build a Next.js app.",
"type": "object",
"properties": {
"root": {

View File

@ -1,7 +1,7 @@
{
"cli": "nx",
"title": "Next Serve",
"description": "Serve a Next.js app",
"description": "Serve a Next.js app.",
"type": "object",
"properties": {
"dev": {

View File

@ -3,6 +3,7 @@
"cli": "nx",
"$id": "NxNextApp",
"title": "Create a Next.js Application for Nx",
"description": "Create a Next.js Application for Nx.",
"examples": [
{
"command": "nx g app myapp --directory=myorg",

View File

@ -3,6 +3,7 @@
"cli": "nx",
"$id": "NxNextReactComponent",
"title": "Create a React Component for Next",
"description": "Create a React Component for Next.",
"type": "object",
"examples": [
{

View File

@ -72,7 +72,9 @@
"packages/node/**/*.spec.tsx",
"packages/node/**/*.spec.js",
"packages/node/**/*.spec.jsx",
"packages/node/**/*.d.ts"
"packages/node/**/*.d.ts",
"packages/node/**/executors/**/schema.json",
"packages/node/**/generators/**/schema.json"
]
},
"outputs": ["{options.outputFile}"]

View File

@ -79,7 +79,6 @@
},
"rootDir": {
"type": "string",
"alias": "srcRootForCompilationRoot",
"description": "Sets the `rootDir` for TypeScript compilation. When not defined, it uses the project's root property, or `srcRootForCompilationRoot` if it is defined."
},
"testEnvironment": {

View File

@ -72,7 +72,9 @@
"packages/nx-plugin/**/*.spec.tsx",
"packages/nx-plugin/**/*.spec.js",
"packages/nx-plugin/**/*.spec.jsx",
"packages/nx-plugin/**/*.d.ts"
"packages/nx-plugin/**/*.d.ts",
"packages/nx-plugin/**/executors/**/schema.json",
"packages/nx-plugin/**/generators/**/schema.json"
]
},
"outputs": ["{options.outputFile}"]

View File

@ -95,9 +95,14 @@ function readDefaultCollection(nxConfig: NxJsonConfiguration) {
return nxConfig.cli ? nxConfig.cli.defaultCollection : null;
}
export function printGenHelp(opts: GenerateOptions, schema: Schema) {
export function printGenHelp(
opts: GenerateOptions,
schema: Schema,
normalizedGeneratorName: string,
aliases: string[]
) {
printHelp(
`nx generate ${opts.collectionName}:${opts.generatorName}`,
`generate ${opts.collectionName}:${normalizedGeneratorName}`,
{
...schema,
properties: schema.properties,
@ -105,7 +110,8 @@ export function printGenHelp(opts: GenerateOptions, schema: Schema) {
{
mode: 'generate',
plugin: opts.collectionName,
entity: opts.generatorName,
entity: normalizedGeneratorName,
aliases,
}
);
}
@ -170,11 +176,11 @@ export async function generate(cwd: string, args: { [k: string]: any }) {
readDefaultCollection(workspaceDefinition),
'generate'
);
const { normalizedGeneratorName, schema, implementationFactory } =
const { normalizedGeneratorName, schema, implementationFactory, aliases } =
ws.readGenerator(opts.collectionName, opts.generatorName);
if (opts.help) {
printGenHelp(opts, schema);
printGenHelp(opts, schema, normalizedGeneratorName, aliases);
return 0;
}

View File

@ -32,7 +32,7 @@ export function printRunHelp(
schema: Schema,
plugin: { plugin: string; entity: string }
) {
printHelp(`nx run ${opts.project}:${opts.target}`, schema, {
printHelp(`run ${opts.project}:${opts.target}`, schema, {
mode: 'run',
...plugin,
});

View File

@ -164,7 +164,12 @@ export class Workspaces {
generatorConfig.implementation,
generatorsDir
);
return { normalizedGeneratorName, schema, implementationFactory };
return {
normalizedGeneratorName,
schema,
implementationFactory,
aliases: generatorConfig.aliases || [],
};
} catch (e) {
throw new Error(
`Unable to resolve ${collectionName}:${generatorName}.\n${e.message}`

View File

@ -55,6 +55,7 @@ export type Schema = {
description?: string;
definitions?: Properties;
additionalProperties?: boolean;
examples?: { command: string; description?: string }[];
};
export type Unmatched = {

View File

@ -1,24 +1,17 @@
import { Schema } from './params';
import * as chalk from 'chalk';
import { logger, stripIndent } from './logger';
function formatOption(
name: string,
description: string,
maxPropertyNameLength: number
) {
const lengthOfKey = Math.max(maxPropertyNameLength + 4, 22);
return ` --${`${name} `.slice(
0,
lengthOfKey
)}${description}`;
}
import * as stringWidth from 'string-width';
// cliui is the CLI layout engine developed by, and used within, yargs
import * as cliui from 'cliui';
import { logger } from './logger';
import { output } from './output';
import { Schema } from './params';
import { nxVersion } from './versions';
export function printHelp(
header: string,
schema: Schema,
meta:
| { mode: 'generate'; plugin: string; entity: string }
| { mode: 'generate'; plugin: string; entity: string; aliases: string[] }
| { mode: 'run'; plugin: string; entity: string }
) {
const allPositional = Object.keys(schema.properties).filter((key) => {
@ -26,56 +19,320 @@ export function printHelp(
return p['$default'] && p['$default']['$source'] === 'argv';
});
const positional = allPositional.length > 0 ? ` [${allPositional[0]}]` : '';
const maxPropertyNameLength = Object.keys(schema.properties)
.map((n) => n.length)
.reduce((a, b) => Math.max(a, b), 0);
const args = Object.keys(schema.properties)
.map((name) => {
const d = schema.properties[name];
const def = d.default ? ` (default: ${d.default})` : '';
return formatOption(
name,
`${d.description}${def}`,
maxPropertyNameLength
);
})
.join('\n');
const missingFlags =
meta.mode === 'generate'
? formatOption(
'dry-run',
'Preview the changes without updating files',
maxPropertyNameLength
logger.info(`
${output.applyNxPrefix(
'cyan',
chalk.bold(
`${`${header + chalk.reset.cyan(positional)} ${chalk.reset.cyan(
'[options,...]'
)}`}`
)
: formatOption(
'skip-nx-cache',
'Skip the use of Nx cache.',
maxPropertyNameLength
)}
${generateOverviewOutput({
pluginName: meta.plugin,
name: meta.entity,
description: schema.description,
mode: meta.mode,
aliases: meta.mode === 'generate' ? meta.aliases : [],
})}
${generateOptionsOutput(schema)}
${generateExamplesOutput(schema)}
${generateLinkOutput({
pluginName: meta.plugin,
name: meta.entity,
type: meta.mode === 'generate' ? 'generators' : 'executors',
})}
`);
}
function generateOverviewOutput({
pluginName,
name,
description,
mode,
aliases,
}: {
pluginName: string;
name: string;
description: string;
mode: 'generate' | 'run';
aliases: string[];
}): string {
switch (mode) {
case 'generate':
return generateGeneratorOverviewOutput({
pluginName,
name,
description,
aliases,
});
case 'run':
return generateExecutorOverviewOutput({
pluginName,
name,
description,
});
default:
throw new Error(`Unexpected mode ${mode}`);
}
}
function generateGeneratorOverviewOutput({
pluginName,
name,
description,
aliases,
}: {
pluginName: string;
name: string;
description: string;
aliases: string[];
}): string {
const ui = cliui();
const overviewItemsLabelWidth =
// Chars in labels "From" and "Name"
4 +
// The `:` char
1;
ui.div(
...[
{
text: chalk.bold('From:'),
padding: [1, 0, 0, 0],
width: overviewItemsLabelWidth,
},
{
text:
pluginName +
(pluginName.startsWith('@nrwl/')
? chalk.dim(` (v${nxVersion})`)
: ''),
padding: [1, 0, 0, 2],
},
]
);
let linkDescription = null;
// we need to generalize link generation so it works for non-party-class plugins as well
if (meta.mode === 'generate' && meta.plugin.startsWith('@nrwl/')) {
linkDescription = generateLink(meta.plugin, meta.entity, 'generators');
} else if (meta.mode === 'run' && meta.plugin.startsWith('@nrwl/')) {
linkDescription = generateLink(meta.plugin, meta.entity, 'executors');
}
logger.info(
stripIndent(`
${chalk.bold(`${header + positional} [options,...]`)}
${chalk.bold('Options')}:
${args}
${missingFlags}${linkDescription}
`)
ui.div(
...[
{
text: chalk.bold('Name:'),
padding: [0, 0, 0, 0],
width: overviewItemsLabelWidth,
},
{
text: `${name}${
aliases.length ? chalk.dim(` (aliases: ${aliases.join(', ')})`) : ''
}`,
padding: [0, 0, 0, 2],
},
]
);
ui.div(
...[
{
text: description,
padding: [2, 0, 1, 2],
},
]
);
return ui.toString();
}
function generateLink(plugin: string, entity: string, type: string) {
const link = `https://nx.dev/packages/${plugin.substring(
6
)}/${type}/${entity}`;
return chalk.bold(`\n\nFind more information and examples at ${link}`);
function generateExecutorOverviewOutput({
pluginName,
name,
description,
}: {
pluginName: string;
name: string;
description: string;
}): string {
const ui = cliui();
const overviewItemsLeftPadding = 2;
const overviewItemsLabelWidth = overviewItemsLeftPadding + 'Executor:'.length;
ui.div(
...[
{
text: chalk.bold('Executor:'),
padding: [1, 0, 0, 0],
width: overviewItemsLabelWidth,
},
{
text:
`${pluginName}:${name}` +
(pluginName.startsWith('@nrwl/')
? chalk.dim(` (v${nxVersion})`)
: ''),
padding: [1, 0, 0, 0],
},
]
);
ui.div(
...[
{
text: description,
padding: [2, 0, 1, 2],
},
]
);
return ui.toString();
}
const formatOptionVal = (maybeStr: unknown) =>
typeof maybeStr === 'string' ? `"${maybeStr}"` : JSON.stringify(maybeStr);
// From our JSON schemas an option could possibly have more than one valid type
const formatOptionType = (optionConfig: Schema['properties'][0]) => {
if (Array.isArray(optionConfig.oneOf)) {
return optionConfig.oneOf
.map((typeConfig) => formatOptionType(typeConfig))
.join(' OR ');
}
return `[${optionConfig.type}]`;
};
function generateOptionsOutput(schema: Schema): string {
const ui = cliui();
const flagAndAliasLeftPadding = 4;
const flagAndAliasRightPadding = 4;
// Construct option flags (including optional aliases) and descriptions and track the required space to render them
const optionsToRender = new Map<
string,
{
renderedFlagAndAlias: string;
renderedDescription: string;
renderedTypesAndDefault: string;
}
>();
let requiredSpaceToRenderAllFlagsAndAliases = 0;
for (const [optionName, optionConfig] of Object.entries(schema.properties)) {
const renderedFlagAndAlias =
`--${optionName}` +
(optionConfig.alias ? `, -${optionConfig.alias}` : '');
const renderedFlagAndAliasTrueWidth = stringWidth(renderedFlagAndAlias);
if (
renderedFlagAndAliasTrueWidth > requiredSpaceToRenderAllFlagsAndAliases
) {
requiredSpaceToRenderAllFlagsAndAliases = renderedFlagAndAliasTrueWidth;
}
const renderedDescription = optionConfig.description;
const renderedTypesAndDefault = `${formatOptionType(optionConfig)}${
optionConfig.enum
? ` [choices: ${optionConfig.enum
.map((e) => formatOptionVal(e))
.join(', ')}]`
: ''
}${
optionConfig.default
? ` [default: ${formatOptionVal(optionConfig.default)}]`
: ''
}`;
optionsToRender.set(optionName, {
renderedFlagAndAlias,
renderedDescription,
renderedTypesAndDefault,
});
}
ui.div({
text: 'Options:',
padding: [1, 0, 0, 0],
});
for (const {
renderedFlagAndAlias,
renderedDescription,
renderedTypesAndDefault,
} of optionsToRender.values()) {
const cols = [
{
text: renderedFlagAndAlias,
width:
requiredSpaceToRenderAllFlagsAndAliases +
flagAndAliasLeftPadding +
flagAndAliasRightPadding,
padding: [0, flagAndAliasRightPadding, 0, flagAndAliasLeftPadding],
},
{
text: renderedDescription,
padding: [0, 0, 0, 0],
},
{
text: renderedTypesAndDefault,
padding: [0, 0, 0, 0],
align: 'right',
},
];
ui.div(...cols);
}
return ui.toString();
}
function generateExamplesOutput(schema: Schema): string {
if (!schema.examples || schema.examples.length === 0) {
return '';
}
const ui = cliui();
const xPadding = 4;
ui.div({
text: 'Examples:',
padding: [1, 0, 0, 0],
});
for (const { command, description } of schema.examples) {
const cols = [
{
text: command,
padding: [0, xPadding, 0, xPadding],
},
{
text: description || '',
padding: [0, 2, 0, 0],
},
];
ui.div(...cols);
}
return ui.toString();
}
// TODO: generalize link generation so it works for non @nrwl plugins as well
function generateLinkOutput({
pluginName,
name,
type,
}: {
pluginName: string;
name: string;
type: 'generators' | 'executors';
}): string {
const nrwlPackagePrefix = '@nrwl/';
if (!pluginName.startsWith(nrwlPackagePrefix)) {
return '';
}
const link = `https://nx.dev/packages/${pluginName.substring(
nrwlPackagePrefix.length
)}/${type}/${name}`;
return `\n\n${chalk.dim(
'Find more information and examples at:'
)} ${chalk.bold(link)}`;
}

View File

@ -12,7 +12,9 @@
"packages/react-native/**/*.spec.tsx",
"packages/react-native/**/*.spec.js",
"packages/react-native/**/*.spec.jsx",
"packages/react-native/**/*.d.ts"
"packages/react-native/**/*.d.ts",
"packages/react-native/**/executors/**/schema.json",
"packages/react-native/**/generators/**/schema.json"
]
},
"outputs": ["{options.outputFile}"]

View File

@ -3,7 +3,7 @@
"cli": "nx",
"$id": "NxReactNativeStorybookConfigure",
"title": "React native Storybook configuration",
"description": "Set up Storybook for a React-Native app or library",
"description": "Set up Storybook for a React-Native app or library.",
"type": "object",
"properties": {
"name": {

View File

@ -82,7 +82,9 @@
"packages/react/**/*.spec.tsx",
"packages/react/**/*.spec.js",
"packages/react/**/*.spec.jsx",
"packages/react/**/*.d.ts"
"packages/react/**/*.d.ts",
"packages/react/**/executors/**/schema.json",
"packages/react/**/generators/**/schema.json"
]
},
"outputs": ["{options.outputFile}"]

View File

@ -2,7 +2,7 @@
"$schema": "http://json-schema.org/schema",
"$id": "NxReactNgInit",
"title": "Init React Plugin",
"description": "Initialize a React Plugin",
"description": "Initialize a React Plugin.",
"cli": "nx",
"type": "object",
"properties": {

View File

@ -3,7 +3,7 @@
"cli": "nx",
"$id": "NxReactLibrary",
"title": "Create a React Library",
"description": "Create a React Library for an Nx workspace",
"description": "Create a React Library for an Nx workspace.",
"type": "object",
"examples": [
{

View File

@ -82,7 +82,9 @@
"packages/storybook/**/*.spec.tsx",
"packages/storybook/**/*.spec.js",
"packages/storybook/**/*.spec.jsx",
"packages/storybook/**/*.d.ts"
"packages/storybook/**/*.d.ts",
"packages/storybook/**/executors/**/schema.json",
"packages/storybook/**/generators/**/schema.json"
]
},
"outputs": ["{options.outputFile}"]

View File

@ -77,7 +77,9 @@
"packages/web/**/*.spec.tsx",
"packages/web/**/*.spec.js",
"packages/web/**/*.spec.jsx",
"packages/web/**/*.d.ts"
"packages/web/**/*.d.ts",
"packages/web/**/executors/**/schema.json",
"packages/web/**/generators/**/schema.json"
]
},
"outputs": ["{options.outputFile}"]

View File

@ -1,6 +1,6 @@
{
"title": "Webpack Executor",
"description": "Builds web applications using webpack",
"description": "Builds web applications using webpack.",
"cli": "nx",
"type": "object",
"properties": {

View File

@ -3,6 +3,7 @@
"$id": "NxWebInit",
"cli": "nx",
"title": "Init Web Plugin",
"description": "Init Web Plugin.",
"type": "object",
"properties": {
"unitTestRunner": {

View File

@ -97,7 +97,9 @@
"packages/workspace/**/*.spec.tsx",
"packages/workspace/**/*.spec.js",
"packages/workspace/**/*.spec.jsx",
"packages/workspace/**/*.d.ts"
"packages/workspace/**/*.d.ts",
"packages/workspace/**/executors/**/schema.json",
"packages/workspace/**/generators/**/schema.json"
]
},
"outputs": ["{options.outputFile}"]

View File

@ -96,6 +96,13 @@ const IGNORE_MATCHES = {
'@angular-devkit/core',
'@angular-devkit/architect',
'@angular/cli',
/**
* cliui is the CLI layout engine developed by yargs and we want to use the version of it
* which our currently installed yargs version brings in. It in turn depends on a specific
* version of string-width which we also leverage directly in print-help.
*/
'cliui',
'string-width',
],
web: [
// we don't want to bloat the install of @nrwl/web by including @swc/core and swc-loader as a dependency.

View File

@ -0,0 +1,31 @@
import {
RULE_NAME as validSchemaDescriptionName,
rule as validSchemaDescription,
} from './rules/valid-schema-description';
/**
* Import your custom workspace rules at the top of this file.
*
* For example:
*
* import { RULE_NAME as myCustomRuleName, rule as myCustomRule } from './rules/my-custom-rule';
*
* In order to quickly get started with writing rules you can use the
* following generator command and provide your desired rule name:
*
* ```sh
* npx nx g @nrwl/linter:workspace-rule {{ NEW_RULE_NAME }}
* ```
*/
module.exports = {
/**
* Apply the imported custom rules here.
*
* For example (using the example import above):
*
* rules: {
* [myCustomRuleName]: myCustomRule
* }
*/
rules: { [validSchemaDescriptionName]: validSchemaDescription },
};

View File

@ -0,0 +1,17 @@
module.exports = {
displayName: 'eslint-rules',
preset: '../../jest.preset.js',
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json',
},
},
transform: {
'^.+\\.[tj]s$': 'ts-jest',
},
moduleFileExtensions: ['ts', 'js', 'html'],
coverageDirectory: '../../coverage/tools/eslint-rules',
moduleNameMapper: {
'@eslint/eslintrc': '@eslint/eslintrc/dist/eslintrc-universal.cjs',
},
};

View File

@ -0,0 +1,14 @@
{
"root": "tools/eslint-rules",
"sourceRoot": "tools/eslint-rules",
"targets": {
"test": {
"executor": "@nrwl/jest:jest",
"outputs": ["coverage/tools/eslint-rules"],
"options": {
"jestConfig": "tools/eslint-rules/jest.config.js",
"passWithNoTests": true
}
}
}
}

View File

@ -0,0 +1,11 @@
import { TSESLint } from '@typescript-eslint/experimental-utils';
import { rule, RULE_NAME } from './valid-schema-description';
const ruleTester = new TSESLint.RuleTester({
parser: require.resolve('@typescript-eslint/parser'),
});
ruleTester.run(RULE_NAME, rule, {
valid: [`const example = true;`],
invalid: [],
});

View File

@ -0,0 +1,81 @@
import { ESLintUtils } from '@typescript-eslint/experimental-utils';
import type { AST } from 'jsonc-eslint-parser';
// NOTE: The rule will be available in ESLint configs as "@nrwl/nx/workspace/valid-schema-description"
export const RULE_NAME = 'valid-schema-description';
export const rule = ESLintUtils.RuleCreator(() => __filename)({
name: RULE_NAME,
meta: {
type: 'problem',
docs: {
description: `Ensures that nx schemas contain valid descriptions in order to provide consistent --help output for commands`,
recommended: 'error',
},
fixable: 'code',
schema: [],
messages: {
requireSchemaDescriptionString:
'A schema description string is required in order to render --help output correctly',
validSchemaDescription:
'A schema description should end with a . character for consistency',
},
},
defaultOptions: [],
create(context) {
// jsonc-eslint-parser adds this property to parserServices where appropriate
if (!(context.parserServices as any).isJSON) {
return {};
}
return {
['JSONExpressionStatement > JSONObjectExpression'](
node: AST.JSONObjectExpression
) {
const descriptionParentJSONPropertyNode =
resolveDescriptionParentPropertyNode(node);
if (!descriptionParentJSONPropertyNode) {
context.report({
node: node as any,
messageId: 'requireSchemaDescriptionString',
});
return;
}
if (!descriptionParentJSONPropertyNode.value.value.endsWith('.')) {
context.report({
node: descriptionParentJSONPropertyNode.value as any,
messageId: 'validSchemaDescription',
fix: (fixer) => {
const [start, end] =
descriptionParentJSONPropertyNode.value.range;
return fixer.insertTextAfterRange(
[start, end - 1], // -1 to account for the closing " of the string
'.'
);
},
});
}
},
};
},
});
interface JSONPropertyWithStringLiteralValue extends AST.JSONProperty {
key: AST.JSONStringLiteral;
value: AST.JSONStringLiteral;
}
function resolveDescriptionParentPropertyNode(
node: AST.JSONObjectExpression
): JSONPropertyWithStringLiteralValue | null {
const descriptionParentJSONPropertyNode = node.properties.find((prop) => {
return (
prop.key.type === 'JSONLiteral' &&
prop.key.value === 'description' &&
prop.value.type === 'JSONLiteral' &&
typeof prop.value.value === 'string' &&
prop.value.value.length > 0
);
});
return descriptionParentJSONPropertyNode as JSONPropertyWithStringLiteralValue;
}

View File

@ -0,0 +1,16 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"module": "commonjs"
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lint.json"
},
{
"path": "./tsconfig.spec.json"
}
]
}

View File

@ -0,0 +1,9 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": ["node"]
},
"exclude": ["**/*.spec.ts"],
"include": ["**/*.ts"]
}

View File

@ -0,0 +1,9 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node"]
},
"include": ["**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"]
}

View File

@ -34,6 +34,7 @@
"e2e-workspace-create": "e2e/workspace-create",
"e2e-workspace-integrations": "e2e/workspace-integrations",
"eslint-plugin-nx": "packages/eslint-plugin-nx",
"eslint-rules": "tools/eslint-rules",
"express": "packages/express",
"jest": "packages/jest",
"js": "packages/js",

View File

@ -10706,7 +10706,7 @@ eslint@8.12.0:
text-table "^0.2.0"
v8-compile-cache "^2.0.3"
espree@^9.3.1:
espree@^9.0.0, espree@^9.3.1:
version "9.3.1"
resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.1.tgz#8793b4bc27ea4c778c19908e0719e7b8f4115bcd"
integrity sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==
@ -14608,6 +14608,16 @@ json5@^1.0.1:
dependencies:
minimist "^1.2.0"
jsonc-eslint-parser@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/jsonc-eslint-parser/-/jsonc-eslint-parser-2.1.0.tgz#4c126b530aa583d85308d0b3041ff81ce402bbb2"
integrity sha512-qCRJWlbP2v6HbmKW7R3lFbeiVWHo+oMJ0j+MizwvauqnCV/EvtAeEeuCgoc/ErtsuoKgYB8U4Ih8AxJbXoE6/g==
dependencies:
acorn "^8.5.0"
eslint-visitor-keys "^3.0.0"
espree "^9.0.0"
semver "^7.3.5"
jsonc-parser@3.0.0, jsonc-parser@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.0.0.tgz#abdd785701c7e7eaca8a9ec8cf070ca51a745a22"