From fd4050e0e91a8c285bbe085d4b1acb04cdd4eac8 Mon Sep 17 00:00:00 2001 From: cha0s Date: Wed, 9 Mar 2022 08:51:10 -0600 Subject: [PATCH] feat: autodoc for build config --- TODO.md | 2 +- packages/core/src/server/index.js | 16 ++++++++++++ packages/dox/src/commands.js | 12 ++++++++- packages/dox/src/generate.js | 19 ++++++++++++++ packages/dox/src/parser.js | 42 +++++++++++++++++++++++++++++++ packages/http/src/server/index.js | 5 ++++ 6 files changed, 94 insertions(+), 2 deletions(-) diff --git a/TODO.md b/TODO.md index 592298f..f176e7b 100644 --- a/TODO.md +++ b/TODO.md @@ -17,7 +17,7 @@ - [ ] hints for hook types - [x] localConfig discovered by hook - [x] renamed to 'build/config'? - - [ ] automatically generated list of build config + - [x] automatically generated list of build config - [ ] static documentation site generator - [ ] autogenerated config dox page - [x] remove `invokeParallel()` diff --git a/packages/core/src/server/index.js b/packages/core/src/server/index.js index 1a078ba..1fb7ea9 100644 --- a/packages/core/src/server/index.js +++ b/packages/core/src/server/index.js @@ -38,10 +38,26 @@ export default { ); }, '@flecks/core.build.config': () => [ + /** + * Babel configuration. See: https://babeljs.io/docs/en/config-files + */ 'babel.config.js', + /** + * ESLint defaults. The default .eslintrc.js just reads from this file so that the build + * process can dynamically configure parts of ESLint. + */ ['.eslint.defaults.js', {specifier: (specific) => `${specific}.eslint.defaults.js`}], + /** + * ESLint configuration. See: https://eslint.org/docs/user-guide/configuring/ + */ ['.eslintrc.js', {specifier: (specific) => `${specific}.eslintrc.js`}], + /** + * Neutrino build configuration. See: https://neutrinojs.org/usage/ + */ ['.neutrinorc.js', {specifier: (specific) => `${specific}.neutrinorc.js`}], + /** + * Webpack (v4) configuration. See: https://v4.webpack.js.org/configuration/ + */ 'webpack.config.js', ], '@flecks/core.commands': commands, diff --git a/packages/dox/src/commands.js b/packages/dox/src/commands.js index d87d91a..eaca010 100644 --- a/packages/dox/src/commands.js +++ b/packages/dox/src/commands.js @@ -3,7 +3,11 @@ import {join} from 'path'; import {D} from '@flecks/core'; -import {generateHookPage, generateTodoPage} from './generate'; +import { + generateBuildConfigsPage, + generateHookPage, + generateTodoPage, +} from './generate'; import {parseFlecks} from './parser'; const { @@ -26,6 +30,9 @@ export default (program, flecks) => { debug('Generating TODO page...'); const todoPage = generateTodoPage(state.todos, flecks); debug('generated'); + debug('Generating build configs page...'); + const buildConfigsPage = generateBuildConfigsPage(state.buildConfigs); + debug('generated'); const output = join(FLECKS_CORE_ROOT, 'dox'); await mkdir(output, {recursive: true}); /* eslint-disable no-console */ @@ -37,6 +44,9 @@ export default (program, flecks) => { debug('Writing TODO page...'); await writeFile(join(output, 'TODO.md'), todoPage); console.log('TODO.md'); + debug('Writing build configs page...'); + await writeFile(join(output, 'build-configs.md'), buildConfigsPage); + console.log('build-configs.md'); console.groupEnd(); console.log(''); /* eslint-enable no-console */ diff --git a/packages/dox/src/generate.js b/packages/dox/src/generate.js index 286731b..341cf3b 100644 --- a/packages/dox/src/generate.js +++ b/packages/dox/src/generate.js @@ -6,6 +6,25 @@ const makeFilenameRewriter = (filenameRewriters) => (filename, line, column) => ) ); +export const generateBuildConfigsPage = (buildConfigs) => { + const source = []; + source.push('# Build configuration files'); + source.push(''); + source.push('This page documents all the build configuration files in this project.'); + source.push(''); + if (buildConfigs.length > 0) { + buildConfigs + .sort(({config: l}, {config: r}) => (l < r ? -1 : 1)) + .forEach(({config, comment}) => { + source.push(`## \`${config}\``); + source.push(''); + source.push(comment); + source.push(''); + }); + } + return source.join('\n'); +}; + export const generateHookPage = (hooks, flecks) => { const {filenameRewriters} = flecks.get('@flecks/dox/server'); const rewriteFilename = makeFilenameRewriter(filenameRewriters); diff --git a/packages/dox/src/parser.js b/packages/dox/src/parser.js index a86a54b..8c1df42 100644 --- a/packages/dox/src/parser.js +++ b/packages/dox/src/parser.js @@ -4,6 +4,8 @@ import {dirname, join} from 'path'; import {transformAsync} from '@babel/core'; import traverse from '@babel/traverse'; import { + isArrayExpression, + isArrowFunctionExpression, isIdentifier, isLiteral, isMemberExpression, @@ -20,10 +22,15 @@ const flecksCorePath = dirname(__non_webpack_require__.resolve('@flecks/core/pac class ParserState { constructor() { + this.buildConfigs = []; this.hooks = {}; this.todos = []; } + addBuildConfig(config, comment) { + this.buildConfigs.push({comment, config}); + } + addImplementation(hook, filename, loc) { this.hooks[hook] = this.hooks[hook] || {}; this.hooks[hook].implementations = this.hooks[hook].implementations || []; @@ -70,6 +77,40 @@ const implementationVisitor = (fn) => ({ }, }); +const FlecksBuildConfigs = (state, filename) => ( + implementationVisitor((property) => { + if ('@flecks/core.build.config' === property.key.value) { + if (isArrowFunctionExpression(property.value)) { + if (isArrayExpression(property.value.body)) { + property.value.body.elements.forEach((element) => { + let config; + if (isStringLiteral(element)) { + config = element.value; + } + if (isArrayExpression(element)) { + if (element.elements.length > 0 && isStringLiteral(element.elements[0])) { + config = element.elements[0].value; + } + } + if (config && element.leadingComments.length > 0) { + state.addBuildConfig( + config, + element.leadingComments.pop().value.split('\n') + .map((line) => line.trim()) + .map((line) => line.replace(/^\*/, '')) + .map((line) => line.trim()) + .filter((line) => !!line) + .join(' ') + .trim(), + ); + } + }); + } + } + } + }) +); + const FlecksInvocations = (state, filename) => ({ CallExpression(path) { if (isMemberExpression(path.node.callee)) { @@ -192,6 +233,7 @@ export const parseCode = async (code) => { export const parseFile = async (filename, resolved, state) => { const buffer = await readFile(filename); const ast = await parseCode(buffer.toString('utf8')); + traverse(ast, FlecksBuildConfigs(state, resolved)); traverse(ast, FlecksInvocations(state, resolved)); traverse(ast, FlecksImplementations(state, resolved)); traverse(ast, FlecksTodos(state, resolved)); diff --git a/packages/http/src/server/index.js b/packages/http/src/server/index.js index bb5f1e6..402e315 100644 --- a/packages/http/src/server/index.js +++ b/packages/http/src/server/index.js @@ -37,6 +37,11 @@ export default { delete neutrinoConfigs.http; }, '@flecks/core.build.config': () => [ + /** + * Template file used to generate the client HTML. + * + * See: https://github.com/jantimon/html-webpack-plugin/blob/main/docs/template-option.md + */ 'template.ejs', ], '@flecks/core.config': () => ({