Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/nylen/node-wp-hooks

WordPress actions and filters in JS
https://github.com/nylen/node-wp-hooks

Last synced: 19 days ago
JSON representation

WordPress actions and filters in JS

Awesome Lists containing this project

README

        

# @nylen/wp-hooks [![Build Status](https://img.shields.io/travis/nylen/node-wp-hooks/master.svg)](https://travis-ci.org/nylen/node-wp-hooks) [![Coverage](https://img.shields.io/coveralls/nylen/node-wp-hooks/master.svg)](https://coveralls.io/github/nylen/node-wp-hooks)

**WordPress-style hooks** (_actions_ and _filters_) for Node.js.

Recent (5.x) versions of WordPress also include a JavaScript actions and
filters library. **It is not exactly the same as this library** - this
library shares history with the WordPress version, but it is simpler and is
mainly intended for usage in Node.js server programs.

When combined with a mechanism for plugin registration and loading (outside the
scope of this library), this is a simple, effective pattern that allows
modifying values or executing other code at key parts in your app.

More information about actions and filters as they are used in WordPress:
https://developer.wordpress.org/plugins/hooks/

## Usage

```js
const WPHooks = require( '@nylen/wp-hooks' );
const hooks = new WPHooks();
```

* **hooks.addAction( 'identifier', callback, priority )** - Add a function to the
action given by `identifier`. Actions do not have return values.
* **hooks.addFilter( 'identifier', callback, priority )** - Add a function to the
filter given by `identifier`. Filters have return values, and functions
hooked into a filter can modify its return value.
* **hooks.removeAction( 'identifier', callback )** - Remove an action.
* **hooks.removeFilter( 'identifier', callback )** - Remove a filter.
* **hooks.removeAllActions( 'identifier' )** - Remove all actions from the given
`identifier`.
* **hooks.removeAllFilters( 'identifier' )** - Remove all filters from the given
`identifier`.
* **hooks.doAction( 'identifier', arg1, ... )** - Run an action along with any
functions hooked into it.
* **hooks.applyFilters( 'identifier', arg1, ... )** - Run a filter along with any
functions hooked into it, and return the default value or the value from the
last function.
* **hooks.doingAction( 'identifier' )** - Whether this object is currently
executing the action with the name `identifier`.
* **hooks.doingFilter( 'identifier' )** - Whether this object is currently
executing the filter with the name `identifier`.
* **hooks.didAction( 'identifier' )** - Whether this object has executed the
action with the name `identifier`.
* **hooks.didFilter( 'identifier' )** - Whether this object has executed the
filter with the name `identifier`.
* **hooks.hasAction( 'identifier' )** - Whether this object has any functions
hooked into the action with the name `identifier`.
* **hooks.hasFilter( 'identifier' )** - Whether this object has any functions
hooked into the filter with the name `identifier`.

In large apps, it is a good idea to enforce separation of different types of
hooks by either prefixing `identifier` hook names with `namespace.identifier`,
or (even better) using separate `hooks` objects for each part of the app.

## Sync or Async?

All filters and actions are **synchronous** by default, and this library
contains no special code for async callbacks or promises.

However, because `async` functions just return `Promise` objects under the
covers, you can easily pass a `Promise` through a chain of hooks. If you
follow a couple of simple rules, then the final result from `applyFilters` will
be a promise that resolves or fails when all of its attached functions have
finished processing.

For hooks that may require asynchronous behavior, the app must use a **filter**
rather than an **action**. Then the app can start the filter chain normally,
and just `await` the final result, which will be a `Promise` returned from the
last function in the filter chain:

```js
const finalValue = await applyFilters(
'my_async_code_path',
'defaultValue'
);
```

Then, individual filters can be written as `async` functions, as long as they
adhere to a simple convention: **Accept a `Promise` as an argument and `await`
it before returning.**

```js
addFilter( 'my_async_code_path', async p => {
const value = await p;
return value + '/modified';
} );
```

This allows for any function in the chain of filters to return a `Promise`,
which other functions will wait for before doing their processing. (It's fine
to `await` a non-`Promise` value too.)

See `index.test.js` for more examples and information.

Future **major versions** of this library may change the behavior around
asynchronous computations. Currently this mostly works (with the exception of
`doingFilter` and `didFilter`), but requires adhering to a non-obvious
convention and can be difficult to understand and use effectively.