From bc9b86e0de83be4c3131c3734eb9c4a04e209172 Mon Sep 17 00:00:00 2001 From: Rel1cx Date: Mon, 14 Apr 2025 03:37:21 +0800 Subject: [PATCH] refactor: remove JSX package and move functionality to Core This commit moves all JSX-related functionality from the dedicated JSX utility package into the Core package. This simplifies the package structure while maintaining the same functionality. The changes include: - Move all JSX utilities (detection, attributes, elements) to Core - Update imports across packages to use JSX utilities from Core - Remove unused JSX utility package --- apps/website/content/docs/contributing.mdx | 6 -- apps/website/content/docs/faq.mdx | 1 - packages/core/docs/README.md | 19 ++++++ .../docs/functions/findParentAttribute.md | 31 ++++++++++ packages/core/docs/functions/getAttribute.md | 43 +++++++++++++ .../core/docs/functions/getAttributeName.md | 31 ++++++++++ .../core/docs/functions/getAttributeValue.md | 37 ++++++++++++ .../core/docs/functions/getElementType.md | 31 ++++++++++ .../core/docs/functions/hasAnyAttribute.md | 31 ++++++++++ packages/core/docs/functions/hasAttribute.md | 31 ++++++++++ .../core/docs/functions/hasEveryAttribute.md | 31 ++++++++++ .../core/docs/functions/isFragmentElement.md | 51 ++++++++++++++++ packages/core/docs/functions/isHostElement.md | 23 +++++++ packages/core/docs/functions/isJsxLike.md | 41 +++++++++++++ packages/core/docs/functions/isJsxText.md | 25 ++++++++ .../core/docs/functions/isKeyedElement.md | 27 +++++++++ packages/core/docs/functions/stringifyJsx.md | 25 ++++++++ .../docs/type-aliases/JSXDetectionHint.md | 9 +++ .../core/docs/type-aliases/TSESTreeJSX.md | 9 +++ .../docs/variables/ComponentDetectionHint.md | 2 +- .../variables/DEFAULT_JSX_DETECTION_HINT.md | 9 +++ .../core/docs/variables/JSXDetectionHint.md | 59 ++++++++++++++++++ packages/core/docs/variables/isJSX.md | 19 ++++++ packages/core/package.json | 1 - .../component/component-collector-legacy.ts | 2 +- .../core/src/component/component-collector.ts | 8 +-- .../src/component/component-definition.ts | 2 +- .../src/component/component-detection-hint.ts | 5 +- .../{hierarchy.ts => component-hierarchy.ts} | 2 +- .../src/component/{is.ts => component-is.ts} | 0 .../src/component/component-render-prop.ts | 12 ++-- .../core/src/component/component-render.ts | 2 +- .../src/component/component-semantic-node.ts | 2 +- packages/core/src/component/index.ts | 4 +- packages/core/src/hook/hook-collector.ts | 2 +- .../hook/{hierarchy.ts => hook-hierarchy.ts} | 2 +- packages/core/src/hook/{is.ts => hook-is.ts} | 0 packages/core/src/hook/hook-semantic-node.ts | 2 +- packages/core/src/hook/index.ts | 4 +- packages/core/src/index.ts | 4 +- packages/core/src/jsx/index.ts | 10 ++++ packages/core/src/jsx/jsx-attribute-name.ts | 13 ++++ .../src/jsx/jsx-attribute-value.ts} | 7 ++- .../src/jsx/jsx-attribute.ts} | 9 ++- .../src/jsx}/jsx-detection-hint.ts | 0 .../jsx/src => core/src/jsx}/jsx-detection.ts | 0 .../src/jsx/jsx-element-type.ts} | 8 ++- .../has.ts => core/src/jsx/jsx-has.ts} | 12 ++-- .../src/jsx/jsx-hierarchy.ts} | 0 .../element/is.ts => core/src/jsx/jsx-is.ts} | 27 +++++---- .../src/jsx/jsx-stringify.ts} | 8 +-- packages/core/src/semantic/index.ts | 2 + .../core/src/{ => semantic}/semantic-entry.ts | 2 +- .../core/src/{ => semantic}/semantic-node.ts | 0 .../eslint-plugin-react-debug/package.json | 1 - .../src/rules/jsx.ts | 12 ++-- .../eslint-plugin-react-dom/package.json | 1 - ...dangerously-set-innerhtml-with-children.ts | 6 +- .../src/rules/no-dangerously-set-innerhtml.ts | 5 +- .../src/rules/no-missing-button-type.ts | 16 ++--- .../src/rules/no-missing-iframe-sandbox.ts | 17 +++--- .../src/rules/no-namespace.ts | 5 +- .../src/rules/no-script-url.ts | 7 +-- .../src/rules/no-unsafe-iframe-sandbox.ts | 15 +++-- .../src/rules/no-unsafe-target-blank.ts | 9 +-- .../rules/no-void-elements-with-children.ts | 6 +- .../src/utils/create-jsx-element-resolver.ts | 12 ++-- .../package.json | 1 - .../use-no-direct-set-state-in-use-effect.ts | 4 +- .../package.json | 1 - .../eslint-plugin-react-web-api/package.json | 1 - .../eslint-plugin-react-x/package.json | 1 - .../src/rules/avoid-shorthand-boolean.ts | 4 +- .../src/rules/no-children-prop.ts | 4 +- .../src/rules/no-context-provider.ts | 4 +- .../src/rules/no-implicit-key.ts | 6 +- .../src/rules/no-missing-key.ts | 6 +- .../rules/no-nested-component-definitions.ts | 3 +- .../no-nested-lazy-component-declarations.ts | 3 +- .../src/rules/no-unstable-context-value.ts | 4 +- .../src/rules/no-useless-fragment.ts | 22 +++---- .../src/rules/prefer-shorthand-boolean.ts | 6 +- .../src/rules/prefer-shorthand-fragment.ts | 4 +- packages/utilities/ast/src/index.ts | 2 +- ...{to-readable-node-name.ts => stringify.ts} | 8 +-- packages/utilities/jsx/package.json | 60 ------------------- .../jsx/src/attribute/attribute-name.ts | 12 ---- packages/utilities/jsx/src/attribute/index.ts | 5 -- packages/utilities/jsx/src/element/index.ts | 2 - packages/utilities/jsx/src/index.ts | 5 -- packages/utilities/jsx/tsconfig.json | 34 ----------- packages/utilities/jsx/tsup.config.ts | 16 ----- pnpm-lock.yaml | 52 ---------------- 93 files changed, 781 insertions(+), 344 deletions(-) create mode 100644 packages/core/docs/functions/findParentAttribute.md create mode 100644 packages/core/docs/functions/getAttribute.md create mode 100644 packages/core/docs/functions/getAttributeName.md create mode 100644 packages/core/docs/functions/getAttributeValue.md create mode 100644 packages/core/docs/functions/getElementType.md create mode 100644 packages/core/docs/functions/hasAnyAttribute.md create mode 100644 packages/core/docs/functions/hasAttribute.md create mode 100644 packages/core/docs/functions/hasEveryAttribute.md create mode 100644 packages/core/docs/functions/isFragmentElement.md create mode 100644 packages/core/docs/functions/isHostElement.md create mode 100644 packages/core/docs/functions/isJsxLike.md create mode 100644 packages/core/docs/functions/isJsxText.md create mode 100644 packages/core/docs/functions/isKeyedElement.md create mode 100644 packages/core/docs/functions/stringifyJsx.md create mode 100644 packages/core/docs/type-aliases/JSXDetectionHint.md create mode 100644 packages/core/docs/type-aliases/TSESTreeJSX.md create mode 100644 packages/core/docs/variables/DEFAULT_JSX_DETECTION_HINT.md create mode 100644 packages/core/docs/variables/JSXDetectionHint.md create mode 100644 packages/core/docs/variables/isJSX.md rename packages/core/src/component/{hierarchy.ts => component-hierarchy.ts} (98%) rename packages/core/src/component/{is.ts => component-is.ts} (100%) rename packages/core/src/hook/{hierarchy.ts => hook-hierarchy.ts} (95%) rename packages/core/src/hook/{is.ts => hook-is.ts} (100%) create mode 100644 packages/core/src/jsx/index.ts create mode 100644 packages/core/src/jsx/jsx-attribute-name.ts rename packages/{utilities/jsx/src/attribute/attribute-value.ts => core/src/jsx/jsx-attribute-value.ts} (90%) rename packages/{utilities/jsx/src/attribute/attribute.ts => core/src/jsx/jsx-attribute.ts} (86%) rename packages/{utilities/jsx/src => core/src/jsx}/jsx-detection-hint.ts (100%) rename packages/{utilities/jsx/src => core/src/jsx}/jsx-detection.ts (100%) rename packages/{utilities/jsx/src/element/element-type.ts => core/src/jsx/jsx-element-type.ts} (50%) rename packages/{utilities/jsx/src/attribute/has.ts => core/src/jsx/jsx-has.ts} (56%) rename packages/{utilities/jsx/src/attribute/hierarchy.ts => core/src/jsx/jsx-hierarchy.ts} (100%) rename packages/{utilities/jsx/src/element/is.ts => core/src/jsx/jsx-is.ts} (52%) rename packages/{utilities/jsx/src/to-string.ts => core/src/jsx/jsx-stringify.ts} (82%) create mode 100644 packages/core/src/semantic/index.ts rename packages/core/src/{ => semantic}/semantic-entry.ts (69%) rename packages/core/src/{ => semantic}/semantic-node.ts (100%) rename packages/utilities/ast/src/{to-readable-node-name.ts => stringify.ts} (67%) delete mode 100644 packages/utilities/jsx/package.json delete mode 100644 packages/utilities/jsx/src/attribute/attribute-name.ts delete mode 100644 packages/utilities/jsx/src/attribute/index.ts delete mode 100644 packages/utilities/jsx/src/element/index.ts delete mode 100644 packages/utilities/jsx/src/index.ts delete mode 100644 packages/utilities/jsx/tsconfig.json delete mode 100644 packages/utilities/jsx/tsup.config.ts diff --git a/apps/website/content/docs/contributing.mdx b/apps/website/content/docs/contributing.mdx index 96bd169c6c..5601e70779 100644 --- a/apps/website/content/docs/contributing.mdx +++ b/apps/website/content/docs/contributing.mdx @@ -29,7 +29,6 @@ flowchart TB subgraph "Utilities Modules" AST["AST Module"]:::utilities Eff["Eff Module"]:::utilities - JSX["JSX Module"]:::utilities Kit["Kit Module"]:::utilities Var["Var Module"]:::utilities end @@ -69,13 +68,11 @@ flowchart TB %% Utilities used by Core and Plugins AST ---|"provides"| Core Eff ---|"provides"| Core - JSX ---|"provides"| Core Kit ---|"provides"| Core Var ---|"provides"| Core AST ---|"provides"| ReactX Eff ---|"provides"| ReactX - JSX ---|"provides"| ReactX Kit ---|"provides"| ReactX Var ---|"provides"| ReactX @@ -95,7 +92,6 @@ flowchart TB Scripts -->|"CI/CD"| Shared Scripts -->|"CI/CD"| AST Scripts -->|"CI/CD"| Eff - Scripts -->|"CI/CD"| JSX Scripts -->|"CI/CD"| Kit Scripts -->|"CI/CD"| Var Scripts -->|"CI/CD"| ReactX @@ -113,7 +109,6 @@ flowchart TB click Shared "https://github.com/rel1cx/eslint-react/tree/main/packages/shared" click AST "https://github.com/rel1cx/eslint-react/tree/main/packages/utilities/ast" click Eff "https://github.com/rel1cx/eslint-react/tree/main/packages/utilities/eff" - click JSX "https://github.com/rel1cx/eslint-react/tree/main/packages/utilities/jsx" click Kit "https://github.com/rel1cx/eslint-react/tree/main/packages/utilities/kit" click Var "https://github.com/rel1cx/eslint-react/tree/main/packages/utilities/var" click ReactX "https://github.com/rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x" @@ -148,7 +143,6 @@ This section provides a summary of the packages in the monorepo. - `packages/utilities/eff`: JavaScript and TypeScript utilities (previously some re-exports of the `effect` library) - `packages/utilities/ast`: TSESTree AST utility module for static analysis - `packages/utilities/var`: TSESTree AST utility module for static analysis of variables - - `packages/utilities/jsx`: TSESTree AST utility module for static analysis of JSX - `packages/utilities/kit`: ESLint React's plugin kit for building plugins and rules - **Core & Shared** - `packages/core`: Utility module for static analysis of React core APIs and patterns diff --git a/apps/website/content/docs/faq.mdx b/apps/website/content/docs/faq.mdx index ca8f8abeb7..517da9f90e 100644 --- a/apps/website/content/docs/faq.mdx +++ b/apps/website/content/docs/faq.mdx @@ -30,7 +30,6 @@ Currently, it includes the following: - `@eslint-react/eff`: JavaScript and TypeScript utilities (previously some re-exports of the `effect` library). - `@eslint-react/ast`: TSESTree AST utility module. - `@eslint-react/var`: TSESTree AST utility module for static analysis of variables. - - `@eslint-react/jsx`: TSESTree AST utility module for static analysis of JSX. - `@eslint-react/kit`: ESLint React's plugin kit for building plugins and rules. - **Core & Shared Modules** - `@eslint-react/core`: Utility module for static analysis of React core APIs and patterns. diff --git a/packages/core/docs/README.md b/packages/core/docs/README.md index 5a22846634..3387ff7028 100644 --- a/packages/core/docs/README.md +++ b/packages/core/docs/README.md @@ -30,6 +30,8 @@ - [ComponentStateKind](type-aliases/ComponentStateKind.md) - [EffectKind](type-aliases/EffectKind.md) - [HookKind](type-aliases/HookKind.md) +- [JSXDetectionHint](type-aliases/JSXDetectionHint.md) +- [TSESTreeJSX](type-aliases/TSESTreeJSX.md) ## Variables @@ -37,6 +39,7 @@ - [ComponentFlag](variables/ComponentFlag.md) - [ComponentPhaseRelevance](variables/ComponentPhaseRelevance.md) - [DEFAULT\_COMPONENT\_DETECTION\_HINT](variables/DEFAULT_COMPONENT_DETECTION_HINT.md) +- [DEFAULT\_JSX\_DETECTION\_HINT](variables/DEFAULT_JSX_DETECTION_HINT.md) - [isCaptureOwnerStack](variables/isCaptureOwnerStack.md) - [isCaptureOwnerStackCall](variables/isCaptureOwnerStackCall.md) - [isChildrenCount](variables/isChildrenCount.md) @@ -60,6 +63,7 @@ - [isForwardRef](variables/isForwardRef.md) - [isForwardRefCall](variables/isForwardRefCall.md) - [isInversePhase](variables/isInversePhase.md) +- [isJSX](variables/isJSX.md) - [isLazy](variables/isLazy.md) - [isLazyCall](variables/isLazyCall.md) - [isMemo](variables/isMemo.md) @@ -83,12 +87,21 @@ - [isUseStateCall](variables/isUseStateCall.md) - [isUseSyncExternalStoreCall](variables/isUseSyncExternalStoreCall.md) - [isUseTransitionCall](variables/isUseTransitionCall.md) +- [JSXDetectionHint](variables/JSXDetectionHint.md) ## Functions +- [findParentAttribute](functions/findParentAttribute.md) +- [getAttribute](functions/getAttribute.md) +- [getAttributeName](functions/getAttributeName.md) +- [getAttributeValue](functions/getAttributeValue.md) - [getComponentFlagFromInitPath](functions/getComponentFlagFromInitPath.md) - [getComponentNameFromId](functions/getComponentNameFromId.md) +- [getElementType](functions/getElementType.md) - [getFunctionComponentId](functions/getFunctionComponentId.md) +- [hasAnyAttribute](functions/hasAnyAttribute.md) +- [hasAttribute](functions/hasAttribute.md) +- [hasEveryAttribute](functions/hasEveryAttribute.md) - [hasNoneOrLooseComponentName](functions/hasNoneOrLooseComponentName.md) - [isAssignmentToThisState](functions/isAssignmentToThisState.md) - [isChildrenOfCreateElement](functions/isChildrenOfCreateElement.md) @@ -105,6 +118,7 @@ - [isComponentWrapperCall](functions/isComponentWrapperCall.md) - [isComponentWrapperCallLoose](functions/isComponentWrapperCallLoose.md) - [isDeclaredInRenderPropLoose](functions/isDeclaredInRenderPropLoose.md) +- [isFragmentElement](functions/isFragmentElement.md) - [isFunctionOfComponentDidMount](functions/isFunctionOfComponentDidMount.md) - [isFunctionOfComponentWillUnmount](functions/isFunctionOfComponentWillUnmount.md) - [isFunctionOfRender](functions/isFunctionOfRender.md) @@ -117,8 +131,12 @@ - [isGetDerivedStateFromProps](functions/isGetDerivedStateFromProps.md) - [isGetInitialState](functions/isGetInitialState.md) - [isGetSnapshotBeforeUpdate](functions/isGetSnapshotBeforeUpdate.md) +- [isHostElement](functions/isHostElement.md) - [isInitializedFromReact](functions/isInitializedFromReact.md) - [isInsideRenderMethod](functions/isInsideRenderMethod.md) +- [isJsxLike](functions/isJsxLike.md) +- [isJsxText](functions/isJsxText.md) +- [isKeyedElement](functions/isKeyedElement.md) - [isPureComponent](functions/isPureComponent.md) - [isReactAPI](functions/isReactAPI.md) - [isReactAPICall](functions/isReactAPICall.md) @@ -139,6 +157,7 @@ - [isUnsafeComponentWillUpdate](functions/isUnsafeComponentWillUpdate.md) - [isUseEffectCallLoose](functions/isUseEffectCallLoose.md) - [isValidComponentDefinition](functions/isValidComponentDefinition.md) +- [stringifyJsx](functions/stringifyJsx.md) - [useComponentCollector](functions/useComponentCollector.md) - [useComponentCollectorLegacy](functions/useComponentCollectorLegacy.md) - [useHookCollector](functions/useHookCollector.md) diff --git a/packages/core/docs/functions/findParentAttribute.md b/packages/core/docs/functions/findParentAttribute.md new file mode 100644 index 0000000000..9d5f4dd5e5 --- /dev/null +++ b/packages/core/docs/functions/findParentAttribute.md @@ -0,0 +1,31 @@ +[**@eslint-react/core**](../README.md) + +*** + +[@eslint-react/core](../README.md) / findParentAttribute + +# Function: findParentAttribute() + +> **findParentAttribute**(`node`, `test`): `undefined` \| `JSXAttribute` + +Find the parent JSX attribute node of a node + +## Parameters + +### node + +`Node` + +The node to find the parent attribute of + +### test + +(`node`) => `boolean` + +The test to apply to the parent attribute + +## Returns + +`undefined` \| `JSXAttribute` + +The parent attribute node or undefined diff --git a/packages/core/docs/functions/getAttribute.md b/packages/core/docs/functions/getAttribute.md new file mode 100644 index 0000000000..0eeaad383a --- /dev/null +++ b/packages/core/docs/functions/getAttribute.md @@ -0,0 +1,43 @@ +[**@eslint-react/core**](../README.md) + +*** + +[@eslint-react/core](../README.md) / getAttribute + +# Function: getAttribute() + +> **getAttribute**(`context`, `name`, `attributes`, `initialScope?`): `undefined` \| `JSXAttribute` \| `JSXSpreadAttribute` + +Get the JSX attribute node with the given name + +## Parameters + +### context + +`RuleContext` + +The ESLint rule context + +### name + +`string` + +The name of the attribute + +### attributes + +(`JSXAttribute` \| `JSXSpreadAttribute`)[] + +The attributes to search + +### initialScope? + +`Scope` + +The initial scope to use for variable resolution + +## Returns + +`undefined` \| `JSXAttribute` \| `JSXSpreadAttribute` + +The JSX attribute node or undefined diff --git a/packages/core/docs/functions/getAttributeName.md b/packages/core/docs/functions/getAttributeName.md new file mode 100644 index 0000000000..827672a1a2 --- /dev/null +++ b/packages/core/docs/functions/getAttributeName.md @@ -0,0 +1,31 @@ +[**@eslint-react/core**](../README.md) + +*** + +[@eslint-react/core](../README.md) / getAttributeName + +# Function: getAttributeName() + +> **getAttributeName**(`context`, `node`): `string` + +Get the stringified name of a JSX attribute + +## Parameters + +### context + +`RuleContext` + +The ESLint rule context + +### node + +`JSXAttribute` + +The JSX attribute node + +## Returns + +`string` + +The name of the attribute diff --git a/packages/core/docs/functions/getAttributeValue.md b/packages/core/docs/functions/getAttributeValue.md new file mode 100644 index 0000000000..6d055b8241 --- /dev/null +++ b/packages/core/docs/functions/getAttributeValue.md @@ -0,0 +1,37 @@ +[**@eslint-react/core**](../README.md) + +*** + +[@eslint-react/core](../README.md) / getAttributeValue + +# Function: getAttributeValue() + +> **getAttributeValue**(`context`, `node`, `name`): \{ `initialScope`: `undefined` \| `Scope`; `kind`: `"none"`; `node`: `Node`; \} \| \{ `initialScope`: `undefined` \| `Scope`; `kind`: `"some"`; `node`: `Node`; `value`: `unknown`; \} + +Get a StaticValue of the attribute value + +## Parameters + +### context + +`RuleContext` + +The rule context + +### node + +The JSX attribute node + +`JSXAttribute` | `JSXSpreadAttribute` + +### name + +`string` + +The name of the attribute + +## Returns + +\{ `initialScope`: `undefined` \| `Scope`; `kind`: `"none"`; `node`: `Node`; \} \| \{ `initialScope`: `undefined` \| `Scope`; `kind`: `"some"`; `node`: `Node`; `value`: `unknown`; \} + +The StaticValue of the attribute value diff --git a/packages/core/docs/functions/getElementType.md b/packages/core/docs/functions/getElementType.md new file mode 100644 index 0000000000..1156a60f22 --- /dev/null +++ b/packages/core/docs/functions/getElementType.md @@ -0,0 +1,31 @@ +[**@eslint-react/core**](../README.md) + +*** + +[@eslint-react/core](../README.md) / getElementType + +# Function: getElementType() + +> **getElementType**(`context`, `node`): `string` + +Get the stringified type of a JSX element + +## Parameters + +### context + +`RuleContext` + +The ESLint rule context + +### node + +The JSX element node + +`JSXElement` | `JSXFragment` + +## Returns + +`string` + +The type of the element diff --git a/packages/core/docs/functions/hasAnyAttribute.md b/packages/core/docs/functions/hasAnyAttribute.md new file mode 100644 index 0000000000..1427856ec3 --- /dev/null +++ b/packages/core/docs/functions/hasAnyAttribute.md @@ -0,0 +1,31 @@ +[**@eslint-react/core**](../README.md) + +*** + +[@eslint-react/core](../README.md) / hasAnyAttribute + +# Function: hasAnyAttribute() + +> **hasAnyAttribute**(`context`, `names`, `attributes`, `initialScope?`): `boolean` + +## Parameters + +### context + +`RuleContext` + +### names + +`string`[] + +### attributes + +(`JSXAttribute` \| `JSXSpreadAttribute`)[] + +### initialScope? + +`Scope` + +## Returns + +`boolean` diff --git a/packages/core/docs/functions/hasAttribute.md b/packages/core/docs/functions/hasAttribute.md new file mode 100644 index 0000000000..8d2269f1fc --- /dev/null +++ b/packages/core/docs/functions/hasAttribute.md @@ -0,0 +1,31 @@ +[**@eslint-react/core**](../README.md) + +*** + +[@eslint-react/core](../README.md) / hasAttribute + +# Function: hasAttribute() + +> **hasAttribute**(`context`, `name`, `attributes`, `initialScope?`): `boolean` + +## Parameters + +### context + +`RuleContext` + +### name + +`string` + +### attributes + +(`JSXAttribute` \| `JSXSpreadAttribute`)[] + +### initialScope? + +`Scope` + +## Returns + +`boolean` diff --git a/packages/core/docs/functions/hasEveryAttribute.md b/packages/core/docs/functions/hasEveryAttribute.md new file mode 100644 index 0000000000..9fb6b3dac3 --- /dev/null +++ b/packages/core/docs/functions/hasEveryAttribute.md @@ -0,0 +1,31 @@ +[**@eslint-react/core**](../README.md) + +*** + +[@eslint-react/core](../README.md) / hasEveryAttribute + +# Function: hasEveryAttribute() + +> **hasEveryAttribute**(`context`, `names`, `attributes`, `initialScope?`): `boolean` + +## Parameters + +### context + +`RuleContext` + +### names + +`string`[] + +### attributes + +(`JSXAttribute` \| `JSXSpreadAttribute`)[] + +### initialScope? + +`Scope` + +## Returns + +`boolean` diff --git a/packages/core/docs/functions/isFragmentElement.md b/packages/core/docs/functions/isFragmentElement.md new file mode 100644 index 0000000000..0ebe13c2b6 --- /dev/null +++ b/packages/core/docs/functions/isFragmentElement.md @@ -0,0 +1,51 @@ +[**@eslint-react/core**](../README.md) + +*** + +[@eslint-react/core](../README.md) / isFragmentElement + +# Function: isFragmentElement() + +## Call Signature + +> **isFragmentElement**(`context`, `node`, `allowJSXFragment?`): `node is JSXElement` + +### Parameters + +#### context + +`RuleContext` + +#### node + +`undefined` | `null` | `Node` + +#### allowJSXFragment? + +`false` + +### Returns + +`node is JSXElement` + +## Call Signature + +> **isFragmentElement**(`context`, `node`, `allowJSXFragment?`): node is JSXElement \| JSXFragment + +### Parameters + +#### context + +`RuleContext` + +#### node + +`undefined` | `null` | `Node` + +#### allowJSXFragment? + +`true` + +### Returns + +node is JSXElement \| JSXFragment diff --git a/packages/core/docs/functions/isHostElement.md b/packages/core/docs/functions/isHostElement.md new file mode 100644 index 0000000000..acfe86ead5 --- /dev/null +++ b/packages/core/docs/functions/isHostElement.md @@ -0,0 +1,23 @@ +[**@eslint-react/core**](../README.md) + +*** + +[@eslint-react/core](../README.md) / isHostElement + +# Function: isHostElement() + +> **isHostElement**(`context`, `node`): `boolean` + +## Parameters + +### context + +`RuleContext` + +### node + +`Node` + +## Returns + +`boolean` diff --git a/packages/core/docs/functions/isJsxLike.md b/packages/core/docs/functions/isJsxLike.md new file mode 100644 index 0000000000..b791b9c4ee --- /dev/null +++ b/packages/core/docs/functions/isJsxLike.md @@ -0,0 +1,41 @@ +[**@eslint-react/core**](../README.md) + +*** + +[@eslint-react/core](../README.md) / isJsxLike + +# Function: isJsxLike() + +> **isJsxLike**(`code`, `node`, `hint`): `boolean` + +Heuristic decision to determine if a node is a JSX-like node. + +## Parameters + +### code + +The sourceCode object + +#### getScope + +(`node`) => `Scope` + +The function to get the scope of a node + +### node + +The AST node to check + +`undefined` | `null` | `Node` + +### hint + +`bigint` = `DEFAULT_JSX_DETECTION_HINT` + +The `JSXDetectionHint` to use + +## Returns + +`boolean` + +boolean diff --git a/packages/core/docs/functions/isJsxText.md b/packages/core/docs/functions/isJsxText.md new file mode 100644 index 0000000000..3d2c2e4952 --- /dev/null +++ b/packages/core/docs/functions/isJsxText.md @@ -0,0 +1,25 @@ +[**@eslint-react/core**](../README.md) + +*** + +[@eslint-react/core](../README.md) / isJsxText + +# Function: isJsxText() + +> **isJsxText**(`node`): node is JSXText \| Literal + +Check if a node is a `JSXText` or a `Literal` node + +## Parameters + +### node + +The AST node to check + +`undefined` | `null` | `Node` + +## Returns + +node is JSXText \| Literal + +`true` if the node is a `JSXText` or a `Literal` node diff --git a/packages/core/docs/functions/isKeyedElement.md b/packages/core/docs/functions/isKeyedElement.md new file mode 100644 index 0000000000..43969cb147 --- /dev/null +++ b/packages/core/docs/functions/isKeyedElement.md @@ -0,0 +1,27 @@ +[**@eslint-react/core**](../README.md) + +*** + +[@eslint-react/core](../README.md) / isKeyedElement + +# Function: isKeyedElement() + +> **isKeyedElement**(`context`, `node`, `initialScope?`): `boolean` + +## Parameters + +### context + +`RuleContext` + +### node + +`Node` + +### initialScope? + +`Scope` + +## Returns + +`boolean` diff --git a/packages/core/docs/functions/stringifyJsx.md b/packages/core/docs/functions/stringifyJsx.md new file mode 100644 index 0000000000..664b8a8f67 --- /dev/null +++ b/packages/core/docs/functions/stringifyJsx.md @@ -0,0 +1,25 @@ +[**@eslint-react/core**](../README.md) + +*** + +[@eslint-react/core](../README.md) / stringifyJsx + +# Function: stringifyJsx() + +> **stringifyJsx**(`node`): `string` + +Get the stringified representation of a JSX node + +## Parameters + +### node + +The JSX node + +`JSXIdentifier` | `JSXMemberExpression` | `JSXNamespacedName` | `JSXOpeningElement` | `JSXClosingElement` | `JSXOpeningFragment` | `JSXClosingFragment` | `JSXText` + +## Returns + +`string` + +The stringified representation diff --git a/packages/core/docs/type-aliases/JSXDetectionHint.md b/packages/core/docs/type-aliases/JSXDetectionHint.md new file mode 100644 index 0000000000..a731ff2769 --- /dev/null +++ b/packages/core/docs/type-aliases/JSXDetectionHint.md @@ -0,0 +1,9 @@ +[**@eslint-react/core**](../README.md) + +*** + +[@eslint-react/core](../README.md) / JSXDetectionHint + +# Type Alias: JSXDetectionHint + +> **JSXDetectionHint** = `bigint` diff --git a/packages/core/docs/type-aliases/TSESTreeJSX.md b/packages/core/docs/type-aliases/TSESTreeJSX.md new file mode 100644 index 0000000000..9322d61f0d --- /dev/null +++ b/packages/core/docs/type-aliases/TSESTreeJSX.md @@ -0,0 +1,9 @@ +[**@eslint-react/core**](../README.md) + +*** + +[@eslint-react/core](../README.md) / TSESTreeJSX + +# Type Alias: TSESTreeJSX + +> **TSESTreeJSX** = `TSESTree.JSXAttribute` \| `TSESTree.JSXClosingElement` \| `TSESTree.JSXClosingFragment` \| `TSESTree.JSXElement` \| `TSESTree.JSXEmptyExpression` \| `TSESTree.JSXExpressionContainer` \| `TSESTree.JSXFragment` \| `TSESTree.JSXIdentifier` \| `TSESTree.JSXMemberExpression` \| `TSESTree.JSXNamespacedName` \| `TSESTree.JSXOpeningElement` \| `TSESTree.JSXOpeningFragment` \| `TSESTree.JSXSpreadAttribute` \| `TSESTree.JSXSpreadChild` \| `TSESTree.JSXText` diff --git a/packages/core/docs/variables/ComponentDetectionHint.md b/packages/core/docs/variables/ComponentDetectionHint.md index dd61fff2c4..24110b9ca0 100644 --- a/packages/core/docs/variables/ComponentDetectionHint.md +++ b/packages/core/docs/variables/ComponentDetectionHint.md @@ -14,7 +14,7 @@ Hints for component collector ### None -> `readonly` **None**: `0n` +> `readonly` **None**: `0n` = `0n` ### SkipArrayMapArgument diff --git a/packages/core/docs/variables/DEFAULT_JSX_DETECTION_HINT.md b/packages/core/docs/variables/DEFAULT_JSX_DETECTION_HINT.md new file mode 100644 index 0000000000..6575ef7645 --- /dev/null +++ b/packages/core/docs/variables/DEFAULT_JSX_DETECTION_HINT.md @@ -0,0 +1,9 @@ +[**@eslint-react/core**](../README.md) + +*** + +[@eslint-react/core](../README.md) / DEFAULT\_JSX\_DETECTION\_HINT + +# Variable: DEFAULT\_JSX\_DETECTION\_HINT + +> `const` **DEFAULT\_JSX\_DETECTION\_HINT**: `bigint` diff --git a/packages/core/docs/variables/JSXDetectionHint.md b/packages/core/docs/variables/JSXDetectionHint.md new file mode 100644 index 0000000000..c661ef591a --- /dev/null +++ b/packages/core/docs/variables/JSXDetectionHint.md @@ -0,0 +1,59 @@ +[**@eslint-react/core**](../README.md) + +*** + +[@eslint-react/core](../README.md) / JSXDetectionHint + +# Variable: JSXDetectionHint + +> **JSXDetectionHint**: `object` + +## Type declaration + +### None + +> `readonly` **None**: `0n` = `0n` + +### SkipBigIntLiteral + +> `readonly` **SkipBigIntLiteral**: `bigint` + +### SkipBooleanLiteral + +> `readonly` **SkipBooleanLiteral**: `bigint` + +### SkipCreateElement + +> `readonly` **SkipCreateElement**: `bigint` + +### SkipEmptyArray + +> `readonly` **SkipEmptyArray**: `bigint` + +### SkipNullLiteral + +> `readonly` **SkipNullLiteral**: `bigint` + +### SkipNumberLiteral + +> `readonly` **SkipNumberLiteral**: `bigint` + +### SkipStringLiteral + +> `readonly` **SkipStringLiteral**: `bigint` + +### SkipUndefined + +> `readonly` **SkipUndefined**: `bigint` + +### StrictArray + +> `readonly` **StrictArray**: `bigint` + +### StrictConditional + +> `readonly` **StrictConditional**: `bigint` + +### StrictLogical + +> `readonly` **StrictLogical**: `bigint` diff --git a/packages/core/docs/variables/isJSX.md b/packages/core/docs/variables/isJSX.md new file mode 100644 index 0000000000..d67d793ea7 --- /dev/null +++ b/packages/core/docs/variables/isJSX.md @@ -0,0 +1,19 @@ +[**@eslint-react/core**](../README.md) + +*** + +[@eslint-react/core](../README.md) / isJSX + +# Variable: isJSX() + +> `const` **isJSX**: (`node`) => node is JSXIdentifier \| JSXMemberExpression \| JSXNamespacedName \| JSXOpeningElement \| JSXClosingElement \| JSXOpeningFragment \| JSXClosingFragment \| JSXText \| JSXAttribute \| JSXSpreadAttribute \| JSXElement \| JSXFragment \| JSXEmptyExpression \| JSXExpressionContainer \| JSXSpreadChild + +## Parameters + +### node + +`undefined` | `null` | `Node` + +## Returns + +node is JSXIdentifier \| JSXMemberExpression \| JSXNamespacedName \| JSXOpeningElement \| JSXClosingElement \| JSXOpeningFragment \| JSXClosingFragment \| JSXText \| JSXAttribute \| JSXSpreadAttribute \| JSXElement \| JSXFragment \| JSXEmptyExpression \| JSXExpressionContainer \| JSXSpreadChild diff --git a/packages/core/package.json b/packages/core/package.json index 5f0cd58da7..983709aab6 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -44,7 +44,6 @@ "dependencies": { "@eslint-react/ast": "workspace:*", "@eslint-react/eff": "workspace:*", - "@eslint-react/jsx": "workspace:*", "@eslint-react/kit": "workspace:*", "@eslint-react/shared": "workspace:*", "@eslint-react/var": "workspace:*", diff --git a/packages/core/src/component/component-collector-legacy.ts b/packages/core/src/component/component-collector-legacy.ts index 2b32ae9d59..af2fb00c5f 100644 --- a/packages/core/src/component/component-collector-legacy.ts +++ b/packages/core/src/component/component-collector-legacy.ts @@ -5,7 +5,7 @@ import { _ } from "@eslint-react/eff"; import { getId } from "@eslint-react/shared"; import { ComponentFlag } from "./component-flag"; -import { isClassComponent, isPureComponent } from "./is"; +import { isClassComponent, isPureComponent } from "./component-is"; export declare namespace useComponentCollectorLegacy { type ReturnType = { diff --git a/packages/core/src/component/component-collector.ts b/packages/core/src/component/component-collector.ts index f8cbd455c9..2e26c6f53e 100644 --- a/packages/core/src/component/component-collector.ts +++ b/packages/core/src/component/component-collector.ts @@ -4,12 +4,12 @@ import type { ComponentDetectionHint } from "./component-detection-hint"; import type { FunctionComponent } from "./component-semantic-node"; import * as AST from "@eslint-react/ast"; import { _ } from "@eslint-react/eff"; -import * as JSX from "@eslint-react/jsx"; import { type RuleContext, SEL } from "@eslint-react/kit"; import { getId } from "@eslint-react/shared"; import { AST_NODE_TYPES as T } from "@typescript-eslint/types"; import { isReactHookCall } from "../hook"; +import { isJsxLike } from "../jsx"; import { isValidComponentDefinition } from "./component-definition"; import { DEFAULT_COMPONENT_DETECTION_HINT } from "./component-detection-hint"; import { getFunctionComponentId } from "./component-id"; @@ -73,7 +73,7 @@ export function useComponentCollector( .some((r) => { return context.sourceCode.getScope(r).block === entry.node && r.argument != null - && !JSX.isJsxLike(context.sourceCode, r.argument, hint); + && !isJsxLike(context.sourceCode, r.argument, hint); }); if (shouldDrop) { components.delete(entry.key); @@ -100,7 +100,7 @@ export function useComponentCollector( if (entry == null) return; const { body } = entry.node; const isComponent = hasNoneOrLooseComponentName(context, entry.node) - && JSX.isJsxLike(context.sourceCode, body, hint) + && isJsxLike(context.sourceCode, body, hint) && isValidComponentDefinition(context, entry.node, hint); if (!isComponent) return; const initPath = AST.getFunctionInitPath(entry.node); @@ -149,7 +149,7 @@ export function useComponentCollector( const entry = getCurrentEntry(); if (entry == null) return; const isComponent = hasNoneOrLooseComponentName(context, entry.node) - && JSX.isJsxLike(context.sourceCode, node.argument, hint) + && isJsxLike(context.sourceCode, node.argument, hint) && isValidComponentDefinition(context, entry.node, hint); if (!isComponent) return; entry.isComponent = true; diff --git a/packages/core/src/component/component-definition.ts b/packages/core/src/component/component-definition.ts index d544d25c93..3c65298420 100644 --- a/packages/core/src/component/component-definition.ts +++ b/packages/core/src/component/component-definition.ts @@ -4,7 +4,7 @@ import { type RuleContext } from "@eslint-react/kit"; import { AST_NODE_TYPES as T } from "@typescript-eslint/types"; import { isMatching, P } from "ts-pattern"; import { ComponentDetectionHint } from "./component-detection-hint"; -import { isChildrenOfCreateElement, isFunctionOfRenderMethod } from "./hierarchy"; +import { isChildrenOfCreateElement, isFunctionOfRenderMethod } from "./component-hierarchy"; const isFunctionOfClassMethod = isMatching({ type: P.union(T.ArrowFunctionExpression, T.FunctionExpression), diff --git a/packages/core/src/component/component-detection-hint.ts b/packages/core/src/component/component-detection-hint.ts index c5f23752a1..d36e835135 100644 --- a/packages/core/src/component/component-detection-hint.ts +++ b/packages/core/src/component/component-detection-hint.ts @@ -1,5 +1,6 @@ /* eslint-disable perfectionist/sort-objects */ -import * as JSX from "@eslint-react/jsx"; + +import { JSXDetectionHint } from "../jsx"; export type ComponentDetectionHint = bigint; @@ -10,7 +11,7 @@ export const ComponentDetectionHint = { /** * 1n << 0n - 1n << 63n are reserved for JSXDetectionHint */ - ...JSX.JSXDetectionHint, + ...JSXDetectionHint, /** * Skip function component created by React.memo */ diff --git a/packages/core/src/component/hierarchy.ts b/packages/core/src/component/component-hierarchy.ts similarity index 98% rename from packages/core/src/component/hierarchy.ts rename to packages/core/src/component/component-hierarchy.ts index 611de781a6..f0f5778e20 100644 --- a/packages/core/src/component/hierarchy.ts +++ b/packages/core/src/component/component-hierarchy.ts @@ -4,10 +4,10 @@ import { type RuleContext } from "@eslint-react/kit"; import { AST_NODE_TYPES as T } from "@typescript-eslint/types"; import { isCreateElementCall } from "../utils"; +import { isClassComponent } from "./component-is"; import { isComponentDidMount, isComponentWillUnmount } from "./component-lifecycle"; import { isRenderLike } from "./component-render"; import { isRenderMethodLike } from "./component-render-method"; -import { isClassComponent } from "./is"; /** * Determines whether inside `createElement`'s children. diff --git a/packages/core/src/component/is.ts b/packages/core/src/component/component-is.ts similarity index 100% rename from packages/core/src/component/is.ts rename to packages/core/src/component/component-is.ts diff --git a/packages/core/src/component/component-render-prop.ts b/packages/core/src/component/component-render-prop.ts index 0d94ff01b4..afcc9bb57e 100644 --- a/packages/core/src/component/component-render-prop.ts +++ b/packages/core/src/component/component-render-prop.ts @@ -1,8 +1,8 @@ import type { RuleContext } from "@eslint-react/kit"; import type { TSESTree } from "@typescript-eslint/types"; import * as AST from "@eslint-react/ast"; -import * as JSX from "@eslint-react/jsx"; import { AST_NODE_TYPES as T } from "@typescript-eslint/types"; +import { isJsxLike, JSXDetectionHint } from "../jsx"; /** * Unsafe check whether given node is a render function @@ -25,13 +25,13 @@ export function isRenderFunctionLoose(context: RuleContext, node: AST.TSESTreeFu && parent.parent.name.type === T.JSXIdentifier && parent.parent.name.name.startsWith("render"); } - return JSX.isJsxLike( + return isJsxLike( context.sourceCode, body, - JSX.JSXDetectionHint.SkipNullLiteral - | JSX.JSXDetectionHint.SkipUndefined - | JSX.JSXDetectionHint.StrictLogical - | JSX.JSXDetectionHint.StrictConditional, + JSXDetectionHint.SkipNullLiteral + | JSXDetectionHint.SkipUndefined + | JSXDetectionHint.StrictLogical + | JSXDetectionHint.StrictConditional, ); } diff --git a/packages/core/src/component/component-render.ts b/packages/core/src/component/component-render.ts index 80072aeada..4a0f5f4ef7 100644 --- a/packages/core/src/component/component-render.ts +++ b/packages/core/src/component/component-render.ts @@ -2,7 +2,7 @@ import type { TSESTree } from "@typescript-eslint/types"; import * as AST from "@eslint-react/ast"; import { AST_NODE_TYPES as T } from "@typescript-eslint/types"; -import { isClassComponent } from "./is"; +import { isClassComponent } from "./component-is"; /** * Check whether given node is a render function of a class component diff --git a/packages/core/src/component/component-semantic-node.ts b/packages/core/src/component/component-semantic-node.ts index 9058c54009..72eeec40b4 100644 --- a/packages/core/src/component/component-semantic-node.ts +++ b/packages/core/src/component/component-semantic-node.ts @@ -2,7 +2,7 @@ import type * as AST from "@eslint-react/ast"; import type { _ } from "@eslint-react/eff"; import type { TSESTree } from "@typescript-eslint/types"; -import type { SemanticNode } from "../semantic-node"; +import type { SemanticNode } from "../semantic"; import type { ComponentDetectionHint } from "./component-detection-hint"; import type { ComponentFlag } from "./component-flag"; diff --git a/packages/core/src/component/index.ts b/packages/core/src/component/index.ts index 3bdaa05f41..1e709b9888 100644 --- a/packages/core/src/component/index.ts +++ b/packages/core/src/component/index.ts @@ -3,8 +3,10 @@ export * from "./component-collector-legacy"; export * from "./component-definition"; export * from "./component-detection-hint"; export * from "./component-flag"; +export * from "./component-hierarchy"; export * from "./component-id"; export * from "./component-init-path"; +export * from "./component-is"; export type * from "./component-kind"; export * from "./component-lifecycle"; export * from "./component-name"; @@ -15,5 +17,3 @@ export * from "./component-render-prop"; export type * from "./component-semantic-node"; export * from "./component-state"; export * from "./component-wrapper"; -export * from "./hierarchy"; -export * from "./is"; diff --git a/packages/core/src/hook/hook-collector.ts b/packages/core/src/hook/hook-collector.ts index 607c89f4b4..2e98c2565d 100644 --- a/packages/core/src/hook/hook-collector.ts +++ b/packages/core/src/hook/hook-collector.ts @@ -3,8 +3,8 @@ import type { Hook } from "./hook-semantic-node"; import * as AST from "@eslint-react/ast"; import { getId } from "@eslint-react/shared"; +import { isReactHookCall } from "./hook-is"; import { isReactHookName } from "./hook-name"; -import { isReactHookCall } from "./is"; type FunctionEntry = { key: string; diff --git a/packages/core/src/hook/hierarchy.ts b/packages/core/src/hook/hook-hierarchy.ts similarity index 95% rename from packages/core/src/hook/hierarchy.ts rename to packages/core/src/hook/hook-hierarchy.ts index 2cc3ee0642..ad0cf9e6dc 100644 --- a/packages/core/src/hook/hierarchy.ts +++ b/packages/core/src/hook/hook-hierarchy.ts @@ -2,7 +2,7 @@ import type { _ } from "@eslint-react/eff"; import type { TSESTree } from "@typescript-eslint/types"; import * as AST from "@eslint-react/ast"; import { AST_NODE_TYPES as T } from "@typescript-eslint/types"; -import { isUseEffectCallLoose } from "./is"; +import { isUseEffectCallLoose } from "./hook-is"; export function isFunctionOfUseEffectSetup(node: TSESTree.Node | _) { if (node == null) return false; diff --git a/packages/core/src/hook/is.ts b/packages/core/src/hook/hook-is.ts similarity index 100% rename from packages/core/src/hook/is.ts rename to packages/core/src/hook/hook-is.ts diff --git a/packages/core/src/hook/hook-semantic-node.ts b/packages/core/src/hook/hook-semantic-node.ts index 204ac9bfb5..7cefc712d9 100644 --- a/packages/core/src/hook/hook-semantic-node.ts +++ b/packages/core/src/hook/hook-semantic-node.ts @@ -2,7 +2,7 @@ import type * as AST from "@eslint-react/ast"; import type { _ } from "@eslint-react/eff"; import type { TSESTree } from "@typescript-eslint/types"; -import type { SemanticNode } from "../semantic-node"; +import type { SemanticNode } from "../semantic"; /* eslint-disable perfectionist/sort-interfaces */ export interface Hook extends SemanticNode { diff --git a/packages/core/src/hook/index.ts b/packages/core/src/hook/index.ts index 822a4fd297..0ac0de64e5 100644 --- a/packages/core/src/hook/index.ts +++ b/packages/core/src/hook/index.ts @@ -1,6 +1,6 @@ -export * from "./hierarchy"; export * from "./hook-collector"; +export * from "./hook-hierarchy"; +export * from "./hook-is"; export type * from "./hook-kind"; export * from "./hook-name"; export type * from "./hook-semantic-node"; -export * from "./is"; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 2d8dfb2cf4..53122c244b 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,6 +1,6 @@ export * from "./component"; export type * from "./effect"; export * from "./hook"; -export type * from "./semantic-entry"; -export type * from "./semantic-node"; +export * from "./jsx"; +export type * from "./semantic"; export * from "./utils"; diff --git a/packages/core/src/jsx/index.ts b/packages/core/src/jsx/index.ts new file mode 100644 index 0000000000..f4469a15d8 --- /dev/null +++ b/packages/core/src/jsx/index.ts @@ -0,0 +1,10 @@ +export * from "./jsx-attribute"; +export * from "./jsx-attribute-name"; +export * from "./jsx-attribute-value"; +export * from "./jsx-detection"; +export * from "./jsx-detection-hint"; +export * from "./jsx-element-type"; +export * from "./jsx-has"; +export * from "./jsx-hierarchy"; +export * from "./jsx-is"; +export * from "./jsx-stringify"; diff --git a/packages/core/src/jsx/jsx-attribute-name.ts b/packages/core/src/jsx/jsx-attribute-name.ts new file mode 100644 index 0000000000..df757acfb7 --- /dev/null +++ b/packages/core/src/jsx/jsx-attribute-name.ts @@ -0,0 +1,13 @@ +import type { RuleContext } from "@eslint-react/kit"; +import type { TSESTree } from "@typescript-eslint/utils"; +import { stringifyJsx } from "./jsx-stringify"; + +/** + * Get the stringified name of a JSX attribute + * @param context The ESLint rule context + * @param node The JSX attribute node + * @returns The name of the attribute + */ +export function getAttributeName(context: RuleContext, node: TSESTree.JSXAttribute) { + return stringifyJsx(node.name); +} diff --git a/packages/utilities/jsx/src/attribute/attribute-value.ts b/packages/core/src/jsx/jsx-attribute-value.ts similarity index 90% rename from packages/utilities/jsx/src/attribute/attribute-value.ts rename to packages/core/src/jsx/jsx-attribute-value.ts index 5b1f504074..650cc2c738 100644 --- a/packages/utilities/jsx/src/attribute/attribute-value.ts +++ b/packages/core/src/jsx/jsx-attribute-value.ts @@ -1,4 +1,4 @@ -import type { Scope } from "@typescript-eslint/scope-manager"; +import type { RuleContext } from "@eslint-react/kit"; import type { TSESTree } from "@typescript-eslint/utils"; import * as VAR from "@eslint-react/var"; import { AST_NODE_TYPES as T } from "@typescript-eslint/types"; @@ -6,16 +6,17 @@ import { match, P } from "ts-pattern"; /** * Get a StaticValue of the attribute value + * @param context The rule context * @param node The JSX attribute node * @param name The name of the attribute - * @param initialScope The initial scope to use * @returns The StaticValue of the attribute value */ export function getAttributeValue( + context: RuleContext, node: TSESTree.JSXAttribute | TSESTree.JSXSpreadAttribute, name: string, - initialScope: Scope, ): Exclude { + const initialScope = context.sourceCode.getScope(node); switch (node.type) { case T.JSXAttribute: if (node.value?.type === T.Literal) { diff --git a/packages/utilities/jsx/src/attribute/attribute.ts b/packages/core/src/jsx/jsx-attribute.ts similarity index 86% rename from packages/utilities/jsx/src/attribute/attribute.ts rename to packages/core/src/jsx/jsx-attribute.ts index cfd3f0f7ed..14b521a929 100644 --- a/packages/utilities/jsx/src/attribute/attribute.ts +++ b/packages/core/src/jsx/jsx-attribute.ts @@ -1,26 +1,29 @@ import type { _ } from "@eslint-react/eff"; +import type { RuleContext } from "@eslint-react/kit"; import type { Scope } from "@typescript-eslint/scope-manager"; import type { TSESTree } from "@typescript-eslint/utils"; import * as VAR from "@eslint-react/var"; -import { AST_NODE_TYPES as T } from "@typescript-eslint/types"; -import { getAttributeName } from "./attribute-name"; +import { AST_NODE_TYPES as T } from "@typescript-eslint/types"; +import { getAttributeName } from "./jsx-attribute-name"; /** * Get the JSX attribute node with the given name + * @param context The ESLint rule context * @param name The name of the attribute * @param attributes The attributes to search * @param initialScope The initial scope to use for variable resolution * @returns The JSX attribute node or undefined */ export function getAttribute( + context: RuleContext, name: string, attributes: (TSESTree.JSXAttribute | TSESTree.JSXSpreadAttribute)[], initialScope?: Scope, ): TSESTree.JSXAttribute | TSESTree.JSXSpreadAttribute | _ { return attributes.findLast((attr) => { if (attr.type === T.JSXAttribute) { - return getAttributeName(attr) === name; + return getAttributeName(context, attr) === name; } if (initialScope == null) return false; switch (attr.argument.type) { diff --git a/packages/utilities/jsx/src/jsx-detection-hint.ts b/packages/core/src/jsx/jsx-detection-hint.ts similarity index 100% rename from packages/utilities/jsx/src/jsx-detection-hint.ts rename to packages/core/src/jsx/jsx-detection-hint.ts diff --git a/packages/utilities/jsx/src/jsx-detection.ts b/packages/core/src/jsx/jsx-detection.ts similarity index 100% rename from packages/utilities/jsx/src/jsx-detection.ts rename to packages/core/src/jsx/jsx-detection.ts diff --git a/packages/utilities/jsx/src/element/element-type.ts b/packages/core/src/jsx/jsx-element-type.ts similarity index 50% rename from packages/utilities/jsx/src/element/element-type.ts rename to packages/core/src/jsx/jsx-element-type.ts index 453e4c8a1b..379425a184 100644 --- a/packages/utilities/jsx/src/element/element-type.ts +++ b/packages/core/src/jsx/jsx-element-type.ts @@ -1,15 +1,17 @@ +import type { RuleContext } from "@eslint-react/kit"; import type { TSESTree } from "@typescript-eslint/types"; import { AST_NODE_TYPES as T } from "@typescript-eslint/types"; -import { toString } from "../to-string"; +import { stringifyJsx } from "./jsx-stringify"; /** * Get the stringified type of a JSX element + * @param context The ESLint rule context * @param node The JSX element node * @returns The type of the element */ -export function getElementType(node: TSESTree.JSXElement | TSESTree.JSXFragment) { +export function getElementType(context: RuleContext, node: TSESTree.JSXElement | TSESTree.JSXFragment) { if (node.type === T.JSXFragment) { return ""; } - return toString(node.openingElement.name); + return stringifyJsx(node.openingElement.name); } diff --git a/packages/utilities/jsx/src/attribute/has.ts b/packages/core/src/jsx/jsx-has.ts similarity index 56% rename from packages/utilities/jsx/src/attribute/has.ts rename to packages/core/src/jsx/jsx-has.ts index 40140d1fec..7c38f769e0 100644 --- a/packages/utilities/jsx/src/attribute/has.ts +++ b/packages/core/src/jsx/jsx-has.ts @@ -1,27 +1,31 @@ +import type { RuleContext } from "@eslint-react/kit"; import type { Scope } from "@typescript-eslint/scope-manager"; import type { TSESTree } from "@typescript-eslint/types"; -import { getAttribute } from "./attribute"; +import { getAttribute } from "./jsx-attribute"; export function hasAttribute( + context: RuleContext, name: string, attributes: TSESTree.JSXOpeningElement["attributes"], initialScope?: Scope, ) { - return getAttribute(name, attributes, initialScope) != null; + return getAttribute(context, name, attributes, initialScope) != null; } export function hasAnyAttribute( + context: RuleContext, names: string[], attributes: TSESTree.JSXOpeningElement["attributes"], initialScope?: Scope, ) { - return names.some((n) => hasAttribute(n, attributes, initialScope)); + return names.some((n) => hasAttribute(context, n, attributes, initialScope)); } export function hasEveryAttribute( + context: RuleContext, names: string[], attributes: TSESTree.JSXOpeningElement["attributes"], initialScope?: Scope, ) { - return names.every((n) => hasAttribute(n, attributes, initialScope)); + return names.every((n) => hasAttribute(context, n, attributes, initialScope)); } diff --git a/packages/utilities/jsx/src/attribute/hierarchy.ts b/packages/core/src/jsx/jsx-hierarchy.ts similarity index 100% rename from packages/utilities/jsx/src/attribute/hierarchy.ts rename to packages/core/src/jsx/jsx-hierarchy.ts diff --git a/packages/utilities/jsx/src/element/is.ts b/packages/core/src/jsx/jsx-is.ts similarity index 52% rename from packages/utilities/jsx/src/element/is.ts rename to packages/core/src/jsx/jsx-is.ts index 8d89ae08d7..9bed93f212 100644 --- a/packages/utilities/jsx/src/element/is.ts +++ b/packages/core/src/jsx/jsx-is.ts @@ -1,34 +1,39 @@ import type { _ } from "@eslint-react/eff"; +import type { RuleContext } from "@eslint-react/kit"; import type { Scope } from "@typescript-eslint/scope-manager"; import type { TSESTree } from "@typescript-eslint/types"; import { AST_NODE_TYPES as T } from "@typescript-eslint/types"; -import { hasAttribute } from "../attribute"; -import { getElementType } from "./element-type"; +import { getElementType } from "./jsx-element-type"; +import { hasAttribute } from "./jsx-has"; -export function isHostElement(node: TSESTree.Node) { +export function isHostElement(context: RuleContext, node: TSESTree.Node) { return node.type === T.JSXElement && node.openingElement.name.type === T.JSXIdentifier && /^[a-z]/u.test(node.openingElement.name.name); } -export function isKeyedElement(node: TSESTree.Node, initialScope?: Scope) { +export function isKeyedElement(context: RuleContext, node: TSESTree.Node, initialScope?: Scope) { return node.type === T.JSXElement - && hasAttribute("key", node.openingElement.attributes, initialScope); + && hasAttribute(context, "key", node.openingElement.attributes, initialScope); } -function isFragmentElement(node: TSESTree.Node | null | _, allowJSXFragment?: false): node is TSESTree.JSXElement; -function isFragmentElement( +export function isFragmentElement( + context: RuleContext, + node: TSESTree.Node | null | _, + allowJSXFragment?: false, +): node is TSESTree.JSXElement; +export function isFragmentElement( + context: RuleContext, node: TSESTree.Node | null | _, allowJSXFragment?: true, ): node is TSESTree.JSXElement | TSESTree.JSXFragment; -function isFragmentElement( +export function isFragmentElement( + context: RuleContext, node: TSESTree.Node | null | _, allowJSXFragment = false, ): node is TSESTree.JSXElement | TSESTree.JSXFragment { if (node == null) return false; if (node.type !== T.JSXElement && node.type !== T.JSXFragment) return false; if (node.type === T.JSXFragment) return allowJSXFragment; - return getElementType(node).split(".").at(-1) === "Fragment"; + return getElementType(context, node).split(".").at(-1) === "Fragment"; } - -export { isFragmentElement }; diff --git a/packages/utilities/jsx/src/to-string.ts b/packages/core/src/jsx/jsx-stringify.ts similarity index 82% rename from packages/utilities/jsx/src/to-string.ts rename to packages/core/src/jsx/jsx-stringify.ts index 9b6382d227..03f497500a 100644 --- a/packages/utilities/jsx/src/to-string.ts +++ b/packages/core/src/jsx/jsx-stringify.ts @@ -6,7 +6,7 @@ import { AST_NODE_TYPES as T } from "@typescript-eslint/types"; * @param node The JSX node * @returns The stringified representation */ -export function toString( +export function stringifyJsx( node: | TSESTree.JSXIdentifier | TSESTree.JSXMemberExpression @@ -23,13 +23,13 @@ export function toString( case T.JSXNamespacedName: return `${node.namespace.name}:${node.name.name}`; case T.JSXMemberExpression: - return `${toString(node.object)}.${toString(node.property)}`; + return `${stringifyJsx(node.object)}.${stringifyJsx(node.property)}`; case T.JSXText: return node.value; case T.JSXOpeningElement: - return `<${toString(node.name)}>`; + return `<${stringifyJsx(node.name)}>`; case T.JSXClosingElement: - return ``; + return ``; case T.JSXOpeningFragment: return "<>"; case T.JSXClosingFragment: diff --git a/packages/core/src/semantic/index.ts b/packages/core/src/semantic/index.ts new file mode 100644 index 0000000000..679155a31f --- /dev/null +++ b/packages/core/src/semantic/index.ts @@ -0,0 +1,2 @@ +export type * from "./semantic-entry"; +export type * from "./semantic-node"; diff --git a/packages/core/src/semantic-entry.ts b/packages/core/src/semantic/semantic-entry.ts similarity index 69% rename from packages/core/src/semantic-entry.ts rename to packages/core/src/semantic/semantic-entry.ts index 2ca042f3ab..38d67d452b 100644 --- a/packages/core/src/semantic-entry.ts +++ b/packages/core/src/semantic/semantic-entry.ts @@ -1,6 +1,6 @@ import type { TSESTree } from "@typescript-eslint/types"; -import type { ComponentPhaseKind } from "./component/component-phase"; +import type { ComponentPhaseKind } from "../component/component-phase"; export interface SemanticEntry { // kind: string; diff --git a/packages/core/src/semantic-node.ts b/packages/core/src/semantic/semantic-node.ts similarity index 100% rename from packages/core/src/semantic-node.ts rename to packages/core/src/semantic/semantic-node.ts diff --git a/packages/plugins/eslint-plugin-react-debug/package.json b/packages/plugins/eslint-plugin-react-debug/package.json index c3e9b2d676..555b07fd2c 100644 --- a/packages/plugins/eslint-plugin-react-debug/package.json +++ b/packages/plugins/eslint-plugin-react-debug/package.json @@ -52,7 +52,6 @@ "@eslint-react/ast": "workspace:*", "@eslint-react/core": "workspace:*", "@eslint-react/eff": "workspace:*", - "@eslint-react/jsx": "workspace:*", "@eslint-react/kit": "workspace:*", "@eslint-react/shared": "workspace:*", "@eslint-react/var": "workspace:*", diff --git a/packages/plugins/eslint-plugin-react-debug/src/rules/jsx.ts b/packages/plugins/eslint-plugin-react-debug/src/rules/jsx.ts index c4ea02c0c8..c8c9fc7caa 100644 --- a/packages/plugins/eslint-plugin-react-debug/src/rules/jsx.ts +++ b/packages/plugins/eslint-plugin-react-debug/src/rules/jsx.ts @@ -1,7 +1,7 @@ import type { RuleListener } from "@typescript-eslint/utils/ts-eslint"; import type { CamelCase } from "string-ts"; +import * as ER from "@eslint-react/core"; import { flow } from "@eslint-react/eff"; -import * as JSX from "@eslint-react/jsx"; import { JsxConfig, Report, type RuleContext, type RuleFeature } from "@eslint-react/kit"; import { AST_NODE_TYPES as T, type TSESTree } from "@typescript-eslint/types"; import { match, P } from "ts-pattern"; @@ -41,14 +41,14 @@ export function create(context: RuleContext): RuleListener { ...jsxConfigFromAnnotation, }; - function getReportDescriptor(node: TSESTree.JSXElement | TSESTree.JSXFragment) { - return { + function getReportDescriptor(context: RuleContext) { + return (node: TSESTree.JSXElement | TSESTree.JSXFragment) => ({ messageId: "jsx", node, data: { json: JSON.stringify({ type: match(node) - .with({ type: T.JSXElement }, (n) => JSX.isFragmentElement(n) ? "fragment" : "element") + .with({ type: T.JSXElement }, (n) => ER.isFragmentElement(context, n) ? "fragment" : "element") .with({ type: T.JSXFragment }, () => "fragment") .exhaustive(), jsx: match(jsxConfig.jsx) @@ -67,9 +67,9 @@ export function create(context: RuleContext): RuleListener { .otherwise(() => "classic"), }), }, - } as const; + } as const); } return { - "JSXElement, JSXFragment": flow(getReportDescriptor, Report.make(context).send), + "JSXElement, JSXFragment": flow(getReportDescriptor(context), Report.make(context).send), }; } diff --git a/packages/plugins/eslint-plugin-react-dom/package.json b/packages/plugins/eslint-plugin-react-dom/package.json index 4a23d0a296..16548ffed3 100644 --- a/packages/plugins/eslint-plugin-react-dom/package.json +++ b/packages/plugins/eslint-plugin-react-dom/package.json @@ -52,7 +52,6 @@ "@eslint-react/ast": "workspace:*", "@eslint-react/core": "workspace:*", "@eslint-react/eff": "workspace:*", - "@eslint-react/jsx": "workspace:*", "@eslint-react/kit": "workspace:*", "@eslint-react/shared": "workspace:*", "@eslint-react/var": "workspace:*", diff --git a/packages/plugins/eslint-plugin-react-dom/src/rules/no-dangerously-set-innerhtml-with-children.ts b/packages/plugins/eslint-plugin-react-dom/src/rules/no-dangerously-set-innerhtml-with-children.ts index d86194f74c..b22ee5d1c3 100644 --- a/packages/plugins/eslint-plugin-react-dom/src/rules/no-dangerously-set-innerhtml-with-children.ts +++ b/packages/plugins/eslint-plugin-react-dom/src/rules/no-dangerously-set-innerhtml-with-children.ts @@ -3,7 +3,7 @@ import type { TSESTree } from "@typescript-eslint/types"; import type { RuleListener } from "@typescript-eslint/utils/ts-eslint"; import type { CamelCase } from "string-ts"; import * as AST from "@eslint-react/ast"; -import * as JSX from "@eslint-react/jsx"; +import * as ER from "@eslint-react/core"; import { createRule } from "../utils"; @@ -38,8 +38,8 @@ export function create(context: RuleContext): RuleListener { JSXElement(node) { const attributes = node.openingElement.attributes; const initialScope = context.sourceCode.getScope(node); - const hasChildren = hasChildrenWithin(node) || JSX.hasAttribute("children", attributes, initialScope); - if (hasChildren && JSX.hasAttribute("dangerouslySetInnerHTML", attributes, initialScope)) { + const hasChildren = hasChildrenWithin(node) || ER.hasAttribute(context, "children", attributes, initialScope); + if (hasChildren && ER.hasAttribute(context, "dangerouslySetInnerHTML", attributes, initialScope)) { context.report({ messageId: "noDangerouslySetInnerhtmlWithChildren", node, diff --git a/packages/plugins/eslint-plugin-react-dom/src/rules/no-dangerously-set-innerhtml.ts b/packages/plugins/eslint-plugin-react-dom/src/rules/no-dangerously-set-innerhtml.ts index af62039803..b8d070d1af 100644 --- a/packages/plugins/eslint-plugin-react-dom/src/rules/no-dangerously-set-innerhtml.ts +++ b/packages/plugins/eslint-plugin-react-dom/src/rules/no-dangerously-set-innerhtml.ts @@ -1,7 +1,7 @@ import type { RuleContext, RuleFeature } from "@eslint-react/kit"; import type { RuleListener } from "@typescript-eslint/utils/ts-eslint"; import type { CamelCase } from "string-ts"; -import * as JSX from "@eslint-react/jsx"; +import * as ER from "@eslint-react/core"; import { createRule } from "../utils"; @@ -34,7 +34,8 @@ export function create(context: RuleContext): RuleListener { return { JSXElement(node) { const attributes = node.openingElement.attributes; - const attribute = JSX.getAttribute( + const attribute = ER.getAttribute( + context, "dangerouslySetInnerHTML", attributes, context.sourceCode.getScope(node), diff --git a/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-button-type.ts b/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-button-type.ts index 659339501a..3aa7e959d6 100644 --- a/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-button-type.ts +++ b/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-button-type.ts @@ -1,8 +1,7 @@ import type { RuleContext, RuleFeature } from "@eslint-react/kit"; import type { RuleListener } from "@typescript-eslint/utils/ts-eslint"; import type { CamelCase } from "string-ts"; -import * as JSX from "@eslint-react/jsx"; - +import * as ER from "@eslint-react/core"; import { createJsxElementResolver, createRule, findCustomComponentProp } from "../utils"; export const RULE_NAME = "no-missing-button-type"; @@ -34,17 +33,20 @@ export function create(context: RuleContext): RuleListener { JSXElement(node) { const { attributes, domElementType } = resolver.resolve(node); if (domElementType !== "button") return; - const elementScope = context.sourceCode.getScope(node); const customComponentProp = findCustomComponentProp("type", attributes); const propNameOnJsx = customComponentProp?.name ?? "type"; - const attributeNode = JSX.getAttribute( + const attributeNode = ER.getAttribute( + context, propNameOnJsx, node.openingElement.attributes, - elementScope, + context.sourceCode.getScope(node), ); if (attributeNode != null) { - const attributeScope = context.sourceCode.getScope(attributeNode); - const attributeValue = JSX.getAttributeValue(attributeNode, propNameOnJsx, attributeScope); + const attributeValue = ER.getAttributeValue( + context, + attributeNode, + propNameOnJsx, + ); if (attributeValue.kind === "some" && typeof attributeValue.value !== "string") { context.report({ messageId: "noMissingButtonType", diff --git a/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-iframe-sandbox.ts b/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-iframe-sandbox.ts index 7b876b9622..884fe45ec5 100644 --- a/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-iframe-sandbox.ts +++ b/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-iframe-sandbox.ts @@ -1,9 +1,9 @@ import type { RuleContext, RuleFeature } from "@eslint-react/kit"; import type { RuleListener } from "@typescript-eslint/utils/ts-eslint"; import type { CamelCase } from "string-ts"; -import * as JSX from "@eslint-react/jsx"; +import * as ER from "@eslint-react/core"; -import { createJsxElementResolver, createRule, findCustomComponent, findCustomComponentProp } from "../utils"; +import { createJsxElementResolver, createRule, findCustomComponentProp } from "../utils"; export const RULE_NAME = "no-missing-iframe-sandbox"; @@ -60,17 +60,20 @@ export function create(context: RuleContext): RuleListener { JSXElement(node) { const { attributes, domElementType } = resolver.resolve(node); if (domElementType !== "iframe") return; - const elementScope = context.sourceCode.getScope(node); const customComponentProp = findCustomComponentProp("sandbox", attributes); const propNameOnJsx = customComponentProp?.name ?? "sandbox"; - const attributeNode = JSX.getAttribute( + const attributeNode = ER.getAttribute( + context, propNameOnJsx, node.openingElement.attributes, - elementScope, + context.sourceCode.getScope(node), ); if (attributeNode != null) { - const attributeScope = context.sourceCode.getScope(attributeNode); - const attributeValue = JSX.getAttributeValue(attributeNode, propNameOnJsx, attributeScope); + const attributeValue = ER.getAttributeValue( + context, + attributeNode, + propNameOnJsx, + ); if (attributeValue.kind === "some" && hasValidSandBox(attributeValue.value)) return; context.report({ messageId: "noMissingIframeSandbox", diff --git a/packages/plugins/eslint-plugin-react-dom/src/rules/no-namespace.ts b/packages/plugins/eslint-plugin-react-dom/src/rules/no-namespace.ts index c19974cbb6..648849ae2b 100644 --- a/packages/plugins/eslint-plugin-react-dom/src/rules/no-namespace.ts +++ b/packages/plugins/eslint-plugin-react-dom/src/rules/no-namespace.ts @@ -1,8 +1,7 @@ import type { RuleContext, RuleFeature } from "@eslint-react/kit"; import type { RuleListener } from "@typescript-eslint/utils/ts-eslint"; import type { CamelCase } from "string-ts"; -import * as JSX from "@eslint-react/jsx"; - +import * as ER from "@eslint-react/core"; import { createRule } from "../utils"; export const RULE_NAME = "no-namespace"; @@ -31,7 +30,7 @@ export default createRule<[], MessageID>({ export function create(context: RuleContext): RuleListener { return { JSXElement(node) { - const name = JSX.getElementType(node); + const name = ER.getElementType(context, node); if (typeof name !== "string" || !name.includes(":")) { return; } diff --git a/packages/plugins/eslint-plugin-react-dom/src/rules/no-script-url.ts b/packages/plugins/eslint-plugin-react-dom/src/rules/no-script-url.ts index 4606bbb57c..dca8057787 100644 --- a/packages/plugins/eslint-plugin-react-dom/src/rules/no-script-url.ts +++ b/packages/plugins/eslint-plugin-react-dom/src/rules/no-script-url.ts @@ -1,7 +1,7 @@ import type { RuleContext, RuleFeature } from "@eslint-react/kit"; import type { RuleListener } from "@typescript-eslint/utils/ts-eslint"; import type { CamelCase } from "string-ts"; -import * as JSX from "@eslint-react/jsx"; +import * as ER from "@eslint-react/core"; import { RE } from "@eslint-react/kit"; import { AST_NODE_TYPES as T } from "@typescript-eslint/types"; @@ -40,9 +40,8 @@ export function create(context: RuleContext): RuleListener { if (node.name.type !== T.JSXIdentifier || node.value == null) { return; } - const attributeScope = context.sourceCode.getScope(node); - const attributeName = JSX.getAttributeName(node); - const attributeValue = JSX.getAttributeValue(node, attributeName, attributeScope); + const attributeName = ER.getAttributeName(context, node); + const attributeValue = ER.getAttributeValue(context, node, attributeName); if (attributeValue.kind === "none" || typeof attributeValue.value !== "string") return; if (RE.JAVASCRIPT_PROTOCOL.test(attributeValue.value)) { context.report({ diff --git a/packages/plugins/eslint-plugin-react-dom/src/rules/no-unsafe-iframe-sandbox.ts b/packages/plugins/eslint-plugin-react-dom/src/rules/no-unsafe-iframe-sandbox.ts index 1c4c4e34c9..a679f4e0d7 100644 --- a/packages/plugins/eslint-plugin-react-dom/src/rules/no-unsafe-iframe-sandbox.ts +++ b/packages/plugins/eslint-plugin-react-dom/src/rules/no-unsafe-iframe-sandbox.ts @@ -1,7 +1,7 @@ import type { RuleContext, RuleFeature } from "@eslint-react/kit"; import type { RuleListener } from "@typescript-eslint/utils/ts-eslint"; import type { CamelCase } from "string-ts"; -import * as JSX from "@eslint-react/jsx"; +import * as ER from "@eslint-react/core"; import { createJsxElementResolver, createRule, findCustomComponentProp } from "../utils"; @@ -45,17 +45,20 @@ export function create(context: RuleContext): RuleListener { JSXElement(node) { const { attributes, domElementType } = resolver.resolve(node); if (domElementType !== "iframe") return; - const elementScope = context.sourceCode.getScope(node); const customComponentProp = findCustomComponentProp("sandbox", attributes); const propNameOnJsx = customComponentProp?.name ?? "sandbox"; - const attributeNode = JSX.getAttribute( + const attributeNode = ER.getAttribute( + context, propNameOnJsx, node.openingElement.attributes, - elementScope, + context.sourceCode.getScope(node), ); if (attributeNode != null) { - const attributeScope = context.sourceCode.getScope(attributeNode); - const attributeValue = JSX.getAttributeValue(attributeNode, propNameOnJsx, attributeScope); + const attributeValue = ER.getAttributeValue( + context, + attributeNode, + propNameOnJsx, + ); if (attributeValue.kind === "some" && !hasSafeSandbox(attributeValue.value)) { context.report({ messageId: "noUnsafeIframeSandbox", diff --git a/packages/plugins/eslint-plugin-react-dom/src/rules/no-unsafe-target-blank.ts b/packages/plugins/eslint-plugin-react-dom/src/rules/no-unsafe-target-blank.ts index d37228dcde..8376b5a276 100644 --- a/packages/plugins/eslint-plugin-react-dom/src/rules/no-unsafe-target-blank.ts +++ b/packages/plugins/eslint-plugin-react-dom/src/rules/no-unsafe-target-blank.ts @@ -2,8 +2,9 @@ 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 { _ } from "@eslint-react/eff"; -import * as JSX from "@eslint-react/jsx"; import { createJsxElementResolver, createRule, findCustomComponentProp } from "../utils"; @@ -54,14 +55,14 @@ export function create(context: RuleContext): RuleListener { const getAttributeStringValue = (name: string) => { const customComponentProp = findCustomComponentProp(name, attributes); const propNameOnJsx = customComponentProp?.name ?? name; - const attributeNode = JSX.getAttribute( + const attributeNode = ER.getAttribute( + context, propNameOnJsx, node.openingElement.attributes, elementScope, ); if (attributeNode == null) return customComponentProp?.defaultValue; - const attributeScope = context.sourceCode.getScope(attributeNode); - const attributeValue = JSX.getAttributeValue(attributeNode, propNameOnJsx, attributeScope); + const attributeValue = ER.getAttributeValue(context, attributeNode, propNameOnJsx); if (attributeValue.kind === "some" && typeof attributeValue.value === "string") { return attributeValue.value; } diff --git a/packages/plugins/eslint-plugin-react-dom/src/rules/no-void-elements-with-children.ts b/packages/plugins/eslint-plugin-react-dom/src/rules/no-void-elements-with-children.ts index 1125893ae2..518653f31c 100644 --- a/packages/plugins/eslint-plugin-react-dom/src/rules/no-void-elements-with-children.ts +++ b/packages/plugins/eslint-plugin-react-dom/src/rules/no-void-elements-with-children.ts @@ -1,7 +1,7 @@ import type { RuleContext, RuleFeature } from "@eslint-react/kit"; import type { RuleListener } from "@typescript-eslint/utils/ts-eslint"; import type { CamelCase } from "string-ts"; -import * as JSX from "@eslint-react/jsx"; +import * as ER from "@eslint-react/core"; import { createRule } from "../utils"; @@ -51,7 +51,7 @@ export default createRule<[], MessageID>({ export function create(context: RuleContext): RuleListener { return { JSXElement(node) { - const elementName = JSX.getElementType(node); + const elementName = ER.getElementType(context, node); if (elementName.length === 0 || !voidElements.has(elementName)) { return; } @@ -66,7 +66,7 @@ export function create(context: RuleContext): RuleListener { } const { attributes } = node.openingElement; const initialScope = context.sourceCode.getScope(node); - const hasAttribute = (name: string) => JSX.hasAttribute(name, attributes, initialScope); + const hasAttribute = (name: string) => ER.hasAttribute(context, name, attributes, initialScope); if (hasAttribute("children") || hasAttribute("dangerouslySetInnerHTML")) { // e.g.
context.report({ diff --git a/packages/plugins/eslint-plugin-react-dom/src/utils/create-jsx-element-resolver.ts b/packages/plugins/eslint-plugin-react-dom/src/utils/create-jsx-element-resolver.ts index 400aa18ae1..b8381f1e6b 100644 --- a/packages/plugins/eslint-plugin-react-dom/src/utils/create-jsx-element-resolver.ts +++ b/packages/plugins/eslint-plugin-react-dom/src/utils/create-jsx-element-resolver.ts @@ -1,6 +1,7 @@ import type { RuleContext } from "@eslint-react/kit"; import type { TSESTree } from "@typescript-eslint/types"; -import * as JSX from "@eslint-react/jsx"; + +import * as ER from "@eslint-react/core"; import { getSettingsFromContext } from "@eslint-react/shared"; export function createJsxElementResolver(context: RuleContext) { @@ -10,7 +11,7 @@ export function createJsxElementResolver(context: RuleContext) { } = getSettingsFromContext(context); return { resolve(node: TSESTree.JSXElement) { - const name = JSX.getElementType(node); + const name = ER.getElementType(context, node); const component = additionalComponents .findLast((c) => c.name === name || c.re.test(name)); const result = { @@ -22,16 +23,17 @@ export function createJsxElementResolver(context: RuleContext) { return result; } const initialScope = context.sourceCode.getScope(node); - const polymorphicPropAttr = JSX.getAttribute( + const polymorphicPropAttr = ER.getAttribute( + context, polymorphicPropName, node.openingElement.attributes, initialScope, ); if (polymorphicPropAttr != null) { - const polymorphicPropValue = JSX.getAttributeValue( + const polymorphicPropValue = ER.getAttributeValue( + context, polymorphicPropAttr, polymorphicPropName, - initialScope, ); if (polymorphicPropValue.kind === "some" && typeof polymorphicPropValue.value === "string") { return { diff --git a/packages/plugins/eslint-plugin-react-hooks-extra/package.json b/packages/plugins/eslint-plugin-react-hooks-extra/package.json index 4327570874..9178072878 100644 --- a/packages/plugins/eslint-plugin-react-hooks-extra/package.json +++ b/packages/plugins/eslint-plugin-react-hooks-extra/package.json @@ -53,7 +53,6 @@ "@eslint-react/ast": "workspace:*", "@eslint-react/core": "workspace:*", "@eslint-react/eff": "workspace:*", - "@eslint-react/jsx": "workspace:*", "@eslint-react/kit": "workspace:*", "@eslint-react/shared": "workspace:*", "@eslint-react/var": "workspace:*", diff --git a/packages/plugins/eslint-plugin-react-hooks-extra/src/hooks/use-no-direct-set-state-in-use-effect.ts b/packages/plugins/eslint-plugin-react-hooks-extra/src/hooks/use-no-direct-set-state-in-use-effect.ts index 12a3d290dc..099a4cfb4e 100644 --- a/packages/plugins/eslint-plugin-react-hooks-extra/src/hooks/use-no-direct-set-state-in-use-effect.ts +++ b/packages/plugins/eslint-plugin-react-hooks-extra/src/hooks/use-no-direct-set-state-in-use-effect.ts @@ -221,7 +221,7 @@ export function useNoDirectSetStateInUseEffect( const setStateCalls = getSetStateCalls(name, context.sourceCode.getScope(callee)); for (const setStateCall of setStateCalls) { onViolation(context, setStateCall, { - name: AST.toReadableNodeName(setStateCall, (n) => context.sourceCode.getText(n)), + name: AST.stringify(setStateCall, (n) => context.sourceCode.getText(n)), }); } } @@ -229,7 +229,7 @@ export function useNoDirectSetStateInUseEffect( const setStateCalls = getSetStateCalls(id.name, context.sourceCode.getScope(id)); for (const setStateCall of setStateCalls) { onViolation(context, setStateCall, { - name: AST.toReadableNodeName(setStateCall, (n) => context.sourceCode.getText(n)), + name: AST.stringify(setStateCall, (n) => context.sourceCode.getText(n)), }); } } diff --git a/packages/plugins/eslint-plugin-react-naming-convention/package.json b/packages/plugins/eslint-plugin-react-naming-convention/package.json index c3f13024e8..c8b5a03f74 100644 --- a/packages/plugins/eslint-plugin-react-naming-convention/package.json +++ b/packages/plugins/eslint-plugin-react-naming-convention/package.json @@ -52,7 +52,6 @@ "@eslint-react/ast": "workspace:*", "@eslint-react/core": "workspace:*", "@eslint-react/eff": "workspace:*", - "@eslint-react/jsx": "workspace:*", "@eslint-react/kit": "workspace:*", "@eslint-react/shared": "workspace:*", "@eslint-react/var": "workspace:*", diff --git a/packages/plugins/eslint-plugin-react-web-api/package.json b/packages/plugins/eslint-plugin-react-web-api/package.json index e66f772b15..e194d9c5f1 100644 --- a/packages/plugins/eslint-plugin-react-web-api/package.json +++ b/packages/plugins/eslint-plugin-react-web-api/package.json @@ -52,7 +52,6 @@ "@eslint-react/ast": "workspace:*", "@eslint-react/core": "workspace:*", "@eslint-react/eff": "workspace:*", - "@eslint-react/jsx": "workspace:*", "@eslint-react/kit": "workspace:*", "@eslint-react/shared": "workspace:*", "@eslint-react/var": "workspace:*", diff --git a/packages/plugins/eslint-plugin-react-x/package.json b/packages/plugins/eslint-plugin-react-x/package.json index 13039d47c0..01114d121c 100644 --- a/packages/plugins/eslint-plugin-react-x/package.json +++ b/packages/plugins/eslint-plugin-react-x/package.json @@ -51,7 +51,6 @@ "@eslint-react/ast": "workspace:*", "@eslint-react/core": "workspace:*", "@eslint-react/eff": "workspace:*", - "@eslint-react/jsx": "workspace:*", "@eslint-react/kit": "workspace:*", "@eslint-react/shared": "workspace:*", "@eslint-react/var": "workspace:*", diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/avoid-shorthand-boolean.ts b/packages/plugins/eslint-plugin-react-x/src/rules/avoid-shorthand-boolean.ts index 2cbdf467f8..15bd7f4685 100644 --- a/packages/plugins/eslint-plugin-react-x/src/rules/avoid-shorthand-boolean.ts +++ b/packages/plugins/eslint-plugin-react-x/src/rules/avoid-shorthand-boolean.ts @@ -1,7 +1,7 @@ import type { RuleFeature } from "@eslint-react/kit"; import type { RuleContext, RuleListener } from "@typescript-eslint/utils/ts-eslint"; import type { CamelCase } from "string-ts"; -import * as JSX from "@eslint-react/jsx"; +import * as ER from "@eslint-react/core"; import { createRule } from "../utils"; @@ -38,7 +38,7 @@ export function create(context: RuleContext): RuleListener { messageId: "avoidShorthandBoolean", node, data: { - propName: JSX.getAttributeName(node), + propName: ER.getAttributeName(context, node), }, fix: (fixer) => fixer.insertTextAfter(node.name, `={true}`), }); diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/no-children-prop.ts b/packages/plugins/eslint-plugin-react-x/src/rules/no-children-prop.ts index d8494190a3..a572a17bb7 100644 --- a/packages/plugins/eslint-plugin-react-x/src/rules/no-children-prop.ts +++ b/packages/plugins/eslint-plugin-react-x/src/rules/no-children-prop.ts @@ -1,7 +1,7 @@ import type { RuleContext, RuleFeature } from "@eslint-react/kit"; import type { RuleListener } from "@typescript-eslint/utils/ts-eslint"; import type { CamelCase } from "string-ts"; -import * as JSX from "@eslint-react/jsx"; +import * as ER from "@eslint-react/core"; import { createRule } from "../utils"; @@ -32,7 +32,7 @@ export function create(context: RuleContext): RuleListener { return { JSXElement(node) { const attributes = node.openingElement.attributes; - const childrenProp = JSX.getAttribute("children", attributes, context.sourceCode.getScope(node)); + const childrenProp = ER.getAttribute(context, "children", attributes, context.sourceCode.getScope(node)); if (childrenProp != null) { context.report({ messageId: "noChildrenProp", diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/no-context-provider.ts b/packages/plugins/eslint-plugin-react-x/src/rules/no-context-provider.ts index 4e3d653c21..c2872f4f41 100644 --- a/packages/plugins/eslint-plugin-react-x/src/rules/no-context-provider.ts +++ b/packages/plugins/eslint-plugin-react-x/src/rules/no-context-provider.ts @@ -2,7 +2,7 @@ import type { RuleContext, RuleFeature } from "@eslint-react/kit"; import type { RuleListener } from "@typescript-eslint/utils/ts-eslint"; import type { CamelCase } from "string-ts"; import * as ER from "@eslint-react/core"; -import * as JSX from "@eslint-react/jsx"; + import { getSettingsFromContext } from "@eslint-react/shared"; import { compare } from "compare-versions"; @@ -40,7 +40,7 @@ export function create(context: RuleContext): RuleListener { if (compare(version, "19.0.0", "<")) return {}; return { JSXElement(node) { - const fullName = JSX.getElementType(node); + const fullName = ER.getElementType(context, node); const parts = fullName.split("."); const selfName = parts.pop(); const contextFullName = parts.join("."); diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/no-implicit-key.ts b/packages/plugins/eslint-plugin-react-x/src/rules/no-implicit-key.ts index 9b9af2e50b..aa57eee9ee 100644 --- a/packages/plugins/eslint-plugin-react-x/src/rules/no-implicit-key.ts +++ b/packages/plugins/eslint-plugin-react-x/src/rules/no-implicit-key.ts @@ -2,7 +2,9 @@ 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 JSX from "@eslint-react/jsx"; + +import * as ER from "@eslint-react/core"; + import { AST_NODE_TYPES as T } from "@typescript-eslint/types"; import { createRule } from "../utils"; @@ -36,7 +38,7 @@ export function create(context: RuleContext): RuleListener { return { JSXOpeningElement(node: TSESTree.JSXOpeningElement) { const initialScope = context.sourceCode.getScope(node); - const keyPropFound = JSX.getAttribute("key", node.attributes, initialScope); + const keyPropFound = ER.getAttribute(context, "key", node.attributes, initialScope); const keyPropOnElement = node.attributes .some((n) => n.type === T.JSXAttribute diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/no-missing-key.ts b/packages/plugins/eslint-plugin-react-x/src/rules/no-missing-key.ts index 1603ad46c7..43c0ffc9bc 100644 --- a/packages/plugins/eslint-plugin-react-x/src/rules/no-missing-key.ts +++ b/packages/plugins/eslint-plugin-react-x/src/rules/no-missing-key.ts @@ -2,7 +2,7 @@ import type { TSESTree } from "@typescript-eslint/types"; import type { ReportDescriptor, RuleListener } from "@typescript-eslint/utils/ts-eslint"; import * as AST from "@eslint-react/ast"; import * as ER from "@eslint-react/core"; -import * as JSX from "@eslint-react/jsx"; + import { Report, type RuleContext, type RuleFeature } from "@eslint-react/kit"; import { AST_NODE_TYPES as T } from "@typescript-eslint/types"; @@ -43,7 +43,7 @@ export function create(context: RuleContext): RuleListener { switch (node.type) { case T.JSXElement: { const initialScope = context.sourceCode.getScope(node); - if (!JSX.hasAttribute("key", node.openingElement.attributes, initialScope)) { + if (!ER.hasAttribute(context, "key", node.openingElement.attributes, initialScope)) { return { messageId: "missingKey", node, @@ -103,7 +103,7 @@ export function create(context: RuleContext): RuleListener { } const initialScope = context.sourceCode.getScope(node); for (const element of elements) { - if (!JSX.hasAttribute("key", element.openingElement.attributes, initialScope)) { + if (!ER.hasAttribute(context, "key", element.openingElement.attributes, initialScope)) { report.send({ messageId: "missingKey", node: element, diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/no-nested-component-definitions.ts b/packages/plugins/eslint-plugin-react-x/src/rules/no-nested-component-definitions.ts index bc7853a8d5..caf7fc6ed5 100644 --- a/packages/plugins/eslint-plugin-react-x/src/rules/no-nested-component-definitions.ts +++ b/packages/plugins/eslint-plugin-react-x/src/rules/no-nested-component-definitions.ts @@ -4,7 +4,6 @@ import type { RuleListener } from "@typescript-eslint/utils/ts-eslint"; import type { CamelCase } from "string-ts"; import * as AST from "@eslint-react/ast"; import * as ER from "@eslint-react/core"; -import * as JSX from "@eslint-react/jsx"; import { AST_NODE_TYPES as T } from "@typescript-eslint/types"; import { createRule } from "../utils"; @@ -153,7 +152,7 @@ export function create(context: RuleContext): RuleListener { */ function isInsideJSXAttributeValue(node: AST.TSESTreeFunction) { return node.parent.type === T.JSXAttribute - || JSX.findParentAttribute(node, (n) => n.value?.type === T.JSXExpressionContainer) != null; + || ER.findParentAttribute(node, (n) => n.value?.type === T.JSXExpressionContainer) != null; } /** diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/no-nested-lazy-component-declarations.ts b/packages/plugins/eslint-plugin-react-x/src/rules/no-nested-lazy-component-declarations.ts index 9f6a1d5d92..4ca4367d54 100644 --- a/packages/plugins/eslint-plugin-react-x/src/rules/no-nested-lazy-component-declarations.ts +++ b/packages/plugins/eslint-plugin-react-x/src/rules/no-nested-lazy-component-declarations.ts @@ -4,7 +4,6 @@ import type { RuleListener } from "@typescript-eslint/utils/ts-eslint"; import type { CamelCase } from "string-ts"; import * as AST from "@eslint-react/ast"; import * as ER from "@eslint-react/core"; -import * as JSX from "@eslint-react/jsx"; import { AST_NODE_TYPES as T } from "@typescript-eslint/types"; import { createRule } from "../utils"; @@ -66,7 +65,7 @@ export function create(context: RuleContext): RuleListener { for (const lazy of lazyComponentDeclarations) { const significantParent = AST.findParentNode(lazy, (n) => { - if (JSX.isJSX(n)) return true; + if (ER.isJSX(n)) return true; if (n.type === T.CallExpression) { return ER.isReactHookCall(n) || ER.isCreateElementCall(context, n) || ER.isCreateContextCall(context, n); } diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/no-unstable-context-value.ts b/packages/plugins/eslint-plugin-react-x/src/rules/no-unstable-context-value.ts index fa2c947e7c..a64c30f6fb 100644 --- a/packages/plugins/eslint-plugin-react-x/src/rules/no-unstable-context-value.ts +++ b/packages/plugins/eslint-plugin-react-x/src/rules/no-unstable-context-value.ts @@ -3,7 +3,7 @@ import type { RuleListener } from "@typescript-eslint/utils/ts-eslint"; import * as AST from "@eslint-react/ast"; import * as ER from "@eslint-react/core"; import { getOrElseUpdate } from "@eslint-react/eff"; -import * as JSX from "@eslint-react/jsx"; + import { getSettingsFromContext } from "@eslint-react/shared"; import * as VAR from "@eslint-react/var"; import { AST_NODE_TYPES as T } from "@typescript-eslint/types"; @@ -45,7 +45,7 @@ export function create(context: RuleContext): RuleListener { return { ...listeners, JSXOpeningElement(node) { - const fullName = JSX.getElementType(node.parent); + const fullName = ER.getElementType(context, node.parent); const selfName = fullName.split(".").at(-1); if (selfName == null) return; if (!isContextName(selfName, isReact18OrBelow)) return; 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 26991077da..e24c84b075 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 @@ -2,7 +2,9 @@ import type { RuleContext, RuleFeature } from "@eslint-react/kit"; import type { TSESTree } from "@typescript-eslint/utils"; import type { RuleFixer, RuleListener } from "@typescript-eslint/utils/ts-eslint"; import * as AST from "@eslint-react/ast"; -import * as JSX from "@eslint-react/jsx"; + +import * as ER from "@eslint-react/core"; + import { AST_NODE_TYPES as T } from "@typescript-eslint/types"; import { createRule } from "../utils"; @@ -57,7 +59,7 @@ export function create(context: RuleContext, [option]: Optio const { allowExpressions = true } = option; return { JSXElement(node) { - if (!JSX.isFragmentElement(node)) return; + if (!ER.isFragmentElement(context, node)) return; checkNode(context, node, allowExpressions); }, JSXFragment(node) { @@ -81,7 +83,7 @@ function isWhiteSpace(node: TSESTree.JSXText | TSESTree.Literal) { * @returns boolean */ function isPaddingSpaces(node: TSESTree.Node) { - return JSX.isJsxText(node) + return ER.isJsxText(node) && isWhiteSpace(node) && node.raw.includes("\n"); } @@ -103,11 +105,11 @@ function checkNode( ) { const initialScope = context.sourceCode.getScope(node); // return if the fragment is keyed (e.g. ) - if (JSX.isKeyedElement(node, initialScope)) { + if (ER.isKeyedElement(context, node, initialScope)) { return; } // report if the fragment is placed inside a host component (e.g.
<>
) - if (JSX.isHostElement(node.parent)) { + if (ER.isHostElement(context, node.parent)) { context.report({ messageId: "uselessFragment", node, @@ -135,7 +137,7 @@ function checkNode( case allowExpressions && !isChildElement && node.children.length === 1 - && JSX.isJsxText(node.children.at(0)): { + && ER.isJsxText(node.children.at(0)): { return; } // <>hello, world @@ -188,7 +190,7 @@ function checkNode( } function getFix(context: RuleContext, node: TSESTree.JSXElement | TSESTree.JSXFragment) { - if (!canFix(node)) return null; + 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; @@ -201,10 +203,10 @@ function getFix(context: RuleContext, node: TSESTree.JSXElement | TSESTree.JSXFr }; } -function canFix(node: TSESTree.JSXElement | TSESTree.JSXFragment) { +function canFix(context: RuleContext, node: TSESTree.JSXElement | TSESTree.JSXFragment) { 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 JSX.isHostElement(node.parent); + return ER.isHostElement(context, node.parent); } // Not safe to fix fragments without a jsx parent. // const a = <> @@ -213,7 +215,7 @@ function canFix(node: TSESTree.JSXElement | TSESTree.JSXFragment) { } // dprint-ignore // const a = <>{meow} - if (node.children.some((child) => (JSX.isJsxText(child) && !isWhiteSpace(child)) || AST.is(T.JSXExpressionContainer)(child))) { + if (node.children.some((child) => (ER.isJsxText(child) && !isWhiteSpace(child)) || AST.is(T.JSXExpressionContainer)(child))) { return false; } return true; 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 index f8c49fa87a..c70741d5cb 100644 --- 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 @@ -2,7 +2,9 @@ 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 JSX from "@eslint-react/jsx"; + +import * as ER from "@eslint-react/core"; + import { AST_NODE_TYPES as T } from "@typescript-eslint/types"; import { createRule } from "../utils"; @@ -37,7 +39,7 @@ export function create(context: RuleContext): RuleListener { return { JSXAttribute(node: TSESTree.JSXAttribute) { const { value } = node; - const propName = JSX.getAttributeName(node); + const propName = ER.getAttributeName(context, node); const hasValueTrue = value?.type === T.JSXExpressionContainer && value.expression.type === T.Literal && value.expression.value === true; 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 index 7b292bd22b..6dfe6ad480 100644 --- 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 @@ -2,7 +2,7 @@ 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 JSX from "@eslint-react/jsx"; +import * as ER from "@eslint-react/core"; import { createRule } from "../utils"; @@ -35,7 +35,7 @@ export default createRule<[], MessageID>({ export function create(context: RuleContext): RuleListener { return { JSXElement(node: TSESTree.JSXElement) { - if (!JSX.isFragmentElement(node)) return; + if (!ER.isFragmentElement(context, node)) return; const hasAttributes = node.openingElement.attributes.length > 0; if (hasAttributes) { return; diff --git a/packages/utilities/ast/src/index.ts b/packages/utilities/ast/src/index.ts index 061f77bbe4..74c5c7894d 100644 --- a/packages/utilities/ast/src/index.ts +++ b/packages/utilities/ast/src/index.ts @@ -20,6 +20,6 @@ export * from "./is-node-equal"; export * from "./is-process-env-node-env"; export * from "./is-process-env-node-env-compare"; export * from "./is-this-expression"; -export * from "./to-readable-node-name"; +export * from "./stringify"; export * from "./to-readable-node-type"; export type * from "./types"; diff --git a/packages/utilities/ast/src/to-readable-node-name.ts b/packages/utilities/ast/src/stringify.ts similarity index 67% rename from packages/utilities/ast/src/to-readable-node-name.ts rename to packages/utilities/ast/src/stringify.ts index ca77845760..6ad0fed546 100644 --- a/packages/utilities/ast/src/to-readable-node-name.ts +++ b/packages/utilities/ast/src/stringify.ts @@ -7,17 +7,17 @@ import { AST_NODE_TYPES as T } from "@typescript-eslint/types"; * @param getText A function that returns the text of the node in the source code * @returns Human readable node name */ -export function toReadableNodeName(node: TSESTree.Node, getText: (node: TSESTree.Node) => string): string { +export function stringify(node: TSESTree.Node, getText: (node: TSESTree.Node) => string): string { switch (node.type) { case T.CallExpression: - return toReadableNodeName(node.callee, getText); + return stringify(node.callee, getText); case T.Identifier: case T.PrivateIdentifier: return node.name; case T.JSXIdentifier: return `<${node.name}>`; case T.JSXMemberExpression: - return `${toReadableNodeName(node.object, getText)}.${toReadableNodeName(node.property, getText)}`; + return `${stringify(node.object, getText)}.${stringify(node.property, getText)}`; case T.JSXNamespacedName: return `${node.namespace.name}:${node.name.name}`; case T.JSXText: @@ -25,7 +25,7 @@ export function toReadableNodeName(node: TSESTree.Node, getText: (node: TSESTree case T.Literal: return node.raw; case T.MemberExpression: - return `${toReadableNodeName(node.object, getText)}.${toReadableNodeName(node.property, getText)}`; + return `${stringify(node.object, getText)}.${stringify(node.property, getText)}`; default: return getText(node); } diff --git a/packages/utilities/jsx/package.json b/packages/utilities/jsx/package.json deleted file mode 100644 index 6aad7d3be6..0000000000 --- a/packages/utilities/jsx/package.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "name": "@eslint-react/jsx", - "version": "1.46.1-next.0", - "description": "ESLint React's TSESTree AST utility module for static analysis of JSX.", - "homepage": "https://github.com/Rel1cx/eslint-react", - "bugs": { - "url": "https://github.com/Rel1cx/eslint-react/issues" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/Rel1cx/eslint-react.git", - "directory": "packages/utilities/jsx" - }, - "license": "MIT", - "author": "Rel1cx", - "sideEffects": false, - "exports": { - ".": { - "import": { - "types": "./dist/index.d.mts", - "default": "./dist/index.mjs" - }, - "require": { - "types": "./dist/index.d.ts", - "default": "./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": "tsup", - "lint:publish": "publint", - "lint:ts": "tsc --noEmit", - "publish": "pnpm run build && pnpm run lint:publish" - }, - "dependencies": { - "@eslint-react/ast": "workspace:*", - "@eslint-react/eff": "workspace:*", - "@eslint-react/var": "workspace:*", - "@typescript-eslint/scope-manager": "^8.29.1", - "@typescript-eslint/types": "^8.29.1", - "@typescript-eslint/utils": "^8.29.1", - "ts-pattern": "^5.7.0" - }, - "devDependencies": { - "@local/configs": "workspace:*", - "tsup": "^8.4.0" - }, - "engines": { - "bun": ">=1.0.15", - "node": ">=18.18.0" - } -} diff --git a/packages/utilities/jsx/src/attribute/attribute-name.ts b/packages/utilities/jsx/src/attribute/attribute-name.ts deleted file mode 100644 index 8b8910cb48..0000000000 --- a/packages/utilities/jsx/src/attribute/attribute-name.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { TSESTree } from "@typescript-eslint/utils"; - -import { toString } from "../to-string"; - -/** - * Get the stringified name of a JSX attribute - * @param node The JSX attribute node - * @returns The name of the attribute - */ -export function getAttributeName(node: TSESTree.JSXAttribute) { - return toString(node.name); -} diff --git a/packages/utilities/jsx/src/attribute/index.ts b/packages/utilities/jsx/src/attribute/index.ts deleted file mode 100644 index ac283b2674..0000000000 --- a/packages/utilities/jsx/src/attribute/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from "./attribute"; -export * from "./attribute-name"; -export * from "./attribute-value"; -export * from "./has"; -export * from "./hierarchy"; diff --git a/packages/utilities/jsx/src/element/index.ts b/packages/utilities/jsx/src/element/index.ts deleted file mode 100644 index b759169ddb..0000000000 --- a/packages/utilities/jsx/src/element/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./element-type"; -export * from "./is"; diff --git a/packages/utilities/jsx/src/index.ts b/packages/utilities/jsx/src/index.ts deleted file mode 100644 index 3333a8a050..0000000000 --- a/packages/utilities/jsx/src/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from "./attribute"; -export * from "./element"; -export * from "./jsx-detection"; -export * from "./jsx-detection-hint"; -export * from "./to-string"; diff --git a/packages/utilities/jsx/tsconfig.json b/packages/utilities/jsx/tsconfig.json deleted file mode 100644 index 8ce1a35fcd..0000000000 --- a/packages/utilities/jsx/tsconfig.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "extends": [ - "@local/configs/tsconfig.base.json", - "@tsconfig/node22/tsconfig.json" - ], - "compilerOptions": { - "module": "ESNext", - "moduleResolution": "bundler", - "skipLibCheck": true, - "moduleDetection": "force", - "isolatedModules": true, - "verbatimModuleSyntax": true, - "resolveJsonModule": true, - "allowJs": false, - "checkJs": false, - "strict": true, - "erasableSyntaxOnly": true, - "noImplicitAny": true, - "noImplicitThis": true, - "noUnusedLocals": false, - "noUnusedParameters": false, - "exactOptionalPropertyTypes": true, - "composite": false, - "stripInternal": false - }, - "exclude": [ - "node_modules", - "**/dist", - "test/fixtures" - ], - "include": [ - "src" - ] -} diff --git a/packages/utilities/jsx/tsup.config.ts b/packages/utilities/jsx/tsup.config.ts deleted file mode 100644 index a84cd44465..0000000000 --- a/packages/utilities/jsx/tsup.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { Options } from "tsup"; - -export default { - clean: true, - dts: true, - entry: ["src/index.ts"], - external: ["eslint", "typescript"], - format: ["cjs", "esm"], - minify: false, - outDir: "dist", - platform: "node", - sourcemap: false, - splitting: false, - target: "node18", - treeshake: true, -} satisfies Options; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1da4d47280..9086dd36bb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -700,9 +700,6 @@ importers: '@eslint-react/eff': specifier: workspace:* version: link:../utilities/eff - '@eslint-react/jsx': - specifier: workspace:* - version: link:../utilities/jsx '@eslint-react/kit': specifier: workspace:* version: link:../utilities/kit @@ -804,9 +801,6 @@ importers: '@eslint-react/eff': specifier: workspace:* version: link:../../utilities/eff - '@eslint-react/jsx': - specifier: workspace:* - version: link:../../utilities/jsx '@eslint-react/kit': specifier: workspace:* version: link:../../utilities/kit @@ -865,9 +859,6 @@ importers: '@eslint-react/eff': specifier: workspace:* version: link:../../utilities/eff - '@eslint-react/jsx': - specifier: workspace:* - version: link:../../utilities/jsx '@eslint-react/kit': specifier: workspace:* version: link:../../utilities/kit @@ -926,9 +917,6 @@ importers: '@eslint-react/eff': specifier: workspace:* version: link:../../utilities/eff - '@eslint-react/jsx': - specifier: workspace:* - version: link:../../utilities/jsx '@eslint-react/kit': specifier: workspace:* version: link:../../utilities/kit @@ -987,9 +975,6 @@ importers: '@eslint-react/eff': specifier: workspace:* version: link:../../utilities/eff - '@eslint-react/jsx': - specifier: workspace:* - version: link:../../utilities/jsx '@eslint-react/kit': specifier: workspace:* version: link:../../utilities/kit @@ -1048,9 +1033,6 @@ importers: '@eslint-react/eff': specifier: workspace:* version: link:../../utilities/eff - '@eslint-react/jsx': - specifier: workspace:* - version: link:../../utilities/jsx '@eslint-react/kit': specifier: workspace:* version: link:../../utilities/kit @@ -1106,9 +1088,6 @@ importers: '@eslint-react/eff': specifier: workspace:* version: link:../../utilities/eff - '@eslint-react/jsx': - specifier: workspace:* - version: link:../../utilities/jsx '@eslint-react/kit': specifier: workspace:* version: link:../../utilities/kit @@ -1242,37 +1221,6 @@ importers: specifier: ^8.4.0 version: 8.4.0(@swc/core@1.11.20)(jiti@2.4.2)(postcss@8.5.3)(tsx@4.19.3)(typescript@5.8.3)(yaml@2.7.1) - packages/utilities/jsx: - dependencies: - '@eslint-react/ast': - specifier: workspace:* - version: link:../ast - '@eslint-react/eff': - specifier: workspace:* - version: link:../eff - '@eslint-react/var': - specifier: workspace:* - version: link:../var - '@typescript-eslint/scope-manager': - specifier: ^8.29.1 - version: 8.29.1 - '@typescript-eslint/types': - specifier: ^8.29.1 - version: 8.29.1 - '@typescript-eslint/utils': - specifier: ^8.29.1 - version: 8.29.1(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3) - ts-pattern: - specifier: ^5.7.0 - version: 5.7.0 - devDependencies: - '@local/configs': - specifier: workspace:* - version: link:../../../.pkgs/configs - tsup: - specifier: ^8.4.0 - version: 8.4.0(@swc/core@1.11.20)(jiti@2.4.2)(postcss@8.5.3)(tsx@4.19.3)(typescript@5.8.3)(yaml@2.7.1) - packages/utilities/kit: dependencies: '@eslint-react/eff':