diff --git a/graal-nodejs/lib/internal/modules/esm/loader.js b/graal-nodejs/lib/internal/modules/esm/loader.js index 41fec79cfa7..1cfc9c67014 100644 --- a/graal-nodejs/lib/internal/modules/esm/loader.js +++ b/graal-nodejs/lib/internal/modules/esm/loader.js @@ -4,9 +4,16 @@ require('internal/modules/cjs/loader'); const { + ArrayPrototypeJoin, + ArrayPrototypeMap, + ArrayPrototypeReduce, FunctionPrototypeCall, + JSONStringify, ObjectSetPrototypeOf, + RegExpPrototypeSymbolReplace, SafeWeakMap, + encodeURIComponent, + hardenRegExp, } = primordials; const { @@ -486,7 +493,7 @@ class CustomizedModuleLoader { } } -let emittedExperimentalWarning = false; +let emittedLoaderFlagWarning = false; /** * A loader instance is used as the main entry point for loading ES modules. Currently, this is a singleton; there is * only one used for loading the main module and everything in its dependency graph, though separate instances of this @@ -502,9 +509,24 @@ function createModuleLoader(useCustomLoadersIfPresent = true) { !require('internal/modules/esm/utils').isLoaderWorker()) { const userLoaderPaths = getOptionValue('--experimental-loader'); if (userLoaderPaths.length > 0) { - if (!emittedExperimentalWarning) { - emitExperimentalWarning('Custom ESM Loaders'); - emittedExperimentalWarning = true; + if (!emittedLoaderFlagWarning) { + const readableURIEncode = (string) => ArrayPrototypeReduce( + [ + [/'/g, '%27'], // We need to URL-encode the single quote as it's the delimiter for the --import flag. + [/%22/g, '"'], // We can decode the double quotes to improve readability. + [/%2F/ig, '/'], // We can decode the slashes to improve readability. + ], + (str, { 0: regex, 1: replacement }) => RegExpPrototypeSymbolReplace(hardenRegExp(regex), str, replacement), + encodeURIComponent(string)); + process.emitWarning( + '`--experimental-loader` may be removed in the future; instead use `register()`:\n' + + `--import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; ${ArrayPrototypeJoin( + ArrayPrototypeMap(userLoaderPaths, (loader) => `register(${readableURIEncode(JSONStringify(loader))}, pathToFileURL("./"))`), + '; ', + )};'`, + 'ExperimentalWarning', + ); + emittedLoaderFlagWarning = true; } customizations = new CustomizedModuleLoader(); } diff --git a/graal-nodejs/test/es-module/test-esm-experimental-warnings.mjs b/graal-nodejs/test/es-module/test-esm-experimental-warnings.mjs index fc167c63584..68a48f2d697 100644 --- a/graal-nodejs/test/es-module/test-esm-experimental-warnings.mjs +++ b/graal-nodejs/test/es-module/test-esm-experimental-warnings.mjs @@ -24,15 +24,19 @@ describe('ESM: warn for obsolete hooks provided', { concurrency: true }, () => { describe('experimental warnings for enabled experimental feature', () => { for ( - const [experiment, arg] of [ - [/Custom ESM Loaders/, `--experimental-loader=${fileURL('es-module-loaders', 'hooks-custom.mjs')}`], + const [experiment, ...args] of [ + [ + /`--experimental-loader` may be removed in the future/, + '--experimental-loader', + fileURL('es-module-loaders', 'hooks-custom.mjs'), + ], [/Network Imports/, '--experimental-network-imports'], [/specifier resolution/, '--experimental-specifier-resolution=node'], ] ) { it(`should print for ${experiment.toString().replaceAll('/', '')}`, async () => { const { code, signal, stderr } = await spawnPromisified(execPath, [ - arg, + ...args, '--input-type=module', '--eval', `import ${JSON.stringify(fileURL('es-module-loaders', 'module-named-exports.mjs'))}`,