Skip to content

Commit

Permalink
Use the correct this in __self for JSX elements in arrows (#11288)
Browse files Browse the repository at this point in the history
* Inject `__source` and `__self` in JSX elements earlier

This fixes an issue where `this` was not correct inside arrow functions, similar to
https://astexplorer.net/#/gist/86638d64cd21c496e7fecaea46313d95/906f8be24d7d99f3f5cd5a956a48355e1864f4b1

* Add test

* Remove try-catch

* Update error

* Update fixtures

* Update windows fixtures
  • Loading branch information
nicolo-ribaudo committed Mar 19, 2020
1 parent 0a02a12 commit 11292a3
Show file tree
Hide file tree
Showing 22 changed files with 221 additions and 250 deletions.
144 changes: 75 additions & 69 deletions packages/babel-helper-builder-react-jsx-experimental/src/index.js
Expand Up @@ -31,6 +31,32 @@ export function helper(babel, options) {
pragmaFrag: PRAGMA_FRAG_DEFAULT = DEFAULT.pragmaFrag,
} = options;

const injectMetaPropertiesVisitor = {
JSXOpeningElement(path, state) {
for (const attr of path.get("attributes")) {
if (!attr.isJSXElement()) continue;

const { name } = attr.node.name;
if (name === "__source" || name === "__self") {
throw path.buildCodeFrameError(
`__source and __self should not be defined in props and are reserved for internal usage.`,
);
}
}

const source = t.jsxAttribute(
t.jsxIdentifier("__source"),
t.jsxExpressionContainer(makeSource(path, state)),
);
const self = t.jsxAttribute(
t.jsxIdentifier("__self"),
t.jsxExpressionContainer(t.thisExpression()),
);

path.pushContainer("attributes", [source, self]);
},
};

return {
JSXNamespacedName(path, state) {
const throwIfNamespace =
Expand Down Expand Up @@ -214,6 +240,10 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
`Runtime must be either "classic" or "automatic".`,
);
}

if (options.development) {
path.traverse(injectMetaPropertiesVisitor, state);
}
}
},

Expand Down Expand Up @@ -409,7 +439,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
}

