How to integrate Yeoman and Require.js in 2014 (last version)

Update 10/06/2014: Yeoman has changed some little stuff so every instance of <%= yeoman.app %> should be replaced with <%= config.app %>, if you are using the last version. (Thanks to jorge from pointing out in the comments). I will include both versions in this guide.

For a long time I've been looking for a great of setup environment for developing web applications which:

  1. Takes care about dependencies;
  2. Groups all files in one for deployment;
  3. Changes are reflected in the browser as soon as I change the files (lazy);

Yes, this can also be named the lazy people environment. Do as less as possible while outputting as much as possible.

So here I am to present to you the Yeoman framework used with requirejs (automatically loads the files you need in the order you need, also used to group the files in one) and bower (loads third party libraries in a standard way)

Update 2014/02/12

Now Yeoman and Require.js is quite complicated to setup together, at least to make the build work. You can see here that many have tried to setup and it's quite complicated, but is an alternative.
In this guide/tutorial you will be able to setup it easily.

Yeoman

Yeoman is a workflow or framework but is better explained in more friendly words. What exactly does:

  • Creates a project structure;
  • Each time you edit a file in your editor the browser automatically refreshes itself and loads the new version;
  • Allows to create a big JavaScript for deployment by grouping all your JavaScript files. The same happens for the CSS files.

Getting started

Roadmap

  1. Install Yeoman
  2. Create the project structure
  3. Setting up require.js

1. Install Yeoman

This part will be the same as the one in the Yeoman tutorial.

Open a command line in any folder and run the command:

npm install -g yo

This will install the Yeoman globally.
Then in the same window run the command:

npm install -g generator-webapp

To install a generator.

A generator is just a type of project structure. There exists various types/templates for Yeoman, but for this example we will use the webapp one.

2. Create the project structure

Open a command line in the folder you want to create the new project (example: c:\web\myYeoman) and run:

yo webapp

Press enter in the following menu.
This will create the project structure in the folder.

3. Setting up require.js

A complicated time

Nowadays is quite complicated to setup require.js with yeoman and grunt. This is caused by some changes to usemin that doesn't allow html requirejs blocks. Yes, kind of confusion but the older users of Yeoman were able to use that (See issues a, b and c for more info).

My solution

But I've figured out a practical and easy way to solve the problem. This is my own solution, if you are interested in what others came with look here.

What is the idea:

  • The index.html file will not contain the data-main="scripts/main" attribute (useminPrepare no longer allow require.js blocks);
  • Divided the previous tag in two: one containing only the require.js file and the other containing the main.js file;
  • The main.js configures overrides the settings to: baseUrl: 'scripts';
  • The requirejs grunt task will override the main.js created by the concat task just after the concat task ended. By doing this the process can continue as would normally do; (This is the main solution)

Let's do it

The require.js is the most interesting part, this enables you to load the files in the way you want. To install, run in the same command line window the command:

bower install requirejs --save

Setup the index.html file

Then open the file \myYeoman\app\index.html and add

<script src="bower_components/requirejs/require.js"></script>

To look like this (notice the 2th line):

    <script src="bower_components/sass-bootstrap/js/tab.js"></script>
    <script src="bower_components/requirejs/require.js"></script>
    <!-- endbuild -->

    <!-- build:js({app,.tmp}) scripts/main.js -->
        <script src="scripts/main.js"></script>
    <!-- endbuild -->

View gist for complete index.html file here. Look at:

  • Line 60: <script src="bower_components/requirejs/require.js"></script>;
  • Line 64: <script src="scripts/main.js"></script>;

This will make the require.js file to be loaded before the main.js file. When you will build the application, require.js file will be included in the scripts/plugins.js file because of <!-- build:js scripts/plugins.js --> comment (see here in context).

Now the tricky part is that we need to change the gruntfile.js in order to make a final build file.

Setup the gruntfile.js with the require.js task

Load the task: You need to install grunt-requirejs node module first and this is done by running the command:

npm install grunt-requirejs --save

Then open gruntfile.js and add the following string (see line 14 so you know where to add it exactly):

grunt.loadNpmTasks('grunt-requirejs');

This will load the grunt-requirejs task within grunt.

Configure the task: Add the requirejs task :

requirejs: {
    dist: {
        options: {
            baseUrl        : '<%= config.app %>/scripts/',
            name           : 'main',
            mainConfigFile : '<%= config.app %>/scripts/main.js',
            out            : '.tmp/concat/scripts/main.js'
        }
    }
},

For older versions: If you are using an older version of yeoman then use this code (see here in context):

requirejs: {
    dist: {
        options: {
            baseUrl        : '<%= yeoman.app %>/scripts/',
            name           : 'main',
            mainConfigFile : '<%= yeoman.app %>/scripts/main.js',
            out            : '.tmp/concat/scripts/main.js'
        }
    }
},

The difference consists in config.app being replaced with yeoman.app. (Thanks to jorge from pointing out in the comments)

