mirror of
https://github.com/nodejs/node.git
synced 2024-11-21 10:59:27 +00:00
ad0bcb9c02
This patch adds support for using `vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER` as `importModuleDynamically` in all APIs that take the option except `vm.SourceTextModule`. This allows users to have a shortcut to support dynamic import() in the compiled code without missing the compilation cache if they don't need customization of the loading process. We emit an experimental warning when the `import()` is actually handled by the default loader through this option instead of requiring `--experimental-vm-modules`. In addition this refactors the documentation for `importModuleDynamically` and adds a dedicated section for it with examples. `vm.SourceTextModule` is not supported in this patch because it needs additional refactoring to handle `initializeImportMeta`, which can be done in a follow-up. PR-URL: https://github.com/nodejs/node/pull/51244 Fixes: https://github.com/nodejs/node/issues/51154 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
143 lines
4.5 KiB
JavaScript
143 lines
4.5 KiB
JavaScript
'use strict';
|
|
|
|
const common = require('../common');
|
|
|
|
// Can't process.chdir() in worker.
|
|
common.skipIfWorker();
|
|
|
|
const tmpdir = require('../common/tmpdir');
|
|
const fixtures = require('../common/fixtures');
|
|
const url = require('url');
|
|
const fs = require('fs');
|
|
const {
|
|
compileFunction,
|
|
Script,
|
|
createContext,
|
|
constants: { USE_MAIN_CONTEXT_DEFAULT_LOADER },
|
|
} = require('vm');
|
|
const assert = require('assert');
|
|
|
|
common.expectWarning('ExperimentalWarning',
|
|
'vm.USE_MAIN_CONTEXT_DEFAULT_LOADER is an experimental feature and might change at any time');
|
|
|
|
assert(
|
|
!process.execArgv.includes('--experimental-vm-modules'),
|
|
'This test must be run without --experimental-vm-modules');
|
|
|
|
assert.strictEqual(typeof USE_MAIN_CONTEXT_DEFAULT_LOADER, 'symbol');
|
|
|
|
async function testNotFoundErrors(options) {
|
|
// Import user modules.
|
|
const script = new Script('import("./message.mjs")', options);
|
|
// Use try-catch for better async stack traces in the logs.
|
|
await assert.rejects(script.runInThisContext(), { code: 'ERR_MODULE_NOT_FOUND' });
|
|
|
|
const imported = compileFunction('return import("./message.mjs")', [], options)();
|
|
// Use try-catch for better async stack traces in the logs.
|
|
await assert.rejects(imported, { code: 'ERR_MODULE_NOT_FOUND' });
|
|
}
|
|
|
|
async function testLoader(options) {
|
|
{
|
|
// Import built-in modules
|
|
const script = new Script('import("fs")', options);
|
|
let result = await script.runInThisContext();
|
|
assert.strictEqual(result.constants.F_OK, fs.constants.F_OK);
|
|
|
|
const imported = compileFunction('return import("fs")', [], options)();
|
|
result = await imported;
|
|
assert.strictEqual(result.constants.F_OK, fs.constants.F_OK);
|
|
}
|
|
|
|
const moduleUrl = fixtures.fileURL('es-modules', 'message.mjs');
|
|
fs.copyFileSync(moduleUrl, tmpdir.resolve('message.mjs'));
|
|
|
|
{
|
|
const namespace = await import(moduleUrl);
|
|
const script = new Script('import("./message.mjs")', options);
|
|
const result = await script.runInThisContext();
|
|
assert.deepStrictEqual(result, namespace);
|
|
}
|
|
|
|
{
|
|
const namespace = await import(moduleUrl);
|
|
const imported = compileFunction('return import("./message.mjs")', [], options)();
|
|
const result = await imported;
|
|
assert.deepStrictEqual(result, namespace);
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
{
|
|
// Importing with absolute path as filename.
|
|
tmpdir.refresh();
|
|
const filename = tmpdir.resolve('index.js');
|
|
const options = {
|
|
filename,
|
|
importModuleDynamically: USE_MAIN_CONTEXT_DEFAULT_LOADER
|
|
};
|
|
await testNotFoundErrors(options);
|
|
await testLoader(options);
|
|
}
|
|
|
|
{
|
|
// Importing with file:// URL as filename.
|
|
tmpdir.refresh();
|
|
// We use a search parameter to bypass caching.
|
|
const filename = url.pathToFileURL(tmpdir.resolve('index.js')).href + '?t=1';
|
|
const options = {
|
|
filename,
|
|
importModuleDynamically: USE_MAIN_CONTEXT_DEFAULT_LOADER
|
|
};
|
|
await testNotFoundErrors(options);
|
|
await testLoader(options);
|
|
}
|
|
|
|
{
|
|
// For undefined or non-path/URL filenames, import() should resolve to the cwd.
|
|
tmpdir.refresh();
|
|
process.chdir(tmpdir.path);
|
|
const undefinedOptions = {
|
|
importModuleDynamically: USE_MAIN_CONTEXT_DEFAULT_LOADER
|
|
};
|
|
const nonPathOptions = {
|
|
filename: 'non-path',
|
|
importModuleDynamically: USE_MAIN_CONTEXT_DEFAULT_LOADER
|
|
};
|
|
// Run the error tests first to avoid caching.
|
|
await testNotFoundErrors(undefinedOptions);
|
|
await testNotFoundErrors(nonPathOptions);
|
|
|
|
// createContext() with null referrer also resolves to cwd.
|
|
{
|
|
const options = {
|
|
importModuleDynamically: USE_MAIN_CONTEXT_DEFAULT_LOADER,
|
|
};
|
|
const ctx = createContext({}, options);
|
|
const s = new Script('Promise.resolve("import(\'./message.mjs\')").then(eval)', {
|
|
importModuleDynamically: common.mustNotCall(),
|
|
});
|
|
await assert.rejects(s.runInContext(ctx), { code: 'ERR_MODULE_NOT_FOUND' });
|
|
}
|
|
|
|
await testLoader(undefinedOptions);
|
|
await testLoader(nonPathOptions);
|
|
|
|
{
|
|
const options = {
|
|
importModuleDynamically: USE_MAIN_CONTEXT_DEFAULT_LOADER,
|
|
};
|
|
const ctx = createContext({}, options);
|
|
const moduleUrl = fixtures.fileURL('es-modules', 'message.mjs');
|
|
const namespace = await import(moduleUrl.href);
|
|
const script = new Script('Promise.resolve("import(\'./message.mjs\')").then(eval)', {
|
|
importModuleDynamically: common.mustNotCall(),
|
|
});
|
|
const result = await script.runInContext(ctx);
|
|
assert.deepStrictEqual(result, namespace);
|
|
}
|
|
}
|
|
}
|
|
|
|
main().catch(common.mustNotCall());
|