reactutil

package
v0.5.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 27, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Index

Constants

View Source
const DefaultReactCreateClass = "createReactClass"

DefaultReactCreateClass is the fallback ES5 factory name when `settings.react.createClass` is not configured, matching eslint-plugin-react.

View Source
const DefaultReactFragment = "Fragment"

DefaultReactFragment is the fallback fragment name for JSX shorthand fragment diagnostics when `settings.react.fragment` is not configured, matching eslint-plugin-react.

View Source
const DefaultReactPragma = "React"

DefaultReactPragma is the fallback object name for createElement calls when `settings.react.pragma` is not configured, matching eslint-plugin-react.

Variables

This section is empty.

Functions

func ExtendsReactComponent added in v0.5.0

func ExtendsReactComponent(classNode *ast.Node, pragma string) bool

ExtendsReactComponent reports whether `classNode` (a ClassDeclaration or ClassExpression) has an `extends` clause referencing `Component` or `PureComponent` — either as a bare identifier or qualified by the configured pragma (e.g. `React.Component`). Parentheses are skipped. Pass the empty string for pragma to default to `DefaultReactPragma`.

NOTE: Matches the name regex used by eslint-plugin-react's `componentUtil.isES6Component` (`/^(Pure)?Component$/`). Aliased imports (e.g. `import { Component as C }`) are not resolved — same as the upstream rule.

func ExtendsReactPureComponent added in v0.5.1

func ExtendsReactPureComponent(classNode *ast.Node, pragma string) bool

ExtendsReactPureComponent reports whether `classNode` (a ClassDeclaration or ClassExpression) has an `extends` clause referencing `PureComponent` — either as a bare identifier or qualified by the configured pragma (e.g. `React.PureComponent`). Parentheses are skipped. Pass the empty string for pragma to default to `DefaultReactPragma`.

Mirrors eslint-plugin-react's `componentUtil.isPureComponent`, which uses the regex `/^(<pragma>\.)?PureComponent$/` over the rendered extends-clause text. Plain `Component` does NOT match (use ExtendsReactComponent for the broader detection).

func FunctionReturnsJSX added in v0.5.1

func FunctionReturnsJSX(fn *ast.Node, pragma string) bool

FunctionReturnsJSX is the strict sibling of FunctionReturnsJSXOrNull: a `null` return does NOT qualify on its own. `<pragma>.createElement(...)` calls still qualify. Mirrors upstream jsxUtil.isReturningJSX invoked with `strict=true, ignoreNull=true`. `<pragma>.createElement(...)` calls still qualify. Pass an empty pragma to default to "React".

func FunctionReturnsJSXOrNull added in v0.5.1

func FunctionReturnsJSXOrNull(fn *ast.Node, pragma string) bool

FunctionReturnsJSXOrNull reports whether the function's body contains a `return <jsx/>` / `return null` / `return <pragma>.createElement(...)` at depth ≤ 1 (nested functions excluded), OR — for an arrow with expression body — whether that expression qualifies under the same rules. ConditionalExpression is traversed so `return cond ? <jsx/> : null` qualifies.

Identifier returns (`return view` where `view` is bound to a JSX value) are resolved structurally via a local block scan. Use `FunctionReturnsJSXOrNullWithChecker` for full TypeChecker-based scope resolution.

Mirrors upstream jsxUtil.isReturningJSX invoked with default arguments (which accept JSX, `null`, and `<pragma>.createElement(...)` returns). Pass an empty pragma to default to "React".

func FunctionReturnsJSXOrNullWithChecker added in v0.5.1

func FunctionReturnsJSXOrNullWithChecker(fn *ast.Node, pragma string, tc *checker.Checker) bool

FunctionReturnsJSXOrNullWithChecker is the TypeChecker-aware variant of FunctionReturnsJSXOrNull. When `tc` is non-nil, Identifier returns are resolved through `GetSymbolAtLocation` → `Declarations[0]` → `VariableDeclaration.Initializer`, matching upstream's `findVariableByName` scope walk semantically (any binding the TS resolver can reach is considered, not just bindings in the immediately-enclosing block). When `tc` is nil, falls back to the local-block scan.

