From 415144a7f448b9450233d915740c72ed254728bd Mon Sep 17 00:00:00 2001 From: Samuel Reed Date: Sat, 5 Nov 2016 11:15:57 -0500 Subject: [PATCH] feature: Support pure expressions in transform-react-constant-elements --- .../src/index.js | 28 +++++++++++++++++-- .../constant-elements/pure-deopt/actual.js | 5 ++++ .../constant-elements/pure-deopt/expected.js | 5 ++++ .../pure-expression-2/actual.js | 5 ++++ .../pure-expression-2/expected.js | 8 ++++++ .../pure-expression-3/actual.js | 10 +++++++ .../pure-expression-3/expected.js | 10 +++++++ .../pure-expression/actual.js | 11 ++++++++ .../pure-expression/expected.js | 8 ++++++ 9 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-deopt/actual.js create mode 100644 packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-deopt/expected.js create mode 100644 packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-expression-2/actual.js create mode 100644 packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-expression-2/expected.js create mode 100644 packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-expression-3/actual.js create mode 100644 packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-expression-3/expected.js create mode 100644 packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-expression/actual.js create mode 100644 packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-expression/expected.js diff --git a/packages/babel-plugin-transform-react-constant-elements/src/index.js b/packages/babel-plugin-transform-react-constant-elements/src/index.js index a25fb67e6c6d..e9ff6950ec70 100644 --- a/packages/babel-plugin-transform-react-constant-elements/src/index.js +++ b/packages/babel-plugin-transform-react-constant-elements/src/index.js @@ -1,4 +1,4 @@ -export default function () { +export default function ({ types: t }) { const immutabilityVisitor = { enter(path, state) { const stop = () => { @@ -11,15 +11,39 @@ export default function () { return; } + // Elements with refs are not safe to hoist. if (path.isJSXIdentifier({ name: "ref" }) && path.parentPath.isJSXAttribute({ name: path.node })) { return stop(); } + // Ignore identifiers & JSX expressions. if (path.isJSXIdentifier() || path.isIdentifier() || path.isJSXMemberExpression()) { return; } - if (!path.isImmutable()) stop(); + if (!path.isImmutable()) { + // If it's not immutable, it may still be a pure expression. + if (path.isPure()) { + // If it's statically evaluatable, then evaluate it. + const expressionResult = path.evaluate(); + if (expressionResult.confident) { + const resultNode = t.valueToNode(expressionResult.value); + // If it evaluated to an immutable value, we can use it. + // If not, it is not safe to replace as mutable values could + // be mutated after render. + // https://github.com/facebook/react/issues/3226 + if (t.isImmutable(resultNode)) { + path.replaceWith(resultNode); + path.skip(); + return; + } + } else if (t.isIdentifier(expressionResult.deopt)) { + // It's safe to stop here if the deopt reason is an identifier. + return; + } + } + stop(); + } } }; diff --git a/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-deopt/actual.js b/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-deopt/actual.js new file mode 100644 index 000000000000..4df51832c7ce --- /dev/null +++ b/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-deopt/actual.js @@ -0,0 +1,5 @@ +// https://github.com/facebook/react/issues/3226 +// Not safe to reuse because it is mutable +function render() { + return
; +} diff --git a/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-deopt/expected.js b/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-deopt/expected.js new file mode 100644 index 000000000000..4df51832c7ce --- /dev/null +++ b/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-deopt/expected.js @@ -0,0 +1,5 @@ +// https://github.com/facebook/react/issues/3226 +// Not safe to reuse because it is mutable +function render() { + return
; +} diff --git a/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-expression-2/actual.js b/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-expression-2/actual.js new file mode 100644 index 000000000000..0acb75cfaf6a --- /dev/null +++ b/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-expression-2/actual.js @@ -0,0 +1,5 @@ +function render(offset) { + return function () { + return
; + }; +} diff --git a/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-expression-2/expected.js b/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-expression-2/expected.js new file mode 100644 index 000000000000..65df627806b2 --- /dev/null +++ b/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-expression-2/expected.js @@ -0,0 +1,8 @@ +function render(offset) { + var _ref =
; + + return function () { + return _ref; + }; +} + diff --git a/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-expression-3/actual.js b/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-expression-3/actual.js new file mode 100644 index 000000000000..c6b89c77fd1a --- /dev/null +++ b/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-expression-3/actual.js @@ -0,0 +1,10 @@ +const OFFSET = 3; + +var Foo = React.createClass({ + render: function () { + return ( +
+ ); + } +}); + diff --git a/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-expression-3/expected.js b/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-expression-3/expected.js new file mode 100644 index 000000000000..a494d251dc48 --- /dev/null +++ b/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-expression-3/expected.js @@ -0,0 +1,10 @@ +const OFFSET = 3; + +var _ref =
; + +var Foo = React.createClass({ + render: function () { + return _ref; + } +}); + diff --git a/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-expression/actual.js b/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-expression/actual.js new file mode 100644 index 000000000000..5131c839899b --- /dev/null +++ b/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-expression/actual.js @@ -0,0 +1,11 @@ +var Foo = React.createClass({ + render: function () { + return ( +
+ ); + } +}); + diff --git a/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-expression/expected.js b/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-expression/expected.js new file mode 100644 index 000000000000..4d5e06872e1b --- /dev/null +++ b/packages/babel-plugin-transform-react-constant-elements/test/fixtures/constant-elements/pure-expression/expected.js @@ -0,0 +1,8 @@ +var _ref =
; + +var Foo = React.createClass({ + render: function () { + return _ref; + } +}); +