advanced configs for container plugins

refactor options parsing to allow advanced config options
  automatic scoping has been removed and a scope method is offered instead
move shared schema definitions into a shared schema to allow to sync them
allow to pass multiple modules to a single exposed name
  they are all executed and the last one is exported
allow to pass multiple container locations to remotes
  they are tried in order, falling back to the next one when one fails
allow remotes to be promises as preparation of async externals
This commit is contained in:
Tobias Koppers 2020-05-14 15:50:35 +02:00
parent cfcac33745
commit 4f194f0748
30 changed files with 1911 additions and 350 deletions

154
declarations/_sharedContainer.d.ts vendored Normal file
View File

@ -0,0 +1,154 @@
/*
* This file was automatically generated.
* DO NOT MODIFY BY HAND.
* Run `yarn special-lint-fix` to update
*/
/**
* Modules that should be exposed by this container. When provided, property name is used as public name, otherwise public name is automatically inferred from request.
*/
export type Exposes = (ExposesItem | ExposesObject)[] | ExposesObject;
/**
* Module that should be exposed by this container.
*/
export type ExposesItem = string;
/**
* Modules that should be exposed by this container.
*/
export type ExposesItems = ExposesItem[];
/**
* Modules in this container that should be able to be overridden by the host. When provided, property name is used as override key, otherwise override key is automatically inferred from request.
*/
export type Overridables =
| (OverridablesItem | OverridablesObject)[]
| OverridablesObject;
/**
* Request to a module in this container that should be able to be overridden by the host.
*/
export type OverridablesItem = string;
/**
* Requests to modules in this container that should be able to be overridden by the host.
*/
export type OverridablesItems = OverridablesItem[];
/**
* Modules in this container that should override overridable modules in the remote container. When provided, property name is used as override key, otherwise override key is automatically inferred from request.
*/
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;
/**
* 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.
*/
export type Remotes = (RemotesItem | RemotesObject)[] | RemotesObject;
/**
* Container location from which modules should be resolved and loaded at runtime.
*/
export type RemotesItem = string;
/**
* Container locations from which modules should be resolved and loaded at runtime.
*/
export type RemotesItems = RemotesItem[];
/**
* Modules that should be shared with remotes and/or host. When provided, property name is used as shared key, otherwise shared key is automatically inferred from request.
*/
export type Shared = (SharedItem | SharedObject)[] | SharedObject;
/**
* Module that should be shared with remotes and/or host.
*/
export type SharedItem = string;
export interface _SharedContainer {
[k: string]: any;
}
/**
* Modules that should be exposed by this container. Property names are used as public paths.
*/
export interface ExposesObject {
/**
* Modules that should be exposed by this container.
*/
[k: string]: ExposesConfig | ExposesItem | ExposesItems;
}
/**
* Advanced configuration for modules that should be exposed by this container.
*/
export interface ExposesConfig {
/**
* Request to a module that should be exposed by this container.
*/
import: ExposesItem | ExposesItems;
}
/**
* Requests to modules in this container that should be able to be overridden by the host. Property names are used as override keys.
*/
export interface OverridablesObject {
/**
* Requests to modules in this container that should be able to be overridden by the host.
*/
[k: string]: OverridablesConfig | OverridablesItem | OverridablesItems;
}
/**
* Advanced configuration for modules in this container that should be able to be overridden by the host.
*/
export interface OverridablesConfig {
/**
* Requests to modules in this container that should be able to be overridden by the host.
*/
import: OverridablesItem | OverridablesItems;
}
/**
* Requests to modules in this container that should override overridable modules in the remote container. Property names are used as override keys.
*/
export interface OverridesObject {
/**
* Requests to modules in this container that should override overridable modules in the remote container.
*/
[k: string]: OverridesConfig | OverridesItem;
}
/**
* Advanced configuration for modules in this container that should override overridable modules in the remote container.
*/
export interface OverridesConfig {
/**
* Request to a module in this container that should override overridable modules in the remote container.
*/
import: OverridesItem;
}
/**
* Container locations from which modules should be resolved and loaded at runtime. Property names are used as request scopes.
*/
export interface RemotesObject {
/**
* Container locations from which modules should be resolved and loaded at runtime.
*/
[k: string]: RemotesConfig | RemotesItem | RemotesItems;
}
/**
* Advanced configuration for container locations from which modules should be resolved and loaded at runtime.
*/
export interface RemotesConfig {
/**
* Container locations from which modules should be resolved and loaded at runtime.
*/
external: RemotesItem | RemotesItems;
}
/**
* Modules that should be shared with remotes and/or host. Property names are used as shared keys.
*/
export interface SharedObject {
/**
* Modules that should be shared with remotes and/or host.
*/
[k: string]: SharedConfig | SharedItem;
}
/**
* Advanced configuration for modules that should be shared with remotes and/or host.
*/
export interface SharedConfig {
/**
* Module that should be shared with remotes and/or host.
*/
import: SharedItem;
}

View File

@ -7,12 +7,15 @@
/** /**
* Modules that should be exposed by this container. When provided, property name is used as public name, otherwise public name is automatically inferred from request. * Modules that should be exposed by this container. When provided, property name is used as public name, otherwise public name is automatically inferred from request.
*/ */
export type Exposes = export type Exposes = (ExposesItem | ExposesObject)[] | ExposesObject;
| Exposes[] /**
| string * Module that should be exposed by this container.
| { */
[k: string]: Exposes; export type ExposesItem = string;
}; /**
* Modules that should be exposed by this container.
*/
export type ExposesItems = ExposesItem[];
/** /**
* Add a comment in the UMD wrapper. * Add a comment in the UMD wrapper.
*/ */
@ -53,11 +56,16 @@ export type UmdNamedDefine = boolean;
* Modules in this container that should be able to be overridden by the host. When provided, property name is used as override key, otherwise override key is automatically inferred from request. * Modules in this container that should be able to be overridden by the host. When provided, property name is used as override key, otherwise override key is automatically inferred from request.
*/ */
export type Overridables = export type Overridables =
| Overridables[] | (OverridablesItem | OverridablesObject)[]
| string | OverridablesObject;
| { /**
[k: string]: Overridables; * Request to a module in this container that should be able to be overridden by the host.
}; */
export type OverridablesItem = string;
/**
* Requests to modules in this container that should be able to be overridden by the host.
*/
export type OverridablesItems = OverridablesItem[];
export interface ContainerPluginOptions { export interface ContainerPluginOptions {
/** /**
@ -81,6 +89,24 @@ export interface ContainerPluginOptions {
*/ */
overridables?: Overridables; overridables?: Overridables;
} }
/**
* Modules that should be exposed by this container. Property names are used as public paths.
*/
export interface ExposesObject {
/**
* Modules that should be exposed by this container.
*/
[k: string]: ExposesConfig | ExposesItem | ExposesItems;
}
/**
* Advanced configuration for modules that should be exposed by this container.
*/
export interface ExposesConfig {
/**
* Request to a module that should be exposed by this container.
*/
import: ExposesItem | ExposesItems;
}
/** /**
* Options for library. * Options for library.
*/ */
@ -144,3 +170,21 @@ export interface LibraryCustomUmdObject {
*/ */
root?: string[] | string; root?: string[] | string;
} }
/**
* Requests to modules in this container that should be able to be overridden by the host. Property names are used as override keys.
*/
export interface OverridablesObject {
/**
* Requests to modules in this container that should be able to be overridden by the host.
*/
[k: string]: OverridablesConfig | OverridablesItem | OverridablesItems;
}
/**
* Advanced configuration for modules in this container that should be able to be overridden by the host.
*/
export interface OverridablesConfig {
/**
* Requests to modules in this container that should be able to be overridden by the host.
*/
import: OverridablesItem | OverridablesItems;
}

View File

@ -7,12 +7,11 @@
/** /**
* Modules in this container that should override overridable modules in the remote container. When provided, property name is used as override key, otherwise override key is automatically inferred from request. * Modules in this container that should override overridable modules in the remote container. When provided, property name is used as override key, otherwise override key is automatically inferred from request.
*/ */
export type Overrides = export type Overrides = (OverridesItem | OverridesObject)[] | OverridesObject;
| Overrides[] /**
| string * Request to a module in this container that should override overridable modules in the remote container.
| { */
[k: string]: Overrides; export type OverridesItem = string;
};
/** /**
* Type of library. * Type of library.
*/ */
@ -36,12 +35,15 @@ export type LibraryType =
/** /**
* 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. * 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.
*/ */
export type Remotes = export type Remotes = (RemotesItem | RemotesObject)[] | RemotesObject;
| Remotes[] /**
| string * Container location from which modules should be resolved and loaded at runtime.
| { */
[k: string]: Remotes; export type RemotesItem = string;
}; /**
* Container locations from which modules should be resolved and loaded at runtime.
*/
export type RemotesItems = RemotesItem[];
export interface ContainerReferencePluginOptions { export interface ContainerReferencePluginOptions {
/** /**
@ -57,3 +59,39 @@ export interface ContainerReferencePluginOptions {
*/ */
remotes: Remotes; remotes: Remotes;
} }
/**
* Requests to modules in this container that should override overridable modules in the remote container. Property names are used as override keys.
*/
export interface OverridesObject {
/**
* Requests to modules in this container that should override overridable modules in the remote container.
*/
[k: string]: OverridesConfig | OverridesItem;
}
/**
* Advanced configuration for modules in this container that should override overridable modules in the remote container.
*/
export interface OverridesConfig {
/**
* Request to a module in this container that should override overridable modules in the remote container.
*/
import: OverridesItem;
}
/**
* Container locations from which modules should be resolved and loaded at runtime. Property names are used as request scopes.
*/
export interface RemotesObject {
/**
* Container locations from which modules should be resolved and loaded at runtime.
*/
[k: string]: RemotesConfig | RemotesItem | RemotesItems;
}
/**
* Advanced configuration for container locations from which modules should be resolved and loaded at runtime.
*/
export interface RemotesConfig {
/**
* Container locations from which modules should be resolved and loaded at runtime.
*/
external: RemotesItem | RemotesItems;
}

View File

