-
Notifications
You must be signed in to change notification settings - Fork 30.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ESM Loader Hooks (20.8) should support a more testable, less stateful design #50042
Comments
This commit allows the `initialize()` hook to optionally return an object having the `resolve()` and `load()` hooks as properties. This allows state passed into `initialize()` to be shared with the `resolve()` and `load()` hooks either via closure or class instance. In addition to developer ergonomics, supporting this model will make it easier to write tests against a loader module. The existing design forces state to be shared at the module level which puts the burden of invalidating the ESM module cache on anyone hoping to write isolated tests against a loader module. Fixes: nodejs#50042
This commit allows the `initialize()` hook to optionally return an object having the `resolve()` and `load()` hooks as properties. This allows state passed into `initialize()` to be shared with the `resolve()` and `load()` hooks either via closure or class instance. In addition to developer ergonomics, supporting this model will make it easier to write tests against a loader module. The existing design forces state to be shared at the module level which puts the burden of invalidating the ESM module cache on anyone hoping to write isolated tests against a loader module. Fixes: nodejs#50042
@nodejs/loaders |
This commit allows the `initialize()` hook to optionally return an object having the `resolve()` and `load()` hooks as properties. This allows state passed into `initialize()` to be shared with the `resolve()` and `load()` hooks either via closure or class instance. In addition to developer ergonomics, supporting this model will make it easier to write tests against a loader module. The existing design forces state to be shared at the module level which puts the burden of invalidating the ESM module cache on anyone hoping to write isolated tests against a loader module. Fixes: nodejs#50042
BTW, I threw #50044 together to explore what a back-compatible approach would look like for this. |
This commit allows the `initialize()` hook to optionally return an object having the `resolve()` and `load()` hooks as properties. This allows state passed into `initialize()` to be shared with the `resolve()` and `load()` hooks either via closure or class instance. In addition to developer ergonomics, supporting this model will make it easier to write tests against a loader module. The existing design forces state to be shared at the module level which puts the burden of invalidating the ESM module cache on anyone hoping to write isolated tests against a loader module. Fixes: nodejs#50042
@GeoffreyBooth, here are some higher-level observations about the current loader API:
I've started speccing out what I think an alternative design might look like and as I'm attempting to port the CoffeScript loader from the docs over, I'm seeing some of the motivation for the |
@ggoodman I’m warming to the idea of As for And for your #5, chaining is the reason we need those things. We want the ability to register multiple sets of hooks, like for example just as people today often do |
You might find node-tap's loader interfaces informative here. I've been putting these hook methods in a The exported types in |
If it was just to help with testing, I would not have been for this change, as there are other ways to deal with tests here. But! We still have an open issue regarding communication with hook (e.g. an api that changes thing in the hook), when there are multiple worker threads. If we call So not am I only not for this idea, I think it's a must have to solve an open issue in loaders. |
@isaacs That sounds quite awesome TBH because it's much higher fidelity than the sorts of tests I'm proposing. At the same time, it relies on using a smaller subset of test frameworks that support this sort of isolation. I don't think Node's own test runner even has this sort of advanced functionality yet. |
Agreed @giltayar, I don't think the current design has a good story for instantiation. In other words, the forced reliance on module-level state means that the same loader can't be instantiated multiple times with different configs. Of course, there are probably some tricky ways to get around this but the current API seems unnecessarily difficult for such use-cases. |
Happy to rebase #50044 and spike out the docs changes if there seems to be momentum in favour of the change. |
Can the PR add some tests for this? This would be good to have coverage around. We might want to add an example in the docs for worker threads if supporting them is something that hook authors will need to explicitly enable. As for the
Yes please! |
This commit allows the `initialize()` hook to optionally return an object having the `resolve()` and `load()` hooks as properties. This allows state passed into `initialize()` to be shared with the `resolve()` and `load()` hooks either via closure or class instance. In addition to developer ergonomics, supporting this model will make it easier to write tests against a loader module. The existing design forces state to be shared at the module level which puts the burden of invalidating the ESM module cache on anyone hoping to write isolated tests against a loader module. Fixes: nodejs#50042
This commit allows the `initialize()` hook to optionally return an object having the `resolve()` and `load()` hooks as properties. This allows state passed into `initialize()` to be shared with the `resolve()` and `load()` hooks either via closure or class instance. In addition to developer ergonomics, supporting this model will make it easier to write tests against a loader module. The existing design forces state to be shared at the module level which puts the burden of invalidating the ESM module cache on anyone hoping to write isolated tests against a loader module. Fixes: nodejs#50042
There has been no activity on this feature request for 5 months. To help maintain relevant open issues, please add the
never-stale
|
There has been no activity on this feature request and it is being closed. If you feel closing this issue is not the right thing to do, please leave a comment. For more information on how the project manages feature requests, please consult the feature request management document. |
What is the problem this feature will solve?
The current ESM Loader Hooks are defined as modules having the following named exports:
initialize()
,resolve()
andload()
. Theinitialize()
function is passed data specified when callingregister()
. The intent is that the other two hooks can thus also leverage this data in extensible, future-proof ways.The problem that this introduces is that the ESM Load Hook module is now a stateful singleton because it encourages designs wherein initialization state is shared at the module level. This means that any tests looking to exercise the loader module needs to deal with ESM cache invalidation. It also makes it slightly less ergonomic (and type-safe!) to set and access any needed shared state.
What is the feature you are proposing to solve the problem?
I propose a slight change (or addition) to the proposed design. The change I'd like to propose is that the
initialize()
function should be able to produce the object implementing the other two components of the interface:resolve()
andload()
.In this way:
initialize()
call and theresolve()
andload()
hooks.initialize()
function. The class can hold shared state in a private field or instance properties. Alternatively, theinitialize()
closure could also be used to hold this state in a way that the two other hooks can easily access.What alternatives have you considered?
No response
The text was updated successfully, but these errors were encountered: