refactor: config gen

This commit is contained in:
cha0s 2022-03-09 14:43:54 -06:00
parent 72042d0ee6
commit 31238b18da
18 changed files with 194 additions and 17 deletions

View File

@ -19,7 +19,7 @@
- [x] renamed to 'build/config'? - [x] renamed to 'build/config'?
- [x] automatically generated list of build config - [x] automatically generated list of build config
- [ ] static documentation site generator - [ ] static documentation site generator
- [ ] autogenerated config dox page - [x] autogenerated config dox page
- [x] remove `invokeParallel()` - [x] remove `invokeParallel()`
- [x] Specialize `invokeReduce()` with `invokeMerge()`. - [x] Specialize `invokeReduce()` with `invokeMerge()`.
- [x] Rename all hooks to dot-first notation; rewrite `lookupFlecks()`. - [x] Rename all hooks to dot-first notation; rewrite `lookupFlecks()`.

View File

@ -69,6 +69,9 @@ export default {
whatever: 'configuration', whatever: 'configuration',
your: 1337, your: 1337,
fleck: 'needs', fleck: 'needs',
/**
* Also, comments like this will be used to automatically generate documentation.
*/
though: 'you should keep the values serializable', though: 'you should keep the values serializable',
}), }),

View File

@ -16,6 +16,9 @@ export {
export default { export default {
[Hooks]: { [Hooks]: {
'@flecks/core.config': () => ({ '@flecks/core.config': () => ({
/**
* The ID of your application.
*/
id: 'flecks', id: 'flecks',
}), }),
}, },

View File

@ -63,6 +63,9 @@ export default {
], ],
'@flecks/core.commands': commands, '@flecks/core.commands': commands,
'@flecks/core.config': () => ({ '@flecks/core.config': () => ({
/**
* Build targets to exclude from ESLint.
*/
'eslint.exclude': [], 'eslint.exclude': [],
}), }),
}, },

View File

