/nld

Node linker

Primary LanguageJavaScriptMIT LicenseMIT

nld - The Node linker

Link files together, compress javascript and produce a single bundle file.

README Contents

Installation

npm install nld -g should do it! This will install nld globally so that you can use nld ... in command line like any other program.

If you plan to embed it inside an application, remove the -g flag to install it locally. You can now use require("nld") but you can no longer use it from command line.

If you want both the executable and the library, install it globally and then use npm link nld to link the global installation in the local scope.

Command line usage

nld <arg or opt>... [-o bundle.ndl]

Arguments and options can be given in any order. Arguments are repeatable, options are intended to be used only once.

Arguments

  • file.js — Link a js file
  • -x file.js — Link a js file as executable, this file will be executed automaticaly when the bundle is itself executed.
  • -s static.txt — Link a static text file.
  • -b foo.bin — Link a static binary file.
  • -l bundle.ndl — Link another nld bundle with this bundle.

Arguments files are processed in the following order: -l > -b > -s > no flag > -x.

Within a group, files are processed in the order given in the command line. If a symbol collision occurs, only the last one will be kept and a warning will be printed.

When linked with -l, bundles are merged together. If you want to link a bundle without merging, link it without flag.

Symbols naming and paths-management

When files are linked inside the bundle, nld uses a symbol name to distinguish them. In fact, a bundle is simply a collection of symbols. This name can be explicitly specified in command line by using symbol_name:file_path as argument.

With -l, this syntax behaves differently: the first-part is used as a symbol prefix. A symbol bar.js inside bundle.ndl linked with -l foo/:bundle.ndl will now be called foo/bar.js. It's a simple concatenation, so -l foo:bundle.ndl will produce foobar.js. In contrast to explicit symbol name, explicit symbol prefix can be empty: -l :bundle.ndl will produce bar.js.

If the symbol name is missing for a specific file, nld generate a name based on the path of this file. To do so, nld search the longest path common to all linked files, the longest path containing all the files, and then use the relative path of the file from this enclosing path.

Example:

$ pwd            
/foo/bar/b

$ nld ../a.js b.js -x c/c.js -o d.js
Absolute paths:
    ../a.js -> /foo/bar/a.js
    b.js    -> /foo/bar/b/b.js
    c/c.js  -> /foo/bar/b/c/c.js
Common path: /foo/bar/
So, symbol names:
    ../a.js -> a.js
    b.js    -> b/b.js
    c/c.js  -> b/c/c.js
Output is written in: /foo/bar/b/d.js (but is not taken into account in the common path)

With -l (and without explicit prefix), the path to the linked bundle will be taken into account when generating the symbol name. Symbol foo/bar.js linked with -b baz/bundle.ndl will be named baz/foo/bar.js. In fact the default prefix is the path to the bundle. Linking the same bundle with -l lib/:baz/bundle.ndl will produce the symbol lib/foo/bar.js and not lib/baz/foo/bar.js.

Autogenerated symbol names reflect the the directory structure before linking. In fact, they are always generated even if a symbol was explicitly named and are used as a fall back name. If you link a file with foo:bar/foo.js, this file will be available as foo and bar/foo.js.

Options

  • -a ["node" | "generic" | path] — Use a specific adapter. The adapter provides required code to use the bundle in a specific environment. You can either specify a built-in adapter or a file path. This file will be loaded as a Node.js module and is expected to export a single function as module.exports. This function will be linked with the bundle and executed when loading the bundle.
  • -c — Compress JS. Use the JS compressor on input scripts and not only on the bundle itself. This option removes shebang from files.
  • -d [2 | 3] — Debug. When debug is used, an indented bundle is generated for debugging purpose. -d 2 disables the compression and -d 3 disables reformating (code produced is raw generated code).
  • -f — Flat linking, small change with symbol names generation: nld will not care about directories structure and only use the file basename as symbol name. Note: this does not affect the path-map that still contains the full original symbol path.
  • -i [path] — Interpreter path, prefix the output with a shebang to the specified path and if no path is given to #!/usr/bin/env node.
  • -j join_name — Join linking, join all arguments together and export an unique symbol with the given name. When join linking is used, -l -s -b arguments and -f are ignored, and -x is now a boolean option used to make the unique joined symbol executable. Linked scripts share a common namespace and are concatenated in the same order than arguments. Compressor, if used, is only run once on the whole resulting script.
  • -o file — Output the resulting bundle in the specified file rather than printing it on stdio.
  • -p [args] — Passive mode. Sometimes adapter are insufficient to load a bundle in a specific environment. Bundle produced with -p take the form of a function declaration and are expected to be eval'ed and run by the host script. If args are given, the generated function will take args as arguments. This flag produces bundle incompatible with nld itself. This flag should not be used except if really necessary.
  • -q — Quiet, does not display warnings.
  • -u — Unwrapped. By default, the bundle is wrapped inside an anonymous function ((function(){})()). Sometimes it can be unnecessary. When this flag is set, -p is ignored.

