Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

View File

@ -0,0 +1,7 @@
prefix module-hooks
# To mark a test as flaky, list the test name in the appropriate section
# below, without ".js", followed by ": PASS,FLAKY". Example:
# sample-test : PASS,FLAKY
[true] # This section applies to all platforms

View File

@ -0,0 +1,35 @@
// Flags: --no-experimental-wasm-modules
// This tests that module.registerHooks() can be used to support unknown formats, like
// import(wasm) (without --experimental-wasm-modules).
import '../common/index.mjs';
import assert from 'node:assert';
import { registerHooks, createRequire } from 'node:module';
import { readFileSync } from 'node:fs';
registerHooks({
load(url, context, nextLoad) {
assert.match(url, /simple\.wasm$/);
const source =
`const buf = Buffer.from([${Array.from(readFileSync(new URL(url))).join(',')}]);
const compiled = new WebAssembly.Module(buf);
const { exports } = new WebAssembly.Instance(compiled);
export default exports;
export { exports as 'module.exports' };
`;
return {
shortCircuit: true,
source,
format: 'module',
};
},
});
// Checks that it works with require.
const require = createRequire(import.meta.url);
const { add } = require('../fixtures/simple.wasm');
assert.strictEqual(add(1, 2), 3);
// Checks that it works with import.
const { default: { add: add2 } } = await import('../fixtures/simple.wasm');
assert.strictEqual(add2(1, 2), 3);

View File

@ -0,0 +1,50 @@
'use strict';
require('../common');
const assert = require('assert');
const { registerHooks } = require('module');
// This tests that the source in the load hook can be returned as
// array buffers or array buffer views.
const arrayBufferSource = 'module.exports = "arrayBuffer"';
const arrayBufferViewSource = 'module.exports = "arrayBufferView"';
const encoder = new TextEncoder();
const hook1 = registerHooks({
resolve(specifier, context, nextResolve) {
return { shortCircuit: true, url: `test://${specifier}` };
},
load(url, context, nextLoad) {
const result = nextLoad(url, context);
if (url === 'test://array_buffer') {
assert.deepStrictEqual(result.source, encoder.encode(arrayBufferSource).buffer);
} else if (url === 'test://array_buffer_view') {
assert.deepStrictEqual(result.source, encoder.encode(arrayBufferViewSource));
}
return result;
},
});
const hook2 = registerHooks({
load(url, context, nextLoad) {
if (url === 'test://array_buffer') {
return {
shortCircuit: true,
source: encoder.encode(arrayBufferSource).buffer,
};
} else if (url === 'test://array_buffer_view') {
return {
shortCircuit: true,
source: encoder.encode(arrayBufferViewSource),
};
}
assert.fail('unreachable');
},
});
assert.strictEqual(require('array_buffer'), 'arrayBuffer');
assert.strictEqual(require('array_buffer_view'), 'arrayBufferView');
hook1.deregister();
hook2.deregister();

View File

@ -0,0 +1,29 @@
import { mustCall } from '../common/index.mjs';
import assert from 'node:assert';
import { registerHooks } from 'node:module';
import process from 'node:process';
// This tests that imported builtins get null as source from default
// step, and the source returned are ignored.
// TODO(joyeecheung): this is to align with the module.register() behavior
// but perhaps the load hooks should not be invoked for builtins at all.
// Pick a builtin that's unlikely to be loaded already - like zlib.
assert(!process.moduleLoadList.includes('NativeModule zlib'));
const hook = registerHooks({
load: mustCall(function load(url, context, nextLoad) {
assert.strictEqual(url, 'node:zlib');
const result = nextLoad(url, context);
assert.strictEqual(result.source, null);
return {
source: 'throw new Error("I should not be thrown")',
format: 'builtin',
};
}),
});
const ns = await import('node:zlib');
assert.strictEqual(typeof ns.createGzip, 'function');
hook.deregister();

View File

