@brookmind/ai-toolkit 1.0.5 → 1.1.2

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 (152) hide show
  1. package/README.md +60 -14
  2. package/agents/code-reviewer.md +6 -1
  3. package/agents/code-simplifier.md +52 -0
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +257 -220
  6. package/dist/index.js.map +1 -1
  7. package/mcps/context7/.mcp.json +13 -0
  8. package/mcps/expo-mcp/.mcp.json +13 -0
  9. package/mcps/figma-mcp/.mcp.json +4 -6
  10. package/package.json +4 -4
  11. package/skills/pdf-processing-pro/FORMS.md +610 -0
  12. package/skills/pdf-processing-pro/OCR.md +137 -0
  13. package/skills/pdf-processing-pro/SKILL.md +296 -0
  14. package/skills/pdf-processing-pro/TABLES.md +626 -0
  15. package/skills/pdf-processing-pro/scripts/analyze_form.py +307 -0
  16. package/skills/react-best-practices/AGENTS.md +915 -0
  17. package/skills/react-best-practices/README.md +127 -0
  18. package/skills/react-best-practices/SKILL.md +110 -0
  19. package/skills/react-best-practices/metadata.json +14 -0
  20. package/skills/react-best-practices/rules/_sections.md +41 -0
  21. package/skills/react-best-practices/rules/_template.md +28 -0
  22. package/skills/react-best-practices/rules/advanced-event-handler-refs.md +80 -0
  23. package/skills/react-best-practices/rules/advanced-use-latest.md +76 -0
  24. package/skills/react-best-practices/rules/async-defer-await.md +80 -0
  25. package/skills/react-best-practices/rules/async-dependencies.md +36 -0
  26. package/skills/react-best-practices/rules/async-parallel.md +28 -0
  27. package/skills/react-best-practices/rules/async-suspense-boundaries.md +100 -0
  28. package/skills/react-best-practices/rules/bundle-barrel-imports.md +42 -0
  29. package/skills/react-best-practices/rules/bundle-conditional.md +106 -0
  30. package/skills/react-best-practices/rules/bundle-preload.md +44 -0
  31. package/skills/react-best-practices/rules/client-event-listeners.md +131 -0
  32. package/skills/react-best-practices/rules/client-swr-dedup.md +133 -0
  33. package/skills/react-best-practices/rules/js-batch-dom-css.md +82 -0
  34. package/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
  35. package/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
  36. package/skills/react-best-practices/rules/js-cache-storage.md +70 -0
  37. package/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
  38. package/skills/react-best-practices/rules/js-early-exit.md +50 -0
  39. package/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
  40. package/skills/react-best-practices/rules/js-index-maps.md +37 -0
  41. package/skills/react-best-practices/rules/js-length-check-first.md +49 -0
  42. package/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
  43. package/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
  44. package/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  45. package/skills/react-best-practices/rules/rendering-activity.md +90 -0
  46. package/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  47. package/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
  48. package/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
  49. package/skills/react-best-practices/rules/rendering-hoist-jsx.md +65 -0
  50. package/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
  51. package/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
  52. package/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
  53. package/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
  54. package/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
  55. package/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  56. package/skills/react-best-practices/rules/rerender-memo.md +85 -0
  57. package/skills/react-best-practices/rules/rerender-transitions.md +40 -0
  58. package/themes/README.md +68 -0
  59. package/themes/claude-vivid.json +72 -0
  60. package/mcps/context7/.claude-plugin +0 -1
  61. package/mcps/context7/README.md +0 -1
  62. package/mcps/context7/server.json +0 -1
  63. package/mcps/expo-mcp/README.md +0 -33
  64. package/mcps/expo-mcp/package.json +0 -30
  65. package/mcps/figma-mcp/README.md +0 -554
  66. package/mcps/figma-mcp/server.json +0 -17
  67. package/mcps/figma-mcp/skills/code-connect-components +0 -1
  68. package/mcps/figma-mcp/skills/create-design-system-rules +0 -1
  69. package/mcps/figma-mcp/skills/implement-design +0 -1
  70. package/mcps/pg-aiguide/.claude-plugin +0 -1
  71. package/mcps/pg-aiguide/CLAUDE.md +0 -21
  72. package/mcps/pg-aiguide/README.md +0 -275
  73. package/mcps/pg-aiguide/skills/design-postgres-tables +0 -1
  74. package/mcps/pg-aiguide/skills/find-hypertable-candidates +0 -1
  75. package/mcps/pg-aiguide/skills/migrate-postgres-tables-to-hypertables +0 -1
  76. package/mcps/pg-aiguide/skills/setup-timescaledb-hypertables +0 -1
  77. package/mcps/pg-aiguide/skills.yaml +0 -4
  78. package/skills/cloudflare-cli/SKILL.md +0 -151
  79. package/skills/docx/LICENSE.txt +0 -30
  80. package/skills/docx/SKILL.md +0 -197
  81. package/skills/docx/docx-js.md +0 -350
  82. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
  83. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
  84. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
  85. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
  86. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
  87. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
  88. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
  89. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
  90. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
  91. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
  92. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
  93. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
  94. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
  95. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
  96. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
  97. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
  98. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
  99. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
  100. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
  101. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
  102. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
  103. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
  104. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
  105. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
  106. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
  107. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
  108. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
  109. package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
  110. package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
  111. package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
  112. package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
  113. package/skills/docx/ooxml/schemas/mce/mc.xsd +0 -75
  114. package/skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
  115. package/skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
  116. package/skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
  117. package/skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
  118. package/skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
  119. package/skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
  120. package/skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
  121. package/skills/docx/ooxml/scripts/pack.py +0 -159
  122. package/skills/docx/ooxml/scripts/unpack.py +0 -29
  123. package/skills/docx/ooxml/scripts/validate.py +0 -69
  124. package/skills/docx/ooxml/scripts/validation/__init__.py +0 -15
  125. package/skills/docx/ooxml/scripts/validation/base.py +0 -951
  126. package/skills/docx/ooxml/scripts/validation/docx.py +0 -274
  127. package/skills/docx/ooxml/scripts/validation/pptx.py +0 -315
  128. package/skills/docx/ooxml/scripts/validation/redlining.py +0 -279
  129. package/skills/docx/ooxml.md +0 -610
  130. package/skills/docx/scripts/__init__.py +0 -1
  131. package/skills/docx/scripts/document.py +0 -1276
  132. package/skills/docx/scripts/templates/comments.xml +0 -3
  133. package/skills/docx/scripts/templates/commentsExtended.xml +0 -3
  134. package/skills/docx/scripts/templates/commentsExtensible.xml +0 -3
  135. package/skills/docx/scripts/templates/commentsIds.xml +0 -3
  136. package/skills/docx/scripts/templates/people.xml +0 -3
  137. package/skills/docx/scripts/utilities.py +0 -374
  138. package/skills/pdf/LICENSE.txt +0 -30
  139. package/skills/pdf/SKILL.md +0 -294
  140. package/skills/pdf/forms.md +0 -205
  141. package/skills/pdf/reference.md +0 -612
  142. package/skills/pdf/scripts/check_bounding_boxes.py +0 -70
  143. package/skills/pdf/scripts/check_bounding_boxes_test.py +0 -226
  144. package/skills/pdf/scripts/check_fillable_fields.py +0 -12
  145. package/skills/pdf/scripts/convert_pdf_to_images.py +0 -35
  146. package/skills/pdf/scripts/create_validation_image.py +0 -41
  147. package/skills/pdf/scripts/extract_form_field_info.py +0 -152
  148. package/skills/pdf/scripts/fill_fillable_fields.py +0 -114
  149. package/skills/pdf/scripts/fill_pdf_form_with_annotations.py +0 -108
  150. package/skills/xlsx/LICENSE.txt +0 -30
  151. package/skills/xlsx/SKILL.md +0 -289
  152. package/skills/xlsx/recalc.py +0 -178
