Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: useControllableState to version 4 #578

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ jobs:
- name: 🚀 Build
run: pnpm build

# - name: 🧪 Test with coverage
# run: pnpm coverage
- name: 🧪 Test
run: pnpm test

# - name: 📝 Upload coverage
# if: always()
Expand Down
4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@
"story": "pnpm storybook dev -p 6006 --no-open",
"lint": "eslint . --cache",
"lint:fix": "eslint . --fix --cache",
"test": "vitest run",
"test:watch": "vitest --watch",
"test:nuxt": "vitest -c vitest.nuxt.config.ts --coverage",
"test": "pnpm --filter '@oku-ui/primitives' test",
"coverage": "vitest run --coverage",
"build:storybook": "pnpm storybook build",
"typecheck": "tsc --noEmit",
Expand Down
1 change: 1 addition & 0 deletions packages/core/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ storybook-static
*.tsbuildinfo

*storybook.log
.data
3 changes: 3 additions & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@
"build-storybook": "storybook build",
"eslint": "eslint .",
"eslint:fix": "eslint . --fix",
"test": "vitest run",
"test:watch": "vitest --watch",
"test:nuxt": "vitest -c vitest.nuxt.config.ts --coverage",
"release": "pnpm build && pnpm publish --no-git-checks --access public",
"release:beta": "pnpm release --tag beta --access public",
"release:alpha": "pnpm release --tag alpha --access public",
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export { createContext } from './createContext.ts'
export { useBodyScrollLock } from './useBodyScrollLock.ts'
export { useComposedElements } from './useComposedElements.ts'
export { useControllableState, useControllableStateV2, useControllableStateV3 } from './useControllableState.ts'
export { useControllableState, useControllableStateV2, useControllableStateV3, useControllableStateV4 } from './useControllableState.ts'
export { useEscapeKeydown } from './useEscapeKeydown.ts'
export { useForwardElement } from './useForwardElement.ts'
export { useId } from './useId.ts'
Expand Down
115 changes: 115 additions & 0 deletions packages/core/src/hooks/tests/performance-results.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { existsSync, mkdirSync, writeFileSync } from 'node:fs'
import { resolve } from 'node:path'

interface PerformanceResult {
version: string
updateCount: number
executionTimeMs: number
onChangeCallCount: number
timestamp: string
}

export class PerformanceLogger {
private static results: PerformanceResult[] = []
private static readonly outputPath = resolve(__dirname, './.data/performance-results.json')

static logResult(version: string, executionTimeMs: number, onChangeCallCount: number, updateCount = 5) {
const result = {
version,
updateCount,
executionTimeMs,
onChangeCallCount,
timestamp: new Date().toISOString(),
}

this.results.push(result)
this.writeToFile() // Write to file after each new result
return result
}

static getResults() {
return [...this.results]
}

static compareVersions() {
const v3Results = this.results.filter(r => r.version === 'useControllableStateV3')
const v4Results = this.results.filter(r => r.version === 'useControllableStateV4')
const vueUseResults = this.results.filter(r => r.version === 'useVModel_VueUse')

const v3Avg = this.calculateAverage(v3Results)
const v4Avg = this.calculateAverage(v4Results)
const vueUseAvg = this.calculateAverage(vueUseResults)

const comparison = {
v3Average: v3Avg,
v4Average: v4Avg,
vueUseAverage: vueUseAvg,
performance: [] as string[],
}

if (v3Avg && v4Avg && vueUseAvg) {
const implementations = [
{ name: 'V3', time: v3Avg.executionTimeMs },
{ name: 'V4', time: v4Avg.executionTimeMs },
{ name: 'VueUse', time: vueUseAvg.executionTimeMs },
].sort((a, b) => a.time - b.time)

const fastest = implementations[0]

implementations.slice(1).forEach((impl) => {
const diff = impl.time - fastest.time
const percentage = (diff / fastest.time) * 100
comparison.performance.push(
`${fastest.name} is ${percentage.toFixed(2)}% faster than ${impl.name}`,
)
})
}

return comparison
}

static clearWarmupResults() {
this.results = this.results.slice(-5) // Keep only last 5 results
}

private static calculateAverage(results: PerformanceResult[]) {
if (results.length === 0)
return null

// Remove outliers
const sorted = [...results].sort((a, b) => a.executionTimeMs - b.executionTimeMs)
const q1Index = Math.floor(sorted.length * 0.25)
const q3Index = Math.floor(sorted.length * 0.75)
const validResults = sorted.slice(q1Index, q3Index + 1)

return {
executionTimeMs: validResults.reduce((sum, r) => sum + r.executionTimeMs, 0) / validResults.length,
onChangeCallCount: validResults.reduce((sum, r) => sum + r.onChangeCallCount, 0) / validResults.length,
}
}

private static writeToFile() {
const output = {
timestamp: new Date().toISOString(),
results: this.results,
comparison: this.compareVersions(),
summary: this.compareVersions().performance,
}

// .data directory must exist before writing to it
if (!existsSync(resolve(__dirname, './.data'))) {
mkdirSync(resolve(__dirname, './.data'))
}

try {
writeFileSync(
this.outputPath,
JSON.stringify(output, null, 2),
'utf-8',
)
}
catch (error) {
console.error('Failed to write performance results:', error)
}
}
}
Loading
Loading