@ -0,0 +1,29 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const { registerHooks } = require('module');
// This tests that required builtins get null as source from default
// step, and the source returned are ignored.
// TODO(joyeecheung): this is to align with the module.register() behavior
// but perhaps the load hooks should not be invoked for builtins at all.
// Pick a builtin that's unlikely to be loaded already - like zlib.
assert(!process.moduleLoadList.includes('NativeModule zlib'));
const hook = registerHooks({
load: common.mustCall(function load(url, context, nextLoad) {
assert.strictEqual(url, 'node:zlib');
const result = nextLoad(url, context);
assert.strictEqual(result.source, null);
return {
source: 'throw new Error("I should not be thrown")',
format: 'builtin',
};
}),
});
assert.strictEqual(typeof require('zlib').createGzip, 'function');
hook.deregister();

View File

@ -0,0 +1,34 @@
'use strict';
require('../common');
const assert = require('assert');
const { registerHooks } = require('module');
// Test that multiple loaders works together.
const hook1 = registerHooks({
load(url, context, nextLoad) {
const result = nextLoad(url, context);
assert.strictEqual(result.source, '');
return {
source: 'exports.hello = "world"',
format: 'commonjs',
};
},
});
const hook2 = registerHooks({
load(url, context, nextLoad) {
const result = nextLoad(url, context);
assert.strictEqual(result.source, 'exports.hello = "world"');
return {
source: 'export const hello = "world"',
format: 'module',
};
},
});
const mod = require('../fixtures/empty.js');
assert.strictEqual(mod.hello, 'world');
hook1.deregister();
hook2.deregister();

View File

@ -0,0 +1,31 @@
// Test that the context parameter will be merged in multiple load hooks.
import * as common from '../common/index.mjs';
import assert from 'node:assert';
import { registerHooks } from 'node:module';
const hook1 = registerHooks({
load: common.mustCall(function(url, context, nextLoad) {
assert.strictEqual(context.testProp, 'custom'); // It should be merged from hook 2 and 3.
return nextLoad(url, context);
}, 1),
});
const hook2 = registerHooks({
load: common.mustCall(function(url, context, nextLoad) {
assert.strictEqual(context.testProp, 'custom'); // It should be merged from hook 3.
return nextLoad(url); // Omit the context.
}, 1),
});
const hook3 = registerHooks({
load: common.mustCall(function(url, context, nextLoad) {
return nextLoad(url, { testProp: 'custom' }); // Add a custom property
}, 1),
});
await import('../fixtures/es-modules/message.mjs');
hook3.deregister();
hook2.deregister();
hook1.deregister();

View File

@ -0,0 +1,33 @@
'use strict';
// Test that the context parameter will be merged in multiple load hooks.
const common = require('../common');
const assert = require('assert');
const { registerHooks } = require('module');
const hook1 = registerHooks({
load: common.mustCall(function(url, context, nextLoad) {
assert.strictEqual(context.testProp, 'custom'); // It should be merged from hook 2 and 3.
return nextLoad(url, context);
}, 1),
});
const hook2 = registerHooks({
load: common.mustCall(function(url, context, nextLoad) {
assert.strictEqual(context.testProp, 'custom'); // It should be merged from hook 3.
return nextLoad(url); // Omit the context.
}, 1),
});
const hook3 = registerHooks({
load: common.mustCall(function(url, context, nextLoad) {
return nextLoad(url, { testProp: 'custom' }); // Add a custom property
}, 1),
});
require('../fixtures/empty.js');
hook3.deregister();
hook2.deregister();
hook1.deregister();

View File

@ -0,0 +1,14 @@
// Test that the context parameter can be omitted in the nextLoad invocation.
import * as common from '../common/index.mjs';
import { registerHooks } from 'node:module';
const hook = registerHooks({
load: common.mustCall(function(url, context, nextLoad) {
return nextLoad(url);
}, 1),
});
await import('../fixtures/es-modules/message.mjs');
hook.deregister();

View File

@ -0,0 +1,16 @@
'use strict';
// Test that the context parameter can be omitted in the nextLoad invocation.
const common = require('../common');
const { registerHooks } = require('module');
const hook = registerHooks({
load: common.mustCall(function(url, context, nextLoad) {
return nextLoad(url);
}, 1),
});
require('../fixtures/empty.js');
hook.deregister();

