128

I'm trying to use Gulp to:

  1. Take 3 specific javascript files, concatenate them, then save the result to a file (concat.js)
  2. Take this concatenated file and uglify/minify it, then save the result to another file (uglify.js)

I have the following code so far

    var gulp = require('gulp'),
        gp_concat = require('gulp-concat'),
        gp_uglify = require('gulp-uglify');

    gulp.task('js-fef', function(){
        return gulp.src(['file1.js', 'file2.js', 'file3.js'])
            .pipe(gp_concat('concat.js'))
            .pipe(gp_uglify())
            .pipe(gulp.dest('js'));
    });

    gulp.task('default', ['js-fef'], function(){});

However, the uglify operation doesn't seem to be working, or the file isn't generated for some reason.

What do I need to do to make this happen?

mido
  • 24,198
  • 15
  • 92
  • 117
ObiHill
  • 11,448
  • 20
  • 86
  • 135
  • 3
    Amazed to not see it yet, so I'd just like to quickly remark that the goal in itself somewhat goes against the philosophy of Gulp. Writing of intermediary files is more the Grunt way of working. Gulp promotes streams to improve speed. But I'm sure the guy asking had his reasons :). – Bart Oct 14 '15 at 10:21
  • I know it's an old thread, but I've made a npm module to do this kind of work very easily using a yaml file. Check it out : http://github.com/Stnaire/gulp-yaml-packages. – Stnaire Jun 29 '16 at 12:24

6 Answers6

166

It turns out that I needed to use gulp-rename and also output the concatenated file first before 'uglification'. Here's the code:

var gulp = require('gulp'),
    gp_concat = require('gulp-concat'),
    gp_rename = require('gulp-rename'),
    gp_uglify = require('gulp-uglify');

gulp.task('js-fef', function(){
    return gulp.src(['file1.js', 'file2.js', 'file3.js'])
        .pipe(gp_concat('concat.js'))
        .pipe(gulp.dest('dist'))
        .pipe(gp_rename('uglify.js'))
        .pipe(gp_uglify())
        .pipe(gulp.dest('dist'));
});

gulp.task('default', ['js-fef'], function(){});

Coming from grunt it was a little confusing at first but it makes sense now. I hope it helps the gulp noobs.

And, if you need sourcemaps, here's the updated code:

var gulp = require('gulp'),
    gp_concat = require('gulp-concat'),
    gp_rename = require('gulp-rename'),
    gp_uglify = require('gulp-uglify'),
    gp_sourcemaps = require('gulp-sourcemaps');

gulp.task('js-fef', function(){
    return gulp.src(['file1.js', 'file2.js', 'file3.js'])
        .pipe(gp_sourcemaps.init())
        .pipe(gp_concat('concat.js'))
        .pipe(gulp.dest('dist'))
        .pipe(gp_rename('uglify.js'))
        .pipe(gp_uglify())
        .pipe(gp_sourcemaps.write('./'))
        .pipe(gulp.dest('dist'));
});

gulp.task('default', ['js-fef'], function(){});

See gulp-sourcemaps for more on options and configuration.

UPDATE - FEB 2022

These days, it may be easier for you to handle build tasks using Gulp4 and Async/Await functionality:

// gulpfile.js

const fs = require('fs/promises');
const concat = require('concat');
const uglify = require('uglify-js');

let files_arr = ['file1.js', 'file2.js', 'file3.js'];

async function myGulpTask()
{
    var concat_str,
        uglify_str
    ;

    // concat content
    concat_str = await concat(files_arr);

    // save to file
    await fs.writeFile('concat.js', concat_str, 'utf8');

    // uglify content
    uglify_str = await uglify.minify(concat_str, {mangle:false}).code;

    // save to file
    await fs.writeFile('uglify.js', uglify_str, 'utf8');
}

module.exports = {
    myTask: myGulpTask
};

Then from the CLI:

