-
Notifications
You must be signed in to change notification settings - Fork 0
/
parseQuery.ts
79 lines (71 loc) · 2.33 KB
/
parseQuery.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import { isArray } from '@utopia-utils/share'
export type LocationQueryValue = string | null
export type LocationQuery = Record<
string,
LocationQueryValue | LocationQueryValue[]
>
const PLUS_RE = /\+/g // %2B
/**
* Decode text using `decodeURIComponent`. Returns the original text if it
* fails.
*
* @param text - string to decode
* @returns decoded string
*/
export function decode(text: string | number): string {
try {
return decodeURIComponent(`${text}`)
}
catch (err) {
console.warn(`Error decoding "${text}". Using original value`)
}
return `${text}`
}
/**
* Transforms a queryString into a {@link LocationQuery} object. Accept both, a
* version with the leading `?` and without Should work as URLSearchParams
* @forked from vue-router https://github.com/vuejs/router/blob/main/packages/router/src/query.ts#L93
*
* @param search - search string to parse
* @returns a query object
* @linkcode https://github.com/GreatAuk/utopia-utils/blob/main/packages/core/src/parseQuery.ts
* @example
* ```
const params = parseQuery<{
id: string
name: string[]
}>('?id=2&name=Adam&name=Smith&sex')
expect(params.id).toBe('2')
expect(params.name).toEqual(['Adam', 'Smith'])
expect(params.sex).toBe(null)
```
*/
export function parseQuery<T extends LocationQuery>(search: string): T {
const query: LocationQuery = {}
// avoid creating an object with an empty key and empty value
// because of split('&')
if (search === '' || search === '?')
return query as T
const hasLeadingIM = search[0] === '?'
const searchParams = (hasLeadingIM ? search.slice(1) : search).split('&')
for (let i = 0; i < searchParams.length; ++i) {
// pre decode the + into space
const searchParam = searchParams[i].replace(PLUS_RE, ' ')
// allow the = character
const eqPos = searchParam.indexOf('=')
const key = decode(eqPos < 0 ? searchParam : searchParam.slice(0, eqPos))
const value = eqPos < 0 ? null : decode(searchParam.slice(eqPos + 1))
if (key in query) {
// an extra variable for ts types
let currentValue = query[key]
if (!isArray(currentValue))
currentValue = query[key] = [currentValue]
// we force the modification
;(currentValue as LocationQueryValue[]).push(value)
}
else {
query[key] = value
}
}
return query as T
}