View File

@ -0,0 +1,21 @@
'use strict';
require('../common');
const assert = require('assert');
const { registerHooks } = require('module');
// Test that module syntax detection works.
const hook = registerHooks({
load(url, context, nextLoad) {
const result = nextLoad(url, context);
assert.strictEqual(result.source, '');
return {
source: 'export const hello = "world"',
};
},
});
const mod = require('../fixtures/empty.js');
assert.strictEqual(mod.hello, 'world');
hook.deregister();

View File

@ -0,0 +1,51 @@
'use strict';
// This tests a pirates-like load hook works.
const common = require('../common');
const assert = require('assert');
const fixtures = require('../common/fixtures');
const { readFileSync } = require('fs');
const loader = require('../fixtures/module-hooks/load-from-this-dir');
const { addHook } = require('../fixtures/module-hooks/add-hook');
const matcherArgs = [];
function matcher(filename) {
matcherArgs.push(filename);
return true;
}
const hookArgs = [];
function hook(code, filename) {
hookArgs.push({ code, filename });
return code.replace('$key', 'hello');
}
(async () => {
const revert = addHook(hook, { exts: ['.js'], matcher });
{
const foo = await loader.import('foo-esm');
const filename = fixtures.path('module-hooks', 'node_modules', 'foo-esm', 'foo-esm.js');
assert.deepStrictEqual(matcherArgs, [filename]);
const code = readFileSync(filename, 'utf-8');
assert.deepStrictEqual(hookArgs, [{ code, filename }]);
assert.deepStrictEqual({ ...foo }, { hello: 'foo-esm' });
}
matcherArgs.splice(0, 1);
hookArgs.splice(0, 1);
revert();
// Later loads are unaffected.
{
const bar = await loader.import('bar-esm');
assert.deepStrictEqual(matcherArgs, []);
assert.deepStrictEqual(hookArgs, []);
assert.deepStrictEqual({ ...bar }, { $key: 'bar-esm' });
}
})().catch(common.mustNotCall());

View File

@ -0,0 +1,51 @@
'use strict';
// This tests a pirates-like load hook works.
const common = require('../common');
const assert = require('assert');
const fixtures = require('../common/fixtures');
const { readFileSync } = require('fs');
const loader = require('../fixtures/module-hooks/load-from-this-dir');
const { addHook } = require('../fixtures/module-hooks/add-hook');
const matcherArgs = [];
function matcher(filename) {
matcherArgs.push(filename);
return true;
}
const hookArgs = [];
function hook(code, filename) {
hookArgs.push({ code, filename });
return code.replace('$key', 'hello');
}
(async () => {
const revert = addHook(hook, { exts: ['.js'], matcher });
{
const foo = await loader.import('foo-esm');
const filename = fixtures.path('module-hooks', 'node_modules', 'foo-esm', 'foo-esm.js');
assert.deepStrictEqual(matcherArgs, [filename]);
const code = readFileSync(filename, 'utf-8');
assert.deepStrictEqual(hookArgs, [{ code, filename }]);
assert.deepStrictEqual({ ...foo }, { hello: 'foo-esm' });
}
matcherArgs.splice(0, 1);
hookArgs.splice(0, 1);
revert();
// Later loads are unaffected.
{
const bar = await loader.import('bar-esm');
assert.deepStrictEqual(matcherArgs, []);
assert.deepStrictEqual(hookArgs, []);
assert.deepStrictEqual({ ...bar }, { $key: 'bar-esm' });
}
})().catch(common.mustNotCall());

View File

@ -0,0 +1,39 @@
'use strict';
require('../common');
const assert = require('assert');
const { registerHooks } = require('module');
// This tests that the invalid return values in load hooks are not accepted.
const hook = registerHooks({
resolve(specifier, context, nextResolve) {
return { shortCircuit: true, url: `test://${specifier}` };
},
load(url, context, nextLoad) {
const result = { shortCircuit: true };
if (url.endsWith('array')) {
result.source = [];
} else if (url.endsWith('null')) {
result.source = null;
} else if (url.endsWith('number')) {
result.source = 1;
} else if (url.endsWith('boolean')) {
result.source = true;
} else if (url.endsWith('function')) {
result.source = () => {};
} else if (url.endsWith('object')) {
result.source = {};
}
return result;
},
});
for (const item of ['undefined', 'array', 'null', 'number', 'boolean', 'function', 'object']) {
assert.throws(() => { require(item); }, {
code: 'ERR_INVALID_RETURN_PROPERTY_VALUE',
message: /"source" from the "load" hook/,
});
}
hook.deregister();

