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,305 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const RuntimeGlobals = require("../RuntimeGlobals");
const JavascriptModulesPlugin = require("../javascript/JavascriptModulesPlugin");
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../../declarations/WebpackOptions").LibraryOptions} LibraryOptions */
/** @typedef {import("../../declarations/WebpackOptions").LibraryType} LibraryType */
/** @typedef {import("../Chunk")} Chunk */
/** @typedef {import("../ChunkGraph")} ChunkGraph */
/** @typedef {import("../Compilation")} Compilation */
/** @typedef {import("../Compilation").ChunkHashContext} ChunkHashContext */
/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../Module")} Module */
/** @typedef {import("../javascript/JavascriptModulesPlugin").RenderContext} RenderContext */
/** @typedef {import("../javascript/JavascriptModulesPlugin").StartupRenderContext} StartupRenderContext */
/** @typedef {import("../util/Hash")} Hash */
const COMMON_LIBRARY_NAME_MESSAGE =
"Common configuration options that specific library names are 'output.library[.name]', 'entry.xyz.library[.name]', 'ModuleFederationPlugin.name' and 'ModuleFederationPlugin.library[.name]'.";
/**
* @template T
* @typedef {object} LibraryContext
* @property {Compilation} compilation
* @property {ChunkGraph} chunkGraph
* @property {T} options
*/
/**
* @typedef {object} AbstractLibraryPluginOptions
* @property {string} pluginName name of the plugin
* @property {LibraryType} type used library type
*/
/**
* @template T
*/
class AbstractLibraryPlugin {
/**
* @param {AbstractLibraryPluginOptions} options options
*/
constructor({ pluginName, type }) {
this._pluginName = pluginName;
this._type = type;
this._parseCache = new WeakMap();
}
/**
* Apply the plugin
* @param {Compiler} compiler the compiler instance
* @returns {void}
*/
apply(compiler) {
const { _pluginName } = this;
compiler.hooks.thisCompilation.tap(_pluginName, compilation => {
compilation.hooks.finishModules.tap(
{ name: _pluginName, stage: 10 },
() => {
for (const [
name,
{
dependencies: deps,
options: { library }
}
] of compilation.entries) {
const options = this._parseOptionsCached(
library !== undefined
? library
: compilation.outputOptions.library
);
if (options !== false) {
const dep = deps[deps.length - 1];
if (dep) {
const module = compilation.moduleGraph.getModule(dep);
if (module) {
this.finishEntryModule(module, name, {
options,
compilation,
chunkGraph: compilation.chunkGraph
});
}
}
}
}
}
);
/**
* @param {Chunk} chunk chunk
* @returns {T | false} options for the chunk
*/
const getOptionsForChunk = chunk => {
if (compilation.chunkGraph.getNumberOfEntryModules(chunk) === 0)
return false;
const options = chunk.getEntryOptions();
const library = options && options.library;
return this._parseOptionsCached(
library !== undefined ? library : compilation.outputOptions.library
);
};
if (
this.render !== AbstractLibraryPlugin.prototype.render ||
this.runtimeRequirements !==
AbstractLibraryPlugin.prototype.runtimeRequirements
) {
compilation.hooks.additionalChunkRuntimeRequirements.tap(
_pluginName,
(chunk, set, { chunkGraph }) => {
const options = getOptionsForChunk(chunk);
if (options !== false) {
this.runtimeRequirements(chunk, set, {
options,
compilation,
chunkGraph
});
}
}
);
}
const hooks = JavascriptModulesPlugin.getCompilationHooks(compilation);
if (this.render !== AbstractLibraryPlugin.prototype.render) {
hooks.render.tap(_pluginName, (source, renderContext) => {
const options = getOptionsForChunk(renderContext.chunk);
if (options === false) return source;
return this.render(source, renderContext, {
options,
compilation,
chunkGraph: compilation.chunkGraph
});
});
}
if (
this.embedInRuntimeBailout !==
AbstractLibraryPlugin.prototype.embedInRuntimeBailout
) {
hooks.embedInRuntimeBailout.tap(
_pluginName,
(module, renderContext) => {
const options = getOptionsForChunk(renderContext.chunk);
if (options === false) return;
return this.embedInRuntimeBailout(module, renderContext, {
options,
compilation,
chunkGraph: compilation.chunkGraph
});
}
);
}
if (
this.strictRuntimeBailout !==
AbstractLibraryPlugin.prototype.strictRuntimeBailout
) {
hooks.strictRuntimeBailout.tap(_pluginName, renderContext => {
const options = getOptionsForChunk(renderContext.chunk);
if (options === false) return;
return this.strictRuntimeBailout(renderContext, {
options,
compilation,
chunkGraph: compilation.chunkGraph
});
});
}
if (
this.renderStartup !== AbstractLibraryPlugin.prototype.renderStartup
) {
hooks.renderStartup.tap(
_pluginName,
(source, module, renderContext) => {
const options = getOptionsForChunk(renderContext.chunk);
if (options === false) return source;
return this.renderStartup(source, module, renderContext, {
options,
compilation,
chunkGraph: compilation.chunkGraph
});
}
);
}
hooks.chunkHash.tap(_pluginName, (chunk, hash, context) => {
const options = getOptionsForChunk(chunk);
if (options === false) return;
this.chunkHash(chunk, hash, context, {
options,
compilation,
chunkGraph: compilation.chunkGraph
});
});
});
}
/**
* @param {LibraryOptions=} library normalized library option
* @returns {T | false} preprocess as needed by overriding
*/
_parseOptionsCached(library) {
if (!library) return false;
if (library.type !== this._type) return false;
const cacheEntry = this._parseCache.get(library);
if (cacheEntry !== undefined) return cacheEntry;
const result = this.parseOptions(library);
this._parseCache.set(library, result);
return result;
}
/* istanbul ignore next */
/**
* @abstract
* @param {LibraryOptions} library normalized library option
* @returns {T | false} preprocess as needed by overriding
*/
parseOptions(library) {
const AbstractMethodError = require("../AbstractMethodError");
throw new AbstractMethodError();
}
/**
* @param {Module} module the exporting entry module
* @param {string} entryName the name of the entrypoint
* @param {LibraryContext<T>} libraryContext context
* @returns {void}
*/
finishEntryModule(module, entryName, libraryContext) {}
/**
* @param {Module} module the exporting entry module
* @param {RenderContext} renderContext render context
* @param {LibraryContext<T>} libraryContext context
* @returns {string | undefined} bailout reason
*/
embedInRuntimeBailout(module, renderContext, libraryContext) {
return undefined;
}
/**
* @param {RenderContext} renderContext render context
* @param {LibraryContext<T>} libraryContext context
* @returns {string | undefined} bailout reason
*/
strictRuntimeBailout(renderContext, libraryContext) {
return undefined;
}
/**
* @param {Chunk} chunk the chunk
* @param {Set<string>} set runtime requirements
* @param {LibraryContext<T>} libraryContext context
* @returns {void}
*/
runtimeRequirements(chunk, set, libraryContext) {
if (this.render !== AbstractLibraryPlugin.prototype.render)
set.add(RuntimeGlobals.returnExportsFromRuntime);
}
/**
* @param {Source} source source
* @param {RenderContext} renderContext render context
* @param {LibraryContext<T>} libraryContext context
* @returns {Source} source with library export
*/
render(source, renderContext, libraryContext) {
return source;
}
/**
* @param {Source} source source
* @param {Module} module module
* @param {StartupRenderContext} renderContext render context
* @param {LibraryContext<T>} libraryContext context
* @returns {Source} source with library export
*/
renderStartup(source, module, renderContext, libraryContext) {
return source;
}
/**
* @param {Chunk} chunk the chunk
* @param {Hash} hash hash
* @param {ChunkHashContext} chunkHashContext chunk hash context
* @param {LibraryContext<T>} libraryContext context
* @returns {void}
*/
chunkHash(chunk, hash, chunkHashContext, libraryContext) {
const options = this._parseOptionsCached(
libraryContext.compilation.outputOptions.library
);
hash.update(this._pluginName);
hash.update(JSON.stringify(options));
}
}
AbstractLibraryPlugin.COMMON_LIBRARY_NAME_MESSAGE = COMMON_LIBRARY_NAME_MESSAGE;
module.exports = AbstractLibraryPlugin;

