Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Vue 3.5+ Composition API reference with progressive sub-file loading for components, composables, reactivity, and testing.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/utils-client.md
1# Client Utilities23Pure functions for formatting, validation, transformation, and parsing.45## Quick Reference67| Category | Examples |8| ------------ | --------------------------------------------- |9| Formatters | `formatCurrency`, `formatDate`, `formatBytes` |10| Validators | `isValidEmail`, `isValidUrl`, `isValidPhone` |11| Transformers | `slugify`, `truncate`, `capitalize` |12| Parsers | `parseQuery`, `parseJSON`, `parseDate` |1314## Rules1516**Pure functions:**1718- Same input → same output19- No side effects20- No external state mutation21- No API calls, no refs, no reactive2223**When NOT to use utils:**2425- Stateful logic → use composables26- Vue-specific → use composables27- Component logic → keep in component28- API calls → use queries2930## Structure3132```ts33// utils/formatters.ts34export function formatCurrency(amount: number, currency = 'USD'): string {35return new Intl.NumberFormat('en-US', {36style: 'currency',37currency,38}).format(amount)39}4041export function formatRelativeTime(date: Date): string {42const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' })43const diff = date.getTime() - Date.now()44const days = Math.floor(diff / (1000 * 60 * 60 * 24))45return rtf.format(days, 'day')46}47```4849**Naming:** Descriptive verbs (`formatCurrency`, `validateEmail`, `parseQuery`)50**Organization:** Group by category (`formatters.ts`, `validators.ts`)51**Exports:** Named exports only5253## Examples by Category5455**Formatters:**5657```ts58// utils/formatters.ts59export function formatBytes(bytes: number): string { ... }60export function formatPhone(phone: string): string { ... }61```6263**Validators:**6465```ts66// utils/validators.ts67export function isValidEmail(email: string): boolean {68return /^[^\s@]+@[^\s@][^\s.@]*\.[^\s@]+$/.test(email)69}7071export function isValidUrl(url: string): boolean {72try { new URL(url); return true }73catch { return false }74}75```7677**Transformers:**7879```ts80// utils/transformers.ts81export function slugify(text: string): string {82return text.toLowerCase()83.replace(/[^\w\s-]/g, '')84.replace(/\s+/g, '-')85}8687export function truncate(text: string, length: number): string {88return text.length > length ? `${text.slice(0, length)}...` : text89}90```9192**Parsers:**9394```ts95// utils/parsers.ts96export function parseQuery(search: string): Record<string, string> {97return Object.fromEntries(new URLSearchParams(search))98}99100export function parseJSON<T>(json: string, fallback: T): T {101try { return JSON.parse(json) }102catch { return fallback }103}104```105106## Common Mistakes107108**Side effects (not pure):**109110```ts111// ❌ Wrong - mutates external state112let count = 0113export function increment() {114count++115return count116}117118// ✅ Correct - pure119export function add(a: number, b: number): number {120return a + b121}122```123124**Using utils for stateful logic:**125126```ts127// ❌ Wrong - should be composable128export function useCounter() { ... }129130// ✅ Correct - pure transformation131export function formatCount(count: number): string { ... }132```133134## Organization135136**Flat for small projects:**137138```139utils/140├── formatters.ts141├── validators.ts142└── transformers.ts143```144145**Nested for large projects:**146147```148utils/149├── formatters/150│ ├── date.ts151│ ├── currency.ts152│ └── index.ts153└── validators/154├── email.ts155└── index.ts156```157