View File

@ -0,0 +1,48 @@
'use strict';
// This tests a pirates-like load hook works.
require('../common');
const assert = require('assert');
const fixtures = require('../common/fixtures');
const { readFileSync } = require('fs');
const loader = require('../fixtures/module-hooks/load-from-this-dir');
const { addHook } = require('../fixtures/module-hooks/add-hook');
const matcherArgs = [];
function matcher(filename) {
matcherArgs.push(filename);
return true;
}
const hookArgs = [];
function hook(code, filename) {
hookArgs.push({ code, filename });
return code.replace('$key', 'hello');
}
const revert = addHook(hook, { exts: ['.js'], matcher });
{
const foo = loader.require('foo');
const filename = fixtures.path('module-hooks', 'node_modules', 'foo', 'foo.js');
assert.deepStrictEqual(matcherArgs, [filename]);
const code = readFileSync(filename, 'utf-8');
assert.deepStrictEqual(hookArgs, [{ code, filename }]);
assert.deepStrictEqual(foo, { hello: 'foo' });
}
matcherArgs.splice(0, 1);
hookArgs.splice(0, 1);
revert();
// Later loads are unaffected.
{
const bar = loader.require('bar');
assert.deepStrictEqual(matcherArgs, []);
assert.deepStrictEqual(hookArgs, []);
assert.deepStrictEqual(bar, { $key: 'bar' });
}

View File

@ -0,0 +1,33 @@
'use strict';
require('../common');
const assert = require('assert');
const { registerHooks } = require('module');
// Test that shortCircuit is required in a middle hook when nextLoad is not called.
const hook1 = registerHooks({
load(url, context, nextLoad) {
return nextLoad(url, context);
},
});
const hook2 = registerHooks({
load(url, context, nextLoad) {
if (url.includes('empty')) {
return {
format: 'commonjs',
source: 'module.exports = "modified"',
};
}
return nextLoad(url, context);
},
});
assert.throws(() => {
require('../fixtures/empty.js');
}, {
code: 'ERR_INVALID_RETURN_PROPERTY_VALUE',
message: /shortCircuit/,
});
hook1.deregister();
hook2.deregister();

View File

@ -0,0 +1,29 @@
'use strict';
require('../common');
const assert = require('assert');
const { registerHooks } = require('module');
// Test that shortCircuit is required in the starting hook when nextLoad is not called.
const hook = registerHooks({
load(url, context, nextLoad) {
if (url.includes('empty')) {
return {
format: 'commonjs',
source: 'module.exports = "modified"',
};
}
return nextLoad(url, context);
},
});
assert.throws(() => {
require('../fixtures/empty.js');
}, {
code: 'ERR_INVALID_RETURN_PROPERTY_VALUE',
message: /shortCircuit/,
});
const baz = require('../fixtures/baz.js');
assert.strictEqual(baz, 'perhaps I work');
hook.deregister();

View File

@ -0,0 +1,28 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const { registerHooks } = require('module');
// Test that shortCircuit is required in a middle hook when nextResolve is not called.
const hook1 = registerHooks({
load: common.mustNotCall(),
});
const hook2 = registerHooks({
load(url, context, nextLoad) {
if (url.includes('empty')) {
return {
format: 'commonjs',
source: 'module.exports = "modified"',
shortCircuit: true,
};
}
return nextLoad(url, context);
},
});
const value = require('../fixtures/empty.js');
assert.strictEqual(value, 'modified');
hook1.deregister();
hook2.deregister();

View File

