Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: compatibility __non_webpack_require__ with ES modules #17308

Merged
merged 8 commits into from
Jun 5, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
210 changes: 117 additions & 93 deletions lib/APIPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,103 +24,121 @@ const GetFullHashRuntimeModule = require("./runtime/GetFullHashRuntimeModule");
/** @typedef {import("./Compiler")} Compiler */
/** @typedef {import("./javascript/JavascriptParser")} JavascriptParser */

/* eslint-disable camelcase */
const REPLACEMENTS = {
__webpack_require__: {
expr: RuntimeGlobals.require,
req: [RuntimeGlobals.require],
type: "function",
assign: false
},
__webpack_public_path__: {
expr: RuntimeGlobals.publicPath,
req: [RuntimeGlobals.publicPath],
type: "string",
assign: true
},
__webpack_base_uri__: {
expr: RuntimeGlobals.baseURI,
req: [RuntimeGlobals.baseURI],
type: "string",
assign: true
},
__webpack_modules__: {
expr: RuntimeGlobals.moduleFactories,
req: [RuntimeGlobals.moduleFactories],
type: "object",
assign: false
},
__webpack_chunk_load__: {
expr: RuntimeGlobals.ensureChunk,
req: [RuntimeGlobals.ensureChunk],
type: "function",
assign: true
},
__non_webpack_require__: {
expr: "require",
req: null,
type: undefined, // type is not known, depends on environment
assign: true
},
__webpack_nonce__: {
expr: RuntimeGlobals.scriptNonce,
req: [RuntimeGlobals.scriptNonce],
type: "string",
assign: true
},
__webpack_hash__: {
expr: `${RuntimeGlobals.getFullHash}()`,
req: [RuntimeGlobals.getFullHash],
type: "string",
assign: false
},
__webpack_chunkname__: {
expr: RuntimeGlobals.chunkName,
req: [RuntimeGlobals.chunkName],
type: "string",
assign: false
},
__webpack_get_script_filename__: {
expr: RuntimeGlobals.getChunkScriptFilename,
req: [RuntimeGlobals.getChunkScriptFilename],
type: "function",
assign: true
},
__webpack_runtime_id__: {
expr: RuntimeGlobals.runtimeId,
req: [RuntimeGlobals.runtimeId],
assign: false
},
"require.onError": {
expr: RuntimeGlobals.uncaughtErrorHandler,
req: [RuntimeGlobals.uncaughtErrorHandler],
type: undefined, // type is not known, could be function or undefined
assign: true // is never a pattern
},
__system_context__: {
expr: RuntimeGlobals.systemContext,
req: [RuntimeGlobals.systemContext],
type: "object",
assign: false
},
__webpack_share_scopes__: {
expr: RuntimeGlobals.shareScopeMap,
req: [RuntimeGlobals.shareScopeMap],
type: "object",
assign: false
},
__webpack_init_sharing__: {
expr: RuntimeGlobals.initializeSharing,
req: [RuntimeGlobals.initializeSharing],
type: "function",
assign: true
}
};
/* eslint-enable camelcase */
/**
* @param {boolean} module true if ES module
* @param {string} importMetaName `import.meta` name
* @returns {Record<string, {expr: string, req: string[], type?: string, assign: boolean}>} replacements
*/
function getReplacements(module, importMetaName) {
return {
__webpack_require__: {
expr: RuntimeGlobals.require,
req: [RuntimeGlobals.require],
type: "function",
assign: false
},
__webpack_public_path__: {
expr: RuntimeGlobals.publicPath,
req: [RuntimeGlobals.publicPath],
type: "string",
assign: true
},
__webpack_base_uri__: {
expr: RuntimeGlobals.baseURI,
req: [RuntimeGlobals.baseURI],
type: "string",
assign: true
},
__webpack_modules__: {
expr: RuntimeGlobals.moduleFactories,
req: [RuntimeGlobals.moduleFactories],
type: "object",
assign: false
},
__webpack_chunk_load__: {
expr: RuntimeGlobals.ensureChunk,
req: [RuntimeGlobals.ensureChunk],
type: "function",
assign: true
},
__non_webpack_require__: {
expr: module
? `__WEBPACK_EXTERNAL_createRequire(${importMetaName}.url)`
: "require",
req: module ? [RuntimeGlobals.createRequire] : null,
type: undefined, // type is not known, depends on environment
assign: true
},
__webpack_nonce__: {
expr: RuntimeGlobals.scriptNonce,
req: [RuntimeGlobals.scriptNonce],
type: "string",
assign: true
},
__webpack_hash__: {
expr: `${RuntimeGlobals.getFullHash}()`,
req: [RuntimeGlobals.getFullHash],
type: "string",
assign: false
},
__webpack_chunkname__: {
expr: RuntimeGlobals.chunkName,
req: [RuntimeGlobals.chunkName],
type: "string",
assign: false
},
__webpack_get_script_filename__: {
expr: RuntimeGlobals.getChunkScriptFilename,
req: [RuntimeGlobals.getChunkScriptFilename],
type: "function",
assign: true
},
__webpack_runtime_id__: {
expr: RuntimeGlobals.runtimeId,
req: [RuntimeGlobals.runtimeId],
assign: false
},
"require.onError": {
expr: RuntimeGlobals.uncaughtErrorHandler,
req: [RuntimeGlobals.uncaughtErrorHandler],
type: undefined, // type is not known, could be function or undefined
assign: true // is never a pattern
},
__system_context__: {
expr: RuntimeGlobals.systemContext,
req: [RuntimeGlobals.systemContext],
type: "object",
assign: false
},
__webpack_share_scopes__: {
expr: RuntimeGlobals.shareScopeMap,
req: [RuntimeGlobals.shareScopeMap],
type: "object",
assign: false
},
__webpack_init_sharing__: {
expr: RuntimeGlobals.initializeSharing,
req: [RuntimeGlobals.initializeSharing],
type: "function",
assign: true
}
};
}

