🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

webpack

Package Overview
Dependencies
Maintainers
8
Versions
883
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

webpack - npm Package Compare versions

Comparing version
5.109.1
to
5.109.2
+259
-5
lib/cache/PackFileCacheStrategy.js

@@ -10,2 +10,3 @@ /*

const ProgressPlugin = require("../ProgressPlugin");
const { getReferencedFilenames } = require("../serialization/FileMiddleware");
const SerializerMiddleware = require("../serialization/SerializerMiddleware");

@@ -38,3 +39,14 @@ const LazySet = require("../util/LazySet");

/** @typedef {Map<string, PackItemInfo>} ItemInfo */
/** @typedef {{ firstSeen: number, size: number }} UnreferencedFile */
/** @typedef {Map<string, UnreferencedFile>} UnreferencedFiles */
// Unreferenced files are kept for this long to not race concurrent builds sharing the cache directory.
const CLEANUP_GRACE_PERIOD = 30 * 60 * 1000;
// Records when each unreferenced file was first seen. Aging by recorded time keeps
// orphans expiring across caches restored with refreshed modification times.
const UNREFERENCED_FILE = "unreferenced.json";
// A file written this recently may belong to a concurrent build that reused the name,
// in which case the recorded time describes the previous file and must not be trusted.
const CLEANUP_RECENT_WRITE_PERIOD = 60 * 1000;
class PackContainer {

@@ -575,2 +587,37 @@ /**

/**
* Drops every content whose items all expired. Unlike a partial collection this
* never unpacks, so it is not limited to a single content per store and lets a
* long unused cache shrink in one go instead of one pack per build.
*/
_gcExpiredContent() {
const now = Date.now();
let packCount = 0;
let itemCount = 0;
for (let loc = 0; loc < this.content.length; loc++) {
const content = this.content[loc];
if (!content) continue;
let expired = true;
for (const identifier of content.items) {
const info = this.itemInfo.get(identifier);
if (info !== undefined && now - info.lastAccess <= this.maxAge) {
expired = false;
break;
}
}
if (!expired) continue;
for (const identifier of content.items) this.itemInfo.delete(identifier);
this.content[loc] = undefined;
packCount++;
itemCount += content.items.size;
}
if (packCount > 0) {
this.logger.log(
"Garbage Collected %d completely expired packs with %d items",
packCount,
itemCount
);
}
}
/**
* Find the content with the oldest item and run GC on that.

@@ -587,7 +634,6 @@ * Only runs for one content to avoid large invalidation.

}
if (
Date.now() - /** @type {PackItemInfo} */ (oldest).lastAccess >
this.maxAge
) {
const loc = /** @type {PackItemInfo} */ (oldest).location;
// collecting expired content may have left no items at all
if (oldest === undefined) return;
if (Date.now() - oldest.lastAccess > this.maxAge) {
const loc = oldest.location;
if (loc < 0) return;

@@ -628,2 +674,3 @@ const content = /** @type {PackContent} */ (this.content[loc]);

this._optimizeUnusedContent();
this._gcExpiredContent();
this._gcOldestContent();

@@ -1174,2 +1221,4 @@ for (const identifier of this.itemInfo.keys()) {

this.compiler = compiler;
/** @type {IntermediateFileSystem} */
this.fs = fs;
/** @type {string} */

@@ -1191,2 +1240,6 @@ this.context = context;

this.allowCollectingMemory = allowCollectingMemory;
// referenced names per on-disk file, versioned by mtime since concurrent
// processes may rewrite packs in place under the same name
/** @type {Map<string, { mtimeMs: number, referenced: string[] }>} */
this._referencedFilesCache = new Map();
/** @type {false | "gzip" | "brotli" | "zstd" | undefined} */

@@ -1598,2 +1651,7 @@ this.compression = compression;

);
const cleanup = this.fs.unlink !== undefined;
/** @type {Set<string> | undefined} */
const writtenFiles = cleanup ? new Set() : undefined;
/** @type {Set<string> | undefined} */
const retainedFiles = cleanup ? new Set() : undefined;
return this.fileSerializer

@@ -1603,2 +1661,4 @@ .serialize(content, {

extension: `${this._extension}`,
writtenFiles,
retainedFiles,
logger: this.logger,

@@ -1620,5 +1680,13 @@ profile: this.profile

);
if (writtenFiles !== undefined) {
return this._cleanupUnusedFiles(
writtenFiles,
/** @type {Set<string>} */ (retainedFiles)
);
}
})
.catch((err) => {
this.logger.timeEnd("store pack");
// files may be in an unknown state after a failed store
this._referencedFilesCache.clear();
this.logger.warn(`Caching failed for pack: ${err}`);

@@ -1635,2 +1703,188 @@ this.logger.debug(err.stack);

/**
* Reads when the currently unreferenced files were first seen. A missing or
* unreadable file just restarts the grace period for every orphan.
* @returns {Promise<UnreferencedFiles>} first seen time and size per file name
*/
_readUnreferencedFiles() {
return new Promise((resolve) => {
this.fs.readFile(
`${this.cacheLocation}/${UNREFERENCED_FILE}`,
(err, content) => {
/** @type {UnreferencedFiles} */
const result = new Map();
if (err) return resolve(result);
try {
const data = JSON.parse(
/** @type {Buffer} */ (content).toString("utf8")
);
for (const [file, entry] of Object.entries(data)) {
const { firstSeen, size } = /** @type {UnreferencedFile} */ (
entry
);
if (typeof firstSeen === "number" && typeof size === "number") {
result.set(file, { firstSeen, size });
}
}
} catch (_err) {
result.clear();
}
resolve(result);
}
);
});
}
/**
* Persists when the still unreferenced files were first seen. Failing to write
* only costs the orphans another grace period, so errors are ignored.
* @param {UnreferencedFiles} unreferenced first seen time and size per file name
* @param {boolean} hadEntries whether a previous state exists that must be replaced
* @returns {Promise<void>} promise
*/
_writeUnreferencedFiles(unreferenced, hadEntries) {
if (unreferenced.size === 0 && !hadEntries) return Promise.resolve();
/** @type {Record<string, UnreferencedFile>} */
const data = {};
for (const [file, entry] of unreferenced) data[file] = entry;
return new Promise((resolve) => {
this.fs.writeFile(
`${this.cacheLocation}/${UNREFERENCED_FILE}`,
JSON.stringify(data),
() => resolve()
);
});
}
/**
* Deletes files from the cache directory that are no longer referenced by the
* stored pack. Retained files are walked on disk since nested lazy segments
* reference files not visible during serialization. Errors only log a warning.
* @param {Set<string>} writtenNames names (without extension) written by this store
* @param {Set<string>} retainedNames names (without extension) referenced but not rewritten
* @returns {Promise<void>} promise
*/
async _cleanupUnusedFiles(writtenNames, retainedNames) {
this.logger.time("cleanup unused cache files");
const fs = this.fs;
const extension = this._extension;
const cacheLocation = this.cacheLocation;
const referencedFilesCache = this._referencedFilesCache;
try {
// rewritten files may reference different names now
for (const name of writtenNames) referencedFilesCache.delete(name);
/** @type {Set<string>} */
const liveFiles = new Set([`index${extension}`, UNREFERENCED_FILE]);
for (const name of writtenNames) liveFiles.add(`${name}${extension}`);
/** @type {string[]} */
const queue = [];
/**
* Marks a file live and queues it for walking its references.
* @param {string} name file name without extension
*/
const enqueue = (name) => {
const file = `${name}${extension}`;
if (liveFiles.has(file)) return;
liveFiles.add(file);
queue.push(name);
};
for (const name of retainedNames) enqueue(name);
while (queue.length > 0) {
const name = /** @type {string} */ (queue.pop());
const file = `${cacheLocation}/${name}${extension}`;
// an unchanged mtime proves the memo entry still matches the disk
const mtimeMs = await new Promise((resolve, reject) => {
fs.stat(file, (err, stats) => {
if (err) return reject(err);
resolve(
/** @type {number} */ (
/** @type {import("../util/fs").IStats} */ (stats).mtimeMs
)
);
});
});
const entry = referencedFilesCache.get(name);
let referenced;
if (entry !== undefined && entry.mtimeMs === mtimeMs) {
referenced = entry.referenced;
} else {
referenced = await getReferencedFilenames(fs, file);
referencedFilesCache.set(name, { mtimeMs, referenced });
}
for (const referencedName of referenced) enqueue(referencedName);
}
const files = await new Promise((resolve, reject) => {
fs.readdir(cacheLocation, (err, files) => {
if (err) return reject(err);
resolve(/** @type {string[]} */ (files));
});
});
const seenFiles = await this._readUnreferencedFiles();
const now = Date.now();
// every store rewrites the index backup, so a recorded time would age a file
// that is in fact new; renaming carries the previous index mtime onto it
const indexBackup = `index${extension}.old`;
/** @type {UnreferencedFiles} */
const stillUnreferenced = new Map();
let deletedCount = 0;
for (const file of files) {
if (typeof file !== "string" || liveFiles.has(file)) continue;
const path = `${cacheLocation}/${file}`;
const stats = await new Promise((resolve) => {
fs.stat(path, (err, stats) => {
resolve(
err
? undefined
: /** @type {import("../util/fs").IStats} */ (stats)
);
});
});
if (stats === undefined || !stats.isFile()) continue;
const size = /** @type {number} */ (stats.size);
const seen = seenFiles.get(file);
// a differing size means the name was rewritten, so it is a new orphan
const firstSeen =
seen !== undefined && seen.size === size ? seen.firstSeen : now;
const expireTime = now - CLEANUP_GRACE_PERIOD;
const mtimeMs = /** @type {number} */ (stats.mtimeMs);
// either signal is enough: modification times are lost when a cache is
// restored, and recorded times are lost when the cache directory is new
const expired =
mtimeMs <= expireTime ||
(file !== indexBackup &&
firstSeen <= expireTime &&
mtimeMs <= now - CLEANUP_RECENT_WRITE_PERIOD);
if (expired) {
const deleted = await new Promise((resolve) => {
/** @type {NonNullable<IntermediateFileSystem["unlink"]>} */
(fs.unlink)(path, (err) => resolve(!err));
});
if (deleted) {
deletedCount++;
continue;
}
}
if (file !== indexBackup) {
stillUnreferenced.set(file, { firstSeen, size });
}
}
await this._writeUnreferencedFiles(stillUnreferenced, seenFiles.size > 0);
// drop entries of files that are no longer alive
for (const name of referencedFilesCache.keys()) {
if (!liveFiles.has(`${name}${extension}`)) {
referencedFilesCache.delete(name);
}
}
if (deletedCount > 0) {
this.logger.log("Deleted %d unused cache files", deletedCount);
}
} catch (err) {
this.logger.warn(
`Cleanup of unused cache files failed: ${/** @type {Error} */ (err)}`
);
this.logger.debug(/** @type {Error} */ (err).stack);
}
this.logger.timeEnd("cleanup unused cache files");
}
clear() {

@@ -1637,0 +1891,0 @@ this.fileSystemInfo.clear();

+11
-9

@@ -25,6 +25,13 @@ /*

const createCompilationHooks = () => ({
/**
* When returning true the file/directory will be kept during cleaning, returning false will clean it and ignore the following plugins and config.
* @type {SyncBailHook<[string], boolean | void>}
* @since 5.20.0
*/
keep: new SyncBailHook(["ignore"])
});
/**
* Defines the clean plugin compilation hooks type used by this module.
* @typedef {object} CleanPluginCompilationHooks
* @property {SyncBailHook<[string], boolean | void>} keep when returning true the file/directory will be kept during cleaning, returning false will clean it and ignore the following plugins and config
* @typedef {ReturnType<typeof createCompilationHooks>} CleanPluginCompilationHooks
*/

@@ -488,10 +495,5 @@

CleanPlugin.getCompilationHooks = createHooksRegistry(
() =>
/** @type {CleanPluginCompilationHooks} */ ({
keep: new SyncBailHook(["ignore"])
})
);
CleanPlugin.getCompilationHooks = createHooksRegistry(createCompilationHooks);
module.exports = CleanPlugin;
module.exports._getDirectories = getDirectories;

@@ -225,5 +225,11 @@ /*

/** @type {AsyncSeriesHook<[]>} */
/**
* @type {AsyncSeriesHook<[]>}
* @since 5.67.0
*/
readRecords: new AsyncSeriesHook([]),
/** @type {AsyncSeriesHook<[]>} */
/**
* @type {AsyncSeriesHook<[]>}
* @since 5.67.0
*/
emitRecords: new AsyncSeriesHook([]),

@@ -239,3 +245,6 @@

watchClose: new SyncHook([]),
/** @type {AsyncSeriesHook<[]>} */
/**
* @type {AsyncSeriesHook<[]>}
* @since 5.17.0
*/
shutdown: new AsyncSeriesHook([]),

@@ -248,3 +257,6 @@

// TODO move them for webpack 5
/** @type {SyncHook<[]>} */
/**
* @type {SyncHook<[]>}
* @since 5.106.0
*/
validate: new SyncHook([]),

@@ -251,0 +263,0 @@ /** @type {SyncHook<[]>} */

@@ -21,7 +21,17 @@ /*

const createCompilationHooks = () => ({
/**
* @type {SyncHook<Dependency>}
* @since 5.96.0
*/
addContainerEntryDependency: new SyncHook(["dependency"]),
/**
* @type {SyncHook<Dependency>}
* @since 5.96.0
*/
addFederationRuntimeDependency: new SyncHook(["dependency"])
});
/**
* Defines the compilation hooks type used by this module.
* @typedef {object} CompilationHooks
* @property {SyncHook<Dependency>} addContainerEntryDependency
* @property {SyncHook<Dependency>} addFederationRuntimeDependency
* @typedef {ReturnType<typeof createCompilationHooks>} CompilationHooks
*/

