Skip to content Skip to sidebar Skip to footer

Grunt: Minify Js Across Project Of Multiple Dirs, So That The Contents Of Each Dir Flattens To A Single Minified File

I have a project, with javascript files structured something like this: My goal: I would like to minify/uglify these javascript files in the build output, such as there is 1 minif

Solution 1:

Thoughts

am I just thinking about this in the completely wrong way?

No, I don't think you are thinking about this in the wrong way. It's always a good thing to question the why/how for any development [period]. This is what makes us better developers long-term.

Whichever solution/tools you choose to utilize for your build process, whether that be a dedicated task runner such as grunt or gulp, or a higher level solution such as nodejs perhaps combined with npm-scripts, you still have to craft some code. Of course, the commonality between all those approaches mentioned is that you have to code using JavaScript.

Is the whole point of grunt to avoid coding things like this in node.js (and so I'm defeating the purpose by doing this?

It's not for me to say which solution/tool is better to use than another (that would be too opinionated) and personally I have had experience developing build processes using all those tools mentioned. Typically though I often find my development time for handling builds is reduced when using a dedicated task runner (grunt or gulp) than when using a more custom approach such as npm-scripts combined with nodejs.

Often, I too find a really great plug-in for grunt (such as grunt-contrib-uglify) to then discover after some tinkering that it meets 90% of my requirements, yet fails miserably for the remaining 10%. However, that's often the case when utilizing/integrating any open sourced package/module... You still have to fill the gaps so to speak!


Grunts lesser known gems

Before ditching gruntjs and going into nodejs only development mode just yet, (and again to reiterate I have no bias towards any of the approaches), I recommend that you check-out some of grunts features such as:

There has been may scenarios when using grunt that I've found utlizing the features listed above has helped me to address that remaining 10% of the requirement that I mentioned previously. The custom-tasks feature in particular has proved very useful many times.


Solution

It's worth noting that grunt-contrib-uglify is a multi-task plugin. I.e. It allows you to configure multiple Targets as you have demonstrated in the your uglify configuration in your question. You have included three Targets named dir1, dir2, and dirN.

With the fact that grunt-contrib-uglify is multi-task and keeping in mind the grunt features I listed previously... One approach to meet your requirement using grunt is to:

  1. Use a custom Task to dynamically configure and run the uglify task.
  2. Utilize grunt.file.expandto get the paths to the parent folder of each .js file.
  3. filter paths Array to only unique directory paths.
  4. Dynamically create the files configuration for each Target of the uglify task.
  5. Configure the uglify task with multiple Targets using grunt.config and then run the task using grunt-task.run.

Grunfile.js

module.exports = function (grunt) {

  'use strict';

  grunt.initConfig({
    uglify: {
      // <-- Intentionally blank, will be dynamically generated.
    }
  });

  /**
   * 1. Helper custom Task to dynamically configure and run the uglify task.
   */
  grunt.registerTask('myUglify', 'Configures uglify task', function () {
    var srcDirGlob = 'src/js/**/*.js';
    var destDir = 'dist/js/';
    var config = {};

    // 2. Get the paths to the parent diretory of each .js file.var paths = grunt.file.expand({ filter: 'isFile' }, srcDirGlob)
        .map(function (_path) {
          return _path.substring(0, _path.lastIndexOf("/"));
        });

    // 3. Filter paths Array to only unique directory paths.
    paths.filter(function(_path, pos){
          return paths.indexOf(_path) === pos;
        })

        // 4. Dynamically create the `files` configuration for each Target.
        .forEach(function(_path, index) {
          var dirName = _path.substring(_path.lastIndexOf('/') + 1);
          var destPath = destDir + dirName + '.min.js';
          var srcGlob = _path + '/*.js';

          config[index] = {
            files: {}
          }

          config[index].files[destPath] = srcGlob;
        });

    // NOTE: The dynamically created `config` object is now something like this:////    config: {//      0: {//        files: {//          'dist/js/dir1.min.js': 'src/js/dir1/**.js'//        }//      },//      1: {//        files: {//          'dist/js/dir2.min.js': 'src/js/dir2/**.js'//        }//      },//      ...//    }// 5. Configure the uglify task with multiple Targets//    (i.e. the `config` object) and then run the Task.
    grunt.config('uglify', config);
    grunt.task.run(['uglify']);
  });

  grunt.loadNpmTasks('grunt-contrib-uglify');

  grunt.registerTask('default', ["myUglify"]);
};

Results

Running $ grunt via your CLI using the Gruntfile.js above with a src directory as follows:

.
└── src
    └── js
        ├── dir1
        │   ├── fileA.js
        │   └── fileB.js
        ├── dir2
        │   ├── fileG.js
        │   └── fileN.js
        └── dir3
            ├── fileQ.js
            └── fileR.js`

..will concatenate and uglify the .js files producing a directory structure as follows:

.
└── dist
    └── js
        ├── dir1.min.js
        ├── dir2.min.js
        └── dir3.min.js`

Note: The parent folder name of the source .js file is used for the name of the resultant concatenated/uglified .js file.


Summary

  • The pattern/approach utilized in the Gruntfile.js (above) can be modified as necessary for many of the the plug-ins available.
  • As long as the plugin itself supports Multi-Tasks then consider utlizing a grunt Custom Task to fill in the gaps.

I hope this helps you.

Post a Comment for "Grunt: Minify Js Across Project Of Multiple Dirs, So That The Contents Of Each Dir Flattens To A Single Minified File"