eslint-plugin-react-debug
Version:
ESLint React's ESLint plugin for debugging related rules.
308 lines (293 loc) • 9.87 kB
JavaScript
import { DEFAULT_ESLINT_REACT_SETTINGS, getSettingsFromContext } from "@eslint-react/shared";
import { ESLintUtils } from "@typescript-eslint/utils";
import * as core from "@eslint-react/core";
import { isUseRefLikeCall } from "@eslint-react/core";
import { merge } from "@eslint-react/eslint";
import { AST_NODE_TYPES } from "@typescript-eslint/types";
import { isInitializedFromReact } from "@eslint-react/var";
import { findVariable } from "@typescript-eslint/utils/ast-utils";
import { getElementFullType, isFragmentElement } from "@eslint-react/jsx";
import { P, match } from "ts-pattern";
import ts from "typescript";
//#region \0rolldown/runtime.js
var __defProp = Object.defineProperty;
var __exportAll = (all, no_symbols) => {
let target = {};
for (var name in all) {
__defProp(target, name, {
get: all[name],
enumerable: true
});
}
if (!no_symbols) {
__defProp(target, Symbol.toStringTag, { value: "Module" });
}
return target;
};
//#endregion
//#region package.json
var name$1 = "eslint-plugin-react-debug";
var version = "5.18.1";
//#endregion
//#region src/utils/create-rule.ts
function getDocsUrl(ruleName) {
return `https://eslint-react.xyz/docs/rules/debug-${ruleName}`;
}
const createRule = ESLintUtils.RuleCreator(getDocsUrl);
//#endregion
//#region src/utils/stringify.ts
function stringify(value) {
return JSON.stringify(value, null, 2);
}
//#endregion
//#region src/rules/function-component/function-component.ts
const RULE_NAME$4 = "function-component";
var function_component_default = createRule({
meta: {
type: "problem",
docs: { description: "Reports all function components in JSON format." },
messages: { default: "{{json}}" },
schema: []
},
name: RULE_NAME$4,
create: create$4,
defaultOptions: []
});
function create$4(context) {
const { api, visitor } = core.getFunctionComponentCollector(context, {
collectDisplayName: true,
hint: core.DEFAULT_COMPONENT_DETECTION_HINT
});
return merge(visitor, { "Program:exit"(program) {
for (const { name, displayName, flag, hookCalls, node } of api.getAllComponents(program)) context.report({
data: { json: stringify({
name: name ?? "anonymous",
displayName: displayName == null ? "none" : context.sourceCode.getText(displayName),
forwardRef: (flag & core.FunctionComponentFlag.ForwardRef) > 0n,
hookCalls: hookCalls.length,
memo: (flag & core.FunctionComponentFlag.Memo) > 0n
}) },
messageId: "default",
node
});
} });
}
//#endregion
//#region src/rules/hook/hook.ts
const RULE_NAME$3 = "hook";
var hook_default = createRule({
meta: {
type: "problem",
docs: { description: "Reports all React Hooks in JSON format." },
messages: { default: "{{json}}" },
schema: []
},
name: RULE_NAME$3,
create: create$3,
defaultOptions: []
});
function create$3(context) {
const { api, visitor } = core.getHookCollector(context);
return merge(visitor, { "Program:exit"(program) {
for (const { name, hookCalls, node } of api.getAllHooks(program)) context.report({
data: { json: stringify({
name,
hookCalls: hookCalls.length
}) },
messageId: "default",
node
});
} });
}
//#endregion
//#region src/rules/is-from-react/lib.ts
/**
* Check if an identifier node is initialized from React
* @param node The identifier node to check
* @param initialScope Initial scope to search for the identifier
* @param importSource The import source to check against
* @returns Whether the identifier node is initialized from React
*/
function isFromReact(node, initialScope, importSource = "react") {
const name = node.name;
switch (true) {
case node.parent.type === AST_NODE_TYPES.MemberExpression && node.parent.property === node && node.parent.object.type === AST_NODE_TYPES.Identifier: return isInitializedFromReact(node.parent.object.name, initialScope, importSource);
case node.parent.type === AST_NODE_TYPES.JSXMemberExpression && node.parent.property === node && node.parent.object.type === AST_NODE_TYPES.JSXIdentifier: return isInitializedFromReact(node.parent.object.name, initialScope, importSource);
default: return isInitializedFromReact(name, initialScope, importSource);
}
}
//#endregion
//#region src/rules/is-from-react/is-from-react.ts
const RULE_NAME$2 = "is-from-react";
var is_from_react_default = createRule({
meta: {
type: "problem",
docs: { description: "Reports all identifiers initialized from React in JSON format." },
messages: { default: "{{json}}" },
schema: []
},
name: RULE_NAME$2,
create: create$2,
defaultOptions: []
});
function create$2(context) {
const { importSource } = getSettingsFromContext(context);
function visit(node) {
if (node.parent.type === AST_NODE_TYPES.ImportSpecifier && node.parent.imported === node && node.parent.imported.name === node.parent.local.name) return;
const name = node.name;
if (!isFromReact(node, context.sourceCode.getScope(node), importSource)) return;
context.report({
data: { json: stringify({
name,
importSource
}) },
messageId: "default",
node
});
}
return {
Identifier: visit,
JSXIdentifier: visit
};
}
//#endregion
//#region src/rules/is-from-ref/lib.ts
function getRefInitNode(context, node, initialScope) {
const name = node.name;
switch (true) {
case node.parent.type === AST_NODE_TYPES.MemberExpression && node.parent.property === node && node.parent.object.type === AST_NODE_TYPES.Identifier: return getRefInit(context, node.parent.object.name, initialScope);
case node.parent.type === AST_NODE_TYPES.JSXMemberExpression && node.parent.property === node && node.parent.object.type === AST_NODE_TYPES.JSXIdentifier: return getRefInit(context, node.parent.object.name, initialScope);
default: return getRefInit(context, name, initialScope);
}
}
/**
* Get the init expression of a ref variable
* @param context The rule context
* @param name The variable name
* @param initialScope The initial scope
* @returns The init expression node if the variable is derived from a ref, or null otherwise
*/
function getRefInit(context, name, initialScope) {
const { additionalRefHooks } = getSettingsFromContext(context);
for (const { node } of findVariable(initialScope, name)?.defs ?? []) {
if (node.type !== AST_NODE_TYPES.VariableDeclarator) continue;
const init = node.init;
if (init == null) continue;
switch (true) {
case init.type === AST_NODE_TYPES.MemberExpression && init.object.type === AST_NODE_TYPES.Identifier && (init.object.name === "ref" || init.object.name.endsWith("Ref")): return init;
case init.type === AST_NODE_TYPES.CallExpression && isUseRefLikeCall(init, additionalRefHooks): return init;
}
}
return null;
}
//#endregion
//#region src/rules/is-from-ref/is-from-ref.ts
const RULE_NAME$1 = "is-from-ref";
var is_from_ref_default = createRule({
meta: {
type: "problem",
docs: { description: "Reports all identifiers initialized or derived from refs in JSON format." },
messages: { default: "{{json}}" },
schema: []
},
name: RULE_NAME$1,
create: create$1,
defaultOptions: []
});
function create$1(context) {
function visit(node) {
const refInit = getRefInitNode(context, node, context.sourceCode.getScope(node));
if (refInit != null) {
const json = stringify({
name: node.name,
init: context.sourceCode.getText(refInit)
});
context.report({
data: { json },
messageId: "default",
node
});
}
}
return {
Identifier: visit,
JSXIdentifier: visit
};
}
//#endregion
//#region src/rules/jsx/jsx.ts
const RULE_NAME = "jsx";
var jsx_default = createRule({
meta: {
type: "problem",
docs: { description: "Reports all JSX elements and fragments in JSON format." },
messages: { default: "{{json}}" },
schema: []
},
name: "jsx",
create,
defaultOptions: []
});
function create(context) {
const jsxConfig = core.getJsxConfig(context);
function visit(node) {
context.report({
data: { json: stringify({
kind: match(node).with({ type: AST_NODE_TYPES.JSXElement }, (n) => isFragmentElement(n, jsxConfig.jsxFragmentFactory) ? "fragment" : "element").with({ type: AST_NODE_TYPES.JSXFragment }, () => "fragment").exhaustive(),
type: getElementFullType(node),
jsx: match(jsxConfig.jsx).with(ts.JsxEmit.None, () => "none").with(ts.JsxEmit.ReactJSX, () => "react-jsx").with(ts.JsxEmit.ReactJSXDev, () => "react-jsx-dev").with(ts.JsxEmit.React, () => "react").with(ts.JsxEmit.ReactNative, () => "react-native").with(ts.JsxEmit.Preserve, () => "preserve").otherwise(() => "unknown"),
jsxFactory: jsxConfig.jsxFactory,
jsxFragmentFactory: jsxConfig.jsxFragmentFactory,
jsxImportSource: jsxConfig.jsxImportSource,
jsxRuntime: match(jsxConfig.jsx).with(P.union(ts.JsxEmit.None, ts.JsxEmit.ReactJSX, ts.JsxEmit.ReactJSXDev), () => "automatic").otherwise(() => "classic")
}) },
messageId: "default",
node
});
}
return {
JSXElement: visit,
JSXFragment: visit
};
}
//#endregion
//#region src/plugin.ts
const plugin = {
meta: {
name: name$1,
version
},
rules: {
["function-component"]: function_component_default,
["hook"]: hook_default,
["is-from-react"]: is_from_react_default,
["is-from-ref"]: is_from_ref_default,
["jsx"]: jsx_default
}
};
//#endregion
//#region src/configs/all.ts
var all_exports = /* @__PURE__ */ __exportAll({
name: () => name,
plugins: () => plugins,
rules: () => rules,
settings: () => settings
});
const name = "react-debug/all";
const rules = {
"react-debug/function-component": "warn",
"react-debug/hook": "warn",
"react-debug/is-from-react": "warn",
"react-debug/is-from-ref": "warn",
"react-debug/jsx": "warn"
};
const plugins = { "react-debug": plugin };
const settings = { "react-x": DEFAULT_ESLINT_REACT_SETTINGS };
//#endregion
//#region src/index.ts
const finalPlugin = {
...plugin,
configs: { ["all"]: all_exports }
};
//#endregion
export { finalPlugin as default };