Skip to content
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: add experimental support for addon modules #55844

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

legendecas
Copy link
Member

Add experimental support to loading .node extension modules in ESM.

An addon exports two names default and module.exports, as same as import(cjs) where
its export names can not be preparsed. Addon export names can not be inferred until it is evaluated.

Fixes: #40541
Fixes: #55821

@nodejs-github-bot
Copy link
Collaborator

Review requested:

  • @nodejs/gyp
  • @nodejs/loaders

@nodejs-github-bot nodejs-github-bot added lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. labels Nov 14, 2024
@legendecas legendecas force-pushed the esm/addon branch 3 times, most recently from c6ce9fd to 0c724cd Compare November 14, 2024 10:07
@aduh95
Copy link
Contributor

aduh95 commented Nov 14, 2024

The commit message does not meet our guidelines, the word after the subsystem should be an infinitive verb. I suggest esm: add experimental support for addon modules

test/addons/esm/test.js Outdated Show resolved Hide resolved
@legendecas legendecas changed the title esm: experimental addon modules esm: add experimental addon modules Nov 14, 2024
@legendecas legendecas changed the title esm: add experimental addon modules esm: add experimental support for addon modules Nov 14, 2024
doc/api/cli.md Outdated Show resolved Hide resolved
lib/internal/modules/esm/translators.js Outdated Show resolved Hide resolved
lib/internal/modules/esm/translators.js Outdated Show resolved Hide resolved
lib/internal/validators.js Outdated Show resolved Hide resolved
src/node_options.cc Outdated Show resolved Hide resolved
@legendecas legendecas force-pushed the esm/addon branch 2 times, most recently from 9841645 to 829cfc6 Compare November 27, 2024 14:42
@legendecas legendecas marked this pull request as ready for review November 27, 2024 14:43
@legendecas legendecas marked this pull request as draft November 27, 2024 14:54
@legendecas legendecas marked this pull request as ready for review November 27, 2024 15:03
Copy link

codecov bot commented Nov 27, 2024

Codecov Report

Attention: Patch coverage is 86.73469% with 13 lines in your changes missing coverage. Please review.

Project coverage is 88.54%. Comparing base (a50f3d5) to head (d70a87c).
Report is 17 commits behind head on main.

Files with missing lines Patch % Lines
lib/internal/modules/esm/translators.js 85.22% 13 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #55844      +/-   ##
==========================================
- Coverage   88.55%   88.54%   -0.01%     
==========================================
  Files         657      657              
  Lines      190243   190372     +129     
  Branches    36536    36546      +10     
==========================================
+ Hits       168461   168571     +110     
- Misses      14963    14991      +28     
+ Partials     6819     6810       -9     
Files with missing lines Coverage Δ
lib/internal/modules/esm/formats.js 98.64% <100.00%> (+0.09%) ⬆️
lib/internal/modules/esm/load.js 91.94% <100.00%> (+0.10%) ⬆️
src/node_options.cc 88.00% <100.00%> (+0.01%) ⬆️
src/node_options.h 98.30% <100.00%> (+<0.01%) ⬆️
lib/internal/modules/esm/translators.js 91.50% <85.22%> (-1.44%) ⬇️

... and 44 files with indirect coverage changes

@legendecas legendecas added the request-ci Add this label to start a Jenkins CI on a PR. label Nov 28, 2024
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Nov 28, 2024
@nodejs-github-bot
Copy link
Collaborator

Copy link
Member

@JakobJingleheimer JakobJingleheimer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this 🙂

for the tests, describe etc provide significantly better DX when tests fail

lib/internal/modules/esm/translators.js Outdated Show resolved Hide resolved
lib/internal/modules/esm/translators.js Show resolved Hide resolved
lib/internal/modules/esm/translators.js Outdated Show resolved Hide resolved

