ima-claude 2.9.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 (182) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +463 -0
  3. package/dist/cli.js +1064 -0
  4. package/package.json +49 -0
  5. package/platforms/claude/adapter.ts +115 -0
  6. package/platforms/junie/adapter.ts +254 -0
  7. package/platforms/junie/agents-template.md +113 -0
  8. package/platforms/junie/hook-translations.md +84 -0
  9. package/platforms/shared/detector.ts +27 -0
  10. package/platforms/shared/installer.ts +202 -0
  11. package/platforms/shared/types.ts +78 -0
  12. package/plugins/ima-claude/.claude-plugin/plugin.json +25 -0
  13. package/plugins/ima-claude/agents/explorer.md +30 -0
  14. package/plugins/ima-claude/agents/implementer.md +30 -0
  15. package/plugins/ima-claude/agents/memory.md +42 -0
  16. package/plugins/ima-claude/agents/reviewer.md +53 -0
  17. package/plugins/ima-claude/agents/tester.md +33 -0
  18. package/plugins/ima-claude/agents/wp-developer.md +46 -0
  19. package/plugins/ima-claude/hooks/README.md +145 -0
  20. package/plugins/ima-claude/hooks/atlassian_prereqs.py +112 -0
  21. package/plugins/ima-claude/hooks/block_sed_edits.py +59 -0
  22. package/plugins/ima-claude/hooks/bootstrap.sh +90 -0
  23. package/plugins/ima-claude/hooks/bootstrap_utility_check.py +94 -0
  24. package/plugins/ima-claude/hooks/composer_autoload_check.py +70 -0
  25. package/plugins/ima-claude/hooks/docs_organization.py +104 -0
  26. package/plugins/ima-claude/hooks/enforce_rg_over_grep.py +56 -0
  27. package/plugins/ima-claude/hooks/fp_utility_check.py +90 -0
  28. package/plugins/ima-claude/hooks/hook_logger.py +69 -0
  29. package/plugins/ima-claude/hooks/hooks.json +239 -0
  30. package/plugins/ima-claude/hooks/jira_issue_fetch.py +79 -0
  31. package/plugins/ima-claude/hooks/jquery_in_wordpress.py +92 -0
  32. package/plugins/ima-claude/hooks/memory_bootstrap.py +79 -0
  33. package/plugins/ima-claude/hooks/memory_store_reminder.py +75 -0
  34. package/plugins/ima-claude/hooks/prompt_coach.py +125 -0
  35. package/plugins/ima-claude/hooks/prompt_coach_digest.md +48 -0
  36. package/plugins/ima-claude/hooks/prompt_coach_system.md +30 -0
  37. package/plugins/ima-claude/hooks/sequential_thinking_check.py +81 -0
  38. package/plugins/ima-claude/hooks/serena_over_grep.py +96 -0
  39. package/plugins/ima-claude/hooks/serena_over_read.py +66 -0
  40. package/plugins/ima-claude/hooks/serena_project_check.py +133 -0
  41. package/plugins/ima-claude/hooks/sql_injection_check.py +73 -0
  42. package/plugins/ima-claude/hooks/task_master_after_plan.py +31 -0
  43. package/plugins/ima-claude/hooks/task_master_before_impl.py +93 -0
  44. package/plugins/ima-claude/hooks/tavily_extract_advanced.py +48 -0
  45. package/plugins/ima-claude/hooks/vestige_before_external.py +86 -0
  46. package/plugins/ima-claude/hooks/webfetch_to_tavily.py +42 -0
  47. package/plugins/ima-claude/hooks/websearch_to_tavily.py +41 -0
  48. package/plugins/ima-claude/hooks/wp_security_check.py +150 -0
  49. package/plugins/ima-claude/personalities/README.md +45 -0
  50. package/plugins/ima-claude/personalities/enable-40k.md +69 -0
  51. package/plugins/ima-claude/personalities/enable-templars.md +69 -0
  52. package/plugins/ima-claude/skills/.research-summary.md +340 -0
  53. package/plugins/ima-claude/skills/architect/SKILL.md +304 -0
  54. package/plugins/ima-claude/skills/compound-bridge/SKILL.md +200 -0
  55. package/plugins/ima-claude/skills/discourse/SKILL.md +440 -0
  56. package/plugins/ima-claude/skills/discourse-admin/SKILL.md +192 -0
  57. package/plugins/ima-claude/skills/discourse-admin/references/api-endpoints.md +441 -0
  58. package/plugins/ima-claude/skills/discourse-admin/references/gotchas.md +107 -0
  59. package/plugins/ima-claude/skills/discourse-admin/references/staging-defaults.md +98 -0
  60. package/plugins/ima-claude/skills/discourse-admin/scripts/discourse-admin.py +319 -0
  61. package/plugins/ima-claude/skills/docs-organize/SKILL.md +254 -0
  62. package/plugins/ima-claude/skills/docs-organize/templates/active-README.md +50 -0
  63. package/plugins/ima-claude/skills/docs-organize/templates/archive-README.md +57 -0
  64. package/plugins/ima-claude/skills/docs-organize/templates/docs-README.md +43 -0
  65. package/plugins/ima-claude/skills/docs-organize/templates/phase-archive-README.md +83 -0
  66. package/plugins/ima-claude/skills/docs-organize/templates/section-README.md +48 -0
  67. package/plugins/ima-claude/skills/docs-organize/templates/transient-README.md +79 -0
  68. package/plugins/ima-claude/skills/docs-organize/templates/transient-gitignore +9 -0
  69. package/plugins/ima-claude/skills/ember-discourse/SKILL.md +496 -0
  70. package/plugins/ima-claude/skills/functional-programmer/SKILL.md +258 -0
  71. package/plugins/ima-claude/skills/ima-bootstrap/SKILL.md +278 -0
  72. package/plugins/ima-claude/skills/ima-bootstrap/references/bootstrap-patterns.md +356 -0
  73. package/plugins/ima-claude/skills/ima-bootstrap/references/ima-brand.md +273 -0
  74. package/plugins/ima-claude/skills/ima-bootstrap/references/theme-integration.md +212 -0
  75. package/plugins/ima-claude/skills/ima-brand/SKILL.md +108 -0
  76. package/plugins/ima-claude/skills/ima-brand/references/brand-identity.md +140 -0
  77. package/plugins/ima-claude/skills/ima-brand/references/digital-standards.md +180 -0
  78. package/plugins/ima-claude/skills/ima-brand/references/visual-system.md +173 -0
  79. package/plugins/ima-claude/skills/ima-forms-expert/SKILL.md +175 -0
  80. package/plugins/ima-claude/skills/ima-forms-expert/references/container-components.md +154 -0
  81. package/plugins/ima-claude/skills/ima-forms-expert/references/examples.md +328 -0
  82. package/plugins/ima-claude/skills/ima-forms-expert/references/field-components.md +298 -0
  83. package/plugins/ima-claude/skills/ima-forms-expert/references/form-factory.md +193 -0
  84. package/plugins/ima-claude/skills/ima-forms-expert/references/quick-reference.md +153 -0
  85. package/plugins/ima-claude/skills/ima-forms-expert/references/validation-engine.md +336 -0
  86. package/plugins/ima-claude/skills/jira-checkpoint/SKILL.md +178 -0
  87. package/plugins/ima-claude/skills/jquery/SKILL.md +413 -0
  88. package/plugins/ima-claude/skills/js-fp/SKILL.md +463 -0
  89. package/plugins/ima-claude/skills/js-fp/core-principles.md +487 -0
  90. package/plugins/ima-claude/skills/js-fp/examples/pure-functions.js +260 -0
  91. package/plugins/ima-claude/skills/js-fp/examples/tests/pure-functions.test.js +262 -0
  92. package/plugins/ima-claude/skills/js-fp/references/anti-patterns.md +120 -0
  93. package/plugins/ima-claude/skills/js-fp/references/performance-patterns.md +116 -0
  94. package/plugins/ima-claude/skills/js-fp/references/testing-patterns.md +134 -0
  95. package/plugins/ima-claude/skills/js-fp-api/SKILL.md +280 -0
  96. package/plugins/ima-claude/skills/js-fp-api/examples/crud-endpoint.js +258 -0
  97. package/plugins/ima-claude/skills/js-fp-api/references/middleware-patterns.md +134 -0
  98. package/plugins/ima-claude/skills/js-fp-api/references/security-sql.md +110 -0
  99. package/plugins/ima-claude/skills/js-fp-api/references/validation-patterns.md +165 -0
  100. package/plugins/ima-claude/skills/js-fp-react/SKILL.md +447 -0
  101. package/plugins/ima-claude/skills/js-fp-react/examples/ProductCard.tsx +65 -0
  102. package/plugins/ima-claude/skills/js-fp-react/references/hooks-advanced.md +136 -0
  103. package/plugins/ima-claude/skills/js-fp-react/references/performance-patterns.md +175 -0
  104. package/plugins/ima-claude/skills/js-fp-vue/SKILL.md +322 -0
  105. package/plugins/ima-claude/skills/js-fp-vue/references/complete-examples.md +397 -0
  106. package/plugins/ima-claude/skills/js-fp-vue/references/composables-advanced.md +282 -0
  107. package/plugins/ima-claude/skills/js-fp-vue/references/reactivity-patterns.md +348 -0
  108. package/plugins/ima-claude/skills/js-fp-vue/references/testing.md +314 -0
  109. package/plugins/ima-claude/skills/js-fp-wordpress/SKILL.md +301 -0
  110. package/plugins/ima-claude/skills/js-fp-wordpress/references/ajax-patterns.md +192 -0
  111. package/plugins/ima-claude/skills/js-fp-wordpress/references/event-patterns.md +136 -0
  112. package/plugins/ima-claude/skills/js-fp-wordpress/references/wp-integration.md +248 -0
  113. package/plugins/ima-claude/skills/livecanvas/SKILL.md +209 -0
  114. package/plugins/ima-claude/skills/livecanvas/references/livecanvas-features.md +311 -0
  115. package/plugins/ima-claude/skills/livecanvas/references/loops-and-logic.md +730 -0
  116. package/plugins/ima-claude/skills/livecanvas/references/picostrap.md +227 -0
  117. package/plugins/ima-claude/skills/mcp-atlassian/SKILL.md +339 -0
  118. package/plugins/ima-claude/skills/mcp-context7/SKILL.md +109 -0
  119. package/plugins/ima-claude/skills/mcp-memory/SKILL.md +182 -0
  120. package/plugins/ima-claude/skills/mcp-qdrant/SKILL.md +233 -0
  121. package/plugins/ima-claude/skills/mcp-sequential/SKILL.md +149 -0
  122. package/plugins/ima-claude/skills/mcp-serena/SKILL.md +174 -0
  123. package/plugins/ima-claude/skills/mcp-tavily/SKILL.md +118 -0
  124. package/plugins/ima-claude/skills/mcp-vestige/SKILL.md +259 -0
  125. package/plugins/ima-claude/skills/php-authnet/SKILL.md +275 -0
  126. package/plugins/ima-claude/skills/php-authnet/references/api-reference.md +624 -0
  127. package/plugins/ima-claude/skills/php-authnet/references/sandbox-testing.md +424 -0
  128. package/plugins/ima-claude/skills/php-fp/SKILL.md +333 -0
  129. package/plugins/ima-claude/skills/php-fp/examples/pure-functions.php +403 -0
  130. package/plugins/ima-claude/skills/php-fp/examples/tests/PureFunctionsTest.php +515 -0
  131. package/plugins/ima-claude/skills/php-fp/references/core-principles.md +277 -0
  132. package/plugins/ima-claude/skills/php-fp/references/testing-patterns.md +374 -0
  133. package/plugins/ima-claude/skills/php-fp-wordpress/SKILL.md +216 -0
  134. package/plugins/ima-claude/skills/php-fp-wordpress/references/fp-patterns.md +275 -0
  135. package/plugins/ima-claude/skills/php-fp-wordpress/references/plugin-architecture.md +295 -0
  136. package/plugins/ima-claude/skills/php-fp-wordpress/references/security-examples.md +203 -0
  137. package/plugins/ima-claude/skills/php-fp-wordpress/references/testing-strategy.md +259 -0
  138. package/plugins/ima-claude/skills/phpunit-wp/SKILL.md +716 -0
  139. package/plugins/ima-claude/skills/playwright/SKILL.md +434 -0
  140. package/plugins/ima-claude/skills/playwright/references/accessibility-testing.md +153 -0
  141. package/plugins/ima-claude/skills/playwright/references/ci-cd.md +268 -0
  142. package/plugins/ima-claude/skills/playwright/references/network-mocking.md +270 -0
  143. package/plugins/ima-claude/skills/playwright/references/visual-regression.md +215 -0
  144. package/plugins/ima-claude/skills/py-fp/SKILL.md +663 -0
  145. package/plugins/ima-claude/skills/py-fp/examples/pure-functions.py +185 -0
  146. package/plugins/ima-claude/skills/py-fp/examples/tests/test_pure_functions.py +244 -0
  147. package/plugins/ima-claude/skills/py-fp/references/core-principles.md +381 -0
  148. package/plugins/ima-claude/skills/py-fp/references/testing-patterns.md +283 -0
  149. package/plugins/ima-claude/skills/quasar-fp/SKILL.md +327 -0
  150. package/plugins/ima-claude/skills/quasar-fp/metadata.json +85 -0
  151. package/plugins/ima-claude/skills/quasar-fp/references/component-patterns.md +257 -0
  152. package/plugins/ima-claude/skills/quasar-fp/references/theme-integration.md +233 -0
  153. package/plugins/ima-claude/skills/quasar-fp/references/utility-classes.md +237 -0
  154. package/plugins/ima-claude/skills/quickstart/SKILL.md +129 -0
  155. package/plugins/ima-claude/skills/rails/SKILL.md +359 -0
  156. package/plugins/ima-claude/skills/resume-session/SKILL.md +68 -0
  157. package/plugins/ima-claude/skills/rg/SKILL.md +205 -0
  158. package/plugins/ima-claude/skills/ruby-fp/SKILL.md +336 -0
  159. package/plugins/ima-claude/skills/save-session/SKILL.md +81 -0
  160. package/plugins/ima-claude/skills/scorecard/SKILL.md +96 -0
  161. package/plugins/ima-claude/skills/skill-analyzer/SKILL.md +127 -0
  162. package/plugins/ima-claude/skills/skill-analyzer/references/advanced-checklist.md +44 -0
  163. package/plugins/ima-claude/skills/skill-analyzer/references/core-checklist.md +60 -0
  164. package/plugins/ima-claude/skills/skill-analyzer/scripts/analyze_skill.py +418 -0
  165. package/plugins/ima-claude/skills/skill-creator/LICENSE.txt +202 -0
  166. package/plugins/ima-claude/skills/skill-creator/SKILL.md +343 -0
  167. package/plugins/ima-claude/skills/skill-creator/references/output-patterns.md +82 -0
  168. package/plugins/ima-claude/skills/skill-creator/references/workflows.md +28 -0
  169. package/plugins/ima-claude/skills/skill-creator/scripts/init_skill.py +303 -0
  170. package/plugins/ima-claude/skills/skill-creator/scripts/package_skill.py +110 -0
  171. package/plugins/ima-claude/skills/skill-creator/scripts/quick_validate.py +103 -0
  172. package/plugins/ima-claude/skills/task-master/SKILL.md +51 -0
  173. package/plugins/ima-claude/skills/task-planner/SKILL.md +228 -0
  174. package/plugins/ima-claude/skills/task-runner/SKILL.md +192 -0
  175. package/plugins/ima-claude/skills/unit-testing/SKILL.md +198 -0
  176. package/plugins/ima-claude/skills/unit-testing/references/mock-patterns.md +181 -0
  177. package/plugins/ima-claude/skills/unit-testing/references/tdd-workflow.md +177 -0
  178. package/plugins/ima-claude/skills/unit-testing/references/test-strategy.md +126 -0
  179. package/plugins/ima-claude/skills/wp-local/SKILL.md +246 -0
  180. package/plugins/ima-claude/skills/wp-local/references/configuration.md +198 -0
  181. package/plugins/ima-claude/skills/wp-local/references/wp-cli-reference.md +406 -0
  182. package/plugins/ima-claude/skills/wp-local/scripts/wp-local.sh +61 -0
