first commit

This commit is contained in:
monjack
2025-06-20 18:01:48 +08:00
commit 6daa6d65c1
24611 changed files with 2512443 additions and 0 deletions

View File

@ -0,0 +1,33 @@
/*
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 ConsumeSharedFallbackDependency extends ModuleDependency {
/**
* @param {string} request the request
*/
constructor(request) {
super(request);
}
get type() {
return "consume shared fallback";
}
get category() {
return "esm";
}
}
makeSerializable(
ConsumeSharedFallbackDependency,
"webpack/lib/sharing/ConsumeSharedFallbackDependency"
);
module.exports = ConsumeSharedFallbackDependency;

View File

@ -0,0 +1,269 @@
/*
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 { CONSUME_SHARED_TYPES } = require("../ModuleSourceTypesConstants");
const {
WEBPACK_MODULE_TYPE_CONSUME_SHARED_MODULE
} = require("../ModuleTypeConstants");
const RuntimeGlobals = require("../RuntimeGlobals");
const makeSerializable = require("../util/makeSerializable");
const { rangeToString, stringifyHoley } = require("../util/semver");
const ConsumeSharedFallbackDependency = require("./ConsumeSharedFallbackDependency");
/** @typedef {import("../../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions */
/** @typedef {import("../ChunkGraph")} ChunkGraph */
/** @typedef {import("../ChunkGroup")} ChunkGroup */
/** @typedef {import("../Compilation")} Compilation */
/** @typedef {import("../Dependency").UpdateHashContext} UpdateHashContext */
/** @typedef {import("../Module").BuildCallback} BuildCallback */
/** @typedef {import("../Module").CodeGenerationContext} CodeGenerationContext */
/** @typedef {import("../Module").CodeGenerationResult} CodeGenerationResult */
/** @typedef {import("../Module").LibIdentOptions} LibIdentOptions */
/** @typedef {import("../Module").NeedBuildCallback} NeedBuildCallback */
/** @typedef {import("../Module").NeedBuildContext} NeedBuildContext */
/** @typedef {import("../Module").SourceTypes} SourceTypes */
/** @typedef {import("../RequestShortener")} RequestShortener */
/** @typedef {import("../ResolverFactory").ResolverWithOptions} ResolverWithOptions */
/** @typedef {import("../WebpackError")} WebpackError */
/** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */
/** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext} ObjectSerializerContext */
/** @typedef {import("../util/Hash")} Hash */
/** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */
/** @typedef {import("../util/semver").SemVerRange} SemVerRange */
/**
* @typedef {object} ConsumeOptions
* @property {string=} import fallback request
* @property {string=} importResolved resolved fallback request
* @property {string} shareKey global share key
* @property {string} shareScope share scope
* @property {SemVerRange | false | undefined} requiredVersion version requirement
* @property {string=} packageName package name to determine required version automatically
* @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
*/
class ConsumeSharedModule extends Module {
/**
* @param {string} context context
* @param {ConsumeOptions} options consume options
*/
constructor(context, options) {
super(WEBPACK_MODULE_TYPE_CONSUME_SHARED_MODULE, context);
this.options = options;
}
/**
* @returns {string} a unique identifier of the module
*/
identifier() {
const {
shareKey,
shareScope,
importResolved,
requiredVersion,
strictVersion,
singleton,
eager
} = this.options;
return `${WEBPACK_MODULE_TYPE_CONSUME_SHARED_MODULE}|${shareScope}|${shareKey}|${
requiredVersion && rangeToString(requiredVersion)
}|${strictVersion}|${importResolved}|${singleton}|${eager}`;
}
/**
* @param {RequestShortener} requestShortener the request shortener
* @returns {string} a user readable identifier of the module
*/
readableIdentifier(requestShortener) {
const {
shareKey,
shareScope,
importResolved,
requiredVersion,
strictVersion,
singleton,
eager
} = this.options;
return `consume shared module (${shareScope}) ${shareKey}@${
requiredVersion ? rangeToString(requiredVersion) : "*"
}${strictVersion ? " (strict)" : ""}${singleton ? " (singleton)" : ""}${
importResolved
? ` (fallback: ${requestShortener.shorten(importResolved)})`
: ""
}${eager ? " (eager)" : ""}`;
}
/**
* @param {LibIdentOptions} options options
* @returns {string | null} an identifier for library inclusion
*/
libIdent(options) {
const { shareKey, shareScope, import: request } = this.options;
return `${
this.layer ? `(${this.layer})/` : ""
}webpack/sharing/consume/${shareScope}/${shareKey}${
request ? `/${request}` : ""
}`;
}
/**
* @param {NeedBuildContext} context context info
* @param {NeedBuildCallback} 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 {BuildCallback} callback callback function
* @returns {void}
*/
build(options, compilation, resolver, fs, callback) {
this.buildMeta = {};
this.buildInfo = {};
if (this.options.import) {
const dep = new ConsumeSharedFallbackDependency(this.options.import);
if (this.options.eager) {
this.addDependency(dep);
} else {
const block = new AsyncDependenciesBlock({});
block.addDependency(dep);
this.addBlock(block);
}
}
callback();
}
/**
* @returns {SourceTypes} types available (do not mutate)
*/
getSourceTypes() {
return CONSUME_SHARED_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 {UpdateHashContext} context context
* @returns {void}
*/
updateHash(hash, context) {
hash.update(JSON.stringify(this.options));
super.updateHash(hash, context);
}
/**
* @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
});
}
}
const args = [
JSON.stringify(shareScope),
JSON.stringify(shareKey),
JSON.stringify(eager)
];
if (requiredVersion) {
args.push(stringifyHoley(requiredVersion));
}
if (fallbackCode) {
args.push(fallbackCode);
}
let fn;
if (requiredVersion) {
if (strictVersion) {
fn = singleton ? "loadStrictSingletonVersion" : "loadStrictVersion";
} else {
fn = singleton ? "loadSingletonVersion" : "loadVersion";
}
} else {
fn = singleton ? "loadSingleton" : "load";
}
const code = runtimeTemplate.returningFunction(`${fn}(${args.join(", ")})`);
const sources = new Map();
sources.set("consume-shared", new RawSource(code));
return {
runtimeRequirements,
sources
};
}
/**
* @param {ObjectSerializerContext} context context
*/
serialize(context) {
const { write } = context;
write(this.options);
super.serialize(context);
}
/**
* @param {ObjectDeserializerContext} context context
*/
deserialize(context) {
const { read } = context;
this.options = read();
super.deserialize(context);
}
}
makeSerializable(
ConsumeSharedModule,
"webpack/lib/sharing/ConsumeSharedModule"
);
module.exports = ConsumeSharedModule;

View File

@ -0,0 +1,379 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const ModuleNotFoundError = require("../ModuleNotFoundError");
const RuntimeGlobals = require("../RuntimeGlobals");
const WebpackError = require("../WebpackError");
const { parseOptions } = require("../container/options");
const LazySet = require("../util/LazySet");
const createSchemaValidation = require("../util/create-schema-validation");
const { parseRange } = require("../util/semver");
const ConsumeSharedFallbackDependency = require("./ConsumeSharedFallbackDependency");
const ConsumeSharedModule = require("./ConsumeSharedModule");
const ConsumeSharedRuntimeModule = require("./ConsumeSharedRuntimeModule");
const ProvideForSharedDependency = require("./ProvideForSharedDependency");
const { resolveMatchedConfigs } = require("./resolveMatchedConfigs");
const {
isRequiredVersion,
getDescriptionFile,
getRequiredVersionFromDescriptionFile
} = require("./utils");
/** @typedef {import("../../declarations/plugins/sharing/ConsumeSharedPlugin").ConsumeSharedPluginOptions} ConsumeSharedPluginOptions */
/** @typedef {import("../../declarations/plugins/sharing/ConsumeSharedPlugin").ConsumesConfig} ConsumesConfig */
/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../ResolverFactory").ResolveOptionsWithDependencyType} ResolveOptionsWithDependencyType */
/** @typedef {import("../util/semver").SemVerRange} SemVerRange */
/** @typedef {import("./ConsumeSharedModule").ConsumeOptions} ConsumeOptions */
/** @typedef {import("./utils").DescriptionFile} DescriptionFile */
const validate = createSchemaValidation(
require("../../schemas/plugins/sharing/ConsumeSharedPlugin.check.js"),
() => require("../../schemas/plugins/sharing/ConsumeSharedPlugin.json"),
{
name: "Consume Shared Plugin",
baseDataPath: "options"
}
);
/** @type {ResolveOptionsWithDependencyType} */
const RESOLVE_OPTIONS = { dependencyType: "esm" };
const PLUGIN_NAME = "ConsumeSharedPlugin";
class ConsumeSharedPlugin {
/**
* @param {ConsumeSharedPluginOptions} options options
*/
constructor(options) {
if (typeof options !== "string") {
validate(options);
}
/** @type {[string, ConsumeOptions][]} */
this._consumes = parseOptions(
options.consumes,
(item, key) => {
if (Array.isArray(item)) throw new Error("Unexpected array in options");
/** @type {ConsumeOptions} */
const result =
item === key || !isRequiredVersion(item)
? // item is a request/key
{
import: key,
shareScope: options.shareScope || "default",
shareKey: key,
requiredVersion: undefined,
packageName: 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: parseRange(item),
strictVersion: true,
packageName: undefined,
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"
? parseRange(item.requiredVersion)
: item.requiredVersion,
strictVersion:
typeof item.strictVersion === "boolean"
? item.strictVersion
: item.import !== false && !item.singleton,
packageName: item.packageName,
singleton: Boolean(item.singleton),
eager: Boolean(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(
ConsumeSharedFallbackDependency,
normalModuleFactory
);
/** @type {Map<string, ConsumeOptions>} */
let unresolvedConsumes;
/** @type {Map<string, ConsumeOptions>} */
let resolvedConsumes;
/** @type {Map<string, ConsumeOptions>} */
let prefixedConsumes;
const promise = resolveMatchedConfigs(compilation, this._consumes).then(
({ resolved, unresolved, prefixed }) => {
resolvedConsumes = resolved;
unresolvedConsumes = unresolved;
prefixedConsumes = prefixed;
}
);
const resolver = compilation.resolverFactory.get(
"normal",
RESOLVE_OPTIONS
);
/**
* @param {string} context issuer directory
* @param {string} request request
* @param {ConsumeOptions} config options
* @returns {Promise<ConsumeSharedModule>} create module
*/
const createConsumeSharedModule = (context, request, config) => {
/**
* @param {string} details details
*/
const requiredVersionWarning = details => {
const error = new WebpackError(
`No required version specified and unable to automatically determine one. ${details}`
);
error.file = `shared module ${request}`;
compilation.warnings.push(error);
};
const directFallback =
config.import &&
/^(\.\.?(\/|$)|\/|[A-Za-z]:|\\\\)/.test(config.import);
return Promise.all([
new Promise(
/**
* @param {(value?: string) => void} resolve resolve
*/
resolve => {
if (!config.import) {
resolve();
return;
}
const resolveContext = {
/** @type {LazySet<string>} */
fileDependencies: new LazySet(),
/** @type {LazySet<string>} */
contextDependencies: new LazySet(),
/** @type {LazySet<string>} */
missingDependencies: new LazySet()
};
resolver.resolve(
{},
directFallback ? compiler.context : context,
config.import,
resolveContext,
(err, result) => {
compilation.contextDependencies.addAll(
resolveContext.contextDependencies
);
compilation.fileDependencies.addAll(
resolveContext.fileDependencies
);
compilation.missingDependencies.addAll(
resolveContext.missingDependencies
);
if (err) {
compilation.errors.push(
new ModuleNotFoundError(null, err, {
name: `resolving fallback for shared module ${request}`
})
);
return resolve();
}
resolve(/** @type {string} */ (result));
}
);
}
),
new Promise(
/**
* @param {(value?: SemVerRange) => void} resolve resolve
*/
resolve => {
if (config.requiredVersion !== undefined) {
resolve(/** @type {SemVerRange} */ (config.requiredVersion));
return;
}
let packageName = config.packageName;
if (packageName === undefined) {
if (/^(\/|[A-Za-z]:|\\\\)/.test(request)) {
// For relative or absolute requests we don't automatically use a packageName.
// If wished one can specify one with the packageName option.
resolve();
return;
}
const match = /^((?:@[^\\/]+[\\/])?[^\\/]+)/.exec(request);
if (!match) {
requiredVersionWarning(
"Unable to extract the package name from request."
);
resolve();
return;
}
packageName = match[0];
}
getDescriptionFile(
compilation.inputFileSystem,
context,
["package.json"],
(err, result, checkedDescriptionFilePaths) => {
if (err) {
requiredVersionWarning(
`Unable to read description file: ${err}`
);
return resolve();
}
const { data } =
/** @type {DescriptionFile} */
(result || {});
if (!data) {
if (checkedDescriptionFilePaths) {
requiredVersionWarning(
[
`Unable to find required version for "${packageName}" in description file/s`,
checkedDescriptionFilePaths.join("\n"),
"It need to be in dependencies, devDependencies or peerDependencies."
].join("\n")
);
} else {
requiredVersionWarning(
`Unable to find description file in ${context}.`
);
}
return resolve();
}
if (data.name === packageName) {
// Package self-referencing
return resolve();
}
const requiredVersion =
getRequiredVersionFromDescriptionFile(data, packageName);
if (requiredVersion) {
return resolve(parseRange(requiredVersion));
}
resolve();
},
result => {
if (!result) return false;
const maybeRequiredVersion =
getRequiredVersionFromDescriptionFile(
result.data,
packageName
);
return (
result.data.name === packageName ||
typeof maybeRequiredVersion === "string"
);
}
);
}
)
]).then(
([importResolved, requiredVersion]) =>
new ConsumeSharedModule(
directFallback ? compiler.context : context,
{
...config,
importResolved,
import: importResolved ? config.import : undefined,
requiredVersion
}
)
);
};
normalModuleFactory.hooks.factorize.tapPromise(
PLUGIN_NAME,
({ context, request, dependencies }) =>
// wait for resolving to be complete
promise.then(() => {
if (
dependencies[0] instanceof ConsumeSharedFallbackDependency ||
dependencies[0] instanceof ProvideForSharedDependency
) {
return;
}
const match = unresolvedConsumes.get(request);
if (match !== undefined) {
return createConsumeSharedModule(context, request, match);
}
for (const [prefix, options] of prefixedConsumes) {
if (request.startsWith(prefix)) {
const remainder = request.slice(prefix.length);
return createConsumeSharedModule(context, request, {
...options,
import: options.import
? options.import + remainder
: undefined,
shareKey: options.shareKey + remainder
});
}
}
})
);
normalModuleFactory.hooks.createModule.tapPromise(
PLUGIN_NAME,
({ resource }, { context, dependencies }) => {
if (
dependencies[0] instanceof ConsumeSharedFallbackDependency ||
dependencies[0] instanceof ProvideForSharedDependency
) {
return Promise.resolve();
}
const options = resolvedConsumes.get(
/** @type {string} */ (resource)
);
if (options !== undefined) {
return createConsumeSharedModule(
context,
/** @type {string} */ (resource),
options
);
}
return Promise.resolve();
}
);
compilation.hooks.additionalTreeRuntimeRequirements.tap(
PLUGIN_NAME,
(chunk, set) => {
set.add(RuntimeGlobals.module);
set.add(RuntimeGlobals.moduleCache);
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;

View File

@ -0,0 +1,355 @@
/*
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");
const {
parseVersionRuntimeCode,
versionLtRuntimeCode,
rangeToStringRuntimeCode,
satisfyRuntimeCode
} = require("../util/semver");
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../Chunk")} Chunk */
/** @typedef {import("../Chunk").ChunkId} ChunkId */
/** @typedef {import("../ChunkGraph")} ChunkGraph */
/** @typedef {import("../ChunkGraph").ModuleId} ModuleId */
/** @typedef {import("../Compilation")} Compilation */
/** @typedef {import("../Module")} Module */
/** @typedef {import("../Module").ReadOnlyRuntimeRequirements} ReadOnlyRuntimeRequirements */
/** @typedef {import("./ConsumeSharedModule")} ConsumeSharedModule */
class ConsumeSharedRuntimeModule extends RuntimeModule {
/**
* @param {ReadOnlyRuntimeRequirements} runtimeRequirements runtime requirements
*/
constructor(runtimeRequirements) {
super("consumes", RuntimeModule.STAGE_ATTACH);
this._runtimeRequirements = runtimeRequirements;
}
/**
* @returns {string | null} runtime code
*/
generate() {
const compilation = /** @type {Compilation} */ (this.compilation);
const chunkGraph = /** @type {ChunkGraph} */ (this.chunkGraph);
const { runtimeTemplate, codeGenerationResults } = compilation;
/** @type {Record<ChunkId, (string | number)[]>} */
const chunkToModuleMapping = {};
/** @type {Map<string | number, Source>} */
const moduleIdToSourceMapping = new Map();
/** @type {(string | number)[]} */
const initialConsumes = [];
/**
* @param {Iterable<Module>} modules modules
* @param {Chunk} chunk the chunk
* @param {(string | number)[]} list list of ids
*/
const addModules = (modules, chunk, list) => {
for (const m of modules) {
const module = m;
const id = /** @type {ModuleId} */ (chunkGraph.getModuleId(module));
list.push(id);
moduleIdToSourceMapping.set(
id,
codeGenerationResults.getSource(
module,
chunk.runtime,
"consume-shared"
)
);
}
};
for (const chunk of /** @type {Chunk} */ (
this.chunk
).getAllReferencedChunks()) {
const modules = chunkGraph.getChunkModulesIterableBySourceType(
chunk,
"consume-shared"
);
if (!modules) continue;
addModules(
modules,
chunk,
(chunkToModuleMapping[/** @type {ChunkId} */ (chunk.id)] = [])
);
}
for (const chunk of /** @type {Chunk} */ (
this.chunk
).getAllInitialChunks()) {
const modules = chunkGraph.getChunkModulesIterableBySourceType(
chunk,
"consume-shared"
);
if (!modules) continue;
addModules(modules, chunk, initialConsumes);
}
if (moduleIdToSourceMapping.size === 0) return null;
return Template.asString([
parseVersionRuntimeCode(runtimeTemplate),
versionLtRuntimeCode(runtimeTemplate),
rangeToStringRuntimeCode(runtimeTemplate),
satisfyRuntimeCode(runtimeTemplate),
`var exists = ${runtimeTemplate.basicFunction("scope, key", [
`return scope && ${RuntimeGlobals.hasOwnProperty}(scope, key);`
])}`,
`var get = ${runtimeTemplate.basicFunction("entry", [
"entry.loaded = 1;",
"return entry.get()"
])};`,
`var eagerOnly = ${runtimeTemplate.basicFunction("versions", [
`return Object.keys(versions).reduce(${runtimeTemplate.basicFunction(
"filtered, version",
Template.indent([
"if (versions[version].eager) {",
Template.indent(["filtered[version] = versions[version];"]),
"}",
"return filtered;"
])
)}, {});`
])};`,
`var findLatestVersion = ${runtimeTemplate.basicFunction(
"scope, key, eager",
[
"var versions = eager ? eagerOnly(scope[key]) : scope[key];",
`var key = Object.keys(versions).reduce(${runtimeTemplate.basicFunction(
"a, b",
["return !a || versionLt(a, b) ? b : a;"]
)}, 0);`,
"return key && versions[key];"
]
)};`,
`var findSatisfyingVersion = ${runtimeTemplate.basicFunction(
"scope, key, requiredVersion, eager",
[
"var versions = eager ? eagerOnly(scope[key]) : scope[key];",
`var key = Object.keys(versions).reduce(${runtimeTemplate.basicFunction(
"a, b",
[
"if (!satisfy(requiredVersion, b)) return a;",
"return !a || versionLt(a, b) ? b : a;"
]
)}, 0);`,
"return key && versions[key]"
]
)};`,
`var findSingletonVersionKey = ${runtimeTemplate.basicFunction(
"scope, key, eager",
[
"var versions = eager ? eagerOnly(scope[key]) : scope[key];",
`return Object.keys(versions).reduce(${runtimeTemplate.basicFunction(
"a, b",
["return !a || (!versions[a].loaded && versionLt(a, b)) ? b : a;"]
)}, 0);`
]
)};`,
`var getInvalidSingletonVersionMessage = ${runtimeTemplate.basicFunction(
"scope, key, version, requiredVersion",
[
'return "Unsatisfied version " + version + " from " + (version && scope[key][version].from) + " of shared singleton module " + key + " (required " + rangeToString(requiredVersion) + ")"'
]
)};`,
`var getInvalidVersionMessage = ${runtimeTemplate.basicFunction(
"scope, scopeName, key, requiredVersion, eager",
[
"var versions = scope[key];",
'return "No satisfying version (" + rangeToString(requiredVersion) + ")" + (eager ? " for eager consumption" : "") + " of shared module " + key + " found in shared scope " + scopeName + ".\\n" +',
`\t"Available versions: " + Object.keys(versions).map(${runtimeTemplate.basicFunction(
"key",
['return key + " from " + versions[key].from;']
)}).join(", ");`
]
)};`,
`var fail = ${runtimeTemplate.basicFunction("msg", [
"throw new Error(msg);"
])}`,
`var failAsNotExist = ${runtimeTemplate.basicFunction("scopeName, key", [
'return fail("Shared module " + key + " doesn\'t exist in shared scope " + scopeName);'
])}`,
`var warn = /*#__PURE__*/ ${
compilation.outputOptions.ignoreBrowserWarnings
? runtimeTemplate.basicFunction("", "")
: runtimeTemplate.basicFunction("msg", [
'if (typeof console !== "undefined" && console.warn) console.warn(msg);'
])
};`,
`var init = ${runtimeTemplate.returningFunction(
Template.asString([
"function(scopeName, key, eager, c, d) {",
Template.indent([
`var promise = ${RuntimeGlobals.initializeSharing}(scopeName);`,
// if we require eager shared, we expect it to be already loaded before it requested, no need to wait the whole scope loaded.
"if (promise && promise.then && !eager) { ",
Template.indent([
`return promise.then(fn.bind(fn, scopeName, ${RuntimeGlobals.shareScopeMap}[scopeName], key, false, c, d));`
]),
"}",
`return fn(scopeName, ${RuntimeGlobals.shareScopeMap}[scopeName], key, eager, c, d);`
]),
"}"
]),
"fn"
)};`,
"",
`var useFallback = ${runtimeTemplate.basicFunction(
"scopeName, key, fallback",
["return fallback ? fallback() : failAsNotExist(scopeName, key);"]
)}`,
`var load = /*#__PURE__*/ init(${runtimeTemplate.basicFunction(
"scopeName, scope, key, eager, fallback",
[
"if (!exists(scope, key)) return useFallback(scopeName, key, fallback);",
"return get(findLatestVersion(scope, key, eager));"
]
)});`,
`var loadVersion = /*#__PURE__*/ init(${runtimeTemplate.basicFunction(
"scopeName, scope, key, eager, requiredVersion, fallback",
[
"if (!exists(scope, key)) return useFallback(scopeName, key, fallback);",
"var satisfyingVersion = findSatisfyingVersion(scope, key, requiredVersion, eager);",
"if (satisfyingVersion) return get(satisfyingVersion);",
"warn(getInvalidVersionMessage(scope, scopeName, key, requiredVersion, eager))",
"return get(findLatestVersion(scope, key, eager));"
]
)});`,
`var loadStrictVersion = /*#__PURE__*/ init(${runtimeTemplate.basicFunction(
"scopeName, scope, key, eager, requiredVersion, fallback",
[
"if (!exists(scope, key)) return useFallback(scopeName, key, fallback);",
"var satisfyingVersion = findSatisfyingVersion(scope, key, requiredVersion, eager);",
"if (satisfyingVersion) return get(satisfyingVersion);",
"if (fallback) return fallback();",
"fail(getInvalidVersionMessage(scope, scopeName, key, requiredVersion, eager));"
]
)});`,
`var loadSingleton = /*#__PURE__*/ init(${runtimeTemplate.basicFunction(
"scopeName, scope, key, eager, fallback",
[
"if (!exists(scope, key)) return useFallback(scopeName, key, fallback);",
"var version = findSingletonVersionKey(scope, key, eager);",
"return get(scope[key][version]);"
]
)});`,
`var loadSingletonVersion = /*#__PURE__*/ init(${runtimeTemplate.basicFunction(
"scopeName, scope, key, eager, requiredVersion, fallback",
[
"if (!exists(scope, key)) return useFallback(scopeName, key, fallback);",
"var version = findSingletonVersionKey(scope, key, eager);",
"if (!satisfy(requiredVersion, version)) {",
Template.indent([
"warn(getInvalidSingletonVersionMessage(scope, key, version, requiredVersion));"
]),
"}",
"return get(scope[key][version]);"
]
)});`,
`var loadStrictSingletonVersion = /*#__PURE__*/ init(${runtimeTemplate.basicFunction(
"scopeName, scope, key, eager, requiredVersion, fallback",
[
"if (!exists(scope, key)) return useFallback(scopeName, key, fallback);",
"var version = findSingletonVersionKey(scope, key, eager);",
"if (!satisfy(requiredVersion, version)) {",
Template.indent([
"fail(getInvalidSingletonVersionMessage(scope, key, version, requiredVersion));"
]),
"}",
"return get(scope[key][version]);"
]
)});`,
"var installedModules = {};",
"var moduleToHandlerMapping = {",
Template.indent(
Array.from(
moduleIdToSourceMapping,
([key, source]) => `${JSON.stringify(key)}: ${source.source()}`
).join(",\n")
),
"};",
initialConsumes.length > 0
? Template.asString([
`var initialConsumes = ${JSON.stringify(initialConsumes)};`,
`initialConsumes.forEach(${runtimeTemplate.basicFunction("id", [
`${
RuntimeGlobals.moduleFactories
}[id] = ${runtimeTemplate.basicFunction("module", [
"// Handle case when module is used sync",
"installedModules[id] = 0;",
`delete ${RuntimeGlobals.moduleCache}[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"
)};`,
"var startedInstallModules = {};",
`${
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]);`,
"if(!startedInstallModules[id]) {",
`var onFactory = ${runtimeTemplate.basicFunction(
"factory",
[
"installedModules[id] = 0;",
`${
RuntimeGlobals.moduleFactories
}[id] = ${runtimeTemplate.basicFunction("module", [
`delete ${RuntimeGlobals.moduleCache}[id];`,
"module.exports = factory();"
])}`
]
)};`,
"startedInstallModules[id] = true;",
`var onError = ${runtimeTemplate.basicFunction("error", [
"delete installedModules[id];",
`${
RuntimeGlobals.moduleFactories
}[id] = ${runtimeTemplate.basicFunction("module", [
`delete ${RuntimeGlobals.moduleCache}[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;

View File

@ -0,0 +1,33 @@
/*
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 ProvideForSharedDependency extends ModuleDependency {
/**
* @param {string} request request string
*/
constructor(request) {
super(request);
}
get type() {
return "provide module for shared";
}
get category() {
return "esm";
}
}
makeSerializable(
ProvideForSharedDependency,
"webpack/lib/sharing/ProvideForSharedDependency"
);
module.exports = ProvideForSharedDependency;

View File

@ -0,0 +1,80 @@
/*
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");
/** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */
/** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext} ObjectSerializerContext */
class ProvideSharedDependency extends Dependency {
/**
* @param {string} shareScope share scope
* @param {string} name module name
* @param {string | false} version version
* @param {string} request request
* @param {boolean} eager true, if this is an eager 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 shared 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)" : ""}`;
}
/**
* @param {ObjectSerializerContext} context context
*/
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);
}
/**
* @param {ObjectDeserializerContext} context context
* @returns {ProvideSharedDependency} deserialize fallback dependency
*/
static deserialize(context) {
const { read } = context;
const obj = new ProvideSharedDependency(
read(),
read(),
read(),
read(),
read()
);
this.shareScope = context.read();
obj.deserialize(context);
return obj;
}
}
makeSerializable(
ProvideSharedDependency,
"webpack/lib/sharing/ProvideSharedDependency"
);
module.exports = ProvideSharedDependency;

View File

@ -0,0 +1,196 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra and Zackary Jackson @ScriptedAlchemy
*/
"use strict";
const AsyncDependenciesBlock = require("../AsyncDependenciesBlock");
const Module = require("../Module");
const { SHARED_INIT_TYPES } = require("../ModuleSourceTypesConstants");
const { WEBPACK_MODULE_TYPE_PROVIDE } = require("../ModuleTypeConstants");
const RuntimeGlobals = require("../RuntimeGlobals");
const makeSerializable = require("../util/makeSerializable");
const ProvideForSharedDependency = require("./ProvideForSharedDependency");
/** @typedef {import("../../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions */
/** @typedef {import("../Chunk")} Chunk */
/** @typedef {import("../ChunkGraph")} ChunkGraph */
/** @typedef {import("../ChunkGroup")} ChunkGroup */
/** @typedef {import("../Compilation")} Compilation */
/** @typedef {import("../Module").BuildCallback} BuildCallback */
/** @typedef {import("../Module").CodeGenerationContext} CodeGenerationContext */
/** @typedef {import("../Module").CodeGenerationResult} CodeGenerationResult */
/** @typedef {import("../Module").LibIdentOptions} LibIdentOptions */
/** @typedef {import("../Module").NeedBuildCallback} NeedBuildCallback */
/** @typedef {import("../Module").NeedBuildContext} NeedBuildContext */
/** @typedef {import("../Module").SourceTypes} SourceTypes */
/** @typedef {import("../RequestShortener")} RequestShortener */
/** @typedef {import("../ResolverFactory").ResolverWithOptions} ResolverWithOptions */
/** @typedef {import("../WebpackError")} WebpackError */
/** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */
/** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext} ObjectSerializerContext */
/** @typedef {import("../util/Hash")} Hash */
/** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */
class ProvideSharedModule extends Module {
/**
* @param {string} shareScope shared scope name
* @param {string} name shared key
* @param {string | false} version version
* @param {string} request request to the provided module
* @param {boolean} eager include the module in sync way
*/
constructor(shareScope, name, version, request, eager) {
super(WEBPACK_MODULE_TYPE_PROVIDE);
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 `provide module (${this._shareScope}) ${this._name}@${this._version} = ${this._request}`;
}
/**
* @param {RequestShortener} requestShortener the request shortener
* @returns {string} a user readable identifier of the module
*/
readableIdentifier(requestShortener) {
return `provide shared module (${this._shareScope}) ${this._name}@${
this._version
} = ${requestShortener.shorten(this._request)}`;
}
/**
* @param {LibIdentOptions} options options
* @returns {string | null} an identifier for library inclusion
*/
libIdent(options) {
return `${this.layer ? `(${this.layer})/` : ""}webpack/sharing/provide/${
this._shareScope
}/${this._name}`;
}
/**
* @param {NeedBuildContext} context context info
* @param {NeedBuildCallback} 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 {BuildCallback} callback callback function
* @returns {void}
*/
build(options, compilation, resolver, fs, callback) {
this.buildMeta = {};
this.buildInfo = {
strict: true
};
this.clearDependenciesAndBlocks();
const dep = new ProvideForSharedDependency(this._request);
if (this._eager) {
this.addDependency(dep);
} else {
const block = new AsyncDependenciesBlock({});
block.addDependency(dep);
this.addBlock(block);
}
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 42;
}
/**
* @returns {SourceTypes} types available (do not mutate)
*/
getSourceTypes() {
return SHARED_INIT_TYPES;
}
/**
* @param {CodeGenerationContext} context context for code generation
* @returns {CodeGenerationResult} result
*/
codeGeneration({ runtimeTemplate, moduleGraph, chunkGraph }) {
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
})
}${this._eager ? ", 1" : ""});`;
const sources = new Map();
const data = new Map();
data.set("share-init", [
{
shareScope: this._shareScope,
initStage: 10,
init: code
}
]);
return { sources, data, runtimeRequirements };
}
/**
* @param {ObjectSerializerContext} context context
*/
serialize(context) {
const { write } = context;
write(this._shareScope);
write(this._name);
write(this._version);
write(this._request);
write(this._eager);
super.serialize(context);
}
/**
* @param {ObjectDeserializerContext} context context
* @returns {ProvideSharedModule} deserialize fallback dependency
*/
static deserialize(context) {
const { read } = context;
const obj = new ProvideSharedModule(read(), read(), read(), read(), read());
obj.deserialize(context);
return obj;
}
}
makeSerializable(
ProvideSharedModule,
"webpack/lib/sharing/ProvideSharedModule"
);
module.exports = ProvideSharedModule;

View File

@ -0,0 +1,37 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra and Zackary Jackson @ScriptedAlchemy
*/
"use strict";
const ModuleFactory = require("../ModuleFactory");
const ProvideSharedModule = require("./ProvideSharedModule");
/** @typedef {import("../ModuleFactory").ModuleFactoryCallback} ModuleFactoryCallback */
/** @typedef {import("../ModuleFactory").ModuleFactoryCreateData} ModuleFactoryCreateData */
/** @typedef {import("./ProvideSharedDependency")} ProvideSharedDependency */
class ProvideSharedModuleFactory extends ModuleFactory {
/**
* @param {ModuleFactoryCreateData} data data object
* @param {ModuleFactoryCallback} callback callback
* @returns {void}
*/
create(data, callback) {
const dep =
/** @type {ProvideSharedDependency} */
(data.dependencies[0]);
callback(null, {
module: new ProvideSharedModule(
dep.shareScope,
dep.name,
dep.version,
dep.request,
dep.eager
)
});
}
}
module.exports = ProvideSharedModuleFactory;

View File

@ -0,0 +1,248 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra and Zackary Jackson @ScriptedAlchemy
*/
"use strict";
const WebpackError = require("../WebpackError");
const { parseOptions } = require("../container/options");
const createSchemaValidation = require("../util/create-schema-validation");
const ProvideForSharedDependency = require("./ProvideForSharedDependency");
const ProvideSharedDependency = require("./ProvideSharedDependency");
const ProvideSharedModuleFactory = require("./ProvideSharedModuleFactory");
/** @typedef {import("../../declarations/plugins/sharing/ProvideSharedPlugin").ProvideSharedPluginOptions} ProvideSharedPluginOptions */
/** @typedef {import("../Compilation")} Compilation */
/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../NormalModuleFactory").NormalModuleCreateData} NormalModuleCreateData */
const validate = createSchemaValidation(
require("../../schemas/plugins/sharing/ProvideSharedPlugin.check.js"),
() => require("../../schemas/plugins/sharing/ProvideSharedPlugin.json"),
{
name: "Provide Shared Plugin",
baseDataPath: "options"
}
);
/**
* @typedef {object} ProvideOptions
* @property {string} shareKey
* @property {string} shareScope
* @property {string | undefined | false} version
* @property {boolean} eager
*/
/** @typedef {Map<string, { config: ProvideOptions, version: string | undefined | false }>} ResolvedProvideMap */
const PLUGIN_NAME = "ProvideSharedPlugin";
class ProvideSharedPlugin {
/**
* @param {ProvideSharedPluginOptions} options options
*/
constructor(options) {
validate(options);
this._provides = /** @type {[string, ProvideOptions][]} */ (
parseOptions(
options.provides,
item => {
if (Array.isArray(item))
throw new Error("Unexpected array of provides");
/** @type {ProvideOptions} */
const result = {
shareKey: item,
version: undefined,
shareScope: options.shareScope || "default",
eager: false
};
return result;
},
item => ({
shareKey: item.shareKey,
version: item.version,
shareScope: item.shareScope || options.shareScope || "default",
eager: Boolean(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) {
/** @type {WeakMap<Compilation, ResolvedProvideMap>} */
const compilationData = new WeakMap();
compiler.hooks.compilation.tap(
PLUGIN_NAME,
(compilation, { normalModuleFactory }) => {
/** @type {ResolvedProvideMap} */
const resolvedProvideMap = new Map();
/** @type {Map<string, ProvideOptions>} */
const matchProvides = new Map();
/** @type {Map<string, ProvideOptions>} */
const prefixMatchProvides = new Map();
for (const [request, config] of this._provides) {
if (/^(\/|[A-Za-z]:\\|\\\\|\.\.?(\/|$))/.test(request)) {
// relative request
resolvedProvideMap.set(request, {
config,
version: config.version
});
} else if (/^(\/|[A-Za-z]:\\|\\\\)/.test(request)) {
// absolute path
resolvedProvideMap.set(request, {
config,
version: config.version
});
} else if (request.endsWith("/")) {
// module request prefix
prefixMatchProvides.set(request, config);
} else {
// module request
matchProvides.set(request, config);
}
}
compilationData.set(compilation, resolvedProvideMap);
/**
* @param {string} key key
* @param {ProvideOptions} config config
* @param {NormalModuleCreateData["resource"]} resource resource
* @param {NormalModuleCreateData["resourceResolveData"]} resourceResolveData resource resolve data
*/
const provideSharedModule = (
key,
config,
resource,
resourceResolveData
) => {
let version = config.version;
if (version === undefined) {
let details = "";
if (!resourceResolveData) {
details = "No resolve data provided from resolver.";
} else {
const descriptionFileData =
resourceResolveData.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 ${resourceResolveData.descriptionFilePath}, or manually specify version in shared config.`;
} else {
version = /** @type {string | false | undefined} */ (
descriptionFileData.version
);
}
}
if (!version) {
const error = new WebpackError(
`No version specified and unable to automatically determine one. ${details}`
);
error.file = `shared module ${key} -> ${resource}`;
compilation.warnings.push(error);
}
}
resolvedProvideMap.set(resource, {
config,
version
});
};
normalModuleFactory.hooks.module.tap(
PLUGIN_NAME,
(module, { resource, resourceResolveData }, resolveData) => {
if (resolvedProvideMap.has(/** @type {string} */ (resource))) {
return module;
}
const { request } = resolveData;
{
const config = matchProvides.get(request);
if (config !== undefined) {
provideSharedModule(
request,
config,
/** @type {string} */ (resource),
resourceResolveData
);
resolveData.cacheable = false;
}
}
for (const [prefix, config] of prefixMatchProvides) {
if (request.startsWith(prefix)) {
const remainder = request.slice(prefix.length);
provideSharedModule(
/** @type {string} */ (resource),
{
...config,
shareKey: config.shareKey + remainder
},
/** @type {string} */ (resource),
resourceResolveData
);
resolveData.cacheable = false;
}
}
return module;
}
);
}
);
compiler.hooks.finishMake.tapPromise(PLUGIN_NAME, compilation => {
const resolvedProvideMap = compilationData.get(compilation);
if (!resolvedProvideMap) return Promise.resolve();
return Promise.all(
Array.from(
resolvedProvideMap,
([resource, { config, version }]) =>
new Promise((resolve, reject) => {
compilation.addInclude(
compiler.context,
new ProvideSharedDependency(
config.shareScope,
config.shareKey,
version || false,
resource,
config.eager
),
{
name: undefined
},
err => {
if (err) return reject(err);
resolve(null);
}
);
})
)
).then(() => {});
});
compiler.hooks.compilation.tap(
PLUGIN_NAME,
(compilation, { normalModuleFactory }) => {
compilation.dependencyFactories.set(
ProvideForSharedDependency,
normalModuleFactory
);
compilation.dependencyFactories.set(
ProvideSharedDependency,
new ProvideSharedModuleFactory()
);
}
);
}
}
module.exports = ProvideSharedPlugin;

View File

@ -0,0 +1,92 @@
/*
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<string, ConsumesConfig>[]} */
const consumes = sharedOptions.map(([key, options]) => ({
[key]: {
import: options.import,
shareKey: options.shareKey || key,
shareScope: options.shareScope,
requiredVersion: options.requiredVersion,
strictVersion: options.strictVersion,
singleton: options.singleton,
packageName: options.packageName,
eager: options.eager
}
}));
/** @type {Record<string, ProvidesConfig>[]} */
const provides = sharedOptions
.filter(([, options]) => options.import !== false)
.map(([key, options]) => ({
[options.import || key]: {
shareKey: options.shareKey || key,
shareScope: options.shareScope,
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;

View File

@ -0,0 +1,151 @@
/*
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");
const {
compareModulesByIdentifier,
compareStrings
} = require("../util/comparators");
/** @typedef {import("../Chunk")} Chunk */
/** @typedef {import("../ChunkGraph")} ChunkGraph */
/** @typedef {import("../Compilation")} Compilation */
class ShareRuntimeModule extends RuntimeModule {
constructor() {
super("sharing");
}
/**
* @returns {string | null} runtime code
*/
generate() {
const compilation = /** @type {Compilation} */ (this.compilation);
const {
runtimeTemplate,
codeGenerationResults,
outputOptions: { uniqueName, ignoreBrowserWarnings }
} = compilation;
const chunkGraph = /** @type {ChunkGraph} */ (this.chunkGraph);
/** @type {Map<string, Map<number, Set<string>>>} */
const initCodePerScope = new Map();
for (const chunk of /** @type {Chunk} */ (
this.chunk
).getAllReferencedChunks()) {
const modules = chunkGraph.getOrderedChunkModulesIterableBySourceType(
chunk,
"share-init",
compareModulesByIdentifier
);
if (!modules) continue;
for (const m of modules) {
const data = codeGenerationResults.getData(
m,
chunk.runtime,
"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 = {};",
"var initTokens = {};",
`${RuntimeGlobals.initializeSharing} = ${runtimeTemplate.basicFunction(
"name, initScope",
[
"if(!initScope) initScope = [];",
"// handling circular init calls",
"var initToken = initTokens[name];",
"if(!initToken) initToken = initTokens[name] = {};",
"if(initScope.indexOf(initToken) >= 0) return;",
"initScope.push(initToken);",
"// only runs once",
"if(initPromises[name]) return initPromises[name];",
"// 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 = ${
ignoreBrowserWarnings
? runtimeTemplate.basicFunction("", "")
: runtimeTemplate.basicFunction("msg", [
'if (typeof console !== "undefined" && console.warn) console.warn(msg);'
])
};`,
`var uniqueName = ${JSON.stringify(uniqueName || undefined)};`,
`var register = ${runtimeTemplate.basicFunction(
"name, version, factory, eager",
[
"var versions = scope[name] = scope[name] || {};",
"var activeVersion = versions[version];",
"if(!activeVersion || (!activeVersion.loaded && (!eager != !activeVersion.eager ? eager : uniqueName > activeVersion.from))) versions[version] = { get: factory, from: uniqueName, eager: !!eager };"
]
)};`,
`var initExternal = ${runtimeTemplate.basicFunction("id", [
`var handleError = ${runtimeTemplate.expressionFunction(
'warn("Initialization of sharing external failed: " + err)',
"err"
)};`,
"try {",
Template.indent([
`var module = ${RuntimeGlobals.require}(id);`,
"if(!module) return;",
`var initFn = ${runtimeTemplate.returningFunction(
`module && module.init && module.init(${RuntimeGlobals.shareScopeMap}[name], initScope)`,
"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)
.sort(([a], [b]) => compareStrings(a, b))
.map(([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;"
])
),
"}",
"if(!promises.length) return initPromises[name] = 1;",
`return initPromises[name] = Promise.all(promises).then(${runtimeTemplate.returningFunction(
"initPromises[name] = 1"
)});`
]
)};`
]);
}
}
module.exports = ShareRuntimeModule;

View File

@ -0,0 +1,92 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const ModuleNotFoundError = require("../ModuleNotFoundError");
const LazySet = require("../util/LazySet");
/** @typedef {import("../Compilation")} Compilation */
/** @typedef {import("../ResolverFactory").ResolveOptionsWithDependencyType} ResolveOptionsWithDependencyType */
/**
* @template T
* @typedef {object} MatchedConfigs
* @property {Map<string, T>} resolved
* @property {Map<string, T>} unresolved
* @property {Map<string, T>} prefixed
*/
/** @type {ResolveOptionsWithDependencyType} */
const RESOLVE_OPTIONS = { dependencyType: "esm" };
/**
* @template T
* @param {Compilation} compilation the compilation
* @param {[string, T][]} configs to be processed configs
* @returns {Promise<MatchedConfigs<T>>} resolved matchers
*/
module.exports.resolveMatchedConfigs = (compilation, configs) => {
/** @type {Map<string, T>} */
const resolved = new Map();
/** @type {Map<string, T>} */
const unresolved = new Map();
/** @type {Map<string, T>} */
const prefixed = new Map();
const resolveContext = {
/** @type {LazySet<string>} */
fileDependencies: new LazySet(),
/** @type {LazySet<string>} */
contextDependencies: new LazySet(),
/** @type {LazySet<string>} */
missingDependencies: new LazySet()
};
const resolver = compilation.resolverFactory.get("normal", RESOLVE_OPTIONS);
const context = compilation.compiler.context;
return Promise.all(
// eslint-disable-next-line array-callback-return
configs.map(([request, config]) => {
if (/^\.\.?(\/|$)/.test(request)) {
// relative request
return new Promise(resolve => {
resolver.resolve(
{},
context,
request,
resolveContext,
(err, result) => {
if (err || result === false) {
err = err || new Error(`Can't resolve ${request}`);
compilation.errors.push(
new ModuleNotFoundError(null, err, {
name: `shared module ${request}`
})
);
return resolve(null);
}
resolved.set(/** @type {string} */ (result), config);
resolve(null);
}
);
});
} else if (/^(\/|[A-Za-z]:\\|\\\\)/.test(request)) {
// absolute path
resolved.set(request, config);
} else if (request.endsWith("/")) {
// module request prefix
prefixed.set(request, config);
} else {
// module request
unresolved.set(request, config);
}
})
).then(() => {
compilation.contextDependencies.addAll(resolveContext.contextDependencies);
compilation.fileDependencies.addAll(resolveContext.fileDependencies);
compilation.missingDependencies.addAll(resolveContext.missingDependencies);
return { resolved, unresolved, prefixed };
});
};

425
app_vue/node_modules/webpack/lib/sharing/utils.js generated vendored Normal file
View File

@ -0,0 +1,425 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const { join, dirname, readJson } = require("../util/fs");
/** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */
/** @typedef {import("../util/fs").JsonObject} JsonObject */
/** @typedef {import("../util/fs").JsonPrimitive} JsonPrimitive */
// Extreme shorthand only for github. eg: foo/bar
const RE_URL_GITHUB_EXTREME_SHORT = /^[^/@:.\s][^/@:\s]*\/[^@:\s]*[^/@:\s]#\S+/;
// Short url with specific protocol. eg: github:foo/bar
const RE_GIT_URL_SHORT = /^(github|gitlab|bitbucket|gist):\/?[^/.]+\/?/i;
// Currently supported protocols
const RE_PROTOCOL =
/^((git\+)?(ssh|https?|file)|git|github|gitlab|bitbucket|gist):$/i;
// Has custom protocol
const RE_CUSTOM_PROTOCOL = /^((git\+)?(ssh|https?|file)|git):\/\//i;
// Valid hash format for npm / yarn ...
const RE_URL_HASH_VERSION = /#(?:semver:)?(.+)/;
// Simple hostname validate
const RE_HOSTNAME = /^(?:[^/.]+(\.[^/]+)+|localhost)$/;
// For hostname with colon. eg: ssh://user@github.com:foo/bar
const RE_HOSTNAME_WITH_COLON =
/([^/@#:.]+(?:\.[^/@#:.]+)+|localhost):([^#/0-9]+)/;
// Reg for url without protocol
const RE_NO_PROTOCOL = /^([^/@#:.]+(?:\.[^/@#:.]+)+)/;
// RegExp for version string
const VERSION_PATTERN_REGEXP = /^([\d^=v<>~]|[*xX]$)/;
// Specific protocol for short url without normal hostname
const PROTOCOLS_FOR_SHORT = [
"github:",
"gitlab:",
"bitbucket:",
"gist:",
"file:"
];
// Default protocol for git url
const DEF_GIT_PROTOCOL = "git+ssh://";
// thanks to https://github.com/npm/hosted-git-info/blob/latest/git-host-info.js
const extractCommithashByDomain = {
/**
* @param {string} pathname pathname
* @param {string} hash hash
* @returns {string | undefined} hash
*/
"github.com": (pathname, hash) => {
let [, user, project, type, commithash] = pathname.split("/", 5);
if (type && type !== "tree") {
return;
}
commithash = !type ? hash : `#${commithash}`;
if (project && project.endsWith(".git")) {
project = project.slice(0, -4);
}
if (!user || !project) {
return;
}
return commithash;
},
/**
* @param {string} pathname pathname
* @param {string} hash hash
* @returns {string | undefined} hash
*/
"gitlab.com": (pathname, hash) => {
const path = pathname.slice(1);
if (path.includes("/-/") || path.includes("/archive.tar.gz")) {
return;
}
const segments = path.split("/");
let project = /** @type {string} */ (segments.pop());
if (project.endsWith(".git")) {
project = project.slice(0, -4);
}
const user = segments.join("/");
if (!user || !project) {
return;
}
return hash;
},
/**
* @param {string} pathname pathname
* @param {string} hash hash
* @returns {string | undefined} hash
*/
"bitbucket.org": (pathname, hash) => {
let [, user, project, aux] = pathname.split("/", 4);
if (["get"].includes(aux)) {
return;
}
if (project && project.endsWith(".git")) {
project = project.slice(0, -4);
}
if (!user || !project) {
return;
}
return hash;
},
/**
* @param {string} pathname pathname
* @param {string} hash hash
* @returns {string | undefined} hash
*/
"gist.github.com": (pathname, hash) => {
let [, user, project, aux] = pathname.split("/", 4);
if (aux === "raw") {
return;
}
if (!project) {
if (!user) {
return;
}
project = user;
}
if (project.endsWith(".git")) {
project = project.slice(0, -4);
}
return hash;
}
};
/**
* extract commit hash from parsed url
* @inner
* @param {URL} urlParsed parsed url
* @returns {string} commithash
*/
function getCommithash(urlParsed) {
let { hostname, pathname, hash } = urlParsed;
hostname = hostname.replace(/^www\./, "");
try {
hash = decodeURIComponent(hash);
// eslint-disable-next-line no-empty
} catch (_err) {}
if (
extractCommithashByDomain[
/** @type {keyof extractCommithashByDomain} */ (hostname)
]
) {
return (
extractCommithashByDomain[
/** @type {keyof extractCommithashByDomain} */ (hostname)
](pathname, hash) || ""
);
}
return hash;
}
/**
* make url right for URL parse
* @inner
* @param {string} gitUrl git url
* @returns {string} fixed url
*/
function correctUrl(gitUrl) {
// like:
// proto://hostname.com:user/repo -> proto://hostname.com/user/repo
return gitUrl.replace(RE_HOSTNAME_WITH_COLON, "$1/$2");
}
/**
* make url protocol right for URL parse
* @inner
* @param {string} gitUrl git url
* @returns {string} fixed url
*/
function correctProtocol(gitUrl) {
// eg: github:foo/bar#v1.0. Should not add double slash, in case of error parsed `pathname`
if (RE_GIT_URL_SHORT.test(gitUrl)) {
return gitUrl;
}
// eg: user@github.com:foo/bar
if (!RE_CUSTOM_PROTOCOL.test(gitUrl)) {
return `${DEF_GIT_PROTOCOL}${gitUrl}`;
}
return gitUrl;
}
/**
* extract git dep version from hash
* @inner
* @param {string} hash hash
* @returns {string} git dep version
*/
function getVersionFromHash(hash) {
const matched = hash.match(RE_URL_HASH_VERSION);
return (matched && matched[1]) || "";
}
/**
* if string can be decoded
* @inner
* @param {string} str str to be checked
* @returns {boolean} if can be decoded
*/
function canBeDecoded(str) {
try {
decodeURIComponent(str);
} catch (_err) {
return false;
}
return true;
}
/**
* get right dep version from git url
* @inner
* @param {string} gitUrl git url
* @returns {string} dep version
*/
function getGitUrlVersion(gitUrl) {
const oriGitUrl = gitUrl;
// github extreme shorthand
gitUrl = RE_URL_GITHUB_EXTREME_SHORT.test(gitUrl)
? `github:${gitUrl}`
: correctProtocol(gitUrl);
gitUrl = correctUrl(gitUrl);
let parsed;
try {
parsed = new URL(gitUrl);
// eslint-disable-next-line no-empty
} catch (_err) {}
if (!parsed) {
return "";
}
const { protocol, hostname, pathname, username, password } = parsed;
if (!RE_PROTOCOL.test(protocol)) {
return "";
}
// pathname shouldn't be empty or URL malformed
if (!pathname || !canBeDecoded(pathname)) {
return "";
}
// without protocol, there should have auth info
if (RE_NO_PROTOCOL.test(oriGitUrl) && !username && !password) {
return "";
}
if (!PROTOCOLS_FOR_SHORT.includes(protocol.toLowerCase())) {
if (!RE_HOSTNAME.test(hostname)) {
return "";
}
const commithash = getCommithash(parsed);
return getVersionFromHash(commithash) || commithash;
}
// for protocol short
return getVersionFromHash(gitUrl);
}
/**
* @param {string} str maybe required version
* @returns {boolean} true, if it looks like a version
*/
function isRequiredVersion(str) {
return VERSION_PATTERN_REGEXP.test(str);
}
module.exports.isRequiredVersion = isRequiredVersion;
/**
* @see https://docs.npmjs.com/cli/v7/configuring-npm/package-json#urls-as-dependencies
* @param {string} versionDesc version to be normalized
* @returns {string} normalized version
*/
function normalizeVersion(versionDesc) {
versionDesc = (versionDesc && versionDesc.trim()) || "";
if (isRequiredVersion(versionDesc)) {
return versionDesc;
}
// add handle for URL Dependencies
return getGitUrlVersion(versionDesc.toLowerCase());
}
module.exports.normalizeVersion = normalizeVersion;
/** @typedef {{ data: JsonObject, path: string }} DescriptionFile */
/**
* @param {InputFileSystem} fs file system
* @param {string} directory directory to start looking into
* @param {string[]} descriptionFiles possible description filenames
* @param {(err?: Error | null, descriptionFile?: DescriptionFile, paths?: string[]) => void} callback callback
* @param {(descriptionFile?: DescriptionFile) => boolean} satisfiesDescriptionFileData file data compliance check
* @param {Set<string>} checkedFilePaths set of file paths that have been checked
*/
const getDescriptionFile = (
fs,
directory,
descriptionFiles,
callback,
satisfiesDescriptionFileData,
checkedFilePaths = new Set()
) => {
let i = 0;
const satisfiesDescriptionFileDataInternal = {
check: satisfiesDescriptionFileData,
checkedFilePaths
};
const tryLoadCurrent = () => {
if (i >= descriptionFiles.length) {
const parentDirectory = dirname(fs, directory);
if (!parentDirectory || parentDirectory === directory) {
return callback(
null,
undefined,
Array.from(satisfiesDescriptionFileDataInternal.checkedFilePaths)
);
}
return getDescriptionFile(
fs,
parentDirectory,
descriptionFiles,
callback,
satisfiesDescriptionFileDataInternal.check,
satisfiesDescriptionFileDataInternal.checkedFilePaths
);
}
const filePath = join(fs, directory, descriptionFiles[i]);
readJson(fs, filePath, (err, data) => {
if (err) {
if ("code" in err && err.code === "ENOENT") {
i++;
return tryLoadCurrent();
}
return callback(err);
}
if (!data || typeof data !== "object" || Array.isArray(data)) {
return callback(
new Error(`Description file ${filePath} is not an object`)
);
}
if (
typeof satisfiesDescriptionFileDataInternal.check === "function" &&
!satisfiesDescriptionFileDataInternal.check({ data, path: filePath })
) {
i++;
satisfiesDescriptionFileDataInternal.checkedFilePaths.add(filePath);
return tryLoadCurrent();
}
callback(null, { data, path: filePath });
});
};
tryLoadCurrent();
};
module.exports.getDescriptionFile = getDescriptionFile;
/**
* @param {JsonObject} data description file data i.e.: package.json
* @param {string} packageName name of the dependency
* @returns {string | undefined} normalized version
*/
const getRequiredVersionFromDescriptionFile = (data, packageName) => {
const dependencyTypes = [
"optionalDependencies",
"dependencies",
"peerDependencies",
"devDependencies"
];
for (const dependencyType of dependencyTypes) {
const dependency = /** @type {JsonObject} */ (data[dependencyType]);
if (
dependency &&
typeof dependency === "object" &&
packageName in dependency
) {
return normalizeVersion(
/** @type {Exclude<JsonPrimitive, null | boolean| number>} */ (
dependency[packageName]
)
);
}
}
};
module.exports.getRequiredVersionFromDescriptionFile =
getRequiredVersionFromDescriptionFile;