.33-浅析webpack源码之doResolve事件流(5)

file => FileExistsPlugin

  这个事件流快接近尾声了,接下来是FileExistsPlugin,很奇怪的是在最后才来检验路径文件是否存在。

  源码如下:

FileExistsPlugin.prototype.apply = function(resolver) {
    var target = this.target;
    resolver.plugin(this.source, function(request, callback) {
        var fs = this.fileSystem;
        // file => d:workspacedocinput.js
        var file = request.path;
        fs.stat(file, function(err, stat) {
            // stat => 文件信息
            if (err || !stat) {
                if (callback.missing) callback.missing.push(file);
                if (callback.log) callback.log(file + " doesn't exist");
                return callback();
            }
            // 是否为文件
            if (!stat.isFile()) {
                if (callback.missing) callback.missing.push(file);
                if (callback.log) callback.log(file + " is not a file");
                return callback();
            }
            // 只做信息提示 不对结果做处理
            this.doResolve(target, request, "existing file: " + file, callback, true);
        }.bind(this));
    });
};

  这里只是简单的对路径文件进行状态获取,然后判断是否存在?是否是文件?最后调用一个有message的doResolve方法进入到下一个事件流。

existing-file => NextPlugin

  这个插件没什么卵用,之前提到过,会直接跳到下一个事件流。

resolved => ResultPlugin

  这是最后一个插件,在注入的时候只有一个参数,代表事件流的结尾。

ResultPlugin.prototype.apply = function(resolver) {
    resolver.plugin(this.source, function(request, callback) {
        var obj = Object.assign({}, request);
        // 调用resolver的事件流
        resolver.applyPluginsAsyncSeries1("result", obj, function(err) {
            if (err) return callback(err);
            callback(null, obj);
        });
    });
};

  这里直接调用了resolver的result事件流,并没有通过doResolve方法。

  经过测试,这个事件流并不存在,所以会直接调用无参callback,这个callback是什么呢???

  找了很久很久,终于找到了,回到最开始的resolve方法中,有个函数:

function onResolve(err, result) {
    // 无错误情况下调用这个
    if (!err && result) {
        return callback(null, result.path === false ? false : result.path + (result.query || ""), result);
    }

    localMissing = [];
    log = [];
    // 错误处理
    return resolver.doResolve("resolve", obj, message, createInnerCallback(onError, {
        log: writeLog,
        missing: localMissing,
        stack: callback.stack
    }));
}

  这里因为成功执行完事件流,第一个参数为null,所以会进入第一个if分支,最后返回的是path与result。

  这个callback就简单了,回到了resolve方法的调用地点:

asyncLib.parallel([
    callback => this.resolveRequestArray(contextInfo, context, elements, this.resolvers.loader, callback),
    callback => {
        if (resource === "" || resource[0] === "?")
            return callback(null, {
                resource
            });
        this.resolvers.normal.resolve(contextInfo, context, resource, (err, resource, resourceResolveData) => {
            // 从这里开始
            if (err) return callback(err);
            /* 
                resource => d:workspacedocinput.js
                resourceResolveData => 
                {
                    context: { issuer: '', compiler: undefined },
                    path: 'd:\workspace\doc\input.js',
                    request: undefined,
                    query: '',
                    module: false,
                    file: false,
                    descriptionFilePath: 'd:\workspace\doc\package.json',
                    descriptionFileData:{ *配置文件内容* },
                    descriptionFileRoot: 'd:\workspace\doc',
                    relativePath: './input.js',
                    __innerRequest_request: undefined,
                    __innerRequest_relativePath: './input.js',
                    __innerRequest: './input.js' 
                }
            */
            callback(null, {
                resourceResolveData,
                resource
            });
        });
    }
], (err, results) => { /**/ })

总结

  这里对之前的resolve方法所做的流程做一个总结,画一个图。

  剔除一些诸如NextPlugin这种垃圾插件以及重复加载的插件,插件加载按顺序,所有的type的before、after均视为一个事件流,特殊分支给给出说明。

原文地址:https://www.cnblogs.com/QH-Jimmy/p/8397760.html