@@ -115,9 +125,5 @@

ModuleFederationPlugin.getCompilationHooks = createHooksRegistry(
() =>
/** @type {CompilationHooks} */ ({
addContainerEntryDependency: new SyncHook(["dependency"]),
addFederationRuntimeDependency: new SyncHook(["dependency"])
})
createCompilationHooks
);
module.exports = ModuleFederationPlugin;

@@ -36,2 +36,3 @@ /*

const { encodeMappings } = require("../util/createMappings");
const { contextifySourceUrl } = require("../util/identifier");
const memoize = require("../util/memoize");

@@ -553,4 +554,9 @@ const {

const generatedJs = /** @type {string} */ (source.source());
const sourceName = module.readableIdentifier(
compilation.requestShortener
// Context-relative identifier, like `NormalModule.createSource` — so
// `SourceMapDevToolPlugin` can match the module and apply the
// devtool filename template instead of leaking this raw name
const sourceName = contextifySourceUrl(
/** @type {string} */ (compilation.options.context),
module.identifier(),
compilation.compiler.root
);

@@ -557,0 +563,0 @@

@@ -23,8 +23,27 @@ /*

const createCompilationHooks = () => ({
/**
* @type {SyncWaterfallHook<[string, Chunk]>}
* @since 5.66.0
*/
createStylesheet: new SyncWaterfallHook(["source", "chunk"]),
/**
* @type {SyncWaterfallHook<[string, Chunk]>}
* @since 5.91.0
*/
linkPreload: new SyncWaterfallHook(["source", "chunk"]),
/**
* @type {SyncWaterfallHook<[string, Chunk]>}
* @since 5.91.0
*/
linkPrefetch: new SyncWaterfallHook(["source", "chunk"]),
/**
* @type {SyncWaterfallHook<[string, Chunk]>}
* @since 5.107.0
*/
linkInsert: new SyncWaterfallHook(["source", "chunk"])
});
/**
* @typedef {object} CssLoadingRuntimeModulePluginHooks
* @property {SyncWaterfallHook<[string, Chunk]>} createStylesheet
* @property {SyncWaterfallHook<[string, Chunk]>} linkPreload
* @property {SyncWaterfallHook<[string, Chunk]>} linkPrefetch
* @property {SyncWaterfallHook<[string, Chunk]>} linkInsert
* @typedef {ReturnType<typeof createCompilationHooks>} CssLoadingRuntimeModulePluginHooks
*/

