这段代码主要是为 Webpack 中的 NormalModule 模块构建逻辑 提供一系列:
- 类型定义(Type Definitions) :包括
SourceMap
、LoaderItem
、NormalModuleCreateData
、CompilationHooks
等。 - 工具函数(Utility Functions) :如
contextifySourceUrl
、contextifySourceMap
、asBuffer
、asString
等,用于处理路径、SourceMap 格式、数据转换。 - 错误处理(Error Wrapping) :定义了
NonErrorEmittedError
,用于处理非标准错误对象。 - Hook 管理(Hook Registration) :通过
compilationHooksMap
存储和隔离每个编译任务的钩子集合。
🔧 总体上,这是 Webpack 中 NormalModule 的 底层构建机制 的一部分,它服务于模块的读取、加载、解析、缓存、source map 注入、错误处理和生命周期钩子等操作。
js
/**
* 类型定义:引入 FakeHook 类型,用于替代真实的 Tapable Hook。
* 通常用于过时的 hook 的兼容处理。
*/
/**
* @template T
* @typedef {import("./util/deprecation").FakeHook<T>} FakeHook
*/
/**
* 类型定义:解析器(parser)和生成器(generator)所使用的参数类型。
* 实际上是任意键值对对象。
*/
/** @typedef {{[k: string]: any}} ParserOptions */
/** @typedef {{[k: string]: any}} GeneratorOptions */
/**
* 类型定义:NormalModule 在缓存中存储的结构,包括 parser、generator 及其 options。
*/
/** @typedef {UnsafeCacheData & { parser: undefined | Parser, parserOptions: undefined | ParserOptions, generator: undefined | Generator, generatorOptions: undefined | GeneratorOptions }} NormalModuleUnsafeCacheData */
/**
* 类型定义:Loader 上下文,传递给每个 Loader 的 `this` 对象。
*/
/** @template T
* @typedef {import("../declarations/LoaderContext").LoaderContext<T>} LoaderContext
*/
/** @template T
* @typedef {import("../declarations/LoaderContext").NormalModuleLoaderContext<T>} NormalModuleLoaderContext
*/
/**
* SourceMap 的结构定义,描述打包后的代码如何映射到源代码。
*/
/** @typedef {object} SourceMap
* @property {number} version Source Map 版本
* @property {string[]} sources 原始源文件路径
* @property {string} mappings 映射数据
* @property {string=} file 生成的文件名
* @property {string=} sourceRoot 可选源文件根路径
* @property {string[]=} sourcesContent 原始源代码内容
* @property {string[]=} names 所有涉及到的变量名
* @property {string=} debugId 调试用的 ID
*/
// 懒加载模块并进行缓存,避免重复 require。
const getInvalidDependenciesModuleWarning = memoize(() =>
require("./InvalidDependenciesModuleWarning")
);
const getValidate = memoize(() => require("schema-utils").validate);
// 判断是否为绝对路径的正则表达式。
const ABSOLUTE_PATH_REGEX = /^([a-zA-Z]:\\|\\\\|\/)/;
/**
* 类型定义:代表一个 loader 项,包含其路径、选项、标识符等。
*/
/** @typedef {object} LoaderItem
* @property {string} loader loader 路径
* @property {any} options 配置参数
* @property {string?} ident 可选标识符
* @property {string?} type 可选 loader 类型
*/
/**
* 将源文件路径转换为 `webpack://` 协议开头的路径,供 SourceMap 使用。
*/
const contextifySourceUrl = (context, source, associatedObjectForCache) => {
if (source.startsWith("webpack://")) return source;
return `webpack://${makePathsRelative(context, source, associatedObjectForCache)}`;
};
/**
* 替换 SourceMap 中所有 source 的路径为 `webpack://` 协议,便于调试。
*/
const contextifySourceMap = (context, sourceMap, associatedObjectForCache) => {
if (!Array.isArray(sourceMap.sources)) return sourceMap;
const { sourceRoot } = sourceMap;
const mapper = !sourceRoot
? source => source
: sourceRoot.endsWith("/")
? source =>
source.startsWith("/")
? `${sourceRoot.slice(0, -1)}${source}`
: `${sourceRoot}${source}`
: source =>
source.startsWith("/")
? `${sourceRoot}${source}`
: `${sourceRoot}/${source}`;
const newSources = sourceMap.sources.map(source =>
contextifySourceUrl(context, mapper(source), associatedObjectForCache)
);
return {
...sourceMap,
file: "x",
sourceRoot: undefined,
sources: newSources
};
};
/**
* 将 Buffer 类型转为 UTF-8 字符串,反之亦然。
*/
const asString = input => {
if (Buffer.isBuffer(input)) {
return input.toString("utf-8");
}
return input;
};
/**
* 将字符串转换为 buffer utf-8
*/
const asBuffer = input => {
if (!Buffer.isBuffer(input)) {
return Buffer.from(input, "utf-8");
}
return input;
};
/**
* 自定义 Webpack 错误类型:用于处理不是 Error 实例但被 emit 出来的错误。
*/
class NonErrorEmittedError extends WebpackError {
constructor(error) {
super();
this.name = "NonErrorEmittedError";
this.message = `(Emitted value instead of an instance of Error) ${error}`;
}
}
// 支持序列化该错误类型
makeSerializable(
NonErrorEmittedError,
"webpack/lib/NormalModule",
"NonErrorEmittedError"
);
/**
* NormalModule 编译过程中会触发的一系列钩子(生命周期钩子)
*/
/** @typedef {object} NormalModuleCompilationHooks
* @property {SyncHook<[LoaderContext<any>, NormalModule]>} loader 执行 loader 前调用
* @property {SyncHook<[LoaderItem[], NormalModule, LoaderContext<any>]>} beforeLoaders 加载 loaders 前调用
* @property {SyncHook<[NormalModule]>} beforeParse 解析前调用
* @property {SyncHook<[NormalModule]>} beforeSnapshot 生成文件快照前调用
* @property {HookMap<FakeHook<AsyncSeriesBailHook<[string, NormalModule], string | Buffer | null>>>} readResourceForScheme 基于 URL 协议读取资源
* @property {HookMap<AsyncSeriesBailHook<[LoaderContext<any>], string | Buffer | null>>} readResource 读取模块资源
* @property {AsyncSeriesBailHook<[NormalModule, NeedBuildContext], boolean>} needBuild 判断模块是否需要重新构建
*/
/**
* 创建 NormalModule 实例所需的数据结构。
*/
/** @typedef {object} NormalModuleCreateData
* @property {string=} layer 模块所属的构建层(可选)
* @property {JavaScriptModuleTypes | ""} type 模块类型(如 commonjs、esm)
* @property {string} request 解析后的请求路径(含 loader)
* @property {string} userRequest 用户原始请求(不含 loader)
* @property {string} rawRequest 原始请求(未解析)
* @property {LoaderItem[]} loaders 模块使用的 loader 列表
* @property {string} resource 最终资源的路径
* @property {Record<string, any>=} resourceResolveData 资源解析信息
* @property {string} context 上下文路径
* @property {string=} matchResource 虚拟资源路径(可选)
* @property {Parser} parser 模块使用的解析器
* @property {ParserOptions=} parserOptions 解析器配置
* @property {Generator} generator 代码生成器
* @property {GeneratorOptions=} generatorOptions 生成器配置
* @property {ResolveOptions=} resolveOptions 模块解析相关配置
*/
/**
* WeakMap 结构用于为每一个 Compilation 存储独立的 NormalModule 钩子,避免污染 Compilation 实例。
*/
const compilationHooksMap = new WeakMap();