diff --git a/packages/babel-plugin-transform-react-jsx-development/test/fixtures/linux/within-ts-module-block/input.ts b/packages/babel-plugin-transform-react-jsx-development/test/fixtures/linux/within-ts-module-block/input.ts new file mode 100644 index 000000000000..26edcac0b890 --- /dev/null +++ b/packages/babel-plugin-transform-react-jsx-development/test/fixtures/linux/within-ts-module-block/input.ts @@ -0,0 +1,5 @@ +export namespace Namespaced { + export const Component = () => ( +
Hello, world!
+ ); +} diff --git a/packages/babel-plugin-transform-react-jsx-development/test/fixtures/linux/within-ts-module-block/options.json b/packages/babel-plugin-transform-react-jsx-development/test/fixtures/linux/within-ts-module-block/options.json new file mode 100644 index 000000000000..3c8e6aa49b85 --- /dev/null +++ b/packages/babel-plugin-transform-react-jsx-development/test/fixtures/linux/within-ts-module-block/options.json @@ -0,0 +1,8 @@ +{ + "plugins": [ + "transform-react-jsx-development", + ["syntax-typescript", { "isTSX": true }] + ], + "sourceType": "module", + "os": ["linux", "darwin"] +} diff --git a/packages/babel-plugin-transform-react-jsx-development/test/fixtures/linux/within-ts-module-block/output.mjs b/packages/babel-plugin-transform-react-jsx-development/test/fixtures/linux/within-ts-module-block/output.mjs new file mode 100644 index 000000000000..9090c80cb91c --- /dev/null +++ b/packages/babel-plugin-transform-react-jsx-development/test/fixtures/linux/within-ts-module-block/output.mjs @@ -0,0 +1,11 @@ +var _jsxFileName = "/packages/babel-plugin-transform-react-jsx-development/test/fixtures/linux/within-ts-module-block/input.ts"; +import { jsxDEV as _jsxDEV } from "react/jsx-dev-runtime"; +export namespace Namespaced { + export const Component = () => /*#__PURE__*/_jsxDEV("div", { + children: "Hello, world!" + }, void 0, false, { + fileName: _jsxFileName, + lineNumber: 3, + columnNumber: 3 + }, void 0); +} diff --git a/packages/babel-plugin-transform-react-jsx-development/test/fixtures/windows/within-ts-module-block/input.ts b/packages/babel-plugin-transform-react-jsx-development/test/fixtures/windows/within-ts-module-block/input.ts new file mode 100644 index 000000000000..26edcac0b890 --- /dev/null +++ b/packages/babel-plugin-transform-react-jsx-development/test/fixtures/windows/within-ts-module-block/input.ts @@ -0,0 +1,5 @@ +export namespace Namespaced { + export const Component = () => ( +
Hello, world!
+ ); +} diff --git a/packages/babel-plugin-transform-react-jsx-development/test/fixtures/windows/within-ts-module-block/options.json b/packages/babel-plugin-transform-react-jsx-development/test/fixtures/windows/within-ts-module-block/options.json new file mode 100644 index 000000000000..1fb16caa5bff --- /dev/null +++ b/packages/babel-plugin-transform-react-jsx-development/test/fixtures/windows/within-ts-module-block/options.json @@ -0,0 +1,8 @@ +{ + "plugins": [ + "transform-react-jsx-development", + ["syntax-typescript", { "isTSX": true }] + ], + "sourceType": "module", + "os": ["win32"] +} diff --git a/packages/babel-plugin-transform-react-jsx-development/test/fixtures/windows/within-ts-module-block/output.mjs b/packages/babel-plugin-transform-react-jsx-development/test/fixtures/windows/within-ts-module-block/output.mjs new file mode 100644 index 000000000000..efb871f367af --- /dev/null +++ b/packages/babel-plugin-transform-react-jsx-development/test/fixtures/windows/within-ts-module-block/output.mjs @@ -0,0 +1,11 @@ +var _jsxFileName = "\\packages\\babel-plugin-transform-react-jsx-development\\test\\fixtures\\windows\\within-ts-module-block\\input.ts"; +import { jsxDEV as _jsxDEV } from "react/jsx-dev-runtime"; +export namespace Namespaced { + export const Component = () => /*#__PURE__*/_jsxDEV("div", { + children: "Hello, world!" + }, void 0, false, { + fileName: _jsxFileName, + lineNumber: 3, + columnNumber: 3 + }, void 0); +} diff --git a/packages/babel-plugin-transform-react-jsx/src/create-plugin.ts b/packages/babel-plugin-transform-react-jsx/src/create-plugin.ts index 982083f1671e..b7a7530f084e 100644 --- a/packages/babel-plugin-transform-react-jsx/src/create-plugin.ts +++ b/packages/babel-plugin-transform-react-jsx/src/create-plugin.ts @@ -2,15 +2,13 @@ import jsx from "@babel/plugin-syntax-jsx"; import { declare } from "@babel/helper-plugin-utils"; import { types as t } from "@babel/core"; import type { PluginPass } from "@babel/core"; -import type { NodePath, Visitor } from "@babel/traverse"; +import type { NodePath, Scope, Visitor } from "@babel/traverse"; import { addNamed, addNamespace, isModule } from "@babel/helper-module-imports"; import annotateAsPure from "@babel/helper-annotate-as-pure"; import type { - ArrowFunctionExpression, CallExpression, Class, Expression, - FunctionParent, Identifier, JSXAttribute, JSXElement, @@ -22,8 +20,6 @@ import type { Program, } from "@babel/types"; -type Diff = T extends U ? never : T; - const DEFAULT = { importSource: "react", runtime: "automatic", @@ -116,7 +112,7 @@ export default function createPlugin({ name, development }) { const injectMetaPropertiesVisitor: Visitor = { JSXOpeningElement(path, state) { const attributes = []; - if (isThisAllowed(path)) { + if (isThisAllowed(path.scope)) { attributes.push( t.jsxAttribute( t.jsxIdentifier("__self"), @@ -295,52 +291,36 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, } as Visitor, }; - // Finds the closest parent function that provides `this`. Specifically, this looks for - // the first parent function that isn't an arrow function. - // - // Derived from `Scope#getFunctionParent` - function getThisFunctionParent( - path: NodePath, - ): NodePath> | null { - let scope = path.scope; - do { - if ( - scope.path.isFunctionParent() && - !scope.path.isArrowFunctionExpression() - ) { - // @ts-expect-error ts can not infer scope.path is Diff - return scope.path; - } - } while ((scope = scope.parent)); - return null; - } - // Returns whether the class has specified a superclass. function isDerivedClass(classPath: NodePath) { return classPath.node.superClass !== null; } - // Returns whether `this` is allowed at given path. - function isThisAllowed(path: NodePath) { + // Returns whether `this` is allowed at given scope. + function isThisAllowed(scope: Scope) { // This specifically skips arrow functions as they do not rewrite `this`. - const parentMethodOrFunction = getThisFunctionParent(path); - if (parentMethodOrFunction === null) { - // We are not in a method or function. It is fine to use `this`. - return true; - } - if (!parentMethodOrFunction.isMethod()) { - // If the closest parent is a regular function, `this` will be rebound, therefore it is fine to use `this`. - return true; - } - // Current node is within a method, so we need to check if the method is a constructor. - if (parentMethodOrFunction.node.kind !== "constructor") { - // We are not in a constructor, therefore it is always fine to use `this`. - return true; - } - // Now we are in a constructor. If it is a derived class, we do not reference `this`. - return !isDerivedClass( - parentMethodOrFunction.parentPath.parentPath as NodePath, - ); + do { + const { path } = scope; + if (path.isFunctionParent() && !path.isArrowFunctionExpression()) { + if (!path.isMethod()) { + // If the closest parent is a regular function, `this` will be rebound, therefore it is fine to use `this`. + return true; + } + // Current node is within a method, so we need to check if the method is a constructor. + if (path.node.kind !== "constructor") { + // We are not in a constructor, therefore it is always fine to use `this`. + return true; + } + // Now we are in a constructor. If it is a derived class, we do not reference `this`. + return !isDerivedClass(path.parentPath.parentPath as NodePath); + } + if (path.isTSModuleBlock()) { + // If the closeset parent is a TS Module block, `this` will not be allowed. + return false; + } + } while ((scope = scope.parent)); + // We are not in a method or function. It is fine to use `this`. + return true; } function call(