@@ -581,11 +600,5 @@

CssLoadingRuntimeModule.getCompilationHooks = createHooksRegistry(
() =>
/** @type {CssLoadingRuntimeModulePluginHooks} */ ({
createStylesheet: new SyncWaterfallHook(["source", "chunk"]),
linkPreload: new SyncWaterfallHook(["source", "chunk"]),
linkPrefetch: new SyncWaterfallHook(["source", "chunk"]),
linkInsert: new SyncWaterfallHook(["source", "chunk"])
})
createCompilationHooks
);
module.exports = CssLoadingRuntimeModule;

@@ -99,10 +99,2 @@ /*

/**
* Defines the compilation hooks type used by this module.
* @typedef {object} CompilationHooks
* @property {SyncWaterfallHook<[Source, Module, ChunkRenderContext]>} renderModulePackage
* @property {SyncHook<[Chunk, Hash, ChunkHashContext]>} chunkHash
* @property {SyncBailHook<[Chunk, Module[], Compilation], Module[] | undefined | void>} orderModules called for each CSS source type (CSS_IMPORT_TYPE, CSS_TYPE) with the chunk's modules pre-sorted by full module name; return an ordered `Module[]` to override the default import-order topological sort, or return `undefined` to keep the default
*/
/**
* Defines the module factory cache entry type used by this module.

@@ -224,2 +216,30 @@ * @typedef {object} ModuleFactoryCacheEntry

const createCompilationHooks = () => ({
/**
* @type {SyncWaterfallHook<[Source, Module, ChunkRenderContext]>}
* @since 5.94.0
*/
renderModulePackage: new SyncWaterfallHook([
"source",
"module",
"renderContext"
]),
/**
* @type {SyncHook<[Chunk, Hash, ChunkHashContext]>}
* @since 5.94.0
*/
chunkHash: new SyncHook(["chunk", "hash", "context"]),
/**
* Called for each CSS source type (CSS_IMPORT_TYPE, CSS_TYPE) with the chunk's modules pre-sorted by full module name; return an ordered `Module[]` to override the default import-order topological sort, or return `undefined` to keep the default.
* @type {SyncBailHook<[Chunk, Module[], Compilation], Module[] | undefined | void>}
* @since 5.107.0
*/
orderModules: new SyncBailHook(["chunk", "modules", "compilation"])
});
/**
* Defines the compilation hooks type used by this module.
* @typedef {ReturnType<typeof createCompilationHooks>} CompilationHooks
*/
class CssModulesPlugin {

@@ -1180,14 +1200,5 @@ constructor() {

CssModulesPlugin.getCompilationHooks = createHooksRegistry(
() =>
/** @type {CompilationHooks} */ ({
renderModulePackage: new SyncWaterfallHook([
"source",
"module",
"renderContext"
]),
chunkHash: new SyncHook(["chunk", "hash", "context"]),
orderModules: new SyncBailHook(["chunk", "modules", "compilation"])
})
createCompilationHooks
);
module.exports = CssModulesPlugin;

@@ -631,2 +631,10 @@ /*

const createCompilationHooks = () => ({
/**
* @type {SyncWaterfallHook<[Record<string, CodeValue>]>}
* @since 5.104.0
*/
definitions: new SyncWaterfallHook(["definitions"])
});
/**

@@ -653,5 +661,3 @@ * Prefixes an evaluation error with the offending define key so a bare

/**
* Defines the define plugin hooks type used by this module.
* @typedef {object} DefinePluginHooks
* @property {SyncWaterfallHook<[Record<string, CodeValue>]>} definitions
* @typedef {ReturnType<typeof createCompilationHooks>} DefinePluginHooks
*/

