@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,318 +0,0 @@
1
- # Container/View Pattern - Common Patterns and Anti-Patterns
2
-
3
- ## Correct Patterns
4
-
5
- ### 1. State in Container, Props in View
6
-
7
- ```tsx
8
- // Container - owns state
9
- const PlayerCardContainer = ({ playerId }: Props) => {
10
- const [isExpanded, setIsExpanded] = useState(false);
11
- const { data } = usePlayerQuery({ variables: { id: playerId } });
12
-
13
- const handleToggle = useCallback(() => {
14
- setIsExpanded(prev => !prev);
15
- }, []);
16
-
17
- return (
18
- <PlayerCardView
19
- player={data?.player}
20
- isExpanded={isExpanded}
21
- onToggle={handleToggle}
22
- />
23
- );
24
- };
25
-
26
- // View - receives props only
27
- const PlayerCardView = ({ player, isExpanded, onToggle }: ViewProps) => (
28
- <Box>
29
- <Pressable onPress={onToggle}>
30
- <Text>{player?.name}</Text>
31
- </Pressable>
32
- {isExpanded && <PlayerDetails player={player} />}
33
- </Box>
34
- );
35
- ```
36
-
37
- ### 2. Memoized Computed Values
38
-
39
- ```tsx
40
- // Container - memoize all derived data
41
- const ListContainer = ({ items }: Props) => {
42
- const sortedItems = useMemo(
43
- () => [...items].sort((a, b) => a.name.localeCompare(b.name)),
44
- [items]
45
- );
46
-
47
- const itemCount = useMemo(() => items.length, [items]);
48
-
49
- return <ListView items={sortedItems} count={itemCount} />;
50
- };
51
- ```
52
-
53
- ### 3. Memoized Callbacks
54
-
55
- ```tsx
56
- // Container - wrap all handlers
57
- const FormContainer = ({ onSubmit }: Props) => {
58
- const [value, setValue] = useState("");
59
-
60
- const handleChange = useCallback((text: string) => {
61
- setValue(text);
62
- }, []);
63
-
64
- const handleSubmit = useCallback(() => {
65
- onSubmit(value);
66
- }, [onSubmit, value]);
67
-
68
- return (
69
- <FormView value={value} onChange={handleChange} onSubmit={handleSubmit} />
70
- );
71
- };
72
- ```
73
-
74
- ### 4. Loading/Error/Empty States
75
-
76
- ```tsx
77
- // Container - determines which state to show
78
- const DataListContainer = () => {
79
- const { data, loading, error } = useDataQuery();
80
-
81
- const isEmpty = useMemo(
82
- () => !loading && !error && (!data?.items || data.items.length === 0),
83
- [loading, error, data?.items]
84
- );
85
-
86
- return (
87
- <DataListView
88
- items={data?.items ?? []}
89
- isLoading={loading}
90
- hasError={!!error}
91
- isEmpty={isEmpty}
92
- />
93
- );
94
- };
95
-
96
- // View - renders based on state props
97
- const DataListView = ({ items, isLoading, hasError, isEmpty }: Props) => (
98
- <Box>
99
- {isLoading && <LoadingSpinner />}
100
- {hasError && <ErrorMessage />}
101
- {isEmpty && <EmptyState />}
102
- {!isLoading && !hasError && !isEmpty && (
103
- <FlashList data={items} renderItem={renderItem} />
104
- )}
105
- </Box>
106
- );
107
- ```
108
-
109
- ### 5. Safe Area Insets
110
-
111
- ```tsx
112
- // Container - gets insets from hook
113
- const ScreenContainer = () => {
114
- const { bottom: bottomInset } = useSafeAreaInsets();
115
-
116
- return <ScreenView bottomInset={bottomInset} />;
117
- };
118
-
119
- // View - applies inset as style
120
- const ScreenView = ({ bottomInset }: { readonly bottomInset: number }) => (
121
- <Box style={{ paddingBottom: bottomInset }}>
122
- <Content />
123
- </Box>
124
- );
125
- ```
126
-
127
- ### 6. Platform-Specific Logic
128
-
129
- ```tsx
130
- // Container - handles platform logic
131
- const ActionContainer = ({ onDelete }: Props) => {
132
- const handleDelete = useCallback(() => {
133
- if (Platform.OS === "web") {
134
- if (window.confirm("Delete this item?")) {
135
- onDelete();
136
- }
137
- } else {
138
- Alert.alert("Confirm", "Delete this item?", [
139
- { text: "Cancel", style: "cancel" },
140
- { text: "Delete", onPress: onDelete, style: "destructive" },
141
- ]);
142
- }
143
- }, [onDelete]);
144
-
145
- return <ActionView onDelete={handleDelete} />;
146
- };
147
- ```
148
-
149
- ## Anti-Patterns to Avoid
150
-
151
- ### 1. Hooks in View
152
-
153
- ```tsx
154
- // WRONG - hooks in View
155
- const BadView = ({ playerId }: Props) => {
156
- const { data } = usePlayerQuery({ variables: { id: playerId } }); // WRONG
157
- const [isOpen, setIsOpen] = useState(false); // WRONG
158
-
159
- return <Box>{data?.name}</Box>;
160
- };
161
-
162
- // CORRECT - hooks in Container
163
- const GoodContainer = ({ playerId }: Props) => {
164
- const { data } = usePlayerQuery({ variables: { id: playerId } });
165
- const [isOpen, setIsOpen] = useState(false);
166
-
167
- return <GoodView player={data} isOpen={isOpen} />;
168
- };
169
- ```
170
-
171
- ### 2. Block Body in View
172
-
173
- ```tsx
174
- // WRONG - block body with return
175
- const BadView = ({ items }: Props) => {
176
- return (
177
- <Box>
178
- {items.map(item => (
179
- <Item key={item.id} />
180
- ))}
181
- </Box>
182
- );
183
- };
184
-
185
- // CORRECT - arrow shorthand
186
- const GoodView = ({ items }: Props) => (
187
- <Box>
188
- {items.map(item => (
189
- <Item key={item.id} />
190
- ))}
191
- </Box>
192
- );
193
- ```
194
-
195
- ### 3. Inline Functions
196
-
197
- ```tsx
198
- // WRONG - inline function creates new reference
199
- const BadContainer = () => (
200
- <View onClick={() => console.log("clicked")} /> // WRONG
201
- );
202
-
203
- // CORRECT - memoized callback
204
- const GoodContainer = () => {
205
- const handleClick = useCallback(() => {
206
- console.log("clicked");
207
- }, []);
208
-
209
- return <View onClick={handleClick} />;
210
- };
211
- ```
212
-
213
- ### 4. Inline Objects
214
-
215
- ```tsx
216
- // WRONG - inline object creates new reference
217
- const BadContainer = ({ user }: Props) => (
218
- <View style={{ padding: 10 }} user={{ name: user.name }} /> // WRONG
219
- );
220
-
221
- // CORRECT - memoized values
222
- const GoodContainer = ({ user }: Props) => {
223
- const style = useMemo(() => ({ padding: 10 }), []);
224
- const userData = useMemo(() => ({ name: user.name }), [user.name]);
225
-
226
- return <View style={style} user={userData} />;
227
- };
228
- ```
229
-
230
- ### 5. Logic in View
231
-
232
- ```tsx
233
- // WRONG - logic in View
234
- const BadView = ({ items, filter }: Props) => {
235
- const filtered = items.filter(i => i.type === filter); // WRONG
236
-
237
- return <List items={filtered} />;
238
- };
239
-
240
- // CORRECT - pre-filtered in Container
241
- const GoodContainer = ({ items, filter }: Props) => {
242
- const filtered = useMemo(
243
- () => items.filter(i => i.type === filter),
244
- [items, filter]
245
- );
246
-
247
- return <GoodView items={filtered} />;
248
- };
249
- ```
250
-
251
- ### 6. Missing memo Wrapper
252
-
253
- ```tsx
254
- // WRONG - no memo
255
- const BadView = ({ data }: Props) => <Box>{data}</Box>;
256
- export default BadView;
257
-
258
- // CORRECT - wrapped with memo
259
- const GoodView = ({ data }: Props) => <Box>{data}</Box>;
260
- GoodView.displayName = "GoodView";
261
- export default memo(GoodView);
262
- ```
263
-
264
- ### 7. Early Returns in View
265
-
266
- ```tsx
267
- // WRONG - early return in View
268
- const BadView = ({ data, isLoading }: Props) => {
269
- if (isLoading) return <Spinner />;
270
- if (!data) return <Empty />;
271
- return <Content data={data} />;
272
- };
273
-
274
- // CORRECT - ternary in View (or handle in Container)
275
- const GoodView = ({ data, isLoading, isEmpty }: Props) => (
276
- <Box>
277
- {isLoading ? <Spinner /> : isEmpty ? <Empty /> : <Content data={data} />}
278
- </Box>
279
- );
280
- ```
281
-
282
- ## Complex View Extraction
283
-
284
- When Views become too complex, extract helper functions:
285
-
286
- ```tsx
287
- /**
288
- * Renders loading state skeleton.
289
- * @param props - Helper props
290
- * @param props.isDark - Dark mode flag
291
- */
292
- function renderLoading(props: { readonly isDark: boolean }) {
293
- return <Skeleton className={props.isDark ? "bg-gray-800" : "bg-gray-200"} />;
294
- }
295
-
296
- /**
297
- * Renders empty state message.
298
- * @param props - Helper props
299
- * @param props.message - Message to display
300
- */
301
- function renderEmpty(props: { readonly message: string }) {
302
- return (
303
- <Box className="items-center justify-center p-8">
304
- <Text>{props.message}</Text>
305
- </Box>
306
- );
307
- }
308
-
309
- const ComplexView = ({ isLoading, isEmpty, isDark, items }: Props) => (
310
- <Box>
311
- {isLoading && renderLoading({ isDark })}
312
- {!isLoading && isEmpty && renderEmpty({ message: "No items found" })}
313
- {!isLoading && !isEmpty && (
314
- <FlashList data={items} renderItem={renderItem} />
315
- )}
316
- </Box>
317
- );
318
- ```
@@ -1,200 +0,0 @@
1
- #!/usr/bin/env python3
2
- # This file is managed by Lisa.
3
- # Do not edit directly — changes will be overwritten on the next `lisa` run.
4
- """
5
- Create a new component following the Container/View pattern.
6
-
7
- Usage:
8
- python3 create_component.py <type> <name> [feature]
9
-
10
- Types:
11
- global-component - Creates in components/<name>/
12
- feature-component - Creates in features/<feature>/components/<name>/
13
- global-screen - Creates in screens/<name>/
14
- feature-screen - Creates in features/<feature>/screens/<name>/
15
-
16
- Examples:
17
- python3 create_component.py global-component PlayerCard
18
- python3 create_component.py feature-component PlayerCard player-kanban
19
- python3 create_component.py global-screen Settings
20
- python3 create_component.py feature-screen Main dashboard
21
- """
22
-
23
- import os
24
- import sys
25
- from pathlib import Path
26
-
27
-
28
- CONTAINER_TEMPLATE = '''import {{ useCallback, useMemo, useState }} from "react";
29
-
30
- import {name}View from "./{name}View";
31
-
32
- /**
33
- * Props for the {name} component.
34
- */
35
- interface {name}Props {{
36
- // Define props here
37
- }}
38
-
39
- /**
40
- * Container component that manages state and logic for {name}.
41
- * @param props - Component properties
42
- */
43
- const {name}Container = (props: {name}Props) => {{
44
- // 1. Variables, state, useMemo, useCallback
45
- const [isLoading, setIsLoading] = useState(false);
46
-
47
- const computedValue = useMemo(() => {{
48
- return null;
49
- }}, []);
50
-
51
- const handleAction = useCallback(() => {{
52
- // Handle action
53
- }}, []);
54
-
55
- // 2. useEffect hooks
56
- // useEffect(() => {{}}, []);
57
-
58
- // 3. Return View
59
- return (
60
- <{name}View
61
- isLoading={{isLoading}}
62
- onAction={{handleAction}}
63
- />
64
- );
65
- }};
66
-
67
- export default {name}Container;
68
- '''
69
-
70
- VIEW_TEMPLATE = '''import {{ memo }} from "react";
71
-
72
- import {{ Box }} from "@/components/ui/box";
73
- import {{ Text }} from "@/components/ui/text";
74
-
75
- /**
76
- * Props for the {name}View component.
77
- */
78
- interface {name}ViewProps {{
79
- readonly isLoading: boolean;
80
- readonly onAction: () => void;
81
- }}
82
-
83
- /**
84
- * View component that renders the {name} UI.
85
- * @param props - Component properties
86
- * @param props.isLoading - Loading state indicator
87
- * @param props.onAction - Action handler callback
88
- */
89
- const {name}View = ({{
90
- isLoading,
91
- onAction,
92
- }}: {name}ViewProps) => (
93
- <Box testID="{test_id}.CONTAINER">
94
- {{isLoading ? (
95
- <Text>Loading...</Text>
96
- ) : (
97
- <Text>{name} Component</Text>
98
- )}}
99
- </Box>
100
- );
101
-
102
- {name}View.displayName = "{name}View";
103
-
104
- export default memo({name}View);
105
- '''
106
-
107
- INDEX_TEMPLATE = '''export {{ default }} from "./{name}Container";
108
- '''
109
-
110
-
111
- def create_component(component_type: str, name: str, feature: str = None) -> bool:
112
- """
113
- Create a new component with Container/View pattern.
114
-
115
- Args:
116
- component_type: Type of component (global-component, feature-component, etc.)
117
- name: Component name in PascalCase
118
- feature: Feature name (required for feature-component and feature-screen)
119
-
120
- Returns:
121
- True if successful, False otherwise
122
- """
123
- # Validate component name is PascalCase
124
- if not name[0].isupper():
125
- print(f"Error: Component name '{name}' must be PascalCase (start with uppercase)")
126
- return False
127
-
128
- # Determine target directory
129
- if component_type == "global-component":
130
- target_dir = Path("components") / name
131
- test_id = name.upper()
132
- elif component_type == "feature-component":
133
- if not feature:
134
- print("Error: feature-component requires a feature name")
135
- return False
136
- feature_dir = Path("features") / feature
137
- if not feature_dir.exists():
138
- print(f"Error: Feature '{feature}' does not exist at {feature_dir}")
139
- return False
140
- target_dir = feature_dir / "components" / name
141
- test_id = f"{feature.upper().replace('-', '_')}.{name.upper()}"
142
- elif component_type == "global-screen":
143
- target_dir = Path("screens") / name
144
- test_id = f"SCREEN.{name.upper()}"
145
- elif component_type == "feature-screen":
146
- if not feature:
147
- print("Error: feature-screen requires a feature name")
148
- return False
149
- feature_dir = Path("features") / feature
150
- if not feature_dir.exists():
151
- print(f"Error: Feature '{feature}' does not exist at {feature_dir}")
152
- return False
153
- target_dir = feature_dir / "screens" / name
154
- test_id = f"{feature.upper().replace('-', '_')}.SCREEN.{name.upper()}"
155
- else:
156
- print(f"Error: Unknown component type '{component_type}'")
157
- print("Valid types: global-component, feature-component, global-screen, feature-screen")
158
- return False
159
-
160
- # Check if component already exists
161
- if target_dir.exists():
162
- print(f"Error: Component already exists at {target_dir}")
163
- return False
164
-
165
- # Create directory
166
- target_dir.mkdir(parents=True, exist_ok=True)
167
-
168
- # Create files
169
- container_content = CONTAINER_TEMPLATE.format(name=name)
170
- view_content = VIEW_TEMPLATE.format(name=name, test_id=test_id)
171
- index_content = INDEX_TEMPLATE.format(name=name)
172
-
173
- (target_dir / f"{name}Container.tsx").write_text(container_content)
174
- (target_dir / f"{name}View.tsx").write_text(view_content)
175
- (target_dir / "index.tsx").write_text(index_content)
176
-
177
- print(f"Created component at {target_dir}/")
178
- print(f" - {name}Container.tsx")
179
- print(f" - {name}View.tsx")
180
- print(f" - index.tsx")
181
-
182
- return True
183
-
184
-
185
- def main():
186
- """Main entry point."""
187
- if len(sys.argv) < 3:
188
- print(__doc__)
189
- sys.exit(1)
190
-
191
- component_type = sys.argv[1]
192
- name = sys.argv[2]
193
- feature = sys.argv[3] if len(sys.argv) > 3 else None
194
-
195
- success = create_component(component_type, name, feature)
196
- sys.exit(0 if success else 1)
197
-
198
-
199
- if __name__ == "__main__":
200
- main()