flow:
dox, hook registration, ensureUniqueReduction, middleware, ...
This commit is contained in:
parent
23f2fae001
commit
c3910ba5f0
|
@ -1,7 +1,7 @@
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<h1>flecks</h1>
|
<h1>flecks</h1>
|
||||||
<p>
|
<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
|
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
|
a highly dynamic structure encourage consistency while allowing you to easily express your own
|
||||||
opinions.
|
opinions.
|
||||||
|
|
3
TODO.md
3
TODO.md
|
@ -22,7 +22,7 @@
|
||||||
- [x] remove `invokeParallel()`
|
- [x] remove `invokeParallel()`
|
||||||
- [x] Specialize `invokeReduce()` with `invokeMerge()`.
|
- [x] Specialize `invokeReduce()` with `invokeMerge()`.
|
||||||
- [x] Rename all hooks to dot-first notation; rewrite `lookupFlecks()`.
|
- [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']}`
|
- [x] `bootstrap({without: ['badplatform']})` should be handled by passing `{platforms: ['!badplatform']}`
|
||||||
- [ ] user redux server hydrate fails if no user in req
|
- [ ] user redux server hydrate fails if no user in req
|
||||||
- [ ] governor fails if not in server up
|
- [ ] governor fails if not in server up
|
||||||
|
@ -31,3 +31,4 @@
|
||||||
- [ ] rename `@flecks/web` to `@flecks/web`
|
- [ ] rename `@flecks/web` to `@flecks/web`
|
||||||
- [ ] simultaneous babel compilation across all compiled flecks
|
- [ ] simultaneous babel compilation across all compiled flecks
|
||||||
- [ ] add building to publish process ...
|
- [ ] 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());
|
config.use.unshift(fleck());
|
||||||
|
|
||||||
|
// AirBnb linting.
|
||||||
config.use.unshift(
|
config.use.unshift(
|
||||||
airbnb({
|
airbnb({
|
||||||
eslint: {
|
eslint: {
|
||||||
|
@ -45,13 +47,13 @@ config.use.unshift(
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Include a shebang and set the executable bit..
|
||||||
config.use.push(banner({
|
config.use.push(banner({
|
||||||
banner: '#!/usr/bin/env node',
|
banner: '#!/usr/bin/env node',
|
||||||
include: /^cli\.js$/,
|
include: /^cli\.js$/,
|
||||||
pluginId: 'shebang',
|
pluginId: 'shebang',
|
||||||
raw: true,
|
raw: true,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
config.use.push(({config}) => {
|
config.use.push(({config}) => {
|
||||||
config
|
config
|
||||||
.plugin('executable')
|
.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).
|
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
|
```javascript
|
||||||
import {Hooks} from '@flecks/core';
|
export const hooks = {
|
||||||
|
|
||||||
export default {
|
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/core.starting': () => {
|
'@flecks/core.starting': () => {
|
||||||
console.log('hello, gorgeous');
|
console.log('hello, gorgeous');
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -133,15 +129,15 @@ assert(foo.type === 'Foo');
|
||||||
```javascript
|
```javascript
|
||||||
{
|
{
|
||||||
// The property added when extending the class to return the numeric ID.
|
// 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.
|
// 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.
|
// A function called with the `Gathered` object to allow checking validity.
|
||||||
check = () => {},
|
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.
|
**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:
|
Here's an example of how you could manually provide `@flecks/db/server.models` in your own fleck:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
import {Hooks} foom '@flecks/core';
|
|
||||||
|
|
||||||
import SomeModel from './models/some-model';
|
import SomeModel from './models/some-model';
|
||||||
import AnotherModel from './models/another-model';
|
import AnotherModel from './models/another-model';
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/db/server.models': () => ({
|
'@flecks/db/server.models': () => ({
|
||||||
SomeModel,
|
SomeModel,
|
||||||
AnotherModel,
|
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.
|
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`:
|
then, this `index.js`:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
import {Flecks, Hooks} from '@flecks/core';
|
import {Flecks} from '@flecks/core';
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/db/server.models': Flecks.provide(require.context('./models', false, /\.js$/)),
|
'@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:
|
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
|
```javascript
|
||||||
import {Hooks} from '@flecks/core';
|
export const hooks = {
|
||||||
|
|
||||||
export default {
|
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/db/server.models.decorate': (Models) => {
|
'@flecks/db/server.models.decorate': (Models) => {
|
||||||
return {
|
return {
|
||||||
...Models,
|
...Models,
|
||||||
|
@ -230,13 +217,12 @@ export default {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `Flecks.decorate(context, options)`
|
#### `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:
|
Supposing our fleck is structured like so:
|
||||||
|
|
||||||
|
@ -266,12 +252,12 @@ export default (User) => {
|
||||||
then, this `index.js`:
|
then, this `index.js`:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
import {Flecks, Hooks} from '@flecks/core';
|
import {Flecks} from '@flecks/core';
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
'@flecks/db/server.models.decorate': (
|
||||||
'@flecks/db/server.models.decorate': Flecks.decorate(require.context('./models/decorators', false, /\.js$/)),
|
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).
|
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).
|
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.
|
* Hook into neutrino configuration.
|
||||||
* @param {string} target The build target; e.g. `server`.
|
* @param {string} target The build target; e.g. `server`.
|
||||||
|
@ -54,7 +52,7 @@ export default {
|
||||||
args: [
|
args: [
|
||||||
'<somearg>',
|
'<somearg>',
|
||||||
],
|
],
|
||||||
description: 'This sure is some command',
|
description: 'This command does tests and also blows up',
|
||||||
options: [
|
options: [
|
||||||
'-t, --test', 'Do a test',
|
'-t, --test', 'Do a test',
|
||||||
'-b, --blow-up', 'Blow up instead of running the command',
|
'-b, --blow-up', 'Blow up instead of running the command',
|
||||||
|
@ -117,6 +115,4 @@ export default {
|
||||||
config.stats = 'verbose';
|
config.stats = 'verbose';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,7 @@
|
||||||
"babel-merge": "^3.0.0",
|
"babel-merge": "^3.0.0",
|
||||||
"babel-plugin-prepend": "^1.0.2",
|
"babel-plugin-prepend": "^1.0.2",
|
||||||
"chai": "4.2.0",
|
"chai": "4.2.0",
|
||||||
|
"chai-as-promised": "7.1.1",
|
||||||
"commander": "^8.3.0",
|
"commander": "^8.3.0",
|
||||||
"debug": "4.3.1",
|
"debug": "4.3.1",
|
||||||
"enhanced-resolve": "^5.9.2",
|
"enhanced-resolve": "^5.9.2",
|
||||||
|
|
|
@ -11,7 +11,7 @@ const {
|
||||||
FLECKS_CORE_ROOT = process.cwd(),
|
FLECKS_CORE_ROOT = process.cwd(),
|
||||||
} = process.env;
|
} = process.env;
|
||||||
|
|
||||||
const resolver = (source) => (path) => {
|
const resolveValidModulePath = (source) => (path) => {
|
||||||
// Does the file resolve as source?
|
// Does the file resolve as source?
|
||||||
try {
|
try {
|
||||||
R.resolve(`${source}/${path}`);
|
R.resolve(`${source}/${path}`);
|
||||||
|
@ -39,7 +39,7 @@ module.exports = () => ({config, options}) => {
|
||||||
.set(name, join(FLECKS_CORE_ROOT, 'src'));
|
.set(name, join(FLECKS_CORE_ROOT, 'src'));
|
||||||
// Calculate entry points from `files`.
|
// Calculate entry points from `files`.
|
||||||
files
|
files
|
||||||
.filter(resolver(source))
|
.filter(resolveValidModulePath(source))
|
||||||
.forEach((file) => {
|
.forEach((file) => {
|
||||||
const trimmed = join(dirname(file), basename(file, extname(file)));
|
const trimmed = join(dirname(file), basename(file, extname(file)));
|
||||||
config
|
config
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
|
// Get a runtime require function by hook or by crook. :)
|
||||||
|
|
||||||
// eslint-disable-next-line no-eval
|
// eslint-disable-next-line no-eval
|
||||||
module.exports = eval('"undefined" !== typeof require ? require : undefined');
|
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 debug = D('@flecks/core/flecks');
|
||||||
const debugSilly = debug.extend('silly');
|
const debugSilly = debug.extend('silly');
|
||||||
|
|
||||||
|
// Symbols for Gathered classes.
|
||||||
export const ById = Symbol.for('@flecks/core.byId');
|
export const ById = Symbol.for('@flecks/core.byId');
|
||||||
export const ByType = Symbol.for('@flecks/core.byType');
|
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);
|
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('');
|
const camelCase = (string) => string.split(/[_-]/).map(capitalize).join('');
|
||||||
|
|
||||||
|
// Track gathered for HMR.
|
||||||
const hotGathered = new Map();
|
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 {
|
class Subclass extends Class {
|
||||||
|
|
||||||
static get [idAttribute]() {
|
static get [idProperty]() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get [typeAttribute]() {
|
static get [typeProperty]() {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,72 +57,121 @@ const wrapperClass = (Class, id, idAttribute, type, typeAttribute) => {
|
||||||
|
|
||||||
export default class Flecks {
|
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({
|
constructor({
|
||||||
config = {},
|
config = {},
|
||||||
flecks = {},
|
flecks = {},
|
||||||
platforms = [],
|
platforms = [],
|
||||||
} = {}) {
|
} = {}) {
|
||||||
this.config = {
|
const emptyConfigForAllFlecks = Object.fromEntries(
|
||||||
...Object.fromEntries(Object.keys(flecks).map((path) => [path, {}])),
|
Object.keys(flecks).map((path) => [path, {}]),
|
||||||
...config,
|
);
|
||||||
};
|
this.config = {...emptyConfigForAllFlecks, ...config};
|
||||||
this.hooks = {};
|
|
||||||
this.flecks = {};
|
|
||||||
this.platforms = platforms;
|
this.platforms = platforms;
|
||||||
const entries = Object.entries(flecks);
|
const entries = Object.entries(flecks);
|
||||||
debugSilly('paths: %O', entries.map(([fleck]) => fleck));
|
debugSilly('paths: %O', entries.map(([fleck]) => fleck));
|
||||||
for (let i = 0; i < entries.length; i++) {
|
for (let i = 0; i < entries.length; i++) {
|
||||||
const [fleck, M] = entries[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);
|
debugSilly('config: %O', this.config);
|
||||||
}
|
}
|
||||||
|
|
||||||
configureFleck(fleck) {
|
/**
|
||||||
|
* Configure defaults for a fleck.
|
||||||
|
*
|
||||||
|
* @param {string} fleck
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
configureFleckDefaults(fleck) {
|
||||||
this.config[fleck] = {
|
this.config[fleck] = {
|
||||||
...this.invokeFleck('@flecks/core.config', fleck),
|
...this.invokeFleck('@flecks/core.config', fleck),
|
||||||
...this.config[fleck],
|
...this.config[fleck],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
configureFlecks() {
|
/**
|
||||||
const defaultConfig = this.invoke('@flecks/core.config');
|
* Configure defaults for all flecks.
|
||||||
const flecks = Object.keys(defaultConfig);
|
*
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
configureFlecksDefaults() {
|
||||||
|
const flecks = this.flecksImplementing('@flecks/core.config');
|
||||||
for (let i = 0; i < flecks.length; i++) {
|
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(
|
static decorate(
|
||||||
context,
|
context,
|
||||||
{
|
{
|
||||||
transformer = camelCase,
|
transformer = camelCase,
|
||||||
} = {},
|
} = {},
|
||||||
) {
|
) {
|
||||||
return (Gathered, flecks) => {
|
return (Gathered, flecks) => (
|
||||||
context.keys()
|
context.keys()
|
||||||
.forEach((path) => {
|
.reduce(
|
||||||
|
(Gathered, path) => {
|
||||||
|
const key = transformer(this.dasherizePath(path));
|
||||||
|
if (!Gathered[key]) {
|
||||||
|
return Gathered;
|
||||||
|
}
|
||||||
const {default: M} = context(path);
|
const {default: M} = context(path);
|
||||||
if ('function' !== typeof M) {
|
if ('function' !== typeof M) {
|
||||||
throw new ReferenceError(
|
throw new ReferenceError(
|
||||||
`Flecks.decorate(): require(${
|
`Flecks.decorate(): require(${path}).default is not a function (from: ${context.id})`,
|
||||||
path
|
|
||||||
}).default is not a function (from: ${
|
|
||||||
context.id
|
|
||||||
})`,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const key = transformer(this.symbolizePath(path));
|
return {...Gathered, [key]: M(Gathered[key], flecks)};
|
||||||
if (Gathered[key]) {
|
},
|
||||||
// eslint-disable-next-line no-param-reassign
|
Gathered,
|
||||||
Gathered[key] = M(Gathered[key], flecks);
|
)
|
||||||
}
|
);
|
||||||
});
|
|
||||||
return Gathered;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy this instance.
|
||||||
|
*/
|
||||||
destroy() {
|
destroy() {
|
||||||
this.config = {};
|
this.config = {};
|
||||||
this.hooks = {};
|
this.hooks = {};
|
||||||
|
@ -116,12 +179,20 @@ export default class Flecks {
|
||||||
this.platforms = [];
|
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) {
|
expandedFlecks(hook) {
|
||||||
const flecks = this.lookupFlecks(hook);
|
const flecks = this.lookupFlecks(hook);
|
||||||
let expanded = [];
|
let expanded = [];
|
||||||
for (let i = 0; i < flecks.length; ++i) {
|
for (let i = 0; i < flecks.length; ++i) {
|
||||||
const fleck = flecks[i];
|
const fleck = flecks[i];
|
||||||
|
// Just the fleck.
|
||||||
expanded.push(fleck);
|
expanded.push(fleck);
|
||||||
|
// Platform-specific variants.
|
||||||
for (let j = 0; j < this.platforms.length; ++j) {
|
for (let j = 0; j < this.platforms.length; ++j) {
|
||||||
const platform = this.platforms[j];
|
const platform = this.platforms[j];
|
||||||
const variant = join(fleck, platform);
|
const variant = join(fleck, platform);
|
||||||
|
@ -130,6 +201,7 @@ export default class Flecks {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Expand elided flecks.
|
||||||
const index = expanded.findIndex((fleck) => '...' === fleck);
|
const index = expanded.findIndex((fleck) => '...' === fleck);
|
||||||
if (-1 !== index) {
|
if (-1 !== index) {
|
||||||
if (-1 !== expanded.slice(index + 1).findIndex((fleck) => '...' === fleck)) {
|
if (-1 !== expanded.slice(index + 1).findIndex((fleck) => '...' === fleck)) {
|
||||||
|
@ -158,33 +230,66 @@ export default class Flecks {
|
||||||
return expanded;
|
return expanded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the module for a fleck.
|
||||||
|
*
|
||||||
|
* @param {*} fleck
|
||||||
|
*
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
fleck(fleck) {
|
fleck(fleck) {
|
||||||
return this.flecks[fleck];
|
return this.flecks[fleck];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test whether a fleck implements a hook.
|
||||||
|
*
|
||||||
|
* @param {*} fleck
|
||||||
|
* @param {string} hook
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
fleckImplements(fleck, hook) {
|
fleckImplements(fleck, hook) {
|
||||||
return !!this.hooks[hook].find(({fleck: candidate}) => fleck === candidate);
|
return !!this.hooks[hook].find(({fleck: candidate}) => fleck === candidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of flecks implementing a hook.
|
||||||
|
*
|
||||||
|
* @param {string} hook
|
||||||
|
* @returns {string[]}
|
||||||
|
*/
|
||||||
flecksImplementing(hook) {
|
flecksImplementing(hook) {
|
||||||
return this.hooks[hook]?.map(({fleck}) => fleck) || [];
|
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(
|
gather(
|
||||||
hook,
|
hook,
|
||||||
{
|
{
|
||||||
idAttribute = 'id',
|
idProperty = 'id',
|
||||||
typeAttribute = 'type',
|
typeProperty = 'type',
|
||||||
check = () => {},
|
check = () => {},
|
||||||
} = {},
|
} = {},
|
||||||
) {
|
) {
|
||||||
if (!hook || 'string' !== typeof hook) {
|
if (!hook || 'string' !== typeof hook) {
|
||||||
throw new TypeError('Flecks.gather(): Expects parameter 1 (hook) to be string');
|
throw new TypeError('Flecks.gather(): Expects parameter 1 (hook) to be string');
|
||||||
}
|
}
|
||||||
|
// Gather classes and check.
|
||||||
const raw = this.invokeMerge(hook);
|
const raw = this.invokeMerge(hook);
|
||||||
check(raw, hook);
|
check(raw, hook);
|
||||||
|
// Decorate and check.
|
||||||
const decorated = this.invokeComposed(`${hook}.decorate`, raw);
|
const decorated = this.invokeComposed(`${hook}.decorate`, raw);
|
||||||
check(decorated, `${hook}.decorate`);
|
check(decorated, `${hook}.decorate`);
|
||||||
|
// Assign unique IDs to each class and sort by type.
|
||||||
let uid = 1;
|
let uid = 1;
|
||||||
const ids = {};
|
const ids = {};
|
||||||
const types = (
|
const types = (
|
||||||
|
@ -193,50 +298,78 @@ export default class Flecks {
|
||||||
.sort(([ltype], [rtype]) => (ltype < rtype ? -1 : 1))
|
.sort(([ltype], [rtype]) => (ltype < rtype ? -1 : 1))
|
||||||
.map(([type, Class]) => {
|
.map(([type, Class]) => {
|
||||||
const id = uid++;
|
const id = uid++;
|
||||||
ids[id] = wrapperClass(Class, id, idAttribute, type, typeAttribute);
|
ids[id] = wrapGathered(Class, id, idProperty, type, typeProperty);
|
||||||
return [type, ids[id]];
|
return [type, ids[id]];
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
// Conglomerate all ID and type keys along with Symbols for accessing either/or.
|
||||||
const gathered = {
|
const gathered = {
|
||||||
...ids,
|
...ids,
|
||||||
...types,
|
...types,
|
||||||
[ById]: ids,
|
[ById]: ids,
|
||||||
[ByType]: types,
|
[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]));
|
debug("gathered '%s': %O", hook, Object.keys(gathered[ByType]));
|
||||||
return gathered;
|
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) {
|
get(path, defaultValue) {
|
||||||
return get(this.config, 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) {
|
invoke(hook, ...args) {
|
||||||
if (!this.hooks[hook]) {
|
if (!this.hooks[hook]) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
return this.flecksImplementing(hook)
|
return this.flecksImplementing(hook)
|
||||||
.reduce((r, fleck) => ({
|
.reduce((r, fleck) => ({...r, [fleck]: this.invokeFleck(hook, fleck, ...args)}), {});
|
||||||
...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]) {
|
if (!this.hooks[hook]) {
|
||||||
return arg;
|
return initial;
|
||||||
}
|
}
|
||||||
const flecks = this.expandedFlecks(hook);
|
const flecks = this.expandedFlecks(hook);
|
||||||
if (0 === flecks.length) {
|
if (0 === flecks.length) {
|
||||||
return arg;
|
return initial;
|
||||||
}
|
}
|
||||||
return flecks
|
return flecks
|
||||||
.filter((fleck) => this.fleckImplements(fleck, hook))
|
.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) {
|
async invokeComposedAsync(hook, arg, ...args) {
|
||||||
if (!this.hooks[hook]) {
|
if (!this.hooks[hook]) {
|
||||||
return arg;
|
return arg;
|
||||||
|
@ -250,6 +383,13 @@ export default class Flecks {
|
||||||
.reduce(async (r, fleck) => this.invokeFleck(hook, fleck, await r, ...args), arg);
|
.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) {
|
invokeFlat(hook, ...args) {
|
||||||
if (!this.hooks[hook]) {
|
if (!this.hooks[hook]) {
|
||||||
return [];
|
return [];
|
||||||
|
@ -257,6 +397,14 @@ export default class Flecks {
|
||||||
return this.hooks[hook].map(({fleck}) => this.invokeFleck(hook, fleck, ...args));
|
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) {
|
invokeFleck(hook, fleck, ...args) {
|
||||||
debugSilly('invokeFleck(%s, %s, ...)', hook, fleck);
|
debugSilly('invokeFleck(%s, %s, ...)', hook, fleck);
|
||||||
if (!this.hooks[hook]) {
|
if (!this.hooks[hook]) {
|
||||||
|
@ -270,33 +418,116 @@ export default class Flecks {
|
||||||
return candidate.fn(...(args.concat(this)));
|
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) {
|
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) {
|
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) {
|
invokeReduce(hook, reducer, initial, ...args) {
|
||||||
if (!this.hooks[hook]) {
|
if (!this.hooks[hook]) {
|
||||||
return initial;
|
return initial;
|
||||||
}
|
}
|
||||||
return this.hooks[hook]
|
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) {
|
async invokeReduceAsync(hook, reducer, initial, ...args) {
|
||||||
if (!this.hooks[hook]) {
|
if (!this.hooks[hook]) {
|
||||||
return initial;
|
return initial;
|
||||||
}
|
}
|
||||||
return this.hooks[hook]
|
return this.hooks[hook]
|
||||||
.reduce(
|
.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,
|
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) {
|
invokeSequential(hook, ...args) {
|
||||||
if (!this.hooks[hook]) {
|
if (!this.hooks[hook]) {
|
||||||
return [];
|
return [];
|
||||||
|
@ -315,6 +546,11 @@ export default class Flecks {
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An async version of `invokeSequential`.
|
||||||
|
*
|
||||||
|
* @see {@link Flecks#invokeSequential}
|
||||||
|
*/
|
||||||
async invokeSequentialAsync(hook, ...args) {
|
async invokeSequentialAsync(hook, ...args) {
|
||||||
if (!this.hooks[hook]) {
|
if (!this.hooks[hook]) {
|
||||||
return [];
|
return [];
|
||||||
|
@ -334,10 +570,18 @@ export default class Flecks {
|
||||||
return results;
|
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) {
|
lookupFlecks(hook) {
|
||||||
const index = hook.indexOf('.');
|
const index = hook.indexOf('.');
|
||||||
if (-1 === index) {
|
if (-1 === index) {
|
||||||
|
@ -346,31 +590,37 @@ export default class Flecks {
|
||||||
return this.get([hook.slice(0, index), hook.slice(index + 1)], ['...']);
|
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) {
|
makeMiddleware(hook) {
|
||||||
debugSilly('makeMiddleware(...): %s', hook);
|
debugSilly('makeMiddleware(...): %s', hook);
|
||||||
if (!this.hooks[hook]) {
|
if (!this.hooks[hook]) {
|
||||||
return Promise.resolve();
|
return (...args) => args.pop()();
|
||||||
}
|
}
|
||||||
const flecks = this.expandedFlecks(hook);
|
const flecks = this.expandedFlecks(hook);
|
||||||
if (0 === flecks.length) {
|
if (0 === flecks.length) {
|
||||||
return Promise.resolve();
|
return (...args) => args.pop()();
|
||||||
}
|
}
|
||||||
const middleware = flecks
|
const middleware = flecks
|
||||||
.filter((fleck) => this.fleckImplements(fleck, hook));
|
.filter((fleck) => this.fleckImplements(fleck, hook));
|
||||||
debugSilly('middleware: %O', middleware);
|
debugSilly('middleware: %O', middleware);
|
||||||
const instance = new Middleware(middleware.map((fleck) => this.invokeFleck(hook, fleck)));
|
const instance = new Middleware(middleware.map((fleck) => this.invokeFleck(hook, fleck)));
|
||||||
return async (...args) => {
|
return instance.dispatch.bind(instance);
|
||||||
const next = args.pop();
|
|
||||||
try {
|
|
||||||
await instance.promise(...args);
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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(
|
static provide(
|
||||||
context,
|
context,
|
||||||
{
|
{
|
||||||
|
@ -393,7 +643,7 @@ export default class Flecks {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
transformer(this.symbolizePath(path)),
|
transformer(this.dasherizePath(path)),
|
||||||
invoke ? M(flecks) : M,
|
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) {
|
refresh(fleck, M) {
|
||||||
debug('refreshing %s...', fleck);
|
debug('refreshing %s...', fleck);
|
||||||
// Remove old hook implementations.
|
// 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);
|
const keys = Object.keys(this.hooks);
|
||||||
for (let j = 0; j < keys.length; j++) {
|
for (let j = 0; j < keys.length; j++) {
|
||||||
const key = keys[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 Class} from './class';
|
||||||
export {default as compose} from './compose';
|
export {default as compose} from './compose';
|
||||||
export {default as D} from './debug';
|
export {default as D} from './debug';
|
||||||
export {default as ensureUniqueReduction} from './ensure-unique-reduction';
|
|
||||||
export {default as EventEmitter} from './event-emitter';
|
export {default as EventEmitter} from './event-emitter';
|
||||||
export {
|
export {
|
||||||
default as Flecks,
|
default as Flecks,
|
||||||
ById,
|
ById,
|
||||||
ByType,
|
ByType,
|
||||||
Hooks,
|
|
||||||
} from './flecks';
|
} from './flecks';
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/core.config': () => ({
|
'@flecks/core.config': () => ({
|
||||||
/**
|
/**
|
||||||
* The ID of your application.
|
* The ID of your application.
|
||||||
*/
|
*/
|
||||||
id: 'flecks',
|
id: 'flecks',
|
||||||
}),
|
}),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -34,6 +34,7 @@ module.exports = {
|
||||||
rules: {
|
rules: {
|
||||||
'babel/object-curly-spacing': 'off',
|
'babel/object-curly-spacing': 'off',
|
||||||
'brace-style': ['error', 'stroustrup'],
|
'brace-style': ['error', 'stroustrup'],
|
||||||
|
'import/prefer-default-export': 'off',
|
||||||
'jsx-a11y/control-has-associated-label': ['error', {assert: 'either'}],
|
'jsx-a11y/control-has-associated-label': ['error', {assert: 'either'}],
|
||||||
'jsx-a11y/label-has-associated-control': ['error', {assert: 'either'}],
|
'jsx-a11y/label-has-associated-control': ['error', {assert: 'either'}],
|
||||||
'no-plusplus': 'off',
|
'no-plusplus': 'off',
|
||||||
|
|
|
@ -22,6 +22,7 @@ const {
|
||||||
FLECKS_CORE_SYNC_FOR_ESLINT = false,
|
FLECKS_CORE_SYNC_FOR_ESLINT = false,
|
||||||
} = process.env;
|
} = process.env;
|
||||||
|
|
||||||
|
// This is kinda nuts, but ESLint doesn't support its configuration files returning a promise!
|
||||||
if (FLECKS_CORE_SYNC_FOR_ESLINT) {
|
if (FLECKS_CORE_SYNC_FOR_ESLINT) {
|
||||||
(async () => {
|
(async () => {
|
||||||
debug('bootstrapping flecks...');
|
debug('bootstrapping flecks...');
|
||||||
|
@ -50,6 +51,7 @@ else {
|
||||||
module.exports = JSON.parse(readFileSync(join(cacheDirectory, 'eslintrc.json')).toString());
|
module.exports = JSON.parse(readFileSync(join(cacheDirectory, 'eslintrc.json')).toString());
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
|
// Just silly. By synchronously spawning... ourselves, the spawned copy can use async.
|
||||||
const {stderr, stdout} = spawnSync('node', [__filename], {
|
const {stderr, stdout} = spawnSync('node', [__filename], {
|
||||||
env: {
|
env: {
|
||||||
FLECKS_CORE_SYNC_FOR_ESLINT: true,
|
FLECKS_CORE_SYNC_FOR_ESLINT: true,
|
||||||
|
|
|
@ -4,7 +4,6 @@ import {inspect} from 'util';
|
||||||
import airbnb from '@neutrinojs/airbnb';
|
import airbnb from '@neutrinojs/airbnb';
|
||||||
import neutrino from 'neutrino';
|
import neutrino from 'neutrino';
|
||||||
|
|
||||||
import {Hooks} from '../flecks';
|
|
||||||
import commands from './commands';
|
import commands from './commands';
|
||||||
import R from '../bootstrap/require';
|
import R from '../bootstrap/require';
|
||||||
|
|
||||||
|
@ -31,8 +30,7 @@ export {default as fleck} from '../bootstrap/fleck';
|
||||||
export {default as require} from '../bootstrap/require';
|
export {default as require} from '../bootstrap/require';
|
||||||
export {JsonStream, transform} from './stream';
|
export {JsonStream, transform} from './stream';
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/core.build': (target, config, flecks) => {
|
'@flecks/core.build': (target, config, flecks) => {
|
||||||
const {
|
const {
|
||||||
'eslint.exclude': exclude,
|
'eslint.exclude': exclude,
|
||||||
|
@ -107,5 +105,4 @@ export default {
|
||||||
*/
|
*/
|
||||||
profile: [],
|
profile: [],
|
||||||
}),
|
}),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
import {expect} from 'chai';
|
import chai from 'chai';
|
||||||
|
import chaiAsPromised from 'chai-as-promised';
|
||||||
|
|
||||||
import {Flecks} from '@flecks/core';
|
import {Flecks} from '@flecks/core';
|
||||||
|
|
||||||
|
chai.use(chaiAsPromised);
|
||||||
|
|
||||||
|
const {expect} = chai;
|
||||||
|
|
||||||
const testOne = require('./one');
|
const testOne = require('./one');
|
||||||
const testTwo = require('./two');
|
const testTwo = require('./two');
|
||||||
|
|
||||||
|
@ -33,3 +38,13 @@ it('can invoke merge async', async () => {
|
||||||
expect(await flecks.invokeMergeAsync('@flecks/core/test/invoke-merge-async'))
|
expect(await flecks.invokeMergeAsync('@flecks/core/test/invoke-merge-async'))
|
||||||
.to.deep.equal({foo: 69, bar: 420});
|
.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
|
// 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 = () => [
|
export const testNodespace = () => [
|
||||||
/* eslint-disable no-eval */
|
/* eslint-disable no-eval */
|
||||||
|
@ -8,8 +8,7 @@ export const testNodespace = () => [
|
||||||
/* eslint-enable no-eval */
|
/* eslint-enable no-eval */
|
||||||
];
|
];
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/core.config': () => ({
|
'@flecks/core.config': () => ({
|
||||||
foo: 'bar',
|
foo: 'bar',
|
||||||
}),
|
}),
|
||||||
|
@ -26,5 +25,11 @@ export default {
|
||||||
},
|
},
|
||||||
'@flecks/core/test/invoke-merge': () => ({foo: 69}),
|
'@flecks/core/test/invoke-merge': () => ({foo: 69}),
|
||||||
'@flecks/core/test/invoke-merge-async': () => new Promise((resolve) => resolve({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 {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/core/one/test-gather': (
|
'@flecks/core/one/test-gather': (
|
||||||
Flecks.provide(require.context('./things', false, /\.js$/))
|
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': () => ({bar: 420}),
|
||||||
'@flecks/core/test/invoke-merge-async': () => new Promise((resolve) => resolve({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 const hooks = {
|
||||||
|
|
||||||
export default {
|
|
||||||
[Hooks]: {
|
|
||||||
/**
|
/**
|
||||||
* Gather database models.
|
* Gather database models.
|
||||||
*
|
*
|
||||||
|
@ -23,6 +20,5 @@ export default {
|
||||||
'@flecks/db/server.models.decorate': (
|
'@flecks/db/server.models.decorate': (
|
||||||
Flecks.decorate(require.context('./models/decorators', false, /\.js$/))
|
Flecks.decorate(require.context('./models/decorators', false, /\.js$/))
|
||||||
),
|
),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import {Hooks} from '@flecks/core';
|
|
||||||
|
|
||||||
import {createDatabaseConnection} from './connection';
|
import {createDatabaseConnection} from './connection';
|
||||||
import containers from './containers';
|
import containers from './containers';
|
||||||
|
|
||||||
|
@ -9,8 +7,7 @@ export {default as Model} from './model';
|
||||||
|
|
||||||
export {createDatabaseConnection};
|
export {createDatabaseConnection};
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/core.config': () => ({
|
'@flecks/core.config': () => ({
|
||||||
/**
|
/**
|
||||||
* The database to connect to.
|
* The database to connect to.
|
||||||
|
@ -42,7 +39,7 @@ export default {
|
||||||
'@flecks/core.starting': (flecks) => {
|
'@flecks/core.starting': (flecks) => {
|
||||||
flecks.set('$flecks/db.models', flecks.gather(
|
flecks.set('$flecks/db.models', flecks.gather(
|
||||||
'@flecks/db/server.models',
|
'@flecks/db/server.models',
|
||||||
{typeAttribute: 'name'},
|
{typeProperty: 'name'},
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
'@flecks/docker.containers': containers,
|
'@flecks/docker.containers': containers,
|
||||||
|
@ -53,5 +50,4 @@ export default {
|
||||||
Models: flecks.get('$flecks/db.models'),
|
Models: flecks.get('$flecks/db.models'),
|
||||||
sequelize: flecks.get('$flecks/db/sequelize'),
|
sequelize: flecks.get('$flecks/db/sequelize'),
|
||||||
}),
|
}),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
import {Hooks} from '@flecks/core';
|
export const hooks = {
|
||||||
|
|
||||||
export default {
|
|
||||||
[Hooks]: {
|
|
||||||
/**
|
/**
|
||||||
* Define docker containers.
|
* Define docker containers.
|
||||||
*
|
*
|
||||||
|
@ -22,6 +19,5 @@ export default {
|
||||||
ports: {3000: 3000},
|
ports: {3000: 3000},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
import {Hooks} from '@flecks/core';
|
|
||||||
|
|
||||||
import commands from './commands';
|
import commands from './commands';
|
||||||
import startContainer from './start-container';
|
import startContainer from './start-container';
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/core.config': () => ({
|
'@flecks/core.config': () => ({
|
||||||
/**
|
/**
|
||||||
* Whether to run docker containers.
|
* Whether to run docker containers.
|
||||||
|
@ -22,5 +19,4 @@ export default {
|
||||||
.map(([key, config]) => startContainer(flecks, key, config)),
|
.map(([key, config]) => startContainer(flecks, key, config)),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,6 +17,7 @@ import {
|
||||||
isObjectExpression,
|
isObjectExpression,
|
||||||
isStringLiteral,
|
isStringLiteral,
|
||||||
isThisExpression,
|
isThisExpression,
|
||||||
|
isVariableDeclaration,
|
||||||
} from '@babel/types';
|
} from '@babel/types';
|
||||||
import {require as R} from '@flecks/core/server';
|
import {require as R} from '@flecks/core/server';
|
||||||
import {parse as parseComment} from 'comment-parser';
|
import {parse as parseComment} from 'comment-parser';
|
||||||
|
@ -75,15 +76,14 @@ class ParserState {
|
||||||
}
|
}
|
||||||
|
|
||||||
const implementationVisitor = (fn) => ({
|
const implementationVisitor = (fn) => ({
|
||||||
ExportDefaultDeclaration(path) {
|
ExportNamedDeclaration(path) {
|
||||||
const {declaration} = path.node;
|
const {declaration} = path.node;
|
||||||
if (isObjectExpression(declaration)) {
|
if (isVariableDeclaration(declaration)) {
|
||||||
const {properties} = declaration;
|
const {declarations} = declaration;
|
||||||
properties.forEach((property) => {
|
declarations.forEach((declarator) => {
|
||||||
const {key, value} = property;
|
if ('hooks' === declarator.id.name) {
|
||||||
if (isIdentifier(key) && key.name === 'Hooks') {
|
if (isObjectExpression(declarator.init)) {
|
||||||
if (isObjectExpression(value)) {
|
const {properties} = declarator.init;
|
||||||
const {properties} = value;
|
|
||||||
properties.forEach((property) => {
|
properties.forEach((property) => {
|
||||||
const {key} = property;
|
const {key} = property;
|
||||||
if (isLiteral(key)) {
|
if (isLiteral(key)) {
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
import {Hooks} from '@flecks/core';
|
|
||||||
|
|
||||||
import commands from './commands';
|
import commands from './commands';
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/core.commands': commands,
|
'@flecks/core.commands': commands,
|
||||||
'@flecks/core.config': () => ({
|
'@flecks/core.config': () => ({
|
||||||
/**
|
/**
|
||||||
|
@ -13,5 +10,4 @@ export default {
|
||||||
*/
|
*/
|
||||||
filenameRewriters: {},
|
filenameRewriters: {},
|
||||||
}),
|
}),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
import {Hooks} from '@flecks/core';
|
export const hooks = {
|
||||||
|
|
||||||
export default {
|
|
||||||
[Hooks]: {
|
|
||||||
/**
|
/**
|
||||||
* Invoked when electron is initializing.
|
* Invoked when electron is initializing.
|
||||||
* @param {Electron.App} app The electron app. See: https://www.electronjs.org/docs/latest/api/app
|
* @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) => {
|
'@flecks/electron/server.window': (win) => {
|
||||||
win.maximize();
|
win.maximize();
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import cluster from 'cluster';
|
import cluster from 'cluster';
|
||||||
import {join} from 'path';
|
import {join} from 'path';
|
||||||
|
|
||||||
import {Hooks} from '@flecks/core';
|
|
||||||
import {require as R} from '@flecks/core/server';
|
import {require as R} from '@flecks/core/server';
|
||||||
import {
|
import {
|
||||||
app,
|
app,
|
||||||
|
@ -21,8 +20,7 @@ async function createWindow(flecks) {
|
||||||
await flecks.invokeSequentialAsync('@flecks/electron/server.window', win);
|
await flecks.invokeSequentialAsync('@flecks/electron/server.window', win);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/core.config': () => ({
|
'@flecks/core.config': () => ({
|
||||||
/**
|
/**
|
||||||
* Browser window options.
|
* Browser window options.
|
||||||
|
@ -135,5 +133,4 @@ export default {
|
||||||
}
|
}
|
||||||
await flecks.invokeSequentialAsync('@flecks/electron/server.initialize', app);
|
await flecks.invokeSequentialAsync('@flecks/electron/server.initialize', app);
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
import {Hooks} from '@flecks/core';
|
|
||||||
|
|
||||||
import commands from './commands';
|
import commands from './commands';
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/core.commands': commands,
|
'@flecks/core.commands': commands,
|
||||||
'@flecks/core.config': () => ({
|
'@flecks/core.config': () => ({
|
||||||
/**
|
/**
|
||||||
|
@ -17,5 +14,4 @@ export default {
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
'@flecks/core.targets': () => ['fleck'],
|
'@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 LimitedPacket from './limited-packet';
|
||||||
import createLimiter from './limiter';
|
import createLimiter from './limiter';
|
||||||
|
|
||||||
export {default as createLimiter} from './limiter';
|
export {default as createLimiter} from './limiter';
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/core.config': () => ({
|
'@flecks/core.config': () => ({
|
||||||
/**
|
/**
|
||||||
* All keys used to determine fingerprint.
|
* All keys used to determine fingerprint.
|
||||||
|
@ -131,5 +130,4 @@ export default {
|
||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
import {Hooks} from '@flecks/core';
|
export const hooks = {
|
||||||
|
|
||||||
export default {
|
|
||||||
[Hooks]: {
|
|
||||||
/**
|
/**
|
||||||
* Define React Providers.
|
* Define React Providers.
|
||||||
*
|
*
|
||||||
|
@ -31,6 +28,5 @@ export default {
|
||||||
// You can also just:
|
// You can also just:
|
||||||
return Component;
|
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 {hydrate, render} from '@hot-loader/react-dom';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
@ -10,8 +10,7 @@ const debug = D('@flecks/react/client');
|
||||||
|
|
||||||
export {FlecksContext};
|
export {FlecksContext};
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/web/client.up': async (flecks) => {
|
'@flecks/web/client.up': async (flecks) => {
|
||||||
const {ssr} = flecks.get('@flecks/react');
|
const {ssr} = flecks.get('@flecks/react');
|
||||||
debug('%sing...', ssr ? 'hydrat' : 'render');
|
debug('%sing...', ssr ? 'hydrat' : 'render');
|
||||||
|
@ -25,5 +24,4 @@ export default {
|
||||||
);
|
);
|
||||||
debug('rendered');
|
debug('rendered');
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import {Hooks} from '@flecks/core';
|
|
||||||
|
|
||||||
export {default as ReactDom} from '@hot-loader/react-dom';
|
export {default as ReactDom} from '@hot-loader/react-dom';
|
||||||
export {default as classnames} from 'classnames';
|
export {default as classnames} from 'classnames';
|
||||||
export {default as PropTypes} from 'prop-types';
|
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 useFlecks} from './hooks/use-flecks';
|
||||||
export {default as usePrevious} from './hooks/use-previous';
|
export {default as usePrevious} from './hooks/use-previous';
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/core.config': () => ({
|
'@flecks/core.config': () => ({
|
||||||
/**
|
/**
|
||||||
* Whether to enable server-side rendering.
|
* Whether to enable server-side rendering.
|
||||||
*/
|
*/
|
||||||
ssr: true,
|
ssr: true,
|
||||||
}),
|
}),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
import {Hooks} from '@flecks/core';
|
|
||||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||||
import {createReduxHistory, history} from '@flecks/react/router/context';
|
import {createReduxHistory, history} from '@flecks/react/router/context';
|
||||||
import {unstable_HistoryRouter as HistoryRouter} from 'react-router-dom';
|
import {unstable_HistoryRouter as HistoryRouter} from 'react-router-dom';
|
||||||
import {HistoryRouter as ReduxHistoryRouter} from 'redux-first-history/rr6';
|
import {HistoryRouter as ReduxHistoryRouter} from 'redux-first-history/rr6';
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/react.providers': (req, flecks) => (
|
'@flecks/react.providers': (req, flecks) => (
|
||||||
flecks.fleck('@flecks/redux')
|
flecks.fleck('@flecks/redux')
|
||||||
? [ReduxHistoryRouter, {history: createReduxHistory(flecks.get('$flecks/redux.store'))}]
|
? [ReduxHistoryRouter, {history: createReduxHistory(flecks.get('$flecks/redux.store'))}]
|
||||||
: [HistoryRouter, {history}]
|
: [HistoryRouter, {history}]
|
||||||
),
|
),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,17 +1,14 @@
|
||||||
import {Hooks} from '@flecks/core';
|
|
||||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||||
import {routerMiddleware, routerReducer} from '@flecks/react/router/context';
|
import {routerMiddleware, routerReducer} from '@flecks/react/router/context';
|
||||||
|
|
||||||
export * from 'react-router-dom';
|
export * from 'react-router-dom';
|
||||||
export * from 'redux-first-history';
|
export * from 'redux-first-history';
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/redux.slices': () => ({
|
'@flecks/redux.slices': () => ({
|
||||||
router: routerReducer,
|
router: routerReducer,
|
||||||
}),
|
}),
|
||||||
'@flecks/redux.store': (options) => {
|
'@flecks/redux.store': (options) => {
|
||||||
options.middleware.push(routerMiddleware);
|
options.middleware.push(routerMiddleware);
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
import {Hooks} from '@flecks/core';
|
|
||||||
import {StaticRouter} from 'react-router-dom/server';
|
import {StaticRouter} from 'react-router-dom/server';
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/react.providers': (req, flecks) => (
|
'@flecks/react.providers': (req, flecks) => (
|
||||||
flecks.get('@flecks/react.ssr') ? [StaticRouter, {location: req.url}] : []
|
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 {augmentBuild} from '@flecks/web/server';
|
||||||
|
|
||||||
import ssr from './ssr';
|
import ssr from './ssr';
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/core.build': (target, config, flecks) => {
|
'@flecks/core.build': (target, config, flecks) => {
|
||||||
// Resolution.
|
// Resolution.
|
||||||
config.use.push(({config}) => {
|
config.use.push(({config}) => {
|
||||||
|
@ -24,5 +22,4 @@ export default {
|
||||||
'@flecks/web/server.stream.html': (stream, req, flecks) => (
|
'@flecks/web/server.stream.html': (stream, req, flecks) => (
|
||||||
flecks.get('@flecks/react.ssr') ? ssr(stream, req, flecks) : stream
|
flecks.get('@flecks/react.ssr') ? ssr(stream, req, flecks) : stream
|
||||||
),
|
),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import {Hooks} from '@flecks/core';
|
|
||||||
|
|
||||||
import containers from './containers';
|
import containers from './containers';
|
||||||
import createClient from './create-client';
|
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 const keys = (client, pattern) => safeKeys(client, pattern, 0);
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/core.config': () => ({
|
'@flecks/core.config': () => ({
|
||||||
/**
|
/**
|
||||||
* Redis server host.
|
* Redis server host.
|
||||||
|
@ -43,5 +40,4 @@ export default {
|
||||||
'@flecks/repl.context': (flecks) => ({
|
'@flecks/repl.context': (flecks) => ({
|
||||||
redisClient: createClient(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 redisAdapter from '@socket.io/redis-adapter';
|
||||||
import ConnectRedis from 'connect-redis';
|
import ConnectRedis from 'connect-redis';
|
||||||
import session from 'express-session';
|
import session from 'express-session';
|
||||||
|
@ -10,8 +10,7 @@ const debugSilly = debug.extend('silly');
|
||||||
|
|
||||||
const RedisStore = ConnectRedis(session);
|
const RedisStore = ConnectRedis(session);
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/user.session': async (flecks) => {
|
'@flecks/user.session': async (flecks) => {
|
||||||
const client = createClient(flecks, {legacyMode: true});
|
const client = createClient(flecks, {legacyMode: true});
|
||||||
await client.connect();
|
await client.connect();
|
||||||
|
@ -28,5 +27,4 @@ export default {
|
||||||
adapter: redisAdapter(pubClient, subClient),
|
adapter: redisAdapter(pubClient, subClient),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
import {Hooks} from '@flecks/core';
|
export const hooks = {
|
||||||
|
|
||||||
export default {
|
|
||||||
[Hooks]: {
|
|
||||||
/**
|
/**
|
||||||
* Define side-effects to run against Redux actions.
|
* Define side-effects to run against Redux actions.
|
||||||
*/
|
*/
|
||||||
|
@ -40,6 +37,5 @@ export default {
|
||||||
options.enhancers.splice(someIndex, 1);
|
options.enhancers.splice(someIndex, 1);
|
||||||
options.middleware.push(mySpecialMiddleware);
|
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 {Provider} from 'react-redux';
|
||||||
|
|
||||||
import configureStore, {createReducer} from '../store';
|
import configureStore, {createReducer} from '../store';
|
||||||
import localStorageEnhancer from './local-storage';
|
import localStorageEnhancer from './local-storage';
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/react.providers': async (req, flecks) => {
|
'@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);
|
const reducer = createReducer(flecks, slices);
|
||||||
// Hydrate from server.
|
// Hydrate from server.
|
||||||
const {preloadedState} = flecks.get('@flecks/redux/client');
|
const {preloadedState} = flecks.get('@flecks/redux/client');
|
||||||
|
@ -22,5 +21,4 @@ export default {
|
||||||
'@flecks/socket.packets.decorate': (
|
'@flecks/socket.packets.decorate': (
|
||||||
Flecks.decorate(require.context('./packets/decorators', false, /\.js$/))
|
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 '@reduxjs/toolkit';
|
||||||
export * from 'react-redux';
|
export * from 'react-redux';
|
||||||
|
|
||||||
export * from './actions';
|
export * from './actions';
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/socket.packets': Flecks.provide(require.context('./packets', false, /\.js$/)),
|
'@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 {Provider} from 'react-redux';
|
||||||
|
|
||||||
import {hydrateServer} from './actions';
|
import {hydrateServer} from './actions';
|
||||||
|
@ -8,10 +8,9 @@ import configureStore from './store';
|
||||||
const debug = D('@flecks/redux/server');
|
const debug = D('@flecks/redux/server');
|
||||||
const debugSilly = debug.extend('silly');
|
const debugSilly = debug.extend('silly');
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/web/server.request.route': (flecks) => async (req, res, next) => {
|
'@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);
|
const reducer = createReducer(flecks, slices);
|
||||||
// Let the slices have a(n async) chance to hydrate with server data.
|
// Let the slices have a(n async) chance to hydrate with server data.
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
|
@ -32,5 +31,4 @@ export default {
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
'@flecks/react.providers': (req) => [Provider, {store: req.redux}],
|
'@flecks/react.providers': (req) => [Provider, {store: req.redux}],
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
import {Hooks} from '@flecks/core';
|
export const hooks = {
|
||||||
|
|
||||||
export default {
|
|
||||||
[Hooks]: {
|
|
||||||
/**
|
/**
|
||||||
* Define REPL commands.
|
* Define REPL commands.
|
||||||
*
|
*
|
||||||
|
@ -25,6 +22,5 @@ export default {
|
||||||
someValue: 'foobar',
|
someValue: 'foobar',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
import {Hooks} from '@flecks/core';
|
|
||||||
|
|
||||||
import commands from './commands';
|
import commands from './commands';
|
||||||
import {createReplServer} from './repl';
|
import {createReplServer} from './repl';
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/core.commands': commands,
|
'@flecks/core.commands': commands,
|
||||||
'@flecks/server.up': (flecks) => createReplServer(flecks),
|
'@flecks/server.up': (flecks) => createReplServer(flecks),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
import {Hooks} from '@flecks/core';
|
export const hooks = {
|
||||||
|
|
||||||
export default {
|
|
||||||
[Hooks]: {
|
|
||||||
/**
|
/**
|
||||||
* Define sequential actions to run when the server comes up.
|
* Define sequential actions to run when the server comes up.
|
||||||
*/
|
*/
|
||||||
'@flecks/server.up': async () => {
|
'@flecks/server.up': async () => {
|
||||||
await youCanDoAsyncThingsHere();
|
await youCanDoAsyncThingsHere();
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -33,7 +33,8 @@ const {version} = require('../package.json');
|
||||||
rcs,
|
rcs,
|
||||||
});
|
});
|
||||||
try {
|
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!');
|
debug('up!');
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
import {Hooks} from '@flecks/core';
|
export const hooks = {
|
||||||
|
|
||||||
export default {
|
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/core.config': () => ({
|
'@flecks/core.config': () => ({
|
||||||
/**
|
/**
|
||||||
* Whether HMR is enabled.
|
* Whether HMR is enabled.
|
||||||
|
@ -24,5 +21,4 @@ export default {
|
||||||
modules: false,
|
modules: false,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
import {Hooks} from '@flecks/core';
|
export const hooks = {
|
||||||
|
|
||||||
export default {
|
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/core.targets': () => ['server'],
|
'@flecks/core.targets': () => ['server'],
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
import {Hooks} from '@flecks/core';
|
export const hooks = {
|
||||||
|
|
||||||
export default {
|
|
||||||
[Hooks]: {
|
|
||||||
/**
|
/**
|
||||||
* Modify Socket.io client configuration.
|
* Modify Socket.io client configuration.
|
||||||
*
|
*
|
||||||
|
@ -60,6 +57,5 @@ export default {
|
||||||
// Express-style route middleware...
|
// Express-style route middleware...
|
||||||
next();
|
next();
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
import {Hooks} from '@flecks/core';
|
|
||||||
|
|
||||||
import SocketClient from './socket';
|
import SocketClient from './socket';
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/web/client.up': (flecks) => {
|
'@flecks/web/client.up': (flecks) => {
|
||||||
const socket = new SocketClient(flecks);
|
const socket = new SocketClient(flecks);
|
||||||
flecks.set('$flecks/socket.socket', socket);
|
flecks.set('$flecks/socket.socket', socket);
|
||||||
|
@ -16,5 +13,4 @@ export default {
|
||||||
},
|
},
|
||||||
path: `/${id}/socket.io`,
|
path: `/${id}/socket.io`,
|
||||||
}),
|
}),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import {Hooks} from '@flecks/core';
|
|
||||||
|
|
||||||
import badPacketsCheck from './packet/bad-packets-check';
|
import badPacketsCheck from './packet/bad-packets-check';
|
||||||
import Bundle from './packet/bundle';
|
import Bundle from './packet/bundle';
|
||||||
import Redirect from './packet/redirect';
|
import Redirect from './packet/redirect';
|
||||||
|
@ -9,8 +7,7 @@ export {default as normalize} from './normalize';
|
||||||
export * from './hooks';
|
export * from './hooks';
|
||||||
export {default as Packet, Packer, ValidationError} from './packet';
|
export {default as Packet, Packer, ValidationError} from './packet';
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/core.starting': (flecks) => {
|
'@flecks/core.starting': (flecks) => {
|
||||||
flecks.set('$flecks/socket.packets', flecks.gather(
|
flecks.set('$flecks/socket.packets', flecks.gather(
|
||||||
'@flecks/socket.packets',
|
'@flecks/socket.packets',
|
||||||
|
@ -32,5 +29,4 @@ export default {
|
||||||
Redirect,
|
Redirect,
|
||||||
Refresh,
|
Refresh,
|
||||||
}),
|
}),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
import {Hooks} from '@flecks/core';
|
|
||||||
|
|
||||||
import createIntercom from './create-intercom';
|
import createIntercom from './create-intercom';
|
||||||
import Sockets from './sockets';
|
import Sockets from './sockets';
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/web/server.request.socket': ({config: {'$flecks/socket.sockets': sockets}}) => (req, res, next) => {
|
'@flecks/web/server.request.socket': ({config: {'$flecks/socket.sockets': sockets}}) => (req, res, next) => {
|
||||||
req.intercom = createIntercom(sockets, 'web');
|
req.intercom = createIntercom(sockets, 'web');
|
||||||
next();
|
next();
|
||||||
|
@ -21,5 +18,4 @@ export default {
|
||||||
'@flecks/socket.server': ({config: {'@flecks/core': {id}}}) => ({
|
'@flecks/socket.server': ({config: {'@flecks/core': {id}}}) => ({
|
||||||
path: `/${id}/socket.io`,
|
path: `/${id}/socket.io`,
|
||||||
}),
|
}),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
import {Hooks} from '@flecks/core';
|
export const hooks = {
|
||||||
|
|
||||||
export default {
|
|
||||||
[Hooks]: {
|
|
||||||
/**
|
/**
|
||||||
* Modify express-session configuration.
|
* Modify express-session configuration.
|
||||||
*
|
*
|
||||||
|
@ -10,6 +7,4 @@ export default {
|
||||||
'@flecks/user.session': () => ({
|
'@flecks/user.session': () => ({
|
||||||
saveUninitialized: true,
|
saveUninitialized: true,
|
||||||
}),
|
}),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
import {Hooks} from '@flecks/core';
|
|
||||||
|
|
||||||
import {Logout} from './packets';
|
import {Logout} from './packets';
|
||||||
|
|
||||||
import {user, users} from './state';
|
import {user, users} from './state';
|
||||||
|
|
||||||
export * from './state';
|
export * from './state';
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/redux.slices': () => ({
|
'@flecks/redux.slices': () => ({
|
||||||
user,
|
user,
|
||||||
users,
|
users,
|
||||||
|
@ -15,5 +12,4 @@ export default {
|
||||||
'@flecks/socket.packets': (flecks) => ({
|
'@flecks/socket.packets': (flecks) => ({
|
||||||
Logout: Logout(flecks),
|
Logout: Logout(flecks),
|
||||||
}),
|
}),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import {randomBytes} from 'crypto';
|
import {randomBytes} from 'crypto';
|
||||||
|
|
||||||
import {Flecks, Hooks} from '@flecks/core';
|
import {Flecks} from '@flecks/core';
|
||||||
import passport from 'passport';
|
import passport from 'passport';
|
||||||
import LocalStrategy from 'passport-local';
|
import LocalStrategy from 'passport-local';
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/core.config': () => ({
|
'@flecks/core.config': () => ({
|
||||||
/**
|
/**
|
||||||
* Path to redirect to after failed login.
|
* 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 passport from 'passport';
|
||||||
import LogOps from 'passport/lib/http/request';
|
import LogOps from 'passport/lib/http/request';
|
||||||
|
|
||||||
const debug = D('@flecks/user/passport');
|
const debug = D('@flecks/user/passport');
|
||||||
const debugSilly = debug.extend('silly');
|
const debugSilly = debug.extend('silly');
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/db/server.models': Flecks.provide(require.context('./models', false, /\.js$/)),
|
'@flecks/db/server.models': Flecks.provide(require.context('./models', false, /\.js$/)),
|
||||||
'@flecks/web/server.request.route': (flecks) => (req, res, next) => {
|
'@flecks/web/server.request.route': (flecks) => (req, res, next) => {
|
||||||
debugSilly('@flecks/web/server.request.route: passport.initialize()');
|
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 express from 'express';
|
||||||
import expressSession from 'express-session';
|
import expressSession from 'express-session';
|
||||||
|
|
||||||
const debug = D('@flecks/user/session');
|
const debug = D('@flecks/user/session');
|
||||||
const debugSilly = debug.extend('silly');
|
const debugSilly = debug.extend('silly');
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/core.config': () => ({
|
'@flecks/core.config': () => ({
|
||||||
/**
|
/**
|
||||||
* Set the cookie secret for session encryption.
|
* Set the cookie secret for session encryption.
|
||||||
|
@ -55,5 +54,4 @@ export default {
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
import {Hooks} from '@flecks/core';
|
export const hooks = {
|
||||||
|
|
||||||
export default {
|
|
||||||
[Hooks]: {
|
|
||||||
/**
|
/**
|
||||||
* Define sequential actions to run when the client comes up.
|
* Define sequential actions to run when the client comes up.
|
||||||
*/
|
*/
|
||||||
|
@ -58,5 +55,4 @@ export default {
|
||||||
'@flecks/web/server.up': async () => {
|
'@flecks/web/server.up': async () => {
|
||||||
await youCanDoAsyncThingsHere();
|
await youCanDoAsyncThingsHere();
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {D, Flecks} from '@flecks/core';
|
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');
|
const {version} = require('@flecks/web/package.json');
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
|
@ -56,7 +56,8 @@ const {version} = require('@flecks/web/package.json');
|
||||||
const flecks = new Flecks(runtime);
|
const flecks = new Flecks(runtime);
|
||||||
window.flecks = flecks;
|
window.flecks = flecks;
|
||||||
try {
|
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';
|
window.document.querySelector('#root').style.display = 'block';
|
||||||
debug('up!');
|
debug('up!');
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import {stat, unlink} from 'fs/promises';
|
import {stat, unlink} from 'fs/promises';
|
||||||
import {join} from 'path';
|
import {join} from 'path';
|
||||||
|
|
||||||
import {D, Hooks} from '@flecks/core';
|
import {D} from '@flecks/core';
|
||||||
import {Flecks, spawnWith} from '@flecks/core/server';
|
import {Flecks, spawnWith} from '@flecks/core/server';
|
||||||
|
|
||||||
import augmentBuild from './augment-build';
|
import augmentBuild from './augment-build';
|
||||||
|
@ -16,8 +16,7 @@ const debug = D('@flecks/web/server');
|
||||||
|
|
||||||
export {augmentBuild};
|
export {augmentBuild};
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
[Hooks]: {
|
|
||||||
'@flecks/core.build': augmentBuild,
|
'@flecks/core.build': augmentBuild,
|
||||||
'@flecks/core.build.alter': async (neutrinoConfigs, flecks) => {
|
'@flecks/core.build.alter': async (neutrinoConfigs, flecks) => {
|
||||||
// Don't build if there's a fleck target.
|
// Don't build if there's a fleck target.
|
||||||
|
@ -210,5 +209,4 @@ export default {
|
||||||
'@flecks/repl.context': (flecks) => ({
|
'@flecks/repl.context': (flecks) => ({
|
||||||
httpServer: flecks.get('$flecks/web/server.instance'),
|
httpServer: flecks.get('$flecks/web/server.instance'),
|
||||||
}),
|
}),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue
Block a user