@codyswann/lisa 1.47.0 → 1.48.0

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 (216) hide show
  1. package/all/copy-overwrite/.claude/rules/lisa.md +23 -10
  2. package/all/copy-overwrite/.claude/settings.json +10 -230
  3. package/all/deletions.json +67 -1
  4. package/cdk/copy-overwrite/.claude/settings.json +80 -0
  5. package/cdk/create-only/.github/workflows/ci.yml +1 -1
  6. package/cdk/create-only/.github/workflows/deploy.yml +1 -1
  7. package/dist/core/lisa.d.ts +14 -0
  8. package/dist/core/lisa.d.ts.map +1 -1
  9. package/dist/core/lisa.js +47 -0
  10. package/dist/core/lisa.js.map +1 -1
  11. package/expo/copy-overwrite/.claude/settings.json +80 -0
  12. package/expo/copy-overwrite/eslint.expo.ts +2 -2
  13. package/expo/create-only/.github/workflows/ci.yml +1 -1
  14. package/expo/create-only/.github/workflows/deploy.yml +1 -1
  15. package/expo/deletions.json +33 -0
  16. package/expo/package-lisa/package.lisa.json +2 -2
  17. package/nestjs/copy-overwrite/.claude/settings.json +80 -0
  18. package/nestjs/create-only/.github/workflows/ci.yml +1 -1
  19. package/nestjs/create-only/.github/workflows/deploy.yml +1 -1
  20. package/nestjs/deletions.json +8 -0
  21. package/package.json +8 -4
  22. package/rails/copy-overwrite/.claude/settings.json +80 -0
  23. package/rails/create-only/.github/workflows/ci.yml +1 -1
  24. package/rails/deletions.json +11 -1
  25. package/typescript/copy-overwrite/.claude/settings.json +13 -231
  26. package/typescript/copy-overwrite/.github/workflows/claude-ci-auto-fix.yml +1 -0
  27. package/typescript/copy-overwrite/.github/workflows/claude-code-review-response.yml +11 -10
  28. package/typescript/copy-overwrite/.github/workflows/claude-deploy-auto-fix.yml +1 -0
  29. package/typescript/copy-overwrite/.github/workflows/claude-nightly-code-complexity.yml +1 -0
  30. package/typescript/copy-overwrite/.github/workflows/claude-nightly-test-coverage.yml +1 -0
  31. package/typescript/copy-overwrite/.github/workflows/claude-nightly-test-improvement.yml +2 -0
  32. package/typescript/copy-overwrite/.github/workflows/claude.yml +1 -0
  33. package/typescript/copy-overwrite/eslint.typescript.ts +1 -1
  34. package/typescript/create-only/.github/workflows/ci.yml +1 -1
  35. package/typescript/deletions.json +12 -1
  36. package/typescript/package-lisa/package.lisa.json +1 -1
  37. package/all/copy-overwrite/.claude/agents/agent-architect.md +0 -310
  38. package/all/copy-overwrite/.claude/agents/architecture-specialist.md +0 -53
  39. package/all/copy-overwrite/.claude/agents/debug-specialist.md +0 -204
  40. package/all/copy-overwrite/.claude/agents/git-history-analyzer.md +0 -183
  41. package/all/copy-overwrite/.claude/agents/hooks-expert.md +0 -74
  42. package/all/copy-overwrite/.claude/agents/implementer.md +0 -54
  43. package/all/copy-overwrite/.claude/agents/learner.md +0 -44
  44. package/all/copy-overwrite/.claude/agents/performance-specialist.md +0 -95
  45. package/all/copy-overwrite/.claude/agents/product-specialist.md +0 -72
  46. package/all/copy-overwrite/.claude/agents/quality-specialist.md +0 -55
  47. package/all/copy-overwrite/.claude/agents/security-specialist.md +0 -58
  48. package/all/copy-overwrite/.claude/agents/skill-evaluator.md +0 -246
  49. package/all/copy-overwrite/.claude/agents/slash-command-architect.md +0 -87
  50. package/all/copy-overwrite/.claude/agents/test-specialist.md +0 -64
  51. package/all/copy-overwrite/.claude/agents/verification-specialist.md +0 -189
  52. package/all/copy-overwrite/.claude/agents/web-search-researcher.md +0 -112
  53. package/all/copy-overwrite/.claude/commands/git/commit-and-submit-pr.md +0 -7
  54. package/all/copy-overwrite/.claude/commands/git/commit-submit-pr-and-verify.md +0 -7
  55. package/all/copy-overwrite/.claude/commands/git/commit-submit-pr-deploy-and-verify.md +0 -7
  56. package/all/copy-overwrite/.claude/commands/git/commit.md +0 -7
  57. package/all/copy-overwrite/.claude/commands/git/prune.md +0 -6
  58. package/all/copy-overwrite/.claude/commands/git/submit-pr.md +0 -7
  59. package/all/copy-overwrite/.claude/commands/jira/create.md +0 -7
  60. package/all/copy-overwrite/.claude/commands/jira/sync.md +0 -7
  61. package/all/copy-overwrite/.claude/commands/jira/verify.md +0 -7
  62. package/all/copy-overwrite/.claude/commands/lisa/review-implementation.md +0 -7
  63. package/all/copy-overwrite/.claude/commands/plan/add-test-coverage.md +0 -7
  64. package/all/copy-overwrite/.claude/commands/plan/create.md +0 -6
  65. package/all/copy-overwrite/.claude/commands/plan/execute.md +0 -7
  66. package/all/copy-overwrite/.claude/commands/plan/fix-linter-error.md +0 -7
  67. package/all/copy-overwrite/.claude/commands/plan/local-code-review.md +0 -6
  68. package/all/copy-overwrite/.claude/commands/plan/lower-code-complexity.md +0 -6
  69. package/all/copy-overwrite/.claude/commands/plan/reduce-max-lines-per-function.md +0 -7
  70. package/all/copy-overwrite/.claude/commands/plan/reduce-max-lines.md +0 -7
  71. package/all/copy-overwrite/.claude/commands/pull-request/review.md +0 -7
  72. package/all/copy-overwrite/.claude/commands/security/zap-scan.md +0 -6
  73. package/all/copy-overwrite/.claude/commands/sonarqube/check.md +0 -6
  74. package/all/copy-overwrite/.claude/commands/sonarqube/fix.md +0 -6
  75. package/all/copy-overwrite/.claude/commands/tasks/load.md +0 -7
  76. package/all/copy-overwrite/.claude/commands/tasks/sync.md +0 -7
  77. package/all/copy-overwrite/.claude/hooks/check-tired-boss.sh +0 -61
  78. package/all/copy-overwrite/.claude/hooks/debug-hook.sh +0 -47
  79. package/all/copy-overwrite/.claude/hooks/enforce-plan-rules.sh +0 -15
  80. package/all/copy-overwrite/.claude/hooks/notify-ntfy.sh +0 -183
  81. package/all/copy-overwrite/.claude/hooks/setup-jira-cli.sh +0 -52
  82. package/all/copy-overwrite/.claude/hooks/sync-tasks.sh +0 -107
  83. package/all/copy-overwrite/.claude/hooks/ticket-sync-reminder.sh +0 -23
  84. package/all/copy-overwrite/.claude/hooks/track-plan-sessions.sh +0 -164
  85. package/all/copy-overwrite/.claude/rules/coding-philosophy.md +0 -428
  86. package/all/copy-overwrite/.claude/rules/verfication.md +0 -541
  87. package/all/copy-overwrite/.claude/skills/agent-design-best-practices/SKILL.md +0 -219
  88. package/all/copy-overwrite/.claude/skills/git-commit/SKILL.md +0 -48
  89. package/all/copy-overwrite/.claude/skills/git-commit-and-submit-pr/SKILL.md +0 -8
  90. package/all/copy-overwrite/.claude/skills/git-commit-submit-pr-and-verify/SKILL.md +0 -7
  91. package/all/copy-overwrite/.claude/skills/git-commit-submit-pr-deploy-and-verify/SKILL.md +0 -7
  92. package/all/copy-overwrite/.claude/skills/git-prune/SKILL.md +0 -35
  93. package/all/copy-overwrite/.claude/skills/git-submit-pr/SKILL.md +0 -44
  94. package/all/copy-overwrite/.claude/skills/jira-create/SKILL.md +0 -41
  95. package/all/copy-overwrite/.claude/skills/jira-sync/SKILL.md +0 -63
  96. package/all/copy-overwrite/.claude/skills/jira-verify/SKILL.md +0 -29
  97. package/all/copy-overwrite/.claude/skills/lisa-review-implementation/SKILL.md +0 -209
  98. package/all/copy-overwrite/.claude/skills/plan-add-test-coverage/SKILL.md +0 -44
  99. package/all/copy-overwrite/.claude/skills/plan-execute/SKILL.md +0 -89
  100. package/all/copy-overwrite/.claude/skills/plan-fix-linter-error/SKILL.md +0 -45
  101. package/all/copy-overwrite/.claude/skills/plan-local-code-review/SKILL.md +0 -88
  102. package/all/copy-overwrite/.claude/skills/plan-lower-code-complexity/SKILL.md +0 -44
  103. package/all/copy-overwrite/.claude/skills/plan-reduce-max-lines/SKILL.md +0 -45
  104. package/all/copy-overwrite/.claude/skills/plan-reduce-max-lines-per-function/SKILL.md +0 -46
  105. package/all/copy-overwrite/.claude/skills/pull-request-review/SKILL.md +0 -68
  106. package/all/copy-overwrite/.claude/skills/security-zap-scan/SKILL.md +0 -33
  107. package/all/copy-overwrite/.claude/skills/skill-creator/LICENSE.txt +0 -202
  108. package/all/copy-overwrite/.claude/skills/skill-creator/SKILL.md +0 -210
  109. package/all/copy-overwrite/.claude/skills/skill-creator/scripts/__pycache__/quick_validate.cpython-312.pyc +0 -0
  110. package/all/copy-overwrite/.claude/skills/skill-creator/scripts/init_skill.py +0 -305
  111. package/all/copy-overwrite/.claude/skills/skill-creator/scripts/package_skill.py +0 -112
  112. package/all/copy-overwrite/.claude/skills/skill-creator/scripts/quick_validate.py +0 -67
  113. package/all/copy-overwrite/.claude/skills/sonarqube-check/SKILL.md +0 -11
  114. package/all/copy-overwrite/.claude/skills/sonarqube-fix/SKILL.md +0 -8
  115. package/all/copy-overwrite/.claude/skills/tasks-load/SKILL.md +0 -88
  116. package/all/copy-overwrite/.claude/skills/tasks-sync/SKILL.md +0 -108
  117. package/eslint-plugin-code-organization/README.md +0 -149
  118. package/eslint-plugin-code-organization/__tests__/enforce-statement-order.test.js +0 -473
  119. package/eslint-plugin-code-organization/index.js +0 -28
  120. package/eslint-plugin-code-organization/package.json +0 -10
  121. package/eslint-plugin-code-organization/rules/enforce-statement-order.js +0 -162
  122. package/expo/copy-overwrite/.claude/agents/ops-specialist.md +0 -124
  123. package/expo/copy-overwrite/.claude/rules/expo-verification.md +0 -261
  124. package/expo/copy-overwrite/.claude/skills/apollo-client/SKILL.md +0 -238
  125. package/expo/copy-overwrite/.claude/skills/apollo-client/references/mutation-patterns.md +0 -360
  126. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/SKILL.md +0 -360
  127. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/atomic-levels.md +0 -417
  128. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/folder-structure.md +0 -257
  129. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/gluestack-mapping.md +0 -233
  130. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/scripts/validate_atomic_structure.py +0 -329
  131. package/expo/copy-overwrite/.claude/skills/container-view-pattern/SKILL.md +0 -299
  132. package/expo/copy-overwrite/.claude/skills/container-view-pattern/references/examples.md +0 -749
  133. package/expo/copy-overwrite/.claude/skills/container-view-pattern/references/patterns.md +0 -318
  134. package/expo/copy-overwrite/.claude/skills/container-view-pattern/scripts/create_component.py +0 -200
  135. package/expo/copy-overwrite/.claude/skills/container-view-pattern/scripts/validate_component.py +0 -209
  136. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/SKILL.md +0 -268
  137. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/common-issues.md +0 -619
  138. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/file-extensions.md +0 -340
  139. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/platform-api.md +0 -276
  140. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/scripts/validate_cross_platform.py +0 -416
  141. package/expo/copy-overwrite/.claude/skills/directory-structure/SKILL.md +0 -202
  142. package/expo/copy-overwrite/.claude/skills/directory-structure/scripts/validate_structure.py +0 -445
  143. package/expo/copy-overwrite/.claude/skills/expo-env-config/SKILL.md +0 -309
  144. package/expo/copy-overwrite/.claude/skills/expo-env-config/references/validation-patterns.md +0 -417
  145. package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/SKILL.md +0 -431
  146. package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/references/official-docs.md +0 -290
  147. package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/scripts/generate-route.py +0 -171
  148. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/SKILL.md +0 -411
  149. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/color-tokens.md +0 -343
  150. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/component-mapping.md +0 -307
  151. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/spacing-scale.md +0 -300
  152. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/scripts/validate_styling.py +0 -315
  153. package/expo/copy-overwrite/.claude/skills/local-state/SKILL.md +0 -362
  154. package/expo/copy-overwrite/.claude/skills/local-state/references/async-storage.md +0 -505
  155. package/expo/copy-overwrite/.claude/skills/local-state/references/persistence-patterns.md +0 -711
  156. package/expo/copy-overwrite/.claude/skills/local-state/references/reactive-variables.md +0 -446
  157. package/expo/copy-overwrite/.claude/skills/ops-browser-uat/SKILL.md +0 -124
  158. package/expo/copy-overwrite/.claude/skills/ops-check-logs/SKILL.md +0 -211
  159. package/expo/copy-overwrite/.claude/skills/ops-db-ops/SKILL.md +0 -119
  160. package/expo/copy-overwrite/.claude/skills/ops-deploy/SKILL.md +0 -119
  161. package/expo/copy-overwrite/.claude/skills/ops-monitor-errors/SKILL.md +0 -99
  162. package/expo/copy-overwrite/.claude/skills/ops-performance/SKILL.md +0 -165
  163. package/expo/copy-overwrite/.claude/skills/ops-run-local/SKILL.md +0 -166
  164. package/expo/copy-overwrite/.claude/skills/ops-verify-health/SKILL.md +0 -101
  165. package/expo/copy-overwrite/.claude/skills/owasp-zap/SKILL.md +0 -56
  166. package/expo/copy-overwrite/.claude/skills/playwright-selectors/SKILL.md +0 -223
  167. package/expo/copy-overwrite/.claude/skills/testing-library/SKILL.md +0 -314
  168. package/expo/copy-overwrite/.claude/skills/testing-library/references/async-patterns.md +0 -420
  169. package/expo/copy-overwrite/.claude/skills/testing-library/references/expo-router-testing.md +0 -556
  170. package/expo/copy-overwrite/.claude/skills/testing-library/references/mocking-patterns.md +0 -590
  171. package/expo/copy-overwrite/.claude/skills/testing-library/references/query-priority.md +0 -291
  172. package/expo/copy-overwrite/eslint-plugin-component-structure/README.md +0 -234
  173. package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/plugin-index.test.js +0 -89
  174. package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/require-memo-in-view.test.js +0 -201
  175. package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/single-component-per-file.test.js +0 -294
  176. package/expo/copy-overwrite/eslint-plugin-component-structure/index.js +0 -37
  177. package/expo/copy-overwrite/eslint-plugin-component-structure/package.json +0 -10
  178. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/enforce-component-structure.js +0 -235
  179. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/no-return-in-view.js +0 -96
  180. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/require-memo-in-view.js +0 -183
  181. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/single-component-per-file.js +0 -243
  182. package/expo/copy-overwrite/eslint-plugin-ui-standards/README.md +0 -192
  183. package/expo/copy-overwrite/eslint-plugin-ui-standards/index.js +0 -31
  184. package/expo/copy-overwrite/eslint-plugin-ui-standards/package.json +0 -10
  185. package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-classname-outside-ui.js +0 -56
  186. package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-direct-rn-imports.js +0 -60
  187. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/SKILL.md +0 -176
  188. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/advanced-features.md +0 -527
  189. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/project-patterns.md +0 -483
  190. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/quick-start.md +0 -257
  191. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/resolvers-mutations.md +0 -413
  192. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/types-scalars.md +0 -513
  193. package/nestjs/copy-overwrite/.claude/skills/nestjs-rules/SKILL.md +0 -536
  194. package/nestjs/copy-overwrite/.claude/skills/security-zap-scan/SKILL.md +0 -33
  195. package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/SKILL.md +0 -275
  196. package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/configuration-patterns.md +0 -487
  197. package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/entity-patterns.md +0 -450
  198. package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/observability-patterns.md +0 -536
  199. package/rails/copy-overwrite/.claude/skills/action-controller-best-practices/SKILL.md +0 -374
  200. package/rails/copy-overwrite/.claude/skills/action-view-best-practices/SKILL.md +0 -335
  201. package/rails/copy-overwrite/.claude/skills/active-record-model-best-practices/SKILL.md +0 -166
  202. package/rails/copy-overwrite/.claude/skills/plan-add-test-coverage/SKILL.md +0 -45
  203. package/rails/copy-overwrite/.claude/skills/plan-fix-linter-error/SKILL.md +0 -45
  204. package/rails/copy-overwrite/.claude/skills/plan-lower-code-complexity/SKILL.md +0 -48
  205. package/rails/copy-overwrite/.claude/skills/plan-reduce-max-lines/SKILL.md +0 -46
  206. package/rails/copy-overwrite/.claude/skills/plan-reduce-max-lines-per-function/SKILL.md +0 -46
  207. package/typescript/copy-overwrite/.claude/hooks/format-on-edit.sh +0 -76
  208. package/typescript/copy-overwrite/.claude/hooks/install-pkgs.sh +0 -64
  209. package/typescript/copy-overwrite/.claude/hooks/lint-on-edit.sh +0 -105
  210. package/typescript/copy-overwrite/.claude/hooks/sg-scan-on-edit.sh +0 -68
  211. package/typescript/copy-overwrite/.claude/skills/jsdoc-best-practices/SKILL.md +0 -432
  212. package/typescript/copy-overwrite/eslint-plugin-code-organization/README.md +0 -149
  213. package/typescript/copy-overwrite/eslint-plugin-code-organization/__tests__/enforce-statement-order.test.js +0 -473
  214. package/typescript/copy-overwrite/eslint-plugin-code-organization/index.js +0 -28
  215. package/typescript/copy-overwrite/eslint-plugin-code-organization/package.json +0 -10
  216. package/typescript/copy-overwrite/eslint-plugin-code-organization/rules/enforce-statement-order.js +0 -162