func FunctionReturnsJSXWithChecker added in v0.5.1

func FunctionReturnsJSXWithChecker(fn *ast.Node, pragma string, tc *checker.Checker) bool

FunctionReturnsJSXWithChecker is the TypeChecker-aware strict variant. See FunctionReturnsJSXOrNullWithChecker for the resolution semantics.

func GetEnclosingReactComponent added in v0.5.0

func GetEnclosingReactComponent(node *ast.Node, pragma, createClass string) *ast.Node

GetEnclosingReactComponent is IsInsideReactComponent's sibling that returns the component node itself (the ClassDeclaration / ClassExpression, or the ObjectLiteralExpression passed to createReactClass) rather than a bool. Returns nil when `node` is not inside a React component. See IsInsideReactComponent for the detection rules.

func GetEnclosingReactComponentOrStateless added in v0.5.0

func GetEnclosingReactComponentOrStateless(node *ast.Node, pragma, createClass string) *ast.Node

GetEnclosingReactComponentOrStateless is GetEnclosingReactComponent extended with eslint-plugin-react's `getParentStatelessComponent` fallback: when no enclosing ES6 class / ES5 createReactClass component is found, the nearest FunctionLike ancestor that looks like a functional component (capital-cased name + returns JSX/null) is returned.

Priority matches upstream's `getParentComponent`:

getParentES6Component || getParentES5Component || getParentStatelessComponent

so when a mutation node is inside an inner function nested within an outer class component, the OUTER class component is returned (preventing the inner stateless candidate from masking the class boundary).

Only a restricted subset of upstream's heuristics is implemented — the patterns covering production React code: named FunctionDeclaration, FunctionExpression / ArrowFunction assigned to a capital-cased VariableDeclarator, PropertyAssignment, or ExportAssignment (default export), plus function expression in a CallExpression (e.g. React.memo wrapper — approximate match). This is intentionally conservative: missed detection causes a rule miss, over-detection would cause false-positive reports in non-component functions.

func GetJsxElementAttributes added in v0.5.0

func GetJsxElementAttributes(element *ast.Node) []*ast.Node

GetJsxElementAttributes returns the attribute nodes of a JsxOpeningElement or JsxSelfClosingElement, or nil for other kinds or when the element has no attributes. Each returned node is either a JsxAttribute or a JsxSpreadAttribute.

func GetJsxElementTypeString added in v0.5.0

func GetJsxElementTypeString(node *ast.Node) string

GetJsxElementTypeString returns the jsx-ast-utils `elementType(node)` equivalent — the dotted / namespaced display string of a JSX tag name as an ESTree-compatible source caller would see it. `node` may be either a JsxOpeningElement / JsxSelfClosingElement, or a raw tag-name node. Returns "" for shapes that don't correspond to a legal React/JSX element type (e.g. a computed member access), so callers can treat "" as "not a user component".

Supported tag shapes:

  • `<Foo>` / `<foo>` → "Foo" / "foo"
  • `<Foo.Bar.Baz>` → "Foo.Bar.Baz" (PropertyAccessExpression chain)
  • `<this.Foo>` → "this.Foo" (ThisKeyword base)
  • `<ns:Name>` → "ns:Name" (JsxNamespacedName)

This is AST-driven — interior whitespace or comments in unusual forms (e.g. `<Foo . Bar />`) are normalized away, matching jsx-ast-utils.

func GetJsxParentElement added in v0.5.0

func GetJsxParentElement(attr *ast.Node) *ast.Node

GetJsxParentElement returns the JsxOpeningElement or JsxSelfClosingElement that owns the given JsxAttribute (or JsxSpreadAttribute), or nil if not applicable.

func GetJsxPropName

func GetJsxPropName(node *ast.Node) string

