fix: support `__non_webpack_require__` for ES modules

This commit is contained in:
Alexander Akait 2025-09-09 22:11:56 +03:00 committed by GitHub
parent df204b5f71
commit 930785fb00
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 90 additions and 51 deletions

View File

@ -5,7 +5,9 @@
"use strict"; "use strict";
const InitFragment = require("./InitFragment"); const {
getExternalModuleNodeCommonjsInitFragment
} = require("./ExternalModule");
const { const {
JAVASCRIPT_MODULE_TYPE_AUTO, JAVASCRIPT_MODULE_TYPE_AUTO,
JAVASCRIPT_MODULE_TYPE_DYNAMIC, JAVASCRIPT_MODULE_TYPE_DYNAMIC,
@ -128,8 +130,6 @@ function getReplacements() {
const PLUGIN_NAME = "APIPlugin"; const PLUGIN_NAME = "APIPlugin";
const moduleCreateRequire = "__WEBPACK_EXTERNAL_createRequire";
class APIPlugin { class APIPlugin {
/** /**
* Apply the plugin * Apply the plugin
@ -140,14 +140,14 @@ class APIPlugin {
compiler.hooks.compilation.tap( compiler.hooks.compilation.tap(
PLUGIN_NAME, PLUGIN_NAME,
(compilation, { normalModuleFactory }) => { (compilation, { normalModuleFactory }) => {
const importMetaName = compilation.outputOptions.importMetaName;
const moduleOutput = compilation.options.output.module; const moduleOutput = compilation.options.output.module;
const nodeTarget = compiler.platform.node; const nodeTarget = compiler.platform.node;
const nodeEsm = moduleOutput && nodeTarget; const nodeEsm = moduleOutput && nodeTarget;
const REPLACEMENTS = getReplacements(); const REPLACEMENTS = getReplacements();
if (nodeEsm) { if (nodeEsm) {
REPLACEMENTS.__non_webpack_require__.expr = `${moduleCreateRequire}(${importMetaName}.url)`; REPLACEMENTS.__non_webpack_require__.expr =
"__WEBPACK_EXTERNAL_createRequire_require";
} }
compilation.dependencyTemplates.set( compilation.dependencyTemplates.set(
@ -179,13 +179,8 @@ class APIPlugin {
(source, module, renderContext) => { (source, module, renderContext) => {
if (/** @type {BuildInfo} */ (module.buildInfo).needCreateRequire) { if (/** @type {BuildInfo} */ (module.buildInfo).needCreateRequire) {
const chunkInitFragments = [ const chunkInitFragments = [
new InitFragment( getExternalModuleNodeCommonjsInitFragment(
`import { createRequire as ${moduleCreateRequire} } from ${renderContext.runtimeTemplate.renderNodePrefixForCoreModule( renderContext.runtimeTemplate
"module"
)};\n`,
InitFragment.STAGE_HARMONY_IMPORTS,
0,
"external module node-commonjs"
) )
]; ];

View File

@ -128,6 +128,23 @@ const getSourceForCommonJsExternal = (moduleAndSpecifiers) => {
}; };
}; };
/**
* @param {RuntimeTemplate} runtimeTemplate the runtime template
* @returns {InitFragment<ChunkRenderContext>} code
*/
const getExternalModuleNodeCommonjsInitFragment = (runtimeTemplate) => {
const importMetaName = runtimeTemplate.outputOptions.importMetaName;
return new InitFragment(
`import { createRequire as __WEBPACK_EXTERNAL_createRequire } from ${runtimeTemplate.renderNodePrefixForCoreModule(
"module"
)};\n${runtimeTemplate.renderConst()} __WEBPACK_EXTERNAL_createRequire_require = __WEBPACK_EXTERNAL_createRequire(${importMetaName}.url);\n`,
InitFragment.STAGE_HARMONY_IMPORTS,
0,
"external module node-commonjs"
);
};
/** /**
* @param {string | string[]} moduleAndSpecifiers the module request * @param {string | string[]} moduleAndSpecifiers the module request
* @param {RuntimeTemplate} runtimeTemplate the runtime template * @param {RuntimeTemplate} runtimeTemplate the runtime template
@ -137,19 +154,13 @@ const getSourceForCommonJsExternalInNodeModule = (
moduleAndSpecifiers, moduleAndSpecifiers,
runtimeTemplate runtimeTemplate
) => { ) => {
const importMetaName = runtimeTemplate.outputOptions.importMetaName;
const chunkInitFragments = [ const chunkInitFragments = [
new InitFragment( getExternalModuleNodeCommonjsInitFragment(runtimeTemplate)
`import { createRequire as __WEBPACK_EXTERNAL_createRequire } from ${runtimeTemplate.renderNodePrefixForCoreModule("module")};\n`,
InitFragment.STAGE_HARMONY_IMPORTS,
0,
"external module node-commonjs"
)
]; ];
if (!Array.isArray(moduleAndSpecifiers)) { if (!Array.isArray(moduleAndSpecifiers)) {
return { return {
chunkInitFragments, chunkInitFragments,
expression: `__WEBPACK_EXTERNAL_createRequire(${importMetaName}.url)(${JSON.stringify( expression: `__WEBPACK_EXTERNAL_createRequire_require(${JSON.stringify(
moduleAndSpecifiers moduleAndSpecifiers
)})` )})`
}; };
@ -157,7 +168,7 @@ const getSourceForCommonJsExternalInNodeModule = (
const moduleName = moduleAndSpecifiers[0]; const moduleName = moduleAndSpecifiers[0];
return { return {
chunkInitFragments, chunkInitFragments,
expression: `__WEBPACK_EXTERNAL_createRequire(${importMetaName}.url)(${JSON.stringify( expression: `__WEBPACK_EXTERNAL_createRequire_require(${JSON.stringify(
moduleName moduleName
)})${propertyAccess(moduleAndSpecifiers, 1)}` )})${propertyAccess(moduleAndSpecifiers, 1)}`
}; };
@ -1039,9 +1050,7 @@ class ExternalModule extends Module {
scope.registerRawExport(specifier, finalName); scope.registerRawExport(specifier, finalName);
} }
} else if (concatenationScope) { } else if (concatenationScope) {
sourceString = `${ sourceString = `${runtimeTemplate.renderConst()} ${ConcatenationScope.NAMESPACE_OBJECT_EXPORT} = ${sourceString};`;
runtimeTemplate.supportsConst() ? "const" : "var"
} ${ConcatenationScope.NAMESPACE_OBJECT_EXPORT} = ${sourceString};`;
concatenationScope.registerNamespaceExport( concatenationScope.registerNamespaceExport(
ConcatenationScope.NAMESPACE_OBJECT_EXPORT ConcatenationScope.NAMESPACE_OBJECT_EXPORT
); );
@ -1145,3 +1154,5 @@ makeSerializable(ExternalModule, "webpack/lib/ExternalModule");
module.exports = ExternalModule; module.exports = ExternalModule;
module.exports.ModuleExternalInitFragment = ModuleExternalInitFragment; module.exports.ModuleExternalInitFragment = ModuleExternalInitFragment;
module.exports.getExternalModuleNodeCommonjsInitFragment =
getExternalModuleNodeCommonjsInitFragment;

View File

@ -168,6 +168,13 @@ class RuntimeTemplate {
: `"${mod}"`; : `"${mod}"`;
} }
/**
* @returns {"const" | "var"} return `const` when it is supported, otherwise `var`
*/
renderConst() {
return this.supportsConst() ? "const" : "var";
}
/** /**
* @param {string} returnValue return value * @param {string} returnValue return value
* @param {string} args arguments * @param {string} args arguments

View File

@ -612,7 +612,7 @@ class AssetGenerator extends Generator {
); );
return new RawSource( return new RawSource(
`${runtimeTemplate.supportsConst() ? "const" : "var"} ${ `${runtimeTemplate.renderConst()} ${
ConcatenationScope.NAMESPACE_OBJECT_EXPORT ConcatenationScope.NAMESPACE_OBJECT_EXPORT
} = ${content};` } = ${content};`
); );

View File

@ -60,7 +60,7 @@ class AssetSourceGenerator extends Generator {
concatenationScope.registerNamespaceExport( concatenationScope.registerNamespaceExport(
ConcatenationScope.NAMESPACE_OBJECT_EXPORT ConcatenationScope.NAMESPACE_OBJECT_EXPORT
); );
sourceContent = `${runtimeTemplate.supportsConst() ? "const" : "var"} ${ sourceContent = `${runtimeTemplate.renderConst()} ${
ConcatenationScope.NAMESPACE_OBJECT_EXPORT ConcatenationScope.NAMESPACE_OBJECT_EXPORT
} = ${JSON.stringify(encodedSource)};`; } = ${JSON.stringify(encodedSource)};`;
} else { } else {

View File

@ -171,11 +171,7 @@ class CssGenerator extends Generator {
usedIdentifiers.add(identifier); usedIdentifiers.add(identifier);
generateContext.concatenationScope.registerExport(name, identifier); generateContext.concatenationScope.registerExport(name, identifier);
source.add( source.add(
`${ `${generateContext.runtimeTemplate.renderConst()} ${identifier} = ${JSON.stringify(v)};\n`
generateContext.runtimeTemplate.supportsConst()
? "const"
: "var"
} ${identifier} = ${JSON.stringify(v)};\n`
); );
} }
return source; return source;

View File

@ -1234,14 +1234,9 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
mode.hidden mode.hidden
) )
: /** @type {ExportModeIgnored} */ (mode.ignored); : /** @type {ExportModeIgnored} */ (mode.ignored);
const modern =
runtimeTemplate.supportsConst() &&
runtimeTemplate.supportsArrowFunction();
let content = let content =
"/* harmony reexport (unknown) */ var __WEBPACK_REEXPORT_OBJECT__ = {};\n" + "/* harmony reexport (unknown) */ var __WEBPACK_REEXPORT_OBJECT__ = {};\n" +
`/* harmony reexport (unknown) */ for(${ `/* harmony reexport (unknown) */ for(${runtimeTemplate.renderConst()} __WEBPACK_IMPORT_KEY__ in ${importVar}) `;
modern ? "const" : "var"
} __WEBPACK_IMPORT_KEY__ in ${importVar}) `;
// Filter out exports which are defined by other exports // Filter out exports which are defined by other exports
// and filter out default export because it cannot be reexported with * // and filter out default export because it cannot be reexported with *
@ -1256,7 +1251,7 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
} }
content += "__WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = "; content += "__WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = ";
content += modern content += runtimeTemplate.supportsArrowFunction()
? `() => ${importVar}[__WEBPACK_IMPORT_KEY__]` ? `() => ${importVar}[__WEBPACK_IMPORT_KEY__]`
: `function(key) { return ${importVar}[key]; }.bind(0, __WEBPACK_IMPORT_KEY__)`; : `function(key) { return ${importVar}[key]; }.bind(0, __WEBPACK_IMPORT_KEY__)`;

View File

@ -206,7 +206,7 @@ class JsonGenerator extends Generator {
/** @type {string} */ /** @type {string} */
let content; let content;
if (concatenationScope) { if (concatenationScope) {
content = `${runtimeTemplate.supportsConst() ? "const" : "var"} ${ content = `${runtimeTemplate.renderConst()} ${
ConcatenationScope.NAMESPACE_OBJECT_EXPORT ConcatenationScope.NAMESPACE_OBJECT_EXPORT
} = ${jsonExpr};`; } = ${jsonExpr};`;
concatenationScope.registerNamespaceExport( concatenationScope.registerNamespaceExport(

View File

@ -144,7 +144,14 @@ class ModuleLibraryPlugin extends AbstractLibraryPlugin {
renderStartup( renderStartup(
source, source,
module, module,
{ moduleGraph, chunk, codeGenerationResults, inlined, inlinedInIIFE }, {
moduleGraph,
chunk,
codeGenerationResults,
inlined,
inlinedInIIFE,
runtimeTemplate
},
{ options, compilation } { options, compilation }
) { ) {
const result = new ConcatSource(source); const result = new ConcatSource(source);
@ -177,10 +184,6 @@ class ModuleLibraryPlugin extends AbstractLibraryPlugin {
); );
} }
const varType = compilation.outputOptions.environment.const
? "const"
: "var";
outer: for (const exportInfo of exportsInfo) { outer: for (const exportInfo of exportsInfo) {
if (!exportInfo.provided) continue; if (!exportInfo.provided) continue;
@ -217,9 +220,9 @@ class ModuleLibraryPlugin extends AbstractLibraryPlugin {
} else { } else {
finalName = `${RuntimeGlobals.exports}${Template.toIdentifier(originalName)}`; finalName = `${RuntimeGlobals.exports}${Template.toIdentifier(originalName)}`;
result.add( result.add(
`${varType} ${finalName} = ${RuntimeGlobals.exports}${propertyAccess([ `${runtimeTemplate.renderConst()} ${finalName} = ${RuntimeGlobals.exports}${propertyAccess(
usedName [usedName]
])};\n` )};\n`
); );
} }
@ -237,7 +240,9 @@ class ModuleLibraryPlugin extends AbstractLibraryPlugin {
if (topLevelDeclarations && topLevelDeclarations.has(originalName)) { if (topLevelDeclarations && topLevelDeclarations.has(originalName)) {
const name = `${RuntimeGlobals.exports}${Template.toIdentifier(originalName)}`; const name = `${RuntimeGlobals.exports}${Template.toIdentifier(originalName)}`;
result.add(`${varType} ${name} = ${finalName};\n`); result.add(
`${runtimeTemplate.renderConst()} ${name} = ${finalName};\n`
);
shortHandedExports.push(`${name} as ${originalName}`); shortHandedExports.push(`${name} as ${originalName}`);
} else { } else {
exports.push([originalName, finalName]); exports.push([originalName, finalName]);
@ -263,7 +268,9 @@ class ModuleLibraryPlugin extends AbstractLibraryPlugin {
} }
for (const [exportName, final] of exports) { for (const [exportName, final] of exports) {
result.add(`export ${varType} ${exportName} = ${final};\n`); result.add(
`export ${runtimeTemplate.renderConst()} ${exportName} = ${final};\n`
);
} }
return result; return result;

View File

@ -3,9 +3,10 @@ const path = require("path");
it("module-import should correctly get fallback type", function() { it("module-import should correctly get fallback type", function() {
const content = fs.readFileSync(path.resolve(__dirname, "a.js"), "utf-8"); const content = fs.readFileSync(path.resolve(__dirname, "a.js"), "utf-8");
expect(content).toContain(`import { default as __WEBPACK_EXTERNAL_MODULE_external0_default__ } from "external0"`); // module expect(content).toContain(`import { default as __WEBPACK_EXTERNAL_MODULE_external0_default__ } from "external0";`); // module
expect(content).toContain(`const __WEBPACK_EXTERNAL_createRequire_require = __WEBPACK_EXTERNAL_createRequire(import.meta.url);`); // module
expect(content).toContain(`import * as __WEBPACK_EXTERNAL_MODULE_external1__ from "external1"`); // module expect(content).toContain(`import * as __WEBPACK_EXTERNAL_MODULE_external1__ from "external1"`); // module
expect(content).toContain(`module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("external2")`); // node-commonjs expect(content).toContain(`module.exports = __WEBPACK_EXTERNAL_createRequire_require("external2")`); // node-commonjs
expect(content).toContain(`import * as __WEBPACK_EXTERNAL_MODULE_external3__ from "external3"`); // module expect(content).toContain(`import * as __WEBPACK_EXTERNAL_MODULE_external3__ from "external3"`); // module
expect(content).toContain(`const external3_2 = Promise.resolve(/*! import() */).then`); // import expect(content).toContain(`const external3_2 = Promise.resolve(/*! import() */).then`); // import
}); });

View File

@ -0,0 +1,3 @@
const pathModule = __non_webpack_require__('path');
export default pathModule;

View File

@ -0,0 +1,5 @@
import pathModule from "./common.js";
it("should work", () => {
expect(typeof pathModule.dirname).toBe("function");
});

View File

@ -0,0 +1,5 @@
"use strict";
const supportsRequireInModule = require("../../../helpers/supportsRequireInModule");
module.exports = () => supportsRequireInModule();

View File

@ -0,0 +1,10 @@
"use strict";
/** @type {import("../../../../").Configuration} */
module.exports = {
devtool: "eval-source-map",
target: "node",
experiments: {
outputModule: true
}
};

4
types.d.ts vendored
View File

@ -5356,6 +5356,9 @@ declare class ExternalModule extends Module {
normalModuleFactory: NormalModuleFactory normalModuleFactory: NormalModuleFactory
): void; ): void;
static ModuleExternalInitFragment: typeof ModuleExternalInitFragment; static ModuleExternalInitFragment: typeof ModuleExternalInitFragment;
static getExternalModuleNodeCommonjsInitFragment: (
runtimeTemplate: RuntimeTemplate
) => InitFragment<ChunkRenderContextJavascriptModulesPlugin>;
} }
declare interface ExternalModuleInfo { declare interface ExternalModuleInfo {
type: "external"; type: "external";
@ -15545,6 +15548,7 @@ declare abstract class RuntimeTemplate {
supportTemplateLiteral(): boolean; supportTemplateLiteral(): boolean;
supportNodePrefixForCoreModules(): boolean; supportNodePrefixForCoreModules(): boolean;
renderNodePrefixForCoreModule(mod: string): string; renderNodePrefixForCoreModule(mod: string): string;
renderConst(): "var" | "const";
returningFunction(returnValue: string, args?: string): string; returningFunction(returnValue: string, args?: string): string;
basicFunction(args: string, body: string | string[]): string; basicFunction(args: string, body: string | string[]): string;
concatenation(...args: (string | { expr: string })[]): string; concatenation(...args: (string | { expr: string })[]): string;