Fork me on GitHub

Gear.js

Build System for Node.js and the Browser

Gear.js is an easy to use, simple to extend, and powerful build system. Chain tasks together to build projects with ease.

new Queue()
    .read('foo.js')
    .inspect()
    .jslint()
    .jsminify()
    .write('foo.min.js')
    .inspect()
    .run();

Features

Installation

Node.js Install

To get the most out of Gear.js, install gear-lib which contains tasks for linting, minifying, and deploying JS/CSS assets.

$ npm install gear gear-lib

Usage

var gear = require('gear');
new gear.Queue({registry: 'gear-lib'}).log('test!').run();
// Or creating a registry object with module parameter explicitly
new gear.Queue({registry: new gear.Registry({module: 'gear-lib'})}).log('test!').run();

Browser Install

<script type="text/javascript" src="https://github.com/yahoo/gear/raw/master/build/gear.js"></script>
<script type="text/javascript" src="https://github.com/yahoo/gear-lib/raw/master/build/gear-lib.js"></script>

Usage (localStorage is used as a simple filesystem for read/write tasks)

require(['gear', 'gear-lib'], function(gear, lib) {
    new gear.Queue({registry: new gear.Registry({tasks: lib})}).log('test!').run();
});

Source

Examples

Execute Tasks On Multiple Items

new Queue()
    .read(['foo.js', {name: 'bar.js'}, 'baz.js'])
    .log('read files')
    .inspect()
    .write(['foo2.js', 'bar2.js']) // Not writing 'baz.js'
    .run();

Parallel Task Execution

new Queue()
    .read('foo.js')
    .log('Parallel Tasks')
    .tasks({
        read:     {task: ['read', ['bar.js', 'baz.js']]},
        combine:  {requires: 'read', task: 'concat'}, // Runs after read completes
        minify:   {requires: 'combine', task: 'jsminify'},
        print:    {requires: 'minify', task: 'inspect'},
        parallel: {task: ['log', "Hello Gear.js world!"]}, // Run parallel to read
        join:     {requires: ['parallel', 'print']} // Joined data is passed to next task in queue
     })
     .inspect()
     .run();

Documentation

Queue

Registry

Tasks

Library Tasks

Custom Tasks

CLI

Queue()

Queue(options)

Queue constructor.

Arguments

  • options - (Object) Options for queue.
  • options.logger - (Object) Logger instance, usually console.
  • options.registry - (Registry) Registry loaded with available tasks.
new Queue()
    .log('New queue')
    .run();

Queue.task(name)

Queue.task(name, options)

Helper method to run the specified task. Preferred task execution style is to call the task directly i.e. inspect() instead of task('inspect').

Arguments

  • name - (String) Name of task in registry.
  • options - Task specific options.
new Queue()
    .task('log', 'New queue')
    .run();

Queue.run()

Queue.run(callback)

Runs the queue.

Arguments

  • callback - (Function(err, results)) Called on queue completion.
new Queue()
    .log('test')
    .run();

Registry()

Registry(options)

Creates a new Registry instance. Registries contain available tasks.

Arguments

  • options - See Registry.load.
new Registry();

Registry.load(options)

Load tasks from NPM module, directory, or file.

Arguments

  • options.module - Module to load tasks from.
  • options.dirname - Directory to load tasks from.
  • options.filename - File to load tasks from.
  • options.tasks - Object to load tasks from. Format: {name0: fn0, ..., nameN: fnN}
new Registry().load({dirname: 'foo'});
var registry = new Registry({tasks: {
    counter: function(options, blob, done) {
        console.log(options + ' for ' + blob.name + ': ' + blob.result.length);
        done(null, blob);
    }
}});

new gear.Queue({registry: registry})
    .read(['foo.js', 'bar.js'])
    .counter('CHAR COUNT')
    .run();

read(name)

read(options)

Appends file contents onto queue.

Arguments

  • options.name - (String) Filename to read.
  • options.encoding - (String) bin (binary) or utf8 (default).
new Queue()
    .read('foo.js')
    .read(['foo.js', 'bar.js'])
    .read([{name: 'foo.js'}, {name: 'bar.js'}, {name: 'baz.js'}])
    .run(function(err, results) {
        console.log(('' + results[0]).length + ' characters');
    });

write(name)

write(options)

Write the blob to disk.

Arguments

  • options.name - (String) File to write, will replace {checksum} with hash of blob content.
  • options.encoding - (String) bin (binary) or utf8 (default).
new Queue()
    .read('foo.js')
    .write('foo2.js')
    .write({name: 'foo3.js'})
    .inspect()
    .run();

concat()

concat(callback)

Concatenates blobs.

Arguments

  • callback - (Function) N/A.
