chore(types): add Compiler and Compilation type support

This commit is contained in:
Sean Larkin 2018-05-03 09:57:02 -07:00 committed by Tobias Koppers
parent 687c03869f
commit 761d73b4e7
28 changed files with 1376 additions and 631 deletions

93
declarations.d.ts vendored
View File

@ -8,6 +8,99 @@ declare namespace NodeJS {
}
}
declare module "neo-async" {
export interface Dictionary<T> {
[key: string]: T;
}
export type IterableCollection<T> = T[] | IterableIterator<T> | Dictionary<T>;
export interface ErrorCallback<T> {
(err?: T): void;
}
export interface AsyncBooleanResultCallback<E> {
(err?: E, truthValue?: boolean): void;
}
export interface AsyncResultCallback<T, E> {
(err?: E, result?: T): void;
}
export interface AsyncResultArrayCallback<T, E> {
(err?: E, results?: Array<T | undefined>): void;
}
export interface AsyncResultObjectCallback<T, E> {
(err: E | undefined, results: Dictionary<T | undefined>): void;
}
export interface AsyncFunction<T, E> {
(callback: (err?: E, result?: T) => void): void;
}
export interface AsyncFunctionEx<T, E> {
(callback: (err?: E, ...results: T[]) => void): void;
}
export interface AsyncIterator<T, E> {
(item: T, callback: ErrorCallback<E>): void;
}
export interface AsyncForEachOfIterator<T, E> {
(item: T, key: number | string, callback: ErrorCallback<E>): void;
}
export interface AsyncResultIterator<T, R, E> {
(item: T, callback: AsyncResultCallback<R, E>): void;
}
export interface AsyncMemoIterator<T, R, E> {
(memo: R | undefined, item: T, callback: AsyncResultCallback<R, E>): void;
}
export interface AsyncBooleanIterator<T, E> {
(item: T, callback: AsyncBooleanResultCallback<E>): void;
}
export interface AsyncWorker<T, E> {
(task: T, callback: ErrorCallback<E>): void;
}
export interface AsyncVoidFunction<E> {
(callback: ErrorCallback<E>): void;
}
export type AsyncAutoTasks<R extends Dictionary<any>, E> = {
[K in keyof R]: AsyncAutoTask<R[K], R, E>
};
export type AsyncAutoTask<R1, R extends Dictionary<any>, E> =
| AsyncAutoTaskFunctionWithoutDependencies<R1, E>
| (keyof R | AsyncAutoTaskFunction<R1, R, E>)[];
export interface AsyncAutoTaskFunctionWithoutDependencies<R1, E> {
(cb: AsyncResultCallback<R1, E> | ErrorCallback<E>): void;
}
export interface AsyncAutoTaskFunction<R1, R extends Dictionary<any>, E> {
(results: R, cb: AsyncResultCallback<R1, E> | ErrorCallback<E>): void;
}
export function each<T, E>(
arr: IterableCollection<T>,
iterator: AsyncIterator<T, E>,
callback?: ErrorCallback<E>
): void;
export function map<T, R, E>(
arr: T[] | IterableIterator<T>,
iterator: AsyncResultIterator<T, R, E>,
callback?: AsyncResultArrayCallback<R, E>
): void;
export function map<T, R, E>(
arr: Dictionary<T>,
iterator: AsyncResultIterator<T, R, E>,
callback?: AsyncResultArrayCallback<R, E>
): void;
export function parallel<T, E>(
tasks: Array<AsyncFunction<T, E>>,
callback?: AsyncResultArrayCallback<T, E>
): void;
export function parallel<T, E>(
tasks: Dictionary<AsyncFunction<T, E>>,
callback?: AsyncResultObjectCallback<T, E>
): void;
export const forEach: typeof each;
}
// There are no typings for chrome-trace-event
declare module "chrome-trace-event" {
interface Event {

View File

@ -5,7 +5,34 @@
"use strict";
const DependenciesBlock = require("./DependenciesBlock");
/**
* @typedef {import("./ChunkGroup")} ChunkGroup
* @typedef {import("./Module")} Module
* @typedef {import("crypto").Hash} Hash
* @typedef {TODO} GroupOptions
*
*/
/**
* @typedef {Object} SourcePosition
* @property {number} line
* @property {number} column
*/
/**
* @typedef {Object} SourceLocation
* @property {number} index
* @property {SourcePosition} start
* @property {SourcePosition} end
*/
module.exports = class AsyncDependenciesBlock extends DependenciesBlock {
/**
* @param {GroupOptions} groupOptions options for the group
* @param {Module} module the Module object
* @param {SourceLocation=} loc the line of code
* @param {TODO=} request the request
*/
constructor(groupOptions, module, loc, request) {
super();
if (typeof groupOptions === "string") {
@ -14,28 +41,50 @@ module.exports = class AsyncDependenciesBlock extends DependenciesBlock {
groupOptions = { name: undefined };
}
this.groupOptions = groupOptions;
/** @type {ChunkGroup=} */
this.chunkGroup = undefined;
this.module = module;
this.loc = loc;
this.request = request;
/** @type {DependenciesBlock} */
this.parent = undefined;
}
/**
* @returns {string} The name of the chunk
*/
get chunkName() {
return this.groupOptions.name;
}
/**
* @param {string} value The new chunk name
* @returns {void}
*/
set chunkName(value) {
this.groupOptions.name = value;
}
/**
* @returns {never} this throws and should never be called
*/
get chunks() {
throw new Error("Moved to AsyncDependenciesBlock.chunkGroup");
}
/**
* @param {never} value setter value
* @returns {never} this is going to throw therefore we should throw type
* assertions by returning never
*/
set chunks(value) {
throw new Error("Moved to AsyncDependenciesBlock.chunkGroup");
}
/**
* @param {Hash} hash the hash used to track block changes, from "crypto" module
* @returns {void}
*/
updateHash(hash) {
hash.update(JSON.stringify(this.groupOptions));
hash.update(
@ -50,16 +99,25 @@ module.exports = class AsyncDependenciesBlock extends DependenciesBlock {
super.updateHash(hash);
}
/**
* @returns {void}
*/
disconnect() {
this.chunkGroup = undefined;
super.disconnect();
}
/**
* @returns {void}
*/
unseal() {
this.chunkGroup = undefined;
super.unseal();
}
/**
* @returns {void}
*/
sortItems() {
super.sortItems();
}

View File

@ -17,6 +17,7 @@ const ERR_CHUNK_INITIAL =
/** @typedef {import("./ChunkGroup")} ChunkGroup */
/** @typedef {import("./ModuleReason.js")} ModuleReason */
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("crypto").Hash} Hash */
/**
* @typedef {Object} Identifiable an object who contains an identifier function property
@ -471,6 +472,11 @@ class Chunk {
return this.addMultiplierAndOverhead(this.modulesSize(), options);
}
/**
* @param {Chunk} otherChunk the other chunk
* @param {TODO} options the options for this function
* @returns {number | false} the size, or false if it can't be integrated
*/
integratedSize(otherChunk, options) {
// Chunk if it's possible to integrate this chunk
if (!this.canBeIntegrated(otherChunk)) {
@ -500,6 +506,9 @@ class Chunk {
this.sortModules();
}
/**
* @returns {Set<Chunk>} a set of all the async chunks
*/
getAllAsyncChunks() {
const queue = new Set();
const chunks = new Set();
@ -522,6 +531,10 @@ class Chunk {
return chunks;
}
/**
* @param {Hash} realHash the hash for the chunk maps
* @returns {{ hash: TODO, contentHash: TODO, name: TODO }} the chunk map information
*/
getChunkMaps(realHash) {
const chunkHashMap = Object.create(null);
const chunkContentHashMap = Object.create(null);
@ -544,6 +557,9 @@ class Chunk {
};
}
/**
* @returns {Record<string, Array<Set<TODO>>>} a record object of names to lists of child ids(?)
*/
getChildIdsByOrders() {
const lists = new Map();
for (const group of this.groupsIterable) {
@ -600,6 +616,12 @@ class Chunk {
return chunkMaps;
}
/** @typedef {(module: Module) => true} FilterFn */
/**
* @param {FilterFn} filterFn function used to filter modules
* @returns {TODO} module map information
*/
getChunkModuleMaps(filterFn) {
const chunkModuleIdMap = Object.create(null);
const chunkModuleHashMap = Object.create(null);

View File

@ -35,14 +35,93 @@ const createHash = require("./util/createHash");
const Queue = require("./util/Queue");
const SortableSet = require("./util/SortableSet");
const GraphHelpers = require("./GraphHelpers");
const SingleEntryDependency = require("./dependencies/SingleEntryDependency");
const byId = (a, b) => {
if (a.id < b.id) return -1;
if (a.id > b.id) return 1;
/** @typedef {import("./Module")} Module */
/** @typedef {import("./Compiler")} Compiler */
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("./WebpackError")} WebpackError */
/** @typedef {import("./DependenciesBlockVariable")} DependenciesBlockVariable */
/** @typedef {import("./dependencies/SingleEntryDependency")} SingleEntryDependency */
/** @typedef {import("./dependencies/MultiEntryDependency")} MultiEntryDependency */
/** @typedef {import("./dependencies/DllEntryDependency")} DllEntryDependency */
/** @typedef {import("./DependenciesBlock")} DependenciesBlock */
/** @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock */
/** @typedef {SingleEntryDependency|MultiEntryDependency|DllEntryDependency} PossibleEntryDependencies */
/** @typedef {{[assetName: string]: Source}} CompilationAssets */
/** @typedef {(err: Error|null, result?: Module) => void } ModuleCallback */
/** @typedef {(err?: Error|null, result?: Module) => void } ModuleChainCallback */
/** @typedef {(module: Module) => void} OnModuleCallback */
/** @typedef {(err?: Error|null) => void} CompilationSealCallback */
/** @typedef {{apply: (dep: Dependency, source: Source, runtime: RuntimeTemplate) => void}} DependencyTemplate */
/** @typedef {(d: Dependency) => any} DepBlockVarDependenciesCallback */
/** @typedef {new (...args: any[]) => Dependency} DepConstructor */
/** @typedef {{apply: () => void}} Plugin */
/**
* @typedef {Object} ModuleFactoryCreateDataContextInfo
* @property {string} issuer
* @property {string} compiler
*/
/**
* @typedef {Object} ModuleFactoryCreateData
* @property {ModuleFactoryCreateDataContextInfo} contextInfo
* @property {any=} resolveOptions
* @property {string} context
* @property {Dependency[]} dependencies
*/
/**
* @typedef {Object} ModuleFactory
* @property {(data: ModuleFactoryCreateData, callback: ModuleCallback) => any} create
*/
/**
* @typedef {Object} SortedDependency
* @property {ModuleFactory} factory
* @property {Dependency[]} dependencies
*/
/**
* @typedef {Object} SourcePosition
* @property {number} line
* @property {number} column
*/
/**
* @typedef {Object} SourceLocation
* @property {number} index
* @property {SourcePosition} start
* @property {SourcePosition} end
*/
/**
* @typedef {Object} AvailableModulesChunkGroupMapping
* @property {ChunkGroup} chunkGroup
* @property {Set<Module>} availableModules
*/
/**
* @param {Chunk} a first chunk to sort by id
* @param {Chunk} b second chunk to sort by id
* @returns {-1|0|1} sort value
*/
function byId(a, b) {
if (a.id && b.id) {
if (a.id < b.id) return -1;
if (a.id > b.id) return 1;
}
return 0;
};
}
const byIdOrIdentifier = (a, b) => {
/**
* @param {Module} a first module to sort by
* @param {Module} b second module to sort by
* @returns {-1|0|1} sort value
*/
function byIdOrIdentifier(a, b) {
if (a.id < b.id) return -1;
if (a.id > b.id) return 1;
const identA = a.identifier();
@ -50,9 +129,14 @@ const byIdOrIdentifier = (a, b) => {
if (identA < identB) return -1;
if (identA > identB) return 1;
return 0;
};
}
const byIndexOrIdentifier = (a, b) => {
/**
* @param {Module} a first module to sort by
* @param {Module} b second module to sort by
* @returns {-1|0|1} sort value
*/
function byIndexOrIdentifier(a, b) {
if (a.index < b.index) return -1;
if (a.index > b.index) return 1;
const identA = a.identifier();
@ -60,35 +144,59 @@ const byIndexOrIdentifier = (a, b) => {
if (identA < identB) return -1;
if (identA > identB) return 1;
return 0;
};
}
const byNameOrHash = (a, b) => {
/**
* @param {Compilation} a first compilation to sort by
* @param {Compilation} b second compilation to sort by
* @returns {-1|0|1} sort value
*/
function byNameOrHash(a, b) {
if (a.name < b.name) return -1;
if (a.name > b.name) return 1;
if (a.fullHash < b.fullHash) return -1;
if (a.fullhash > b.fullHash) return 1;
if (a.fullHash > b.fullHash) return 1;
return 0;
};
}
const iterationBlockVariable = (variables, fn) => {
/**
* @param {DependenciesBlockVariable[]} variables DepBlock Variables to iterate over
* @param {DepBlockVarDependenciesCallback} fn callback to apply on iterated elements
* @returns {void}
*/
function iterationBlockVariable(variables, fn) {
for (
let indexVariable = 0;
indexVariable < variables.length;
indexVariable++
) {
// TODO: Remove when Dependency + DependenciesBlockVariable is typed
/** @type {Dependency[]} */
const varDep = variables[indexVariable].dependencies;
for (let indexVDep = 0; indexVDep < varDep.length; indexVDep++) {
fn(varDep[indexVDep]);
}
}
};
}
const iterationOfArrayCallback = (arr, fn) => {
/**
* @template {T}
* @param {T[]} arr array of elements to iterate over
* @param {function(T): void} fn callback applied to each element
* @returns {void}
*/
function iterationOfArrayCallback(arr, fn) {
for (let index = 0; index < arr.length; index++) {
fn(arr[index]);
}
};
}
/**
* @template {T}
* @param {Set<T>} set set to add items to
* @param {Set<T>} otherSet set to add items from
* @returns {void}
*/
function addAllToSet(set, otherSet) {
for (const item of otherSet) {
set.add(item);
@ -96,92 +204,156 @@ function addAllToSet(set, otherSet) {
}
class Compilation extends Tapable {
/**
* Creates an instance of Compilation.
* @param {Compiler} compiler the compiler which created the compilation
*/
constructor(compiler) {
super();
this.hooks = {
/** @type {SyncHook<Module>} */
buildModule: new SyncHook(["module"]),
/** @type {SyncHook<Module>} */
rebuildModule: new SyncHook(["module"]),
/** @type {SyncHook<Module, Error>} */
failedModule: new SyncHook(["module", "error"]),
/** @type {SyncHook<Module>} */
succeedModule: new SyncHook(["module"]),
/** @type {SyncHook<Module[]>} */
finishModules: new SyncHook(["modules"]),
/** @type {SyncHook<Module>} */
finishRebuildingModule: new SyncHook(["module"]),
/** @type {SyncHook} */
unseal: new SyncHook([]),
/** @type {SyncHook} */
seal: new SyncHook([]),
/** @type {SyncBailHook<Module[]>} */
optimizeDependenciesBasic: new SyncBailHook(["modules"]),
/** @type {SyncBailHook<Module[]>} */
optimizeDependencies: new SyncBailHook(["modules"]),
/** @type {SyncBailHook<Module[]>} */
optimizeDependenciesAdvanced: new SyncBailHook(["modules"]),
/** @type {SyncBailHook<Module[]>} */
afterOptimizeDependencies: new SyncHook(["modules"]),
/** @type {SyncHook} */
optimize: new SyncHook([]),
/** @type {SyncBailHook<Module[]>} */
optimizeModulesBasic: new SyncBailHook(["modules"]),
/** @type {SyncBailHook<Module[]>} */
optimizeModules: new SyncBailHook(["modules"]),
/** @type {SyncBailHook<Module[]>} */
optimizeModulesAdvanced: new SyncBailHook(["modules"]),
/** @type {SyncHook<Module[]>} */
afterOptimizeModules: new SyncHook(["modules"]),
/** @type {SyncBailHook<Chunk[], ChunkGroup[]>} */
optimizeChunksBasic: new SyncBailHook(["chunks", "chunkGroups"]),
/** @type {SyncBailHook<Chunk[], ChunkGroup[]>} */
optimizeChunks: new SyncBailHook(["chunks", "chunkGroups"]),
/** @type {SyncBailHook<Chunk[], ChunkGroup[]>} */
optimizeChunksAdvanced: new SyncBailHook(["chunks", "chunkGroups"]),
/** @type {SyncHook<Chunk[], ChunkGroup[]>} */
afterOptimizeChunks: new SyncHook(["chunks", "chunkGroups"]),
/** @type {AsyncSeriesHook<Chunk[], Module[]>} */
optimizeTree: new AsyncSeriesHook(["chunks", "modules"]),
/** @type {SyncHook<Chunk[], Module[]>} */
afterOptimizeTree: new SyncHook(["chunks", "modules"]),
/** @type {SyncBailHook<Chunk[], Module[]>} */
optimizeChunkModulesBasic: new SyncBailHook(["chunks", "modules"]),
/** @type {SyncBailHook<Chunk[], Module[]>} */
optimizeChunkModules: new SyncBailHook(["chunks", "modules"]),
/** @type {SyncBailHook<Chunk[], Module[]>} */
optimizeChunkModulesAdvanced: new SyncBailHook(["chunks", "modules"]),
/** @type {SyncHook<Chunk[], Module[]>} */
afterOptimizeChunkModules: new SyncHook(["chunks", "modules"]),
/** @type {SyncBailHook} */
shouldRecord: new SyncBailHook([]),
/** @type {SyncHook<Module[], any>} */
reviveModules: new SyncHook(["modules", "records"]),
/** @type {SyncHook<Module[]>} */
optimizeModuleOrder: new SyncHook(["modules"]),
/** @type {SyncHook<Module[]>} */
advancedOptimizeModuleOrder: new SyncHook(["modules"]),
/** @type {SyncHook<Module[]>} */
beforeModuleIds: new SyncHook(["modules"]),
/** @type {SyncHook<Module[]>} */
moduleIds: new SyncHook(["modules"]),
/** @type {SyncHook<Module[]>} */
optimizeModuleIds: new SyncHook(["modules"]),
/** @type {SyncHook<Module[]>} */
afterOptimizeModuleIds: new SyncHook(["modules"]),
/** @type {SyncHook<Chunk[], any>} */
reviveChunks: new SyncHook(["chunks", "records"]),
/** @type {SyncHook<Chunk[]>} */
optimizeChunkOrder: new SyncHook(["chunks"]),
/** @type {SyncHook<Chunk[]>} */
beforeChunkIds: new SyncHook(["chunks"]),
/** @type {SyncHook<Chunk[]>} */
optimizeChunkIds: new SyncHook(["chunks"]),
/** @type {SyncHook<Chunk[]>} */
afterOptimizeChunkIds: new SyncHook(["chunks"]),
/** @type {SyncHook<Module[], any>} */
recordModules: new SyncHook(["modules", "records"]),
/** @type {SyncHook<Chunk[], any>} */
recordChunks: new SyncHook(["chunks", "records"]),
/** @type {SyncHook} */
beforeHash: new SyncHook([]),
/** @type {SyncHook<Chunk>} */
contentHash: new SyncHook(["chunk"]),
/** @type {SyncHook} */
afterHash: new SyncHook([]),
/** @type {SyncHook<any>} */
recordHash: new SyncHook(["records"]),
/** @type {SyncHook<Compilation, any>} */
record: new SyncHook(["compilation", "records"]),
/** @type {SyncHook} */
beforeModuleAssets: new SyncHook([]),
/** @type {SyncBailHook} */
shouldGenerateChunkAssets: new SyncBailHook([]),
/** @type {SyncHook} */
beforeChunkAssets: new SyncHook([]),
/** @type {SyncHook<Chunk[]>} */
additionalChunkAssets: new SyncHook(["chunks"]),
/** @type {AsyncSeriesHook} */
additionalAssets: new AsyncSeriesHook([]),
/** @type {AsyncSeriesHook<Chunk[]>} */
optimizeChunkAssets: new AsyncSeriesHook(["chunks"]),
/** @type {SyncHook<Chunk[]>} */
afterOptimizeChunkAssets: new SyncHook(["chunks"]),
/** @type {AsyncSeriesHook<CompilationAssets>} */
optimizeAssets: new AsyncSeriesHook(["assets"]),
/** @type {SyncHook<CompilationAssets>} */
afterOptimizeAssets: new SyncHook(["assets"]),
/** @type {SyncBailHook} */
needAdditionalSeal: new SyncBailHook([]),
/** @type {AsyncSeriesHook} */
afterSeal: new AsyncSeriesHook([]),
/** @type {SyncHook<Chunk, any>} */
chunkHash: new SyncHook(["chunk", "chunkHash"]),
/** @type {SyncHook<Module, string>} */
moduleAsset: new SyncHook(["module", "filename"]),
/** @type {SyncHook<Chunk, string>} */
chunkAsset: new SyncHook(["chunk", "filename"]),
/** @type {SyncWaterfallHook<string, any>} */
assetPath: new SyncWaterfallHook(["filename", "data"]), // TODO MainTemplate
/** @type {SyncBailHook} */
needAdditionalPass: new SyncBailHook([]),
/** @type {SyncHook<Compiler, string, number>} */
childCompiler: new SyncHook([
"childCompiler",
"compilerName",
@ -190,11 +362,16 @@ class Compilation extends Tapable {
// TODO the following hooks are weirdly located here
// TODO move them for webpack 5
/** @type {SyncHook<string, Module>} */
normalModuleLoader: new SyncHook(["loaderContext", "module"]),
/** @type {SyncBailHook<Chunk[]>} */
optimizeExtractedChunksBasic: new SyncBailHook(["chunks"]),
/** @type {SyncBailHook<Chunk[]>} */
optimizeExtractedChunks: new SyncBailHook(["chunks"]),
/** @type {SyncBailHook<Chunk[]>} */
optimizeExtractedChunksAdvanced: new SyncBailHook(["chunks"]),
/** @type {SyncHook<Chunk[]>} */
afterOptimizeExtractedChunks: new SyncHook(["chunks"])
};
this._pluginCompat.tap("Compilation", options => {
@ -208,7 +385,9 @@ class Compilation extends Tapable {
break;
}
});
/** @type {string=} */
this.name = undefined;
/** @type {Compiler} */
this.compiler = compiler;
this.resolverFactory = compiler.resolverFactory;
this.inputFileSystem = compiler.inputFileSystem;
@ -216,6 +395,7 @@ class Compilation extends Tapable {
const options = (this.options = compiler.options);
this.outputOptions = options && options.output;
/** @type {boolean=} */
this.bail = options && options.bail;
this.profile = options && options.profile;
this.performance = options && options.performance;
@ -237,34 +417,56 @@ class Compilation extends Tapable {
this.semaphore = new Semaphore(options.parallelism || 100);
this.entries = [];
/** @private @type {{name: string, request: string, module: Module}[]} */
this._preparedEntrypoints = [];
this.entrypoints = new Map();
/** @type {Chunk[]} */
this.chunks = [];
/** @type {ChunkGroup[]} */
this.chunkGroups = [];
/** @type {Map<string, ChunkGroup>} */
this.namedChunkGroups = new Map();
/** @type {Map<string, Chunk>} */
this.namedChunks = new Map();
/** @type {Module[]} */
this.modules = [];
/** @private @type {Map<string, Module>} */
this._modules = new Map();
this.cache = null;
this.records = null;
/** @type {number=} */
this.nextFreeModuleIndex = undefined;
/** @type {number=} */
this.nextFreeModuleIndex2 = undefined;
/** @type {string[]} */
this.additionalChunkAssets = [];
/** @type {CompilationAssets} */
this.assets = {};
/** @type {WebpackError[]} */
this.errors = [];
/** @type {WebpackError[]} */
this.warnings = [];
/** @type {Compilation[]} */
this.children = [];
/** @type {Map<DepConstructor, ModuleFactory>} */
this.dependencyFactories = new Map();
/** @type {Map<DepConstructor|string, DependencyTemplate|string>} */
this.dependencyTemplates = new Map();
this.dependencyTemplates.set("hash", "");
this.childrenCounters = {};
/** @type {Set<number>} */
this.usedChunkIds = null;
/** @type {Set<number>} */
this.usedModuleIds = null;
/** @type {Map<string, number>=} */
this.fileTimestamps = undefined;
/** @type {Map<string, number>=} */
this.contextTimestamps = undefined;
/** @type {Set} */
this.compilationDependencies = undefined;
/** @private @type {Map<Module, any>} */
this._buildingModules = new Map();
/** @private @type {Map<Module, any>} */
this._rebuildingModules = new Map();
}
@ -272,6 +474,12 @@ class Compilation extends Tapable {
return new Stats(this);
}
/**
* @param {Module} module module to be added that was created
* @param {any=} cacheGroup cacheGroup it is apart of
* @returns {{module: Module, issuer: boolean, build: boolean, dependencies: boolean}} returns meta about whether or not the module had built
* had an issuer, or any dependnecies
*/
addModule(module, cacheGroup) {
const identifier = module.identifier();
const alreadyAddedModule = this._modules.get(identifier);
@ -327,15 +535,30 @@ class Compilation extends Tapable {
};
}
/**
* Fetches a module from a compilation by its identifier
* @param {Module} module the module provided
* @returns {Module} the module requested
*/
getModule(module) {
const identifier = module.identifier();
return this._modules.get(identifier);
}
/**
* Attempts to search for a module by its identifier
* @param {string} identifier identifier (usually path) for module
* @returns {Module|undefined} attempt to search for module and return it, else undefined
*/
findModule(identifier) {
return this._modules.get(identifier);
}
/**
* @param {Module} module module with its callback list
* @param {any} callback the callback function
* @returns {void}
*/
waitForBuildingFinished(module, callback) {
let callbackList = this._buildingModules.get(module);
if (callbackList) {
@ -345,6 +568,16 @@ class Compilation extends Tapable {
}
}
/**
* Builds the module object
*
* @param {Module} module module to be built
* @param {boolean} optional optional flag
* @param {Module=} origin origin module this module build was requested from
* @param {Dependency[]=} dependencies optional dependencies from the module to be built
* @param {any} thisCallback the callback
* @returns {any} returns the callback function with results
*/
buildModule(module, optional, origin, dependencies, thisCallback) {
let callbackList = this._buildingModules.get(module);
if (callbackList) {
@ -396,6 +629,11 @@ class Compilation extends Tapable {
);
}
/**
* @param {Module} module to be processed for deps
* @param {*} callback callback to be triggered
* @returns {void}
*/
processModuleDependencies(module, callback) {
const dependencies = new Map();
@ -457,6 +695,15 @@ class Compilation extends Tapable {
);
}
/**
* @param {Module} module module to add deps to
* @param {SortedDependency[]} dependencies set of sorted dependencies to iterate through
* @param {(boolean|null)=} bail whether to bail or not
* @param {any} cacheGroup optional cacheGroup
* @param {boolean} recursive whether it is recursive traversal
* @param {function} callback callback for when dependencies are finished being added
* @returns {void}
*/
addModuleDependencies(
module,
dependencies,
@ -623,6 +870,14 @@ class Compilation extends Tapable {
);
}
/**
*
* @param {string} context context string path
* @param {Dependency} dependency dependency used to create Module chain
* @param {OnModuleCallback} onModule function invoked on modules creation
* @param {ModuleChainCallback} callback callback for when module chain is complete
* @returns {void|never} will throw if dependency instance is not a valid Dependency
*/
_addModuleChain(context, dependency, onModule, callback) {
const start = this.profile && Date.now();
const currentProfile = this.profile && {};
@ -644,7 +899,10 @@ class Compilation extends Tapable {
) {
throw new Error("Parameter 'dependency' must be a Dependency");
}
// @ts-ignore
// TODO: Not sure how to handle the
// dependency.constructor error,
// maybe I typed it wrong
const moduleFactory = this.dependencyFactories.get(dependency.constructor);
if (!moduleFactory) {
throw new Error(
@ -655,6 +913,7 @@ class Compilation extends Tapable {
}
this.semaphore.acquire(() => {
/** @type {ModuleFactory} */
moduleFactory.create(
{
contextInfo: {
@ -731,12 +990,25 @@ class Compilation extends Tapable {
});
}
/**
*
* @param {string} context context path for entry
* @param {PossibleEntryDependencies} entry entry dependency being created
* @param {string} name name of entry
* @param {ModuleCallback} callback callback function
* @returns {void} returns
*/
addEntry(context, entry, name, callback) {
const slot = {
name: name,
request: entry.request,
request: null,
module: null
};
if (entry instanceof SingleEntryDependency) {
slot.request = entry.request;
}
this._preparedEntrypoints.push(slot);
this._addModuleChain(
context,
@ -760,6 +1032,12 @@ class Compilation extends Tapable {
);
}
/**
* @param {string} context context path string
* @param {PossibleEntryDependencies} dependency dep used to create module
* @param {ModuleCallback} callback module callback sending module up a level
* @returns {void}
*/
prefetch(context, dependency, callback) {
this._addModuleChain(
context,
@ -831,6 +1109,10 @@ class Compilation extends Tapable {
}
}
/**
* @param {CompilationSealCallback} callback signals when the seal method is finishes
* @returns {void}
*/
seal(callback) {
this.hooks.seal.call();
@ -970,10 +1252,19 @@ class Compilation extends Tapable {
});
}
/**
* @param {Module[]} modules the modules array on compilation to perform the sort for
* @returns {void}
*/
sortModules(modules) {
modules.sort(byIndexOrIdentifier);
}
/**
* @param {Module} module moulde to report from
* @param {DependenciesBlock[]} blocks blocks to report from
* @returns {void}
*/
reportDependencyErrorsAndWarnings(module, blocks) {
for (let indexBlock = 0; indexBlock < blocks.length; indexBlock++) {
const block = blocks[indexBlock];
@ -1006,6 +1297,13 @@ class Compilation extends Tapable {
}
}
/**
* @param {any} groupOptions options provided for group
* @param {Module} module module in question
* @param {SourceLocation} loc source location reference
* @param {string} request request string
* @returns {ChunkGroup} the chunk group added inside
*/
addChunkInGroup(groupOptions, module, loc, request) {
if (typeof groupOptions === "string") {
groupOptions = { name: groupOptions };
@ -1034,6 +1332,13 @@ class Compilation extends Tapable {
return chunkGroup;
}
/**
* This method first looks to see if a name is provided for a new chunk,
* and first looks to see if any named chunks already exist and reuse that chunk instead.
*
* @param {string=} name optional chunk name to be provided
* @returns {Chunk} create a chunk (invoked during seal event)
*/
addChunk(name) {
if (name) {
const chunk = this.namedChunks.get(name);
@ -1049,7 +1354,15 @@ class Compilation extends Tapable {
return chunk;
}
/**
* @param {Module} module index will be assigned to this module
* @returns {void}
*/
assignIndex(module) {
/**
* @param {Module} module assign index to the module
* @returns {void}
*/
const assignIndexToModule = module => {
// enter module
if (typeof module.index !== "number") {
@ -1069,11 +1382,23 @@ class Compilation extends Tapable {
}
};
/**
* @param {DependenciesBlock} block to assign index to
* @returns {void}
*/
const assignIndexToDependencyBlock = block => {
let allDependencies = [];
/**
* @param {Dependency} d iterator over dependencies
* @returns {number} returned push function value (side-effect)
*/
const iteratorDependency = d => allDependencies.push(d);
/**
* @param {DependenciesBlock} b blocks to iterate
* @returns {number} returned push value from queue (side-effect)
*/
const iteratorBlock = b =>
queue.push(() => assignIndexToDependencyBlock(b));
@ -1104,6 +1429,10 @@ class Compilation extends Tapable {
}
];
/**
* @param {(Dependency|DependenciesBlockVariable)[]} d all dependencies of a block being added to queue
* @returns {void}
*/
const iteratorAllDependencies = d => {
queue.push(() => assignIndexToDependency(d));
};
@ -1113,12 +1442,20 @@ class Compilation extends Tapable {
}
}
/**
* @param {Module} module module to assign depth
* @returns {void}
*/
assignDepth(module) {
const queue = new Set([module]);
let depth;
module.depth = 0;
/**
* @param {Module} module module for processeing
* @returns {void}
*/
const enqueueJob = module => {
const d = module.depth;
if (typeof d === "number" && d <= depth) return;
@ -1126,12 +1463,20 @@ class Compilation extends Tapable {
module.depth = depth;
};
const assignDepthToDependency = (dependency, depth) => {
/**
* @param {Dependency} dependency dependency to assign depth to
* @returns {void}
*/
const assignDepthToDependency = dependency => {
if (dependency.module) {
enqueueJob(dependency.module);
}
};
/**
* @param {DependenciesBlock} block block to assign depth to
* @returns {void}
*/
const assignDepthToDependencyBlock = block => {
if (block.variables) {
iterationBlockVariable(block.variables, assignDepthToDependency);
@ -1156,6 +1501,10 @@ class Compilation extends Tapable {
}
// This method creates the Chunk graph from the Module graph
/**
* @param {TODO} inputChunkGroups input chunkGroups to be processed
* @returns {void}
*/
processDependenciesBlocksForChunkGroups(inputChunkGroups) {
// Process is splitting into two parts:
// Part one traverse the module graph and builds a very basic chunks graph
@ -1169,8 +1518,13 @@ class Compilation extends Tapable {
const allCreatedChunkGroups = new Set();
// PREPARE
/** @type {Map<DependenciesBlock, {modules: Set<Module>, blocks: AsyncDependenciesBlock[]}>} */
const blockInfoMap = new Map();
/**
* @param {Dependency} d dependency to iterate over
* @returns {void}
*/
const iteratorDependency = d => {
// We skip Dependencies without Reference
const ref = d.getReference();
@ -1190,12 +1544,23 @@ class Compilation extends Tapable {
blockInfoModules.add(refModule);
};
/**
* @param {AsyncDependenciesBlock} b blocks to prepare
* @returns {void}
*/
const iteratorBlockPrepare = b => {
blockInfoBlocks.push(b);
blockQueue.push(b);
};
let block, blockQueue, blockInfoModules, blockInfoBlocks;
/** @type {DependenciesBlock} */
let block;
/** @type {DependenciesBlock[]} */
let blockQueue;
/** @type {Set<Module>} */
let blockInfoModules;
/** @type {AsyncDependenciesBlock[]} */
let blockInfoBlocks;
for (const module of this.modules) {
blockQueue = [module];
while (blockQueue.length > 0) {
@ -1224,10 +1589,12 @@ class Compilation extends Tapable {
}
// PART ONE
/** @type {Map<DependenciesBlock, ChunkGroup>} */
const blockChunkGroups = new Map();
// Start with the provided modules/chunks
/** @type {{block: DependenciesBlock, module: Module, chunk: Chunk, chunkGroup: ChunkGroup}[]} */
const queue = inputChunkGroups.map(chunkGroup => ({
block: chunkGroup.chunks[0].entryModule,
module: chunkGroup.chunks[0].entryModule,
@ -1235,9 +1602,18 @@ class Compilation extends Tapable {
chunkGroup
}));
let module, chunk, chunkGroup;
/** @type {Module} */
let module;
/** @type {Chunk} */
let chunk;
/** @type {ChunkGroup} */
let chunkGroup;
// For each async Block in graph
/**
* @param {AsyncDependenciesBlock} b iterating over each Async DepBlock
* @returns {void}
*/
const iteratorBlock = b => {
// 1. We create a chunk for this Block
// but only once (blockChunkGroups map)
@ -1315,9 +1691,10 @@ class Compilation extends Tapable {
}
// PART TWO
/** @type {Set<Module>} */
let availableModules;
let newAvailableModules;
/** @type {Queue<AvailableModulesChunkGroupMapping>} */
const queue2 = new Queue(
inputChunkGroups.map(chunkGroup => ({
chunkGroup,
@ -1325,7 +1702,13 @@ class Compilation extends Tapable {
}))
);
// Helper function to check if all modules of a chunk are available
/**
* Helper function to check if all modules of a chunk are available
*
* @param {ChunkGroup} chunkGroup the chunkGroup to scan
* @param {Set<Module>} availableModules the comparitor set
* @returns {boolean} return true if all modules of a chunk are available
*/
const areModulesAvailable = (chunkGroup, availableModules) => {
for (const chunk of chunkGroup.chunks) {
for (const module of chunk.modulesIterable) {
@ -1336,14 +1719,18 @@ class Compilation extends Tapable {
};
// For each edge in the basic chunk graph
/**
* @param {TODO} dep the dependency used for filtering
* @returns {boolean} used to filter "edges" (aka Dependencies) that were pointing
* to modules that are already available. Also filters circular dependencies in the chunks graph
*/
const filterFn = dep => {
// Filter egdes that are not needed because all modules are already available
// This also filters circular dependencies in the chunks graph
const depChunkGroup = dep.chunkGroup;
if (areModulesAvailable(depChunkGroup, newAvailableModules)) return false; // break all modules are already available
return true;
};
/** @type {Map<ChunkGroup, Set<Module>>} */
const minAvailableModulesMap = new Map();
// Iterative traversing of the basic chunk graph
@ -1425,6 +1812,12 @@ class Compilation extends Tapable {
}
}
/**
*
* @param {Module} module module relationship for removal
* @param {DependenciesBlock} block //TODO: good description
* @returns {void}
*/
removeReasonsOfDependencyBlock(module, block) {
const iteratorDependency = d => {
if (!d.module) {
@ -1452,6 +1845,11 @@ class Compilation extends Tapable {
}
}
/**
* @param {Module} module module to patch tie
* @param {Chunk} chunk chunk to patch tie
* @returns {void}
*/
patchChunksAfterReasonRemoval(module, chunk) {
if (!module.hasReasons()) {
this.removeReasonsOfDependencyBlock(module, module);
@ -1463,6 +1861,12 @@ class Compilation extends Tapable {
}
}
/**
*
* @param {DependenciesBlock} block block tie for Chunk
* @param {Chunk} chunk chunk to remove from dep
* @returns {void}
*/
removeChunkFromDependencies(block, chunk) {
const iteratorDependency = d => {
if (!d.module) {
@ -1473,12 +1877,15 @@ class Compilation extends Tapable {
const blocks = block.blocks;
for (let indexBlock = 0; indexBlock < blocks.length; indexBlock++) {
const chunks = blocks[indexBlock].chunks;
// Grab all chunks from the first Block's AsyncDepBlock
const chunks = blocks[indexBlock].chunkGroup.chunks;
// For each chunk in chunkGroup
for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
const blockChunk = chunks[indexChunk];
chunk.removeChunk(blockChunk);
blockChunk.removeParent(chunk);
this.removeChunkFromDependencies(chunks, blockChunk);
const iteratedChunk = chunks[indexChunk];
block.chunkGroup.removeChunk(iteratedChunk);
block.chunkGroup.removeParent(iteratedChunk);
// Recurse
this.removeChunkFromDependencies(block, iteratedChunk);
}
}
@ -1539,6 +1946,7 @@ class Compilation extends Tapable {
}
applyChunkIds() {
/** @type {Set<number|string>} */
const usedIds = new Set();
// Get used ids from usedChunkIds property (i. e. from records)
@ -1568,11 +1976,15 @@ class Compilation extends Tapable {
// Calculate maximum assigned chunk id
let nextFreeChunkId = -1;
for (const id of usedIds) {
if (typeof id !== "number") {
continue;
}
nextFreeChunkId = Math.max(nextFreeChunkId, id);
}
nextFreeChunkId++;
// Determine free chunk ids from 0 to maximum
/** @type {number[]} */
const unusedIds = [];
if (nextFreeChunkId > 0) {
let index = nextFreeChunkId;
@ -1630,6 +2042,16 @@ class Compilation extends Tapable {
chunks[indexChunk].sortItems(true);
}
/**
* Used to sort errors and warnings in compilation. this.warnings, and
* this.errors contribute to the compilation hash and therefore shoudl be
* updated whenever other references (having a chunk id) are sorted. This preserves the hash
* integrity
*
* @param {WebpackError} a first WebpackError instance (including subclasses)
* @param {WebpackError} b second WebpackError instance (including subclasses)
* @returns {-1|0|1} sort order index
*/
const byMessage = (a, b) => {
const ma = `${a.message}`;
const mb = `${b.message}`;
@ -1746,6 +2168,10 @@ class Compilation extends Tapable {
this.hash = this.fullHash.substr(0, hashDigestLength);
}
/**
* @param {TODO} update //TODO (update hash function?)
* @returns {void}
*/
modifyHash(update) {
const outputOptions = this.outputOptions;
const hashFunction = outputOptions.hashFunction;
@ -1839,12 +2265,27 @@ class Compilation extends Tapable {
}
}
/**
* @param {string} filename used to get asset path with hash
* @param {TODO=} data // TODO: figure out this param type
* @returns {TODO} figure out this return type
*/
getPath(filename, data) {
data = data || {};
data.hash = data.hash || this.hash;
return this.mainTemplate.getAssetPath(filename, data);
}
/**
* This function allows you to run another instance of webpack inside of webpack however as
* a child with different settings and configurations (if desired) applied. It copies all hooks, plugins
* from parent (or top level compiler) and creates a child Compilation
*
* @param {string} name name of the child compiler
* @param {TODO} outputOptions // Need to convert config schema to types for this
* @param {Plugin[]} plugins webpack plugins that will be applied
* @returns {Compiler} creates a child Compiler instance
*/
createChildCompiler(name, outputOptions, plugins) {
const idx = this.childrenCounters[name] || 0;
this.childrenCounters[name] = idx + 1;
@ -1858,6 +2299,7 @@ class Compilation extends Tapable {
}
checkConstraints() {
/** @type {Set<number|string>} */
const usedIds = new Set();
const modules = this.modules;

View File

@ -26,38 +26,92 @@ const RequestShortener = require("./RequestShortener");
const { makePathsRelative } = require("./util/identifier");
const ConcurrentCompilationError = require("./ConcurrentCompilationError");
/**
* @typedef {Object} CompilationParams
* @property {NormalModuleFactory} normalModuleFactory
* @property {ContextModuleFactory} contextModuleFactory
* @property {Set<any>} compilationDependencies
*/
/** @typedef {string|string[]} EntryValues */
/** @typedef {{[entryKey: string]: EntryValues}} EntryOptionValues */
/** @typedef {(() => EntryOptionValues) | EntryOptionValues | EntryValues} EntryOptions */
class Compiler extends Tapable {
constructor(context) {
super();
this.hooks = {
/** @type {SyncBailHook<Compilation>} */
shouldEmit: new SyncBailHook(["compilation"]),
/** @type {AsyncSeriesHook<Stats>} */
done: new AsyncSeriesHook(["stats"]),
/** @type {AsyncSeriesHook<>} */
additionalPass: new AsyncSeriesHook([]),
beforeRun: new AsyncSeriesHook(["compilation"]),
run: new AsyncSeriesHook(["compilation"]),
/** @type {AsyncSeriesHook<Compiler>} */
beforeRun: new AsyncSeriesHook(["compiler"]),
/** @type {AsyncSeriesHook<Compiler>} */
run: new AsyncSeriesHook(["compiler"]),
/** @type {AsyncSeriesHook<Compilation>} */
emit: new AsyncSeriesHook(["compilation"]),
/** @type {AsyncSeriesHook<Compilation>} */
afterEmit: new AsyncSeriesHook(["compilation"]),
/** @type {SyncHook<Compilation, CompilationParams>} */
thisCompilation: new SyncHook(["compilation", "params"]),
/** @type {SyncHook<Compilation, CompilationParams>} */
compilation: new SyncHook(["compilation", "params"]),
/** @type {SyncHook<NormalModuleFactory>} */
normalModuleFactory: new SyncHook(["normalModuleFactory"]),
/** @type {SyncHook<ContextModuleFactory>} */
contextModuleFactory: new SyncHook(["contextModulefactory"]),
/** @type {AsyncSeriesHook<CompilationParams>} */
beforeCompile: new AsyncSeriesHook(["params"]),
/** @type {SyncHook<CompilationParams>} */
compile: new SyncHook(["params"]),
/** @type {AsyncParallelHook<Compilation>} */
make: new AsyncParallelHook(["compilation"]),
/** @type {AsyncSeriesHook<Compilation>} */
afterCompile: new AsyncSeriesHook(["compilation"]),
/** @type {AsyncSeriesHook<Compiler>} */
watchRun: new AsyncSeriesHook(["compiler"]),
/** @type {SyncHook<Error>} */
failed: new SyncHook(["error"]),
/** @type {SyncHook<string, string>} */
invalid: new SyncHook(["filename", "changeTime"]),
/** @type {SyncHook} */
watchClose: new SyncHook([]),
// TODO the following hooks are weirdly located here
// TODO move them for webpack 5
/** @type {SyncHook} */
environment: new SyncHook([]),
/** @type {SyncHook} */
afterEnvironment: new SyncHook([]),
/** @type {SyncHook<Compiler>} */
afterPlugins: new SyncHook(["compiler"]),
/** @type {SyncHook<Compiler>} */
afterResolvers: new SyncHook(["compiler"]),
/** @type {SyncBailHook<string, EntryOptions>} */
entryOption: new SyncBailHook(["context", "entry"])
};
this._pluginCompat.tap("Compiler", options => {
switch (options.name) {
case "additional-pass":
@ -74,19 +128,26 @@ class Compiler extends Tapable {
}
});
/** @type {string=} */
this.name = undefined;
/** @type {Compilation=} */
this.parentCompilation = undefined;
/** @type {string} */
this.outputPath = "";
this.outputFileSystem = null;
this.inputFileSystem = null;
/** @type {string|null} */
this.recordsInputPath = null;
/** @type {string|null} */
this.recordsOutputPath = null;
this.records = {};
/** @type {Map<string, number>} */
this.fileTimestamps = new Map();
/** @type {Map<string, number>} */
this.contextTimestamps = new Map();
/** @type {ResolverFactory} */
this.resolverFactory = new ResolverFactory();
// TODO remove in webpack 5
@ -135,6 +196,7 @@ class Compiler extends Tapable {
this.requestShortener = new RequestShortener(context);
/** @type {boolean} */
this.running = false;
}

View File

@ -8,6 +8,7 @@ const util = require("util");
const { OriginalSource, RawSource } = require("webpack-sources");
const Module = require("./Module");
const AsyncDependenciesBlock = require("./AsyncDependenciesBlock");
const ModuleDependency = require("./dependencies/ModuleDependency");
const Template = require("./Template");
class ContextModule extends Module {
@ -645,10 +646,10 @@ webpackEmptyAsyncContext.id = ${JSON.stringify(id)};`;
const initialSize = 160;
// if we dont have dependencies we stop here.
return this.dependencies.reduce(
(size, dependency) => size + 5 + dependency.userRequest.length,
initialSize
);
return this.dependencies.reduce((size, dependency) => {
if (dependency instanceof ModuleDependency)
return size + 5 + dependency.userRequest.length;
}, initialSize);
}
}

View File

@ -11,6 +11,8 @@ const WebpackMissingModule = require("./dependencies/WebpackMissingModule");
const DelegatedSourceDependency = require("./dependencies/DelegatedSourceDependency");
const DelegatedExportsDependency = require("./dependencies/DelegatedExportsDependency");
/** @typedef {import("./dependencies/ModuleDependency")} ModuleDependency */
class DelegatedModule extends Module {
constructor(sourceRequest, data, type, userRequest, originalRequest) {
super("javascript/dynamic", null);
@ -48,6 +50,8 @@ class DelegatedModule extends Module {
this.built = true;
this.buildMeta = Object.assign({}, this.delegateData.buildMeta);
this.buildInfo = {};
/** @type {ModuleDependency[]=} */
this.dependencies = [];
this.addDependency(new DelegatedSourceDependency(this.sourceRequest));
this.addDependency(
new DelegatedExportsDependency(this, this.delegateData.exports || true)

View File

@ -6,23 +6,46 @@
const DependenciesBlockVariable = require("./DependenciesBlockVariable");
/** @typedef {import("./ChunkGroup")} ChunkGroup */
/**
* @typedef {import("./ChunkGroup")} ChunkGroup
* @typedef {import("./Dependency")} Dependency
* @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock
* @typedef {import("./DependenciesBlockVariable")} DependenciesBlockVariable
* @typedef {(d: Dependency) => boolean} DependencyFilterFunction
* @typedef {import("crypto").Hash} Hash
*/
class DependenciesBlock {
constructor() {
/** @type {Dependency[]} */
this.dependencies = [];
/** @type {AsyncDependenciesBlock[]} */
this.blocks = [];
/** @type {DependenciesBlockVariable[]} */
this.variables = [];
// TODO remove this line, it's wrong
/** @type {ChunkGroup=} */
this.chunkGroup = undefined;
}
/**
* Adds a DependencyBlock to DependencyBlock relationship.
* This is used for when a Module has a AsyncDependencyBlock tie (for code-splitting)
*
* @param {AsyncDependenciesBlock} block block being added
* @returns {void}
*/
addBlock(block) {
this.blocks.push(block);
block.parent = this;
}
/**
* @param {string} name name of dependency
* @param {string} expression expression string for variable
* @param {Dependency[]} dependencies dependency instances tied to variable
* @returns {void}
*/
addVariable(name, expression, dependencies) {
for (let v of this.variables) {
if (v.name === name && v.expression === expression) {
@ -34,15 +57,28 @@ class DependenciesBlock {
);
}
/**
* @param {Dependency} dependency dependency being tied to block.
* This is an "edge" pointing to another "node" on module graph.
* @returns {void}
*/
addDependency(dependency) {
this.dependencies.push(dependency);
}
/**
* @param {Dependency} dependency dependency being removed
* @returns {void}
*/
removeDependency(dependency) {
const idx = this.dependencies.indexOf(dependency);
if (idx >= 0) this.dependencies.splice(idx, 1);
}
/**
* @param {Hash} hash the hash used to track dependencies, from "crypto" module
* @returns {void}
*/
updateHash(hash) {
for (const dep of this.dependencies) dep.updateHash(hash);
for (const block of this.blocks) block.updateHash(hash);
@ -59,6 +95,10 @@ class DependenciesBlock {
for (const block of this.blocks) block.unseal();
}
/**
* @param {DependencyFilterFunction} filter filter function for dependencies, gets passed all dependency ties from current instance
* @returns {boolean} returns boolean for filter
*/
hasDependencies(filter) {
if (filter) {
for (const dep of this.dependencies) {

View File

@ -6,13 +6,29 @@
const { RawSource, ReplaceSource } = require("webpack-sources");
/** @typedef {import("./Dependency")} Dependency */
/** @typedef {import("./RuntimeTemplate")} RuntimeTemplate */
/** @typedef {import("crypto").Hash} Hash */
/** @typedef {(d: Dependency) => boolean} DependencyFilterFunction */
/** @typedef {Map<Dependency["constructor"], any>} DependencyFactoryConstruction */
class DependenciesBlockVariable {
/**
* Creates an instance of DependenciesBlockVariable.
* @param {string} name name of DependenciesBlockVariable
* @param {string} expression expression string
* @param {Dependency[]=} dependencies dependencies tied to this varaiable
*/
constructor(name, expression, dependencies) {
this.name = name;
this.expression = expression;
this.dependencies = dependencies || [];
}
/**
* @param {Hash} hash hash for instance to update
* @returns {void}
*/
updateHash(hash) {
hash.update(this.name);
hash.update(this.expression);
@ -21,8 +37,13 @@ class DependenciesBlockVariable {
}
}
/**
* @param {DependencyFactoryConstruction} dependencyTemplates Dependency constructors and templates Map.
* @param {RuntimeTemplate} runtimeTemplate runtimeTemplate to generate expression souce
* @returns {ReplaceSource} returns constructed source for expression via templates
*/
expressionSource(dependencyTemplates, runtimeTemplate) {
const source = new ReplaceSource(new RawSource(this.expression));
const source = new ReplaceSource(new RawSource(this.expression), null);
for (const dep of this.dependencies) {
const template = dependencyTemplates.get(dep.constructor);
if (!template)

View File

@ -7,8 +7,11 @@
const compareLocations = require("./compareLocations");
const DependencyReference = require("./dependencies/DependencyReference");
/** @typedef {import("./Module")} Module */
class Dependency {
constructor() {
/** @type {Module|null} */
this.module = null;
this.weak = false;
this.optional = false;

View File

@ -10,12 +10,25 @@ const MultiModuleFactory = require("./MultiModuleFactory");
const MultiEntryPlugin = require("./MultiEntryPlugin");
const SingleEntryPlugin = require("./SingleEntryPlugin");
/** @typedef {import("./Compiler")} Compiler */
/** @typedef {string|string[]} EntryValues */
/** @typedef {{[entryKey: string]: EntryValues | EntryValues}} EntryOptionValues */
/** @typedef {(() => (EntryOptionValues|EntryValues))} EntryOptionValuesFunction */
class DynamicEntryPlugin {
/**
* @param {string} context the context path
* @param {EntryOptionValuesFunction} entry the entry value
*/
constructor(context, entry) {
this.context = context;
this.entry = entry;
}
/**
* @param {Compiler} compiler the compiler instance
* @returns {void}
*/
apply(compiler) {
compiler.hooks.compilation.tap(
"DynamicEntryPlugin",
@ -36,6 +49,11 @@ class DynamicEntryPlugin {
compiler.hooks.make.tapAsync(
"DynamicEntryPlugin",
(compilation, callback) => {
/**
* @param {string|string[]} entry entry value or array of entry values
* @param {string} name name of entry
* @returns {Promise<any>} returns the promise resolving the Compilation#addEntry function
*/
const addEntry = (entry, name) => {
const dep = DynamicEntryPlugin.createDependency(entry, name);
return new Promise((resolve, reject) => {
@ -63,7 +81,11 @@ class DynamicEntryPlugin {
}
module.exports = DynamicEntryPlugin;
/**
* @param {string|string[]} entry entry value or array of entry paths
* @param {string} name name of entry
* @returns {SingleEntryDependency|MultiEntryDependency} returns dep
*/
DynamicEntryPlugin.createDependency = (entry, name) => {
if (Array.isArray(entry))
return MultiEntryPlugin.createDependency(entry, name);

View File

@ -8,6 +8,14 @@ const SingleEntryPlugin = require("./SingleEntryPlugin");
const MultiEntryPlugin = require("./MultiEntryPlugin");
const DynamicEntryPlugin = require("./DynamicEntryPlugin");
/** @typedef {import("./Compiler")} Compiler */
/**
* @param {string} context context path
* @param {string | string[]} item entry array or single path
* @param {string} name entry key name
* @returns {SingleEntryPlugin | MultiEntryPlugin} returns either a single or multi entry plugin
*/
const itemToPlugin = (context, item, name) => {
if (Array.isArray(item)) {
return new MultiEntryPlugin(context, item, name);
@ -16,6 +24,10 @@ const itemToPlugin = (context, item, name) => {
};
module.exports = class EntryOptionPlugin {
/**
* @param {Compiler} compiler the compiler instance one is tapping into
* @returns {void}
*/
apply(compiler) {
compiler.hooks.entryOption.tap("EntryOptionPlugin", (context, entry) => {
if (typeof entry === "string" || Array.isArray(entry)) {

View File

@ -18,7 +18,7 @@ class JavascriptGenerator {
return new RawSource("throw new Error('No source available');");
}
const source = new ReplaceSource(originalSource);
const source = new ReplaceSource(originalSource, null);
this.sourceBlock(
module,

View File

@ -11,8 +11,14 @@ const ModuleReason = require("./ModuleReason");
const SortableSet = require("./util/SortableSet");
const Template = require("./Template");
<<<<<<< HEAD
/** @typedef {import("./Chunk")} Chunk */
/** @typedef {import("./RequestShortener")} RequestShortener */
=======
/** @typedef {typeof import("./Chunk")} Chunk */
/** @typedef {typeof import("./RequestShortener")} RequestShortener */
/** @typedef {import("./WebpackError")} WebpackError */
>>>>>>> 206d1a6... chore(types): add Compiler and Compilation type support
const EMPTY_RESOLVE_OPTIONS = {};
@ -47,15 +53,15 @@ class Module extends DependenciesBlock {
this.renderedHash = undefined;
// Info from Factory
/** @type {object} */
/** @type {any} */
this.resolveOptions = EMPTY_RESOLVE_OPTIONS;
/** @type {object} */
this.factoryMeta = {};
// Info from Build
/** @type {Error[]} */
/** @type {WebpackError[]} */
this.warnings = [];
/** @type {Error[]} */
/** @type {WebpackError[]} */
this.errors = [];
/** @type {object} */
this.buildMeta = undefined;

View File

@ -8,13 +8,25 @@ const MultiEntryDependency = require("./dependencies/MultiEntryDependency");
const SingleEntryDependency = require("./dependencies/SingleEntryDependency");
const MultiModuleFactory = require("./MultiModuleFactory");
module.exports = class MultiEntryPlugin {
/** @typedef {import("./Compiler")} Compiler */
class MultiEntryPlugin {
/**
* The MultiEntryPlugin is invoked whenever this.options.entry value is an array of paths
* @param {string} context context path
* @param {string[]} entries array of entry paths
* @param {string} name entry key name
*/
constructor(context, entries, name) {
this.context = context;
this.entries = entries;
this.name = name;
}
/**
* @param {Compiler} compiler the compiler instance
* @returns {void}
*/
apply(compiler) {
compiler.hooks.compilation.tap(
"MultiEntryPlugin",
@ -43,6 +55,11 @@ module.exports = class MultiEntryPlugin {
);
}
/**
* @param {string[]} entries each entry path string
* @param {string} name name of the entry
* @return {MultiEntryDependency} returns a constructed Dependency
*/
static createDependency(entries, name) {
return new MultiEntryDependency(
entries.map((e, idx) => {
@ -55,4 +72,6 @@ module.exports = class MultiEntryPlugin {
name
);
}
};
}
module.exports = MultiEntryPlugin;

View File

@ -218,6 +218,8 @@ class NormalModule extends Module {
}
if (Buffer.isBuffer(source)) {
// @ts-ignore
// TODO We need to fix @types/webpack-sources to allow RawSource to take a Buffer | string
return new RawSource(source);
}

View File

@ -5,13 +5,27 @@
"use strict";
const SingleEntryDependency = require("./dependencies/SingleEntryDependency");
/** @typedef {import("./Compiler")} Compiler */
class SingleEntryPlugin {
/**
* An entry plugin which will handle
* creation of the SingleEntryDependency
*
* @param {string} context context path
* @param {string} entry entry path
* @param {string} name entry key name
*/
constructor(context, entry, name) {
this.context = context;
this.entry = entry;
this.name = name;
}
/**
* @param {Compiler} compiler the compiler instance
* @returns {void}
*/
apply(compiler) {
compiler.hooks.compilation.tap(
"SingleEntryPlugin",

View File

@ -4,11 +4,16 @@
*/
"use strict";
module.exports = class WebpackError extends Error {
class WebpackError extends Error {
constructor(message) {
super(message);
this.details = undefined;
this.missing = undefined;
this.origin = undefined;
this.dependencies = undefined;
this.module = undefined;
this.message = undefined;
Error.captureStackTrace(this, this.constructor);
}
@ -16,4 +21,6 @@ module.exports = class WebpackError extends Error {
inspect() {
return this.stack + (this.details ? `\n${this.details}` : "");
}
};
}
module.exports = WebpackError;

View File

@ -4,7 +4,16 @@
*/
"use strict";
/** @typedef {import('../Module')} Module */
class DependencyReference {
/**
* Creates an instance of DependencyReference.
* @param {Module} module module there reference comes from
* @param {string[]|boolean} importedNames imported names or boolean
* @param {boolean} weak is weak reference or not
* @memberof DependencyReference
*/
constructor(module, importedNames, weak) {
this.module = module;
// true: full object

View File

@ -5,6 +5,7 @@
"use strict";
const LoaderDependency = require("./LoaderDependency");
const NormalModule = require("../NormalModule");
class LoaderPlugin {
apply(compiler) {
@ -51,11 +52,10 @@ class LoaderPlugin {
err => {
compilation.semaphore.acquire(() => {
if (err) return callback(err);
if (!dep.module)
return callback(new Error("Cannot load the module"));
if (dep.module.error) return callback(dep.module.error);
if (dep.module instanceof NormalModule && dep.module.error)
return callback(dep.module.error);
if (!dep.module._source)
throw new Error(
"The module created for a LoaderDependency must have a property _source"

View File

@ -6,6 +6,9 @@
const Dependency = require("../Dependency");
class ModuleDependency extends Dependency {
/**
* @param {string} request request path which needs resolving
*/
constructor(request) {
super();
this.request = request;

View File

@ -3,9 +3,14 @@
Author Tobias Koppers @sokra
*/
"use strict";
/** @typedef {import("./SingleEntryDependency")} SingleEntryDependency */
const Dependency = require("../Dependency");
class MultiEntryDependency extends Dependency {
/**
* @param {SingleEntryDependency[]} dependencies an array of SingleEntryDependencies
* @param {string} name entry name
*/
constructor(dependencies, name) {
super();
this.dependencies = dependencies;

View File

@ -6,8 +6,12 @@
const ModuleDependency = require("./ModuleDependency");
class SingleEntryDependency extends ModuleDependency {
/**
* @param {string} request request path for entry
*/
constructor(request) {
super(request);
this.request = request;
}
get type() {

View File

@ -621,7 +621,7 @@ class ConcatenatedModule extends Module {
});
const globalScope = scopeManager.acquire(ast);
const moduleScope = globalScope.childScopes[0];
const resultSource = new ReplaceSource(source);
const resultSource = new ReplaceSource(source, null);
info.ast = ast;
info.internalSource = source;
info.source = resultSource;

View File

@ -5,12 +5,24 @@
"use strict";
class Semaphore {
/**
* Creates an instance of Semaphore.
*
* @param {number} available the amount available number of "tasks"
* in the Semaphore
*/
constructor(available) {
this.available = available;
/** @type {(() => void)[]} */
this.waiters = [];
/** @private */
this._continue = this._continue.bind(this);
}
/**
* @param {function(): void} callback function block to capture and run
* @returns {void}
*/
acquire(callback) {
if (this.available > 0) {
this.available--;

View File

@ -31,6 +31,7 @@
"devDependencies": {
"@types/node": "^9.6.4",
"@types/tapable": "^1.0.1",
"@types/webpack-sources": "^0.1.4",
"benchmark": "^2.1.1",
"bundle-loader": "~0.5.0",
"codacy-coverage": "^2.0.1",

View File

@ -1,62 +1,65 @@
{
"compilerOptions": {
/* Basic Options */
"target": "ES2017", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"lib": ["es2017", "dom"], /* Specify library files to be included in the compilation. */
"allowJs": true, /* Allow javascript files to be compiled. */
"checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "removeComments": true, /* Do not emit comments to output. */
"noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
"compilerOptions": {
/* Basic Options */
"target":
"ES2017" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */,
"module":
"commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
"lib": [
"es2017",
"dom"
] /* Specify library files to be included in the compilation. */,
"allowJs": true /* Allow javascript files to be compiled. */,
"checkJs": true /* Report errors in .js files. */,
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "removeComments": true, /* Do not emit comments to output. */
"noEmit": true /* Do not emit outputs. */,
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": false, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
"strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
"alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Strict Type-Checking Options */
"strict": false /* Enable all strict type-checking options. */,
// "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */,
// "strictNullChecks": true /* Enable strict null checks. */,
// "strictFunctionTypes": true /* Enable strict checking of function types. */,
// "strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */,
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
"alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */,
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
"types": ["node"], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
"types": [
"node"
] /* Type declaration files to be included in compilation. */,
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
/* Source Map Options */
// "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Source Map Options */
// "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
},
"include": [
"declarations.d.ts",
"bin/*.js",
"lib/**/*.js"
]
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
},
"include": ["declarations.d.ts", "bin/*.js", "lib/**/*.js"]
}

914
yarn.lock

File diff suppressed because it is too large Load Diff