| | |
| | | |
| | | "use strict"; |
| | | |
| | | const parseJson = require("json-parse-even-better-errors"); |
| | | const asyncLib = require("neo-async"); |
| | | const { |
| | | AsyncParallelHook, |
| | |
| | | const { Logger } = require("./logging/Logger"); |
| | | const { dirname, join, mkdirp } = require("./util/fs"); |
| | | const { makePathsRelative } = require("./util/identifier"); |
| | | const memoize = require("./util/memoize"); |
| | | const parseJson = require("./util/parseJson"); |
| | | const { isSourceEqual } = require("./util/source"); |
| | | const webpack = require("."); |
| | | |
| | |
| | | /** @typedef {import("../declarations/WebpackOptions").WatchOptions} WatchOptions */ |
| | | /** @typedef {import("../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions */ |
| | | /** @typedef {import("../declarations/WebpackOptions").Plugins} Plugins */ |
| | | /** @typedef {import("../declarations/WebpackOptions").WebpackPluginFunction} WebpackPluginFunction */ |
| | | /** @typedef {import("./webpack").WebpackPluginFunction} WebpackPluginFunction */ |
| | | /** @typedef {import("./Chunk")} Chunk */ |
| | | /** @typedef {import("./Dependency")} Dependency */ |
| | | /** @typedef {import("./HotModuleReplacementPlugin").ChunkHashes} ChunkHashes */ |
| | |
| | | /** @typedef {import("./util/fs").OutputFileSystem} OutputFileSystem */ |
| | | /** @typedef {import("./util/fs").TimeInfoEntries} TimeInfoEntries */ |
| | | /** @typedef {import("./util/fs").WatchFileSystem} WatchFileSystem */ |
| | | /** @typedef {import("schema-utils").validate} Validate */ |
| | | /** @typedef {import("schema-utils").Schema} Schema */ |
| | | /** @typedef {import("schema-utils").ValidationErrorConfiguration} ValidationErrorConfiguration */ |
| | | |
| | | /** |
| | | * Defines the compilation params type used by this module. |
| | | * @typedef {object} CompilationParams |
| | | * @property {NormalModuleFactory} normalModuleFactory |
| | | * @property {ContextModuleFactory} contextModuleFactory |
| | | */ |
| | | |
| | | /** |
| | | * Defines the callback type used by this module. |
| | | * @template T |
| | | * @template [R=void] |
| | | * @typedef {import("./webpack").Callback<T, R>} Callback |
| | |
| | | /** @typedef {import("./webpack").ErrorCallback} ErrorCallback */ |
| | | |
| | | /** |
| | | * Defines the run as child callback callback. |
| | | * @callback RunAsChildCallback |
| | | * @param {Error | null} err |
| | | * @param {Chunk[]=} entries |
| | |
| | | */ |
| | | |
| | | /** |
| | | * Defines the known records type used by this module. |
| | | * @typedef {object} KnownRecords |
| | | * @property {SplitData[]=} aggressiveSplits |
| | | * @property {RecordsChunks=} chunks |
| | |
| | | /** @typedef {KnownRecords & Record<string, KnownRecords[]> & Record<string, EXPECTED_ANY>} Records */ |
| | | |
| | | /** |
| | | * Defines the asset emitted info type used by this module. |
| | | * @typedef {object} AssetEmittedInfo |
| | | * @property {Buffer} content |
| | | * @property {Source} source |
| | |
| | | /** @typedef {{ buildInfo: BuildInfo, references: WeakReferences | undefined, memCache: MemCache }} ModuleMemCachesItem */ |
| | | |
| | | /** |
| | | * Checks whether this object is sorted. |
| | | * @template T |
| | | * @param {T[]} array an array |
| | | * @returns {boolean} true, if the array is sorted |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Returns the object with properties sorted by property name. |
| | | * @template {object} T |
| | | * @param {T} obj an object |
| | | * @param {(keyof T)[]} keys the keys of the object |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Returns true, if the filename contains any hash. |
| | | * @param {string} filename filename |
| | | * @param {string | string[] | undefined} hashes list of hashes |
| | | * @returns {boolean} true, if the filename contains any hash |
| | |
| | | return filename.includes(hashes); |
| | | }; |
| | | |
| | | const getValidate = memoize(() => require("schema-utils").validate); |
| | | |
| | | class Compiler { |
| | | /** |
| | | * Creates an instance of Compiler. |
| | | * @param {string} context the compilation path |
| | | * @param {WebpackOptions} options options |
| | | */ |
| | |
| | | |
| | | // TODO the following hooks are weirdly located here |
| | | // TODO move them for webpack 5 |
| | | /** @type {SyncHook<[]>} */ |
| | | validate: new SyncHook([]), |
| | | /** @type {SyncHook<[]>} */ |
| | | environment: new SyncHook([]), |
| | | /** @type {SyncHook<[]>} */ |
| | |
| | | } |
| | | |
| | | /** |
| | | * Returns the cache facade instance. |
| | | * @param {string} name cache name |
| | | * @returns {CacheFacade} the cache facade instance |
| | | */ |
| | |
| | | } |
| | | |
| | | /** |
| | | * Gets infrastructure logger. |
| | | * @param {string | (() => string)} name name of the logger, or function called once to get the logger name |
| | | * @returns {Logger} a logger with that name |
| | | */ |
| | |
| | | } |
| | | |
| | | /** |
| | | * Returns a compiler watcher. |
| | | * @param {WatchOptions} watchOptions the watcher's options |
| | | * @param {Callback<Stats>} handler signals when the call finishes |
| | | * @returns {Watching | undefined} a compiler watcher |
| | |
| | | } |
| | | |
| | | /** |
| | | * Processes the provided stat. |
| | | * @param {Callback<Stats>} callback signals when the call finishes |
| | | * @returns {void} |
| | | */ |
| | |
| | | let logger; |
| | | |
| | | /** |
| | | * Processes the provided err. |
| | | * @param {Error | null} err error |
| | | * @param {Stats=} stats stats |
| | | */ |
| | |
| | | if (logger) logger.time("beginIdle"); |
| | | this.idle = true; |
| | | this.cache.beginIdle(); |
| | | this.idle = true; |
| | | if (logger) logger.timeEnd("beginIdle"); |
| | | this.running = false; |
| | | if (err) { |
| | |
| | | this.running = true; |
| | | |
| | | /** |
| | | * Processes the provided err. |
| | | * @param {Error | null} err error |
| | | * @param {Compilation=} _compilation compilation |
| | | * @returns {void} |
| | |
| | | } |
| | | |
| | | /** |
| | | * Processes the provided run as child callback. |
| | | * @param {RunAsChildCallback} callback signals when the call finishes |
| | | * @returns {void} |
| | | */ |
| | |
| | | const startTime = Date.now(); |
| | | |
| | | /** |
| | | * Processes the provided err. |
| | | * @param {Error | null} err error |
| | | * @param {Chunk[]=} entries entries |
| | | * @param {Compilation=} compilation compilation |
| | |
| | | } |
| | | |
| | | /** |
| | | * Processes the provided compilation. |
| | | * @param {Compilation} compilation the compilation |
| | | * @param {ErrorCallback} callback signals when the assets are emitted |
| | | * @returns {void} |
| | |
| | | let outputPath; |
| | | |
| | | /** |
| | | * Processes the provided err. |
| | | * @param {Error=} err error |
| | | * @returns {void} |
| | | */ |
| | |
| | | ({ name: file, source, info }, callback) => { |
| | | let targetFile = file; |
| | | let immutable = info.immutable; |
| | | const queryStringIdx = targetFile.indexOf("?"); |
| | | if (queryStringIdx >= 0) { |
| | | targetFile = targetFile.slice(0, queryStringIdx); |
| | | const queryOrHashStringIdx = targetFile.search(/[?#]/); |
| | | if (queryOrHashStringIdx >= 0) { |
| | | targetFile = targetFile.slice(0, queryOrHashStringIdx); |
| | | // We may remove the hash, which is in the query string |
| | | // So we recheck if the file is immutable |
| | | // This doesn't cover all cases, but immutable is only a performance optimization anyway |
| | |
| | | } |
| | | |
| | | /** |
| | | * Processes the provided err. |
| | | * @param {Error=} err error |
| | | * @returns {void} |
| | | */ |
| | |
| | | if (cacheEntry === undefined) { |
| | | cacheEntry = { |
| | | sizeOnlySource: undefined, |
| | | /** @type {CacheEntry["writtenTo"]} */ |
| | | writtenTo: new Map() |
| | | }; |
| | | this._assetEmittingSourceCache.set(source, cacheEntry); |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Updates with replacement source. |
| | | * @param {number} size size |
| | | */ |
| | | const updateWithReplacementSource = (size) => { |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Updates file with replacement source. |
| | | * @param {string} file file |
| | | * @param {CacheEntry} cacheEntry cache entry |
| | | * @param {number} size size |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Process existing file. |
| | | * @param {IStats} stats stats |
| | | * @returns {void} |
| | | */ |
| | |
| | | } |
| | | |
| | | /** |
| | | * Processes the provided error callback. |
| | | * @param {ErrorCallback} callback signals when the call finishes |
| | | * @returns {void} |
| | | */ |
| | |
| | | (cb) => this.hooks.emitRecords.callAsync(cb), |
| | | this._emitRecords.bind(this) |
| | | ], |
| | | (err) => callback(err) |
| | | (err) => callback(/** @type {Error | null} */ (err)) |
| | | ); |
| | | } else { |
| | | this.hooks.emitRecords.callAsync(callback); |
| | |
| | | } |
| | | |
| | | /** |
| | | * Processes the provided error callback. |
| | | * @param {ErrorCallback} callback signals when the call finishes |
| | | * @returns {void} |
| | | */ |
| | |
| | | } |
| | | |
| | | /** |
| | | * Processes the provided error callback. |
| | | * @param {ErrorCallback} callback signals when the call finishes |
| | | * @returns {void} |
| | | */ |
| | |
| | | (cb) => this.hooks.readRecords.callAsync(cb), |
| | | this._readRecords.bind(this) |
| | | ], |
| | | (err) => callback(err) |
| | | (err) => callback(/** @type {Error | null} */ (err)) |
| | | ); |
| | | } else { |
| | | this.records = {}; |
| | |
| | | } |
| | | |
| | | /** |
| | | * Processes the provided error callback. |
| | | * @param {ErrorCallback} callback signals when the call finishes |
| | | * @returns {void} |
| | | */ |
| | |
| | | if (err) return callback(err); |
| | | |
| | | try { |
| | | this.records = parseJson( |
| | | /** @type {Buffer} */ (content).toString("utf8") |
| | | ); |
| | | this.records = |
| | | /** @type {Records} */ |
| | | (parseJson(/** @type {Buffer} */ (content).toString("utf8"))); |
| | | } catch (parseErr) { |
| | | return callback( |
| | | new Error( |
| | |
| | | } |
| | | |
| | | /** |
| | | * Creates a child compiler. |
| | | * @param {Compilation} compilation the compilation |
| | | * @param {string} compilerName the compiler's name |
| | | * @param {number} compilerIndex the compiler's index |
| | |
| | | } |
| | | |
| | | /** |
| | | * Creates a compilation. |
| | | * @param {CompilationParams} params the compilation parameters |
| | | * @returns {Compilation} compilation |
| | | */ |
| | |
| | | } |
| | | |
| | | /** |
| | | * Returns the created compilation. |
| | | * @param {CompilationParams} params the compilation parameters |
| | | * @returns {Compilation} the created compilation |
| | | */ |
| | |
| | | } |
| | | |
| | | /** |
| | | * Processes the provided compilation. |
| | | * @param {Callback<Compilation>} callback signals when the compilation finishes |
| | | * @returns {void} |
| | | */ |
| | |
| | | } |
| | | |
| | | /** |
| | | * Processes the provided error callback. |
| | | * @param {ErrorCallback} callback signals when the compiler closes |
| | | * @returns {void} |
| | | */ |
| | |
| | | this.cache.shutdown(callback); |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * Schema validation function with optional pre-compiled check |
| | | * @template {EXPECTED_OBJECT | EXPECTED_OBJECT[]} [T=EXPECTED_OBJECT] |
| | | * @param {Schema | (() => Schema)} schema schema |
| | | * @param {T} value value |
| | | * @param {ValidationErrorConfiguration=} options options |
| | | * @param {((value: T) => boolean)=} check options |
| | | */ |
| | | validate(schema, value, options, check) { |
| | | // Avoid validation at all when disabled |
| | | if (this.options.validate === false) { |
| | | return; |
| | | } |
| | | |
| | | /** |
| | | * Returns schema. |
| | | * @returns {Schema} schema |
| | | */ |
| | | const getSchema = () => { |
| | | if (typeof schema === "function") { |
| | | return schema(); |
| | | } |
| | | |
| | | return schema; |
| | | }; |
| | | |
| | | // // If we have precompiled schema let's use it |
| | | if (check) { |
| | | if (!check(value)) { |
| | | getValidate()( |
| | | getSchema(), |
| | | /** @type {EXPECTED_OBJECT | EXPECTED_OBJECT[]} */ |
| | | (value), |
| | | options |
| | | ); |
| | | require("util").deprecate( |
| | | () => {}, |
| | | "webpack bug: Pre-compiled schema reports error while real schema is happy. This has performance drawbacks.", |
| | | "DEP_WEBPACK_PRE_COMPILED_SCHEMA_INVALID" |
| | | )(); |
| | | } |
| | | return; |
| | | } |
| | | |
| | | // Otherwise let's standard validation |
| | | getValidate()( |
| | | getSchema(), |
| | | /** @type {EXPECTED_OBJECT | EXPECTED_OBJECT[]} */ (value), |
| | | options |
| | | ); |
| | | } |
| | | } |
| | | |
| | | module.exports = Compiler; |