Skip to content

Commit db24cc9

Browse files
committed
improvement(audits): follow aliased persist imports, require strict !== for filterUndefined, state the .with scope
1 parent f6c27c8 commit db24cc9

4 files changed

Lines changed: 12 additions & 12 deletions

File tree

‎.claude/rules/sim-react-performance.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ return items.sort(compare)
7777
return [...items].sort(compare)
7878
```
7979

80-
**Do NOT use `toSorted()` / `toReversed()` / `with()` / `toSpliced()`.** They are ES2023 *runtime* methods — and a tsconfig `"lib": ["ES2023"]` only makes them **type-check**, it does not make them **run**. Next/SWC compiles syntax but does **not** polyfill prototype methods, and the default browserslist still includes browsers without them (`toSorted` landed in Safari 16 / iOS 16, so any device capped at iOS 15 throws `TypeError: x.toSorted is not a function` and crashes the page). The perf difference vs `[...arr].sort()` is negligible (both allocate one array), so the copy-then-sort form is used everywhere: whether a module reaches the browser is not visible from its path, and `check:utils` bans the methods repo-wide.
80+
**Do NOT use `toSorted()` / `toReversed()` / `with()` / `toSpliced()`.** They are ES2023 *runtime* methods — and a tsconfig `"lib": ["ES2023"]` only makes them **type-check**, it does not make them **run**. Next/SWC compiles syntax but does **not** polyfill prototype methods, and the default browserslist still includes browsers without them (`toSorted` landed in Safari 16 / iOS 16, so any device capped at iOS 15 throws `TypeError: x.toSorted is not a function` and crashes the page). The perf difference vs `[...arr].sort()` is negligible (both allocate one array), so the copy-then-sort form is used everywhere: whether a module reaches the browser is not visible from its path. `check:utils` flags `toSorted`/`toReversed`/`toSpliced` repo-wide and `with` when called with a numeric index (an identifier index is indistinguishable from OpenTelemetry's `context.with`).
8181

8282
## Run independent awaits in parallel
8383

‎.cursor/rules/sim-react-performance.mdc‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ return items.sort(compare)
8080
return [...items].sort(compare)
8181
```
8282

83-
**Do NOT use `toSorted()` / `toReversed()` / `with()` / `toSpliced()`.** They are ES2023 *runtime* methods — and a tsconfig `"lib": ["ES2023"]` only makes them **type-check**, it does not make them **run**. Next/SWC compiles syntax but does **not** polyfill prototype methods, and the default browserslist still includes browsers without them (`toSorted` landed in Safari 16 / iOS 16, so any device capped at iOS 15 throws `TypeError: x.toSorted is not a function` and crashes the page). The perf difference vs `[...arr].sort()` is negligible (both allocate one array), so the copy-then-sort form is used everywhere: whether a module reaches the browser is not visible from its path, and `check:utils` bans the methods repo-wide.
83+
**Do NOT use `toSorted()` / `toReversed()` / `with()` / `toSpliced()`.** They are ES2023 *runtime* methods — and a tsconfig `"lib": ["ES2023"]` only makes them **type-check**, it does not make them **run**. Next/SWC compiles syntax but does **not** polyfill prototype methods, and the default browserslist still includes browsers without them (`toSorted` landed in Safari 16 / iOS 16, so any device capped at iOS 15 throws `TypeError: x.toSorted is not a function` and crashes the page). The perf difference vs `[...arr].sort()` is negligible (both allocate one array), so the copy-then-sort form is used everywhere: whether a module reaches the browser is not visible from its path. `check:utils` flags `toSorted`/`toReversed`/`toSpliced` repo-wide and `with` when called with a numeric index (an identifier index is indistinguishable from OpenTelemetry's `context.with`).
8484

8585
## Run independent awaits in parallel
8686

‎scripts/check-utils-enforcement.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ const BANNED_PATTERNS: Array<{
109109
},
110110
{
111111
pattern:
112-
/Object\.fromEntries\(\s*Object\.entries\([^()]*\)\s*\.filter\(\s*\(\[\s*\w*\s*,\s*(\w+)\s*\]\)\s*=>\s*\1\s*!==?\s*undefined\s*\)\s*,?\s*\)/g,
112+
/Object\.fromEntries\(\s*Object\.entries\([^()]*\)\s*\.filter\(\s*\(\[\s*\w*\s*,\s*(\w+)\s*\]\)\s*=>\s*\1\s*!==\s*undefined\s*\)\s*,?\s*\)/g,
113113
description: 'Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined))',
114114
suggestion: 'filterUndefined(obj) from @sim/utils/object',
115115
prefilter: FROM_ENTRIES,

‎scripts/check-zustand-v5-selectors.ts‎

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -294,23 +294,23 @@ function auditFile(file: string, source: string): Violation[] {
294294
return violations
295295
}
296296

297-
/** A `persist(` middleware call; `.persist(` (an instance method) is excluded. */
298-
const PERSIST_CALL_PATTERN = /(?<![.\w$])persist\s*(?:<[^()]*?>)?\s*\(/g
297+
/** The local name `persist` is imported under from `zustand/middleware`, including an alias. */
298+
const PERSIST_IMPORT =
299+
/import\s*\{[^}]*\bpersist\b(?:\s+as\s+(\w+))?[^}]*\}\s*from\s*'zustand\/middleware'/
299300

300301
/**
301302
* `.claude/rules/sim-stores.md`: every `persist` names its durable fields in `partialize`.
302303
* Without one, zustand writes the whole state — transient flags, drag state, `_hasHydrated` —
303304
* to storage and rehydrates it on the next load.
304305
*/
305306
function auditPersist(file: string, source: string): Violation[] {
306-
if (!/import\s*\{[^}]*\bpersist\b[^}]*\}\s*from\s*'zustand\/middleware'/.test(source)) return []
307+
const persistImport = PERSIST_IMPORT.exec(source)
308+
if (!persistImport) return []
309+
const local = persistImport[1] ?? 'persist'
310+
/** A call of the imported middleware; `.persist(` (an instance method) is excluded. */
311+
const persistCall = new RegExp(`(?<![.\\w$])${local}\\s*(?:<[^()]*?>)?\\s*\\(`, 'g')
307312
const violations: Violation[] = []
308-
PERSIST_CALL_PATTERN.lastIndex = 0
309-
for (
310-
let match = PERSIST_CALL_PATTERN.exec(source);
311-
match;
312-
match = PERSIST_CALL_PATTERN.exec(source)
313-
) {
313+
for (let match = persistCall.exec(source); match; match = persistCall.exec(source)) {
314314
if (hasSafeAnnotation(source, match.index)) continue
315315
const openParenIndex = match.index + match[0].length - 1
316316
const closeParenIndex = findMatchingParen(source, openParenIndex)

0 commit comments

Comments
 (0)