@@ -1264,8 +1270,3 @@

DefinePlugin.getCompilationHooks = createHooksRegistry(
() =>
/** @type {DefinePluginHooks} */ ({
definitions: new SyncWaterfallHook(["definitions"])
})
);
DefinePlugin.getCompilationHooks = createHooksRegistry(createCompilationHooks);

@@ -1272,0 +1273,0 @@ module.exports = DefinePlugin;

@@ -12,2 +12,3 @@ /*

const makeSerializable = require("../util/makeSerializable");
const memoize = require("../util/memoize");
const { propertyAccess } = require("../util/property");

@@ -23,2 +24,3 @@ const {

/** @typedef {import("../Dependency")} Dependency */
/** @typedef {import("../Dependency").GetConditionFn} GetConditionFn */
/** @typedef {import("../Dependency").ReferencedExports} ReferencedExports */

@@ -31,5 +33,8 @@ /** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */

/** @typedef {import("../util/chainedImports").IdRanges} IdRanges */
/** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext<[ExportInfoName[], IdRanges | undefined, boolean, undefined | boolean]>} ObjectDeserializerContext */
/** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext<[ExportInfoName[], IdRanges | undefined, boolean, undefined | boolean]>} ObjectSerializerContext */
/** @typedef {import("./HarmonyImportGuard").DependencyGuard} DependencyGuard */
/** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext<[ExportInfoName[], IdRanges | undefined, boolean, undefined | boolean, DependencyGuard[] | undefined]>} ObjectDeserializerContext */
/** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext<[ExportInfoName[], IdRanges | undefined, boolean, undefined | boolean, DependencyGuard[] | undefined]>} ObjectSerializerContext */
const getHarmonyImportGuard = memoize(() => require("./HarmonyImportGuard"));
class CommonJsFullRequireDependency extends ModuleDependency {

@@ -59,5 +64,19 @@ /**

this.asiSafe = undefined;
/** @type {DependencyGuard[] | undefined} */
this.branchGuards = undefined;
}
/**
* Returns function to determine if the connection is active.
* @param {ModuleGraph} moduleGraph module graph
* @returns {null | false | GetConditionFn} function to determine if the connection is active
*/
getCondition(moduleGraph) {
const guards = this.branchGuards;
if (guards === undefined) return null;
return (connection, runtime) =>
!getHarmonyImportGuard().isDeadByGuards(guards, moduleGraph, runtime);
}
/**
* Returns list of exports referenced by this dependency

@@ -99,3 +118,4 @@ * @param {ModuleGraph} moduleGraph module graph

.write(this.call)
.write(this.asiSafe);
.write(this.asiSafe)
.write(this.branchGuards);
super.serialize(context);

@@ -116,3 +136,5 @@ }

this.asiSafe = c3.read();
super.deserialize(c3.rest);
const c4 = c3.rest;
this.branchGuards = c4.read();
super.deserialize(c4.rest);
}

@@ -146,2 +168,9 @@

if (!dep.range) return;
const connection = moduleGraph.getConnection(dep);
// Dead branch: module is excluded and has no id; code is never executed.
if (connection && !connection.isTargetActive(runtime)) {
// Replaces the whole member chain, so no property access is left dangling
source.replace(dep.range[0], dep.range[1] - 1, "null /* dead branch */");
return;
}
const importedModule = moduleGraph.getModule(dep);

