安装 Git 子模块的依赖项:npm install

18

我有一个包含Handlebars.js子模块的Git项目。这里提供一个基本的示例 - 完整的项目更加复杂,包括与Dojo任务和部署步骤相关的内容,但这些内容在此并不相关。

$ mkdir handlebarstest ; cd handlebarstest ; git init
$ mkdir src ; git add src
$ git submodule add https://github.com/wycats/handlebars.js.git src/handlebars.js

我们的 package.json 没有提到 handlebars.js,只有grunt:

{
  "name": "handlebarstest",
  "version": "1.0.0",
  "dependencies": {},
  "devDependencies": {
    "grunt": "~0.4.1",
    "grunt-contrib-clean": "~0.4.1",
    "grunt-run-grunt": "latest"
  }
}
在这个项目的历史中,我们的安装和构建过程是:
$ npm install   # install dependencies in package.json
$ grunt init    # run the 'init' task which initialises git submodules
$ grunt build   # run the 'build' task which compiles dojo etc.
在克隆项目后(例如在构建服务器上),需要初始化git子模块,我们通过grunt来控制这一过程,所以必须先安装grunt,然后运行任务进行子模块的初始化,包括麻烦的 handlebars.js。
我们执行 "npm install" 安装grunt,然后运行 "grunt init",其中会获取handlebars.js,并包括它的package.json。因此,问题的根源是在顶层 "npm install" 运行时无法使用 package.json。
我们的Gruntfile.js知道如何调用handlebars.js中的Gruntfile.js:
/*jshint node:true */
module.exports = function (grunt) {
    /*jshint camelcase: false */
    var path = require('path');

    grunt.initConfig({
        clean: [ 'dist' ],
        run_grunt: {
            options: {

            },
            handlebars_build: {
                options: {
                    log: true
                },
                src: [ 'src/handlebars.js/Gruntfile.js' ],
                task: 'build'
            },
            handlebars_amd: {
                options: {
                    log: true
                },
                src: [ 'src/handlebars.js/Gruntfile.js' ],
                task: 'amd'
            }
        }
    });

    // var handlebarsLink= grunt.registerTask('handlebarsLink', function () {
    //  var done = this.async(),
    //      child = grunt.util.spawn({
    //          cmd: 'npm',
    //          args: [ 'link', 'src/handlebars.js' ]
    //      }, function (error) {
    //          if (error) {
    //              grunt.warn(error);
    //              done(false);
    //              return;
    //          }
    //          done();
    //      });

    //  child.stdout.on('data', function (data) {
    //      grunt.log.write(data);
    //  });
    //  child.stderr.on('data', function (data) {
    //      grunt.log.error(data);
    //  });
    // });
    var submodules;
    if (grunt.file.exists(__dirname, '.git')) {
        submodules = grunt.registerTask('submodules', function () {
            var done = this.async(),
                child = grunt.util.spawn({
                    cmd: 'git',
                    args: [ 'submodule', 'update', '--init', '--recursive' ]
                }, function (error) {
                    if (error) {
                        grunt.warn(error);
                        done(false);
                        return;
                    }
                    done();
                });

            child.stdout.on('data', function (data) {
                grunt.log.write(data);
            });
            child.stderr.on('data', function (data) {
                grunt.log.error(data);
            });
        });
    }
    var init = submodules ? [ 'submodules'/*, 'handlebarsLink'*/ ] : [];
    grunt.registerTask('init', init);
    grunt.registerTask('default', 'build');
    grunt.registerTask('build', init.concat([ 'clean', 'run_grunt:handlebars_build', 'run_grunt:handlebars_amd' ]));

    grunt.loadTasks(path.join(__dirname, 'grunt'));
    grunt.loadTasks(path.join(__dirname, 'src', 'intern', 'tasks'));
    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-run-grunt');
};

运行Grunt失败,因为它递归进入了handlebars.js,但是它的package.json中的模块依赖项没有被安装。

Running "run_grunt:handlebars_build" (run_grunt) task
--> starting  "src/handlebars.js/Gruntfile.js"
--> reporting "src/handlebars.js/Gruntfile.js"
  |  >> Local Npm module "grunt-contrib-clean" not found. Is it installed?
  |  >> Local Npm module "grunt-contrib-concat" not found. Is it installed?
  |  >> Local Npm module "grunt-contrib-connect" not found. Is it installed?
  |  >> Local Npm module "grunt-contrib-copy" not found. Is it installed?
  |  >> Local Npm module "grunt-contrib-requirejs" not found. Is it installed?
  |  >> Local Npm module "grunt-contrib-jshint" not found. Is it installed?
  |  >> Local Npm module "grunt-contrib-uglify" not found. Is it installed?
  |  >> Local Npm module "grunt-contrib-watch" not found. Is it installed?
  |  >> Local Npm module "grunt-saucelabs" not found. Is it installed?
  |  >> Local Npm module "es6-module-packager" not found. Is it installed?
  |  Loading "metrics.js" tasks...ERROR
  |  >> Error: Cannot find module 'underscore'
  |  Loading "publish.js" tasks...ERROR
  |  >> Error: Cannot find module 'underscore'
  |  Loading "version.js" tasks...ERROR
  |  >> Error: Cannot find module 'async'
  |  Warning: Task "clean" not found. Use --force to continue.
  |  
  |  Aborted due to warnings.
  |  
