Skip to content

Commit c951c98

Browse files
committed
fix: module-import get fallback from externalsPresets
1 parent 1012ed8 commit c951c98

File tree

7 files changed

+152
-92
lines changed

7 files changed

+152
-92
lines changed

lib/ExternalModule.js

Lines changed: 88 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const propertyAccess = require("./util/propertyAccess");
2222
const { register } = require("./util/serialization");
2323

2424
/** @typedef {import("webpack-sources").Source} Source */
25+
/** @typedef {import("../declarations/WebpackOptions").ExternalsPresets} ExternalsPresets */
2526
/** @typedef {import("../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions */
2627
/** @typedef {import("./Chunk")} Chunk */
2728
/** @typedef {import("./ChunkGraph")} ChunkGraph */
@@ -53,7 +54,7 @@ const { register } = require("./util/serialization");
5354
/** @typedef {import("./util/fs").InputFileSystem} InputFileSystem */
5455
/** @typedef {import("./util/runtime").RuntimeSpec} RuntimeSpec */
5556

56-
/** @typedef {{ attributes?: ImportAttributes, externalType: "import" | "module" | undefined }} ImportDependencyMeta */
57+
/** @typedef {{ attributes?: ImportAttributes, externalType: "import" | "module" | undefined, externalsPresets: ExternalsPresets | undefined }} ImportDependencyMeta */
5758
/** @typedef {{ layer?: string, supports?: string, media?: string }} CssImportDependencyMeta */
5859