@@ -0,0 +1,80 @@
1
+ ---
2
+ title: Cache Repeated Function Calls
3
+ impact: MEDIUM
4
+ impactDescription: avoid redundant computation
5
+ tags: javascript, cache, memoization, performance
6
+ ---
7
+
8
+ ## Cache Repeated Function Calls
9
+
10
+ Use a module-level Map to cache function results when the same function is called repeatedly with the same inputs during render.
11
+
12
+ **Incorrect (redundant computation):**
13
+
14
+ ```typescript
15
+ function ProjectList({ projects }: { projects: Project[] }) {
16
+ return (
17
+ <div>
18
+ {projects.map(project => {
19
+ // slugify() called 100+ times for same project names
20
+ const slug = slugify(project.name)
21
+
22
+ return <ProjectCard key={project.id} slug={slug} />
23
+ })}
24
+ </div>
25
+ )
26
+ }
27
+ ```
28
+
29
+ **Correct (cached results):**
30
+
31
+ ```typescript
32
+ // Module-level cache
33
+ const slugifyCache = new Map<string, string>()
34
+
35
+ function cachedSlugify(text: string): string {
36
+ if (slugifyCache.has(text)) {
37
+ return slugifyCache.get(text)!
38
+ }
39
+ const result = slugify(text)
40
+ slugifyCache.set(text, result)
41
+ return result
42
+ }
43
+
44
+ function ProjectList({ projects }: { projects: Project[] }) {
45
+ return (
46
+ <div>
47
+ {projects.map(project => {
48
+ // Computed only once per unique project name
49
+ const slug = cachedSlugify(project.name)
50
+
51
+ return <ProjectCard key={project.id} slug={slug} />
52
+ })}
53
+ </div>
54
+ )
55
+ }
56
+ ```
57
+
58
+ **Simpler pattern for single-value functions:**
59
+
60
+ ```typescript
61
+ let isLoggedInCache: boolean | null = null
62
+
63
+ function isLoggedIn(): boolean {
64
+ if (isLoggedInCache !== null) {
65
+ return isLoggedInCache
66
+ }
67
+
68
+ isLoggedInCache = document.cookie.includes('auth=')
69
+ return isLoggedInCache
70
+ }
71
+
72
+ // Clear cache when auth changes
73
+ function onAuthChange() {
74
+ isLoggedInCache = null
75
+ }
76
+ ```
77
+
78
+ Use a Map (not a hook) so it works everywhere: utilities, event handlers, not just React components.
79
+
80
+ Reference: [How we made the Vercel Dashboard twice as fast](https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast)
@@ -0,0 +1,28 @@
1
+ ---
2
+ title: Cache Property Access in Loops
3
+ impact: LOW-MEDIUM
4
+ impactDescription: reduces lookups
5
+ tags: javascript, loops, optimization, caching
6
+ ---
7
+
8
+ ## Cache Property Access in Loops
9
+
10
+ Cache object property lookups in hot paths.
11
+
12
+ **Incorrect (3 lookups × N iterations):**
13
+
14
+ ```typescript
15
+ for (let i = 0; i < arr.length; i++) {
16
+ process(obj.config.settings.value)
17
+ }
18
+ ```
19
+
20
+ **Correct (1 lookup total):**
21
+
22
+ ```typescript
23
+ const value = obj.config.settings.value
24
+ const len = arr.length
25
+ for (let i = 0; i < len; i++) {
26
+ process(value)
27
+ }
28
+ ```
@@ -0,0 +1,70 @@
1
+ ---
2
+ title: Cache Storage API Calls
3
+ impact: LOW-MEDIUM
4
+ impactDescription: reduces expensive I/O
5
+ tags: javascript, localStorage, storage, caching, performance
6
+ ---
7
+
8
+ ## Cache Storage API Calls
9
+
10
+ `localStorage`, `sessionStorage`, and `document.cookie` are synchronous and expensive. Cache reads in memory.
11
+
12
+ **Incorrect (reads storage on every call):**
13
+
14
+ ```typescript
15
+ function getTheme() {
16
+ return localStorage.getItem('theme') ?? 'light'
17
+ }
18
+ // Called 10 times = 10 storage reads
19
+ ```
20
+
21
+ **Correct (Map cache):**
22
+
23
+ ```typescript
24
+ const storageCache = new Map<string, string | null>()
25
+
26
+ function getLocalStorage(key: string) {
27
+ if (!storageCache.has(key)) {
28
+ storageCache.set(key, localStorage.getItem(key))
29
+ }
30
+ return storageCache.get(key)
31
+ }
32
+
33
+ function setLocalStorage(key: string, value: string) {
34
+ localStorage.setItem(key, value)
35
+ storageCache.set(key, value) // keep cache in sync
36
+ }
37
+ ```
38
+
39
+ Use a Map (not a hook) so it works everywhere: utilities, event handlers, not just React components.
40
+
41
+ **Cookie caching:**
42
+
43
+ ```typescript
44
+ let cookieCache: Record<string, string> | null = null
45
+
46
+ function getCookie(name: string) {
47
+ if (!cookieCache) {
48
+ cookieCache = Object.fromEntries(
49
+ document.cookie.split('; ').map(c => c.split('='))
50
+ )
51
+ }
52
+ return cookieCache[name]
53
+ }
54
+ ```
55
+
56
+ **Important (invalidate on external changes):**
57
+
58
+ If storage can change externally (another tab, server-set cookies), invalidate cache:
59
+
60
+ ```typescript
61
+ window.addEventListener('storage', (e) => {
62
+ if (e.key) storageCache.delete(e.key)
63
+ })
64
+
65
+ document.addEventListener('visibilitychange', () => {
66
+ if (document.visibilityState === 'visible') {
67
+ storageCache.clear()
68
+ }
69
+ })
70
+ ```
@@ -0,0 +1,32 @@
1
+ ---
2
+ title: Combine Multiple Array Iterations
3
+ impact: LOW-MEDIUM
4
+ impactDescription: reduces iterations
5
+ tags: javascript, arrays, loops, performance
6
+ ---
7
+
8
+ ## Combine Multiple Array Iterations
9
+
10
+ Multiple `.filter()` or `.map()` calls iterate the array multiple times. Combine into one loop.
11
+
12
+ **Incorrect (3 iterations):**
13
+
14
+ ```typescript
15
+ const admins = users.filter(u => u.isAdmin)
16
+ const testers = users.filter(u => u.isTester)
17
+ const inactive = users.filter(u => !u.isActive)
18
+ ```
19
+
20
+ **Correct (1 iteration):**
21
+
22
+ ```typescript
23
+ const admins: User[] = []
24
+ const testers: User[] = []
25
+ const inactive: User[] = []
26
+
27
+ for (const user of users) {
28
+ if (user.isAdmin) admins.push(user)
29
+ if (user.isTester) testers.push(user)
30
+ if (!user.isActive) inactive.push(user)
31
+ }
32
+ ```
@@ -0,0 +1,50 @@
1
+ ---
2
+ title: Early Return from Functions
3
+ impact: LOW-MEDIUM
4
+ impactDescription: avoids unnecessary computation
5
+ tags: javascript, functions, optimization, early-return
6
+ ---
7
+
8
+ ## Early Return from Functions
9
+
10
+ Return early when result is determined to skip unnecessary processing.
11
+
12
+ **Incorrect (processes all items even after finding answer):**
13
+
14
+ ```typescript
15
+ function validateUsers(users: User[]) {
16
+ let hasError = false
17
+ let errorMessage = ''
18
+
19
+ for (const user of users) {
20
+ if (!user.email) {
21
+ hasError = true
22
+ errorMessage = 'Email required'
23
+ }
24
+ if (!user.name) {
25
+ hasError = true
26
+ errorMessage = 'Name required'
27
+ }
28
+ // Continues checking all users even after error found
29
+ }
30
+
31
+ return hasError ? { valid: false, error: errorMessage } : { valid: true }
32
+ }
33
+ ```
34
+
35
+ **Correct (returns immediately on first error):**
36
+
37
+ ```typescript
38
+ function validateUsers(users: User[]) {
39
+ for (const user of users) {
40
+ if (!user.email) {
41
+ return { valid: false, error: 'Email required' }
42
+ }
43
+ if (!user.name) {
44
+ return { valid: false, error: 'Name required' }
45
+ }
46
+ }
47
+
48
+ return { valid: true }
49
+ }
50
+ ```
@@ -0,0 +1,45 @@
1
+ ---
2
+ title: Hoist RegExp Creation
3
+ impact: LOW-MEDIUM
4
+ impactDescription: avoids recreation
5
+ tags: javascript, regexp, optimization, memoization
6
+ ---
7
+
8
+ ## Hoist RegExp Creation
9
+
10
+ Don't create RegExp inside render. Hoist to module scope or memoize with `useMemo()`.
11
+
12
+ **Incorrect (new RegExp every render):**
13
+
14
+ ```tsx
15
+ function Highlighter({ text, query }: Props) {
16
+ const regex = new RegExp(`(${query})`, 'gi')
17
+ const parts = text.split(regex)
18
+ return <>{parts.map((part, i) => ...)}</>
19
+ }
20
+ ```
21
+
22
+ **Correct (memoize or hoist):**
23
+
24
+ ```tsx
25
+ const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
26
+
27
+ function Highlighter({ text, query }: Props) {
28
+ const regex = useMemo(
29
+ () => new RegExp(`(${escapeRegex(query)})`, 'gi'),
30
+ [query]
31
+ )
32
+ const parts = text.split(regex)
33
+ return <>{parts.map((part, i) => ...)}</>
34
+ }
35
+ ```
36
+
37
+ **Warning (global regex has mutable state):**
38
+
39
+ Global regex (`/g`) has mutable `lastIndex` state:
40
+
41
+ ```typescript
42
+ const regex = /foo/g
43
+ regex.test('foo') // true, lastIndex = 3
44
+ regex.test('foo') // false, lastIndex = 0
45
+ ```
@@ -0,0 +1,37 @@
1
+ ---
2
+ title: Build Index Maps for Repeated Lookups
3
+ impact: LOW-MEDIUM
4
+ impactDescription: 1M ops to 2K ops
5
+ tags: javascript, map, indexing, optimization, performance
6
+ ---
7
+
8
+ ## Build Index Maps for Repeated Lookups
9
+
10
+ Multiple `.find()` calls by the same key should use a Map.
11
+
12
+ **Incorrect (O(n) per lookup):**
13
+
14
+ ```typescript
15
+ function processOrders(orders: Order[], users: User[]) {
16
+ return orders.map(order => ({
17
+ ...order,
18
+ user: users.find(u => u.id === order.userId)
19
+ }))
20
+ }
21
+ ```
22
+
23
+ **Correct (O(1) per lookup):**
24
+
25
+ ```typescript
26
+ function processOrders(orders: Order[], users: User[]) {
27
+ const userById = new Map(users.map(u => [u.id, u]))
28
+
29
+ return orders.map(order => ({
30
+ ...order,
31
+ user: userById.get(order.userId)
32
+ }))
33
+ }
34
+ ```
35
+
36
+ Build map once (O(n)), then all lookups are O(1).
37
+ For 1000 orders × 1000 users: 1M ops → 2K ops.
@@ -0,0 +1,49 @@
1
+ ---
2
+ title: Early Length Check for Array Comparisons
3
+ impact: MEDIUM-HIGH
4
+ impactDescription: avoids expensive operations when lengths differ
5
+ tags: javascript, arrays, performance, optimization, comparison
6
+ ---
7
+
8
+ ## Early Length Check for Array Comparisons
9
+
10
+ When comparing arrays with expensive operations (sorting, deep equality, serialization), check lengths first. If lengths differ, the arrays cannot be equal.
11
+
12
+ In real-world applications, this optimization is especially valuable when the comparison runs in hot paths (event handlers, render loops).
13
+
14
+ **Incorrect (always runs expensive comparison):**
15
+
16
+ ```typescript
17
+ function hasChanges(current: string[], original: string[]) {
18
+ // Always sorts and joins, even when lengths differ
19
+ return current.sort().join() !== original.sort().join()
20
+ }
21
+ ```
22
+
23
+ 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.
24
+
25
+ **Correct (O(1) length check first):**
26
+
27
+ ```typescript
28
+ function hasChanges(current: string[], original: string[]) {
29
+ // Early return if lengths differ
30
+ if (current.length !== original.length) {
31
+ return true
32
+ }
33
+ // Only sort/join when lengths match
34
+ const currentSorted = current.toSorted()
35
+ const originalSorted = original.toSorted()
36
+ for (let i = 0; i < currentSorted.length; i++) {
37
+ if (currentSorted[i] !== originalSorted[i]) {
38
+ return true
39
+ }
40
+ }
41
+ return false
42
+ }
43
+ ```
44
+
45
+ This new approach is more efficient because:
46
+ - It avoids the overhead of sorting and joining the arrays when lengths differ
47
+ - It avoids consuming memory for the joined strings (especially important for large arrays)
48
+ - It avoids mutating the original arrays
49
+ - It returns early when a difference is found
@@ -0,0 +1,82 @@
1
+ ---
2
+ title: Use Loop for Min/Max Instead of Sort
3
+ impact: LOW
4
+ impactDescription: O(n) instead of O(n log n)
5
+ tags: javascript, arrays, performance, sorting, algorithms
6
+ ---
7
+
8
+ ## Use Loop for Min/Max Instead of Sort
9
+
10
+ Finding the smallest or largest element only requires a single pass through the array. Sorting is wasteful and slower.
11
+
12
+ **Incorrect (O(n log n) - sort to find latest):**
13
+
14
+ ```typescript
15
+ interface Project {
16
+ id: string
17
+ name: string
18
+ updatedAt: number
19
+ }
20
+
21
+ function getLatestProject(projects: Project[]) {
22
+ const sorted = [...projects].sort((a, b) => b.updatedAt - a.updatedAt)
23
+ return sorted[0]
24
+ }
25
+ ```
26
+
27
+ Sorts the entire array just to find the maximum value.
28
+
29
+ **Incorrect (O(n log n) - sort for oldest and newest):**
30
+
31
+ ```typescript
32
+ function getOldestAndNewest(projects: Project[]) {
33
+ const sorted = [...projects].sort((a, b) => a.updatedAt - b.updatedAt)
34
+ return { oldest: sorted[0], newest: sorted[sorted.length - 1] }
35
+ }
36
+ ```
37
+
38
+ Still sorts unnecessarily when only min/max are needed.
39
+
40
+ **Correct (O(n) - single loop):**
41
+
42
+ ```typescript
43
+ function getLatestProject(projects: Project[]) {
44
+ if (projects.length === 0) return null
45
+
46
+ let latest = projects[0]
47
+
48
+ for (let i = 1; i < projects.length; i++) {
49
+ if (projects[i].updatedAt > latest.updatedAt) {
50
+ latest = projects[i]
51
+ }
52
+ }
53
+
54
+ return latest
55
+ }
56
+
57
+ function getOldestAndNewest(projects: Project[]) {
58
+ if (projects.length === 0) return { oldest: null, newest: null }
59
+
60
+ let oldest = projects[0]
61
+ let newest = projects[0]
62
+
63
+ for (let i = 1; i < projects.length; i++) {
64
+ if (projects[i].updatedAt < oldest.updatedAt) oldest = projects[i]
65
+ if (projects[i].updatedAt > newest.updatedAt) newest = projects[i]
66
+ }
67
+
68
+ return { oldest, newest }
69
+ }
70
+ ```
71
+
72
+ Single pass through the array, no copying, no sorting.
73
+
74
+ **Alternative (Math.min/Math.max for small arrays):**
75
+
76
+ ```typescript
77
+ const numbers = [5, 2, 8, 1, 9]
78
+ const min = Math.min(...numbers)
79
+ const max = Math.max(...numbers)
80
+ ```
81
+
82
+ This works for small arrays but can be slower for very large arrays due to spread operator limitations. Use the loop approach for reliability.
@@ -0,0 +1,24 @@
1
+ ---
2
+ title: Use Set/Map for O(1) Lookups
3
+ impact: LOW-MEDIUM
4
+ impactDescription: O(n) to O(1)
5
+ tags: javascript, set, map, data-structures, performance
6
+ ---
7
+
8
+ ## Use Set/Map for O(1) Lookups
9
+
10
+ Convert arrays to Set/Map for repeated membership checks.
11
+
12
+ **Incorrect (O(n) per check):**
13
+
14
+ ```typescript
15
+ const allowedIds = ['a', 'b', 'c', ...]
16
+ items.filter(item => allowedIds.includes(item.id))
17
+ ```
18
+
19
+ **Correct (O(1) per check):**
20
+
21
+ ```typescript
22
+ const allowedIds = new Set(['a', 'b', 'c', ...])
23
+ items.filter(item => allowedIds.has(item.id))
24
+ ```
@@ -0,0 +1,57 @@
1
+ ---
2
+ title: Use toSorted() Instead of sort() for Immutability
3
+ impact: MEDIUM-HIGH
4
+ impactDescription: prevents mutation bugs in React state
5
+ tags: javascript, arrays, immutability, react, state, mutation
6
+ ---
7
+
8
+ ## Use toSorted() Instead of sort() for Immutability
9
+
10
+ `.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.
11
+
12
+ **Incorrect (mutates original array):**
13
+
14
+ ```typescript
15
+ function UserList({ users }: { users: User[] }) {
16
+ // Mutates the users prop array!
17
+ const sorted = useMemo(
18
+ () => users.sort((a, b) => a.name.localeCompare(b.name)),
19
+ [users]
20
+ )
21
+ return <div>{sorted.map(renderUser)}</div>
22
+ }
23
+ ```
24
+
25
+ **Correct (creates new array):**
26
+
27
+ ```typescript
28
+ function UserList({ users }: { users: User[] }) {
29
+ // Creates new sorted array, original unchanged
30
+ const sorted = useMemo(
31
+ () => users.toSorted((a, b) => a.name.localeCompare(b.name)),
32
+ [users]
33
+ )
34
+ return <div>{sorted.map(renderUser)}</div>
35
+ }
36
+ ```
37
+
38
+ **Why this matters in React:**
39
+
40
+ 1. Props/state mutations break React's immutability model - React expects props and state to be treated as read-only
41
+ 2. Causes stale closure bugs - Mutating arrays inside closures (callbacks, effects) can lead to unexpected behavior
42
+
43
+ **Browser support (fallback for older browsers):**
44
+
45
+ `.toSorted()` is available in all modern browsers (Chrome 110+, Safari 16+, Firefox 115+, Node.js 20+). For older environments, use spread operator:
46
+
47
+ ```typescript
48
+ // Fallback for older browsers
49
+ const sorted = [...items].sort((a, b) => a.value - b.value)
50
+ ```
51
+
52
+ **Other immutable array methods:**
53
+
54
+ - `.toSorted()` - immutable sort
55
+ - `.toReversed()` - immutable reverse
56
+ - `.toSpliced()` - immutable splice
57
+ - `.with()` - immutable element replacement
@@ -0,0 +1,90 @@
1
+ ---
2
+ title: Preserve State for Hidden Components
3
+ impact: MEDIUM
4
+ impactDescription: preserves state/DOM without unmounting
5
+ tags: rendering, visibility, state-preservation, css
6
+ ---
7
+
8
+ ## Preserve State for Hidden Components
9
+
10
+ When toggling visibility of expensive components, preserve their state and DOM to avoid re-initialization.
11
+
12
+ **CSS visibility pattern (Recommended - works today):**
13
+
14
+ ```tsx
15
+ function Dropdown({ isOpen }: { isOpen: boolean }) {
16
+ return (
17
+ <div
18
+ style={{
19
+ display: isOpen ? "block" : "none",
20
+ // Or: visibility: isOpen ? 'visible' : 'hidden'
21
+ }}
22
+ >
23
+ <ExpensiveMenu />
24
+ </div>
25
+ );
26
+ }
27
+ ```
28
+
29
+ - Component stays mounted, preserving state
30
+ - DOM stays in place, no re-creation cost
31
+ - No re-initialization of expensive child components
32
+
33
+ **With CSS classes (cleaner):**
34
+
35
+ ```tsx
36
+ function Dropdown({ isOpen }: { isOpen: boolean }) {
37
+ return (
38
+ <div className={isOpen ? 'visible' : 'hidden'}>
39
+ <ExpensiveMenu />
40
+ </div>
41
+ )
42
+ }
43
+
44
+ // CSS
45
+ .hidden {
46
+ display: none;
47
+ }
48
+ .visible {
49
+ display: block;
50
+ }
51
+ ```
52
+
53
+ **Incorrect (unmounts and remounts, loses state):**
54
+
55
+ ```tsx
56
+ function Dropdown({ isOpen }: { isOpen: boolean }) {
57
+ // Component unmounts when closed - loses all internal state!
58
+ return isOpen ? <ExpensiveMenu /> : null;
59
+ }
60
+ ```
61
+
62
+ **When to use each approach:**
63
+
64
+ | Pattern | Use when |
65
+ | -------------------- | ----------------------------------------- |
66
+ | `display: none` | Hide completely, remove from layout |
67
+ | `visibility: hidden` | Hide but preserve layout space |
68
+ | `opacity: 0` | Hide but allow animations/transitions |
69
+ | Conditional render | Component state doesn't need preservation |
70
+
71
+ **Future: React Activity API (Experimental)**
72
+
73
+ React is developing an `<Activity>` component for this pattern, but it's not yet stable:
74
+
75
+ ```tsx
76
+ // EXPERIMENTAL - Not yet in stable React
77
+ import { Activity } from "react";
78
+
79
+ function Dropdown({ isOpen }: { isOpen: boolean }) {
80
+ return (
81
+ <Activity mode={isOpen ? "visible" : "hidden"}>
82
+ <ExpensiveMenu />
83
+ </Activity>
84
+ );
85
+ }
86
+ ```
87
+
88
+ Use the CSS patterns above until `<Activity>` reaches stable release.
89
+
90
+ Reference: [React Activity RFC](https://github.com/reactjs/rfcs/pull/extracting-state-from-the-react-tree)
@@ -0,0 +1,47 @@
1
+ ---
2
+ title: Animate SVG Wrapper Instead of SVG Element
3
+ impact: LOW
4
+ impactDescription: enables hardware acceleration
5
+ tags: rendering, svg, css, animation, performance
6
+ ---
7
+
8
+ ## Animate SVG Wrapper Instead of SVG Element
9
+
10
+ Many browsers don't have hardware acceleration for CSS3 animations on SVG elements. Wrap SVG in a `<div>` and animate the wrapper instead.
11
+
12
+ **Incorrect (animating SVG directly - no hardware acceleration):**
13
+
14
+ ```tsx
15
+ function LoadingSpinner() {
16
+ return (
17
+ <svg
18
+ className="animate-spin"
19
+ width="24"
20
+ height="24"
21
+ viewBox="0 0 24 24"
22
+ >
23
+ <circle cx="12" cy="12" r="10" stroke="currentColor" />
24
+ </svg>
25
+ )
26
+ }
27
+ ```
28
+
29
+ **Correct (animating wrapper div - hardware accelerated):**
30
+
31
+ ```tsx
32
+ function LoadingSpinner() {
33
+ return (
34
+ <div className="animate-spin">
35
+ <svg
36
+ width="24"
37
+ height="24"
38
+ viewBox="0 0 24 24"
39
+ >
40
+ <circle cx="12" cy="12" r="10" stroke="currentColor" />
41
+ </svg>
42
+ </div>
43
+ )
44
+ }
45
+ ```
46
+
47
+ This applies to all CSS transforms and transitions (`transform`, `opacity`, `translate`, `scale`, `rotate`). The wrapper div allows browsers to use GPU acceleration for smoother animations.