-
Notifications
You must be signed in to change notification settings - Fork 28
Add the WordPress hook library from 21170-core #13
Add the WordPress hook library from 21170-core #13
Conversation
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.
The machinery around createSomeFunction( type )
and all the corresponding HOOKS[ type ]
checks feels a bit overwrought (and will perform more slowly than necessary). How about something like this instead:
const HOOKS = {
actions: {},
filters: {},
};
function createAddHookFor( hooksOfThisType ) {
// Manipulate `hooksOfThisType` directly
}
export addAction = createAddHookFor( HOOKS.actions );
packages/hooks/package.json
Outdated
@@ -0,0 +1,18 @@ | |||
{ | |||
"name": "@wordpress/hooks", | |||
"version": "1.0.0", |
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.
Let's make this 0.1.0
or something, I don't think we're at "final" v1 yet.
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.
Ok!
/** | ||
* Internal Dependencies | ||
*/ | ||
import { doAction, applyFilters, addAction, addFilter, doingAction, doingFilter, didAction, didFilter, hasAction, hasFilter, removeAllActions, removeAllFilters, currentFilter } from '../'; |
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.
Let's split this long line up into one import per line.
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.
We could also export default { doAction, ... }
and then just import hooks from '../
, would likely be a bit nicer.
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.
The drawback there is that you have to prefix every function call with hooks.
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'll work on cleaning this up and maybe splitting out the generator code into files/export/import.
packages/hooks/src/index.js
Outdated
|
||
// Doing functions. | ||
export doingAction = createDoingHookByType( 'actions' ), /* True for actions until;next action fired. */ | ||
export doingFilter = createDoingHookByType( 'filters' ), /* True for filters while;filter is being applied. */ |
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.
Why the semicolons in the middle of these comments? Should be //
inline comments as well
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.
thanks for noting, will correct
packages/hooks/src/index.js
Outdated
*/ | ||
function createRemoveAllByType( type ) { | ||
return function( action, type ) { | ||
|
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.
No implementation yet?
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.
fixed on the ticket, will update...
@nylen I'm not sure I understand how exactly you are proposing refactoring this and how it would help... the |
Instead of passing around This should make the code clearer, shorter, and faster. |
Ah, ok - interesting idea, I can see how that would be cleaner and faster. I'll give it a try on the core patch where I have unit tests that will tell me if I broke anything. |
@nylen something like this? https://core.trac.wordpress.org/attachment/ticket/21170/21170.11.diff |
Yes, except several operations are |
Not sure what you mean by this, can you clarify? I reworked the tests as Jest tests. Thinking of breaking out the code into files and maybe temporarily adding a build step to generate the core file so I don't have to keep updating both. |
I broke out the code a bit and tested the built in build process. What is the best way to get a build step that sets up a single file we can load into core that loads functions on wp.hooks? Do we need webpack with an entry file like this? https://gist.github.com/adamsilverstein/4fbbd4ca14c5f7526a1ee41b63509cd2 |
Webpack can do that part for us, like the Gutenberg build does: https://github.com/WordPress/gutenberg/blob/c78ad83/webpack.config.js#L49-L74 That should probably be handled at the root level of this repo, though also related is how we'll publish the package on |
Ok, i'll work on the build part in the root with webpack, can we publish the package as @wordpress/hooks - and do we let people import individual functions, or only the entire hooks object? |
People should be able to import individual functions. |
Also fixes a bug that `doingAction` would remain true after its action was completed. Effectively it would always be true.
In the core JS chat today @schlessera suggested we consider adding a vendor prefix as part of the namespace string: https://wordpress.slack.com/archives/C5UNMSU4R/p1502803194000254 - this eems like a worthwhile addition that could prevent naming conflicts down the line. |
As discussed in Slack, I'd like to suggest avoiding potential conflicts by changing the namespace prefix from The reasoning behind this is that, for similar solutions, people tend to pick similar names. This makes the most important namespace level ( Adding the For the example of the WP Core hooks above, you'd have I do understand that you want to keep the strings as short as possible, but maybe it would make sense to provide an automated/more convenient mechanism for creating unique hook name instead in this case. You could then have 1 central location where one sets up the namespace to use. People that need more control can still do it the more traditional way. |
@schlessera - I updated the namespace as per your suggestion, thanks! |
@nylen @kadamwhite @youknowriad @aduth - I'm planning to merge this to WordPress core soon so it has some time to soak for 4.9. Any final feedback here? Can we go ahead and merge this and release as a package version 1.0.0? |
I worry this was driven by a desire to mimic PHP, "what if x"s and "maybe it'd be nice if x" than it was by real-world requirements, ease-of-use, or expectations via prior art in JavaScript, culminating in awkward semantics around vendoring, namespaces, and hook names. If you count each of these as a pseudo-argument of sorts, it requires a minimum of 5 arguments to hook anything. I think hook naming was a valid direction to explore, but not one that ultimately had a positive outcome. In place of naming to remove hooks, I'd rather we had taken the direction of some precedent:
Or even a combination of the first and second. Where are the requirements that dictated we should have addressable names for hooks? Even if we wanted to allow plugins to remove other plugin or core hook handlers, could it not be up to the plugins or core to create patterns around doing so, without needlessly bloating the argument signature of the hook creators? Alternatively, even if we were to keep the direction we'd explored here, we might have considered some helpers to help create scoping to avoid repetition, like hook creators. const pluginHookCreator = createHookCreator( 'vendor/myplugin' );
pluginHookCreator.addAction( 'actionName', callback ); Or perhaps something integrated into build tooling that eliminates the need to explicitly specify the vendor, namespace. I'd like to explore similar for JavaScript i18n string extraction. In its current state, I'm not a fan of the approach here. Maybe we can discuss again in tomorrow's JS meeting. |
This needs more than just time to soak in trunk before 4.9, it's a huge new API. Please consider this a blocker: before it's merged, it needs to be integrated into a substantial project to help iron out practical problems that are likely to come up. |
My concern with these approaches is that none of them solve the plugin-unhooking-other-plugin problem. Anything that isn't a static identifier is going to fail for that use case. This is a pretty significant use case in PHP-land; I don't see that there are any fundamental differences that would change that in JS-land. The PHP-land solution is that you always hook something that's globally-exposed, whether that's a function name (always global) or an object method (where you need to expose the object yourself). The problem in JS-land is that functions are not usually globally-exposed, or are completely anonymous, which means we have basically no ID to use. (Anonymous functions for PHP hooks are already discouraged as a best practice for exactly this reason.) |
You'll have to forgive my ignorance, but this is not a pattern I've seen very often. Can you point to a few examples so I have a better idea of their use-case? I'm not anti-ad-hoc-removal (well, maybe a bit 😄 ), but it's a decision of best compromise: Would I sacrifice a built-in option to remove other plugin's hooks if it meant I'd only need to provide a hook name and callback to register my own, rather than hook name, vendor name, namespace, callback name, and callback? For me, yes I would. |
@pento what would you think about merging as an experimental API, similar to what we did with Heartbeat? |
An example would be you have installed some plugin that filters post content in some way and you want to use the plugin, but remove its content filtering, perhaps in a certain context only. |
I'm exceedingly skeptical of that option. Heartbeat being merged as an experimental API didn't really achieve any of its goals: it didn't gain much adoption, the adoption it did gain meant we struggled to break backwards compatibility, and it didn't evolve much in subsequent releases. I think the most successful new API in recent years was the REST API core - it was solid, having been used as the base for the REST API endpoints, there have been lots of examples of plugins building new endpoints based on it, and we haven't had huge issues with needing to break backwards compatibility. |
A quick grep of the plugin repo shows many examples. Here is one where a plugin looks to see if woocomerce breadcrumbs are enabled and if so, replaces the action with one of it's own: https://plugins.trac.wordpress.org/browser/catalyst-connect/trunk/inc/frontend.php#L104 Here is an example of modifying the genesis theme: https://plugins.trac.wordpress.org/browser/thememix-pro-genesis/trunk/page-templates/page-login.php#L14 This technique was also talked about as a part of how NPR built the Argo network: https://wordpress.tv/2011/09/25/marc-lavallee-wes-lindamood-plugins-are-blueprints/ |
It appears the goal is to turn hook names into PHP namespaces. If so, I think we need about 7 more slashes to really replicate the experience. As with all large API changes (Gutenberg, REST-API, etc), this should have a long period of relative stability with solid documentation in order to develop maturity before it is added to core. |
Worth noting that the code in this package was originally provided on https://core.trac.wordpress.org/ticket/21170 over 5 years ago. Although we have added some tests and small improvements in the last few months, the core of the library remains unchanged. In regards to documentation, here is what we have so far: https://github.com/adamsilverstein/packages/blob/45747e5ad147a6b35f2b7bb508eb139a475e2094/packages/hooks/README.md - this could definitely be improved with additional details and examples.
Really? This seems pretty solid and well tested to me, and worth having in core as we explore ways to make our JavaScript more extensible, for example the media manager. I am ready to commit this code on https://core.trac.wordpress.org/ticket/21170 - it is mainly 5+ year old code that has had review from project leads and some of our top core JS engineers. This is a solid, reliable hooks library I would use in production today. Sure, we may still find edge cases bugs: let's start using it to find out. Lets release this as an NPM package, then projects like Gutenberg or WordPress core itself can test it out by importing it from npm. |
the readme is redone, can you please re-review?
Argh, the context from https://core.trac.wordpress.org/ticket/21170 is so much missing here, or rather this discussion should have been at the core trac ticket, where it belongs :( Not sure if this is a "big new API", it is a pub/sub that replaces Heartbeat was marked "experimental" not because it needed to "wait" for plugins to start using it. There were two main reasons:
Think it's about time to bite the bullet and finally commit this. The alternative is to continue using |
wp.hooks has landed in core, let's get this package released to npm as 1.0.0. |
Let's get this published on npm as |
See https://core.trac.wordpress.org/ticket/21170