如何在Angular项目中为ngcc配置自定义临时文件路径?
我们的场景是:项目所需的node_modules已提前安装并存储在指定位置,多个Angular项目实例分别运行在独立的Docker容器中,且所有容器挂载同一个node_modules目录。
日常运行一切正常,但执行测试命令npm run test(实际命令为jest --json --outputFile=./testOutput.json; node transformJestOutputToMochaStyle.js)时,因node_modules目录为只读权限,ngcc尝试创建.ngcc_lock_file等临时文件失败,报错信息如下:
gxjc5w@84d7538af516:~/project/application$ npm run test > angular-interview@0.0.0 test > jest --json --outputFile=./testOutput.json; node transformJestOutputToMochaStyle.js Determining test suites to run... ngcc-jest-processor: running ngcc Error: EROFS: read-only file system, open '/deps/node_modules/.ngcc_lock_file' at Object.openSync (node:fs:585:3) at Module.writeFileSync (node:fs:2157:35) at NodeJSFileSystem.writeFile (file:///deps/node_modules/@angular/compiler-cli/bundles/chunk-TOKOIIBI.js:357:9) at LockFileWithChildProcess.write (file:///deps/node_modules/@angular/compiler-cli/bundles/chunk-2LU77VSE.js:1730:13) at AsyncLocker.create (file:///deps/node_modules/@angular/compiler-cli/bundles/chunk-2LU77VSE.js:1682:30) at AsyncLocker.lock (file:///deps/node_modules/@angular/compiler-cli/bundles/chunk-2LU77VSE.js:1671:16) at ClusterExecutor.execute (file:///deps/node_modules/@angular/compiler-cli/bundles/chunk-2LU77VSE.js:1566:26) at mainNgcc (file:///deps/node_modules/@angular/compiler-cli/bundles/chunk-2LU77VSE.js:2113:19) at file:///deps/node_modules/@angular/compiler-cli/bundles/ngcc/main-ngcc.js:32:11 at file:///deps/node_modules/@angular/compiler-cli/bundles/ngcc/main-ngcc.js:42:3 Error: Jest: Got error running globalSetup - /deps/node_modules/jest-preset-angular/build/config/global-setup.js, reason: NGCC failed . at runNgccJestProcessor (/deps/node_modules/jest-preset-angular/build/utils/ngcc-jest-processor.js:42:19) at /deps/node_modules/jest-preset-angular/build/config/global-setup.js:10:56 at Generator.next (<anonymous>) at /deps/node_modules/tslib/tslib.js:118:75 at new Promise (<anonymous>) at Object.__awaiter (/deps/node_modules/tslib/tslib.js:114:16) at globalSetup (/deps/node_modules/jest-preset-angular/build/config/global-setup.js:6:35) at /deps/node_modules/@jest/core/build/runGlobalHook.js:125:19 at ScriptTransformer.requireAndTranspileModule (/deps/node_modules/@jest/transform/build/ScriptTransformer.js:905:24) at processTicksAndRejections (node:internal/process/task_queues:96:5) node:internal/fs/utils:344 throw err; ^ Error: ENOENT: no such file or directory, open './testOutput.json' at Object.openSync (node:fs:585:3) at Object.readFileSync (node:fs:453:35) at readTestOutput (/candidate_files/preview/gxjc5w/editablequestions-angular7-q1675409290800/application/transformJestOutputToMochaStyle.js:41:28) at Object.<anonymous> (/candidate_files/preview/gxjc5w/editablequestions-angular7-q1675409290800/application/transformJestOutputToMochaStyle.js:50:20) at Module._compile (node:internal/modules/cjs/loader:1103:14) at Object.Module._extensions..js (node:internal/modules/cjs/loader:1157:10) at Module.load (node:internal/modules/cjs/loader:981:32) at Function.Module._load (node:internal/modules/cjs/loader:822:12) at Function.executeUserEntryPoint [as runMain] (node:internal/run_main:77:12) at node:internal/main/run_main_module:17:47 { errno: -2, syscall: 'open', code: 'ENOENT', path: './testOutput.json' }
核心疑问:是否可以为ngcc指定自定义目录来存储.ngcc_lock_file等临时文件?
可以通过以下两种方式指定ngcc的临时文件存储目录:
1. 使用环境变量配置
ngcc支持通过NGCC_WORKSPACE_CACHE_PATH环境变量指定缓存目录,临时锁文件会存储在这个目录下。在执行测试命令前设置该变量即可:
NGCC_WORKSPACE_CACHE_PATH=/path/to/writable/directory npm run test
注意替换/path/to/writable/directory为容器内可读写的路径(比如项目目录下的.ngcc-cache文件夹),每个容器可以使用独立的缓存目录,避免冲突。
2. 修改jest-preset-angular的配置
如果使用jest-preset-angular,可以在jest配置文件(jest.config.js)中自定义ngcc的运行参数,指定--workspace-cache-path选项:
module.exports = { preset: 'jest-preset-angular', globalSetup: 'jest-preset-angular/global-setup', // 其他配置... globals: { 'ts-jest': { // 添加ngcc配置 ngcc: { workspaceCachePath: '/path/to/writable/directory' } } } };
同样,确保指定的目录在容器内是可读写的。
额外建议
因为多个容器共享只读的node_modules,建议在构建阶段提前运行ngcc处理所有依赖,这样运行时ngcc就不会再尝试生成临时文件和处理依赖。命令如下:
ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points
将该命令加入Docker镜像构建流程,后续容器运行测试时就不会触发ngcc的自动处理。
内容的提问来源于stack exchange,提问作者Pawan Singh

