Compare commits

...

15 Commits

Author SHA1 Message Date
Sebastian McKenzie
0ee4a15d01 v1.14.17 2014-12-06 23:37:32 +11:00
Sebastian McKenzie
fbdaedc4f8 remove unused loopParent variable 2014-12-06 23:36:28 +11:00
Sebastian McKenzie
75c8d73d35 add default initializer to let variables within loop bodies and fix excessive break replacement inside of switches in let scoping - fixes #255 2014-12-06 23:35:47 +11:00
Sebastian McKenzie
21599d3a9a change memoisation to memoization 2014-12-06 20:49:33 +11:00
Sebastian McKenzie
9fbbabc7e4 v1.14.16 2014-12-06 20:48:31 +11:00
Sebastian McKenzie
8c1fe49b2e upgrade acorn-6to5 2014-12-06 20:47:40 +11:00
Sebastian McKenzie
763b4caf32 v1.14.15 2014-12-06 20:46:16 +11:00
Sebastian McKenzie
c0248cf04c add 1.14.15 changelog 2014-12-06 20:44:58 +11:00
Sebastian McKenzie
a7a6ee80f2 add object getter memoization and this shorthand to playground 2014-12-06 19:37:23 +11:00
Sebastian McKenzie
aa298b1e0a update doc/differences to reflect current status of project - fixes #253 2014-12-06 19:36:46 +11:00
Sebastian McKenzie
9c0a8e22d2 fix let scoping not working with while loops - fixes #254 2014-12-06 19:36:08 +11:00
Sebastian McKenzie
7c1a924ef6 v1.14.14 2014-12-06 00:04:00 +11:00
Sebastian McKenzie
1d975a2635 Fix tagged template literals 2014-12-06 00:03:11 +11:00
Sebastian McKenzie
67cfdbd447 better closure variable name in default parameters 2014-12-06 00:00:09 +11:00
Sebastian McKenzie
ab02231d39 fix template literals escaping 2014-12-06 00:00:00 +11:00
14 changed files with 267 additions and 38 deletions

View File

@@ -1,3 +1,18 @@
# 1.14.17
* Add default initializer to let variables within loop bodies.
* Fix excessive `break` replacement inside of switches in let scoping.
# 1.14.16
* Add object getter memos and this shorthand to playground.
* Fix while loops in let scoping.
* Upgrade `acorn-6to5`.
# 1.14.14
* Fix template literals escaping.
# 1.14.13
* Fix let scoping of `while` loops.

View File

