title: npm install dependencies of a git submodule
body: <p>I've got a git project with handlebars.js as a submodule.  I will provide here a bare-bones example - the full project is more complicated with dojo tasks and deployment steps that aren't relevant here.</p>
  
  <pre><code>$ mkdir handlebarstest ; cd handlebarstest ; git init
  $ mkdir src ; git add src
  $ git submodule add https://github.com/wycats/handlebars.js.git src/handlebars.js
  </code></pre>
  
  <p>Our <code>package.json</code> doesn't mention handlebars.js, just grunt:</p>
  
  <pre><code>{
    "name": "handlebarstest",
    "version": "1.0.0",
    "dependencies": {},
    "devDependencies": {
      "grunt": "~0.4.1",
      "grunt-contrib-clean": "~0.4.1",
      "grunt-run-grunt": "latest"
    }
  }
  </code></pre>
  
  <p>In this project's history, our install and build procedure has been:</p>
  
  <pre><code>$ 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.
  </code></pre>
  
  <p>Having done a fresh clone of our project, say on our build server, git submodules need to be initialised, and we control that from grunt, so we must install grunt first, then run a task to init submodules, including the troublesome handlebars.js.</p>
  
  <p>We do <code>npm install</code> and install grunt, and <code>grunt init</code> which fetches handlebars.js including its <code>package.json</code>.  So the root of our problem is that the <code>package.json</code> is not available when the top level <code>npm install</code> is run.</p>
  
  <p>Our Gruntfile.js knows how to call the Gruntfile.js in handlebars.js:</p>
  
  <pre><code>/*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');
  };
  </code></pre>
  
  <p>Running grunt fails because it recurses into handlebars.js but the module dependencies in its package.json have not been installed.</p>
  
  <pre><code>Running "run_grunt:handlebars_build" (run_grunt) task
  --&gt; starting  "src/handlebars.js/Gruntfile.js"
  --&gt; reporting "src/handlebars.js/Gruntfile.js"
    |  &gt;&gt; Local Npm module "grunt-contrib-clean" not found. Is it installed?
    |  &gt;&gt; Local Npm module "grunt-contrib-concat" not found. Is it installed?
    |  &gt;&gt; Local Npm module "grunt-contrib-connect" not found. Is it installed?
    |  &gt;&gt; Local Npm module "grunt-contrib-copy" not found. Is it installed?
    |  &gt;&gt; Local Npm module "grunt-contrib-requirejs" not found. Is it installed?
    |  &gt;&gt; Local Npm module "grunt-contrib-jshint" not found. Is it installed?
    |  &gt;&gt; Local Npm module "grunt-contrib-uglify" not found. Is it installed?
    |  &gt;&gt; Local Npm module "grunt-contrib-watch" not found. Is it installed?
    |  &gt;&gt; Local Npm module "grunt-saucelabs" not found. Is it installed?
    |  &gt;&gt; Local Npm module "es6-module-packager" not found. Is it installed?
    |  Loading "metrics.js" tasks...ERROR
    |  &gt;&gt; Error: Cannot find module 'underscore'
    |  Loading "publish.js" tasks...ERROR
    |  &gt;&gt; Error: Cannot find module 'underscore'
    |  Loading "version.js" tasks...ERROR
    |  &gt;&gt; Error: Cannot find module 'async'
    |  Warning: Task "clean" not found. Use --force to continue.
    |  
    |  Aborted due to warnings.
    |  
  --&gt; failed "src/handlebars.js/Gruntfile.js" (304ms)
  --&gt; failed handlebars_build @ "src/handlebars.js/Gruntfile.js"
  
  Warning: 1 gruntfile failed and completed 0 (308ms)
   Use --force to continue.
  </code></pre>
  
  <p>Solutions might be:</p>
  
  <ol>
  <li>Use <code>npm link</code> somehow in our top level <code>package.json</code> to do
  something clever with a script hook after <code>npm install</code> has
  finished.  This seems impossible, because the
  <code>handlebars.js/package.json</code> isn't going to be available until well
  after <code>npm install</code> has finished.</li>
  <li>Put a manual step before
  <code>run_grunt:handlebars_build</code> to recurse into <code>src/handlebars.js</code> and run
  <code>npm install</code> in that directory.  This seems more manual than I
  would expect.</li>
  <li>Put a step before <code>run_grunt:handlebars_build</code> that
  installs dependencies from <code>src/handlebars.js/package.json</code> into the
  project's top level node_modules directory, which might then be
  picked up when running Gruntfile.js inside <code>src/handlebars.js</code> (this
  may require that there is no node_modules directory in
  <code>src/handlebars.js</code> - my knowledge of npm and node_modules is not
  great).</li>
  <li>Give up on trying to build handlebars.js as part of our
  project's build, remove the <code>src/handlebars.js</code> submodule from the
  project, and just add built files as plain files in its place.  This
  would be a shame because you lose the benefits of tracking a
  project's versions intelligently via the git submodule approach.</li>
  </ol>
  
  <p>Some advice would be appreciated on the best way forward.</p>