From f91b4e2bf08ed6cc72b7588dafe680a1f70e00a3 Mon Sep 17 00:00:00 2001 From: Geoffrey Booth Date: Mon, 18 Sep 2023 20:59:50 -0700 Subject: [PATCH] esm: update loaders warning PR-URL: https://github.com/nodejs/node/pull/49633 Reviewed-By: Jacob Smith Reviewed-By: Benjamin Gruenbaum Reviewed-By: James M Snell Reviewed-By: Guy Bedford --- lib/internal/modules/esm/loader.js | 30 ++++++++++++++++--- .../test-esm-experimental-warnings.mjs | 10 +++++-- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/lib/internal/modules/esm/loader.js b/lib/internal/modules/esm/loader.js index 8cbd255a5550ba..0b08db7ffef7ec 100644 --- a/lib/internal/modules/esm/loader.js +++ b/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 { @@ -511,7 +518,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 @@ -527,9 +534,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/test/es-module/test-esm-experimental-warnings.mjs b/test/es-module/test-esm-experimental-warnings.mjs index 85b458258b6134..d499aae0afc1cd 100644 --- a/test/es-module/test-esm-experimental-warnings.mjs +++ b/test/es-module/test-esm-experimental-warnings.mjs @@ -24,14 +24,18 @@ 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'], ] ) { 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'))}`,