refactor: build

This commit is contained in:
cha0s 2024-01-22 09:16:07 -06:00
parent 7711c7cb2d
commit 0c8ff56850
104 changed files with 651 additions and 376 deletions

View File

@ -1,3 +1,4 @@
'@flecks/build': {}
'@flecks/core': '@flecks/core':
packageManager: yarn packageManager: yarn
'@flecks/create-app': {} '@flecks/create-app': {}

119
packages/build/.gitignore vendored Normal file
View File

@ -0,0 +1,119 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
# copy-webpack-plugin charity
.git

View File

@ -0,0 +1 @@
module.exports = require('./default.eslint.config')(require('./build').from());

View File

@ -1,12 +1,11 @@
const {realpath} = require('fs/promises'); const {realpath} = require('fs/promises');
const {dirname, join} = require('path'); const {dirname, join} = require('path');
const D = require('@flecks/core/build/debug');
const {Flecks} = require('@flecks/core/build/flecks');
const babelmerge = require('babel-merge'); const babelmerge = require('babel-merge');
const set = require('lodash.set');
const D = require('./debug');
const explicate = require('./explicate'); const explicate = require('./explicate');
const {Flecks} = require('./flecks');
const loadConfig = require('./load-config'); const loadConfig = require('./load-config');
const Resolver = require('./resolver'); const Resolver = require('./resolver');
@ -14,7 +13,7 @@ const {
FLECKS_CORE_ROOT = process.cwd(), FLECKS_CORE_ROOT = process.cwd(),
} = process.env; } = process.env;
const debug = D('@flecks/core/build/bootstrap'); const debug = D('@flecks/build/build/build');
const debugSilly = debug.extend('silly'); const debugSilly = debug.extend('silly');
function environmentalize(path) { function environmentalize(path) {
@ -39,11 +38,11 @@ function environmentConfiguration(config) {
.map(([subkey, value]) => [subkey.split('_'), value]) .map(([subkey, value]) => [subkey.split('_'), value])
.forEach(([path, jsonOrString]) => { .forEach(([path, jsonOrString]) => {
try { try {
set(config, [fleck, ...path], JSON.parse(jsonOrString)); Flecks.set(config, [fleck, ...path], JSON.parse(jsonOrString));
debug('read (%s) as JSON', jsonOrString); debug('read (%s) as JSON', jsonOrString);
} }
catch (error) { catch (error) {
set(config, [fleck, ...path], jsonOrString); Flecks.set(config, [fleck, ...path], jsonOrString);
debug('read (%s) as string', jsonOrString); debug('read (%s) as string', jsonOrString);
} }
}); });
@ -51,7 +50,7 @@ function environmentConfiguration(config) {
return config; return config;
} }
module.exports = class Server extends Flecks { module.exports = class Build extends Flecks {
aliased = {}; aliased = {};
@ -206,10 +205,6 @@ module.exports = class Server extends Flecks {
}; };
} }
get extensions() {
return this.invokeFlat('@flecks/core.exts').flat();
}
static async from( static async from(
{ {
config: configParameter, config: configParameter,
@ -247,7 +242,7 @@ module.exports = class Server extends Flecks {
} }
loadBuildConfigs() { loadBuildConfigs() {
Object.entries(this.invoke('@flecks/core.build.config')) Object.entries(this.invoke('@flecks/build.files'))
.forEach(([fleck, configs]) => { .forEach(([fleck, configs]) => {
configs.forEach((config) => { configs.forEach((config) => {
this.buildConfigs[config] = fleck; this.buildConfigs[config] = fleck;
@ -298,16 +293,16 @@ module.exports = class Server extends Flecks {
{ {
flecks, flecks,
path, path,
resolved,
source, source,
}, },
]) => { ]) => {
flecks.forEach((fleck) => { allowlist.push(new RegExp(`^${path}`));
allowlist.push(fleck); // flecks.forEach((fleck) => {
}); // allowlist.push(fleck);
debugSilly('%s runtime de-externalized %s, alias: %s', runtime, root, resolved); // });
debugSilly('%s runtime de-externalized %s, alias: %s', runtime, root, source || path);
// Alias. // Alias.
config.resolve.alias[path] = source || resolved; config.resolve.alias[path] = source || path;
// Root aliases. // Root aliases.
if (root) { if (root) {
config.resolve.alias[ config.resolve.alias[
@ -315,7 +310,7 @@ module.exports = class Server extends Flecks {
] = join(FLECKS_CORE_ROOT, 'node_modules'); ] = join(FLECKS_CORE_ROOT, 'node_modules');
config.resolve.fallback[path] = root; config.resolve.fallback[path] = root;
} }
includes.push(root || resolved); includes.push(root || path);
}), }),
); );
// Compile. // Compile.
@ -347,7 +342,7 @@ module.exports = class Server extends Flecks {
} }
}); });
// Our very own lil' chunk. // Our very own lil' chunk.
set(config, 'optimization.splitChunks.cacheGroups.flecks-compiled', { Flecks.set(config, 'optimization.splitChunks.cacheGroups.flecks-compiled', {
chunks: 'all', chunks: 'all',
enforce: true, enforce: true,
priority: 100, priority: 100,

View File

@ -0,0 +1,12 @@
const Build = require('./build');
const configFn = require('./fleck.webpack.config');
const {ProcessAssets} = require('./process-assets');
const {executable} = require('./webpack');
module.exports = async (env, argv) => {
const flecks = await Build.from();
const config = await configFn(env, argv, flecks);
config.plugins.push(new ProcessAssets());
config.plugins.push(executable());
return config;
};

View File

@ -1,12 +1,12 @@
#!/usr/bin/env node #!/usr/bin/env node
const D = require('@flecks/core/build/debug');
const {Command} = require('commander'); const {Command} = require('commander');
const Build = require('./build');
const {processCode} = require('./commands'); const {processCode} = require('./commands');
const D = require('./debug');
const Server = require('./server');
const debug = D('@flecks/core/cli'); const debug = D('@flecks/build/build/cli');
const debugSilly = debug.extend('silly'); const debugSilly = debug.extend('silly');
// Asynchronous command process code forwarding. // Asynchronous command process code forwarding.
@ -38,10 +38,10 @@ program
// Bootstrap. // Bootstrap.
(async () => { (async () => {
debugSilly('bootstrapping flecks...'); debugSilly('bootstrapping flecks...');
const flecks = await Server.from(); const flecks = await Build.from();
debugSilly('bootstrapped'); debugSilly('bootstrapped');
// Register commands. // Register commands.
const commands = flecks.invokeMerge('@flecks/core.commands', program); const commands = flecks.invokeMerge('@flecks/build.commands', program);
const keys = Object.keys(commands).sort(); const keys = Object.keys(commands).sort();
for (let i = 0; i < keys.length; ++i) { for (let i = 0; i < keys.length; ++i) {
const { const {

View File

@ -1,23 +1,23 @@
const {spawn} = require('child_process'); const {spawn} = require('child_process');
const {join, normalize} = require('path'); const {join, normalize} = require('path');
const D = require('@flecks/core/build/debug');
const {Argument, Option, program} = require('commander'); const {Argument, Option, program} = require('commander');
const {glob} = require('glob'); const {glob} = require('glob');
const rimraf = require('rimraf'); const rimraf = require('rimraf');
const D = require('./debug');
const addFleckToYml = require('./add-fleck-to-yml'); const addFleckToYml = require('./add-fleck-to-yml');
const { const {
FLECKS_CORE_ROOT = process.cwd(), FLECKS_CORE_ROOT = process.cwd(),
} = process.env; } = process.env;
const debug = D('@flecks/core/commands'); const debug = D('@flecks/build/build/commands');
const debugSilly = debug.extend('silly'); const debugSilly = debug.extend('silly');
const flecksRoot = normalize(FLECKS_CORE_ROOT); const flecksRoot = normalize(FLECKS_CORE_ROOT);
exports.commands = (program, flecks) => { exports.commands = (program, flecks) => {
const {packageManager} = flecks.get('@flecks/core'); const {packageManager} = flecks.get('@flecks/build');
const commands = { const commands = {
add: { add: {
args: [ args: [
@ -33,7 +33,7 @@ exports.commands = (program, flecks) => {
args.push(packageManager, ['install', fleck]); args.push(packageManager, ['install', fleck]);
} }
args.push({stdio: 'inherit'}); args.push({stdio: 'inherit'});
await module.exports.processCode(spawn(...args)); await exports.processCode(spawn(...args));
await addFleckToYml(fleck); await addFleckToYml(fleck);
}, },
}, },
@ -62,7 +62,8 @@ exports.commands = (program, flecks) => {
if (targets.length > 0) { if (targets.length > 0) {
commands.build = { commands.build = {
args: [ args: [
new Argument('[target]', 'build target').choices(targets.map(([, target]) => target)), program.createArgument('[target]', 'build target')
.choices(targets.map(([, target]) => target)),
], ],
options: [ options: [
['-d, --no-production', 'dev build'], ['-d, --no-production', 'dev build'],
@ -84,7 +85,7 @@ exports.commands = (program, flecks) => {
'--mode', (production && !hot) ? 'production' : 'development', '--mode', (production && !hot) ? 'production' : 'development',
...((watch || hot) ? ['--watch'] : []), ...((watch || hot) ? ['--watch'] : []),
]; ];
return module.exports.spawnWith( return exports.spawnWith(
cmd, cmd,
{ {
env: { env: {
@ -116,7 +117,7 @@ exports.commands = (program, flecks) => {
'.', '.',
]; ];
promises.push(new Promise((resolve, reject) => { promises.push(new Promise((resolve, reject) => {
const child = module.exports.spawnWith( const child = exports.spawnWith(
cmd, cmd,
{ {
cwd, cwd,

View File

@ -7,10 +7,11 @@ const {
} = require('fs'); } = require('fs');
const {join} = require('path'); const {join} = require('path');
const D = require('./debug'); const D = require('@flecks/core/build/debug');
const Server = require('./server');
const debug = D('@flecks/core/build/eslint.config.js'); const Build = require('./build');
const debug = D('@flecks/build/build/eslint.config.js');
const { const {
FLECKS_CORE_ROOT = process.cwd(), FLECKS_CORE_ROOT = process.cwd(),
@ -21,7 +22,7 @@ const {
if (FLECKS_CORE_SYNC_FOR_ESLINT) { if (FLECKS_CORE_SYNC_FOR_ESLINT) {
(async () => { (async () => {
debug('bootstrapping flecks...'); debug('bootstrapping flecks...');
const flecks = await Server.from(); const flecks = await Build.from();
debug('bootstrapped'); debug('bootstrapped');
// Load and finalize ESLint configuration. // Load and finalize ESLint configuration.
const eslintConfig = await require( const eslintConfig = await require(
@ -64,8 +65,14 @@ else {
module.exports = parsed; module.exports = parsed;
} }
catch (error) { catch (error) {
if (error.message.match(/Unexpected .*? in JSON/)) {
// eslint-disable-next-line no-console
console.error('Expected JSON, got:\n%s', json);
}
else {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.error(error); console.error(error);
} }
} }
} }
}

View File

@ -37,9 +37,6 @@ module.exports = async function explicate(
) { ) {
return; return;
} }
if (path !== request) {
resolver.addAlias(path, request);
}
descriptors[request] = descriptor; descriptors[request] = descriptor;
} }
async function getRootDescriptor(descriptor) { async function getRootDescriptor(descriptor) {
@ -86,23 +83,35 @@ module.exports = async function explicate(
if (resolved) { if (resolved) {
await doExplication(descriptor); await doExplication(descriptor);
} }
if (descriptor.path !== descriptor.request) {
resolver.addAlias(descriptor.path, descriptor.request);
}
await Promise.all( await Promise.all(
platforms platforms
.filter((platform) => !platform.startsWith('!')) .filter((platform) => !platform.startsWith('!'))
.map(async (platform) => { .map(async (platform) => {
if (await resolver.resolve(join(descriptor.request, platform))) { if (await resolver.resolve(join(descriptor.request, platform))) {
return doExplication({ const [path, request] = [
path: join(descriptor.path, platform), join(descriptor.path, platform),
request: join(descriptor.request, platform), join(descriptor.request, platform),
}); ];
await doExplication({path, request});
if (path !== request) {
resolver.addAlias(path, request);
}
return;
} }
if (await resolver.resolve(join(descriptor.request, 'src', platform))) { if (await resolver.resolve(join(descriptor.request, 'src', platform))) {
return doExplication({ const [path, request] = [
path: join(descriptor.path, platform), join(descriptor.path, platform),
request: join(descriptor.request, 'src', platform), join(descriptor.request, 'src', platform),
}); ];
await doExplication({path, request});
if (path !== request) {
resolver.addAlias(path, request);
}
return;
} }
return undefined;
}), }),
); );
} }

View File

@ -0,0 +1,59 @@
const {join} = require('path');
const webpack = require('webpack');
const {commands} = require('./commands');
const {
FLECKS_CORE_ROOT = process.cwd(),
} = process.env;
exports.hooks = {
'@flecks/build.extensions': () => ['.mjs', '.js', '.json', '.wasm'],
'@flecks/build.config': async (target, config, env, argv, flecks) => {
if (flecks.get('@flecks/build.profile').includes(target)) {
config.plugins.push(
new webpack.debug.ProfilingPlugin({
outputPath: join(FLECKS_CORE_ROOT, `profile.build-${target}.json`),
}),
);
}
if (Object.entries(flecks.compiled).length > 0) {
config.resolve.symlinks = false;
}
},
'@flecks/build.files': () => [
/**
* Babel configuration. See: https://babeljs.io/docs/en/config-files
*/
'babel.config.js',
/**
* ESLint defaults. The generated `eslint.config.js` just reads from this file so that the
* build can dynamically configure parts of ESLint.
*/
'default.eslint.config.js',
/**
* ESLint configuration managed by flecks to allow async.
*/
'eslint.config.js',
/**
* Flecks webpack configuration. See: https://webpack.js.org/configuration/
*/
'fleckspack.config.js',
/**
* Fleck build configuration. See: https://webpack.js.org/configuration/
*/
'fleck.webpack.config.js',
],
'@flecks/build.commands': commands,
'@flecks/core.config': () => ({
/**
* The package manager used for tasks.
*/
packageManager: 'npm',
/**
* Build targets to profile with `webpack.debug.ProfilingPlugin`.
*/
profile: [],
}),
};

View File

@ -1,9 +1,10 @@
require('source-map-support/register'); require('source-map-support/register');
const D = require('./debug'); const D = require('@flecks/core/build/debug');
const Server = require('./server');
const debug = D('@flecks/core/build/fleckspack.config.js'); const Build = require('./build');
const debug = D('@flecks/build/build/fleckspack.config.js');
const { const {
FLECKS_CORE_BUILD_LIST = '', FLECKS_CORE_BUILD_LIST = '',
@ -16,7 +17,7 @@ const buildList = FLECKS_CORE_BUILD_LIST
module.exports = async (env, argv) => { module.exports = async (env, argv) => {
debug('bootstrapping flecks...'); debug('bootstrapping flecks...');
const flecks = await Server.from(); const flecks = await Build.from();
debug('bootstrapped'); debug('bootstrapped');
debug('gathering configs'); debug('gathering configs');
const {targets} = flecks; const {targets} = flecks;
@ -43,11 +44,11 @@ module.exports = async (env, argv) => {
)); ));
await Promise.all( await Promise.all(
entries.map(async ([target, config]) => ( entries.map(async ([target, config]) => (
Promise.all(flecks.invokeFlat('@flecks/core.build', target, config, env, argv)) Promise.all(flecks.invokeFlat('@flecks/build.config', target, config, env, argv))
)), )),
); );
const webpackConfigs = Object.fromEntries(entries); const webpackConfigs = Object.fromEntries(entries);
await Promise.all(flecks.invokeFlat('@flecks/core.build.alter', webpackConfigs, env, argv)); await Promise.all(flecks.invokeFlat('@flecks/build.config.alter', webpackConfigs, env, argv));
const enterableWebpackConfigs = Object.values(webpackConfigs) const enterableWebpackConfigs = Object.values(webpackConfigs)
.filter((webpackConfig) => { .filter((webpackConfig) => {
if (!webpackConfig.entry) { if (!webpackConfig.entry) {

View File

@ -1,9 +1,9 @@
const {readFile} = require('fs/promises'); const {readFile} = require('fs/promises');
const {join} = require('path'); const {join} = require('path');
const D = require('./debug'); const D = require('@flecks/core/build/debug');
const debug = D('@flecks/core:load-config'); const debug = D('@flecks/build/build/load-config');
const { const {
FLECKS_CORE_ROOT = process.cwd(), FLECKS_CORE_ROOT = process.cwd(),
@ -22,7 +22,11 @@ module.exports = async function loadConfig() {
throw error; throw error;
} }
const {name} = require(join(FLECKS_CORE_ROOT, 'package.json')); const {name} = require(join(FLECKS_CORE_ROOT, 'package.json'));
const barebones = {'@flecks/core': {}, '@flecks/fleck': {}}; const barebones = {
'@flecks/build': {},
'@flecks/core': {},
'@flecks/fleck': {},
};
if (barebones[name]) { if (barebones[name]) {
delete barebones[name]; delete barebones[name];
} }

View File

@ -0,0 +1,74 @@
const {join} = require('path');
const {glob} = require('glob');
const {
FLECKS_CORE_ROOT = process.cwd(),
} = process.env;
exports.ProcessAssets = class ProcessAssets {
constructor(flecks) {
this.flecks = flecks;
}
apply(compiler) {
compiler.hooks.thisCompilation.tap('@flecks/build.process-assets', (compilation) => {
compilation.hooks.processAssets.tapAsync(
{
name: '@flecks/build.process-assets',
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_REPORT,
},
async (assets, callback) => {
if (this.flecks) {
await this.flecks.invokeSequentialAsync(
'@flecks/build.processAssets',
assets,
compilation,
);
}
else {
await exports.hook(assets, compilation);
}
callback();
},
);
});
}
};
exports.hook = async (assets, compilation, flecks) => {
const {RawSource} = compilation.compiler.webpack.sources;
const packageJson = assets['package.json'];
const json = JSON.parse(packageJson.source().toString());
const {files} = json;
// Add defaults.
files.push('build');
// Add source if it exists.
if ((await glob(join(FLECKS_CORE_ROOT, 'src/**/*.js'))).length > 0) {
files.push('src');
}
// Add tests if they exist.
const testFiles = await glob(join(FLECKS_CORE_ROOT, 'test/**/*.js'));
if (testFiles.length > 0) {
files.push('test');
}
// Let others have a say.
if (flecks) {
await flecks.invokeSequentialAsync('@flecks/build.packageJson', json, compilation);
}
// Add any sourcemaps.
json.files = json.files
.map((filename) => {
const maybeWithMap = [filename];
if (compilation.assets[`${filename}.map`]) {
maybeWithMap.push(`${filename}.map`);
}
return maybeWithMap;
})
.flat();
// Sort and uniquify.
json.files = [...new Set(json.files.sort((l, r) => (l < r ? -1 : 1)))];
compilation.updateAsset('package.json', new RawSource(JSON.stringify(json, null, 2)));
};

View File

@ -1,13 +1,12 @@
const {join} = require('path'); const {join} = require('path');
const D = require('@flecks/core/build/debug');
const {CachedInputFileSystem, ResolverFactory} = require('enhanced-resolve'); const {CachedInputFileSystem, ResolverFactory} = require('enhanced-resolve');
const AppendPlugin = require('enhanced-resolve/lib/AppendPlugin'); const AppendPlugin = require('enhanced-resolve/lib/AppendPlugin');
const AliasPlugin = require('enhanced-resolve/lib/AliasPlugin'); const AliasPlugin = require('enhanced-resolve/lib/AliasPlugin');
const fs = require('graceful-fs'); const fs = require('graceful-fs');
const D = require('./debug'); const debug = D('@flecks/build/build/resolver');
const debug = D('@flecks/core/build/resolver');
const debugSilly = debug.extend('silly'); const debugSilly = debug.extend('silly');
const { const {

View File

@ -20,7 +20,7 @@ exports.banner = (options) => (
exports.copy = (options) => (new CopyPlugin(options)); exports.copy = (options) => (new CopyPlugin(options));
exports.defaultConfig = (flecks, specializedConfig) => { exports.defaultConfig = (flecks, specializedConfig) => {
const {extensions} = flecks; const extensions = flecks.invokeFlat('@flecks/build.extensions').flat();
const extensionsRegex = exports.regexFromExtensions(extensions); const extensionsRegex = exports.regexFromExtensions(extensions);
const defaults = { const defaults = {
context: FLECKS_CORE_ROOT, context: FLECKS_CORE_ROOT,
@ -82,7 +82,7 @@ exports.defaultConfig = (flecks, specializedConfig) => {
}; };
// Include a shebang and set the executable bit.. // Include a shebang and set the executable bit..
exports.executable = () => ([ exports.executable = () => (
new class Executable { new class Executable {
// eslint-disable-next-line class-methods-use-this // eslint-disable-next-line class-methods-use-this
@ -95,8 +95,8 @@ exports.executable = () => ([
); );
} }
}(), }()
]); );
exports.externals = nodeExternals; exports.externals = nodeExternals;

View File

@ -0,0 +1,52 @@
{
"name": "@flecks/build",
"version": "3.0.0",
"scripts": {
"build": "NODE_PATH=./node_modules webpack --config ./build/build.webpack.config.js --mode production",
"clean": "rm -rf dist yarn.lock && yarn",
"lint": "NODE_PATH=./node_modules eslint --config ./build/eslint.config.js .",
"postversion": "cp package.json dist",
"test": "npm run build -d && mocha -t 10000 --colors ./dist/test.js"
},
"bin": {
"flecks": "./build/cli.js"
},
"files": [
"server.js"
],
"dependencies": {
"@babel/core": "^7.12.10",
"@babel/eslint-parser": "^7.23.3",
"@babel/eslint-plugin": "^7.22.10",
"@babel/plugin-transform-regenerator": "^7.16.7",
"@babel/preset-env": "^7.12.11",
"@flecks/core": "^3.0.0",
"babel-loader": "^9.1.3",
"babel-merge": "^3.0.0",
"chai": "4.2.0",
"chai-as-promised": "7.1.1",
"commander": "11.1.0",
"copy-webpack-plugin": "^11.0.0",
"enhanced-resolve": "^5.9.2",
"eslint": "^7.0.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-import-resolver-webpack": "^0.13.8",
"eslint-plugin-import": "^2.29.0",
"eslint-plugin-jsx-a11y": "^6.8.0",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-webpack-plugin": "^3.2.0",
"glob": "^10.3.10",
"globals": "^13.23.0",
"graceful-fs": "^4.2.11",
"js-yaml": "4.1.0",
"mocha": "^8.3.2",
"rimraf": "^3.0.2",
"source-map-loader": "4.0.1",
"source-map-support": "0.5.19",
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4",
"webpack-node-externals": "^3.0.0"
}
}

View File

@ -0,0 +1,5 @@
export {dump as dumpYml, load as loadYml} from 'js-yaml';
export {default as webpack} from 'webpack';
export * from '../build/webpack';

View File

@ -2,8 +2,8 @@ import {join} from 'path';
import {expect} from 'chai'; import {expect} from 'chai';
import explicate from '@flecks/core/build/explicate'; import explicate from '@flecks/build/build/explicate';
import Resolver from '@flecks/core/build/resolver'; import Resolver from '@flecks/build/build/resolver';
const { const {
FLECKS_CORE_ROOT = process.cwd(), FLECKS_CORE_ROOT = process.cwd(),

View File

@ -2,7 +2,7 @@ import {join} from 'path';
import {expect} from 'chai'; import {expect} from 'chai';
import Resolver from '@flecks/core/build/resolver'; import Resolver from '@flecks/build/build/resolver';
const { const {
FLECKS_CORE_ROOT = process.cwd(), FLECKS_CORE_ROOT = process.cwd(),

View File

@ -1,4 +0,0 @@
const defaultConfigFn = require('./default.eslint.config');
const Server = require('./server');
module.exports = defaultConfigFn(Server.from());

View File

@ -1,10 +1,10 @@
const Server = require('./server'); const Build = require('../../build/build/build');
const configFn = require('./fleck.webpack.config'); const configFn = require('../../build/build/fleck.webpack.config');
const {executable} = require('./webpack'); const {ProcessAssets} = require('../../build/build/process-assets');
module.exports = async (env, argv) => { module.exports = async (env, argv) => {
const flecks = await Server.from(); const flecks = await Build.from();
const config = await configFn(env, argv, flecks); const config = await configFn(env, argv, flecks);
config.plugins.push(...executable()); config.plugins.push(new ProcessAssets());
return config; return config;
}; };

View File

@ -21,7 +21,7 @@ module.exports = (name) => {
D.formatters.o = undefined; D.formatters.o = undefined;
D.formatters.O = undefined; D.formatters.O = undefined;
} }
const type = 'server' === process.env.FLECKS_CORE_BUILD_TARGET ? 'error' : 'debug'; const type = 'web' === process.env.FLECKS_CORE_BUILD_TARGET ? 'debug' : 'error';
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
D.log = console[type].bind(console); D.log = console[type].bind(console);
} }

View File

@ -1,63 +1,14 @@
const {join} = require('path'); const {inspect: {defaultOptions}} = require('util');
const webpack = require('webpack'); defaultOptions.breakLength = 160;
defaultOptions.compact = 6;
const {commands} = require('./commands'); defaultOptions.sorted = true;
const {
FLECKS_CORE_ROOT = process.cwd(),
} = process.env;
exports.hooks = { exports.hooks = {
'@flecks/core.exts': () => ['.mjs', '.js', '.json', '.wasm'],
'@flecks/core.build': async (target, config, env, argv, flecks) => {
if (flecks.get('@flecks/core.profile').includes(target)) {
config.plugins.push(
new webpack.debug.ProfilingPlugin({
outputPath: join(FLECKS_CORE_ROOT, `profile.build-${target}.json`),
}),
);
}
if (Object.entries(flecks.compiled).length > 0) {
config.resolve.symlinks = false;
}
},
'@flecks/core.build.config': () => [
/**
* Babel configuration. See: https://babeljs.io/docs/en/config-files
*/
'babel.config.js',
/**
* ESLint defaults. The generated `eslint.config.js` just reads from this file so that the
* build can dynamically configure parts of ESLint.
*/
'default.eslint.config.js',
/**
* ESLint configuration managed by flecks to allow async.
*/
'eslint.config.js',
/**
* Flecks webpack configuration. See: https://webpack.js.org/configuration/
*/
'fleckspack.config.js',
/**
* Fleck build configuration. See: https://webpack.js.org/configuration/
*/
'fleck.webpack.config.js',
],
'@flecks/core.commands': commands,
'@flecks/core.config': () => ({ '@flecks/core.config': () => ({
/** /**
* The ID of your application. * The ID of your application.
*/ */
id: 'flecks', id: 'flecks',
/**
* The package manager used for tasks.
*/
packageManager: 'npm',
/**
* Build targets to profile with `webpack.debug.ProfilingPlugin`.
*/
profile: [],
}), }),
}; };

View File

@ -462,6 +462,26 @@ exports.Flecks = class Flecks {
.reduce((r, fleck) => ({...r, [fleck]: this.invokeFleck(hook, fleck, ...args)}), {}); .reduce((r, fleck) => ({...r, [fleck]: this.invokeFleck(hook, fleck, ...args)}), {});
} }
/**
* Return an object whose keys are fleck paths and values are the `await`ed result of invoking the hook.
* @param {string} hook
* @param {...any} args Arguments passed to each implementation.
* @returns {*}
*/
async invokeAsync(hook, ...args) {
if (!this.hooks[hook]) {
return {};
}
return this.flecksImplementing(hook)
.reduce(
async (r, fleck) => ({
...(await r),
[fleck]: await this.invokeFleck(hook, fleck, ...args),
}),
{},
);
}
/** /**
* See: [function composition](https://www.educative.io/edpresso/function-composition-in-javascript). * See: [function composition](https://www.educative.io/edpresso/function-composition-in-javascript).
* *

View File

@ -0,0 +1,2 @@
'@flecks/build:../build': {}
'@flecks/core:.': {}

View File

@ -12,13 +12,10 @@
"main": "index.js", "main": "index.js",
"author": "cha0s", "author": "cha0s",
"license": "MIT", "license": "MIT",
"bin": {
"flecks": "./build/cli.js"
},
"scripts": { "scripts": {
"build": "NODE_PATH=./node_modules webpack --config ./build/core.webpack.config.js --mode production", "build": "NODE_PATH=./node_modules webpack --config ./build/core.webpack.config.js --mode production",
"clean": "rm -rf dist bun.lockb && bun install", "clean": "rm -rf yarn.lock && yarn",
"lint": "NODE_PATH=./node_modules eslint --config ./build/eslint.config.js .", "lint": "NODE_PATH=./node_modules eslint --config ../build/build/eslint.config.js .",
"postversion": "cp package.json dist", "postversion": "cp package.json dist",
"test": "npm run build -d && mocha -t 10000 --colors ./dist/test.js" "test": "npm run build -d && mocha -t 10000 --colors ./dist/test.js"
}, },
@ -34,6 +31,15 @@
"test" "test"
], ],
"dependencies": { "dependencies": {
"debug": "4.3.1",
"glob": "^10.3.10",
"jsonparse": "^1.3.1",
"lodash.get": "^4.4.2",
"lodash.set": "^4.3.2",
"source-map-support": "0.5.19",
"supports-color": "9.2.1"
},
"devDependencies": {
"@babel/core": "^7.12.10", "@babel/core": "^7.12.10",
"@babel/eslint-parser": "^7.23.3", "@babel/eslint-parser": "^7.23.3",
"@babel/eslint-plugin": "^7.22.10", "@babel/eslint-plugin": "^7.22.10",
@ -43,10 +49,7 @@
"babel-merge": "^3.0.0", "babel-merge": "^3.0.0",
"chai": "4.2.0", "chai": "4.2.0",
"chai-as-promised": "7.1.1", "chai-as-promised": "7.1.1",
"commander": "11.1.0",
"copy-webpack-plugin": "^11.0.0", "copy-webpack-plugin": "^11.0.0",
"debug": "4.3.1",
"enhanced-resolve": "^5.9.2",
"eslint": "^7.0.0", "eslint": "^7.0.0",
"eslint-config-airbnb": "^19.0.4", "eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-base": "^15.0.0", "eslint-config-airbnb-base": "^15.0.0",
@ -56,19 +59,10 @@
"eslint-plugin-react": "^7.33.2", "eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",
"eslint-webpack-plugin": "^3.2.0", "eslint-webpack-plugin": "^3.2.0",
"glob": "^10.3.10",
"globals": "^13.23.0", "globals": "^13.23.0",
"graceful-fs": "^4.2.11",
"js-yaml": "4.1.0",
"jsonparse": "^1.3.1",
"lodash.get": "^4.4.2",
"lodash.set": "^4.3.2",
"mocha": "^8.3.2", "mocha": "^8.3.2",
"null-loader": "^4.0.1",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"source-map-loader": "4.0.1", "source-map-loader": "4.0.1",
"source-map-support": "0.5.19",
"supports-color": "9.2.1",
"webpack": "^5.89.0", "webpack": "^5.89.0",
"webpack-cli": "^5.1.4", "webpack-cli": "^5.1.4",
"webpack-node-externals": "^3.0.0" "webpack-node-externals": "^3.0.0"

View File

@ -1,27 +1,11 @@
import {inspect} from 'util';
import webpack from 'webpack';
const {defaultOptions} = inspect;
defaultOptions.breakLength = 160;
defaultOptions.compact = 6;
defaultOptions.sorted = true;
export {glob} from 'glob'; export {glob} from 'glob';
export {dump as dumpYml, load as loadYml} from 'js-yaml';
export {commands, processCode, spawnWith} from '../../build/commands';
export {JsonStream, transform} from '../../build/stream'; export {JsonStream, transform} from '../../build/stream';
export * from '../../build/webpack';
export {webpack};
export const hooks = { export const hooks = {
'@flecks/web.config': async (req, flecks) => ({ '@flecks/web.config': async (req, flecks) => ({
'@flecks/core': { '@flecks/core': {
id: flecks.get('@flecks/core.id'), id: flecks.get('@flecks/core.id'),
packageManager: undefined,
profile: undefined,
}, },
}), }),
}; };

View File

@ -1,10 +1,10 @@
const {copy, executable} = require('@flecks/core/server'); const {copy, executable} = require('@flecks/build/server');
// eslint-disable-next-line import/no-extraneous-dependencies // eslint-disable-next-line import/no-extraneous-dependencies
const configFn = require('@flecks/fleck/build/fleck.webpack.config'); const configFn = require('@flecks/fleck/build/fleck.webpack.config');
module.exports = async (env, argv, flecks) => { module.exports = async (env, argv, flecks) => {
const config = await configFn(env, argv, flecks); const config = await configFn(env, argv, flecks);
config.plugins.push(...executable()); config.plugins.push(executable());
config.plugins.push(copy({ config.plugins.push(copy({
patterns: [ patterns: [
{ {

View File

@ -29,6 +29,7 @@
"validate-npm-package-name": "^3.0.0" "validate-npm-package-name": "^3.0.0"
}, },
"devDependencies": { "devDependencies": {
"@flecks/build": "^3.0.0",
"@flecks/fleck": "^3.0.0" "@flecks/fleck": "^3.0.0"
} }
} }

View File

@ -14,6 +14,7 @@
"@flecks/server": "3.0.0" "@flecks/server": "3.0.0"
}, },
"devDependencies": { "devDependencies": {
"@flecks/build": "3.0.0",
"@flecks/create-fleck": "3.0.0", "@flecks/create-fleck": "3.0.0",
"lerna": "^3.22.1" "lerna": "^3.22.1"
} }

View File

@ -3,9 +3,9 @@
const {stat} = require('fs/promises'); const {stat} = require('fs/promises');
const {join} = require('path'); const {join} = require('path');
const addFleckToYml = require('@flecks/core/build/add-fleck-to-yml'); const Build = require('@flecks/build/build/build');
const {program} = require('@flecks/core/build/commands'); const addFleckToYml = require('@flecks/build/build/add-fleck-to-yml');
const Server = require('@flecks/core/build/server'); const {program} = require('@flecks/build/build/commands');
const {transform} = require('@flecks/core/server'); const {transform} = require('@flecks/core/server');
const build = require('@flecks/create-app/build/build'); const build = require('@flecks/create-app/build/build');
const {move, testDestination} = require('@flecks/create-app/build/move'); const {move, testDestination} = require('@flecks/create-app/build/move');
@ -67,7 +67,7 @@ const target = async (fleck) => {
program.action(async (fleck, o) => { program.action(async (fleck, o) => {
const {alias, add} = o; const {alias, add} = o;
try { try {
const flecks = await Server.from(); const flecks = await Build.from();
const {packageManager} = flecks.get('@flecks/core'); const {packageManager} = flecks.get('@flecks/core');
const isMonorepo = await checkIsMonorepo(); const isMonorepo = await checkIsMonorepo();
const [scope, pkg] = await target(fleck); const [scope, pkg] = await target(fleck);

View File

@ -1,10 +1,10 @@
const {copy, executable} = require('@flecks/core/server'); const {copy, executable} = require('@flecks/build/server');
// eslint-disable-next-line import/no-extraneous-dependencies // eslint-disable-next-line import/no-extraneous-dependencies
const configFn = require('@flecks/fleck/build/fleck.webpack.config'); const configFn = require('@flecks/fleck/build/fleck.webpack.config');
module.exports = async (env, argv, flecks) => { module.exports = async (env, argv, flecks) => {
const config = await configFn(env, argv, flecks); const config = await configFn(env, argv, flecks);
config.plugins.push(...executable()); config.plugins.push(executable());
config.plugins.push(copy({ config.plugins.push(copy({
patterns: [ patterns: [
{ {

View File

@ -27,6 +27,7 @@
"@flecks/create-app": "^3.0.0" "@flecks/create-app": "^3.0.0"
}, },
"devDependencies": { "devDependencies": {
"@flecks/build": "^3.0.0",
"@flecks/fleck": "^3.0.0" "@flecks/fleck": "^3.0.0"
} }
} }

View File

@ -14,6 +14,7 @@
"@flecks/core": "3.0.0" "@flecks/core": "3.0.0"
}, },
"devDependencies": { "devDependencies": {
"@flecks/build": "3.0.0",
"@flecks/fleck": "3.0.0" "@flecks/fleck": "3.0.0"
} }
} }

View File

@ -25,6 +25,7 @@
"sqlite3": "^5.0.2" "sqlite3": "^5.0.2"
}, },
"devDependencies": { "devDependencies": {
"@flecks/build": "^3.0.0",
"@flecks/fleck": "^3.0.0" "@flecks/fleck": "^3.0.0"
} }
} }

View File

@ -1,7 +1,7 @@
const FlecksDockerOutput = require('./plugin'); const FlecksDockerOutput = require('./plugin');
exports.hooks = { exports.hooks = {
'@flecks/core.build': (target, config, env, argv, flecks) => { '@flecks/build.config': (target, config, env, argv, flecks) => {
if ('server' !== target) { if ('server' !== target) {
return; return;
} }

View File

@ -1,4 +1,4 @@
const {dumpYml} = require('@flecks/core/server'); const {dumpYml} = require('@flecks/build/server');
const {generateComposeConfig, generateDockerFile} = require('./generate'); const {generateComposeConfig, generateDockerFile} = require('./generate');

View File

@ -25,6 +25,7 @@
"debug": "^4.3.3" "debug": "^4.3.3"
}, },
"devDependencies": { "devDependencies": {
"@flecks/build": "^3.0.0",
"@flecks/fleck": "^3.0.0" "@flecks/fleck": "^3.0.0"
} }
} }

View File

@ -2,7 +2,7 @@ import startContainer from './start-container';
export const hooks = { export const hooks = {
'@flecks/server.up': async (flecks) => { '@flecks/server.up': async (flecks) => {
if (!flecks.get('@flecks/docker/server.enabled')) { if (!flecks.get('@flecks/docker.enabled')) {
return; return;
} }
const containers = await flecks.invokeMergeAsync('@flecks/docker.containers'); const containers = await flecks.invokeMergeAsync('@flecks/docker.containers');

View File

@ -7,8 +7,6 @@ const {
} = require('fs/promises'); } = require('fs/promises');
const {dirname, join} = require('path'); const {dirname, join} = require('path');
const {Argument} = require('@flecks/core/build/commands');
const { const {
generate, generate,
resolveSiteDir, resolveSiteDir,
@ -21,7 +19,7 @@ const {
module.exports = (program, flecks) => { module.exports = (program, flecks) => {
const commands = {}; const commands = {};
const siteDirArgument = new Argument('[siteDir]', 'Docusaurus directory'); const siteDirArgument = program.createArgument('[siteDir]', 'Docusaurus directory');
siteDirArgument.defaultValue = 'website'; siteDirArgument.defaultValue = 'website';
commands.docusaurus = { commands.docusaurus = {
description: 'create a documentation website for this project', description: 'create a documentation website for this project',
@ -79,7 +77,8 @@ module.exports = (program, flecks) => {
} }
}, },
args: [ args: [
new Argument('subcommand', 'Docusaurus command to run').choices(['build', 'create', 'start']), program.createArgument('subcommand', 'Docusaurus command to run')
.choices(['build', 'create', 'start']),
siteDirArgument, siteDirArgument,
], ],
}; };

View File

@ -1,7 +1,7 @@
const commands = require('./commands'); const commands = require('./commands');
exports.hooks = { exports.hooks = {
'@flecks/core.commands': commands, '@flecks/build.commands': commands,
'@flecks/core.config': () => ({ '@flecks/core.config': () => ({
/** /**
* Rewrite the output filenames of source files. * Rewrite the output filenames of source files.

View File

@ -96,7 +96,7 @@ const implementationVisitor = (fn) => ({
const FlecksBuildConfigs = (state) => ( const FlecksBuildConfigs = (state) => (
implementationVisitor((property) => { implementationVisitor((property) => {
if ('@flecks/core.build.config' === property.key.value) { if ('@flecks/build.files' === property.key.value) {
if (isArrowFunctionExpression(property.value)) { if (isArrowFunctionExpression(property.value)) {
if (isArrayExpression(property.value.body)) { if (isArrayExpression(property.value.body)) {
property.value.body.elements.forEach((element) => { property.value.body.elements.forEach((element) => {

View File

@ -39,6 +39,7 @@
"rimraf": "^5.0.5" "rimraf": "^5.0.5"
}, },
"devDependencies": { "devDependencies": {
"@flecks/build": "^3.0.0",
"@flecks/fleck": "^3.0.0" "@flecks/fleck": "^3.0.0"
} }
} }

View File

@ -1,9 +1,9 @@
const {join} = require('path'); const {join} = require('path');
const {banner} = require('@flecks/core/server'); const {banner} = require('@flecks/build/server');
exports.hooks = { exports.hooks = {
'@flecks/core.build': (target, config) => { '@flecks/build.config': (target, config) => {
if ('server' === target) { if ('server' === target) {
config.plugins.push( config.plugins.push(
banner({ banner({
@ -38,7 +38,7 @@ exports.hooks = {
*/ */
url: undefined, url: undefined,
}), }),
'@flecks/core.build.alter': (configs) => { '@flecks/build.config.alter': (configs) => {
const {server: config} = configs; const {server: config} = configs;
if (config) { if (config) {
const plugin = config.plugins.find(({pluginName}) => pluginName === 'StartServerPlugin'); const plugin = config.plugins.find(({pluginName}) => pluginName === 'StartServerPlugin');

View File

@ -25,6 +25,7 @@
"electron-devtools-installer": "^3.2.0" "electron-devtools-installer": "^3.2.0"
}, },
"devDependencies": { "devDependencies": {
"@flecks/build": "^3.0.0",
"@flecks/fleck": "^3.0.0" "@flecks/fleck": "^3.0.0"
} }
} }

View File

@ -1,13 +1,14 @@
const {stat, unlink} = require('fs/promises'); const {stat, unlink} = require('fs/promises');
const {join} = require('path'); const {join} = require('path');
const {commands: coreCommands} = require('@flecks/build/build/commands');
const {D} = require('@flecks/core'); const {D} = require('@flecks/core');
const {commands: coreCommands, glob} = require('@flecks/core/server'); const {glob} = require('@flecks/core/server');
const chokidar = require('chokidar'); const chokidar = require('chokidar');
const clearModule = require('clear-module'); const clearModule = require('clear-module');
const Mocha = require('mocha'); const Mocha = require('mocha');
const debug = D('@flecks/core.commands'); const debug = D('@flecks/build.commands');
const { const {
FLECKS_CORE_ROOT = process.cwd(), FLECKS_CORE_ROOT = process.cwd(),

View File

@ -1,6 +1,6 @@
const flecksConfigFn = require('@flecks/core/build/fleck.webpack.config'); const flecksConfigFn = require('@flecks/build/build/fleck.webpack.config');
const ProcessAssets = require('./process-assets'); const {ProcessAssets} = require('@flecks/build/build/process-assets');
module.exports = async (env, argv, flecks) => { module.exports = async (env, argv, flecks) => {
const config = await flecksConfigFn(env, argv, flecks); const config = await flecksConfigFn(env, argv, flecks);

View File

@ -1,15 +1,11 @@
const {join} = require('path'); const {join} = require('path');
const {glob} = require('@flecks/core/server'); const {hook} = require('@flecks/build/build/process-assets');
const commands = require('./commands'); const commands = require('./commands');
const {
FLECKS_CORE_ROOT = process.cwd(),
} = process.env;
exports.hooks = { exports.hooks = {
'@flecks/core.commands': commands, '@flecks/build.commands': commands,
'@flecks/core.config': () => ({ '@flecks/core.config': () => ({
/** /**
* Webpack stats configuration. * Webpack stats configuration.
@ -20,36 +16,5 @@ exports.hooks = {
}, },
}), }),
'@flecks/core.targets': () => ['fleck'], '@flecks/core.targets': () => ['fleck'],
'@flecks/fleck.processAssets': async (assets, compilation, flecks) => { '@flecks/build.processAssets': hook,
const {RawSource} = compilation.compiler.webpack.sources;
const packageJson = assets['package.json'];
const json = JSON.parse(packageJson.source().toString());
const {files} = json;
// Add defaults.
files.push('build');
// Add source if it exists.
if ((await glob(join(FLECKS_CORE_ROOT, 'src/**/*.js'))).length > 0) {
files.push('src');
}
// Add tests if they exist.
const testFiles = await glob(join(FLECKS_CORE_ROOT, 'test/**/*.js'));
if (testFiles.length > 0) {
files.push('test');
}
// Let others have a say.
await flecks.invokeSequentialAsync('@flecks/fleck.packageJson', json, compilation);
// Add any sourcemaps.
json.files = json.files
.map((filename) => {
const maybeWithMap = [filename];
if (compilation.assets[`${filename}.map`]) {
maybeWithMap.push(`${filename}.map`);
}
return maybeWithMap;
})
.flat();
// Sort and uniquify.
json.files = [...new Set(json.files.sort((l, r) => (l < r ? -1 : 1)))];
compilation.updateAsset('package.json', new RawSource(JSON.stringify(json, null, 2)));
},
}; };

View File

@ -1,2 +0,0 @@
'@flecks/core': {}
'@flecks/fleck:.': {}

View File

@ -1,28 +0,0 @@
class ProcessAssets {
constructor(flecks) {
this.flecks = flecks;
}
apply(compiler) {
compiler.hooks.thisCompilation.tap('@flecks/fleck/build/process-assets', (compilation) => {
compilation.hooks.processAssets.tapAsync(
{
name: '@flecks/fleck/build/process-assets',
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_REPORT,
},
async (assets, callback) => {
await this.flecks.invokeSequentialAsync(
'@flecks/fleck.processAssets',
assets,
compilation,
);
callback();
},
);
});
}
}
module.exports = ProcessAssets;

View File

@ -27,6 +27,7 @@
"mocha": "^8.3.2" "mocha": "^8.3.2"
}, },
"devDependencies": { "devDependencies": {
"@flecks/build": "^3.0.0",
"chai": "4.2.0" "chai": "4.2.0"
} }
} }

View File

@ -24,10 +24,11 @@
"dependencies": { "dependencies": {
"@flecks/core": "^3.0.0", "@flecks/core": "^3.0.0",
"@flecks/db": "^3.0.0", "@flecks/db": "^3.0.0",
"rate-limiter-flexible": "^2.1.13", "@flecks/redis": "^3.0.0",
"redis": "^3.1.2" "rate-limiter-flexible": "^2.1.13"
}, },
"devDependencies": { "devDependencies": {
"@flecks/build": "^3.0.0",
"@flecks/fleck": "^3.0.0" "@flecks/fleck": "^3.0.0"
} }
} }

View File

@ -1,16 +1,13 @@
import {createClient} from 'redis'; import {createClient} from '@flecks/redis/server';
import {RateLimiterRedis} from 'rate-limiter-flexible'; import {RateLimiterRedis} from 'rate-limiter-flexible';
export default async (flecks, options) => { export default async (flecks, options) => {
const { const storeClient = await createClient(flecks);
host, const legacyClient = storeClient.duplicate({legacyMode: true});
port, await legacyClient.connect();
} = flecks.get('@flecks/redis/server');
const storeClient = createClient({host, port});
// @todo node-redis@4
// await storeClient.connect();
return new RateLimiterRedis({ return new RateLimiterRedis({
...options, ...options,
storeClient, // @todo node-redis@4
storeClient: legacyClient,
}); });
}; };

View File

@ -1,4 +1,5 @@
import {ByType, Flecks} from '@flecks/core'; import {ByType, Flecks} from '@flecks/core';
import {RateLimiterRes} from 'rate-limiter-flexible';
import LimitedPacket from './limited-packet'; import LimitedPacket from './limited-packet';
import createLimiter from './limiter'; import createLimiter from './limiter';
@ -25,7 +26,8 @@ export const hooks = {
}, },
}), }),
'@flecks/db/server.models': Flecks.provide(require.context('./models', false, /\.js$/)), '@flecks/db/server.models': Flecks.provide(require.context('./models', false, /\.js$/)),
'@flecks/web/server.request.route': (flecks) => { '@flecks/web/server.request.route': Flecks.priority(
(flecks) => {
const {web} = flecks.get('@flecks/governor/server'); const {web} = flecks.get('@flecks/governor/server');
return async (req, res, next) => { return async (req, res, next) => {
const {Ban} = flecks.db.Models; const {Ban} = flecks.db.Models;
@ -46,6 +48,10 @@ export const hooks = {
next(); next();
} }
catch (error) { catch (error) {
console.log(error);
if (!(error instanceof RateLimiterRes)) {
throw error;
}
const {ttl, keys} = web; const {ttl, keys} = web;
const ban = Ban.fromRequest(req, keys, ttl); const ban = Ban.fromRequest(req, keys, ttl);
await Ban.create({...ban}); await Ban.create({...ban});
@ -53,6 +59,8 @@ export const hooks = {
} }
}; };
}, },
{after: '@flecks/passport/server'},
),
'@flecks/server.up': Flecks.priority( '@flecks/server.up': Flecks.priority(
async (flecks) => { async (flecks) => {
if (flecks.fleck('@flecks/web/server')) { if (flecks.fleck('@flecks/web/server')) {
@ -91,7 +99,7 @@ export const hooks = {
); );
} }
}, },
{after: '@flecks/redis/server'}, {before: '@flecks/web/server', after: '@flecks/redis/server'},
), ),
'@flecks/socket/server.request.socket': (flecks) => ( '@flecks/socket/server.request.socket': (flecks) => (
async (socket, next) => { async (socket, next) => {
@ -113,6 +121,9 @@ export const hooks = {
next(); next();
} }
catch (error) { catch (error) {
if (!(error instanceof RateLimiterRes)) {
throw error;
}
const {ttl, keys} = socket; const {ttl, keys} = socket;
await Ban.create(Ban.fromRequest(req, keys, ttl)); await Ban.create(Ban.fromRequest(req, keys, ttl));
next(error); next(error);

View File

@ -1,3 +0,0 @@
'@flecks/core': {}
'@flecks/fleck': {}
'@flecks/react': {}

View File

@ -22,9 +22,11 @@
"dependencies": { "dependencies": {
"@flecks/core": "^3.0.0", "@flecks/core": "^3.0.0",
"@flecks/passport-local": "^3.0.0", "@flecks/passport-local": "^3.0.0",
"@flecks/passport-react": "^3.0.0",
"@flecks/react": "^3.0.0" "@flecks/react": "^3.0.0"
}, },
"devDependencies": { "devDependencies": {
"@flecks/build": "^3.0.0",
"@flecks/fleck": "^3.0.0" "@flecks/fleck": "^3.0.0"
} }
} }

View File

@ -26,6 +26,7 @@
"passport-local": "^1.0.0" "passport-local": "^1.0.0"
}, },
"devDependencies": { "devDependencies": {
"@flecks/build": "^3.0.0",
"@flecks/fleck": "^3.0.0" "@flecks/fleck": "^3.0.0"
} }
} }

View File

@ -27,6 +27,7 @@
"@flecks/web": "^3.0.0" "@flecks/web": "^3.0.0"
}, },
"devDependencies": { "devDependencies": {
"@flecks/build": "^3.0.0",
"@flecks/fleck": "^3.0.0" "@flecks/fleck": "^3.0.0"
} }
} }

View File

@ -28,6 +28,7 @@
"passport": "^0.7.0" "passport": "^0.7.0"
}, },
"devDependencies": { "devDependencies": {
"@flecks/build": "^3.0.0",
"@flecks/fleck": "^3.0.0" "@flecks/fleck": "^3.0.0"
} }
} }

View File

@ -38,7 +38,7 @@ export const hooks = {
}); });
}); });
}, },
{after: '@flecks/session/server'}, {after: '@flecks/session/server', before: '@flecks/redux/server'},
), ),
'@flecks/server.up': Flecks.priority( '@flecks/server.up': Flecks.priority(
async (flecks) => { async (flecks) => {

View File

@ -19,11 +19,11 @@ exports.hooks = {
'@babel/preset-react', '@babel/preset-react',
], ],
}), }),
'@flecks/core.build': (target, config, env, argv) => { '@flecks/build.config': (target, config, env, argv) => {
const isProduction = 'production' === argv.mode; const isProduction = 'production' === argv.mode;
if (!isProduction) { if (!isProduction) {
config.plugins.push(new ReactRefreshWebpackPlugin()); config.plugins.push(new ReactRefreshWebpackPlugin());
} }
}, },
'@flecks/core.exts': () => ['.jsx'], '@flecks/build.extensions': () => ['.jsx'],
}; };

View File

@ -49,6 +49,7 @@
"redux-first-history": "5.1.1" "redux-first-history": "5.1.1"
}, },
"devDependencies": { "devDependencies": {
"@flecks/build": "^3.0.0",
"@flecks/fleck": "^3.0.0" "@flecks/fleck": "^3.0.0"
} }
} }

View File

@ -28,6 +28,7 @@
"redis": "4.0.3" "redis": "4.0.3"
}, },
"devDependencies": { "devDependencies": {
"@flecks/build": "^3.0.0",
"@flecks/fleck": "^3.0.0" "@flecks/fleck": "^3.0.0"
} }
} }

View File

@ -1,12 +1,19 @@
import {createClient} from 'redis'; import {createClient} from 'redis';
export default (flecks, opts = {}) => { export default async (flecks, opts = {}) => {
const { const {
host, host,
port, port,
} = flecks.get('@flecks/redis/server'); } = flecks.get('@flecks/redis/server');
return createClient({ const client = createClient({
url: `redis://${host}:${port}`, url: `redis://${host}:${port}`,
...opts, ...opts,
}); });
const promise = new Promise((resolve, reject) => {
client.on('ready', resolve);
client.on('error', reject);
});
await client.connect();
await promise;
return client;
}; };

View File

@ -3,6 +3,8 @@ import {Flecks} from '@flecks/core';
import containers from './containers'; import containers from './containers';
import createClient from './create-client'; import createClient from './create-client';
export {default as redis} from 'redis';
export {createClient}; export {createClient};
const safeKeys = async (client, pattern, caret) => { const safeKeys = async (client, pattern, caret) => {
@ -39,18 +41,12 @@ export const hooks = {
port: 6379, port: 6379,
}), }),
'@flecks/docker.containers': containers, '@flecks/docker.containers': containers,
'@flecks/repl.context': (flecks) => ({ '@flecks/repl.context': async (flecks) => ({
redisClient: createClient(flecks), redisClient: await createClient(flecks),
}), }),
'@flecks/server.up': Flecks.priority( '@flecks/server.up': Flecks.priority(
async (flecks) => { async (flecks) => {
const client = createClient(flecks); const client = await createClient(flecks);
const promise = new Promise((resolve, reject) => {
client.on('ready', resolve);
client.on('error', reject);
});
await client.connect();
await promise;
await client.disconnect(); await client.disconnect();
}, },
{after: '@flecks/docker/server'}, {after: '@flecks/docker/server'},

View File

@ -17,16 +17,14 @@ export const hooks = {
} }
}, },
'@flecks/session.config': async (flecks) => { '@flecks/session.config': async (flecks) => {
const client = createClient(flecks, {legacyMode: true}); const client = await createClient(flecks, {legacyMode: true});
await client.connect();
return { return {
store: new RedisStore({client}), store: new RedisStore({client}),
}; };
}, },
'@flecks/socket.server': async (flecks) => { '@flecks/socket.server': async (flecks) => {
const pubClient = createClient(flecks); const pubClient = await createClient(flecks);
const subClient = createClient(flecks); const subClient = await createClient(flecks);
await Promise.all([pubClient.connect(), subClient.connect()]);
debugSilly('creating adapter'); debugSilly('creating adapter');
return { return {
adapter: redisAdapter(pubClient, subClient), adapter: redisAdapter(pubClient, subClient),

View File

@ -31,6 +31,7 @@
"reduce-reducers": "^1.0.4" "reduce-reducers": "^1.0.4"
}, },
"devDependencies": { "devDependencies": {
"@flecks/build": "^3.0.0",
"@flecks/fleck": "^3.0.0" "@flecks/fleck": "^3.0.0"
} }
} }

View File

@ -1,5 +1,5 @@
const commands = require('./commands'); const commands = require('./commands');
exports.hooks = { exports.hooks = {
'@flecks/core.commands': commands, '@flecks/build.commands': commands,
}; };

View File

@ -25,6 +25,7 @@
"debug": "4.3.1" "debug": "4.3.1"
}, },
"devDependencies": { "devDependencies": {
"@flecks/build": "^3.0.0",
"@flecks/fleck": "^3.0.0" "@flecks/fleck": "^3.0.0"
} }
} }

View File

@ -11,7 +11,7 @@ const debugSilly = debug.extend('silly');
export async function createReplServer(flecks) { export async function createReplServer(flecks) {
const {id} = flecks.get('@flecks/core'); const {id} = flecks.get('@flecks/core');
const context = flecks.invokeFlat('@flecks/repl.context') const context = (await Promise.all(flecks.invokeFlat('@flecks/repl.context')))
.reduce((r, vars) => ({...r, ...vars}), {flecks}); .reduce((r, vars) => ({...r, ...vars}), {flecks});
debug( debug(
'Object.keys(context) === %O', 'Object.keys(context) === %O',

View File

@ -1,5 +1,5 @@
exports.hooks = { exports.hooks = {
'@flecks/core.build.config': () => [ '@flecks/build.files': () => [
/** /**
* Server build configuration. See: https://webpack.js.org/configuration/ * Server build configuration. See: https://webpack.js.org/configuration/
*/ */

View File

@ -1,7 +1,7 @@
const {externals} = require('@flecks/core/server'); const {externals} = require('@flecks/build/server');
module.exports = async (config, env, argv, flecks) => { module.exports = async (config, env, argv, flecks) => {
const runtime = await flecks.resolver.resolve('@flecks/server/runtime'); const runtimePath = await flecks.resolver.resolve('@flecks/server/runtime');
// Inject flecks configuration. // Inject flecks configuration.
const paths = Object.keys(flecks.flecks); const paths = Object.keys(flecks.flecks);
const resolvedPaths = (await Promise.all( const resolvedPaths = (await Promise.all(
@ -9,19 +9,45 @@ module.exports = async (config, env, argv, flecks) => {
)) ))
.filter(([, resolved]) => resolved) .filter(([, resolved]) => resolved)
.map(([path]) => path); .map(([path]) => path);
const runtime = {
config: JSON.stringify(flecks.config),
loadFlecks: [
'async () => (',
' Object.fromEntries(',
' (await Promise.all(',
' [',
...resolvedPaths.map((path) => [
' (async () => {',
' try {',
` return ['${path}', await import('${path}')];`,
' }',
' catch (error) {',
' if (!error.message.startsWith("Cannot find module")) {',
' throw error;',
' }',
' }',
' })(),',
]).flat(),
' ],',
' ))',
' .filter((entry) => entry),',
' )',
')',
].join('\n'),
stubs: (
JSON.stringify(flecks.stubs.map((stub) => (
stub instanceof RegExp ? [stub.source, stub.flags] : stub
)))
),
...await flecks.invokeAsync('@flecks/server.runtime'),
};
const runtimeString = `{${
Object.entries(runtime)
.map(([key, value]) => `"${key}": ${value}`).join(', ')
}}`;
const source = [ const source = [
"process.env.FLECKS_CORE_BUILD_TARGET = 'server';", "process.env.FLECKS_CORE_BUILD_TARGET = 'server';",
'module.exports = (async () => ({', `module.exports = (async () => (${runtimeString}))();`,
` config: ${JSON.stringify(flecks.realiasedConfig)},`,
' loadFlecks: async () => Object.fromEntries(await Promise.all([',
...resolvedPaths.map((path) => (
` ['${path}', import('${path}')],`
)),
' ].map(async ([path, M]) => [path, await M]))),',
` stubs: ${JSON.stringify(flecks.stubs.map((stub) => (
stub instanceof RegExp ? [stub.source, stub.flags] : stub
)))}`,
'}))();',
]; ];
// HMR. // HMR.
source.push('if (module.hot) {'); source.push('if (module.hot) {');
@ -55,10 +81,10 @@ module.exports = async (config, env, argv, flecks) => {
// Create runtime. // Create runtime.
config.module.rules.push( config.module.rules.push(
{ {
test: runtime, test: runtimePath,
use: [ use: [
{ {
loader: runtime, loader: runtimePath,
options: { options: {
source: source.join('\n'), source: source.join('\n'),
}, },
@ -71,14 +97,14 @@ module.exports = async (config, env, argv, flecks) => {
'@flecks/server/runtime', '@flecks/server/runtime',
/^@babel\/runtime\/helpers\/esm/, /^@babel\/runtime\/helpers\/esm/,
]; ];
config.resolve.alias['@flecks/server/runtime$'] = runtime; config.resolve.alias['@flecks/server/runtime$'] = runtimePath;
const nodeExternalsConfig = { const nodeExternalsConfig = {
allowlist, allowlist,
}; };
await flecks.runtimeCompiler('server', config, nodeExternalsConfig); await flecks.runtimeCompiler('server', config, nodeExternalsConfig);
// Rewrite to signals for HMR. // Rewrite to signals for HMR.
if ('production' !== argv.mode) { if ('production' !== argv.mode) {
allowlist.push(/^webpack/); allowlist.push(/^webpack\/hot\/signal/);
} }
// Externalize the rest. // Externalize the rest.
config.externals = externals(nodeExternalsConfig); config.externals = externals(nodeExternalsConfig);

View File

@ -4,7 +4,7 @@ const {
banner, banner,
defaultConfig, defaultConfig,
webpack, webpack,
} = require('@flecks/core/server'); } = require('@flecks/build/server');
const runtime = require('./runtime'); const runtime = require('./runtime');
const startServer = require('./start'); const startServer = require('./start');

View File

@ -26,6 +26,7 @@
"@flecks/core": "^3.0.0" "@flecks/core": "^3.0.0"
}, },
"devDependencies": { "devDependencies": {
"@flecks/build": "^3.0.0",
"@flecks/fleck": "^3.0.0" "@flecks/fleck": "^3.0.0"
} }
} }

View File

@ -2,13 +2,13 @@ import {mkdir} from 'fs/promises';
import {tmpdir} from 'os'; import {tmpdir} from 'os';
import {join} from 'path'; import {join} from 'path';
import {D} from '@flecks/core'; import {D, Flecks} from '@flecks/core';
import Server from '@flecks/core/build/server';
const {version} = require('../package.json'); const {version} = require('../package.json');
(async () => { (async () => {
const {config, loadFlecks, stubs} = await __non_webpack_require__('@flecks/server/runtime'); const runtime = await __non_webpack_require__('@flecks/server/runtime');
const {config, loadFlecks, stubs} = runtime;
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(`flecks server v${version}`); console.log(`flecks server v${version}`);
try { try {
@ -26,7 +26,7 @@ const {version} = require('../package.json');
debug('stubbing with %O', unserializedStubs); debug('stubbing with %O', unserializedStubs);
__non_webpack_require__('@flecks/core/build/stub')(unserializedStubs); __non_webpack_require__('@flecks/core/build/stub')(unserializedStubs);
} }
global.flecks = await Server.from({config, flecks: await loadFlecks()}); global.flecks = await Flecks.from({...runtime, flecks: await loadFlecks()});
try { try {
await Promise.all(global.flecks.invokeFlat('@flecks/core.starting')); await Promise.all(global.flecks.invokeFlat('@flecks/core.starting'));
await global.flecks.invokeSequentialAsync('@flecks/server.up'); await global.flecks.invokeSequentialAsync('@flecks/server.up');

View File

@ -25,6 +25,7 @@
"express-session": "^1.17.3" "express-session": "^1.17.3"
}, },
"devDependencies": { "devDependencies": {
"@flecks/build": "^3.0.0",
"@flecks/fleck": "^3.0.0" "@flecks/fleck": "^3.0.0"
} }
} }

View File

@ -32,6 +32,7 @@
"socket.io-client": "^4.1.2" "socket.io-client": "^4.1.2"
}, },
"devDependencies": { "devDependencies": {
"@flecks/build": "^3.0.0",
"@flecks/fleck": "^3.0.0" "@flecks/fleck": "^3.0.0"
} }
} }

View File

@ -1,4 +1,4 @@
const {copy, externals} = require('@flecks/core/server'); const {copy, externals} = require('@flecks/build/server');
// eslint-disable-next-line import/no-extraneous-dependencies // eslint-disable-next-line import/no-extraneous-dependencies
const configFn = require('@flecks/fleck/build/fleck.webpack.config'); const configFn = require('@flecks/fleck/build/fleck.webpack.config');

View File

@ -1,7 +1,9 @@
const {stat, unlink} = require('fs/promises'); const {stat, unlink} = require('fs/promises');
const {join} = require('path'); const {join} = require('path');
const {regexFromExtensions, spawnWith} = require('@flecks/core/server'); const Build = require('@flecks/build/build/build');
const {spawnWith} = require('@flecks/build/build/commands');
const {regexFromExtensions} = require('@flecks/build/server');
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { const {
@ -9,7 +11,7 @@ const {
} = process.env; } = process.env;
exports.hooks = { exports.hooks = {
'@flecks/core.build': async (target, config, env, argv, flecks) => { '@flecks/build.config': async (target, config, env, argv, flecks) => {
const isProduction = 'production' === argv.mode; const isProduction = 'production' === argv.mode;
let finalLoader; let finalLoader;
switch (target) { switch (target) {
@ -107,7 +109,7 @@ exports.hooks = {
type: 'asset', type: 'asset',
}); });
}, },
'@flecks/core.build.alter': async (configs, env, argv, flecks) => { '@flecks/build.config.alter': async (configs, env, argv, flecks) => {
const isProduction = 'production' === argv.mode; const isProduction = 'production' === argv.mode;
// Only build vendor in dev. // Only build vendor in dev.
if (configs['web-vendor']) { if (configs['web-vendor']) {
@ -187,7 +189,7 @@ exports.hooks = {
// Remove the build config since we're handing off to WDS. // Remove the build config since we're handing off to WDS.
delete configs.web; delete configs.web;
}, },
'@flecks/core.build.config': () => [ '@flecks/build.files': () => [
/** /**
* Template file used to generate the client HTML. * Template file used to generate the client HTML.
* *
@ -290,6 +292,11 @@ exports.hooks = {
*/ */
trust: false, trust: false,
}), }),
'@flecks/build.packageJson': (json, compilation) => {
if (Object.keys(compilation.assets).some((filename) => filename.match(/^assets\//))) {
json.files.push('assets');
}
},
'@flecks/core.targets': (flecks) => [ '@flecks/core.targets': (flecks) => [
'web', 'web',
...(flecks.get('@flecks/web.dll').length > 0 ? ['web-vendor'] : []), ...(flecks.get('@flecks/web.dll').length > 0 ? ['web-vendor'] : []),
@ -300,10 +307,12 @@ exports.hooks = {
targets.delete('web'); targets.delete('web');
} }
}, },
'@flecks/fleck.packageJson': (json, compilation) => { '@flecks/server.runtime': async (flecks) => {
if (Object.keys(compilation.assets).some((filename) => filename.match(/^assets\//))) { const {config} = await Build.from({
json.files.push('assets'); config: flecks.config,
} platforms: ['client', '!server'],
});
return JSON.stringify(config);
}, },
}; };

View File

@ -6,11 +6,11 @@ const {
join, join,
} = require('path'); } = require('path');
const Server = require('@flecks/core/build/server'); const Build = require('@flecks/build/build/build');
const {glob} = require('@flecks/core/server'); const {glob} = require('@flecks/core/server');
module.exports = async (config, env, argv, flecks) => { module.exports = async (config, env, argv, flecks) => {
const buildFlecks = await Server.from({ const buildFlecks = await Build.from({
config: flecks.realiasedConfig, config: flecks.realiasedConfig,
platforms: ['client', '!server'], platforms: ['client', '!server'],
}); });

View File

@ -1,6 +1,6 @@
const {join} = require('path'); const {join} = require('path');
const {defaultConfig, webpack} = require('@flecks/core/server'); const {defaultConfig, webpack} = require('@flecks/build/server');
const {CleanWebpackPlugin} = require('clean-webpack-plugin'); const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const { const {

View File

@ -4,7 +4,7 @@ const {
defaultConfig, defaultConfig,
regexFromExtensions, regexFromExtensions,
webpack, webpack,
} = require('@flecks/core/server'); } = require('@flecks/build/server');
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin'); const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin');
const {htmlTagObjectToString} = require('html-webpack-plugin/lib/html-tags'); const {htmlTagObjectToString} = require('html-webpack-plugin/lib/html-tags');
@ -81,7 +81,7 @@ module.exports = async (env, argv, flecks) => {
.map(async ([name, mainsConfig]) => { .map(async ([name, mainsConfig]) => {
const {entry: entryPoint, ...htmlTemplateConfig} = mainsConfig; const {entry: entryPoint, ...htmlTemplateConfig} = mainsConfig;
// @todo source maps working? // @todo source maps working?
entry[name] = [entryPoint]; entry[name] = ['source-map-support/register', entryPoint];
plugins.push(new HtmlWebpackPlugin({ plugins.push(new HtmlWebpackPlugin({
appMountId: flecks.interpolate(appMountId), appMountId: flecks.interpolate(appMountId),
base: flecks.interpolate(base), base: flecks.interpolate(base),

View File

@ -45,6 +45,7 @@
"lodash.flatten": "^4.4.0", "lodash.flatten": "^4.4.0",
"mini-css-extract-plugin": "^2.7.6", "mini-css-extract-plugin": "^2.7.6",
"mocha": "^8.3.2", "mocha": "^8.3.2",
"null-loader": "^4.0.1",
"path-browserify": "^1.0.1", "path-browserify": "^1.0.1",
"postcss-loader": "4.2.0", "postcss-loader": "4.2.0",
"process": "^0.11.10", "process": "^0.11.10",
@ -57,6 +58,7 @@
"webpack-dev-server": "^4.15.1" "webpack-dev-server": "^4.15.1"
}, },
"devDependencies": { "devDependencies": {
"@flecks/build": "^3.0.0",
"@flecks/fleck": "^3.0.0" "@flecks/fleck": "^3.0.0"
} }
} }

View File

@ -1,6 +1,3 @@
// eslint-disable-next-line import/no-extraneous-dependencies
require('source-map-support/register');
const mochaDiv = window.document.createElement('div'); const mochaDiv = window.document.createElement('div');
mochaDiv.id = 'mocha'; mochaDiv.id = 'mocha';
window.document.body.appendChild(mochaDiv); window.document.body.appendChild(mochaDiv);

Some files were not shown because too many files have changed in this diff Show More