UNPKG

nx

Version:

The core Nx plugin contains the core functionality of Nx like the project graph, nx commands and task orchestration.

259 lines (257 loc) • 8.82 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.printHelp = printHelp; const chalk = require("chalk"); const stringWidth = require("string-width"); const logger_1 = require("./logger"); const output_1 = require("./output"); const versions_1 = require("./versions"); const package_json_1 = require("./package-json"); // cliui is the CLI layout engine developed by, and used within, yargs // the typings for cliui do not play nice with our tsconfig, it either // works in build or in test but not both. const cliui = require('cliui'); function printHelp(header, schema, meta) { const allPositional = Object.keys(schema.properties).filter((key) => { const p = schema.properties[key]; return p['$default'] && p['$default']['$source'] === 'argv'; }); const positional = allPositional.length > 0 ? ` [${allPositional[0]}]` : ''; logger_1.logger.info(` ${output_1.output.applyNxPrefix('cyan', chalk.bold(`${`${header + chalk.reset.cyan(positional)} ${chalk.reset.cyan('[options,...]')}`}`))} ${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, }) { 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, }) { const ui = cliui(null); const overviewItemsLabelWidth = // Chars in labels "From" and "Name" 4 + // The `:` char 1; let installedVersion; try { installedVersion = (0, package_json_1.readModulePackageJson)(pluginName).packageJson.version; } catch { } ui.div(...[ { text: chalk.bold('From:'), padding: [1, 0, 0, 0], width: overviewItemsLabelWidth, }, { text: pluginName + (installedVersion ? chalk.dim(` (v${installedVersion})`) : ''), padding: [1, 0, 0, 2], }, ]); 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 generateExecutorOverviewOutput({ pluginName, name, description, }) { const ui = cliui(null); 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('@nx/') ? chalk.dim(` (v${versions_1.nxVersion})`) : ''), padding: [1, 0, 0, 0], }, ]); ui.div(...[ { text: description, padding: [2, 0, 1, 2], }, ]); return ui.toString(); } const formatOptionVal = (maybeStr) => typeof maybeStr === 'string' ? `"${maybeStr}"` : JSON.stringify(maybeStr); // From our JSON schemas an option could possibly have more than one valid type const formatOptionType = (optionConfig) => { if (Array.isArray(optionConfig.oneOf)) { return optionConfig.oneOf .map((typeConfig) => formatOptionType(typeConfig)) .join(' OR '); } return `[${optionConfig.type}]`; }; function generateOptionsOutput(schema) { const ui = cliui(null); 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(); let requiredSpaceToRenderAllFlagsAndAliases = 0; const sorted = Object.entries(schema.properties).sort((a, b) => compareByPriority(a, b, schema)); for (const [optionName, optionConfig] of sorted) { 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)}]` : ''}`; optionConfig.hidden ??= optionConfig.visible === false; if (!optionConfig.hidden) 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) { if (!schema.examples || schema.examples.length === 0) { return ''; } const ui = cliui(null); 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 @nx plugins as well function generateLinkOutput({ pluginName, name, type, }) { const nxPackagePrefix = '@nx/'; if (!pluginName.startsWith(nxPackagePrefix)) { return ''; } const link = `https://nx.dev/nx-api/${pluginName.substring(nxPackagePrefix.length)}/${type}/${name}`; return `\n\n${chalk.dim('Find more information and examples at:')} ${chalk.bold(link)}`; } /** * sorts properties in the following order * - required * - x-priority: important * - everything else * - x-priority: internal * - deprecated * if two properties have equal priority, they are sorted by name */ function compareByPriority(a, b, schema) { function getPriority([name, property]) { if (schema.required?.includes(name)) { return 0; } if (property['x-priority'] === 'important') { return 1; } if (property['x-deprecated']) { return 4; } if (property['x-priority'] === 'internal') { return 3; } return 2; } const aPriority = getPriority(a); const bPriority = getPriority(b); if (aPriority === bPriority) { return a[0].localeCompare(b[0]); } return aPriority - bPriority; }