@mrtrinhvn/ag-kit 1.0.11 → 1.1.1

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 (218) hide show
  1. package/README.md +31 -0
  2. package/bin/cli.js +72 -0
  3. package/package.json +7 -1
  4. package/template/.agent/knowledge/orchestrator_v3_protocol.md +60 -0
  5. package/template/.agent/knowledge/self_healing_logs.md +22 -0
  6. package/{.agent → template/.agent}/skills/telegram-agentic-gateway/SKILL.md +0 -0
  7. package/template/.agent/skills/vfs-assistant/SKILL.md +39 -0
  8. package/.agent/agents/backend-specialist.md +0 -263
  9. package/.agent/agents/code-archaeologist.md +0 -106
  10. package/.agent/agents/database-architect.md +0 -226
  11. package/.agent/agents/debugger.md +0 -225
  12. package/.agent/agents/devops-engineer.md +0 -242
  13. package/.agent/agents/documentation-writer.md +0 -104
  14. package/.agent/agents/explorer-agent.md +0 -73
  15. package/.agent/agents/frontend-specialist.md +0 -556
  16. package/.agent/agents/game-developer.md +0 -162
  17. package/.agent/agents/mobile-developer.md +0 -377
  18. package/.agent/agents/orchestrator.md +0 -416
  19. package/.agent/agents/penetration-tester.md +0 -188
  20. package/.agent/agents/performance-optimizer.md +0 -187
  21. package/.agent/agents/product-manager.md +0 -112
  22. package/.agent/agents/product-owner.md +0 -95
  23. package/.agent/agents/project-planner.md +0 -406
  24. package/.agent/agents/qa-automation-engineer.md +0 -103
  25. package/.agent/agents/quant-architect.md +0 -31
  26. package/.agent/agents/security-auditor.md +0 -170
  27. package/.agent/agents/seo-specialist.md +0 -111
  28. package/.agent/agents/test-engineer.md +0 -158
  29. package/.agent/rules/GEMINI.md +0 -280
  30. package/.agent/scripts/auto_preview.py +0 -148
  31. package/.agent/scripts/checklist.py +0 -217
  32. package/.agent/scripts/session_manager.py +0 -120
  33. package/.agent/scripts/verify_all.py +0 -327
  34. package/.agent/skills/api-patterns/SKILL.md +0 -81
  35. package/.agent/skills/api-patterns/api-style.md +0 -42
  36. package/.agent/skills/api-patterns/auth.md +0 -24
  37. package/.agent/skills/api-patterns/documentation.md +0 -26
  38. package/.agent/skills/api-patterns/graphql.md +0 -41
  39. package/.agent/skills/api-patterns/rate-limiting.md +0 -31
  40. package/.agent/skills/api-patterns/response.md +0 -37
  41. package/.agent/skills/api-patterns/rest.md +0 -40
  42. package/.agent/skills/api-patterns/scripts/api_validator.py +0 -211
  43. package/.agent/skills/api-patterns/security-testing.md +0 -122
  44. package/.agent/skills/api-patterns/trpc.md +0 -41
  45. package/.agent/skills/api-patterns/versioning.md +0 -22
  46. package/.agent/skills/app-builder/SKILL.md +0 -75
  47. package/.agent/skills/app-builder/agent-coordination.md +0 -71
  48. package/.agent/skills/app-builder/feature-building.md +0 -53
  49. package/.agent/skills/app-builder/project-detection.md +0 -34
  50. package/.agent/skills/app-builder/scaffolding.md +0 -118
  51. package/.agent/skills/app-builder/tech-stack.md +0 -40
  52. package/.agent/skills/app-builder/templates/SKILL.md +0 -39
  53. package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +0 -76
  54. package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +0 -92
  55. package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +0 -88
  56. package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +0 -88
  57. package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +0 -83
  58. package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +0 -90
  59. package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +0 -90
  60. package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +0 -82
  61. package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +0 -100
  62. package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +0 -106
  63. package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +0 -101
  64. package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +0 -83
  65. package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +0 -93
  66. package/.agent/skills/architecture/SKILL.md +0 -55
  67. package/.agent/skills/architecture/context-discovery.md +0 -43
  68. package/.agent/skills/architecture/examples.md +0 -94
  69. package/.agent/skills/architecture/pattern-selection.md +0 -68
  70. package/.agent/skills/architecture/patterns-reference.md +0 -50
  71. package/.agent/skills/architecture/trade-off-analysis.md +0 -77
  72. package/.agent/skills/bash-linux/SKILL.md +0 -199
  73. package/.agent/skills/behavioral-modes/SKILL.md +0 -242
  74. package/.agent/skills/brainstorming/SKILL.md +0 -168
  75. package/.agent/skills/brainstorming/dynamic-questioning.md +0 -350
  76. package/.agent/skills/business-ops/SKILL.md +0 -26
  77. package/.agent/skills/clean-code/SKILL.md +0 -202
  78. package/.agent/skills/cli-generator/SKILL.md +0 -48
  79. package/.agent/skills/code-review-checklist/SKILL.md +0 -109
  80. package/.agent/skills/cognitive-session/SKILL.md +0 -28
  81. package/.agent/skills/data-science/SKILL.md +0 -28
  82. package/.agent/skills/database-design/SKILL.md +0 -52
  83. package/.agent/skills/database-design/database-selection.md +0 -43
  84. package/.agent/skills/database-design/indexing.md +0 -39
  85. package/.agent/skills/database-design/migrations.md +0 -48
  86. package/.agent/skills/database-design/optimization.md +0 -36
  87. package/.agent/skills/database-design/orm-selection.md +0 -30
  88. package/.agent/skills/database-design/schema-design.md +0 -56
  89. package/.agent/skills/database-design/scripts/schema_validator.py +0 -172
  90. package/.agent/skills/deployment-procedures/SKILL.md +0 -241
  91. package/.agent/skills/doc.md +0 -177
  92. package/.agent/skills/documentation-templates/SKILL.md +0 -194
  93. package/.agent/skills/frontend-design/SKILL.md +0 -418
  94. package/.agent/skills/frontend-design/animation-guide.md +0 -331
  95. package/.agent/skills/frontend-design/color-system.md +0 -311
  96. package/.agent/skills/frontend-design/decision-trees.md +0 -418
  97. package/.agent/skills/frontend-design/motion-graphics.md +0 -306
  98. package/.agent/skills/frontend-design/scripts/accessibility_checker.py +0 -183
  99. package/.agent/skills/frontend-design/scripts/ux_audit.py +0 -722
  100. package/.agent/skills/frontend-design/typography-system.md +0 -345
  101. package/.agent/skills/frontend-design/ux-psychology.md +0 -541
  102. package/.agent/skills/frontend-design/visual-effects.md +0 -383
  103. package/.agent/skills/game-development/2d-games/SKILL.md +0 -119
  104. package/.agent/skills/game-development/3d-games/SKILL.md +0 -135
  105. package/.agent/skills/game-development/SKILL.md +0 -167
  106. package/.agent/skills/game-development/game-art/SKILL.md +0 -185
  107. package/.agent/skills/game-development/game-audio/SKILL.md +0 -190
  108. package/.agent/skills/game-development/game-design/SKILL.md +0 -129
  109. package/.agent/skills/game-development/mobile-games/SKILL.md +0 -108
  110. package/.agent/skills/game-development/multiplayer/SKILL.md +0 -132
  111. package/.agent/skills/game-development/pc-games/SKILL.md +0 -144
  112. package/.agent/skills/game-development/vr-ar/SKILL.md +0 -123
  113. package/.agent/skills/game-development/web-games/SKILL.md +0 -150
  114. package/.agent/skills/geo-fundamentals/SKILL.md +0 -156
  115. package/.agent/skills/geo-fundamentals/scripts/geo_checker.py +0 -289
  116. package/.agent/skills/i18n-localization/SKILL.md +0 -154
  117. package/.agent/skills/i18n-localization/scripts/i18n_checker.py +0 -241
  118. package/.agent/skills/intelligent-routing/SKILL.md +0 -335
  119. package/.agent/skills/knowledge-management/SKILL.md +0 -66
  120. package/.agent/skills/lint-and-validate/SKILL.md +0 -45
  121. package/.agent/skills/lint-and-validate/scripts/lint_runner.py +0 -172
  122. package/.agent/skills/lint-and-validate/scripts/type_coverage.py +0 -173
  123. package/.agent/skills/llm-routing-quirks/SKILL.md +0 -41
  124. package/.agent/skills/mcp-builder/SKILL.md +0 -176
  125. package/.agent/skills/memory-architecture/SKILL.md +0 -107
  126. package/.agent/skills/mini-antigravity-injection/SKILL.md +0 -66
  127. package/.agent/skills/mobile-design/SKILL.md +0 -394
  128. package/.agent/skills/mobile-design/decision-trees.md +0 -516
  129. package/.agent/skills/mobile-design/mobile-backend.md +0 -491
  130. package/.agent/skills/mobile-design/mobile-color-system.md +0 -420
  131. package/.agent/skills/mobile-design/mobile-debugging.md +0 -122
  132. package/.agent/skills/mobile-design/mobile-design-thinking.md +0 -357
  133. package/.agent/skills/mobile-design/mobile-navigation.md +0 -458
  134. package/.agent/skills/mobile-design/mobile-performance.md +0 -767
  135. package/.agent/skills/mobile-design/mobile-testing.md +0 -356
  136. package/.agent/skills/mobile-design/mobile-typography.md +0 -433
  137. package/.agent/skills/mobile-design/platform-android.md +0 -666
  138. package/.agent/skills/mobile-design/platform-ios.md +0 -561
  139. package/.agent/skills/mobile-design/scripts/mobile_audit.py +0 -670
  140. package/.agent/skills/mobile-design/touch-psychology.md +0 -537
  141. package/.agent/skills/nextjs-react-expert/1-async-eliminating-waterfalls.md +0 -312
  142. package/.agent/skills/nextjs-react-expert/2-bundle-bundle-size-optimization.md +0 -240
  143. package/.agent/skills/nextjs-react-expert/3-server-server-side-performance.md +0 -490
  144. package/.agent/skills/nextjs-react-expert/4-client-client-side-data-fetching.md +0 -264
  145. package/.agent/skills/nextjs-react-expert/5-rerender-re-render-optimization.md +0 -581
  146. package/.agent/skills/nextjs-react-expert/6-rendering-rendering-performance.md +0 -432
  147. package/.agent/skills/nextjs-react-expert/7-js-javascript-performance.md +0 -684
  148. package/.agent/skills/nextjs-react-expert/8-advanced-advanced-patterns.md +0 -150
  149. package/.agent/skills/nextjs-react-expert/SKILL.md +0 -267
  150. package/.agent/skills/nextjs-react-expert/scripts/convert_rules.py +0 -222
  151. package/.agent/skills/nextjs-react-expert/scripts/react_performance_checker.py +0 -252
  152. package/.agent/skills/nodejs-best-practices/SKILL.md +0 -333
  153. package/.agent/skills/parallel-agents/SKILL.md +0 -175
  154. package/.agent/skills/performance-profiling/SKILL.md +0 -143
  155. package/.agent/skills/performance-profiling/scripts/lighthouse_audit.py +0 -76
  156. package/.agent/skills/plan-writing/SKILL.md +0 -153
  157. package/.agent/skills/powershell-windows/SKILL.md +0 -167
  158. package/.agent/skills/product-management/SKILL.md +0 -30
  159. package/.agent/skills/python-patterns/SKILL.md +0 -441
  160. package/.agent/skills/red-team-tactics/SKILL.md +0 -199
  161. package/.agent/skills/seo-fundamentals/SKILL.md +0 -129
  162. package/.agent/skills/seo-fundamentals/scripts/seo_checker.py +0 -219
  163. package/.agent/skills/server-management/SKILL.md +0 -161
  164. package/.agent/skills/systematic-debugging/SKILL.md +0 -120
  165. package/.agent/skills/tailwind-patterns/SKILL.md +0 -269
  166. package/.agent/skills/tdd-workflow/SKILL.md +0 -148
  167. package/.agent/skills/testing-patterns/SKILL.md +0 -178
  168. package/.agent/skills/testing-patterns/scripts/test_runner.py +0 -219
  169. package/.agent/skills/vulnerability-scanner/SKILL.md +0 -276
  170. package/.agent/skills/vulnerability-scanner/checklists.md +0 -121
  171. package/.agent/skills/vulnerability-scanner/scripts/security_scan.py +0 -458
  172. package/.agent/skills/web-design-guidelines/SKILL.md +0 -57
  173. package/.agent/skills/webapp-testing/SKILL.md +0 -187
  174. package/.agent/skills/webapp-testing/scripts/playwright_runner.py +0 -173
  175. package/.agent/workflows/brainstorm.md +0 -113
  176. package/.agent/workflows/create.md +0 -59
  177. package/.agent/workflows/debug.md +0 -103
  178. package/.agent/workflows/deploy.md +0 -176
  179. package/.agent/workflows/enhance.md +0 -63
  180. package/.agent/workflows/orchestrate.md +0 -237
  181. package/.agent/workflows/plan.md +0 -89
  182. package/.agent/workflows/preview.md +0 -81
  183. package/.agent/workflows/status.md +0 -86
  184. package/.agent/workflows/test.md +0 -144
  185. package/.agent/workflows/ui-ux-pro-max.md +0 -296
  186. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/data/charts.csv +0 -0
  187. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/data/colors.csv +0 -0
  188. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/data/icons.csv +0 -0
  189. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/data/landing.csv +0 -0
  190. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/data/products.csv +0 -0
  191. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/data/prompts.csv +0 -0
  192. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/data/react-performance.csv +0 -0
  193. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/data/stacks/flutter.csv +0 -0
  194. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +0 -0
  195. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/data/stacks/jetpack-compose.csv +0 -0
  196. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +0 -0
  197. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +0 -0
  198. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +0 -0
  199. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/data/stacks/react-native.csv +0 -0
  200. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/data/stacks/react.csv +0 -0
  201. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/data/stacks/shadcn.csv +0 -0
  202. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/data/stacks/svelte.csv +0 -0
  203. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +0 -0
  204. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/data/stacks/vue.csv +0 -0
  205. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/data/styles.csv +0 -0
  206. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/data/typography.csv +0 -0
  207. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/data/ui-reasoning.csv +0 -0
  208. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/data/ux-guidelines.csv +0 -0
  209. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/data/web-interface.csv +0 -0
  210. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/scripts/__pycache__/core.cpython-313.pyc +0 -0
  211. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-313.pyc +0 -0
  212. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/scripts/core.py +0 -0
  213. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/scripts/design_system.py +0 -0
  214. /package/{.agent → template/.agent}/.shared/ui-ux-pro-max/scripts/search.py +0 -0
  215. /package/{.agent → template/.agent}/ARCHITECTURE.md +0 -0
  216. /package/{.agent → template/.agent}/mcp_config.json +0 -0
  217. /package/{.agent → template/.agent}/skills/nextjs-react-expert/9-cache-components.md +0 -0
  218. /package/{.agent → template/.agent}/skills/rust-pro/SKILL.md +0 -0