@@ -148,0 +177,0 @@ let requireExpr = runtimeTemplate.moduleExports({

@@ -645,2 +645,3 @@ /*

parser.state.current.addDependency(dep);
getHarmonyImportGuard().attachDependencyGuards(parser, dep);
return true;

@@ -684,2 +685,3 @@ }

parser.state.current.addDependency(dep);
getHarmonyImportGuard().attachDependencyGuards(parser, dep);
parser.walkExpressions(expr.arguments);

@@ -686,0 +688,0 @@ return true;

@@ -17,2 +17,3 @@ /*

/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
/** @typedef {import("./CommonJsFullRequireDependency")} CommonJsFullRequireDependency */
/** @typedef {import("./CommonJsRequireDependency")} CommonJsRequireDependency */

@@ -33,3 +34,3 @@ /** @typedef {import("./HarmonyEvaluatedImportSpecifierDependency")} HarmonyEvaluatedImportSpecifierDependency */

/** @typedef {{ formula: GuardFormula, value: boolean }} DependencyGuard branch guard: the dependency is live only when the formula evaluates to `value` */
/** @typedef {HarmonyImportSpecifierDependency | CommonJsRequireDependency | ImportDependency} GuardableDependency */
/** @typedef {HarmonyImportSpecifierDependency | CommonJsRequireDependency | CommonJsFullRequireDependency | ImportDependency} GuardableDependency */

@@ -36,0 +37,0 @@ /**

@@ -28,7 +28,17 @@ /*

const createCompilationHooks = () => ({
/**
* @type {SyncWaterfallHook<[string, Chunk]>}
* @since 5.41.0
*/
linkPreload: new SyncWaterfallHook(["source", "chunk"]),
/**
* @type {SyncWaterfallHook<[string, Chunk]>}
* @since 5.41.0
*/
linkPrefetch: new SyncWaterfallHook(["source", "chunk"])
});
/**
* Defines the jsonp compilation plugin hooks type used by this module.
* @typedef {object} JsonpCompilationPluginHooks
* @property {SyncWaterfallHook<[string, Chunk]>} linkPreload
* @property {SyncWaterfallHook<[string, Chunk]>} linkPrefetch
* @typedef {ReturnType<typeof createCompilationHooks>} JsonpCompilationPluginHooks
*/

@@ -442,9 +452,5 @@

ModuleChunkLoadingRuntimeModule.getCompilationHooks = createHooksRegistry(
() =>
/** @type {JsonpCompilationPluginHooks} */ ({
linkPreload: new SyncWaterfallHook(["source", "chunk"]),
linkPrefetch: new SyncWaterfallHook(["source", "chunk"])
})
createCompilationHooks
);
module.exports = ModuleChunkLoadingRuntimeModule;

@@ -813,6 +813,12 @@ /*

const createCompilationHooks = () => ({
/**
* @type {SyncBailHook<[Chunk, Compilation], boolean>}
* @since 5.106.0
*/
chunkCondition: new SyncBailHook(["chunk", "compilation"])
});
/**
* Defines the external module hooks type used by this module.
* @typedef {object} ExternalModuleHooks
* @property {SyncBailHook<[Chunk, Compilation], boolean>} chunkCondition
* @typedef {ReturnType<typeof createCompilationHooks>} ExternalModuleHooks
*/

@@ -1490,6 +1496,3 @@

ExternalModule.getCompilationHooks = createHooksRegistry(
() =>
/** @type {ExternalModuleHooks} */ ({
chunkCondition: new SyncBailHook(["chunk", "compilation"])
})
createCompilationHooks
);

@@ -1496,0 +1499,0 @@

@@ -75,8 +75,32 @@ /*

/** @typedef {{ outputName: string, html: string }} HtmlTransformTagsContext */
const createCompilationHooks = () => ({
/**
* Called with the list of extra tags to inject into each page (initially empty) plus the current HTML; push `HtmlTagDescriptor`s and return the list — webpack serializes and places them by `injectTo`. A structured alternative to the string-level `transformHtml` for adding tags; runs before CSP so injected inline tags are hashed.
* @type {AsyncSeriesWaterfallHook<[HtmlTagDescriptor[], HtmlInjectTagsContext]>}
* @since 5.109.0
*/
injectTags: new AsyncSeriesWaterfallHook(["tags", "context"]),
/**
* Called with the page's `<script>`/`<link>`/`<style>`/`<meta>` tags (webpack's own and any injected) as mutable descriptors; mutate `attrs` (add a `nonce`/`data-*`, switch `defer`↔`async`, …), set `remove: true`, or change `injectTo` to move a tag between `<head>` and `<body>`, and webpack rewrites the changed tags. Add new tags with `injectTags` instead.
* @type {AsyncSeriesHook<[HtmlMutableTag[], HtmlTransformTagsContext]>}
* @since 5.109.0
*/
transformTags: new AsyncSeriesHook(["tags", "context"]),
/**
* Called with each emitted page's final HTML (all sentinels resolved) just before it is written; return the (possibly transformed) HTML — e.g. to minify, inject a CSP meta, or rewrite tags.
* @type {AsyncSeriesWaterfallHook<[string, HtmlTransformHtmlContext]>}
* @since 5.109.0
*/
transformHtml: new AsyncSeriesWaterfallHook(["html", "context"]),
/**
* Called once each page's HTML asset has been finalized — a post-emit notification (nothing to return).
* @type {AsyncSeriesHook<[HtmlEmittedContext]>}
* @since 5.109.0
*/
htmlEmitted: new AsyncSeriesHook(["context"])
});
/**
* @typedef {object} HtmlCompilationHooks
* @property {AsyncSeriesWaterfallHook<[HtmlTagDescriptor[], HtmlInjectTagsContext]>} injectTags called with the list of extra tags to inject into each page (initially empty) plus the current HTML; push `HtmlTagDescriptor`s and return the list — webpack serializes and places them by `injectTo`. A structured alternative to the string-level `transformHtml` for adding tags; runs before CSP so injected inline tags are hashed
* @property {AsyncSeriesHook<[HtmlMutableTag[], HtmlTransformTagsContext]>} transformTags called with the page's `<script>`/`<link>`/`<style>`/`<meta>` tags (webpack's own and any injected) as mutable descriptors; mutate `attrs` (add a `nonce`/`data-*`, switch `defer`↔`async`, …), set `remove: true`, or change `injectTo` to move a tag between `<head>` and `<body>`, and webpack rewrites the changed tags. Add new tags with `injectTags` instead
* @property {AsyncSeriesWaterfallHook<[string, HtmlTransformHtmlContext]>} transformHtml called with each emitted page's final HTML (all sentinels resolved) just before it is written; return the (possibly transformed) HTML — e.g. to minify, inject a CSP meta, or rewrite tags
* @property {AsyncSeriesHook<[HtmlEmittedContext]>} htmlEmitted called once each page's HTML asset has been finalized — a post-emit notification (nothing to return)
* @typedef {ReturnType<typeof createCompilationHooks>} HtmlCompilationHooks
*/

