aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard van Velzen <rvanvelzen1@gmail.com>2016-01-20 19:04:36 +0100
committerRichard van Velzen <rvanvelzen1@gmail.com>2016-01-20 19:04:36 +0100
commitb5a7197ae592a1ccdb217e74df4973d2a02a3a06 (patch)
tree70a36f264c9ba5992dcaeb2ccf7b75ec223759a0
parent26641f3fb20bce9394c3989bea0099dcd209be61 (diff)
parent1b703349cf824020c4dc64a58aa6d0dc3b809cea (diff)
downloadtracifyjs-b5a7197ae592a1ccdb217e74df4973d2a02a3a06.tar.gz
tracifyjs-b5a7197ae592a1ccdb217e74df4973d2a02a3a06.zip
Merge pull request #928 from STRML/constPragma
Mark vars with /** @const */ pragma as consts so they can be eliminated.
-rw-r--r--README.md6
-rw-r--r--lib/scope.js12
-rw-r--r--test/compress/dead-code.js117
3 files changed, 132 insertions, 3 deletions
diff --git a/README.md b/README.md
index 67324dbd..6dea439a 100644
--- a/README.md
+++ b/README.md
@@ -395,6 +395,8 @@ separate file and include it into the build. For example you can have a
```javascript
const DEBUG = false;
const PRODUCTION = true;
+// Alternative for environments that don't support `const`
+/** @const */ var STAGING = false;
// etc.
```
@@ -404,8 +406,8 @@ and build your code like this:
UglifyJS will notice the constants and, since they cannot be altered, it
will evaluate references to them to the value itself and drop unreachable
-code as usual. The possible downside of this approach is that the build
-will contain the `const` declarations.
+code as usual. The build will contain the `const` declarations if you use
+them. If you are targeting < ES6 environments, use `/** @const */ var`.
<a name="codegen-options"></a>
## Beautifier options
diff --git a/lib/scope.js b/lib/scope.js
index 5e93020f..4cea5176 100644
--- a/lib/scope.js
+++ b/lib/scope.js
@@ -94,6 +94,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
var scope = self.parent_scope = null;
var labels = new Dictionary();
var defun = null;
+ var last_var_had_const_pragma = false;
var nesting = 0;
var tw = new TreeWalker(function(node, descend){
if (options.screw_ie8 && node instanceof AST_Catch) {
@@ -151,10 +152,13 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
// later.
(node.scope = defun.parent_scope).def_function(node);
}
+ else if (node instanceof AST_Var) {
+ last_var_had_const_pragma = node.has_const_pragma();
+ }
else if (node instanceof AST_SymbolVar
|| node instanceof AST_SymbolConst) {
var def = defun.def_variable(node);
- def.constant = node instanceof AST_SymbolConst;
+ def.constant = node instanceof AST_SymbolConst || last_var_had_const_pragma;
def.init = tw.parent().value;
}
else if (node instanceof AST_SymbolCatch) {
@@ -357,6 +361,12 @@ AST_Symbol.DEFMETHOD("global", function(){
return this.definition().global;
});
+AST_Var.DEFMETHOD("has_const_pragma", function() {
+ var comments_before = this.start && this.start.comments_before;
+ var lastComment = comments_before && comments_before[comments_before.length - 1];
+ return lastComment && /@const\b/.test(lastComment.value);
+});
+
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
return defaults(options, {
except : [],
diff --git a/test/compress/dead-code.js b/test/compress/dead-code.js
index 5009ae1e..fa4b37d6 100644
--- a/test/compress/dead-code.js
+++ b/test/compress/dead-code.js
@@ -87,3 +87,120 @@ dead_code_constant_boolean_should_warn_more: {
var moo;
}
}
+
+dead_code_const_declaration: {
+ options = {
+ dead_code : true,
+ loops : true,
+ booleans : true,
+ conditionals : true,
+ evaluate : true
+ };
+ input: {
+ var unused;
+ const CONST_FOO = false;
+ if (CONST_FOO) {
+ console.log("unreachable");
+ var moo;
+ function bar() {}
+ }
+ }
+ expect: {
+ var unused;
+ const CONST_FOO = !1;
+ var moo;
+ function bar() {}
+ }
+}
+
+dead_code_const_annotation: {
+ options = {
+ dead_code : true,
+ loops : true,
+ booleans : true,
+ conditionals : true,
+ evaluate : true
+ };
+ input: {
+ var unused;
+ /** @const */ var CONST_FOO_ANN = false;
+ if (CONST_FOO_ANN) {
+ console.log("unreachable");
+ var moo;
+ function bar() {}
+ }
+ }
+ expect: {
+ var unused;
+ var CONST_FOO_ANN = !1;
+ var moo;
+ function bar() {}
+ }
+}
+
+dead_code_const_annotation_regex: {
+ options = {
+ dead_code : true,
+ loops : true,
+ booleans : true,
+ conditionals : true,
+ evaluate : true
+ };
+ input: {
+ var unused;
+ // @constraint this shouldn't be a constant
+ var CONST_FOO_ANN = false;
+ if (CONST_FOO_ANN) {
+ console.log("reachable");
+ }
+ }
+ expect: {
+ var unused;
+ var CONST_FOO_ANN = !1;
+ CONST_FOO_ANN && console.log('reachable');
+ }
+}
+
+dead_code_const_annotation_complex_scope: {
+ options = {
+ dead_code : true,
+ loops : true,
+ booleans : true,
+ conditionals : true,
+ evaluate : true
+ };
+ input: {
+ var unused_var;
+ /** @const */ var test = 'test';
+ // @const
+ var CONST_FOO_ANN = false;
+ var unused_var_2;
+ if (CONST_FOO_ANN) {
+ console.log("unreachable");
+ var moo;
+ function bar() {}
+ }
+ if (test === 'test') {
+ var beef = 'good';
+ /** @const */ var meat = 'beef';
+ var pork = 'bad';
+ if (meat === 'pork') {
+ console.log('also unreachable');
+ } else if (pork === 'good') {
+ console.log('reached, not const');
+ }
+ }
+ }
+ expect: {
+ var unused_var;
+ var test = 'test';
+ var CONST_FOO_ANN = !1;
+ var unused_var_2;
+ var moo;
+ function bar() {}
+ var beef = 'good';
+ var meat = 'beef';
+ var pork = 'bad';
+ 'good' === pork && console.log('reached, not const');
+ }
+}