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
。