nx
Version:
259 lines (257 loc) • 8.82 kB
JavaScript
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;
}
;