Skip to content

Commit

Permalink
feat(pachages): add parse query & stringify query
Browse files Browse the repository at this point in the history
  • Loading branch information
mufeng889 committed Aug 8, 2024
1 parent 8d6cc79 commit c0134c7
Show file tree
Hide file tree
Showing 3 changed files with 224 additions and 90 deletions.
6 changes: 4 additions & 2 deletions packages/simple-router/src/matcher/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type { ElegantConstRoute } from '@ohh-889/react-auto-route';
import type { Location } from 'react-router-dom';
import type { RouteLocationNamedRaw } from '../types';
import { stringifyQuery } from '../query';
import type { RouteRecordRaw } from './types';
import { createRouteRecordMatcher } from './pathMatcher';
import { generatePath, getQueryParams, mergeMetaFields, normalizeRouteRecord, objectToQueryParams } from './shared';
import { generatePath, getQueryParams, mergeMetaFields, normalizeRouteRecord } from './shared';

class CreateRouterMatcher {
// Internal routes maintained for react-router
Expand Down Expand Up @@ -155,7 +156,8 @@ class CreateRouterMatcher {
if ('query' in location) {
query = location.query || {};

const queryParams = objectToQueryParams(query);
const queryParams = stringifyQuery(query);
console.log(queryParams);

fullPath += queryParams ? `?${queryParams}` : '';
}
Expand Down
135 changes: 135 additions & 0 deletions packages/simple-router/src/query.ts
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, ']');
}
173 changes: 85 additions & 88 deletions vercel.json
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"
}
]
}

0 comments on commit c0134c7

Please sign in to comment.