Skip to content

Conversation

legendecas
Copy link
Member

@legendecas legendecas commented Jun 29, 2025

When a module is being statically linked with module requests, if two
module requests with a same specifier but different attributes are
resolved to two modules, the module requests should be linked to these
two modules.

For example,

import * as secret1 from 'a-json-package' with { type: 'json' };
import * as secret2 from 'a-json-package';

should result in two different module instances, if the second import
is been evaluated as a CommonJS/ESM module by a loader.

ECMA-262 requires that in HostLoadImportedModule, if the operation is called
multiple times with two (referrer, moduleRequest) pairs, it should return the same
result. But if the moduleRequest is different, and the module loader resolves to
different module instances, it should return different module instances.

Refs: https://tc39.es/ecma262/#sec-HostLoadImportedModule
Refs: https://github.com/tc39/proposal-import-attributes?tab=readme-ov-file#how-would-this-proposal-work-with-caching

@nodejs-github-bot
Copy link
Collaborator

Review requested:

  • @nodejs/loaders
  • @nodejs/vm

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. esm Issues and PRs related to the ECMAScript Modules implementation. needs-ci PRs that need a full CI run. vm Issues and PRs related to the vm subsystem. labels Jun 29, 2025
Copy link

codecov bot commented Jun 29, 2025

Codecov Report

❌ Patch coverage is 77.21519% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.08%. Comparing base (4d5ee24) to head (ed81a84).
⚠️ Report is 669 commits behind head on main.

Files with missing lines Patch % Lines
src/module_wrap.cc 69.38% 10 Missing and 5 partials ⚠️
src/module_wrap.h 72.72% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #58886      +/-   ##
==========================================
- Coverage   90.10%   90.08%   -0.03%     
==========================================
  Files         640      640              
  Lines      188493   188502       +9     
  Branches    36971    36977       +6     
==========================================
- Hits       169843   169805      -38     
- Misses      11358    11428      +70     
+ Partials     7292     7269      -23     
Files with missing lines Coverage Δ
lib/internal/modules/esm/module_job.js 97.46% <100.00%> (-0.02%) ⬇️
lib/internal/vm/module.js 98.39% <100.00%> (-0.01%) ⬇️
src/module_wrap.h 44.44% <72.72%> (+44.44%) ⬆️
src/module_wrap.cc 73.15% <69.38%> (+0.40%) ⬆️

... and 44 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@legendecas legendecas force-pushed the module-link-requests branch 4 times, most recently from a8faeb0 to 5ea8994 Compare June 29, 2025 17:55
@nodejs-github-bot
Copy link
Collaborator

@aduh95
Copy link
Contributor

aduh95 commented Jun 30, 2025

But if the moduleRequest is different, it should return a different

That's not how I interpret the spec, AFAICT the spec says nothing about what the behavior should if they are different. If specifier and/or moduleRequest is different, the return value may or may not be different – e.g. import('fs') === import('node:fs').
Do we have a use case for supporting multiple modules with the same specifier?

@legendecas
Copy link
Member Author

legendecas commented Jun 30, 2025

But if the moduleRequest is different, it should return a different

That's not how I interpret the spec, AFAICT the spec says nothing about what the behavior should if they are different. If specifier and/or moduleRequest is different, the return value may or may not be different – e.g. import('fs') === import('node:fs'). Do we have a use case for supporting multiple modules with the same specifier?

The spec didn't say the two specifier may be mapped to a single module.

The issue is that, if a loader resolves a single specifier with different import attributes, to two modules, the current implementation take the latter because the cache is keyed by specifier only.

If a loader interprets additional attributes, like:

import * as secret1 from 'a-json-package' with { type: 'json', integrity: 'frozen' };
import * as secret2 from 'a-json-package' with { type: 'json' };

This will be problematic because both namespaces secret1 and secret2 refer to the same mutable second resolution result. This is linked in https://github.com/tc39/proposal-import-attributes?tab=readme-ov-file#how-would-this-proposal-work-with-caching that the attributes should be part of the module cache key.

Copy link
Contributor

@guybedford guybedford left a comment

Choose a reason for hiding this comment

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

This definitely better aligns with the specification, thanks for doing this work.

@@ -0,0 +1,52 @@
import '../common/index.mjs';
import assert from 'node:assert';
import Module from 'node:module';
Copy link
Contributor

Choose a reason for hiding this comment

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

I wasn't aware of a convention to import Module instead of module here. What makes Module different from other core libs here? Seems it should use the same casing unless there is a good reason.

