diff --git a/declarations/_container.d.ts b/declarations/_container.d.ts new file mode 100644 index 000000000..1399f934c --- /dev/null +++ b/declarations/_container.d.ts @@ -0,0 +1,74 @@ +/* + * 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[]; +/** + * 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[]; + +export interface _Container { + [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; +} +/** + * 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; + /** + * The name of the share scope shared with this remote. + */ + shareScope?: string; +} diff --git a/declarations/_sharedContainer.d.ts b/declarations/_sharedContainer.d.ts deleted file mode 100644 index b7cc29cb1..000000000 --- a/declarations/_sharedContainer.d.ts +++ /dev/null @@ -1,154 +0,0 @@ -/* - * 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; -} diff --git a/declarations/_sharing.d.ts b/declarations/_sharing.d.ts new file mode 100644 index 000000000..c269ddee6 --- /dev/null +++ b/declarations/_sharing.d.ts @@ -0,0 +1,68 @@ +/* + * This file was automatically generated. + * DO NOT MODIFY BY HAND. + * Run `yarn special-lint-fix` to update + */ + +/** + * Modules that should be shared in the share scope. When provided, property names are used to match requested modules in this compilation. + */ +export type Shared = (SharedItem | SharedObject)[] | SharedObject; +/** + * A module that should be shared in the share scope. + */ +export type SharedItem = string; +/** + * Version number as array. Numbers and strings are accepted. Strings are treated as tags, which only match exactly. Numbers can match higher numbers. + */ +export type SharedVersionArray = (number | string)[]; + +export interface _Sharing { + [k: string]: any; +} +/** + * Modules that should be shared in the share scope. Property names are used to match requested modules in this compilation. Relative requests are resolved, module requests are matched unresolved, absolute paths will match resolved requests. A trailing slash will match all requests with this prefix. In this case shareKey must also have a trailing slash. + */ +export interface SharedObject { + /** + * Modules that should be shared in the share scope. + */ + [k: string]: SharedConfig | SharedItem; +} +/** + * Advanced configuration for modules that should be shared in the share scope. + */ +export interface SharedConfig { + /** + * Include the provided and fallback module directly instead behind an async request. This allows to use this shared module in initial load too. All possible shared modules need to be eager too. + */ + eager?: boolean; + /** + * Provided module that should be provided to share scope. Also acts as fallback module if no shared module is found in share scope or version isn't valid. Defaults to the property name. + */ + import?: false | SharedItem; + /** + * Version requirement from module in share scope. + */ + requiredVersion?: string | SharedVersionArray; + /** + * Module is looked up under this key from the share scope. + */ + shareKey?: string; + /** + * Share scope name. + */ + shareScope?: string; + /** + * Allow only a single version of the shared module in share scope (disabled by default). + */ + singleton?: boolean; + /** + * Do not accept shared module if version is not valid (defaults to yes, if local fallback module is available and shared module is not a singleton, otherwise no, has no effect if there is no required version specified). + */ + strictVersion?: boolean; + /** + * Version of the provided module. Will replace lower matching versions, but not higher. + */ + version?: false | string | SharedVersionArray; +} diff --git a/declarations/plugins/container/ContainerPlugin.d.ts b/declarations/plugins/container/ContainerPlugin.d.ts index 5fec33b2b..c6b717cfb 100644 --- a/declarations/plugins/container/ContainerPlugin.d.ts +++ b/declarations/plugins/container/ContainerPlugin.d.ts @@ -52,20 +52,6 @@ export type LibraryType = * 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; -/** - * 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[]; export interface ContainerPluginOptions { /** @@ -85,9 +71,9 @@ export interface ContainerPluginOptions { */ 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. + * The name of the share scope which is shared with the host (defaults to 'default'). */ - overridables?: Overridables; + shareScope?: string; } /** * Modules that should be exposed by this container. Property names are used as public paths. @@ -170,21 +156,3 @@ export interface LibraryCustomUmdObject { */ 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; -} diff --git a/declarations/plugins/container/ContainerReferencePlugin.d.ts b/declarations/plugins/container/ContainerReferencePlugin.d.ts index ee523741d..5f3cbcd1d 100644 --- a/declarations/plugins/container/ContainerReferencePlugin.d.ts +++ b/declarations/plugins/container/ContainerReferencePlugin.d.ts @@ -4,14 +4,6 @@ * Run `yarn special-lint-fix` to update */ -/** - * 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; /** * Specifies the default type of externals ('amd*', 'umd*', 'system' and 'jsonp' depend on output.libraryTarget set to the same value). */ @@ -48,10 +40,6 @@ export type RemotesItem = string; export type RemotesItems = RemotesItem[]; export interface ContainerReferencePluginOptions { - /** - * 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. */ @@ -60,24 +48,10 @@ export 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. */ 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. + * The name of the share scope shared with all remotes (defaults to 'default'). */ - [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; + shareScope?: string; } /** * Container locations from which modules should be resolved and loaded at runtime. Property names are used as request scopes. @@ -96,4 +70,8 @@ export interface RemotesConfig { * Container locations from which modules should be resolved and loaded at runtime. */ external: RemotesItem | RemotesItems; + /** + * The name of the share scope shared with this remote. + */ + shareScope?: string; } diff --git a/declarations/plugins/container/ModuleFederationPlugin.d.ts b/declarations/plugins/container/ModuleFederationPlugin.d.ts index f43de301c..6634181d3 100644 --- a/declarations/plugins/container/ModuleFederationPlugin.d.ts +++ b/declarations/plugins/container/ModuleFederationPlugin.d.ts @@ -52,28 +52,6 @@ export type LibraryType = * 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; -/** - * 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; /** * Specifies the default type of externals ('amd*', 'umd*', 'system' and 'jsonp' depend on output.libraryTarget set to the same value). */ @@ -109,13 +87,17 @@ export type RemotesItem = string; */ 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 in the share scope. When provided, property names are used to match requested modules in this compilation. */ export type Shared = (SharedItem | SharedObject)[] | SharedObject; /** - * Module that should be shared with remotes and/or host. + * A module that should be shared in the share scope. */ export type SharedItem = string; +/** + * Version number as array. Numbers and strings are accepted. Strings are treated as tags, which only match exactly. Numbers can match higher numbers. + */ +export type SharedVersionArray = (number | string)[]; export interface ModuleFederationPluginOptions { /** @@ -134,14 +116,6 @@ export interface ModuleFederationPluginOptions { * The name of the container. */ 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. */ @@ -151,7 +125,11 @@ export interface ModuleFederationPluginOptions { */ 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. + * Share scope name used for all shared modules (defaults to 'default'). + */ + shareScope?: string; + /** + * Modules that should be shared in the share scope. When provided, property names are used to match requested modules in this compilation. */ shared?: Shared; } @@ -236,42 +214,6 @@ export interface LibraryCustomUmdObject { */ 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. */ @@ -289,22 +231,54 @@ export interface RemotesConfig { * Container locations from which modules should be resolved and loaded at runtime. */ external: RemotesItem | RemotesItems; + /** + * The name of the share scope shared with this remote. + */ + shareScope?: string; } /** - * Modules that should be shared with remotes and/or host. Property names are used as shared keys. + * Modules that should be shared in the share scope. Property names are used to match requested modules in this compilation. Relative requests are resolved, module requests are matched unresolved, absolute paths will match resolved requests. A trailing slash will match all requests with this prefix. In this case shareKey must also have a trailing slash. */ export interface SharedObject { /** - * Modules that should be shared with remotes and/or host. + * Modules that should be shared in the share scope. */ [k: string]: SharedConfig | SharedItem; } /** - * Advanced configuration for modules that should be shared with remotes and/or host. + * Advanced configuration for modules that should be shared in the share scope. */ export interface SharedConfig { /** - * Module that should be shared with remotes and/or host. + * Include the provided and fallback module directly instead behind an async request. This allows to use this shared module in initial load too. All possible shared modules need to be eager too. */ - import: SharedItem; + eager?: boolean; + /** + * Provided module that should be provided to share scope. Also acts as fallback module if no shared module is found in share scope or version isn't valid. Defaults to the property name. + */ + import?: false | SharedItem; + /** + * Version requirement from module in share scope. + */ + requiredVersion?: string | SharedVersionArray; + /** + * Module is looked up under this key from the share scope. + */ + shareKey?: string; + /** + * Share scope name. + */ + shareScope?: string; + /** + * Allow only a single version of the shared module in share scope (disabled by default). + */ + singleton?: boolean; + /** + * Do not accept shared module if version is not valid (defaults to yes, if local fallback module is available and shared module is not a singleton, otherwise no, has no effect if there is no required version specified). + */ + strictVersion?: boolean; + /** + * Version of the provided module. Will replace lower matching versions, but not higher. + */ + version?: false | string | SharedVersionArray; } diff --git a/declarations/plugins/container/OverridablesPlugin.d.ts b/declarations/plugins/container/OverridablesPlugin.d.ts deleted file mode 100644 index 34a58f09a..000000000 --- a/declarations/plugins/container/OverridablesPlugin.d.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * This file was automatically generated. - * DO NOT MODIFY BY HAND. - * 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. - */ -export 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; -} -/** - * 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; -} diff --git a/declarations/plugins/sharing/ConsumeSharedPlugin.d.ts b/declarations/plugins/sharing/ConsumeSharedPlugin.d.ts new file mode 100644 index 000000000..825fc74f1 --- /dev/null +++ b/declarations/plugins/sharing/ConsumeSharedPlugin.d.ts @@ -0,0 +1,74 @@ +/* + * This file was automatically generated. + * DO NOT MODIFY BY HAND. + * Run `yarn special-lint-fix` to update + */ + +/** + * Modules that should be consumed from share scope. When provided, property names are used to match requested modules in this compilation. + */ +export type Consumes = (ConsumesItem | ConsumesObject)[] | ConsumesObject; +/** + * A module that should be consumed from share scope. + */ +export type ConsumesItem = string; +/** + * Version number as array. Numbers and strings are accepted. Strings are treated as tags, which only match exactly. Numbers can match higher numbers. + */ +export type SharedVersionArray = (number | string)[]; + +/** + * Options for consuming shared modules. + */ +export interface ConsumeSharedPluginOptions { + /** + * Modules that should be consumed from share scope. When provided, property names are used to match requested modules in this compilation. + */ + consumes: Consumes; + /** + * Share scope name used for all consumed modules (defaults to 'default'). + */ + shareScope?: string; +} +/** + * Modules that should be consumed from share scope. Property names are used to match requested modules in this compilation. Relative requests are resolved, module requests are matched unresolved, absolute paths will match resolved requests. A trailing slash will match all requests with this prefix. In this case shareKey must also have a trailing slash. + */ +export interface ConsumesObject { + /** + * Modules that should be consumed from share scope. + */ + [k: string]: ConsumesConfig | ConsumesItem; +} +/** + * Advanced configuration for modules that should be consumed from share scope. + */ +export interface ConsumesConfig { + /** + * Include the fallback module directly instead behind an async request. This allows to use fallback module in initial load too. All possible shared modules need to be eager too. + */ + eager?: boolean; + /** + * Fallback module if no shared module is found in share scope. Defaults to the property name. + */ + import?: false | ConsumesItem; + /** + * Version requirement from module in share scope. + */ + requiredVersion?: string | SharedVersionArray; + /** + * Module is looked up under this key from the share scope. + */ + shareKey?: string; + /** + * Share scope name. + */ + shareScope?: string; + /** + * Allow only a single version of the shared module in share scope (disabled by default). + */ + singleton?: boolean; + /** + * Do not accept shared module if version is not valid (defaults to yes, if local fallback module is available and shared module is not a singleton, otherwise no, has no effect if there is no required version specified). + */ + strictVersion?: boolean; +} diff --git a/declarations/plugins/sharing/ProvideSharedPlugin.d.ts b/declarations/plugins/sharing/ProvideSharedPlugin.d.ts new file mode 100644 index 000000000..4d8c8f3aa --- /dev/null +++ b/declarations/plugins/sharing/ProvideSharedPlugin.d.ts @@ -0,0 +1,59 @@ +/* + * This file was automatically generated. + * DO NOT MODIFY BY HAND. + * Run `yarn special-lint-fix` to update + */ + +/** + * Modules that should be provided as shared modules to the share scope. When provided, property name is used as share key, otherwise share key is automatically inferred from request. + */ +export type Provides = (ProvidesItem | ProvidesObject)[] | ProvidesObject; +/** + * Request to a module that should be provided as shared module to the share scope. + */ +export type ProvidesItem = string; +/** + * Version number as array. Numbers and strings are accepted. Strings are treated as tags, which only match exactly. Numbers can match higher numbers. + */ +export type SharedVersionArray = (number | string)[]; + +export interface ProvideSharedPluginOptions { + /** + * Modules that should be provided as shared modules to the share scope. When provided, property name is used as share key, otherwise share key is automatically inferred from request. + */ + provides: Provides; + /** + * Share scope name used for all provided modules (defaults to 'default'). + */ + shareScope?: string; +} +/** + * Modules that should be provided as shared modules to the share scope. Property names are used as share keys. + */ +export interface ProvidesObject { + /** + * Modules that should be provided as shared modules to the share scope. + */ + [k: string]: ProvidesConfig | ProvidesItem; +} +/** + * Advanced configuration for modules that should be provided as shared modules to the share scope. + */ +export interface ProvidesConfig { + /** + * Include the provided module directly instead behind an async request. This allows to use this shared module in initial load too. All possible shared modules need to be eager too. + */ + eager?: boolean; + /** + * Request to a module that should be provided as shared module to the share scope. + */ + import: ProvidesItem; + /** + * Share scope name. + */ + shareScope?: string; + /** + * Version of the provided module. Will replace lower matching versions, but not higher. + */ + version?: false | string | SharedVersionArray; +} diff --git a/declarations/plugins/sharing/SharePlugin.d.ts b/declarations/plugins/sharing/SharePlugin.d.ts new file mode 100644 index 000000000..4e6357b95 --- /dev/null +++ b/declarations/plugins/sharing/SharePlugin.d.ts @@ -0,0 +1,78 @@ +/* + * This file was automatically generated. + * DO NOT MODIFY BY HAND. + * Run `yarn special-lint-fix` to update + */ + +/** + * Modules that should be shared in the share scope. When provided, property names are used to match requested modules in this compilation. + */ +export type Shared = (SharedItem | SharedObject)[] | SharedObject; +/** + * A module that should be shared in the share scope. + */ +export type SharedItem = string; +/** + * Version number as array. Numbers and strings are accepted. Strings are treated as tags, which only match exactly. Numbers can match higher numbers. + */ +export type SharedVersionArray = (number | string)[]; + +/** + * Options for shared modules. + */ +export interface SharePluginOptions { + /** + * Share scope name used for all shared modules (defaults to 'default'). + */ + shareScope?: string; + /** + * Modules that should be shared in the share scope. When provided, property names are used to match requested modules in this compilation. + */ + shared: Shared; +} +/** + * Modules that should be shared in the share scope. Property names are used to match requested modules in this compilation. Relative requests are resolved, module requests are matched unresolved, absolute paths will match resolved requests. A trailing slash will match all requests with this prefix. In this case shareKey must also have a trailing slash. + */ +export interface SharedObject { + /** + * Modules that should be shared in the share scope. + */ + [k: string]: SharedConfig | SharedItem; +} +/** + * Advanced configuration for modules that should be shared in the share scope. + */ +export interface SharedConfig { + /** + * Include the provided and fallback module directly instead behind an async request. This allows to use this shared module in initial load too. All possible shared modules need to be eager too. + */ + eager?: boolean; + /** + * Provided module that should be provided to share scope. Also acts as fallback module if no shared module is found in share scope or version isn't valid. Defaults to the property name. + */ + import?: false | SharedItem; + /** + * Version requirement from module in share scope. + */ + requiredVersion?: string | SharedVersionArray; + /** + * Module is looked up under this key from the share scope. + */ + shareKey?: string; + /** + * Share scope name. + */ + shareScope?: string; + /** + * Allow only a single version of the shared module in share scope (disabled by default). + */ + singleton?: boolean; + /** + * Do not accept shared module if version is not valid (defaults to yes, if local fallback module is available and shared module is not a singleton, otherwise no, has no effect if there is no required version specified). + */ + strictVersion?: boolean; + /** + * Version of the provided module. Will replace lower matching versions, but not higher. + */ + version?: false | string | SharedVersionArray; +} diff --git a/examples/top-level-await/README.md b/examples/top-level-await/README.md index 531181ff2..6b998ed65 100644 --- a/examples/top-level-await/README.md +++ b/examples/top-level-await/README.md @@ -1,5 +1,5 @@ -Let's use `await` at top level in a module `db-connection.js`. -This makes sense since the connection to the DB need to established before the module is usable. +Let's use `await` at the top level in a module `db-connection.js`. +This makes sense since the connection to the DB needs to be established before the module is usable. # db-connection.js @@ -25,15 +25,15 @@ export const close = () => { But `db-connection.js` is no longer a normal module now. It's an **async module** now. Async modules have a different evaluation semantics. -While normal modules evaluate in a synchronous way, async modules evaluate in an asynchronous way. +While normal modules evaluate synchronously way, async modules evaluate asynchronously. -Async modules can't imported with a normal `import`. +Async modules can't be imported with a normal `import`. They need to be imported with `import await`. The main reason for this is to make the using module aware of the different evaluation semantics. Using `import await` in a module also makes the module an async module. -You can see it as form of top-level-await, but it's a bit different because imports hoist, so does `import await`. +You can see it as a form of top-level-await, but it's a bit different because imports hoist, so does `import await`. All `import`s and `import await`s hoist and are evaluated in parallel. `import await` doesn't affect tree shaking negatively. @@ -56,7 +56,7 @@ Now it looks like that this pattern will continue and will infect all using modu Yes, this is kind of true and makes sense. All these modules have their evaluation semantics changed to be async. -But you as developer don't want this. +But you as a developer don't want this. You want to break the chain at a point in your module graph where it makes sense. Luckily there is a nice way to break the chain. @@ -113,12 +113,12 @@ import { CreateUserAction } from "./Actions.js"; ``` Note that you may `import await` from a normal module too. -This is legal, but mostly unneeded. -`import await` may also been seen by developers as hint that this dependency does some async actions and may delay evaluation. +This is legal, but mostly not required. +`import await` may also be seen by developers as a hint that this dependency does some async actions and may delay evaluation. -As guideline you should prevent your application entry point to become an async module when compiling for web targets. +As a guideline, you should prevent your application entry point to become an async module when compiling for web targets. Doing async actions at application bootstrap will delay your application startup and may be negative for UX. -Use `import()` to do async action on demand or in background and use spinners or other indicators to inform the user about background actions. +Use `import()` to do async action on-demand or in the background and use spinners or other indicators to inform the user about background actions. When compiling for other targets like node.js, electron or WebWorkers, it may be fine that your entry point becomes an async module. diff --git a/examples/top-level-await/template.md b/examples/top-level-await/template.md index 80914eb0a..3da83813a 100644 --- a/examples/top-level-await/template.md +++ b/examples/top-level-await/template.md @@ -1,5 +1,5 @@ -Let's use `await` at top level in a module `db-connection.js`. -This makes sense since the connection to the DB need to established before the module is usable. +Let's use `await` at the top level in a module `db-connection.js`. +This makes sense since the connection to the DB needs to be established before the module is usable. # db-connection.js @@ -10,15 +10,15 @@ _{{db-connection.js}}_ But `db-connection.js` is no longer a normal module now. It's an **async module** now. Async modules have a different evaluation semantics. -While normal modules evaluate in a synchronous way, async modules evaluate in an asynchronous way. +While normal modules evaluate synchronously, async modules evaluate asynchronously. -Async modules can't imported with a normal `import`. +Async modules can't be imported with a normal `import`. They need to be imported with `import await`. The main reason for this is to make the using module aware of the different evaluation semantics. Using `import await` in a module also makes the module an async module. -You can see it as form of top-level-await, but it's a bit different because imports hoist, so does `import await`. +You can see it as a form of top-level-await, but it's a bit different because imports hoist, so does `import await`. All `import`s and `import await`s hoist and are evaluated in parallel. `import await` doesn't affect tree shaking negatively. @@ -35,7 +35,7 @@ Now it looks like that this pattern will continue and will infect all using modu Yes, this is kind of true and makes sense. All these modules have their evaluation semantics changed to be async. -But you as developer don't want this. +But you as a developer don't want this. You want to break the chain at a point in your module graph where it makes sense. Luckily there is a nice way to break the chain. @@ -62,12 +62,12 @@ _{{example.js}}_ ``` Note that you may `import await` from a normal module too. -This is legal, but mostly unneeded. -`import await` may also been seen by developers as hint that this dependency does some async actions and may delay evaluation. +This is legal, but mostly not required. +`import await` may also be seen by developers as a hint that this dependency does some async actions and may delay evaluation. -As guideline you should prevent your application entry point to become an async module when compiling for web targets. +As a guideline, you should prevent your application entry point to become an async module when compiling for web targets. Doing async actions at application bootstrap will delay your application startup and may be negative for UX. -Use `import()` to do async action on demand or in background and use spinners or other indicators to inform the user about background actions. +Use `import()` to do async action on-demand or in the background and use spinners or other indicators to inform the user about background actions. When compiling for other targets like node.js, electron or WebWorkers, it may be fine that your entry point becomes an async module. diff --git a/examples/wasm-simple/README.md b/examples/wasm-simple/README.md index 3e4512317..77ff25bd7 100644 --- a/examples/wasm-simple/README.md +++ b/examples/wasm-simple/README.md @@ -1,7 +1,7 @@ -This very simple example shows usage of WebAssembly. +This is a simple example that shows the usage of WebAssembly. WebAssembly modules can be imported like other async modules with `import await` or `import()`. -They are downloaded and instantiated in a streaming way when importing. +When importing, they are downloaded and instantiated in a streaming way. # example.js diff --git a/examples/wasm-simple/template.md b/examples/wasm-simple/template.md index 81990f4d2..d5a5c0c65 100644 --- a/examples/wasm-simple/template.md +++ b/examples/wasm-simple/template.md @@ -1,7 +1,7 @@ -This very simple example shows usage of WebAssembly. +This is a simple example that shows the usage of WebAssembly. WebAssembly modules can be imported like other async modules with `import await` or `import()`. -They are downloaded and instantiated in a streaming way when importing. +When importing, they are downloaded and instantiated in a streaming way. # example.js diff --git a/lib/APIPlugin.js b/lib/APIPlugin.js index bf2bb627f..835f16d5a 100644 --- a/lib/APIPlugin.js +++ b/lib/APIPlugin.js @@ -84,6 +84,18 @@ const REPLACEMENTS = { req: [RuntimeGlobals.systemContext], type: "object", assign: false + }, + __webpack_share_scopes__: { + expr: RuntimeGlobals.shareScopeMap, + req: [RuntimeGlobals.shareScopeMap], + type: "object", + assign: false + }, + __webpack_init_sharing__: { + expr: RuntimeGlobals.initializeSharing, + req: [RuntimeGlobals.initializeSharing], + type: "function", + assign: true } }; /* eslint-enable camelcase */ diff --git a/lib/Chunk.js b/lib/Chunk.js index 46a028132..b85d7a564 100644 --- a/lib/Chunk.js +++ b/lib/Chunk.js @@ -534,6 +534,13 @@ class Chunk { return chunks; } + /** + * @returns {Set} a set of all the initial chunks (including itself) + */ + getAllInitialChunks() { + return intersect(Array.from(this.groupsIterable, g => new Set(g.chunks))); + } + /** * @returns {Set} a set of all the referenced chunks (including itself) */ diff --git a/lib/Compilation.js b/lib/Compilation.js index 3ff9e2356..ad67bc01a 100644 --- a/lib/Compilation.js +++ b/lib/Compilation.js @@ -150,7 +150,8 @@ const { /** * @typedef {Object} EntryData - * @property {Dependency[]} dependencies dependencies of the entrypoint + * @property {Dependency[]} dependencies dependencies of the entrypoint that should be evaluated at startup + * @property {Dependency[]} includeDependencies dependencies of the entrypoint that should be included by not evaluated * @property {EntryOptions} options options of the entrypoint */ @@ -701,6 +702,14 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si /** @type {Map} */ this.entries = new Map(); + /** @type {EntryData} */ + this.globalEntry = { + dependencies: [], + includeDependencies: [], + options: { + name: undefined + } + }; /** @type {Map} */ this.entrypoints = new Map(); /** @type {Set} */ @@ -1514,9 +1523,8 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si } /** - * * @param {string} context context path for entry - * @param {Dependency} entry entry dependency being created + * @param {Dependency} entry entry dependency that should be followed * @param {string | EntryOptions} optionsOrName options or deprecated name of entry * @param {ModuleCallback} callback callback function * @returns {void} returns @@ -1528,19 +1536,51 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si ? optionsOrName : { name: optionsOrName }; + this._addEntryItem(context, entry, "dependencies", options, callback); + } + + /** + * @param {string} context context path for entry + * @param {Dependency} dependency dependency that should be followed + * @param {EntryOptions} options options + * @param {ModuleCallback} callback callback function + * @returns {void} returns + */ + addInclude(context, dependency, options, callback) { + this._addEntryItem( + context, + dependency, + "includeDependencies", + options, + callback + ); + } + + /** + * @param {string} context context path for entry + * @param {Dependency} entry entry dependency that should be followed + * @param {"dependencies" | "includeDependencies"} target type of entry + * @param {EntryOptions} options options + * @param {ModuleCallback} callback callback function + * @returns {void} returns + */ + _addEntryItem(context, entry, target, options, callback) { const { name } = options; - let entryData = this.entries.get(name); + let entryData = + name !== undefined ? this.entries.get(name) : this.globalEntry; if (entryData === undefined) { entryData = { - dependencies: [entry], + dependencies: [], + includeDependencies: [], options: { name: undefined, ...options } }; + entryData[target].push(entry); this.entries.set(name, entryData); } else { - entryData.dependencies.push(entry); + entryData[target].push(entry); for (const key of Object.keys(options)) { if (entryData.options[key] === options[key]) continue; if (entryData.options[key] === undefined) { @@ -1700,7 +1740,9 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si this.logger.time("create chunks"); this.hooks.beforeChunks.call(); - for (const [name, { dependencies, options }] of this.entries) { + const chunkGraphInit = new Map(); + for (const [name, { dependencies, includeDependencies, options }] of this + .entries) { const chunk = this.addChunk(name); chunk.name = name; if (options.filename) { @@ -1713,13 +1755,35 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si this.chunkGroups.push(entrypoint); connectChunkGroupAndChunk(entrypoint, chunk); - for (const dep of dependencies) { + for (const dep of [...this.globalEntry.dependencies, ...dependencies]) { entrypoint.addOrigin(null, { name }, /** @type {any} */ (dep).request); const module = this.moduleGraph.getModule(dep); if (module) { chunkGraph.connectChunkAndEntryModule(chunk, module, entrypoint); this.assignDepth(module); + const modulesList = chunkGraphInit.get(entrypoint); + if (modulesList === undefined) { + chunkGraphInit.set(entrypoint, [module]); + } else { + modulesList.push(module); + } + } + } + + for (const dep of [ + ...this.globalEntry.includeDependencies, + ...includeDependencies + ]) { + const module = this.moduleGraph.getModule(dep); + if (module) { + this.assignDepth(module); + const modulesList = chunkGraphInit.get(entrypoint); + if (modulesList === undefined) { + chunkGraphInit.set(entrypoint, [module]); + } else { + modulesList.push(module); + } } } } @@ -1742,10 +1806,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si } } } - buildChunkGraph( - this, - /** @type {Entrypoint[]} */ (this.chunkGroups.slice()) - ); + buildChunkGraph(this, chunkGraphInit); this.hooks.afterChunks.call(this.chunks); this.logger.timeEnd("create chunks"); diff --git a/lib/EntryPlugin.js b/lib/EntryPlugin.js index f27cbc36c..12c89acc0 100644 --- a/lib/EntryPlugin.js +++ b/lib/EntryPlugin.js @@ -22,7 +22,7 @@ class EntryPlugin { constructor(context, entry, options) { this.context = context; this.entry = entry; - this.options = options; + this.options = options || ""; } /** diff --git a/lib/FileSystemInfo.js b/lib/FileSystemInfo.js index a5987690e..2899a6386 100644 --- a/lib/FileSystemInfo.js +++ b/lib/FileSystemInfo.js @@ -835,6 +835,13 @@ class FileSystemInfo { if (optimizationEntry.shared > 0) { // It's a shared snapshot // We can't change it, so we can only use it when all files match + // and startTime is compatible + if ( + startTime && + (!snapshot.startTime || snapshot.startTime > startTime) + ) { + continue; + } const nonSharedFiles = new Set(); for (const path of optimizationEntry.snapshotContent) { if (!capturedFiles.has(path)) { @@ -864,7 +871,13 @@ class FileSystemInfo { } // Create and attach snapshot /** @type {Snapshot} */ - const commonSnapshot = { fileTimestamps: commonMap }; + const commonSnapshot = { + startTime: + startTime && snapshot.startTime + ? Math.min(startTime, snapshot.startTime) + : startTime || snapshot.startTime, + fileTimestamps: commonMap + }; children.add(commonSnapshot); if (!snapshot.children) snapshot.children = new Set(); snapshot.children.add(commonSnapshot); @@ -889,7 +902,6 @@ class FileSystemInfo { const ts = snapshot.fileTimestamps.get(path); if (ts === undefined) continue; commonMap.set(path, ts); - snapshot.fileTimestamps.delete(path); } if (commonMap.size < 2) { // Common part it too small @@ -897,7 +909,13 @@ class FileSystemInfo { } // Create and attach snapshot /** @type {Snapshot} */ - const commonSnapshot = { fileTimestamps: commonMap }; + const commonSnapshot = { + startTime: + startTime && snapshot.startTime + ? Math.min(startTime, snapshot.startTime) + : startTime || snapshot.startTime, + fileTimestamps: commonMap + }; children.add(commonSnapshot); if (!snapshot.children) snapshot.children = new Set(); snapshot.children.add(commonSnapshot); @@ -1243,7 +1261,7 @@ class FileSystemInfo { } if (current) { // For existing items only - if (current.safeTime > startTime) { + if (typeof startTime === "number" && current.safeTime > startTime) { // If a change happened after starting reading the item // this may no longer be valid if (this._remainingLogs > 0) { diff --git a/lib/FlagDependencyUsagePlugin.js b/lib/FlagDependencyUsagePlugin.js index aeceb64b4..dca4de17f 100644 --- a/lib/FlagDependencyUsagePlugin.js +++ b/lib/FlagDependencyUsagePlugin.js @@ -147,13 +147,28 @@ class FlagDependencyUsagePlugin { /** @type {Queue} */ const queue = new Queue(); - for (const { dependencies: deps } of compilation.entries.values()) { + const processEntryDependency = dep => { + const module = moduleGraph.getModule(dep); + if (module) { + processModule(module, NO_EXPORTS_REFERENCED); + queue.enqueue(module); + } + }; + for (const dep of compilation.globalEntry.dependencies) { + processEntryDependency(dep); + } + for (const dep of compilation.globalEntry.includeDependencies) { + processEntryDependency(dep); + } + for (const { + dependencies: deps, + includeDependencies: includeDeps + } of compilation.entries.values()) { for (const dep of deps) { - const module = moduleGraph.getModule(dep); - if (module) { - processModule(module, NO_EXPORTS_REFERENCED); - queue.enqueue(module); - } + processEntryDependency(dep); + } + for (const dep of includeDeps) { + processEntryDependency(dep); } } diff --git a/lib/Module.js b/lib/Module.js index 8c2ce370f..e527a3a84 100644 --- a/lib/Module.js +++ b/lib/Module.js @@ -49,6 +49,7 @@ const makeSerializable = require("./util/makeSerializable"); /** * @typedef {Object} CodeGenerationResult * @property {Map} sources the resulting sources for all source types + * @property {Map=} data the resulting data for all source types * @property {ReadonlySet} runtimeRequirements the runtime requirements */ diff --git a/lib/RuntimeGlobals.js b/lib/RuntimeGlobals.js index 251210ce3..0df62b833 100644 --- a/lib/RuntimeGlobals.js +++ b/lib/RuntimeGlobals.js @@ -196,9 +196,16 @@ exports.interceptModuleExecution = "__webpack_require__.i"; exports.global = "__webpack_require__.g"; /** - * the overrides mapping + * an object with all share scopes */ -exports.overrides = "__webpack_require__.O"; +exports.shareScopeMap = "__webpack_require__.S"; + +/** + * The sharing init sequence function (only runs once per share scope). + * Has one argument, the name of the share scope. + * Creates a share scope if not existing + */ +exports.initializeSharing = "__webpack_require__.I"; /** * the filename of the HMR manifest diff --git a/lib/RuntimePlugin.js b/lib/RuntimePlugin.js index f9687819a..f02e0010d 100644 --- a/lib/RuntimePlugin.js +++ b/lib/RuntimePlugin.js @@ -20,6 +20,7 @@ const HasOwnPropertyRuntimeModule = require("./runtime/HasOwnPropertyRuntimeModu const MakeNamespaceObjectRuntimeModule = require("./runtime/MakeNamespaceObjectRuntimeModule"); const PublicPathRuntimeModule = require("./runtime/PublicPathRuntimeModule"); const SystemContextRuntimeModule = require("./runtime/SystemContextRuntimeModule"); +const ShareRuntimeModule = require("./sharing/ShareRuntimeModule"); /** @typedef {import("./Chunk")} Chunk */ /** @typedef {import("./Compiler")} Compiler */ @@ -43,7 +44,9 @@ const GLOBALS_ON_REQUIRE = [ RuntimeGlobals.scriptNonce, RuntimeGlobals.uncaughtErrorHandler, RuntimeGlobals.wasmInstances, - RuntimeGlobals.instantiateWasm + RuntimeGlobals.instantiateWasm, + RuntimeGlobals.shareScopeMap, + RuntimeGlobals.initializeSharing ]; const MODULE_DEPENDENCIES = { @@ -60,7 +63,9 @@ const TREE_DEPENDENCIES = { RuntimeGlobals.definePropertyGetters, RuntimeGlobals.makeNamespaceObject, RuntimeGlobals.require - ] + ], + [RuntimeGlobals.initializeSharing]: [RuntimeGlobals.shareScopeMap], + [RuntimeGlobals.shareScopeMap]: [RuntimeGlobals.hasOwnProperty] }; class RuntimePlugin { @@ -254,6 +259,12 @@ class RuntimePlugin { .tap("RuntimePlugin", (chunk, set) => { set.add(RuntimeGlobals.ensureChunkHandlers); }); + compilation.hooks.runtimeRequirementInTree + .for(RuntimeGlobals.shareScopeMap) + .tap("RuntimePlugin", (chunk, set) => { + compilation.addRuntimeModule(chunk, new ShareRuntimeModule()); + return true; + }); // TODO webpack 6: remove CompatRuntimePlugin compilation.hooks.additionalTreeRuntimeRequirements.tap( "RuntimePlugin", diff --git a/lib/RuntimeTemplate.js b/lib/RuntimeTemplate.js index 9569ec358..6f74b95a7 100644 --- a/lib/RuntimeTemplate.js +++ b/lib/RuntimeTemplate.js @@ -13,6 +13,7 @@ const propertyAccess = require("./util/propertyAccess"); /** @typedef {import("../declarations/WebpackOptions").OutputNormalized} OutputOptions */ /** @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock */ /** @typedef {import("./ChunkGraph")} ChunkGraph */ +/** @typedef {import("./Dependency")} Dependency */ /** @typedef {import("./InitFragment")} InitFragment */ /** @typedef {import("./Module")} Module */ /** @typedef {import("./ModuleGraph")} ModuleGraph */ @@ -725,6 +726,59 @@ class RuntimeTemplate { } } + /** + * @param {Object} options options + * @param {AsyncDependenciesBlock} options.block the async block + * @param {ChunkGraph} options.chunkGraph the chunk graph + * @param {Set} options.runtimeRequirements if set, will be filled with runtime requirements + * @param {string=} options.request request string used originally + * @returns {string} expression + */ + asyncModuleFactory({ block, chunkGraph, runtimeRequirements, request }) { + const dep = block.dependencies[0]; + const module = chunkGraph.moduleGraph.getModule(dep); + const ensureChunk = this.blockPromise({ + block, + message: "", + chunkGraph, + runtimeRequirements + }); + const factory = this.returningFunction( + this.moduleRaw({ + module, + chunkGraph, + request, + runtimeRequirements + }) + ); + return this.returningFunction( + ensureChunk.startsWith("Promise.resolve(") + ? `${factory}` + : `${ensureChunk}.then(${this.returningFunction(factory)})` + ); + } + + /** + * @param {Object} options options + * @param {Dependency} options.dependency the dependency + * @param {ChunkGraph} options.chunkGraph the chunk graph + * @param {Set} options.runtimeRequirements if set, will be filled with runtime requirements + * @param {string=} options.request request string used originally + * @returns {string} expression + */ + syncModuleFactory({ dependency, chunkGraph, runtimeRequirements, request }) { + const module = chunkGraph.moduleGraph.getModule(dependency); + const factory = this.returningFunction( + this.moduleRaw({ + module, + chunkGraph, + request, + runtimeRequirements + }) + ); + return this.returningFunction(factory); + } + /** * @param {Object} options options * @param {string} options.exportsArgument the name of the exports object diff --git a/lib/buildChunkGraph.js b/lib/buildChunkGraph.js index 2824e5d90..c6de20469 100644 --- a/lib/buildChunkGraph.js +++ b/lib/buildChunkGraph.js @@ -134,7 +134,7 @@ const extractBlockModulesMap = compilation => { * * @param {Logger} logger a logger * @param {Compilation} compilation the compilation - * @param {Entrypoint[]} inputEntrypoints input groups + * @param {Map} inputEntrypointsAndModules chunk groups which are processed with the modules * @param {Map} chunkGroupInfoMap mapping from chunk group to available modules * @param {Map} chunkGroupDependencies dependencies for chunk groups * @param {Set} blocksWithNestedBlocks flag for blocks that have nested blocks @@ -143,7 +143,7 @@ const extractBlockModulesMap = compilation => { const visitModules = ( logger, compilation, - inputEntrypoints, + inputEntrypointsAndModules, chunkGroupInfoMap, chunkGroupDependencies, blocksWithNestedBlocks, @@ -179,7 +179,7 @@ const visitModules = ( // Fill queue with entrypoint modules // Create ChunkGroupInfo for entrypoints - for (const chunkGroup of inputEntrypoints) { + for (const [chunkGroup, modules] of inputEntrypointsAndModules) { /** @type {ChunkGroupInfo} */ const chunkGroupInfo = { chunkGroup, @@ -200,27 +200,24 @@ const visitModules = ( // This means no module is added until other sets are merged into // this minAvailableModules (by the parent entrypoints) const skippedItems = new Set(); - for (const chunk of chunkGroup.chunks) { - for (const module of chunkGraph.getChunkEntryModulesIterable(chunk)) { - skippedItems.add(module); - } + for (const module of modules) { + skippedItems.add(module); } chunkGroupInfo.skippedItems = skippedItems; chunkGroupsForCombining.add(chunkGroupInfo); } else { // The application may start here: We start with an empty list of available modules chunkGroupInfo.minAvailableModules = EMPTY_SET; - for (const chunk of chunkGroup.chunks) { - for (const module of chunkGraph.getChunkEntryModulesIterable(chunk)) { - queue.push({ - action: ADD_AND_ENTER_MODULE, - block: module, - module, - chunk, - chunkGroup, - chunkGroupInfo - }); - } + const chunk = chunkGroup.chunks[0]; + for (const module of modules) { + queue.push({ + action: ADD_AND_ENTER_MODULE, + block: module, + module, + chunk, + chunkGroup, + chunkGroupInfo + }); } } chunkGroupInfoMap.set(chunkGroup, chunkGroupInfo); @@ -902,10 +899,10 @@ const cleanupUnconnectedGroups = (compilation, allCreatedChunkGroups) => { /** * This method creates the Chunk graph from the Module graph * @param {Compilation} compilation the compilation - * @param {Entrypoint[]} inputEntrypoints chunk groups which are processed + * @param {Map} inputEntrypointsAndModules chunk groups which are processed with the modules * @returns {void} */ -const buildChunkGraph = (compilation, inputEntrypoints) => { +const buildChunkGraph = (compilation, inputEntrypointsAndModules) => { const logger = compilation.getLogger("webpack.buildChunkGraph"); // SHARED STATE @@ -928,7 +925,7 @@ const buildChunkGraph = (compilation, inputEntrypoints) => { visitModules( logger, compilation, - inputEntrypoints, + inputEntrypointsAndModules, chunkGroupInfoMap, chunkGroupDependencies, blocksWithNestedBlocks, diff --git a/lib/container/ContainerEntryDependency.js b/lib/container/ContainerEntryDependency.js index 132be5671..a4f064ab5 100644 --- a/lib/container/ContainerEntryDependency.js +++ b/lib/container/ContainerEntryDependency.js @@ -14,11 +14,13 @@ class ContainerEntryDependency extends Dependency { /** * @param {string} name entry name * @param {[string, ExposeOptions][]} exposes list of exposed modules + * @param {string} shareScope name of the share scope */ - constructor(name, exposes) { + constructor(name, exposes, shareScope) { super(); this.name = name; this.exposes = exposes; + this.shareScope = shareScope; this.optional = true; } diff --git a/lib/container/ContainerEntryModule.js b/lib/container/ContainerEntryModule.js index 6caf658a2..efae33852 100644 --- a/lib/container/ContainerEntryModule.js +++ b/lib/container/ContainerEntryModule.js @@ -39,11 +39,13 @@ class ContainerEntryModule extends Module { /** * @param {string} name container entry name * @param {[string, ExposeOptions][]} exposes list of exposed modules + * @param {string} shareScope name of the share scope */ - constructor(name, exposes) { + constructor(name, exposes, shareScope) { super("javascript/dynamic", null); this._name = name; this._exposes = exposes; + this._shareScope = shareScope; } /** @@ -57,7 +59,9 @@ class ContainerEntryModule extends Module { * @returns {string} a unique identifier of the module */ identifier() { - return `container entry ${JSON.stringify(this._exposes)}`; + return `container entry (${this._shareScope}) ${JSON.stringify( + this._exposes + )}`; } /** @@ -203,16 +207,21 @@ class ContainerEntryModule extends Module { ]), ");" ])};`, - `var override = ${runtimeTemplate.basicFunction( - "override", - `Object.assign(${RuntimeGlobals.overrides}, override);` - )};`, + `var init = ${runtimeTemplate.basicFunction("shareScope", [ + `var oldScope = ${RuntimeGlobals.shareScopeMap}[${JSON.stringify( + this._shareScope + )}];`, + `var name = ${JSON.stringify(this._shareScope)}`, + `if(oldScope && oldScope !== shareScope) throw new Error("Container initialization failed as it has already been initialized with a different share scope");`, + `${RuntimeGlobals.shareScopeMap}[name] = shareScope;`, + `return ${RuntimeGlobals.initializeSharing}(name);` + ])};`, "", "// This exports getters to disallow modifications", `${RuntimeGlobals.definePropertyGetters}(exports, {`, Template.indent([ `get: ${runtimeTemplate.returningFunction("get")},`, - `override: ${runtimeTemplate.returningFunction("override")}` + `init: ${runtimeTemplate.returningFunction("init")}` ]), "});" ]); @@ -238,14 +247,17 @@ class ContainerEntryModule extends Module { serialize(context) { const { write } = context; + write(this._name); write(this._exposes); + write(this._shareScope); super.serialize(context); } - deserialize(context) { + static deserialize(context) { const { read } = context; - this._exposes = read(); - super.deserialize(context); + const obj = new ContainerEntryModule(read(), read(), read()); + obj.deserialize(context); + return obj; } } diff --git a/lib/container/ContainerEntryModuleFactory.js b/lib/container/ContainerEntryModuleFactory.js index bd81e6ef2..1a1a7af89 100644 --- a/lib/container/ContainerEntryModuleFactory.js +++ b/lib/container/ContainerEntryModuleFactory.js @@ -21,7 +21,7 @@ module.exports = class ContainerEntryModuleFactory extends ModuleFactory { create({ dependencies: [dependency] }, callback) { const dep = /** @type {ContainerEntryDependency} */ (dependency); callback(null, { - module: new ContainerEntryModule(dep.name, dep.exposes) + module: new ContainerEntryModule(dep.name, dep.exposes, dep.shareScope) }); } }; diff --git a/lib/container/ContainerPlugin.js b/lib/container/ContainerPlugin.js index 3f3c83356..60c3c53b0 100644 --- a/lib/container/ContainerPlugin.js +++ b/lib/container/ContainerPlugin.js @@ -10,7 +10,6 @@ const schema = require("../../schemas/plugins/container/ContainerPlugin.json"); const ContainerEntryDependency = require("./ContainerEntryDependency"); const ContainerEntryModuleFactory = require("./ContainerEntryModuleFactory"); const ContainerExposedDependency = require("./ContainerExposedDependency"); -const OverridablesPlugin = require("./OverridablesPlugin"); const { parseOptions } = require("./options"); /** @typedef {import("../../declarations/plugins/container/ContainerPlugin").ContainerPluginOptions} ContainerPluginOptions */ @@ -26,8 +25,8 @@ class ContainerPlugin { validateOptions(schema, options, { name: "Container Plugin" }); this._options = { - overridables: options.overridables, name: options.name, + shareScope: options.shareScope || "default", library: options.library || { type: "var", name: options.name @@ -51,14 +50,12 @@ class ContainerPlugin { * @returns {void} */ apply(compiler) { - const { name, exposes, filename, library, overridables } = this._options; + const { name, exposes, shareScope, filename, library } = this._options; compiler.options.output.enabledLibraryTypes.push(library.type); - new OverridablesPlugin({ overridables }).apply(compiler); - compiler.hooks.make.tapAsync(PLUGIN_NAME, (compilation, callback) => { - const dep = new ContainerEntryDependency(name, exposes); + const dep = new ContainerEntryDependency(name, exposes, shareScope); dep.loc = { name }; compilation.addEntry( compilation.options.context, diff --git a/lib/container/ContainerReferencePlugin.js b/lib/container/ContainerReferencePlugin.js index fcaa256f9..8741fbdea 100644 --- a/lib/container/ContainerReferencePlugin.js +++ b/lib/container/ContainerReferencePlugin.js @@ -9,10 +9,10 @@ const validateOptions = require("schema-utils"); const schema = require("../../schemas/plugins/container/ContainerReferencePlugin.json"); const ExternalsPlugin = require("../ExternalsPlugin"); const RuntimeGlobals = require("../RuntimeGlobals"); +const FallbackDependency = require("./FallbackDependency"); +const FallbackItemDependency = require("./FallbackItemDependency"); +const FallbackModuleFactory = require("./FallbackModuleFactory"); const RemoteModule = require("./RemoteModule"); -const RemoteOverrideDependency = require("./RemoteOverrideDependency"); -const RemoteOverridesDependency = require("./RemoteOverridesDependency"); -const RemoteOverridesModuleFactory = require("./RemoteOverridesModuleFactory"); const RemoteRuntimeModule = require("./RemoteRuntimeModule"); const RemoteToExternalDependency = require("./RemoteToExternalDependency"); const { parseOptions } = require("./options"); @@ -20,7 +20,6 @@ const { parseOptions } = require("./options"); /** @typedef {import("../../declarations/plugins/container/ContainerReferencePlugin").ContainerReferencePluginOptions} ContainerReferencePluginOptions */ /** @typedef {import("../../declarations/plugins/container/ContainerReferencePlugin").RemotesConfig} RemotesConfig */ /** @typedef {import("../Compiler")} Compiler */ -/** @typedef {import("./RemoteOverridesModule").OverrideOptions} OverrideOptions */ const slashCode = "/".charCodeAt(0); @@ -35,31 +34,16 @@ class ContainerReferencePlugin { this._remotes = parseOptions( options.remotes, item => ({ - external: Array.isArray(item) ? item : [item] + external: Array.isArray(item) ? item : [item], + shareScope: options.shareScope || "default" }), item => ({ - external: Array.isArray(item.external) ? item.external : [item.external] + external: Array.isArray(item.external) + ? item.external + : [item.external], + shareScope: item.shareScope || options.shareScope || "default" }) ); - /** @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 (b < a) return 1; - return 0; - }); } /** @@ -94,13 +78,13 @@ class ContainerReferencePlugin { ); compilation.dependencyFactories.set( - RemoteOverrideDependency, + FallbackItemDependency, normalModuleFactory ); compilation.dependencyFactories.set( - RemoteOverridesDependency, - new RemoteOverridesModuleFactory() + FallbackDependency, + new FallbackModuleFactory() ); normalModuleFactory.hooks.factorize.tap( @@ -115,7 +99,6 @@ class ContainerReferencePlugin { ) { return new RemoteModule( data.request, - this._overrides, config.external.map((external, i) => external.startsWith("internal ") ? external.slice(9) @@ -123,7 +106,8 @@ class ContainerReferencePlugin { i ? `/fallback-${i}` : "" }` ), - `.${data.request.slice(key.length)}` + `.${data.request.slice(key.length)}`, + config.shareScope ); } } @@ -135,8 +119,10 @@ class ContainerReferencePlugin { .for(RuntimeGlobals.ensureChunkHandlers) .tap("ContainerReferencePlugin", (chunk, set) => { set.add(RuntimeGlobals.module); - set.add(RuntimeGlobals.moduleFactories); + set.add(RuntimeGlobals.moduleFactoriesAddOnly); set.add(RuntimeGlobals.hasOwnProperty); + set.add(RuntimeGlobals.initializeSharing); + set.add(RuntimeGlobals.shareScopeMap); compilation.addRuntimeModule(chunk, new RemoteRuntimeModule()); }); } diff --git a/lib/container/FallbackDependency.js b/lib/container/FallbackDependency.js new file mode 100644 index 000000000..2861d88f3 --- /dev/null +++ b/lib/container/FallbackDependency.js @@ -0,0 +1,47 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ + +"use strict"; + +const Dependency = require("../Dependency"); +const makeSerializable = require("../util/makeSerializable"); + +class FallbackDependency extends Dependency { + constructor(requests) { + super(); + this.requests = requests; + } + + /** + * @returns {string | null} an identifier to merge equal requests + */ + getResourceIdentifier() { + return `fallback ${this.requests.join(" ")}`; + } + + get type() { + return "fallbacks"; + } + + serialize(context) { + const { write } = context; + write(this.requests); + super.serialize(context); + } + + static deserialize(context) { + const { read } = context; + const obj = new FallbackDependency(read()); + obj.deserialize(context); + return obj; + } +} + +makeSerializable( + FallbackDependency, + "webpack/lib/container/FallbackDependency" +); + +module.exports = FallbackDependency; diff --git a/lib/container/OverridableOriginalDependency.js b/lib/container/FallbackItemDependency.js similarity index 59% rename from lib/container/OverridableOriginalDependency.js rename to lib/container/FallbackItemDependency.js index 962347f58..c71a30260 100644 --- a/lib/container/OverridableOriginalDependency.js +++ b/lib/container/FallbackItemDependency.js @@ -8,19 +8,19 @@ const ModuleDependency = require("../dependencies/ModuleDependency"); const makeSerializable = require("../util/makeSerializable"); -class OverridableOriginalDependency extends ModuleDependency { +class FallbackItemDependency extends ModuleDependency { constructor(request) { super(request); } get type() { - return "overridable original"; + return "fallback item"; } } makeSerializable( - OverridableOriginalDependency, - "webpack/lib/container/OverridableOriginalDependency" + FallbackItemDependency, + "webpack/lib/container/FallbackItemDependency" ); -module.exports = OverridableOriginalDependency; +module.exports = FallbackItemDependency; diff --git a/lib/container/OverridableModule.js b/lib/container/FallbackModule.js similarity index 52% rename from lib/container/OverridableModule.js rename to lib/container/FallbackModule.js index f46e577e1..cc51cae27 100644 --- a/lib/container/OverridableModule.js +++ b/lib/container/FallbackModule.js @@ -1,6 +1,6 @@ /* MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra + Author Tobias Koppers @sokra and Zackary Jackson @ScriptedAlchemy */ "use strict"; @@ -10,10 +10,10 @@ const Module = require("../Module"); const RuntimeGlobals = require("../RuntimeGlobals"); const Template = require("../Template"); const makeSerializable = require("../util/makeSerializable"); -const OverridableDependenciesBlock = require("./OverridableDependenciesBlock"); -const OverridableOriginalDependency = require("./OverridableOriginalDependency"); +const FallbackItemDependency = require("./FallbackItemDependency"); /** @typedef {import("../../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions */ +/** @typedef {import("../Chunk")} Chunk */ /** @typedef {import("../ChunkGraph")} ChunkGraph */ /** @typedef {import("../ChunkGroup")} ChunkGroup */ /** @typedef {import("../Compilation")} Compilation */ @@ -27,31 +27,24 @@ const OverridableOriginalDependency = require("./OverridableOriginalDependency") /** @typedef {import("../util/Hash")} Hash */ /** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */ -/** - * @typedef {Object} OverridableOptions - * @property {string} name global overridable name - */ +const TYPES = new Set(["javascript"]); +const RUNTIME_REQUIREMENTS = new Set([RuntimeGlobals.module]); -const TYPES = new Set(["overridable"]); - -class OverridableModule extends Module { +class FallbackModule extends Module { /** - * @param {string} context context - * @param {string} originalRequest original request - * @param {OverridableOptions} options overridable options + * @param {string[]} requests list of requests to choose one */ - constructor(context, originalRequest, options) { - super("overridable-module", context); - this.originalRequest = originalRequest; - this.options = options; + constructor(requests) { + super("fallback-module"); + this.requests = requests; + this._identifier = `fallback ${this.requests.join(" ")}`; } /** * @returns {string} a unique identifier of the module */ identifier() { - const { name } = this.options; - return `overridable|${name}|${this.context}|${this.originalRequest}`; + return this._identifier; } /** @@ -59,8 +52,7 @@ class OverridableModule extends Module { * @returns {string} a user readable identifier of the module */ readableIdentifier(requestShortener) { - const { name } = this.options; - return `overridable ${name} -> ${this.originalRequest}`; + return this._identifier; } /** @@ -68,8 +60,18 @@ class OverridableModule extends Module { * @returns {string | null} an identifier for library inclusion */ libIdent(options) { - const { name } = this.options; - return `webpack/container/overridable/${name}=${this.originalRequest}`; + return `webpack/container/fallback/${this.requests[0]}/and ${ + this.requests.length - 1 + } more`; + } + + /** + * @param {Chunk} chunk the chunk which condition should be checked + * @param {Compilation} compilation the compilation + * @returns {boolean} true, if the chunk is ok for the module + */ + chunkCondition(chunk, { chunkGraph }) { + return chunkGraph.getNumberOfEntryModules(chunk) > 0; } /** @@ -91,14 +93,25 @@ class OverridableModule extends Module { */ build(options, compilation, resolver, fs, callback) { this.buildMeta = {}; - this.buildInfo = {}; - const block = new OverridableDependenciesBlock(); - const dep = new OverridableOriginalDependency(this.originalRequest); - block.addDependency(dep); - this.addBlock(block); + this.buildInfo = { + strict: true + }; + + this.clearDependenciesAndBlocks(); + for (const request of this.requests) + this.addDependency(new FallbackItemDependency(request)); + callback(); } + /** + * @param {string=} type the source type for which the size should be estimated + * @returns {number} the estimated size of the module (must be non-zero) + */ + size(type) { + return this.requests.length * 5 + 42; + } + /** * @returns {Set} types available (do not mutate) */ @@ -106,82 +119,55 @@ class OverridableModule extends Module { return TYPES; } - /** - * @param {string=} type the source type for which the size should be estimated - * @returns {number} the estimated size of the module (must be non-zero) - */ - size(type) { - return 42; - } - - /** - * @param {Hash} hash the hash used to track dependencies - * @param {ChunkGraph} chunkGraph the chunk graph - * @returns {void} - */ - updateHash(hash, chunkGraph) { - hash.update(JSON.stringify(this.options)); - hash.update(this.originalRequest); - super.updateHash(hash, chunkGraph); - } - /** * @param {CodeGenerationContext} context context for code generation * @returns {CodeGenerationResult} result */ - codeGeneration({ chunkGraph, moduleGraph, runtimeTemplate }) { - const runtimeRequirements = new Set([RuntimeGlobals.overrides]); - const originalBlock = this.blocks[0]; - const originalDep = originalBlock.dependencies[0]; - const originalRequest = moduleGraph.getModule(originalDep); - const ensureChunk = runtimeTemplate.blockPromise({ - block: originalBlock, - message: "", - chunkGraph, - runtimeRequirements - }); - const factory = runtimeTemplate.returningFunction( - runtimeTemplate.moduleRaw({ - module: originalRequest, - chunkGraph, - request: "", - runtimeRequirements - }) + codeGeneration({ runtimeTemplate, moduleGraph, chunkGraph }) { + const ids = this.dependencies.map(dep => + chunkGraph.getModuleId(moduleGraph.getModule(dep)) ); + const code = Template.asString([ + `var ids = ${JSON.stringify(ids)};`, + "var error, result, i = 0;", + `var loop = ${runtimeTemplate.basicFunction("next", [ + "while(i < ids.length) {", + Template.indent([ + "try { next = __webpack_require__(ids[i++]); } catch(e) { return handleError(e); }", + "if(next) return next.then ? next.then(handleResult, handleError) : handleResult(next);" + ]), + "}", + "if(error) throw error;" + ])}`, + `var handleResult = ${runtimeTemplate.basicFunction("result", [ + "if(result) return result;", + "return loop();" + ])};`, + `var handleError = ${runtimeTemplate.basicFunction("e", [ + "error = e;", + "return loop();" + ])};`, + "module.exports = loop();" + ]); const sources = new Map(); - sources.set( - "overridable", - new RawSource( - Template.asString([ - ensureChunk.startsWith("Promise.resolve(") - ? `return ${factory};` - : `return ${ensureChunk}.then(${runtimeTemplate.returningFunction( - factory - )});` - ]) - ) - ); - return { - runtimeRequirements, - sources - }; + sources.set("javascript", new RawSource(code)); + return { sources, runtimeRequirements: RUNTIME_REQUIREMENTS }; } serialize(context) { const { write } = context; - write(this.originalRequest); - write(this.options); + write(this.requests); super.serialize(context); } - deserialize(context) { + static deserialize(context) { const { read } = context; - this.originalRequest = read(); - this.options = read(); - super.deserialize(context); + const obj = new FallbackModule(read()); + obj.deserialize(context); + return obj; } } -makeSerializable(OverridableModule, "webpack/lib/container/OverridableModule"); +makeSerializable(FallbackModule, "webpack/lib/container/FallbackModule"); -module.exports = OverridableModule; +module.exports = FallbackModule; diff --git a/lib/container/FallbackModuleFactory.js b/lib/container/FallbackModuleFactory.js new file mode 100644 index 000000000..350e910b9 --- /dev/null +++ b/lib/container/FallbackModuleFactory.js @@ -0,0 +1,27 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra, Zackary Jackson @ScriptedAlchemy, Marais Rossouw @maraisr +*/ + +"use strict"; + +const ModuleFactory = require("../ModuleFactory"); +const FallbackModule = require("./FallbackModule"); + +/** @typedef {import("../ModuleFactory").ModuleFactoryCreateData} ModuleFactoryCreateData */ +/** @typedef {import("../ModuleFactory").ModuleFactoryResult} ModuleFactoryResult */ +/** @typedef {import("./FallbackDependency")} FallbackDependency */ + +module.exports = class FallbackModuleFactory extends ModuleFactory { + /** + * @param {ModuleFactoryCreateData} data data object + * @param {function(Error=, ModuleFactoryResult=): void} callback callback + * @returns {void} + */ + create({ dependencies: [dependency] }, callback) { + const dep = /** @type {FallbackDependency} */ (dependency); + callback(null, { + module: new FallbackModule(dep.requests) + }); + } +}; diff --git a/lib/container/ModuleFederationPlugin.js b/lib/container/ModuleFederationPlugin.js index 449434e6e..16ef804ab 100644 --- a/lib/container/ModuleFederationPlugin.js +++ b/lib/container/ModuleFederationPlugin.js @@ -7,6 +7,7 @@ const validateOptions = require("schema-utils"); const schema = require("../../schemas/plugins/container/ModuleFederationPlugin.json"); +const SharePlugin = require("../sharing/SharePlugin"); const ContainerPlugin = require("./ContainerPlugin"); const ContainerReferencePlugin = require("./ContainerReferencePlugin"); @@ -14,31 +15,6 @@ const ContainerReferencePlugin = require("./ContainerReferencePlugin"); /** @typedef {import("../../declarations/plugins/container/ModuleFederationPlugin").Shared} Shared */ /** @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 { /** * @param {ModuleFederationPluginOptions} options options @@ -75,8 +51,7 @@ class ModuleFederationPlugin { name: options.name, library: options.library || compiler.options.output.library, filename: options.filename, - exposes: options.exposes, - overridables: merge(options.shared, options.overridables) + exposes: options.exposes }).apply(compiler); } if ( @@ -90,8 +65,13 @@ class ModuleFederationPlugin { options.remoteType || (options.library && options.library.type) || compiler.options.externalsType, - remotes: options.remotes, - overrides: merge(options.shared, options.overrides) + remotes: options.remotes + }).apply(compiler); + } + if (options.shared) { + new SharePlugin({ + shared: options.shared, + shareScope: options.shareScope }).apply(compiler); } }); diff --git a/lib/container/OverridableDependenciesBlock.js b/lib/container/OverridableDependenciesBlock.js deleted file mode 100644 index 5a01299d0..000000000 --- a/lib/container/OverridableDependenciesBlock.js +++ /dev/null @@ -1,26 +0,0 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ - -"use strict"; - -const AsyncDependenciesBlock = require("../AsyncDependenciesBlock"); - -/** @typedef {import("../ChunkGroup")} ChunkGroup */ - -class OverridableDependenciesBlock extends AsyncDependenciesBlock { - constructor() { - super({}); - } - - /** - * @param {ChunkGroup} parentChunkGroup the parent chunk group - * @returns {boolean} true when this dependencies block should be loaded async - */ - isAsync(parentChunkGroup) { - return !parentChunkGroup.isInitial(); - } -} - -module.exports = OverridableDependenciesBlock; diff --git a/lib/container/OverridablesPlugin.js b/lib/container/OverridablesPlugin.js deleted file mode 100644 index 123bab1b9..000000000 --- a/lib/container/OverridablesPlugin.js +++ /dev/null @@ -1,194 +0,0 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ - -"use strict"; - -const validateOptions = require("schema-utils"); -const schema = require("../../schemas/plugins/container/OverridablesPlugin.json"); -const ModuleNotFoundError = require("../ModuleNotFoundError"); -const RuntimeGlobals = require("../RuntimeGlobals"); -const { - toConstantDependency, - evaluateToString -} = require("../javascript/JavascriptParserHelpers"); -const LazySet = require("../util/LazySet"); -const OverridableModule = require("./OverridableModule"); -const OverridableOriginalDependency = require("./OverridableOriginalDependency"); -const OverridablesRuntimeModule = require("./OverridablesRuntimeModule"); -const { parseOptions } = require("./options"); - -/** @typedef {import("enhanced-resolve").ResolveContext} ResolveContext */ -/** @typedef {import("../../declarations/plugins/container/OverridablesPlugin").OverridablesConfig} OverridablesConfig */ -/** @typedef {import("../../declarations/plugins/container/OverridablesPlugin").OverridablesPluginOptions} OverridablesPluginOptions */ -/** @typedef {import("../Compiler")} Compiler */ -/** @typedef {import("./OverridableModule").OverridableOptions} OverridableOptions */ - -const PLUGIN_NAME = "OverridablesPlugin"; - -class OverridablesPlugin { - /** - * @param {OverridablesPluginOptions} options options - */ - constructor(options) { - if (typeof options !== "string") { - validateOptions(schema, options, { name: "Overridables Plugin" }); - } - - this._overridables = parseOptions( - options.overridables, - item => ({ - import: Array.isArray(item) ? item : [item] - }), - item => ({ - import: Array.isArray(item.import) ? item.import : [item.import] - }) - ); - } - - /** - * Apply the plugin - * @param {Compiler} compiler the compiler instance - * @returns {void} - */ - apply(compiler) { - compiler.hooks.thisCompilation.tap( - PLUGIN_NAME, - (compilation, { normalModuleFactory }) => { - compilation.dependencyFactories.set( - OverridableOriginalDependency, - normalModuleFactory - ); - - /** @type {Map} */ - const resolvedOverridables = new Map(); - const resolveContext = { - /** @type {LazySet} */ - fileDependencies: new LazySet(), - /** @type {LazySet} */ - contextDependencies: new LazySet(), - /** @type {LazySet} */ - missingDependencies: new LazySet() - }; - const resolver = compilation.resolverFactory.get( - "normal", - undefined, - "esm" - ); - - /** - * @param {string} request imported request - * @param {string} name overridable name - * @param {OverridablesConfig} config config - * @returns {Promise} promise - */ - const resolveOverridable = (request, name, config) => { - return new Promise(resolve => { - resolver.resolve( - {}, - compiler.context, - request, - resolveContext, - (err, result) => { - if (err) { - compilation.errors.push( - new ModuleNotFoundError(null, err, { - name: `overridable ${name}` - }) - ); - return resolve(); - } - resolvedOverridables.set(result, { - name - }); - resolve(); - } - ); - }); - }; - const promise = Promise.all( - this._overridables.map(([name, config]) => { - return Promise.all( - config.import.map(request => - resolveOverridable(request, name, config) - ) - ); - }) - ).then(() => { - compilation.contextDependencies.addAll( - resolveContext.contextDependencies - ); - compilation.fileDependencies.addAll(resolveContext.fileDependencies); - compilation.missingDependencies.addAll( - resolveContext.missingDependencies - ); - }); - normalModuleFactory.hooks.afterResolve.tapAsync( - PLUGIN_NAME, - (resolveData, callback) => { - // wait for resolving to be complete - promise.then(() => callback()); - } - ); - normalModuleFactory.hooks.module.tap( - PLUGIN_NAME, - (module, createData, resolveData) => { - if ( - resolveData.dependencies[0] instanceof - OverridableOriginalDependency - ) { - return; - } - const options = resolvedOverridables.get(createData.resource); - if (options !== undefined) { - return new OverridableModule( - resolveData.context, - resolveData.request, - options - ); - } - } - ); - compilation.hooks.additionalTreeRuntimeRequirements.tap( - PLUGIN_NAME, - (chunk, set) => { - set.add(RuntimeGlobals.module); - set.add(RuntimeGlobals.moduleFactories); - set.add(RuntimeGlobals.hasOwnProperty); - compilation.addRuntimeModule( - chunk, - new OverridablesRuntimeModule(set) - ); - } - ); - const handler = parser => { - parser.hooks.expression - .for("__webpack_override__") - .tap( - PLUGIN_NAME, - toConstantDependency( - parser, - `Object.assign.bind(Object, ${RuntimeGlobals.overrides})`, - [RuntimeGlobals.overrides] - ) - ); - parser.hooks.evaluateTypeof - .for("__webpack_override__") - .tap(PLUGIN_NAME, evaluateToString("function")); - }; - normalModuleFactory.hooks.parser - .for("javascript/auto") - .tap("APIPlugin", handler); - normalModuleFactory.hooks.parser - .for("javascript/dynamic") - .tap("APIPlugin", handler); - normalModuleFactory.hooks.parser - .for("javascript/esm") - .tap("APIPlugin", handler); - } - ); - } -} - -module.exports = OverridablesPlugin; diff --git a/lib/container/OverridablesRuntimeModule.js b/lib/container/OverridablesRuntimeModule.js deleted file mode 100644 index 5f08ff578..000000000 --- a/lib/container/OverridablesRuntimeModule.js +++ /dev/null @@ -1,153 +0,0 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ - -"use strict"; - -const RuntimeGlobals = require("../RuntimeGlobals"); -const RuntimeModule = require("../RuntimeModule"); -const Template = require("../Template"); - -/** @typedef {import("./OverridableModule")} OverridableModule */ - -class OverridablesRuntimeModule extends RuntimeModule { - constructor(runtimeRequirements) { - super("overridables"); - this._runtimeRequirements = runtimeRequirements; - } - - /** - * @returns {string} runtime code - */ - generate() { - const { - runtimeTemplate, - moduleGraph, - chunkGraph, - codeGenerationResults - } = this.compilation; - const chunkToOverridableMapping = {}; - const idToNameMapping = {}; - const overridableToFallbackMapping = new Map(); - const initialOverridables = {}; - const asyncChunks = this.chunk.getAllAsyncChunks(); - for (const chunk of asyncChunks) { - const modules = chunkGraph.getChunkModulesIterableBySourceType( - chunk, - "overridable" - ); - if (!modules) continue; - const overridables = (chunkToOverridableMapping[chunk.id] = []); - for (const m of modules) { - const module = /** @type {OverridableModule} */ (m); - const name = module.options.name; - const id = chunkGraph.getModuleId(module); - overridables.push(id); - idToNameMapping[id] = name; - const source = codeGenerationResults - .get(module) - .sources.get("overridable"); - overridableToFallbackMapping.set(id, source.source()); - } - } - for (const chunk of this.chunk.getAllReferencedChunks()) { - if (asyncChunks.has(chunk)) continue; - const modules = chunkGraph.getChunkModulesIterableBySourceType( - chunk, - "overridable" - ); - if (!modules) continue; - for (const m of modules) { - const module = /** @type {OverridableModule} */ (m); - const name = module.options.name; - const id = chunkGraph.getModuleId(module); - idToNameMapping[id] = name; - const fallbackModule = moduleGraph.getModule( - module.blocks[0].dependencies[0] - ); - const fallbackId = chunkGraph.getModuleId(fallbackModule); - initialOverridables[id] = fallbackId; - } - } - return Template.asString([ - `${RuntimeGlobals.overrides} = {};`, - "var installedModules = {};", - `var idToNameMapping = ${JSON.stringify(idToNameMapping, null, "\t")};`, - - Object.keys(initialOverridables).length - ? Template.asString([ - `var initialOverridables = ${JSON.stringify( - initialOverridables, - null, - "\t" - )};`, - `for(var id in initialOverridables) if(${ - RuntimeGlobals.hasOwnProperty - }(initialOverridables, id)) __webpack_modules__[id] = (${runtimeTemplate.returningFunction( - `${runtimeTemplate.basicFunction("module", [ - "// Handle case when module is used sync", - "installedModules[id] = 0;", - `var override = ${RuntimeGlobals.overrides}[idToNameMapping[id]];`, - "module.exports = override ? override()() : __webpack_require__(initialOverridables[id]);" - ])}`, - "id" - )})(id);` - ]) - : "// no overridables in initial chunks", - this._runtimeRequirements.has(RuntimeGlobals.ensureChunkHandlers) - ? Template.asString([ - `var chunkMapping = ${JSON.stringify( - chunkToOverridableMapping, - null, - "\t" - )};`, - "var fallbackMapping = {", - Template.indent( - Array.from( - overridableToFallbackMapping, - ([id, source]) => - `${JSON.stringify(id)}: ${runtimeTemplate.basicFunction( - "", - source - )}` - ).join(",\n") - ), - "};", - `${ - RuntimeGlobals.ensureChunkHandlers - }.overridables = ${runtimeTemplate.basicFunction( - "chunkId, promises", - [ - `if(${RuntimeGlobals.hasOwnProperty}(chunkMapping, chunkId)) {`, - Template.indent([ - `chunkMapping[chunkId].forEach(${runtimeTemplate.basicFunction( - "id", - [ - `promises.push(${ - RuntimeGlobals.hasOwnProperty - }(installedModules, id) ? installedModules[id] : installedModules[id] = Promise.resolve((${ - RuntimeGlobals.overrides - }[idToNameMapping[id]] || fallbackMapping[id])()).then(${runtimeTemplate.basicFunction( - "factory", - [ - "installedModules[id] = 0;", - `__webpack_modules__[id] = ${runtimeTemplate.basicFunction( - "module", - ["module.exports = factory();"] - )}` - ] - )}))` - ] - )});` - ]), - "}" - ] - )}` - ]) - : "// no chunk loading of overridables" - ]); - } -} - -module.exports = OverridablesRuntimeModule; diff --git a/lib/container/RemoteModule.js b/lib/container/RemoteModule.js index d714e94fa..b2bea8e33 100644 --- a/lib/container/RemoteModule.js +++ b/lib/container/RemoteModule.js @@ -8,9 +8,8 @@ const { RawSource } = require("webpack-sources"); const Module = require("../Module"); const RuntimeGlobals = require("../RuntimeGlobals"); -const createHash = require("../util/createHash"); const makeSerializable = require("../util/makeSerializable"); -const RemoteOverridesDependency = require("./RemoteOverridesDependency"); +const FallbackDependency = require("./FallbackDependency"); const RemoteToExternalDependency = require("./RemoteToExternalDependency"); /** @typedef {import("../../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions */ @@ -26,33 +25,26 @@ const RemoteToExternalDependency = require("./RemoteToExternalDependency"); /** @typedef {import("../WebpackError")} WebpackError */ /** @typedef {import("../util/Hash")} Hash */ /** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */ -/** @typedef {import("./RemoteOverridesModule").OverrideOptions} OverrideOptions */ -const TYPES = new Set(["remote"]); +const TYPES = new Set(["remote", "share-init"]); const RUNTIME_REQUIREMENTS = new Set([RuntimeGlobals.module]); class RemoteModule extends Module { /** * @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 + * @param {string} shareScope the used share scope name */ - constructor(request, overrides, externalRequests, internalRequest) { + constructor(request, externalRequests, internalRequest, shareScope) { super("remote-module"); this.request = request; - this.overrides = overrides; this.externalRequests = externalRequests; this.internalRequest = internalRequest; - const hash = createHash("md4"); - for (const [key, request] of overrides) { - hash.update(key); - hash.update(request.import); - } - this._overridesHash = hash.digest("hex"); - this._identifier = `remote ${this.externalRequests.join(" ")} ${ - this.internalRequest - } ${this._overridesHash}`; + this.shareScope = shareScope; + this._identifier = `remote (${shareScope}) ${this.externalRequests.join( + " " + )} ${this.internalRequest}`; } /** @@ -102,9 +94,13 @@ class RemoteModule extends Module { }; this.clearDependenciesAndBlocks(); - this.addDependency(new RemoteOverridesDependency(this.overrides)); - for (const externalRequest of this.externalRequests) - this.addDependency(new RemoteToExternalDependency(externalRequest)); + if (this.externalRequests.length === 1) { + this.addDependency( + new RemoteToExternalDependency(this.externalRequests[0]) + ); + } else { + this.addDependency(new FallbackDependency(this.externalRequests)); + } callback(); } @@ -114,7 +110,7 @@ class RemoteModule extends Module { * @returns {number} the estimated size of the module (must be non-zero) */ size(type) { - return 42; + return 6; } /** @@ -136,17 +132,27 @@ class RemoteModule extends Module { * @returns {CodeGenerationResult} result */ codeGeneration({ runtimeTemplate, moduleGraph, chunkGraph }) { + const module = moduleGraph.getModule(this.dependencies[0]); + const id = module && chunkGraph.getModuleId(module); const sources = new Map(); sources.set("remote", new RawSource("")); - return { sources, runtimeRequirements: RUNTIME_REQUIREMENTS }; + const data = new Map(); + data.set("share-init", [ + { + shareScope: this.shareScope, + initStage: 20, + init: id === undefined ? "" : `initExternal(${JSON.stringify(id)});` + } + ]); + return { sources, data, runtimeRequirements: RUNTIME_REQUIREMENTS }; } serialize(context) { const { write } = context; write(this.request); - write(this.overrides); write(this.externalRequests); write(this.internalRequest); + write(this.shareScope); super.serialize(context); } diff --git a/lib/container/RemoteOverridesDependency.js b/lib/container/RemoteOverridesDependency.js deleted file mode 100644 index 5ed3e0f74..000000000 --- a/lib/container/RemoteOverridesDependency.js +++ /dev/null @@ -1,46 +0,0 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ - -"use strict"; - -const Dependency = require("../Dependency"); -const makeSerializable = require("../util/makeSerializable"); - -class RemoteOverridesDependency extends Dependency { - constructor(overrides) { - super(); - this.overrides = overrides; - } - - get type() { - return "remote overrides"; - } - - /** - * @returns {string | null} an identifier to merge equal requests - */ - getResourceIdentifier() { - return `remote overrides ${this.overrides - .map(([key, request]) => `${key}=${request}`) - .join()}`; - } - - serialize(context) { - context.write(this.overrides); - super.serialize(context); - } - - deserialize(context) { - this.overrides = context.read(); - super.deserialize(context); - } -} - -makeSerializable( - RemoteOverridesDependency, - "webpack/lib/container/RemoteOverridesDependency" -); - -module.exports = RemoteOverridesDependency; diff --git a/lib/container/RemoteRuntimeModule.js b/lib/container/RemoteRuntimeModule.js index 4da2c216b..008fbf905 100644 --- a/lib/container/RemoteRuntimeModule.js +++ b/lib/container/RemoteRuntimeModule.js @@ -21,7 +21,6 @@ class RemoteRuntimeModule extends RuntimeModule { */ generate() { const { runtimeTemplate, chunkGraph, moduleGraph } = this.compilation; - let hasFallback = false; const chunkToRemotesMapping = {}; const idToExternalAndNameMapping = {}; for (const chunk of this.chunk.getAllAsyncChunks()) { @@ -35,20 +34,13 @@ class RemoteRuntimeModule extends RuntimeModule { const module = /** @type {RemoteModule} */ (m); const name = module.internalRequest; const id = chunkGraph.getModuleId(module); - const overridesModule = moduleGraph.getModule(module.dependencies[0]); - const overridesModuleId = - 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; + const shareScope = module.shareScope; + const dep = module.dependencies[0]; + const externalModule = moduleGraph.getModule(dep); + const externalModuleId = + externalModule && chunkGraph.getModuleId(externalModule); remotes.push(id); - idToExternalAndNameMapping[id] = [ - overridesModuleId, - name, - ...externalModuleIds - ]; + idToExternalAndNameMapping[id] = [shareScope, name, externalModuleId]; } } return Template.asString([ @@ -69,84 +61,58 @@ class RemoteRuntimeModule extends RuntimeModule { `if(${RuntimeGlobals.hasOwnProperty}(chunkMapping, chunkId)) {`, Template.indent([ `chunkMapping[chunkId].forEach(${runtimeTemplate.basicFunction("id", [ - `if(${RuntimeGlobals.hasOwnProperty}(installedModules, id)) return installedModules[id] && promises.push(installedModules[id]);`, + `if(installedModules[id]) return promises.push(installedModules[id]);`, "var data = idToExternalAndNameMapping[id];", `var onError = ${runtimeTemplate.basicFunction("error", [ '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" - }];` + `error.message += '\\nwhile loading "' + data[1] + '" from ' + data[2];` ), `__webpack_modules__[id] = ${runtimeTemplate.basicFunction("", [ "throw error;" ])}`, - "delete installedModules[id];" + "installedModules[id] = 0;" ])};`, - `var onExternal = ${runtimeTemplate.basicFunction( - "external, handlePromise", + `var handleFunction = ${runtimeTemplate.basicFunction( + "fn, key, data, next, first", [ "try {", Template.indent([ - "var promise = __webpack_require__(data[0])(external).get(data[1]);", + "var promise = fn(key, data);", "if(promise && promise.then) {", Template.indent([ - "var p = promise.then(onFactory, onError);", - `if(handlePromise) promises.push(installedModules[id] = p); else return p;` + `var p = promise.then(${runtimeTemplate.returningFunction( + "next(result, data)", + "result" + )}, onError);`, + `if(first) promises.push(installedModules[id] = p); else return p;` ]), "} else {", - Template.indent([`onFactory(promise);`]), + Template.indent(["return next(promise, data, first);"]), "}" ]), "} catch(error) {", Template.indent(["onError(error);"]), "}" ] + )}`, + `var onExternal = ${runtimeTemplate.returningFunction( + `external ? handleFunction(${RuntimeGlobals.initializeSharing}, data[0], external, onInitialized, first) : onError()`, + "external, _, first" + )};`, + `var onInitialized = ${runtimeTemplate.returningFunction( + `handleFunction(external.get, data[1], external, onFactory, first)`, + "_, external, first" )};`, `var onFactory = ${runtimeTemplate.basicFunction("factory", [ - "installedModules[id] = 0;", + "installedModules[id] = 1;", `__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);"]), - "}" - ]) + "handleFunction(__webpack_require__, data[2], 1, onExternal, 1);" ])});` ]), "}" diff --git a/lib/container/options.js b/lib/container/options.js index 5829ab837..8cd9698a0 100644 --- a/lib/container/options.js +++ b/lib/container/options.js @@ -11,8 +11,8 @@ * @template T * @template N * @param {ContainerOptionsFormat} 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 | string[], string) : N} normalizeSimple normalize a simple item + * @param {function(T, string) : N} normalizeOptions normalize a complex item * @param {function(string, N): void} fn processing function * @returns {void} */ @@ -20,7 +20,7 @@ const process = (options, normalizeSimple, normalizeOptions, fn) => { const array = items => { for (const item of items) { if (typeof item === "string") { - fn(item, normalizeSimple(item)); + fn(item, normalizeSimple(item, item)); } else if (item && typeof item === "object") { object(item); } else { @@ -31,9 +31,9 @@ const process = (options, normalizeSimple, normalizeOptions, fn) => { const object = obj => { for (const [key, value] of Object.entries(obj)) { if (typeof value === "string" || Array.isArray(value)) { - fn(key, normalizeSimple(value)); + fn(key, normalizeSimple(value, key)); } else { - fn(key, normalizeOptions(value)); + fn(key, normalizeOptions(value, key)); } } }; @@ -52,8 +52,8 @@ const process = (options, normalizeSimple, normalizeOptions, fn) => { * @template T * @template R * @param {ContainerOptionsFormat} 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 + * @param {function(string | string[], string) : R} normalizeSimple normalize a simple item + * @param {function(T, string) : R} normalizeOptions normalize a complex item * @returns {[string, R][]} parsed options */ const parseOptions = (options, normalizeSimple, normalizeOptions) => { diff --git a/lib/index.js b/lib/index.js index 4a63923cf..f0ceb030d 100644 --- a/lib/index.js +++ b/lib/index.js @@ -390,8 +390,20 @@ module.exports = mergeExports(fn, { get ModuleFederationPlugin() { return require("./container/ModuleFederationPlugin"); }, - get OverridablesPlugin() { - return require("./container/OverridablesPlugin"); + get scope() { + return require("./container/options").scope; + } + }, + + sharing: { + get ConsumeSharedPlugin() { + return require("./sharing/ConsumeSharedPlugin"); + }, + get ProvideSharedPlugin() { + return require("./sharing/ProvideSharedPlugin"); + }, + get SharePlugin() { + return require("./sharing/SharePlugin"); }, get scope() { return require("./container/options").scope; diff --git a/lib/sharing/ConsumeFallbackDependency.js b/lib/sharing/ConsumeFallbackDependency.js new file mode 100644 index 000000000..ed531e257 --- /dev/null +++ b/lib/sharing/ConsumeFallbackDependency.js @@ -0,0 +1,26 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ + +"use strict"; + +const ModuleDependency = require("../dependencies/ModuleDependency"); +const makeSerializable = require("../util/makeSerializable"); + +class ConsumeFallbackDependency extends ModuleDependency { + constructor(request) { + super(request); + } + + get type() { + return "consume fallback"; + } +} + +makeSerializable( + ConsumeFallbackDependency, + "webpack/lib/sharing/ConsumeFallbackDependency" +); + +module.exports = ConsumeFallbackDependency; diff --git a/lib/sharing/ConsumeSharedModule.js b/lib/sharing/ConsumeSharedModule.js new file mode 100644 index 000000000..cbcc6d6f6 --- /dev/null +++ b/lib/sharing/ConsumeSharedModule.js @@ -0,0 +1,239 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ + +"use strict"; + +const { RawSource } = require("webpack-sources"); +const AsyncDependenciesBlock = require("../AsyncDependenciesBlock"); +const Module = require("../Module"); +const RuntimeGlobals = require("../RuntimeGlobals"); +const makeSerializable = require("../util/makeSerializable"); +const ConsumeFallbackDependency = require("./ConsumeFallbackDependency"); +const { versionToString } = require("./utils"); + +/** @typedef {import("../../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions */ +/** @typedef {import("../ChunkGraph")} ChunkGraph */ +/** @typedef {import("../ChunkGroup")} ChunkGroup */ +/** @typedef {import("../Compilation")} Compilation */ +/** @typedef {import("../Module").CodeGenerationContext} CodeGenerationContext */ +/** @typedef {import("../Module").CodeGenerationResult} CodeGenerationResult */ +/** @typedef {import("../Module").LibIdentOptions} LibIdentOptions */ +/** @typedef {import("../Module").NeedBuildContext} NeedBuildContext */ +/** @typedef {import("../RequestShortener")} RequestShortener */ +/** @typedef {import("../ResolverFactory").ResolverWithOptions} ResolverWithOptions */ +/** @typedef {import("../WebpackError")} WebpackError */ +/** @typedef {import("../util/Hash")} Hash */ +/** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */ + +/** + * @typedef {Object} ConsumeOptions + * @property {string=} import fallback request + * @property {string} shareKey global share key + * @property {string} shareScope share scope + * @property {(number|string)[]} requiredVersion version requirement + * @property {boolean} strictVersion don't use shared version even if version isn't valid + * @property {boolean} singleton use single global version + * @property {boolean} eager include the fallback module in a sync way + */ + +const TYPES = new Set(["consume-shared"]); + +class ConsumeSharedModule extends Module { + /** + * @param {string} context context + * @param {ConsumeOptions} options consume options + */ + constructor(context, options) { + super("consume-shared-module", context); + this.options = options; + } + + /** + * @returns {string} a unique identifier of the module + */ + identifier() { + const { + shareKey, + shareScope, + import: request, + requiredVersion, + strictVersion, + singleton, + eager + } = this.options; + return `consume-shared-module|${shareScope}|${shareKey}|${versionToString( + requiredVersion + )}|${strictVersion}|${request}|${singleton}|${eager}`; + } + + /** + * @param {RequestShortener} requestShortener the request shortener + * @returns {string} a user readable identifier of the module + */ + readableIdentifier(requestShortener) { + const { + shareKey, + shareScope, + import: request, + requiredVersion, + strictVersion, + singleton, + eager + } = this.options; + return `shared module (${shareScope}) ${shareKey}@${versionToString( + requiredVersion + )}${strictVersion ? " (strict)" : ""}${singleton ? " (singleton)" : ""}${ + request ? ` -> ${request}` : "" + }${eager ? " (eager)" : ""}`; + } + + /** + * @param {LibIdentOptions} options options + * @returns {string | null} an identifier for library inclusion + */ + libIdent(options) { + const { shareKey, shareScope, import: request } = this.options; + return `webpack/sharing/consume/${shareScope}/${shareKey}${ + request ? `/${request}` : "" + }`; + } + + /** + * @param {NeedBuildContext} context context info + * @param {function(WebpackError=, boolean=): void} callback callback function, returns true, if the module needs a rebuild + * @returns {void} + */ + needBuild(context, callback) { + callback(null, !this.buildInfo); + } + + /** + * @param {WebpackOptions} options webpack options + * @param {Compilation} compilation the compilation + * @param {ResolverWithOptions} resolver the resolver + * @param {InputFileSystem} fs the file system + * @param {function(WebpackError=): void} callback callback function + * @returns {void} + */ + build(options, compilation, resolver, fs, callback) { + this.buildMeta = {}; + this.buildInfo = {}; + if (this.options.import) { + const dep = new ConsumeFallbackDependency(this.options.import); + if (this.options.eager) { + this.addDependency(dep); + } else { + const block = new AsyncDependenciesBlock({}); + block.addDependency(dep); + this.addBlock(block); + } + } + callback(); + } + + /** + * @returns {Set} types available (do not mutate) + */ + getSourceTypes() { + return TYPES; + } + + /** + * @param {string=} type the source type for which the size should be estimated + * @returns {number} the estimated size of the module (must be non-zero) + */ + size(type) { + return 42; + } + + /** + * @param {Hash} hash the hash used to track dependencies + * @param {ChunkGraph} chunkGraph the chunk graph + * @returns {void} + */ + updateHash(hash, chunkGraph) { + hash.update(JSON.stringify(this.options)); + super.updateHash(hash, chunkGraph); + } + + /** + * @param {CodeGenerationContext} context context for code generation + * @returns {CodeGenerationResult} result + */ + codeGeneration({ chunkGraph, moduleGraph, runtimeTemplate }) { + const runtimeRequirements = new Set([RuntimeGlobals.shareScopeMap]); + const { + shareScope, + shareKey, + strictVersion, + requiredVersion, + import: request, + singleton, + eager + } = this.options; + let fallbackCode; + if (request) { + if (eager) { + const dep = this.dependencies[0]; + fallbackCode = runtimeTemplate.syncModuleFactory({ + dependency: dep, + chunkGraph, + runtimeRequirements, + request: this.options.import + }); + } else { + const block = this.blocks[0]; + fallbackCode = runtimeTemplate.asyncModuleFactory({ + block, + chunkGraph, + runtimeRequirements, + request: this.options.import + }); + } + } + let fn = "load"; + const args = [JSON.stringify(shareScope), JSON.stringify(shareKey)]; + if (requiredVersion) { + if (strictVersion) { + fn += "Strict"; + } + if (singleton) { + fn += "Singleton"; + } + args.push(JSON.stringify(requiredVersion)); + fn += "VersionCheck"; + } + if (fallbackCode) { + fn += "Fallback"; + args.push(fallbackCode); + } + const code = runtimeTemplate.returningFunction(`${fn}(${args.join(", ")})`); + const sources = new Map(); + sources.set("consume-shared", new RawSource(code)); + return { + runtimeRequirements, + sources + }; + } + + serialize(context) { + const { write } = context; + write(this.options); + super.serialize(context); + } + + deserialize(context) { + const { read } = context; + this.options = read(); + super.deserialize(context); + } +} + +makeSerializable( + ConsumeSharedModule, + "webpack/lib/sharing/ConsumeSharedModule" +); + +module.exports = ConsumeSharedModule; diff --git a/lib/sharing/ConsumeSharedPlugin.js b/lib/sharing/ConsumeSharedPlugin.js new file mode 100644 index 000000000..5bd48327d --- /dev/null +++ b/lib/sharing/ConsumeSharedPlugin.js @@ -0,0 +1,229 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ + +"use strict"; + +const validateOptions = require("schema-utils"); +const schema = require("../../schemas/plugins/sharing/ConsumeSharedPlugin.json"); +const ModuleNotFoundError = require("../ModuleNotFoundError"); +const RuntimeGlobals = require("../RuntimeGlobals"); +const { parseOptions } = require("../container/options"); +const LazySet = require("../util/LazySet"); +const ConsumeFallbackDependency = require("./ConsumeFallbackDependency"); +const ConsumeSharedModule = require("./ConsumeSharedModule"); +const ConsumeSharedRuntimeModule = require("./ConsumeSharedRuntimeModule"); +const ProvidedDependency = require("./ProvidedDependency"); +const { parseRequiredVersion, isRequiredVersion } = require("./utils"); + +/** @typedef {import("enhanced-resolve").ResolveContext} ResolveContext */ +/** @typedef {import("../../declarations/plugins/sharing/ConsumeSharedPlugin").ConsumeSharedPluginOptions} ConsumeSharedPluginOptions */ +/** @typedef {import("../../declarations/plugins/sharing/ConsumeSharedPlugin").ConsumesConfig} ConsumesConfig */ +/** @typedef {import("../Compiler")} Compiler */ +/** @typedef {import("./ConsumeSharedModule").ConsumeOptions} ConsumeOptions */ + +const PLUGIN_NAME = "ConsumeSharedPlugin"; + +class ConsumeSharedPlugin { + /** + * @param {ConsumeSharedPluginOptions} options options + */ + constructor(options) { + if (typeof options !== "string") { + validateOptions(schema, options, { name: "Consumes Shared Plugin" }); + } + + /** @type {[string, ConsumeOptions][]} */ + this._consumes = parseOptions( + options.consumes, + (item, key) => { + if (Array.isArray(item)) throw new Error("Unexpected array in options"); + /** @type {ConsumeOptions} */ + let result = + item === key || !isRequiredVersion(item) + ? // item is a request/key + { + import: key, + shareScope: options.shareScope || "default", + shareKey: key, + requiredVersion: undefined, + strictVersion: false, + singleton: false, + eager: false + } + : // key is a request/key + // item is a version + { + import: key, + shareScope: options.shareScope || "default", + shareKey: key, + requiredVersion: parseRequiredVersion(item), + strictVersion: true, + singleton: false, + eager: false + }; + return result; + }, + (item, key) => ({ + import: item.import === false ? undefined : item.import || key, + shareScope: item.shareScope || options.shareScope || "default", + shareKey: item.shareKey || key, + requiredVersion: + typeof item.requiredVersion === "string" + ? parseRequiredVersion(item.requiredVersion) + : item.requiredVersion, + strictVersion: + item.requiredVersion && + (typeof item.strictVersion === "boolean" + ? item.strictVersion + : item.import !== false && !item.singleton), + singleton: !!item.singleton, + eager: !!item.eager + }) + ); + } + + /** + * Apply the plugin + * @param {Compiler} compiler the compiler instance + * @returns {void} + */ + apply(compiler) { + compiler.hooks.thisCompilation.tap( + PLUGIN_NAME, + (compilation, { normalModuleFactory }) => { + compilation.dependencyFactories.set( + ConsumeFallbackDependency, + normalModuleFactory + ); + + /** @type {Map} */ + const resolvedConsumes = new Map(); + /** @type {Map} */ + const unresolvedConsumes = new Map(); + /** @type {Map} */ + const prefixConsumes = new Map(); + const resolveContext = { + /** @type {LazySet} */ + fileDependencies: new LazySet(), + /** @type {LazySet} */ + contextDependencies: new LazySet(), + /** @type {LazySet} */ + missingDependencies: new LazySet() + }; + const resolver = compilation.resolverFactory.get("normal"); + /** + * @param {string} request imported request + * @param {ConsumeOptions} options options + * @returns {Promise} promise + */ + const resolveConsume = (request, options) => { + if (/^\.\.?(\/|$)/.test(request)) { + // relative request + return new Promise(resolve => { + resolver.resolve( + {}, + compiler.context, + request, + resolveContext, + (err, result) => { + if (err) { + compilation.errors.push( + new ModuleNotFoundError(null, err, { + name: `consumed shared module ${request}` + }) + ); + return resolve(); + } + resolvedConsumes.set(result, options); + resolve(); + } + ); + }); + } else if (/^(\/|[A-Za-z]:\\|\\\\)/.test(request)) { + // absolute path + resolvedConsumes.set(request, options); + } else if (request.endsWith("/")) { + // module request prefix + prefixConsumes.set(request, options); + } else { + // module request + unresolvedConsumes.set(request, options); + } + }; + const promise = Promise.all( + this._consumes.map(([name, config]) => resolveConsume(name, config)) + ).then(() => { + compilation.contextDependencies.addAll( + resolveContext.contextDependencies + ); + compilation.fileDependencies.addAll(resolveContext.fileDependencies); + compilation.missingDependencies.addAll( + resolveContext.missingDependencies + ); + }); + normalModuleFactory.hooks.factorize.tapPromise( + PLUGIN_NAME, + ({ context, request, dependencies }) => + // wait for resolving to be complete + promise.then(() => { + if ( + dependencies[0] instanceof ConsumeFallbackDependency || + dependencies[0] instanceof ProvidedDependency + ) { + return; + } + const match = unresolvedConsumes.get(request); + if (match !== undefined) { + return new ConsumeSharedModule(compiler.context, match); + } + for (const [prefix, options] of prefixConsumes) { + if (request.startsWith(prefix)) { + const remainder = request.slice(prefix.length); + return new ConsumeSharedModule(compiler.context, { + ...options, + import: options.import + ? options.import + remainder + : undefined, + shareKey: options.shareKey + remainder + }); + } + } + }) + ); + normalModuleFactory.hooks.module.tap( + PLUGIN_NAME, + (module, createData, { context, request, dependencies }) => { + if ( + dependencies[0] instanceof ConsumeFallbackDependency || + dependencies[0] instanceof ProvidedDependency + ) { + return; + } + const options = resolvedConsumes.get(createData.resource); + if (options !== undefined) { + return new ConsumeSharedModule(compiler.context, options); + } + } + ); + compilation.hooks.additionalTreeRuntimeRequirements.tap( + PLUGIN_NAME, + (chunk, set) => { + set.add(RuntimeGlobals.module); + set.add(RuntimeGlobals.moduleFactoriesAddOnly); + set.add(RuntimeGlobals.shareScopeMap); + set.add(RuntimeGlobals.initializeSharing); + set.add(RuntimeGlobals.hasOwnProperty); + compilation.addRuntimeModule( + chunk, + new ConsumeSharedRuntimeModule(set) + ); + } + ); + } + ); + } +} + +module.exports = ConsumeSharedPlugin; diff --git a/lib/sharing/ConsumeSharedRuntimeModule.js b/lib/sharing/ConsumeSharedRuntimeModule.js new file mode 100644 index 000000000..6b0f7e83e --- /dev/null +++ b/lib/sharing/ConsumeSharedRuntimeModule.js @@ -0,0 +1,294 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ + +"use strict"; + +const RuntimeGlobals = require("../RuntimeGlobals"); +const RuntimeModule = require("../RuntimeModule"); +const Template = require("../Template"); + +/** @typedef {import("./ConsumeSharedModule")} ConsumeSharedModule */ + +class ConsumeSharedRuntimeModule extends RuntimeModule { + constructor(runtimeRequirements) { + super("consumes", 10); + this._runtimeRequirements = runtimeRequirements; + } + + /** + * @returns {string} runtime code + */ + generate() { + const { + runtimeTemplate, + chunkGraph, + codeGenerationResults + } = this.compilation; + const chunkToModuleMapping = {}; + const idToModuleMapping = new Map(); + const initialConsumes = []; + for (const chunk of this.chunk.getAllAsyncChunks()) { + const modules = chunkGraph.getChunkModulesIterableBySourceType( + chunk, + "consume-shared" + ); + if (!modules) continue; + const consumes = (chunkToModuleMapping[chunk.id] = []); + for (const m of modules) { + const module = /** @type {ConsumeSharedModule} */ (m); + const id = chunkGraph.getModuleId(module); + consumes.push(id); + idToModuleMapping.set(id, module); + } + } + for (const chunk of this.chunk.getAllInitialChunks()) { + const modules = chunkGraph.getChunkModulesIterableBySourceType( + chunk, + "consume-shared" + ); + if (!modules) continue; + for (const m of modules) { + const module = /** @type {ConsumeSharedModule} */ (m); + const id = chunkGraph.getModuleId(module); + initialConsumes.push(id); + idToModuleMapping.set(id, module); + } + } + if (idToModuleMapping.size === 0) return null; + return Template.asString([ + `var ensureExistence = ${runtimeTemplate.basicFunction( + "scope, scopeName, key", + `if(!scope || !${RuntimeGlobals.hasOwnProperty}(scope, key)) throw new Error("Shared module " + key + " doesn't exist in shared scope " + scopeName);` + )};`, + `var invalidVersion = ${runtimeTemplate.basicFunction( + "version, requiredVersion", + [ + "for(var i = 0; i < requiredVersion.length; i++) {", + Template.indent([ + "if(i === version.length) return 1;", + "if(version[i] != requiredVersion[i]) { // loose equal is intentional to match string and number", + Template.indent([ + 'if(typeof version[i] === "string" || typeof requiredVersion[i] === "string" || version[i] < requiredVersion[i]) return 1;', + "if(version[i] > requiredVersion[i]) return;" + ]), + "}" + ]), + "}" + ] + )};`, + `var checkSingletonVersion = ${runtimeTemplate.basicFunction( + "key, version, requiredVersion, strict", + [ + "if(!invalidVersion(version, requiredVersion)) return 1;", + 'var msg = "Unsatisfied version of shared singleton module " + key + "@" + (version && version.join(".")) + " (required " + key + "@" + requiredVersion.join(".") + ")";', + "if(strict) throw new Error(msg);", + 'typeof console !== "undefined" && console.warn && console.warn(msg);' + ] + )};`, + `var findVersion = ${runtimeTemplate.basicFunction( + "scope, key, requiredVersion, strict", + [ + "requiredVersion = requiredVersion || [];", + "var currentName = key;", + `var versions = requiredVersion.map(${runtimeTemplate.returningFunction( + 'currentName += "`" + v', + "v" + )});`, + "versions.unshift(key);", + "var lastVersion;", + "while(currentName = versions.shift()) {", + Template.indent([ + `if(${RuntimeGlobals.hasOwnProperty}(scope, currentName) && !invalidVersion(lastVersion = scope[currentName].version || [], requiredVersion)) return scope[currentName];` + ]), + "}", + 'var msg = "Unsatisfied version of shared module " + key + "@" + (lastVersion && lastVersion.join(".")) + " (required " + key + "@" + requiredVersion.join(".") + ")";', + "if(strict) throw new Error(msg);", + 'typeof console !== "undefined" && console.warn && console.warn(msg);' + ] + )};`, + `var get = ${runtimeTemplate.returningFunction( + "(sharedModule.loaded = 1, sharedModule.get())", + "sharedModule" + )};`, + `var load = ${runtimeTemplate.basicFunction("scopeName, key", [ + `${RuntimeGlobals.initializeSharing}(scopeName);`, + `var scope = ${RuntimeGlobals.shareScopeMap}[scopeName];`, + `ensureExistence(scope, scopeName, key);`, + "return get(scope[key]);" + ])};`, + `var loadFallback = ${runtimeTemplate.basicFunction( + "scopeName, key, fallback", + [ + `${RuntimeGlobals.initializeSharing}(scopeName);`, + `var scope = ${RuntimeGlobals.shareScopeMap}[scopeName];`, + `return scope && ${RuntimeGlobals.hasOwnProperty}(scope, key) ? get(scope[key]) : fallback();` + ] + )};`, + `var loadVersionCheck = ${runtimeTemplate.basicFunction( + "scopeName, key, version", + [ + `${RuntimeGlobals.initializeSharing}(scopeName);`, + `var scope = ${RuntimeGlobals.shareScopeMap}[scopeName];`, + "ensureExistence(scope, scopeName, key);", + "return get(findVersion(scope, key, version) || scope[key]);" + ] + )};`, + `var loadSingletonVersionCheck = ${runtimeTemplate.basicFunction( + "scopeName, key, version", + [ + `${RuntimeGlobals.initializeSharing}(scopeName);`, + `var scope = ${RuntimeGlobals.shareScopeMap}[scopeName];`, + "ensureExistence(scope, scopeName, key);", + "checkSingletonVersion(key, scope[key].version, version);", + "return get(scope[key]);" + ] + )};`, + `var loadStrictVersionCheck = ${runtimeTemplate.basicFunction( + "scopeName, key, version", + [ + `${RuntimeGlobals.initializeSharing}(scopeName);`, + `var scope = ${RuntimeGlobals.shareScopeMap}[scopeName];`, + "ensureExistence(scope, scopeName, key);", + "return get(findVersion(scope, key, version, 1));" + ] + )};`, + `var loadStrictSingletonVersionCheck = ${runtimeTemplate.basicFunction( + "scopeName, key, version", + [ + `${RuntimeGlobals.initializeSharing}(scopeName);`, + `var scope = ${RuntimeGlobals.shareScopeMap}[scopeName];`, + "ensureExistence(scope, scopeName, key);", + "checkSingletonVersion(key, scope[key].version, version, 1);", + "return get(scope[key]);" + ] + )};`, + `var loadVersionCheckFallback = ${runtimeTemplate.basicFunction( + "scopeName, key, version, fallback", + [ + `${RuntimeGlobals.initializeSharing}(scopeName);`, + `var scope = ${RuntimeGlobals.shareScopeMap}[scopeName];`, + `if(!scope || !${RuntimeGlobals.hasOwnProperty}(scope, key)) return fallback();`, + "return get(findVersion(scope, key, version) || scope[key]);" + ] + )};`, + `var loadSingletonVersionCheckFallback = ${runtimeTemplate.basicFunction( + "scopeName, key, version, fallback", + [ + `${RuntimeGlobals.initializeSharing}(scopeName);`, + `var scope = ${RuntimeGlobals.shareScopeMap}[scopeName];`, + `if(!scope || !${RuntimeGlobals.hasOwnProperty}(scope, key)) return fallback();`, + "checkSingletonVersion(key, scope[key].version, version);", + "return get(scope[key]);" + ] + )};`, + `var loadStrictVersionCheckFallback = ${runtimeTemplate.basicFunction( + "scopeName, key, version, fallback", + [ + `${RuntimeGlobals.initializeSharing}(scopeName);`, + `var scope = ${RuntimeGlobals.shareScopeMap}[scopeName];`, + "var entry = scope && findVersion(scope, key, version);", + `return entry ? get(entry) : fallback();` + ] + )};`, + `var loadStrictSingletonVersionCheckFallback = ${runtimeTemplate.basicFunction( + "scopeName, key, version, fallback", + [ + `${RuntimeGlobals.initializeSharing}(scopeName);`, + `var scope = ${RuntimeGlobals.shareScopeMap}[scopeName];`, + `if(!scope || !${RuntimeGlobals.hasOwnProperty}(scope, key)) return fallback();`, + "checkSingletonVersion(key, scope[key].version, version, 1);", + "return get(scope[key]);" + ] + )};`, + "var installedModules = {};", + "var moduleToHandlerMapping = {", + Template.indent( + Array.from( + idToModuleMapping, + ([key, module]) => + `${JSON.stringify(key)}: ${codeGenerationResults + .get(module) + .sources.get("consume-shared") + .source()}` + ).join(",\n") + ), + "};", + + initialConsumes.length > 0 + ? Template.asString([ + `var initialConsumes = ${JSON.stringify(initialConsumes)};`, + `initialConsumes.forEach(${runtimeTemplate.basicFunction("id", [ + `__webpack_modules__[id] = ${runtimeTemplate.basicFunction( + "module", + [ + "// Handle case when module is used sync", + "installedModules[id] = 0;", + "delete __webpack_module_cache__[id];", + "var factory = moduleToHandlerMapping[id]();", + 'if(typeof factory !== "function") throw new Error("Shared module is not available for eager consumption: " + id);', + `module.exports = factory();` + ] + )}` + ])});` + ]) + : "// no consumes in initial chunks", + this._runtimeRequirements.has(RuntimeGlobals.ensureChunkHandlers) + ? Template.asString([ + `var chunkMapping = ${JSON.stringify( + chunkToModuleMapping, + null, + "\t" + )};`, + `${ + RuntimeGlobals.ensureChunkHandlers + }.consumes = ${runtimeTemplate.basicFunction("chunkId, promises", [ + `if(${RuntimeGlobals.hasOwnProperty}(chunkMapping, chunkId)) {`, + Template.indent([ + `chunkMapping[chunkId].forEach(${runtimeTemplate.basicFunction( + "id", + [ + `if(${RuntimeGlobals.hasOwnProperty}(installedModules, id)) return promises.push(installedModules[id]);`, + `var onFactory = ${runtimeTemplate.basicFunction( + "factory", + [ + "installedModules[id] = 0;", + `__webpack_modules__[id] = ${runtimeTemplate.basicFunction( + "module", + [ + "delete __webpack_module_cache__[id];", + "module.exports = factory();" + ] + )}` + ] + )};`, + `var onError = ${runtimeTemplate.basicFunction("error", [ + "delete installedModules[id];", + `__webpack_modules__[id] = ${runtimeTemplate.basicFunction( + "module", + ["delete __webpack_module_cache__[id];", "throw error;"] + )}` + ])};`, + "try {", + Template.indent([ + "var promise = moduleToHandlerMapping[id]();", + "if(promise.then) {", + Template.indent( + `promises.push(installedModules[id] = promise.then(onFactory).catch(onError));` + ), + "} else onFactory(promise);" + ]), + "} catch(e) { onError(e); }" + ] + )});` + ]), + "}" + ])}` + ]) + : "// no chunk loading of consumes" + ]); + } +} + +module.exports = ConsumeSharedRuntimeModule; diff --git a/lib/sharing/ProvideDependency.js b/lib/sharing/ProvideDependency.js new file mode 100644 index 000000000..653042b6e --- /dev/null +++ b/lib/sharing/ProvideDependency.js @@ -0,0 +1,54 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ + +"use strict"; + +const Dependency = require("../Dependency"); +const makeSerializable = require("../util/makeSerializable"); + +class ProvideDependency extends Dependency { + constructor(shareScope, name, version, request, eager) { + super(); + this.shareScope = shareScope; + this.name = name; + this.version = version; + this.request = request; + this.eager = eager; + } + + get type() { + return "provide module"; + } + + /** + * @returns {string | null} an identifier to merge equal requests + */ + getResourceIdentifier() { + return `provide module (${this.shareScope}) ${this.request} as ${ + this.name + } @ ${this.version}${this.eager ? " (eager)" : ""}`; + } + + serialize(context) { + context.write(this.shareScope); + context.write(this.name); + context.write(this.request); + context.write(this.version); + context.write(this.eager); + super.serialize(context); + } + + static deserialize(context) { + const { read } = context; + const obj = new ProvideDependency(read(), read(), read(), read(), read()); + this.shareScope = context.read(); + obj.deserialize(context); + return obj; + } +} + +makeSerializable(ProvideDependency, "webpack/lib/sharing/ProvideDependency"); + +module.exports = ProvideDependency; diff --git a/lib/container/RemoteOverridesModule.js b/lib/sharing/ProvideModule.js similarity index 55% rename from lib/container/RemoteOverridesModule.js rename to lib/sharing/ProvideModule.js index 4457c471d..878c4c0e1 100644 --- a/lib/container/RemoteOverridesModule.js +++ b/lib/sharing/ProvideModule.js @@ -5,14 +5,11 @@ "use strict"; -const { OriginalSource } = require("webpack-sources"); const AsyncDependenciesBlock = require("../AsyncDependenciesBlock"); const Module = require("../Module"); const RuntimeGlobals = require("../RuntimeGlobals"); -const Template = require("../Template"); -const createHash = require("../util/createHash"); const makeSerializable = require("../util/makeSerializable"); -const RemoteOverrideDependency = require("./RemoteOverrideDependency"); +const ProvidedDependency = require("./ProvidedDependency"); /** @typedef {import("../../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions */ /** @typedef {import("../Chunk")} Chunk */ @@ -29,37 +26,32 @@ const RemoteOverrideDependency = require("./RemoteOverrideDependency"); /** @typedef {import("../util/Hash")} Hash */ /** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */ -/** - * @typedef {Object} OverrideOptions - * @property {string} import request to new module - */ +const TYPES = new Set(["share-init"]); -const TYPES = new Set(["javascript"]); - -class RemoteOverridesModule extends Module { +class ProvideModule extends Module { /** - * @param {[string, OverrideOptions][]} overrides list of overrides + * @param {string} shareScope shared scope name + * @param {string} name shared key + * @param {(string | number)[]} version version + * @param {string} request request to the provided module + * @param {boolean} eager include the module in sync way */ - constructor(overrides) { - super("remote-overrides-module"); - this._overrides = overrides; - if (overrides.length > 0) { - const hash = createHash("md4"); - for (const [key, request] of overrides) { - hash.update(key); - hash.update(request.import); - } - this._overridesHash = hash.digest("hex"); - } else { - this._overridesHash = "empty"; - } + constructor(shareScope, name, version, request, eager) { + super("provide-module"); + this._shareScope = shareScope; + this._name = name; + this._version = version; + this._request = request; + this._eager = eager; } /** * @returns {string} a unique identifier of the module */ identifier() { - return `remote overrides ${this._overridesHash}`; + return `provide module (${this._shareScope}) ${this._name}@${ + this._version && this._version.join(".") + } = ${this._request}`; } /** @@ -67,9 +59,9 @@ class RemoteOverridesModule extends Module { * @returns {string} a user readable identifier of the module */ readableIdentifier(requestShortener) { - return `remote overrides ${this._overrides - .map(([key, request]) => `${key} = ${request}`) - .join(", ")}`; + return `provide module (${this._shareScope}) ${this._name}@${ + this._version && this._version.join(".") + } = ${this._request}`; } /** @@ -77,10 +69,7 @@ class RemoteOverridesModule extends Module { * @returns {string | null} an identifier for library inclusion */ libIdent(options) { - return `webpack/container/remote-overrides/${this._overridesHash.slice( - 0, - 6 - )}`; + return `webpack/sharing/provide/${this._shareScope}/${this._name}`; } /** @@ -107,9 +96,11 @@ class RemoteOverridesModule extends Module { }; this.clearDependenciesAndBlocks(); - for (const [, options] of this._overrides) { + const dep = new ProvidedDependency(this._request); + if (this._eager) { + this.addDependency(dep); + } else { const block = new AsyncDependenciesBlock({}); - const dep = new RemoteOverrideDependency(options.import); block.addDependency(dep); this.addBlock(block); } @@ -146,81 +137,54 @@ class RemoteOverridesModule extends Module { * @returns {CodeGenerationResult} result */ codeGeneration({ runtimeTemplate, moduleGraph, chunkGraph }) { - const runtimeRequirements = new Set([RuntimeGlobals.module]); - let i = 0; - const source = Template.asString([ - `module.exports = ${runtimeTemplate.basicFunction( - "external", - this._overrides.length > 0 - ? [ - "if(external.override) external.override(Object.assign({", - Template.indent( - this._overrides - .map(([key, options]) => { - const block = this.blocks[i++]; - const dep = block.dependencies[0]; - const module = moduleGraph.getModule(dep); - return `${JSON.stringify( - key - )}: ${runtimeTemplate.basicFunction( - "", - `return ${runtimeTemplate.blockPromise({ - block, - message: "", - chunkGraph, - runtimeRequirements - })}.then(${runtimeTemplate.basicFunction( - "", - `return ${runtimeTemplate.returningFunction( - runtimeTemplate.moduleRaw({ - module, - chunkGraph, - request: options.import, - runtimeRequirements - }) - )}` - )})` - )}`; - }) - .join(",\n") - ), - `}, ${RuntimeGlobals.overrides}));`, - "return external;" - ] - : [ - `if(external.override) external.override(${RuntimeGlobals.overrides});`, - "return external;" - ] - )};` - ]); + const runtimeRequirements = new Set([RuntimeGlobals.initializeSharing]); + const code = `register(${JSON.stringify(this._name)}, ${JSON.stringify( + this._version || 0 + )}, ${ + this._eager + ? runtimeTemplate.syncModuleFactory({ + dependency: this.dependencies[0], + chunkGraph, + request: this._request, + runtimeRequirements + }) + : runtimeTemplate.asyncModuleFactory({ + block: this.blocks[0], + chunkGraph, + request: this._request, + runtimeRequirements + }) + });`; const sources = new Map(); - sources.set( - "javascript", - new OriginalSource( - source, - `webpack/remote-overrides ${this._overridesHash}` - ) - ); - return { sources, runtimeRequirements }; + const data = new Map(); + data.set("share-init", [ + { + shareScope: this._shareScope, + initStage: 10, + init: code + } + ]); + return { sources, data, runtimeRequirements }; } serialize(context) { const { write } = context; - write(this._overrides); + write(this._shareScope); + write(this._name); + write(this._version); + write(this._request); + write(this._eager); super.serialize(context); } static deserialize(context) { const { read } = context; - const obj = new RemoteOverridesModule(read()); + const obj = new ProvideModule(read(), read(), read(), read(), read()); obj.deserialize(context); return obj; } } -makeSerializable( - RemoteOverridesModule, - "webpack/lib/container/RemoteOverridesModule" -); +makeSerializable(ProvideModule, "webpack/lib/sharing/ProvideModule"); -module.exports = RemoteOverridesModule; +module.exports = ProvideModule; diff --git a/lib/container/RemoteOverridesModuleFactory.js b/lib/sharing/ProvideModuleFactory.js similarity index 60% rename from lib/container/RemoteOverridesModuleFactory.js rename to lib/sharing/ProvideModuleFactory.js index a54c9a38d..04e1e9f9f 100644 --- a/lib/container/RemoteOverridesModuleFactory.js +++ b/lib/sharing/ProvideModuleFactory.js @@ -6,24 +6,30 @@ "use strict"; const ModuleFactory = require("../ModuleFactory"); -const RemoteOverridesModule = require("./RemoteOverridesModule"); +const ProvideModule = require("./ProvideModule"); /** @typedef {import("../ModuleFactory").ModuleFactoryCreateData} ModuleFactoryCreateData */ /** @typedef {import("../ModuleFactory").ModuleFactoryResult} ModuleFactoryResult */ -/** @typedef {import("./RemoteOverridesDependency")} RemoteOverridesDependency */ +/** @typedef {import("./ProvideDependency")} ProvideDependency */ -class RemoteOverridesModuleFactory extends ModuleFactory { +class ProvideModuleFactory extends ModuleFactory { /** * @param {ModuleFactoryCreateData} data data object * @param {function(Error=, ModuleFactoryResult=): void} callback callback * @returns {void} */ create(data, callback) { - const dep = /** @type {RemoteOverridesDependency} */ (data.dependencies[0]); + const dep = /** @type {ProvideDependency} */ (data.dependencies[0]); callback(null, { - module: new RemoteOverridesModule(dep.overrides) + module: new ProvideModule( + dep.shareScope, + dep.name, + dep.version, + dep.request, + dep.eager + ) }); } } -module.exports = RemoteOverridesModuleFactory; +module.exports = ProvideModuleFactory; diff --git a/lib/sharing/ProvideSharedPlugin.js b/lib/sharing/ProvideSharedPlugin.js new file mode 100644 index 000000000..5b64e994b --- /dev/null +++ b/lib/sharing/ProvideSharedPlugin.js @@ -0,0 +1,185 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra and Zackary Jackson @ScriptedAlchemy +*/ + +"use strict"; + +const validateOptions = require("schema-utils"); +const schema = require("../../schemas/plugins/sharing/ProvideSharedPlugin.json"); +const WebpackError = require("../WebpackError"); +const { parseOptions } = require("../container/options"); +const LazySet = require("../util/LazySet"); +const ProvideDependency = require("./ProvideDependency"); +const ProvideModuleFactory = require("./ProvideModuleFactory"); +const ProvidedDependency = require("./ProvidedDependency"); +const { parseVersion } = require("./utils"); + +/** @typedef {import("../../declarations/plugins/sharing/ProvideSharedPlugin").ProvideSharedPluginOptions} ProvideSharedPluginOptions */ +/** @typedef {import("../Compiler")} Compiler */ + +/** + * @typedef {Object} ProvideOptions + * @property {string} import + * @property {string} shareScope + * @property {(string|number)[] | undefined | false} version + * @property {boolean} eager + */ + +class ProvideSharedPlugin { + /** + * @param {ProvideSharedPluginOptions} options options + */ + constructor(options) { + validateOptions(schema, options, { name: "Provide Shared Plugin" }); + + /** @type {[string, ProvideOptions][]} */ + this._provides = parseOptions( + options.provides, + item => { + if (Array.isArray(item)) + throw new Error("Unexpected array of provides"); + /** @type {ProvideOptions} */ + const result = { + import: item, + version: undefined, + shareScope: options.shareScope || "default", + eager: false + }; + return result; + }, + item => ({ + import: item.import, + version: + typeof item.version === "string" + ? parseVersion(item.version) + : item.version, + shareScope: item.shareScope || options.shareScope || "default", + eager: !!item.eager + }) + ); + this._provides.sort(([a], [b]) => { + if (a < b) return -1; + if (b < a) return 1; + return 0; + }); + } + + /** + * Apply the plugin + * @param {Compiler} compiler the compiler instance + * @returns {void} + */ + apply(compiler) { + const { _provides: provides } = this; + + for (const [name, config] of provides) { + compiler.hooks.make.tapAsync( + "ProvideSharedPlugin", + (compilation, callback) => { + let version = config.version; + const addModule = () => { + compilation.addInclude( + compiler.context, + new ProvideDependency( + config.shareScope, + name, + version || false, + config.import, + config.eager + ), + { + name: undefined + }, + err => callback(err) + ); + }; + if ( + version !== undefined || + config.import.startsWith("./") || + config.import.startsWith("../") + ) { + return addModule(); + } + const resolveContext = { + /** @type {LazySet} */ + fileDependencies: new LazySet(), + /** @type {LazySet} */ + contextDependencies: new LazySet(), + /** @type {LazySet} */ + missingDependencies: new LazySet() + }; + const resolver = compiler.resolverFactory.get("normal"); + resolver.resolve( + {}, + compiler.context, + config.import, + resolveContext, + (err, result, additionalInfo) => { + compilation.fileDependencies.addAll( + resolveContext.fileDependencies + ); + compilation.contextDependencies.addAll( + resolveContext.contextDependencies + ); + compilation.missingDependencies.addAll( + resolveContext.missingDependencies + ); + let details; + if (err) { + details = `Failed to resolve: ${err}.`; + } else if (!result) { + details = `Resolved to void.`; + } else if (!additionalInfo) { + details = `No additional info provided from resolver.`; + } else { + const info = /** @type {any} */ (additionalInfo); + const descriptionFileData = info.descriptionFileData; + if (!descriptionFileData) { + details = + "No description file (usually package.json) found. Add description file with name and version, or manually specify version in shared config."; + } else if (!descriptionFileData.version) { + details = + "No version in description file (usually package.json). Add version to description file, or manually specify version in shared config."; + } else if ( + descriptionFileData.name && + config.import !== descriptionFileData.name && + !config.import.startsWith(`${descriptionFileData.name}/`) + ) { + details = `Invalid name in description file (usually package.json): ${descriptionFileData.name}. Check location of description file, update name in description file, add missing description file to the package, or manually specify version in shared config.`; + } else { + version = parseVersion(descriptionFileData.version); + } + } + if (!version) { + const error = new WebpackError( + `No version specified and unable to automatically determine one. ${details}` + ); + error.file = `shared module ${name} -> ${config.import}`; + compilation.warnings.push(error); + } + addModule(); + } + ); + } + ); + } + + compiler.hooks.compilation.tap( + "ProvideSharedPlugin", + (compilation, { normalModuleFactory }) => { + compilation.dependencyFactories.set( + ProvidedDependency, + normalModuleFactory + ); + + compilation.dependencyFactories.set( + ProvideDependency, + new ProvideModuleFactory() + ); + } + ); + } +} + +module.exports = ProvideSharedPlugin; diff --git a/lib/container/RemoteOverrideDependency.js b/lib/sharing/ProvidedDependency.js similarity index 54% rename from lib/container/RemoteOverrideDependency.js rename to lib/sharing/ProvidedDependency.js index da0e6e626..be44bfdf6 100644 --- a/lib/container/RemoteOverrideDependency.js +++ b/lib/sharing/ProvidedDependency.js @@ -8,9 +8,7 @@ const ModuleDependency = require("../dependencies/ModuleDependency"); const makeSerializable = require("../util/makeSerializable"); -/** @typedef {import("./RemoteOverridesModule").OverrideOptions} OverrideOptions */ - -class RemoteOverrideDependency extends ModuleDependency { +class ProvidedDependency extends ModuleDependency { /** * * @param {string} request request string @@ -20,13 +18,10 @@ class RemoteOverrideDependency extends ModuleDependency { } get type() { - return "remote override"; + return "provided"; } } -makeSerializable( - RemoteOverrideDependency, - "webpack/lib/container/RemoteOverrideDependency" -); +makeSerializable(ProvidedDependency, "webpack/lib/sharing/ProvidedDependency"); -module.exports = RemoteOverrideDependency; +module.exports = ProvidedDependency; diff --git a/lib/sharing/SharePlugin.js b/lib/sharing/SharePlugin.js new file mode 100644 index 000000000..30762971a --- /dev/null +++ b/lib/sharing/SharePlugin.js @@ -0,0 +1,89 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra and Zackary Jackson @ScriptedAlchemy +*/ + +"use strict"; + +const { parseOptions } = require("../container/options"); +const ConsumeSharedPlugin = require("./ConsumeSharedPlugin"); +const ProvideSharedPlugin = require("./ProvideSharedPlugin"); +const { isRequiredVersion } = require("./utils"); + +/** @typedef {import("../../declarations/plugins/sharing/ConsumeSharedPlugin").ConsumeSharedPluginOptions} ConsumeSharedPluginOptions */ +/** @typedef {import("../../declarations/plugins/sharing/ConsumeSharedPlugin").ConsumesConfig} ConsumesConfig */ +/** @typedef {import("../../declarations/plugins/sharing/ProvideSharedPlugin").ProvideSharedPluginOptions} ProvideSharedPluginOptions */ +/** @typedef {import("../../declarations/plugins/sharing/ProvideSharedPlugin").ProvidesConfig} ProvidesConfig */ +/** @typedef {import("../../declarations/plugins/sharing/SharePlugin").SharePluginOptions} SharePluginOptions */ +/** @typedef {import("../../declarations/plugins/sharing/SharePlugin").SharedConfig} SharedConfig */ +/** @typedef {import("../Compiler")} Compiler */ + +class SharePlugin { + /** + * @param {SharePluginOptions} options options + */ + constructor(options) { + /** @type {[string, SharedConfig][]} */ + const sharedOptions = parseOptions( + options.shared, + (item, key) => { + if (typeof item !== "string") + throw new Error("Unexpected array in shared"); + /** @type {SharedConfig} */ + const config = + item === key || !isRequiredVersion(item) + ? { + import: item + } + : { + import: key, + requiredVersion: item + }; + return config; + }, + item => item + ); + /** @type {Record[]} */ + const consumes = sharedOptions.map(([key, options]) => ({ + [key]: { + import: options.import, + shareKey: options.shareKey, + requiredVersion: options.requiredVersion, + strictVersion: options.strictVersion, + singleton: options.singleton, + eager: options.eager + } + })); + /** @type {Record[]} */ + const provides = sharedOptions + .filter(([, options]) => options.import !== false) + .map(([key, options]) => ({ + [options.shareKey || key]: { + import: options.import || key, + version: options.version, + eager: options.eager + } + })); + this._shareScope = options.shareScope; + this._consumes = consumes; + this._provides = provides; + } + + /** + * Apply the plugin + * @param {Compiler} compiler the compiler instance + * @returns {void} + */ + apply(compiler) { + new ConsumeSharedPlugin({ + shareScope: this._shareScope, + consumes: this._consumes + }).apply(compiler); + new ProvideSharedPlugin({ + shareScope: this._shareScope, + provides: this._provides + }).apply(compiler); + } +} + +module.exports = SharePlugin; diff --git a/lib/sharing/ShareRuntimeModule.js b/lib/sharing/ShareRuntimeModule.js new file mode 100644 index 000000000..f79dcdafd --- /dev/null +++ b/lib/sharing/ShareRuntimeModule.js @@ -0,0 +1,152 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ + +"use strict"; + +const RuntimeGlobals = require("../RuntimeGlobals"); +const RuntimeModule = require("../RuntimeModule"); +const Template = require("../Template"); + +class ShareRuntimeModule extends RuntimeModule { + constructor() { + super("sharing"); + } + + /** + * @returns {string} runtime code + */ + generate() { + const { + runtimeTemplate, + chunkGraph, + codeGenerationResults + } = this.compilation; + /** @type {Map>>} */ + const initCodePerScope = new Map(); + for (const chunk of this.chunk.getAllReferencedChunks()) { + const modules = chunkGraph.getChunkModulesIterableBySourceType( + chunk, + "share-init" + ); + if (!modules) continue; + for (const m of modules) { + const codeGen = codeGenerationResults.get(m); + if (!codeGen) continue; + const data = codeGen.data && codeGen.data.get("share-init"); + if (!data) continue; + for (const item of data) { + const { shareScope, initStage, init } = item; + let stages = initCodePerScope.get(shareScope); + if (stages === undefined) { + initCodePerScope.set(shareScope, (stages = new Map())); + } + let list = stages.get(initStage || 0); + if (list === undefined) { + stages.set(initStage || 0, (list = new Set())); + } + list.add(init); + } + } + } + return Template.asString([ + `${RuntimeGlobals.shareScopeMap} = {};`, + "var initPromises = {};", + `${RuntimeGlobals.initializeSharing} = ${runtimeTemplate.basicFunction( + "name", + [ + "// only runs once", + "if(initPromises[name]) return initPromises[name];", + "// handling circular init calls", + "initPromises[name] = 1;", + "// creates a new share scope if needed", + `if(!${RuntimeGlobals.hasOwnProperty}(${RuntimeGlobals.shareScopeMap}, name)) ${RuntimeGlobals.shareScopeMap}[name] = {};`, + "// runs all init snippets from all modules reachable", + `var scope = ${RuntimeGlobals.shareScopeMap}[name];`, + `var warn = ${runtimeTemplate.returningFunction( + 'typeof console !== "undefined" && console.warn && console.warn(msg);', + "msg" + )};`, + `var register = ${runtimeTemplate.basicFunction( + "name, version, factory, currentName", + [ + "version = version || [];", + "currentName = name;", + `var versionConflict = ${runtimeTemplate.returningFunction( + 'warn("Version conflict for shared modules: " + name + " " + (v && v.join(".")) + " <=> " + (version && version.join(".")));' + )};`, + `var registerCurrent = ${runtimeTemplate.basicFunction("", [ + "if(scope[currentName]) {", + Template.indent([ + "var v = scope[currentName].version || [];", + "for(var i = 0; i < version.length && i < v.length; i++) {", + Template.indent([ + "if(v[i] != version[i]) { // loose equal is intentional to match string and number", + Template.indent([ + 'if(typeof v[i] === "string" || typeof version[i] === "string") return versionConflict();', + "if(v[i] > version[i]) return;", + "if(v[i] < version[i]) { i = -1; break; }" + ]), + "}" + ]), + "}", + "if(i >= 0 && version.length <= v.length) return;", + 'if(scope[currentName].loaded) return warn("Ignoring providing of already used shared module: " + name);' + ]), + "}", + "scope[currentName] = { get: factory, version: version };" + ])};`, + "registerCurrent();", + `version.forEach(${runtimeTemplate.basicFunction("part", [ + 'currentName += "`" + part;', + "registerCurrent();" + ])});` + ] + )};`, + `var initExternal = ${runtimeTemplate.basicFunction("id", [ + `var handleError = ${runtimeTemplate.returningFunction( + 'warn("Initialization of sharing external failed: " + err)', + "err" + )};`, + "try {", + Template.indent([ + "var module = __webpack_require__(id);", + "if(!module) return;", + `var initFn = ${runtimeTemplate.returningFunction( + `module && module.init && module.init(${RuntimeGlobals.shareScopeMap}[name])`, + "module" + )}`, + "if(module.then) return promises.push(module.then(initFn, handleError));", + "var initResult = initFn(module);", + "if(initResult && initResult.then) return promises.push(initResult.catch(handleError));" + ]), + "} catch(err) { handleError(err); }" + ])}`, + "var promises = [];", + "switch(name) {", + ...Array.from(initCodePerScope, ([name, stages]) => + Template.indent([ + `case ${JSON.stringify(name)}: {`, + Template.indent( + Array.from(stages) + .sort(([a], [b]) => a - b) + .map(([, initCode]) => + Template.asString(Array.from(initCode)) + ) + ), + "}", + "break;" + ]) + ), + "}", + `return promises.length && (initPromises[name] = Promise.all(promises).then(${runtimeTemplate.returningFunction( + "initPromises[name] = 1" + )}));` + ] + )};` + ]); + } +} + +module.exports = ShareRuntimeModule; diff --git a/lib/sharing/utils.js b/lib/sharing/utils.js new file mode 100644 index 000000000..4c3e82455 --- /dev/null +++ b/lib/sharing/utils.js @@ -0,0 +1,103 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ + +"use strict"; + +/** + * @param {string} version version as string + * @returns {(number|string)[]} version as array + */ +exports.parseRequiredVersion = version => { + let fuzzyStart = Infinity; + if (version.startsWith(">=")) { + fuzzyStart = 0; + version = version.slice(2); + } else if (version.startsWith("^")) { + fuzzyStart = 1; + version = version.slice(1); + } else if (version.startsWith("~")) { + fuzzyStart = 2; + version = version.slice(1); + } + return version + .split(".") + .map((x, i) => (i >= fuzzyStart && `${+x}` === x ? +x : x)); +}; + +/** + * @param {string} version version as string + * @returns {(number|string)[]} version as array + */ +exports.parseVersion = version => { + return version.split(".").map(x => (`${+x}` === x ? +x : x)); +}; + +/** + * @param {(number|string)[]} version version + * @returns {string} version as string + */ +exports.versionToString = version => { + if (!version) return "(unknown)"; + const info = version.map(value => + typeof value !== "string" + ? { + type: "min", + value: `${value}` + } + : `${+value}` === value + ? { + type: "exact", + value + } + : { + type: "tag", + value + } + ); + switch (`${info[0].type}.${info.length > 1 ? info[1].type : "undefined"}`) { + case "exact.min": + case "tag.min": + if (!info.slice(2).some(i => i.type === "exact")) + return `^${version.join(".")}`; + break; + + case "exact.exact": + case "exact.tag": + case "tag.exact": + case "tag.tag": + if (!info.slice(2).some(i => i.type === "exact")) + return `~${version.join(".")}`; + else if (!info.slice(2).some(i => i.type === "min")) + return version.join("."); + break; + + case "min.min": + case "min.tag": + if (!info.slice(2).some(i => i.type === "exact")) + return `>=${version.join(".")}`; + break; + + case "min.undefined": + return `>=${version.join(".")}`; + + case "exact.undefined": + case "tag.undefined": + return version.join("."); + } + return info + .map(i => (i.type === "exact" ? i.value : `[>=${i.value}]`)) + .join("."); +}; + +/** + * @param {string} str maybe required version + * @returns {boolean} true, if it looks like a version + */ +exports.isRequiredVersion = str => { + if (str.startsWith("^")) return true; + if (str.startsWith("~")) return true; + if (str.startsWith(">=")) return true; + return /^\d/.test(str); +}; diff --git a/lib/util/internalSerializables.js b/lib/util/internalSerializables.js index 58f2b3112..65018fecd 100644 --- a/lib/util/internalSerializables.js +++ b/lib/util/internalSerializables.js @@ -23,17 +23,12 @@ module.exports = { require("../container/ContainerEntryModule"), "container/ContainerExposedDependency": () => require("../container/ContainerExposedDependency"), - "container/OverridableModule": () => - require("../container/OverridableModule"), - "container/OverridableOriginalDependency": () => - require("../container/OverridableOriginalDependency"), + "container/FallbackDependency": () => + require("../container/FallbackDependency"), + "container/FallbackItemDependency": () => + require("../container/FallbackItemDependency"), + "container/FallbackModule": () => require("../container/FallbackModule"), "container/RemoteModule": () => require("../container/RemoteModule"), - "container/RemoteOverrideDependency": () => - require("../container/RemoteOverrideDependency"), - "container/RemoteOverridesDependency": () => - require("../container/RemoteOverridesDependency"), - "container/RemoteOverridesModule": () => - require("../container/RemoteOverridesModule"), "container/RemoteToExternalDependency": () => require("../container/RemoteToExternalDependency"), "dependencies/AMDDefineDependency": () => @@ -159,6 +154,13 @@ module.exports = { ModuleWarning: () => require("../ModuleWarning"), NormalModule: () => require("../NormalModule"), RawModule: () => require("../RawModule"), + "sharing/ConsumeSharedModule": () => + require("../sharing/ConsumeSharedModule"), + "sharing/ConsumeFallbackDependency": () => + require("../sharing/ConsumeFallbackDependency"), + "sharing/ProvideModule": () => require("../sharing/ProvideModule"), + "sharing/ProvideDependency": () => require("../sharing/ProvideDependency"), + "sharing/ProvidedDependency": () => require("../sharing/ProvidedDependency"), UnsupportedFeatureWarning: () => require("../UnsupportedFeatureWarning"), "util/LazySet": () => require("../util/LazySet"), WebpackError: () => require("../WebpackError"), diff --git a/package.json b/package.json index 3eb3075e1..5e47d715a 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "description": "Packs CommonJs/AMD modules for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand. Support loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.", "license": "MIT", "dependencies": { + "@types/estree": "0.0.42", "@webassemblyjs/ast": "1.9.0", "@webassemblyjs/helper-module-context": "1.9.0", "@webassemblyjs/wasm-edit": "1.9.0", @@ -23,13 +24,12 @@ "pkg-dir": "^4.2.0", "schema-utils": "^2.5.0", "tapable": "2.0.0-beta.9", - "terser-webpack-plugin": "^2.3.6", + "terser-webpack-plugin": "^3.0.2", "watchpack": "2.0.0-beta.13", "webpack-sources": "2.0.0-beta.8" }, "devDependencies": { "@babel/core": "^7.7.2", - "@types/estree": "0.0.42", "@types/jest": "^25.1.5", "@types/node": "^12.6.9", "babel-loader": "^8.0.6", diff --git a/schemas/_container.json b/schemas/_container.json new file mode 100644 index 000000000..3530888a5 --- /dev/null +++ b/schemas/_container.json @@ -0,0 +1,151 @@ +{ + "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" + } + ] + } + }, + "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" + } + ] + }, + "shareScope": { + "description": "The name of the share scope shared with this remote.", + "type": "string", + "minLength": 1 + } + }, + "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" + } + ] + } + } + } +} diff --git a/schemas/_sharedContainer.json b/schemas/_sharedContainer.json deleted file mode 100644 index 0185848b6..000000000 --- a/schemas/_sharedContainer.json +++ /dev/null @@ -1,323 +0,0 @@ -{ - "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" - } - ] - } - } - } -} diff --git a/schemas/_sharing.json b/schemas/_sharing.json new file mode 100644 index 000000000..ab4490873 --- /dev/null +++ b/schemas/_sharing.json @@ -0,0 +1,132 @@ +{ + "definitions": { + "Shared": { + "description": "Modules that should be shared in the share scope. When provided, property names are used to match requested modules in this compilation.", + "anyOf": [ + { + "type": "array", + "items": { + "description": "Modules that should be shared in the share scope.", + "anyOf": [ + { + "$ref": "#/definitions/SharedItem" + }, + { + "$ref": "#/definitions/SharedObject" + } + ] + } + }, + { + "$ref": "#/definitions/SharedObject" + } + ] + }, + "SharedConfig": { + "description": "Advanced configuration for modules that should be shared in the share scope.", + "type": "object", + "additionalProperties": false, + "properties": { + "eager": { + "description": "Include the provided and fallback module directly instead behind an async request. This allows to use this shared module in initial load too. All possible shared modules need to be eager too.", + "type": "boolean" + }, + "import": { + "description": "Provided module that should be provided to share scope. Also acts as fallback module if no shared module is found in share scope or version isn't valid. Defaults to the property name.", + "anyOf": [ + { + "description": "No provided or fallback module.", + "enum": [false] + }, + { + "$ref": "#/definitions/SharedItem" + } + ] + }, + "requiredVersion": { + "description": "Version requirement from module in share scope.", + "anyOf": [ + { + "description": "Version as string. Can be prefixed with '^' or '~' for minimum matches. Each part of the version should be separated by a dot '.'.", + "type": "string" + }, + { + "$ref": "#/definitions/SharedVersionArray" + } + ] + }, + "shareKey": { + "description": "Module is looked up under this key from the share scope.", + "type": "string", + "minLength": 1 + }, + "shareScope": { + "description": "Share scope name.", + "type": "string", + "minLength": 1 + }, + "singleton": { + "description": "Allow only a single version of the shared module in share scope (disabled by default).", + "type": "boolean" + }, + "strictVersion": { + "description": "Do not accept shared module if version is not valid (defaults to yes, if local fallback module is available and shared module is not a singleton, otherwise no, has no effect if there is no required version specified).", + "type": "boolean" + }, + "version": { + "description": "Version of the provided module. Will replace lower matching versions, but not higher.", + "anyOf": [ + { + "description": "Don't provide a version.", + "enum": [false] + }, + { + "description": "Version as string. Each part of the version should be separated by a dot '.'.", + "type": "string" + }, + { + "$ref": "#/definitions/SharedVersionArray" + } + ] + } + } + }, + "SharedItem": { + "description": "A module that should be shared in the share scope.", + "type": "string", + "minLength": 1 + }, + "SharedObject": { + "description": "Modules that should be shared in the share scope. Property names are used to match requested modules in this compilation. Relative requests are resolved, module requests are matched unresolved, absolute paths will match resolved requests. A trailing slash will match all requests with this prefix. In this case shareKey must also have a trailing slash.", + "type": "object", + "additionalProperties": { + "description": "Modules that should be shared in the share scope.", + "anyOf": [ + { + "$ref": "#/definitions/SharedConfig" + }, + { + "$ref": "#/definitions/SharedItem" + } + ] + } + }, + "SharedVersionArray": { + "description": "Version number as array. Numbers and strings are accepted. Strings are treated as tags, which only match exactly. Numbers can match higher numbers.", + "type": "array", + "items": { + "description": "An item of the version.", + "anyOf": [ + { + "description": "Version number. It's assumed that the module is compatible with any higher version number.", + "type": "number" + }, + { + "description": "Version tag. The module is only compatible with the exact matching version tag.", + "type": "string" + } + ] + } + } + } +} diff --git a/schemas/plugins/container/ContainerPlugin.json b/schemas/plugins/container/ContainerPlugin.json index d4e024a85..7f11f3c4e 100644 --- a/schemas/plugins/container/ContainerPlugin.json +++ b/schemas/plugins/container/ContainerPlugin.json @@ -221,77 +221,6 @@ "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" - } - ] - } - }, "UmdNamedDefine": { "description": "If `output.libraryTarget` is set to umd and `output.library` is set, setting this to true will name the AMD module.", "type": "boolean" @@ -307,17 +236,21 @@ "filename": { "description": "The filename for this container relative path inside the `output.path` directory.", "type": "string", - "absolutePath": false + "absolutePath": false, + "minLength": 1 }, "library": { "$ref": "#/definitions/LibraryOptions" }, "name": { "description": "The name for this container.", - "type": "string" + "type": "string", + "minLength": 1 }, - "overridables": { - "$ref": "#/definitions/Overridables" + "shareScope": { + "description": "The name of the share scope which is shared with the host (defaults to 'default').", + "type": "string", + "minLength": 1 } }, "required": ["name", "exposes"] diff --git a/schemas/plugins/container/ContainerReferencePlugin.json b/schemas/plugins/container/ContainerReferencePlugin.json index d2e916f52..dda6a87e9 100644 --- a/schemas/plugins/container/ContainerReferencePlugin.json +++ b/schemas/plugins/container/ContainerReferencePlugin.json @@ -23,59 +23,6 @@ "import" ] }, - "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": [ @@ -113,6 +60,11 @@ "$ref": "#/definitions/RemotesItems" } ] + }, + "shareScope": { + "description": "The name of the share scope shared with this remote.", + "type": "string", + "minLength": 1 } }, "required": ["external"] @@ -152,9 +104,6 @@ "type": "object", "additionalProperties": false, "properties": { - "overrides": { - "$ref": "#/definitions/Overrides" - }, "remoteType": { "description": "The external type of the remote containers.", "oneOf": [ @@ -165,6 +114,11 @@ }, "remotes": { "$ref": "#/definitions/Remotes" + }, + "shareScope": { + "description": "The name of the share scope shared with all remotes (defaults to 'default').", + "type": "string", + "minLength": 1 } }, "required": ["remoteType", "remotes"] diff --git a/schemas/plugins/container/ModuleFederationPlugin.json b/schemas/plugins/container/ModuleFederationPlugin.json index e805aa6a8..e25f99d81 100644 --- a/schemas/plugins/container/ModuleFederationPlugin.json +++ b/schemas/plugins/container/ModuleFederationPlugin.json @@ -244,130 +244,6 @@ "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": { "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": [ @@ -405,6 +281,11 @@ "$ref": "#/definitions/RemotesItems" } ] + }, + "shareScope": { + "description": "The name of the share scope shared with this remote.", + "type": "string", + "minLength": 1 } }, "required": ["external"] @@ -440,12 +321,12 @@ } }, "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 in the share scope. When provided, property names are used to match requested modules in this compilation.", "anyOf": [ { "type": "array", "items": { - "description": "Modules that should be shared with remotes and/or host.", + "description": "Modules that should be shared in the share scope.", "anyOf": [ { "$ref": "#/definitions/SharedItem" @@ -462,26 +343,84 @@ ] }, "SharedConfig": { - "description": "Advanced configuration for modules that should be shared with remotes and/or host.", + "description": "Advanced configuration for modules that should be shared in the share scope.", "type": "object", "additionalProperties": false, "properties": { + "eager": { + "description": "Include the provided and fallback module directly instead behind an async request. This allows to use this shared module in initial load too. All possible shared modules need to be eager too.", + "type": "boolean" + }, "import": { - "$ref": "#/definitions/SharedItem" + "description": "Provided module that should be provided to share scope. Also acts as fallback module if no shared module is found in share scope or version isn't valid. Defaults to the property name.", + "anyOf": [ + { + "description": "No provided or fallback module.", + "enum": [false] + }, + { + "$ref": "#/definitions/SharedItem" + } + ] + }, + "requiredVersion": { + "description": "Version requirement from module in share scope.", + "anyOf": [ + { + "description": "Version as string. Can be prefixed with '^' or '~' for minimum matches. Each part of the version should be separated by a dot '.'.", + "type": "string" + }, + { + "$ref": "#/definitions/SharedVersionArray" + } + ] + }, + "shareKey": { + "description": "Module is looked up under this key from the share scope.", + "type": "string", + "minLength": 1 + }, + "shareScope": { + "description": "Share scope name.", + "type": "string", + "minLength": 1 + }, + "singleton": { + "description": "Allow only a single version of the shared module in share scope (disabled by default).", + "type": "boolean" + }, + "strictVersion": { + "description": "Do not accept shared module if version is not valid (defaults to yes, if local fallback module is available and shared module is not a singleton, otherwise no, has no effect if there is no required version specified).", + "type": "boolean" + }, + "version": { + "description": "Version of the provided module. Will replace lower matching versions, but not higher.", + "anyOf": [ + { + "description": "Don't provide a version.", + "enum": [false] + }, + { + "description": "Version as string. Each part of the version should be separated by a dot '.'.", + "type": "string" + }, + { + "$ref": "#/definitions/SharedVersionArray" + } + ] } - }, - "required": ["import"] + } }, "SharedItem": { - "description": "Module that should be shared with remotes and/or host.", + "description": "A module that should be shared in the share scope.", "type": "string", "minLength": 1 }, "SharedObject": { - "description": "Modules that should be shared with remotes and/or host. Property names are used as shared keys.", + "description": "Modules that should be shared in the share scope. Property names are used to match requested modules in this compilation. Relative requests are resolved, module requests are matched unresolved, absolute paths will match resolved requests. A trailing slash will match all requests with this prefix. In this case shareKey must also have a trailing slash.", "type": "object", "additionalProperties": { - "description": "Modules that should be shared with remotes and/or host.", + "description": "Modules that should be shared in the share scope.", "anyOf": [ { "$ref": "#/definitions/SharedConfig" @@ -492,6 +431,23 @@ ] } }, + "SharedVersionArray": { + "description": "Version number as array. Numbers and strings are accepted. Strings are treated as tags, which only match exactly. Numbers can match higher numbers.", + "type": "array", + "items": { + "description": "An item of the version.", + "anyOf": [ + { + "description": "Version number. It's assumed that the module is compatible with any higher version number.", + "type": "number" + }, + { + "description": "Version tag. The module is only compatible with the exact matching version tag.", + "type": "string" + } + ] + } + }, "UmdNamedDefine": { "description": "If `output.libraryTarget` is set to umd and `output.library` is set, setting this to true will name the AMD module.", "type": "boolean" @@ -516,12 +472,6 @@ "description": "The name of the container.", "type": "string" }, - "overridables": { - "$ref": "#/definitions/Overridables" - }, - "overrides": { - "$ref": "#/definitions/Overrides" - }, "remoteType": { "description": "The external type of the remote containers.", "oneOf": [ @@ -533,6 +483,11 @@ "remotes": { "$ref": "#/definitions/Remotes" }, + "shareScope": { + "description": "Share scope name used for all shared modules (defaults to 'default').", + "type": "string", + "minLength": 1 + }, "shared": { "$ref": "#/definitions/Shared" } diff --git a/schemas/plugins/container/OverridablesPlugin.json b/schemas/plugins/container/OverridablesPlugin.json deleted file mode 100644 index 099855803..000000000 --- a/schemas/plugins/container/OverridablesPlugin.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "definitions": { - "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" - } - ] - } - } - }, - "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" - } - } -} diff --git a/schemas/plugins/sharing/ConsumeSharedPlugin.json b/schemas/plugins/sharing/ConsumeSharedPlugin.json new file mode 100644 index 000000000..2c346029d --- /dev/null +++ b/schemas/plugins/sharing/ConsumeSharedPlugin.json @@ -0,0 +1,131 @@ +{ + "definitions": { + "Consumes": { + "description": "Modules that should be consumed from share scope. When provided, property names are used to match requested modules in this compilation.", + "anyOf": [ + { + "type": "array", + "items": { + "description": "Modules that should be consumed from share scope.", + "anyOf": [ + { + "$ref": "#/definitions/ConsumesItem" + }, + { + "$ref": "#/definitions/ConsumesObject" + } + ] + } + }, + { + "$ref": "#/definitions/ConsumesObject" + } + ] + }, + "ConsumesConfig": { + "description": "Advanced configuration for modules that should be consumed from share scope.", + "type": "object", + "additionalProperties": false, + "properties": { + "eager": { + "description": "Include the fallback module directly instead behind an async request. This allows to use fallback module in initial load too. All possible shared modules need to be eager too.", + "type": "boolean" + }, + "import": { + "description": "Fallback module if no shared module is found in share scope. Defaults to the property name.", + "anyOf": [ + { + "description": "No fallback module.", + "enum": [false] + }, + { + "$ref": "#/definitions/ConsumesItem" + } + ] + }, + "requiredVersion": { + "description": "Version requirement from module in share scope.", + "anyOf": [ + { + "description": "Version as string. Can be prefixed with '^' or '~' for minimum matches. Each part of the version should be separated by a dot '.'.", + "type": "string" + }, + { + "$ref": "#/definitions/SharedVersionArray" + } + ] + }, + "shareKey": { + "description": "Module is looked up under this key from the share scope.", + "type": "string", + "minLength": 1 + }, + "shareScope": { + "description": "Share scope name.", + "type": "string", + "minLength": 1 + }, + "singleton": { + "description": "Allow only a single version of the shared module in share scope (disabled by default).", + "type": "boolean" + }, + "strictVersion": { + "description": "Do not accept shared module if version is not valid (defaults to yes, if local fallback module is available and shared module is not a singleton, otherwise no, has no effect if there is no required version specified).", + "type": "boolean" + } + } + }, + "ConsumesItem": { + "description": "A module that should be consumed from share scope.", + "type": "string", + "minLength": 1 + }, + "ConsumesObject": { + "description": "Modules that should be consumed from share scope. Property names are used to match requested modules in this compilation. Relative requests are resolved, module requests are matched unresolved, absolute paths will match resolved requests. A trailing slash will match all requests with this prefix. In this case shareKey must also have a trailing slash.", + "type": "object", + "additionalProperties": { + "description": "Modules that should be consumed from share scope.", + "anyOf": [ + { + "$ref": "#/definitions/ConsumesConfig" + }, + { + "$ref": "#/definitions/ConsumesItem" + } + ] + } + }, + "SharedVersionArray": { + "description": "Version number as array. Numbers and strings are accepted. Strings are treated as tags, which only match exactly. Numbers can match higher numbers.", + "type": "array", + "items": { + "description": "An item of the version.", + "anyOf": [ + { + "description": "Version number. It's assumed that the module is compatible with any higher version number.", + "type": "number" + }, + { + "description": "Version tag. The module is only compatible with the exact matching version tag.", + "type": "string" + } + ] + } + } + }, + "title": "ConsumeSharedPluginOptions", + "description": "Options for consuming shared modules.", + "type": "object", + "additionalProperties": false, + "properties": { + "consumes": { + "$ref": "#/definitions/Consumes" + }, + "shareScope": { + "description": "Share scope name used for all consumed modules (defaults to 'default').", + "type": "string", + "minLength": 1 + } + }, + "required": ["consumes"] +} diff --git a/schemas/plugins/sharing/ProvideSharedPlugin.json b/schemas/plugins/sharing/ProvideSharedPlugin.json new file mode 100644 index 000000000..c4f986271 --- /dev/null +++ b/schemas/plugins/sharing/ProvideSharedPlugin.json @@ -0,0 +1,113 @@ +{ + "definitions": { + "Provides": { + "description": "Modules that should be provided as shared modules to the share scope. When provided, property name is used as share key, otherwise share key is automatically inferred from request.", + "anyOf": [ + { + "type": "array", + "items": { + "description": "Modules that should be provided as shared modules to the share scope.", + "anyOf": [ + { + "$ref": "#/definitions/ProvidesItem" + }, + { + "$ref": "#/definitions/ProvidesObject" + } + ] + } + }, + { + "$ref": "#/definitions/ProvidesObject" + } + ] + }, + "ProvidesConfig": { + "description": "Advanced configuration for modules that should be provided as shared modules to the share scope.", + "type": "object", + "additionalProperties": false, + "properties": { + "eager": { + "description": "Include the provided module directly instead behind an async request. This allows to use this shared module in initial load too. All possible shared modules need to be eager too.", + "type": "boolean" + }, + "import": { + "$ref": "#/definitions/ProvidesItem" + }, + "shareScope": { + "description": "Share scope name.", + "type": "string", + "minLength": 1 + }, + "version": { + "description": "Version of the provided module. Will replace lower matching versions, but not higher.", + "anyOf": [ + { + "description": "Don't provide a version.", + "enum": [false] + }, + { + "description": "Version as string. Each part of the version should be separated by a dot '.'.", + "type": "string" + }, + { + "$ref": "#/definitions/SharedVersionArray" + } + ] + } + }, + "required": ["import"] + }, + "ProvidesItem": { + "description": "Request to a module that should be provided as shared module to the share scope.", + "type": "string", + "minLength": 1 + }, + "ProvidesObject": { + "description": "Modules that should be provided as shared modules to the share scope. Property names are used as share keys.", + "type": "object", + "additionalProperties": { + "description": "Modules that should be provided as shared modules to the share scope.", + "anyOf": [ + { + "$ref": "#/definitions/ProvidesConfig" + }, + { + "$ref": "#/definitions/ProvidesItem" + } + ] + } + }, + "SharedVersionArray": { + "description": "Version number as array. Numbers and strings are accepted. Strings are treated as tags, which only match exactly. Numbers can match higher numbers.", + "type": "array", + "items": { + "description": "An item of the version.", + "anyOf": [ + { + "description": "Version number. It's assumed that the module is compatible with any higher version number.", + "type": "number" + }, + { + "description": "Version tag. The module is only compatible with the exact matching version tag.", + "type": "string" + } + ] + } + } + }, + "title": "ProvideSharedPluginOptions", + "type": "object", + "additionalProperties": false, + "properties": { + "provides": { + "$ref": "#/definitions/Provides" + }, + "shareScope": { + "description": "Share scope name used for all provided modules (defaults to 'default').", + "type": "string", + "minLength": 1 + } + }, + "required": ["provides"] +} diff --git a/schemas/plugins/sharing/SharePlugin.json b/schemas/plugins/sharing/SharePlugin.json new file mode 100644 index 000000000..0fc000ff0 --- /dev/null +++ b/schemas/plugins/sharing/SharePlugin.json @@ -0,0 +1,147 @@ +{ + "definitions": { + "Shared": { + "description": "Modules that should be shared in the share scope. When provided, property names are used to match requested modules in this compilation.", + "anyOf": [ + { + "type": "array", + "items": { + "description": "Modules that should be shared in the share scope.", + "anyOf": [ + { + "$ref": "#/definitions/SharedItem" + }, + { + "$ref": "#/definitions/SharedObject" + } + ] + } + }, + { + "$ref": "#/definitions/SharedObject" + } + ] + }, + "SharedConfig": { + "description": "Advanced configuration for modules that should be shared in the share scope.", + "type": "object", + "additionalProperties": false, + "properties": { + "eager": { + "description": "Include the provided and fallback module directly instead behind an async request. This allows to use this shared module in initial load too. All possible shared modules need to be eager too.", + "type": "boolean" + }, + "import": { + "description": "Provided module that should be provided to share scope. Also acts as fallback module if no shared module is found in share scope or version isn't valid. Defaults to the property name.", + "anyOf": [ + { + "description": "No provided or fallback module.", + "enum": [false] + }, + { + "$ref": "#/definitions/SharedItem" + } + ] + }, + "requiredVersion": { + "description": "Version requirement from module in share scope.", + "anyOf": [ + { + "description": "Version as string. Can be prefixed with '^' or '~' for minimum matches. Each part of the version should be separated by a dot '.'.", + "type": "string" + }, + { + "$ref": "#/definitions/SharedVersionArray" + } + ] + }, + "shareKey": { + "description": "Module is looked up under this key from the share scope.", + "type": "string", + "minLength": 1 + }, + "shareScope": { + "description": "Share scope name.", + "type": "string", + "minLength": 1 + }, + "singleton": { + "description": "Allow only a single version of the shared module in share scope (disabled by default).", + "type": "boolean" + }, + "strictVersion": { + "description": "Do not accept shared module if version is not valid (defaults to yes, if local fallback module is available and shared module is not a singleton, otherwise no, has no effect if there is no required version specified).", + "type": "boolean" + }, + "version": { + "description": "Version of the provided module. Will replace lower matching versions, but not higher.", + "anyOf": [ + { + "description": "Don't provide a version.", + "enum": [false] + }, + { + "description": "Version as string. Each part of the version should be separated by a dot '.'.", + "type": "string" + }, + { + "$ref": "#/definitions/SharedVersionArray" + } + ] + } + } + }, + "SharedItem": { + "description": "A module that should be shared in the share scope.", + "type": "string", + "minLength": 1 + }, + "SharedObject": { + "description": "Modules that should be shared in the share scope. Property names are used to match requested modules in this compilation. Relative requests are resolved, module requests are matched unresolved, absolute paths will match resolved requests. A trailing slash will match all requests with this prefix. In this case shareKey must also have a trailing slash.", + "type": "object", + "additionalProperties": { + "description": "Modules that should be shared in the share scope.", + "anyOf": [ + { + "$ref": "#/definitions/SharedConfig" + }, + { + "$ref": "#/definitions/SharedItem" + } + ] + } + }, + "SharedVersionArray": { + "description": "Version number as array. Numbers and strings are accepted. Strings are treated as tags, which only match exactly. Numbers can match higher numbers.", + "type": "array", + "items": { + "description": "An item of the version.", + "anyOf": [ + { + "description": "Version number. It's assumed that the module is compatible with any higher version number.", + "type": "number" + }, + { + "description": "Version tag. The module is only compatible with the exact matching version tag.", + "type": "string" + } + ] + } + } + }, + "title": "SharePluginOptions", + "description": "Options for shared modules.", + "type": "object", + "additionalProperties": false, + "properties": { + "shareScope": { + "description": "Share scope name used for all shared modules (defaults to 'default').", + "type": "string", + "minLength": 1 + }, + "shared": { + "$ref": "#/definitions/Shared" + } + }, + "required": ["shared"] +} diff --git a/test/PersistentCaching.test.js b/test/PersistentCaching.test.js index 7bb14ba7b..1baf11d89 100644 --- a/test/PersistentCaching.test.js +++ b/test/PersistentCaching.test.js @@ -54,6 +54,8 @@ describe("Persistent Caching", () => { return new Promise((resolve, reject) => { webpack({ ...config, ...configAdditions }, (err, stats) => { if (err) return reject(err); + if (stats.hasErrors()) + return reject(stats.toString({ preset: "errors-only" })); resolve(stats); }); }); @@ -138,7 +140,8 @@ export default ${files.map((_, i) => `f${i}`).join(" + ")}; const data = { "index.js": "export default import('container/src/exposed').then(m => m.default);", - "exposed.js": "export default 42;" + "exposed.js": "import lib from 'lib'; export default 21 + lib;", + "lib.js": "export default 21" }; await updateSrc(data); const configAdditions = { @@ -148,7 +151,15 @@ export default ${files.map((_, i) => `f${i}`).join(" + ")}; library: { type: "commonjs-module" }, exposes: ["./src/exposed"], remotes: { - container: "./container" + container: ["./no-container", "./container"] + }, + shared: { + lib: { + import: "./src/lib", + shareKey: "lib", + version: "1.2.3", + requiredVersion: "^1.0.0" + } } }) ] diff --git a/test/__snapshots__/StatsTestCases.test.js.snap b/test/__snapshots__/StatsTestCases.test.js.snap index 158e603c7..5d11966dd 100644 --- a/test/__snapshots__/StatsTestCases.test.js.snap +++ b/test/__snapshots__/StatsTestCases.test.js.snap @@ -835,9 +835,9 @@ external \\"test\\" 42 bytes [built]" `; exports[`StatsTestCases should print correct stats for filter-warnings 1`] = ` -"Hash: 7763b48a6d4533935ca17763b48a6d4533935ca17763b48a6d4533935ca17763b48a6d4533935ca17763b48a6d4533935ca17763b48a6d4533935ca17763b48a6d4533935ca17763b48a6d4533935ca17763b48a6d4533935ca17763b48a6d4533935ca17763b48a6d4533935ca17763b48a6d4533935ca17763b48a6d4533935ca1 +"Hash: 73a3198368d23afbbd6573a3198368d23afbbd6573a3198368d23afbbd6573a3198368d23afbbd6573a3198368d23afbbd6573a3198368d23afbbd6573a3198368d23afbbd6573a3198368d23afbbd6573a3198368d23afbbd6573a3198368d23afbbd6573a3198368d23afbbd6573a3198368d23afbbd6573a3198368d23afbbd65 Child undefined: - Hash: 7763b48a6d4533935ca1 + Hash: 73a3198368d23afbbd65 Time: X ms Built at: 1970-04-20 12:42:42 Asset Size @@ -867,49 +867,49 @@ Child undefined: WARNING in Terser Plugin: Dropping unused function someUnUsedFunction5 [webpack://./index.js:12,0] Child Terser: - Hash: 7763b48a6d4533935ca1 + Hash: 73a3198368d23afbbd65 Time: X ms Built at: 1970-04-20 12:42:42 Asset Size bundle1.js 556 bytes [emitted] [name: main] Entrypoint main = bundle1.js Child /Terser/: - Hash: 7763b48a6d4533935ca1 + Hash: 73a3198368d23afbbd65 Time: X ms Built at: 1970-04-20 12:42:42 Asset Size bundle2.js 556 bytes [emitted] [name: main] Entrypoint main = bundle2.js Child warnings => true: - Hash: 7763b48a6d4533935ca1 + Hash: 73a3198368d23afbbd65 Time: X ms Built at: 1970-04-20 12:42:42 Asset Size bundle3.js 556 bytes [emitted] [name: main] Entrypoint main = bundle3.js Child [Terser]: - Hash: 7763b48a6d4533935ca1 + Hash: 73a3198368d23afbbd65 Time: X ms Built at: 1970-04-20 12:42:42 Asset Size bundle4.js 556 bytes [emitted] [name: main] Entrypoint main = bundle4.js Child [/Terser/]: - Hash: 7763b48a6d4533935ca1 + Hash: 73a3198368d23afbbd65 Time: X ms Built at: 1970-04-20 12:42:42 Asset Size bundle5.js 556 bytes [emitted] [name: main] Entrypoint main = bundle5.js Child [warnings => true]: - Hash: 7763b48a6d4533935ca1 + Hash: 73a3198368d23afbbd65 Time: X ms Built at: 1970-04-20 12:42:42 Asset Size bundle6.js 556 bytes [emitted] [name: main] Entrypoint main = bundle6.js Child should not filter: - Hash: 7763b48a6d4533935ca1 + Hash: 73a3198368d23afbbd65 Time: X ms Built at: 1970-04-20 12:42:42 Asset Size @@ -939,7 +939,7 @@ Child should not filter: WARNING in Terser Plugin: Dropping unused function someUnUsedFunction5 [webpack://./index.js:12,0] Child /should not filter/: - Hash: 7763b48a6d4533935ca1 + Hash: 73a3198368d23afbbd65 Time: X ms Built at: 1970-04-20 12:42:42 Asset Size @@ -969,7 +969,7 @@ Child /should not filter/: WARNING in Terser Plugin: Dropping unused function someUnUsedFunction5 [webpack://./index.js:12,0] Child warnings => false: - Hash: 7763b48a6d4533935ca1 + Hash: 73a3198368d23afbbd65 Time: X ms Built at: 1970-04-20 12:42:42 Asset Size @@ -999,7 +999,7 @@ Child warnings => false: WARNING in Terser Plugin: Dropping unused function someUnUsedFunction5 [webpack://./index.js:12,0] Child [should not filter]: - Hash: 7763b48a6d4533935ca1 + Hash: 73a3198368d23afbbd65 Time: X ms Built at: 1970-04-20 12:42:42 Asset Size @@ -1029,7 +1029,7 @@ Child [should not filter]: WARNING in Terser Plugin: Dropping unused function someUnUsedFunction5 [webpack://./index.js:12,0] Child [/should not filter/]: - Hash: 7763b48a6d4533935ca1 + Hash: 73a3198368d23afbbd65 Time: X ms Built at: 1970-04-20 12:42:42 Asset Size @@ -1059,7 +1059,7 @@ Child [/should not filter/]: WARNING in Terser Plugin: Dropping unused function someUnUsedFunction5 [webpack://./index.js:12,0] Child [warnings => false]: - Hash: 7763b48a6d4533935ca1 + Hash: 73a3198368d23afbbd65 Time: X ms Built at: 1970-04-20 12:42:42 Asset Size @@ -2731,9 +2731,9 @@ Entrypoint main = main.js `; exports[`StatsTestCases should print correct stats for side-effects-optimization 1`] = ` -"Hash: d0117c2c1b2089facc60a79894f4120790dbe077 +"Hash: d7d1b4f45cd90d4abdb64369e7ea517dce8ffb09 Child - Hash: d0117c2c1b2089facc60 + Hash: d7d1b4f45cd90d4abdb6 Time: X ms Built at: 1970-04-20 12:42:42 Asset Size @@ -2755,7 +2755,7 @@ Child ModuleConcatenation bailout: Module is not an ECMAScript module + 2 hidden modules Child - Hash: a79894f4120790dbe077 + Hash: 4369e7ea517dce8ffb09 Time: X ms Built at: 1970-04-20 12:42:42 Asset Size @@ -3879,7 +3879,7 @@ require.include() is deprecated and will be removed soon. `; exports[`StatsTestCases should print correct stats for warnings-terser 1`] = ` -"Hash: 07f3df8603085e838fb0 +"Hash: 9d4799d2d13af78e55f1 Time: X ms Built at: 1970-04-20 12:42:42 Asset Size diff --git a/test/configCases/code-generation/harmony-pure-default/deprecations.js b/test/configCases/code-generation/harmony-pure-default/deprecations.js deleted file mode 100644 index 9bff44c7d..000000000 --- a/test/configCases/code-generation/harmony-pure-default/deprecations.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = [{ code: /DEP_WEBPACK_COMPILATION_OPTIMIZE_CHUNK_ASSETS/ }]; diff --git a/test/configCases/container/0-container-full/webpack.config.js b/test/configCases/container/0-container-full/webpack.config.js index a5751dea3..8eef81562 100644 --- a/test/configCases/container/0-container-full/webpack.config.js +++ b/test/configCases/container/0-container-full/webpack.config.js @@ -17,7 +17,7 @@ module.exports = { external: "./container.js" } }, - shared: ["react"] + shared: { react: { version: false } } }) ] }; diff --git a/test/configCases/container/0-transitive-overriding/webpack.config.js b/test/configCases/container/0-transitive-overriding/webpack.config.js index 31ca6d032..82dd86951 100644 --- a/test/configCases/container/0-transitive-overriding/webpack.config.js +++ b/test/configCases/container/0-transitive-overriding/webpack.config.js @@ -16,7 +16,10 @@ module.exports = { "container-with-shared": "./container-with-shared.js" }, shared: { - shared: "./shared" + "./shared": { + shareKey: "shared", + version: "1" + } } }) ] diff --git a/test/configCases/container/1-container-full/node_modules/package.json b/test/configCases/container/1-container-full/node_modules/package.json new file mode 100644 index 000000000..87032da00 --- /dev/null +++ b/test/configCases/container/1-container-full/node_modules/package.json @@ -0,0 +1,3 @@ +{ + "version": "2.1.0" +} diff --git a/test/configCases/container/2-container-full/App.js b/test/configCases/container/2-container-full/App.js index 3544d07aa..24380bb85 100644 --- a/test/configCases/container/2-container-full/App.js +++ b/test/configCases/container/2-container-full/App.js @@ -1,6 +1,8 @@ +import OldReact from "old-react"; +import OldReactSingleton from "old-react-singleton"; import React from "react"; import ComponentC from "containerB/ComponentC"; export default () => { - return `App rendered with [${React()}] and [${ComponentC()}]`; + return `App rendered with [${React()}] and [${OldReact()}] and [${OldReactSingleton()}] and [${ComponentC()}]`; }; diff --git a/test/configCases/container/2-container-full/ComponentB.js b/test/configCases/container/2-container-full/ComponentB.js deleted file mode 100644 index 1943469c7..000000000 --- a/test/configCases/container/2-container-full/ComponentB.js +++ /dev/null @@ -1,5 +0,0 @@ -import React from "react"; - -export default () => { - return `ComponentB rendered with [${React()}]`; -}; diff --git a/test/configCases/container/2-container-full/index.js b/test/configCases/container/2-container-full/index.js index a9b7522c8..859c491e1 100644 --- a/test/configCases/container/2-container-full/index.js +++ b/test/configCases/container/2-container-full/index.js @@ -1,14 +1,46 @@ +let warnings = []; +let oldWarn; + +beforeEach(done => { + oldWarn = console.warn; + console.warn = m => warnings.push(m); + done(); +}); + +afterEach(done => { + expectWarning(); + console.warn = oldWarn; + done(); +}); + +const expectWarning = regexp => { + if (!regexp) { + expect(warnings).toEqual([]); + } else { + expect(warnings).toEqual( + expect.objectContaining({ + 0: expect.stringMatching(regexp), + length: 1 + }) + ); + } + warnings.length = 0; +}; + it("should load the component from container", () => { return import("./App").then(({ default: App }) => { + expectWarning( + /Unsatisfied version of shared singleton module react@8 \(required react@2\)/ + ); const rendered = App(); expect(rendered).toBe( - "App rendered with [This is react 8] and [ComponentC rendered with [This is react 8] and [ComponentA rendered with [This is react 8]] and [ComponentB rendered with [This is react 8]]]" + "App rendered with [This is react 8] and [This is react 2.1.0] and [This is react 8] and [ComponentC rendered with [This is react 8] and [ComponentA rendered with [This is react 8]] and [ComponentB rendered with [This is react 8]]]" ); return import("./upgrade-react").then(({ default: upgrade }) => { upgrade(); const rendered = App(); expect(rendered).toBe( - "App rendered with [This is react 9] and [ComponentC rendered with [This is react 9] and [ComponentA rendered with [This is react 9]] and [ComponentB rendered with [This is react 9]]]" + "App rendered with [This is react 9] and [This is react 2.1.0] and [This is react 9] and [ComponentC rendered with [This is react 9] and [ComponentA rendered with [This is react 9]] and [ComponentB rendered with [This is react 9]]]" ); }); }); diff --git a/test/configCases/container/2-container-full/node_modules/package.json b/test/configCases/container/2-container-full/node_modules/package.json new file mode 100644 index 000000000..88d4e7f3e --- /dev/null +++ b/test/configCases/container/2-container-full/node_modules/package.json @@ -0,0 +1,3 @@ +{ + "version": "8" +} diff --git a/test/configCases/container/2-container-full/webpack.config.js b/test/configCases/container/2-container-full/webpack.config.js index 67a420885..0b62f5417 100644 --- a/test/configCases/container/2-container-full/webpack.config.js +++ b/test/configCases/container/2-container-full/webpack.config.js @@ -16,7 +16,20 @@ module.exports = { ] }, exposes: ["./Self"], - shared: ["react"] + shared: { + react: "react", + "old-react": { + import: false, + shareKey: "react", + requiredVersion: "^2" + }, + "old-react-singleton": { + import: false, + shareKey: "react", + requiredVersion: "^2", + singleton: true + } + } }) ] }; diff --git a/test/configCases/container/2-transitive-overriding/index.js b/test/configCases/container/2-transitive-overriding/index.js index f87ba9039..f77717268 100644 --- a/test/configCases/container/2-transitive-overriding/index.js +++ b/test/configCases/container/2-transitive-overriding/index.js @@ -20,7 +20,7 @@ it("should have good module ids", async () => { "./b.js", "./modules.js", "webpack/container/entry/container-with-shared", - "webpack/container/overridable/shared=./shared" + "webpack/sharing/consume/default/shared/./shared" ]); expect(m1).toEqual([ "./a.js", @@ -29,7 +29,6 @@ it("should have good module ids", async () => { "./modules.js", "webpack/container/entry/container-no-shared", "webpack/container/reference/container-with-shared", - "webpack/container/remote-overrides/empty", "webpack/container/remote/container-with-shared/b", "webpack/container/remote/container-with-shared/modules" ]); @@ -37,7 +36,6 @@ it("should have good module ids", async () => { "./index.js", "./shared.js", "webpack/container/reference/container-no-shared", - "webpack/container/remote-overrides/7d3b8a", "webpack/container/remote/container-no-shared/a", "webpack/container/remote/container-no-shared/b", "webpack/container/remote/container-no-shared/modules", diff --git a/test/configCases/container/2-transitive-overriding/webpack.config.js b/test/configCases/container/2-transitive-overriding/webpack.config.js index 378f853c4..3d384985c 100644 --- a/test/configCases/container/2-transitive-overriding/webpack.config.js +++ b/test/configCases/container/2-transitive-overriding/webpack.config.js @@ -14,7 +14,10 @@ module.exports = { "../1-transitive-overriding/container-no-shared.js" }, shared: { - shared: "./shared" + "./shared": { + shareKey: "shared", + version: "2" + } } }) ] diff --git a/test/configCases/container/3-container-full/App.js b/test/configCases/container/3-container-full/App.js new file mode 100644 index 000000000..3544d07aa --- /dev/null +++ b/test/configCases/container/3-container-full/App.js @@ -0,0 +1,6 @@ +import React from "react"; +import ComponentC from "containerB/ComponentC"; + +export default () => { + return `App rendered with [${React()}] and [${ComponentC()}]`; +}; diff --git a/test/configCases/container/3-container-full/index.js b/test/configCases/container/3-container-full/index.js new file mode 100644 index 000000000..b1968f663 --- /dev/null +++ b/test/configCases/container/3-container-full/index.js @@ -0,0 +1,15 @@ +it("should load the component from container", () => { + return import("./App").then(({ default: App }) => { + const rendered = App(); + expect(rendered).toBe( + "App rendered with [This is react 2.1.0] and [ComponentC rendered with [This is react 2.1.0] and [ComponentA rendered with [This is react 2.1.0]] and [ComponentB rendered with [This is react 2.1.0]]]" + ); + return import("./upgrade-react").then(({ default: upgrade }) => { + upgrade(); + const rendered = App(); + expect(rendered).toBe( + "App rendered with [This is react 9] and [ComponentC rendered with [This is react 9] and [ComponentA rendered with [This is react 9]] and [ComponentB rendered with [This is react 9]]]" + ); + }); + }); +}); diff --git a/test/configCases/container/3-container-full/node_modules/react.js b/test/configCases/container/3-container-full/node_modules/react.js new file mode 100644 index 000000000..ab65e86c7 --- /dev/null +++ b/test/configCases/container/3-container-full/node_modules/react.js @@ -0,0 +1,3 @@ +let version = "8"; +export default () => `This is react ${version}`; +export function setVersion(v) { version = v; } diff --git a/test/configCases/container/3-container-full/upgrade-react.js b/test/configCases/container/3-container-full/upgrade-react.js new file mode 100644 index 000000000..083ef3a45 --- /dev/null +++ b/test/configCases/container/3-container-full/upgrade-react.js @@ -0,0 +1,5 @@ +import { setVersion } from "react"; + +export default function upgrade() { + setVersion("9"); +} diff --git a/test/configCases/container/3-container-full/warnings.js b/test/configCases/container/3-container-full/warnings.js new file mode 100644 index 000000000..55cee80a3 --- /dev/null +++ b/test/configCases/container/3-container-full/warnings.js @@ -0,0 +1,3 @@ +module.exports = [ + [/No version specified and unable to automatically determine one/] +]; diff --git a/test/configCases/container/3-container-full/webpack.config.js b/test/configCases/container/3-container-full/webpack.config.js new file mode 100644 index 000000000..e38e0c6dd --- /dev/null +++ b/test/configCases/container/3-container-full/webpack.config.js @@ -0,0 +1,16 @@ +const { ModuleFederationPlugin } = require("../../../../").container; + +/** @type {import("../../../../").Configuration} */ +module.exports = { + plugins: [ + new ModuleFederationPlugin({ + remoteType: "commonjs-module", + remotes: { + containerB: "../1-container-full/container.js" + }, + shared: { + react: "^2.0.1" + } + }) + ] +}; diff --git a/test/configCases/container/container-entry-overridables/index.js b/test/configCases/container/container-entry-overridables/index.js index 77e618c41..74404c130 100644 --- a/test/configCases/container/container-entry-overridables/index.js +++ b/test/configCases/container/container-entry-overridables/index.js @@ -1,17 +1,19 @@ it("should expose modules from the container", async () => { const container = __non_webpack_require__("./container-file.js"); expect(container).toBeTypeOf("object"); - expect(container.override).toBeTypeOf("function"); - container.override({ - value: () => - new Promise(resolve => { - setTimeout(() => { - resolve(() => ({ - __esModule: true, - default: "overriden-value" - })); - }, 100); - }) + expect(container.init).toBeTypeOf("function"); + container.init({ + value: { + get: () => + new Promise(resolve => { + setTimeout(() => { + resolve(() => ({ + __esModule: true, + default: "overriden-value" + })); + }, 100); + }) + } }); const testFactory = await container.get("./test"); expect(testFactory).toBeTypeOf("function"); diff --git a/test/configCases/container/container-entry-overridables/webpack.config.js b/test/configCases/container/container-entry-overridables/webpack.config.js index a5856cf06..770f5f4de 100644 --- a/test/configCases/container/container-entry-overridables/webpack.config.js +++ b/test/configCases/container/container-entry-overridables/webpack.config.js @@ -1,4 +1,5 @@ const { ContainerPlugin } = require("../../../../").container; +const { ConsumeSharedPlugin } = require("../../../../").sharing; /** @type {import("../../../../").Configuration} */ module.exports = { @@ -11,9 +12,13 @@ module.exports = { }, exposes: { "./test": "./test" - }, - overridables: { - value: "./value" + } + }), + new ConsumeSharedPlugin({ + consumes: { + "./value": { + shareKey: "value" + } } }) ] diff --git a/test/configCases/container/container-reference-override/test.config.js b/test/configCases/container/container-reference-override/test.config.js index af5edeed4..3cf3450ed 100644 --- a/test/configCases/container/container-reference-override/test.config.js +++ b/test/configCases/container/container-reference-override/test.config.js @@ -1,18 +1,16 @@ module.exports = { moduleScope(scope) { - const o = { - test: () => () => x => `wrong ${x}` - }; + let ss; scope.ABC = { async get(module) { - const testFactory = await o["test"](); + const testFactory = await ss.test.get(); const test = testFactory(); return () => { return test(module); }; }, - override(overrides) { - Object.assign(o, overrides); + async init(shareScope) { + ss = shareScope; } }; } diff --git a/test/configCases/container/container-reference-override/webpack.config.js b/test/configCases/container/container-reference-override/webpack.config.js index d586db704..25ccfa1f5 100644 --- a/test/configCases/container/container-reference-override/webpack.config.js +++ b/test/configCases/container/container-reference-override/webpack.config.js @@ -1,4 +1,5 @@ const { ContainerReferencePlugin } = require("../../../../").container; +const { ProvideSharedPlugin } = require("../../../../").sharing; /** @type {import("../../../../").Configuration} */ module.exports = { @@ -7,10 +8,13 @@ module.exports = { remoteType: "var", remotes: { abc: "ABC" - }, - overrides: { + } + }), + new ProvideSharedPlugin({ + provides: { test: { - import: "./new-test" + import: "./new-test", + version: false } } }) diff --git a/test/configCases/container/error-handling/index.js b/test/configCases/container/error-handling/index.js index 913443682..a3a080b43 100644 --- a/test/configCases/container/error-handling/index.js +++ b/test/configCases/container/error-handling/index.js @@ -1,5 +1,34 @@ "use strict"; +let warnings = []; +let oldWarn; + +beforeEach(done => { + oldWarn = console.warn; + console.warn = m => warnings.push(m); + done(); +}); + +afterEach(done => { + expectWarning(); + console.warn = oldWarn; + done(); +}); + +const expectWarning = regexp => { + if (!regexp) { + expect(warnings).toEqual([]); + } else { + expect(warnings).toEqual( + expect.objectContaining({ + 0: expect.stringMatching(regexp), + length: 1 + }) + ); + } + warnings.length = 0; +}; + it("should allow to handle remote loading error with import()", async () => { await expect(import("./loading-error")).rejects.toEqual( expect.objectContaining({ @@ -33,6 +62,8 @@ it("should allow to handle invalid remote module error with import()", async () 'Module "./invalid" does not exist in container.\nwhile loading "./invalid" from webpack/container/reference/remote' }) ); + // at this point sharing initialization runs and triggers a warning that 'invalid' remote can't be loaded + expectWarning(/ENOENT/); }); it("should allow to handle invalid remote module error with require", async () => { diff --git a/test/configCases/container/exposed-overridables/node_modules/package.json b/test/configCases/container/exposed-overridables/node_modules/package.json new file mode 100644 index 000000000..a1069cc8a --- /dev/null +++ b/test/configCases/container/exposed-overridables/node_modules/package.json @@ -0,0 +1,4 @@ +{ + "name": "react", + "version": "1.0.0" +} diff --git a/test/configCases/container/exposed-overridables/webpack.config.js b/test/configCases/container/exposed-overridables/webpack.config.js index 441f6d2bf..525c2710b 100644 --- a/test/configCases/container/exposed-overridables/webpack.config.js +++ b/test/configCases/container/exposed-overridables/webpack.config.js @@ -9,7 +9,11 @@ module.exports = { exposes: { "./Button": "./Button" }, - shared: ["react"] + shared: { + react: { + eager: true + } + } }) ] }; diff --git a/test/configCases/container/overridables/cjs/test1.js b/test/configCases/container/overridables/cjs/test1.js deleted file mode 100644 index d7affb014..000000000 --- a/test/configCases/container/overridables/cjs/test1.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = "original1-cjs"; diff --git a/test/configCases/container/overridables/cjs/test2.js b/test/configCases/container/overridables/cjs/test2.js deleted file mode 100644 index b303a87db..000000000 --- a/test/configCases/container/overridables/cjs/test2.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = "original2-cjs"; diff --git a/test/configCases/container/overridables/cjs/test3.js b/test/configCases/container/overridables/cjs/test3.js deleted file mode 100644 index c0a9caf92..000000000 --- a/test/configCases/container/overridables/cjs/test3.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = "original3-cjs"; diff --git a/test/configCases/container/overridables/index.js b/test/configCases/container/overridables/index.js deleted file mode 100644 index 1c19816e8..000000000 --- a/test/configCases/container/overridables/index.js +++ /dev/null @@ -1,86 +0,0 @@ -__webpack_override__({ - test1: () => - new Promise(resolve => { - setTimeout(() => { - resolve(() => ({ - __esModule: true, - default: "overriden1" - })); - }, 100); - }), - test3: () => () => "overriden3", - package: () => - new Promise(resolve => { - setTimeout(() => { - resolve(() => "overriden-package"); - }, 100); - }), - "././options/test1": () => () => "1", - "nested1/options/test2": () => () => "2", - "nested2/deep/deep": () => () => "3" -}); - -it("should be able to override a esm overridable", () => { - return import("./modules/test1").then(m => { - expect(m.default).toBe("overriden1"); - }); -}); - -it("should be able to not override a esm overridable", () => { - return import("./modules/test2").then(m => { - expect(m.default).toBe("original2"); - }); -}); - -import test3 from "./modules/test3"; -it("should be able to use an overridable module in the initial chunk, but it's not overriden", () => { - expect(test3).toBe("original3"); -}); - -it("should be able to override a cjs overridable", () => { - return import("./cjs/test1").then(m => { - expect(m.default).toBe("overriden1"); - }); -}); - -it("should be able to not override a cjs overridable", () => { - return import("./cjs/test2").then(m => { - expect(m.default).toBe("original2-cjs"); - }); -}); - -it("should be able to use an overridable module in the initial chunk, and it's overriden", () => { - expect(require("./cjs/test3")).toBe("overriden3"); -}); - -it("should be able to override with a package name shortcut", () => { - return import("package").then(m => { - expect(m.default).toBe("overriden-package"); - }); -}); - -it("should be able to override a relative request via shortcut", () => { - return import("./options/test1").then(m => { - expect(m.default).toBe("1"); - }); -}); - -it("should be able to override a nested relative request via shortcut", () => { - return import("./options/test2").then(m => { - expect(m.default).toBe("2"); - }); -}); - -it("should be able to override a deep nested request", () => { - return import("./options/test3").then(m => { - expect(m.default).toBe("3"); - }); -}); - -it("should be able to override when fallback module has multiple chunks", () => { - return import("./splitChunks").then(m => { - expect(m.default).toBe( - "index+app+vendor+shared+shared-separate+shared+shared-separate" - ); - }); -}); diff --git a/test/configCases/container/overridables/modules/test1.js b/test/configCases/container/overridables/modules/test1.js deleted file mode 100644 index 2b0140d12..000000000 --- a/test/configCases/container/overridables/modules/test1.js +++ /dev/null @@ -1 +0,0 @@ -export default "original1"; diff --git a/test/configCases/container/overridables/modules/test2.js b/test/configCases/container/overridables/modules/test2.js deleted file mode 100644 index a17b946d0..000000000 --- a/test/configCases/container/overridables/modules/test2.js +++ /dev/null @@ -1 +0,0 @@ -export default "original2"; diff --git a/test/configCases/container/overridables/modules/test3.js b/test/configCases/container/overridables/modules/test3.js deleted file mode 100644 index c3cf42914..000000000 --- a/test/configCases/container/overridables/modules/test3.js +++ /dev/null @@ -1 +0,0 @@ -export default "original3"; diff --git a/test/configCases/container/overridables/node_modules/package/index.js b/test/configCases/container/overridables/node_modules/package/index.js deleted file mode 100644 index c55183c02..000000000 --- a/test/configCases/container/overridables/node_modules/package/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = "original-package"; diff --git a/test/configCases/container/overridables/options/test3.js b/test/configCases/container/overridables/options/test3.js deleted file mode 100644 index dd6228b1c..000000000 --- a/test/configCases/container/overridables/options/test3.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = "test3"; diff --git a/test/configCases/container/overridables/splitChunks/app.js b/test/configCases/container/overridables/splitChunks/app.js deleted file mode 100644 index d41fa2d88..000000000 --- a/test/configCases/container/overridables/splitChunks/app.js +++ /dev/null @@ -1,4 +0,0 @@ -import vendor from "./vendor"; -import shared from "./shared"; -import shared2 from "./shared-separate"; -export default "app+" + vendor + "+" + shared + "+" + shared2; diff --git a/test/configCases/container/overridables/splitChunks/index.js b/test/configCases/container/overridables/splitChunks/index.js deleted file mode 100644 index daf4480b6..000000000 --- a/test/configCases/container/overridables/splitChunks/index.js +++ /dev/null @@ -1,4 +0,0 @@ -import app from "./app"; -import shared from "./shared"; -import shared2 from "./shared-separate"; -export default "index+" + app + "+" + shared + "+" + shared2; diff --git a/test/configCases/container/overridables/splitChunks/shared-separate.js b/test/configCases/container/overridables/splitChunks/shared-separate.js deleted file mode 100644 index d249522d2..000000000 --- a/test/configCases/container/overridables/splitChunks/shared-separate.js +++ /dev/null @@ -1 +0,0 @@ -export default "shared-separate"; diff --git a/test/configCases/container/overridables/splitChunks/shared.js b/test/configCases/container/overridables/splitChunks/shared.js deleted file mode 100644 index 0e28fd9d1..000000000 --- a/test/configCases/container/overridables/splitChunks/shared.js +++ /dev/null @@ -1 +0,0 @@ -export default "shared"; diff --git a/test/configCases/container/overridables/splitChunks/vendor.js b/test/configCases/container/overridables/splitChunks/vendor.js deleted file mode 100644 index b09808c5e..000000000 --- a/test/configCases/container/overridables/splitChunks/vendor.js +++ /dev/null @@ -1 +0,0 @@ -export default "vendor"; diff --git a/test/configCases/container/overridables/webpack.config.js b/test/configCases/container/overridables/webpack.config.js deleted file mode 100644 index 9fcab6cc6..000000000 --- a/test/configCases/container/overridables/webpack.config.js +++ /dev/null @@ -1,45 +0,0 @@ -const { OverridablesPlugin, scope } = require("../../../../").container; - -/** @type {import("../../../../").Configuration} */ -module.exports = { - plugins: [ - new OverridablesPlugin({ - overridables: [ - { - test1: ["./modules/test1.js", "./cjs/test1"], - test2: "./modules/test2", - test3: { - import: "./modules/test3" - } - }, - { - test2: "./cjs/test2.js", - test3: "./cjs/../cjs/test3.js", - ...scope("nested1", ["./options/test2"]), - ...scope("nested2", { - ...scope("deep", { - deep: "./options/test3" - }) - }) - }, - "package", - "././options/test1", - "./splitChunks/app" - ] - }) - ], - optimization: { - splitChunks: { - cacheGroups: { - vendorTest: { - test: /splitChunks.vendor/, - enforce: true - }, - sharedTest: { - test: /splitChunks.shared-separate/, - enforce: true - } - } - } - } -}; diff --git a/test/configCases/plugins/terser-plugin/deprecations.js b/test/configCases/plugins/terser-plugin/deprecations.js index 9bff44c7d..e0a30c5df 100644 --- a/test/configCases/plugins/terser-plugin/deprecations.js +++ b/test/configCases/plugins/terser-plugin/deprecations.js @@ -1 +1 @@ -module.exports = [{ code: /DEP_WEBPACK_COMPILATION_OPTIMIZE_CHUNK_ASSETS/ }]; +module.exports = []; diff --git a/test/configCases/scope-hoisting/harmony-pure-default/deprecations.js b/test/configCases/scope-hoisting/harmony-pure-default/deprecations.js deleted file mode 100644 index 9bff44c7d..000000000 --- a/test/configCases/scope-hoisting/harmony-pure-default/deprecations.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = [{ code: /DEP_WEBPACK_COMPILATION_OPTIMIZE_CHUNK_ASSETS/ }]; diff --git a/test/configCases/sharing/consume-module/errors.js b/test/configCases/sharing/consume-module/errors.js new file mode 100644 index 000000000..722d5d0a4 --- /dev/null +++ b/test/configCases/sharing/consume-module/errors.js @@ -0,0 +1 @@ +module.exports = [[/prefix\/deep\/c/]]; diff --git a/test/configCases/sharing/consume-module/index.js b/test/configCases/sharing/consume-module/index.js new file mode 100644 index 000000000..b8bd82b3c --- /dev/null +++ b/test/configCases/sharing/consume-module/index.js @@ -0,0 +1,216 @@ +let warnings = []; +let oldWarn; + +beforeEach(done => { + oldWarn = console.warn; + console.warn = m => warnings.push(m); + done(); +}); + +afterEach(done => { + expectWarning(); + console.warn = oldWarn; + done(); +}); + +const expectWarning = regexp => { + if (!regexp) { + expect(warnings).toEqual([]); + } else { + expect(warnings).toEqual( + expect.objectContaining({ + 0: expect.stringMatching(regexp), + length: 1 + }) + ); + } + warnings.length = 0; +}; + +it("should load the shared modules", async () => { + __webpack_share_scopes__["test-scope"] = { + package: { + get: () => () => "shared package" + }, + "@scoped/package": { + get: () => Promise.resolve(() => "shared @scoped/package") + }, + "prefix/a": { + get: () => () => "shared prefix/a" + }, + "prefix/deep/c": { + get: () => () => "shared prefix/deep/c" + }, + "./relative1": { + get: () => () => "shared relative1" + } + }; + __webpack_share_scopes__["other-scope"] = { + "advanced/123": { + get: () => () => "123", + version: [1, 3, "0-beta", 1] + }, + "advanced/error1": { + get: () => { + throw new Error("error1"); + }, + version: [1, 2, 3] + }, + "advanced/error2": { + get: () => + Promise.resolve().then(() => { + throw new Error("error2"); + }), + version: [1, 2, 3] + }, + "advanced/error3": { + get: () => + Promise.resolve().then(() => () => { + throw new Error("error3"); + }), + version: [1, 2, 3] + }, + "advanced/error4": { + get: () => () => "wrong", + version: [1, 0, 0] + } + }; + { + const result = await import("package"); + expect(result.default).toBe("shared package"); + } + { + const result = await import("@scoped/package"); + expect(result.default).toBe("shared @scoped/package"); + } + { + const result = await import("prefix/a"); + expect(result.default).toBe("shared prefix/a"); + } + { + const result = await import("prefix/deep/b"); + expect(result.default).toBe("b"); + } + { + const result = await import("prefix/deep/c"); + expect(result.default).toBe("shared prefix/deep/c"); + } + { + const result = await import("./relative1"); + expect(result.default).toBe("shared relative1"); + } + { + const result = await import("./relative2"); + expect(result.default).toBe("relative2"); + } + { + const result = await import("advanced/123"); + expect(result.default).toBe("123"); + } + { + await expect(() => import("advanced/error0")).rejects.toEqual( + expect.objectContaining({ + message: expect.stringContaining("advanced/error0") + }) + ); + } + { + await expect(() => import("advanced/error1")).rejects.toEqual( + expect.objectContaining({ + message: expect.stringContaining("error1") + }) + ); + } + { + await expect(() => import("advanced/error2")).rejects.toEqual( + expect.objectContaining({ + message: expect.stringContaining("error2") + }) + ); + } + { + await expect(() => import("advanced/error3")).rejects.toEqual( + expect.objectContaining({ + message: expect.stringContaining("error3") + }) + ); + } + { + await expect(() => import("advanced/error4")).rejects.toEqual( + expect.objectContaining({ + message: expect.stringContaining("1.2.3") + }) + ); + } +}); + +it("should handle version matching correctly in strict and singleton mode", async () => { + __webpack_share_scopes__["default"] = { + strict0: { + get: () => () => "shared strict0", + version: [1, 1, 1] + }, + strict1: { + get: () => () => "shared strict1", + version: [1, 1, 1] + }, + strict2: { + get: () => () => "shared strict2", + version: [1, 1, 1] + }, + strict3: { + get: () => () => "shared strict3", + version: [1, 1, 1] + }, + strict4: { + get: () => () => "shared strict4", + version: [1, 1, 1] + }, + strict5: { + get: () => () => "shared strict5", + version: [1, 1, 1] + }, + singleton: { + get: () => () => "shared singleton", + version: [1, 1, 1] + } + }; + { + const result = await import("strict0"); + expect(result.default).toBe("shared strict0"); + expectWarning(); + } + { + const result = await import("strict1"); + expect(result.default).toBe("strict"); + expectWarning(/strict1@1\.1\.1 \(required strict1@1\.2\.0\)/); + } + { + const result = await import("strict2"); + expect(result.default).toBe("strict"); + expectWarning(/strict2@1\.1\.1 \(required strict2@1\.1\.0\)/); + } + { + const result = await import("strict3"); + expect(result.default).toBe("strict"); + expectWarning(/strict3@1\.1\.1 \(required strict3@1\.0\.0\)/); + } + { + const result = await import("strict4"); + expect(result.default).toBe("strict"); + expectWarning(/strict4@1\.1\.1 \(required strict4@2\.2\.3\)/); + } + { + await expect(() => import("strict5")).rejects.toEqual( + expect.objectContaining({ + message: expect.stringContaining("strict5") + }) + ); + expectWarning(); + } + { + const result = await import("singleton"); + expect(result.default).toBe("shared singleton"); + expectWarning(/singleton@1\.1\.1 \(required singleton@1\.1\.0\)/); + } +}); diff --git a/test/configCases/sharing/consume-module/node_modules/@scoped/package/index.js b/test/configCases/sharing/consume-module/node_modules/@scoped/package/index.js new file mode 100644 index 000000000..8678386a6 --- /dev/null +++ b/test/configCases/sharing/consume-module/node_modules/@scoped/package/index.js @@ -0,0 +1 @@ +module.exports = "@scoped/package"; diff --git a/test/configCases/sharing/consume-module/node_modules/package.js b/test/configCases/sharing/consume-module/node_modules/package.js new file mode 100644 index 000000000..7c1dac1c3 --- /dev/null +++ b/test/configCases/sharing/consume-module/node_modules/package.js @@ -0,0 +1 @@ +module.exports = "package"; diff --git a/test/configCases/sharing/consume-module/node_modules/prefix/a.js b/test/configCases/sharing/consume-module/node_modules/prefix/a.js new file mode 100644 index 000000000..6cd1d0075 --- /dev/null +++ b/test/configCases/sharing/consume-module/node_modules/prefix/a.js @@ -0,0 +1 @@ +module.exports = "a"; diff --git a/test/configCases/sharing/consume-module/node_modules/prefix/deep/b.js b/test/configCases/sharing/consume-module/node_modules/prefix/deep/b.js new file mode 100644 index 000000000..dfbbeb621 --- /dev/null +++ b/test/configCases/sharing/consume-module/node_modules/prefix/deep/b.js @@ -0,0 +1 @@ +module.exports = "b"; diff --git a/test/configCases/sharing/consume-module/node_modules/singleton.js b/test/configCases/sharing/consume-module/node_modules/singleton.js new file mode 100644 index 000000000..ec0140e27 --- /dev/null +++ b/test/configCases/sharing/consume-module/node_modules/singleton.js @@ -0,0 +1 @@ +module.exports = "singleton"; diff --git a/test/configCases/sharing/consume-module/node_modules/strict0.js b/test/configCases/sharing/consume-module/node_modules/strict0.js new file mode 100644 index 000000000..51df4cc66 --- /dev/null +++ b/test/configCases/sharing/consume-module/node_modules/strict0.js @@ -0,0 +1 @@ +module.exports = "strict"; diff --git a/test/configCases/sharing/consume-module/node_modules/strict1.js b/test/configCases/sharing/consume-module/node_modules/strict1.js new file mode 100644 index 000000000..51df4cc66 --- /dev/null +++ b/test/configCases/sharing/consume-module/node_modules/strict1.js @@ -0,0 +1 @@ +module.exports = "strict"; diff --git a/test/configCases/sharing/consume-module/node_modules/strict2.js b/test/configCases/sharing/consume-module/node_modules/strict2.js new file mode 100644 index 000000000..51df4cc66 --- /dev/null +++ b/test/configCases/sharing/consume-module/node_modules/strict2.js @@ -0,0 +1 @@ +module.exports = "strict"; diff --git a/test/configCases/sharing/consume-module/node_modules/strict3.js b/test/configCases/sharing/consume-module/node_modules/strict3.js new file mode 100644 index 000000000..51df4cc66 --- /dev/null +++ b/test/configCases/sharing/consume-module/node_modules/strict3.js @@ -0,0 +1 @@ +module.exports = "strict"; diff --git a/test/configCases/sharing/consume-module/node_modules/strict4.js b/test/configCases/sharing/consume-module/node_modules/strict4.js new file mode 100644 index 000000000..51df4cc66 --- /dev/null +++ b/test/configCases/sharing/consume-module/node_modules/strict4.js @@ -0,0 +1 @@ +module.exports = "strict"; diff --git a/test/configCases/sharing/consume-module/relative1.js b/test/configCases/sharing/consume-module/relative1.js new file mode 100644 index 000000000..ce7c23d16 --- /dev/null +++ b/test/configCases/sharing/consume-module/relative1.js @@ -0,0 +1 @@ +module.exports = "relative1"; diff --git a/test/configCases/sharing/consume-module/relative2.js b/test/configCases/sharing/consume-module/relative2.js new file mode 100644 index 000000000..7097875c2 --- /dev/null +++ b/test/configCases/sharing/consume-module/relative2.js @@ -0,0 +1 @@ +module.exports = "relative2"; diff --git a/test/configCases/sharing/consume-module/webpack.config.js b/test/configCases/sharing/consume-module/webpack.config.js new file mode 100644 index 000000000..e36fac411 --- /dev/null +++ b/test/configCases/sharing/consume-module/webpack.config.js @@ -0,0 +1,59 @@ +const { ConsumeSharedPlugin } = require("../../../../").sharing; + +/** @type {import("../../../../").Configuration} */ +module.exports = { + mode: "development", + plugins: [ + new ConsumeSharedPlugin({ + shareScope: "test-scope", + consumes: [ + "package", + "@scoped/package", + "prefix/", + "./relative1", + "./relative2", + { + "advanced/": { + import: false, + requiredVersion: "^1.2.3", + shareScope: "other-scope", + strictVersion: true + } + } + ] + }), + new ConsumeSharedPlugin({ + consumes: { + strict0: { + requiredVersion: "^1.0.0", + strictVersion: true + }, + strict1: { + requiredVersion: ">=1.2.0", + strictVersion: true + }, + strict2: { + requiredVersion: "1.1.0", + strictVersion: true + }, + strict3: { + requiredVersion: "~1.0.0", + strictVersion: true + }, + strict4: { + requiredVersion: "^2.2.3", + strictVersion: true + }, + strict5: { + import: false, + requiredVersion: "alpha", + strictVersion: true + }, + singleton: { + requiredVersion: "1.1.0", + strictVersion: false + } + } + }) + ] +}; diff --git a/test/configCases/sharing/provide-module/index.js b/test/configCases/sharing/provide-module/index.js new file mode 100644 index 000000000..335ce23e4 --- /dev/null +++ b/test/configCases/sharing/provide-module/index.js @@ -0,0 +1,36 @@ +it("should add provided modules to the share scope on init", async () => { + expect(__webpack_share_scopes__).toEqual({}); + await __webpack_init_sharing__("default"); + expect(Object.keys(__webpack_share_scopes__)).toEqual(["default"]); + await __webpack_init_sharing__("test-scope"); + await __webpack_init_sharing__("other-scope"); + expect(__webpack_init_sharing__("other-scope")).toBe( + __webpack_init_sharing__("other-scope") + ); + expect(Object.keys(__webpack_share_scopes__).length).toBe(3); + expect(Object.keys(__webpack_share_scopes__)).toContain("default"); + expect(Object.keys(__webpack_share_scopes__)).toContain("test-scope"); + expect(Object.keys(__webpack_share_scopes__)).toContain("other-scope"); + expect(Object.keys(__webpack_share_scopes__.default)).toContain("package"); + expect(Object.keys(__webpack_share_scopes__["test-scope"])).toContain( + "package" + ); + expect(Object.keys(__webpack_share_scopes__["test-scope"])).toContain( + "test1" + ); + expect(Object.keys(__webpack_share_scopes__["other-scope"])).toContain( + "test2" + ); + + { + const factory = await __webpack_share_scopes__["test-scope"]["test1"].get(); + expect(factory()).toBe("test1"); + } + + { + const factory = await __webpack_share_scopes__["other-scope"][ + "test2" + ].get(); + expect(factory()).toBe("test2"); + } +}); diff --git a/test/configCases/sharing/provide-module/node_modules/package/index.js b/test/configCases/sharing/provide-module/node_modules/package/index.js new file mode 100644 index 000000000..7c1dac1c3 --- /dev/null +++ b/test/configCases/sharing/provide-module/node_modules/package/index.js @@ -0,0 +1 @@ +module.exports = "package"; diff --git a/test/configCases/sharing/provide-module/node_modules/package/package.json b/test/configCases/sharing/provide-module/node_modules/package/package.json new file mode 100644 index 000000000..1587a6696 --- /dev/null +++ b/test/configCases/sharing/provide-module/node_modules/package/package.json @@ -0,0 +1,3 @@ +{ + "version": "1.0.0" +} diff --git a/test/configCases/container/overridables/options/test1.js b/test/configCases/sharing/provide-module/test1.js similarity index 100% rename from test/configCases/container/overridables/options/test1.js rename to test/configCases/sharing/provide-module/test1.js diff --git a/test/configCases/sharing/provide-module/test2-wrong.js b/test/configCases/sharing/provide-module/test2-wrong.js new file mode 100644 index 000000000..c298d0e59 --- /dev/null +++ b/test/configCases/sharing/provide-module/test2-wrong.js @@ -0,0 +1 @@ +module.exports = "test2-wrong"; diff --git a/test/configCases/container/overridables/options/test2.js b/test/configCases/sharing/provide-module/test2.js similarity index 100% rename from test/configCases/container/overridables/options/test2.js rename to test/configCases/sharing/provide-module/test2.js diff --git a/test/configCases/sharing/provide-module/webpack.config.js b/test/configCases/sharing/provide-module/webpack.config.js new file mode 100644 index 000000000..a6aae7bc0 --- /dev/null +++ b/test/configCases/sharing/provide-module/webpack.config.js @@ -0,0 +1,42 @@ +const { ProvideSharedPlugin } = require("../../../../").sharing; + +/** @type {import("../../../../").Configuration} */ +module.exports = { + plugins: [ + new ProvideSharedPlugin({ + shareScope: "test-scope", + provides: [ + { + test1: "./test1", + test2: { + import: "./test2-wrong", + shareScope: "other-scope", + version: "1.2.3" + } + }, + "package" + ] + }), + new ProvideSharedPlugin({ + provides: ["package"] + }), + new ProvideSharedPlugin({ + shareScope: "other-scope", + provides: { + test2: { + import: "./test2", + version: [1, 3, 0] + } + } + }), + new ProvideSharedPlugin({ + provides: { + test2: { + import: "./test2-wrong", + shareScope: "other-scope", + version: [1, 1, 9] + } + } + }) + ] +}; diff --git a/test/configCases/sharing/share-plugin/index.js b/test/configCases/sharing/share-plugin/index.js new file mode 100644 index 000000000..0a9017269 --- /dev/null +++ b/test/configCases/sharing/share-plugin/index.js @@ -0,0 +1,31 @@ +it("should provide and consume a normal library async", async () => { + expect(await import("lib1")).toEqual( + expect.objectContaining({ + default: "lib1" + }) + ); +}); + +it("should provide and consume a renamed library sync", () => { + expect(require("lib-two")).toEqual( + expect.objectContaining({ + default: "lib2" + }) + ); +}); + +it("should provide and consume a relative request async", async () => { + expect(await import("./relative1")).toEqual( + expect.objectContaining({ + default: "rel1" + }) + ); +}); + +it("should consume a remapped relative request async", async () => { + expect(await import("./relative2")).toEqual( + expect.objectContaining({ + default: "store" + }) + ); +}); diff --git a/test/configCases/sharing/share-plugin/node_modules/lib1/index.js b/test/configCases/sharing/share-plugin/node_modules/lib1/index.js new file mode 100644 index 000000000..461d2376f --- /dev/null +++ b/test/configCases/sharing/share-plugin/node_modules/lib1/index.js @@ -0,0 +1 @@ +export default "lib1"; diff --git a/test/configCases/sharing/share-plugin/node_modules/lib1/package.json b/test/configCases/sharing/share-plugin/node_modules/lib1/package.json new file mode 100644 index 000000000..2a38ae1d1 --- /dev/null +++ b/test/configCases/sharing/share-plugin/node_modules/lib1/package.json @@ -0,0 +1,3 @@ +{ + "version": "1.1.1" +} diff --git a/test/configCases/sharing/share-plugin/node_modules/lib2/index.js b/test/configCases/sharing/share-plugin/node_modules/lib2/index.js new file mode 100644 index 000000000..c2a6f9581 --- /dev/null +++ b/test/configCases/sharing/share-plugin/node_modules/lib2/index.js @@ -0,0 +1 @@ +export default "lib2"; diff --git a/test/configCases/sharing/share-plugin/node_modules/store/index.js b/test/configCases/sharing/share-plugin/node_modules/store/index.js new file mode 100644 index 000000000..225383e5c --- /dev/null +++ b/test/configCases/sharing/share-plugin/node_modules/store/index.js @@ -0,0 +1 @@ +export default "store"; diff --git a/test/configCases/sharing/share-plugin/node_modules/store/package.json b/test/configCases/sharing/share-plugin/node_modules/store/package.json new file mode 100644 index 000000000..ce04135d2 --- /dev/null +++ b/test/configCases/sharing/share-plugin/node_modules/store/package.json @@ -0,0 +1,3 @@ +{ + "version": "0" +} diff --git a/test/configCases/sharing/share-plugin/relative1.js b/test/configCases/sharing/share-plugin/relative1.js new file mode 100644 index 000000000..e460ea840 --- /dev/null +++ b/test/configCases/sharing/share-plugin/relative1.js @@ -0,0 +1 @@ +export default "rel1"; diff --git a/test/configCases/sharing/share-plugin/relative2.js b/test/configCases/sharing/share-plugin/relative2.js new file mode 100644 index 000000000..9f52f4c6b --- /dev/null +++ b/test/configCases/sharing/share-plugin/relative2.js @@ -0,0 +1 @@ +export default "rel2"; diff --git a/test/configCases/sharing/share-plugin/webpack.config.js b/test/configCases/sharing/share-plugin/webpack.config.js new file mode 100644 index 000000000..4a2c66e6f --- /dev/null +++ b/test/configCases/sharing/share-plugin/webpack.config.js @@ -0,0 +1,33 @@ +const { SharePlugin } = require("../../../../").sharing; + +/** @type {import("../../../../").Configuration} */ +module.exports = { + mode: "development", + devtool: false, + plugins: [ + new SharePlugin({ + shared: { + lib1: "^1.0.0", + "lib-two": { + import: "lib2", + requiredVersion: "^1.0.0", + version: "1.3.4", + strictVersion: true, + eager: true + }, + "./relative1": { + import: "./relative1", + version: false + }, + "./relative2": { + import: false, + shareKey: "store", + version: "0", + requiredVersion: "0", + strictVersion: true + }, + store: "0" + } + }) + ] +}; diff --git a/test/configCases/source-map/sources-array-production/deprecations.js b/test/configCases/source-map/sources-array-production/deprecations.js deleted file mode 100644 index 9bff44c7d..000000000 --- a/test/configCases/source-map/sources-array-production/deprecations.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = [{ code: /DEP_WEBPACK_COMPILATION_OPTIMIZE_CHUNK_ASSETS/ }]; diff --git a/test/statsCases/warnings-terser/webpack.config.js b/test/statsCases/warnings-terser/webpack.config.js index ab35f8345..4a7cc9a2d 100644 --- a/test/statsCases/warnings-terser/webpack.config.js +++ b/test/statsCases/warnings-terser/webpack.config.js @@ -22,7 +22,7 @@ module.exports = { }, warnings: true }, - warningsFilter(message, filename) { + warningsFilter(message, file, filename) { return /a\.js$/.test(filename); } }) diff --git a/types.d.ts b/types.d.ts index 64bc8050a..708b7e804 100644 --- a/types.d.ts +++ b/types.d.ts @@ -504,6 +504,7 @@ declare class Chunk { split(newChunk: Chunk): void; updateHash(hash: Hash, chunkGraph: ChunkGraph): void; getAllAsyncChunks(): Set; + getAllInitialChunks(): Set; getAllReferencedChunks(): Set; hasAsyncChunks(): boolean; getChildIdsByOrders( @@ -839,6 +840,11 @@ declare interface CodeGenerationResult { */ sources: Map; + /** + * the resulting data for all source types + */ + data?: Map; + /** * the runtime requirements */ @@ -1024,6 +1030,7 @@ declare class Compilation { */ creatingModuleDuringBuild: WeakMap>; entries: Map; + globalEntry: EntryData; entrypoints: Map; chunks: Set; chunkGroups: ChunkGroup[]; @@ -1108,6 +1115,15 @@ declare class Compilation { >), callback: (err?: WebpackError, result?: Module) => void ): void; + addInclude( + context: string, + dependency: Dependency, + options: { name: string } & Pick< + EntryDescriptionNormalized, + "filename" | "dependOn" | "library" + >, + callback: (err?: WebpackError, result?: Module) => void + ): void; rebuildModule( module: Module, callback: (err?: WebpackError, result?: Module) => void @@ -1531,6 +1547,77 @@ declare interface Configuration { */ watchOptions?: WatchOptions; } +declare class ConsumeSharedPlugin { + constructor(options: ConsumeSharedPluginOptions); + + /** + * Apply the plugin + */ + apply(compiler: Compiler): void; +} + +/** + * Options for consuming shared modules. + */ +declare interface ConsumeSharedPluginOptions { + /** + * Modules that should be consumed from share scope. When provided, property names are used to match requested modules in this compilation. + */ + consumes: Consumes; + + /** + * Share scope name used for all consumed modules (defaults to 'default'). + */ + shareScope?: string; +} +type Consumes = (string | ConsumesObject)[] | ConsumesObject; + +/** + * Advanced configuration for modules that should be consumed from share scope. + */ +declare interface ConsumesConfig { + /** + * Include the fallback module directly instead behind an async request. This allows to use fallback module in initial load too. All possible shared modules need to be eager too. + */ + eager?: boolean; + + /** + * Fallback module if no shared module is found in share scope. Defaults to the property name. + */ + import?: DevTool; + + /** + * Version requirement from module in share scope. + */ + requiredVersion?: string | (string | number)[]; + + /** + * Module is looked up under this key from the share scope. + */ + shareKey?: string; + + /** + * Share scope name. + */ + shareScope?: string; + + /** + * Allow only a single version of the shared module in share scope (disabled by default). + */ + singleton?: boolean; + + /** + * Do not accept shared module if version is not valid (defaults to yes, if local fallback module is available and shared module is not a singleton, otherwise no, has no effect if there is no required version specified). + */ + strictVersion?: boolean; +} + +/** + * Modules that should be consumed from share scope. Property names are used to match requested modules in this compilation. Relative requests are resolved, module requests are matched unresolved, absolute paths will match resolved requests. A trailing slash will match all requests with this prefix. In this case shareKey must also have a trailing slash. + */ +declare interface ConsumesObject { + [index: string]: string | ConsumesConfig; +} declare class ContainerPlugin { constructor(options: ContainerPluginOptions); @@ -1561,9 +1648,9 @@ declare interface ContainerPluginOptions { 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. + * The name of the share scope which is shared with the host (defaults to 'default'). */ - overridables?: Overridables; + shareScope?: string; } declare class ContainerReferencePlugin { constructor(options: ContainerReferencePluginOptions); @@ -1574,11 +1661,6 @@ declare class ContainerReferencePlugin { apply(compiler: Compiler): void; } declare interface ContainerReferencePluginOptions { - /** - * 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. */ @@ -1588,6 +1670,11 @@ 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. */ remotes: Remotes; + + /** + * The name of the share scope shared with all remotes (defaults to 'default'). + */ + shareScope?: string; } declare class ContextExclusionPlugin { constructor(negativeMatcher: RegExp); @@ -2002,10 +2089,15 @@ type Entry = | [string, ...string[]]; declare interface EntryData { /** - * dependencies of the entrypoint + * dependencies of the entrypoint that should be evaluated at startup */ dependencies: Dependency[]; + /** + * dependencies of the entrypoint that should be included by not evaluated + */ + includeDependencies: Dependency[]; + /** * options of the entrypoint */ @@ -3838,16 +3930,6 @@ declare interface ModuleFederationPluginOptions { */ 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. */ @@ -3859,7 +3941,12 @@ declare interface ModuleFederationPluginOptions { 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. + * Share scope name used for all shared modules (defaults to 'default'). + */ + shareScope?: string; + + /** + * Modules that should be shared in the share scope. When provided, property names are used to match requested modules in this compilation. */ shared?: Shared; } @@ -5144,60 +5231,6 @@ declare interface OutputNormalized { */ webassemblyModuleFilename?: string; } -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 { - constructor(options: OverridablesPluginOptions); - - /** - * Apply the plugin - */ - apply(compiler: Compiler): void; -} - -/** - * 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. - */ -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 { constructor(); parse( @@ -5391,6 +5424,58 @@ declare class ProvidePlugin { */ apply(compiler: Compiler): void; } +declare class ProvideSharedPlugin { + constructor(options: ProvideSharedPluginOptions); + + /** + * Apply the plugin + */ + apply(compiler: Compiler): void; +} +declare interface ProvideSharedPluginOptions { + /** + * Modules that should be provided as shared modules to the share scope. When provided, property name is used as share key, otherwise share key is automatically inferred from request. + */ + provides: Provides; + + /** + * Share scope name used for all provided modules (defaults to 'default'). + */ + shareScope?: string; +} +type Provides = (string | ProvidesObject)[] | ProvidesObject; + +/** + * Advanced configuration for modules that should be provided as shared modules to the share scope. + */ +declare interface ProvidesConfig { + /** + * Include the provided module directly instead behind an async request. This allows to use this shared module in initial load too. All possible shared modules need to be eager too. + */ + eager?: boolean; + + /** + * Request to a module that should be provided as shared module to the share scope. + */ + import: string; + + /** + * Share scope name. + */ + shareScope?: string; + + /** + * Version of the provided module. Will replace lower matching versions, but not higher. + */ + version?: string | false | (string | number)[]; +} + +/** + * Modules that should be provided as shared modules to the share scope. Property names are used as share keys. + */ +declare interface ProvidesObject { + [index: string]: string | ProvidesConfig; +} type PublicPath = | string | ((pathData: PathData, assetInfo: AssetInfo) => string); @@ -5428,6 +5513,11 @@ declare interface RemotesConfig { * Container locations from which modules should be resolved and loaded at runtime. */ external: string | string[]; + + /** + * The name of the share scope shared with this remote. + */ + shareScope?: string; } /** @@ -6319,6 +6409,42 @@ declare abstract class RuntimeTemplate { */ runtimeRequirements: Set; }): string; + asyncModuleFactory(__0: { + /** + * the async block + */ + block: AsyncDependenciesBlock; + /** + * the chunk graph + */ + chunkGraph: ChunkGraph; + /** + * if set, will be filled with runtime requirements + */ + runtimeRequirements: Set; + /** + * request string used originally + */ + request?: string; + }): string; + syncModuleFactory(__0: { + /** + * the dependency + */ + dependency: Dependency; + /** + * the chunk graph + */ + chunkGraph: ChunkGraph; + /** + * if set, will be filled with runtime requirements + */ + runtimeRequirements: Set; + /** + * request string used originally + */ + request?: string; + }): string; defineEsModuleFlagStatement(__0: { /** * the name of the exports object @@ -6350,20 +6476,78 @@ declare abstract class Serializer { serialize(obj?: any, context?: any): any; deserialize(value?: any, context?: any): any; } -type Shared = (string | SharedObject)[] | SharedObject; +declare class SharePlugin { + constructor(options: SharePluginOptions); -/** - * 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. + * Apply the plugin */ - import: string; + apply(compiler: Compiler): void; } /** - * Modules that should be shared with remotes and/or host. Property names are used as shared keys. + * Options for shared modules. + */ +declare interface SharePluginOptions { + /** + * Share scope name used for all shared modules (defaults to 'default'). + */ + shareScope?: string; + + /** + * Modules that should be shared in the share scope. When provided, property names are used to match requested modules in this compilation. + */ + shared: Shared; +} +type Shared = (string | SharedObject)[] | SharedObject; + +/** + * Advanced configuration for modules that should be shared in the share scope. + */ +declare interface SharedConfig { + /** + * Include the provided and fallback module directly instead behind an async request. This allows to use this shared module in initial load too. All possible shared modules need to be eager too. + */ + eager?: boolean; + + /** + * Provided module that should be provided to share scope. Also acts as fallback module if no shared module is found in share scope or version isn't valid. Defaults to the property name. + */ + import?: DevTool; + + /** + * Version requirement from module in share scope. + */ + requiredVersion?: string | (string | number)[]; + + /** + * Module is looked up under this key from the share scope. + */ + shareKey?: string; + + /** + * Share scope name. + */ + shareScope?: string; + + /** + * Allow only a single version of the shared module in share scope (disabled by default). + */ + singleton?: boolean; + + /** + * Do not accept shared module if version is not valid (defaults to yes, if local fallback module is available and shared module is not a singleton, otherwise no, has no effect if there is no required version specified). + */ + strictVersion?: boolean; + + /** + * Version of the provided module. Will replace lower matching versions, but not higher. + */ + version?: string | false | (string | number)[]; +} + +/** + * Modules that should be shared in the share scope. Property names are used to match requested modules in this compilation. Relative requests are resolved, module requests are matched unresolved, absolute paths will match resolved requests. A trailing slash will match all requests with this prefix. In this case shareKey must also have a trailing slash. */ declare interface SharedObject { [index: string]: string | SharedConfig; @@ -7463,7 +7647,8 @@ declare namespace exports { export let startupNoDefault: string; export let interceptModuleExecution: string; export let global: string; - export let overrides: string; + export let shareScopeMap: string; + export let initializeSharing: string; export let getUpdateManifestFilename: string; export let hmrDownloadManifest: string; export let hmrDownloadUpdateHandlers: string; @@ -7545,10 +7730,18 @@ declare namespace exports { export { ContainerPlugin, ContainerReferencePlugin, - ModuleFederationPlugin, - OverridablesPlugin + ModuleFederationPlugin }; } + export namespace sharing { + export const scope: ( + scope: string, + options: + | Record + | (string | Record)[] + ) => Record; + export { ConsumeSharedPlugin, ProvideSharedPlugin, SharePlugin }; + } export namespace debug { export { ProfilingPlugin }; } diff --git a/yarn.lock b/yarn.lock index f27b2c139..f5c5ac972 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,26 +2,26 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" - integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.1.tgz#d5481c5095daa1c57e16e54c6f9198443afb49ff" + integrity sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw== dependencies: - "@babel/highlight" "^7.8.3" + "@babel/highlight" "^7.10.1" "@babel/core@^7.1.0", "@babel/core@^7.7.2", "@babel/core@^7.7.5": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.9.6.tgz#d9aa1f580abf3b2286ef40b6904d390904c63376" - integrity sha512-nD3deLvbsApbHAHttzIssYqgb883yU/d9roe4RZymBCDaZryMJDbptVpEpeQuRh4BJ+SYI8le9YGxKvFEvl1Wg== + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.10.2.tgz#bd6786046668a925ac2bd2fd95b579b92a23b36a" + integrity sha512-KQmV9yguEjQsXqyOUGKjS4+3K8/DlOCE2pZcq4augdQmtTy5iv5EHtmMSJ7V4c1BIPjuwtZYqYLCq9Ga+hGBRQ== dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.9.6" - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helpers" "^7.9.6" - "@babel/parser" "^7.9.6" - "@babel/template" "^7.8.6" - "@babel/traverse" "^7.9.6" - "@babel/types" "^7.9.6" + "@babel/code-frame" "^7.10.1" + "@babel/generator" "^7.10.2" + "@babel/helper-module-transforms" "^7.10.1" + "@babel/helpers" "^7.10.1" + "@babel/parser" "^7.10.2" + "@babel/template" "^7.10.1" + "@babel/traverse" "^7.10.1" + "@babel/types" "^7.10.2" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.1" @@ -31,65 +31,65 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/generator@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.6.tgz#5408c82ac5de98cda0d77d8124e99fa1f2170a43" - integrity sha512-+htwWKJbH2bL72HRluF8zumBxzuX0ZZUFl3JLNyoUjM/Ho8wnVpPXM6aUz8cfKDqQ/h7zHqKt4xzJteUosckqQ== +"@babel/generator@^7.10.1", "@babel/generator@^7.10.2": + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.10.2.tgz#0fa5b5b2389db8bfdfcc3492b551ee20f5dd69a9" + integrity sha512-AxfBNHNu99DTMvlUPlt1h2+Hn7knPpH5ayJ8OqDWSeLld+Fi2AYBTC/IejWDM9Edcii4UzZRCsbUt0WlSDsDsA== dependencies: - "@babel/types" "^7.9.6" + "@babel/types" "^7.10.2" jsesc "^2.5.1" lodash "^4.17.13" source-map "^0.5.0" -"@babel/helper-function-name@^7.9.5": - version "7.9.5" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz#2b53820d35275120e1874a82e5aabe1376920a5c" - integrity sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw== +"@babel/helper-function-name@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.1.tgz#92bd63829bfc9215aca9d9defa85f56b539454f4" + integrity sha512-fcpumwhs3YyZ/ttd5Rz0xn0TpIwVkN7X0V38B9TWNfVF42KEkhkAAuPCQ3oXmtTRtiPJrmZ0TrfS0GKF0eMaRQ== dependencies: - "@babel/helper-get-function-arity" "^7.8.3" - "@babel/template" "^7.8.3" - "@babel/types" "^7.9.5" + "@babel/helper-get-function-arity" "^7.10.1" + "@babel/template" "^7.10.1" + "@babel/types" "^7.10.1" -"@babel/helper-get-function-arity@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz#b894b947bd004381ce63ea1db9f08547e920abd5" - integrity sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA== +"@babel/helper-get-function-arity@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.1.tgz#7303390a81ba7cb59613895a192b93850e373f7d" + integrity sha512-F5qdXkYGOQUb0hpRaPoetF9AnsXknKjWMZ+wmsIRsp5ge5sFh4c3h1eH2pRTTuy9KKAA2+TTYomGXAtEL2fQEw== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.1" -"@babel/helper-member-expression-to-functions@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz#659b710498ea6c1d9907e0c73f206eee7dadc24c" - integrity sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA== +"@babel/helper-member-expression-to-functions@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.1.tgz#432967fd7e12a4afef66c4687d4ca22bc0456f15" + integrity sha512-u7XLXeM2n50gb6PWJ9hoO5oO7JFPaZtrh35t8RqKLT1jFKj9IWeD1zrcrYp1q1qiZTdEarfDWfTIP8nGsu0h5g== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.1" -"@babel/helper-module-imports@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz#7fe39589b39c016331b6b8c3f441e8f0b1419498" - integrity sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg== +"@babel/helper-module-imports@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.1.tgz#dd331bd45bccc566ce77004e9d05fe17add13876" + integrity sha512-SFxgwYmZ3HZPyZwJRiVNLRHWuW2OgE5k2nrVs6D9Iv4PPnXVffuEHy83Sfx/l4SqF+5kyJXjAyUmrG7tNm+qVg== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.1" -"@babel/helper-module-transforms@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz#43b34dfe15961918707d247327431388e9fe96e5" - integrity sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA== +"@babel/helper-module-transforms@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.10.1.tgz#24e2f08ee6832c60b157bb0936c86bef7210c622" + integrity sha512-RLHRCAzyJe7Q7sF4oy2cB+kRnU4wDZY/H2xJFGof+M+SJEGhZsb+GFj5j1AD8NiSaVBJ+Pf0/WObiXu/zxWpFg== dependencies: - "@babel/helper-module-imports" "^7.8.3" - "@babel/helper-replace-supers" "^7.8.6" - "@babel/helper-simple-access" "^7.8.3" - "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/template" "^7.8.6" - "@babel/types" "^7.9.0" + "@babel/helper-module-imports" "^7.10.1" + "@babel/helper-replace-supers" "^7.10.1" + "@babel/helper-simple-access" "^7.10.1" + "@babel/helper-split-export-declaration" "^7.10.1" + "@babel/template" "^7.10.1" + "@babel/types" "^7.10.1" lodash "^4.17.13" -"@babel/helper-optimise-call-expression@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz#7ed071813d09c75298ef4f208956006b6111ecb9" - integrity sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ== +"@babel/helper-optimise-call-expression@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.1.tgz#b4a1f2561870ce1247ceddb02a3860fa96d72543" + integrity sha512-a0DjNS1prnBsoKx83dP2falChcs7p3i8VMzdrSbfLhuQra/2ENC4sbri34dz/rWmDADsmF1q5GbfaXydh0Jbjg== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.1" "@babel/helper-plugin-utils@^7.0.0": version "7.0.0" @@ -101,58 +101,58 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670" integrity sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ== -"@babel/helper-replace-supers@^7.8.6": - version "7.8.6" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz#5ada744fd5ad73203bf1d67459a27dcba67effc8" - integrity sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA== +"@babel/helper-replace-supers@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.1.tgz#ec6859d20c5d8087f6a2dc4e014db7228975f13d" + integrity sha512-SOwJzEfpuQwInzzQJGjGaiG578UYmyi2Xw668klPWV5n07B73S0a9btjLk/52Mlcxa+5AdIYqws1KyXRfMoB7A== dependencies: - "@babel/helper-member-expression-to-functions" "^7.8.3" - "@babel/helper-optimise-call-expression" "^7.8.3" - "@babel/traverse" "^7.8.6" - "@babel/types" "^7.8.6" + "@babel/helper-member-expression-to-functions" "^7.10.1" + "@babel/helper-optimise-call-expression" "^7.10.1" + "@babel/traverse" "^7.10.1" + "@babel/types" "^7.10.1" -"@babel/helper-simple-access@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz#7f8109928b4dab4654076986af575231deb639ae" - integrity sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw== +"@babel/helper-simple-access@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.1.tgz#08fb7e22ace9eb8326f7e3920a1c2052f13d851e" + integrity sha512-VSWpWzRzn9VtgMJBIWTZ+GP107kZdQ4YplJlCmIrjoLVSi/0upixezHCDG8kpPVTBJpKfxTH01wDhh+jS2zKbw== dependencies: - "@babel/template" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/template" "^7.10.1" + "@babel/types" "^7.10.1" -"@babel/helper-split-export-declaration@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9" - integrity sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA== +"@babel/helper-split-export-declaration@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.1.tgz#c6f4be1cbc15e3a868e4c64a17d5d31d754da35f" + integrity sha512-UQ1LVBPrYdbchNhLwj6fetj46BcFwfS4NllJo/1aJsT+1dLTEnXJL0qHqtY7gPzF8S2fXBJamf1biAXV3X077g== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.1" -"@babel/helper-validator-identifier@^7.9.5": - version "7.9.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80" - integrity sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g== +"@babel/helper-validator-identifier@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz#5770b0c1a826c4f53f5ede5e153163e0318e94b5" + integrity sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw== -"@babel/helpers@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.9.6.tgz#092c774743471d0bb6c7de3ad465ab3d3486d580" - integrity sha512-tI4bUbldloLcHWoRUMAj4g1bF313M/o6fBKhIsb3QnGVPwRm9JsNf/gqMkQ7zjqReABiffPV6RWj7hEglID5Iw== +"@babel/helpers@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.1.tgz#a6827b7cb975c9d9cef5fd61d919f60d8844a973" + integrity sha512-muQNHF+IdU6wGgkaJyhhEmI54MOZBKsFfsXFhboz1ybwJ1Kl7IHlbm2a++4jwrmY5UYsgitt5lfqo1wMFcHmyw== dependencies: - "@babel/template" "^7.8.3" - "@babel/traverse" "^7.9.6" - "@babel/types" "^7.9.6" + "@babel/template" "^7.10.1" + "@babel/traverse" "^7.10.1" + "@babel/types" "^7.10.1" -"@babel/highlight@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797" - integrity sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg== +"@babel/highlight@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.1.tgz#841d098ba613ba1a427a2b383d79e35552c38ae0" + integrity sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg== dependencies: + "@babel/helper-validator-identifier" "^7.10.1" chalk "^2.0.0" - esutils "^2.0.2" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.7.5", "@babel/parser@^7.8.6", "@babel/parser@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.6.tgz#3b1bbb30dabe600cd72db58720998376ff653bc7" - integrity sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q== +"@babel/parser@^7.1.0", "@babel/parser@^7.10.1", "@babel/parser@^7.10.2", "@babel/parser@^7.7.5": + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.2.tgz#871807f10442b92ff97e4783b9b54f6a0ca812d0" + integrity sha512-PApSXlNMJyB4JiGVhCOlzKIif+TKFTvu0aQAhnTvfP/z3vVSN6ZypH5bfUNwFXXjRQtUEBNFd2PtmCmG2Py3qQ== "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -231,36 +231,36 @@ dependencies: regenerator-runtime "^0.13.2" -"@babel/template@^7.3.3", "@babel/template@^7.7.4", "@babel/template@^7.8.3", "@babel/template@^7.8.6": - version "7.8.6" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b" - integrity sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg== +"@babel/template@^7.10.1", "@babel/template@^7.3.3", "@babel/template@^7.7.4": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.1.tgz#e167154a94cb5f14b28dc58f5356d2162f539811" + integrity sha512-OQDg6SqvFSsc9A0ej6SKINWrpJiNonRIniYondK2ViKhB06i3c0s+76XUft71iqBEe9S1OKsHwPAjfHnuvnCig== dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/parser" "^7.8.6" - "@babel/types" "^7.8.6" + "@babel/code-frame" "^7.10.1" + "@babel/parser" "^7.10.1" + "@babel/types" "^7.10.1" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.7.4", "@babel/traverse@^7.8.6", "@babel/traverse@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.6.tgz#5540d7577697bf619cc57b92aa0f1c231a94f442" - integrity sha512-b3rAHSjbxy6VEAvlxM8OV/0X4XrG72zoxme6q1MOoe2vd0bEc+TwayhuC1+Dfgqh1QEG+pj7atQqvUprHIccsg== +"@babel/traverse@^7.1.0", "@babel/traverse@^7.10.1", "@babel/traverse@^7.7.4": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.10.1.tgz#bbcef3031e4152a6c0b50147f4958df54ca0dd27" + integrity sha512-C/cTuXeKt85K+p08jN6vMDz8vSV0vZcI0wmQ36o6mjbuo++kPMdpOYw23W2XH04dbRt9/nMEfA4W3eR21CD+TQ== dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.9.6" - "@babel/helper-function-name" "^7.9.5" - "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/parser" "^7.9.6" - "@babel/types" "^7.9.6" + "@babel/code-frame" "^7.10.1" + "@babel/generator" "^7.10.1" + "@babel/helper-function-name" "^7.10.1" + "@babel/helper-split-export-declaration" "^7.10.1" + "@babel/parser" "^7.10.1" + "@babel/types" "^7.10.1" debug "^4.1.0" globals "^11.1.0" lodash "^4.17.13" -"@babel/types@^7.0.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.0", "@babel/types@^7.9.5", "@babel/types@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.6.tgz#2c5502b427251e9de1bd2dff95add646d95cc9f7" - integrity sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA== +"@babel/types@^7.0.0", "@babel/types@^7.10.1", "@babel/types@^7.10.2", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.10.2.tgz#30283be31cad0dbf6fb00bd40641ca0ea675172d" + integrity sha512-AD3AwWBSz0AWF0AkCN9VPiWrvldXq+/e3cHa4J89vo4ymjz1XwrBFFVZmkJTsQIPNk+ZVomPSXUJqq8yyjZsng== dependencies: - "@babel/helper-validator-identifier" "^7.9.5" + "@babel/helper-validator-identifier" "^7.10.1" lodash "^4.17.13" to-fast-properties "^2.0.0" @@ -1013,7 +1013,7 @@ anymatch@^3.0.3: normalize-path "^3.0.0" picomatch "^2.0.4" -aproba@^1.0.3, aproba@^1.1.1: +aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== @@ -1341,28 +1341,27 @@ bundle-loader@~0.5.0: dependencies: loader-utils "^1.1.0" -cacache@^13.0.1: - version "13.0.1" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-13.0.1.tgz#a8000c21697089082f85287a1aec6e382024a71c" - integrity sha512-5ZvAxd05HDDU+y9BVvcqYu2LLXmPnQ0hW62h32g4xBTgL/MppR4/04NHfj/ycM2y6lmTnbw6HVi+1eN0Psba6w== +cacache@^15.0.3: + version "15.0.3" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.3.tgz#2225c2d1dd8e872339950d6a39c051e0e9334392" + integrity sha512-bc3jKYjqv7k4pWh7I/ixIjfcjPul4V4jme/WbjvwGS5LzoPL/GzXr4C5EgPNLO/QEZl9Oi61iGitYEdwcrwLCQ== dependencies: - chownr "^1.1.2" - figgy-pudding "^3.5.1" + chownr "^2.0.0" fs-minipass "^2.0.0" glob "^7.1.4" - graceful-fs "^4.2.2" infer-owner "^1.0.4" lru-cache "^5.1.1" - minipass "^3.0.0" + minipass "^3.1.1" minipass-collect "^1.0.2" minipass-flush "^1.0.5" minipass-pipeline "^1.2.2" - mkdirp "^0.5.1" - move-concurrently "^1.0.1" - p-map "^3.0.0" + mkdirp "^1.0.3" + move-file "^2.0.0" + p-map "^4.0.0" promise-inflight "^1.0.1" - rimraf "^2.7.1" - ssri "^7.0.0" + rimraf "^3.0.2" + ssri "^8.0.0" + tar "^6.0.2" unique-filename "^1.1.1" cache-base@^1.0.1: @@ -1501,11 +1500,16 @@ chokidar@^2.0.4: optionalDependencies: fsevents "^1.2.7" -chownr@^1.1.1, chownr@^1.1.2: +chownr@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142" integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw== +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + chrome-trace-event@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4" @@ -1559,7 +1563,7 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" -cli-truncate@^2.1.0: +cli-truncate@2.1.0, cli-truncate@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== @@ -1746,18 +1750,6 @@ convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: dependencies: safe-buffer "~5.1.1" -copy-concurrently@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" - integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A== - dependencies: - aproba "^1.1.1" - fs-write-stream-atomic "^1.0.8" - iferr "^0.1.5" - mkdirp "^0.5.1" - rimraf "^2.5.4" - run-queue "^1.0.0" - copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" @@ -2474,9 +2466,9 @@ eslint-plugin-es@^3.0.0: regexpp "^3.0.0" eslint-plugin-jest@^23.8.1: - version "23.13.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-23.13.1.tgz#b2ce83f76064ad8ba1f1f26f322b86a86e44148e" - integrity sha512-TRLJH6M6EDvGocD98a7yVThrAOCK9WJfo9phuUb0MJptcrOYZeCKzC9aOzZCD93sxXCsiJVZywaTHdI/mAi0FQ== + version "23.13.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-23.13.2.tgz#7b7993b4e09be708c696b02555083ddefd7e4cc7" + integrity sha512-qZit+moTXTyZFNDqSIR88/L3rdBlTU7CuW6XmyErD2FfHEkdoLgThkRbiQjzgYnX6rfgLx3Ci4eJmF4Ui5v1Cw== dependencies: "@typescript-eslint/experimental-utils" "^2.5.0" @@ -2802,11 +2794,6 @@ fb-watchman@^2.0.0: dependencies: bser "^2.0.0" -figgy-pudding@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" - integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w== - figures@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.1.0.tgz#4b198dd07d8d71530642864af2d45dd9e459c4ec" @@ -2993,16 +2980,6 @@ fs-monkey@1.0.1: resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.1.tgz#4a82f36944365e619f4454d9fff106553067b781" integrity sha512-fcSa+wyTqZa46iWweI7/ZiUfegOZl0SG8+dltIwFXo7+zYU9J9kpS3NB6pZcSlJdhvIwp81Adx2XhZorncxiaA== -fs-write-stream-atomic@^1.0.8: - version "1.0.10" - resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" - integrity sha1-tH31NJPvkR33VzHnCp3tAYnbQMk= - dependencies: - graceful-fs "^4.1.2" - iferr "^0.1.5" - imurmurhash "^0.1.4" - readable-stream "1 || 2" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -3156,7 +3133,7 @@ globals@^12.1.0: dependencies: type-fest "^0.8.1" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4: +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: version "4.2.4" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== @@ -3322,11 +3299,6 @@ icss-utils@^4.0.0, icss-utils@^4.1.1: dependencies: postcss "^7.0.14" -iferr@^0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" - integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= - ignore-walk@^3.0.1: version "3.0.3" resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" @@ -4123,14 +4095,6 @@ jest-watcher@^25.5.0: jest-util "^25.5.0" string-length "^3.1.0" -jest-worker@^25.4.0: - version "25.4.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-25.4.0.tgz#ee0e2ceee5a36ecddf5172d6d7e0ab00df157384" - integrity sha512-ghAs/1FtfYpMmYQ0AHqxV62XPvKdUDIBBApMZfly+E9JEmYh2K45G0R5dWxx986RN12pRCxsViwQVtGl+N4whw== - dependencies: - merge-stream "^2.0.0" - supports-color "^7.0.0" - jest-worker@^25.5.0: version "25.5.0" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-25.5.0.tgz#2611d071b79cea0f43ee57a3d118593ac1547db1" @@ -4139,6 +4103,14 @@ jest-worker@^25.5.0: merge-stream "^2.0.0" supports-color "^7.0.0" +jest-worker@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.0.0.tgz#4920c7714f0a96c6412464718d0c58a3df3fb066" + integrity sha512-pPaYa2+JnwmiZjK9x7p9BoZht+47ecFCDFA/CJxspHzeDvQcfVBLWzCiWyo+EGrSiQMWZtCFo9iSvMZnAAo8vw== + dependencies: + merge-stream "^2.0.0" + supports-color "^7.0.0" + jest@^25.1.0: version "25.5.4" resolved "https://registry.yarnpkg.com/jest/-/jest-25.5.4.tgz#f21107b6489cfe32b076ce2adcadee3587acb9db" @@ -4404,11 +4376,12 @@ lines-and-columns@^1.1.6: integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= lint-staged@^10.0.8: - version "10.2.4" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.2.4.tgz#0ed5d1cf06bdac0d3fbb003931bb6df3771fbf42" - integrity sha512-doTMGKXQAT34c3S3gwDrTnXmCZp/z1/92D8suPqqh755sKPT18ew1NoPNHxJdrvv1D4WrJ7CEnx79Ns3EdEFbg== + version "10.2.6" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.2.6.tgz#7d9658bd89dee946a859cbfc6e09566a9fb50b53" + integrity sha512-2oEBWyPZHkdyjKcIv2U6ay80Q52ZMlZZrUnfsV0WTVcgzPlt3o2t5UFy2v8ETUTsIDZ0xSJVnffWCgD3LF6xTQ== dependencies: chalk "^4.0.0" + cli-truncate "2.1.0" commander "^5.1.0" cosmiconfig "^6.0.0" debug "^4.1.1" @@ -4801,6 +4774,13 @@ minipass@^3.0.0: dependencies: yallist "^4.0.0" +minipass@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" + integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== + dependencies: + yallist "^4.0.0" + minizlib@^1.2.1: version "1.3.3" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" @@ -4808,6 +4788,14 @@ minizlib@^1.2.1: dependencies: minipass "^2.9.0" +minizlib@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.0.tgz#fd52c645301ef09a63a2c209697c294c6ce02cf3" + integrity sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + mixin-deep@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" @@ -4830,17 +4818,17 @@ mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3: dependencies: minimist "^1.2.5" -move-concurrently@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" - integrity sha1-viwAX9oy4LKa8fBdfEszIUxwH5I= +mkdirp@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +move-file@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/move-file/-/move-file-2.0.0.tgz#83ffa309b5d7f69d518b28e1333e2ffadf331e3e" + integrity sha512-cdkdhNCgbP5dvS4tlGxZbD+nloio9GIimP57EjqFhwLcMjnU+XJKAZzlmg/TN/AK1LuNAdTSvm3CPPP4Xkv0iQ== dependencies: - aproba "^1.1.1" - copy-concurrently "^1.0.0" - fs-write-stream-atomic "^1.0.8" - mkdirp "^0.5.1" - rimraf "^2.5.4" - run-queue "^1.0.3" + path-exists "^4.0.0" ms@2.0.0: version "2.0.0" @@ -5212,13 +5200,6 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" -p-map@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-3.0.0.tgz#d704d9af8a2ba684e2600d9a215983d4141a979d" - integrity sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ== - dependencies: - aggregate-error "^3.0.0" - p-map@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" @@ -5773,7 +5754,7 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -"readable-stream@1 || 2", readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6: +readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== @@ -5969,14 +5950,14 @@ rimraf@2.6.3: dependencies: glob "^7.1.3" -rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.7.1: +rimraf@^2.6.1: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: glob "^7.1.3" -rimraf@^3.0.0: +rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== @@ -5995,13 +5976,6 @@ run-async@^2.2.0: dependencies: is-promise "^2.1.0" -run-queue@^1.0.0, run-queue@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" - integrity sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec= - dependencies: - aproba "^1.1.1" - rxjs@^6.4.0: version "6.5.3" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.3.tgz#510e26317f4db91a7eb1de77d9dd9ba0a4899a3a" @@ -6364,13 +6338,12 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" -ssri@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-7.0.1.tgz#b0cab7bbb11ac9ea07f003453e2011f8cbed9f34" - integrity sha512-FfndBvkXL9AHyGLNzU3r9AvYIBBZ7gm+m+kd0p8cT3/v4OliMAyipZAhLVEv1Zi/k4QFq9CstRGVd9pW/zcHFQ== +ssri@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.0.tgz#79ca74e21f8ceaeddfcb4b90143c458b8d988808" + integrity sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA== dependencies: - figgy-pudding "^3.5.1" - minipass "^3.0.0" + minipass "^3.1.1" stack-utils@^1.0.1: version "1.0.2" @@ -6628,6 +6601,18 @@ tar@^4: safe-buffer "^5.1.2" yallist "^3.0.3" +tar@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.2.tgz#5df17813468a6264ff14f766886c622b84ae2f39" + integrity sha512-Glo3jkRtPcvpDlAs/0+hozav78yoXKFr+c4wgw62NNMO3oo4AaJdCo21Uu7lcwr55h39W2XD1LMERc64wtbItg== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.0" + mkdirp "^1.0.3" + yallist "^4.0.0" + temp-dir@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" @@ -6652,25 +6637,25 @@ terminal-link@^2.0.0: ansi-escapes "^4.2.1" supports-hyperlinks "^2.0.0" -terser-webpack-plugin@^2.3.6: - version "2.3.6" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-2.3.6.tgz#a4014b311a61f87c6a1b217ef4f5a75bd0665a69" - integrity sha512-I8IDsQwZrqjdmOicNeE8L/MhwatAap3mUrtcAKJuilsemUNcX+Hier/eAzwStVqhlCxq0aG3ni9bK/0BESXkTg== +terser-webpack-plugin@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-3.0.2.tgz#fdc501c73847d8904f6a80c5009b11ee2d11b8eb" + integrity sha512-QeBHLJzKJHCnrPNlZj5EmOF6wwvzpVGDHvTrySIH8+jZEXfcKKCiriRmF6945rKzuZDnkOEU/LDv7qtPiiyP/Q== dependencies: - cacache "^13.0.1" + cacache "^15.0.3" find-cache-dir "^3.3.1" - jest-worker "^25.4.0" + jest-worker "^26.0.0" p-limit "^2.3.0" schema-utils "^2.6.6" serialize-javascript "^3.0.0" source-map "^0.6.1" - terser "^4.6.12" + terser "^4.6.13" webpack-sources "^1.4.3" -terser@^4.6.12: - version "4.6.12" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.12.tgz#44b98aef8703fdb09a3491bf79b43faffc5b4fee" - integrity sha512-fnIwuaKjFPANG6MAixC/k1TDtnl1YlPLUlLVIxxGZUn1gfUx2+l3/zGNB72wya+lgsb50QBi2tUV75RiODwnww== +terser@^4.6.13: + version "4.7.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.7.0.tgz#15852cf1a08e3256a80428e865a2fa893ffba006" + integrity sha512-Lfb0RiZcjRDXCC3OSHJpEkxJ9Qeqs6mp2v4jf2MHfy8vGERmVDuvjXdd/EnP5Deme5F2yBRBymKmKHCBg2echw== dependencies: commander "^2.20.0" source-map "~0.6.1"