From 6b35f83a3c00bd5c794276ae75c54b5e313526bb Mon Sep 17 00:00:00 2001 From: Michael Sweeney Date: Sat, 31 Aug 2024 21:59:44 -0700 Subject: [PATCH] ran prettier --- README.md | 80 +++++++------- src/index.ts | 190 +++++++++++++++++----------------- tests/01_basic_schema.spec.ts | 120 ++++++++++----------- 3 files changed, 195 insertions(+), 195 deletions(-) diff --git a/README.md b/README.md index 5974541..31bb931 100644 --- a/README.md +++ b/README.md @@ -29,22 +29,22 @@ are welcome and strongly encouraged. ## How to use it ```js -import { schema } from 'valtio-zod' +import { schema } from 'valtio-zod'; const userSchema = z.object({ name: z.string(), - age: z.number() -}) + age: z.number(), +}); const [state, snap] = schema(userSchema).proxy({ name: 'John Doe', - age: 30 -}) + age: 30, +}); -state.name = 'Jane Doe' +state.name = 'Jane Doe'; // state.name = 'Jane Doe' -state.name = 55 // Error +state.name = 55; // Error // state.name = 'Jane Doe' ``` @@ -61,17 +61,17 @@ function that returns a tuple of 2 values. 2. The underlying valtio store. This object is what must be used when using `useSnapshot()` to access the values in the store. ```js -import { schema } from 'valtio-zod' +import { schema } from 'valtio-zod'; const userSchema = z.object({ name: z.string(), - age: z.number() -}) + age: z.number(), +}); const [state, snap] = schema(userSchema).proxy({ name: 'John Doe', - age: 30 -}) + age: 30, +}); // state is the proxy, snap is the underlying valtio store. You must use the snap object // when you use useSnapshot() @@ -92,51 +92,51 @@ to configure the default behavior of all schemas that don't provide their own co #### `parseAsync` example ```js -import { schema } from 'valtio-zod' +import { schema } from 'valtio-zod'; const promiseSchema = z.object({ - name: z.promise(z.string()) -}) + name: z.promise(z.string()), +}); const [state, snap] = schema(promiseSchema).proxy( { - name: Promise.resolve('Jane Doe') + name: Promise.resolve('Jane Doe'), }, { - parseAsync: true - } -) + parseAsync: true, + }, +); -state.name = 'Jane Doe' +state.name = 'Jane Doe'; // state.name = 'Jane Doe' -userState.name = 55 // Error +userState.name = 55; // Error // userState.name = 'Jane Doe' ``` #### `parseSafe` example ```js -import { schema } from 'valtio-zod' +import { schema } from 'valtio-zod'; const userSchema = z.object({ - name: z.string() -}) + name: z.string(), +}); const userState = schema(userSchema).proxy( { name: 'John Doe', - age: 30 + age: 30, }, { - parseSafe: true - } -) + parseSafe: true, + }, +); -userState.name = 'Jane Doe' +userState.name = 'Jane Doe'; // userState.name = 'Jane Doe' -userState.name = 55 // Error +userState.name = 55; // Error // this will call the errorHandler function without throwing an error // userState.name = 'Jane Doe' ``` @@ -144,9 +144,9 @@ userState.name = 55 // Error `parseSafe` is also available as a property on the `vzGlobalConfig` object ```js -import { vzGlobalConfig } from 'valtio-zod' +import { vzGlobalConfig } from 'valtio-zod'; -vzGlobalConfig.parseSafe = true +vzGlobalConfig.parseSafe = true; ``` #### `errorHandler` example @@ -154,22 +154,22 @@ vzGlobalConfig.parseSafe = true This will allow you to use your own error handling logic (or use `Zod.Error`) ```js -import { schema, errorHandler } from 'valtio-zod' +import { schema, errorHandler } from 'valtio-zod'; const userSchema = z.object({ name: z.string(), - age: z.number() -}) + age: z.number(), +}); const [userState] = schema(userSchema).proxy( { name: 'John Doe', - age: 30 + age: 30, }, { errorHandler: (error) => { - console.error(error) - } - } -) + console.error(error); + }, + }, +); ``` diff --git a/src/index.ts b/src/index.ts index e0e7e20..3cd1ac4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,96 +1,96 @@ /* eslint-disable */ -import { z, ZodType } from 'zod' -import { proxy as vproxy, useSnapshot as vsnap } from 'valtio' -import _ from 'lodash' +import { z, ZodType } from 'zod'; +import { proxy as vproxy, useSnapshot as vsnap } from 'valtio'; +import _ from 'lodash'; type ValtioProxy = { - [P in keyof T]: T[P] -} + [P in keyof T]: T[P]; +}; type SchemaConfig = { - parseAsync?: boolean - safeParse?: boolean - errorHandler?: (error: unknown) => void -} + parseAsync?: boolean; + safeParse?: boolean; + errorHandler?: (error: unknown) => void; +}; const defaultConfig = { parseAsync: false, safeParse: false, - errorHandler: (error: unknown) => console.error(error) -} + errorHandler: (error: unknown) => console.error(error), +}; export const vzGlobalConfig = { safeParse: false, - errorHandler: (error: unknown) => console.error(error) -} + errorHandler: (error: unknown) => console.error(error), +}; const isObject = (x: unknown): x is object => - typeof x === 'object' && x !== null + typeof x === 'object' && x !== null; -type MergedConfig = Required +type MergedConfig = Required; type SchemaMeta = SchemaConfig & { - initialState: unknown -} + initialState: unknown; +}; -type PropType = string | number | symbol -const schemaMeta = new WeakMap, SchemaMeta>() -const pathList = new WeakMap<{}, PropType[]>() +type PropType = string | number | symbol; +const schemaMeta = new WeakMap, SchemaMeta>(); +const pathList = new WeakMap<{}, PropType[]>(); type SchemaReturn> = { proxy: { - (initialState: any, config?: SchemaConfig): ValtioProxy> - } - useSnapshot: (store: ValtioProxy>) => any -} + (initialState: any, config?: SchemaConfig): ValtioProxy>; + }; + useSnapshot: (store: ValtioProxy>) => any; +}; function updateObjectAtPath(obj: any, path: PropType[], newValue: any) { - let stack = [...path] - let object = obj + let stack = [...path]; + let object = obj; while (stack.length > 1) { - const key = stack.shift() - if (key === undefined) return + const key = stack.shift(); + if (key === undefined) return; if (!object[key] || typeof object[key] !== 'object') { - object[key] = {} + object[key] = {}; } - object = object[key] + object = object[key]; } - const lastKey = stack.shift() - if (lastKey !== undefined) object[lastKey] = newValue + const lastKey = stack.shift(); + if (lastKey !== undefined) object[lastKey] = newValue; } -const valtioStoreSymbol = Symbol('valtioStore') +const valtioStoreSymbol = Symbol('valtioStore'); export const schema = >( - zodSchema: T + zodSchema: T, ): SchemaReturn => { - let valtioProxy: any + let valtioProxy: any; const proxy = ( initialState: z.infer, - config: SchemaConfig = {} + config: SchemaConfig = {}, ): ValtioProxy> => { if (!isObject(initialState)) { - throw new Error('object required') + throw new Error('object required'); } - const mergedConfig: MergedConfig = { ...defaultConfig, ...config } + const mergedConfig: MergedConfig = { ...defaultConfig, ...config }; - const parseAsync = mergedConfig.parseAsync - const safeParse = mergedConfig.safeParse - const errorHandler = mergedConfig.errorHandler + const parseAsync = mergedConfig.parseAsync; + const safeParse = mergedConfig.safeParse; + const errorHandler = mergedConfig.errorHandler; // before proxying, validate the initial state if (parseAsync) { zodSchema.parseAsync(initialState).catch((e) => { - throw e - }) + throw e; + }); } else { - zodSchema.parse(initialState) + zodSchema.parse(initialState); } - valtioProxy = vproxy(initialState) + valtioProxy = vproxy(initialState); const createProxy = (target: any, parentPath: PropType[] = []): any => { if (!schemaMeta.has(zodSchema)) { @@ -98,99 +98,99 @@ export const schema = >( safeParse, parseAsync, errorHandler, - initialState - }) + initialState, + }); } Reflect.ownKeys(target).forEach((key) => { if (isObject(target[key])) { - const newPath = parentPath.concat(key) - pathList.set(target[key], newPath) - createProxy(target[key], newPath) + const newPath = parentPath.concat(key); + pathList.set(target[key], newPath); + createProxy(target[key], newPath); } - }) + }); return new Proxy(target, { get(target, prop, receiver) { - const value = Reflect.get(target, prop, receiver) + const value = Reflect.get(target, prop, receiver); return isObject(value) ? createProxy(value, parentPath.concat(prop)) - : value + : value; }, set(target, prop, value, receiver) { const originalObject = schemaMeta.get(zodSchema)! - .initialState as z.infer + .initialState as z.infer; - const objectToValidate = JSON.parse(JSON.stringify(originalObject)) - const path = (pathList.get(target) || []).concat(prop) + const objectToValidate = JSON.parse(JSON.stringify(originalObject)); + const path = (pathList.get(target) || []).concat(prop); - updateObjectAtPath(objectToValidate, path, value) + updateObjectAtPath(objectToValidate, path, value); const handleAsyncParse = async () => { try { - const parsedValue = await zodSchema.parseAsync(objectToValidate) - _.set(valtioProxy, value, path) - Reflect.set(target, prop, value, receiver) - return true + const parsedValue = await zodSchema.parseAsync(objectToValidate); + _.set(valtioProxy, value, path); + Reflect.set(target, prop, value, receiver); + return true; } catch (error) { - errorHandler(error) + errorHandler(error); if (!safeParse) { - throw error + throw error; } - return false + return false; } - } + }; const handleSyncParse = () => { try { if (safeParse) { - const result = zodSchema.safeParse(objectToValidate) + const result = zodSchema.safeParse(objectToValidate); if (result.success) { - valtioProxy[prop] = value - Reflect.set(target, prop, value, receiver) - return true + valtioProxy[prop] = value; + Reflect.set(target, prop, value, receiver); + return true; } else { - errorHandler(result.error) - return false + errorHandler(result.error); + return false; } } else { - const parsedValue = zodSchema.parse(objectToValidate) - Reflect.set(target, prop, value, receiver) - valtioProxy[prop] = value - return true + const parsedValue = zodSchema.parse(objectToValidate); + Reflect.set(target, prop, value, receiver); + valtioProxy[prop] = value; + return true; } } catch (error) { - errorHandler(error) + errorHandler(error); if (!safeParse) { - throw error + throw error; } - return false + return false; } - } + }; if (parseAsync) { handleAsyncParse().catch((error) => { - errorHandler(error) + errorHandler(error); if (!safeParse) { - throw error + throw error; } - }) - return true + }); + return true; } else { - return handleSyncParse() + return handleSyncParse(); } - } - }) - } + }, + }); + }; - const store = createProxy(valtioProxy) - store[valtioStoreSymbol] = valtioProxy + const store = createProxy(valtioProxy); + store[valtioStoreSymbol] = valtioProxy; - return store - } + return store; + }; const snap = (store: any) => { - return vsnap(store[valtioStoreSymbol]) - } - return { proxy, useSnapshot: snap } -} + return vsnap(store[valtioStoreSymbol]); + }; + return { proxy, useSnapshot: snap }; +}; diff --git a/tests/01_basic_schema.spec.ts b/tests/01_basic_schema.spec.ts index 9339ec0..1b9577d 100644 --- a/tests/01_basic_schema.spec.ts +++ b/tests/01_basic_schema.spec.ts @@ -1,71 +1,71 @@ -import { z } from 'zod' -import { describe, it, expect, vi } from 'vitest' -import { schema } from 'valtio-zod' +import { z } from 'zod'; +import { describe, it, expect, vi } from 'vitest'; +import { schema } from 'valtio-zod'; describe('valtio-zod schema', () => { it('should create a proxy and set synchronous values correctly', () => { const userSchema = z.object({ username: z.string(), - age: z.number().int() - }) + age: z.number().int(), + }); - const { proxy } = schema(userSchema) - const user = proxy({ username: 'Alice', age: 30 }) + const { proxy } = schema(userSchema); + const user = proxy({ username: 'Alice', age: 30 }); - user.username = 'Bob' - expect(user.username).toBe('Bob') + user.username = 'Bob'; + expect(user.username).toBe('Bob'); - user.age = 42 - expect(user.age).toBe(42) - }) + user.age = 42; + expect(user.age).toBe(42); + }); it('should handle parseSafe correctly', () => { const userSchema = z.object({ username: z.string(), - age: z.number().int() - }) + age: z.number().int(), + }); - const { proxy } = schema(userSchema) + const { proxy } = schema(userSchema); const user = proxy( { username: 'Alice', age: 30 }, - { safeParse: true, errorHandler: vi.fn() } - ) + { safeParse: true, errorHandler: vi.fn() }, + ); - const errorHandler = vi.fn() + const errorHandler = vi.fn(); try { // Invalid age // eslint-disable-next-line @typescript-eslint/no-explicit-any - user.age = 'invalidAge' as any + user.age = 'invalidAge' as any; } catch (e) { - errorHandler(e) + errorHandler(e); } - expect(errorHandler).toHaveBeenCalled() - expect(user.age).toBe(30) // Ensure the value hasn't changed - }) + expect(errorHandler).toHaveBeenCalled(); + expect(user.age).toBe(30); // Ensure the value hasn't changed + }); it('should use custom error handler', () => { const userSchema = z.object({ username: z.string(), - age: z.number().int() - }) + age: z.number().int(), + }); - const errorHandler = vi.fn() + const errorHandler = vi.fn(); - const { proxy } = schema(userSchema) - const user = proxy({ username: 'Alice', age: 30 }, { errorHandler }) + const { proxy } = schema(userSchema); + const user = proxy({ username: 'Alice', age: 30 }, { errorHandler }); try { // Invalid age // eslint-disable-next-line @typescript-eslint/no-explicit-any - user.age = 'invalidAge' as any + user.age = 'invalidAge' as any; } catch (_e) { // Since parseSafe is false, the error should be caught here } - expect(errorHandler).toHaveBeenCalled() - }) + expect(errorHandler).toHaveBeenCalled(); + }); it('should handle multi-level objects correctly', async () => { const userSchema = z.object({ @@ -76,12 +76,12 @@ describe('valtio-zod schema', () => { lastName: z.string(), address: z.object({ city: z.string(), - country: z.string() - }) - }) - }) + country: z.string(), + }), + }), + }); - const { proxy } = schema(userSchema) + const { proxy } = schema(userSchema); const user = proxy({ username: 'Alice', age: 30, @@ -90,15 +90,15 @@ describe('valtio-zod schema', () => { lastName: 'Smith', address: { city: 'Wonderland', - country: 'Fantasy' - } - } - }) + country: 'Fantasy', + }, + }, + }); // Ensure nested fields maintain object structure and types - user.profile.address.city = 'New City' // Ensure the proxy update handling completes - expect(user.profile.address.city).toBe('New City') - }) + user.profile.address.city = 'New City'; // Ensure the proxy update handling completes + expect(user.profile.address.city).toBe('New City'); + }); it('should error by updating a value in a nested object', () => { const userSchema = z.object({ @@ -109,14 +109,14 @@ describe('valtio-zod schema', () => { lastName: z.string(), address: z.object({ city: z.string(), - country: z.string() - }) - }) - }) + country: z.string(), + }), + }), + }); - const errorHandler = vi.fn() + const errorHandler = vi.fn(); - const { proxy } = schema(userSchema) + const { proxy } = schema(userSchema); const user = proxy( { username: 'Alice', @@ -126,19 +126,19 @@ describe('valtio-zod schema', () => { lastName: 'Smith', address: { city: 'Wonderland', - country: 'Fantasy' - } - } + country: 'Fantasy', + }, + }, }, - { safeParse: true, errorHandler } - ) + { safeParse: true, errorHandler }, + ); // Invalid country type - const result = Reflect.set(user.profile.address, 'country', 123) + const result = Reflect.set(user.profile.address, 'country', 123); - expect(result).toBe(false) - expect(errorHandler).toHaveBeenCalled() + expect(result).toBe(false); + expect(errorHandler).toHaveBeenCalled(); // Ensure the value hasn't changed from the initial valid value - expect(user.profile.address.country).toBe('Fantasy') - }) -}) + expect(user.profile.address.country).toBe('Fantasy'); + }); +});