factorizeModule
make阶段第一步做的事是通过factorizeModule将dependency创建成相应的module。
_factorizeModule方法
找到_factorizeModule方法的定义:
_factorizeModule({ factory }) {
factory.create(
{
// ...
},
(err, result) => {
// ...
}
);
}该方法实际上调用的是factory方法。factory方法是一个工厂函数,用于创建module。这里以NormalModuleFactory为例。找到webpack/lib/NormalModuleFactory.js文件中create方法的定义,该方法最终会走到hooks.factorize:
this.hooks.factorize.callAsync(resolveData, (err, module) => {})而在NormalModuleFactory的constructor中正好定义了factorize.tapAsync,并会执行hooks.resolve.callAsync(),进入到resolve的回调函数。回调函数内主要由两段逻辑,一段是对dependency进行resolve,另一段逻辑是处理loader。
getResolver
在resolve之前,会先获取处理dependency的resolver:
const normalResolver = this.getResolver(
"normal",
dependencyType
? cachedSetProperty(
resolveOptions || EMPTY_RESOLVE_OPTIONS,
"dependencyType",
dependencyType
)
: resolveOptions
);
getResolver(type, resolveOptions) {
return this.resolverFactory.get(type, resolveOptions);
}resolverFactory在compiler对象初始化时就已经实例化。找到webpack/lib/ResolverFactory.js,调用get方法时如果缓存中没有resolver,就会调用_create方法创建一个resolver。
_create(type, resolveOptionsWithDepType) {
const resolver = /** @type {ResolverWithOptions} */ (Factory.createResolver(
resolveOptions
));
// ...
return resolver;
};createResolver
找到node_modules/enhanced-resolve/lib/ResolverFactory.js文件,里面定义了createResolver方法。该方法在实例化Resolver后,定义了一系列的钩子,用于整个dependency的resolve过程。
resolver.ensureHook("resolve");
resolver.ensureHook("internalResolve");
resolver.ensureHook("newInteralResolve");
resolver.ensureHook("parsedResolve");
resolver.ensureHook("describedResolve");
resolver.ensureHook("internal");
resolver.ensureHook("rawModule");
resolver.ensureHook("module");
resolver.ensureHook("resolveAsModule");
resolver.ensureHook("undescribedResolveInPackage");
resolver.ensureHook("resolveInPackage");
resolver.ensureHook("resolveInExistingDirectory");
resolver.ensureHook("relative");
resolver.ensureHook("describedRelative");
resolver.ensureHook("directory");
resolver.ensureHook("undescribedExistingDirectory");
resolver.ensureHook("existingDirectory");
resolver.ensureHook("undescribedRawFile");
resolver.ensureHook("rawFile");
resolver.ensureHook("file");
resolver.ensureHook("finalFile");
resolver.ensureHook("existingFile");
resolver.ensureHook("resolved");同时,后面还在对应的钩子上应用了一些插件用于resolve。比如:
// 处理 resolve 缓存
plugins.push(
new UnsafeCachePlugin(
source,
cachePredicate,
unsafeCache,
cacheWithContext,
`new-${source}`
)
);
// 处理 路径
plugins.push(
new ParsePlugin(`new-${source}`, resolveOptions, "parsed-resolve")
);
// 处理 描述文件
plugins.push(
new DescriptionFilePlugin(
"parsed-resolve",
descriptionFiles,
false,
"described-resolve"
)
);
// 处理 别名
if (alias.length > 0) {
plugins.push(new AliasPlugin("normal-resolve", alias, "internal-resolve"));
}
// 等等其他插件...这些插件正好对应了webpack中的resolve配置:webpack resolve 配置

resolver.resolve
获取完resolver后会调用resolver.resolve解析dependency,包括文件引用路径解析(比如是绝对路径还是相对路径还是模块,有哪些参数等等),文件路径查找,文件描述文件读取,文件别名替换等等操作。
loader 匹配
解析完当前dependency之后进入到回调函数,这里调用的是continueCallback方法。
const result = this.ruleSet.exec({
resource: resourceDataForRules.path,
realResource: resourceData.path,
resourceQuery: resourceDataForRules.query,
resourceFragment: resourceDataForRules.fragment,
mimetype: matchResourceData ? "" : resourceData.data.mimetype || "",
dependency: dependencyType,
descriptionData: matchResourceData
? undefined
: resourceData.data.descriptionFileData,
issuer: contextInfo.issuer,
compiler: contextInfo.compiler,
issuerLayer: contextInfo.issuerLayer || ""
});该方法会对dependency和loader的配置进行匹配,返回该dependency使用到的loaders。随后使用loaderResolver对每个loader进行文件解析,返回loader的文件路径。
this.resolveRequestArray(
contextInfo,
this.context,
useLoadersPost,
loaderResolver,
resolveContext,
(err, result) => {
postLoaders = result;
continueCallback(err);
}
);createModule
当dependency解析完成后,执行resolve回调:
this.hooks.factorize.tapAsync(
{
name: "NormalModuleFactory",
stage: 100
},
(resolveData, callback) => {
this.hooks.resolve.callAsync(resolveData, (err, result) => {
this.hooks.afterResolve.callAsync(resolveData, (err, result) => {
const createData = resolveData.createData;
this.hooks.createModule.callAsync(
createData,
resolveData,
(err, createdModule) => {
if (!createdModule) {
createdModule = new NormalModule(createData);
}
createdModule = this.hooks.module.call(
createdModule,
createData,
resolveData
);
return callback(null, createdModule);
}
);
});
});
}
);最终会通过createdModule = new NormalModule(createData)创建一个module,并将该module作为回调函数的参数传递给下一阶段,也就是addModule阶段。
总结
factorizeModule调用xxxModuleFactory.create方法将dependency转化成module。
在这个过程中会实例化resolver,通过不同的resolver对dependency进行解析,包括文件路径,文件描述,文件别名等等进行解析。
解析完成后与配置的loader匹配规则进行匹配。如果与该dependency匹配成功,那么会使用loaderResolver解析loader文件路径,存放到createData.loaders当中。
最后根据解析好的createData创建一个module。