GetJsxPropName returns the display name of a JSX node. For JsxAttribute: returns the attribute name (including namespaced names like "foo:bar"). For JsxSpreadAttribute: returns "spread". For Identifier nodes (e.g. tag names): returns the identifier text. For unknown nodes: returns "".

func GetJsxTagBaseIdentifier added in v0.5.0

func GetJsxTagBaseIdentifier(tagName *ast.Node) *ast.Node

GetJsxTagBaseIdentifier returns the leftmost Identifier of a JSX tag-name node — i.e. the symbol a rule must resolve to classify the tag. Pass the tag-name node obtained from `GetJsxTagName` (or directly from `JsxOpeningElement.TagName` / `JsxSelfClosingElement.TagName`). Returns nil when the tag does not terminate in an Identifier (ThisKeyword base, JsxNamespacedName, unknown shape).

Shapes handled:

  • `<Foo />` → Identifier("Foo")
  • `<Foo.Bar />` → Identifier("Foo")
  • `<Foo.Bar.Baz />` → Identifier("Foo")
  • `<this />` / `<this.X />` → nil (ThisKeyword base)
  • `<a:b />` → nil (JsxNamespacedName — not an identifier reference in any scope)
  • `<foo-bar />` → Identifier("foo-bar") (tsgo preserves the hyphenated text verbatim; callers decide whether that's DOM).

func GetJsxTagName added in v0.5.0

func GetJsxTagName(element *ast.Node) *ast.Node

GetJsxTagName returns the tag-name node of a JsxOpeningElement or JsxSelfClosingElement, or nil for other kinds.

func GetReactCreateClass added in v0.5.0

func GetReactCreateClass(settings map[string]interface{}) string

GetReactCreateClass reads `settings.react.createClass` from the config settings map. Returns DefaultReactCreateClass when the setting is absent, not a string, or empty.

func GetReactFragmentPragma added in v0.5.0

func GetReactFragmentPragma(settings map[string]interface{}) string

GetReactFragmentPragma reads `settings.react.fragment` from the config settings map. Returns DefaultReactFragment when the setting is absent, not a string, or empty.

func GetReactPragma added in v0.5.0

func GetReactPragma(settings map[string]interface{}) string

GetReactPragma reads `settings.react.pragma` from the config settings map. Returns DefaultReactPragma when the setting is absent, not a string, or empty.

func GlobToRegex added in v0.5.1

func GlobToRegex(pattern string) *regexp.Regexp

GlobToRegex converts a minimatch-style glob (only `*` and `?` wildcards recognized; every other regex metacharacter is literally escaped) into a fully anchored regular expression. Mirrors the subset of minimatch behavior eslint-plugin-react relies on for `propNamePattern`-style options — sufficient for `render*`, `*Renderer`, etc.

Compilation is cached per-pattern; the returned `*regexp.Regexp` is safe to share across goroutines.

func IsCreateClassCall added in v0.5.0

func IsCreateClassCall(call *ast.CallExpression, pragma, createClass string) bool

IsCreateClassCall reports whether the given CallExpression's callee is `<createClass>(...)` or `<pragma>.<createClass>(...)`. Parentheses are skipped on both the callee and the pragma identifier. Pass the empty string for pragma/createClass to fall back to `DefaultReactPragma` / `DefaultReactCreateClass`.

func IsCreateElementCall

func IsCreateElementCall(callee *ast.Node, pragma string) bool

IsCreateElementCall reports whether the callee is `<pragma>.createElement` (or, with the WithChecker variant below, bare `createElement` resolved to a pragma-destructured binding).

Pass an empty pragma to default to "React"; pass GetReactPragma(ctx.Settings) to honor the user's `settings.react.pragma` configuration.

Parentheses AND TS expression wrappers (`as` / `satisfies` / `<T>x` / `x!`) are transparently skipped on both the callee itself and the pragma identifier (e.g. `(React).createElement` / `(React as any).createElement`). Optional-chain calls (`React?.createElement(...)`) are NOT recognized (upstream's `node.callee.object.name` access fails on the OptionalCall shape).

This non-checker variant only recognizes the member-access form. To recognize bare `createElement(...)` calls (with the `isDestructuredFromPragmaImport` gate), use `IsCreateElementCallWithChecker`.

func IsCreateElementCallWithChecker added in v0.5.1

func IsCreateElementCallWithChecker(callee *ast.Node, pragma string, tc *checker.Checker) bool

IsCreateElementCallWithChecker is the import-aware variant. When `tc` is non-nil, additionally recognizes bare `createElement(arg)` calls where the bare callee resolves to a pragma-destructured binding (`import { createElement } from 'react'` / `const { createElement } = React` / `const createElement = React.createElement` / `const { createElement } = require('react')`). Mirrors upstream `isCreateElement`'s second branch byte-for-byte.

func IsCreateReactClassObjectArg added in v0.5.0

func IsCreateReactClassObjectArg(obj *ast.Node, pragma, createClass string) bool

IsCreateReactClassObjectArg reports whether `obj` (an ObjectLiteralExpression) is the FIRST argument of a `<createClass>(...)` / `<pragma>.<createClass>(...)` call. Parens wrapping `obj` before it reaches the call argument position are transparent — tsgo preserves them while ESTree flattens — so `createReactClass(({...}))` still matches.

Pass the empty string for pragma / createClass to fall back to `DefaultReactPragma` / `DefaultReactCreateClass`. Returns false for any non-ObjectLiteralExpression input, for objects in non-argument positions, and for calls whose callee is not the configured createClass name.

func IsDOMComponent added in v0.5.0

func IsDOMComponent(element *ast.Node) bool

IsDOMComponent reports whether a JSX opening/self-closing element refers to an intrinsic (DOM) element like <div> or <svg:path>, rather than a user component like <Foo> or <Foo.Bar>.

Mirrors ESLint-plugin-react's `jsxUtil.isDOMComponent`: a tag name is intrinsic iff `elementType(node)` starts with a lowercase letter (regex `/^[a-z]/`). For member-expression tags (`<foo.bar>`, `<this.Foo>`) this means the classification is decided by the leftmost base identifier's first character — so `<foo.bar>` is DOM (matches ESLint, even though the runtime React behavior is "always user component"), while `<Foo.Bar>` is a user component.

func IsDestructuredFromPragmaImport added in v0.5.1

func IsDestructuredFromPragmaImport(ident *ast.Node, pragma string, tc *checker.Checker) bool

IsDestructuredFromPragmaImport mirrors upstream eslint-plugin-react's `lib/util/isDestructuredFromPragmaImport.js`: reports whether the Identifier `ident` (a bare callee like `memo`) was bound from the pragma module. Returns true when ident's local binding originated from any of:

  • `import { memo } from 'react'` (named import)
  • `import { memo as m } from 'react'` (named-import rename — checks the imported name, not the local alias)
  • `import * as React from 'react'`'s namespace + `const memo = React.memo`
  • `const { memo } = React` (object destructure of the pragma binding)
  • `const memo = React.memo` (member access via pragma binding)
  • `const { memo } = require('react')` (require destructure)
  • `const memo = require('react').memo` (require member access)

`pragma` is the React pragma name (e.g. "React") — the comparison against ImportDeclaration / require argument uses `strings.ToLower(pragma)` to match upstream's `pragma.toLocaleLowerCase()` semantic. `tc` may be nil — when no TypeChecker is available the function falls back to a syntax-only SourceFile-wide scan via `findPragmaBindingByName`. That fallback is strictly a subset of TC-based resolution (no scope precision) but covers the idiomatic top-level pragma-import patterns, keeping the observable wrapper-recognition behavior aligned with upstream in no-tsconfig modes.

func IsFirstLetterCapitalized added in v0.5.1

func IsFirstLetterCapitalized(s string) bool

IsFirstLetterCapitalized is the exported alias for the package-private helper. Mirrors eslint-plugin-react's `lib/util/isFirstLetterCapitalized.js` — strips leading underscores then compares `unicode.ToUpper(r) == r`. Non-cased characters (CJK, digits, emoji) count as "capitalized" because they have no upper/lower mapping. Use this for any parent-name / binding capitalization check that needs to align with upstream's component detection semantics.

func IsHookName added in v0.5.1

func IsHookName(name string) bool

IsHookName reports whether `name` matches the React hook naming convention. Returns false for empty input.

func IsInsideReactComponent added in v0.5.0

func IsInsideReactComponent(node *ast.Node, pragma, createClass string) bool

IsInsideReactComponent reports whether `node` is lexically contained within a React component — either an ES5 component (object literal passed as an argument to `<createClass>(...)` / `<pragma>.<createClass>(...)`) or an ES6 component (ClassDeclaration / ClassExpression extending Component or PureComponent, optionally qualified by pragma).

Pass the empty string for pragma/createClass to fall back to `DefaultReactPragma` / `DefaultReactCreateClass`.

Mirrors eslint-plugin-react's componentUtil:

  • ES6 path: only the nearest enclosing class decides component status (matching `getParentES6Component`'s `while scope.type !== 'class'`). A non-component class does not "pass through" to let an outer component class match — this prevents false positives like a non-React inner class nested inside a React class.

  • ES5 path: `this` / `this.refs` must occur inside some function, whose ObjectExpression parent is the argument to `<createClass>(...)`. We approximate this by requiring that an enclosing function has been passed on the walk up before an ObjectExpression is accepted — which rules out pathological cases like `createReactClass({ x: this.refs.y })` where `this` is not inside any function (ESLint's scope walk returns null for that too).

func IsJsxElementLike added in v0.5.1

func IsJsxElementLike(node *ast.Node) bool

IsJsxElementLike reports whether node is a JsxElement or JsxSelfClosingElement — the two tsgo kinds that correspond to ESTree's single `JSXElement` type.

func IsJsxLike added in v0.5.1

func IsJsxLike(node *ast.Node) bool

IsJsxLike mirrors eslint-plugin-react's `jsxUtil.isJSX` — true for a JSX element (either tag form) or a JSX fragment.

func IsLowercaseFirstLetter added in v0.5.1

func IsLowercaseFirstLetter(s string) bool

IsLowercaseFirstLetter is the companion of IsFirstLetterCapitalized that matches upstream's exact lowercase-skip predicate from `lib/rules/no-unstable-nested-components.js`:

parentName[0] === parentName[0].toLowerCase()

Notably this is NOT the negation of IsFirstLetterCapitalized: this helper does NOT strip leading underscores, so `_Foo` is treated as lowercase here (the `_` round-trips through `ToLower`) even though IsFirstLetterCapitalized returns true for `_Foo` (after stripping `_`, `Foo` is capitalized). Both helpers exist because upstream uses each in different code paths — keep them paired.

func IsObjectLiteralShorthandFunction added in v0.5.1

func IsObjectLiteralShorthandFunction(node *ast.Node) bool

IsObjectLiteralShorthandFunction reports whether `node` is a FunctionLike that, in ESTree, would be the inner FunctionExpression of a `Property { value: FunctionExpression }` — i.e. an object-literal shorthand method / getter / setter. Useful for callers that want to narrow diagnostic ranges to the parameter-list `(` (see ParamListOpenParenPos) so positions align with ESTree's reporting shape.

func IsStatelessReactComponent added in v0.5.0

func IsStatelessReactComponent(fn *ast.Node, pragma string) bool

IsStatelessReactComponent reports whether `fn` (a FunctionLike) looks like a React functional component. Mirrors eslint-plugin-react's `getStatelessComponent` decision tree:

  • FunctionDeclaration — component iff returns JSX/null AND either: (a) its own Identifier is capitalized, OR (b) it is anonymous AND carries the `export default` modifier (ESLint's `!node.id || capitalized(node.id.name)` condition).

  • FunctionExpression / ArrowFunction — component iff returns JSX/null AND either wrapped in a pragma component call OR in an "allowed position" AND the position-specific capitalization check passes:

  • Wrapped in `<pragma>.memo(...)` / `<pragma>.forwardRef(...)` / bare `memo(...)` / bare `forwardRef(...)` — always a component.

  • Allowed positions (VariableDeclarator, AssignmentExpression, PropertyAssignment, ReturnStatement, ExportAssignment, outer ArrowFunction body) gate everything else. A bare IIFE or any other CallExpression argument position is NOT allowed, matching upstream's `isInAllowedPositionForComponent` default-false branch.

  • Within an allowed position, specific capitalization rules apply per upstream: VariableDeclarator/PropertyAssignment use the binding name; `Id = fn` assignments use the LHS Identifier; MemberExpression LHS uses the rightmost property name (with `module.exports = ...` as a special blanket-true case); a named FunctionExpression defers to its own Identifier.

Pass the empty string for `pragma` to default to `DefaultReactPragma`.

This wrapper preserves the historical "no checker" call shape used by every other React rule. Pass a non-nil checker via `IsStatelessReactComponentWithChecker` to enable Identifier-through-scope resolution inside the JSX-return checks (relevant for any input where the function returns a name bound elsewhere — `return view` ↔ `let view = <div/>` etc).

func IsStatelessReactComponentWithChecker added in v0.5.1

func IsStatelessReactComponentWithChecker(fn *ast.Node, pragma string, tc *checker.Checker) bool

IsStatelessReactComponentWithChecker mirrors IsStatelessReactComponent and additionally threads `tc` into every isReturningJSX / isReturningJSXOrNull gate inside the decision tree. When `tc` is nil, all behavior matches `IsStatelessReactComponent` exactly (local-block initializer scan only).

The pragma-component-wrapper branch (Branch 11) uses the hardcoded default wrappers (`memo` / `forwardRef`, pragma-qualified or bare). To honor `settings.componentWrapperFunctions` here, use `IsStatelessReactComponentWithWrappers` instead.

func IsStatelessReactComponentWithWrappers added in v0.5.1

func IsStatelessReactComponentWithWrappers(fn *ast.Node, pragma string, tc *checker.Checker, wrappers []ComponentWrapperEntry) bool

IsStatelessReactComponentWithWrappers is the variant that consults a user-provided wrapper list when classifying the inner function of pragma-component-wrapper calls (Branch 11 of the decision tree).

Why this matters: `myMemo(() => null)` with `settings.componentWrapperFunctions: ['myMemo']` should classify the inner arrow as a stateless component (via the wrapper-arm of upstream's `getStatelessComponent`), so that the null-only return correctly triggers `isStatelessComponentReturningNull` and the rule SKIPs. With the hardcoded variant above, `myMemo` isn't recognized → the arrow isn't classified → the null-only skip never fires → the rule reports where upstream would not.

Pass `wrappers = nil` for hardcoded defaults; pass the configured `GetComponentWrapperFunctions(...)` list to honor user settings.

func MatchGlob added in v0.5.1

func MatchGlob(text, pattern string) bool

MatchGlob is the fast path equivalent of `GlobToRegex(pattern).MatchString(text)`, returning false for empty `text`.

func MatchesAnyComponentWrapper added in v0.5.1

func MatchesAnyComponentWrapper(call, fn *ast.Node, wrappers []ComponentWrapperEntry) bool

MatchesAnyComponentWrapper reports whether `call` matches any entry in `wrappers`, with `fn` as its first argument (paren / TS-wrapper transparent). Pass an empty pragma to default to "React"; the pragma is only consulted for entries whose `Object` is empty AND need to fall back to the configured pragma — but `DefaultComponentWrappers` already pre-fills the pragma form, so callers normally shouldn't need to thread pragma through twice.

Optional-chain handling mirrors upstream's `isPragmaComponentWrapper`:

  • Member-level optional (`React?.memo(arg)`) — recognized; Babel emits the callee as MemberExpression with `optional: true` and upstream's `callee.type === 'MemberExpression'` check still passes.

  • Call-level optional (`memo?.(arg)`) on a bare Identifier callee — recognized only against `IsUserConfigured: true` entries. Hardcoded bare-default entries (`memo` / `forwardRef` without pragma object) are upstream-gated by `isDestructuredFromPragmaImport`, which we cannot enforce without an import resolver. Restricting hardcoded bare defaults to non-optional matches keeps us conservative; user wrappers don't need that gate (they're explicit user opt-in).

func MatchesAnyComponentWrapperWithChecker added in v0.5.1

func MatchesAnyComponentWrapperWithChecker(call, fn *ast.Node, wrappers []ComponentWrapperEntry, pragma string, tc *checker.Checker) bool

MatchesAnyComponentWrapperWithChecker is the import-aware variant. When `tc` is non-nil and the callee is a bare Identifier matching a hardcoded bare default entry (`{Property: "memo"}` / `{Property: "forwardRef"}` from `DefaultComponentWrappers`), the callee's binding must additionally be destructured from / imported from / required from the pragma module (per `IsDestructuredFromPragmaImport`). This precisely mirrors upstream's

(!wrapperFunction.object ||
 (wrapperFunction.object === pragma &&
  this.isDestructuredFromPragmaImport(node, node.callee.name)))

gate. Without this, `memo(arrow)` would silently classify when `memo` is a user-defined function unrelated to React — leading to over-reports where upstream skips. Use this variant whenever a TypeChecker is available; otherwise the import-resolution check is skipped (matching the non-checker variant's conservative behavior — call-level optional rejected for hardcoded bare defaults).

func ParamListOpenParenPos added in v0.5.1

func ParamListOpenParenPos(sf *ast.SourceFile, node *ast.Node) int

ParamListOpenParenPos returns the source position of the `(` that opens `node`'s parameter list, or -1 when the position cannot be located. Walks tokens after `node.Name().End()` via the scanner — robust against type-parameter lists (`<T>(p: T)`) where the `(` is not contiguous with the name. Use this when narrowing a diagnostic range on an object-literal shorthand method / getter / setter so the report site aligns with ESTree's `Property { value: FunctionExpression }` shape (FE.loc.start at `(`).

`node` must be a MethodDeclaration / GetAccessor / SetAccessor (or anything with a non-nil `Name()`); other inputs return -1.

func ParseReactVersion added in v0.5.0

func ParseReactVersion(settings map[string]interface{}) (int, int, int)

ParseReactVersion returns the (major, minor, patch) triple of `settings.react.version`. When the setting is missing, not a string, empty, or not recognizable as a version, it defaults to (999, 999, 999) — matching eslint-plugin-react's `getReactVersionFromContext`, which treats an absent version as "latest".

func ReactVersionLessThan added in v0.5.0

func ReactVersionLessThan(settings map[string]interface{}, major, minor, patch int) bool

ReactVersionLessThan reports whether `settings.react.version` is strictly less than the given major.minor.patch. See ParseReactVersion for the default when the setting is missing.

func SkipExpressionWrappers added in v0.5.1

func SkipExpressionWrappers(node *ast.Node) *ast.Node

SkipExpressionWrappers is a paren-and-TS-type-wrapper-transparent variant of `ast.SkipParentheses`. It additionally peels back tsgo's TS-only expression wrappers that ESLint's ESTree never produces: `as`-expressions, `satisfies`-expressions, `<T>x` type assertions, and `x!` non-null assertions. Use it whenever a rule must reach the underlying expression regardless of whether the source uses any of those wrappers — e.g. when matching a callee identifier, a JSX tag base, or a return-statement argument that may sit behind a `(x as Foo)`.

func SkipExpressionWrappersUp added in v0.5.1

func SkipExpressionWrappersUp(node *ast.Node) *ast.Node

SkipExpressionWrappersUp is the parent-walk equivalent of `SkipExpressionWrappers`: starting from `node.Parent`, walks up while the current parent is a transparent expression wrapper (`()` / `as` / `satisfies` / `<T>x` / `x!`) and returns the first non-wrapper ancestor, or nil when no such ancestor exists. Mirrors what ESTree implicitly does by flattening these wrappers — three sites in this rule used to inline the loop; one helper keeps them in lockstep.

Types

type ComponentMap added in v0.5.1

type ComponentMap map[string][]string

ComponentMap maps a component tag name (e.g. "a", "Link") to the set of attribute names that identify its link target (e.g. ["href"] or ["to"]).

func DefaultFormComponents added in v0.5.1

func DefaultFormComponents() ComponentMap

DefaultFormComponents returns the default form-component map: {"form": ["action"]}.

func DefaultLinkComponents added in v0.5.1

func DefaultLinkComponents() ComponentMap

DefaultLinkComponents returns the default link-component map: {"a": ["href"]}.

func ReadComponentsFromSettings added in v0.5.1

func ReadComponentsFromSettings(settings map[string]interface{}, key, attrField, defaultAttr string, base ComponentMap) ComponentMap

ReadComponentsFromSettings extracts a component-name→attribute-list map from `settings.<key>`, matching upstream `util/linkComponents`.

Upstream builds the map via `new Map(DEFAULT.concat(settings[key]).map(…))`, where same-key entries use last-wins (replace) semantics. This function mirrors that: a settings entry for an already-present component replaces the base entry entirely.

Shapes accepted (each entry may appear standalone or as an element of an outer array, mirroring upstream's `DEFAULT.concat(settings[key] || [])`):

  • string: "Link" → {Link: [defaultAttr]}
  • {name, <attrField>}: <attrField> string or []str → {name: [attr…]}

`attrField` is "linkAttribute" for linkComponents and "formAttribute" for formComponents — upstream uses distinct field names for each category (`value.linkAttribute` vs `value.formAttribute`), so getting this wrong would silently fall back to the default attribute for every custom form component the user configures.

type ComponentWrapperEntry added in v0.5.1

type ComponentWrapperEntry struct {
	Object           string
	Property         string
	IsUserConfigured bool
}

ComponentWrapperEntry describes one user-configured component-wrapping call site. Either form is recognized:

  • `{property: "memo", object: "React"}` matches `<object>.<property>(fn)` calls. Empty `object` is treated as `DefaultReactPragma`.
  • `{property: "memo"}` matches bare `<property>(fn)` calls when `object` is empty.

Mirrors eslint-plugin-react's `settings.componentWrapperFunctions` — strings in the user setting expand to `{property: <s>}`, objects pass through.

`IsUserConfigured` distinguishes entries the user explicitly added via `settings.componentWrapperFunctions` from entries we inject as hardcoded defaults (memo / forwardRef, pragma-qualified or bare). Upstream's `isDestructuredFromPragmaImport` adds a runtime guard to bare default entries — they only match when the bare callee was destructure-imported from the pragma module. We have no import resolver, so we approximate by matching default bare entries on non-optional calls only, and matching user-configured bare entries freely (since they don't depend on import resolution).

func DefaultComponentWrappers added in v0.5.1

func DefaultComponentWrappers(pragma string) []ComponentWrapperEntry

DefaultComponentWrappers is the always-on wrapper list every React rule uses regardless of `settings.componentWrapperFunctions`. Mirrors upstream: `{property: 'memo', object: pragma}`, `{property: 'forwardRef', object: pragma}`, plus the bare `memo` / `forwardRef` aliases.

func GetComponentWrapperFunctions added in v0.5.1

func GetComponentWrapperFunctions(settings map[string]interface{}, pragma string) []ComponentWrapperEntry

GetComponentWrapperFunctions reads `settings.componentWrapperFunctions` and merges the user's additions on top of `DefaultComponentWrappers`. Accepted shapes per entry:

  • string: "myMemo" → {Property: "myMemo"}
  • object: {"property": "memo", "object": "React"} → {Object: "React", Property: "memo"}; "object" defaults to empty (bare call) when omitted

Unknown / malformed entries are silently ignored, matching upstream.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL