17

is it possible to configure grunt in a way that you have the required modules on a central point?

I have following project structure

-Project
-- subproject
-- subproject
-- subproject

I build the project via grunt with all subprojects, and I can build each subproject for itself too. Currently I have a Gruntfile.js, package.json & folder node_modules (~50mb) with all required modules in each subproject and on the root level.

So is it possible to have the node_modules folder only on one level, for e.g. on the root level and the subprojects refer to the node_modules on root level?

-Project
--subproject
--subproject
--subproject
--node_modules

Is there a way to reference the node_module folder via package.json or anything else?

Edit:

Gruntfile.js (subproject level)

/*global module:false */
/*jshint node:true */

module.exports = function(grunt) {

    "use strict";

    // ================================================================================
    // project configuration
    // ================================================================================
    grunt.initConfig({
        pkg : grunt.file.readJSON('package.json'),
        jshint: {
            globals : {
                d3:true,
                Modernizr:true,
                tempo:true
            },
            options: grunt.file.readJSON('.jshintrc')
        },
        csslint: {
            subproject: {
                src: 'css/**/*.css'
            }
        },
        htmllint : {
            subproject: {
                src: 'html/**/*.html'
            }
        },
        clean : [ 'output' ],
        less : {
            options: {
                paths: ['./']
            },
            src: {
                expand: true,
                cwd:    'css/',
                src:    ['**/*.less'],
                dest:   'css/',
                ext:    '.css'
            }
        },
        copy: {
            subproject: {
                files: [
                    {src: ['img/**', 'js/**', 'folderX/**','!**/*.less'], dest: 'output/subproject/'}
                ]
            }
        }
    });

    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-html');
    grunt.loadNpmTasks('grunt-css');
    grunt.loadNpmTasks('grunt-contrib-less');
    grunt.loadNpmTasks('grunt-contrib-copy');

    // ================================================================================
    // default task
    // ================================================================================
    grunt.registerTask('default', ['clean', 'less', 'csslint', 'htmllint', 'copy']);
};

package.json (subproejct level)

{
    "description": "subproject", 
    "title": "Lorem Ipsum", 
    "devDependencies": {
        "grunt-contrib-watch": "~0.2.0", 
        "grunt-contrib-jshint": "~0.1.1", 
        "grunt-contrib-less": "~0.5.0", 
        "grunt-contrib-uglify": "~0.1.1", 
        "grunt-contrib-copy": "~0.4.0", 
        "grunt-contrib-qunit": "~0.1.1", 
        "grunt-css": "~0.5.4", 
        "grunt-contrib-clean": "~0.4.0", 
        "grunt-html": "~0.3.3", 
        "grunt-contrib-concat": "~0.1.3"
    }
}

BR, mybecks

mybecks
  • 2,443
  • 8
  • 31
  • 43
  • What issues do you have with the default approach? Why do you need them to be at the root level? By default node.js will load matching modules in the root node_modules directory of it doesn't find them further down the hierarchy. – Floby Mar 05 '13 at 15:06

4 Answers4

6

This works out of the box. npm looks for node_modules in the current directory, and all its parent directories, then looks in the global location.

So you could even do this:

-Project
--subproject1
---node_modules
--subproject2
--subproject3
--node_modules

subproject1 will have access to all npms inside Project/subproject1/node_modules and Project/node_modules, while subproject2 and subproject3 will only find those inside Project/node_modules

Update

There is a very little documented feature called grunt collections. It requires a bit of a setup, but you won't need a copy of all your grunt plugins in each subproject.

Here's the file layout

-Project
--subproject1
---node_modules
----grunt-collection
-----package.json
--subproject2
...
--subproject3
...
--node_modules
---grunt
---grunt-contrib-concat
---grunt-contrib-jshint
---grunt-contrib-qunit
---grunt-contrib-watch
---grunt-html
---grunt-contrib-clean
---grunt-contrib-copy
---grunt-contrib-less
---grunt-contrib-uglify
---grunt-css
--package.json

Project/package.json

{
    "description": "subproject", 
    "version": "0.0.0",
    "name": "Lorem",
    "title": "Lorem Ipsum", 
    "devDependencies": {
        "grunt": "*",
        "grunt-contrib-watch": "~0.2.0", 
        "grunt-contrib-jshint": "~0.1.1", 
        "grunt-contrib-less": "~0.5.0", 
        "grunt-contrib-uglify": "~0.1.1", 
        "grunt-contrib-copy": "~0.4.0", 
        "grunt-contrib-qunit": "~0.1.1", 
        "grunt-css": "~0.5.4", 
        "grunt-contrib-clean": "~0.4.0", 
        "grunt-html": "~0.3.3", 
        "grunt-contrib-concat": "~0.1.3"
    }
}