View File

@ -0,0 +1,178 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const { ConcatSource } = require("webpack-sources");
const ExternalModule = require("../ExternalModule");
const Template = require("../Template");
const AbstractLibraryPlugin = require("./AbstractLibraryPlugin");
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../../declarations/WebpackOptions").LibraryOptions} LibraryOptions */
/** @typedef {import("../../declarations/WebpackOptions").LibraryType} LibraryType */
/** @typedef {import("../Chunk")} Chunk */
/** @typedef {import("../Compilation").ChunkHashContext} ChunkHashContext */
/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../javascript/JavascriptModulesPlugin").RenderContext} RenderContext */
/** @typedef {import("../util/Hash")} Hash */
/** @template T @typedef {import("./AbstractLibraryPlugin").LibraryContext<T>} LibraryContext<T> */
/**
* @typedef {object} AmdLibraryPluginOptions
* @property {LibraryType} type
* @property {boolean=} requireAsWrapper
*/
/**
* @typedef {object} AmdLibraryPluginParsed
* @property {string} name
* @property {string} amdContainer
*/
/**
* @typedef {AmdLibraryPluginParsed} T
* @extends {AbstractLibraryPlugin<AmdLibraryPluginParsed>}
*/
class AmdLibraryPlugin extends AbstractLibraryPlugin {
/**
* @param {AmdLibraryPluginOptions} options the plugin options
*/
constructor(options) {
super({
pluginName: "AmdLibraryPlugin",
type: options.type
});
this.requireAsWrapper = options.requireAsWrapper;
}
/**
* @param {LibraryOptions} library normalized library option
* @returns {T | false} preprocess as needed by overriding
*/
parseOptions(library) {
const { name, amdContainer } = library;
if (this.requireAsWrapper) {
if (name) {
throw new Error(
`AMD library name must be unset. ${AbstractLibraryPlugin.COMMON_LIBRARY_NAME_MESSAGE}`
);
}
} else if (name && typeof name !== "string") {
throw new Error(
`AMD library name must be a simple string or unset. ${AbstractLibraryPlugin.COMMON_LIBRARY_NAME_MESSAGE}`
);
}
const _name = /** @type {string} */ (name);
const _amdContainer = /** @type {string} */ (amdContainer);
return { name: _name, amdContainer: _amdContainer };
}
/**
* @param {Source} source source
* @param {RenderContext} renderContext render context
* @param {LibraryContext<T>} libraryContext context
* @returns {Source} source with library export
*/
render(
source,
{ chunkGraph, chunk, runtimeTemplate },
{ options, compilation }
) {
const modern = runtimeTemplate.supportsArrowFunction();
const modules = chunkGraph
.getChunkModules(chunk)
.filter(
m =>
m instanceof ExternalModule &&
(m.externalType === "amd" || m.externalType === "amd-require")
);
const externals = /** @type {ExternalModule[]} */ (modules);
const externalsDepsArray = JSON.stringify(
externals.map(m =>
typeof m.request === "object" && !Array.isArray(m.request)
? m.request.amd
: m.request
)
);
const externalsArguments = externals
.map(
m =>
`__WEBPACK_EXTERNAL_MODULE_${Template.toIdentifier(
`${chunkGraph.getModuleId(m)}`
)}__`
)
.join(", ");
const iife = runtimeTemplate.isIIFE();
const fnStart =
(modern
? `(${externalsArguments}) => {`
: `function(${externalsArguments}) {`) +
(iife || !chunk.hasRuntime() ? " return " : "\n");
const fnEnd = iife ? ";\n}" : "\n}";
let amdContainerPrefix = "";
if (options.amdContainer) {
amdContainerPrefix = `${options.amdContainer}.`;
}
if (this.requireAsWrapper) {
return new ConcatSource(
`${amdContainerPrefix}require(${externalsDepsArray}, ${fnStart}`,
source,
`${fnEnd});`
);
} else if (options.name) {
const name = compilation.getPath(options.name, {
chunk
});
return new ConcatSource(
`${amdContainerPrefix}define(${JSON.stringify(
name
)}, ${externalsDepsArray}, ${fnStart}`,
source,
`${fnEnd});`
);
} else if (externalsArguments) {
return new ConcatSource(
`${amdContainerPrefix}define(${externalsDepsArray}, ${fnStart}`,
source,
`${fnEnd});`
);
}
return new ConcatSource(
`${amdContainerPrefix}define(${fnStart}`,
source,
`${fnEnd});`
);
}
/**
* @param {Chunk} chunk the chunk
* @param {Hash} hash hash
* @param {ChunkHashContext} chunkHashContext chunk hash context
* @param {LibraryContext<T>} libraryContext context
* @returns {void}
*/
chunkHash(chunk, hash, chunkHashContext, { options, compilation }) {
hash.update("AmdLibraryPlugin");
if (this.requireAsWrapper) {
hash.update("requireAsWrapper");
} else if (options.name) {
hash.update("named");
const name = compilation.getPath(options.name, {
chunk
});
hash.update(name);
} else if (options.amdContainer) {
hash.update("amdContainer");
hash.update(options.amdContainer);
}
}
}
module.exports = AmdLibraryPlugin;

View File