@ -0,0 +1,25 @@
import { mustCall } from '../common/index.mjs';
import assert from 'node:assert';
import { registerHooks } from 'node:module';
import { fileURL } from '../common/fixtures.mjs';
// This tests shows the url parameter in `load` can be changed in the `nextLoad` call
// It changes `foo` package name into `redirected-fs` and then loads `redirected-fs`
const hook = registerHooks({
resolve(specifier, context, nextResolve) {
assert.strictEqual(specifier, 'foo');
return {
url: 'foo://bar',
shortCircuit: true,
};
},
load: mustCall(function load(url, context, nextLoad) {
assert.strictEqual(url, 'foo://bar');
return nextLoad(fileURL('module-hooks', 'redirected-fs.js').href, context);
}),
});
assert.strictEqual((await import('foo')).exports_for_test, 'redirected fs');
hook.deregister();

View File

@ -0,0 +1,30 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const { registerHooks } = require('module');
const fixtures = require('../common/fixtures');
// This tests shows the url parameter in `load` can be changed in the `nextLoad` call
// It changes `foo` package name into `redirected-fs` and then loads `redirected-fs`
const hook = registerHooks({
resolve(specifier, context, nextResolve) {
assert.strictEqual(specifier, 'foo');
return {
url: 'foo://bar',
shortCircuit: true,
};
},
load: common.mustCall(function load(url, context, nextLoad) {
assert.strictEqual(url, 'foo://bar');
return nextLoad(
fixtures.fileURL('module-hooks', 'redirected-fs.js').href,
context,
);
}),
});
assert.strictEqual(require('foo').exports_for_test, 'redirected fs');
hook.deregister();

View File

@ -0,0 +1,47 @@
'use strict';
require('../common');
const fixtures = require('../common/fixtures.js');
const { spawnSyncAndAssert } = require('../common/child_process.js');
spawnSyncAndAssert(process.execPath,
[
'--require',
fixtures.path('module-hooks', 'register-typescript-hooks.js'),
fixtures.path('module-hooks', 'log-user.cts'),
], {
trim: true,
stdout: 'UserAccount { name: \'john\', id: 100, type: 1 }',
});
spawnSyncAndAssert(process.execPath,
[
'--no-experimental-transform-types',
'--require',
fixtures.path('module-hooks', 'register-typescript-hooks.js'),
fixtures.path('module-hooks', 'log-user.cts'),
], {
trim: true,
stdout: 'UserAccount { name: \'john\', id: 100, type: 1 }',
});
spawnSyncAndAssert(process.execPath,
[
'--import',
fixtures.fileURL('module-hooks', 'register-typescript-hooks.js'),
fixtures.path('module-hooks', 'log-user.mts'),
], {
trim: true,
stdout: 'UserAccount { name: \'john\', id: 100, type: 1 }',
});
spawnSyncAndAssert(process.execPath,
[
'--no-experimental-transform-types',
'--import',
fixtures.fileURL('module-hooks', 'register-typescript-hooks.js'),
fixtures.path('module-hooks', 'log-user.mts'),
], {
trim: true,
stdout: 'UserAccount { name: \'john\', id: 100, type: 1 }',
});

View File

@ -0,0 +1,34 @@
// Flags: --no-experimental-wasm-modules
'use strict';
// This tests that module.registerHooks() can be used to support unknown formats, like
// require(wasm) and import(wasm) (without --experimental-wasm-modules).
const common = require('../common');
const assert = require('assert');
const { registerHooks } = require('module');
const { readFileSync } = require('fs');
registerHooks({
load(url, context, nextLoad) {
assert.match(url, /simple\.wasm$/);
const source =
`const buf = Buffer.from([${Array.from(readFileSync(new URL(url))).join(',')}]);
const compiled = new WebAssembly.Module(buf);
module.exports = (new WebAssembly.Instance(compiled)).exports;`;
return {
shortCircuit: true,
source,
format: 'commonjs',
};
},
});
// Checks that it works with require.
const { add } = require('../fixtures/simple.wasm');
assert.strictEqual(add(1, 2), 3);
(async () => { // Checks that it works with import.
const { default: { add } } = await import('../fixtures/simple.wasm');
assert.strictEqual(add(1, 2), 3);
})().then(common.mustCall());