@ -12,11 +12,31 @@ export {createDatabaseConnection};
export default { export default {
[Hooks]: { [Hooks]: {
'@flecks/core.config': () => ({ '@flecks/core.config': () => ({
/**
* The database to connect to.
*/
database: ':memory:', database: ':memory:',
/**
* SQL dialect.
*
* See: https://sequelize.org/v5/manual/dialects.html
*/
dialect: 'sqlite', dialect: 'sqlite',
/**
* Database server host.
*/
host: undefined, host: undefined,
/**
* Database server password.
*/
password: undefined, password: undefined,
/**
* Database server port.
*/
port: undefined, port: undefined,
/**
* Database server username.
*/
username: undefined, username: undefined,
}), }),
'@flecks/core.starting': (flecks) => { '@flecks/core.starting': (flecks) => {

View File

@ -6,6 +6,9 @@ import startContainer from './start-container';
export default { export default {
[Hooks]: { [Hooks]: {
'@flecks/core.config': () => ({ '@flecks/core.config': () => ({
/**
* Whether to run docker containers.
*/
enabled: true, enabled: true,
}), }),
'@flecks/core.commands': commands, '@flecks/core.commands': commands,

View File

@ -5,6 +5,7 @@ import {D} from '@flecks/core';
import { import {
generateBuildConfigsPage, generateBuildConfigsPage,
generateConfigPage,
generateHookPage, generateHookPage,
generateTodoPage, generateTodoPage,
} from './generate'; } from './generate';
@ -33,6 +34,9 @@ export default (program, flecks) => {
debug('Generating build configs page...'); debug('Generating build configs page...');
const buildConfigsPage = generateBuildConfigsPage(state.buildConfigs); const buildConfigsPage = generateBuildConfigsPage(state.buildConfigs);
debug('generated'); debug('generated');
debug('Generating config page...');
const configPage = generateConfigPage(state.configs);
debug('generated');
const output = join(FLECKS_CORE_ROOT, 'dox'); const output = join(FLECKS_CORE_ROOT, 'dox');
await mkdir(output, {recursive: true}); await mkdir(output, {recursive: true});
/* eslint-disable no-console */ /* eslint-disable no-console */
@ -47,6 +51,9 @@ export default (program, flecks) => {
debug('Writing build configs page...'); debug('Writing build configs page...');
await writeFile(join(output, 'build-configs.md'), buildConfigsPage); await writeFile(join(output, 'build-configs.md'), buildConfigsPage);
console.log('build-configs.md'); console.log('build-configs.md');
debug('Writing config page...');
await writeFile(join(output, 'config.md'), configPage);
console.log('config.md');
console.groupEnd(); console.groupEnd();
console.log(''); console.log('');
/* eslint-enable no-console */ /* eslint-enable no-console */

View File

@ -25,6 +25,31 @@ export const generateBuildConfigsPage = (buildConfigs) => {
return source.join('\n'); return source.join('\n');
}; };
export const generateConfigPage = (configs) => {
const source = [];
source.push('# Configuration');
source.push('');
source.push('This page documents all the configuration in this project.');
source.push('');
Object.entries(configs)
.sort(([l], [r]) => (l < r ? -1 : 1))
.forEach(([fleck, configs]) => {
source.push(`## \`${fleck}\``);
source.push('');
configs.forEach(({comment, config, defaultValue}) => {
comment.split('\n').forEach((line) => {
source.push(`> ${line}`);
});
source.push('');
source.push('```javascript');
source.push(`${config}: ${defaultValue}`);
source.push('```');
source.push('');
});
});
return source.join('\n');
};
export const generateHookPage = (hooks, flecks) => { export const generateHookPage = (hooks, flecks) => {
const {filenameRewriters} = flecks.get('@flecks/dox/server'); const {filenameRewriters} = flecks.get('@flecks/dox/server');
const rewriteFilename = makeFilenameRewriter(filenameRewriters); const rewriteFilename = makeFilenameRewriter(filenameRewriters);

View File

@ -1,5 +1,10 @@
import {readFile} from 'fs/promises'; import {readFile} from 'fs/promises';
import {dirname, join} from 'path'; import {
basename,
dirname,
extname,
join,
} from 'path';
import {transformAsync} from '@babel/core'; import {transformAsync} from '@babel/core';
import traverse from '@babel/traverse'; import traverse from '@babel/traverse';
@ -23,6 +28,7 @@ class ParserState {
constructor() { constructor() {
this.buildConfigs = []; this.buildConfigs = [];
this.configs = {};
this.hooks = {}; this.hooks = {};
this.todos = []; this.todos = [];
} }
@ -31,6 +37,20 @@ class ParserState {
this.buildConfigs.push({comment, config}); this.buildConfigs.push({comment, config});
} }
addConfig(config, comment, filename, defaultValue) {
const ext = extname(filename);
const trimmed = join(dirname(filename), basename(filename, ext)).replace('/src', '');
const fleck = 'index' === basename(trimmed) ? dirname(trimmed) : trimmed;
if (!this.configs[fleck]) {
this.configs[fleck] = [];
}
this.configs[fleck].push({
comment,
config,
defaultValue,
});
}
addImplementation(hook, filename, loc) { addImplementation(hook, filename, loc) {
this.hooks[hook] = this.hooks[hook] || {}; this.hooks[hook] = this.hooks[hook] || {};
this.hooks[hook].implementations = this.hooks[hook].implementations || []; this.hooks[hook].implementations = this.hooks[hook].implementations || [];
@ -92,16 +112,47 @@ const FlecksBuildConfigs = (state) => (
config = element.elements[0].value; config = element.elements[0].value;
} }
} }
if (config && element.leadingComments.length > 0) { if (config) {
state.addBuildConfig( state.addBuildConfig(
config, config,
element.leadingComments.pop().value.split('\n') (element.leadingComments?.length > 0)
? element.leadingComments.pop().value.split('\n')
.map((line) => line.trim()) .map((line) => line.trim())
.map((line) => line.replace(/^\*/, '')) .map((line) => line.replace(/^\*/, ''))
.map((line) => line.trim()) .map((line) => line.trim())
.filter((line) => !!line) .filter((line) => !!line)
.join(' ') .join(' ')
.trim(), .trim()
: '*No description provided.*',
);
}
});
}
}
}
})
);
const FlecksConfigs = (state, filename, source) => (
implementationVisitor((property) => {
if ('@flecks/core.config' === property.key.value) {
if (isArrowFunctionExpression(property.value)) {
if (isObjectExpression(property.value.body)) {
property.value.body.properties.forEach((property) => {
if (isIdentifier(property.key) || isStringLiteral(property.key)) {
state.addConfig(
property.key.name || property.key.value,
(property.leadingComments?.length > 0)
? property.leadingComments.pop().value.split('\n')
.map((line) => line.trim())
.map((line) => line.replace(/^\*/, ''))
.map((line) => line.trim())
.filter((line) => !!line)
.join(' ')
.trim()
: '*No description provided.*',
filename,
source.slice(property.value.start, property.value.end),
); );
} }
}); });
@ -232,8 +283,10 @@ export const parseCode = async (code) => {
export const parseFile = async (filename, resolved, state) => { export const parseFile = async (filename, resolved, state) => {
const buffer = await readFile(filename); const buffer = await readFile(filename);
const ast = await parseCode(buffer.toString('utf8')); const source = buffer.toString('utf8');
const ast = await parseCode(source);
traverse(ast, FlecksBuildConfigs(state, resolved)); traverse(ast, FlecksBuildConfigs(state, resolved));
traverse(ast, FlecksConfigs(state, resolved, source));
traverse(ast, FlecksInvocations(state, resolved)); traverse(ast, FlecksInvocations(state, resolved));
traverse(ast, FlecksImplementations(state, resolved)); traverse(ast, FlecksImplementations(state, resolved));
traverse(ast, FlecksTodos(state, resolved)); traverse(ast, FlecksTodos(state, resolved));

View File

@ -6,6 +6,11 @@ export default {
[Hooks]: { [Hooks]: {
'@flecks/core.commands': commands, '@flecks/core.commands': commands,
'@flecks/core.config': () => ({ '@flecks/core.config': () => ({
/**
* Rewrite the output filenames of source files.
*
* `filename.replace(new RegExp([key]), [value]);`
*/
filenameRewriters: {}, filenameRewriters: {},
}), }),
}, },

View File

@ -8,6 +8,9 @@ export {default as createLimiter} from './limiter';
export default { export default {
[Hooks]: { [Hooks]: {
'@flecks/core.config': () => ({ '@flecks/core.config': () => ({
/**
* All keys used to determine fingerprint.
*/
keys: ['ip'], keys: ['ip'],
http: { http: {
keys: ['ip'], keys: ['ip'],

View File

@ -45,15 +45,39 @@ export default {
'template.ejs', 'template.ejs',
], ],
'@flecks/core.config': () => ({ '@flecks/core.config': () => ({
/**
* (webpack-dev-server) Host to bind.
*/
devHost: 'localhost', devHost: 'localhost',
/**
* (webpack-dev-server) Port to bind.
*/
devPort: undefined, devPort: undefined,
/**
* (webpack-dev-server) Public path to serve.
*/
devPublic: undefined, devPublic: undefined,
/**
* (webpack-dev-server) Webpack stats output.
*/
devStats: 'minimal', devStats: 'minimal',
/**
* Host to bind.
*/
host: '0.0.0.0', host: '0.0.0.0',
/**
* Build path.
*/
output: 'http', output: 'http',
/**
* Port to bind.
*/
port: 32340, port: 32340,
'request.route': [], /**
'request.socket': [], * Proxies to trust.
*
* See: https://www.npmjs.com/package/proxy-addr
*/
trust: false, trust: false,
}), }),
'@flecks/core.starting': (flecks) => { '@flecks/core.starting': (flecks) => {

View File

@ -16,6 +16,9 @@ export {default as usePrevious} from './hooks/use-previous';
export default { export default {
[Hooks]: { [Hooks]: {
'@flecks/core.config': () => ({ '@flecks/core.config': () => ({
/**
* Whether to enable server-side rendering.
*/
ssr: true, ssr: true,
}), }),
}, },

View File

@ -30,7 +30,13 @@ export const keys = (client, pattern) => safeKeys(client, pattern, 0);
export default { export default {
[Hooks]: { [Hooks]: {
'@flecks/core.config': () => ({ '@flecks/core.config': () => ({
/**
* Redis server host.
*/
host: 'localhost', host: 'localhost',
/**
* Redis server port.
*/
port: 6379, port: 6379,
}), }),
'@flecks/docker.containers': containers, '@flecks/docker.containers': containers,

View File

@ -3,9 +3,21 @@ import {Hooks} from '@flecks/core';
export default { export default {
[Hooks]: { [Hooks]: {
'@flecks/core.config': () => ({ '@flecks/core.config': () => ({
/**
* Whether HMR is enabled.
*/
hot: false, hot: false,
/**
* Whether the Node inspector is enabled.
*/
inspect: false, inspect: false,
/**
* Whether node profiling is enabled.
*/
profile: false, profile: false,
/**
* Whether to start the server after building.
*/
start: false, start: false,
}), }),
}, },

View File

@ -5,10 +5,6 @@ import Sockets from './sockets';
export default { export default {
[Hooks]: { [Hooks]: {
'@flecks/core.config': () => ({
connect: [],
'request.socket': [],
}),
'@flecks/http/server.request.socket': ({config: {'$flecks/socket.sockets': sockets}}) => (req, res, next) => { '@flecks/http/server.request.socket': ({config: {'$flecks/socket.sockets': sockets}}) => (req, res, next) => {
req.intercom = createIntercom(sockets, 'http'); req.intercom = createIntercom(sockets, 'http');
next(); next();

View File

@ -7,7 +7,13 @@ import LocalStrategy from 'passport-local';
export default { export default {
[Hooks]: { [Hooks]: {
'@flecks/core.config': () => ({ '@flecks/core.config': () => ({
/**
* Path to redirect to after failed login.
*/
failureRedirect: '/', failureRedirect: '/',
/**
* Path to redirect to after successful login.
*/
successRedirect: '/', successRedirect: '/',
}), }),
'@flecks/db/server.models.decorate': ( '@flecks/db/server.models.decorate': (

View File

@ -7,6 +7,11 @@ const debug = D('@flecks/user/session');
export default { export default {
[Hooks]: { [Hooks]: {
'@flecks/core.config': () => ({ '@flecks/core.config': () => ({
/**
* Set the cookie secret for session encryption.
*
* See: http://expressjs.com/en/resources/middleware/cookie-parser.html
*/
cookieSecret: ( cookieSecret: (
'Set the FLECKS_ENV_FLECKS_USER_SESSION_SERVER_cookieSecret environment variable!' 'Set the FLECKS_ENV_FLECKS_USER_SESSION_SERVER_cookieSecret environment variable!'
), ),