diff --git a/declarations/WebpackOptions.d.ts b/declarations/WebpackOptions.d.ts index 3f2a2d36f..cebceb426 100644 --- a/declarations/WebpackOptions.d.ts +++ b/declarations/WebpackOptions.d.ts @@ -20,7 +20,12 @@ export type EntryDynamic = () => EntryStatic | Promise; * This interface was referenced by `WebpackOptions`'s JSON-Schema * via the `definition` "EntryStatic". */ -export type EntryStatic = EntryObject | EntryItem; +export type EntryStatic = EntryObject | EntryUnnamed; +/** + * This interface was referenced by `WebpackOptions`'s JSON-Schema + * via the `definition` "EntryItem". + */ +export type EntryItem = string | NonEmptyArrayOfUniqueStringValues; /** * A non-empty array of non-empty strings * @@ -29,10 +34,12 @@ export type EntryStatic = EntryObject | EntryItem; */ export type NonEmptyArrayOfUniqueStringValues = [string, ...string[]]; /** + * An entry point without name. + * * This interface was referenced by `WebpackOptions`'s JSON-Schema - * via the `definition` "EntryItem". + * via the `definition` "EntryUnnamed". */ -export type EntryItem = string | NonEmptyArrayOfUniqueStringValues; +export type EntryUnnamed = EntryItem; /** * This interface was referenced by `WebpackOptions`'s JSON-Schema * via the `definition` "Externals". @@ -526,7 +533,7 @@ export interface EntryObject { /** * An entry point with name */ - [k: string]: string | NonEmptyArrayOfUniqueStringValues | EntryDescription; + [k: string]: EntryItem | EntryDescription; } /** * An object with entry point description. @@ -536,7 +543,7 @@ export interface EntryObject { */ export interface EntryDescription { /** - * Entrypoint modules. + * The module(s) loaded at startup. */ import: EntryItem; } diff --git a/lib/Compilation.js b/lib/Compilation.js index 10361b2d2..d81bbcd29 100644 --- a/lib/Compilation.js +++ b/lib/Compilation.js @@ -139,6 +139,11 @@ const { arrayToSetDeprecation } = require("./util/deprecation"); * @property {ChunkGraph} chunkGraph the chunk graph */ +/** + * @typedef {Object} EntryOptions + * @property {string} name name of the entrypoint + */ + /** * @typedef {Object} LogEntry * @property {string} type @@ -270,12 +275,12 @@ class Compilation { /** @type {SyncHook<[Module]>} */ stillValidModule: new SyncHook(["module"]), - /** @type {SyncHook<[Dependency, string]>} */ - addEntry: new SyncHook(["entry", "name"]), - /** @type {SyncHook<[Dependency, string, Error]>} */ - failedEntry: new SyncHook(["entry", "name", "error"]), - /** @type {SyncHook<[Dependency, string, Module]>} */ - succeedEntry: new SyncHook(["entry", "name", "module"]), + /** @type {SyncHook<[Dependency, EntryOptions]>} */ + addEntry: new SyncHook(["entry", "options"]), + /** @type {SyncHook<[Dependency, EntryOptions, Error]>} */ + failedEntry: new SyncHook(["entry", "options", "error"]), + /** @type {SyncHook<[Dependency, EntryOptions, Module]>} */ + succeedEntry: new SyncHook(["entry", "options", "module"]), /** @type {SyncWaterfallHook<[string[][], Dependency]>} */ dependencyReferencedExports: new SyncWaterfallHook([ @@ -1380,13 +1385,18 @@ class Compilation { * * @param {string} context context path for entry * @param {EntryDependency} entry entry dependency being created - * @param {string} name name of entry + * @param {string | EntryOptions} options options or deprecated name of entry * @param {ModuleCallback} callback callback function * @returns {void} returns */ - addEntry(context, entry, name, callback) { - this.hooks.addEntry.call(entry, name); + addEntry(context, entry, options, callback) { + // TODO webpack 6 remove + if (typeof options !== "object") { + options = { name: options }; + } + this.hooks.addEntry.call(entry, options); + const { name } = options; let entriesArray = this.entryDependencies.get(name); if (entriesArray === undefined) { entriesArray = []; @@ -1396,10 +1406,10 @@ class Compilation { this.addModuleChain(context, entry, (err, module) => { if (err) { - this.hooks.failedEntry.call(entry, name, err); + this.hooks.failedEntry.call(entry, options, err); return callback(err); } - this.hooks.succeedEntry.call(entry, name, module); + this.hooks.succeedEntry.call(entry, options, module); return callback(null, module); }); } diff --git a/lib/DllEntryPlugin.js b/lib/DllEntryPlugin.js index 8422f1ab0..e5b4182ea 100644 --- a/lib/DllEntryPlugin.js +++ b/lib/DllEntryPlugin.js @@ -45,7 +45,7 @@ class DllEntryPlugin { }), this.name ), - this.name, + { name: this.name }, callback ); }); diff --git a/lib/DynamicEntryPlugin.js b/lib/DynamicEntryPlugin.js index f8f71a2ad..dd56bbef4 100644 --- a/lib/DynamicEntryPlugin.js +++ b/lib/DynamicEntryPlugin.js @@ -5,6 +5,7 @@ "use strict"; +const EntryOptionPlugin = require("./EntryOptionPlugin"); const EntryPlugin = require("./EntryPlugin"); const EntryDependency = require("./dependencies/EntryDependency"); @@ -38,60 +39,32 @@ class DynamicEntryPlugin { } ); - compiler.hooks.make.tapAsync( + compiler.hooks.make.tapPromise( "DynamicEntryPlugin", - (compilation, callback) => { - /** - * @param {string|string[]} entry entry value or array of entry values - * @param {string} name name of entry - * @returns {Promise} returns the promise resolving the Compilation#addEntry function - */ - const addEntry = (entry, name) => { - const deps = DynamicEntryPlugin.createDependencies(entry, name); - return new Promise((resolve, reject) => { - for (const dep of deps) { - compilation.addEntry(this.context, dep, name, err => { - if (err) return reject(err); - resolve(); - }); - } - }); - }; - - Promise.resolve(this.entry()).then(entry => { - if (typeof entry === "string" || Array.isArray(entry)) { - addEntry(entry, "main").then(() => { - callback(); - }, callback); - } else if (typeof entry === "object") { - Promise.all( - Object.keys(entry).map(name => { - const entryItem = entry[name]; - - if (typeof entryItem === "string" || Array.isArray(entryItem)) { - return addEntry(entryItem, name); - } - - return addEntry(entryItem.import, name); - }) - ).then(() => { - callback(); - }, callback); - } - }, callback); - } + (compilation, callback) => + Promise.resolve(this.entry()) + .then(entry => { + const promises = []; + EntryOptionPlugin.processEntryStatic(entry, (entry, options) => { + promises.push( + new Promise((resolve, reject) => { + compilation.addEntry( + this.context, + EntryPlugin.createDependency(entry, options), + options, + err => { + if (err) return reject(err); + resolve(); + } + ); + }) + ); + }); + return Promise.all(promises); + }) + .then(x => {}) ); } - - /** - * @param {string|string[]} entry entry value or array of entry paths - * @param {string} name name of entry - * @returns {EntryDependency[]} dependencies - */ - static createDependencies(entry, name) { - const entryArray = Array.isArray(entry) ? entry : [entry]; - return entryArray.map(entry => EntryPlugin.createDependency(entry, name)); - } } module.exports = DynamicEntryPlugin; diff --git a/lib/EntryOptionPlugin.js b/lib/EntryOptionPlugin.js index bb2580ccf..35d5997ce 100644 --- a/lib/EntryOptionPlugin.js +++ b/lib/EntryOptionPlugin.js @@ -5,47 +5,76 @@ "use strict"; -const DynamicEntryPlugin = require("./DynamicEntryPlugin"); -const EntryPlugin = require("./EntryPlugin"); - +/** @typedef {import("../declarations/WebpackOptions").Entry} Entry */ /** @typedef {import("../declarations/WebpackOptions").EntryDescription} EntryDescription */ /** @typedef {import("../declarations/WebpackOptions").EntryItem} EntryItem */ +/** @typedef {import("../declarations/WebpackOptions").EntryStatic} EntryStatic */ +/** @typedef {import("./Compilation").EntryOptions} EntryOptions */ /** @typedef {import("./Compiler")} Compiler */ -module.exports = class EntryOptionPlugin { +class EntryOptionPlugin { /** * @param {Compiler} compiler the compiler instance one is tapping into * @returns {void} */ apply(compiler) { compiler.hooks.entryOption.tap("EntryOptionPlugin", (context, entry) => { - /** - * @param {EntryItem|EntryDescription} entry entry array or single path - * @param {string} name entry key name - * @returns {void} - */ - const applyEntryPlugins = (entry, name) => { - if (typeof entry === "string") { - new EntryPlugin(context, entry, name).apply(compiler); - } else if (Array.isArray(entry)) { - for (const item of entry) { - applyEntryPlugins(item, name); - } - } else if (entry && typeof entry === "object") { - applyEntryPlugins(entry.import, name); - } - }; - - if (typeof entry === "string" || Array.isArray(entry)) { - applyEntryPlugins(entry, "main"); - } else if (typeof entry === "object") { - for (const name of Object.keys(entry)) { - applyEntryPlugins(entry[name], name); - } - } else if (typeof entry === "function") { + if (typeof entry === "function") { + const DynamicEntryPlugin = require("./DynamicEntryPlugin"); new DynamicEntryPlugin(context, entry).apply(compiler); + } else { + const EntryPlugin = require("./EntryPlugin"); + EntryOptionPlugin.processEntryStatic(entry, (entry, options) => { + new EntryPlugin(context, entry, options).apply(compiler); + }); } return true; }); } -}; + + /** + * @param {EntryStatic} entry entry array or single path + * @param {function(string, EntryOptions): void} onEntry callback for each entry + * @returns {void} + */ + static processEntryStatic(entry, onEntry) { + /** + * @param {EntryItem} entry entry array or single path + * @param {EntryOptions} options entry options + * @returns {void} + */ + const applyEntryItemPlugins = (entry, options) => { + if (typeof entry === "string") { + onEntry(entry, options); + } else if (Array.isArray(entry)) { + for (const item of entry) { + applyEntryItemPlugins(item, options); + } + } + }; + + /** + * @param {EntryDescription} entry entry array or single path + * @param {EntryOptions} options entry options + * @returns {void} + */ + const applyEntryDescriptionPlugins = (entry, options) => { + applyEntryItemPlugins(entry.import, options); + }; + + if (typeof entry === "string" || Array.isArray(entry)) { + applyEntryItemPlugins(entry, { name: "main" }); + } else if (entry && typeof entry === "object") { + for (const name of Object.keys(entry)) { + const value = entry[name]; + if (typeof value === "string" || Array.isArray(value)) { + applyEntryItemPlugins(value, { name }); + } else { + applyEntryDescriptionPlugins(value, { name }); + } + } + } + } +} + +module.exports = EntryOptionPlugin; diff --git a/lib/EntryPlugin.js b/lib/EntryPlugin.js index d1e7f2310..9cf2e5f74 100644 --- a/lib/EntryPlugin.js +++ b/lib/EntryPlugin.js @@ -7,6 +7,7 @@ const EntryDependency = require("./dependencies/EntryDependency"); +/** @typedef {import("./Compilation").EntryOptions} EntryOptions */ /** @typedef {import("./Compiler")} Compiler */ class EntryPlugin { @@ -16,12 +17,12 @@ class EntryPlugin { * * @param {string} context context path * @param {string} entry entry path - * @param {string} name entry key name + * @param {EntryOptions | string} options entry options (passing a string is deprecated) */ - constructor(context, entry, name) { + constructor(context, entry, options) { this.context = context; this.entry = entry; - this.name = name; + this.options = options; } /** @@ -40,10 +41,10 @@ class EntryPlugin { ); compiler.hooks.make.tapAsync("EntryPlugin", (compilation, callback) => { - const { entry, name, context } = this; + const { entry, options, context } = this; - const dep = EntryPlugin.createDependency(entry, name); - compilation.addEntry(context, dep, name, err => { + const dep = EntryPlugin.createDependency(entry, options); + compilation.addEntry(context, dep, options, err => { callback(err); }); }); @@ -51,12 +52,13 @@ class EntryPlugin { /** * @param {string} entry entry request - * @param {string} name entry name + * @param {EntryOptions | string} options entry options (passing string is deprecated) * @returns {EntryDependency} the dependency */ - static createDependency(entry, name) { + static createDependency(entry, options) { const dep = new EntryDependency(entry); - dep.loc = { name }; + // TODO webpack 6 remove string option + dep.loc = { name: typeof options === "object" ? options.name : options }; return dep; } } diff --git a/lib/ProgressPlugin.js b/lib/ProgressPlugin.js index 99d9f9d53..65d7399f9 100644 --- a/lib/ProgressPlugin.js +++ b/lib/ProgressPlugin.js @@ -252,7 +252,7 @@ class ProgressPlugin { } }; - const entryAdd = (entry, name) => { + const entryAdd = (entry, options) => { entriesCount++; if (entriesCount % 10 === 0) updateThrottled(); }; @@ -276,7 +276,7 @@ class ProgressPlugin { if (doneModules % 100 === 0) updateThrottled(); }; - const entryDone = (entry, name) => { + const entryDone = (entry, options) => { doneEntries++; update(); }; diff --git a/schemas/WebpackOptions.json b/schemas/WebpackOptions.json index 8203789a4..9caa96bcf 100644 --- a/schemas/WebpackOptions.json +++ b/schemas/WebpackOptions.json @@ -44,7 +44,7 @@ "additionalProperties": false, "properties": { "import": { - "description": "Entrypoint modules.", + "description": "The module(s) loaded at startup.", "oneOf": [ { "$ref": "#/definitions/EntryItem" @@ -62,12 +62,12 @@ "EntryItem": { "oneOf": [ { - "description": "An entry point without name. The string is resolved to a module which is loaded upon startup.", + "description": "The string is resolved to a module which is loaded upon startup.", "type": "string", "minLength": 1 }, { - "description": "An entry point without name. All modules are loaded upon startup. The last one is exported.", + "description": "All modules are loaded upon startup. The last one is exported.", "anyOf": [ { "$ref": "#/definitions/NonEmptyArrayOfUniqueStringValues" @@ -83,15 +83,10 @@ "description": "An entry point with name", "oneOf": [ { - "description": "The string is resolved to a module which is loaded upon startup.", - "type": "string", - "minLength": 1 - }, - { - "description": "All modules are loaded upon startup. The last one is exported.", + "description": "The module(s) loaded at startup.", "anyOf": [ { - "$ref": "#/definitions/NonEmptyArrayOfUniqueStringValues" + "$ref": "#/definitions/EntryItem" } ] }, @@ -112,6 +107,14 @@ { "$ref": "#/definitions/EntryObject" }, + { + "$ref": "#/definitions/EntryUnnamed" + } + ] + }, + "EntryUnnamed": { + "description": "An entry point without name.", + "anyOf": [ { "$ref": "#/definitions/EntryItem" }