const PLUGIN_NAME = "APIPlugin";

/**
* @typedef {Object} APIPluginOptions
* @property {boolean} [module] the output filename
*/

class APIPlugin {
/**
* @param {APIPluginOptions} [options] options
*/
constructor(options = {}) {
this.options = options;
}
/**
* Apply the plugin
* @param {Compiler} compiler the compiler instance
Expand All @@ -130,6 +148,12 @@ class APIPlugin {
compiler.hooks.compilation.tap(
PLUGIN_NAME,
(compilation, { normalModuleFactory }) => {
const { importMetaName } = compilation.outputOptions;
const REPLACEMENTS = getReplacements(
this.options.module,
importMetaName
);

compilation.dependencyTemplates.set(
ConstDependency,
new ConstDependency.Template()
Expand Down
39 changes: 21 additions & 18 deletions lib/ExternalModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ const RUNTIME_REQUIREMENTS_FOR_MODULE = new Set([
RuntimeGlobals.definePropertyGetters
]);
const EMPTY_RUNTIME_REQUIREMENTS = new Set([]);
const RUNTIME_REQUIREMENTS_FOR_CREATE_REQUIRE = new Set([
RuntimeGlobals.createRequire
]);

/**
* @param {string|string[]} variableName the variable name or path
Expand Down Expand Up @@ -104,31 +107,28 @@ const getSourceForCommonJsExternal = moduleAndSpecifiers => {

/**
* @param {string|string[]} moduleAndSpecifiers the module request
* @param {Compilation} compilation the compilation
* @returns {SourceData} the generated source
*/
const getSourceForCommonJsExternalInNodeModule = moduleAndSpecifiers => {
const chunkInitFragments = [
new InitFragment(
'import { createRequire as __WEBPACK_EXTERNAL_createRequire } from "module";\n',
InitFragment.STAGE_HARMONY_IMPORTS,
0,
"external module node-commonjs"
)
];
const getSourceForCommonJsExternalInNodeModule = (
moduleAndSpecifiers,
compilation
) => {
const { importMetaName } = compilation.outputOptions;
if (!Array.isArray(moduleAndSpecifiers)) {
return {
expression: `__WEBPACK_EXTERNAL_createRequire(import.meta.url)(${JSON.stringify(
runtimeRequirements: RUNTIME_REQUIREMENTS_FOR_CREATE_REQUIRE,
expression: `__WEBPACK_EXTERNAL_createRequire(${importMetaName}.url)(${JSON.stringify(
TheLarkInn marked this conversation as resolved.
Show resolved Hide resolved
moduleAndSpecifiers
)})`,
chunkInitFragments
)})`
};
}
const moduleName = moduleAndSpecifiers[0];
return {
expression: `__WEBPACK_EXTERNAL_createRequire(import.meta.url)(${JSON.stringify(
runtimeRequirements: RUNTIME_REQUIREMENTS_FOR_CREATE_REQUIRE,
expression: `__WEBPACK_EXTERNAL_createRequire(${importMetaName}.url)(${JSON.stringify(
moduleName
)})${propertyAccess(moduleAndSpecifiers, 1)}`,
chunkInitFragments
)})${propertyAccess(moduleAndSpecifiers, 1)}`
};
};

Expand Down Expand Up @@ -538,7 +538,8 @@ class ExternalModule extends Module {
runtimeTemplate,
moduleGraph,
chunkGraph,
runtime
runtime,
compilation
) {
switch (externalType) {
case "this":
Expand All @@ -557,7 +558,7 @@ class ExternalModule extends Module {
return getSourceForCommonJsExternal(request);
case "node-commonjs":
return this.buildInfo.module
? getSourceForCommonJsExternalInNodeModule(request)
? getSourceForCommonJsExternalInNodeModule(request, compilation)
: getSourceForCommonJsExternal(request);
case "amd":
case "amd-require":
Expand Down Expand Up @@ -620,6 +621,7 @@ class ExternalModule extends Module {
* @returns {CodeGenerationResult} result
*/
codeGeneration({
compilation,
runtimeTemplate,
moduleGraph,
chunkGraph,
Expand Down Expand Up @@ -656,7 +658,8 @@ class ExternalModule extends Module {
runtimeTemplate,
moduleGraph,
chunkGraph,
runtime
runtime,
compilation
);

let sourceString = sourceData.expression;
Expand Down
5 changes: 5 additions & 0 deletions lib/RuntimeGlobals.js
Original file line number Diff line number Diff line change
Expand Up @@ -379,3 +379,8 @@ exports.relativeUrl = "__webpack_require__.U";
* ) => void
*/
exports.asyncModule = "__webpack_require__.a";

/**
* import external module
*/
exports.createRequire = "create-require";
4 changes: 3 additions & 1 deletion lib/WebpackOptionsApply.js
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,9 @@ class WebpackOptionsApply extends OptionsApply {
const NodeStuffPlugin = require("./NodeStuffPlugin");
new NodeStuffPlugin(options.node).apply(compiler);
}
new APIPlugin().apply(compiler);
new APIPlugin({
module: options.output.module
}).apply(compiler);
new ExportsInfoApiPlugin().apply(compiler);
new WebpackIsIncludedPlugin().apply(compiler);
new ConstPlugin().apply(compiler);
Expand Down
12 changes: 12 additions & 0 deletions lib/javascript/JavascriptModulesPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,18 @@ class JavascriptModulesPlugin {
"JavascriptModulesPlugin error: JavascriptModulesPlugin.getCompilationHooks().renderContent plugins should return something"
);
}

if (runtimeRequirements.has(RuntimeGlobals.createRequire)) {
chunkRenderContext.chunkInitFragments.push(
new InitFragment(
'import { createRequire as __WEBPACK_EXTERNAL_createRequire } from "module";\n',
InitFragment.STAGE_HARMONY_IMPORTS,
0,
"external module node-commonjs"
)
);
}

finalSource = InitFragment.addToSource(
finalSource,
chunkRenderContext.chunkInitFragments,
Expand Down
3 changes: 2 additions & 1 deletion lib/node/ReadFileCompileAsyncWasmPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class ReadFileCompileAsyncWasmPlugin {
: globalWasmLoading;
return wasmLoading === this._type;
};
const { importMetaName } = compilation.outputOptions;
/**
* @type {(path: string) => string}
*/
Expand All @@ -48,7 +49,7 @@ class ReadFileCompileAsyncWasmPlugin {
Template.asString([
"Promise.all([import('fs'), import('url')]).then(([{ readFile }, { URL }]) => new Promise((resolve, reject) => {",
Template.indent([
`readFile(new URL(${path}, import.meta.url), (err, buffer) => {`,
`readFile(new URL(${path}, ${importMetaName}.url), (err, buffer) => {`,
TheLarkInn marked this conversation as resolved.
Show resolved Hide resolved
Template.indent([
"if (err) return reject(err);",
"",
Expand Down
1 change: 1 addition & 0 deletions test/configCases/plugins/api-plugin/baz.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default "baz module text";
16 changes: 16 additions & 0 deletions test/configCases/plugins/api-plugin/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { createRequire as func_create_require, builtinModules as builtin } from "module";
import external from "external-module";
import externalOther from "external-other-module";
import baz from "./baz.js";

it("should work with __non_webpack_require__ and ES modules", function () {
const foo = __non_webpack_require__("./mod.js");

expect(foo).toBe("module text");
expect(external).toBe("external module text");
expect(externalOther).toBe("external module text");
expect(baz).toBe("baz module text");
expect(typeof func_create_require).toBe("function");
expect(func_create_require(import.meta.url)("./mod.js")).toBe("module text");
expect(typeof builtin).toBe("object")
});
1 change: 1 addition & 0 deletions test/configCases/plugins/api-plugin/mod.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = "module text";