new Queue()
    .read(['foo.js', 'bar.js'])
    .concat()
    .inspect()
    .run();

inspect()

Inspects blobs.

new Queue()
    .read(['foo.js', 'bar.js'])
    .inspect()
    .run();

log(message)

Log a message.

Arguments

  • message - (String) Message to log.
new Queue()
    .log('Hi')
    .run();

replace(options)

Replace strings using RegExp.

Arguments

  • options.regex - RegExp object or string.
  • options.flags - RegExp flags if using string.
  • options.replace - Replacement string.
new Queue()
    .read('foo.js')
    .inspect()
    .replace({regex: /Y.log\(.+?\);?/mg, replace: ''})
    .replace({regex: "console.log\\(.+?\\);?", replace: '', flags: 'mg'}) // Using string
    .inspect()
    .run();

tasks(workflow)

Arguments

  • workflow - (Object) Task workflow described below.
// label - Task instance name.
// label.task - Task name and optionally options.
// label.requires - List of labels that must be executed before this task runs.
new Queue()
    .read('foo.js')
    .tasks({
        dev:     {task: ['write', 'output.js']},
        prodmin: {task: 'jsminify'},
        prod:    {requires: 'prodmin', task: ['write', 'output.min.js']},
        join:    {requires: ['dev', 'prod']}
    })
    .run(function() {
        console.log('output.js - ' + localStorage['output.js'].length);
        console.log('output.min.js - ' + localStorage['output.min.js'].length);
    });

Library Tasks

gear-lib contains tasks such as:

  • jslint
  • jsminify
  • csslint
  • cssminify
  • s3
  • handlebars

Node.js

$ npm install gear-lib

Browser

<script type="text/javascript" src="https://github.com/yahoo/gear-lib/raw/master/build/gear-lib.js"></script>

Custom Tasks

Writing a task is especially easy compared to other Node.js build systems. Tasks operate on simple immutable blobs. The task returns a transformed blob via its callback.

Arguments

  • options - Options for the task.
  • blob - Immutable blob.
  • done(err, result) - Callback executed when task is complete.
// example.js
// Example task creates new blob containing `string`
exports.example = function(string, blob, done) {
    done(null, new blob.constructor(string)); // blob.constructor() is equivalent to Blob()
};

Running Example Task

new Queue({registry: new Registry({filename: 'example.js'})})
    .example('EXAMPLE')
    .run();

Inline Custom Task

var tasks = {
    example: function(string, blob, done) {
        done(null, new blob.constructor(string));
    };
};

new Queue({registry: new Registry({tasks: tasks})})
    .example('EXAMPLE')
    .run();

Working With Blobs

Blobs are loosely based on the W3C Blob.

  • Immutable data object.
  • Contains a property bag as well as a private non-enumerable result property.
  • Blobs can be merged with other blobs. When merging, properties in the property bag are merged, while result is concatenated.
  • result property can be Buffer or String.
var b1 = new Blob('Hello');
console.log(b1);
var b2 = new Blob([b1, ' world!']);
console.log(b2);
var b3 = new Blob(b2, {name: 'TEST'});
console.log(b3.result + ' - ' + b3.name);

CLI

It's possible to execute a tasks workflow from the command line.

Create a file named Gearfile in your project directory. You can include executable Javascript which evaluates to a valid workflow.

// Gearfile
{
    js:        {task: ['read', ['foo.js', 'bar.js', 'baz.js']]},
    js_concat: {requires: 'js', task: 'concat'},
    js_min:    {requires: 'js_concat', task: 'jsminify'}
    js_write:  {requires: 'js_min', task: ['write', 'foobarbaz.min.js']}

    css:       {task: ['read', 'xyz.css']},
    css_min:   {requires: 'css', task: 'cssminify'},
    css_write: {requires: 'css_min', task: ['write', 'xyz.min.css']}

    join:  {requires: ['js_write', 'css_write']}
}

Then run the gear command:

$ gear

If you have any require paths you will need to use an absolute path like so:

// Sample Gearfile
(function() {
    var path = require('path'),
        handlebars = require(path.join(process.cwd(), 'node_modules/handlebars')),
        template = handlebars.compile('<h1>{{msg}}<h1>');

    return {
        log:     {task: ['log', template({msg: 'Hello, world!'})]},
        join:    {requires: 'log'}
    };
})();

By not returning a workflow you can run a queue in the normal way. The gear object is automatically included in the context.

(function() {
    new gear.Queue()
        .log('Hello, Gearfile!')
        .run();
})();

Who's Using Gear.js

Mojito Shaker by Yahoo.

Shifter, YUI builder by Dav Glass.

Special Thanks

Gear.js takes inspiration from a few sources: