UNPKG

3.31 kBJavaScriptView Raw
1"use strict";
2
3const t = require('@babel/types');
4
5const EXPORTS_RE = /^\$([^$]+)\$exports$/;
6/**
7 * This is a small small implementation of dead code removal specialized to handle
8 * removing unused exports. All other dead code removal happens in workers on each
9 * individual file by babel-minify.
10 */
11
12function treeShake(scope) {
13 // Keep passing over all bindings in the scope until we don't remove any.
14 // This handles cases where we remove one binding which had a reference to
15 // another one. That one will get removed in the next pass if it is now unreferenced.
16 let removed;
17
18 do {
19 removed = false; // Recrawl to get all bindings.
20
21 scope.crawl();
22 Object.keys(scope.bindings).forEach(name => {
23 let binding = getUnusedBinding(scope.path, name); // If it is not safe to remove the binding don't touch it.
24
25 if (!binding) {
26 return;
27 } // Remove the binding and all references to it.
28
29
30 binding.path.remove();
31 binding.referencePaths.concat(binding.constantViolations).forEach(remove);
32 scope.removeBinding(name);
33 removed = true;
34 });
35 } while (removed);
36}
37
38module.exports = treeShake; // Check if a binding is safe to remove and returns it if it is.
39
40function getUnusedBinding(path, name) {
41 let binding = path.scope.getBinding(name);
42
43 if (!binding) {
44 return null;
45 }
46
47 if (isPure(binding)) {
48 return binding;
49 }
50
51 if (!EXPORTS_RE.test(name)) {
52 return null;
53 } // Is there any references which aren't simple assignments?
54
55
56 let bailout = binding.referencePaths.some(path => !isExportAssignment(path) && !isUnusedWildcard(path));
57
58 if (bailout) {
59 return null;
60 } else {
61 return binding;
62 }
63}
64
65function isPure(binding) {
66 if (binding.referenced) {
67 return false;
68 }
69
70 if (binding.path.isVariableDeclarator() && binding.path.get('id').isIdentifier()) {
71 let init = binding.path.get('init');
72 return init.isPure() || init.isIdentifier() || init.isThisExpression();
73 }
74
75 return binding.path.isPure();
76}
77
78function isExportAssignment(path) {
79 return (// match "path.any = any;"
80 path.parentPath.isMemberExpression() && path.parentPath.parentPath.isAssignmentExpression() && path.parentPath.parentPath.node.left === path.parentPath.node
81 );
82}
83
84function isUnusedWildcard(path) {
85 let parent = path.parent;
86 return (// match `$parcel$exportWildcard` calls
87 t.isCallExpression(parent) && t.isIdentifier(parent.callee, {
88 name: '$parcel$exportWildcard'
89 }) && parent.arguments[0] === path.node && // check if the $id$exports variable is used
90 !getUnusedBinding(path, parent.arguments[1].name)
91 );
92}
93
94function remove(path) {
95 if (path.isAssignmentExpression()) {
96 if (path.parentPath.isSequenceExpression()) {
97 if (path.parent.expressions.length == 1) {
98 // replace sequence expression with it's sole child
99 path.parentPath.replaceWith(path);
100 remove(path.parentPath);
101 } else {
102 path.remove();
103 }
104 } else if (!path.parentPath.isExpressionStatement()) {
105 path.replaceWith(path.node.right);
106 } else {
107 path.remove();
108 }
109 } else if (isExportAssignment(path)) {
110 remove(path.parentPath.parentPath);
111 } else if (isUnusedWildcard(path)) {
112 remove(path.parentPath);
113 } else if (!path.removed) {
114 path.remove();
115 }
116}
\No newline at end of file