TypeError [ERR_INVALID_CALLBACK]报错:Angular Webpack规则添加脚本异常排查
解决Angular Webpack配置脚本的回调错误与执行不稳定问题
我来帮你解决这个问题,你的脚本遇到了两个核心问题:回调函数缺失触发了Node.js的API错误,以及异步与同步操作混用导致脚本有时仅部分执行。下面是详细的分析和修复方案:
错误原因分析
你看到的TypeError [ERR_INVALID_CALLBACK],是因为fs.close()方法必须传入一个回调函数作为参数,但你的代码里直接调用了fs.close(file)没有提供回调,违反了Node.js的fs模块API要求。
除此之外,脚本里同时使用了异步的fs.readFile和同步的fs.openSync,再加上fs.writeFile的使用方式错误(它通常接收文件路径而非文件描述符),这些混合操作会导致代码执行顺序不可控,这就是为什么脚本有时仅部分执行的原因。
修复后的脚本
我重构了脚本,改用同步文件操作(适合这类一次性脚本场景),同时优化了规则匹配和格式,避免出现格式错误:
const fs = require('fs'); const commonCliConfig = 'node_modules/@angular-devkit/build-angular/src/angular-cli-files/models/webpack-configs/common.js'; // 调整规则字符串格式,避免多余逗号导致语法错误 const pugRule = ` { test: /\\.pug$/, loader: ['raw-loader', 'pug-html-loader'] } `; try { // 同步读取配置文件,避免异步回调嵌套的执行顺序问题 const configText = fs.readFileSync(commonCliConfig, 'utf8'); // 用正则匹配关键内容,避免空格/换行差异导致的误判 const hasPugRule = configText.includes(/test: \/\\\.pug\$/.source); if (hasPugRule) { console.log('Pug规则已存在,无需重复添加'); process.exit(0); } // 精准定位rules数组的起始位置 const rulesStartMarker = 'rules: ['; const rulesStartIndex = configText.indexOf(rulesStartMarker) + rulesStartMarker.length; // 匹配原有缩进,保持配置文件格式一致 const indentMatch = configText.slice(0, rulesStartIndex).match(/(\s*)$/); const indent = indentMatch ? indentMatch[1] + ' ' : ' '; const formattedPugRule = indent + pugRule.trim(); // 拼接新的配置内容 const output = [ configText.slice(0, rulesStartIndex), formattedPugRule + ',', configText.slice(rulesStartIndex) ].join(''); // 同步写入文件,自动处理文件的打开与关闭 fs.writeFileSync(commonCliConfig, output, 'utf8'); console.log('Pug规则已成功添加到Webpack配置中'); } catch (err) { console.error('操作失败:', err.message); process.exit(1); }
关键修复点说明
- 改用同步文件操作:
fs.readFileSync和fs.writeFileSync适合一次性脚本,避免异步回调嵌套带来的执行顺序问题,彻底解决“有时仅部分执行”的情况。 - 移除手动文件描述符操作:同步写入会自动处理文件的打开和关闭,不需要手动调用
fs.openSync和fs.close,从根源上解决回调错误。 - 鲁棒的规则存在性检查:用正则匹配规则的关键部分,避免因为空格、换行等格式差异导致误判规则已存在。
- 保持配置格式一致:自动匹配原有缩进,确保添加的规则和配置文件的格式统一,避免Webpack解析报错。
更优的长期解决方案
直接修改node_modules里的文件是不推荐的,因为每次执行npm install或yarn install后,修改的内容都会被覆盖。更好的方式是使用Angular官方支持的自定义Webpack配置方案:
- 安装自定义Webpack构建器:
npm install @angular-builders/custom-webpack --save-dev
- 在项目根目录创建
webpack.custom.js文件:
module.exports = { module: { rules: [ { test: /\.pug$/, loader: ['raw-loader', 'pug-html-loader'] } ] } };
- 修改
angular.json中的构建配置:
将原来的@angular-devkit/build-angular:browser和@angular-devkit/build-angular:dev-server替换为自定义构建器,并指定配置文件路径:
"architect": { "build": { "builder": "@angular-builders/custom-webpack:browser", "options": { "customWebpackConfig": { "path": "./webpack.custom.js" }, // 保留原有其他配置项... } }, "serve": { "builder": "@angular-builders/custom-webpack:dev-server", // 保留原有其他配置项... } }
这种方式不需要修改node_modules里的文件,配置持久化且符合Angular的最佳实践。
内容的提问来源于stack exchange,提问作者ryuu
相关产品推荐
相关产品推荐

