likes
comments
collection
share

从 npm run build 学习 webpack(上)

作者站长头像
站长
· 阅读数 43

在一个 webpack 构建的项目中,npm run build作为前端常用的打包命令,可以在 package.jsonscripts 中看到是在执行 webpack 命令。

webpack 命令是如何被 npm 识别并执行的?

通过 npm init 命令初始化一个项目时,会生成一个 package.json 文件。在文件中提供一个 bin 字段,它是命令名到本地文件的映射。以便在 scripts 中以命令名调用,可以链接到可用的位置。

接着在 webpackpackage.json 中,可以看到 webpack 命令对应的可执行文件 bin/webpack.js

从 npm run build 学习 webpack(上)

打开文件可以看出定义了三个工具函数,还有一个常量 cli 对象。然后执行判断

从 npm run build 学习 webpack(上)

判断中最后都执行了 runCli 函数。只不过在检测到 cli 没安装时候,会做一些校验,提示安装等逻辑(先忽略)。最后都会执行 runCli 函数。

从 npm run build 学习 webpack(上)

runCli 函数通过 require.resolve 获取 webpack-cli 依赖的 package.json 文件绝对路径。然后导入整个 package.json 文件,赋值给 pkg。然后判断是否是 ESModule,是就使用 import 导入,否则使用 require。pkg.bin 就是 webpack-clipackage.jsonbin 字段。然后通过 cli.binName 也就是 webpack-cli,来获取 webpack-cli 的可执行文件。****

const cli = {
	name: "webpack-cli",
	package: "webpack-cli",
	binName: "webpack-cli",
	installed: isInstalled("webpack-cli"),
	url: "https://github.com/webpack/webpack-cli"
};

const runCli = cli => {
	const path = require("path");
	const pkgPath = require.resolve(`${cli.package}/package.json`);
	const pkg = require(pkgPath);

	if (pkg.type === "module" || /.mjs/i.test(pkg.bin[cli.binName])) {
		// eslint-disable-next-line n/no-unsupported-features/es-syntax
		import(path.resolve(path.dirname(pkgPath), pkg.bin[cli.binName])).catch(
			error => {
				console.error(error);
				process.exitCode = 1;
			}
		);
	} else {
		require(path.resolve(path.dirname(pkgPath), pkg.bin[cli.binName]));
	}
};

webpack-clipackage.json 中看到,可执行文件为 cli.js

从 npm run build 学习 webpack(上)

cli.js 中,运行命令时没有设置自定义字段 WEBPACK_CLI_SKIP_IMPORT_LOCAL 就是 undefined,然后 importLocal 如果加载本地 cli 会返回 false,最后就执行了 runCli

const importLocal = require("import-local");
const runCLI = require("../lib/bootstrap");

if (!process.env.WEBPACK_CLI_SKIP_IMPORT_LOCAL) {
  // Prefer the local installation of `webpack-cli`
  if (importLocal(__filename)) {
    return;
  }
}

process.title = "webpack";

runCLI(process.argv);

../lib/bootstraprunCLI 函数,实例化 WebapckCLI,执行 cli.run 方法

Object.defineProperty(exports, "__esModule", { value: true });
// eslint-disable-next-line @typescript-eslint/no-var-requires
const WebpackCLI = require("./webpack-cli");
const runCLI = async (args) => {
    // Create a new instance of the CLI object
    const cli = new WebpackCLI();
    try {
        await cli.run(args);
    }
    catch (error) {
        cli.logger.error(error);
        process.exit(2);
    }
};
module.exports = runCLI;

实例化时

  1. createColors 根据中断是否支持彩色,返回一个对象,包含生成各种颜色的 ANSI 码。
  2. getLogger 返回一个对象,包含 error、warn、info、success等日志函数,日志前缀增加 [webpack-cli]
  3. this.program = program; 这里将commander的实例赋值给this.program commander是一个轻量级的,表达式丰富的命令行框架,它可以帮助你快速构建命令行应用。
  4. this.program.name("webpack"); :这个方法设置了CLI的名称为webpack。在帮助信息中,这个名称会被显示出来,告诉用户这个CLI的名称。
  5. this.program.configureOutput({...}); :这个方法用于配置命令行输出的行为。它接受一个对象作为参数,这个对象可以有writeOutwriteErroutputError等属性,用于自定义标准输出、错误输出和错误处理。
    • writeErr: this.logger.error, :这里指定了当有错误需要写入标准错误输出时,使用this.logger.error方法。this.loggerwebpack-cli自定义的日志记录器,它会对错误信息进行格式化或添加前缀等处理。
    • outputError: (str, write) => write( Error: ${this.capitalizeFirstLetter(str.replace(/^error:/, "").trim())} ), :这个函数用于自定义错误信息的输出格式。它首先使用正则表达式 /^error:/ 来移除错误字符串前面的error:前缀(如果有的话),然后调用this.capitalizeFirstLetter方法将错误信息的第一个字母大写,最后在前面加上Error: 前缀,并通过write函数输出。这个write函数实际上是commander内部处理输出的方法。
constructor() {
    this.colors = this.createColors();
    this.logger = this.getLogger();
    // Initialize program
    this.program = program;
    this.program.name("webpack");
    this.program.configureOutput({
        writeErr: this.logger.error,
        outputError: (str, write) => write(`Error: ${this.capitalizeFirstLetter(str.replace(/^error:/, "").trim())}`),
    });
}

接下来就是最重要的 run 方法:整体上就是定义一些 操作命令 常数,然后对 cli 做基础操作配置。this.program.action方法就是对命令的回调处理。

从 npm run build 学习 webpack(上)

展开this.program.action,当只执行 webpack 时,没有其他参数执行默认 build 命令,最终执行 loadCommanByName 函数

从 npm run build 学习 webpack(上)

loadCommanByName 函数判断是 build 命令,执行 makeCommand 传入 build 操作配置,以及回调函数。

从 npm run build 学习 webpack(上)

makeCommand 函数。注册 build 命令,检测并安装所需依赖 webpack,然后执行第二个参数,构建 webpack 所需 option。最后使用第三个参数作为 action,处理 build 命令。

从 npm run build 学习 webpack(上)

  • 第二个参数:加载 webpack,构建 webpack 所需的 option
this.webpack = await this.loadWebpack();
return this.getBuiltInOptions();
  • 第三个参数:通过 runWebpack 执行 webpack,处理 build
if (entries.length > 0) {
    options.entry = [...entries, ...(options.entry || [])];
}
await this.runWebpack(options, isWatchCommandUsed);

下面是整个过程的流程图

从 npm run build 学习 webpack(上)

转载自:https://juejin.cn/post/7360856203840143411
评论
请登录