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 @@ -53,6 +53,7 @@
"prefer-shorthand-boolean",
"prefer-shorthand-fragment",
"jsx-no-duplicate-props",
"jsx-no-undef",
"jsx-uses-vars",
"---DOM Rules---",
"dom-no-dangerously-set-innerhtml",
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 @@ -76,6 +76,7 @@ Linter rules can have false positives, false negatives, and some rules are depen
| [`prefer-shorthand-boolean`](./prefer-shorthand-boolean) | 0️⃣ | `🔧` | Enforces shorthand syntax for boolean attributes | |
| [`prefer-shorthand-fragment`](./prefer-shorthand-fragment) | 0️⃣ | `🔧` | Enforces shorthand syntax for fragments | |
| [`jsx-no-duplicate-props`](./jsx-no-duplicate-props) | 1️⃣ | | Disallow duplicate props in JSX elements | |
| [`jsx-no-undef`](./jsx-no-undef) | 2️⃣ | | Disallow undefined variables in JSX elements | |
| [`jsx-uses-vars`](./jsx-uses-vars) | 1️⃣ | | Marks variables used in JSX as used | |

## DOM Rules
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const name = "react-x/recommended-typescript";
export const rules = {
...recommended.rules,
"react-x/jsx-no-duplicate-props": "off",
"react-x/jsx-no-undef": "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 @@ -4,6 +4,9 @@ import { DEFAULT_ESLINT_REACT_SETTINGS } from "@eslint-react/shared";
export const name = "react-x/recommended";

export const rules = {
"react-x/jsx-no-duplicate-props": "warn",
"react-x/jsx-no-undef": "error",
"react-x/jsx-uses-vars": "warn",
"react-x/no-access-state-in-setstate": "error",
"react-x/no-array-index-key": "warn",
"react-x/no-children-count": "warn",
Expand All @@ -20,7 +23,6 @@ export const rules = {
"react-x/no-create-ref": "error",
"react-x/no-default-props": "error",
"react-x/no-direct-mutation-state": "error",
"react-x/jsx-no-duplicate-props": "warn",
"react-x/no-duplicate-key": "warn",
"react-x/no-forward-ref": "warn",
"react-x/no-implicit-key": "warn",
Expand All @@ -41,7 +43,6 @@ export const rules = {
"react-x/no-unused-state": "warn",
"react-x/no-use-context": "warn",
"react-x/no-useless-forward-ref": "warn",
"react-x/jsx-uses-vars": "warn",
} as const satisfies RulePreset;

export const settings = {
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 @@ -2,6 +2,7 @@ import { name, version } from "../package.json";
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 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 @@ -111,6 +112,7 @@ export const plugin = {

// Part: JSX only rules
"jsx-no-duplicate-props": jsxNoDuplicateProps,
"jsx-no-undef": jsxNoUndef,
"jsx-uses-vars": jsxUsesVars,

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

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

```plain copy
react-x/jsx-no-undef
```

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

```plain copy
@eslint-react/jsx-no-undef
```

**Presets**

- `core`
- `recommended`

## Description

This rule is used to prevent the use of undefined variables in JSX. It checks for any undefined variables in the JSX code and reports them as errors.

## Examples

### Failing

```jsx
const MyComponent = () => {
return (
<div>
<Foo />
</div>
);
};
```

### Passing

```jsx
import Foo from "./Foo";

const MyComponent = () => {
return (
<div>
<Foo />
</div>
);
};
```

```jsx
const Foo = () => <div>Foo</div>;

const MyComponent = () => {
return (
<div>
<Foo />
</div>
);
};
```

## Implementation

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

import { ruleTester } from "../../../../../test";
import rule, { RULE_NAME } from "./jsx-no-undef";

ruleTester.run(RULE_NAME, rule, {
invalid: [
{
code: tsx`
const element = <Foo />;
`,
errors: [
{
messageId: "jsxNoUndef",
data: { name: "Foo" },
},
],
},
{
code: tsx`
const element = <Foo />;
const element = <Bar />;
`,
errors: [
{
messageId: "jsxNoUndef",
data: { name: "Foo" },
},
{
messageId: "jsxNoUndef",
data: { name: "Bar" },
},
],
},
{
code: tsx`
function Foo() {
return <Bar />;
}
`,
errors: [
{
messageId: "jsxNoUndef",
data: { name: "Bar" },
},
],
},
],
valid: [
{
code: tsx`
function Foo() {
return <div />;
}
function Bar() {
return <Foo />;
}
`,
},
{
code: tsx`
import { Foo } from "./Foo";
import { Bar } from "./Bar";

function App() {
return (
<div>
<Foo />
<Bar />
</div>
);
}
`,
},
],
});
56 changes: 56 additions & 0 deletions packages/plugins/eslint-plugin-react-x/src/rules/jsx-no-undef.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import type { RuleContext, RuleFeature } from "@eslint-react/kit";
import type { RuleListener } from "@typescript-eslint/utils/ts-eslint";
import type { CamelCase } from "string-ts";
import * as VAR from "@eslint-react/var";

import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
import { createRule } from "../utils";

export const RULE_NAME = "jsx-no-undef";

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

export type MessageID = CamelCase<typeof RULE_NAME>;

export default createRule<[], MessageID>({
meta: {
type: "problem",
docs: {
description: "Disallow undefined variables in JSX.",
[Symbol.for("rule_features")]: RULE_FEATURES,
},
messages: {
jsxNoUndef: "JSX variable {{name}} is not defined.",
},
schema: [],
},
name: RULE_NAME,
create,
defaultOptions: [],
});

export function create(context: RuleContext<MessageID, []>): RuleListener {
return {
JSXIdentifier(node) {
if (node.name === "this") {
return;
}
// Skip JsxIntrinsicElements
if (/^[a-z]/u.test(node.name)) {
return;
}
// Skip JSXMemberExpression property
if (node.parent.type === T.JSXMemberExpression && node.parent.property === node) {
return;
}
const initialScope = context.sourceCode.getScope(node);
if (VAR.findVariable(node.name, initialScope) == null) {
context.report({
messageId: "jsxNoUndef",
node,
data: { name: node.name },
});
}
},
};
}
5 changes: 3 additions & 2 deletions packages/plugins/eslint-plugin/src/configs/all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ export const name = "@eslint-react/all";
export const rules = {
"@eslint-react/avoid-shorthand-boolean": "off",
"@eslint-react/avoid-shorthand-fragment": "off",
"@eslint-react/jsx-no-duplicate-props": "warn",
"@eslint-react/jsx-no-undef": "error",
"@eslint-react/jsx-uses-vars": "warn",
"@eslint-react/no-access-state-in-setstate": "error",
"@eslint-react/no-array-index-key": "warn",
"@eslint-react/no-children-count": "warn",
Expand All @@ -31,7 +34,6 @@ export const rules = {
"@eslint-react/no-create-ref": "error",
"@eslint-react/no-default-props": "error",
"@eslint-react/no-direct-mutation-state": "error",
"@eslint-react/jsx-no-duplicate-props": "warn",
"@eslint-react/no-duplicate-key": "warn",
"@eslint-react/no-forward-ref": "warn",
"@eslint-react/no-implicit-key": "warn",
Expand All @@ -58,7 +60,6 @@ export const rules = {
"@eslint-react/prefer-destructuring-assignment": "warn",
"@eslint-react/prefer-shorthand-boolean": "warn",
"@eslint-react/prefer-shorthand-fragment": "warn",
"@eslint-react/jsx-uses-vars": "warn",

"@eslint-react/dom/no-dangerously-set-innerhtml": "warn",
"@eslint-react/dom/no-dangerously-set-innerhtml-with-children": "error",
Expand Down
5 changes: 3 additions & 2 deletions packages/plugins/eslint-plugin/src/configs/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import reactx from "eslint-plugin-react-x";
export const name = "@eslint-react/core";

export const rules = {
"@eslint-react/jsx-no-duplicate-props": "warn",
"@eslint-react/jsx-no-undef": "error",
"@eslint-react/jsx-uses-vars": "warn",
"@eslint-react/no-access-state-in-setstate": "error",
"@eslint-react/no-array-index-key": "warn",
"@eslint-react/no-children-count": "warn",
Expand All @@ -21,7 +24,6 @@ export const rules = {
"@eslint-react/no-create-ref": "error",
"@eslint-react/no-default-props": "error",
"@eslint-react/no-direct-mutation-state": "error",
"@eslint-react/jsx-no-duplicate-props": "warn",
"@eslint-react/no-duplicate-key": "warn",
"@eslint-react/no-forward-ref": "warn",
"@eslint-react/no-implicit-key": "warn",
Expand All @@ -43,7 +45,6 @@ export const rules = {
"@eslint-react/no-use-context": "warn",
"@eslint-react/no-useless-forward-ref": "warn",
"@eslint-react/no-useless-fragment": "warn",
"@eslint-react/jsx-uses-vars": "warn",
} as const satisfies RulePreset;

export const plugins = {
Expand Down