Windows环境下NodeJs+Gulp编译项目报错求助
解决Windows下Angular+Gulp构建时MODULE_NOT_FOUND错误(找不到build模块)
问题背景
原本在Linux环境下可通过mvn clean install正常构建的Java后端+Angular前端项目,迁移到Windows环境后执行相同命令报错,提示无法找到模块D:\git\java\flextotal\src\main\webapp\build。已尝试重装npm依赖、修改Gulp配置,但问题未解决。
报错信息
[10:52:34] Starting '<anonymous>'... internal/modules/cjs/loader.js:883 throw err; ^ Error: Cannot find module 'D:\git\java\flextotal\src\main\webapp\build' at Function.Module._resolveFilename (internal/modules/cjs/loader.js:880:15) at Function.Module._load (internal/modules/cjs/loader.js:725:27) at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12) at internal/main/run_main_module.js:17:47 { code: 'MODULE_NOT_FOUND', requireStack: [] } [10:52:34] '<anonymous>' errored after 45 ms [10:52:34] Error: 1 at formatError (D:\git\java\flextotal\src\main\webapp\node_modules\gulp\node_modules\gulp-cli\lib\versioned\^4.0.0\format-error.js:21:10) at Gulp.<anonymous> (D:\git\java\flextotal\src\main\webapp\node_modules\gulp\node_modules\gulp-cli\lib\versioned\^4.0.0\log\events.js:33:15) at Gulp.emit (events.js:327:22) at Gulp.EventEmitter.emit (domain.js:467:12) at Object.error (D:\git\java\flextotal\src\main\webapp\node_modules\undertaker\lib\helpers\createExtensions.js:61:10) at handler (D:\git\java\flextotal\src\main\webapp\node_modules\now-and-later\lib\mapSeries.js:47:14) at f (D:\git\java\flextotal\src\main\webapp\node_modules\once\once.js:25:25) at f (D:\git\java\flextotal\src\main\webapp\node_modules\once\once.js:25:25) at tryCatch (D:\git\java\flextotal\src\main\webapp\node_modules\async-done\index.js:24:15) at done (D:\git\java\flextotal\src\main\webapp\node_modules\async-done\index.js:40:12) [10:52:34] 'cliBuildProd' errored after 83 ms [10:52:34] 'copyReleaseBundles' errored after 89 ms [10:52:34] 'buildRelease' errored after 90 ms [10:52:34] '<anonymous>' errored after 91 ms [10:52:34] 'default' errored after 92 ms npm ERR! code ELIFECYCLE npm ERR! errno 1 npm ERR! flextotal@1.0.0 build:release: `gulp --release` npm ERR! Exit status 1 npm ERR! npm ERR! Failed at the flextotal@1.0.0 build:release script. npm ERR! This is probably not a problem with npm. There is likely additional logging output above. at org.codehaus.plexus.classworlds.launcher.Launcher.main (Launcher.java:347)
当前Gulp配置文件
process.setMaxListeners(Number.POSITIVE_INFINITY); const cp = require('child_process'); const gulp = require('gulp'); const del = require('del'); const browserSync = require('browser-sync'); const plugins = require('gulp-load-plugins')(); const tslint = require("tslint"); const gulpMultiProcess = require('gulp-multi-process'); const fs = require('fs'); const debug = process.argv.indexOf("--development") > -1; const useAot = !debug; // constante usada para testes sem AOT console.log("debug: " + debug); const ngBuildArgs = ['build', '--deploy-url', 'dist/']; const ngBuildArgsProd = [...ngBuildArgs, '--prod', '--extract-licenses', 'false']; // o cli não está gerando os paths corretos sem usar extract-css const ngBuildArgsDebug = [...ngBuildArgs]; const ngBuildArgsWatch = [...ngBuildArgsDebug, '--watch', '--no-delete-output-path', '--progress', 'false']; if (debug) { var localConfig = JSON.parse(fs.readFileSync('./local-config.json')); if (!localConfig.deployPath) { throw new Error("deployPath não definido no local-config.json"); } if (!localConfig.applicationUrl) { throw new Error("applicationUrl não definido no local-config.json"); } } // workaround pra remover reflect do build aot // https://github.com/angular/angular-cli/issues/6325 gulp.task('processPolyfills', gulp.series(function() { return gulp.src("app/polyfills." + (debug ? "development" : "production") + ".ts") .pipe(plugins.rename("polyfills.ts")) .pipe(gulp.dest("app/")); })); gulp.task('clean', (done) => { del.sync(['./app.jsp', 'dist/**/*', 'dist-aot']); done(); }); function copyAppJsp() { const html = fs.readFileSync('./dist-aot/index.html', 'utf-8'); fs.writeFileSync('app.jsp', html); if (debug) { fs.writeFileSync(localConfig.deployPath + 'app.jsp', html); } } gulp.task('compileDebug', gulp.series(['processPolyfills'], function(cb) { const ng = cp.spawn(process.execPath, ngBuildArgsDebug, { stdio: 'inherit' }); ng.on('close', code => cb(code)); })); gulp.task('copyDebugBundles', gulp.series(['compileDebug'], function() { copyAppJsp() return gulp.src(['dist-aot/**/*']) .pipe(gulp.dest('dist')); })); gulp.task('startNgWatch', gulp.series(['processPolyfills'], function(done) { // criamos o diretório antes pois o watch não funciona com diretórios // inexistentes (que serão criados no ng build, e não tem como verificar quando) fs.mkdirSync('dist-aot'); cp.spawn(process.execPath, ngBuildArgsWatch, { stdio: 'inherit' }); done(); })); gulp.task('multiProcessBundle', gulp.series(function(cb) { // por padrão o node executa tudo na mesma thread, // este plugin usa child_process pra torná-lo multi processo return gulpMultiProcess(['lint'], cb); })); gulp.task('watch', gulp.series(['clean', 'startNgWatch', 'multiProcessBundle'], function() { let firstFile = true; // plugin utilizado porque o watch padrão do gulp não detecta criação de arquivos return plugins.watch('dist-aot/**/*', { readDelay: 500 }) .pipe(gulp.dest('dist')) .pipe(gulp.dest(localConfig.deployPath + 'dist')) .pipe(plugins.tap(function(file) { if (file.path.endsWith("/index.html")) { copyAppJsp(); } if (firstFile) { firstFile = false; browserSync.init({ proxy: localConfig.applicationUrl, browser: localConfig.browser, ghostMode: false, reloadDebounce: 500 }); } else { browserSync.reload(file); } })); })); gulp.task('lint', gulp.series(function() { var program = tslint.Linter.createProgram("./tsconfig.json"); return gulp.src('app/**/*.ts') .pipe(plugins.tslint({ program: program })) .pipe(plugins.tslint.report({ allowWarnings: true })); })) gulp.task('cliBuildProd', gulp.series(['processPolyfills'], function(cb) { const ng = cp.spawn(process.execPath, ngBuildArgsProd, { stdio: 'inherit' }); ng.on('close', code => cb(code)); })); gulp.task('copyReleaseBundles', gulp.series(['cliBuildProd'], function() { copyAppJsp() return gulp.src(['dist-aot/**/*', '!dist-aot/index.html']) .pipe(gulp.dest('dist')); })); gulp.task('_test', gulp.series(gulp.parallel(['clean', 'copyDebugBundles', 'multiProcessBundle']), function(cb) { const ng = cp.spawn(process.execPath, ['test', '--single-run'], { stdio: 'inherit' }); ng.on('close', code => cb(code)); })); gulp.task('buildDebug', gulp.series(gulp.parallel(['clean', 'copyDebugBundles', 'multiProcessBundle']), function() { return del(['dist-aot']); })); gulp.task('buildRelease', gulp.series(gulp.parallel(['clean', 'copyReleaseBundles', 'multiProcessBundle']), function() { return del(['dist-aot']); })); gulp.task('test', gulp.series('_test', function() { return del(['dist-aot']); })); function runTaskIf(condition, task) { task = gulp.series(task) // make sure we have a function that takes callback as first argument return function(cb) { if (condition()) { task(cb) } else { cb() } } } isDebug = function() { return debug; } isRelease = function() { return !debug; } gulp.task('default', gulp.parallel([ runTaskIf(isDebug, 'buildDebug'), runTaskIf(isRelease, 'buildRelease') ]));
解决方案
1. 修复Angular CLI调用逻辑(核心问题)
报错根源:代码中用process.execPath(即node.exe)直接执行build参数,导致Node尝试加载名为build的模块,而非调用Angular CLI的ng build命令。
方法一:使用cross-spawn处理跨平台命令
- 安装跨平台命令执行依赖:
npm install cross-spawn --save-dev
- 替换Gulp配置中的
child_process引用:
// 替换原有的const cp = require('child_process'); const cp = require('cross-spawn');
- 所有spawn调用改为直接使用
'ng'作为命令:
// 例如cliBuildProd任务中: const ng = cp.spawn('ng', ngBuildArgsProd, { stdio: 'inherit' });
cross-spawn会自动适配Windows下的ng.cmd和Linux下的ng可执行文件。
方法二:手动适配平台命令(无需新增依赖)
在需要调用Angular CLI的任务中添加平台判断:
// 以cliBuildProd任务为例 const ngCommand = process.platform === 'win32' ? 'ng.cmd' : 'ng'; const ng = cp.spawn(ngCommand, ngBuildArgsProd, { stdio: 'inherit', shell: true });
添加shell: true确保Windows能正确识别.cmd命令。
2. 确认Angular CLI安装状态
- 检查
package.json是否包含@angular/cli开发依赖,若缺失则安装:
npm install @angular/cli --save-dev
- 验证
node_modules/.bin目录下存在ng.cmd(Windows)或ng(Linux)文件。
3. 验证Angular CLI可独立执行
在项目前端根目录(src/main/webapp)下直接执行构建命令,排除CLI本身问题:
ng build --prod --deploy-url dist/ --extract-licenses false
4. 统一路径分隔符(可选)
Windows使用\、Linux使用/作为路径分隔符,建议用path模块生成路径避免硬编码问题:
const path = require('path'); // 替换copyAppJsp中的路径读取逻辑 const html = fs.readFileSync(path.join(__dirname, 'dist-aot', 'index.html'), 'utf-8');
内容的提问来源于stack exchange,提问作者Allan Campos
相关产品推荐
相关产品推荐

