Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor to use a disk-persistent cache. #84

Closed
wants to merge 31 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
81d504f
Refactor to use a disk-persistent cache.
tarruda Oct 2, 2013
000a9e5
Add --module-uids option.
tarruda Oct 2, 2013
6d6fd88
Refactored, now using string concatenation and
tarruda Oct 4, 2013
47a2782
Forked and updated package.json
tarruda Oct 4, 2013
a086a44
Removed coffeescript redux dependency, improved
tarruda Oct 4, 2013
105e10e
Enable usage of multiple entry points
tarruda Oct 4, 2013
6a88429
More refactoring, switch from esmangle to uglify
tarruda Oct 5, 2013
46fa7e2
Update escodegen version
tarruda Oct 5, 2013
182882b
Fixed all tests
tarruda Oct 5, 2013
e6777ef
Switched from make to grunt
tarruda Oct 5, 2013
b12cf40
Refactored options handling into a separate class
tarruda Oct 5, 2013
066833d
Implement grunt task
tarruda Oct 6, 2013
3ce875d
Re-add disk cache to command
tarruda Oct 6, 2013
58d54a4
By default ignore source maps for npm modules.
tarruda Oct 6, 2013
457c6db
Wrap bundle into umd
tarruda Oct 6, 2013
a0f4d20
Add support for loading in node.js environments
tarruda Oct 6, 2013
8be8895
Add support for global Buffer/process/setImmediate
tarruda Oct 7, 2013
cb3f627
Replace all module canonical paths by uids. When
tarruda Oct 7, 2013
c97453c
Add more options to the cli
tarruda Oct 7, 2013
20b9910
Refactor gruntfile to run tests from
tarruda Oct 7, 2013
9dca92d
Refactored so that the disk cache may be reused
tarruda Oct 7, 2013
938b996
Now considering 'sourceMappingURL' comments to set
tarruda Oct 8, 2013
bbb7028
Fixed cache bug due to incurrect uid persistence
tarruda Oct 8, 2013
d8f5ea2
Updated README and changed package name
tarruda Oct 8, 2013
274fe1e
Fixed source maps of files generated with handlers
tarruda Oct 8, 2013
70ac3f5
Switched to 'browser-builtins' module for core
tarruda Oct 8, 2013
3e781cd
Add test shim for 'freelist' and fixed tests
tarruda Oct 8, 2013
77acfc9
Add detection for console
tarruda Oct 8, 2013
5553a04
Disable UglifyJS messages about mappings
tarruda Oct 8, 2013
4eff2dc
Reset package version
tarruda Oct 8, 2013
0ef2c97
release 0.0.1
tarruda Oct 8, 2013
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
commonjs-everywhere-*.tgz
test/fixtures/*
test/fixtures/*
/lib/
tests.js
tests.js.map
powerbuild-cache
3 changes: 0 additions & 3 deletions .gitmodules

This file was deleted.

2 changes: 1 addition & 1 deletion .npmignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
commonjs-everywhere-*.tgz
node
node/*
!node/lib
src
test
.gitmodules
Makefile
powerbuild-cache
68 changes: 68 additions & 0 deletions Gruntfile.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
coffee = require 'coffee-script'

module.exports = (grunt) ->

grunt.initConfig
clean:
all: ['build']

coffee:
options:
sourceMap: true
all:
src: '*.coffee'
dest: 'lib'
cwd: 'src'
flatten: true
expand: true
ext: '.js'

powerbuild:
options:
sourceMap: true
node: false
handlers:
'.coffee': (src, canonicalName) ->
{js, v3SourceMap} = coffee.compile src, sourceMap: true, bare: true
return {code: js, map: v3SourceMap}

all:
files: [
{src: ['test-setup.coffee', 'test/*.coffee'], dest: 'tests.js'}
]

mocha_debug:
options:
ui: 'tdd'
reporter: 'dot'
check: ['test-setup.coffee', 'src/*.coffee', 'test/*.coffee']
all:
options:
src: ['tests.js']

watch:
options:
nospawn: true
all:
files: [
'test-setup.coffee'
'Gruntfile.coffee'
'tasks/*.coffee'
'src/*.coffee'
'test/*.coffee'
]
tasks: [
'test'
]


grunt.loadTasks('tasks')

grunt.loadNpmTasks('grunt-release')
grunt.loadNpmTasks('grunt-mocha-debug')
grunt.loadNpmTasks('grunt-contrib-watch')
grunt.loadNpmTasks('grunt-contrib-coffee')
grunt.loadNpmTasks('grunt-newer')

grunt.registerTask('test', ['newer:coffee', 'powerbuild', 'mocha_debug'])
grunt.registerTask('default', ['test', 'watch'])
48 changes: 0 additions & 48 deletions Makefile

This file was deleted.

181 changes: 135 additions & 46 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,75 @@
# CommonJS Everywhere
# Powerbuild

> CommonJS bundler with aliasing, extensibility, and source maps from the minified JS bundle. Forked from commonjs-everywhere adding speed improvements, persistent disk cache for incremental builds, support for reading '// [#@] sourceMappingURL' from input files and bundled grunt task

## Main changes from commonjs-everywhere

- Escodegen is only used when generating partial source maps, the final
result is computed manually.
- For minification esmangle/escodegen is replaced by UglifyJS for two
reasons:
* It was breaking in some of my tests
* It is 10x slower than UglifyJS. For a bundle with 50k lines of code
UglifyJS took about 4 seconds versus 35 seconds from esmangle/escodegen)
- Dependency on coffee-script-redux was removed. While its still possible
to use the 'handlers' option to build compile-to-js languages directly,
this tool now reads '// @sourceMappingURL' tags from the end of the file
in order to map correctly to the original files. This means any
compile-to-js language that produces source maps is supported out-of-box.
- By default, source maps for npm dependencies are not included.
- Module paths are replaced by unique identifiers, which leads to a small
improvement in the resulting size. When the __filename or __dirname
variables are used, a mapping for that module uid to the filename will
be used.
- Multiple entry points can be specified, with the last being exported
if the 'export' option is used. This can be used to create test bundles.
- The result is wrapped into [UMD](https://github.com/umdjs/umd).
- If the 'node' option is unset, it will disable node.js emulation and
inclusion of core modules. The result can be loaded as a normal node.js
module.

CommonJS (node module) browser bundler with source maps from the minified JS bundle to the original source, aliasing for browser overrides, and extensibility for arbitrary compile-to-JS language support.

## Install

npm install -g commonjs-everywhere
npm install -g powerbuild

## Usage

### CLI

$ bin/cjsify --help
$ bin/powerbuild --help

Usage: cjsify OPT* path/to/entry-file.ext OPT*
Usage: powerbuild OPT* ENTRY_FILE+ OPT*

-a, --alias ALIAS:TO replace requires of file identified by ALIAS with TO
-h, --handler EXT:MODULE handle files with extension EXT with module MODULE
-m, --minify minify output
-m, --minify minify output using uglify.js
-c, --compress Compress/optimize code when minifying
(automatically enabled by this option). Enabling
will break the generated source map.
-o, --output FILE output to FILE instead of stdout
-r, --root DIR unqualified requires are relative to DIR; default: cwd
-s, --source-map FILE output a source map to FILE
-v, --verbose verbose output sent to stderr
-w, --watch watch input files/dependencies for changes and rebuild bundle
-x, --export NAME export the given entry module as NAME
-x, --export NAME export the last given entry module as NAME
--deps do not bundle; just list the files that would be bundled
--help display this help message and exit
--ignore-missing continue without error when dependency resolution fails
--inline-source-map include the source map as a data URI in the generated bundle
--inline-sources include source content in generated source maps; default: on
--node include process object; emulate node environment; default: on
--inline-sources include source content in generated source maps
--node if needed by any module, emulate a node.js
environment by including globals such as Buffer,
process and setImmediate; default: on
--cache-path file where to read/write a json-encoded cache that
is used for fast, incremental builds.
default: '.powerbuild-cache~' in the current
directory
--disable-disk-cache disables persistence of incremental build cache
to disk. Incremental build will only work with the
--watch option
--npm-source-maps add mappings for npm modules in the resulting
source map(significantly increases the build time)
--version display the version number and exit

*Note:* use `-` as an entry file to accept JavaScript over stdin
Expand All @@ -37,38 +78,96 @@ CommonJS (node module) browser bundler with source maps from the minified JS bun

#### Example:

Common usage
Common usage, a single entry point which will be used to build the entire
dependency graph. Whatever is exported by 'entry-file.js' will go to the
global property 'MyLibrary':

```bash
cjsify src/entry-file.js --export MyLibrary --source-map my-library.js.map >my-library.js
powerbuild src/entry-file.js --export MyLibrary --source-map my-library.js.map >my-library.js
```

Watch entry file, its dependencies, and even newly added dependencies. Notice that only the files that need to be rebuilt are accessed when one of the watched dependencies are touched. This is a much more efficient approach than simply rebuilding everything.
Specify multiple entry points which will be "required" at startup. Only
the last entry point will be exported when used in conjunction with the
'--export' option. This is mostly useful for building test bundles which
can be referenced from a single 'script' tag

```bash
cjsify -wo my-library.js -x MyLibrary src/entry-file.js
powerbuild test/*.js --source-map tests.js.map -o tests.js
```

Use a browser-specific version of `/lib/node-compatible.js` (remember to use `root`-relative paths for aliasing). An empty alias target is used to delay errors to runtime when requiring the source module (`fs` in this case).
Watch every file in the dependency graph and rebuild when a file changes.
Unlike commonjs-everywhere, this tool caches partial builds to disk, so this
is not necessary for incremental builds.

```bash
cjsify -a /lib/node-compatible.js:/lib/browser-compatible.js -a fs: -x MyLibrary lib/entry-file.js
powerbuild -wo my-library.js -x MyLibrary src/entry-file.js
```

### Module Interface
Use a browser-specific version of `/lib/node-compatible.js` (remember to use
`root`-relative paths for aliasing). An empty alias target is used to delay
errors to runtime when requiring the source module (`fs` in this case). The
'browser' field in package.json will also be used if available when building
bundles with node.js emulation(which is the default).

#### `cjsify(entryPoint, root, options)` → Spidermonkey AST
Bundles the given file and its dependencies; returns a Spidermonkey AST representation of the bundle. Run the AST through `escodegen` to generate JS code.
```bash
powerbuild -a /lib/node-compatible.js:/lib/browser-compatible.js -a fs: -x MyLibrary lib/entry-file.js
```

* `entryPoint` is a file relative to `process.cwd()` that will be the initial module marked for inclusion in the bundle as well as the exported module
* `root` is the directory to which unqualified requires are relative; defaults to `process.cwd()`
* `options` is an optional object (defaulting to `{}`) with zero or more of the following properties
* `export`: a variable name to add to the global scope; assigned the exported object from the `entryPoint` module. Any valid [Left-Hand-Side Expression](http://es5.github.com/#x11.2) may be given instead.
* `aliases`: an object whose keys and values are `root`-rooted paths (`/src/file.js`), representing values that will replace requires that resolve to the associated keys
* `handlers`: an object whose keys are file extensions (`'.roy'`) and whose values are functions from the file contents to either a Spidermonkey-format JS AST like the one esprima produces or a string of JS. Handlers for CoffeeScript and JSON are included by default. If no handler is defined for a file extension, it is assumed to be JavaScript.
* `node`: a falsey value causes the bundling phase to omit the `process` stub that emulates a node environment
* `verbose`: log additional operational information to stderr
* `ignoreMissing`: continue without error when dependency resolution fails
### Module Interface

#### `new Powerbuild(options)`
Constructor for an object that can keeps track of build options and is used to
trigger incremental rebuilds.

* `options` is an object that can contain the following properties:
* `entryPoints` is an array of filenames relative to `process.cwd()` that
will be used to initialize the bundle. The last item in this array will
also be used when the 'export' option is specified
* `root`: Same as cli.
* `export`: Same as cli.
* `aliases`: an object whose keys and values are `root`-rooted paths
(`/src/file.js`), representing values that will replace requires that
resolve to the associated keys
* `handlers`: an object whose keys are file extensions (`'.coffee'`) and
whose values are functions that receives the file contents as arguments
and returns one of the following:
- Spidermonkey-format JS AST like the one esprima produces
- A string of javascript
- An object with the keys 'code' and 'map' containing strings with
javascript and sourcemaps respectively.
A handler for JSON is included by default. If no handler is defined for
a file extension, it is assumed to be JavaScript. (The default
coffeescript-redux handler was removed because this tool now reads
'// @sourceMappingURL' comment tags, so it can be used in conjunction
with the default coffeescript compiler)
* `node`: Same as cli. When true(default) the bundling phase will emit
globals for 'process', 'Buffer' or 'setImmediate' if any of those are
used by any of the bundled modules. Setting this to false will completely
disable node.js emulation, excluding core node.js modules(path, util...)
from the bundle. This may be used to create bundles targeted at node.js.
(While this will not be a very common case, it can be used for example
to distribute node.js apps as a single javascript file containing all
dependencies).
* `verbose`: Same as cli.
* `ignoreMissing`: Same as cli.
* `minify`: Same as cli.
* `compress`: Same as cli.
* `output`: Name of the output file. The file will not be written, this
is used when building the source map.
* `sourceMap`: Same as cli. This may be true to make the source map have
the same name as 'output' with '.map' appended.
* `inlineSourceMap`: Same as cli.
* `inlineSources`: Same as cli.
* `npmSourceMaps`: Same as cli. This is disabled by default because
it greatly increases build efficiency and normally you wont care about
debugging external modules.


### Grunt task

This package includes a grunt task that takes any of the API or cli options(
with dashes removed and converted to camelCase). For an example see this
package's [test bundle configuration]()

## Examples

Expand All @@ -89,29 +188,19 @@ Say we have the following directory tree:
Running the following command will export `index.coffee` and its dependencies as `App.Todos`.

```
cjsify -o public/javascripts/app.js -x App.Todos -r components components/todos/index.coffee
powerbuild -o public/javascripts/app.js -x App.Todos -r components components/todos/index.coffee
```

Since the above command specifies `components` as the root directory for unqualified requires, we are able to require `components/users/model.coffee` with `require 'users/model'`. The output file will be `public/javascripts/app.js`.
Since the above command specifies `components` as the root directory for
unqualified requires, we are able to require `components/users/model.coffee`
with `require 'users/model'`. The output file will be
`public/javascripts/app.js`.

### Node Module Example

```coffee
jsAst = (require 'commonjs-everywhere').cjsify 'src/entry-file.coffee', __dirname,
export: 'MyLibrary'
aliases:
'/src/module-that-only-works-in-node.coffee': '/src/module-that-does-the-same-thing-in-the-browser.coffee'
handlers:
'.roy': (roySource, filename) ->
# the Roy compiler outputs JS code right now, so we parse it with esprima
(require 'esprima').parse (require 'roy').compile roySource, {filename}

{map, code} = (require 'escodegen').generate jsAst,
sourceMapRoot: __dirname
sourceMapWithCode: true
sourceMap: true
opts.root = 'components'
opts.entryPoints = ['index.coffee']
powerbuild = new Powerbuild opts
{code, map} = powerbuild.bundle()
```

### Sample Output

![](http://i.imgur.com/oDcQh8H.png)
File renamed without changes.
16 changes: 0 additions & 16 deletions changelog.sh

This file was deleted.

Loading