Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
cloud-walker committed Nov 22, 2023
1 parent 521ed2a commit b10ffbd
Show file tree
Hide file tree
Showing 5 changed files with 2,138 additions and 1,268 deletions.
24 changes: 23 additions & 1 deletion packages/zod-sp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
"version": "0.0.0",
"type": "module",
"scripts": {
"types": "tsc --noEmit",
"test": "vitest",
"coverage": "vitest run --coverage",
"build": "tsup src/main.ts",
"dev": "tsup src/main.ts --watch"
},
Expand All @@ -23,12 +26,31 @@
"email": "baro.luc@gmail.com",
"url": "https://github.com/cloud-walker"
},
"repository": {
"type": "git",
"url": "https://github.com/cloud-walker/workbench"
},
"bugs": {
"url": "https://github.com/cloud-walker/workbench/issues"
},
"sideEffects": false,
"publishConfig": {
"access": "public"
},
"devDependencies": {
"@vitest/coverage-v8": "^0.32.2",
"tsup": "^7.1.0",
"typescript": "^5.0.4"
"typescript": "^5.0.4",
"vitest": "^0.32.2"
},
"keywords": [
"zod",
"qs",
"query-string",
"conform",
"url-parse"
],
"dependencies": {
"zod": "^3.22.4"
}
}
58 changes: 58 additions & 0 deletions packages/zod-sp/src/main.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { test, expect, describe } from 'vitest'
import { z } from 'zod'

import { zodSp } from './main.js'

const ISO_DATE_STUB = '2023-11-22T07:32:03.889Z'

test.each([
{ input: '', expected: {} },
{ input: 'a=1', expected: { a: '1' } },
{ input: 'a=true', expected: { a: 'true' } },
{ input: 'a=asd', expected: { a: 'asd' } },
{ input: 'a=1&b=foo&b=bar', expected: { a: '1', b: 'bar' } },
{ input: 'a=1&b=foo,bar', expected: { a: '1', b: 'foo,bar' } },
{ input: 'a=1&b[]=foo,bar', expected: { a: '1', b: ['foo', 'bar'] } },
])('.parse($input) -> $expected', ({ input, expected }) => {
expect(zodSp.parse(new URLSearchParams(input))).toEqual(expected)
})

test.each([
{ input: {}, expected: '' },
{ input: { a: 1 }, expected: 'a=1' },
{ input: { a: true }, expected: 'a=true' },
{ input: { a: 'asd' }, expected: 'a=asd' },
{ input: { a: 1, b: ['foo', 'bar'] }, expected: 'a=1&b%5B%5D=foo%2Cbar' },
{ input: { a: 1, b: null, c: undefined }, expected: 'a=1' },
{ input: { a: 1, b: new Date(ISO_DATE_STUB), c: [new Date(ISO_DATE_STUB), new Date(ISO_DATE_STUB)] }, expected: `a=1&b=${encodeURIComponent(ISO_DATE_STUB)}&c%5B%5D=${encodeURIComponent(`${ISO_DATE_STUB},${ISO_DATE_STUB}`)}` },
])('.serialize($input) -> $expected', ({ input, expected }) => {
expect(zodSp.serialize(input).toString()).toBe(expected)
})

describe('.with(ZodSchema)', () => {
const defaultTuple: [string, number] = ['default', 0]
const ZodSchema = z.object({
a: z.string().optional(),
b: z.coerce.number().optional(),
c: z.coerce.boolean().default(false),
d: z.array(z.coerce.date()).optional(),
e: z.tuple([z.string(), z.coerce.number()]).catch(() => defaultTuple),
f: z.coerce.date().optional(),
})
const dummySp = zodSp.with(ZodSchema)

test.each([
{ input: '', expected: { c: false, e: defaultTuple } },
{ input: 'c=true&a=123&e[]=foo,123', expected: { a: '123', c: true, e: ['foo', 123] } },
{ input: 'c=true&a=123&e[]=foo,123', expected: { a: '123', c: true, e: ['foo', 123] } },
{ input: zodSp.serialize({ d: [new Date(ISO_DATE_STUB), new Date(ISO_DATE_STUB)] }), expected: { a: '123', c: true, d: [new Date(ISO_DATE_STUB), new Date(ISO_DATE_STUB)], e: ['default', 0] } },
])('.parse($input) -> $expected', ({ input, expected }) => {
expect(dummySp.parse(new URLSearchParams(input))).toEqual(expected)
})

test.each([
{ input: 1, expected: 1 },
])('.serialize($input) -> $expected', ({ input, expected }) => {
expect(input).toBe(expected)
})
})
50 changes: 46 additions & 4 deletions packages/zod-sp/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,50 @@
import { ZodObject, ZodRawShape, TypeOf } from 'zod'

export const zodSp = {
parse() { },
serialize() { },
with() {
const parse = this.parse
parse(searchParams: URLSearchParams): Record<string, string | string[]> {
return [...searchParams.entries()].reduce<Record<string, string | string[]>>((acc, [key, value]) => {
if (value == null) {
return acc
}

if (key.endsWith('[]')) {
acc[key.replace('[]', '')] = value.split(',')
} else {
acc[key] = value
}
return acc
}, {})
},
serialize<T extends Record<string, unknown>>(params: T): URLSearchParams {
const searchParams = new URLSearchParams()
Object.entries(params).forEach(([key, value]) => {
if (value == null) {
return
}

if (Array.isArray(value)) {
if (value.length > 0) {
searchParams.set(`${key}[]`, value.map(v => {
const serialized = v instanceof Date ? v.toISOString() : String(v)
return serialized
}).join(','))
}
return
}

const serialized = value instanceof Date ? value.toISOString() : String(value)

searchParams.set(key, serialized)
})
return searchParams
},
with<Z extends ZodObject<ZodRawShape>>(schema: Z) {
type T = TypeOf<Z>
const parse = (searchParams: URLSearchParams): T => {
const parsed = this.parse(searchParams)
console.log(parsed)
return schema.parse(parsed)
}
const serialize = () => { }
return { parse, serialize } as const
}
Expand Down
15 changes: 15 additions & 0 deletions packages/zod-sp/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { defineConfig } from 'vitest/config'

export default defineConfig({
test: {
coverage: {
all: true,
functions: 90,
branches: 90,
lines: 90,
statements: 90,
include: ['src/**/*.ts'],
skipFull: true,
},
},
})
Loading

0 comments on commit b10ffbd

Please sign in to comment.