15

This is my first time using Grunt and I'd like to have it combine all my js modules, each of which is wrapped in an immediately executing function, containing a 'use strict' declaration and put them into one file, wrapped up in only one immediately executing function, with only one 'use strict' declaration.

How is this normally done?

I figured this would be a common use case? Perhaps I'm going about things the wrong way? Should I be using one of the module loading formats (i.e. commonjs, amd) All these files will always be loaded together in the browser, so I actually wouldn't mind removing all the immediately executing functions if that's how people normally go about it. The important part is that the end result is somehow wrapped, passes lint and unit tests and contains the 'use strict' declaration.

(I should clarify, I do have it working, linting, unit-testing, concatenating, and minifying, it just feels like I'm doing something wrong when I see a bunch of unnecessary immediately executing functions in the final concatenated file.)

Dmitry Pashkevich
  • 13,431
  • 9
  • 55
  • 73
gxc
  • 4,946
  • 6
  • 37
  • 55

4 Answers4

35

As of pull request 10, grunt-contrib-concat now has a footer option. Instead of an intro and an outro file, I would use a banner and a footer.

Gruntfile.js

concat: {
  dist: {
    src: ['src/my-lib.js'],
    dest: 'dist/<%= pkg.name %>.js',
    options: {
      banner: ";(function( window, undefined ){ \n 'use strict';",
      footer: "}( window ));"
    }
  }
}

In my opinion, this is more maintainable and allows for templating using other properties defined in your Gruntfile.

briangonzalez
  • 1,606
  • 16
  • 21
  • Agree, this is far better than any other option and since is the official Grunt release, you can be sure that the plugin will be maintained. – Rodrigo Sep 06 '14 at 06:21
23

I usually do it like the jQuery team does it. You create an intro.js and outro.js and put everything else in between:

intro.js

;(function( window, undefined ){
  'use strict';

outro.js

}( window ));

grunt.js:

concat: {
  dist: {
    src: [
      'js/src/intro.js',
      ...
      'js/src/outro.js'
    ],
    dest: 'js/out/app.js'
  }
}
Laurens Rietveld
  • 958
  • 1
  • 11
  • 21
elclanrs
  • 92,861
  • 21
  • 134
  • 171
  • damn, I even looked at their github repo to see how they did it but totally didn't catch that, thanks! – gxc Oct 16 '12 at 05:03
  • 1
    I actually use grunt to do this with my static html during development. And I started doing the same with js, but instead of intro/outro, I use head and footer. Since i don't need to dynamically update doc titles during development it works great. – jonschlinkert Oct 19 '12 at 20:52
2

Just want to add to @elclanrs answer that if you want to be able to keep your modules in separate files for easier debugging during development, you would obviously have to wrap them with intro.js and outro.js as well. Using the built-in concat task you'd have to write something like:

concat: {
  events_debug: { // wrap the 'events' module in IIFE
    src: [
      'js/src/intro.js',
      'js/src/events.js',
      'js/src/outro.js'
    ],
    dest: 'js/out/events.js'
  },
  callbacks_debug: { // wrap the 'callbacks' module in IIFE
    src: [
      'js/src/intro.js',
      'js/src/callbacks.js',
      'js/src/outro.js'
    ],
    dest: 'js/out/callbacks.js'
  }

  // zzZZZ...
}

Which is very tedious and self-repeating. Perhaps you may want to create a custom task for mass-wrapping files, e.g.

wrap: {
    html: {
        intro: 'partials/intro.js',
        outro: 'partials/outro.js',
        src: 'js/*.js',
        dest: 'out' // output directory
    }
}

There was a question about this recently, see this thread:

Using grunt concat, how would I automate the concatenation of the same file to many other files?

Community
  • 1
  • 1
Dmitry Pashkevich
  • 13,431
  • 9
  • 55
  • 73
  • 1
    Thanks @Dmitry, I think I'm slowly getting how this works, its not very straight forward. It makes me wonder if it makes more sense to write each module wrapped in an iife, and then have a grunt task that strips the wrapper. Like maybe it strips everything before an `//<--intro` and everything after an `//outro-->`...? – gxc Oct 16 '12 at 17:35
  • ...I guess doing "wrap" instead of "unwrap" adheres better to "DRY" – gxc Oct 16 '12 at 17:55
  • It's up to you :) Of course concatenating is easier and safer and faster than stripping a pattern. But on the other hand, I'm not sure I like having my source files depend on the build tool (i.e. your modules aren't portable anymore because they don't work without being wrapped in IIFE. On the opposite, you'll have to manually strip IIFE from any external modules you include into your project). So good considerations... – Dmitry Pashkevich Oct 16 '12 at 19:12
  • Yeah, the build tool is great and all but when it comes to debugging, there's nothing like just good 'ol cmd+shift+r :) However, I don't see why you would have to strip the iife from 3rd party modules...? – gxc Oct 16 '12 at 20:42
  • You may want to do that if you want to an add 3rd party modules to the one big file file alongside your concatenated code – Dmitry Pashkevich Oct 17 '12 at 08:16
1

I would recommend you to use my grunt plugin grunt-concat-deps since it automatically resolves your modules independent on your architecture.

PLUS: You don't need any explicit module configuration for the plugin as it relies on declarative and decentralized module definition in a YUIDoc style.

See here for further information: https://github.com/leoselig/grunt-concat-deps

Leo Selig
  • 1,062
  • 1
  • 16
  • 30