Hooks
Hooks are how everything happens in flecks. There are many hooks and the hooks provided by flecks are documented at the hooks reference page.
Implementing (your and other) hooks
To implement hooks (and turn your plain ol' boring JS modules into beautiful interesting flecks),
you only have to export a hooks
object:
export const hooks = {
'@flecks/core.starting': () => {
console.log('hello, gorgeous');
},
};
An even better way to hook
You may also implement hooks using Flecks.hooks
and require.context
.
If you are implementing many hooks in a single file, things can start to get cluttered. There is a better way to structure your hooks:
export const hooks = Flecks.hooks(require.context('./hooks'));
Supposing you implemented the above in index.js
and had a file structure like so:
src
├─ index.js
└─ hooks/
└─ @flecks/
└─ core.starting.js
and the content of core.starting.js
was as follows:
export const hook = () => {
console.log('hello, gorgeous');
};
The result would be exactly equivalent to the hooks
object definition above.
For a real example of this, see the flecks source for examples here or here.
You may even use require.context
for your hooks within flecks.bootstrap.js
! Even though
it's a CommonJS environment, pirates is used for that little
bit of magic. (If that scares you, don't worry — it's only during the build process.)
Ergonomics matter!
Some hooks (e.g. @flecks/server.up
) are tracked by watching @flecks/core.hmr.hook
and will
restart the application when any implementations are changed.
If you structure your hooks using Flecks.hooks
, HMR will only refresh the hooks that
changed. If you export a simple object, all hooks will appear to change on any change. This
may lead to more restarts than necessary for a given change.
Invoking your hooks
Hooks may be invoked using different invocation methods which may affect the order of invocation as well as the final result.
All methods accept an arbitrary number of arguments after the specified arguments.
All methods pass the flecks
instance as the last argument. You never have to include it.
invoke
invokeAsync
Invokes all hook implementations and returns the results keyed by the implementing flecks' paths.
hook: string
The hook to invoke.
invokeFleck
Invoke a single fleck's hook implementation and return the result.
hook: string
The hook to invoke.
fleck: string
The fleck whose hook to invoke.
invokeFlat
Invokes all hook implementations and returns the results as an array.
hook: string
The hook to invoke.
The following test would pass:
expect(flecks.invokeFlat('some-hook'))
.to.deep.equal(Object.values(flecks.invoke('some-hook')));
invokeComposed
invokeComposedAsync
See: function composition.
initial
is passed to the first implementation, which returns a result which is passed to the
second implementation, which returns a result which is passed to the third implementation, etc.
hook: string
The hook to invoke.
initial: any
The initial value.
invokeMerge
invokeMergeAsync
Invokes all hook implementations and returns the result of merging all implementations' returned objects together.
hook: string
The hook to invoke.
invokeMergeUnique
invokeMergeUniqueAsync
Specialization of invokeMerge
that will throw an error if any keys overlap.
hook: string
The hook to invoke.
invokeReduce
invokeReduceAsync
Invokes hook implementations one at a time, their results being passed to the reducer as currentValue
. Returns the final reduction.
hook: string
The hook to invoke.
reduce: function
The reducer function.
initial: any
The initial value.
invokeSequential
invokeSequentialAsync
Invokes all hook implementations, one after another. In the async variant, each implementation's result is await
ed before invoking the next implementation.
hook: string
The hook to invoke.
makeMiddleware
Hooks may be implemented in the style of Express middleware.
Each implementation will be expected to accept 0 or more arguments followed by a next
function
which the implementation invokes when passing execution on to the next implementation.
Usage with express would look something like:
app.use(flecks.makeMiddleware('@my/fleck.hook'));
For more information, see: http://expressjs.com/en/guide/using-middleware.html
Documenting your hooks
You should document the hooks you define for the benefit of others (and future you)! You may do so
in build/flecks.hooks.js
within your fleck. If you define a hook called @my/fleck.alarm
, you
might write:
export const hooks = {
/**
* Ring an alarm.
*
* @param {number} ding The dinger.
* @param {string} dong The donger.
* @returns {boolean} Whether the ding was donged.
*
* @invoke Async
*/
'@my/fleck.alarm': (ding, dong) => {
// Here's some example usage.
},
}
This is how the generated hooks page gathers data to generate the page. You can even take advantage of this for your project!