Copy link
Member Author

Choose a reason for hiding this comment

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

It's just my preference lasted from CJS that to prevent name collision with CJS module.

Copy link
Member

Choose a reason for hiding this comment

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

In node:module, module.exports = Module, so the default export is also the Module constructor.

@legendecas legendecas force-pushed the module-link-requests branch 2 times, most recently from 6a8b7e7 to 1abf30b Compare June 30, 2025 23:20
@nodejs-github-bot
Copy link
Collaborator

@legendecas legendecas force-pushed the module-link-requests branch from 1abf30b to f7185b1 Compare July 2, 2025 10:43
@nodejs-github-bot
Copy link
Collaborator

@legendecas legendecas force-pushed the module-link-requests branch from f7185b1 to 008345a Compare July 3, 2025 09:09
@nodejs-github-bot
Copy link
Collaborator

@legendecas
Copy link
Member Author

@aduh95 @guybedford @joyeecheung updated PR to use hash+equality caching. PTAL again, thanks!

@nodejs-github-bot
Copy link
Collaborator

@joyeecheung
Copy link
Member

I think we might still be able to avoid the extra string storage & transcoding with cppgc-managed wrappers; but that's probably a task for another PR.

@joyeecheung joyeecheung added the request-ci Add this label to start a Jenkins CI on a PR. label Jul 3, 2025
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Jul 3, 2025
@nodejs-github-bot
Copy link
Collaborator

When a module is being statically linked with module requests, if two
module requests with a same specifier but different attributes are
resolved to two modules, the module requests should be linked to these
two modules.
@legendecas legendecas force-pushed the module-link-requests branch from 008345a to ed81a84 Compare July 4, 2025 08:57
@nodejs-github-bot
Copy link
Collaborator

@legendecas legendecas added the commit-queue Add this label to land a pull request using GitHub Actions. label Jul 4, 2025
@nodejs-github-bot nodejs-github-bot removed the commit-queue Add this label to land a pull request using GitHub Actions. label Jul 4, 2025
@nodejs-github-bot nodejs-github-bot merged commit cc856b3 into nodejs:main Jul 4, 2025
56 checks passed
@nodejs-github-bot
Copy link
Collaborator

Landed in cc856b3

@legendecas legendecas deleted the module-link-requests branch July 4, 2025 19:57
RafaelGSS pushed a commit that referenced this pull request Jul 8, 2025
When a module is being statically linked with module requests, if two
module requests with a same specifier but different attributes are
resolved to two modules, the module requests should be linked to these
two modules.

PR-URL: #58886
Refs: https://tc39.es/ecma262/#sec-HostLoadImportedModule
Refs: https://github.com/tc39/proposal-import-attributes?tab=readme-ov-file#how-would-this-proposal-work-with-caching
Reviewed-By: Guy Bedford <[email protected]>
Reviewed-By: Joyee Cheung <[email protected]>
@aduh95 aduh95 added the backport-requested-v22.x PRs awaiting manual backport to the v22.x-staging branch. label Jul 21, 2025
@aduh95
Copy link
Contributor

aduh95 commented Jul 21, 2025

This doesn't land cleanly on v22.x-staging

@legendecas legendecas added backport-open-v22.x Indicate that the PR has an open backport and removed backport-requested-v22.x PRs awaiting manual backport to the v22.x-staging branch. labels Sep 24, 2025
aduh95 pushed a commit that referenced this pull request Oct 7, 2025
When a module is being statically linked with module requests, if two
module requests with a same specifier but different attributes are
resolved to two modules, the module requests should be linked to these
two modules.

PR-URL: #58886
Backport-PR-URL: #60000
Refs: https://tc39.es/ecma262/#sec-HostLoadImportedModule
Refs: https://github.com/tc39/proposal-import-attributes?tab=readme-ov-file#how-would-this-proposal-work-with-caching
Reviewed-By: Guy Bedford <[email protected]>
Reviewed-By: Joyee Cheung <[email protected]>
@aduh95 aduh95 added backported-to-v22.x PRs backported to the v22.x-staging branch. and removed backport-open-v22.x Indicate that the PR has an open backport labels Oct 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backported-to-v22.x PRs backported to the v22.x-staging branch. c++ Issues and PRs that require attention from people who are familiar with C++. esm Issues and PRs related to the ECMAScript Modules implementation. needs-ci PRs that need a full CI run. vm Issues and PRs related to the vm subsystem.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants