diff --git a/declarations/WebpackOptions.d.ts b/declarations/WebpackOptions.d.ts index ca1261fb1..4942c1888 100644 --- a/declarations/WebpackOptions.d.ts +++ b/declarations/WebpackOptions.d.ts @@ -312,6 +312,189 @@ export type OptimizationSplitChunksSizes = */ [k: string]: number; }; +/** + * The filename of asset modules as relative path inside the `output.path` directory. + */ +export type AssetModuleFilename = + | string + | (( + pathData: import("../lib/Compilation").PathData, + assetInfo?: import("../lib/Compilation").AssetInfo + ) => string); +/** + * Add a comment in the UMD wrapper. + */ +export type AuxiliaryComment = string | LibraryCustomUmdCommentObject; +/** + * The callback function name used by webpack for loading of chunks in WebWorkers. + */ +export type ChunkCallbackName = string; +/** + * The filename of non-entry chunks as relative path inside the `output.path` directory. + */ +export type ChunkFilename = string; +/** + * Number of milliseconds before chunk request expires + */ +export type ChunkLoadTimeout = number; +/** + * Check if to be emitted file already exists and have the same content before writing to output filesystem + */ +export type CompareBeforeEmit = boolean; +/** + * This option enables cross-origin loading of chunks. + */ +export type CrossOriginLoading = false | "anonymous" | "use-credentials"; +/** + * Similar to `output.devtoolModuleFilenameTemplate`, but used in the case of duplicate module identifiers. + */ +export type DevtoolFallbackModuleFilenameTemplate = string | Function; +/** + * Filename template string of function for the sources array in a generated SourceMap. + */ +export type DevtoolModuleFilenameTemplate = string | Function; +/** + * Module namespace to use when interpolating filename template string for the sources array in a generated SourceMap. Defaults to `output.library` if not set. It's useful for avoiding runtime collisions in sourcemaps from multiple webpack projects built as libraries. + */ +export type DevtoolNamespace = string; +/** + * The maximum EcmaScript version of the webpack generated code (doesn't include input source code from modules). + */ +export type EcmaVersion = number | 2009; +/** + * Type of library + */ +export type LibraryType = + | "var" + | "module" + | "assign" + | "this" + | "window" + | "self" + | "global" + | "commonjs" + | "commonjs2" + | "commonjs-module" + | "amd" + | "amd-require" + | "umd" + | "umd2" + | "jsonp" + | "system"; +/** + * List of library types enabled for use by entry points + */ +export type EnabledLibraryTypes = LibraryType[]; +/** + * Specifies the name of each output file on disk. You must **not** specify an absolute path here! The `output.path` option determines the location on disk the files are written to, filename is used solely for naming the individual files. + */ +export type Filename = + | string + | (( + pathData: import("../lib/Compilation").PathData, + assetInfo?: import("../lib/Compilation").AssetInfo + ) => string); +/** + * An expression which is used to address the global object/scope in runtime code + */ +export type GlobalObject = string; +/** + * Digest type used for the hash + */ +export type HashDigest = string; +/** + * Number of chars which are used for the hash + */ +export type HashDigestLength = number; +/** + * Algorithm used for generation the hash (see node.js crypto package) + */ +export type HashFunction = string | typeof import("../lib/util/Hash"); +/** + * Any string which is added to the hash to salt it + */ +export type HashSalt = string; +/** + * The filename of the Hot Update Chunks. They are inside the output.path directory. + */ +export type HotUpdateChunkFilename = string; +/** + * The JSONP function used by webpack for async loading of hot update chunks. + */ +export type HotUpdateFunction = string; +/** + * The filename of the Hot Update Main File. It is inside the `output.path` directory. + */ +export type HotUpdateMainFilename = string; +/** + * Wrap javascript code into IIFEs to avoid leaking into global scope. + */ +export type Iife = boolean; +/** + * The JSONP function used by webpack for async loading of chunks. + */ +export type JsonpFunction = string; +/** + * This option enables loading async chunks via a custom script type, such as script type="module" + */ +export type JsonpScriptType = false | "text/javascript" | "module"; +/** + * Make the output files a library, exporting the exports of the entry point + */ +export type Library = LibraryName | LibraryOptions; +/** + * The name of the library (some types allow unnamed libraries too) + */ +export type LibraryName = string | string[] | LibraryCustomUmdObject; +/** + * Specify which export should be exposed as library + */ +export type LibraryExport = string | ArrayOfStringValues; +/** + * If `output.libraryTarget` is set to umd and `output.library` is set, setting this to true will name the AMD module. + */ +export type UmdNamedDefine = boolean; +/** + * Output javascript files as module source type. + */ +export type OutputModule = boolean; +/** + * The output directory as **absolute path** (required). + */ +export type Path = string; +/** + * Include comments with information about the modules. + */ +export type Pathinfo = boolean; +/** + * The `publicPath` specifies the public URL address of the output files when referenced in a browser. + */ +export type PublicPath = + | string + | (( + pathData: import("../lib/Compilation").PathData, + assetInfo?: import("../lib/Compilation").AssetInfo + ) => string); +/** + * The filename of the SourceMaps for the JavaScript files. They are inside the `output.path` directory. + */ +export type SourceMapFilename = string; +/** + * Prefixes every line of the source in the bundle with this string. + */ +export type SourcePrefix = string; +/** + * Handles exceptions in module loading correctly at a performance cost. + */ +export type StrictModuleExceptionHandling = boolean; +/** + * A unique name of the webpack build to avoid multiple webpack runtimes to conflict when using globals. + */ +export type UniqueName = string; +/** + * The filename of WebAssembly modules as relative path inside the `output.path` directory. + */ +export type WebassemblyModuleFilename = string; /** * The number of parallel processed modules in the compilation. */ @@ -1286,178 +1469,151 @@ export interface Output { /** * The filename of asset modules as relative path inside the `output.path` directory. */ - assetModuleFilename?: - | string - | (( - pathData: import("../lib/Compilation").PathData, - assetInfo?: import("../lib/Compilation").AssetInfo - ) => string); + assetModuleFilename?: AssetModuleFilename; /** * Add a comment in the UMD wrapper. */ - auxiliaryComment?: string | LibraryCustomUmdCommentObject; + auxiliaryComment?: AuxiliaryComment; /** * The callback function name used by webpack for loading of chunks in WebWorkers. */ - chunkCallbackName?: string; + chunkCallbackName?: ChunkCallbackName; /** * The filename of non-entry chunks as relative path inside the `output.path` directory. */ - chunkFilename?: string; + chunkFilename?: ChunkFilename; /** * Number of milliseconds before chunk request expires */ - chunkLoadTimeout?: number; + chunkLoadTimeout?: ChunkLoadTimeout; /** * Check if to be emitted file already exists and have the same content before writing to output filesystem */ - compareBeforeEmit?: boolean; + compareBeforeEmit?: CompareBeforeEmit; /** * This option enables cross-origin loading of chunks. */ - crossOriginLoading?: false | "anonymous" | "use-credentials"; + crossOriginLoading?: CrossOriginLoading; /** * Similar to `output.devtoolModuleFilenameTemplate`, but used in the case of duplicate module identifiers. */ - devtoolFallbackModuleFilenameTemplate?: string | Function; + devtoolFallbackModuleFilenameTemplate?: DevtoolFallbackModuleFilenameTemplate; /** * Filename template string of function for the sources array in a generated SourceMap. */ - devtoolModuleFilenameTemplate?: string | Function; + devtoolModuleFilenameTemplate?: DevtoolModuleFilenameTemplate; /** * Module namespace to use when interpolating filename template string for the sources array in a generated SourceMap. Defaults to `output.library` if not set. It's useful for avoiding runtime collisions in sourcemaps from multiple webpack projects built as libraries. */ - devtoolNamespace?: string; + devtoolNamespace?: DevtoolNamespace; /** * The maximum EcmaScript version of the webpack generated code (doesn't include input source code from modules). */ - ecmaVersion?: number | 2009; + ecmaVersion?: EcmaVersion; + /** + * List of library types enabled for use by entry points + */ + enabledLibraryTypes?: EnabledLibraryTypes; /** * Specifies the name of each output file on disk. You must **not** specify an absolute path here! The `output.path` option determines the location on disk the files are written to, filename is used solely for naming the individual files. */ - filename?: - | string - | (( - pathData: import("../lib/Compilation").PathData, - assetInfo?: import("../lib/Compilation").AssetInfo - ) => string); + filename?: Filename; /** * An expression which is used to address the global object/scope in runtime code */ - globalObject?: string; + globalObject?: GlobalObject; /** * Digest type used for the hash */ - hashDigest?: string; + hashDigest?: HashDigest; /** * Number of chars which are used for the hash */ - hashDigestLength?: number; + hashDigestLength?: HashDigestLength; /** * Algorithm used for generation the hash (see node.js crypto package) */ - hashFunction?: string | typeof import("../lib/util/Hash"); + hashFunction?: HashFunction; /** * Any string which is added to the hash to salt it */ - hashSalt?: string; + hashSalt?: HashSalt; /** * The filename of the Hot Update Chunks. They are inside the output.path directory. */ - hotUpdateChunkFilename?: string; + hotUpdateChunkFilename?: HotUpdateChunkFilename; /** * The JSONP function used by webpack for async loading of hot update chunks. */ - hotUpdateFunction?: string; + hotUpdateFunction?: HotUpdateFunction; /** * The filename of the Hot Update Main File. It is inside the `output.path` directory. */ - hotUpdateMainFilename?: string; + hotUpdateMainFilename?: HotUpdateMainFilename; /** * Wrap javascript code into IIFEs to avoid leaking into global scope. */ - iife?: boolean; + iife?: Iife; /** * The JSONP function used by webpack for async loading of chunks. */ - jsonpFunction?: string; + jsonpFunction?: JsonpFunction; /** * This option enables loading async chunks via a custom script type, such as script type="module" */ - jsonpScriptType?: false | "text/javascript" | "module"; + jsonpScriptType?: JsonpScriptType; /** - * If set, export the bundle as library. `output.library` is the name. + * Make the output files a library, exporting the exports of the entry point */ - library?: string | string[] | LibraryCustomUmdObject; + library?: Library; /** * Specify which export should be exposed as library */ - libraryExport?: string | ArrayOfStringValues; + libraryExport?: LibraryExport; /** * Type of library */ - libraryTarget?: - | "var" - | "module" - | "assign" - | "this" - | "window" - | "self" - | "global" - | "commonjs" - | "commonjs2" - | "commonjs-module" - | "amd" - | "amd-require" - | "umd" - | "umd2" - | "jsonp" - | "system"; + libraryTarget?: LibraryType; /** * Output javascript files as module source type. */ - module?: boolean; + module?: OutputModule; /** * The output directory as **absolute path** (required). */ - path?: string; + path?: Path; /** * Include comments with information about the modules. */ - pathinfo?: boolean; + pathinfo?: Pathinfo; /** * The `publicPath` specifies the public URL address of the output files when referenced in a browser. */ - publicPath?: - | string - | (( - pathData: import("../lib/Compilation").PathData, - assetInfo?: import("../lib/Compilation").AssetInfo - ) => string); + publicPath?: PublicPath; /** * The filename of the SourceMaps for the JavaScript files. They are inside the `output.path` directory. */ - sourceMapFilename?: string; + sourceMapFilename?: SourceMapFilename; /** * Prefixes every line of the source in the bundle with this string. */ - sourcePrefix?: string; + sourcePrefix?: SourcePrefix; /** * Handles exceptions in module loading correctly at a performance cost. */ - strictModuleExceptionHandling?: boolean; + strictModuleExceptionHandling?: StrictModuleExceptionHandling; /** * If `output.libraryTarget` is set to umd and `output.library` is set, setting this to true will name the AMD module. */ - umdNamedDefine?: boolean; + umdNamedDefine?: UmdNamedDefine; /** * A unique name of the webpack build to avoid multiple webpack runtimes to conflict when using globals. */ - uniqueName?: string; + uniqueName?: UniqueName; /** * The filename of WebAssembly modules as relative path inside the `output.path` directory. */ - webassemblyModuleFilename?: string; + webassemblyModuleFilename?: WebassemblyModuleFilename; } /** * Set explicit comments for `commonjs`, `commonjs2`, `amd`, and `root`. @@ -1497,6 +1653,31 @@ export interface LibraryCustomUmdObject { */ root?: string | ArrayOfStringValues; } +/** + * Options for library + */ +export interface LibraryOptions { + /** + * Add a comment in the UMD wrapper. + */ + auxiliaryComment?: AuxiliaryComment; + /** + * Specify which export should be exposed as library + */ + export?: LibraryExport; + /** + * The name of the library (some types allow unnamed libraries too) + */ + name?: LibraryName; + /** + * Type of library + */ + type: LibraryType; + /** + * If `output.libraryTarget` is set to umd and `output.library` is set, setting this to true will name the AMD module. + */ + umdNamedDefine?: UmdNamedDefine; +} /** * Configuration object for web performance recommendations */ @@ -1784,6 +1965,143 @@ export interface EntryStaticNormalized { */ [k: string]: EntryDescription; } +/** + * Normalized options affecting the output of the compilation. `output` options tell webpack how to write the compiled files to disk. + */ +export interface OutputNormalized { + /** + * The filename of asset modules as relative path inside the `output.path` directory. + */ + assetModuleFilename?: AssetModuleFilename; + /** + * The callback function name used by webpack for loading of chunks in WebWorkers. + */ + chunkCallbackName?: ChunkCallbackName; + /** + * The filename of non-entry chunks as relative path inside the `output.path` directory. + */ + chunkFilename?: ChunkFilename; + /** + * Number of milliseconds before chunk request expires + */ + chunkLoadTimeout?: ChunkLoadTimeout; + /** + * Check if to be emitted file already exists and have the same content before writing to output filesystem + */ + compareBeforeEmit?: CompareBeforeEmit; + /** + * This option enables cross-origin loading of chunks. + */ + crossOriginLoading?: CrossOriginLoading; + /** + * Similar to `output.devtoolModuleFilenameTemplate`, but used in the case of duplicate module identifiers. + */ + devtoolFallbackModuleFilenameTemplate?: DevtoolFallbackModuleFilenameTemplate; + /** + * Filename template string of function for the sources array in a generated SourceMap. + */ + devtoolModuleFilenameTemplate?: DevtoolModuleFilenameTemplate; + /** + * Module namespace to use when interpolating filename template string for the sources array in a generated SourceMap. Defaults to `output.library` if not set. It's useful for avoiding runtime collisions in sourcemaps from multiple webpack projects built as libraries. + */ + devtoolNamespace?: DevtoolNamespace; + /** + * The maximum EcmaScript version of the webpack generated code (doesn't include input source code from modules). + */ + ecmaVersion?: EcmaVersion; + /** + * List of library types enabled for use by entry points + */ + enabledLibraryTypes?: EnabledLibraryTypes; + /** + * Specifies the name of each output file on disk. You must **not** specify an absolute path here! The `output.path` option determines the location on disk the files are written to, filename is used solely for naming the individual files. + */ + filename?: Filename; + /** + * An expression which is used to address the global object/scope in runtime code + */ + globalObject?: GlobalObject; + /** + * Digest type used for the hash + */ + hashDigest?: HashDigest; + /** + * Number of chars which are used for the hash + */ + hashDigestLength?: HashDigestLength; + /** + * Algorithm used for generation the hash (see node.js crypto package) + */ + hashFunction?: HashFunction; + /** + * Any string which is added to the hash to salt it + */ + hashSalt?: HashSalt; + /** + * The filename of the Hot Update Chunks. They are inside the output.path directory. + */ + hotUpdateChunkFilename?: HotUpdateChunkFilename; + /** + * The JSONP function used by webpack for async loading of hot update chunks. + */ + hotUpdateFunction?: HotUpdateFunction; + /** + * The filename of the Hot Update Main File. It is inside the `output.path` directory. + */ + hotUpdateMainFilename?: HotUpdateMainFilename; + /** + * Wrap javascript code into IIFEs to avoid leaking into global scope. + */ + iife?: Iife; + /** + * The JSONP function used by webpack for async loading of chunks. + */ + jsonpFunction?: JsonpFunction; + /** + * This option enables loading async chunks via a custom script type, such as script type="module" + */ + jsonpScriptType?: JsonpScriptType; + /** + * Options for library + */ + library?: LibraryOptions; + /** + * Output javascript files as module source type. + */ + module?: OutputModule; + /** + * The output directory as **absolute path** (required). + */ + path?: Path; + /** + * Include comments with information about the modules. + */ + pathinfo?: Pathinfo; + /** + * The `publicPath` specifies the public URL address of the output files when referenced in a browser. + */ + publicPath?: PublicPath; + /** + * The filename of the SourceMaps for the JavaScript files. They are inside the `output.path` directory. + */ + sourceMapFilename?: SourceMapFilename; + /** + * Prefixes every line of the source in the bundle with this string. + */ + sourcePrefix?: SourcePrefix; + /** + * Handles exceptions in module loading correctly at a performance cost. + */ + strictModuleExceptionHandling?: StrictModuleExceptionHandling; + /** + * A unique name of the webpack build to avoid multiple webpack runtimes to conflict when using globals. + */ + uniqueName?: UniqueName; + /** + * The filename of WebAssembly modules as relative path inside the `output.path` directory. + */ + webassemblyModuleFilename?: WebassemblyModuleFilename; +} /** * Normalized webpack options object */ @@ -1861,9 +2179,9 @@ export interface WebpackOptionsNormalized { */ optimization: Optimization; /** - * Options affecting the output of the compilation. `output` options tell webpack how to write the compiled files to disk. + * Normalized options affecting the output of the compilation. `output` options tell webpack how to write the compiled files to disk. */ - output: Output; + output: OutputNormalized; /** * The number of parallel processed modules in the compilation. */ diff --git a/lib/AmdTemplatePlugin.js b/lib/AmdTemplatePlugin.js deleted file mode 100644 index dc3a17ce0..000000000 --- a/lib/AmdTemplatePlugin.js +++ /dev/null @@ -1,130 +0,0 @@ -/* - 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 RuntimeGlobals = require("./RuntimeGlobals"); -const Template = require("./Template"); -const JavascriptModulesPlugin = require("./javascript/JavascriptModulesPlugin"); - -/** @typedef {import("./Compiler")} Compiler */ - -/** - * @typedef {Object} AmdTemplatePluginOptions - * @param {string=} name the library name - * @property {boolean=} requireAsWrapper - */ - -class AmdTemplatePlugin { - /** - * @param {AmdTemplatePluginOptions} options the plugin options - */ - constructor(options) { - if (!options || typeof options === "string") { - this.name = options; - this.requireAsWrapper = false; - } else { - this.name = options.name; - this.requireAsWrapper = options.requireAsWrapper; - } - } - - /** - * @param {Compiler} compiler the compiler instance - * @returns {void} - */ - apply(compiler) { - compiler.hooks.thisCompilation.tap("AmdTemplatePlugin", compilation => { - compilation.hooks.additionalTreeRuntimeRequirements.tap( - "AmdTemplatePlugin", - (chunk, set) => { - set.add(RuntimeGlobals.returnExportsFromRuntime); - } - ); - - const hooks = JavascriptModulesPlugin.getCompilationHooks(compilation); - - hooks.render.tap( - "AmdTemplatePlugin", - (source, { chunk, runtimeTemplate, chunkGraph }) => { - if (chunkGraph.getNumberOfEntryModules(chunk) === 0) return source; - const modern = runtimeTemplate.supportsArrowFunction(); - const modules = chunkGraph - .getChunkModules(chunk) - .filter(m => m instanceof ExternalModule); - 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 fnStart = modern - ? `(${externalsArguments}) => ` - : `function(${externalsArguments}) { return `; - const fnEnd = modern ? "" : "}"; - - if (this.requireAsWrapper) { - return new ConcatSource( - `require(${externalsDepsArray}, ${fnStart}`, - source, - `${fnEnd});` - ); - } else if (this.name) { - const name = compilation.getPath(this.name, { - chunk - }); - - return new ConcatSource( - `define(${JSON.stringify( - name - )}, ${externalsDepsArray}, ${fnStart}`, - source, - `${fnEnd});` - ); - } else if (externalsArguments) { - return new ConcatSource( - `define(${externalsDepsArray}, ${fnStart}`, - source, - `${fnEnd});` - ); - } else { - return new ConcatSource(`define(${fnStart}`, source, `${fnEnd});`); - } - } - ); - - hooks.chunkHash.tap( - "AmdTemplatePlugin", - (chunk, hash, { chunkGraph }) => { - if (chunkGraph.getNumberOfEntryModules(chunk) === 0) return; - hash.update("exports amd"); - if (this.requireAsWrapper) { - hash.update("requireAsWrapper"); - } else if (this.name) { - const name = compilation.getPath(this.name, { - chunk - }); - hash.update(name); - } - } - ); - }); - } -} - -module.exports = AmdTemplatePlugin; diff --git a/lib/Compilation.js b/lib/Compilation.js index acf382de4..8ae5ffad2 100644 --- a/lib/Compilation.js +++ b/lib/Compilation.js @@ -64,7 +64,7 @@ const createHash = require("./util/createHash"); const { arrayToSetDeprecation } = require("./util/deprecation"); /** @typedef {import("webpack-sources").Source} Source */ -/** @typedef {import("../declarations/WebpackOptions").Output} OutputOptions */ +/** @typedef {import("../declarations/WebpackOptions").OutputNormalized} OutputOptions */ /** @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock */ /** @typedef {import("./ChunkGroup").ChunkGroupOptions} ChunkGroupOptions */ /** @typedef {import("./Compiler")} Compiler */ diff --git a/lib/Compiler.js b/lib/Compiler.js index bf6bc0fef..71b7342e7 100644 --- a/lib/Compiler.js +++ b/lib/Compiler.js @@ -30,7 +30,7 @@ const { makePathsRelative } = require("./util/identifier"); /** @typedef {import("webpack-sources").Source} Source */ /** @typedef {import("../declarations/WebpackOptions").EntryNormalized} Entry */ -/** @typedef {import("../declarations/WebpackOptions").Output} OutputOptions */ +/** @typedef {import("../declarations/WebpackOptions").OutputNormalized} OutputOptions */ /** @typedef {import("../declarations/WebpackOptions").WatchOptions} WatchOptions */ /** @typedef {import("../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions */ /** @typedef {import("../declarations/WebpackOptions").WebpackPluginInstance} WebpackPluginInstance */ diff --git a/lib/ExportPropertyTemplatePlugin.js b/lib/ExportPropertyTemplatePlugin.js deleted file mode 100644 index 1ebf398e7..000000000 --- a/lib/ExportPropertyTemplatePlugin.js +++ /dev/null @@ -1,85 +0,0 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ - -"use strict"; - -const { ConcatSource } = require("webpack-sources"); -const { UsageState } = require("./ModuleGraph"); -const JavascriptModulesPlugin = require("./javascript/JavascriptModulesPlugin"); - -/** @typedef {import("./Compiler")} Compiler */ - -/** - * @param {string[]} accessor the accessor to convert to path - * @returns {string} the path - */ -const accessorToObjectAccess = accessor => { - return accessor.map(a => `[${JSON.stringify(a)}]`).join(""); -}; - -class ExportPropertyTemplatePlugin { - /** - * @param {string|string[]} property the name of the property to export - * @param {string} explanation an explanation why this property is used - */ - constructor(property, explanation) { - this.property = property; - this.explanation = explanation; - } - - /** - * @param {Compiler} compiler the compiler instance - * @returns {void} - */ - apply(compiler) { - compiler.hooks.thisCompilation.tap( - "ExportPropertyTemplatePlugin", - compilation => { - const moduleGraph = compilation.moduleGraph; - compilation.hooks.seal.tap("ExportPropertyTemplatePlugin", () => { - for (const { dependencies: deps } of compilation.entries.values()) { - const dep = deps[deps.length - 1]; - if (dep) { - const module = moduleGraph.getModule(dep); - if (module) { - const exportsInfo = moduleGraph.getExportInfo( - module, - Array.isArray(this.property) - ? this.property[0] - : this.property - ); - exportsInfo.used = UsageState.Used; - exportsInfo.canMangleUse = false; - moduleGraph.addExtraReason(module, this.explanation); - } - } - } - }); - - const hooks = JavascriptModulesPlugin.getCompilationHooks(compilation); - - hooks.render.tap( - "ExportPropertyTemplatePlugin", - (source, { chunk, chunkGraph }) => { - if (chunkGraph.getNumberOfEntryModules(chunk) === 0) return source; - const postfix = accessorToObjectAccess([].concat(this.property)); - return new ConcatSource(source, postfix); - } - ); - - hooks.chunkHash.tap( - "ExportPropertyTemplatePlugin", - (chunk, hash, { chunkGraph }) => { - if (chunkGraph.getNumberOfEntryModules(chunk) === 0) return; - hash.update("export property"); - hash.update(`${this.property}`); - } - ); - } - ); - } -} - -module.exports = ExportPropertyTemplatePlugin; diff --git a/lib/ExternalModule.js b/lib/ExternalModule.js index 8fa254e9a..434d12fa5 100644 --- a/lib/ExternalModule.js +++ b/lib/ExternalModule.js @@ -235,6 +235,8 @@ class ExternalModule extends Module { case "module": throw new Error("Module external type is not implemented yet"); case "var": + case "const": + case "let": case "assign": default: return getSourceForDefaultCase( diff --git a/lib/LibraryTemplatePlugin.js b/lib/LibraryTemplatePlugin.js index b60bd721d..73ba926cf 100644 --- a/lib/LibraryTemplatePlugin.js +++ b/lib/LibraryTemplatePlugin.js @@ -5,75 +5,32 @@ "use strict"; -const SetVarTemplatePlugin = require("./SetVarTemplatePlugin"); +const EnableLibraryPlugin = require("./library/EnableLibraryPlugin"); -/** @typedef {import("../declarations/WebpackOptions").LibraryCustomUmdCommentObject} LibraryCustomUmdCommentObject */ -/** @typedef {import("../declarations/WebpackOptions").LibraryCustomUmdObject} LibraryCustomUmdObject */ +/** @typedef {import("../declarations/WebpackOptions").AuxiliaryComment} AuxiliaryComment */ +/** @typedef {import("../declarations/WebpackOptions").LibraryExport} LibraryExport */ +/** @typedef {import("../declarations/WebpackOptions").LibraryName} LibraryName */ +/** @typedef {import("../declarations/WebpackOptions").LibraryType} LibraryType */ +/** @typedef {import("../declarations/WebpackOptions").UmdNamedDefine} UmdNamedDefine */ /** @typedef {import("./Compiler")} Compiler */ -/** - * @param {string[]} accessor the accessor to convert to path - * @returns {string} the path - */ -const accessorToObjectAccess = accessor => { - return accessor.map(a => `[${JSON.stringify(a)}]`).join(""); -}; - -/** - * @param {string=} base the path prefix - * @param {string|string[]|LibraryCustomUmdObject} accessor the accessor - * @param {"amd" | "commonjs" | "root"} umdProperty property used when a custom umd object is provided - * @param {string=} joinWith the element separator - * @returns {string} the path - */ -const accessorAccess = (base, accessor, umdProperty, joinWith = "; ") => { - const normalizedAccessor = - typeof accessor === "object" && !Array.isArray(accessor) - ? accessor[umdProperty] - : accessor; - const accessors = Array.isArray(normalizedAccessor) - ? normalizedAccessor - : [normalizedAccessor]; - 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 {Object} LibraryOptions - * @property {string|string[]|LibraryCustomUmdObject=} library name of library - * @property {string=} libraryTarget type of library - * @property {boolean=} umdNamedDefine setting this to true will name the UMD module - * @property {string|LibraryCustomUmdCommentObject=} auxiliaryComment comment in the UMD wrapper - * @property {string|string[]=} libraryExport which export should be exposed as library - * @property {string=} globalObject global object expression - */ - +// TODO webpack 6 remove class LibraryTemplatePlugin { /** - * @param {LibraryOptions} options options + * @param {LibraryName} name name of library + * @param {LibraryType} target type of library + * @param {UmdNamedDefine} umdNamedDefine setting this to true will name the UMD module + * @param {AuxiliaryComment} auxiliaryComment comment in the UMD wrapper + * @param {LibraryExport} exportProperty which export should be exposed as library */ - constructor(options) { - if (arguments.length > 1) { - options = { - library: arguments[0], - libraryTarget: arguments[1], - umdNamedDefine: arguments[2], - auxiliaryComment: arguments[3], - libraryExport: arguments[4], - globalObject: null - }; - } - this.options = options; + constructor(name, target, umdNamedDefine, auxiliaryComment, exportProperty) { + this.library = { + type: target || "var", + name, + umdNamedDefine, + auxiliaryComment, + export: exportProperty + }; } /** @@ -81,130 +38,9 @@ class LibraryTemplatePlugin { * @returns {void} */ apply(compiler) { - if (this.options.libraryExport) { - const ExportPropertyTemplatePlugin = require("./ExportPropertyTemplatePlugin"); - new ExportPropertyTemplatePlugin( - this.options.libraryExport, - "used a library export" - ).apply(compiler); - } else { - const FlagEntryExportAsUsedPlugin = require("./FlagEntryExportAsUsedPlugin"); - new FlagEntryExportAsUsedPlugin( - this.options.libraryTarget !== "module", - "used a library export" - ).apply(compiler); - } - switch (this.options.libraryTarget) { - case "umd": - case "umd2": { - const UmdTemplatePlugin = require("./UmdTemplatePlugin"); - new UmdTemplatePlugin(this.options.library, { - optionalAmdExternalAsGlobal: this.options.libraryTarget === "umd2", - namedDefine: this.options.umdNamedDefine, - auxiliaryComment: this.options.auxiliaryComment || "" - }).apply(compiler); - break; - } - case "amd": - case "amd-require": { - const AmdTemplatePlugin = require("./AmdTemplatePlugin"); - new AmdTemplatePlugin({ - name: this.options.library, - requireAsWrapper: this.options.libraryTarget === "amd-require" - }).apply(compiler); - break; - } - case "var": - if ( - !this.options.library || - (typeof this.options.library === "object" && - !Array.isArray(this.options.library)) - ) { - throw new Error( - "library name must be set and not an UMD custom object for non-UMD target" - ); - } - new SetVarTemplatePlugin( - `var ${accessorAccess(undefined, this.options.library, "root")}`, - false - ).apply(compiler); - break; - case "assign": - new SetVarTemplatePlugin( - accessorAccess(undefined, this.options.library, "root"), - false - ).apply(compiler); - break; - case "this": - case "self": - case "window": - if (this.options.library) { - new SetVarTemplatePlugin( - accessorAccess( - this.options.libraryTarget, - this.options.library, - "root" - ), - false - ).apply(compiler); - } else { - new SetVarTemplatePlugin(this.options.libraryTarget, true).apply( - compiler - ); - } - break; - case "global": { - const globalObject = - this.options.globalObject || compiler.options.output.globalObject; - if (this.options.library) { - new SetVarTemplatePlugin( - accessorAccess(globalObject, this.options.library, "root"), - false - ).apply(compiler); - } else { - new SetVarTemplatePlugin(globalObject, true).apply(compiler); - } - break; - } - case "commonjs": - if (this.options.library) { - new SetVarTemplatePlugin( - accessorAccess("exports", this.options.library, "commonjs"), - false - ).apply(compiler); - } else { - new SetVarTemplatePlugin("exports", true).apply(compiler); - } - break; - case "commonjs2": - case "commonjs-module": - new SetVarTemplatePlugin("module.exports", false).apply(compiler); - break; - case "jsonp": { - if (!this.options.library || typeof this.options.library === "object") { - throw new Error( - "library name must be set and not an array or UMD custom object for non-UMD target" - ); - } - const JsonpExportTemplatePlugin = require("./web/JsonpExportTemplatePlugin"); - new JsonpExportTemplatePlugin(this.options.library).apply(compiler); - break; - } - case "system": { - const SystemTemplatePlugin = require("./SystemTemplatePlugin"); - new SystemTemplatePlugin({ - name: this.options.library - }).apply(compiler); - break; - } - case "module": - // TODO - break; - default: - throw new Error( - `${this.options.libraryTarget} is not a valid Library target` - ); - } + const { output } = compiler.options; + output.library = this.library; + new EnableLibraryPlugin(this.library.type).apply(compiler); } } diff --git a/lib/SetVarTemplatePlugin.js b/lib/SetVarTemplatePlugin.js deleted file mode 100644 index dc2cd85e6..000000000 --- a/lib/SetVarTemplatePlugin.js +++ /dev/null @@ -1,77 +0,0 @@ -/* - 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 JavascriptModulesPlugin = require("./javascript/JavascriptModulesPlugin"); - -/** @typedef {import("./Compiler")} Compiler */ - -class SetVarTemplatePlugin { - /** - * @param {string} varExpression the accessor where the library is exported - * @param {boolean} copyObject specify copying the exports - */ - constructor(varExpression, copyObject) { - /** @type {string} */ - this.varExpression = varExpression; - /** @type {boolean} */ - this.copyObject = copyObject; - } - - /** - * @param {Compiler} compiler the compiler instance - * @returns {void} - */ - apply(compiler) { - compiler.hooks.thisCompilation.tap("SetVarTemplatePlugin", compilation => { - compilation.hooks.additionalTreeRuntimeRequirements.tap( - "LibraryTemplatePlugin", - (chunk, set) => { - set.add(RuntimeGlobals.returnExportsFromRuntime); - } - ); - - const hooks = JavascriptModulesPlugin.getCompilationHooks(compilation); - - hooks.render.tap( - "SetVarTemplatePlugin", - (source, { chunk, chunkGraph }) => { - if (chunkGraph.getNumberOfEntryModules(chunk) === 0) return source; - const varExpression = compilation.getPath(this.varExpression, { - chunk - }); - if (this.copyObject) { - return new ConcatSource( - `(function(e, a) { for(var i in a) e[i] = a[i]; }(${varExpression}, `, - source, - "))" - ); - } else { - const prefix = `${varExpression} =\n`; - return new ConcatSource(prefix, source); - } - } - ); - - hooks.chunkHash.tap( - "SetVarTemplatePlugin", - (chunk, hash, { chunkGraph }) => { - if (chunkGraph.getNumberOfEntryModules(chunk) === 0) return; - hash.update("set var"); - const varExpression = compilation.getPath(this.varExpression, { - chunk - }); - hash.update(`${varExpression}`); - hash.update(`${this.copyObject}`); - } - ); - }); - } -} - -module.exports = SetVarTemplatePlugin; diff --git a/lib/SystemTemplatePlugin.js b/lib/SystemTemplatePlugin.js deleted file mode 100644 index 8b42df14a..000000000 --- a/lib/SystemTemplatePlugin.js +++ /dev/null @@ -1,145 +0,0 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Joel Denning @joeldenning -*/ - -"use strict"; - -const { ConcatSource } = require("webpack-sources"); -const ExternalModule = require("./ExternalModule"); -const RuntimeGlobals = require("./RuntimeGlobals"); -const Template = require("./Template"); -const JavascriptModulesPlugin = require("./javascript/JavascriptModulesPlugin"); - -/** @typedef {import("./Compiler")} Compiler */ - -/** - * @typedef {Object} SystemTemplatePluginOptions - * @param {string=} name the library name - */ - -class SystemTemplatePlugin { - /** - * @param {SystemTemplatePluginOptions} options the plugin options - */ - constructor(options) { - this.name = options.name; - } - - /** - * @param {Compiler} compiler the compiler instance - * @returns {void} - */ - apply(compiler) { - compiler.hooks.thisCompilation.tap("SystemTemplatePlugin", compilation => { - compilation.hooks.additionalTreeRuntimeRequirements.tap( - "SystemTemplatePlugin", - (chunk, set) => { - set.add(RuntimeGlobals.returnExportsFromRuntime); - } - ); - - const hooks = JavascriptModulesPlugin.getCompilationHooks(compilation); - - hooks.render.tap( - "SystemTemplatePlugin", - (source, { chunk, chunkGraph }) => { - if (chunkGraph.getNumberOfEntryModules(chunk) === 0) return source; - - const modules = chunkGraph - .getChunkModules(chunk) - .filter(m => m instanceof ExternalModule); - const externals = /** @type {ExternalModule[]} */ (modules); - - // The name this bundle should be registered as with System - const name = this.name - ? `${JSON.stringify(compilation.getPath(this.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.length > 0 - ? `var ${externalWebpackNames.join(", ")};` - : ""; - - // The system.register format requires an array of setter functions for externals. - const setters = - externalWebpackNames.length === 0 - ? "" - : Template.asString([ - "setters: [", - Template.indent( - externalWebpackNames - .map(external => - Template.asString([ - "function(module) {", - Template.indent(`${external} = module;`), - "}" - ]) - ) - .join(",\n") - ), - "]," - ]); - - return new ConcatSource( - Template.asString([ - `System.register(${name}${systemDependencies}, function(${dynamicExport}) {`, - Template.indent([ - externalVarDeclarations, - "return {", - Template.indent([ - setters, - "execute: function() {", - Template.indent(`${dynamicExport}(`) - ]) - ]) - ]) + "\n", - source, - "\n" + - Template.asString([ - Template.indent([ - Template.indent([Template.indent([");"]), "}"]), - "};" - ]), - "})" - ]) - ); - } - ); - - hooks.chunkHash.tap( - "SystemTemplatePlugin", - (chunk, hash, { chunkGraph }) => { - if (chunkGraph.getNumberOfEntryModules(chunk) === 0) return; - hash.update("exports system"); - if (this.name) { - hash.update(compilation.getPath(this.name, { chunk })); - } - } - ); - }); - } -} - -module.exports = SystemTemplatePlugin; diff --git a/lib/UmdTemplatePlugin.js b/lib/UmdTemplatePlugin.js deleted file mode 100644 index ade3dc0ad..000000000 --- a/lib/UmdTemplatePlugin.js +++ /dev/null @@ -1,315 +0,0 @@ -/* - 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 RuntimeGlobals = require("./RuntimeGlobals"); -const Template = require("./Template"); -const JavascriptModulesPlugin = require("./javascript/JavascriptModulesPlugin"); - -/** @typedef {import("webpack-sources").Source} Source */ -/** @typedef {import("../declarations/WebpackOptions").LibraryCustomUmdCommentObject} LibraryCustomUmdCommentObject */ -/** @typedef {import("../declarations/WebpackOptions").LibraryCustomUmdObject} LibraryCustomUmdObject */ -/** @typedef {import("./Chunk")} Chunk */ -/** @typedef {import("./Compiler")} Compiler */ - -/** - * @param {string[]} accessor the accessor to convert to path - * @returns {string} the path - */ -const accessorToObjectAccess = accessor => { - return accessor.map(a => `[${JSON.stringify(a)}]`).join(""); -}; - -/** - * @param {string=} 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} UmdTemplatePluginName */ - -/** - * @typedef {Object} UmdTemplatePluginOption - * @property {boolean=} optionalAmdExternalAsGlobal - * @property {boolean} namedDefine - * @property {string | LibraryCustomUmdCommentObject} auxiliaryComment - */ - -class UmdTemplatePlugin { - /** - * @param {UmdTemplatePluginName} name the name of the UMD library - * @param {UmdTemplatePluginOption} options the plugin option - */ - constructor(name, options) { - if (typeof name === "object" && !Array.isArray(name)) { - this.name = name.root || name.amd || name.commonjs; - this.names = name; - } else { - this.name = name; - this.names = { - commonjs: name, - root: name, - amd: name - }; - } - this.optionalAmdExternalAsGlobal = options.optionalAmdExternalAsGlobal; - this.namedDefine = options.namedDefine; - this.auxiliaryComment = options.auxiliaryComment; - } - - /** - * @param {Compiler} compiler the compiler instance - * @returns {void} - */ - apply(compiler) { - compiler.hooks.thisCompilation.tap("UmdTemplatePlugin", compilation => { - compilation.hooks.additionalTreeRuntimeRequirements.tap( - "UmdTemplatePlugin", - (chunk, set) => { - set.add(RuntimeGlobals.returnExportsFromRuntime); - } - ); - - const hooks = JavascriptModulesPlugin.getCompilationHooks(compilation); - - hooks.render.tap( - "UmdTemplatePlugin", - (source, { chunk, moduleGraph, chunkGraph, runtimeTemplate }) => { - if (chunkGraph.getNumberOfEntryModules(chunk) === 0) return source; - - 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; - } - - const replaceKeys = str => { - return compilation.getPath(str, { - chunk - }); - }; - - const externalsDepsArray = modules => { - return `[${replaceKeys( - modules - .map(m => - JSON.stringify( - typeof m.request === "object" ? m.request.amd : m.request - ) - ) - .join(", ") - )}]`; - }; - - const externalsRootArray = modules => { - return replaceKeys( - modules - .map(m => { - let request = m.request; - if (typeof request === "object") request = request.root; - return `root${accessorToObjectAccess([].concat(request))}`; - }) - .join(", ") - ); - }; - - const externalsRequireArray = type => { - return replaceKeys( - externals - .map(m => { - let expr; - let request = m.request; - if (typeof request === "object") { - request = request[type]; - } - if (request === undefined) { - throw new Error( - "Missing external configuration for type:" + type - ); - } - if (Array.isArray(request)) { - expr = `require(${JSON.stringify( - request[0] - )})${accessorToObjectAccess(request.slice(1))}`; - } else { - expr = `require(${JSON.stringify(request)})`; - } - if (m.isOptional(moduleGraph)) { - expr = `(function webpackLoadOptionalExternalModule() { try { return ${expr}; } catch(e) {} }())`; - } - return expr; - }) - .join(", ") - ); - }; - - const externalsArguments = modules => { - return modules - .map( - m => - `__WEBPACK_EXTERNAL_MODULE_${Template.toIdentifier( - `${chunkGraph.getModuleId(m)}` - )}__` - ) - .join(", "); - }; - - const libraryName = library => { - return JSON.stringify(replaceKeys([].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 = this.auxiliaryComment; - - const getAuxilaryComment = 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" + - getAuxilaryComment("commonjs2") + - " if(typeof exports === 'object' && typeof module === 'object')\n" + - " module.exports = factory(" + - externalsRequireArray("commonjs2") + - ");\n" + - getAuxilaryComment("amd") + - " else if(typeof define === 'function' && define.amd)\n" + - (requiredExternals.length > 0 - ? this.names.amd && this.namedDefine === true - ? " define(" + - libraryName(this.names.amd) + - ", " + - externalsDepsArray(requiredExternals) + - ", " + - amdFactory + - ");\n" - : " define(" + - externalsDepsArray(requiredExternals) + - ", " + - amdFactory + - ");\n" - : this.names.amd && this.namedDefine === true - ? " define(" + - libraryName(this.names.amd) + - ", [], " + - amdFactory + - ");\n" - : " define([], " + amdFactory + ");\n") + - (this.names.root || this.names.commonjs - ? getAuxilaryComment("commonjs") + - " else if(typeof exports === 'object')\n" + - " exports[" + - libraryName(this.names.commonjs || this.names.root) + - "] = factory(" + - externalsRequireArray("commonjs") + - ");\n" + - getAuxilaryComment("root") + - " else\n" + - " " + - replaceKeys( - accessorAccess( - "root", - this.names.root || this.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 - }, function(${externalsArguments(externals)}) {\nreturn `, - "webpack/universalModuleDefinition" - ), - source, - ";\n})" - ); - } - ); - - hooks.chunkHash.tap( - "UmdTemplatePlugin", - (chunk, hash, { chunkGraph }) => { - if (chunkGraph.getNumberOfEntryModules(chunk) === 0) return; - hash.update("umd"); - hash.update(`${this.names.root}`); - hash.update(`${this.names.amd}`); - hash.update(`${this.names.commonjs}`); - } - ); - }); - } -} - -module.exports = UmdTemplatePlugin; diff --git a/lib/WebpackOptionsApply.js b/lib/WebpackOptionsApply.js index 71b7236e0..06fca3dd2 100644 --- a/lib/WebpackOptionsApply.js +++ b/lib/WebpackOptionsApply.js @@ -221,9 +221,11 @@ class WebpackOptionsApply extends OptionsApply { options.target(compiler); } - if (options.output.library || options.output.libraryTarget !== "var") { - const LibraryTemplatePlugin = require("./LibraryTemplatePlugin"); - new LibraryTemplatePlugin(options.output).apply(compiler); + if (options.output.enabledLibraryTypes.length > 0) { + for (const type of options.output.enabledLibraryTypes) { + const EnableLibraryPlugin = require("./library/EnableLibraryPlugin"); + new EnableLibraryPlugin(type).apply(compiler); + } } if (options.externals) { const ExternalsPlugin = require("./ExternalsPlugin"); @@ -277,9 +279,9 @@ class WebpackOptionsApply extends OptionsApply { "'output.module: true' is only allowed when 'experiments.outputModule' is enabled" ); } - if (options.output.libraryTarget === "module") { + if (options.output.enabledLibraryTypes.includes("module")) { throw new Error( - "'output.libraryTarget: \"module\"' is only allowed when 'experiments.outputModule' is enabled" + "library type \"module\" is only allowed when 'experiments.outputModule' is enabled" ); } if (options.externalsType === "module") { diff --git a/lib/config/defaults.js b/lib/config/defaults.js index 43f91e639..aeb768087 100644 --- a/lib/config/defaults.js +++ b/lib/config/defaults.js @@ -11,6 +11,9 @@ const Template = require("../Template"); /** @typedef {import("../../declarations/WebpackOptions").CacheNormalized} WebpackCache */ /** @typedef {import("../../declarations/WebpackOptions").Experiments} Experiments */ /** @typedef {import("../../declarations/WebpackOptions").InfrastructureLogging} InfrastructureLogging */ +/** @typedef {import("../../declarations/WebpackOptions").Library} Library */ +/** @typedef {import("../../declarations/WebpackOptions").LibraryName} LibraryName */ +/** @typedef {import("../../declarations/WebpackOptions").LibraryOptions} LibraryOptions */ /** @typedef {import("../../declarations/WebpackOptions").Module} WebpackModule */ /** @typedef {import("../../declarations/WebpackOptions").Node} WebpackNode */ /** @typedef {import("../../declarations/WebpackOptions").Optimization} Optimization */ @@ -118,7 +121,17 @@ const applyWebpackOptionsDefaults = options => { development }); - D(options, "externalsType", options.output.libraryTarget); + if (options.output.library) { + options.output.enabledLibraryTypes.push(options.output.library.type); + } + + F(options, "externalsType", () => + options.output.library + ? options.output.library.type + : options.output.module + ? "module" + : "var" + ); applyNodeDefaults(options.node, { target }); @@ -323,14 +336,26 @@ const applyOutputDefaults = ( output, { context, target, outputModule, development } ) => { + /** + * @param {Library=} library the library option + * @returns {string} a readable library name + */ const getLibraryName = library => { - // if options.output.library is a string - if (Array.isArray(library)) { - return library.join("."); - } else if (typeof library === "object") { - return getLibraryName(library.root); + const libraryName = + typeof library === "object" && + library && + !Array.isArray(library) && + "type" in library + ? library.name + : /** @type {LibraryName=} */ (library); + if (Array.isArray(libraryName)) { + return libraryName.join("."); + } else if (typeof libraryName === "object") { + return getLibraryName(libraryName.root); + } else if (typeof libraryName === "string") { + return libraryName; } - return library || ""; + return ""; }; F(output, "uniqueName", () => { @@ -364,7 +389,6 @@ const applyOutputDefaults = ( }); D(output, "assetModuleFilename", "[hash][ext]"); D(output, "webassemblyModuleFilename", "[hash].module.wasm"); - D(output, "library", ""); D(output, "publicPath", ""); D(output, "compareBeforeEmit", true); F(output, "hotUpdateFunction", () => diff --git a/lib/config/normalization.js b/lib/config/normalization.js index 9e5ee4238..151b158e8 100644 --- a/lib/config/normalization.js +++ b/lib/config/normalization.js @@ -9,8 +9,11 @@ const normalizeEcmaVersion = require("../util/normalizeEcmaVersion"); /** @typedef {import("../../declarations/WebpackOptions").EntryStatic} EntryStatic */ /** @typedef {import("../../declarations/WebpackOptions").EntryStaticNormalized} EntryStaticNormalized */ +/** @typedef {import("../../declarations/WebpackOptions").LibraryName} LibraryName */ +/** @typedef {import("../../declarations/WebpackOptions").LibraryOptions} LibraryOptions */ /** @typedef {import("../../declarations/WebpackOptions").OptimizationRuntimeChunk} OptimizationRuntimeChunk */ /** @typedef {import("../../declarations/WebpackOptions").OptimizationRuntimeChunkNormalized} OptimizationRuntimeChunkNormalized */ +/** @typedef {import("../../declarations/WebpackOptions").OutputNormalized} OutputNormalized */ /** @typedef {import("../../declarations/WebpackOptions").WebpackOptions} WebpackOptions */ /** @typedef {import("../../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptionsNormalized */ @@ -160,10 +163,80 @@ const getNormalizedWebpackOptions = config => { } ) })), - output: nestedConfig(config.output, output => ({ - ...output, - ecmaVersion: normalizeEcmaVersion(output.ecmaVersion) - })), + output: nestedConfig(config.output, output => { + const library = output.library; + const libraryAsName = /** @type {LibraryName} */ (library); + const libraryBase = + typeof library === "object" && + library && + !Array.isArray(library) && + "type" in library + ? library + : libraryAsName || output.libraryTarget + ? /** @type {LibraryOptions} */ ({ + type: "var", + name: libraryAsName + }) + : undefined; + /** @type {OutputNormalized} */ + const result = { + assetModuleFilename: output.assetModuleFilename, + chunkCallbackName: output.chunkCallbackName, + chunkFilename: output.chunkFilename, + chunkLoadTimeout: output.chunkLoadTimeout, + compareBeforeEmit: output.compareBeforeEmit, + crossOriginLoading: output.crossOriginLoading, + devtoolFallbackModuleFilenameTemplate: + output.devtoolFallbackModuleFilenameTemplate, + devtoolModuleFilenameTemplate: output.devtoolModuleFilenameTemplate, + devtoolNamespace: output.devtoolNamespace, + ecmaVersion: normalizeEcmaVersion(output.ecmaVersion), + enabledLibraryTypes: nestedArray(output.enabledLibraryTypes, t => [ + ...t + ]), + filename: output.filename, + globalObject: output.globalObject, + hashDigest: output.hashDigest, + hashDigestLength: output.hashDigestLength, + hashFunction: output.hashFunction, + hashSalt: output.hashSalt, + hotUpdateChunkFilename: output.hotUpdateChunkFilename, + hotUpdateFunction: output.hotUpdateFunction, + hotUpdateMainFilename: output.hotUpdateMainFilename, + iife: output.iife, + jsonpFunction: output.jsonpFunction, + jsonpScriptType: output.jsonpScriptType, + library: libraryBase && { + type: + output.libraryTarget !== undefined + ? output.libraryTarget + : libraryBase.type, + auxiliaryComment: + output.auxiliaryComment !== undefined + ? output.auxiliaryComment + : libraryBase.auxiliaryComment, + export: + output.libraryExport !== undefined + ? output.libraryExport + : libraryBase.export, + name: libraryBase.name, + umdNamedDefine: + output.umdNamedDefine !== undefined + ? output.umdNamedDefine + : libraryBase.umdNamedDefine + }, + module: output.module, + path: output.path, + pathinfo: output.pathinfo, + publicPath: output.publicPath, + sourceMapFilename: output.sourceMapFilename, + sourcePrefix: output.sourcePrefix, + strictModuleExceptionHandling: output.strictModuleExceptionHandling, + uniqueName: output.uniqueName, + webassemblyModuleFilename: output.webassemblyModuleFilename + }; + return result; + }), parallelism: config.parallelism, performance: optionalNestedConfig(config.performance, performance => { if (performance === false) return false; diff --git a/lib/index.js b/lib/index.js index 30cd40577..d426611ff 100644 --- a/lib/index.js +++ b/lib/index.js @@ -57,7 +57,11 @@ exportPlugins(module.exports, { "DEP_WEBPACK_JAVASCRIPT_MODULES_PLUGIN" ), LibManifestPlugin: () => require("./LibManifestPlugin"), - LibraryTemplatePlugin: () => require("./LibraryTemplatePlugin"), + LibraryTemplatePlugin: util.deprecate( + () => require("./LibraryTemplatePlugin"), + "webpack.LibraryTemplatePlugin is deprecated and has been replaced by compilation.outputOptions.library or compilation.addEntry + passing a library option", + "DEP_WEBPACK_LIBRARY_TEMPLATE_PLUGIN" + ), LoaderOptionsPlugin: () => require("./LoaderOptionsPlugin"), LoaderTargetPlugin: () => require("./LoaderTargetPlugin"), Module: () => require("./Module"), @@ -78,11 +82,9 @@ exportPlugins(module.exports, { "SingleEntryPlugin was renamed to EntryPlugin", "DEP_WEBPACK_SINGLE_ENTRY_PLUGIN" ), - SetVarTemplatePlugin: () => require("./SetVarTemplatePlugin"), SourceMapDevToolPlugin: () => require("./SourceMapDevToolPlugin"), Stats: () => require("./Stats"), Template: () => require("./Template"), - UmdTemplatePlugin: () => require("./UmdTemplatePlugin"), WatchIgnorePlugin: () => require("./WatchIgnorePlugin"), WebpackOptionsDefaulter: util.deprecate( () => require("./WebpackOptionsDefaulter"), @@ -157,6 +159,11 @@ exportPlugins((module.exports.wasm = {}), { require("./wasm-async/AsyncWebAssemblyModulesPlugin") }); +exportPlugins((module.exports.library = {}), { + AbstractLibraryPlugin: () => require("./library/AbstractLibraryPlugin"), + EnableLibraryPlugin: () => require("./library/EnableLibraryPlugin") +}); + exportPlugins((module.exports.debug = {}), { ProfilingPlugin: () => require("./debug/ProfilingPlugin") }); diff --git a/lib/library/AbstractLibraryPlugin.js b/lib/library/AbstractLibraryPlugin.js new file mode 100644 index 000000000..6b03ccd24 --- /dev/null +++ b/lib/library/AbstractLibraryPlugin.js @@ -0,0 +1,173 @@ +/* + 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("../Compilation")} Compilation */ +/** @typedef {import("../Compilation").ChunkHashContext} ChunkHashContext */ +/** @typedef {import("../Compiler")} Compiler */ +/** @typedef {import("../Module")} Module */ +/** @typedef {import("../javascript/JavascriptModulesPlugin").RenderContext} RenderContext */ +/** @typedef {import("../util/Hash")} Hash */ + +/** + * @template T + * @typedef {Object} LibraryContext + * @property {Compilation} compilation + * @property {T} options + */ + +/** + * @template T + */ +class AbstractLibraryPlugin { + /** + * @param {Object} options options + * @param {string} options.pluginName name of the plugin + * @param {LibraryType} options.type used library type + */ + constructor({ pluginName, type }) { + this._pluginName = pluginName; + this._type = type; + this._parseCache = new WeakMap(); + } + + /** + * @param {Compiler} compiler the compiler instance + * @returns {void} + */ + apply(compiler) { + const { _pluginName } = this; + compiler.hooks.thisCompilation.tap(_pluginName, compilation => { + compilation.hooks.finishModules.tap(_pluginName, () => { + const options = this._parseOptionsCached( + compilation.outputOptions.library + ); + if (options !== false) { + for (const { dependencies: deps } of compilation.entries.values()) { + const dep = deps[deps.length - 1]; + if (dep) { + const module = compilation.moduleGraph.getModule(dep); + if (module) { + this.finishEntryModule(module, { options, compilation }); + } + } + } + } + }); + + compilation.hooks.additionalChunkRuntimeRequirements.tap( + _pluginName, + (chunk, set) => { + if (compilation.chunkGraph.getNumberOfEntryModules(chunk) === 0) + return; + const options = this._parseOptionsCached( + compilation.outputOptions.library + ); + if (options !== false) { + this.runtimeRequirements(chunk, set, { options, compilation }); + } + } + ); + + const hooks = JavascriptModulesPlugin.getCompilationHooks(compilation); + + hooks.render.tap(_pluginName, (source, renderContext) => { + const { chunk, chunkGraph } = renderContext; + if (chunkGraph.getNumberOfEntryModules(chunk) === 0) return source; + const options = this._parseOptionsCached( + compilation.outputOptions.library + ); + if (options === false) return source; + return this.render(source, renderContext, { options, compilation }); + }); + + hooks.chunkHash.tap(_pluginName, (chunk, hash, context) => { + const { chunkGraph } = context; + if (chunkGraph.getNumberOfEntryModules(chunk) === 0) return; + const options = this._parseOptionsCached( + compilation.outputOptions.library + ); + if (options === false) return; + this.chunkHash(chunk, hash, context, { options, compilation }); + }); + }); + } + + /** + * @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; + } + + /** + * @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 {LibraryContext} libraryContext context + * @returns {void} + */ + finishEntryModule(module, libraryContext) {} + + /** + * @param {Chunk} chunk the chunk + * @param {Set} set runtime requirements + * @param {LibraryContext} libraryContext context + * @returns {void} + */ + runtimeRequirements(chunk, set, libraryContext) { + set.add(RuntimeGlobals.returnExportsFromRuntime); + } + + /** + * @param {Source} source source + * @param {RenderContext} renderContext render context + * @param {LibraryContext} libraryContext context + * @returns {Source} source with library export + */ + render(source, renderContext, libraryContext) { + return source; + } + + /** + * @param {Chunk} chunk the chunk + * @param {Hash} hash hash + * @param {ChunkHashContext} chunkHashContext chunk hash context + * @param {LibraryContext} 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)); + } +} + +module.exports = AbstractLibraryPlugin; diff --git a/lib/library/AmdLibraryPlugin.js b/lib/library/AmdLibraryPlugin.js new file mode 100644 index 000000000..2b64675cc --- /dev/null +++ b/lib/library/AmdLibraryPlugin.js @@ -0,0 +1,155 @@ +/* + 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} LibraryContext */ + +/** + * @typedef {Object} AmdLibraryPluginOptions + * @property {LibraryType} type + * @property {boolean=} requireAsWrapper + */ + +/** + * @typedef {Object} AmdLibraryPluginParsed + * @property {string} name + */ + +/** + * @typedef {AmdLibraryPluginParsed} T + * @extends {AbstractLibraryPlugin} + */ +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 = library.name; + if (this.requireAsWrapper) { + if (name) { + throw new Error("AMD library name must be unset"); + } + } else { + if (name && typeof name !== "string") { + throw new Error("AMD library name must be a simple string or unset"); + } + } + return { + name: /** @type {string=} */ (name) + }; + } + + /** + * @param {Source} source source + * @param {RenderContext} renderContext render context + * @param {LibraryContext} 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); + 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 fnStart = modern + ? `(${externalsArguments}) => ` + : `function(${externalsArguments}) { return `; + const fnEnd = modern ? "" : "}"; + + if (this.requireAsWrapper) { + return new ConcatSource( + `require(${externalsDepsArray}, ${fnStart}`, + source, + `${fnEnd});` + ); + } else if (options.name) { + const name = compilation.getPath(options.name, { + chunk + }); + + return new ConcatSource( + `define(${JSON.stringify(name)}, ${externalsDepsArray}, ${fnStart}`, + source, + `${fnEnd});` + ); + } else if (externalsArguments) { + return new ConcatSource( + `define(${externalsDepsArray}, ${fnStart}`, + source, + `${fnEnd});` + ); + } else { + return new ConcatSource(`define(${fnStart}`, source, `${fnEnd});`); + } + } + + /** + * @param {Chunk} chunk the chunk + * @param {Hash} hash hash + * @param {ChunkHashContext} chunkHashContext chunk hash context + * @param {LibraryContext} 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); + } + } +} + +module.exports = AmdLibraryPlugin; diff --git a/lib/library/AssignLibraryPlugin.js b/lib/library/AssignLibraryPlugin.js new file mode 100644 index 000000000..9a67c60c5 --- /dev/null +++ b/lib/library/AssignLibraryPlugin.js @@ -0,0 +1,170 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ + +"use strict"; + +const { ConcatSource } = require("webpack-sources"); +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} LibraryContext */ + +/** + * @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]; + let current = + existingLength > 0 || (!initLast && accessor.length === 1) + ? base + : `(${base} = typeof ${base} === "undefined" ? {} : ${base})`; + let propsSoFar = accessor.slice(1, existingLength); + current += propertyAccess(propsSoFar); + if (existingLength >= accessor.length - 1) return current; + for (let i = Math.max(1, existingLength); i < accessor.length - 1; i++) { + const prop = accessor[i]; + propsSoFar.push(prop); + current = `(${current}${propertyAccess([prop])} = ${base}${propertyAccess( + propsSoFar + )} || {})`; + } + return `${current}${propertyAccess([accessor[accessor.length - 1]])}`; +}; + +/** + * @typedef {Object} AssignLibraryPluginOptions + * @property {LibraryType} type + * @property {string[] | "global"} prefix name prefix + * @property {string | false} declare declare name as variable + * @property {"error"|"copy"|"assign"} unnamed behavior for unnamed library name + */ + +/** + * @typedef {Object} AssignLibraryPluginParsed + * @property {string | string[]} name + */ + +/** + * @typedef {AssignLibraryPluginParsed} T + * @extends {AbstractLibraryPlugin} + */ +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; + } + + /** + * @param {LibraryOptions} library normalized library option + * @returns {T | false} preprocess as needed by overriding + */ + parseOptions(library) { + const name = library.name; + if (this.unnamed === "error") { + if (typeof name !== "string" && !Array.isArray(name)) { + throw new Error("Library name must be a string or string array"); + } + } else { + if (name && typeof name !== "string" && !Array.isArray(name)) { + throw new Error("Library name must be a string, string array or unset"); + } + } + return { + name: /** @type {string|string[]=} */ (name) + }; + } + + /** + * @param {Source} source source + * @param {RenderContext} renderContext render context + * @param {LibraryContext} libraryContext context + * @returns {Source} source with library export + */ + render(source, { chunkGraph, moduleGraph, chunk }, { options, compilation }) { + const prefix = + this.prefix === "global" + ? [compilation.outputOptions.globalObject] + : this.prefix; + const fullName = options.name ? prefix.concat(options.name) : prefix; + const fullNameResolved = fullName.map(n => + compilation.getPath(n, { + chunk + }) + ); + const result = new ConcatSource(); + if (this.declare) { + const base = fullNameResolved[0]; + result.add(`${this.declare} ${base};`); + } + if (!options.name && this.unnamed === "copy") { + result.add( + `(function(e, a) { for(var i in a) e[i] = a[i]; if(a.__esModule) Object.defineProperty(e, "__esModule", { value: true }); }(${accessWithInit( + fullNameResolved, + prefix.length, + true + )},\n` + ); + result.add(source); + result.add("\n))"); + } else { + result.add( + `${accessWithInit(fullNameResolved, prefix.length, false)} =\n` + ); + result.add(source); + } + return result; + } + + /** + * @param {Chunk} chunk the chunk + * @param {Hash} hash hash + * @param {ChunkHashContext} chunkHashContext chunk hash context + * @param {LibraryContext} libraryContext context + * @returns {void} + */ + chunkHash(chunk, hash, chunkHashContext, { options, compilation }) { + hash.update("AssignLibraryPlugin"); + const prefix = + this.prefix === "global" + ? [compilation.outputOptions.globalObject] + : this.prefix; + const fullName = options.name ? prefix.concat(options.name) : prefix; + const fullNameResolved = fullName.map(n => + compilation.getPath(n, { + chunk + }) + ); + if (!options.name && this.unnamed === "copy") { + hash.update("copy"); + } + if (this.declare) { + hash.update(this.declare); + } + hash.update(fullNameResolved.join(".")); + } +} + +module.exports = AssignLibraryPlugin; diff --git a/lib/library/EnableLibraryPlugin.js b/lib/library/EnableLibraryPlugin.js new file mode 100644 index 000000000..d63063a45 --- /dev/null +++ b/lib/library/EnableLibraryPlugin.js @@ -0,0 +1,175 @@ +/* + 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>} */ +const enabledTypes = new WeakMap(); + +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 + */ + constructor(type) { + this.type = type; + } + + /** + * @param {Compiler} compiler the compiler instance + * @returns {void} + */ + apply(compiler) { + const { type } = this; + + // Only enable once + if (getEnabledTypes(compiler).has(type)) return; + + if (typeof type === "string") { + const ExportPropertyTemplatePlugin = require("./ExportPropertyLibraryPlugin"); + new ExportPropertyTemplatePlugin({ + type, + nsObjectUsed: type !== "module" + }).apply(compiler); + switch (type) { + case "var": { + const AssignLibraryPlugin = require("./AssignLibraryPlugin"); + new AssignLibraryPlugin({ + type, + prefix: [], + declare: "var", + unnamed: "error" + }).apply(compiler); + break; + } + case "assign": { + const AssignLibraryPlugin = require("./AssignLibraryPlugin"); + new AssignLibraryPlugin({ + type, + prefix: [], + declare: false, + unnamed: "error" + }).apply(compiler); + break; + } + case "this": { + const AssignLibraryPlugin = require("./AssignLibraryPlugin"); + new AssignLibraryPlugin({ + type, + prefix: ["this"], + declare: false, + unnamed: "copy" + }).apply(compiler); + break; + } + case "window": { + const AssignLibraryPlugin = require("./AssignLibraryPlugin"); + new AssignLibraryPlugin({ + type, + prefix: ["window"], + declare: false, + unnamed: "copy" + }).apply(compiler); + break; + } + case "self": { + const AssignLibraryPlugin = require("./AssignLibraryPlugin"); + new AssignLibraryPlugin({ + type, + prefix: ["self"], + declare: false, + unnamed: "copy" + }).apply(compiler); + break; + } + case "global": { + const AssignLibraryPlugin = require("./AssignLibraryPlugin"); + new AssignLibraryPlugin({ + type, + prefix: "global", + declare: false, + unnamed: "copy" + }).apply(compiler); + break; + } + case "commonjs": { + const AssignLibraryPlugin = require("./AssignLibraryPlugin"); + new AssignLibraryPlugin({ + type, + prefix: ["exports"], + declare: false, + unnamed: "copy" + }).apply(compiler); + break; + } + case "commonjs2": + case "commonjs-module": { + const AssignLibraryPlugin = require("./AssignLibraryPlugin"); + new AssignLibraryPlugin({ + type, + prefix: ["module", "exports"], + declare: false, + unnamed: "assign" + }).apply(compiler); + break; + } + case "amd": + case "amd-require": { + const AmdLibraryPlugin = require("./AmdLibraryPlugin"); + new AmdLibraryPlugin({ + type, + requireAsWrapper: type === "amd-require" + }).apply(compiler); + break; + } + case "umd": + case "umd2": { + const UmdLibraryPlugin = require("./UmdLibraryPlugin"); + new UmdLibraryPlugin({ + type, + optionalAmdExternalAsGlobal: type === "umd2" + }).apply(compiler); + break; + } + case "system": { + const SystemLibraryPlugin = require("./SystemLibraryPlugin"); + new SystemLibraryPlugin({ + type + }).apply(compiler); + break; + } + case "jsonp": { + const JsonpLibraryPlugin = require("./JsonpLibraryPlugin"); + new JsonpLibraryPlugin({ + type + }).apply(compiler); + break; + } + case "module": + // TODO implement module library + break; + default: + throw new Error(`Unsupported library type ${type}`); + } + } else { + // TODO support plugin instances here + // apply them to the compiler + } + } +} + +module.exports = EnableLibraryPlugin; diff --git a/lib/library/ExportPropertyLibraryPlugin.js b/lib/library/ExportPropertyLibraryPlugin.js new file mode 100644 index 000000000..87f89dcfa --- /dev/null +++ b/lib/library/ExportPropertyLibraryPlugin.js @@ -0,0 +1,95 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ + +"use strict"; + +const { ConcatSource } = require("webpack-sources"); +const { UsageState } = require("../ModuleGraph"); +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("../Compiler")} Compiler */ +/** @typedef {import("../Module")} Module */ +/** @typedef {import("../javascript/JavascriptModulesPlugin").RenderContext} RenderContext */ +/** @template T @typedef {import("./AbstractLibraryPlugin").LibraryContext} LibraryContext */ + +/** + * @typedef {Object} ExportPropertyLibraryPluginParsed + * @property {string | string[]} export + */ + +/** + * @typedef {Object} ExportPropertyLibraryPluginOptions + * @property {LibraryType} type + * @property {boolean} nsObjectUsed the namespace object is used + */ +/** + * @typedef {ExportPropertyLibraryPluginParsed} T + * @extends {AbstractLibraryPlugin} + */ +class ExportPropertyLibraryPlugin extends AbstractLibraryPlugin { + /** + * @param {ExportPropertyLibraryPluginOptions} options options + */ + constructor({ type, nsObjectUsed }) { + super({ + pluginName: "ExportPropertyLibraryPlugin", + type + }); + this.nsObjectUsed = nsObjectUsed; + } + + /** + * @param {LibraryOptions} library normalized library option + * @returns {T | false} preprocess as needed by overriding + */ + parseOptions(library) { + return { + export: library.export + }; + } + + /** + * @param {Module} module the exporting entry module + * @param {LibraryContext} libraryContext context + * @returns {void} + */ + finishEntryModule(module, { options, compilation: { moduleGraph } }) { + if (options.export) { + const exportsInfo = moduleGraph.getExportInfo( + module, + Array.isArray(options.export) ? options.export[0] : options.export + ); + exportsInfo.used = UsageState.Used; + exportsInfo.canMangleUse = false; + } else { + const exportsInfo = moduleGraph.getExportsInfo(module); + if (this.nsObjectUsed) { + exportsInfo.setUsedInUnknownWay(); + } else { + exportsInfo.setAllKnownExportsUsed(); + } + } + moduleGraph.addExtraReason(module, "used as library export"); + } + /** + * @param {Source} source source + * @param {RenderContext} renderContext render context + * @param {LibraryContext} libraryContext context + * @returns {Source} source with library export + */ + render(source, renderContext, { options }) { + if (!options.export) return source; + const postfix = propertyAccess( + Array.isArray(options.export) ? options.export : [options.export] + ); + return new ConcatSource(source, postfix); + } +} + +module.exports = ExportPropertyLibraryPlugin; diff --git a/lib/library/JsonpLibraryPlugin.js b/lib/library/JsonpLibraryPlugin.js new file mode 100644 index 000000000..de99d2e36 --- /dev/null +++ b/lib/library/JsonpLibraryPlugin.js @@ -0,0 +1,86 @@ +/* + 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} LibraryContext */ + +/** + * @typedef {Object} JsonpLibraryPluginOptions + * @property {LibraryType} type + */ + +/** + * @typedef {Object} JsonpLibraryPluginParsed + * @property {string} name + */ + +/** + * @typedef {JsonpLibraryPluginParsed} T + * @extends {AbstractLibraryPlugin} + */ +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.name; + if (typeof name !== "string") { + throw new Error("Jsonp library name must be a simple string"); + } + return { + name: /** @type {string} */ (name) + }; + } + + /** + * @param {Source} source source + * @param {RenderContext} renderContext render context + * @param {LibraryContext} 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} libraryContext context + * @returns {void} + */ + chunkHash(chunk, hash, chunkHashContext, { options, compilation }) { + hash.update("JsonpLibraryPlugin"); + hash.update(compilation.getPath(options.name, { chunk })); + } +} + +module.exports = JsonpLibraryPlugin; diff --git a/lib/library/SystemLibraryPlugin.js b/lib/library/SystemLibraryPlugin.js new file mode 100644 index 000000000..19cbe19db --- /dev/null +++ b/lib/library/SystemLibraryPlugin.js @@ -0,0 +1,167 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Joel Denning @joeldenning +*/ + +"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} LibraryContext */ + +/** + * @typedef {Object} SystemLibraryPluginOptions + * @property {LibraryType} type + */ + +/** + * @typedef {Object} SystemLibraryPluginParsed + * @property {string} name + */ + +/** + * @typedef {SystemLibraryPluginParsed} T + * @extends {AbstractLibraryPlugin} + */ +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.name; + if (name && typeof name !== "string") { + throw new Error( + "System.js library name must be a simple string or unset" + ); + } + return { + name: /** @type {string=} */ (name) + }; + } + + /** + * @param {Source} source source + * @param {RenderContext} renderContext render context + * @param {LibraryContext} libraryContext context + * @returns {Source} source with library export + */ + render(source, { chunkGraph, chunk }, { options, compilation }) { + const modules = chunkGraph + .getChunkModules(chunk) + .filter(m => m instanceof ExternalModule); + 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.length > 0 + ? `var ${externalWebpackNames.join(", ")};` + : ""; + + // The system.register format requires an array of setter functions for externals. + const setters = + externalWebpackNames.length === 0 + ? "" + : Template.asString([ + "setters: [", + Template.indent( + externalWebpackNames + .map(external => + Template.asString([ + "function(module) {", + Template.indent(`${external} = module;`), + "}" + ]) + ) + .join(",\n") + ), + "]," + ]); + + return new ConcatSource( + Template.asString([ + `System.register(${name}${systemDependencies}, function(${dynamicExport}) {`, + Template.indent([ + externalVarDeclarations, + "return {", + Template.indent([ + setters, + "execute: function() {", + Template.indent(`${dynamicExport}(`) + ]) + ]) + ]) + "\n", + source, + "\n" + + 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} 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; diff --git a/lib/library/UmdLibraryPlugin.js b/lib/library/UmdLibraryPlugin.js new file mode 100644 index 000000000..c028f1308 --- /dev/null +++ b/lib/library/UmdLibraryPlugin.js @@ -0,0 +1,324 @@ +/* + 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("../util/Hash")} Hash */ +/** @template T @typedef {import("./AbstractLibraryPlugin").LibraryContext} LibraryContext */ + +/** + * @param {string[]} accessor the accessor to convert to path + * @returns {string} the path + */ +const accessorToObjectAccess = accessor => { + return accessor.map(a => `[${JSON.stringify(a)}]`).join(""); +}; + +/** + * @param {string=} 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[]} name + * @property {LibraryCustomUmdObject} names + * @property {string | LibraryCustomUmdCommentObject} auxiliaryComment + * @property {boolean} namedDefine + */ + +/** + * @typedef {UmdLibraryPluginParsed} T + * @extends {AbstractLibraryPlugin} + */ +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} */ + 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} 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; + } + + const replaceKeys = str => { + return compilation.getPath(str, { + chunk + }); + }; + + const externalsDepsArray = modules => { + return `[${replaceKeys( + modules + .map(m => + JSON.stringify( + typeof m.request === "object" ? m.request.amd : m.request + ) + ) + .join(", ") + )}]`; + }; + + const externalsRootArray = modules => { + return replaceKeys( + modules + .map(m => { + let request = m.request; + if (typeof request === "object") request = request.root; + return `root${accessorToObjectAccess([].concat(request))}`; + }) + .join(", ") + ); + }; + + const externalsRequireArray = type => { + return replaceKeys( + externals + .map(m => { + let expr; + let request = m.request; + if (typeof request === "object") { + request = request[type]; + } + if (request === undefined) { + throw new Error( + "Missing external configuration for type:" + type + ); + } + if (Array.isArray(request)) { + expr = `require(${JSON.stringify( + request[0] + )})${accessorToObjectAccess(request.slice(1))}`; + } else { + expr = `require(${JSON.stringify(request)})`; + } + if (m.isOptional(moduleGraph)) { + expr = `(function webpackLoadOptionalExternalModule() { try { return ${expr}; } catch(e) {} }())`; + } + return expr; + }) + .join(", ") + ); + }; + + const externalsArguments = modules => { + return modules + .map( + m => + `__WEBPACK_EXTERNAL_MODULE_${Template.toIdentifier( + `${chunkGraph.getModuleId(m)}` + )}__` + ) + .join(", "); + }; + + const libraryName = library => { + return JSON.stringify(replaceKeys([].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; + + const getAuxilaryComment = 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" + + getAuxilaryComment("commonjs2") + + " if(typeof exports === 'object' && typeof module === 'object')\n" + + " module.exports = factory(" + + externalsRequireArray("commonjs2") + + ");\n" + + getAuxilaryComment("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 + ? getAuxilaryComment("commonjs") + + " else if(typeof exports === 'object')\n" + + " exports[" + + libraryName(names.commonjs || names.root) + + "] = factory(" + + externalsRequireArray("commonjs") + + ");\n" + + getAuxilaryComment("root") + + " else\n" + + " " + + replaceKeys( + accessorAccess("root", 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 + }, function(${externalsArguments(externals)}) {\nreturn `, + "webpack/universalModuleDefinition" + ), + source, + ";\n})" + ); + } +} + +module.exports = UmdLibraryPlugin; diff --git a/lib/web/JsonpExportTemplatePlugin.js b/lib/web/JsonpExportTemplatePlugin.js deleted file mode 100644 index 6f523488d..000000000 --- a/lib/web/JsonpExportTemplatePlugin.js +++ /dev/null @@ -1,66 +0,0 @@ -/* - 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 JavascriptModulesPlugin = require("../javascript/JavascriptModulesPlugin"); - -/** @typedef {import("../Compiler")} Compiler */ - -class JsonpExportTemplatePlugin { - /** - * @param {string} name jsonp function name - */ - constructor(name) { - this.name = name; - } - - /** - * @param {Compiler} compiler the compiler instance - * @returns {void} - */ - apply(compiler) { - compiler.hooks.thisCompilation.tap( - "JsonpExportTemplatePlugin", - compilation => { - compilation.hooks.additionalTreeRuntimeRequirements.tap( - "JsonpExportTemplatePlugin", - (chunk, set) => { - set.add(RuntimeGlobals.returnExportsFromRuntime); - } - ); - - const hooks = JavascriptModulesPlugin.getCompilationHooks(compilation); - - hooks.render.tap( - "JsonpExportTemplatePlugin", - (source, { chunk, chunkGraph }) => { - if (chunkGraph.getNumberOfEntryModules(chunk) === 0) return source; - const name = compilation.getPath(this.name || "", { - chunk - }); - return new ConcatSource(`${name}(`, source, ");"); - } - ); - - hooks.chunkHash.tap( - "JsonpExportTemplatePlugin", - (chunk, hash, { chunkGraph }) => { - if (chunkGraph.getNumberOfEntryModules(chunk) === 0) return; - hash.update("jsonp export"); - const name = compilation.getPath(this.name || "", { - chunk - }); - hash.update(`${name}`); - } - ); - } - ); - } -} - -module.exports = JsonpExportTemplatePlugin; diff --git a/schemas/WebpackOptions.json b/schemas/WebpackOptions.json index 5d32768f6..741e5dd07 100644 --- a/schemas/WebpackOptions.json +++ b/schemas/WebpackOptions.json @@ -43,6 +43,31 @@ "minLength": 1 } }, + "AssetModuleFilename": { + "description": "The filename of asset modules as relative path inside the `output.path` directory.", + "anyOf": [ + { + "type": "string", + "absolutePath": false + }, + { + "instanceof": "Function", + "tsType": "((pathData: import(\"../lib/Compilation\").PathData, assetInfo?: import(\"../lib/Compilation\").AssetInfo) => string)" + } + ] + }, + "AuxiliaryComment": { + "description": "Add a comment in the UMD wrapper.", + "anyOf": [ + { + "description": "Append the same comment above each import style.", + "type": "string" + }, + { + "$ref": "#/definitions/LibraryCustomUmdCommentObject" + } + ] + }, "Bail": { "description": "Report the first error as a hard error instead of tolerating it.", "type": "boolean" @@ -84,11 +109,32 @@ } ] }, + "ChunkCallbackName": { + "description": "The callback function name used by webpack for loading of chunks in WebWorkers.", + "type": "string" + }, + "ChunkFilename": { + "description": "The filename of non-entry chunks as relative path inside the `output.path` directory.", + "type": "string", + "absolutePath": false + }, + "ChunkLoadTimeout": { + "description": "Number of milliseconds before chunk request expires", + "type": "number" + }, + "CompareBeforeEmit": { + "description": "Check if to be emitted file already exists and have the same content before writing to output filesystem", + "type": "boolean" + }, "Context": { "description": "The base directory (absolute path!) for resolving the `entry` option. If `output.pathinfo` is set, the included pathinfo is shortened to this directory.", "type": "string", "absolutePath": true }, + "CrossOriginLoading": { + "description": "This option enables cross-origin loading of chunks.", + "enum": [false, "anonymous", "use-credentials"] + }, "Dependencies": { "description": "References to other configurations to depend on.", "type": "array", @@ -113,6 +159,59 @@ } ] }, + "DevtoolFallbackModuleFilenameTemplate": { + "description": "Similar to `output.devtoolModuleFilenameTemplate`, but used in the case of duplicate module identifiers.", + "anyOf": [ + { + "type": "string" + }, + { + "instanceof": "Function", + "tsType": "Function" + } + ] + }, + "DevtoolModuleFilenameTemplate": { + "description": "Filename template string of function for the sources array in a generated SourceMap.", + "anyOf": [ + { + "type": "string" + }, + { + "instanceof": "Function", + "tsType": "Function" + } + ] + }, + "DevtoolNamespace": { + "description": "Module namespace to use when interpolating filename template string for the sources array in a generated SourceMap. Defaults to `output.library` if not set. It's useful for avoiding runtime collisions in sourcemaps from multiple webpack projects built as libraries.", + "type": "string" + }, + "EcmaVersion": { + "description": "The maximum EcmaScript version of the webpack generated code (doesn't include input source code from modules).", + "anyOf": [ + { + "type": "number", + "minimum": 5, + "maximum": 11 + }, + { + "enum": [2009] + }, + { + "type": "number", + "minimum": 2015, + "maximum": 2020 + } + ] + }, + "EnabledLibraryTypes": { + "description": "List of library types enabled for use by entry points", + "type": "array", + "items": { + "$ref": "#/definitions/LibraryType" + } + }, "Entry": { "description": "The entry point(s) of the compilation.", "anyOf": [ @@ -465,6 +564,20 @@ }, "required": ["type"] }, + "Filename": { + "description": "Specifies the name of each output file on disk. You must **not** specify an absolute path here! The `output.path` option determines the location on disk the files are written to, filename is used solely for naming the individual files.", + "anyOf": [ + { + "type": "string", + "absolutePath": false, + "minLength": 1 + }, + { + "instanceof": "Function", + "tsType": "((pathData: import(\"../lib/Compilation\").PathData, assetInfo?: import(\"../lib/Compilation\").AssetInfo) => string)" + } + ] + }, "FilterItemTypes": { "description": "Filtering value, regexp or function", "anyOf": [ @@ -500,6 +613,56 @@ } ] }, + "GlobalObject": { + "description": "An expression which is used to address the global object/scope in runtime code", + "type": "string", + "minLength": 1 + }, + "HashDigest": { + "description": "Digest type used for the hash", + "type": "string" + }, + "HashDigestLength": { + "description": "Number of chars which are used for the hash", + "type": "number", + "minimum": 1 + }, + "HashFunction": { + "description": "Algorithm used for generation the hash (see node.js crypto package)", + "anyOf": [ + { + "type": "string", + "minLength": 1 + }, + { + "instanceof": "Function", + "tsType": "typeof import('../lib/util/Hash')" + } + ] + }, + "HashSalt": { + "description": "Any string which is added to the hash to salt it", + "type": "string", + "minLength": 1 + }, + "HotUpdateChunkFilename": { + "description": "The filename of the Hot Update Chunks. They are inside the output.path directory.", + "type": "string", + "absolutePath": false + }, + "HotUpdateFunction": { + "description": "The JSONP function used by webpack for async loading of hot update chunks.", + "type": "string" + }, + "HotUpdateMainFilename": { + "description": "The filename of the Hot Update Main File. It is inside the `output.path` directory.", + "type": "string", + "absolutePath": false + }, + "Iife": { + "description": "Wrap javascript code into IIFEs to avoid leaking into global scope.", + "type": "boolean" + }, "InfrastructureLogging": { "description": "Options for infrastructure level logging", "type": "object", @@ -523,6 +686,25 @@ } } }, + "JsonpFunction": { + "description": "The JSONP function used by webpack for async loading of chunks.", + "type": "string" + }, + "JsonpScriptType": { + "description": "This option enables loading async chunks via a custom script type, such as script type=\"module\"", + "enum": [false, "text/javascript", "module"] + }, + "Library": { + "description": "Make the output files a library, exporting the exports of the entry point", + "anyOf": [ + { + "$ref": "#/definitions/LibraryName" + }, + { + "$ref": "#/definitions/LibraryOptions" + } + ] + }, "LibraryCustomUmdCommentObject": { "description": "Set explicit comments for `commonjs`, `commonjs2`, `amd`, and `root`.", "type": "object", @@ -572,6 +754,79 @@ } } }, + "LibraryExport": { + "description": "Specify which export should be exposed as library", + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/ArrayOfStringValues" + } + ] + }, + "LibraryName": { + "description": "The name of the library (some types allow unnamed libraries too)", + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "description": "A part of the library name", + "type": "string" + } + }, + { + "$ref": "#/definitions/LibraryCustomUmdObject" + } + ] + }, + "LibraryOptions": { + "description": "Options for library", + "type": "object", + "additionalProperties": false, + "properties": { + "auxiliaryComment": { + "$ref": "#/definitions/AuxiliaryComment" + }, + "export": { + "$ref": "#/definitions/LibraryExport" + }, + "name": { + "$ref": "#/definitions/LibraryName" + }, + "type": { + "$ref": "#/definitions/LibraryType" + }, + "umdNamedDefine": { + "$ref": "#/definitions/UmdNamedDefine" + } + }, + "required": ["type"] + }, + "LibraryType": { + "description": "Type of library", + "enum": [ + "var", + "module", + "assign", + "this", + "window", + "self", + "global", + "commonjs", + "commonjs2", + "commonjs-module", + "amd", + "amd-require", + "umd", + "umd2", + "jsonp", + "system" + ] + }, "Loader": { "description": "Custom values available in the loader context.", "type": "object" @@ -1356,270 +1611,225 @@ "additionalProperties": false, "properties": { "assetModuleFilename": { - "description": "The filename of asset modules as relative path inside the `output.path` directory.", - "anyOf": [ - { - "type": "string", - "absolutePath": false - }, - { - "instanceof": "Function", - "tsType": "((pathData: import(\"../lib/Compilation\").PathData, assetInfo?: import(\"../lib/Compilation\").AssetInfo) => string)" - } - ] + "$ref": "#/definitions/AssetModuleFilename" }, "auxiliaryComment": { - "description": "Add a comment in the UMD wrapper.", - "anyOf": [ - { - "description": "Append the same comment above each import style.", - "type": "string" - }, - { - "$ref": "#/definitions/LibraryCustomUmdCommentObject" - } - ] + "$ref": "#/definitions/AuxiliaryComment" }, "chunkCallbackName": { - "description": "The callback function name used by webpack for loading of chunks in WebWorkers.", - "type": "string" + "$ref": "#/definitions/ChunkCallbackName" }, "chunkFilename": { - "description": "The filename of non-entry chunks as relative path inside the `output.path` directory.", - "type": "string", - "absolutePath": false + "$ref": "#/definitions/ChunkFilename" }, "chunkLoadTimeout": { - "description": "Number of milliseconds before chunk request expires", - "type": "number" + "$ref": "#/definitions/ChunkLoadTimeout" }, "compareBeforeEmit": { - "description": "Check if to be emitted file already exists and have the same content before writing to output filesystem", - "type": "boolean" + "$ref": "#/definitions/CompareBeforeEmit" }, "crossOriginLoading": { - "description": "This option enables cross-origin loading of chunks.", - "enum": [false, "anonymous", "use-credentials"] + "$ref": "#/definitions/CrossOriginLoading" }, "devtoolFallbackModuleFilenameTemplate": { - "description": "Similar to `output.devtoolModuleFilenameTemplate`, but used in the case of duplicate module identifiers.", - "anyOf": [ - { - "type": "string" - }, - { - "instanceof": "Function", - "tsType": "Function" - } - ] + "$ref": "#/definitions/DevtoolFallbackModuleFilenameTemplate" }, "devtoolModuleFilenameTemplate": { - "description": "Filename template string of function for the sources array in a generated SourceMap.", - "anyOf": [ - { - "type": "string" - }, - { - "instanceof": "Function", - "tsType": "Function" - } - ] + "$ref": "#/definitions/DevtoolModuleFilenameTemplate" }, "devtoolNamespace": { - "description": "Module namespace to use when interpolating filename template string for the sources array in a generated SourceMap. Defaults to `output.library` if not set. It's useful for avoiding runtime collisions in sourcemaps from multiple webpack projects built as libraries.", - "type": "string" + "$ref": "#/definitions/DevtoolNamespace" }, "ecmaVersion": { - "description": "The maximum EcmaScript version of the webpack generated code (doesn't include input source code from modules).", - "anyOf": [ - { - "type": "number", - "minimum": 5, - "maximum": 11 - }, - { - "enum": [2009] - }, - { - "type": "number", - "minimum": 2015, - "maximum": 2020 - } - ] + "$ref": "#/definitions/EcmaVersion" + }, + "enabledLibraryTypes": { + "$ref": "#/definitions/EnabledLibraryTypes" }, "filename": { - "description": "Specifies the name of each output file on disk. You must **not** specify an absolute path here! The `output.path` option determines the location on disk the files are written to, filename is used solely for naming the individual files.", - "anyOf": [ - { - "type": "string", - "absolutePath": false, - "minLength": 1 - }, - { - "instanceof": "Function", - "tsType": "((pathData: import(\"../lib/Compilation\").PathData, assetInfo?: import(\"../lib/Compilation\").AssetInfo) => string)" - } - ] + "$ref": "#/definitions/Filename" }, "globalObject": { - "description": "An expression which is used to address the global object/scope in runtime code", - "type": "string", - "minLength": 1 + "$ref": "#/definitions/GlobalObject" }, "hashDigest": { - "description": "Digest type used for the hash", - "type": "string" + "$ref": "#/definitions/HashDigest" }, "hashDigestLength": { - "description": "Number of chars which are used for the hash", - "type": "number", - "minimum": 1 + "$ref": "#/definitions/HashDigestLength" }, "hashFunction": { - "description": "Algorithm used for generation the hash (see node.js crypto package)", - "anyOf": [ - { - "type": "string", - "minLength": 1 - }, - { - "instanceof": "Function", - "tsType": "typeof import('../lib/util/Hash')" - } - ] + "$ref": "#/definitions/HashFunction" }, "hashSalt": { - "description": "Any string which is added to the hash to salt it", - "type": "string", - "minLength": 1 + "$ref": "#/definitions/HashSalt" }, "hotUpdateChunkFilename": { - "description": "The filename of the Hot Update Chunks. They are inside the output.path directory.", - "type": "string", - "absolutePath": false + "$ref": "#/definitions/HotUpdateChunkFilename" }, "hotUpdateFunction": { - "description": "The JSONP function used by webpack for async loading of hot update chunks.", - "type": "string" + "$ref": "#/definitions/HotUpdateFunction" }, "hotUpdateMainFilename": { - "description": "The filename of the Hot Update Main File. It is inside the `output.path` directory.", - "type": "string", - "absolutePath": false + "$ref": "#/definitions/HotUpdateMainFilename" }, "iife": { - "description": "Wrap javascript code into IIFEs to avoid leaking into global scope.", - "type": "boolean" + "$ref": "#/definitions/Iife" }, "jsonpFunction": { - "description": "The JSONP function used by webpack for async loading of chunks.", - "type": "string" + "$ref": "#/definitions/JsonpFunction" }, "jsonpScriptType": { - "description": "This option enables loading async chunks via a custom script type, such as script type=\"module\"", - "enum": [false, "text/javascript", "module"] + "$ref": "#/definitions/JsonpScriptType" }, "library": { - "description": "If set, export the bundle as library. `output.library` is the name.", - "anyOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "description": "A part of the library name", - "type": "string" - } - }, - { - "$ref": "#/definitions/LibraryCustomUmdObject" - } - ] + "$ref": "#/definitions/Library" }, "libraryExport": { - "description": "Specify which export should be exposed as library", - "anyOf": [ - { - "type": "string" - }, - { - "$ref": "#/definitions/ArrayOfStringValues" - } - ] + "$ref": "#/definitions/LibraryExport" }, "libraryTarget": { - "description": "Type of library", - "enum": [ - "var", - "module", - "assign", - "this", - "window", - "self", - "global", - "commonjs", - "commonjs2", - "commonjs-module", - "amd", - "amd-require", - "umd", - "umd2", - "jsonp", - "system" - ] + "$ref": "#/definitions/LibraryType" }, "module": { - "description": "Output javascript files as module source type.", - "type": "boolean" + "$ref": "#/definitions/OutputModule" }, "path": { - "description": "The output directory as **absolute path** (required).", - "type": "string", - "absolutePath": true + "$ref": "#/definitions/Path" }, "pathinfo": { - "description": "Include comments with information about the modules.", - "type": "boolean" + "$ref": "#/definitions/Pathinfo" }, "publicPath": { - "description": "The `publicPath` specifies the public URL address of the output files when referenced in a browser.", - "anyOf": [ - { - "type": "string" - }, - { - "instanceof": "Function", - "tsType": "((pathData: import(\"../lib/Compilation\").PathData, assetInfo?: import(\"../lib/Compilation\").AssetInfo) => string)" - } - ] + "$ref": "#/definitions/PublicPath" }, "sourceMapFilename": { - "description": "The filename of the SourceMaps for the JavaScript files. They are inside the `output.path` directory.", - "type": "string", - "absolutePath": false + "$ref": "#/definitions/SourceMapFilename" }, "sourcePrefix": { - "description": "Prefixes every line of the source in the bundle with this string.", - "type": "string" + "$ref": "#/definitions/SourcePrefix" }, "strictModuleExceptionHandling": { - "description": "Handles exceptions in module loading correctly at a performance cost.", - "type": "boolean" + "$ref": "#/definitions/StrictModuleExceptionHandling" }, "umdNamedDefine": { - "description": "If `output.libraryTarget` is set to umd and `output.library` is set, setting this to true will name the AMD module.", - "type": "boolean" + "$ref": "#/definitions/UmdNamedDefine" }, "uniqueName": { - "description": "A unique name of the webpack build to avoid multiple webpack runtimes to conflict when using globals.", - "type": "string", - "minLength": 1 + "$ref": "#/definitions/UniqueName" }, "webassemblyModuleFilename": { - "description": "The filename of WebAssembly modules as relative path inside the `output.path` directory.", - "type": "string", - "absolutePath": false + "$ref": "#/definitions/WebassemblyModuleFilename" + } + } + }, + "OutputModule": { + "description": "Output javascript files as module source type.", + "type": "boolean" + }, + "OutputNormalized": { + "description": "Normalized options affecting the output of the compilation. `output` options tell webpack how to write the compiled files to disk.", + "type": "object", + "additionalProperties": false, + "properties": { + "assetModuleFilename": { + "$ref": "#/definitions/AssetModuleFilename" + }, + "chunkCallbackName": { + "$ref": "#/definitions/ChunkCallbackName" + }, + "chunkFilename": { + "$ref": "#/definitions/ChunkFilename" + }, + "chunkLoadTimeout": { + "$ref": "#/definitions/ChunkLoadTimeout" + }, + "compareBeforeEmit": { + "$ref": "#/definitions/CompareBeforeEmit" + }, + "crossOriginLoading": { + "$ref": "#/definitions/CrossOriginLoading" + }, + "devtoolFallbackModuleFilenameTemplate": { + "$ref": "#/definitions/DevtoolFallbackModuleFilenameTemplate" + }, + "devtoolModuleFilenameTemplate": { + "$ref": "#/definitions/DevtoolModuleFilenameTemplate" + }, + "devtoolNamespace": { + "$ref": "#/definitions/DevtoolNamespace" + }, + "ecmaVersion": { + "$ref": "#/definitions/EcmaVersion" + }, + "enabledLibraryTypes": { + "$ref": "#/definitions/EnabledLibraryTypes" + }, + "filename": { + "$ref": "#/definitions/Filename" + }, + "globalObject": { + "$ref": "#/definitions/GlobalObject" + }, + "hashDigest": { + "$ref": "#/definitions/HashDigest" + }, + "hashDigestLength": { + "$ref": "#/definitions/HashDigestLength" + }, + "hashFunction": { + "$ref": "#/definitions/HashFunction" + }, + "hashSalt": { + "$ref": "#/definitions/HashSalt" + }, + "hotUpdateChunkFilename": { + "$ref": "#/definitions/HotUpdateChunkFilename" + }, + "hotUpdateFunction": { + "$ref": "#/definitions/HotUpdateFunction" + }, + "hotUpdateMainFilename": { + "$ref": "#/definitions/HotUpdateMainFilename" + }, + "iife": { + "$ref": "#/definitions/Iife" + }, + "jsonpFunction": { + "$ref": "#/definitions/JsonpFunction" + }, + "jsonpScriptType": { + "$ref": "#/definitions/JsonpScriptType" + }, + "library": { + "$ref": "#/definitions/LibraryOptions" + }, + "module": { + "$ref": "#/definitions/OutputModule" + }, + "path": { + "$ref": "#/definitions/Path" + }, + "pathinfo": { + "$ref": "#/definitions/Pathinfo" + }, + "publicPath": { + "$ref": "#/definitions/PublicPath" + }, + "sourceMapFilename": { + "$ref": "#/definitions/SourceMapFilename" + }, + "sourcePrefix": { + "$ref": "#/definitions/SourcePrefix" + }, + "strictModuleExceptionHandling": { + "$ref": "#/definitions/StrictModuleExceptionHandling" + }, + "uniqueName": { + "$ref": "#/definitions/UniqueName" + }, + "webassemblyModuleFilename": { + "$ref": "#/definitions/WebassemblyModuleFilename" } } }, @@ -1628,6 +1838,15 @@ "type": "number", "minimum": 1 }, + "Path": { + "description": "The output directory as **absolute path** (required).", + "type": "string", + "absolutePath": true + }, + "Pathinfo": { + "description": "Include comments with information about the modules.", + "type": "boolean" + }, "Performance": { "description": "Configuration for web performance recommendations", "anyOf": [ @@ -1682,6 +1901,18 @@ "description": "Capture timing information for each module.", "type": "boolean" }, + "PublicPath": { + "description": "The `publicPath` specifies the public URL address of the output files when referenced in a browser.", + "anyOf": [ + { + "type": "string" + }, + { + "instanceof": "Function", + "tsType": "((pathData: import(\"../lib/Compilation\").PathData, assetInfo?: import(\"../lib/Compilation\").AssetInfo) => string)" + } + ] + }, "RecordsInputPath": { "description": "Store compiler state to a json file.", "oneOf": [ @@ -2357,6 +2588,15 @@ } ] }, + "SourceMapFilename": { + "description": "The filename of the SourceMaps for the JavaScript files. They are inside the `output.path` directory.", + "type": "string", + "absolutePath": false + }, + "SourcePrefix": { + "description": "Prefixes every line of the source in the bundle with this string.", + "type": "string" + }, "Stats": { "description": "Stats options object or preset name", "anyOf": [ @@ -2672,6 +2912,10 @@ } } }, + "StrictModuleExceptionHandling": { + "description": "Handles exceptions in module loading correctly at a performance cost.", + "type": "boolean" + }, "Target": { "description": "Environment to build for", "anyOf": [ @@ -2693,6 +2937,15 @@ } ] }, + "UmdNamedDefine": { + "description": "If `output.libraryTarget` is set to umd and `output.library` is set, setting this to true will name the AMD module.", + "type": "boolean" + }, + "UniqueName": { + "description": "A unique name of the webpack build to avoid multiple webpack runtimes to conflict when using globals.", + "type": "string", + "minLength": 1 + }, "Watch": { "description": "Enter watch mode, which rebuilds on file change.", "type": "boolean" @@ -2738,6 +2991,11 @@ } } }, + "WebassemblyModuleFilename": { + "description": "The filename of WebAssembly modules as relative path inside the `output.path` directory.", + "type": "string", + "absolutePath": false + }, "WebpackOptionsNormalized": { "description": "Normalized webpack options object", "type": "object", @@ -2798,7 +3056,7 @@ "$ref": "#/definitions/Optimization" }, "output": { - "$ref": "#/definitions/Output" + "$ref": "#/definitions/OutputNormalized" }, "parallelism": { "$ref": "#/definitions/Parallelism" diff --git a/test/Defaults.unittest.js b/test/Defaults.unittest.js index 64d3c1deb..22c790df3 100644 --- a/test/Defaults.unittest.js +++ b/test/Defaults.unittest.js @@ -195,26 +195,31 @@ describe("Defaults", () => { "chunkLoadTimeout": 120000, "compareBeforeEmit": true, "crossOriginLoading": false, + "devtoolFallbackModuleFilenameTemplate": undefined, + "devtoolModuleFilenameTemplate": undefined, "devtoolNamespace": "webpack", "ecmaVersion": 6, + "enabledLibraryTypes": Array [], "filename": "[name].js", "globalObject": "window", "hashDigest": "hex", "hashDigestLength": 20, "hashFunction": "md4", + "hashSalt": undefined, "hotUpdateChunkFilename": "[id].[fullhash].hot-update.js", "hotUpdateFunction": "webpackHotUpdatewebpack", "hotUpdateMainFilename": "[fullhash].hot-update.json", "iife": true, "jsonpFunction": "webpackJsonpwebpack", "jsonpScriptType": false, - "library": "", + "library": undefined, "libraryTarget": "var", "module": false, "path": "/dist", "pathinfo": false, "publicPath": "", "sourceMapFilename": "[file].map[query]", + "sourcePrefix": undefined, "strictModuleExceptionHandling": false, "uniqueName": "webpack", "webassemblyModuleFilename": "[hash].module.wasm", @@ -327,7 +332,7 @@ describe("Defaults", () => { @@ -113,1 +113,1 @@ - "minSize": 10000, + "minSize": 30000, - @@ -149,1 +149,5 @@ + @@ -154,1 +154,5 @@ - "performance": false, + "performance": Object { + "hints": "warning", @@ -376,7 +381,7 @@ describe("Defaults", () => { @@ -113,1 +113,1 @@ - "minSize": 10000, + "minSize": 30000, - @@ -149,1 +149,5 @@ + @@ -154,1 +154,5 @@ - "performance": false, + "performance": Object { + "hints": "warning", @@ -420,13 +425,13 @@ describe("Defaults", () => { @@ -112,1 +118,1 @@ - "minRemainingSize": undefined, + "minRemainingSize": 0, - @@ -141,1 +147,1 @@ + @@ -145,1 +151,1 @@ - "pathinfo": false, + "pathinfo": true, - @@ -158,1 +164,1 @@ + @@ -163,1 +169,1 @@ - "cache": false, + "cache": true, - @@ -177,1 +183,1 @@ + @@ -182,1 +188,1 @@ - "cache": false, + "cache": true, `) @@ -466,7 +471,7 @@ describe("Defaults", () => { + "type": "javascript/esm", + }, + ], - @@ -160,0 +170,1 @@ + @@ -165,0 +175,1 @@ + ".mjs", `) ); @@ -481,13 +486,13 @@ describe("Defaults", () => { @@ -27,1 +27,1 @@ - "externalsType": "var", + "externalsType": "module", - @@ -134,1 +134,1 @@ + @@ -138,1 +138,1 @@ - "iife": true, + "iife": false, - @@ -136,1 +136,1 @@ + @@ -140,1 +140,1 @@ - "jsonpScriptType": false, + "jsonpScriptType": "module", - @@ -138,2 +138,2 @@ + @@ -142,2 +142,2 @@ - "libraryTarget": "var", - "module": false, + "libraryTarget": "module", @@ -538,7 +543,7 @@ describe("Defaults", () => { @@ -120,1 +120,1 @@ - "chunkFilename": "[name].js", + "chunkFilename": "[id].bundle.js", - @@ -126,1 +126,1 @@ + @@ -129,1 +129,1 @@ - "filename": "[name].js", + "filename": "bundle.js", `) @@ -551,7 +556,7 @@ describe("Defaults", () => { @@ -120,1 +120,1 @@ - "chunkFilename": "[name].js", + "chunkFilename": "[id].js", - @@ -126,1 +126,1 @@ + @@ -129,1 +129,1 @@ - "filename": "[name].js", + "filename": [Function filename], `) @@ -564,22 +569,33 @@ describe("Defaults", () => { @@ -119,1 +119,1 @@ - "chunkCallbackName": "webpackChunkwebpack", + "chunkCallbackName": "webpackChunkmyLib_awesome", - @@ -124,1 +124,1 @@ + @@ -126,1 +126,1 @@ - "devtoolNamespace": "webpack", + "devtoolNamespace": "myLib.awesome", - @@ -132,1 +132,1 @@ + @@ -128,1 +128,3 @@ + - "enabledLibraryTypes": Array [], + + "enabledLibraryTypes": Array [ + + "var", + + ], + @@ -136,1 +138,1 @@ - "hotUpdateFunction": "webpackHotUpdatewebpack", + "hotUpdateFunction": "webpackHotUpdatemyLib_awesome", - @@ -135,1 +135,1 @@ + @@ -139,1 +141,1 @@ - "jsonpFunction": "webpackJsonpwebpack", + "jsonpFunction": "webpackJsonpmyLib_awesome", - @@ -137,1 +137,4 @@ - - "library": "", - + "library": Array [ - + "myLib", - + "awesome", - + ], - @@ -145,1 +148,1 @@ + @@ -141,1 +143,10 @@ + - "library": undefined, + + "library": Object { + + "auxiliaryComment": undefined, + + "export": undefined, + + "name": Array [ + + "myLib", + + "awesome", + + ], + + "type": "var", + + "umdNamedDefine": undefined, + + }, + @@ -150,1 +161,1 @@ - "uniqueName": "webpack", + "uniqueName": "myLib.awesome", `) @@ -596,17 +612,17 @@ describe("Defaults", () => { + "__dirname": false, + "__filename": false, + "global": false, - @@ -127,1 +127,1 @@ + @@ -130,1 +130,1 @@ - "globalObject": "window", + "globalObject": "global", - @@ -155,3 +155,1 @@ + @@ -160,3 +160,1 @@ - "aliasFields": Array [ - "browser", - ], + "aliasFields": Array [], - @@ -165,1 +163,0 @@ + @@ -170,1 +168,0 @@ - "browser", - @@ -190,1 +187,1 @@ + @@ -195,1 +192,1 @@ - "target": "web", + "target": "node", `) @@ -616,10 +632,10 @@ describe("Defaults", () => { - Expected + Received - @@ -127,1 +127,1 @@ + @@ -130,1 +130,1 @@ - "globalObject": "window", + "globalObject": "self", - @@ -190,1 +190,1 @@ + @@ -195,1 +195,1 @@ - "target": "web", + "target": "webworker", `) @@ -632,7 +648,7 @@ describe("Defaults", () => { @@ -85,1 +85,1 @@ - "portableRecords": false, + "portableRecords": true, - @@ -152,2 +152,2 @@ + @@ -157,2 +157,2 @@ - "recordsInputPath": false, - "recordsOutputPath": false, + "recordsInputPath": "some-path", @@ -644,7 +660,7 @@ describe("Defaults", () => { - Expected + Received - @@ -125,1 +125,1 @@ + @@ -127,1 +127,1 @@ - "ecmaVersion": 6, + "ecmaVersion": 11, `) @@ -705,10 +721,10 @@ describe("Defaults", () => { @@ -56,1 +62,1 @@ - "unsafeCache": false, + "unsafeCache": [Function anonymous], - @@ -158,1 +164,1 @@ + @@ -163,1 +169,1 @@ - "cache": false, + "cache": true, - @@ -177,1 +183,1 @@ + @@ -182,1 +188,1 @@ - "cache": false, + "cache": true, `) @@ -743,10 +759,10 @@ describe("Defaults", () => { @@ -56,1 +75,1 @@ - "unsafeCache": false, + "unsafeCache": [Function anonymous], - @@ -158,1 +177,1 @@ + @@ -163,1 +182,1 @@ - "cache": false, + "cache": true, - @@ -177,1 +196,1 @@ + @@ -182,1 +201,1 @@ - "cache": false, + "cache": true, `) @@ -803,4 +819,34 @@ describe("Defaults", () => { + "splitChunks": false, `) ); + + test( + "uniqueName", + { + output: { + uniqueName: "@@@Hello World!" + } + }, + e => + e.toMatchInlineSnapshot(` + - Expected + + Received + + @@ -119,1 +119,1 @@ + - "chunkCallbackName": "webpackChunkwebpack", + + "chunkCallbackName": "webpackChunk_Hello_World_", + @@ -126,1 +126,1 @@ + - "devtoolNamespace": "webpack", + + "devtoolNamespace": "@@@Hello World!", + @@ -136,1 +136,1 @@ + - "hotUpdateFunction": "webpackHotUpdatewebpack", + + "hotUpdateFunction": "webpackHotUpdate_Hello_World_", + @@ -139,1 +139,1 @@ + - "jsonpFunction": "webpackJsonpwebpack", + + "jsonpFunction": "webpackJsonp_Hello_World_", + @@ -150,1 +150,1 @@ + - "uniqueName": "webpack", + + "uniqueName": "@@@Hello World!", + `) + ); }); diff --git a/test/Schemas.lint.js b/test/Schemas.lint.js index c530e58f3..f27176fd1 100644 --- a/test/Schemas.lint.js +++ b/test/Schemas.lint.js @@ -130,7 +130,7 @@ describe("Schemas", () => { }); if ("items" in item) { describe("items", () => { - if (Object.keys(item).join() !== "$ref") { + if (Object.keys(item.items).join() !== "$ref") { validateProperty(item.items); } walker(item.items); diff --git a/test/configCases/externals/optional-externals-root/webpack.config.js b/test/configCases/externals/optional-externals-root/webpack.config.js index 51175908d..8e541fe7e 100644 --- a/test/configCases/externals/optional-externals-root/webpack.config.js +++ b/test/configCases/externals/optional-externals-root/webpack.config.js @@ -1,7 +1,5 @@ module.exports = { - output: { - libraryTarget: "var" - }, + externalsType: "var", externals: { external: "external" } diff --git a/test/configCases/library/0-create-library/webpack.config.js b/test/configCases/library/0-create-library/webpack.config.js index a05d67afe..23be5708d 100644 --- a/test/configCases/library/0-create-library/webpack.config.js +++ b/test/configCases/library/0-create-library/webpack.config.js @@ -3,7 +3,7 @@ module.exports = [ { output: { filename: "commonjs.js", - libraryTarget: "commonjs-module" + libraryTarget: "commonjs" }, resolve: { alias: {