Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Grunt & requirejs optimizer for a multi app project

I'm having issues getting Grunt to perform requirejs optimization on a project with the following structure:

static/js
    |── apps
        |── app.js
        |── dash.js
        |── news.js
        ... (many more 'app' files)
    |── build
    |── collections
    |── libs
    |── models
    |── util
    |── views

Each of static/js/apps/*.js should be compiled to static/js/build/*.js containing the relevant dependencies (eg. views/view1, libs/query etc).

This is currently being performed by a basic bash script:

JS_ROOT="static/js"

for f in ${JS_ROOT}/apps/*
do
    FILE=$(basename -s .js ${f})
    pushd .
    cd ${JS_ROOT} && r.js -o baseUrl=. name=libs/require-min.js include=apps/${FILE} out=build/${FILE}.js
    popd
done

I'm attempting to move to a Grunt-based optimization, with the following in Grunt.js:

requirejs: {
    compile: {
        options: {
            appDir: 'static/js/',
            baseUrl: './apps/',
            dir: 'static/js/build/',
            modules: [
                {
                    name: 'app',
                }
            ]
        }
    }
}

Running generates the following error:

>> Tracing dependencies for: app
>> Error: ENOENT, no such file or directory
>> 'static/js/build/apps/libs/jquery.js'
>> In module tree:
>>     app

I can clearly see what the problem is, but am failing to figure out how to indicate that the dependencies in each static/js/apps/*.js file are in static/js/ not static/js/build

In addition to this, I'm assuming that the modules block containing name: 'app' should be outputting the compiled file static/js/build/app.js from the contents of static/js/apps/app.js.

Without creating an additional module block for each file in static/js/apps, how can I compile each of the files into their relevant static/js/build/*.js file?

Update 1

So the following in my Gruntfile compiles static/js/apps/app.js successfully into static/js/build/app.js:

requirejs: {
    compile: {
        options: {
            baseUrl: 'static/js/',
            include: './apps/app.js',
            out: 'static/js/build/app.js',
        }
    }
}

The next step being to compile static/js/apps/*.js into static/js/build/*.js without having to define each individually...

Update 2

Modifying the above to:

requirejs: {
    compile: {
        options: {
            baseUrl: '../',
            include: './apps/<%= appFile %>',
            out: 'static/js/build/<%= appFile %>',
        }
    }
}

And creating the task:

grunt.registerTask('buildrjs', function() {
    var dir='static/js/apps/';
    grunt.file.setBase(dir);
    var files = grunt.file.expand(['*.js']);
    files.forEach(function(filename) {
        grunt.log.write('Compiling '+filename+'\n');
        grunt.config.set('appFile', filename);
        grunt.task.run('requirejs:compile');
    });
});

Almost gets me to the solution. The tasks runs through each file in static/js/apps/ and passes the filename into grunt.config.set('appFile', filename);. The output of the task outputs Compiling app.js Compiling news.js... etc, however afterwards the actual requirejs:compile tasks runs over & over on the last file in the static/js/apps/ directory, rather than each individual file. An async issue?

like image 288
Greg Annandale Avatar asked Dec 14 '13 13:12

Greg Annandale


1 Answers

Solved, by passing multiple sets of options to the requirejs task, thanks to this article for the final pointers I needed:

module.exports = function(grunt) {
    var files = grunt.file.expand('static/js/apps/*.js');
    var requirejsOptions = {};

    files.forEach(function(file) {
        var filename = file.split('/').pop();
        requirejsOptions[filename] = {
            options: {
                baseUrl: 'static/js/',
                include: './apps/'+filename,
                out: 'static/js/build/'+filename
            }
        };
    });

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        requirejs: requirejsOptions,
    });
};

Then the ['requirejs'] task can be run as normal and will output the appropriate compiled .js file as per each of the options: {} blocks that were specified in requirejsOptions, eg:

grunt.registerTask('default', ['requirejs']);

like image 177
Greg Annandale Avatar answered Nov 02 '22 10:11

Greg Annandale