2020-11-25 20:56:18 +01:00
|
|
|
export interface Group {
|
2020-06-22 20:16:21 +02:00
|
|
|
pos: number
|
|
|
|
repeat: boolean
|
|
|
|
optional: boolean
|
|
|
|
}
|
|
|
|
|
2020-04-28 09:59:47 +02:00
|
|
|
// this isn't importing the escape-string-regex module
|
|
|
|
// to reduce bytes
|
|
|
|
function escapeRegex(str: string) {
|
|
|
|
return str.replace(/[|\\{}()[\]^$+*?.-]/g, '\\$&')
|
|
|
|
}
|
|
|
|
|
2020-06-22 05:00:30 +02:00
|
|
|
function parseParameter(param: string) {
|
2020-06-22 20:16:21 +02:00
|
|
|
const optional = param.startsWith('[') && param.endsWith(']')
|
2020-06-22 05:00:30 +02:00
|
|
|
if (optional) {
|
2020-06-22 20:16:21 +02:00
|
|
|
param = param.slice(1, -1)
|
2020-06-22 05:00:30 +02:00
|
|
|
}
|
2020-06-22 20:16:21 +02:00
|
|
|
const repeat = param.startsWith('...')
|
2020-06-22 05:00:30 +02:00
|
|
|
if (repeat) {
|
2020-06-22 20:16:21 +02:00
|
|
|
param = param.slice(3)
|
2020-06-22 05:00:30 +02:00
|
|
|
}
|
2020-06-22 20:16:21 +02:00
|
|
|
return { key: param, repeat, optional }
|
2020-06-22 05:00:30 +02:00
|
|
|
}
|
|
|
|
|
2019-05-27 20:20:33 +02:00
|
|
|
export function getRouteRegex(
|
2019-05-30 22:42:45 +02:00
|
|
|
normalizedRoute: string
|
2019-12-02 22:55:49 +01:00
|
|
|
): {
|
|
|
|
re: RegExp
|
2020-04-28 09:59:47 +02:00
|
|
|
namedRegex?: string
|
2020-06-16 15:49:13 +02:00
|
|
|
routeKeys?: { [named: string]: string }
|
2020-06-22 20:16:21 +02:00
|
|
|
groups: { [groupName: string]: Group }
|
2019-12-02 22:55:49 +01:00
|
|
|
} {
|
2020-06-22 20:16:21 +02:00
|
|
|
const segments = (normalizedRoute.replace(/\/$/, '') || '/')
|
|
|
|
.slice(1)
|
|
|
|
.split('/')
|
2019-05-27 20:20:33 +02:00
|
|
|
|
2020-06-22 20:16:21 +02:00
|
|
|
const groups: { [groupName: string]: Group } = {}
|
2019-05-27 20:20:33 +02:00
|
|
|
let groupIndex = 1
|
2020-06-22 20:16:21 +02:00
|
|
|
const parameterizedRoute = segments
|
|
|
|
.map((segment) => {
|
|
|
|
if (segment.startsWith('[') && segment.endsWith(']')) {
|
|
|
|
const { key, optional, repeat } = parseParameter(segment.slice(1, -1))
|
|
|
|
groups[key] = { pos: groupIndex++, repeat, optional }
|
|
|
|
return repeat ? (optional ? '(?:/(.+?))?' : '/(.+?)') : '/([^/]+?)'
|
|
|
|
} else {
|
|
|
|
return `/${escapeRegex(segment)}`
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.join('')
|
2020-04-28 09:59:47 +02:00
|
|
|
|
|
|
|
// dead code eliminate for browser since it's only needed
|
|
|
|
// while generating routes-manifest
|
|
|
|
if (typeof window === 'undefined') {
|
2020-07-08 20:45:53 +02:00
|
|
|
let routeKeyCharCode = 97
|
|
|
|
let routeKeyCharLength = 1
|
|
|
|
|
|
|
|
// builds a minimal routeKey using only a-z and minimal number of characters
|
|
|
|
const getSafeRouteKey = () => {
|
|
|
|
let routeKey = ''
|
|
|
|
|
|
|
|
for (let i = 0; i < routeKeyCharLength; i++) {
|
|
|
|
routeKey += String.fromCharCode(routeKeyCharCode)
|
|
|
|
routeKeyCharCode++
|
|
|
|
|
|
|
|
if (routeKeyCharCode > 122) {
|
|
|
|
routeKeyCharLength++
|
|
|
|
routeKeyCharCode = 97
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return routeKey
|
|
|
|
}
|
|
|
|
|
2020-06-16 15:49:13 +02:00
|
|
|
const routeKeys: { [named: string]: string } = {}
|
|
|
|
|
2020-06-22 20:16:21 +02:00
|
|
|
let namedParameterizedRoute = segments
|
|
|
|
.map((segment) => {
|
|
|
|
if (segment.startsWith('[') && segment.endsWith(']')) {
|
|
|
|
const { key, optional, repeat } = parseParameter(segment.slice(1, -1))
|
|
|
|
// replace any non-word characters since they can break
|
|
|
|
// the named regex
|
2020-07-08 20:45:53 +02:00
|
|
|
let cleanedKey = key.replace(/\W/g, '')
|
|
|
|
let invalidKey = false
|
|
|
|
|
|
|
|
// check if the key is still invalid and fallback to using a known
|
|
|
|
// safe key
|
|
|
|
if (cleanedKey.length === 0 || cleanedKey.length > 30) {
|
|
|
|
invalidKey = true
|
|
|
|
}
|
|
|
|
if (!isNaN(parseInt(cleanedKey.substr(0, 1)))) {
|
|
|
|
invalidKey = true
|
|
|
|
}
|
|
|
|
|
|
|
|
if (invalidKey) {
|
|
|
|
cleanedKey = getSafeRouteKey()
|
|
|
|
}
|
|
|
|
|
2020-06-22 20:16:21 +02:00
|
|
|
routeKeys[cleanedKey] = key
|
|
|
|
return repeat
|
|
|
|
? optional
|
|
|
|
? `(?:/(?<${cleanedKey}>.+?))?`
|
|
|
|
: `/(?<${cleanedKey}>.+?)`
|
|
|
|
: `/(?<${cleanedKey}>[^/]+?)`
|
|
|
|
} else {
|
|
|
|
return `/${escapeRegex(segment)}`
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.join('')
|
2020-06-16 15:49:13 +02:00
|
|
|
|
|
|
|
return {
|
2020-07-25 07:11:42 +02:00
|
|
|
re: new RegExp(`^${parameterizedRoute}(?:/)?$`),
|
2020-06-16 15:49:13 +02:00
|
|
|
groups,
|
|
|
|
routeKeys,
|
|
|
|
namedRegex: `^${namedParameterizedRoute}(?:/)?$`,
|
|
|
|
}
|
2020-04-28 09:59:47 +02:00
|
|
|
}
|
|
|
|
|
2019-05-27 20:20:33 +02:00
|
|
|
return {
|
2020-07-25 07:11:42 +02:00
|
|
|
re: new RegExp(`^${parameterizedRoute}(?:/)?$`),
|
2019-05-27 20:20:33 +02:00
|
|
|
groups,
|
|
|
|
}
|
|
|
|
}
|