@@ -1103,11 +1127,5 @@

HtmlModulesPlugin.getCompilationHooks = createHooksRegistry(
() =>
/** @type {HtmlCompilationHooks} */ ({
injectTags: new AsyncSeriesWaterfallHook(["tags", "context"]),
transformTags: new AsyncSeriesHook(["tags", "context"]),
transformHtml: new AsyncSeriesWaterfallHook(["html", "context"]),
htmlEmitted: new AsyncSeriesHook(["context"])
})
createCompilationHooks
);
module.exports = HtmlModulesPlugin;

@@ -172,2 +172,4 @@ /*

let shortIdentifier;
/** @type {ReturnStringCallback} */
let resourceIdentifier;
if (typeof module === "string") {

@@ -178,2 +180,3 @@ shortIdentifier =

identifier = shortIdentifier;
resourceIdentifier = shortIdentifier;
moduleId = () => "";

@@ -187,2 +190,10 @@ absoluteResourcePath = () =>

);
// `[resource]` and `[loaders]` must stay request paths: a subclass's
// readable identifier may carry display-only decorations (e.g. CssModule's
// `css ` prefix)
resourceIdentifier = memoize(() =>
module instanceof NormalModule
? /** @type {string} */ (requestShortener.shorten(module.userRequest))
: module.readableIdentifier(requestShortener)
);
identifier =

@@ -202,5 +213,5 @@ /** @type {ReturnStringCallback} */

/** @type {ReturnStringCallback} */
(memoize(() => shortIdentifier().split("!").pop()));
(memoize(() => resourceIdentifier().split("!").pop()));
const loaders = getBefore(shortIdentifier, "!");
const loaders = getBefore(resourceIdentifier, "!");
const allLoaders = getBefore(identifier, "!");

@@ -207,0 +218,0 @@ const query = getAfter(resource, "?");

@@ -438,3 +438,6 @@ /*

),
/** @type {HookMap<AsyncSeriesBailHook<[ResourceDataWithData, ResolveData], true | void>>} */
/**
* @type {HookMap<AsyncSeriesBailHook<[ResourceDataWithData, ResolveData], true | void>>}
* @since 5.49.0
*/
resolveInScheme: new HookMap(

@@ -465,3 +468,6 @@ () => new AsyncSeriesBailHook(["resourceData", "resolveData"])

),
/** @type {HookMap<SyncBailHook<[CreateData, ResolveData], Module | void>>} */
/**
* @type {HookMap<SyncBailHook<[CreateData, ResolveData], Module | void>>}
* @since 5.81.0
*/
createModuleClass: new HookMap(

@@ -468,0 +474,0 @@ () => new SyncBailHook(["createData", "resolveData"])

@@ -169,6 +169,12 @@ /*

const createCompilationHooks = () => ({
/**
* @type {SyncBailHook<[Buffer[], string], string | void>}
* @since 5.8.0
*/
updateHash: new SyncBailHook(["content", "oldHash"])
});
/**
* Defines the compilation hooks type used by this module.
* @typedef {object} CompilationHooks
* @property {SyncBailHook<[Buffer[], string], string | void>} updateHash
* @typedef {ReturnType<typeof createCompilationHooks>} CompilationHooks
*/

@@ -558,8 +564,5 @@

RealContentHashPlugin.getCompilationHooks = createHooksRegistry(
() =>
/** @type {CompilationHooks} */ ({
updateHash: new SyncBailHook(["content", "oldHash"])
})
createCompilationHooks
);
module.exports = RealContentHashPlugin;

@@ -10,2 +10,4 @@ /*

const {
// eslint-disable-next-line n/no-unsupported-features/node-builtins
brotliDecompress,
constants: zConstants,

@@ -22,3 +24,6 @@ // eslint-disable-next-line n/no-unsupported-features/node-builtins

// eslint-disable-next-line n/no-unsupported-features/node-builtins
createZstdDecompress
createZstdDecompress,
gunzip,
// eslint-disable-next-line n/no-unsupported-features/node-builtins
zstdDecompress
} = require("zlib");

@@ -56,2 +61,4 @@ const { DEFAULTS } = require("../config/defaults");

const WRITE_LIMIT_CHUNK = 511 * 1024 * 1024;
// headers and pointer sections are tiny; anything above this is a corrupt file
const MAX_HEADER_OR_POINTER_SIZE = 256 * 1024 * 1024;

@@ -117,2 +124,3 @@ /**

* @param {HashFunction=} hashFunction hash function to use
* @param {Set<string>=} retainedNames collects names of files that stay referenced without being rewritten
* @returns {Promise<SerializeResult>} resulting file pointer and promise

@@ -125,3 +133,4 @@ */

writeFile,
hashFunction = DEFAULTS.HASH_FUNCTION
hashFunction = DEFAULTS.HASH_FUNCTION,
retainedNames = undefined
) => {

@@ -152,2 +161,6 @@ /** @type {(Buffer[] | Buffer | Promise<SerializeResult>)[]} */

} else {
if (retainedNames !== undefined) {
// pointer buffer layout: u64 size + utf-8 file name
retainedNames.add(serializedInfo.toString("utf8", 8));
}
processedData.push(serializedInfo);

@@ -166,3 +179,4 @@ }

