diff --git a/doc/caveats.md b/doc/caveats.md index af8fb6b60f..3d70e833d0 100644 --- a/doc/caveats.md +++ b/doc/caveats.md @@ -6,9 +6,11 @@ satisfy **all** 6to5 feature requirements by using the included | Feature | Requirements | | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| Abstract References | `Symbol` | | Array destructuring | `Array.isArray`, `Array.from` | | Async functions, Generators | [experimental option](usage.md#experimental), [regenerator runtime](https://github.com/facebook/regenerator/blob/master/runtime.js) | | Comprehensions | [experimental option](usage.md#experimental), `Array.isArray`, `Array.from` | +| For..Of | `Symbol`, `prototype[Symbol.iterator]` | | Spread | `Array.isArray`, `Array.from` | ## Classes @@ -36,14 +38,3 @@ class Bar extends Foo { } } ``` - -## Constructor spread - -Constructor spreads do not currently support built-ins. ie. -`new Array(...items)`. - -## For-of - -A polyfill is required for for-of functionality that implements `Symbol` and -adds `prototype[Symbol.iterator]` behaviour to built-ins. Using the polyfills -specified in [polyfill](polyfill.md) suffices. diff --git a/doc/differences.md b/doc/differences.md index 0a887a0ec0..81a07e0b27 100644 --- a/doc/differences.md +++ b/doc/differences.md @@ -69,6 +69,7 @@ better suited if you'd like a full ES6 environment with polyfills and all. | | 6to5 | Traceur | es6-transpiler | esnext | es6now | jstransform | | ---------------------------- | ----- | ------- | -------------- | ------ | ------ | ----------- | +| Abstract references | ✓ | | | | | | | Array comprehension | ✓ | ✓ | ✓ | | | | | Arrow functions | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | Async functions | ✓ | ✓ | | ✓ | | | diff --git a/doc/features.md b/doc/features.md index 1d2732533d..08f55b657f 100644 --- a/doc/features.md +++ b/doc/features.md @@ -1,5 +1,13 @@ # Features +## Abstract references ([experimental](usage.md#experimental)) ([spec](https://github.com/zenparsing/es-abstract-refs)) + +```javascript +foo::bar; +foo::bar = baz; +delete foo::bar; +``` + ## Array comprehension ([experimental](usage.md#experimental)) ```javascript @@ -31,7 +39,7 @@ var bob = { }; ``` -## Async functions ([experimental](usage.md#experimental)) +## Async functions ([experimental](usage.md#experimental)) ([spec](https://github.com/lukehoban/ecmascript-asyncawait)) ```javascript async function chainAnimationsAsync(elem, animations) { diff --git a/doc/index.md b/doc/index.md index 0e986d4869..45325bbfde 100644 --- a/doc/index.md +++ b/doc/index.md @@ -31,6 +31,7 @@ And it doesn't end here! To see all the ways you can use 6to5, check out the ## [Features](features.md) + - [Abstract references](features.md#abstract-references) ([experimental](usage.md#experimental)) - [Array comprehension](features.md#array-comprehension) ([experimental](usage.md#experimental)) - [Async functions](features.md#async-functions) ([experimental](usage.md#experimental)) - [Arrow functions](features.md#arrow-functions) diff --git a/lib/6to5/generation/generators/types.js b/lib/6to5/generation/generators/types.js index 92b4b2509d..f9f79fcc2e 100644 --- a/lib/6to5/generation/generators/types.js +++ b/lib/6to5/generation/generators/types.js @@ -9,6 +9,12 @@ exports.SpreadElement = function (node, print) { print(node.argument); }; +exports.VirtualPropertyExpression = function (node, print) { + print(node.object); + this.push("::"); + print(node.property); +}; + exports.ObjectExpression = exports.ObjectPattern = function (node, print) { var props = node.properties; diff --git a/lib/6to5/generation/node/index.js b/lib/6to5/generation/node/index.js index 847508fb55..9e8995889a 100644 --- a/lib/6to5/generation/node/index.js +++ b/lib/6to5/generation/node/index.js @@ -27,8 +27,7 @@ Node.prototype.isUserWhitespacable = function () { var parent = this.parent; var node = this.node; - if (t.isUserWhitespacable(node) || - t.isSequenceExpression(parent)) { + if (t.isUserWhitespacable(node)) { return true; } diff --git a/lib/6to5/polyfill.js b/lib/6to5/polyfill.js index f777ba700d..2cf4d1784d 100644 --- a/lib/6to5/polyfill.js +++ b/lib/6to5/polyfill.js @@ -1,6 +1,37 @@ +// + +var ensureSymbol = function (key) { + Symbol[key] = Symbol[key] || Symbol(); +}; + +var ensureProto = function (Constructor, key, val) { + var proto = Constructor.prototype; + proto[key] = proto[key] || val; +}; + +// + if (typeof Symbol === "undefined") { require("es6-symbol/implement"); } require("es6-shim"); require("./transformation/transformers/es6-generators/runtime"); + +// Abstract references + +ensureSymbol("referenceGet"); +ensureSymbol("referenceSet"); +ensureSymbol("referenceDelete"); + +ensureProto(Function, Symbol.referenceGet, function () { return this }); + +ensureProto(Map, Symbol.referenceGet, Map.prototype.get); +ensureProto(Map, Symbol.referenceSet, Map.prototype.set); +ensureProto(Map, Symbol.referenceDelete, Map.prototype.delete); + +if (global.WeakMap) { + ensureProto(WeakMap, Symbol.referenceGet, WeakMap.prototype.get); + ensureProto(WeakMap, Symbol.referenceSet, WeakMap.prototype.set); + ensureProto(WeakMap, Symbol.referenceDelete, WeakMap.prototype.delete); +} diff --git a/lib/6to5/templates/abstract-expression-call.js b/lib/6to5/templates/abstract-expression-call.js new file mode 100644 index 0000000000..622b0588aa --- /dev/null +++ b/lib/6to5/templates/abstract-expression-call.js @@ -0,0 +1 @@ +PROPERTY[Symbol.referenceGet](OBJECT).call(OBJECT) diff --git a/lib/6to5/templates/abstract-expression-delete.js b/lib/6to5/templates/abstract-expression-delete.js new file mode 100644 index 0000000000..a78f195e1f --- /dev/null +++ b/lib/6to5/templates/abstract-expression-delete.js @@ -0,0 +1 @@ +PROPERTY[Symbol.referenceDelete](OBJECT) diff --git a/lib/6to5/templates/abstract-expression-get.js b/lib/6to5/templates/abstract-expression-get.js new file mode 100644 index 0000000000..bc8a623e9c --- /dev/null +++ b/lib/6to5/templates/abstract-expression-get.js @@ -0,0 +1 @@ +PROPERTY[Symbol.referenceGet](OBJECT) diff --git a/lib/6to5/templates/abstract-expression-set.js b/lib/6to5/templates/abstract-expression-set.js new file mode 100644 index 0000000000..90a5edd887 --- /dev/null +++ b/lib/6to5/templates/abstract-expression-set.js @@ -0,0 +1 @@ +PROPERTY[Symbol.referenceSet](OBJECT, VALUE) diff --git a/lib/6to5/transformation/transform.js b/lib/6to5/transformation/transform.js index d5f36ab759..67fa614fa9 100644 --- a/lib/6to5/transformation/transform.js +++ b/lib/6to5/transformation/transform.js @@ -46,6 +46,7 @@ _.each({ destructuring: require("./transformers/es6-destructuring"), forOf: require("./transformers/es6-for-of"), unicodeRegex: require("./transformers/es6-unicode-regex"), + abstractReferences: require("./transformers/es7-abstract-references"), react: require("./transformers/react"), constants: require("./transformers/es6-constants"), diff --git a/lib/6to5/transformation/transformers/es7-abstract-references.js b/lib/6to5/transformation/transformers/es7-abstract-references.js new file mode 100644 index 0000000000..a8cd071237 --- /dev/null +++ b/lib/6to5/transformation/transformers/es7-abstract-references.js @@ -0,0 +1,60 @@ +// https://github.com/zenparsing/es-abstract-refs + +var util = require("../../util"); +var t = require("../../types"); + +var container = function (parent, call, ret) { + if (t.isExpressionStatement(parent)) { + // we don't need to worry about return values + return call; + } else { + return t.sequenceExpression([call, ret]); + } +}; + +exports.AssignmentExpression = function (node, parent) { + var left = node.left; + if (!t.isVirtualPropertyExpression(left)) return; + + var right = node.right; + + var call = util.template("abstract-expression-set", { + PROPERTY: left.property, + OBJECT: left.object, + VALUE: right + }); + + return container(parent, call, right); +}; + +exports.UnaryExpression = function (node, parent) { + var arg = node.argument; + if (!t.isVirtualPropertyExpression(arg)) return; + if (node.operator !== "delete") return; + + var call = util.template("abstract-expression-delete", { + PROPERTY: arg.property, + OBJECT: arg.object + }); + + return container(parent, call, t.literal(true)); +}; + +exports.CallExpression = function (node, parent) { + var callee = node.callee; + if (!t.isVirtualPropertyExpression(callee)) return; + + var call = util.template("abstract-expression-call", { + PROPERTY: callee.property, + OBJECT: callee.object + }); + call.arguments = call.arguments.concat(node.arguments); + return call; +}; + +exports.VirtualPropertyExpression = function (node) { + return util.template("abstract-expression-get", { + PROPERTY: node.property, + OBJECT: node.object + }); +}; diff --git a/lib/6to5/types/visitor-keys.json b/lib/6to5/types/visitor-keys.json index af3cf76b1d..44b32401de 100644 --- a/lib/6to5/types/visitor-keys.json +++ b/lib/6to5/types/visitor-keys.json @@ -1,76 +1,77 @@ { - "ArrayExpression": ["elements"], - "ArrayPattern": ["elements"], - "ArrowFunctionExpression": ["params", "defaults", "rest", "body"], - "AssignmentExpression": ["left", "right"], - "AwaitExpression": ["argument"], - "BinaryExpression": ["left", "right"], - "BlockStatement": ["body"], - "BreakStatement": ["label"], - "CallExpression": ["callee", "arguments"], - "CatchClause": ["param", "body"], - "ClassBody": ["body"], - "ClassDeclaration": ["id", "body", "superClass"], - "ClassExpression": ["id", "body", "superClass"], - "ComprehensionBlock": ["left", "right", "body"], - "ComprehensionExpression": ["filter", "blocks", "body"], - "ConditionalExpression": ["test", "consequent", "alternate"], - "ContinueStatement": ["label"], - "DebuggerStatement": [], - "DoWhileStatement": ["body", "test"], - "EmptyStatement": [], - "ExportBatchSpecifier": [], - "ExportDeclaration": ["declaration", "specifiers", "source"], - "ExportSpecifier": ["id", "name"], - "ExpressionStatement": ["expression"], - "File": ["program"], - "ForInStatement": ["left", "right", "body"], - "ForOfStatement": ["left", "right", "body"], - "ForStatement": ["init", "test", "update", "body"], - "FunctionDeclaration": ["id", "params", "defaults", "rest", "body"], - "FunctionExpression": ["id", "params", "defaults", "rest", "body"], - "Identifier": [], - "IfStatement": ["test", "consequent", "alternate"], - "ImportBatchSpecifier": ["id"], - "ImportDeclaration": ["specifiers", "source"], - "ImportSpecifier": ["id", "name"], - "LabeledStatement": ["label", "body"], - "Literal": [], - "LogicalExpression": ["left", "right"], - "MemberExpression": ["object", "property"], - "MethodDefinition": ["key", "value"], - "NewExpression": ["callee", "arguments"], - "ObjectExpression": ["properties"], - "ObjectPattern": ["properties"], - "ParenthesizedExpression": ["expression"], - "Program": ["body"], - "Property": ["key", "value"], - "ReturnStatement": ["argument"], - "SequenceExpression": ["expressions"], - "SpreadElement": ["argument"], - "SwitchCase": ["test", "consequent"], - "SwitchStatement": ["discriminant", "cases"], - "TaggedTemplateExpression": ["tag", "quasi"], - "TemplateElement": [], - "TemplateLiteral": ["quasis", "expressions"], - "ThisExpression": [], - "ThrowStatement": ["argument"], - "TryStatement": ["block", "handlers", "handler", "guardedHandlers", "finalizer"], - "UnaryExpression": ["argument"], - "UpdateExpression": ["argument"], - "VariableDeclaration": ["declarations"], - "VariableDeclarator": ["id", "init"], - "WhileStatement": ["test", "body"], - "WithStatement": ["object", "body"], - "XJSAttribute": ["name", "value"], - "XJSClosingElement": ["name"], - "XJSElement": ["openingElement", "closingElement", "children"], - "XJSEmptyExpression": [], - "XJSExpressionContainer": ["expression"], - "XJSIdentifier": [], - "XJSMemberExpression": ["object", "property"], - "XJSNamespacedName": ["namespace", "name"], - "XJSOpeningElement": ["name", "attributes"], - "XJSSpreadAttribute": ["argument"], - "YieldExpression": ["argument"] + "ArrayExpression": ["elements"], + "ArrayPattern": ["elements"], + "ArrowFunctionExpression": ["params", "defaults", "rest", "body"], + "AssignmentExpression": ["left", "right"], + "AwaitExpression": ["argument"], + "BinaryExpression": ["left", "right"], + "BlockStatement": ["body"], + "BreakStatement": ["label"], + "CallExpression": ["callee", "arguments"], + "CatchClause": ["param", "body"], + "ClassBody": ["body"], + "ClassDeclaration": ["id", "body", "superClass"], + "ClassExpression": ["id", "body", "superClass"], + "ComprehensionBlock": ["left", "right", "body"], + "ComprehensionExpression": ["filter", "blocks", "body"], + "ConditionalExpression": ["test", "consequent", "alternate"], + "ContinueStatement": ["label"], + "DebuggerStatement": [], + "DoWhileStatement": ["body", "test"], + "EmptyStatement": [], + "ExportBatchSpecifier": [], + "ExportDeclaration": ["declaration", "specifiers", "source"], + "ExportSpecifier": ["id", "name"], + "ExpressionStatement": ["expression"], + "File": ["program"], + "ForInStatement": ["left", "right", "body"], + "ForOfStatement": ["left", "right", "body"], + "ForStatement": ["init", "test", "update", "body"], + "FunctionDeclaration": ["id", "params", "defaults", "rest", "body"], + "FunctionExpression": ["id", "params", "defaults", "rest", "body"], + "Identifier": [], + "IfStatement": ["test", "consequent", "alternate"], + "ImportBatchSpecifier": ["id"], + "ImportDeclaration": ["specifiers", "source"], + "ImportSpecifier": ["id", "name"], + "LabeledStatement": ["label", "body"], + "Literal": [], + "LogicalExpression": ["left", "right"], + "MemberExpression": ["object", "property"], + "MethodDefinition": ["key", "value"], + "NewExpression": ["callee", "arguments"], + "ObjectExpression": ["properties"], + "ObjectPattern": ["properties"], + "ParenthesizedExpression": ["expression"], + "Program": ["body"], + "Property": ["key", "value"], + "ReturnStatement": ["argument"], + "SequenceExpression": ["expressions"], + "SpreadElement": ["argument"], + "SwitchCase": ["test", "consequent"], + "SwitchStatement": ["discriminant", "cases"], + "TaggedTemplateExpression": ["tag", "quasi"], + "TemplateElement": [], + "TemplateLiteral": ["quasis", "expressions"], + "ThisExpression": [], + "ThrowStatement": ["argument"], + "TryStatement": ["block", "handlers", "handler", "guardedHandlers", "finalizer"], + "UnaryExpression": ["argument"], + "UpdateExpression": ["argument"], + "VariableDeclaration": ["declarations"], + "VariableDeclarator": ["id", "init"], + "VirtualPropertyExpression": ["left", "right"], + "WhileStatement": ["test", "body"], + "WithStatement": ["object", "body"], + "XJSAttribute": ["name", "value"], + "XJSClosingElement": ["name"], + "XJSElement": ["openingElement", "closingElement", "children"], + "XJSEmptyExpression": [], + "XJSExpressionContainer": ["expression"], + "XJSIdentifier": [], + "XJSMemberExpression": ["object", "property"], + "XJSNamespacedName": ["namespace", "name"], + "XJSOpeningElement": ["name", "attributes"], + "XJSSpreadAttribute": ["argument"], + "YieldExpression": ["argument"] } diff --git a/package.json b/package.json index b75748aed5..ef64290cae 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "chokidar": "0.11.1", "source-map-support": "0.2.8", "esutils": "1.1.6", - "acorn-6to5": "0.9.1-4", + "acorn-6to5": "0.9.1-5", "estraverse": "1.8.0", "private": "0.1.6" }, diff --git a/test/fixtures/transformation/es7-abstract-references/call/actual.js b/test/fixtures/transformation/es7-abstract-references/call/actual.js new file mode 100644 index 0000000000..ced7e8054a --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/call/actual.js @@ -0,0 +1,2 @@ +foo::bar(); +foo::bar("arg"); diff --git a/test/fixtures/transformation/es7-abstract-references/call/expected.js b/test/fixtures/transformation/es7-abstract-references/call/expected.js new file mode 100644 index 0000000000..9906be863c --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/call/expected.js @@ -0,0 +1,4 @@ +"use strict"; + +bar[Symbol.referenceGet](foo).call(foo); +bar[Symbol.referenceGet](foo).call(foo, "arg"); diff --git a/test/fixtures/transformation/es7-abstract-references/delete/actual.js b/test/fixtures/transformation/es7-abstract-references/delete/actual.js new file mode 100644 index 0000000000..47c17e0269 --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/delete/actual.js @@ -0,0 +1,3 @@ +delete foo::bar; + +if (delete foo::bar); diff --git a/test/fixtures/transformation/es7-abstract-references/delete/expected.js b/test/fixtures/transformation/es7-abstract-references/delete/expected.js new file mode 100644 index 0000000000..38b7e10af1 --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/delete/expected.js @@ -0,0 +1,5 @@ +"use strict"; + +bar[Symbol.referenceDelete](foo); + +if ((bar[Symbol.referenceDelete](foo), true)) ; diff --git a/test/fixtures/transformation/es7-abstract-references/get/actual.js b/test/fixtures/transformation/es7-abstract-references/get/actual.js new file mode 100644 index 0000000000..d6a275fb29 --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/get/actual.js @@ -0,0 +1 @@ +foo::bar; diff --git a/test/fixtures/transformation/es7-abstract-references/get/expected.js b/test/fixtures/transformation/es7-abstract-references/get/expected.js new file mode 100644 index 0000000000..70419d5d74 --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/get/expected.js @@ -0,0 +1,3 @@ +"use strict"; + +bar[Symbol.referenceGet](foo); diff --git a/test/fixtures/transformation/es7-abstract-references/options.json b/test/fixtures/transformation/es7-abstract-references/options.json new file mode 100644 index 0000000000..252f473a73 --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/options.json @@ -0,0 +1,3 @@ +{ + "experimental": true +} diff --git a/test/fixtures/transformation/es7-abstract-references/set/actual.js b/test/fixtures/transformation/es7-abstract-references/set/actual.js new file mode 100644 index 0000000000..3f18c6b93b --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/set/actual.js @@ -0,0 +1,2 @@ +foo::bar = baz; +if (foo::bar = baz); diff --git a/test/fixtures/transformation/es7-abstract-references/set/expected.js b/test/fixtures/transformation/es7-abstract-references/set/expected.js new file mode 100644 index 0000000000..fd1b67fb34 --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/set/expected.js @@ -0,0 +1,4 @@ +"use strict"; + +bar[Symbol.referenceSet](foo, baz); +if ((bar[Symbol.referenceSet](foo, baz), baz)) ;