add optimisation.react.constantElements transformer - facebook/react#3228
This commit is contained in:
parent
3952eefd01
commit
ca5daca5dd
@ -80,7 +80,8 @@ export default class File {
|
||||
"object-destructuring-empty",
|
||||
"temporal-undefined",
|
||||
"temporal-assert-defined",
|
||||
"self-global"
|
||||
"self-global",
|
||||
"default-props"
|
||||
];
|
||||
|
||||
static options = require("./options");
|
||||
|
||||
@ -147,20 +147,7 @@ export default function (exports, opts) {
|
||||
exit(node) {
|
||||
var callExpr = node.openingElement;
|
||||
|
||||
for (var i = 0; i < node.children.length; i++) {
|
||||
var child = node.children[i];
|
||||
|
||||
if (t.isLiteral(child) && typeof child.value === "string") {
|
||||
cleanJSXElementLiteralChild(child, callExpr.arguments);
|
||||
continue;
|
||||
} else if (t.isJSXEmptyExpression(child)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
callExpr.arguments.push(child);
|
||||
}
|
||||
|
||||
callExpr.arguments = flatten(callExpr.arguments);
|
||||
callExpr.arguments = callExpr.arguments.concat(react.buildChildren(node));
|
||||
|
||||
if (callExpr.arguments.length >= 3) {
|
||||
callExpr._prettyCall = true;
|
||||
@ -170,69 +157,6 @@ export default function (exports, opts) {
|
||||
}
|
||||
};
|
||||
|
||||
var isStringLiteral = function (node) {
|
||||
return t.isLiteral(node) && isString(node.value);
|
||||
};
|
||||
|
||||
var flatten = function (args) {
|
||||
var flattened = [];
|
||||
var last;
|
||||
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
var arg = args[i];
|
||||
if (isStringLiteral(arg) && isStringLiteral(last)) {
|
||||
last.value += arg.value;
|
||||
} else {
|
||||
last = arg;
|
||||
flattened.push(arg);
|
||||
}
|
||||
}
|
||||
|
||||
return flattened;
|
||||
};
|
||||
|
||||
var cleanJSXElementLiteralChild = function (child, args) {
|
||||
var lines = child.value.split(/\r\n|\n|\r/);
|
||||
|
||||
var lastNonEmptyLine = 0;
|
||||
var i;
|
||||
|
||||
for (i = 0; i < lines.length; i++) {
|
||||
if (lines[i].match(/[^ \t]/)) {
|
||||
lastNonEmptyLine = i;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < lines.length; i++) {
|
||||
var line = lines[i];
|
||||
|
||||
var isFirstLine = i === 0;
|
||||
var isLastLine = i === lines.length - 1;
|
||||
var isLastNonEmptyLine = i === lastNonEmptyLine;
|
||||
|
||||
// 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) {
|
||||
if (!isLastNonEmptyLine) {
|
||||
trimmedLine += " ";
|
||||
}
|
||||
|
||||
args.push(t.literal(trimmedLine));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// display names
|
||||
|
||||
var addDisplayName = function (id, call) {
|
||||
|
||||
84
src/babel/transformation/helpers/react.js
vendored
84
src/babel/transformation/helpers/react.js
vendored
@ -1,3 +1,4 @@
|
||||
import isString from "lodash/lang/isString";
|
||||
import * as t from "../../types";
|
||||
|
||||
var isCreateClassCallExpression = t.buildMatchMemberExpression("React.createClass");
|
||||
@ -24,3 +25,86 @@ export var isReactComponent = t.buildMatchMemberExpression("React.Component");
|
||||
export function isCompatTag(tagName) {
|
||||
return tagName && /^[a-z]|\-/.test(tagName);
|
||||
}
|
||||
|
||||
function flattenChildren(args) {
|
||||
var flattened = [];
|
||||
var last;
|
||||
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
var arg = args[i];
|
||||
if (isStringLiteral(arg) && isStringLiteral(last)) {
|
||||
last.value += arg.value;
|
||||
} else {
|
||||
last = arg;
|
||||
flattened.push(arg);
|
||||
}
|
||||
}
|
||||
|
||||
return flattened;
|
||||
}
|
||||
|
||||
function isStringLiteral(node) {
|
||||
return t.isLiteral(node) && isString(node.value);
|
||||
}
|
||||
|
||||
function cleanJSXElementLiteralChild(child, args) {
|
||||
var lines = child.value.split(/\r\n|\n|\r/);
|
||||
|
||||
var lastNonEmptyLine = 0;
|
||||
var i;
|
||||
|
||||
for (i = 0; i < lines.length; i++) {
|
||||
if (lines[i].match(/[^ \t]/)) {
|
||||
lastNonEmptyLine = i;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < lines.length; i++) {
|
||||
var line = lines[i];
|
||||
|
||||
var isFirstLine = i === 0;
|
||||
var isLastLine = i === lines.length - 1;
|
||||
var isLastNonEmptyLine = i === lastNonEmptyLine;
|
||||
|
||||
// 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) {
|
||||
if (!isLastNonEmptyLine) {
|
||||
trimmedLine += " ";
|
||||
}
|
||||
|
||||
args.push(t.literal(trimmedLine));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function buildChildren(node) {
|
||||
var elems = [];
|
||||
|
||||
for (var i = 0; i < node.children.length; i++) {
|
||||
var child = node.children[i];
|
||||
if (t.isJSXExpressionContainer(child)) child = child.expression;
|
||||
|
||||
if (t.isLiteral(child) && typeof child.value === "string") {
|
||||
cleanJSXElementLiteralChild(child, elems);
|
||||
continue;
|
||||
} else if (t.isJSXEmptyExpression(child)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
elems.push(child);
|
||||
}
|
||||
|
||||
return flattenChildren(elems);
|
||||
}
|
||||
|
||||
10
src/babel/transformation/templates/helper-default-props.js
Normal file
10
src/babel/transformation/templates/helper-default-props.js
Normal file
@ -0,0 +1,10 @@
|
||||
(function (defaultProps, props) {
|
||||
if (defaultProps) {
|
||||
for (var propName in defaultProps) {
|
||||
if (typeof props[propName] === "undefined") {
|
||||
props[propName] = defaultProps[propName];
|
||||
}
|
||||
}
|
||||
}
|
||||
return props;
|
||||
})
|
||||
@ -19,6 +19,7 @@ export default {
|
||||
"spec.blockScopedFunctions": require("./spec/block-scoped-functions"),
|
||||
|
||||
"optimisation.react.constantElements": require("./optimisation/react.constant-elements"),
|
||||
"optimisation.react.inlineElements": require("./optimisation/react.inline-elements"),
|
||||
reactCompat: require("./other/react-compat"),
|
||||
react: require("./other/react"),
|
||||
|
||||
|
||||
@ -0,0 +1,70 @@
|
||||
import * as react from "../../helpers/react";
|
||||
import * as t from "../../../types";
|
||||
|
||||
export var metadata = {
|
||||
optional: true
|
||||
};
|
||||
|
||||
function hasRefOrSpread(attrs) {
|
||||
for (var i = 0; i < attrs.length; i++) {
|
||||
var attr = attrs[i];
|
||||
if (t.isJSXSpreadAttribute(attr)) return true;
|
||||
if (isJSXAttributeOfName(attr, "ref")) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isJSXAttributeOfName(attr, name) {
|
||||
return t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name, { name: name });
|
||||
}
|
||||
|
||||
export function JSXElement(node, parent, scope, file) {
|
||||
// filter
|
||||
var open = node.openingElement;
|
||||
if (hasRefOrSpread(open.attributes)) return;
|
||||
|
||||
// init
|
||||
var isComponent = true;
|
||||
var props = t.objectExpression([]);
|
||||
var obj = t.objectExpression([]);
|
||||
var key = t.literal(null);
|
||||
var type = open.name;
|
||||
|
||||
if (t.isJSXIdentifier(type) && react.isCompatTag(type.name)) {
|
||||
type = t.literal(type.name);
|
||||
isComponent = false;
|
||||
}
|
||||
|
||||
function pushElemProp(key, value) {
|
||||
obj.properties.push(t.property("init", t.identifier(key), value));
|
||||
}
|
||||
|
||||
// metadata
|
||||
pushElemProp("type", type);
|
||||
pushElemProp("ref", t.literal(null));
|
||||
|
||||
if (!open.selfClosing) {
|
||||
pushElemProp("children", t.arrayExpression(react.buildChildren(node)));
|
||||
}
|
||||
|
||||
// props
|
||||
for (var i = 0; i < open.attributes.length; i++) {
|
||||
var attr = open.attributes[i];
|
||||
if (isJSXAttributeOfName(attr, "key")) {
|
||||
key = attr.value;
|
||||
} else {
|
||||
props.properties.push(t.property("init", attr.name, attr.value));
|
||||
}
|
||||
}
|
||||
|
||||
if (isComponent) {
|
||||
props = t.callExpression(file.addHelper("default-props"), [t.memberExpression(type, t.identifier("defaultProps")), props]);
|
||||
}
|
||||
|
||||
pushElemProp("props", props);
|
||||
|
||||
// key
|
||||
pushElemProp("key", key);
|
||||
|
||||
return obj;
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
function render() {
|
||||
return <foo />;
|
||||
}
|
||||
|
||||
function render() {
|
||||
var text = getText();
|
||||
return function () {
|
||||
return <foo>{text}</foo>;
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
"use strict";
|
||||
|
||||
var _ref = {
|
||||
type: "foo",
|
||||
ref: null,
|
||||
props: {},
|
||||
key: null
|
||||
};
|
||||
function render() {
|
||||
return _ref;
|
||||
}
|
||||
|
||||
function render() {
|
||||
var text = getText();
|
||||
var _ref = {
|
||||
type: "foo",
|
||||
ref: null,
|
||||
children: [text],
|
||||
props: {},
|
||||
key: null
|
||||
};
|
||||
return function () {
|
||||
return _ref;
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"optional": ["optimisation.react.constantElements", "optimisation.react.inlineElements"]
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
<Baz foo="bar"></Baz>;
|
||||
@ -0,0 +1,11 @@
|
||||
"use strict";
|
||||
|
||||
({
|
||||
type: Baz,
|
||||
ref: null,
|
||||
children: [],
|
||||
props: babelHelpers.defaultProps(Baz.defaultProps, {
|
||||
foo: "bar"
|
||||
}),
|
||||
key: null
|
||||
});
|
||||
@ -0,0 +1 @@
|
||||
<Baz></Baz>;
|
||||
@ -0,0 +1,9 @@
|
||||
"use strict";
|
||||
|
||||
({
|
||||
type: Baz,
|
||||
ref: null,
|
||||
children: [],
|
||||
props: babelHelpers.defaultProps(Baz.defaultProps, {}),
|
||||
key: null
|
||||
});
|
||||
@ -0,0 +1 @@
|
||||
<foo bar="foo"></foo>;
|
||||
@ -0,0 +1,11 @@
|
||||
"use strict";
|
||||
|
||||
({
|
||||
type: "foo",
|
||||
ref: null,
|
||||
children: [],
|
||||
props: {
|
||||
bar: "foo"
|
||||
},
|
||||
key: null
|
||||
});
|
||||
@ -0,0 +1 @@
|
||||
<foo></foo>;
|
||||
@ -0,0 +1,9 @@
|
||||
"use strict";
|
||||
|
||||
({
|
||||
type: "foo",
|
||||
ref: null,
|
||||
children: [],
|
||||
props: {},
|
||||
key: null
|
||||
});
|
||||
@ -0,0 +1 @@
|
||||
<Foo key="foo" />
|
||||
@ -0,0 +1,8 @@
|
||||
"use strict";
|
||||
|
||||
({
|
||||
type: Foo,
|
||||
ref: null,
|
||||
props: babelHelpers.defaultProps(Foo.defaultProps, {}),
|
||||
key: "foo"
|
||||
});
|
||||
@ -0,0 +1 @@
|
||||
<Foo className="foo">{bar}<Baz key="baz" /></Foo>
|
||||
@ -0,0 +1,16 @@
|
||||
"use strict";
|
||||
|
||||
({
|
||||
type: Foo,
|
||||
ref: null,
|
||||
children: [bar, {
|
||||
type: Baz,
|
||||
ref: null,
|
||||
props: babelHelpers.defaultProps(Baz.defaultProps, {}),
|
||||
key: "baz"
|
||||
}],
|
||||
props: babelHelpers.defaultProps(Foo.defaultProps, {
|
||||
className: "foo"
|
||||
}),
|
||||
key: null
|
||||
});
|
||||
@ -0,0 +1 @@
|
||||
<div className="foo">{bar}</div>;
|
||||
@ -0,0 +1,11 @@
|
||||
"use strict";
|
||||
|
||||
({
|
||||
type: "div",
|
||||
ref: null,
|
||||
children: [bar],
|
||||
props: {
|
||||
className: "foo"
|
||||
},
|
||||
key: null
|
||||
});
|
||||
@ -0,0 +1 @@
|
||||
<div className="foo">{bar}<Baz key="baz" /></div>
|
||||
@ -0,0 +1,16 @@
|
||||
"use strict";
|
||||
|
||||
({
|
||||
type: "div",
|
||||
ref: null,
|
||||
children: [bar, {
|
||||
type: Baz,
|
||||
ref: null,
|
||||
props: babelHelpers.defaultProps(Baz.defaultProps, {}),
|
||||
key: "baz"
|
||||
}],
|
||||
props: {
|
||||
className: "foo"
|
||||
},
|
||||
key: null
|
||||
});
|
||||
@ -0,0 +1,5 @@
|
||||
{
|
||||
"externalHelpers": true,
|
||||
"noCheckAst": true,
|
||||
"optional": ["optimisation.react.inlineElements"]
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
<Foo ref="bar" />
|
||||
@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
|
||||
React.createElement(Foo, { ref: "bar" });
|
||||
@ -0,0 +1 @@
|
||||
<Baz foo="bar" />;
|
||||
@ -0,0 +1,10 @@
|
||||
"use strict";
|
||||
|
||||
({
|
||||
type: Baz,
|
||||
ref: null,
|
||||
props: babelHelpers.defaultProps(Baz.defaultProps, {
|
||||
foo: "bar"
|
||||
}),
|
||||
key: null
|
||||
});
|
||||
@ -0,0 +1 @@
|
||||
<Baz />;
|
||||
@ -0,0 +1,8 @@
|
||||
"use strict";
|
||||
|
||||
({
|
||||
type: Baz,
|
||||
ref: null,
|
||||
props: babelHelpers.defaultProps(Baz.defaultProps, {}),
|
||||
key: null
|
||||
});
|
||||
@ -0,0 +1 @@
|
||||
<foo bar="foo" />;
|
||||
@ -0,0 +1,10 @@
|
||||
"use strict";
|
||||
|
||||
({
|
||||
type: "foo",
|
||||
ref: null,
|
||||
props: {
|
||||
bar: "foo"
|
||||
},
|
||||
key: null
|
||||
});
|
||||
@ -0,0 +1 @@
|
||||
<foo />;
|
||||
@ -0,0 +1,8 @@
|
||||
"use strict";
|
||||
|
||||
({
|
||||
type: "foo",
|
||||
ref: null,
|
||||
props: {},
|
||||
key: null
|
||||
});
|
||||
@ -0,0 +1 @@
|
||||
<Foo {...bar} />
|
||||
@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
|
||||
React.createElement(Foo, bar);
|
||||
Loading…
x
Reference in New Issue
Block a user