add support for async externals

add `promise` external
add `import` external (uses import())

add `output.importFunctionName` option to change the `import()` function name

allow for inline external type when using arrays

fix some typings

fix namespace object behavior when using system external and accessing nested property
fix interop behavior for async dynamic modules
This commit is contained in:
Tobias Koppers 2020-05-15 16:24:11 +02:00
parent 3c7f6a6616
commit a66290a15f
23 changed files with 381 additions and 98 deletions

View File

@ -127,8 +127,7 @@ export type ExternalItem =
};
}
| ((
context: string,
request: string,
data: {context: string; request: string},
callback: (err?: Error, result?: string) => void
) => void);
/**
@ -150,7 +149,9 @@ export type ExternalsType =
| "umd"
| "umd2"
| "jsonp"
| "system";
| "system"
| "promise"
| "import";
/**
* Filtering values.
*/
@ -397,6 +398,10 @@ export type HotUpdateMainFilename = string;
* Wrap javascript code into IIFE's to avoid leaking into global scope.
*/
export type Iife = boolean;
/**
* The name of the native import() function (can be exchanged for a polyfill).
*/
export type ImportFunctionName = string;
/**
* The JSONP function used by webpack for async loading of chunks.
*/
@ -1586,6 +1591,10 @@ export interface Output {
* Wrap javascript code into IIFE's to avoid leaking into global scope.
*/
iife?: Iife;
/**
* The name of the native import() function (can be exchanged for a polyfill).
*/
importFunctionName?: ImportFunctionName;
/**
* The JSONP function used by webpack for async loading of chunks.
*/
@ -2043,6 +2052,10 @@ export interface OutputNormalized {
* Wrap javascript code into IIFE's to avoid leaking into global scope.
*/
iife?: Iife;
/**
* The name of the native import() function (can be exchanged for a polyfill).
*/
importFunctionName?: ImportFunctionName;
/**
* The JSONP function used by webpack for async loading of chunks.
*/

View File

@ -13,9 +13,9 @@ export type Overrides = (OverridesItem | OverridesObject)[] | OverridesObject;
*/
export type OverridesItem = string;
/**
* Type of library.
* Specifies the default type of externals ('amd*', 'umd*', 'system' and 'jsonp' depend on output.libraryTarget set to the same value).
*/
export type LibraryType =
export type ExternalsType =
| "var"
| "module"
| "assign"
@ -31,7 +31,9 @@ export type LibraryType =
| "umd"
| "umd2"
| "jsonp"
| "system";
| "system"
| "promise"
| "import";
/**
* Container locations and request scopes from which modules should be resolved and loaded at runtime. When provided, property name is used as request scope, otherwise request scope is automatically inferred from container location.
*/
@ -53,7 +55,7 @@ export interface ContainerReferencePluginOptions {
/**
* The external type of the remote containers.
*/
remoteType: LibraryType;
remoteType: ExternalsType;
/**
* Container locations and request scopes from which modules should be resolved and loaded at runtime. When provided, property name is used as request scope, otherwise request scope is automatically inferred from container location.
*/

View File

@ -74,6 +74,28 @@ export type Overrides = (OverridesItem | OverridesObject)[] | OverridesObject;
* Request to a module in this container that should override overridable modules in the remote container.
*/
export type OverridesItem = string;
/**
* Specifies the default type of externals ('amd*', 'umd*', 'system' and 'jsonp' depend on output.libraryTarget set to the same value).
*/
export type ExternalsType =
| "var"
| "module"
| "assign"
| "this"
| "window"
| "self"
| "global"
| "commonjs"
| "commonjs2"
| "commonjs-module"
| "amd"
| "amd-require"
| "umd"
| "umd2"
| "jsonp"
| "system"
| "promise"
| "import";
/**
* Container locations and request scopes from which modules should be resolved and loaded at runtime. When provided, property name is used as request scope, otherwise request scope is automatically inferred from container location.
*/
@ -123,7 +145,7 @@ export interface ModuleFederationPluginOptions {
/**
* The external type of the remote containers.
*/
remoteType?: LibraryType;
remoteType?: ExternalsType;
/**
* Container locations and request scopes from which modules should be resolved and loaded at runtime. When provided, property name is used as request scope, otherwise request scope is automatically inferred from container location.
*/

View File

@ -15,6 +15,7 @@ const makePathsRelative = require("./util/identifier").makePathsRelative;
const validateOptions = require("schema-utils");
const schema = require("../schemas/plugins/DllReferencePlugin.json");
/** @typedef {import("../declarations/WebpackOptions").Externals} Externals */
/** @typedef {import("../declarations/plugins/DllReferencePlugin").DllReferencePluginOptions} DllReferencePluginOptions */
/** @typedef {import("../declarations/plugins/DllReferencePlugin").DllReferencePluginOptionsManifest} DllReferencePluginOptionsManifest */
@ -106,6 +107,7 @@ class DllReferencePlugin {
if (!content) content = manifest.content;
}
}
/** @type {Externals} */
const externals = {};
const source = "dll-reference " + name;
externals[source] = name;

View File

@ -55,13 +55,35 @@ const getSourceForCommonJsExternal = moduleAndSpecifiers => {
return `module.exports = require(${JSON.stringify(moduleAndSpecifiers)});`;
}
const moduleName = moduleAndSpecifiers[0];
const objectLookup = moduleAndSpecifiers
.slice(1)
.map(r => `[${JSON.stringify(r)}]`)
.join("");
return `module.exports = require(${JSON.stringify(
moduleName
)})${objectLookup};`;
)})${propertyAccess(moduleAndSpecifiers, 1)};`;
};
/**
* @param {string|string[]} moduleAndSpecifiers the module request
* @param {RuntimeTemplate} runtimeTemplate the runtime template
* @returns {string} the generated source
*/
const getSourceForImportExternal = (moduleAndSpecifiers, runtimeTemplate) => {
const importName = runtimeTemplate.outputOptions.importFunctionName;
if (!Array.isArray(moduleAndSpecifiers)) {
return `module.exports = ${importName}(${JSON.stringify(
moduleAndSpecifiers
)});`;
}
if (moduleAndSpecifiers.length === 1) {
return `module.exports = ${importName}(${JSON.stringify(
moduleAndSpecifiers[0]
)});`;
}
const moduleName = moduleAndSpecifiers[0];
return `module.exports = ${importName}(${JSON.stringify(
moduleName
)}).then(${runtimeTemplate.returningFunction(
`module${propertyAccess(moduleAndSpecifiers, 1)}`,
"module"
)});`;
};
/**
@ -196,15 +218,27 @@ class ExternalModule extends Module {
*/
build(options, compilation, resolver, fs, callback) {
this.buildMeta = {
async: false,
exportsType: undefined
};
this.buildInfo = {
strict: true
};
this.clearDependenciesAndBlocks();
if (this.externalType === "system") {
this.buildMeta.exportsType = "namespace";
this.addDependency(new StaticExportsDependency(true, true));
switch (this.externalType) {
case "system":
if (!Array.isArray(this.request) || this.request.length === 1)
this.buildMeta.exportsType = "namespace";
this.addDependency(new StaticExportsDependency(true, true));
break;
case "promise":
this.buildMeta.async = true;
break;
case "import":
this.buildMeta.async = true;
if (!Array.isArray(this.request) || this.request.length === 1)
this.buildMeta.exportsType = "namespace";
break;
}
callback();
}
@ -240,9 +274,12 @@ class ExternalModule extends Module {
request,
runtimeTemplate
);
case "import":
return getSourceForImportExternal(request, runtimeTemplate);
case "module":
throw new Error("Module external type is not implemented yet");
case "var":
case "promise":
case "const":
case "let":
case "assign":

View File

@ -7,6 +7,10 @@
const util = require("util");
const ExternalModule = require("./ExternalModule");
/** @typedef {import("../declarations/WebpackOptions").Externals} Externals */
/** @typedef {import("./NormalModuleFactory")} NormalModuleFactory */
const UNSPECIFIED_EXTERNAL_TYPE_REGEXP = /^[a-z0-9]+ /;
// TODO webpack 6 remove this
@ -19,11 +23,19 @@ const callDeprecatedExternals = util.deprecate(
);
class ExternalModuleFactoryPlugin {
/**
* @param {string | undefined} type default external type
* @param {Externals} externals externals config
*/
constructor(type, externals) {
this.type = type;
this.externals = externals;
}
/**
* @param {NormalModuleFactory} normalModuleFactory the normal module factory
* @returns {void}
*/
apply(normalModuleFactory) {
const globalType = this.type;
normalModuleFactory.hooks.factorize.tapAsync(
@ -33,7 +45,7 @@ class ExternalModuleFactoryPlugin {
const dependency = data.dependencies[0];
/**
* @param {string|boolean} value the external config
* @param {string|string[]|boolean|Record<string, string|string[]>} value the external config
* @param {string|undefined} type type of external
* @param {function(Error=, ExternalModule=): void} callback callback
* @returns {void}
@ -43,7 +55,7 @@ class ExternalModuleFactoryPlugin {
// Not externals, fallback to original factory
return callback();
}
/** @type {string} */
/** @type {string | string[] | Record<string, string|string[]>} */
let externalConfig;
if (value === true) {
externalConfig = dependency.request;
@ -51,13 +63,27 @@ class ExternalModuleFactoryPlugin {
externalConfig = value;
}
// When no explicit type is specified, extract it from the externalConfig
if (
type === undefined &&
UNSPECIFIED_EXTERNAL_TYPE_REGEXP.test(externalConfig)
) {
const idx = externalConfig.indexOf(" ");
type = externalConfig.substr(0, idx);
externalConfig = externalConfig.substr(idx + 1);
if (type === undefined) {
if (
typeof externalConfig === "string" &&
UNSPECIFIED_EXTERNAL_TYPE_REGEXP.test(externalConfig)
) {
const idx = externalConfig.indexOf(" ");
type = externalConfig.substr(0, idx);
externalConfig = externalConfig.substr(idx + 1);
} else if (
Array.isArray(externalConfig) &&
externalConfig.length > 0 &&
UNSPECIFIED_EXTERNAL_TYPE_REGEXP.test(externalConfig[0])
) {
const firstItem = externalConfig[0];
const idx = firstItem.indexOf(" ");
type = firstItem.substr(0, idx);
externalConfig = [
firstItem.substr(idx + 1),
...externalConfig.slice(1)
];
}
}
callback(
null,
@ -69,6 +95,11 @@ class ExternalModuleFactoryPlugin {
);
};
/**
* @param {Externals} externals externals config
* @param {function(Error=, ExternalModule=): void} callback callback
* @returns {void}
*/
const handleExternals = (externals, callback) => {
if (typeof externals === "string") {
if (externals === dependency.request) {

View File

@ -7,9 +7,14 @@
const ExternalModuleFactoryPlugin = require("./ExternalModuleFactoryPlugin");
/** @typedef {import("../declarations/WebpackOptions").Externals} Externals */
/** @typedef {import("./Compiler")} Compiler */
class ExternalsPlugin {
/**
* @param {string | undefined} type default external type
* @param {Externals} externals externals config
*/
constructor(type, externals) {
this.type = type;
this.externals = externals;

View File

@ -115,5 +115,6 @@ InitFragment.STAGE_HARMONY_EXPORTS = 30;
InitFragment.STAGE_HARMONY_IMPORTS = 40;
InitFragment.STAGE_PROVIDES = 50;
InitFragment.STAGE_ASYNC_DEPENDENCIES = 60;
InitFragment.STAGE_ASYNC_HARMONY_IMPORTS = 70;
module.exports = InitFragment;

View File

@ -10,7 +10,7 @@ const RuntimeGlobals = require("./RuntimeGlobals");
const Template = require("./Template");
const propertyAccess = require("./util/propertyAccess");
/** @typedef {import("../declarations/WebpackOptions").Output} OutputOptions */
/** @typedef {import("../declarations/WebpackOptions").OutputNormalized} OutputOptions */
/** @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock */
/** @typedef {import("./ChunkGraph")} ChunkGraph */
/** @typedef {import("./InitFragment")} InitFragment */
@ -398,7 +398,7 @@ class RuntimeTemplate {
runtimeRequirements
});
let getModuleFunction;
let appending;
let idExpr = JSON.stringify(chunkGraph.getModuleId(module));
const comment = this.comment({
request
@ -439,13 +439,13 @@ class RuntimeTemplate {
weak,
runtimeRequirements
});
getModuleFunction = this.basicFunction(
appending = `.then(${this.basicFunction(
"",
`${header}return ${rawModule};`
);
)})`;
} else {
runtimeRequirements.add(RuntimeGlobals.require);
getModuleFunction = `__webpack_require__.bind(__webpack_require__, ${comment}${idExpr})`;
appending = `.then(__webpack_require__.bind(__webpack_require__, ${comment}${idExpr}))`;
}
break;
case "dynamic":
@ -457,18 +457,42 @@ class RuntimeTemplate {
case "default-only":
fakeType |= 1;
runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
if (header) {
const returnExpression = `${RuntimeGlobals.createFakeNamespaceObject}(${moduleIdExpr}, ${fakeType})`;
getModuleFunction = header
? this.basicFunction("", `${header}return ${returnExpression};`)
: this.returningFunction(returnExpression);
if (chunkGraph.moduleGraph.isAsync(module)) {
if (header) {
const rawModule = this.moduleRaw({
module,
chunkGraph,
request,
weak,
runtimeRequirements
});
appending = `.then(${this.basicFunction(
"",
`${header}return ${rawModule};`
)})`;
} else {
runtimeRequirements.add(RuntimeGlobals.require);
appending = `.then(__webpack_require__.bind(__webpack_require__, ${comment}${idExpr}))`;
}
appending += `.then(${this.returningFunction(
`${RuntimeGlobals.createFakeNamespaceObject}(m, ${fakeType})`,
"m"
)})`;
} else {
getModuleFunction = `${RuntimeGlobals.createFakeNamespaceObject}.bind(__webpack_require__, ${comment}${idExpr}, ${fakeType})`;
if (header) {
const returnExpression = `${RuntimeGlobals.createFakeNamespaceObject}(${moduleIdExpr}, ${fakeType})`;
appending = `.then(${this.basicFunction(
"",
`${header}return ${returnExpression};`
)})`;
} else {
appending = `.then(${RuntimeGlobals.createFakeNamespaceObject}.bind(__webpack_require__, ${comment}${idExpr}, ${fakeType}))`;
}
}
break;
}
return `${promise || "Promise.resolve()"}.then(${getModuleFunction})`;
return `${promise || "Promise.resolve()"}${appending}`;
}
/**
@ -482,7 +506,7 @@ class RuntimeTemplate {
* @param {Module} options.originModule module in which the statement is emitted
* @param {boolean=} options.weak true, if this is a weak dependency
* @param {Set<string>} options.runtimeRequirements if set, will be filled with runtime requirements
* @returns {string} the import statement
* @returns {[string, string]} the import statement and the compat statement
*/
importStatement({
update,
@ -495,20 +519,26 @@ class RuntimeTemplate {
runtimeRequirements
}) {
if (!module) {
return this.missingModuleStatement({
request
});
return [
this.missingModuleStatement({
request
}),
""
];
}
if (chunkGraph.getModuleId(module) === null) {
if (weak) {
// only weak referenced modules don't get an id
// we can always emit an error emitting code here
return this.weakError({
module,
chunkGraph,
request,
type: "statements"
});
return [
this.weakError({
module,
chunkGraph,
request,
type: "statements"
}),
""
];
}
throw new Error(
`RuntimeTemplate.importStatement(): Module ${module.identifier()} has no id. This should not happen.`
@ -522,15 +552,20 @@ class RuntimeTemplate {
});
const optDeclaration = update ? "" : "var ";
const exportsType = module.buildMeta && module.buildMeta.exportsType;
const exportsType = module.getExportsType(
originModule.buildMeta.strictHarmonyModule
);
runtimeRequirements.add(RuntimeGlobals.require);
let content = `/* harmony import */ ${optDeclaration}${importVar} = __webpack_require__(${moduleId});\n`;
const importContent = `/* harmony import */ ${optDeclaration}${importVar} = __webpack_require__(${moduleId});\n`;
if (!exportsType && !originModule.buildMeta.strictHarmonyModule) {
if (exportsType === "dynamic") {
runtimeRequirements.add(RuntimeGlobals.compatGetDefaultExport);
content += `/* harmony import */ ${optDeclaration}${importVar}_default = /*#__PURE__*/${RuntimeGlobals.compatGetDefaultExport}(${importVar});\n`;
return [
importContent,
`/* harmony import */ ${optDeclaration}${importVar}_default = /*#__PURE__*/${RuntimeGlobals.compatGetDefaultExport}(${importVar});\n`
];
}
return content;
return [importContent, ""];
}
/**

View File

@ -379,6 +379,7 @@ const applyOutputDefaults = (
F(output, "module", () => !!outputModule);
F(output, "iife", () => !output.module);
D(output, "ecmaVersion", 6);
D(output, "importFunctionName", "import");
F(output, "chunkFilename", () => {
const filename = output.filename;
if (typeof filename !== "function") {

View File

@ -202,6 +202,7 @@ const getNormalizedWebpackOptions = config => {
hotUpdateFunction: output.hotUpdateFunction,
hotUpdateMainFilename: output.hotUpdateMainFilename,
iife: output.iife,
importFunctionName: output.importFunctionName,
jsonpFunction: output.jsonpFunction,
jsonpScriptType: output.jsonpScriptType,
library: libraryBase && {

View File

@ -67,7 +67,10 @@ HarmonyAcceptDependency.Template = class HarmonyAcceptDependencyTemplate extends
.filter(dependency =>
HarmonyImportDependency.Template.isImportEmitted(dependency, module)
)
.map(dependency => dependency.getImportStatement(true, templateContext))
.map(dependency => {
const s = dependency.getImportStatement(true, templateContext);
return s[0] + s[1];
})
.join("");
if (dep.hasCallback) {

View File

@ -65,7 +65,7 @@ class HarmonyImportDependency extends ModuleDependency {
/**
* @param {boolean} update create new variables or update existing one
* @param {DependencyTemplateContext} templateContext the template context
* @returns {string} name of the variable for the import
* @returns {[string, string]} the import statement and the compat statement
*/
getImportStatement(
update,
@ -253,20 +253,38 @@ HarmonyImportDependency.Template = class HarmonyImportDependencyTemplate extends
emittedModules.add(module);
}
templateContext.initFragments.push(
new InitFragment(
dep.getImportStatement(false, templateContext),
InitFragment.STAGE_HARMONY_IMPORTS,
dep.sourceOrder,
key
)
);
const importStatement = dep.getImportStatement(false, templateContext);
if (templateContext.moduleGraph.isAsync(referencedModule)) {
templateContext.initFragments.push(
new InitFragment(
importStatement[0],
InitFragment.STAGE_HARMONY_IMPORTS,
dep.sourceOrder,
key
)
);
templateContext.initFragments.push(
new AwaitDependenciesInitFragment(
new Set([dep.getImportVar(templateContext.moduleGraph)])
)
);
templateContext.initFragments.push(
new InitFragment(
importStatement[1],
InitFragment.STAGE_ASYNC_HARMONY_IMPORTS,
dep.sourceOrder,
key + " compat"
)
);
} else {
templateContext.initFragments.push(
new InitFragment(
importStatement[0] + importStatement[1],
InitFragment.STAGE_HARMONY_IMPORTS,
dep.sourceOrder,
key
)
);
}
}

View File

@ -139,9 +139,14 @@ class SystemLibraryPlugin extends AbstractLibraryPlugin {
}
}
if (!otherUnused) {
externalVarInitialization.push(
`Object.defineProperty(${external}, "__esModule", { value: true });`
);
if (
!Array.isArray(module.request) ||
module.request.length === 1
) {
externalVarInitialization.push(
`Object.defineProperty(${external}, "__esModule", { value: true });`
);
}
if (handledNames.length > 0) {
const name = `${external}handledNames`;
externalVarInitialization.push(

View File

@ -103,6 +103,8 @@ class AsyncWebAssemblyJavascriptGenerator extends Generator {
});
}
);
const importsCode = importStatements.map(([x]) => x).join("");
const importsCompatCode = importStatements.map(([_, x]) => x).join("");
const importObjRequestItems = Array.from(
wasmDepsByRequest,
@ -149,18 +151,28 @@ class AsyncWebAssemblyJavascriptGenerator extends Generator {
(importsObj ? `, ${importsObj})` : `)`);
const source = new RawSource(
Template.asString([
...importStatements,
`${importsCode}${
promises.length > 1
? `${module.moduleArgument}.exports = Promise.all([${promises.join(
", "
)}]).then(function([${promises.join(
", "
)}]) { return ${instantiateCall}; })`
? Template.asString([
`${module.moduleArgument}.exports = Promise.all([${promises.join(
", "
)}]).then(${runtimeTemplate.basicFunction(
`[${promises.join(", ")}]`,
`${importsCompatCode}return ${instantiateCall};`
)})`
])
: promises.length === 1
? `${module.moduleArgument}.exports = Promise.resolve(${promises[0]}).then(function(${promises[0]}) { return ${instantiateCall}; })`
: `${module.moduleArgument}.exports = ${instantiateCall}`
])
? Template.asString([
`${module.moduleArgument}.exports = Promise.resolve(${
promises[0]
}).then(${runtimeTemplate.basicFunction(
promises[0],
`${importsCompatCode}return ${instantiateCall};`
)})`
])
: `${importsCompatCode}${module.moduleArgument}.exports = ${instantiateCall}`
}`
);
return InitFragment.addToSource(source, initFragments, generateContext);
}

View File

@ -155,7 +155,7 @@ class WebAssemblyJavascriptGenerator extends Generator {
originModule: module,
runtimeRequirements
});
return importStatement + reexports.join("\n");
return importStatement[0] + importStatement[1] + reexports.join("\n");
}
)
);

View File

@ -429,7 +429,7 @@
{
"description": "The function is called on each dependency (`function(context, request, callback(err, result))`).",
"instanceof": "Function",
"tsType": "((context: string, request: string, callback: (err?: Error, result?: string) => void) => void)"
"tsType": "((data: { context: string, request: string }, callback: (err?: Error, result?: string) => void) => void)"
}
]
},
@ -465,7 +465,9 @@
"umd",
"umd2",
"jsonp",
"system"
"system",
"promise",
"import"
]
},
"FileCacheOptions": {
@ -658,6 +660,10 @@
"description": "Wrap javascript code into IIFE's to avoid leaking into global scope.",
"type": "boolean"
},
"ImportFunctionName": {
"description": "The name of the native import() function (can be exchanged for a polyfill).",
"type": "string"
},
"InfrastructureLogging": {
"description": "Options for infrastructure level logging.",
"type": "object",
@ -1694,6 +1700,9 @@
"iife": {
"$ref": "#/definitions/Iife"
},
"importFunctionName": {
"$ref": "#/definitions/ImportFunctionName"
},
"jsonpFunction": {
"$ref": "#/definitions/JsonpFunction"
},
@ -1834,6 +1843,9 @@
"iife": {
"$ref": "#/definitions/Iife"
},
"importFunctionName": {
"$ref": "#/definitions/ImportFunctionName"
},
"jsonpFunction": {
"$ref": "#/definitions/JsonpFunction"
},

View File

@ -1,7 +1,7 @@
{
"definitions": {
"LibraryType": {
"description": "Type of library.",
"ExternalsType": {
"description": "Specifies the default type of externals ('amd*', 'umd*', 'system' and 'jsonp' depend on output.libraryTarget set to the same value).",
"enum": [
"var",
"module",
@ -18,7 +18,9 @@
"umd",
"umd2",
"jsonp",
"system"
"system",
"promise",
"import"
]
},
"Overrides": {
@ -157,7 +159,7 @@
"description": "The external type of the remote containers.",
"oneOf": [
{
"$ref": "#/definitions/LibraryType"
"$ref": "#/definitions/ExternalsType"
}
]
},

View File

@ -83,6 +83,29 @@
]
}
},
"ExternalsType": {
"description": "Specifies the default type of externals ('amd*', 'umd*', 'system' and 'jsonp' depend on output.libraryTarget set to the same value).",
"enum": [
"var",
"module",
"assign",
"this",
"window",
"self",
"global",
"commonjs",
"commonjs2",
"commonjs-module",
"amd",
"amd-require",
"umd",
"umd2",
"jsonp",
"system",
"promise",
"import"
]
},
"LibraryCustomUmdCommentObject": {
"description": "Set explicit comments for `commonjs`, `commonjs2`, `amd`, and `root`.",
"type": "object",
@ -503,7 +526,7 @@
"description": "The external type of the remote containers.",
"oneOf": [
{
"$ref": "#/definitions/LibraryType"
"$ref": "#/definitions/ExternalsType"
}
]
},

View File

@ -3917,7 +3917,7 @@ Built at: 1970-04-20 12:42:42
256e72dd8b9a83a6e45b.module.wasm 120 bytes [emitted] [immutable]
325.bundle.js 3.74 KiB [emitted]
526.bundle.js 368 bytes [emitted] [id hint: vendors]
780.bundle.js 526 bytes [emitted]
780.bundle.js 525 bytes [emitted]
99.bundle.js 220 bytes [emitted]
a0e9dd97d7ced35a5b2c.module.wasm 154 bytes [emitted] [immutable]
bundle.js 11 KiB [emitted] [name: main-1df31ce3]

View File

@ -0,0 +1,15 @@
import value from "promise-external";
import request from "import-external";
it("should allow async externals", () => {
expect(value).toBe(42);
expect(request).toBe("/hello/world.js");
});
it("should allow to catch errors of async externals", () => {
return expect(() => import("failing-promise-external")).rejects.toEqual(
expect.objectContaining({
message: "external reject"
})
);
});

View File

@ -0,0 +1,16 @@
module.exports = {
output: {
libraryTarget: "commonjs-module",
importFunctionName: "((name) => Promise.resolve({ request: name }))"
},
externals: {
"promise-external":
"promise new Promise(resolve => setTimeout(() => resolve(42), 100))",
"failing-promise-external":
"promise new Promise((resolve, reject) => setTimeout(() => reject(new Error('external reject')), 100))",
"import-external": ["import /hello/world.js", "request"]
},
experiments: {
importAsync: true
}
};

55
types.d.ts vendored
View File

@ -1337,7 +1337,7 @@ declare interface Configuration {
/**
* Specifies the default type of externals ('amd*', 'umd*', 'system' and 'jsonp' depend on output.libraryTarget set to the same value).
*/
externalsType?: LibraryType;
externalsType?: ExternalsType;
/**
* Options for infrastructure level logging.
@ -1498,7 +1498,7 @@ declare interface ContainerReferencePluginOptions {
/**
* The external type of the remote containers.
*/
remoteType: LibraryType;
remoteType: ExternalsType;
/**
* Container locations and request scopes from which modules should be resolved and loaded at runtime. When provided, property name is used as request scope, otherwise request scope is automatically inferred from container location.
@ -2310,8 +2310,7 @@ type ExternalItem =
| RegExp
| { [index: string]: string | boolean | string[] | { [index: string]: any } }
| ((
context: string,
request: string,
data: { context: string; request: string },
callback: (err: Error, result: string) => void
) => void);
declare class ExternalModule extends Module {
@ -2331,20 +2330,38 @@ type Externals =
| ExternalItem[]
| { [index: string]: string | boolean | string[] | { [index: string]: any } }
| ((
context: string,
request: string,
data: { context: string; request: string },
callback: (err: Error, result: string) => void
) => void);
declare class ExternalsPlugin {
constructor(type?: any, externals?: any);
type: any;
externals: any;
constructor(type: string, externals: Externals);
type: string;
externals: Externals;
/**
* Apply the plugin
*/
apply(compiler: Compiler): void;
}
type ExternalsType =
| "var"
| "module"
| "assign"
| "this"
| "window"
| "self"
| "global"
| "commonjs"
| "commonjs2"
| "commonjs-module"
| "amd"
| "amd-require"
| "umd"
| "umd2"
| "jsonp"
| "system"
| "promise"
| "import";
declare interface FactorizeModuleOptions {
currentProfile: ModuleProfile;
factory: ModuleFactory;
@ -3748,7 +3765,7 @@ declare interface ModuleFederationPluginOptions {
/**
* The external type of the remote containers.
*/
remoteType?: LibraryType;
remoteType?: ExternalsType;
/**
* Container locations and request scopes from which modules should be resolved and loaded at runtime. When provided, property name is used as request scope, otherwise request scope is automatically inferred from container location.
@ -4767,6 +4784,11 @@ declare interface Output {
*/
iife?: boolean;
/**
* The name of the native import() function (can be exchanged for a polyfill).
*/
importFunctionName?: string;
/**
* The JSONP function used by webpack for async loading of chunks.
*/
@ -4971,6 +4993,11 @@ declare interface OutputNormalized {
*/
iife?: boolean;
/**
* The name of the native import() function (can be exchanged for a polyfill).
*/
importFunctionName?: string;
/**
* The JSONP function used by webpack for async loading of chunks.
*/
@ -5875,7 +5902,7 @@ declare class RuntimeModule extends Module {
getGeneratedCode(): string;
}
declare abstract class RuntimeTemplate {
outputOptions: Output;
outputOptions: OutputNormalized;
requestShortener: RequestShortener;
isIIFE(): boolean;
supportsConst(): boolean;
@ -5961,7 +5988,7 @@ declare abstract class RuntimeTemplate {
/**
* which kind of code should be returned
*/
type: "expression" | "promise" | "statements";
type: "promise" | "expression" | "statements";
}): string;
moduleId(__0: {
/**
@ -6118,7 +6145,7 @@ declare abstract class RuntimeTemplate {
* if set, will be filled with runtime requirements
*/
runtimeRequirements: Set<string>;
}): string;
}): [string, string];
exportFromImport(__0: {
/**
* the module graph
@ -7075,7 +7102,7 @@ declare interface WebpackOptionsNormalized {
/**
* Specifies the default type of externals ('amd*', 'umd*', 'system' and 'jsonp' depend on output.libraryTarget set to the same value).
*/
externalsType?: LibraryType;
externalsType?: ExternalsType;
/**
* Options for infrastructure level logging.