View File

@ -0,0 +1,27 @@
import '../common/index.mjs';
import assert from 'node:assert';
import { registerHooks } from 'node:module';
import process from 'node:process';
// This tests that builtins can be redirected to another builtin.
// Pick a builtin that's unlikely to be loaded already - like zlib.
assert(!process.moduleLoadList.includes('NativeModule zlib'));
const hook = registerHooks({
resolve(specifier, context, nextLoad) {
if (specifier === 'node:assert') {
return {
url: 'node:zlib',
shortCircuit: true,
};
}
},
});
// Check assert, which is already loaded.
// zlib.createGzip is a function.
const redirected = await import('node:assert');
assert.strictEqual(typeof redirected.createGzip, 'function');
hook.deregister();

View File

@ -0,0 +1,26 @@
'use strict';
require('../common');
const assert = require('assert');
const { registerHooks } = require('module');
// This tests that builtins can be redirected to another builtin.
// Pick a builtin that's unlikely to be loaded already - like zlib.
assert(!process.moduleLoadList.includes('NativeModule zlib'));
const hook = registerHooks({
resolve(specifier, context, nextLoad) {
if (specifier === 'assert') {
return {
url: 'node:zlib',
shortCircuit: true,
};
}
},
});
// Check assert, which is already loaded.
// zlib.createGzip is a function.
assert.strictEqual(typeof require('assert').createGzip, 'function');
hook.deregister();

View File

@ -0,0 +1,36 @@
import '../common/index.mjs';
import { fileURL } from '../common/fixtures.mjs';
import assert from 'node:assert';
import { registerHooks } from 'node:module';
import process from 'node:process';
// This tests that builtins can be redirected to a local file.
// Pick a builtin that's unlikely to be loaded already - like zlib.
assert(!process.moduleLoadList.includes('NativeModule zlib'));
const hook = registerHooks({
resolve(specifier, context, nextLoad) {
// FIXME(joyeecheung): when it gets redirected to a CommonJS module, the
// ESM loader invokes the CJS loader with the resolved URL again even when
// it already has the url and source code. Fix it so that the hooks are
// skipped during the second loading.
if (!specifier.startsWith('node:')) {
return nextLoad(specifier, context);
}
return {
url: fileURL(
'module-hooks',
`redirected-${specifier.replace('node:', '')}.js`).href,
shortCircuit: true,
};
},
});
// Check assert, which is already loaded.
assert.strictEqual((await import('node:assert')).exports_for_test, 'redirected assert');
// Check zlib, which is not yet loaded.
assert.strictEqual((await import('node:zlib')).exports_for_test, 'redirected zlib');
// Check fs, which is redirected to an ESM
assert.strictEqual((await import('node:fs')).exports_for_test, 'redirected fs');
hook.deregister();

View File

@ -0,0 +1,29 @@
'use strict';
require('../common');
const assert = require('assert');
const { registerHooks } = require('module');
const fixtures = require('../common/fixtures');
// This tests that builtins can be redirected to a local file.
// Pick a builtin that's unlikely to be loaded already - like zlib.
assert(!process.moduleLoadList.includes('NativeModule zlib'));
const hook = registerHooks({
resolve(specifier, context, nextLoad) {
return {
url: fixtures.fileURL('module-hooks', `redirected-${specifier}.js`).href,
shortCircuit: true,
};
},
});
// Check assert, which is already loaded.
assert.strictEqual(require('assert').exports_for_test, 'redirected assert');
// Check zlib, which is not yet loaded.
assert.strictEqual(require('zlib').exports_for_test, 'redirected zlib');
// Check fs, which is redirected to an ESM
assert.strictEqual(require('fs').exports_for_test, 'redirected fs');
hook.deregister();

View File