function makeSource(path, state) {
const location = path.node.openingElement.loc;
const location = path.node.loc;
if (!location) {
// the element was generated and doesn't have location information
return;
Expand Down Expand Up @@ -532,33 +562,28 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
}

let attribs = [];
let key;
let source;
let self;
const extracted = Object.create(null);

// for React.jsx, key, __source (dev), and __self (dev) is passed in as
// a separate argument rather than in the args object. We go through the
// props and filter out these three keywords so we can pass them in
// as separate arguments later
for (let i = 0; i < openingPath.node.attributes.length; i++) {
const attr = openingPath.node.attributes[i];
if (t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name)) {
if (attr.name.name === "key") {
key = convertAttribute(attr).value;
} else if (
attr.name.name === "__source" ||
attr.name.name === "__self"
) {
throw path.buildCodeFrameError(
`__source and __self should not be defined in props. You are most likely using the deprecated transform-react-jsx-self or transform-react-jsx-source Babel plugins. __source and __self will be set automatically in automatic runtime. Please remove transform-react-jsx-self or transform-react-jsx-source from your Babel config.`,
);
} else {
// If someone is still using the __source and __self Babel plugins
// filter the results out
attribs.push(attr);
for (const attr of openingPath.get("attributes")) {
if (attr.isJSXAttribute() && t.isJSXIdentifier(attr.node.name)) {
const { name } = attr.node.name;
switch (name) {
case "__source":
case "__self":
if (extracted[name]) throw sourceSelfError(path, name);
/* falls through */
case "key":
extracted[name] = convertAttributeValue(attr.node.value);
break;
default:
attribs.push(attr.node);
}
} else {
attribs.push(attr);
attribs.push(attr.node);
}
}

Expand All @@ -576,20 +601,18 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
args.push(attribs);

if (!options.development) {
if (key !== undefined) {
args.push(key);
if (extracted.key !== undefined) {
args.push(extracted.key);
}
} else {
// isStaticChildren, __source, and __self are only used in development
// automatically include __source and __self in this plugin
// so we can eliminate the need for separate Babel plugins in Babel 8
source = makeSource(path, file);
self = t.thisExpression();
args.push(
key === undefined ? path.scope.buildUndefinedNode() : key,
extracted.key ?? path.scope.buildUndefinedNode(),
t.booleanLiteral(path.node.children.length > 1),
source ?? path.scope.buildUndefinedNode(),
self,
extracted.__source ?? path.scope.buildUndefinedNode(),
extracted.__self ?? t.thisExpression(),
);
}

Expand All @@ -611,16 +634,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
// Builds props for React.jsx. This function adds children into the props
// and ensures that props is always an object
function buildJSXOpeningElementAttributes(attribs, file, children) {
const _attribs = attribs.filter(
prop =>
!(
t.isJSXAttribute(prop) &&
prop.name &&
(prop.name.name === "__source" || prop.name.name === "__self")
),
);

const props = _attribs.map(convertAttribute);
const props = attribs.map(convertAttribute);

// In React.jsx, children is no longer a separate argument, but passed in
// through the argument object
Expand Down Expand Up @@ -774,7 +788,6 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
const attribs = buildCreateElementOpeningElementAttributes(
path,
openingPath.node.attributes,
file,
);

args.push(attribs, ...path.node.children);
Expand All @@ -796,40 +809,33 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
* breaking on spreads, we then push a new object containing
* all prior attributes to an array for later processing.
*/
function buildCreateElementOpeningElementAttributes(path, attribs, file) {
// We want source and self to be automatically included in the future
// so we will error when we see it

const hasSourceSelf = attribs.some(
prop =>
t.isJSXAttribute(prop) &&
prop.name &&
(prop.name.name === "__source" || prop.name.name === "__self"),
);
function buildCreateElementOpeningElementAttributes(path, attribs) {
const props = [];
const found = Object.create(null);

if (hasSourceSelf) {
throw path.buildCodeFrameError(
`__source and __self should not be defined in props. You are most likely using the deprecated transform-react-jsx-self or transform-react-jsx-source Babel plugins. __source and __self will be set automatically in automatic runtime. Please remove transform-react-jsx-self or transform-react-jsx-source from your Babel config.`,
);
}
for (const attr of attribs) {
const name =
t.isJSXAttribute(attr) &&
t.isJSXIdentifier(attr.name) &&
attr.name.name;

if (options.development) {
attribs.push(
t.jsxAttribute(
t.jsxIdentifier("__source"),
t.jsxExpressionContainer(makeSource(path, file)),
),
);
attribs.push(
t.jsxAttribute(
t.jsxIdentifier("__self"),
t.jsxExpressionContainer(t.thisExpression()),
),
);
}
if (name === "__source" || name === "__self") {
if (found[name]) throw sourceSelfError(path, name);
found[name] = true;
if (!options.development) continue;
}

const props = attribs.map(convertAttribute);
props.push(convertAttribute(attr));
}

return props.length > 0 ? t.objectExpression(props) : t.nullLiteral();
}

function sourceSelfError(path, name) {
const pluginName = `transform-react-jsx-${name.slice(2)}`;

return path.buildCodeFrameError(
`Duplicate ${name} prop found. You are most likely using the deprecated ${pluginName} Babel plugin. Both __source and __self are automatically set when using the automatic runtime. Please remove transform-react-jsx-source and transform-react-jsx-self from your Babel config.`,
);
}
}
Expand Up @@ -5,5 +5,5 @@
"transform-react-jsx-self"
],
"os": ["linux", "darwin"],
"throws": "__source and __self should not be defined in props. You are most likely using the deprecated transform-react-jsx-self or transform-react-jsx-source Babel plugins. __source and __self will be set automatically in automatic runtime. Please remove transform-react-jsx-self or transform-react-jsx-source from your Babel config."
"throws": "Duplicate __self prop found. You are most likely using the deprecated transform-react-jsx-self Babel plugin. Both __source and __self are automatically set when using the automatic runtime. Please remove transform-react-jsx-source and transform-react-jsx-self from your Babel config."
}
@@ -0,0 +1,7 @@
<div />;
() => <div />;

function fn() {
<div />;
() => <div />;
}
@@ -0,0 +1,38 @@
import { jsxDEV as _jsxDEV } from "react/jsx-dev-runtime";

var _jsxFileName = "<CWD>/packages/babel-plugin-transform-react-jsx-development/test/fixtures/linux/self-inside-arrow/input.mjs",
_this = this;

/*#__PURE__*/
_jsxDEV("div", {}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 1,
columnNumber: 1
}, this);

(function () {
return /*#__PURE__*/_jsxDEV("div", {}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 2,
columnNumber: 7
}, _this);
});

function fn() {
var _this2 = this;

/*#__PURE__*/
_jsxDEV("div", {}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 5,
columnNumber: 3
}, this);

(function () {
return /*#__PURE__*/_jsxDEV("div", {}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 6,
columnNumber: 9
}, _this2);
});
}
Expand Up @@ -6,5 +6,5 @@
],
"sourceType": "module",
"os": ["linux", "darwin"],
"throws": "__source and __self should not be defined in props. You are most likely using the deprecated transform-react-jsx-self or transform-react-jsx-source Babel plugins. __source and __self will be set automatically in automatic runtime. Please remove transform-react-jsx-self or transform-react-jsx-source from your Babel config."
"throws": "Duplicate __self prop found. You are most likely using the deprecated transform-react-jsx-self Babel plugin. Both __source and __self are automatically set when using the automatic runtime. Please remove transform-react-jsx-source and transform-react-jsx-self from your Babel config."
}
Expand Up @@ -4,5 +4,6 @@
"transform-react-jsx-source",
"transform-react-jsx-self"
],
"throws": "__source and __self should not be defined in props. You are most likely using the deprecated transform-react-jsx-self or transform-react-jsx-source Babel plugins. __source and __self will be set automatically in automatic runtime. Please remove transform-react-jsx-self or transform-react-jsx-source from your Babel config."
"os": ["windows"],
"throws": "Duplicate __self prop found. You are most likely using the deprecated transform-react-jsx-self Babel plugin. Both __source and __self are automatically set when using the automatic runtime. Please remove transform-react-jsx-source and transform-react-jsx-self from your Babel config."
}
Expand Up @@ -2,7 +2,7 @@ var _reactJsxDevRuntime = require("react/jsx-dev-runtime");

