From b8a80364df7894f4d8450a4ebe996f8a0358d90b Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Sun, 19 Oct 2014 19:46:18 +1100 Subject: [PATCH] Revert "remove jsx and react transformers" This reverts commit 4241227dbe683a529745ee6eb1107d0e25b3e555. --- lib/6to5/transform.js | 3 + lib/6to5/transformers/jsx/index.js | 99 +++++++++++++ lib/6to5/transformers/jsx/known-tags.json | 132 ++++++++++++++++++ lib/6to5/transformers/react.js | 63 +++++++++ test/fixtures/syntax/jsx/annotation/actual.js | 3 + .../syntax/jsx/annotation/expected.js | 2 + test/fixtures/syntax/jsx/empty/actual.js | 1 + test/fixtures/syntax/jsx/empty/expected.js | 2 + test/fixtures/syntax/jsx/everything/actual.js | 2 + .../syntax/jsx/everything/expected.js | 7 + .../fixtures/syntax/jsx/expressions/actual.js | 5 + .../syntax/jsx/expressions/expected.js | 8 ++ test/fixtures/syntax/jsx/known-tags/actual.js | 1 + .../syntax/jsx/known-tags/expected.js | 2 + .../syntax/jsx/member-expression/actual.js | 1 + .../syntax/jsx/member-expression/expected.js | 2 + .../syntax/jsx/no-xml-namespaces/actual.js | 1 + .../syntax/jsx/no-xml-namespaces/options.json | 3 + .../syntax/jsx/self-closing-tags/actual.js | 3 + .../syntax/jsx/self-closing-tags/expected.js | 6 + .../fixtures/syntax/jsx/simple-tags/actual.js | 1 + .../syntax/jsx/simple-tags/expected.js | 2 + .../syntax/jsx/tags-with-children/actual.js | 3 + .../syntax/jsx/tags-with-children/expected.js | 9 ++ .../syntax/jsx/tags-with-literals/actual.js | 13 ++ .../syntax/jsx/tags-with-literals/expected.js | 5 + .../actual.js | 6 + .../expected.js | 10 ++ .../react/display-name-if-missing/actual.js | 6 + .../react/display-name-if-missing/expected.js | 9 ++ .../display-name-object-declaration/actual.js | 7 + .../expected.js | 11 ++ .../actual.js | 5 + .../expected.js | 9 ++ .../actual.js | 5 + .../expected.js | 9 ++ 36 files changed, 456 insertions(+) create mode 100644 lib/6to5/transformers/jsx/index.js create mode 100644 lib/6to5/transformers/jsx/known-tags.json create mode 100644 lib/6to5/transformers/react.js create mode 100644 test/fixtures/syntax/jsx/annotation/actual.js create mode 100644 test/fixtures/syntax/jsx/annotation/expected.js create mode 100644 test/fixtures/syntax/jsx/empty/actual.js create mode 100644 test/fixtures/syntax/jsx/empty/expected.js create mode 100644 test/fixtures/syntax/jsx/everything/actual.js create mode 100644 test/fixtures/syntax/jsx/everything/expected.js create mode 100644 test/fixtures/syntax/jsx/expressions/actual.js create mode 100644 test/fixtures/syntax/jsx/expressions/expected.js create mode 100644 test/fixtures/syntax/jsx/known-tags/actual.js create mode 100644 test/fixtures/syntax/jsx/known-tags/expected.js create mode 100644 test/fixtures/syntax/jsx/member-expression/actual.js create mode 100644 test/fixtures/syntax/jsx/member-expression/expected.js create mode 100644 test/fixtures/syntax/jsx/no-xml-namespaces/actual.js create mode 100644 test/fixtures/syntax/jsx/no-xml-namespaces/options.json create mode 100644 test/fixtures/syntax/jsx/self-closing-tags/actual.js create mode 100644 test/fixtures/syntax/jsx/self-closing-tags/expected.js create mode 100644 test/fixtures/syntax/jsx/simple-tags/actual.js create mode 100644 test/fixtures/syntax/jsx/simple-tags/expected.js create mode 100644 test/fixtures/syntax/jsx/tags-with-children/actual.js create mode 100644 test/fixtures/syntax/jsx/tags-with-children/expected.js create mode 100644 test/fixtures/syntax/jsx/tags-with-literals/actual.js create mode 100644 test/fixtures/syntax/jsx/tags-with-literals/expected.js create mode 100644 test/fixtures/syntax/react/display-name-assignment-expression/actual.js create mode 100644 test/fixtures/syntax/react/display-name-assignment-expression/expected.js create mode 100644 test/fixtures/syntax/react/display-name-if-missing/actual.js create mode 100644 test/fixtures/syntax/react/display-name-if-missing/expected.js create mode 100644 test/fixtures/syntax/react/display-name-object-declaration/actual.js create mode 100644 test/fixtures/syntax/react/display-name-object-declaration/expected.js create mode 100644 test/fixtures/syntax/react/display-name-property-assignment/actual.js create mode 100644 test/fixtures/syntax/react/display-name-property-assignment/expected.js create mode 100644 test/fixtures/syntax/react/display-name-variable-declaration/actual.js create mode 100644 test/fixtures/syntax/react/display-name-variable-declaration/expected.js diff --git a/lib/6to5/transform.js b/lib/6to5/transform.js index 1f3a3e96f3..25bc7d8003 100644 --- a/lib/6to5/transform.js +++ b/lib/6to5/transform.js @@ -96,6 +96,9 @@ transform.transformers = { unicodeRegex: require("./transformers/unicode-regex"), generators: require("./transformers/generators"), + react: require("./transformers/react"), + jsx: require("./transformers/jsx"), + _aliasFunctions: require("./transformers/_alias-functions"), _blockHoist: require("./transformers/_block-hoist"), _declarations: require("./transformers/_declarations"), diff --git a/lib/6to5/transformers/jsx/index.js b/lib/6to5/transformers/jsx/index.js new file mode 100644 index 0000000000..f3e6477421 --- /dev/null +++ b/lib/6to5/transformers/jsx/index.js @@ -0,0 +1,99 @@ +// Based upon the excellent jsx-transpiler by Ingvar Stepanyan (RReverser) +// https://github.com/RReverser/jsx-transpiler + +var esutils = require("esutils"); +var b = require("recast").types.builders; +var _ = require("lodash"); + +var JSX_ANNOTATION_REGEX = /^\*\s*@jsx\s+([^\s]+)/; +var KNOWN_TAGS = require("./known-tags"); + +exports.Program = function (node, parent, file) { + var jsx = "React.DOM"; + + // looking for namespace annotation + _.each(node.comments, function (comment) { + if (!comment.possiblyLeading) return; + + var matches = JSX_ANNOTATION_REGEX.exec(comment.value); + if (matches) jsx = matches[1]; + }); + + // prebuilding AST node + file.jsx = jsx.split(".").map(b.identifier).reduce(function (object, property) { + return b.memberExpression(object, property, false); + }); +}; + +exports.XJSIdentifier = function (node) { + if (esutils.keyword.isIdentifierName(node.name)) { + node.type = "Identifier"; + } else { + return b.literal(node.name); + } +}; + +exports.XJSNamespacedName = function () { + throw new Error("Namespace tags are not supported. ReactJSX is not XML."); +}; + +exports.XJSMemberExpression = { + exit: function (node) { + node.computed = node.property.type === "Literal"; + node.type = "MemberExpression"; + } +}; + +exports.XJSEmptyExpression = function (node) { + node.value = null; + node.type = "Literal"; +}; + +exports.XJSExpressionContainer = function (node) { + return node.expression; +}; + +exports.XJSAttribute = { + exit: function (node) { + var value = node.value || b.literal(true); + var propNode = b.property("init", node.name, value); + propNode.loc = node.loc; + return propNode; + } +}; + +exports.XJSOpeningElement = { + exit: function (node, parent, file) { + var tagExpr = node.name; + + if (_.contains(KNOWN_TAGS, tagExpr.name)) { + tagExpr = b.memberExpression(file.jsx, tagExpr, false); + } + + var props = node.attributes; + if (props.length) { + props = b.objectExpression(props); + } else { + props = b.literal(null); + } + + return b.callExpression(tagExpr, [props]); + } +}; + +exports.XJSElement = { + exit: function (node) { + var callExpr = node.openingElement; + var children = node.children; + var args = callExpr.arguments; + + switch (children.length) { + case 0: break; + case 1: args.push(children[0]); break; + default: args.push(b.arrayExpression(children)); + } + + callExpr.loc = node.loc; + return callExpr; + } +}; diff --git a/lib/6to5/transformers/jsx/known-tags.json b/lib/6to5/transformers/jsx/known-tags.json new file mode 100644 index 0000000000..5a530c5bb9 --- /dev/null +++ b/lib/6to5/transformers/jsx/known-tags.json @@ -0,0 +1,132 @@ +[ + "a", + "abbr", + "address", + "applet", + "area", + "article", + "aside", + "audio", + "b", + "base", + "bdi", + "bdo", + "big", + "blockquote", + "body", + "br", + "button", + "canvas", + "caption", + "circle", + "cite", + "code", + "col", + "colgroup", + "command", + "data", + "datalist", + "dd", + "defs", + "del", + "details", + "dfn", + "dialog", + "div", + "dl", + "dt", + "ellipse", + "em", + "embed", + "fieldset", + "figcaption", + "figure", + "footer", + "form", + "g", + "h1", + "h2", + "h3", + "h4", + "h5", + "h6", + "head", + "header", + "hgroup", + "hr", + "html", + "i", + "iframe", + "img", + "input", + "ins", + "kbd", + "keygen", + "label", + "legend", + "li", + "line", + "linearGradient", + "link", + "main", + "map", + "mark", + "marquee", + "menu", + "menuitem", + "meta", + "meter", + "nav", + "noscript", + "object", + "ol", + "optgroup", + "option", + "output", + "p", + "param", + "path", + "polygon", + "polyline", + "pre", + "progress", + "q", + "radialGradient", + "rect", + "rp", + "rt", + "ruby", + "s", + "samp", + "script", + "section", + "select", + "small", + "source", + "span", + "stop", + "strong", + "style", + "sub", + "summary", + "sup", + "svg", + "table", + "tbody", + "td", + "text", + "textarea", + "tfoot", + "th", + "thead", + "time", + "title", + "tr", + "track", + "tspan", + "u", + "ul", + "var", + "video", + "wbr" +] diff --git a/lib/6to5/transformers/react.js b/lib/6to5/transformers/react.js new file mode 100644 index 0000000000..9a413abba7 --- /dev/null +++ b/lib/6to5/transformers/react.js @@ -0,0 +1,63 @@ +var b = require("recast").types.builders; +var _ = require("lodash"); + +var addDisplayName = function (id, call) { + if (!call || call.type !== "CallExpression") return; + + var callee = call.callee; + if (callee.type !== "MemberExpression") return; + + // not React + var obj = callee.object; + if (obj.type !== "Identifier" || obj.name !== "React") return; + + // not createClass + var prop = callee.property; + if (prop.type !== "Identifier" || prop.name !== "createClass") return; + + // no arguments + var args = call.arguments; + if (args.length !== 1) return; + + // not an object + var first = args[0]; + if (first.type !== "ObjectExpression") return; + + var props = first.properties; + var safe = true; + + _.each(props, function (prop) { + if (prop.key.name === "displayName") { + return safe = false; + } + }); + + if (safe) { + props.unshift(b.property("init", b.identifier("displayName"), b.literal(id))); + } +}; + +exports.AssignmentExpression = +exports.Property = +exports.VariableDeclarator = function (node) { + var left, right; + + if (node.type === "AssignmentExpression") { + left = node.left; + right = node.right; + } else if (node.type === "Property") { + left = node.key; + right = node.value; + } else if (node.type === "VariableDeclarator") { + left = node.id; + right = node.init; + } + + if (left && left.type === "MemberExpression") { + left = left.property; + } + + if (left && left.type === "Identifier") { + addDisplayName(left.name, right); + } +}; diff --git a/test/fixtures/syntax/jsx/annotation/actual.js b/test/fixtures/syntax/jsx/annotation/actual.js new file mode 100644 index 0000000000..2412fb7b42 --- /dev/null +++ b/test/fixtures/syntax/jsx/annotation/actual.js @@ -0,0 +1,3 @@ +/** @jsx CUSTOM_DOM */ + + diff --git a/test/fixtures/syntax/jsx/annotation/expected.js b/test/fixtures/syntax/jsx/annotation/expected.js new file mode 100644 index 0000000000..cd478b7097 --- /dev/null +++ b/test/fixtures/syntax/jsx/annotation/expected.js @@ -0,0 +1,2 @@ +"use strict"; +CUSTOM_DOM.a(null); \ No newline at end of file diff --git a/test/fixtures/syntax/jsx/empty/actual.js b/test/fixtures/syntax/jsx/empty/actual.js new file mode 100644 index 0000000000..c9f99abcd0 --- /dev/null +++ b/test/fixtures/syntax/jsx/empty/actual.js @@ -0,0 +1 @@ +{} diff --git a/test/fixtures/syntax/jsx/empty/expected.js b/test/fixtures/syntax/jsx/empty/expected.js new file mode 100644 index 0000000000..4835a2a251 --- /dev/null +++ b/test/fixtures/syntax/jsx/empty/expected.js @@ -0,0 +1,2 @@ +"use strict"; +x(null, null); \ No newline at end of file diff --git a/test/fixtures/syntax/jsx/everything/actual.js b/test/fixtures/syntax/jsx/everything/actual.js new file mode 100644 index 0000000000..0c42b3a8af --- /dev/null +++ b/test/fixtures/syntax/jsx/everything/actual.js @@ -0,0 +1,2 @@ + : +}> diff --git a/test/fixtures/syntax/jsx/everything/expected.js b/test/fixtures/syntax/jsx/everything/expected.js new file mode 100644 index 0000000000..7d39e64fbc --- /dev/null +++ b/test/fixtures/syntax/jsx/everything/expected.js @@ -0,0 +1,7 @@ +"use strict"; + +X({ + "data-prop": (x ? Y({ + prop: 2 + }) : Z(null, "\n")) +}); \ No newline at end of file diff --git a/test/fixtures/syntax/jsx/expressions/actual.js b/test/fixtures/syntax/jsx/expressions/actual.js new file mode 100644 index 0000000000..8191b76c06 --- /dev/null +++ b/test/fixtures/syntax/jsx/expressions/actual.js @@ -0,0 +1,5 @@ +({a}); + +({a} {b}); + +(); diff --git a/test/fixtures/syntax/jsx/expressions/expected.js b/test/fixtures/syntax/jsx/expressions/expected.js new file mode 100644 index 0000000000..d3818d86d3 --- /dev/null +++ b/test/fixtures/syntax/jsx/expressions/expected.js @@ -0,0 +1,8 @@ +"use strict"; +X(null, a); +X(null, [a, " ", b]); + +X({ + prop: a, + yes: true +}); \ No newline at end of file diff --git a/test/fixtures/syntax/jsx/known-tags/actual.js b/test/fixtures/syntax/jsx/known-tags/actual.js new file mode 100644 index 0000000000..41ab602321 --- /dev/null +++ b/test/fixtures/syntax/jsx/known-tags/actual.js @@ -0,0 +1 @@ + diff --git a/test/fixtures/syntax/jsx/known-tags/expected.js b/test/fixtures/syntax/jsx/known-tags/expected.js new file mode 100644 index 0000000000..ec70567216 --- /dev/null +++ b/test/fixtures/syntax/jsx/known-tags/expected.js @@ -0,0 +1,2 @@ +"use strict"; +React.DOM.a(null); \ No newline at end of file diff --git a/test/fixtures/syntax/jsx/member-expression/actual.js b/test/fixtures/syntax/jsx/member-expression/actual.js new file mode 100644 index 0000000000..67707269e9 --- /dev/null +++ b/test/fixtures/syntax/jsx/member-expression/actual.js @@ -0,0 +1 @@ + diff --git a/test/fixtures/syntax/jsx/member-expression/expected.js b/test/fixtures/syntax/jsx/member-expression/expected.js new file mode 100644 index 0000000000..4dea12440d --- /dev/null +++ b/test/fixtures/syntax/jsx/member-expression/expected.js @@ -0,0 +1,2 @@ +"use strict"; +Test.X(null); \ No newline at end of file diff --git a/test/fixtures/syntax/jsx/no-xml-namespaces/actual.js b/test/fixtures/syntax/jsx/no-xml-namespaces/actual.js new file mode 100644 index 0000000000..80e9ac6925 --- /dev/null +++ b/test/fixtures/syntax/jsx/no-xml-namespaces/actual.js @@ -0,0 +1 @@ + diff --git a/test/fixtures/syntax/jsx/no-xml-namespaces/options.json b/test/fixtures/syntax/jsx/no-xml-namespaces/options.json new file mode 100644 index 0000000000..84921377cb --- /dev/null +++ b/test/fixtures/syntax/jsx/no-xml-namespaces/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Namespace tags are not supported. ReactJSX is not XML." +} diff --git a/test/fixtures/syntax/jsx/self-closing-tags/actual.js b/test/fixtures/syntax/jsx/self-closing-tags/actual.js new file mode 100644 index 0000000000..c74cd039aa --- /dev/null +++ b/test/fixtures/syntax/jsx/self-closing-tags/actual.js @@ -0,0 +1,3 @@ +(); + +(); diff --git a/test/fixtures/syntax/jsx/self-closing-tags/expected.js b/test/fixtures/syntax/jsx/self-closing-tags/expected.js new file mode 100644 index 0000000000..3ebb174fab --- /dev/null +++ b/test/fixtures/syntax/jsx/self-closing-tags/expected.js @@ -0,0 +1,6 @@ +"use strict"; +X(null); + +X({ + prop: "1" +}); \ No newline at end of file diff --git a/test/fixtures/syntax/jsx/simple-tags/actual.js b/test/fixtures/syntax/jsx/simple-tags/actual.js new file mode 100644 index 0000000000..87779ff5c6 --- /dev/null +++ b/test/fixtures/syntax/jsx/simple-tags/actual.js @@ -0,0 +1 @@ + diff --git a/test/fixtures/syntax/jsx/simple-tags/expected.js b/test/fixtures/syntax/jsx/simple-tags/expected.js new file mode 100644 index 0000000000..f38433898c --- /dev/null +++ b/test/fixtures/syntax/jsx/simple-tags/expected.js @@ -0,0 +1,2 @@ +"use strict"; +X(null); \ No newline at end of file diff --git a/test/fixtures/syntax/jsx/tags-with-children/actual.js b/test/fixtures/syntax/jsx/tags-with-children/actual.js new file mode 100644 index 0000000000..aa8ed7080b --- /dev/null +++ b/test/fixtures/syntax/jsx/tags-with-children/actual.js @@ -0,0 +1,3 @@ +(); + +(); diff --git a/test/fixtures/syntax/jsx/tags-with-children/expected.js b/test/fixtures/syntax/jsx/tags-with-children/expected.js new file mode 100644 index 0000000000..1183f4a58a --- /dev/null +++ b/test/fixtures/syntax/jsx/tags-with-children/expected.js @@ -0,0 +1,9 @@ +"use strict"; + +X({ + prop: "2" +}, Y(null)); + +X({ + prop: "2" +}, [Y(null), Z(null)]); \ No newline at end of file diff --git a/test/fixtures/syntax/jsx/tags-with-literals/actual.js b/test/fixtures/syntax/jsx/tags-with-literals/actual.js new file mode 100644 index 0000000000..5507ef2680 --- /dev/null +++ b/test/fixtures/syntax/jsx/tags-with-literals/actual.js @@ -0,0 +1,13 @@ +( ); + +( +); + +( + string +); + +( + string + string + ); diff --git a/test/fixtures/syntax/jsx/tags-with-literals/expected.js b/test/fixtures/syntax/jsx/tags-with-literals/expected.js new file mode 100644 index 0000000000..956943003e --- /dev/null +++ b/test/fixtures/syntax/jsx/tags-with-literals/expected.js @@ -0,0 +1,5 @@ +"use strict"; +X(null, " "); +X(null, "\n"); +X(null, "\n string\n"); +X(null, "\n string\n string\n "); \ No newline at end of file diff --git a/test/fixtures/syntax/react/display-name-assignment-expression/actual.js b/test/fixtures/syntax/react/display-name-assignment-expression/actual.js new file mode 100644 index 0000000000..f3affde052 --- /dev/null +++ b/test/fixtures/syntax/react/display-name-assignment-expression/actual.js @@ -0,0 +1,6 @@ +var Component; +Component = React.createClass({ + render: function() { + return null; + } +}); diff --git a/test/fixtures/syntax/react/display-name-assignment-expression/expected.js b/test/fixtures/syntax/react/display-name-assignment-expression/expected.js new file mode 100644 index 0000000000..0096748e3d --- /dev/null +++ b/test/fixtures/syntax/react/display-name-assignment-expression/expected.js @@ -0,0 +1,10 @@ +"use strict"; +var Component; + +Component = React.createClass({ + displayName: "Component", + + render: function() { + return null; + } +}); \ No newline at end of file diff --git a/test/fixtures/syntax/react/display-name-if-missing/actual.js b/test/fixtures/syntax/react/display-name-if-missing/actual.js new file mode 100644 index 0000000000..4232881d66 --- /dev/null +++ b/test/fixtures/syntax/react/display-name-if-missing/actual.js @@ -0,0 +1,6 @@ +var Whateva = React.createClass({ + displayName: "Whatever", + render: function() { + return null; + } +}); diff --git a/test/fixtures/syntax/react/display-name-if-missing/expected.js b/test/fixtures/syntax/react/display-name-if-missing/expected.js new file mode 100644 index 0000000000..e802413800 --- /dev/null +++ b/test/fixtures/syntax/react/display-name-if-missing/expected.js @@ -0,0 +1,9 @@ +"use strict"; + +var Whateva = React.createClass({ + displayName: "Whatever", + + render: function() { + return null; + } +}); \ No newline at end of file diff --git a/test/fixtures/syntax/react/display-name-object-declaration/actual.js b/test/fixtures/syntax/react/display-name-object-declaration/actual.js new file mode 100644 index 0000000000..51a000a533 --- /dev/null +++ b/test/fixtures/syntax/react/display-name-object-declaration/actual.js @@ -0,0 +1,7 @@ +exports = { + Component: React.createClass({ + render: function() { + return null; + } + }) +}; diff --git a/test/fixtures/syntax/react/display-name-object-declaration/expected.js b/test/fixtures/syntax/react/display-name-object-declaration/expected.js new file mode 100644 index 0000000000..cde3afa785 --- /dev/null +++ b/test/fixtures/syntax/react/display-name-object-declaration/expected.js @@ -0,0 +1,11 @@ +"use strict"; + +exports = { + Component: React.createClass({ + displayName: "Component", + + render: function() { + return null; + } + }) +}; \ No newline at end of file diff --git a/test/fixtures/syntax/react/display-name-property-assignment/actual.js b/test/fixtures/syntax/react/display-name-property-assignment/actual.js new file mode 100644 index 0000000000..f856d33f07 --- /dev/null +++ b/test/fixtures/syntax/react/display-name-property-assignment/actual.js @@ -0,0 +1,5 @@ +exports.Component = React.createClass({ + render: function() { + return null; + } +}); diff --git a/test/fixtures/syntax/react/display-name-property-assignment/expected.js b/test/fixtures/syntax/react/display-name-property-assignment/expected.js new file mode 100644 index 0000000000..ca445b30a1 --- /dev/null +++ b/test/fixtures/syntax/react/display-name-property-assignment/expected.js @@ -0,0 +1,9 @@ +"use strict"; + +exports.Component = React.createClass({ + displayName: "Component", + + render: function() { + return null; + } +}); \ No newline at end of file diff --git a/test/fixtures/syntax/react/display-name-variable-declaration/actual.js b/test/fixtures/syntax/react/display-name-variable-declaration/actual.js new file mode 100644 index 0000000000..e751186def --- /dev/null +++ b/test/fixtures/syntax/react/display-name-variable-declaration/actual.js @@ -0,0 +1,5 @@ +var Component = React.createClass({ + render: function() { + return null; + } +}); diff --git a/test/fixtures/syntax/react/display-name-variable-declaration/expected.js b/test/fixtures/syntax/react/display-name-variable-declaration/expected.js new file mode 100644 index 0000000000..60d186b8dd --- /dev/null +++ b/test/fixtures/syntax/react/display-name-variable-declaration/expected.js @@ -0,0 +1,9 @@ +"use strict"; + +var Component = React.createClass({ + displayName: "Component", + + render: function() { + return null; + } +}); \ No newline at end of file