@@ -1,684 +0,0 @@
1
- # 7. JavaScript Performance
2
-
3
- > **Impact:** LOW-MEDIUM
4
- > **Focus:** Micro-optimizations for hot paths can add up to meaningful improvements.
5
-
6
- ---
7
-
8
- ## Overview
9
-
10
- This section contains **12 rules** focused on javascript performance.
11
-
12
- ---
13
-
14
- ## Rule 7.1: Avoid Layout Thrashing
15
-
16
- **Impact:** MEDIUM
17
- **Tags:** javascript, dom, css, performance, reflow, layout-thrashing
18
-
19
- ## Avoid Layout Thrashing
20
-
21
- Avoid interleaving style writes with layout reads. When you read a layout property (like `offsetWidth`, `getBoundingClientRect()`, or `getComputedStyle()`) between style changes, the browser is forced to trigger a synchronous reflow.
22
-
23
- **This is OK (browser batches style changes):**
24
- ```typescript
25
- function updateElementStyles(element: HTMLElement) {
26
- // Each line invalidates style, but browser batches the recalculation
27
- element.style.width = '100px'
28
- element.style.height = '200px'
29
- element.style.backgroundColor = 'blue'
30
- element.style.border = '1px solid black'
31
- }
32
- ```
33
-
34
- **Incorrect (interleaved reads and writes force reflows):**
35
- ```typescript
36
- function layoutThrashing(element: HTMLElement) {
37
- element.style.width = '100px'
38
- const width = element.offsetWidth // Forces reflow
39
- element.style.height = '200px'
40
- const height = element.offsetHeight // Forces another reflow
41
- }
42
- ```
43
-
44
- **Correct (batch writes, then read once):**
45
- ```typescript
46
- function updateElementStyles(element: HTMLElement) {
47
- // Batch all writes together
48
- element.style.width = '100px'
49
- element.style.height = '200px'
50
- element.style.backgroundColor = 'blue'
51
- element.style.border = '1px solid black'
52
-
53
- // Read after all writes are done (single reflow)
54
- const { width, height } = element.getBoundingClientRect()
55
- }
56
- ```
57
-
58
- **Correct (batch reads, then writes):**
59
- ```typescript
60
- function avoidThrashing(element: HTMLElement) {
61
- // Read phase - all layout queries first
62
- const rect1 = element.getBoundingClientRect()
63
- const offsetWidth = element.offsetWidth
64
- const offsetHeight = element.offsetHeight
65
-
66
- // Write phase - all style changes after
67
- element.style.width = '100px'
68
- element.style.height = '200px'
69
- }
70
- ```
71
-
72
- **Better: use CSS classes**
73
- ```css
74
- .highlighted-box {
75
- width: 100px;
76
- height: 200px;
77
- background-color: blue;
78
- border: 1px solid black;
79
- }
80
- ```
81
- ```typescript
82
- function updateElementStyles(element: HTMLElement) {
83
- element.classList.add('highlighted-box')
84
-
85
- const { width, height } = element.getBoundingClientRect()
86
- }
87
- ```
88
-
89
- **React example:**
90
- ```tsx
91
- // Incorrect: interleaving style changes with layout queries
92
- function Box({ isHighlighted }: { isHighlighted: boolean }) {
93
- const ref = useRef<HTMLDivElement>(null)
94
-
95
- useEffect(() => {
96
- if (ref.current && isHighlighted) {
97
- ref.current.style.width = '100px'
98
- const width = ref.current.offsetWidth // Forces layout
99
- ref.current.style.height = '200px'
100
- }
101
- }, [isHighlighted])
102
-
103
- return <div ref={ref}>Content</div>
104
- }
105
-
106
- // Correct: toggle class
107
- function Box({ isHighlighted }: { isHighlighted: boolean }) {
108
- return (
109
- <div className={isHighlighted ? 'highlighted-box' : ''}>
110
- Content
111
- </div>
112
- )
113
- }
114
- ```
115
-
116
- Prefer CSS classes over inline styles when possible. CSS files are cached by the browser, and classes provide better separation of concerns and are easier to maintain.
117
-
118
- See [this gist](https://gist.github.com/paulirish/5d52fb081b3570c81e3a) and [CSS Triggers](https://csstriggers.com/) for more information on layout-forcing operations.
119
-
120
- ---
121
-
122
- ## Rule 7.2: Build Index Maps for Repeated Lookups
123
-
124
- **Impact:** LOW-MEDIUM
125
- **Tags:** javascript, map, indexing, optimization, performance
126
-
127
- ## Build Index Maps for Repeated Lookups
128
-
129
- Multiple `.find()` calls by the same key should use a Map.
130
-
131
- **Incorrect (O(n) per lookup):**
132
-
133
- ```typescript
134
- function processOrders(orders: Order[], users: User[]) {
135
- return orders.map(order => ({
136
- ...order,
137
- user: users.find(u => u.id === order.userId)
138
- }))
139
- }
140
- ```
141
-
142
- **Correct (O(1) per lookup):**
143
-
144
- ```typescript
145
- function processOrders(orders: Order[], users: User[]) {
146
- const userById = new Map(users.map(u => [u.id, u]))
147
-
148
- return orders.map(order => ({
149
- ...order,
150
- user: userById.get(order.userId)
151
- }))
152
- }
153
- ```
154
-
155
- Build map once (O(n)), then all lookups are O(1).
156
- For 1000 orders × 1000 users: 1M ops → 2K ops.
157
-
158
- ---
159
-
160
- ## Rule 7.3: Cache Property Access in Loops
161
-
162
- **Impact:** LOW-MEDIUM
163
- **Tags:** javascript, loops, optimization, caching
164
-
165
- ## Cache Property Access in Loops
166
-
167
- Cache object property lookups in hot paths.
168
-
169
- **Incorrect (3 lookups × N iterations):**
170
-
171
- ```typescript
172
- for (let i = 0; i < arr.length; i++) {
173
- process(obj.config.settings.value)
174
- }
175
- ```
176
-
177
- **Correct (1 lookup total):**
178
-
179
- ```typescript
180
- const value = obj.config.settings.value
181
- const len = arr.length
182
- for (let i = 0; i < len; i++) {
183
- process(value)
184
- }
185
- ```
186
-
187
- ---
188
-
189
- ## Rule 7.4: Cache Repeated Function Calls
190
-
191
- **Impact:** MEDIUM
192
- **Tags:** javascript, cache, memoization, performance
193
-
194
- ## Cache Repeated Function Calls
195
-
196
- Use a module-level Map to cache function results when the same function is called repeatedly with the same inputs during render.
197
-
198
- **Incorrect (redundant computation):**
199
-
200
- ```typescript
201
- function ProjectList({ projects }: { projects: Project[] }) {
202
- return (
203
- <div>
204
- {projects.map(project => {
205
- // slugify() called 100+ times for same project names
206
- const slug = slugify(project.name)
207
-
208
- return <ProjectCard key={project.id} slug={slug} />
209
- })}
210
- </div>
211
- )
212
- }
213
- ```
214
-
215
- **Correct (cached results):**
216
-
217
- ```typescript
218
- // Module-level cache
219
- const slugifyCache = new Map<string, string>()
220
-
221
- function cachedSlugify(text: string): string {
222
- if (slugifyCache.has(text)) {
223
- return slugifyCache.get(text)!
224
- }
225
- const result = slugify(text)
226
- slugifyCache.set(text, result)
227
- return result
228
- }
229
-
230
- function ProjectList({ projects }: { projects: Project[] }) {
231
- return (
232
- <div>
233
- {projects.map(project => {
234
- // Computed only once per unique project name
235
- const slug = cachedSlugify(project.name)
236
-
237
- return <ProjectCard key={project.id} slug={slug} />
238
- })}
239
- </div>
240
- )
241
- }
242
- ```
243
-
244
- **Simpler pattern for single-value functions:**
245
-
246
- ```typescript
247
- let isLoggedInCache: boolean | null = null
248
-
249
- function isLoggedIn(): boolean {
250
- if (isLoggedInCache !== null) {
251
- return isLoggedInCache
252
- }
253
-
254
- isLoggedInCache = document.cookie.includes('auth=')
255
- return isLoggedInCache
256
- }
257
-
258
- // Clear cache when auth changes
259
- function onAuthChange() {
260
- isLoggedInCache = null
261
- }
262
- ```
263
-
264
- Use a Map (not a hook) so it works everywhere: utilities, event handlers, not just React components.
265
-
266
- Reference: [How we made the Vercel Dashboard twice as fast](https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast)
267
-
268
- ---
269
-
270
- ## Rule 7.5: Cache Storage API Calls
271
-
272
- **Impact:** LOW-MEDIUM
273
- **Tags:** javascript, localStorage, storage, caching, performance
274
-
275
- ## Cache Storage API Calls
276
-
277
- `localStorage`, `sessionStorage`, and `document.cookie` are synchronous and expensive. Cache reads in memory.
278
-
279
- **Incorrect (reads storage on every call):**
280
-
281
- ```typescript
282
- function getTheme() {
283
- return localStorage.getItem('theme') ?? 'light'
284
- }
285
- // Called 10 times = 10 storage reads
286
- ```
287
-
288
- **Correct (Map cache):**
289
-
290
- ```typescript
291
- const storageCache = new Map<string, string | null>()
292
-
293
- function getLocalStorage(key: string) {
294
- if (!storageCache.has(key)) {
295
- storageCache.set(key, localStorage.getItem(key))
296
- }
297
- return storageCache.get(key)
298
- }
299
-
300
- function setLocalStorage(key: string, value: string) {
301
- localStorage.setItem(key, value)
302
- storageCache.set(key, value) // keep cache in sync
303
- }
304
- ```
305
-
306
- Use a Map (not a hook) so it works everywhere: utilities, event handlers, not just React components.
307
-
308
- **Cookie caching:**
309
-
310
- ```typescript
311
- let cookieCache: Record<string, string> | null = null
312
-
313
- function getCookie(name: string) {
314
- if (!cookieCache) {
315
- cookieCache = Object.fromEntries(
316
- document.cookie.split('; ').map(c => c.split('='))
317
- )
318
- }
319
- return cookieCache[name]
320
- }
321
- ```
322
-
323
- **Important (invalidate on external changes):**
324
-
325
- If storage can change externally (another tab, server-set cookies), invalidate cache:
326
-
327
- ```typescript
328
- window.addEventListener('storage', (e) => {
329
- if (e.key) storageCache.delete(e.key)
330
- })
331
-
332
- document.addEventListener('visibilitychange', () => {
333
- if (document.visibilityState === 'visible') {
334
- storageCache.clear()
335
- }
336
- })
337
- ```
338
-
339
- ---
340
-
341
- ## Rule 7.6: Combine Multiple Array Iterations
342
-
343
- **Impact:** LOW-MEDIUM
344
- **Tags:** javascript, arrays, loops, performance
345
-
346
- ## Combine Multiple Array Iterations
347
-
348
- Multiple `.filter()` or `.map()` calls iterate the array multiple times. Combine into one loop.
349
-
350
- **Incorrect (3 iterations):**
351
-
352
- ```typescript
353
- const admins = users.filter(u => u.isAdmin)
354
- const testers = users.filter(u => u.isTester)
355
- const inactive = users.filter(u => !u.isActive)
356
- ```
357
-
358
- **Correct (1 iteration):**
359
-
360
- ```typescript
361
- const admins: User[] = []
362
- const testers: User[] = []
363
- const inactive: User[] = []
364
-
365
- for (const user of users) {
366
- if (user.isAdmin) admins.push(user)
367
- if (user.isTester) testers.push(user)
368
- if (!user.isActive) inactive.push(user)
369
- }
370
- ```
371
-
372
- ---
373
-
374
- ## Rule 7.7: Early Length Check for Array Comparisons
375
-
376
- **Impact:** MEDIUM-HIGH
377
- **Tags:** javascript, arrays, performance, optimization, comparison
378
-
379
- ## Early Length Check for Array Comparisons
380
-
381
- When comparing arrays with expensive operations (sorting, deep equality, serialization), check lengths first. If lengths differ, the arrays cannot be equal.
382
-
383
- In real-world applications, this optimization is especially valuable when the comparison runs in hot paths (event handlers, render loops).
384
-
385
- **Incorrect (always runs expensive comparison):**
386
-
387
- ```typescript
388
- function hasChanges(current: string[], original: string[]) {
389
- // Always sorts and joins, even when lengths differ
390
- return current.sort().join() !== original.sort().join()
391
- }
392
- ```
393
-
394
- Two O(n log n) sorts run even when `current.length` is 5 and `original.length` is 100. There is also overhead of joining the arrays and comparing the strings.
395
-
396
- **Correct (O(1) length check first):**
397
-
398
- ```typescript
399
- function hasChanges(current: string[], original: string[]) {
400
- // Early return if lengths differ
401
- if (current.length !== original.length) {
402
- return true
403
- }
404
- // Only sort when lengths match
405
- const currentSorted = current.toSorted()
406
- const originalSorted = original.toSorted()
407
- for (let i = 0; i < currentSorted.length; i++) {
408
- if (currentSorted[i] !== originalSorted[i]) {
409
- return true
410
- }
411
- }
412
- return false
413
- }
414
- ```
415
-
416
- This new approach is more efficient because:
417
- - It avoids the overhead of sorting and joining the arrays when lengths differ
418
- - It avoids consuming memory for the joined strings (especially important for large arrays)
419
- - It avoids mutating the original arrays
420
- - It returns early when a difference is found
421
-
422
- ---
423
-
424
- ## Rule 7.8: Early Return from Functions
425
-
426
- **Impact:** LOW-MEDIUM
427
- **Tags:** javascript, functions, optimization, early-return
428
-
429
- ## Early Return from Functions
430
-
431
- Return early when result is determined to skip unnecessary processing.
432
-
433
- **Incorrect (processes all items even after finding answer):**
434
-
435
- ```typescript
436
- function validateUsers(users: User[]) {
437
- let hasError = false
438
- let errorMessage = ''
439
-
440
- for (const user of users) {
441
- if (!user.email) {
442
- hasError = true
443
- errorMessage = 'Email required'
444
- }
445
- if (!user.name) {
446
- hasError = true
447
- errorMessage = 'Name required'
448
- }
449
- // Continues checking all users even after error found
450
- }
451
-
452
- return hasError ? { valid: false, error: errorMessage } : { valid: true }
453
- }
454
- ```
455
-
456
- **Correct (returns immediately on first error):**
457
-
458
- ```typescript
459
- function validateUsers(users: User[]) {
460
- for (const user of users) {
461
- if (!user.email) {
462
- return { valid: false, error: 'Email required' }
463
- }
464
- if (!user.name) {
465
- return { valid: false, error: 'Name required' }
466
- }
467
- }
468
-
469
- return { valid: true }
470
- }
471
- ```
472
-
473
- ---
474
-
475
- ## Rule 7.9: Hoist RegExp Creation
476
-
477
- **Impact:** LOW-MEDIUM
478
- **Tags:** javascript, regexp, optimization, memoization
479
-
480
- ## Hoist RegExp Creation
481
-
482
- Don't create RegExp inside render. Hoist to module scope or memoize with `useMemo()`.
483
-
484
- **Incorrect (new RegExp every render):**
485
-
486
- ```tsx
487
- function Highlighter({ text, query }: Props) {
488
- const regex = new RegExp(`(${query})`, 'gi')
489
- const parts = text.split(regex)
490
- return <>{parts.map((part, i) => ...)}</>
491
- }
492
- ```
493
-
494
- **Correct (memoize or hoist):**
495
-
496
- ```tsx
497
- const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
498
-
499
- function Highlighter({ text, query }: Props) {
500
- const regex = useMemo(
501
- () => new RegExp(`(${escapeRegex(query)})`, 'gi'),
502
- [query]
503
- )
504
- const parts = text.split(regex)
505
- return <>{parts.map((part, i) => ...)}</>
506
- }
507
- ```
508
-
509
- **Warning (global regex has mutable state):**
510
-
511
- Global regex (`/g`) has mutable `lastIndex` state:
512
-
513
- ```typescript
514
- const regex = /foo/g
515
- regex.test('foo') // true, lastIndex = 3
516
- regex.test('foo') // false, lastIndex = 0
517
- ```
518
-
519
- ---
520
-
521
- ## Rule 7.10: Use Loop for Min/Max Instead of Sort
522
-
523
- **Impact:** LOW
524
- **Tags:** javascript, arrays, performance, sorting, algorithms
525
-
526
- ## Use Loop for Min/Max Instead of Sort
527
-
528
- Finding the smallest or largest element only requires a single pass through the array. Sorting is wasteful and slower.
529
-
530
- **Incorrect (O(n log n) - sort to find latest):**
531
-
532
- ```typescript
533
- interface Project {
534
- id: string
535
- name: string
536
- updatedAt: number
537
- }
538
-
539
- function getLatestProject(projects: Project[]) {
540
- const sorted = [...projects].sort((a, b) => b.updatedAt - a.updatedAt)
541
- return sorted[0]
542
- }
543
- ```
544
-
545
- Sorts the entire array just to find the maximum value.
546
-
547
- **Incorrect (O(n log n) - sort for oldest and newest):**
548
-
549
- ```typescript
550
- function getOldestAndNewest(projects: Project[]) {
551
- const sorted = [...projects].sort((a, b) => a.updatedAt - b.updatedAt)
552
- return { oldest: sorted[0], newest: sorted[sorted.length - 1] }
553
- }
554
- ```
555
-
556
- Still sorts unnecessarily when only min/max are needed.
557
-
558
- **Correct (O(n) - single loop):**
559
-
560
- ```typescript
561
- function getLatestProject(projects: Project[]) {
562
- if (projects.length === 0) return null
563
-
564
- let latest = projects[0]
565
-
566
- for (let i = 1; i < projects.length; i++) {
567
- if (projects[i].updatedAt > latest.updatedAt) {
568
- latest = projects[i]
569
- }
570
- }
571
-
572
- return latest
573
- }
574
-
575
- function getOldestAndNewest(projects: Project[]) {
576
- if (projects.length === 0) return { oldest: null, newest: null }
577
-
578
- let oldest = projects[0]
579
- let newest = projects[0]
580
-
581
- for (let i = 1; i < projects.length; i++) {
582
- if (projects[i].updatedAt < oldest.updatedAt) oldest = projects[i]
583
- if (projects[i].updatedAt > newest.updatedAt) newest = projects[i]
584
- }
585
-
586
- return { oldest, newest }
587
- }
588
- ```
589
-
590
- Single pass through the array, no copying, no sorting.
591
-
592
- **Alternative (Math.min/Math.max for small arrays):**
593
-
594
- ```typescript
595
- const numbers = [5, 2, 8, 1, 9]
596
- const min = Math.min(...numbers)
597
- const max = Math.max(...numbers)
598
- ```
599
-
600
- This works for small arrays, but can be slower or just throw an error for very large arrays due to spread operator limitations. Maximal array length is approximately 124000 in Chrome 143 and 638000 in Safari 18; exact numbers may vary - see [the fiddle](https://jsfiddle.net/qw1jabsx/4/). Use the loop approach for reliability.
601
-
602
- ---
603
-
604
- ## Rule 7.11: Use Set/Map for O(1) Lookups
605
-
606
- **Impact:** LOW-MEDIUM
607
- **Tags:** javascript, set, map, data-structures, performance
608
-
609
- ## Use Set/Map for O(1) Lookups
610
-
611
- Convert arrays to Set/Map for repeated membership checks.
612
-
613
- **Incorrect (O(n) per check):**
614
-
615
- ```typescript
616
- const allowedIds = ['a', 'b', 'c', ...]
617
- items.filter(item => allowedIds.includes(item.id))
618
- ```
619
-
620
- **Correct (O(1) per check):**
621
-
622
- ```typescript
623
- const allowedIds = new Set(['a', 'b', 'c', ...])
624
- items.filter(item => allowedIds.has(item.id))
625
- ```
626
-
627
- ---
628
-
629
- ## Rule 7.12: Use toSorted() Instead of sort() for Immutability
630
-
631
- **Impact:** MEDIUM-HIGH
632
- **Tags:** javascript, arrays, immutability, react, state, mutation
633
-
634
- ## Use toSorted() Instead of sort() for Immutability
635
-
636
- `.sort()` mutates the array in place, which can cause bugs with React state and props. Use `.toSorted()` to create a new sorted array without mutation.
637
-
638
- **Incorrect (mutates original array):**
639
-
640
- ```typescript
641
- function UserList({ users }: { users: User[] }) {
642
- // Mutates the users prop array!
643
- const sorted = useMemo(
644
- () => users.sort((a, b) => a.name.localeCompare(b.name)),
645
- [users]
646
- )
647
- return <div>{sorted.map(renderUser)}</div>
648
- }
649
- ```
650
-
651
- **Correct (creates new array):**
652
-
653
- ```typescript
654
- function UserList({ users }: { users: User[] }) {
655
- // Creates new sorted array, original unchanged
656
- const sorted = useMemo(
657
- () => users.toSorted((a, b) => a.name.localeCompare(b.name)),
658
- [users]
659
- )
660
- return <div>{sorted.map(renderUser)}</div>
661
- }
662
- ```
663
-
664
- **Why this matters in React:**
665
-
666
- 1. Props/state mutations break React's immutability model - React expects props and state to be treated as read-only
667
- 2. Causes stale closure bugs - Mutating arrays inside closures (callbacks, effects) can lead to unexpected behavior
668
-
669
- **Browser support (fallback for older browsers):**
670
-
671
- `.toSorted()` is available in all modern browsers (Chrome 110+, Safari 16+, Firefox 115+, Node.js 20+). For older environments, use spread operator:
672
-
673
- ```typescript
674
- // Fallback for older browsers
675
- const sorted = [...items].sort((a, b) => a.value - b.value)
676
- ```
677
-
678
- **Other immutable array methods:**
679
-
680
- - `.toSorted()` - immutable sort
681
- - `.toReversed()` - immutable reverse
682
- - `.toSpliced()` - immutable splice
683
- - `.with()` - immutable element replacement
684
-