writeFile,
hashFunction
hashFunction,
retainedNames
).then((result) => {

@@ -454,3 +468,10 @@ /** @type {LazyOptions} */

/** @typedef {true} SerializedType */
/** @typedef {{ filename: string, extension?: string }} Context */
/**
* `writtenFiles`/`retainedFiles` collect file names (without extension) during
* `serialize`: files written in this run and files that stay referenced by
* lazy pointers without being rewritten. Retained files may reference further
* files on disk not listed here (nested lazy segments); use
* `getReferencedFilenames` to walk them.
* @typedef {{ filename: string, extension?: string, writtenFiles?: Set<string>, retainedFiles?: Set<string> }} Context
*/

@@ -482,3 +503,3 @@ /**

serialize(data, context) {
const { filename, extension = "" } = context;
const { filename, extension = "", writtenFiles, retainedFiles } = context;
return new Promise((resolve, reject) => {

@@ -593,62 +614,70 @@ mkdirp(this.fs, dirname(this.fs, filename), (err) => {

);
if (name) allWrittenFiles.add(file);
if (name) {
allWrittenFiles.add(file);
if (writtenFiles !== undefined) writtenFiles.add(name);
}
};
resolve(
serialize(this, data, false, writeFile, this._hashFunction).then(
async ({ backgroundJob }) => {
await backgroundJob;
serialize(
this,
data,
false,
writeFile,
this._hashFunction,
retainedFiles
).then(async ({ backgroundJob }) => {
await backgroundJob;
// Rename the index file to disallow access during inconsistent file state
await new Promise(
/**
* Handles the callback logic for this hook.
* @param {(value?: undefined) => void} resolve resolve
*/
(resolve) => {
this.fs.rename(filename, `${filename}.old`, (_err) => {
resolve();
});
}
);
// Rename the index file to disallow access during inconsistent file state
await new Promise(
/**
* Handles the callback logic for this hook.
* @param {(value?: undefined) => void} resolve resolve
*/
(resolve) => {
this.fs.rename(filename, `${filename}.old`, (_err) => {
resolve();
});
}
);
// update all written files
await Promise.all(
Array.from(
allWrittenFiles,
(file) =>
new Promise(
/**
* Handles the callback logic for this hook.
* @param {(value?: undefined) => void} resolve resolve
* @param {(reason?: Error | null) => void} reject reject
* @returns {void}
*/
(resolve, reject) => {
this.fs.rename(`${file}_`, file, (err) => {
if (err) return reject(err);
resolve();
});
}
)
)
);
// update all written files
await Promise.all(
Array.from(
allWrittenFiles,
(file) =>
new Promise(
/**
* Handles the callback logic for this hook.
* @param {(value?: undefined) => void} resolve resolve
* @param {(reason?: Error | null) => void} reject reject
* @returns {void}
*/
(resolve, reject) => {
this.fs.rename(`${file}_`, file, (err) => {
if (err) return reject(err);
resolve();
});
}
)
)
);
// As final step automatically update the index file to have a consistent pack again
await new Promise(
/**
* Handles the callback logic for this hook.
* @param {(value?: undefined) => void} resolve resolve
* @returns {void}
*/
(resolve) => {
this.fs.rename(`${filename}_`, filename, (err) => {
if (err) return reject(err);
resolve();
});
}
);
return /** @type {true} */ (true);
}
)
// As final step automatically update the index file to have a consistent pack again
await new Promise(
/**
* Handles the callback logic for this hook.
* @param {(value?: undefined) => void} resolve resolve
* @returns {void}
*/
(resolve) => {
this.fs.rename(`${filename}_`, filename, (err) => {
if (err) return reject(err);
resolve();
});
}
);
return /** @type {true} */ (true);
})
);

@@ -821,4 +850,214 @@ });