--> failed "src/handlebars.js/Gruntfile.js" (304ms)
--> failed handlebars_build @ "src/handlebars.js/Gruntfile.js"

Warning: 1 gruntfile failed and completed 0 (308ms)
 Use --force to continue.

可能的解决方案:

  1. 在我们的顶层package.json中以某种方式使用npm link,在npm install完成后执行脚本钩子。但是这似乎是不可能的,因为handlebars.js/package.json直到npm install完成之后才会可用。
  2. run_grunt:handlebars_build之前添加手动步骤,进入src/handlebars.js并在该目录中运行npm install。但是这比我期望的更加繁琐。
  3. run_grunt:handlebars_build之前添加一步骤,将src/handlebars.js/package.json中的依赖项安装到项目的顶级node_modules目录中,这样在运行src/handlebars.js内部的Gruntfile.js时可能会被捕捉到(这可能需要在src/handlebars.js中没有node_modules目录-我的npm和node_modules知识不是很好)。
  4. 放弃尝试将handlebars.js构建为我们项目的构建的一部分,从项目中删除src/handlebars.js子模块,并在其位置添加已构建的文件作为普通文件。这会很遗憾,因为您将通过git子模块方法失去跟踪项目版本的优势。

在最佳前进方向上提供一些建议将不胜感激。


2
我猜我不明白你为什么首先要“构建”手柄?你说要“智能地跟踪项目的版本”,但我不确定你的意思是什么。你是如何跟踪版本的?为什么要这样做?除此之外,你是只在客户端使用Handlebars,还是在预编译步骤中也使用了它?如果只在客户端使用,或许将该依赖项切换到像Bower这样的东西会更好? - Jordan Kasper
@jakerella 通过“跟踪版本”,我指的是我们可以在特定时间内使用项目的特定版本,没有其他原因...基本上就是package.json中的“依赖项”行所给出的内容,但在这种情况下是通过git子模块修订进行管理的。这是一个预编译步骤...我们的“grunt build”需要运行Dojo构建过程,该过程会收集构建目录中的文件(包括handlebars及其AMD部分),并将它们准备为“dist”Web应用程序。也许Bower对我们来说是正确的方法,我会尝试一下。谢谢你的提示。 - Neek
1
嗯...好的,如果它是一个构建依赖项,那么我想我不明白为什么你不能使用npm和package.json文件来包含它。抱歉,我确定我错过了什么,只是不确定是什么。 - Jordan Kasper
2个回答

44
为了确保在安装主仓库的依赖时也安装子模块的依赖,您需要修改主要的 package.json 文件。假设您的子模块名为“common”,位于 src/common 目录下,它的 package.json 文件也在 src/common 目录中,那么您应该运行命令 npm install --save file:src/common,它会将依赖项添加到您的 package.json 文件中(或者您可以手动添加,只需将 "common": "file:src/common" 添加到 "dependencies" 键中)。在此之后运行 npm install 命令,将安装主模块和子模块中的所有依赖项。这些步骤还将有助于解决持续部署环境中的问题。

8
这个答案更有用,应该被接受为正确答案。 - nxmohamad
这个答案真的很有帮助! - undefined

5
这可能对一些像我一样在研究类似问题时遇到困难的人有所帮助:

我的解决方案使用grunt插件grunt-run在子模块目录中运行npm install(将其依赖项安装到自己的node_modules目录中:在您的情况下,这将是src/handlebars.js/node_modules/)。

在我的情况下,我需要使用grunt任务在子模块内编译TypeScript文件。我使用grunt-submodule插件解决了这个问题。

>> Local Npm module "grunt-contrib-clean" not found. Is it installed?`

如果在错误的工作目录中执行grunt,那么可能会出现错误 - 如果您之前已成功安装了依赖项。
例如:只有子模块使用grunt-contrib-clean来清除其构建产物。您已在子目录中安装了子模块的要求,但是您在父目录中运行grunt clean。此处不知道节点模块。
当然,这些任务都是从grunt中运行的,并集成到我的构建/开发生命周期中。
参考链接:

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接