diff --git a/apps/website/content/docs/rules/meta.json b/apps/website/content/docs/rules/meta.json
index 9968fc71c..0c6e071fa 100644
--- a/apps/website/content/docs/rules/meta.json
+++ b/apps/website/content/docs/rules/meta.json
@@ -78,7 +78,6 @@
"dom-no-unsafe-target-blank",
"dom-no-use-form-state",
"dom-no-void-elements-with-children",
- "dom-prefer-namespace-import",
"---Web API Rules---",
"web-api-no-leaked-event-listener",
"web-api-no-leaked-interval",
diff --git a/packages/plugins/eslint-plugin-react-dom/src/rules/prefer-namespace-import.mdx b/packages/plugins/eslint-plugin-react-dom/src/rules/prefer-namespace-import.mdx
deleted file mode 100644
index 75b0c13cb..000000000
--- a/packages/plugins/eslint-plugin-react-dom/src/rules/prefer-namespace-import.mdx
+++ /dev/null
@@ -1,45 +0,0 @@
----
-title: prefer-namespace-import
----
-
-**Full Name in `eslint-plugin-react-dom`**
-
-```sh copy
-react-dom/prefer-namespace-import
-```
-
-**Full Name in `@eslint-react/eslint-plugin`**
-
-```sh copy
-@eslint-react/dom/prefer-namespace-import
-```
-
-**Features**
-
-`๐ง`
-
-## Description
-
-Enforces React DOM is imported via a namespace import.
-
-## Examples
-
-### Failing
-
-```tsx
-import ReactDOM from "react-dom/client";
-
-import type ReactDOM from "react-dom/client";
-```
-
-### Passing
-
-```tsx
-import * as ReactDOM from "react-dom/client";
-import type * as ReactDOM from "react-dom/client";
-```
-
-## Implementation
-
-- [Rule Source](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom/src/rules/prefer-namespace-import.ts)
-- [Test Source](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom/src/rules/prefer-namespace-import.spec.ts)
diff --git a/packages/plugins/eslint-plugin-react-hooks-extra/src/rules/no-direct-set-state-in-use-layout-effect.mdx b/packages/plugins/eslint-plugin-react-hooks-extra/src/rules/no-direct-set-state-in-use-layout-effect.mdx
index d55bd1239..088c497e3 100644
--- a/packages/plugins/eslint-plugin-react-hooks-extra/src/rules/no-direct-set-state-in-use-layout-effect.mdx
+++ b/packages/plugins/eslint-plugin-react-hooks-extra/src/rules/no-direct-set-state-in-use-layout-effect.mdx
@@ -2,8 +2,6 @@
title: no-direct-set-state-in-use-layout-effect
---
-This rule is experimental and may change in the future or be removed. It is not recommended to use it in production code at this time.
-
**Full Name in `eslint-plugin-react-hooks-extra`**
```sh copy
@@ -20,12 +18,6 @@ react-hooks-extra/no-direct-set-state-in-use-layout-effect
`๐งช`
-**Presets**
-
-- `recommended`
-- `recommended-typescript`
-- `recommended-type-checked`
-
## Description
Disallow **direct** calls to the [`set` function](https://react.dev/reference/react/useState#setstate) of `useState` in `useLayoutEffect`.
@@ -149,7 +141,11 @@ export default function RemoteContent() {
}
```
-The following examples are derived from the [React documentation](https://react.dev/learn/you-might-not-need-an-effect):
+
+ If you need to fetch remote data within the component, consider using libraries like [TanStack Query](https://tanstack.com/query/v3/) or [SWR](https://swr.vercel.app/). They handle caching, re-fetching, and state management for you, making your code cleaner and more efficient.
+
+
+The following examples are derived from the [React Docs: You Might Not Need an Effect](https://react.dev/learn/you-might-not-need-an-effect):
### Failing
diff --git a/packages/plugins/eslint-plugin-react-naming-convention/src/rules/component-name.mdx b/packages/plugins/eslint-plugin-react-naming-convention/src/rules/component-name.mdx
index 7adb72fe8..6c9addc94 100644
--- a/packages/plugins/eslint-plugin-react-naming-convention/src/rules/component-name.mdx
+++ b/packages/plugins/eslint-plugin-react-naming-convention/src/rules/component-name.mdx
@@ -54,7 +54,7 @@ function MyComponent() {
- `excepts`: (optional) An array of component names that are allowed to not follow the rule.
- `allowAllCaps`: (optional) If `true`, allows all caps component names. Default is `false`.
-## Rule Options Examples
+## Configuration Examples
```json
{
diff --git a/packages/plugins/eslint-plugin-react-naming-convention/src/rules/component-name.ts b/packages/plugins/eslint-plugin-react-naming-convention/src/rules/component-name.ts
index 15333d412..c35345ff7 100644
--- a/packages/plugins/eslint-plugin-react-naming-convention/src/rules/component-name.ts
+++ b/packages/plugins/eslint-plugin-react-naming-convention/src/rules/component-name.ts
@@ -15,16 +15,6 @@ type Options = readonly [
| Case
| {
allowAllCaps?: boolean;
- /**
- * @todo Remove in the next major version
- * @deprecated Component names now need to start with an uppercase letter instead of a non-lowercase letter. This means `_Button` or `_component` are no longer valid. (@kassens) in https://github.com/facebook/react/pull/25162
- */
- allowLeadingUnderscore?: boolean;
- /**
- * @todo Remove in the next major version
- * @deprecated This option has no actual effect on the rule
- */
- allowNamespace?: boolean;
excepts?: readonly string[];
rule?: Case;
},
@@ -50,16 +40,6 @@ const schema = [
additionalProperties: false,
properties: {
allowAllCaps: { type: "boolean" },
- /**
- * @todo Remove in the next major version
- * @deprecated
- */
- allowLeadingUnderscore: { type: "boolean" },
- /**
- * @todo Remove in the next major version
- * @deprecated
- */
- allowNamespace: { type: "boolean" },
excepts: {
type: "array",
items: { type: "string", format: "regex" },
diff --git a/packages/plugins/eslint-plugin-react-naming-convention/src/rules/filename-extension.mdx b/packages/plugins/eslint-plugin-react-naming-convention/src/rules/filename-extension.mdx
index ccac381b2..b9045a3c2 100644
--- a/packages/plugins/eslint-plugin-react-naming-convention/src/rules/filename-extension.mdx
+++ b/packages/plugins/eslint-plugin-react-naming-convention/src/rules/filename-extension.mdx
@@ -34,7 +34,7 @@ This rule enforces consistent file extensions for JSX files.
- `extensions`: List of file extensions that should be treated as JSX files. Default is `[".jsx", ".tsx"]`.
- `ignoreFilesWithoutCode`: When set to `true`, this rule will ignore files that do not contain JSX syntax. Default is `true`.
-## Rule Options Examples
+## Configuration Examples
```js title="eslint.config.js"
// ...
diff --git a/packages/plugins/eslint-plugin-react-naming-convention/src/rules/filename.mdx b/packages/plugins/eslint-plugin-react-naming-convention/src/rules/filename.mdx
index 91ccc27c5..9d42c03e9 100644
--- a/packages/plugins/eslint-plugin-react-naming-convention/src/rules/filename.mdx
+++ b/packages/plugins/eslint-plugin-react-naming-convention/src/rules/filename.mdx
@@ -20,106 +20,117 @@ react-naming-convention/filename
## Description
-Enforces consistent file naming conventions.
+This rule enforces consistent file naming conventions for React components, hooks, and other project files.
+
+By default, this rule enforces `PascalCase`, but it can be configured to support `camelCase`, `kebab-case`, or `snake_case` to match your project's standards.
## Examples
### Failing
-```bash title="Terminal" {3}
-npx eslint --rule '@eslint-react/naming-convention/filename: ["warn", { "rule": "PascalCase" }]' .
-
-src/components/component.tsx
- 1:1 error "File name `component.tsx` does not match `PascalCase`. Should rename to `Component.tsx` @eslint-react/naming-convention/filename
+If the rule is configured for `PascalCase`, the following filename will trigger a warning:
-โ 1 problems (0 errors, 1 warnings)
+```js title="eslint.config.js"
+export default [
+ {
+ files: ["**/*.tsx"],
+ rules: {
+ "@eslint-react/naming-convention/filename": ["warn", { rule: "PascalCase" }],
+ },
+ },
+];
```
-```bash {3}
-npx eslint --rule '@eslint-react/naming-convention/filename: ["warn", { "rule": "kebab-case" }]' .
+```bash title="src/components/component.tsx"
+# File name "component.tsx" does not match PascalCase.
+```
-src/components/example_component.tsx
- 1:1 error "File name `example_component.tsx` does not match `kebab-case`. Should rename to `example-component.tsx` @eslint-react/naming-convention/filename
+```text title="ESLint Output"
+src/components/component.tsx
+ 1:1 error File name `component.tsx` does not match `PascalCase`. Should rename to `Component.tsx` @eslint-react/naming-convention/filename
-โ 1 problems (0 errors, 1 warnings)
+โ 1 problem (1 error, 0 warnings)
```
### Passing
-```bash title="Terminal"
-npx eslint --rule '@eslint-react/naming-convention/filename: ["warn", { "rule": "PascalCase" }]' .
+With the same `PascalCase` configuration, this file name is valid:
-src/components/Component.tsx
-
-โจ Done in 0.61s.
+```bash title="src/components/Component.tsx"
+# Correctly named file.
```
-```bash title="Terminal"
-npx eslint --rule '@eslint-react/naming-convention/filename: ["warn", { "rule": "kebab-case" }]' .
+This file will pass without any warnings.
-src/components/example-component.tsx
+## Rule Options
-โจ Done in 0.61s.
-```
+The rule accepts an object with the following properties:
-## Rule Options
+- `rule`: The naming convention to enforce. Default: `"PascalCase"`.
+ - `"PascalCase"`: `ExampleComponent.tsx`
+ - `"camelCase"`: `exampleComponent.tsx`
+ - `"kebab-case"`: `example-component.tsx`
+ - `"snake_case"`: `example_component.tsx`
+- `excepts`: An array of strings or regex patterns to exclude certain file names from this rule. The default exceptions are designed to accommodate common patterns in modern web frameworks:
+ - `"index"`: Ignores `index` files (e.g., `index.ts`, `index.tsx`).
+ - `"/^_/"`: Ignores files starting with an underscore (e.g., `_app.tsx`, `_layout.tsx`).
+ - `"/^\\$/"`: Ignores files starting with a dollar sign (e.g., `$.tsx`).
+ - `"/^[0-9]+$/"`: Ignores files that are purely numeric (e.g., `404.tsx`).
+ - `"/^\[[^\]]+\]$/"`: Ignores files with dynamic route segments in brackets (e.g., `[slug].tsx`).
-- `rule`: The rule to apply to the file name. Default is `"PascalCase"`. Possible values:
- 1. `PascalCase`: PascalCase
- 2. `camelCase`: camelCase
- 3. `kebab-case`: kebab-case
- 4. `snake_case`: snake\_case
+## Configuration Examples
-## Rule Options Examples
+### Enforcing `kebab-case`
```js title="eslint.config.js"
-// ...
export default [
- // ...
{
files: ["**/*.tsx"],
rules: {
- "@eslint-react/naming-convention/filename": ["warn", "PascalCase"]
- }
+ "@eslint-react/naming-convention/filename": ["warn", { rule: "kebab-case" }],
+ },
+ },
];
```
+### Different Rules for Different Files
+
+You can apply different conventions to different parts of your project. For example, use `PascalCase` for components and `camelCase` for hooks.
+
```js title="eslint.config.js"
-// ...
export default [
- // ...
{
- files: ["**/*.tsx"],
+ files: ["src/components/**/*.{ts,tsx}"],
+ rules: {
+ "@eslint-react/naming-convention/filename": ["warn", { rule: "PascalCase" }],
+ },
+ },
+ {
+ files: ["src/hooks/**/use*.{ts,tsx}"],
rules: {
- "@eslint-react/naming-convention/filename": ["warn", { "rule": "kebab-case" }]
- }
+ "@eslint-react/naming-convention/filename": ["warn", { rule: "camelCase" }],
+ },
+ },
];
```
-### Applying different rules to different files
+### Disabling the Rule for Specific Directories
+
+Framework-specific directories like Next.js's `app` router often have their own naming conventions. You can disable the rule for these directories.
```js title="eslint.config.js"
-// ...
export default [
- // ...
{
- files: ["src/**/*.{ts,tsx}"],
- ignore: ["**/index.{ts,tsx}"],
+ files: ["**/*.{ts,tsx}"],
rules: {
- "@eslint-react/naming-convention/filename": ["warn", "PascalCase"],
+ "@eslint-react/naming-convention/filename": ["warn", { rule: "kebab-case" }],
},
},
{
- files: ["src/pages/**/*.{ts,tsx}"],
- ignore: ["**/index.{ts,tsx}"],
+ // Opt-out for files in the 'app' directory
+ files: ["app/**/*.{ts,tsx}"],
rules: {
- "@eslint-react/naming-convention/filename": ["warn", "kebab-case"],
- },
- },
- {
- files: ["src/hooks/**/use*.{ts,tsx}"],
- rules: {
- "@eslint-react/naming-convention/filename": ["warn", "camelCase"],
+ "@eslint-react/naming-convention/filename": "off",
},
},
];
@@ -129,10 +140,3 @@ export default [
- [Rule source](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-naming-convention/src/rules/filename.ts)
- [Test source](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-naming-convention/src/rules/filename.spec.ts)
-
----
-
-## See Also
-
-- [`filename-extension`](./filename-extension):
- Enforces consistent use of the JSX file extension.
diff --git a/packages/plugins/eslint-plugin-react-naming-convention/src/rules/filename.spec.ts b/packages/plugins/eslint-plugin-react-naming-convention/src/rules/filename.spec.ts
index 62d6000f6..40754c06f 100644
--- a/packages/plugins/eslint-plugin-react-naming-convention/src/rules/filename.spec.ts
+++ b/packages/plugins/eslint-plugin-react-naming-convention/src/rules/filename.spec.ts
@@ -110,6 +110,21 @@ ruleTester.run(RULE_NAME, rule, {
filename: "snake_case.tsx",
options: [{ rule: "camelCase" }],
},
+ {
+ code,
+ errors: [
+ {
+ messageId: "filenameInvalid",
+ data: {
+ name: "snake_case.test.tsx",
+ rule: "camelCase",
+ suggestion: "snakeCase.test.tsx",
+ },
+ },
+ ],
+ filename: "snake_case.test.tsx",
+ options: [{ rule: "camelCase" }],
+ },
],
valid: [
{
@@ -151,5 +166,35 @@ ruleTester.run(RULE_NAME, rule, {
filename: "snake_case.tsx",
options: [{ rule: "snake_case" }],
},
+ {
+ code,
+ filename: "snake_case.test.tsx",
+ options: [{ rule: "snake_case" }],
+ },
+ {
+ code,
+ filename: "404.tsx",
+ options: [{ rule: "PascalCase" }],
+ },
+ {
+ code,
+ filename: "$.tsx",
+ options: [{ rule: "PascalCase" }],
+ },
+ {
+ code,
+ filename: "$id.tsx",
+ options: [{ rule: "PascalCase" }],
+ },
+ {
+ code,
+ filename: "_app.tsx",
+ options: [{ rule: "PascalCase" }],
+ },
+ {
+ code,
+ filename: "[slug].tsx",
+ options: [{ rule: "PascalCase" }],
+ },
],
});
diff --git a/packages/plugins/eslint-plugin-react-naming-convention/src/rules/filename.ts b/packages/plugins/eslint-plugin-react-naming-convention/src/rules/filename.ts
index 9181ed23c..33e60c829 100644
--- a/packages/plugins/eslint-plugin-react-naming-convention/src/rules/filename.ts
+++ b/packages/plugins/eslint-plugin-react-naming-convention/src/rules/filename.ts
@@ -25,22 +25,20 @@ type Options = readonly [
| unit
| Case
| {
- /**
- * @deprecated Use ESLint's [files](https://eslint.org/docs/latest/use/configure/configuration-files#specifying-files-and-ignores) feature instead
- */
excepts?: readonly string[];
- /**
- * @deprecated Use ESLint's [files](https://eslint.org/docs/latest/use/configure/configuration-files#specifying-files-and-ignores) feature instead
- */
- extensions?: readonly string[];
rule?: Case;
},
];
const defaultOptions = [
{
- excepts: ["^index$"],
- extensions: [".js", ".jsx", ".ts", ".tsx"],
+ excepts: [
+ "index",
+ String.raw`/^_/`,
+ String.raw`/^\$/`,
+ String.raw`/^[0-9]+$/`,
+ String.raw`/^\[[^\]]+\]$/`,
+ ],
rule: "PascalCase",
},
] as const satisfies Options;
@@ -100,20 +98,17 @@ export function create(context: RuleContext): RuleListener {
: options.rule ?? "PascalCase";
const excepts = typeof options === "string"
? []
- // eslint-disable-next-line @typescript-eslint/no-deprecated
- : options.excepts ?? [];
-
- function validate(name: string, casing: Case = rule, ignores: readonly string[] = excepts) {
- const shouldIgnore = ignores
- .map((s) => RE.toRegExp(s))
- .some((pattern) => pattern.test(name));
- if (shouldIgnore) return true;
+ : (options.excepts ?? []).map((s) => RE.toRegExp(s));
+ function validate(name: string, casing: Case = rule, ignores = excepts) {
+ if (ignores.some((pattern) => pattern.test(name))) return true;
+ const filteredName = name.match(/[\w.-]/gu)?.join("") ?? "";
+ if (filteredName.length === 0) return true;
return match(casing)
- .with("PascalCase", () => RE.PASCAL_CASE.test(name))
- .with("camelCase", () => RE.CAMEL_CASE.test(name))
- .with("kebab-case", () => RE.KEBAB_CASE.test(name))
- .with("snake_case", () => RE.SNAKE_CASE.test(name))
+ .with("PascalCase", () => RE.PASCAL_CASE.test(filteredName))
+ .with("camelCase", () => RE.CAMEL_CASE.test(filteredName))
+ .with("kebab-case", () => RE.KEBAB_CASE.test(filteredName))
+ .with("snake_case", () => RE.SNAKE_CASE.test(filteredName))
.exhaustive();
}