Skip to content

Commit 47f252b

Browse files
authored
Add support for optional dependencies (#788)
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.
1 parent 13864d1 commit 47f252b

5 files changed

Lines changed: 38 additions & 4 deletions

File tree

src/Bundler.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,10 @@ class Bundler extends EventEmitter {
328328
let thrown = err;
329329

330330
if (thrown.message.indexOf(`Cannot find module '${dep.name}'`) === 0) {
331+
if (dep.optional) {
332+
return;
333+
}
334+
331335
thrown.message = `Cannot resolve dependency '${dep.name}'`;
332336

333337
// Add absolute path to the error message if the dependency specifies a relative path
@@ -403,7 +407,10 @@ class Bundler extends EventEmitter {
403407
this.watch(dep.name, asset);
404408
} else {
405409
let assetDep = await this.resolveDep(asset, dep);
406-
await this.loadAsset(assetDep);
410+
if (assetDep) {
411+
await this.loadAsset(assetDep);
412+
}
413+
407414
return assetDep;
408415
}
409416
})

src/assets/JSAsset.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ class JSAsset extends Asset {
8787
}
8888

8989
collectDependencies() {
90-
this.traverseFast(collectDependencies);
90+
walk.ancestor(this.ast, collectDependencies, this);
9191
}
9292

9393
async pretransform() {

src/visitors/dependencies.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ module.exports = {
3030
asset.isES6Module = true;
3131
},
3232

33-
CallExpression(node, asset) {
33+
CallExpression(node, asset, ancestors) {
3434
let {callee, arguments: args} = node;
3535

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

4242
if (isRequire) {
43-
addDependency(asset, args[0]);
43+
let optional = ancestors.some(a => types.isTryStatement(a)) || undefined;
44+
addDependency(asset, args[0], {optional});
4445
return;
4546
}
4647

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
try {
2+
require('optional-dep');
3+
} catch (err) {
4+
module.exports = err;
5+
}

test/javascript.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,4 +676,25 @@ describe('javascript', function() {
676676
let file = fs.readFileSync(__dirname + '/dist/index.js', 'utf8');
677677
assert(file.includes('h("div"'));
678678
});
679+
680+
it('should support optional dependencies in try...catch blocks', async function() {
681+
let b = await bundle(__dirname + '/integration/optional-dep/index.js');
682+
683+
assertBundleTree(b, {
684+
name: 'index.js',
685+
assets: ['index.js'],
686+
childBundles: [
687+
{
688+
type: 'map'
689+
}
690+
]
691+
});
692+
693+
let output = run(b);
694+
695+
let err = new Error('Cannot find module "optional-dep"');
696+
err.code = 'MODULE_NOT_FOUND';
697+
698+
assert.deepEqual(output, err);
699+
});
679700
});

0 commit comments

Comments
 (0)