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

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

View File

@ -168,6 +168,13 @@ class RuntimeTemplate {
: `"${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} args arguments

View File

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

View File

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

View File

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

View File

@ -1234,14 +1234,9 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
mode.hidden
)
: /** @type {ExportModeIgnored} */ (mode.ignored);
const modern =
runtimeTemplate.supportsConst() &&
runtimeTemplate.supportsArrowFunction();
let content =
"/* harmony reexport (unknown) */ var __WEBPACK_REEXPORT_OBJECT__ = {};\n" +
`/* harmony reexport (unknown) */ for(${
modern ? "const" : "var"
} __WEBPACK_IMPORT_KEY__ in ${importVar}) `;
`/* harmony reexport (unknown) */ for(${runtimeTemplate.renderConst()} __WEBPACK_IMPORT_KEY__ in ${importVar}) `;
// Filter out exports which are defined by other exports
// 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 += modern
content += runtimeTemplate.supportsArrowFunction()
? `() => ${importVar}[__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} */
let content;
if (concatenationScope) {
content = `${runtimeTemplate.supportsConst() ? "const" : "var"} ${
content = `${runtimeTemplate.renderConst()} ${
ConcatenationScope.NAMESPACE_OBJECT_EXPORT
} = ${jsonExpr};`;
concatenationScope.registerNamespaceExport(

View File

@ -144,7 +144,14 @@ class ModuleLibraryPlugin extends AbstractLibraryPlugin {
renderStartup(
source,
module,
{ moduleGraph, chunk, codeGenerationResults, inlined, inlinedInIIFE },
{
moduleGraph,
chunk,
codeGenerationResults,
inlined,
inlinedInIIFE,
runtimeTemplate
},
{ options, compilation }
) {
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) {
if (!exportInfo.provided) continue;
@ -217,9 +220,9 @@ class ModuleLibraryPlugin extends AbstractLibraryPlugin {
} else {
finalName = `${RuntimeGlobals.exports}${Template.toIdentifier(originalName)}`;
result.add(
`${varType} ${finalName} = ${RuntimeGlobals.exports}${propertyAccess([
usedName
])};\n`
`${runtimeTemplate.renderConst()} ${finalName} = ${RuntimeGlobals.exports}${propertyAccess(
[usedName]
)};\n`
);
}
@ -237,7 +240,9 @@ class ModuleLibraryPlugin extends AbstractLibraryPlugin {
if (topLevelDeclarations && topLevelDeclarations.has(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}`);
} else {
exports.push([originalName, finalName]);
@ -263,7 +268,9 @@ class ModuleLibraryPlugin extends AbstractLibraryPlugin {
}
for (const [exportName, final] of exports) {
result.add(`export ${varType} ${exportName} = ${final};\n`);
result.add(
`export ${runtimeTemplate.renderConst()} ${exportName} = ${final};\n`
);
}
return result;

View File

@ -3,9 +3,10 @@ const path = require("path");
it("module-import should correctly get fallback type", function() {
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(`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(`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
): void;
static ModuleExternalInitFragment: typeof ModuleExternalInitFragment;
static getExternalModuleNodeCommonjsInitFragment: (
runtimeTemplate: RuntimeTemplate
) => InitFragment<ChunkRenderContextJavascriptModulesPlugin>;
}
declare interface ExternalModuleInfo {
type: "external";
@ -15545,6 +15548,7 @@ declare abstract class RuntimeTemplate {
supportTemplateLiteral(): boolean;
supportNodePrefixForCoreModules(): boolean;
renderNodePrefixForCoreModule(mod: string): string;
renderConst(): "var" | "const";
returningFunction(returnValue: string, args?: string): string;
basicFunction(args: string, body: string | string[]): string;
concatenation(...args: (string | { expr: string })[]): string;