@@ -1,243 +0,0 @@
1
- /**
2
- * This file is managed by Lisa.
3
- * Do not edit directly — changes will be overwritten on the next `lisa` run.
4
- */
5
-
6
- /**
7
- * ESLint rule to enforce exactly one React component per file
8
- *
9
- * This rule ensures that View and Container files contain only one React component.
10
- * A React component is defined as any PascalCase function that returns JSX.
11
- *
12
- * Applies to:
13
- * - *View.tsx, *View.jsx files
14
- * - *Container.tsx, *Container.jsx files
15
- *
16
- * Excludes:
17
- * - components/ui/** directory (third-party generated files)
18
- * - components/custom/ui/** directory (third-party generated files)
19
- * - components/shared/** directory (shared utility components)
20
- * @module eslint-plugin-component-structure/rules/single-component-per-file
21
- */
22
-
23
- module.exports = {
24
- meta: {
25
- type: "problem",
26
- docs: {
27
- description:
28
- "Enforce exactly one React component per View or Container file",
29
- category: "Best Practices",
30
- recommended: true,
31
- },
32
- schema: [],
33
- messages: {
34
- multipleComponents:
35
- "Only one React component is allowed per file. Found '{{componentName}}' in addition to '{{firstComponentName}}'. Extract '{{componentName}}' to a separate file.",
36
- },
37
- },
38
-
39
- create(context) {
40
- const filename = context.getFilename();
41
- const normalizedPath = filename.replace(/\\/g, "/");
42
-
43
- // Only check View and Container files
44
- const isViewOrContainer =
45
- filename.endsWith("View.tsx") ||
46
- filename.endsWith("View.jsx") ||
47
- filename.endsWith("Container.tsx") ||
48
- filename.endsWith("Container.jsx");
49
-
50
- if (!isViewOrContainer) {
51
- return {};
52
- }
53
-
54
- // Exclude components/ui/**, components/custom/ui/**, and components/shared/** directories
55
- if (
56
- normalizedPath.includes("/components/ui/") ||
57
- normalizedPath.includes("/components/custom/ui/") ||
58
- normalizedPath.includes("/components/shared/") ||
59
- normalizedPath.startsWith("components/ui/") ||
60
- normalizedPath.startsWith("components/custom/ui/") ||
61
- normalizedPath.startsWith("components/shared/")
62
- ) {
63
- return {};
64
- }
65
-
66
- // Check if file is in features/**/components directory or components directory
67
- const isFeatureComponent =
68
- normalizedPath.includes("/features/") &&
69
- normalizedPath.includes("/components/");
70
- const isComponentsDir =
71
- normalizedPath.includes("/components/") ||
72
- normalizedPath.startsWith("components/");
73
-
74
- if (!isFeatureComponent && !isComponentsDir) {
75
- return {};
76
- }
77
-
78
- const state = {
79
- components: [], // Array of { name, node }
80
- firstComponent: null,
81
- };
82
-
83
- /**
84
- * Recursively checks if an expression contains JSX
85
- * @param {object} node - AST node to check
86
- * @returns {boolean} True if expression contains JSX
87
- */
88
- const containsJSX = node => {
89
- if (!node) {
90
- return false;
91
- }
92
-
93
- const type = node.type;
94
- if (type === "JSXElement" || type === "JSXFragment") {
95
- return true;
96
- }
97
-
98
- // Check conditional expressions: condition ? consequent : alternate
99
- if (type === "ConditionalExpression") {
100
- return containsJSX(node.consequent) || containsJSX(node.alternate);
101
- }
102
-
103
- // Check logical expressions: left && right, left || right
104
- if (type === "LogicalExpression") {
105
- return containsJSX(node.left) || containsJSX(node.right);
106
- }
107
-
108
- // Check parenthesized expressions
109
- if (type === "ParenthesizedExpression") {
110
- return containsJSX(node.expression);
111
- }
112
-
113
- return false;
114
- };
115
-
116
- /**
117
- * Checks if a function returns JSX by examining its body
118
- * @param {object} node - AST node to check
119
- * @returns {boolean} True if function returns JSX
120
- */
121
- const returnsJSX = node => {
122
- if (!node || !node.body) {
123
- return false;
124
- }
125
-
126
- // Handle arrow function with direct JSX return (no block)
127
- if (node.type === "ArrowFunctionExpression") {
128
- return containsJSX(node.body);
129
- }
130
-
131
- // Handle function with block body
132
- if (node.body.type === "BlockStatement") {
133
- const hasJSXReturn = node.body.body.some(statement => {
134
- return (
135
- statement.type === "ReturnStatement" &&
136
- containsJSX(statement.argument)
137
- );
138
- });
139
-
140
- if (hasJSXReturn) {
141
- return true;
142
- }
143
- }
144
-
145
- return false;
146
- };
147
-
148
- /**
149
- * Records a component if it meets all criteria (PascalCase + returns JSX)
150
- * @param {string} name - Component name
151
- * @param {object} node - AST node
152
- * @param {object} functionNode - Function AST node
153
- */
154
- const recordComponent = (name, node, functionNode) => {
155
- // Check if name is PascalCase
156
- if (!/^[A-Z]/.test(name)) {
157
- return;
158
- }
159
-
160
- // Check if function returns JSX
161
- if (!returnsJSX(functionNode)) {
162
- return;
163
- }
164
-
165
- // This is a component - record it
166
- if (state.components.length === 0) {
167
- state.firstComponent = { name, node };
168
- }
169
- state.components.push({ name, node });
170
- };
171
-
172
- return {
173
- VariableDeclarator(node) {
174
- if (node.id.type !== "Identifier") {
175
- return;
176
- }
177
-
178
- const name = node.id.name;
179
-
180
- // Check for arrow function assignment: const Component = () => <div />
181
- if (node.init && node.init.type === "ArrowFunctionExpression") {
182
- const jsxCheck = returnsJSX(node.init);
183
- if (jsxCheck) {
184
- recordComponent(name, node, node.init);
185
- return;
186
- }
187
- }
188
-
189
- // Check for memo-wrapped component: const Component = memo(() => <div />)
190
- if (
191
- node.init &&
192
- node.init.type === "CallExpression" &&
193
- ((node.init.callee.type === "Identifier" &&
194
- node.init.callee.name === "memo") ||
195
- (node.init.callee.type === "MemberExpression" &&
196
- node.init.callee.object.name === "React" &&
197
- node.init.callee.property.name === "memo"))
198
- ) {
199
- const firstArg = node.init.arguments[0];
200
- if (firstArg && returnsJSX(firstArg)) {
201
- recordComponent(name, node, firstArg);
202
- return;
203
- }
204
- }
205
-
206
- // Check for React.FC typed components: const Component: React.FC = () => <div />
207
- if (
208
- node.init &&
209
- node.init.type === "ArrowFunctionExpression" &&
210
- node.id.typeAnnotation &&
211
- returnsJSX(node.init)
212
- ) {
213
- recordComponent(name, node, node.init);
214
- }
215
- },
216
-
217
- FunctionDeclaration(node) {
218
- if (!node.id || node.id.type !== "Identifier") {
219
- return;
220
- }
221
-
222
- const name = node.id.name;
223
- recordComponent(name, node, node);
224
- },
225
-
226
- "Program:exit"() {
227
- // Report all components after the first one
228
- if (state.components.length > 1) {
229
- state.components.slice(1).forEach(component => {
230
- context.report({
231
- node: component.node,
232
- messageId: "multipleComponents",
233
- data: {
234
- componentName: component.name,
235
- firstComponentName: state.firstComponent.name,
236
- },
237
- });
238
- });
239
- }
240
- },
241
- };
242
- },
243
- };
@@ -1,192 +0,0 @@
1
- # ESLint Plugin: UI Standards
2
-
3
- Custom ESLint rules for enforcing UI-related coding standards in React Native applications.
4
-
5
- ## Rules
6
-
7
- ### no-classname-outside-ui
8
-
9
- Restricts the use of `className` prop to designated UI component directories.
10
-
11
- #### Rule Details
12
-
13
- This rule ensures that `className` (used with Tailwind/NativeWind) is only used in reusable UI components. Business components should use semantic props instead of styling classes.
14
-
15
- **Why this rule exists:**
16
- - Keeps styling concerns in UI layer components
17
- - Business components remain style-agnostic
18
- - Makes component APIs more semantic and maintainable
19
- - Facilitates design system consistency
20
-
21
- **Where is className allowed?**
22
- - `components/ui/` - Core UI components
23
- - `components/custom/ui/` - Custom UI components
24
-
25
- #### Examples
26
-
27
- **Incorrect** (className in business component):
28
-
29
- ```tsx
30
- // features/user/components/ProfileCard/ProfileCardView.tsx
31
- const ProfileCardView = ({ user }) => (
32
- <View className="p-4 bg-white rounded-lg"> {/* className here - NOT allowed */}
33
- <Text className="text-lg font-bold">{user.name}</Text>
34
- </View>
35
- );
36
- ```
37
-
38
- **Correct** (using UI components with semantic props):
39
-
40
- ```tsx
41
- // features/user/components/ProfileCard/ProfileCardView.tsx
42
- import { Card, Heading } from '@/components/ui';
43
-
44
- const ProfileCardView = ({ user }) => (
45
- <Card variant="elevated">
46
- <Heading size="lg">{user.name}</Heading>
47
- </Card>
48
- );
49
- ```
50
-
51
- **Correct** (className in UI component):
52
-
53
- ```tsx
54
- // components/ui/Card/CardView.tsx
55
- const CardView = ({ variant, children }) => (
56
- <View className={cn("rounded-lg", variants[variant])}>
57
- {children}
58
- </View>
59
- );
60
- ```
61
-
62
- #### Configuration
63
-
64
- ```javascript
65
- // eslint.config.mjs
66
- {
67
- rules: {
68
- 'ui-standards/no-classname-outside-ui': ['error', {
69
- allowedPaths: ['/components/ui/', '/components/custom/ui/']
70
- }]
71
- }
72
- }
73
- ```
74
-
75
- **Options:**
76
-
77
- | Option | Type | Default | Description |
78
- |--------|------|---------|-------------|
79
- | `allowedPaths` | `string[]` | `['/components/ui/', '/components/custom/ui/']` | Paths where className is allowed |
80
-
81
- ---
82
-
83
- ### no-direct-rn-imports
84
-
85
- Prevents direct imports from `react-native` to encourage use of wrapped UI components.
86
-
87
- #### Rule Details
88
-
89
- This rule blocks direct imports from `react-native` in favor of using the project's UI component library. This ensures:
90
-
91
- - Consistent styling across the app
92
- - Ability to swap underlying implementations
93
- - Centralized accessibility handling
94
- - Design system compliance
95
-
96
- **What's blocked?**
97
- - `import { View, Text, ... } from 'react-native'`
98
-
99
- **What to use instead?**
100
- - `import { View, Text, ... } from '@/components/ui'`
101
-
102
- #### Examples
103
-
104
- **Incorrect:**
105
-
106
- ```tsx
107
- import { View, Text, TouchableOpacity } from 'react-native';
108
-
109
- const MyComponent = () => (
110
- <View>
111
- <Text>Hello</Text>
112
- <TouchableOpacity onPress={handlePress}>
113
- <Text>Click me</Text>
114
- </TouchableOpacity>
115
- </View>
116
- );
117
- ```
118
-
119
- **Correct:**
120
-
121
- ```tsx
122
- import { View, Text, Button } from '@/components/ui';
123
-
124
- const MyComponent = () => (
125
- <View>
126
- <Text>Hello</Text>
127
- <Button onPress={handlePress}>Click me</Button>
128
- </View>
129
- );
130
- ```
131
-
132
- #### Configuration
133
-
134
- ```javascript
135
- // eslint.config.mjs
136
- {
137
- rules: {
138
- 'ui-standards/no-direct-rn-imports': 'error'
139
- }
140
- }
141
- ```
142
-
143
- **Allowed directories:**
144
- - `components/ui/` - UI wrappers need to import from react-native
145
- - `components/custom/ui/` - Custom UI components
146
-
147
- ---
148
-
149
- ## Installation
150
-
151
- This plugin is installed locally as a file dependency:
152
-
153
- ```json
154
- {
155
- "devDependencies": {
156
- "eslint-plugin-ui-standards": "file:./eslint-plugin-ui-standards"
157
- }
158
- }
159
- ```
160
-
161
- ## Usage with ESLint 9 Flat Config
162
-
163
- ```javascript
164
- // eslint.config.mjs
165
- import uiStandardsPlugin from './eslint-plugin-ui-standards/index.js';
166
-
167
- export default [
168
- {
169
- plugins: {
170
- 'ui-standards': uiStandardsPlugin,
171
- },
172
- rules: {
173
- 'ui-standards/no-classname-outside-ui': 'error',
174
- 'ui-standards/no-direct-rn-imports': 'error',
175
- },
176
- },
177
- ];
178
- ```
179
-
180
- ## Contributing
181
-
182
- When adding new rules:
183
-
184
- 1. Create rule implementation in `rules/`
185
- 2. Add tests in `__tests__/`
186
- 3. Export in `index.js`
187
- 4. Document in this README
188
- 5. Add to ESLint configuration
189
-
190
- ## Version
191
-
192
- 1.0.0
@@ -1,31 +0,0 @@
1
- /**
2
- * This file is managed by Lisa.
3
- * Do not edit directly — changes will be overwritten on the next `lisa` run.
4
- */
5
-
6
- /**
7
- * ESLint plugin for UI standards
8
- *
9
- * This plugin enforces UI-related coding standards for React Native components.
10
- * Supports ESLint 9 flat config format.
11
- *
12
- * Rules:
13
- * - no-classname-outside-ui: Disallows className prop outside UI components
14
- * - no-direct-rn-imports: Disallows direct React Native imports
15
- * @module eslint-plugin-ui-standards
16
- */
17
- const noClassnameOutsideUi = require("./rules/no-classname-outside-ui");
18
- const noDirectRnImports = require("./rules/no-direct-rn-imports");
19
-
20
- const plugin = {
21
- meta: {
22
- name: "eslint-plugin-ui-standards",
23
- version: "1.0.0",
24
- },
25
- rules: {
26
- "no-classname-outside-ui": noClassnameOutsideUi,
27
- "no-direct-rn-imports": noDirectRnImports,
28
- },
29
- };
30
-
31
- module.exports = plugin;
@@ -1,10 +0,0 @@
1
- {
2
- "name": "eslint-plugin-ui-standards",
3
- "version": "1.0.0",
4
- "description": "ESLint plugin for UI component standards",
5
- "main": "index.js",
6
- "private": true,
7
- "peerDependencies": {
8
- "eslint": ">=9.0.0"
9
- }
10
- }
@@ -1,56 +0,0 @@
1
- /**
2
- * This file is managed by Lisa.
3
- * Do not edit directly — changes will be overwritten on the next `lisa` run.
4
- */
5
-
6
- module.exports = {
7
- meta: {
8
- type: "problem",
9
- docs: {
10
- description: "Disallow className outside of UI component directories",
11
- category: "Best Practices",
12
- recommended: true,
13
- },
14
- fixable: null,
15
- schema: [
16
- {
17
- type: "object",
18
- properties: {
19
- allowedPaths: {
20
- type: "array",
21
- items: { type: "string" },
22
- },
23
- },
24
- additionalProperties: false,
25
- },
26
- ],
27
- messages: {
28
- noClassNameOutsideUI:
29
- "className is only allowed in components/ui and components/custom/ui directories. Create a reusable component with semantic props instead.",
30
- },
31
- },
32
-
33
- create(context) {
34
- const options = context.options[0] || {};
35
- const allowedPaths = options.allowedPaths || [
36
- "/components/ui/",
37
- "/components/custom/ui/",
38
- ];
39
-
40
- return {
41
- JSXAttribute(node) {
42
- if (node.name.name !== "className") return;
43
-
44
- const filename = context.getFilename().replace(/\\/g, "/");
45
- const isAllowed = allowedPaths.some(path => filename.includes(path));
46
-
47
- if (!isAllowed) {
48
- context.report({
49
- node,
50
- messageId: "noClassNameOutsideUI",
51
- });
52
- }
53
- },
54
- };
55
- },
56
- };
@@ -1,60 +0,0 @@
1
- /**
2
- * This file is managed by Lisa.
3
- * Do not edit directly — changes will be overwritten on the next `lisa` run.
4
- */
5
-
6
- module.exports = {
7
- meta: {
8
- type: "problem",
9
- docs: {
10
- description: "Prevent direct React Native component imports",
11
- category: "Best Practices",
12
- recommended: true,
13
- },
14
- fixable: null,
15
- schema: [],
16
- messages: {
17
- noDirectRNImport:
18
- "Don't import {{importName}} from 'react-native'. Use {{suggestion}} from '@/components/ui' instead.",
19
- },
20
- },
21
-
22
- create(context) {
23
- const componentMap = {
24
- View: "Box",
25
- Text: "Text",
26
- Image: "Image",
27
- ScrollView: "ScrollView",
28
- Pressable: "Pressable",
29
- TouchableOpacity: "Pressable",
30
- TouchableHighlight: "Pressable",
31
- TouchableWithoutFeedback: "Pressable",
32
- TextInput: "Input",
33
- FlatList: "FlatList",
34
- SectionList: "SectionList",
35
- };
36
-
37
- return {
38
- ImportDeclaration(node) {
39
- if (node.source.value !== "react-native") return;
40
-
41
- node.specifiers.forEach(specifier => {
42
- if (specifier.type === "ImportSpecifier") {
43
- const importedName = specifier.imported.name;
44
-
45
- if (componentMap[importedName]) {
46
- context.report({
47
- node: specifier,
48
- messageId: "noDirectRNImport",
49
- data: {
50
- importName: importedName,
51
- suggestion: componentMap[importedName],
52
- },
53
- });
54
- }
55
- }
56
- });
57
- },
58
- };
59
- },
60
- };