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.
$ mkdir handlebarstest ; cd handlebarstest ; git init
$ mkdir src ; git add src
$ git submodule add https://github.com/wycats/handlebars.js.git src/handlebars.js
Our package.json doesn't mention handlebars.js, just grunt:
{
"name": "handlebarstest",
"version": "1.0.0",
"dependencies": {},
"devDependencies": {
"grunt": "~0.4.1",
"grunt-contrib-clean": "~0.4.1",
"grunt-run-grunt": "latest"
}
}
In this project's history, our install and build procedure has been:
$ 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.
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.
We do npm install and install grunt, and grunt init which fetches handlebars.js including its package.json. So the root of our problem is that the package.json is not available when the top level npm install is run.
Our Gruntfile.js knows how to call the Gruntfile.js in handlebars.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');
};
Running grunt fails because it recurses into handlebars.js but the module dependencies in its package.json have not been installed.
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.
Solutions might be:
- Use
npm linksomehow in our top levelpackage.jsonto do something clever with a script hook afternpm installhas finished. This seems impossible, because thehandlebars.js/package.jsonisn't going to be available until well afternpm installhas finished. - Put a manual step before
run_grunt:handlebars_buildto recurse intosrc/handlebars.jsand runnpm installin that directory. This seems more manual than I would expect. - Put a step before
run_grunt:handlebars_buildthat installs dependencies fromsrc/handlebars.js/package.jsoninto the project's top level node_modules directory, which might then be picked up when running Gruntfile.js insidesrc/handlebars.js(this may require that there is no node_modules directory insrc/handlebars.js- my knowledge of npm and node_modules is not great). - Give up on trying to build handlebars.js as part of our
project's build, remove the
src/handlebars.jssubmodule 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.
Some advice would be appreciated on the best way forward.
grunt buildneeds to run the Dojo build process, which collects files in the build directory (including handlebars and its AMD pieces) and prepares them as a 'dist' web app. Perhaps Bower is the right way forward for us, I will try it. Thank you for the tip.package.jsonfile. Sorry, I'm sure I'm missing something, just not sure what.