vercel/nft
GitHub: vercel/nft
一个通过静态分析精确追踪 Node.js 应用运行时所需全部文件及依赖的命令行工具与库。
Stars: 1658 | Forks: 179
# Node File Trace
用于精确确定应用程序 runtime 所需的所有文件(包括 `node_modules`)。
这类似于 [@vercel/ncc](https://npmjs.com/package/@vercel/ncc),不同之处在于它不执行打包,因此不依赖 webpack。这在不移动任何静态资源或二进制文件的情况下,实现了相同的 tree-shaking 优势。
## 用法
### 安装
```
npm i @vercel/nft
```
### 用法
提供源文件列表作为输入:
```
const { nodeFileTrace } = require('@vercel/nft');
const files = ['./src/main.js', './src/second.js'];
const { fileList } = await nodeFileTrace(files);
```
文件列表将包含应用程序代码可能需要的所有 `node_modules` 模块和静态资源。
### 选项
#### 基础
文件列表的 base 路径 - 所有文件都将作为相对于此 base 的路径提供。
默认使用 `process.cwd()`:
```
const { fileList } = await nodeFileTrace(files, {
base: process.cwd(),
});
```
在列表和分析中,位于 `base` 之上的任何文件/文件夹都会被忽略。
#### Process Cwd
在应用分析时,某些函数依赖于 `process.cwd()` 的值,例如 `path.resolve('./relative')`,甚至直接调用 `process.cwd()`。
设置 `processCwd` 选项可以引导此分析使用正确的路径,以确保正确检测到静态资源。
```
const { fileList } = await nodeFileTrace(files, {
processCwd: path.resolve(__dirname),
});
```
默认情况下,`processCwd` 与 `base` 相同。
#### Exports & Imports
默认情况下,支持对 [Node.js "exports" 和 "imports" 字段](https://nodejs.org/dist/latest-v14.x/docs/api/esm.html#esm_package_entry_points)的追踪,并按照定义追踪 `"node"`、`"require"`、`"import"` 和 `"default"` 条件。
或者,也可以提供明确的条件列表:
```
const { fileList } = await nodeFileTrace(files, {
conditions: ['node', 'production'],
});
```
在指定确切的导出条件列表时,只有 `"node"` 导出应被显式包含(如果需要)。无论设置了什么自定义条件,`"require"`、`"import"` 和 `"default"` 条件都将始终按定义被追踪。
在 Node.js 22 或更高版本中进行追踪时,`"module-sync"` 条件默认是启用的。设置 `moduleSyncCatchall` 可以同时追踪 `module-sync` 分支和正常的 runtime/fallback 分支:
```
const { fileList } = await nodeFileTrace(files, {
moduleSyncCatchall: true,
});
```
#### Exports Only
在追踪导出时,对于不支持 `"exports"` 的 Node.js 版本,仍会追踪 `"main"` / index 字段。
这可以通过 `exportsOnly` 选项来禁用:
```
const { fileList } = await nodeFileTrace(files, {
exportsOnly: true,
});
```
这样,任何带有 `"exports"` 的 package 都将只追踪其导出内容,而完全不会包含 main。当目标环境为 [Node.js 12.17.0](https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V12.md#12.17.0) 或更高版本时,这可以减小输出体积。
#### Paths
要使用的自定义解析路径定义。
```
const { fileList } = await nodeFileTrace(files, {
paths: {
'utils/': '/path/to/utils/',
},
});
```
尾部斜杠映射目录,确切路径仅映射确切路径。
#### Hooks
可以通过将以下 FS 函数作为选项传入来进行挂钩(hook):
- `readFile(path): Promise`
- `stat(path): Promise`
- `readlink(path): Promise`
- `resolve(id: string, parent: string): Promise`
##### 高级解析
当提供自定义 resolve hook 时,您有责任根据 `id` 输入返回一个或多个已解析文件的绝对路径。然而,在某些情况下,您可能只想增强或覆盖特定情况下的解析行为。您可以通过导入 `nft` 底层的 resolver 来使用它。内置的 `resolve` 函数需要从 hook 中转发一些额外的参数:
- `resolve(id: string, parent: string, job: Job, isCjs: boolean): Promise`
以下示例展示了一个 id 被解析为自定义路径,而所有其他路径都由内置 resolver 进行解析:
```
const { nodeFileTrace, resolve } = require('@vercel/nft');
const files = ['./src/main.js', './src/second.js'];
const { fileList } = await nodeFileTrace(files, {
resolve: async (id, parent, job, isCjs) => {
if (id === './src/main.js') {
return '/path/to/some/resolved/main/file.js';
} else {
return resolve(id, parent, job, isCjs);
}
},
});
```
#### TypeScript
内部解析默认支持在追踪中解析 `.ts` 文件。
由于其集成了现有构建系统的特性,TypeScript 编译器并未包含在本项目中 - 而是 TypeScript 转换层需要单独集成到 `readFile` hook 中。
#### File IO Concurrency
在一些大型项目中,文件追踪逻辑可能会同时处理许多文件。在这种情况下,如果您不限制并发文件 IO 的数量,很可能会发生 OOM(内存不足)问题。
我们对 fs 操作默认使用 1024 的并发数,以在性能和内存使用之间取得平衡。您可以将此值增加到更大的数字以获得更快的速度,但要注意如果并发量过高会引发内存问题。
```
const { fileList } = await nodeFileTrace(files, {
fileIOConcurrency: 2048,
});
```
#### 分析
Analysis 选项允许自定义应执行多少分析,以精确计算出依赖列表。
默认情况下,会尽可能多地进行详细分析,以确保追踪结果中不会遗漏任何可能需要的文件。
要禁用所有分析,请设置 `analysis: false`。或者,可以通过以下方式自定义各个分析选项:
```
const { fileList } = await nodeFileTrace(files, {
// default
analysis: {
// whether to glob any analysis like __dirname + '/dir/' or require('x/' + y)
// that might output any file in a directory
emitGlobs: true,
// whether __filename and __dirname style
// expressions should be analyzed as file references
computeFileReferences: true,
// evaluate known bindings to assist with glob and file reference analysis
evaluatePureExpressions: true,
},
});
```
#### Ignore
可以提供自定义忽略规则来跳过文件包含(因此也会跳过对该文件中引用的分析)。
```
const { fileList } = await nodeFileTrace(files, {
ignore: ['./node_modules/pkg/file.js'],
});
```
Ignore 也接受函数或 glob。
请注意,提供给 ignore 的路径是相对于 `base` 的。
#### Cache
要在构建之间持久化文件缓存,请传入一个空的 `cache` 对象:
```
const cache = Object.create(null);
const { fileList } = await nodeFileTrace(['index.ts'], { cache });
// later:
{
const { fileList } = await nodeFileTrace(['index.ts'], { cache });
}
```
请注意,不支持缓存失效,因此前提是文件系统在多次运行之间不会发生改变。
#### Depth
`depth` 选项控制从入口点文件开始追踪 module graph 的层级数。这对于限制追踪范围或进行调试很有用:
```
const { fileList } = await nodeFileTrace(files, {
depth: 2,
});
```
如果 `depth: 0`,则追踪结果仅包含入口点文件本身,不包含任何额外的模块。如果 `depth: 1`,则会追踪入口点及其直接 import 的模块,依此类推。
请注意,`depth` 选项不能小于 0。设置为负值会导致错误。
#### Reasons
为了获取单个文件被包含的底层原因,输出结果中还提供了一个 `reasons` 对象:
```
const { fileList, reasons } = await nodeFileTrace(files);
```
`reasons` 输出将是一个如下形式的对象:
```
{
[file: string]: {
type: 'dependency' | 'asset' | 'sharedlib',
ignored: true | false,
parents: string[]
}
}
```
`reasons` 还包括被忽略的文件(标记为 `ignored: true`),以及它们的 `ignoreReason`。
每个文件都是因为被另一个文件引用而被包含的。`parents` 列表将包含导致该文件被包含的所有文件的列表。
标签:GNU通用公共许可证, MITM代理, Node.js, SOC Prime, 代码分析, 依赖追踪, 凭证管理, 开发工具, 数据可视化, 自定义脚本