You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 12:40:22