Skip to content

Commit c7a11de

Browse files
authored
fix: refactor JSX runtime annotation handling (#1038)
1 parent 37b9905 commit c7a11de

20 files changed

+209
-87
lines changed

apps/website/content/docs/rules/overview.mdx

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Linter rules can have false positives, false negatives, and some rules are depen
2727
| :----------------------------------------------------------------------------------- | :- | :------: | :---------------------------------------------------------------------------------------------------- | :-----: |
2828
| [`jsx-no-duplicate-props`](./jsx-no-duplicate-props) | 1️⃣ | | Disallow duplicate props in JSX elements | |
2929
| [`jsx-no-undef`](./jsx-no-undef) | 0️⃣ | | Disallow undefined variables in JSX elements | |
30-
| [`jsx-uses-react`](./jsx-uses-react) | 1️⃣ | | Marks React variables as used when JSX is used in the file | |
30+
| [`jsx-uses-react`](./jsx-uses-react) | 1️⃣ | | Marks React variables as used when JSX is used | |
3131
| [`jsx-uses-vars`](./jsx-uses-vars) | 1️⃣ | | Marks variables used in JSX elements as used | |
3232
| [`no-access-state-in-setstate`](./no-access-state-in-setstate) | 2️⃣ | | Disallow accessing `this.state` inside `setState` calls | |
3333
| [`no-array-index-key`](./no-array-index-key) | 1️⃣ | | Disallow an item's index in the array as its key | |

packages/plugins/eslint-plugin-react-x/src/rules/jsx-uses-react.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ react-x/jsx-uses-react
2121

2222
## Description
2323

24-
Marks React variables as used when JSX is used in the file.
24+
Marks React variables as used when JSX is used.
2525

2626
If you are using the `@jsx` pragma this rule will mark the designated variable and not the React one.
2727

packages/plugins/eslint-plugin-react-x/src/rules/jsx-uses-react.spec.ts

-4
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,6 @@ debug
2929
console.log(Hello);
3030
`,
3131
errors: [
32-
{
33-
messageId: "jsxUsesReact",
34-
data: { name: "React" },
35-
},
3632
{
3733
messageId: "jsxUsesReact",
3834
data: { name: "React.createElement" },

packages/plugins/eslint-plugin-react-x/src/rules/jsx-uses-react.ts

+15-42
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,7 @@ import type { TSESTree } from "@typescript-eslint/types";
22
import type { RuleListener } from "@typescript-eslint/utils/ts-eslint";
33

44
import type { CamelCase } from "string-ts";
5-
import {
6-
JsxRuntime,
7-
RE_JSX_ANNOTATION,
8-
RE_JSX_FRAG_ANNOTATION,
9-
type RuleContext,
10-
type RuleFeature,
11-
} from "@eslint-react/kit";
5+
import { JsxRuntime, type RuleContext, type RuleFeature } from "@eslint-react/kit";
126
import { JsxEmit } from "typescript";
137
import { createRule } from "../utils";
148

@@ -18,13 +12,13 @@ export const RULE_FEATURES = [] as const satisfies RuleFeature[];
1812

1913
export type MessageID = CamelCase<typeof RULE_NAME>;
2014

21-
export const debug = true;
15+
export const debug = false;
2216

2317
export default createRule<[], MessageID>({
2418
meta: {
2519
type: "problem",
2620
docs: {
27-
description: "Marks React variables as used when JSX is used in the file.",
21+
description: "Marks React variables as used when JSX is used.",
2822
[Symbol.for("rule_features")]: RULE_FEATURES,
2923
},
3024
messages: {
@@ -38,32 +32,24 @@ export default createRule<[], MessageID>({
3832
});
3933

4034
export function create(context: RuleContext<MessageID, []>): RuleListener {
41-
const { jsx, jsxFactory, jsxFragmentFactory, reactNamespace } = JsxRuntime.getJsxRuntimeOptionsFromContext(context);
42-
const jsxAnnotation = getJsxAnnotation(context);
35+
const jsxOptions = JsxRuntime.getJsxRuntimeOptions(context);
36+
const jsxAnnotation = JsxRuntime.getJsxRuntimeAnnotation(context);
37+
const jsx = jsxAnnotation.jsxRuntime === "classic"
38+
? JsxEmit.React
39+
: jsxOptions.jsx;
4340
if (jsx === JsxEmit.ReactJSX || jsx === JsxEmit.ReactJSXDev) return {};
4441

42+
const jsxFactory = jsxAnnotation.jsx ?? jsxOptions.jsxFactory;
43+
const jsxFragmentFactory = jsxAnnotation.jsxFrag ?? jsxOptions.jsxFragmentFactory;
44+
4545
function handleJsxElement(node: TSESTree.Node) {
46-
if (jsxAnnotation == null) {
47-
context.sourceCode.markVariableAsUsed(reactNamespace, node);
48-
context.sourceCode.markVariableAsUsed(jsxFactory, node);
49-
debugReport(context, node, reactNamespace);
50-
debugReport(context, node, jsxFactory);
51-
}
52-
if (jsxAnnotation?.jsx != null) {
53-
context.sourceCode.markVariableAsUsed(jsxAnnotation.jsx, node);
54-
debugReport(context, node, jsxAnnotation.jsx);
55-
}
46+
context.sourceCode.markVariableAsUsed(jsxFactory, node);
47+
debugReport(context, node, jsxFactory);
5648
}
5749

5850
function handleJsxFragment(node: TSESTree.Node) {
59-
if (jsxAnnotation == null) {
60-
context.sourceCode.markVariableAsUsed(jsxFragmentFactory, node);
61-
debugReport(context, node, jsxFragmentFactory);
62-
}
63-
if (jsxAnnotation?.jsxFrag != null) {
64-
context.sourceCode.markVariableAsUsed(jsxAnnotation.jsxFrag, node);
65-
debugReport(context, node, jsxAnnotation.jsxFrag);
66-
}
51+
context.sourceCode.markVariableAsUsed(jsxFragmentFactory, node);
52+
debugReport(context, node, jsxFragmentFactory);
6753
}
6854

6955
return {
@@ -73,19 +59,6 @@ export function create(context: RuleContext<MessageID, []>): RuleListener {
7359
};
7460
}
7561

76-
function getJsxAnnotation(context: RuleContext) {
77-
if (!context.sourceCode.text.includes("@jsx")) return;
78-
const allComments = context.sourceCode.getAllComments();
79-
const jsxComment = allComments.find((n) => RE_JSX_ANNOTATION.test(n.value));
80-
const jsxFragComment = allComments.find((n) => RE_JSX_FRAG_ANNOTATION.test(n.value));
81-
const jsx = jsxComment?.value.match(RE_JSX_ANNOTATION)?.[1];
82-
const jsxFrag = jsxFragComment?.value.match(RE_JSX_FRAG_ANNOTATION)?.[1];
83-
return {
84-
jsx,
85-
jsxFrag,
86-
};
87-
}
88-
8962
function debugReport(context: RuleContext, node: TSESTree.Node, name: string) {
9063
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
9164
if (!debug) return;

packages/utilities/kit/docs/@eslint-react/namespaces/JsxRuntime/README.md

+14-1
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,23 @@
66

77
# JsxRuntime
88

9+
## Interfaces
10+
11+
- [JsxRuntimeAnnotation](interfaces/JsxRuntimeAnnotation.md)
12+
913
## Type Aliases
1014

1115
- [JsxRuntimeOptions](type-aliases/JsxRuntimeOptions.md)
1216

17+
## Variables
18+
19+
- [RE\_JSX\_RT\_ANNOTATION\_JSX](variables/RE_JSX_RT_ANNOTATION_JSX.md)
20+
- [RE\_JSX\_RT\_ANNOTATION\_JSX\_FRAG](variables/RE_JSX_RT_ANNOTATION_JSX_FRAG.md)
21+
- [RE\_JSX\_RT\_ANNOTATION\_JSX\_IMPORT\_SOURCE](variables/RE_JSX_RT_ANNOTATION_JSX_IMPORT_SOURCE.md)
22+
- [RE\_JSX\_RT\_ANNOTATION\_JSX\_RUNTIME](variables/RE_JSX_RT_ANNOTATION_JSX_RUNTIME.md)
23+
1324
## Functions
1425

15-
- [getJsxRuntimeOptionsFromContext](functions/getJsxRuntimeOptionsFromContext.md)
26+
- [getJsxRuntimeAnnotation](functions/getJsxRuntimeAnnotation.md)
27+
- [getJsxRuntimeOptions](functions/getJsxRuntimeOptions.md)
28+
- [make](functions/make.md)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
[**@eslint-react/kit**](../../../../README.md)
2+
3+
***
4+
5+
[@eslint-react/kit](../../../../README.md) / [JsxRuntime](../README.md) / getJsxRuntimeAnnotation
6+
7+
# Function: getJsxRuntimeAnnotation()
8+
9+
> **getJsxRuntimeAnnotation**(`context`): [`JsxRuntimeAnnotation`](../interfaces/JsxRuntimeAnnotation.md)
10+
11+
Get the a JsxRuntimeAnnotation object representing the JSX annotations in the file.
12+
13+
## Parameters
14+
15+
### context
16+
17+
[`RuleContext`](../../../../type-aliases/RuleContext.md)
18+
19+
The RuleContext
20+
21+
## Returns
22+
23+
[`JsxRuntimeAnnotation`](../interfaces/JsxRuntimeAnnotation.md)
24+
25+
JsxRuntimeAnnotation
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
***
44

5-
[@eslint-react/kit](../../../../README.md) / [JsxRuntime](../README.md) / getJsxRuntimeOptionsFromContext
5+
[@eslint-react/kit](../../../../README.md) / [JsxRuntime](../README.md) / getJsxRuntimeOptions
66

7-
# Function: getJsxRuntimeOptionsFromContext()
7+
# Function: getJsxRuntimeOptions()
88

9-
> **getJsxRuntimeOptionsFromContext**(`context`): `object`
9+
> **getJsxRuntimeOptions**(`context`): `object`
1010
1111
Get JsxRuntimeOptions from RuleContext
1212

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[**@eslint-react/kit**](../../../../README.md)
2+
3+
***
4+
5+
[@eslint-react/kit](../../../../README.md) / [JsxRuntime](../README.md) / make
6+
7+
# Function: make()
8+
9+
> **make**(): [`JsxRuntimeAnnotation`](../interfaces/JsxRuntimeAnnotation.md)
10+
11+
## Returns
12+
13+
[`JsxRuntimeAnnotation`](../interfaces/JsxRuntimeAnnotation.md)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
[**@eslint-react/kit**](../../../../README.md)
2+
3+
***
4+
5+
[@eslint-react/kit](../../../../README.md) / [JsxRuntime](../README.md) / JsxRuntimeAnnotation
6+
7+
# Interface: JsxRuntimeAnnotation
8+
9+
## Properties
10+
11+
### jsx
12+
13+
> **jsx**: `undefined` \| `string`
14+
15+
***
16+
17+
### jsxFrag
18+
19+
> **jsxFrag**: `undefined` \| `string`
20+
21+
***
22+
23+
### jsxImportSource
24+
25+
> **jsxImportSource**: `undefined` \| `string`
26+
27+
***
28+
29+
### jsxRuntime
30+
31+
> **jsxRuntime**: `undefined` \| `string`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[**@eslint-react/kit**](../../../../README.md)
2+
3+
***
4+
5+
[@eslint-react/kit](../../../../README.md) / [JsxRuntime](../README.md) / RE\_JSX\_RT\_ANNOTATION\_JSX
6+
7+
# Variable: RE\_JSX\_RT\_ANNOTATION\_JSX
8+
9+
> `const` **RE\_JSX\_RT\_ANNOTATION\_JSX**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
10+
11+
Regular expression for matching a `@jsx` annotation comment.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[**@eslint-react/kit**](../../../../README.md)
2+
3+
***
4+
5+
[@eslint-react/kit](../../../../README.md) / [JsxRuntime](../README.md) / RE\_JSX\_RT\_ANNOTATION\_JSX\_FRAG
6+
7+
# Variable: RE\_JSX\_RT\_ANNOTATION\_JSX\_FRAG
8+
9+
> `const` **RE\_JSX\_RT\_ANNOTATION\_JSX\_FRAG**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
10+
11+
Regular expression for matching a `@jsxFrag` annotation comment.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[**@eslint-react/kit**](../../../../README.md)
2+
3+
***
4+
5+
[@eslint-react/kit](../../../../README.md) / [JsxRuntime](../README.md) / RE\_JSX\_RT\_ANNOTATION\_JSX\_IMPORT\_SOURCE
6+
7+
# Variable: RE\_JSX\_RT\_ANNOTATION\_JSX\_IMPORT\_SOURCE
8+
9+
> `const` **RE\_JSX\_RT\_ANNOTATION\_JSX\_IMPORT\_SOURCE**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
10+
11+
Regular expression for matching a `@jsxImportSource` annotation comment.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[**@eslint-react/kit**](../../../../README.md)
2+
3+
***
4+
5+
[@eslint-react/kit](../../../../README.md) / [JsxRuntime](../README.md) / RE\_JSX\_RT\_ANNOTATION\_JSX\_RUNTIME
6+
7+
# Variable: RE\_JSX\_RT\_ANNOTATION\_JSX\_RUNTIME
8+
9+
> `const` **RE\_JSX\_RT\_ANNOTATION\_JSX\_RUNTIME**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
10+
11+
Regular expression for matching a `@jsxRuntime` annotation comment.

packages/utilities/kit/docs/README.md

-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@
2323
- [RE\_JAVASCRIPT\_PROTOCOL](variables/RE_JAVASCRIPT_PROTOCOL.md)
2424
- [RE\_JS\_EXT](variables/RE_JS_EXT.md)
2525
- [RE\_JS\_IDENTIFIER](variables/RE_JS_IDENTIFIER.md)
26-
- [RE\_JSX\_ANNOTATION](variables/RE_JSX_ANNOTATION.md)
27-
- [RE\_JSX\_FRAG\_ANNOTATION](variables/RE_JSX_FRAG_ANNOTATION.md)
2826
- [RE\_KEBAB\_CASE](variables/RE_KEBAB_CASE.md)
2927
- [RE\_PASCAL\_CASE](variables/RE_PASCAL_CASE.md)
3028
- [RE\_REGEXP\_STR](variables/RE_REGEXP_STR.md)

packages/utilities/kit/docs/variables/RE_JSX_ANNOTATION.md

-11
This file was deleted.

packages/utilities/kit/docs/variables/RE_JSX_FRAG_ANNOTATION.md

-11
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import type { RuleContext } from "../Rule";
2+
import { _ } from "@eslint-react/eff";
3+
4+
/**
5+
* Regular expression for matching a `@jsx` annotation comment.
6+
*/
7+
export const RE_JSX_RT_ANNOTATION_JSX = /@jsx\s+(\S+)/u;
8+
9+
/**
10+
* Regular expression for matching a `@jsxFrag` annotation comment.
11+
*/
12+
export const RE_JSX_RT_ANNOTATION_JSX_FRAG = /@jsxFrag\s+(\S+)/u;
13+
14+
/**
15+
* Regular expression for matching a `@jsxRuntime` annotation comment.
16+
*/
17+
export const RE_JSX_RT_ANNOTATION_JSX_RUNTIME = /@jsxRuntime\s+(\S+)/u;
18+
19+
/**
20+
* Regular expression for matching a `@jsxImportSource` annotation comment.
21+
*/
22+
export const RE_JSX_RT_ANNOTATION_JSX_IMPORT_SOURCE = /@jsxImportSource\s+(\S+)/u;
23+
24+
export interface JsxRuntimeAnnotation {
25+
jsx: _ | string;
26+
jsxFrag: _ | string;
27+
jsxImportSource: _ | string;
28+
jsxRuntime: _ | string;
29+
}
30+
31+
export function make(): JsxRuntimeAnnotation {
32+
return {
33+
jsx: _,
34+
jsxFrag: _,
35+
jsxImportSource: _,
36+
jsxRuntime: _,
37+
};
38+
}
39+
40+
/**
41+
* Get the a JsxRuntimeAnnotation object representing the JSX annotations in the file.
42+
* @param context The RuleContext
43+
* @returns JsxRuntimeAnnotation
44+
*/
45+
export function getJsxRuntimeAnnotation(context: RuleContext) {
46+
const JsxRuntimeAnnotation = make();
47+
if (!context.sourceCode.text.includes("@jsx")) return JsxRuntimeAnnotation;
48+
const allComments = context.sourceCode.getAllComments();
49+
for (const comment of allComments) {
50+
const jsx = comment.value.match(RE_JSX_RT_ANNOTATION_JSX);
51+
const jsxFrag = comment.value.match(RE_JSX_RT_ANNOTATION_JSX_FRAG);
52+
const jsxRuntime = comment.value.match(RE_JSX_RT_ANNOTATION_JSX_RUNTIME);
53+
const jsxImportSource = comment.value.match(RE_JSX_RT_ANNOTATION_JSX_IMPORT_SOURCE);
54+
JsxRuntimeAnnotation.jsx = jsx?.[1];
55+
JsxRuntimeAnnotation.jsxFrag = jsxFrag?.[1];
56+
JsxRuntimeAnnotation.jsxRuntime = jsxRuntime?.[1];
57+
JsxRuntimeAnnotation.jsxImportSource = jsxImportSource?.[1];
58+
}
59+
return JsxRuntimeAnnotation;
60+
}

packages/utilities/kit/src/JsxRuntime/JsxRuntimeOptions.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export type JsxRuntimeOptions = Pick<
2020
* @param context The RuleContext
2121
* @returns JsxRuntimeOptions
2222
*/
23-
export function getJsxRuntimeOptionsFromContext(context: RuleContext) {
23+
export function getJsxRuntimeOptions(context: RuleContext) {
2424
const options = context.sourceCode.parserServices?.program?.getCompilerOptions() ?? {};
2525
return {
2626
jsx: options.jsx ?? JsxEmit.ReactJSX,
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
export * from "./JsxRuntimeAnnotation";
12
export * from "./JsxRuntimeOptions";

0 commit comments

Comments
 (0)