0
0
mirror of https://github.com/honojs/hono.git synced 2024-11-24 19:26:56 +01:00

fix(client): return query params in $url (#3541)

This commit is contained in:
Alex Grover 2024-10-22 01:14:01 -04:00 committed by GitHub
parent 267186d65e
commit 3d8abbc239
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 70 additions and 18 deletions

View File

@ -847,6 +847,27 @@ describe('$url() with a param option', () => {
}) })
}) })
describe('$url() with a query option', () => {
const app = new Hono().get(
'/posts',
validator('query', () => {
return {} as { filter: 'test' }
}),
(c) => c.json({ ok: true })
)
type AppType = typeof app
const client = hc<AppType>('http://localhost')
it('Should return the correct path - /posts?filter=test', async () => {
const url = client.posts.$url({
query: {
filter: 'test',
},
})
expect(url.search).toBe('?filter=test')
})
})
describe('Client can be awaited', () => { describe('Client can be awaited', () => {
it('Can be awaited without side effects', async () => { it('Can be awaited without side effects', async () => {
const client = hc('http://localhost') const client = hc('http://localhost')

View File

@ -4,6 +4,7 @@ import { serialize } from '../utils/cookie'
import type { UnionToIntersection } from '../utils/types' import type { UnionToIntersection } from '../utils/types'
import type { Callback, Client, ClientRequestOptions } from './types' import type { Callback, Client, ClientRequestOptions } from './types'
import { import {
buildSearchParams,
deepMerge, deepMerge,
mergePath, mergePath,
removeIndexString, removeIndexString,
@ -49,20 +50,7 @@ class ClientRequestImpl {
) => { ) => {
if (args) { if (args) {
if (args.query) { if (args.query) {
for (const [k, v] of Object.entries(args.query)) { this.queryParams = buildSearchParams(args.query)
if (v === undefined) {
continue
}
this.queryParams ||= new URLSearchParams()
if (Array.isArray(v)) {
for (const v2 of v) {
this.queryParams.append(k, v2)
}
} else {
this.queryParams.set(k, v)
}
}
} }
if (args.form) { if (args.form) {
@ -172,10 +160,16 @@ export const hc = <T extends Hono<any, any, any>>(
const path = parts.join('/') const path = parts.join('/')
const url = mergePath(baseUrl, path) const url = mergePath(baseUrl, path)
if (method === 'url') { if (method === 'url') {
if (opts.args[0] && opts.args[0].param) { let result = url
return new URL(replaceUrlParam(url, opts.args[0].param)) if (opts.args[0]) {
if (opts.args[0].param) {
result = replaceUrlParam(url, opts.args[0].param)
}
if (opts.args[0].query) {
result = result + '?' + buildSearchParams(opts.args[0].query).toString()
}
} }
return new URL(url) return new URL(result)
} }
if (method === 'ws') { if (method === 'ws') {
const webSocketUrl = replaceUrlProtocol( const webSocketUrl = replaceUrlProtocol(

View File

@ -38,7 +38,11 @@ export type ClientRequest<S extends Schema> = {
$url: ( $url: (
arg?: S[keyof S] extends { input: infer R } arg?: S[keyof S] extends { input: infer R }
? R extends { param: infer P } ? R extends { param: infer P }
? { param: P } ? R extends { query: infer Q }
? { param: P; query: Q }
: { param: P }
: R extends { query: infer Q }
? { query: Q }
: {} : {}
: {} : {}
) => URL ) => URL

View File

@ -1,4 +1,5 @@
import { import {
buildSearchParams,
deepMerge, deepMerge,
mergePath, mergePath,
removeIndexString, removeIndexString,
@ -59,6 +60,18 @@ describe('replaceUrlParams', () => {
}) })
}) })
describe('buildSearchParams', () => {
it('Should build URLSearchParams correctly', () => {
const query = {
id: '123',
type: 'test',
tag: ['a', 'b'],
}
const searchParams = buildSearchParams(query)
expect(searchParams.toString()).toBe('id=123&type=test&tag=a&tag=b')
})
})
describe('replaceUrlProtocol', () => { describe('replaceUrlProtocol', () => {
it('Should replace http to ws', () => { it('Should replace http to ws', () => {
const url = 'http://localhost' const url = 'http://localhost'

View File

@ -15,6 +15,26 @@ export const replaceUrlParam = (urlString: string, params: Record<string, string
return urlString return urlString
} }
export const buildSearchParams = (query: Record<string, string | string[]>) => {
const searchParams = new URLSearchParams()
for (const [k, v] of Object.entries(query)) {
if (v === undefined) {
continue
}
if (Array.isArray(v)) {
for (const v2 of v) {
searchParams.append(k, v2)
}
} else {
searchParams.set(k, v)
}
}
return searchParams
}
export const replaceUrlProtocol = (urlString: string, protocol: 'ws' | 'http') => { export const replaceUrlProtocol = (urlString: string, protocol: 'ws' | 'http') => {
switch (protocol) { switch (protocol) {
case 'ws': case 'ws':