Skip to main content

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!

Coming in hot (and correct)

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.

Catch you on the flip

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.

Just a spoonful of sugar

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

See: Array.prototype.reduce()

Invokes hook implementations one at a time, their results being passed to the reducer as currentValue. Returns the final reduction.

hook: string

The hook to invoke.

reduce: function

The reducer function.

initial: any

The initial value.

invokeSequential

invokeSequentialAsync

Invokes all hook implementations, one after another. In the async variant, each implementation's result is awaited 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:

build/flecks.hooks.js
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!