@trackunit/eslint-plugin-trackunit 0.4.7 → 0.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/package.json +1 -1
  3. package/src/lib/config/index.d.ts +21 -21
  4. package/src/lib/config/plugins.d.ts +7 -7
  5. package/src/lib/config/presets/base.d.ts +14 -14
  6. package/src/lib/config/presets/react.d.ts +7 -7
  7. package/src/lib/rules/no-internal-graphql-when-tagged-with-gql-public/no-internal-graphql-when-tagged-with-gql-public.d.ts +3 -4
  8. package/src/lib/rules/no-internal-graphql-when-tagged-with-gql-public/no-internal-graphql-when-tagged-with-gql-public.js +60 -57
  9. package/src/lib/rules/no-jest-mock-trackunit-react-core-hooks/no-jest-mock-trackunit-react-core-hooks.d.ts +4 -2
  10. package/src/lib/rules/no-jest-mock-trackunit-react-core-hooks/no-jest-mock-trackunit-react-core-hooks.js +6 -2
  11. package/src/lib/rules/no-typescript-assertion/no-typescript-assertion.d.ts +5 -4
  12. package/src/lib/rules/no-typescript-assertion/no-typescript-assertion.js +57 -72
  13. package/src/lib/rules/require-list-item-virtualization-props/require-list-item-virtualization-props.js +1 -1
  14. package/src/lib/rules-map.d.ts +7 -8
  15. package/src/index.js.map +0 -1
  16. package/src/lib/config/fragments/ignores.js.map +0 -1
  17. package/src/lib/config/fragments/import-rules.js.map +0 -1
  18. package/src/lib/config/fragments/jest-overrides.js.map +0 -1
  19. package/src/lib/config/fragments/jsdoc-rules.js.map +0 -1
  20. package/src/lib/config/fragments/module-boundaries.js.map +0 -1
  21. package/src/lib/config/fragments/react-rules.js.map +0 -1
  22. package/src/lib/config/fragments/restricted-imports.js.map +0 -1
  23. package/src/lib/config/fragments/testing-library.js.map +0 -1
  24. package/src/lib/config/fragments/typescript-rules.js.map +0 -1
  25. package/src/lib/config/index.js.map +0 -1
  26. package/src/lib/config/plugins.js.map +0 -1
  27. package/src/lib/config/presets/base.js.map +0 -1
  28. package/src/lib/config/presets/e2e.js.map +0 -1
  29. package/src/lib/config/presets/react.js.map +0 -1
  30. package/src/lib/config/presets/server.js.map +0 -1
  31. package/src/lib/config/utils.js.map +0 -1
  32. package/src/lib/config-helpers/create-skip-when.js.map +0 -1
  33. package/src/lib/rules/cva-merge-base-classes-as-array/cva-merge-base-classes-as-array.js.map +0 -1
  34. package/src/lib/rules/design-guideline-button-icon-size-match/design-guideline-button-icon-size-match.js.map +0 -1
  35. package/src/lib/rules/no-internal-barrel-files/examples.d.ts +0 -80
  36. package/src/lib/rules/no-internal-barrel-files/examples.js +0 -84
  37. package/src/lib/rules/no-internal-barrel-files/examples.js.map +0 -1
  38. package/src/lib/rules/no-internal-barrel-files/no-internal-barrel-files.js.map +0 -1
  39. package/src/lib/rules/no-internal-graphql-when-tagged-with-gql-public/no-internal-graphql-when-tagged-with-gql-public.js.map +0 -1
  40. package/src/lib/rules/no-jest-mock-trackunit-react-core-hooks/no-jest-mock-trackunit-react-core-hooks.js.map +0 -1
  41. package/src/lib/rules/no-template-strings-in-classname-prop/no-template-strings-in-classname-prop.js.map +0 -1
  42. package/src/lib/rules/no-typescript-assertion/examples.d.ts +0 -1
  43. package/src/lib/rules/no-typescript-assertion/examples.js +0 -45
  44. package/src/lib/rules/no-typescript-assertion/examples.js.map +0 -1
  45. package/src/lib/rules/no-typescript-assertion/no-typescript-assertion.js.map +0 -1
  46. package/src/lib/rules/prefer-destructured-imports/prefer-destructured-imports.js.map +0 -1
  47. package/src/lib/rules/prefer-event-specific-callback-naming/name-suggestion-strategies.js.map +0 -1
  48. package/src/lib/rules/prefer-event-specific-callback-naming/prefer-event-specific-callback-naming.js.map +0 -1
  49. package/src/lib/rules/prefer-event-specific-callback-naming/strategies/string-based.js.map +0 -1
  50. package/src/lib/rules/prefer-event-specific-callback-naming/strategies/type-based.js.map +0 -1
  51. package/src/lib/rules/prefer-event-specific-callback-naming/utils.js.map +0 -1
  52. package/src/lib/rules/prefer-field-components/prefer-field-components.js.map +0 -1
  53. package/src/lib/rules/prefer-mouse-event-handler-in-react-props/prefer-mouse-event-handler-in-react-props.js.map +0 -1
  54. package/src/lib/rules/require-classname-alternatives/require-classname-alternatives.js.map +0 -1
  55. package/src/lib/rules/require-list-item-virtualization-props/require-list-item-virtualization-props.js.map +0 -1
  56. package/src/lib/rules/require-optional-prop-initialization/require-optional-prop-initialization.js.map +0 -1
  57. package/src/lib/rules/require-optional-prop-initialization/suggestion-utils.js.map +0 -1
  58. package/src/lib/rules-map.js.map +0 -1
  59. package/src/lib/utils/ast-utils.js.map +0 -1
  60. package/src/lib/utils/classname-utils.js.map +0 -1
  61. package/src/lib/utils/file-utils.js.map +0 -1
  62. package/src/lib/utils/import-utils.js.map +0 -1
  63. package/src/lib/utils/nx-utils.js.map +0 -1
  64. package/src/lib/utils/package-utils.js.map +0 -1
  65. package/src/lib/utils/typescript-utils.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"prefer-destructured-imports.js","sourceRoot":"","sources":["../../../../../../../../libs/eslint/plugin-trackunit/src/lib/rules/prefer-destructured-imports/prefer-destructured-imports.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Enforce destructured imports instead of namespace imports for configured packages.\n * This rule helps maintain consistent import styles by converting namespace imports and detecting global UMD usage.\n * @example\n * // ❌ Bad (namespace import)\n * import React from \"react\";\n * const Component = () => {\n * React.useEffect(() => {}, []);\n * return <div />;\n * };\n *\n * // ✅ Good (destructured import)\n * import { useEffect } from \"react\";\n * const Component = () => {\n * useEffect(() => {}, []);\n * return <div />;\n * };\n * @example\n * // ❌ Bad (global UMD usage without import)\n * interface Props {\n * children: React.ReactNode;\n * }\n *\n * // ✅ Good (explicit import)\n * import { ReactNode } from \"react\";\n * interface Props {\n * children: ReactNode;\n * }\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, TSESTree } from \"@typescript-eslint/utils\";\nimport { addImportSpecifiers, replaceNamespaceWithDestructured } from \"../../utils/import-utils\";\n\nconst createRule = ESLintUtils.RuleCreator(\n name => `https://github.com/trackunit/manager/blob/main/libs/eslint/plugin-trackunit/src/lib/rules/${name}.ts`\n);\n\ntype MessageIds = \"preferDestructured\" | \"preferDestructuredGlobal\";\n\ntype PackageConfig = {\n /** Mapping of package names to their global UMD variable names (e.g., { \"react\": \"React\", \"lodash\": \"_\" }) */\n packages: Record<string, string>;\n};\n\ntype Options = [PackageConfig?];\n\n/**\n * ESLint rule: prefer-destructured-imports\n *\n * ## Configuration\n *\n * This rule requires explicit configuration to specify which packages to enforce.\n * Without configuration, the rule does nothing.\n *\n * @example\n * // .eslintrc.js - Enforce for React\n * {\n * \"rules\": {\n * \"@trackunit/prefer-destructured-imports\": [\"error\", {\n * \"packages\": {\n * \"react\": \"React\"\n * }\n * }]\n * }\n * }\n * @example\n * // .eslintrc.js - Multiple packages\n * {\n * \"rules\": {\n * \"@trackunit/prefer-destructured-imports\": [\"error\", {\n * \"packages\": {\n * \"react\": \"React\",\n * \"lodash\": \"_\",\n * \"jquery\": \"$\"\n * }\n * }]\n * }\n * }\n */\nexport const preferDestructuredImports = createRule<Options, MessageIds>({\n name: \"prefer-destructured-imports\",\n meta: {\n type: \"suggestion\",\n docs: {\n description: \"Prefer destructured imports instead of namespace imports for certain packages\",\n },\n fixable: \"code\",\n schema: [\n {\n type: \"object\",\n properties: {\n packages: {\n type: \"object\",\n description: \"Mapping of package names to their global UMD variable names (e.g., { 'react': 'React' })\",\n additionalProperties: {\n type: \"string\",\n },\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n preferDestructured: \"Prefer destructured imports instead of namespace imports\",\n preferDestructuredGlobal:\n \"Import '{{members}}' from '{{packageName}}' instead of using the global {{globalName}} variable\",\n },\n },\n defaultOptions: [],\n create(context) {\n const sourceCode = context.sourceCode;\n const options = context.options[0];\n\n // Return early if no packages are configured\n if (options === undefined || Object.keys(options.packages).length === 0) {\n return {};\n }\n\n const packageToGlobalName = options.packages;\n const targetPackages = Object.keys(packageToGlobalName);\n\n // Helper to check if a string is a valid/configured package name\n const isValidPackageName = (value: string): boolean => {\n return targetPackages.includes(value);\n };\n\n // Track which global identifiers are actually imported (to avoid false positives)\n const importedGlobalIdentifiers = new Set<string>();\n\n const namespaceImports = new Map<string, { node: TSESTree.ImportDeclaration; existingSpecifiers: Set<string> }>();\n const usagesByImport = new Map<string, Map<number, { name: string; start: number; end: number }>>();\n\n // Track usages of global identifiers that aren't imported (e.g., React.ReactElement without importing React)\n const globalUsages = new Map<string, { packageName: string; members: Set<string>; nodes: Array<TSESTree.Node> }>();\n\n return {\n ImportDeclaration(node: TSESTree.ImportDeclaration) {\n const sourceValue = node.source.value;\n if (typeof sourceValue === \"string\" && isValidPackageName(sourceValue)) {\n const globalName = packageToGlobalName[sourceValue];\n\n // Get any existing destructured imports\n const existingSpecifiers = new Set(\n node.specifiers\n .filter((s): s is TSESTree.ImportSpecifier => s.type === AST_NODE_TYPES.ImportSpecifier)\n .map(s => s.local.name)\n );\n\n // Find the namespace or default import if it exists\n const namespaceOrDefaultSpecifier = node.specifiers.find(\n (s): s is TSESTree.ImportDefaultSpecifier | TSESTree.ImportNamespaceSpecifier =>\n s.type === AST_NODE_TYPES.ImportDefaultSpecifier || s.type === AST_NODE_TYPES.ImportNamespaceSpecifier\n );\n\n if (namespaceOrDefaultSpecifier) {\n const importName = namespaceOrDefaultSpecifier.local.name;\n // Only mark this global as imported if the import name matches the global name\n // This prevents false positives when someone imports React as a different name\n if (importName === globalName) {\n importedGlobalIdentifiers.add(globalName);\n }\n\n // If this import also has destructured specifiers, store it as both\n if (existingSpecifiers.size > 0) {\n namespaceImports.set(\"destructured\", { node, existingSpecifiers });\n }\n namespaceImports.set(importName, { node, existingSpecifiers });\n usagesByImport.set(importName, new Map());\n } else if (existingSpecifiers.size > 0) {\n // Store pure destructured imports\n namespaceImports.set(\"destructured\", { node, existingSpecifiers });\n // Do NOT mark the global as imported - we want to catch global usage even with destructured imports\n }\n }\n },\n\n // Handle React.something in regular code\n MemberExpression(node: TSESTree.MemberExpression) {\n const objectNode = node.object;\n\n if (objectNode.type === AST_NODE_TYPES.Identifier && node.property.type === AST_NODE_TYPES.Identifier) {\n // Check if this is a tracked namespace import\n if (namespaceImports.has(objectNode.name)) {\n const usages = usagesByImport.get(objectNode.name);\n if (usages) {\n usages.set(node.range[0], {\n name: node.property.name,\n start: node.range[0],\n end: node.range[1],\n });\n }\n }\n // Check if this is a global identifier usage (e.g., React.ReactElement without import)\n else if (!importedGlobalIdentifiers.has(objectNode.name)) {\n // Find if this identifier matches any of our configured global names\n for (const packageName of targetPackages) {\n const globalName = packageToGlobalName[packageName];\n if (objectNode.name === globalName) {\n if (!globalUsages.has(globalName)) {\n globalUsages.set(globalName, {\n packageName: packageName,\n members: new Set(),\n nodes: [],\n });\n }\n const usage = globalUsages.get(globalName);\n if (usage) {\n usage.members.add(node.property.name);\n usage.nodes.push(node);\n }\n break;\n }\n }\n }\n }\n },\n\n // Handle React.ReactNode in type annotations\n TSQualifiedName(node: TSESTree.TSQualifiedName) {\n if (node.left.type === AST_NODE_TYPES.Identifier) {\n // Check if this is a tracked namespace import\n if (namespaceImports.has(node.left.name)) {\n const usages = usagesByImport.get(node.left.name);\n if (usages) {\n usages.set(node.range[0], {\n name: node.right.name,\n start: node.range[0],\n end: node.range[1],\n });\n }\n }\n // Check if this is a global identifier usage\n else if (!importedGlobalIdentifiers.has(node.left.name)) {\n for (const packageName of targetPackages) {\n const globalName = packageToGlobalName[packageName];\n if (node.left.name === globalName) {\n if (!globalUsages.has(globalName)) {\n globalUsages.set(globalName, {\n packageName: packageName,\n members: new Set(),\n nodes: [],\n });\n }\n const usage = globalUsages.get(globalName);\n if (usage) {\n usage.members.add(node.right.name);\n usage.nodes.push(node);\n }\n break;\n }\n }\n }\n }\n },\n\n // Note: TSQualifiedName already handles React.ReactElement, React.RefObject, etc.\n // This visitor is kept for potential future expansion but currently does nothing\n // to avoid duplicate tracking with TSQualifiedName\n TSTypeReference(_node: TSESTree.TSTypeReference) {\n // TSQualifiedName visitor handles all qualified type names (React.Something)\n // No action needed here\n },\n\n \"Program:exit\"() {\n // Report errors for namespace imports\n for (const [importName, { node: importNode }] of namespaceImports) {\n const usages = usagesByImport.get(importName);\n if (!usages || usages.size === 0) {\n continue;\n }\n\n const sourceValue = importNode.source.value;\n\n if (typeof sourceValue !== \"string\" || !isValidPackageName(sourceValue)) {\n continue;\n }\n\n context.report({\n node: importNode,\n messageId: \"preferDestructured\",\n fix(fixer) {\n const fixes = [];\n\n // Collect all specifiers needed from usages (e.g., [\"useState\", \"useEffect\"] from React.useState, React.useEffect)\n const specifiersFromUsages = Array.from(usages.values()).map(usage => usage.name);\n\n // Check if there's a separate destructured import to merge with\n const existingDestructured = namespaceImports.get(\"destructured\");\n const otherDestructuredImport =\n existingDestructured && existingDestructured.node !== importNode\n ? existingDestructured.node\n : undefined;\n\n // Replace the namespace import with destructured imports (merges if other import exists)\n fixes.push(\n ...replaceNamespaceWithDestructured({\n sourceCode,\n fixer,\n packageName: sourceValue,\n namespaceImportToReplace: importNode,\n specifiersToAdd: specifiersFromUsages,\n mergeIntoImport: otherDestructuredImport,\n })\n );\n\n // Remove namespace/default prefix from all usages\n for (const { start, end } of usages.values()) {\n const text = sourceCode.getText().slice(start, end);\n const newText = text.replace(new RegExp(`^${importName}\\\\.`), \"\");\n fixes.push(fixer.replaceTextRange([start, end], newText));\n }\n\n return fixes;\n },\n });\n }\n\n // Report errors for global UMD variable usages (e.g., React.ReactElement without import)\n for (const [globalName, { packageName, members, nodes }] of globalUsages) {\n if (members.size === 0 || nodes.length === 0) {\n continue;\n }\n\n // Report on the first usage node\n const firstNode = nodes[0];\n if (!firstNode) {\n continue;\n }\n\n const membersList = Array.from(members);\n const existingDestructured = namespaceImports.get(\"destructured\");\n\n // Filter out members that are already imported (no need to add them again)\n const membersToAdd = existingDestructured\n ? membersList.filter(member => !existingDestructured.existingSpecifiers.has(member))\n : membersList;\n\n context.report({\n node: firstNode,\n messageId: \"preferDestructuredGlobal\",\n data: {\n globalName,\n packageName,\n members: membersList.sort().join(\", \"), // Sort only for display in error message\n },\n fix(fixer) {\n const fixes = [];\n\n // Only update imports if there are new members to add\n if (membersToAdd.length > 0) {\n fixes.push(\n ...addImportSpecifiers({\n sourceCode,\n fixer,\n packageName,\n specifiers: membersToAdd,\n existingImport: existingDestructured?.node,\n })\n );\n }\n\n // Replace all global usages (React.X → X)\n // Deduplicate nodes by range and sort by position (reverse order to maintain positions)\n const uniqueNodesByRange = new Map<string, TSESTree.Node>();\n for (const node of nodes) {\n const rangeKey = `${node.range[0]}-${node.range[1]}`;\n if (!uniqueNodesByRange.has(rangeKey)) {\n uniqueNodesByRange.set(rangeKey, node);\n }\n }\n\n const sortedNodes = Array.from(uniqueNodesByRange.values()).sort((a, b) => b.range[0] - a.range[0]);\n\n for (const node of sortedNodes) {\n const nodeText = sourceCode.getText(node);\n const newText = nodeText.replace(new RegExp(`^${globalName}\\\\.`), \"\");\n fixes.push(fixer.replaceText(node, newText));\n }\n\n return fixes;\n },\n });\n }\n },\n };\n },\n});\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"name-suggestion-strategies.js","sourceRoot":"","sources":["../../../../../../../../libs/eslint/plugin-trackunit/src/lib/rules/prefer-event-specific-callback-naming/name-suggestion-strategies.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Strategy-based name suggestion system for the prefer-event-specific-callback-naming rule.\n *\n * This module provides a structured, extensible approach to generating suggested names\n * for callback functions based on different naming patterns. Each strategy handles a\n * specific type of problematic name and knows how to transform it correctly.\n *\n * Strategies are evaluated in order - the first matching strategy wins.\n */\n\n/**\n * Context passed to name suggestion strategies.\n */\nexport type NameSuggestionContext = {\n /** The current callback name (e.g., \"onPrimary\", \"onClose\", \"handleClick\") */\n callbackName: string;\n /** The event handler prop name (e.g., \"onClick\", \"primaryAction\", \"onSubmit\") */\n eventName: string;\n};\n\n/**\n * A strategy for suggesting renamed callback names.\n * Each strategy handles a specific pattern of problematic naming.\n */\nexport type NameSuggestionStrategy = {\n /** Human-readable name for debugging and logging */\n name: string;\n /** Returns true if this strategy should handle the given context */\n matches: (ctx: NameSuggestionContext) => boolean;\n /** Returns the suggested name for the callback */\n suggest: (ctx: NameSuggestionContext) => string;\n};\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\n/**\n * Capitalizes the first letter of a string.\n *\n * @example capitalize(\"primary\") // \"Primary\"\n */\nconst capitalize = (str: string): string => {\n if (str.length === 0) return str;\n return str.charAt(0).toUpperCase() + str.slice(1);\n};\n\n// =============================================================================\n// Strategy Implementations\n// =============================================================================\n\n/**\n * Strategy: Handle `*Action` suffix event names.\n *\n * When the event prop ends with \"Action\" (e.g., \"primaryAction\", \"secondaryAction\"),\n * the prop itself is poorly named for a click handler. We extract the prefix and\n * construct a proper onClick* name.\n *\n * @example\n * - eventName: \"primaryAction\", callbackName: \"onPrimary\" → \"onClickPrimary\"\n * - eventName: \"secondaryAction\", callbackName: \"action\" → \"onClickSecondary\"\n * - eventName: \"primaryAction\", callbackName: \"foo\" → \"onClickPrimary\"\n */\nconst actionSuffixEventStrategy: NameSuggestionStrategy = {\n name: \"actionSuffixEventStrategy\",\n\n matches: (ctx: NameSuggestionContext): boolean => {\n // Matches when the event name ends with \"Action\" (case-sensitive)\n return ctx.eventName.endsWith(\"Action\");\n },\n\n suggest: (ctx: NameSuggestionContext): string => {\n // Extract the prefix before \"Action\" (e.g., \"primary\" from \"primaryAction\")\n const prefix = ctx.eventName.slice(0, -\"Action\".length);\n\n // If the callback starts with \"on\", extract its semantic part\n // e.g., \"onPrimary\" → \"Primary\", \"onConfirm\" → \"Confirm\"\n if (ctx.callbackName.startsWith(\"on\") && ctx.callbackName.length > 2) {\n const callbackSuffix = ctx.callbackName.slice(2);\n\n // If the callback suffix matches the event prefix (case-insensitive),\n // use the callback's casing for consistency\n // e.g., eventName: \"primaryAction\", callbackName: \"onPrimary\" → \"onClickPrimary\"\n if (callbackSuffix.toLowerCase() === prefix.toLowerCase()) {\n return `onClick${callbackSuffix}`;\n }\n\n // Otherwise, combine both for clarity\n // e.g., eventName: \"primaryAction\", callbackName: \"onConfirm\" → \"onClickPrimaryConfirm\"\n return `onClick${capitalize(prefix)}${callbackSuffix}`;\n }\n\n // For non-\"on\" prefixed callbacks, just use the event prefix\n // e.g., eventName: \"primaryAction\", callbackName: \"foo\" → \"onClickPrimary\"\n return `onClick${capitalize(prefix)}`;\n },\n};\n\n/**\n * Strategy: Handle `*Handler` suffix event names.\n *\n * When the event prop ends with \"Handler\" (e.g., \"clickHandler\", \"submitHandler\"),\n * the prop itself is poorly named. We extract the prefix and construct a proper onClick* name.\n *\n * @example\n * - eventName: \"clickHandler\", callbackName: \"onClose\" → \"onClickClose\"\n * - eventName: \"submitHandler\", callbackName: \"foo\" → \"onClickSubmit\"\n */\nconst handlerSuffixEventStrategy: NameSuggestionStrategy = {\n name: \"handlerSuffixEventStrategy\",\n\n matches: (ctx: NameSuggestionContext): boolean => {\n // Matches when the event name ends with \"Handler\" (case-sensitive)\n return ctx.eventName.endsWith(\"Handler\");\n },\n\n suggest: (ctx: NameSuggestionContext): string => {\n // Extract the prefix before \"Handler\" (e.g., \"click\" from \"clickHandler\")\n const prefix = ctx.eventName.slice(0, -\"Handler\".length);\n\n // If the callback starts with \"on\", extract its semantic part\n if (ctx.callbackName.startsWith(\"on\") && ctx.callbackName.length > 2) {\n const callbackSuffix = ctx.callbackName.slice(2);\n return `onClick${capitalize(prefix)}${callbackSuffix}`;\n }\n\n // For non-\"on\" prefixed callbacks\n return `onClick${capitalize(prefix)}`;\n },\n};\n\n/**\n * Strategy: Handle `on*` callback → `onClick` event.\n *\n * When a callback starts with \"on\" (but not \"onClick\") and is passed to an onClick handler,\n * we replace the \"on\" prefix with \"onClick\".\n *\n * @example\n * - eventName: \"onClick\", callbackName: \"onClose\" → \"onClickClose\"\n * - eventName: \"onClick\", callbackName: \"onCancel\" → \"onClickCancel\"\n */\nconst onPrefixCallbackToClickEventStrategy: NameSuggestionStrategy = {\n name: \"onPrefixCallbackToClickEventStrategy\",\n\n matches: (ctx: NameSuggestionContext): boolean => {\n return ctx.eventName === \"onClick\" && ctx.callbackName.startsWith(\"on\") && !ctx.callbackName.startsWith(\"onClick\");\n },\n\n suggest: (ctx: NameSuggestionContext): string => {\n // Replace \"on\" with \"onClick\"\n // e.g., \"onClose\" → \"onClickClose\"\n const suffix = ctx.callbackName.slice(2);\n return `onClick${suffix}`;\n },\n};\n\n/**\n * Strategy: Handle `on*` callback → generic `on*` event (not onClick).\n *\n * When a callback starts with \"on\" and is passed to a different on* event handler\n * (like onMouseDown, onSubmit, etc.), we replace the \"on\" prefix with the event name.\n *\n * @example\n * - eventName: \"onMouseDown\", callbackName: \"onClose\" → \"onMouseDownClose\"\n * - eventName: \"onSubmit\", callbackName: \"onConfirm\" → \"onSubmitConfirm\"\n */\nconst onPrefixCallbackToGenericEventStrategy: NameSuggestionStrategy = {\n name: \"onPrefixCallbackToGenericEventStrategy\",\n\n matches: (ctx: NameSuggestionContext): boolean => {\n return (\n ctx.eventName.startsWith(\"on\") &&\n ctx.eventName !== \"onClick\" &&\n ctx.callbackName.startsWith(\"on\") &&\n !ctx.callbackName.startsWith(ctx.eventName)\n );\n },\n\n suggest: (ctx: NameSuggestionContext): string => {\n const callbackSuffix = ctx.callbackName.slice(2);\n\n // Avoid duplication: if event name already ends with the suffix, just use the event name\n // e.g., eventName: \"onClickClose\", callbackName: \"onClose\" → \"onClickClose\"\n if (ctx.eventName.toLowerCase().endsWith(callbackSuffix.toLowerCase())) {\n return ctx.eventName;\n }\n\n return `${ctx.eventName}${callbackSuffix}`;\n },\n};\n\n/**\n * Strategy: Default fallback.\n *\n * For any callback that doesn't match other patterns, capitalize and prepend\n * the event name.\n *\n * @example\n * - eventName: \"onClick\", callbackName: \"myCallback\" → \"onClickMyCallback\"\n * - eventName: \"onClick\", callbackName: \"close\" → \"onClickClose\"\n */\nconst defaultCapitalizeStrategy: NameSuggestionStrategy = {\n name: \"defaultCapitalizeStrategy\",\n\n matches: (): boolean => {\n // Always matches as a fallback\n return true;\n },\n\n suggest: (ctx: NameSuggestionContext): string => {\n const capitalizedName = capitalize(ctx.callbackName);\n return `${ctx.eventName}${capitalizedName}`;\n },\n};\n\n// =============================================================================\n// Strategy Registry\n// =============================================================================\n\n/**\n * Ordered list of strategies. First matching strategy wins.\n *\n * Order matters:\n * 1. Specific patterns (Action suffix, Handler suffix) first\n * 2. General patterns (on* prefix) next\n * 3. Default fallback last\n */\nconst strategies: ReadonlyArray<NameSuggestionStrategy> = [\n actionSuffixEventStrategy,\n handlerSuffixEventStrategy,\n onPrefixCallbackToClickEventStrategy,\n onPrefixCallbackToGenericEventStrategy,\n defaultCapitalizeStrategy,\n];\n\n// =============================================================================\n// Public API\n// =============================================================================\n\n/**\n * Generates a suggested name for a callback based on the event it's passed to.\n *\n * Uses a strategy pattern to handle different naming conventions appropriately.\n * The first matching strategy determines the suggested name.\n *\n * @param ctx - The naming context with callback and event names\n * @returns The suggested name for the callback\n * @example\n * getSuggestedName({ callbackName: \"onClose\", eventName: \"onClick\" })\n * // Returns: \"onClickClose\"\n *\n * getSuggestedName({ callbackName: \"onPrimary\", eventName: \"primaryAction\" })\n * // Returns: \"onClickPrimary\"\n */\nexport const getSuggestedName = (ctx: NameSuggestionContext): string => {\n for (const strategy of strategies) {\n if (strategy.matches(ctx)) {\n return strategy.suggest(ctx);\n }\n }\n\n // Should never reach here since defaultCapitalizeStrategy always matches,\n // but TypeScript doesn't know that\n return defaultCapitalizeStrategy.suggest(ctx);\n};\n\n/**\n * Exported for testing purposes - allows testing individual strategies.\n */\nexport const testableStrategies = {\n actionSuffixEventStrategy,\n handlerSuffixEventStrategy,\n onPrefixCallbackToClickEventStrategy,\n onPrefixCallbackToGenericEventStrategy,\n defaultCapitalizeStrategy,\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"prefer-event-specific-callback-naming.js","sourceRoot":"","sources":["../../../../../../../../libs/eslint/plugin-trackunit/src/lib/rules/prefer-event-specific-callback-naming/prefer-event-specific-callback-naming.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;GAgBG;;;AAEH,oDAAiE;AAEjE,4DAAmG;AACnG,wDAAiG;AAEjG,MAAM,UAAU,GAAG,mBAAW,CAAC,WAAW,CACxC,IAAI,CAAC,EAAE,CAAC,6FAA6F,IAAI,IAAI,IAAI,KAAK,CACvH,CAAC;AA8BW,QAAA,iCAAiC,GAAG,UAAU,CAAsB;IAC/E,IAAI,EAAE,uCAAuC;IAC7C,IAAI,EAAE;QACJ,IAAI,EAAE,YAAY;QAClB,cAAc,EAAE,IAAI;QACpB,IAAI,EAAE;YACJ,WAAW,EACT,8EAA8E;gBAC9E,uGAAuG;SAC1G;QACD,MAAM,EAAE;YACN;gBACE,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,YAAY,EAAE;wBACZ,IAAI,EAAE,OAAO;wBACb,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;wBACzB,WAAW,EAAE,yCAAyC;qBACvD;oBACD,MAAM,EAAE;wBACN,IAAI,EAAE,OAAO;wBACb,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE;wBAC9C,WAAW,EACT,qEAAqE;4BACrE,yEAAyE;qBAC5E;iBACF;gBACD,oBAAoB,EAAE,KAAK;aAC5B;SACF;QACD,QAAQ,EAAE;YACR,GAAG,gCAAiB;YACpB,GAAG,8BAAiB;SACrB;KACF;IACD,cAAc,EAAE;QACd;YACE,YAAY,EAAE,EAAE;YAChB,MAAM,EAAE,CAAC,SAAS,CAAC;SACpB;KACF;IACD,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC;QACvB,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC;QAChD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,CAAC;QAE7C,OAAO;YACL,YAAY,CAAC,IAA2B;gBACtC,IAAA,6BAAc,EAAC,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;gBACpD,IAAA,2BAAc,EAAC,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;YACtD,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC,CAAC","sourcesContent":["/**\n * ESLint rule: prefer-event-specific-callback-naming\n *\n * Enforces that callbacks passed to event handler attributes use matching\n * event-specific naming (e.g., onClick* for onClick, onSubmit* for onSubmit).\n *\n * This prevents confusing names like `onClose` (sounds like \"when close happens\")\n * when the callback is actually triggered by a click event.\n *\n * Uses two detection strategies:\n * 1. Name-based: Checks configured event attributes for mismatched callback names\n * 2. Type-based: Detects any prop typed as MouseEventHandler with problematic naming\n * (only active when \"onClick\" is in the events list)\n *\n * Configuration via the `events` option controls which event handler attributes\n * are checked by both strategies.\n */\n\nimport { ESLintUtils, TSESTree } from \"@typescript-eslint/utils\";\n\nimport { checkNameBased, NameBasedMessageIds, nameBasedMessages } from \"./strategies/string-based\";\nimport { checkTypeBased, TypeBasedMessageIds, typeBasedMessages } from \"./strategies/type-based\";\n\nconst createRule = ESLintUtils.RuleCreator(\n name => `https://github.com/trackunit/manager/blob/main/libs/eslint/plugin-trackunit/src/lib/rules/${name}/${name}.ts`\n);\n\ntype Options = [\n {\n /**\n * Callback names to exempt from this rule.\n *\n * @example [\"onToggle\", \"onTrigger\"]\n */\n allowedNames?: ReadonlyArray<string>;\n\n /**\n * Event handler attributes to check for matching callback naming.\n *\n * For each event in this list, callbacks passed to that attribute must use\n * matching naming (e.g., `onClick*` for `onClick`, `onSubmit*` for `onSubmit`).\n *\n * - `on*` callbacks that don't match the event prefix are always flagged\n * - Other callbacks (`handle*`, `close`, etc.) are only flagged when they come from props\n * - When \"onClick\" is included, type-based MouseEventHandler detection is also enabled\n *\n * @default [\"onClick\"]\n * @example [\"onClick\", \"onSubmit\", \"onDoubleClick\"]\n */\n events?: ReadonlyArray<string>;\n },\n];\n\ntype MessageIds = NameBasedMessageIds | TypeBasedMessageIds;\n\nexport const preferEventSpecificCallbackNaming = createRule<Options, MessageIds>({\n name: \"prefer-event-specific-callback-naming\",\n meta: {\n type: \"suggestion\",\n hasSuggestions: true,\n docs: {\n description:\n \"Enforce using event-specific naming for callbacks passed to event handlers. \" +\n \"Names like onClose are confusing because they sound like lifecycle events rather than click handlers.\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n allowedNames: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Callback names to exempt from this rule\",\n },\n events: {\n type: \"array\",\n items: { type: \"string\", pattern: \"^on[A-Z]\" },\n description:\n \"Event handler attributes to check (e.g., ['onClick', 'onSubmit']). \" +\n \"Callbacks passed to these handlers must use matching on[Event]* naming.\",\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n ...nameBasedMessages,\n ...typeBasedMessages,\n },\n },\n defaultOptions: [\n {\n allowedNames: [],\n events: [\"onClick\"],\n },\n ],\n create(context, [options]) {\n const allowedNames = options.allowedNames ?? [];\n const events = options.events ?? [\"onClick\"];\n\n return {\n JSXAttribute(node: TSESTree.JSXAttribute) {\n checkNameBased(node, context, allowedNames, events);\n checkTypeBased(node, context, allowedNames, events);\n },\n };\n },\n});\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"string-based.js","sourceRoot":"","sources":["../../../../../../../../../libs/eslint/plugin-trackunit/src/lib/rules/prefer-event-specific-callback-naming/strategies/string-based.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Name-based detection strategy for prefer-event-specific-callback-naming rule.\n *\n * Checks JSX attributes whose names appear in the configured `events` list\n * and flags callbacks that don't use matching event prefix naming.\n *\n * Two levels of strictness:\n * - Callbacks starting with `on*` that don't match the event prefix are always flagged\n * (e.g., onClick={onClose} - regardless of source)\n * - Other callbacks (handle*, close, etc.) are only flagged when they come from props\n * (not internal functions or hook results)\n */\n\nimport { AST_NODE_TYPES, TSESLint, TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isIdentifierFromParameter, isIdentifierFromRenderPropCallback } from \"../../../utils/ast-utils\";\nimport { extractCallbackIdentifier, findLocalDefinitionInfo, getSuggestedNameForEvent } from \"../utils\";\n\nexport type NameBasedMessageIds =\n | \"nameBasedEventNaming\"\n | \"nameBasedEventNamingWrapped\"\n | \"suggestRenameAllNameBased\"\n | \"suggestRenameLocalNameBased\";\n\nexport const nameBasedMessages: Record<NameBasedMessageIds, string> = {\n nameBasedEventNaming:\n \"Callback '{{current}}' passed to {{event}} should use '{{expectedPrefix}}*' naming (e.g., '{{suggested}}'). \" +\n \"Reported because {{event}} is an explicitly monitored event handler name (see rule 'events' option). Rename it at its definition.\",\n nameBasedEventNamingWrapped:\n \"Callback '{{current}}' called in {{event}} should use '{{expectedPrefix}}*' naming (e.g., '{{suggested}}'). \" +\n \"Reported because {{event}} is an explicitly monitored event handler name (see rule 'events' option). Rename it at its definition.\",\n suggestRenameAllNameBased: \"Rename '{{current}}' to '{{suggested}}' (definition and all usages in this file)\",\n suggestRenameLocalNameBased: \"Rename to '{{suggested}}' (this reference only - definition is in another file)\",\n};\n\n/**\n * Extracts the expected naming prefix from a suggested name.\n * This is used to show the correct expected pattern in error messages.\n *\n * @example\n * getExpectedPrefix(\"onClickPrimary\") // \"onClick\"\n * getExpectedPrefix(\"onMouseDownClose\") // \"onMouseDown\"\n */\nconst getExpectedPrefix = (suggestedName: string): string => {\n const match = suggestedName.match(/^(on[A-Z][a-z]*)/);\n return match?.[1] ?? suggestedName;\n};\n\ntype SuggestionDescriptor = {\n messageId: NameBasedMessageIds;\n data: { current: string; suggested: string };\n fix: (fixer: TSESLint.RuleFixer) => TSESLint.RuleFix | ReadonlyArray<TSESLint.RuleFix>;\n};\n\n/**\n * Unified name-based check for event handler callback naming.\n *\n * Checks whether a callback passed to a configured event attribute uses the correct\n * naming convention. The check applies two levels of strictness:\n *\n * 1. `on*` callbacks that don't match the event prefix are always flagged\n * (e.g., `onClick={onClose}` - should be `onClickClose`)\n * 2. Other callbacks (`handle*`, `close`, etc.) are only flagged when they come from\n * component props (not internal functions, hook results, or render prop callbacks)\n *\n * @param node - The JSX attribute node to check\n * @param context - The ESLint rule context\n * @param allowedNames - Callback names to exempt from this rule\n * @param events - Event handler attribute names to check (e.g., [\"onClick\", \"onSubmit\"])\n */\nexport const checkNameBased = (\n node: TSESTree.JSXAttribute,\n context: TSESLint.RuleContext<NameBasedMessageIds, ReadonlyArray<unknown>>,\n allowedNames: ReadonlyArray<string>,\n events: ReadonlyArray<string>\n): void => {\n // Must be a JSX identifier attribute\n if (node.name.type !== AST_NODE_TYPES.JSXIdentifier) {\n return;\n }\n\n const eventName = node.name.name;\n\n // Only check attributes in the events list\n if (!events.includes(eventName)) {\n return;\n }\n\n // Must have a value that's an expression container\n if (!node.value || node.value.type !== AST_NODE_TYPES.JSXExpressionContainer) {\n return;\n }\n\n const { expression } = node.value;\n if (expression.type === AST_NODE_TYPES.JSXEmptyExpression) {\n return;\n }\n\n // Extract callback identifier (without checking specific naming patterns)\n const result = extractCallbackIdentifier(expression);\n\n if (!result) {\n return;\n }\n\n // Check if this name is in the allowed list\n if (allowedNames.includes(result.callbackName)) {\n return;\n }\n\n // Check if the name already matches the event pattern (e.g., onClick* for onClick)\n if (result.callbackName.startsWith(eventName)) {\n return;\n }\n\n // Determine if this is an on* callback that doesn't match the event prefix\n const isOnPrefixMismatch = result.callbackName.startsWith(\"on\") && !result.callbackName.startsWith(eventName);\n\n if (!isOnPrefixMismatch) {\n // For non-on* callbacks (handle*, close, etc.), only flag if they come from props\n // but NOT from render prop callbacks (e.g., {close => <MenuItem onClick={close} />})\n if (!isIdentifierFromParameter(result.identifierNode, context)) {\n return;\n }\n if (isIdentifierFromRenderPropCallback(result.identifierNode, context)) {\n return;\n }\n }\n\n const suggestedName = getSuggestedNameForEvent(result.callbackName, eventName);\n\n // Select the appropriate messageId based on whether the callback is wrapped\n const messageId: NameBasedMessageIds = result.isWrapped ? \"nameBasedEventNamingWrapped\" : \"nameBasedEventNaming\";\n\n // Build suggestions based on whether the definition is in the same file\n const suggestions: Array<SuggestionDescriptor> = [];\n const localInfo = findLocalDefinitionInfo(result.identifierNode, context);\n\n if (localInfo) {\n // Definition is in this file - offer to rename all occurrences\n suggestions.push({\n messageId: \"suggestRenameAllNameBased\",\n data: {\n current: result.callbackName,\n suggested: suggestedName,\n },\n fix: fixer => {\n return localInfo.allNodesToRename.map(nodeToRename => fixer.replaceText(nodeToRename, suggestedName));\n },\n });\n } else {\n // Definition is external - only offer to rename this reference\n suggestions.push({\n messageId: \"suggestRenameLocalNameBased\",\n data: {\n current: result.callbackName,\n suggested: suggestedName,\n },\n fix: fixer => {\n return fixer.replaceText(result.identifierNode, suggestedName);\n },\n });\n }\n\n context.report({\n node: result.node,\n messageId,\n data: {\n current: result.callbackName,\n suggested: suggestedName,\n event: eventName,\n expectedPrefix: getExpectedPrefix(suggestedName),\n },\n suggest: suggestions,\n });\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"type-based.js","sourceRoot":"","sources":["../../../../../../../../../libs/eslint/plugin-trackunit/src/lib/rules/prefer-event-specific-callback-naming/strategies/type-based.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Type-based detection strategy for prefer-event-specific-callback-naming rule.\n *\n * Uses TypeScript's type checker to detect when a callback with problematic naming\n * is passed to a prop typed as a mouse event handler (MouseEventHandler, MouseEvent, etc.).\n *\n * This catches cases where custom components use non-standard prop names for click handlers:\n * - <MobileButton onTap={onClose} /> where onTap is typed as MouseEventHandler\n * - <PressableArea onPress={onCancel} /> where onPress accepts MouseEvent\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, TSESLint, TSESTree } from \"@typescript-eslint/utils\";\nimport * as ts from \"typescript\";\n\nimport { extractCallbackIdentifier, findLocalDefinitionInfo, getSuggestedNameForEvent } from \"../utils\";\n\nexport type TypeBasedMessageIds =\n | \"preferOnClickNamingTyped\"\n | \"preferOnClickNamingTypedWrapped\"\n | \"suggestRenameAllTyped\"\n | \"suggestRenameLocalTyped\";\n\nexport const typeBasedMessages: Record<TypeBasedMessageIds, string> = {\n preferOnClickNamingTyped:\n \"Callback '{{current}}' passed to {{event}} should use '{{expectedPrefix}}*' naming (e.g., '{{suggested}}'). \" +\n \"Reported because {{event}} is typed as a MouseEventHandler. Rename it at its definition.\",\n preferOnClickNamingTypedWrapped:\n \"Callback '{{current}}' called in {{event}} should use '{{expectedPrefix}}*' naming (e.g., '{{suggested}}'). \" +\n \"Reported because {{event}} is typed as a MouseEventHandler. Rename it at its definition.\",\n suggestRenameAllTyped: \"Rename '{{current}}' to '{{suggested}}' (definition and all usages in this file)\",\n suggestRenameLocalTyped: \"Rename to '{{suggested}}' (this reference only - definition is in another file)\",\n};\n\n/**\n * Extracts the expected naming prefix from a suggested name.\n * This is used to show the correct expected pattern in error messages.\n *\n * @example\n * getExpectedPrefix(\"onClickPrimary\") // \"onClick\"\n * getExpectedPrefix(\"onMouseDownClose\") // \"onMouseDown\"\n */\nconst getExpectedPrefix = (suggestedName: string): string => {\n // Find where the capitalized suffix starts (after the event prefix)\n // e.g., \"onClickPrimary\" -> find \"Primary\" -> prefix is \"onClick\"\n const match = suggestedName.match(/^(on[A-Z][a-z]*)/);\n return match?.[1] ?? suggestedName;\n};\n\ntype SuggestionDescriptor = {\n messageId: TypeBasedMessageIds;\n data: { current: string; suggested: string };\n fix: (fixer: TSESLint.RuleFixer) => TSESLint.RuleFix | ReadonlyArray<TSESLint.RuleFix>;\n};\n\n/**\n * Checks if a type represents a mouse event handler.\n * Looks for:\n * - MouseEventHandler<T>\n * - React.MouseEventHandler<T>\n * - Function types with MouseEvent as first parameter\n */\nconst isMouseEventHandlerType = (type: ts.Type, checker: ts.TypeChecker): boolean => {\n const typeString = checker.typeToString(type);\n\n // Quick check for common patterns in the type string\n if (\n typeString.includes(\"MouseEventHandler\") ||\n typeString.includes(\"MouseEvent<\") ||\n typeString.includes(\"MouseEvent)\")\n ) {\n return true;\n }\n\n // Check call signatures for MouseEvent parameter\n const signatures = type.getCallSignatures();\n for (const signature of signatures) {\n const params = signature.getParameters();\n if (params.length > 0) {\n const firstParam = params[0];\n if (firstParam) {\n const paramType = checker.getTypeOfSymbol(firstParam);\n const paramTypeString = checker.typeToString(paramType);\n\n if (paramTypeString.includes(\"MouseEvent\")) {\n return true;\n }\n }\n }\n }\n\n return false;\n};\n\n/**\n * Checks a JSX attribute for type-based mouse event handler naming issues.\n *\n * Only runs when \"onClick\" is in the events list (since this check only detects\n * MouseEventHandler types, which are click events).\n *\n * Skips props already in the events list (those are handled by the name-based strategy).\n *\n * @param node - The JSX attribute node to check\n * @param context - The ESLint rule context\n * @param allowedNames - Callback names to exempt from this rule\n * @param events - Configured event names (used to skip already-covered props)\n */\nexport const checkTypeBased = (\n node: TSESTree.JSXAttribute,\n context: TSESLint.RuleContext<TypeBasedMessageIds, ReadonlyArray<unknown>>,\n allowedNames: ReadonlyArray<string>,\n events: ReadonlyArray<string>\n): void => {\n // Only run if \"onClick\" is in the events list, since this check only detects\n // MouseEventHandler types (which are click events)\n if (!events.includes(\"onClick\")) {\n return;\n }\n\n // Extract prop name - must be a JSXIdentifier\n if (node.name.type !== AST_NODE_TYPES.JSXIdentifier) {\n return;\n }\n\n const propName = node.name.name;\n\n // Skip props already in the events list - those are handled by the name-based strategy\n if (events.includes(propName)) {\n return;\n }\n\n // Must have a value that's an expression container\n if (!node.value || node.value.type !== AST_NODE_TYPES.JSXExpressionContainer) {\n return;\n }\n\n const { expression } = node.value;\n if (expression.type === AST_NODE_TYPES.JSXEmptyExpression) {\n return;\n }\n\n // Extract callback identifier (without checking hardcoded onClick pattern)\n const result = extractCallbackIdentifier(expression);\n\n if (!result) {\n return;\n }\n\n // Check if this name is in the allowed list\n if (allowedNames.includes(result.callbackName)) {\n return;\n }\n\n // Skip if callback name already matches or starts with the prop name\n // e.g., onMouseEnter={onMouseEnter} or onMouseEnter={onMouseEnterSomething}\n if (result.callbackName === propName || result.callbackName.startsWith(propName)) {\n return;\n }\n\n // Skip if callback doesn't start with \"on\" - we only care about on* callbacks\n // that might be confused with lifecycle/event callbacks\n if (!result.callbackName.startsWith(\"on\")) {\n return;\n }\n\n // Now check if the prop is typed as a mouse event handler\n try {\n const services = ESLintUtils.getParserServices(context);\n const checker = services.program.getTypeChecker();\n\n // Get the TypeScript node for the expression (already validated above)\n const tsExpressionNode = services.esTreeNodeToTSNodeMap.get(expression);\n\n // Get the contextual type (what type is expected at this position)\n // The ESTree to TS node map returns a generic node, but getContextualType\n // expects an Expression. This assertion is necessary for TypeScript API interop.\n // eslint-disable-next-line @trackunit/no-typescript-assertion\n const contextualType = checker.getContextualType(tsExpressionNode as ts.Expression);\n\n if (!contextualType) {\n return;\n }\n\n if (!isMouseEventHandlerType(contextualType, checker)) {\n return;\n }\n\n // Calculate suggested name using the actual prop name (e.g., onMouseDown -> onMouseDownClose)\n const suggestedName = getSuggestedNameForEvent(result.callbackName, propName);\n\n // Select the appropriate messageId based on whether the callback is wrapped\n const messageId: TypeBasedMessageIds = result.isWrapped\n ? \"preferOnClickNamingTypedWrapped\"\n : \"preferOnClickNamingTyped\";\n\n // Build suggestions based on whether the definition is in the same file\n const suggestions: Array<SuggestionDescriptor> = [];\n const localInfo = findLocalDefinitionInfo(result.identifierNode, context);\n\n if (localInfo) {\n // Definition is in this file - offer to rename all occurrences\n suggestions.push({\n messageId: \"suggestRenameAllTyped\",\n data: {\n current: result.callbackName,\n suggested: suggestedName,\n },\n fix: fixer => {\n return localInfo.allNodesToRename.map(nodeToRename => fixer.replaceText(nodeToRename, suggestedName));\n },\n });\n } else {\n // Definition is external - only offer to rename this reference\n suggestions.push({\n messageId: \"suggestRenameLocalTyped\",\n data: {\n current: result.callbackName,\n suggested: suggestedName,\n },\n fix: fixer => {\n return fixer.replaceText(result.identifierNode, suggestedName);\n },\n });\n }\n\n context.report({\n node: result.node,\n messageId,\n data: {\n current: result.callbackName,\n suggested: suggestedName,\n event: propName,\n expectedPrefix: getExpectedPrefix(suggestedName),\n },\n suggest: suggestions,\n });\n } catch {\n // Type checking failed - silently skip this check\n // This can happen if TypeScript services are not available\n return;\n }\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../../../../../../libs/eslint/plugin-trackunit/src/lib/rules/prefer-event-specific-callback-naming/utils.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shared utilities for the prefer-event-specific-callback-naming rule.\n * These functions handle the naming convention logic and callback extraction\n * used by both string-based and type-based detection strategies.\n */\n\nimport { AST_NODE_TYPES, TSESLint, TSESTree } from \"@typescript-eslint/utils\";\n\nimport { getSuggestedName as getStrategySuggestedName } from \"./name-suggestion-strategies\";\n\n/**\n * Result of extracting callback information from an expression.\n * Does not include messageId - each strategy adds its own.\n */\nexport type CallbackExtractionResult = {\n callbackName: string;\n suggestedName: string;\n node: TSESTree.Node;\n /** The identifier node for the callback - used for fix ranges */\n identifierNode: TSESTree.Identifier;\n /** Whether the callback is wrapped in an arrow function */\n isWrapped: boolean;\n} | null;\n\n/**\n * Information about a locally defined variable, including its definition\n * and all references within the same file.\n */\nexport type LocalDefinitionInfo = {\n /** The identifier node where the variable is defined */\n definitionNode: TSESTree.Identifier;\n /** All nodes that need to be renamed (variable references + type property definitions) */\n allNodesToRename: ReadonlyArray<TSESTree.Identifier>;\n};\n\n/**\n * Recursively finds all TSPropertySignature nodes in the AST with the given name.\n * This finds interface/type property definitions that need to be renamed along with the variable.\n */\nconst findTypePropertyDefinitions = (ast: TSESTree.Program, propertyName: string): Array<TSESTree.Identifier> => {\n const results: Array<TSESTree.Identifier> = [];\n\n const visit = (node: TSESTree.Node): void => {\n // Find interface/type property signatures with matching name\n if (\n node.type === AST_NODE_TYPES.TSPropertySignature &&\n node.key.type === AST_NODE_TYPES.Identifier &&\n node.key.name === propertyName\n ) {\n results.push(node.key);\n }\n\n // Recursively visit all child nodes\n // Use Object.entries to iterate without needing type assertions\n for (const [key, value] of Object.entries(node)) {\n if (key === \"parent\") continue; // Skip parent to avoid cycles\n\n if (value !== null && typeof value === \"object\") {\n if (Array.isArray(value)) {\n for (const item of value) {\n if (item !== null && typeof item === \"object\" && \"type\" in item) {\n visit(item);\n }\n }\n } else if (\"type\" in value) {\n visit(value);\n }\n }\n }\n };\n\n visit(ast);\n return results;\n};\n\n/**\n * Finds the local definition and all references of a variable in the current file.\n * Also finds TypeScript interface/type property definitions with the same name\n * (for React component props that need to be renamed in both the type and usage).\n * Returns null if the variable is not defined in the current file (e.g., imported).\n *\n * @param identifier - The identifier node to look up\n * @param context - The ESLint rule context\n * @returns LocalDefinitionInfo if defined locally, null if defined externally\n */\nexport const findLocalDefinitionInfo = (\n identifier: TSESTree.Identifier,\n context: TSESLint.RuleContext<string, ReadonlyArray<unknown>>\n): LocalDefinitionInfo | null => {\n const scope = context.sourceCode.getScope(identifier);\n const variable = scope.references.find(ref => ref.identifier === identifier)?.resolved;\n\n if (!variable) {\n return null;\n }\n\n // Check if the variable has definition nodes in this file\n const definitions = variable.defs;\n if (definitions.length === 0) {\n // No definitions found - likely a global or imported variable\n return null;\n }\n\n // Get the first definition (typically there's only one)\n const definition = definitions[0];\n if (!definition) {\n return null;\n }\n\n // The definition's name node is where the variable is defined\n // It could be an Identifier, ArrayPattern, or ObjectPattern\n // We only handle simple Identifier definitions\n const definitionName = definition.name;\n if (definitionName.type !== AST_NODE_TYPES.Identifier) {\n return null;\n }\n\n // Collect all variable references (identifiers that refer to this variable)\n const variableReferences = variable.references\n .map(ref => ref.identifier)\n .filter((node): node is TSESTree.Identifier => node.type === AST_NODE_TYPES.Identifier);\n\n // Also find TypeScript type property definitions with the same name\n // This handles the case where a callback is destructured from props:\n // interface Props { onCancel: () => void } <-- need to rename this too\n // const { onCancel } = props; <-- the variable reference\n const ast = context.sourceCode.ast;\n const typePropertyDefinitions = findTypePropertyDefinitions(ast, identifier.name);\n\n // Combine definition, variable references, and type property definitions\n // Use a Set based on node range to deduplicate (needed because shorthand properties\n // may share node ranges between definition and reference)\n const seenRanges = new Set<string>();\n const allNodesToRename: Array<TSESTree.Identifier> = [];\n\n for (const node of [definitionName, ...variableReferences, ...typePropertyDefinitions]) {\n const rangeKey = `${node.range[0]}-${node.range[1]}`;\n if (!seenRanges.has(rangeKey)) {\n seenRanges.add(rangeKey);\n allNodesToRename.push(node);\n }\n }\n\n return {\n definitionNode: definitionName,\n allNodesToRename,\n };\n};\n\n/**\n * Checks if a callback name is problematic when used with click handlers.\n * A name is problematic if it starts with \"on\" but not \"onClick\".\n *\n * @example\n * isProblematicName(\"onClose\") // true - should be onClickClose\n * isProblematicName(\"onClickClose\") // false - already correct\n * isProblematicName(\"handleClose\") // false - different convention\n * isProblematicName(\"close\") // false - doesn't start with \"on\"\n */\nconst isProblematicName = (name: string): boolean => {\n return name.startsWith(\"on\") && !name.startsWith(\"onClick\");\n};\n\n/**\n * Converts a problematic callback name to the suggested onClick* format.\n *\n * @example\n * getSuggestedName(\"onClose\") // \"onClickClose\"\n * getSuggestedName(\"onCancel\") // \"onClickCancel\"\n */\nconst getSuggestedName = (name: string): string => {\n // Remove \"on\" prefix and add \"onClick\" prefix\n return `onClick${name.slice(2)}`;\n};\n\n/**\n * Extracts callback info from a direct identifier pattern: onClick={onClose}\n */\nconst extractFromDirectIdentifier = (expression: TSESTree.Expression): CallbackExtractionResult => {\n if (expression.type !== AST_NODE_TYPES.Identifier) {\n return null;\n }\n\n const callbackName = expression.name;\n if (!isProblematicName(callbackName)) {\n return null;\n }\n\n return {\n callbackName,\n suggestedName: getSuggestedName(callbackName),\n node: expression,\n identifierNode: expression,\n isWrapped: false,\n };\n};\n\n/**\n * Extracts callback info from a single-call arrow function: onClick={() => onClose()}\n */\nconst extractFromSingleCallArrow = (expression: TSESTree.Expression): CallbackExtractionResult => {\n if (expression.type !== AST_NODE_TYPES.ArrowFunctionExpression) {\n return null;\n }\n\n const { body } = expression;\n\n // Only check if body is a single call expression (not a block statement)\n if (body.type !== AST_NODE_TYPES.CallExpression) {\n return null;\n }\n\n // Only check if the callee is a simple identifier\n if (body.callee.type !== AST_NODE_TYPES.Identifier) {\n return null;\n }\n\n const callbackName = body.callee.name;\n if (!isProblematicName(callbackName)) {\n return null;\n }\n\n return {\n callbackName,\n suggestedName: getSuggestedName(callbackName),\n node: body.callee,\n identifierNode: body.callee,\n isWrapped: true,\n };\n};\n\n/**\n * Extracts callback information from an expression.\n * Handles both direct identifiers (onClick={onClose}) and\n * single-call arrow functions (onClick={() => onClose()}).\n *\n * @returns Callback extraction result without messageId, or null if no problematic callback found\n */\nconst extractCallbackInfo = (expression: TSESTree.Expression): CallbackExtractionResult => {\n return extractFromDirectIdentifier(expression) ?? extractFromSingleCallArrow(expression);\n};\n\n/**\n * Result of extracting a callback identifier from an expression.\n * Unlike CallbackExtractionResult, this doesn't include suggested names\n * since those depend on the specific event handler being used.\n */\nexport type CallbackIdentifierResult = {\n callbackName: string;\n node: TSESTree.Node;\n /** The identifier node for the callback - used for fix ranges */\n identifierNode: TSESTree.Identifier;\n /** Whether the callback is wrapped in an arrow function */\n isWrapped: boolean;\n} | null;\n\n/**\n * Extracts the callback identifier from an expression without checking naming patterns.\n * Used by strict prop events check which has its own naming criteria.\n *\n * @returns Callback identifier info, or null if not a simple identifier/single-call arrow\n */\nexport const extractCallbackIdentifier = (expression: TSESTree.Expression): CallbackIdentifierResult => {\n // Direct identifier: onClick={onClose}\n if (expression.type === AST_NODE_TYPES.Identifier) {\n return {\n callbackName: expression.name,\n node: expression,\n identifierNode: expression,\n isWrapped: false,\n };\n }\n\n // Single-call arrow function: onClick={() => onClose()}\n if (expression.type === AST_NODE_TYPES.ArrowFunctionExpression) {\n const { body } = expression;\n\n // Only check if body is a single call expression (not a block statement)\n if (body.type !== AST_NODE_TYPES.CallExpression) {\n return null;\n }\n\n // Only check if the callee is a simple identifier\n if (body.callee.type !== AST_NODE_TYPES.Identifier) {\n return null;\n }\n\n return {\n callbackName: body.callee.name,\n node: body.callee,\n identifierNode: body.callee,\n isWrapped: true,\n };\n }\n\n return null;\n};\n\n/**\n * Generates the suggested name for a callback based on the event handler it's passed to.\n *\n * Uses a strategy-based approach to handle different naming patterns appropriately.\n * See `name-suggestion-strategies.ts` for the individual strategies.\n *\n * @param currentName - The current callback name (e.g., \"secondaryAction\", \"close\")\n * @param eventName - The event handler name (e.g., \"onClick\", \"onSubmit\", \"primaryAction\")\n * @returns The suggested name (e.g., \"onClickSecondaryAction\", \"onSubmitClose\", \"onClickPrimary\")\n * @example\n * getSuggestedNameForEvent(\"close\", \"onClick\") // \"onClickClose\"\n * getSuggestedNameForEvent(\"onCancel\", \"onClick\") // \"onClickCancel\"\n * getSuggestedNameForEvent(\"onPrimary\", \"primaryAction\") // \"onClickPrimary\"\n * getSuggestedNameForEvent(\"onClose\", \"onClickClose\") // \"onClickClose\"\n */\nexport const getSuggestedNameForEvent = (currentName: string, eventName: string): string => {\n return getStrategySuggestedName({ callbackName: currentName, eventName });\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"prefer-field-components.js","sourceRoot":"","sources":["../../../../../../../../libs/eslint/plugin-trackunit/src/lib/rules/prefer-field-components/prefer-field-components.ts"],"names":[],"mappings":"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","sourcesContent":["import { AST_NODE_TYPES, ESLintUtils, TSESTree } from \"@typescript-eslint/utils\";\nimport { findImportDeclaration, getImportSpecifiers } from \"../../utils/import-utils\";\n\nconst createRule = ESLintUtils.RuleCreator(\n name => `https://github.com/trackunit/manager/blob/main/libs/eslint/plugin-trackunit/src/lib/rules/${name}/README.md`\n);\n\ntype MessageIds = \"preferFieldComponent\";\n\nconst FORM_COMPONENTS_PACKAGE = \"@trackunit/react-form-components\";\nconst REACT_COMPONENTS_PACKAGE = \"@trackunit/react-components\";\n\n/**\n * Map of base component names to their recommended Field component replacements\n * used in the error message (human-readable, may contain alternatives).\n */\nconst BASE_TO_FIELD_MESSAGE_MAP: Record<string, string> = {\n BaseSelect: \"SelectField (or MultiSelectField for multi-select)\",\n CreatableSelect: \"CreatableSelectField\",\n BaseInput: \"TextField (or the appropriate *Field component for your input type)\",\n TextBaseInput: \"TextField\",\n NumberBaseInput: \"NumberField\",\n DateBaseInput: \"DateField\",\n PhoneBaseInput: \"PhoneField\",\n TextAreaBaseInput: \"TextAreaField\",\n PasswordBaseInput: \"PasswordField\",\n EmailBaseInput: \"EmailField\",\n UrlBaseInput: \"UrlField\",\n};\n\n/**\n * Map used for autofix — always resolves to a single concrete component name.\n *\n * NOTE: BaseSelect and CreatableSelect are intentionally excluded here.\n * SelectField and CreatableSelectField have different value/onChange APIs\n * compared to their base counterparts:\n * - BaseSelect uses `value={optionObject}` and `onChange={(option) => ...}`\n * - SelectField uses `value={rawValue}` and `onChange={(event) => ...}`\n * Auto-fixing would silently break the component's behaviour.\n *\n * BaseSelect with isMulti → MultiSelectField IS compatible and is handled\n * separately in getAutofixFieldName().\n */\nconst BASE_TO_FIELD_AUTOFIX_MAP: Record<string, string> = {\n BaseInput: \"TextField\",\n TextBaseInput: \"TextField\",\n NumberBaseInput: \"NumberField\",\n DateBaseInput: \"DateField\",\n PhoneBaseInput: \"PhoneField\",\n TextAreaBaseInput: \"TextAreaField\",\n PasswordBaseInput: \"PasswordField\",\n EmailBaseInput: \"EmailField\",\n UrlBaseInput: \"UrlField\",\n};\n\nconst BASE_COMPONENT_NAMES = new Set(Object.keys(BASE_TO_FIELD_MESSAGE_MAP));\n\n/**\n * Component names that act as a manual label above a base input/select.\n */\nconst LABEL_COMPONENT_NAMES = new Set([\"Label\", \"Text\"]);\n\n/**\n * Get the JSX element name from a JSXOpeningElement.\n * Returns null for member expressions or namespaced names.\n */\nfunction getJSXElementName(openingElement: TSESTree.JSXOpeningElement): string | null {\n if (openingElement.name.type === AST_NODE_TYPES.JSXIdentifier) {\n return openingElement.name.name;\n }\n return null;\n}\n\n/**\n * Check if a JSX child is a JSXText node that only contains whitespace.\n */\nfunction isWhitespaceText(child: TSESTree.JSXChild): boolean {\n return child.type === AST_NODE_TYPES.JSXText && child.value.trim() === \"\";\n}\n\n/**\n * Check if a JSX child is a label-like element (Label or Text).\n */\nfunction isLabelLikeElement(child: TSESTree.JSXChild): child is TSESTree.JSXElement {\n if (child.type !== AST_NODE_TYPES.JSXElement) {\n return false;\n }\n const name = getJSXElementName(child.openingElement);\n return name !== null && LABEL_COMPONENT_NAMES.has(name);\n}\n\n/**\n * Find the previous non-whitespace sibling in a list of JSX children.\n */\nfunction getPreviousNonWhitespaceSibling(\n children: Array<TSESTree.JSXChild>,\n currentIndex: number\n): TSESTree.JSXChild | null {\n for (let i = currentIndex - 1; i >= 0; i--) {\n const sibling = children[i];\n if (sibling !== undefined && !isWhitespaceText(sibling)) {\n return sibling;\n }\n }\n return null;\n}\n\n/**\n * Check if a JSXOpeningElement has a specific boolean or truthy prop.\n */\nfunction hasJSXProp(openingElement: TSESTree.JSXOpeningElement, propName: string): boolean {\n return openingElement.attributes.some(\n attr =>\n attr.type === AST_NODE_TYPES.JSXAttribute &&\n attr.name.type === AST_NODE_TYPES.JSXIdentifier &&\n attr.name.name === propName\n );\n}\n\ntype LabelContent = { type: \"string\"; value: string } | { type: \"expression\"; text: string };\n\n/**\n * Extract the meaningful label content from a Label/Text element's children.\n *\n * Returns null if the content is too complex to autofix (e.g. multiple meaningful\n * children, or JSX element children).\n */\nfunction extractLabelContent(\n labelElement: TSESTree.JSXElement,\n sourceCode: Readonly<{ getText(node: TSESTree.Node): string }>\n): LabelContent | null {\n // Filter out whitespace-only text children\n const meaningfulChildren = labelElement.children.filter(node => {\n if (node.type === AST_NODE_TYPES.JSXText) {\n return node.value.trim() !== \"\";\n }\n return true;\n });\n\n if (meaningfulChildren.length !== 1) {\n return null; // Complex content — skip autofix\n }\n\n const child = meaningfulChildren[0];\n if (child === undefined) {\n return null;\n }\n\n if (child.type === AST_NODE_TYPES.JSXText) {\n const trimmed = child.value.trim();\n if (trimmed.length === 0) {\n return null;\n }\n return { type: \"string\", value: trimmed };\n }\n\n if (child.type === AST_NODE_TYPES.JSXExpressionContainer) {\n if (child.expression.type === AST_NODE_TYPES.JSXEmptyExpression) {\n return null;\n }\n return { type: \"expression\", text: sourceCode.getText(child.expression) };\n }\n\n return null;\n}\n\n/**\n * Determine the field component name for autofix.\n *\n * Returns null (no autofix) for BaseSelect without isMulti and CreatableSelect,\n * because SelectField/CreatableSelectField have incompatible value/onChange APIs.\n *\n * BaseSelect with isMulti → MultiSelectField is safe because MultiSelectField\n * preserves the same option-object-based value/onChange API as BaseSelect.\n */\nfunction getAutofixFieldName(baseComponentName: string, openingElement: TSESTree.JSXOpeningElement): string | null {\n if (baseComponentName === \"BaseSelect\" && hasJSXProp(openingElement, \"isMulti\")) {\n return \"MultiSelectField\";\n }\n return BASE_TO_FIELD_AUTOFIX_MAP[baseComponentName] ?? null;\n}\n\n/**\n * Build the label prop string for the autofix.\n */\nfunction buildLabelPropValue(content: LabelContent): string {\n switch (content.type) {\n case \"string\":\n return `\"${content.value}\"`;\n case \"expression\":\n return `{${content.text}}`;\n default: {\n const _exhaustiveCheck: never = content;\n return _exhaustiveCheck;\n }\n }\n}\n\n/**\n * Count how many times a component is used as a JSX opening tag in the source text.\n * Uses a regex that matches `<ComponentName` followed by whitespace, `>`, or `/>`.\n */\nfunction countJSXElementUsages(sourceText: string, componentName: string): number {\n const pattern = new RegExp(`<${componentName}(?=[\\\\s/>])`, \"g\");\n const matches = sourceText.match(pattern);\n return matches ? matches.length : 0;\n}\n\n/**\n * Build a new import statement string with the given specifiers, preserving semicolons.\n */\nfunction buildImportStatement(\n sourceCode: Readonly<{ getText(node: TSESTree.Node): string }>,\n importNode: TSESTree.ImportDeclaration,\n specifiers: Iterable<string>\n): string {\n const sortedSpecifiers = Array.from(specifiers).sort().join(\", \");\n const originalText = sourceCode.getText(importNode);\n const semicolon = originalText.trimEnd().endsWith(\";\") ? \";\" : \"\";\n return `import { ${sortedSpecifiers} } from \"${importNode.source.value}\"${semicolon}`;\n}\n\nexport const preferFieldComponents = createRule<[], MessageIds>({\n name: \"prefer-field-components\",\n meta: {\n type: \"suggestion\",\n fixable: \"code\",\n docs: {\n description:\n \"Prefer using Field components (e.g. SelectField, TextField) instead of manually combining Label/Text with base input components (e.g. BaseSelect, BaseInput).\",\n },\n schema: [],\n messages: {\n preferFieldComponent:\n \"Avoid using {{labelComponent}} with {{baseComponent}}. Use {{fieldComponent}} instead, which includes a built-in label via the `label` prop.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const sourceCode = context.sourceCode;\n\n return {\n JSXElement(node: TSESTree.JSXElement) {\n const children = node.children;\n\n for (let i = 0; i < children.length; i++) {\n const child = children[i];\n\n // Only look at JSXElement children\n if (child === undefined || child.type !== AST_NODE_TYPES.JSXElement) {\n continue;\n }\n\n const childName = getJSXElementName(child.openingElement);\n\n // Check if this child is a base component\n if (childName === null || !BASE_COMPONENT_NAMES.has(childName)) {\n continue;\n }\n\n // Found a base component — check if the previous non-whitespace sibling is a Label/Text\n const previousSibling = getPreviousNonWhitespaceSibling(children, i);\n\n if (previousSibling === null || !isLabelLikeElement(previousSibling)) {\n continue;\n }\n\n const labelName = getJSXElementName(previousSibling.openingElement);\n const fieldComponentMessage = BASE_TO_FIELD_MESSAGE_MAP[childName] ?? \"the appropriate *Field component\";\n const fieldComponentAutofix = getAutofixFieldName(childName, child.openingElement);\n const labelContent = extractLabelContent(previousSibling, sourceCode);\n\n context.report({\n node: child,\n messageId: \"preferFieldComponent\",\n data: {\n labelComponent: labelName ?? \"Label/Text\",\n baseComponent: childName,\n fieldComponent: fieldComponentMessage,\n },\n fix:\n fieldComponentAutofix !== null && labelContent !== null\n ? fixer => {\n const fixes: Array<ReturnType<typeof fixer.replaceText>> = [];\n const labelPropValue = buildLabelPropValue(labelContent);\n const fullSourceText = sourceCode.getText();\n\n // 1. Replace everything from Label start through the base component's tag name\n // in a single operation: <Label>...</Label>\\n...<BaseSelect → <SelectField label={...}\n fixes.push(\n fixer.replaceTextRange(\n [previousSibling.range[0], child.openingElement.name.range[1]],\n `<${fieldComponentAutofix} label=${labelPropValue}`\n )\n );\n\n // 2. Rename the closing tag if present (for non-self-closing elements)\n if (child.closingElement !== null) {\n fixes.push(fixer.replaceText(child.closingElement.name, fieldComponentAutofix));\n }\n\n // 3. Update imports: add the field component, remove base/label if no longer used.\n // All modifications to the same import declaration are combined into a single fix.\n const baseUsageCount = countJSXElementUsages(fullSourceText, childName);\n const labelUsageCount = labelName !== null ? countJSXElementUsages(fullSourceText, labelName) : 0;\n\n // Handle @trackunit/react-form-components import\n const formImport = findImportDeclaration(sourceCode, FORM_COMPONENTS_PACKAGE);\n if (formImport) {\n const specifiers = getImportSpecifiers(formImport);\n\n // Add field component\n specifiers.add(fieldComponentAutofix);\n\n // Remove base component if this is the only JSX usage\n if (baseUsageCount <= 1) {\n specifiers.delete(childName);\n }\n\n // Remove Label if it's from this package and this is the only JSX usage\n if (labelName === \"Label\" && labelUsageCount <= 1) {\n specifiers.delete(\"Label\");\n }\n\n fixes.push(\n fixer.replaceText(formImport, buildImportStatement(sourceCode, formImport, specifiers))\n );\n }\n\n // Handle @trackunit/react-components import (for Text removal)\n if (labelName === \"Text\" && labelUsageCount <= 1) {\n const reactImport = findImportDeclaration(sourceCode, REACT_COMPONENTS_PACKAGE);\n if (reactImport) {\n const specifiers = getImportSpecifiers(reactImport);\n if (specifiers.has(\"Text\")) {\n specifiers.delete(\"Text\");\n if (specifiers.size === 0) {\n fixes.push(fixer.remove(reactImport));\n } else {\n fixes.push(\n fixer.replaceText(reactImport, buildImportStatement(sourceCode, reactImport, specifiers))\n );\n }\n }\n }\n }\n\n return fixes;\n }\n : null,\n });\n }\n },\n };\n },\n});\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"prefer-mouse-event-handler-in-react-props.js","sourceRoot":"","sources":["../../../../../../../../libs/eslint/plugin-trackunit/src/lib/rules/prefer-mouse-event-handler-in-react-props/prefer-mouse-event-handler-in-react-props.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ESLint rule: prefer-mouse-event-handler-in-react-props\n *\n * Enforces using MouseEventHandler<T> instead of () => void or (e: MouseEvent) => void\n * for onClick* props in React components. This ensures proper React typing and consistency.\n *\n * Only flags properties in actual React component props, not in hook options or other interfaces.\n *\n * Uses ESLint suggestions (not autofix) to let users choose the appropriate element type.\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, TSESLint, TSESTree } from \"@typescript-eslint/utils\";\nimport * as ts from \"typescript\";\nimport { isReactComponent } from \"../../utils/ast-utils\";\n\ntype SuggestionResult = {\n messageId: \"suggestMouseEventHandler\";\n data: { elementType: string };\n fix: (fixer: TSESLint.RuleFixer) => TSESLint.RuleFix;\n};\n\nconst createRule = ESLintUtils.RuleCreator(\n name => `https://github.com/trackunit/manager/blob/main/libs/eslint/plugin-trackunit/src/lib/rules/${name}/${name}.ts`\n);\n\ntype Options = [\n {\n /**\n * Prop names to exempt from this rule.\n *\n * @example [\"onClick\"]\n */\n allowedNames?: ReadonlyArray<string>;\n },\n];\n\ntype MessageIds = \"preferMouseEventHandler\" | \"suggestMouseEventHandler\";\n\n/**\n * Checks if a property name starts with \"onClick\" (case-sensitive).\n */\nconst isOnClickProp = (name: string): boolean => {\n return name.startsWith(\"onClick\");\n};\n\n/**\n * Extracts the element type from a MouseEvent generic type annotation.\n * Returns null if not a MouseEvent type or no generic provided.\n *\n * Examples:\n * - MouseEvent<HTMLButtonElement> -> \"HTMLButtonElement\"\n * - React.MouseEvent<HTMLDivElement> -> \"HTMLDivElement\"\n * - MouseEvent -> null (no generic)\n */\nconst extractElementTypeFromMouseEvent = (typeAnnotation: TSESTree.TypeNode): string | null => {\n if (typeAnnotation.type !== AST_NODE_TYPES.TSTypeReference) {\n return null;\n }\n\n const { typeName, typeArguments } = typeAnnotation;\n\n let isMouseEvent = false;\n\n if (typeName.type === AST_NODE_TYPES.Identifier) {\n isMouseEvent = typeName.name === \"MouseEvent\";\n } else if (typeName.type === AST_NODE_TYPES.TSQualifiedName) {\n isMouseEvent =\n typeName.left.type === AST_NODE_TYPES.Identifier &&\n typeName.left.name === \"React\" &&\n typeName.right.name === \"MouseEvent\";\n }\n\n if (!isMouseEvent) {\n return null;\n }\n\n if (!typeArguments || typeArguments.params.length === 0) {\n return null;\n }\n\n const firstTypeArg = typeArguments.params[0];\n if (firstTypeArg?.type === AST_NODE_TYPES.TSTypeReference) {\n if (firstTypeArg.typeName.type === AST_NODE_TYPES.Identifier) {\n return firstTypeArg.typeName.name;\n }\n }\n\n return null;\n};\n\n/**\n * Checks if a type annotation is a MouseEvent type (with or without generics).\n */\nconst isMouseEventType = (typeAnnotation: TSESTree.TypeNode): boolean => {\n if (typeAnnotation.type !== AST_NODE_TYPES.TSTypeReference) {\n return false;\n }\n\n const { typeName } = typeAnnotation;\n\n if (typeName.type === AST_NODE_TYPES.Identifier) {\n return typeName.name === \"MouseEvent\";\n }\n\n if (typeName.type === AST_NODE_TYPES.TSQualifiedName) {\n return (\n typeName.left.type === AST_NODE_TYPES.Identifier &&\n typeName.left.name === \"React\" &&\n typeName.right.name === \"MouseEvent\"\n );\n }\n\n return false;\n};\n\n/**\n * Checks if a function type returns void.\n */\nconst hasVoidReturn = (funcType: TSESTree.TSFunctionType): boolean => {\n const { returnType } = funcType;\n\n if (!returnType) {\n return true;\n }\n\n const typeAnnotation = returnType.typeAnnotation;\n\n if (typeAnnotation.type === AST_NODE_TYPES.TSVoidKeyword) {\n return true;\n }\n\n return false;\n};\n\ntype AnalysisResult =\n | { shouldFlag: false }\n | {\n shouldFlag: true;\n inferredElementType: string | null;\n typeAnnotationNode: TSESTree.TSFunctionType;\n };\n\n/**\n * Analyzes a TSFunctionType to determine if it should be flagged.\n */\nconst analyzeFunctionType = (funcType: TSESTree.TSFunctionType): AnalysisResult => {\n if (!hasVoidReturn(funcType)) {\n return { shouldFlag: false };\n }\n\n const { params } = funcType;\n\n // Case A: No parameters - () => void\n if (params.length === 0) {\n return {\n shouldFlag: true,\n inferredElementType: null,\n typeAnnotationNode: funcType,\n };\n }\n\n // Case B: Exactly one parameter that is MouseEvent\n if (params.length === 1) {\n const param = params[0];\n\n let paramTypeAnnotation: TSESTree.TypeNode | undefined;\n\n if (param?.type === AST_NODE_TYPES.Identifier && param.typeAnnotation) {\n paramTypeAnnotation = param.typeAnnotation.typeAnnotation;\n }\n\n if (paramTypeAnnotation && isMouseEventType(paramTypeAnnotation)) {\n const elementType = extractElementTypeFromMouseEvent(paramTypeAnnotation);\n return {\n shouldFlag: true,\n inferredElementType: elementType,\n typeAnnotationNode: funcType,\n };\n }\n }\n\n return { shouldFlag: false };\n};\n\n/**\n * Creates suggestion fixes for a given element type.\n */\nconst createSuggestion = (elementType: string, typeAnnotationNode: TSESTree.TSFunctionType): SuggestionResult => {\n return {\n messageId: \"suggestMouseEventHandler\",\n data: { elementType },\n fix: fixer => {\n return fixer.replaceText(typeAnnotationNode, `MouseEventHandler<${elementType}>`);\n },\n };\n};\n\n/**\n * Known valid HTML element types for MouseEventHandler.\n */\nconst VALID_HTML_ELEMENT_TYPES = new Set([\n \"Element\",\n \"HTMLElement\",\n \"HTMLAnchorElement\",\n \"HTMLButtonElement\",\n \"HTMLDivElement\",\n \"HTMLFormElement\",\n \"HTMLImageElement\",\n \"HTMLInputElement\",\n \"HTMLLabelElement\",\n \"HTMLLIElement\",\n \"HTMLParagraphElement\",\n \"HTMLSelectElement\",\n \"HTMLSpanElement\",\n \"HTMLTableElement\",\n \"HTMLTableRowElement\",\n \"HTMLTableCellElement\",\n \"HTMLTextAreaElement\",\n \"HTMLUListElement\",\n \"SVGElement\",\n \"SVGSVGElement\",\n]);\n\n/**\n * Extracts element type from a MouseEventHandler type string.\n */\nconst extractElementTypeFromTypeString = (typeString: string): string | null => {\n const match = typeString.match(/MouseEventHandler<([^>]+)>/);\n if (match?.[1]) {\n const elementType = match[1].trim();\n if (VALID_HTML_ELEMENT_TYPES.has(elementType)) {\n return elementType;\n }\n }\n return null;\n};\n\n/**\n * Tries to infer the element type by finding where the prop is used in JSX.\n */\nconst inferElementTypeFromUsage = (\n propName: string,\n context: TSESLint.RuleContext<MessageIds, ReadonlyArray<unknown>>\n): string | null => {\n try {\n const services = ESLintUtils.getParserServices(context);\n const checker = services.program.getTypeChecker();\n const sourceFile = services.program.getSourceFile(context.filename);\n\n if (!sourceFile) {\n return null;\n }\n\n let inferredType: string | null = null;\n\n const visit = (node: ts.Node): void => {\n if (ts.isJsxAttribute(node)) {\n const attrName = node.name.getText(sourceFile);\n\n if (attrName.startsWith(\"on\")) {\n const initializer = node.initializer;\n\n if (initializer && ts.isJsxExpression(initializer) && initializer.expression) {\n const expr = initializer.expression;\n let referencedName: string | null = null;\n\n if (ts.isIdentifier(expr) && expr.text === propName) {\n referencedName = propName;\n }\n\n if (ts.isArrowFunction(expr) && ts.isCallExpression(expr.body)) {\n const callee = expr.body.expression;\n if (ts.isIdentifier(callee) && callee.text === propName) {\n referencedName = propName;\n }\n }\n\n if (referencedName) {\n const contextualType = checker.getContextualType(expr);\n if (contextualType) {\n const typeString = checker.typeToString(contextualType);\n const elementType = extractElementTypeFromTypeString(typeString);\n if (elementType) {\n inferredType = elementType;\n }\n }\n }\n }\n }\n }\n\n ts.forEachChild(node, visit);\n };\n\n visit(sourceFile);\n return inferredType;\n } catch {\n return null;\n }\n};\n\n/**\n * Gets onClick* properties from a component's props type annotation.\n */\nconst getOnClickPropsFromType = (\n context: TSESLint.RuleContext<MessageIds, ReadonlyArray<unknown>>,\n typeAnnotation: TSESTree.TypeNode | undefined\n): Array<{ name: string; typeNode: TSESTree.TSFunctionType; propertyNode: TSESTree.Node }> => {\n if (!typeAnnotation) {\n return [];\n }\n\n const result: Array<{ name: string; typeNode: TSESTree.TSFunctionType; propertyNode: TSESTree.Node }> = [];\n\n // Handle inline type literals: ({ onClick }: { onClick: () => void }) => ...\n if (typeAnnotation.type === AST_NODE_TYPES.TSTypeLiteral) {\n for (const member of typeAnnotation.members) {\n if (\n member.type === AST_NODE_TYPES.TSPropertySignature &&\n member.key.type === AST_NODE_TYPES.Identifier &&\n isOnClickProp(member.key.name) &&\n member.typeAnnotation?.typeAnnotation.type === AST_NODE_TYPES.TSFunctionType\n ) {\n result.push({\n name: member.key.name,\n typeNode: member.typeAnnotation.typeAnnotation,\n propertyNode: member,\n });\n }\n }\n return result;\n }\n\n // Handle type references: ({ onClick }: ButtonProps) => ...\n if (typeAnnotation.type === AST_NODE_TYPES.TSTypeReference) {\n try {\n const services = ESLintUtils.getParserServices(context);\n const checker = services.program.getTypeChecker();\n const tsNode = services.esTreeNodeToTSNodeMap.get(typeAnnotation);\n const type = checker.getTypeAtLocation(tsNode);\n\n for (const prop of type.getProperties()) {\n if (isOnClickProp(prop.name)) {\n const propType = checker.getTypeOfSymbolAtLocation(prop, tsNode);\n const callSignatures = propType.getCallSignatures();\n\n if (callSignatures.length > 0) {\n // Find the corresponding ESTree node for this property\n const declarations = prop.getDeclarations();\n const decl = declarations?.[0];\n if (decl) {\n const esTreeNode = services.tsNodeToESTreeNodeMap.get(decl);\n if (\n esTreeNode.type === AST_NODE_TYPES.TSPropertySignature &&\n esTreeNode.typeAnnotation?.typeAnnotation.type === AST_NODE_TYPES.TSFunctionType\n ) {\n result.push({\n name: prop.name,\n typeNode: esTreeNode.typeAnnotation.typeAnnotation,\n propertyNode: esTreeNode,\n });\n }\n }\n }\n }\n }\n } catch {\n // Type checking failed - silently skip\n }\n }\n\n return result;\n};\n\nexport const preferMouseEventHandlerInReactProps = createRule<Options, MessageIds>({\n name: \"prefer-mouse-event-handler-in-react-props\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Enforce using MouseEventHandler<T> instead of () => void or (e: MouseEvent) => void for onClick* props in React components. \" +\n \"This ensures proper React typing and consistency across the codebase.\",\n },\n hasSuggestions: true,\n schema: [\n {\n type: \"object\",\n properties: {\n allowedNames: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Prop names to exempt from this rule\",\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n preferMouseEventHandler: \"Prop '{{propName}}' should use MouseEventHandler<T> instead of {{currentType}}.\",\n suggestMouseEventHandler: \"Use MouseEventHandler<{{elementType}}>\",\n },\n },\n defaultOptions: [\n {\n allowedNames: [],\n },\n ],\n create(context, [options]) {\n const allowedNames = options.allowedNames ?? [];\n\n // Only run on .tsx files\n const filename = context.filename;\n if (!filename.endsWith(\".tsx\")) {\n return {};\n }\n\n // Track reported property nodes to avoid duplicate reports when multiple components use the same interface\n const reportedNodes = new Set<TSESTree.Node>();\n\n const checkComponentProps = (\n _node: TSESTree.FunctionDeclaration | TSESTree.ArrowFunctionExpression,\n propsParam: TSESTree.Parameter | undefined\n ) => {\n if (!propsParam) {\n return;\n }\n\n let typeAnnotation: TSESTree.TypeNode | undefined;\n\n // Handle both regular parameters (props: Props) and destructured parameters ({prop1, prop2}: Props)\n if (propsParam.type === AST_NODE_TYPES.Identifier || propsParam.type === AST_NODE_TYPES.ObjectPattern) {\n typeAnnotation = propsParam.typeAnnotation?.typeAnnotation;\n } else {\n return;\n }\n\n const onClickProps = getOnClickPropsFromType(context, typeAnnotation);\n\n for (const { name: propName, typeNode, propertyNode } of onClickProps) {\n // Skip if already reported (prevents duplicates when multiple components use the same interface)\n if (reportedNodes.has(propertyNode)) {\n continue;\n }\n\n // Check if it's in the allowed list\n if (allowedNames.includes(propName)) {\n continue;\n }\n\n // Analyze the function type\n const result = analyzeFunctionType(typeNode);\n\n if (!result.shouldFlag) {\n continue;\n }\n\n // Get the current type as a string for the error message\n const sourceCode = context.sourceCode;\n const currentType = sourceCode.getText(typeNode);\n\n // Build suggestions\n const suggestions: Array<SuggestionResult> = [];\n\n let inferredElementType = result.inferredElementType;\n\n if (!inferredElementType) {\n inferredElementType = inferElementTypeFromUsage(propName, context);\n }\n\n if (inferredElementType) {\n suggestions.push(createSuggestion(inferredElementType, result.typeAnnotationNode));\n } else {\n suggestions.push(createSuggestion(\"HTMLButtonElement\", result.typeAnnotationNode));\n suggestions.push(createSuggestion(\"HTMLElement\", result.typeAnnotationNode));\n suggestions.push(createSuggestion(\"HTMLDivElement\", result.typeAnnotationNode));\n }\n\n // Mark as reported to prevent duplicates\n reportedNodes.add(propertyNode);\n\n context.report({\n node: propertyNode,\n messageId: \"preferMouseEventHandler\",\n data: {\n propName,\n currentType,\n },\n suggest: suggestions,\n });\n }\n };\n\n return {\n FunctionDeclaration(node: TSESTree.FunctionDeclaration) {\n if (isReactComponent(node)) {\n checkComponentProps(node, node.params[0]);\n }\n },\n\n ArrowFunctionExpression(node: TSESTree.ArrowFunctionExpression) {\n if (isReactComponent(node)) {\n checkComponentProps(node, node.params[0]);\n }\n },\n };\n },\n});\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"require-classname-alternatives.js","sourceRoot":"","sources":["../../../../../../../../libs/eslint/plugin-trackunit/src/lib/rules/require-classname-alternatives/require-classname-alternatives.ts"],"names":[],"mappings":"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","sourcesContent":["import { AST_NODE_TYPES, ESLintUtils, TSESTree } from \"@typescript-eslint/utils\";\nimport { findClassnameStringsInAttribute, findClassnameStringsInCall } from \"../../utils/classname-utils\";\n\nconst createRule = ESLintUtils.RuleCreator(\n name => `https://github.com/trackunit/manager/blob/main/libs/eslint/plugin-trackunit/src/lib/rules/${name}.ts`\n);\n\ntype MessageIds = \"bannedClassAutoFix\" | \"bannedClassSuggest\" | \"suggestReplacement\";\n\ntype RuleConfig = {\n /** Map of banned class name patterns to their allowed alternatives */\n alternatives: Record<string, Array<string>>;\n /** Optional prefixes to match (e.g., \"bg\", \"text\", \"border\"). If not provided, matches exact class names. */\n prefixes?: Array<string>;\n};\n\ntype Options = [RuleConfig?];\n\n/**\n * Replace a specific pattern in a string with its replacement.\n * Handles both prefix-based patterns (e.g., bg-red-500 -> bg-danger-500)\n * and exact matches.\n */\nfunction replacePattern(value: string, oldPattern: string, newPattern: string, hasPrefixes: boolean): string {\n if (hasPrefixes) {\n // With prefixes, replace -oldPattern- with -newPattern-\n const pattern = new RegExp(`-${oldPattern}(-|$|/)`, \"g\");\n return value.replace(pattern, `-${newPattern}$1`);\n } else {\n // Without prefixes, replace exact word boundaries\n const pattern = new RegExp(`(^|\\\\s)${oldPattern}($|\\\\s)`, \"g\");\n return value.replace(pattern, `$1${newPattern}$2`);\n }\n}\n\n/**\n * Build a regex pattern that matches class names based on configuration.\n *\n * With prefixes: Matches patterns like bg-red-500, text-blue-200, hover:border-green-400\n * Without prefixes: Matches exact class names as standalone words\n */\nfunction buildBannedPattern(bannedPatterns: Array<string>, prefixes?: Array<string>): RegExp {\n const patternList = bannedPatterns.join(\"|\");\n const variantPrefixes =\n \"(?:hover:|focus:|focus-visible:|focus-within:|active:|disabled:|group-hover:|peer-hover:|dark:|light:|sm:|md:|lg:|xl:|2xl:)*\";\n\n if (prefixes && prefixes.length > 0) {\n const prefixPattern = prefixes.join(\"|\");\n // Match: [variant:]prefix-banned[-suffix][/opacity]\n return new RegExp(\n `(?:^|\\\\s)(${variantPrefixes}(?:${prefixPattern})-(${patternList})(?:-[a-zA-Z0-9]+)?(?:\\\\/\\\\d+)?)(?=\\\\s|$|\")`,\n \"g\"\n );\n } else {\n // Exact match: standalone class name\n return new RegExp(`(?:^|\\\\s)(${variantPrefixes}(${patternList}))(?=\\\\s|$|\")`, \"g\");\n }\n}\n\nexport const requireClassnameAlternatives = createRule<Options, MessageIds>({\n name: \"require-classname-alternatives\",\n meta: {\n type: \"problem\",\n fixable: \"code\",\n hasSuggestions: true,\n docs: {\n description: \"Require specific class names to be replaced with alternatives. Configurable via rule options.\",\n },\n messages: {\n bannedClassAutoFix: '\"{{match}}\" is not allowed. Use \"{{alternative}}\" instead.',\n bannedClassSuggest: '\"{{match}}\" is not allowed. Consider using: {{alternatives}}.',\n suggestReplacement: 'Replace with \"{{replacement}}\"',\n },\n schema: [\n {\n type: \"object\",\n properties: {\n alternatives: {\n type: \"object\",\n description: \"Map of banned patterns to their allowed alternatives (array of strings)\",\n additionalProperties: {\n type: \"array\",\n items: { type: \"string\" },\n },\n },\n prefixes: {\n type: \"array\",\n description: \"Prefixes to match (e.g., 'bg', 'text'). If not provided, matches exact class names.\",\n items: { type: \"string\" },\n },\n },\n additionalProperties: false,\n },\n ],\n },\n defaultOptions: [],\n create(context) {\n const options = context.options[0];\n\n // Return early if no options or empty alternatives to save compute\n if (!options || Object.keys(options.alternatives).length === 0) {\n return {};\n }\n\n const { alternatives, prefixes } = options;\n const bannedPatterns = Object.keys(alternatives);\n const hasPrefixes = prefixes !== undefined && prefixes.length > 0;\n\n const isBannedPattern = (pattern: string): boolean => pattern in alternatives;\n\n const getAlternatives = (pattern: string): Array<string> => {\n return alternatives[pattern] ?? [];\n };\n\n const isAutoFixable = (pattern: string): boolean => getAlternatives(pattern).length === 1;\n\n const BANNED_PATTERN = buildBannedPattern(bannedPatterns, prefixes);\n\n /**\n * Find all banned pattern usages in a string value\n */\n function findBannedUsages(value: string): Array<{ match: string; pattern: string }> {\n const results: Array<{ match: string; pattern: string }> = [];\n BANNED_PATTERN.lastIndex = 0;\n\n let regexMatch;\n while ((regexMatch = BANNED_PATTERN.exec(value)) !== null) {\n const matchedClass = regexMatch[1];\n const matchedPattern = regexMatch[2];\n if (matchedClass !== undefined && matchedPattern !== undefined && isBannedPattern(matchedPattern)) {\n results.push({\n match: matchedClass.trim(),\n pattern: matchedPattern,\n });\n }\n }\n\n return results;\n }\n\n /**\n * Replace ALL auto-fixable patterns in a string at once\n */\n function replaceAllAutoFixablePatterns(value: string): string {\n let result = value;\n for (const [pattern, alts] of Object.entries(alternatives)) {\n const singleAlternative = alts.length === 1 ? alts[0] : undefined;\n if (singleAlternative) {\n result = replacePattern(result, pattern, singleAlternative, hasPrefixes);\n }\n }\n return result;\n }\n\n // Track which string literals have already had a fix generated\n // to avoid duplicate/conflicting fixes for the same literal\n const fixedLiterals = new WeakSet<TSESTree.Literal | TSESTree.TemplateLiteral>();\n\n /**\n * Create a fixer function that replaces all auto-fixable patterns in a string literal\n */\n function createFixer(stringLiteralNode: TSESTree.Literal | TSESTree.TemplateLiteral) {\n return (fixer: Parameters<NonNullable<Parameters<typeof context.report>[0][\"fix\"]>>[0]) => {\n switch (stringLiteralNode.type) {\n case AST_NODE_TYPES.Literal: {\n const originalValue = stringLiteralNode.value;\n if (typeof originalValue !== \"string\") {\n return null;\n }\n const fixedValue = replaceAllAutoFixablePatterns(originalValue);\n const raw = stringLiteralNode.raw;\n const quote = raw.charAt(0);\n return fixer.replaceText(stringLiteralNode, `${quote}${fixedValue}${quote}`);\n }\n case AST_NODE_TYPES.TemplateLiteral: {\n const fixes: Array<ReturnType<typeof fixer.replaceTextRange>> = [];\n const autoFixablePatterns = Object.entries(alternatives)\n .filter(([, alts]) => alts.length === 1)\n .map(([pattern]) => pattern);\n\n for (const quasi of stringLiteralNode.quasis) {\n const hasFixablePattern = autoFixablePatterns.some(pattern =>\n hasPrefixes ? quasi.value.raw.includes(`-${pattern}`) : quasi.value.raw.includes(pattern)\n );\n if (hasFixablePattern) {\n const fixedRaw = replaceAllAutoFixablePatterns(quasi.value.raw);\n const rangeStart = quasi.range[0] + (quasi.tail ? 1 : 0);\n const rangeEnd = quasi.range[1] - (quasi.tail ? 1 : 0);\n fixes.push(\n fixer.replaceTextRange(\n [rangeStart, rangeEnd],\n quasi.tail ? fixedRaw : fixedRaw + (quasi.value.raw.endsWith(\" \") ? \"\" : \"\")\n )\n );\n }\n }\n return fixes.length > 0 ? fixes : null;\n }\n default:\n return null;\n }\n };\n }\n\n /**\n * Create a suggestion fixer for a specific pattern replacement\n */\n function createSuggestionFixer(\n stringLiteralNode: TSESTree.Literal | TSESTree.TemplateLiteral,\n oldPattern: string,\n newPattern: string\n ) {\n return (fixer: Parameters<NonNullable<Parameters<typeof context.report>[0][\"fix\"]>>[0]) => {\n switch (stringLiteralNode.type) {\n case AST_NODE_TYPES.Literal: {\n const originalValue = stringLiteralNode.value;\n if (typeof originalValue !== \"string\") {\n return null;\n }\n const fixedValue = replacePattern(originalValue, oldPattern, newPattern, hasPrefixes);\n const raw = stringLiteralNode.raw;\n const quote = raw.charAt(0);\n return fixer.replaceText(stringLiteralNode, `${quote}${fixedValue}${quote}`);\n }\n case AST_NODE_TYPES.TemplateLiteral: {\n const fixes: Array<ReturnType<typeof fixer.replaceTextRange>> = [];\n for (const quasi of stringLiteralNode.quasis) {\n const containsPattern = hasPrefixes\n ? quasi.value.raw.includes(`-${oldPattern}`)\n : quasi.value.raw.includes(oldPattern);\n if (containsPattern) {\n const fixedRaw = replacePattern(quasi.value.raw, oldPattern, newPattern, hasPrefixes);\n const rangeStart = quasi.range[0] + (quasi.tail ? 1 : 0);\n const rangeEnd = quasi.range[1] - (quasi.tail ? 1 : 0);\n fixes.push(\n fixer.replaceTextRange(\n [rangeStart, rangeEnd],\n quasi.tail ? fixedRaw : fixedRaw + (quasi.value.raw.endsWith(\" \") ? \"\" : \"\")\n )\n );\n }\n }\n return fixes.length > 0 ? fixes : null;\n }\n default:\n return null;\n }\n };\n }\n\n /**\n * Report banned pattern usages for a given node and value.\n * - Auto-fixable patterns (1 alternative): applies fix automatically\n * - Multi-alternative patterns: shows suggestions in editor quick-fix menu\n */\n function checkAndReport(\n node: TSESTree.Node,\n value: string,\n stringLiteralNode?: TSESTree.Literal | TSESTree.TemplateLiteral\n ): void {\n const usages = findBannedUsages(value);\n let fixProvided = false;\n\n for (const usage of usages) {\n const patternIsAutoFixable = isAutoFixable(usage.pattern);\n const patternAlternatives = getAlternatives(usage.pattern);\n\n let fix: Parameters<typeof context.report>[0][\"fix\"];\n let suggest: Parameters<typeof context.report>[0][\"suggest\"];\n\n if (stringLiteralNode) {\n if (patternIsAutoFixable && !fixProvided && !fixedLiterals.has(stringLiteralNode)) {\n fixedLiterals.add(stringLiteralNode);\n fixProvided = true;\n fix = createFixer(stringLiteralNode);\n }\n\n if (!patternIsAutoFixable) {\n suggest = patternAlternatives.map(alternative => ({\n messageId: \"suggestReplacement\" as const,\n data: { replacement: replacePattern(usage.match, usage.pattern, alternative, hasPrefixes) },\n fix: createSuggestionFixer(stringLiteralNode, usage.pattern, alternative),\n }));\n }\n }\n\n if (patternIsAutoFixable) {\n const singleAlternative = patternAlternatives[0];\n context.report({\n node,\n messageId: \"bannedClassAutoFix\",\n data: {\n match: usage.match,\n alternative: singleAlternative ?? \"\",\n },\n fix,\n });\n } else {\n context.report({\n node,\n messageId: \"bannedClassSuggest\",\n data: {\n match: usage.match,\n alternatives: patternAlternatives.join(\", \"),\n },\n suggest,\n });\n }\n }\n }\n\n return {\n JSXAttribute(node: TSESTree.JSXAttribute) {\n const locations = findClassnameStringsInAttribute(node);\n for (const location of locations) {\n checkAndReport(location.reportNode, location.value, location.fixNode);\n }\n },\n\n CallExpression(node: TSESTree.CallExpression) {\n const locations = findClassnameStringsInCall(node);\n for (const location of locations) {\n checkAndReport(location.reportNode, location.value, location.fixNode);\n }\n },\n };\n },\n});\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"require-list-item-virtualization-props.js","sourceRoot":"","sources":["../../../../../../../../libs/eslint/plugin-trackunit/src/lib/rules/require-list-item-virtualization-props/require-list-item-virtualization-props.ts"],"names":[],"mappings":"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","sourcesContent":["import { AST_NODE_TYPES, ESLintUtils, TSESLint, TSESTree } from \"@typescript-eslint/utils\";\n\nconst createRule = ESLintUtils.RuleCreator(\n name => `https://github.com/trackunit/manager/blob/main/libs/eslint/plugin-trackunit/src/lib/rules/${name}.ts`\n);\n\ntype Options = [\n {\n /**\n * Whether to allow custom components that extend VirtualizationListItemProps\n * instead of requiring explicit spreading of listItemProps\n */\n allowCustomComponents?: boolean;\n },\n];\n\ntype MessageIds = \"requireVirtualizationProps\" | \"spreadListItemProps\" | \"useSemanticListItem\";\n\n/**\n * Check if a spread attribute argument contains listItemProps\n */\nconst isListItemPropsSpread = (argument: TSESTree.Expression): boolean => {\n // Direct identifier: {...listItemProps}\n if (argument.type === AST_NODE_TYPES.Identifier) {\n return argument.name === \"listItemProps\" || argument.name.includes(\"ItemProps\");\n }\n\n // Member expression: {...params.listItemProps}\n if (argument.type === AST_NODE_TYPES.MemberExpression) {\n // Check if property is an identifier (not computed like params[\"listItemProps\"])\n if (!argument.computed && argument.property.type === AST_NODE_TYPES.Identifier) {\n const propertyName = argument.property.name;\n if (propertyName === \"listItemProps\" || propertyName.includes(\"ItemProps\")) {\n return true;\n }\n }\n }\n\n return false;\n};\n\n/**\n * Check if a JSX element has the required virtualization props\n */\nconst hasRequiredVirtualizationProps = (\n element: TSESTree.JSXElement,\n context: TSESLint.RuleContext<string, ReadonlyArray<unknown>>\n): boolean => {\n // ref is functionally required for virtualization even though TypeScript marks it optional\n const requiredProps = [\"className\", \"data-index\", \"tabIndex\", \"ref\"];\n\n const attributes = element.openingElement.attributes;\n const propNames = new Set<string>();\n let hasSpreadListItemProps = false;\n\n for (const attr of attributes) {\n if (attr.type === AST_NODE_TYPES.JSXAttribute && attr.name.type === AST_NODE_TYPES.JSXIdentifier) {\n propNames.add(attr.name.name);\n } else if (attr.type === AST_NODE_TYPES.JSXSpreadAttribute) {\n // Check if spreading listItemProps or similar\n if (isListItemPropsSpread(attr.argument)) {\n hasSpreadListItemProps = true;\n } else {\n // Fallback: check source code text for listItemProps\n // This handles edge cases where AST structure might be different\n const sourceCode = context.sourceCode.getText(attr.argument);\n const hasListItemProps = sourceCode.includes(\"listItemProps\");\n const hasItemProps = sourceCode.includes(\"ItemProps\");\n if (hasListItemProps || hasItemProps) {\n hasSpreadListItemProps = true;\n }\n }\n }\n }\n\n // If spreading listItemProps, assume it has all required props\n if (hasSpreadListItemProps) {\n return true;\n }\n\n // Check if all required props are present\n return requiredProps.every(prop => propNames.has(prop));\n};\n\n/**\n * Check if we're inside a List component's render function\n */\nconst isInsideListRenderFunction = (node: TSESTree.Node): boolean => {\n let current: TSESTree.Node | undefined = node.parent;\n\n while (current) {\n // Look for JSX element named \"List\"\n if (\n current.type === AST_NODE_TYPES.JSXElement &&\n current.openingElement.name.type === AST_NODE_TYPES.JSXIdentifier &&\n current.openingElement.name.name === \"List\"\n ) {\n return true;\n }\n current = current.parent;\n }\n\n return false;\n};\n\n/**\n * Check if a JSX element is a direct list item (directly returned from List render function)\n */\nconst isListItemElement = (element: TSESTree.JSXElement): boolean => {\n // Check if this element is the direct return value from a List render function\n const current: TSESTree.Node | undefined = element.parent;\n\n // We need to find if this element is DIRECTLY returned from the render function\n // This means it should be the immediate child of a return statement or arrow function body\n\n // Case 1: Direct return statement - return (<li>...</li>)\n if (current.type === AST_NODE_TYPES.ReturnStatement) {\n return isInsideListRenderFunction(current);\n }\n\n // Case 2: Arrow function body - ({ key, listItemProps, item }) => (<li>...</li>)\n if (current.type === AST_NODE_TYPES.ArrowFunctionExpression) {\n return isInsideListRenderFunction(current);\n }\n\n // Case 3: Conditional expression - item ? <li>...</li> : null\n if (current.type === AST_NODE_TYPES.ConditionalExpression) {\n // Check if the conditional is directly returned or is the arrow function body\n const conditionalParent = current.parent;\n if (\n conditionalParent.type === AST_NODE_TYPES.ReturnStatement ||\n conditionalParent.type === AST_NODE_TYPES.ArrowFunctionExpression\n ) {\n return isInsideListRenderFunction(conditionalParent);\n }\n }\n\n return false;\n};\n\n/**\n * Get the name of a JSX element\n */\nconst getJSXElementName = (element: TSESTree.JSXElement): string | null => {\n if (element.openingElement.name.type === AST_NODE_TYPES.JSXIdentifier) {\n return element.openingElement.name.name;\n }\n return null;\n};\n\n/**\n * Check if element is a semantic list item element\n */\nconst isSemanticListItem = (elementName: string): boolean => {\n return elementName.toLowerCase() === \"li\";\n};\n\n/**\n * Check if element name is a built-in HTML element (vs custom React component)\n */\nconst isHTMLElement = (elementName: string): boolean => {\n // HTML elements are lowercase, React components start with uppercase\n if (elementName.length === 0) {\n return false;\n }\n const firstChar = elementName[0];\n if (!firstChar) {\n return false;\n }\n return firstChar === firstChar.toLowerCase();\n};\n\nexport const requireListItemVirtualizationProps = createRule<Options, MessageIds>({\n name: \"require-list-item-virtualization-props\",\n meta: {\n type: \"problem\",\n docs: {\n description: \"Require list items within <List> to have VirtualizationListItemProps\",\n },\n hasSuggestions: true,\n schema: [\n {\n type: \"object\",\n properties: {\n allowCustomComponents: { type: \"boolean\" },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n requireVirtualizationProps:\n \"List item '{{elementName}}' must include VirtualizationListItemProps. \" +\n \"Spread {{...listItemProps}} or manually specify className, data-index, tabIndex, ref props.\",\n spreadListItemProps: \"Spread {...listItemProps} on this element\",\n useSemanticListItem:\n \"Use semantic <li> elements for list items instead of '{{elementName}}'. \" +\n \"List items should be <li> elements with proper VirtualizationListItemProps.\",\n },\n },\n defaultOptions: [\n {\n allowCustomComponents: true,\n },\n ],\n create(context, options) {\n const config = options[0];\n\n return {\n JSXElement(node: TSESTree.JSXElement) {\n // Skip if not inside a List render function\n if (!isListItemElement(node)) {\n return;\n }\n\n const elementName = getJSXElementName(node);\n if (!elementName) {\n return;\n }\n\n // Check element type and apply appropriate rules\n if (isHTMLElement(elementName)) {\n // For HTML elements, enforce semantic list items\n if (!isSemanticListItem(elementName)) {\n context.report({\n node: node.openingElement,\n messageId: \"useSemanticListItem\",\n data: { elementName },\n });\n return;\n }\n\n // For <li> elements, require virtualization props\n if (!hasRequiredVirtualizationProps(node, context)) {\n context.report({\n node: node.openingElement,\n messageId: \"requireVirtualizationProps\",\n data: { elementName },\n suggest: [\n {\n messageId: \"spreadListItemProps\",\n fix(fixer) {\n // Add {...listItemProps} as the first attribute\n const openingTag = node.openingElement;\n const firstAttr = openingTag.attributes[0];\n\n if (firstAttr) {\n return fixer.insertTextBefore(firstAttr, \"{...listItemProps} \");\n } else {\n // Insert after the element name\n return fixer.insertTextAfter(openingTag.name, \" {...listItemProps}\");\n }\n },\n },\n ],\n });\n }\n } else {\n // For custom components, check allowCustomComponents setting\n if (config.allowCustomComponents === false && !hasRequiredVirtualizationProps(node, context)) {\n context.report({\n node: node.openingElement,\n messageId: \"requireVirtualizationProps\",\n data: { elementName },\n suggest: [\n {\n messageId: \"spreadListItemProps\",\n fix(fixer) {\n // Add {...listItemProps} as the first attribute\n const openingTag = node.openingElement;\n const firstAttr = openingTag.attributes[0];\n\n if (firstAttr) {\n return fixer.insertTextBefore(firstAttr, \"{...listItemProps} \");\n } else {\n // Insert after the element name\n return fixer.insertTextAfter(openingTag.name, \" {...listItemProps}\");\n }\n },\n },\n ],\n });\n }\n }\n },\n };\n },\n});\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"require-optional-prop-initialization.js","sourceRoot":"","sources":["../../../../../../../../libs/eslint/plugin-trackunit/src/lib/rules/require-optional-prop-initialization/require-optional-prop-initialization.ts"],"names":[],"mappings":"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","sourcesContent":["import { AST_NODE_TYPES, ESLintUtils, TSESTree } from \"@typescript-eslint/utils\";\nimport {\n hasExistingBodyInitializations,\n isParameterExplicitlyDestructured,\n isParameterInitialized,\n isParameterOnlyUsedInJSX,\n isParameterUsedInFunction,\n isReactComponent,\n} from \"../../utils/ast-utils\";\nimport { getOptionalPropsFromType } from \"../../utils/typescript-utils\";\nimport { generateSuggestions, type MessageIds } from \"./suggestion-utils\";\n\nconst createRule = ESLintUtils.RuleCreator(\n name => `https://github.com/trackunit/manager/blob/main/libs/eslint/plugin-trackunit/src/lib/rules/${name}.ts`\n);\n\ntype Options = [\n {\n allowNullableObject?: boolean;\n allowNullableBoolean?: boolean;\n allowNullableString?: boolean;\n allowNullableNumber?: boolean;\n allowNullableEnum?: boolean;\n allowNullableStringLiteralUnion?: boolean;\n onlyRequireUsedProps?: boolean;\n },\n];\n\nexport const requireOptionalPropInitialization = createRule<Options, MessageIds>({\n name: \"require-optional-prop-initialization\",\n meta: {\n type: \"suggestion\",\n docs: {\n description: \"Require optional component props to be initialized inside the component\",\n },\n hasSuggestions: true,\n schema: [\n {\n type: \"object\",\n properties: {\n allowNullableObject: { type: \"boolean\" },\n allowNullableBoolean: { type: \"boolean\" },\n allowNullableString: { type: \"boolean\" },\n allowNullableNumber: { type: \"boolean\" },\n allowNullableEnum: { type: \"boolean\" },\n allowNullableStringLiteralUnion: { type: \"boolean\" },\n onlyRequireUsedProps: { type: \"boolean\" },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n requireInitialization: \"Optional prop '{{propName}}' should be initialized\",\n initializeProp: \"{{description}}\",\n },\n },\n defaultOptions: [\n {\n allowNullableObject: true,\n allowNullableBoolean: false,\n allowNullableString: true,\n allowNullableNumber: true,\n allowNullableEnum: false,\n allowNullableStringLiteralUnion: false,\n onlyRequireUsedProps: true,\n },\n ],\n create(context, options) {\n const config = options[0];\n\n // Helper function to check component props\n const checkComponentProps = (\n functionNode: TSESTree.FunctionDeclaration | TSESTree.ArrowFunctionExpression,\n propsParam: TSESTree.Parameter | undefined\n ) => {\n if (!propsParam) {\n return;\n }\n\n let typeAnnotation: TSESTree.TypeNode | undefined;\n\n // Handle both regular parameters (props: Props) and destructured parameters ({prop1, prop2}: Props)\n if (propsParam.type === AST_NODE_TYPES.Identifier || propsParam.type === AST_NODE_TYPES.ObjectPattern) {\n typeAnnotation = propsParam.typeAnnotation?.typeAnnotation;\n } else {\n return;\n }\n\n const optionalProps = getOptionalPropsFromType(context, typeAnnotation);\n\n if (optionalProps.length === 0) {\n return;\n }\n\n const functionBody = functionNode.body.type === AST_NODE_TYPES.BlockStatement ? functionNode.body.body : [];\n\n optionalProps.forEach(prop => {\n // Check if this prop type is allowed to be nullable based on configuration\n const isAllowed =\n (prop.type === \"object\" && config.allowNullableObject === true) ||\n (prop.type === \"boolean\" && config.allowNullableBoolean === true) ||\n (prop.type === \"string\" && config.allowNullableString === true) ||\n (prop.type === \"number\" && config.allowNullableNumber === true) ||\n (prop.type === \"enum\" && config.allowNullableEnum === true) ||\n (prop.type === \"stringLiteral\" && config.allowNullableStringLiteralUnion === true) ||\n prop.type === \"unknown\"; // Always allow unknown types\n\n if (isAllowed) {\n return;\n }\n\n // If onlyRequireUsedProps is enabled, skip unused props\n if (config.onlyRequireUsedProps === true && !isParameterUsedInFunction(context, functionNode, prop.name)) {\n return;\n }\n\n // Skip props that are not explicitly destructured (i.e., captured in rest parameters like ...restOptions)\n // and are not used in the component body - these are just being passed through\n if (\n !isParameterExplicitlyDestructured(propsParam, prop.name) &&\n !isParameterUsedInFunction(context, functionNode, prop.name)\n ) {\n return;\n }\n\n // Skip props that are only used for pass-through to child components (e.g., <Tag color={tagColor} />)\n if (isParameterOnlyUsedInJSX(context, functionNode, prop.name) === true) {\n return;\n }\n\n // Check if the prop is initialized in the function body or parameter defaults\n if (!isParameterInitialized(functionBody, prop.name, propsParam)) {\n const isDestructuredParam = propsParam.type === AST_NODE_TYPES.ObjectPattern;\n const hasBodyInits = hasExistingBodyInitializations(functionBody);\n const propsParamName = propsParam.type === AST_NODE_TYPES.Identifier ? propsParam.name : \"props\";\n\n const suggestions = generateSuggestions(\n prop,\n propsParamName,\n functionNode,\n isDestructuredParam,\n hasBodyInits,\n propsParam,\n context\n );\n\n // Find the specific property node for more precise reporting\n let reportNode: TSESTree.Node = propsParam;\n if (propsParam.type === AST_NODE_TYPES.ObjectPattern) {\n const propProperty = propsParam.properties.find(\n p =>\n p.type === AST_NODE_TYPES.Property &&\n p.key.type === AST_NODE_TYPES.Identifier &&\n p.key.name === prop.name\n );\n if (propProperty && propProperty.type === AST_NODE_TYPES.Property) {\n reportNode = propProperty;\n }\n }\n\n context.report({\n node: reportNode,\n messageId: \"requireInitialization\",\n data: { propName: prop.name },\n suggest: suggestions,\n });\n }\n });\n };\n\n return {\n FunctionDeclaration(node: TSESTree.FunctionDeclaration) {\n if (isReactComponent(node)) {\n checkComponentProps(node, node.params[0]);\n }\n },\n\n ArrowFunctionExpression(node: TSESTree.ArrowFunctionExpression) {\n if (isReactComponent(node)) {\n checkComponentProps(node, node.params[0]);\n }\n },\n };\n },\n});\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"suggestion-utils.js","sourceRoot":"","sources":["../../../../../../../../libs/eslint/plugin-trackunit/src/lib/rules/require-optional-prop-initialization/suggestion-utils.ts"],"names":[],"mappings":"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","sourcesContent":["import { AST_NODE_TYPES, TSESLint, TSESTree } from \"@typescript-eslint/utils\";\nimport { isParameterExplicitlyDestructured } from \"../../utils/ast-utils\";\nimport { OptionalProp } from \"../../utils/typescript-utils\";\n\nexport type MessageIds = \"requireInitialization\" | \"initializeProp\";\n\nexport type SuggestionResult = {\n messageId: MessageIds;\n data?: Record<string, unknown>;\n fix: (fixer: TSESLint.RuleFixer) => TSESLint.RuleFix | Array<TSESLint.RuleFix> | null;\n};\n\n/**\n * Helper to find and replace a property in destructuring pattern\n */\nconst createParameterInitializationFix = (propName: string, value: string, propsParam: TSESTree.Parameter) => {\n return (fixer: TSESLint.RuleFixer) => {\n if (propsParam.type === AST_NODE_TYPES.ObjectPattern) {\n const property = propsParam.properties.find(\n p => p.type === AST_NODE_TYPES.Property && p.key.type === AST_NODE_TYPES.Identifier && p.key.name === propName\n );\n if (property && property.type === AST_NODE_TYPES.Property) {\n return fixer.replaceText(property, `${propName} = ${value}`);\n }\n }\n return null;\n };\n};\n\n/**\n * Helper to create body initialization fix\n */\nconst createBodyInitializationFix = (\n propName: string,\n value: string,\n propsParamName: string,\n functionBodyStart: number\n) => {\n return (fixer: TSESLint.RuleFixer) => {\n const destructuringFix = `\\n const { ${propName} = ${value} } = ${propsParamName};`;\n return fixer.insertTextAfterRange([functionBodyStart, functionBodyStart + 1], destructuringFix);\n };\n};\n\n/**\n * Helper to create a suggestion for a specific value\n */\nconst createSuggestion = (\n propName: string,\n value: string,\n displayValue: string,\n shouldUseParameterInit: boolean,\n propsParam: TSESTree.Parameter,\n propsParamName: string,\n functionBodyStart: number\n): SuggestionResult => {\n let locationSuffix = \"\";\n let fixFunction: (fixer: TSESLint.RuleFixer) => TSESLint.RuleFix | Array<TSESLint.RuleFix> | null;\n\n if (shouldUseParameterInit) {\n locationSuffix = \" in parameter\";\n fixFunction = createParameterInitializationFix(propName, value, propsParam);\n } else {\n fixFunction = createBodyInitializationFix(propName, value, propsParamName, functionBodyStart);\n }\n\n return {\n messageId: \"initializeProp\",\n data: { description: `Initialize '${propName}' to → ${displayValue}${locationSuffix}` },\n fix: fixFunction,\n };\n};\n\n/**\n * Helper to create suggestions for a set of values\n */\nconst createSuggestionsForValues = (\n propName: string,\n values: Array<{ value: string; display: string }>,\n shouldUseParameterInit: boolean,\n propsParam: TSESTree.Parameter,\n propsParamName: string,\n functionBodyStart: number\n): Array<SuggestionResult> => {\n return values.map(({ value, display }) =>\n createSuggestion(propName, value, display, shouldUseParameterInit, propsParam, propsParamName, functionBodyStart)\n );\n};\n\n/**\n * Helper function to generate suggestions for prop initialization\n */\nexport const generateSuggestions = (\n prop: OptionalProp,\n propsParamName: string,\n functionNode: TSESTree.FunctionDeclaration | TSESTree.ArrowFunctionExpression,\n isDestructuredParam: boolean,\n hasBodyInits: boolean,\n propsParam: TSESTree.Parameter,\n _context: TSESLint.RuleContext<string, ReadonlyArray<unknown>>\n): Array<SuggestionResult> => {\n const suggestions: Array<SuggestionResult> = [];\n\n if (functionNode.body.type !== AST_NODE_TYPES.BlockStatement) {\n return suggestions; // Can't suggest for arrow functions without block body\n }\n\n // Don't offer suggestions for props captured in rest parameters (e.g., ...rest)\n // The developer should manually handle extracting these from the rest object\n if (!isParameterExplicitlyDestructured(propsParam, prop.name)) {\n return suggestions;\n }\n\n const functionBodyStart = functionNode.body.range[0];\n const MAX_SUGGESTIONS = 5; // Don't overwhelm with too many suggestions\n\n // Decide where to place the initialization:\n // Since we know the prop is explicitly destructured (early return above handles rest params),\n // we can choose between parameter initialization or body initialization\n const isAlreadyInParameterDestructuring =\n isDestructuredParam &&\n propsParam.type === AST_NODE_TYPES.ObjectPattern &&\n propsParam.properties.some(\n p => p.type === AST_NODE_TYPES.Property && p.key.type === AST_NODE_TYPES.Identifier && p.key.name === prop.name\n );\n\n const shouldUseParameterInit = isAlreadyInParameterDestructuring || (isDestructuredParam && !hasBodyInits);\n\n // Boolean suggestions: true, false, and undefined\n if (prop.type === \"boolean\") {\n const booleanValues = [\n { value: \"true\", display: \"true\" },\n { value: \"false\", display: \"false\" },\n { value: \"undefined\", display: \"undefined\" },\n ];\n suggestions.push(\n ...createSuggestionsForValues(\n prop.name,\n booleanValues,\n shouldUseParameterInit,\n propsParam,\n propsParamName,\n functionBodyStart\n )\n );\n }\n\n // String literal suggestions: each individual literal (if not too many)\n if (prop.type === \"stringLiteral\" && prop.stringLiterals.length <= MAX_SUGGESTIONS) {\n const stringLiteralValues = [\n ...prop.stringLiterals.map(literal => ({ value: `\"${literal}\"`, display: `\"${literal}\"` })),\n { value: \"undefined\", display: \"undefined\" },\n ];\n suggestions.push(\n ...createSuggestionsForValues(\n prop.name,\n stringLiteralValues,\n shouldUseParameterInit,\n propsParam,\n propsParamName,\n functionBodyStart\n )\n );\n }\n\n // Enum suggestions: each individual enum value (if not too many)\n if (prop.type === \"enum\" && prop.enumValues.length <= MAX_SUGGESTIONS) {\n const enumValues = [\n ...prop.enumValues.map(enumValue => {\n const valueStr = typeof enumValue === \"string\" ? `\"${enumValue}\"` : String(enumValue);\n return { value: valueStr, display: valueStr };\n }),\n { value: \"undefined\", display: \"undefined\" },\n ];\n suggestions.push(\n ...createSuggestionsForValues(\n prop.name,\n enumValues,\n shouldUseParameterInit,\n propsParam,\n propsParamName,\n functionBodyStart\n )\n );\n }\n\n // String type suggestions\n if (prop.type === \"string\") {\n const stringValues = [\n { value: '\"\"', display: '\"\"' },\n { value: \"undefined\", display: \"undefined\" },\n ];\n suggestions.push(\n ...createSuggestionsForValues(\n prop.name,\n stringValues,\n shouldUseParameterInit,\n propsParam,\n propsParamName,\n functionBodyStart\n )\n );\n }\n\n // Number type suggestions\n if (prop.type === \"number\") {\n const numberValues = [\n { value: \"0\", display: \"0\" },\n { value: \"undefined\", display: \"undefined\" },\n ];\n suggestions.push(\n ...createSuggestionsForValues(\n prop.name,\n numberValues,\n shouldUseParameterInit,\n propsParam,\n propsParamName,\n functionBodyStart\n )\n );\n }\n\n // Object/unknown type suggestions\n if (prop.type === \"object\" || prop.type === \"unknown\") {\n const typeDisplay = prop.type === \"unknown\" ? \"any value\" : prop.type;\n suggestions.push(\n createSuggestion(\n prop.name,\n \"undefined\",\n `undefined (${typeDisplay})`,\n shouldUseParameterInit,\n propsParam,\n propsParamName,\n functionBodyStart\n )\n );\n }\n\n return suggestions;\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"rules-map.js","sourceRoot":"","sources":["../../../../../../libs/eslint/plugin-trackunit/src/lib/rules-map.ts"],"names":[],"mappings":";;;AAEA,6HAAqH;AACrH,qJAA6I;AAC7I,wGAAkG;AAClG,6KAAmK;AACnK,qJAA4I;AAC5I,+IAAmI;AACnI,qGAAgG;AAChG,iHAA4G;AAC5G,+IAAwI;AACxI,qGAAgG;AAChG,2JAAkJ;AAClJ,0HAAqH;AACrH,kJAA2I;AAC3I,4IAAsI;AAEzH,QAAA,QAAQ,GAAG;IACtB,iCAAiC,EAAE,4DAA0B;IAC7D,0BAA0B,EAAE,gDAAqB;IACjD,yBAAyB,EAAE,+CAAqB;IAChD,iDAAiD,EAAE,0FAAwC;IAC3F,yCAAyC,EAAE,4EAAkC;IAC7E,uCAAuC,EAAE,oEAA4B;IACrE,gCAAgC,EAAE,6DAA4B;IAC9D,yCAAyC,EAAE,2EAAiC;IAC5E,6BAA6B,EAAE,uDAAyB;IACxD,2CAA2C,EAAE,+EAAmC;IAChF,uCAAuC,EAAE,yEAAiC;IAC1E,sCAAsC,EAAE,wEAAiC;IACzE,yBAAyB,EAAE,+CAAqB;IAChD,wCAAwC,EAAE,2EAAkC;CACsB,CAAC","sourcesContent":["import { ESLintUtils } from \"@typescript-eslint/utils\";\nimport type { Rule } from \"eslint\";\nimport { cvaMergeBaseClassesAsArray } from \"./rules/cva-merge-base-classes-as-array/cva-merge-base-classes-as-array\";\nimport { designGuidelineButtonIconSizeMatch } from \"./rules/design-guideline-button-icon-size-match/design-guideline-button-icon-size-match\";\nimport { noInternalBarrelFiles } from \"./rules/no-internal-barrel-files/no-internal-barrel-files\";\nimport { noInternalGraphqlWhenTaggedWithGqlPublic } from \"./rules/no-internal-graphql-when-tagged-with-gql-public/no-internal-graphql-when-tagged-with-gql-public\";\nimport { noJestMockTrackunitReactCoreHooks } from \"./rules/no-jest-mock-trackunit-react-core-hooks/no-jest-mock-trackunit-react-core-hooks\";\nimport { noTemplateStringsInClassName } from \"./rules/no-template-strings-in-classname-prop/no-template-strings-in-classname-prop\";\nimport { noTypescriptAssertion } from \"./rules/no-typescript-assertion/no-typescript-assertion\";\nimport { preferDestructuredImports } from \"./rules/prefer-destructured-imports/prefer-destructured-imports\";\nimport { preferEventSpecificCallbackNaming } from \"./rules/prefer-event-specific-callback-naming/prefer-event-specific-callback-naming\";\nimport { preferFieldComponents } from \"./rules/prefer-field-components/prefer-field-components\";\nimport { preferMouseEventHandlerInReactProps } from \"./rules/prefer-mouse-event-handler-in-react-props/prefer-mouse-event-handler-in-react-props\";\nimport { requireClassnameAlternatives } from \"./rules/require-classname-alternatives/require-classname-alternatives\";\nimport { requireListItemVirtualizationProps } from \"./rules/require-list-item-virtualization-props/require-list-item-virtualization-props\";\nimport { requireOptionalPropInitialization } from \"./rules/require-optional-prop-initialization/require-optional-prop-initialization\";\n\nexport const rulesMap = {\n \"cva-merge-base-classes-as-array\": cvaMergeBaseClassesAsArray,\n \"no-internal-barrel-files\": noInternalBarrelFiles,\n \"no-typescript-assertion\": noTypescriptAssertion,\n \"no-internal-graphql-when-tagged-with-gql-public\": noInternalGraphqlWhenTaggedWithGqlPublic,\n \"design-guideline-button-icon-size-match\": designGuidelineButtonIconSizeMatch,\n \"no-template-strings-in-classname-prop\": noTemplateStringsInClassName,\n \"require-classname-alternatives\": requireClassnameAlternatives,\n \"no-jest-mock-trackunit-react-core-hooks\": noJestMockTrackunitReactCoreHooks,\n \"prefer-destructured-imports\": preferDestructuredImports,\n \"prefer-mouse-event-handler-in-react-props\": preferMouseEventHandlerInReactProps,\n \"prefer-event-specific-callback-naming\": preferEventSpecificCallbackNaming,\n \"require-optional-prop-initialization\": requireOptionalPropInitialization,\n \"prefer-field-components\": preferFieldComponents,\n \"require-list-item-virtualization-props\": requireListItemVirtualizationProps,\n} satisfies Record<string, Rule.RuleModule | ESLintUtils.RuleModule<string, ReadonlyArray<unknown>>>;\n"]}