Use the correct this in __self for JSX elements in arrows (#11288)

* Inject `__source` and `__self` in JSX elements earlier

This fixes an issue where `this` was not correct inside arrow functions, similar to
906f8be24d

* Add test

* Remove try-catch

* Update error

* Update fixtures

* Update windows fixtures
This commit is contained in:
Nicolò Ribaudo 2020-03-19 20:01:17 +01:00 committed by GitHub
parent 0a02a12235
commit 11292a3c74
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 221 additions and 250 deletions

View File

@ -31,6 +31,32 @@ export function helper(babel, options) {
pragmaFrag: PRAGMA_FRAG_DEFAULT = DEFAULT.pragmaFrag, pragmaFrag: PRAGMA_FRAG_DEFAULT = DEFAULT.pragmaFrag,
} = options; } = 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 { return {
JSXNamespacedName(path, state) { JSXNamespacedName(path, state) {
const throwIfNamespace = const throwIfNamespace =
@ -214,6 +240,10 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
`Runtime must be either "classic" or "automatic".`, `Runtime must be either "classic" or "automatic".`,
); );
} }
if (options.development) {
path.traverse(injectMetaPropertiesVisitor, state);
}
} }
}, },
@ -409,7 +439,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
} }
function makeSource(path, state) { function makeSource(path, state) {
const location = path.node.openingElement.loc; const location = path.node.loc;
if (!location) { if (!location) {
// the element was generated and doesn't have location information // the element was generated and doesn't have location information
return; return;
@ -532,33 +562,28 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
} }
let attribs = []; let attribs = [];
let key; const extracted = Object.create(null);
let source;
let self;
// for React.jsx, key, __source (dev), and __self (dev) is passed in as // 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 // 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 // props and filter out these three keywords so we can pass them in
// as separate arguments later // as separate arguments later
for (let i = 0; i < openingPath.node.attributes.length; i++) { for (const attr of openingPath.get("attributes")) {
const attr = openingPath.node.attributes[i]; if (attr.isJSXAttribute() && t.isJSXIdentifier(attr.node.name)) {
if (t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name)) { const { name } = attr.node.name;
if (attr.name.name === "key") { switch (name) {
key = convertAttribute(attr).value; case "__source":
} else if ( case "__self":
attr.name.name === "__source" || if (extracted[name]) throw sourceSelfError(path, name);
attr.name.name === "__self" /* falls through */
) { case "key":
throw path.buildCodeFrameError( extracted[name] = convertAttributeValue(attr.node.value);
`__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.`, break;
); default:
} else { attribs.push(attr.node);
// If someone is still using the __source and __self Babel plugins
// filter the results out
attribs.push(attr);
} }
} else { } else {
attribs.push(attr); attribs.push(attr.node);
} }
} }
@ -576,20 +601,18 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
args.push(attribs); args.push(attribs);
if (!options.development) { if (!options.development) {
if (key !== undefined) { if (extracted.key !== undefined) {
args.push(key); args.push(extracted.key);
} }
} else { } else {
// isStaticChildren, __source, and __self are only used in development // isStaticChildren, __source, and __self are only used in development
// automatically include __source and __self in this plugin // automatically include __source and __self in this plugin
// so we can eliminate the need for separate Babel plugins in Babel 8 // so we can eliminate the need for separate Babel plugins in Babel 8
source = makeSource(path, file);
self = t.thisExpression();
args.push( args.push(
key === undefined ? path.scope.buildUndefinedNode() : key, extracted.key ?? path.scope.buildUndefinedNode(),
t.booleanLiteral(path.node.children.length > 1), t.booleanLiteral(path.node.children.length > 1),
source ?? path.scope.buildUndefinedNode(), extracted.__source ?? path.scope.buildUndefinedNode(),
self, extracted.__self ?? t.thisExpression(),
); );
} }
@ -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 // Builds props for React.jsx. This function adds children into the props
// and ensures that props is always an object // and ensures that props is always an object
function buildJSXOpeningElementAttributes(attribs, file, children) { function buildJSXOpeningElementAttributes(attribs, file, children) {
const _attribs = attribs.filter( const props = attribs.map(convertAttribute);
prop =>
!(
t.isJSXAttribute(prop) &&
prop.name &&
(prop.name.name === "__source" || prop.name.name === "__self")
),
);
const props = _attribs.map(convertAttribute);
// In React.jsx, children is no longer a separate argument, but passed in // In React.jsx, children is no longer a separate argument, but passed in
// through the argument object // through the argument object
@ -774,7 +788,6 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
const attribs = buildCreateElementOpeningElementAttributes( const attribs = buildCreateElementOpeningElementAttributes(
path, path,
openingPath.node.attributes, openingPath.node.attributes,
file,
); );
args.push(attribs, ...path.node.children); args.push(attribs, ...path.node.children);
@ -796,40 +809,33 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
* breaking on spreads, we then push a new object containing * breaking on spreads, we then push a new object containing
* all prior attributes to an array for later processing. * all prior attributes to an array for later processing.
*/ */
function buildCreateElementOpeningElementAttributes(path, attribs, file) { function buildCreateElementOpeningElementAttributes(path, attribs) {
// We want source and self to be automatically included in the future const props = [];
// so we will error when we see it const found = Object.create(null);
const hasSourceSelf = attribs.some( for (const attr of attribs) {
prop => const name =
t.isJSXAttribute(prop) && t.isJSXAttribute(attr) &&
prop.name && t.isJSXIdentifier(attr.name) &&
(prop.name.name === "__source" || prop.name.name === "__self"), attr.name.name;
);
if (hasSourceSelf) { if (name === "__source" || name === "__self") {
throw path.buildCodeFrameError( if (found[name]) throw sourceSelfError(path, name);
`__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.`, found[name] = true;
); if (!options.development) continue;
}
props.push(convertAttribute(attr));
} }
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()),
),
);
}
const props = attribs.map(convertAttribute);
return props.length > 0 ? t.objectExpression(props) : t.nullLiteral(); 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.`,
);
}
} }

View File

@ -5,5 +5,5 @@
"transform-react-jsx-self" "transform-react-jsx-self"
], ],
"os": ["linux", "darwin"], "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."
} }

View File

@ -0,0 +1,7 @@
<div />;
() => <div />;
function fn() {
<div />;
() => <div />;
}

View File

@ -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);
});
}

View File

@ -6,5 +6,5 @@
], ],
"sourceType": "module", "sourceType": "module",
"os": ["linux", "darwin"], "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."
} }

View File

@ -4,5 +4,6 @@
"transform-react-jsx-source", "transform-react-jsx-source",
"transform-react-jsx-self" "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."
} }

View File

@ -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 _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, fileName: _jsxFileName,
lineNumber: 1, lineNumber: 1,
columnNumber: 9 columnNumber: 9

View File

@ -0,0 +1,7 @@
<div />;
() => <div />;
function fn() {
<div />;
() => <div />;
}

View File

@ -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);
});
}

View File

@ -6,5 +6,5 @@
], ],
"sourceType": "module", "sourceType": "module",
"os": ["windows"], "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."
} }

View File

@ -1,32 +0,0 @@
var actual = transform(
`var x = (
<>
<div>
<div key="1" />
<div key="2" meow="wolf" />
<div key="3" />
<div {...props} key="4" />
</div>
</>
);`,
Object.assign({}, opts, { filename: 'C:\\fake\\path\\mock.js' })
).code;
var expected =
`import { createElement as _createElement } from "react";
import { jsxs as _jsxs } from "react/jsx-runtime";
import { jsx as _jsx } from "react/jsx-runtime";
import { Fragment as _Fragment } from "react/jsx-runtime";
var _jsxFileName = "C:\\\\fake\\\\path\\\\mock.js";
var x = _jsx(_Fragment, {
children: _jsxs("div", {
children: [_jsx("div", {}, "1"), _jsx("div", {
meow: "wolf"
}, "2"), _jsx("div", {}, "3"), _createElement("div", { ...props,
key: "4"
})]
})
});`;
expect(actual).toBe(expected);

View File

@ -1,16 +0,0 @@
{
"plugins": [
[
"transform-react-jsx",
{
"autoImport": "namedExports",
"runtime": "automatic"
}
],
"transform-react-jsx-source",
"transform-react-jsx-self"
],
"sourceType": "module",
"os": ["win32"],
"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."
}

View File

@ -1,32 +0,0 @@
var actual = transform(
`var x = (
<>
<div>
<div key="1" />
<div key="2" meow="wolf" />
<div key="3" />
<div {...props} key="4" />
</div>
</>
);`,
Object.assign({}, opts, { filename: '/fake/path/mock.js' })
).code;
var expected =
`import { createElement as _createElement } from "react";
import { jsxs as _jsxs } from "react/jsx-runtime";
import { jsx as _jsx } from "react/jsx-runtime";
import { Fragment as _Fragment } from "react/jsx-runtime";
var _jsxFileName = "/fake/path/mock.js";
var x = _jsx(_Fragment, {
children: _jsxs("div", {
children: [_jsx("div", {}, "1"), _jsx("div", {
meow: "wolf"
}, "2"), _jsx("div", {}, "3"), _createElement("div", { ...props,
key: "4"
})]
})
});`;
expect(actual).toBe(expected);

View File

@ -0,0 +1,10 @@
var x = (
<>
<div>
<div key="1" />
<div key="2" meow="wolf" />
<div key="3" />
<div {...props} key="4" />
</div>
</>
);

View File

@ -6,11 +6,6 @@
"autoImport": "namedExports", "autoImport": "namedExports",
"runtime": "automatic" "runtime": "automatic"
} }
], ]
"transform-react-jsx-source", ]
"transform-react-jsx-self"
],
"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."
} }

View File

@ -0,0 +1,14 @@
import { createElement as _createElement } from "react";
import { jsxs as _jsxs } from "react/jsx-runtime";
import { jsx as _jsx } from "react/jsx-runtime";
import { Fragment as _Fragment } from "react/jsx-runtime";
var x = /*#__PURE__*/_jsx(_Fragment, {
children: /*#__PURE__*/_jsxs("div", {
children: [/*#__PURE__*/_jsx("div", {}, "1"), /*#__PURE__*/_jsx("div", {
meow: "wolf"
}, "2"), /*#__PURE__*/_jsx("div", {}, "3"), /*#__PURE__*/_createElement("div", { ...props,
key: "4"
})]
})
});

View File

@ -1,31 +0,0 @@
var actual = transform(
`/** @jsxRuntime classic */
var x = (
<>
<div>
<div key="1" />
<div key="2" meow="wolf" />
<div key="3" />
<div {...props} key="4" />
</div>
</>
);`,
Object.assign({}, opts, { filename: 'C:\\fake\\path\\mock.js' })
).code;
var expected =
`var _jsxFileName = "C:\\\\fake\\\\path\\\\mock.js";
/** @jsxRuntime classic */
var x = React.createElement(React.Fragment, null, React.createElement("div", null, React.createElement("div", {
key: "1"
}), React.createElement("div", {
key: "2",
meow: "wolf"
}), React.createElement("div", {
key: "3"
}), React.createElement("div", { ...props,
key: "4"
})));`;
expect(actual).toBe(expected);

View File

@ -1,15 +0,0 @@
{
"plugins": [
[
"transform-react-jsx",
{
"runtime": "automatic"
}
],
"transform-react-jsx-source",
"transform-react-jsx-self"
],
"sourceType": "module",
"os": ["win32"],
"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."
}

View File

@ -1,31 +0,0 @@
var actual = transform(
`/** @jsxRuntime classic */
var x = (
<>
<div>
<div key="1" />
<div key="2" meow="wolf" />
<div key="3" />
<div {...props} key="4" />
</div>
</>
);`,
Object.assign({}, opts, { filename: '/fake/path/mock.js' })
).code;
var expected =
`var _jsxFileName = "/fake/path/mock.js";
/** @jsxRuntime classic */
var x = React.createElement(React.Fragment, null, React.createElement("div", null, React.createElement("div", {
key: "1"
}), React.createElement("div", {
key: "2",
meow: "wolf"
}), React.createElement("div", {
key: "3"
}), React.createElement("div", { ...props,
key: "4"
})));`;
expect(actual).toBe(expected);

View File

@ -0,0 +1,11 @@
/** @jsxRuntime classic */
var x = (
<>
<div>
<div key="1" />
<div key="2" meow="wolf" />
<div key="3" />
<div {...props} key="4" />
</div>
</>
);

View File

@ -1,15 +1,5 @@
{ {
"plugins": [ "plugins": [
[ ["transform-react-jsx", { "runtime": "automatic" }]
"transform-react-jsx", ]
{
"runtime": "automatic"
}
],
"transform-react-jsx-source",
"transform-react-jsx-self"
],
"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."
} }

View File

@ -0,0 +1,11 @@
/** @jsxRuntime classic */
var x = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
key: "1"
}), /*#__PURE__*/React.createElement("div", {
key: "2",
meow: "wolf"
}), /*#__PURE__*/React.createElement("div", {
key: "3"
}), /*#__PURE__*/React.createElement("div", { ...props,
key: "4"
})));