Skip to content

Commit dea672b

Browse files
committed
Entirely remove side-effect-free unused modules, closes #28
1 parent 1034d3f commit dea672b

File tree

5 files changed

+93
-19
lines changed

5 files changed

+93
-19
lines changed

index.js

Lines changed: 71 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict'
2-
const relative = require('path').relative
2+
const path = require('path')
33
const Analyzer = require('@goto-bus-stop/common-shake').Analyzer
4+
const evaluateConst = require('@goto-bus-stop/common-shake').evaluateConst
45
const transformAst = require('transform-ast')
56
const wrapComment = require('wrap-comment')
67
const through = require('through2')
@@ -17,9 +18,9 @@ module.exports = function commonShake (b, opts) {
1718
const seen = {}
1819
opts = Object.assign({
1920
verbose: false,
20-
onExportDelete (path, name) {
21+
onExportDelete (source, name) {
2122
if (opts.verbose || opts.v) {
22-
console.warn('common-shake: removed', `\`${name}\``, 'in', relative(basedir, path))
23+
console.warn('common-shake: removed', `\`${name}\``, 'in', path.relative(basedir, source))
2324
}
2425
},
2526
onModuleBailout (resource, reasons) {
@@ -29,15 +30,15 @@ module.exports = function commonShake (b, opts) {
2930
seen[resource.resource + reason.reason] = true
3031
const loc = reason.loc.start
3132
const source = reason.source || resource.resource
32-
console.warn('common-shake: bailed out: ', reason.reason, 'in', `${relative(basedir, source)}:${loc.line}:${loc.column}`)
33+
console.warn('common-shake: bailed out: ', reason.reason, 'in', `${path.relative(basedir, source)}:${loc.line}:${loc.column}`)
3334
})
3435
}
3536
},
3637
onGlobalBailout (reasons) {
3738
if (opts.verbose || opts.v) {
3839
reasons.forEach((reason) => {
3940
const loc = reason.loc.start
40-
console.warn('common-shake: GLOBAL BAILOUT:', reason.reason, 'in', `${relative(basedir, reason.source)}:${loc.line}:${loc.column}`)
41+
console.warn('common-shake: GLOBAL BAILOUT:', reason.reason, 'in', `${path.relative(basedir, reason.source)}:${loc.line}:${loc.column}`)
4142
})
4243
}
4344
}
@@ -48,11 +49,29 @@ module.exports = function commonShake (b, opts) {
4849
addHooks()
4950
b.on('reset', addHooks)
5051
function addHooks () {
51-
b.pipeline.get('label').unshift(createStream(opts))
52+
const packages = new Map()
53+
const aliases = new Map()
54+
b._mdeps.on('package', (pkg) => {
55+
packages.set(pkg.__dirname, pkg)
56+
})
57+
b._mdeps.on('file', (file, id) => {
58+
aliases.set(id, file)
59+
})
60+
b.pipeline.get('label').unshift(createStream(opts, {
61+
getSideEffects(name) {
62+
const file = aliases.get(name) || name
63+
let pkg
64+
let dir = file
65+
while (!pkg && (dir = path.dirname(dir))) {
66+
pkg = packages.get(dir)
67+
}
68+
return pkg && pkg.sideEffects === false ? false : true
69+
}
70+
}))
5271
}
5372
}
5473

55-
function createStream (opts) {
74+
function createStream (opts, api) {
5675
const analyzer = new Analyzer()
5776

5877
const rows = new Map()
@@ -87,7 +106,9 @@ function createStream (opts) {
87106
}, (node) => {
88107
if (node.type === 'Program') ast = node
89108
})
90-
analyzer.run(ast, index)
109+
analyzer.run(ast, index, {
110+
sideEffects: api.getSideEffects(row.file)
111+
})
91112

92113
Object.keys(row.indexDeps).forEach((name) => {
93114
if (row.indexDeps[name]) {
@@ -124,13 +145,15 @@ function createStream (opts) {
124145
// If this module was a duplicate of another module,
125146
// the original module will have been rewritten already.
126147
if (row.dedupe) {
127-
this.push(row)
128148
return
129149
}
130150

131151
if (module.bailouts) {
132152
opts.onModuleBailout(module, module.bailouts)
133-
this.push(row)
153+
return
154+
}
155+
156+
if (module.getInfo().removeImport) {
134157
return
135158
}
136159

@@ -142,15 +165,6 @@ function createStream (opts) {
142165
}
143166
})
144167

145-
const transformed = string.toString()
146-
if (opts.sourceMap) {
147-
row.source = transformed + '\n' + convertSourceMap.fromObject(string.map).toComment()
148-
} else {
149-
row.source = transformed
150-
}
151-
152-
this.push(row)
153-
154168
// Check if a name was used in this module, or
155169
// in any of this module's deduped versions.
156170
function isUsed (name) {
@@ -167,6 +181,44 @@ function createStream (opts) {
167181
}
168182
})
169183

184+
rows.forEach((row, index) => {
185+
const module = analyzer.getModule(index)
186+
if (module && module.getInfo().removeImport) {
187+
return
188+
}
189+
190+
if (row.dedupe || module.bailouts) {
191+
this.push(row)
192+
return
193+
}
194+
195+
const string = strings.get(index)
196+
197+
Object.keys(row.indexDeps).forEach((depName) => {
198+
const depModule = analyzer.getModule(row.indexDeps[depName])
199+
if (depModule && depModule.getInfo().removeImport) {
200+
delete row.indexDeps[depName]
201+
delete row.deps[depName]
202+
const imports = module.requireNodes.get(depName) || []
203+
imports.forEach((node) => {
204+
// We can replace this with `undefined` because this will not be a .property.
205+
// If it was a require('mod').property, the .property would be marked as used,
206+
// and the module's import would not be removable
207+
node.edit.update(`void 0 ${wrapComment(node.getSource())}`)
208+
})
209+
}
210+
})
211+
212+
const transformed = string.toString()
213+
if (opts.sourceMap) {
214+
row.source = transformed + '\n' + convertSourceMap.fromObject(string.map).toComment()
215+
} else {
216+
row.source = transformed
217+
}
218+
219+
this.push(row)
220+
})
221+
170222
next()
171223
}
172224

test/side-effects/app.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
var dep = require('./dep')
2+
3+
console.log(dep.three())

test/side-effects/dep.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
var _ = require('functional')
2+
3+
exports.two = function () { return _.addOne(1) }
4+
exports.three = function () { return 3 }

test/side-effects/expected.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
2+
var dep = require('./dep')
3+
4+
console.log(dep.three())
5+
6+
},{"./dep":2}],2:[function(require,module,exports){
7+
var _ = void 0 /* require('functional') */
8+
9+
/* common-shake removed: exports.two = */ void function () { return _.addOne(1) }
10+
exports.three = function () { return 3 }
11+
12+
},{}]},{},[1]);

test/test.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ test('exclude', function (t) {
6969
test('paren-exports', function (t) {
7070
runTest(t, 'paren-exports')
7171
})
72+
test('side-effects', function (t) {
73+
runTest(t, 'side-effects')
74+
})
7275

7376
test('external', function (t) {
7477
var b = browserify({

0 commit comments

Comments
 (0)