Skip to content

Commit

Permalink
Merge pull request #357 from mohsen1/mohsen--router-utils
Browse files Browse the repository at this point in the history
Use URLSearchParams for parsing search params
  • Loading branch information
peterp authored Apr 10, 2020
2 parents 8d510db + c64cd8a commit eb1e4c9
Showing 1 changed file with 63 additions and 51 deletions.
114 changes: 63 additions & 51 deletions packages/router/src/util.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
// Create a React Context with the given name.
/** Create a React Context with the given name. */
const createNamedContext = (name, defaultValue) => {
const Ctx = React.createContext(defaultValue)
Ctx.displayName = name
return Ctx
}

// Get param name and type tranform for a route
//
// '/blog/{year}/{month}/{day:Int}' => [['year'], ['month'], ['day', 'Int']]
/**
* Get param name and type transform for a route
*
* '/blog/{year}/{month}/{day:Int}' => [['year'], ['month'], ['day', 'Int']]
*/
const paramsForRoute = (route) => {
// Match the strings between `{` and `}`.
const params = [...route.matchAll(/\{([^}]+)\}/g)]
Expand All @@ -18,31 +20,33 @@ const paramsForRoute = (route) => {
})
}

// Definitions of the core param types.
/** Definitions of the core param types. */
const coreParamTypes = {
Int: {
constraint: /\d+/,
transform: Number,
},
}

// Determine if the given route is a match for the given pathname. If so,
// extract any named params and return them in an object.
//
// route - The route path as specified in the <Route path={...} />
// pathname - The pathname from the window.location.
// allParamTypes - The object containing all param type definitions.
//
// Examples:
//
// matchPath('/blog/{year}/{month}/{day}', '/blog/2019/12/07')
// => { match: true, params: { year: '2019', month: '12', day: '07' }}
//
// matchPath('/about', '/')
// => { match: false }
//
// matchPath('/post/{id:Int}', '/post/7')
// => { match: true, params: { id: 7 }}
/**
* Determine if the given route is a match for the given pathname. If so,
* extract any named params and return them in an object.
*
* route - The route path as specified in the <Route path={...} />
* pathname - The pathname from the window.location.
* allParamTypes - The object containing all param type definitions.
*
* Examples:
*
* matchPath('/blog/{year}/{month}/{day}', '/blog/2019/12/07')
* => { match: true, params: { year: '2019', month: '12', day: '07' }}
*
* matchPath('/about', '/')
* => { match: false }
*
* matchPath('/post/{id:Int}', '/post/7')
* => { match: true, params: { id: 7 }}
*/
const matchPath = (route, pathname, paramTypes) => {
// Get the names and the transform types for the given route.
const routeParams = paramsForRoute(route)
Expand Down Expand Up @@ -92,33 +96,38 @@ const matchPath = (route, pathname, paramTypes) => {
return { match: true, params }
}

// Parse the given search string into key/value pairs and return them in an
// object.
//
// Examples:
//
// parseSearch('?key1=val1&key2=val2')
// => { key1: 'val1', key2: 'val2' }
/**
* Parse the given search string into key/value pairs and return them in an
* object.
*
* Examples:
*
* parseSearch('?key1=val1&key2=val2')
* => { key1: 'val1', key2: 'val2' }
*
* @fixme
* This utility ignores keys with multiple values such as `?foo=1&foo=2`.
*/
const parseSearch = (search) => {
if (search === '') {
return {}
}
const searchPart = search.substring(1)
const pairs = searchPart.split('&')
const searchProps = {}
pairs.forEach((pair) => {
const keyval = pair.split('=')
searchProps[keyval[0]] = keyval[1] || ''
})
return searchProps
const searchParams = new URLSearchParams(search)

return [...searchParams.keys()].reduce(
(params, key) => ({
...params,
[key]: searchParams.get(key),
}),
{}
)
}

// Validate a path to make sure it follows the router's rules. If any problems
// are found, a descriptive Error will be thrown, as problems with routes are
// critical enough to be considered fatal.
/**
* Validate a path to make sure it follows the router's rules. If any problems
* are found, a descriptive Error will be thrown, as problems with routes are
* critical enough to be considered fatal.
*/
const validatePath = (path) => {
// Check that path begins with a slash.
if (path[0] !== '/') {
if (!path.startsWith('/')) {
throw new Error('Route path does not begin with a slash: "' + path + '"')
}

Expand All @@ -135,13 +144,16 @@ const validatePath = (path) => {
}
}

// Take a given route path and replace any named parameters with those in the
// given args object. Any extra params not used in the path will be appended
// as key=value pairs in the search part.
//
// Examples:
// replaceParams('/tags/{tag}', { tag: 'code', extra: 'foo' })
// => '/tags/code?extra=foo
/**
* Take a given route path and replace any named parameters with those in the
* given args object. Any extra params not used in the path will be appended
* as key=value pairs in the search part.
*
* Examples:
*
* replaceParams('/tags/{tag}', { tag: 'code', extra: 'foo' })
* => '/tags/code?extra=foo
*/
const replaceParams = (path, args = {}) => {
// Split the path apart and replace named parameters with those sent in,
// then join it back together.
Expand Down

0 comments on commit eb1e4c9

Please sign in to comment.