export async function run() {
// binding.node
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use describe + it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally find describe + it to be worse UX due to the reasons stated in #56027 (comment), unless there is a way to silence the passing tests and make the console logs from the failed test cases appear at the end together with the errors..

Copy link
Member

@joyeecheung joyeecheung left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some suggestions, but non-blocking.

Addon export names can not be inferred until it is evaluated.

I wonder if there's anything we can do about it...potentially, defining some macro in node.h that the addons can use to export names with a specific pattern, which we can scan for by parsing the dynamic library (ideally through some new API in libuv, though we are already parsing binaries using postject for SEA which implements the parsing by itself...). Can be done as a follow-up, of course..

@@ -23,6 +24,10 @@ if (experimentalWasmModules) {
extensionFormatMap['.wasm'] = 'wasm';
}

if (experimentalAddonModules) {
extensionFormatMap['.node'] = 'addon';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am somewhat surprised to find that there is not a test for the node-addons exports condition...it seems the the files pointed to by node-addons won't get interpreted as addons unless they have the .node extension, though this issue predates this PR and also happens to require().

Copy link
Member Author

@legendecas legendecas Dec 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taking a step back, I'm wondering if the format name could be renamed as node-addon/node-addons for alignment.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, also I am not even sure if anyone is actually using it, it seems only useful to distinguish "if the current run times can load Node.js style addons" as it currently stands, so may not really be that big of a deal anyway....

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer the more concise addon

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I think I misread #55844 (comment) - I think for the format name, addon or node-addon would make more sense. node-addons would be a bit weird in the hooks since it's plural.

Copy link
Member Author

@legendecas legendecas Dec 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This loader format enum is specific for Node.js so I'll stick with the concise addon.

Copy link
Member

@joyeecheung joyeecheung Dec 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In any case, the exports condition question is separate - it's more about when a module that doesn't end with .node is pointed to by node-addons, should it be interpreted(translated) into an addon or should it be interpreted based on whatever extension it has - and if it has a different extension (e.g. dylib or dll), what should happen then. As I said in the first comment, it's fine to keep it as is since CJS also only considers .node, but it is a bit strange for the exports condition.

Copy link
Member Author

@legendecas legendecas Dec 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reality is that the conditional exported entries are been loaded based on their extensions, not their entry types. With require(esm), it is possible to do the following already 👀:

{
  "exports": {
    "import": "./index-require.cjs",
    "require": "./index-module.mjs"
  }
}

lib/internal/modules/esm/translators.js Outdated Show resolved Hide resolved

export async function run() {
// binding.node
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally find describe + it to be worse UX due to the reasons stated in #56027 (comment), unless there is a way to silence the passing tests and make the console logs from the failed test cases appear at the end together with the errors..

test/addons/esm/test-esm.mjs Show resolved Hide resolved
test/addons/esm/test-import.js Show resolved Hide resolved
@joyeecheung
Copy link
Member

This needs a rebase.

@legendecas legendecas added request-ci Add this label to start a Jenkins CI on a PR. and removed request-ci Add this label to start a Jenkins CI on a PR. labels Dec 16, 2024
@nodejs-github-bot

This comment was marked as outdated.

@legendecas legendecas added the request-ci Add this label to start a Jenkins CI on a PR. label Dec 16, 2024
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Dec 16, 2024
@nodejs-github-bot
Copy link
Collaborator

doc/api/module.md Outdated Show resolved Hide resolved
@legendecas legendecas added the request-ci Add this label to start a Jenkins CI on a PR. label Dec 17, 2024
doc/api/module.md Outdated Show resolved Hide resolved
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Dec 17, 2024
@nodejs-github-bot
Copy link
Collaborator

Co-authored-by: Antoine du Hamel <[email protected]>
@legendecas legendecas added commit-queue-squash Add this label to instruct the Commit Queue to squash all the PR commits into the first one. request-ci Add this label to start a Jenkins CI on a PR. labels Dec 18, 2024
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Dec 18, 2024
@nodejs-github-bot
Copy link
Collaborator

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
commit-queue-squash Add this label to instruct the Commit Queue to squash all the PR commits into the first one. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support for import('./native.node') Can not import .node file in esm
7 participants