feat: add new external type "module-import" and use it by default for modules

This commit is contained in:
Alexander Akait 2024-08-06 16:03:19 +03:00 committed by GitHub
commit 740ec9e462
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 158 additions and 69 deletions

View File

@ -219,6 +219,7 @@ export type ExternalsType =
| "system"
| "promise"
| "import"
| "module-import"
| "script"
| "node-commonjs";
/**

View File

@ -27,6 +27,7 @@ export type ExternalsType =
| "system"
| "promise"
| "import"
| "module-import"
| "script"
| "node-commonjs";
/**

View File

@ -84,6 +84,7 @@ export type ExternalsType =
| "system"
| "promise"
| "import"
| "module-import"
| "script"
| "node-commonjs";
/**

View File

@ -53,7 +53,7 @@ const { register } = require("./util/serialization");
/** @typedef {import("./util/fs").InputFileSystem} InputFileSystem */
/** @typedef {import("./util/runtime").RuntimeSpec} RuntimeSpec */
/** @typedef {{ attributes?: ImportAttributes }} ImportDependencyMeta */
/** @typedef {{ attributes?: ImportAttributes, externalType: "import" | "module" | undefined }} ImportDependencyMeta */
/** @typedef {{ layer?: string, supports?: string, media?: string }} CssImportDependencyMeta */
/** @typedef {ImportDependencyMeta | CssImportDependencyMeta} DependencyMeta */
@ -164,7 +164,10 @@ const getSourceForImportExternal = (
dependencyMeta
) => {
const importName = runtimeTemplate.outputOptions.importFunctionName;
if (!runtimeTemplate.supportsDynamicImport() && importName === "import") {
if (
!runtimeTemplate.supportsDynamicImport() &&
(importName === "import" || importName !== "module-import")
) {
throw new Error(
"The target environment doesn't support 'import()' so it's not possible to use external type 'import'"
);
@ -575,7 +578,34 @@ class ExternalModule extends Module {
canMangle = true;
}
break;
case "script":
this.buildMeta.async = true;
EnvironmentNotSupportAsyncWarning.check(
this,
compilation.runtimeTemplate,
"external script"
);
break;
case "promise":
this.buildMeta.async = true;
EnvironmentNotSupportAsyncWarning.check(
this,
compilation.runtimeTemplate,
"external promise"
);
break;
case "module":
case "import":
case "module-import": {
const type =
externalType === "module-import" &&
this.dependencyMeta &&
/** @type {ImportDependencyMeta} */ (this.dependencyMeta).externalType
? /** @type {ImportDependencyMeta} */ (this.dependencyMeta)
.externalType
: externalType;
if (type === "module") {
if (this.buildInfo.module) {
if (!Array.isArray(request) || request.length === 1) {
this.buildMeta.exportsType = "namespace";
@ -593,24 +623,9 @@ class ExternalModule extends Module {
canMangle = false;
}
}
break;
case "script":
this.buildMeta.async = true;
EnvironmentNotSupportAsyncWarning.check(
this,
compilation.runtimeTemplate,
"external script"
);
break;
case "promise":
this.buildMeta.async = true;
EnvironmentNotSupportAsyncWarning.check(
this,
compilation.runtimeTemplate,
"external promise"
);
break;
case "import":
}
if (type === "import") {
this.buildMeta.async = true;
EnvironmentNotSupportAsyncWarning.check(
this,
@ -621,8 +636,11 @@ class ExternalModule extends Module {
this.buildMeta.exportsType = "namespace";
canMangle = false;
}
}
break;
}
}
this.addDependency(new StaticExportsDependency(true, canMangle));
callback();
}
@ -718,15 +736,27 @@ class ExternalModule extends Module {
runtimeTemplate
);
}
case "script":
return getSourceForScriptExternal(request, runtimeTemplate);
case "module":
case "import":
case "module-import": {
const type =
externalType === "module-import" &&
dependencyMeta &&
/** @type {ImportDependencyMeta} */ (dependencyMeta).externalType
? /** @type {ImportDependencyMeta} */ (dependencyMeta).externalType
: externalType;
if (type === "import") {
return getSourceForImportExternal(
request,
runtimeTemplate,
/** @type {ImportDependencyMeta} */ (dependencyMeta)
);
case "script":
return getSourceForScriptExternal(request, runtimeTemplate);
case "module": {
}
if (type === "module") {
if (!(/** @type {BuildInfo} */ (this.buildInfo).module)) {
if (!runtimeTemplate.supportsDynamicImport()) {
throw new Error(
@ -756,6 +786,9 @@ class ExternalModule extends Module {
/** @type {ImportDependencyMeta} */ (dependencyMeta)
);
}
break;
}
case "var":
case "promise":
case "const":

View File

@ -118,8 +118,16 @@ class ExternalModuleFactoryPlugin {
dependency instanceof ImportDependency ||
dependency instanceof ContextElementDependency
) {
const externalType =
dependency instanceof HarmonyImportDependency
? "module"
: dependency instanceof ImportDependency
? "import"
: undefined;
dependencyMeta = {
attributes: dependency.assertions
attributes: dependency.assertions,
externalType
};
} else if (dependency instanceof CssImportDependency) {
dependencyMeta = {

View File

@ -286,7 +286,10 @@ class WebpackOptionsApply extends OptionsApply {
"library type \"modern-module\" is only allowed when 'experiments.outputModule' is enabled"
);
}
if (options.externalsType === "module") {
if (
options.externalsType === "module" ||
options.externalsType === "module-import"
) {
throw new Error(
"'externalsType: \"module\"' is only allowed when 'experiments.outputModule' is enabled"
);

View File

@ -275,7 +275,7 @@ const applyWebpackOptionsDefaults = (options, compilerIndex) => {
validExternalTypes.includes(options.output.library.type)
? /** @type {ExternalsType} */ (options.output.library.type)
: options.output.module
? "module"
? "module-import"
: "var";
});

File diff suppressed because one or more lines are too long

View File

@ -1285,6 +1285,7 @@
"system",
"promise",
"import",
"module-import",
"script",
"node-commonjs"
]

File diff suppressed because one or more lines are too long

View File

@ -22,6 +22,7 @@
"system",
"promise",
"import",
"module-import",
"script",
"node-commonjs"
]

View File

@ -3,4 +3,4 @@
* DO NOT MODIFY BY HAND.
* Run `yarn special-lint-fix` to update
*/
"use strict";function o(r,{instancePath:s="",parentData:m,parentDataProperty:t,rootData:e=r}={}){return"var"!==r&&"module"!==r&&"assign"!==r&&"this"!==r&&"window"!==r&&"self"!==r&&"global"!==r&&"commonjs"!==r&&"commonjs2"!==r&&"commonjs-module"!==r&&"commonjs-static"!==r&&"amd"!==r&&"amd-require"!==r&&"umd"!==r&&"umd2"!==r&&"jsonp"!==r&&"system"!==r&&"promise"!==r&&"import"!==r&&"script"!==r&&"node-commonjs"!==r?(o.errors=[{params:{}}],!1):(o.errors=null,!0)}module.exports=o,module.exports.default=o;
"use strict";function o(m,{instancePath:r="",parentData:s,parentDataProperty:t,rootData:e=m}={}){return"var"!==m&&"module"!==m&&"assign"!==m&&"this"!==m&&"window"!==m&&"self"!==m&&"global"!==m&&"commonjs"!==m&&"commonjs2"!==m&&"commonjs-module"!==m&&"commonjs-static"!==m&&"amd"!==m&&"amd-require"!==m&&"umd"!==m&&"umd2"!==m&&"jsonp"!==m&&"system"!==m&&"promise"!==m&&"import"!==m&&"module-import"!==m&&"script"!==m&&"node-commonjs"!==m?(o.errors=[{params:{}}],!1):(o.errors=null,!0)}module.exports=o,module.exports.default=o;

File diff suppressed because one or more lines are too long

View File

@ -126,6 +126,7 @@
"system",
"promise",
"import",
"module-import",
"script",
"node-commonjs"
]

View File

@ -920,7 +920,7 @@ describe("snapshots", () => {
+ "outputModule": true,
@@ ... @@
- "externalsType": "var",
+ "externalsType": "module",
+ "externalsType": "module-import",
@@ ... @@
- "dynamicImport": undefined,
- "dynamicImportInWorker": undefined,

View File

@ -1030,6 +1030,7 @@ Object {
"system",
"promise",
"import",
"module-import",
"script",
"node-commonjs",
],

View File

@ -2,6 +2,7 @@ import value from "promise-external";
import value2 from "module-promise-external";
import value3 from "object-promise-external";
import request from "import-external";
import request2 from "module-import-external";
import "./module.mjs";
it("should allow async externals", () => {
@ -9,6 +10,7 @@ it("should allow async externals", () => {
expect(value2).toBe(42);
expect(value3).toEqual({ default: 42, named: true });
expect(request).toBe("/hello/world.js");
expect(request2).toBe("/hello/world.js");
});
it("should allow to catch errors of async externals", () => {

View File

@ -1,4 +1,5 @@
module.exports = {
target: ["web", "es2020"],
output: {
libraryTarget: "commonjs-module",
importFunctionName: "((name) => Promise.resolve({ request: name }))"
@ -12,6 +13,7 @@ module.exports = {
"promise new Promise(resolve => setTimeout(() => resolve({ default: 42, named: true }), 100))",
"failing-promise-external":
"promise new Promise((resolve, reject) => setTimeout(() => reject(new Error('external reject')), 100))",
"import-external": ["import /hello/world.js", "request"]
"import-external": ["import /hello/world.js", "request"],
"module-import-external": ["module-import /hello/world.js", "request"]
}
};

View File

@ -4,9 +4,12 @@ import fsPromises1 from "fs-promises";
import fsPromises2 from "module-fs-promises";
import path1 from "path";
import path2 from "module-path";
import url1 from "url";
import url2 from "module-import-url";
it("should be possible to import multiple module externals", () => {
expect(fs2).toBe(fs1);
expect(path2).toBe(path1);
expect(fsPromises2).toBe(fsPromises1);
expect(url1).toBe(url2);
});

View File

@ -8,7 +8,9 @@ const config = o => ({
? ["node-commonjs fs", "promises"]
: "node-commonjs fs/promises",
"module-path": "module path",
path: "node-commonjs path"
path: "node-commonjs path",
"module-import-url": "module-import url",
url: "node-commonjs url"
},
optimization: {
concatenateModules: true,

View File

@ -1,9 +1,11 @@
import * as staticPkg from "./static-package.json" assert { type: "json" };
import * as staticPkgStr from "./static-package-str.json" assert { "type": "json" };
import * as staticPkgModuleImport from "./static-package-module-import.json" assert { type: "json" };
it("should allow async externals", async () => {
expect(staticPkg.default.foo).toBe("static");
expect(staticPkgStr.default.foo).toBe("static-str");
expect(staticPkgModuleImport.default.foo).toBe("static");
const dynamicPkg = await import("./dynamic-package.json", {
assert: { type: "json" }
@ -42,6 +44,12 @@ it("should allow async externals", async () => {
const reExportPkg = await import("./re-export.js");
expect(reExportPkg.foo).toBe("re-export");
const dynamicPkgModuleImport = await import("./dynamic-package-module-import.json", {
assert: { type: "json" }
})
expect(dynamicPkgModuleImport.default.foo).toBe("dynamic");
});
export * from "./re-export-directly.json" assert { type: "json" }

View File

@ -60,6 +60,10 @@ module.exports = {
"./pkg.json": "import ./pkg.json",
"./pkg": "import ./pkg",
"./re-export.json": "module ./re-export.json",
"./re-export-directly.json": "module ./re-export-directly.json"
"./re-export-directly.json": "module ./re-export-directly.json",
"./static-package-module-import.json":
"module-import ./static-package.json",
"./dynamic-package-module-import.json":
"module-import ./dynamic-package.json"
}
};

View File

@ -1,9 +1,11 @@
import * as staticPkg from "./static-package.json" with { type: "json" };
import * as staticPkgStr from "./static-package-str.json" with { "type": "json" };
import * as staticPkgModuleImport from "./static-package-module-import.json" with { "type": "json" };
it("should allow async externals", async () => {
expect(staticPkg.default.foo).toBe("static");
expect(staticPkgStr.default.foo).toBe("static-str");
expect(staticPkgModuleImport.default.foo).toBe("static");
const dynamicPkg = await import("./dynamic-package.json", {
with: { type: "json" }
@ -42,6 +44,12 @@ it("should allow async externals", async () => {
const reExportPkg = await import("./re-export.js");
expect(reExportPkg.foo).toBe("re-export");
const dynamicPkgModuleImport = await import("./dynamic-package-module-import.json", {
with: { type: "json" }
})
expect(dynamicPkgModuleImport.default.foo).toBe("dynamic");
});
export * from "./re-export-directly.json" with { type: "json" }

View File

@ -60,6 +60,10 @@ module.exports = {
"./pkg.json": "import ./pkg.json",
"./pkg": "import ./pkg",
"./re-export.json": "module ./re-export.json",
"./re-export-directly.json": "module ./re-export-directly.json"
"./re-export-directly.json": "module ./re-export-directly.json",
"./static-package-module-import.json":
"module-import ./static-package.json",
"./dynamic-package-module-import.json":
"module-import ./dynamic-package.json"
}
};

5
types.d.ts vendored
View File

@ -2515,6 +2515,7 @@ declare interface Configuration {
| "jsonp"
| "system"
| "promise"
| "module-import"
| "script"
| "node-commonjs";
@ -4631,6 +4632,7 @@ type ExternalsType =
| "jsonp"
| "system"
| "promise"
| "module-import"
| "script"
| "node-commonjs";
declare interface FSImplementation {
@ -5347,6 +5349,7 @@ type IgnorePluginOptions =
type ImportAttributes = Record<string, string> & {};
declare interface ImportDependencyMeta {
attributes?: ImportAttributes;
externalType?: "import" | "module";
}
declare interface ImportModuleOptions {
/**
@ -8257,6 +8260,7 @@ declare interface ModuleFederationPluginOptions {
| "jsonp"
| "system"
| "promise"
| "module-import"
| "script"
| "node-commonjs";
@ -14674,6 +14678,7 @@ declare interface WebpackOptionsNormalized {
| "jsonp"
| "system"
| "promise"
| "module-import"
| "script"
| "node-commonjs";