Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/website/content/docs/rules/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"---Core Rules---",
"jsx-no-duplicate-props",
"jsx-no-undef",
"jsx-uses-react",
"jsx-uses-vars",
"no-access-state-in-setstate",
"no-array-index-key",
Expand Down
1 change: 1 addition & 0 deletions apps/website/content/docs/rules/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Linter rules can have false positives, false negatives, and some rules are depen
| :----------------------------------------------------------------------------------- | :- | :------: | :---------------------------------------------------------------------------------------------------- | :-----: |
| [`jsx-no-duplicate-props`](./jsx-no-duplicate-props) | 1️⃣ | | Disallow duplicate props in JSX elements | |
| [`jsx-no-undef`](./jsx-no-undef) | 0️⃣ | | Disallow undefined variables in JSX elements | |
| [`jsx-uses-react`](./jsx-uses-react) | 1️⃣ | | Marks React variables as used when JSX is used in the file | |
| [`jsx-uses-vars`](./jsx-uses-vars) | 1️⃣ | | Marks variables used in JSX elements as used | |
| [`no-access-state-in-setstate`](./no-access-state-in-setstate) | 2️⃣ | | Disallow accessing `this.state` inside `setState` calls | |
| [`no-array-index-key`](./no-array-index-key) | 1️⃣ | | Disallow an item's index in the array as its key | |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const rules = {
...recommended.rules,
"react-x/jsx-no-duplicate-props": "off",
"react-x/jsx-no-undef": "off",
"react-x/jsx-uses-react": "off",
"react-x/jsx-uses-vars": "off",
} as const satisfies RulePreset;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const name = "react-x/recommended";

