webpack 检出图 第 一 节

✅ 类型定义与常量

  • 定义了多个模块类型、结构、函数签名等,用于 JSDoc 提示。

  • 常量:

    • EMPTY_SET:空集合,常用于默认值。
    • ZERO_BIG_INT:BigInt 形式的 0。
    • compareModuleIterables:模块排序器。

✅ 类型别名定义

  • ChunkFilterPredicate:用于过滤 chunk 的函数类型。
  • ModuleFilterPredicate:用于过滤 module 的函数类型。
  • EntryModuleWithChunkGroup:模块和它对应入口的元组。
  • ChunkSizeOptions:chunk 体积计算参数。

✅ 工具类

  • ModuleHashInfo

    • 存储模块的 hash 和渲染后的 hash。
    • 用于构建缓存和是否重新编译判断。

✅ 工具函数

  • getArray(set):将 SortableSet 转换为普通数组。

  • getModuleRuntimes(chunks):获取 chunk 集合中的所有 runtime。

  • modulesBySourceType(sourceTypesByModule)

    • 把模块集合按 sourceType 分类。
    • 可重用原集合,优化排序缓存。
  • createOrderedArrayFunction(comparator)

    • 创建一个集合转排序数组的函数,并做缓存。
  • getModulesSize(modules):计算所有模块的总大小(所有类型总和)。

  • getModulesSizes(modules):按 sourceType 分类统计每类大小。

  • isAvailableChunk(a, b)

    • 判断 chunk a 是否一定是 chunk b 的父 chunk。
    • 用于 chunk 之间的依赖关系判断。

✅ ChunkGraphModule 类

  • chunks:模块所属的 chunk 集合。
  • entryInChunks:如果模块是入口模块,则保存相关 chunk。
  • runtimeInChunks:如果模块是 runtime 模块,则保存相关 chunk。
  • hashes:不同 runtime 下的模块 hash 信息。
  • id:模块的输出 ID。
  • runtimeRequirements:各 runtime 下的 runtime 依赖。
  • graphHashes:模块图结构的 hash。
  • graphHashesWithConnections:模块图+依赖连接信息的 hash(更严格)。
js 复制代码
// ==== 类型导入(用于 JSDoc 类型提示) ====

/** @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock */
/** @typedef {import("./Chunk")} Chunk */
/** @typedef {import("./Chunk").ChunkId} ChunkId */
/** @typedef {import("./ChunkGroup")} ChunkGroup */
/** @typedef {import("./Generator").SourceTypes} SourceTypes */
/** @typedef {import("./Module")} Module */
/** @typedef {import("./Module").ReadOnlyRuntimeRequirements} ReadOnlyRuntimeRequirements */
/** @typedef {import("./ModuleGraph")} ModuleGraph */
/** @typedef {import("./ModuleGraphConnection").ConnectionState} ConnectionState */
/** @typedef {import("./RuntimeModule")} RuntimeModule */
/** @typedef {typeof import("./util/Hash")} Hash */
/** @typedef {import("./util/runtime").RuntimeSpec} RuntimeSpec */

// ==== 常量定义 ====

/** 空集合常量(不可变),可用于默认值等 */
const EMPTY_SET = new Set();

/** 零值的 BigInt 常量 */
const ZERO_BIG_INT = BigInt(0);

/** 比较模块集合的工具函数,用于排序 */
const compareModuleIterables = compareIterables(compareModulesByIdentifier);

// ==== 类型别名定义 ====

/**
 * 过滤 chunk 的函数类型定义
 * @typedef {(c: Chunk, chunkGraph: ChunkGraph) => boolean} ChunkFilterPredicate
 */

/**
 * 过滤模块的函数类型定义
 * @typedef {(m: Module) => boolean} ModuleFilterPredicate
 */

/**
 * 模块和它所属的入口 chunk group 对(可能 undefined)
 * @typedef {[Module, Entrypoint | undefined]} EntryModuleWithChunkGroup
 */

/**
 * 表示计算 chunk 大小时的一些配置参数
 * @typedef {object} ChunkSizeOptions
 * @property {number=} chunkOverhead chunk 的固定开销
 * @property {number=} entryChunkMultiplicator 入口 chunk 的权重乘数
 */

// ==== 工具类 ====

/**
 * 模块的 hash 信息,用于缓存和优化重构
 */
class ModuleHashInfo {
	/**
	 * @param {string} hash 模块构建产物的 hash
	 * @param {string} renderedHash 渲染后的 hash,用于输出文件名等
	 */
	constructor(hash, renderedHash) {
		this.hash = hash;
		this.renderedHash = renderedHash;
	}
}

// ==== 工具函数 ====

/**
 * 将 SortableSet 转为数组
 * @template T
 * @param {SortableSet<T>} set 可排序的集合
 * @returns {T[]} 转换后的数组
 */
const getArray = set => Array.from(set);

/**
 * 获取某些 chunk 所涉及的 runtime 集合(RuntimeSpecSet)
 * @param {SortableSet<Chunk>} chunks chunk 集合
 * @returns {RuntimeSpecSet} 所有 runtime 的集合
 */
const getModuleRuntimes = chunks => {
	const runtimes = new RuntimeSpecSet();
	for (const chunk of chunks) {
		runtimes.add(chunk.runtime);
	}
	return runtimes;
};

/**
 * 把模块集合根据 sourceType 分类
 * @param {WeakMap<Module, Set<string>> | undefined} sourceTypesByModule 提前缓存的模块 sourceType
 * @returns {function (SortableSet<Module>): Map<string, SortableSet<Module>>} 分类后的模块映射表
 */