@ -7,12 +7,15 @@
/** /**
* Modules that should be exposed by this container. When provided, property name is used as public name, otherwise public name is automatically inferred from request. * Modules that should be exposed by this container. When provided, property name is used as public name, otherwise public name is automatically inferred from request.
*/ */
export type Exposes = export type Exposes = (ExposesItem | ExposesObject)[] | ExposesObject;
| Exposes[] /**
| string * Module that should be exposed by this container.
| { */
[k: string]: Exposes; export type ExposesItem = string;
}; /**
* Modules that should be exposed by this container.
*/
export type ExposesItems = ExposesItem[];
/** /**
* Add a comment in the UMD wrapper. * Add a comment in the UMD wrapper.
*/ */
@ -49,24 +52,48 @@ export type LibraryType =
* If `output.libraryTarget` is set to umd and `output.library` is set, setting this to true will name the AMD module. * If `output.libraryTarget` is set to umd and `output.library` is set, setting this to true will name the AMD module.
*/ */
export type UmdNamedDefine = boolean; export type UmdNamedDefine = boolean;
/**
* Modules in this container that should be able to be overridden by the host. When provided, property name is used as override key, otherwise override key is automatically inferred from request.
*/
export type Overridables =
| (OverridablesItem | OverridablesObject)[]
| OverridablesObject;
/**
* Request to a module in this container that should be able to be overridden by the host.
*/
export type OverridablesItem = string;
/**
* Requests to modules in this container that should be able to be overridden by the host.
*/
export type OverridablesItems = OverridablesItem[];
/**
* Modules in this container that should override overridable modules in the remote container. When provided, property name is used as override key, otherwise override key is automatically inferred from request.
*/
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;
/** /**
* 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. * 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.
*/ */
export type Remotes = export type Remotes = (RemotesItem | RemotesObject)[] | RemotesObject;
| Remotes[] /**
| string * Container location from which modules should be resolved and loaded at runtime.
| { */
[k: string]: Remotes; export type RemotesItem = string;
}; /**
* Container locations from which modules should be resolved and loaded at runtime.
*/
export type RemotesItems = RemotesItem[];
/** /**
* Modules that should be shared with remotes and/or host. When provided, property name is used as shared key, otherwise shared key is automatically inferred from request. * Modules that should be shared with remotes and/or host. When provided, property name is used as shared key, otherwise shared key is automatically inferred from request.
*/ */
export type Shared = export type Shared = (SharedItem | SharedObject)[] | SharedObject;
| Shared[] /**
| string * Module that should be shared with remotes and/or host.
| { */
[k: string]: Shared; export type SharedItem = string;
};
export interface ModuleFederationPluginOptions { export interface ModuleFederationPluginOptions {
/** /**
@ -85,6 +112,14 @@ export interface ModuleFederationPluginOptions {
* The name of the container. * The name of the container.
*/ */
name?: string; name?: string;
/**
* Modules in this container that should be able to be overridden by the host. When provided, property name is used as override key, otherwise override key is automatically inferred from request.
*/
overridables?: Overridables;
/**
* Modules in this container that should override overridable modules in the remote container. When provided, property name is used as override key, otherwise override key is automatically inferred from request.
*/
overrides?: Overrides;
/** /**
* The external type of the remote containers. * The external type of the remote containers.
*/ */
@ -98,6 +133,24 @@ export interface ModuleFederationPluginOptions {
*/ */
shared?: Shared; shared?: Shared;
} }
/**
* Modules that should be exposed by this container. Property names are used as public paths.
*/
export interface ExposesObject {
/**
* Modules that should be exposed by this container.
*/
[k: string]: ExposesConfig | ExposesItem | ExposesItems;
}
/**
* Advanced configuration for modules that should be exposed by this container.
*/
export interface ExposesConfig {
/**
* Request to a module that should be exposed by this container.
*/
import: ExposesItem | ExposesItems;
}
/** /**
* Options for library. * Options for library.
*/ */
@ -161,3 +214,75 @@ export interface LibraryCustomUmdObject {
*/ */
root?: string[] | string; root?: string[] | string;
} }
/**
* Requests to modules in this container that should be able to be overridden by the host. Property names are used as override keys.
*/
export interface OverridablesObject {
/**
* Requests to modules in this container that should be able to be overridden by the host.
*/
[k: string]: OverridablesConfig | OverridablesItem | OverridablesItems;
}
/**
* Advanced configuration for modules in this container that should be able to be overridden by the host.
*/
export interface OverridablesConfig {
/**
* Requests to modules in this container that should be able to be overridden by the host.
*/
import: OverridablesItem | OverridablesItems;
}
/**
* Requests to modules in this container that should override overridable modules in the remote container. Property names are used as override keys.
*/
export interface OverridesObject {
/**
* Requests to modules in this container that should override overridable modules in the remote container.
*/
[k: string]: OverridesConfig | OverridesItem;
}
/**
* Advanced configuration for modules in this container that should override overridable modules in the remote container.
*/
export interface OverridesConfig {
/**
* Request to a module in this container that should override overridable modules in the remote container.
*/
import: OverridesItem;
}
/**
* Container locations from which modules should be resolved and loaded at runtime. Property names are used as request scopes.
*/
export interface RemotesObject {
/**
* Container locations from which modules should be resolved and loaded at runtime.
*/
[k: string]: RemotesConfig | RemotesItem | RemotesItems;
}
/**
* Advanced configuration for container locations from which modules should be resolved and loaded at runtime.
*/
export interface RemotesConfig {
/**
* Container locations from which modules should be resolved and loaded at runtime.
*/
external: RemotesItem | RemotesItems;
}
/**
* Modules that should be shared with remotes and/or host. Property names are used as shared keys.
*/
export interface SharedObject {
/**
* Modules that should be shared with remotes and/or host.
*/
[k: string]: SharedConfig | SharedItem;
}
/**
* Advanced configuration for modules that should be shared with remotes and/or host.
*/
export interface SharedConfig {
/**
* Module that should be shared with remotes and/or host.
*/
import: SharedItem;
}

View File

@ -4,12 +4,45 @@
* Run `yarn special-lint-fix` to update * Run `yarn special-lint-fix` to update
*/ */
/**
* Modules in this container that should be able to be overridden by the host. When provided, property name is used as override key, otherwise override key is automatically inferred from request.
*/
export type Overridables =
| (OverridablesItem | OverridablesObject)[]
| OverridablesObject;
/**
* Request to a module in this container that should be able to be overridden by the host.
*/
export type OverridablesItem = string;
/**
* Requests to modules in this container that should be able to be overridden by the host.
*/
export type OverridablesItems = OverridablesItem[];
/** /**
* Modules that should be able to be overridden. When provided, property name is used as override key, otherwise override key is automatically inferred from request. * Modules that should be able to be overridden. When provided, property name is used as override key, otherwise override key is automatically inferred from request.
*/ */
export type OverridablesPluginOptions = export interface OverridablesPluginOptions {
| OverridablesPluginOptions[] /**
| string * Modules in this container that should be able to be overridden by the host. When provided, property name is used as override key, otherwise override key is automatically inferred from request.
| { */
[k: string]: OverridablesPluginOptions; overridables?: Overridables;
}; }
/**
* Requests to modules in this container that should be able to be overridden by the host. Property names are used as override keys.
*/
export interface OverridablesObject {
/**
* Requests to modules in this container that should be able to be overridden by the host.
*/
[k: string]: OverridablesConfig | OverridablesItem | OverridablesItems;
}
/**
* Advanced configuration for modules in this container that should be able to be overridden by the host.
*/
export interface OverridablesConfig {
/**
* Requests to modules in this container that should be able to be overridden by the host.
*/
import: OverridablesItem | OverridablesItems;
}

View File

