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:
npm link somehow in our top level package.json to do
something clever with a script hook after npm install has
finished. This seems impossible, because the
handlebars.js/package.json isn't going to be available until well
after npm install has finished.run_grunt:handlebars_build to recurse into src/handlebars.js and run
npm install in that directory. This seems more manual than I
would expect.run_grunt:handlebars_build that
installs dependencies from src/handlebars.js/package.json into the
project's top level node_modules directory, which might then be
picked up when running Gruntfile.js inside src/handlebars.js (this
may require that there is no node_modules directory in
src/handlebars.js - my knowledge of npm and node_modules is not
great).src/handlebars.js 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.Some advice would be appreciated on the best way forward.
NPM installs devDependencies within the package. json file. The 'npm install' command should add all the dependencies and devDependencies automatically during installation. If you need to add specific devDependencies to your project, you can use this command- 'npm install --save-dev'.
To add dependencies and devDependencies to a package. json file from the command line, you can install them in the root directory of your package using the --save-prod flag for dependencies (the default behavior of npm install ) or the --save-dev flag for devDependencies.
In order to add a Git submodule, use the “git submodule add” command and specify the URL of the Git remote repository to be included as a submodule. When adding a Git submodule, your submodule will be staged. As a consequence, you will need to commit your submodule by using the “git commit” command.
To ensure your submodule's dependencies are installed whenever you install your main repo's dependencies, you need to modify your main package.json.
Assuming your submodule is called 'common' and sits under src/common and its package.json is in src/common, then,
You should run npm install --save file:src/common and it will add the dependency to your package.json (or you can add by yourself, just append to your dependencies key the key:value of "common": "file:src/common",
Running npm install afterwards, will install all dependencies in the main module and submodule.
These steps will also help solve the issue in continuous deployment environments.
this might be of some help to someone stumbling over this question like i did during my research regarding a similar question:
My solution uses the grunt plugin grunt-run to run npm install in the submodule directory (installing its dependencies into its own node_modules directory: that would be src/handlebars.js/node_modules/ in your case).
In my case i needed to compile the TypeScript files using a grunt task inside the sub-module afterwards. I solved this using the plugin grunt-submodule.
The
>> Local Npm module "grunt-contrib-clean" not found. Is it installed?`
errors seem to appear if grunt is being executed in the wrong working directory - if you've managed to install the dependencies before.
Example:
Only the sub-module uses grunt-contrib-clean to clean its build artifacts. You've installed the sub-modules requirements (inside its directory) but you run grunt clean in the parents directory. The node module is not known in this place.
Hope that helps,
Markus
These tasks are of course run from within grunt, integrated into my build/development life-cycle.
References:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With