webpack命令-从零开始webpack源码思考1

1、下载需要分析的项目

分别是核心、cli、本地开发服务器

2、看webpack-4.43.0

package.json

"main": "lib/webpack.js",
  "web": "lib/webpack.web.js",
  "bin": "./bin/webpack.js",

 

命令行调用,执行bin对应js文件;nodejs代码调用执行main对应文件。

3、./bin/webpack.js

#!/usr/bin/env node

// @ts-ignore
process.exitCode = 0;

/**
 * @param {string} command process to run
 * @param {string[]} args commandline arguments
 * @returns {Promise<void>} promise
 */
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();
			}
		});
	});
};

process.exitCode,设置退出状态,0成功,1错误。

runCommand,使用child_process开启子进程,执行命令,返回promise。inherit:继承父进程相关的stdio。

 

/**
 * @param {string} packageName name of the package
 * @returns {boolean} is the package installed?
 */
const isInstalled = packageName => {
	try {
		require.resolve(packageName);

		return true;
	} catch (err) {
		return false;
	}
};

require.resolve执行成功,说明包存在,出现错误,则捕获说明不存在。

 

/**
 * @typedef {Object} CliOption
 * @property {string} name display name
 * @property {string} package npm package name
 * @property {string} binName name of the executable file
 * @property {string} alias shortcut for choice
 * @property {boolean} installed currently installed?
 * @property {boolean} recommended is recommended
 * @property {string} url homepage
 * @property {string} description description
 */

/** @type {CliOption[]} */
const CLIs = [
	{
		name: "webpack-cli",
		package: "webpack-cli",
		binName: "webpack-cli",
		alias: "cli",
		installed: isInstalled("webpack-cli"),
		recommended: true,
		url: "https://github.com/webpack/webpack-cli",
		description: "The original webpack full-featured CLI."
	},
	{
		name: "webpack-command",
		package: "webpack-command",
		binName: "webpack-command",
		alias: "command",
		installed: isInstalled("webpack-command"),
		recommended: false,
		url: "https://github.com/webpack-contrib/webpack-command",
		description: "A lightweight, opinionated webpack CLI."
	}
];

两个包的信息,webpack-cli和webpack-command,下面会用到。

const installedClis = CLIs.filter(cli => cli.installed);

过滤这两包,获取安装过的包组成的数组。

if (installedClis.length === 0) {
	const path = require("path");
	const fs = require("fs");
	const readLine = require("readline");

	let notify =
		"One CLI for webpack must be installed. These are recommended choices, delivered as separate packages:";

	for (const item of CLIs) {
		if (item.recommended) {
			notify += `\n - ${item.name} (${item.url})\n   ${item.description}`;
		}
	}

	console.error(notify);

	const isYarn = fs.existsSync(path.resolve(process.cwd(), "yarn.lock"));

	const packageManager = isYarn ? "yarn" : "npm";
	const installOptions = [isYarn ? "add" : "install", "-D"];

	console.error(
		`We will use "${packageManager}" to install the CLI via "${packageManager} ${installOptions.join(
			" "
		)}".`
	);

	const question = `Do you want to install 'webpack-cli' (yes/no): `;

	const questionInterface = readLine.createInterface({
		input: process.stdin,
		output: process.stderr
	});
	questionInterface.question(question, answer => {
		questionInterface.close();

		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."
			);
			process.exitCode = 1;

			return;
		}

		const packageName = "webpack-cli";

		console.log(
			`Installing '${packageName}' (running '${packageManager} ${installOptions.join(
				" "
			)} ${packageName}')...`
		);

		runCommand(packageManager, installOptions.concat(packageName))
			.then(() => {
				require(packageName); //eslint-disable-line
			})
			.catch(error => {
				console.error(error);
				process.exitCode = 1;
			});
	});
} 

如果两个包,一个也没安装,就提示必须安装,这是推荐的包。

使用 fs.existsSync,在当前工作目录,判断是否存在yarn.lock文件。

存在,设置yarn add;否则npm install -D 。

提示询问是否同意安装webpack-cli? yes/no

使用readLine.createInterface,监控用户输入。

questionInterface.question监控到输入,就关闭监控输入,转小写判断输入是否y开头,不是,就提示你得安装,你可以人工手动安装。

并设置错误process.exitCode = 1;返回。

如果输入的开头是y,就提示安装webpack-cli,并且执行安装命令,注意runCommand参数,第二个参数是数组,表示命令行命令的参数,child_process子进程的.spawn函数,也支持这个数组形式参数。

前面runCommand返回promise,子进程执行退出时,如果code为0,表示成功执行命令,也就是成功安装,执行resolve。

然后就到了then,为了确保成功安装了webpack-cli,就require(packageName)下试下。 如果异常,也是catch,提示错误,设置process.exitCode = 1。 子进程如果失败,非0,会走reject();这时也会到catch逻辑。 promise的错误传递体现了。

else if (installedClis.length === 1) {
	const path = require("path");
	const pkgPath = require.resolve(`${installedClis[0].package}/package.json`);
	// eslint-disable-next-line node/no-missing-require
	const pkg = require(pkgPath);
	// eslint-disable-next-line node/no-missing-require
	require(path.resolve(
		path.dirname(pkgPath),
		pkg.bin[installedClis[0].binName]
	));
} 

俩个如果有一个安装了,我们这时并不知道安装了哪儿个,不过无所谓,我们就取这个安装过的包的package.json文件路径,然后引用它。

path.dirname(pkgPath),找到文件路径的目录路径。

找到package.json内bin对象,进而根据安装过的cli的binName,找到对应bin对应的js路径。

然后引用它。

这里引用它,其实就是执行它,相当于执行命令行命令。

 

 else {
	console.warn(
		`You have installed ${installedClis
			.map(item => item.name)
			.join(
				" and "
			)} together. To work with the "webpack" command you need only one CLI package, please remove one of them or use them directly via their binary.`
	);

	// @ts-ignore
	process.exitCode = 1;
}

如果两个都安装了,提示只需要一个,卸载一个吧,设置退出码=1。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值