Files, in-depth

Last updated on Jan 20th, 2014

Tasks can be:

Multi-tasks work by taking source files and mapping them to destination files. For each destination, you can define one or more source files. Let's look at the ways to define files:

Compact mode

todo.

Files object format

In the Files Object Format, you specify a files object that has:

It looks something like this:

files: {
    'dist/file1.js': 'app/file1.js',
    'dist/combined.js': ['app/file1.js', 'app/file2.js', 'app/file3.js']
}

The first line defines one source for the destination file, while the second defines multiple source files for the destination.

Files Array Format

The Files Array Format is the canonical form of defining source/destination pairs. Grunt converts all the other formats to the Files Array Format before sending them to the task. It's very similar to the Files Object format, except we explicitly define the src and dest properties:

files: [{
    src: 'app/file1.js',
    dest: 'dist/file1.js'
}, {
    src: ['app/file1.js', 'app/file2.js', 'app/file3.js'],
    dest: 'dist/combined.js'
}]

It has the advantage of allowing us to specify additional properties, such as:

... and a couple of others.

Patterns

A few common patterns

Let's assume the following structure:

app/
    app.js
    data.js
    lib/
        jquery.js
        backbone.js
        underscore.js
    modules/
        api.js
        auth.js

The star pattern * is generally used to match all files in the folder:

src: 'app/*.js'
// matches app.js, data.js

The double star pattern **/* is used to match all files in the folder and its subfolders:

src: 'app/**/*.js'
// matches app.js, data.js, jquery.js, backbone.js, underscore.js, api.js, auth.js

You don't need to define complicated, all-encompassing patterns, because you can define the sources as arrays. They will all be processed and will result in a set of files (meaning each file will appear only once even if it's matched by multiple patterns).

For example, let's say you want to select all JavaScript files in the app folder and its subfolders, but not the lib subfolder:

src: ['app/**/*.js', '!app/lib/*.js']
// matches app.js, data.js, api.js, auth.js

This array of patterns will initially match all JavaScript files in app but then will exclude those from the lib subfolder. Easy!

Practice your patterns

Todo.

Defining the files object dynamically

We mentioned earlier that each object in the Files Array format can take additional properties. Some of these properties are useful in defining our file mappings dynamically. Let's take a look:

The dynamic mapping is useful for one-to-one mappings: for each source file you will get a destination file.

A real-world example would be copying an entire folder structure from one place to another.

my-project
    app/
        js/
            app.js
            data.js
            lib/
                jquery.js
                backbone.js
            modules/
                api.js
                auth.js
    dist/
    Gruntfile.js
    package.json

We will use grunt-contrib-copy to copy the content of the js folder into dist. A first attempt:

copy: {
    all: {
        files: [{
            expand: true,
            src: 'app/js/**/*.js',
            dest: 'dist/'
        }]
    }
}

An honest attempt, but it generates the following structure:

my-project
    app/
        js/
            app.js
            data.js
            lib/
                jquery.js
                backbone.js
            modules/
                api.js
                auth.js
    dist/
        app/
            js/
                app.js
                data.js
                lib/
                    jquery.js
                    backbone.js
                modules/
                    api.js
                    auth.js
    Gruntfile.js
    package.json

Close, but no cigar. We wanted the js folder included directly under dist.

What we need to do is take the app part out of the src property and put it in the cwd property:

copy: {
    all: {
        files: [{
            expand: true,
            cwd: 'app/',
            src: 'js/**/*.js',
            dest: 'dist/'
        }]
    }
}

Which gives us the expected result:

my-project
    app/
        js/
            app.js
            data.js
            lib/
                jquery.js
                backbone.js
            modules/
                api.js
                auth.js
    dist/
        js/
            app.js
            data.js
            lib/
                jquery.js
                backbone.js
            modules/
                api.js
                auth.js
    Gruntfile.js
    package.json

Freeze Frame High-Five!™

So where's the difference? Well, the cwd parameter — standing for the Common Working Directory, if you remember — dictates where the root of the whole structure we want to match is located, and the rest of the folder structure (from the src parameter) is mapped one-to-one in the path defined by dest.

Take five