refactor: hooks
This commit is contained in:
parent
6303681e6b
commit
aed55e9c3f
|
@ -17,7 +17,7 @@
|
|||
"test": "lerna exec 'yarn && yarn test'"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@flecks/build": "*",
|
||||
"@flecks/build": "^3.1.3",
|
||||
"lerna": "^8.0.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ module.exports = class Build extends Flecks {
|
|||
async babel() {
|
||||
return babelmerge.all([
|
||||
{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(
|
||||
{
|
||||
config: configParameter,
|
||||
|
|
|
@ -41,7 +41,7 @@ program
|
|||
const flecks = await Build.from();
|
||||
debugSilly('bootstrapped');
|
||||
// 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();
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
const {
|
||||
|
|
|
@ -33,12 +33,7 @@ if (FLECKS_CORE_SYNC_FOR_ESLINT) {
|
|||
const webpackConfigs = {
|
||||
fleck: await require(webpackConfigPath)(env, argv, flecks),
|
||||
};
|
||||
await Promise.all(
|
||||
flecks.invokeFlat('@flecks/build.config', 'fleck', webpackConfigs.fleck, env, argv),
|
||||
);
|
||||
await Promise.all(
|
||||
flecks.invokeFlat('@flecks/build.config.alter', webpackConfigs, env, argv),
|
||||
);
|
||||
await flecks.configureBuilds(webpackConfigs, env, argv);
|
||||
const {resolve} = webpackConfigs.fleck;
|
||||
eslintConfig.settings['import/resolver'].webpack = {config: {resolve}};
|
||||
// Write it out to stdout.
|
||||
|
|
|
@ -7,6 +7,7 @@ export const hooks = {
|
|||
* @param {Object} env The webpack environment.
|
||||
* @param {Object} argv The webpack commandline arguments.
|
||||
* @see {@link https://webpack.js.org/configuration/configuration-types/#exporting-a-function}
|
||||
* @invoke SequentialAsync
|
||||
*/
|
||||
'@flecks/build.config': (target, config, env, argv) => {
|
||||
if ('something' === target) {
|
||||
|
@ -22,6 +23,7 @@ export const hooks = {
|
|||
* @param {Object} env The webpack environment.
|
||||
* @param {Object} argv The webpack commandline arguments.
|
||||
* @see {@link https://webpack.js.org/configuration/configuration-types/#exporting-a-function}
|
||||
* @invoke SequentialAsync
|
||||
*/
|
||||
'@flecks/build.config.alter': (configs) => {
|
||||
// Maybe we want to do something if a target exists..?
|
||||
|
@ -32,11 +34,13 @@ export const hooks = {
|
|||
|
||||
/**
|
||||
* Add implicitly resolved extensions.
|
||||
* @invoke Flat
|
||||
*/
|
||||
'@flecks/build.extensions': () => ['.coffee'],
|
||||
|
||||
/**
|
||||
* Register build files. See [the build files page](./build-files) for more details.
|
||||
* @invoke
|
||||
*/
|
||||
'@flecks/build.files': () => [
|
||||
/**
|
||||
|
@ -48,6 +52,7 @@ export const hooks = {
|
|||
/**
|
||||
* 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.
|
||||
* @invoke MergeUniqueAsync
|
||||
*/
|
||||
'@flecks/build.commands': (program, flecks) => {
|
||||
return {
|
||||
|
@ -74,6 +79,7 @@ export const hooks = {
|
|||
* @param {string} target The build target.
|
||||
* @param {Record<string, Source>} assets The assets.
|
||||
* @param {[Compilation](https://webpack.js.org/api/compilation-object/)} compilation The webpack compilation.
|
||||
* @invoke SequentialAsync
|
||||
*/
|
||||
'@flecks/build.processAssets': (target, assets, compilation) => {
|
||||
if (this.myTargets.includes(target)) {
|
||||
|
@ -83,12 +89,14 @@ export const hooks = {
|
|||
|
||||
/**
|
||||
* Define build targets.
|
||||
* @invoke
|
||||
*/
|
||||
'@flecks/build.targets': () => ['sometarget'],
|
||||
|
||||
/**
|
||||
* Alter defined build targets.
|
||||
* @param {Set<string>} targets The targets to build.
|
||||
* @invoke
|
||||
*/
|
||||
'@flecks/build.targets.alter': (targets) => {
|
||||
targets.delete('some-target');
|
||||
|
|
|
@ -28,9 +28,12 @@ module.exports = async (env, argv) => {
|
|||
debug('no build configuration found! aborting...');
|
||||
await new Promise(() => {});
|
||||
}
|
||||
const entries = await Promise.all(building.map(
|
||||
const webpackConfigs = Object.fromEntries(
|
||||
await Promise.all(building.map(
|
||||
async ([fleck, target]) => {
|
||||
const configFn = require(await flecks.resolveBuildConfig(`${target}.webpack.config.js`, fleck));
|
||||
const configFn = require(
|
||||
await flecks.resolveBuildConfig(`${target}.webpack.config.js`, fleck),
|
||||
);
|
||||
if ('function' !== typeof configFn) {
|
||||
debug(`'${
|
||||
target
|
||||
|
@ -41,14 +44,9 @@ module.exports = async (env, argv) => {
|
|||
}
|
||||
return [target, await configFn(env, argv, flecks)];
|
||||
},
|
||||
));
|
||||
await Promise.all(
|
||||
entries.map(async ([target, config]) => (
|
||||
Promise.all(flecks.invokeFlat('@flecks/build.config', target, config, env, argv))
|
||||
)),
|
||||
);
|
||||
const webpackConfigs = Object.fromEntries(entries);
|
||||
await Promise.all(flecks.invokeFlat('@flecks/build.config.alter', webpackConfigs, env, argv));
|
||||
await flecks.configureBuilds(webpackConfigs, env, argv);
|
||||
const enterableWebpackConfigs = Object.values(webpackConfigs)
|
||||
.filter((webpackConfig) => {
|
||||
if (!webpackConfig.entry) {
|
||||
|
|
|
@ -2,6 +2,7 @@ export const hooks = {
|
|||
|
||||
/**
|
||||
* Babel configuration.
|
||||
* @invoke SequentialAsync
|
||||
*/
|
||||
'@flecks/core.babel': () => ({
|
||||
plugins: ['...'],
|
||||
|
@ -9,6 +10,7 @@ export const hooks = {
|
|||
|
||||
/**
|
||||
* Define configuration. See [the configuration page](./config) for more details.
|
||||
* @invoke Fleck
|
||||
*/
|
||||
'@flecks/core.config': () => ({
|
||||
whatever: 'configuration',
|
||||
|
@ -24,6 +26,7 @@ export const hooks = {
|
|||
* Let flecks gather for you.
|
||||
*
|
||||
* See [the Gathering guide](../gathering).
|
||||
* @invoke Async
|
||||
*/
|
||||
'@flecks/core.gathered': () => ({
|
||||
// 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
|
||||
* @param {string} path The path of the fleck
|
||||
* @param {Module} updatedFleck The updated fleck module.
|
||||
* @invoke
|
||||
*/
|
||||
'@flecks/core.hmr': (path, updatedFleck) => {
|
||||
if ('my-fleck' === path) {
|
||||
|
@ -52,6 +56,7 @@ export const hooks = {
|
|||
* Invoked when a gathered set is HMR'd.
|
||||
* @param {constructor} gathered The gathered set.
|
||||
* @param {string} hook The gather hook; e.g. `@flecks/db.models`.
|
||||
* @invoke
|
||||
*/
|
||||
'@flecks/core.hmr.gathered': (gathered, hook) => {
|
||||
// Do something with the gathered set...
|
||||
|
@ -61,6 +66,7 @@ export const hooks = {
|
|||
* Invoked when a gathered class is HMR'd.
|
||||
* @param {constructor} Class The class.
|
||||
* @param {string} hook The gather hook; e.g. `@flecks/db.models`.
|
||||
* @invoke
|
||||
*/
|
||||
'@flecks/core.hmr.gathered.class': (Class, hook) => {
|
||||
// Do something with Class...
|
||||
|
@ -70,6 +76,7 @@ export const hooks = {
|
|||
* Invoked when flecks is building a fleck dependency graph.
|
||||
* @param {Digraph} graph The dependency graph.
|
||||
* @param {string} hook The hook; e.g. `@flecks/server.up`.
|
||||
* @invoke
|
||||
*/
|
||||
'@flecks/core.priority': (graph, hook) => {
|
||||
// Make `@flecks/socket/server`'s `@flecks/server.up` implementation depend on
|
||||
|
@ -85,6 +92,7 @@ export const hooks = {
|
|||
* Invoked when a fleck is registered.
|
||||
* @param {string} fleck
|
||||
* @param {Module} M
|
||||
* @invoke
|
||||
*/
|
||||
'@flecks/core.registered': (fleck, M) => {
|
||||
if ('@something/or-other' === fleck) {
|
||||
|
@ -94,6 +102,7 @@ export const hooks = {
|
|||
|
||||
/**
|
||||
* Invoked when the application is starting.
|
||||
* @invoke SequentialAsync
|
||||
*/
|
||||
'@flecks/core.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.
|
||||
check(raw, hook);
|
||||
// Decorate and check.
|
||||
const decorated = this.invokeComposed(`${hook}.decorate`, raw);
|
||||
const decorated = await this.invokeComposedAsync(`${hook}.decorate`, raw);
|
||||
check(decorated, `${hook}.decorate`);
|
||||
return decorated;
|
||||
}
|
||||
|
@ -383,7 +383,7 @@ exports.Flecks = class Flecks {
|
|||
* @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}.
|
||||
*/
|
||||
gather(
|
||||
async gather(
|
||||
hook,
|
||||
{
|
||||
idProperty = 'id',
|
||||
|
@ -395,8 +395,8 @@ exports.Flecks = class Flecks {
|
|||
throw new TypeError('Flecks.gather(): Expects parameter 1 (hook) to be string');
|
||||
}
|
||||
// Gather classes and check.
|
||||
const raw = this.invokeMerge(hook);
|
||||
const decorated = this.checkAndDecorateRawGathered(hook, raw, check);
|
||||
const raw = await this.invokeMergeAsync(hook);
|
||||
const decorated = await this.checkAndDecorateRawGathered(hook, raw, check);
|
||||
// Assign unique IDs to each class and sort by type.
|
||||
let uid = 1;
|
||||
const ids = {};
|
||||
|
@ -834,8 +834,9 @@ exports.Flecks = class Flecks {
|
|||
* @param {string} fleck
|
||||
*/
|
||||
async refreshGathered(fleck) {
|
||||
await Promise.all(
|
||||
Object.entries(this.$$gathered)
|
||||
.forEach(([
|
||||
.map(async ([
|
||||
hook,
|
||||
{
|
||||
check,
|
||||
|
@ -847,16 +848,16 @@ exports.Flecks = class Flecks {
|
|||
let raw;
|
||||
// If decorating, gather all again
|
||||
if (this.fleckImplementation(fleck, `${hook}.decorate`)) {
|
||||
raw = this.invokeMergeAsync(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)) {
|
||||
raw = this.invokeFleck(hook, fleck);
|
||||
raw = await this.invokeFleck(hook, fleck);
|
||||
debugSilly('%s implements %s', fleck, hook);
|
||||
}
|
||||
if (raw) {
|
||||
const decorated = this.checkAndDecorateRawGathered(hook, raw, check);
|
||||
const decorated = await this.checkAndDecorateRawGathered(hook, raw, check);
|
||||
debug('updating gathered %s from %s...', hook, fleck);
|
||||
debugSilly('%O', decorated);
|
||||
const entries = Object.entries(decorated);
|
||||
|
@ -872,7 +873,8 @@ exports.Flecks = class Flecks {
|
|||
});
|
||||
this.invoke('@flecks/core.hmr.gathered', gathered, hook);
|
||||
}
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,7 +12,7 @@ it('can gather', async () => {
|
|||
'@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)
|
||||
.to.equal(Object.keys(Gathered[ById]).length);
|
||||
const typeKeys = Object.keys(Gathered[ByType]);
|
||||
|
|
|
@ -3,20 +3,18 @@ export const hooks = {
|
|||
/**
|
||||
* Gather database models.
|
||||
*
|
||||
* In the example below, your fleck would have a `models` subdirectory, and each model would be
|
||||
* defined in its own file.
|
||||
* See: https://github.com/cha0s/flecks/tree/master/packages/user/src/server/models
|
||||
* See: [the Gathering guide](../gathering).
|
||||
* @invoke MergeAsync
|
||||
*/
|
||||
'@flecks/db.models': Flecks.provide(require.context('./models', false, /\.js$/)),
|
||||
|
||||
/**
|
||||
* Decorate database models.
|
||||
*
|
||||
* In the example below, your fleck would have a `models/decorators` subdirectory, and each
|
||||
* decorator would be defined in its own file.
|
||||
* See: https://github.com/cha0s/flecks/tree/master/packages/user/src/local/server/models/decorators
|
||||
* See: [the Gathering guide](../gathering).
|
||||
*
|
||||
* @param {constructor} Model The model to decorate.
|
||||
* @invoke ComposedAsync
|
||||
*/
|
||||
'@flecks/db.models.decorate': (
|
||||
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
|
||||
*
|
||||
* :::
|
||||
* @invoke MergeUniqueAsync
|
||||
*/
|
||||
'@flecks/docker.containers': () => ({
|
||||
someContainer: {
|
||||
|
@ -29,6 +30,7 @@ export const hooks = {
|
|||
* @param {string} dockerfile The content of the Dockerfile.
|
||||
*
|
||||
* @returns The new content of the Dockerfile.
|
||||
* @invoke ComposedAsync
|
||||
*/
|
||||
'@flecks/docker.Dockerfile': (dockerfile) => (
|
||||
dockerfile.replace('DEBUG=*', 'DEBUG=*,-*:silly')
|
||||
|
@ -37,6 +39,7 @@ export const hooks = {
|
|||
/**
|
||||
*
|
||||
* @param {Object} config The object representing the docker compose configuration.
|
||||
* @invoke SequentialAsync
|
||||
*/
|
||||
'@flecks/docker.docker-compose.yml': (config) => {
|
||||
config.version = '3.1';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
exports.generateDockerFile = (flecks) => {
|
||||
exports.generateDockerFile = async (flecks) => {
|
||||
const dockerfile = [
|
||||
'FROM node:20',
|
||||
'',
|
||||
|
@ -16,7 +16,7 @@ exports.generateDockerFile = (flecks) => {
|
|||
'VOLUME /var/www/node_modules',
|
||||
'',
|
||||
].join('\n');
|
||||
return flecks.invokeComposed('@flecks/docker.Dockerfile', dockerfile);
|
||||
return flecks.invokeComposedAsync('@flecks/docker.Dockerfile', dockerfile);
|
||||
};
|
||||
|
||||
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(
|
||||
Object.entries(containers)
|
||||
|
@ -69,6 +69,6 @@ exports.generateComposeConfig = async (flecks) => {
|
|||
});
|
||||
});
|
||||
const config = {version: '3', services};
|
||||
flecks.invoke('@flecks/docker.docker-compose.yml', config);
|
||||
await flecks.invokeSequentialAsync('@flecks/docker.docker-compose.yml', config);
|
||||
return config;
|
||||
};
|
||||
|
|
|
@ -12,7 +12,7 @@ module.exports = class FlecksDockerOutput {
|
|||
apply(compiler) {
|
||||
compiler.hooks.compilation.tap('FlecksDockerOutput', (compilation) => {
|
||||
compilation.hooks.additionalAssets.tapAsync('FlecksDockerOutput', async (callback) => {
|
||||
const dockerFile = generateDockerFile(this.options.flecks);
|
||||
const dockerFile = await generateDockerFile(this.options.flecks);
|
||||
compilation.assets.Dockerfile = {
|
||||
source: () => dockerFile,
|
||||
size: () => dockerFile.length,
|
||||
|
|
|
@ -5,7 +5,7 @@ export const hooks = {
|
|||
if (!flecks.get('@flecks/docker.enabled')) {
|
||||
return;
|
||||
}
|
||||
const containers = await flecks.invokeMergeAsync('@flecks/docker.containers');
|
||||
const containers = await flecks.invokeMergeUniqueAsync('@flecks/docker.containers');
|
||||
await Promise.all(
|
||||
Object.entries(containers)
|
||||
.map(([key, config]) => startContainer(flecks, key, config)),
|
||||
|
|
|
@ -104,11 +104,22 @@ exports.generateDocusaurusHookPage = (hooks) => {
|
|||
Object.entries(hooks)
|
||||
.sort(([lhook], [rhook]) => (lhook < rhook ? -1 : 1))
|
||||
.forEach(([hook, {implementations = [], invocations = [], specification}]) => {
|
||||
const {description, example, params} = specification || {
|
||||
const {
|
||||
description,
|
||||
example,
|
||||
invoke,
|
||||
params,
|
||||
} = specification || {
|
||||
params: [],
|
||||
};
|
||||
source.push(`## \`${hook}\``);
|
||||
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) {
|
||||
source.push(...description.split('\n'));
|
||||
source.push('');
|
||||
|
@ -151,6 +162,7 @@ exports.generateDocusaurusHookPage = (hooks) => {
|
|||
source.push('</div>');
|
||||
}
|
||||
source.push('</div>');
|
||||
source.push('\n');
|
||||
}
|
||||
});
|
||||
return source.join('\n');
|
||||
|
@ -310,18 +322,9 @@ exports.generateJson = async function generate(flecks) {
|
|||
type,
|
||||
});
|
||||
});
|
||||
hookSpecifications.forEach(({
|
||||
hook,
|
||||
description,
|
||||
example,
|
||||
params,
|
||||
}) => {
|
||||
hookSpecifications.forEach(({hook, ...specification}) => {
|
||||
ensureHook(hook);
|
||||
r.hooks[hook].specification = {
|
||||
description,
|
||||
example,
|
||||
params,
|
||||
};
|
||||
r.hooks[hook].specification = specification;
|
||||
});
|
||||
},
|
||||
);
|
||||
|
|
|
@ -84,17 +84,10 @@ exports.parseHookSpecificationSource = async (path, source, options) => {
|
|||
const hookSpecifications = [];
|
||||
traverse(ast, hookSpecificationVisitor((hookSpecification) => {
|
||||
const {
|
||||
description,
|
||||
hook,
|
||||
location: {start: {index: start}, end: {index: end}},
|
||||
params,
|
||||
...specification
|
||||
} = hookSpecification;
|
||||
hookSpecifications.push({
|
||||
description,
|
||||
example: source.slice(start, end),
|
||||
hook,
|
||||
params,
|
||||
});
|
||||
hookSpecifications.push({...specification, example: source.slice(start, end)});
|
||||
}));
|
||||
return {
|
||||
hookSpecifications,
|
||||
|
|
|
@ -203,6 +203,9 @@ exports.hookSpecificationVisitor = (fn) => (
|
|||
const {key, value: example} = property;
|
||||
const [{value}] = property.leadingComments;
|
||||
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
|
||||
.filter(({tag}) => 'returns' === tag)
|
||||
.map(({name, type}) => ({description: name, type}));
|
||||
|
@ -214,6 +217,7 @@ exports.hookSpecificationVisitor = (fn) => (
|
|||
.filter(({tag}) => 'param' === tag)
|
||||
.map(({description, name, type}) => ({description, name, type})),
|
||||
...returns && {returns},
|
||||
...invoke && {invoke},
|
||||
});
|
||||
}
|
||||
})
|
||||
|
|
|
@ -32,6 +32,10 @@ export default [
|
|||
{description: 'Foo', name: 'foo', type: 'string'},
|
||||
{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.
|
||||
* @param {[BrowserWindowConstructorOptions](https://www.electronjs.org/docs/latest/api/structures/browser-window-options)} browserWindowOptions The options.
|
||||
* @invoke SequentialAsync
|
||||
*/
|
||||
'@flecks/electron/server.browserWindowOptions.alter': (browserWindowOptions) => {
|
||||
browserWindowOptions.icon = 'cute-kitten.png';
|
||||
|
@ -11,6 +12,7 @@ export const hooks = {
|
|||
/**
|
||||
* Extensions to install.
|
||||
* @param {[Installer](https://github.com/MarshallOfSound/electron-devtools-installer)} installer The installer.
|
||||
* @invoke Flat
|
||||
*/
|
||||
'@flecks/electron/server.extensions': (installer) => [
|
||||
// Some defaults provided...
|
||||
|
@ -22,6 +24,7 @@ export const hooks = {
|
|||
/**
|
||||
* Invoked when electron is initializing.
|
||||
* @param {Electron} electron The electron module.
|
||||
* @invoke SequentialAsync
|
||||
*/
|
||||
'@flecks/electron/server.initialize': (electron) => {
|
||||
electron.app.on('will-quit', () => {
|
||||
|
@ -32,6 +35,7 @@ export const hooks = {
|
|||
/**
|
||||
* Invoked when a window is created
|
||||
* @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) => {
|
||||
win.maximize();
|
||||
|
|
|
@ -7,7 +7,10 @@ let win;
|
|||
async function createWindow(flecks) {
|
||||
const {BrowserWindow} = 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);
|
||||
await flecks.invokeSequentialAsync('@flecks/electron/server.window', win);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ export const hooks = {
|
|||
* Process the `package.json` for a built fleck.
|
||||
* @param {Object} json The JSON.
|
||||
* @param {[Compilation](https://webpack.js.org/api/compilation-object/)} compilation The webpack compilation.
|
||||
* @invoke SequentialAsync
|
||||
*/
|
||||
'@flecks/fleck.packageJson': (json, compilation) => {
|
||||
json.files.push('something');
|
||||
|
|
|
@ -2,6 +2,7 @@ export const hooks = {
|
|||
|
||||
/**
|
||||
* Define React components for login strategies.
|
||||
* @invoke MergeUnique
|
||||
*/
|
||||
'@flecks/passport-react.strategies': () => ({
|
||||
MyService: SomeBeautifulComponent,
|
||||
|
|
|
@ -3,6 +3,7 @@ export const hooks = {
|
|||
/**
|
||||
* Define passport login strategies. See: https://www.passportjs.org/concepts/authentication/strategies/
|
||||
* @param {Passport} passport The passport instance.
|
||||
* @invoke MergeUniqueAsync
|
||||
*/
|
||||
'@flecks/passport.strategies': (passport) => ({
|
||||
MyService: SomeStrategy,
|
||||
|
|
|
@ -41,7 +41,7 @@ export const hooks = {
|
|||
flecks.passport = {
|
||||
initialize: passport.initialize(),
|
||||
session: passport.session(),
|
||||
strategies: flecks.invokeMergeUnique('@flecks/passport.strategies', passport),
|
||||
strategies: await flecks.invokeMergeUniqueAsync('@flecks/passport.strategies', passport),
|
||||
};
|
||||
Object.entries(flecks.passport.strategies)
|
||||
.forEach(([name, strategy]) => {
|
||||
|
@ -51,7 +51,7 @@ export const hooks = {
|
|||
{before: '@flecks/web/server', after: ['@flecks/db/server', '@flecks/session/server']},
|
||||
),
|
||||
'@flecks/socket.intercom': () => ({
|
||||
'@flecks/passport.users': async (sids, server) => {
|
||||
users: async (sids, server) => {
|
||||
const sockets = await server.sockets();
|
||||
return sids
|
||||
.filter((sid) => sockets.has(sid))
|
||||
|
|
|
@ -4,6 +4,7 @@ export const hooks = {
|
|||
*
|
||||
* Note: `req` will be only be defined when server-side rendering.
|
||||
* @param {http.ClientRequest} req The HTTP request object.
|
||||
* @invoke SequentialAsync
|
||||
*/
|
||||
'@flecks/react.providers': (req) => {
|
||||
// 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
|
||||
* is the props passed to the component.
|
||||
* @param {http.ClientRequest} req The HTTP request object.
|
||||
* @invoke Async
|
||||
*/
|
||||
'@flecks/react.roots': (req) => {
|
||||
// Note that we're not returning `<Component />`, but `Component`.
|
||||
|
|
|
@ -10,7 +10,7 @@ const debug = D('@flecks/react/root');
|
|||
const debugSilly = debug.extend('silly');
|
||||
|
||||
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);
|
||||
const Providers = await flecks.invokeSequentialAsync('@flecks/react.providers', req);
|
||||
const FlattenedProviders = [];
|
||||
|
|
|
@ -4,7 +4,7 @@ export default async (flecks, opts = {}) => {
|
|||
const {
|
||||
host,
|
||||
port,
|
||||
} = flecks.get('@flecks/redis/server');
|
||||
} = flecks.get('@flecks/redis');
|
||||
const client = createClient({
|
||||
url: `redis://${host}:${port}`,
|
||||
...opts,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
export const hooks = {
|
||||
/**
|
||||
* Define side-effects to run against Redux actions.
|
||||
* @invoke SequentialAsync
|
||||
*/
|
||||
'@flecks/redux.effects': () => ({
|
||||
someActionName: (store, action) => {
|
||||
|
@ -9,6 +10,7 @@ export const hooks = {
|
|||
}),
|
||||
/**
|
||||
* Define root-level reducers for the Redux store.
|
||||
* @invoke SequentialAsync
|
||||
*/
|
||||
'@flecks/redux.reducers': () => {
|
||||
return (state, action) => {
|
||||
|
@ -20,6 +22,7 @@ export const hooks = {
|
|||
* Define Redux slices.
|
||||
*
|
||||
* See: https://redux-toolkit.js.org/api/createSlice
|
||||
* @invoke MergeUniqueAsync
|
||||
*/
|
||||
'@flecks/redux.slices': () => {
|
||||
const something = createSlice(
|
||||
|
@ -32,6 +35,7 @@ export const hooks = {
|
|||
/**
|
||||
* Modify Redux store configuration.
|
||||
* @param {Object} options A mutable object with keys for enhancers and middleware.
|
||||
* @invoke SequentialAsync
|
||||
*/
|
||||
'@flecks/redux.store': (options) => {
|
||||
options.enhancers.splice(someIndex, 1);
|
||||
|
|
|
@ -6,8 +6,8 @@ import localStorageEnhancer from './local-storage';
|
|||
export const hooks = {
|
||||
'@flecks/web/client.up': Flecks.priority(
|
||||
async (flecks) => {
|
||||
const slices = await flecks.invokeMergeUnique('@flecks/redux.slices');
|
||||
const reducer = createReducer(flecks, slices);
|
||||
const slices = await flecks.invokeMergeUniqueAsync('@flecks/redux.slices');
|
||||
const reducer = await createReducer(flecks, slices);
|
||||
// Hydrate from server.
|
||||
const {preloadedState} = flecks.get('@flecks/redux');
|
||||
const store = await configureStore(flecks, reducer, {preloadedState});
|
||||
|
|
|
@ -10,8 +10,8 @@ const debugSilly = debug.extend('silly');
|
|||
export const hooks = {
|
||||
'@flecks/electron/server.extensions': (installer) => [installer.REDUX_DEVTOOLS],
|
||||
'@flecks/web/server.request.route': (flecks) => async (req, res, next) => {
|
||||
const slices = await flecks.invokeMergeUnique('@flecks/redux.slices');
|
||||
const reducer = createReducer(flecks, slices);
|
||||
const slices = await flecks.invokeMergeUniqueAsync('@flecks/redux.slices');
|
||||
const reducer = await createReducer(flecks, slices);
|
||||
// Let the slices have a(n async) chance to hydrate with server data.
|
||||
await Promise.all(
|
||||
Object.values(slices).map(({hydrateServer}) => hydrateServer?.(req, flecks)),
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import {combineReducers} from '@reduxjs/toolkit';
|
||||
import reduceReducers from 'reduce-reducers';
|
||||
|
||||
export default (flecks, slices) => {
|
||||
let reducers = flecks.invokeFlat('@flecks/redux.reducers');
|
||||
export default async (flecks, slices) => {
|
||||
let reducers = await flecks.invokeSequentialAsync('@flecks/redux.reducers');
|
||||
if (Object.keys(slices).length > 0) {
|
||||
reducers = reducers.concat(combineReducers(slices));
|
||||
}
|
||||
|
|
|
@ -11,10 +11,10 @@ export default async function configureStore(flecks, reducer, {preloadedState})
|
|||
],
|
||||
middleware: [
|
||||
'@flecks/redux/defaultMiddleware',
|
||||
effectsMiddleware(flecks),
|
||||
await effectsMiddleware(flecks),
|
||||
],
|
||||
};
|
||||
flecks.invokeFlat('@flecks/redux.store', options);
|
||||
await flecks.invokeSequentialAsync('@flecks/redux.store', options);
|
||||
return configureStoreR({
|
||||
enhancers: (defaultEnhancers) => {
|
||||
const index = options.enhancers.indexOf('@flecks/redux/defaultEnhancers');
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export default (flecks) => {
|
||||
const effects = flecks.invokeFlat('@flecks/redux.effects');
|
||||
export default async (flecks) => {
|
||||
const effects = await flecks.invokeSequentialAsync('@flecks/redux.effects');
|
||||
const effect = (store, action) => {
|
||||
effects.forEach((map) => {
|
||||
if (map[action.type]) {
|
||||
|
|
|
@ -3,6 +3,7 @@ export const hooks = {
|
|||
* Define REPL commands.
|
||||
*
|
||||
* Note: commands will be prefixed with a period in the Node REPL.
|
||||
* @invoke MergeUniqueAsync
|
||||
*/
|
||||
'@flecks/repl.commands': () => ({
|
||||
someCommand: (...args) => {
|
||||
|
@ -13,6 +14,7 @@ export const hooks = {
|
|||
}),
|
||||
/**
|
||||
* Provide global context to the REPL.
|
||||
* @invoke MergeUniqueAsync
|
||||
*/
|
||||
'@flecks/repl.context': () => {
|
||||
// Now you'd be able to do like:
|
||||
|
|
|
@ -11,16 +11,17 @@ const debugSilly = debug.extend('silly');
|
|||
|
||||
export async function createReplServer(flecks) {
|
||||
const {id} = flecks.get('@flecks/core');
|
||||
const context = (await Promise.all(flecks.invokeFlat('@flecks/repl.context')))
|
||||
.reduce((r, vars) => ({...r, ...vars}), {flecks});
|
||||
const context = {
|
||||
...await flecks.invokeMergeUniqueAsync('@flecks/repl.context'),
|
||||
flecks,
|
||||
};
|
||||
debug(
|
||||
'Object.keys(context) === %O',
|
||||
Object.keys(context),
|
||||
);
|
||||
const commands = {};
|
||||
Object.entries(
|
||||
flecks.invokeFlat('@flecks/repl.commands').reduce((r, commands) => ({...r, ...commands}), {}),
|
||||
).forEach(([key, value]) => {
|
||||
Object.entries(await flecks.invokeMergeUniqueAsync('@flecks/repl.commands'))
|
||||
.forEach(([key, value]) => {
|
||||
commands[key] = value;
|
||||
debugSilly('registered command: %s', key);
|
||||
});
|
||||
|
|
|
@ -2,6 +2,7 @@ export const hooks = {
|
|||
|
||||
/**
|
||||
* Pass information to the runtime.
|
||||
* @invoke Async
|
||||
*/
|
||||
'@flecks/server.runtime': async () => ({
|
||||
something: '...',
|
||||
|
@ -9,6 +10,7 @@ export const hooks = {
|
|||
|
||||
/**
|
||||
* Define sequential actions to run when the server comes up.
|
||||
* @invoke SequentialAsync
|
||||
*/
|
||||
'@flecks/server.up': async () => {
|
||||
await youCanDoAsyncThingsHere();
|
||||
|
|
|
@ -2,16 +2,10 @@ export const hooks = {
|
|||
|
||||
/**
|
||||
* Configure the session. See: https://github.com/expressjs/session#sessionoptions
|
||||
* @invoke MergeAsync
|
||||
*/
|
||||
'@flecks/session.config': async () => ({
|
||||
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.
|
||||
*
|
||||
* See: https://socket.io/docs/v4/client-options/
|
||||
* @invoke MergeAsync
|
||||
*/
|
||||
'@flecks/socket.client': () => ({
|
||||
timeout: Infinity,
|
||||
|
@ -10,36 +11,34 @@ export const hooks = {
|
|||
|
||||
/**
|
||||
* Define server-side intercom channels.
|
||||
* @invoke Async
|
||||
*/
|
||||
'@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)`.
|
||||
// `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
|
||||
// against that server instance.
|
||||
'@my/fleck.key': async (payload, server) => {
|
||||
key: async (payload, server) => {
|
||||
return someServiceSpecificInformation();
|
||||
},
|
||||
}),
|
||||
|
||||
/**
|
||||
* Define socket packets.
|
||||
* Gather socket packets.
|
||||
*
|
||||
* In the example below, your fleck would have a `packets` subdirectory, and each
|
||||
* decorator would be defined in its own file.
|
||||
* 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
|
||||
* See: [the Gathering guide](../gathering).
|
||||
* @invoke MergeAsync
|
||||
*/
|
||||
'@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.
|
||||
* @invoke ComposedAsync
|
||||
*/
|
||||
'@flecks/socket.packets.decorate': (
|
||||
Flecks.decorate(require.context('./packets/decorators', false, /\.js$/))
|
||||
|
@ -49,6 +48,7 @@ export const hooks = {
|
|||
* Modify Socket.io server configuration.
|
||||
*
|
||||
* See: https://socket.io/docs/v4/server-options/
|
||||
* @invoke MergeAsync
|
||||
*/
|
||||
'@flecks/socket.server': () => ({
|
||||
pingTimeout: Infinity,
|
||||
|
@ -58,6 +58,7 @@ export const hooks = {
|
|||
* 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.
|
||||
* @invoke SequentialAsync
|
||||
*/
|
||||
'@flecks/socket/server.connect': (socket) => {
|
||||
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/
|
||||
* @param {SocketIo} io The Socket.IO server instance.
|
||||
* @invoke SequentialAsync
|
||||
*/
|
||||
'@flecks/socket/server.io': (io) => {
|
||||
io.engine.on("headers", (headers, req) => {
|
||||
|
@ -79,6 +81,7 @@ export const hooks = {
|
|||
|
||||
/**
|
||||
* Define middleware to run when a socket connection is established.
|
||||
* @invoke Middleware
|
||||
*/
|
||||
'@flecks/socket/server.request.socket': () => (socket, next) => {
|
||||
// Express-style route middleware...
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import SocketClient from './socket';
|
||||
|
||||
export const hooks = {
|
||||
'@flecks/web/client.up': (flecks) => {
|
||||
'@flecks/web/client.up': async (flecks) => {
|
||||
const socket = new SocketClient(flecks);
|
||||
flecks.socket.client = socket;
|
||||
socket.connect();
|
||||
await socket.connect();
|
||||
socket.listen();
|
||||
},
|
||||
'@flecks/socket.client': ({config: {'@flecks/core': {id}}}) => ({
|
||||
|
|
|
@ -19,7 +19,7 @@ export default class SocketClient extends decorate(Socket) {
|
|||
this.socket = null;
|
||||
}
|
||||
|
||||
connect(address) {
|
||||
async connect(address) {
|
||||
if (this.socket) {
|
||||
this.socket.destroy();
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ export default class SocketClient extends decorate(Socket) {
|
|||
{
|
||||
reconnectionDelay: 'production' === process.env.NODE_ENV ? 1000 : 100,
|
||||
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));
|
||||
|
|
|
@ -15,14 +15,6 @@ export default class SocketServer {
|
|||
this.onConnect = this.onConnect.bind(this);
|
||||
this.flecks = flecks;
|
||||
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) {
|
||||
|
@ -31,13 +23,32 @@ export default class SocketServer {
|
|||
}
|
||||
|
||||
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, {
|
||||
...await this.flecks.invokeMergeAsync('@flecks/socket.server'),
|
||||
serveClient: false,
|
||||
});
|
||||
this.io.use(this.makeSocketMiddleware());
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
export const hooks = {
|
||||
/**
|
||||
* Define sequential actions to run when the client comes up.
|
||||
* @invoke SequentialAsync
|
||||
*/
|
||||
'@flecks/web/client.up': async () => {
|
||||
await youCanDoAsyncThingsHere();
|
||||
|
@ -8,12 +9,14 @@ export const hooks = {
|
|||
/**
|
||||
* Send configuration to clients.
|
||||
* @param {http.ClientRequest} req The HTTP request object.
|
||||
* @invoke Async
|
||||
*/
|
||||
'@flecks/web.config': (req) => ({
|
||||
someConfig: req.someConfig,
|
||||
}),
|
||||
/**
|
||||
* Define HTTP routes.
|
||||
* @invoke Async
|
||||
*/
|
||||
'@flecks/web.routes': () => [
|
||||
{
|
||||
|
@ -27,6 +30,7 @@ export const hooks = {
|
|||
],
|
||||
/**
|
||||
* Define middleware to run when a route is matched.
|
||||
* @invoke Middleware
|
||||
*/
|
||||
'@flecks/web/server.request.route': () => (req, res, next) => {
|
||||
// Express-style route middleware...
|
||||
|
@ -34,6 +38,7 @@ export const hooks = {
|
|||
},
|
||||
/**
|
||||
* Define middleware to run when an HTTP socket connection is established.
|
||||
* @invoke Middleware
|
||||
*/
|
||||
'@flecks/web/server.request.socket': () => (req, res, next) => {
|
||||
// Express-style route middleware...
|
||||
|
@ -43,12 +48,14 @@ export const hooks = {
|
|||
* Define composition functions to run over the HTML stream prepared for the client.
|
||||
* @param {stream.Readable} stream The HTML stream.
|
||||
* @param {http.ClientRequest} req The HTTP request object.
|
||||
* @invoke ComposedAsync
|
||||
*/
|
||||
'@flecks/web/server.stream.html': (stream, req) => {
|
||||
return stream.pipe(myTransformStream);
|
||||
},
|
||||
/**
|
||||
* Define sequential actions to run when the HTTP server comes up.
|
||||
* @invoke SequentialAsync
|
||||
*/
|
||||
'@flecks/web/server.up': async () => {
|
||||
await youCanDoAsyncThingsHere();
|
||||
|
|
|
@ -6,7 +6,6 @@ import {D} from '@flecks/core';
|
|||
import compression from 'compression';
|
||||
import express from 'express';
|
||||
import httpProxy from 'http-proxy';
|
||||
import flatten from 'lodash.flatten';
|
||||
|
||||
const {
|
||||
FLECKS_CORE_ROOT = process.cwd(),
|
||||
|
@ -38,7 +37,7 @@ export const createHttpServer = async (flecks) => {
|
|||
app.use(flecks.makeMiddleware('@flecks/web/server.request.socket'));
|
||||
// Routes.
|
||||
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);
|
||||
routes.forEach(({method, path, middleware}) => app[method](path, routeMiddleware, middleware));
|
||||
// In development mode, create a proxy to the webpack-dev-server.
|
||||
|
@ -135,7 +134,7 @@ export const createHttpServer = async (flecks) => {
|
|||
reject(error);
|
||||
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(':'));
|
||||
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,
|
||||
items: [
|
||||
'testing',
|
||||
'hooks',
|
||||
'gathering',
|
||||
'ordering',
|
||||
'isomorphism',
|
||||
|
|
Loading…
Reference in New Issue
Block a user