@ -0,0 +1,32 @@
// Test that the context parameter will be merged in multiple resolve hooks.
import * as common from '../common/index.mjs';
import assert from 'node:assert';
import { registerHooks } from 'node:module';
const hook1 = registerHooks({
resolve: common.mustCall(function(specifier, context, nextResolve) {
assert.strictEqual(context.testProp, 'custom'); // It should be merged from hook 2 and 3.
const result = nextResolve(specifier, context);
return result;
}, 1),
});
const hook2 = registerHooks({
resolve: common.mustCall(function(specifier, context, nextResolve) {
assert.strictEqual(context.testProp, 'custom'); // It should be merged from hook 3.
return nextResolve(specifier); // Omit the context.
}, 1),
});
const hook3 = registerHooks({
resolve: common.mustCall(function(specifier, context, nextResolve) {
return nextResolve(specifier, { testProp: 'custom' }); // Add a custom property
}, 1),
});
await import('../fixtures/es-modules/message.mjs');
hook3.deregister();
hook2.deregister();
hook1.deregister();

View File

@ -0,0 +1,34 @@
'use strict';
// Test that the context parameter will be merged in multiple resolve hooks.
const common = require('../common');
const assert = require('assert');
const { registerHooks } = require('module');
const hook1 = registerHooks({
resolve: common.mustCall(function(specifier, context, nextResolve) {
assert.strictEqual(context.testProp, 'custom'); // It should be merged from hook 2 and 3.
const result = nextResolve(specifier, context);
return result;
}, 1),
});
const hook2 = registerHooks({
resolve: common.mustCall(function(specifier, context, nextResolve) {
assert.strictEqual(context.testProp, 'custom'); // It should be merged from hook 3.
return nextResolve(specifier); // Omit the context.
}, 1),
});
const hook3 = registerHooks({
resolve: common.mustCall(function(specifier, context, nextResolve) {
return nextResolve(specifier, { testProp: 'custom' }); // Add a custom property
}, 1),
});
require('../fixtures/empty.js');
hook3.deregister();
hook2.deregister();
hook1.deregister();

View File

@ -0,0 +1,14 @@
// Test that the context parameter can be omitted in the nextResolve invocation.
import * as common from '../common/index.mjs';
import { registerHooks } from 'node:module';
const hook = registerHooks({
resolve: common.mustCall(function(specifier, context, nextResolve) {
return nextResolve(specifier);
}, 1),
});
await import('../fixtures/es-modules/message.mjs');
hook.deregister();

View File

@ -0,0 +1,16 @@
'use strict';
// Test that the context parameter can be omitted in the nextResolve invocation.
const common = require('../common');
const { registerHooks } = require('module');
const hook = registerHooks({
resolve: common.mustCall(function(specifier, context, nextResolve) {
return nextResolve(specifier);
}, 1),
});
require('../fixtures/empty.js');
hook.deregister();

View File

@ -0,0 +1,36 @@
'use strict';
require('../common');
const assert = require('assert');
const { registerHooks } = require('module');
// This tests that the invalid return values in resolve hooks are not accepted.
const hook = registerHooks({
resolve(specifier, context, nextLoad) {
const result = { shortCircuit: true };
if (specifier === 'array') {
result.url = [];
} else if (specifier === 'null') {
result.url = null;
} else if (specifier === 'number') {
result.url = 1;
} else if (specifier === 'boolean') {
result.url = true;
} else if (specifier === 'function') {
result.url = () => {};
} else if (specifier === 'object') {
result.url = {};
}
return result;
},
});
for (const item of ['undefined', 'array', 'null', 'number', 'boolean', 'function', 'object']) {
assert.throws(() => { require(item); }, {
code: 'ERR_INVALID_RETURN_PROPERTY_VALUE',
message: /"url" from the "resolve" hook/,
});
}
hook.deregister();

View File

@ -0,0 +1,11 @@
// Flags: --no-experimental-transform-types
// This tests that a mini TypeScript loader works with resolve and
// load hooks when overriding --experimental-strip-types in ESM.
import '../common/index.mjs';
import assert from 'node:assert';
await import('../fixtures/module-hooks/register-typescript-hooks.js');
// Test inline import(), if override fails, this should fail too because enum is
// not supported when --experimental-transform-types is disabled.
const { UserAccount, UserType } = await import('../fixtures/module-hooks/user.ts');
assert.strictEqual((new UserAccount('foo', 1, UserType.Admin).name), 'foo');

