mirror of
https://github.com/stablyai/orca.git
synced 2026-10-01 00:02:10 +00:00
Review hardening for the churn probe and the widened lint rules. Probe: it no longer resolves a functional updater itself. Zustand keeps sole ownership of when and with what argument an updater runs, so the middleware cannot double-invoke it or hand it a stale state. Object partials still scope the scan to the write's own keys; updater and replace writes fall back to the full field list, which costs one Object.is per untouched field and nothing more, since the deep compare only runs on replaced references. store/index.ts installs the probe only when import.meta.env.DEV or e2eConfig.exposeStore is set, the same gate as __store exposure. Nothing in the app arms it, so a shipped build was paying a wrapper frame per write for a diagnostic it could never read. The cascade probe stays unconditional because crash telemetry arms it in the field. Site capture now skips any *-probe.ts frame; under the real composition the first non-node_modules frame was the cascade probe's wrapper, so every churn was attributed to react-commit-cascade-write-probe.ts:32 instead of the caller. Plugin: named-selector recording is restricted to module scope. A component-local `const selectRows = ...` used to overwrite the entry for a same-named imported selector and flag an unrelated useAppStore(selectRows). The any-branch and every-branch allocation predicates are one function with a flag, the Object.* static list is a Set, and import recording is a single pass. Tests: updater called once with live state, identical-state writes ignored, disarmed path forwards exact arguments without calling get(), full composition with the cascade probe (no drop, no double, correct site), and the module-scope shadowing case for the plugin.
145 lines
5.9 KiB
JavaScript
145 lines
5.9 KiB
JavaScript
import path from 'node:path'
|
|
import { describe, expect, it } from 'vitest'
|
|
import { runOxlintPluginOnSource } from './oxlint-plugin-test-runner.mjs'
|
|
|
|
const pluginPath = path.resolve('config/oxlint-plugins/app-store-performance.mjs')
|
|
|
|
function lintSource(source) {
|
|
return runOxlintPluginOnSource({
|
|
pluginName: 'app-store-performance',
|
|
pluginPath,
|
|
source,
|
|
rules: {
|
|
'app-store-performance/require-selector': 'warn',
|
|
'app-store-performance/no-identity-selector': 'warn',
|
|
'app-store-performance/no-fresh-selector-result': 'warn',
|
|
'app-store-performance/no-nested-fresh-under-shallow': 'warn'
|
|
}
|
|
})
|
|
}
|
|
|
|
describe('app store performance Oxlint plugin', () => {
|
|
it('reports whole-store and fresh-reference subscriptions', () => {
|
|
const diagnostics = lintSource(`
|
|
import { useAppStore as useStore } from '@/store'
|
|
const WholeStore = () => useStore()
|
|
const Identity = () => useStore((state) => state)
|
|
const Fresh = () => useStore((state) => ({ active: state.active }))
|
|
const Conditional = () => useStore((state) => state.active ? state.items : [])
|
|
const Nested = () => useStore((state) => {
|
|
if (state.active) return state.items.filter(Boolean)
|
|
return state.items
|
|
})
|
|
`)
|
|
|
|
expect(diagnostics.map((diagnostic) => diagnostic.code)).toEqual([
|
|
'app-store-performance(require-selector)',
|
|
'app-store-performance(no-identity-selector)',
|
|
'app-store-performance(no-fresh-selector-result)',
|
|
'app-store-performance(no-fresh-selector-result)',
|
|
'app-store-performance(no-fresh-selector-result)'
|
|
])
|
|
})
|
|
|
|
it('allows focused, cached, and useShallow selectors', () => {
|
|
const diagnostics = lintSource(`
|
|
import { useAppStore } from '@/store'
|
|
import { useShallow as shallow } from 'zustand/react/shallow'
|
|
const selectActive = (state) => state.active
|
|
const Focused = () => useAppStore(selectActive)
|
|
const Cached = () => useAppStore((state) => state.cachedProjection)
|
|
const Shallow = () => useAppStore(shallow((state) => ({ active: state.active })))
|
|
`)
|
|
|
|
expect(diagnostics).toEqual([])
|
|
})
|
|
|
|
it('resolves selectors referenced by name, including ones hoisted below the call', () => {
|
|
const diagnostics = lintSource(`
|
|
import { useAppStore } from '@/store'
|
|
const EarlyFresh = () => useAppStore(selectFreshRows)
|
|
const selectFreshRows = (state) => state.rows.filter(Boolean)
|
|
const Stable = () => useAppStore(selectActiveId)
|
|
const selectActiveId = (state) => state.activeId
|
|
`)
|
|
|
|
expect(diagnostics.map((diagnostic) => diagnostic.code)).toEqual([
|
|
'app-store-performance(no-fresh-selector-result)'
|
|
])
|
|
})
|
|
|
|
it('does not let a component-local helper resolve a same-named imported selector', () => {
|
|
const diagnostics = lintSource(`
|
|
import { useAppStore } from '@/store'
|
|
import { selectRows } from './selectors'
|
|
const Other = () => {
|
|
const selectRows = (state) => state.rows.map((row) => row.id)
|
|
return selectRows
|
|
}
|
|
const Imported = () => useAppStore(selectRows)
|
|
`)
|
|
|
|
expect(diagnostics).toEqual([])
|
|
})
|
|
|
|
it('covers sibling store hooks but not useSyncExternalStore', () => {
|
|
const diagnostics = lintSource(`
|
|
import { usePluginPanelsStore } from '@/store/plugin-panels'
|
|
import { useSyncExternalStore } from 'react'
|
|
const WholePanels = () => usePluginPanelsStore()
|
|
const FreshPanels = () => usePluginPanelsStore((state) => ({ open: state.open }))
|
|
const External = () => useSyncExternalStore(subscribe, () => ({ open: true }))
|
|
`)
|
|
|
|
expect(diagnostics.map((diagnostic) => diagnostic.code)).toEqual([
|
|
'app-store-performance(require-selector)',
|
|
'app-store-performance(no-fresh-selector-result)'
|
|
])
|
|
})
|
|
|
|
it('reports fresh references nested inside a useShallow projection', () => {
|
|
const diagnostics = lintSource(`
|
|
import { useAppStore } from '@/store'
|
|
import { useShallow } from 'zustand/react/shallow'
|
|
const NestedObject = () => useAppStore(useShallow((state) => ({ ids: state.rows.map((row) => row.id) })))
|
|
const NestedArray = () => useAppStore(useShallow((state) => [state.activeId, state.rows.filter(Boolean)]))
|
|
const Flat = () => useAppStore(useShallow((state) => ({ activeId: state.activeId, rows: state.rows })))
|
|
`)
|
|
|
|
expect(diagnostics.map((diagnostic) => diagnostic.code)).toEqual([
|
|
'app-store-performance(no-nested-fresh-under-shallow)',
|
|
'app-store-performance(no-nested-fresh-under-shallow)'
|
|
])
|
|
})
|
|
|
|
it('follows a selector one hop into a module-scope helper', () => {
|
|
const diagnostics = lintSource(`
|
|
import { useAppStore } from '@/store'
|
|
import { useShallow } from 'zustand/react/shallow'
|
|
const buildRows = (state) => state.rows.map((row) => row.id)
|
|
const Delegating = () => useAppStore((state) => buildRows(state))
|
|
const NestedDelegating = () => useAppStore(useShallow((state) => ({ ids: buildRows(state) })))
|
|
`)
|
|
|
|
expect(diagnostics.map((diagnostic) => diagnostic.code)).toEqual([
|
|
'app-store-performance(no-fresh-selector-result)',
|
|
'app-store-performance(no-nested-fresh-under-shallow)'
|
|
])
|
|
})
|
|
|
|
it('does not flag a helper that returns a cached reference on some branch', () => {
|
|
const diagnostics = lintSource(`
|
|
import { useAppStore } from '@/store'
|
|
import { useShallow } from 'zustand/react/shallow'
|
|
// The identity-caching shape: fresh only on a miss, cached otherwise.
|
|
const selectCachedRows = (state) => cache.get(state.key) ?? state.rows.filter(Boolean)
|
|
const Cached = () => useAppStore((state) => selectCachedRows(state))
|
|
const CachedNested = () => useAppStore(useShallow((state) => ({ rows: selectCachedRows(state) })))
|
|
// An unknown helper cannot be resolved, so it must not be guessed at.
|
|
const External = () => useAppStore((state) => externalBuild(state))
|
|
`)
|
|
|
|
expect(diagnostics).toEqual([])
|
|
})
|
|
})
|