Repository: babel/minify (js)

Issue:
constant-folding template strings
There is a bug in the [babel-plugin-minify-constant-folding](https://github.com/babel/babili/tree/master/packages/babel-plugin-minify-constant-folding) package where it incorrectly inlines template strings. Here is a simple example:

```
const a = ["a", "b"].join(' ');
// -> const a="a b";


const b = [`a${a}`, `b${a}`].join(' ');
// -> const b=" ";
```

In the second example it should keep the interpolation and produce a template string, but it does not, instead returning just an empty string. The result is broken code.

Pull request description:
constant-folding: Fix some literal types used in `.join()`, closes #612
The `.join()` inlining function was checking for `isLiteral()` on each array element, and then using the element's `.value`. Some literals don't have `.value`s though: null literals, regexp literals and template literals.

This patch fixes `.join()`ing regexp literals and bails out of joining template literals entirely. 

Null literals already did the right thing despite not having a `.value`, because `.join()` appears to treat `undefined` and `null` the same.

Patch:
diff --git a/packages/babel-plugin-minify-constant-folding/src/index.js b/packages/babel-plugin-minify-constant-folding/src/index.js
index 729919d..8b15665 100644
--- a/packages/babel-plugin-minify-constant-folding/src/index.js
+++ b/packages/babel-plugin-minify-constant-folding/src/index.js
@@ -19,7 +19,7 @@ function getName(member) {
 }
 
 function swap(path, member, handlers, ...args) {
-  const key = getName(member);
+  const key = getName(member.node);
   if (key === undefined) return;
   let handler = handlers[key];
   if (
@@ -27,12 +27,12 @@ function swap(path, member, handlers, ...args) {
     !Object.hasOwnProperty.call(handlers, key)
   ) {
     if (typeof handlers[FALLBACK_HANDLER] === "function") {
-      handler = handlers[FALLBACK_HANDLER].bind(member.object, key);
+      handler = handlers[FALLBACK_HANDLER].bind(member.get("object"), key);
     } else {
       return false;
     }
   }
-  const replacement = handler.apply(member.object, args);
+  const replacement = handler.apply(member.get("object"), args);
   if (replacement) {
     path.replaceWith(replacement);
     return true;
@@ -168,18 +168,18 @@ module.exports = babel => {
       },
       CallExpression(path) {
         const { node } = path;
-        const { callee: member } = node;
+        const member = path.get("callee");
         if (t.isMemberExpression(member)) {
-          const helpers = replacements[member.object.type];
+          const helpers = replacements[member.node.object.type];
           if (!helpers || !helpers.calls) return;
           swap(path, member, helpers.calls, ...node.arguments);
         }
       },
       MemberExpression(path) {
-        const { node: member } = path;
-        const helpers = replacements[member.object.type];
+        const { node } = path;
+        const helpers = replacements[node.object.type];
         if (!helpers || !helpers.members) return;
-        swap(path, member, helpers.members);
+        swap(path, path, helpers.members);
       }
     }
   };
diff --git a/packages/babel-plugin-minify-constant-folding/src/replacements.js b/packages/babel-plugin-minify-constant-folding/src/replacements.js
index 0cbc591..ae52887 100644
--- a/packages/babel-plugin-minify-constant-folding/src/replacements.js
+++ b/packages/babel-plugin-minify-constant-folding/src/replacements.js
@@ -14,7 +14,7 @@ module.exports = ({ types: t }) => {
   function defaultZero(cb) {
     return function(i = t.numericLiteral(0), ...args) {
       if (t.isNumericLiteral(i)) {
-        return cb.call(this, this, i.value, ...args);
+        return cb.call(this.node, this.node, i.value, ...args);
       }
     };
   }
@@ -23,17 +23,17 @@ module.exports = ({ types: t }) => {
     ArrayExpression: {
       members: {
         length() {
-          if (this.elements.some(el => t.isSpreadElement(el))) {
+          if (this.node.elements.some(el => t.isSpreadElement(el))) {
             return;
           }
-          return t.numericLiteral(this.elements.length);
+          return t.numericLiteral(this.node.elements.length);
         },
         [FALLBACK_HANDLER](i) {
-          if (this.elements.some(el => t.isSpreadElement(el))) {
+          if (this.node.elements.some(el => t.isSpreadElement(el))) {
             return;
           }
           if (typeof i === "number" || i.match(/^\d+$/)) {
-            return this.elements[i] || undef;
+            return this.node.elements[i] || undef;
           }
         }
       },
@@ -41,39 +41,40 @@ module.exports = ({ types: t }) => {
         join(sep = t.stringLiteral(",")) {
           if (!t.isStringLiteral(sep)) return;
           let bad = false;
-          const str = this.elements
+          const str = this.get("elements")
             .map(el => {
-              if (!t.isLiteral(el)) {
+              const evaled = el.evaluate();
+              if (!evaled.confident) {
                 bad = true;
                 return;
               }
-              return el.value;
+              return evaled.value;
             })
             .join(sep.value);
           return bad ? undefined : t.stringLiteral(str);
         },
         push(...args) {
-          return t.numericLiteral(this.elements.length + args.length);
+          return t.numericLiteral(this.node.elements.length + args.length);
         },
         shift() {
-          if (this.elements.length === 0) {
+          if (this.node.elements.length === 0) {
             return undef;
           }
-          return t.numericLiteral(this.elements.length - 1);
+          return t.numericLiteral(this.node.elements.length - 1);
         },
         slice(start = t.numericLiteral(0), end) {
           if (!t.isNumericLiteral(start) || (end && !t.isNumericLiteral(end))) {
             return;
           }
           return t.arrayExpression(
-            this.elements.slice(start.value, end && end.value)
+            this.node.elements.slice(start.value, end && end.value)
           );
         },
         pop() {
-          return this.elements[this.elements.length - 1] || undef;
+          return this.node.elements[this.node.elements.length - 1] || undef;
         },
         reverse() {
-          return t.arrayExpression(this.elements.reverse());
+          return t.arrayExpression(this.node.elements.reverse());
         },
         splice(start, end, ...args) {
           if (!t.isNumericLiteral(start) || (end && !t.isNumericLiteral(end))) {
@@ -83,7 +84,7 @@ module.exports = ({ types: t }) => {
             args.unshift(end.value);
           }
           return t.arrayExpression(
-            this.elements.slice().splice(start.value, ...args)
+            this.node.elements.slice().splice(start.value, ...args)
           );
         }
       }
@@ -91,11 +92,11 @@ module.exports = ({ types: t }) => {
     StringLiteral: {
       members: {
         length() {
-          return t.numericLiteral(this.value.length);
+          return t.numericLiteral(this.node.value.length);
         },
         [FALLBACK_HANDLER](i) {
           if (typeof i === "number" || i.match(/^\d+$/)) {
-            const ch = this.value[i];
+            const ch = this.node.value[i];
             return ch ? t.stringLiteral(ch) : undef;
           }
         }
@@ -111,7 +112,7 @@ module.exports = ({ types: t }) => {
           }
           if (realSep !== null) {
             return t.arrayExpression(
-              this.value.split(realSep).map(str => t.stringLiteral(str))
+              this.node.value.split(realSep).map(str => t.stringLiteral(str))
             );
           }
         },


Test patch:
diff --git a/packages/babel-plugin-minify-constant-folding/__tests__/constant-folding-test.js b/packages/babel-plugin-minify-constant-folding/__tests__/constant-folding-test.js
index 2cacfd7..7e7178b 100644
--- a/packages/babel-plugin-minify-constant-folding/__tests__/constant-folding-test.js
+++ b/packages/babel-plugin-minify-constant-folding/__tests__/constant-folding-test.js
@@ -103,6 +103,10 @@ describe("constant-folding-plugin", () => {
     [1, 2, 3].join();
     ["a", "b", "c"].join();
     ["a", "b", "c"].join("@");
+    [null, 1].join("/");
+    [/xyz/im, true].join("abc");
+    [\`a\${xyz}\`].join("1");
+    [\`a\`, \`c\`].join('b');
 
     [1, 2, 3].length;
     [1, 2, 3][1];
@@ -134,6 +138,10 @@ describe("constant-folding-plugin", () => {
     "1,2,3";
     "a,b,c";
     "a@b@c";
+    "/1";
+    [/xyz/im, true].join("abc");
+    [\`a\${xyz}\`].join("1");
+    "abc";
 
     3;
     2;