5960
/** @typedef {ImportDependencyMeta | CssImportDependencyMeta} DependencyMeta */
@@ -166,7 +167,7 @@ const getSourceForImportExternal = (
166167
const importName = runtimeTemplate.outputOptions.importFunctionName;
167168
if (
168169
!runtimeTemplate.supportsDynamicImport() &&
169-
(importName === "import" || importName !== "module-import")
170+
(importName === "import" || importName === "module-import")
170171
) {
171172
throw new Error(
172173
"The target environment doesn't support 'import()' so it's not possible to use external type 'import'"
@@ -578,6 +579,25 @@ class ExternalModule extends Module {
578579
canMangle = true;
579580
}
580581
break;
582+
case "module":
583+
if (this.buildInfo.module) {
584+
if (!Array.isArray(request) || request.length === 1) {
585+
this.buildMeta.exportsType = "namespace";
586+
canMangle = true;
587+
}
588+
} else {
589+
this.buildMeta.async = true;
590+
EnvironmentNotSupportAsyncWarning.check(
591+
this,
592+
compilation.runtimeTemplate,
593+
"external module"
594+
);
595+
if (!Array.isArray(request) || request.length === 1) {
596+
this.buildMeta.exportsType = "namespace";
597+
canMangle = false;
598+
}
599+
}
600+
break;
581601
case "script":
582602
this.buildMeta.async = true;
583603
EnvironmentNotSupportAsyncWarning.check(
@@ -594,52 +614,18 @@ class ExternalModule extends Module {
594614
"external promise"
595615
);
596616
break;
597-
case "module":
598617
case "import":
599-
case "module-import": {
600-
const type =
601-
externalType === "module-import" &&
602-
this.dependencyMeta &&
603-
/** @type {ImportDependencyMeta} */ (this.dependencyMeta).externalType
604-
? /** @type {ImportDependencyMeta} */ (this.dependencyMeta)
605-
.externalType
606-
: externalType;
607-
608-
if (type === "module") {
609-
if (this.buildInfo.module) {
610-
if (!Array.isArray(request) || request.length === 1) {
611-
this.buildMeta.exportsType = "namespace";
612-
canMangle = true;
613-
}
614-
} else {
615-
this.buildMeta.async = true;
616-
EnvironmentNotSupportAsyncWarning.check(
617-
this,
618-
compilation.runtimeTemplate,
619-
"external module"
620-
);
621-
if (!Array.isArray(request) || request.length === 1) {
622-
this.buildMeta.exportsType = "namespace";
623-
canMangle = false;
624-
}
625-
}
626-
}
627-
628-
if (type === "import") {
629-
this.buildMeta.async = true;
630-
EnvironmentNotSupportAsyncWarning.check(
631-
this,
632-
compilation.runtimeTemplate,
633-
"external import"
634-
);
635-
if (!Array.isArray(request) || request.length === 1) {
636-
this.buildMeta.exportsType = "namespace";
637-
canMangle = false;
638-
}
618+
this.buildMeta.async = true;
619+
EnvironmentNotSupportAsyncWarning.check(
620+
this,
621+
compilation.runtimeTemplate,
622+
"external import"
623+
);
624+
if (!Array.isArray(request) || request.length === 1) {
625+
this.buildMeta.exportsType = "namespace";
626+
canMangle = false;
639627
}
640-
641628
break;
642-
}
643629
}
644630
this.addDependency(new StaticExportsDependency(true, canMangle));
645631
callback();
@@ -673,6 +659,36 @@ class ExternalModule extends Module {
673659

674660
_getRequestAndExternalType() {
675661
let { request, externalType } = this;
662+
663+
if (externalType === "module-import") {
664+
const dependencyMeta = /** @type {ImportDependencyMeta} */ (
665+
this.dependencyMeta
666+
);
667+
668+
if (dependencyMeta && dependencyMeta.externalType) {
669+
externalType = dependencyMeta.externalType;
670+
} else if (dependencyMeta && dependencyMeta.externalsPresets) {
671+
const presets = dependencyMeta.externalsPresets;
672+
// TODO: what if user set multiple presets?
673+
if (presets.web) {
674+
externalType = "module";
675+
} else if (presets.webAsync) {
676+
externalType = "import";
677+
} else if (
678+
presets.electron ||
679+
presets.electronMain ||
680+
presets.electronPreload ||
681+
presets.electronRenderer ||
682+
presets.node ||
683+
presets.nwjs
684+
) {
685+
externalType = "node-commonjs";
686+
}
687+
} else {
688+
externalType = "commonjs";
689+
}
690+
}
691+
676692
if (typeof request === "object" && !Array.isArray(request))
677693
request = request[externalType];
678694
return { request, externalType };
@@ -737,58 +753,43 @@ class ExternalModule extends Module {
737753
runtimeTemplate
738754
);
739755
}
756+
case "import":
757+
return getSourceForImportExternal(
758+
request,
759+
runtimeTemplate,
760+
/** @type {ImportDependencyMeta} */ (dependencyMeta)
761+
);
740762
case "script":
741763
return getSourceForScriptExternal(request, runtimeTemplate);
742-
case "module":
743-
case "import":
744-
case "module-import": {
745-
const type =
746-
externalType === "module-import" &&
747-
dependencyMeta &&
748-
/** @type {ImportDependencyMeta} */ (dependencyMeta).externalType
749-
? /** @type {ImportDependencyMeta} */ (dependencyMeta).externalType
750-
: externalType;
751-
752-
if (type === "import") {
753-
return getSourceForImportExternal(
754-
request,
755-
runtimeTemplate,
756-
/** @type {ImportDependencyMeta} */ (dependencyMeta)
757-
);
758-
}
759-
760-
if (type === "module") {
761-
if (!(/** @type {BuildInfo} */ (this.buildInfo).module)) {
762-
if (!runtimeTemplate.supportsDynamicImport()) {
763-
throw new Error(
764-
`The target environment doesn't support dynamic import() syntax so it's not possible to use external type 'module' within a script${
765-
runtimeTemplate.supportsEcmaScriptModuleSyntax()
766-
? "\nDid you mean to build a EcmaScript Module ('output.module: true')?"
767-
: ""
768-
}`
769-
);
770-
}
771-
return getSourceForImportExternal(
772-
request,
773-
runtimeTemplate,
774-
/** @type {ImportDependencyMeta} */ (dependencyMeta)
775-
);
776-
}
777-
if (!runtimeTemplate.supportsEcmaScriptModuleSyntax()) {
764+
case "module": {
765+
if (!(/** @type {BuildInfo} */ (this.buildInfo).module)) {
766+
if (!runtimeTemplate.supportsDynamicImport()) {
778767
throw new Error(
779-
"The target environment doesn't support EcmaScriptModule syntax so it's not possible to use external type 'module'"
768+
`The target environment doesn't support dynamic import() syntax so it's not possible to use external type 'module' within a script${
769+
runtimeTemplate.supportsEcmaScriptModuleSyntax()
770+
? "\nDid you mean to build a EcmaScript Module ('output.module: true')?"
771+
: ""
772+
}`
780773
);
781774
}
782-
return getSourceForModuleExternal(
775+
return getSourceForImportExternal(
783776
request,
784-
moduleGraph.getExportsInfo(this),
785-
runtime,
786777
runtimeTemplate,
787778
/** @type {ImportDependencyMeta} */ (dependencyMeta)
788779
);
789780
}
790-
791-
break;
781+
if (!runtimeTemplate.supportsEcmaScriptModuleSyntax()) {
782+
throw new Error(
783+
"The target environment doesn't support EcmaScriptModule syntax so it's not possible to use external type 'module'"
784+
);
785+
}
786+
return getSourceForModuleExternal(
787+
request,
788+
moduleGraph.getExportsInfo(this),
789+
runtime,
790+
runtimeTemplate,
791+
/** @type {ImportDependencyMeta} */ (dependencyMeta)
792+
);
792793
}
793794
case "var":
794795
case "promise":

lib/ExternalModuleFactoryPlugin.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const ImportDependency = require("./dependencies/ImportDependency");
1414
const { resolveByProperty, cachedSetProperty } = require("./util/cleverMerge");
1515

1616
/** @typedef {import("../declarations/WebpackOptions").Externals} Externals */
17+
/** @typedef {import("../declarations/WebpackOptions").ExternalsPresets} ExternalsPresets */
1718
/** @typedef {import("./Compilation").DepConstructor} DepConstructor */
1819
/** @typedef {import("./ExternalModule").DependencyMeta} DependencyMeta */
1920
/** @typedef {import("./Module")} Module */
@@ -60,10 +61,12 @@ class ExternalModuleFactoryPlugin {
6061
/**
6162
* @param {string | undefined} type default external type
6263
* @param {Externals} externals externals config
64+
* @param {ExternalsPresets} [externalsPresets] externals preset config
6365
*/
64-
constructor(type, externals) {
66+
constructor(type, externals, externalsPresets) {
6567
this.type = type;
6668
this.externals = externals;
69+
this.externalsPresets = externalsPresets;
6770
}
6871

6972
/**
@@ -72,6 +75,7 @@ class ExternalModuleFactoryPlugin {
7275
*/
7376
apply(normalModuleFactory) {
7477
const globalType = this.type;
78+
const externalsPresets = this.externalsPresets;
7579
normalModuleFactory.hooks.factorize.tapAsync(
7680
"ExternalModuleFactoryPlugin",
7781
(data, callback) => {
@@ -135,14 +139,21 @@ class ExternalModuleFactoryPlugin {
135139

136140
dependencyMeta = {
137141
attributes: dependency.assertions,
138-
externalType
142+
externalType,
143+
externalsPresets
139144
};
140145
} else if (dependency instanceof CssImportDependency) {
141146
dependencyMeta = {
142147
layer: dependency.layer,
143148
supports: dependency.supports,
144149
media: dependency.media
145150
};
151+
} else {
152+
dependencyMeta = {
153+
attributes: undefined,
154+
externalType: undefined,
155+
externalsPresets
156+
};
146157
}
147158

148159
callback(

lib/ExternalsPlugin.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,11 @@ class ExternalsPlugin {
2727
*/
2828
apply(compiler) {
2929
compiler.hooks.compile.tap("ExternalsPlugin", ({ normalModuleFactory }) => {
30-
new ExternalModuleFactoryPlugin(this.type, this.externals).apply(
31-
normalModuleFactory
32-
);
30+
new ExternalModuleFactoryPlugin(
31+
this.type,
32+
this.externals,
33+
compiler.options.externalsPresets
34+
).apply(normalModuleFactory);
3335
});
3436
}
3537
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import fs from 'fs'
2+
3+
it("require should use `node-commonjs` when externalsPresets.node is true", () => {
4+
const nodeCommonjsFs = require("node-commonjs-fs");
5+
expect(nodeCommonjsFs).toBe(fs);
6+
});
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
module.exports = () =>
2+
!process.version.startsWith("v10.") && !process.version.startsWith("v12.");
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/** @typedef {import("../../../../").Compilation} Compilation */
2+
3+
/** @type {import("../../../../").Configuration} */
4+
module.exports = {
5+
externals: {
6+
"node-commonjs-fs": "fs",
7+
"node-commonjs-url": "url"
8+
},
9+
externalsType: "module-import",
10+
externalsPresets: {
11+
node: true
12+
},
13+
output: {
14+
module: true
15+
},
16+
target: "node14",
17+
experiments: {
18+
outputModule: true
19+
},
20+
plugins: [
21+
function () {
22+
/**
23+
* @param {Compilation} compilation compilation
24+
* @returns {void}
25+
*/
26+
const handler = compilation => {
27+
compilation.hooks.afterProcessAssets.tap("testcase", assets => {
28+
const output = assets["bundle0.mjs"].source();
29+
expect(output).toContain(
30+
`module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("fs");`
31+
);
32+
});
33+
};
34+
this.hooks.compilation.tap("testcase", handler);
35+
}
36+
]
37+
};

types.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5399,6 +5399,7 @@ type ImportAttributes = Record<string, string> & {};
53995399
declare interface ImportDependencyMeta {
54005400
attributes?: ImportAttributes;
54015401
externalType?: "import" | "module";
5402+
externalsPresets?: ExternalsPresets;
54025403
}
54035404
declare interface ImportModuleOptions {
54045405
/**

0 commit comments

Comments
 (0)