nx/scripts/documentation/generate-generators-data.ts
Phillip Barta c0ce1ce65e
cleanup(core): normalized usage of fs-extra and updated fs-extra to 9.1.0 (#5199)
* cleanup(core): normalized usage of fs-extra and updated fs-extra

* cleanup(misc): use fs over fs-extra when possible

Co-authored-by: Jason Jean <jasonjean1993@gmail.com>
2021-04-22 12:52:52 -04:00

227 lines
6.8 KiB
TypeScript

import { removeSync, readJsonSync } from 'fs-extra';
import * as chalk from 'chalk';
import * as path from 'path';
import { dedent } from 'tslint/lib/utils';
import { FileSystemSchematicJsonDescription } from '@angular-devkit/schematics/tools';
import { CoreSchemaRegistry } from '@angular-devkit/core/src/json/schema';
import {
htmlSelectorFormat,
pathFormat,
} from '@angular-devkit/schematics/src/formats';
import {
generateJsonFile,
generateMarkdownFile,
sortAlphabeticallyFunction,
} from './utils';
import {
Configuration,
getPackageConfigurations,
} from './get-package-configurations';
import { Framework } from './frameworks';
import { parseJsonSchemaToOptions } from './json-parser';
/**
* @WhatItDoes: Generates default documentation from the schematics' schema.
* We need to construct an Array of objects containing all the information
* of the schematics and their associates schema info. It should be easily
* parsable in order to be used in a rendering process using template. This
* in order to generate a markdown file for each available schematic.
*/
const registry = new CoreSchemaRegistry();
registry.addFormat(pathFormat);
registry.addFormat(htmlSelectorFormat);
function generateSchematicList(
config: Configuration,
registry: CoreSchemaRegistry
): Promise<FileSystemSchematicJsonDescription>[] {
const schematicCollectionFile = path.join(config.root, 'collection.json');
removeSync(config.schematicOutput);
const schematicCollection = readJsonSync(schematicCollectionFile).schematics;
return Object.keys(schematicCollection).map((schematicName) => {
const schematic = {
name: schematicName,
collectionName: `@nrwl/${config.name}`,
...schematicCollection[schematicName],
alias: schematicCollection[schematicName].hasOwnProperty('aliases')
? schematicCollection[schematicName]['aliases'][0]
: null,
rawSchema: readJsonSync(
path.join(config.root, schematicCollection[schematicName]['schema'])
),
};
return parseJsonSchemaToOptions(registry, schematic.rawSchema)
.then((options) => ({ ...schematic, options }))
.catch((error) =>
console.error(
`Can't parse schema option of ${schematic.name}:\n${error}`
)
);
});
}
function generateTemplate(
framework: Framework,
schematic
): { name: string; template: string } {
const cliCommand = 'nx';
const filename = framework === 'angular' ? 'angular.json' : 'workspace.json';
let template = dedent`
# ${schematic.name} ${schematic.hidden ? '[hidden]' : ''}
${schematic.description}
## Usage
\`\`\`bash
${cliCommand} generate ${schematic.name} ...
\`\`\`
`;
if (schematic.alias) {
template += dedent`
\`\`\`bash
${cliCommand} g ${schematic.alias} ... # same
\`\`\`
`;
}
template += dedent`
By default, Nx will search for \`${schematic.name}\` in the default collection provisioned in \`${filename}\`.\n
You can specify the collection explicitly as follows:
\`\`\`bash
${cliCommand} g ${schematic.collectionName}:${schematic.name} ...
\`\`\`
`;
template += dedent`
Show what will be generated without writing to disk:
\`\`\`bash
${cliCommand} g ${schematic.name} ... --dry-run
\`\`\`\n
`;
if (schematic.rawSchema.examples) {
template += `### Examples`;
schematic.rawSchema.examples.forEach((example) => {
template += dedent`
${example.description}:
\`\`\`bash
${cliCommand} ${example.command}
\`\`\`
`;
});
}
if (Array.isArray(schematic.options) && !!schematic.options.length) {
template += '## Options';
schematic.options
.sort((a, b) => sortAlphabeticallyFunction(a.name, b.name))
.forEach((option) => {
let enumValues = [];
const rawSchemaProp = schematic.rawSchema.properties[option.name];
if (
rawSchemaProp &&
rawSchemaProp['x-prompt'] &&
rawSchemaProp['x-prompt'].items
) {
rawSchemaProp['x-prompt'].items.forEach((p) => {
enumValues.push(`\`${p.value}\``);
});
} else if (option.enum) {
enumValues = option.enum.map((e) => `\`${e}\``);
}
const enumStr =
enumValues.length > 0
? `Possible values: ${enumValues.join(', ')}`
: ``;
template += dedent`
### ${option.name} ${option.required ? '(*__required__*)' : ''} ${
option.hidden ? '(__hidden__)' : ''
}
${
!!option.aliases.length
? `Alias(es): ${option.aliases.join(',')}\n`
: ''
}
${
option.default === undefined || option.default === ''
? ''
: `Default: \`${option.default}\`\n`
}
Type: \`${option.type}\`
${enumStr}
${option.description}
`;
});
}
return { name: schematic.name, template };
}
export async function generateGeneratorsDocumentation() {
console.log(`\n${chalk.blue('i')} Generating Documentation for Generators\n`);
await Promise.all(
getPackageConfigurations().map(({ framework, configs }) => {
return Promise.all(
configs
.filter((item) => item.hasSchematics)
.map(async (config) => {
const schematicList = await Promise.all(
generateSchematicList(config, registry)
);
const markdownList = schematicList
.filter((s) => !s['hidden'])
.map((s_1) => generateTemplate(framework, s_1));
await Promise.all(
markdownList.map((template) =>
generateMarkdownFile(config.schematicOutput, template)
)
);
console.log(
` - ${chalk.blue(
config.framework
)} Documentation for ${chalk.magenta(
path.relative(process.cwd(), config.root)
)} generated at ${chalk.grey(
path.relative(process.cwd(), config.schematicOutput)
)}`
);
})
);
})
);
console.log();
await Promise.all(
getPackageConfigurations().map(({ framework, configs }) => {
const schematics = configs
.filter((item) => item.hasSchematics)
.map((item) => item.name);
return generateJsonFile(
path.join(__dirname, '../../docs', framework, 'generators.json'),
schematics
).then(() => {
console.log(
`${chalk.green('🗸')} Generated ${chalk.blue(
framework
)} generators.json at ${chalk.grey(
`docs/${framework}/generators.json`
)}`
);
});
})
);
console.log(`\n${chalk.green('🗸')} Generated Documentation for Generators`);
}