refactor: bootstrap
This commit is contained in:
parent
5d2bc3ee07
commit
757cd89e99
|
@ -20,6 +20,10 @@
|
|||
"@flecks/electron": "*",
|
||||
"@flecks/fleck": "*",
|
||||
"@flecks/governor": "*",
|
||||
"@flecks/passport": "*",
|
||||
"@flecks/passport-local": "*",
|
||||
"@flecks/passport-local-react": "*",
|
||||
"@flecks/passport-react": "*",
|
||||
"@flecks/react": "*",
|
||||
"@flecks/redis": "*",
|
||||
"@flecks/redux": "*",
|
||||
|
@ -27,10 +31,6 @@
|
|||
"@flecks/server": "*",
|
||||
"@flecks/session": "*",
|
||||
"@flecks/socket": "*",
|
||||
"@flecks/passport": "*",
|
||||
"@flecks/passport-local": "*",
|
||||
"@flecks/passport-local-react": "*",
|
||||
"@flecks/passport-react": "*",
|
||||
"@flecks/web": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
19
packages/core/build/add-fleck-to-yml.js
Normal file
19
packages/core/build/add-fleck-to-yml.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
const {readFile, writeFile} = require('fs/promises');
|
||||
const {
|
||||
join,
|
||||
sep,
|
||||
} = require('path');
|
||||
|
||||
const {dump: dumpYml, load: loadYml} = require('js-yaml');
|
||||
|
||||
const {
|
||||
FLECKS_CORE_ROOT = process.cwd(),
|
||||
} = process.env;
|
||||
|
||||
module.exports = async (fleck, path) => {
|
||||
const key = [fleck].concat(path ? `.${sep}${join('packages', path, 'src')}` : []).join(':');
|
||||
const ymlPath = join(FLECKS_CORE_ROOT, 'build', 'flecks.yml');
|
||||
let yml = loadYml(await readFile(ymlPath));
|
||||
yml = Object.fromEntries(Object.entries(yml).concat([[key, {}]]));
|
||||
await writeFile(ymlPath, dumpYml(yml, {sortKeys: true}));
|
||||
};
|
1
packages/core/build/class.js
Normal file
1
packages/core/build/class.js
Normal file
|
@ -0,0 +1 @@
|
|||
module.exports = class {};
|
67
packages/core/build/cli.js
Executable file
67
packages/core/build/cli.js
Executable file
|
@ -0,0 +1,67 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const {Command} = require('commander');
|
||||
|
||||
const {processCode} = require('./commands');
|
||||
const D = require('./debug');
|
||||
const Server = require('./server');
|
||||
|
||||
const debug = D('@flecks/core/cli');
|
||||
const debugSilly = debug.extend('silly');
|
||||
|
||||
// Asynchronous command process code forwarding.
|
||||
const forwardProcessCode = (fn) => async (...args) => {
|
||||
const child = await fn(...args);
|
||||
if ('object' !== typeof child) {
|
||||
const code = 'undefined' !== typeof child ? child : 0;
|
||||
debugSilly('action returned code %d', code);
|
||||
process.exitCode = code;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const code = await processCode(child);
|
||||
debugSilly('action exited with code %d', code);
|
||||
process.exitCode = code;
|
||||
}
|
||||
catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
process.exitCode = child.exitCode || 1;
|
||||
}
|
||||
};
|
||||
// Initialize Commander.
|
||||
const program = new Command();
|
||||
program
|
||||
.enablePositionalOptions()
|
||||
.name('flecks')
|
||||
.usage('[command] [...]');
|
||||
// Bootstrap.
|
||||
(async () => {
|
||||
debugSilly('bootstrapping flecks...');
|
||||
const flecks = await Server.from();
|
||||
debugSilly('bootstrapped');
|
||||
// Register commands.
|
||||
const commands = flecks.invokeMerge('@flecks/core.commands', program);
|
||||
const keys = Object.keys(commands).sort();
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
const {
|
||||
action,
|
||||
args = [],
|
||||
description,
|
||||
name = keys[i],
|
||||
options = [],
|
||||
} = commands[keys[i]];
|
||||
debugSilly('adding command %s...', name);
|
||||
const cmd = program.command(name);
|
||||
cmd.description(description);
|
||||
for (let i = 0; i < args.length; ++i) {
|
||||
cmd.addArgument(args[i]);
|
||||
}
|
||||
for (let i = 0; i < options.length; ++i) {
|
||||
cmd.option(...options[i]);
|
||||
}
|
||||
cmd.action(forwardProcessCode(action));
|
||||
}
|
||||
// Parse commandline.
|
||||
program.parse(process.argv);
|
||||
})();
|
|
@ -1,13 +1,12 @@
|
|||
import {spawn} from 'child_process';
|
||||
import {join, normalize} from 'path';
|
||||
const {spawn} = require('child_process');
|
||||
const {join, normalize} = require('path');
|
||||
|
||||
import {Argument} from 'commander';
|
||||
import {glob} from 'glob';
|
||||
import flatten from 'lodash.flatten';
|
||||
import rimraf from 'rimraf';
|
||||
const {Argument, Option, program} = require('commander');
|
||||
const {glob} = require('glob');
|
||||
const rimraf = require('rimraf');
|
||||
|
||||
import D from '../debug';
|
||||
import Flecks from './flecks';
|
||||
const D = require('./debug');
|
||||
const addFleckToYml = require('./add-fleck-to-yml');
|
||||
|
||||
const {
|
||||
FLECKS_CORE_ROOT = process.cwd(),
|
||||
|
@ -17,32 +16,8 @@ const debug = D('@flecks/core/commands');
|
|||
const debugSilly = debug.extend('silly');
|
||||
const flecksRoot = normalize(FLECKS_CORE_ROOT);
|
||||
|
||||
export {Argument, Option, program} from 'commander';
|
||||
|
||||
export const processCode = (child) => new Promise((resolve, reject) => {
|
||||
child.on('error', reject);
|
||||
child.on('exit', (code) => {
|
||||
child.off('error', reject);
|
||||
resolve(code);
|
||||
});
|
||||
});
|
||||
|
||||
export const spawnWith = (cmd, opts = {}) => {
|
||||
debug("spawning: '%s'", cmd.join(' '));
|
||||
debugSilly('with options: %O', opts);
|
||||
const child = spawn(cmd[0], cmd.slice(1), {
|
||||
stdio: 'inherit',
|
||||
...opts,
|
||||
env: {
|
||||
...process.env,
|
||||
...opts.env,
|
||||
},
|
||||
});
|
||||
return child;
|
||||
};
|
||||
|
||||
export default (program, flecks) => {
|
||||
const {packageManager} = flecks.get('@flecks/core/server');
|
||||
exports.commands = (program, flecks) => {
|
||||
const {packageManager} = flecks.get('@flecks/core');
|
||||
const commands = {
|
||||
add: {
|
||||
args: [
|
||||
|
@ -58,8 +33,8 @@ export default (program, flecks) => {
|
|||
args.push(packageManager, ['install', fleck]);
|
||||
}
|
||||
args.push({stdio: 'inherit'});
|
||||
await processCode(spawn(...args));
|
||||
await Flecks.addFleckToYml(fleck);
|
||||
await module.exports.processCode(spawn(...args));
|
||||
await addFleckToYml(fleck);
|
||||
},
|
||||
},
|
||||
clean: {
|
||||
|
@ -83,11 +58,11 @@ export default (program, flecks) => {
|
|||
},
|
||||
},
|
||||
};
|
||||
const targets = flatten(flecks.invokeFlat('@flecks/core.targets'));
|
||||
const {targets} = flecks;
|
||||
if (targets.length > 0) {
|
||||
commands.build = {
|
||||
args: [
|
||||
new Argument('[target]', 'build target').choices(targets),
|
||||
new Argument('[target]', 'build target').choices(targets.map(([, target]) => target)),
|
||||
],
|
||||
options: [
|
||||
['-d, --no-production', 'dev build'],
|
||||
|
@ -95,21 +70,21 @@ export default (program, flecks) => {
|
|||
['-w, --watch', 'watch for changes'],
|
||||
],
|
||||
description: 'build a target in your application',
|
||||
action: (target, opts) => {
|
||||
action: async (target, opts) => {
|
||||
const {
|
||||
hot,
|
||||
production,
|
||||
watch,
|
||||
} = opts;
|
||||
debug('Building...', opts);
|
||||
const webpackConfig = flecks.buildConfig('fleckspack.config.js');
|
||||
const webpackConfig = await flecks.resolveBuildConfig('fleckspack.config.js');
|
||||
const cmd = [
|
||||
'npx', 'webpack',
|
||||
'--config', webpackConfig,
|
||||
'--mode', (production && !hot) ? 'production' : 'development',
|
||||
...((watch || hot) ? ['--watch'] : []),
|
||||
];
|
||||
return spawnWith(
|
||||
return module.exports.spawnWith(
|
||||
cmd,
|
||||
{
|
||||
env: {
|
||||
|
@ -131,28 +106,30 @@ export default (program, flecks) => {
|
|||
if (0 === packages.length) {
|
||||
packages.push('.');
|
||||
}
|
||||
packages
|
||||
.map((pkg) => join(process.cwd(), pkg))
|
||||
.forEach((cwd) => {
|
||||
const cmd = [
|
||||
'npx', 'eslint',
|
||||
'--config', flecks.buildConfig('eslint.config.js'),
|
||||
'.',
|
||||
];
|
||||
promises.push(new Promise((resolve, reject) => {
|
||||
const child = spawnWith(
|
||||
cmd,
|
||||
{
|
||||
cwd,
|
||||
},
|
||||
);
|
||||
child.on('error', reject);
|
||||
child.on('exit', (code) => {
|
||||
child.off('error', reject);
|
||||
resolve(code);
|
||||
});
|
||||
}));
|
||||
});
|
||||
await Promise.all(
|
||||
packages
|
||||
.map((pkg) => join(process.cwd(), pkg))
|
||||
.map(async (cwd) => {
|
||||
const cmd = [
|
||||
'npx', 'eslint',
|
||||
'--config', await flecks.resolveBuildConfig('eslint.config.js'),
|
||||
'.',
|
||||
];
|
||||
promises.push(new Promise((resolve, reject) => {
|
||||
const child = module.exports.spawnWith(
|
||||
cmd,
|
||||
{
|
||||
cwd,
|
||||
},
|
||||
);
|
||||
child.on('error', reject);
|
||||
child.on('exit', (code) => {
|
||||
child.off('error', reject);
|
||||
resolve(code);
|
||||
});
|
||||
}));
|
||||
}),
|
||||
);
|
||||
const promise = Promise.all(promises)
|
||||
.then(
|
||||
(codes) => (
|
||||
|
@ -176,3 +153,29 @@ export default (program, flecks) => {
|
|||
};
|
||||
return commands;
|
||||
};
|
||||
|
||||
exports.processCode = (child) => new Promise((resolve, reject) => {
|
||||
child.on('error', reject);
|
||||
child.on('exit', (code) => {
|
||||
child.off('error', reject);
|
||||
resolve(code);
|
||||
});
|
||||
});
|
||||
|
||||
exports.spawnWith = (cmd, opts = {}) => {
|
||||
debug("spawning: '%s'", cmd.join(' '));
|
||||
debugSilly('with options: %O', opts);
|
||||
const child = spawn(cmd[0], cmd.slice(1), {
|
||||
stdio: 'inherit',
|
||||
...opts,
|
||||
env: {
|
||||
...process.env,
|
||||
...opts.env,
|
||||
},
|
||||
});
|
||||
return child;
|
||||
};
|
||||
|
||||
exports.Argument = Argument;
|
||||
exports.Option = Option;
|
||||
exports.program = program;
|
|
@ -1,4 +1,4 @@
|
|||
export default function compose(...funcs) {
|
||||
module.exports = function compose(...funcs) {
|
||||
if (funcs.length === 0) {
|
||||
return (arg) => arg;
|
||||
}
|
||||
|
@ -6,4 +6,4 @@ export default function compose(...funcs) {
|
|||
return funcs[0];
|
||||
}
|
||||
return funcs.reduce((a, b) => (...args) => a(b(...args)));
|
||||
}
|
||||
};
|
4
packages/core/build/core.eslint.config.js
Normal file
4
packages/core/build/core.eslint.config.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
const defaultConfigFn = require('./default.eslint.config');
|
||||
const Server = require('./server');
|
||||
|
||||
module.exports = defaultConfigFn(Server.from());
|
10
packages/core/build/core.webpack.config.js
Normal file
10
packages/core/build/core.webpack.config.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
const Server = require('./server');
|
||||
const configFn = require('./fleck.webpack.config');
|
||||
const {executable} = require('./webpack');
|
||||
|
||||
module.exports = async (env, argv) => {
|
||||
const flecks = await Server.from();
|
||||
const config = await configFn(env, argv, flecks);
|
||||
config.plugins.push(...executable());
|
||||
return config;
|
||||
};
|
74
packages/core/build/default.eslint.config.js
Normal file
74
packages/core/build/default.eslint.config.js
Normal file
|
@ -0,0 +1,74 @@
|
|||
const globals = require('globals');
|
||||
|
||||
module.exports = async (flecks) => ({
|
||||
extends: [
|
||||
require.resolve('eslint-config-airbnb'),
|
||||
require.resolve('eslint-config-airbnb/hooks'),
|
||||
],
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.es2021,
|
||||
...globals.mocha,
|
||||
...globals.node,
|
||||
__non_webpack_require__: true,
|
||||
},
|
||||
ignorePatterns: [
|
||||
'dist/**',
|
||||
// Not even gonna try.
|
||||
'build/dox/hooks.js',
|
||||
],
|
||||
overrides: [
|
||||
{
|
||||
files: [
|
||||
'build/**/*.js',
|
||||
],
|
||||
rules: {
|
||||
'import/no-dynamic-require': 'off',
|
||||
'global-require': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'test/**/*.js',
|
||||
],
|
||||
rules: {
|
||||
'brace-style': 'off',
|
||||
'class-methods-use-this': 'off',
|
||||
'import/no-extraneous-dependencies': 'off',
|
||||
'import/no-unresolved': 'off',
|
||||
'max-classes-per-file': 'off',
|
||||
'no-new': 'off',
|
||||
'no-unused-expressions': 'off',
|
||||
'padded-blocks': 'off',
|
||||
},
|
||||
},
|
||||
],
|
||||
parser: require.resolve('@babel/eslint-parser'),
|
||||
parserOptions: {
|
||||
requireConfigFile: false,
|
||||
babelOptions: await flecks.babel(),
|
||||
},
|
||||
plugins: ['@babel'],
|
||||
rules: {
|
||||
'brace-style': ['error', 'stroustrup'],
|
||||
// Bug: https://github.com/import-js/eslint-plugin-import/issues/2181
|
||||
'import/no-import-module-exports': 'off',
|
||||
'import/prefer-default-export': 'off',
|
||||
'jsx-a11y/control-has-associated-label': ['error', {assert: 'either'}],
|
||||
'jsx-a11y/label-has-associated-control': ['error', {assert: 'either'}],
|
||||
'no-param-reassign': ['error', {props: false}],
|
||||
'no-plusplus': 'off',
|
||||
'no-shadow': 'off',
|
||||
'object-curly-spacing': 'off',
|
||||
'padded-blocks': ['error', {classes: 'always'}],
|
||||
yoda: 'off',
|
||||
},
|
||||
settings: {
|
||||
'import/resolver': {
|
||||
node: {},
|
||||
},
|
||||
react: {
|
||||
version: '18.2.0',
|
||||
},
|
||||
},
|
||||
});
|
|
@ -87,4 +87,4 @@ class Digraph {
|
|||
|
||||
}
|
||||
|
||||
export default Digraph;
|
||||
module.exports = Digraph;
|
|
@ -68,7 +68,7 @@ Have fun!
|
|||
|
||||
## Resolution order 🤔
|
||||
|
||||
The flecks server provides an interface (`flecks.buildConfig()`) for gathering configuration files
|
||||
The flecks server provides an interface (`flecks.resolveBuildConfig()`) 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. `server.webpack.config.js`.
|
||||
|
|
|
@ -1,3 +1,71 @@
|
|||
const defaultConfigFn = require('../src/server/build/default.eslint.config');
|
||||
const {spawnSync} = require('child_process');
|
||||
const {
|
||||
mkdirSync,
|
||||
readFileSync,
|
||||
statSync,
|
||||
writeFileSync,
|
||||
} = require('fs');
|
||||
const {join} = require('path');
|
||||
|
||||
module.exports = defaultConfigFn();
|
||||
const D = require('./debug');
|
||||
const Server = require('./server');
|
||||
|
||||
const debug = D('@flecks/core/build/eslint.config.js');
|
||||
|
||||
const {
|
||||
FLECKS_CORE_ROOT = process.cwd(),
|
||||
FLECKS_CORE_SYNC_FOR_ESLINT = false,
|
||||
} = process.env;
|
||||
|
||||
// This is kinda nuts, but ESLint doesn't support its configuration files returning a promise!
|
||||
if (FLECKS_CORE_SYNC_FOR_ESLINT) {
|
||||
(async () => {
|
||||
debug('bootstrapping flecks...');
|
||||
const flecks = await Server.from();
|
||||
debug('bootstrapped');
|
||||
// Load and finalize ESLint configuration.
|
||||
const eslintConfig = await require(
|
||||
await flecks.resolveBuildConfig('default.eslint.config.js'),
|
||||
)(flecks);
|
||||
const {resolve} = await require(
|
||||
await flecks.resolveBuildConfig('fleck.webpack.config.js'),
|
||||
)({}, {mode: 'development'}, flecks);
|
||||
eslintConfig.settings['import/resolver'].webpack = {config: {resolve}};
|
||||
// Write it out to stdout.
|
||||
process.stdout.write(JSON.stringify(eslintConfig, null, 2));
|
||||
})();
|
||||
}
|
||||
else {
|
||||
// Check cache first.
|
||||
const cacheDirectory = join(FLECKS_CORE_ROOT, 'node_modules', '.cache', '@flecks', 'core');
|
||||
try {
|
||||
statSync(join(cacheDirectory, 'eslint.config.json'));
|
||||
module.exports = JSON.parse(readFileSync(join(cacheDirectory, 'eslint.config.json')).toString());
|
||||
}
|
||||
catch (error) {
|
||||
// Just silly. By synchronously spawning... ourselves, the child can use async.
|
||||
const {stderr, stdout} = spawnSync('node', [__filename], {
|
||||
env: {
|
||||
FLECKS_CORE_SYNC_FOR_ESLINT: true,
|
||||
NODE_PATH: join(FLECKS_CORE_ROOT, 'node_modules'),
|
||||
...process.env,
|
||||
},
|
||||
});
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(stderr.toString());
|
||||
// Read the JSON written out to stdout.
|
||||
const json = stdout.toString();
|
||||
try {
|
||||
const parsed = JSON.parse(json);
|
||||
statSync(join(FLECKS_CORE_ROOT, 'node_modules'));
|
||||
mkdirSync(cacheDirectory, {recursive: true});
|
||||
// Cache.
|
||||
writeFileSync(join(cacheDirectory, 'eslint.config.json'), json);
|
||||
module.exports = parsed;
|
||||
}
|
||||
catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ const createListener = (fn, that, type, once) => ({
|
|||
bound: that ? fn.bind(that) : fn,
|
||||
});
|
||||
|
||||
export default function EventEmitterDecorator(Superclass) {
|
||||
module.exports = function EventEmitterDecorator(Superclass) {
|
||||
|
||||
return class EventEmitter extends Superclass {
|
||||
|
||||
|
@ -123,4 +123,4 @@ export default function EventEmitterDecorator(Superclass) {
|
|||
|
||||
};
|
||||
|
||||
}
|
||||
};
|
135
packages/core/build/explicate.js
Normal file
135
packages/core/build/explicate.js
Normal file
|
@ -0,0 +1,135 @@
|
|||
const {
|
||||
join,
|
||||
resolve,
|
||||
} = require('path');
|
||||
|
||||
module.exports = async function explicate(
|
||||
maybeAliasedPaths,
|
||||
{
|
||||
importer,
|
||||
platforms = ['server'],
|
||||
resolver,
|
||||
root,
|
||||
},
|
||||
) {
|
||||
const descriptors = {};
|
||||
const seen = {};
|
||||
const roots = {};
|
||||
function createDescriptor(maybeAliasedPath) {
|
||||
const index = maybeAliasedPath.indexOf(':');
|
||||
return -1 === index
|
||||
? {
|
||||
path: maybeAliasedPath,
|
||||
request: maybeAliasedPath,
|
||||
}
|
||||
: {
|
||||
path: maybeAliasedPath.slice(0, index),
|
||||
request: resolve(root, maybeAliasedPath.slice(index + 1)),
|
||||
};
|
||||
}
|
||||
async function doExplication(descriptor) {
|
||||
const {path, request} = descriptor;
|
||||
if (
|
||||
platforms
|
||||
.filter((platform) => platform.startsWith('!'))
|
||||
.map((platform) => platform.slice(1))
|
||||
.includes(path.split('/').pop())
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (path !== request) {
|
||||
resolver.addAlias(path, request);
|
||||
}
|
||||
descriptors[request] = descriptor;
|
||||
}
|
||||
async function getRootDescriptor(descriptor) {
|
||||
const {path, request} = descriptor;
|
||||
// Walk up and find the root, if any.
|
||||
const pathParts = path.split('/');
|
||||
const requestParts = request.split('/');
|
||||
let rootDescriptor;
|
||||
while (pathParts.length > 0 && requestParts.length > 0) {
|
||||
const candidate = requestParts.join('/');
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
if (await resolver.resolve(join(candidate, 'package.json'))) {
|
||||
rootDescriptor = {
|
||||
path: pathParts.join('/'),
|
||||
request: requestParts.join('/'),
|
||||
};
|
||||
break;
|
||||
}
|
||||
pathParts.pop();
|
||||
requestParts.pop();
|
||||
}
|
||||
return rootDescriptor;
|
||||
}
|
||||
function descriptorsAreTheSame(l, r) {
|
||||
return (l && !r) || (!l && r) ? false : l.request === r.request;
|
||||
}
|
||||
async function explicateDescriptor(descriptor) {
|
||||
if (descriptors[descriptor.request] || seen[descriptor.request]) {
|
||||
return;
|
||||
}
|
||||
seen[descriptor.request] = true;
|
||||
const areDescriptorsTheSame = descriptorsAreTheSame(
|
||||
descriptor,
|
||||
await getRootDescriptor(descriptor),
|
||||
);
|
||||
const resolved = await resolver.resolve(descriptor.request);
|
||||
if (resolved || areDescriptorsTheSame) {
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
await explicateRoot(descriptor);
|
||||
}
|
||||
if (!resolved && areDescriptorsTheSame) {
|
||||
descriptors[descriptor.request] = descriptor;
|
||||
}
|
||||
if (resolved) {
|
||||
await doExplication(descriptor);
|
||||
}
|
||||
await Promise.all(
|
||||
platforms
|
||||
.filter((platform) => !platform.startsWith('!'))
|
||||
.map(async (platform) => {
|
||||
if (await resolver.resolve(join(descriptor.request, platform))) {
|
||||
return doExplication({
|
||||
path: join(descriptor.path, platform),
|
||||
request: join(descriptor.request, platform),
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
}),
|
||||
);
|
||||
}
|
||||
async function explicateRoot(descriptor) {
|
||||
// Walk up and find the root, if any.
|
||||
const rootDescriptor = await getRootDescriptor(descriptor);
|
||||
if (!rootDescriptor || roots[rootDescriptor.request]) {
|
||||
return;
|
||||
}
|
||||
const {request} = rootDescriptor;
|
||||
roots[request] = true;
|
||||
// Import bootstrap script.
|
||||
const bootstrapPath = await resolver.resolve(join(request, 'build', 'flecks.bootstrap'));
|
||||
const bootstrap = bootstrapPath ? importer(bootstrapPath) : {};
|
||||
roots[request] = bootstrap;
|
||||
// Explicate dependcies.
|
||||
const {dependencies = []} = bootstrap;
|
||||
if (dependencies.length > 0) {
|
||||
await Promise.all(
|
||||
dependencies
|
||||
.map(createDescriptor)
|
||||
.map(explicateDescriptor),
|
||||
);
|
||||
}
|
||||
await explicateDescriptor(rootDescriptor);
|
||||
}
|
||||
await Promise.all(
|
||||
maybeAliasedPaths
|
||||
.map(createDescriptor)
|
||||
.map(explicateDescriptor),
|
||||
);
|
||||
return {
|
||||
descriptors,
|
||||
roots,
|
||||
};
|
||||
};
|
|
@ -5,34 +5,28 @@ const {
|
|||
join,
|
||||
} = require('path');
|
||||
|
||||
const babelmerge = require('babel-merge');
|
||||
const CopyPlugin = require('copy-webpack-plugin');
|
||||
const glob = require('glob');
|
||||
|
||||
const D = require('../../debug');
|
||||
const R = require('../../require');
|
||||
const {defaultConfig, externals, regexFromExtensions} = require('../webpack');
|
||||
const {defaultConfig, externals, regexFromExtensions} = require('./webpack');
|
||||
|
||||
const {
|
||||
FLECKS_CORE_ROOT = process.cwd(),
|
||||
} = process.env;
|
||||
|
||||
const debug = D('@flecks/core/server/build/fleck.webpack.config.js');
|
||||
const debugSilly = debug.extend('silly');
|
||||
|
||||
const source = join(FLECKS_CORE_ROOT, 'src');
|
||||
const tests = join(FLECKS_CORE_ROOT, 'test');
|
||||
|
||||
const resolveValidModulePath = (source) => (path) => {
|
||||
// Does the file resolve as source?
|
||||
try {
|
||||
R.resolve(`${source}/${path}`);
|
||||
require.resolve(`${source}/${path}`);
|
||||
}
|
||||
catch (error) {
|
||||
const ext = extname(path);
|
||||
// Try the implicit [path]/index[.ext] variation.
|
||||
try {
|
||||
R.resolve(`${source}/${dirname(path)}/${basename(path, ext)}/index${ext}`);
|
||||
require.resolve(`${source}/${dirname(path)}/${basename(path, ext)}/index${ext}`);
|
||||
}
|
||||
catch (error) {
|
||||
return false;
|
||||
|
@ -41,8 +35,8 @@ const resolveValidModulePath = (source) => (path) => {
|
|||
return true;
|
||||
};
|
||||
|
||||
module.exports = (env, argv, flecks) => {
|
||||
const {name, files = []} = R(join(FLECKS_CORE_ROOT, 'package.json'));
|
||||
module.exports = async (env, argv, flecks) => {
|
||||
const {name, files = []} = require(join(FLECKS_CORE_ROOT, 'package.json'));
|
||||
const config = defaultConfig(flecks, {
|
||||
externals: externals({importType: 'umd'}),
|
||||
node: {
|
||||
|
@ -86,6 +80,9 @@ module.exports = (env, argv, flecks) => {
|
|||
alias: {
|
||||
[name]: source,
|
||||
},
|
||||
fallback: {
|
||||
[name]: FLECKS_CORE_ROOT,
|
||||
},
|
||||
},
|
||||
stats: {
|
||||
colors: true,
|
||||
|
@ -93,30 +90,7 @@ module.exports = (env, argv, flecks) => {
|
|||
},
|
||||
target: 'node',
|
||||
});
|
||||
const merging = [
|
||||
{
|
||||
plugins: ['@babel/plugin-syntax-dynamic-import'],
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
shippedProposals: true,
|
||||
targets: {
|
||||
esmodules: true,
|
||||
node: 'current',
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
];
|
||||
if (flecks) {
|
||||
merging.push({configFile: flecks.buildConfig('babel.config.js')});
|
||||
const flecksBabelConfig = flecks.babel();
|
||||
debugSilly('flecks.config.js: babel: %j', flecksBabelConfig);
|
||||
merging.push(...flecksBabelConfig.map(([, babel]) => babel));
|
||||
}
|
||||
const babelConfig = babelmerge.all(merging);
|
||||
const babelConfig = await flecks.babel();
|
||||
const extensionsRegex = regexFromExtensions(config.resolve.extensions);
|
||||
config.module.rules.push(
|
||||
{
|
||||
|
@ -144,14 +118,12 @@ module.exports = (env, argv, flecks) => {
|
|||
});
|
||||
// Test entry.
|
||||
const testPaths = glob.sync(join(tests, '*.js'));
|
||||
const platforms = flecks
|
||||
? flecks.platforms
|
||||
: ['server'];
|
||||
const {platforms} = flecks;
|
||||
for (let i = 0; i < platforms.length; ++i) {
|
||||
testPaths.push(...glob.sync(join(tests, `platforms/${platforms[i]}/*.js`)));
|
||||
testPaths.push(...glob.sync(join(tests, platforms[i], '*.js')));
|
||||
}
|
||||
if (testPaths.length > 0) {
|
||||
config.entry.test = testPaths;
|
||||
config.entry.test = ['source-map-support/register', ...testPaths];
|
||||
}
|
||||
return config;
|
||||
};
|
60
packages/core/build/flecks.bootstrap.js
Normal file
60
packages/core/build/flecks.bootstrap.js
Normal file
|
@ -0,0 +1,60 @@
|
|||
const {join} = require('path');
|
||||
|
||||
const webpack = require('webpack');
|
||||
|
||||
const {commands} = require('./commands');
|
||||
|
||||
const {
|
||||
FLECKS_CORE_ROOT = process.cwd(),
|
||||
} = process.env;
|
||||
|
||||
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`),
|
||||
}),
|
||||
);
|
||||
}
|
||||
},
|
||||
'@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': () => ({
|
||||
/**
|
||||
* The ID of your application.
|
||||
*/
|
||||
id: 'flecks',
|
||||
/**
|
||||
* The package manager used for tasks.
|
||||
*/
|
||||
packageManager: 'npm',
|
||||
/**
|
||||
* Build targets to profile with `webpack.debug.ProfilingPlugin`.
|
||||
*/
|
||||
profile: [],
|
||||
}),
|
||||
};
|
|
@ -1,3 +0,0 @@
|
|||
module.exports = {
|
||||
exts: ['.mjs', '.js'],
|
||||
};
|
|
@ -1,18 +1,18 @@
|
|||
// eslint-disable-next-line max-classes-per-file
|
||||
import {
|
||||
const {
|
||||
basename,
|
||||
dirname,
|
||||
extname,
|
||||
join,
|
||||
} from 'path';
|
||||
} = require('path');
|
||||
|
||||
import get from 'lodash.get';
|
||||
import set from 'lodash.set';
|
||||
const get = require('lodash.get');
|
||||
const set = require('lodash.set');
|
||||
|
||||
import compose from './compose';
|
||||
import D from './debug';
|
||||
import Digraph from './digraph';
|
||||
import Middleware from './middleware';
|
||||
const compose = require('./compose');
|
||||
const D = require('./debug');
|
||||
const Digraph = require('./digraph');
|
||||
const Middleware = require('./middleware');
|
||||
|
||||
const debug = D('@flecks/core/flecks');
|
||||
const debugSilly = debug.extend('silly');
|
||||
|
@ -21,8 +21,8 @@ const debugSilly = debug.extend('silly');
|
|||
const HookPriority = Symbol.for('@flecks/core.hookPriority');
|
||||
|
||||
// Symbols for Gathered classes.
|
||||
export const ById = Symbol.for('@flecks/core.byId');
|
||||
export const ByType = Symbol.for('@flecks/core.byType');
|
||||
exports.ById = Symbol.for('@flecks/core.byId');
|
||||
exports.ByType = Symbol.for('@flecks/core.byType');
|
||||
|
||||
/**
|
||||
* Capitalize a string.
|
||||
|
@ -59,7 +59,7 @@ const wrapGathered = (Class, id, idProperty, type, typeProperty) => {
|
|||
return Subclass;
|
||||
};
|
||||
|
||||
export default class Flecks {
|
||||
exports.Flecks = class Flecks {
|
||||
|
||||
config = {};
|
||||
|
||||
|
@ -69,23 +69,19 @@ export default class Flecks {
|
|||
|
||||
hooks = {};
|
||||
|
||||
platforms = {};
|
||||
|
||||
/**
|
||||
* @param {object} init
|
||||
* @param {object} init.config The Flecks configuration (e.g. loaded from `flecks.yml`).
|
||||
* @param {string[]} init.platforms Platforms this instance is running on.
|
||||
* @param {object} runtime
|
||||
* @param {object} runtime.config configuration (e.g. loaded from `flecks.yml`).
|
||||
* @param {object} runtime.flecks fleck modules.
|
||||
*/
|
||||
constructor({
|
||||
config = {},
|
||||
flecks = {},
|
||||
platforms = [],
|
||||
} = {}) {
|
||||
const emptyConfigForAllFlecks = Object.fromEntries(
|
||||
Object.keys(flecks).map((path) => [path, {}]),
|
||||
);
|
||||
this.config = {...emptyConfigForAllFlecks, ...config};
|
||||
this.platforms = platforms;
|
||||
const entries = Object.entries(flecks);
|
||||
debugSilly('paths: %O', entries.map(([fleck]) => fleck));
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
|
@ -191,7 +187,6 @@ export default class Flecks {
|
|||
this.config = {};
|
||||
this.hooks = {};
|
||||
this.flecks = {};
|
||||
this.platforms = [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -206,17 +201,9 @@ export default class Flecks {
|
|||
}
|
||||
const flecks = this.lookupFlecks(hook);
|
||||
let expanded = [];
|
||||
// Expand configured flecks.
|
||||
for (let i = 0; i < flecks.length; ++i) {
|
||||
const fleck = flecks[i];
|
||||
expanded.push(fleck);
|
||||
for (let j = 0; j < this.platforms.length; ++j) {
|
||||
const platform = this.platforms[j];
|
||||
const variant = join(fleck, platform);
|
||||
if (this.fleck(variant)) {
|
||||
expanded.push(variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Handle elision.
|
||||
const index = expanded.findIndex((fleck) => '...' === fleck);
|
||||
|
@ -238,13 +225,6 @@ export default class Flecks {
|
|||
if (!expanded.includes(fleck)) {
|
||||
elided.push(fleck);
|
||||
}
|
||||
for (let j = 0; j < this.platforms.length; ++j) {
|
||||
const platform = this.platforms[j];
|
||||
const variant = join(fleck, platform);
|
||||
if (this.fleck(variant) && !expanded.includes(variant)) {
|
||||
elided.push(variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Map the fleck implementations to vertices in a dependency graph.
|
||||
const graph = this.flecksHookGraph([...before, ...elided, ...after], hook);
|
||||
|
@ -380,13 +360,13 @@ export default class Flecks {
|
|||
* @param {Object} config Configuration.
|
||||
* @returns {Flecks} A flecks instance.
|
||||
*/
|
||||
static from(config) {
|
||||
const {flecks} = config;
|
||||
static from(runtime) {
|
||||
const {flecks} = runtime;
|
||||
const mixins = Object.entries(flecks)
|
||||
.map(([, M]) => M.hooks?.['@flecks/core.mixin'])
|
||||
.filter((e) => e);
|
||||
const Flecks = compose(...mixins)(this);
|
||||
return new Flecks(config);
|
||||
return new Flecks(runtime);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -431,8 +411,8 @@ export default class Flecks {
|
|||
const gathered = {
|
||||
...ids,
|
||||
...types,
|
||||
[ById]: ids,
|
||||
[ByType]: types,
|
||||
[exports.ById]: ids,
|
||||
[exports.ByType]: types,
|
||||
};
|
||||
// Register for HMR?
|
||||
hotGathered.set(
|
||||
|
@ -444,7 +424,7 @@ export default class Flecks {
|
|||
gathered,
|
||||
},
|
||||
);
|
||||
debug("gathered '%s': %O", hook, Object.keys(gathered[ByType]));
|
||||
debug("gathered '%s': %O", hook, Object.keys(gathered[exports.ByType]));
|
||||
return gathered;
|
||||
}
|
||||
|
||||
|
@ -846,7 +826,10 @@ export default class Flecks {
|
|||
const {[type]: {[idProperty]: id}} = gathered;
|
||||
const Subclass = wrapGathered(Class, id, idProperty, type, typeProperty);
|
||||
// eslint-disable-next-line no-multi-assign
|
||||
gathered[type] = gathered[id] = gathered[ById][id] = gathered[ByType][type] = Subclass;
|
||||
gathered[type] = Subclass;
|
||||
gathered[id] = Subclass;
|
||||
gathered[exports.ById][id] = Subclass;
|
||||
gathered[exports.ByType][type] = Subclass;
|
||||
this.invoke('@flecks/core.hmr.gathered.class', Subclass, hook);
|
||||
});
|
||||
this.invoke('@flecks/core.hmr.gathered', gathered, hook);
|
||||
|
@ -905,7 +888,7 @@ export default class Flecks {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
Flecks.get = get;
|
||||
Flecks.set = set;
|
||||
exports.Flecks.get = get;
|
||||
exports.Flecks.set = set;
|
|
@ -1,2 +0,0 @@
|
|||
# This isn't a "real" `flecks.yml`. It only exists for testing purposes.
|
||||
'@flecks/core:./src': {}
|
65
packages/core/build/fleckspack.config.js
Normal file
65
packages/core/build/fleckspack.config.js
Normal file
|
@ -0,0 +1,65 @@
|
|||
require('source-map-support/register');
|
||||
|
||||
const D = require('./debug');
|
||||
const Server = require('./server');
|
||||
|
||||
const debug = D('@flecks/core/build/fleckspack.config.js');
|
||||
|
||||
const {
|
||||
FLECKS_CORE_BUILD_LIST = '',
|
||||
} = process.env;
|
||||
|
||||
const buildList = FLECKS_CORE_BUILD_LIST
|
||||
.split(',')
|
||||
.map((name) => name.trim())
|
||||
.filter((e) => e);
|
||||
|
||||
module.exports = async (env, argv) => {
|
||||
debug('bootstrapping flecks...');
|
||||
const flecks = await Server.from();
|
||||
debug('bootstrapped');
|
||||
debug('gathering configs');
|
||||
const {targets} = flecks;
|
||||
const building = targets
|
||||
.filter(([, target]) => 0 === buildList.length || buildList.includes(target));
|
||||
debug('building: %O', building.map(([target]) => target));
|
||||
if (0 === building.length) {
|
||||
debug('no build configuration found! aborting...');
|
||||
await new Promise(() => {});
|
||||
}
|
||||
const entries = await Promise.all(building.map(
|
||||
async ([fleck, target]) => {
|
||||
const configFn = require(await flecks.resolveBuildConfig(`${target}.webpack.config.js`, fleck));
|
||||
if ('function' !== typeof configFn) {
|
||||
debug(`'${
|
||||
target
|
||||
}' build configuration expected function got ${
|
||||
typeof configFn
|
||||
}! aborting...`);
|
||||
return undefined;
|
||||
}
|
||||
return [target, await configFn(env, argv, flecks)];
|
||||
},
|
||||
));
|
||||
await Promise.all(
|
||||
entries.map(async ([target, config]) => (
|
||||
Promise.all(flecks.invokeFlat('@flecks/core.build', target, config, env, argv))
|
||||
)),
|
||||
);
|
||||
const webpackConfigs = Object.fromEntries(entries);
|
||||
await Promise.all(flecks.invokeFlat('@flecks/core.build.alter', webpackConfigs, env, argv));
|
||||
const enterableWebpackConfigs = Object.values(webpackConfigs)
|
||||
.filter((webpackConfig) => {
|
||||
if (!webpackConfig.entry) {
|
||||
debug('webpack configurations %O had no entry... discarding', webpackConfig);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (0 === enterableWebpackConfigs.length) {
|
||||
debug('no webpack configuration found! aborting...');
|
||||
await new Promise(() => {});
|
||||
}
|
||||
debug('webpack configurations %O', enterableWebpackConfigs);
|
||||
return enterableWebpackConfigs;
|
||||
};
|
32
packages/core/build/load-config.js
Normal file
32
packages/core/build/load-config.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
const {readFile} = require('fs/promises');
|
||||
const {join} = require('path');
|
||||
|
||||
const D = require('./debug');
|
||||
|
||||
const debug = D('@flecks/core:load-config');
|
||||
|
||||
const {
|
||||
FLECKS_CORE_ROOT = process.cwd(),
|
||||
} = process.env;
|
||||
|
||||
module.exports = async function loadConfig() {
|
||||
try {
|
||||
const {load} = require('js-yaml');
|
||||
const filename = join(FLECKS_CORE_ROOT, 'build', 'flecks.yml');
|
||||
const buffer = await readFile(filename, 'utf8');
|
||||
debug('parsing configuration from YML...');
|
||||
return ['YML', load(buffer, {filename})];
|
||||
}
|
||||
catch (error) {
|
||||
if ('ENOENT' !== error.code) {
|
||||
throw error;
|
||||
}
|
||||
const {name} = require(join(FLECKS_CORE_ROOT, 'package.json'));
|
||||
const barebones = {'@flecks/core': {}, '@flecks/fleck': {}};
|
||||
if (barebones[name]) {
|
||||
delete barebones[name];
|
||||
}
|
||||
barebones[`${name}:${FLECKS_CORE_ROOT}`] = {};
|
||||
return ['barebones', barebones];
|
||||
}
|
||||
};
|
|
@ -1,4 +1,4 @@
|
|||
export default class Middleware {
|
||||
module.exports = class Middleware {
|
||||
|
||||
constructor(middleware = []) {
|
||||
this.middleware = [];
|
||||
|
@ -61,4 +61,4 @@ export default class Middleware {
|
|||
this.middleware.push(this.constructor.check(fn));
|
||||
}
|
||||
|
||||
}
|
||||
};
|
88
packages/core/build/resolver.js
Normal file
88
packages/core/build/resolver.js
Normal file
|
@ -0,0 +1,88 @@
|
|||
const {join} = require('path');
|
||||
|
||||
const {CachedInputFileSystem, ResolverFactory} = require('enhanced-resolve');
|
||||
const AppendPlugin = require('enhanced-resolve/lib/AppendPlugin');
|
||||
const AliasPlugin = require('enhanced-resolve/lib/AliasPlugin');
|
||||
const fs = require('graceful-fs');
|
||||
|
||||
const D = require('./debug');
|
||||
|
||||
const debug = D('@flecks/core/build/resolver');
|
||||
const debugSilly = debug.extend('silly');
|
||||
|
||||
const {
|
||||
FLECKS_CORE_ROOT = process.cwd(),
|
||||
} = process.env;
|
||||
|
||||
const nodeContext = {
|
||||
environments: ['node+es3+es5+process+native'],
|
||||
};
|
||||
|
||||
const nodeFileSystem = new CachedInputFileSystem(fs, 4000);
|
||||
|
||||
module.exports = class Resolver {
|
||||
|
||||
constructor(options) {
|
||||
this.resolver = ResolverFactory.createResolver({
|
||||
conditionNames: ['node'],
|
||||
extensions: ['.js', '.json', '.node'],
|
||||
fileSystem: nodeFileSystem,
|
||||
symlinks: false,
|
||||
...{
|
||||
modules: [join(FLECKS_CORE_ROOT, 'node_modules')],
|
||||
...options,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
addAlias(name, alias) {
|
||||
debugSilly("adding alias: '%s' -> '%s'", name, alias);
|
||||
new AliasPlugin(
|
||||
'raw-resolve',
|
||||
{name, onlyModule: false, alias},
|
||||
'internal-resolve',
|
||||
).apply(this.resolver);
|
||||
}
|
||||
|
||||
addExtensions(extensions) {
|
||||
debugSilly("adding extensions: '%O'", extensions);
|
||||
extensions.forEach((extension) => {
|
||||
new AppendPlugin('raw-file', extension, 'file').apply(this);
|
||||
});
|
||||
}
|
||||
|
||||
addFallback(name, alias) {
|
||||
debugSilly("adding fallback: '%s' -> '%s'", name, alias);
|
||||
new AliasPlugin(
|
||||
'described-resolve',
|
||||
{name, onlyModule: false, alias},
|
||||
'internal-resolve',
|
||||
).apply(this.resolver);
|
||||
}
|
||||
|
||||
static isResolutionError(error) {
|
||||
return error.message.startsWith("Can't resolve");
|
||||
}
|
||||
|
||||
async resolve(request) {
|
||||
try {
|
||||
return await new Promise((resolve, reject) => {
|
||||
this.resolver.resolve(nodeContext, FLECKS_CORE_ROOT, request, {}, (error, path) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
}
|
||||
else {
|
||||
resolve(path);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
if (!this.constructor.isResolutionError(error)) {
|
||||
throw error;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
318
packages/core/build/server.js
Normal file
318
packages/core/build/server.js
Normal file
|
@ -0,0 +1,318 @@
|
|||
const {realpath} = require('fs/promises');
|
||||
const {join} = require('path');
|
||||
|
||||
const babelmerge = require('babel-merge');
|
||||
const set = require('lodash.set');
|
||||
|
||||
const D = require('./debug');
|
||||
const explicate = require('./explicate');
|
||||
const {Flecks} = require('./flecks');
|
||||
const loadConfig = require('./load-config');
|
||||
const Resolver = require('./resolver');
|
||||
|
||||
const {
|
||||
FLECKS_CORE_ROOT = process.cwd(),
|
||||
} = process.env;
|
||||
|
||||
const debug = D('@flecks/core/build/bootstrap');
|
||||
const debugSilly = debug.extend('silly');
|
||||
|
||||
function environmentalize(path) {
|
||||
return path
|
||||
// - `@flecks/core` -> `flecks_core`
|
||||
.replace(/[^a-zA-Z0-9]/g, '_')
|
||||
.replace(/_*(.*)_*/, '$1');
|
||||
}
|
||||
|
||||
function environmentConfiguration(config) {
|
||||
const keys = Object.keys(process.env);
|
||||
Object.keys(config)
|
||||
.sort((l, r) => (l < r ? 1 : -1))
|
||||
.forEach((fleck) => {
|
||||
const prefix = `FLECKS_ENV__${environmentalize(fleck)}`;
|
||||
keys
|
||||
.filter((key) => key.startsWith(`${prefix}__`))
|
||||
.map((key) => {
|
||||
debug('reading environment from %s...', key);
|
||||
return [key.slice(prefix.length + 2), process.env[key]];
|
||||
})
|
||||
.map(([subkey, value]) => [subkey.split('_'), value])
|
||||
.forEach(([path, jsonOrString]) => {
|
||||
try {
|
||||
set(config, [fleck, ...path], JSON.parse(jsonOrString));
|
||||
debug('read (%s) as JSON', jsonOrString);
|
||||
}
|
||||
catch (error) {
|
||||
set(config, [fleck, ...path], jsonOrString);
|
||||
debug('read (%s) as string', jsonOrString);
|
||||
}
|
||||
});
|
||||
});
|
||||
return config;
|
||||
}
|
||||
|
||||
module.exports = class Server extends Flecks {
|
||||
|
||||
buildConfigs = {};
|
||||
|
||||
platforms = ['server'];
|
||||
|
||||
resolved = {};
|
||||
|
||||
resolver = new Resolver();
|
||||
|
||||
roots = {};
|
||||
|
||||
async babel() {
|
||||
const merging = [
|
||||
{
|
||||
plugins: ['@babel/plugin-syntax-dynamic-import'],
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
shippedProposals: true,
|
||||
targets: {
|
||||
esmodules: true,
|
||||
node: 'current',
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
];
|
||||
merging.push({configFile: await this.resolveBuildConfig('babel.config.js')});
|
||||
merging.push(...this.invokeFlat('@flecks/core.babel'));
|
||||
return babelmerge.all(merging);
|
||||
}
|
||||
|
||||
static async buildRuntime(originalConfig, platforms, flecks = {}) {
|
||||
const dealiasedConfig = Object.fromEntries(
|
||||
Object.entries(originalConfig)
|
||||
.map(([maybeAliasedPath, config]) => {
|
||||
const index = maybeAliasedPath.indexOf(':');
|
||||
return [
|
||||
-1 === index ? maybeAliasedPath : maybeAliasedPath.slice(0, index),
|
||||
config,
|
||||
];
|
||||
}),
|
||||
);
|
||||
const resolver = new Resolver();
|
||||
const explication = await explicate(
|
||||
Object.keys(originalConfig),
|
||||
{
|
||||
platforms,
|
||||
resolver,
|
||||
root: FLECKS_CORE_ROOT,
|
||||
importer: (request) => require(request),
|
||||
},
|
||||
);
|
||||
const runtime = {
|
||||
config: environmentConfiguration(
|
||||
Object.fromEntries(
|
||||
Object.values(explication.descriptors)
|
||||
.map(({path}) => [path, dealiasedConfig[path] || {}]),
|
||||
),
|
||||
),
|
||||
flecks: Object.fromEntries(
|
||||
Object.values(explication.descriptors)
|
||||
.map(({path, request}) => [path, flecks[path] || explication.roots[request] || {}]),
|
||||
),
|
||||
};
|
||||
const resolved = {};
|
||||
await Promise.all(
|
||||
Object.entries(explication.descriptors)
|
||||
.map(async ([, {path, request}]) => {
|
||||
try {
|
||||
if (path !== request || request !== await realpath(request)) {
|
||||
resolved[path] = request;
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line no-empty
|
||||
catch (error) {}
|
||||
}),
|
||||
);
|
||||
const reverseRequest = Object.fromEntries(
|
||||
Object.entries(explication.descriptors)
|
||||
.map(([, {path, request}]) => [request, path]),
|
||||
);
|
||||
return {
|
||||
resolved,
|
||||
resolver,
|
||||
roots: Object.fromEntries(
|
||||
Object.entries(explication.roots)
|
||||
.map(([request, bootstrap]) => [reverseRequest[request], {bootstrap, request}]),
|
||||
),
|
||||
runtime,
|
||||
};
|
||||
}
|
||||
|
||||
get extensions() {
|
||||
return this.invokeFlat('@flecks/core.exts').flat();
|
||||
}
|
||||
|
||||
static async from(
|
||||
{
|
||||
config: configParameter,
|
||||
flecks: configFlecks,
|
||||
platforms = ['server'],
|
||||
} = {},
|
||||
) {
|
||||
// Load or use parameterized configuration.
|
||||
let originalConfig;
|
||||
let configType = 'parameter';
|
||||
if (!configParameter) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
[configType, originalConfig] = await loadConfig();
|
||||
}
|
||||
else {
|
||||
originalConfig = JSON.parse(JSON.stringify(configParameter));
|
||||
}
|
||||
debug('bootstrap configuration (%s)', configType);
|
||||
debugSilly(originalConfig);
|
||||
const {
|
||||
resolved,
|
||||
resolver,
|
||||
roots,
|
||||
runtime,
|
||||
} = await this.buildRuntime(originalConfig, platforms, configFlecks);
|
||||
const flecks = super.from(runtime);
|
||||
flecks.roots = roots;
|
||||
flecks.platforms = platforms;
|
||||
flecks.resolved = resolved;
|
||||
flecks.resolver = resolver;
|
||||
flecks.loadBuildConfigs();
|
||||
return flecks;
|
||||
}
|
||||
|
||||
loadBuildConfigs() {
|
||||
Object.entries(this.invoke('@flecks/core.build.config'))
|
||||
.forEach(([fleck, configs]) => {
|
||||
configs.forEach((config) => {
|
||||
this.buildConfigs[config] = fleck;
|
||||
});
|
||||
});
|
||||
debugSilly('build configs loaded: %O', this.buildConfigs);
|
||||
}
|
||||
|
||||
async resolveBuildConfig(config, override) {
|
||||
const fleck = this.buildConfigs[config];
|
||||
if (!fleck) {
|
||||
throw new Error(`Unknown build config: '${config}'`);
|
||||
}
|
||||
const rootConfig = await this.resolver.resolve(join(FLECKS_CORE_ROOT, 'build', config));
|
||||
if (rootConfig) {
|
||||
return rootConfig;
|
||||
}
|
||||
if (override) {
|
||||
const overrideConfig = await this.resolver.resolve(join(override, 'build', config));
|
||||
if (overrideConfig) {
|
||||
return overrideConfig;
|
||||
}
|
||||
}
|
||||
return this.resolver.resolve(join(fleck, 'build', config));
|
||||
}
|
||||
|
||||
async runtimeCompiler(runtime, config, {allowlist = []} = {}) {
|
||||
// Compile.
|
||||
const needCompilation = Object.entries(this.resolved);
|
||||
if (needCompilation.length > 0) {
|
||||
const babelConfig = await this.babel();
|
||||
// const flecksBabelConfig = this.babel();
|
||||
// Alias and de-externalize.
|
||||
await Promise.all(
|
||||
needCompilation
|
||||
.map(async ([fleck, resolved]) => {
|
||||
allowlist.push(fleck);
|
||||
// Create alias.
|
||||
config.resolve.alias[fleck] = resolved;
|
||||
debugSilly('%s runtime de-externalized %s, alias: %s', runtime, fleck, resolved);
|
||||
// Alias this compiled fleck's `node_modules` to the root `node_modules`.
|
||||
config.resolve.alias[
|
||||
join(resolved, 'node_modules')
|
||||
] = join(FLECKS_CORE_ROOT, 'node_modules');
|
||||
config.module.rules.push(
|
||||
{
|
||||
test: /\.(m?jsx?)?$/,
|
||||
include: [resolved],
|
||||
use: [
|
||||
{
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: {
|
||||
cacheDirectory: true,
|
||||
babelrc: false,
|
||||
configFile: false,
|
||||
...babelConfig,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
}),
|
||||
);
|
||||
// Our very own lil' chunk.
|
||||
set(config, 'optimization.splitChunks.cacheGroups.flecks-compiled', {
|
||||
chunks: 'all',
|
||||
enforce: true,
|
||||
priority: 100,
|
||||
test: new RegExp(
|
||||
`(?:${
|
||||
Object.keys(this.resolved)
|
||||
.map((path) => path.replace(/[\\/]/g, '[\\/]')).join('|')
|
||||
})`,
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
get stubs() {
|
||||
return Object.values(this.flecks)
|
||||
.reduce(
|
||||
(r, {stubs = {}}) => (
|
||||
r.concat(
|
||||
Object.entries(stubs)
|
||||
.reduce(
|
||||
(r, [platform, stubs]) => (
|
||||
r.concat(this.platforms.includes(platform) ? stubs : [])
|
||||
),
|
||||
[],
|
||||
),
|
||||
)
|
||||
),
|
||||
[],
|
||||
).flat();
|
||||
}
|
||||
|
||||
get targets() {
|
||||
const targets = this.invoke('@flecks/core.targets');
|
||||
const duplicates = {};
|
||||
const entries = Object.entries(targets);
|
||||
const set = new Set();
|
||||
entries
|
||||
.forEach(([fleck, targets]) => {
|
||||
targets.forEach((target) => {
|
||||
if (set.has(target)) {
|
||||
if (!duplicates[target]) {
|
||||
duplicates[target] = [];
|
||||
}
|
||||
duplicates[target].push(fleck);
|
||||
}
|
||||
set.add(target);
|
||||
});
|
||||
});
|
||||
const errorMessage = Object.entries(duplicates).map(([target, flecks]) => (
|
||||
`Multiple flecks ('${flecks.join("', '")})' tried to build target '${target}'`
|
||||
)).join('\n');
|
||||
if (errorMessage) {
|
||||
throw new Error(`@flecks/core.targets:\n${errorMessage}`);
|
||||
}
|
||||
this.invoke('@flecks/core.targets.alter', set);
|
||||
return entries
|
||||
.map(([fleck, targets]) => (
|
||||
targets
|
||||
.filter((target) => set.has(target))
|
||||
.map((target) => [fleck, target])
|
||||
)).flat();
|
||||
}
|
||||
|
||||
};
|
|
@ -1,8 +1,8 @@
|
|||
// eslint-disable-next-line max-classes-per-file
|
||||
import JsonParse from 'jsonparse';
|
||||
import {Transform} from 'stream';
|
||||
const JsonParse = require('jsonparse');
|
||||
const {Transform} = require('stream');
|
||||
|
||||
export class JsonStream extends Transform {
|
||||
exports.JsonStream = class JsonStream extends Transform {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
@ -23,9 +23,9 @@ export class JsonStream extends Transform {
|
|||
this.parser.write(chunk);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
JsonStream.PrettyPrint = class extends Transform {
|
||||
exports.JsonStream.PrettyPrint = class extends Transform {
|
||||
|
||||
constructor(indent = 2) {
|
||||
super();
|
||||
|
@ -40,7 +40,7 @@ JsonStream.PrettyPrint = class extends Transform {
|
|||
|
||||
};
|
||||
|
||||
export const transform = (fn, opts = {}) => {
|
||||
exports.transform = (fn, opts = {}) => {
|
||||
class EasyTransform extends Transform {
|
||||
|
||||
constructor() {
|
15
packages/core/build/stub.js
Normal file
15
packages/core/build/stub.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
module.exports = function stub(stubs) {
|
||||
if (0 === stubs.length) {
|
||||
return;
|
||||
}
|
||||
const {Module} = require('module');
|
||||
const {require: Mr} = Module.prototype;
|
||||
Module.prototype.require = function hackedRequire(request, options) {
|
||||
for (let i = 0; i < stubs.length; ++i) {
|
||||
if (request.match(stubs[i])) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
return Mr.call(this, request, options);
|
||||
};
|
||||
};
|
|
@ -1,15 +0,0 @@
|
|||
const configFn = require('../src/server/build/webpack.config');
|
||||
const {executable} = require('../src/server/webpack');
|
||||
const eslintConfigFn = require('../src/server/build/default.eslint.config');
|
||||
|
||||
module.exports = async (env, argv) => {
|
||||
const config = await configFn(env, argv);
|
||||
config.plugins.push(...executable());
|
||||
const eslint = await eslintConfigFn();
|
||||
eslint.settings['import/resolver'].webpack = {
|
||||
config: {
|
||||
resolve: config.resolve,
|
||||
},
|
||||
};
|
||||
return config;
|
||||
};
|
|
@ -20,10 +20,7 @@ exports.banner = (options) => (
|
|||
exports.copy = (options) => (new CopyPlugin(options));
|
||||
|
||||
exports.defaultConfig = (flecks, specializedConfig) => {
|
||||
const extensions = ['.mjs', '.js', '.json', '.wasm'];
|
||||
if (flecks) {
|
||||
extensions.push(...flecks.exts());
|
||||
}
|
||||
const {extensions} = flecks;
|
||||
const extensionsRegex = exports.regexFromExtensions(extensions);
|
||||
const defaults = {
|
||||
context: FLECKS_CORE_ROOT,
|
||||
|
@ -47,10 +44,10 @@ exports.defaultConfig = (flecks, specializedConfig) => {
|
|||
alias: {},
|
||||
extensions,
|
||||
fallback: {},
|
||||
modules: [
|
||||
'node_modules',
|
||||
join(FLECKS_CORE_ROOT, 'node_modules'),
|
||||
],
|
||||
// modules: [
|
||||
// 'node_modules',
|
||||
// join(FLECKS_CORE_ROOT, 'node_modules'),
|
||||
// ],
|
||||
},
|
||||
stats: {
|
||||
colors: true,
|
||||
|
@ -90,10 +87,6 @@ exports.defaultConfig = (flecks, specializedConfig) => {
|
|||
|
||||
// Include a shebang and set the executable bit..
|
||||
exports.executable = () => ([
|
||||
exports.banner({
|
||||
banner: '#!/usr/bin/env node',
|
||||
include: /^cli\.js$/,
|
||||
}),
|
||||
new class Executable {
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
|
@ -101,7 +94,7 @@ exports.executable = () => ([
|
|||
compiler.hooks.afterEmit.tapAsync(
|
||||
'Executable',
|
||||
(compilation, callback) => {
|
||||
chmod(join(FLECKS_CORE_ROOT, 'dist', 'cli.js'), 0o755, callback);
|
||||
chmod(join(FLECKS_CORE_ROOT, 'dist', 'build', 'cli.js'), 0o755, callback);
|
||||
},
|
||||
);
|
||||
}
|
|
@ -8,38 +8,26 @@
|
|||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"version": "2.0.3",
|
||||
"version": "3.0.0",
|
||||
"main": "index.js",
|
||||
"author": "cha0s",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"flecks": "./cli.js"
|
||||
"flecks": "./build/cli.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "NODE_PATH=./node_modules webpack --config ./build/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",
|
||||
"lint": "NODE_PATH=./node_modules eslint --config ./build/eslint.config.js .",
|
||||
"postversion": "cp package.json dist",
|
||||
"test": "npm run build && mocha -t 10000 --colors ./dist/test.js"
|
||||
"test": "npm run build -d && mocha -t 10000 --colors ./dist/test.js"
|
||||
},
|
||||
"files": [
|
||||
"build",
|
||||
"cli.js",
|
||||
"cli.js.map",
|
||||
"index.js",
|
||||
"index.js.map",
|
||||
"server.js",
|
||||
"server.js.map",
|
||||
"server/build/babel.config.js",
|
||||
"server/build/babel.config.js.map",
|
||||
"server/build/default.eslint.config.js",
|
||||
"server/build/default.eslint.config.js.map",
|
||||
"server/build/eslint.config.js",
|
||||
"server/build/eslint.config.js.map",
|
||||
"server/build/webpack.config.js",
|
||||
"server/build/webpack.config.js.map",
|
||||
"server/build/fleckspack.config.js",
|
||||
"server/build/fleckspack.config.js.map",
|
||||
"src",
|
||||
"start.js",
|
||||
"start.js.map",
|
||||
|
@ -51,13 +39,10 @@
|
|||
"@babel/core": "^7.12.10",
|
||||
"@babel/eslint-parser": "^7.23.3",
|
||||
"@babel/eslint-plugin": "^7.22.10",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.12.16",
|
||||
"@babel/plugin-transform-regenerator": "^7.16.7",
|
||||
"@babel/preset-env": "^7.12.11",
|
||||
"@babel/register": "7.13.0",
|
||||
"babel-loader": "^9.1.3",
|
||||
"babel-merge": "^3.0.0",
|
||||
"babel-plugin-prepend": "^1.0.2",
|
||||
"chai": "4.2.0",
|
||||
"chai-as-promised": "7.1.1",
|
||||
"commander": "11.1.0",
|
||||
|
@ -72,17 +57,16 @@
|
|||
"eslint-plugin-jsx-a11y": "^6.8.0",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-webpack-plugin": "^4.0.1",
|
||||
"eslint-webpack-plugin": "^3.2.0",
|
||||
"glob": "^10.3.10",
|
||||
"globals": "^13.23.0",
|
||||
"graceful-fs": "^4.2.11",
|
||||
"js-yaml": "4.1.0",
|
||||
"jsonparse": "^1.3.1",
|
||||
"lodash.flatten": "^4.4.0",
|
||||
"lodash.get": "^4.4.2",
|
||||
"lodash.intersectionby": "4.7.0",
|
||||
"lodash.set": "^4.3.2",
|
||||
"mocha": "^8.3.2",
|
||||
"pirates": "^4.0.5",
|
||||
"null-loader": "^4.0.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"source-map-loader": "4.0.1",
|
||||
"source-map-support": "0.5.19",
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export default class {}
|
|
@ -1,116 +0,0 @@
|
|||
import {fork} from 'child_process';
|
||||
import {join, resolve, sep} from 'path';
|
||||
|
||||
import {Command} from 'commander';
|
||||
|
||||
import D from './debug';
|
||||
import {processCode} from './server/commands';
|
||||
import Flecks from './server/flecks';
|
||||
|
||||
const {
|
||||
FLECKS_CORE_ROOT = process.cwd(),
|
||||
} = process.env;
|
||||
|
||||
const debug = D('@flecks/core/cli');
|
||||
const debugSilly = debug.extend('silly');
|
||||
|
||||
// Guarantee local node_modules path.
|
||||
const defaultNodeModules = resolve(join(FLECKS_CORE_ROOT, 'node_modules'));
|
||||
const nodePathSeparator = '/' === sep ? ':' : ';';
|
||||
let updatedNodePath;
|
||||
if (!process.env.NODE_PATH) {
|
||||
updatedNodePath = defaultNodeModules;
|
||||
}
|
||||
else {
|
||||
const parts = process.env.NODE_PATH.split(nodePathSeparator);
|
||||
if (!parts.some((part) => resolve(part) === defaultNodeModules)) {
|
||||
parts.push(defaultNodeModules);
|
||||
updatedNodePath = parts.join(nodePathSeparator);
|
||||
}
|
||||
}
|
||||
// Guarantee symlink preservation for linked flecks.
|
||||
const updateSymlinkPreservation = !process.env.NODE_PRESERVE_SYMLINKS;
|
||||
|
||||
const environmentUpdates = (
|
||||
updatedNodePath
|
||||
|| updateSymlinkPreservation
|
||||
)
|
||||
? {
|
||||
NODE_PATH: updatedNodePath,
|
||||
NODE_PRESERVE_SYMLINKS: 1,
|
||||
}
|
||||
: undefined;
|
||||
if (environmentUpdates) {
|
||||
debugSilly('updating environment, forking with %O...', environmentUpdates);
|
||||
const forkOptions = {
|
||||
env: {
|
||||
...process.env,
|
||||
...environmentUpdates,
|
||||
DEBUG_COLORS: 'true',
|
||||
},
|
||||
stdio: 'inherit',
|
||||
};
|
||||
fork(__filename, process.argv.slice(2), forkOptions)
|
||||
.on('exit', (code, signal) => {
|
||||
process.exitCode = code;
|
||||
process.exit(signal);
|
||||
});
|
||||
}
|
||||
else {
|
||||
// Asynchronous command process code forwarding.
|
||||
const forwardProcessCode = (fn) => async (...args) => {
|
||||
const child = await fn(...args);
|
||||
if ('object' !== typeof child) {
|
||||
const code = 'undefined' !== typeof child ? child : 0;
|
||||
debugSilly('action returned code %d', code);
|
||||
process.exitCode = code;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const code = await processCode(child);
|
||||
debugSilly('action exited with code %d', code);
|
||||
process.exitCode = code;
|
||||
}
|
||||
catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
process.exitCode = child.exitCode || 1;
|
||||
}
|
||||
};
|
||||
// Initialize Commander.
|
||||
const program = new Command();
|
||||
program
|
||||
.enablePositionalOptions()
|
||||
.name('flecks')
|
||||
.usage('[command] [...]');
|
||||
// Bootstrap.
|
||||
(async () => {
|
||||
debugSilly('bootstrapping flecks...');
|
||||
const flecks = Flecks.bootstrap();
|
||||
debugSilly('bootstrapped');
|
||||
// Register commands.
|
||||
const commands = flecks.invokeMerge('@flecks/core.commands', program);
|
||||
const keys = Object.keys(commands).sort();
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
const {
|
||||
action,
|
||||
args = [],
|
||||
description,
|
||||
name = keys[i],
|
||||
options = [],
|
||||
} = commands[keys[i]];
|
||||
debugSilly('adding command %s...', name);
|
||||
const cmd = program.command(name);
|
||||
cmd.description(description);
|
||||
for (let i = 0; i < args.length; ++i) {
|
||||
cmd.addArgument(args[i]);
|
||||
}
|
||||
for (let i = 0; i < options.length; ++i) {
|
||||
cmd.option(...options[i]);
|
||||
}
|
||||
cmd.action(forwardProcessCode(action));
|
||||
}
|
||||
// Parse commandline.
|
||||
program.parse(process.argv);
|
||||
})();
|
||||
}
|
|
@ -1,18 +1,9 @@
|
|||
export {default as Class} from './class';
|
||||
export {default as compose} from './compose';
|
||||
export {default as D} from './debug';
|
||||
export {default as EventEmitter} from './event-emitter';
|
||||
export {default as Class} from '../build/class';
|
||||
export {default as compose} from '../build/compose';
|
||||
export {default as D} from '../build/debug';
|
||||
export {default as EventEmitter} from '../build/event-emitter';
|
||||
export {
|
||||
default as Flecks,
|
||||
ById,
|
||||
ByType,
|
||||
} from './flecks';
|
||||
|
||||
export const hooks = {
|
||||
'@flecks/core.config': () => ({
|
||||
/**
|
||||
* The ID of your application.
|
||||
*/
|
||||
id: 'flecks',
|
||||
}),
|
||||
};
|
||||
Flecks,
|
||||
} from '../build/flecks';
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
// Get a runtime require function by hook or by crook. :)
|
||||
|
||||
// eslint-disable-next-line no-eval
|
||||
module.exports = eval('"undefined" !== typeof require ? require : undefined');
|
|
@ -1,93 +0,0 @@
|
|||
const babelmerge = require('babel-merge');
|
||||
const globals = require('globals');
|
||||
|
||||
const R = require('../../require');
|
||||
|
||||
module.exports = (flecks) => {
|
||||
const merging = [
|
||||
{
|
||||
plugins: [R.resolve('@babel/plugin-syntax-dynamic-import')],
|
||||
presets: [
|
||||
[
|
||||
R.resolve('@babel/preset-env'),
|
||||
{
|
||||
shippedProposals: true,
|
||||
targets: {
|
||||
esmodules: true,
|
||||
node: 'current',
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
];
|
||||
if (flecks) {
|
||||
merging.push({configFile: flecks.buildConfig('babel.config.js')});
|
||||
const flecksBabelConfig = flecks.babel();
|
||||
merging.push(...flecksBabelConfig.map(([, babel]) => babel));
|
||||
}
|
||||
const babelConfig = babelmerge.all(merging);
|
||||
return {
|
||||
extends: [
|
||||
R.resolve('eslint-config-airbnb'),
|
||||
R.resolve('eslint-config-airbnb/hooks'),
|
||||
],
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.es2021,
|
||||
...globals.mocha,
|
||||
...globals.node,
|
||||
__non_webpack_require__: true,
|
||||
},
|
||||
ignorePatterns: [
|
||||
'dist/**',
|
||||
// Not even gonna try.
|
||||
'build/dox/hooks.js',
|
||||
],
|
||||
overrides: [
|
||||
{
|
||||
files: [
|
||||
'test/**/*.js',
|
||||
],
|
||||
rules: {
|
||||
'brace-style': 'off',
|
||||
'class-methods-use-this': 'off',
|
||||
'import/no-extraneous-dependencies': 'off',
|
||||
'import/no-unresolved': 'off',
|
||||
'max-classes-per-file': 'off',
|
||||
'no-new': 'off',
|
||||
'no-unused-expressions': 'off',
|
||||
'padded-blocks': 'off',
|
||||
},
|
||||
},
|
||||
],
|
||||
parser: R.resolve('@babel/eslint-parser'),
|
||||
parserOptions: {
|
||||
requireConfigFile: false,
|
||||
babelOptions: babelConfig,
|
||||
},
|
||||
plugins: ['@babel'],
|
||||
rules: {
|
||||
'brace-style': ['error', 'stroustrup'],
|
||||
// Bug: https://github.com/import-js/eslint-plugin-import/issues/2181
|
||||
'import/no-import-module-exports': 'off',
|
||||
'import/prefer-default-export': 'off',
|
||||
'jsx-a11y/control-has-associated-label': ['error', {assert: 'either'}],
|
||||
'jsx-a11y/label-has-associated-control': ['error', {assert: 'either'}],
|
||||
'no-param-reassign': ['error', {props: false}],
|
||||
'no-plusplus': 'off',
|
||||
'no-shadow': 'off',
|
||||
'object-curly-spacing': 'off',
|
||||
'padded-blocks': ['error', {classes: 'always'}],
|
||||
yoda: 'off',
|
||||
},
|
||||
settings: {
|
||||
'import/resolver': {
|
||||
node: {},
|
||||
},
|
||||
react: {
|
||||
version: '18.2.0',
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
|
@ -1,65 +0,0 @@
|
|||
const {spawnSync} = require('child_process');
|
||||
const {
|
||||
mkdirSync,
|
||||
readFileSync,
|
||||
statSync,
|
||||
writeFileSync,
|
||||
} = require('fs');
|
||||
const {join} = require('path');
|
||||
|
||||
const D = require('../../debug');
|
||||
const {default: Flecks} = require('../flecks');
|
||||
|
||||
const debug = D('@flecks/core/server/build/eslint.config.js');
|
||||
|
||||
const {
|
||||
FLECKS_CORE_ROOT = process.cwd(),
|
||||
FLECKS_CORE_SYNC_FOR_ESLINT = false,
|
||||
} = process.env;
|
||||
|
||||
// This is kinda nuts, but ESLint doesn't support its configuration files returning a promise!
|
||||
if (FLECKS_CORE_SYNC_FOR_ESLINT) {
|
||||
(async () => {
|
||||
debug('bootstrapping flecks...');
|
||||
const flecks = Flecks.bootstrap();
|
||||
debug('bootstrapped');
|
||||
const eslintConfigFn = __non_webpack_require__(flecks.buildConfig('default.eslint.config.js'));
|
||||
const eslintConfig = await eslintConfigFn(flecks);
|
||||
const webpackConfigFn = __non_webpack_require__(flecks.buildConfig('webpack.config.js', 'fleck'));
|
||||
const webpackConfig = await webpackConfigFn({}, {mode: 'development'}, flecks);
|
||||
eslintConfig.settings['import/resolver'].webpack = {
|
||||
config: {
|
||||
resolve: webpackConfig.resolve,
|
||||
},
|
||||
};
|
||||
process.stdout.write(JSON.stringify(eslintConfig, null, 2));
|
||||
})();
|
||||
}
|
||||
else {
|
||||
const cacheDirectory = join(FLECKS_CORE_ROOT, 'node_modules', '.cache', '@flecks', 'core');
|
||||
try {
|
||||
statSync(join(cacheDirectory, 'eslint.config.json'));
|
||||
module.exports = JSON.parse(readFileSync(join(cacheDirectory, 'eslint.config.json')).toString());
|
||||
}
|
||||
catch (error) {
|
||||
// Just silly. By synchronously spawning... ourselves, the spawned copy can use async.
|
||||
const {stderr, stdout} = spawnSync('node', [__filename], {
|
||||
env: {
|
||||
FLECKS_CORE_SYNC_FOR_ESLINT: true,
|
||||
NODE_PATH: join(FLECKS_CORE_ROOT, 'node_modules'),
|
||||
...process.env,
|
||||
},
|
||||
});
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(stderr.toString());
|
||||
const json = stdout.toString();
|
||||
try {
|
||||
statSync(join(FLECKS_CORE_ROOT, 'node_modules'));
|
||||
mkdirSync(cacheDirectory, {recursive: true});
|
||||
writeFileSync(join(cacheDirectory, 'eslint.config.json'), json);
|
||||
}
|
||||
// eslint-disable-next-line no-empty
|
||||
catch (error) {}
|
||||
module.exports = JSON.parse(json);
|
||||
}
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
/* eslint-disable import/first */
|
||||
import 'source-map-support/register';
|
||||
|
||||
if ('production' !== process.env.NODE_ENV) {
|
||||
try {
|
||||
// eslint-disable-next-line global-require, import/no-unresolved
|
||||
__non_webpack_require__('dotenv/config');
|
||||
}
|
||||
// eslint-disable-next-line no-empty
|
||||
catch (error) {}
|
||||
}
|
||||
|
||||
import intersectionBy from 'lodash.intersectionby';
|
||||
|
||||
import D from '../../debug';
|
||||
import Flecks from '../flecks';
|
||||
|
||||
const debug = D('@flecks/core/server/build/fleckspack.config.js');
|
||||
|
||||
const {
|
||||
FLECKS_CORE_BUILD_LIST = '',
|
||||
FLECKS_CORE_ROOT = process.cwd(),
|
||||
} = process.env;
|
||||
|
||||
const buildList = FLECKS_CORE_BUILD_LIST
|
||||
.split(',')
|
||||
.map((name) => name.trim())
|
||||
.filter((e) => e);
|
||||
|
||||
export default async (env, argv) => {
|
||||
debug('bootstrapping flecks...');
|
||||
const flecks = Flecks.bootstrap();
|
||||
debug('bootstrapped');
|
||||
|
||||
debug('gathering configs');
|
||||
const targets = [];
|
||||
Object.entries(flecks.invoke('@flecks/core.targets'))
|
||||
.forEach(([fleck, fleckTargets]) => {
|
||||
intersectionBy(fleckTargets, buildList.length ? buildList : fleckTargets)
|
||||
.forEach((target) => {
|
||||
targets.push([target, fleck]);
|
||||
});
|
||||
});
|
||||
debug('building: %O', targets.map(([target]) => target));
|
||||
if (0 === targets.length) {
|
||||
debug('no build configuration found! aborting...');
|
||||
await new Promise(() => {});
|
||||
}
|
||||
const entries = await Promise.all(targets.map(
|
||||
async ([target, fleck]) => {
|
||||
const buildConfig = flecks.resolveBuildConfig(
|
||||
[
|
||||
FLECKS_CORE_ROOT,
|
||||
flecks.resolvePath(fleck),
|
||||
],
|
||||
[
|
||||
`${target}.webpack.config.js`,
|
||||
'webpack.config.js',
|
||||
],
|
||||
);
|
||||
const configFn = __non_webpack_require__(buildConfig);
|
||||
if ('function' !== typeof configFn) {
|
||||
debug(`'${
|
||||
target
|
||||
}' build configuration expected function got ${
|
||||
typeof configFn
|
||||
}! aborting...`);
|
||||
return undefined;
|
||||
}
|
||||
return [target, await configFn(env, argv, flecks)];
|
||||
},
|
||||
));
|
||||
await Promise.all(
|
||||
entries.map(async ([target, config]) => (
|
||||
flecks.invokeFlat('@flecks/core.build', target, config, env, argv)
|
||||
)),
|
||||
);
|
||||
const webpackConfigs = Object.fromEntries(entries);
|
||||
await Promise.all(flecks.invokeFlat('@flecks/core.build.alter', webpackConfigs, env, argv));
|
||||
const enterableWebpackConfigs = Object.values(webpackConfigs)
|
||||
.filter((webpackConfig) => {
|
||||
if (!webpackConfig.entry) {
|
||||
debug('webpack configurations %O had no entry... discarding', webpackConfig);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (0 === enterableWebpackConfigs.length) {
|
||||
debug('no webpack configuration found! aborting...');
|
||||
await new Promise(() => {});
|
||||
}
|
||||
debug('webpack configurations %O', enterableWebpackConfigs);
|
||||
return enterableWebpackConfigs;
|
||||
};
|
|
@ -1,163 +0,0 @@
|
|||
import {statSync} from 'fs';
|
||||
import {dirname, sep} from 'path';
|
||||
|
||||
import {
|
||||
getEnv,
|
||||
OptionManager,
|
||||
transformSync,
|
||||
version,
|
||||
} from '@babel/core';
|
||||
import sourceMapSupport from 'source-map-support';
|
||||
|
||||
sourceMapSupport.install();
|
||||
|
||||
const cache = require('@babel/register/lib/cache');
|
||||
|
||||
const identity = (i) => i;
|
||||
|
||||
// This is basically what @babel/register does, but better in several ways.
|
||||
class Compiler {
|
||||
|
||||
constructor(options) {
|
||||
// Make some goodies exist in nodespace.
|
||||
this.options = {
|
||||
...options,
|
||||
plugins: [
|
||||
...(options.plugins || []),
|
||||
...this.constructor.nodespaceBabelPlugins(),
|
||||
],
|
||||
};
|
||||
this.constructor.warmUpCache();
|
||||
}
|
||||
|
||||
compile(input, request) {
|
||||
const options = new OptionManager()
|
||||
.init({
|
||||
sourceRoot: `${dirname(request)}${sep}`,
|
||||
...this.options,
|
||||
filename: request,
|
||||
});
|
||||
if (null === options) {
|
||||
return null;
|
||||
}
|
||||
const {cached, store} = this.lookup(options, request);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
const {code, map} = transformSync(input, {
|
||||
...options,
|
||||
sourceMaps: 'both',
|
||||
ast: false,
|
||||
});
|
||||
this.constructor.maps[request] = map;
|
||||
return store({code, map});
|
||||
}
|
||||
|
||||
static installSourceMapSupport() {
|
||||
this.maps = Object.create(null);
|
||||
sourceMapSupport.install({
|
||||
handleUncaughtExceptions: false,
|
||||
environment: 'node',
|
||||
retrieveSourceMap: (request) => {
|
||||
const map = this.maps[request];
|
||||
if (map) {
|
||||
return {url: null, map};
|
||||
}
|
||||
return null;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
lookup(options, request) {
|
||||
if (!this.constructor.cache) {
|
||||
return {
|
||||
cached: null,
|
||||
store: identity,
|
||||
};
|
||||
}
|
||||
let cacheKey = `${JSON.stringify(options)}:${version}`;
|
||||
const env = getEnv();
|
||||
if (env) {
|
||||
cacheKey += `:${env}`;
|
||||
}
|
||||
const cached = this.constructor.cache[cacheKey];
|
||||
const mtime = +statSync(request).mtime;
|
||||
if (cached && cached.mtime === mtime) {
|
||||
return {
|
||||
cached: cached.value,
|
||||
store: identity,
|
||||
};
|
||||
}
|
||||
return {
|
||||
cached: null,
|
||||
store: (value) => {
|
||||
this.constructor.cache[cacheKey] = {mtime, value};
|
||||
cache.setDirty();
|
||||
return value;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
static nodespaceBabelPlugins() {
|
||||
return [
|
||||
[
|
||||
'prepend',
|
||||
{
|
||||
prepend: [
|
||||
'require.context = (',
|
||||
' directory,',
|
||||
' useSubdirectories = true,',
|
||||
' regExp = /^\\.\\/.*$/,',
|
||||
' mode = "sync",',
|
||||
') => {',
|
||||
' const glob = require("glob");',
|
||||
' const {join} = require("path");',
|
||||
' const {resolve, sep} = require("path");',
|
||||
' const keys = glob.sync(',
|
||||
' useSubdirectories ? "**/*" : "*",',
|
||||
' {cwd: resolve(__dirname, directory)},',
|
||||
' )',
|
||||
' .filter((key) => key.match(regExp))',
|
||||
' .map(',
|
||||
' (key) => (',
|
||||
' -1 !== [".".charCodeAt(0), "/".charCodeAt(0)].indexOf(key.charCodeAt(0))',
|
||||
' ? key',
|
||||
' : ("." + sep + key)',
|
||||
' ),',
|
||||
' );',
|
||||
' const R = (request) => {',
|
||||
' if (-1 === keys.indexOf(request)) {',
|
||||
// eslint-disable-next-line no-template-curly-in-string
|
||||
' throw new Error(`Cannot find module \'${request}\'`);',
|
||||
' }',
|
||||
' return require(join(__dirname, directory, request));',
|
||||
' };',
|
||||
' R.id = __filename',
|
||||
' R.keys = () => keys;',
|
||||
' return R;',
|
||||
'};',
|
||||
].join('\n'),
|
||||
},
|
||||
'require.context',
|
||||
],
|
||||
[
|
||||
'prepend',
|
||||
{
|
||||
prepend: '"undefined" === typeof global.__non_webpack_require__ && (global.__non_webpack_require__ = require);',
|
||||
},
|
||||
'__non_webpack_require__',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
static warmUpCache() {
|
||||
if ('undefined' === typeof this.cache) {
|
||||
cache.load();
|
||||
this.cache = cache.get();
|
||||
this.installSourceMapSupport();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Compiler;
|
|
@ -1,733 +0,0 @@
|
|||
import {
|
||||
readFileSync,
|
||||
realpathSync,
|
||||
statSync,
|
||||
} from 'fs';
|
||||
import {readFile, writeFile} from 'fs/promises';
|
||||
import {
|
||||
basename,
|
||||
dirname,
|
||||
extname,
|
||||
isAbsolute,
|
||||
join,
|
||||
resolve,
|
||||
sep,
|
||||
} from 'path';
|
||||
|
||||
import babelmerge from 'babel-merge';
|
||||
import enhancedResolve from 'enhanced-resolve';
|
||||
import {dump as dumpYml, load as loadYml} from 'js-yaml';
|
||||
import {addHook} from 'pirates';
|
||||
|
||||
import D from '../debug';
|
||||
import Flecks from '../flecks';
|
||||
import R from '../require';
|
||||
import Compiler from './compiler';
|
||||
|
||||
const {
|
||||
FLECKS_CORE_ROOT = process.cwd(),
|
||||
FLECKS_YML = 'flecks.yml',
|
||||
} = process.env;
|
||||
|
||||
const debug = D('@flecks/core/flecks/server');
|
||||
const debugSilly = debug.extend('silly');
|
||||
|
||||
export default class ServerFlecks extends Flecks {
|
||||
|
||||
constructor(options = {}) {
|
||||
super(options);
|
||||
this.overrideConfigFromEnvironment();
|
||||
this.buildConfigs = {};
|
||||
this.loadBuildConfigs();
|
||||
this.flecksConfig = options.flecksConfig || {};
|
||||
this.resolver = options.resolver || {};
|
||||
}
|
||||
|
||||
static async addFleckToYml(fleck, path) {
|
||||
const key = [fleck].concat(path ? `.${sep}${join('packages', path, 'src')}` : []).join(':');
|
||||
const ymlPath = join(FLECKS_CORE_ROOT, 'build', 'flecks.yml');
|
||||
let yml = loadYml(await readFile(ymlPath));
|
||||
yml = Object.fromEntries(Object.entries(yml).concat([[key, {}]]));
|
||||
await writeFile(ymlPath, dumpYml(yml, {sortKeys: true}));
|
||||
}
|
||||
|
||||
get aliasedConfig() {
|
||||
const aliases = this.aliases();
|
||||
return Object.fromEntries(
|
||||
Object.entries(
|
||||
this.config,
|
||||
)
|
||||
.map(([path, config]) => [
|
||||
this.fleckIsAliased(path) ? `${path}:${aliases[path]}` : path,
|
||||
config,
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
aliases() {
|
||||
return this.constructor.aliases(this.flecksConfig);
|
||||
}
|
||||
|
||||
static aliases(flecksConfig) {
|
||||
const keys = Object.keys(flecksConfig);
|
||||
let aliases = {};
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
const key = keys[i];
|
||||
const config = flecksConfig[key];
|
||||
if (config.aliases && Object.keys(config.aliases).length > 0) {
|
||||
aliases = {...aliases, ...config.aliases};
|
||||
}
|
||||
}
|
||||
return aliases;
|
||||
}
|
||||
|
||||
babel() {
|
||||
return this.constructor.babel(this.flecksConfig);
|
||||
}
|
||||
|
||||
static babel(flecksConfig) {
|
||||
return Object.entries(flecksConfig)
|
||||
.filter(([, {babel}]) => babel)
|
||||
.map(([key, {babel}]) => [key, babel]);
|
||||
}
|
||||
|
||||
static bootstrap(
|
||||
{
|
||||
config,
|
||||
platforms = ['server'],
|
||||
root = FLECKS_CORE_ROOT,
|
||||
} = {},
|
||||
) {
|
||||
// Load or use parameterized configuration.
|
||||
let configType;
|
||||
if (!config) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
[configType, config] = this.loadConfig(root);
|
||||
}
|
||||
else {
|
||||
configType = 'parameter';
|
||||
}
|
||||
debug('bootstrap configuration (%s)', configType);
|
||||
debugSilly(config);
|
||||
// Make resolver and load flecksConfig.
|
||||
const {flecksConfig, resolver} = this.makeResolverAndLoadRcs(
|
||||
Object.keys(config),
|
||||
platforms,
|
||||
root,
|
||||
);
|
||||
// Rewrite aliased config keys.
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
config = Object.fromEntries(
|
||||
Object.entries(config)
|
||||
.map(([key, value]) => {
|
||||
const index = key.indexOf(':');
|
||||
return [-1 !== index ? key.slice(0, index) : key, value];
|
||||
}),
|
||||
);
|
||||
this.installCompilers(flecksConfig, resolver);
|
||||
// Instantiate with mixins.
|
||||
return ServerFlecks.from({
|
||||
config,
|
||||
flecks: Object.fromEntries(
|
||||
Object.keys(resolver)
|
||||
.map((path) => [path, R(this.resolve(resolver, path))]),
|
||||
),
|
||||
platforms,
|
||||
flecksConfig,
|
||||
resolver,
|
||||
});
|
||||
}
|
||||
|
||||
buildConfig(path, specific) {
|
||||
const config = this.buildConfigs[path];
|
||||
if (!config) {
|
||||
throw new Error(`Unknown build config '${path}'`);
|
||||
}
|
||||
const paths = [];
|
||||
if (specific) {
|
||||
if ('specifier' in config) {
|
||||
if (false === config.specifier) {
|
||||
paths.shift();
|
||||
}
|
||||
else {
|
||||
paths.push(config.specifier(specific));
|
||||
}
|
||||
}
|
||||
else {
|
||||
paths.push(`${specific}.${path}`);
|
||||
}
|
||||
}
|
||||
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(path) {
|
||||
return path
|
||||
// - `@flecks/core` -> `flecks_core`
|
||||
.replace(/[^a-zA-Z0-9]/g, '_')
|
||||
.replace(/_*(.*)_*/, '$1');
|
||||
}
|
||||
|
||||
exts() {
|
||||
return this.constructor.exts(this.flecksConfig);
|
||||
}
|
||||
|
||||
static exts(flecksConfig) {
|
||||
const keys = Object.keys(flecksConfig);
|
||||
const exts = [];
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
const key = keys[i];
|
||||
const config = flecksConfig[key];
|
||||
if (config.exts) {
|
||||
exts.push(...config.exts);
|
||||
}
|
||||
}
|
||||
return exts;
|
||||
}
|
||||
|
||||
fleckIsAliased(fleck) {
|
||||
return this.constructor.fleckIsAliased(this.resolver, fleck);
|
||||
}
|
||||
|
||||
static fleckIsAliased(resolver, fleck) {
|
||||
return fleck !== this.resolve(resolver, fleck);
|
||||
}
|
||||
|
||||
fleckIsCompiled(fleck) {
|
||||
return this.constructor.fleckIsCompiled(this.resolver, fleck);
|
||||
}
|
||||
|
||||
static fleckIsCompiled(resolver, fleck) {
|
||||
return this.fleckIsAliased(resolver, fleck) || this.fleckIsSymlinked(resolver, fleck);
|
||||
}
|
||||
|
||||
fleckIsSymlinked(fleck) {
|
||||
return this.constructor.fleckIsSymlinked(this.resolver, fleck);
|
||||
}
|
||||
|
||||
static fleckIsSymlinked(resolver, fleck) {
|
||||
const resolved = R.resolve(this.resolve(resolver, fleck));
|
||||
const realpath = realpathSync(resolved);
|
||||
return realpath !== resolved;
|
||||
}
|
||||
|
||||
static installCompilers(flecksConfig, resolver) {
|
||||
const paths = Object.keys(resolver);
|
||||
debugSilly('flecksConfig: %O', flecksConfig);
|
||||
// Merge aliases;
|
||||
const aliases = Object.fromEntries(
|
||||
Object.entries({
|
||||
// from fleck configuration above,
|
||||
...Object.fromEntries(Object.entries(resolver).filter(([from, to]) => from !== to)),
|
||||
// from symlinks,
|
||||
...(
|
||||
Object.fromEntries(
|
||||
paths.filter((path) => this.fleckIsSymlinked(resolver, path))
|
||||
.map((path) => [path, this.sourcepath(R.resolve(this.resolve(resolver, path)))]),
|
||||
)
|
||||
),
|
||||
// and from RCs.
|
||||
...this.aliases(flecksConfig),
|
||||
})
|
||||
.map(([from, to]) => [from, to.endsWith('/index') ? to.slice(0, -6) : to]),
|
||||
);
|
||||
if (Object.keys(aliases).length > 0) {
|
||||
debugSilly('aliases: %O', aliases);
|
||||
}
|
||||
const exts = this.exts(flecksConfig);
|
||||
const enhancedResolver = enhancedResolve.create.sync({
|
||||
extensions: exts,
|
||||
alias: aliases,
|
||||
});
|
||||
// Stub server-unfriendly modules.
|
||||
const stubs = this.stubs(['server'], flecksConfig);
|
||||
if (stubs.length > 0) {
|
||||
debugSilly('stubbing: %O', stubs);
|
||||
}
|
||||
// Do we need to get up in `require()`'s guts?
|
||||
if (
|
||||
Object.keys(aliases).length > 0
|
||||
|| stubs.length > 0
|
||||
) {
|
||||
const {Module} = R('module');
|
||||
const {require: Mr} = Module.prototype;
|
||||
const aliasKeys = Object.keys(aliases);
|
||||
Module.prototype.require = function hackedRequire(request, options) {
|
||||
for (let i = 0; i < stubs.length; ++i) {
|
||||
if (request.match(stubs[i])) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
if (aliasKeys.find((aliasKey) => request.startsWith(aliasKey))) {
|
||||
try {
|
||||
const resolved = enhancedResolver(FLECKS_CORE_ROOT, request);
|
||||
if (resolved) {
|
||||
return Mr.call(this, resolved, options);
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line no-empty
|
||||
catch (error) {}
|
||||
}
|
||||
return Mr.call(this, request, options);
|
||||
};
|
||||
}
|
||||
// Compile.
|
||||
const compilations = [];
|
||||
const needCompilation = paths
|
||||
.filter((path) => this.fleckIsCompiled(resolver, path));
|
||||
if (needCompilation.length > 0) {
|
||||
// Augment the compilations with babel config from flecksrc.
|
||||
const flecksBabelConfig = babelmerge.all(this.babel(flecksConfig).map(([, babel]) => babel));
|
||||
debugSilly('.flecksrc: babel: %O', flecksBabelConfig);
|
||||
// Key flecks needing compilation by their roots, so we can compile all common roots with a
|
||||
// single invocation of `@babel/register`.
|
||||
const compilationRootMap = {};
|
||||
needCompilation.forEach((fleck) => {
|
||||
const root = this.root(resolver, fleck);
|
||||
if (!compilationRootMap[root]) {
|
||||
compilationRootMap[root] = [];
|
||||
}
|
||||
compilationRootMap[root].push(fleck);
|
||||
});
|
||||
// Register a compilation for each root.
|
||||
Object.entries(compilationRootMap).forEach(([root, compiling]) => {
|
||||
const resolved = dirname(R.resolve(join(root, 'package.json')));
|
||||
const sourcepath = this.sourcepath(resolved);
|
||||
const sourceroot = join(sourcepath, '..');
|
||||
// Load babel config from whichever we find first:
|
||||
// - The fleck being compiled's build directory
|
||||
// - The root build directory
|
||||
// - Finally, the built-in babel config
|
||||
let builtInPath;
|
||||
try {
|
||||
builtInPath = this.resolvePath(resolver, '@flecks/core/server');
|
||||
}
|
||||
catch (error) {
|
||||
// This file won't be resolved during testing.
|
||||
builtInPath = join(__dirname, '..', 'src', 'server');
|
||||
}
|
||||
const configFile = this.resolveBuildConfig(
|
||||
resolver,
|
||||
[
|
||||
resolved,
|
||||
FLECKS_CORE_ROOT,
|
||||
builtInPath,
|
||||
],
|
||||
[
|
||||
'babel.config.js',
|
||||
],
|
||||
);
|
||||
const ignore = `${resolve(join(sourceroot, 'node_modules'))}/`;
|
||||
const only = `${resolve(sourceroot)}/`;
|
||||
const config = {
|
||||
// Augment the selected config with the babel config from RCs.
|
||||
configFile,
|
||||
// Target the compiler to avoid unnecessary work.
|
||||
ignore: [ignore],
|
||||
only: [only],
|
||||
};
|
||||
debugSilly('compiling %O with %j at %s', compiling, config, only);
|
||||
compilations.push({
|
||||
ignore,
|
||||
only,
|
||||
compiler: new Compiler(babelmerge(config, flecksBabelConfig)),
|
||||
});
|
||||
});
|
||||
}
|
||||
const findCompiler = (request) => {
|
||||
for (let i = 0; i < compilations.length; ++i) {
|
||||
const {compiler, ignore, only} = compilations[i];
|
||||
if (request.startsWith(only) && !request.startsWith(ignore)) {
|
||||
return compiler;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
debugSilly('pirating exts: %O', exts);
|
||||
addHook(
|
||||
(code, request) => {
|
||||
const compilation = findCompiler(request).compile(code, request);
|
||||
return null === compilation ? code : compilation.code;
|
||||
},
|
||||
{exts, matcher: (request) => !!findCompiler(request)},
|
||||
);
|
||||
}
|
||||
|
||||
loadBuildConfigs() {
|
||||
Object.entries(this.invoke('@flecks/core.build.config'))
|
||||
.forEach(([fleck, configs]) => {
|
||||
configs.forEach((config) => {
|
||||
const defaults = {
|
||||
fleck,
|
||||
};
|
||||
if (Array.isArray(config)) {
|
||||
this.registerBuildConfig(config[0], {...defaults, ...config[1]});
|
||||
}
|
||||
this.registerBuildConfig(config, defaults);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
static loadConfig(root) {
|
||||
const resolvedRoot = resolve(FLECKS_CORE_ROOT, root);
|
||||
try {
|
||||
const {load} = R('js-yaml');
|
||||
const filename = join(resolvedRoot, 'build', FLECKS_YML);
|
||||
const buffer = readFileSync(filename, 'utf8');
|
||||
debugSilly('parsing configuration from YML...');
|
||||
return ['YML', load(buffer, {filename}) || {}];
|
||||
}
|
||||
catch (error) {
|
||||
if ('ENOENT' !== error.code) {
|
||||
throw error;
|
||||
}
|
||||
return ['barebones', {'@flecks/core': {}, '@flecks/fleck': {}}];
|
||||
}
|
||||
}
|
||||
|
||||
static makeResolver(maybeAliasedPath, platforms, root) {
|
||||
const resolvedRoot = resolve(FLECKS_CORE_ROOT, root);
|
||||
const resolver = {};
|
||||
// `!platform` excludes that platform.
|
||||
const without = platforms
|
||||
.filter((platform) => '!'.charCodeAt(0) === platform.charCodeAt(0))
|
||||
.map((platform) => platform.slice(1));
|
||||
// Parse the alias (if any).
|
||||
const index = maybeAliasedPath.indexOf(':');
|
||||
const [path, alias] = -1 === index
|
||||
? [maybeAliasedPath, undefined]
|
||||
: [maybeAliasedPath.slice(0, index), maybeAliasedPath.slice(index + 1)];
|
||||
// Run it by the exception list.
|
||||
if (-1 !== without.indexOf(path.split('/').pop())) {
|
||||
// eslint-disable-next-line no-continue
|
||||
return {};
|
||||
}
|
||||
// Resolve the path (if necessary).
|
||||
let resolvedPath;
|
||||
if (alias) {
|
||||
resolvedPath = isAbsolute(alias) ? alias : join(resolvedRoot, alias);
|
||||
}
|
||||
else {
|
||||
if (path.startsWith('.')) {
|
||||
throw new Error(`non-aliased relative path '${path}' in configuration`);
|
||||
}
|
||||
resolvedPath = path;
|
||||
}
|
||||
try {
|
||||
R.resolve(resolvedPath);
|
||||
resolver[path] = resolvedPath;
|
||||
}
|
||||
// eslint-disable-next-line no-empty
|
||||
catch (error) {}
|
||||
// Discover platform-specific variants.
|
||||
if (platforms) {
|
||||
platforms.forEach((platform) => {
|
||||
try {
|
||||
const resolvedPlatformPath = join(resolvedPath, platform);
|
||||
R.resolve(resolvedPlatformPath);
|
||||
resolver[join(path, platform)] = resolvedPlatformPath;
|
||||
}
|
||||
// eslint-disable-next-line no-empty
|
||||
catch (error) {}
|
||||
});
|
||||
}
|
||||
return resolver;
|
||||
}
|
||||
|
||||
static makeResolverAndLoadRcs(
|
||||
maybeAliasedPaths,
|
||||
platforms = ['server'],
|
||||
root = FLECKS_CORE_ROOT,
|
||||
) {
|
||||
const resolver = {};
|
||||
const rootsFrom = (paths) => (
|
||||
Array.from(new Set(
|
||||
paths
|
||||
.map((path) => this.root(resolver, path))
|
||||
.filter((e) => !!e),
|
||||
))
|
||||
);
|
||||
for (let i = 0; i < maybeAliasedPaths.length; ++i) {
|
||||
const maybeAliasedPath = maybeAliasedPaths[i];
|
||||
Object.entries(this.makeResolver(maybeAliasedPath, platforms, root))
|
||||
.forEach(([path, alias]) => {
|
||||
resolver[path] = alias;
|
||||
});
|
||||
}
|
||||
const flecksConfig = {};
|
||||
const roots = Array.from(new Set(
|
||||
rootsFrom(Object.keys(resolver))
|
||||
.concat(FLECKS_CORE_ROOT),
|
||||
));
|
||||
let rootsToScan = roots;
|
||||
while (rootsToScan.length > 0) {
|
||||
const dependencies = [];
|
||||
for (let i = 0; i < rootsToScan.length; ++i) {
|
||||
const root = rootsToScan[i];
|
||||
try {
|
||||
flecksConfig[root] = R(join(root, 'build', 'flecks.config'));
|
||||
const {dependencies: flecksConfigDependencies = []} = flecksConfig[root];
|
||||
dependencies.push(...flecksConfigDependencies);
|
||||
flecksConfigDependencies.forEach((dependency) => {
|
||||
Object.entries(this.makeResolver(dependency, platforms, root))
|
||||
.forEach(([path, alias]) => {
|
||||
resolver[path] = alias;
|
||||
});
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
if ('MODULE_NOT_FOUND' !== error.code) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
rootsToScan = rootsFrom(dependencies)
|
||||
.filter((root) => !flecksConfig[root]);
|
||||
}
|
||||
return {flecksConfig, resolver};
|
||||
}
|
||||
|
||||
overrideConfigFromEnvironment() {
|
||||
const keys = Object.keys(process.env);
|
||||
const seen = [];
|
||||
Object.keys(this.flecks)
|
||||
.sort((l, r) => (l < r ? 1 : -1))
|
||||
.forEach((fleck) => {
|
||||
const prefix = `FLECKS_ENV__${this.constructor.environmentalize(fleck)}`;
|
||||
keys
|
||||
.filter((key) => key.startsWith(`${prefix}__`) && -1 === seen.indexOf(key))
|
||||
.map((key) => {
|
||||
seen.push(key);
|
||||
debug('reading environment from %s...', key);
|
||||
return [key, process.env[key]];
|
||||
})
|
||||
.map(([key, value]) => [key.slice(prefix.length + 2), value])
|
||||
.map(([subkey, value]) => [subkey.split('_'), value])
|
||||
.forEach(([path, jsonOrString]) => {
|
||||
try {
|
||||
this.set([fleck, ...path], JSON.parse(jsonOrString));
|
||||
debug('read (%s) as JSON', jsonOrString);
|
||||
}
|
||||
catch (error) {
|
||||
this.set([fleck, ...path], jsonOrString);
|
||||
debug('read (%s) as string', jsonOrString);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
flecksConfig() {
|
||||
return this.flecksConfig;
|
||||
}
|
||||
|
||||
registerBuildConfig(filename, config) {
|
||||
const defaults = {
|
||||
root: FLECKS_CORE_ROOT,
|
||||
};
|
||||
this.buildConfigs[filename] = {...defaults, ...config};
|
||||
}
|
||||
|
||||
registerResolver(from, to = from) {
|
||||
this.resolver[from] = to;
|
||||
}
|
||||
|
||||
resolve(path) {
|
||||
return this.constructor.resolve(this.resolver, path);
|
||||
}
|
||||
|
||||
static resolve(resolver, fleck) {
|
||||
return resolver[fleck] || fleck;
|
||||
}
|
||||
|
||||
resolveBuildConfig(roots, paths) {
|
||||
return this.constructor.resolveBuildConfig(this.resolver, roots, paths);
|
||||
}
|
||||
|
||||
static resolveBuildConfig(resolver, roots, paths) {
|
||||
const tried = [];
|
||||
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 {
|
||||
tried.push(resolved);
|
||||
statSync(resolved);
|
||||
return resolved;
|
||||
}
|
||||
// eslint-disable-next-line no-empty
|
||||
catch (error) {}
|
||||
}
|
||||
}
|
||||
throw new Error(`Couldn't resolve build file '${paths.pop()}', tried: ${tried.join(', ')}`);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static root(resolver, fleck) {
|
||||
const parts = this.resolve(resolver, fleck).split('/');
|
||||
try {
|
||||
R.resolve(parts.join('/'));
|
||||
}
|
||||
catch (error) {
|
||||
try {
|
||||
R.resolve(join(parts.join('/'), 'build', 'flecks.config'));
|
||||
}
|
||||
catch (error) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
while (parts.length > 0) {
|
||||
try {
|
||||
R.resolve(join(parts.join('/'), 'package.json'));
|
||||
return parts.join('/');
|
||||
}
|
||||
catch (error) {
|
||||
parts.pop();
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
runtimeCompiler(resolver, runtime, config, {allowlist = []} = {}) {
|
||||
// Compile.
|
||||
const needCompilation = Object.entries(resolver)
|
||||
.filter(([fleck]) => this.constructor.fleckIsCompiled(resolver, fleck));
|
||||
if (needCompilation.length > 0) {
|
||||
const flecksBabelConfig = this.babel();
|
||||
debugSilly('flecks.config.js: babel: %O', flecksBabelConfig);
|
||||
// Alias and de-externalize.
|
||||
needCompilation
|
||||
.sort(([l], [r]) => (l < r ? 1 : -1))
|
||||
.forEach(([fleck, resolved]) => {
|
||||
let alias = this.constructor.fleckIsAliased(resolver, fleck)
|
||||
? resolved
|
||||
: this.constructor.sourcepath(R.resolve(this.constructor.resolve(resolver, fleck)));
|
||||
alias = alias.endsWith('/index') ? alias.slice(0, -6) : alias;
|
||||
allowlist.push(fleck);
|
||||
config.resolve.alias[fleck] = alias;
|
||||
debugSilly('%s runtime de-externalized %s, alias: %s', runtime, fleck, alias);
|
||||
});
|
||||
// Set up compilation at each root.
|
||||
const compiledPaths = [];
|
||||
Array.from(new Set(
|
||||
needCompilation
|
||||
.map(([fleck]) => fleck)
|
||||
.map((fleck) => this.constructor.root(resolver, fleck)),
|
||||
))
|
||||
.forEach((root) => {
|
||||
const resolved = dirname(R.resolve(join(root, 'package.json')));
|
||||
const sourcepath = this.constructor.sourcepath(resolved);
|
||||
const sourceroot = join(sourcepath, '..');
|
||||
compiledPaths.push(sourceroot);
|
||||
// @todo Ideally the fleck's 3rd party modules would be externalized.
|
||||
// Alias this compiled fleck's `node_modules` to the root `node_modules`.
|
||||
config.resolve.alias[join(sourceroot, 'node_modules')] = join(FLECKS_CORE_ROOT, 'node_modules');
|
||||
const configFile = this.buildConfig('babel.config.js');
|
||||
debugSilly('compiling: %s with %s', root, configFile);
|
||||
const babel = {
|
||||
configFile,
|
||||
// Augment the compiler with babel config from `flecks.config.js`.
|
||||
...babelmerge.all(flecksBabelConfig.map(([, babel]) => babel)),
|
||||
};
|
||||
config.module.rules.push(
|
||||
{
|
||||
test: /\.(m?jsx?)?$/,
|
||||
include: [sourceroot],
|
||||
use: [
|
||||
{
|
||||
loader: R.resolve('babel-loader'),
|
||||
options: {
|
||||
cacheDirectory: true,
|
||||
babelrc: false,
|
||||
configFile: false,
|
||||
...babel,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
});
|
||||
const compiledPathsRegex = new RegExp(
|
||||
`(?:${compiledPaths.map((path) => path.replace(/[\\/]/g, '[\\/]')).join('|')})`,
|
||||
);
|
||||
if (!config.optimization) {
|
||||
config.optimization = {};
|
||||
}
|
||||
if (!config.optimization.splitChunks) {
|
||||
config.optimization.splitChunks = {};
|
||||
}
|
||||
if (!config.optimization.splitChunks.cacheGroups) {
|
||||
config.optimization.splitChunks.cacheGroups = {};
|
||||
}
|
||||
config.optimization.splitChunks.cacheGroups.flecksCompiled = {
|
||||
chunks: 'all',
|
||||
enforce: true,
|
||||
priority: 100,
|
||||
test: compiledPathsRegex,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
sourcepath(fleck) {
|
||||
return this.constructor.sourcepath(fleck);
|
||||
}
|
||||
|
||||
static sourcepath(path) {
|
||||
let sourcepath = realpathSync(path);
|
||||
const parts = sourcepath.split('/');
|
||||
const indexOf = parts.lastIndexOf('dist');
|
||||
if (-1 !== indexOf) {
|
||||
parts.splice(indexOf, 1, 'src');
|
||||
sourcepath = parts.join('/');
|
||||
sourcepath = join(dirname(sourcepath), basename(sourcepath, extname(sourcepath)));
|
||||
}
|
||||
else {
|
||||
sourcepath = join(sourcepath, 'src');
|
||||
}
|
||||
return sourcepath;
|
||||
}
|
||||
|
||||
stubs() {
|
||||
return this.constructor.stubs(this.platforms, this.flecksConfig);
|
||||
}
|
||||
|
||||
static stubs(platforms, flecksConfig) {
|
||||
const keys = Object.keys(flecksConfig);
|
||||
const stubs = [];
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
const key = keys[i];
|
||||
const config = flecksConfig[key];
|
||||
if (config.stubs) {
|
||||
Object.entries(config.stubs)
|
||||
.forEach(([platform, patterns]) => {
|
||||
if (-1 !== platforms.indexOf(platform)) {
|
||||
patterns.forEach((pattern) => {
|
||||
stubs.push(pattern);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return stubs;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,14 +1,7 @@
|
|||
import {join} from 'path';
|
||||
import {inspect} from 'util';
|
||||
|
||||
import webpack from 'webpack';
|
||||
|
||||
import commands from './commands';
|
||||
|
||||
const {
|
||||
FLECKS_CORE_ROOT = process.cwd(),
|
||||
} = process.env;
|
||||
|
||||
const {defaultOptions} = inspect;
|
||||
defaultOptions.breakLength = 160;
|
||||
defaultOptions.compact = 6;
|
||||
|
@ -17,66 +10,18 @@ defaultOptions.sorted = true;
|
|||
export {glob} from 'glob';
|
||||
export {dump as dumpYml, load as loadYml} from 'js-yaml';
|
||||
|
||||
export {
|
||||
Argument,
|
||||
default as commands,
|
||||
Option,
|
||||
processCode,
|
||||
program,
|
||||
spawnWith,
|
||||
} from './commands';
|
||||
export {default as Flecks} from './flecks';
|
||||
export {default as require} from '../require';
|
||||
export {JsonStream, transform} from './stream';
|
||||
export * from './webpack';
|
||||
export {commands, processCode, spawnWith} from '../../build/commands';
|
||||
export {JsonStream, transform} from '../../build/stream';
|
||||
export * from '../../build/webpack';
|
||||
|
||||
export {webpack};
|
||||
|
||||
export const hooks = {
|
||||
'@flecks/core.build': async (target, config, env, argv, flecks) => {
|
||||
const {
|
||||
profile,
|
||||
} = flecks.get('@flecks/core/server');
|
||||
if (profile.includes(target)) {
|
||||
config.plugins.push(
|
||||
new webpack.debug.ProfilingPlugin({
|
||||
outputPath: join(FLECKS_CORE_ROOT, `profile.build-${target}.json`),
|
||||
}),
|
||||
);
|
||||
}
|
||||
},
|
||||
'@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', {specifier: false}],
|
||||
/**
|
||||
* ESLint configuration. See: https://eslint.org/docs/user-guide/configuring/
|
||||
*/
|
||||
['eslint.config.js', {specifier: false}],
|
||||
/**
|
||||
* Flecks webpack configuration. See: https://webpack.js.org/configuration/
|
||||
*/
|
||||
['fleckspack.config.js', {specifier: false}],
|
||||
/**
|
||||
* Webpack configuration. See: https://webpack.js.org/configuration/
|
||||
*/
|
||||
'webpack.config.js',
|
||||
],
|
||||
'@flecks/core.commands': commands,
|
||||
'@flecks/core.config': () => ({
|
||||
/**
|
||||
* The package manager used for tasks.
|
||||
*/
|
||||
packageManager: 'npm',
|
||||
/**
|
||||
* Build targets to profile with `webpack.debug.ProfilingPlugin`.
|
||||
*/
|
||||
profile: [],
|
||||
'@flecks/web.config': async (req, flecks) => ({
|
||||
'@flecks/core': {
|
||||
id: flecks.get('@flecks/core.id'),
|
||||
packageManager: undefined,
|
||||
profile: undefined,
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
'@flecks/core:../src': {}
|
||||
'@flecks/core/one:./one': {}
|
||||
'@flecks/core/one/server:./one/server': {}
|
||||
'@flecks/core/two:./two': {}
|
|
@ -2,8 +2,8 @@ import {expect} from 'chai';
|
|||
|
||||
import {Flecks, ById, ByType} from '@flecks/core';
|
||||
|
||||
const testOne = require('./one');
|
||||
const testTwo = require('./two');
|
||||
const testOne = require('./packages/one');
|
||||
const testTwo = require('./packages/two');
|
||||
|
||||
it('can gather', () => {
|
||||
const flecks = Flecks.from({
|
||||
|
|
|
@ -2,7 +2,7 @@ import {expect} from 'chai';
|
|||
|
||||
import {Flecks} from '@flecks/core';
|
||||
|
||||
const testOne = require('./one');
|
||||
const testOne = require('./packages/one');
|
||||
|
||||
it('can create an empty instance', () => {
|
||||
const flecks = new Flecks();
|
||||
|
|
|
@ -7,8 +7,8 @@ chai.use(chaiAsPromised);
|
|||
|
||||
const {expect} = chai;
|
||||
|
||||
const testOne = require('./one');
|
||||
const testTwo = require('./two');
|
||||
const testOne = require('./packages/one');
|
||||
const testTwo = require('./packages/two');
|
||||
|
||||
let flecks;
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@ import {expect} from 'chai';
|
|||
|
||||
import {Flecks} from '@flecks/core';
|
||||
|
||||
const testOne = require('./one');
|
||||
const testTwo = require('./two');
|
||||
const testThree = require('./three');
|
||||
const testOne = require('./packages/one');
|
||||
const testTwo = require('./packages/two');
|
||||
const testThree = require('./packages/three');
|
||||
|
||||
it('can make middleware', (done) => {
|
||||
const flecks = Flecks.from({
|
||||
|
|
51
packages/core/test/platforms/server/bootstrap.js
vendored
51
packages/core/test/platforms/server/bootstrap.js
vendored
|
@ -1,51 +0,0 @@
|
|||
import {expect} from 'chai';
|
||||
|
||||
// eslint-disable-next-line import/no-unresolved, import/no-extraneous-dependencies
|
||||
import {Flecks} from '../../../src/server';
|
||||
|
||||
it('bootstraps FLECKS_CORE_ROOT by default', () => {
|
||||
const flecks = Flecks.bootstrap();
|
||||
expect(flecks.fleck('@flecks/core')).to.not.equal(undefined);
|
||||
});
|
||||
|
||||
it('bootstraps server platform by default', () => {
|
||||
const flecks = Flecks.bootstrap();
|
||||
expect(flecks.fleck('@flecks/core/server')).to.not.equal(undefined);
|
||||
});
|
||||
|
||||
it('can bootstrap from a foreign root', () => {
|
||||
const flecks = Flecks.bootstrap({
|
||||
root: './test',
|
||||
});
|
||||
expect(flecks.fleck('@flecks/core/one')).to.not.equal(undefined);
|
||||
expect(flecks.fleck('@flecks/core/two')).to.not.equal(undefined);
|
||||
});
|
||||
|
||||
it('can bootstrap other platforms', () => {
|
||||
const flecks = Flecks.bootstrap({
|
||||
platforms: ['client'],
|
||||
root: './test',
|
||||
});
|
||||
expect(flecks.fleck('@flecks/core/one')).to.not.equal(undefined);
|
||||
expect(flecks.fleck('@flecks/core/one/client')).to.not.equal(undefined);
|
||||
expect(flecks.fleck('@flecks/core/one/server')).to.not.equal(undefined);
|
||||
});
|
||||
|
||||
it('can exclude platforms', () => {
|
||||
const flecks = Flecks.bootstrap({
|
||||
platforms: ['client', '!server'],
|
||||
root: './test',
|
||||
});
|
||||
expect(flecks.fleck('@flecks/core/one')).to.not.equal(undefined);
|
||||
expect(flecks.fleck('@flecks/core/one/client')).to.not.equal(undefined);
|
||||
expect(flecks.fleck('@flecks/core/one/server')).to.equal(undefined);
|
||||
});
|
||||
|
||||
it('provides webpack goodies in nodespace', () => {
|
||||
const flecks = Flecks.bootstrap({
|
||||
root: './test',
|
||||
});
|
||||
flecks.fleck('@flecks/core/one').testNodespace().forEach((result) => {
|
||||
expect(result).to.not.equal('undefined');
|
||||
});
|
||||
});
|
80
packages/core/test/server/explicate.js
Normal file
80
packages/core/test/server/explicate.js
Normal file
|
@ -0,0 +1,80 @@
|
|||
import {join} from 'path';
|
||||
|
||||
import {expect} from 'chai';
|
||||
|
||||
import explicate from '@flecks/core/build/explicate';
|
||||
import Resolver from '@flecks/core/build/resolver';
|
||||
|
||||
const {
|
||||
FLECKS_CORE_ROOT = process.cwd(),
|
||||
} = process.env;
|
||||
|
||||
const root = join(FLECKS_CORE_ROOT, 'test', 'server', 'explicate');
|
||||
|
||||
function createExplication(paths, platforms) {
|
||||
const resolver = new Resolver({modules: [join(root, 'fake_node_modules')]});
|
||||
return explicate(
|
||||
paths,
|
||||
{
|
||||
platforms,
|
||||
resolver,
|
||||
root,
|
||||
importer: (request) => __non_webpack_require__(request),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
describe('explication', () => {
|
||||
|
||||
it('derives platforms', async () => {
|
||||
expect(Object.keys((await createExplication(['platformed'])).descriptors))
|
||||
.to.deep.equal([
|
||||
'platformed', 'platformed/server',
|
||||
]);
|
||||
expect(Object.keys((await createExplication(['server-only'])).descriptors))
|
||||
.to.deep.equal([
|
||||
'server-only/server',
|
||||
]);
|
||||
});
|
||||
|
||||
it('derives through bootstrap', async () => {
|
||||
expect(Object.keys((await createExplication(['real-root'])).descriptors))
|
||||
.to.deep.equal([
|
||||
'dependency', 'dependency/server',
|
||||
'real-root', 'real-root/server',
|
||||
]);
|
||||
});
|
||||
|
||||
it('excludes platforms', async () => {
|
||||
expect(Object.keys(
|
||||
(await createExplication(
|
||||
['platformed/client', 'dependency'],
|
||||
['server', '!client'],
|
||||
)).descriptors,
|
||||
))
|
||||
.to.deep.equal([
|
||||
'dependency', 'dependency/server',
|
||||
]);
|
||||
});
|
||||
|
||||
it('explicates parents first', async () => {
|
||||
expect(Object.keys((await createExplication(['real-root/server'])).descriptors))
|
||||
.to.deep.equal([
|
||||
'dependency', 'dependency/server',
|
||||
'real-root', 'real-root/server',
|
||||
]);
|
||||
});
|
||||
|
||||
it('explicates only bootstrapped', async () => {
|
||||
expect(Object.keys((await createExplication(['only-bootstrapped'])).descriptors))
|
||||
.to.deep.equal([
|
||||
'only-bootstrapped',
|
||||
]);
|
||||
});
|
||||
|
||||
it('skips nonexistent', async () => {
|
||||
expect(await createExplication(['real-root/nonexistent']))
|
||||
.to.deep.equal({descriptors: {}, roots: {}});
|
||||
});
|
||||
|
||||
});
|
0
packages/core/test/server/explicate/fake_node_modules/dependency/index.js
generated
Normal file
0
packages/core/test/server/explicate/fake_node_modules/dependency/index.js
generated
Normal file
0
packages/core/test/server/explicate/fake_node_modules/dependency/server/index.js
generated
Normal file
0
packages/core/test/server/explicate/fake_node_modules/dependency/server/index.js
generated
Normal file
1
packages/core/test/server/explicate/fake_node_modules/only-bootstrapped/package.json
generated
Normal file
1
packages/core/test/server/explicate/fake_node_modules/only-bootstrapped/package.json
generated
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
0
packages/core/test/server/explicate/fake_node_modules/platformed/client/index.js
generated
Normal file
0
packages/core/test/server/explicate/fake_node_modules/platformed/client/index.js
generated
Normal file
0
packages/core/test/server/explicate/fake_node_modules/platformed/index.js
generated
Normal file
0
packages/core/test/server/explicate/fake_node_modules/platformed/index.js
generated
Normal file
0
packages/core/test/server/explicate/fake_node_modules/platformed/server/index.js
generated
Normal file
0
packages/core/test/server/explicate/fake_node_modules/platformed/server/index.js
generated
Normal file
1
packages/core/test/server/explicate/fake_node_modules/real-root/build/flecks.bootstrap.js
generated
Normal file
1
packages/core/test/server/explicate/fake_node_modules/real-root/build/flecks.bootstrap.js
generated
Normal file
|
@ -0,0 +1 @@
|
|||
exports.dependencies = ['dependency'];
|
0
packages/core/test/server/explicate/fake_node_modules/real-root/index.js
generated
Normal file
0
packages/core/test/server/explicate/fake_node_modules/real-root/index.js
generated
Normal file
1
packages/core/test/server/explicate/fake_node_modules/real-root/package.json
generated
Normal file
1
packages/core/test/server/explicate/fake_node_modules/real-root/package.json
generated
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
0
packages/core/test/server/explicate/fake_node_modules/real-root/server/index.js
generated
Normal file
0
packages/core/test/server/explicate/fake_node_modules/real-root/server/index.js
generated
Normal file
0
packages/core/test/server/explicate/fake_node_modules/server-only/server.js
generated
Normal file
0
packages/core/test/server/explicate/fake_node_modules/server-only/server.js
generated
Normal file
24
packages/core/test/server/resolve.js
Normal file
24
packages/core/test/server/resolve.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
import {join} from 'path';
|
||||
|
||||
import {expect} from 'chai';
|
||||
|
||||
import Resolver from '@flecks/core/build/resolver';
|
||||
|
||||
const {
|
||||
FLECKS_CORE_ROOT = process.cwd(),
|
||||
} = process.env;
|
||||
|
||||
it('can resolve', async () => {
|
||||
const resolver = new Resolver();
|
||||
expect(await resolver.resolve('./test/server/resolve'))
|
||||
.to.equal(join(FLECKS_CORE_ROOT, 'test', 'server', 'resolve.js'));
|
||||
});
|
||||
|
||||
it('can create aliases at runtime', async () => {
|
||||
const resolver = new Resolver();
|
||||
expect(await resolver.resolve('./test/server/foobar'))
|
||||
.to.be.undefined;
|
||||
resolver.addAlias('./test/server/foobar', './test/server/resolve');
|
||||
expect(await resolver.resolve('./test/server/foobar'))
|
||||
.to.not.be.undefined;
|
||||
});
|
|
@ -1,3 +1,5 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
import {join} from 'path';
|
||||
|
||||
import {
|
||||
|
@ -19,7 +21,7 @@ const {
|
|||
(async () => {
|
||||
program.argument('<app>', 'name of the app to create');
|
||||
program.addOption(
|
||||
new Option('--package-manager <binary>', 'package manager binary')
|
||||
new Option('-pm,--package-manager <binary>', 'package manager binary')
|
||||
.choices(['npm', 'bun', 'yarn'])
|
||||
.default('npm'),
|
||||
);
|
|
@ -1,6 +1,6 @@
|
|||
const {copy, executable} = require('@flecks/core/server');
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
const configFn = require('@flecks/fleck/server/build/fleck.webpack.config');
|
||||
const configFn = require('@flecks/fleck/build/fleck.webpack.config');
|
||||
|
||||
module.exports = async (env, argv, flecks) => {
|
||||
const config = await configFn(env, argv, flecks);
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
import {
|
||||
stat,
|
||||
} from 'fs/promises';
|
||||
import {stat} from 'fs/promises';
|
||||
import {basename, dirname, join} from 'path';
|
||||
|
||||
import {
|
||||
JsonStream,
|
||||
transform,
|
||||
} from '@flecks/core/server';
|
||||
import {JsonStream, transform} from '@flecks/core/server';
|
||||
|
||||
import FileTree from './tree';
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"version": "2.0.3",
|
||||
"version": "3.0.0",
|
||||
"bin": {
|
||||
"create-app": "./cli.js"
|
||||
},
|
||||
|
@ -22,15 +22,14 @@
|
|||
"files": [
|
||||
"cli.js",
|
||||
"server.js",
|
||||
"src",
|
||||
"template"
|
||||
],
|
||||
"dependencies": {
|
||||
"@flecks/core": "^2.0.3",
|
||||
"@flecks/core": "^3.0.0",
|
||||
"minimatch": "^5.0.1",
|
||||
"validate-npm-package-name": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@flecks/fleck": "^2.0.3"
|
||||
"@flecks/fleck": "^3.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1 @@
|
|||
export {default as validate} from 'validate-npm-package-name';
|
||||
|
||||
export {default as build} from './build';
|
||||
export {default as move, testDestination} from './move';
|
||||
export {default as FileTree} from './tree';
|
||||
|
|
|
@ -9,11 +9,11 @@
|
|||
"start": "DEBUG=@flecks/*,-*:silly npm run dev"
|
||||
},
|
||||
"dependencies": {
|
||||
"@flecks/core": "^2.0.0",
|
||||
"@flecks/server": "^2.0.0"
|
||||
"@flecks/core": "3.0.0",
|
||||
"@flecks/server": "3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@flecks/create-fleck": "^2.0.0",
|
||||
"@flecks/create-fleck": "3.0.0",
|
||||
"lerna": "^3.22.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import {stat} from 'fs/promises';
|
||||
import {join} from 'path';
|
||||
#!/usr/bin/env node
|
||||
|
||||
import {
|
||||
build,
|
||||
move,
|
||||
testDestination,
|
||||
validate,
|
||||
} from '@flecks/create-app/server';
|
||||
import {Flecks, program} from '@flecks/core/server';
|
||||
const {stat} = require('fs/promises');
|
||||
const {join} = require('path');
|
||||
|
||||
const addFleckToYml = require('@flecks/core/build/add-fleck-to-yml');
|
||||
const {Server, program} = require('@flecks/core/server');
|
||||
const build = require('@flecks/create-app/build/build');
|
||||
const move = require('@flecks/create-app/build/move');
|
||||
const testDestination = require('@flecks/create-app/build/testDestination');
|
||||
const {validate} = require('@flecks/create-app/server');
|
||||
|
||||
const {
|
||||
FLECKS_CORE_ROOT = process.cwd(),
|
||||
|
@ -65,8 +66,8 @@ const target = async (fleck) => {
|
|||
program.action(async (fleck, o) => {
|
||||
const {alias, add} = o;
|
||||
try {
|
||||
const flecks = await Flecks.bootstrap();
|
||||
const {packageManager} = flecks.get('@flecks/core/server');
|
||||
const flecks = await Server.from();
|
||||
const {packageManager} = flecks.get('@flecks/core');
|
||||
const isMonorepo = await checkIsMonorepo();
|
||||
const [scope, pkg] = await target(fleck);
|
||||
const name = [scope, pkg].filter((e) => !!e).join('/');
|
||||
|
@ -86,7 +87,7 @@ const target = async (fleck) => {
|
|||
await fileTree.writeTo(destination);
|
||||
await build(packageManager, destination);
|
||||
if (isMonorepo && add) {
|
||||
await Flecks.addFleckToYml(...[name].concat(alias ? pkg : []));
|
||||
await addFleckToYml(...[name].concat(alias ? pkg : []));
|
||||
}
|
||||
}
|
||||
catch (error) {
|
|
@ -1,6 +1,6 @@
|
|||
const {copy, executable} = require('@flecks/core/server');
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
const configFn = require('@flecks/fleck/server/build/fleck.webpack.config');
|
||||
const configFn = require('@flecks/fleck/build/fleck.webpack.config');
|
||||
|
||||
module.exports = async (env, argv, flecks) => {
|
||||
const config = await configFn(env, argv, flecks);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"version": "2.0.3",
|
||||
"version": "3.0.0",
|
||||
"bin": {
|
||||
"create-fleck": "./cli.js"
|
||||
},
|
||||
|
@ -20,15 +20,13 @@
|
|||
"test": "flecks test"
|
||||
},
|
||||
"files": [
|
||||
"cli.js",
|
||||
"src",
|
||||
"template"
|
||||
],
|
||||
"dependencies": {
|
||||
"@flecks/core": "^2.0.3",
|
||||
"@flecks/create-app": "^2.0.3"
|
||||
"@flecks/core": "^3.0.0",
|
||||
"@flecks/create-app": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@flecks/fleck": "^2.0.3"
|
||||
"@flecks/fleck": "^3.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
"index.js"
|
||||
],
|
||||
"dependencies": {
|
||||
"@flecks/core": "^2.0.0"
|
||||
"@flecks/core": "3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@flecks/fleck": "^2.0.0"
|
||||
"@flecks/fleck": "3.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"version": "2.0.3",
|
||||
"version": "3.0.0",
|
||||
"scripts": {
|
||||
"build": "flecks build",
|
||||
"clean": "flecks clean",
|
||||
|
@ -20,11 +20,11 @@
|
|||
"server.js"
|
||||
],
|
||||
"dependencies": {
|
||||
"@flecks/core": "^2.0.3",
|
||||
"@flecks/core": "^3.0.0",
|
||||
"sequelize": "^6.3.5",
|
||||
"sqlite3": "^5.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@flecks/fleck": "^2.0.3"
|
||||
"@flecks/fleck": "^3.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"version": "2.0.3",
|
||||
"version": "3.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "flecks build",
|
||||
|
@ -21,10 +21,10 @@
|
|||
"server.js"
|
||||
],
|
||||
"dependencies": {
|
||||
"@flecks/core": "^2.0.3",
|
||||
"@flecks/core": "^3.0.0",
|
||||
"debug": "^4.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@flecks/fleck": "^2.0.3"
|
||||
"@flecks/fleck": "^3.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,25 @@
|
|||
import {
|
||||
const {
|
||||
access,
|
||||
cp,
|
||||
mkdir,
|
||||
rename,
|
||||
rmdir,
|
||||
} from 'fs/promises';
|
||||
import {dirname, join} from 'path';
|
||||
} = require('fs/promises');
|
||||
const {dirname, join} = require('path');
|
||||
|
||||
import {
|
||||
const {Argument} = require('@flecks/core/build/commands');
|
||||
|
||||
const {
|
||||
generate,
|
||||
resolveSiteDir,
|
||||
spawn,
|
||||
} from './docusaurus';
|
||||
} = require('./docusaurus');
|
||||
|
||||
const {
|
||||
FLECKS_CORE_ROOT = process.cwd(),
|
||||
} = process.env;
|
||||
|
||||
export default (program, flecks) => {
|
||||
const {Argument} = flecks.fleck('@flecks/core/server');
|
||||
module.exports = (program, flecks) => {
|
||||
const commands = {};
|
||||
const siteDirArgument = new Argument('[siteDir]', 'Docusaurus directory');
|
||||
siteDirArgument.defaultValue = 'website';
|
|
@ -1,23 +1,23 @@
|
|||
import {mkdir, writeFile} from 'fs/promises';
|
||||
import {isAbsolute, join, resolve} from 'path';
|
||||
const {mkdir, writeFile} = require('fs/promises');
|
||||
const {isAbsolute, join, resolve} = require('path');
|
||||
|
||||
import {spawnWith} from '@flecks/core/server';
|
||||
import {themes as prismThemes} from 'prism-react-renderer';
|
||||
import {rimraf} from 'rimraf';
|
||||
const {spawnWith} = require('@flecks/core/server');
|
||||
const {themes: prismThemes} = require('prism-react-renderer');
|
||||
const {rimraf} = require('rimraf');
|
||||
|
||||
import {
|
||||
const {
|
||||
generateBuildConfigsPage,
|
||||
generateConfigPage,
|
||||
generateHookPage,
|
||||
generateTodoPage,
|
||||
} from './generate';
|
||||
import {parseFlecks} from './parser';
|
||||
} = require('./generate');
|
||||
const {parseFlecks} = require('./parser');
|
||||
|
||||
const {
|
||||
FLECKS_CORE_ROOT = process.cwd(),
|
||||
} = process.env;
|
||||
|
||||
export function configDefaults() {
|
||||
exports.configDefaults = function configDefaults() {
|
||||
/** @type {import('@docusaurus/types').Config} */
|
||||
const config = {
|
||||
tagline: 'built with flecks',
|
||||
|
@ -49,15 +49,15 @@ export function configDefaults() {
|
|||
},
|
||||
};
|
||||
return config;
|
||||
}
|
||||
};
|
||||
|
||||
export function resolveSiteDir(siteDir) {
|
||||
exports.resolveSiteDir = function resolveSiteDir(siteDir) {
|
||||
return isAbsolute(siteDir)
|
||||
? siteDir
|
||||
: resolve(FLECKS_CORE_ROOT, siteDir);
|
||||
}
|
||||
};
|
||||
|
||||
export async function generate(flecks, siteDir) {
|
||||
exports.generate = async function generate(flecks, siteDir) {
|
||||
// Generate "docs".
|
||||
const docsDirectory = join(siteDir, 'docs', 'flecks');
|
||||
await rimraf(docsDirectory);
|
||||
|
@ -72,9 +72,9 @@ export async function generate(flecks, siteDir) {
|
|||
await writeFile(join(generatedDirectory, 'TODO.md'), todoPage);
|
||||
await writeFile(join(generatedDirectory, 'build-configs.md'), buildConfigsPage);
|
||||
await writeFile(join(generatedDirectory, 'config.mdx'), configPage);
|
||||
}
|
||||
};
|
||||
|
||||
export function spawn(subcommand, siteDir) {
|
||||
exports.spawn = function spawn(subcommand, siteDir) {
|
||||
const args = [];
|
||||
switch (subcommand) {
|
||||
case 'start':
|
||||
|
@ -112,4 +112,4 @@ export function spawn(subcommand, siteDir) {
|
|||
child.kill();
|
||||
});
|
||||
return child;
|
||||
}
|
||||
};
|
|
@ -1,8 +1,6 @@
|
|||
import commands from './commands';
|
||||
const commands = require('./commands');
|
||||
|
||||
export {configDefaults} from './docusaurus';
|
||||
|
||||
export const hooks = {
|
||||
exports.hooks = {
|
||||
'@flecks/core.commands': commands,
|
||||
'@flecks/core.config': () => ({
|
||||
/**
|
|
@ -1,2 +0,0 @@
|
|||
'@flecks/core': {}
|
||||
'@flecks/fleck': {}
|
|
@ -6,7 +6,7 @@ const makeFilenameRewriter = (filenameRewriters) => (filename, line, column) =>
|
|||
)
|
||||
);
|
||||
|
||||
export const generateBuildConfigsPage = (buildConfigs) => {
|
||||
exports.generateBuildConfigsPage = (buildConfigs) => {
|
||||
const source = [];
|
||||
source.push('# Build configuration');
|
||||
source.push('');
|
||||
|
@ -25,9 +25,9 @@ export const generateBuildConfigsPage = (buildConfigs) => {
|
|||
return source.join('\n');
|
||||
};
|
||||
|
||||
export const generateConfigPage = (configs) => {
|
||||
exports.generateConfigPage = (configs) => {
|
||||
const source = [];
|
||||
source.push("import CodeBlock from '@theme/CodeBlock';");
|
||||
source.push("const CodeBlock = require('@theme/CodeBlock');");
|
||||
source.push('');
|
||||
source.push('# Fleck configuration');
|
||||
source.push('');
|
||||
|
@ -61,8 +61,8 @@ export const generateConfigPage = (configs) => {
|
|||
return source.join('\n');
|
||||
};
|
||||
|
||||
export const generateHookPage = (hooks, flecks) => {
|
||||
const {filenameRewriters} = flecks.get('@flecks/dox/server');
|
||||
exports.generateHookPage = (hooks, flecks) => {
|
||||
const {filenameRewriters} = flecks.get('@flecks/dox');
|
||||
const rewriteFilename = makeFilenameRewriter(filenameRewriters);
|
||||
const source = [];
|
||||
source.push('# Hooks');
|
||||
|
@ -126,8 +126,8 @@ export const generateHookPage = (hooks, flecks) => {
|
|||
return source.join('\n');
|
||||
};
|
||||
|
||||
export const generateTodoPage = (todos, flecks) => {
|
||||
const {filenameRewriters} = flecks.get('@flecks/dox/server');
|
||||
exports.generateTodoPage = (todos, flecks) => {
|
||||
const {filenameRewriters} = flecks.get('@flecks/dox');
|
||||
const rewriteFilename = makeFilenameRewriter(filenameRewriters);
|
||||
const source = [];
|
||||
source.push('# TODO list');
|
|
@ -1,14 +1,14 @@
|
|||
import {readFile} from 'fs/promises';
|
||||
import {
|
||||
const {readFile} = require('fs/promises');
|
||||
const {
|
||||
basename,
|
||||
dirname,
|
||||
extname,
|
||||
join,
|
||||
} from 'path';
|
||||
} = require('path');
|
||||
|
||||
import {transformAsync} from '@babel/core';
|
||||
import traverse from '@babel/traverse';
|
||||
import {
|
||||
const {transformAsync} = require('@babel/core');
|
||||
const traverse = require('@babel/traverse');
|
||||
const {
|
||||
isArrayExpression,
|
||||
isArrowFunctionExpression,
|
||||
isIdentifier,
|
||||
|
@ -18,9 +18,9 @@ import {
|
|||
isStringLiteral,
|
||||
isThisExpression,
|
||||
isVariableDeclaration,
|
||||
} from '@babel/types';
|
||||
import {glob, require as R} from '@flecks/core/server';
|
||||
import {parse as parseComment} from 'comment-parser';
|
||||
} = require('@babel/types');
|
||||
const {glob} = require('@flecks/core/server');
|
||||
const {parse: parseComment} = require('comment-parser');
|
||||
|
||||
class ParserState {
|
||||
|
||||
|
@ -272,7 +272,7 @@ const FlecksTodos = (state, filename) => ({
|
|||
},
|
||||
});
|
||||
|
||||
export const parseCode = async (code) => {
|
||||
exports.parseCode = async (code) => {
|
||||
const config = {
|
||||
ast: true,
|
||||
code: false,
|
||||
|
@ -281,10 +281,10 @@ export const parseCode = async (code) => {
|
|||
return ast;
|
||||
};
|
||||
|
||||
export const parseFile = async (filename, resolved, state) => {
|
||||
exports.parseFile = async (filename, resolved, state) => {
|
||||
const buffer = await readFile(filename);
|
||||
const source = buffer.toString('utf8');
|
||||
const ast = await parseCode(source);
|
||||
const ast = await exports.parseCode(source);
|
||||
traverse(ast, FlecksBuildConfigs(state, resolved));
|
||||
traverse(ast, FlecksConfigs(state, resolved, source));
|
||||
traverse(ast, FlecksInvocations(state, resolved));
|
||||
|
@ -294,19 +294,19 @@ export const parseFile = async (filename, resolved, state) => {
|
|||
|
||||
const fleckSources = async (path) => glob(join(path, 'src', '**', '*.js'));
|
||||
|
||||
export const parseFleckRoot = async (root, state) => {
|
||||
const resolved = dirname(R.resolve(join(root, 'package.json')));
|
||||
exports.parseFleckRoot = async (root, state) => {
|
||||
const resolved = dirname(require.resolve(join(root, 'package.json')));
|
||||
const sources = await fleckSources(resolved);
|
||||
await Promise.all(
|
||||
sources.map(async (source) => {
|
||||
// @todo Aliased fleck paths are gonna be bad.
|
||||
await parseFile(source, join(root, source.slice(resolved.length)), state);
|
||||
await exports.parseFile(source, join(root, source.slice(resolved.length)), state);
|
||||
}),
|
||||
);
|
||||
try {
|
||||
const buffer = await readFile(join(resolved, 'build', 'dox', 'hooks.js'));
|
||||
const source = buffer.toString('utf8');
|
||||
const ast = await parseCode(source);
|
||||
const ast = await exports.parseCode(source);
|
||||
traverse(ast, FlecksSpecifications(state, source));
|
||||
}
|
||||
catch (error) {
|
||||
|
@ -316,7 +316,7 @@ export const parseFleckRoot = async (root, state) => {
|
|||
}
|
||||
};
|
||||
|
||||
export const parseFlecks = async (flecks) => {
|
||||
exports.parseFlecks = async (flecks) => {
|
||||
const state = new ParserState();
|
||||
const paths = Object.keys(flecks.resolver);
|
||||
const roots = Array.from(new Set(
|
||||
|
@ -327,7 +327,7 @@ export const parseFlecks = async (flecks) => {
|
|||
await Promise.all(
|
||||
roots
|
||||
.map(async (root) => {
|
||||
await parseFleckRoot(root, state);
|
||||
await exports.parseFleckRoot(root, state);
|
||||
}),
|
||||
);
|
||||
return state;
|
|
@ -8,7 +8,7 @@
|
|||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"version": "2.0.3",
|
||||
"version": "3.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "flecks build",
|
||||
|
@ -18,7 +18,6 @@
|
|||
"test": "flecks test"
|
||||
},
|
||||
"files": [
|
||||
"server.js",
|
||||
"website"
|
||||
],
|
||||
"dependencies": {
|
||||
|
@ -29,7 +28,7 @@
|
|||
"@docusaurus/module-type-aliases": "3.0.1",
|
||||
"@docusaurus/preset-classic": "3.0.1",
|
||||
"@docusaurus/types": "3.0.1",
|
||||
"@flecks/core": "^2.0.3",
|
||||
"@flecks/core": "^3.0.0",
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"clsx": "^2.0.0",
|
||||
"comment-parser": "^1.3.0",
|
||||
|
@ -40,6 +39,6 @@
|
|||
"rimraf": "^5.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@flecks/fleck": "^2.0.3"
|
||||
"@flecks/fleck": "^3.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,10 +4,9 @@
|
|||
// There are various equivalent ways to declare your Docusaurus config.
|
||||
// See: https://docusaurus.io/docs/api/docusaurus-config
|
||||
|
||||
// For some reason we get a webpack warning if we use import here...
|
||||
const {configDefaults} = require('@flecks/dox/server'); // eslint-disable-line import/no-extraneous-dependencies
|
||||
const {configDefaults} = require('@flecks/dox/build/docusaurus');
|
||||
|
||||
export default async function flecksDocusaurus() {
|
||||
module.exports = async function flecksDocusaurus() {
|
||||
const defaults = configDefaults();
|
||||
/** @type {import('@docusaurus/types').Config} */
|
||||
const config = {
|
||||
|
@ -18,4 +17,4 @@ export default async function flecksDocusaurus() {
|
|||
baseUrl: '/',
|
||||
};
|
||||
return config;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import clsx from 'clsx';
|
||||
import Link from '@docusaurus/Link';
|
||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||
import Layout from '@theme/Layout';
|
||||
const clsx = require('clsx');
|
||||
const Link = require('@docusaurus/Link');
|
||||
const useDocusaurusContext = require('@docusaurus/useDocusaurusContext');
|
||||
const Layout = require('@theme/Layout');
|
||||
|
||||
import Heading from '@theme/Heading';
|
||||
const Heading = require('@theme/Heading');
|
||||
import styles from './index.module.css';
|
||||
|
||||
function HomepageHeader() {
|
||||
|
|
59
packages/electron/build/flecks.bootstrap.js
Normal file
59
packages/electron/build/flecks.bootstrap.js
Normal file
|
@ -0,0 +1,59 @@
|
|||
const {join} = require('path');
|
||||
|
||||
const {banner} = require('@flecks/core/server');
|
||||
|
||||
exports.hooks = {
|
||||
'@flecks/core.build': (target, config) => {
|
||||
if ('server' === target) {
|
||||
config.plugins.push(
|
||||
banner({
|
||||
// Bootstrap our `require()` magic.
|
||||
banner: "require('module').Module._initPaths();",
|
||||
include: 'index.js',
|
||||
}),
|
||||
);
|
||||
}
|
||||
},
|
||||
'@flecks/core.config': () => ({
|
||||
/**
|
||||
* Browser window options.
|
||||
*
|
||||
* See: https://www.electronjs.org/docs/latest/api/browser-window
|
||||
*/
|
||||
browserWindowOptions: {},
|
||||
/**
|
||||
* Install devtools extensions (by default).
|
||||
*
|
||||
* If `true`, will install some devtools extensions based on which flecks are enabled.
|
||||
*
|
||||
* You can pass an array of Chrome store IDs to install a list of custom extensions.
|
||||
*
|
||||
* Extensions will not be installed if `'production' === process.env.NODE_ENV`
|
||||
*/
|
||||
installExtensions: true,
|
||||
/**
|
||||
* Quit the app when all windows are closed.
|
||||
*/
|
||||
quitOnClosed: true,
|
||||
/**
|
||||
* The URL to load in electron by default.
|
||||
*
|
||||
* Defaults to `http://${flecks.get('@flecks/web.public')}`.
|
||||
*/
|
||||
url: undefined,
|
||||
}),
|
||||
'@flecks/core.build.alter': (configs) => {
|
||||
const {server: config} = configs;
|
||||
if (config) {
|
||||
const plugin = config.plugins.find(({pluginName}) => pluginName === 'StartServerPlugin');
|
||||
// Extremely hackish, c'est la vie.
|
||||
if (plugin) {
|
||||
const {exec} = plugin.options;
|
||||
plugin.options.exec = (compilation) => {
|
||||
plugin.options.args = [join(config.output.path, compilation.getPath(exec))];
|
||||
return join('..', '..', 'node_modules', '.bin', 'electron');
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
|
@ -8,7 +8,7 @@
|
|||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"version": "2.0.3",
|
||||
"version": "3.0.0",
|
||||
"scripts": {
|
||||
"build": "flecks build",
|
||||
"clean": "flecks clean",
|
||||
|
@ -20,11 +20,11 @@
|
|||
"server.js"
|
||||
],
|
||||
"dependencies": {
|
||||
"@flecks/core": "^2.0.3",
|
||||
"@flecks/core": "^3.0.0",
|
||||
"electron": "^18.0.1",
|
||||
"electron-devtools-installer": "^3.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@flecks/fleck": "^2.0.3"
|
||||
"@flecks/fleck": "^3.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
import {join} from 'path';
|
||||
|
||||
import {Flecks} from '@flecks/core';
|
||||
import {banner} from '@flecks/core/server';
|
||||
|
||||
const electron = __non_webpack_require__('electron');
|
||||
|
||||
|
@ -13,66 +10,13 @@ let win;
|
|||
|
||||
async function createWindow(flecks) {
|
||||
const {BrowserWindow} = flecks.electron;
|
||||
const {browserWindowOptions} = flecks.get('@flecks/electron/server');
|
||||
flecks.invoke('@flecks/electron/server.browserWindowOptions.alter', browserWindowOptions)
|
||||
const {browserWindowOptions} = flecks.get('@flecks/electron');
|
||||
flecks.invoke('@flecks/electron/server.browserWindowOptions.alter', browserWindowOptions);
|
||||
win = new BrowserWindow(browserWindowOptions);
|
||||
await flecks.invokeSequentialAsync('@flecks/electron/server.window', win);
|
||||
}
|
||||
|
||||
export const hooks = {
|
||||
'@flecks/core.build': (target, config) => {
|
||||
if ('server' === target) {
|
||||
config.plugins.push(
|
||||
banner({
|
||||
// Bootstrap our `require()` magic.
|
||||
banner: "require('module').Module._initPaths();",
|
||||
include: 'index.js',
|
||||
}),
|
||||
);
|
||||
}
|
||||
},
|
||||
'@flecks/core.config': () => ({
|
||||
/**
|
||||
* Browser window options.
|
||||
*
|
||||
* See: https://www.electronjs.org/docs/latest/api/browser-window
|
||||
*/
|
||||
browserWindowOptions: {},
|
||||
/**
|
||||
* Install devtools extensions (by default).
|
||||
*
|
||||
* If `true`, will install some devtools extensions based on which flecks are enabled.
|
||||
*
|
||||
* You can pass an array of Chrome store IDs to install a list of custom extensions.
|
||||
*
|
||||
* Extensions will not be installed if `'production' === process.env.NODE_ENV`
|
||||
*/
|
||||
installExtensions: true,
|
||||
/**
|
||||
* Quit the app when all windows are closed.
|
||||
*/
|
||||
quitOnClosed: true,
|
||||
/**
|
||||
* The URL to load in electron by default.
|
||||
*
|
||||
* Defaults to `http://${flecks.get('@flecks/web/server.public')}`.
|
||||
*/
|
||||
url: undefined,
|
||||
}),
|
||||
'@flecks/core.build.alter': (configs) => {
|
||||
const {server: config} = configs;
|
||||
if (config) {
|
||||
const plugin = config.plugins.find(({pluginName}) => pluginName === 'StartServerPlugin');
|
||||
// Extremely hackish, c'est la vie.
|
||||
if (plugin) {
|
||||
const {exec} = plugin.options;
|
||||
plugin.options.exec = (compilation) => {
|
||||
plugin.options.args = [join(config.output.path, compilation.getPath(exec))];
|
||||
return join('..', '..', 'node_modules', '.bin', 'electron');
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
'@flecks/core.mixin': (Flecks) => (
|
||||
class FlecksWithElectron extends Flecks {
|
||||
|
||||
|
@ -82,7 +26,7 @@ export const hooks = {
|
|||
),
|
||||
'@flecks/electron/server.initialize': async (electron, flecks) => {
|
||||
electron.app.on('window-all-closed', () => {
|
||||
const {quitOnClosed} = flecks.get('@flecks/electron/server');
|
||||
const {quitOnClosed} = flecks.get('@flecks/electron');
|
||||
if (!quitOnClosed) {
|
||||
return;
|
||||
}
|
||||
|
@ -101,11 +45,11 @@ export const hooks = {
|
|||
await createWindow(flecks);
|
||||
},
|
||||
'@flecks/electron/server.window': async (win, flecks) => {
|
||||
const {public: $$public} = flecks.get('@flecks/web/server');
|
||||
const {public: $$public} = flecks.get('@flecks/web');
|
||||
const {
|
||||
installExtensions,
|
||||
url = `http://${$$public}`,
|
||||
} = flecks.get('@flecks/electron/server');
|
||||
} = flecks.get('@flecks/electron');
|
||||
if (installExtensions && 'production' !== NODE_ENV) {
|
||||
const {
|
||||
default: installExtension,
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import {stat, unlink} from 'fs/promises';
|
||||
import {join} from 'path';
|
||||
const {stat, unlink} = require('fs/promises');
|
||||
const {join} = require('path');
|
||||
|
||||
import {D} from '@flecks/core';
|
||||
import {commands as coreCommands, glob} from '@flecks/core/server';
|
||||
import chokidar from 'chokidar';
|
||||
import clearModule from 'clear-module';
|
||||
import Mocha from 'mocha';
|
||||
const {D} = require('@flecks/core');
|
||||
const {commands: coreCommands, glob} = require('@flecks/core/server');
|
||||
const chokidar = require('chokidar');
|
||||
const clearModule = require('clear-module');
|
||||
const Mocha = require('mocha');
|
||||
|
||||
const debug = D('@flecks/core.commands');
|
||||
|
||||
|
@ -13,7 +13,7 @@ const {
|
|||
FLECKS_CORE_ROOT = process.cwd(),
|
||||
} = process.env;
|
||||
|
||||
export default (program, flecks) => {
|
||||
module.exports = (program, flecks) => {
|
||||
const commands = {};
|
||||
commands.test = {
|
||||
options: [
|
||||
|
@ -27,7 +27,7 @@ export default (program, flecks) => {
|
|||
watch,
|
||||
} = opts;
|
||||
const {build} = coreCommands(program, flecks);
|
||||
const child = build.action(undefined, opts);
|
||||
const child = await build.action(undefined, opts);
|
||||
const testPaths = await glob(join(FLECKS_CORE_ROOT, 'test/*.js'));
|
||||
if (0 === testPaths.length) {
|
||||
// eslint-disable-next-line no-console
|
||||
|
@ -71,6 +71,7 @@ export default (program, flecks) => {
|
|||
});
|
||||
});
|
||||
};
|
||||
require('@flecks/core/build/stub')(flecks.stubs);
|
||||
if (!watch) {
|
||||
await new Promise((resolve, reject) => {
|
||||
child.on('exit', (code) => {
|
|
@ -1,10 +1,10 @@
|
|||
const flecksConfigFn = require('@flecks/core/server/build/webpack.config');
|
||||
const flecksConfigFn = require('@flecks/core/build/fleck.webpack.config');
|
||||
|
||||
const ProcessAssets = require('./process-assets');
|
||||
|
||||
module.exports = async (env, argv, flecks) => {
|
||||
const config = await flecksConfigFn(env, argv, flecks);
|
||||
config.plugins.push(new ProcessAssets(flecks));
|
||||
config.stats = flecks.get('@flecks/fleck/server.stats');
|
||||
config.stats = flecks.get('@flecks/fleck.stats');
|
||||
return config;
|
||||
};
|
|
@ -1,14 +1,14 @@
|
|||
import {join} from 'path';
|
||||
const {join} = require('path');
|
||||
|
||||
import {glob} from '@flecks/core/server';
|
||||
const {glob} = require('@flecks/core/server');
|
||||
|
||||
import commands from './commands';
|
||||
const commands = require('./commands');
|
||||
|
||||
const {
|
||||
FLECKS_CORE_ROOT = process.cwd(),
|
||||
} = process.env;
|
||||
|
||||
export const hooks = {
|
||||
exports.hooks = {
|
||||
'@flecks/core.commands': commands,
|
||||
'@flecks/core.config': () => ({
|
||||
/**
|
||||
|
@ -20,20 +20,24 @@ export const hooks = {
|
|||
},
|
||||
}),
|
||||
'@flecks/core.targets': () => ['fleck'],
|
||||
'@flecks/fleck/server.processAssets': async (assets, compilation, flecks) => {
|
||||
'@flecks/fleck.processAssets': 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', 'src');
|
||||
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'));
|
||||
const testFiles = await glob(join(FLECKS_CORE_ROOT, 'test/**/*.js'));
|
||||
if (testFiles.length > 0) {
|
||||
files.push('test', 'test.js');
|
||||
}
|
||||
// Let others have a say.
|
||||
await flecks.invokeSequentialAsync('@flecks/fleck/server.packageJson', json, compilation);
|
||||
await flecks.invokeSequentialAsync('@flecks/fleck.packageJson', json, compilation);
|
||||
// Add any sourcemaps.
|
||||
json.files = json.files
|
||||
.map((filename) => {
|
|
@ -1,2 +1,2 @@
|
|||
'@flecks/core': {}
|
||||
'@flecks/fleck:./src': {}
|
||||
'@flecks/fleck:.': {}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user