/** * @license * Copyright 2019 Balena Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { flagUsages } from '@oclif/parser'; import * as ent from 'ent'; import * as _ from 'lodash'; import { getManualSortCompareFunction } from '../../lib/utils/helpers'; import { capitanoizeOclifUsage } from '../../lib/utils/oclif-utils'; import { CapitanoCommand, Category, Document, OclifCommand } from './doc-types'; import * as utils from './utils'; function renderCapitanoCommand(command: CapitanoCommand): string[] { const result = [`## ${ent.encode(command.signature)}`, command.help!]; if (!_.isEmpty(command.options)) { result.push('### Options'); for (const option of command.options!) { if (option == null) { throw new Error(`Undefined option in markdown generation!`); } if (option.description == null) { throw new Error(`Undefined option.description in markdown generation!`); } result.push( `#### ${utils.parseCapitanoOption(option)}`, option.description, ); } } return result; } function renderOclifCommand(command: OclifCommand): string[] { const result = [`## ${ent.encode(command.usage)}`]; const description = (command.description || '') .split('\n') .slice(1) // remove the first line, which oclif uses as help header .join('\n') .trim(); result.push(description); if (!_.isEmpty(command.examples)) { result.push('Examples:', command.examples!.map(v => `\t${v}`).join('\n')); } if (!_.isEmpty(command.args)) { result.push('### Arguments'); for (const arg of command.args!) { result.push(`#### ${arg.name.toUpperCase()}`, arg.description || ''); } } if (!_.isEmpty(command.flags)) { result.push('### Options'); for (const [name, flag] of Object.entries(command.flags!)) { if (name === 'help') { continue; } flag.name = name; const flagUsage = flagUsages([flag]) .map(([usage, _description]) => usage) .join() .trim(); result.push(`#### ${flagUsage}`); result.push(flag.description || ''); } } return result; } function renderCategory(category: Category): string[] { const result = [`# ${category.title}`]; for (const command of category.commands) { result.push( ...(typeof command === 'object' ? renderCapitanoCommand(command) : renderOclifCommand(command)), ); } return result; } function getAnchor(cmdSignature: string): string { return `#${_.trim(cmdSignature.replace(/\W+/g, '-'), '-').toLowerCase()}`; } function renderToc(categories: Category[]): string[] { const result = [`# CLI Command Reference`]; for (const category of categories) { result.push(`- ${category.title}`); result.push( category.commands .map(command => { const signature = typeof command === 'object' ? command.signature // Capitano : capitanoizeOclifUsage(command.usage); // oclif return `\t- [${ent.encode(signature)}](${getAnchor(signature)})`; }) .join('\n'), ); } return result; } const manualCategorySorting: { [category: string]: string[] } = { 'Environment Variables': ['envs', 'env rm', 'env add', 'env rename'], OS: [ 'os versions', 'os download', 'os build config', 'os configure', 'os initialize', ], }; function sortCommands(doc: Document): void { for (const category of doc.categories) { if (category.title in manualCategorySorting) { category.commands = category.commands.sort( getManualSortCompareFunction( manualCategorySorting[category.title], (cmd: CapitanoCommand | OclifCommand, x: string) => typeof cmd === 'object' // Capitano vs oclif command ? cmd.signature.replace(/\W+/g, ' ').includes(x) : (cmd.usage || '') .toString() .replace(/\W+/g, ' ') .includes(x), ), ); } } } export function render(doc: Document) { sortCommands(doc); const result = [ `# ${doc.title}`, doc.introduction, ...renderToc(doc.categories), ]; for (const category of doc.categories) { result.push(...renderCategory(category)); } return result.join('\n\n'); }