fix(javascript): honor module-sync package exports (#1361)
This commit is contained in:
parent
a9f5dad5a8
commit
d0e606930a
|
|
@ -307,6 +307,8 @@ such as `./web3:app.js` still names a local artifact.
|
|||
Package `exports` fallback arrays are supported both at the top level and under
|
||||
the root `"."` entry. The resolver selects targets in declared order using the
|
||||
same conditional and invalid-entry handling as nested exports arrays.
|
||||
For imports and requires, the active Node conditions include `module-sync`;
|
||||
earlier active conditions retain precedence over later ones.
|
||||
|
||||
ESM relative module paths and selected package exports targets use URL suffix handling
|
||||
and one percent-decoding pass. CommonJS relative paths and legacy package main
|
||||
|
|
|
|||
|
|
@ -640,7 +640,8 @@ const exportTargets = (
|
|||
* Node selects an exports target by walking the declared keys in order and
|
||||
* taking the first whose condition is active for the calling resolver.
|
||||
* "default" is always active, and "node" plus "node-addons" are active for the
|
||||
* built-in resolver that owns installed-package imports and requires.
|
||||
* built-in resolver that owns installed-package imports and requires. The
|
||||
* supported Node runtimes also activate "module-sync" for both loaders.
|
||||
*/
|
||||
const packageExportConditions = (
|
||||
moduleKind: ResolveArtifactPathInput["moduleKind"],
|
||||
|
|
@ -648,6 +649,7 @@ const packageExportConditions = (
|
|||
new Set([
|
||||
"node",
|
||||
"node-addons",
|
||||
"module-sync",
|
||||
...(moduleKind === undefined ? [] : [moduleKind]),
|
||||
"default",
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -103,6 +103,27 @@ const cases: readonly {
|
|||
exports: { browser: "./../outside.cjs", default: "./actual.cjs" },
|
||||
expected: "node_modules/fixture/actual.cjs",
|
||||
},
|
||||
{
|
||||
name: "active module-sync rejects an invalid target instead of default",
|
||||
exports: { "module-sync": "./../outside.cjs", default: "./actual.cjs" },
|
||||
expected: null,
|
||||
nodeError: "ERR_INVALID_PACKAGE_TARGET",
|
||||
rejectedTarget: "./../outside.cjs",
|
||||
},
|
||||
{
|
||||
name: "module-sync null blocks default",
|
||||
exports: { "module-sync": null, default: "./actual.cjs" },
|
||||
expected: null,
|
||||
nodeError: "ERR_PACKAGE_PATH_NOT_EXPORTED",
|
||||
},
|
||||
{
|
||||
name: "unmatched nested module-sync permits default",
|
||||
exports: {
|
||||
"module-sync": { browser: "./missing.cjs" },
|
||||
default: "./actual.cjs",
|
||||
},
|
||||
expected: "node_modules/fixture/actual.cjs",
|
||||
},
|
||||
...["./bad%.cjs", "./bad%C3.cjs"].map((target) => ({
|
||||
name: `malformed URL ${target} is selected before URL decoding`,
|
||||
exports: [target, "./actual.cjs"],
|
||||
|
|
|
|||
|
|
@ -202,6 +202,36 @@ it.each([
|
|||
exports: { import: "./exported.mjs", require: "./actual.cjs" },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "import selects module-sync before default",
|
||||
kind: "import",
|
||||
metadata: {
|
||||
exports: { "module-sync": "./sync.mjs", default: "./actual.cjs" },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "require selects nested module-sync before default",
|
||||
kind: "require",
|
||||
metadata: {
|
||||
exports: {
|
||||
".": { node: { "module-sync": "./sync.mjs", default: "./actual.cjs" } },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "an earlier import condition precedes module-sync",
|
||||
kind: "import",
|
||||
metadata: {
|
||||
exports: { import: "./exported.mjs", "module-sync": "./sync.mjs" },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "an earlier default condition precedes module-sync",
|
||||
kind: "require",
|
||||
metadata: {
|
||||
exports: { default: "./actual.cjs", "module-sync": "./sync.mjs" },
|
||||
},
|
||||
},
|
||||
] as const)(
|
||||
"matches the native Node loader when $name",
|
||||
async ({ kind, metadata }) => {
|
||||
|
|
@ -210,7 +240,7 @@ it.each([
|
|||
const source =
|
||||
kind === "import"
|
||||
? `import value from "fixture"; console.log(JSON.stringify({value, resolved: import.meta.resolve("fixture")}));`
|
||||
: `const value = require("fixture"); console.log(JSON.stringify({value, resolved: require.resolve("fixture")}));`;
|
||||
: `const value = require("fixture"); console.log(JSON.stringify({value: typeof value === "string" ? value : value.default, resolved: require.resolve("fixture")}));`;
|
||||
const files = {
|
||||
"package.json": JSON.stringify({
|
||||
name: "app",
|
||||
|
|
@ -226,6 +256,7 @@ it.each([
|
|||
"node_modules/fixture/index.js": "module.exports = 'index.js';",
|
||||
"node_modules/fixture/bundler.mjs": "export default 'bundler.mjs';",
|
||||
"node_modules/fixture/exported.mjs": "export default 'exported.mjs';",
|
||||
"node_modules/fixture/sync.mjs": "export default 'sync.mjs';",
|
||||
};
|
||||
await Promise.all(
|
||||
Object.entries(files).map(async ([path, text]) => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue