addEntry takes options object instead of only name

remove duplication in EntryOptionsPlugin and DynamicEntryPlugin
lazy require (Dynamic)EntryPlugin in EntryOptionsPlugin
This commit is contained in:
Tobias Koppers 2020-02-04 21:21:42 +01:00
parent 7da1b5e70a
commit 18ae77ef56
8 changed files with 142 additions and 118 deletions

View File

@ -20,7 +20,12 @@ export type EntryDynamic = () => EntryStatic | Promise<EntryStatic>;
* 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;
}

View File

@ -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);
});
}

View File

@ -45,7 +45,7 @@ class DllEntryPlugin {
}),
this.name
),
this.name,
{ name: this.name },
callback
);
});

View File

@ -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<EntryStatic>} 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;

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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();
};

View File

@ -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"
}