esm: consolidate ESM Loader methods #37468
Conversation
|
I ordinarily try to avoid touching lines that don't otherwise have changes, but I had to tidy up code that was otherwise untouched because it was very difficult to read & follow (it looked like it had been through an uglifier—perhaps some kind of lint autofixer?). |
|
@GeoffreyBooth I have a few questions: There're very few code comments (at least in the ESM section), and it's very difficult to figure out what anything is doing / supposed to do. I haven't come across any docs that seem to cover this aside from "try to avoid unnecessary breaking changes". I know this particular piece is intended to merely consolidate some methods, but as far as I've managed to glean, a lot of the ESM code needs ground-up change (ex how the Loader class is designed and used is…very strange). How much change there might you/y'all be open to? Happy to jump on a call to discuss. I'm EST (Mondays and Fridays after ~17:30 would be good for me). |
@guybedford is the expert on this part of the codebase, I think. Perhaps he can answer that. I would be happy to join a call but my schedule is difficult and I don’t know that part of the codebase too well either (just the hooks, for the most part). I assume you’ve reviewed #35524? I was thinking that the first PR would just be completing what was started on that branch. |
|
Yes, I reviewed it to help figure out what the code is doing. I'm still not 100% (probably closer to 70%). I'd like to run an approach by someone more familiar. |
|
@JakobJingleheimer the code is split into explicit phases (resolve, getformat, getsource, transform). In terms of the system model of the loader, I'm not sure this is clearer. Additionally, you should run a lint pass on this code ( This is also going to need a review for primordials. For example, |
|
Actually I'm now realizing this is changing the public loader API. I will remove myself from the discussion. |
|
@devsnek i gathered about the phases ;) It's a WIP commit; I didn't bother with the non-IDE-integrating linter yet. On a related note, why 80 columns? Are we devving on iPads? |
The Node codebase is over ten years old, so some things you just have to live with at this point @devsnek is also a good resource for this part of the codebase, he knows it better than I do. |
This comment has been hidden.
This comment has been hidden.
This comment has been hidden.
This comment has been hidden.
The tests themselves are mostly in JavaScript. The runner is Python. Remember, the test runner for Node was written before any Node-based test runner existed I wouldn’t spend time on things like trying to change Node’s linting rules. The project is very mature at this point and seemingly little things like that are big efforts now because of the number of open branches and collaborators and release lines that things need to be backported to, etc. Also changes like that would need sign-off by a huge number of people, and it’s just not worth the effort if it’s not blocking you from the immediate task you’re trying to accomplish. |
@devsnek The goal is to change both the public and private loader. See #35524. The goal is to complete that PR. You’re right about primordials, but we can fix all those at the end once we have a working PR. |
|
@devsnek Thanks for the tip about array.push → primordial! Where do primordials come from? They appear to be magically available (no import/require). This morning I tried to find them via a file search on the repo, but didn't find where they're defined. |
|
Okay, so since @devsnek and @guybedford aren't available for that call to discuss, I'll try to summarise here: esm_loader.js instantiates ESMLoader¹ twice: first to use esmLoader.import(), then immediately instantiate a new instance (overwriting the previous) to pass the output from esmLoader1.import to esmLoader2.hooks (abbreviated / pseudo code): hooks = (new ESMLoader()).import(userLoaderSpecifier)
(new ESMLoader()).hooks(hooks)
// esmLoader2.runGlobalPreloader()ESMLoader.import uses:
For esmLoader1, resolve and getFormat are node's built-ins. I think instead:
¹ the class itself is actually named |
This comment has been hidden.
This comment has been hidden.
|
I'm not too familiar with that section of the codebase, but in general this approach seems fine to me. |
|
@JakobJingleheimer happy to answer questions and review, two things would help to do that:
We have an outer and inner loader to ensure that the hooks do not affect the loader modules themselves. |
|
@guybedford sure! I'll update the docs over lunch today. Would you be available to chat about them this evening? (If so, mind DMing me on Twitter or emailing me?) My questions are mostly around internal approach (I think the changes to the public API are pretty simple and agreed). |
|
@DerekNonGeneric You should chat with @JakobJingleheimer here about this PR, I think this could be a good effort to collaborate on. |
|
Yes please! I didn't expect this to drag on so long @guybedford did you see my previous message here? (I didn't receive any follow-up from you—did I miss it somewhere?) When I try to build, I get the following error, which I don't know how to properly fix. I presume it's because I deleted the obsolete make[1]: *** No rule to make target `../lib/internal/modules/esm/transform_source.js', needed by `[…]/node/out/Release/obj/gen/node_javascript.cc'. Stop.I see in #37468 that file was replaced with a comment (presumably to circumvent this error). I can do that, but I think that's not the ideal thing to do. |
|
@JakobJingleheimer feel free to DM me further to arrange a discusson, although written questions would always be preferable for context. |
|
I'm trying to get the tests updated so I can verify the updates, but I don't recognise what it uses—seems to be something homebrewed? I tried compiling, but I think it got stuck in a loop ( I have tomorrow afternoon I can work on this. It would be a lot more productive not having to discover the context around the ESM subsystem (and test framework). I didn't hear back from @guybedford, and I noticed @DerekNonGeneric has a note in his profile that he's taking a break from Node work. |
I think he's back now. |
|
Yeah, thanks for reaching out. I was planning to get around to doing this a bit later next month. We can continue work on your branch, but I am not 100% on the naming you have here. We can bikeshed in the #node-dev Freenode IRC channel if you're up for it (ping me on there tomorrow if you can). |
|
Chatted with Derek, and he wants to revisit the consolidation discussion (to potentially reduce the hooks down to just 1). He's planning to return from his break in late April. Conceptually, I'm not sure if the One Hook to Rule Them All is logically possible (aside from that, I am a fan of simplicity). I didn't get any answers to my questions so far, so I'm still groping around in the dark. And now with the potential discussion revisit, I'm not sure what the status of this PR is and whether I should still try to figure out the unit tests. |
|
@JakobJingleheimer, please keep this as a draft for now. I will address your questions momentarily too. |
This comment has been hidden.
This comment has been hidden.
This comment has been hidden.
This comment has been hidden.
This comment has been hidden.
This comment has been hidden.
This comment has been hidden.
This comment has been hidden.
This comment has been hidden.
This comment has been hidden.
This comment has been hidden.
This comment has been hidden.
This comment has been hidden.
This comment has been hidden.
This comment has been hidden.
This comment has been hidden.
This comment has been hidden.
This comment has been hidden.
632546c
to
04d9c1b
This comment has been hidden.
This comment has been hidden.
04d9c1b
to
3a3df4e
This comment has been hidden.
This comment has been hidden.
doc: update ESM hook examples esm: fix unsafe primordial doc: fix ESM example linting esm: allow source of type ArrayBuffer doc: update ESM hook changelog to include resolve format esm: allow all ArrayBuffers and TypedArrays for load hook source doc: tidy code & API docs doc: convert ESM source table header from Title Case to Sentence case doc: add detailed explanation for getPackageType esm: add caveat that ESMLoader::import() must NOT be renamed esm: tidy code declaration of getFormat protocolHandlers doc: correct ESM doc link (bad conflict resolution) doc: update ESM hook limitation for CJS esm: tweak preload description doc: update ESM getPackageType() example explanation
3a3df4e
to
1fdc320
This comment has been hidden.
This comment has been hidden.
This comment has been hidden.
This comment has been hidden.
This comment has been hidden.
This comment has been hidden.
|
Finally got a passing CI run! https://ci.nodejs.org/job/node-test-pull-request/39921/ Going to land. @JakobJingleheimer When I rebased off of |
doc: update ESM hook examples esm: fix unsafe primordial doc: fix ESM example linting esm: allow source of type ArrayBuffer doc: update ESM hook changelog to include resolve format esm: allow all ArrayBuffers and TypedArrays for load hook source doc: tidy code & API docs doc: convert ESM source table header from Title Case to Sentence case doc: add detailed explanation for getPackageType esm: add caveat that ESMLoader::import() must NOT be renamed esm: tidy code declaration of getFormat protocolHandlers doc: correct ESM doc link (bad conflict resolution) doc: update ESM hook limitation for CJS esm: tweak preload description doc: update ESM getPackageType() example explanation PR-URL: #37468 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Guy Bedford <guybedford@gmail.com> Reviewed-By: Bradley Farias <bradley.meck@gmail.com> Reviewed-By: Geoffrey Booth <webmaster@geoffreybooth.com>
|
Landed in df22736. Congratulations @JakobJingleheimer!!! |
|
FYI the docs site won’t update until the next release, but you can preview the new docs here: https://github.com/nodejs/node/blob/master/doc/api/esm.md#loaders |

Formed in 2009, the Archive Team (not to be confused with the archive.org Archive-It Team) is a rogue archivist collective dedicated to saving copies of rapidly dying or deleted websites for the sake of history and digital heritage. The group is 100% composed of volunteers and interested parties, and has expanded into a large amount of related projects for saving online and digital history.

closes #35524
Implements step 1 of #36396
resolve=resolve[+getFormat]load=getFormat+getSource+transformSourceglobalPreload=getGlobalPreloadload(wrap existingget_format&get_source; deletetransform_source)resolveERR_UNKNOWN_MODULE_FORMAT) is checked / thrown from afterresolveto afterloadFor code changes:
make -j4 test(UNIX), orvcbuild test(Windows) passes.Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.