@ -0,0 +1,415 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const { ConcatSource } = require("webpack-sources");
const { UsageState } = require("../ExportsInfo");
const RuntimeGlobals = require("../RuntimeGlobals");
const Template = require("../Template");
const propertyAccess = require("../util/propertyAccess");
const { getEntryRuntime } = require("../util/runtime");
const AbstractLibraryPlugin = require("./AbstractLibraryPlugin");
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../../declarations/WebpackOptions").LibraryOptions} LibraryOptions */
/** @typedef {import("../../declarations/WebpackOptions").LibraryType} LibraryType */
/** @typedef {import("../Chunk")} Chunk */
/** @typedef {import("../Compilation")} Compilation */
/** @typedef {import("../Compilation").ChunkHashContext} ChunkHashContext */
/** @typedef {import("../Module")} Module */
/** @typedef {import("../javascript/JavascriptModulesPlugin").RenderContext} RenderContext */
/** @typedef {import("../javascript/JavascriptModulesPlugin").StartupRenderContext} StartupRenderContext */
/** @typedef {import("../util/Hash")} Hash */
/** @template T @typedef {import("./AbstractLibraryPlugin").LibraryContext<T>} LibraryContext<T> */
const KEYWORD_REGEX =
/^(await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|function|if|implements|import|in|instanceof|interface|let|new|null|package|private|protected|public|return|super|switch|static|this|throw|try|true|typeof|var|void|while|with|yield)$/;
const IDENTIFIER_REGEX =
/^[\p{L}\p{Nl}$_][\p{L}\p{Nl}$\p{Mn}\p{Mc}\p{Nd}\p{Pc}]*$/iu;
/**
* Validates the library name by checking for keywords and valid characters
* @param {string} name name to be validated
* @returns {boolean} true, when valid
*/
const isNameValid = name =>
!KEYWORD_REGEX.test(name) && IDENTIFIER_REGEX.test(name);
/**
* @param {string[]} accessor variable plus properties
* @param {number} existingLength items of accessor that are existing already
* @param {boolean=} initLast if the last property should also be initialized to an object
* @returns {string} code to access the accessor while initializing
*/
const accessWithInit = (accessor, existingLength, initLast = false) => {
// This generates for [a, b, c, d]:
// (((a = typeof a === "undefined" ? {} : a).b = a.b || {}).c = a.b.c || {}).d
const base = accessor[0];
if (accessor.length === 1 && !initLast) return base;
let current =
existingLength > 0
? base
: `(${base} = typeof ${base} === "undefined" ? {} : ${base})`;
// i is the current position in accessor that has been printed
let i = 1;
// all properties printed so far (excluding base)
/** @type {string[] | undefined} */
let propsSoFar;
// if there is existingLength, print all properties until this position as property access
if (existingLength > i) {
propsSoFar = accessor.slice(1, existingLength);
i = existingLength;
current += propertyAccess(propsSoFar);
} else {
propsSoFar = [];
}
// all remaining properties (except the last one when initLast is not set)
// should be printed as initializer
const initUntil = initLast ? accessor.length : accessor.length - 1;
for (; i < initUntil; i++) {
const prop = accessor[i];
propsSoFar.push(prop);
current = `(${current}${propertyAccess([prop])} = ${base}${propertyAccess(
propsSoFar
)} || {})`;
}
// print the last property as property access if not yet printed
if (i < accessor.length)
current = `${current}${propertyAccess([accessor[accessor.length - 1]])}`;
return current;
};
/**
* @typedef {object} AssignLibraryPluginOptions
* @property {LibraryType} type
* @property {string[] | "global"} prefix name prefix
* @property {string | false} declare declare name as variable
* @property {"error"|"static"|"copy"|"assign"} unnamed behavior for unnamed library name
* @property {"copy"|"assign"=} named behavior for named library name
*/
/**
* @typedef {object} AssignLibraryPluginParsed
* @property {string | string[]} name
* @property {string | string[] | undefined} export
*/
/**
* @typedef {AssignLibraryPluginParsed} T
* @extends {AbstractLibraryPlugin<AssignLibraryPluginParsed>}
*/
class AssignLibraryPlugin extends AbstractLibraryPlugin {
/**
* @param {AssignLibraryPluginOptions} options the plugin options
*/
constructor(options) {
super({
pluginName: "AssignLibraryPlugin",
type: options.type
});
this.prefix = options.prefix;
this.declare = options.declare;
this.unnamed = options.unnamed;
this.named = options.named || "assign";
}
/**
* @param {LibraryOptions} library normalized library option
* @returns {T | false} preprocess as needed by overriding
*/
parseOptions(library) {
const { name } = library;
if (this.unnamed === "error") {
if (typeof name !== "string" && !Array.isArray(name)) {
throw new Error(
`Library name must be a string or string array. ${AbstractLibraryPlugin.COMMON_LIBRARY_NAME_MESSAGE}`
);
}
} else if (name && typeof name !== "string" && !Array.isArray(name)) {
throw new Error(
`Library name must be a string, string array or unset. ${AbstractLibraryPlugin.COMMON_LIBRARY_NAME_MESSAGE}`
);
}
const _name = /** @type {string | string[]} */ (name);
return {
name: _name,
export: library.export
};
}
/**
* @param {Module} module the exporting entry module
* @param {string} entryName the name of the entrypoint
* @param {LibraryContext<T>} libraryContext context
* @returns {void}
*/
finishEntryModule(
module,
entryName,
{ options, compilation, compilation: { moduleGraph } }
) {
const runtime = getEntryRuntime(compilation, entryName);
if (options.export) {
const exportsInfo = moduleGraph.getExportInfo(
module,
Array.isArray(options.export) ? options.export[0] : options.export
);
exportsInfo.setUsed(UsageState.Used, runtime);
exportsInfo.canMangleUse = false;
} else {
const exportsInfo = moduleGraph.getExportsInfo(module);
exportsInfo.setUsedInUnknownWay(runtime);
}
moduleGraph.addExtraReason(module, "used as library export");
}
/**
* @param {Compilation} compilation the compilation
* @returns {string[]} the prefix
*/
_getPrefix(compilation) {
return this.prefix === "global"
? [compilation.runtimeTemplate.globalObject]
: this.prefix;
}
/**
* @param {AssignLibraryPluginParsed} options the library options
* @param {Chunk} chunk the chunk
* @param {Compilation} compilation the compilation
* @returns {Array<string>} the resolved full name
*/
_getResolvedFullName(options, chunk, compilation) {
const prefix = this._getPrefix(compilation);
const fullName = options.name ? prefix.concat(options.name) : prefix;
return fullName.map(n =>
compilation.getPath(n, {
chunk
})
);
}
/**
* @param {Source} source source
* @param {RenderContext} renderContext render context
* @param {LibraryContext<T>} libraryContext context
* @returns {Source} source with library export
*/
render(source, { chunk }, { options, compilation }) {
const fullNameResolved = this._getResolvedFullName(
options,
chunk,
compilation
);
if (this.declare) {
const base = fullNameResolved[0];
if (!isNameValid(base)) {
throw new Error(
`Library name base (${base}) must be a valid identifier when using a var declaring library type. Either use a valid identifier (e. g. ${Template.toIdentifier(
base
)}) or use a different library type (e. g. 'type: "global"', which assign a property on the global scope instead of declaring a variable). ${
AbstractLibraryPlugin.COMMON_LIBRARY_NAME_MESSAGE
}`
);
}
source = new ConcatSource(`${this.declare} ${base};\n`, source);
}
return source;
}
/**
* @param {Module} module the exporting entry module
* @param {RenderContext} renderContext render context
* @param {LibraryContext<T>} libraryContext context
* @returns {string | undefined} bailout reason
*/
embedInRuntimeBailout(
module,
{ chunk, codeGenerationResults },
{ options, compilation }
) {
const { data } = codeGenerationResults.get(module, chunk.runtime);
const topLevelDeclarations =
(data && data.get("topLevelDeclarations")) ||
(module.buildInfo && module.buildInfo.topLevelDeclarations);
if (!topLevelDeclarations)
return "it doesn't tell about top level declarations.";
const fullNameResolved = this._getResolvedFullName(
options,
chunk,
compilation
);
const base = fullNameResolved[0];
if (topLevelDeclarations.has(base))
return `it declares '${base}' on top-level, which conflicts with the current library output.`;
}
/**
* @param {RenderContext} renderContext render context
* @param {LibraryContext<T>} libraryContext context
* @returns {string | undefined} bailout reason
*/
strictRuntimeBailout({ chunk }, { options, compilation }) {
if (
this.declare ||
this.prefix === "global" ||
this.prefix.length > 0 ||
!options.name
) {
return;
}
return "a global variable is assign and maybe created";
}
/**
* @param {Source} source source
* @param {Module} module module
* @param {StartupRenderContext} renderContext render context
* @param {LibraryContext<T>} libraryContext context
* @returns {Source} source with library export
*/
renderStartup(
source,
module,
{ moduleGraph, chunk },
{ options, compilation }
) {
const fullNameResolved = this._getResolvedFullName(
options,
chunk,
compilation
);
const staticExports = this.unnamed === "static";
const exportAccess = options.export
? propertyAccess(
Array.isArray(options.export) ? options.export : [options.export]
)
: "";
const result = new ConcatSource(source);
if (staticExports) {
const exportsInfo = moduleGraph.getExportsInfo(module);
const exportTarget = accessWithInit(
fullNameResolved,
this._getPrefix(compilation).length,
true
);
/** @type {string[]} */
const provided = [];
for (const exportInfo of exportsInfo.orderedExports) {
if (!exportInfo.provided) continue;
const nameAccess = propertyAccess([exportInfo.name]);
result.add(
`${exportTarget}${nameAccess} = ${RuntimeGlobals.exports}${exportAccess}${nameAccess};\n`
);
provided.push(exportInfo.name);
}
const webpackExportTarget = accessWithInit(
fullNameResolved,
this._getPrefix(compilation).length,
true
);
/** @type {string} */
let exports = RuntimeGlobals.exports;
if (exportAccess) {
result.add(
`var __webpack_exports_export__ = ${RuntimeGlobals.exports}${exportAccess};\n`
);
exports = "__webpack_exports_export__";
}
result.add(`for(var __webpack_i__ in ${exports}) {\n`);
const hasProvided = provided.length > 0;
if (hasProvided) {
result.add(
` if (${JSON.stringify(provided)}.indexOf(__webpack_i__) === -1) {\n`
);
}
result.add(
` ${hasProvided ? " " : ""}${webpackExportTarget}[__webpack_i__] = ${exports}[__webpack_i__];\n`
);
result.add(hasProvided ? " }\n}\n" : "\n");
result.add(
`Object.defineProperty(${exportTarget}, "__esModule", { value: true });\n`
);
} else if (options.name ? this.named === "copy" : this.unnamed === "copy") {
result.add(
`var __webpack_export_target__ = ${accessWithInit(
fullNameResolved,
this._getPrefix(compilation).length,
true
)};\n`
);
/** @type {string} */
let exports = RuntimeGlobals.exports;
if (exportAccess) {
result.add(
`var __webpack_exports_export__ = ${RuntimeGlobals.exports}${exportAccess};\n`
);
exports = "__webpack_exports_export__";
}
result.add(
`for(var __webpack_i__ in ${exports}) __webpack_export_target__[__webpack_i__] = ${exports}[__webpack_i__];\n`
);
result.add(
`if(${exports}.__esModule) Object.defineProperty(__webpack_export_target__, "__esModule", { value: true });\n`
);
} else {
result.add(
`${accessWithInit(
fullNameResolved,
this._getPrefix(compilation).length,
false
)} = ${RuntimeGlobals.exports}${exportAccess};\n`
);
}
return result;
}
/**
* @param {Chunk} chunk the chunk
* @param {Set<string>} set runtime requirements
* @param {LibraryContext<T>} libraryContext context
* @returns {void}
*/
runtimeRequirements(chunk, set, libraryContext) {
set.add(RuntimeGlobals.exports);
}
/**
* @param {Chunk} chunk the chunk
* @param {Hash} hash hash
* @param {ChunkHashContext} chunkHashContext chunk hash context
* @param {LibraryContext<T>} libraryContext context
* @returns {void}
*/
chunkHash(chunk, hash, chunkHashContext, { options, compilation }) {
hash.update("AssignLibraryPlugin");
const fullNameResolved = this._getResolvedFullName(
options,
chunk,
compilation
);
if (options.name ? this.named === "copy" : this.unnamed === "copy") {
hash.update("copy");
}
if (this.declare) {
hash.update(this.declare);
}
hash.update(fullNameResolved.join("."));
if (options.export) {
hash.update(`${options.export}`);
}
}
}
module.exports = AssignLibraryPlugin;