View File

@ -0,0 +1,11 @@
// Flags: --no-experimental-strip-types --no-experimental-transform-types
// This tests that a mini TypeScript loader works with resolve and
// load hooks when TypeScript support is disabled.
import '../common/index.mjs';
import assert from 'node:assert';
await import('../fixtures/module-hooks/register-typescript-hooks.js');
// Test inline import(), if override fails, this should fail too because enum is
// not supported when --experimental-transform-types is disabled.
const { UserAccount, UserType } = await import('../fixtures/module-hooks/user.ts');
assert.strictEqual((new UserAccount('foo', 1, UserType.Admin).name), 'foo');

View File

@ -0,0 +1,13 @@
'use strict';
// Flags: --no-experimental-transform-types
// This tests that a mini TypeScript loader works with resolve and
// load hooks when overriding --experimental-strip-types in CJS.
require('../common');
const assert = require('assert');
require('../fixtures/module-hooks/register-typescript-hooks.js');
// Test inline require(), if override fails, this should fail too because enum is
// not supported when --experimental-transform-types is disabled.
const { UserAccount, UserType } = require('../fixtures/module-hooks/user.ts');
assert.strictEqual((new UserAccount('foo', 1, UserType.Admin).name), 'foo');

View File

@ -0,0 +1,12 @@
'use strict';
// Flags: --no-experimental-strip-types --no-experimental-transform-types
// This tests that a mini TypeScript loader works with resolve and
// load hooks when TypeScript support is disabled.
require('../common');
const assert = require('assert');
// Test inline require().
require('../fixtures/module-hooks/register-typescript-hooks.js');
const { UserAccount, UserType } = require('../fixtures/module-hooks/user.ts');
assert.strictEqual((new UserAccount('foo', 1, UserType.Admin).name), 'foo');

View File

@ -0,0 +1,32 @@
'use strict';
require('../common');
const assert = require('assert');
const { registerHooks } = require('module');
// Test that shortCircuit is required in a middle hook when nextResolve is not called.
const hook1 = registerHooks({
resolve(specifier, context, nextResolve) {
return nextResolve(specifier, context);
},
});
const hook2 = registerHooks({
resolve(specifier, context, nextResolve) {
if (specifier === 'bar') {
return {
url: 'node:bar',
};
}
return nextResolve(specifier, context);
},
});
assert.throws(() => {
require('bar');
}, {
code: 'ERR_INVALID_RETURN_PROPERTY_VALUE',
message: /shortCircuit/,
});
hook1.deregister();
hook2.deregister();

View File

@ -0,0 +1,28 @@
'use strict';
require('../common');
const assert = require('assert');
const { registerHooks } = require('module');
// Test that shortCircuit is required in the starting hook when nextResolve is not called.
const hook = registerHooks({
resolve(specifier, context, nextResolve) {
if (specifier === 'foo') {
return {
url: 'node:foo',
};
}
return nextResolve(specifier, context);
},
});
assert.throws(() => {
require('foo');
}, {
code: 'ERR_INVALID_RETURN_PROPERTY_VALUE',
message: /shortCircuit/,
});
const baz = require('../fixtures/baz.js');
assert.strictEqual(baz, 'perhaps I work');
hook.deregister();

View File

@ -0,0 +1,29 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const { registerHooks } = require('module');
// Test that shortCircuit works for the resolve hook.
const source1 = 'module.exports = "modified"';
const hook1 = registerHooks({
load: common.mustNotCall(),
});
const hook2 = registerHooks({
load(url, context, nextLoad) {
if (url.includes('empty')) {
return {
format: 'commonjs',
source: source1,
shortCircuit: true,
};
}
return nextLoad(url, context);
},
});
const value = require('../fixtures/empty.js');
assert.strictEqual(value, 'modified');
hook1.deregister();
hook2.deregister();

View File

@ -0,0 +1,6 @@
import sys, os
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
import testpy
def GetConfiguration(context, root):
return testpy.ParallelTestConfiguration(context, root, 'module-hooks')