Repository: Rich-Harris/buble
Pull request #61 (2017-11-28 17:15:31)

Allow aliases in block scope
I think I'm understanding the logic introduced [here](https://github.com/Rich-Harris/buble/commit/6d9fc0ab0cd769425b026748ffa372d09b1ef35a#diff-3ada2be4c92366e1bf09caeaa19ce9c1R18).

Currently, `blockScopedDeclarations` and `aliases` are set to null within block statements, causing #60. I believe these should be objects in both `block` and `!block` cases.

Includes a minimal test that shows the issue.


Patch:
diff --git a/src/program/Program.js b/src/program/Program.js
index 5a6065d..b85a104 100644
--- a/src/program/Program.js
+++ b/src/program/Program.js
@@ -18,6 +18,8 @@ export default function Program(source, ast, transforms, options) {
 	wrap((this.body = ast), this);
 	this.body.__proto__ = BlockStatement.prototype;
 
+	this.templateLiteralQuasis = Object.create(null);
+
 	this.indentExclusionElements = [];
 	this.body.initialise(transforms);
 
diff --git a/src/program/Scope.js b/src/program/Scope.js
index 18f1664..758f154 100644
--- a/src/program/Scope.js
+++ b/src/program/Scope.js
@@ -15,7 +15,7 @@ export default function Scope(options) {
 	this.declarations = Object.create(null);
 	this.references = Object.create(null);
 	this.blockScopedDeclarations = this.isBlockScope ? null : Object.create(null);
-	this.aliases = this.isBlockScope ? null : Object.create(null);
+	this.aliases = Object.create(null);
 }
 
 Scope.prototype = {
diff --git a/src/program/types/ClassDeclaration.js b/src/program/types/ClassDeclaration.js
index ddca8e7..d3563b8 100644
--- a/src/program/types/ClassDeclaration.js
+++ b/src/program/types/ClassDeclaration.js
@@ -31,10 +31,10 @@ export default class ClassDeclaration extends Node {
 				code.remove(this.parent.start, this.start);
 			}
 
-			let c = this.start
+			let c = this.start;
 			if (this.id) {
 				code.overwrite(c, this.id.start, 'var ');
-				c = this.id.end
+				c = this.id.end;
 			} else {
 				code.prependLeft(c, `var ${this.name}`);
 			}
diff --git a/src/program/types/NewExpression.js b/src/program/types/NewExpression.js
index 8d33c06..2df5314 100644
--- a/src/program/types/NewExpression.js
+++ b/src/program/types/NewExpression.js
@@ -1,5 +1,6 @@
 import Node from '../Node.js';
 import spread, { isArguments } from '../../utils/spread.js';
+import removeTrailingComma from '../../utils/removeTrailingComma.js';
 
 export default class NewExpression extends Node {
 	initialise(transforms) {
@@ -46,5 +47,8 @@ export default class NewExpression extends Node {
 		}
 
 		super.transpile(code, transforms);
+		if (this.arguments.length) {
+			removeTrailingComma(code, this.arguments[this.arguments.length - 1].end);
+		}
 	}
 }
diff --git a/src/program/types/TaggedTemplateExpression.js b/src/program/types/TaggedTemplateExpression.js
index 987aabe..2c014e0 100644
--- a/src/program/types/TaggedTemplateExpression.js
+++ b/src/program/types/TaggedTemplateExpression.js
@@ -22,14 +22,26 @@ export default class TaggedTemplateExpression extends Node {
 				.concat(this.quasi.quasis)
 				.sort((a, b) => a.start - b.start);
 
+			const program = this.program;
+			const rootScope = program.body.scope;
+
 			// insert strings at start
 			const templateStrings = this.quasi.quasis.map(quasi =>
 				JSON.stringify(quasi.value.cooked)
-			);
+			).join(', ');
+
+			let templateObject = this.program.templateLiteralQuasis[templateStrings];
+			if (!templateObject) {
+				templateObject = rootScope.createIdentifier('templateObject');
+				code.prependRight(0, `var ${templateObject} = Object.freeze([${templateStrings}]);\n`);
+
+				this.program.templateLiteralQuasis[templateStrings] = templateObject;
+			}
+
 			code.overwrite(
 				this.tag.end,
 				ordered[0].start,
-				`([${templateStrings.join(', ')}]`
+				`(${templateObject}`
 			);
 
 			let lastIndex = ordered[0].start;


Test patch:
diff --git a/test/samples/destructuring.js b/test/samples/destructuring.js
index 8ea25cb..8f4686b 100644
--- a/test/samples/destructuring.js
+++ b/test/samples/destructuring.js
@@ -782,5 +782,24 @@ module.exports = [
 			var a = ref.a;
 			var b = ref.b;
 		`
+	},
+
+	{
+		description: 'destructures within block scope',
+
+		input: `
+			if (true) {
+				let [[a, b]] = [[1, 2]];
+			}
+		`,
+
+		output: `
+			if (true) {
+				var ref = [[1, 2]];
+				var ref_0 = ref[0];
+				var a = ref_0[0];
+				var b = ref_0[1];
+			}
+		`
 	}
 ];
diff --git a/test/samples/template-strings.js b/test/samples/template-strings.js
index 6b69b50..de7a426 100644
--- a/test/samples/template-strings.js
+++ b/test/samples/template-strings.js
@@ -41,7 +41,7 @@ module.exports = [
 			'transpiles tagged template literals with `transforms.dangerousTaggedTemplateString = true`',
 		options: { transforms: { dangerousTaggedTemplateString: true } },
 		input: 'var str = x`y${(() => 42)()}`;',
-		output: `var str = x(["y", ""], (function () { return 42; })());`
+		output: `var templateObject = Object.freeze(["y", ""]);\nvar str = x(templateObject, (function () { return 42; })());`
 	},
 
 	{
@@ -49,7 +49,14 @@ module.exports = [
 			'transpiles tagged template literals with `transforms.dangerousTaggedTemplateString = true`',
 		options: { transforms: { dangerousTaggedTemplateString: true } },
 		input: 'var str = x`${(() => 42)()}y`;',
-		output: `var str = x(["", "y"], (function () { return 42; })());`
+		output: `var templateObject = Object.freeze(["", "y"]);\nvar str = x(templateObject, (function () { return 42; })());`
+	},
+
+	{
+		description: 'reuses quasi array for identical tagged template strings',
+		options: { transforms: { dangerousTaggedTemplateString: true } },
+		input: 'x`a${a}b`, x`a${b}b`, x`b${c}a`',
+		output: `var templateObject$1 = Object.freeze(["b", "a"]);\nvar templateObject = Object.freeze(["a", "b"]);\nx(templateObject, a), x(templateObject, b), x(templateObject$1, c)`
 	},
 
 	{
diff --git a/test/samples/trailing-function-commas.js b/test/samples/trailing-function-commas.js
index 67cc793..597efe0 100644
--- a/test/samples/trailing-function-commas.js
+++ b/test/samples/trailing-function-commas.js
@@ -64,5 +64,15 @@ module.exports = [
 				var b = ref[0];
 				var c = ref$1.c;
 })`
+	},
+
+	{
+		description: 'strips trailing commas in new expression arguments',
+
+		input: `
+			new f(a,)`,
+
+		output: `
+			new f(a)`
 	}
 ];