View File

@ -0,0 +1,289 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
/** @typedef {import("../../declarations/WebpackOptions").LibraryOptions} LibraryOptions */
/** @typedef {import("../../declarations/WebpackOptions").LibraryType} LibraryType */
/** @typedef {import("../Compiler")} Compiler */
/** @type {WeakMap<Compiler, Set<LibraryType>>} */
const enabledTypes = new WeakMap();
/**
* @typedef {object} EnableLibraryPluginOptions
* @property {() => void=} additionalApply function that runs when applying the current plugin.
*/
/**
* @param {Compiler} compiler the compiler instance
* @returns {Set<LibraryType>} enabled types
*/
const getEnabledTypes = compiler => {
let set = enabledTypes.get(compiler);
if (set === undefined) {
set = new Set();
enabledTypes.set(compiler, set);
}
return set;
};
class EnableLibraryPlugin {
/**
* @param {LibraryType} type library type that should be available
* @param {EnableLibraryPluginOptions} options options of EnableLibraryPlugin
*/
constructor(type, options = {}) {
/** @type {LibraryType} */
this.type = type;
/** @type {EnableLibraryPluginOptions} */
this.options = options;
}
/**
* @param {Compiler} compiler the compiler instance
* @param {LibraryType} type type of library
* @returns {void}
*/
static setEnabled(compiler, type) {
getEnabledTypes(compiler).add(type);
}
/**
* @param {Compiler} compiler the compiler instance
* @param {LibraryType} type type of library
* @returns {void}
*/
static checkEnabled(compiler, type) {
if (!getEnabledTypes(compiler).has(type)) {
throw new Error(
`Library type "${type}" is not enabled. ` +
"EnableLibraryPlugin need to be used to enable this type of library. " +
'This usually happens through the "output.enabledLibraryTypes" option. ' +
'If you are using a function as entry which sets "library", you need to add all potential library types to "output.enabledLibraryTypes". ' +
`These types are enabled: ${Array.from(
getEnabledTypes(compiler)
).join(", ")}`
);
}
}
/**
* Apply the plugin
* @param {Compiler} compiler the compiler instance
* @returns {void}
*/
apply(compiler) {
const { type, options } = this;
// Only enable once
const enabled = getEnabledTypes(compiler);
if (enabled.has(type)) return;
enabled.add(type);
if (typeof options.additionalApply === "function") {
options.additionalApply();
}
if (typeof type === "string") {
const enableExportProperty = () => {
const ExportPropertyTemplatePlugin = require("./ExportPropertyLibraryPlugin");
new ExportPropertyTemplatePlugin({
type,
nsObjectUsed: !["module", "modern-module"].includes(type),
runtimeExportsUsed: !["module", "modern-module"].includes(type),
renderStartupUsed: !["module", "modern-module"].includes(type)
}).apply(compiler);
};
switch (type) {
case "var": {
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
const AssignLibraryPlugin = require("./AssignLibraryPlugin");
new AssignLibraryPlugin({
type,
prefix: [],
declare: "var",
unnamed: "error"
}).apply(compiler);
break;
}
case "assign-properties": {
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
const AssignLibraryPlugin = require("./AssignLibraryPlugin");
new AssignLibraryPlugin({
type,
prefix: [],
declare: false,
unnamed: "error",
named: "copy"
}).apply(compiler);
break;
}
case "assign": {
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
const AssignLibraryPlugin = require("./AssignLibraryPlugin");
new AssignLibraryPlugin({
type,
prefix: [],
declare: false,
unnamed: "error"
}).apply(compiler);
break;
}
case "this": {
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
const AssignLibraryPlugin = require("./AssignLibraryPlugin");
new AssignLibraryPlugin({
type,
prefix: ["this"],
declare: false,
unnamed: "copy"
}).apply(compiler);
break;
}
case "window": {
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
const AssignLibraryPlugin = require("./AssignLibraryPlugin");
new AssignLibraryPlugin({
type,
prefix: ["window"],
declare: false,
unnamed: "copy"
}).apply(compiler);
break;
}
case "self": {
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
const AssignLibraryPlugin = require("./AssignLibraryPlugin");
new AssignLibraryPlugin({
type,
prefix: ["self"],
declare: false,
unnamed: "copy"
}).apply(compiler);
break;
}
case "global": {
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
const AssignLibraryPlugin = require("./AssignLibraryPlugin");
new AssignLibraryPlugin({
type,
prefix: "global",
declare: false,
unnamed: "copy"
}).apply(compiler);
break;
}
case "commonjs": {
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
const AssignLibraryPlugin = require("./AssignLibraryPlugin");
new AssignLibraryPlugin({
type,
prefix: ["exports"],
declare: false,
unnamed: "copy"
}).apply(compiler);
break;
}
case "commonjs-static": {
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
const AssignLibraryPlugin = require("./AssignLibraryPlugin");
new AssignLibraryPlugin({
type,
prefix: ["exports"],
declare: false,
unnamed: "static"
}).apply(compiler);
break;
}
case "commonjs2":
case "commonjs-module": {
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
const AssignLibraryPlugin = require("./AssignLibraryPlugin");
new AssignLibraryPlugin({
type,
prefix: ["module", "exports"],
declare: false,
unnamed: "assign"
}).apply(compiler);
break;
}
case "amd":
case "amd-require": {
enableExportProperty();
const AmdLibraryPlugin = require("./AmdLibraryPlugin");
new AmdLibraryPlugin({
type,
requireAsWrapper: type === "amd-require"
}).apply(compiler);
break;
}
case "umd":
case "umd2": {
if (compiler.options.output.iife === false) {
compiler.options.output.iife = true;
class WarnFalseIifeUmdPlugin {
/**
* @param {Compiler} compiler the compiler instance
*/
apply(compiler) {
compiler.hooks.thisCompilation.tap(
"WarnFalseIifeUmdPlugin",
compilation => {
const FalseIIFEUmdWarning = require("../FalseIIFEUmdWarning");
compilation.warnings.push(new FalseIIFEUmdWarning());
}
);
}
}
new WarnFalseIifeUmdPlugin().apply(compiler);
}
enableExportProperty();
const UmdLibraryPlugin = require("./UmdLibraryPlugin");
new UmdLibraryPlugin({
type,
optionalAmdExternalAsGlobal: type === "umd2"
}).apply(compiler);
break;
}
case "system": {
enableExportProperty();
const SystemLibraryPlugin = require("./SystemLibraryPlugin");
new SystemLibraryPlugin({
type
}).apply(compiler);
break;
}
case "jsonp": {
enableExportProperty();
const JsonpLibraryPlugin = require("./JsonpLibraryPlugin");
new JsonpLibraryPlugin({
type
}).apply(compiler);
break;
}
case "module":
case "modern-module": {
enableExportProperty();
const ModuleLibraryPlugin = require("./ModuleLibraryPlugin");
new ModuleLibraryPlugin({
type
}).apply(compiler);
break;
}
default:
throw new Error(`Unsupported library type ${type}.
Plugins which provide custom library types must call EnableLibraryPlugin.setEnabled(compiler, type) to disable this error.`);
}
} else {
// TODO support plugin instances here
// apply them to the compiler
}
}
}
module.exports = EnableLibraryPlugin;

