From 82c18a837df8970de5901e079df03d5e0d42d015 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Sat, 31 Jan 2015 17:59:30 +1100 Subject: [PATCH] add detection skeleton #631 --- lib/6to5/detection/detect-visitors.js | 54 ++++++++++ lib/6to5/detection/detect.js | 31 ++++++ lib/6to5/detection/index.js | 7 -- lib/6to5/detection/syntax-keys.json | 84 +++++++++++++++ lib/6to5/transformation/transformer.js | 12 +-- lib/6to5/traverse/index.js | 14 +++ test/detection.js | 140 +++++++++++++++++++++++++ 7 files changed, 327 insertions(+), 15 deletions(-) create mode 100644 lib/6to5/detection/detect-visitors.js create mode 100644 lib/6to5/detection/detect.js delete mode 100644 lib/6to5/detection/index.js create mode 100644 lib/6to5/detection/syntax-keys.json create mode 100644 test/detection.js diff --git a/lib/6to5/detection/detect-visitors.js b/lib/6to5/detection/detect-visitors.js new file mode 100644 index 0000000000..696f25607f --- /dev/null +++ b/lib/6to5/detection/detect-visitors.js @@ -0,0 +1,54 @@ +var t = require("../types"); +var _ = require("lodash"); + +exports.AssignmentExpression = function (node, parent, detected) { + if (node.operator === "**=") { + detected("es6.exponentation"); + } +}; + +exports.BinaryExpression = function (node, parent, detected) { + if (node.operator === "**") { + detected("es6.exponentation"); + } +}; + +exports.VariableDeclaration = function (node, parent, detected) { + if (node.kind === "let" || node.kind === "const") { + detected("es6.blockScoping"); + } + + if (node.kind === "const") { + detected("es6.constants"); + } +}; + +exports.Property = function (node, parent, detected) { + if (node.shorthand || node.method) { + detected("es6.properties.shorthand"); + } + + if (node.kind === "set" || node.kind === "get") { + detected("es5.properties.mutators"); + } + + if (node.computed) { + detected("es6.properties.computed"); + } +}; + +exports.AssignmentPattern = function (node, parent, detected) { + if (t.isFunction(parent) && _.contains(parent.params, node)) { + detected("es6.parameters.default"); + } +}; + +exports.Function = function (node, parent, detected) { + if (node.generator) { + detected("es6.generators"); + } + + if (node.async) { + detected("es7.asyncFunctions"); + } +}; diff --git a/lib/6to5/detection/detect.js b/lib/6to5/detection/detect.js new file mode 100644 index 0000000000..b6e9596c7c --- /dev/null +++ b/lib/6to5/detection/detect.js @@ -0,0 +1,31 @@ +module.exports = detect; + +var useragent = require("useragent"); +var traverse = require("../traverse"); + +var SYNTAX_KEYS = require("./syntax-keys"); +var visitors = traverse.explode(require("./detect-visitors")); + +function detect(ast) { + var stats = { + syntax: {}, + builtins: {} + }; + + var detectedSyntax = function (name) { + stats.syntax[name] = true; + }; + + traverse(ast, { + enter: function (node, parent) { + if (SYNTAX_KEYS[node.type]) { + detectedSyntax(SYNTAX_KEYS[node.type]); + } + + var visitor = visitors[node.type]; + if (visitor) visitor(node, parent, detectedSyntax); + } + }); + + return stats; +} diff --git a/lib/6to5/detection/index.js b/lib/6to5/detection/index.js deleted file mode 100644 index 86a573d70e..0000000000 --- a/lib/6to5/detection/index.js +++ /dev/null @@ -1,7 +0,0 @@ -//exports.canRun = function (ast, userAgent) { - -//}; - -//exports.whoCanRun = function (ast, userAgent) { - -//}; diff --git a/lib/6to5/detection/syntax-keys.json b/lib/6to5/detection/syntax-keys.json new file mode 100644 index 0000000000..07756104d4 --- /dev/null +++ b/lib/6to5/detection/syntax-keys.json @@ -0,0 +1,84 @@ +{ + "ArrowFunctionExpression": "es6.arrowFunctions", + + "AwaitExpression": "es7.asyncFunctions", + + "ClassBody": "es6.classes", + "ClassDeclaration": "es6.classes", + "ClassExpression": "es6.classes", + "MethodDefinition": "es6.classes", + + "ComprehensionBlock": "es7.comprehensions", + "ComprehensionExpression": "es7.comprehensions", + + "ForOfStatement": "es6.forOf", + + "ExportBatchSpecifier": "es6.modules", + "ExportDeclaration": "es6.modules", + "ExportSpecifier": "es6.modules", + "ImportBatchSpecifier": "es6.modules", + "ImportDeclaration": "es6.modules", + "ImportSpecifier": "es6.modules", + + "ArrayPattern": "es6.destructuring", + "AssignmentPattern": "es6.destructuring", + "ObjectPattern": "es6.destructuring", + + "RestElement": "es6.parameters.rest", + + "SpreadElement": "es6.spread", + + "SpreadProperty": "es7.objectSpread", + + "TaggedTemplateExpression": "es6.templateLiterals", + "TemplateElement": "es6.templateLiterals", + "TemplateLiteral": "es6.templateLiterals", + + "VirtualPropertyExpression": "es7.abstractReferences", + "PrivateDeclaration": "es7.abstractReferences", + + "YieldExpression": "es6.generators", + + "AnyTypeAnnotation": "flow", + "ArrayTypeAnnotation": "flow", + "BooleanTypeAnnotation": "flow", + "ClassProperty": "flow", + "DeclareClass": "flow", + "DeclareFunction": "flow", + "DeclareModule": "flow", + "DeclareVariable": "flow", + "FunctionTypeAnnotation": "flow", + "FunctionTypeParam": "flow", + "GenericTypeAnnotation": "flow", + "InterfaceExtends": "flow", + "InterfaceDeclaration": "flow", + "IntersectionTypeAnnotation": "flow", + "NullableTypeAnnotation": "flow", + "NumberTypeAnnotation": "flow", + "StringLiteralTypeAnnotation": "flow", + "StringTypeAnnotation": "flow", + "TupleTypeAnnotation": "flow", + "TypeofTypeAnnotation": "flow", + "TypeAlias": "flow", + "TypeAnnotation": "flow", + "TypeParameterDeclaration": "flow", + "TypeParameterInstantiation": "flow", + "ObjectTypeAnnotation": "flow", + "ObjectTypeCallProperty": "flow", + "ObjectTypeIndexer": "flow", + "ObjectTypeProperty": "flow", + "QualifiedTypeIdentifier": "flow", + "UnionTypeAnnotation": "flow", + "VoidTypeAnnotation": "flow", + + "JSXAttribute": "jsx", + "JSXClosingElement": "jsx", + "JSXElement": "jsx", + "JSXEmptyExpression": "jsx", + "JSXExpressionContainer": "jsx", + "JSXIdentifier": "jsx", + "JSXMemberExpression": "jsx", + "JSXNamespacedName": "jsx", + "JSXOpeningElement": "jsx", + "JSXSpreadAttribute": "jsx" +} diff --git a/lib/6to5/transformation/transformer.js b/lib/6to5/transformation/transformer.js index 591c9a35da..a05821cfa6 100644 --- a/lib/6to5/transformation/transformer.js +++ b/lib/6to5/transformation/transformer.js @@ -3,10 +3,11 @@ module.exports = Transformer; var TransformerPass = require("./transformer-pass"); -var t = require("../types"); var isFunction = require("lodash/lang/isFunction"); +var traverse = require("../traverse"); var isObject = require("lodash/lang/isObject"); var each = require("lodash/collection/each"); +var t = require("../types"); /** * This is the class responsible for normalising a transformers handlers @@ -31,6 +32,8 @@ Transformer.prototype.normalise = function (transformer) { transformer = { ast: transformer }; } + traverse.explode(transformer); + each(transformer, function (fns, type) { // hidden property if (type[0] === "_") { @@ -46,13 +49,6 @@ Transformer.prototype.normalise = function (transformer) { if (!fns.exit) fns.exit = function () { }; transformer[type] = fns; - - var aliases = t.FLIPPED_ALIAS_KEYS[type]; - if (aliases) { - each(aliases, function (alias) { - transformer[alias] = fns; - }); - } }); return transformer; diff --git a/lib/6to5/traverse/index.js b/lib/6to5/traverse/index.js index 2dbdc8089c..d34b8155c8 100644 --- a/lib/6to5/traverse/index.js +++ b/lib/6to5/traverse/index.js @@ -237,6 +237,20 @@ traverse.removeProperties = function (tree) { return tree; }; +traverse.explode = function (obj) { + for (var type in obj) { + var fns = obj[type]; + + var aliases = t.FLIPPED_ALIAS_KEYS[type]; + if (aliases) { + for (var i = 0; i < aliases.length; i++) { + obj[aliases[i]] = fns; + } + } + } + return obj; +}; + function hasBlacklistedType(node, parent, scope, context, state) { if (node.type === state.type) { state.has = true; diff --git a/test/detection.js b/test/detection.js new file mode 100644 index 0000000000..53af975992 --- /dev/null +++ b/test/detection.js @@ -0,0 +1,140 @@ +var detect = require("../lib/6to5/detection/detect"); +var assert = require("assert"); +var util = require("../lib/6to5/util"); + +suite("detection", function () { + var checkSyntax = function (code, name) { + var ast = util.parse({ + experimental: true + }, code); + + assert.ok(detect(ast).syntax[name]); + }; + + test("es5.properties.mutators", function () { + checkSyntax("var obj = { get foo() {} };", "es5.properties.mutators"); + checkSyntax("var obj = { set foo() {} };", "es5.properties.mutators"); + }); + + test("es6.exponentation", function () { + checkSyntax("x ** 2;", "es6.exponentation"); + checkSyntax("x **= 2;", "es6.exponentation"); + }); + + test("es6.blockScoping", function () { + checkSyntax("let foo;", "es6.blockScoping"); + checkSyntax("let foo = bar;", "es6.blockScoping"); + checkSyntax("const foo = bar;", "es6.blockScoping"); + }); + + test("es6.constants", function () { + checkSyntax("const foo = bar;", "es6.constants"); + }); + + test("es6.properties.shorthand", function () { + checkSyntax("var obj = { foo };", "es6.properties.shorthand"); + checkSyntax("var obj = { foo };", "es6.properties.shorthand"); + }); + + test("es6.properties.computed", function () { + checkSyntax("var obj = { [foo]: bar };", "es6.properties.computed"); + checkSyntax("var obj = { ['foo']: bar };", "es6.properties.computed"); + }); + + test("es6.parameters.default", function () { + checkSyntax("var obj = (foo = bar) => {};", "es6.parameters.default"); + checkSyntax("var obj = function (foo = bar) {};", "es6.parameters.default"); + checkSyntax("function foo(foo = bar) {}", "es6.parameters.default"); + }); + + test("es6.arrowFunctions", function () { + checkSyntax("var foo = x => x;", "es6.arrowFunctions"); + checkSyntax("var foo = x => { return x * x };", "es6.arrowFunctions"); + checkSyntax("var foo = (x) => x;", "es6.arrowFunctions"); + checkSyntax("var foo = (a, b) => { return a * b };", "es6.arrowFunctions"); + }); + + test("es6.classes", function () { + checkSyntax("class Foo {}", "es6.classes"); + checkSyntax("var Foo = class {};", "es6.classes"); + }); + + test("es6.forOf", function () { + checkSyntax("for (var val of foo);", "es6.forOf"); + checkSyntax("for (val of foo);", "es6.forOf"); + }); + + test("es6.modules", function () { + checkSyntax("import 'foo';", "es6.modules"); + checkSyntax("import foo from 'foo';", "es6.modules"); + checkSyntax("import * as foo from 'foo';", "es6.modules"); + checkSyntax("import { foo } from 'foo';", "es6.modules"); + checkSyntax("export { foo } from 'foo';", "es6.modules"); + checkSyntax("export var foo = 5;", "es6.modules"); + checkSyntax("export class Foo {}", "es6.modules"); + checkSyntax("export function foo() {}", "es6.modules"); + checkSyntax("export default class Foo {}", "es6.modules"); + checkSyntax("export default function foo() {}", "es6.modules"); + }); + + test("es6.destructuring", function () { + checkSyntax("[a, b] = [];", "es6.destructuring"); + checkSyntax("var [a, b] = [];", "es6.destructuring"); + checkSyntax("({ a, b }) = {};", "es6.destructuring"); + checkSyntax("var { a, b } = {};", "es6.destructuring"); + checkSyntax("function foo(foo = bar) {}", "es6.destructuring"); + }); + + test("es6.parameters.rest", function () { + checkSyntax("function foo(...items) {}", "es6.parameters.rest"); + checkSyntax("var foo = (...items) => {}", "es6.parameters.rest"); + }); + + test("es6.spread", function () { + checkSyntax("new Foo(...items);", "es6.spread"); + checkSyntax("foo(...items);", "es6.spread"); + checkSyntax("[...items];", "es6.spread"); + }); + + test("es6.templateLiterals", function () { + checkSyntax("`foobar`;", "es6.templateLiterals"); + checkSyntax("foobar`foobar`;", "es6.templateLiterals"); + }); + + test("es6.generators", function () { + checkSyntax("function* foo() {}", "es6.generators"); + checkSyntax("var foo = function* () {};", "es6.generators"); + }); + + test("es7.asyncFunctions", function () { + checkSyntax("async function foo() {}", "es7.asyncFunctions"); + checkSyntax("var foo = async function() {};", "es7.asyncFunctions"); + checkSyntax("var foo = async () => {};", "es7.asyncFunctions"); + }); + + test("es7.comprehensions", function () { + checkSyntax("[for (i of test) i]", "es7.comprehensions"); + checkSyntax("(for (i of test) i)", "es7.comprehensions"); + }); + + test("es7.objectSpread", function () { + checkSyntax("var foo = { ...bar };", "es7.objectSpread"); + }); + + test("es7.abstractReferences", function () { + checkSyntax("class Foo { private A; }", "es7.abstractReferences"); + checkSyntax("foo::bar();", "es7.abstractReferences"); + checkSyntax("delete foo::bar;", "es7.abstractReferences"); + checkSyntax("foo::bar;", "es7.abstractReferences"); + checkSyntax("foo::bar = baz;", "es7.abstractReferences"); + }); + + test("flow", function () { + + }); + + test("jsx", function () { + checkSyntax("
", "jsx"); + checkSyntax("", "jsx"); + }); +});