flow:
dox, hook registration, ensureUniqueReduction, middleware, ...
This commit is contained in:
parent
23f2fae001
commit
c3910ba5f0
|
@ -1,7 +1,7 @@
|
|||
<div align="center">
|
||||
<h1>flecks</h1>
|
||||
<p>
|
||||
Flecks is a dynamic, configuration-driven, fullstack application production system. Its purpose
|
||||
Flecks is an exceptionally extensible fullstack application production system. Its true purpose
|
||||
is to make application development a more joyful endeavor. Intelligent defaults combined with
|
||||
a highly dynamic structure encourage consistency while allowing you to easily express your own
|
||||
opinions.
|
||||
|
|
3
TODO.md
3
TODO.md
|
@ -22,7 +22,7 @@
|
|||
- [x] remove `invokeParallel()`
|
||||
- [x] Specialize `invokeReduce()` with `invokeMerge()`.
|
||||
- [x] Rename all hooks to dot-first notation; rewrite `lookupFlecks()`.
|
||||
- [ ] ensureUniqueReduction moved into invokeMerge
|
||||
- [x] ensureUniqueReduction moved into invokeMerge
|
||||
- [x] `bootstrap({without: ['badplatform']})` should be handled by passing `{platforms: ['!badplatform']}`
|
||||
- [ ] user redux server hydrate fails if no user in req
|
||||
- [ ] governor fails if not in server up
|
||||
|
@ -31,3 +31,4 @@
|
|||
- [ ] rename `@flecks/web` to `@flecks/web`
|
||||
- [ ] simultaneous babel compilation across all compiled flecks
|
||||
- [ ] add building to publish process ...
|
||||
- [ ] @babel/register@7.18.x has a bug
|
||||
|
|
1
packages/core/QUIRKS.md
Normal file
1
packages/core/QUIRKS.md
Normal file
|
@ -0,0 +1 @@
|
|||
- I use the variable `r` a lot when referencing a reducer's accumulator value
|
|
@ -35,8 +35,10 @@ config.use.push(({config}) => {
|
|||
}
|
||||
});
|
||||
|
||||
// Fleck build configuration.
|
||||
config.use.unshift(fleck());
|
||||
|
||||
// AirBnb linting.
|
||||
config.use.unshift(
|
||||
airbnb({
|
||||
eslint: {
|
||||
|
@ -45,13 +47,13 @@ config.use.unshift(
|
|||
}),
|
||||
);
|
||||
|
||||
// Include a shebang and set the executable bit..
|
||||
config.use.push(banner({
|
||||
banner: '#!/usr/bin/env node',
|
||||
include: /^cli\.js$/,
|
||||
pluginId: 'shebang',
|
||||
raw: true,
|
||||
}))
|
||||
|
||||
config.use.push(({config}) => {
|
||||
config
|
||||
.plugin('executable')
|
||||
|
|
|
@ -2,17 +2,13 @@
|
|||
|
||||
Hooks are how everything happens in flecks. There are many hooks and the hooks provided by flecks are documented at the [hooks reference page](https://github.com/cha0s/flecks/blob/gh-pages/hooks.md).
|
||||
|
||||
To define hooks (and turn your plain ol' boring JS modules into beautiful interesting flecks), you only have to import the `Hooks` symbol and key your default export:
|
||||
To define hooks (and turn your plain ol' boring JS modules into beautiful interesting flecks), you only have to export a `hooks` object:
|
||||
|
||||
```javascript
|
||||
import {Hooks} from '@flecks/core';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/core.starting': () => {
|
||||
console.log('hello, gorgeous');
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
|
@ -133,15 +129,15 @@ assert(foo.type === 'Foo');
|
|||
```javascript
|
||||
{
|
||||
// The property added when extending the class to return the numeric ID.
|
||||
idAttribute = 'id',
|
||||
idProperty = 'id',
|
||||
// The property added when extending the class to return the type.
|
||||
typeAttribute = 'type',
|
||||
typeProperty = 'type',
|
||||
// A function called with the `Gathered` object to allow checking validity.
|
||||
check = () => {},
|
||||
}
|
||||
```
|
||||
|
||||
As an example, when `@flecks/db/server` gathers models, `typeAttribute` is set to `name`, because Sequelize requires its model classes to have a unique `name` property.
|
||||
As an example, when `@flecks/db/server` gathers models, `typeProperty` is set to `name`, because Sequelize requires its model classes to have a unique `name` property.
|
||||
|
||||
**Note:** the numeric IDs are useful for efficient serialization between the client and server, but **if you are using this property, ensure that `flecks.gather()` is called equivalently on both the client and the server**. As a rule of thumb, if you have serializable `Gathered`s, they should be invoked and defined in `your-fleck`, and not in `your-fleck/[platform]`, so that they are invoked for every platform.
|
||||
|
||||
|
@ -152,19 +148,15 @@ Complementary to gather hooks above, `Flecks.provide()` allows you to ergonomica
|
|||
Here's an example of how you could manually provide `@flecks/db/server.models` in your own fleck:
|
||||
|
||||
```javascript
|
||||
import {Hooks} foom '@flecks/core';
|
||||
|
||||
import SomeModel from './models/some-model';
|
||||
import AnotherModel from './models/another-model';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/db/server.models': () => ({
|
||||
SomeModel,
|
||||
AnotherModel,
|
||||
}),
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
If you think about the example above, you might realize that it will become a lot of typing to keep adding new models over time. Provider hooks exist to reduce this maintenance burden for you.
|
||||
|
@ -183,12 +175,10 @@ models/
|
|||
then, this `index.js`:
|
||||
|
||||
```javascript
|
||||
import {Flecks, Hooks} from '@flecks/core';
|
||||
import {Flecks} from '@flecks/core';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/db/server.models': Flecks.provide(require.context('./models', false, /\.js$/)),
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
|
@ -212,10 +202,7 @@ is *exactly equivalent* to the gather example above. By default, `Flecks.provide
|
|||
When a Model (or any other) is gathered as above, an implicit hook is called: `${hook}.decorate`. This allows other flecks to decorate whatever has been gathered:
|
||||
|
||||
```javascript
|
||||
import {Hooks} from '@flecks/core';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/db/server.models.decorate': (Models) => {
|
||||
return {
|
||||
...Models,
|
||||
|
@ -230,13 +217,12 @@ export default {
|
|||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
#### `Flecks.decorate(context, options)`
|
||||
|
||||
As with above, there exists an API for making the maintenance of decorators more ergonomic.
|
||||
As with above, there exists an API for making the maintenance of decorators even more ergonomic.
|
||||
|
||||
Supposing our fleck is structured like so:
|
||||
|
||||
|
@ -266,12 +252,12 @@ export default (User) => {
|
|||
then, this `index.js`:
|
||||
|
||||
```javascript
|
||||
import {Flecks, Hooks} from '@flecks/core';
|
||||
import {Flecks} from '@flecks/core';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
'@flecks/db/server.models.decorate': Flecks.decorate(require.context('./models/decorators', false, /\.js$/)),
|
||||
},
|
||||
export const hooks = {
|
||||
'@flecks/db/server.models.decorate': (
|
||||
Flecks.decorate(require.context('./models/decorators', false, /\.js$/))
|
||||
),
|
||||
};
|
||||
```
|
||||
|
||||
|
@ -307,7 +293,7 @@ Our `flecks.yml` could be configured like so:
|
|||
|
||||
In this application, when `@flecks/http/server.request.route` is invoked, `@flecks/user/session`'s implementation is invoked (which reifies the user's session from cookies), followed by `my-cool-fleck`'s (which, we assume, does some kind of very cool dark mode check).
|
||||
|
||||
### Ellipses
|
||||
### Ellipses/elision
|
||||
|
||||
It may not always be ergonomic to configure the order of every single implementation, but enough to specify which implementations must run first (or last).
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
export const hooks = {
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
/**
|
||||
* Hook into neutrino configuration.
|
||||
* @param {string} target The build target; e.g. `server`.
|
||||
|
@ -54,7 +52,7 @@ export default {
|
|||
args: [
|
||||
'<somearg>',
|
||||
],
|
||||
description: 'This sure is some command',
|
||||
description: 'This command does tests and also blows up',
|
||||
options: [
|
||||
'-t, --test', 'Do a test',
|
||||
'-b, --blow-up', 'Blow up instead of running the command',
|
||||
|
@ -117,6 +115,4 @@ export default {
|
|||
config.stats = 'verbose';
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -59,6 +59,7 @@
|
|||
"babel-merge": "^3.0.0",
|
||||
"babel-plugin-prepend": "^1.0.2",
|
||||
"chai": "4.2.0",
|
||||
"chai-as-promised": "7.1.1",
|
||||
"commander": "^8.3.0",
|
||||
"debug": "4.3.1",
|
||||
"enhanced-resolve": "^5.9.2",
|
||||
|
|
|
@ -11,7 +11,7 @@ const {
|
|||
FLECKS_CORE_ROOT = process.cwd(),
|
||||
} = process.env;
|
||||
|
||||
const resolver = (source) => (path) => {
|
||||
const resolveValidModulePath = (source) => (path) => {
|
||||
// Does the file resolve as source?
|
||||
try {
|
||||
R.resolve(`${source}/${path}`);
|
||||
|
@ -39,7 +39,7 @@ module.exports = () => ({config, options}) => {
|
|||
.set(name, join(FLECKS_CORE_ROOT, 'src'));
|
||||
// Calculate entry points from `files`.
|
||||
files
|
||||
.filter(resolver(source))
|
||||
.filter(resolveValidModulePath(source))
|
||||
.forEach((file) => {
|
||||
const trimmed = join(dirname(file), basename(file, extname(file)));
|
||||
config
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
// Get a runtime require function by hook or by crook. :)
|
||||
|
||||
// eslint-disable-next-line no-eval
|
||||
module.exports = eval('"undefined" !== typeof require ? require : undefined');
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
export default async (flecks, hook, ...args) => {
|
||||
const track = {};
|
||||
return Object.entries(flecks.invoke(hook, ...args))
|
||||
.reduce(async (r, [pkg, impl]) => {
|
||||
const aimpl = await impl;
|
||||
Object.keys(aimpl).forEach((key) => {
|
||||
if (track[key]) {
|
||||
throw new ReferenceError(
|
||||
`Conflict in ${hook}: '${track[key]}' implemented '${key}', followed by '${pkg}'`,
|
||||
);
|
||||
}
|
||||
track[key] = pkg;
|
||||
});
|
||||
return {...(await r), ...aimpl};
|
||||
}, {});
|
||||
};
|
|
@ -16,24 +16,38 @@ import Middleware from './middleware';
|
|||
const debug = D('@flecks/core/flecks');
|
||||
const debugSilly = debug.extend('silly');
|
||||
|
||||
// Symbols for Gathered classes.
|
||||
export const ById = Symbol.for('@flecks/core.byId');
|
||||
export const ByType = Symbol.for('@flecks/core.byType');
|
||||
export const Hooks = Symbol.for('@flecks/core.hooks');
|
||||
|
||||
/**
|
||||
* Capitalize a string.
|
||||
*
|
||||
* @param {string} string
|
||||
* @returns {string}
|
||||
*/
|
||||
const capitalize = (string) => string.substring(0, 1).toUpperCase() + string.substring(1);
|
||||
|
||||
/**
|
||||
* CamelCase a string.
|
||||
*
|
||||
* @param {string} string
|
||||
* @returns {string}
|
||||
*/
|
||||
const camelCase = (string) => string.split(/[_-]/).map(capitalize).join('');
|
||||
|
||||
// Track gathered for HMR.
|
||||
const hotGathered = new Map();
|
||||
|
||||
const wrapperClass = (Class, id, idAttribute, type, typeAttribute) => {
|
||||
// Wrap classes to expose their flecks ID and type.
|
||||
const wrapGathered = (Class, id, idProperty, type, typeProperty) => {
|
||||
class Subclass extends Class {
|
||||
|
||||
static get [idAttribute]() {
|
||||
static get [idProperty]() {
|
||||
return id;
|
||||
}
|
||||
|
||||
static get [typeAttribute]() {
|
||||
static get [typeProperty]() {
|
||||
return type;
|
||||
}
|
||||
|
||||
|
@ -43,72 +57,121 @@ const wrapperClass = (Class, id, idAttribute, type, typeAttribute) => {
|
|||
|
||||
export default class Flecks {
|
||||
|
||||
config = {};
|
||||
|
||||
flecks = {};
|
||||
|
||||
hooks = {};
|
||||
|
||||
platforms = {};
|
||||
|
||||
/**
|
||||
* @param {object} init
|
||||
* @param {object} init.config The Flecks configuration (e.g. loaded from `flecks.yml`).
|
||||
* @param {string[]} init.platforms Platforms this instance is running on.
|
||||
*/
|
||||
constructor({
|
||||
config = {},
|
||||
flecks = {},
|
||||
platforms = [],
|
||||
} = {}) {
|
||||
this.config = {
|
||||
...Object.fromEntries(Object.keys(flecks).map((path) => [path, {}])),
|
||||
...config,
|
||||
};
|
||||
this.hooks = {};
|
||||
this.flecks = {};
|
||||
const emptyConfigForAllFlecks = Object.fromEntries(
|
||||
Object.keys(flecks).map((path) => [path, {}]),
|
||||
);
|
||||
this.config = {...emptyConfigForAllFlecks, ...config};
|
||||
this.platforms = platforms;
|
||||
const entries = Object.entries(flecks);
|
||||
debugSilly('paths: %O', entries.map(([fleck]) => fleck));
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
const [fleck, M] = entries[i];
|
||||
this.registerFleck(fleck, M);
|
||||
this.registerFleckHooks(fleck, M);
|
||||
this.invoke('@flecks/core.registered', fleck, M);
|
||||
}
|
||||
this.configureFlecks();
|
||||
this.configureFlecksDefaults();
|
||||
debugSilly('config: %O', this.config);
|
||||
}
|
||||
|
||||
configureFleck(fleck) {
|
||||
/**
|
||||
* Configure defaults for a fleck.
|
||||
*
|
||||
* @param {string} fleck
|
||||
* @protected
|
||||
*/
|
||||
configureFleckDefaults(fleck) {
|
||||
this.config[fleck] = {
|
||||
...this.invokeFleck('@flecks/core.config', fleck),
|
||||
...this.config[fleck],
|
||||
};
|
||||
}
|
||||
|
||||
configureFlecks() {
|
||||
const defaultConfig = this.invoke('@flecks/core.config');
|
||||
const flecks = Object.keys(defaultConfig);
|
||||
/**
|
||||
* Configure defaults for all flecks.
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
configureFlecksDefaults() {
|
||||
const flecks = this.flecksImplementing('@flecks/core.config');
|
||||
for (let i = 0; i < flecks.length; i++) {
|
||||
this.configureFleck(flecks[i]);
|
||||
this.configureFleckDefaults(flecks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [Dasherize]{@link https://en.wiktionary.org/wiki/dasherize} a fleck path.
|
||||
*
|
||||
* @param {string} path The path to dasherize.
|
||||
* @returns {string}
|
||||
*/
|
||||
static dasherizePath(path) {
|
||||
const parts = dirname(path).split('/');
|
||||
if ('.' === parts[0]) {
|
||||
parts.shift();
|
||||
}
|
||||
if ('index' === parts[parts.length - 1]) {
|
||||
parts.pop();
|
||||
}
|
||||
return join(parts.join('-'), basename(path, extname(path)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a decorator from a require context.
|
||||
*
|
||||
* @param {*} context @see {@link https://webpack.js.org/guides/dependency-management/#requirecontext}
|
||||
* @param {object} config
|
||||
* @param {function} [config.transformer = {@link camelCase}]
|
||||
* Function to run on each context path.
|
||||
* @returns {function} The decorator.
|
||||
*/
|
||||
static decorate(
|
||||
context,
|
||||
{
|
||||
transformer = camelCase,
|
||||
} = {},
|
||||
) {
|
||||
return (Gathered, flecks) => {
|
||||
return (Gathered, flecks) => (
|
||||
context.keys()
|
||||
.forEach((path) => {
|
||||
.reduce(
|
||||
(Gathered, path) => {
|
||||
const key = transformer(this.dasherizePath(path));
|
||||
if (!Gathered[key]) {
|
||||
return Gathered;
|
||||
}
|
||||
const {default: M} = context(path);
|
||||
if ('function' !== typeof M) {
|
||||
throw new ReferenceError(
|
||||
`Flecks.decorate(): require(${
|
||||
path
|
||||
}).default is not a function (from: ${
|
||||
context.id
|
||||
})`,
|
||||
`Flecks.decorate(): require(${path}).default is not a function (from: ${context.id})`,
|
||||
);
|
||||
}
|
||||
const key = transformer(this.symbolizePath(path));
|
||||
if (Gathered[key]) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
Gathered[key] = M(Gathered[key], flecks);
|
||||
}
|
||||
});
|
||||
return Gathered;
|
||||
};
|
||||
return {...Gathered, [key]: M(Gathered[key], flecks)};
|
||||
},
|
||||
Gathered,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy this instance.
|
||||
*/
|
||||
destroy() {
|
||||
this.config = {};
|
||||
this.hooks = {};
|
||||
|
@ -116,12 +179,20 @@ export default class Flecks {
|
|||
this.platforms = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all flecks implementing a hook, including platform-specific and elided variants.
|
||||
*
|
||||
* @param {string} hook
|
||||
* @returns {string[]} The expanded list of flecks.
|
||||
*/
|
||||
expandedFlecks(hook) {
|
||||
const flecks = this.lookupFlecks(hook);
|
||||
let expanded = [];
|
||||
for (let i = 0; i < flecks.length; ++i) {
|
||||
const fleck = flecks[i];
|
||||
// Just the fleck.
|
||||
expanded.push(fleck);
|
||||
// Platform-specific variants.
|
||||
for (let j = 0; j < this.platforms.length; ++j) {
|
||||
const platform = this.platforms[j];
|
||||
const variant = join(fleck, platform);
|
||||
|
@ -130,6 +201,7 @@ export default class Flecks {
|
|||
}
|
||||
}
|
||||
}
|
||||
// Expand elided flecks.
|
||||
const index = expanded.findIndex((fleck) => '...' === fleck);
|
||||
if (-1 !== index) {
|
||||
if (-1 !== expanded.slice(index + 1).findIndex((fleck) => '...' === fleck)) {
|
||||
|
@ -158,33 +230,66 @@ export default class Flecks {
|
|||
return expanded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the module for a fleck.
|
||||
*
|
||||
* @param {*} fleck
|
||||
*
|
||||
* @returns {*}
|
||||
*/
|
||||
fleck(fleck) {
|
||||
return this.flecks[fleck];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether a fleck implements a hook.
|
||||
*
|
||||
* @param {*} fleck
|
||||
* @param {string} hook
|
||||
* @returns {boolean}
|
||||
*/
|
||||
fleckImplements(fleck, hook) {
|
||||
return !!this.hooks[hook].find(({fleck: candidate}) => fleck === candidate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of flecks implementing a hook.
|
||||
*
|
||||
* @param {string} hook
|
||||
* @returns {string[]}
|
||||
*/
|
||||
flecksImplementing(hook) {
|
||||
return this.hooks[hook]?.map(({fleck}) => fleck) || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gather and register class types.
|
||||
*
|
||||
* @param {string} hook
|
||||
* @param {object} config
|
||||
* @param {string} [config.idProperty='id'] The property used to get/set the class ID.
|
||||
* @param {string} [config.typeProperty='type'] The property used to get/set the class type.
|
||||
* @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(
|
||||
hook,
|
||||
{
|
||||
idAttribute = 'id',
|
||||
typeAttribute = 'type',
|
||||
idProperty = 'id',
|
||||
typeProperty = 'type',
|
||||
check = () => {},
|
||||
} = {},
|
||||
) {
|
||||
if (!hook || 'string' !== typeof hook) {
|
||||
throw new TypeError('Flecks.gather(): Expects parameter 1 (hook) to be string');
|
||||
}
|
||||
// Gather classes and check.
|
||||
const raw = this.invokeMerge(hook);
|
||||
check(raw, hook);
|
||||
// Decorate and check.
|
||||
const decorated = this.invokeComposed(`${hook}.decorate`, raw);
|
||||
check(decorated, `${hook}.decorate`);
|
||||
// Assign unique IDs to each class and sort by type.
|
||||
let uid = 1;
|
||||
const ids = {};
|
||||
const types = (
|
||||
|
@ -193,50 +298,78 @@ export default class Flecks {
|
|||
.sort(([ltype], [rtype]) => (ltype < rtype ? -1 : 1))
|
||||
.map(([type, Class]) => {
|
||||
const id = uid++;
|
||||
ids[id] = wrapperClass(Class, id, idAttribute, type, typeAttribute);
|
||||
ids[id] = wrapGathered(Class, id, idProperty, type, typeProperty);
|
||||
return [type, ids[id]];
|
||||
}),
|
||||
)
|
||||
);
|
||||
// Conglomerate all ID and type keys along with Symbols for accessing either/or.
|
||||
const gathered = {
|
||||
...ids,
|
||||
...types,
|
||||
[ById]: ids,
|
||||
[ByType]: types,
|
||||
};
|
||||
hotGathered.set(hook, {idAttribute, gathered, typeAttribute});
|
||||
// Register for HMR?
|
||||
if (module.hot) {
|
||||
hotGathered.set(hook, {idProperty, gathered, typeProperty});
|
||||
}
|
||||
debug("gathered '%s': %O", hook, Object.keys(gathered[ByType]));
|
||||
return gathered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a configuration value.
|
||||
*
|
||||
* @param {string} path The configuration path e.g. `@flecks/example.config`.
|
||||
* @param {*} defaultValue The default value if no configuration value is found.
|
||||
* @returns {*}
|
||||
*/
|
||||
get(path, defaultValue) {
|
||||
return get(this.config, path, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an object whose keys are fleck paths and values are the result of invoking the hook.
|
||||
* @param {string} hook
|
||||
* @param {...any} args Arguments passed to each implementation.
|
||||
* @returns {*}
|
||||
*/
|
||||
invoke(hook, ...args) {
|
||||
if (!this.hooks[hook]) {
|
||||
return {};
|
||||
}
|
||||
return this.flecksImplementing(hook)
|
||||
.reduce((r, fleck) => ({
|
||||
...r,
|
||||
[fleck]: this.invokeFleck(hook, fleck, ...args),
|
||||
}), {});
|
||||
.reduce((r, fleck) => ({...r, [fleck]: this.invokeFleck(hook, fleck, ...args)}), {});
|
||||
}
|
||||
|
||||
invokeComposed(hook, arg, ...args) {
|
||||
/**
|
||||
* See: [function composition](https://www.educative.io/edpresso/function-composition-in-javascript).
|
||||
*
|
||||
* @configurable
|
||||
* @param {string} hook
|
||||
* @param {*} initial The initial value passed to the composition chain.
|
||||
* @param {...any} args The arguments passed after the accumulator to each implementation.
|
||||
* @returns {*} The final composed value.
|
||||
*/
|
||||
invokeComposed(hook, initial, ...args) {
|
||||
if (!this.hooks[hook]) {
|
||||
return arg;
|
||||
return initial;
|
||||
}
|
||||
const flecks = this.expandedFlecks(hook);
|
||||
if (0 === flecks.length) {
|
||||
return arg;
|
||||
return initial;
|
||||
}
|
||||
return flecks
|
||||
.filter((fleck) => this.fleckImplements(fleck, hook))
|
||||
.reduce((r, fleck) => this.invokeFleck(hook, fleck, r, ...args), arg);
|
||||
.reduce((r, fleck) => this.invokeFleck(hook, fleck, r, ...args), initial);
|
||||
}
|
||||
|
||||
/**
|
||||
* An async version of `invokeComposed`.
|
||||
*
|
||||
* @see {@link Flecks#invokeComposed}
|
||||
*/
|
||||
async invokeComposedAsync(hook, arg, ...args) {
|
||||
if (!this.hooks[hook]) {
|
||||
return arg;
|
||||
|
@ -250,6 +383,13 @@ export default class Flecks {
|
|||
.reduce(async (r, fleck) => this.invokeFleck(hook, fleck, await r, ...args), arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes a hook and returns a flat array of results.
|
||||
*
|
||||
* @param {string} hook
|
||||
* @param {...any} args The arguments passed to each implementation.
|
||||
* @returns {any[]}
|
||||
*/
|
||||
invokeFlat(hook, ...args) {
|
||||
if (!this.hooks[hook]) {
|
||||
return [];
|
||||
|
@ -257,6 +397,14 @@ export default class Flecks {
|
|||
return this.hooks[hook].map(({fleck}) => this.invokeFleck(hook, fleck, ...args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes a hook on a single fleck.
|
||||
*
|
||||
* @param {string} hook
|
||||
* @param {*} fleck
|
||||
* @param {...any} args
|
||||
* @returns {*}
|
||||
*/
|
||||
invokeFleck(hook, fleck, ...args) {
|
||||
debugSilly('invokeFleck(%s, %s, ...)', hook, fleck);
|
||||
if (!this.hooks[hook]) {
|
||||
|
@ -270,33 +418,116 @@ export default class Flecks {
|
|||
return candidate.fn(...(args.concat(this)));
|
||||
}
|
||||
|
||||
static $$invokeMerge(r, o) {
|
||||
return {...r, ...o};
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialization of `invokeReduce`. Invokes a hook and reduces an object from all the resulting
|
||||
* objects.
|
||||
*
|
||||
* @param {string} hook
|
||||
* @param {...any} args
|
||||
* @returns {object}
|
||||
*/
|
||||
invokeMerge(hook, ...args) {
|
||||
return this.invokeReduce(hook, (r, o) => ({...r, ...o}), {}, ...args);
|
||||
return this.invokeReduce(hook, this.constructor.$$invokeMerge, {}, ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* An async version of `invokeMerge`.
|
||||
*
|
||||
* @see {@link Flecks#invokeMerge}
|
||||
*/
|
||||
async invokeMergeAsync(hook, ...args) {
|
||||
return this.invokeReduceAsync(hook, (r, o) => ({...r, ...o}), {}, ...args);
|
||||
return this.invokeReduceAsync(hook, this.constructor.$$invokeMerge, {}, ...args);
|
||||
}
|
||||
|
||||
static $$invokeMergeUnique() {
|
||||
const track = {};
|
||||
return (r, o, fleck, hook) => {
|
||||
const keys = Object.keys(o);
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
const key = keys[i];
|
||||
if (track[key]) {
|
||||
throw new ReferenceError(
|
||||
`Conflict in ${hook}: '${track[key]}' implemented '${key}', followed by '${fleck}'`,
|
||||
);
|
||||
}
|
||||
track[key] = fleck;
|
||||
}
|
||||
return ({...r, ...o});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialization of `invokeMerge`. Invokes a hook and reduces an object from all the resulting
|
||||
* objects.
|
||||
*
|
||||
* @param {string} hook
|
||||
* @param {...any} args
|
||||
* @returns {object}
|
||||
*/
|
||||
invokeMergeUnique(hook, ...args) {
|
||||
return this.invokeReduce(hook, this.constructor.$$invokeMergeUnique(), {}, ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* An async version of `invokeMergeUnique`.
|
||||
*
|
||||
* @see {@link Flecks#invokeMergeUnique}
|
||||
*/
|
||||
async invokeMergeUniqueAsync(hook, ...args) {
|
||||
return this.invokeReduceAsync(hook, this.constructor.$$invokeMergeUnique(), {}, ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* See: [Array.prototype.reduce](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce)
|
||||
*
|
||||
* @param {string} hook
|
||||
* @param {*} reducer
|
||||
* @param {*} initial
|
||||
* @param {...any} args The arguments passed after the accumulator to each implementation.
|
||||
* @returns {*}
|
||||
*/
|
||||
invokeReduce(hook, reducer, initial, ...args) {
|
||||
if (!this.hooks[hook]) {
|
||||
return initial;
|
||||
}
|
||||
return this.hooks[hook]
|
||||
.reduce((r, {fleck}) => reducer(r, this.invokeFleck(hook, fleck, ...args)), initial);
|
||||
.reduce(
|
||||
(r, {fleck}) => reducer(r, this.invokeFleck(hook, fleck, ...args), fleck, hook),
|
||||
initial,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* An async version of `invokeReduce`.
|
||||
*
|
||||
* @see {@link Flecks#invokeReduce}
|
||||
*/
|
||||
async invokeReduceAsync(hook, reducer, initial, ...args) {
|
||||
if (!this.hooks[hook]) {
|
||||
return initial;
|
||||
}
|
||||
return this.hooks[hook]
|
||||
.reduce(
|
||||
async (r, {fleck}) => reducer(await r, await this.invokeFleck(hook, fleck, ...args)),
|
||||
async (r, {fleck}) => (
|
||||
reducer(await r, await this.invokeFleck(hook, fleck, ...args), fleck, hook)
|
||||
),
|
||||
initial,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes hooks on a fleck one after another. This is effectively a configurable version of
|
||||
* {@link Flecks#invokeFlat}.
|
||||
*
|
||||
* @configurable
|
||||
* @param {string} hook
|
||||
* @param {...any} args The arguments passed to each implementation.
|
||||
* @returns {any[]}
|
||||
*/
|
||||
invokeSequential(hook, ...args) {
|
||||
if (!this.hooks[hook]) {
|
||||
return [];
|
||||
|
@ -315,6 +546,11 @@ export default class Flecks {
|
|||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* An async version of `invokeSequential`.
|
||||
*
|
||||
* @see {@link Flecks#invokeSequential}
|
||||
*/
|
||||
async invokeSequentialAsync(hook, ...args) {
|
||||
if (!this.hooks[hook]) {
|
||||
return [];
|
||||
|
@ -334,10 +570,18 @@ export default class Flecks {
|
|||
return results;
|
||||
}
|
||||
|
||||
isOnPlatform(platform) {
|
||||
return -1 !== this.platforms.indexOf(platform);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup flecks configured for a hook.
|
||||
*
|
||||
* If no configuration is found, defaults to ellipses.
|
||||
*
|
||||
* @param {string} hook
|
||||
* @example
|
||||
* # Given hook @flecks/example.hook, `flecks.yml` could be configured as such:
|
||||
* '@flecks/example':
|
||||
* hook: ['...']
|
||||
* @returns {string[]}
|
||||
*/
|
||||
lookupFlecks(hook) {
|
||||
const index = hook.indexOf('.');
|
||||
if (-1 === index) {
|
||||
|
@ -346,31 +590,37 @@ export default class Flecks {
|
|||
return this.get([hook.slice(0, index), hook.slice(index + 1)], ['...']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a middleware function from configured middleware.
|
||||
* @param {string} hook
|
||||
* @returns {function}
|
||||
*/
|
||||
makeMiddleware(hook) {
|
||||
debugSilly('makeMiddleware(...): %s', hook);
|
||||
if (!this.hooks[hook]) {
|
||||
return Promise.resolve();
|
||||
return (...args) => args.pop()();
|
||||
}
|
||||
const flecks = this.expandedFlecks(hook);
|
||||
if (0 === flecks.length) {
|
||||
return Promise.resolve();
|
||||
return (...args) => args.pop()();
|
||||
}
|
||||
const middleware = flecks
|
||||
.filter((fleck) => this.fleckImplements(fleck, hook));
|
||||
debugSilly('middleware: %O', middleware);
|
||||
const instance = new Middleware(middleware.map((fleck) => this.invokeFleck(hook, fleck)));
|
||||
return async (...args) => {
|
||||
const next = args.pop();
|
||||
try {
|
||||
await instance.promise(...args);
|
||||
next();
|
||||
}
|
||||
catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
return instance.dispatch.bind(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide classes for e.g. {@link Flecks#gather}
|
||||
*
|
||||
* @param {*} context @see {@link https://webpack.js.org/guides/dependency-management/#requirecontext}
|
||||
* @param {object} config
|
||||
* @param {function} [config.invoke = true] Invoke the default exports as a function?
|
||||
* @param {function} [config.transformer = {@link camelCase}]
|
||||
* Function to run on each context path.
|
||||
* @returns {object}
|
||||
*/
|
||||
static provide(
|
||||
context,
|
||||
{
|
||||
|
@ -393,7 +643,7 @@ export default class Flecks {
|
|||
);
|
||||
}
|
||||
return [
|
||||
transformer(this.symbolizePath(path)),
|
||||
transformer(this.dasherizePath(path)),
|
||||
invoke ? M(flecks) : M,
|
||||
];
|
||||
}),
|
||||
|
@ -401,9 +651,103 @@ export default class Flecks {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh a fleck's hooks, configuration, and any gathered classes.
|
||||
*
|
||||
* @example
|
||||
* module.hot.accept('@flecks/example', async () => {
|
||||
* flecks.refresh('@flecks/example', require('@flecks/example'));
|
||||
* });
|
||||
* @param {string} fleck
|
||||
* @param {object} M The fleck module
|
||||
* @protected
|
||||
*/
|
||||
refresh(fleck, M) {
|
||||
debug('refreshing %s...', fleck);
|
||||
// Remove old hook implementations.
|
||||
this.unregisterFleckHooks(fleck);
|
||||
// Replace the fleck.
|
||||
this.registerFleckHooks(fleck, M);
|
||||
// Write config.
|
||||
this.configureFleckDefaults(fleck);
|
||||
// HMR.
|
||||
if (module.hot) {
|
||||
this.refreshGathered(fleck);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh gathered classes for a fleck.
|
||||
*
|
||||
* @param {string} fleck
|
||||
*/
|
||||
refreshGathered(fleck) {
|
||||
const it = hotGathered.entries();
|
||||
for (let current = it.next(); current.done !== true; current = it.next()) {
|
||||
const {
|
||||
value: [
|
||||
hook,
|
||||
{
|
||||
idProperty,
|
||||
gathered,
|
||||
typeProperty,
|
||||
},
|
||||
],
|
||||
} = current;
|
||||
const updates = this.invokeFleck(hook, fleck);
|
||||
if (updates) {
|
||||
debug('updating gathered %s from %s...', hook, fleck);
|
||||
const entries = Object.entries(updates);
|
||||
for (let i = 0, [type, Class] = entries[i]; i < entries.length; ++i) {
|
||||
const {[type]: {[idProperty]: id}} = gathered;
|
||||
const Subclass = wrapGathered(Class, id, idProperty, type, typeProperty);
|
||||
// eslint-disable-next-line no-multi-assign
|
||||
gathered[type] = gathered[id] = gathered[ById][id] = gathered[ByType][type] = Subclass;
|
||||
this.invoke('@flecks/core.hmr.gathered', Subclass, hook);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register hooks for a fleck.
|
||||
*
|
||||
* @param {string} fleck
|
||||
* @param {object} M The fleck module
|
||||
* @protected
|
||||
*/
|
||||
registerFleckHooks(fleck, M) {
|
||||
debugSilly('registering %s...', fleck);
|
||||
this.flecks[fleck] = M;
|
||||
if (M.hooks) {
|
||||
const keys = Object.keys(M.hooks);
|
||||
debugSilly("hooks for '%s': %O", fleck, keys);
|
||||
for (let j = 0; j < keys.length; j++) {
|
||||
const key = keys[j];
|
||||
if (!this.hooks[key]) {
|
||||
this.hooks[key] = [];
|
||||
}
|
||||
this.hooks[key].push({fleck, fn: M.hooks[key]});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a configuration value.
|
||||
*
|
||||
* @param {string} path The configuration path e.g. `@flecks/example.config`.
|
||||
* @param {*} value The value to set.
|
||||
* @returns {*} The value that was set.
|
||||
*/
|
||||
set(path, value) {
|
||||
return set(this.config, path, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister hooks for a fleck.
|
||||
* @param {*} fleck
|
||||
*/
|
||||
unregisterFleckHooks(fleck) {
|
||||
const keys = Object.keys(this.hooks);
|
||||
for (let j = 0; j < keys.length; j++) {
|
||||
const key = keys[j];
|
||||
|
@ -414,82 +758,6 @@ export default class Flecks {
|
|||
}
|
||||
}
|
||||
}
|
||||
// Replace the fleck.
|
||||
this.registerFleck(fleck, M);
|
||||
// Write config.
|
||||
this.configureFleck(fleck);
|
||||
// HMR.
|
||||
this.updateHotGathered(fleck);
|
||||
}
|
||||
|
||||
registerFleck(fleck, M) {
|
||||
debugSilly('registering %s...', fleck);
|
||||
this.flecks[fleck] = M;
|
||||
if (M.default) {
|
||||
const {default: {[Hooks]: hooks}} = M;
|
||||
if (hooks) {
|
||||
const keys = Object.keys(hooks);
|
||||
debugSilly("hooks for '%s': %O", fleck, keys);
|
||||
for (let j = 0; j < keys.length; j++) {
|
||||
const key = keys[j];
|
||||
if (!this.hooks[key]) {
|
||||
this.hooks[key] = [];
|
||||
}
|
||||
this.hooks[key].push({fleck, fn: hooks[key]});
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
debugSilly("'%s' has no default export", fleck);
|
||||
}
|
||||
}
|
||||
|
||||
set(path, value) {
|
||||
return set(this.config, path, value);
|
||||
}
|
||||
|
||||
static symbolizePath(path) {
|
||||
const parts = dirname(path).split('/');
|
||||
if ('.' === parts[0]) {
|
||||
parts.shift();
|
||||
}
|
||||
if ('index' === parts[parts.length - 1]) {
|
||||
parts.pop();
|
||||
}
|
||||
return join(parts.join('-'), basename(path, extname(path)));
|
||||
}
|
||||
|
||||
async up(hook) {
|
||||
await Promise.all(this.invokeFlat('@flecks/core.starting'));
|
||||
await this.invokeSequentialAsync(hook);
|
||||
}
|
||||
|
||||
updateHotGathered(fleck) {
|
||||
const it = hotGathered.entries();
|
||||
for (let current = it.next(); current.done !== true; current = it.next()) {
|
||||
const {
|
||||
value: [
|
||||
hook,
|
||||
{
|
||||
idAttribute,
|
||||
gathered,
|
||||
typeAttribute,
|
||||
},
|
||||
],
|
||||
} = current;
|
||||
const updates = this.invokeFleck(hook, fleck);
|
||||
if (updates) {
|
||||
debug('updating gathered %s from %s...', hook, fleck);
|
||||
const entries = Object.entries(updates);
|
||||
for (let i = 0, [type, Class] = entries[i]; i < entries.length; ++i) {
|
||||
const {[type]: {[idAttribute]: id}} = gathered;
|
||||
const Subclass = wrapperClass(Class, id, idAttribute, type, typeAttribute);
|
||||
// eslint-disable-next-line no-multi-assign
|
||||
gathered[type] = gathered[id] = gathered[ById][id] = gathered[ByType][type] = Subclass;
|
||||
this.invoke('@flecks/core.hmr.gathered', Subclass, hook);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,24 +1,18 @@
|
|||
import {Hooks} from './flecks';
|
||||
|
||||
export {default as Class} from './class';
|
||||
export {default as compose} from './compose';
|
||||
export {default as D} from './debug';
|
||||
export {default as ensureUniqueReduction} from './ensure-unique-reduction';
|
||||
export {default as EventEmitter} from './event-emitter';
|
||||
export {
|
||||
default as Flecks,
|
||||
ById,
|
||||
ByType,
|
||||
Hooks,
|
||||
} from './flecks';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/core.config': () => ({
|
||||
/**
|
||||
* The ID of your application.
|
||||
*/
|
||||
id: 'flecks',
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -34,6 +34,7 @@ module.exports = {
|
|||
rules: {
|
||||
'babel/object-curly-spacing': 'off',
|
||||
'brace-style': ['error', 'stroustrup'],
|
||||
'import/prefer-default-export': 'off',
|
||||
'jsx-a11y/control-has-associated-label': ['error', {assert: 'either'}],
|
||||
'jsx-a11y/label-has-associated-control': ['error', {assert: 'either'}],
|
||||
'no-plusplus': 'off',
|
||||
|
|
|
@ -22,6 +22,7 @@ const {
|
|||
FLECKS_CORE_SYNC_FOR_ESLINT = false,
|
||||
} = process.env;
|
||||
|
||||
// This is kinda nuts, but ESLint doesn't support its configuration files returning a promise!
|
||||
if (FLECKS_CORE_SYNC_FOR_ESLINT) {
|
||||
(async () => {
|
||||
debug('bootstrapping flecks...');
|
||||
|
@ -50,6 +51,7 @@ else {
|
|||
module.exports = JSON.parse(readFileSync(join(cacheDirectory, 'eslintrc.json')).toString());
|
||||
}
|
||||
catch (error) {
|
||||
// Just silly. By synchronously spawning... ourselves, the spawned copy can use async.
|
||||
const {stderr, stdout} = spawnSync('node', [__filename], {
|
||||
env: {
|
||||
FLECKS_CORE_SYNC_FOR_ESLINT: true,
|
||||
|
|
|
@ -4,7 +4,6 @@ import {inspect} from 'util';
|
|||
import airbnb from '@neutrinojs/airbnb';
|
||||
import neutrino from 'neutrino';
|
||||
|
||||
import {Hooks} from '../flecks';
|
||||
import commands from './commands';
|
||||
import R from '../bootstrap/require';
|
||||
|
||||
|
@ -31,8 +30,7 @@ export {default as fleck} from '../bootstrap/fleck';
|
|||
export {default as require} from '../bootstrap/require';
|
||||
export {JsonStream, transform} from './stream';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/core.build': (target, config, flecks) => {
|
||||
const {
|
||||
'eslint.exclude': exclude,
|
||||
|
@ -107,5 +105,4 @@ export default {
|
|||
*/
|
||||
profile: [],
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import {expect} from 'chai';
|
||||
import chai from 'chai';
|
||||
import chaiAsPromised from 'chai-as-promised';
|
||||
|
||||
import {Flecks} from '@flecks/core';
|
||||
|
||||
chai.use(chaiAsPromised);
|
||||
|
||||
const {expect} = chai;
|
||||
|
||||
const testOne = require('./one');
|
||||
const testTwo = require('./two');
|
||||
|
||||
|
@ -33,3 +38,13 @@ it('can invoke merge async', async () => {
|
|||
expect(await flecks.invokeMergeAsync('@flecks/core/test/invoke-merge-async'))
|
||||
.to.deep.equal({foo: 69, bar: 420});
|
||||
});
|
||||
|
||||
it('can enforce uniqueness', () => {
|
||||
expect(() => flecks.invokeMergeUnique('@flecks/core/test/invoke-merge-unique'))
|
||||
.to.throw(ReferenceError);
|
||||
});
|
||||
|
||||
it('can enforce uniqueness async', async () => {
|
||||
expect(flecks.invokeMergeUniqueAsync('@flecks/core/test/invoke-merge-unique-async'))
|
||||
.to.be.rejectedWith(ReferenceError);
|
||||
});
|
||||
|
|
51
packages/core/test/middleware.js
Normal file
51
packages/core/test/middleware.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
import {expect} from 'chai';
|
||||
|
||||
import {Flecks} from '@flecks/core';
|
||||
|
||||
const testOne = require('./one');
|
||||
const testTwo = require('./two');
|
||||
|
||||
it('can make middleware', (done) => {
|
||||
let flecks;
|
||||
let foo;
|
||||
let mw;
|
||||
flecks = new Flecks({
|
||||
config: {
|
||||
'@flecks/core/test': {
|
||||
middleware: [
|
||||
'@flecks/core/one',
|
||||
'@flecks/core/two',
|
||||
],
|
||||
},
|
||||
},
|
||||
flecks: {
|
||||
'@flecks/core/one': testOne,
|
||||
'@flecks/core/two': testTwo,
|
||||
},
|
||||
});
|
||||
foo = {bar: 1};
|
||||
mw = flecks.makeMiddleware('@flecks/core/test.middleware');
|
||||
mw(foo, () => {
|
||||
expect(foo.bar).to.equal(4);
|
||||
flecks = new Flecks({
|
||||
config: {
|
||||
'@flecks/core/test': {
|
||||
middleware: [
|
||||
'@flecks/core/two',
|
||||
'@flecks/core/one',
|
||||
],
|
||||
},
|
||||
},
|
||||
flecks: {
|
||||
'@flecks/core/one': testOne,
|
||||
'@flecks/core/two': testTwo,
|
||||
},
|
||||
});
|
||||
foo = {bar: 1};
|
||||
mw = flecks.makeMiddleware('@flecks/core/test.middleware');
|
||||
mw(foo, () => {
|
||||
expect(foo.bar).to.equal(3);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,5 +1,5 @@
|
|||
// eslint-disable-next-line import/no-extraneous-dependencies, import/no-unresolved
|
||||
import {Flecks, Hooks} from '@flecks/core';
|
||||
import {Flecks} from '@flecks/core';
|
||||
|
||||
export const testNodespace = () => [
|
||||
/* eslint-disable no-eval */
|
||||
|
@ -8,8 +8,7 @@ export const testNodespace = () => [
|
|||
/* eslint-enable no-eval */
|
||||
];
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/core.config': () => ({
|
||||
foo: 'bar',
|
||||
}),
|
||||
|
@ -26,5 +25,11 @@ export default {
|
|||
},
|
||||
'@flecks/core/test/invoke-merge': () => ({foo: 69}),
|
||||
'@flecks/core/test/invoke-merge-async': () => new Promise((resolve) => resolve({foo: 69})),
|
||||
'@flecks/core/test/invoke-merge-unique': () => ({foo: 69}),
|
||||
'@flecks/core/test/invoke-merge-unique-async': () => new Promise((resolve) => resolve({foo: 69})),
|
||||
'@flecks/core/test.middleware': () => (foo, next) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
foo.bar += 1;
|
||||
next();
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import {Flecks, Hooks} from '@flecks/core';
|
||||
import {Flecks} from '@flecks/core';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/core/one/test-gather': (
|
||||
Flecks.provide(require.context('./things', false, /\.js$/))
|
||||
),
|
||||
|
@ -15,5 +14,11 @@ export default {
|
|||
}),
|
||||
'@flecks/core/test/invoke-merge': () => ({bar: 420}),
|
||||
'@flecks/core/test/invoke-merge-async': () => new Promise((resolve) => resolve({bar: 420})),
|
||||
'@flecks/core/test/invoke-merge-unique': () => ({foo: 69}),
|
||||
'@flecks/core/test/invoke-merge-unique-async': () => new Promise((resolve) => resolve({foo: 69})),
|
||||
'@flecks/core/test.middleware': () => (foo, next) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
foo.bar *= 2;
|
||||
next();
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
/**
|
||||
* Gather database models.
|
||||
*
|
||||
|
@ -23,6 +20,5 @@ export default {
|
|||
'@flecks/db/server.models.decorate': (
|
||||
Flecks.decorate(require.context('./models/decorators', false, /\.js$/))
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
|
||||
import {createDatabaseConnection} from './connection';
|
||||
import containers from './containers';
|
||||
|
||||
|
@ -9,8 +7,7 @@ export {default as Model} from './model';
|
|||
|
||||
export {createDatabaseConnection};
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/core.config': () => ({
|
||||
/**
|
||||
* The database to connect to.
|
||||
|
@ -42,7 +39,7 @@ export default {
|
|||
'@flecks/core.starting': (flecks) => {
|
||||
flecks.set('$flecks/db.models', flecks.gather(
|
||||
'@flecks/db/server.models',
|
||||
{typeAttribute: 'name'},
|
||||
{typeProperty: 'name'},
|
||||
));
|
||||
},
|
||||
'@flecks/docker.containers': containers,
|
||||
|
@ -53,5 +50,4 @@ export default {
|
|||
Models: flecks.get('$flecks/db.models'),
|
||||
sequelize: flecks.get('$flecks/db/sequelize'),
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
/**
|
||||
* Define docker containers.
|
||||
*
|
||||
|
@ -22,6 +19,5 @@ export default {
|
|||
ports: {3000: 3000},
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
|
||||
import commands from './commands';
|
||||
import startContainer from './start-container';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/core.config': () => ({
|
||||
/**
|
||||
* Whether to run docker containers.
|
||||
|
@ -22,5 +19,4 @@ export default {
|
|||
.map(([key, config]) => startContainer(flecks, key, config)),
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
isObjectExpression,
|
||||
isStringLiteral,
|
||||
isThisExpression,
|
||||
isVariableDeclaration,
|
||||
} from '@babel/types';
|
||||
import {require as R} from '@flecks/core/server';
|
||||
import {parse as parseComment} from 'comment-parser';
|
||||
|
@ -75,15 +76,14 @@ class ParserState {
|
|||
}
|
||||
|
||||
const implementationVisitor = (fn) => ({
|
||||
ExportDefaultDeclaration(path) {
|
||||
ExportNamedDeclaration(path) {
|
||||
const {declaration} = path.node;
|
||||
if (isObjectExpression(declaration)) {
|
||||
const {properties} = declaration;
|
||||
properties.forEach((property) => {
|
||||
const {key, value} = property;
|
||||
if (isIdentifier(key) && key.name === 'Hooks') {
|
||||
if (isObjectExpression(value)) {
|
||||
const {properties} = value;
|
||||
if (isVariableDeclaration(declaration)) {
|
||||
const {declarations} = declaration;
|
||||
declarations.forEach((declarator) => {
|
||||
if ('hooks' === declarator.id.name) {
|
||||
if (isObjectExpression(declarator.init)) {
|
||||
const {properties} = declarator.init;
|
||||
properties.forEach((property) => {
|
||||
const {key} = property;
|
||||
if (isLiteral(key)) {
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
|
||||
import commands from './commands';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/core.commands': commands,
|
||||
'@flecks/core.config': () => ({
|
||||
/**
|
||||
|
@ -13,5 +10,4 @@ export default {
|
|||
*/
|
||||
filenameRewriters: {},
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
/**
|
||||
* Invoked when electron is initializing.
|
||||
* @param {Electron.App} app The electron app. See: https://www.electronjs.org/docs/latest/api/app
|
||||
|
@ -19,6 +16,5 @@ export default {
|
|||
'@flecks/electron/server.window': (win) => {
|
||||
win.maximize();
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import cluster from 'cluster';
|
||||
import {join} from 'path';
|
||||
|
||||
import {Hooks} from '@flecks/core';
|
||||
import {require as R} from '@flecks/core/server';
|
||||
import {
|
||||
app,
|
||||
|
@ -21,8 +20,7 @@ async function createWindow(flecks) {
|
|||
await flecks.invokeSequentialAsync('@flecks/electron/server.window', win);
|
||||
}
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/core.config': () => ({
|
||||
/**
|
||||
* Browser window options.
|
||||
|
@ -135,5 +133,4 @@ export default {
|
|||
}
|
||||
await flecks.invokeSequentialAsync('@flecks/electron/server.initialize', app);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
|
||||
import commands from './commands';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/core.commands': commands,
|
||||
'@flecks/core.config': () => ({
|
||||
/**
|
||||
|
@ -17,5 +14,4 @@ export default {
|
|||
},
|
||||
}),
|
||||
'@flecks/core.targets': () => ['fleck'],
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import {ByType, Flecks, Hooks} from '@flecks/core';
|
||||
import {ByType, Flecks} from '@flecks/core';
|
||||
|
||||
import LimitedPacket from './limited-packet';
|
||||
import createLimiter from './limiter';
|
||||
|
||||
export {default as createLimiter} from './limiter';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/core.config': () => ({
|
||||
/**
|
||||
* All keys used to determine fingerprint.
|
||||
|
@ -131,5 +130,4 @@ export default {
|
|||
]),
|
||||
)
|
||||
),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
/**
|
||||
* Define React Providers.
|
||||
*
|
||||
|
@ -31,6 +28,5 @@ export default {
|
|||
// You can also just:
|
||||
return Component;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {D, Hooks} from '@flecks/core';
|
||||
import {D} from '@flecks/core';
|
||||
import {hydrate, render} from '@hot-loader/react-dom';
|
||||
import React from 'react';
|
||||
|
||||
|
@ -10,8 +10,7 @@ const debug = D('@flecks/react/client');
|
|||
|
||||
export {FlecksContext};
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/web/client.up': async (flecks) => {
|
||||
const {ssr} = flecks.get('@flecks/react');
|
||||
debug('%sing...', ssr ? 'hydrat' : 'render');
|
||||
|
@ -25,5 +24,4 @@ export default {
|
|||
);
|
||||
debug('rendered');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
|
||||
export {default as ReactDom} from '@hot-loader/react-dom';
|
||||
export {default as classnames} from 'classnames';
|
||||
export {default as PropTypes} from 'prop-types';
|
||||
|
@ -14,13 +12,11 @@ export {default as useEvent} from './hooks/use-event';
|
|||
export {default as useFlecks} from './hooks/use-flecks';
|
||||
export {default as usePrevious} from './hooks/use-previous';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/core.config': () => ({
|
||||
/**
|
||||
* Whether to enable server-side rendering.
|
||||
*/
|
||||
ssr: true,
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import {createReduxHistory, history} from '@flecks/react/router/context';
|
||||
import {unstable_HistoryRouter as HistoryRouter} from 'react-router-dom';
|
||||
import {HistoryRouter as ReduxHistoryRouter} from 'redux-first-history/rr6';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/react.providers': (req, flecks) => (
|
||||
flecks.fleck('@flecks/redux')
|
||||
? [ReduxHistoryRouter, {history: createReduxHistory(flecks.get('$flecks/redux.store'))}]
|
||||
: [HistoryRouter, {history}]
|
||||
),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import {routerMiddleware, routerReducer} from '@flecks/react/router/context';
|
||||
|
||||
export * from 'react-router-dom';
|
||||
export * from 'redux-first-history';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/redux.slices': () => ({
|
||||
router: routerReducer,
|
||||
}),
|
||||
'@flecks/redux.store': (options) => {
|
||||
options.middleware.push(routerMiddleware);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
import {StaticRouter} from 'react-router-dom/server';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/react.providers': (req, flecks) => (
|
||||
flecks.get('@flecks/react.ssr') ? [StaticRouter, {location: req.url}] : []
|
||||
),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
import {augmentBuild} from '@flecks/web/server';
|
||||
|
||||
import ssr from './ssr';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/core.build': (target, config, flecks) => {
|
||||
// Resolution.
|
||||
config.use.push(({config}) => {
|
||||
|
@ -24,5 +22,4 @@ export default {
|
|||
'@flecks/web/server.stream.html': (stream, req, flecks) => (
|
||||
flecks.get('@flecks/react.ssr') ? ssr(stream, req, flecks) : stream
|
||||
),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
|
||||
import containers from './containers';
|
||||
import createClient from './create-client';
|
||||
|
||||
|
@ -27,8 +25,7 @@ const safeKeys = async (client, pattern, caret) => {
|
|||
|
||||
export const keys = (client, pattern) => safeKeys(client, pattern, 0);
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/core.config': () => ({
|
||||
/**
|
||||
* Redis server host.
|
||||
|
@ -43,5 +40,4 @@ export default {
|
|||
'@flecks/repl.context': (flecks) => ({
|
||||
redisClient: createClient(flecks),
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {D, Hooks} from '@flecks/core';
|
||||
import {D} from '@flecks/core';
|
||||
import redisAdapter from '@socket.io/redis-adapter';
|
||||
import ConnectRedis from 'connect-redis';
|
||||
import session from 'express-session';
|
||||
|
@ -10,8 +10,7 @@ const debugSilly = debug.extend('silly');
|
|||
|
||||
const RedisStore = ConnectRedis(session);
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/user.session': async (flecks) => {
|
||||
const client = createClient(flecks, {legacyMode: true});
|
||||
await client.connect();
|
||||
|
@ -28,5 +27,4 @@ export default {
|
|||
adapter: redisAdapter(pubClient, subClient),
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
/**
|
||||
* Define side-effects to run against Redux actions.
|
||||
*/
|
||||
|
@ -40,6 +37,5 @@ export default {
|
|||
options.enhancers.splice(someIndex, 1);
|
||||
options.middleware.push(mySpecialMiddleware);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import {ensureUniqueReduction, Flecks, Hooks} from '@flecks/core';
|
||||
import {Flecks} from '@flecks/core';
|
||||
import {Provider} from 'react-redux';
|
||||
|
||||
import configureStore, {createReducer} from '../store';
|
||||
import localStorageEnhancer from './local-storage';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/react.providers': async (req, flecks) => {
|
||||
const slices = await ensureUniqueReduction(flecks, '@flecks/redux.slices');
|
||||
const slices = await flecks.invokeMergeUnique('@flecks/redux.slices');
|
||||
const reducer = createReducer(flecks, slices);
|
||||
// Hydrate from server.
|
||||
const {preloadedState} = flecks.get('@flecks/redux/client');
|
||||
|
@ -22,5 +21,4 @@ export default {
|
|||
'@flecks/socket.packets.decorate': (
|
||||
Flecks.decorate(require.context('./packets/decorators', false, /\.js$/))
|
||||
),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
import {Flecks, Hooks} from '@flecks/core';
|
||||
import {Flecks} from '@flecks/core';
|
||||
|
||||
export * from '@reduxjs/toolkit';
|
||||
export * from 'react-redux';
|
||||
|
||||
export * from './actions';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/socket.packets': Flecks.provide(require.context('./packets', false, /\.js$/)),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {D, ensureUniqueReduction, Hooks} from '@flecks/core';
|
||||
import {D} from '@flecks/core';
|
||||
import {Provider} from 'react-redux';
|
||||
|
||||
import {hydrateServer} from './actions';
|
||||
|
@ -8,10 +8,9 @@ import configureStore from './store';
|
|||
const debug = D('@flecks/redux/server');
|
||||
const debugSilly = debug.extend('silly');
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/web/server.request.route': (flecks) => async (req, res, next) => {
|
||||
const slices = await ensureUniqueReduction(flecks, '@flecks/redux.slices');
|
||||
const slices = await flecks.invokeMergeUnique('@flecks/redux.slices');
|
||||
const reducer = createReducer(flecks, slices);
|
||||
// Let the slices have a(n async) chance to hydrate with server data.
|
||||
await Promise.all(
|
||||
|
@ -32,5 +31,4 @@ export default {
|
|||
},
|
||||
}),
|
||||
'@flecks/react.providers': (req) => [Provider, {store: req.redux}],
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
/**
|
||||
* Define REPL commands.
|
||||
*
|
||||
|
@ -25,6 +22,5 @@ export default {
|
|||
someValue: 'foobar',
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
|
||||
import commands from './commands';
|
||||
import {createReplServer} from './repl';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/core.commands': commands,
|
||||
'@flecks/server.up': (flecks) => createReplServer(flecks),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
/**
|
||||
* Define sequential actions to run when the server comes up.
|
||||
*/
|
||||
'@flecks/server.up': async () => {
|
||||
await youCanDoAsyncThingsHere();
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -33,7 +33,8 @@ const {version} = require('../package.json');
|
|||
rcs,
|
||||
});
|
||||
try {
|
||||
await global.flecks.up('@flecks/server.up');
|
||||
await Promise.all(global.flecks.invokeFlat('@flecks/core.starting'));
|
||||
await global.flecks.invokeSequentialAsync('@flecks/server.up');
|
||||
debug('up!');
|
||||
}
|
||||
catch (error) {
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/core.config': () => ({
|
||||
/**
|
||||
* Whether HMR is enabled.
|
||||
|
@ -24,5 +21,4 @@ export default {
|
|||
modules: false,
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/core.targets': () => ['server'],
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
/**
|
||||
* Modify Socket.io client configuration.
|
||||
*
|
||||
|
@ -60,6 +57,5 @@ export default {
|
|||
// Express-style route middleware...
|
||||
next();
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
|
||||
import SocketClient from './socket';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/web/client.up': (flecks) => {
|
||||
const socket = new SocketClient(flecks);
|
||||
flecks.set('$flecks/socket.socket', socket);
|
||||
|
@ -16,5 +13,4 @@ export default {
|
|||
},
|
||||
path: `/${id}/socket.io`,
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
|
||||
import badPacketsCheck from './packet/bad-packets-check';
|
||||
import Bundle from './packet/bundle';
|
||||
import Redirect from './packet/redirect';
|
||||
|
@ -9,8 +7,7 @@ export {default as normalize} from './normalize';
|
|||
export * from './hooks';
|
||||
export {default as Packet, Packer, ValidationError} from './packet';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/core.starting': (flecks) => {
|
||||
flecks.set('$flecks/socket.packets', flecks.gather(
|
||||
'@flecks/socket.packets',
|
||||
|
@ -32,5 +29,4 @@ export default {
|
|||
Redirect,
|
||||
Refresh,
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
|
||||
import createIntercom from './create-intercom';
|
||||
import Sockets from './sockets';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/web/server.request.socket': ({config: {'$flecks/socket.sockets': sockets}}) => (req, res, next) => {
|
||||
req.intercom = createIntercom(sockets, 'web');
|
||||
next();
|
||||
|
@ -21,5 +18,4 @@ export default {
|
|||
'@flecks/socket.server': ({config: {'@flecks/core': {id}}}) => ({
|
||||
path: `/${id}/socket.io`,
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
/**
|
||||
* Modify express-session configuration.
|
||||
*
|
||||
|
@ -10,6 +7,4 @@ export default {
|
|||
'@flecks/user.session': () => ({
|
||||
saveUninitialized: true,
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
|
||||
import {Logout} from './packets';
|
||||
|
||||
import {user, users} from './state';
|
||||
|
||||
export * from './state';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/redux.slices': () => ({
|
||||
user,
|
||||
users,
|
||||
|
@ -15,5 +12,4 @@ export default {
|
|||
'@flecks/socket.packets': (flecks) => ({
|
||||
Logout: Logout(flecks),
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import {randomBytes} from 'crypto';
|
||||
|
||||
import {Flecks, Hooks} from '@flecks/core';
|
||||
import {Flecks} from '@flecks/core';
|
||||
import passport from 'passport';
|
||||
import LocalStrategy from 'passport-local';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/core.config': () => ({
|
||||
/**
|
||||
* Path to redirect to after failed login.
|
||||
|
@ -66,5 +65,4 @@ export default {
|
|||
},
|
||||
));
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import {D, Flecks, Hooks} from '@flecks/core';
|
||||
import {D, Flecks} from '@flecks/core';
|
||||
import passport from 'passport';
|
||||
import LogOps from 'passport/lib/http/request';
|
||||
|
||||
const debug = D('@flecks/user/passport');
|
||||
const debugSilly = debug.extend('silly');
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/db/server.models': Flecks.provide(require.context('./models', false, /\.js$/)),
|
||||
'@flecks/web/server.request.route': (flecks) => (req, res, next) => {
|
||||
debugSilly('@flecks/web/server.request.route: passport.initialize()');
|
||||
|
@ -81,5 +80,4 @@ export default {
|
|||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import {D, Hooks} from '@flecks/core';
|
||||
import {D} from '@flecks/core';
|
||||
import express from 'express';
|
||||
import expressSession from 'express-session';
|
||||
|
||||
const debug = D('@flecks/user/session');
|
||||
const debugSilly = debug.extend('silly');
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/core.config': () => ({
|
||||
/**
|
||||
* Set the cookie secret for session encryption.
|
||||
|
@ -55,5 +54,4 @@ export default {
|
|||
next();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
import {Hooks} from '@flecks/core';
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
/**
|
||||
* Define sequential actions to run when the client comes up.
|
||||
*/
|
||||
|
@ -58,5 +55,4 @@ export default {
|
|||
'@flecks/web/server.up': async () => {
|
||||
await youCanDoAsyncThingsHere();
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {D, Flecks} from '@flecks/core';
|
||||
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies, import/no-unresolved
|
||||
const {version} = require('@flecks/web/package.json');
|
||||
|
||||
(async () => {
|
||||
|
@ -56,7 +56,8 @@ const {version} = require('@flecks/web/package.json');
|
|||
const flecks = new Flecks(runtime);
|
||||
window.flecks = flecks;
|
||||
try {
|
||||
await flecks.up('@flecks/web/client.up');
|
||||
await Promise.all(flecks.invokeFlat('@flecks/core.starting'));
|
||||
await flecks.invokeSequentialAsync('@flecks/web/client.up');
|
||||
window.document.querySelector('#root').style.display = 'block';
|
||||
debug('up!');
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {stat, unlink} from 'fs/promises';
|
||||
import {join} from 'path';
|
||||
|
||||
import {D, Hooks} from '@flecks/core';
|
||||
import {D} from '@flecks/core';
|
||||
import {Flecks, spawnWith} from '@flecks/core/server';
|
||||
|
||||
import augmentBuild from './augment-build';
|
||||
|
@ -16,8 +16,7 @@ const debug = D('@flecks/web/server');
|
|||
|
||||
export {augmentBuild};
|
||||
|
||||
export default {
|
||||
[Hooks]: {
|
||||
export const hooks = {
|
||||
'@flecks/core.build': augmentBuild,
|
||||
'@flecks/core.build.alter': async (neutrinoConfigs, flecks) => {
|
||||
// Don't build if there's a fleck target.
|
||||
|
@ -210,5 +209,4 @@ export default {
|
|||
'@flecks/repl.context': (flecks) => ({
|
||||
httpServer: flecks.get('$flecks/web/server.instance'),
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue
Block a user