webpack 执行文件
/bin/webpack
这个文件相对简单,其实就是判断一下webpack-cli
是否已经安装,没有安装就调用命令安装,要是已经安装则使用webpack-cli
1. 声明 webpack-cli
信息对象
const cli = {
name: "webpack-cli",
package: "webpack-cli",
binName: "webpack-cli",
installed: isInstalled("webpack-cli"),
url: "https://github.com/webpack/webpack-cli"
};
2. 判断 webpack-cli
是否有安装, 只要判断目录下node_module
是有webpack-cli
这个文件夹即可
const isInstalled = packageName => {
if (process.versions.pnp) {
return true;
}
const path = require("path");
const fs = require("graceful-fs");
let dir = __dirname;
do {
try {
if (fs.statSync(path.join(dir, "node_modules", packageName)).isDirectory()) {
return true;
}
} catch (_error) {
// Nothing
}
} while (dir !== (dir = path.dirname(dir)));
return false;
};
3. 判断 webpack-cli
是否有安装,如果有则调用runCli
, 没有则 执行runCommand
if (!cli.installed) {
const path = require("path");
const fs = require("graceful-fs");
const readLine = require("readline");
const notify =
"CLI for webpack must be installed.\n" + ` ${cli.name} (${cli.url})\n`;
console.error(notify);
/** 判断是使用yarn 还是node 还是其他的包管理工具 */
let packageManager;
if (fs.existsSync(path.resolve(process.cwd(), "yarn.lock"))) {
packageManager = "yarn";
} else if (fs.existsSync(path.resolve(process.cwd(), "pnpm-lock.yaml"))) {
packageManager = "pnpm";
} else {
packageManager = "npm";
}
const installOptions = [packageManager === "yarn" ? "add" : "install", "-D"];
console.error(
`We will use "${packageManager}" to install the CLI via "${packageManager} ${installOptions.join(
" "
)} ${cli.package}".`
);
const question = `Do you want to install 'webpack-cli' (yes/no): `;
const questionInterface = readLine.createInterface({
input: process.stdin,
output: process.stderr
});
process.exitCode = 1;
questionInterface.question(question, answer => {
questionInterface.close();
/** 命令行输入y 时,安装`webpack-cli`, 否则提示需要安装webpack-cli */
const normalizedAnswer = answer.toLowerCase().startsWith("y");
if (!normalizedAnswer) {
console.error(
"You need to install 'webpack-cli' to use webpack via CLI.\n" +
"You can also install the CLI manually."
);
return;
}
process.exitCode = 0;
runCommand(packageManager, installOptions.concat(cli.package))
.then(() => {
runCli(cli);
})
.catch(error => {
console.error(error);
process.exitCode = 1;
});
});
} else {
runCli(cli);
}
4. runCammand 执行命令
const runCommand = (command, args) => {
const cp = require("child_process");
return new Promise((resolve, reject) => {
const executedCommand = cp.spawn(command, args, {
stdio: "inherit",
shell: true
});
executedCommand.on("error", error => {
reject(error);
});
executedCommand.on("exit", code => {
if (code === 0) {
resolve();
} else {
reject();
}
});
});
};
5. 最后 runCli, 执行webpack-cli
const runCli = cli => {
const path = require("path");
/** webpack-cli, 这里require.resolve 是获取webpack-cli.package.json 的目录路径 */
const pkgPath = require.resolve(`${cli.package}/package.json`);
const pkg = require(pkgPath);
require(path.resolve(path.dirname(pkgPath), pkg.bin[cli.binName]));
};
webpack-cli
- 上面所说运行
webpack-cli
中的package.json
下的bin
, 即./bin/cli.js
./bin/cli.js
执行了runCli()
从../lib/bootstrap
中加载啊- 然后执行了
cli = new WebpackCli()
, 然后调用了cli.run()
new WebpackCli()
的构造函数中使用了 commandercli.run
调用了this.program.action
回调 调用await loadCommandByName(commandToRun, true);
,然后在调用了makeCommand
,makeCommand
调用了loadWebpack
, 并且getBuiltInOptions
创建了webpack
的option
也就是webpack.config.js
, 最后在makeCommand
调用了command.action
触发回调,调用了await this.runWebpack(options, isWatchCommandUsed);
。
总结一下流程this.program.action -> loadCommandByName -> makeCommad( loadWebpack, runWebpack )
- runWebpack 执行了 createCompiler 然后返回
this.webpack(option)
const webpack = require('webpack');
const config = require('./webpack.config');
compiler = webpack(config)
compiler.run((err) => {
console.log(err)
})
webpack 源码
主入口
// webpack.js
// webpack 函数简易结构
cosnt webpack = (option, callback) => {
/** 先忽略掉watch */
const create = () => {
let compiler = createCompiler(option);
return { compiler };
}
/** 判断有没有回调,最终都是返回compiler */
if (callback) {
const { compiler } = create()
compiler.run(() => {
callback()
})
return compiler
} else {
const { compiler } = create();
return compiler
}
return compiler
}
createCompiler
// lib/webpack.js
const createCompiler = rawOptions => {
/** 序列化一下option */
const options = getNormalizedWebpackOptions(rawOptions);
// 设置一下默认值比如context 设为当前运行目录
applyWebpackOptionsBaseDefaults(options);
/** compiler 最主要的函数在于run 函数 */
const compiler = new Compiler(options.context);
compiler.options = options;
// 加载plugins , 先加载我们配置文件下的plugin
if (Array.isArray(options.plugins)) {
for (const plugin of options.plugins) {
if (typeof plugin === "function") {
plugin.call(compiler, compiler);
} else {
plugin.apply(compiler);
}
}
}
// 给option默认值
applyWebpackOptionsDefaults(options);
/** 触发环境设置hooks */
compiler.hooks.environment.call();
compiler.hooks.afterEnvironment.call();
// 给config 的 key/value 转换成插件 plugin
new WebpackOptionsApply().process(options, compiler);
compiler.hooks.initialize.call();
return compiler;
};