@@ -0,0 +1,447 @@
1
+ ---
2
+ name: "js-fp-react"
3
+ description: "FP patterns for React with hooks, HOCs, and pure components - references js-fp core"
4
+ ---
5
+
6
+ # JavaScript FP - React
7
+
8
+ Functional programming patterns for React components with custom hooks, higher-order components, and pure component architecture.
9
+
10
+ ## When to Use This Skill
11
+
12
+ - Building React 16.8+ components (hooks era)
13
+ - Need pure, testable component logic
14
+ - Implementing custom hooks with FP principles
15
+ - HOC patterns for dependency injection
16
+ - Performance optimization without over-engineering
17
+
18
+ ## Core Philosophy
19
+
20
+ **Pure components** with **business logic in custom hooks**, **HOCs for dependency injection**, and **appropriate memoization** (not obsessive).
21
+
22
+ **Foundation**: This skill builds on `js-fp` core principles. Reference `../js-fp/SKILL.md` for purity, composition, dependency injection, and testing patterns.
23
+
24
+ ## Pure Component with Custom Hook Pattern
25
+
26
+ **Rule**: Separate business logic (custom hooks) from presentation (components).
27
+
28
+ ```typescript
29
+ import { memo, useMemo, useCallback } from 'react'
30
+
31
+ interface UserData {
32
+ id: string
33
+ name: string
34
+ email: string
35
+ }
36
+
37
+ interface UserConfig {
38
+ showEmail: boolean
39
+ variant: 'compact' | 'detailed'
40
+ }
41
+
42
+ // ───── Custom hook with pure business logic ─────
43
+ const useUserLogic = (userData: UserData, config: UserConfig) => {
44
+ // Pure computation - no side effects
45
+ const displayData = useMemo(() => ({
46
+ ...userData,
47
+ displayName: userData.name.trim(),
48
+ shouldShowEmail: config.showEmail && userData.email
49
+ }), [userData, config])
50
+
51
+ // Pre-compiled handlers
52
+ const handleAction = useCallback((action: string) => ({
53
+ type: 'USER_ACTION',
54
+ payload: { userId: userData.id, action }
55
+ }), [userData.id])
56
+
57
+ return { displayData, handleAction }
58
+ }
59
+
60
+ // ───── Pure component with memo ─────
61
+ const UserCard = memo<UserCardProps>(({ userData, config, onAction }) => {
62
+ const { displayData, handleAction } = useUserLogic(userData, config)
63
+
64
+ const handleClick = useCallback(() => {
65
+ const action = handleAction('view')
66
+ onAction?.(action)
67
+ }, [handleAction, onAction])
68
+
69
+ return (
70
+ <div className={`user-card user-card--${config.variant}`}>
71
+ <h3>{displayData.displayName}</h3>
72
+ {displayData.shouldShowEmail && <p>{displayData.email}</p>}
73
+ <button onClick={handleClick}>View</button>
74
+ </div>
75
+ )
76
+ })
77
+
78
+ UserCard.displayName = 'UserCard'
79
+ ```
80
+
81
+ ## HOC for Dependency Injection Pattern
82
+
83
+ **Rule**: Inject dependencies via HOCs for testability.
84
+
85
+ ```typescript
86
+ // ───── Service interfaces ─────
87
+ interface ServiceDependencies {
88
+ userService: {
89
+ getUser: (id: string) => Promise<UserData>
90
+ updateUser: (id: string, data: Partial<UserData>) => Promise<UserData>
91
+ }
92
+ logger: {
93
+ info: (message: string, meta?: any) => void
94
+ error: (message: string, meta?: any) => void
95
+ }
96
+ }
97
+
98
+ // ───── HOC factory (core: function factories) ─────
99
+ export const withUserService = <P extends object>(
100
+ WrappedComponent: React.ComponentType<P & ServiceDependencies>
101
+ ) => {
102
+ const WithUserServiceComponent = (props: P) => {
103
+ // Service injection - can be mocked for testing
104
+ const services: ServiceDependencies = {
105
+ userService: useUserService(),
106
+ logger: useLogger()
107
+ }
108
+
109
+ return <WrappedComponent {...props} {...services} />
110
+ }
111
+
112
+ WithUserServiceComponent.displayName =
113
+ `withUserService(${WrappedComponent.displayName || WrappedComponent.name})`
114
+
115
+ return WithUserServiceComponent
116
+ }
117
+
118
+ // ───── Pure component with injected dependencies ─────
119
+ const UserProfile = ({
120
+ userId,
121
+ userService,
122
+ logger
123
+ }: { userId: string } & ServiceDependencies) => {
124
+ const [user, setUser] = useState<UserData | null>(null)
125
+ const [loading, setLoading] = useState(true)
126
+
127
+ useEffect(() => {
128
+ const loadUser = async () => {
129
+ try {
130
+ const userData = await userService.getUser(userId)
131
+ setUser(userData)
132
+ logger.info('User loaded', { userId })
133
+ } catch (error) {
134
+ logger.error('Failed to load user', { userId, error })
135
+ } finally {
136
+ setLoading(false)
137
+ }
138
+ }
139
+
140
+ loadUser()
141
+ }, [userId, userService, logger])
142
+
143
+ if (loading) return <div>Loading...</div>
144
+ if (!user) return <div>User not found</div>
145
+
146
+ return <UserCard userData={user} config={{ showEmail: true, variant: 'detailed' }} />
147
+ }
148
+
149
+ // ───── Enhanced component with service injection ─────
150
+ export const UserProfileWithServices = withUserService(UserProfile)
151
+ ```
152
+
153
+ ## Compound Component Pattern
154
+
155
+ **Rule**: Use composition for flexible, reusable component APIs.
156
+
157
+ ```typescript
158
+ import { createContext, useContext, useState, useEffect, useCallback } from 'react'
159
+
160
+ // ───── Context for compound component ─────
161
+ interface ModalContextValue {
162
+ isOpen: boolean
163
+ onClose: () => void
164
+ }
165
+
166
+ const ModalContext = createContext<ModalContextValue | null>(null)
167
+
168
+ const useModalContext = () => {
169
+ const context = useContext(ModalContext)
170
+ if (!context) {
171
+ throw new Error('Modal components must be used within Modal')
172
+ }
173
+ return context
174
+ }
175
+
176
+ // ───── Main compound component ─────
177
+ interface ModalProps {
178
+ isOpen: boolean
179
+ onClose: () => void
180
+ children: React.ReactNode
181
+ }
182
+
183
+ const Modal = ({ isOpen, onClose, children }: ModalProps) => {
184
+ // Keyboard handling (side effect isolated)
185
+ useEffect(() => {
186
+ const handleEscape = (event: KeyboardEvent) => {
187
+ if (event.key === 'Escape') onClose()
188
+ }
189
+
190
+ if (isOpen) {
191
+ document.addEventListener('keydown', handleEscape)
192
+ return () => document.removeEventListener('keydown', handleEscape)
193
+ }
194
+ }, [isOpen, onClose])
195
+
196
+ if (!isOpen) return null
197
+
198
+ return (
199
+ <ModalContext.Provider value={{ isOpen, onClose }}>
200
+ <div className="modal-overlay" onClick={onClose}>
201
+ <div className="modal-content" onClick={e => e.stopPropagation()}>
202
+ {children}
203
+ </div>
204
+ </div>
205
+ </ModalContext.Provider>
206
+ )
207
+ }
208
+
209
+ // ───── Compound component parts ─────
210
+ Modal.Header = ({ children }: { children: React.ReactNode }) => {
211
+ const { onClose } = useModalContext()
212
+
213
+ return (
214
+ <div className="modal-header">
215
+ {children}
216
+ <button onClick={onClose} aria-label="Close">×</button>
217
+ </div>
218
+ )
219
+ }
220
+
221
+ Modal.Body = ({ children }: { children: React.ReactNode }) => (
222
+ <div className="modal-body">{children}</div>
223
+ )
224
+
225
+ Modal.Footer = ({ children }: { children: React.ReactNode }) => (
226
+ <div className="modal-footer">{children}</div>
227
+ )
228
+
229
+ // ───── Usage - composition over configuration ─────
230
+ const UserEditModal = ({ isOpen, onClose, user }: {
231
+ isOpen: boolean
232
+ onClose: () => void
233
+ user: UserData
234
+ }) => (
235
+ <Modal isOpen={isOpen} onClose={onClose}>
236
+ <Modal.Header>Edit User: {user.name}</Modal.Header>
237
+ <Modal.Body>
238
+ <UserEditForm user={user} />
239
+ </Modal.Body>
240
+ <Modal.Footer>
241
+ <button onClick={onClose}>Cancel</button>
242
+ <button type="submit" form="user-edit-form">Save</button>
243
+ </Modal.Footer>
244
+ </Modal>
245
+ )
246
+ ```
247
+
248
+ ## Performance Optimization (Evidence-Based)
249
+
250
+ **⚠️ IMPORTANT**: Follow core principles - optimize only when needed with evidence.
251
+
252
+ ```typescript
253
+ // ✅ Good - memo for expensive renders
254
+ const ExpensiveComponent = memo(({ data }: { data: LargeDataSet }) => {
255
+ // Expensive rendering logic
256
+ return <ComplexVisualization data={data} />
257
+ })
258
+
259
+ // ✅ Good - useMemo for expensive computations
260
+ const useExpensiveComputation = (largeDataSet: LargeDataSet) => {
261
+ const result = useMemo(() => {
262
+ return performExpensiveCalculation(largeDataSet) // Only when actually expensive
263
+ }, [largeDataSet])
264
+
265
+ return result
266
+ }
267
+
268
+ // ✅ Good - useCallback to prevent prop changes
269
+ const Parent = () => {
270
+ const [count, setCount] = useState(0)
271
+
272
+ const handleClick = useCallback(() => {
273
+ setCount(c => c + 1)
274
+ }, [])
275
+
276
+ return <MemoizedChild onCount={handleClick} />
277
+ }
278
+
279
+ // ❌ Avoid - unnecessary optimization
280
+ const SimpleComponent = memo(({ text }: { text: string }) => <p>{text}</p>) // Not needed
281
+
282
+ // ❌ Avoid - over-using useMemo
283
+ const DisplayName = ({ first, last }: { first: string; last: string }) => {
284
+ const fullName = useMemo(() => `${first} ${last}`, [first, last]) // Overkill
285
+ return <p>{fullName}</p>
286
+ }
287
+
288
+ // ✅ Better - direct computation for simple operations
289
+ const DisplayName = ({ first, last }: { first: string; last: string }) => {
290
+ const fullName = `${first} ${last}` // Simple, no memo needed
291
+ return <p>{fullName}</p>
292
+ }
293
+ ```
294
+
295
+ ## Testing React FP Components
296
+
297
+ ### Test Custom Hooks
298
+
299
+ ```typescript
300
+ // __tests__/useUserLogic.test.ts
301
+ import { renderHook } from '@testing-library/react-hooks'
302
+ import { useUserLogic } from '../useUserLogic'
303
+
304
+ describe('useUserLogic', () => {
305
+ it('processes user data correctly', () => {
306
+ const { result } = renderHook(() =>
307
+ useUserLogic(
308
+ { id: '1', name: ' John ', email: 'john@test.com' },
309
+ { showEmail: true, variant: 'compact' }
310
+ )
311
+ )
312
+
313
+ expect(result.current.displayData.displayName).toBe('John')
314
+ expect(result.current.displayData.email).toBe('john@test.com')
315
+ })
316
+
317
+ it('hides email when config.showEmail is false', () => {
318
+ const { result } = renderHook(() =>
319
+ useUserLogic(
320
+ { id: '1', name: 'John', email: 'john@test.com' },
321
+ { showEmail: false, variant: 'compact' }
322
+ )
323
+ )
324
+
325
+ expect(result.current.displayData.shouldShowEmail).toBe(false)
326
+ })
327
+ })
328
+ ```
329
+
330
+ ### Test Components with React Testing Library
331
+
332
+ ```typescript
333
+ // __tests__/UserCard.test.tsx
334
+ import { render, screen, userEvent } from '@testing-library/react'
335
+ import UserCard from '../UserCard'
336
+
337
+ describe('UserCard', () => {
338
+ it('renders user data correctly', () => {
339
+ render(
340
+ <UserCard
341
+ userData={{ id: '1', name: 'John', email: 'john@test.com' }}
342
+ config={{ showEmail: true, variant: 'compact' }}
343
+ onAction={jest.fn()}
344
+ />
345
+ )
346
+
347
+ expect(screen.getByText('John')).toBeInTheDocument()
348
+ expect(screen.getByText('john@test.com')).toBeInTheDocument()
349
+ })
350
+
351
+ it('calls onAction when button clicked', async () => {
352
+ const onAction = jest.fn()
353
+ render(
354
+ <UserCard
355
+ userData={{ id: '1', name: 'John', email: 'john@test.com' }}
356
+ config={{ showEmail: true, variant: 'compact' }}
357
+ onAction={onAction}
358
+ />
359
+ )
360
+
361
+ await userEvent.click(screen.getByRole('button'))
362
+ expect(onAction).toHaveBeenCalled()
363
+ })
364
+ })
365
+ ```
366
+
367
+ ## Anti-Patterns (AVOID)
368
+
369
+ ### ❌ Overusing Context
370
+
371
+ ```typescript
372
+ // ❌ Context for local state
373
+ const UserContext = createContext<UserData | null>(null)
374
+
375
+ // ✅ Props for local state (simpler)
376
+ <UserCard userData={user} />
377
+ ```
378
+
379
+ ### ❌ Premature Memoization
380
+
381
+ ```typescript
382
+ // ❌ Memoizing everything
383
+ const Component = () => {
384
+ const a = useMemo(() => 1 + 1, []) // Overkill
385
+ const b = useMemo(() => 'hello', []) // Overkill
386
+ const c = useCallback(() => {}, []) // Overkill when passed to non-memoized children
387
+ }
388
+
389
+ // ✅ Memoize only when needed
390
+ const Component = () => {
391
+ const a = 2 // Simple calculation
392
+ const b = 'hello' // Simple value
393
+ const c = () => {} // Only memo if passed to memoized child
394
+ }
395
+ ```
396
+
397
+ ## Quality Gates
398
+
399
+ Before implementing any React component:
400
+
401
+ 1. ✅ **Pure custom hook**: Business logic separated from presentation?
402
+ 2. ✅ **HOC for DI**: Dependencies injected via HOC when appropriate?
403
+ 3. ✅ **Appropriate memoization**: Using memo/useMemo/useCallback only when needed?
404
+ 4. ✅ **Compound components**: Using composition for flexible APIs?
405
+ 5. ✅ **Testability**: Can inject mocks for all dependencies?
406
+ 6. ✅ **FP principles**: Pure functions, immutable updates?
407
+ 7. ✅ **Performance**: Optimized without over-engineering?
408
+
409
+ ## When to Load Additional Content
410
+
411
+ ### Hooks Advanced
412
+ **File**: `references/hooks-advanced.md`
413
+ **When**: Complex custom hooks, advanced patterns
414
+ **Contains**: Hook composition, state machines, async patterns, effect isolation
415
+
416
+ ### Performance Patterns
417
+ **File**: `references/performance-patterns.md`
418
+ **When**: Performance optimization needed, large lists
419
+ **Contains**: React.memo strategies, virtualization, code splitting
420
+
421
+ ### Working Examples
422
+ **File**: `examples/ProductCard.tsx`
423
+ **When**: Need complete working component example
424
+ **Contains**: Full ProductCard component with custom hook, types, and exports
425
+
426
+ ## Foundation Reference
427
+
428
+ **Core FP Principles**: `../js-fp/SKILL.md`
429
+ - Purity and side effect isolation
430
+ - Composition patterns
431
+ - Dependency injection
432
+ - Immutability
433
+ - Testing strategies
434
+
435
+ **Deep Dive**: `../js-fp/core-principles.md` for complete FP philosophy
436
+
437
+ ## Success Metrics
438
+
439
+ - **Testability**: 100% testable custom hooks
440
+ - **Performance**: Appropriate memoization, sub-100ms renders
441
+ - **Maintainability**: Clear separation of concerns
442
+ - **Code Quality**: Simple, readable component logic
443
+ - **Bundle Size**: Tree-shakeable, minimal overhead
444
+
445
+ ## Philosophy
446
+
447
+ *"Pure component architecture through custom hooks, HOCs for dependency injection, and appropriate memoization - optimize for testability and simplicity over premature optimization."*
@@ -0,0 +1,65 @@
1
+ // ProductCard.tsx
2
+ // Complete FP React component example demonstrating:
3
+ // - Custom hook for business logic
4
+ // - Pure component with memo
5
+ // - Appropriate memoization
6
+ // - TypeScript interfaces
7
+
8
+ import { memo, useMemo, useCallback } from 'react'
9
+
10
+ // ───── Types ─────
11
+ interface Product {
12
+ id: string
13
+ name: string
14
+ price: number
15
+ inStock: boolean
16
+ }
17
+
18
+ interface ProductCardProps {
19
+ product: Product
20
+ onAddToCart: (productId: string) => void
21
+ }
22
+
23
+ // ───── Custom Hook (Business Logic) ─────
24
+ // Pure computations separated from presentation
25
+ const useProductLogic = (product: Product) => {
26
+ const displayData = useMemo(() => ({
27
+ ...product,
28
+ formattedPrice: `$${product.price.toFixed(2)}`,
29
+ availability: product.inStock ? 'In Stock' : 'Out of Stock'
30
+ }), [product])
31
+
32
+ const cssClasses = useMemo(() => ({
33
+ card: `product-card ${product.inStock ? 'in-stock' : 'out-of-stock'}`,
34
+ price: `product-price ${product.inStock ? 'available' : 'unavailable'}`
35
+ }), [product.inStock])
36
+
37
+ return { displayData, cssClasses }
38
+ }
39
+
40
+ // ───── Pure Component ─────
41
+ const ProductCard = memo<ProductCardProps>(({ product, onAddToCart }) => {
42
+ const { displayData, cssClasses } = useProductLogic(product)
43
+
44
+ const handleAddToCart = useCallback(() => {
45
+ if (product.inStock) {
46
+ onAddToCart(product.id)
47
+ }
48
+ }, [product.inStock, product.id, onAddToCart])
49
+
50
+ return (
51
+ <div className={cssClasses.card}>
52
+ <h3>{displayData.name}</h3>
53
+ <p className={cssClasses.price}>{displayData.formattedPrice}</p>
54
+ <p className="availability">{displayData.availability}</p>
55
+ <button onClick={handleAddToCart} disabled={!product.inStock}>
56
+ Add to Cart
57
+ </button>
58
+ </div>
59
+ )
60
+ })
61
+
62
+ ProductCard.displayName = 'ProductCard'
63
+
64
+ export { ProductCard, useProductLogic }
65
+ export type { Product, ProductCardProps }
@@ -0,0 +1,136 @@
1
+ # Advanced React Hooks Patterns
2
+
3
+ Advanced patterns for custom hooks following FP principles.
4
+
5
+ ## Table of Contents
6
+
7
+ 1. [Composition Patterns](#composition-patterns)
8
+ 2. [State Machine Hooks](#state-machine-hooks)
9
+ 3. [Async Data Hooks](#async-data-hooks)
10
+ 4. [Effect Isolation](#effect-isolation)
11
+
12
+ ## Composition Patterns
13
+
14
+ ### Hook Composition
15
+
16
+ Compose smaller hooks into larger ones:
17
+
18
+ ```typescript
19
+ // Small, focused hooks
20
+ const useToggle = (initial = false) => {
21
+ const [value, setValue] = useState(initial)
22
+ const toggle = useCallback(() => setValue(v => !v), [])
23
+ return { value, toggle }
24
+ }
25
+
26
+ const useAsync = <T>(asyncFn: () => Promise<T>, deps: any[]) => {
27
+ const [state, setState] = useState<{
28
+ data: T | null; loading: boolean; error: Error | null
29
+ }>({ data: null, loading: true, error: null })
30
+
31
+ useEffect(() => {
32
+ setState(s => ({ ...s, loading: true }))
33
+ asyncFn()
34
+ .then(data => setState({ data, loading: false, error: null }))
35
+ .catch(error => setState({ data: null, loading: false, error }))
36
+ }, deps)
37
+
38
+ return state
39
+ }
40
+ ```
41
+
42
+ ### Reducer Pattern for Complex State
43
+
44
+ ```typescript
45
+ type Action =
46
+ | { type: 'FETCH_START' }
47
+ | { type: 'FETCH_SUCCESS'; payload: User[] }
48
+ | { type: 'FETCH_ERROR'; error: Error }
49
+
50
+ // Pure reducer function
51
+ const userReducer = (state: State, action: Action): State => {
52
+ switch (action.type) {
53
+ case 'FETCH_START': return { ...state, loading: true, error: null }
54
+ case 'FETCH_SUCCESS': return { ...state, users: action.payload, loading: false }
55
+ case 'FETCH_ERROR': return { ...state, error: action.error, loading: false }
56
+ default: return state
57
+ }
58
+ }
59
+ ```
60
+
61
+ ## State Machine Hooks
62
+
63
+ ```typescript
64
+ type FormState = 'idle' | 'validating' | 'submitting' | 'success' | 'error'
65
+
66
+ const useFormMachine = () => {
67
+ const [state, setState] = useState<FormState>('idle')
68
+
69
+ const transition = useCallback((event: string) => {
70
+ setState(current => {
71
+ const transitions: Record<FormState, Record<string, FormState>> = {
72
+ idle: { SUBMIT: 'submitting' },
73
+ submitting: { SUCCESS: 'success', FAILURE: 'error' },
74
+ error: { RETRY: 'idle' },
75
+ success: { RESET: 'idle' }
76
+ }
77
+ return transitions[current]?.[event] ?? current
78
+ })
79
+ }, [])
80
+
81
+ return { state, canSubmit: state === 'idle', transition }
82
+ }
83
+ ```
84
+
85
+ ## Async Data Hooks
86
+
87
+ ### Debounced Search
88
+
89
+ ```typescript
90
+ const useDebouncedSearch = <T>(searchFn: (q: string) => Promise<T[]>, delay = 300) => {
91
+ const [query, setQuery] = useState('')
92
+ const [results, setResults] = useState<T[]>([])
93
+
94
+ useEffect(() => {
95
+ if (!query.trim()) { setResults([]); return }
96
+ const timer = setTimeout(async () => {
97
+ setResults(await searchFn(query))
98
+ }, delay)
99
+ return () => clearTimeout(timer)
100
+ }, [query, searchFn, delay])
101
+
102
+ return { query, setQuery, results }
103
+ }
104
+ ```
105
+
106
+ ## Effect Isolation
107
+
108
+ ```typescript
109
+ // Pure computation hook
110
+ const useCartCalculations = (items: CartItem[]) => {
111
+ return useMemo(() => ({
112
+ subtotal: items.reduce((sum, item) => sum + item.price * item.qty, 0),
113
+ itemCount: items.reduce((sum, item) => sum + item.qty, 0)
114
+ }), [items])
115
+ }
116
+
117
+ // Side effect hook (separated)
118
+ const useCartPersistence = (items: CartItem[]) => {
119
+ useEffect(() => {
120
+ localStorage.setItem('cart', JSON.stringify(items))
121
+ }, [items])
122
+ }
123
+ ```
124
+
125
+ ## Testing
126
+
127
+ ```typescript
128
+ // Test reducer in isolation (pure function)
129
+ describe('userReducer', () => {
130
+ it('handles FETCH_SUCCESS', () => {
131
+ const state = { users: [], loading: true, error: null }
132
+ const result = userReducer(state, { type: 'FETCH_SUCCESS', payload: [mockUser] })
133
+ expect(result.users).toEqual([mockUser])
134
+ })
135
+ })
136
+ ```