Skip to content

Commit

Permalink
Add support for optional dependencies (#788)
Browse files Browse the repository at this point in the history
Put a `require` call inside a try…catch block, and it becomes optional. If that dependency cannot be resolved, it will throw a runtime error instead of a build time one.
  • Loading branch information
devongovett authored Feb 12, 2018
1 parent 13864d1 commit 47f252b
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 4 deletions.
9 changes: 8 additions & 1 deletion src/Bundler.js
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,10 @@ class Bundler extends EventEmitter {
let thrown = err;

if (thrown.message.indexOf(`Cannot find module '${dep.name}'`) === 0) {
if (dep.optional) {
return;
}

thrown.message = `Cannot resolve dependency '${dep.name}'`;

// Add absolute path to the error message if the dependency specifies a relative path
Expand Down Expand Up @@ -403,7 +407,10 @@ class Bundler extends EventEmitter {
this.watch(dep.name, asset);
} else {
let assetDep = await this.resolveDep(asset, dep);
await this.loadAsset(assetDep);
if (assetDep) {
await this.loadAsset(assetDep);
}

return assetDep;
}
})
Expand Down
2 changes: 1 addition & 1 deletion src/assets/JSAsset.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ class JSAsset extends Asset {
}

collectDependencies() {
this.traverseFast(collectDependencies);
walk.ancestor(this.ast, collectDependencies, this);
}

async pretransform() {
Expand Down
5 changes: 3 additions & 2 deletions src/visitors/dependencies.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ module.exports = {
asset.isES6Module = true;
},

CallExpression(node, asset) {
CallExpression(node, asset, ancestors) {
let {callee, arguments: args} = node;

let isRequire =
Expand All @@ -40,7 +40,8 @@ module.exports = {
types.isStringLiteral(args[0]);

if (isRequire) {
addDependency(asset, args[0]);
let optional = ancestors.some(a => types.isTryStatement(a)) || undefined;
addDependency(asset, args[0], {optional});
return;
}

Expand Down
5 changes: 5 additions & 0 deletions test/integration/optional-dep/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
try {
require('optional-dep');
} catch (err) {
module.exports = err;
}
21 changes: 21 additions & 0 deletions test/javascript.js
Original file line number Diff line number Diff line change
Expand Up @@ -676,4 +676,25 @@ describe('javascript', function() {
let file = fs.readFileSync(__dirname + '/dist/index.js', 'utf8');
assert(file.includes('h("div"'));
});

it('should support optional dependencies in try...catch blocks', async function() {
let b = await bundle(__dirname + '/integration/optional-dep/index.js');

assertBundleTree(b, {
name: 'index.js',
assets: ['index.js'],
childBundles: [
{
type: 'map'
}
]
});

let output = run(b);

let err = new Error('Cannot find module "optional-dep"');
err.code = 'MODULE_NOT_FOUND';

assert.deepEqual(output, err);
});
});

0 comments on commit 47f252b

Please sign in to comment.