diff --git a/packages/hooks/package.json b/packages/hooks/package.json index 54b8d461fc260d..d13101c077dd4b 100644 --- a/packages/hooks/package.json +++ b/packages/hooks/package.json @@ -21,6 +21,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "types": "build-types", "dependencies": { "@babel/runtime": "^7.11.2" }, diff --git a/packages/hooks/src/createAddHook.js b/packages/hooks/src/createAddHook.js index 3d545a2e824cfc..49bc4046b3ca91 100644 --- a/packages/hooks/src/createAddHook.js +++ b/packages/hooks/src/createAddHook.js @@ -6,21 +6,23 @@ import validateHookName from './validateHookName.js'; import { doAction } from './'; /** - * Returns a function which, when invoked, will add a hook. + * @callback AddHook + * + * Adds the hook to the appropriate hooks container. * - * @param {Object} hooks Stored hooks, keyed by hook name. + * @param {string} hookName Name of hook to add + * @param {string} namespace The unique namespace identifying the callback in the form `vendor/plugin/function`. + * @param {import('.').Callback} callback Function to call when the hook is run + * @param {?number} priority Priority of this hook (default=10) + */ + +/** + * Returns a function which, when invoked, will add a hook. * - * @return {Function} Function that adds a new hook. + * @param {import('.').Hooks} hooks Stored hooks, keyed by hook name. + * @return {AddHook} Function that adds a new hook. */ function createAddHook( hooks ) { - /** - * Adds the hook to the appropriate hooks container. - * - * @param {string} hookName Name of hook to add - * @param {string} namespace The unique namespace identifying the callback in the form `vendor/plugin/function`. - * @param {Function} callback Function to call when the hook is run - * @param {?number} priority Priority of this hook (default=10) - */ return function addHook( hookName, namespace, callback, priority = 10 ) { if ( ! validateHookName( hookName ) ) { return; @@ -51,6 +53,7 @@ function createAddHook( hooks ) { // Find the correct insert index of the new hook. const handlers = hooks[ hookName ].handlers; + /** @type {number} */ let i; for ( i = handlers.length; i > 0; i-- ) { if ( priority >= handlers[ i - 1 ].priority ) { @@ -70,7 +73,7 @@ function createAddHook( hooks ) { // we're adding would come after the current callback, there's no // problem; otherwise we need to increase the execution index of // any other runs by 1 to account for the added element. - ( hooks.__current || [] ).forEach( ( hookInfo ) => { + hooks.__current.forEach( ( hookInfo ) => { if ( hookInfo.name === hookName && hookInfo.currentIndex >= i diff --git a/packages/hooks/src/createCurrentHook.js b/packages/hooks/src/createCurrentHook.js index c36cab8b43df24..4d44b96c039613 100644 --- a/packages/hooks/src/createCurrentHook.js +++ b/packages/hooks/src/createCurrentHook.js @@ -3,24 +3,13 @@ * currently running hook, or `null` if no hook of the given type is currently * running. * - * @param {Object} hooks Stored hooks, keyed by hook name. + * @param {import('.').Hooks} hooks Stored hooks, keyed by hook name. * - * @return {Function} Function that returns the current hook. + * @return {() => string | null} Function that returns the current hook name or null. */ function createCurrentHook( hooks ) { - /** - * Returns the name of the currently running hook, or `null` if no hook of - * the given type is currently running. - * - * @return {?string} The name of the currently running hook, or - * `null` if no hook is currently running. - */ return function currentHook() { - if ( ! hooks.__current || ! hooks.__current.length ) { - return null; - } - - return hooks.__current[ hooks.__current.length - 1 ].name; + return hooks.__current[ hooks.__current.length - 1 ]?.name ?? null; }; } diff --git a/packages/hooks/src/createDidHook.js b/packages/hooks/src/createDidHook.js index 9075507b3d8653..efae6c63038e75 100644 --- a/packages/hooks/src/createDidHook.js +++ b/packages/hooks/src/createDidHook.js @@ -3,22 +3,25 @@ */ import validateHookName from './validateHookName.js'; +/** + * @callback DidHook + * + * Returns the number of times an action has been fired. + * + * @param {string} hookName The hook name to check. + * + * @return {number|undefined} The number of times the hook has run. + */ + /** * Returns a function which, when invoked, will return the number of times a * hook has been called. * - * @param {Object} hooks Stored hooks, keyed by hook name. + * @param {import('.').Hooks} hooks Stored hooks, keyed by hook name. * - * @return {Function} Function that returns a hook's call count. + * @return {DidHook} Function that returns a hook's call count. */ function createDidHook( hooks ) { - /** - * Returns the number of times an action has been fired. - * - * @param {string} hookName The hook name to check. - * - * @return {number} The number of times the hook has run. - */ return function didHook( hookName ) { if ( ! validateHookName( hookName ) ) { return; diff --git a/packages/hooks/src/createDoingHook.js b/packages/hooks/src/createDoingHook.js index 055e99e52cedd9..f54bf5b16eb6a9 100644 --- a/packages/hooks/src/createDoingHook.js +++ b/packages/hooks/src/createDoingHook.js @@ -1,21 +1,23 @@ +/** + * @callback DoingHook + * Returns whether a hook is currently being executed. + * + * @param {string} [hookName] The name of the hook to check for. If + * omitted, will check for any hook being executed. + * + * @return {boolean} Whether the hook is being executed. + */ + /** * Returns a function which, when invoked, will return whether a hook is * currently being executed. * - * @param {Object} hooks Stored hooks, keyed by hook name. + * @param {import('.').Hooks} hooks Stored hooks, keyed by hook name. * - * @return {Function} Function that returns whether a hook is currently + * @return {DoingHook} Function that returns whether a hook is currently * being executed. */ function createDoingHook( hooks ) { - /** - * Returns whether a hook is currently being executed. - * - * @param {?string} hookName The name of the hook to check for. If - * omitted, will check for any hook being executed. - * - * @return {boolean} Whether the hook is being executed. - */ return function doingHook( hookName ) { // If the hookName was not passed, check for any current hook. if ( 'undefined' === typeof hookName ) { diff --git a/packages/hooks/src/createHasHook.js b/packages/hooks/src/createHasHook.js index d92f884560b5e6..e144e37d9a4890 100644 --- a/packages/hooks/src/createHasHook.js +++ b/packages/hooks/src/createHasHook.js @@ -1,22 +1,24 @@ +/** + * @callback HasHook + * + * Returns whether any handlers are attached for the given hookName and optional namespace. + * + * @param {string} hookName The name of the hook to check for. + * @param {?string} namespace Optional. The unique namespace identifying the callback + * in the form `vendor/plugin/function`. + * + * @return {boolean} Whether there are handlers that are attached to the given hook. + */ /** * Returns a function which, when invoked, will return whether any handlers are * attached to a particular hook. * - * @param {Object} hooks Stored hooks, keyed by hook name. + * @param {import('.').Hooks} hooks Stored hooks, keyed by hook name. * - * @return {Function} Function that returns whether any handlers are + * @return {HasHook} Function that returns whether any handlers are * attached to a particular hook and optional namespace. */ function createHasHook( hooks ) { - /** - * Returns whether any handlers are attached for the given hookName and optional namespace. - * - * @param {string} hookName The name of the hook to check for. - * @param {?string} namespace Optional. The unique namespace identifying the callback - * in the form `vendor/plugin/function`. - * - * @return {boolean} Whether there are handlers that are attached to the given hook. - */ return function hasHook( hookName, namespace ) { // Use the namespace if provided. if ( 'undefined' !== typeof namespace ) { diff --git a/packages/hooks/src/createHooks.js b/packages/hooks/src/createHooks.js index d61effc36a4ac0..6acdb2288405b1 100644 --- a/packages/hooks/src/createHooks.js +++ b/packages/hooks/src/createHooks.js @@ -11,11 +11,11 @@ import createDidHook from './createDidHook'; /** * Returns an instance of the hooks object. - * - * @return {Object} Object that contains all hooks. */ function createHooks() { + /** @type {import('.').Hooks} */ const actions = Object.create( null ); + /** @type {import('.').Hooks} */ const filters = Object.create( null ); actions.__current = []; filters.__current = []; diff --git a/packages/hooks/src/createRemoveHook.js b/packages/hooks/src/createRemoveHook.js index f2cf9456d45f8a..5df54cf3eb8e5b 100644 --- a/packages/hooks/src/createRemoveHook.js +++ b/packages/hooks/src/createRemoveHook.js @@ -5,25 +5,27 @@ import validateNamespace from './validateNamespace.js'; import validateHookName from './validateHookName.js'; import { doAction } from './'; +/** + * @callback RemoveHook + * Removes the specified callback (or all callbacks) from the hook with a + * given hookName and namespace. + * + * @param {string} hookName The name of the hook to modify. + * @param {string} namespace The unique namespace identifying the callback in the form `vendor/plugin/function`. + * + * @return {number|undefined} The number of callbacks removed. + */ + /** * Returns a function which, when invoked, will remove a specified hook or all * hooks by the given name. * - * @param {Object} hooks Stored hooks, keyed by hook name. + * @param {import('.').Hooks} hooks Stored hooks, keyed by hook name. * @param {boolean} removeAll Whether to remove all callbacks for a hookName, without regard to namespace. Used to create `removeAll*` functions. * - * @return {Function} Function that removes hooks. + * @return {RemoveHook} Function that removes hooks. */ -function createRemoveHook( hooks, removeAll ) { - /** - * Removes the specified callback (or all callbacks) from the hook with a - * given hookName and namespace. - * - * @param {string} hookName The name of the hook to modify. - * @param {string} namespace The unique namespace identifying the callback in the form `vendor/plugin/function`. - * - * @return {number} The number of callbacks removed. - */ +function createRemoveHook( hooks, removeAll = false ) { return function removeHook( hookName, namespace ) { if ( ! validateHookName( hookName ) ) { return; @@ -58,7 +60,7 @@ function createRemoveHook( hooks, removeAll ) { // comes after the current callback, there's no problem; // otherwise we need to decrease the execution index of any // other runs by 1 to account for the removed element. - ( hooks.__current || [] ).forEach( ( hookInfo ) => { + hooks.__current.forEach( ( hookInfo ) => { if ( hookInfo.name === hookName && hookInfo.currentIndex >= i diff --git a/packages/hooks/src/createRunHook.js b/packages/hooks/src/createRunHook.js index 311b1cc9052620..85d068594d04bf 100644 --- a/packages/hooks/src/createRunHook.js +++ b/packages/hooks/src/createRunHook.js @@ -3,21 +3,13 @@ * registered to a hook of the specified type, optionally returning the final * value of the call chain. * - * @param {Object} hooks Stored hooks, keyed by hook name. + * @param {import('.').Hooks} hooks Stored hooks, keyed by hook name. * @param {?boolean} returnFirstArg Whether each hook callback is expected to * return its first argument. * - * @return {Function} Function that runs hook callbacks. + * @return {(hookName:string, ...args: unknown[]) => unknown} Function that runs hook callbacks. */ -function createRunHook( hooks, returnFirstArg ) { - /** - * Runs all callbacks for the specified hook. - * - * @param {string} hookName The name of the hook to run. - * @param {...*} args Arguments to pass to the hook callbacks. - * - * @return {*} Return value of runner, if applicable. - */ +function createRunHook( hooks, returnFirstArg = false ) { return function runHooks( hookName, ...args ) { if ( ! hooks[ hookName ] ) { hooks[ hookName ] = { diff --git a/packages/hooks/src/index.js b/packages/hooks/src/index.js index 78a68df8090862..492f82f5313a0d 100644 --- a/packages/hooks/src/index.js +++ b/packages/hooks/src/index.js @@ -3,6 +3,30 @@ */ import createHooks from './createHooks'; +/** @typedef {(...args: any[])=>any} Callback */ + +/** + * @typedef Handler + * @property {Callback} callback The callback + * @property {string} namespace The namespace + * @property {number} priority The namespace + */ + +/** + * @typedef Hook + * @property {Handler[]} handlers Array of handlers + * @property {number} runs Run counter + * + */ +/** + * @typedef Current + * @property {string} name Hook name + * @property {number} currentIndex The index + */ +/** + * @typedef {Record & {__current: Current[]}} Hooks + */ + const { addAction, addFilter, diff --git a/packages/hooks/tsconfig.json b/packages/hooks/tsconfig.json new file mode 100644 index 00000000000000..12d2d5c2dff539 --- /dev/null +++ b/packages/hooks/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "declarationDir": "build-types", + "types": [ "gutenberg-env" ] + }, + "references": [], + "include": [ "src/**/*" ] +} diff --git a/tsconfig.json b/tsconfig.json index 57392eebc289f9..eea00a2544e0f2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,6 +12,7 @@ { "path": "packages/escape-html" }, { "path": "packages/eslint-plugin" }, { "path": "packages/html-entities" }, + { "path": "packages/hooks" }, { "path": "packages/i18n" }, { "path": "packages/icons" }, { "path": "packages/is-shallow-equal" },