codeGeneration(...)
功能总结:
用于执行模块的代码生成阶段,将构建后的模块内容根据不同的 sourceType
(如 javascript
, css
等)生成最终用于输出的代码,处理构建错误、收集 runtime 依赖,并封装为 CodeGenerationResult
返回。
核心逻辑:
- 如果模块尚未被解析,添加基础的 runtime 依赖;
- 遍历所有
sourceType
,通过模块的generator
来生成对应源码; - 对每个生成的源码进行缓存包装;
- 返回包含所有生成源码及 runtime 依赖的结果对象。
js
/**
* 生成模块的代码
*
* @param {CodeGenerationContext} context - 代码生成的上下文环境,包含依赖模板、运行时代码模板、模块图、chunk 图等
* @returns {CodeGenerationResult} - 返回代码生成的结果,包括不同类型源码及运行时需求
*/
codeGeneration({
dependencyTemplates,
runtimeTemplate,
moduleGraph,
chunkGraph,
runtime,
concatenationScope,
codeGenerationResults,
sourceTypes
}) {
/** @type {Set<string>} */
const runtimeRequirements = new Set();
// 获取构建信息中是否包含解析后的内容
const { parsed } = /** @type {BuildInfo} */ (this.buildInfo);
// 如果没有解析过,说明模块内容需要基本的运行时支持
if (!parsed) {
runtimeRequirements.add(RuntimeGlobals.module);
runtimeRequirements.add(RuntimeGlobals.exports);
runtimeRequirements.add(RuntimeGlobals.thisAsExports);
}
/** 用于获取 code generation 阶段使用的临时数据 */
const getData = () => this._codeGeneratorData;
const sources = new Map();
// 遍历所有 source 类型(如 javascript、css),生成对应源码
for (const type of sourceTypes || chunkGraph.getModuleSourceTypes(this)) {
const source = this.error
// 如果模块构建报错,返回一个抛出错误的源码片段
? new RawSource(
`throw new Error(${JSON.stringify(this.error.message)});`
)
: /** 使用模块的 generator 来生成代码 */
/** @type {Generator} */ (this.generator).generate(this, {
dependencyTemplates,
runtimeTemplate,
moduleGraph,
chunkGraph,
runtimeRequirements,
runtime,
concatenationScope,
codeGenerationResults,
getData,
type
});
// 缓存生成结果
if (source) {
sources.set(type, new CachedSource(source));
}
}
// 返回代码生成结果
const resultEntry = {
sources,
runtimeRequirements,
data: this._codeGeneratorData
};
return resultEntry;
}
originalSource()
功能总结:
返回该模块最初的、未经 webpack 转换的源码。
核心逻辑:
- 返回构建前保存的
_source
字段,通常是RawSource
类型。
js
/**
* 获取模块在被 webpack 转换前的原始源码
*
* @returns {Source | null}
*/
originalSource() {
return this._source;
}
invalidateBuild()
功能总结:
标记模块为"强制需要重建"。
核心逻辑:
- 设置内部标志
_forceBuild = true
,强制跳过缓存检查,触发重新构建。
js
/**
* 强制标记该模块下次构建时必须重建
*
* @returns {void}
*/
invalidateBuild() {
this._forceBuild = true;
}
needBuild(context, callback)
功能总结:
判断模块是否需要重新构建,是增量构建(cache)中非常关键的逻辑。
核心逻辑:
- 若标记了强制构建或模块出错,返回需构建;
- 如果模块不可缓存或没有快照,返回需构建;
- 对比
valueDependencies
和valueCacheVersions
是否有变动; - 调用
fileSystemInfo
检查文件快照是否仍有效; - 最后触发
needBuild
钩子供其他插件判断是否需要构建。
js
/**
* 判断当前模块是否需要重新构建
*
* @param {NeedBuildContext} context - 构建判断所需的上下文信息(包含文件快照、缓存版本等)
* @param {function((WebpackError | null)=, boolean=): void} callback - 回调函数,返回是否需要重新构建
* @returns {void}
*/
needBuild(context, callback) {
const { fileSystemInfo, compilation, valueCacheVersions } = context;
// 如果设置了强制构建,则直接返回 true
if (this._forceBuild) return callback(null, true);
// 如果之前构建报错,也需要重新构建
if (this.error) return callback(null, true);
const { cacheable, snapshot, valueDependencies } =
/** @type {BuildInfo} */ (this.buildInfo);
// 如果模块不可缓存,则每次都需要构建
if (!cacheable) return callback(null, true);
// 没有快照信息说明模块状态未知,也需要重新构建
if (!snapshot) return callback(null, true);
// 判断值依赖是否发生变化
if (valueDependencies) {
if (!valueCacheVersions) return callback(null, true);
for (const [key, value] of valueDependencies) {
if (value === undefined) return callback(null, true);
const current = valueCacheVersions.get(key);
if (
value !== current &&
(typeof value === "string" ||
typeof current === "string" ||
current === undefined ||
!isSubset(value, current))
) {
return callback(null, true);
}
}
}
// 校验文件快照是否仍然有效
fileSystemInfo.checkSnapshotValid(snapshot, (err, valid) => {
if (err) return callback(err);
if (!valid) return callback(null, true);
// 调用 compilation 钩子判断是否需要构建
const hooks = NormalModule.getCompilationHooks(compilation);
hooks.needBuild.callAsync(this, context, (err, needBuild) => {
if (err) {
return callback(
HookWebpackError.makeWebpackError(
err,
"NormalModule.getCompilationHooks().needBuild"
)
);
}
callback(null, Boolean(needBuild));
});
});
}
size(type)
功能总结:
返回该模块某个类型源码的大小估算值,用于 chunk 拆分、优化等场景。
核心逻辑:
- 优先从缓存中获取;
- 如果没有,则通过 generator 的
getSize
方法动态计算; - 结果缓存后返回,保证下次访问高效。
js
/**
* 获取模块的大小估算值(某个 source 类型)
*
* @param {string=} type - 指定的源码类型(如 javascript)
* @returns {number} - 返回模块估算大小(最小为 1)
*/
size(type) {
const cachedSize =
this._sourceSizes === undefined ? undefined : this._sourceSizes.get(type);
if (cachedSize !== undefined) {
return cachedSize;
}
// 使用 generator 获取模块的大小
const size = Math.max(
1,
/** @type {Generator} */ (this.generator).getSize(this, type)
);
// 缓存计算结果
if (this._sourceSizes === undefined) {
this._sourceSizes = new Map();
}
this._sourceSizes.set(type, size);
return size;
}
addCacheDependencies(...)
功能总结:
向 webpack 缓存系统添加当前模块的所有构建依赖项,用于确保依赖变更时可以触发重新构建。
核心逻辑:
- 如果有构建快照,则使用快照提供的依赖集合;
- 否则回退到旧字段(如
fileDependencies
); - 最后合并所有
buildDependencies
(如 loader/plugin 产生的构建依赖)。
js
/**
* 添加模块构建依赖信息到对应集合中
*
* @param {LazySet<string>} fileDependencies - 文件依赖集合
* @param {LazySet<string>} contextDependencies - 目录依赖集合
* @param {LazySet<string>} missingDependencies - 缺失依赖集合
* @param {LazySet<string>} buildDependencies - 构建依赖集合
*/
addCacheDependencies(
fileDependencies,
contextDependencies,
missingDependencies,
buildDependencies
) {
const { snapshot, buildDependencies: buildDeps } =
/** @type {BuildInfo} */ (this.buildInfo);
if (snapshot) {
// 从快照中恢复依赖
fileDependencies.addAll(snapshot.getFileIterable());
contextDependencies.addAll(snapshot.getContextIterable());
missingDependencies.addAll(snapshot.getMissingIterable());
} else {
// 如果没有 snapshot,使用旧字段恢复依赖
const {
fileDependencies: fileDeps,
contextDependencies: contextDeps,
missingDependencies: missingDeps
} = /** @type {BuildInfo} */ (this.buildInfo);
if (fileDeps !== undefined) fileDependencies.addAll(fileDeps);
if (contextDeps !== undefined) contextDependencies.addAll(contextDeps);
if (missingDeps !== undefined) missingDependencies.addAll(missingDeps);
}
if (buildDeps !== undefined) {
buildDependencies.addAll(buildDeps);
}
}
updateHash(hash, context)
功能总结:
更新当前模块的哈希,用于内容变更检测及构建缓存标识。
核心逻辑:
- 使用模块构建信息中的哈希作为一部分输入;
- 使用模块的
generator
更新哈希; - 调用父类方法补充其他必要信息。
js
/**
* 更新模块的 hash 值,用于标识模块内容变更
*
* @param {Hash} hash - 当前使用的 hash 对象
* @param {UpdateHashContext} context - hash 更新所需上下文(包含 chunkGraph、runtime 等)
* @returns {void}
*/
updateHash(hash, context) {
// 加入构建信息中的 hash 值
hash.update(/** @type {BuildInfo} */ (this.buildInfo).hash);
// 使用 generator 更新 hash
/** @type {Generator} */
(this.generator).updateHash(hash, {
module: this,
...context
});
// 调用父类逻辑(可能加入依赖信息等)
super.updateHash(hash, context);
}