mirror of
https://github.com/nodejs/node.git
synced 2025-08-15 13:48:44 +02:00
module: fix conditions override in synchronous resolve hooks
1. Make sure that the conditions are converted into arrays when being passed into user hooks. 2. Pass the conditions from user hooks into the ESM resolution so that it takes effect. PR-URL: https://github.com/nodejs/node/pull/59011 Fixes: https://github.com/nodejs/node/issues/59003 Reviewed-By: Zeyu "Alex" Yang <himself65@outlook.com> Reviewed-By: Jacob Smith <jacob@frende.me>
This commit is contained in:
parent
a7999c602c
commit
6ea421a3d3
10 changed files with 269 additions and 33 deletions
2
test/fixtures/es-modules/custom-condition/node_modules/foo/default.cjs
generated
vendored
Normal file
2
test/fixtures/es-modules/custom-condition/node_modules/foo/default.cjs
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
exports.result = 'default';
|
||||
|
2
test/fixtures/es-modules/custom-condition/node_modules/foo/foo-esm.mjs
generated
vendored
Normal file
2
test/fixtures/es-modules/custom-condition/node_modules/foo/foo-esm.mjs
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
export const result = 'foo-esm';
|
||||
|
2
test/fixtures/es-modules/custom-condition/node_modules/foo/foo.cjs
generated
vendored
Normal file
2
test/fixtures/es-modules/custom-condition/node_modules/foo/foo.cjs
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
exports.result = 'foo';
|
||||
|
28
test/fixtures/es-modules/custom-condition/node_modules/foo/package.json
generated
vendored
Normal file
28
test/fixtures/es-modules/custom-condition/node_modules/foo/package.json
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"exports": {
|
||||
".": {
|
||||
"foo": "./foo.cjs",
|
||||
"foo-esm": "./foo-esm.mjs",
|
||||
"default": "./default.cjs"
|
||||
},
|
||||
"./second": {
|
||||
"foo": "./foo.cjs",
|
||||
"foo-esm": "./foo-esm.mjs",
|
||||
"default": "./default.cjs"
|
||||
},
|
||||
"./third": {
|
||||
"foo": "./foo.cjs",
|
||||
"foo-esm": "./foo-esm.mjs",
|
||||
"default": "./default.cjs"
|
||||
},
|
||||
"./fourth": {
|
||||
"foo": "./foo.cjs",
|
||||
"foo-esm": "./foo-esm.mjs",
|
||||
"default": "./default.cjs"
|
||||
},
|
||||
"./no-default": {
|
||||
"foo": "./foo.cjs",
|
||||
"foo-esm": "./foo-esm.mjs"
|
||||
}
|
||||
}
|
||||
}
|
57
test/module-hooks/test-module-hooks-custom-conditions-cjs.js
Normal file
57
test/module-hooks/test-module-hooks-custom-conditions-cjs.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
// Similar to test-module-hooks-custom-conditions.mjs, but checking the
|
||||
// real require() instead of the re-invented one for imported CJS.
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const { registerHooks } = require('node:module');
|
||||
const assert = require('node:assert');
|
||||
const { cjs, esm } = require('../fixtures/es-modules/custom-condition/load.cjs');
|
||||
|
||||
(async () => {
|
||||
// Without hooks, the default condition is used.
|
||||
assert.strictEqual(cjs('foo').result, 'default');
|
||||
assert.strictEqual((await esm('foo')).result, 'default');
|
||||
|
||||
// Prepending 'foo' to the conditions array in the resolve hook should
|
||||
// allow a CJS to be resolved with that condition.
|
||||
{
|
||||
const hooks = registerHooks({
|
||||
resolve(specifier, context, nextResolve) {
|
||||
assert(Array.isArray(context.conditions));
|
||||
context.conditions = ['foo', ...context.conditions];
|
||||
return nextResolve(specifier, context);
|
||||
},
|
||||
});
|
||||
assert.strictEqual(cjs('foo/second').result, 'foo');
|
||||
assert.strictEqual((await esm('foo/second')).result, 'foo');
|
||||
hooks.deregister();
|
||||
}
|
||||
|
||||
// Prepending 'foo-esm' to the conditions array in the resolve hook should
|
||||
// allow a ESM to be resolved with that condition.
|
||||
{
|
||||
const hooks = registerHooks({
|
||||
resolve(specifier, context, nextResolve) {
|
||||
assert(Array.isArray(context.conditions));
|
||||
context.conditions = ['foo-esm', ...context.conditions];
|
||||
return nextResolve(specifier, context);
|
||||
},
|
||||
});
|
||||
assert.strictEqual(cjs('foo/third').result, 'foo-esm');
|
||||
assert.strictEqual((await esm('foo/third')).result, 'foo-esm');
|
||||
hooks.deregister();
|
||||
}
|
||||
|
||||
// Duplicating the 'foo' condition in the resolve hook should not change the result.
|
||||
{
|
||||
const hooks = registerHooks({
|
||||
resolve(specifier, context, nextResolve) {
|
||||
assert(Array.isArray(context.conditions));
|
||||
context.conditions = ['foo', ...context.conditions, 'foo'];
|
||||
return nextResolve(specifier, context);
|
||||
},
|
||||
});
|
||||
assert.strictEqual(cjs('foo/fourth').result, 'foo');
|
||||
assert.strictEqual((await esm('foo/fourth')).result, 'foo');
|
||||
hooks.deregister();
|
||||
}
|
||||
})().then(common.mustCall());
|
|
@ -0,0 +1,70 @@
|
|||
// Check various special values of `conditions` in the context object
|
||||
// when using synchronous module hooks to override the loaders in a
|
||||
// CJS module.
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const { registerHooks } = require('node:module');
|
||||
const assert = require('node:assert');
|
||||
const { cjs, esm } = require('../fixtures/es-modules/custom-condition/load.cjs');
|
||||
|
||||
(async () => {
|
||||
// Setting it to undefined would lead to the default conditions being used.
|
||||
{
|
||||
const hooks = registerHooks({
|
||||
resolve(specifier, context, nextResolve) {
|
||||
context.conditions = undefined;
|
||||
return nextResolve(specifier, context);
|
||||
},
|
||||
});
|
||||
assert.strictEqual(cjs('foo').result, 'default');
|
||||
assert.strictEqual((await esm('foo')).result, 'default');
|
||||
hooks.deregister();
|
||||
}
|
||||
|
||||
// Setting it to an empty array would lead to the default conditions being used.
|
||||
{
|
||||
const hooks = registerHooks({
|
||||
resolve(specifier, context, nextResolve) {
|
||||
context.conditions = [];
|
||||
return nextResolve(specifier, context);
|
||||
},
|
||||
});
|
||||
assert.strictEqual(cjs('foo/second').result, 'default');
|
||||
assert.strictEqual((await esm('foo/second')).result, 'default');
|
||||
hooks.deregister();
|
||||
}
|
||||
|
||||
// If the exports have no default export, it should error.
|
||||
{
|
||||
const hooks = registerHooks({
|
||||
resolve(specifier, context, nextResolve) {
|
||||
context.conditions = [];
|
||||
return nextResolve(specifier, context);
|
||||
},
|
||||
});
|
||||
assert.throws(() => cjs('foo/no-default'), {
|
||||
code: 'ERR_PACKAGE_PATH_NOT_EXPORTED',
|
||||
});
|
||||
await assert.rejects(esm('foo/no-default'), {
|
||||
code: 'ERR_PACKAGE_PATH_NOT_EXPORTED',
|
||||
});
|
||||
hooks.deregister();
|
||||
}
|
||||
|
||||
// If the exports have no default export, it should error.
|
||||
{
|
||||
const hooks = registerHooks({
|
||||
resolve(specifier, context, nextResolve) {
|
||||
context.conditions = 'invalid';
|
||||
return nextResolve(specifier, context);
|
||||
},
|
||||
});
|
||||
assert.throws(() => cjs('foo/third'), {
|
||||
code: 'ERR_INVALID_ARG_VALUE',
|
||||
});
|
||||
await assert.rejects(esm('foo/third'), {
|
||||
code: 'ERR_INVALID_ARG_VALUE',
|
||||
});
|
||||
hooks.deregister();
|
||||
}
|
||||
})().then(common.mustCall());
|
53
test/module-hooks/test-module-hooks-custom-conditions.mjs
Normal file
53
test/module-hooks/test-module-hooks-custom-conditions.mjs
Normal file
|
@ -0,0 +1,53 @@
|
|||
// This tests that custom conditions can be used in module resolution hooks.
|
||||
import '../common/index.mjs';
|
||||
import { registerHooks } from 'node:module';
|
||||
import assert from 'node:assert';
|
||||
import { cjs, esm } from '../fixtures/es-modules/custom-condition/load.cjs';
|
||||
|
||||
// Without hooks, the default condition is used.
|
||||
assert.strictEqual(cjs('foo').result, 'default');
|
||||
assert.strictEqual((await esm('foo')).result, 'default');
|
||||
|
||||
// Prepending 'foo' to the conditions array in the resolve hook should
|
||||
// allow a CJS to be resolved with that condition.
|
||||
{
|
||||
const hooks = registerHooks({
|
||||
resolve(specifier, context, nextResolve) {
|
||||
assert(Array.isArray(context.conditions));
|
||||
context.conditions = ['foo', ...context.conditions];
|
||||
return nextResolve(specifier, context);
|
||||
},
|
||||
});
|
||||
assert.strictEqual(cjs('foo/second').result, 'foo');
|
||||
assert.strictEqual((await esm('foo/second')).result, 'foo');
|
||||
hooks.deregister();
|
||||
}
|
||||
|
||||
// Prepending 'foo-esm' to the conditions array in the resolve hook should
|
||||
// allow a ESM to be resolved with that condition.
|
||||
{
|
||||
const hooks = registerHooks({
|
||||
resolve(specifier, context, nextResolve) {
|
||||
assert(Array.isArray(context.conditions));
|
||||
context.conditions = ['foo-esm', ...context.conditions];
|
||||
return nextResolve(specifier, context);
|
||||
},
|
||||
});
|
||||
assert.strictEqual(cjs('foo/third').result, 'foo-esm');
|
||||
assert.strictEqual((await esm('foo/third')).result, 'foo-esm');
|
||||
hooks.deregister();
|
||||
}
|
||||
|
||||
// Duplicating the 'foo' condition in the resolve hook should not change the result.
|
||||
{
|
||||
const hooks = registerHooks({
|
||||
resolve(specifier, context, nextResolve) {
|
||||
assert(Array.isArray(context.conditions));
|
||||
context.conditions = ['foo', ...context.conditions, 'foo'];
|
||||
return nextResolve(specifier, context);
|
||||
},
|
||||
});
|
||||
assert.strictEqual(cjs('foo/fourth').result, 'foo');
|
||||
assert.strictEqual((await esm('foo/fourth')).result, 'foo');
|
||||
hooks.deregister();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue