webpack 格式化模块 第 一 节

这段代码主要是为 Webpack 中的 NormalModule 模块构建逻辑 提供一系列:

  1. 类型定义(Type Definitions) :包括 SourceMapLoaderItemNormalModuleCreateDataCompilationHooks 等。
  2. 工具函数(Utility Functions) :如 contextifySourceUrlcontextifySourceMapasBufferasString 等,用于处理路径、SourceMap 格式、数据转换。
  3. 错误处理(Error Wrapping) :定义了 NonErrorEmittedError,用于处理非标准错误对象。
  4. 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();
相关推荐
多多*14 分钟前
Spring之Bean的初始化 Bean的生命周期 全站式解析
java·开发语言·前端·数据库·后端·spring·servlet
linweidong19 分钟前
在企业级应用中,你如何构建一个全面的前端测试策略,包括单元测试、集成测试、端到端测试
前端·selenium·单元测试·集成测试·前端面试·mocha·前端面经
满怀101539 分钟前
【HTML 全栈进阶】从语义化到现代 Web 开发实战
前端·html
东锋1.31 小时前
前端动画库 Anime.js 的V4 版本,兼容 Vue、React
前端·javascript·vue.js
满怀10151 小时前
【Flask全栈开发指南】从零构建企业级Web应用
前端·python·flask·后端开发·全栈开发
小杨升级打怪中1 小时前
前端面经-webpack篇--定义、配置、构建流程、 Loader、Tree Shaking、懒加载与预加载、代码分割、 Plugin 机制
前端·webpack·node.js
Yvonne爱编码2 小时前
CSS- 4.4 固定定位(fixed)& 咖啡售卖官网实例
前端·css·html·状态模式·hbuilder
SuperherRo2 小时前
Web开发-JavaEE应用&SpringBoot栈&SnakeYaml反序列化链&JAR&WAR&构建打包
前端·java-ee·jar·反序列化·war·snakeyaml
大帅不是我2 小时前
Python多进程编程执行任务
java·前端·python
前端怎么个事2 小时前
框架的源码理解——V3中的ref和reactive
前端·javascript·vue.js