const modulesBySourceType = sourceTypesByModule => set => {
	/** @type {Map<string, SortableSet<Module>>} */
	const map = new Map();
	for (const module of set) {
		const sourceTypes =
			(sourceTypesByModule && sourceTypesByModule.get(module)) ||
			module.getSourceTypes();
		for (const sourceType of sourceTypes) {
			let innerSet = map.get(sourceType);
			if (innerSet === undefined) {
				innerSet = new SortableSet();
				map.set(sourceType, innerSet);
			}
			innerSet.add(module);
		}
	}
	for (const [key, innerSet] of map) {
		// 如果所有模块都包含该 sourceType,就直接复用原集合,提高排序缓存效率
		if (innerSet.size === set.size) {
			map.set(key, set);
		}
	}
	return map;
};

// 默认情况下不提供预缓存,直接调用模块的 getSourceTypes 方法
const defaultModulesBySourceType = modulesBySourceType(undefined);

/**
 * 创建一个能排序集合并转数组的函数,并缓存该函数避免重复生成
 * @template T
 * @type {WeakMap<Function, any>}
 */
const createOrderedArrayFunctionMap = new WeakMap();

/**
 * 返回一个函数,该函数能将集合排序后转成数组
 * @template T
 * @param {function(T, T): -1|0|1} comparator 排序比较器
 * @returns {SetToArrayFunction<T>} 生成的转换函数
 */
const createOrderedArrayFunction = comparator => {
	let fn = createOrderedArrayFunctionMap.get(comparator);
	if (fn !== undefined) return fn;
	fn = set => {
		set.sortWith(comparator);
		return Array.from(set);
	};
	createOrderedArrayFunctionMap.set(comparator, fn);
	return fn;
};

/**
 * 计算模块总大小(所有 sourceType 加起来)
 * @param {Iterable<Module>} modules 模块集合
 * @returns {number} 模块总大小
 */
const getModulesSize = modules => {
	let size = 0;
	for (const module of modules) {
		for (const type of module.getSourceTypes()) {
			size += module.size(type);
		}
	}
	return size;
};

/**
 * 分 sourceType 统计模块大小
 * @param {Iterable<Module>} modules 模块集合
 * @returns {Record<string, number>} 每种类型的大小
 */
const getModulesSizes = modules => {
	const sizes = Object.create(null);
	for (const module of modules) {
		for (const type of module.getSourceTypes()) {
			sizes[type] = (sizes[type] || 0) + module.size(type);
		}
	}
	return sizes;
};

/**
 * 判断 chunk `a` 是否是 chunk `b` 的父 chunk
 * @param {Chunk} a 父 chunk
 * @param {Chunk} b 子 chunk
 * @returns {boolean} 如果 a 是 b 的父 chunk,返回 true
 */
const isAvailableChunk = (a, b) => {
	const queue = new Set(b.groupsIterable);
	for (const chunkGroup of queue) {
		if (a.isInGroup(chunkGroup)) continue;
		if (chunkGroup.isInitial()) return false;
		for (const parent of chunkGroup.parentsIterable) {
			queue.add(parent);
		}
	}
	return true;
};

// ==== 类型别名:用于 ChunkGraphModule 中属性类型标注 ====

/** @typedef {Set<Chunk>} EntryInChunks */
/** @typedef {Set<Chunk>} RuntimeInChunks */
/** @typedef {string | number} ModuleId */

// ==== ChunkGraph 中用于存储模块信息的结构 ====

class ChunkGraphModule {
	constructor() {
		/** @type {SortableSet<Chunk>} 模块所属的 chunk 集合 */
		this.chunks = new SortableSet();

		/** @type {EntryInChunks | undefined} 如果模块是 entry 模块,这里存放其所在的入口 chunk 集合 */
		this.entryInChunks = undefined;

		/** @type {RuntimeInChunks | undefined} 如果模块是 runtime 模块,这里存放其对应 chunk */
		this.runtimeInChunks = undefined;

		/** @type {RuntimeSpecMap<ModuleHashInfo> | undefined} 各 runtime 对应的 hash 信息 */
		this.hashes = undefined;

		/** @type {ModuleId | null} 模块 ID(用于输出) */
		this.id = null;

		/** @type {RuntimeSpecMap<Set<string>> | undefined} 各 runtime 所依赖的 runtime helper */
		this.runtimeRequirements = undefined;

		/** @type {RuntimeSpecMap<string> | undefined} 模块图结构的 hash */
		this.graphHashes = undefined;

		/** @type {RuntimeSpecMap<string> | undefined} 模块图 + 连接信息 的 hash(用于更精确地判断是否变化) */
		this.graphHashesWithConnections = undefined;
	}
}
相关推荐
wordbaby3 分钟前
TanStack Router 基于文件的路由
前端
wordbaby8 分钟前
TanStack Router 路由概念
前端
wordbaby10 分钟前
TanStack Router 路由匹配
前端
cc蒲公英11 分钟前
vue nextTick和setTimeout区别
前端·javascript·vue.js
程序员刘禹锡16 分钟前
Html中常用的块标签!!!12.16日
前端·html
我血条子呢26 分钟前
【CSS】类似渐变色弯曲border
前端·css
DanyHope27 分钟前
LeetCode 两数之和:从 O (n²) 到 O (n),空间换时间的经典实践
前端·javascript·算法·leetcode·职场和发展
hgz071028 分钟前
企业级多项目部署与Tomcat运维实战
前端·firefox
用户18878710698428 分钟前
基于vant3的搜索选择组件
前端
zhoumeina9928 分钟前
懒加载图片
前端·javascript·vue.js