refactor: build config

This commit is contained in:
cha0s 2022-03-09 07:25:58 -06:00
parent f8d07874ae
commit af6a70678b
21 changed files with 146 additions and 113 deletions

View File

@ -15,8 +15,8 @@
- [x] `flecks.expandedFlecks()` should use `platforms`
- [ ] config validation
- [ ] hints for hook types
- [ ] localConfig discovered by hook
- [ ] renamed to 'build/config'?
- [x] localConfig discovered by hook
- [x] renamed to 'build/config'?
- [ ] automatically generated list of build config
- [ ] static documentation site generator
- [ ] autogenerated config dox page

View File

@ -17,9 +17,9 @@ module.exports = require('../src/bootstrap/fleck.neutrinorc');
module.exports.use.push((neutrino) => {
['eslintrc', 'eslint.defaults'].forEach((filename) => {
neutrino.config
.entry(`build/.${filename}`)
.entry(`server/build/.${filename}`)
.clear()
.add(`./src/build/${filename}`);
.add(`./src/server/build/${filename}`);
})
});
@ -44,7 +44,7 @@ module.exports.use.unshift(
airbnb({
eslint: {
baseConfig: {
...require('../src/build/eslint.defaults'),
...require('../src/server/build/eslint.defaults'),
env: {
mocha: true,
},

View File

@ -62,20 +62,16 @@ Have fun!
## Resolution order 🤔
The flecks server provides an interface (`flecks.localConfig()`) for gathering configuration files
The flecks server provides an interface (`flecks.buildConfig()`) for gathering configuration files
from the `build` directory. The resolution order is determined by a few variables:
- `filename` specifies the name of the configuration file, e.g. `babel.config.js`.
- `filename` specifies the name of the configuration file, e.g. `server.neutrinorc.js`.
- `general` specifies a general variation of the given configuration. `@flecks/server` looks for
an overridden `server.neutrinorc.js` when building, however `general` is set to `.neutrinorc.js`,
so it will also accept overrides of that more general configuration file.
- `general` specifies a general variation of the given configuration. The general form of `server.neutrinorc.js` is `.neutrinorc.js`.
- `root` specifies an alternative location to search. Defaults to `FLECKS_CORE_ROOT`.
- `fleck` specifies the fleck owning the configuration. `@flecks/core` owns `babel.config.js`,
`@flecks/server` owns `server.neutrinorc.js`, etc. This only really matters if you are writing a
fleck that owns its configuration.
- `fleck` specifies the fleck owning the configuration. `@flecks/server` owns `server.neutrinorc.js`.
Given these considerations, and supposing we had the above variables set like:
@ -86,7 +82,7 @@ const root = '/foo/bar/baz';
const fleck = '@flecks/server';
```
We would then expect flecks to search using the following resolution order:
Flecks will then search the following paths top-down until it finds the build configuration:
- `/foo/bar/baz/build/server.neutrinorc.js`
- `/foo/bar/baz/build/.neutrinorc.js`

View File

@ -26,6 +26,14 @@ export default {
}
},
/**
* Register build configuration.
*/
'@flecks/core.build.config': () => [
'.myrc.js',
['mygeneralrc.js', {specifier: (specific) => `${specific}.mygeneralrc.js`}],
],
/**
* Define CLI commands.
*/

View File

@ -24,14 +24,6 @@
},
"files": [
"build",
"build/.eslint.defaults.js",
"build/.eslint.defaults.js.map",
"build/.eslintrc.js",
"build/.eslintrc.js.map",
"build/babel.config.js",
"build/babel.config.js.map",
"build/webpack.config.js",
"build/webpack.config.js.map",
"cli.js",
"cli.js.map",
"empty.js",
@ -40,6 +32,14 @@
"index.js.map",
"server.js",
"server.js.map",
"server/build/.eslint.defaults.js",
"server/build/.eslint.defaults.js.map",
"server/build/.eslintrc.js",
"server/build/.eslintrc.js.map",
"server/build/babel.config.js",
"server/build/babel.config.js.map",
"server/build/webpack.config.js",
"server/build/webpack.config.js.map",
"src",
"start.js",
"start.js.map",

View File

@ -1,9 +1,9 @@
const neutrino = require('neutrino');
const R = require('../bootstrap/require');
const {targetNeutrino} = require('../server/commands');
const D = require('../debug');
const {default: Flecks} = require('../server/flecks');
const R = require('../../bootstrap/require');
const D = require('../../debug');
const {targetNeutrino} = require('../commands');
const {default: Flecks} = require('../flecks');
const debug = D('@flecks/core/.eslintrc.js');

View File

@ -14,9 +14,9 @@ import flatten from 'lodash.flatten';
import intersection from 'lodash.intersection';
import neutrino from 'neutrino';
import {targetNeutrino} from '../server/commands';
import Flecks from '../server/flecks';
import D from '../debug';
import D from '../../debug';
import {targetNeutrino} from '../commands';
import Flecks from '../flecks';
const debug = D('@flecks/core/build/webpack.config.js');

View File

@ -50,10 +50,15 @@ export const targetNeutrinos = (flecks) => {
const [fleck, targets] = entries[i];
targets
.forEach((target) => {
targetNeutrinos[targetNeutrino(target)] = flecks.localConfig(
`${target}.neutrinorc.js`,
fleck,
{general: '.neutrinorc.js'},
targetNeutrinos[targetNeutrino(target)] = flecks.resolveBuildConfig(
[
FLECKS_CORE_ROOT,
flecks.resolvePath(fleck),
],
[
`${target}.neutrinorc.js`,
'.neutrinorc.js',
],
);
});
}
@ -107,7 +112,7 @@ export default (program, flecks) => {
verbose,
} = opts;
debug('Building...', opts);
const webpackConfig = flecks.localConfig('webpack.config.js', '@flecks/core');
const webpackConfig = flecks.buildConfig('webpack.config.js');
const cmd = [
'npx', 'webpack',
'--colors',
@ -144,11 +149,7 @@ export default (program, flecks) => {
process.env.FLECKS_CORE_BUILD_TARGET = target;
const cmd = [
'npx', 'eslint',
'--config', flecks.localConfig(
`${target}.eslintrc.js`,
'@flecks/core',
{general: '.eslintrc.js'},
),
'--config', flecks.buildConfig('.eslintrc.js', target),
'--format', 'codeframe',
'--ext', 'js',
'.',

View File

@ -45,7 +45,7 @@ export default class ServerFlecks extends Flecks {
Object.keys(this.flecks)
.sort((l, r) => (l < r ? 1 : -1))
.forEach((fleck) => {
const prefix = `FLECKS_ENV_${this.constructor.environmentalize(fleck).toUpperCase()}`;
const prefix = `FLECKS_ENV_${this.constructor.environmentalize(fleck)}`;
keys
.filter((key) => key.startsWith(`${prefix}_`) && -1 === seen.indexOf(key))
.map((key) => {
@ -66,6 +66,22 @@ export default class ServerFlecks extends Flecks {
}
});
});
this.buildConfigs = Object.fromEntries(
Object.entries(this.invoke('@flecks/core.build.config'))
.map(([fleck, configs]) => (
configs.map((config) => {
const defaults = {
fleck,
root: FLECKS_CORE_ROOT,
};
if (Array.isArray(config)) {
return [config[0], {...defaults, ...config[1]}];
}
return [config, defaults];
})
))
.flat(),
);
this.resolver = resolver;
this.rcs = rcs;
}
@ -263,11 +279,16 @@ export default class ServerFlecks extends Flecks {
debug('compiling: %s', root);
const resolved = dirname(R.resolve(join(root, 'package.json')));
const sourcepath = this.sourcepath(resolved);
const configFile = this.localConfig(
const configFile = this.resolveBuildConfig(
resolver,
'babel.config.js',
'@flecks/core',
{root: join(sourcepath, '..')},
[
resolved,
FLECKS_CORE_ROOT,
this.resolvePath(resolver, '@flecks/core/server'),
],
[
'babel.config.js',
],
);
const register = R('@babel/register');
const config = {
@ -354,11 +375,30 @@ export default class ServerFlecks extends Flecks {
});
}
buildConfig(path, specific) {
const config = this.buildConfigs[path];
if (!config) {
throw new Error(`Unknown build config '${path}'`);
}
const paths = [];
if (config.specifier) {
paths.push(config.specifier(specific));
}
paths.push(path);
const roots = [config.root];
if (config.root !== FLECKS_CORE_ROOT) {
roots.push(FLECKS_CORE_ROOT);
}
roots.push(this.resolvePath(this.resolve(config.fleck)));
return this.constructor.resolveBuildConfig(this.resolver, roots, paths);
}
static environmentalize(key) {
return key
// - `@flecks/core` -> `FLECKS_CORE`
.replace(/[^a-zA-Z0-9]/g, '_')
.replace(/_*(.*)_*/, '$1');
.replace(/_*(.*)_*/, '$1')
.toUpperCase();
}
fleckIsAliased(fleck) {
@ -379,50 +419,6 @@ export default class ServerFlecks extends Flecks {
return realpath !== resolved;
}
localConfig(path, fleck, options) {
return this.constructor.localConfig(this.resolver, path, fleck, options);
}
static localConfig(resolver, path, fleck, {general = path, root = FLECKS_CORE_ROOT} = {}) {
let configFile;
try {
const localConfig = join(root, 'build', path);
statSync(localConfig);
configFile = localConfig;
}
catch (error) {
try {
const localConfig = join(root, 'build', general);
statSync(localConfig);
configFile = localConfig;
}
catch (error) {
try {
const localConfig = join(FLECKS_CORE_ROOT, 'build', path);
statSync(localConfig);
configFile = localConfig;
}
catch (error) {
try {
const localConfig = join(FLECKS_CORE_ROOT, 'build', general);
statSync(localConfig);
configFile = localConfig;
}
catch (error) {
const resolved = this.resolve(resolver, fleck);
try {
configFile = R.resolve(join(resolved, 'build', path));
}
catch (error) {
configFile = R.resolve(join(resolved, 'build', general));
}
}
}
}
}
return configFile;
}
rcs() {
return this.rcs;
}
@ -435,6 +431,38 @@ export default class ServerFlecks extends Flecks {
return resolver[fleck] || fleck;
}
resolveBuildConfig(roots, paths) {
return this.constructor.resolveBuildConfig(this.resolver, roots, paths);
}
static resolveBuildConfig(resolver, roots, paths) {
for (let i = 0; i < roots.length; ++i) {
const root = roots[i];
for (let j = 0; j < paths.length; ++j) {
const path = paths[j];
const resolved = join(root, 'build', path);
try {
statSync(resolved);
return resolved;
}
// eslint-disable-next-line no-empty
catch (error) {}
}
}
throw new Error(`Couldn't resolve build file '${paths.pop()}'`);
}
resolvePath(path) {
return this.constructor.resolvePath(this.resolver, path);
}
static resolvePath(resolver, path) {
const resolved = R.resolve(this.resolve(resolver, path));
const ext = extname(resolved);
const base = basename(resolved, ext);
return join(dirname(resolved), 'index' === base ? '' : base);
}
root(fleck) {
return this.constructor.root(this.resolver, fleck);
}
@ -491,11 +519,7 @@ export default class ServerFlecks extends Flecks {
.forEach((root) => {
const resolved = dirname(R.resolve(join(root, 'package.json')));
const sourcepath = this.sourcepath(resolved);
const configFile = this.localConfig(
'babel.config.js',
'@flecks/core',
{root: resolved},
);
const configFile = this.buildConfig('babel.config.js');
debug('compiling: %s with %s', root, configFile);
const babel = {
configFile,
@ -503,8 +527,8 @@ export default class ServerFlecks extends Flecks {
...babelmerge(...rcBabel.map(([, babel]) => babel)),
};
compileLoader({
ignore: [dirname(sourcepath, '..')],
include: [dirname(sourcepath, '..')],
ignore: [join(sourcepath, '..')],
include: [join(sourcepath, '..')],
babel,
ruleId: `@flecks/${runtime}/runtime/compile[${root}]`,
})(neutrino);

View File

@ -23,13 +23,7 @@ export default {
if (-1 !== exclude.indexOf(target)) {
return;
}
const baseConfig = R(
flecks.localConfig(
`${target}.eslint.defaults.js`,
'@flecks/core',
{general: '.eslint.defaults.js'},
),
);
const baseConfig = R(flecks.buildConfig('.eslint.defaults.js', target));
config.use.unshift(
airbnb({
eslint: {
@ -43,6 +37,13 @@ export default {
}),
);
},
'@flecks/core.build.config': () => [
'babel.config.js',
['.eslint.defaults.js', {specifier: (specific) => `${specific}.eslint.defaults.js`}],
['.eslintrc.js', {specifier: (specific) => `${specific}.eslintrc.js`}],
['.neutrinorc.js', {specifier: (specific) => `${specific}.neutrinorc.js`}],
'webpack.config.js',
],
'@flecks/core.commands': commands,
},
};

View File

@ -57,7 +57,7 @@ export default (program, flecks) => {
services[key] = {image: config.image, environment: {}};
});
return [
`FLECKS_ENV_${flecks.constructor.environmentalize(fleck).toUpperCase()}`,
`FLECKS_ENV_${flecks.constructor.environmentalize(fleck)}`,
config,
];
}),

View File

@ -30,7 +30,7 @@ module.exports = (async () => {
config.use.unshift((neutrino) => {
neutrino.config.plugins.delete('start-server');
});
const configFile = flecks.localConfig('babel.config.js', '@flecks/core');
const configFile = flecks.buildConfig('babel.config.js');
config.use.unshift(node({
babel: {configFile},
clean: {

View File

@ -5,15 +5,15 @@ module.exports = (async () => {
// eslint-disable-next-line import/no-extraneous-dependencies, global-require
const config = await require('@flecks/fleck/server/build/fleck.neutrinorc');
config.use.push(({config}) => {
config.entryPoints.delete('build/template');
config.entryPoints.delete('server/build/template');
});
config.use.push(
copy({
copyUnmodified: true,
patterns: [
{
from: 'src/build/template.ejs',
to: 'build/template.ejs',
from: 'src/server/build/template.ejs',
to: 'server/build/template.ejs',
},
],
}),

View File

@ -19,7 +19,6 @@
},
"files": [
"build",
"build/template.ejs",
"client/tests.js",
"client/tests.js.map",
"entry.js",
@ -30,6 +29,7 @@
"runtime.js.map",
"server.js",
"server.js.map",
"server/build/template.ejs",
"server/build/http.neutrinorc.js",
"server/build/http.neutrinorc.js.map",
"src",

View File

@ -50,7 +50,7 @@ module.exports = (async () => {
hot: false,
html: {
inject: false,
template: flecks.localConfig('template.ejs', '@flecks/http'),
template: flecks.buildConfig('template.ejs'),
},
style: {
extract: {

View File

@ -22,7 +22,7 @@ export default {
'npx', 'webpack-dev-server',
'--mode', 'development',
'--hot',
'--config', flecks.localConfig('webpack.config.js', '@flecks/core'),
'--config', flecks.buildConfig('webpack.config.js'),
];
spawnWith(
cmd,
@ -36,6 +36,9 @@ export default {
// eslint-disable-next-line no-param-reassign
delete neutrinoConfigs.http;
},
'@flecks/core.build.config': () => [
'template.ejs',
],
'@flecks/core.config': () => ({
devHost: 'localhost',
devPort: undefined,

View File

@ -11,7 +11,7 @@ export default {
hot: false,
html: {
inject: false,
template: flecks.localConfig('template.ejs', '@flecks/http'),
template: flecks.buildConfig('template.ejs'),
},
style: {
extract: {

View File

@ -18,7 +18,7 @@ export default {
},
'@flecks/http.config': async (
req,
{config: {'@flecks/socket': {'packets.decorate': decorators}}},
{config: {'@flecks/socket': {'packets.decorate': decorators = ['...']}}},
) => ({
'@flecks/socket': {
'packets.decorate': decorators.filter(