diff options
author | Luke Karrys <luke@lukekarrys.com> | 2022-03-31 03:39:21 +0300 |
---|---|---|
committer | Gar <wraithgar@github.com> | 2022-04-06 19:26:45 +0300 |
commit | 03f36bf8c30b713e86f8e0b7e6643b68d74f7d91 (patch) | |
tree | afc33df0328cfda57f8cb5e4a7126fc615f00281 /docs | |
parent | 840c338aa6aba7dc39d9d3afba075701e3979362 (diff) |
chore(docs): move scripts and refactor
All docs related scripts are now located in the docs workspace
Diffstat (limited to 'docs')
-rw-r--r-- | docs/.gitignore | 1 | ||||
-rw-r--r-- | docs/bin/config-doc-command.js | 142 | ||||
-rw-r--r-- | docs/bin/config-doc.js | 63 | ||||
-rw-r--r-- | docs/bin/dockhand.js | 65 | ||||
-rw-r--r-- | docs/bin/docs-build.js | 39 | ||||
-rw-r--r-- | docs/lib/config.json (renamed from docs/bin/config.json) | 0 | ||||
-rw-r--r-- | docs/lib/npm.js | 14 | ||||
-rw-r--r-- | docs/lib/template.html (renamed from docs/bin/template.html) | 0 | ||||
-rw-r--r-- | docs/package.json | 6 | ||||
-rw-r--r-- | docs/test/index.js | 8 |
10 files changed, 298 insertions, 40 deletions
diff --git a/docs/.gitignore b/docs/.gitignore index 823857bf9..2231d46ac 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -18,5 +18,6 @@ !/.gitignore !/bin/ !/content/ +!/lib/ !/nav.yml !/package.json diff --git a/docs/bin/config-doc-command.js b/docs/bin/config-doc-command.js new file mode 100644 index 000000000..f55213cdc --- /dev/null +++ b/docs/bin/config-doc-command.js @@ -0,0 +1,142 @@ +const fs = require('fs').promises +const { resolve } = require('path') +const { definitions, aliases } = require('../lib/npm.js') + +const describeAll = (content) => + content.map(name => definitions[name].describe()).join( + '\n\n<!-- automatically generated, do not edit manually -->\n' + + '<!-- see lib/utils/config/definitions.js -->\n\n' + ) + +const describeUsage = ({ usage, configDoc, commandFile }) => { + const synopsis = [] + + // Grab the command name from the *.md filename + // NOTE: We cannot use the name property command file because in the case of + // `npx` the file being used is `lib/commands/exec.js` + const commandName = configDoc.split('/').pop().split('.')[0].replace('npm-', '') + synopsis.push('\n```bash') + + if (commandName) { + // special case for `npx`: + // `npx` is not technically a command in and of itself, + // so it just needs the usage and parameters of npm exec, and none of the aliases + if (commandName === 'npx') { + synopsis.push(usage.map(usageInfo => `npx ${usageInfo}`).join('\n')) + } else { + const baseCommand = `npm ${commandName}` + if (!usage) { + synopsis.push(baseCommand) + } else { + synopsis.push(usage.map(usageInfo => `${baseCommand} ${usageInfo}`).join('\n')) + } + + const cmdAliases = Object.keys(aliases).reduce((p, c) => { + if (aliases[c] === commandName) { + p.push(c) + } + return p + }, []) + + if (cmdAliases.length === 1) { + synopsis.push('') + synopsis.push(`alias: ${cmdAliases[0]}`) + } else if (cmdAliases.length > 1) { + synopsis.push('') + synopsis.push(`aliases: ${cmdAliases.join(', ')}`) + } + } + } else { + console.error(`could not determine command name from ${commandFile}`) + } + + synopsis.push('```') + return synopsis.join('\n') +} + +const addBetweenTags = ( + doc, + startTag, + endTag, + body, + sourceFilepath = 'lib/utils/config/definitions.js') => { + const startSplit = doc.split(startTag) + + if (startSplit.length !== 2) { + throw new Error('Did not find exactly one start tag') + } + + const endSplit = startSplit[1].split(endTag) + if (endSplit.length !== 2) { + throw new Error('Did not find exactly one end tag') + } + + return [ + startSplit[0], + startTag, + '\n<!-- automatically generated, do not edit manually -->\n' + + '<!-- see ' + sourceFilepath + ' -->\n', + body, + '\n\n<!-- automatically generated, do not edit manually -->\n' + + '<!-- see ' + sourceFilepath + ' -->', + '\n\n', + endTag, + endSplit[1], + ].join('') +} + +const TAGS = { + CONFIG: { + START: '<!-- AUTOGENERATED CONFIG DESCRIPTIONS START -->', + END: '<!-- AUTOGENERATED CONFIG DESCRIPTIONS END -->', + }, + USAGE: { + START: '<!-- AUTOGENERATED USAGE DESCRIPTIONS START -->', + END: '<!-- AUTOGENERATED USAGE DESCRIPTIONS END -->', + }, +} + +const addDescriptions = ({ doc, params }) => + addBetweenTags(doc, TAGS.CONFIG.START, TAGS.CONFIG.END, describeAll(params)) + +const addUsageDescriptions = ({ doc, usage, configDoc, commandFile }) => + addBetweenTags(doc, TAGS.USAGE.START, TAGS.USAGE.END, + describeUsage({ usage, configDoc, commandFile }), + commandFile + ) + +const run = async (configDoc, commandFile) => { + try { + // Note: commands without params skip this whole process. + const { params, usage } = require(resolve(commandFile)) + + // always write SOMETHING so that Make sees the file is up to date. + const doc = await fs.readFile(configDoc, 'utf8') + const hasTag = doc.includes(TAGS.CONFIG.START) + const hasUsageTag = doc.includes(TAGS.USAGE.START) + + if (params && params.length) { + let newDoc = hasTag ? addDescriptions({ doc, params }) : doc + newDoc = hasUsageTag + ? addUsageDescriptions({ doc: newDoc, usage, configDoc, commandFile }) + : newDoc + + if (!hasTag) { + console.error('WARNING: did not find config description section', configDoc) + } + + if ((usage && usage.length) && !hasUsageTag) { + console.error('WARNING: did not find usage description section', configDoc) + } + await fs.writeFile(configDoc, newDoc) + } + } catch (err) { + console.error(`WARNING: file cannot be open: ${configDoc}`) + throw err + } +} + +run(...process.argv.slice(2)).catch((err) => { + process.exitCode = 1 + console.error(err) +}) diff --git a/docs/bin/config-doc.js b/docs/bin/config-doc.js new file mode 100644 index 000000000..1aa96e53c --- /dev/null +++ b/docs/bin/config-doc.js @@ -0,0 +1,63 @@ +const fs = require('fs').promises +const { resolve, relative } = require('path') +const { shorthands, describeAll } = require('../lib/npm.js') + +const addBetweenTags = (doc, startTag, endTag, body) => { + const startSplit = doc.split(startTag) + if (startSplit.length !== 2) { + throw new Error('Did not find exactly one start tag') + } + + const endSplit = startSplit[1].split(endTag) + if (endSplit.length !== 2) { + throw new Error('Did not find exactly one end tag') + } + + return [ + startSplit[0], + startTag, + '\n<!-- automatically generated, do not edit manually -->\n' + + '<!-- see lib/utils/config/definitions.js -->\n', + body, + '\n\n<!-- automatically generated, do not edit manually -->\n' + + '<!-- see lib/utils/config/definitions.js -->\n', + endTag, + endSplit[1], + ].join('') +} + +const addDescriptions = doc => { + const startTag = '<!-- AUTOGENERATED CONFIG DESCRIPTIONS START -->' + const endTag = '<!-- AUTOGENERATED CONFIG DESCRIPTIONS END -->' + return addBetweenTags(doc, startTag, endTag, describeAll()) +} + +const addShorthands = doc => { + const startTag = '<!-- AUTOGENERATED CONFIG SHORTHANDS START -->' + const endTag = '<!-- AUTOGENERATED CONFIG SHORTHANDS END -->' + const body = Object.entries(shorthands) + .sort(([shorta, expansiona], [shortb, expansionb]) => { + // sort by what they're short FOR + return expansiona.join(' ').localeCompare(expansionb.join(' '), 'en') || + shorta.localeCompare(shortb, 'en') + }) + .map(([short, expansion]) => { + const dash = short.length === 1 ? '-' : '--' + return `* \`${dash}${short}\`: \`${expansion.join(' ')}\`` + }).join('\n') + return addBetweenTags(doc, startTag, endTag, body) +} + +const run = async (dir) => { + const configDoc = resolve(dir, '../content/using-npm/config.md') + const doc = await fs.readFile(configDoc, 'utf8') + + await fs.writeFile(configDoc, addDescriptions(addShorthands(doc))) + + return `updated ${relative(process.cwd(), configDoc)}` +} + +run(__dirname).then(console.log).catch((err) => { + process.exitCode = 1 + console.error(err) +}) diff --git a/docs/bin/dockhand.js b/docs/bin/dockhand.js index 4b359e1b6..56160136e 100644 --- a/docs/bin/dockhand.js +++ b/docs/bin/dockhand.js @@ -1,5 +1,3 @@ -#!/usr/bin/env node - const path = require('path') const fs = require('fs') const yaml = require('yaml') @@ -7,35 +5,7 @@ const cmark = require('cmark-gfm') const mdx = require('@mdx-js/mdx') const mkdirp = require('mkdirp') const jsdom = require('jsdom') -const npm = require('../../lib/npm.js') - -const run = async function (rootDir) { - const dir = (...p) => path.join(rootDir, '..', ...p) - - const config = require(dir('bin', 'config.json')) - const template = fs.readFileSync(dir('bin', 'template.html'), 'utf-8') - const nav = yaml.parse(fs.readFileSync(dir('nav.yml'), 'utf-8')) - - try { - const navPaths = getNavigationPaths(nav) - const fsPaths = await renderFilesystemPaths({ - input: dir('content'), - output: dir('output'), - config, - template, - }) - - const navErrors = ensureNavigationComplete(navPaths, fsPaths) - if (navErrors) { - console.error(navErrors) - process.exit(1) - } - } catch (error) { - console.error(error) - } -} - -run(__dirname) +const { version: VERSION } = require('../lib/npm.js') function ensureNavigationComplete (navPaths, fsPaths) { const unmatchedNav = {} @@ -124,7 +94,7 @@ async function renderFile (root, outputRoot, childPath, { template, config }) { const inputPath = path.join(root, childPath) if (!inputPath.match(/\.md$/)) { - console.log(`warning: unknown file type ${inputPath}, ignored`) + console.error(`warning: unknown file type ${inputPath}, ignored`) return } @@ -140,7 +110,7 @@ async function renderFile (root, outputRoot, childPath, { template, config }) { }) // Replace any tokens in the source - md = md.replace(/@VERSION@/, npm.version) + md = md.replace(/@VERSION@/, VERSION) // Render the markdown into an HTML snippet using a GFM renderer. const content = cmark.renderHtmlSync(md, { @@ -190,7 +160,7 @@ async function renderFile (root, outputRoot, childPath, { template, config }) { return config[key.replace(/^config\./, '')] default: - console.log(`warning: unknown token '${token}' in ${inputPath}`) + console.error(`warning: unknown token '${token}' in ${inputPath}`) return '' } }) @@ -323,3 +293,30 @@ class MarkdownError extends Error { this.inner = inner } } + +const run = async function (rootDir) { + const dir = (...p) => path.join(rootDir, '..', ...p) + + const config = require(dir('lib', 'config.json')) + const template = fs.readFileSync(dir('lib', 'template.html'), 'utf-8') + const nav = yaml.parse(fs.readFileSync(dir('nav.yml'), 'utf-8')) + + const navPaths = getNavigationPaths(nav) + const fsPaths = await renderFilesystemPaths({ + input: dir('content'), + output: dir('output'), + config, + template, + }) + + const navErrors = ensureNavigationComplete(navPaths, fsPaths) + if (navErrors) { + console.error(navErrors) + throw new Error('Nav Errors') + } +} + +run(__dirname).catch((err) => { + process.exitCode = 1 + console.error(err) +}) diff --git a/docs/bin/docs-build.js b/docs/bin/docs-build.js new file mode 100644 index 000000000..61343bfa4 --- /dev/null +++ b/docs/bin/docs-build.js @@ -0,0 +1,39 @@ +const fs = require('fs').promises +const marked = require('marked-man') +const { version: VERSION } = require('../lib/npm.js') + +function frontmatter (_, p1) { + const fm = {} + + p1.split(/\r?\n/).forEach((kv) => { + const result = kv.match(/^([^\s:]+):\s*(.*)/) + if (result) { + fm[result[1]] = result[2] + } + }) + + return `# ${fm.title}(${fm.section}) - ${fm.description}` +} + +function replacer (_, p1) { + return 'npm help ' + p1.replace(/npm /, '') +} + +const run = async (src, dest = src) => { + const data = await fs.readFile(src, 'utf8') + + const result = data.replace(/@VERSION@/g, VERSION) + .replace(/^<!--.*-->$/gm, '') + .replace(/^---\n([\s\S]+\n)---/, frontmatter) + .replace(/\[([^\]]+)\]\(\/commands\/([^)]+)\)/g, replacer) + .replace(/\[([^\]]+)\]\(\/configuring-npm\/([^)]+)\)/g, replacer) + .replace(/\[([^\]]+)\]\(\/using-npm\/([^)]+)\)/g, replacer) + .trim() + + await fs.writeFile(dest, marked(result), 'utf8') +} + +run(...process.argv.slice(2)).catch((err) => { + process.exitCode = 1 + console.error(err) +}) diff --git a/docs/bin/config.json b/docs/lib/config.json index c9f183e4e..c9f183e4e 100644 --- a/docs/bin/config.json +++ b/docs/lib/config.json diff --git a/docs/lib/npm.js b/docs/lib/npm.js new file mode 100644 index 000000000..fb801a17b --- /dev/null +++ b/docs/lib/npm.js @@ -0,0 +1,14 @@ +const { definitions, shorthands, describeAll } = require('../../lib/utils/config/index.js') +const { aliases } = require('../../lib/utils/cmd-list') +const Npm = require('../../lib/npm.js') + +// These are all the npm internals depended on by the docs scripts. If these change +// the associated targets should also be updated in the Makefile. + +module.exports = { + definitions, + shorthands, + describeAll, + aliases, + version: Npm.version, +} diff --git a/docs/bin/template.html b/docs/lib/template.html index be3bafd61..be3bafd61 100644 --- a/docs/bin/template.html +++ b/docs/lib/template.html diff --git a/docs/package.json b/docs/package.json index 9bd06f094..037db15a0 100644 --- a/docs/package.json +++ b/docs/package.json @@ -4,7 +4,6 @@ "version": "1.0.0", "private": true, "scripts": { - "build": "node bin/dockhand", "lint": "eslint \"**/*.js\"", "postlint": "template-oss-check", "template-oss-apply": "template-oss-apply --force", @@ -30,12 +29,14 @@ "jsdom": "^18.1.0", "marked-man": "^0.7.0", "tap": "^15.2.3", + "which": "^2.0.2", "yaml": "^1.10.0" }, "author": "GitHub Inc.", "license": "ISC", "files": [ "bin/", + "lib/", "content/", "nav.yml" ], @@ -45,13 +46,14 @@ "tap": { "statements": 81, "branches": 72, - "functions": 80, + "functions": 75, "lines": 81 }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", "distPaths": [ "bin/", + "lib/", "content/", "nav.yml" ], diff --git a/docs/test/index.js b/docs/test/index.js index fab9748a3..57524327b 100644 --- a/docs/test/index.js +++ b/docs/test/index.js @@ -9,24 +9,24 @@ const output = join(cwd, 'output') const rmOutput = () => fs.rm(output, { recursive: true, force: true }).catch(() => {}) -const spawnNpm = (...args) => { +const spawnNpm = (cmd, ...args) => { // remove npm config when spawning so config set by test commands don't interfere const env = Object.entries(process.env) .filter(([k]) => k.toLowerCase() !== 'npm_config_ignore_scripts') - return spawn(which('npm'), args, { + return spawn(which(cmd), args, { env: Object.fromEntries(env), stdioString: true, cwd, }) } -t.before(() => spawnNpm('rebuild', 'cmark-gfm')) +t.before(() => spawnNpm('npm', 'rebuild', 'cmark-gfm')) t.beforeEach(() => rmOutput()) t.test('docs', async (t) => { t.rejects(() => fs.stat(output)) - const docs = await spawnNpm('run', 'build') + const docs = await spawnNpm('node', join('bin', 'dockhand')) t.equal(docs.code, 0) t.ok((await fs.stat(output)).isDirectory()) }) |