{"id":13672928,"url":"https://github.com/unjs/hookable","last_synced_at":"2025-06-10T22:41:33.555Z","repository":{"id":40353184,"uuid":"119112302","full_name":"unjs/hookable","owner":"unjs","description":"🪝 Awaitable Hooks","archived":false,"fork":false,"pushed_at":"2025-04-04T09:56:31.000Z","size":1227,"stargazers_count":816,"open_issues_count":15,"forks_count":31,"subscribers_count":8,"default_branch":"main","last_synced_at":"2025-04-24T23:45:34.635Z","etag":null,"topics":["hook","hookable","node","tapable","tappable"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/unjs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-01-26T22:58:21.000Z","updated_at":"2025-04-24T17:43:57.000Z","dependencies_parsed_at":"2023-12-20T06:43:02.485Z","dependency_job_id":"420dee0d-a3db-44e8-9911-314151476030","html_url":"https://github.com/unjs/hookable","commit_stats":{"total_commits":198,"total_committers":16,"mean_commits":12.375,"dds":0.6616161616161615,"last_synced_commit":"dc85dadf7624d171a186fe0ec662efeb5886d815"},"previous_names":["jsless/hable"],"tags_count":44,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unjs%2Fhookable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unjs%2Fhookable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unjs%2Fhookable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unjs%2Fhookable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/unjs","download_url":"https://codeload.github.com/unjs/hookable/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250771334,"owners_count":21484536,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hook","hookable","node","tapable","tappable"],"created_at":"2024-08-02T09:01:57.951Z","updated_at":"2025-04-28T04:30:33.990Z","avatar_url":"https://github.com/unjs.png","language":"TypeScript","readme":"# Hookable\n\n[![npm version][npm-version-src]][npm-version-href]\n[![npm downloads][npm-downloads-src]][npm-downloads-href]\n[![bundle][bundle-src]][bundle-href]\n[![Codecov][codecov-src]][codecov-href]\n[![License][license-src]][license-href]\n\nAwaitable hooks system.\n\n## Install\n\nUsing yarn:\n\n```bash\nyarn add hookable\n```\n\nUsing npm:\n\n```bash\nnpm install hookable\n```\n\n## Usage\n\n**Method A: Create a hookable instance:**\n\n```js\nimport { createHooks } from 'hookable'\n\n// Create a hookable instance\nconst hooks = createHooks()\n\n// Hook on 'hello'\nhooks.hook('hello', () =\u003e { console.log('Hello World' )})\n\n// Call 'hello' hook\nhooks.callHook('hello')\n```\n\n**Method B: Extend your base class from Hookable:**\n\n```js\nimport { Hookable } from 'hookable'\n\nexport default class FooLib extends Hookable {\n  constructor() {\n    // Call to parent to initialize\n    super()\n    // Initialize Hookable with custom logger\n    // super(consola)\n  }\n\n  async someFunction() {\n    // Call and wait for `hook1` hooks (if any) sequential\n    await this.callHook('hook1')\n  }\n}\n```\n\n**Inside plugins, register for any hook:**\n\n```js\nconst lib = new FooLib()\n\n// Register a handler for `hook2`\nlib.hook('hook2', async () =\u003e { /* ... */ })\n\n// Register multiply handlers at once\nlib.addHooks({\n  hook1: async () =\u003e { /* ... */ },\n  hook2: [ /* can be also an array */ ]\n})\n```\n\n**Unregistering hooks:**\n\n```js\nconst lib = new FooLib()\n\nconst hook0 = async () =\u003e { /* ... */ }\nconst hook1 = async () =\u003e { /* ... */ }\nconst hook2 = async () =\u003e { /* ... */ }\n\n// The hook() method returns an \"unregister\" function\nconst unregisterHook0 = lib.hook('hook0', hook0)\nconst unregisterHooks1and2 = lib.addHooks({ hook1, hook2 })\n\n/* ... */\n\nunregisterHook0()\nunregisterHooks1and2()\n\n// or\n\nlib.removeHooks({ hook0, hook1 })\nlib.removeHook('hook2', hook2)\n```\n\n**Triggering a hook handler once:**\n\n```js\nconst lib = new FooLib()\n\nconst unregister = lib.hook('hook0', async () =\u003e {\n  // Unregister as soon as the hook is executed\n  unregister()\n\n  /* ... */\n})\n```\n\n\n## Hookable class\n\n### `constructor()`\n\n### `hook (name, fn)`\n\nRegister a handler for a specific hook. `fn` must be a function.\n\nReturns an `unregister` function that, when called, will remove the registered handler.\n\n### `hookOnce (name, fn)`\n\nSimilar to `hook` but unregisters hook once called.\n\nReturns an `unregister` function that, when called, will remove the registered handler before first call.\n\n### `addHooks(configHooks)`\n\nFlatten and register hooks object.\n\nExample:\n\n```js\nhookable.addHooks({\n  test: {\n    before: () =\u003e {},\n    after: () =\u003e {}\n  }\n})\n\n```\n\nThis registers `test:before` and `test:after` hooks at bulk.\n\nReturns an `unregister` function that, when called, will remove all the registered handlers.\n\n### `async callHook (name, ...args)`\n\nUsed by class itself to **sequentially** call handlers of a specific hook.\n\n### `callHookWith (name, callerFn)`\n\nIf you need custom control over how hooks are called, you can provide a custom function that will receive an array of handlers of a specific hook.\n\n`callerFn` if a callback function that accepts two arguments, `hooks` and `args`:\n- `hooks`: Array of user hooks to be called\n- `args`: Array of arguments that should be passed each time calling a hook\n\n### `deprecateHook (old, name)`\n\nDeprecate hook called `old` in favor of `name` hook.\n\n### `deprecateHooks (deprecatedHooks)`\n\nDeprecate all hooks from an object (keys are old and values or newer ones).\n\n### `removeHook (name, fn)`\n\nRemove a particular hook handler, if the `fn` handler is present.\n\n### `removeHooks (configHooks)`\n\nRemove multiple hook handlers.\n\nExample:\n\n```js\nconst handler = async () =\u003e { /* ... */ }\n\nhookable.hook('test:before', handler)\nhookable.addHooks({ test: { after: handler } })\n\n// ...\n\nhookable.removeHooks({\n  test: {\n    before: handler,\n    after: handler\n  }\n})\n```\n\n### `removeAllHooks`\n\nRemove all hook handlers.\n\n### `beforeEach (syncCallback)`\n\nRegisters a (sync) callback to be called before each hook is being called.\n\n```js\nhookable.beforeEach((event) =\u003e { console.log(`${event.name} hook is being called with ${event.args}`)})\nhookable.hook('test', () =\u003e { console.log('running test hook') })\n\n// test hook is being called with []\n// running test hook\nawait hookable.callHook('test')\n```\n\n### `afterEach (syncCallback)`\n\nRegisters a (sync) callback to be called after each hook is being called.\n\n```js\nhookable.afterEach((event) =\u003e { console.log(`${event.name} hook called with ${event.args}`)})\nhookable.hook('test', () =\u003e { console.log('running test hook') })\n\n// running test hook\n// test hook called with []\nawait hookable.callHook('test')\n```\n\n### `createDebugger`\n\nAutomatically logs each hook that is called and how long it takes to run.\n\n```js\nconst debug = hookable.createDebugger(hooks, { tag: 'something' })\n\nhooks.callHook('some-hook', 'some-arg')\n// [something] some-hook: 0.21ms\n\ndebug.close()\n```\n\n## Migration\n\n### From `4.x` to `5.x`\n\n- Type checking improved. You can use `Hookable\u003cT\u003e` or `createHooks\u003cT\u003e()` to provide types interface **([c2e1e22](https://github.com/unjs/hookable/commit/c2e1e223d16e7bf87117cd8d72ad3ba211a333d8))**\n- We no longer provide an IE11 compatible umd build. Instead, you should use an ESM-aware bundler such as webpack or rollup to transpile if needed.\n- Logger param is dropped. We use `console.warn` by default for deprecated hooks.\n- Package now uses named exports. You should import `{ Hookable }` instead of  `Hookable` or use new `createHooks` util\n- `mergeHooks` util is exported standalone. You should replace `Hookable.mergeHooks` and `this.mergeHooks` with new `{ mergeHooks }` export\n- In versions \u003c 5.0.0 when using `callHook` if an error happened by one of the hook callbacks, we was handling errors globally and call global `error` hook + `console.error` instead and resolve `callHook` promise!  This sometimes makes confusing behavior when we think code worked but it didn't. v5 introduced a breaking change that when a hook throws an error, `callHook` also rejects instead of a global `error` event. This means you should be careful to handle all errors when using `callHook` now.\n\n## Credits\n\nExtracted from [Nuxt](https://github.com/nuxt/nuxt.js) hooks system originally introduced by [Sébastien Chopin](https://github.com/Atinux)\n\nThanks to [Joe Paice](https://github.com/RGBboy) for donating [hookable](https://www.npmjs.com/package/hookable) package name.\n\n## License\n\nMIT - Made with 💖\n\n\u003c!-- Badges --\u003e\n[npm-version-src]: https://img.shields.io/npm/v/hookable?style=flat\u0026colorA=18181B\u0026colorB=F0DB4F\n[npm-version-href]: https://npmjs.com/package/hookable\n[npm-downloads-src]: https://img.shields.io/npm/dm/hookable?style=flat\u0026colorA=18181B\u0026colorB=F0DB4F\n[npm-downloads-href]: https://npmjs.com/package/hookable\n[codecov-src]: https://img.shields.io/codecov/c/gh/unjs/hookable/main?style=flat\u0026colorA=18181B\u0026colorB=F0DB4F\n[codecov-href]: https://codecov.io/gh/unjs/h3\n[bundle-src]: https://img.shields.io/bundlephobia/minzip/hookable?style=flat\u0026colorA=18181B\u0026colorB=F0DB4F\n[bundle-href]: https://bundlephobia.com/result?p=hookable\n[license-src]: https://img.shields.io/github/license/unjs/hookable.svg?style=flat\u0026colorA=18181B\u0026colorB=F0DB4F\n[license-href]: https://github.com/unjs/hookable/blob/main/LICENSE\n","funding_links":[],"categories":["TypeScript","node"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funjs%2Fhookable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Funjs%2Fhookable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funjs%2Fhookable/lists"}