Problem
I'm currently working on a project where we have a parent web application (happens to be an AngularJS application) and multiple child Bower modules (containing Javascript, SASS, images, etc.) that are included in the parent using Bower.
For example the parent bower.json looks like this:
{
"name": "parent-app",
"version": "1.0.0",
"dependencies": {
"child-1-module": "1.0.0",
"child-2-module": "1.0.0"
}
}
When performing 'bower install' on the parent the child modules will get installed to:
bower_components/child-1-module
bower_components/child-2-module
We then use 'bower link' for each of the child modules.
Then 'bower link child-1-module' and 'bower link child-2-module' on the parent to create local soft links such as:
bower_components/child-1-module -> /some/where/else/child-1-module
bower_components/child-2-module -> /some/where/else/child-2-module
This allows us to make changes locally to the individual child modules and see the results in the parent app.
We then also use Grunt with grunt-contrib-watch in the parent to watch the parent app for changes and then to perform other Grunt tasks, or to perform a 'livereload' to refresh the browser being used to view the app.
A cut down Gruntfile.js example which watches .js files and then performs a 'jshint' task and a 'livereload' is below:
grunt.initConfig({
// Watches files for changes and runs tasks based on the changed files
watch: {
js: {
files: [
'scripts/{,*/}*.js',
],
tasks: ['newer:jshint:all'],
options: {
livereload: true
}
}
}
}
This works well for watching the parent app for changes, but we would also like to know when files in the child modules change. There are currently a number of solutions I have thought of/investigated with mixed results.
Potential Solution 1: Add bower_components to grunt watch in parent
So I could just modify the Gruntfile to additionally also watch .js files in the bower_components folder, like so:
grunt.initConfig({
// Watches files for changes and runs tasks based on the changed files
watch: {
js: {
files: [
'scripts/**/*.js',
'bower_components/**/*.js'
],
tasks: ['newer:jshint:all'],
options: {
livereload: true
}
}
}
}
However there could be many child modules (containing many .js files), so the performance suffers dramatically and often does not even run due to 'EMFILE: Too many opened files' issues (see here).
Additionally child modules are added dynamically, so we can't specify up front in the Gruntfile only specific ones, for example:
'bower_components/child-1-module/**/*.js'
Potential Solution 2: Add grunt watch in child modules and in parent
We could instead add a Gruntfile.js in each child module that contains a watch to watch their own files.
When a file changes in the child module we can update a specific 'livereload' file, then in the parent we can then only watch for those specific files.
The example 'Gruntfile.js' in child-module-1
grunt.initConfig({
watch: {
js: {
files: ['scripts/**/*.js'],
tasks: ['livereloadEvent:js', 'newer:jshint:all']
}
}
}
grunt.registerTask('livereloadEvent', function() {
// create a 'livereload/livereload.{arg}' file for the event specified
grunt.file.mkdir('livereload');
for(var i = 0; i < this.args.length; i++) {
// contents of the file is the current timestamp
grunt.file.write('livereload/livereload.'+ this.args[i], new Date());
}
});
Then in the parent we can add the following folder to our watch:
'bower_components/**/livereload/livereload.js'
This works OK. The parent now doesn't have to watch too many files, it is now down to the child to perform its own watching and basically notify the parent.
The drawback is that every child has to be aware of this and implement this in the same way.
Other Potential Solutions...
How else have others handled this? Is there an accepted and widely used pattern for handling this?