var _jsxFileName = "C:\\Users\\travis\\build\\babel\\babel\\packages\\babel-plugin-transform-react-jsx-development\\test\\fixtures\\windows\\handle-fragments-with-key-windows\\input.js";

var x = /*#__PURE__*/_reactJsxDevRuntime.jsxDEV(React.Fragment, {}, "foo", false, {
var x = /*#__PURE__*/_reactJsxDevRuntime.jsxDEV(React.Fragment, {}, 'foo', false, {
fileName: _jsxFileName,
lineNumber: 1,
columnNumber: 9
Expand Down
@@ -0,0 +1,7 @@
<div />;
() => <div />;

function fn() {
<div />;
() => <div />;
}
@@ -0,0 +1,38 @@
import { jsxDEV as _jsxDEV } from "react/jsx-dev-runtime";

var _jsxFileName = "C:\\Users\\travis\\build\\babel\\babel\\packages\\babel-plugin-transform-react-jsx-development\\test\\fixtures\\windows\\self-inside-arrow-windows\\input.mjs",
_this = this;

/*#__PURE__*/
_jsxDEV("div", {}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 1,
columnNumber: 1
}, this);

(function () {
return /*#__PURE__*/_jsxDEV("div", {}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 2,
columnNumber: 7
}, _this);
});

function fn() {
var _this2 = this;

/*#__PURE__*/
_jsxDEV("div", {}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 5,
columnNumber: 3
}, this);

(function () {
return /*#__PURE__*/_jsxDEV("div", {}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 6,
columnNumber: 9
}, _this2);
});
}
Expand Up @@ -6,5 +6,5 @@
],
"sourceType": "module",
"os": ["windows"],
"throws": "__source and __self should not be defined in props. You are most likely using the deprecated transform-react-jsx-self or transform-react-jsx-source Babel plugins. __source and __self will be set automatically in automatic runtime. Please remove transform-react-jsx-self or transform-react-jsx-source from your Babel config."
"throws": "Duplicate __self prop found. You are most likely using the deprecated transform-react-jsx-self Babel plugin. Both __source and __self are automatically set when using the automatic runtime. Please remove transform-react-jsx-source and transform-react-jsx-self from your Babel config."
}

This file was deleted.

This file was deleted.

0 comments on commit 11292a3

Please sign in to comment.