CLI 模块

文件位置

src/cli.js — 命令行接口的完整实现。

概述

CLI 是 ncc 的主要用户界面,通过 ncc <cmd> <opts> 调用。它负责参数解析、命令路由、输出文件写入和构建摘要渲染。

入口逻辑

// src/cli.js:48-63
if (require.main === module) {
  runCmd(process.argv.slice(2), process.stdout, process.stderr)
  .then((watching) => {
    if (!watching) process.exit();
  })
  .catch(e => {
    if (!e.silent) console.error(e.nccError ? e.message : e);
    process.exit(e.exitCode || 1);
  });
} else {
  module.exports = runCmd;
  api = true;
}

关键设计:CLI 支持两种模式:

  • 直接执行:作为命令行工具运行
  • API 模式:被 require() 时导出 runCmd 函数(用于测试)

api 标志影响子进程的 stdio 配置(API 模式用 pipe,直接模式用 inherit)。

命令结构

参数解析

使用 arg 库进行严格模式解析(permissive: false):

// src/cli.js:141-179 - 参数定义
{
  "--help": Boolean,        "-h": "--help",
  "--version": Boolean,     "-v": "--version",
  "--asset-builds": Boolean, "-a": "--asset-builds",
  "--debug": Boolean,       "-d": "--debug",
  "--external": [String],   "-e": "--external",
  "--out": String,          "-o": "--out",
  "--minify": Boolean,      "-m": "--minify",
  "--source-map": Boolean,  "-s": "--source-map",
  "--no-cache": Boolean,    "-C": "--no-cache",
  "--quiet": Boolean,       "-q": "--quiet",
  "--watch": Boolean,       "-w": "--watch",
  "--v8-cache": Boolean,
  "--transpile-only": Boolean, "-t": "--transpile-only",
  "--license": String,
  "--stats-out": String,
  "--target": String,
}

命令路由

命令功能特殊处理
build编译文件到输出目录默认输出到 dist/
run编译到临时目录并执行自动启用 source-map,完成后清理
cache clean清除构建缓存使用 rimraf
cache dir输出缓存目录路径
cache size显示缓存大小使用 get-folder-size
help显示帮助信息
version显示版本号

build 命令流程

编译阶段

// src/cli.js:256-276
const buildFile = eval("require.resolve")(resolve(args._[1] || "."));
const ncc = require("./index.js")(buildFile, { /* 选项 */ });

eval("require.resolve") 的使用是为了防止 ncc 在编译自身时尝试解析这个 require 调用。

输出阶段(handler 函数)

  1. 清理输出目录:删除现有的 .js.cjs 文件
  2. 写入主文件outDir/index.js(如有 shebang 则设置 0o777 权限)
  3. 写入 Source MapoutDir/index.js.map
  4. 写入资源文件:遍历 assets 对象,创建目录并写入
  5. 创建符号链接:遍历 symlinks 对象
  6. 渲染构建摘要:文件大小、构建时间
  7. 写入 stats:如有 --stats-out 选项

构建摘要

   1kB  dist/index.js
  12kB  dist/some-asset.js
  13kB  [1234ms] - ncc 0.38.1

renderSummary 函数(src/cli.js:65-121)按文件大小排序显示所有输出文件,主文件 index.js 插入到适当的排序位置。

run 命令流程

  1. 生成临时目录:基于输入路径 SHA-256 哈希在 os.tmpdir() 中创建
  2. 如目录已存在则清理
  3. 复用 build 逻辑(通过 fallthrough)编译代码
  4. 额外步骤:
    • 查找最近的 node_modules 并创建符号链接到临时目录
    • 使用 child_process.fork() 执行编译产物
    • 监听退出信号,退出时清理临时目录
// src/cli.js:331-364
ps = require("child_process").fork(`${outDir}/index${ext}`, {
  stdio: api ? 'pipe' : 'inherit'
});

Watch 模式

build 命令中使用 --watch

// src/cli.js:366-377
ncc.handler(handler);           // 注册编译完成回调
ncc.rebuild(() => {             // 注册重建开始回调
  if (ps) ps.kill();            // run+watch 时杀死旧进程
  startTime = Date.now();
  stdout.write('File change, rebuilding...\n');
});

注意:run 命令不支持 --watch(会报错退出)。

错误处理

ncc 使用自定义错误对象区分用户错误和内部错误:

// src/cli.js:123-128
function nccError(msg, exitCode = 1) {
  const err = new Error(msg);
  err.nccError = true;    // 标记为用户错误
  err.exitCode = exitCode;
  throw err;
}
  • nccError = true:只显示 message(用户错误,不需要堆栈)
  • silent = true:完全静默退出(子进程非零退出码)

环境变量

变量用途来源
process.noDeprecation抑制 Webpack 废弃警告src/cli.js:15 设置
__NCC_OPTS传递选项给 loader 和 TypeScript 解析src/index.js:94-97 设置
TYPESCRIPT_LOOKUP_PATH覆盖 TypeScript 查找路径用户设置