javascriptnode.jsgruntjsfrontendgrunt-contrib-watch

nodejs - run grunt task programmatically


i'm using grunt tasks to watch and concatenate some js files.

my first configuration worked fine:

grunt.initConfig({
    watch: {
      js: {
        files: ['web/**/*.js'], 
        tasks: ['concat:JS'] 
      }
    },
    concat: { 
      JS: {
        files: {
          "index.js": [
            "test-1.js",
            "test-2.js",
          ],
          "about_us.js": [
            "test-3.js",
            "test-4.js",
          ],
          "store.js": [
            "test-5.js",
            "test-6.js",
          ]
        }
      }
    }
  });

my problem is that with this configuration whenever i change a file it concatenates everything.

i would be able to concatenate only a specific file (based on which file changed).

e.g. if i change test-1.js i would like concatenate only the index.js file (..so the task to run should be 'concat:JS:index.js' )

so i tryed adding a watch event

 grunt.event.on('watch', function(action, filepath, target) {
        var taskName = "concat:JS"; //here i calculate dinamically the specific task name to be run
        grunt.task.run(taskName);
    });

but nothing happens.. any idea? thanks


Solution

  • New answer post edit to question

    Using the grunt.event.on('watch', ...) listener is the wrong tool for this kind of requirement. Instead you should utilize multiple Targets for both the the watch and concat tasks as shown in the Gruntfile.js excerpt below.

    Gruntfile.js

    grunt.initConfig({
      watch: {
        'JS-index': {
          files: ['web/js/test-1.js', 'web/js/test-2.js'], 
          tasks: ['concat:JS-index']
        },
        'JS-about-us': {
          files: ['web/js/test-3.js', 'web/js/test-4.js'], 
          tasks: ['concat:JS-about-us']
        },
        'JS-store': {
          files: ['web/js/test-5.js', 'web/js/test-6.js'], 
          tasks: ['concat:JS-store']
        }
      },
      concat: { 
        'JS-index': {
          files: {
            'dist/js/index.js': [
              "web/js/test-1.js",
              "web/js/test-2.js",
            ]
          }
        },
        'JS-about-us': {
          files: {
            "dist/js/about_us.js": [
              "web/js/test-3.js",
              "web/js/test-4.js",
            ]
          }
        },
        'JS-store': {
          files: {
            'dist/js/store.js': [
              "web/js/test-5.js",
              "web/js/test-6.js",
            ]
          }
        }
      }
    });
    

    Notes

    Given the configuration shown above, the following will happen after running grunt watch via your CLI:

    1. Changing either test-1.js or test-2.js will concatenate both files to dist/js/index.js.

    2. Changing either test-3.js or test-4.js will concatenate both files to dist/js/about_us.js.

    3. Changing either test-5.js or test-6.js will concatenate both files to dist/js/store.js.

    Your paths will need to be configured as necessary


    Original answer before edit to question

    how do i execute a task with grunt?

    Using grunt.task.run is the correct way to programmatically run a task.

    However, see this comment in the grunt-contrib-watch github repo. Excerpts from it read:

    "Don't use grunt.event.on('watch') to run your tasks."

    and...

    "The watch event is not intended for running tasks."

    The following solution assumes your requirement is to concatenate some .js files when one of them is changed/edited. In which case you need to utilize the tasks property of the watch task to define which task to run (I.e. 'concat:JS')


    Gruntfile

    module.exports = function(grunt) {
    
      grunt.loadNpmTasks('grunt-contrib-watch');
      grunt.loadNpmTasks('grunt-contrib-concat');
    
      grunt.initConfig({
        watch: {
          js: {
            files: ['path/to/js/**/*.js'], // <-- 1. Glob pattern to .js files
            tasks: ['concat:JS'] // <-- 2. Task to run when .js file changes.
          }
        },
        concat: {  // <-- 3. Configure your `concat` task as necessary.
          JS: {
            src: [
              'path/to/js/foo.js',
              'path/to/js/bar.js',
              'path/to/js/quux.js'
            ],
            dest: 'path/to/output/combined.js'
          }
        }
      });
    
      grunt.registerTask('default', ['watch']);
    
      grunt.event.on('watch', function(action, filepath, target) {
        console.log("yep, this code is being executed");
        // 4. <-- Do not run task from 'on' listener event.
      });
    }
    

    Notes

    1. In your watch task define the glob pattern to the source .js files to watch for changes.
    2. Specify the task to run when a .js file changes; I.e. 'concat:JS'
    3. Configure your concat task as necessary.
    4. Remove grunt.task.run('concat:JS'); from the grunt.event.on('watch', ...) listener.
    5. Using the gist above and running grunt via your cli creates a new combined.js file when any change is made to a .js file stored in the path/to/js/ directory. (You'll need to configure the paths as per your requirements)
    6. Whilst the grunt.event.on('watch', ...) listener should not be used to run a task, it can be utilized to configure other tasks.