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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/core/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- [ClassComponent](interfaces/ClassComponent.md)
- [FunctionComponent](interfaces/FunctionComponent.md)
- [Hook](interfaces/Hook.md)
- [JsxConfig](interfaces/JsxConfig.md)
- [SemanticEntry](interfaces/SemanticEntry.md)
- [SemanticNode](interfaces/SemanticNode.md)

Expand Down Expand Up @@ -107,6 +108,7 @@
- [isUseSyncExternalStoreCall](variables/isUseSyncExternalStoreCall.md)
- [isUseTransitionCall](variables/isUseTransitionCall.md)
- [JSXDetectionHint](variables/JSXDetectionHint.md)
- [JsxEmit](variables/JsxEmit.md)
- [REACT\_BUILTIN\_HOOK\_NAMES](variables/REACT_BUILTIN_HOOK_NAMES.md)

## Functions
Expand All @@ -118,6 +120,8 @@
- [getComponentNameFromId](functions/getComponentNameFromId.md)
- [getElementType](functions/getElementType.md)
- [getFunctionComponentId](functions/getFunctionComponentId.md)
- [getJsxConfigFromAnnotation](functions/getJsxConfigFromAnnotation.md)
- [getJsxConfigFromContext](functions/getJsxConfigFromContext.md)
- [hasAnyAttribute](functions/hasAnyAttribute.md)
- [hasAttribute](functions/hasAttribute.md)
- [hasEveryAttribute](functions/hasEveryAttribute.md)
Expand Down
25 changes: 25 additions & 0 deletions packages/core/docs/functions/getJsxConfigFromAnnotation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[**@eslint-react/core**](../README.md)

***

[@eslint-react/core](../README.md) / getJsxConfigFromAnnotation

# Function: getJsxConfigFromAnnotation()

> **getJsxConfigFromAnnotation**(`context`): [`JsxConfig`](../interfaces/JsxConfig.md)

Get JsxConfig from pragma comments (annotations) in the source code.

## Parameters

### context

`RuleContext`

The RuleContext.

## Returns

[`JsxConfig`](../interfaces/JsxConfig.md)

JsxConfig derived from pragma comments.
41 changes: 41 additions & 0 deletions packages/core/docs/functions/getJsxConfigFromContext.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
[**@eslint-react/core**](../README.md)

***

[@eslint-react/core](../README.md) / getJsxConfigFromContext

# Function: getJsxConfigFromContext()

> **getJsxConfigFromContext**(`context`): `object`

Get JsxConfig from the rule context by reading compiler options.

## Parameters

### context

`RuleContext`

The RuleContext.

## Returns

`object`

JsxConfig derived from compiler options.

### jsx

> **jsx**: `4` \| `JsxEmit`

### jsxFactory

> **jsxFactory**: `string`

### jsxFragmentFactory

> **jsxFragmentFactory**: `string`

### jsxImportSource

> **jsxImportSource**: `string`
4 changes: 2 additions & 2 deletions packages/core/docs/functions/hasNoneOrLooseComponentName.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

# Function: hasNoneOrLooseComponentName()

> **hasNoneOrLooseComponentName**(`context`, `fn`): `any`
> **hasNoneOrLooseComponentName**(`context`, `fn`): `boolean`

## Parameters

Expand All @@ -20,4 +20,4 @@

## Returns

`any`
`boolean`
4 changes: 2 additions & 2 deletions packages/core/docs/functions/isComponentName.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

# Function: isComponentName()

> **isComponentName**(`name`): `any`
> **isComponentName**(`name`): `boolean`

## Parameters

Expand All @@ -16,4 +16,4 @@

## Returns

`any`
`boolean`
4 changes: 2 additions & 2 deletions packages/core/docs/functions/isComponentNameLoose.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

# Function: isComponentNameLoose()

> **isComponentNameLoose**(`name`): `any`
> **isComponentNameLoose**(`name`): `boolean`

## Parameters

Expand All @@ -16,4 +16,4 @@

## Returns

`any`
`boolean`
31 changes: 31 additions & 0 deletions packages/core/docs/interfaces/JsxConfig.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[**@eslint-react/core**](../README.md)

***

[@eslint-react/core](../README.md) / JsxConfig

