refactor: hooks
This commit is contained in:
parent
6303681e6b
commit
aed55e9c3f
|
@ -17,7 +17,7 @@
|
||||||
"test": "lerna exec 'yarn && yarn test'"
|
"test": "lerna exec 'yarn && yarn test'"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@flecks/build": "*",
|
"@flecks/build": "^3.1.3",
|
||||||
"lerna": "^8.0.2"
|
"lerna": "^8.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,7 @@ module.exports = class Build extends Flecks {
|
||||||
async babel() {
|
async babel() {
|
||||||
return babelmerge.all([
|
return babelmerge.all([
|
||||||
{configFile: await this.resolveBuildConfig('babel.config.js')},
|
{configFile: await this.resolveBuildConfig('babel.config.js')},
|
||||||
...this.invokeFlat('@flecks/core.babel'),
|
...await this.invokeSequentialAsync('@flecks/core.babel'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,6 +106,16 @@ module.exports = class Build extends Flecks {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async configureBuilds(config, env, argv) {
|
||||||
|
await Promise.all(
|
||||||
|
Object.entries(config)
|
||||||
|
.map(([target, config]) => (
|
||||||
|
this.invokeSequentialAsync('@flecks/build.config', target, config, env, argv)
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
await this.invokeSequentialAsync('@flecks/build.config.alter', config, env, argv);
|
||||||
|
}
|
||||||
|
|
||||||
static async from(
|
static async from(
|
||||||
{
|
{
|
||||||
config: configParameter,
|
config: configParameter,
|
||||||
|
|
|
@ -41,7 +41,7 @@ program
|
||||||
const flecks = await Build.from();
|
const flecks = await Build.from();
|
||||||
debugSilly('bootstrapped');
|
debugSilly('bootstrapped');
|
||||||
// Register commands.
|
// Register commands.
|
||||||
const commands = flecks.invokeMerge('@flecks/build.commands', program);
|
const commands = await flecks.invokeMergeUniqueAsync('@flecks/build.commands', program);
|
||||||
const keys = Object.keys(commands).sort();
|
const keys = Object.keys(commands).sort();
|
||||||
for (let i = 0; i < keys.length; ++i) {
|
for (let i = 0; i < keys.length; ++i) {
|
||||||
const {
|
const {
|
||||||
|
|
|
@ -33,12 +33,7 @@ if (FLECKS_CORE_SYNC_FOR_ESLINT) {
|
||||||
const webpackConfigs = {
|
const webpackConfigs = {
|
||||||
fleck: await require(webpackConfigPath)(env, argv, flecks),
|
fleck: await require(webpackConfigPath)(env, argv, flecks),
|
||||||
};
|
};
|
||||||
await Promise.all(
|
await flecks.configureBuilds(webpackConfigs, env, argv);
|
||||||
flecks.invokeFlat('@flecks/build.config', 'fleck', webpackConfigs.fleck, env, argv),
|
|
||||||
);
|
|
||||||
await Promise.all(
|
|
||||||
flecks.invokeFlat('@flecks/build.config.alter', webpackConfigs, env, argv),
|
|
||||||
);
|
|
||||||
const {resolve} = webpackConfigs.fleck;
|
const {resolve} = webpackConfigs.fleck;
|
||||||
eslintConfig.settings['import/resolver'].webpack = {config: {resolve}};
|
eslintConfig.settings['import/resolver'].webpack = {config: {resolve}};
|
||||||
// Write it out to stdout.
|
// Write it out to stdout.
|
||||||
|
|
|
@ -7,6 +7,7 @@ export const hooks = {
|
||||||
* @param {Object} env The webpack environment.
|
* @param {Object} env The webpack environment.
|
||||||
* @param {Object} argv The webpack commandline arguments.
|
* @param {Object} argv The webpack commandline arguments.
|
||||||
* @see {@link https://webpack.js.org/configuration/configuration-types/#exporting-a-function}
|
* @see {@link https://webpack.js.org/configuration/configuration-types/#exporting-a-function}
|
||||||
|
* @invoke SequentialAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/build.config': (target, config, env, argv) => {
|
'@flecks/build.config': (target, config, env, argv) => {
|
||||||
if ('something' === target) {
|
if ('something' === target) {
|
||||||
|
@ -22,6 +23,7 @@ export const hooks = {
|
||||||
* @param {Object} env The webpack environment.
|
* @param {Object} env The webpack environment.
|
||||||
* @param {Object} argv The webpack commandline arguments.
|
* @param {Object} argv The webpack commandline arguments.
|
||||||
* @see {@link https://webpack.js.org/configuration/configuration-types/#exporting-a-function}
|
* @see {@link https://webpack.js.org/configuration/configuration-types/#exporting-a-function}
|
||||||
|
* @invoke SequentialAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/build.config.alter': (configs) => {
|
'@flecks/build.config.alter': (configs) => {
|
||||||
// Maybe we want to do something if a target exists..?
|
// Maybe we want to do something if a target exists..?
|
||||||
|
@ -32,11 +34,13 @@ export const hooks = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add implicitly resolved extensions.
|
* Add implicitly resolved extensions.
|
||||||
|
* @invoke Flat
|
||||||
*/
|
*/
|
||||||
'@flecks/build.extensions': () => ['.coffee'],
|
'@flecks/build.extensions': () => ['.coffee'],
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register build files. See [the build files page](./build-files) for more details.
|
* Register build files. See [the build files page](./build-files) for more details.
|
||||||
|
* @invoke
|
||||||
*/
|
*/
|
||||||
'@flecks/build.files': () => [
|
'@flecks/build.files': () => [
|
||||||
/**
|
/**
|
||||||
|
@ -48,6 +52,7 @@ export const hooks = {
|
||||||
/**
|
/**
|
||||||
* Define CLI commands.
|
* Define CLI commands.
|
||||||
* @param {[Command](https://github.com/tj/commander.js/tree/master#declaring-program-variable)} program The [Commander.js](https://github.com/tj/commander.js) program.
|
* @param {[Command](https://github.com/tj/commander.js/tree/master#declaring-program-variable)} program The [Commander.js](https://github.com/tj/commander.js) program.
|
||||||
|
* @invoke MergeUniqueAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/build.commands': (program, flecks) => {
|
'@flecks/build.commands': (program, flecks) => {
|
||||||
return {
|
return {
|
||||||
|
@ -74,6 +79,7 @@ export const hooks = {
|
||||||
* @param {string} target The build target.
|
* @param {string} target The build target.
|
||||||
* @param {Record<string, Source>} assets The assets.
|
* @param {Record<string, Source>} assets The assets.
|
||||||
* @param {[Compilation](https://webpack.js.org/api/compilation-object/)} compilation The webpack compilation.
|
* @param {[Compilation](https://webpack.js.org/api/compilation-object/)} compilation The webpack compilation.
|
||||||
|
* @invoke SequentialAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/build.processAssets': (target, assets, compilation) => {
|
'@flecks/build.processAssets': (target, assets, compilation) => {
|
||||||
if (this.myTargets.includes(target)) {
|
if (this.myTargets.includes(target)) {
|
||||||
|
@ -83,12 +89,14 @@ export const hooks = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define build targets.
|
* Define build targets.
|
||||||
|
* @invoke
|
||||||
*/
|
*/
|
||||||
'@flecks/build.targets': () => ['sometarget'],
|
'@flecks/build.targets': () => ['sometarget'],
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Alter defined build targets.
|
* Alter defined build targets.
|
||||||
* @param {Set<string>} targets The targets to build.
|
* @param {Set<string>} targets The targets to build.
|
||||||
|
* @invoke
|
||||||
*/
|
*/
|
||||||
'@flecks/build.targets.alter': (targets) => {
|
'@flecks/build.targets.alter': (targets) => {
|
||||||
targets.delete('some-target');
|
targets.delete('some-target');
|
||||||
|
|
|
@ -28,27 +28,25 @@ module.exports = async (env, argv) => {
|
||||||
debug('no build configuration found! aborting...');
|
debug('no build configuration found! aborting...');
|
||||||
await new Promise(() => {});
|
await new Promise(() => {});
|
||||||
}
|
}
|
||||||
const entries = await Promise.all(building.map(
|
const webpackConfigs = Object.fromEntries(
|
||||||
async ([fleck, target]) => {
|
await Promise.all(building.map(
|
||||||
const configFn = require(await flecks.resolveBuildConfig(`${target}.webpack.config.js`, fleck));
|
async ([fleck, target]) => {
|
||||||
if ('function' !== typeof configFn) {
|
const configFn = require(
|
||||||
debug(`'${
|
await flecks.resolveBuildConfig(`${target}.webpack.config.js`, fleck),
|
||||||
target
|
);
|
||||||
}' build configuration expected function got ${
|
if ('function' !== typeof configFn) {
|
||||||
typeof configFn
|
debug(`'${
|
||||||
}! aborting...`);
|
target
|
||||||
return undefined;
|
}' build configuration expected function got ${
|
||||||
}
|
typeof configFn
|
||||||
return [target, await configFn(env, argv, flecks)];
|
}! aborting...`);
|
||||||
},
|
return undefined;
|
||||||
));
|
}
|
||||||
await Promise.all(
|
return [target, await configFn(env, argv, flecks)];
|
||||||
entries.map(async ([target, config]) => (
|
},
|
||||||
Promise.all(flecks.invokeFlat('@flecks/build.config', target, config, env, argv))
|
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
const webpackConfigs = Object.fromEntries(entries);
|
await flecks.configureBuilds(webpackConfigs, env, argv);
|
||||||
await Promise.all(flecks.invokeFlat('@flecks/build.config.alter', webpackConfigs, env, argv));
|
|
||||||
const enterableWebpackConfigs = Object.values(webpackConfigs)
|
const enterableWebpackConfigs = Object.values(webpackConfigs)
|
||||||
.filter((webpackConfig) => {
|
.filter((webpackConfig) => {
|
||||||
if (!webpackConfig.entry) {
|
if (!webpackConfig.entry) {
|
||||||
|
|
|
@ -2,6 +2,7 @@ export const hooks = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Babel configuration.
|
* Babel configuration.
|
||||||
|
* @invoke SequentialAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/core.babel': () => ({
|
'@flecks/core.babel': () => ({
|
||||||
plugins: ['...'],
|
plugins: ['...'],
|
||||||
|
@ -9,6 +10,7 @@ export const hooks = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define configuration. See [the configuration page](./config) for more details.
|
* Define configuration. See [the configuration page](./config) for more details.
|
||||||
|
* @invoke Fleck
|
||||||
*/
|
*/
|
||||||
'@flecks/core.config': () => ({
|
'@flecks/core.config': () => ({
|
||||||
whatever: 'configuration',
|
whatever: 'configuration',
|
||||||
|
@ -24,6 +26,7 @@ export const hooks = {
|
||||||
* Let flecks gather for you.
|
* Let flecks gather for you.
|
||||||
*
|
*
|
||||||
* See [the Gathering guide](../gathering).
|
* See [the Gathering guide](../gathering).
|
||||||
|
* @invoke Async
|
||||||
*/
|
*/
|
||||||
'@flecks/core.gathered': () => ({
|
'@flecks/core.gathered': () => ({
|
||||||
// If this hook is implemented by a fleck called `@some/fleck`, then:
|
// If this hook is implemented by a fleck called `@some/fleck`, then:
|
||||||
|
@ -41,6 +44,7 @@ export const hooks = {
|
||||||
* Invoked when a fleck is HMR'd
|
* Invoked when a fleck is HMR'd
|
||||||
* @param {string} path The path of the fleck
|
* @param {string} path The path of the fleck
|
||||||
* @param {Module} updatedFleck The updated fleck module.
|
* @param {Module} updatedFleck The updated fleck module.
|
||||||
|
* @invoke
|
||||||
*/
|
*/
|
||||||
'@flecks/core.hmr': (path, updatedFleck) => {
|
'@flecks/core.hmr': (path, updatedFleck) => {
|
||||||
if ('my-fleck' === path) {
|
if ('my-fleck' === path) {
|
||||||
|
@ -52,6 +56,7 @@ export const hooks = {
|
||||||
* Invoked when a gathered set is HMR'd.
|
* Invoked when a gathered set is HMR'd.
|
||||||
* @param {constructor} gathered The gathered set.
|
* @param {constructor} gathered The gathered set.
|
||||||
* @param {string} hook The gather hook; e.g. `@flecks/db.models`.
|
* @param {string} hook The gather hook; e.g. `@flecks/db.models`.
|
||||||
|
* @invoke
|
||||||
*/
|
*/
|
||||||
'@flecks/core.hmr.gathered': (gathered, hook) => {
|
'@flecks/core.hmr.gathered': (gathered, hook) => {
|
||||||
// Do something with the gathered set...
|
// Do something with the gathered set...
|
||||||
|
@ -61,6 +66,7 @@ export const hooks = {
|
||||||
* Invoked when a gathered class is HMR'd.
|
* Invoked when a gathered class is HMR'd.
|
||||||
* @param {constructor} Class The class.
|
* @param {constructor} Class The class.
|
||||||
* @param {string} hook The gather hook; e.g. `@flecks/db.models`.
|
* @param {string} hook The gather hook; e.g. `@flecks/db.models`.
|
||||||
|
* @invoke
|
||||||
*/
|
*/
|
||||||
'@flecks/core.hmr.gathered.class': (Class, hook) => {
|
'@flecks/core.hmr.gathered.class': (Class, hook) => {
|
||||||
// Do something with Class...
|
// Do something with Class...
|
||||||
|
@ -70,6 +76,7 @@ export const hooks = {
|
||||||
* Invoked when flecks is building a fleck dependency graph.
|
* Invoked when flecks is building a fleck dependency graph.
|
||||||
* @param {Digraph} graph The dependency graph.
|
* @param {Digraph} graph The dependency graph.
|
||||||
* @param {string} hook The hook; e.g. `@flecks/server.up`.
|
* @param {string} hook The hook; e.g. `@flecks/server.up`.
|
||||||
|
* @invoke
|
||||||
*/
|
*/
|
||||||
'@flecks/core.priority': (graph, hook) => {
|
'@flecks/core.priority': (graph, hook) => {
|
||||||
// Make `@flecks/socket/server`'s `@flecks/server.up` implementation depend on
|
// Make `@flecks/socket/server`'s `@flecks/server.up` implementation depend on
|
||||||
|
@ -85,6 +92,7 @@ export const hooks = {
|
||||||
* Invoked when a fleck is registered.
|
* Invoked when a fleck is registered.
|
||||||
* @param {string} fleck
|
* @param {string} fleck
|
||||||
* @param {Module} M
|
* @param {Module} M
|
||||||
|
* @invoke
|
||||||
*/
|
*/
|
||||||
'@flecks/core.registered': (fleck, M) => {
|
'@flecks/core.registered': (fleck, M) => {
|
||||||
if ('@something/or-other' === fleck) {
|
if ('@something/or-other' === fleck) {
|
||||||
|
@ -94,6 +102,7 @@ export const hooks = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked when the application is starting.
|
* Invoked when the application is starting.
|
||||||
|
* @invoke SequentialAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/core.starting': () => {
|
'@flecks/core.starting': () => {
|
||||||
console.log('starting!');
|
console.log('starting!');
|
||||||
|
|
|
@ -170,11 +170,11 @@ exports.Flecks = class Flecks {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkAndDecorateRawGathered(hook, raw, check) {
|
async checkAndDecorateRawGathered(hook, raw, check) {
|
||||||
// Gather classes and check.
|
// Gather classes and check.
|
||||||
check(raw, hook);
|
check(raw, hook);
|
||||||
// Decorate and check.
|
// Decorate and check.
|
||||||
const decorated = this.invokeComposed(`${hook}.decorate`, raw);
|
const decorated = await this.invokeComposedAsync(`${hook}.decorate`, raw);
|
||||||
check(decorated, `${hook}.decorate`);
|
check(decorated, `${hook}.decorate`);
|
||||||
return decorated;
|
return decorated;
|
||||||
}
|
}
|
||||||
|
@ -383,7 +383,7 @@ exports.Flecks = class Flecks {
|
||||||
* @param {function} [config.check=() => {}] Check the validity of the gathered classes.
|
* @param {function} [config.check=() => {}] Check the validity of the gathered classes.
|
||||||
* @returns {object} An object with keys for ID, type, {@link ById}, and {@link ByType}.
|
* @returns {object} An object with keys for ID, type, {@link ById}, and {@link ByType}.
|
||||||
*/
|
*/
|
||||||
gather(
|
async gather(
|
||||||
hook,
|
hook,
|
||||||
{
|
{
|
||||||
idProperty = 'id',
|
idProperty = 'id',
|
||||||
|
@ -395,8 +395,8 @@ exports.Flecks = class Flecks {
|
||||||
throw new TypeError('Flecks.gather(): Expects parameter 1 (hook) to be string');
|
throw new TypeError('Flecks.gather(): Expects parameter 1 (hook) to be string');
|
||||||
}
|
}
|
||||||
// Gather classes and check.
|
// Gather classes and check.
|
||||||
const raw = this.invokeMerge(hook);
|
const raw = await this.invokeMergeAsync(hook);
|
||||||
const decorated = this.checkAndDecorateRawGathered(hook, raw, check);
|
const decorated = await this.checkAndDecorateRawGathered(hook, raw, check);
|
||||||
// Assign unique IDs to each class and sort by type.
|
// Assign unique IDs to each class and sort by type.
|
||||||
let uid = 1;
|
let uid = 1;
|
||||||
const ids = {};
|
const ids = {};
|
||||||
|
@ -834,45 +834,47 @@ exports.Flecks = class Flecks {
|
||||||
* @param {string} fleck
|
* @param {string} fleck
|
||||||
*/
|
*/
|
||||||
async refreshGathered(fleck) {
|
async refreshGathered(fleck) {
|
||||||
Object.entries(this.$$gathered)
|
await Promise.all(
|
||||||
.forEach(([
|
Object.entries(this.$$gathered)
|
||||||
hook,
|
.map(async ([
|
||||||
{
|
hook,
|
||||||
check,
|
{
|
||||||
idProperty,
|
check,
|
||||||
gathered,
|
idProperty,
|
||||||
typeProperty,
|
gathered,
|
||||||
},
|
typeProperty,
|
||||||
]) => {
|
},
|
||||||
let raw;
|
]) => {
|
||||||
// If decorating, gather all again
|
let raw;
|
||||||
if (this.fleckImplementation(fleck, `${hook}.decorate`)) {
|
// If decorating, gather all again
|
||||||
raw = this.invokeMergeAsync(hook);
|
if (this.fleckImplementation(fleck, `${hook}.decorate`)) {
|
||||||
debugSilly('%s implements %s.decorate', fleck, hook);
|
raw = await this.invokeMergeAsync(hook);
|
||||||
}
|
debugSilly('%s implements %s.decorate', fleck, hook);
|
||||||
// If only implementing, gather and decorate.
|
}
|
||||||
else if (this.fleckImplementation(fleck, hook)) {
|
// If only implementing, gather and decorate.
|
||||||
raw = this.invokeFleck(hook, fleck);
|
else if (this.fleckImplementation(fleck, hook)) {
|
||||||
debugSilly('%s implements %s', fleck, hook);
|
raw = await this.invokeFleck(hook, fleck);
|
||||||
}
|
debugSilly('%s implements %s', fleck, hook);
|
||||||
if (raw) {
|
}
|
||||||
const decorated = this.checkAndDecorateRawGathered(hook, raw, check);
|
if (raw) {
|
||||||
debug('updating gathered %s from %s...', hook, fleck);
|
const decorated = await this.checkAndDecorateRawGathered(hook, raw, check);
|
||||||
debugSilly('%O', decorated);
|
debug('updating gathered %s from %s...', hook, fleck);
|
||||||
const entries = Object.entries(decorated);
|
debugSilly('%O', decorated);
|
||||||
entries.forEach(([type, Class]) => {
|
const entries = Object.entries(decorated);
|
||||||
const {[type]: {[idProperty]: id}} = gathered;
|
entries.forEach(([type, Class]) => {
|
||||||
const Subclass = wrapGathered(Class, id, idProperty, type, typeProperty);
|
const {[type]: {[idProperty]: id}} = gathered;
|
||||||
// eslint-disable-next-line no-multi-assign
|
const Subclass = wrapGathered(Class, id, idProperty, type, typeProperty);
|
||||||
gathered[type] = Subclass;
|
// eslint-disable-next-line no-multi-assign
|
||||||
gathered[id] = Subclass;
|
gathered[type] = Subclass;
|
||||||
gathered[exports.ById][id] = Subclass;
|
gathered[id] = Subclass;
|
||||||
gathered[exports.ByType][type] = Subclass;
|
gathered[exports.ById][id] = Subclass;
|
||||||
this.invoke('@flecks/core.hmr.gathered.class', Subclass, hook);
|
gathered[exports.ByType][type] = Subclass;
|
||||||
});
|
this.invoke('@flecks/core.hmr.gathered.class', Subclass, hook);
|
||||||
this.invoke('@flecks/core.hmr.gathered', gathered, hook);
|
});
|
||||||
}
|
this.invoke('@flecks/core.hmr.gathered', gathered, hook);
|
||||||
});
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -12,7 +12,7 @@ it('can gather', async () => {
|
||||||
'@flecks/core/two': testTwo,
|
'@flecks/core/two': testTwo,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const Gathered = flecks.gather('@flecks/core/one/test-gather');
|
const Gathered = await flecks.gather('@flecks/core/one/test-gather');
|
||||||
expect(Object.keys(Gathered[ByType]).length)
|
expect(Object.keys(Gathered[ByType]).length)
|
||||||
.to.equal(Object.keys(Gathered[ById]).length);
|
.to.equal(Object.keys(Gathered[ById]).length);
|
||||||
const typeKeys = Object.keys(Gathered[ByType]);
|
const typeKeys = Object.keys(Gathered[ByType]);
|
||||||
|
|
|
@ -3,20 +3,18 @@ export const hooks = {
|
||||||
/**
|
/**
|
||||||
* Gather database models.
|
* Gather database models.
|
||||||
*
|
*
|
||||||
* In the example below, your fleck would have a `models` subdirectory, and each model would be
|
* See: [the Gathering guide](../gathering).
|
||||||
* defined in its own file.
|
* @invoke MergeAsync
|
||||||
* See: https://github.com/cha0s/flecks/tree/master/packages/user/src/server/models
|
|
||||||
*/
|
*/
|
||||||
'@flecks/db.models': Flecks.provide(require.context('./models', false, /\.js$/)),
|
'@flecks/db.models': Flecks.provide(require.context('./models', false, /\.js$/)),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decorate database models.
|
* Decorate database models.
|
||||||
*
|
*
|
||||||
* In the example below, your fleck would have a `models/decorators` subdirectory, and each
|
* See: [the Gathering guide](../gathering).
|
||||||
* decorator would be defined in its own file.
|
|
||||||
* See: https://github.com/cha0s/flecks/tree/master/packages/user/src/local/server/models/decorators
|
|
||||||
*
|
*
|
||||||
* @param {constructor} Model The model to decorate.
|
* @param {constructor} Model The model to decorate.
|
||||||
|
* @invoke ComposedAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/db.models.decorate': (
|
'@flecks/db.models.decorate': (
|
||||||
Flecks.decorate(require.context('./models/decorators', false, /\.js$/))
|
Flecks.decorate(require.context('./models/decorators', false, /\.js$/))
|
||||||
|
|
|
@ -8,6 +8,7 @@ export const hooks = {
|
||||||
* See: https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user
|
* See: https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user
|
||||||
*
|
*
|
||||||
* :::
|
* :::
|
||||||
|
* @invoke MergeUniqueAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/docker.containers': () => ({
|
'@flecks/docker.containers': () => ({
|
||||||
someContainer: {
|
someContainer: {
|
||||||
|
@ -29,6 +30,7 @@ export const hooks = {
|
||||||
* @param {string} dockerfile The content of the Dockerfile.
|
* @param {string} dockerfile The content of the Dockerfile.
|
||||||
*
|
*
|
||||||
* @returns The new content of the Dockerfile.
|
* @returns The new content of the Dockerfile.
|
||||||
|
* @invoke ComposedAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/docker.Dockerfile': (dockerfile) => (
|
'@flecks/docker.Dockerfile': (dockerfile) => (
|
||||||
dockerfile.replace('DEBUG=*', 'DEBUG=*,-*:silly')
|
dockerfile.replace('DEBUG=*', 'DEBUG=*,-*:silly')
|
||||||
|
@ -37,6 +39,7 @@ export const hooks = {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {Object} config The object representing the docker compose configuration.
|
* @param {Object} config The object representing the docker compose configuration.
|
||||||
|
* @invoke SequentialAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/docker.docker-compose.yml': (config) => {
|
'@flecks/docker.docker-compose.yml': (config) => {
|
||||||
config.version = '3.1';
|
config.version = '3.1';
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
exports.generateDockerFile = (flecks) => {
|
exports.generateDockerFile = async (flecks) => {
|
||||||
const dockerfile = [
|
const dockerfile = [
|
||||||
'FROM node:20',
|
'FROM node:20',
|
||||||
'',
|
'',
|
||||||
|
@ -16,7 +16,7 @@ exports.generateDockerFile = (flecks) => {
|
||||||
'VOLUME /var/www/node_modules',
|
'VOLUME /var/www/node_modules',
|
||||||
'',
|
'',
|
||||||
].join('\n');
|
].join('\n');
|
||||||
return flecks.invokeComposed('@flecks/docker.Dockerfile', dockerfile);
|
return flecks.invokeComposedAsync('@flecks/docker.Dockerfile', dockerfile);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.generateComposeConfig = async (flecks) => {
|
exports.generateComposeConfig = async (flecks) => {
|
||||||
|
@ -36,7 +36,7 @@ exports.generateComposeConfig = async (flecks) => {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const containers = flecks.invoke('@flecks/docker.containers');
|
const containers = await flecks.invokeAsync('@flecks/docker.containers');
|
||||||
(
|
(
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
Object.entries(containers)
|
Object.entries(containers)
|
||||||
|
@ -69,6 +69,6 @@ exports.generateComposeConfig = async (flecks) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
const config = {version: '3', services};
|
const config = {version: '3', services};
|
||||||
flecks.invoke('@flecks/docker.docker-compose.yml', config);
|
await flecks.invokeSequentialAsync('@flecks/docker.docker-compose.yml', config);
|
||||||
return config;
|
return config;
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,7 +12,7 @@ module.exports = class FlecksDockerOutput {
|
||||||
apply(compiler) {
|
apply(compiler) {
|
||||||
compiler.hooks.compilation.tap('FlecksDockerOutput', (compilation) => {
|
compiler.hooks.compilation.tap('FlecksDockerOutput', (compilation) => {
|
||||||
compilation.hooks.additionalAssets.tapAsync('FlecksDockerOutput', async (callback) => {
|
compilation.hooks.additionalAssets.tapAsync('FlecksDockerOutput', async (callback) => {
|
||||||
const dockerFile = generateDockerFile(this.options.flecks);
|
const dockerFile = await generateDockerFile(this.options.flecks);
|
||||||
compilation.assets.Dockerfile = {
|
compilation.assets.Dockerfile = {
|
||||||
source: () => dockerFile,
|
source: () => dockerFile,
|
||||||
size: () => dockerFile.length,
|
size: () => dockerFile.length,
|
||||||
|
|
|
@ -5,7 +5,7 @@ export const hooks = {
|
||||||
if (!flecks.get('@flecks/docker.enabled')) {
|
if (!flecks.get('@flecks/docker.enabled')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const containers = await flecks.invokeMergeAsync('@flecks/docker.containers');
|
const containers = await flecks.invokeMergeUniqueAsync('@flecks/docker.containers');
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
Object.entries(containers)
|
Object.entries(containers)
|
||||||
.map(([key, config]) => startContainer(flecks, key, config)),
|
.map(([key, config]) => startContainer(flecks, key, config)),
|
||||||
|
|
|
@ -104,11 +104,22 @@ exports.generateDocusaurusHookPage = (hooks) => {
|
||||||
Object.entries(hooks)
|
Object.entries(hooks)
|
||||||
.sort(([lhook], [rhook]) => (lhook < rhook ? -1 : 1))
|
.sort(([lhook], [rhook]) => (lhook < rhook ? -1 : 1))
|
||||||
.forEach(([hook, {implementations = [], invocations = [], specification}]) => {
|
.forEach(([hook, {implementations = [], invocations = [], specification}]) => {
|
||||||
const {description, example, params} = specification || {
|
const {
|
||||||
|
description,
|
||||||
|
example,
|
||||||
|
invoke,
|
||||||
|
params,
|
||||||
|
} = specification || {
|
||||||
params: [],
|
params: [],
|
||||||
};
|
};
|
||||||
source.push(`## \`${hook}\``);
|
source.push(`## \`${hook}\``);
|
||||||
source.push('');
|
source.push('');
|
||||||
|
if (invoke) {
|
||||||
|
source.push('<h3 style={{fontSize: "1.125rem", marginTop: 0}}>');
|
||||||
|
source.push(`[${invoke}](../hooks#${invoke.toLowerCase()})`);
|
||||||
|
source.push('</h3>');
|
||||||
|
source.push('');
|
||||||
|
}
|
||||||
if (description) {
|
if (description) {
|
||||||
source.push(...description.split('\n'));
|
source.push(...description.split('\n'));
|
||||||
source.push('');
|
source.push('');
|
||||||
|
@ -151,6 +162,7 @@ exports.generateDocusaurusHookPage = (hooks) => {
|
||||||
source.push('</div>');
|
source.push('</div>');
|
||||||
}
|
}
|
||||||
source.push('</div>');
|
source.push('</div>');
|
||||||
|
source.push('\n');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return source.join('\n');
|
return source.join('\n');
|
||||||
|
@ -310,18 +322,9 @@ exports.generateJson = async function generate(flecks) {
|
||||||
type,
|
type,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
hookSpecifications.forEach(({
|
hookSpecifications.forEach(({hook, ...specification}) => {
|
||||||
hook,
|
|
||||||
description,
|
|
||||||
example,
|
|
||||||
params,
|
|
||||||
}) => {
|
|
||||||
ensureHook(hook);
|
ensureHook(hook);
|
||||||
r.hooks[hook].specification = {
|
r.hooks[hook].specification = specification;
|
||||||
description,
|
|
||||||
example,
|
|
||||||
params,
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -84,17 +84,10 @@ exports.parseHookSpecificationSource = async (path, source, options) => {
|
||||||
const hookSpecifications = [];
|
const hookSpecifications = [];
|
||||||
traverse(ast, hookSpecificationVisitor((hookSpecification) => {
|
traverse(ast, hookSpecificationVisitor((hookSpecification) => {
|
||||||
const {
|
const {
|
||||||
description,
|
|
||||||
hook,
|
|
||||||
location: {start: {index: start}, end: {index: end}},
|
location: {start: {index: start}, end: {index: end}},
|
||||||
params,
|
...specification
|
||||||
} = hookSpecification;
|
} = hookSpecification;
|
||||||
hookSpecifications.push({
|
hookSpecifications.push({...specification, example: source.slice(start, end)});
|
||||||
description,
|
|
||||||
example: source.slice(start, end),
|
|
||||||
hook,
|
|
||||||
params,
|
|
||||||
});
|
|
||||||
}));
|
}));
|
||||||
return {
|
return {
|
||||||
hookSpecifications,
|
hookSpecifications,
|
||||||
|
|
|
@ -203,6 +203,9 @@ exports.hookSpecificationVisitor = (fn) => (
|
||||||
const {key, value: example} = property;
|
const {key, value: example} = property;
|
||||||
const [{value}] = property.leadingComments;
|
const [{value}] = property.leadingComments;
|
||||||
const [{description, tags}] = parseComment(`/**\n${value}\n*/`, {spacing: 'preserve'});
|
const [{description, tags}] = parseComment(`/**\n${value}\n*/`, {spacing: 'preserve'});
|
||||||
|
const [invoke] = tags
|
||||||
|
.filter(({tag}) => 'invoke' === tag)
|
||||||
|
.map(({name}) => (name ? `invoke${name}` : 'invoke'));
|
||||||
const [returns] = tags
|
const [returns] = tags
|
||||||
.filter(({tag}) => 'returns' === tag)
|
.filter(({tag}) => 'returns' === tag)
|
||||||
.map(({name, type}) => ({description: name, type}));
|
.map(({name, type}) => ({description: name, type}));
|
||||||
|
@ -214,6 +217,7 @@ exports.hookSpecificationVisitor = (fn) => (
|
||||||
.filter(({tag}) => 'param' === tag)
|
.filter(({tag}) => 'param' === tag)
|
||||||
.map(({description, name, type}) => ({description, name, type})),
|
.map(({description, name, type}) => ({description, name, type})),
|
||||||
...returns && {returns},
|
...returns && {returns},
|
||||||
|
...invoke && {invoke},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -32,6 +32,10 @@ export default [
|
||||||
{description: 'Foo', name: 'foo', type: 'string'},
|
{description: 'Foo', name: 'foo', type: 'string'},
|
||||||
{description: 'Bar', name: 'bar', type: 'number'},
|
{description: 'Bar', name: 'bar', type: 'number'},
|
||||||
],
|
],
|
||||||
|
returns: {
|
||||||
|
description: 'Baz',
|
||||||
|
type: 'Baz',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,6 +3,7 @@ export const hooks = {
|
||||||
/**
|
/**
|
||||||
* Alter the options for initialization of the Electron browser window.
|
* Alter the options for initialization of the Electron browser window.
|
||||||
* @param {[BrowserWindowConstructorOptions](https://www.electronjs.org/docs/latest/api/structures/browser-window-options)} browserWindowOptions The options.
|
* @param {[BrowserWindowConstructorOptions](https://www.electronjs.org/docs/latest/api/structures/browser-window-options)} browserWindowOptions The options.
|
||||||
|
* @invoke SequentialAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/electron/server.browserWindowOptions.alter': (browserWindowOptions) => {
|
'@flecks/electron/server.browserWindowOptions.alter': (browserWindowOptions) => {
|
||||||
browserWindowOptions.icon = 'cute-kitten.png';
|
browserWindowOptions.icon = 'cute-kitten.png';
|
||||||
|
@ -11,6 +12,7 @@ export const hooks = {
|
||||||
/**
|
/**
|
||||||
* Extensions to install.
|
* Extensions to install.
|
||||||
* @param {[Installer](https://github.com/MarshallOfSound/electron-devtools-installer)} installer The installer.
|
* @param {[Installer](https://github.com/MarshallOfSound/electron-devtools-installer)} installer The installer.
|
||||||
|
* @invoke Flat
|
||||||
*/
|
*/
|
||||||
'@flecks/electron/server.extensions': (installer) => [
|
'@flecks/electron/server.extensions': (installer) => [
|
||||||
// Some defaults provided...
|
// Some defaults provided...
|
||||||
|
@ -22,6 +24,7 @@ export const hooks = {
|
||||||
/**
|
/**
|
||||||
* Invoked when electron is initializing.
|
* Invoked when electron is initializing.
|
||||||
* @param {Electron} electron The electron module.
|
* @param {Electron} electron The electron module.
|
||||||
|
* @invoke SequentialAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/electron/server.initialize': (electron) => {
|
'@flecks/electron/server.initialize': (electron) => {
|
||||||
electron.app.on('will-quit', () => {
|
electron.app.on('will-quit', () => {
|
||||||
|
@ -32,6 +35,7 @@ export const hooks = {
|
||||||
/**
|
/**
|
||||||
* Invoked when a window is created
|
* Invoked when a window is created
|
||||||
* @param {Electron.BrowserWindow} win The electron browser window. See: https://www.electronjs.org/docs/latest/api/browser-window
|
* @param {Electron.BrowserWindow} win The electron browser window. See: https://www.electronjs.org/docs/latest/api/browser-window
|
||||||
|
* @invoke SequentialAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/electron/server.window': (win) => {
|
'@flecks/electron/server.window': (win) => {
|
||||||
win.maximize();
|
win.maximize();
|
||||||
|
|
|
@ -7,7 +7,10 @@ let win;
|
||||||
async function createWindow(flecks) {
|
async function createWindow(flecks) {
|
||||||
const {BrowserWindow} = flecks.electron;
|
const {BrowserWindow} = flecks.electron;
|
||||||
const {browserWindowOptions} = flecks.get('@flecks/electron');
|
const {browserWindowOptions} = flecks.get('@flecks/electron');
|
||||||
flecks.invoke('@flecks/electron/server.browserWindowOptions.alter', browserWindowOptions);
|
await flecks.invokeSequentialAsync(
|
||||||
|
'@flecks/electron/server.browserWindowOptions.alter',
|
||||||
|
browserWindowOptions,
|
||||||
|
);
|
||||||
win = new BrowserWindow(browserWindowOptions);
|
win = new BrowserWindow(browserWindowOptions);
|
||||||
await flecks.invokeSequentialAsync('@flecks/electron/server.window', win);
|
await flecks.invokeSequentialAsync('@flecks/electron/server.window', win);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ export const hooks = {
|
||||||
* Process the `package.json` for a built fleck.
|
* Process the `package.json` for a built fleck.
|
||||||
* @param {Object} json The JSON.
|
* @param {Object} json The JSON.
|
||||||
* @param {[Compilation](https://webpack.js.org/api/compilation-object/)} compilation The webpack compilation.
|
* @param {[Compilation](https://webpack.js.org/api/compilation-object/)} compilation The webpack compilation.
|
||||||
|
* @invoke SequentialAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/fleck.packageJson': (json, compilation) => {
|
'@flecks/fleck.packageJson': (json, compilation) => {
|
||||||
json.files.push('something');
|
json.files.push('something');
|
||||||
|
|
|
@ -2,6 +2,7 @@ export const hooks = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define React components for login strategies.
|
* Define React components for login strategies.
|
||||||
|
* @invoke MergeUnique
|
||||||
*/
|
*/
|
||||||
'@flecks/passport-react.strategies': () => ({
|
'@flecks/passport-react.strategies': () => ({
|
||||||
MyService: SomeBeautifulComponent,
|
MyService: SomeBeautifulComponent,
|
||||||
|
|
|
@ -3,6 +3,7 @@ export const hooks = {
|
||||||
/**
|
/**
|
||||||
* Define passport login strategies. See: https://www.passportjs.org/concepts/authentication/strategies/
|
* Define passport login strategies. See: https://www.passportjs.org/concepts/authentication/strategies/
|
||||||
* @param {Passport} passport The passport instance.
|
* @param {Passport} passport The passport instance.
|
||||||
|
* @invoke MergeUniqueAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/passport.strategies': (passport) => ({
|
'@flecks/passport.strategies': (passport) => ({
|
||||||
MyService: SomeStrategy,
|
MyService: SomeStrategy,
|
||||||
|
|
|
@ -41,7 +41,7 @@ export const hooks = {
|
||||||
flecks.passport = {
|
flecks.passport = {
|
||||||
initialize: passport.initialize(),
|
initialize: passport.initialize(),
|
||||||
session: passport.session(),
|
session: passport.session(),
|
||||||
strategies: flecks.invokeMergeUnique('@flecks/passport.strategies', passport),
|
strategies: await flecks.invokeMergeUniqueAsync('@flecks/passport.strategies', passport),
|
||||||
};
|
};
|
||||||
Object.entries(flecks.passport.strategies)
|
Object.entries(flecks.passport.strategies)
|
||||||
.forEach(([name, strategy]) => {
|
.forEach(([name, strategy]) => {
|
||||||
|
@ -51,7 +51,7 @@ export const hooks = {
|
||||||
{before: '@flecks/web/server', after: ['@flecks/db/server', '@flecks/session/server']},
|
{before: '@flecks/web/server', after: ['@flecks/db/server', '@flecks/session/server']},
|
||||||
),
|
),
|
||||||
'@flecks/socket.intercom': () => ({
|
'@flecks/socket.intercom': () => ({
|
||||||
'@flecks/passport.users': async (sids, server) => {
|
users: async (sids, server) => {
|
||||||
const sockets = await server.sockets();
|
const sockets = await server.sockets();
|
||||||
return sids
|
return sids
|
||||||
.filter((sid) => sockets.has(sid))
|
.filter((sid) => sockets.has(sid))
|
||||||
|
|
|
@ -4,6 +4,7 @@ export const hooks = {
|
||||||
*
|
*
|
||||||
* Note: `req` will be only be defined when server-side rendering.
|
* Note: `req` will be only be defined when server-side rendering.
|
||||||
* @param {http.ClientRequest} req The HTTP request object.
|
* @param {http.ClientRequest} req The HTTP request object.
|
||||||
|
* @invoke SequentialAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/react.providers': (req) => {
|
'@flecks/react.providers': (req) => {
|
||||||
// Generally it makes more sense to separate client and server concerns using platform
|
// Generally it makes more sense to separate client and server concerns using platform
|
||||||
|
@ -18,6 +19,7 @@ export const hooks = {
|
||||||
* or an array of two elements where the first element is the component and the second element
|
* or an array of two elements where the first element is the component and the second element
|
||||||
* is the props passed to the component.
|
* is the props passed to the component.
|
||||||
* @param {http.ClientRequest} req The HTTP request object.
|
* @param {http.ClientRequest} req The HTTP request object.
|
||||||
|
* @invoke Async
|
||||||
*/
|
*/
|
||||||
'@flecks/react.roots': (req) => {
|
'@flecks/react.roots': (req) => {
|
||||||
// Note that we're not returning `<Component />`, but `Component`.
|
// Note that we're not returning `<Component />`, but `Component`.
|
||||||
|
|
|
@ -10,7 +10,7 @@ const debug = D('@flecks/react/root');
|
||||||
const debugSilly = debug.extend('silly');
|
const debugSilly = debug.extend('silly');
|
||||||
|
|
||||||
export default async (flecks, req) => {
|
export default async (flecks, req) => {
|
||||||
const Roots = flecks.invoke('@flecks/react.roots', req);
|
const Roots = await flecks.invokeAsync('@flecks/react.roots', req);
|
||||||
debugSilly('roots: %O', Roots);
|
debugSilly('roots: %O', Roots);
|
||||||
const Providers = await flecks.invokeSequentialAsync('@flecks/react.providers', req);
|
const Providers = await flecks.invokeSequentialAsync('@flecks/react.providers', req);
|
||||||
const FlattenedProviders = [];
|
const FlattenedProviders = [];
|
||||||
|
|
|
@ -4,7 +4,7 @@ export default async (flecks, opts = {}) => {
|
||||||
const {
|
const {
|
||||||
host,
|
host,
|
||||||
port,
|
port,
|
||||||
} = flecks.get('@flecks/redis/server');
|
} = flecks.get('@flecks/redis');
|
||||||
const client = createClient({
|
const client = createClient({
|
||||||
url: `redis://${host}:${port}`,
|
url: `redis://${host}:${port}`,
|
||||||
...opts,
|
...opts,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
export const hooks = {
|
export const hooks = {
|
||||||
/**
|
/**
|
||||||
* Define side-effects to run against Redux actions.
|
* Define side-effects to run against Redux actions.
|
||||||
|
* @invoke SequentialAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/redux.effects': () => ({
|
'@flecks/redux.effects': () => ({
|
||||||
someActionName: (store, action) => {
|
someActionName: (store, action) => {
|
||||||
|
@ -9,6 +10,7 @@ export const hooks = {
|
||||||
}),
|
}),
|
||||||
/**
|
/**
|
||||||
* Define root-level reducers for the Redux store.
|
* Define root-level reducers for the Redux store.
|
||||||
|
* @invoke SequentialAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/redux.reducers': () => {
|
'@flecks/redux.reducers': () => {
|
||||||
return (state, action) => {
|
return (state, action) => {
|
||||||
|
@ -20,6 +22,7 @@ export const hooks = {
|
||||||
* Define Redux slices.
|
* Define Redux slices.
|
||||||
*
|
*
|
||||||
* See: https://redux-toolkit.js.org/api/createSlice
|
* See: https://redux-toolkit.js.org/api/createSlice
|
||||||
|
* @invoke MergeUniqueAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/redux.slices': () => {
|
'@flecks/redux.slices': () => {
|
||||||
const something = createSlice(
|
const something = createSlice(
|
||||||
|
@ -32,6 +35,7 @@ export const hooks = {
|
||||||
/**
|
/**
|
||||||
* Modify Redux store configuration.
|
* Modify Redux store configuration.
|
||||||
* @param {Object} options A mutable object with keys for enhancers and middleware.
|
* @param {Object} options A mutable object with keys for enhancers and middleware.
|
||||||
|
* @invoke SequentialAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/redux.store': (options) => {
|
'@flecks/redux.store': (options) => {
|
||||||
options.enhancers.splice(someIndex, 1);
|
options.enhancers.splice(someIndex, 1);
|
||||||
|
|
|
@ -6,8 +6,8 @@ import localStorageEnhancer from './local-storage';
|
||||||
export const hooks = {
|
export const hooks = {
|
||||||
'@flecks/web/client.up': Flecks.priority(
|
'@flecks/web/client.up': Flecks.priority(
|
||||||
async (flecks) => {
|
async (flecks) => {
|
||||||
const slices = await flecks.invokeMergeUnique('@flecks/redux.slices');
|
const slices = await flecks.invokeMergeUniqueAsync('@flecks/redux.slices');
|
||||||
const reducer = createReducer(flecks, slices);
|
const reducer = await createReducer(flecks, slices);
|
||||||
// Hydrate from server.
|
// Hydrate from server.
|
||||||
const {preloadedState} = flecks.get('@flecks/redux');
|
const {preloadedState} = flecks.get('@flecks/redux');
|
||||||
const store = await configureStore(flecks, reducer, {preloadedState});
|
const store = await configureStore(flecks, reducer, {preloadedState});
|
||||||
|
|
|
@ -10,8 +10,8 @@ const debugSilly = debug.extend('silly');
|
||||||
export const hooks = {
|
export const hooks = {
|
||||||
'@flecks/electron/server.extensions': (installer) => [installer.REDUX_DEVTOOLS],
|
'@flecks/electron/server.extensions': (installer) => [installer.REDUX_DEVTOOLS],
|
||||||
'@flecks/web/server.request.route': (flecks) => async (req, res, next) => {
|
'@flecks/web/server.request.route': (flecks) => async (req, res, next) => {
|
||||||
const slices = await flecks.invokeMergeUnique('@flecks/redux.slices');
|
const slices = await flecks.invokeMergeUniqueAsync('@flecks/redux.slices');
|
||||||
const reducer = createReducer(flecks, slices);
|
const reducer = await createReducer(flecks, slices);
|
||||||
// Let the slices have a(n async) chance to hydrate with server data.
|
// Let the slices have a(n async) chance to hydrate with server data.
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
Object.values(slices).map(({hydrateServer}) => hydrateServer?.(req, flecks)),
|
Object.values(slices).map(({hydrateServer}) => hydrateServer?.(req, flecks)),
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import {combineReducers} from '@reduxjs/toolkit';
|
import {combineReducers} from '@reduxjs/toolkit';
|
||||||
import reduceReducers from 'reduce-reducers';
|
import reduceReducers from 'reduce-reducers';
|
||||||
|
|
||||||
export default (flecks, slices) => {
|
export default async (flecks, slices) => {
|
||||||
let reducers = flecks.invokeFlat('@flecks/redux.reducers');
|
let reducers = await flecks.invokeSequentialAsync('@flecks/redux.reducers');
|
||||||
if (Object.keys(slices).length > 0) {
|
if (Object.keys(slices).length > 0) {
|
||||||
reducers = reducers.concat(combineReducers(slices));
|
reducers = reducers.concat(combineReducers(slices));
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,10 @@ export default async function configureStore(flecks, reducer, {preloadedState})
|
||||||
],
|
],
|
||||||
middleware: [
|
middleware: [
|
||||||
'@flecks/redux/defaultMiddleware',
|
'@flecks/redux/defaultMiddleware',
|
||||||
effectsMiddleware(flecks),
|
await effectsMiddleware(flecks),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
flecks.invokeFlat('@flecks/redux.store', options);
|
await flecks.invokeSequentialAsync('@flecks/redux.store', options);
|
||||||
return configureStoreR({
|
return configureStoreR({
|
||||||
enhancers: (defaultEnhancers) => {
|
enhancers: (defaultEnhancers) => {
|
||||||
const index = options.enhancers.indexOf('@flecks/redux/defaultEnhancers');
|
const index = options.enhancers.indexOf('@flecks/redux/defaultEnhancers');
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
export default (flecks) => {
|
export default async (flecks) => {
|
||||||
const effects = flecks.invokeFlat('@flecks/redux.effects');
|
const effects = await flecks.invokeSequentialAsync('@flecks/redux.effects');
|
||||||
const effect = (store, action) => {
|
const effect = (store, action) => {
|
||||||
effects.forEach((map) => {
|
effects.forEach((map) => {
|
||||||
if (map[action.type]) {
|
if (map[action.type]) {
|
||||||
|
|
|
@ -3,6 +3,7 @@ export const hooks = {
|
||||||
* Define REPL commands.
|
* Define REPL commands.
|
||||||
*
|
*
|
||||||
* Note: commands will be prefixed with a period in the Node REPL.
|
* Note: commands will be prefixed with a period in the Node REPL.
|
||||||
|
* @invoke MergeUniqueAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/repl.commands': () => ({
|
'@flecks/repl.commands': () => ({
|
||||||
someCommand: (...args) => {
|
someCommand: (...args) => {
|
||||||
|
@ -13,6 +14,7 @@ export const hooks = {
|
||||||
}),
|
}),
|
||||||
/**
|
/**
|
||||||
* Provide global context to the REPL.
|
* Provide global context to the REPL.
|
||||||
|
* @invoke MergeUniqueAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/repl.context': () => {
|
'@flecks/repl.context': () => {
|
||||||
// Now you'd be able to do like:
|
// Now you'd be able to do like:
|
||||||
|
|
|
@ -11,19 +11,20 @@ const debugSilly = debug.extend('silly');
|
||||||
|
|
||||||
export async function createReplServer(flecks) {
|
export async function createReplServer(flecks) {
|
||||||
const {id} = flecks.get('@flecks/core');
|
const {id} = flecks.get('@flecks/core');
|
||||||
const context = (await Promise.all(flecks.invokeFlat('@flecks/repl.context')))
|
const context = {
|
||||||
.reduce((r, vars) => ({...r, ...vars}), {flecks});
|
...await flecks.invokeMergeUniqueAsync('@flecks/repl.context'),
|
||||||
|
flecks,
|
||||||
|
};
|
||||||
debug(
|
debug(
|
||||||
'Object.keys(context) === %O',
|
'Object.keys(context) === %O',
|
||||||
Object.keys(context),
|
Object.keys(context),
|
||||||
);
|
);
|
||||||
const commands = {};
|
const commands = {};
|
||||||
Object.entries(
|
Object.entries(await flecks.invokeMergeUniqueAsync('@flecks/repl.commands'))
|
||||||
flecks.invokeFlat('@flecks/repl.commands').reduce((r, commands) => ({...r, ...commands}), {}),
|
.forEach(([key, value]) => {
|
||||||
).forEach(([key, value]) => {
|
commands[key] = value;
|
||||||
commands[key] = value;
|
debugSilly('registered command: %s', key);
|
||||||
debugSilly('registered command: %s', key);
|
});
|
||||||
});
|
|
||||||
const netServer = createServer((socket) => {
|
const netServer = createServer((socket) => {
|
||||||
debug('client connection to repl established');
|
debug('client connection to repl established');
|
||||||
socket.on('close', () => {
|
socket.on('close', () => {
|
||||||
|
|
|
@ -2,6 +2,7 @@ export const hooks = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pass information to the runtime.
|
* Pass information to the runtime.
|
||||||
|
* @invoke Async
|
||||||
*/
|
*/
|
||||||
'@flecks/server.runtime': async () => ({
|
'@flecks/server.runtime': async () => ({
|
||||||
something: '...',
|
something: '...',
|
||||||
|
@ -9,6 +10,7 @@ export const hooks = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define sequential actions to run when the server comes up.
|
* Define sequential actions to run when the server comes up.
|
||||||
|
* @invoke SequentialAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/server.up': async () => {
|
'@flecks/server.up': async () => {
|
||||||
await youCanDoAsyncThingsHere();
|
await youCanDoAsyncThingsHere();
|
||||||
|
|
|
@ -2,16 +2,10 @@ export const hooks = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure the session. See: https://github.com/expressjs/session#sessionoptions
|
* Configure the session. See: https://github.com/expressjs/session#sessionoptions
|
||||||
|
* @invoke MergeAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/session.config': async () => ({
|
'@flecks/session.config': async () => ({
|
||||||
saveUninitialized: true,
|
saveUninitialized: true,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
/**
|
|
||||||
* Define sequential actions to run when the server comes up.
|
|
||||||
*/
|
|
||||||
'@flecks/server.up': async () => {
|
|
||||||
await youCanDoAsyncThingsHere();
|
|
||||||
},
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,6 +3,7 @@ export const hooks = {
|
||||||
* Modify Socket.io client configuration.
|
* Modify Socket.io client configuration.
|
||||||
*
|
*
|
||||||
* See: https://socket.io/docs/v4/client-options/
|
* See: https://socket.io/docs/v4/client-options/
|
||||||
|
* @invoke MergeAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/socket.client': () => ({
|
'@flecks/socket.client': () => ({
|
||||||
timeout: Infinity,
|
timeout: Infinity,
|
||||||
|
@ -10,36 +11,34 @@ export const hooks = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define server-side intercom channels.
|
* Define server-side intercom channels.
|
||||||
|
* @invoke Async
|
||||||
*/
|
*/
|
||||||
'@flecks/socket.intercom': (req) => ({
|
'@flecks/socket.intercom': (req) => ({
|
||||||
// This would have been called like:
|
// Assuming `@my/fleck` implemented this hook, this could be called like:
|
||||||
// `const result = await req.intercom('@my/fleck.key', payload)`.
|
// `const result = await req.intercom('@my/fleck.key', payload)`.
|
||||||
// `result` will be an `n`-length array, where `n` is the number of server instances. Each
|
// `result` will be an `n`-length array, where `n` is the number of server instances. Each
|
||||||
// element in the array will be the result of `someServiceSpecificInformation()` running
|
// element in the array will be the result of `someServiceSpecificInformation()` running
|
||||||
// against that server instance.
|
// against that server instance.
|
||||||
'@my/fleck.key': async (payload, server) => {
|
key: async (payload, server) => {
|
||||||
return someServiceSpecificInformation();
|
return someServiceSpecificInformation();
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define socket packets.
|
* Gather socket packets.
|
||||||
*
|
*
|
||||||
* In the example below, your fleck would have a `packets` subdirectory, and each
|
* See: [the Gathering guide](../gathering).
|
||||||
* decorator would be defined in its own file.
|
* @invoke MergeAsync
|
||||||
* See: https://github.com/cha0s/flecks/tree/master/packages/redux/src/packets
|
|
||||||
*
|
|
||||||
* See: https://github.com/cha0s/flecks/tree/master/packages/socket/src/packet/packet.js
|
|
||||||
* See: https://github.com/cha0s/flecks/tree/master/packages/socket/src/packet/redirect.js
|
|
||||||
*/
|
*/
|
||||||
'@flecks/socket.packets': Flecks.provide(require.context('./packets', false, /\.js$/)),
|
'@flecks/socket.packets': Flecks.provide(require.context('./packets', false, /\.js$/)),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decorate database models.
|
* Decorate socket packets.
|
||||||
|
*
|
||||||
|
* See: [the Gathering guide](../gathering).
|
||||||
*
|
*
|
||||||
* In the example below, your fleck would have a `packets/decorators` subdirectory, and each
|
|
||||||
* decorator would be defined in its own file.
|
|
||||||
* @param {constructor} Packet The packet to decorate.
|
* @param {constructor} Packet The packet to decorate.
|
||||||
|
* @invoke ComposedAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/socket.packets.decorate': (
|
'@flecks/socket.packets.decorate': (
|
||||||
Flecks.decorate(require.context('./packets/decorators', false, /\.js$/))
|
Flecks.decorate(require.context('./packets/decorators', false, /\.js$/))
|
||||||
|
@ -49,6 +48,7 @@ export const hooks = {
|
||||||
* Modify Socket.io server configuration.
|
* Modify Socket.io server configuration.
|
||||||
*
|
*
|
||||||
* See: https://socket.io/docs/v4/server-options/
|
* See: https://socket.io/docs/v4/server-options/
|
||||||
|
* @invoke MergeAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/socket.server': () => ({
|
'@flecks/socket.server': () => ({
|
||||||
pingTimeout: Infinity,
|
pingTimeout: Infinity,
|
||||||
|
@ -58,6 +58,7 @@ export const hooks = {
|
||||||
* Do something with a connecting socket.
|
* Do something with a connecting socket.
|
||||||
*
|
*
|
||||||
* @param {[ServerSocket](https://github.com/cha0s/flecks/blob/master/packages/socket/src/server/socket.js)} socket The connecting socket.
|
* @param {[ServerSocket](https://github.com/cha0s/flecks/blob/master/packages/socket/src/server/socket.js)} socket The connecting socket.
|
||||||
|
* @invoke SequentialAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/socket/server.connect': (socket) => {
|
'@flecks/socket/server.connect': (socket) => {
|
||||||
socket.on('disconnect', () => {
|
socket.on('disconnect', () => {
|
||||||
|
@ -66,10 +67,11 @@ export const hooks = {
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the Socket.IO instance.
|
* Do something with the Socket.IO instance.
|
||||||
*
|
*
|
||||||
* See: https://socket.io/docs/v4/server-instance/
|
* See: https://socket.io/docs/v4/server-instance/
|
||||||
* @param {SocketIo} io The Socket.IO server instance.
|
* @param {SocketIo} io The Socket.IO server instance.
|
||||||
|
* @invoke SequentialAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/socket/server.io': (io) => {
|
'@flecks/socket/server.io': (io) => {
|
||||||
io.engine.on("headers", (headers, req) => {
|
io.engine.on("headers", (headers, req) => {
|
||||||
|
@ -79,6 +81,7 @@ export const hooks = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define middleware to run when a socket connection is established.
|
* Define middleware to run when a socket connection is established.
|
||||||
|
* @invoke Middleware
|
||||||
*/
|
*/
|
||||||
'@flecks/socket/server.request.socket': () => (socket, next) => {
|
'@flecks/socket/server.request.socket': () => (socket, next) => {
|
||||||
// Express-style route middleware...
|
// Express-style route middleware...
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import SocketClient from './socket';
|
import SocketClient from './socket';
|
||||||
|
|
||||||
export const hooks = {
|
export const hooks = {
|
||||||
'@flecks/web/client.up': (flecks) => {
|
'@flecks/web/client.up': async (flecks) => {
|
||||||
const socket = new SocketClient(flecks);
|
const socket = new SocketClient(flecks);
|
||||||
flecks.socket.client = socket;
|
flecks.socket.client = socket;
|
||||||
socket.connect();
|
await socket.connect();
|
||||||
socket.listen();
|
socket.listen();
|
||||||
},
|
},
|
||||||
'@flecks/socket.client': ({config: {'@flecks/core': {id}}}) => ({
|
'@flecks/socket.client': ({config: {'@flecks/core': {id}}}) => ({
|
||||||
|
|
|
@ -19,7 +19,7 @@ export default class SocketClient extends decorate(Socket) {
|
||||||
this.socket = null;
|
this.socket = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(address) {
|
async connect(address) {
|
||||||
if (this.socket) {
|
if (this.socket) {
|
||||||
this.socket.destroy();
|
this.socket.destroy();
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ export default class SocketClient extends decorate(Socket) {
|
||||||
{
|
{
|
||||||
reconnectionDelay: 'production' === process.env.NODE_ENV ? 1000 : 100,
|
reconnectionDelay: 'production' === process.env.NODE_ENV ? 1000 : 100,
|
||||||
reconnectionDelayMax: 'production' === process.env.NODE_ENV ? 5000 : 500,
|
reconnectionDelayMax: 'production' === process.env.NODE_ENV ? 5000 : 500,
|
||||||
...this.flecks.invokeMerge('@flecks/socket.client'),
|
...await this.flecks.invokeMergeAsync('@flecks/socket.client'),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
this.socket.emitPromise = promisify(this.socket.emit.bind(this.socket));
|
this.socket.emitPromise = promisify(this.socket.emit.bind(this.socket));
|
||||||
|
|
|
@ -15,14 +15,6 @@ export default class SocketServer {
|
||||||
this.onConnect = this.onConnect.bind(this);
|
this.onConnect = this.onConnect.bind(this);
|
||||||
this.flecks = flecks;
|
this.flecks = flecks;
|
||||||
this.httpServer = httpServer;
|
this.httpServer = httpServer;
|
||||||
const hooks = flecks.invokeMerge('@flecks/socket.intercom');
|
|
||||||
debugSilly('intercom hooks(%O)', hooks);
|
|
||||||
this.localIntercom = async ({payload, type}, fn) => {
|
|
||||||
debugSilly('customHook: %s(%o)', type, payload);
|
|
||||||
if (hooks[type]) {
|
|
||||||
fn(await hooks[type](payload, this));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
close(fn) {
|
close(fn) {
|
||||||
|
@ -31,13 +23,32 @@ export default class SocketServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
async connect() {
|
async connect() {
|
||||||
|
const results = await this.flecks.invokeAsync('@flecks/socket.intercom');
|
||||||
|
const hooks = Object.entries(results)
|
||||||
|
.reduce(
|
||||||
|
(hooks, [fleck, endpoints]) => ({
|
||||||
|
...hooks,
|
||||||
|
...Object.fromEntries(
|
||||||
|
Object.entries(endpoints)
|
||||||
|
.map(([key, fn]) => [`${fleck}.${key}`, fn]),
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
debugSilly('intercom hooks(%O)', hooks);
|
||||||
|
this.localIntercom = async ({payload, type}, fn) => {
|
||||||
|
debugSilly('customHook: %s(%o)', type, payload);
|
||||||
|
if (hooks[type]) {
|
||||||
|
fn(await hooks[type](payload, this));
|
||||||
|
}
|
||||||
|
};
|
||||||
this.io = SocketIoServer(this.httpServer, {
|
this.io = SocketIoServer(this.httpServer, {
|
||||||
...await this.flecks.invokeMergeAsync('@flecks/socket.server'),
|
...await this.flecks.invokeMergeAsync('@flecks/socket.server'),
|
||||||
serveClient: false,
|
serveClient: false,
|
||||||
});
|
});
|
||||||
this.io.use(this.makeSocketMiddleware());
|
this.io.use(this.makeSocketMiddleware());
|
||||||
this.io.on('@flecks/socket.intercom', this.localIntercom);
|
this.io.on('@flecks/socket.intercom', this.localIntercom);
|
||||||
this.flecks.invoke('@flecks/socket/server.io', this.io);
|
await this.flecks.invokeSequentialAsync('@flecks/socket/server.io', this.io);
|
||||||
this.io.on('connect', this.onConnect);
|
this.io.on('connect', this.onConnect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
export const hooks = {
|
export const hooks = {
|
||||||
/**
|
/**
|
||||||
* Define sequential actions to run when the client comes up.
|
* Define sequential actions to run when the client comes up.
|
||||||
|
* @invoke SequentialAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/web/client.up': async () => {
|
'@flecks/web/client.up': async () => {
|
||||||
await youCanDoAsyncThingsHere();
|
await youCanDoAsyncThingsHere();
|
||||||
|
@ -8,12 +9,14 @@ export const hooks = {
|
||||||
/**
|
/**
|
||||||
* Send configuration to clients.
|
* Send configuration to clients.
|
||||||
* @param {http.ClientRequest} req The HTTP request object.
|
* @param {http.ClientRequest} req The HTTP request object.
|
||||||
|
* @invoke Async
|
||||||
*/
|
*/
|
||||||
'@flecks/web.config': (req) => ({
|
'@flecks/web.config': (req) => ({
|
||||||
someConfig: req.someConfig,
|
someConfig: req.someConfig,
|
||||||
}),
|
}),
|
||||||
/**
|
/**
|
||||||
* Define HTTP routes.
|
* Define HTTP routes.
|
||||||
|
* @invoke Async
|
||||||
*/
|
*/
|
||||||
'@flecks/web.routes': () => [
|
'@flecks/web.routes': () => [
|
||||||
{
|
{
|
||||||
|
@ -27,6 +30,7 @@ export const hooks = {
|
||||||
],
|
],
|
||||||
/**
|
/**
|
||||||
* Define middleware to run when a route is matched.
|
* Define middleware to run when a route is matched.
|
||||||
|
* @invoke Middleware
|
||||||
*/
|
*/
|
||||||
'@flecks/web/server.request.route': () => (req, res, next) => {
|
'@flecks/web/server.request.route': () => (req, res, next) => {
|
||||||
// Express-style route middleware...
|
// Express-style route middleware...
|
||||||
|
@ -34,6 +38,7 @@ export const hooks = {
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Define middleware to run when an HTTP socket connection is established.
|
* Define middleware to run when an HTTP socket connection is established.
|
||||||
|
* @invoke Middleware
|
||||||
*/
|
*/
|
||||||
'@flecks/web/server.request.socket': () => (req, res, next) => {
|
'@flecks/web/server.request.socket': () => (req, res, next) => {
|
||||||
// Express-style route middleware...
|
// Express-style route middleware...
|
||||||
|
@ -43,12 +48,14 @@ export const hooks = {
|
||||||
* Define composition functions to run over the HTML stream prepared for the client.
|
* Define composition functions to run over the HTML stream prepared for the client.
|
||||||
* @param {stream.Readable} stream The HTML stream.
|
* @param {stream.Readable} stream The HTML stream.
|
||||||
* @param {http.ClientRequest} req The HTTP request object.
|
* @param {http.ClientRequest} req The HTTP request object.
|
||||||
|
* @invoke ComposedAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/web/server.stream.html': (stream, req) => {
|
'@flecks/web/server.stream.html': (stream, req) => {
|
||||||
return stream.pipe(myTransformStream);
|
return stream.pipe(myTransformStream);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Define sequential actions to run when the HTTP server comes up.
|
* Define sequential actions to run when the HTTP server comes up.
|
||||||
|
* @invoke SequentialAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/web/server.up': async () => {
|
'@flecks/web/server.up': async () => {
|
||||||
await youCanDoAsyncThingsHere();
|
await youCanDoAsyncThingsHere();
|
||||||
|
|
|
@ -6,7 +6,6 @@ import {D} from '@flecks/core';
|
||||||
import compression from 'compression';
|
import compression from 'compression';
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import httpProxy from 'http-proxy';
|
import httpProxy from 'http-proxy';
|
||||||
import flatten from 'lodash.flatten';
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
FLECKS_CORE_ROOT = process.cwd(),
|
FLECKS_CORE_ROOT = process.cwd(),
|
||||||
|
@ -38,7 +37,7 @@ export const createHttpServer = async (flecks) => {
|
||||||
app.use(flecks.makeMiddleware('@flecks/web/server.request.socket'));
|
app.use(flecks.makeMiddleware('@flecks/web/server.request.socket'));
|
||||||
// Routes.
|
// Routes.
|
||||||
const routeMiddleware = flecks.makeMiddleware('@flecks/web/server.request.route');
|
const routeMiddleware = flecks.makeMiddleware('@flecks/web/server.request.route');
|
||||||
const routes = flatten(flecks.invokeFlat('@flecks/web.routes'));
|
const routes = (await Promise.all(flecks.invokeFlat('@flecks/web.routes'))).flat();
|
||||||
debug('routes: %O', routes);
|
debug('routes: %O', routes);
|
||||||
routes.forEach(({method, path, middleware}) => app[method](path, routeMiddleware, middleware));
|
routes.forEach(({method, path, middleware}) => app[method](path, routeMiddleware, middleware));
|
||||||
// In development mode, create a proxy to the webpack-dev-server.
|
// In development mode, create a proxy to the webpack-dev-server.
|
||||||
|
@ -135,7 +134,7 @@ export const createHttpServer = async (flecks) => {
|
||||||
reject(error);
|
reject(error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await Promise.all(flecks.invokeFlat('@flecks/web/server.up', httpServer));
|
await flecks.invokeSequentialAsync('@flecks/web/server.up', httpServer);
|
||||||
debug('HTTP server up @ %s!', [host, port].filter((e) => !!e).join(':'));
|
debug('HTTP server up @ %s!', [host, port].filter((e) => !!e).join(':'));
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
|
|
211
website/docs/hooks.mdx
Normal file
211
website/docs/hooks.mdx
Normal file
|
@ -0,0 +1,211 @@
|
||||||
|
---
|
||||||
|
title: Hooks
|
||||||
|
description: The key to unlocking the power of flecks.
|
||||||
|
---
|
||||||
|
|
||||||
|
Hooks are how everything happens in flecks. There are many hooks and the hooks provided by flecks
|
||||||
|
are documented at the [hooks reference page](./flecks/hooks).
|
||||||
|
|
||||||
|
To define hooks (and turn your plain ol' boring JS modules into beautiful interesting flecks), you
|
||||||
|
only have to export a `hooks` object:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
export const hooks = {
|
||||||
|
'@flecks/core.starting': () => {
|
||||||
|
console.log('hello, gorgeous');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note:** All hooks recieve an extra final argument, which is the flecks instance.
|
||||||
|
|
||||||
|
## Invocation
|
||||||
|
|
||||||
|
Hooks may be invoked using different invocation methods which may affect the order of invocation
|
||||||
|
as well as the final result.
|
||||||
|
|
||||||
|
All methods accept an arbitrary number of arguments after the specified arguments.
|
||||||
|
|
||||||
|
All methods pass the `flecks` instance as the last argument.
|
||||||
|
|
||||||
|
<style>{`
|
||||||
|
h3 > code:before {
|
||||||
|
content: 'flecks.';
|
||||||
|
}
|
||||||
|
#invoke > code:after,
|
||||||
|
#invokeasync > code:after
|
||||||
|
{
|
||||||
|
content: '(hook, ...args)';
|
||||||
|
}
|
||||||
|
#invokeflat > code:after {
|
||||||
|
content: '(hook, ...args)';
|
||||||
|
}
|
||||||
|
#invoke,
|
||||||
|
#invokecomposed,
|
||||||
|
#invokemerge,
|
||||||
|
#invokemergeunique,
|
||||||
|
#invokereduce,
|
||||||
|
#invokesequential
|
||||||
|
{
|
||||||
|
margin-bottom: calc( var(--ifm-heading-vertical-rhythm-bottom) * var(--ifm-leading) / 2 )
|
||||||
|
}
|
||||||
|
#invokeasync,
|
||||||
|
#invokecomposedasync,
|
||||||
|
#invokemergeasync,
|
||||||
|
#invokemergeuniqueasync,
|
||||||
|
#invokereduceasync,
|
||||||
|
#invokesequentialasync
|
||||||
|
{
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
#invokecomposed > code:after,
|
||||||
|
#invokecomposedasync > code:after
|
||||||
|
{
|
||||||
|
content: '(hook, initial, ...args)';
|
||||||
|
}
|
||||||
|
#invokefleck > code:after {
|
||||||
|
content: '(hook, fleck, ...args)';
|
||||||
|
}
|
||||||
|
#invokemerge > code:after,
|
||||||
|
#invokemergeasync > code:after,
|
||||||
|
#invokemergeunique > code:after,
|
||||||
|
#invokemergeuniqueasync > code:after
|
||||||
|
{
|
||||||
|
content: '(hook, ...args)';
|
||||||
|
}
|
||||||
|
#invokereduce > code:after,
|
||||||
|
#invokereduceasync > code:after
|
||||||
|
{
|
||||||
|
content: '(hook, reducer, initial, ...args)';
|
||||||
|
}
|
||||||
|
#invokesequential > code:after,
|
||||||
|
#invokesequentialasync > code:after
|
||||||
|
{
|
||||||
|
content: '(hook, ...args)';
|
||||||
|
}
|
||||||
|
#invokemiddleware > code:after
|
||||||
|
{
|
||||||
|
content: '(hook, ...args)';
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
|
||||||
|
### `invoke`
|
||||||
|
### `invokeAsync`
|
||||||
|
|
||||||
|
Invokes all hook implementations and returns the results keyed by the implementing flecks' paths.
|
||||||
|
|
||||||
|
#### `hook: string`
|
||||||
|
|
||||||
|
The hook to invoke.
|
||||||
|
|
||||||
|
### `invokeFleck`
|
||||||
|
|
||||||
|
Invoke a single fleck's hook implementation and return the result.
|
||||||
|
|
||||||
|
#### `hook: string`
|
||||||
|
|
||||||
|
The hook to invoke.
|
||||||
|
|
||||||
|
#### `fleck: string`
|
||||||
|
|
||||||
|
The fleck whose hook to invoke.
|
||||||
|
|
||||||
|
### `invokeFlat`
|
||||||
|
|
||||||
|
Invokes all hook implementations and returns the results as an array.
|
||||||
|
|
||||||
|
#### `hook: string`
|
||||||
|
|
||||||
|
The hook to invoke.
|
||||||
|
|
||||||
|
:::tip[Just a spoonful of sugar]
|
||||||
|
|
||||||
|
The following test would pass:
|
||||||
|
|
||||||
|
```js
|
||||||
|
expect(flecks.invokeFlat('some-hook'))
|
||||||
|
.to.deep.equal(Object.values(flecks.invoke('some-hook')));
|
||||||
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
### `invokeComposed`
|
||||||
|
### `invokeComposedAsync`
|
||||||
|
|
||||||
|
See: [function composition](https://www.educative.io/edpresso/function-composition-in-javascript).
|
||||||
|
|
||||||
|
`initial` is passed to the first implementation, which returns a result which is passed to the
|
||||||
|
second implementation, which returns a result which is passed to the third implementation, etc.
|
||||||
|
|
||||||
|
#### `hook: string`
|
||||||
|
|
||||||
|
The hook to invoke.
|
||||||
|
|
||||||
|
#### `initial: any`
|
||||||
|
|
||||||
|
The initial value.
|
||||||
|
|
||||||
|
Composed hooks are [orderable](./ordering).
|
||||||
|
|
||||||
|
### `invokeMerge`
|
||||||
|
### `invokeMergeAsync`
|
||||||
|
|
||||||
|
Invokes all hook implementations and returns the result of merging all implementations' returned objects together.
|
||||||
|
|
||||||
|
#### `hook: string`
|
||||||
|
|
||||||
|
The hook to invoke.
|
||||||
|
|
||||||
|
### `invokeMergeUnique`
|
||||||
|
### `invokeMergeUniqueAsync`
|
||||||
|
|
||||||
|
Specialization of `invokeMerge` that will throw an error if any keys overlap.
|
||||||
|
|
||||||
|
#### `hook: string`
|
||||||
|
|
||||||
|
The hook to invoke.
|
||||||
|
|
||||||
|
### `invokeReduce`
|
||||||
|
### `invokeReduceAsync`
|
||||||
|
|
||||||
|
See: [Array.prototype.reduce()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce)
|
||||||
|
|
||||||
|
Invokes hook implementations one at a time, their results being passed to the reducer as `currentValue`. Returns the final reduction.
|
||||||
|
|
||||||
|
#### `hook: string`
|
||||||
|
|
||||||
|
The hook to invoke.
|
||||||
|
|
||||||
|
#### `reduce: function`
|
||||||
|
|
||||||
|
The reducer function.
|
||||||
|
|
||||||
|
#### `initial: any`
|
||||||
|
|
||||||
|
The initial value.
|
||||||
|
|
||||||
|
### `invokeSequential`
|
||||||
|
### `invokeSequentialAsync`
|
||||||
|
|
||||||
|
Invokes all hook implementations, one after another. In the async variant, each implementation's result is `await`ed before invoking the next implementation.
|
||||||
|
|
||||||
|
#### `hook: string`
|
||||||
|
|
||||||
|
The hook to invoke.
|
||||||
|
|
||||||
|
Sequential hooks are [orderable](./ordering.mdx).
|
||||||
|
|
||||||
|
### `makeMiddleware` {#invokemiddleware}
|
||||||
|
|
||||||
|
Hooks may be implemented in the style of Express middleware.
|
||||||
|
|
||||||
|
Each implementation will be expected to accept 0 or more arguments followed by a `next` function
|
||||||
|
which the implementation invokes when passing execution on to the next implementation.
|
||||||
|
|
||||||
|
Usage with express would look something like:
|
||||||
|
|
||||||
|
```js
|
||||||
|
app.use(flecks.makeMiddleware('@my/fleck.hook'));
|
||||||
|
```
|
||||||
|
|
||||||
|
For more information, see: http://expressjs.com/en/guide/using-middleware.html
|
|
@ -34,6 +34,7 @@ export default {
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
items: [
|
items: [
|
||||||
'testing',
|
'testing',
|
||||||
|
'hooks',
|
||||||
'gathering',
|
'gathering',
|
||||||
'ordering',
|
'ordering',
|
||||||
'isomorphism',
|
'isomorphism',
|
||||||
|
|
Loading…
Reference in New Issue
Block a user