export const rules = {
"react-x/jsx-no-duplicate-props": "warn",
"react-x/jsx-uses-react": "warn",
"react-x/jsx-uses-vars": "warn",
"react-x/no-access-state-in-setstate": "error",
"react-x/no-array-index-key": "warn",
Expand Down
2 changes: 2 additions & 0 deletions packages/plugins/eslint-plugin-react-x/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import avoidShorthandBoolean from "./rules/avoid-shorthand-boolean";
import avoidShorthandFragment from "./rules/avoid-shorthand-fragment";
import jsxNoDuplicateProps from "./rules/jsx-no-duplicate-props";
import jsxNoUndef from "./rules/jsx-no-undef";
import jsxUsesReact from "./rules/jsx-uses-react";
import jsxUsesVars from "./rules/jsx-uses-vars";
import noAccessStateInSetstate from "./rules/no-access-state-in-setstate";
import noArrayIndexKey from "./rules/no-array-index-key";
Expand Down Expand Up @@ -113,6 +114,7 @@ export const plugin = {
// Part: JSX only rules
"jsx-no-duplicate-props": jsxNoDuplicateProps,
"jsx-no-undef": jsxNoUndef,
"jsx-uses-react": jsxUsesReact,
"jsx-uses-vars": jsxUsesVars,

// Part: deprecated rules
Expand Down
65 changes: 65 additions & 0 deletions packages/plugins/eslint-plugin-react-x/src/rules/jsx-uses-react.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
title: jsx-uses-react
---

**Full Name in `eslint-plugin-react-x`**

```plain copy
react-x/jsx-uses-react
```

**Full Name in `@eslint-react/eslint-plugin`**

```plain copy
@eslint-react/jsx-uses-react
```

**Presets**

- `core`
- `recommended`

## Description

Marks React variables as used when JSX is used in the file.

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

This rule does nothing when using the [New JSX Transform](https://legacy.reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html) or if the `no-unused-vars` rule is not enabled.

## Examples

### Failing

```tsx
import React from "react";
// nothing to do with React
```

```tsx
/** @jsx Foo */
import React from "react";
// nothing to do with React

const Hello = <div>Hello</div>;
```

### Passing

```tsx
import React from "react";

const Hello = <div>Hello</div>;
```

```tsx
/** @jsx Foo */
import Foo from "foo";

const Hello = <div>Hello</div>;
```

## Implementation

- [Rule source](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x/src/rules/jsx-uses-react.ts)
- [Test source](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x/src/rules/jsx-uses-react.spec.ts)
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import tsx from "dedent";

import { allValid, ruleTester } from "../../../../../test";
import rule from "./jsx-uses-react";

ruleTester.run("no-unused-vars", rule, {
// TODO: Add invalid test cases
invalid: [],
valid: [
...allValid,
{
code: tsx`
import React from "react";

const Hello = <div>Hello</div>;
`,
},
{
code: tsx`
/** @jsx Foo */
import Foo from "foo";

const Hello = <div>Hello</div>;
`,
},
],
});
48 changes: 48 additions & 0 deletions packages/plugins/eslint-plugin-react-x/src/rules/jsx-uses-react.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import type { RuleListener } from "@typescript-eslint/utils/ts-eslint";
import type { CamelCase } from "string-ts";
import { JsxRuntime, type RuleContext, type RuleFeature } from "@eslint-react/kit";

import { JsxEmit } from "typescript";
import { createRule } from "../utils";

export const RULE_NAME = "jsx-uses-react";

export const RULE_FEATURES = [] as const satisfies RuleFeature[];

export type MessageID = CamelCase<typeof RULE_NAME>;

export default createRule<[], MessageID>({
meta: {
type: "problem",
docs: {
description: "Marks React variables as used when JSX is used in the file.",
[Symbol.for("rule_features")]: RULE_FEATURES,
},
messages: {
jsxUsesReact: "",
},
schema: [],
},
name: RULE_NAME,
create,
defaultOptions: [],
});

export function create(context: RuleContext<MessageID, []>): RuleListener {
const { jsx, jsxFactory, jsxFragmentFactory, reactNamespace } = JsxRuntime.getJsxRuntimeOptionsFromContext(context);
// If we are using the New JSX Transform, this rule should do nothing.
if (jsx === JsxEmit.ReactJSX || jsx === JsxEmit.ReactJSXDev) return {};
return {
JSXFragment(node) {
context.sourceCode.markVariableAsUsed(jsxFragmentFactory, node);
},
JSXOpeningElement(node) {
context.sourceCode.markVariableAsUsed(reactNamespace, node);
context.sourceCode.markVariableAsUsed(jsxFactory, node);
},
JSXOpeningFragment(node) {
context.sourceCode.markVariableAsUsed(reactNamespace, node);
context.sourceCode.markVariableAsUsed(jsxFactory, node);
},
};
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import tsx from "dedent";

import { allValid, ruleTester } from "../../../../../test";
import rule, { RULE_NAME } from "./jsx-uses-vars";
import rule from "./jsx-uses-vars";

ruleTester.run(RULE_NAME, rule, {
ruleTester.run("no-unused-vars", rule, {
// TODO: Add invalid test cases
invalid: [],
valid: [
...allValid,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default createRule<[], MessageID>({
[Symbol.for("rule_features")]: RULE_FEATURES,
},
messages: {
jsxUsesVars: "JSX variables should be marked as used.",
jsxUsesVars: "",
},
schema: [],
},
Expand Down
1 change: 1 addition & 0 deletions packages/plugins/eslint-plugin/src/configs/all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const rules = {
"@eslint-react/avoid-shorthand-fragment": "warn",
"@eslint-react/jsx-no-duplicate-props": "warn",
"@eslint-react/jsx-no-undef": "error",
"@eslint-react/jsx-uses-react": "warn",
"@eslint-react/jsx-uses-vars": "warn",
"@eslint-react/no-access-state-in-setstate": "error",
"@eslint-react/no-array-index-key": "warn",
Expand Down
1 change: 1 addition & 0 deletions packages/plugins/eslint-plugin/src/configs/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const name = "@eslint-react/core";

export const rules = {
"@eslint-react/jsx-no-duplicate-props": "warn",
"@eslint-react/jsx-uses-react": "warn",
"@eslint-react/jsx-uses-vars": "warn",
"@eslint-react/no-access-state-in-setstate": "error",
"@eslint-react/no-array-index-key": "warn",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const rules = {
...recommended.rules,
"@eslint-react/dom/no-unknown-property": "off",
"@eslint-react/jsx-no-duplicate-props": "off",
"@eslint-react/jsx-uses-react": "off",
"@eslint-react/jsx-uses-vars": "off",
} as const satisfies RulePreset;

Expand Down
2 changes: 2 additions & 0 deletions packages/utilities/kit/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
- [RE\_CONSTANT\_CASE](variables/RE_CONSTANT_CASE.md)
- [RE\_JAVASCRIPT\_PROTOCOL](variables/RE_JAVASCRIPT_PROTOCOL.md)
- [RE\_JS\_EXT](variables/RE_JS_EXT.md)
- [RE\_JS\_IDENTIFIER](variables/RE_JS_IDENTIFIER.md)
- [RE\_JSX\_ANNOTATION](variables/RE_JSX_ANNOTATION.md)
- [RE\_KEBAB\_CASE](variables/RE_KEBAB_CASE.md)
- [RE\_PASCAL\_CASE](variables/RE_PASCAL_CASE.md)
- [RE\_REGEXP\_STR](variables/RE_REGEXP_STR.md)
Expand Down
4 changes: 4 additions & 0 deletions packages/utilities/kit/docs/functions/toRegExp.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,7 @@ Returns the `RegExp`.
#### Returns

`boolean`

## See

https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/src/utils/regexp.ts
11 changes: 11 additions & 0 deletions packages/utilities/kit/docs/variables/RE_JSX_ANNOTATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[**@eslint-react/kit**](../README.md)

***

[@eslint-react/kit](../README.md) / RE\_JSX\_ANNOTATION

# Variable: RE\_JSX\_ANNOTATION

> `const` **RE\_JSX\_ANNOTATION**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)

Regular expression for matching a JSX pragma comment.
11 changes: 11 additions & 0 deletions packages/utilities/kit/docs/variables/RE_JS_IDENTIFIER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[**@eslint-react/kit**](../README.md)

***

[@eslint-react/kit](../README.md) / RE\_JS\_IDENTIFIER

# Variable: RE\_JS\_IDENTIFIER

> `const` **RE\_JS\_IDENTIFIER**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)

Regular expression for matching a valid JavaScript identifier.
12 changes: 10 additions & 2 deletions packages/utilities/kit/src/RegExp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,15 @@ export const RE_CONSTANT_CASE = /^[A-Z][\d_A-Z]*$/u;
// eslint-disable-next-line no-control-regex
export const RE_JAVASCRIPT_PROTOCOL = /^[\u0000-\u001F ]*j[\t\n\r]*a[\t\n\r]*v[\t\n\r]*a[\t\n\r]*s[\t\n\r]*c[\t\n\r]*r[\t\n\r]*i[\t\n\r]*p[\t\n\r]*t[\t\n\r]*:/iu;

// Ported from https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/src/utils/regexp.ts
/**
* Regular expression for matching a JSX pragma comment.
*/
export const RE_JSX_ANNOTATION = /@jsx\s+(\S+)/;

/**
* Regular expression for matching a valid JavaScript identifier.
*/
export const RE_JS_IDENTIFIER = /^[_$a-z][\w$]*$/i;

/**
* Regular expression for matching a RegExp string.
Expand All @@ -49,7 +57,7 @@ export const RE_REGEXP_STR = /^\/(.+)\/([A-Za-z]*)$/u;
* Convert a string to the `RegExp`.
* Normal strings (e.g. `"foo"`) is converted to `/^foo$/` of `RegExp`.
* Strings like `"/^foo/i"` are converted to `/^foo/i` of `RegExp`.
*
* @see https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/src/utils/regexp.ts
* @param string The string to convert.
* @returns Returns the `RegExp`.
*/
Expand Down