# Interface: JsxConfig

## Properties

### jsx?

> `optional` **jsx**: `number`

***

### jsxFactory?

> `optional` **jsxFactory**: `string`

***

### jsxFragmentFactory?

> `optional` **jsxFragmentFactory**: `string`

***

### jsxImportSource?

> `optional` **jsxImportSource**: `string`
35 changes: 35 additions & 0 deletions packages/core/docs/variables/JsxEmit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[**@eslint-react/core**](../README.md)

***

[@eslint-react/core](../README.md) / JsxEmit

# Variable: JsxEmit

> `const` **JsxEmit**: `object`

## Type Declaration

### None

> `readonly` **None**: `0` = `0`

### Preserve

> `readonly` **Preserve**: `1` = `1`

### React

> `readonly` **React**: `2` = `2`

### ReactJSX

> `readonly` **ReactJSX**: `4` = `4`

### ReactJSXDev

> `readonly` **ReactJSXDev**: `5` = `5`

### ReactNative

> `readonly` **ReactNative**: `3` = `3`
4 changes: 2 additions & 2 deletions packages/core/src/component/component-collector.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as AST from "@eslint-react/ast";
import { unit } from "@eslint-react/eff";
import { type RuleContext, Selector as SEL } from "@eslint-react/kit";
import { type RuleContext, SEL_DISPLAY_NAME_ASSIGNMENT_EXPRESSION } from "@eslint-react/kit";
import { getId } from "@eslint-react/shared";
import type { TSESTree } from "@typescript-eslint/types";
import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
Expand Down Expand Up @@ -123,7 +123,7 @@ export function useComponentCollector(
},
...collectDisplayName
? {
[SEL.DISPLAY_NAME_ASSIGNMENT_EXPRESSION](node: TSESTree.AssignmentExpression) {
[SEL_DISPLAY_NAME_ASSIGNMENT_EXPRESSION](node: TSESTree.AssignmentExpression) {
const { left, right } = node;
if (left.type !== T.MemberExpression) return;
const componentName = left.object.type === T.Identifier
Expand Down
7 changes: 3 additions & 4 deletions packages/core/src/component/component-name.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import type * as AST from "@eslint-react/ast";
import { unit } from "@eslint-react/eff";
import type { RuleContext } from "@eslint-react/kit";
import { RegExp as RE } from "@eslint-react/kit";
import { RE_COMPONENT_NAME, RE_COMPONENT_NAME_LOOSE, type RuleContext } from "@eslint-react/kit";
import type { TSESTree } from "@typescript-eslint/types";

import { getFunctionComponentId } from "./component-id";

export function isComponentName(name: string) {
return RE.COMPONENT_NAME.test(name);
return RE_COMPONENT_NAME.test(name);
}

export function isComponentNameLoose(name: string) {
return RE.COMPONENT_NAME_LOOSE.test(name);
return RE_COMPONENT_NAME_LOOSE.test(name);
}

export function getComponentNameFromId(id: TSESTree.Identifier | TSESTree.Identifier[] | unit) {
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/jsx/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from "./jsx-attribute";
export * from "./jsx-attribute-name";
export * from "./jsx-attribute-value";
export * from "./jsx-config";
export * from "./jsx-detection";
export * from "./jsx-element-is";
export * from "./jsx-element-type";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
/* eslint-disable perfectionist/sort-objects */
import { getOrElseUpdate } from "@eslint-react/eff";
import * as RE from "../RegExp";
import type { RuleContext } from "../types";
import {
RE_ANNOTATION_JSX,
RE_ANNOTATION_JSX_FRAG,
RE_ANNOTATION_JSX_IMPORT_SOURCE,
RE_ANNOTATION_JSX_RUNTIME,
type RuleContext,
} from "@eslint-react/kit";

// Constants for JSX emit settings.
export const JsxEmit = {
None: 0,
Preserve: 1,
React: 2,
ReactNative: 3,
ReactJSX: 4,
ReactJSXDev: 5,
None: 0, // Do not emit JSX code.
Preserve: 1, // Emit .jsx files with JSX preserved.
React: 2, // Emit .js files with React.createElement calls.
ReactNative: 3, // Emit .js files with React Native specific output.
ReactJSX: 4, // Emit .js files with the new JSX transform.
ReactJSXDev: 5, // Emit .js files with the new JSX transform for development.
} as const;

// Interface for JSX configuration.
export interface JsxConfig {
// Specifies what JSX code is generated.
jsx?: number;
Expand All @@ -24,52 +31,48 @@ export interface JsxConfig {
}

/**
* Create a JsxConfig object
* @returns JsxConfig
* Get JsxConfig from the rule context by reading compiler options.
* @param context The RuleContext.
* @returns JsxConfig derived from compiler options.
*/
export function make(): JsxConfig {
return {};
}

/**
* Get JsxConfig from RuleContext
* @param context The RuleContext
* @returns JsxConfig
*/
export function getFromContext(context: RuleContext) {
export function getJsxConfigFromContext(context: RuleContext) {
const options = context.sourceCode.parserServices?.program?.getCompilerOptions() ?? {};
return {
jsx: options.jsx ?? JsxEmit.ReactJSX,
jsxFactory: options.jsxFactory ?? "React.createElement",
jsxFragmentFactory: options.jsxFragmentFactory ?? "React.Fragment",
jsxImportSource: options.jsxImportSource ?? "react",
reactNamespace: options.reactNamespace ?? "React",
};
}

// A weak map to cache JsxConfig for each source code.
const cache = new WeakMap<RuleContext["sourceCode"], JsxConfig>();

/**
* Get JsxConfig from annotation
* @param context The RuleContext
* @returns JsxConfig
* Get JsxConfig from pragma comments (annotations) in the source code.
* @param context The RuleContext.
* @returns JsxConfig derived from pragma comments.
*/
export function getFromAnnotation(context: RuleContext) {
export function getJsxConfigFromAnnotation(context: RuleContext) {
return getOrElseUpdate(
cache,
context.sourceCode,
() => {
const options = make();
const options: JsxConfig = {};
// Early return if no @jsx pragma is present.
if (!context.sourceCode.text.includes("@jsx")) return options;
// eslint-disable-next-line perfectionist/sort-variable-declarations
let jsx, jsxFrag, jsxRuntime, jsxImportSource;
// Iterate over comments in reverse to find the last pragma.
for (const comment of context.sourceCode.getAllComments().reverse()) {
const value = comment.value;
jsx ??= value.match(RE.ANNOTATION_JSX)?.[1];
jsxFrag ??= value.match(RE.ANNOTATION_JSX_FRAG)?.[1];
jsxRuntime ??= value.match(RE.ANNOTATION_JSX_RUNTIME)?.[1];
jsxImportSource ??= value.match(RE.ANNOTATION_JSX_IMPORT_SOURCE)?.[1];
// Match pragma comments and extract their values.
jsx ??= value.match(RE_ANNOTATION_JSX)?.[1];
jsxFrag ??= value.match(RE_ANNOTATION_JSX_FRAG)?.[1];
jsxRuntime ??= value.match(RE_ANNOTATION_JSX_RUNTIME)?.[1];
jsxImportSource ??= value.match(RE_ANNOTATION_JSX_IMPORT_SOURCE)?.[1];
}
// Update options with the extracted values.
if (jsx != null) options.jsxFactory = jsx;
if (jsxFrag != null) options.jsxFragmentFactory = jsxFrag;
if (jsxRuntime != null) options.jsx = jsxRuntime === "classic" ? JsxEmit.React : JsxEmit.ReactJSX;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { JsxConfig } from "@eslint-react/kit";
import { RuleTester } from "@typescript-eslint/rule-tester";

import tsx from "dedent";

import { JsxEmit } from "@eslint-react/core";
import { defaultLanguageOptionsWithTypes, getProjectForJsxEmit } from "../../../../../test";
import { stringify } from "../utils";
import rule, { RULE_NAME } from "./jsx";
Expand All @@ -12,7 +12,7 @@ const ruleTester = new RuleTester({
...defaultLanguageOptionsWithTypes,
parserOptions: {
...defaultLanguageOptionsWithTypes.parserOptions,
project: getProjectForJsxEmit(JsxConfig.JsxEmit.ReactJSX),
project: getProjectForJsxEmit(JsxEmit.ReactJSX),
projectService: false,
},
},
Expand Down
Loading