This repository has been archived by the owner on Jul 9, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 29
Add the WordPress hook library from 21170-core #13
Merged
adamsilverstein
merged 98 commits into
WordPress:master
from
adamsilverstein:add-wp-hooks
Sep 22, 2017
Merged
Changes from all commits
Commits
Show all changes
98 commits
Select commit
Hold shift + click to select a range
6bca9da
add wp-hooks, first pass
85c73be
updates from ticket, flesh out removeAll, cleanup
dc71cd1
inline docs cleanup
4013c9a
remove some commented code
ff65810
downgrade initial version
be62409
clean up imports for (empty) tests file
45d70c3
bring over object approach from core instead of passing around string
632c98a
First pass, jest tests
77c9906
add remainder of tests
b4b713d
Docs refinements
9d24e77
Break out code into one file per funciton
57f6038
Centralize all run logic into `createRunHook`
nylen 1901b43
Use a consistent order for functions
nylen b05f488
Merge branch 'add-wp-hooks' of github.com:adamsilverstein/packages in…
f6c2b45
Fix `currentFilter` and unit-test it
nylen f50aadb
Add `currentAction` and unit-test it
nylen bee29d7
Declare base hooks objects as objects, not arrays
nylen f8e129d
Merge branch 'add-wp-hooks' of github.com:adamsilverstein/packages in…
b02296c
Several fixes to hook removal behavior
nylen aeca7df
Fix `hasAction` and `hasFilter` if all hooks have been removed
nylen 678f790
Remove now-unused `createRemoveAllHook`
nylen 78acfe1
Naming and documentation cleanup
nylen 637af4d
Make `hasAction` and `hasFilter` return the number of registered hooks
nylen b7b30f4
Remove unnecessary `describe`/`it` pairs
nylen b351db4
A bit more function declaration cleanup
nylen 498e15b
Use ES6 object shorthand
nylen e3392c3
Add explicit error reporting
nylen 53294fd
Test adding and removing multiple filters at the same priority
nylen 07b61a0
Keep the hooks list always sorted instead of explicitly sorting it
nylen ee303e6
Remove remaining `var` statements
nylen b1251e3
Remove remaining per-test cleanup logic
nylen 93998e1
Merge branch 'add-wp-hooks' of github.com:adamsilverstein/packages in…
7eda5e2
Add webpack and build wp-hooks for core
0e9986a
remove built file and adjust build path
c9890db
Remove unused dependencies
nylen de6efd7
Use an ES6 default argument to set the default hook priority
nylen ad47998
Refactor handlers objects: don't assign extra properties on arrays
nylen e217d7f
Merge branch 'add-wp-hooks' of github.com:adamsilverstein/packages in…
562cf37
Run core build JS thru babel
f882313
remove babel-loader for now
9eeb198
rename ‘current’ => ‘__current’ and make it a stack
fd0ccd3
Add initial readme based on https://github.com/adamsilverstein/WP-JS-…
fc48fbf
test remove a filter callback of lower priority when running hook
566af31
test remove a filter callback of same priority when running hook
6ce6268
prevent and test for (dingle level) hook recursion
83dffa4
test remove a filter callback of higher priority when running hook
2a60b9f
Failing recursive test: remove one hook in a callback before adding a…
79ef421
Add babel-loader for core build
742857a
Merge remote-tracking branch 'origin/master' into add-wp-hooks
nylen bf69f89
Clean up tests for removing a filter while filters are running
nylen bd911d0
Add test demonstrating desired recursion behavior
nylen b802c1d
Convert most functions in tests to arrow functions (for consistency)
nylen 4e393d6
Add failing test for `currentFilter` with multiple filters running
nylen 5d0513c
Fix `currentFilter` behavior
nylen c11003c
Add more `currentFilter` test assertions
nylen ad09872
Remove `wp-hooks.js` entry point
nylen a19fa3b
Merge remote-tracking branch 'origin/master' into add-wp-hooks
nylen 0498ee8
Prevent hook names from starting with `__`
nylen 5b8c5f3
Prevent running hooks with invalid names
nylen 6327a54
Add missing tests for error conditions
nylen dd10eab
Make hook removal functions return the number of hooks removed
nylen 8be21e5
Ensure that hook callbacks to remove are functions
nylen 70b3ebc
Test running a filter with no callbacks
nylen a3b4901
`doAction` should return `undefined`
nylen 6653227
Add failing test for recursive filters
nylen 2e9b96e
First attempt at handling adding/removing callbacks during execution
nylen 54f70c1
Make the filter adding/removing/recursion test pass
nylen afa92ea
Minor style and speed improvement
nylen 0919ab9
Add missing tests
nylen d319b9d
Merge branch 'add-wp-hooks' of github.com:adamsilverstein/packages in…
90942a7
Add a centralized webpack folder based build for packages
e2547d9
Add check: the hook name can only contain numbers, letters and unders…
cefc79a
use yoda conditional as per wp coding standards
ca95500
removeHook - accept a namespace parameter and use it to identify item…
cc8b852
addHook - The hook name can only contain numbers, letters and undersc…
06ddd95
addHook - accept a namespace parameter and store in handler data
edd46d0
Use some Yoda conditionals
77be5ed
Ensure the passed namespace is a string of the form `my-plugin-slug/f…
2c62cdc
Add a build-modules command to run webpack
581e715
Add a `validateNamespace` helper, centralizing namespace checks
4c7f252
Add a `validateHookName` helper, centralizing hookName validation
ef06958
Use new hookName and namespace validation helpers throughout generato…
319a023
Update tests to use namespaces
4ece207
Update readme with new hook/namespace signature
aa709ac
Improve validateNamespace and validateHookName inline docs
a8628a0
require a non-empty string when validating namespace/hookName
242f351
Adjust expected error language in tests.
d8af28f
Improved regexes for hookName and namespace validation
650e9e2
Add more tests around hookName and namespace validation
a617214
Validate namespace takes form `vendorName/pluginName/functionName`
30e5e82
Update tests with new namespace format
3a13768
update readme with new namespace format
7c1fc29
remove module build from branch
45747e5
Correct namespace strings in docblocks
a24847b
Add args to applyFilters docs in readme
9f4cefc
camelCase for readme variable names
73744fa
update hook and `vendor/plugin/function` wording, removing `Name`
7465667
complte namespace shortening to `vendor/plugin/function`
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# WP-JS-Hooks | ||
|
||
A lightweight & efficient EventManager for JavaScript in WordPress. | ||
|
||
|
||
### API Usage | ||
API functions can be called via the global `wp.hooks` like this `wp.hooks.addAction()`, etc. | ||
|
||
* `addAction( 'hook', 'vendor/plugin/function', callback, priority )` | ||
* `addFilter( 'hook', 'vendor/plugin/function', callback, priority )` | ||
* `removeAction( 'hook', 'vendor/plugin/function' )` | ||
* `removeFilter( 'hook', 'vendor/plugin/function' )` | ||
* `removeAllActions( 'hook' )` | ||
* `removeAllFilters( 'hook' )` | ||
* `doAction( 'hook', arg1, arg2, moreArgs, finalArg )` | ||
* `applyFilters( 'hook', content, arg1, arg2, moreArgs, finalArg )` | ||
* `doingAction( 'hook' )` | ||
* `doingFilter( 'hook' )` | ||
* `didAction( 'hook' )` | ||
* `didFilter( 'hook' )` | ||
* `hasAction( 'hook' )` | ||
* `hasFilter( 'hook' )` | ||
|
||
|
||
### Background | ||
See ticket [#21170](http://core.trac.wordpress.org/ticket/21170) for more information. | ||
|
||
|
||
### Features | ||
|
||
* Fast and lightweight. | ||
* Priorities system ensures hooks with lower integer priority are fired first. | ||
* Uses native object hash lookup for finding hook callbacks. | ||
* Utilizes insertion sort for keeping priorities correct. Best Case: O(n), worst case: O(n^2) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"name": "@wordpress/hooks", | ||
"version": "0.1.0", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/WordPress/packages.git" | ||
}, | ||
"description": "WordPress Hooks library", | ||
"main": "build/index.js", | ||
"module": "build-module/index.js", | ||
"browser": "build-browser/index.js", | ||
"author": "WordPress", | ||
"license": "GPL-2.0+" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import validateNamespace from './validateNamespace.js'; | ||
import validateHookName from './validateHookName.js'; | ||
|
||
/** | ||
* Returns a function which, when invoked, will add a hook. | ||
* | ||
* @param {Object} hooks Stored hooks, keyed by hook name. | ||
* | ||
* @return {Function} 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; | ||
} | ||
|
||
if ( ! validateNamespace( namespace ) ) { | ||
return; | ||
} | ||
|
||
if ( 'function' !== typeof callback ) { | ||
console.error( 'The hook callback must be a function.' ); | ||
return; | ||
} | ||
|
||
// Validate numeric priority | ||
if ( 'number' !== typeof priority ) { | ||
console.error( 'If specified, the hook priority must be a number.' ); | ||
return; | ||
} | ||
|
||
const handler = { callback, priority, namespace }; | ||
|
||
if ( hooks.hasOwnProperty( hookName ) ) { | ||
// Find the correct insert index of the new hook. | ||
const handlers = hooks[ hookName ].handlers; | ||
let i = 0; | ||
while ( i < handlers.length ) { | ||
if ( handlers[ i ].priority > priority ) { | ||
break; | ||
} | ||
i++; | ||
} | ||
// Insert (or append) the new hook. | ||
handlers.splice( i, 0, handler ); | ||
// We may also be currently executing this hook. If the callback | ||
// 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 => { | ||
if ( hookInfo.name === hookName && hookInfo.currentIndex >= i ) { | ||
hookInfo.currentIndex++; | ||
} | ||
} ); | ||
} else { | ||
// This is the first hook of its type. | ||
hooks[ hookName ] = { | ||
handlers: [ handler ], | ||
runs: 0, | ||
}; | ||
} | ||
}; | ||
} | ||
|
||
export default createAddHook; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/** | ||
* Returns a function which, when invoked, will return the name of the | ||
* currently running hook, or `null` if no hook of the given type is currently | ||
* running. | ||
* | ||
* @param {Object} hooks Stored hooks, keyed by hook name. | ||
* | ||
* @return {Function} Function that returns the current hook. | ||
*/ | ||
function createCurrentHook( hooks, returnFirstArg ) { | ||
/** | ||
* 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; | ||
}; | ||
} | ||
|
||
export default createCurrentHook; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import validateHookName from './validateHookName.js'; | ||
|
||
/** | ||
* 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. | ||
* | ||
* @return {Function} 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; | ||
} | ||
|
||
return hooks.hasOwnProperty( hookName ) && hooks[ hookName ].runs | ||
? hooks[ hookName ].runs | ||
: 0; | ||
}; | ||
} | ||
|
||
export default createDidHook; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
/** | ||
* Returns a function which, when invoked, will return whether a hook is | ||
* currently being executed. | ||
* | ||
* @param {Object} hooks Stored hooks, keyed by hook name. | ||
* | ||
* @return {Function} 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 {bool} 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 ) { | ||
return 'undefined' !== typeof hooks.__current[0]; | ||
} | ||
|
||
// Return the __current hook. | ||
return hooks.__current[0] | ||
? hookName === hooks.__current[0].name | ||
: false; | ||
}; | ||
} | ||
|
||
export default createDoingHook; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/** | ||
* 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. | ||
* | ||
* @return {Function} Function that returns whether any handlers are | ||
* attached to a particular hook. | ||
*/ | ||
function createHasHook( hooks ) { | ||
/** | ||
* Returns how many handlers are attached for the given hook. | ||
* | ||
* @param {string} hookName The name of the hook to check for. | ||
* | ||
* @return {number} The number of handlers that are attached to | ||
* the given hook. | ||
*/ | ||
return function hasHook( hookName ) { | ||
return hooks.hasOwnProperty( hookName ) | ||
? hooks[ hookName ].handlers.length | ||
: 0; | ||
}; | ||
} | ||
|
||
export default createHasHook; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import validateNamespace from './validateNamespace.js'; | ||
import validateHookName from './validateHookName.js'; | ||
|
||
/** | ||
* 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 {bool} removeAll Whether to remove all callbacks for a hookName, without regard to namespace. Used to create `removeAll*` functions. | ||
* | ||
* @return {Function} 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. | ||
*/ | ||
return function removeHook( hookName, namespace ) { | ||
|
||
if ( ! validateHookName( hookName ) ) { | ||
return; | ||
} | ||
|
||
if ( ! removeAll && ! validateNamespace( namespace ) ) { | ||
return; | ||
} | ||
|
||
// Bail if no hooks exist by this name | ||
if ( ! hooks.hasOwnProperty( hookName ) ) { | ||
return 0; | ||
} | ||
|
||
let handlersRemoved = 0; | ||
|
||
if ( removeAll ) { | ||
handlersRemoved = hooks[ hookName ].handlers.length; | ||
hooks[ hookName ] = { | ||
runs: hooks[ hookName ].runs, | ||
handlers: [], | ||
}; | ||
} else { | ||
// Try to find the specified callback to remove. | ||
const handlers = hooks[ hookName ].handlers; | ||
for ( let i = handlers.length - 1; i >= 0; i-- ) { | ||
if ( | ||
handlers[ i ].namespace === namespace | ||
) { | ||
handlers.splice( i, 1 ); | ||
handlersRemoved++; | ||
// This callback may also be part of a hook that is | ||
// currently executing. If the callback we're removing | ||
// 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 => { | ||
if ( hookInfo.name === hookName && hookInfo.currentIndex >= i ) { | ||
hookInfo.currentIndex--; | ||
} | ||
} ); | ||
} | ||
} | ||
} | ||
|
||
return handlersRemoved; | ||
}; | ||
} | ||
|
||
export default createRemoveHook; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest a
files
values to reduce bloat of distributed module:https://docs.npmjs.com/files/package.json#files
e.g. omitting tests.