compiler hook之make
webpack 怎么读取文件并处理依赖
SingleEntryDependency
依赖入口
compiler.hooks.make.tapAsync(
"SingleEntryPlugin",
(compilation, callback) => {
const { entry, name, context } = this;
const dep = SingleEntryPlugin.createDependency(entry, name);
compilation.addEntry(context, dep, name, callback);
}
);
复制代码
- addEntry
this.hooks.addEntry.call(entry, name);
const slot = {
name: name,
// TODO webpack 5 remove `request`
request: null,
module: null
};
if (entry instanceof ModuleDependency) {
slot.request = entry.request;
}
// TODO webpack 5: merge modules instead when multiple entry modules are supported
const idx = this._preparedEntrypoints.findIndex(slot => slot.name === name);
if (idx >= 0) {
// Overwrite existing entrypoint
this._preparedEntrypoints[idx] = slot;
} else {
this._preparedEntrypoints.push(slot);
}
this._addModuleChain(
context,
entry,
module => {
this.entries.push(module);
},
(err, module) => {
if (err) {
this.hooks.failedEntry.call(entry, name, err);
return callback(err);
}
if (module) {
slot.module = module;
} else {
const idx = this._preparedEntrypoints.indexOf(slot);
if (idx >= 0) {
this._preparedEntrypoints.splice(idx, 1);
}
}
this.hooks.succeedEntry.call(entry, name, module);
return callback(null, module);
}
);
复制代码
- _addModuleChain
const start = this.profile && Date.now();
const currentProfile = this.profile && {};
const errorAndCallback = this.bail
? err => {
callback(err);
}
: err => {
err.dependencies = [dependency];
this.errors.push(err);
callback();
};
if (
typeof dependency !== "object" ||
dependency === null ||
!dependency.constructor
) {
throw new Error("Parameter 'dependency' must be a Dependency");
}
const Dep = /** @type {DepConstructor} */ (dependency.constructor);
const moduleFactory = this.dependencyFactories.get(Dep);
if (!moduleFactory) {
throw new Error(
`No dependency factory available for this dependency type: ${
dependency.constructor.name
}`
);
}
this.semaphore.acquire(() => {
moduleFactory.create(
{
contextInfo: {
issuer: "",
compiler: this.compiler.name
},
context: context,
dependencies: [dependency]
},
(err, module) => {
if (err) {
this.semaphore.release();
return errorAndCallback(new EntryModuleNotFoundError(err));
}
let afterFactory;
if (currentProfile) {
afterFactory = Date.now();
currentProfile.factory = afterFactory - start;
}
const addModuleResult = this.addModule(module);
module = addModuleResult.module;
onModule(module);
dependency.module = module;
module.addReason(null, dependency);
const afterBuild = () => {
if (currentProfile) {
const afterBuilding = Date.now();
currentProfile.building = afterBuilding - afterFactory;
}
if (addModuleResult.dependencies) {
this.processModuleDependencies(module, err => {
if (err) return callback(err);
callback(null, module);
});
} else {
return callback(null, module);
}
};
if (addModuleResult.issuer) {
if (currentProfile) {
module.profile = currentProfile;
}
}
if (addModuleResult.build) {
this.buildModule(module, false, null, null, err => {
if (err) {
this.semaphore.release();
return errorAndCallback(err);
}
if (currentProfile) {
const afterBuilding = Date.now();
currentProfile.building = afterBuilding - afterFactory;
}
this.semaphore.release();
afterBuild();
});
} else {
this.semaphore.release();
this.waitForBuildingFinished(module, afterBuild);
}
}
);
});
复制代码
- moduleFactory.create
create(data, callback) {
const dependencies = data.dependencies;
const cacheEntry = dependencyCache.get(dependencies[0]);
if (cacheEntry) return callback(null, cacheEntry);
const context = data.context || this.context;
const resolveOptions = data.resolveOptions || EMPTY_RESOLVE_OPTIONS;
const request = dependencies[0].request;
const contextInfo = data.contextInfo || {};
this.hooks.beforeResolve.callAsync(
{
contextInfo,
resolveOptions,
context,
request,
dependencies
},
(err, result) => {
if (err) return callback(err);
// Ignored
if (!result) return callback();
const factory = this.hooks.factory.call(null);
// Ignored
if (!factory) return callback();
factory(result, (err, module) => {
if (err) return callback(err);
if (module && this.cachePredicate(module)) {
for (const d of dependencies) {
dependencyCache.set(d, module);
}
}
callback(null, module);
});
}
);
}
复制代码
- factory 是normalFactory 的factory hook监控函数 注意下面 回调函数 返回的是函数
this.hooks.factory.tap("NormalModuleFactory", () => (result, callback) => {
let resolver = this.hooks.resolver.call(null);
// Ignored
if (!resolver) return callback();
resolver(result, (err, data) => {
if (err) return callback(err);
// Ignored
if (!data) return callback();
// direct module
if (typeof data.source === "function") return callback(null, data);
this.hooks.afterResolve.callAsync(data, (err, result) => {
if (err) return callback(err);
// Ignored
if (!result) return callback();
let createdModule = this.hooks.createModule.call(result);
if (!createdModule) {
if (!result.request) {
return callback(new Error("Empty dependency (no request)"));
}
createdModule = new NormalModule(result);
}
createdModule = this.hooks.module.call(createdModule, result);
return callback(null, createdModule);
});
});
});
复制代码
- resolver 同理 resolver也是 normalFactory 的 hook
/**
* data = {contextInfo,
resolveOptions,
context,
request,
dependencies
}
*/
this.hooks.resolver.tap("NormalModuleFactory", () => (data, callback) => {
const contextInfo = data.contextInfo;
const context = data.context;
const request = data.request;
const loaderResolver = this.getResolver("loader");
const normalResolver = this.getResolver("normal", data.resolveOptions);
let matchResource = undefined;
let requestWithoutMatchResource = request;
const matchResourceMatch = MATCH_RESOURCE_REGEX.exec(request);
if (matchResourceMatch) {
matchResource = matchResourceMatch[1];
if (/^\.\.?\//.test(matchResource)) {
matchResource = path.join(context, matchResource);
}
requestWithoutMatchResource = request.substr(
matchResourceMatch[0].length
);
}
const noPreAutoLoaders = requestWithoutMatchResource.startsWith("-!");
const noAutoLoaders =
noPreAutoLoaders || requestWithoutMatchResource.startsWith("!");
const noPrePostAutoLoaders = requestWithoutMatchResource.startsWith("!!");
let elements = requestWithoutMatchResource
.replace(/^-?!+/, "")
.replace(/!!+/g, "!")
.split("!");
let resource = elements.pop();
elements = elements.map(identToLoaderRequest);
asyncLib.parallel(
[
callback =>
this.resolveRequestArray(
contextInfo,
context,
elements,
loaderResolver,
callback
),
callback => {
if (resource === "" || resource[0] === "?") {
return callback(null, {
resource
});
}
normalResolver.resolve(
contextInfo,
context,
resource,
{},
(err, resource, resourceResolveData) => {
if (err) return callback(err);
callback(null, {
resourceResolveData,
resource
});
}
);
}
],
(err, results) => {
if (err) return callback(err);
let loaders = results[0];
const resourceResolveData = results[1].resourceResolveData;
resource = results[1].resource;
// translate option idents
try {
for (const item of loaders) {
if (typeof item.options === "string" && item.options[0] === "?") {
const ident = item.options.substr(1);
item.options = this.ruleSet.findOptionsByIdent(ident);
item.ident = ident;
}
}
} catch (e) {
return callback(e);
}
if (resource === false) {
// ignored
return callback(
null,
new RawModule(
"/* (ignored) */",
`ignored ${context} ${request}`,
`${request} (ignored)`
)
);
}
const userRequest =
(matchResource !== undefined ? `${matchResource}!=!` : "") +
loaders
.map(loaderToIdent)
.concat([resource])
.join("!");
let resourcePath =
matchResource !== undefined ? matchResource : resource;
let resourceQuery = "";
const queryIndex = resourcePath.indexOf("?");
if (queryIndex >= 0) {
resourceQuery = resourcePath.substr(queryIndex);
resourcePath = resourcePath.substr(0, queryIndex);
}
const result = this.ruleSet.exec({
resource: resourcePath,
realResource:
matchResource !== undefined
? resource.replace(/\?.*/, "")
: resourcePath,
resourceQuery,
issuer: contextInfo.issuer,
compiler: contextInfo.compiler
});
const settings = {};
const useLoadersPost = [];
const useLoaders = [];
const useLoadersPre = [];
for (const r of result) {
if (r.type === "use") {
if (r.enforce === "post" && !noPrePostAutoLoaders) {
useLoadersPost.push(r.value);
} else if (
r.enforce === "pre" &&
!noPreAutoLoaders &&
!noPrePostAutoLoaders
) {
useLoadersPre.push(r.value);
} else if (
!r.enforce &&
!noAutoLoaders &&
!noPrePostAutoLoaders
) {
useLoaders.push(r.value);
}
} else if (
typeof r.value === "object" &&
r.value !== null &&
typeof settings[r.type] === "object" &&
settings[r.type] !== null
) {
settings[r.type] = cachedMerge(settings[r.type], r.value);
} else {
settings[r.type] = r.value;
}
}
asyncLib.parallel(
[
this.resolveRequestArray.bind(
this,
contextInfo,
this.context,
useLoadersPost,
loaderResolver
),
this.resolveRequestArray.bind(
this,
contextInfo,
this.context,
useLoaders,
loaderResolver
),
this.resolveRequestArray.bind(
this,
contextInfo,
this.context,
useLoadersPre,
loaderResolver
)
],
(err, results) => {
if (err) return callback(err);
loaders = results[0].concat(loaders, results[1], results[2]);
process.nextTick(() => {
const type = settings.type;
const resolveOptions = settings.resolve;
callback(null, {
context: context,
request: loaders
.map(loaderToIdent)
.concat([resource])
.join("!"),
dependencies: data.dependencies,
userRequest,
rawRequest: request,
loaders,
resource,
matchResource,
resourceResolveData,
settings,
type,
parser: this.getParser(type, settings.parser),
generator: this.getGenerator(type, settings.generator),
resolveOptions
});
});
}
);
}
);
});
复制代码
- getResolver 为loader和 normal 创建resolver
const loaderResolver = this.getResolver("loader");
const normalResolver = this.getResolver("normal", data.resolveOptions);
复制代码
- normalResolver.resolve
asyncLib.parallel(
[
callback =>
this.resolveRequestArray(
contextInfo,
context,
elements,
loaderResolver,
callback
),
callback => {
if (resource === "" || resource[0] === "?") {
return callback(null, {
resource
});
}
normalResolver.resolve(
contextInfo,
context,
resource,
{},
(err, resource, resourceResolveData) => {
if (err) return callback(err);
callback(null, {
resourceResolveData,
resource
});
}
);
}
],
(err, results) => {
context: {issuer: "", compiler: undefined}
descriptionFileData: {name: "module-inject", version: "1.0.0", description: "", main: "index.js", scripts: {…}, …}
descriptionFilePath: "C:\work\module-inject\package.json"
descriptionFileRoot: "C:\work\module-inject"
file: false
module: false
path: "C:\work\module-inject\src\main.js"
query: ""
relativePath: "./src/main.js"
request: undefined
__innerRequest: "./src/main.js"
__innerRequest_relativePath: "./src/main.js"
__innerRequest_request: undefined
__proto__: Object
复制代码
const result = this.ruleSet.exec({
resource: resourcePath,
realResource:
matchResource !== undefined
? resource.replace(/\?.*/, "")
: resourcePath,
resourceQuery,
issuer: contextInfo.issuer,
compiler: contextInfo.compiler
});
复制代码
rule 处理方式
/*
* [
0: {type: "type", value: "javascript/auto"}
1: {type: "resolve", value: {…}}
2: {type: "use", value: {…}, enforce: undefined}
length: 3
__proto__: Array(0)
]
*/
exec(data) {
const result = [];
this._run(
data,
{
rules: this.rules
},
result
);
return result;
}
_run(data, rule, result) {
// test conditions
if (rule.resource && !data.resource) return false;
if (rule.realResource && !data.realResource) return false;
if (rule.resourceQuery && !data.resourceQuery) return false;
if (rule.compiler && !data.compiler) return false;
if (rule.issuer && !data.issuer) return false;
if (rule.resource && !rule.resource(data.resource)) return false;
if (rule.realResource && !rule.realResource(data.realResource))
return false;
if (data.issuer && rule.issuer && !rule.issuer(data.issuer)) return false;
if (
data.resourceQuery &&
rule.resourceQuery &&
!rule.resourceQuery(data.resourceQuery)
) {
return false;
}
if (data.compiler && rule.compiler && !rule.compiler(data.compiler)) {
return false;
}
// apply
const keys = Object.keys(rule).filter(key => {
return ![
"resource",
"realResource",
"resourceQuery",
"compiler",
"issuer",
"rules",
"oneOf",
"use",
"enforce"
].includes(key);
});
for (const key of keys) {
result.push({
type: key,
value: rule[key]
});
}
if (rule.use) {
const process = use => {
if (typeof use === "function") {
process(use(data));
} else if (Array.isArray(use)) {
use.forEach(process);
} else {
result.push({
type: "use",
value: use,
enforce: rule.enforce
});
}
};
process(rule.use);
}
if (rule.rules) {
for (let i = 0; i < rule.rules.length; i++) {
this._run(data, rule.rules[i], result);
}
}
if (rule.oneOf) {
for (let i = 0; i < rule.oneOf.length; i++) {
if (this._run(data, rule.oneOf[i], result)) break;
}
}
return true;
}
复制代码
const settings = {};
const useLoadersPost = [];
const useLoaders = [];
const useLoadersPre = [];
for (const r of result) {
if (r.type === "use") {
if (r.enforce === "post" && !noPrePostAutoLoaders) {
useLoadersPost.push(r.value);
} else if (
r.enforce === "pre" &&
!noPreAutoLoaders &&
!noPrePostAutoLoaders
) {
useLoadersPre.push(r.value);
} else if (
!r.enforce &&
!noAutoLoaders &&
!noPrePostAutoLoaders
) {
useLoaders.push(r.value);
}
} else if (
typeof r.value === "object" &&
r.value !== null &&
typeof settings[r.type] === "object" &&
settings[r.type] !== null
) {
settings[r.type] = cachedMerge(settings[r.type], r.value);
} else {
settings[r.type] = r.value;
}
}
复制代码
resolveRequestArray(contextInfo, context, array, resolver, callback) {
if (array.length === 0) return callback(null, []);
asyncLib.map(
array,
(item, callback) => {
resolver.resolve(
contextInfo,
context,
item.loader,
{},
(err, result) => {
if (
err &&
/^[^/]*$/.test(item.loader) &&
!/-loader$/.test(item.loader)
) {
return resolver.resolve(
contextInfo,
context,
item.loader + "-loader",
{},
err2 => {
if (!err2) {
err.message =
err.message +
"\n" +
"BREAKING CHANGE: It's no longer allowed to omit the '-loader' suffix when using loaders.\n" +
` You need to specify '${
item.loader
}-loader' instead of '${item.loader}',\n` +
" see https://webpack.js.org/migrate/3/#automatic-loader-module-name-extension-removed";
}
callback(err);
}
);
}
if (err) return callback(err);
const optionsOnly = item.options
? {
options: item.options
}
: undefined;
return callback(
null,
Object.assign({}, item, identToLoaderRequest(result), optionsOnly)
);
}
);
},
callback
);
}
复制代码
--->
resolver 方法 回调
resolver(result, (err, data) => {
if (err) return callback(err);
// Ignored
if (!data) return callback();
// direct module
if (typeof data.source === "function") return callback(null, data);
this.hooks.afterResolve.callAsync(data, (err, result) => {
if (err) return callback(err);
// Ignored
if (!result) return callback();
let createdModule = this.hooks.createModule.call(result);
if (!createdModule) {
if (!result.request) {
return callback(new Error("Empty dependency (no request)"));
}
createdModule = new NormalModule(result);
}
createdModule = this.hooks.module.call(createdModule, result);
return callback(null, createdModule);
});
});
复制代码
----> 推入 factory 回调
this.hooks.factory.tap("NormalModuleFactory", () => (result, callback) => {
let resolver = this.hooks.resolver.call(null);
// Ignored
if (!resolver) return callback();
resolver(result, (err, data) => {
if (err) return callback(err);
// Ignored
if (!data) return callback();
// direct module
if (typeof data.source === "function") return callback(null, data);
this.hooks.afterResolve.callAsync(data, (err, result) => {
if (err) return callback(err);
// Ignored
if (!result) return callback();
let createdModule = this.hooks.createModule.call(result);
if (!createdModule) {
if (!result.request) {
return callback(new Error("Empty dependency (no request)"));
}
createdModule = new NormalModule(result);
}
createdModule = this.hooks.module.call(createdModule, result);
return callback(null, createdModule);
});
});
复制代码
-----> 推入factory.create 回调
factory.create(
{
contextInfo: {
issuer: module.nameForCondition && module.nameForCondition(),
compiler: this.compiler.name
},
resolveOptions: module.resolveOptions,
context: module.context,
dependencies: dependencies
},
(err, dependentModule) => {
let afterFactory;
const isOptional = () => {
return dependencies.every(d => d.optional);
};
const errorOrWarningAndCallback = err => {
if (isOptional()) {
return warningAndCallback(err);
} else {
return errorAndCallback(err);
}
};
if (err) {
semaphore.release();
return errorOrWarningAndCallback(
new ModuleNotFoundError(module, err)
);
}
if (!dependentModule) {
semaphore.release();
return process.nextTick(callback);
}
if (currentProfile) {
afterFactory = Date.now();
....
复制代码
- buildModule 这里就去读取文件
this.buildModule(
dependentModule,
isOptional(),
module,
dependencies,
err => {
if (err) {
semaphore.release();
return errorOrWarningAndCallback(err);
}
if (currentProfile) {
const afterBuilding = Date.now();
currentProfile.building = afterBuilding - afterFactory;
}
semaphore.release();
afterBuild();
}
);
复制代码
- buildModule
let callbackList = this._buildingModules.get(module);
if (callbackList) {
callbackList.push(thisCallback);
return;
}
this._buildingModules.set(module, (callbackList = [thisCallback]));
const callback = err => {
this._buildingModules.delete(module);
for (const cb of callbackList) {
cb(err);
}
};
this.hooks.buildModule.call(module);
module.build(
this.options,
this,
this.resolverFactory.get("normal", module.resolveOptions),
this.inputFileSystem,
error => {
const errors = module.errors;
for (let indexError = 0; indexError < errors.length; indexError++) {
const err = errors[indexError];
err.origin = origin;
err.dependencies = dependencies;
if (optional) {
this.warnings.push(err);
} else {
this.errors.push(err);
}
}
const warnings = module.warnings;
for (
let indexWarning = 0;
indexWarning < warnings.length;
indexWarning++
) {
const war = warnings[indexWarning];
war.origin = origin;
war.dependencies = dependencies;
this.warnings.push(war);
}
const originalMap = module.dependencies.reduce((map, v, i) => {
map.set(v, i);
return map;
}, new Map());
module.dependencies.sort((a, b) => {
const cmp = compareLocations(a.loc, b.loc);
if (cmp) return cmp;
return originalMap.get(a) - originalMap.get(b);
});
if (error) {
this.hooks.failedModule.call(module, error);
return callback(error);
}
this.hooks.succeedModule.call(module);
return callback();
}
);
复制代码
- module.build
this.buildTimestamp = Date.now();
this.built = true;
this._source = null;
// 生成语法树 这个很重要
this._ast = null;
this._buildHash = "";
this.error = null;
this.errors.length = 0;
this.warnings.length = 0;
this.buildMeta = {};
this.buildInfo = {
cacheable: false,
fileDependencies: new Set(),
contextDependencies: new Set()
};
return this.doBuild(options, compilation, resolver, fs, err => {
this._cachedSources.clear();
// if we have an error mark module as failed and exit
if (err) {
this.markModuleAsErrored(err);
this._initBuildHash(compilation);
return callback();
}
// check if this module should !not! be parsed.
// if so, exit here;
const noParseRule = options.module && options.module.noParse;
if (this.shouldPreventParsing(noParseRule, this.request)) {
this._initBuildHash(compilation);
return callback();
}
const handleParseError = e => {
const source = this._source.source();
const error = new ModuleParseError(this, source, e);
this.markModuleAsErrored(error);
this._initBuildHash(compilation);
return callback();
};
const handleParseResult = result => {
this._lastSuccessfulBuildMeta = this.buildMeta;
this._initBuildHash(compilation);
return callback();
};
try {
const result = this.parser.parse(
this._ast || this._source.source(),
{
current: this,
module: this,
compilation: compilation,
options: options
},
(err, result) => {
if (err) {
handleParseError(err);
} else {
handleParseResult(result);
}
}
);
if (result !== undefined) {
// parse is sync
handleParseResult(result);
}
} catch (e) {
handleParseError(e);
}
});
复制代码
- doBuild
loaderContext
/**
* emitError: error => {…}
emitFile: (name, content, sourceMap) => {…}
emitWarning: warning => {…}
exec: (code, filename) => {…}
fs: CachedInputFileSystem {fileSystem: NodeJsInputFileSystem, _statStorage: Storage, _readdirStorage: Storage, _readFileStorage: Storage, _readJsonStorage: Storage, …}
getResolve: getResolve(options) { const child = options ? resolver.withOptions(options) : resolver; return (context, request, callback) => {…}
loadModule: (request, callback) => {…}
resolve: ƒ resolve(context, request, callback)
rootContext: "C:\work\module-inject"
sourceMap: false
target: "web"
version: 2
webpack: true
_compilation: Compilation {_pluginCompat: SyncBailHook, hooks: {…}, name: undefined, compiler: Compiler, resolverFactory: ResolverFactory, …}
_compiler: Compiler {_pluginCompat: SyncBailHook, hooks: {…}, name: undefined, parentCompilation: undefined, outputPath: "C:\work\module-inject\dist", …}
_module: NormalModule {dependencies: Array(0), blocks: Array(0), variables: Array(0), type: "javascript/auto", context: "C:\work\module-inject\src\truck", …}
__proto__:
constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
valueOf: ƒ valueOf()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()
*/
doBuild(options, compilation, resolver, fs, callback) {
const loaderContext = this.createLoaderContext(
resolver,
options,
compilation,
fs
);
runLoaders(
{
resource: this.resource,
loaders: this.loaders,
context: loaderContext,
readResource: fs.readFile.bind(fs)
},
(err, result) => {
if (result) {
this.buildInfo.cacheable = result.cacheable;
this.buildInfo.fileDependencies = new Set(result.fileDependencies);
this.buildInfo.contextDependencies = new Set(
result.contextDependencies
);
}
if (err) {
if (!(err instanceof Error)) {
err = new NonErrorEmittedError(err);
}
const currentLoader = this.getCurrentLoader(loaderContext);
const error = new ModuleBuildError(this, err, {
from:
currentLoader &&
compilation.runtimeTemplate.requestShortener.shorten(
currentLoader.loader
)
});
return callback(error);
}
const resourceBuffer = result.resourceBuffer;
const source = result.result[0];
const sourceMap = result.result.length >= 1 ? result.result[1] : null;
const extraInfo = result.result.length >= 2 ? result.result[2] : null;
if (!Buffer.isBuffer(source) && typeof source !== "string") {
const currentLoader = this.getCurrentLoader(loaderContext, 0);
const err = new Error(
`Final loader (${
currentLoader
? compilation.runtimeTemplate.requestShortener.shorten(
currentLoader.loader
)
: "unknown"
}) didn't return a Buffer or String`
);
const error = new ModuleBuildError(this, err);
return callback(error);
}
this._source = this.createSource(
this.binary ? asBuffer(source) : asString(source),
resourceBuffer,
sourceMap
);
this._ast =
typeof extraInfo === "object" &&
extraInfo !== null &&
extraInfo.webpackAST !== undefined
? extraInfo.webpackAST
: null;
return callback();
}
);
复制代码
- runLoaders
exports.runLoaders = function runLoaders(options, callback) {
// read options
var resource = options.resource || "";
var loaders = options.loaders || [];
var loaderContext = options.context || {};
var readResource = options.readResource || readFile;
//
var splittedResource = resource && splitQuery(resource);
var resourcePath = splittedResource ? splittedResource[0] : undefined;
var resourceQuery = splittedResource ? splittedResource[1] : undefined;
var contextDirectory = resourcePath ? dirname(resourcePath) : null;
// execution state
var requestCacheable = true;
var fileDependencies = [];
var contextDependencies = [];
// prepare loader objects
loaders = loaders.map(createLoaderObject);
loaderContext.context = contextDirectory;
loaderContext.loaderIndex = 0;
loaderContext.loaders = loaders;
loaderContext.resourcePath = resourcePath;
loaderContext.resourceQuery = resourceQuery;
loaderContext.async = null;
loaderContext.callback = null;
loaderContext.cacheable = function cacheable(flag) {
if(flag === false) {
requestCacheable = false;
}
};
loaderContext.dependency = loaderContext.addDependency = function addDependency(file) {
fileDependencies.push(file);
};
loaderContext.addContextDependency = function addContextDependency(context) {
contextDependencies.push(context);
};
loaderContext.getDependencies = function getDependencies() {
return fileDependencies.slice();
};
loaderContext.getContextDependencies = function getContextDependencies() {
return contextDependencies.slice();
};
loaderContext.clearDependencies = function clearDependencies() {
fileDependencies.length = 0;
contextDependencies.length = 0;
requestCacheable = true;
};
Object.defineProperty(loaderContext, "resource", {
enumerable: true,
get: function() {
if(loaderContext.resourcePath === undefined)
return undefined;
return loaderContext.resourcePath + loaderContext.resourceQuery;
},
set: function(value) {
var splittedResource = value && splitQuery(value);
loaderContext.resourcePath = splittedResource ? splittedResource[0] : undefined;
loaderContext.resourceQuery = splittedResource ? splittedResource[1] : undefined;
}
});
Object.defineProperty(loaderContext, "request", {
enumerable: true,
get: function() {
return loaderContext.loaders.map(function(o) {
return o.request;
}).concat(loaderContext.resource || "").join("!");
}
});
Object.defineProperty(loaderContext, "remainingRequest", {
enumerable: true,
get: function() {
if(loaderContext.loaderIndex >= loaderContext.loaders.length - 1 && !loaderContext.resource)
return "";
return loaderContext.loaders.slice(loaderContext.loaderIndex + 1).map(function(o) {
return o.request;
}).concat(loaderContext.resource || "").join("!");
}
});
Object.defineProperty(loaderContext, "currentRequest", {
enumerable: true,
get: function() {
return loaderContext.loaders.slice(loaderContext.loaderIndex).map(function(o) {
return o.request;
}).concat(loaderContext.resource || "").join("!");
}
});
Object.defineProperty(loaderContext, "previousRequest", {
enumerable: true,
get: function() {
return loaderContext.loaders.slice(0, loaderContext.loaderIndex).map(function(o) {
return o.request;
}).join("!");
}
});
Object.defineProperty(loaderContext, "query", {
enumerable: true,
get: function() {
var entry = loaderContext.loaders[loaderContext.loaderIndex];
return entry.options && typeof entry.options === "object" ? entry.options : entry.query;
}
});
Object.defineProperty(loaderContext, "data", {
enumerable: true,
get: function() {
return loaderContext.loaders[loaderContext.loaderIndex].data;
}
});
// finish loader context
if(Object.preventExtensions) {
Object.preventExtensions(loaderContext);
}
var processOptions = {
resourceBuffer: null,
readResource: readResource
};
iteratePitchingLoaders(processOptions, loaderContext, function(err, result) {
if(err) {
return callback(err, {
cacheable: requestCacheable,
fileDependencies: fileDependencies,
contextDependencies: contextDependencies
});
}
callback(null, {
result: result,
resourceBuffer: processOptions.resourceBuffer,
cacheable: requestCacheable,
fileDependencies: fileDependencies,
contextDependencies: contextDependencies
});
});
};
});
复制代码
----> module.build callback
module.build(
this.options,
this,
this.resolverFactory.get("normal", module.resolveOptions),
this.inputFileSystem,
error => {
const errors = module.errors;
for (let indexError = 0; indexError < errors.length; indexError++) {
const err = errors[indexError];
err.origin = origin;
err.dependencies = dependencies;
if (optional) {
this.warnings.push(err);
} else {
this.errors.push(err);
}
}
const warnings = module.warnings;
for (
let indexWarning = 0;
indexWarning < warnings.length;
indexWarning++
) {
const war = warnings[indexWarning];
war.origin = origin;
war.dependencies = dependencies;
this.warnings.push(war);
}
const originalMap = module.dependencies.reduce((map, v, i) => {
map.set(v, i);
return map;
}, new Map());
module.dependencies.sort((a, b) => {
const cmp = compareLocations(a.loc, b.loc);
if (cmp) return cmp;
return originalMap.get(a) - originalMap.get(b);
});
if (error) {
this.hooks.failedModule.call(module, error);
return callback(error);
}
this.hooks.succeedModule.call(module);
return callback();
}
复制代码
---> 回到factory.create buildModule 回调
if (addModuleResult.build) {
this.buildModule(
dependentModule,
isOptional(),
module,
dependencies,
err => {
if (err) {
semaphore.release();
return errorOrWarningAndCallback(err);
}
if (currentProfile) {
const afterBuilding = Date.now();
currentProfile.building = afterBuilding - afterFactory;
}
semaphore.release();
afterBuild();
}
);
复制代码
- afterBuild // 处理依赖
processModuleDependencies()
const addDependency = dep => {
const resourceIdent = dep.getResourceIdentifier();
if (resourceIdent) {
const factory = this.dependencyFactories.get(dep.constructor);
if (factory === undefined) {
throw new Error(
`No module factory available for dependency type: ${
dep.constructor.name
}`
);
}
let innerMap = dependencies.get(factory);
if (innerMap === undefined) {
dependencies.set(factory, (innerMap = new Map()));
}
let list = innerMap.get(resourceIdent);
if (list === undefined) innerMap.set(resourceIdent, (list = []));
list.push(dep);
}
};
// 重新添加module 进行处理
this.addModuleDependencies(
module,
sortedDependencies,
this.bail,
null,
true,
callback
);
复制代码