Update the build task: Edit the build task to be like this (in the same file, see here in context):

grunt.registerTask('build', [
    'clean:dist',
    'useminPrepare',
    'concurrent:dist',
    'autoprefixer',
    'concat',
    'requirejs:dist', // <- added now
    'cssmin',
    'uglify',
    'copy:dist',
    'modernizr',
    'rev',
    'usemin',
    'htmlmin',
]);

View the complete gruntfile.js here. Look at the lines:

  • Line 14: grunt.loadNpmTasks('grunt-requirejs');;
  • Lines 21-30: requirejs: {...;
  • Line 421: 'requirejs:dist',;

The 'requirejs:dist' task is necessary in order to compile all your JavaScript files in one big file.

Test the build

Run in the command line:

grunt build --verbose

If warnings come up do:

grunt build --verbose --force

But be careful to fix the warning, ask in the comments if you have any questions about it.

By doing the build it will create a file in /dist/scripts/****.main.js. Check it out and you'll see all the files concatenated and minified, a very ugly file.

Final words about yeoman and require.js

I know it's a bit complicated at first but it will save you a lot of time. All this is required in order to be able to create a final build file that is as small as possible (minified and uglified) and that contains all used files.

Importing jQuery

The web app generator already includes the jquery in the index.html, but I prefer to use bower with require.js dependencies. So remove the script tag with jquery from the index.html and continue with the tutorial.

In the command line window run:

bower install jquery --save

This will create a folder in your project myYeoman/app/bower_components/jquery that will contain a lot of js files. You want the jquery.js file so you will require it from the main.js file.

As jquery doesn't exactly work with require.js because doesn't follow the AMD module standard format you have to configure the jquery in your main.js in order to be loaded correctly. To do that open your main.js file and write (see here in context):

require.config({
    baseUrl: 'scripts',
    paths: {
        'jquery': '../bower_components/jquery/jquery',
    },
    shim: {
        'jquery': {
            deps: [],
            exports: '$'
        }
    }
});

The paths part defined where the file is on disk.

  • shim and deps: defines the dependencies of the jquery module;
  • shim and exports: '$': defines that the jquery will output a $ global variable;
  • paths.jquery: defineds that the main file to load for the module when requesting jquery should be ../bower_components/jquery/jquery. Notice that doesn't include the .js extension;

Here is how a jquery should be in order to don't need to configure the module:

define([/* deps */], function(){
    var jQuery = {...};
    return jQuery;
})

Now to require the jquery library put in main.js (see here in context):

require(['jquery'], function($){
    // now you can do what you always done with $
    $('body').remove();
})

Remember that your main.js file contains the require.js settings (require.config({...) defined above and the require(['jquery']... part.

Look here to see the complete main.js file.

Importing multiple modules

So you might need to import more modules and define dependencies between them, so I will show you some examples.

Import underscore

Let's import underscore in your project:

require.config({
    baseUrl: 'scripts',
    paths: {
        'jquery': '../bower_components/jquery/jquery',
        'underscore': '../bower_components/underscore/underscore',
    },
    shim: {
        'jquery': {
            deps: [],
            exports: '$'
        }
        'underscore': {
            deps: [],
            exports: '_'
        },
    }
});

// use here
require(['jquery', 'underscore'], function($, _){...});

Import backbone

require.config({
    baseUrl: 'scripts',
    paths: {
        'jquery': '../bower_components/jquery/jquery',
        'underscore': '../bower_components/underscore/underscore',
        'backbone': '../bower_components/backbone/backbone',
    },
    shim: {
        'jquery': {
            deps: [],
            exports: '$'
        }
        'underscore': {
            deps: [],
            exports: '_'
        },
        'backbone': {
            deps: [
                'underscore',
                'jquery'
            ],
            exports: 'Backbone'
        },
    }
});

// use here
require(['jquery', 'underscore', 'backbone'], function($, _, Backbone){...});

You could also require only backbone:

require(['backbone'], function(Backbone){
    // use Backbone at will!
})

Your first module

Create

Create a file named Zombie.js like this /app/scripts/Zombie.js and put into it:

define([], function(){
    var Zombie = function(){
        // zombie stuff
    }
    return Zombie;
})

See the complete file here.

Consume

Now you want to require the file in your main. Update the require(['jquery']... part in order to be:

require(['jquery', './Zombie.js'], function($, Zombie){
    // now you can do what you always done with $
    $('body').remove();
    // now you have the Zombie variable available in this context
    var zombie = new Zombie();
})
// Zombie is undefined here

See here the full gist with all the files.

Build it again

Build the project again to see it working with jquery and the Zombie class.

That was all about yeoman and require.js, thanks for your time.

Further discussion

What is your solution? How is your environment?
Please share with us how you do your JavaScript "stuff", maybe this post deserves an update.

Further reading

Subscribe to receive new interesting posts about programming

comments powered by Disqus