Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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