feat(misc): updated documentation generation scripts and removed shelljs (#5381)

cleanup(repo): updated documentation generation scripts and removed shelljs
This commit is contained in:
Phillip Barta 2021-04-19 21:26:17 +02:00 committed by GitHub
parent 6bd6e1485f
commit 27df60164d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 378 additions and 275 deletions

View File

@ -22,7 +22,7 @@
"lint": "nx run-many --target=lint --all --parallel", "lint": "nx run-many --target=lint --all --parallel",
"depcheck": "ts-node -P ./scripts/tsconfig.scripts.json ./scripts/depcheck", "depcheck": "ts-node -P ./scripts/tsconfig.scripts.json ./scripts/depcheck",
"local-registry": "./scripts/local-registry.sh", "local-registry": "./scripts/local-registry.sh",
"documentation": "./scripts/documentation/documentation.sh && ./scripts/documentation/check-documentation.sh && yarn check-documentation-map && yarn check-internal-links", "documentation": "ts-node -P scripts/tsconfig.scripts.json ./scripts/documentation/documentation.ts && yarn check-documentation-map && yarn check-internal-links",
"submit-plugin": "node ./scripts/submit-plugin.js" "submit-plugin": "node ./scripts/submit-plugin.js"
}, },
"devDependencies": { "devDependencies": {
@ -209,7 +209,6 @@
"sass": "1.26.3", "sass": "1.26.3",
"sass-loader": "8.0.2", "sass-loader": "8.0.2",
"semver": "7.3.4", "semver": "7.3.4",
"shelljs": "^0.8.3",
"source-map": "0.7.3", "source-map": "0.7.3",
"source-map-loader": "0.2.4", "source-map-loader": "0.2.4",
"source-map-support": "0.5.16", "source-map-support": "0.5.16",

View File

@ -16,8 +16,8 @@ import { join } from 'path';
import { gt } from 'semver'; import { gt } from 'semver';
import * as chalk from 'chalk'; import * as chalk from 'chalk';
import { dasherize } from '../packages/workspace/src/utils/strings'; import { dasherize } from '../packages/workspace/src/utils/strings';
import * as shell from 'shelljs';
import * as glob from 'glob'; import * as glob from 'glob';
import { execSync } from 'child_process';
const excluded = ['nxVersion']; const excluded = ['nxVersion'];
const scoped = [ const scoped = [
@ -123,7 +123,9 @@ function getVersionData(
} { } {
try { try {
const latest = JSON.parse( const latest = JSON.parse(
shell.exec(`npm view ${p} version --json --silent`, { silent: true }) execSync(`npm view ${p} version --json --silent`, {
stdio: ['ignore'],
}).toString('utf-8')
); );
if (gt(latest, v)) { if (gt(latest, v)) {
return { package: p, outdated: true, invalid: false, latest, prev: v }; return { package: p, outdated: true, invalid: false, latest, prev: v };

View File

@ -1,3 +1,3 @@
const shell = require('shelljs'); const fs = require('fs');
shell.chmod('+x', process.argv[2]); fs.chmodSync(process.argv[2], 0o777);

View File

@ -1,10 +0,0 @@
#!/usr/bin/env bash
if [ -z "$(git status --porcelain ./docs)" ]; then
echo "📄 Documentation not modified";
exit 0;
else
echo "📄 Documentation has been modified, you need to commit the changes.";
git status --porcelain ./docs
exit 1;
fi

View File

@ -1,9 +0,0 @@
#!/usr/bin/env bash
set -e
echo "Generating API documentation"
ts-node -r tsconfig-paths/register --project=scripts/tsconfig.scripts.json ./scripts/documentation/generate-executors-data.ts
ts-node -r tsconfig-paths/register --project=scripts/tsconfig.scripts.json ./scripts/documentation/generate-generators-data.ts
ts-node -r tsconfig-paths/register --project=scripts/tsconfig.scripts.json ./scripts/documentation/generate-cli-data.ts
echo 'Done generating all Documentation'

View File

@ -0,0 +1,51 @@
import * as chalk from 'chalk';
import { execSync } from 'child_process';
import { generateCLIDocumentation } from './generate-cli-data';
import { generateExecutorsDocumentation } from './generate-executors-data';
import { generateGeneratorsDocumentation } from './generate-generators-data';
async function generate() {
console.log(`${chalk.blue('i')} Generating Documentation`);
await generateGeneratorsDocumentation();
await generateExecutorsDocumentation();
await generateCLIDocumentation();
console.log(`\n${chalk.green('🗸')} Generated Documentation\n`);
}
function checkDocumentation() {
const output = execSync('git status --porcelain ./docs').toString('utf-8');
if (output) {
console.log(
`${chalk.red(
'!'
)} 📄 Documentation has been modified, you need to commit the changes. ${chalk.red(
'!'
)} `
);
console.log('\nChanged Docs:');
execSync('git status --porcelain ./docs', { stdio: 'inherit' });
process.exit(1);
} else {
console.log('📄 Documentation not modified');
}
}
generate().then(() => {
checkDocumentation();
});
function printInfo(
str: string,
newLine: boolean = true,
newLineAfter: boolean = true
) {
console.log(
`${newLine ? '\n' : ''}${chalk.blue('i')} ${str}${newLineAfter ? '\n' : ''}`
);
}

View File

@ -0,0 +1,2 @@
export const Frameworks = ['angular', 'react', 'node'] as const;
export type Framework = typeof Frameworks[number];

View File

@ -1,9 +1,10 @@
import * as chalk from 'chalk';
import * as fs from 'fs-extra'; import * as fs from 'fs-extra';
import * as path from 'path'; import * as path from 'path';
import { dedent } from 'tslint/lib/utils'; import { dedent } from 'tslint/lib/utils';
import { commandsObject } from '../../packages/workspace'; import { commandsObject } from '../../packages/workspace';
import { Framework, Frameworks } from './frameworks';
import { generateMarkdownFile, sortAlphabeticallyFunction } from './utils'; import { generateMarkdownFile, sortAlphabeticallyFunction } from './utils';
const importFresh = require('import-fresh'); const importFresh = require('import-fresh');
const examples = { const examples = {
@ -395,55 +396,59 @@ const sharedCommands = [
'test', 'test',
]; ];
console.log('Generating Nx Commands Documentation'); export async function generateCLIDocumentation() {
Promise.all( console.log(`\n${chalk.blue('i')} Generating Documentation for Nx Commands`);
['angular', 'react', 'node'].map(async (framework) => {
const commandsOutputDirectory = path.join( await Promise.all(
__dirname, Frameworks.map(async (framework: Framework) => {
'../../docs/', const commandsOutputDirectory = path.join(
framework, __dirname,
'cli' '../../docs/',
); framework,
fs.removeSync(commandsOutputDirectory); 'cli'
function getCommands(command) { );
return command.getCommandInstance().getCommandHandlers(); fs.removeSync(commandsOutputDirectory);
} function getCommands(command) {
async function parseCommandInstance(name, command) { return command.getCommandInstance().getCommandHandlers();
// It is not a function return a strip down version of the command }
if ( async function parseCommandInstance(name, command) {
!( // It is not a function return a strip down version of the command
command.builder && if (
command.builder.constructor && !(
command.builder.call && command.builder &&
command.builder.apply command.builder.constructor &&
) command.builder.call &&
) { command.builder.apply
)
) {
return {
command: name,
description: command['description'],
};
}
// Show all the options we can get from yargs
const builder = await command.builder(
importFresh('yargs')().resetOptions()
);
const builderDescriptions = builder
.getUsageInstance()
.getDescriptions();
const builderDefaultOptions = builder.getOptions().default;
return { return {
command: name, command: name,
description: command['description'], description: command['description'],
options:
Object.keys(builderDescriptions).map((key) => ({
command: '--'.concat(key),
description: builderDescriptions[key]
? builderDescriptions[key].replace('__yargsString__:', '')
: '',
default: builderDefaultOptions[key],
})) || null,
}; };
} }
// Show all the options we can get from yargs function generateMarkdown(command) {
const builder = await command.builder( let template = dedent`
importFresh('yargs')().resetOptions()
);
const builderDescriptions = builder.getUsageInstance().getDescriptions();
const builderDefaultOptions = builder.getOptions().default;
return {
command: name,
description: command['description'],
options:
Object.keys(builderDescriptions).map((key) => ({
command: '--'.concat(key),
description: builderDescriptions[key]
? builderDescriptions[key].replace('__yargsString__:', '')
: '',
default: builderDefaultOptions[key],
})) || null,
};
}
function generateMarkdown(command) {
let template = dedent`
# ${command.command} # ${command.command}
${command.description} ${command.description}
@ -454,31 +459,31 @@ Promise.all(
Install \`nx\` globally to invoke the command directly using \`nx\`, or use \`npm run nx\` or \`yarn nx\`.\n`; Install \`nx\` globally to invoke the command directly using \`nx\`, or use \`npm run nx\` or \`yarn nx\`.\n`;
if (examples[command.command] && examples[command.command].length > 0) { if (examples[command.command] && examples[command.command].length > 0) {
template += `### Examples`; template += `### Examples`;
examples[command.command].forEach((example) => { examples[command.command].forEach((example) => {
template += dedent` template += dedent`
${example.description}: ${example.description}:
\`\`\`bash \`\`\`bash
nx ${example.command} nx ${example.command}
\`\`\` \`\`\`
`; `;
}); });
} }
if (Array.isArray(command.options) && !!command.options.length) { if (Array.isArray(command.options) && !!command.options.length) {
template += '\n## Options'; template += '\n## Options';
command.options command.options
.sort((a, b) => .sort((a, b) =>
sortAlphabeticallyFunction( sortAlphabeticallyFunction(
a.command.replace('--', ''), a.command.replace('--', ''),
b.command.replace('--', '') b.command.replace('--', '')
)
) )
) .forEach(
.forEach( (option) =>
(option) => (template += dedent`
(template += dedent`
### ${option.command.replace('--', '')} ### ${option.command.replace('--', '')}
${ ${
option.default === undefined || option.default === '' option.default === undefined || option.default === ''
@ -487,56 +492,57 @@ Promise.all(
} }
${option.description} ${option.description}
`) `)
); );
}
return {
name: command.command
.replace(':', '-')
.replace(' ', '-')
.replace(/[\]\[.]+/gm, ''),
template,
};
} }
return { // TODO: Try to add option's type, examples, and group?
name: command.command const nxCommands = getCommands(commandsObject);
.replace(':', '-') await Promise.all(
.replace(' ', '-') Object.keys(nxCommands)
.replace(/[\]\[.]+/gm, ''), .filter((name) => !sharedCommands.includes(name))
template, .map((name) => parseCommandInstance(name, nxCommands[name]))
}; .map(async (command) => generateMarkdown(await command))
} .map(async (templateObject) =>
generateMarkdownFile(commandsOutputDirectory, await templateObject)
)
);
// TODO: Try to add option's type, examples, and group? await Promise.all(
const nxCommands = getCommands(commandsObject); sharedCommands.map((command) => {
await Promise.all( const sharedCommandsDirectory = path.join(
Object.keys(nxCommands) __dirname,
.filter((name) => !sharedCommands.includes(name)) '../../docs/shared/cli'
.map((name) => parseCommandInstance(name, nxCommands[name])) );
.map(async (command) => generateMarkdown(await command)) const sharedCommandsOutputDirectory = path.join(
.map(async (templateObject) => __dirname,
generateMarkdownFile(commandsOutputDirectory, await templateObject) '../../docs/',
) framework,
); 'cli'
);
const templateObject = {
name: command,
template: fs
.readFileSync(path.join(sharedCommandsDirectory, `${command}.md`))
.toString('utf-8'),
};
await Promise.all( return generateMarkdownFile(
sharedCommands.map((command) => { sharedCommandsOutputDirectory,
const sharedCommandsDirectory = path.join( templateObject
__dirname, );
'../../docs/shared/cli' })
); );
const sharedCommandsOutputDirectory = path.join( })
__dirname, );
'../../docs/',
framework,
'cli'
);
const templateObject = {
name: command,
template: fs
.readFileSync(path.join(sharedCommandsDirectory, `${command}.md`))
.toString('utf-8'),
};
return generateMarkdownFile( console.log(`${chalk.green('🗸')} Generated Documentation for Nx Commands`);
sharedCommandsOutputDirectory, }
templateObject
);
})
);
})
).then(() => {
console.log('Finished generating Nx Commands Documentation');
});

View File

@ -18,6 +18,8 @@ import {
Configuration, Configuration,
getPackageConfigurations, getPackageConfigurations,
} from './get-package-configurations'; } from './get-package-configurations';
import { Framework } from './frameworks';
import * as chalk from 'chalk';
/** /**
* @WhatItDoes: Generates default documentation from the builders' schema. * @WhatItDoes: Generates default documentation from the builders' schema.
@ -69,7 +71,7 @@ function generateSchematicList(
} }
function generateTemplate( function generateTemplate(
framework, framework: Framework,
builder builder
): { name: string; template: string } { ): { name: string; template: string } {
const filename = framework === 'angular' ? 'angular.json' : 'workspace.json'; const filename = framework === 'angular' ? 'angular.json' : 'workspace.json';
@ -160,41 +162,62 @@ function generateTemplate(
return { name: builder.name, template }; return { name: builder.name, template };
} }
Promise.all( export async function generateExecutorsDocumentation() {
getPackageConfigurations().map(({ framework, configs }) => { console.log(`\n${chalk.blue('i')} Generating Documentation for Executors\n`);
return Promise.all(
configs await Promise.all(
.filter((item) => item.hasBuilders) getPackageConfigurations().map(({ framework, configs }) => {
.map((config) => { return Promise.all(
Promise.all(generateSchematicList(config, registry)) configs
.then((builderList) => .filter((item) => item.hasBuilders)
builderList.map((b) => generateTemplate(framework, b)) .map(async (config) => {
) const buildersList = await Promise.all(
.then((markdownList) => generateSchematicList(config, registry)
Promise.all( );
markdownList.map((template) =>
generateMarkdownFile(config.builderOutput, template) const markdownList = buildersList.map((b) =>
) generateTemplate(framework, b)
) );
)
.then(() => await Promise.all(
console.log( markdownList.map((template) =>
`Generated documentation for ${config.root} to ${config.output}` generateMarkdownFile(config.builderOutput, template)
) )
); );
})
);
})
).then(() => {
console.log('Done generating documentation for executors');
});
getPackageConfigurations().forEach(async ({ framework, configs }) => { console.log(
const builders = configs ` - ${chalk.blue(
.filter((item) => item.hasBuilders) config.framework
.map((item) => item.name); )} Documentation for ${chalk.magenta(
await generateJsonFile( path.relative(process.cwd(), config.root)
path.join(__dirname, '../../docs', framework, 'executors.json'), )} generated at ${chalk.grey(
builders path.relative(process.cwd(), config.builderOutput)
)}`
);
})
);
})
); );
});
console.log();
await Promise.all(
getPackageConfigurations().map(async ({ framework, configs }) => {
const builders = configs
.filter((item) => item.hasBuilders)
.map((item) => item.name);
await generateJsonFile(
path.join(__dirname, '../../docs', framework, 'executors.json'),
builders
);
console.log(
`${chalk.green('🗸')} Generated ${chalk.blue(
framework
)} executors.json at ${chalk.grey(`docs/${framework}/executors.json`)}`
);
})
);
console.log(`\n${chalk.green('🗸')} Generated Documentation for Executors`);
}

View File

@ -1,4 +1,5 @@
import * as fs from 'fs-extra'; import * as fs from 'fs-extra';
import * as chalk from 'chalk';
import * as path from 'path'; import * as path from 'path';
import { dedent } from 'tslint/lib/utils'; import { dedent } from 'tslint/lib/utils';
import { FileSystemSchematicJsonDescription } from '@angular-devkit/schematics/tools'; import { FileSystemSchematicJsonDescription } from '@angular-devkit/schematics/tools';
@ -16,6 +17,7 @@ import {
Configuration, Configuration,
getPackageConfigurations, getPackageConfigurations,
} from './get-package-configurations'; } from './get-package-configurations';
import { Framework } from './frameworks';
import { parseJsonSchemaToOptions } from './json-parser'; import { parseJsonSchemaToOptions } from './json-parser';
/** /**
@ -61,7 +63,7 @@ function generateSchematicList(
} }
function generateTemplate( function generateTemplate(
framework: string, framework: Framework,
schematic schematic
): { name: string; template: string } { ): { name: string; template: string } {
const cliCommand = 'nx'; const cliCommand = 'nx';
@ -163,43 +165,63 @@ function generateTemplate(
return { name: schematic.name, template }; return { name: schematic.name, template };
} }
Promise.all( export async function generateGeneratorsDocumentation() {
getPackageConfigurations().map(({ framework, configs }) => { console.log(`\n${chalk.blue('i')} Generating Documentation for Generators\n`);
return Promise.all(
configs
.filter((item) => item.hasSchematics)
.map((config) => {
return Promise.all(generateSchematicList(config, registry))
.then((schematicList) => {
return schematicList
.filter((s) => !s['hidden'])
.map((s) => generateTemplate(framework, s));
})
.then((markdownList) =>
Promise.all(
markdownList.map((template) =>
generateMarkdownFile(config.schematicOutput, template)
)
)
)
.then(() => {
console.log(
`Documentation from ${config.root} generated to ${config.schematicOutput}`
);
});
})
);
})
).then(() => {
console.log('Finished Generating Documentation for Generators');
});
getPackageConfigurations().forEach(async ({ framework, configs }) => { await Promise.all(
const schematics = configs getPackageConfigurations().map(({ framework, configs }) => {
.filter((item) => item.hasSchematics) return Promise.all(
.map((item) => item.name); configs
await generateJsonFile( .filter((item) => item.hasSchematics)
path.join(__dirname, '../../docs', framework, 'generators.json'), .map(async (config) => {
schematics 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`);
}

View File

@ -1,15 +1,17 @@
import * as glob from 'glob';
import * as path from 'path'; import * as path from 'path';
import * as shelljs from 'shelljs'; import { Framework, Frameworks } from './frameworks';
export interface Configuration { export interface Configuration {
name: string; name: string;
root: string; root: string;
framework: Framework;
source: string; source: string;
output: string; output: string;
builderOutput: string; builderOutput: string;
schematicOutput: string; schematicOutput: string;
hasBuilders: string; hasBuilders: boolean;
hasSchematics: string; hasSchematics: boolean;
} }
/** /**
@ -21,37 +23,43 @@ export interface Configuration {
export function getPackageConfigurations( export function getPackageConfigurations(
packagesDirectory: string = 'packages', packagesDirectory: string = 'packages',
documentationsDirectory: string = 'docs' documentationsDirectory: string = 'docs'
): { framework: 'angular' | 'react' | 'node'; configs: Configuration[] }[] { ): { framework: Framework; configs: Configuration[] }[] {
return ['angular', 'react', 'node'].map((framework) => { return Frameworks.map((framework: Framework) => {
const packagesDir = path.resolve( const packagesDir = path.resolve(
path.join(__dirname, '../../', packagesDirectory) path.join(__dirname, '../../', packagesDirectory)
); );
const documentationDir = path.resolve( const documentationDir = path.resolve(
path.join(__dirname, '../../', documentationsDirectory) path.join(__dirname, '../../', documentationsDirectory)
); );
const configs = shelljs.ls(packagesDir).map((folderName) => {
const itemList = shelljs.ls(path.join(packagesDir, folderName)); const configs = glob.sync(`${packagesDir}/*`).map(
const output = path.join( (folderPath): Configuration => {
documentationDir, const folderName = folderPath.substring(packagesDir.length + 1);
framework, const itemList = glob
`api-${folderName}` .sync(`${folderPath}/*`)
); .map((item) => item.split(folderPath + '/')[1]);
return { const output = path.join(
name: folderName, documentationDir,
root: path.join(packagesDir, folderName), framework,
source: path.join(packagesDir, `${folderName}/src`), `api-${folderName}`
output, );
framework, return {
builderOutput: path.join(output, 'executors'), name: folderName,
schematicOutput: path.join(output, 'generators'), root: folderPath,
hasBuilders: source: path.join(folderPath, '/src'),
itemList.includes('builders.json') || output,
itemList.includes('executors.json'), framework,
hasSchematics: builderOutput: path.join(output, 'executors'),
itemList.includes('collection.json') || schematicOutput: path.join(output, 'generators'),
itemList.includes('generators.json'), hasBuilders:
}; itemList.includes('builders.json') ||
}); itemList.includes('executors.json'),
return { framework: framework as any, configs }; hasSchematics:
itemList.includes('collection.json') ||
itemList.includes('generators.json'),
};
}
);
return { framework, configs };
}); });
} }

View File

@ -1,8 +1,10 @@
import { green, red } from 'chalk'; import * as chalk from 'chalk';
import * as shell from 'shelljs';
import * as fs from 'fs'; import * as fs from 'fs';
import * as parseLinks from 'parse-markdown-links'; import * as parseLinks from 'parse-markdown-links';
import * as path from 'path'; import * as path from 'path';
import * as glob from 'glob';
console.log(`${chalk.blue('i')} Internal Link Check`);
const LOGGING_KEYS = [ const LOGGING_KEYS = [
'LOG_DOC_TREE', 'LOG_DOC_TREE',
@ -76,7 +78,7 @@ function expandFrameworks(linkPaths: string[]): string[] {
} }
function extractAllInternalLinks(): Record<string, string[]> { function extractAllInternalLinks(): Record<string, string[]> {
return shell.ls(`${BASE_PATH}/**/*.md`).reduce((acc, path) => { return glob.sync(`${BASE_PATH}/**/*.md`).reduce((acc, path) => {
const fileContents = readFileContents(path); const fileContents = readFileContents(path);
const directLinks = fileContents const directLinks = fileContents
.split(/[ ,]+/) .split(/[ ,]+/)
@ -103,7 +105,7 @@ function extractAllInternalLinks(): Record<string, string[]> {
} }
function extractAllInternalLinksWithAnchors(): Record<string, string[]> { function extractAllInternalLinksWithAnchors(): Record<string, string[]> {
return shell.ls(`${BASE_PATH}/**/*.md`).reduce((acc, path) => { return glob.sync(`${BASE_PATH}/**/*.md`).reduce((acc, path) => {
const links = parseLinks(readFileContents(path)) const links = parseLinks(readFileContents(path))
.filter(isLinkInternal) .filter(isLinkInternal)
.filter(isNotAsset) .filter(isNotAsset)
@ -138,7 +140,7 @@ function isCategoryNode(
function getDocumentMap(): DocumentTree[] { function getDocumentMap(): DocumentTree[] {
return JSON.parse( return JSON.parse(
fs.readFileSync(path.join(BASE_PATH, 'map.json'), { encoding: 'utf-8' }) fs.readFileSync(path.join(BASE_PATH, 'map.json'), 'utf-8')
) as DocumentTree[]; ) as DocumentTree[];
} }
@ -253,29 +255,27 @@ function checkInternalAnchoredLinks(
} }
if (!erroneousInternalLinks) { if (!erroneousInternalLinks) {
console.log(green('All internal links appear to be valid!!')); console.log(`${chalk.green('🗸')} All internal links appear to be valid!`);
console.log('Moving on to check internal anchors...');
const erroneousAnchoredInternalLinks = checkInternalAnchoredLinks( const erroneousAnchoredInternalLinks = checkInternalAnchoredLinks(
validInternalLinksMap validInternalLinksMap
); );
if (!erroneousAnchoredInternalLinks) { if (!erroneousAnchoredInternalLinks) {
console.log(green('All internal anchored links appear to be valid!!')); console.log(
`${chalk.green('🗸')} All internal anchored links appear to be valid!`
);
process.exit(0); process.exit(0);
} else { } else {
console.log( console.log(`${chalk.red(
red( 'ERROR'
'The following files appear to contain the following invalid anchored internal links:' )} The following files appear to contain the following invalid anchored internal links:
) ${JSON.stringify(erroneousAnchoredInternalLinks, null, 2)}`);
);
console.log(red(JSON.stringify(erroneousAnchoredInternalLinks, null, 2)));
process.exit(1); process.exit(1);
} }
} else { } else {
console.log( console.log(`${chalk.red(
red( 'ERROR'
'The following files appear to contain the following invalid internal links:' )} The following files appear to contain the following invalid internal links:
) ${JSON.stringify(erroneousInternalLinks, null, 2)}`);
);
console.log(red(JSON.stringify(erroneousInternalLinks, null, 2)));
process.exit(1); process.exit(1);
} }

View File

@ -1,12 +1,14 @@
import { green, red } from 'chalk';
import * as fs from 'fs'; import * as fs from 'fs';
import * as shell from 'shelljs'; import * as glob from 'glob';
import * as chalk from 'chalk';
console.log(`${chalk.blue('i')} Documentation Map Check`);
const basePath = 'docs'; const basePath = 'docs';
const sharedFilesPattern = 'shared/cli'; const sharedFilesPattern = 'shared/cli';
const readmePathList: string[] = shell const readmePathList: string[] = glob
.ls(`${basePath}/**/*.md`) .sync(`${basePath}/**/*.md`)
.map((path: string) => path.split(basePath)[1]) .map((path: string) => path.split(basePath)[1])
.map((path: string) => path.slice(1, -3)) // Removing first `/` and `.md` .map((path: string) => path.slice(1, -3)) // Removing first `/` and `.md`
.filter((path: string) => !path.startsWith(sharedFilesPattern)); .filter((path: string) => !path.startsWith(sharedFilesPattern));
@ -49,38 +51,44 @@ let scriptError = false;
if (!!readmeMissList.length) { if (!!readmeMissList.length) {
console.error( console.error(
red("\n⚠ Documentation files and 'map.json' file are out of sync!\n") chalk.red(
"\n⚠ Documentation files and 'map.json' file are out of sync!\n"
)
); );
console.log(readmeMissList.map((x) => x.concat('.md')).join('\n')); console.log(readmeMissList.map((x) => x.concat('.md')).join('\n'));
console.error( console.error(
red( chalk.red(
`\nSome documentation files exist without any reference in \'map.json\', make sure to add an entry.` `\nSome documentation files exist without any reference in \'map.json\', make sure to add an entry.`
) )
); );
scriptError = true; scriptError = true;
} else { } else {
console.log( console.log(
green("Markdown files are in sync with 'map.json', everything is good 👍") `${chalk.green('🗸')} Markdown files are in sync with ${chalk.grey(
'docs/maps.json'
)}.`
); );
} }
if (!!mapMissList.length) { if (!!mapMissList.length) {
console.error( console.log(
red( `\n${chalk.red(
"\n⚠ The 'map.json' file and the documentation files are out of sync!\n" 'ERROR'
) )} The 'map.json' file and the documentation files are out of sync!\n`
); );
console.log(mapMissList.map((x) => x.concat('.md')).join('\n')); console.log(mapMissList.map((x) => x.concat('.md')).join('\n'));
console.error( console.log(
red( `\n${chalk.red(
`\nThe \'map.json\' file is linking documenation files that do not exist.` 'ERROR'
) )} The \'map.json\' file is linking documenation files that do not exist.`
); );
scriptError = true; scriptError = true;
} else { } else {
console.log( console.log(
green( console.log(
"The 'map.json' file and the documentation files are in sync, everything is good 👍" `${chalk.green(
'🗸'
)} The 'map.json' file and the documentation files are in sync.`
) )
); );
} }

View File

@ -3190,6 +3190,7 @@
url-loader "^4.0.0" url-loader "^4.0.0"
util-deprecate "^1.0.2" util-deprecate "^1.0.2"
webpack "^4.44.2" webpack "^4.44.2"
webpack-dev-middleware "^3.7.0" webpack-dev-middleware "^3.7.0"
webpack-filter-warnings-plugin "^1.2.1" webpack-filter-warnings-plugin "^1.2.1"
webpack-hot-middleware "^2.25.0" webpack-hot-middleware "^2.25.0"