View File

@ -0,0 +1,125 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const { ConcatSource } = require("webpack-sources");
const { UsageState } = require("../ExportsInfo");
const RuntimeGlobals = require("../RuntimeGlobals");
const propertyAccess = require("../util/propertyAccess");
const { getEntryRuntime } = require("../util/runtime");
const AbstractLibraryPlugin = require("./AbstractLibraryPlugin");
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../../declarations/WebpackOptions").LibraryOptions} LibraryOptions */
/** @typedef {import("../../declarations/WebpackOptions").LibraryType} LibraryType */
/** @typedef {import("../Chunk")} Chunk */
/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../Module")} Module */
/** @typedef {import("../javascript/JavascriptModulesPlugin").StartupRenderContext} StartupRenderContext */
/** @template T @typedef {import("./AbstractLibraryPlugin").LibraryContext<T>} LibraryContext<T> */
/**
* @typedef {object} ExportPropertyLibraryPluginParsed
* @property {string | string[]} export
*/
/**
* @typedef {object} ExportPropertyLibraryPluginOptions
* @property {LibraryType} type
* @property {boolean} nsObjectUsed the namespace object is used
* @property {boolean} runtimeExportsUsed runtime exports are used
* @property {boolean} renderStartupUsed render startup is used
*/
/**
* @typedef {ExportPropertyLibraryPluginParsed} T
* @extends {AbstractLibraryPlugin<ExportPropertyLibraryPluginParsed>}
*/
class ExportPropertyLibraryPlugin extends AbstractLibraryPlugin {
/**
* @param {ExportPropertyLibraryPluginOptions} options options
*/
constructor({ type, nsObjectUsed, runtimeExportsUsed, renderStartupUsed }) {
super({
pluginName: "ExportPropertyLibraryPlugin",
type
});
this.nsObjectUsed = nsObjectUsed;
this.runtimeExportsUsed = runtimeExportsUsed;
this.renderStartupUsed = renderStartupUsed;
}
/**
* @param {LibraryOptions} library normalized library option
* @returns {T | false} preprocess as needed by overriding
*/
parseOptions(library) {
return {
export: /** @type {string | string[]} */ (library.export)
};
}
/**
* @param {Module} module the exporting entry module
* @param {string} entryName the name of the entrypoint
* @param {LibraryContext<T>} libraryContext context
* @returns {void}
*/
finishEntryModule(
module,
entryName,
{ options, compilation, compilation: { moduleGraph } }
) {
const runtime = getEntryRuntime(compilation, entryName);
if (options.export) {
const exportsInfo = moduleGraph.getExportInfo(
module,
Array.isArray(options.export) ? options.export[0] : options.export
);
exportsInfo.setUsed(UsageState.Used, runtime);
exportsInfo.canMangleUse = false;
} else {
const exportsInfo = moduleGraph.getExportsInfo(module);
if (this.nsObjectUsed) {
exportsInfo.setUsedInUnknownWay(runtime);
} else {
exportsInfo.setAllKnownExportsUsed(runtime);
}
}
moduleGraph.addExtraReason(module, "used as library export");
}
/**
* @param {Chunk} chunk the chunk
* @param {Set<string>} set runtime requirements
* @param {LibraryContext<T>} libraryContext context
* @returns {void}
*/
runtimeRequirements(chunk, set, libraryContext) {
if (this.runtimeExportsUsed) {
set.add(RuntimeGlobals.exports);
}
}
/**
* @param {Source} source source
* @param {Module} module module
* @param {StartupRenderContext} renderContext render context
* @param {LibraryContext<T>} libraryContext context
* @returns {Source} source with library export
*/
renderStartup(source, module, renderContext, { options }) {
if (!this.renderStartupUsed) return source;
if (!options.export) return source;
const postfix = `${RuntimeGlobals.exports} = ${
RuntimeGlobals.exports
}${propertyAccess(
Array.isArray(options.export) ? options.export : [options.export]
)};\n`;
return new ConcatSource(source, postfix);
}
}
module.exports = ExportPropertyLibraryPlugin;

View File

@ -0,0 +1,89 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const { ConcatSource } = require("webpack-sources");
const AbstractLibraryPlugin = require("./AbstractLibraryPlugin");
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../../declarations/WebpackOptions").LibraryOptions} LibraryOptions */
/** @typedef {import("../../declarations/WebpackOptions").LibraryType} LibraryType */
/** @typedef {import("../Chunk")} Chunk */
/** @typedef {import("../Compilation").ChunkHashContext} ChunkHashContext */
/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../javascript/JavascriptModulesPlugin").RenderContext} RenderContext */
/** @typedef {import("../util/Hash")} Hash */
/** @template T @typedef {import("./AbstractLibraryPlugin").LibraryContext<T>} LibraryContext<T> */
/**
* @typedef {object} JsonpLibraryPluginOptions
* @property {LibraryType} type
*/
/**
* @typedef {object} JsonpLibraryPluginParsed
* @property {string} name
*/
/**
* @typedef {JsonpLibraryPluginParsed} T
* @extends {AbstractLibraryPlugin<JsonpLibraryPluginParsed>}
*/
class JsonpLibraryPlugin extends AbstractLibraryPlugin {
/**
* @param {JsonpLibraryPluginOptions} options the plugin options
*/
constructor(options) {
super({
pluginName: "JsonpLibraryPlugin",
type: options.type
});
}
/**
* @param {LibraryOptions} library normalized library option
* @returns {T | false} preprocess as needed by overriding
*/
parseOptions(library) {
const { name } = library;
if (typeof name !== "string") {
throw new Error(
`Jsonp library name must be a simple string. ${AbstractLibraryPlugin.COMMON_LIBRARY_NAME_MESSAGE}`
);
}
const _name = /** @type {string} */ (name);
return {
name: _name
};
}
/**
* @param {Source} source source
* @param {RenderContext} renderContext render context
* @param {LibraryContext<T>} libraryContext context
* @returns {Source} source with library export
*/
render(source, { chunk }, { options, compilation }) {
const name = compilation.getPath(options.name, {
chunk
});
return new ConcatSource(`${name}(`, source, ")");
}
/**
* @param {Chunk} chunk the chunk
* @param {Hash} hash hash
* @param {ChunkHashContext} chunkHashContext chunk hash context
* @param {LibraryContext<T>} libraryContext context
* @returns {void}
*/
chunkHash(chunk, hash, chunkHashContext, { options, compilation }) {
hash.update("JsonpLibraryPlugin");
hash.update(compilation.getPath(options.name, { chunk }));
}
}
module.exports = JsonpLibraryPlugin;

