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