@codyswann/lisa 1.47.1 → 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 (210) hide show
  1. package/all/copy-overwrite/.claude/rules/lisa.md +23 -10
  2. package/all/copy-overwrite/.claude/settings.json +10 -252
  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 -253
  26. package/typescript/copy-overwrite/eslint.typescript.ts +1 -1
  27. package/typescript/create-only/.github/workflows/ci.yml +1 -1
  28. package/typescript/deletions.json +12 -1
  29. package/typescript/package-lisa/package.lisa.json +1 -1
  30. package/all/copy-overwrite/.claude/agents/agent-architect.md +0 -310
  31. package/all/copy-overwrite/.claude/agents/architecture-specialist.md +0 -53
  32. package/all/copy-overwrite/.claude/agents/debug-specialist.md +0 -204
  33. package/all/copy-overwrite/.claude/agents/git-history-analyzer.md +0 -183
  34. package/all/copy-overwrite/.claude/agents/hooks-expert.md +0 -74
  35. package/all/copy-overwrite/.claude/agents/implementer.md +0 -54
  36. package/all/copy-overwrite/.claude/agents/learner.md +0 -44
  37. package/all/copy-overwrite/.claude/agents/performance-specialist.md +0 -95
  38. package/all/copy-overwrite/.claude/agents/product-specialist.md +0 -72
  39. package/all/copy-overwrite/.claude/agents/quality-specialist.md +0 -55
  40. package/all/copy-overwrite/.claude/agents/security-specialist.md +0 -58
  41. package/all/copy-overwrite/.claude/agents/skill-evaluator.md +0 -246
  42. package/all/copy-overwrite/.claude/agents/slash-command-architect.md +0 -87
  43. package/all/copy-overwrite/.claude/agents/test-specialist.md +0 -64
  44. package/all/copy-overwrite/.claude/agents/verification-specialist.md +0 -189
  45. package/all/copy-overwrite/.claude/agents/web-search-researcher.md +0 -112
  46. package/all/copy-overwrite/.claude/commands/git/commit-and-submit-pr.md +0 -7
  47. package/all/copy-overwrite/.claude/commands/git/commit-submit-pr-and-verify.md +0 -7
  48. package/all/copy-overwrite/.claude/commands/git/commit-submit-pr-deploy-and-verify.md +0 -7
  49. package/all/copy-overwrite/.claude/commands/git/commit.md +0 -7
  50. package/all/copy-overwrite/.claude/commands/git/prune.md +0 -6
  51. package/all/copy-overwrite/.claude/commands/git/submit-pr.md +0 -7
  52. package/all/copy-overwrite/.claude/commands/jira/create.md +0 -7
  53. package/all/copy-overwrite/.claude/commands/jira/sync.md +0 -7
  54. package/all/copy-overwrite/.claude/commands/jira/verify.md +0 -7
  55. package/all/copy-overwrite/.claude/commands/lisa/review-implementation.md +0 -7
  56. package/all/copy-overwrite/.claude/commands/plan/add-test-coverage.md +0 -7
  57. package/all/copy-overwrite/.claude/commands/plan/create.md +0 -6
  58. package/all/copy-overwrite/.claude/commands/plan/execute.md +0 -7
  59. package/all/copy-overwrite/.claude/commands/plan/fix-linter-error.md +0 -7
  60. package/all/copy-overwrite/.claude/commands/plan/local-code-review.md +0 -6
  61. package/all/copy-overwrite/.claude/commands/plan/lower-code-complexity.md +0 -6
  62. package/all/copy-overwrite/.claude/commands/plan/reduce-max-lines-per-function.md +0 -7
  63. package/all/copy-overwrite/.claude/commands/plan/reduce-max-lines.md +0 -7
  64. package/all/copy-overwrite/.claude/commands/pull-request/review.md +0 -7
  65. package/all/copy-overwrite/.claude/commands/security/zap-scan.md +0 -6
  66. package/all/copy-overwrite/.claude/commands/sonarqube/check.md +0 -6
  67. package/all/copy-overwrite/.claude/commands/sonarqube/fix.md +0 -6
  68. package/all/copy-overwrite/.claude/commands/tasks/load.md +0 -7
  69. package/all/copy-overwrite/.claude/commands/tasks/sync.md +0 -7
  70. package/all/copy-overwrite/.claude/hooks/check-tired-boss.sh +0 -61
  71. package/all/copy-overwrite/.claude/hooks/debug-hook.sh +0 -47
  72. package/all/copy-overwrite/.claude/hooks/enforce-plan-rules.sh +0 -15
  73. package/all/copy-overwrite/.claude/hooks/notify-ntfy.sh +0 -183
  74. package/all/copy-overwrite/.claude/hooks/setup-jira-cli.sh +0 -52
  75. package/all/copy-overwrite/.claude/hooks/sync-tasks.sh +0 -107
  76. package/all/copy-overwrite/.claude/hooks/ticket-sync-reminder.sh +0 -23
  77. package/all/copy-overwrite/.claude/hooks/track-plan-sessions.sh +0 -164
  78. package/all/copy-overwrite/.claude/hooks/verify-completion.sh +0 -77
  79. package/all/copy-overwrite/.claude/rules/coding-philosophy.md +0 -428
  80. package/all/copy-overwrite/.claude/rules/verfication.md +0 -596
  81. package/all/copy-overwrite/.claude/skills/agent-design-best-practices/SKILL.md +0 -219
  82. package/all/copy-overwrite/.claude/skills/git-commit/SKILL.md +0 -48
  83. package/all/copy-overwrite/.claude/skills/git-commit-and-submit-pr/SKILL.md +0 -8
  84. package/all/copy-overwrite/.claude/skills/git-commit-submit-pr-and-verify/SKILL.md +0 -7
  85. package/all/copy-overwrite/.claude/skills/git-commit-submit-pr-deploy-and-verify/SKILL.md +0 -7
  86. package/all/copy-overwrite/.claude/skills/git-prune/SKILL.md +0 -35
  87. package/all/copy-overwrite/.claude/skills/git-submit-pr/SKILL.md +0 -44
  88. package/all/copy-overwrite/.claude/skills/jira-create/SKILL.md +0 -41
  89. package/all/copy-overwrite/.claude/skills/jira-sync/SKILL.md +0 -63
  90. package/all/copy-overwrite/.claude/skills/jira-verify/SKILL.md +0 -29
  91. package/all/copy-overwrite/.claude/skills/lisa-review-implementation/SKILL.md +0 -209
  92. package/all/copy-overwrite/.claude/skills/plan-add-test-coverage/SKILL.md +0 -44
  93. package/all/copy-overwrite/.claude/skills/plan-execute/SKILL.md +0 -89
  94. package/all/copy-overwrite/.claude/skills/plan-fix-linter-error/SKILL.md +0 -45
  95. package/all/copy-overwrite/.claude/skills/plan-local-code-review/SKILL.md +0 -88
  96. package/all/copy-overwrite/.claude/skills/plan-lower-code-complexity/SKILL.md +0 -44
  97. package/all/copy-overwrite/.claude/skills/plan-reduce-max-lines/SKILL.md +0 -45
  98. package/all/copy-overwrite/.claude/skills/plan-reduce-max-lines-per-function/SKILL.md +0 -46
  99. package/all/copy-overwrite/.claude/skills/pull-request-review/SKILL.md +0 -68
  100. package/all/copy-overwrite/.claude/skills/security-zap-scan/SKILL.md +0 -33
  101. package/all/copy-overwrite/.claude/skills/skill-creator/LICENSE.txt +0 -202
  102. package/all/copy-overwrite/.claude/skills/skill-creator/SKILL.md +0 -210
  103. package/all/copy-overwrite/.claude/skills/skill-creator/scripts/__pycache__/quick_validate.cpython-312.pyc +0 -0
  104. package/all/copy-overwrite/.claude/skills/skill-creator/scripts/init_skill.py +0 -305
  105. package/all/copy-overwrite/.claude/skills/skill-creator/scripts/package_skill.py +0 -112
  106. package/all/copy-overwrite/.claude/skills/skill-creator/scripts/quick_validate.py +0 -67
  107. package/all/copy-overwrite/.claude/skills/sonarqube-check/SKILL.md +0 -11
  108. package/all/copy-overwrite/.claude/skills/sonarqube-fix/SKILL.md +0 -8
  109. package/all/copy-overwrite/.claude/skills/tasks-load/SKILL.md +0 -88
  110. package/all/copy-overwrite/.claude/skills/tasks-sync/SKILL.md +0 -108
  111. package/eslint-plugin-code-organization/README.md +0 -149
  112. package/eslint-plugin-code-organization/__tests__/enforce-statement-order.test.js +0 -473
  113. package/eslint-plugin-code-organization/index.js +0 -28
  114. package/eslint-plugin-code-organization/package.json +0 -10
  115. package/eslint-plugin-code-organization/rules/enforce-statement-order.js +0 -162
  116. package/expo/copy-overwrite/.claude/agents/ops-specialist.md +0 -124
  117. package/expo/copy-overwrite/.claude/rules/expo-verification.md +0 -261
  118. package/expo/copy-overwrite/.claude/skills/apollo-client/SKILL.md +0 -238
  119. package/expo/copy-overwrite/.claude/skills/apollo-client/references/mutation-patterns.md +0 -360
  120. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/SKILL.md +0 -360
  121. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/atomic-levels.md +0 -417
  122. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/folder-structure.md +0 -257
  123. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/gluestack-mapping.md +0 -233
  124. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/scripts/validate_atomic_structure.py +0 -329
  125. package/expo/copy-overwrite/.claude/skills/container-view-pattern/SKILL.md +0 -299
  126. package/expo/copy-overwrite/.claude/skills/container-view-pattern/references/examples.md +0 -749
  127. package/expo/copy-overwrite/.claude/skills/container-view-pattern/references/patterns.md +0 -318
  128. package/expo/copy-overwrite/.claude/skills/container-view-pattern/scripts/create_component.py +0 -200
  129. package/expo/copy-overwrite/.claude/skills/container-view-pattern/scripts/validate_component.py +0 -209
  130. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/SKILL.md +0 -268
  131. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/common-issues.md +0 -619
  132. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/file-extensions.md +0 -340
  133. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/platform-api.md +0 -276
  134. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/scripts/validate_cross_platform.py +0 -416
  135. package/expo/copy-overwrite/.claude/skills/directory-structure/SKILL.md +0 -202
  136. package/expo/copy-overwrite/.claude/skills/directory-structure/scripts/validate_structure.py +0 -445
  137. package/expo/copy-overwrite/.claude/skills/expo-env-config/SKILL.md +0 -309
  138. package/expo/copy-overwrite/.claude/skills/expo-env-config/references/validation-patterns.md +0 -417
  139. package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/SKILL.md +0 -431
  140. package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/references/official-docs.md +0 -290
  141. package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/scripts/generate-route.py +0 -171
  142. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/SKILL.md +0 -411
  143. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/color-tokens.md +0 -343
  144. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/component-mapping.md +0 -307
  145. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/spacing-scale.md +0 -300
  146. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/scripts/validate_styling.py +0 -315
  147. package/expo/copy-overwrite/.claude/skills/local-state/SKILL.md +0 -362
  148. package/expo/copy-overwrite/.claude/skills/local-state/references/async-storage.md +0 -505
  149. package/expo/copy-overwrite/.claude/skills/local-state/references/persistence-patterns.md +0 -711
  150. package/expo/copy-overwrite/.claude/skills/local-state/references/reactive-variables.md +0 -446
  151. package/expo/copy-overwrite/.claude/skills/ops-browser-uat/SKILL.md +0 -124
  152. package/expo/copy-overwrite/.claude/skills/ops-check-logs/SKILL.md +0 -211
  153. package/expo/copy-overwrite/.claude/skills/ops-db-ops/SKILL.md +0 -119
  154. package/expo/copy-overwrite/.claude/skills/ops-deploy/SKILL.md +0 -119
  155. package/expo/copy-overwrite/.claude/skills/ops-monitor-errors/SKILL.md +0 -99
  156. package/expo/copy-overwrite/.claude/skills/ops-performance/SKILL.md +0 -165
  157. package/expo/copy-overwrite/.claude/skills/ops-run-local/SKILL.md +0 -166
  158. package/expo/copy-overwrite/.claude/skills/ops-verify-health/SKILL.md +0 -101
  159. package/expo/copy-overwrite/.claude/skills/owasp-zap/SKILL.md +0 -56
  160. package/expo/copy-overwrite/.claude/skills/playwright-selectors/SKILL.md +0 -223
  161. package/expo/copy-overwrite/.claude/skills/testing-library/SKILL.md +0 -314
  162. package/expo/copy-overwrite/.claude/skills/testing-library/references/async-patterns.md +0 -420
  163. package/expo/copy-overwrite/.claude/skills/testing-library/references/expo-router-testing.md +0 -556
  164. package/expo/copy-overwrite/.claude/skills/testing-library/references/mocking-patterns.md +0 -590
  165. package/expo/copy-overwrite/.claude/skills/testing-library/references/query-priority.md +0 -291
  166. package/expo/copy-overwrite/eslint-plugin-component-structure/README.md +0 -234
  167. package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/plugin-index.test.js +0 -89
  168. package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/require-memo-in-view.test.js +0 -201
  169. package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/single-component-per-file.test.js +0 -294
  170. package/expo/copy-overwrite/eslint-plugin-component-structure/index.js +0 -37
  171. package/expo/copy-overwrite/eslint-plugin-component-structure/package.json +0 -10
  172. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/enforce-component-structure.js +0 -235
  173. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/no-return-in-view.js +0 -96
  174. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/require-memo-in-view.js +0 -183
  175. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/single-component-per-file.js +0 -243
  176. package/expo/copy-overwrite/eslint-plugin-ui-standards/README.md +0 -192
  177. package/expo/copy-overwrite/eslint-plugin-ui-standards/index.js +0 -31
  178. package/expo/copy-overwrite/eslint-plugin-ui-standards/package.json +0 -10
  179. package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-classname-outside-ui.js +0 -56
  180. package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-direct-rn-imports.js +0 -60
  181. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/SKILL.md +0 -176
  182. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/advanced-features.md +0 -527
  183. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/project-patterns.md +0 -483
  184. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/quick-start.md +0 -257
  185. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/resolvers-mutations.md +0 -413
  186. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/types-scalars.md +0 -513
  187. package/nestjs/copy-overwrite/.claude/skills/nestjs-rules/SKILL.md +0 -536
  188. package/nestjs/copy-overwrite/.claude/skills/security-zap-scan/SKILL.md +0 -33
  189. package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/SKILL.md +0 -275
  190. package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/configuration-patterns.md +0 -487
  191. package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/entity-patterns.md +0 -450
  192. package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/observability-patterns.md +0 -536
  193. package/rails/copy-overwrite/.claude/skills/action-controller-best-practices/SKILL.md +0 -374
  194. package/rails/copy-overwrite/.claude/skills/action-view-best-practices/SKILL.md +0 -335
  195. package/rails/copy-overwrite/.claude/skills/active-record-model-best-practices/SKILL.md +0 -166
  196. package/rails/copy-overwrite/.claude/skills/plan-add-test-coverage/SKILL.md +0 -45
  197. package/rails/copy-overwrite/.claude/skills/plan-fix-linter-error/SKILL.md +0 -45
  198. package/rails/copy-overwrite/.claude/skills/plan-lower-code-complexity/SKILL.md +0 -48
  199. package/rails/copy-overwrite/.claude/skills/plan-reduce-max-lines/SKILL.md +0 -46
  200. package/rails/copy-overwrite/.claude/skills/plan-reduce-max-lines-per-function/SKILL.md +0 -46
  201. package/typescript/copy-overwrite/.claude/hooks/format-on-edit.sh +0 -76
  202. package/typescript/copy-overwrite/.claude/hooks/install-pkgs.sh +0 -64
  203. package/typescript/copy-overwrite/.claude/hooks/lint-on-edit.sh +0 -81
  204. package/typescript/copy-overwrite/.claude/hooks/sg-scan-on-edit.sh +0 -68
  205. package/typescript/copy-overwrite/.claude/skills/jsdoc-best-practices/SKILL.md +0 -432
  206. package/typescript/copy-overwrite/eslint-plugin-code-organization/README.md +0 -149
  207. package/typescript/copy-overwrite/eslint-plugin-code-organization/__tests__/enforce-statement-order.test.js +0 -473
  208. package/typescript/copy-overwrite/eslint-plugin-code-organization/index.js +0 -28
  209. package/typescript/copy-overwrite/eslint-plugin-code-organization/package.json +0 -10
  210. 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
- };