View File

@ -0,0 +1,241 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const { ConcatSource } = require("webpack-sources");
const RuntimeGlobals = require("../RuntimeGlobals");
const Template = require("../Template");
const ConcatenatedModule = require("../optimize/ConcatenatedModule");
const propertyAccess = require("../util/propertyAccess");
const AbstractLibraryPlugin = require("./AbstractLibraryPlugin");
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../../declarations/WebpackOptions").LibraryOptions} LibraryOptions */
/** @typedef {import("../../declarations/WebpackOptions").LibraryType} LibraryType */
/** @typedef {import("../Chunk")} Chunk */
/** @typedef {import("../Compilation").ChunkHashContext} ChunkHashContext */
/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../Module")} Module */
/** @typedef {import("../Module").BuildMeta} BuildMeta */
/** @typedef {import("../javascript/JavascriptModulesPlugin").StartupRenderContext} StartupRenderContext */
/** @typedef {import("../util/Hash")} Hash */
/**
* @template T
* @typedef {import("./AbstractLibraryPlugin").LibraryContext<T>} LibraryContext<T>
*/
/**
* @typedef {object} ModuleLibraryPluginOptions
* @property {LibraryType} type
*/
/**
* @typedef {object} ModuleLibraryPluginParsed
* @property {string} name
* @property {string | string[]=} export
*/
const PLUGIN_NAME = "ModuleLibraryPlugin";
/**
* @typedef {ModuleLibraryPluginParsed} T
* @extends {AbstractLibraryPlugin<ModuleLibraryPluginParsed>}
*/
class ModuleLibraryPlugin extends AbstractLibraryPlugin {
/**
* @param {ModuleLibraryPluginOptions} options the plugin options
*/
constructor(options) {
super({
pluginName: "ModuleLibraryPlugin",
type: options.type
});
}
/**
* Apply the plugin
* @param {Compiler} compiler the compiler instance
* @returns {void}
*/
apply(compiler) {
super.apply(compiler);
compiler.hooks.thisCompilation.tap(PLUGIN_NAME, compilation => {
const { exportsDefinitions } =
ConcatenatedModule.getCompilationHooks(compilation);
exportsDefinitions.tap(PLUGIN_NAME, (definitions, module) => {
// If we have connections not all modules were concatenated, so we need the wrapper
const connections =
compilation.moduleGraph.getIncomingConnections(module);
for (const connection of connections) {
if (connection.originModule) {
return false;
}
}
// Runtime and splitting chunks now requires the wrapper too
for (const chunk of compilation.chunkGraph.getModuleChunksIterable(
module
)) {
if (
!chunk.hasRuntime() ||
compilation.chunkGraph.getNumberOfEntryModules(chunk) > 1
) {
return false;
}
}
return true;
});
});
}
/**
* @param {LibraryOptions} library normalized library option
* @returns {T | false} preprocess as needed by overriding
*/
parseOptions(library) {
const { name } = library;
if (name) {
throw new Error(
`Library name must be unset. ${AbstractLibraryPlugin.COMMON_LIBRARY_NAME_MESSAGE}`
);
}
const _name = /** @type {string} */ (name);
return {
name: _name,
export: library.export
};
}
/**
* @param {Source} source source
* @param {Module} module module
* @param {StartupRenderContext} renderContext render context
* @param {LibraryContext<T>} libraryContext context
* @returns {Source} source with library export
*/
renderStartup(
source,
module,
{ moduleGraph, chunk, codeGenerationResults },
{ options, compilation }
) {
const result = new ConcatSource(source);
const exportsInfo = options.export
? [
moduleGraph.getExportInfo(
module,
Array.isArray(options.export) ? options.export[0] : options.export
)
]
: moduleGraph.getExportsInfo(module).orderedExports;
const definitions =
/** @type {BuildMeta} */
(module.buildMeta).exportsFinalName || {};
/** @type {string[]} */
const shortHandedExports = [];
/** @type {[string, string][]} */
const exports = [];
const isAsync = moduleGraph.isAsync(module);
if (isAsync) {
result.add(
`${RuntimeGlobals.exports} = await ${RuntimeGlobals.exports};\n`
);
}
const varType = compilation.outputOptions.environment.const
? "const"
: "var";
for (const exportInfo of exportsInfo) {
if (!exportInfo.provided) continue;
let shouldContinue = false;
const reexport = exportInfo.findTarget(moduleGraph, _m => true);
if (reexport) {
const exp = moduleGraph.getExportsInfo(reexport.module);
for (const reexportInfo of exp.orderedExports) {
if (
reexportInfo.provided === false &&
reexportInfo.name !== "default" &&
reexportInfo.name === /** @type {string[]} */ (reexport.export)[0]
) {
shouldContinue = true;
}
}
}
if (shouldContinue) continue;
const originalName = exportInfo.name;
const usedName =
/** @type {string} */
(exportInfo.getUsedName(originalName, chunk.runtime));
/** @type {string | undefined} */
const definition = definitions[usedName];
const finalName =
definition ||
`${RuntimeGlobals.exports}${Template.toIdentifier(originalName)}`;
if (!definition) {
result.add(
`${varType} ${finalName} = ${RuntimeGlobals.exports}${propertyAccess([
usedName
])};\n`
);
}
if (
finalName &&
(finalName.includes(".") ||
finalName.includes("[") ||
finalName.includes("("))
) {
if (exportInfo.isReexport()) {
const { data } = codeGenerationResults.get(module, chunk.runtime);
const topLevelDeclarations =
(data && data.get("topLevelDeclarations")) ||
(module.buildInfo && module.buildInfo.topLevelDeclarations);
if (topLevelDeclarations && topLevelDeclarations.has(originalName)) {
const name = `${RuntimeGlobals.exports}${Template.toIdentifier(originalName)}`;
result.add(`${varType} ${name} = ${finalName};\n`);
shortHandedExports.push(`${name} as ${originalName}`);
} else {
exports.push([originalName, finalName]);
}
} else {
exports.push([originalName, finalName]);
}
} else {
shortHandedExports.push(
definition && finalName === originalName
? finalName
: `${finalName} as ${originalName}`
);
}
}
if (shortHandedExports.length > 0) {
result.add(`export { ${shortHandedExports.join(", ")} };\n`);
}
for (const [exportName, final] of exports) {
result.add(`export ${varType} ${exportName} = ${final};\n`);
}
return result;
}
}
module.exports = ModuleLibraryPlugin;

View File

