Skip to content

Commit

Permalink
Add support for external modules (not to be bundled but used as UMD d…
Browse files Browse the repository at this point in the history
…eps).

* Added option for external modules hash with optional separate AMD / global names. Fixes #5.
* Add proper source map comparison (check whether all the mappings still work).
  • Loading branch information
RReverser committed Oct 10, 2014
1 parent f729114 commit 35fecd9
Show file tree
Hide file tree
Showing 15 changed files with 165 additions and 45 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ transform | `Array` / `Function(input)` | Transformation [through](https://githu
defaultExt | `String` | Default extension for requires | `"js"`
moduleDir | `String` | Modules directory name | `"node_modules"`
dryRun | `Boolean` | Don't write output to disk (and don't append `//# sourceMappingURL=...` to code) | `false`
external | `{ [CommonJS name]: (true / { amd?: String, global?: String }) }` | External dependencies (to be excluded from bundling). Each value can be either `true` (for same name across module systems) or hash with specific names;<br />example: `{jquery: true, lodash: {amd: '../vendor/lodash.js', global: '_'}}` | `{}`

### Result object

Expand Down
8 changes: 6 additions & 2 deletions lib/astConsts.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@ exports.preamble = astUtils.parse(fs.readFileSync(__dirname + '/templates/preamb
attachComment: true
}).body;

exports.umdWrapper = astUtils.parse(fs.readFileSync(__dirname + '/templates/umdWrapper.js'), {
var umdTmpl = astUtils.tmpl.compile(fs.readFileSync(__dirname + '/templates/umdWrapper.js', 'utf-8'), {
loc: true,
source: 'umdWrapper.js',
comment: true,
attachComment: true
}).body[0].expression;
});

exports.getUmdWrapper = function (deps) {
return umdTmpl({b: b, deps: deps}).body[0].expression;
};
5 changes: 4 additions & 1 deletion lib/astUtils.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
var parse = require('esprima').parse,
generate = require('escodegen').generate,
types = require('ast-types'),
estemplate = require('estemplate'),
traverse = types.traverse.fast;

exports.parse = parse;
Expand All @@ -9,4 +10,6 @@ exports.generate = generate;
exports.traverse = traverse;

exports.builders = types.builders;
exports.namedTypes = types.namedTypes;
exports.namedTypes = types.namedTypes;