Project/subproject1/package.json

{
    "description": "subproject", 
    "version": "0.0.0",
    "name": "Lorem",
    "title": "Lorem Ipsum", 
    "devDependencies": {
    }
}

Project/subproject1/Gruntfile.js excerpt (you only need the grunt-collection task).

grunt.loadNpmTasks('grunt-collection');
// grunt.loadNpmTasks('grunt-contrib-jshint');
// grunt.loadNpmTasks('grunt-html');
// grunt.loadNpmTasks('grunt-css');
// grunt.loadNpmTasks('grunt-contrib-less');
// grunt.loadNpmTasks('grunt-contrib-copy');

Project/subproject1/node_modules/grunt-collection/package.json

{
    "description": "subproject", 
    "version": "0.0.0",
    "name": "Lorem",
    "title": "Lorem Ipsum", 
    "dependencies": {
      "grunt-contrib-watch": "~0.2.0", 
      "grunt-contrib-jshint": "~0.1.1", 
      "grunt-contrib-less": "~0.5.0", 
      "grunt-contrib-uglify": "~0.1.1", 
      "grunt-contrib-copy": "~0.4.0", 
      "grunt-contrib-qunit": "~0.1.1", 
      "grunt-css": "~0.5.4", 
      "grunt-contrib-clean": "~0.4.0", 
      "grunt-html": "~0.3.3", 
      "grunt-contrib-concat": "~0.1.3"
    },
    "keywords": ["gruntcollection"]
}

The key is to create in each of your subproject, a small module with just a package.json which includes the keyword gruntcollection and includes the dependencies your Grunfile uses.

Grunt will then load these using the same strategy require uses, which means they can be found in the node_modules of your parent project.

Caveat: the way grunt collection works by using the dependency tag of package.json, this means you can not install it with npm install, but you should be able to store it source control.

Community
  • 1
  • 1
Pascal Belloncle
  • 11,184
  • 3
  • 56
  • 56
  • thx for your answer. I tried it, but it doesn't work. I get the following error msg:`>> Local Npm module "grunt-contrib-clean" not found. Is it installed?`I have a `Gruntfile.js` & `package.json` in every subproject. This msg I got when trying to run a task from `subproject/Gruntfile.js`. BR mybecks – mybecks Mar 06 '13 at 08:48
  • can you include more of your setup (grunfile, package.json) in your question? `require` works, but perhaps having a package.json in the subproject confuses grunt or require? – Pascal Belloncle Mar 06 '13 at 08:59
  • I added the Gruntfile.js of the subproject and the package.json? How should I use `require`, is it possible to embed a Gruntfile of a subproject to the one in the root level? – mybecks Mar 06 '13 at 10:09
  • did not work for me at all. I get a "TypeError: Object.keys called on non-object" Error. Could you help me? – Jörn Berkefeld Mar 12 '13 at 10:31
  • might be easier to create a new question with more details about the setup and a more complete error with stack trace. – Pascal Belloncle Mar 12 '13 at 18:37
  • Here you have working example of proposed sollution: https://github.com/marcin-wosinek/grunt-collection-example – Marcin Wosinek Aug 12 '13 at 13:45
1

I created a npm module load-grunt-parent-tasks to fix the issue. It was inspired by the answer that Pascal Belloncle gave and uses a gruntcollection hack.

All you need to do is require the module, pass it grunt and a config object and it will do the rest.

module.exports = function(grunt) {

  require('load-grunt-parent-tasks')(grunt, {
    config: 'package.json',
    pattern: 'grunt-*',
    scope: 'dependencies',
    module: 'grunt-collection'
  });

};

You can filter which grunt tasks you would like to load based on the globbing pattern you pass to pattern.

You can check out the module on Npm: https://www.npmjs.org/package/load-grunt-parent-tasks

Community
  • 1
  • 1
psyrendust
  • 1,186
  • 9
  • 13
1

Another solution, found in https://github.com/gruntjs/grunt/issues/696 module.exports = function (grunt) { grunt.file.expand('../node_modules/grunt-*/tasks').forEach(grunt.loadTasks); }

geting
  • 141
  • 1
  • 6
0

Following is a simple workaround. Change Gruntfile.js. module.exports = function (grunt) { var cwd = process.cwd(); process.chdir(cwd+'/../'); require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks); process.chdir(cwd); }

geting
  • 141
  • 1
  • 6