2019-01-22 12:52:04 +00:00
|
|
|
module.exports = function processCliCommands(createDocMessage) {
|
2018-09-14 09:05:57 +00:00
|
|
|
return {
|
|
|
|
|
$runAfter: ['extra-docs-added'],
|
|
|
|
|
$runBefore: ['rendering-docs'],
|
|
|
|
|
$process(docs) {
|
|
|
|
|
const navigationDoc = docs.find(doc => doc.docType === 'navigation-json');
|
2020-08-06 16:39:06 +00:00
|
|
|
const cliCommandsNode = navigationDoc && findCliCommandsNode(navigationDoc.data['SideNav']);
|
2019-01-22 12:52:04 +00:00
|
|
|
|
2020-08-06 16:39:06 +00:00
|
|
|
if (!cliCommandsNode) {
|
2020-03-02 17:47:06 +00:00
|
|
|
throw new Error(createDocMessage(
|
|
|
|
|
'Missing `cli` url - CLI Commands must include a first child node with url set at `cli`',
|
|
|
|
|
navigationDoc));
|
2019-01-22 12:52:04 +00:00
|
|
|
}
|
2018-09-14 09:05:57 +00:00
|
|
|
|
|
|
|
|
docs.forEach(doc => {
|
|
|
|
|
if (doc.docType === 'cli-command') {
|
|
|
|
|
doc.names = collectNames(doc.name, doc.commandAliases);
|
|
|
|
|
|
|
|
|
|
// Recursively process the options
|
2020-03-02 17:49:20 +00:00
|
|
|
const optionKeywords = new Set();
|
|
|
|
|
processOptions(doc, doc.options, optionKeywords);
|
2022-03-02 08:49:57 +00:00
|
|
|
|
|
|
|
|
doc.usages = generateUsages(doc);
|
|
|
|
|
|
|
|
|
|
// Recurse if there are subcommands
|
|
|
|
|
if (doc.subcommands) {
|
|
|
|
|
doc.subcommands = Object.values(doc.subcommands);
|
|
|
|
|
doc.subcommands.forEach((subcommand) => {
|
|
|
|
|
subcommand.names = collectNames(subcommand.name, subcommand.aliases);
|
|
|
|
|
subcommand.names.forEach((name) => optionKeywords.add(name));
|
|
|
|
|
subcommand.usages = generateUsages(subcommand, doc);
|
|
|
|
|
processOptions(subcommand, subcommand.options, optionKeywords);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-02 17:49:20 +00:00
|
|
|
doc.optionKeywords = Array.from(optionKeywords).join(' ');
|
2018-09-14 09:05:57 +00:00
|
|
|
|
|
|
|
|
// Add to navigation doc
|
2020-08-06 16:39:06 +00:00
|
|
|
cliCommandsNode.children.push({url: doc.path, title: `ng ${doc.name}`});
|
2018-09-14 09:05:57 +00:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
2022-03-02 08:49:57 +00:00
|
|
|
function generateUsages(command, parentCommand) {
|
|
|
|
|
const usage = parentCommand
|
|
|
|
|
? // `ng generate <schematic>` -> `ng generate app-shell`
|
|
|
|
|
parentCommand.command.split(/ [<|[]/, 1)[0] + ' ' + command.command
|
|
|
|
|
: // `ng build [project]`
|
|
|
|
|
command.command;
|
|
|
|
|
|
|
|
|
|
// Handle required and optional command line args.
|
|
|
|
|
// Wrap them in a <var> element.
|
|
|
|
|
const commandUsageHtmlString = usage
|
|
|
|
|
.replace(/>/g, '>/var>>')
|
|
|
|
|
.replace(/</g, '<<var>')
|
|
|
|
|
.replace(/>\//g, '</')
|
|
|
|
|
.replace(/\[/g, '[<var>')
|
|
|
|
|
.replace(/\]/g, '</var>]');
|
|
|
|
|
|
|
|
|
|
// `ng build` -> `ng <span class="cli-name">build</span>`
|
|
|
|
|
return command.names.map((name) =>
|
|
|
|
|
commandUsageHtmlString.replace(
|
|
|
|
|
' ' + command.name,
|
|
|
|
|
` <span class="cli-name">${name}</span>`
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-06 16:39:06 +00:00
|
|
|
// Look for the `CLI Commands` navigation node. It is the node whose first child has `url: 'cli'`.
|
|
|
|
|
// (NOTE: Using the URL instead of the title, because it is more robust.)
|
|
|
|
|
function findCliCommandsNode(nodes) {
|
|
|
|
|
// We will "recursively" check all navigation nodes and their children (in breadth-first order),
|
|
|
|
|
// until we find the `CLI Commands` node. Keep a list of nodes lists to check.
|
|
|
|
|
// (NOTE: Each item in the list is a LIST of nodes.)
|
|
|
|
|
const nodesList = [nodes];
|
|
|
|
|
|
|
|
|
|
while (nodesList.length > 0) {
|
|
|
|
|
// Get the first item from the list of nodes lists.
|
|
|
|
|
const currentNodes = nodesList.shift();
|
|
|
|
|
const cliCommandsNode = currentNodes.find(isCliCommandsNode);
|
|
|
|
|
|
|
|
|
|
// One of the nodes in `currentNodes` was the `CLI Commands` node. Return it.
|
|
|
|
|
if (cliCommandsNode) return cliCommandsNode;
|
|
|
|
|
|
|
|
|
|
// The `CLI Commands` node is not in `currentNodes`. Check each node's children (if any).
|
|
|
|
|
currentNodes.forEach(node => node.children && nodesList.push(node.children));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We checked all navigation nodes and their children and did not find the `CLI Commands` node.
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isCliCommandsNode(node) {
|
|
|
|
|
return node.children && node.children.length && node.children[0].url === 'cli';
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-02 17:49:20 +00:00
|
|
|
function processOptions(container, options, optionKeywords) {
|
2018-09-14 09:05:57 +00:00
|
|
|
container.positionalOptions = [];
|
|
|
|
|
container.namedOptions = [];
|
|
|
|
|
|
|
|
|
|
options.forEach(option => {
|
|
|
|
|
// Ignore any hidden options
|
2020-03-02 17:47:06 +00:00
|
|
|
if (option.hidden) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2018-09-14 09:05:57 +00:00
|
|
|
|
|
|
|
|
option.types = option.types || [option.type];
|
|
|
|
|
option.names = collectNames(option.name, option.aliases);
|
2020-03-02 17:49:20 +00:00
|
|
|
option.names.forEach(name => optionKeywords.add(name));
|
2018-09-14 09:05:57 +00:00
|
|
|
|
|
|
|
|
// Now work out what kind of option it is: positional/named
|
|
|
|
|
if (option.positional !== undefined) {
|
|
|
|
|
container.positionalOptions[option.positional] = option;
|
|
|
|
|
} else {
|
|
|
|
|
container.namedOptions.push(option);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
container.namedOptions.sort((a, b) => a.name > b.name ? 1 : -1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function collectNames(name, aliases) {
|
2020-03-02 17:49:20 +00:00
|
|
|
return [name].concat(aliases || []);
|
2018-09-14 09:05:57 +00:00
|
|
|
}
|