@ -0,0 +1,235 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Joel Denning @joeldenning
*/
"use strict";
const { ConcatSource } = require("webpack-sources");
const { UsageState } = require("../ExportsInfo");
const ExternalModule = require("../ExternalModule");
const Template = require("../Template");
const propertyAccess = require("../util/propertyAccess");
const AbstractLibraryPlugin = require("./AbstractLibraryPlugin");
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../../declarations/WebpackOptions").LibraryOptions} LibraryOptions */
/** @typedef {import("../../declarations/WebpackOptions").LibraryType} LibraryType */
/** @typedef {import("../Chunk")} Chunk */
/** @typedef {import("../Compilation").ChunkHashContext} ChunkHashContext */
/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../javascript/JavascriptModulesPlugin").RenderContext} RenderContext */
/** @typedef {import("../util/Hash")} Hash */
/** @template T @typedef {import("./AbstractLibraryPlugin").LibraryContext<T>} LibraryContext<T> */
/**
* @typedef {object} SystemLibraryPluginOptions
* @property {LibraryType} type
*/
/**
* @typedef {object} SystemLibraryPluginParsed
* @property {string} name
*/
/**
* @typedef {SystemLibraryPluginParsed} T
* @extends {AbstractLibraryPlugin<SystemLibraryPluginParsed>}
*/
class SystemLibraryPlugin extends AbstractLibraryPlugin {
/**
* @param {SystemLibraryPluginOptions} options the plugin options
*/
constructor(options) {
super({
pluginName: "SystemLibraryPlugin",
type: options.type
});
}
/**
* @param {LibraryOptions} library normalized library option
* @returns {T | false} preprocess as needed by overriding
*/
parseOptions(library) {
const { name } = library;
if (name && typeof name !== "string") {
throw new Error(
`System.js library name must be a simple string or unset. ${AbstractLibraryPlugin.COMMON_LIBRARY_NAME_MESSAGE}`
);
}
const _name = /** @type {string} */ (name);
return {
name: _name
};
}
/**
* @param {Source} source source
* @param {RenderContext} renderContext render context
* @param {LibraryContext<T>} libraryContext context
* @returns {Source} source with library export
*/
render(source, { chunkGraph, moduleGraph, chunk }, { options, compilation }) {
const modules = chunkGraph
.getChunkModules(chunk)
.filter(m => m instanceof ExternalModule && m.externalType === "system");
const externals = /** @type {ExternalModule[]} */ (modules);
// The name this bundle should be registered as with System
const name = options.name
? `${JSON.stringify(compilation.getPath(options.name, { chunk }))}, `
: "";
// The array of dependencies that are external to webpack and will be provided by System
const systemDependencies = JSON.stringify(
externals.map(m =>
typeof m.request === "object" && !Array.isArray(m.request)
? m.request.amd
: m.request
)
);
// The name of the variable provided by System for exporting
const dynamicExport = "__WEBPACK_DYNAMIC_EXPORT__";
// An array of the internal variable names for the webpack externals
const externalWebpackNames = externals.map(
m =>
`__WEBPACK_EXTERNAL_MODULE_${Template.toIdentifier(
`${chunkGraph.getModuleId(m)}`
)}__`
);
// Declaring variables for the internal variable names for the webpack externals
const externalVarDeclarations = externalWebpackNames
.map(name => `var ${name} = {};`)
.join("\n");
// Define __esModule flag on all internal variables and helpers
/** @type {string[]} */
const externalVarInitialization = [];
// The system.register format requires an array of setter functions for externals.
const setters =
externalWebpackNames.length === 0
? ""
: Template.asString([
"setters: [",
Template.indent(
externals
.map((module, i) => {
const external = externalWebpackNames[i];
const exportsInfo = moduleGraph.getExportsInfo(module);
const otherUnused =
exportsInfo.otherExportsInfo.getUsed(chunk.runtime) ===
UsageState.Unused;
const instructions = [];
const handledNames = [];
for (const exportInfo of exportsInfo.orderedExports) {
const used = exportInfo.getUsedName(
undefined,
chunk.runtime
);
if (used) {
if (otherUnused || used !== exportInfo.name) {
instructions.push(
`${external}${propertyAccess([
used
])} = module${propertyAccess([exportInfo.name])};`
);
handledNames.push(exportInfo.name);
}
} else {
handledNames.push(exportInfo.name);
}
}
if (!otherUnused) {
if (
!Array.isArray(module.request) ||
module.request.length === 1
) {
externalVarInitialization.push(
`Object.defineProperty(${external}, "__esModule", { value: true });`
);
}
if (handledNames.length > 0) {
const name = `${external}handledNames`;
externalVarInitialization.push(
`var ${name} = ${JSON.stringify(handledNames)};`
);
instructions.push(
Template.asString([
"Object.keys(module).forEach(function(key) {",
Template.indent([
`if(${name}.indexOf(key) >= 0)`,
Template.indent(`${external}[key] = module[key];`)
]),
"});"
])
);
} else {
instructions.push(
Template.asString([
"Object.keys(module).forEach(function(key) {",
Template.indent([`${external}[key] = module[key];`]),
"});"
])
);
}
}
if (instructions.length === 0) return "function() {}";
return Template.asString([
"function(module) {",
Template.indent(instructions),
"}"
]);
})
.join(",\n")
),
"],"
]);
return new ConcatSource(
Template.asString([
`System.register(${name}${systemDependencies}, function(${dynamicExport}, __system_context__) {`,
Template.indent([
externalVarDeclarations,
Template.asString(externalVarInitialization),
"return {",
Template.indent([
setters,
"execute: function() {",
Template.indent(`${dynamicExport}(`)
])
]),
""
]),
source,
Template.asString([
"",
Template.indent([
Template.indent([Template.indent([");"]), "}"]),
"};"
]),
"})"
])
);
}
/**
* @param {Chunk} chunk the chunk
* @param {Hash} hash hash
* @param {ChunkHashContext} chunkHashContext chunk hash context
* @param {LibraryContext<T>} libraryContext context
* @returns {void}
*/
chunkHash(chunk, hash, chunkHashContext, { options, compilation }) {
hash.update("SystemLibraryPlugin");
if (options.name) {
hash.update(compilation.getPath(options.name, { chunk }));
}
}
}
module.exports = SystemLibraryPlugin;

View File

