From 911a2821204e9c8e671701588008ce8f01927f94 Mon Sep 17 00:00:00 2001 From: termi Date: Mon, 2 Dec 2013 01:04:56 +0400 Subject: [PATCH] Add block binding (let/const) support fixes #65 + tests --- .gitignore | 2 ++ lib/util.js | 19 ++++++++++++++++ main.js | 47 ++++++++++++++++++++++++++++++-------- package.json | 5 ++-- test/tests.es6.js | 58 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 119 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 94aa1532f15c..ea026f5882b7 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ test/mocha.css test/tests.es5.js test/tests.browser.js + +.idea \ No newline at end of file diff --git a/lib/util.js b/lib/util.js index 1546cb9ec96f..3df8120a1eb1 100644 --- a/lib/util.js +++ b/lib/util.js @@ -33,3 +33,22 @@ exports.guessTabWidth = function(source) { return result; }; + +exports.extend = function(obj) { + var len = arguments.length; + var extension; + + for (var i = 1; + i < len; + i++) { + if (extension = arguments[i]) { + for (var key in extension) { + if (Object.prototype.hasOwnProperty.call(extension, key)) { + obj[key] = extension[key]; + } + } + } + } + + return obj; +} \ No newline at end of file diff --git a/main.js b/main.js index d86c84079cb1..10a85b21ef31 100644 --- a/main.js +++ b/main.js @@ -11,10 +11,11 @@ var assert = require("assert"); var path = require("path"); var fs = require("fs"); var transform = require("./lib/visit").transform; -var guessTabWidth = require("./lib/util").guessTabWidth; +var utils = require("./lib/util"); var recast = require("recast"); var esprimaHarmony = require("esprima"); var genFunExp = /\bfunction\s*\*/; +var blockBindingExp = /\b(let|const)\s*/; assert.ok( /harmony/.test(esprimaHarmony.version), @@ -22,11 +23,11 @@ assert.ok( ); function regenerator(source, options) { - if (!options) { - options = { - includeRuntime: false - }; - } + options = utils.extend(options || {}, { + includeRuntime: false, + supportBlockBinding: true + } + ); var runtime = options.includeRuntime ? fs.readFileSync( regenerator.runtime.dev, "utf-8" @@ -36,15 +37,41 @@ function regenerator(source, options) { return runtime + source; // Shortcut: no generators to transform. } + var supportBlockBinding = options.supportBlockBinding; + if (supportBlockBinding) { + if (!blockBindingExp.test(source)) { + supportBlockBinding = false; + } + } + var recastOptions = { - tabWidth: guessTabWidth(source), + tabWidth: utils.guessTabWidth(source), // Use the harmony branch of Esprima that installs with regenerator // instead of the master branch that recast provides. - esprima: esprimaHarmony + esprima: esprimaHarmony, + range: supportBlockBinding }; - var ast = recast.parse(source, recastOptions); - var es5 = recast.print(transform(ast), recastOptions); + var recastAst = recast.parse(source, recastOptions); + var ast = recastAst.program; + + if (supportBlockBinding) {// smart transpiling let/const into var + var defsResult = require("defs")(ast, { + ast: true, + disallowUnknownReferences: false, + disallowDuplicated: false, + disallowVars: false, + loopClosures: "iife" + }); + + if (defsResult.errors) { + throw new Error(defsResult.errors.join("\n")) + } + } + + recastAst.program = transform(ast); + + var es5 = recast.print(recastAst, recastOptions); return runtime + es5; } diff --git a/package.json b/package.json index 378b528390a8..0ba7a0e697e4 100644 --- a/package.json +++ b/package.json @@ -31,8 +31,9 @@ "commander": "~2.0.0", "esprima": "git://github.com/ariya/esprima.git#harmony", "ast-types": ">= 0.3.8", - "recast": "~0.4.23", - "private": "~0.0.5" + "recast": "~0.4.25", + "private": "~0.0.5", + "defs": "~0.6.0" }, "devDependencies": { "mocha": "~1.13.0", diff --git a/test/tests.es6.js b/test/tests.es6.js index f6390e785845..4acf58fd8810 100644 --- a/test/tests.es6.js +++ b/test/tests.es6.js @@ -1048,3 +1048,61 @@ describe("new expressions", function() { assert.deepEqual(g.next("qwer"), { value: "qwer", done: true }); }); }); + +describe("block binding", function() { + it("should translate block binding correctly", function() { + "use strict"; + + function *gen() { + var a$0 = 0, a$1 = 1; + + let a = 3; + + { + let a = 1; + yield a + a$0; + } + + { + let a = 2; + yield a - 1 + a$1; + } + + yield a; + } + + var g = gen(); + + assert.deepEqual(g.next(), { value: 1, done: false }); + assert.deepEqual(g.next(), { value: 2, done: false }); + assert.deepEqual(g.next(), { value: 3, done: false }); + assert.deepEqual(g.next(), { value: void 0, done: true }); + }); + + it("should translate block binding with iife correctly", function() { + "use strict"; + + function *gen() { + let arr = []; + + for (let x = 0; x < 3; x++) { + let y = x; + arr.push(function() { return y; }); + } + + { + let x; + while( x = arr.pop() ) { + yield x; + } + } + } + + var g = gen(); + + assert.equal(g.next().value(), 2); + assert.equal(g.next().value(), 1); + assert.equal(g.next().value(), 0); + assert.deepEqual(g.next(), { value: void 0, done: true }); + }); +});