gulp myTask
ObiHill
  • 11,448
  • 20
  • 86
  • 135
  • FYI, you are missing a single quotation mark before concat.js. The line after your return statement in `gulp.task` should be: `.pipe(gp_concat('concat.js'))` – Eric Jorgensen Dec 19 '14 at 18:02
  • 1
    All files are generated, however, in the debugger I still see the minified version. What can be the reason? The map file is named correctly and can be accessed by its URL. – Meglio Dec 18 '15 at 03:44
  • It will, depends on browsers the original sources are on different tab. You need to put breakpoint there. – przemcio Jan 05 '16 at 07:28
  • 4
    It is not clear for me why we need to do rename? Is it a bug or ? – przemcio Jan 05 '16 at 07:29
  • @przemcio In my case I wanted a record of all the files at each step in the process. However, if all you care about is the final minified file, then you can of course shorten the gulp file even further – ObiHill Jan 05 '16 at 19:11
  • excuse the noob question: Will this write sourcemaps both for the regular and the minified (uglified) ouput? – Frank N Jan 20 '17 at 18:38
  • Why do you need to do this twice? .pipe(gulp.dest('dist')); – Norbert Norbertson Mar 22 '19 at 14:13
18

My gulp file produces a final compiled-bundle-min.js, hope this helps someone.

enter image description here

//Gulpfile.js

var gulp = require("gulp");
var watch = require("gulp-watch");

var concat = require("gulp-concat");
var rename = require("gulp-rename");
var uglify = require("gulp-uglify");
var del = require("del");
var minifyCSS = require("gulp-minify-css");
var copy = require("gulp-copy");
var bower = require("gulp-bower");
var sourcemaps = require("gulp-sourcemaps");

var path = {
    src: "bower_components/",
    lib: "lib/"
}

var config = {
    jquerysrc: [
        path.src + "jquery/dist/jquery.js",
        path.src + "jquery-validation/dist/jquery.validate.js",
        path.src + "jquery-validation/dist/jquery.validate.unobtrusive.js"
    ],
    jquerybundle: path.lib + "jquery-bundle.js",
    ngsrc: [
        path.src + "angular/angular.js",
         path.src + "angular-route/angular-route.js",
         path.src + "angular-resource/angular-resource.js"
    ],
    ngbundle: path.lib + "ng-bundle.js",

    //JavaScript files that will be combined into a Bootstrap bundle
    bootstrapsrc: [
        path.src + "bootstrap/dist/js/bootstrap.js"
    ],
    bootstrapbundle: path.lib + "bootstrap-bundle.js"
}

// Synchronously delete the output script file(s)
gulp.task("clean-scripts", function (cb) {
    del(["lib","dist"], cb);
});

//Create a jquery bundled file
gulp.task("jquery-bundle", ["clean-scripts", "bower-restore"], function () {
    return gulp.src(config.jquerysrc)
     .pipe(concat("jquery-bundle.js"))
     .pipe(gulp.dest("lib"));
});

//Create a angular bundled file
gulp.task("ng-bundle", ["clean-scripts", "bower-restore"], function () {
    return gulp.src(config.ngsrc)
     .pipe(concat("ng-bundle.js"))
     .pipe(gulp.dest("lib"));
});

//Create a bootstrap bundled file
gulp.task("bootstrap-bundle", ["clean-scripts", "bower-restore"], function     () {
    return gulp.src(config.bootstrapsrc)
     .pipe(concat("bootstrap-bundle.js"))
     .pipe(gulp.dest("lib"));
});


// Combine and the vendor files from bower into bundles (output to the Scripts folder)
gulp.task("bundle-scripts", ["jquery-bundle", "ng-bundle", "bootstrap-bundle"], function () {

});

//Restore all bower packages
gulp.task("bower-restore", function () {
    return bower();
});

//build lib scripts
gulp.task("compile-lib", ["bundle-scripts"], function () {
    return gulp.src("lib/*.js")
        .pipe(sourcemaps.init())
        .pipe(concat("compiled-bundle.js"))
        .pipe(gulp.dest("dist"))
        .pipe(rename("compiled-bundle.min.js"))
        .pipe(uglify())
        .pipe(sourcemaps.write("./"))
        .pipe(gulp.dest("dist"));
});
dynamiclynk
  • 2,275
  • 27
  • 31
  • 1
    Great example @wchoward it was just what i was looking for, very clean straightforward design. – Faito Jun 03 '16 at 20:34
10

Solution using gulp-uglify, gulp-concat and gulp-sourcemaps. This is from a project I'm working on.

