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
12 changes: 6 additions & 6 deletions packages/core/src/jsx/jsx-attribute.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type * as AST from "@eslint-react/ast";
import type { RuleContext } from "@eslint-react/kit";
import * as VAR from "@eslint-react/var";
import { findProperty, findVariable, getVariableDefinitionNode } from "@eslint-react/var";
import type { Scope } from "@typescript-eslint/scope-manager";

import { AST_NODE_TYPES as T } from "@typescript-eslint/types";

import { getAttributeName } from "./jsx-attribute-name";

export function getAttribute(context: RuleContext, attributes: AST.TSESTreeJSXAttributeLike[], initialScope?: Scope) {
Expand All @@ -18,16 +18,16 @@ export function getAttribute(context: RuleContext, attributes: AST.TSESTreeJSXAt
switch (attr.argument.type) {
// Case 2: Spread from variable (e.g., {...props})
case T.Identifier: {
const variable = VAR.findVariable(attr.argument.name, initialScope);
const variableNode = VAR.getVariableDefinitionNode(variable, 0);
const variable = findVariable(attr.argument.name, initialScope);
const variableNode = getVariableDefinitionNode(variable, 0);
if (variableNode?.type === T.ObjectExpression) {
return VAR.findProperty(name, variableNode.properties, initialScope) != null;
return findProperty(name, variableNode.properties, initialScope) != null;
}
return false;
}
// Case 3: Spread from object literal (e.g., {{...{prop: value}}})
case T.ObjectExpression:
return VAR.findProperty(name, attr.argument.properties, initialScope) != null;
return findProperty(name, attr.argument.properties, initialScope) != null;
}
return false;
});
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/jsx/jsx-detection.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
import * as AST from "@eslint-react/ast";
import type { unit } from "@eslint-react/eff";
import * as VAR from "@eslint-react/var";
import { findVariable, getVariableDefinitionNode } from "@eslint-react/var";
import type { Scope } from "@typescript-eslint/scope-manager";
import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
import type { TSESTree } from "@typescript-eslint/utils";
Expand Down Expand Up @@ -185,9 +185,9 @@ export function isJsxLike(
return true;
}
// Resolve variables to their values and check if they're JSX-like
const variable = VAR.findVariable(name, code.getScope(node));
const variable = findVariable(name, code.getScope(node));
const variableNode = variable
&& VAR.getVariableDefinitionNode(variable, 0);
&& getVariableDefinitionNode(variable, 0);
return !!variableNode
&& isJsxLike(code, variableNode, hint);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/utils/is-from-react.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as AST from "@eslint-react/ast";
import { identity } from "@eslint-react/eff";
import * as VAR from "@eslint-react/var";
import { findVariable } from "@eslint-react/var";
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";
Expand Down Expand Up @@ -34,7 +34,7 @@ export function isInitializedFromReact(
initialScope: Scope,
): boolean {
if (name.toLowerCase() === "react") return true;
const latestDef = VAR.findVariable(name, initialScope)?.defs.at(-1);
const latestDef = findVariable(name, initialScope)?.defs.at(-1);
if (latestDef == null) return false;
const { node, parent } = latestDef;
if (node.type === T.VariableDeclarator && node.init != null) {
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/utils/is-instance-id-equal.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/* eslint-disable jsdoc/require-param */
import * as AST from "@eslint-react/ast";
import type { RuleContext } from "@eslint-react/kit";
import * as VAR from "@eslint-react/var";
import { isNodeValueEqual } from "@eslint-react/var";
import type { TSESTree } from "@typescript-eslint/types";

/** @internal */
export function isInstanceIdEqual(context: RuleContext, a: TSESTree.Node, b: TSESTree.Node) {
return AST.isNodeEqual(a, b) || VAR.isNodeValueEqual(a, b, [
return AST.isNodeEqual(a, b) || isNodeValueEqual(a, b, [
context.sourceCode.getScope(a),
context.sourceCode.getScope(b),
]);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as ER from "@eslint-react/core";
import { useComponentCollectorLegacy } from "@eslint-react/core";
import type { RuleContext, RuleFeature } from "@eslint-react/kit";
import type { RuleListener } from "@typescript-eslint/utils/ts-eslint";
import type { CamelCase } from "string-ts";
Expand Down Expand Up @@ -31,7 +31,7 @@ export default createRule<[], MessageID>({
});

export function create(context: RuleContext<MessageID, []>): RuleListener {
const { ctx, listeners } = ER.useComponentCollectorLegacy();
const { ctx, listeners } = useComponentCollectorLegacy();
return {
...listeners,
"Program:exit"(program) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as ER from "@eslint-react/core";
import { ComponentFlag, DEFAULT_COMPONENT_DETECTION_HINT, useComponentCollector } from "@eslint-react/core";
import type { RuleContext, RuleFeature } from "@eslint-react/kit";
import type { RuleListener } from "@typescript-eslint/utils/ts-eslint";
import type { CamelCase } from "string-ts";
Expand Down Expand Up @@ -32,12 +32,12 @@ export default createRule<[], MessageID>({
});

export function create(context: RuleContext<MessageID, []>): RuleListener {
const { ctx, listeners } = ER.useComponentCollector(
const { ctx, listeners } = useComponentCollector(
context,
{
collectDisplayName: true,
collectHookCalls: true,
hint: ER.DEFAULT_COMPONENT_DETECTION_HINT,
hint: DEFAULT_COMPONENT_DETECTION_HINT,
},
);
return {
Expand All @@ -54,9 +54,9 @@ export function create(context: RuleContext<MessageID, []>): RuleListener {
displayName: displayName == null
? "none"
: context.sourceCode.getText(displayName),
forwardRef: (flag & ER.ComponentFlag.ForwardRef) > 0n,
forwardRef: (flag & ComponentFlag.ForwardRef) > 0n,
hookCalls: hookCalls.length,
memo: (flag & ER.ComponentFlag.Memo) > 0n,
memo: (flag & ComponentFlag.Memo) > 0n,
}),
},
});
Expand Down
4 changes: 2 additions & 2 deletions packages/plugins/eslint-plugin-react-debug/src/rules/hook.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as ER from "@eslint-react/core";
import { useHookCollector } from "@eslint-react/core";
import type { RuleContext, RuleFeature } from "@eslint-react/kit";
import type { RuleListener } from "@typescript-eslint/utils/ts-eslint";
import type { CamelCase } from "string-ts";
Expand Down Expand Up @@ -31,7 +31,7 @@ export default createRule<[], MessageID>({
});

export function create(context: RuleContext<MessageID, []>): RuleListener {
const { ctx, listeners } = ER.useHookCollector();
const { ctx, listeners } = useHookCollector();

return {
...listeners,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as ER from "@eslint-react/core";
import { isInitializedFromReact } from "@eslint-react/core";
import type { RuleContext, RuleFeature } from "@eslint-react/kit";
import { getSettingsFromContext } from "@eslint-react/shared";
import type { Scope } from "@typescript-eslint/scope-manager";
Expand Down Expand Up @@ -79,12 +79,12 @@ function isFromReact(
case node.parent.type === T.MemberExpression
&& node.parent.property === node
&& node.parent.object.type === T.Identifier:
return ER.isInitializedFromReact(node.parent.object.name, importSource, initialScope);
return isInitializedFromReact(node.parent.object.name, importSource, initialScope);
case node.parent.type === T.JSXMemberExpression
&& node.parent.property === node
&& node.parent.object.type === T.JSXIdentifier:
return ER.isInitializedFromReact(node.parent.object.name, importSource, initialScope);
return isInitializedFromReact(node.parent.object.name, importSource, initialScope);
default:
return ER.isInitializedFromReact(name, importSource, initialScope);
return isInitializedFromReact(name, importSource, initialScope);
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import * as ER from "@eslint-react/core";
import { hasAttribute, isJsxText } from "@eslint-react/core";
import type { RuleContext, RuleFeature } from "@eslint-react/kit";
import { AST_NODE_TYPES, type TSESTree } from "@typescript-eslint/types";
import type { RuleListener } from "@typescript-eslint/utils/ts-eslint";
import type { CamelCase } from "string-ts";

import { AST_NODE_TYPES, type TSESTree } from "@typescript-eslint/types";
import { createRule } from "../utils";

export const RULE_NAME = "no-dangerously-set-innerhtml-with-children";
Expand Down Expand Up @@ -40,8 +40,8 @@ export function create(context: RuleContext<MessageID, []>): RuleListener {
const attributes = node.openingElement.attributes;
const initialScope = context.sourceCode.getScope(node);
const hasChildren = node.children.some(isSignificantChildren)
|| ER.hasAttribute(context, "children", attributes, initialScope);
if (hasChildren && ER.hasAttribute(context, dangerouslySetInnerHTML, attributes, initialScope)) {
|| hasAttribute(context, "children", attributes, initialScope);
if (hasChildren && hasAttribute(context, dangerouslySetInnerHTML, attributes, initialScope)) {
context.report({
messageId: "noDangerouslySetInnerhtmlWithChildren",
node,
Expand All @@ -66,7 +66,7 @@ function isWhiteSpace(node: TSESTree.JSXText | TSESTree.Literal) {
* @returns boolean
*/
function isPaddingSpaces(node: TSESTree.Node) {
return ER.isJsxText(node)
return isJsxText(node)
&& isWhiteSpace(node)
&& node.raw.includes("\n");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as ER from "@eslint-react/core";
import { getAttribute } from "@eslint-react/core";
import type { RuleContext, RuleFeature } from "@eslint-react/kit";
import type { RuleListener } from "@typescript-eslint/utils/ts-eslint";
import type { CamelCase } from "string-ts";
Expand Down Expand Up @@ -35,8 +35,8 @@ export function create(context: RuleContext<MessageID, []>): RuleListener {
if (!context.sourceCode.text.includes(dangerouslySetInnerHTML)) return {};
return {
JSXElement(node) {
const getAttribute = ER.getAttribute(context, node.openingElement.attributes, context.sourceCode.getScope(node));
const attribute = getAttribute(dangerouslySetInnerHTML);
const getAttributeEx = getAttribute(context, node.openingElement.attributes, context.sourceCode.getScope(node));
const attribute = getAttributeEx(dangerouslySetInnerHTML);
if (attribute == null) return;
context.report({
messageId: "noDangerouslySetInnerhtml",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as ER from "@eslint-react/core";
import { getAttribute, resolveAttributeValue } from "@eslint-react/core";
import type { RuleContext, RuleFeature, RuleSuggest } from "@eslint-react/kit";
import type { RuleFixer, RuleListener } from "@typescript-eslint/utils/ts-eslint";
import type { CamelCase } from "string-ts";
Expand Down Expand Up @@ -42,8 +42,8 @@ export function create(context: RuleContext<MessageID, []>): RuleListener {
JSXElement(node) {
const { domElementType } = resolver.resolve(node);
if (domElementType !== "button") return;
const getAttribute = ER.getAttribute(context, node.openingElement.attributes, context.sourceCode.getScope(node));
const typeAttribute = getAttribute("type");
const getAttributeEx = getAttribute(context, node.openingElement.attributes, context.sourceCode.getScope(node));
const typeAttribute = getAttributeEx("type");
if (typeAttribute == null) {
context.report({
messageId: "noMissingButtonType",
Expand All @@ -54,7 +54,7 @@ export function create(context: RuleContext<MessageID, []>): RuleListener {
});
return;
}
if (typeof ER.resolveAttributeValue(context, typeAttribute).toStatic("type") === "string") return;
if (typeof resolveAttributeValue(context, typeAttribute).toStatic("type") === "string") return;
context.report({
messageId: "noMissingButtonType",
node: typeAttribute,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as ER from "@eslint-react/core";
import { getAttribute, resolveAttributeValue } from "@eslint-react/core";
import type { RuleContext, RuleFeature } from "@eslint-react/kit";
import type { RuleListener } from "@typescript-eslint/utils/ts-eslint";
import type { CamelCase } from "string-ts";
Expand Down Expand Up @@ -41,8 +41,8 @@ export function create(context: RuleContext<MessageID, []>): RuleListener {
JSXElement(node) {
const { domElementType } = resolver.resolve(node);
if (domElementType !== "iframe") return;
const getAttribute = ER.getAttribute(context, node.openingElement.attributes, context.sourceCode.getScope(node));
const sandboxAttribute = getAttribute("sandbox");
const getAttributeEx = getAttribute(context, node.openingElement.attributes, context.sourceCode.getScope(node));
const sandboxAttribute = getAttributeEx("sandbox");
if (sandboxAttribute == null) {
context.report({
messageId: "noMissingIframeSandbox",
Expand All @@ -57,7 +57,7 @@ export function create(context: RuleContext<MessageID, []>): RuleListener {
});
return;
}
const sandboxAttributeValue = ER.resolveAttributeValue(context, sandboxAttribute);
const sandboxAttributeValue = resolveAttributeValue(context, sandboxAttribute);
if (typeof sandboxAttributeValue.toStatic("sandbox") === "string") return;
context.report({
messageId: "noMissingIframeSandbox",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import * as ER from "@eslint-react/core";
import { getElementType } from "@eslint-react/core";
import type { RuleContext, RuleFeature } from "@eslint-react/kit";
import type { RuleListener } from "@typescript-eslint/utils/ts-eslint";
import type { CamelCase } from "string-ts";

import { createRule } from "../utils";

export const RULE_NAME = "no-namespace";
Expand Down Expand Up @@ -30,7 +31,7 @@ export default createRule<[], MessageID>({
export function create(context: RuleContext<MessageID, []>): RuleListener {
return {
JSXElement(node) {
const name = ER.getElementType(context, node);
const name = getElementType(context, node);
if (typeof name !== "string" || !name.includes(":")) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as ER from "@eslint-react/core";
import { resolveAttributeValue } from "@eslint-react/core";
import { RE_JAVASCRIPT_PROTOCOL, type RuleContext, type RuleFeature } from "@eslint-react/kit";
import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
import type { RuleListener } from "@typescript-eslint/utils/ts-eslint";
Expand Down Expand Up @@ -33,7 +33,7 @@ export function create(context: RuleContext<MessageID, []>): RuleListener {
return {
JSXAttribute(node) {
if (node.name.type !== T.JSXIdentifier || node.value == null) return;
const value = ER.resolveAttributeValue(context, node).toStatic();
const value = resolveAttributeValue(context, node).toStatic();
if (typeof value === "string" && RE_JAVASCRIPT_PROTOCOL.test(value)) {
context.report({
messageId: "noScriptUrl",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as ER from "@eslint-react/core";
import { getAttribute, isHostElement, resolveAttributeValue } from "@eslint-react/core";
import type { RuleContext, RuleFeature } from "@eslint-react/kit";
import type { RuleListener } from "@typescript-eslint/utils/ts-eslint";
import type { CamelCase } from "string-ts";
Expand Down Expand Up @@ -31,11 +31,11 @@ export default createRule<[], MessageID>({
export function create(context: RuleContext<MessageID, []>): RuleListener {
return {
JSXElement(node) {
if (!ER.isHostElement(context, node)) return;
const getAttribute = ER.getAttribute(context, node.openingElement.attributes, context.sourceCode.getScope(node));
const attribute = getAttribute("style");
if (!isHostElement(context, node)) return;
const getAttributeEx = getAttribute(context, node.openingElement.attributes, context.sourceCode.getScope(node));
const attribute = getAttributeEx("style");
if (attribute == null) return;
const attributeValue = ER.resolveAttributeValue(context, attribute);
const attributeValue = resolveAttributeValue(context, attribute);
if (typeof attributeValue.toStatic() === "string") {
context.report({
messageId: "noStringStyleProp",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as ER from "@eslint-react/core";
import { getAttribute, resolveAttributeValue } from "@eslint-react/core";
import type { RuleContext, RuleFeature } from "@eslint-react/kit";
import type { RuleListener } from "@typescript-eslint/utils/ts-eslint";
import type { CamelCase } from "string-ts";
Expand Down Expand Up @@ -45,10 +45,10 @@ export function create(context: RuleContext<MessageID, []>): RuleListener {
JSXElement(node) {
const { domElementType } = resolver.resolve(node);
if (domElementType !== "iframe") return;
const getAttribute = ER.getAttribute(context, node.openingElement.attributes, context.sourceCode.getScope(node));
const sandboxAttribute = getAttribute("sandbox");
const getAttributeEx = getAttribute(context, node.openingElement.attributes, context.sourceCode.getScope(node));
const sandboxAttribute = getAttributeEx("sandbox");
if (sandboxAttribute == null) return;
const sandboxValue = ER.resolveAttributeValue(context, sandboxAttribute);
const sandboxValue = resolveAttributeValue(context, sandboxAttribute);
const sandboxValueStatic = sandboxValue.toStatic("sandbox");
if (!isSafeSandbox(sandboxValueStatic)) {
context.report({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as ER from "@eslint-react/core";
import { getAttribute, resolveAttributeValue } from "@eslint-react/core";
import type { RuleContext, RuleFeature } from "@eslint-react/kit";
import type { TSESTree } from "@typescript-eslint/types";
import type { RuleListener } from "@typescript-eslint/utils/ts-eslint";
Expand Down Expand Up @@ -71,28 +71,28 @@ export function create(context: RuleContext<MessageID, []>): RuleListener {
if (domElementType !== "a") return;

// Get access to the component attributes
const getAttributes = ER.getAttribute(
const getAttributeEx = getAttribute(
context,
node.openingElement.attributes,
context.sourceCode.getScope(node),
);

// Check if target="_blank" is present
const targetAttribute = getAttributes("target");
const targetAttribute = getAttributeEx("target");
if (targetAttribute == null) return;

const targetAttributeValue = ER.resolveAttributeValue(context, targetAttribute).toStatic("target");
const targetAttributeValue = resolveAttributeValue(context, targetAttribute).toStatic("target");
if (targetAttributeValue !== "_blank") return;

// Check if href points to an external resource
const hrefAttribute = getAttributes("href");
const hrefAttribute = getAttributeEx("href");
if (hrefAttribute == null) return;

const hrefAttributeValue = ER.resolveAttributeValue(context, hrefAttribute).toStatic("href");
const hrefAttributeValue = resolveAttributeValue(context, hrefAttribute).toStatic("href");
if (!isExternalLinkLike(hrefAttributeValue)) return;

// Check if rel attribute exists and is secure
const relAttribute = getAttributes("rel");
const relAttribute = getAttributeEx("rel");

// No rel attribute case - suggest adding one
if (relAttribute == null) {
Expand All @@ -113,7 +113,7 @@ export function create(context: RuleContext<MessageID, []>): RuleListener {
}

// Check if existing rel attribute is secure
const relAttributeValue = ER.resolveAttributeValue(context, relAttribute).toStatic("rel");
const relAttributeValue = resolveAttributeValue(context, relAttribute).toStatic("rel");
if (isSafeRel(relAttributeValue)) return;

// Existing rel attribute is not secure - suggest replacing it
Expand Down
Loading