浅析webpack源码之convert-argv模块(二)

11次阅读

共计 7597 个字符,预计需要花费 19 分钟才能阅读完成。

打开 webpeck-cli 下的 convert-argv.js 文件
// 定义 options 为空数组
const options = [];
// webpack -d 检查 - d 指令
if (argv.d) {
//…
}
// webpack -p
if (argv.p) {
//…
}
if (argv.output) {
//…
}
//…
/* 如果有 –config –config webpack.config.js config 就是 webpack.config.js
可以这样理解
“dev”: “webpack-dev-server –inline –progress –hot –config webpack.config.js”, 当我们 npm run dev 的时候执行这段
package.json 的内容 此时有 config 读取 webpack.config.js 的内容 当我们 npm run build 时 执行 “webpack” 此时没有 config 走 else 分支 */
if (argv.config) {
// … 获取文件
}else{
/* 读取默认配置 ts co 等后缀类
defaultConfigFiles 是 数组 [{path:
‘/Users/orion/Desktop/react-beauty-highcharts/webpack.config.js’,
ext: ‘.js’
},{path:’/Users/orion/Desktop/react-beauty-highcharts/webpack.config.ts’, ext: ‘.ts’},{},…] */
for (i = 0; i < defaultConfigFiles.length; i++) {
const webpackConfig = defaultConfigFiles[i].path;
// 读取文件, 如果有的话 push 推进去
if (fs.existsSync(webpackConfig)) {
configFiles.push({
path: webpackConfig,
ext: defaultConfigFiles[i].ext
});
// 最终结果 configFiles is the Array [{ path:’/Users/orion/Desktop/react-beauty-highcharts/webpack.config.js’,
// ext: ‘.js’ } ]
break;
}

}
}
process.cwd() 是 node.js 里读取文件路径的一个 API
//configFiles 长度大于 0 时
if (configFiles.length > 0) {
// …
const requireConfig = function requireConfig(configPath) {
// 这是局部 options 不要和全局的 options 数组混淆
let options = (function WEBPACK_OPTIONS() {
if (argv.configRegister && argv.configRegister.length) {
module.paths.unshift(
path.resolve(process.cwd(), “node_modules”),
process.cwd()
);
argv.configRegister.forEach(dep => {
require(dep);
});
return require(configPath);
} else {
// 读取路径下的文件内容返回
return require(configPath);
}
})();
// 预处理 options,options 若是数组的话,处理成对象之类的
options = prepareOptions(options, argv);
return options;
};
configFiles.forEach(function(file) {
/// interpret.extensions[.js] 为 null
// 这里直接跳出
registerCompiler(interpret.extensions[file.ext]);
// options 这里是全局 options 空数组
options.push(requireConfig(file.path));
});
// configFileLoaded 加载完毕
configFileLoaded = true;
}
// 如果没有加载完毕,调用函数传递空数组
if (!configFileLoaded) {
return processConfiguredOptions({});
} else if (options.length === 1) {
// 如果只有一个,把仅有的传进去
return processConfiguredOptions(options[0]);
} else {
// 传 options
return processConfiguredOptions(options);
}
注意了,这里有一个 return 也就是这个 convert-argv 模块的最终返回结果,函数到这里就结束了。接下来我看看一下 processConfiguredOptions 函数
我们先按照 npm run build 分支走 options.length 为 1, 读取 options[0] 是 webpack.config.js 里的 module.exports ={} 对象,饶了这大的一个圈子,那么接下来一起来看一看对你的输入配置做了怎么样的处理吧????
function processConfiguredOptions(options) {
// 非法输出类型检测
const webpackConfigurationValidationErrors = validateSchema(
webpackConfigurationSchema,
options
);
if (webpackConfigurationValidationErrors.length) {
const error = new WebpackOptionsValidationError(
webpackConfigurationValidationErrors
);
// 报错处理,具体什么是非法,不影响主流程先过????
console.error(
error.message,
`\nReceived: ${typeof options} : ${JSON.stringify(options, null, 2)}`
);
process.exit(-1); // eslint-disable-line
}

// 如果 options 是 Promise????,以后待查,按照我们的 npm run build 流程走不是 Promise
if (typeof options.then === “function”) {
return options.then(processConfiguredOptions);
}

/* process ES6 default 检测, 虽说咱们的 webpack.congfig.js 的内容是用的 ES6 module.exports 写的,但是 options 是对象已经被读出来了,所以不走这个分支,
可是为什么要再写一遍呢?看来还是有这种情况的,继续往下读 */
if (typeof options === “object” && typeof options.default === “object”) {
return processConfiguredOptions(options.default);
}

// filter multi-config by name, 对于多配置的处理,我们先不走这个分支
if (Array.isArray(options) && argv[“config-name”]) {
//…
}
if (Array.isArray(options)) {
options.forEach(processOptions);
} else {
/* 看了这个多判断终于有没有找到家的感觉,回家的路程好艰辛, 接下来我们看看 processOptions 函数对 options 做了什么
それは何をしましたか????? */
processOptions(options);
}
// …
// 这个 return 正好对应上个 return,是最终的模块返回值
return options;
function processOptions(options) {
// 基本处理函数
function ifArg(name, fn, init, finalize) {
//…
}
function ifArgPair(name, fn, init, finalize){
//…
}
function ifBooleanArg(name, fn) {
//…
}
}
我们看到 processOptions 先定义了几个函数 ifArg,ifArgPair,ifBooleanArg,loadPlugin 根据名字可以翻译如果是参数,如果是键值对形式参数 如果是布尔参数,如果是插件 提起插件,是不是就想起了我们如下写的 plugins, 对这个配置的处理,读源码联想大法和通过名称翻译的方法还是很重要的,自己写的时候,尽量起好懂的名称,不要用中文拼音????
{
plugins: [
new HtmlWebpackPlugin({// 实例化生成 html 插件
title: ‘title’,
template: ‘./src/index.html’,
filename: ‘index.html’,
inlineSource: ‘.(js|css))$’,
minify: {
removeComments: true,
collapseWhitespace: true
},
chunks: [“index”]
}),
new HtmlWebpackPlugin()
],
}
接下来是真正的函数调用
ifArg(“mode”, function(value) {
options.mode = value;
});