exports.tmpl = estemplate;
4 changes: 2 additions & 2 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ exports.transform = function (inOptions) {
return map.whenAll().then(function (modules) {
var factoryExpr = b.functionExpression(
null,
astConsts.factoryArgs,
options.deps.map(function (dep) { return dep.id }).concat(astConsts.factoryArgs),
b.blockStatement(astConsts.preamble.concat([
b.expressionStatement(b.assignmentExpression(
'=',
Expand All @@ -31,7 +31,7 @@ exports.transform = function (inOptions) {
),
expr = (
options.exports
? b.callExpression(astConsts.umdWrapper, [b.literal(options.exports), factoryExpr])
? b.callExpression(astConsts.getUmdWrapper(options.deps), [b.literal(options.exports), factoryExpr])
: b.callExpression(factoryExpr, [])
),
result = astUtils.generate(b.program([b.expressionStatement(expr)]), {
Expand Down
14 changes: 14 additions & 0 deletions lib/parseOptions.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
var pathUtils = require('./pathUtils');
var b = require('./astUtils').builders;

function toValue(value) {
return value instanceof Function ? value.apply(null, Array.prototype.slice.call(arguments, 1)) : value;
Expand Down Expand Up @@ -36,5 +37,18 @@ module.exports = function (inOptions) {
options.comments = !!inOptions.comments;
options.dryRun = !!inOptions.dryRun;

options.deps = [];

for (var name in inOptions.external) {
var dep = inOptions.external[name];
if (dep === true) {
dep = {name: name};
}
dep.global = dep.global || dep.name.replace(/\W/g, '');
dep.amd = dep.amd || dep.name;
dep.id = b.identifier('__external_' + (dep.global || deps.length));
options.deps.push(dep);
}

return options;
};
52 changes: 30 additions & 22 deletions lib/replacer.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,36 @@ function Replacer(options) {
this.path = options.path;
this.refs = [];

var pipeline = this.map.transform.reduce(function (stream, transform) {
return stream.pipe(transform(options.path));
}, fs.createReadStream(this.path, {encoding: 'utf-8'}));

var defer = Promise.defer();

pipeline.pipe(es.wait(function (err, js) {
err ? defer.reject(err) : defer.fulfill(js);
}));

this.promise = defer.promise.then(function (js) {
var ast = astUtils.parse(js, {
loc: true,
source: this.path,
comment: options.comments,
attachComment: options.comments
});

this.visit(ast);

return b.functionExpression(null, astConsts.moduleArgs, b.blockStatement(ast.body));
}.bind(this));
if (options.externalId) {
this.promise = Promise.resolve([b.returnStatement(options.externalId)]);
} else {
var pipeline = this.map.transform.reduce(function (stream, transform) {
return stream.pipe(transform(options.path));
}, fs.createReadStream(this.path, {encoding: 'utf-8'}));

var defer = Promise.defer();

pipeline.pipe(es.wait(function (err, js) {
err ? defer.reject(err) : defer.fulfill(js);
}));

this.promise = defer.promise.then(function (js) {
var ast = astUtils.parse(js, {
loc: true,
source: this.path,
comment: options.comments,
attachComment: options.comments
});

this.visit(ast);

return ast.body;
}.bind(this));
}

this.promise = this.promise.then(function (body) {
return b.functionExpression(null, astConsts.moduleArgs, b.blockStatement(body));
});
}

Replacer.prototype.getDependency = function (path) {
Expand Down
10 changes: 8 additions & 2 deletions lib/replacerMap.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
var Promise = require('./promise'),
Replacer = require('./replacer');
Replacer = require('./replacer'),
pathUtils = require('./pathUtils');

function ReplacerMap(options) {
this.replacers = {};
Expand All @@ -8,6 +9,10 @@ function ReplacerMap(options) {
this.comments = options.comments;
this.defaultExt = options.defaultExt;
this.moduleDir = options.moduleDir;
this.depsMap = options.deps.reduce(function (obj, dep) {
obj[pathUtils.getNodePath('.', dep.name, this)] = dep;
return obj;
}.bind(this), {});
}

ReplacerMap.prototype.get = function (path) {
Expand All @@ -19,7 +24,8 @@ ReplacerMap.prototype.get = function (path) {
replacer = this.replacers[path] = new Replacer({
map: this,
path: path,
comments: this.comments
comments: this.comments,
externalId: (this.depsMap[path] || {}).id
});

this.promises[id] = replacer.promise;
Expand Down
12 changes: 9 additions & 3 deletions lib/templates/umdWrapper.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
(function (name, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define([], factory);
define(<%= b.arrayExpression(deps.map(function (dep) {
return b.literal(dep.amd);
})) %>, factory);
} else if (typeof exports === 'object') {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like enviroments that support module.exports,
// like Node.
module.exports = factory();
module.exports = <%= b.callExpression(b.identifier('factory'), deps.map(function (dep) {
return b.callExpression(b.identifier('require'), [b.literal(dep.name)]);
})) %>;
} else {
// Browser globals (root is window)
this[name] = factory();
this[name] = <%= b.callExpression(b.identifier('factory'), deps.map(function (dep) {
return b.memberExpression(b.thisExpression(), b.identifier(dep.global), false);
})) %>;
}
})
14 changes: 8 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "pure-cjs",
"version": "1.11.1",
"version": "1.12.0",
"description": "Pure minimalistic CommonJS builder",
"bin": "./bin/pure-cjs.js",
"main": "./lib/index.js",
Expand All @@ -12,15 +12,17 @@
"license": "MIT",
"dependencies": {
"ast-types": "^0.3.28",
"commander": "^2.2.0",
"commander": "^2.3.0",
"davy": "0.3.0",
"escodegen": "git+https://github.com/RReverser/escodegen.git",
"esprima": "^1.2.2",
"event-stream": "^3.1.5",
"resolve": "^0.7.0"
"estemplate": "^0.1.1",
"event-stream": "^3.1.7",
"resolve": "^1.0.0"
},
"devDependencies": {
"mocha": "^1.19.0",
"chai": "^1.9.1"
"chai": "^1.9.2",
"mocha": "^1.21.4",
"source-map": "^0.1.40"
}
}
49 changes: 49 additions & 0 deletions test/suites/a (exports A with externals)/expected.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions test/suites/a (exports A with externals)/expected.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions test/suites/a (exports A with externals)/options.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module.exports = {
input: 'fixtures/a.js',
exports: 'A',
map: true,
comments: true,
external: {
davy: true
}
};
Loading

0 comments on commit 35fecd9

Please sign in to comment.