-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(pachages): add parse query & stringify query
- Loading branch information
Showing
3 changed files
with
224 additions
and
90 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
export type LocationQueryValue = string | null; | ||
|
||
/** | ||
* Normalized query object that appears in {@link RouteLocationNormalized} | ||
* | ||
* @public | ||
*/ | ||
export type LocationQuery = Record<string, LocationQueryValue | LocationQueryValue[]>; | ||
export type LocationQueryValueRaw = LocationQueryValue | number | undefined; | ||
|
||
export type LocationQueryRaw = Record<string | number, LocationQueryValueRaw | LocationQueryValueRaw[]>; | ||
|
||
/** | ||
* Transforms a queryString into a {@link LocationQuery} object. Accept both, a version with the leading `?` and without | ||
* Should work as URLSearchParams | ||
* | ||
* @param search - search string to parse | ||
* @returns a query object | ||
* @internal | ||
*/ | ||
|
||
export const PLUS_RE = /\+/g; // %2B | ||
|
||
const EQUAL_RE = /[=]/g; // %3D | ||
|
||
const ENC_BRACKET_OPEN_RE = /%5B/g; // [ | ||
const ENC_BRACKET_CLOSE_RE = /%5D/g; // ] | ||
const ENC_CARET_RE = /%5E/g; // ^ | ||
const ENC_BACKTICK_RE = /%60/g; // ` | ||
const ENC_CURLY_OPEN_RE = /%7B/g; // { | ||
const ENC_PIPE_RE = /%7C/g; // | | ||
const ENC_CURLY_CLOSE_RE = /%7D/g; // } | ||
const ENC_SPACE_RE = /%20/g; // } | ||
const HASH_RE = /#/g; // %23 | ||
const AMPERSAND_RE = /&/g; // %26 | ||
export function parseQuery(search: string): LocationQuery { | ||
const query: LocationQuery = {}; | ||
// avoid creating an object with an empty key and empty value | ||
// because of split('&') | ||
if (search === '' || search === '?') return query; | ||
const hasLeadingIM = search[0] === '?'; | ||
const searchParams = (hasLeadingIM ? search.slice(1) : search).split('&'); | ||
// eslint-disable-next-line no-plusplus | ||
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 (!Array.isArray(currentValue)) { | ||
// eslint-disable-next-line no-multi-assign | ||
currentValue = query[key] = [currentValue]; | ||
} | ||
// we force the modification | ||
(currentValue as LocationQueryValue[]).push(value); | ||
} else { | ||
query[key] = value; | ||
} | ||
} | ||
return query; | ||
} | ||
|
||
export function stringifyQuery(query: LocationQueryRaw): string { | ||
let search = ''; | ||
// eslint-disable-next-line guard-for-in | ||
for (let key in query) { | ||
const value = query[key]; | ||
key = encodeQueryKey(key); | ||
if (value === null) { | ||
// only null adds the value | ||
if (value !== undefined) { | ||
search += (search.length ? '&' : '') + key; | ||
} | ||
// eslint-disable-next-line no-continue | ||
continue; | ||
} | ||
// keep null values | ||
const values: LocationQueryValueRaw[] = Array.isArray(value) | ||
? value.map(v => v && encodeQueryValue(v)) | ||
: [value && encodeQueryValue(value)]; | ||
|
||
// eslint-disable-next-line no-loop-func | ||
values.forEach(v => { | ||
// skip undefined values in arrays as if they were not present | ||
// smaller code than using filter | ||
if (v !== undefined) { | ||
// only append & with non-empty search | ||
search += (search.length ? '&' : '') + key; | ||
if (v !== null) search += `=${v}`; | ||
} | ||
}); | ||
} | ||
|
||
return search; | ||
} | ||
|
||
export function decode(text: string | number): string { | ||
try { | ||
return decodeURIComponent(`${text}`); | ||
} catch (err) { | ||
console.warn(`Error decoding "${text}". Using original value`); | ||
} | ||
return `${text}`; | ||
} | ||
|
||
export function encodeQueryKey(text: string | number): string { | ||
return encodeQueryValue(text).replace(EQUAL_RE, '%3D'); | ||
} | ||
|
||
export function encodeQueryValue(text: string | number): string { | ||
return ( | ||
commonEncode(text) | ||
// Encode the space as +, encode the + to differentiate it from the space | ||
.replace(PLUS_RE, '%2B') | ||
.replace(ENC_SPACE_RE, '+') | ||
.replace(HASH_RE, '%23') | ||
.replace(AMPERSAND_RE, '%26') | ||
.replace(ENC_BACKTICK_RE, '`') | ||
.replace(ENC_CURLY_OPEN_RE, '{') | ||
.replace(ENC_CURLY_CLOSE_RE, '}') | ||
.replace(ENC_CARET_RE, '^') | ||
); | ||
} | ||
|
||
function commonEncode(text: string | number): string { | ||
return encodeURI(`${text}`) | ||
.replace(ENC_PIPE_RE, '|') | ||
.replace(ENC_BRACKET_OPEN_RE, '[') | ||
.replace(ENC_BRACKET_CLOSE_RE, ']'); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,95 +1,92 @@ | ||
{ | ||
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }], | ||
"headers": [ | ||
{ | ||
"source": "/sw.js", | ||
"headers": [ | ||
{ | ||
"key": "Cache-Control", | ||
"value": "public, max-age=0, must-revalidate" | ||
} | ||
] | ||
}, | ||
{ | ||
"source": "(.*)", | ||
"headers": [ | ||
{ | ||
"key": "Cache-Control", | ||
"value": "public, s-maxage=86400, max-age=86400" | ||
} | ||
] | ||
}, | ||
{ | ||
"source": "/medias/(.*)", | ||
"headers": [ | ||
{ | ||
"key": "Cache-Control", | ||
"value": "public, s-maxage=2592000, max-age=2592000" | ||
} | ||
] | ||
}, | ||
{ | ||
"source": "/medias_webp/(.*)", | ||
"headers": [ | ||
{ | ||
"key": "Cache-Control", | ||
"value": "public, s-maxage=2592000, max-age=2592000" | ||
} | ||
] | ||
}, | ||
{ | ||
"source": "(.*).html", | ||
"headers": [ | ||
{ | ||
"key": "Cache-Control", | ||
"value": "public, s-maxage=1800, max-age=1800" | ||
} | ||
] | ||
}, | ||
{ | ||
"source": "(.*).js", | ||
"headers": [ | ||
{ | ||
"key": "Cache-Control", | ||
"value": "public, s-maxage=2592000, max-age=2592000" | ||
} | ||
] | ||
}, | ||
{ | ||
"source": "(.*).css", | ||
"headers": [ | ||
{ | ||
"key": "Cache-Control", | ||
"value": "public, s-maxage=2592000, max-age=2592000" | ||
} | ||
] | ||
}, | ||
{ | ||
"source": "(.*).json", | ||
"headers": [ | ||
{ | ||
"key": "Cache-Control", | ||
"value": "public, s-maxage=2592000, max-age=2592000" | ||
} | ||
] | ||
} | ||
], | ||
"regions": [ | ||
"hkg1" | ||
{ | ||
"source": "/sw.js", | ||
"headers": [ | ||
{ | ||
"key": "Cache-Control", | ||
"value": "public, max-age=0, must-revalidate" | ||
} | ||
] | ||
}, | ||
{ | ||
"source": "(.*)", | ||
"headers": [ | ||
{ | ||
"key": "Cache-Control", | ||
"value": "public, s-maxage=86400, max-age=86400" | ||
} | ||
] | ||
}, | ||
{ | ||
"source": "/medias/(.*)", | ||
"headers": [ | ||
{ | ||
"key": "Cache-Control", | ||
"value": "public, s-maxage=2592000, max-age=2592000" | ||
} | ||
] | ||
}, | ||
{ | ||
"source": "/medias_webp/(.*)", | ||
"headers": [ | ||
{ | ||
"key": "Cache-Control", | ||
"value": "public, s-maxage=2592000, max-age=2592000" | ||
} | ||
] | ||
}, | ||
{ | ||
"source": "(.*).html", | ||
"headers": [ | ||
{ | ||
"key": "Cache-Control", | ||
"value": "public, s-maxage=1800, max-age=1800" | ||
} | ||
] | ||
}, | ||
{ | ||
"source": "(.*).js", | ||
"headers": [ | ||
{ | ||
"key": "Cache-Control", | ||
"value": "public, s-maxage=2592000, max-age=2592000" | ||
} | ||
] | ||
}, | ||
{ | ||
"source": "(.*).css", | ||
"headers": [ | ||
{ | ||
"key": "Cache-Control", | ||
"value": "public, s-maxage=2592000, max-age=2592000" | ||
} | ||
] | ||
}, | ||
{ | ||
"source": "(.*).json", | ||
"headers": [ | ||
{ | ||
"key": "Cache-Control", | ||
"value": "public, s-maxage=2592000, max-age=2592000" | ||
} | ||
] | ||
} | ||
], | ||
"regions": ["hkg1"], | ||
"redirects": [ | ||
{ | ||
"source": "/gtag/js", | ||
"destination": "https://www.googletagmanager.com/gtag/js" | ||
}, | ||
{ | ||
"source": "/atom.xml", | ||
"destination": "https://cfblog.17lai.site/atom.xml" | ||
}, | ||
{ | ||
"source": "/rss.xml", | ||
"destination": "https://cfblog.17lai.site/rss.xml" | ||
} | ||
{ | ||
"source": "/gtag/js", | ||
"destination": "https://www.googletagmanager.com/gtag/js" | ||
}, | ||
{ | ||
"source": "/atom.xml", | ||
"destination": "https://cfblog.17lai.site/atom.xml" | ||
}, | ||
{ | ||
"source": "/rss.xml", | ||
"destination": "https://cfblog.17lai.site/rss.xml" | ||
} | ||
] | ||
} | ||
|