function ifArg(name, fn, init, finalize) {
const isArray = Array.isArray(argv[name]);
const isSet = typeof argv[name] !== “undefined” && argv[name] !== null;
// isArray 为 false 直接返回
if (!isArray && !isSet) return;

init && init();
if (isArray) argv[name].forEach(fn);
else if (isSet) fn(argv[name], -1);
finalize && finalize();
}

说一下 argv 目前 argv 是这么个对象
webpakck -p 有 - p 的时候有 p =true
有 - d 的时候有 d =true
‘$0’ 是 webpack 的目前地址
其他的自己悟,哈哈哈哈哈???? 听说 90 后喜欢用这个表情

{
_: [],
cache: null,
bail: null,
profile: null,
color: [Function: getSupportLevel],
colors: [Function: getSupportLevel],
d: true,
p: true,
‘info-verbosity’: ‘info’,
infoVerbosity: ‘info’,
‘$0’:
‘/Users/orion/Desktop/react-beauty-highcharts/node_modules/.bin/webpack’,
debug: true,
‘output-pathinfo’: true,
devtool: ‘eval-cheap-module-source-map’,
mode: ‘development’,
‘optimize-minimize’: true,
define: [‘process.env.NODE_ENV=”production”‘]
}
我们接着读这个函数
function ifArg(name, fn, init, finalize) {
// 如果 argv[name] 是数组
const isArray = Array.isArray(argv[name]);
// 如果 argv[name] 存在
const isSet = typeof argv[name] !== “undefined” && argv[name] !== null;
// isArray 和 isSet 同时为 false 的时候返回
if (!isArray && !isSet) return;
// 按照目前的流程走,能过来的 name 有 define,output-pathinfo,debug,devtool,optimize-minimize
// 如果 init 函数存在就执行 init 函数, 目前流程只有 define 有 init,执行函数 defineObject = {};
init && init();
// 如果是数组,执行传入的 fn
if (isArray) argv[name].forEach(fn);
else if (isSet) fn(argv[name], -1);
finalize && finalize();
}
这里只有一个是数组 define,我们追踪 define
ifArgPair(
“define”,
function(name, value) {
if (name === null) {
name = value;
value = true;
}
defineObject[name] = value;
},
function() {
defineObject = {};
},
function() {
const DefinePlugin = require(“webpack”).DefinePlugin;
addPlugin(options, new DefinePlugin(defineObject));
}
);
}

