): RuleListener {
}
}
},
- [SEL.OBJECT_DESTRUCTURING_VARIABLE_DECLARATOR](node: SEL.ObjectDestructuringVariableDeclarator) {
- const functionEntry = ctx.getCurrentEntry();
- if (functionEntry == null) return;
- getOrElseUpdate(
- declarators,
- functionEntry.node,
- () => [],
- ).push(node);
- },
};
}
diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/no-unused-props.mdx b/packages/plugins/eslint-plugin-react-x/src/rules/no-unused-props.mdx
new file mode 100644
index 0000000000..6cd7122fe5
--- /dev/null
+++ b/packages/plugins/eslint-plugin-react-x/src/rules/no-unused-props.mdx
@@ -0,0 +1,121 @@
+---
+title: no-unused-props
+---
+
+**Full Name in `eslint-plugin-react-x`**
+
+```sh copy
+react-x/no-unused-props
+```
+
+**Full Name in `@eslint-react/eslint-plugin`**
+
+```sh copy
+@eslint-react/no-unused-props
+```
+
+**Features**
+
+`๐ญ` `๐งช`
+
+**Presets**
+
+- `recommended-type-checked`
+
+## Description
+
+Warns about unused component prop declarations.
+
+Unused props increase maintenance overhead and may mislead consumers of the component into thinking the prop is required or meaningful, even when it has no effect.
+
+This is the TypeScript-only version of [`eslint-plugin-react/no-unused-prop-types`](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-unused-prop-types.md). In contrast to the original rule, this rule
+
+- doesn't support the legacy propTypes syntax
+- combines the used props of one type definition declared by multiple components
+
+## Examples
+
+### Failing
+
+```tsx
+// 'onClick' is defined in 'ButtonProps' but never used in the 'Button' component.
+type ButtonProps = {
+ children: React.ReactNode;
+ onClick: () => void;
+};
+
+function Button({ children }: ButtonProps) {
+ return ;
+}
+```
+
+```tsx
+// 'avatarUrl' is defined in 'UserProfileProps' but never used in the 'UserProfile' component.
+interface UserProfileProps {
+ username: string;
+ avatarUrl: string;
+}
+
+function UserProfile({ username }: UserProfileProps) {
+ return {username}
;
+}
+```
+
+### Passing
+
+```tsx
+// All props are used.
+type ButtonProps = {
+ children: React.ReactNode;
+ onClick: () => void;
+};
+
+function Button({ children, onClick }: ButtonProps) {
+ return (
+
+ );
+}
+```
+
+```tsx
+// 'className' is passed to the underlying element with the rest of the props.
+// '...rest' is considered a use of all props that are not destructured.
+interface TextProps extends React.HTMLAttributes {
+ children: React.ReactNode;
+ className?: string;
+}
+
+function Text({ children, ...rest }: TextProps) {
+ return {children}
;
+}
+```
+
+```tsx
+// Both components use props from the same type definition.
+interface ProfileProps {
+ userId: string; // Used by ProfilePage
+ theme: "light" | "dark"; // Used by ProfileAvatar
+}
+
+function ProfilePage({ userId }: ProfileProps) {
+ // ...
+ return User ID: {userId}
;
+}
+
+function ProfileAvatar({ theme }: ProfileProps) {
+ // ...
+ return ...
;
+}
+```
+
+## Implementation
+
+- [Rule Source](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x/src/rules/no-unused-props.ts)
+- [Test Source](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x/src/rules/no-unused-props.spec.ts)
+
+## See Also
+
+- [`no-prop-types`](/docs/rules/no-prop-types)\
+ Disallows `propTypes`
diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/no-unused-props.spec.ts b/packages/plugins/eslint-plugin-react-x/src/rules/no-unused-props.spec.ts
new file mode 100644
index 0000000000..726e6946e1
--- /dev/null
+++ b/packages/plugins/eslint-plugin-react-x/src/rules/no-unused-props.spec.ts
@@ -0,0 +1,811 @@
+import tsx from "dedent";
+
+import { allValid, ruleTesterWithTypes } from "../../../../../test";
+import rule, { RULE_NAME } from "./no-unused-props";
+
+ruleTesterWithTypes.run(RULE_NAME, rule, {
+ invalid: [
+ {
+ // interface type and later destructuring
+ code: tsx`
+ interface Props {
+ abc: string;
+ hello: string;
+ }
+
+ function Component(props: Props) {
+ const { abc } = props;
+ return null;
+ }
+ `,
+ errors: [{
+ messageId: "noUnusedProps",
+ column: 3,
+ data: {
+ name: "hello",
+ },
+ endColumn: 8,
+ endLine: 3,
+ line: 3,
+ }],
+ },
+ {
+ // interface type and direct destructuring
+ code: tsx`
+ interface Props {
+ abc: string;
+ hello: string;
+ }
+
+ function Component({ abc }: Props) {
+ return null;
+ }
+ `,
+ errors: [{
+ messageId: "noUnusedProps",
+ column: 3,
+ data: {
+ name: "hello",
+ },
+ endColumn: 8,
+ endLine: 3,
+ line: 3,
+ }],
+ },
+ {
+ // named type and later destructuring
+ code: tsx`
+ type Props = {
+ abc: string;
+ hello: string;
+ }
+
+ function Component(props: Props) {
+ const { abc } = props;
+ return null;
+ }
+ `,
+ errors: [{
+ messageId: "noUnusedProps",
+ column: 3,
+ data: {
+ name: "hello",
+ },
+ endColumn: 8,
+ endLine: 3,
+ line: 3,
+ }],
+ },
+ {
+ // interface type and direct destructuring
+ code: tsx`
+ type Props = {
+ abc: string;
+ hello: string;
+ }
+
+ function Component({ abc }: Props) {
+ return null;
+ }
+ `,
+ errors: [{
+ messageId: "noUnusedProps",
+ column: 3,
+ data: {
+ name: "hello",
+ },
+ endColumn: 8,
+ endLine: 3,
+ line: 3,
+ }],
+ },
+ {
+ // inline type and later destructuring
+ code: tsx`
+ function Component(props: { abc: string; hello: string; }) {
+ const { abc } = props;
+ return null;
+ }
+ `,
+ errors: [{
+ messageId: "noUnusedProps",
+ column: 42,
+ data: {
+ name: "hello",
+ },
+ endColumn: 47,
+ endLine: 1,
+ line: 1,
+ }],
+ },
+ {
+ // inline type and direct destructuring
+ code: tsx`
+ function Component({ abc }: { abc: string; hello: string; }) {
+ return null;
+ }
+ `,
+ errors: [{
+ messageId: "noUnusedProps",
+ column: 44,
+ data: {
+ name: "hello",
+ },
+ endColumn: 49,
+ endLine: 1,
+ line: 1,
+ }],
+ },
+ {
+ // multiple properties unused
+ code: tsx`
+ function Component({ }: { abc: string; hello: string; }) {
+ return null;
+ }
+ `,
+ errors: [{
+ messageId: "noUnusedProps",
+ column: 27,
+ data: {
+ name: "abc",
+ },
+ endColumn: 30,
+ endLine: 1,
+ line: 1,
+ }, {
+ messageId: "noUnusedProps",
+ column: 40,
+ data: {
+ name: "hello",
+ },
+ endColumn: 45,
+ endLine: 1,
+ line: 1,
+ }],
+ },
+ {
+ // interface augmentation
+ code: tsx`
+ interface Props {
+ used1: string;
+ abc: string;
+ }
+
+ interface Props {
+ used2: string;
+ hello: string;
+ }
+
+ function Component({ used1, used2 }: Props) {
+ return null;
+ }
+ `,
+ errors: [{
+ messageId: "noUnusedProps",
+ column: 3,
+ data: {
+ name: "abc",
+ },
+ endColumn: 6,
+ endLine: 3,
+ line: 3,
+ }, {
+ messageId: "noUnusedProps",
+ column: 3,
+ data: {
+ name: "hello",
+ },
+ endColumn: 8,
+ endLine: 8,
+ line: 8,
+ }],
+ },
+ {
+ // interface union
+ code: tsx`
+ interface Props1 {
+ used1: string;
+ abc: string;
+ }
+
+ interface Props2 {
+ used2: string;
+ hello: string;
+ }
+
+ function Component({ used1, used2 }: Props1 & Props2) {
+ return null;
+ }
+ `,
+ errors: [{
+ messageId: "noUnusedProps",
+ column: 3,
+ data: {
+ name: "abc",
+ },
+ endColumn: 6,
+ endLine: 3,
+ line: 3,
+ }, {
+ messageId: "noUnusedProps",
+ column: 3,
+ data: {
+ name: "hello",
+ },
+ endColumn: 8,
+ endLine: 8,
+ line: 8,
+ }],
+ },
+ {
+ // interface extends
+ code: tsx`
+ interface PropsBase {
+ used1: string;
+ abc: string;
+ }
+
+ interface Props extends PropsBase {
+ used2: string;
+ hello: string;
+ }
+
+ function Component({ used1, used2 }: Props) {
+ return null;
+ }
+ `,
+ errors: [{
+ messageId: "noUnusedProps",
+ column: 3,
+ data: {
+ name: "abc",
+ },
+ endColumn: 6,
+ endLine: 3,
+ line: 3,
+ }, {
+ messageId: "noUnusedProps",
+ column: 3,
+ data: {
+ name: "hello",
+ },
+ endColumn: 8,
+ endLine: 8,
+ line: 8,
+ }],
+ },
+ {
+ // track uses of properties on rest element
+ code: tsx`
+ function Component({ ...rest }: { abc: string; hello: string; }) {
+ return {rest.abc}
;
+ }
+ `,
+ errors: [{
+ messageId: "noUnusedProps",
+ column: 48,
+ data: {
+ name: "hello",
+ },
+ endColumn: 53,
+ endLine: 1,
+ line: 1,
+ }],
+ },
+ {
+ // track uses of properties on rest element
+ code: tsx`
+ function Component(props: { abc: string; hello: string; }) {
+ const { ...rest } = props;
+ return {rest.abc}
;
+ }
+ `,
+ errors: [{
+ messageId: "noUnusedProps",
+ column: 42,
+ data: {
+ name: "hello",
+ },
+ endColumn: 47,
+ endLine: 1,
+ line: 1,
+ }],
+ },
+ {
+ // track assignment
+ code: tsx`
+ function Component(props: { abc: string; hello: string; }) {
+ const abc = props.abc;
+ return {abc}
;
+ }
+ `,
+ errors: [{
+ messageId: "noUnusedProps",
+ column: 42,
+ data: {
+ name: "hello",
+ },
+ endColumn: 47,
+ endLine: 1,
+ line: 1,
+ }],
+ },
+ {
+ // track computed member access
+ code: tsx`
+ function Component(props: { abc: string; hello: string; }) {
+ return {props["abc"]}
;
+ }
+ `,
+ errors: [{
+ messageId: "noUnusedProps",
+ column: 42,
+ data: {
+ name: "hello",
+ },
+ endColumn: 47,
+ endLine: 1,
+ line: 1,
+ }],
+ },
+ {
+ // correct error span on complex prop type
+ code: tsx`
+ function Component({ abc }: { abc: string; hello: { abc: string; subHello: number | null }; }) {
+ return null;
+ }
+ `,
+ errors: [{
+ messageId: "noUnusedProps",
+ column: 44,
+ data: {
+ name: "hello",
+ },
+ endColumn: 49,
+ endLine: 1,
+ line: 1,
+ }],
+ },
+ {
+ // access of sub property should mark property as used
+ code: tsx`
+ function Component({ hello: { subHello } }: { abc: string; hello: { abc: string; subHello: number | null }; }) {
+ return null;
+ }
+ `,
+ errors: [{
+ messageId: "noUnusedProps",
+ column: 47,
+ data: {
+ name: "abc",
+ },
+ endColumn: 50,
+ endLine: 1,
+ line: 1,
+ }],
+ },
+ {
+ // expect no false negatives when using PropsWithChildren
+ code: tsx`
+ import { PropsWithChildren } from 'react';
+
+ type ButtonProps = {
+ backgroundColor : string;
+ onClick: () => void;
+ };
+
+ const Button = ({ backgroundColor }: PropsWithChildren) => {
+ return (
+
+ );
+ };
+ `,
+ errors: [{
+ messageId: "noUnusedProps",
+ column: 3,
+ data: {
+ name: "onClick",
+ },
+ endColumn: 10,
+ endLine: 5,
+ line: 5,
+ }],
+ },
+ {
+ // expect no false negatives when using PropsWithChildren
+ code: tsx`
+ import { PropsWithChildren } from 'react';
+
+ type ButtonProps = PropsWithChildren<{
+ backgroundColor : string;
+ onClick: () => void;
+ }>;
+
+ const Button = ({ backgroundColor }: ButtonProps) => {
+ return (
+
+ );
+ };
+ `,
+ errors: [{
+ messageId: "noUnusedProps",
+ column: 3,
+ data: {
+ name: "onClick",
+ },
+ endColumn: 10,
+ endLine: 5,
+ line: 5,
+ }],
+ },
+ {
+ // expect no false negatives when using PropsWithChildren
+ code: tsx`
+ import { PropsWithChildren } from 'react';
+
+ type ButtonProps = {
+ backgroundColor : string;
+ onClick: () => void;
+ };
+
+ const Button = ({ backgroundColor }: ButtonProps & PropsWithChildren) => {
+ return (
+
+ );
+ };
+ `,
+ errors: [{
+ messageId: "noUnusedProps",
+ column: 3,
+ data: {
+ name: "onClick",
+ },
+ endColumn: 10,
+ endLine: 5,
+ line: 5,
+ }],
+ },
+ {
+ // expect no false negatives when using PropsWithChildren
+ code: tsx`
+ import { PropsWithChildren } from 'react';
+
+ type ButtonProps = {
+ backgroundColor : string;
+ onClick: () => void;
+ } & PropsWithChildren;
+
+ const Button = ({ backgroundColor }: ButtonProps) => {
+ return (
+
+ );
+ };
+ `,
+ errors: [{
+ messageId: "noUnusedProps",
+ column: 3,
+ data: {
+ name: "onClick",
+ },
+ endColumn: 10,
+ endLine: 5,
+ line: 5,
+ }],
+ },
+ {
+ // expect no false negatives when using forwardRef
+ code: tsx`
+ import * as React from 'react'
+ interface ComponentProps {
+ foo: string;
+ }
+ const Component = React.forwardRef(function Component(props, ref) {
+ return ;
+ });
+ `,
+ errors: [{
+ messageId: "noUnusedProps",
+ column: 3,
+ data: {
+ name: "foo",
+ },
+ endColumn: 6,
+ endLine: 3,
+ line: 3,
+ }],
+ },
+ // TODO: Should we report unused ref prop?
+ // {
+ // // expect no false negatives when using ref as a prop
+ // code: tsx`
+ // import * as React from 'react'
+ // interface ComponentProps {
+ // foo: string;
+ // }
+ // const Component = function Component({ ref, ...props }: ComponentProps & { ref?: React.RefObject }) {
+ // return {props.foo}
;
+ // };
+ // `,
+ // errors: [{
+ // messageId: "noUnusedProps",
+ // data: {
+ // name: "ref",
+ // },
+ // }],
+ // },
+ ],
+ valid: [
+ // all props are used
+ tsx`
+ interface Props {
+ abc: string;
+ hello: string;
+ }
+
+ function Component(props: Props) {
+ const { abc, hello } = props;
+ return null;
+ }
+ `,
+ // all props are used
+ tsx`
+ interface Props {
+ abc: string;
+ hello: string;
+ }
+
+ function Component({ abc, hello }: Props) {
+ return null;
+ }
+ `,
+ // all props are used
+ tsx`
+ type Props = {
+ abc: string;
+ hello: string;
+ }
+
+ function Component(props: Props) {
+ const { abc, hello } = props;
+ return null;
+ }
+ `,
+ // all props are used
+ tsx`
+ type Props = {
+ abc: string;
+ hello: string;
+ }
+
+ function Component({ abc, hello }: Props) {
+ return null;
+ }
+ `,
+ // all props are used
+ tsx`
+ function Component(props: { abc: string; hello: string; }) {
+ const { abc, hello } = props;
+ return null;
+ }
+ `,
+ // all props are used
+ tsx`
+ function Component({ abc, hello }: { abc: string; hello: string; }) {
+ return null;
+ }
+ `,
+ // all props are used
+ tsx`
+ function Component({ abc: abc2, hello: hello2 }: { abc: string; hello: string; }) {
+ return null;
+ }
+ `,
+ // props are used by two components each accessing one prop
+ tsx`
+ interface Props {
+ abc: string;
+ hello: string;
+ }
+
+ function Component1({ abc }: Props) {
+ return null;
+ }
+
+ function Component2({ hello }: Props) {
+ return null;
+ }
+ `,
+ // props are used by two components each accessing a different part of it
+ tsx`
+ interface Props {
+ foo: string;
+ bar: string;
+ baz: string;
+ }
+
+ function Component1({ foo, bar }: Props) {
+ return {foo}
;
+ }
+
+ function Component2({ bar, baz }: Props) {
+ return {bar}
;
+ }
+ `,
+ // we can't track what happens to the props object
+ tsx`
+ import { Component2 } from "./component2";
+
+ interface Props {
+ abc: string;
+ hello: string;
+ }
+
+ function Component(props: Props) {
+ return ;
+ }
+ `,
+ // we can't track what happens to the props object
+ tsx`
+ import { anyFunction } from "./anyFunction";
+
+ interface Props {
+ abc: string;
+ hello: string;
+ }
+
+ function Component(props: Props) {
+ anyFunction(props);
+
+ return null;
+ }
+ `,
+ // we can't track what happens to the props object
+ tsx`
+ import { anyFunction } from "./anyFunction";
+
+ interface Props {
+ abc: string;
+ hello: string;
+ }
+
+ function Component(props: Props) {
+ anyFunction({ props });
+
+ return null;
+ }
+ `,
+ // one value used in jsx, the other in effect
+ tsx`
+ import { useEffect } from "react";
+
+ function Component({ abc, hello }: { abc: string; hello: string }) {
+ useEffect(() => {
+ console.log(hello);
+ }, []);
+ return {abc}
;
+ }
+ `,
+ // we can't track what happens to the rest object
+ tsx`
+ import { anyFunction } from "./anyFunction";
+
+ function Component({ abc, ...rest }: { abc: string; hello: string }) {
+ anyFunction(rest);
+ return null;
+ }
+ `,
+ // we can't track what happens to the rest object
+ tsx`
+ import { anyFunction } from "./anyFunction";
+
+ function Component(props: { abc: string; hello: string; }) {
+ const { abc, ...rest } = props;
+ anyFunction(rest);
+ return null;
+ }
+ `,
+ // props used inside nested function
+ tsx`
+ function Component(props: { abc: string; hello: string }) {
+ function inner() {
+ return props.hello;
+ }
+ return props.abc;
+ }
+ `,
+ // props used conditionally
+ tsx`
+ function Component(props: { abc: string; hello: string }) {
+ if (Math.random() > 0.5) {
+ return {props.abc}
;
+ }
+ return {props.hello}
;
+ }
+ `,
+ // expect no false positives when using PropsWithChildren
+ tsx`
+ import { PropsWithChildren } from 'react';
+
+ type ButtonProps = {
+ backgroundColor : string;
+ onClick: () => void;
+ };
+
+ const Button = ({ backgroundColor, onClick, children }: PropsWithChildren) => {
+ return (
+
+ );
+ };
+ `,
+ // expect no false positives when using PropsWithChildren
+ tsx`
+ import { PropsWithChildren } from 'react';
+
+ type ButtonProps = PropsWithChildren<{
+ backgroundColor : string;
+ onClick: () => void;
+ }>;
+
+ const Button = ({ backgroundColor, onClick, children }: ButtonProps) => {
+ return (
+
+ );
+ };
+ `,
+ // TODO: Should we report unused children prop when using PropsWithChildren? currently we don't
+ tsx`
+ import { PropsWithChildren } from 'react';
+
+ type ButtonProps = {
+ backgroundColor : string;
+ onClick: () => void;
+ };
+
+ const Button = ({ backgroundColor, onClick }: PropsWithChildren) => {
+ return (
+
+ );
+ };
+ `,
+ // TODO: Should we report unused children prop when using PropsWithChildren? currently we don't
+ tsx`
+ import { PropsWithChildren } from 'react';
+
+ type ButtonProps = PropsWithChildren<{
+ backgroundColor : string;
+ onClick: () => void;
+ }>;
+
+ const Button = ({ backgroundColor, onClick }: ButtonProps) => {
+ return (
+
+ );
+ };
+ `,
+ // expect no false positives when using forwardRef
+ tsx`
+ import * as React from 'react'
+ interface ComponentProps {
+ foo: string;
+ }
+ const Component = React.forwardRef(function Component(props, ref) {
+ return {props.foo}
;
+ });
+ `,
+ // expect no false positives when using ref as a prop
+ tsx`
+ import * as React from 'react'
+ interface ComponentProps {
+ foo: string;
+ }
+ const Component = function Component({ ref, ...props }: ComponentProps & { ref?: React.RefObject }) {
+ return {props.foo}
;
+ };
+ `,
+ ...allValid,
+ ],
+});
diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/no-unused-props.ts b/packages/plugins/eslint-plugin-react-x/src/rules/no-unused-props.ts
new file mode 100644
index 0000000000..6a3feea668
--- /dev/null
+++ b/packages/plugins/eslint-plugin-react-x/src/rules/no-unused-props.ts
@@ -0,0 +1,236 @@
+import * as ER from "@eslint-react/core";
+import type { RuleContext, RuleFeature } from "@eslint-react/kit";
+import type { Reference } from "@typescript-eslint/scope-manager";
+import type { TSESTree } from "@typescript-eslint/types";
+import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
+import { ESLintUtils, type ParserServicesWithTypeInformation } from "@typescript-eslint/utils";
+import type { RuleListener } from "@typescript-eslint/utils/ts-eslint";
+import type { CamelCase } from "string-ts";
+import type ts from "typescript";
+
+import { createRule } from "../utils";
+
+export const RULE_NAME = "no-unused-props";
+
+export const RULE_FEATURES = ["TSC", "EXP"] as const satisfies RuleFeature[];
+
+export type MessageID = CamelCase;
+
+export default createRule<[], MessageID>({
+ meta: {
+ type: "problem",
+ docs: {
+ description: "Warns about unused component prop declarations.",
+ [Symbol.for("rule_features")]: RULE_FEATURES,
+ },
+ messages: {
+ noUnusedProps: "Prop `{{name}}` is declared but never used",
+ },
+ schema: [],
+ },
+ name: RULE_NAME,
+ create,
+ defaultOptions: [],
+});
+
+export function create(context: RuleContext): RuleListener {
+ const services = ESLintUtils.getParserServices(context, false);
+ const { ctx, listeners } = ER.useComponentCollector(context);
+
+ return {
+ ...listeners,
+ "Program:exit"(program) {
+ const checker = services.program.getTypeChecker();
+ const components = ctx.getAllComponents(program);
+
+ const totalDeclaredProps = new Set();
+ const totalUsedProps = new Set();
+
+ for (const [, component] of components) {
+ const [props] = component.node.params;
+ if (props == null) continue;
+
+ const usedPropKeys = new Set();
+ const couldFindAllUsedPropKeys = collectUsedPropKeysOfParameter(context, usedPropKeys, props);
+ if (!couldFindAllUsedPropKeys) {
+ // unable to determine all used prop keys => bail out to avoid false positives
+ continue;
+ }
+
+ const tsNode = services.esTreeNodeToTSNodeMap.get(props);
+ const declaredProps = checker.getTypeAtLocation(tsNode).getProperties();
+
+ for (const declaredProp of declaredProps) {
+ totalDeclaredProps.add(declaredProp);
+
+ if (usedPropKeys.has(declaredProp.name)) {
+ totalUsedProps.add(declaredProp);
+ }
+ }
+ }
+
+ // TODO: Node 20 doesn't support Set.difference. Use it when minimum Node version is 22.
+ const unusedProps = [...totalDeclaredProps].filter((x) => !totalUsedProps.has(x));
+
+ for (const unusedProp of unusedProps) {
+ reportUnusedProp(context, services, unusedProp);
+ }
+ },
+ };
+}
+
+function collectUsedPropKeysOfParameter(
+ context: RuleContext,
+ usedPropKeys: Set,
+ parameter: TSESTree.Parameter,
+): boolean {
+ switch (parameter.type) {
+ case T.Identifier: {
+ return collectUsedPropKeysOfIdentifier(context, usedPropKeys, parameter);
+ }
+ case T.ObjectPattern: {
+ return collectUsedPropKeysOfObjectPattern(context, usedPropKeys, parameter);
+ }
+ default: {
+ return false;
+ }
+ }
+}
+
+function collectUsedPropKeysOfObjectPattern(
+ context: RuleContext,
+ usedPropKeys: Set,
+ objectPattern: TSESTree.ObjectPattern,
+): boolean {
+ for (const property of objectPattern.properties) {
+ switch (property.type) {
+ case T.Property: {
+ const key = getKeyOfExpression(property.key);
+ if (key == null) return false;
+ usedPropKeys.add(key);
+ break;
+ }
+ case T.RestElement: {
+ if (!collectUsedPropsOfRestElement(context, usedPropKeys, property)) {
+ return false;
+ }
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+function collectUsedPropsOfRestElement(
+ context: RuleContext,
+ usedPropKeys: Set,
+ restElement: TSESTree.RestElement,
+): boolean {
+ switch (restElement.argument.type) {
+ case T.Identifier: {
+ return collectUsedPropKeysOfIdentifier(context, usedPropKeys, restElement.argument);
+ }
+ default: {
+ return false;
+ }
+ }
+}
+
+function collectUsedPropKeysOfIdentifier(
+ context: RuleContext,
+ usedPropKeys: Set,
+ identifier: TSESTree.Identifier,
+): boolean {
+ const scope = context.sourceCode.getScope(identifier);
+ const variable = scope.variables.find((v) => v.name === identifier.name);
+ if (variable == null) return false;
+
+ for (const ref of variable.references) {
+ if (ref.identifier === identifier) {
+ continue;
+ }
+
+ if (!collectUsedPropKeysOfReference(context, usedPropKeys, identifier, ref)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+function collectUsedPropKeysOfReference(
+ context: RuleContext,
+ usedPropKeys: Set,
+ identifier: TSESTree.Identifier,
+ ref: Reference,
+): boolean {
+ const { parent } = ref.identifier;
+
+ switch (parent.type) {
+ case T.MemberExpression: {
+ if (
+ parent.object.type === T.Identifier
+ && parent.object.name === identifier.name
+ ) {
+ const key = getKeyOfExpression(parent.property);
+ if (key == null) return false;
+ usedPropKeys.add(key);
+ return true;
+ }
+ break;
+ }
+ case T.VariableDeclarator: {
+ if (
+ parent.id.type === T.ObjectPattern
+ && parent.init === ref.identifier
+ ) {
+ return collectUsedPropKeysOfObjectPattern(context, usedPropKeys, parent.id);
+ }
+ break;
+ }
+ }
+
+ return false;
+}
+
+function getKeyOfExpression(
+ expr: TSESTree.Expression | TSESTree.PrivateIdentifier,
+): string | null {
+ switch (expr.type) {
+ case T.Identifier: {
+ return expr.name;
+ }
+ case T.Literal: {
+ if (typeof expr.value === "string") {
+ return expr.value;
+ }
+ }
+ }
+
+ return null;
+}
+
+function reportUnusedProp(
+ context: RuleContext,
+ services: ParserServicesWithTypeInformation,
+ prop: ts.Symbol,
+) {
+ const declaration = prop.getDeclarations()?.[0];
+ if (declaration == null) return;
+
+ const declarationNode = services.tsNodeToESTreeNodeMap.get(declaration);
+
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+ if (declarationNode == null) return; // is undefined if declaration is in a different file
+
+ const nodeToReport = declarationNode.type === T.TSPropertySignature
+ ? declarationNode.key
+ : declarationNode;
+
+ context.report({
+ messageId: "noUnusedProps",
+ node: nodeToReport,
+ data: { name: prop.name },
+ });
+}
diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/no-unused-state.ts b/packages/plugins/eslint-plugin-react-x/src/rules/no-unused-state.ts
index b56ad2c4cc..5fb684b726 100644
--- a/packages/plugins/eslint-plugin-react-x/src/rules/no-unused-state.ts
+++ b/packages/plugins/eslint-plugin-react-x/src/rules/no-unused-state.ts
@@ -7,7 +7,7 @@ import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
import type { TSESTree } from "@typescript-eslint/utils";
import type { RuleListener } from "@typescript-eslint/utils/ts-eslint";
import type { CamelCase } from "string-ts";
-import { isMatching, match, P } from "ts-pattern";
+import { P, isMatching, match } from "ts-pattern";
import { createRule } from "../utils";
diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/no-use-context.spec.ts b/packages/plugins/eslint-plugin-react-x/src/rules/no-use-context.spec.ts
index 8f1b8a280f..8a2063233d 100644
--- a/packages/plugins/eslint-plugin-react-x/src/rules/no-use-context.spec.ts
+++ b/packages/plugins/eslint-plugin-react-x/src/rules/no-use-context.spec.ts
@@ -166,33 +166,6 @@ ruleTester.run(RULE_NAME, rule, {
},
},
},
- {
- code: tsx`
- import { use, useContext as useCtx } from 'react'
-
- export const Component = () => {
- const value = useCtx(MyContext)
- return {value}
- }
- `,
- errors: [
- { messageId: "noUseContext" },
- { messageId: "noUseContext" },
- ],
- output: tsx`
- import { use } from 'react'
-
- export const Component = () => {
- const value = use(MyContext)
- return {value}
- }
- `,
- settings: {
- "react-x": {
- version: "19.0.0",
- },
- },
- },
],
valid: [
{
diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/no-use-context.ts b/packages/plugins/eslint-plugin-react-x/src/rules/no-use-context.ts
index e4e7b7ef61..f279afcc3c 100644
--- a/packages/plugins/eslint-plugin-react-x/src/rules/no-use-context.ts
+++ b/packages/plugins/eslint-plugin-react-x/src/rules/no-use-context.ts
@@ -42,7 +42,6 @@ export function create(context: RuleContext): RuleListener {
if (compare(settings.version, "19.0.0", "<")) {
return {};
}
- const useContextNames = new Set();
const hookCalls = new Set();
return {
CallExpression(node) {
@@ -61,11 +60,6 @@ export function create(context: RuleContext): RuleListener {
if (specifier.type !== T.ImportSpecifier) continue;
if (specifier.imported.type !== T.Identifier) continue;
if (specifier.imported.name === "useContext") {
- // import { useContext as useCtx } from 'react'
- if (specifier.local.name !== "useContext") {
- // add alias to useContextAlias to keep track of it in future call expressions
- useContextNames.add(specifier.local.name);
- }
context.report({
messageId: "noUseContext",
node: specifier,
@@ -90,9 +84,8 @@ export function create(context: RuleContext): RuleListener {
}
},
"Program:exit"() {
- const isUseContextCall = ER.isReactHookCallWithNameAlias(context, "useContext", [...useContextNames]);
for (const node of hookCalls) {
- if (!isUseContextCall(node)) {
+ if (!ER.isUseContextCall(node)) {
continue;
}
context.report({
diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/no-useless-fragment.spec.ts b/packages/plugins/eslint-plugin-react-x/src/rules/no-useless-fragment.spec.ts
index c99dedb423..b1b3fa80c2 100644
--- a/packages/plugins/eslint-plugin-react-x/src/rules/no-useless-fragment.spec.ts
+++ b/packages/plugins/eslint-plugin-react-x/src/rules/no-useless-fragment.spec.ts
@@ -365,12 +365,6 @@ ruleTester.run(RULE_NAME, rule, {
{}
`,
- settings: {
- "react-x": {
- jsxPragma: "SomeReact",
- jsxPragmaFrag: "SomeFragment",
- },
- },
},
{
code: tsx`{foo}`,
diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/no-useless-fragment.ts b/packages/plugins/eslint-plugin-react-x/src/rules/no-useless-fragment.ts
index 5afdd09324..595af4e374 100644
--- a/packages/plugins/eslint-plugin-react-x/src/rules/no-useless-fragment.ts
+++ b/packages/plugins/eslint-plugin-react-x/src/rules/no-useless-fragment.ts
@@ -1,12 +1,11 @@
+/* eslint-disable jsdoc/require-param */
import * as AST from "@eslint-react/ast";
+import * as ER from "@eslint-react/core";
import type { RuleContext, RuleFeature } from "@eslint-react/kit";
+import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
import type { TSESTree } from "@typescript-eslint/utils";
import type { RuleFixer, RuleListener } from "@typescript-eslint/utils/ts-eslint";
-import * as ER from "@eslint-react/core";
-
-import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
-
import { createRule } from "../utils";
export const RULE_NAME = "no-useless-fragment";
@@ -57,30 +56,31 @@ export default createRule({
export function create(context: RuleContext, [option]: Options): RuleListener {
const { allowExpressions = true } = option;
+
return {
+ // Check JSX elements that might be fragments
JSXElement(node) {
if (!ER.isFragmentElement(context, node)) return;
checkNode(context, node, allowExpressions);
},
+ // Check JSX fragments
JSXFragment(node) {
checkNode(context, node, allowExpressions);
},
};
}
+// ----- Helper Functions -----
+
/**
* Check if a Literal or JSXText node is whitespace
- * @param node The AST node to check
- * @returns boolean `true` if the node is whitespace
*/
function isWhiteSpace(node: TSESTree.JSXText | TSESTree.Literal) {
return typeof node.value === "string" && node.raw.trim() === "";
}
/**
- * Check if a Literal or JSXText node is padding spaces
- * @param node The AST node to check
- * @returns boolean
+ * Check if a node is padding spaces (whitespace with line breaks)
*/
function isPaddingSpaces(node: TSESTree.Node) {
return ER.isJsxText(node)
@@ -88,6 +88,9 @@ function isPaddingSpaces(node: TSESTree.Node) {
&& node.raw.includes("\n");
}
+/**
+ * Trim whitespace like React would in JSX
+ */
function trimLikeReact(text: string) {
const leadingSpaces = /^\s*/.exec(text)?.[0] ?? "";
const trailingSpaces = /\s*$/.exec(text)?.[0] ?? "";
@@ -98,99 +101,104 @@ function trimLikeReact(text: string) {
return text.slice(start, end);
}
+/**
+ * Check if a fragment node is useless and should be reported
+ */
function checkNode(
context: RuleContext,
node: TSESTree.JSXElement | TSESTree.JSXFragment,
allowExpressions: boolean,
) {
const initialScope = context.sourceCode.getScope(node);
- // return if the fragment is keyed (e.g. )
- if (ER.isKeyedElement(context, node, initialScope)) {
+
+ // Skip if the fragment has a key prop (indicates it's needed for lists)
+ if (node.type === T.JSXElement && ER.hasAttribute(context, "key", node.openingElement.attributes, initialScope)) {
return;
}
- // report if the fragment is placed inside a host component (e.g. <>>
)
+
+ // Report fragment placed inside a host component (e.g. <>>
)
if (ER.isHostElement(context, node.parent)) {
context.report({
messageId: "uselessFragment",
node,
- data: {
- reason: "placed inside a host component",
- },
+ data: { reason: "placed inside a host component" },
fix: getFix(context, node),
});
}
- // report and return if the fragment has no children (e.g. <>>)
+
+ // Report empty fragments (e.g. <>>)
if (node.children.length === 0) {
context.report({
messageId: "uselessFragment",
node,
- data: {
- reason: "contains less than two children",
- },
+ data: { reason: "contains less than two children" },
fix: getFix(context, node),
});
return;
}
+
const isChildElement = AST.isOneOf([T.JSXElement, T.JSXFragment])(node.parent);
+
+ // Handle various fragment cases
switch (true) {
- // ee eeee eeee ...>} />
+ // Allow single text child in attribute value (e.g. content={<>text>})
case allowExpressions
&& !isChildElement
&& node.children.length === 1
&& ER.isJsxText(node.children.at(0)): {
return;
}
- // <>hello, world>
+
+ // Report fragment with single child inside JSX element
case !allowExpressions
&& isChildElement: {
context.report({
messageId: "uselessFragment",
node,
- data: {
- reason: "contains less than two children",
- },
+ data: { reason: "contains less than two children" },
fix: getFix(context, node),
});
return;
}
+
+ // Report fragment with single child in expressions
case !allowExpressions
&& !isChildElement
&& node.children.length === 1: {
- // const foo = <>{children}>;
- // return <>{children}>;
context.report({
messageId: "uselessFragment",
node,
- data: {
- reason: "contains less than two children",
- },
+ data: { reason: "contains less than two children" },
fix: getFix(context, node),
});
return;
}
}
+
+ // Filter out padding spaces to check actual content
const nonPaddingChildren = node.children.filter((child) => !isPaddingSpaces(child));
const firstNonPaddingChild = nonPaddingChildren.at(0);
- switch (true) {
- case nonPaddingChildren.length === 0:
- case nonPaddingChildren.length === 1
- && firstNonPaddingChild?.type !== T.JSXExpressionContainer: {
- context.report({
- messageId: "uselessFragment",
- node,
- data: {
- reason: "contains less than two children",
- },
- fix: getFix(context, node),
- });
- return;
- }
+
+ // Report if empty or only has one non-expression child
+ if (
+ nonPaddingChildren.length === 0
+ || (nonPaddingChildren.length === 1 && firstNonPaddingChild?.type !== T.JSXExpressionContainer)
+ ) {
+ context.report({
+ messageId: "uselessFragment",
+ node,
+ data: { reason: "contains less than two children" },
+ fix: getFix(context, node),
+ });
}
- return;
}
+/**
+ * Generate fix for removing useless fragment
+ */
function getFix(context: RuleContext, node: TSESTree.JSXElement | TSESTree.JSXFragment) {
if (!canFix(context, node)) return null;
+
return (fixer: RuleFixer) => {
const opener = node.type === T.JSXFragment ? node.openingFragment : node.openingElement;
const closer = node.type === T.JSXFragment ? node.closingFragment : node.closingElement;
@@ -203,20 +211,22 @@ function getFix(context: RuleContext, node: TSESTree.JSXElement | TSESTree.JSXFr
};
}
+/**
+ * Check if it's safe to automatically fix the fragment
+ */
function canFix(context: RuleContext, node: TSESTree.JSXElement | TSESTree.JSXFragment) {
+ // Don't fix fragments inside custom components (might require children to be ReactElement)
if (node.parent.type === T.JSXElement || node.parent.type === T.JSXFragment) {
- // Not safe to fix `<>foo>` because `Eeee` might require its children be a ReactElement.
return ER.isHostElement(context, node.parent);
}
- // Not safe to fix fragments without a jsx parent.
- // const a = <>>
+
+ // Don't fix empty fragments without a JSX parent
if (node.children.length === 0) {
return false;
}
- // dprint-ignore
- // const a = <>{meow}>
- if (node.children.some((child) => (ER.isJsxText(child) && !isWhiteSpace(child)) || AST.is(T.JSXExpressionContainer)(child))) {
- return false;
- }
- return true;
+
+ // Don't fix fragments with text or expressions outside of JSX context
+ return !node
+ .children
+ .some((child) => (ER.isJsxText(child) && !isWhiteSpace(child)) || AST.is(T.JSXExpressionContainer)(child));
}
diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/prefer-react-namespace-import.mdx b/packages/plugins/eslint-plugin-react-x/src/rules/prefer-namespace-import.mdx
similarity index 71%
rename from packages/plugins/eslint-plugin-react-x/src/rules/prefer-react-namespace-import.mdx
rename to packages/plugins/eslint-plugin-react-x/src/rules/prefer-namespace-import.mdx
index f6ff17a09f..c6ef1a09be 100644
--- a/packages/plugins/eslint-plugin-react-x/src/rules/prefer-react-namespace-import.mdx
+++ b/packages/plugins/eslint-plugin-react-x/src/rules/prefer-namespace-import.mdx
@@ -1,17 +1,17 @@
---
-title: prefer-react-namespace-import
+title: prefer-namespace-import
---
**Full Name in `eslint-plugin-react-x`**
```sh copy
-react-x/prefer-react-namespace-import
+react-x/prefer-namespace-import
```
**Full Name in `@eslint-react/eslint-plugin`**
```sh copy
-@eslint-react/prefer-react-namespace-import
+@eslint-react/prefer-namespace-import
```
**Features**
@@ -50,5 +50,5 @@ import type { useState } from "react";
## Implementation
-- [Rule Source](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x/src/rules/prefer-react-namespace-import.ts)
-- [Test Source](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x/src/rules/prefer-react-namespace-import.spec.ts)
+- [Rule Source](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x/src/rules/prefer-namespace-import.ts)
+- [Test Source](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x/src/rules/prefer-namespace-import.spec.ts)
diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/prefer-react-namespace-import.spec.ts b/packages/plugins/eslint-plugin-react-x/src/rules/prefer-namespace-import.spec.ts
similarity index 80%
rename from packages/plugins/eslint-plugin-react-x/src/rules/prefer-react-namespace-import.spec.ts
rename to packages/plugins/eslint-plugin-react-x/src/rules/prefer-namespace-import.spec.ts
index afa2172096..7c764e806c 100644
--- a/packages/plugins/eslint-plugin-react-x/src/rules/prefer-react-namespace-import.spec.ts
+++ b/packages/plugins/eslint-plugin-react-x/src/rules/prefer-namespace-import.spec.ts
@@ -1,48 +1,48 @@
import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
import { ruleTester } from "../../../../../test";
-import rule, { RULE_NAME } from "./prefer-react-namespace-import";
+import rule, { RULE_NAME } from "./prefer-namespace-import";
ruleTester.run(RULE_NAME, rule, {
invalid: [
{
code: `import React from 'react';`,
- errors: [{ type: T.ImportDeclaration, messageId: "preferReactNamespaceImport" }],
+ errors: [{ type: T.ImportDeclaration, messageId: "preferNamespaceImport" }],
output: `import * as React from 'react';`,
},
{
code: `import REACT from 'react';`,
- errors: [{ type: T.ImportDeclaration, messageId: "preferReactNamespaceImport" }],
+ errors: [{ type: T.ImportDeclaration, messageId: "preferNamespaceImport" }],
output: `import * as REACT from 'react';`,
},
{
code: `import type React from 'react';`,
- errors: [{ type: T.ImportDeclaration, messageId: "preferReactNamespaceImport" }],
+ errors: [{ type: T.ImportDeclaration, messageId: "preferNamespaceImport" }],
output: `import type * as React from 'react';`,
},
{
code: `import React, {useState} from 'react';`,
- errors: [{ type: T.ImportDefaultSpecifier, messageId: "preferReactNamespaceImport" }],
+ errors: [{ type: T.ImportDefaultSpecifier, messageId: "preferNamespaceImport" }],
output: `import * as React from 'react';\nimport {useState} from 'react';`,
},
{
code: `import React, {useState, useReducer} from 'react';`,
- errors: [{ type: T.ImportDefaultSpecifier, messageId: "preferReactNamespaceImport" }],
+ errors: [{ type: T.ImportDefaultSpecifier, messageId: "preferNamespaceImport" }],
output: `import * as React from 'react';\nimport {useState, useReducer} from 'react';`,
},
{
code: `import REACT, {useState} from 'react';`,
- errors: [{ type: T.ImportDefaultSpecifier, messageId: "preferReactNamespaceImport" }],
+ errors: [{ type: T.ImportDefaultSpecifier, messageId: "preferNamespaceImport" }],
output: `import * as REACT from 'react';\nimport {useState} from 'react';`,
},
{
code: `import type React, {useState} from 'react';`,
- errors: [{ type: T.ImportDefaultSpecifier, messageId: "preferReactNamespaceImport" }],
+ errors: [{ type: T.ImportDefaultSpecifier, messageId: "preferNamespaceImport" }],
output: `import type * as React from 'react';\nimport type {useState} from 'react';`,
},
{
code: `import type React, {useState} from '@pika/react';`,
- errors: [{ type: T.ImportDefaultSpecifier, messageId: "preferReactNamespaceImport" }],
+ errors: [{ type: T.ImportDefaultSpecifier, messageId: "preferNamespaceImport" }],
output: `import type * as React from '@pika/react';\nimport type {useState} from '@pika/react';`,
settings: {
"react-x": {
@@ -52,42 +52,42 @@ ruleTester.run(RULE_NAME, rule, {
},
{
code: `import React from "react";`,
- errors: [{ type: T.ImportDeclaration, messageId: "preferReactNamespaceImport" }],
+ errors: [{ type: T.ImportDeclaration, messageId: "preferNamespaceImport" }],
output: `import * as React from "react";`,
},
{
code: `import REACT from "react";`,
- errors: [{ type: T.ImportDeclaration, messageId: "preferReactNamespaceImport" }],
+ errors: [{ type: T.ImportDeclaration, messageId: "preferNamespaceImport" }],
output: `import * as REACT from "react";`,
},
{
code: `import type React from "react";`,
- errors: [{ type: T.ImportDeclaration, messageId: "preferReactNamespaceImport" }],
+ errors: [{ type: T.ImportDeclaration, messageId: "preferNamespaceImport" }],
output: `import type * as React from "react";`,
},
{
code: `import React, {useState} from "react";`,
- errors: [{ type: T.ImportDefaultSpecifier, messageId: "preferReactNamespaceImport" }],
+ errors: [{ type: T.ImportDefaultSpecifier, messageId: "preferNamespaceImport" }],
output: `import * as React from "react";\nimport {useState} from "react";`,
},
{
code: `import React, {useState, useReducer} from "react";`,
- errors: [{ type: T.ImportDefaultSpecifier, messageId: "preferReactNamespaceImport" }],
+ errors: [{ type: T.ImportDefaultSpecifier, messageId: "preferNamespaceImport" }],
output: `import * as React from "react";\nimport {useState, useReducer} from "react";`,
},
{
code: `import REACT, {useState} from "react";`,
- errors: [{ type: T.ImportDefaultSpecifier, messageId: "preferReactNamespaceImport" }],
+ errors: [{ type: T.ImportDefaultSpecifier, messageId: "preferNamespaceImport" }],
output: `import * as REACT from "react";\nimport {useState} from "react";`,
},
{
code: `import type React, {useState} from "react";`,
- errors: [{ type: T.ImportDefaultSpecifier, messageId: "preferReactNamespaceImport" }],
+ errors: [{ type: T.ImportDefaultSpecifier, messageId: "preferNamespaceImport" }],
output: `import type * as React from "react";\nimport type {useState} from "react";`,
},
{
code: `import type React, {useState} from "@pika/react";`,
- errors: [{ type: T.ImportDefaultSpecifier, messageId: "preferReactNamespaceImport" }],
+ errors: [{ type: T.ImportDefaultSpecifier, messageId: "preferNamespaceImport" }],
output: `import type * as React from "@pika/react";\nimport type {useState} from "@pika/react";`,
settings: {
"react-x": {
@@ -97,7 +97,7 @@ ruleTester.run(RULE_NAME, rule, {
},
{
code: `import React from 'react'`,
- errors: [{ type: T.ImportDeclaration, messageId: "preferReactNamespaceImport" }],
+ errors: [{ type: T.ImportDeclaration, messageId: "preferNamespaceImport" }],
output: `import * as React from 'react'`,
},
],
diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/prefer-react-namespace-import.ts b/packages/plugins/eslint-plugin-react-x/src/rules/prefer-namespace-import.ts
similarity index 89%
rename from packages/plugins/eslint-plugin-react-x/src/rules/prefer-react-namespace-import.ts
rename to packages/plugins/eslint-plugin-react-x/src/rules/prefer-namespace-import.ts
index 6bae29306f..d74b107fcb 100644
--- a/packages/plugins/eslint-plugin-react-x/src/rules/prefer-react-namespace-import.ts
+++ b/packages/plugins/eslint-plugin-react-x/src/rules/prefer-namespace-import.ts
@@ -6,7 +6,7 @@ import type { CamelCase } from "string-ts";
import { createRule } from "../utils";
-export const RULE_NAME = "prefer-react-namespace-import";
+export const RULE_NAME = "prefer-namespace-import";
export const RULE_FEATURES = [
"FIX",
@@ -23,7 +23,7 @@ export default createRule<[], MessageID>({
},
fixable: "code",
messages: {
- preferReactNamespaceImport: "Prefer importing React as 'import * as React from \"{{importSource}}\"';",
+ preferNamespaceImport: "Prefer importing React as 'import * as React from \"{{importSource}}\"';",
},
schema: [],
},
@@ -35,12 +35,11 @@ export default createRule<[], MessageID>({
export function create(context: RuleContext): RuleListener {
const { importSource } = getSettingsFromContext(context);
return {
- [`ImportDeclaration[source.value="${importSource}"] ImportDefaultSpecifier`](
- node: TSESTree.ImportDefaultSpecifier,
- ) {
+ // dprint-ignore
+ [`ImportDeclaration[source.value="${importSource}"] ImportDefaultSpecifier`](node: TSESTree.ImportDefaultSpecifier) {
const hasOtherSpecifiers = node.parent.specifiers.length > 1;
context.report({
- messageId: "preferReactNamespaceImport",
+ messageId: "preferNamespaceImport",
node: hasOtherSpecifiers ? node : node.parent,
data: { importSource },
fix(fixer) {
diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/prefer-shorthand-boolean.spec.ts b/packages/plugins/eslint-plugin-react-x/src/rules/prefer-shorthand-boolean.spec.ts
deleted file mode 100644
index fad413b888..0000000000
--- a/packages/plugins/eslint-plugin-react-x/src/rules/prefer-shorthand-boolean.spec.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import tsx from "dedent";
-
-import { allValid, ruleTester } from "../../../../../test";
-import rule, { RULE_NAME } from "./prefer-shorthand-boolean";
-
-ruleTester.run(RULE_NAME, rule, {
- invalid: [
- {
- code: tsx``,
- errors: [{
- messageId: "preferShorthandBoolean",
- data: { propName: "disabled" },
- }],
- output: tsx``,
- },
- {
- code: tsx``,
- errors: [{
- messageId: "preferShorthandBoolean",
- data: { propName: "foo" },
- }],
- output: tsx``,
- },
- {
- code: tsx``,
- errors: [{
- messageId: "preferShorthandBoolean",
- data: { propName: "foo" },
- }],
- output: tsx``,
- },
- ],
- valid: [
- ...allValid,
- tsx``,
- "",
- "",
- "",
- "",
- ],
-});
diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/prefer-shorthand-boolean.ts b/packages/plugins/eslint-plugin-react-x/src/rules/prefer-shorthand-boolean.ts
deleted file mode 100644
index eeb04c2c8e..0000000000
--- a/packages/plugins/eslint-plugin-react-x/src/rules/prefer-shorthand-boolean.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import type { RuleContext, RuleFeature } from "@eslint-react/kit";
-import type { TSESTree } from "@typescript-eslint/types";
-import type { RuleListener } from "@typescript-eslint/utils/ts-eslint";
-import type { CamelCase } from "string-ts";
-
-import * as ER from "@eslint-react/core";
-
-import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
-
-import { createRule } from "../utils";
-
-export const RULE_NAME = "prefer-shorthand-boolean";
-
-export const RULE_FEATURES = [
- "FIX",
-] as const satisfies RuleFeature[];
-
-export type MessageID = CamelCase;
-
-export default createRule<[], MessageID>({
- meta: {
- type: "problem",
- docs: {
- description: "Enforces shorthand syntax for boolean attributes.",
- [Symbol.for("rule_features")]: RULE_FEATURES,
- },
- fixable: "code",
- messages: {
- preferShorthandBoolean: "Use shorthand boolean attribute '{{propName}}'.",
- },
- schema: [],
- },
- name: RULE_NAME,
- create,
- defaultOptions: [],
-});
-
-export function create(context: RuleContext): RuleListener {
- return {
- JSXAttribute(node: TSESTree.JSXAttribute) {
- const { value } = node;
- const propName = ER.getAttributeName(context, node);
- const hasValueTrue = value?.type === T.JSXExpressionContainer
- && value.expression.type === T.Literal
- && value.expression.value === true;
- if (!hasValueTrue) {
- return;
- }
- context.report({
- messageId: "preferShorthandBoolean",
- node: node.value ?? node,
- data: {
- propName,
- },
- fix: (fixer) => fixer.removeRange([node.name.range[1], value.range[1]]),
- });
- },
- };
-}
diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/prefer-shorthand-fragment.ts b/packages/plugins/eslint-plugin-react-x/src/rules/prefer-shorthand-fragment.ts
deleted file mode 100644
index c497529978..0000000000
--- a/packages/plugins/eslint-plugin-react-x/src/rules/prefer-shorthand-fragment.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import * as ER from "@eslint-react/core";
-import type { RuleContext, RuleFeature } from "@eslint-react/kit";
-import type { TSESTree } from "@typescript-eslint/types";
-import type { RuleListener } from "@typescript-eslint/utils/ts-eslint";
-import type { CamelCase } from "string-ts";
-
-import { createRule } from "../utils";
-
-export const RULE_NAME = "prefer-shorthand-fragment";
-
-export const RULE_FEATURES = [
- "FIX",
-] as const satisfies RuleFeature[];
-
-export type MessageID = CamelCase;
-
-export default createRule<[], MessageID>({
- meta: {
- type: "problem",
- docs: {
- description: "Enforces shorthand syntax for fragments.",
- [Symbol.for("rule_features")]: RULE_FEATURES,
- },
- fixable: "code",
- messages: {
- preferShorthandFragment: "Use fragment shorthand syntax instead of 'Fragment' component.",
- },
- schema: [],
- },
- name: RULE_NAME,
- create,
- defaultOptions: [],
-});
-
-export function create(context: RuleContext): RuleListener {
- return {
- JSXElement(node: TSESTree.JSXElement) {
- if (!ER.isFragmentElement(context, node)) return;
- const hasAttributes = node.openingElement.attributes.length > 0;
- if (hasAttributes) {
- return;
- }
- context.report({
- messageId: "preferShorthandFragment",
- node,
- fix: (fixer) => {
- const { closingElement, openingElement } = node;
- if (closingElement == null) {
- return [];
- }
- return [
- fixer.replaceTextRange([openingElement.range[0], openingElement.range[1]], "<>"),
- fixer.replaceTextRange([closingElement.range[0], closingElement.range[1]], ">"),
- ];
- },
- });
- },
- };
-}
diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/prefer-use-state-lazy-initialization.mdx b/packages/plugins/eslint-plugin-react-x/src/rules/prefer-use-state-lazy-initialization.mdx
index 6f36ed609f..89bcd1e66e 100644
--- a/packages/plugins/eslint-plugin-react-x/src/rules/prefer-use-state-lazy-initialization.mdx
+++ b/packages/plugins/eslint-plugin-react-x/src/rules/prefer-use-state-lazy-initialization.mdx
@@ -2,16 +2,16 @@
title: prefer-use-state-lazy-initialization
---
-**Full Name in `eslint-plugin-react-hooks-extra`**
+**Full Name in `eslint-plugin-react-x`**
```sh copy
-react-hooks-extra/prefer-use-state-lazy-initialization
+react-x/prefer-use-state-lazy-initialization
```
**Full Name in `@eslint-react/eslint-plugin`**
```sh copy
-@eslint-react/hooks-extra/prefer-use-state-lazy-initialization
+@eslint-react/prefer-use-state-lazy-initialization
```
**Presets**
@@ -63,9 +63,10 @@ declare function generateTodos(): string[];
## Implementation
-- [Rule Source](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-hooks-extra/src/rules/prefer-use-state-lazy-initialization.ts)
-- [Test Source](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-hooks-extra/src/rules/prefer-use-state-lazy-initialization.spec.ts)
+- [Rule Source](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x/src/rules/prefer-use-state-lazy-initialization.ts)
+- [Test Source](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x/src/rules/prefer-use-state-lazy-initialization.spec.ts)
## Further Reading
- [React Docs: `useState` Hook](https://react.dev/reference/react/useState#setstate)
+ - # [Avoiding recreating the initial state](https://react.dev/reference/react/useState#avoiding-recreating-the-initial-state)
diff --git a/packages/plugins/eslint-plugin-react-hooks-extra/src/rules/prefer-use-state-lazy-initialization.spec.ts b/packages/plugins/eslint-plugin-react-x/src/rules/prefer-use-state-lazy-initialization.spec.ts
similarity index 92%
rename from packages/plugins/eslint-plugin-react-hooks-extra/src/rules/prefer-use-state-lazy-initialization.spec.ts
rename to packages/plugins/eslint-plugin-react-x/src/rules/prefer-use-state-lazy-initialization.spec.ts
index 2061388b92..8f0187b7dc 100644
--- a/packages/plugins/eslint-plugin-react-hooks-extra/src/rules/prefer-use-state-lazy-initialization.spec.ts
+++ b/packages/plugins/eslint-plugin-react-x/src/rules/prefer-use-state-lazy-initialization.spec.ts
@@ -139,22 +139,6 @@ ruleTester.run(RULE_NAME, rule, {
},
},
},
- {
- code: tsx`useLocalStorageState(1 || getValue())`,
- errors: [
- {
- type: T.CallExpression,
- messageId: "preferUseStateLazyInitialization",
- },
- ],
- settings: {
- "react-x": {
- additionalHooks: {
- useState: ["useLocalStorageState"],
- },
- },
- },
- },
],
valid: [
...allValid,
@@ -248,15 +232,5 @@ ruleTester.run(RULE_NAME, rule, {
},
},
},
- {
- code: "useLocalStorage(() => JSON.parse('{}'))",
- settings: {
- "react-x": {
- additionalHooks: {
- useState: ["useLocalStorage"],
- },
- },
- },
- },
],
});
diff --git a/packages/plugins/eslint-plugin-react-hooks-extra/src/rules/prefer-use-state-lazy-initialization.ts b/packages/plugins/eslint-plugin-react-x/src/rules/prefer-use-state-lazy-initialization.ts
similarity index 81%
rename from packages/plugins/eslint-plugin-react-hooks-extra/src/rules/prefer-use-state-lazy-initialization.ts
rename to packages/plugins/eslint-plugin-react-x/src/rules/prefer-use-state-lazy-initialization.ts
index 08c01ad7b9..3a17dae6a8 100644
--- a/packages/plugins/eslint-plugin-react-hooks-extra/src/rules/prefer-use-state-lazy-initialization.ts
+++ b/packages/plugins/eslint-plugin-react-x/src/rules/prefer-use-state-lazy-initialization.ts
@@ -2,7 +2,6 @@
import * as AST from "@eslint-react/ast";
import * as ER from "@eslint-react/core";
import type { RuleContext, RuleFeature } from "@eslint-react/kit";
-import { getSettingsFromContext } from "@eslint-react/shared";
import type { RuleListener } from "@typescript-eslint/utils/ts-eslint";
import type { CamelCase } from "string-ts";
@@ -43,14 +42,9 @@ export default createRule<[], MessageID>({
});
export function create(context: RuleContext): RuleListener {
- const alias = getSettingsFromContext(context).additionalHooks.useState ?? [];
- const isUseStateCall = ER.isReactHookCallWithNameAlias(context, "useState", alias);
return {
CallExpression(node) {
- if (!ER.isReactHookCall(node)) {
- return;
- }
- if (!isUseStateCall(node)) {
+ if (!ER.isUseStateCall(node)) {
return;
}
const [useStateInput] = node.arguments;
@@ -60,7 +54,7 @@ export function create(context: RuleContext): RuleListener {
for (const expr of AST.getNestedNewExpressions(useStateInput)) {
if (!("name" in expr.callee)) continue;
if (ALLOW_LIST.includes(expr.callee.name)) continue;
- if (AST.findParentNode(expr, (n) => ER.isUseCall(context, n)) != null) continue;
+ if (AST.findParentNode(expr, ER.isUseCall) != null) continue;
context.report({
messageId: "preferUseStateLazyInitialization",
node: expr,
@@ -70,7 +64,7 @@ export function create(context: RuleContext): RuleListener {
if (!("name" in expr.callee)) continue;
if (ER.isReactHookName(expr.callee.name)) continue;
if (ALLOW_LIST.includes(expr.callee.name)) continue;
- if (AST.findParentNode(expr, (n) => ER.isUseCall(context, n)) != null) continue;
+ if (AST.findParentNode(expr, ER.isUseCall) != null) continue;
context.report({
messageId: "preferUseStateLazyInitialization",
node: expr,
diff --git a/packages/plugins/eslint-plugin-react-x/src/utils/index.ts b/packages/plugins/eslint-plugin-react-x/src/utils/index.ts
index 69d92f43e3..034587919d 100644
--- a/packages/plugins/eslint-plugin-react-x/src/utils/index.ts
+++ b/packages/plugins/eslint-plugin-react-x/src/utils/index.ts
@@ -1 +1,3 @@
export * from "./create-rule";
+export * from "./type-is";
+export * from "./type-variant";
diff --git a/packages/plugins/eslint-plugin-react-x/src/utils/type-is.ts b/packages/plugins/eslint-plugin-react-x/src/utils/type-is.ts
new file mode 100644
index 0000000000..708c98e82a
--- /dev/null
+++ b/packages/plugins/eslint-plugin-react-x/src/utils/type-is.ts
@@ -0,0 +1,72 @@
+/* eslint-disable jsdoc/require-param */
+import { isTypeFlagSet } from "ts-api-utils";
+import { P, isMatching } from "ts-pattern";
+import ts from "typescript";
+
+/** @internal */
+export { isFalseLiteralType, isTrueLiteralType } from "ts-api-utils";
+
+/** @internal */
+export const isAnyType = (type: ts.Type) => isTypeFlagSet(type, ts.TypeFlags.TypeParameter | ts.TypeFlags.Any);
+
+/** @internal */
+export const isBigIntType = (type: ts.Type) => isTypeFlagSet(type, ts.TypeFlags.BigIntLike);
+
+/** @internal */
+export const isBooleanType = (type: ts.Type) => isTypeFlagSet(type, ts.TypeFlags.BooleanLike);
+
+/** @internal */
+export const isEnumType = (type: ts.Type) => isTypeFlagSet(type, ts.TypeFlags.EnumLike);
+
+/** @internal */
+export const isFalsyBigIntType = (type: ts.Type) =>
+ type.isLiteral() && isMatching({ value: { base10Value: "0" } }, type);
+
+/** @internal */
+export const isFalsyNumberType = (type: ts.Type) => type.isNumberLiteral() && type.value === 0;
+
+/** @internal */
+export const isFalsyStringType = (type: ts.Type) => type.isStringLiteral() && type.value === "";
+
+/** @internal */
+export const isNeverType = (type: ts.Type) => isTypeFlagSet(type, ts.TypeFlags.Never);
+
+/** @internal */
+export const isNullishType = (type: ts.Type) =>
+ isTypeFlagSet(type, ts.TypeFlags.Null | ts.TypeFlags.Undefined | ts.TypeFlags.VoidLike);
+
+/** @internal */
+export const isNumberType = (type: ts.Type) => isTypeFlagSet(type, ts.TypeFlags.NumberLike);
+
+/** @internal */
+export const isObjectType = (type: ts.Type) =>
+ !isTypeFlagSet(
+ type,
+ ts.TypeFlags.Null
+ | ts.TypeFlags.Undefined
+ | ts.TypeFlags.VoidLike
+ | ts.TypeFlags.BooleanLike
+ | ts.TypeFlags.StringLike
+ | ts.TypeFlags.NumberLike
+ | ts.TypeFlags.BigIntLike
+ | ts.TypeFlags.TypeParameter
+ | ts.TypeFlags.Any
+ | ts.TypeFlags.Unknown
+ | ts.TypeFlags.Never,
+ );
+
+/** @internal */
+export const isStringType = (type: ts.Type) => isTypeFlagSet(type, ts.TypeFlags.StringLike);
+
+/** @internal */
+export const isTruthyBigIntType = (type: ts.Type) =>
+ type.isLiteral() && isMatching({ value: { base10Value: P.not("0") } }, type);
+
+/** @internal */
+export const isTruthyNumberType = (type: ts.Type) => type.isNumberLiteral() && type.value !== 0;
+
+/** @internal */
+export const isTruthyStringType = (type: ts.Type) => type.isStringLiteral() && type.value !== "";
+
+/** @internal */
+export const isUnknownType = (type: ts.Type) => isTypeFlagSet(type, ts.TypeFlags.Unknown);
diff --git a/packages/plugins/eslint-plugin-react-x/src/utils/type-variant.ts b/packages/plugins/eslint-plugin-react-x/src/utils/type-variant.ts
new file mode 100644
index 0000000000..115b52a7fa
--- /dev/null
+++ b/packages/plugins/eslint-plugin-react-x/src/utils/type-variant.ts
@@ -0,0 +1,112 @@
+import { match } from "ts-pattern";
+import type ts from "typescript";
+import {
+ isAnyType,
+ isBigIntType,
+ isBooleanType,
+ isEnumType,
+ isFalseLiteralType,
+ isFalsyBigIntType,
+ isFalsyNumberType,
+ isFalsyStringType,
+ isNeverType,
+ isNullishType,
+ isNumberType,
+ isObjectType,
+ isStringType,
+ isTrueLiteralType,
+ isTruthyBigIntType,
+ isTruthyNumberType,
+ isTruthyStringType,
+ isUnknownType,
+} from "./type-is";
+
+export type TypeVariant =
+ | "any"
+ | "bigint"
+ | "boolean"
+ | "enum"
+ | "never"
+ | "nullish"
+ | "number"
+ | "object"
+ | "string"
+ | "unknown"
+ | "falsy bigint"
+ | "falsy boolean"
+ | "falsy number"
+ | "falsy string"
+ | "truthy bigint"
+ | "truthy boolean"
+ | "truthy number"
+ | "truthy string";
+
+/**
+ * Ported from https://github.com/typescript-eslint/typescript-eslint/blob/eb736bbfc22554694400e6a4f97051d845d32e0b/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts#L826 with some enhancements
+ * Get the variants of an array of types.
+ * @param types The types to get the variants of
+ * @returns The variants of the types
+ * @internal
+ */
+export function getTypeVariants(types: ts.Type[]) {
+ const variants = new Set();
+ if (types.some(isUnknownType)) {
+ variants.add("unknown");
+ return variants;
+ }
+ if (types.some(isNullishType)) {
+ variants.add("nullish");
+ }
+ const booleans = types.filter(isBooleanType);
+ const boolean0 = booleans[0];
+ // If incoming type is either "true" or "false", there will be one type
+ // object with intrinsicName set accordingly
+ // If incoming type is boolean, there will be two type objects with
+ // intrinsicName set "true" and "false" each because of ts-api-utils.unionTypeParts()
+ if (booleans.length === 1 && boolean0 != null) {
+ if (isFalseLiteralType(boolean0)) {
+ variants.add("falsy boolean");
+ } else if (isTrueLiteralType(boolean0)) {
+ variants.add("truthy boolean");
+ }
+ } else if (booleans.length === 2) {
+ variants.add("boolean");
+ }
+ const strings = types.filter(isStringType);
+ if (strings.length > 0) {
+ const evaluated = match(strings)
+ .when((types) => types.every(isTruthyStringType), () => "truthy string")
+ .when((types) => types.every(isFalsyStringType), () => "falsy string")
+ .otherwise(() => "string" as const);
+ variants.add(evaluated);
+ }
+ const bigints = types.filter(isBigIntType);
+ if (bigints.length > 0) {
+ const evaluated = match(bigints)
+ .when((types) => types.every(isTruthyBigIntType), () => "truthy bigint")
+ .when((types) => types.every(isFalsyBigIntType), () => "falsy bigint")
+ .otherwise(() => "bigint");
+ variants.add(evaluated);
+ }
+ const numbers = types.filter(isNumberType);
+ if (numbers.length > 0) {
+ const evaluated = match(numbers)
+ .when((types) => types.every(isTruthyNumberType), () => "truthy number")
+ .when((types) => types.every(isFalsyNumberType), () => "falsy number")
+ .otherwise(() => "number" as const);
+ variants.add(evaluated);
+ }
+ if (types.some(isEnumType)) {
+ variants.add("enum");
+ }
+ if (types.some(isObjectType)) {
+ variants.add("object");
+ }
+ if (types.some(isAnyType)) {
+ variants.add("any");
+ }
+ if (types.some(isNeverType)) {
+ variants.add("never");
+ }
+ return variants;
+}
diff --git a/packages/plugins/eslint-plugin-react-x/tsdown.config.ts b/packages/plugins/eslint-plugin-react-x/tsdown.config.ts
index 49470fbf17..031761a959 100644
--- a/packages/plugins/eslint-plugin-react-x/tsdown.config.ts
+++ b/packages/plugins/eslint-plugin-react-x/tsdown.config.ts
@@ -5,11 +5,11 @@ export default {
dts: true,
entry: ["src/index.ts"],
external: ["eslint", "typescript"],
- format: ["cjs", "esm"],
+ format: ["esm"],
minify: false,
outDir: "dist",
platform: "node",
sourcemap: false,
- target: "node18",
+ target: "node20",
treeshake: true,
} satisfies Options;
diff --git a/packages/plugins/eslint-plugin/README.md b/packages/plugins/eslint-plugin/README.md
index e7c72983b2..437964b6e8 100644
--- a/packages/plugins/eslint-plugin/README.md
+++ b/packages/plugins/eslint-plugin/README.md
@@ -22,6 +22,7 @@
- [TypeScript Specialized](#typescript-specialized)
- [Other](#other)
- [Rules](#rules)
+- [Benchmark](#benchmark)
- [FAQ](#faq)
- [Roadmap](#roadmap)
- [Contributing](#contributing)
@@ -29,10 +30,10 @@
## Features
-- **Modern**: First-class support for TypeScript, React 19, and more.
-- **Flexible**: Fully customizable rule severity levels, allowing you to enforce or relax rules as needed.
-- **Performant**: Built with performance in mind, optimized for large codebases, **4-7x faster** than other ESLint plugins.
-- **Context-aware Linting**: Rules that understand the context of your code and project configuration to provide more accurate linting.
+- **Modern**: First-class support for **TypeScript**, **React 19**, and more.
+- **Flexible**: Fully customizable rule severity levels, allowing you to **enforce** or **relax** rules as needed.
+- **Performant**: Built with performance in mind, optimized for large codebases, [**4-7x faster**](https://github.com/Rel1cx/eslint-react-benchmark) than other ESLint plugins.
+- **Context-aware Linting**: Rules that understand the context of your code and [project configuration](https://eslint-react.xyz/docs/configuration/configure-project-config) to provide more **accurate** linting.
## Public Packages
@@ -53,8 +54,8 @@
> [!NOTE]\
> ESLint React requires the following minimum versions:
>
-> - Node.js: 18.18.0
-> - ESLint: 8.57.0
+> - Node.js: 20.19.0
+> - ESLint: 9.24.0
> - TypeScript: 4.9.5
### Install
@@ -68,46 +69,46 @@ npm install --save-dev typescript-eslint @eslint-react/eslint-plugin
```js
// eslint.config.js
-// @ts-check
import eslintReact from "@eslint-react/eslint-plugin";
import eslintJs from "@eslint/js";
+import { defineConfig } from "eslint/config";
import tseslint from "typescript-eslint";
-export default tseslint.config({
- files: ["**/*.ts", "**/*.tsx"],
-
- // Extend recommended rule sets from:
- // 1. ESLint JS's recommended rules
- // 2. TypeScript ESLint recommended rules
- // 3. ESLint React's recommended-typescript rules
- extends: [
- eslintJs.configs.recommended,
- tseslint.configs.recommended,
- eslintReact.configs["recommended-typescript"],
- ],
-
- // Configure language/parsing options
- languageOptions: {
- // Use TypeScript ESLint parser for TypeScript files
- parser: tseslint.parser,
- parserOptions: {
- // Enable project service for better TypeScript integration
- projectService: true,
- tsconfigRootDir: import.meta.dirname,
+export default defineConfig([
+ {
+ files: ["**/*.ts", "**/*.tsx"],
+
+ // Extend recommended rule sets from:
+ // 1. ESLint JS's recommended rules
+ // 2. TypeScript ESLint recommended rules
+ // 3. ESLint React's recommended-typescript rules
+ extends: [
+ eslintJs.configs.recommended,
+ tseslint.configs.recommended,
+ eslintReact.configs["recommended-typescript"],
+ ],
+
+ // Configure language/parsing options
+ languageOptions: {
+ // Use TypeScript ESLint parser for TypeScript files
+ parser: tseslint.parser,
+ parserOptions: {
+ // Enable project service for better TypeScript integration
+ projectService: true,
+ tsconfigRootDir: import.meta.dirname,
+ },
},
- },
- // Custom rule overrides (modify rule levels or disable rules)
- rules: {
- "@eslint-react/no-missing-key": "warn",
+ // Custom rule overrides (modify rule levels or disable rules)
+ rules: {
+ "@eslint-react/no-missing-key": "warn",
+ },
},
-});
+]);
```
[Full Installation Guide โ](https://eslint-react.xyz/docs/getting-started/typescript)
-
-
## Presets
### Bare Bones
@@ -141,6 +142,8 @@ export default tseslint.config({
Disable rules in the `web-api` preset.
- `disable-type-checked`\
Disable rules that require type information.
+- `disable-conflict-eslint-plugin-react`\
+ Disable rules in `eslint-plugin-react` that conflict with rules in our plugins.
- `off`\
Disable all rules in this plugin except for debug rules.
@@ -150,6 +153,10 @@ export default tseslint.config({
[Rules Overview โ](https://eslint-react.xyz/docs/rules/overview)
+## Benchmark
+
+[Benchmark Results โ](https://github.com/Rel1cx/eslint-react-benchmark)
+
## FAQ
[Frequently Asked Questions โ](https://eslint-react.xyz/docs/faq)
@@ -162,8 +169,8 @@ export default tseslint.config({
Contributions are welcome!
-Please follow our [contributing guidelines](https://github.com/Rel1cx/eslint-react/tree/main/.github/CONTRIBUTING.md).
+Please follow our [contributing guidelines](https://github.com/Rel1cx/eslint-react/tree/2.0.0/.github/CONTRIBUTING.md).
## License
-This project is licensed under the MIT License - see the [LICENSE](https://github.com/Rel1cx/eslint-react/tree/main/LICENSE) file for details.
+This project is licensed under the MIT License - see the [LICENSE](https://github.com/Rel1cx/eslint-react/tree/2.0.0/LICENSE) file for details.
diff --git a/packages/plugins/eslint-plugin/package.json b/packages/plugins/eslint-plugin/package.json
index f2ac52a4a1..082d8c4f77 100644
--- a/packages/plugins/eslint-plugin/package.json
+++ b/packages/plugins/eslint-plugin/package.json
@@ -1,6 +1,6 @@
{
"name": "@eslint-react/eslint-plugin",
- "version": "1.53.2-beta.1",
+ "version": "2.0.0-beta.194",
"description": "A unified plugin that combines all individual plugins from the eslint-react monorepo into one.",
"keywords": [
"react",
@@ -10,7 +10,6 @@
"eslint-plugin-react-x",
"eslint-plugin-react-dom",
"eslint-plugin-react-web-api",
- "eslint-plugin-react-hooks-extra",
"eslint-plugin-react-naming-convention"
],
"homepage": "https://github.com/Rel1cx/eslint-react",
@@ -25,22 +24,14 @@
"license": "MIT",
"author": "Rel1cx",
"sideEffects": false,
+ "type": "module",
"exports": {
".": {
- "import": {
- "types": "./dist/index.d.mts",
- "default": "./dist/index.mjs"
- },
- "require": {
- "types": "./dist/index.d.ts",
- "default": "./dist/index.js"
- }
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.js"
},
"./package.json": "./package.json"
},
- "main": "dist/index.js",
- "module": "dist/index.mjs",
- "types": "dist/index.d.ts",
"files": [
"dist",
"./package.json"
@@ -55,10 +46,10 @@
"@eslint-react/eff": "workspace:*",
"@eslint-react/kit": "workspace:*",
"@eslint-react/shared": "workspace:*",
- "@typescript-eslint/scope-manager": "^8.44.0",
- "@typescript-eslint/type-utils": "^8.44.0",
- "@typescript-eslint/types": "^8.44.0",
- "@typescript-eslint/utils": "^8.44.0",
+ "@typescript-eslint/scope-manager": "^8.44.1",
+ "@typescript-eslint/type-utils": "^8.44.1",
+ "@typescript-eslint/types": "^8.44.1",
+ "@typescript-eslint/utils": "^8.44.1",
"eslint-plugin-react-debug": "workspace:*",
"eslint-plugin-react-dom": "workspace:*",
"eslint-plugin-react-hooks-extra": "workspace:*",
@@ -68,22 +59,14 @@
},
"devDependencies": {
"@local/configs": "workspace:*",
- "tsdown": "^0.15.3"
+ "tsdown": "^0.15.4"
},
"peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": "^4.9.5 || ^5.3.3"
- },
- "peerDependenciesMeta": {
- "eslint": {
- "optional": false
- },
- "typescript": {
- "optional": true
- }
+ "eslint": "^9.36.0",
+ "typescript": "^5.9.2"
},
"engines": {
- "node": ">=18.18.0"
+ "node": ">=20.19.0"
},
"publishConfig": {
"access": "public"
diff --git a/packages/plugins/eslint-plugin/src/configs/all.ts b/packages/plugins/eslint-plugin/src/configs/all.ts
index 0cf22df9b7..0d93870919 100644
--- a/packages/plugins/eslint-plugin/src/configs/all.ts
+++ b/packages/plugins/eslint-plugin/src/configs/all.ts
@@ -10,14 +10,16 @@ import * as x from "./x";
export const name = "@eslint-react/all";
export const rules = {
- "@eslint-react/avoid-shorthand-boolean": "warn",
- "@eslint-react/avoid-shorthand-fragment": "warn",
"@eslint-react/jsx-key-before-spread": "warn",
+ "@eslint-react/jsx-no-comment-textnodes": "warn",
"@eslint-react/jsx-no-duplicate-props": "warn",
"@eslint-react/jsx-no-iife": "warn",
"@eslint-react/jsx-no-undef": "error",
+ "@eslint-react/jsx-shorthand-boolean": "warn",
+ "@eslint-react/jsx-shorthand-fragment": "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",
"@eslint-react/no-children-count": "warn",
@@ -28,8 +30,6 @@ export const rules = {
"@eslint-react/no-children-to-array": "warn",
"@eslint-react/no-class-component": "warn",
"@eslint-react/no-clone-element": "warn",
- "@eslint-react/no-comment-textnodes": "warn",
- "@eslint-react/no-complex-conditional-rendering": "warn",
"@eslint-react/no-component-will-mount": "error",
"@eslint-react/no-component-will-receive-props": "error",
"@eslint-react/no-component-will-update": "error",
@@ -37,7 +37,7 @@ export const rules = {
"@eslint-react/no-create-ref": "error",
"@eslint-react/no-default-props": "error",
"@eslint-react/no-direct-mutation-state": "error",
- "@eslint-react/no-duplicate-key": "warn",
+ "@eslint-react/no-duplicate-key": "error",
"@eslint-react/no-forward-ref": "warn",
"@eslint-react/no-implicit-key": "warn",
// "@eslint-react/no-leaked-conditional-rendering": "warn",
@@ -46,28 +46,35 @@ export const rules = {
"@eslint-react/no-missing-key": "error",
"@eslint-react/no-misused-capture-owner-stack": "error",
"@eslint-react/no-nested-component-definitions": "error",
- "@eslint-react/no-nested-lazy-component-declarations": "warn",
+ "@eslint-react/no-nested-lazy-component-declarations": "error",
"@eslint-react/no-prop-types": "error",
"@eslint-react/no-redundant-should-component-update": "error",
"@eslint-react/no-set-state-in-component-did-mount": "warn",
"@eslint-react/no-set-state-in-component-did-update": "warn",
"@eslint-react/no-set-state-in-component-will-update": "warn",
"@eslint-react/no-string-refs": "error",
+ "@eslint-react/no-unnecessary-key": "warn",
+ "@eslint-react/no-unnecessary-use-callback": "warn",
+ "@eslint-react/no-unnecessary-use-memo": "warn",
+ "@eslint-react/no-unnecessary-use-prefix": "warn",
"@eslint-react/no-unsafe-component-will-mount": "warn",
"@eslint-react/no-unsafe-component-will-receive-props": "warn",
"@eslint-react/no-unsafe-component-will-update": "warn",
"@eslint-react/no-unstable-context-value": "warn",
"@eslint-react/no-unstable-default-props": "warn",
"@eslint-react/no-unused-class-component-members": "warn",
+ // "@eslint-react/no-unused-props": "warn",
"@eslint-react/no-unused-state": "warn",
"@eslint-react/no-use-context": "warn",
"@eslint-react/no-useless-forward-ref": "warn",
"@eslint-react/no-useless-fragment": "warn",
"@eslint-react/prefer-destructuring-assignment": "warn",
- "@eslint-react/prefer-react-namespace-import": "warn",
- // "@eslint-react/prefer-read-only-props": "warn",
- "@eslint-react/prefer-shorthand-boolean": "off",
- "@eslint-react/prefer-shorthand-fragment": "off",
+ "@eslint-react/prefer-namespace-import": "warn",
+ // "@eslint-react/prefer-read-only-props": "error",
+ "@eslint-react/prefer-use-state-lazy-initialization": "warn",
+
+ "@eslint-react/hooks-extra/no-direct-set-state-in-use-effect": "warn",
+ "@eslint-react/hooks-extra/no-direct-set-state-in-use-layout-effect": "warn",
"@eslint-react/dom/no-dangerously-set-innerhtml": "warn",
"@eslint-react/dom/no-dangerously-set-innerhtml-with-children": "error",
@@ -91,13 +98,6 @@ export const rules = {
"@eslint-react/web-api/no-leaked-resize-observer": "warn",
"@eslint-react/web-api/no-leaked-timeout": "warn",
- "@eslint-react/hooks-extra/no-direct-set-state-in-use-effect": "warn",
- "@eslint-react/hooks-extra/no-direct-set-state-in-use-layout-effect": "warn",
- "@eslint-react/hooks-extra/no-unnecessary-use-callback": "warn",
- "@eslint-react/hooks-extra/no-unnecessary-use-memo": "warn",
- "@eslint-react/hooks-extra/no-unnecessary-use-prefix": "warn",
- "@eslint-react/hooks-extra/prefer-use-state-lazy-initialization": "warn",
-
"@eslint-react/naming-convention/component-name": "warn",
"@eslint-react/naming-convention/context-name": "warn",
"@eslint-react/naming-convention/filename": "warn",
diff --git a/packages/plugins/eslint-plugin/src/configs/debug.ts b/packages/plugins/eslint-plugin/src/configs/debug.ts
index 5368db7f38..57d3b5daed 100644
--- a/packages/plugins/eslint-plugin/src/configs/debug.ts
+++ b/packages/plugins/eslint-plugin/src/configs/debug.ts
@@ -1,3 +1,5 @@
+import type { RuleConfig } from "@eslint-react/kit";
+
import reactDebug from "eslint-plugin-react-debug";
export const name = "@eslint-react/debug";
@@ -8,7 +10,7 @@ export const rules = {
"@eslint-react/debug/hook": "warn",
"@eslint-react/debug/is-from-react": "warn",
"@eslint-react/debug/jsx": "warn",
-} as const;
+} as const satisfies Record;
export const plugins = {
"@eslint-react/debug": reactDebug,
diff --git a/packages/plugins/eslint-plugin/src/configs/disable-conflict-eslint-plugin-react.ts b/packages/plugins/eslint-plugin/src/configs/disable-conflict-eslint-plugin-react.ts
new file mode 100644
index 0000000000..3ffa9b3c05
--- /dev/null
+++ b/packages/plugins/eslint-plugin/src/configs/disable-conflict-eslint-plugin-react.ts
@@ -0,0 +1,59 @@
+import type { RuleConfig } from "@eslint-react/kit";
+
+// This rulelist should be kept in sync with `apps/website/content/docs/migration.mdx`
+// Rules provided by `eslint-plugin-react` that exist in this plugin.
+// Rules provided by `@stylistic` are also omitted from this list.
+const conflictingRules = [
+ "button-has-type",
+ "destructuring-assignment",
+ "display-name",
+ "forbid-prop-types",
+ "forward-ref-uses-ref",
+ "hook-use-state",
+ "iframe-missing-sandbox",
+ "jsx-boolean-value",
+ "jsx-filename-extension",
+ "jsx-fragments",
+ "jsx-key",
+ "jsx-no-comment-textnodes",
+ "jsx-no-constructed-context-values",
+ "jsx-no-duplicate-props",
+ "jsx-no-leaked-render",
+ "jsx-no-script-url",
+ "jsx-no-target-blank",
+ "jsx-no-undef",
+ "jsx-no-useless-fragment",
+ "jsx-pascal-case",
+ "jsx-uses-react",
+ "jsx-uses-vars",
+ "no-access-state-in-setstate",
+ "no-array-index-key",
+ "no-children-prop",
+ "no-danger",
+ "no-danger-with-children",
+ "no-deprecated",
+ "no-did-mount-set-state",
+ "no-did-update-set-state",
+ "no-direct-mutation-state",
+ "no-find-dom-node",
+ "no-namespace",
+ "no-object-type-as-default-prop",
+ "no-redundant-should-component-update",
+ "no-render-return-value",
+ "no-string-refs",
+ "no-unknown-property",
+ "no-unsafe",
+ "no-unstable-nested-components",
+ "no-unused-class-component-members",
+ "no-unused-state",
+ "no-will-update-set-state",
+ "prefer-read-only-props",
+ "prop-types",
+ "void-dom-elements-no-children",
+];
+
+export const name = "@eslint-react/disable-conflict-eslint-plugin-react";
+
+export const rules: Record = Object.fromEntries(
+ conflictingRules.map((key) => [key, "off"] as const),
+);
diff --git a/packages/plugins/eslint-plugin/src/configs/disable-type-checked.ts b/packages/plugins/eslint-plugin/src/configs/disable-type-checked.ts
index d2272286bb..e65dad965f 100644
--- a/packages/plugins/eslint-plugin/src/configs/disable-type-checked.ts
+++ b/packages/plugins/eslint-plugin/src/configs/disable-type-checked.ts
@@ -4,5 +4,6 @@ export const name = "@eslint-react/disable-type-checked";
export const rules: Record = {
"@eslint-react/no-leaked-conditional-rendering": "off",
+ "@eslint-react/no-unused-props": "off",
"@eslint-react/prefer-read-only-props": "off",
};
diff --git a/packages/plugins/eslint-plugin/src/configs/recommended-type-checked.ts b/packages/plugins/eslint-plugin/src/configs/recommended-type-checked.ts
index b05d697585..02428ee022 100644
--- a/packages/plugins/eslint-plugin/src/configs/recommended-type-checked.ts
+++ b/packages/plugins/eslint-plugin/src/configs/recommended-type-checked.ts
@@ -7,6 +7,7 @@ export const name = "@eslint-react/recommended-type-checked";
export const rules = {
...recommendedTypeScript.rules,
"@eslint-react/no-leaked-conditional-rendering": "warn",
+ "@eslint-react/no-unused-props": "warn",
// "@eslint-react/prefer-read-only-props": "warn",
} as const satisfies Record;
diff --git a/packages/plugins/eslint-plugin/src/configs/recommended-typescript.ts b/packages/plugins/eslint-plugin/src/configs/recommended-typescript.ts
index 43460293da..ce441c91f1 100644
--- a/packages/plugins/eslint-plugin/src/configs/recommended-typescript.ts
+++ b/packages/plugins/eslint-plugin/src/configs/recommended-typescript.ts
@@ -6,6 +6,7 @@ export const name = "@eslint-react/recommended-typescript";
export const rules = {
...recommended.rules,
+ "@eslint-react/dom/no-string-style-prop": "off",
"@eslint-react/dom/no-unknown-property": "off",
"@eslint-react/jsx-no-duplicate-props": "off",
"@eslint-react/jsx-uses-react": "off",
diff --git a/packages/plugins/eslint-plugin/src/configs/recommended.ts b/packages/plugins/eslint-plugin/src/configs/recommended.ts
index cb1fa1ffd0..9b8964e471 100644
--- a/packages/plugins/eslint-plugin/src/configs/recommended.ts
+++ b/packages/plugins/eslint-plugin/src/configs/recommended.ts
@@ -14,10 +14,6 @@ export const rules = {
...dom.rules,
...webApi.rules,
- "@eslint-react/hooks-extra/no-direct-set-state-in-use-effect": "warn",
- "@eslint-react/hooks-extra/no-unnecessary-use-prefix": "warn",
- "@eslint-react/hooks-extra/prefer-use-state-lazy-initialization": "warn",
-
"@eslint-react/naming-convention/context-name": "warn",
// "@eslint-react/naming-convention/use-state": "warn",
} as const satisfies Record;
diff --git a/packages/plugins/eslint-plugin/src/configs/x.ts b/packages/plugins/eslint-plugin/src/configs/x.ts
index 86edcd39d2..acac7c85b4 100644
--- a/packages/plugins/eslint-plugin/src/configs/x.ts
+++ b/packages/plugins/eslint-plugin/src/configs/x.ts
@@ -5,19 +5,25 @@ import react from "eslint-plugin-react-x";
export const name = "@eslint-react/x";
export const rules = {
- "@eslint-react/jsx-key-before-spread": "warn",
+ "@eslint-react/jsx-no-comment-textnodes": "warn",
"@eslint-react/jsx-no-duplicate-props": "warn",
+ // "@eslint-react/jsx-no-iife": "warn",
+ // "@eslint-react/jsx-no-undef": "error",
+ // "@eslint-react/jsx-shorthand-boolean": "warn",
+ // "@eslint-react/jsx-shorthand-fragment": "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",
"@eslint-react/no-children-count": "warn",
"@eslint-react/no-children-for-each": "warn",
"@eslint-react/no-children-map": "warn",
"@eslint-react/no-children-only": "warn",
+ // "@eslint-react/no-children-prop": "warn",
"@eslint-react/no-children-to-array": "warn",
+ // "@eslint-react/no-class-component": "warn",
"@eslint-react/no-clone-element": "warn",
- "@eslint-react/no-comment-textnodes": "warn",
"@eslint-react/no-component-will-mount": "error",
"@eslint-react/no-component-will-receive-props": "error",
"@eslint-react/no-component-will-update": "error",
@@ -25,28 +31,40 @@ export const rules = {
"@eslint-react/no-create-ref": "error",
"@eslint-react/no-default-props": "error",
"@eslint-react/no-direct-mutation-state": "error",
- "@eslint-react/no-duplicate-key": "warn",
+ "@eslint-react/no-duplicate-key": "error",
"@eslint-react/no-forward-ref": "warn",
"@eslint-react/no-implicit-key": "warn",
+ // "@eslint-react/no-leaked-conditional-rendering": "warn",
+ // "@eslint-react/no-missing-component-display-name": "warn",
+ // "@eslint-react/no-missing-context-display-name": "warn",
"@eslint-react/no-missing-key": "error",
- "@eslint-react/no-misused-capture-owner-stack": "error",
+ // "@eslint-react/no-misused-capture-owner-stack": "error",
"@eslint-react/no-nested-component-definitions": "error",
- "@eslint-react/no-nested-lazy-component-declarations": "warn",
+ "@eslint-react/no-nested-lazy-component-declarations": "error",
"@eslint-react/no-prop-types": "error",
"@eslint-react/no-redundant-should-component-update": "error",
"@eslint-react/no-set-state-in-component-did-mount": "warn",
"@eslint-react/no-set-state-in-component-did-update": "warn",
"@eslint-react/no-set-state-in-component-will-update": "warn",
"@eslint-react/no-string-refs": "error",
+ // "@eslint-react/no-unnecessary-use-callback": "warn",
+ // "@eslint-react/no-unnecessary-use-memo": "warn",
+ "@eslint-react/no-unnecessary-use-prefix": "warn",
"@eslint-react/no-unsafe-component-will-mount": "warn",
"@eslint-react/no-unsafe-component-will-receive-props": "warn",
"@eslint-react/no-unsafe-component-will-update": "warn",
"@eslint-react/no-unstable-context-value": "warn",
"@eslint-react/no-unstable-default-props": "warn",
"@eslint-react/no-unused-class-component-members": "warn",
+ // "@eslint-react/no-unused-props": "warn",
"@eslint-react/no-unused-state": "warn",
"@eslint-react/no-use-context": "warn",
"@eslint-react/no-useless-forward-ref": "warn",
+ // "@eslint-react/no-useless-fragment": "warn",
+ // "@eslint-react/prefer-destructuring-assignment": "warn",
+ // "@eslint-react/prefer-namespace-import": "warn",
+ // "@eslint-react/prefer-read-only-props": "error",
+ "@eslint-react/prefer-use-state-lazy-initialization": "warn",
} as const satisfies Record;
export const plugins = {
diff --git a/packages/plugins/eslint-plugin/src/index.ts b/packages/plugins/eslint-plugin/src/index.ts
index 1e37e16465..79224d25aa 100644
--- a/packages/plugins/eslint-plugin/src/index.ts
+++ b/packages/plugins/eslint-plugin/src/index.ts
@@ -1,4 +1,4 @@
-import type { CompatibleConfig, CompatiblePlugin } from "@eslint-react/kit";
+import type { CompatiblePlugin } from "@eslint-react/kit";
import reactDebug from "eslint-plugin-react-debug";
import reactDom from "eslint-plugin-react-dom";
import reactHooksExtra from "eslint-plugin-react-hooks-extra";
@@ -9,6 +9,7 @@ import react from "eslint-plugin-react-x";
import { name, version } from "../package.json";
import * as allConfig from "./configs/all";
import * as debugConfig from "./configs/debug";
+import * as disableConflictEslintPluginReact from "./configs/disable-conflict-eslint-plugin-react";
import * as disableDebugConfig from "./configs/disable-debug";
import * as disableDomConfig from "./configs/disable-dom";
import * as disableTypeCheckedConfig from "./configs/disable-type-checked";
@@ -21,13 +22,6 @@ import * as recommendedTypeScriptConfig from "./configs/recommended-typescript";
import * as xConfig from "./configs/x";
import { padKeysLeft } from "./utils";
-function toLegacyConfig({ rules }: CompatibleConfig) {
- return {
- plugins: ["@eslint-react"],
- rules,
- };
-}
-
const plugin: CompatiblePlugin = {
meta: {
name,
@@ -47,38 +41,17 @@ export default {
...plugin,
configs: {
["all"]: allConfig,
- ["all-legacy"]: toLegacyConfig(allConfig),
["debug"]: debugConfig,
- ["debug-legacy"]: toLegacyConfig(debugConfig),
+ ["disable-conflict-eslint-plugin-react"]: disableConflictEslintPluginReact,
["disable-debug"]: disableDebugConfig,
- ["disable-debug-legacy"]: toLegacyConfig(disableDebugConfig),
["disable-dom"]: disableDomConfig,
- ["disable-dom-legacy"]: toLegacyConfig(disableDomConfig),
["disable-type-checked"]: disableTypeCheckedConfig,
- ["disable-type-checked-legacy"]: toLegacyConfig(disableTypeCheckedConfig),
["disable-web-api"]: disableWebApiConfig,
- ["disable-web-api-legacy"]: toLegacyConfig(disableWebApiConfig),
["dom"]: domConfig,
- ["dom-legacy"]: toLegacyConfig(domConfig),
["off"]: offConfig,
- ["off-legacy"]: toLegacyConfig(offConfig),
["recommended"]: recommendedConfig,
- ["recommended-legacy"]: toLegacyConfig(recommendedConfig),
["recommended-type-checked"]: recommendedTypeCheckedConfig,
- ["recommended-type-checked-legacy"]: toLegacyConfig(recommendedTypeCheckedConfig),
["recommended-typescript"]: recommendedTypeScriptConfig,
- ["recommended-typescript-legacy"]: toLegacyConfig(recommendedTypeScriptConfig),
["x"]: xConfig,
- ["x-legacy"]: toLegacyConfig(xConfig),
-
- // Part: deprecated presets
- /** @deprecated Use `x` instead */
- ["core"]: xConfig,
- /** @deprecated Use `x-legacy` instead */
- ["core-legacy"]: toLegacyConfig(xConfig),
- /** @deprecated Use `disable-dom` instead */
- ["off-dom"]: disableDomConfig,
- /** @deprecated Use `disable-dom-legacy` instead */
- ["off-dom-legacy"]: toLegacyConfig(disableDomConfig),
},
};
diff --git a/packages/plugins/eslint-plugin/tsdown.config.ts b/packages/plugins/eslint-plugin/tsdown.config.ts
index 49470fbf17..031761a959 100644
--- a/packages/plugins/eslint-plugin/tsdown.config.ts
+++ b/packages/plugins/eslint-plugin/tsdown.config.ts
@@ -5,11 +5,11 @@ export default {
dts: true,
entry: ["src/index.ts"],
external: ["eslint", "typescript"],
- format: ["cjs", "esm"],
+ format: ["esm"],
minify: false,
outDir: "dist",
platform: "node",
sourcemap: false,
- target: "node18",
+ target: "node20",
treeshake: true,
} satisfies Options;
diff --git a/packages/shared/docs/README.md b/packages/shared/docs/README.md
index cf3b6f0bcc..dc4b095d44 100644
--- a/packages/shared/docs/README.md
+++ b/packages/shared/docs/README.md
@@ -6,23 +6,15 @@
## Interfaces
-- [CustomComponentNormalized](interfaces/CustomComponentNormalized.md)
-- [CustomComponentPropNormalized](interfaces/CustomComponentPropNormalized.md)
- [ESLintReactSettingsNormalized](interfaces/ESLintReactSettingsNormalized.md)
## Type Aliases
-- [CustomComponent](type-aliases/CustomComponent.md)
-- [CustomComponentProp](type-aliases/CustomComponentProp.md)
-- [CustomHooks](type-aliases/CustomHooks.md)
- [ESLintReactSettings](type-aliases/ESLintReactSettings.md)
- [ESLintSettings](type-aliases/ESLintSettings.md)
## Variables
-- [CustomComponentPropSchema](variables/CustomComponentPropSchema.md)
-- [CustomComponentSchema](variables/CustomComponentSchema.md)
-- [CustomHooksSchema](variables/CustomHooksSchema.md)
- [DEFAULT\_ESLINT\_REACT\_SETTINGS](variables/DEFAULT_ESLINT_REACT_SETTINGS.md)
- [DEFAULT\_ESLINT\_SETTINGS](variables/DEFAULT_ESLINT_SETTINGS.md)
- [defineSettings](variables/defineSettings.md)
@@ -37,7 +29,6 @@
- [decodeESLintSettings](functions/decodeESLintSettings.md)
- [decodeSettings](functions/decodeSettings.md)
- [getConfigAdapters](functions/getConfigAdapters.md)
-- [getId](functions/getId.md)
- [getReactVersion](functions/getReactVersion.md)
- [getSettingsFromContext](functions/getSettingsFromContext.md)
- [isESLintReactSettings](functions/isESLintReactSettings.md)
diff --git a/packages/shared/docs/functions/coerceESLintSettings.md b/packages/shared/docs/functions/coerceESLintSettings.md
index 430bacf7c8..b695b3cf9e 100644
--- a/packages/shared/docs/functions/coerceESLintSettings.md
+++ b/packages/shared/docs/functions/coerceESLintSettings.md
@@ -8,12 +8,16 @@
> **coerceESLintSettings**(`settings`): `PartialDeep`\<`undefined` \| \{ `react-x?`: `unknown`; \}\>
+Coerces unknown input to ESLintSettings type
+
## Parameters
### settings
`unknown`
+The settings object to coerce
+
## Returns
`PartialDeep`\<`undefined` \| \{ `react-x?`: `unknown`; \}\>
diff --git a/packages/shared/docs/functions/coerceSettings.md b/packages/shared/docs/functions/coerceSettings.md
index 4848a95c89..3a363db8c2 100644
--- a/packages/shared/docs/functions/coerceSettings.md
+++ b/packages/shared/docs/functions/coerceSettings.md
@@ -8,199 +8,65 @@
> **coerceSettings**(`settings`): `object`
+Coerces unknown input to ESLintReactSettings type
+
## Parameters
### settings
`unknown`
-## Returns
-
-### additionalComponents?
-
-> `optional` **additionalComponents**: `object`[]
-
-An array of user-defined components
-
-#### Description
-
-This is used to inform the ESLint React plugins how to treat these components during checks.
-
-#### Example
-
-```ts
-`[{ name: "Link", as: "a", attributes: [{ name: "to", as: "href" }, { name: "rel", defaultValue: "noopener noreferrer" }] }]`
-```
-
-### additionalHooks?
-
-> `optional` **additionalHooks**: `object`
-
-A object to define additional hooks that are equivalent to the built-in React Hooks.
-
-#### Description
-
-ESLint React will recognize these aliases as equivalent to the built-in hooks in all its rules.
-
-#### Example
-
-```ts
-`{ useEffect: ["useIsomorphicLayoutEffect"] }`
-```
-
-#### additionalHooks.use?
-
-> `optional` **use**: `string`[]
-
-#### additionalHooks.useActionState?
-
-> `optional` **useActionState**: `string`[]
-
-#### additionalHooks.useCallback?
-
-> `optional` **useCallback**: `string`[]
-
-#### additionalHooks.useContext?
-
-> `optional` **useContext**: `string`[]
-
-#### additionalHooks.useDebugValue?
-
-> `optional` **useDebugValue**: `string`[]
-
-#### additionalHooks.useDeferredValue?
-
-> `optional` **useDeferredValue**: `string`[]
-
-#### additionalHooks.useEffect?
-
-> `optional` **useEffect**: `string`[]
-
-#### additionalHooks.useFormStatus?
-
-> `optional` **useFormStatus**: `string`[]
-
-#### additionalHooks.useId?
-
-> `optional` **useId**: `string`[]
-
-#### additionalHooks.useImperativeHandle?
-
-> `optional` **useImperativeHandle**: `string`[]
+The settings object to coerce
-#### additionalHooks.useInsertionEffect?
-
-> `optional` **useInsertionEffect**: `string`[]
-
-#### additionalHooks.useLayoutEffect?
-
-> `optional` **useLayoutEffect**: `string`[]
-
-#### additionalHooks.useMemo?
-
-> `optional` **useMemo**: `string`[]
-
-#### additionalHooks.useOptimistic?
-
-> `optional` **useOptimistic**: `string`[]
-
-#### additionalHooks.useReducer?
-
-> `optional` **useReducer**: `string`[]
-
-#### additionalHooks.useRef?
-
-> `optional` **useRef**: `string`[]
-
-#### additionalHooks.useState?
-
-> `optional` **useState**: `string`[]
-
-#### additionalHooks.useSyncExternalStore?
-
-> `optional` **useSyncExternalStore**: `string`[]
-
-#### additionalHooks.useTransition?
-
-> `optional` **useTransition**: `string`[]
+## Returns
### importSource?
> `optional` **importSource**: `string`
-The source where React is imported from.
-
-#### Description
-
-This allows to specify a custom import location for React when not using the official distribution.
+The source where React is imported from
+Allows specifying a custom import location for React
#### Default
-`"react"`
+```ts
+"react"
+```
#### Example
```ts
-`"@pika/react"`
+"@pika/react"
```
-### ~~jsxPragma?~~
-
-> `optional` **jsxPragma**: `string`
-
-The identifier that's used for JSX Element creation.
-
-#### Default
-
-`"createElement"`
-
-#### Deprecated
-
-### ~~jsxPragmaFrag?~~
-
-> `optional` **jsxPragmaFrag**: `string`
-
-The identifier that's used for JSX fragment elements.
-
-#### Description
-
-This should not be a member expression (i.e. use "Fragment" instead of "React.Fragment").
-
-#### Default
-
-`"Fragment"`
-
-#### Deprecated
-
### polymorphicPropName?
> `optional` **polymorphicPropName**: `string`
-The name of the prop that is used for polymorphic components.
-
-#### Description
-
-This is used to determine the type of the component.
+The prop name used for polymorphic components
+Used to determine the component's type
#### Example
```ts
-`"as"`
+"as"
```
### version?
> `optional` **version**: `string`
-React version to use, "detect" means auto detect React version from the project's dependencies.
-If `importSource` is specified, an equivalent version of React should be provided here.
+React version to use
+"detect" means auto-detect React version from project dependencies
#### Example
```ts
-`"18.3.1"`
+"18.3.1"
```
#### Default
-`"detect"`
+```ts
+"detect"
+```
diff --git a/packages/shared/docs/functions/decodeESLintSettings.md b/packages/shared/docs/functions/decodeESLintSettings.md
index 5fa0a29a73..917f788d01 100644
--- a/packages/shared/docs/functions/decodeESLintSettings.md
+++ b/packages/shared/docs/functions/decodeESLintSettings.md
@@ -8,12 +8,16 @@
> **decodeESLintSettings**(`settings`): `undefined` \| \{ `react-x?`: `unknown`; \}
+Decodes and validates ESLint settings, using defaults if invalid
+
## Parameters
### settings
`unknown`
+The settings object to decode
+
## Returns
`undefined` \| \{ `react-x?`: `unknown`; \}
diff --git a/packages/shared/docs/functions/decodeSettings.md b/packages/shared/docs/functions/decodeSettings.md
index 920b530a86..dbd603bcdd 100644
--- a/packages/shared/docs/functions/decodeSettings.md
+++ b/packages/shared/docs/functions/decodeSettings.md
@@ -8,199 +8,65 @@
> **decodeSettings**(`settings`): `object`
+Decodes and validates ESLint React settings, using defaults if invalid
+
## Parameters
### settings
`unknown`
-## Returns
-
-### additionalComponents?
-
-> `optional` **additionalComponents**: `object`[]
-
-An array of user-defined components
-
-#### Description
-
-This is used to inform the ESLint React plugins how to treat these components during checks.
-
-#### Example
-
-```ts
-`[{ name: "Link", as: "a", attributes: [{ name: "to", as: "href" }, { name: "rel", defaultValue: "noopener noreferrer" }] }]`
-```
-
-### additionalHooks?
-
-> `optional` **additionalHooks**: `object`
-
-A object to define additional hooks that are equivalent to the built-in React Hooks.
-
-#### Description
-
-ESLint React will recognize these aliases as equivalent to the built-in hooks in all its rules.
-
-#### Example
-
-```ts
-`{ useEffect: ["useIsomorphicLayoutEffect"] }`
-```
-
-#### additionalHooks.use?
-
-> `optional` **use**: `string`[]
-
-#### additionalHooks.useActionState?
-
-> `optional` **useActionState**: `string`[]
-
-#### additionalHooks.useCallback?
-
-> `optional` **useCallback**: `string`[]
-
-#### additionalHooks.useContext?
-
-> `optional` **useContext**: `string`[]
-
-#### additionalHooks.useDebugValue?
-
-> `optional` **useDebugValue**: `string`[]
-
-#### additionalHooks.useDeferredValue?
-
-> `optional` **useDeferredValue**: `string`[]
-
-#### additionalHooks.useEffect?
-
-> `optional` **useEffect**: `string`[]
-
-#### additionalHooks.useFormStatus?
-
-> `optional` **useFormStatus**: `string`[]
-
-#### additionalHooks.useId?
-
-> `optional` **useId**: `string`[]
-
-#### additionalHooks.useImperativeHandle?
-
-> `optional` **useImperativeHandle**: `string`[]
+The settings object to decode
-#### additionalHooks.useInsertionEffect?
-
-> `optional` **useInsertionEffect**: `string`[]
-
-#### additionalHooks.useLayoutEffect?
-
-> `optional` **useLayoutEffect**: `string`[]
-
-#### additionalHooks.useMemo?
-
-> `optional` **useMemo**: `string`[]
-
-#### additionalHooks.useOptimistic?
-
-> `optional` **useOptimistic**: `string`[]
-
-#### additionalHooks.useReducer?
-
-> `optional` **useReducer**: `string`[]
-
-#### additionalHooks.useRef?
-
-> `optional` **useRef**: `string`[]
-
-#### additionalHooks.useState?
-
-> `optional` **useState**: `string`[]
-
-#### additionalHooks.useSyncExternalStore?
-
-> `optional` **useSyncExternalStore**: `string`[]
-
-#### additionalHooks.useTransition?
-
-> `optional` **useTransition**: `string`[]
+## Returns
### importSource?
> `optional` **importSource**: `string`
-The source where React is imported from.
-
-#### Description
-
-This allows to specify a custom import location for React when not using the official distribution.
+The source where React is imported from
+Allows specifying a custom import location for React
#### Default
-`"react"`
+```ts
+"react"
+```
#### Example
```ts
-`"@pika/react"`
+"@pika/react"
```
-### ~~jsxPragma?~~
-
-> `optional` **jsxPragma**: `string`
-
-The identifier that's used for JSX Element creation.
-
-#### Default
-
-`"createElement"`
-
-#### Deprecated
-
-### ~~jsxPragmaFrag?~~
-
-> `optional` **jsxPragmaFrag**: `string`
-
-The identifier that's used for JSX fragment elements.
-
-#### Description
-
-This should not be a member expression (i.e. use "Fragment" instead of "React.Fragment").
-
-#### Default
-
-`"Fragment"`
-
-#### Deprecated
-
### polymorphicPropName?
> `optional` **polymorphicPropName**: `string`
-The name of the prop that is used for polymorphic components.
-
-#### Description
-
-This is used to determine the type of the component.
+The prop name used for polymorphic components
+Used to determine the component's type
#### Example
```ts
-`"as"`
+"as"
```
### version?
> `optional` **version**: `string`
-React version to use, "detect" means auto detect React version from the project's dependencies.
-If `importSource` is specified, an equivalent version of React should be provided here.
+React version to use
+"detect" means auto-detect React version from project dependencies
#### Example
```ts
-`"18.3.1"`
+"18.3.1"
```
#### Default
-`"detect"`
+```ts
+"detect"
+```
diff --git a/packages/shared/docs/functions/getId.md b/packages/shared/docs/functions/getId.md
deleted file mode 100644
index 79899a8e14..0000000000
--- a/packages/shared/docs/functions/getId.md
+++ /dev/null
@@ -1,13 +0,0 @@
-[**@eslint-react/shared**](../README.md)
-
-***
-
-[@eslint-react/shared](../README.md) / getId
-
-# Function: getId()
-
-> **getId**(): `string`
-
-## Returns
-
-`string`
diff --git a/packages/shared/docs/functions/getSettingsFromContext.md b/packages/shared/docs/functions/getSettingsFromContext.md
index 315fcb5e02..ab06e272d3 100644
--- a/packages/shared/docs/functions/getSettingsFromContext.md
+++ b/packages/shared/docs/functions/getSettingsFromContext.md
@@ -8,12 +8,17 @@
> **getSettingsFromContext**(`context`): [`ESLintReactSettingsNormalized`](../interfaces/ESLintReactSettingsNormalized.md)
+Retrieves normalized ESLint React settings from the rule context
+Uses caching for performance optimization
+
## Parameters
### context
`RuleContext`
+The ESLint rule context
+
## Returns
[`ESLintReactSettingsNormalized`](../interfaces/ESLintReactSettingsNormalized.md)
diff --git a/packages/shared/docs/functions/isESLintReactSettings.md b/packages/shared/docs/functions/isESLintReactSettings.md
index 81dd694533..a702523861 100644
--- a/packages/shared/docs/functions/isESLintReactSettings.md
+++ b/packages/shared/docs/functions/isESLintReactSettings.md
@@ -6,7 +6,9 @@
# Function: isESLintReactSettings()
-> **isESLintReactSettings**(`settings`): `settings is { additionalComponents?: { as?: string; attributes?: { as?: string; defaultValue?: string; name: string }[]; name: string }[]; additionalHooks?: { use?: string[]; useActionState?: string[]; useCallback?: string[]; useContext?: string[]; useDebugValue?: string[]; useDeferredValue?: string[]; useEffect?: string[]; useFormStatus?: string[]; useId?: string[]; useImperativeHandle?: string[]; useInsertionEffect?: string[]; useLayoutEffect?: string[]; useMemo?: string[]; useOptimistic?: string[]; useReducer?: string[]; useRef?: string[]; useState?: string[]; useSyncExternalStore?: string[]; useTransition?: string[] }; importSource?: string; jsxPragma?: string; jsxPragmaFrag?: string; polymorphicPropName?: string; version?: string }`
+> **isESLintReactSettings**(`settings`): `settings is { importSource?: string; polymorphicPropName?: string; version?: string }`
+
+Checks if the provided settings conform to ESLintReactSettings schema
## Parameters
@@ -14,6 +16,8 @@
`unknown`
+The settings object to validate
+
## Returns
-`settings is { additionalComponents?: { as?: string; attributes?: { as?: string; defaultValue?: string; name: string }[]; name: string }[]; additionalHooks?: { use?: string[]; useActionState?: string[]; useCallback?: string[]; useContext?: string[]; useDebugValue?: string[]; useDeferredValue?: string[]; useEffect?: string[]; useFormStatus?: string[]; useId?: string[]; useImperativeHandle?: string[]; useInsertionEffect?: string[]; useLayoutEffect?: string[]; useMemo?: string[]; useOptimistic?: string[]; useReducer?: string[]; useRef?: string[]; useState?: string[]; useSyncExternalStore?: string[]; useTransition?: string[] }; importSource?: string; jsxPragma?: string; jsxPragmaFrag?: string; polymorphicPropName?: string; version?: string }`
+`settings is { importSource?: string; polymorphicPropName?: string; version?: string }`
diff --git a/packages/shared/docs/functions/isESLintSettings.md b/packages/shared/docs/functions/isESLintSettings.md
index 95ab7943f2..254da4caaa 100644
--- a/packages/shared/docs/functions/isESLintSettings.md
+++ b/packages/shared/docs/functions/isESLintSettings.md
@@ -8,12 +8,16 @@
> **isESLintSettings**(`settings`): settings is undefined \| \{ react-x?: unknown \}
+Checks if the provided settings conform to ESLintSettings schema
+
## Parameters
### settings
`unknown`
+The settings object to validate
+
## Returns
settings is undefined \| \{ react-x?: unknown \}
diff --git a/packages/shared/docs/functions/normalizeSettings.md b/packages/shared/docs/functions/normalizeSettings.md
index 6ff6c510eb..0e8cb1f87b 100644
--- a/packages/shared/docs/functions/normalizeSettings.md
+++ b/packages/shared/docs/functions/normalizeSettings.md
@@ -8,329 +8,76 @@
> **normalizeSettings**(`__namedParameters`): `object`
+Normalizes ESLint React settings to a consistent internal format
+Transforms component definitions and resolves version information
+
## Parameters
### \_\_namedParameters
-#### additionalComponents?
-
-`object`[] = `[]`
-
-An array of user-defined components
-
-**Description**
-
-This is used to inform the ESLint React plugins how to treat these components during checks.
-
-**Example**
-
-```ts
-`[{ name: "Link", as: "a", attributes: [{ name: "to", as: "href" }, { name: "rel", defaultValue: "noopener noreferrer" }] }]`
-```
-
-#### additionalHooks?
-
-\{ `use?`: `string`[]; `useActionState?`: `string`[]; `useCallback?`: `string`[]; `useContext?`: `string`[]; `useDebugValue?`: `string`[]; `useDeferredValue?`: `string`[]; `useEffect?`: `string`[]; `useFormStatus?`: `string`[]; `useId?`: `string`[]; `useImperativeHandle?`: `string`[]; `useInsertionEffect?`: `string`[]; `useLayoutEffect?`: `string`[]; `useMemo?`: `string`[]; `useOptimistic?`: `string`[]; `useReducer?`: `string`[]; `useRef?`: `string`[]; `useState?`: `string`[]; `useSyncExternalStore?`: `string`[]; `useTransition?`: `string`[]; \} = `{}`
-
-A object to define additional hooks that are equivalent to the built-in React Hooks.
-
-**Description**
-
-ESLint React will recognize these aliases as equivalent to the built-in hooks in all its rules.
-
-**Example**
-
-```ts
-`{ useEffect: ["useIsomorphicLayoutEffect"] }`
-```
-
-#### additionalHooks.use?
-
-`string`[] = `...`
-
-#### additionalHooks.useActionState?
-
-`string`[] = `...`
-
-#### additionalHooks.useCallback?
-
-`string`[] = `...`
-
-#### additionalHooks.useContext?
-
-`string`[] = `...`
-
-#### additionalHooks.useDebugValue?
-
-`string`[] = `...`
-
-#### additionalHooks.useDeferredValue?
-
-`string`[] = `...`
-
-#### additionalHooks.useEffect?
-
-`string`[] = `...`
-
-#### additionalHooks.useFormStatus?
-
-`string`[] = `...`
-
-#### additionalHooks.useId?
-
-`string`[] = `...`
-
-#### additionalHooks.useImperativeHandle?
-
-`string`[] = `...`
-
-#### additionalHooks.useInsertionEffect?
-
-`string`[] = `...`
-
-#### additionalHooks.useLayoutEffect?
-
-`string`[] = `...`
-
-#### additionalHooks.useMemo?
-
-`string`[] = `...`
-
-#### additionalHooks.useOptimistic?
-
-`string`[] = `...`
-
-#### additionalHooks.useReducer?
-
-`string`[] = `...`
-
-#### additionalHooks.useRef?
-
-`string`[] = `...`
-
-#### additionalHooks.useState?
-
-`string`[] = `...`
-
-#### additionalHooks.useSyncExternalStore?
-
-`string`[] = `...`
-
-#### additionalHooks.useTransition?
-
-`string`[] = `...`
-
#### importSource?
`string` = `"react"`
-The source where React is imported from.
-
-**Description**
-
-This allows to specify a custom import location for React when not using the official distribution.
+The source where React is imported from
+Allows specifying a custom import location for React
**Default**
-`"react"`
+```ts
+"react"
+```
**Example**
```ts
-`"@pika/react"`
+"@pika/react"
```
-#### jsxPragma?
-
-`string` = `...`
-
-The identifier that's used for JSX Element creation.
-
-**Default**
-
-`"createElement"`
-
-**Deprecated**
-
-#### jsxPragmaFrag?
-
-`string` = `...`
-
-The identifier that's used for JSX fragment elements.
-
-**Description**
-
-This should not be a member expression (i.e. use "Fragment" instead of "React.Fragment").
-
-**Default**
-
-`"Fragment"`
-
-**Deprecated**
-
#### polymorphicPropName?
`string` = `"as"`
-The name of the prop that is used for polymorphic components.
-
-**Description**
-
-This is used to determine the type of the component.
+The prop name used for polymorphic components
+Used to determine the component's type
**Example**
```ts
-`"as"`
+"as"
```
#### version?
`string`
-React version to use, "detect" means auto detect React version from the project's dependencies.
-If `importSource` is specified, an equivalent version of React should be provided here.
+React version to use
+"detect" means auto-detect React version from project dependencies
**Example**
```ts
-`"18.3.1"`
+"18.3.1"
```
**Default**
-`"detect"`
+```ts
+"detect"
+```
## Returns
-### additionalHooks
-
-> **additionalHooks**: `object`
-
-#### additionalHooks.use?
-
-> `optional` **use**: `string`[]
-
-#### additionalHooks.useActionState?
-
-> `optional` **useActionState**: `string`[]
-
-#### additionalHooks.useCallback?
-
-> `optional` **useCallback**: `string`[]
-
-#### additionalHooks.useContext?
-
-> `optional` **useContext**: `string`[]
-
-#### additionalHooks.useDebugValue?
-
-> `optional` **useDebugValue**: `string`[]
-
-#### additionalHooks.useDeferredValue?
-
-> `optional` **useDeferredValue**: `string`[]
-
-#### additionalHooks.useEffect?
-
-> `optional` **useEffect**: `string`[]
-
-#### additionalHooks.useFormStatus?
-
-> `optional` **useFormStatus**: `string`[]
-
-#### additionalHooks.useId?
-
-> `optional` **useId**: `string`[]
-
-#### additionalHooks.useImperativeHandle?
-
-> `optional` **useImperativeHandle**: `string`[]
-
-#### additionalHooks.useInsertionEffect?
-
-> `optional` **useInsertionEffect**: `string`[]
-
-#### additionalHooks.useLayoutEffect?
-
-> `optional` **useLayoutEffect**: `string`[]
-
-#### additionalHooks.useMemo?
-
-> `optional` **useMemo**: `string`[]
-
-#### additionalHooks.useOptimistic?
-
-> `optional` **useOptimistic**: `string`[]
-
-#### additionalHooks.useReducer?
-
-> `optional` **useReducer**: `string`[]
-
-#### additionalHooks.useRef?
-
-> `optional` **useRef**: `string`[]
-
-#### additionalHooks.useState?
-
-> `optional` **useState**: `string`[]
-
-#### additionalHooks.useSyncExternalStore?
-
-> `optional` **useSyncExternalStore**: `string`[]
-
-#### additionalHooks.useTransition?
-
-> `optional` **useTransition**: `string`[]
-
-### components
-
-> `readonly` **components**: `object`[]
+`object`
### importSource
> **importSource**: `string`
-### ~~jsxPragma?~~
-
-> `readonly` `optional` **jsxPragma**: `string`
-
-The identifier that's used for JSX Element creation.
-
-#### Default
-
-`"createElement"`
-
-#### Deprecated
-
-### ~~jsxPragmaFrag?~~
-
-> `readonly` `optional` **jsxPragmaFrag**: `string`
-
-The identifier that's used for JSX fragment elements.
-
-#### Description
-
-This should not be a member expression (i.e. use "Fragment" instead of "React.Fragment").
-
-#### Default
-
-`"Fragment"`
-
-#### Deprecated
-
### polymorphicPropName
> **polymorphicPropName**: `string`
-### skipImportCheck
-
-> **skipImportCheck**: `boolean`
-
-### strict
-
-> **strict**: `boolean`
-
### version
> `readonly` **version**: `string`
diff --git a/packages/shared/docs/interfaces/CustomComponentNormalized.md b/packages/shared/docs/interfaces/CustomComponentNormalized.md
deleted file mode 100644
index 30dc026e61..0000000000
--- a/packages/shared/docs/interfaces/CustomComponentNormalized.md
+++ /dev/null
@@ -1,45 +0,0 @@
-[**@eslint-react/shared**](../README.md)
-
-***
-
-[@eslint-react/shared](../README.md) / CustomComponentNormalized
-
-# Interface: CustomComponentNormalized
-
-## Properties
-
-### as
-
-> **as**: `string`
-
-***
-
-### attributes
-
-> **attributes**: [`CustomComponentPropNormalized`](CustomComponentPropNormalized.md)[]
-
-***
-
-### name
-
-> **name**: `string`
-
-***
-
-### re
-
-> **re**: `object`
-
-#### test()
-
-> **test**(`s`): `boolean`
-
-##### Parameters
-
-###### s
-
-`string`
-
-##### Returns
-
-`boolean`
diff --git a/packages/shared/docs/interfaces/CustomComponentPropNormalized.md b/packages/shared/docs/interfaces/CustomComponentPropNormalized.md
deleted file mode 100644
index f86f3eab54..0000000000
--- a/packages/shared/docs/interfaces/CustomComponentPropNormalized.md
+++ /dev/null
@@ -1,25 +0,0 @@
-[**@eslint-react/shared**](../README.md)
-
-***
-
-[@eslint-react/shared](../README.md) / CustomComponentPropNormalized
-
-# Interface: CustomComponentPropNormalized
-
-## Properties
-
-### as
-
-> **as**: `string`
-
-***
-
-### defaultValue?
-
-> `optional` **defaultValue**: `string`
-
-***
-
-### name
-
-> **name**: `string`
diff --git a/packages/shared/docs/interfaces/ESLintReactSettingsNormalized.md b/packages/shared/docs/interfaces/ESLintReactSettingsNormalized.md
index 3afb07eefc..8b756beba9 100644
--- a/packages/shared/docs/interfaces/ESLintReactSettingsNormalized.md
+++ b/packages/shared/docs/interfaces/ESLintReactSettingsNormalized.md
@@ -6,95 +6,9 @@
# Interface: ESLintReactSettingsNormalized
-## Properties
-
-### additionalHooks
-
-> **additionalHooks**: `object`
-
-#### use?
-
-> `optional` **use**: `string`[]
-
-#### useActionState?
-
-> `optional` **useActionState**: `string`[]
-
-#### useCallback?
-
-> `optional` **useCallback**: `string`[]
-
-#### useContext?
-
-> `optional` **useContext**: `string`[]
-
-#### useDebugValue?
-
-> `optional` **useDebugValue**: `string`[]
-
-#### useDeferredValue?
-
-> `optional` **useDeferredValue**: `string`[]
-
-#### useEffect?
-
-> `optional` **useEffect**: `string`[]
-
-#### useFormStatus?
-
-> `optional` **useFormStatus**: `string`[]
-
-#### useId?
-
-> `optional` **useId**: `string`[]
-
-#### useImperativeHandle?
-
-> `optional` **useImperativeHandle**: `string`[]
-
-#### useInsertionEffect?
-
-> `optional` **useInsertionEffect**: `string`[]
-
-#### useLayoutEffect?
-
-> `optional` **useLayoutEffect**: `string`[]
-
-#### useMemo?
+Normalized ESLint React settings with processed values
-> `optional` **useMemo**: `string`[]
-
-#### useOptimistic?
-
-> `optional` **useOptimistic**: `string`[]
-
-#### useReducer?
-
-> `optional` **useReducer**: `string`[]
-
-#### useRef?
-
-> `optional` **useRef**: `string`[]
-
-#### useState?
-
-> `optional` **useState**: `string`[]
-
-#### useSyncExternalStore?
-
-> `optional` **useSyncExternalStore**: `string`[]
-
-#### useTransition?
-
-> `optional` **useTransition**: `string`[]
-
-***
-
-### components
-
-> **components**: [`CustomComponentNormalized`](CustomComponentNormalized.md)[]
-
-***
+## Properties
### importSource
@@ -108,18 +22,6 @@
***
-### skipImportCheck
-
-> **skipImportCheck**: `boolean`
-
-***
-
-### strict
-
-> **strict**: `boolean`
-
-***
-
### version
> **version**: `string`
diff --git a/packages/shared/docs/type-aliases/CustomComponent.md b/packages/shared/docs/type-aliases/CustomComponent.md
deleted file mode 100644
index 8cf7529301..0000000000
--- a/packages/shared/docs/type-aliases/CustomComponent.md
+++ /dev/null
@@ -1,9 +0,0 @@
-[**@eslint-react/shared**](../README.md)
-
-***
-
-[@eslint-react/shared](../README.md) / CustomComponent
-
-# Type Alias: CustomComponent
-
-> **CustomComponent** = `z.infer`\<*typeof* [`CustomComponentSchema`](../variables/CustomComponentSchema.md)\>
diff --git a/packages/shared/docs/type-aliases/CustomComponentProp.md b/packages/shared/docs/type-aliases/CustomComponentProp.md
deleted file mode 100644
index 5fca3dfc58..0000000000
--- a/packages/shared/docs/type-aliases/CustomComponentProp.md
+++ /dev/null
@@ -1,9 +0,0 @@
-[**@eslint-react/shared**](../README.md)
-
-***
-
-[@eslint-react/shared](../README.md) / CustomComponentProp
-
-# Type Alias: CustomComponentProp
-
-> **CustomComponentProp** = `z.infer`\<*typeof* [`CustomComponentPropSchema`](../variables/CustomComponentPropSchema.md)\>
diff --git a/packages/shared/docs/type-aliases/CustomHooks.md b/packages/shared/docs/type-aliases/CustomHooks.md
deleted file mode 100644
index 5f3fd253df..0000000000
--- a/packages/shared/docs/type-aliases/CustomHooks.md
+++ /dev/null
@@ -1,9 +0,0 @@
-[**@eslint-react/shared**](../README.md)
-
-***
-
-[@eslint-react/shared](../README.md) / CustomHooks
-
-# Type Alias: CustomHooks
-
-> **CustomHooks** = `z.infer`\<*typeof* [`CustomHooksSchema`](../variables/CustomHooksSchema.md)\>
diff --git a/packages/shared/docs/variables/CustomComponentPropSchema.md b/packages/shared/docs/variables/CustomComponentPropSchema.md
deleted file mode 100644
index cb63259515..0000000000
--- a/packages/shared/docs/variables/CustomComponentPropSchema.md
+++ /dev/null
@@ -1,9 +0,0 @@
-[**@eslint-react/shared**](../README.md)
-
-***
-
-[@eslint-react/shared](../README.md) / CustomComponentPropSchema
-
-# Variable: CustomComponentPropSchema
-
-> `const` **CustomComponentPropSchema**: `ZodObject`\<\{ `as`: `ZodOptional`\<`ZodString`\>; `defaultValue`: `ZodOptional`\<`ZodString`\>; `name`: `ZodString`; \}, `$strip`\>
diff --git a/packages/shared/docs/variables/CustomComponentSchema.md b/packages/shared/docs/variables/CustomComponentSchema.md
deleted file mode 100644
index a7973b7dbf..0000000000
--- a/packages/shared/docs/variables/CustomComponentSchema.md
+++ /dev/null
@@ -1,15 +0,0 @@
-[**@eslint-react/shared**](../README.md)
-
-***
-
-[@eslint-react/shared](../README.md) / CustomComponentSchema
-
-# Variable: CustomComponentSchema
-
-> `const` **CustomComponentSchema**: `ZodObject`\<\{ `as`: `ZodOptional`\<`ZodString`\>; `attributes`: `ZodOptional`\<`ZodArray`\<`ZodObject`\<\{ `as`: `ZodOptional`\<`ZodString`\>; `defaultValue`: `ZodOptional`\<`ZodString`\>; `name`: `ZodString`; \}, `$strip`\>\>\>; `name`: `ZodString`; \}, `$strip`\>
-
-## Description
-
-This will provide some key information to the rule before checking for user-defined components.
-For example:
-Which prop is used as the `href` prop for the user-defined `Link` component that represents the built-in `a` element.
diff --git a/packages/shared/docs/variables/CustomHooksSchema.md b/packages/shared/docs/variables/CustomHooksSchema.md
deleted file mode 100644
index 77694fca5f..0000000000
--- a/packages/shared/docs/variables/CustomHooksSchema.md
+++ /dev/null
@@ -1,9 +0,0 @@
-[**@eslint-react/shared**](../README.md)
-
-***
-
-[@eslint-react/shared](../README.md) / CustomHooksSchema
-
-# Variable: CustomHooksSchema
-
-> `const` **CustomHooksSchema**: `ZodObject`\<\{ `use`: `ZodOptional`\<`ZodArray`\<`ZodString`\>\>; `useActionState`: `ZodOptional`\<`ZodArray`\<`ZodString`\>\>; `useCallback`: `ZodOptional`\<`ZodArray`\<`ZodString`\>\>; `useContext`: `ZodOptional`\<`ZodArray`\<`ZodString`\>\>; `useDebugValue`: `ZodOptional`\<`ZodArray`\<`ZodString`\>\>; `useDeferredValue`: `ZodOptional`\<`ZodArray`\<`ZodString`\>\>; `useEffect`: `ZodOptional`\<`ZodArray`\<`ZodString`\>\>; `useFormStatus`: `ZodOptional`\<`ZodArray`\<`ZodString`\>\>; `useId`: `ZodOptional`\<`ZodArray`\<`ZodString`\>\>; `useImperativeHandle`: `ZodOptional`\<`ZodArray`\<`ZodString`\>\>; `useInsertionEffect`: `ZodOptional`\<`ZodArray`\<`ZodString`\>\>; `useLayoutEffect`: `ZodOptional`\<`ZodArray`\<`ZodString`\>\>; `useMemo`: `ZodOptional`\<`ZodArray`\<`ZodString`\>\>; `useOptimistic`: `ZodOptional`\<`ZodArray`\<`ZodString`\>\>; `useReducer`: `ZodOptional`\<`ZodArray`\<`ZodString`\>\>; `useRef`: `ZodOptional`\<`ZodArray`\<`ZodString`\>\>; `useState`: `ZodOptional`\<`ZodArray`\<`ZodString`\>\>; `useSyncExternalStore`: `ZodOptional`\<`ZodArray`\<`ZodString`\>\>; `useTransition`: `ZodOptional`\<`ZodArray`\<`ZodString`\>\>; \}, `$strip`\>
diff --git a/packages/shared/docs/variables/DEFAULT_ESLINT_REACT_SETTINGS.md b/packages/shared/docs/variables/DEFAULT_ESLINT_REACT_SETTINGS.md
index 31aac91eb9..fcc2d063e8 100644
--- a/packages/shared/docs/variables/DEFAULT_ESLINT_REACT_SETTINGS.md
+++ b/packages/shared/docs/variables/DEFAULT_ESLINT_REACT_SETTINGS.md
@@ -8,26 +8,10 @@
> `const` **DEFAULT\_ESLINT\_REACT\_SETTINGS**: `object`
-The default ESLint settings for "react-x".
+Default ESLint React settings
## Type Declaration
-### additionalComponents
-
-> `readonly` **additionalComponents**: \[\] = `[]`
-
-### additionalHooks
-
-> `readonly` **additionalHooks**: `object`
-
-#### additionalHooks.useEffect
-
-> `readonly` **useEffect**: \[`"useIsomorphicLayoutEffect"`\]
-
-#### additionalHooks.useLayoutEffect
-
-> `readonly` **useLayoutEffect**: \[`"useIsomorphicLayoutEffect"`\]
-
### importSource
> `readonly` **importSource**: `"react"` = `"react"`
@@ -36,14 +20,6 @@ The default ESLint settings for "react-x".
> `readonly` **polymorphicPropName**: `"as"` = `"as"`
-### skipImportCheck
-
-> `readonly` **skipImportCheck**: `true` = `true`
-
-### strict
-
-> `readonly` **strict**: `true` = `true`
-
### version
> `readonly` **version**: `"detect"` = `"detect"`
diff --git a/packages/shared/docs/variables/DEFAULT_ESLINT_SETTINGS.md b/packages/shared/docs/variables/DEFAULT_ESLINT_SETTINGS.md
index 4020c9a2e4..54e632e132 100644
--- a/packages/shared/docs/variables/DEFAULT_ESLINT_SETTINGS.md
+++ b/packages/shared/docs/variables/DEFAULT_ESLINT_SETTINGS.md
@@ -8,28 +8,14 @@
> `const` **DEFAULT\_ESLINT\_SETTINGS**: `object`
+Default ESLint settings with React settings included
+
## Type Declaration
### react-x
> `readonly` **react-x**: `object` = `DEFAULT_ESLINT_REACT_SETTINGS`
-#### react-x.additionalComponents
-
-> `readonly` **additionalComponents**: \[\] = `[]`
-
-#### react-x.additionalHooks
-
-> `readonly` **additionalHooks**: `object`
-
-#### react-x.additionalHooks.useEffect
-
-> `readonly` **useEffect**: \[`"useIsomorphicLayoutEffect"`\]
-
-#### react-x.additionalHooks.useLayoutEffect
-
-> `readonly` **useLayoutEffect**: \[`"useIsomorphicLayoutEffect"`\]
-
#### react-x.importSource
> `readonly` **importSource**: `"react"` = `"react"`
@@ -38,14 +24,6 @@
> `readonly` **polymorphicPropName**: `"as"` = `"as"`
-#### react-x.skipImportCheck
-
-> `readonly` **skipImportCheck**: `true` = `true`
-
-#### react-x.strict
-
-> `readonly` **strict**: `true` = `true`
-
#### react-x.version
> `readonly` **version**: `"detect"` = `"detect"`
diff --git a/packages/shared/docs/variables/defineSettings.md b/packages/shared/docs/variables/defineSettings.md
index 15eb84a730..d578737967 100644
--- a/packages/shared/docs/variables/defineSettings.md
+++ b/packages/shared/docs/variables/defineSettings.md
@@ -8,7 +8,8 @@
> `const` **defineSettings**: (`settings`) => [`ESLintReactSettings`](../type-aliases/ESLintReactSettings.md) = `identity`
-A helper function to define settings for "react-x" with type checking in JavaScript files.
+Helper function for defining typed settings for "react-x" in JavaScript files
+Provides type checking without runtime transformation
## Parameters
@@ -16,10 +17,6 @@ A helper function to define settings for "react-x" with type checking in JavaScr
[`ESLintReactSettings`](../type-aliases/ESLintReactSettings.md)
-The settings.
-
## Returns
[`ESLintReactSettings`](../type-aliases/ESLintReactSettings.md)
-
-The settings.
diff --git a/packages/shared/package.json b/packages/shared/package.json
index 06c7fb18de..c3dfbe0ae4 100644
--- a/packages/shared/package.json
+++ b/packages/shared/package.json
@@ -1,6 +1,6 @@
{
"name": "@eslint-react/shared",
- "version": "1.53.2-beta.1",
+ "version": "2.0.0-beta.194",
"description": "ESLint React's Shared constants and functions.",
"homepage": "https://github.com/Rel1cx/eslint-react",
"bugs": {
@@ -14,22 +14,14 @@
"license": "MIT",
"author": "Rel1cx",
"sideEffects": false,
+ "type": "module",
"exports": {
".": {
- "import": {
- "types": "./dist/index.d.mts",
- "default": "./dist/index.mjs"
- },
- "require": {
- "types": "./dist/index.d.ts",
- "default": "./dist/index.js"
- }
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.js"
},
"./package.json": "./package.json"
},
- "main": "dist/index.js",
- "module": "dist/index.mjs",
- "types": "dist/index.d.ts",
"files": [
"dist",
"./package.json"
@@ -44,18 +36,18 @@
"dependencies": {
"@eslint-react/eff": "workspace:*",
"@eslint-react/kit": "workspace:*",
- "@typescript-eslint/utils": "^8.44.0",
+ "@typescript-eslint/utils": "^8.44.1",
"ts-pattern": "^5.8.0",
- "zod": "^4.1.9"
+ "zod": "^4.1.11"
},
"devDependencies": {
"@local/configs": "workspace:*",
"@tsconfig/node22": "^22.0.2",
"@types/picomatch": "^4.0.2",
- "tsdown": "^0.15.3",
+ "tsdown": "^0.15.4",
"type-fest": "^5.0.1"
},
"engines": {
- "node": ">=18.18.0"
+ "node": ">=20.19.0"
}
}
diff --git a/packages/shared/src/get-id.ts b/packages/shared/src/_id.ts
similarity index 74%
rename from packages/shared/src/get-id.ts
rename to packages/shared/src/_id.ts
index fbb380af66..03cbd699ad 100644
--- a/packages/shared/src/get-id.ts
+++ b/packages/shared/src/_id.ts
@@ -1,3 +1,6 @@
let id = 0n;
+/**
+ * @internal
+ */
export const getId = () => (id++).toString();
diff --git a/packages/shared/src/_require.ts b/packages/shared/src/_require.ts
new file mode 100644
index 0000000000..393ccd46ba
--- /dev/null
+++ b/packages/shared/src/_require.ts
@@ -0,0 +1,8 @@
+import module from "node:module";
+
+import path from "node:path";
+
+/**
+ * @internal
+ */
+export const _require = module.createRequire(process.cwd() + path.sep);
diff --git a/packages/shared/src/get-react-version.ts b/packages/shared/src/get-react-version.ts
index 30025038b4..c7f6520ca5 100644
--- a/packages/shared/src/get-react-version.ts
+++ b/packages/shared/src/get-react-version.ts
@@ -1,9 +1,6 @@
-import module from "node:module";
-
import { identity } from "@eslint-react/eff";
-import { match, P } from "ts-pattern";
-
-const _require = module.createRequire(import.meta.url);
+import { P, match } from "ts-pattern";
+import { _require } from "./_require";
export function getReactVersion(fallback: string): string {
try {
diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts
index 4ab3a3a0a3..abfeefd3a2 100644
--- a/packages/shared/src/index.ts
+++ b/packages/shared/src/index.ts
@@ -1,6 +1,7 @@
+export * from "./_id";
+export * from "./_require";
export * from "./constants";
export * from "./get-config-adapters";
export * from "./get-doc-url";
-export * from "./get-id";
export * from "./get-react-version";
export * from "./settings";
diff --git a/packages/shared/src/settings.ts b/packages/shared/src/settings.ts
index 2b555ab52d..2c7ae6340a 100644
--- a/packages/shared/src/settings.ts
+++ b/packages/shared/src/settings.ts
@@ -1,162 +1,49 @@
+/* eslint-disable jsdoc/require-param */
/* eslint-disable perfectionist/sort-objects */
import type { unit } from "@eslint-react/eff";
import { getOrElseUpdate, identity } from "@eslint-react/eff";
-import { RegExp as RE, type RuleContext } from "@eslint-react/kit";
+import { type RuleContext } from "@eslint-react/kit";
import type { ESLint, SharedConfigurationSettings } from "@typescript-eslint/utils/ts-eslint"; // eslint-disable-line @typescript-eslint/no-unused-vars
import type { PartialDeep } from "type-fest";
-import { match, P } from "ts-pattern";
+import { P, match } from "ts-pattern";
import { z } from "zod/v4";
import { getReactVersion } from "./get-react-version";
-export const CustomComponentPropSchema = z.object({
- /**
- * The name of the prop in the user-defined component.
- * @example
- * "to"
- */
- name: z.string(),
- /**
- * The name of the prop in the host component.
- * @example
- * "href"
- */
- as: z.optional(z.string()),
- /**
- * Whether the prop is controlled or not in the user-defined component.
- * @internal
- * @example
- * `true`
- */
- controlled: z.optional(z.boolean()),
- /**
- * The default value of the prop in the user-defined component.
- * @example
- * `"/"`
- */
- defaultValue: z.optional(z.string()),
-});
-
-/**
- * @description
- * This will provide some key information to the rule before checking for user-defined components.
- * For example:
- * Which prop is used as the `href` prop for the user-defined `Link` component that represents the built-in `a` element.
- */
-export const CustomComponentSchema = z.object({
- /**
- * The name of the user-defined component.
- * @example
- * "Link"
- */
- name: z.string(),
- /**
- * The name of the host component that the user-defined component represents.
- * @example
- * "a"
- */
- as: z.optional(z.string()),
- /**
- * Attributes mapping between the user-defined component and the host component.
- * @example
- * `Link` component has a `to` attribute that represents the `href` attribute in the built-in `a` element with a default value of `"/"`.
- */
- attributes: z.optional(z.array(CustomComponentPropSchema)),
- /**
- * The ESQuery selector to select the component precisely.
- * @internal
- * @example
- * `JSXElement:has(JSXAttribute[name.name='component'][value.value='a'])`
- */
- selector: z.optional(z.string()),
-});
-
-export const CustomHooksSchema = z.object({
- use: z.optional(z.array(z.string())),
- useActionState: z.optional(z.array(z.string())),
- useCallback: z.optional(z.array(z.string())),
- useContext: z.optional(z.array(z.string())),
- useDebugValue: z.optional(z.array(z.string())),
- useDeferredValue: z.optional(z.array(z.string())),
- useEffect: z.optional(z.array(z.string())),
- useFormStatus: z.optional(z.array(z.string())),
- useId: z.optional(z.array(z.string())),
- useImperativeHandle: z.optional(z.array(z.string())),
- useInsertionEffect: z.optional(z.array(z.string())),
- useLayoutEffect: z.optional(z.array(z.string())),
- useMemo: z.optional(z.array(z.string())),
- useOptimistic: z.optional(z.array(z.string())),
- useReducer: z.optional(z.array(z.string())),
- useRef: z.optional(z.array(z.string())),
- useState: z.optional(z.array(z.string())),
- useSyncExternalStore: z.optional(z.array(z.string())),
- useTransition: z.optional(z.array(z.string())),
-});
+// ===== Schema Definitions =====
/**
+ * Schema for ESLint React settings configuration
* @internal
*/
export const ESLintReactSettingsSchema = z.object({
/**
- * The source where React is imported from.
- * @description This allows to specify a custom import location for React when not using the official distribution.
- * @default `"react"`
- * @example `"@pika/react"`
+ * The source where React is imported from
+ * Allows specifying a custom import location for React
+ * @default "react"
+ * @example "@pika/react"
*/
importSource: z.optional(z.string()),
+
/**
- * The identifier that's used for JSX Element creation.
- * @default `"createElement"`
- * @deprecated
- */
- jsxPragma: z.optional(z.string()),
- /**
- * The identifier that's used for JSX fragment elements.
- * @description This should not be a member expression (i.e. use "Fragment" instead of "React.Fragment").
- * @default `"Fragment"`
- * @deprecated
- */
- jsxPragmaFrag: z.optional(z.string()),
- /**
- * The name of the prop that is used for polymorphic components.
- * @description This is used to determine the type of the component.
- * @example `"as"`
+ * The prop name used for polymorphic components
+ * Used to determine the component's type
+ * @example "as"
*/
polymorphicPropName: z.optional(z.string()),
+
/**
- * @default `true`
- * @internal
- */
- strict: z.optional(z.boolean()),
- /**
- * Check both the shape and the import to determine if an API is from React.
- * @default `true`
- * @internal
- */
- skipImportCheck: z.optional(z.boolean()),
- /**
- * React version to use, "detect" means auto detect React version from the project's dependencies.
- * If `importSource` is specified, an equivalent version of React should be provided here.
- * @example `"18.3.1"`
- * @default `"detect"`
+ * React version to use
+ * "detect" means auto-detect React version from project dependencies
+ * @example "18.3.1"
+ * @default "detect"
*/
version: z.optional(z.string()),
- /**
- * A object to define additional hooks that are equivalent to the built-in React Hooks.
- * @description ESLint React will recognize these aliases as equivalent to the built-in hooks in all its rules.
- * @example `{ useEffect: ["useIsomorphicLayoutEffect"] }`
- */
- additionalHooks: z.optional(CustomHooksSchema),
- /**
- * An array of user-defined components
- * @description This is used to inform the ESLint React plugins how to treat these components during checks.
- * @example `[{ name: "Link", as: "a", attributes: [{ name: "to", as: "href" }, { name: "rel", defaultValue: "noopener noreferrer" }] }]`
- */
- additionalComponents: z.optional(z.array(CustomComponentSchema)),
});
/**
+ * Schema for ESLint settings
* @internal
*/
export const ESLintSettingsSchema = z.optional(
@@ -165,73 +52,67 @@ export const ESLintSettingsSchema = z.optional(
}),
);
-export type CustomComponent = z.infer;
-
-export type CustomComponentProp = z.infer;
-
-export type CustomHooks = z.infer;
-
+// ===== Type Definitions =====
export type ESLintSettings = z.infer;
-
export type ESLintReactSettings = z.infer;
-export function isESLintSettings(settings: unknown): settings is ESLintSettings {
- return ESLintSettingsSchema.safeParse(settings).success;
+/**
+ * Normalized ESLint React settings with processed values
+ */
+export interface ESLintReactSettingsNormalized {
+ importSource: string;
+ polymorphicPropName: string | unit;
+ version: string;
}
-export function isESLintReactSettings(settings: unknown): settings is ESLintReactSettings {
- return ESLintReactSettingsSchema.safeParse(settings).success;
-}
+// ===== Default Values =====
/**
- * The default ESLint settings for "react-x".
+ * Default ESLint React settings
*/
export const DEFAULT_ESLINT_REACT_SETTINGS = {
version: "detect",
importSource: "react",
- strict: true,
- skipImportCheck: true,
polymorphicPropName: "as",
- additionalComponents: [],
- additionalHooks: {
- useEffect: ["useIsomorphicLayoutEffect"],
- useLayoutEffect: ["useIsomorphicLayoutEffect"],
- },
} as const satisfies ESLintReactSettings;
+/**
+ * Default ESLint settings with React settings included
+ */
export const DEFAULT_ESLINT_SETTINGS = {
"react-x": DEFAULT_ESLINT_REACT_SETTINGS,
} as const satisfies ESLintSettings;
-export interface CustomComponentPropNormalized {
- name: string;
- as: string;
- // controlled?: boolean | unit;
- defaultValue?: string | unit;
-}
+// ===== Utility Functions =====
-export interface CustomComponentNormalized {
- name: string;
- as: string;
- attributes: CustomComponentPropNormalized[];
- re: { test(s: string): boolean };
- // selector?: string | unit;
+/**
+ * Checks if the provided settings conform to ESLintSettings schema
+ * @param settings The settings object to validate
+ */
+export function isESLintSettings(settings: unknown): settings is ESLintSettings {
+ return ESLintSettingsSchema.safeParse(settings).success;
}
-export interface ESLintReactSettingsNormalized {
- additionalHooks: CustomHooks;
- components: CustomComponentNormalized[];
- importSource: string;
- polymorphicPropName: string | unit;
- skipImportCheck: boolean;
- strict: boolean;
- version: string;
+/**
+ * Checks if the provided settings conform to ESLintReactSettings schema
+ * @param settings The settings object to validate
+ */
+export function isESLintReactSettings(settings: unknown): settings is ESLintReactSettings {
+ return ESLintReactSettingsSchema.safeParse(settings).success;
}
+/**
+ * Coerces unknown input to ESLintSettings type
+ * @param settings The settings object to coerce
+ */
export const coerceESLintSettings = (settings: unknown): PartialDeep => {
return settings as PartialDeep;
};
+/**
+ * Decodes and validates ESLint settings, using defaults if invalid
+ * @param settings The settings object to decode
+ */
export const decodeESLintSettings = (settings: unknown): ESLintSettings => {
if (isESLintSettings(settings)) {
return settings;
@@ -239,10 +120,18 @@ export const decodeESLintSettings = (settings: unknown): ESLintSettings => {
return DEFAULT_ESLINT_SETTINGS;
};
+/**
+ * Coerces unknown input to ESLintReactSettings type
+ * @param settings The settings object to coerce
+ */
export const coerceSettings = (settings: unknown): PartialDeep => {
return settings as PartialDeep;
};
+/**
+ * Decodes and validates ESLint React settings, using defaults if invalid
+ * @param settings The settings object to decode
+ */
export const decodeSettings = (settings: unknown): ESLintReactSettings => {
if (isESLintReactSettings(settings)) {
return settings;
@@ -250,46 +139,34 @@ export const decodeSettings = (settings: unknown): ESLintReactSettings => {
return DEFAULT_ESLINT_REACT_SETTINGS;
};
+/**
+ * Normalizes ESLint React settings to a consistent internal format
+ * Transforms component definitions and resolves version information
+ */
export const normalizeSettings = ({
- additionalComponents = [],
- additionalHooks = {},
importSource = "react",
polymorphicPropName = "as",
- skipImportCheck = true,
- strict = true,
version,
...rest
}: ESLintReactSettings) => {
return {
...rest,
- components: additionalComponents.map((component) => {
- const { name, as = name, attributes = [], ...rest } = component;
- const re = RE.toRegExp(name);
- return {
- ...rest,
- name,
- re,
- as,
- attributes: attributes.map(({ name, as = name, ...rest }) => ({
- ...rest,
- name,
- as,
- })),
- };
- }),
- additionalHooks,
importSource,
polymorphicPropName,
- skipImportCheck,
- strict,
version: match(version)
.with(P.union(P.nullish, "", "detect"), () => getReactVersion("19.1.0"))
.otherwise(identity),
} as const satisfies ESLintReactSettingsNormalized;
};
+// Cache for storing normalized settings to avoid repeated processing
const cache = new Map();
+/**
+ * Retrieves normalized ESLint React settings from the rule context
+ * Uses caching for performance optimization
+ * @param context The ESLint rule context
+ */
export function getSettingsFromContext(context: RuleContext): ESLintReactSettingsNormalized {
const settings = context.settings;
return getOrElseUpdate(
@@ -300,12 +177,12 @@ export function getSettingsFromContext(context: RuleContext): ESLintReactSetting
}
/**
- * A helper function to define settings for "react-x" with type checking in JavaScript files.
- * @param settings The settings.
- * @returns The settings.
+ * Helper function for defining typed settings for "react-x" in JavaScript files
+ * Provides type checking without runtime transformation
*/
export const defineSettings: (settings: ESLintReactSettings) => ESLintReactSettings = identity;
+// Type declaration augmentation for TypeScript ESLint
declare module "@typescript-eslint/utils/ts-eslint" {
export interface SharedConfigurationSettings {
["react-x"]?: Partial;
diff --git a/packages/shared/tsdown.config.ts b/packages/shared/tsdown.config.ts
index 49470fbf17..031761a959 100644
--- a/packages/shared/tsdown.config.ts
+++ b/packages/shared/tsdown.config.ts
@@ -5,11 +5,11 @@ export default {
dts: true,
entry: ["src/index.ts"],
external: ["eslint", "typescript"],
- format: ["cjs", "esm"],
+ format: ["esm"],
minify: false,
outDir: "dist",
platform: "node",
sourcemap: false,
- target: "node18",
+ target: "node20",
treeshake: true,
} satisfies Options;
diff --git a/packages/utilities/ast/package.json b/packages/utilities/ast/package.json
index 839b591904..e611bdaec9 100644
--- a/packages/utilities/ast/package.json
+++ b/packages/utilities/ast/package.json
@@ -1,6 +1,6 @@
{
"name": "@eslint-react/ast",
- "version": "1.53.2-beta.1",
+ "version": "2.0.0-beta.194",
"description": "ESLint React's TSESTree AST utility module.",
"homepage": "https://github.com/Rel1cx/eslint-react",
"bugs": {
@@ -14,45 +14,37 @@
"license": "MIT",
"author": "Rel1cx",
"sideEffects": false,
+ "type": "module",
"exports": {
".": {
- "import": {
- "types": "./dist/index.d.mts",
- "default": "./dist/index.mjs"
- },
- "require": {
- "types": "./dist/index.d.ts",
- "default": "./dist/index.js"
- }
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.js"
},
"./package.json": "./package.json"
},
- "main": "dist/index.js",
- "module": "dist/index.mjs",
- "types": "dist/index.d.ts",
"files": [
"dist",
"./package.json"
],
"scripts": {
- "build": "tsdown",
+ "build": "tsdown --dts-resolve",
"lint:publish": "publint",
"lint:ts": "tsc --noEmit",
"publish": "pnpm run build && pnpm run lint:publish"
},
"dependencies": {
"@eslint-react/eff": "workspace:*",
- "@typescript-eslint/types": "^8.44.0",
- "@typescript-eslint/typescript-estree": "^8.44.0",
- "@typescript-eslint/utils": "^8.44.0",
+ "@typescript-eslint/types": "^8.44.1",
+ "@typescript-eslint/typescript-estree": "^8.44.1",
+ "@typescript-eslint/utils": "^8.44.1",
"string-ts": "^2.2.1",
"ts-pattern": "^5.8.0"
},
"devDependencies": {
"@local/configs": "workspace:*",
- "tsdown": "^0.15.3"
+ "tsdown": "^0.15.4"
},
"engines": {
- "node": ">=18.18.0"
+ "node": ">=20.19.0"
}
}
diff --git a/packages/utilities/ast/src/__tests__/ast-class-id.test.ts b/packages/utilities/ast/src/__tests__/class.test.ts
similarity index 94%
rename from packages/utilities/ast/src/__tests__/ast-class-id.test.ts
rename to packages/utilities/ast/src/__tests__/class.test.ts
index 2109bdf168..2566e59419 100644
--- a/packages/utilities/ast/src/__tests__/ast-class-id.test.ts
+++ b/packages/utilities/ast/src/__tests__/class.test.ts
@@ -1,13 +1,13 @@
-import type { TSESTreeClass } from "../ast-node";
-
import { parseForESLint } from "@typescript-eslint/parser";
import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
import { simpleTraverse } from "@typescript-eslint/typescript-estree";
import path from "node:path";
-
import { describe, expect, it } from "vitest";
+
import { getFixturesRootDir } from "../../../../../test";
-import { getClassId } from "../ast-class-id";
+
+import { getClassId } from "../class-id";
+import type { TSESTreeClass } from "../node";
function parse(code: string) {
return parseForESLint(code, {
diff --git a/packages/utilities/ast/src/__tests__/ast-hierarchy.test.ts b/packages/utilities/ast/src/__tests__/expression.test.ts
similarity index 95%
rename from packages/utilities/ast/src/__tests__/ast-hierarchy.test.ts
rename to packages/utilities/ast/src/__tests__/expression.test.ts
index bf4d76fcb1..10d5c73b75 100644
--- a/packages/utilities/ast/src/__tests__/ast-hierarchy.test.ts
+++ b/packages/utilities/ast/src/__tests__/expression.test.ts
@@ -1,5 +1,3 @@
-import type { TSESTreeFunction } from "../ast-node";
-
import { parseForESLint } from "@typescript-eslint/parser";
import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
import { simpleTraverse } from "@typescript-eslint/typescript-estree";
@@ -7,9 +5,10 @@ import tsx from "dedent";
import path from "node:path";
import { describe, expect, it } from "vitest";
+
import { getFixturesRootDir } from "../../../../../test";
-import { getNestedReturnStatements } from "../ast-hierarchy";
-import { isFunction } from "../ast-is";
+import { getNestedReturnStatements } from "../expression";
+import { type TSESTreeFunction, isFunction } from "../node";
function parse(code: string) {
return parseForESLint(code, {
diff --git a/packages/utilities/ast/src/__tests__/ast-function-id.test.ts b/packages/utilities/ast/src/__tests__/function.test.ts
similarity index 94%
rename from packages/utilities/ast/src/__tests__/ast-function-id.test.ts
rename to packages/utilities/ast/src/__tests__/function.test.ts
index 4a54d54c5a..efd46d102c 100644
--- a/packages/utilities/ast/src/__tests__/ast-function-id.test.ts
+++ b/packages/utilities/ast/src/__tests__/function.test.ts
@@ -1,14 +1,13 @@
-import type { TSESTreeFunction } from "../ast-node";
-
import { parseForESLint } from "@typescript-eslint/parser";
import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
import { simpleTraverse } from "@typescript-eslint/typescript-estree";
import path from "node:path";
-
import { describe, expect, it } from "vitest";
+
import { getFixturesRootDir } from "../../../../../test";
-import { getFunctionId } from "../ast-function-id";
-import { isFunction } from "../ast-is";
+
+import { getFunctionId } from "../function-id";
+import { type TSESTreeFunction, isFunction } from "../node";
function parse(code: string) {
return parseForESLint(code, {
diff --git a/packages/utilities/ast/src/ast-array-method-callback.ts b/packages/utilities/ast/src/array-index.ts
similarity index 100%
rename from packages/utilities/ast/src/ast-array-method-callback.ts
rename to packages/utilities/ast/src/array-index.ts
diff --git a/packages/utilities/ast/src/ast-array-method.ts b/packages/utilities/ast/src/array-method.ts
similarity index 100%
rename from packages/utilities/ast/src/ast-array-method.ts
rename to packages/utilities/ast/src/array-method.ts
diff --git a/packages/utilities/ast/src/array.ts b/packages/utilities/ast/src/array.ts
new file mode 100644
index 0000000000..ef6f77d6b3
--- /dev/null
+++ b/packages/utilities/ast/src/array.ts
@@ -0,0 +1,2 @@
+export * from "./array-index";
+export * from "./array-method";
diff --git a/packages/utilities/ast/src/ast-expression.ts b/packages/utilities/ast/src/ast-expression.ts
deleted file mode 100644
index ed766c4bac..0000000000
--- a/packages/utilities/ast/src/ast-expression.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import type { TSESTree } from "@typescript-eslint/types";
-import type { TSESTreeTypeExpression } from "./ast-node";
-
-import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
-import { isTypeExpression } from "./ast-is";
-
-/**
- * Recursively get the inner expression until it's not a TypeExpression
- * @param node - The node to get the expression from
- * @returns The inner expression
- */
-export function getJSExpression(node: TSESTree.Node): Exclude<
- TSESTree.Node,
- TSESTreeTypeExpression
-> {
- if (isTypeExpression(node)) {
- return getJSExpression(node.expression);
- }
- return node;
-}
-
-export function isThisExpression(node: TSESTree.Expression) {
- return getJSExpression(node).type === T.ThisExpression;
-}
diff --git a/packages/utilities/ast/src/ast-function-is.ts b/packages/utilities/ast/src/ast-function-is.ts
deleted file mode 100644
index b0d65d48bf..0000000000
--- a/packages/utilities/ast/src/ast-function-is.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import type { TSESTreeFunction } from "./ast-node";
-
-import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
-
-export function isEmptyFunction(node: TSESTreeFunction) {
- return node.body.type === T.BlockStatement
- && node.body.body.length === 0;
-}
diff --git a/packages/utilities/ast/src/ast-class-id.ts b/packages/utilities/ast/src/class-id.ts
similarity index 55%
rename from packages/utilities/ast/src/ast-class-id.ts
rename to packages/utilities/ast/src/class-id.ts
index 81163b150d..a6bedffd0a 100644
--- a/packages/utilities/ast/src/ast-class-id.ts
+++ b/packages/utilities/ast/src/class-id.ts
@@ -1,15 +1,10 @@
import { unit } from "@eslint-react/eff";
import type { TSESTree } from "@typescript-eslint/types";
-import type { TSESTreeClass } from "./ast-node";
+import type { TSESTreeClass } from "./node";
import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
-/**
- * Gets class identifier from ClassDeclaration or ClassExpression
- * @param node The AST node to check
- * @returns class identifier or null
- */
-export function getClassId(node: TSESTreeClass): unit | TSESTree.Identifier {
+export function getClassId(node: TSESTreeClass): TSESTree.Identifier | unit {
if (node.id != null) return node.id;
if (node.parent.type === T.VariableDeclarator && node.parent.id.type === T.Identifier) {
return node.parent.id;
diff --git a/packages/utilities/ast/src/expression-base.ts b/packages/utilities/ast/src/expression-base.ts
new file mode 100644
index 0000000000..405ed3ff9e
--- /dev/null
+++ b/packages/utilities/ast/src/expression-base.ts
@@ -0,0 +1,19 @@
+import type { TSESTree } from "@typescript-eslint/types";
+import { type TSESTreeTypeExpression, isTypeExpression } from "./node";
+
+/**
+ * Unwraps any type expressions to get the underlying JavaScript expression node.
+ * Recursively processes nodes until a non-type expression is found.
+ *
+ * @param node - The AST node to unwrap
+ * @returns The underlying JavaScript expression node
+ */
+export function getUnderlyingExpression(node: TSESTree.Node): Exclude<
+ TSESTree.Node,
+ TSESTreeTypeExpression
+> {
+ if (isTypeExpression(node)) {
+ return getUnderlyingExpression(node.expression);
+ }
+ return node;
+}
diff --git a/packages/utilities/ast/src/expression-is.ts b/packages/utilities/ast/src/expression-is.ts
new file mode 100644
index 0000000000..f9f7dd2552
--- /dev/null
+++ b/packages/utilities/ast/src/expression-is.ts
@@ -0,0 +1,15 @@
+import type { TSESTree } from "@typescript-eslint/types";
+
+import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
+import { getUnderlyingExpression } from "./expression-base";
+
+/**
+ * Checks if the given expression is a 'this' expression.
+ * Unwraps any type expressions before checking.
+ *
+ * @param node - The expression node to check
+ * @returns true if the expression is a ThisExpression, false otherwise
+ */
+export function isThisExpression(node: TSESTree.Expression) {
+ return getUnderlyingExpression(node).type === T.ThisExpression;
+}
diff --git a/packages/utilities/ast/src/ast-hierarchy.ts b/packages/utilities/ast/src/expression-nested.ts
similarity index 85%
rename from packages/utilities/ast/src/ast-hierarchy.ts
rename to packages/utilities/ast/src/expression-nested.ts
index e512d5f834..6a374165ca 100644
--- a/packages/utilities/ast/src/ast-hierarchy.ts
+++ b/packages/utilities/ast/src/expression-nested.ts
@@ -1,41 +1,8 @@
-import { unit } from "@eslint-react/eff";
-import { AST_NODE_TYPES as T, type TSESTree } from "@typescript-eslint/types";
+import type { TSESTree } from "@typescript-eslint/types";
+import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
import { simpleTraverse } from "@typescript-eslint/typescript-estree";
-import { is, isFunction } from "./ast-is";
-
-/**
- * Find the parent node that satisfies the test function
- * @param node The AST node
- * @param test The test function
- * @returns The parent node that satisfies the test function or `_` if not found
- */
-function findParentNode(
- node: TSESTree.Node | unit,
- test: (n: TSESTree.Node) => n is A,
-): A | unit;
-/**
- * Find the parent node that satisfies the test function or `_` if not found
- * @param node The AST node
- * @param test The test function
- * @returns The parent node that satisfies the test function
- */
-function findParentNode(node: TSESTree.Node | unit, test: (node: TSESTree.Node) => boolean): TSESTree.Node | unit;
-function findParentNode(
- node: TSESTree.Node | unit,
- test: ((node: TSESTree.Node) => boolean) | ((n: TSESTree.Node) => n is A),
-): TSESTree.Node | A | unit {
- if (node == null) return unit;
- let parent = node.parent;
- while (parent != null && parent.type !== T.Program) {
- if (test(parent)) {
- return parent;
- }
- parent = parent.parent;
- }
- return unit;
-}
-
-export { findParentNode };
+import { is, isFunction } from "./node";
+import { findParentNode } from "./traverse";
/**
* Get all nested identifiers in a expression like node
diff --git a/packages/utilities/ast/src/expression.ts b/packages/utilities/ast/src/expression.ts
new file mode 100644
index 0000000000..1a8e48dab2
--- /dev/null
+++ b/packages/utilities/ast/src/expression.ts
@@ -0,0 +1,3 @@
+export * from "./expression-base";
+export * from "./expression-is";
+export * from "./expression-nested";
diff --git a/packages/utilities/ast/src/ast-function-id.ts b/packages/utilities/ast/src/function-id.ts
similarity index 92%
rename from packages/utilities/ast/src/ast-function-id.ts
rename to packages/utilities/ast/src/function-id.ts
index bbf47cc7ff..193149a0a5 100644
--- a/packages/utilities/ast/src/ast-function-id.ts
+++ b/packages/utilities/ast/src/function-id.ts
@@ -1,3 +1,11 @@
+/* eslint-disable jsdoc/require-param */
+import { unit } from "@eslint-react/eff";
+import type { TSESTree } from "@typescript-eslint/types";
+import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
+
+import type { TSESTreeFunction } from "./node";
+import { isMethodOrProperty, isTypeAssertionExpression } from "./node";
+
// Ported from https://github.com/eps1lon/react/blob/8b8d265bd9a4cab7bbd04a9a13950fdc946ea51c/packages/eslint-plugin-react-hooks/src/RulesOfHooks.js#L642
/**
* Gets the static name of a function AST node. For function declarations it is
@@ -6,21 +14,13 @@
* where JS gives anonymous function expressions names. We roughly detect the
* same AST nodes with some exceptions to better fit our use case.
*/
-
-import { unit } from "@eslint-react/eff";
-import type { TSESTree } from "@typescript-eslint/types";
-import type { TSESTreeFunction } from "./ast-node";
-
-import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
-import { isMethodOrProperty, isTypeAssertionExpression } from "./ast-is";
-
-export function getFunctionId(node: TSESTree.Expression | TSESTreeFunction): unit | TSESTree.Identifier {
+export function getFunctionId(node: TSESTree.Expression | TSESTreeFunction): TSESTree.Identifier | unit {
switch (true) {
// function MaybeComponent() {}
case "id" in node
&& node.id != null:
+ // const whatever = function MaybeComponent() {};
return node.id;
- // const whatever = function MaybeComponent() {};
case node.parent.type === T.VariableDeclarator
&& node.parent.init === node
&& node.parent.id.type === T.Identifier:
diff --git a/packages/utilities/ast/src/ast-function-init-path.ts b/packages/utilities/ast/src/function-init-path.ts
similarity index 52%
rename from packages/utilities/ast/src/ast-function-init-path.ts
rename to packages/utilities/ast/src/function-init-path.ts
index 289dc50eec..032ff4dc15 100644
--- a/packages/utilities/ast/src/ast-function-init-path.ts
+++ b/packages/utilities/ast/src/function-init-path.ts
@@ -1,36 +1,30 @@
import { unit } from "@eslint-react/eff";
import type { TSESTree } from "@typescript-eslint/types";
-import type { TSESTreeFunction } from "./ast-node";
-
import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
+import type { TSESTreeFunction } from "./node";
+
+/**
+ * Represents various AST paths for React component function declarations.
+ * Each tuple type represents a specific component definition pattern.
+ */
export type FunctionInitPath =
- /**
- * function Comp() { return ; }
- */
+ // Standard function declaration: function Comp() { return ; }
| readonly [TSESTree.FunctionDeclaration]
- /**
- * const Comp = () => ;
- * const Comp = function () { return ; };
- */
+ // Variable declarations: const Comp = () => ; or const Comp = function() { return ; }
| readonly [
TSESTree.VariableDeclaration,
TSESTree.VariableDeclarator,
TSESTreeFunction,
]
- /**
- * const Comp = React.memo(() => );
- * const Comp = React.forwardRef(() => );
- */
+ // Higher-order component patterns: const Comp = React.memo(() => );
| readonly [
TSESTree.VariableDeclaration,
TSESTree.VariableDeclarator,
TSESTree.CallExpression,
TSESTreeFunction,
]
- /**
- * const Comp = React.memo(React.forwardRef(() => ));
- */
+ // Nested higher-order components: const Comp = React.memo(React.forwardRef(() => ));
| readonly [
TSESTree.VariableDeclaration,
TSESTree.VariableDeclarator,
@@ -38,12 +32,7 @@ export type FunctionInitPath =
TSESTree.CallExpression,
TSESTreeFunction,
]
- /**
- * const Comps = {
- * TopNav() { return ; },
- * SidPanel: () => ,
- * }
- */
+ // Object property components: const Comps = { TopNav() {}, SidePanel: () => }
| readonly [
TSESTree.VariableDeclaration,
TSESTree.VariableDeclarator,
@@ -51,12 +40,7 @@ export type FunctionInitPath =
TSESTree.Property,
TSESTreeFunction,
]
- /**
- * const Comps = {
- * TopNav: React.memo(() => ),
- * SidPanel: React.forwardRef(() => ),
- * }
- */
+ // HOC inside object property: const Comps = { TopNav: React.memo(() => ) }
| readonly [
TSESTree.VariableDeclaration,
TSESTree.VariableDeclarator,
@@ -65,12 +49,7 @@ export type FunctionInitPath =
TSESTree.CallExpression,
TSESTreeFunction,
]
- /**
- * const Comps = {
- * TopNav: React.memo(React.forwardRef(() => )),
- * SidPanel: React.forwardRef(React.memo(() => )),
- * }
- */
+ // Nested HOCs in object: const Comps = { TopNav: React.memo(React.forwardRef(() => )) }
| readonly [
TSESTree.VariableDeclaration,
TSESTree.VariableDeclarator,
@@ -80,22 +59,14 @@ export type FunctionInitPath =
TSESTree.CallExpression,
TSESTreeFunction,
]
- /**
- * class Comp {
- * TopNav() { return ; }
- * }
- */
+ // Class method components: class Comp { TopNav() { return ; } }
| readonly [
TSESTree.ClassDeclaration,
TSESTree.ClassBody,
TSESTree.MethodDefinition,
TSESTreeFunction,
]
- /**
- * class Comp {
- * TopNav = () => ;
- * }
- */
+ // Class property arrow functions: class Comp { TopNav = () => ; }
| readonly [
TSESTree.ClassDeclaration,
TSESTree.ClassBody,
@@ -103,48 +74,85 @@ export type FunctionInitPath =
TSESTreeFunction,
];
+/**
+ * Identifies the initialization path of a function node in the AST.
+ * Determines what kind of component declaration pattern the function belongs to.
+ *
+ * @param node - The function node to analyze
+ * @returns The function initialization path or unit if not identifiable
+ */
export function getFunctionInitPath(node: TSESTreeFunction): unit | FunctionInitPath {
+ // Function declaration is the simplest case
if (node.type === T.FunctionDeclaration) {
return [node] as const;
}
+
const { parent } = node;
+
+ // Match against various component patterns
switch (true) {
+ // Basic variable declaration: const Comp = () => {}
case parent.type === T.VariableDeclarator:
return [parent.parent, parent, node] as const;
+
+ // HOC pattern: const Comp = React.memo(() => {})
case parent.type === T.CallExpression
&& parent.parent.type === T.VariableDeclarator:
return [parent.parent.parent, parent.parent, parent, node] as const;
+
+ // Nested HOC pattern: const Comp = React.memo(React.forwardRef(() => {}))
case parent.type === T.CallExpression
&& parent.parent.type === T.CallExpression
&& parent.parent.parent.type === T.VariableDeclarator:
return [parent.parent.parent.parent, parent.parent.parent, parent.parent, parent, node] as const;
+
+ // Object property component: const Comps = { Nav: () => {} }
case parent.type === T.Property
&& parent.parent.type === T.ObjectExpression
&& parent.parent.parent.type === T.VariableDeclarator:
return [parent.parent.parent.parent, parent.parent.parent, parent.parent, parent, node] as const;
+
+ // Class method component: class Comp { render() {} }
case parent.type === T.MethodDefinition
&& parent.parent.parent.type === T.ClassDeclaration:
return [parent.parent.parent, parent.parent, parent, node] as const;
+
+ // Class property arrow function: class Comp { render = () => {} }
case parent.type === T.PropertyDefinition
&& parent.parent.parent.type === T.ClassDeclaration:
return [parent.parent.parent, parent.parent, parent, node] as const;
}
+
+ // Not a recognized function component initialization pattern
return unit;
}
-export function hasCallInFunctionInitPath(callName: string, initPath: FunctionInitPath) {
- return initPath
- .some((n) => {
- if (n.type !== T.CallExpression) {
- return false;
- }
- switch (n.callee.type) {
- case T.Identifier:
- return n.callee.name === callName;
- case T.MemberExpression:
- return "name" in n.callee.property && n.callee.property.name === callName;
- default:
- return false;
- }
- });
+/**
+ * Checks if a specific function call exists in the function initialization path.
+ * Useful for detecting HOCs like React.memo, React.forwardRef, etc.
+ *
+ * @param callName - The name of the call to check for (e.g., "memo", "forwardRef")
+ * @param initPath - The function initialization path to search in
+ * @returns True if the call exists in the path, false otherwise
+ */
+export function hasCallInFunctionInitPath(callName: string, initPath: FunctionInitPath): boolean {
+ return initPath.some((node) => {
+ if (node.type !== T.CallExpression) {
+ return false;
+ }
+
+ const { callee } = node;
+
+ // Check direct function calls: memo(...)
+ if (callee.type === T.Identifier) {
+ return callee.name === callName;
+ }
+
+ // Check member expressions: React.memo(...)
+ if (callee.type === T.MemberExpression && "name" in callee.property) {
+ return callee.property.name === callName;
+ }
+
+ return false;
+ });
}
diff --git a/packages/utilities/ast/src/function-is.ts b/packages/utilities/ast/src/function-is.ts
new file mode 100644
index 0000000000..5c20a778bb
--- /dev/null
+++ b/packages/utilities/ast/src/function-is.ts
@@ -0,0 +1,14 @@
+import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
+
+import type { TSESTreeFunction } from "./node";
+
+export function isFunctionEmpty(node: TSESTreeFunction) {
+ return node.body.type === T.BlockStatement
+ && node.body.body.length === 0;
+}
+
+export function isFunctionImmediatelyInvoked(node: TSESTreeFunction) {
+ return node.type !== T.FunctionDeclaration
+ && node.parent.type === T.CallExpression
+ && node.parent.callee === node;
+}
diff --git a/packages/utilities/ast/src/function.ts b/packages/utilities/ast/src/function.ts
new file mode 100644
index 0000000000..af42b0053b
--- /dev/null
+++ b/packages/utilities/ast/src/function.ts
@@ -0,0 +1,3 @@
+export * from "./function-id";
+export * from "./function-init-path";
+export * from "./function-is";
diff --git a/packages/utilities/ast/src/index.ts b/packages/utilities/ast/src/index.ts
index 6e357d02b3..e7f597c814 100644
--- a/packages/utilities/ast/src/index.ts
+++ b/packages/utilities/ast/src/index.ts
@@ -1,16 +1,27 @@
-export * from "./ast-array-method";
-export * from "./ast-array-method-callback";
-export * from "./ast-class-id";
-export * from "./ast-expression";
-export * from "./ast-function-id";
-export * from "./ast-function-init-path";
-export * from "./ast-function-is";
-export * from "./ast-hierarchy";
-export * from "./ast-is";
-export * from "./ast-line";
-export * from "./ast-literal-is";
-export type * from "./ast-node";
-export * from "./ast-node-equal";
-export * from "./ast-process-env-node-env";
-export * from "./ast-property-name";
-export * from "./ast-to-string";
+export * from "./array";
+export * from "./array-index";
+export * from "./array-method";
+export * from "./class-id";
+export * from "./expression";
+export * from "./expression-base";
+export * from "./expression-is";
+export * from "./expression-nested";
+export * from "./function";
+export * from "./function-id";
+export * from "./function-init-path";
+export * from "./function-is";
+export * from "./literal";
+export * from "./misc";
+export * from "./node";
+export * from "./node-equal";
+export * from "./node-format";
+export * from "./node-is";
+export * from "./node-selectors";
+export type * from "./node-types";
+export * from "./process-env-node-env";
+export * from "./promise-then";
+export * from "./property-name";
+export * from "./traverse";
+export type * from "./traverse-down";
+export * from "./traverse-up";
+export * from "./vitest-mock";
diff --git a/packages/utilities/ast/src/ast-literal-is.ts b/packages/utilities/ast/src/literal.ts
similarity index 100%
rename from packages/utilities/ast/src/ast-literal-is.ts
rename to packages/utilities/ast/src/literal.ts
diff --git a/packages/utilities/ast/src/ast-line.ts b/packages/utilities/ast/src/misc.ts
similarity index 94%
rename from packages/utilities/ast/src/ast-line.ts
rename to packages/utilities/ast/src/misc.ts
index b418b9e4a9..1f7c10d10b 100644
--- a/packages/utilities/ast/src/ast-line.ts
+++ b/packages/utilities/ast/src/misc.ts
@@ -1,6 +1,7 @@
import type { TSESTree } from "@typescript-eslint/types";
import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
-import { isOneOf } from "./ast-is";
+
+import { isOneOf } from "./node";
/**
* Check if a node is multiline
diff --git a/packages/utilities/ast/src/ast-node-equal.ts b/packages/utilities/ast/src/node-equal.ts
similarity index 100%
rename from packages/utilities/ast/src/ast-node-equal.ts
rename to packages/utilities/ast/src/node-equal.ts
diff --git a/packages/utilities/ast/src/ast-to-string.ts b/packages/utilities/ast/src/node-format.ts
similarity index 70%
rename from packages/utilities/ast/src/ast-to-string.ts
rename to packages/utilities/ast/src/node-format.ts
index 2d5c0a20fc..9601f24f30 100644
--- a/packages/utilities/ast/src/ast-to-string.ts
+++ b/packages/utilities/ast/src/node-format.ts
@@ -1,8 +1,9 @@
+/* eslint-disable jsdoc/require-param */
import type { TSESTree } from "@typescript-eslint/types";
import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
import { delimiterCase, replace, toLowerCase } from "string-ts";
-import { isJSX } from "./ast-is";
+import { isJSX } from "./node-is";
function getLiteralValueType(input: bigint | boolean | null | number | string | symbol) {
// eslint-disable-next-line local/prefer-eqeq-nullish-comparison
@@ -10,7 +11,10 @@ function getLiteralValueType(input: bigint | boolean | null | number | string |
return typeof input;
}
-export function toDelimiterCaseType(node: TSESTree.Node, delimiter = " ") {
+/**
+ * @internal
+ */
+export function toDelimiterFormat(node: TSESTree.Node, delimiter = " ") {
if (node.type === T.Literal) {
if ("regex" in node) {
return "RegExp literal";
@@ -24,12 +28,11 @@ export function toDelimiterCaseType(node: TSESTree.Node, delimiter = " ") {
}
/**
- * Returns human readable node name for given AST node
- * @param node AST node
- * @param getText A function that returns the text of the node in the source code
- * @returns Human readable node name
+ * Incomplete but sufficient stringification of AST nodes for common use cases
+ *
+ * @internal
*/
-export function toString(node: TSESTree.Node, getText: (node: TSESTree.Node) => string): string {
+export function toStringFormat(node: TSESTree.Node, getText: (node: TSESTree.Node) => string): string {
switch (node.type) {
case T.Identifier:
case T.JSXIdentifier:
@@ -37,7 +40,7 @@ export function toString(node: TSESTree.Node, getText: (node: TSESTree.Node) =>
return node.name;
case T.MemberExpression:
case T.JSXMemberExpression:
- return `${toString(node.object, getText)}.${toString(node.property, getText)}`;
+ return `${toStringFormat(node.object, getText)}.${toStringFormat(node.property, getText)}`;
case T.JSXNamespacedName:
return `${node.namespace.name}:${node.name.name}`;
case T.JSXText:
diff --git a/packages/utilities/ast/src/ast-is.ts b/packages/utilities/ast/src/node-is.ts
similarity index 100%
rename from packages/utilities/ast/src/ast-is.ts
rename to packages/utilities/ast/src/node-is.ts
diff --git a/packages/utilities/kit/src/Selector.ts b/packages/utilities/ast/src/node-selectors.ts
similarity index 78%
rename from packages/utilities/kit/src/Selector.ts
rename to packages/utilities/ast/src/node-selectors.ts
index 68e35ba1c7..9670bd2a94 100644
--- a/packages/utilities/kit/src/Selector.ts
+++ b/packages/utilities/ast/src/node-selectors.ts
@@ -20,15 +20,15 @@ export type DisplayNameAssignmentExpression = TSESTree.AssignmentExpression & {
right: TSESTree.Literal;
};
-export const IMPLICIT_RETURN_ARROW_FUNCTION_EXPRESSION = "ArrowFunctionExpression[body.type!='BlockStatement']";
+export const SEL_IMPLICIT_RETURN_ARROW_FUNCTION_EXPRESSION = "ArrowFunctionExpression[body.type!='BlockStatement']";
-export const OBJECT_DESTRUCTURING_VARIABLE_DECLARATOR = [
+export const SEL_OBJECT_DESTRUCTURING_VARIABLE_DECLARATOR = [
"VariableDeclarator",
"[id.type='ObjectPattern']",
"[init.type='Identifier']",
].join("");
-export const DISPLAY_NAME_ASSIGNMENT_EXPRESSION = [
+export const SEL_DISPLAY_NAME_ASSIGNMENT_EXPRESSION = [
"AssignmentExpression",
"[operator='=']",
"[left.type='MemberExpression']",
diff --git a/packages/utilities/ast/src/ast-node.ts b/packages/utilities/ast/src/node-types.ts
similarity index 96%
rename from packages/utilities/ast/src/ast-node.ts
rename to packages/utilities/ast/src/node-types.ts
index 6f0367b6fc..3566d5262a 100644
--- a/packages/utilities/ast/src/ast-node.ts
+++ b/packages/utilities/ast/src/node-types.ts
@@ -55,6 +55,8 @@ export type TSESTreeJSX =
| TSESTree.JSXText
| TSESTree.JSXTextToken;
+export type TSESTreeJSXAttributeLike = TSESTree.JSXAttribute | TSESTree.JSXSpreadAttribute;
+
export type TSESTreeDestructuringPattern =
| TSESTree.ArrayPattern
| TSESTree.AssignmentPattern
diff --git a/packages/utilities/ast/src/node.ts b/packages/utilities/ast/src/node.ts
new file mode 100644
index 0000000000..0bc9676c9e
--- /dev/null
+++ b/packages/utilities/ast/src/node.ts
@@ -0,0 +1,4 @@
+export * from "./node-equal";
+export * from "./node-format";
+export * from "./node-is";
+export type * from "./node-types";
diff --git a/packages/utilities/ast/src/ast-process-env-node-env.ts b/packages/utilities/ast/src/process-env-node-env.ts
similarity index 88%
rename from packages/utilities/ast/src/ast-process-env-node-env.ts
rename to packages/utilities/ast/src/process-env-node-env.ts
index eaaeb77c81..19d1d62141 100644
--- a/packages/utilities/ast/src/ast-process-env-node-env.ts
+++ b/packages/utilities/ast/src/process-env-node-env.ts
@@ -1,7 +1,6 @@
import type { unit } from "@eslint-react/eff";
-import type { TSESTree } from "@typescript-eslint/types";
-import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
-import { isLiteral } from "./ast-literal-is";
+import { AST_NODE_TYPES as T, type TSESTree } from "@typescript-eslint/types";
+import { isLiteral } from "./literal";
/**
* Check if the given node is a member expression that accesses `process.env.NODE_ENV`
@@ -21,7 +20,7 @@ export function isProcessEnvNodeEnv(node: TSESTree.Node | null | unit): node is
}
/**
- * Check if the given node is a binary expression that compares `process.env.NODE_ENV` with a string literal
+ * Check if the given node is a binary expression that compares `process.env.NODE_ENV` with a string literal.
* @param node The AST node
* @param operator The operator used in the comparison
* @param value The string literal value to compare against
diff --git a/packages/plugins/eslint-plugin-react-hooks-extra/src/utils/is-then-call.ts b/packages/utilities/ast/src/promise-then.ts
similarity index 100%
rename from packages/plugins/eslint-plugin-react-hooks-extra/src/utils/is-then-call.ts
rename to packages/utilities/ast/src/promise-then.ts
diff --git a/packages/utilities/ast/src/ast-property-name.ts b/packages/utilities/ast/src/property-name.ts
similarity index 77%
rename from packages/utilities/ast/src/ast-property-name.ts
rename to packages/utilities/ast/src/property-name.ts
index 044b2494c7..f640d2e4bb 100644
--- a/packages/utilities/ast/src/ast-property-name.ts
+++ b/packages/utilities/ast/src/property-name.ts
@@ -1,12 +1,12 @@
import { unit } from "@eslint-react/eff";
import { AST_NODE_TYPES as T, type TSESTree } from "@typescript-eslint/types";
-import { getJSExpression } from "./ast-expression";
-import { isTypeExpression } from "./ast-is";
+import { getUnderlyingExpression } from "./expression";
+import { isTypeExpression } from "./node";
export function getPropertyName(node: TSESTree.Node): string | unit {
if (isTypeExpression(node)) {
- return getPropertyName(getJSExpression(node));
+ return getPropertyName(getUnderlyingExpression(node));
}
if (node.type === T.Identifier || node.type === T.PrivateIdentifier) {
return node.name;
diff --git a/packages/utilities/ast/src/traverse-down.ts b/packages/utilities/ast/src/traverse-down.ts
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/utilities/ast/src/traverse-up.ts b/packages/utilities/ast/src/traverse-up.ts
new file mode 100644
index 0000000000..32da0302d2
--- /dev/null
+++ b/packages/utilities/ast/src/traverse-up.ts
@@ -0,0 +1,36 @@
+import { unit } from "@eslint-react/eff";
+import { AST_NODE_TYPES as T, type TSESTree } from "@typescript-eslint/types";
+
+/**
+ * Find the parent node that satisfies the test function
+ * @param node The AST node
+ * @param test The test function
+ * @returns The parent node that satisfies the test function or `_` if not found
+ */
+function findParentNode(
+ node: TSESTree.Node | unit,
+ test: (n: TSESTree.Node) => n is A,
+): A | unit;
+/**
+ * Find the parent node that satisfies the test function or `_` if not found
+ * @param node The AST node
+ * @param test The test function
+ * @returns The parent node that satisfies the test function
+ */
+function findParentNode(node: TSESTree.Node | unit, test: (node: TSESTree.Node) => boolean): TSESTree.Node | unit;
+function findParentNode(
+ node: TSESTree.Node | unit,
+ test: ((node: TSESTree.Node) => boolean) | ((n: TSESTree.Node) => n is A),
+): TSESTree.Node | A | unit {
+ if (node == null) return unit;
+ let parent = node.parent;
+ while (parent != null && parent.type !== T.Program) {
+ if (test(parent)) {
+ return parent;
+ }
+ parent = parent.parent;
+ }
+ return unit;
+}
+
+export { findParentNode };
diff --git a/packages/utilities/ast/src/traverse.ts b/packages/utilities/ast/src/traverse.ts
new file mode 100644
index 0000000000..ad7a9f766e
--- /dev/null
+++ b/packages/utilities/ast/src/traverse.ts
@@ -0,0 +1,2 @@
+export type * from "./traverse-down";
+export * from "./traverse-up";
diff --git a/packages/utilities/ast/src/vitest-mock.ts b/packages/utilities/ast/src/vitest-mock.ts
new file mode 100644
index 0000000000..ee2a67585c
--- /dev/null
+++ b/packages/utilities/ast/src/vitest-mock.ts
@@ -0,0 +1,32 @@
+import type { unit } from "@eslint-react/eff";
+import { AST_NODE_TYPES as T, type TSESTree } from "@typescript-eslint/types";
+import { isFunction } from "./node-is";
+
+/**
+ * Checks if the given node is a `vi.mock`.
+ * @param node The node to check
+ * @returns `true` if the node is a `vi.mock`, otherwise `false`.
+ * @internal
+ */
+export function isViMock(node: TSESTree.Node | null | unit): node is TSESTree.MemberExpression {
+ return node != null
+ && node.type === T.MemberExpression
+ && node.object.type === T.Identifier
+ && node.object.name === "vi"
+ && node.property.type === T.Identifier
+ && node.property.name === "mock";
+}
+
+/**
+ * Checks if the given node is a `vi.mock` callback.
+ * @param node The node to check
+ * @returns `true` if the node is a `vi.mock` callback, otherwise `false`.
+ * @internal
+ */
+export function isViMockCallback(node: TSESTree.Node | null | unit) {
+ return node != null
+ && isFunction(node)
+ && node.parent.type === T.CallExpression
+ && isViMock(node.parent.callee)
+ && node.parent.arguments[1] === node;
+}
diff --git a/packages/utilities/ast/tsdown.config.ts b/packages/utilities/ast/tsdown.config.ts
index 49470fbf17..031761a959 100644
--- a/packages/utilities/ast/tsdown.config.ts
+++ b/packages/utilities/ast/tsdown.config.ts
@@ -5,11 +5,11 @@ export default {
dts: true,
entry: ["src/index.ts"],
external: ["eslint", "typescript"],
- format: ["cjs", "esm"],
+ format: ["esm"],
minify: false,
outDir: "dist",
platform: "node",
sourcemap: false,
- target: "node18",
+ target: "node20",
treeshake: true,
} satisfies Options;
diff --git a/packages/utilities/eff/THIRD-PARTY-LICENSE b/packages/utilities/eff/THIRD-PARTY-LICENSE
deleted file mode 100644
index be1f5c14c7..0000000000
--- a/packages/utilities/eff/THIRD-PARTY-LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2023 Effectful Technologies Inc
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/packages/utilities/eff/package.json b/packages/utilities/eff/package.json
index b2a07baf89..688f5681d9 100644
--- a/packages/utilities/eff/package.json
+++ b/packages/utilities/eff/package.json
@@ -1,6 +1,6 @@
{
"name": "@eslint-react/eff",
- "version": "1.53.2-beta.1",
+ "version": "2.0.0-beta.194",
"description": "JavaScript and TypeScript utilities (previously some re-exports of the effect library).",
"homepage": "https://github.com/Rel1cx/eslint-react",
"bugs": {
@@ -14,28 +14,20 @@
"license": "MIT",
"author": "Rel1cx",
"sideEffects": false,
+ "type": "module",
"exports": {
".": {
- "import": {
- "types": "./dist/index.d.mts",
- "default": "./dist/index.mjs"
- },
- "require": {
- "types": "./dist/index.d.ts",
- "default": "./dist/index.js"
- }
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.js"
},
"./package.json": "./package.json"
},
- "main": "dist/index.js",
- "module": "dist/index.mjs",
- "types": "dist/index.d.ts",
"files": [
"dist",
"./package.json"
],
"scripts": {
- "build": "tsdown",
+ "build": "tsdown --dts-resolve",
"build:docs": "typedoc",
"lint:publish": "publint",
"lint:ts": "tsc --noEmit",
@@ -43,9 +35,9 @@
},
"devDependencies": {
"@local/configs": "workspace:*",
- "tsdown": "^0.15.3"
+ "tsdown": "^0.15.4"
},
"engines": {
- "node": ">=18.18.0"
+ "node": ">=20.19.0"
}
}
diff --git a/packages/utilities/eff/src/index.ts b/packages/utilities/eff/src/index.ts
index 1be0e0f53f..cc716d4db7 100644
--- a/packages/utilities/eff/src/index.ts
+++ b/packages/utilities/eff/src/index.ts
@@ -1,3 +1,51 @@
+// #region Licenses
+
+// MIT License
+
+// Copyright(c) 2023 Effectful Technologies Inc
+
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+// MIT License
+
+// Copyright(c) 2023 Rel1cx
+
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+// region Directives
+
/* eslint-disable @typescript-eslint/ban-ts-comment */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-non-null-assertion */
@@ -10,12 +58,15 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/unified-signatures */
-/* eslint-disable jsdoc/require-param */
+/* eslint-disable jsdoc/check-param-names */
+/* eslint-disable jsdoc/require-param-description */
/* eslint-disable local/no-shadow-underscore */
/* eslint-disable local/prefer-eqeq-nullish-comparison */
/* eslint-disable prefer-rest-params */
-// #region Helper
+// #endregion
+
+// #region Helpers
/**
* alias for `undefined`
@@ -509,6 +560,7 @@ export const pipeArguments = (self: A, args: IArguments): unknown => {
* ```
*
* @param a - The value to pipe.
+ * @param args
* @since 1.0.0
*/
export function pipe(a: A): A;
@@ -969,6 +1021,14 @@ export function pipe(a: unknown, ...args: Array): unknown {
* See also [`pipe`](#pipe).
*
* @param ab - The first function to apply.
+ * @param bc
+ * @param cd
+ * @param de
+ * @param ef
+ * @param fg
+ * @param gh
+ * @param hi
+ * @param ij
* @example
* ```ts
* import * as assert from "node:assert"
diff --git a/packages/utilities/eff/tsdown.config.ts b/packages/utilities/eff/tsdown.config.ts
index fc40997783..ab2056e62f 100644
--- a/packages/utilities/eff/tsdown.config.ts
+++ b/packages/utilities/eff/tsdown.config.ts
@@ -5,11 +5,11 @@ export default {
dts: true,
entry: ["src/index.ts"],
external: ["eslint", "typescript"],
- format: ["cjs", "esm"],
+ format: ["esm"],
minify: false,
outDir: "dist",
platform: "neutral",
sourcemap: false,
- target: "node18",
+ target: "node20",
treeshake: true,
} satisfies Options;
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/JsxConfig/README.md b/packages/utilities/kit/docs/@eslint-react/namespaces/JsxConfig/README.md
deleted file mode 100644
index b693c6d3fa..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/JsxConfig/README.md
+++ /dev/null
@@ -1,21 +0,0 @@
-[**@eslint-react/kit**](../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../README.md) / JsxConfig
-
-# JsxConfig
-
-## Interfaces
-
-- [JsxConfig](interfaces/JsxConfig.md)
-
-## Variables
-
-- [JsxEmit](variables/JsxEmit.md)
-
-## Functions
-
-- [getFromAnnotation](functions/getFromAnnotation.md)
-- [getFromContext](functions/getFromContext.md)
-- [make](functions/make.md)
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/JsxConfig/functions/getFromAnnotation.md b/packages/utilities/kit/docs/@eslint-react/namespaces/JsxConfig/functions/getFromAnnotation.md
deleted file mode 100644
index 2049a118da..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/JsxConfig/functions/getFromAnnotation.md
+++ /dev/null
@@ -1,25 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [JsxConfig](../README.md) / getFromAnnotation
-
-# Function: getFromAnnotation()
-
-> **getFromAnnotation**(`context`): [`JsxConfig`](../interfaces/JsxConfig.md)
-
-Get JsxConfig from annotation
-
-## Parameters
-
-### context
-
-[`RuleContext`](../../../../type-aliases/RuleContext.md)
-
-The RuleContext
-
-## Returns
-
-[`JsxConfig`](../interfaces/JsxConfig.md)
-
-JsxConfig
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/JsxConfig/functions/getFromContext.md b/packages/utilities/kit/docs/@eslint-react/namespaces/JsxConfig/functions/getFromContext.md
deleted file mode 100644
index 842bf0b8e6..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/JsxConfig/functions/getFromContext.md
+++ /dev/null
@@ -1,45 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [JsxConfig](../README.md) / getFromContext
-
-# Function: getFromContext()
-
-> **getFromContext**(`context`): `object`
-
-Get JsxConfig from RuleContext
-
-## Parameters
-
-### context
-
-[`RuleContext`](../../../../type-aliases/RuleContext.md)
-
-The RuleContext
-
-## Returns
-
-`object`
-
-JsxConfig
-
-### jsx
-
-> **jsx**: `4` \| `JsxEmit`
-
-### jsxFactory
-
-> **jsxFactory**: `string`
-
-### jsxFragmentFactory
-
-> **jsxFragmentFactory**: `string`
-
-### jsxImportSource
-
-> **jsxImportSource**: `string`
-
-### reactNamespace
-
-> **reactNamespace**: `string`
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/JsxConfig/functions/make.md b/packages/utilities/kit/docs/@eslint-react/namespaces/JsxConfig/functions/make.md
deleted file mode 100644
index b2930db707..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/JsxConfig/functions/make.md
+++ /dev/null
@@ -1,17 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [JsxConfig](../README.md) / make
-
-# Function: make()
-
-> **make**(): [`JsxConfig`](../interfaces/JsxConfig.md)
-
-Create a JsxConfig object
-
-## Returns
-
-[`JsxConfig`](../interfaces/JsxConfig.md)
-
-JsxConfig
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/LanguagePreference/README.md b/packages/utilities/kit/docs/@eslint-react/namespaces/LanguagePreference/README.md
deleted file mode 100644
index 92ea9d9830..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/LanguagePreference/README.md
+++ /dev/null
@@ -1,16 +0,0 @@
-[**@eslint-react/kit**](../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../README.md) / LanguagePreference
-
-# LanguagePreference
-
-## Variables
-
-- [defaultLanguagePreference](variables/defaultLanguagePreference.md)
-
-## Functions
-
-- [getFromContext](functions/getFromContext.md)
-- [make](functions/make.md)
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/LanguagePreference/functions/getFromContext.md b/packages/utilities/kit/docs/@eslint-react/namespaces/LanguagePreference/functions/getFromContext.md
deleted file mode 100644
index 995323be65..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/LanguagePreference/functions/getFromContext.md
+++ /dev/null
@@ -1,13 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [LanguagePreference](../README.md) / getFromContext
-
-# Function: getFromContext()
-
-> **getFromContext**(): `void`
-
-## Returns
-
-`void`
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/LanguagePreference/functions/make.md b/packages/utilities/kit/docs/@eslint-react/namespaces/LanguagePreference/functions/make.md
deleted file mode 100644
index 7bc3072a73..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/LanguagePreference/functions/make.md
+++ /dev/null
@@ -1,39 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [LanguagePreference](../README.md) / make
-
-# Function: make()
-
-> **make**(): `object`
-
-Get a copy of the default LanguagePreference.
-
-## Returns
-
-`object`
-
-### indentStyle?
-
-> `optional` **indentStyle**: `"tab"` \| `"space"`
-
-### indentWidth?
-
-> `optional` **indentWidth**: `number`
-
-### jsxQuoteStyle?
-
-> `optional` **jsxQuoteStyle**: `"single"` \| `"double"`
-
-### quoteStyle?
-
-> `optional` **quoteStyle**: `"single"` \| `"double"`
-
-### semicolons?
-
-> `optional` **semicolons**: `"always"` \| `"asNeeded"`
-
-### trailingCommas?
-
-> `optional` **trailingCommas**: `"all"` \| `"es5"` \| `"none"`
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/LanguagePreference/variables/defaultLanguagePreference.md b/packages/utilities/kit/docs/@eslint-react/namespaces/LanguagePreference/variables/defaultLanguagePreference.md
deleted file mode 100644
index dfccec4907..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/LanguagePreference/variables/defaultLanguagePreference.md
+++ /dev/null
@@ -1,37 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [LanguagePreference](../README.md) / defaultLanguagePreference
-
-# Variable: defaultLanguagePreference
-
-> `const` **defaultLanguagePreference**: `object`
-
-A default LanguagePreference object.
-
-## Type Declaration
-
-### indentStyle?
-
-> `optional` **indentStyle**: `"tab"` \| `"space"`
-
-### indentWidth?
-
-> `optional` **indentWidth**: `number`
-
-### jsxQuoteStyle?
-
-> `optional` **jsxQuoteStyle**: `"single"` \| `"double"`
-
-### quoteStyle?
-
-> `optional` **quoteStyle**: `"single"` \| `"double"`
-
-### semicolons?
-
-> `optional` **semicolons**: `"always"` \| `"asNeeded"`
-
-### trailingCommas?
-
-> `optional` **trailingCommas**: `"all"` \| `"es5"` \| `"none"`
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/README.md b/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/README.md
deleted file mode 100644
index a2021e4766..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/README.md
+++ /dev/null
@@ -1,33 +0,0 @@
-[**@eslint-react/kit**](../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../README.md) / RegExp
-
-# RegExp
-
-## Variables
-
-- [ANNOTATION\_JSX](variables/ANNOTATION_JSX.md)
-- [ANNOTATION\_JSX\_FRAG](variables/ANNOTATION_JSX_FRAG.md)
-- [ANNOTATION\_JSX\_IMPORT\_SOURCE](variables/ANNOTATION_JSX_IMPORT_SOURCE.md)
-- [ANNOTATION\_JSX\_RUNTIME](variables/ANNOTATION_JSX_RUNTIME.md)
-- [CAMEL\_CASE](variables/CAMEL_CASE.md)
-- [COMPONENT\_NAME](variables/COMPONENT_NAME.md)
-- [COMPONENT\_NAME\_LOOSE](variables/COMPONENT_NAME_LOOSE.md)
-- [CONSTANT\_CASE](variables/CONSTANT_CASE.md)
-- [HOOK\_NAME](variables/HOOK_NAME.md)
-- [HTML\_TAG](variables/HTML_TAG.md)
-- [JAVASCRIPT\_PROTOCOL](variables/JAVASCRIPT_PROTOCOL.md)
-- [JS\_EXT](variables/JS_EXT.md)
-- [JS\_IDENTIFIER](variables/JS_IDENTIFIER.md)
-- [KEBAB\_CASE](variables/KEBAB_CASE.md)
-- [PASCAL\_CASE](variables/PASCAL_CASE.md)
-- [REGEXP\_STR](variables/REGEXP_STR.md)
-- [SNAKE\_CASE](variables/SNAKE_CASE.md)
-- [TS\_EXT](variables/TS_EXT.md)
-
-## Functions
-
-- [isRegExp](functions/isRegExp.md)
-- [toRegExp](functions/toRegExp.md)
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/ANNOTATION_JSX.md b/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/ANNOTATION_JSX.md
deleted file mode 100644
index 5d6901a7d2..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/ANNOTATION_JSX.md
+++ /dev/null
@@ -1,11 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [RegExp](../README.md) / ANNOTATION\_JSX
-
-# Variable: ANNOTATION\_JSX
-
-> `const` **ANNOTATION\_JSX**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
-
-Regular expression for matching a `@jsx` annotation comment.
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/ANNOTATION_JSX_FRAG.md b/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/ANNOTATION_JSX_FRAG.md
deleted file mode 100644
index dc0ae0bf70..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/ANNOTATION_JSX_FRAG.md
+++ /dev/null
@@ -1,11 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [RegExp](../README.md) / ANNOTATION\_JSX\_FRAG
-
-# Variable: ANNOTATION\_JSX\_FRAG
-
-> `const` **ANNOTATION\_JSX\_FRAG**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
-
-Regular expression for matching a `@jsxFrag` annotation comment.
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/ANNOTATION_JSX_IMPORT_SOURCE.md b/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/ANNOTATION_JSX_IMPORT_SOURCE.md
deleted file mode 100644
index 79147ebb45..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/ANNOTATION_JSX_IMPORT_SOURCE.md
+++ /dev/null
@@ -1,11 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [RegExp](../README.md) / ANNOTATION\_JSX\_IMPORT\_SOURCE
-
-# Variable: ANNOTATION\_JSX\_IMPORT\_SOURCE
-
-> `const` **ANNOTATION\_JSX\_IMPORT\_SOURCE**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
-
-Regular expression for matching a `@jsxImportSource` annotation comment.
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/ANNOTATION_JSX_RUNTIME.md b/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/ANNOTATION_JSX_RUNTIME.md
deleted file mode 100644
index 3e9c39ab09..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/ANNOTATION_JSX_RUNTIME.md
+++ /dev/null
@@ -1,11 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [RegExp](../README.md) / ANNOTATION\_JSX\_RUNTIME
-
-# Variable: ANNOTATION\_JSX\_RUNTIME
-
-> `const` **ANNOTATION\_JSX\_RUNTIME**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
-
-Regular expression for matching a `@jsxRuntime` annotation comment.
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/CAMEL_CASE.md b/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/CAMEL_CASE.md
deleted file mode 100644
index 8418a11bc8..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/CAMEL_CASE.md
+++ /dev/null
@@ -1,11 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [RegExp](../README.md) / CAMEL\_CASE
-
-# Variable: CAMEL\_CASE
-
-> `const` **CAMEL\_CASE**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
-
-Regular expression for matching a camelCase string.
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/COMPONENT_NAME.md b/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/COMPONENT_NAME.md
deleted file mode 100644
index 58f2ad31ee..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/COMPONENT_NAME.md
+++ /dev/null
@@ -1,11 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [RegExp](../README.md) / COMPONENT\_NAME
-
-# Variable: COMPONENT\_NAME
-
-> `const` **COMPONENT\_NAME**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
-
-Regular expression for matching a React component name.
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/COMPONENT_NAME_LOOSE.md b/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/COMPONENT_NAME_LOOSE.md
deleted file mode 100644
index 1e6a68b115..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/COMPONENT_NAME_LOOSE.md
+++ /dev/null
@@ -1,11 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [RegExp](../README.md) / COMPONENT\_NAME\_LOOSE
-
-# Variable: COMPONENT\_NAME\_LOOSE
-
-> `const` **COMPONENT\_NAME\_LOOSE**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
-
-Regular expression for matching a React component name (loose).
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/CONSTANT_CASE.md b/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/CONSTANT_CASE.md
deleted file mode 100644
index aa181a9038..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/CONSTANT_CASE.md
+++ /dev/null
@@ -1,11 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [RegExp](../README.md) / CONSTANT\_CASE
-
-# Variable: CONSTANT\_CASE
-
-> `const` **CONSTANT\_CASE**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
-
-Regular expression for matching a CONSTANT_CASE string.
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/HOOK_NAME.md b/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/HOOK_NAME.md
deleted file mode 100644
index bc9e427269..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/HOOK_NAME.md
+++ /dev/null
@@ -1,11 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [RegExp](../README.md) / HOOK\_NAME
-
-# Variable: HOOK\_NAME
-
-> `const` **HOOK\_NAME**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
-
-Regular expression for matching a React Hook name.
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/HTML_TAG.md b/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/HTML_TAG.md
deleted file mode 100644
index 68a44d68b2..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/HTML_TAG.md
+++ /dev/null
@@ -1,11 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [RegExp](../README.md) / HTML\_TAG
-
-# Variable: HTML\_TAG
-
-> `const` **HTML\_TAG**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
-
-Regular expressions for matching a HTML tag name
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/JAVASCRIPT_PROTOCOL.md b/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/JAVASCRIPT_PROTOCOL.md
deleted file mode 100644
index 81ef3b3371..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/JAVASCRIPT_PROTOCOL.md
+++ /dev/null
@@ -1,9 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [RegExp](../README.md) / JAVASCRIPT\_PROTOCOL
-
-# Variable: JAVASCRIPT\_PROTOCOL
-
-> `const` **JAVASCRIPT\_PROTOCOL**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/JS_EXT.md b/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/JS_EXT.md
deleted file mode 100644
index 6447f265d5..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/JS_EXT.md
+++ /dev/null
@@ -1,11 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [RegExp](../README.md) / JS\_EXT
-
-# Variable: JS\_EXT
-
-> `const` **JS\_EXT**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
-
-Regular expression for matching a JavaScript file extension.
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/JS_IDENTIFIER.md b/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/JS_IDENTIFIER.md
deleted file mode 100644
index ee5e462f21..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/JS_IDENTIFIER.md
+++ /dev/null
@@ -1,11 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [RegExp](../README.md) / JS\_IDENTIFIER
-
-# Variable: JS\_IDENTIFIER
-
-> `const` **JS\_IDENTIFIER**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
-
-Regular expression for matching a valid JavaScript identifier.
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/KEBAB_CASE.md b/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/KEBAB_CASE.md
deleted file mode 100644
index de856deee9..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/KEBAB_CASE.md
+++ /dev/null
@@ -1,11 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [RegExp](../README.md) / KEBAB\_CASE
-
-# Variable: KEBAB\_CASE
-
-> `const` **KEBAB\_CASE**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
-
-Regular expression for matching a kebab-case string.
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/PASCAL_CASE.md b/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/PASCAL_CASE.md
deleted file mode 100644
index d99e369a63..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/PASCAL_CASE.md
+++ /dev/null
@@ -1,11 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [RegExp](../README.md) / PASCAL\_CASE
-
-# Variable: PASCAL\_CASE
-
-> `const` **PASCAL\_CASE**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
-
-Regular expression for matching a PascalCase string.
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/REGEXP_STR.md b/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/REGEXP_STR.md
deleted file mode 100644
index 2eecac023e..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/REGEXP_STR.md
+++ /dev/null
@@ -1,11 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [RegExp](../README.md) / REGEXP\_STR
-
-# Variable: REGEXP\_STR
-
-> `const` **REGEXP\_STR**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
-
-Regular expression for matching a RegExp string.
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/SNAKE_CASE.md b/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/SNAKE_CASE.md
deleted file mode 100644
index bdef79aad2..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/SNAKE_CASE.md
+++ /dev/null
@@ -1,11 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [RegExp](../README.md) / SNAKE\_CASE
-
-# Variable: SNAKE\_CASE
-
-> `const` **SNAKE\_CASE**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
-
-Regular expression for matching a snake_case string.
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/TS_EXT.md b/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/TS_EXT.md
deleted file mode 100644
index d6509bc5c7..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/variables/TS_EXT.md
+++ /dev/null
@@ -1,11 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [RegExp](../README.md) / TS\_EXT
-
-# Variable: TS\_EXT
-
-> `const` **TS\_EXT**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
-
-Regular expression for matching a TypeScript file extension.
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/Reporter/README.md b/packages/utilities/kit/docs/@eslint-react/namespaces/Reporter/README.md
deleted file mode 100644
index a026563561..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/Reporter/README.md
+++ /dev/null
@@ -1,20 +0,0 @@
-[**@eslint-react/kit**](../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../README.md) / Reporter
-
-# Reporter
-
-## Interfaces
-
-- [Reporter](interfaces/Reporter.md)
-
-## Variables
-
-- [send](variables/send.md)
-- [sendOrElse](variables/sendOrElse.md)
-
-## Functions
-
-- [make](functions/make.md)
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/Reporter/functions/make.md b/packages/utilities/kit/docs/@eslint-react/namespaces/Reporter/functions/make.md
deleted file mode 100644
index b1cf6a4845..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/Reporter/functions/make.md
+++ /dev/null
@@ -1,25 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [Reporter](../README.md) / make
-
-# Function: make()
-
-> **make**\<`TMessageID`\>(`context`): [`Reporter`](../interfaces/Reporter.md)\<`TMessageID`\>
-
-## Type Parameters
-
-### TMessageID
-
-`TMessageID` *extends* `string`
-
-## Parameters
-
-### context
-
-[`RuleContext`](../../../../type-aliases/RuleContext.md)
-
-## Returns
-
-[`Reporter`](../interfaces/Reporter.md)\<`TMessageID`\>
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/Reporter/interfaces/Reporter.md b/packages/utilities/kit/docs/@eslint-react/namespaces/Reporter/interfaces/Reporter.md
deleted file mode 100644
index 2d55bbe729..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/Reporter/interfaces/Reporter.md
+++ /dev/null
@@ -1,55 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [Reporter](../README.md) / Reporter
-
-# Interface: Reporter\
-
-## Type Parameters
-
-### TMessageID
-
-`TMessageID` *extends* `string`
-
-## Properties
-
-### send()
-
-> **send**: (`descriptor`) => `void`
-
-#### Parameters
-
-##### descriptor
-
-`undefined` | `null` | `ReportDescriptor`\<`TMessageID`\>
-
-#### Returns
-
-`void`
-
-***
-
-### sendOrElse()
-
-> **sendOrElse**: \<`TElse`\>(`descriptor`, `cb`) => `undefined` \| `TElse`
-
-#### Type Parameters
-
-##### TElse
-
-`TElse`
-
-#### Parameters
-
-##### descriptor
-
-`undefined` | `null` | `ReportDescriptor`\<`TMessageID`\>
-
-##### cb
-
-() => `TElse`
-
-#### Returns
-
-`undefined` \| `TElse`
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/Reporter/variables/send.md b/packages/utilities/kit/docs/@eslint-react/namespaces/Reporter/variables/send.md
deleted file mode 100644
index e4174a2375..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/Reporter/variables/send.md
+++ /dev/null
@@ -1,63 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [Reporter](../README.md) / send
-
-# Variable: send()
-
-> `const` **send**: \{\<`TMessageID`\>(`context`, `descriptor`): `void`; \<`TMessageID`\>(`context`): (`descriptor`) => `void`; \}
-
-## Call Signature
-
-> \<`TMessageID`\>(`context`, `descriptor`): `void`
-
-### Type Parameters
-
-#### TMessageID
-
-`TMessageID` *extends* `string`
-
-### Parameters
-
-#### context
-
-[`RuleContext`](../../../../type-aliases/RuleContext.md)
-
-#### descriptor
-
-`undefined` | `null` | `ReportDescriptor`\<`TMessageID`\>
-
-### Returns
-
-`void`
-
-## Call Signature
-
-> \<`TMessageID`\>(`context`): (`descriptor`) => `void`
-
-### Type Parameters
-
-#### TMessageID
-
-`TMessageID` *extends* `string`
-
-### Parameters
-
-#### context
-
-[`RuleContext`](../../../../type-aliases/RuleContext.md)
-
-### Returns
-
-> (`descriptor`): `void`
-
-#### Parameters
-
-##### descriptor
-
-`undefined` | `null` | `ReportDescriptor`\<`TMessageID`\>
-
-#### Returns
-
-`void`
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/Reporter/variables/sendOrElse.md b/packages/utilities/kit/docs/@eslint-react/namespaces/Reporter/variables/sendOrElse.md
deleted file mode 100644
index 80172bf12a..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/Reporter/variables/sendOrElse.md
+++ /dev/null
@@ -1,85 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [Reporter](../README.md) / sendOrElse
-
-# Variable: sendOrElse()
-
-> `const` **sendOrElse**: \{\<`TMessageID`, `TElse`\>(`context`, `descriptor`, `cb`): `undefined` \| `TElse`; \<`TMessageID`, `TElse`\>(`context`): (`descriptor`) => (`cb`) => `undefined` \| `TElse`; \}
-
-## Call Signature
-
-> \<`TMessageID`, `TElse`\>(`context`, `descriptor`, `cb`): `undefined` \| `TElse`
-
-### Type Parameters
-
-#### TMessageID
-
-`TMessageID` *extends* `string`
-
-#### TElse
-
-`TElse`
-
-### Parameters
-
-#### context
-
-[`RuleContext`](../../../../type-aliases/RuleContext.md)
-
-#### descriptor
-
-`undefined` | `null` | `ReportDescriptor`\<`TMessageID`\>
-
-#### cb
-
-() => `TElse`
-
-### Returns
-
-`undefined` \| `TElse`
-
-## Call Signature
-
-> \<`TMessageID`, `TElse`\>(`context`): (`descriptor`) => (`cb`) => `undefined` \| `TElse`
-
-### Type Parameters
-
-#### TMessageID
-
-`TMessageID` *extends* `string`
-
-#### TElse
-
-`TElse`
-
-### Parameters
-
-#### context
-
-[`RuleContext`](../../../../type-aliases/RuleContext.md)
-
-### Returns
-
-> (`descriptor`): (`cb`) => `undefined` \| `TElse`
-
-#### Parameters
-
-##### descriptor
-
-`undefined` | `null` | `ReportDescriptor`\<`TMessageID`\>
-
-#### Returns
-
-> (`cb`): `undefined` \| `TElse`
-
-##### Parameters
-
-###### cb
-
-() => `TElse`
-
-##### Returns
-
-`undefined` \| `TElse`
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/Selector/README.md b/packages/utilities/kit/docs/@eslint-react/namespaces/Selector/README.md
deleted file mode 100644
index e23d50aa23..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/Selector/README.md
+++ /dev/null
@@ -1,19 +0,0 @@
-[**@eslint-react/kit**](../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../README.md) / Selector
-
-# Selector
-
-## Type Aliases
-
-- [DisplayNameAssignmentExpression](type-aliases/DisplayNameAssignmentExpression.md)
-- [ImplicitReturnArrowFunctionExpression](type-aliases/ImplicitReturnArrowFunctionExpression.md)
-- [ObjectDestructuringVariableDeclarator](type-aliases/ObjectDestructuringVariableDeclarator.md)
-
-## Variables
-
-- [DISPLAY\_NAME\_ASSIGNMENT\_EXPRESSION](variables/DISPLAY_NAME_ASSIGNMENT_EXPRESSION.md)
-- [IMPLICIT\_RETURN\_ARROW\_FUNCTION\_EXPRESSION](variables/IMPLICIT_RETURN_ARROW_FUNCTION_EXPRESSION.md)
-- [OBJECT\_DESTRUCTURING\_VARIABLE\_DECLARATOR](variables/OBJECT_DESTRUCTURING_VARIABLE_DECLARATOR.md)
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/Selector/type-aliases/DisplayNameAssignmentExpression.md b/packages/utilities/kit/docs/@eslint-react/namespaces/Selector/type-aliases/DisplayNameAssignmentExpression.md
deleted file mode 100644
index 79320e15b7..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/Selector/type-aliases/DisplayNameAssignmentExpression.md
+++ /dev/null
@@ -1,39 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [Selector](../README.md) / DisplayNameAssignmentExpression
-
-# Type Alias: DisplayNameAssignmentExpression
-
-> **DisplayNameAssignmentExpression** = `TSESTree.AssignmentExpression` & `object`
-
-## Type Declaration
-
-### left
-
-> **left**: `TSESTree.MemberExpression` & `object`
-
-#### Type Declaration
-
-##### property
-
-> **property**: `TSESTree.Identifier` & `object`
-
-###### Type Declaration
-
-###### name
-
-> **name**: `"displayName"`
-
-### operator
-
-> **operator**: `"="`
-
-### right
-
-> **right**: `TSESTree.Literal`
-
-### type
-
-> **type**: `"AssignmentExpression"`
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/Selector/type-aliases/ImplicitReturnArrowFunctionExpression.md b/packages/utilities/kit/docs/@eslint-react/namespaces/Selector/type-aliases/ImplicitReturnArrowFunctionExpression.md
deleted file mode 100644
index bb83e7c766..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/Selector/type-aliases/ImplicitReturnArrowFunctionExpression.md
+++ /dev/null
@@ -1,15 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [Selector](../README.md) / ImplicitReturnArrowFunctionExpression
-
-# Type Alias: ImplicitReturnArrowFunctionExpression
-
-> **ImplicitReturnArrowFunctionExpression** = `TSESTree.ArrowFunctionExpression` & `object`
-
-## Type Declaration
-
-### body
-
-> **body**: `TSESTree.Expression`
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/Selector/type-aliases/ObjectDestructuringVariableDeclarator.md b/packages/utilities/kit/docs/@eslint-react/namespaces/Selector/type-aliases/ObjectDestructuringVariableDeclarator.md
deleted file mode 100644
index 4f2afc50ff..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/Selector/type-aliases/ObjectDestructuringVariableDeclarator.md
+++ /dev/null
@@ -1,19 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [Selector](../README.md) / ObjectDestructuringVariableDeclarator
-
-# Type Alias: ObjectDestructuringVariableDeclarator
-
-> **ObjectDestructuringVariableDeclarator** = `TSESTree.VariableDeclarator` & `object`
-
-## Type Declaration
-
-### id
-
-> **id**: `TSESTree.ObjectPattern`
-
-### init
-
-> **init**: `TSESTree.Identifier`
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/Selector/variables/DISPLAY_NAME_ASSIGNMENT_EXPRESSION.md b/packages/utilities/kit/docs/@eslint-react/namespaces/Selector/variables/DISPLAY_NAME_ASSIGNMENT_EXPRESSION.md
deleted file mode 100644
index 82ca82fad0..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/Selector/variables/DISPLAY_NAME_ASSIGNMENT_EXPRESSION.md
+++ /dev/null
@@ -1,9 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [Selector](../README.md) / DISPLAY\_NAME\_ASSIGNMENT\_EXPRESSION
-
-# Variable: DISPLAY\_NAME\_ASSIGNMENT\_EXPRESSION
-
-> `const` **DISPLAY\_NAME\_ASSIGNMENT\_EXPRESSION**: `string`
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/Selector/variables/IMPLICIT_RETURN_ARROW_FUNCTION_EXPRESSION.md b/packages/utilities/kit/docs/@eslint-react/namespaces/Selector/variables/IMPLICIT_RETURN_ARROW_FUNCTION_EXPRESSION.md
deleted file mode 100644
index 10fc00bb34..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/Selector/variables/IMPLICIT_RETURN_ARROW_FUNCTION_EXPRESSION.md
+++ /dev/null
@@ -1,9 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [Selector](../README.md) / IMPLICIT\_RETURN\_ARROW\_FUNCTION\_EXPRESSION
-
-# Variable: IMPLICIT\_RETURN\_ARROW\_FUNCTION\_EXPRESSION
-
-> `const` **IMPLICIT\_RETURN\_ARROW\_FUNCTION\_EXPRESSION**: `"ArrowFunctionExpression[body.type!='BlockStatement']"` = `"ArrowFunctionExpression[body.type!='BlockStatement']"`
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/Selector/variables/OBJECT_DESTRUCTURING_VARIABLE_DECLARATOR.md b/packages/utilities/kit/docs/@eslint-react/namespaces/Selector/variables/OBJECT_DESTRUCTURING_VARIABLE_DECLARATOR.md
deleted file mode 100644
index 646ae21a63..0000000000
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/Selector/variables/OBJECT_DESTRUCTURING_VARIABLE_DECLARATOR.md
+++ /dev/null
@@ -1,9 +0,0 @@
-[**@eslint-react/kit**](../../../../README.md)
-
-***
-
-[@eslint-react/kit](../../../../README.md) / [Selector](../README.md) / OBJECT\_DESTRUCTURING\_VARIABLE\_DECLARATOR
-
-# Variable: OBJECT\_DESTRUCTURING\_VARIABLE\_DECLARATOR
-
-> `const` **OBJECT\_DESTRUCTURING\_VARIABLE\_DECLARATOR**: `string`
diff --git a/packages/utilities/kit/docs/README.md b/packages/utilities/kit/docs/README.md
index 7791519a53..5114058013 100644
--- a/packages/utilities/kit/docs/README.md
+++ b/packages/utilities/kit/docs/README.md
@@ -4,14 +4,6 @@
# @eslint-react/kit
-## Namespaces
-
-- [JsxConfig](@eslint-react/namespaces/JsxConfig/README.md)
-- [LanguagePreference](@eslint-react/namespaces/LanguagePreference/README.md)
-- [RegExp](@eslint-react/namespaces/RegExp/README.md)
-- [Reporter](@eslint-react/namespaces/Reporter/README.md)
-- [Selector](@eslint-react/namespaces/Selector/README.md)
-
## Interfaces
- [CompatibleConfig](interfaces/CompatibleConfig.md)
@@ -28,3 +20,30 @@
- [Severity](type-aliases/Severity.md)
- [SeverityLevel](type-aliases/SeverityLevel.md)
- [SeverityName](type-aliases/SeverityName.md)
+
+## Variables
+
+- [RE\_ANNOTATION\_JSX](variables/RE_ANNOTATION_JSX.md)
+- [RE\_ANNOTATION\_JSX\_FRAG](variables/RE_ANNOTATION_JSX_FRAG.md)
+- [RE\_ANNOTATION\_JSX\_IMPORT\_SOURCE](variables/RE_ANNOTATION_JSX_IMPORT_SOURCE.md)
+- [RE\_ANNOTATION\_JSX\_RUNTIME](variables/RE_ANNOTATION_JSX_RUNTIME.md)
+- [RE\_CAMEL\_CASE](variables/RE_CAMEL_CASE.md)
+- [RE\_COMPONENT\_NAME](variables/RE_COMPONENT_NAME.md)
+- [RE\_COMPONENT\_NAME\_LOOSE](variables/RE_COMPONENT_NAME_LOOSE.md)
+- [RE\_CONSTANT\_CASE](variables/RE_CONSTANT_CASE.md)
+- [RE\_HOOK\_NAME](variables/RE_HOOK_NAME.md)
+- [RE\_HTML\_TAG](variables/RE_HTML_TAG.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\_KEBAB\_CASE](variables/RE_KEBAB_CASE.md)
+- [RE\_PASCAL\_CASE](variables/RE_PASCAL_CASE.md)
+- [RE\_REGEXP\_STR](variables/RE_REGEXP_STR.md)
+- [RE\_SNAKE\_CASE](variables/RE_SNAKE_CASE.md)
+- [RE\_TS\_EXT](variables/RE_TS_EXT.md)
+
+## Functions
+
+- [isRegExp](functions/isRegExp.md)
+- [report](functions/report.md)
+- [toRegExp](functions/toRegExp.md)
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/functions/isRegExp.md b/packages/utilities/kit/docs/functions/isRegExp.md
similarity index 61%
rename from packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/functions/isRegExp.md
rename to packages/utilities/kit/docs/functions/isRegExp.md
index fbf670e3c9..7de03f128c 100644
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/functions/isRegExp.md
+++ b/packages/utilities/kit/docs/functions/isRegExp.md
@@ -1,8 +1,8 @@
-[**@eslint-react/kit**](../../../../README.md)
+[**@eslint-react/kit**](../README.md)
***
-[@eslint-react/kit](../../../../README.md) / [RegExp](../README.md) / isRegExp
+[@eslint-react/kit](../README.md) / isRegExp
# Function: isRegExp()
diff --git a/packages/utilities/kit/docs/functions/report.md b/packages/utilities/kit/docs/functions/report.md
new file mode 100644
index 0000000000..01bf34ce62
--- /dev/null
+++ b/packages/utilities/kit/docs/functions/report.md
@@ -0,0 +1,29 @@
+[**@eslint-react/kit**](../README.md)
+
+***
+
+[@eslint-react/kit](../README.md) / report
+
+# Function: report()
+
+> **report**(`context`): (`descriptor`) => `void`
+
+## Parameters
+
+### context
+
+[`RuleContext`](../type-aliases/RuleContext.md)
+
+## Returns
+
+> (`descriptor`): `void`
+
+### Parameters
+
+#### descriptor
+
+`undefined` | `null` | `ReportDescriptor`\<`string`\>
+
+### Returns
+
+`void`
diff --git a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/functions/toRegExp.md b/packages/utilities/kit/docs/functions/toRegExp.md
similarity index 81%
rename from packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/functions/toRegExp.md
rename to packages/utilities/kit/docs/functions/toRegExp.md
index a5d6c68ad0..cbff14dab2 100644
--- a/packages/utilities/kit/docs/@eslint-react/namespaces/RegExp/functions/toRegExp.md
+++ b/packages/utilities/kit/docs/functions/toRegExp.md
@@ -1,8 +1,8 @@
-[**@eslint-react/kit**](../../../../README.md)
+[**@eslint-react/kit**](../README.md)
***
-[@eslint-react/kit](../../../../README.md) / [RegExp](../README.md) / toRegExp
+[@eslint-react/kit](../README.md) / toRegExp
# Function: toRegExp()
diff --git a/packages/utilities/kit/docs/variables/RE_ANNOTATION_JSX.md b/packages/utilities/kit/docs/variables/RE_ANNOTATION_JSX.md
new file mode 100644
index 0000000000..15b9ab2014
--- /dev/null
+++ b/packages/utilities/kit/docs/variables/RE_ANNOTATION_JSX.md
@@ -0,0 +1,11 @@
+[**@eslint-react/kit**](../README.md)
+
+***
+
+[@eslint-react/kit](../README.md) / RE\_ANNOTATION\_JSX
+
+# Variable: RE\_ANNOTATION\_JSX
+
+> `const` **RE\_ANNOTATION\_JSX**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
+
+Regular expression for matching a `@jsx` annotation comment.
diff --git a/packages/utilities/kit/docs/variables/RE_ANNOTATION_JSX_FRAG.md b/packages/utilities/kit/docs/variables/RE_ANNOTATION_JSX_FRAG.md
new file mode 100644
index 0000000000..025f9f2b64
--- /dev/null
+++ b/packages/utilities/kit/docs/variables/RE_ANNOTATION_JSX_FRAG.md
@@ -0,0 +1,11 @@
+[**@eslint-react/kit**](../README.md)
+
+***
+
+[@eslint-react/kit](../README.md) / RE\_ANNOTATION\_JSX\_FRAG
+
+# Variable: RE\_ANNOTATION\_JSX\_FRAG
+
+> `const` **RE\_ANNOTATION\_JSX\_FRAG**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
+
+Regular expression for matching a `@jsxFrag` annotation comment.
diff --git a/packages/utilities/kit/docs/variables/RE_ANNOTATION_JSX_IMPORT_SOURCE.md b/packages/utilities/kit/docs/variables/RE_ANNOTATION_JSX_IMPORT_SOURCE.md
new file mode 100644
index 0000000000..b23b7838f3
--- /dev/null
+++ b/packages/utilities/kit/docs/variables/RE_ANNOTATION_JSX_IMPORT_SOURCE.md
@@ -0,0 +1,11 @@
+[**@eslint-react/kit**](../README.md)
+
+***
+
+[@eslint-react/kit](../README.md) / RE\_ANNOTATION\_JSX\_IMPORT\_SOURCE
+
+# Variable: RE\_ANNOTATION\_JSX\_IMPORT\_SOURCE
+
+> `const` **RE\_ANNOTATION\_JSX\_IMPORT\_SOURCE**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
+
+Regular expression for matching a `@jsxImportSource` annotation comment.
diff --git a/packages/utilities/kit/docs/variables/RE_ANNOTATION_JSX_RUNTIME.md b/packages/utilities/kit/docs/variables/RE_ANNOTATION_JSX_RUNTIME.md
new file mode 100644
index 0000000000..0b4ef3932b
--- /dev/null
+++ b/packages/utilities/kit/docs/variables/RE_ANNOTATION_JSX_RUNTIME.md
@@ -0,0 +1,11 @@
+[**@eslint-react/kit**](../README.md)
+
+***
+
+[@eslint-react/kit](../README.md) / RE\_ANNOTATION\_JSX\_RUNTIME
+
+# Variable: RE\_ANNOTATION\_JSX\_RUNTIME
+
+> `const` **RE\_ANNOTATION\_JSX\_RUNTIME**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
+
+Regular expression for matching a `@jsxRuntime` annotation comment.
diff --git a/packages/utilities/kit/docs/variables/RE_CAMEL_CASE.md b/packages/utilities/kit/docs/variables/RE_CAMEL_CASE.md
new file mode 100644
index 0000000000..cb3acf1729
--- /dev/null
+++ b/packages/utilities/kit/docs/variables/RE_CAMEL_CASE.md
@@ -0,0 +1,11 @@
+[**@eslint-react/kit**](../README.md)
+
+***
+
+[@eslint-react/kit](../README.md) / RE\_CAMEL\_CASE
+
+# Variable: RE\_CAMEL\_CASE
+
+> `const` **RE\_CAMEL\_CASE**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
+
+Regular expression for matching a camelCase string.
diff --git a/packages/utilities/kit/docs/variables/RE_COMPONENT_NAME.md b/packages/utilities/kit/docs/variables/RE_COMPONENT_NAME.md
new file mode 100644
index 0000000000..778107b92c
--- /dev/null
+++ b/packages/utilities/kit/docs/variables/RE_COMPONENT_NAME.md
@@ -0,0 +1,11 @@
+[**@eslint-react/kit**](../README.md)
+
+***
+
+[@eslint-react/kit](../README.md) / RE\_COMPONENT\_NAME
+
+# Variable: RE\_COMPONENT\_NAME
+
+> `const` **RE\_COMPONENT\_NAME**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
+
+Regular expression for matching a React component name.
diff --git a/packages/utilities/kit/docs/variables/RE_COMPONENT_NAME_LOOSE.md b/packages/utilities/kit/docs/variables/RE_COMPONENT_NAME_LOOSE.md
new file mode 100644
index 0000000000..4b9b2150fe
--- /dev/null
+++ b/packages/utilities/kit/docs/variables/RE_COMPONENT_NAME_LOOSE.md
@@ -0,0 +1,11 @@
+[**@eslint-react/kit**](../README.md)
+
+***
+
+[@eslint-react/kit](../README.md) / RE\_COMPONENT\_NAME\_LOOSE
+
+# Variable: RE\_COMPONENT\_NAME\_LOOSE
+
+> `const` **RE\_COMPONENT\_NAME\_LOOSE**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
+
+Regular expression for matching a React component name (loose).
diff --git a/packages/utilities/kit/docs/variables/RE_CONSTANT_CASE.md b/packages/utilities/kit/docs/variables/RE_CONSTANT_CASE.md
new file mode 100644
index 0000000000..5b0c155286
--- /dev/null
+++ b/packages/utilities/kit/docs/variables/RE_CONSTANT_CASE.md
@@ -0,0 +1,11 @@
+[**@eslint-react/kit**](../README.md)
+
+***
+
+[@eslint-react/kit](../README.md) / RE\_CONSTANT\_CASE
+
+# Variable: RE\_CONSTANT\_CASE
+
+> `const` **RE\_CONSTANT\_CASE**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
+
+Regular expression for matching a CONSTANT_CASE string.
diff --git a/packages/utilities/kit/docs/variables/RE_HOOK_NAME.md b/packages/utilities/kit/docs/variables/RE_HOOK_NAME.md
new file mode 100644
index 0000000000..d930fbcfa3
--- /dev/null
+++ b/packages/utilities/kit/docs/variables/RE_HOOK_NAME.md
@@ -0,0 +1,11 @@
+[**@eslint-react/kit**](../README.md)
+
+***
+
+[@eslint-react/kit](../README.md) / RE\_HOOK\_NAME
+
+# Variable: RE\_HOOK\_NAME
+
+> `const` **RE\_HOOK\_NAME**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
+
+Regular expression for matching a React Hook name.
diff --git a/packages/utilities/kit/docs/variables/RE_HTML_TAG.md b/packages/utilities/kit/docs/variables/RE_HTML_TAG.md
new file mode 100644
index 0000000000..bc5e09121c
--- /dev/null
+++ b/packages/utilities/kit/docs/variables/RE_HTML_TAG.md
@@ -0,0 +1,11 @@
+[**@eslint-react/kit**](../README.md)
+
+***
+
+[@eslint-react/kit](../README.md) / RE\_HTML\_TAG
+
+# Variable: RE\_HTML\_TAG
+
+> `const` **RE\_HTML\_TAG**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
+
+Regular expressions for matching a HTML tag name
diff --git a/packages/utilities/kit/docs/variables/RE_JAVASCRIPT_PROTOCOL.md b/packages/utilities/kit/docs/variables/RE_JAVASCRIPT_PROTOCOL.md
new file mode 100644
index 0000000000..002bf1c166
--- /dev/null
+++ b/packages/utilities/kit/docs/variables/RE_JAVASCRIPT_PROTOCOL.md
@@ -0,0 +1,9 @@
+[**@eslint-react/kit**](../README.md)
+
+***
+
+[@eslint-react/kit](../README.md) / RE\_JAVASCRIPT\_PROTOCOL
+
+# Variable: RE\_JAVASCRIPT\_PROTOCOL
+
+> `const` **RE\_JAVASCRIPT\_PROTOCOL**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
diff --git a/packages/utilities/kit/docs/variables/RE_JS_EXT.md b/packages/utilities/kit/docs/variables/RE_JS_EXT.md
new file mode 100644
index 0000000000..6c317f66e3
--- /dev/null
+++ b/packages/utilities/kit/docs/variables/RE_JS_EXT.md
@@ -0,0 +1,11 @@
+[**@eslint-react/kit**](../README.md)
+
+***
+
+[@eslint-react/kit](../README.md) / RE\_JS\_EXT
+
+# Variable: RE\_JS\_EXT
+
+> `const` **RE\_JS\_EXT**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
+
+Regular expression for matching a JavaScript file extension.
diff --git a/packages/utilities/kit/docs/variables/RE_JS_IDENTIFIER.md b/packages/utilities/kit/docs/variables/RE_JS_IDENTIFIER.md
new file mode 100644
index 0000000000..d1a918a72d
--- /dev/null
+++ b/packages/utilities/kit/docs/variables/RE_JS_IDENTIFIER.md
@@ -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.
diff --git a/packages/utilities/kit/docs/variables/RE_KEBAB_CASE.md b/packages/utilities/kit/docs/variables/RE_KEBAB_CASE.md
new file mode 100644
index 0000000000..8d38a52b46
--- /dev/null
+++ b/packages/utilities/kit/docs/variables/RE_KEBAB_CASE.md
@@ -0,0 +1,11 @@
+[**@eslint-react/kit**](../README.md)
+
+***
+
+[@eslint-react/kit](../README.md) / RE\_KEBAB\_CASE
+
+# Variable: RE\_KEBAB\_CASE
+
+> `const` **RE\_KEBAB\_CASE**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
+
+Regular expression for matching a kebab-case string.
diff --git a/packages/utilities/kit/docs/variables/RE_PASCAL_CASE.md b/packages/utilities/kit/docs/variables/RE_PASCAL_CASE.md
new file mode 100644
index 0000000000..5cd6b3dfa3
--- /dev/null
+++ b/packages/utilities/kit/docs/variables/RE_PASCAL_CASE.md
@@ -0,0 +1,11 @@
+[**@eslint-react/kit**](../README.md)
+
+***
+
+[@eslint-react/kit](../README.md) / RE\_PASCAL\_CASE
+
+# Variable: RE\_PASCAL\_CASE
+
+> `const` **RE\_PASCAL\_CASE**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
+
+Regular expression for matching a PascalCase string.
diff --git a/packages/utilities/kit/docs/variables/RE_REGEXP_STR.md b/packages/utilities/kit/docs/variables/RE_REGEXP_STR.md
new file mode 100644
index 0000000000..566f76236c
--- /dev/null
+++ b/packages/utilities/kit/docs/variables/RE_REGEXP_STR.md
@@ -0,0 +1,11 @@
+[**@eslint-react/kit**](../README.md)
+
+***
+
+[@eslint-react/kit](../README.md) / RE\_REGEXP\_STR
+
+# Variable: RE\_REGEXP\_STR
+
+> `const` **RE\_REGEXP\_STR**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
+
+Regular expression for matching a RegExp string.
diff --git a/packages/utilities/kit/docs/variables/RE_SNAKE_CASE.md b/packages/utilities/kit/docs/variables/RE_SNAKE_CASE.md
new file mode 100644
index 0000000000..9091c48f20
--- /dev/null
+++ b/packages/utilities/kit/docs/variables/RE_SNAKE_CASE.md
@@ -0,0 +1,11 @@
+[**@eslint-react/kit**](../README.md)
+
+***
+
+[@eslint-react/kit](../README.md) / RE\_SNAKE\_CASE
+
+# Variable: RE\_SNAKE\_CASE
+
+> `const` **RE\_SNAKE\_CASE**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
+
+Regular expression for matching a snake_case string.
diff --git a/packages/utilities/kit/docs/variables/RE_TS_EXT.md b/packages/utilities/kit/docs/variables/RE_TS_EXT.md
new file mode 100644
index 0000000000..ea61b2074b
--- /dev/null
+++ b/packages/utilities/kit/docs/variables/RE_TS_EXT.md
@@ -0,0 +1,11 @@
+[**@eslint-react/kit**](../README.md)
+
+***
+
+[@eslint-react/kit](../README.md) / RE\_TS\_EXT
+
+# Variable: RE\_TS\_EXT
+
+> `const` **RE\_TS\_EXT**: [`RegExp`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
+
+Regular expression for matching a TypeScript file extension.
diff --git a/packages/utilities/kit/package.json b/packages/utilities/kit/package.json
index 640963512c..0bf7045947 100644
--- a/packages/utilities/kit/package.json
+++ b/packages/utilities/kit/package.json
@@ -1,6 +1,6 @@
{
"name": "@eslint-react/kit",
- "version": "1.53.2-beta.1",
+ "version": "2.0.0-beta.194",
"description": "ESLint React's plugin kit for building plugins and rules.",
"homepage": "https://github.com/Rel1cx/eslint-react",
"bugs": {
@@ -14,28 +14,20 @@
"license": "MIT",
"author": "Rel1cx",
"sideEffects": false,
+ "type": "module",
"exports": {
".": {
- "import": {
- "types": "./dist/index.d.mts",
- "default": "./dist/index.mjs"
- },
- "require": {
- "types": "./dist/index.d.ts",
- "default": "./dist/index.js"
- }
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.js"
},
"./package.json": "./package.json"
},
- "main": "dist/index.js",
- "module": "dist/index.mjs",
- "types": "dist/index.d.ts",
"files": [
"dist",
"./package.json"
],
"scripts": {
- "build": "tsdown",
+ "build": "tsdown --dts-resolve",
"build:docs": "typedoc",
"lint:publish": "publint",
"lint:ts": "tsc --noEmit",
@@ -43,17 +35,17 @@
},
"dependencies": {
"@eslint-react/eff": "workspace:*",
- "@typescript-eslint/utils": "^8.44.0",
+ "@typescript-eslint/utils": "^8.44.1",
"ts-pattern": "^5.8.0",
- "zod": "^4.1.9"
+ "zod": "^4.1.11"
},
"devDependencies": {
"@local/configs": "workspace:*",
"@tsconfig/node22": "^22.0.2",
- "tsdown": "^0.15.3",
+ "tsdown": "^0.15.4",
"type-fest": "^5.0.1"
},
"engines": {
- "node": ">=18.18.0"
+ "node": ">=20.19.0"
}
}
diff --git a/packages/utilities/kit/src/JsxConfig/index.ts b/packages/utilities/kit/src/JsxConfig/index.ts
deleted file mode 100644
index 039fc31338..0000000000
--- a/packages/utilities/kit/src/JsxConfig/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from "./JsxConfig";
diff --git a/packages/utilities/kit/src/LanguagePreference/LanguagePreference.ts b/packages/utilities/kit/src/LanguagePreference/LanguagePreference.ts
deleted file mode 100644
index 394720901a..0000000000
--- a/packages/utilities/kit/src/LanguagePreference/LanguagePreference.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import type { SharedConfigurationSettings } from "@typescript-eslint/utils/ts-eslint"; // eslint-disable-line @typescript-eslint/no-unused-vars
-import type { z } from "zod/v4";
-import type { LanguagePreferenceSchema } from "./LanguagePreferenceSchema";
-
-/**
- * @internal
- */
-export type LanguagePreference = z.infer;
-
-/**
- * Get a copy of the default LanguagePreference.
- */
-export function make(): LanguagePreference {
- return {
- indentStyle: "space",
- indentWidth: 2,
- jsxQuoteStyle: "double",
- quoteStyle: "single",
- semicolons: "always",
- trailingCommas: "all",
- };
-}
-
-/**
- * A default LanguagePreference object.
- */
-export const defaultLanguagePreference = make();
-
-export function getFromContext() {
- throw new Error("getFromContext is not implemented");
-}
-
-declare module "@typescript-eslint/utils/ts-eslint" {
- export interface SharedConfigurationSettings {
- // TODO: Add the language preference to the shared configuration settings when it is ready.
- // languagePreference?: Partial;
- }
-}
diff --git a/packages/utilities/kit/src/LanguagePreference/LanguagePreferenceSchema.ts b/packages/utilities/kit/src/LanguagePreference/LanguagePreferenceSchema.ts
deleted file mode 100644
index 301ac43419..0000000000
--- a/packages/utilities/kit/src/LanguagePreference/LanguagePreferenceSchema.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import { z } from "zod/v4";
-
-/**
- * @internal
- */
-export const LanguagePreferenceSchema = z.object({
- indentStyle: z.optional(
- z.union([
- z.literal("tab"),
- z.literal("space"),
- ]),
- ),
- indentWidth: z.optional(z.number()),
- quoteStyle: z.optional(
- z.union([
- z.literal("single"),
- z.literal("double"),
- ]),
- ),
- semicolons: z.optional(
- z.union([
- z.literal("always"),
- z.literal("asNeeded"),
- ]),
- ),
- trailingCommas: z.optional(
- z.union([
- z.literal("all"),
- z.literal("es5"),
- z.literal("none"),
- ]),
- ),
-
- // JSX specific options
- jsxQuoteStyle: z.optional(
- z.union([
- z.literal("single"),
- z.literal("double"),
- ]),
- ),
-}, {});
diff --git a/packages/utilities/kit/src/LanguagePreference/index.ts b/packages/utilities/kit/src/LanguagePreference/index.ts
deleted file mode 100644
index 4d1da9f67c..0000000000
--- a/packages/utilities/kit/src/LanguagePreference/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from "./LanguagePreference";
-export * from "./LanguagePreferenceSchema";
diff --git a/packages/utilities/kit/src/RegExp.ts b/packages/utilities/kit/src/RegExp.ts
deleted file mode 100644
index 827af921b1..0000000000
--- a/packages/utilities/kit/src/RegExp.ts
+++ /dev/null
@@ -1,112 +0,0 @@
-/**
- * Regular expressions for matching a HTML tag name
- */
-export const HTML_TAG = /^[a-z][^-]*$/u;
-
-/**
- * Regular expression for matching a TypeScript file extension.
- */
-export const TS_EXT = /^[cm]?tsx?$/u;
-
-/**
- * Regular expression for matching a JavaScript file extension.
- */
-export const JS_EXT = /^[cm]?jsx?$/u;
-
-/**
- * Regular expression for matching a PascalCase string.
- */
-export const PASCAL_CASE = /^[A-Z][\dA-Za-z]*$/u;
-
-/**
- * Regular expression for matching a camelCase string.
- */
-export const CAMEL_CASE = /^[a-z][\dA-Za-z]*$/u;
-
-/**
- * Regular expression for matching a kebab-case string.
- */
-export const KEBAB_CASE = /^[a-z][\d\-a-z]*$/u;
-
-/**
- * Regular expression for matching a snake_case string.
- */
-export const SNAKE_CASE = /^[a-z][\d_a-z]*$/u;
-
-/**
- * Regular expression for matching a CONSTANT_CASE string.
- */
-export const CONSTANT_CASE = /^[A-Z][\d_A-Z]*$/u;
-
-// @see https://github.com/facebook/react/blob/6db7f4209e6f32ebde298a0b7451710dd6aa3e19/packages/react-dom-bindings/src/shared/sanitizeURL.js#L22
-// dprint-ignore
-// eslint-disable-next-line no-control-regex
-export const 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;
-
-/**
- * Regular expression for matching a valid JavaScript identifier.
- */
-export const JS_IDENTIFIER = /^[_$a-z][\w$]*$/i;
-
-/**
- * Regular expression for matching a RegExp string.
- */
-export const REGEXP_STR = /^\/(.+)\/([A-Za-z]*)$/u;
-
-/**
- * Regular expression for matching a `@jsx` annotation comment.
- */
-export const ANNOTATION_JSX = /@jsx\s+(\S+)/u;
-
-/**
- * Regular expression for matching a `@jsxFrag` annotation comment.
- */
-export const ANNOTATION_JSX_FRAG = /@jsxFrag\s+(\S+)/u;
-
-/**
- * Regular expression for matching a `@jsxRuntime` annotation comment.
- */
-export const ANNOTATION_JSX_RUNTIME = /@jsxRuntime\s+(\S+)/u;
-
-/**
- * Regular expression for matching a `@jsxImportSource` annotation comment.
- */
-export const ANNOTATION_JSX_IMPORT_SOURCE = /@jsxImportSource\s+(\S+)/u;
-
-/**
- * Regular expression for matching a React component name.
- */
-export const COMPONENT_NAME = /^[A-Z]/u;
-
-/**
- * Regular expression for matching a React component name (loose).
- */
-export const COMPONENT_NAME_LOOSE = /^_?[A-Z]/u;
-
-/**
- * Regular expression for matching a React Hook name.
- */
-export const HOOK_NAME = /^use/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`.
- */
-export function toRegExp(string: string): { test(s: string): boolean } {
- const [, pattern, flags = "u"] = REGEXP_STR.exec(string) ?? [];
- if (pattern != null) return new RegExp(pattern, flags);
- return { test: (s) => s === string };
-}
-
-/**
- * Checks whether given string is regexp string
- * @param string The string to check
- * @returns boolean
- */
-export function isRegExp(string: string): boolean {
- return REGEXP_STR.test(string);
-}
diff --git a/packages/utilities/kit/src/Reporter.ts b/packages/utilities/kit/src/Reporter.ts
deleted file mode 100644
index 4926349b12..0000000000
--- a/packages/utilities/kit/src/Reporter.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { dual, type unit } from "@eslint-react/eff";
-import type { ReportDescriptor } from "@typescript-eslint/utils/ts-eslint";
-import type { RuleContext } from "./types";
-
-export interface Reporter {
- send: (descriptor: unit | null | ReportDescriptor) => void;
- // dprint-ignore
- sendOrElse: (descriptor: unit | null | ReportDescriptor, cb: () => TElse) => unit | TElse;
-}
-
-export const send: {
- (context: RuleContext, descriptor: unit | null | ReportDescriptor): void;
- (context: RuleContext): (descriptor: unit | null | ReportDescriptor) => void;
-} = dual(2, (context: RuleContext, descriptor: unit | null | ReportDescriptor