1 | # Node File Trace
|
2 |
|
3 | [![CI Status](https://badgen.net/github/checks/vercel/node-file-trace?label=CI)](https://github.com/vercel/node-file-trace/actions?workflow=CI)
|
4 | [![Code Coverage](https://badgen.net/codecov/c/github/vercel/node-file-trace)](https://codecov.io/gh/vercel/node-file-trace)
|
5 |
|
6 | This package is used in [@vercel/node](https://npmjs.com/package/@vercel/node) and [@vercel/next](https://npmjs.com/package/@vercel/next) to determine exactly which files (including `node_modules`) are necessary for the application runtime.
|
7 |
|
8 | This is similar to [@zeit/ncc](https://npmjs.com/package/@zeit/ncc) except there is no bundling performed and therefore no reliance on webpack. This achieves the same tree-shaking benefits without moving any assets or binaries.
|
9 |
|
10 | ## Usage
|
11 |
|
12 | ### Installation
|
13 | ```bash
|
14 | npm i @zeit/node-file-trace
|
15 | ```
|
16 |
|
17 | ### Usage
|
18 |
|
19 | Provide the list of source files as input:
|
20 |
|
21 | ```js
|
22 | const nodeFileTrace = require('@zeit/node-file-trace');
|
23 | const files = ['./src/main.js', './src/second.js'];
|
24 | const { fileList } = await nodeFileTrace(files);
|
25 | ```
|
26 |
|
27 | The list of files will include all `node_modules` modules and assets that may be needed by the application code.
|
28 |
|
29 | ### Options
|
30 |
|
31 | #### Base
|
32 |
|
33 | The base path for the file list - all files will be provided as relative to this base.
|
34 |
|
35 | By default the `process.cwd()` is used:
|
36 |
|
37 | ```js
|
38 | const { fileList } = await nodeFileTrace(files, {
|
39 | base: process.cwd()
|
40 | }
|
41 | ```
|
42 |
|
43 | Any files/folders above the `base` are ignored in the listing and analysis.
|
44 |
|
45 | #### Process Cwd
|
46 |
|
47 | When applying analysis certain functions rely on the `process.cwd()` value, such as `path.resolve('./relative')` or even a direct `process.cwd()`
|
48 | invocation.
|
49 |
|
50 | Setting the `processCwd` option allows this analysis to be guided to the right path to ensure that assets are correctly detected.
|
51 |
|
52 | ```js
|
53 | const { fileList } = await nodeFileTrace(files, {
|
54 | processCwd: path.resolve(__dirname)
|
55 | }
|
56 | ```
|
57 |
|
58 | By default `processCwd` is the same as `base`.
|
59 |
|
60 | #### Exports
|
61 |
|
62 | By default tracing of the [Node.js "exports" field](https://nodejs.org/dist/latest-v14.x/docs/api/esm.html#esm_package_entry_points) is supported, with the `"node"`, `"require"`, `"import"` and `"default"` conditions traced as defined.
|
63 |
|
64 | Alternatively the explicit list of exports can be provided:
|
65 |
|
66 | ```js
|
67 | const { fileList } = await nodeFileTrace(files, {
|
68 | exports: ['node', 'production']
|
69 | });
|
70 | ```
|
71 |
|
72 | Only the `"node"` export should be explicitly included (if needed) when specifying the exact export condition list. The `"require"`, `"import"` and `"default"` conditions will always be traced as defined, no matter what custom conditions are set.
|
73 |
|
74 | #### Exports Only
|
75 |
|
76 | When tracing exports the `"main"` / index field will still be traced for Node.js versions without `"exports"` support.
|
77 |
|
78 | This can be disabled with the `exportsOnly` option:
|
79 |
|
80 | ```js
|
81 | const { fileList } = await nodeFileTrace(files, {
|
82 | exportsOnly: true
|
83 | });
|
84 | ```
|
85 |
|
86 | Any package with `"exports"` will then only have its exports traced, and the main will not be included at all. This can reduce the output size when targeting [Node.js 12.17.0)(https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V12.md#12.17.0) or newer.
|
87 |
|
88 | #### Paths
|
89 |
|
90 | > Status: Experimental. May change at any time.
|
91 |
|
92 | Custom resolution path definitions to use.
|
93 |
|
94 | ```js
|
95 | const { fileList } = await nodeFileTrace(files, {
|
96 | paths: {
|
97 | 'utils/': '/path/to/utils/'
|
98 | }
|
99 | });
|
100 | ```
|
101 |
|
102 | Trailing slashes map directories, exact paths map exact only.
|
103 |
|
104 | #### Hooks
|
105 |
|
106 | The following FS functions can be hooked by passing them as options:
|
107 |
|
108 | * `readFile(path): Promise<string>`
|
109 | * `stat(path): Promise<FS.Stats>`
|
110 | * `readlink(path): Promise<string>`
|
111 |
|
112 | #### TypeScript
|
113 |
|
114 | The internal resolution supports resolving `.ts` files in traces by default.
|
115 |
|
116 | By its nature of integrating into existing build systems, the TypeScript
|
117 | compiler is not included in this project - rather the TypeScript transform
|
118 | layer requires separate integration into the `readFile` hook.
|
119 |
|
120 | #### Analysis
|
121 |
|
122 | Analysis options allow customizing how much analysis should be performed to exactly work out the dependency list.
|
123 |
|
124 | By default as much analysis as possible is done to ensure no possibly needed files are left out of the trace.
|
125 |
|
126 | To disable all analysis, set `analysis: false`. Alternatively, individual analysis options can be customized via:
|
127 |
|
128 | ```js
|
129 | const { fileList } = await nodeFileTrace(files, {
|
130 | // default
|
131 | analysis: {
|
132 | // whether to glob any analysis like __dirname + '/dir/' or require('x/' + y)
|
133 | // that might output any file in a directory
|
134 | emitGlobs: true,
|
135 | // whether __filename and __dirname style
|
136 | // expressions should be analyzed as file references
|
137 | computeFileReferences: true,
|
138 | // evaluate known bindings to assist with glob and file reference analysis
|
139 | evaluatePureExpressions: true,
|
140 | }
|
141 | });
|
142 | ```
|
143 |
|
144 | #### Ignore
|
145 |
|
146 | Custom ignores can be provided to skip file inclusion (and consequently analysis of the file for references in turn as well).
|
147 |
|
148 | ```js
|
149 | const { fileList } = await nodeFileTrace(files, {
|
150 | ignore: ['./node_modules/pkg/file.js']
|
151 | });
|
152 | ```
|
153 |
|
154 | Ignore will also accept a function or globs.
|
155 |
|
156 | Note that the path provided to ignore is relative to `base`.
|
157 |
|
158 | #### Cache
|
159 |
|
160 | To persist the file cache between builds, pass an empty `cache` object:
|
161 |
|
162 | ```js
|
163 | const cache = Object.create(null);
|
164 | const { fileList } = await nodeFileTrace(['index.ts'], { cache });
|
165 | // later:
|
166 | {
|
167 | const { fileList } = await nodeFileTrace(['index.ts'], { cache });
|
168 | }
|
169 | ```
|
170 |
|
171 | Note that cache invalidations are not supported so the assumption is that the file system is not changed between runs.
|
172 |
|
173 | #### Reasons
|
174 |
|
175 | To get the underlying reasons for individual files being included, a `reasons` object is also provided by the output:
|
176 |
|
177 | ```js
|
178 | const { fileList, reasons } = await nodeFileTrace(files);
|
179 | ```
|
180 |
|
181 | The `reasons` output will then be an object of the following form:
|
182 |
|
183 | ```js
|
184 | {
|
185 | [file: string]: {
|
186 | type: 'dependency' | 'asset' | 'sharedlib',
|
187 | ignored: true | false,
|
188 | parents: string[]
|
189 | }
|
190 | }
|
191 | ```
|
192 |
|
193 | `reasons` also includes files that were ignored as `ignored: true`, with their `ignoreReason`.
|
194 |
|
195 | Every file is included because it is referenced by another file. The `parents` list will contain the list of all files that caused this file to be included.
|