@ -8,10 +8,12 @@
const Dependency = require("../Dependency"); const Dependency = require("../Dependency");
const makeSerializable = require("../util/makeSerializable"); const makeSerializable = require("../util/makeSerializable");
/** @typedef {import("./ContainerEntryModule").ExposeOptions} ExposeOptions */
class ContainerEntryDependency extends Dependency { class ContainerEntryDependency extends Dependency {
/** /**
* @param {string} name entry name * @param {string} name entry name
* @param {[string, string][]} exposes list of exposed modules * @param {[string, ExposeOptions][]} exposes list of exposed modules
*/ */
constructor(name, exposes) { constructor(name, exposes) {
super(); super();

View File

@ -28,12 +28,17 @@ const ContainerExposedDependency = require("./ContainerExposedDependency");
/** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */ /** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */
/** @typedef {import("./ContainerEntryDependency")} ContainerEntryDependency */ /** @typedef {import("./ContainerEntryDependency")} ContainerEntryDependency */
/**
* @typedef {Object} ExposeOptions
* @property {string[]} import requests to exposed modules (last one is exported)
*/
const SOURCE_TYPES = new Set(["javascript"]); const SOURCE_TYPES = new Set(["javascript"]);
class ContainerEntryModule extends Module { class ContainerEntryModule extends Module {
/** /**
* @param {string} name container entry name * @param {string} name container entry name
* @param {[string, string][]} exposes list of exposed modules * @param {[string, ExposeOptions][]} exposes list of exposed modules
*/ */
constructor(name, exposes) { constructor(name, exposes) {
super("javascript/dynamic", null); super("javascript/dynamic", null);
@ -96,23 +101,22 @@ class ContainerEntryModule extends Module {
this.clearDependenciesAndBlocks(); this.clearDependenciesAndBlocks();
const dependencies = []; for (const [name, options] of this._exposes) {
const block = new AsyncDependenciesBlock(
let idx = -1; undefined,
for (const [name, request] of this._exposes) { { name },
++idx; options.import[options.import.length - 1]
);
let idx = 0;
for (const request of options.import) {
const dep = new ContainerExposedDependency(name, request); const dep = new ContainerExposedDependency(name, request);
dep.loc = { dep.loc = {
name, name,
index: idx index: idx++
}; };
dependencies.push(dep);
}
for (const dep of dependencies) {
const block = new AsyncDependenciesBlock(undefined, dep.loc, dep.request);
block.addDependency(dep); block.addDependency(dep);
}
this.addBlock(block); this.addBlock(block);
} }
@ -133,19 +137,22 @@ class ContainerEntryModule extends Module {
const getters = []; const getters = [];
for (const block of this.blocks) { for (const block of this.blocks) {
const { const { dependencies } = block;
dependencies: [dependency]
} = block; const modules = dependencies.map(dependency => {
const dep = /** @type {ContainerExposedDependency} */ (dependency); const dep = /** @type {ContainerExposedDependency} */ (dependency);
const name = dep.exposedName; return {
const mod = moduleGraph.getModule(dep); name: dep.exposedName,
const request = dep.userRequest; module: moduleGraph.getModule(dep),
request: dep.userRequest
};
});
let str; let str;
if (!mod) { if (modules.some(m => !m.module)) {
str = runtimeTemplate.throwMissingModuleErrorBlock({ str = runtimeTemplate.throwMissingModuleErrorBlock({
request: dep.userRequest request: modules.map(m => m.request).join(", ")
}); });
} else { } else {
str = `return ${runtimeTemplate.blockPromise({ str = `return ${runtimeTemplate.blockPromise({
@ -155,19 +162,26 @@ class ContainerEntryModule extends Module {
runtimeRequirements runtimeRequirements
})}.then(${runtimeTemplate.returningFunction( })}.then(${runtimeTemplate.returningFunction(
runtimeTemplate.returningFunction( runtimeTemplate.returningFunction(
`(${modules
.map(({ module, request }) =>
runtimeTemplate.moduleRaw({ runtimeTemplate.moduleRaw({
module: mod, module,
chunkGraph, chunkGraph,
request, request,
weak: false, weak: false,
runtimeRequirements runtimeRequirements
}) })
) )
.join(", ")})`
)
)});`; )});`;
} }
getters.push( getters.push(
`${JSON.stringify(name)}: ${runtimeTemplate.basicFunction("", str)}` `${JSON.stringify(modules[0].name)}: ${runtimeTemplate.basicFunction(
"",
str
)}`
); );
} }

View File

@ -9,6 +9,10 @@ const ModuleDependency = require("../dependencies/ModuleDependency");
const makeSerializable = require("../util/makeSerializable"); const makeSerializable = require("../util/makeSerializable");
class ContainerExposedDependency extends ModuleDependency { class ContainerExposedDependency extends ModuleDependency {
/**
* @param {string} exposedName public name
* @param {string} request request to module
*/
constructor(exposedName, request) { constructor(exposedName, request) {
super(request); super(request);
this.exposedName = exposedName; this.exposedName = exposedName;

View File

@ -11,14 +11,14 @@ const ContainerEntryDependency = require("./ContainerEntryDependency");
const ContainerEntryModuleFactory = require("./ContainerEntryModuleFactory"); const ContainerEntryModuleFactory = require("./ContainerEntryModuleFactory");
const ContainerExposedDependency = require("./ContainerExposedDependency"); const ContainerExposedDependency = require("./ContainerExposedDependency");
const OverridablesPlugin = require("./OverridablesPlugin"); const OverridablesPlugin = require("./OverridablesPlugin");
const parseOptions = require("./parseOptions"); const { parseOptions } = require("./options");
/** @typedef {import("../../declarations/plugins/container/ContainerPlugin").ContainerPluginOptions} ContainerPluginOptions */ /** @typedef {import("../../declarations/plugins/container/ContainerPlugin").ContainerPluginOptions} ContainerPluginOptions */
/** @typedef {import("../Compiler")} Compiler */ /** @typedef {import("../Compiler")} Compiler */
const PLUGIN_NAME = "ContainerPlugin"; const PLUGIN_NAME = "ContainerPlugin";
module.exports = class ContainerPlugin { class ContainerPlugin {
/** /**
* @param {ContainerPluginOptions} options options * @param {ContainerPluginOptions} options options
*/ */
@ -33,7 +33,15 @@ module.exports = class ContainerPlugin {
name: options.name name: options.name
}, },
filename: options.filename || undefined, filename: options.filename || undefined,
exposes: parseOptions(options.exposes) exposes: parseOptions(
options.exposes,
item => ({
import: Array.isArray(item) ? item : [item]
}),
item => ({
import: Array.isArray(item.import) ? item.import : [item.import]
})
)
}; };
} }
@ -47,7 +55,7 @@ module.exports = class ContainerPlugin {
compiler.options.output.enabledLibraryTypes.push(library.type); compiler.options.output.enabledLibraryTypes.push(library.type);
new OverridablesPlugin(overridables || []).apply(compiler); new OverridablesPlugin({ overridables }).apply(compiler);
compiler.hooks.make.tapAsync(PLUGIN_NAME, (compilation, callback) => { compiler.hooks.make.tapAsync(PLUGIN_NAME, (compilation, callback) => {
const dep = new ContainerEntryDependency(name, exposes); const dep = new ContainerEntryDependency(name, exposes);
@ -82,4 +90,6 @@ module.exports = class ContainerPlugin {
} }
); );
} }
}; }
module.exports = ContainerPlugin;

View File

@ -15,12 +15,14 @@ const RemoteOverridesDependency = require("./RemoteOverridesDependency");
const RemoteOverridesModuleFactory = require("./RemoteOverridesModuleFactory"); const RemoteOverridesModuleFactory = require("./RemoteOverridesModuleFactory");
const RemoteRuntimeModule = require("./RemoteRuntimeModule"); const RemoteRuntimeModule = require("./RemoteRuntimeModule");
const RemoteToExternalDependency = require("./RemoteToExternalDependency"); const RemoteToExternalDependency = require("./RemoteToExternalDependency");
const parseOptions = require("./parseOptions"); const { parseOptions } = require("./options");
/** @typedef {import("../../declarations/plugins/container/ContainerReferencePlugin").ContainerReferencePluginOptions} ContainerReferencePluginOptions */ /** @typedef {import("../../declarations/plugins/container/ContainerReferencePlugin").ContainerReferencePluginOptions} ContainerReferencePluginOptions */
/** @typedef {import("../../declarations/plugins/container/ContainerReferencePlugin").RemotesConfig} RemotesConfig */
/** @typedef {import("../Compiler")} Compiler */ /** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("./RemoteOverridesModule").OverrideOptions} OverrideOptions */
module.exports = class ContainerReferencePlugin { class ContainerReferencePlugin {
/** /**
* @param {ContainerReferencePluginOptions} options options * @param {ContainerReferencePluginOptions} options options
*/ */
@ -28,8 +30,30 @@ module.exports = class ContainerReferencePlugin {
validateOptions(schema, options, { name: "Container Reference Plugin" }); validateOptions(schema, options, { name: "Container Reference Plugin" });
this._remoteType = options.remoteType; this._remoteType = options.remoteType;
this._remotes = parseOptions(options.remotes || []); this._remotes = parseOptions(
this._overrides = parseOptions(options.overrides || {}).sort(([a], [b]) => { options.remotes,
item => ({
external: Array.isArray(item) ? item : [item]
}),
item => ({
external: Array.isArray(item.external) ? item.external : [item.external]
})
);
/** @type {[string, OverrideOptions][]} */
this._overrides = parseOptions(
options.overrides,
item => {
if (Array.isArray(item))
throw new Error("Unexpected array of overrides");
return {
import: item
};
},
item => ({
import: item.import
})
);
this._overrides.sort(([a], [b]) => {
if (a < b) return -1; if (a < b) return -1;
if (b < a) return 1; if (b < a) return 1;
return 0; return 0;
@ -44,9 +68,16 @@ module.exports = class ContainerReferencePlugin {
apply(compiler) { apply(compiler) {
const { _remotes: remotes, _remoteType: remoteType } = this; const { _remotes: remotes, _remoteType: remoteType } = this;
/** @type {Record<string, string>} */
const remoteExternals = {}; const remoteExternals = {};
for (const [key, value] of remotes) { for (const [key, config] of remotes) {
remoteExternals[`webpack/container/reference/${key}`] = value; let i = 0;
for (const external of config.external) {
remoteExternals[
`webpack/container/reference/${key}${i ? `/fallback-${i}` : ""}`
] = external;
i++;
}
} }
new ExternalsPlugin(remoteType, remoteExternals).apply(compiler); new ExternalsPlugin(remoteType, remoteExternals).apply(compiler);
@ -73,12 +104,17 @@ module.exports = class ContainerReferencePlugin {
"ContainerReferencePlugin", "ContainerReferencePlugin",
data => { data => {
if (!data.request.includes("!")) { if (!data.request.includes("!")) {
for (const [key] of remotes) { for (const [key, config] of remotes) {
if (data.request.startsWith(`${key}/`)) { if (data.request.startsWith(`${key}/`)) {
return new RemoteModule( return new RemoteModule(
data.request, data.request,
this._overrides, this._overrides,
`webpack/container/reference/${key}`, config.external.map(
(_, i) =>
`webpack/container/reference/${key}${
i ? `/fallback-${i}` : ""
}`
),
data.request.slice(key.length + 1) data.request.slice(key.length + 1)
); );
} }
@ -98,4 +134,6 @@ module.exports = class ContainerReferencePlugin {
} }
); );
} }
}; }
module.exports = ContainerReferencePlugin;

View File

@ -11,8 +11,34 @@ const ContainerPlugin = require("./ContainerPlugin");
const ContainerReferencePlugin = require("./ContainerReferencePlugin"); const ContainerReferencePlugin = require("./ContainerReferencePlugin");
/** @typedef {import("../../declarations/plugins/container/ModuleFederationPlugin").ModuleFederationPluginOptions} ModuleFederationPluginOptions */ /** @typedef {import("../../declarations/plugins/container/ModuleFederationPlugin").ModuleFederationPluginOptions} ModuleFederationPluginOptions */
/** @typedef {import("../../declarations/plugins/container/ModuleFederationPlugin").Shared} Shared */
/** @typedef {import("../Compiler")} Compiler */ /** @typedef {import("../Compiler")} Compiler */
/**
* @template T
* @template O
* @param {T[] | O} a first
* @param {T[] | O} b second
* @returns {(T | O)[] | O} merged
*/
const merge = (a, b) => {
if (!b) return a;
if (!a) return b;
if (Array.isArray(a)) {
if (Array.isArray(b)) {
return [...a, ...b];
} else {
return [...a, b];
}
} else {
if (Array.isArray(b)) {
return [a, ...b];
} else {
return [a, b];
}
}
};
class ModuleFederationPlugin { class ModuleFederationPlugin {
/** /**
* @param {ModuleFederationPluginOptions} options options * @param {ModuleFederationPluginOptions} options options
@ -50,7 +76,7 @@ class ModuleFederationPlugin {
library: options.library || compiler.options.output.library, library: options.library || compiler.options.output.library,
filename: options.filename, filename: options.filename,
exposes: options.exposes, exposes: options.exposes,
overridables: options.shared overridables: merge(options.shared, options.overridables)
}).apply(compiler); }).apply(compiler);
} }
if ( if (
@ -65,7 +91,7 @@ class ModuleFederationPlugin {
(options.library && options.library.type) || (options.library && options.library.type) ||
compiler.options.externalsType, compiler.options.externalsType,
remotes: options.remotes, remotes: options.remotes,
overrides: options.shared overrides: merge(options.shared, options.overrides)
}).apply(compiler); }).apply(compiler);
} }
}); });

View File

@ -27,25 +27,31 @@ const OverridableOriginalDependency = require("./OverridableOriginalDependency")
/** @typedef {import("../util/Hash")} Hash */ /** @typedef {import("../util/Hash")} Hash */
/** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */ /** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */
/**
* @typedef {Object} OverridableOptions
* @property {string} name global overridable name
*/
const TYPES = new Set(["overridable"]); const TYPES = new Set(["overridable"]);
class OverridableModule extends Module { class OverridableModule extends Module {
/** /**
* @param {string} context context * @param {string} context context
* @param {string} originalRequest original request * @param {string} originalRequest original request
* @param {string} name global overridable name * @param {OverridableOptions} options overridable options
*/ */
constructor(context, originalRequest, name) { constructor(context, originalRequest, options) {
super("overridable-module", context); super("overridable-module", context);
this.originalRequest = originalRequest; this.originalRequest = originalRequest;
this.name = name; this.options = options;
} }
/** /**
* @returns {string} a unique identifier of the module * @returns {string} a unique identifier of the module
*/ */
identifier() { identifier() {
return `overridable|${this.name}|${this.context}|${this.originalRequest}`; const { name } = this.options;
return `overridable|${name}|${this.context}|${this.originalRequest}`;
} }
/** /**
@ -53,7 +59,8 @@ class OverridableModule extends Module {
* @returns {string} a user readable identifier of the module * @returns {string} a user readable identifier of the module
*/ */
readableIdentifier(requestShortener) { readableIdentifier(requestShortener) {
return `overridable ${this.name} -> ${this.originalRequest}`; const { name } = this.options;
return `overridable ${name} -> ${this.originalRequest}`;
} }
/** /**
@ -61,7 +68,8 @@ class OverridableModule extends Module {
* @returns {string | null} an identifier for library inclusion * @returns {string | null} an identifier for library inclusion
*/ */
libIdent(options) { libIdent(options) {
return `webpack/container/overridable/${this.name}=${this.originalRequest}`; const { name } = this.options;
return `webpack/container/overridable/${name}=${this.originalRequest}`;
} }
/** /**
@ -112,7 +120,7 @@ class OverridableModule extends Module {
* @returns {void} * @returns {void}
*/ */
updateHash(hash, chunkGraph) { updateHash(hash, chunkGraph) {
hash.update(this.name); hash.update(JSON.stringify(this.options));
hash.update(this.originalRequest); hash.update(this.originalRequest);
super.updateHash(hash, chunkGraph); super.updateHash(hash, chunkGraph);
} }
@ -162,14 +170,14 @@ class OverridableModule extends Module {
serialize(context) { serialize(context) {
const { write } = context; const { write } = context;
write(this.originalRequest); write(this.originalRequest);
write(this.name); write(this.options);
super.serialize(context); super.serialize(context);
} }
deserialize(context) { deserialize(context) {
const { read } = context; const { read } = context;
this.originalRequest = read(); this.originalRequest = read();
this.name = read(); this.options = read();
super.deserialize(context); super.deserialize(context);
} }
} }

View File

@ -17,11 +17,13 @@ const LazySet = require("../util/LazySet");
const OverridableModule = require("./OverridableModule"); const OverridableModule = require("./OverridableModule");
const OverridableOriginalDependency = require("./OverridableOriginalDependency"); const OverridableOriginalDependency = require("./OverridableOriginalDependency");
const OverridablesRuntimeModule = require("./OverridablesRuntimeModule"); const OverridablesRuntimeModule = require("./OverridablesRuntimeModule");
const parseOptions = require("./parseOptions"); const { parseOptions } = require("./options");
/** @typedef {import("enhanced-resolve").ResolveContext} ResolveContext */ /** @typedef {import("enhanced-resolve").ResolveContext} ResolveContext */
/** @typedef {import("../../declarations/plugins/container/OverridablesPlugin").OverridablesConfig} OverridablesConfig */
/** @typedef {import("../../declarations/plugins/container/OverridablesPlugin").OverridablesPluginOptions} OverridablesPluginOptions */ /** @typedef {import("../../declarations/plugins/container/OverridablesPlugin").OverridablesPluginOptions} OverridablesPluginOptions */
/** @typedef {import("../Compiler")} Compiler */ /** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("./OverridableModule").OverridableOptions} OverridableOptions */
const PLUGIN_NAME = "OverridablesPlugin"; const PLUGIN_NAME = "OverridablesPlugin";
@ -34,7 +36,15 @@ class OverridablesPlugin {
validateOptions(schema, options, { name: "Overridables Plugin" }); validateOptions(schema, options, { name: "Overridables Plugin" });
} }
this._overridables = parseOptions(options); this._overridables = parseOptions(
options.overridables,
item => ({
import: Array.isArray(item) ? item : [item]
}),
item => ({
import: Array.isArray(item.import) ? item.import : [item.import]
})
);
} }
/** /**
@ -51,6 +61,7 @@ class OverridablesPlugin {
normalModuleFactory normalModuleFactory
); );
/** @type {Map<string, OverridableOptions>} */
const resolvedOverridables = new Map(); const resolvedOverridables = new Map();
const resolveContext = { const resolveContext = {
/** @type {LazySet<string>} */ /** @type {LazySet<string>} */
@ -60,10 +71,15 @@ class OverridablesPlugin {
/** @type {LazySet<string>} */ /** @type {LazySet<string>} */
missingDependencies: new LazySet() missingDependencies: new LazySet()
}; };
const promise = Promise.all(
this._overridables.map(([key, request]) => {
const resolver = compilation.resolverFactory.get("normal"); const resolver = compilation.resolverFactory.get("normal");
return new Promise((resolve, reject) => { /**
* @param {string} request imported request
* @param {string} name overridable name
* @param {OverridablesConfig} config config
* @returns {Promise<void>} promise
*/
const resolveOverridable = (request, name, config) => {
return new Promise(resolve => {
resolver.resolve( resolver.resolve(
{}, {},
compiler.context, compiler.context,
@ -73,16 +89,26 @@ class OverridablesPlugin {
if (err) { if (err) {
compilation.errors.push( compilation.errors.push(
new ModuleNotFoundError(null, err, { new ModuleNotFoundError(null, err, {
name: `overridable ${key}` name: `overridable ${name}`
}) })
); );
return resolve(); return resolve();
} }
resolvedOverridables.set(result, key); resolvedOverridables.set(result, {
name
});
resolve(); resolve();
} }
); );
}); });
};
const promise = Promise.all(
this._overridables.map(([name, config]) => {
return Promise.all(
config.import.map(request =>
resolveOverridable(request, name, config)
)
);
}) })
).then(() => { ).then(() => {
compilation.contextDependencies.addAll( compilation.contextDependencies.addAll(
@ -109,12 +135,12 @@ class OverridablesPlugin {
) { ) {
return; return;
} }
const key = resolvedOverridables.get(createData.resource); const options = resolvedOverridables.get(createData.resource);
if (key !== undefined) { if (options !== undefined) {
return new OverridableModule( return new OverridableModule(
resolveData.context, resolveData.context,
resolveData.request, resolveData.request,
key options
); );
} }
} }

View File

@ -41,7 +41,7 @@ class OverridablesRuntimeModule extends RuntimeModule {
const overridables = (chunkToOverridableMapping[chunk.id] = []); const overridables = (chunkToOverridableMapping[chunk.id] = []);
for (const m of modules) { for (const m of modules) {
const module = /** @type {OverridableModule} */ (m); const module = /** @type {OverridableModule} */ (m);
const name = module.name; const name = module.options.name;
const id = chunkGraph.getModuleId(module); const id = chunkGraph.getModuleId(module);
overridables.push(id); overridables.push(id);
idToNameMapping[id] = name; idToNameMapping[id] = name;
@ -60,7 +60,7 @@ class OverridablesRuntimeModule extends RuntimeModule {
if (!modules) continue; if (!modules) continue;
for (const m of modules) { for (const m of modules) {
const module = /** @type {OverridableModule} */ (m); const module = /** @type {OverridableModule} */ (m);
const name = module.name; const name = module.options.name;
const id = chunkGraph.getModuleId(module); const id = chunkGraph.getModuleId(module);
idToNameMapping[id] = name; idToNameMapping[id] = name;
const fallbackModule = moduleGraph.getModule( const fallbackModule = moduleGraph.getModule(

View File

@ -26,24 +26,33 @@ const RemoteToExternalDependency = require("./RemoteToExternalDependency");
/** @typedef {import("../WebpackError")} WebpackError */ /** @typedef {import("../WebpackError")} WebpackError */
/** @typedef {import("../util/Hash")} Hash */ /** @typedef {import("../util/Hash")} Hash */
/** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */ /** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */
/** @typedef {import("./RemoteOverridesModule").OverrideOptions} OverrideOptions */
const TYPES = new Set(["remote"]); const TYPES = new Set(["remote"]);
const RUNTIME_REQUIREMENTS = new Set([RuntimeGlobals.module]); const RUNTIME_REQUIREMENTS = new Set([RuntimeGlobals.module]);
class RemoteModule extends Module { class RemoteModule extends Module {
constructor(request, overrides, externalRequest, internalRequest) { /**
* @param {string} request request string
* @param {[string, OverrideOptions][]} overrides list of overrides
* @param {string[]} externalRequests list of external requests to containers
* @param {string} internalRequest name of exposed module in container
*/
constructor(request, overrides, externalRequests, internalRequest) {
super("remote-module"); super("remote-module");
this.request = request; this.request = request;
this.overrides = overrides; this.overrides = overrides;
this.externalRequest = externalRequest; this.externalRequests = externalRequests;
this.internalRequest = internalRequest; this.internalRequest = internalRequest;
const hash = createHash("md4"); const hash = createHash("md4");
for (const [key, request] of overrides) { for (const [key, request] of overrides) {
hash.update(key); hash.update(key);
hash.update(request); hash.update(request.import);
} }
this._overridesHash = hash.digest("hex"); this._overridesHash = hash.digest("hex");
this._identifier = `remote ${this.externalRequest} ${this.internalRequest} ${this._overridesHash}`; this._identifier = `remote ${this.externalRequests.join(" ")} ${
this.internalRequest
} ${this._overridesHash}`;
} }
/** /**
@ -93,8 +102,9 @@ class RemoteModule extends Module {
}; };
this.clearDependenciesAndBlocks(); this.clearDependenciesAndBlocks();
this.addDependency(new RemoteToExternalDependency(this.externalRequest));
this.addDependency(new RemoteOverridesDependency(this.overrides)); this.addDependency(new RemoteOverridesDependency(this.overrides));
for (const externalRequest of this.externalRequests)
this.addDependency(new RemoteToExternalDependency(externalRequest));
callback(); callback();
} }
@ -135,7 +145,7 @@ class RemoteModule extends Module {
const { write } = context; const { write } = context;
write(this.request); write(this.request);
write(this.overrides); write(this.overrides);
write(this.externalRequest); write(this.externalRequests);
write(this.internalRequest); write(this.internalRequest);
super.serialize(context); super.serialize(context);
} }
@ -144,7 +154,7 @@ class RemoteModule extends Module {
const { read } = context; const { read } = context;
this.request = read(); this.request = read();
this.overrides = read(); this.overrides = read();
this.externalRequest = read(); this.externalRequests = read();
this.internalRequest = read(); this.internalRequest = read();
super.deserialize(context); super.deserialize(context);
} }

View File

@ -8,7 +8,13 @@
const ModuleDependency = require("../dependencies/ModuleDependency"); const ModuleDependency = require("../dependencies/ModuleDependency");
const makeSerializable = require("../util/makeSerializable"); const makeSerializable = require("../util/makeSerializable");
/** @typedef {import("./RemoteOverridesModule").OverrideOptions} OverrideOptions */
class RemoteOverrideDependency extends ModuleDependency { class RemoteOverrideDependency extends ModuleDependency {
/**
*
* @param {string} request request string
*/
constructor(request) { constructor(request) {
super(request); super(request);
} }

View File

@ -29,9 +29,17 @@ const RemoteOverrideDependency = require("./RemoteOverrideDependency");
/** @typedef {import("../util/Hash")} Hash */ /** @typedef {import("../util/Hash")} Hash */
/** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */ /** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */
/**
* @typedef {Object} OverrideOptions
* @property {string} import request to new module
*/
const TYPES = new Set(["javascript"]); const TYPES = new Set(["javascript"]);
class RemoteOverridesModule extends Module { class RemoteOverridesModule extends Module {
/**
* @param {[string, OverrideOptions][]} overrides list of overrides
*/
constructor(overrides) { constructor(overrides) {
super("remote-overrides-module"); super("remote-overrides-module");
this._overrides = overrides; this._overrides = overrides;
@ -39,7 +47,7 @@ class RemoteOverridesModule extends Module {
const hash = createHash("md4"); const hash = createHash("md4");
for (const [key, request] of overrides) { for (const [key, request] of overrides) {
hash.update(key); hash.update(key);
hash.update(request); hash.update(request.import);
} }
this._overridesHash = hash.digest("hex"); this._overridesHash = hash.digest("hex");
} else { } else {
@ -99,9 +107,9 @@ class RemoteOverridesModule extends Module {
}; };
this.clearDependenciesAndBlocks(); this.clearDependenciesAndBlocks();
for (const [, value] of this._overrides) { for (const [, options] of this._overrides) {
const block = new AsyncDependenciesBlock({}); const block = new AsyncDependenciesBlock({});
const dep = new RemoteOverrideDependency(value); const dep = new RemoteOverrideDependency(options.import);
block.addDependency(dep); block.addDependency(dep);
this.addBlock(block); this.addBlock(block);
} }
@ -155,7 +163,7 @@ class RemoteOverridesModule extends Module {
"if(external.override) external.override(Object.assign({", "if(external.override) external.override(Object.assign({",
Template.indent( Template.indent(
this._overrides this._overrides
.map(([key, value]) => { .map(([key, options]) => {
const block = this.blocks[i++]; const block = this.blocks[i++];
const dep = block.dependencies[0]; const dep = block.dependencies[0];
const module = moduleGraph.getModule(dep); const module = moduleGraph.getModule(dep);
@ -174,7 +182,7 @@ class RemoteOverridesModule extends Module {
runtimeTemplate.moduleRaw({ runtimeTemplate.moduleRaw({
module, module,
chunkGraph, chunkGraph,
request: value, request: options.import,
runtimeRequirements runtimeRequirements
}) })
)}` )}`

View File

@ -21,6 +21,7 @@ class RemoteRuntimeModule extends RuntimeModule {
*/ */
generate() { generate() {
const { runtimeTemplate, chunkGraph, moduleGraph } = this.compilation; const { runtimeTemplate, chunkGraph, moduleGraph } = this.compilation;
let hasFallback = false;
const chunkToRemotesMapping = {}; const chunkToRemotesMapping = {};
const idToExternalAndNameMapping = {}; const idToExternalAndNameMapping = {};
for (const chunk of this.chunk.getAllAsyncChunks()) { for (const chunk of this.chunk.getAllAsyncChunks()) {
@ -34,17 +35,19 @@ class RemoteRuntimeModule extends RuntimeModule {
const module = /** @type {RemoteModule} */ (m); const module = /** @type {RemoteModule} */ (m);
const name = module.internalRequest; const name = module.internalRequest;
const id = chunkGraph.getModuleId(module); const id = chunkGraph.getModuleId(module);
const externalModule = moduleGraph.getModule(module.dependencies[0]); const overridesModule = moduleGraph.getModule(module.dependencies[0]);
const externalModuleId =
externalModule && chunkGraph.getModuleId(externalModule);
const overridesModule = moduleGraph.getModule(module.dependencies[1]);
const overridesModuleId = const overridesModuleId =
overridesModule && chunkGraph.getModuleId(overridesModule); overridesModule && chunkGraph.getModuleId(overridesModule);
const externalModuleIds = module.dependencies.slice(1).map(dep => {
const externalModule = moduleGraph.getModule(dep);
return externalModule && chunkGraph.getModuleId(externalModule);
});
if (externalModuleIds.length > 1) hasFallback = true;
remotes.push(id); remotes.push(id);
idToExternalAndNameMapping[id] = [ idToExternalAndNameMapping[id] = [
overridesModuleId, overridesModuleId,
externalModuleId, name,
name ...externalModuleIds
]; ];
} }
} }
@ -69,24 +72,28 @@ class RemoteRuntimeModule extends RuntimeModule {
`if(${RuntimeGlobals.hasOwnProperty}(installedModules, id)) return installedModules[id] && promises.push(installedModules[id]);`, `if(${RuntimeGlobals.hasOwnProperty}(installedModules, id)) return installedModules[id] && promises.push(installedModules[id]);`,
"var data = idToExternalAndNameMapping[id];", "var data = idToExternalAndNameMapping[id];",
`var onError = ${runtimeTemplate.basicFunction("error", [ `var onError = ${runtimeTemplate.basicFunction("error", [
"if(error && typeof error.message === \"string\") error.message += '\\nwhile loading \"' + data[2] + '\" from ' + data[1];", 'if(!error) error = new Error("Container missing");',
'if(typeof error.message === "string")',
Template.indent(
`error.message += '\\nwhile loading "' + data[1] + '" from ' + data[${
hasFallback ? "i" : "2"
}];`
),
`__webpack_modules__[id] = ${runtimeTemplate.basicFunction("", [ `__webpack_modules__[id] = ${runtimeTemplate.basicFunction("", [
"throw error;" "throw error;"
])}`, ])}`,
"delete installedModules[id];" "delete installedModules[id];"
])};`, ])};`,
`var onFactory = ${runtimeTemplate.basicFunction("factory", [ `var onExternal = ${runtimeTemplate.basicFunction(
`__webpack_modules__[id] = ${runtimeTemplate.basicFunction( "external, handlePromise",
"module", [
["module.exports = factory();"]
)}`
])};`,
"try {", "try {",
Template.indent([ Template.indent([
"var promise = __webpack_require__(data[0])(__webpack_require__(data[1])).get(data[2]);", "var promise = __webpack_require__(data[0])(external).get(data[1]);",
"if(promise && promise.then) {", "if(promise && promise.then) {",
Template.indent([ Template.indent([
`promises.push(installedModules[id] = promise.then(onFactory, onError));` "var p = promise.then(onFactory, onError);",
`if(handlePromise) promises.push(installedModules[id] = p); else return p;`
]), ]),
"} else {", "} else {",
Template.indent([`onFactory(promise);`]), Template.indent([`onFactory(promise);`]),
@ -95,6 +102,51 @@ class RemoteRuntimeModule extends RuntimeModule {
"} catch(error) {", "} catch(error) {",
Template.indent(["onError(error);"]), Template.indent(["onError(error);"]),
"}" "}"
]
)};`,
`var onFactory = ${runtimeTemplate.basicFunction("factory", [
"installedModules[id] = 0;",
`__webpack_modules__[id] = ${runtimeTemplate.basicFunction(
"module",
["module.exports = factory();"]
)}`
])};`,
hasFallback
? Template.asString([
"var i = 1, item, result;",
"(function next(error) {",
Template.indent([
"for(;;) {",
Template.indent([
"try {",
Template.indent([
"// Process with the next external in the data array",
"item = data[++i];",
"// Reached end of data, report the last error",
"if(!item) return onError(error);",
"result = __webpack_require__(item);",
"// Continue when receiving falsy value, otherwise handle promise or normal value",
"if(result) return result.then ? promises.push(installedModules[id] = result.then(onExternal, next)) : onExternal(result, 1);"
]),
"} catch(e) {",
Template.indent("error = e;"),
"}"
]),
"}"
]),
"})();"
])
: Template.asString([
"try {",
Template.indent([
"var promise =__webpack_require__(data[2]);",
"if(promise) return promise.then ? promises.push(installedModules[id] = promise.then(onExternal, onError)) : onExternal(promise, 1);",
"onError();"
]),
"} catch(error) {",
Template.indent(["onError(error);"]),
"}"
])
])});` ])});`
]), ]),
"}" "}"

90
lib/container/options.js Normal file
View File

@ -0,0 +1,90 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
/** @template T @typedef {(string | Record<string, string | string[] | T>)[] | Record<string, string | string[] | T>} ContainerOptionsFormat */
/**
* @template T
* @template N
* @param {ContainerOptionsFormat<T>} options options passed by the user
* @param {function(string | string[]) : N} normalizeSimple normalize a simple item
* @param {function(T) : N} normalizeOptions normalize a complex item
* @param {function(string, N): void} fn processing function
* @returns {void}
*/
const process = (options, normalizeSimple, normalizeOptions, fn) => {
const array = items => {
for (const item of items) {
if (typeof item === "string") {
fn(item.replace(/^([^\w]+\/)+/, ""), normalizeSimple(item));
} else if (item && typeof item === "object") {
object(item);
} else {
throw new Error("Unexpected options format");
}
}
};
const object = obj => {
for (const [key, value] of Object.entries(obj)) {
if (typeof value === "string" || Array.isArray(value)) {
fn(key, normalizeSimple(value));
} else {
fn(key, normalizeOptions(value));
}
}
};
if (!options) {
return;
} else if (Array.isArray(options)) {
array(options);
} else if (typeof options === "object") {
object(options);
} else {
throw new Error("Unexpected options format");
}
};
/**
* @template T
* @template R
* @param {ContainerOptionsFormat<T>} options options passed by the user
* @param {function(string | string[]) : R} normalizeSimple normalize a simple item
* @param {function(T) : R} normalizeOptions normalize a complex item
* @returns {[string, R][]} parsed options
*/
const parseOptions = (options, normalizeSimple, normalizeOptions) => {
/** @type {[string, R][]} */
const items = [];
process(options, normalizeSimple, normalizeOptions, (key, value) => {
items.push([key, value]);
});
return items;
};
/**
* @template T
* @param {string} scope scope name
* @param {ContainerOptionsFormat<T>} options options passed by the user
* @returns {Record<string, string | string[] | T>} options to spread or pass
*/
const scope = (scope, options) => {
const prefix = `${scope}/`;
/** @type {Record<string, string | string[] | T>} */
const obj = {};
process(
options,
item => /** @type {string | string[] | T} */ (item),
item => /** @type {string | string[] | T} */ (item),
(key, value) => {
obj[prefix + key] = value;
}
);
return obj;
};
exports.parseOptions = parseOptions;
exports.scope = scope;

View File

@ -1,37 +0,0 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const parseOptionsInternal = (prefix, options, result) => {
if (Array.isArray(options)) {
for (const item of options) {
parseOptionsInternal(prefix, item, result);
}
} else if (typeof options === "string") {
result.push([prefix + options.replace(/^([^\w]+\/)+/, ""), options]);
} else if (options && typeof options === "object") {
for (const key of Object.keys(options)) {
const value = options[key];
if (typeof value === "string") {
result.push([prefix + key, value]);
} else {
parseOptionsInternal(prefix + key + "/", value, result);
}
}
}
};
/**
* @param {TODO} options options passed by the user
* @returns {[string, string][]} parsed options
*/
const parseOptions = options => {
const result = [];
parseOptionsInternal("", options, result);
return result;
};
module.exports = parseOptions;

View File

@ -392,6 +392,9 @@ module.exports = mergeExports(fn, {
}, },
get OverridablesPlugin() { get OverridablesPlugin() {
return require("./container/OverridablesPlugin"); return require("./container/OverridablesPlugin");
},
get scope() {
return require("./container/options").scope;
} }
}, },

View File

@ -80,7 +80,7 @@
"strip-ansi": "^6.0.0", "strip-ansi": "^6.0.0",
"style-loader": "^1.0.0", "style-loader": "^1.0.0",
"toml": "^3.0.0", "toml": "^3.0.0",
"tooling": "webpack/tooling#v1.6.0", "tooling": "webpack/tooling#v1.7.0",
"ts-loader": "^6.0.4", "ts-loader": "^6.0.4",
"typescript": "^3.9.2", "typescript": "^3.9.2",
"url-loader": "^2.1.0", "url-loader": "^2.1.0",

View File

@ -0,0 +1,323 @@
{
"definitions": {
"Exposes": {
"description": "Modules that should be exposed by this container. When provided, property name is used as public name, otherwise public name is automatically inferred from request.",
"anyOf": [
{
"type": "array",
"items": {
"description": "Modules that should be exposed by this container.",
"anyOf": [
{
"$ref": "#/definitions/ExposesItem"
},
{
"$ref": "#/definitions/ExposesObject"
}
]
}
},
{
"$ref": "#/definitions/ExposesObject"
}
]
},
"ExposesConfig": {
"description": "Advanced configuration for modules that should be exposed by this container.",
"type": "object",
"additionalProperties": false,
"properties": {
"import": {
"description": "Request to a module that should be exposed by this container.",
"anyOf": [
{
"$ref": "#/definitions/ExposesItem"
},
{
"$ref": "#/definitions/ExposesItems"
}
]
}
},
"required": ["import"]
},
"ExposesItem": {
"description": "Module that should be exposed by this container.",
"type": "string",
"minLength": 1
},
"ExposesItems": {
"description": "Modules that should be exposed by this container.",
"type": "array",
"items": {
"$ref": "#/definitions/ExposesItem"
}
},
"ExposesObject": {
"description": "Modules that should be exposed by this container. Property names are used as public paths.",
"type": "object",
"additionalProperties": {
"description": "Modules that should be exposed by this container.",
"anyOf": [
{
"$ref": "#/definitions/ExposesConfig"
},
{
"$ref": "#/definitions/ExposesItem"
},
{
"$ref": "#/definitions/ExposesItems"
}
]
}
},
"Overridables": {
"description": "Modules in this container that should be able to be overridden by the host. When provided, property name is used as override key, otherwise override key is automatically inferred from request.",
"anyOf": [
{
"type": "array",
"items": {
"description": "Modules in this container that should be able to be overridden by the host.",
"anyOf": [
{
"$ref": "#/definitions/OverridablesItem"
},
{
"$ref": "#/definitions/OverridablesObject"
}
]
}
},
{
"$ref": "#/definitions/OverridablesObject"
}
]
},
"OverridablesConfig": {
"description": "Advanced configuration for modules in this container that should be able to be overridden by the host.",
"type": "object",
"additionalProperties": false,
"properties": {
"import": {
"description": "Requests to modules in this container that should be able to be overridden by the host.",
"anyOf": [
{
"$ref": "#/definitions/OverridablesItem"
},
{
"$ref": "#/definitions/OverridablesItems"
}
]
}
},
"required": ["import"]
},
"OverridablesItem": {
"description": "Request to a module in this container that should be able to be overridden by the host.",
"type": "string",
"minLength": 1
},
"OverridablesItems": {
"description": "Requests to modules in this container that should be able to be overridden by the host.",
"type": "array",
"items": {
"$ref": "#/definitions/OverridablesItem"
}
},
"OverridablesObject": {
"description": "Requests to modules in this container that should be able to be overridden by the host. Property names are used as override keys.",
"type": "object",
"additionalProperties": {
"description": "Requests to modules in this container that should be able to be overridden by the host.",
"anyOf": [
{
"$ref": "#/definitions/OverridablesConfig"
},
{
"$ref": "#/definitions/OverridablesItem"
},
{
"$ref": "#/definitions/OverridablesItems"
}
]
}
},
"Overrides": {
"description": "Modules in this container that should override overridable modules in the remote container. When provided, property name is used as override key, otherwise override key is automatically inferred from request.",
"anyOf": [
{
"type": "array",
"items": {
"description": "Modules in this container that should override overridable modules in the remote container.",
"anyOf": [
{
"$ref": "#/definitions/OverridesItem"
},
{
"$ref": "#/definitions/OverridesObject"
}
]
}
},
{
"$ref": "#/definitions/OverridesObject"
}
]
},
"OverridesConfig": {
"description": "Advanced configuration for modules in this container that should override overridable modules in the remote container.",
"type": "object",
"additionalProperties": false,
"properties": {
"import": {
"$ref": "#/definitions/OverridesItem"
}
},
"required": ["import"]
},
"OverridesItem": {
"description": "Request to a module in this container that should override overridable modules in the remote container.",
"type": "string",
"minLength": 1
},
"OverridesObject": {
"description": "Requests to modules in this container that should override overridable modules in the remote container. Property names are used as override keys.",
"type": "object",
"additionalProperties": {
"description": "Requests to modules in this container that should override overridable modules in the remote container.",
"anyOf": [
{
"$ref": "#/definitions/OverridesConfig"
},
{
"$ref": "#/definitions/OverridesItem"
}
]
}
},
"Remotes": {
"description": "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.",
"anyOf": [
{
"type": "array",
"items": {
"description": "Container locations and request scopes from which modules should be resolved and loaded at runtime.",
"anyOf": [
{
"$ref": "#/definitions/RemotesItem"
},
{
"$ref": "#/definitions/RemotesObject"
}
]
}
},
{
"$ref": "#/definitions/RemotesObject"
}
]
},
"RemotesConfig": {
"description": "Advanced configuration for container locations from which modules should be resolved and loaded at runtime.",
"type": "object",
"additionalProperties": false,
"properties": {
"external": {
"description": "Container locations from which modules should be resolved and loaded at runtime.",
"anyOf": [
{
"$ref": "#/definitions/RemotesItem"
},
{
"$ref": "#/definitions/RemotesItems"
}
]
}
},
"required": ["external"]
},
"RemotesItem": {
"description": "Container location from which modules should be resolved and loaded at runtime.",
"type": "string",
"minLength": 1
},
"RemotesItems": {
"description": "Container locations from which modules should be resolved and loaded at runtime.",
"type": "array",
"items": {
"$ref": "#/definitions/RemotesItem"
}
},
"RemotesObject": {
"description": "Container locations from which modules should be resolved and loaded at runtime. Property names are used as request scopes.",
"type": "object",
"additionalProperties": {
"description": "Container locations from which modules should be resolved and loaded at runtime.",
"anyOf": [
{
"$ref": "#/definitions/RemotesConfig"
},
{
"$ref": "#/definitions/RemotesItem"
},
{
"$ref": "#/definitions/RemotesItems"
}
]
}
},
"Shared": {
"description": "Modules that should be shared with remotes and/or host. When provided, property name is used as shared key, otherwise shared key is automatically inferred from request.",
"anyOf": [
{
"type": "array",
"items": {
"description": "Modules that should be shared with remotes and/or host.",
"anyOf": [
{
"$ref": "#/definitions/SharedItem"
},
{
"$ref": "#/definitions/SharedObject"
}
]
}
},
{
"$ref": "#/definitions/SharedObject"
}
]
},
"SharedConfig": {
"description": "Advanced configuration for modules that should be shared with remotes and/or host.",
"type": "object",
"additionalProperties": false,
"properties": {
"import": {
"$ref": "#/definitions/SharedItem"
}
},
"required": ["import"]
},
"SharedItem": {
"description": "Module that should be shared with remotes and/or host.",
"type": "string",
"minLength": 1
},
"SharedObject": {
"description": "Modules that should be shared with remotes and/or host. Property names are used as shared keys.",
"type": "object",
"additionalProperties": {
"description": "Modules that should be shared with remotes and/or host.",
"anyOf": [
{
"$ref": "#/definitions/SharedConfig"
},
{
"$ref": "#/definitions/SharedItem"
}
]
}
}
}
}

View File

@ -18,22 +18,70 @@
{ {
"type": "array", "type": "array",
"items": { "items": {
"$ref": "#/definitions/Exposes" "description": "Modules that should be exposed by this container.",
"anyOf": [
{
"$ref": "#/definitions/ExposesItem"
},
{
"$ref": "#/definitions/ExposesObject"
}
]
} }
}, },
{ {
"$ref": "#/definitions/ExposesObject"
}
]
},
"ExposesConfig": {
"description": "Advanced configuration for modules that should be exposed by this container.",
"type": "object",
"additionalProperties": false,
"properties": {
"import": {
"description": "Request to a module that should be exposed by this container.", "description": "Request to a module that should be exposed by this container.",
"anyOf": [
{
"$ref": "#/definitions/ExposesItem"
},
{
"$ref": "#/definitions/ExposesItems"
}
]
}
},
"required": ["import"]
},
"ExposesItem": {
"description": "Module that should be exposed by this container.",
"type": "string", "type": "string",
"minLength": 1 "minLength": 1
}, },
{ "ExposesItems": {
"description": "Requests to modules that should be exposed by this container. Property names are used as public names.", "description": "Modules that should be exposed by this container.",
"type": "array",
"items": {
"$ref": "#/definitions/ExposesItem"
}
},
"ExposesObject": {
"description": "Modules that should be exposed by this container. Property names are used as public paths.",
"type": "object", "type": "object",
"additionalProperties": { "additionalProperties": {
"$ref": "#/definitions/Exposes" "description": "Modules that should be exposed by this container.",
} "anyOf": [
{
"$ref": "#/definitions/ExposesConfig"
},
{
"$ref": "#/definitions/ExposesItem"
},
{
"$ref": "#/definitions/ExposesItems"
} }
] ]
}
}, },
"LibraryCustomUmdCommentObject": { "LibraryCustomUmdCommentObject": {
"description": "Set explicit comments for `commonjs`, `commonjs2`, `amd`, and `root`.", "description": "Set explicit comments for `commonjs`, `commonjs2`, `amd`, and `root`.",
@ -179,22 +227,70 @@
{ {
"type": "array", "type": "array",
"items": { "items": {
"$ref": "#/definitions/Overridables" "description": "Modules in this container that should be able to be overridden by the host.",
"anyOf": [
{
"$ref": "#/definitions/OverridablesItem"
},
{
"$ref": "#/definitions/OverridablesObject"
}
]
} }
}, },
{ {
"description": "Request to a module in this container that should be able to be overridden by the host. Override key is automatically inferred from request.", "$ref": "#/definitions/OverridablesObject"
}
]
},
"OverridablesConfig": {
"description": "Advanced configuration for modules in this container that should be able to be overridden by the host.",
"type": "object",
"additionalProperties": false,
"properties": {
"import": {
"description": "Requests to modules in this container that should be able to be overridden by the host.",
"anyOf": [
{
"$ref": "#/definitions/OverridablesItem"
},
{
"$ref": "#/definitions/OverridablesItems"
}
]
}
},
"required": ["import"]
},
"OverridablesItem": {
"description": "Request to a module in this container that should be able to be overridden by the host.",
"type": "string", "type": "string",
"minLength": 1 "minLength": 1
}, },
{ "OverridablesItems": {
"description": "Requests to modules in this container that should be able to be overridden by the host.",
"type": "array",
"items": {
"$ref": "#/definitions/OverridablesItem"
}
},
"OverridablesObject": {
"description": "Requests to modules in this container that should be able to be overridden by the host. Property names are used as override keys.", "description": "Requests to modules in this container that should be able to be overridden by the host. Property names are used as override keys.",
"type": "object", "type": "object",
"additionalProperties": { "additionalProperties": {
"$ref": "#/definitions/Overridables" "description": "Requests to modules in this container that should be able to be overridden by the host.",
} "anyOf": [
{
"$ref": "#/definitions/OverridablesConfig"
},
{
"$ref": "#/definitions/OverridablesItem"
},
{
"$ref": "#/definitions/OverridablesItems"
} }
] ]
}
}, },
"UmdNamedDefine": { "UmdNamedDefine": {
"description": "If `output.libraryTarget` is set to umd and `output.library` is set, setting this to true will name the AMD module.", "description": "If `output.libraryTarget` is set to umd and `output.library` is set, setting this to true will name the AMD module.",

View File

@ -27,22 +27,52 @@
{ {
"type": "array", "type": "array",
"items": { "items": {
"$ref": "#/definitions/Overrides" "description": "Modules in this container that should override overridable modules in the remote container.",
"anyOf": [
{
"$ref": "#/definitions/OverridesItem"
},
{
"$ref": "#/definitions/OverridesObject"
}
]
} }
}, },
{ {
"description": "Request to a module in this container that should override overridable modules in the remote container. Override key is automatically inferred from request.", "$ref": "#/definitions/OverridesObject"
}
]
},
"OverridesConfig": {
"description": "Advanced configuration for modules in this container that should override overridable modules in the remote container.",
"type": "object",
"additionalProperties": false,
"properties": {
"import": {
"$ref": "#/definitions/OverridesItem"
}
},
"required": ["import"]
},
"OverridesItem": {
"description": "Request to a module in this container that should override overridable modules in the remote container.",
"type": "string", "type": "string",
"minLength": 1 "minLength": 1
}, },
{ "OverridesObject": {
"description": "Request to a module in this container that should override overridable modules in the remote container. Property names are used as override keys.", "description": "Requests to modules in this container that should override overridable modules in the remote container. Property names are used as override keys.",
"type": "object", "type": "object",
"additionalProperties": { "additionalProperties": {
"$ref": "#/definitions/Overrides" "description": "Requests to modules in this container that should override overridable modules in the remote container.",
} "anyOf": [
{
"$ref": "#/definitions/OverridesConfig"
},
{
"$ref": "#/definitions/OverridesItem"
} }
] ]
}
}, },
"Remotes": { "Remotes": {
"description": "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.", "description": "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.",
@ -50,23 +80,71 @@
{ {
"type": "array", "type": "array",
"items": { "items": {
"$ref": "#/definitions/Remotes" "description": "Container locations and request scopes from which modules should be resolved and loaded at runtime.",
"anyOf": [
{
"$ref": "#/definitions/RemotesItem"
},
{
"$ref": "#/definitions/RemotesObject"
}
]
} }
}, },
{ {
"$ref": "#/definitions/RemotesObject"
}
]
},
"RemotesConfig": {
"description": "Advanced configuration for container locations from which modules should be resolved and loaded at runtime.",
"type": "object",
"additionalProperties": false,
"properties": {
"external": {
"description": "Container locations from which modules should be resolved and loaded at runtime.",
"anyOf": [
{
"$ref": "#/definitions/RemotesItem"
},
{
"$ref": "#/definitions/RemotesItems"
}
]
}
},
"required": ["external"]
},
"RemotesItem": {
"description": "Container location from which modules should be resolved and loaded at runtime.", "description": "Container location from which modules should be resolved and loaded at runtime.",
"type": "string", "type": "string",
"minLength": 1 "minLength": 1
}, },
{ "RemotesItems": {
"description": "Container locations from which modules should be resolved and loaded at runtime.",
"type": "array",
"items": {
"$ref": "#/definitions/RemotesItem"
}
},
"RemotesObject": {
"description": "Container locations from which modules should be resolved and loaded at runtime. Property names are used as request scopes.", "description": "Container locations from which modules should be resolved and loaded at runtime. Property names are used as request scopes.",
"type": "object", "type": "object",
"additionalProperties": { "additionalProperties": {
"$ref": "#/definitions/Remotes" "description": "Container locations from which modules should be resolved and loaded at runtime.",
} "anyOf": [
{
"$ref": "#/definitions/RemotesConfig"
},
{
"$ref": "#/definitions/RemotesItem"
},
{
"$ref": "#/definitions/RemotesItems"
} }
] ]
} }
}
}, },
"title": "ContainerReferencePluginOptions", "title": "ContainerReferencePluginOptions",
"type": "object", "type": "object",

View File

@ -18,22 +18,70 @@
{ {
"type": "array", "type": "array",
"items": { "items": {
"$ref": "#/definitions/Exposes" "description": "Modules that should be exposed by this container.",
"anyOf": [
{
"$ref": "#/definitions/ExposesItem"
},
{
"$ref": "#/definitions/ExposesObject"
}
]
} }
}, },
{ {
"description": "Request to a module that should be exposed by this container. Public name is automatically inferred from request.", "$ref": "#/definitions/ExposesObject"
}
]
},
"ExposesConfig": {
"description": "Advanced configuration for modules that should be exposed by this container.",
"type": "object",
"additionalProperties": false,
"properties": {
"import": {
"description": "Request to a module that should be exposed by this container.",
"anyOf": [
{
"$ref": "#/definitions/ExposesItem"
},
{
"$ref": "#/definitions/ExposesItems"
}
]
}
},
"required": ["import"]
},
"ExposesItem": {
"description": "Module that should be exposed by this container.",
"type": "string", "type": "string",
"minLength": 1 "minLength": 1
}, },
{ "ExposesItems": {
"description": "Requests to modules that should be exposed by this container. Property names are used as public names.", "description": "Modules that should be exposed by this container.",
"type": "array",
"items": {
"$ref": "#/definitions/ExposesItem"
}
},
"ExposesObject": {
"description": "Modules that should be exposed by this container. Property names are used as public paths.",
"type": "object", "type": "object",
"additionalProperties": { "additionalProperties": {
"$ref": "#/definitions/Exposes" "description": "Modules that should be exposed by this container.",
} "anyOf": [
{
"$ref": "#/definitions/ExposesConfig"
},
{
"$ref": "#/definitions/ExposesItem"
},
{
"$ref": "#/definitions/ExposesItems"
} }
] ]
}
}, },
"LibraryCustomUmdCommentObject": { "LibraryCustomUmdCommentObject": {
"description": "Set explicit comments for `commonjs`, `commonjs2`, `amd`, and `root`.", "description": "Set explicit comments for `commonjs`, `commonjs2`, `amd`, and `root`.",
@ -173,28 +221,200 @@
"system" "system"
] ]
}, },
"Overridables": {
"description": "Modules in this container that should be able to be overridden by the host. When provided, property name is used as override key, otherwise override key is automatically inferred from request.",
"anyOf": [
{
"type": "array",
"items": {
"description": "Modules in this container that should be able to be overridden by the host.",
"anyOf": [
{
"$ref": "#/definitions/OverridablesItem"
},
{
"$ref": "#/definitions/OverridablesObject"
}
]
}
},
{
"$ref": "#/definitions/OverridablesObject"
}
]
},
"OverridablesConfig": {
"description": "Advanced configuration for modules in this container that should be able to be overridden by the host.",
"type": "object",
"additionalProperties": false,
"properties": {
"import": {
"description": "Requests to modules in this container that should be able to be overridden by the host.",
"anyOf": [
{
"$ref": "#/definitions/OverridablesItem"
},
{
"$ref": "#/definitions/OverridablesItems"
}
]
}
},
"required": ["import"]
},
"OverridablesItem": {
"description": "Request to a module in this container that should be able to be overridden by the host.",
"type": "string",
"minLength": 1
},
"OverridablesItems": {
"description": "Requests to modules in this container that should be able to be overridden by the host.",
"type": "array",
"items": {
"$ref": "#/definitions/OverridablesItem"
}
},
"OverridablesObject": {
"description": "Requests to modules in this container that should be able to be overridden by the host. Property names are used as override keys.",
"type": "object",
"additionalProperties": {
"description": "Requests to modules in this container that should be able to be overridden by the host.",
"anyOf": [
{
"$ref": "#/definitions/OverridablesConfig"
},
{
"$ref": "#/definitions/OverridablesItem"
},
{
"$ref": "#/definitions/OverridablesItems"
}
]
}
},
"Overrides": {
"description": "Modules in this container that should override overridable modules in the remote container. When provided, property name is used as override key, otherwise override key is automatically inferred from request.",
"anyOf": [
{
"type": "array",
"items": {
"description": "Modules in this container that should override overridable modules in the remote container.",
"anyOf": [
{
"$ref": "#/definitions/OverridesItem"
},
{
"$ref": "#/definitions/OverridesObject"
}
]
}
},
{
"$ref": "#/definitions/OverridesObject"
}
]
},
"OverridesConfig": {
"description": "Advanced configuration for modules in this container that should override overridable modules in the remote container.",
"type": "object",
"additionalProperties": false,
"properties": {
"import": {
"$ref": "#/definitions/OverridesItem"
}
},
"required": ["import"]
},
"OverridesItem": {
"description": "Request to a module in this container that should override overridable modules in the remote container.",
"type": "string",
"minLength": 1
},
"OverridesObject": {
"description": "Requests to modules in this container that should override overridable modules in the remote container. Property names are used as override keys.",
"type": "object",
"additionalProperties": {
"description": "Requests to modules in this container that should override overridable modules in the remote container.",
"anyOf": [
{
"$ref": "#/definitions/OverridesConfig"
},
{
"$ref": "#/definitions/OverridesItem"
}
]
}
},
"Remotes": { "Remotes": {
"description": "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.", "description": "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.",
"anyOf": [ "anyOf": [
{ {
"type": "array", "type": "array",
"items": { "items": {
"$ref": "#/definitions/Remotes" "description": "Container locations and request scopes from which modules should be resolved and loaded at runtime.",
"anyOf": [
{
"$ref": "#/definitions/RemotesItem"
},
{
"$ref": "#/definitions/RemotesObject"
}
]
} }
}, },
{ {
"$ref": "#/definitions/RemotesObject"
}
]
},
"RemotesConfig": {
"description": "Advanced configuration for container locations from which modules should be resolved and loaded at runtime.",
"type": "object",
"additionalProperties": false,
"properties": {
"external": {
"description": "Container locations from which modules should be resolved and loaded at runtime.",
"anyOf": [
{
"$ref": "#/definitions/RemotesItem"
},
{
"$ref": "#/definitions/RemotesItems"
}
]
}
},
"required": ["external"]
},
"RemotesItem": {
"description": "Container location from which modules should be resolved and loaded at runtime.", "description": "Container location from which modules should be resolved and loaded at runtime.",
"type": "string", "type": "string",
"minLength": 1 "minLength": 1
}, },
{ "RemotesItems": {
"description": "Container locations from which modules should be resolved and loaded at runtime.",
"type": "array",
"items": {
"$ref": "#/definitions/RemotesItem"
}
},
"RemotesObject": {
"description": "Container locations from which modules should be resolved and loaded at runtime. Property names are used as request scopes.", "description": "Container locations from which modules should be resolved and loaded at runtime. Property names are used as request scopes.",
"type": "object", "type": "object",
"additionalProperties": { "additionalProperties": {
"$ref": "#/definitions/Remotes" "description": "Container locations from which modules should be resolved and loaded at runtime.",
} "anyOf": [
{
"$ref": "#/definitions/RemotesConfig"
},
{
"$ref": "#/definitions/RemotesItem"
},
{
"$ref": "#/definitions/RemotesItems"
} }
] ]
}
}, },
"Shared": { "Shared": {
"description": "Modules that should be shared with remotes and/or host. When provided, property name is used as shared key, otherwise shared key is automatically inferred from request.", "description": "Modules that should be shared with remotes and/or host. When provided, property name is used as shared key, otherwise shared key is automatically inferred from request.",
@ -202,22 +422,52 @@
{ {
"type": "array", "type": "array",
"items": { "items": {
"$ref": "#/definitions/Shared" "description": "Modules that should be shared with remotes and/or host.",
"anyOf": [
{
"$ref": "#/definitions/SharedItem"
},
{
"$ref": "#/definitions/SharedObject"
}
]
} }
}, },
{ {
"description": "Request to a module that should be shared with remotes and/or host.", "$ref": "#/definitions/SharedObject"
}
]
},
"SharedConfig": {
"description": "Advanced configuration for modules that should be shared with remotes and/or host.",
"type": "object",
"additionalProperties": false,
"properties": {
"import": {
"$ref": "#/definitions/SharedItem"
}
},
"required": ["import"]
},
"SharedItem": {
"description": "Module that should be shared with remotes and/or host.",
"type": "string", "type": "string",
"minLength": 1 "minLength": 1
}, },
{ "SharedObject": {
"description": "Requests to modules that should be shared with remotes and/or host. Property names are used as shared keys.", "description": "Modules that should be shared with remotes and/or host. Property names are used as shared keys.",
"type": "object", "type": "object",
"additionalProperties": { "additionalProperties": {
"$ref": "#/definitions/Shared" "description": "Modules that should be shared with remotes and/or host.",
} "anyOf": [
{
"$ref": "#/definitions/SharedConfig"
},
{
"$ref": "#/definitions/SharedItem"
} }
] ]
}
}, },
"UmdNamedDefine": { "UmdNamedDefine": {
"description": "If `output.libraryTarget` is set to umd and `output.library` is set, setting this to true will name the AMD module.", "description": "If `output.libraryTarget` is set to umd and `output.library` is set, setting this to true will name the AMD module.",
@ -243,6 +493,12 @@
"description": "The name of the container.", "description": "The name of the container.",
"type": "string" "type": "string"
}, },
"overridables": {
"$ref": "#/definitions/Overridables"
},
"overrides": {
"$ref": "#/definitions/Overrides"
},
"remoteType": { "remoteType": {
"description": "The external type of the remote containers.", "description": "The external type of the remote containers.",
"oneOf": [ "oneOf": [

View File

@ -1,24 +1,84 @@
{ {
"title": "OverridablesPluginOptions", "definitions": {
"description": "Modules that should be able to be overridden. When provided, property name is used as override key, otherwise override key is automatically inferred from request.", "Overridables": {
"description": "Modules in this container that should be able to be overridden by the host. When provided, property name is used as override key, otherwise override key is automatically inferred from request.",
"anyOf": [ "anyOf": [
{ {
"type": "array", "type": "array",
"items": { "items": {
"$ref": "#" "description": "Modules in this container that should be able to be overridden by the host.",
"anyOf": [
{
"$ref": "#/definitions/OverridablesItem"
},
{
"$ref": "#/definitions/OverridablesObject"
}
]
} }
}, },
{ {
"description": "Request to a module that should be able to be overridden.", "$ref": "#/definitions/OverridablesObject"
}
]
},
"OverridablesConfig": {
"description": "Advanced configuration for modules in this container that should be able to be overridden by the host.",
"type": "object",
"additionalProperties": false,
"properties": {
"import": {
"description": "Requests to modules in this container that should be able to be overridden by the host.",
"anyOf": [
{
"$ref": "#/definitions/OverridablesItem"
},
{
"$ref": "#/definitions/OverridablesItems"
}
]
}
},
"required": ["import"]
},
"OverridablesItem": {
"description": "Request to a module in this container that should be able to be overridden by the host.",
"type": "string", "type": "string",
"minLength": 1 "minLength": 1
}, },
{ "OverridablesItems": {
"description": "Requests to modules that should be able to be overridden. Property names are used as override keys.", "description": "Requests to modules in this container that should be able to be overridden by the host.",
"type": "array",
"items": {
"$ref": "#/definitions/OverridablesItem"
}
},
"OverridablesObject": {
"description": "Requests to modules in this container that should be able to be overridden by the host. Property names are used as override keys.",
"type": "object", "type": "object",
"additionalProperties": { "additionalProperties": {
"$ref": "#" "description": "Requests to modules in this container that should be able to be overridden by the host.",
} "anyOf": [
{
"$ref": "#/definitions/OverridablesConfig"
},
{
"$ref": "#/definitions/OverridablesItem"
},
{
"$ref": "#/definitions/OverridablesItems"
} }
] ]
}
}
},
"title": "OverridablesPluginOptions",
"description": "Modules that should be able to be overridden. When provided, property name is used as override key, otherwise override key is automatically inferred from request.",
"type": "object",
"additionalProperties": false,
"properties": {
"overridables": {
"$ref": "#/definitions/Overridables"
}
}
} }

View File

@ -1,29 +1,30 @@
const { OverridablesPlugin } = require("../../../../").container; const { OverridablesPlugin, scope } = require("../../../../").container;
/** @type {import("../../../../").Configuration} */ /** @type {import("../../../../").Configuration} */
module.exports = { module.exports = {
plugins: [ plugins: [
new OverridablesPlugin([ new OverridablesPlugin({
overridables: [
{ {
test1: "./modules/test1.js", test1: ["./modules/test1.js", "./cjs/test1"],
test2: "./modules/test2", test2: "./modules/test2",
test3: "./modules/test3" test3: "./modules/test3"
}, },
{ {
test1: "./cjs/test1",
test2: "./cjs/test2.js", test2: "./cjs/test2.js",
test3: "./cjs/../cjs/test3.js", test3: "./cjs/../cjs/test3.js",
nested1: ["./options/test2"], ...scope("nested1", ["./options/test2"]),
nested2: { ...scope("nested2", {
deep: { ...scope("deep", {
deep: "./options/test3" deep: "./options/test3"
} })
} })
}, },
"package", "package",
"././options/test1", "././options/test1",
"./splitChunks/app" "./splitChunks/app"
]) ]
})
], ],
optimization: { optimization: {
splitChunks: { splitChunks: {

147
types.d.ts vendored
View File

@ -1459,7 +1459,7 @@ declare interface ContainerPluginOptions {
/** /**
* Modules that should be exposed by this container. When provided, property name is used as public name, otherwise public name is automatically inferred from request. * Modules that should be exposed by this container. When provided, property name is used as public name, otherwise public name is automatically inferred from request.
*/ */
exposes: ExposesContainerPlugin; exposes: Exposes;
/** /**
* The filename for this container relative path inside the `output.path` directory. * The filename for this container relative path inside the `output.path` directory.
@ -1503,7 +1503,7 @@ declare interface ContainerReferencePluginOptions {
/** /**
* 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. * 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.
*/ */
remotes: RemotesContainerReferencePlugin; remotes: Remotes;
} }
declare class ContextExclusionPlugin { declare class ContextExclusionPlugin {
constructor(negativeMatcher: RegExp); constructor(negativeMatcher: RegExp);
@ -2253,14 +2253,24 @@ declare interface ExportsSpec {
*/ */
dependencies?: Module[]; dependencies?: Module[];
} }
type ExposesContainerPlugin = type Exposes = (string | ExposesObject)[] | ExposesObject;
| string
| ExposesContainerPlugin[] /**
| { [index: string]: ExposesContainerPlugin }; * Advanced configuration for modules that should be exposed by this container.
type ExposesModuleFederationPlugin = */
| string declare interface ExposesConfig {
| ExposesModuleFederationPlugin[] /**
| { [index: string]: ExposesModuleFederationPlugin }; * Request to a module that should be exposed by this container.
*/
import: string | string[];
}
/**
* Modules that should be exposed by this container. Property names are used as public paths.
*/
declare interface ExposesObject {
[index: string]: string | ExposesConfig | string[];
}
type Expression = type Expression =
| UnaryExpression | UnaryExpression
| ThisExpression | ThisExpression
@ -3699,7 +3709,7 @@ declare interface ModuleFederationPluginOptions {
/** /**
* Modules that should be exposed by this container. When provided, property name is used as public name, otherwise public name is automatically inferred from request. * Modules that should be exposed by this container. When provided, property name is used as public name, otherwise public name is automatically inferred from request.
*/ */
exposes?: ExposesModuleFederationPlugin; exposes?: Exposes;
/** /**
* The filename of the container as relative path inside the `output.path` directory. * The filename of the container as relative path inside the `output.path` directory.
@ -3716,6 +3726,16 @@ declare interface ModuleFederationPluginOptions {
*/ */
name?: string; name?: string;
/**
* Modules in this container that should be able to be overridden by the host. When provided, property name is used as override key, otherwise override key is automatically inferred from request.
*/
overridables?: Overridables;
/**
* Modules in this container that should override overridable modules in the remote container. When provided, property name is used as override key, otherwise override key is automatically inferred from request.
*/
overrides?: Overrides;
/** /**
* The external type of the remote containers. * The external type of the remote containers.
*/ */
@ -3724,7 +3744,7 @@ declare interface ModuleFederationPluginOptions {
/** /**
* 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. * 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.
*/ */
remotes?: RemotesModuleFederationPlugin; remotes?: Remotes;
/** /**
* Modules that should be shared with remotes and/or host. When provided, property name is used as shared key, otherwise shared key is automatically inferred from request. * Modules that should be shared with remotes and/or host. When provided, property name is used as shared key, otherwise shared key is automatically inferred from request.
@ -5002,7 +5022,24 @@ declare interface OutputNormalized {
*/ */
webassemblyModuleFilename?: string; webassemblyModuleFilename?: string;
} }
type Overridables = string | Overridables[] | { [index: string]: Overridables }; type Overridables = (string | OverridablesObject)[] | OverridablesObject;
/**
* Advanced configuration for modules in this container that should be able to be overridden by the host.
*/
declare interface OverridablesConfig {
/**
* Requests to modules in this container that should be able to be overridden by the host.
*/
import: string | string[];
}
/**
* Requests to modules in this container that should be able to be overridden by the host. Property names are used as override keys.
*/
declare interface OverridablesObject {
[index: string]: string | OverridablesConfig | string[];
}
declare class OverridablesPlugin { declare class OverridablesPlugin {
constructor(options: OverridablesPluginOptions); constructor(options: OverridablesPluginOptions);
@ -5011,11 +5048,34 @@ declare class OverridablesPlugin {
*/ */
apply(compiler: Compiler): void; apply(compiler: Compiler): void;
} }
type OverridablesPluginOptions =
| string /**
| OverridablesPluginOptions[] * Modules that should be able to be overridden. When provided, property name is used as override key, otherwise override key is automatically inferred from request.
| { [index: string]: OverridablesPluginOptions }; */
type Overrides = string | Overrides[] | { [index: string]: Overrides }; declare interface OverridablesPluginOptions {
/**
* Modules in this container that should be able to be overridden by the host. When provided, property name is used as override key, otherwise override key is automatically inferred from request.
*/
overridables?: Overridables;
}
type Overrides = (string | OverridesObject)[] | OverridesObject;
/**
* Advanced configuration for modules in this container that should override overridable modules in the remote container.
*/
declare interface OverridesConfig {
/**
* Request to a module in this container that should override overridable modules in the remote container.
*/
import: string;
}
/**
* Requests to modules in this container that should override overridable modules in the remote container. Property names are used as override keys.
*/
declare interface OverridesObject {
[index: string]: string | OverridesConfig;
}
declare class Parser { declare class Parser {
constructor(); constructor();
parse( parse(
@ -5236,14 +5296,24 @@ type RecursiveArrayOrRecord =
| RuntimeValue | RuntimeValue
| { [index: string]: RecursiveArrayOrRecord } | { [index: string]: RecursiveArrayOrRecord }
| RecursiveArrayOrRecord[]; | RecursiveArrayOrRecord[];
type RemotesContainerReferencePlugin = type Remotes = (string | RemotesObject)[] | RemotesObject;
| string
| RemotesContainerReferencePlugin[] /**
| { [index: string]: RemotesContainerReferencePlugin }; * Advanced configuration for container locations from which modules should be resolved and loaded at runtime.
type RemotesModuleFederationPlugin = */
| string declare interface RemotesConfig {
| RemotesModuleFederationPlugin[] /**
| { [index: string]: RemotesModuleFederationPlugin }; * Container locations from which modules should be resolved and loaded at runtime.
*/
external: string | string[];
}
/**
* Container locations from which modules should be resolved and loaded at runtime. Property names are used as request scopes.
*/
declare interface RemotesObject {
[index: string]: string | RemotesConfig | string[];
}
declare interface RenderBootstrapContext { declare interface RenderBootstrapContext {
/** /**
* the chunk * the chunk
@ -6139,7 +6209,24 @@ declare abstract class Serializer {
serialize(obj?: any, context?: any): any; serialize(obj?: any, context?: any): any;
deserialize(value?: any, context?: any): any; deserialize(value?: any, context?: any): any;
} }
type Shared = string | Shared[] | { [index: string]: Shared }; type Shared = (string | SharedObject)[] | SharedObject;
/**
* Advanced configuration for modules that should be shared with remotes and/or host.
*/
declare interface SharedConfig {
/**
* Module that should be shared with remotes and/or host.
*/
import: string;
}
/**
* Modules that should be shared with remotes and/or host. Property names are used as shared keys.
*/
declare interface SharedObject {
[index: string]: string | SharedConfig;
}
declare class SideEffectsFlagPlugin { declare class SideEffectsFlagPlugin {
constructor(); constructor();
@ -7308,6 +7395,12 @@ declare namespace exports {
export { AbstractLibraryPlugin, EnableLibraryPlugin }; export { AbstractLibraryPlugin, EnableLibraryPlugin };
} }
export namespace container { export namespace container {
export const scope: <T>(
scope: string,
options:
| Record<string, string | string[] | T>
| (string | Record<string, string | string[] | T>)[]
) => Record<string, string | string[] | T>;
export { export {
ContainerPlugin, ContainerPlugin,
ContainerReferencePlugin, ContainerReferencePlugin,

View File

@ -6744,9 +6744,9 @@ toml@^3.0.0:
resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee" resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee"
integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w== integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==
tooling@webpack/tooling#v1.6.0: tooling@webpack/tooling#v1.7.0:
version "1.6.0" version "1.7.0"
resolved "https://codeload.github.com/webpack/tooling/tar.gz/a42d42ad589daf63b30cfc4c035c4bed93f4027b" resolved "https://codeload.github.com/webpack/tooling/tar.gz/199a510b8e4307e5b6629b11cb2ef377d183e5cf"
dependencies: dependencies:
"@yarnpkg/lockfile" "^1.1.0" "@yarnpkg/lockfile" "^1.1.0"
commondir "^1.0.1" commondir "^1.0.1"