@ -0,0 +1,351 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const { ConcatSource, OriginalSource } = require("webpack-sources");
const ExternalModule = require("../ExternalModule");
const Template = require("../Template");
const AbstractLibraryPlugin = require("./AbstractLibraryPlugin");
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../../declarations/WebpackOptions").LibraryCustomUmdCommentObject} LibraryCustomUmdCommentObject */
/** @typedef {import("../../declarations/WebpackOptions").LibraryCustomUmdObject} LibraryCustomUmdObject */
/** @typedef {import("../../declarations/WebpackOptions").LibraryName} LibraryName */
/** @typedef {import("../../declarations/WebpackOptions").LibraryOptions} LibraryOptions */
/** @typedef {import("../../declarations/WebpackOptions").LibraryType} LibraryType */
/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../javascript/JavascriptModulesPlugin").RenderContext} RenderContext */
/** @typedef {import("../ExternalModule").RequestRecord} RequestRecord */
/** @typedef {import("../util/Hash")} Hash */
/**
* @template T
* @typedef {import("./AbstractLibraryPlugin").LibraryContext<T>}
* LibraryContext<T>
*/
/**
* @param {string[]} accessor the accessor to convert to path
* @returns {string} the path
*/
const accessorToObjectAccess = accessor =>
accessor.map(a => `[${JSON.stringify(a)}]`).join("");
/**
* @param {string|undefined} base the path prefix
* @param {string|string[]} accessor the accessor
* @param {string=} joinWith the element separator
* @returns {string} the path
*/
const accessorAccess = (base, accessor, joinWith = ", ") => {
const accessors = Array.isArray(accessor) ? accessor : [accessor];
return accessors
.map((_, idx) => {
const a = base
? base + accessorToObjectAccess(accessors.slice(0, idx + 1))
: accessors[0] + accessorToObjectAccess(accessors.slice(1, idx + 1));
if (idx === accessors.length - 1) return a;
if (idx === 0 && base === undefined)
return `${a} = typeof ${a} === "object" ? ${a} : {}`;
return `${a} = ${a} || {}`;
})
.join(joinWith);
};
/** @typedef {string | string[] | LibraryCustomUmdObject} UmdLibraryPluginName */
/**
* @typedef {object} UmdLibraryPluginOptions
* @property {LibraryType} type
* @property {boolean=} optionalAmdExternalAsGlobal
*/
/**
* @typedef {object} UmdLibraryPluginParsed
* @property {string | string[] | undefined} name
* @property {LibraryCustomUmdObject} names
* @property {string | LibraryCustomUmdCommentObject | undefined} auxiliaryComment
* @property {boolean | undefined} namedDefine
*/
/**
* @typedef {UmdLibraryPluginParsed} T
* @extends {AbstractLibraryPlugin<UmdLibraryPluginParsed>}
*/
class UmdLibraryPlugin extends AbstractLibraryPlugin {
/**
* @param {UmdLibraryPluginOptions} options the plugin option
*/
constructor(options) {
super({
pluginName: "UmdLibraryPlugin",
type: options.type
});
this.optionalAmdExternalAsGlobal = options.optionalAmdExternalAsGlobal;
}
/**
* @param {LibraryOptions} library normalized library option
* @returns {T | false} preprocess as needed by overriding
*/
parseOptions(library) {
/** @type {LibraryName | undefined} */
let name;
/** @type {LibraryCustomUmdObject} */
let names;
if (typeof library.name === "object" && !Array.isArray(library.name)) {
name = library.name.root || library.name.amd || library.name.commonjs;
names = library.name;
} else {
name = library.name;
const singleName = Array.isArray(name) ? name[0] : name;
names = {
commonjs: singleName,
root: library.name,
amd: singleName
};
}
return {
name,
names,
auxiliaryComment: library.auxiliaryComment,
namedDefine: library.umdNamedDefine
};
}
/**
* @param {Source} source source
* @param {RenderContext} renderContext render context
* @param {LibraryContext<T>} libraryContext context
* @returns {Source} source with library export
*/
render(
source,
{ chunkGraph, runtimeTemplate, chunk, moduleGraph },
{ options, compilation }
) {
const modules = chunkGraph
.getChunkModules(chunk)
.filter(
m =>
m instanceof ExternalModule &&
(m.externalType === "umd" || m.externalType === "umd2")
);
let externals = /** @type {ExternalModule[]} */ (modules);
/** @type {ExternalModule[]} */
const optionalExternals = [];
/** @type {ExternalModule[]} */
let requiredExternals = [];
if (this.optionalAmdExternalAsGlobal) {
for (const m of externals) {
if (m.isOptional(moduleGraph)) {
optionalExternals.push(m);
} else {
requiredExternals.push(m);
}
}
externals = requiredExternals.concat(optionalExternals);
} else {
requiredExternals = externals;
}
/**
* @param {string} str the string to replace
* @returns {string} the replaced keys
*/
const replaceKeys = str =>
compilation.getPath(str, {
chunk
});
/**
* @param {ExternalModule[]} modules external modules
* @returns {string} result
*/
const externalsDepsArray = modules =>
`[${replaceKeys(
modules
.map(m =>
JSON.stringify(
typeof m.request === "object"
? /** @type {RequestRecord} */
(m.request).amd
: m.request
)
)
.join(", ")
)}]`;
/**
* @param {ExternalModule[]} modules external modules
* @returns {string} result
*/
const externalsRootArray = modules =>
replaceKeys(
modules
.map(m => {
let request = m.request;
if (typeof request === "object")
request =
/** @type {RequestRecord} */
(request).root;
return `root${accessorToObjectAccess(/** @type {string[]} */ ([]).concat(request))}`;
})
.join(", ")
);
/**
* @param {string} type the type
* @returns {string} external require array
*/
const externalsRequireArray = type =>
replaceKeys(
externals
.map(m => {
let expr;
let request = m.request;
if (typeof request === "object") {
request =
/** @type {RequestRecord} */
(request)[type];
}
if (request === undefined) {
throw new Error(
`Missing external configuration for type:${type}`
);
}
expr = Array.isArray(request)
? `require(${JSON.stringify(
request[0]
)})${accessorToObjectAccess(request.slice(1))}`
: `require(${JSON.stringify(request)})`;
if (m.isOptional(moduleGraph)) {
expr = `(function webpackLoadOptionalExternalModule() { try { return ${expr}; } catch(e) {} }())`;
}
return expr;
})
.join(", ")
);
/**
* @param {ExternalModule[]} modules external modules
* @returns {string} arguments
*/
const externalsArguments = modules =>
modules
.map(
m =>
`__WEBPACK_EXTERNAL_MODULE_${Template.toIdentifier(
`${chunkGraph.getModuleId(m)}`
)}__`
)
.join(", ");
/**
* @param {string| string[]} library library name
* @returns {string} stringified library name
*/
const libraryName = library =>
JSON.stringify(
replaceKeys(
/** @type {string} */
(/** @type {string[]} */ ([]).concat(library).pop())
)
);
let amdFactory;
if (optionalExternals.length > 0) {
const wrapperArguments = externalsArguments(requiredExternals);
const factoryArguments =
requiredExternals.length > 0
? `${externalsArguments(requiredExternals)}, ${externalsRootArray(
optionalExternals
)}`
: externalsRootArray(optionalExternals);
amdFactory =
`function webpackLoadOptionalExternalModuleAmd(${wrapperArguments}) {\n` +
` return factory(${factoryArguments});\n` +
" }";
} else {
amdFactory = "factory";
}
const { auxiliaryComment, namedDefine, names } = options;
/**
* @param {keyof LibraryCustomUmdCommentObject} type type
* @returns {string} comment
*/
const getAuxiliaryComment = type => {
if (auxiliaryComment) {
if (typeof auxiliaryComment === "string")
return `\t//${auxiliaryComment}\n`;
if (auxiliaryComment[type]) return `\t//${auxiliaryComment[type]}\n`;
}
return "";
};
return new ConcatSource(
new OriginalSource(
`(function webpackUniversalModuleDefinition(root, factory) {\n${getAuxiliaryComment(
"commonjs2"
)} if(typeof exports === 'object' && typeof module === 'object')\n` +
` module.exports = factory(${externalsRequireArray(
"commonjs2"
)});\n${getAuxiliaryComment(
"amd"
)} else if(typeof define === 'function' && define.amd)\n${
requiredExternals.length > 0
? names.amd && namedDefine === true
? ` define(${libraryName(names.amd)}, ${externalsDepsArray(
requiredExternals
)}, ${amdFactory});\n`
: ` define(${externalsDepsArray(requiredExternals)}, ${
amdFactory
});\n`
: names.amd && namedDefine === true
? ` define(${libraryName(names.amd)}, [], ${amdFactory});\n`
: ` define([], ${amdFactory});\n`
}${
names.root || names.commonjs
? `${getAuxiliaryComment(
"commonjs"
)} else if(typeof exports === 'object')\n` +
` exports[${libraryName(
/** @type {string | string[]} */
(names.commonjs || names.root)
)}] = factory(${externalsRequireArray(
"commonjs"
)});\n${getAuxiliaryComment("root")} else\n` +
` ${replaceKeys(
accessorAccess(
"root",
/** @type {string | string[]} */
(names.root || names.commonjs)
)
)} = factory(${externalsRootArray(externals)});\n`
: ` else {\n${
externals.length > 0
? ` var a = typeof exports === 'object' ? factory(${externalsRequireArray(
"commonjs"
)}) : factory(${externalsRootArray(externals)});\n`
: " var a = factory();\n"
} for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];\n` +
" }\n"
}})(${runtimeTemplate.outputOptions.globalObject}, ${
runtimeTemplate.supportsArrowFunction()
? `(${externalsArguments(externals)}) =>`
: `function(${externalsArguments(externals)})`
} {\nreturn `,
"webpack/universalModuleDefinition"
),
source,
";\n})"
);
}
}
module.exports = UmdLibraryPlugin;