From 8919873ea19e2e73871f7b870431e9784906e4a5 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Sat, 17 Jan 2015 18:26:14 +1100 Subject: [PATCH] clean up react/jsx transformer --- lib/6to5/transformation/transformers/react.js | 183 ++++++++++-------- 1 file changed, 100 insertions(+), 83 deletions(-) diff --git a/lib/6to5/transformation/transformers/react.js b/lib/6to5/transformation/transformers/react.js index 41b1ae4e14..4dc53db287 100644 --- a/lib/6to5/transformation/transformers/react.js +++ b/lib/6to5/transformation/transformers/react.js @@ -37,8 +37,8 @@ exports.XJSAttribute = { } }; -var isTag = function(tagName) { - return (/^[a-z]|\-/).test(tagName); +var isCompatTag = function(tagName) { + return /^[a-z]|\-/.test(tagName); }; exports.XJSOpeningElement = { @@ -55,7 +55,7 @@ exports.XJSOpeningElement = { } if (!reactCompat) { - if (tagName && isTag(tagName)) { + if (tagName && isCompatTag(tagName)) { args.push(t.literal(tagName)); } else { args.push(tagExpr); @@ -64,48 +64,7 @@ exports.XJSOpeningElement = { var attribs = node.attributes; if (attribs.length) { - var _props = []; - var objs = []; - - // so basically in order to support spread elements we - // loop over all the attributes, breaking on spreads - // we then push a new object containing all prior attributes - // to an array for later processing - - var pushProps = function () { - if (!_props.length) return; - - objs.push(t.objectExpression(_props)); - _props = []; - }; - - while (attribs.length) { - var prop = attribs.shift(); - if (t.isXJSSpreadAttribute(prop)) { - pushProps(); - objs.push(prop.argument); - } else { - _props.push(prop); - } - } - - pushProps(); - - if (objs.length === 1) { - // only one object - attribs = objs[0]; - } else { - // looks like we have multiple objects - if (!t.isObjectExpression(objs[0])) { - objs.unshift(t.objectExpression([])); - } - - // spread it - attribs = t.callExpression( - t.memberExpression(t.identifier("React"), t.identifier("__spread")), - objs - ); - } + attribs = buildXJSOpeningElementAttributes(attribs); } else { attribs = t.literal(null); } @@ -113,7 +72,7 @@ exports.XJSOpeningElement = { args.push(attribs); if (reactCompat) { - if (tagName && isTag(tagName)) { + if (tagName && isCompatTag(tagName)) { return t.callExpression( t.memberExpression( t.memberExpression(t.identifier("React"), t.identifier("DOM")), @@ -131,6 +90,55 @@ exports.XJSOpeningElement = { } }; +/** + * The logic for this is quite terse. It's because we need to + * support spread elements. We loop over all attributes, + * breaking on spreads, we then push a new object containg + * all prior attributes to an array for later processing. + */ + +var buildXJSOpeningElementAttributes = function (attribs) { + var _props = []; + var objs = []; + + var pushProps = function () { + if (!_props.length) return; + + objs.push(t.objectExpression(_props)); + _props = []; + }; + + while (attribs.length) { + var prop = attribs.shift(); + if (t.isXJSSpreadAttribute(prop)) { + pushProps(); + objs.push(prop.argument); + } else { + _props.push(prop); + } + } + + pushProps(); + + if (objs.length === 1) { + // only one object + attribs = objs[0]; + } else { + // looks like we have multiple objects + if (!t.isObjectExpression(objs[0])) { + objs.unshift(t.objectExpression([])); + } + + // spread it + attribs = t.callExpression( + t.memberExpression(t.identifier("React"), t.identifier("__spread")), + objs + ); + } + + return attribs; +}; + exports.XJSElement = { exit: function (node) { var callExpr = node.openingElement; @@ -139,32 +147,7 @@ exports.XJSElement = { var child = node.children[i]; if (t.isLiteral(child) && _.isString(child.value)) { - var lines = child.value.split(/\r\n|\n|\r/); - - for (var i2 = 0; i2 < lines.length; i2++) { - var line = lines[i2]; - - var isFirstLine = i2 === 0; - var isLastLine = i2 === lines.length - 1; - - // replace rendered whitespace tabs with spaces - var trimmedLine = line.replace(/\t/g, " "); - - // trim whitespace touching a newline - if (!isFirstLine) { - trimmedLine = trimmedLine.replace(/^[ ]+/, ""); - } - - // trim whitespace touching an endline - if (!isLastLine) { - trimmedLine = trimmedLine.replace(/[ ]+$/, ""); - } - - if (trimmedLine) { - callExpr.arguments.push(t.literal(trimmedLine)); - } - } - + cleanXJSElementLiteralChild(child, callExpr.arguments); continue; } else if (t.isXJSEmptyExpression(child)) { continue; @@ -181,31 +164,65 @@ exports.XJSElement = { } }; +var cleanXJSElementLiteralChild = function (child, args) { + var lines = child.value.split(/\r\n|\n|\r/); + + for (var i = 0; i < lines.length; i++) { + var line = lines[i]; + + var isFirstLine = i === 0; + var isLastLine = i === lines.length - 1; + + // replace rendered whitespace tabs with spaces + var trimmedLine = line.replace(/\t/g, " "); + + // trim whitespace touching a newline + if (!isFirstLine) { + trimmedLine = trimmedLine.replace(/^[ ]+/, ""); + } + + // trim whitespace touching an endline + if (!isLastLine) { + trimmedLine = trimmedLine.replace(/[ ]+$/, ""); + } + + if (trimmedLine) { + args.push(t.literal(trimmedLine)); + } + } +}; + // display names -var addDisplayName = function (id, call) { - if (!call || !t.isCallExpression(call)) return; +var isCreateClass = function (call) { + if (!call || !t.isCallExpression(call)) return false; var callee = call.callee; - if (!t.isMemberExpression(callee)) return; + if (!t.isMemberExpression(callee)) return false; - // not React + // not React call member object var obj = callee.object; - if (!t.isIdentifier(obj, { name: "React" })) return; + if (!t.isIdentifier(obj, { name: "React" })) return false; - // not createClass + // not createClass call member property var prop = callee.property; - if (!t.isIdentifier(prop, { name: "createClass" })) return; + if (!t.isIdentifier(prop, { name: "createClass" })) return false; - // no arguments + // no call arguments var args = call.arguments; - if (args.length !== 1) return; + if (args.length !== 1) return false; - // not an object + // first call arg is not an object var first = args[0]; if (!t.isObjectExpression(first)) return; - var props = first.properties; + return true; +}; + +var addDisplayName = function (id, call) { + if (!isCreateClass(call)) return; + + var props = call.arguments[0].properties; var safe = true; for (var i = 0; i < props.length; i++) {