function ifArgPair(name, fn, init, finalize) {
ifArg(
name,
function(content, idx) {
const i = content.indexOf(“=”);
if (i < 0) {
return fn(null, content, idx);
} else {
// 根据
return fn(content.substr(0, i), content.substr(i + 1), idx);
}
},
init,
finalize
);
}
// 接着,ifArgPair 会调用 ifArg 对 fn 进行了处理
从 fn 到
function(content, idx) {
const i = content.indexOf(“=”);
if (i < 0) {
return fn(null, content, idx);
} else {
// 根据
return fn(content.substr(0, i), content.substr(i + 1), idx);
}
}

// 目前 define 是数组,define =[‘process.env.NODE_ENV=”production”‘]
if (isArray) argv[name].forEach(fn);
// 执行 fn 的参数把 等号分割,执行
function(name, value) {
// name = process.env.NODE_ENV
// value = production

if (name === null) {
name = value;
value = true;
}
defineObject[name] = value;

}
// 函数执行结果就是
defineObject.process.env.NODE_ENV = production

// 最后返回的是 options,此后还有执行第三个参数出入了 defineObject 最终影响了 options 配置

我们接着往下读
// 如果不为数组,而且存在执行 fn
else if (isSet) fn(argv[name], -1);
我们追踪 output-pathinfo
ifBooleanArg(“output-pathinfo”, function() {
ensureObject(options, “output”);
options.output.pathinfo = true;
});

function ifBooleanArg(name, fn) {
ifArg(name, function(bool) {
if (bool) {
// 如果配置是 true 那么执行函数
fn();
}
});
}
// 函数执行结果就是
options.output.pathinfo = true;
再往下读
finalize && finalize();
对于 defin 字段春入了 finalize 函数,即第三个函数
function() {
// 从 webpack 引入 DefinePlugin 对象
const DefinePlugin = require(“webpack”).DefinePlugin;
// 调用 addPlugin
addPlugin(options, new DefinePlugin(defineObject));
}

function addPlugin(options, plugin) {
ensureArray(options, “plugins”);
options.plugins.unshift(plugin);
}
// 最终结果对 options 做了处理
其他同理???? 这个模块终于写完了
我们总结一下
这个模块做的就是从 webpack 配置文件读取配置赋值给 options,然后根据配置,对 options 做了附加的对象处理比如 options.output.pathinfo = true; 然后返回 options,国外小哥哥好能绕我们在 bin/cli 文件里打印一下最后的 config
{entry: ‘./src/index.js’,
output:
{filename: ‘index.js’,
path: ‘/Users/orion/Desktop/react-beauty-highcharts/dist’,
libraryTarget: ‘commonjs2’,
pathinfo: true },
module: {rules: [ [Object], [Object] ] },
devServer: {openPage: ‘./src/index.html’, open: true, hot: true},
externals: [[Function] ],
mode: ‘development’,
plugins:
[LoaderOptionsPlugin { options: [Object] },
LoaderOptionsPlugin {options: [Object] },
DefinePlugin {definitions: [Object] } ],
devtool: ‘eval-cheap-module-source-map’,
context: ‘/Users/orion/Desktop/react-beauty-highcharts’ }
鼓掌???? 给自己一朵小红花????

正文完
 0