/**
* Extracts the file names referenced by lazy pointer sections from serialized content.
* @param {Buffer} buf decompressed file content
* @returns {string[]} referenced file names (without extension)
*/
const parsePointerNames = (buf) => {
const version = buf.readUInt32LE(0);
if (version !== VERSION) {
throw new Error(`Invalid file version ${version}`);
}
const sectionCount = buf.readUInt32LE(4);
let offset = 8 + sectionCount * 4;
// a corrupt section table must abort the walk, never silently drop a name
if (offset > buf.length) {
throw new Error(`Invalid section count ${sectionCount}`);
}
/** @type {string[]} */
const names = [];
for (let i = 0; i < sectionCount; i++) {
const length = buf.readInt32LE(8 + i * 4);
if (length < 0) {
// pointer section: u64 size + utf-8 file name
const end = offset - length;
if (end > buf.length) {
throw new Error("Truncated pointer section");
}
names.push(buf.toString("utf8", offset + 8, end));
offset = end;
} else {
offset += length;
}
}
if (offset !== buf.length) {
throw new Error("Section table does not match file size");
}
return names;
};
/**
* Reads the pointer names of a compressed file by decompressing it fully
* (compressed content cannot be read by byte range).
* @param {IntermediateFileSystem} fs a file system
* @param {string} file absolute path of the serialized file
* @returns {Promise<string[]>} referenced file names (without extension)
*/
const getReferencedFilenamesCompressed = (fs, file) =>
new Promise((resolve, reject) => {
fs.readFile(file, (err, rawContent) => {
if (err) return reject(err);
/**
* Parses the decompressed content.
* @param {Error | null} err error
* @param {Buffer=} content decompressed content
* @returns {void}
*/
const onContent = (err, content) => {
if (err) return reject(err);
try {
resolve(parsePointerNames(/** @type {Buffer} */ (content)));
} catch (err_) {
reject(/** @type {Error} */ (err_));
}
};
const buf = /** @type {Buffer} */ (rawContent);
if (file.endsWith(".gz")) {
gunzip(buf, onContent);
} else if (file.endsWith(".br")) {
brotliDecompress(buf, onContent);
} else {
if (!zstdDecompress) {
return reject(
new Error("zstd decompression requires Node.js >= 22.15.0")
);
}
zstdDecompress(buf, onContent);
}
});
});
/**
* Reads the pointer names of an uncompressed file by reading only the header
* and the pointer sections instead of the whole file.
* @param {IntermediateFileSystem} fs a file system
* @param {string} file absolute path of the serialized file
* @returns {Promise<string[]>} referenced file names (without extension)
*/
const getReferencedFilenamesUncompressed = (fs, file) =>
new Promise((resolve, reject) => {
fs.open(file, "r", (err, _fd) => {
if (err) return reject(err);
const fd = /** @type {number} */ (_fd);
/**
* Closes the file descriptor and rejects.
* @param {Error} err error
*/
const fail = (err) => {
fs.close(fd, () => reject(err));
};
/**
* Reads exactly `length` bytes at `position`.
* @param {number} position file position
* @param {number} length byte count
* @param {(buffer: Buffer) => void} callback called with the filled buffer
* @returns {void}
*/
const readAt = (position, length, callback) => {
// corrupt headers can request absurd sizes; must reject, not crash
if (length > MAX_HEADER_OR_POINTER_SIZE) {
return fail(new Error(`Invalid section size ${length} in ${file}`));
}
/** @type {Buffer} */
let buffer;
try {
buffer = Buffer.allocUnsafe(length);
} catch (err) {
return fail(/** @type {Error} */ (err));
}
let bytesDone = 0;
const readMore = () => {
fs.read(
fd,
buffer,
bytesDone,
length - bytesDone,
position + bytesDone,
(err, bytesRead) => {
if (err) return fail(err);
if (bytesRead === 0) {
return fail(new Error(`Unexpected end of file ${file}`));
}
bytesDone += bytesRead;
if (bytesDone < length) return readMore();
callback(buffer);
}
);
};
readMore();
};
readAt(0, 8, (header) => {
const version = header.readUInt32LE(0);
if (version !== VERSION) {
return fail(new Error(`Invalid file version ${version}`));
}
const sectionCount = header.readUInt32LE(4);
if (sectionCount === 0) {
return fs.close(fd, (err) => (err ? reject(err) : resolve([])));
}
readAt(8, sectionCount * 4, (lengthsBuffer) => {
/** @type {{ position: number, length: number }[]} */
const pointerSections = [];
let position = 8 + sectionCount * 4;
for (let i = 0; i < sectionCount; i++) {
const length = lengthsBuffer.readInt32LE(i * 4);
if (length < 0) {
pointerSections.push({ position, length: -length });
position -= length;
} else {
position += length;
}
}
// the section table must account for exactly the whole file,
// otherwise pointer reads land on wrong bytes and under-report
fs.stat(file, (err, stats) => {
if (err) return fail(err);
const stat = /** @type {import("../util/fs").IStats} */ (stats);
if (stat.size !== position) {
return fail(
new Error(`Section table does not match size of ${file}`)
);
}
/** @type {string[]} */
const names = [];
/**
* Reads the pointer section at `index`, then the next one.
* @param {number} index pointer section index
* @returns {void}
*/
const readNext = (index) => {
if (index >= pointerSections.length) {
return fs.close(fd, (err) =>
err ? reject(err) : resolve(names)
);
}
const section = pointerSections[index];
readAt(section.position, section.length, (buffer) => {
// pointer section: u64 size + utf-8 file name
names.push(buffer.toString("utf8", 8));
readNext(index + 1);
});
};
readNext(0);
});
});
});
});
});
/**
* Reads the file names referenced by a serialized file (its lazy pointer sections)
* without deserializing its content. Referenced files may reference further files.
* @param {IntermediateFileSystem} fs a file system
* @param {string} file absolute path of the serialized file
* @returns {Promise<string[]>} referenced file names (without extension)
*/
const getReferencedFilenames = (fs, file) =>
file.endsWith(".gz") || file.endsWith(".br") || file.endsWith(".zst")
? getReferencedFilenamesCompressed(fs, file)
: getReferencedFilenamesUncompressed(fs, file);
module.exports = FileMiddleware;
// Exposed for testing the content-buffer boundary handling in `deserialize`.
module.exports._deserialize = deserialize;
module.exports.getReferencedFilenames = getReferencedFilenames;

@@ -535,2 +535,21 @@ /*

const makePathsRelative = makeCacheableWithContext(_makePathsRelative);
/**
* Turns a source path into a `webpack://`-prefixed, context-relative source
* URL, as used for the `sources` of module-level source maps.
* @param {string} context absolute context path
* @param {string} source a source path
* @param {AssociatedObjectForCache=} associatedObjectForCache an object to which the cache will be attached
* @returns {string} new source path
*/
const contextifySourceUrl = (context, source, associatedObjectForCache) => {
if (source.startsWith("webpack://")) return source;
return `webpack://${makePathsRelative(
context,
source,
associatedObjectForCache
)}`;
};
const LINE_SEPARATOR_REGEXP = /[\u2028\u2029]/g;

@@ -554,2 +573,3 @@

module.exports.contextify = contextify;
module.exports.contextifySourceUrl = contextifySourceUrl;
module.exports.escapeHashInPathRequest = escapeHashInPathRequest;

@@ -559,3 +579,3 @@ module.exports.getUndoPath = getUndoPath;

module.exports.makePathsAbsolute = makeCacheableWithContext(_makePathsAbsolute);
module.exports.makePathsRelative = makeCacheableWithContext(_makePathsRelative);
module.exports.makePathsRelative = makePathsRelative;
module.exports.parseResource = makeCacheable(_parseResource);

@@ -562,0 +582,0 @@ module.exports.parseResourceWithoutFragment = makeCacheable(

{
"name": "webpack",
"version": "5.109.1",
"version": "5.109.2",
"description": "Packs ECMAScript/CommonJs/AMD modules for the browser. Allows you to split your codebase into multiple bundles, which can be loaded on demand. Supports loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.",

@@ -108,3 +108,3 @@ "homepage": "https://github.com/webpack/webpack",

"chrome-trace-event": "^1.0.2",
"enhanced-resolve": "^5.24.2",
"enhanced-resolve": "^5.24.4",
"es-module-lexer": "^2.1.0",

@@ -204,3 +204,3 @@ "eslint-scope": "5.1.1",

"toml": "^4.1.1",
"tooling": "webpack/tooling#v1.26.4",
"tooling": "webpack/tooling#v1.27.0",
"ts-loader": "^9.6.0",

@@ -207,0 +207,0 @@ "typescript": "^6.0.3",

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display