gulp.task('scripts', function () {
    return gulp.src(scripts, {base: '.'})
        .pipe(plumber(plumberOptions))
        .pipe(sourcemaps.init({
            loadMaps: false,
            debug: debug,
        }))
        .pipe(gulpif(debug, wrapper({
            header: fileHeader,
        })))
        .pipe(concat('all_the_things.js', {
            newLine:'\n;' // the newline is needed in case the file ends with a line comment, the semi-colon is needed if the last statement wasn't terminated
        }))
        .pipe(uglify({
            output: { // http://lisperator.net/uglifyjs/codegen
                beautify: debug,
                comments: debug ? true : /^!|\b(copyright|license)\b|@(preserve|license|cc_on)\b/i,
            },
            compress: { // http://lisperator.net/uglifyjs/compress, http://davidwalsh.name/compress-uglify
                sequences: !debug,
                booleans: !debug,
                conditionals: !debug,
                hoist_funs: false,
                hoist_vars: debug,
                warnings: debug,
            },
            mangle: !debug,
            outSourceMap: true,
            basePath: 'www',
            sourceRoot: '/'
        }))
        .pipe(sourcemaps.write('.', {
            includeContent: true,
            sourceRoot: '/',
        }))
        .pipe(plumber.stop())
        .pipe(gulp.dest('www/js'))
});

This combines and compresses all your scripts, puts them into a file called all_the_things.js. The file will end with a special line

//# sourceMappingURL=all_the_things.js.map

Which tells your browser to look for that map file, which it also writes out.

mpen
  • 272,448
  • 266
  • 850
  • 1,236
8
var gulp = require('gulp');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');

gulp.task('create-vendor', function () {
var files = [
    'bower_components/q/q.js',
    'bower_components/moment/min/moment-with-locales.min.js',
    'node_modules/jstorage/jstorage.min.js'
];

return gulp.src(files)
    .pipe(concat('vendor.js'))
    .pipe(gulp.dest('scripts'))
    .pipe(uglify())
    .pipe(gulp.dest('scripts'));
});

Your solution does not work because you need to save file after concat process and then uglify and save again. You do not need to rename file between concat and uglify.

Milos
  • 1,678
  • 4
  • 23
  • 49
  • I would say it's the prerogative of the developer to decide what they do and do not need when using gulp. In my case, I wanted to rename the files at each step. Others may prefer otherwise. – ObiHill Jul 28 '16 at 15:21
  • 1
    Of course, you can decide what is the best option for you. I understood, that answer below says you need to rename file, I just said that you do not need (it is not obligatory), sorry if I made some confusion. – Milos Jul 29 '16 at 06:35
4

Jun 10 2015: Note from the author of gulp-uglifyjs:

DEPRECATED: This plugin has been blacklisted as it relies on Uglify to concat the files instead of using gulp-concat, which breaks the "It should do one thing" paradigm. When I created this plugin, there was no way to get source maps to work with gulp, however now there is a gulp-sourcemaps plugin that achieves the same goal. gulp-uglifyjs still works great and gives very granular control over the Uglify execution, I'm just giving you a heads up that other options now exist.


Feb 18 2015: gulp-uglify and gulp-concat both work nicely with gulp-sourcemaps now. Just make sure to set the newLine option correctly for gulp-concat; I recommend \n;.


Original Answer (Dec 2014): Use gulp-uglifyjs instead. gulp-concat isn't necessarily safe; it needs to handle trailing semi-colons correctly. gulp-uglify also doesn't support source maps. Here's a snippet from a project I'm working on:

gulp.task('scripts', function () {
    gulp.src(scripts)
        .pipe(plumber())
        .pipe(uglify('all_the_things.js',{
            output: {
                beautify: false
            },
            outSourceMap: true,
            basePath: 'www',
            sourceRoot: '/'
        }))
        .pipe(plumber.stop())
        .pipe(gulp.dest('www/js'))
});
mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • Huh? gulp-uglify definitely supports source maps: https://github.com/floridoo/gulp-sourcemaps/wiki/Plugins-with-gulp-sourcemaps-support – Mister Oh Jan 16 '15 at 22:46
  • 1
    @MisterOh Not sure it did at the time of writing, or if it did, perhaps `gulp-concat` didn't (`gulp-uglify` won't let you minify multiple files so you have to concat first). Also, `gulp-concat` uses a `\r\n` by default, which *could* cause issues if your JS files don't terminate properly. But yes, now that there's support, it's probably better to go that route as its more flexible. – mpen Jan 17 '15 at 00:15
  • @Mark - Would appreciate if you would post the solution with gulp-sourcemaps that works inline with Obinwanne's answer. I can't seem to get it working. – NightOwl888 Jun 02 '15 at 15:25
  • @NightOwl888 [okay](http://stackoverflow.com/a/30600529/65387) Actually, that doesn't produce inline sourcemaps if that's what you were asking; it's still a separate file. – mpen Jun 02 '15 at 15:38
  • gulp-uglifyjs is also depecated now. Just using the gulp-uglify plugin should be enough now. See other answers for an up-to-date solution. – Neil Monroe Jun 10 '15 at 21:35
  • @NeilMonroe Thanks. Added a note to my answer to make it more clear. Keeping this here for history. – mpen Jun 10 '15 at 22:28
  • @Mark I don't understand. Firstly, my question made no mention of `gulp-uglifyjs`. Finally, you say `gulp-uglifyjs` is deprecated, use `gulp-uglifyjs` instead?! – ObiHill Jun 21 '15 at 11:44
  • @ObinwanneHill The horizontal rule indicates a section of my answer written during a different time period. I added the "DEPRECATED" warning to the top of my answer just recently. Below is my original answer. I offered `gulp-uglifyjs` as an alternative to `gulp-uglify` because at the time `gulp-uglify` didn't work with sourcemaps but `gulp-uglifyjs` did. I made note of that in my Feb 18th edit. – mpen Jun 27 '15 at 05:07
0

we are using below configuration to do something similar

    var gulp = require('gulp'),
    async = require("async"),
    less = require('gulp-less'),
    minifyCSS = require('gulp-minify-css'),
    uglify = require('gulp-uglify'),
    concat = require('gulp-concat'),
    gulpDS = require("./gulpDS"),
    del = require('del');

// CSS & Less
var jsarr = [gulpDS.jsbundle.mobile, gulpDS.jsbundle.desktop, gulpDS.jsbundle.common];
var cssarr = [gulpDS.cssbundle];

var generateJS = function() {

    jsarr.forEach(function(gulpDSObject) {
        async.map(Object.keys(gulpDSObject), function(key) {
            var val = gulpDSObject[key]
            execGulp(val, key);
        });

    })
}

var generateCSS = function() {
    cssarr.forEach(function(gulpDSObject) {
        async.map(Object.keys(gulpDSObject), function(key) {
            var val = gulpDSObject[key];
            execCSSGulp(val, key);
        })
    })
}

var execGulp = function(arrayOfItems, dest) {
    var destSplit = dest.split("/");
    var file = destSplit.pop();
    del.sync([dest])
    gulp.src(arrayOfItems)
        .pipe(concat(file))
        .pipe(uglify())
        .pipe(gulp.dest(destSplit.join("/")));
}

var execCSSGulp = function(arrayOfItems, dest) {
    var destSplit = dest.split("/");
    var file = destSplit.pop();
    del.sync([dest])
    gulp.src(arrayOfItems)
        .pipe(less())
        .pipe(concat(file))
        .pipe(minifyCSS())
        .pipe(gulp.dest(destSplit.join("/")));
}

gulp.task('css', generateCSS);
gulp.task('js', generateJS);

gulp.task('default', ['css', 'js']);

sample GulpDS file is below:

{

    jsbundle: {
        "mobile": {
            "public/javascripts/sample.min.js": ["public/javascripts/a.js", "public/javascripts/mobile/b.js"]
           },
        "desktop": {
            'public/javascripts/sample1.js': ["public/javascripts/c.js", "public/javascripts/d.js"]},
        "common": {
            'public/javascripts/responsive/sample2.js': ['public/javascripts/n.js']
           }
    },
    cssbundle: {
        "public/stylesheets/a.css": "public/stylesheets/less/a.less",
        }
}
dinesh
  • 229
  • 1
  • 3
  • 13