infuse

Mainline your node JavaScript for universal consumption.

npm install infuse
15 downloads in the last week
131 downloads in the last month

Infuse

Mainline your node JavaScript for universal consumption.

Summary

Infuse bundles up your node JavaScript files by following the require("moduleName") statements in your source file(s), and then bundles it all up, and uglifies (minimizes) it into one JavaScript file.

In addition, infuse can replace symbols found in the source file with JavaScript values defined either with command line arguments, or through the use of another node module.

Coupled with uglify-js' ability to remove dead code, infuse acts as a pre-processor of sorts on your uglified file when using the --define and --define-module options.

Installation

% npm install -g infuse

Command Line Usage

% infuse -h

Usage: infuse INPUT_PATH OUTPUT_PATH [options]

INPUT_PATH     File or directory to read.
OUTPUT_PATH     File or directory to write to. If not specified, write to STDOUT.

Options:
   -N, --no-minify               Do not minify the output. Essentially, set `beautify` for
                                 the UglifyJS output.
   -D, --define SYMBOL[=VALUE]   Replace all instances of the specified SYMBOL with VALUE.
                                 If VALUE is not given, SYMBOL will be set to true.
                                 Otherwise, VALUE will be a JSON parsed value, or plain
                                 string. Can be specified multiple times.
   -d, --define-module NAME      Will load the NAMEd module (as per require()) and 'define'
                                 all exported properties. Can be specified multiple times.
   -E, --embed                   Embed the infused modules as strings in the final output,
                                 and lazy-evaluate them when required.
   -R, --reserved WORD           A comma-delimited list of reserved words that should NOT be
                                 mangled. Can be specified multiple times.
   -L, --node-lib PATH           PATH to your local directory of node builtin modules. These
                                 are used to resolve requires for 'core' modules (not
                                 suggested). Can be specified multiple times, and each
                                 directory will be tried.
   -i, --infuse PATH             Pre-infuse with PATH. These files will be infused into the
                                 final output and will be automatically 'de-fused' before
                                 the INPUT_PATH executes. Can be specified multiple times.
   -I, --dump-infusions          Print all the paths 'required' by INPUT_PATH (and all other
                                 required files) to STDOUT and exit.
   -A, --dump-ast                Dump out the generated Abstract Syntax Tree and exit.
   -V, --version                 Print the version information and exit.
   -h, --help                    Print this and exit.

NOTES:
If OUTPUT_PATH is a directory then each file from INPUT_PATH will be infused and placed in
OUTPUT_PATH. If not a directory OUTPUT_PATH is assumed to be a file, and all files from INPUT_PATH
will be infused and combined into OUTPUT_PATH (as '-i, --infuse').

Dependencies

These are installed when infuse is installed.

uglify-js: >=1.3.x
nomnom:    >=1.5.x
resolve:   >=0.2.x
proteus:   >=0.0.x
wordwrap:  >=0.0.2

Development Dependencies

Installed when you run npm link in the package directory.

mocha:      >=0.3.x
should:     >=0.5.x
sake:       >=0.1.x
underscore: >=1.3.x

Defines

Infuse now processes all defines itself, and the values returned from defines are translated into the appropriate AST structure. No need for your define/define-module to return an AST formatted array.

By having infuse handle the defines in the pre-mangled/squeezed AST, if you supply the --no-minify flag to infuse you can see the beautified uglify-js generated output without any dead-code being removed (this is helpful when reviewing what your defines are returning/generating).

Example

// contents my-defines.js

var env = process.env.ENVIRONMENT || "dev",
    tokens = {
        appName: "My Great WebApp",
        authorName: "Me"
    },
    isDebug = env === "dev"
;

module.exports = {
    ENVIRONMENT: env,
    DEBUG: isDebug,
    TOKEN: function (key) {
        return tokens[key];
    },
    MY_METHOD: function () {
        return isDebug ?
            function () {
                return "is debug";
            } :
            function () {
                return "is not debug";
            }
    },
    CONFIG: {
        foo: "foo",
        baz: [1, 2, 3],
        doSomething: function () {
            return "something";
        },
        isDebug: isDebug
    }
};

// contents of my-script.js

var appConfig   = CONFIG;

if (ENVIRONMENT === "dev") {
    console.log("A note to the developer...");
}

function MyClass () {}

MyClass.prototype = {
    appName: TOKEN("appName"),
    authorName: TOKEN("authorName"),

    specialMethod: MY_METHOD(),
};

Running infuse my-script.js script.js -d ./my-defines.js -N (assuming ENVIRONMENT is set to "dev") the following would be produced:

// contents of script.js

var appConfig = {
    foo: "foo",
    baz: [ 1, 2, 3 ],
    doSomething: function() {
        return "something";
    },
    isDebug: true
};

if ("dev" === "dev") {
    console.log("A note to the developer...");
}

function MyClass() {}

MyClass.prototype = {
    appName: "My Great WebApp",
    authorName: "Me",
    specialMethod: function() {
        return "is debug";
    }
};

And the minified result (infuse my-script.js script.js -d ./my-defines.js) would be:

// contents of script.js
function b(){}var a={foo:"foo",baz:[1,2,3],doSomething:function(){return"something"},isDebug:true};console.log("A note to the developer..."),b.prototype={appName:"My Great WebApp",authorName:"Me",specialMethod:function(){return"is debug"}}

Note how the if block was removed around the console.log statement, since this is "dev" mode. If ENVIRONMENT was set to some other value, console.log would be removed too.

There are more examples in the "examples" directory. Install the development dependencies, and then run:

sake examples ENVIRONMENT=[dev|prod] BUILD_TYPE=[debug|release]

Embedding

The infuse -E, --embed option will infuse required modules as strings and lazy-evaluate them when used in the final script. In the case of a browser, this means appending a script element to the head of the document temporarily. In other cases, infuse goes to the dark side and uses eval.

The advantage of embedding is that the required JavaScript modules are not evaluated until the strings are put into a script node, or eval'd, when needed. This facilitates faster loading of the infused JavaScript file, since it is, mostly, one large string.

A Poor Man's Minifier

You can get started quickly with infuse by using the -i, --infuse PATH option:

infuse -i path/one.js -i path/two.js -i path/three.js > compiled.js

This basically infuses up the individual files and automatically de-fuses them when the compiled script runs.

Caveats

Run-time variables

Currently, infuse does not compute local variables, and can not determine require statements with variables defined at run-time.

For example, this will not work, and infuse will throw an error:

// script.js

var pkgModule = require("./" + process.env.ENVIRONMENT + "-pkg.js");

This will however:

// script.js infused with `--define PKG=prod-pkg.js`

var pkgModule = require(PKG);

Global scope leakage

Since infuse does not do any computing of local variables, it can not determine if variables leak outside of a required module's scope. In node this is not so much a problem, since each module is run in it's own context and only the items explicitly exported through module.exports are exposed. Infuse uses an anonymous function to simulate what node does, however if the infused module doesn't locally scope a variable with the var keyword, that variable will be set on the global object (usually the window).

In the future, plans are to resolve the caveats with more introspection of the AST parsed by UglifyJS. Stay tuned.

Report an Issue

License

Copyright (c) 2012 Jerry Hamlet jerry@hamletink.com

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

The Software shall be used for Good, not Evil.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

npm loves you