How to grunt-uglify multiple script files while keeping folder structure

18,919

Solution 1

The principle in Rafa Heringer's answer on the post you linked to looks promising, with a little twist:

uglify: {
    min: {
        files: grunt.file.expandMapping(['path/**/*.js', 'path2/**/*.js'], 'destination/', {
            rename: function(destBase, destPath) {
                return destBase+destPath.replace('.js', '.min.js');
            }
        })
    }
}

The only difference here is the double asterisk between the base path and the wildcard filename with its extension. That will go through all the sub-folders and - hopefully - output each find it finds in its rightful folder.

The output would be:

path/test.js => destination/path/test.min.js
path/subpath1/abc.js => destination/path/subpath1/abc.min.js
path/subpath2/yey.js => destination/path/subpath2/yey.min.js
path2/foo.js => destination/path2/foo.min.js

When it comes to doing the same with CSS (using the grunt-contrib-cssmin plugin), the approach mentioned above would still work, but you would have to combine it with the relevant plugin configurations that must be in place to output minified CSS the way you want.

PS: Haven't tried running it myself!

Solution 2

Similar to the answer by @DioNNiS, but stores minified files in the same folder:

    uglify: {
        all: {
            files: [{
                expand: true,
                cwd: 'path/to/js/',
                src: ['*.js', '!*.min.js'],
                dest: 'path/to/js/',
                ext: '.min.js'
            }]
        }
    }

Solution 3

Actually you can use following approach:

uglify: {
  all: {
    files: [{
      expand: true,
      cwd: 'js/',
      src: ['*.js', '**/*.js'],
      dest: 'js-min/',
      ext: '.min.js',
    }],
  },
}

Solution 4

The answer from Wallace is great, but if the files you are trying to minify don't exist before grunt starts (i.e. if you are depending on another task), it won't work because the map is generated before any task is run.

I came up with a solution for minifying generated files individually, using the node package uglify-js instead of grunt-contrib-uglify.

  • Add uglify-js to your package.json
  • Add one of the following example to your Grunfile (just replace "YOUR FILES HERE" by the appropriate glob(s) if you are using Example 1).
  • If you need to change the minified file destination or extension, use Example 2 instead. It uses grunt.file.recurse with a callback which provides you with the root directory, sub directory and file name of each file (it is easier then to build a custom destination path). Replace "FOLDER" by the directory you want to scan, and build your own "CUSTOM PATH HERE".

Example 1: With grunt.file.expand

grunt.registerTask('uglifyFiles', 'Uglifies files', function () {
    var jsp = require("uglify-js").parser,
        pro = require("uglify-js").uglify,
        count = 0;

    grunt.file.expand(['YOUR FILES HERE']).forEach(function (abspath) {
        // Exclude already minified files (with extension .min.js)
        if (!abspath.match(/\.min\.js$/i)) {
            // Get Abstract Syntax Tree
            var ast = jsp.parse(grunt.file.read(abspath));
            // If mangling
            // ast = pro.ast_mangle(ast);
            // If squeezing
            ast = pro.ast_squeeze(ast);
            // Write new file
            grunt.file.write(abspath.replace(/\.js$/i, '.min.js'), pro.gen_code(ast));
            count += 1;
        }
    });

    grunt.log.oklns("Successfully uglified " + count + " files");
});

Example 2: With grunt.file.recurse

grunt.registerTask('uglifyFiles', 'Uglifies files', function () {
    var jsp = require("uglify-js").parser,
        pro = require("uglify-js").uglify,
        count = 0;

    grunt.file.recurse('FOLDER', function callback(abspath, rootdir, subdir, filename) {
        // Exclude already minified files (with extension .min.js)
        if (!abspath.match(/\.min\.js$/i)) {
            // Get Abstract Syntax Tree
            var ast = jsp.parse(grunt.file.read(abspath));
            // If mangling
            // ast = pro.ast_mangle(ast);
            // If squeezing
            ast = pro.ast_squeeze(ast);
            // Write new file, using abspath or rootdir, subdir and filename
            grunt.file.write('CUSTOM PATH HERE', pro.gen_code(ast));
            count += 1;
        }
    });

    grunt.log.oklns("Successfully uglified " + count + " files");
});
Share:
18,919

Related videos on Youtube

KungWaz
Author by

KungWaz

Web developer at Netlight AS.

Updated on June 04, 2022

Comments

  • KungWaz
    KungWaz almost 2 years

    I have not found a good way to grunt-uglify multiple script files spread over multiple folders while keeping the folder structure including the uglified files intact. The only reason I want to do this is to be able to increase the performance of the "legacy" part of the web page I'm working on.

    I have found a way around this which I don't want to do, since it will take to much time, and that is to do it like in this answer (they specify each src and dest pair seperately): how to config grunt.js to minify files separately

    An example of what I want to achieve:

    **src dir (no uglify applied):**
    src
     |- app1
        |- randomFile.js
        |- scripts
           |- file1.js
           |- file2.js
        |- libs
           |- file3.js
           |- file4.js
     |- app2
       |- scripts
           |- file1.js
           |- file2.js
    
    **destination dir (uglify applied, same file name):**
    dist
     |- app1
        |- randomFile.js
        |- scripts
           |- file1.js
           |- file2.js
        |- libs
           |- file3.js
           |- file4.js
     |- app2
        |- scripts
           |- file1.js
           |- file2.js
    

    Btw, want to do the same for CSS-files if possible.

    Does anyone know if this is possible?

  • KungWaz
    KungWaz about 10 years
    Seems to work, thanks! Only problem I have left now is to make all legacy js files go through the uglifier :s
  • Solomon Closson
    Solomon Closson almost 4 years
    While this is great and all, the problem is that this will also minify already minified files, so if you already have .min.js files in those directories, it will create .min.min.js and so on everytime you run it. How to fix this?