webpack原理篇(五十一):webpack启动过程分析

Posted 凯小默

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了webpack原理篇(五十一):webpack启动过程分析相关的知识,希望对你有一定的参考价值。

说明

玩转 webpack 学习笔记

开始:从 webpack 命令行说起

通过 npm scripts 运行 webpack

  • 开发环境:npm run dev
  • 生产环境:npm run build

通过 webpack 直接运行

  • webpack entry.js bundle.js

查找 webpack 入口文件

在命令行运行以上命令后,npm会让命令行工具进入 node_modules\\.bin 目录查找是否存在 webpack.sh 或者 webpack.cmd 文件,如果存在,就执行,不存在,就抛出错误。

实际的入口文件是:node_modules\\webpack\\bin\\webpack.js

分析 webpack 的入口文件:webpack.js

源代码如下:

#!/usr/bin/env node

// @ts-ignore
// 1# 正常执行返回
process.exitCode = 0;

/**
 * @param string command process to run
 * @param string[] args commandline arguments
 * @returns Promise<void> promise
 */
 // 2# 运行某个命令
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();
			
		);
	);
;

/**
 * @param string packageName name of the package
 * @returns boolean is the package installed?
 */
// 3# 判断某个包是否安装
const isInstalled = packageName => 
	try 
		require.resolve(packageName);

		return true;
	 catch (err) 
		return false;
	
;

/**
 * @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[] */
// 4# webpack 可用 cli webpack-cli(功能丰富一点) 跟 webpack-command
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."
	
];
// 5# 判断两个是否都安装了
const installedClis = CLIs.filter(cli => cli.installed);
// 6# 根据安装的数量进行处理
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;
			);
	);
 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]
	));
 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;

启动后的结果

webpack 最终找到 webpack-cli (webpack-command) 这个 npm 包,并且执行 CLI。

以上是关于webpack原理篇(五十一):webpack启动过程分析的主要内容,如果未能解决你的问题,请参考以下文章

webpack原理篇(五十七):webpack流程:文件生成

webpack原理篇(五十四):Tapable是如何和webpack进行关联起来的?

webpack原理篇(五十二):webpack-cli源码阅读

webpack原理篇(五十六):webpack流程:模块构建和chunk生成阶段

webpack原理篇(五十八):实战开发一个简易的webpack

webpack原理篇(五十三):Tapable插件架构与Hooks设计