Todo

More features

Better externals

Better externals handling: Sometimes you want to have granular control over what external modules are included in your build. The following options let you do that:

--external <name>: specifies that that module should not be bundled but instead looked up from the global context. For example, --external underscore specifies that the underscore module should not be included in the build result. Any references to underscore will be ignored.

--remap <name>=<expression>: specifies that require(name) should return whatever the value of the expression is. This is useful for remapping external modules. For example you might want to load jQuery externally from window.$ using --remap jquery=window.$.

--no-externals: this option prevents any modules from under node_modules from being included.

--include-external <name>: this option adds an external back after --no-externals (whitelist approach).

--only-externals: this option only bundles modules under node_modules.

Middleware enhancements

Switching between development and production modes:

Example:

if (development) {
  app.use();
} else {
  glue.package();
  app.use(express.static(outDir));
}

Shimming non-commonJS libraries that export globals

Interoperability with libraries that export globals.

Two options:

--shim { file: "", name: "", global: "", deps: "" }.

This wraps the file in a way that the global variable is available as require(name).

-> this is basically --remap name=require(file) --no-parse file --append-text file "module.exports = global;"

Misc

Mocking out dependencies for testing: This is probably more useful when used via the Express middleware, but --remap name=path allows specific externals to be replaced, which can be used for testing:

// example

Might be nice to make this even easier to use from tests... via REST API?

Inclusion optimization

Replacing modules or individual files

Substituting a module. To replace a module with a different module in gluejs, use the remap option:

remap: { "underscore": "require('lodash')" }

via the command line, this would be written as --remap underscore="require('lodash')".

Substituting a file. The browser field in package.json is a new addition which allows CommonJS bundlers to replace files which are not compatible with the browser with alternatives provided by the module. You can use this to replace files in your build, and it can also be used by 3rd party modules which support both the browser and Node.

You can replace the whole package with a specific file:

"browser": "dist/browser.js"

or you can override individual modules and files in package.json:

"browser": {
  "fs": "level-fs",
  "./lib/filters.js": "./lib/filters-client.js"
},

This will replace ./lib/filters.js with ./lib/filters-client.js in the build result.

Core variable and core module shimming

Evaluation

New architecture

Cleaner separation between Map and Reduce phases.

During the Map phase, a number of tasks are applied to each file.

The tasks take one input, run it through transforms, and write a file into cache. If nothing needs to be done, then a simple direct-read tuple is created.

The Map phase uses a shared queue which supports incremental task queueing.

During the reduce phase, the list of metadata tuples is converted into a set of packages using package inference. Then, the underlying data for each metadata tuple is read in serial order and written to the final output. On read, the final wrapping is done, utilizing the inferred packages.

If any tasks require the package metadata, then they must be performed during the reduce phase. In theory one could add another map phase after packages have been inferred.

-- check for full build match --

Transform queue (transforms/index.js):

[ Initialize queue ]
[ Add new file ]
  [ Check that file has not been processed ]
  [ Check for cached results, and return if done ]
  [ Apply user and other exclusions ]
  [ Queue task ]
[ Task run]
  [ Match tasks ]
  [ If no tasks, just run the parse-result-and-update-deps ]
    [ Push deps to queue when done ]
  [ If transformations, append parse-result-and-update-deps task ]
  [ Run transforms ]
    [ Push deps to queue when done ]
[ Start running the queue ]
  [ Once done ]
    [ Check queue for more, assign if under parallelism limit ]

{ filename: path-to-file, content: path-to-file, deps: [ "str", "str2" ] }
{ filename: "original-path-to-file", content: path-to-result-file, deps: [ "str", "str2" ] }

-- generate joinable list --

Queue tasks:

in a generic way by defining a bunch of callbacks, rather than doing these each in ugly and ad-hoc ways.

--shim:

--no-externals:

{
  expr: '*'
  type: 'package-filter',
  task: function() {
    return false;
  }
}

--only-externals:

{
  expr: base + '/**',
  phase: 'file-filter',
  task: function() {
    return false;
  }
}

--ignore:

argv.ignore.toExpr()

{
  expr: file paths,
  phase: 'file-filter',
  task: function() {
    // no need to parse the file since it's always an empty file
    self.addResult(filename, self.ignoreFile, [], [], []);
    // queue has been updated, finish this task
    self.emit('miss', filename);
    return false;
  }
}
{
  expr: packages,
  phase: 'package-filter',
  task: function() {
    return false;
  }
}

TODO:

Package generator queue (commonjs2/index.js):

[ Infer packages ]
[ Infer package deps ]
  - for --parse: just collect
  - for --no-parse: guess (e.g. modules in folders at higher levels, and one-level-removed child node_modules)

[ Update reporter size during read ]
[ Wrap file during read (w/ full meta?) ]


{
  id: ..,
  main: "original-name",
  basepath: ...,
  files: [ ... ],
  deps: { "name": "target" }
}

[ Join files ]

-- generate full build --

TODO:

use detective and amdetective

Steps:

Test cases:

Tiny module optimizations

Implicit global support

Docs todo

--cache-clean

TODO

--cache-clean: Clears the global cache folder by deleting it. This is always done before processing anything else.

Known issues

How do I ...?

How do I package tests for the browser?

There is a builtin Mocha test server task, which takes a set of tests as input and creates a server specifically for running one or more tests.

If you're not using Mocha, you can still use the API to create a package and serve it (with test-framework wrapping).