@@ -17,7 +17,7 @@ var seattlers = [for (c of customers) if (c.city == "Seattle") { name: c.name, a
is generated to the following with 6to5:
```javascript
var seattlers = customers.filter(function (c) {
var seattlers = Array.from(customers).filter(function (c) {
return c.city == "Seattle";
}).map(function (c) {
return {
@@ -51,8 +51,7 @@ As you can tell, it's not very pretty, unreadable even. Instead of mapping
directly to a runtime, like other transpilers, 6to5 maps directly to the
equivalent ES5.
I'm not saying 6to5 is for everyone or even suited for everything. Traceur is
better suited if you'd like a full ES6 environment with polyfills and all.
Sometimes there are little things that 6to5 needs
## Comparison to other transpilers

View File

@@ -18,6 +18,8 @@ to5.transform("code", { playground: true });
* [Memoization operator](#memoization-operator)
* [Method binding](#method-binding)
* [Object getter memoization](#object-getter-memoization)
* [This shorthand](#this-shorthand)
### Memoization assignment operator
@@ -66,3 +68,53 @@ var fn = obj.method.bind(obj, "foob");
["foo", "bar"].map(function (val) { return val.toUpperCase(); });
[1.1234, 23.53245, 3].map(function (val) { return val.toFixed(2); });
```
### Object getter memoization
```javascript
var foo = {
memo bar() {
return complex();
}
};
class Foo {
memo bar() {
return complex();
}
}
```
equivalent to
```javascript
var foo = {
get bar() {
if (this._barRan) return this._bar;
this._barRan = true;
return this._bar = complex();
}
};
class Foo {
get bar() {
if (this._barRan) return this._bar;
this._barRan = true;
return this._bar = complex();
}
}
```
**NOTE:** Memoised functions will return the result of the **first** execution, regardless of arguments.
### This shorthand
```javascript
@foo
```
equivalent to
```javascirpt
this.foo
```

View File

@@ -29,6 +29,11 @@ transform.moduleFormatters = {
};
_.each({
// plyground
methodBinding: require("./transformers/playground-method-binding"),
memoizationOperator: require("./transformers/playground-memoization-operator"),
objectGetterMemoization: require("./transformers/playground-object-getter-memoization"),
modules: require("./transformers/es6-modules"),
propertyNameShorthand: require("./transformers/es6-property-name-shorthand"),
arrayComprehension: require("./transformers/es7-array-comprehension"),
@@ -56,10 +61,6 @@ _.each({
generators: require("./transformers/es6-generators"),
// plyground
methodBinding: require("./transformers/playground-method-binding"),
memoizationOperator: require("./transformers/playground-memoization-operator"),
_blockHoist: require("./transformers/_block-hoist"),
_declarations: require("./transformers/_declarations"),
_aliasFunctions: require("./transformers/_alias-functions"),

View File

@@ -11,7 +11,7 @@ exports.Function = function (node, parent, file, scope) {
return t.getIds(param);
});
var closure = false;
var iife = false;
_.each(node.defaults, function (def, i) {
if (!def) return;
@@ -29,7 +29,7 @@ exports.Function = function (node, parent, file, scope) {
}
if (scope.has(node.name)) {
closure = true;
iife = true;
}
};
@@ -40,7 +40,7 @@ exports.Function = function (node, parent, file, scope) {
// we're accessing a variable that's already defined within this function
var has = scope.get(param.name);
if (has && !_.contains(node.params, has)) {
closure = true;
iife = true;
}
});
@@ -55,7 +55,7 @@ exports.Function = function (node, parent, file, scope) {
}, true));
});
if (closure) {
if (iife) {
var container = t.functionExpression(null, [], node.body, node.generator);
container._aliasFunction = true;

View File

@@ -27,7 +27,7 @@ exports.VariableDeclaration = function (node) {
isLet(node);
};
exports.For = function (node, parent, file, scope) {
exports.Loop = function (node, parent, file, scope) {
var init = node.left || node.init;
if (isLet(init)) {
t.ensureBlock(node);
@@ -49,7 +49,7 @@ exports.For = function (node, parent, file, scope) {
};
exports.BlockStatement = function (block, parent, file, scope) {
if (!t.isFor(parent)) {
if (!t.isLoop(parent)) {
var letScoping = new LetScoping(false, block, parent, file, scope);
letScoping.run();
}
@@ -58,19 +58,19 @@ exports.BlockStatement = function (block, parent, file, scope) {
/**
* Description
*
* @param {Boolean|Node} forParent
* @param {Boolean|Node} loopParent
* @param {Node} block
* @param {Node} parent
* @param {File} file
* @param {Scope} scope
*/
function LetScoping(forParent, block, parent, file, scope) {
this.forParent = forParent;
this.parent = parent;
this.scope = scope;
this.block = block;
this.file = file;
function LetScoping(loopParent, block, parent, file, scope) {
this.loopParent = loopParent;
this.parent = parent;
this.scope = scope;
this.block = block;
this.file = file;
this.letReferences = {};
this.body = [];
@@ -91,6 +91,9 @@ LetScoping.prototype.run = function () {
// remap all let references that exist in upper scopes to their uid
this.remap();
// add default initializer to let variables in loop bodys
this.initialiseLoopLets();
// this is a block within a `Function` so we can safely leave it be
if (t.isFunction(this.parent)) return this.noClosure();
@@ -106,7 +109,7 @@ LetScoping.prototype.run = function () {
// if we're inside of a for loop then we search to see if there are any
// `break`s, `continue`s, `return`s etc
this.has = this.checkFor();
this.has = this.checkLoop();
// hoist var references to retain scope
this.hoistVarDeclarations();
@@ -172,11 +175,11 @@ LetScoping.prototype.remap = function () {
traverse(node, replace);
};
var forParent = this.forParent;
if (forParent) {
traverseReplace(forParent.right, forParent);
traverseReplace(forParent.test, forParent);
traverseReplace(forParent.update, forParent);
var loopParent = this.loopParent;
if (loopParent) {
traverseReplace(loopParent.right, loopParent);
traverseReplace(loopParent.test, loopParent);
traverseReplace(loopParent.update, loopParent);
}
traverse(block, replace);
@@ -244,6 +247,30 @@ LetScoping.prototype.getInfo = function () {
return opts;
};
/**
* Any let variable declared within a loop body need to have an initializer or
* else they'll be hoisted and subsequent iterations of the loop will have a
* previous state. This function adds a default initializer of `undefined` to
* those variables.
*/
LetScoping.prototype.initialiseLoopLets = function () {
var loopParent = this.loopParent;
if (!loopParent) return;
traverse(this.block, function (node) {
if (t.isFunction(node) || t.isLoop(node)) {
return false;
}
if (isLet(node)) {
_.each(node.declarations, function (declar) {
declar.init = declar.init || t.identifier("undefined");
});
}
});
};
/**
* If we're inside of a `For*Statement` then traverse it and check if it has one
* of the following node types `ReturnStatement`, `BreakStatement`,
@@ -253,22 +280,24 @@ LetScoping.prototype.getInfo = function () {
* @returns {Object}
*/
LetScoping.prototype.checkFor = function () {
LetScoping.prototype.checkLoop = function () {
var has = {
hasContinue: false,
hasReturn: false,
hasBreak: false,
};
traverse(this.block, function (node) {
traverse(this.block, function (node, parent) {
var replace;
if (t.isFunction(node) || t.isLoop(node) || t.isSwitchStatement(node)) {
if (t.isFunction(node) || t.isLoop(node)) {
return false;
}
if (node && !node.label) {
if (t.isBreakStatement(node)) {
if (t.isSwitchCase(parent)) return;
has.hasBreak = true;
replace = t.returnStatement(t.literal("break"));
} else if (t.isContinueStatement(node)) {
@@ -365,7 +394,7 @@ LetScoping.prototype.getLetReferences = function () {
});
return false;
} else if (t.isFor(node)) {
} else if (t.isLoop(node)) {
return false;
}
});
@@ -428,7 +457,7 @@ LetScoping.prototype.buildHas = function (ret, call) {
t.variableDeclarator(ret, call)
]));
var forParent = this.forParent;
var loopParent = this.loopParent;
var retCheck;
var has = this.has;
var cases = [];
@@ -443,7 +472,7 @@ LetScoping.prototype.buildHas = function (ret, call) {
if (has.hasBreak || has.hasContinue) {
// ensure that the parent has a label as we're building a switch and we
// need to be able to access it
var label = forParent.label = forParent.label || this.file.generateUidIdentifier("loop", this.scope);
var label = loopParent.label = loopParent.label || this.file.generateUidIdentifier("loop", this.scope);
if (has.hasBreak) {
cases.push(t.switchCase(t.literal("break"), [t.breakStatement(label)]));

View File

@@ -31,7 +31,7 @@ exports.TemplateLiteral = function (node) {
var nodes = [];
_.each(node.quasis, function (elem) {
nodes.push(t.literal(elem.value.raw));
nodes.push(t.literal(elem.value.cooked));
var expr = node.expressions.shift();
if (expr) {

View File

@@ -0,0 +1,37 @@
var traverse = require("../../traverse");
var t = require("../../types");
exports.Property =
exports.MethodDefinition = function (node, parent, file, scope) {
if (node.kind !== "memo") return;
node.kind = "get";
var value = node.value;
t.ensureBlock(value);
var body = value.body.body;
var key = node.key;
if (t.isIdentifier(key) && !node.computed) {
key = "_" + key.name;
} else {
key = file.generateUid("memo", scope);
}
var memoId = t.memberExpression(t.thisExpression(), t.identifier(key));
var doneId = t.memberExpression(t.thisExpression(), t.identifier(key + "Done"));
traverse(value, function (node) {
if (t.isFunction(node)) return;
if (t.isReturnStatement(node) && node.argument) {
node.argument = t.assignmentExpression("=", memoId, node.argument);
}
});
// this._barDone = true;
body.unshift(t.expressionStatement(t.assignmentExpression("=", doneId, t.literal(true))));
// if (this._barDone) return this._bar;
body.unshift(t.ifStatement(doneId, t.returnStatement(memoId)));
};

View File

@@ -3,14 +3,14 @@
"BreakStatement": ["Statement"],
"ContinueStatement": ["Statement"],
"DebuggerStatement": ["Statement"],
"DoWhileStatement": ["Statement", "Loop", "With"],
"DoWhileStatement": ["Statement", "Loop", "While"],
"IfStatement": ["Statement"],
"ReturnStatement": ["Statement"],
"SwitchStatement": ["Statement"],
"ThrowStatement": ["Statement"],
"TryStatement": ["Statement"],
"WhileStatement": ["Statement"],
"WithStatement": ["Statement", "Loop", "With"],
"WhileStatement": ["Statement", "Loop", "While"],
"WithStatement": ["Statement"],
"EmptyStatement": ["Statement"],
"LabeledStatement": ["Statement"],
"VariableDeclaration": ["Statement", "Declaration"],

View File

@@ -1,7 +1,7 @@
{
"name": "6to5",
"description": "Turn ES6 code into readable vanilla ES5 with source maps",
"version": "1.14.13",
"version": "1.14.17",
"author": "Sebastian McKenzie <sebmck@gmail.com>",
"homepage": "https://github.com/6to5/6to5",
"repository": {
@@ -35,7 +35,7 @@
"test": "make test"
},
"dependencies": {
"acorn-6to5": "0.9.1-11",
"acorn-6to5": "0.9.1-12",
"ast-types": "0.6.5",
"chokidar": "0.11.1",
"commander": "2.5.0",

View File

@@ -0,0 +1,11 @@
while (value) {
let foo;
if (bar) {
foo = [];
}
if (foo) {
doIt();
}
}

View File

@@ -0,0 +1,13 @@
"use strict";
while (value) {
var foo = undefined;
if (bar) {
foo = [];
}
if (foo) {
doIt();
}
}

View File

@@ -0,0 +1,19 @@
class Foo {
memo bar() {
return complex();
}
memo [bar]() {
return complex();
}
}
var foo = {
memo bar() {
return complex();
},
memo [bar]() {
return complex();
}
};

View File

@@ -0,0 +1,53 @@
"use strict";
var _classProps = function (child, staticProps, instanceProps) {
if (staticProps) Object.defineProperties(child, staticProps);
if (instanceProps) Object.defineProperties(child.prototype, instanceProps);
};
var Foo = (function () {
var Foo = function Foo() {};
_classProps(Foo, null, (function (_ref) {
_ref[bar] = {
get: function () {
if (this._memoDone) return this._memo;
this._memoDone = true;
return this._memo = complex();
}
};
return _ref;
})({
bar: {
get: function () {
if (this._barDone) return this._bar;
this._barDone = true;
return this._bar = complex();
}
}
}));
return Foo;
})();
var foo = (function (_foo) {
_foo[bar] = function () {
if (this._memo2Done) return this._memo2;
this._memo2Done = true;
return this._memo2 = complex();
};
return _foo;
})((function (_ref2) {
Object.defineProperties(_ref2, {
bar: {
get: function () {
if (this._barDone) return this._bar;
this._barDone = true;
return this._bar = complex();
}
}
});
return _ref2;
})({}));