Additional operations

Dump

nld --dump bundle.ndl [symbol]

Dump the bundle content. This will print all symbols name, all paths, and the autorun list.

If a symbol name is given, this will print the symbol value instead. Binary symbols are encoded in Base64 inside the bundle, and it's this encoding that will be printed. If you want the original binary content, you must use nld --dump bundle.ndl symbol -b. The -b flag should not be used with text symbols (linked without -b).

Edit / Convert

nld --edit bundle.ndl <options...> or nld--convert bundle.ndl <options...>

Load a bundle and rebuild it. Build options -a -d -i -o -p -q -u are available in convert mode.

Using the bundle

All these use cases assume that -a is not used and that the bundle is a default "node" one.

Inside the bundle

Inside the bundle, scripts are sandboxed. This sandbox mimics the Node.js one, and replace some Node.js native objects. In theory, most scripts should run without modification inside or outside a nld bundle.

require() is replaced by a bundle specific function. When a module is loaded inside a bundle, this function first check if the require'd path is a symbol inside the bundle based on the given path and the caller original path. If it's the case, the module is loaded from the bundle. But if it's not the case, this version of require falls back to the native Node.js' require and load the module from the outside.

The require(module, weak) function inside the bundle take a additional boolean argument weak. If weak this function will first try to load the file from the outside, and then from the inside, reversing the default behaviour.

Standalone bundle

Bundle are standard javascript files. When executed, the whole bundle contents is loaded at once and then a small function checks for scripts flagged as executable and executes them.

So if you can node a.js, you can nld -x a.js -o b.ndl && node b.ndl. And if a.js requires c.js, you can nld -x a.js c.js -o b.ndl && node b.ndl.

Included bundle

require("./bundle.ndl"), from a Node.js script, returns the loader function: __nld(symbol, statically). This function load the requested symbol and execute it if it's a script (except if statically is true) and return it. If executed the return value will be module.exports, if not it will be the raw symbol value.

var bundle = require("./bundle.ndl");
var foo = bundle("./foo.js"); // eval foo.js and returns its module.exports
var bar = bundle("./bar.txt"); // returns the raw file content from bar.txt
var bar = bundle("./bar.txt"); // returns the raw baz.js without evaluation

var foo = require("./bundle.ndl")("./foo.js"); // like `bundle("./foo.js")`

API

var nld = require("nld");

// Open a bundle
var bundle = nld.open(path);
var bundle = new nld.Bundle(path);

// Create an empty bundle
var bundle = nld.create();
var bundle = new nld.Bundle();

bundle.symbol("foo");
// -> { data: "...", path: "foo.js", flag: "x" }

bundle.symbols();
// -> [{ data: "...", path: "foo.js", flag: "x" }, ...]

bundle.map();
// -> { "foo": "foo.js", ... }

bundle.autoruns();
// -> [ "foo", ... ]

bundle.resolve("foo.js");
bundle.resolve("foo");
// -> "foo"
bundle.resolve("doesntexists");
// -> false

bundle.addSymbol(name, data, path, flag); // This will create map and autorun automatically
bundle.removeSymbol(name); // This will remove map and autorun automatically

bundle.compile(options); // Returns the compiled bundle as string
bundle.print(options);
bundle.write(path, options);

nld.compress(code, options);

License

(The MIT License)

Copyright (C) 2012 Bastien Clément <g@ledric.me>

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 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.