normalize entry option and reduce normalization in EntryOptionPlugin

This commit is contained in:
Tobias Koppers 2020-02-26 13:08:05 +01:00
parent f936aab240
commit 39e407e927
6 changed files with 112 additions and 99 deletions

View File

@ -56,6 +56,15 @@ export type EntryItem = string | NonEmptyArrayOfUniqueStringValues;
* A non-empty array of non-empty strings
*/
export type NonEmptyArrayOfUniqueStringValues = [string, ...string[]];
/**
* 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 entry point without name.
*/
@ -385,15 +394,6 @@ export type LibraryType =
* 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
*/
@ -817,11 +817,11 @@ export interface EntryDescription {
*/
dependOn?: string | NonEmptyArrayOfUniqueStringValues;
/**
* The filename of the entrypoint as relative path inside the `output.path` directory.
* 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;
filename?: Filename;
/**
* The module(s) loaded at startup.
* Module(s) that are loaded upon startup
*/
import: EntryItem;
}
@ -1956,6 +1956,23 @@ export interface WatchOptions {
*/
stdin?: boolean;
}
/**
* An object with entry point description.
*/
export interface EntryDescriptionNormalized {
/**
* The entrypoints that the current entrypoint depend on. They must be loaded when this entrypoint is loaded.
*/
dependOn?: NonEmptyArrayOfUniqueStringValues;
/**
* 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;
/**
* Module(s) that are loaded upon startup. The last one is exported.
*/
import: NonEmptyArrayOfUniqueStringValues;
}
/**
* Multiple entry bundles are created. The key is the entry name. The value is an entry description object.
*/
@ -1963,7 +1980,7 @@ export interface EntryStaticNormalized {
/**
* An object with entry point description.
*/
[k: string]: EntryDescription;
[k: string]: EntryDescriptionNormalized;
}
/**
* Normalized options affecting the output of the compilation. `output` options tell webpack how to write the compiled files to disk.

View File

@ -64,6 +64,7 @@ const createHash = require("./util/createHash");
const { arrayToSetDeprecation } = require("./util/deprecation");
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../declarations/WebpackOptions").EntryDescriptionNormalized} EntryDescription */
/** @typedef {import("../declarations/WebpackOptions").OutputNormalized} OutputOptions */
/** @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock */
/** @typedef {import("./ChunkGroup").ChunkGroupOptions} ChunkGroupOptions */
@ -142,12 +143,7 @@ const { arrayToSetDeprecation } = require("./util/deprecation");
* @property {ChunkGraph} chunkGraph the chunk graph
*/
/**
* @typedef {Object} EntryOptions
* @property {string} name name of the entrypoint
* @property {string[]=} dependOn list of other entries on which this entry depends
* @property {string=} filename filename of entry chunk
*/
/** @typedef {{ name: string } & Omit<EntryDescription, "import">} EntryOptions */
/**
* @typedef {Object} EntryData

View File

@ -9,9 +9,9 @@ const EntryOptionPlugin = require("./EntryOptionPlugin");
const EntryPlugin = require("./EntryPlugin");
const EntryDependency = require("./dependencies/EntryDependency");
/** @typedef {import("../declarations/WebpackOptions").EntryDynamic} EntryDynamic */
/** @typedef {import("../declarations/WebpackOptions").EntryDynamicNormalized} EntryDynamic */
/** @typedef {import("../declarations/WebpackOptions").EntryItem} EntryItem */
/** @typedef {import("../declarations/WebpackOptions").EntryStatic} EntryStatic */
/** @typedef {import("../declarations/WebpackOptions").EntryStaticNormalized} EntryStatic */
/** @typedef {import("./Compiler")} Compiler */
class DynamicEntryPlugin {
@ -45,21 +45,28 @@ class DynamicEntryPlugin {
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();
}
);
})
for (const name of Object.keys(entry)) {
const desc = entry[name];
const options = EntryOptionPlugin.entryDescriptionToOptions(
name,
desc
);
});
for (const entry of desc.import) {
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 => {})

View File

@ -5,10 +5,7 @@
"use strict";
/** @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("../declarations/WebpackOptions").EntryDescriptionNormalized} EntryDescription */
/** @typedef {import("./Compilation").EntryOptions} EntryOptions */
/** @typedef {import("./Compiler")} Compiler */
@ -24,66 +21,34 @@ class EntryOptionPlugin {
new DynamicEntryPlugin(context, entry).apply(compiler);
} else {
const EntryPlugin = require("./EntryPlugin");
EntryOptionPlugin.processEntryStatic(entry, (entry, options) => {
new EntryPlugin(context, entry, options).apply(compiler);
});
for (const name of Object.keys(entry)) {
const desc = entry[name];
const options = EntryOptionPlugin.entryDescriptionToOptions(
name,
desc
);
for (const entry of desc.import) {
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}
* @param {string} name entry name
* @param {EntryDescription} desc entry description
* @returns {EntryOptions} options for the entry
*/
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);
}
}
static entryDescriptionToOptions(name, desc) {
/** @type {EntryOptions} */
const options = {
name,
filename: desc.filename,
dependOn: desc.dependOn
};
/**
* @param {EntryDescription} entry entry array or single path
* @param {EntryOptions} options entry options
* @returns {void}
*/
const applyEntryDescriptionPlugins = (entry, options) => {
let dependOn = undefined;
if (entry.dependOn) {
dependOn = Array.isArray(entry.dependOn)
? Array.from(entry.dependOn)
: [entry.dependOn];
}
applyEntryItemPlugins(entry.import, {
...options,
dependOn,
filename: entry.filename
});
};
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 });
}
}
}
return options;
}
}

View File

@ -318,7 +318,15 @@ const getNormalizedEntryStatic = entry => {
import: value
};
} else {
result[key] = value;
result[key] = {
import:
value.import &&
(Array.isArray(value.import) ? value.import : [value.import]),
filename: value.filename,
dependOn:
value.dependOn &&
(Array.isArray(value.dependOn) ? value.dependOn : [value.dependOn])
};
}
}
return result;

View File

@ -247,15 +247,35 @@
]
},
"filename": {
"description": "The filename of the entrypoint as relative path inside the `output.path` directory.",
"type": "string",
"absolutePath": false
"$ref": "#/definitions/Filename"
},
"import": {
"description": "The module(s) loaded at startup.",
"oneOf": [
"$ref": "#/definitions/EntryItem"
}
},
"required": ["import"]
},
"EntryDescriptionNormalized": {
"description": "An object with entry point description.",
"type": "object",
"additionalProperties": false,
"properties": {
"dependOn": {
"description": "The entrypoints that the current entrypoint depend on. They must be loaded when this entrypoint is loaded.",
"anyOf": [
{
"$ref": "#/definitions/EntryItem"
"$ref": "#/definitions/NonEmptyArrayOfUniqueStringValues"
}
]
},
"filename": {
"$ref": "#/definitions/Filename"
},
"import": {
"description": "Module(s) that are loaded upon startup. The last one is exported.",
"anyOf": [
{
"$ref": "#/definitions/NonEmptyArrayOfUniqueStringValues"
}
]
}
@ -345,7 +365,7 @@
"description": "An object with entry point description.",
"anyOf": [
{
"$ref": "#/definitions/EntryDescription"
"$ref": "#/definitions/EntryDescriptionNormalized"
}
]
},