describe('Path parser', () => {
describe('tokenizer', () => {
it('root', () => {
- expect(tokenizePath('/')).toEqual([[]])
+ expect(tokenizePath('/')).toEqual([
+ [{ type: TokenType.Static, value: '' }],
+ ])
+ })
+
+ it('empty', () => {
+ expect(tokenizePath('')).toEqual([[]])
})
it('escapes :', () => {
}
it('static single', () => {
- matchRegExp('^/$', [[]])
+ matchRegExp('^/?$', [[]], { strict: false })
+ })
+
+ it('empty path', () => {
+ matchRegExp('^$', [[]], { strict: true })
+ })
+
+ it('strict /', () => {
+ matchRegExp('^/$', [[{ type: TokenType.Static, value: '' }]], {
+ strict: true,
+ })
})
it('static single', () => {
matchParams('/home', '/', null)
})
+ it('allows an empty rooot', () => {
+ matchParams('', '/', {})
+ })
+
+ it('makes the difference between "" and "/" when strict', () => {
+ matchParams('', '/', null, { strict: true })
+ matchParams('/', '', null, { strict: true })
+ })
+
it('allow a trailing slash', () => {
matchParams('/home', '/home/', {})
matchParams('/a/b', '/a/b/', {})
// TODO: better syntax? like /a/{b-:param}+
// also to allow repeatable because otherwise groups are meaningless
- it('group', () => {
+ it('group+', () => {
matchParams('/a/:a(?:b-([^/]+\\)?)', '/a/b-one', {
a: 'one',
})
const VALID_PARAM_RE = /[a-zA-Z0-9_]/
export function tokenizePath(path: string): Array<Token[]> {
- if (path === '/') return [[]]
+ if (!path) return [[]]
+ if (path === '/') return [[{ type: TokenType.Static, value: '' }]]
// remove the leading slash
- if (!path) throw new Error('An empty path cannot be tokenized')
+ if (path[0] !== '/') throw new Error('A non-empty path must start with "/"')
function crash(message: string) {
throw new Error(`ERR (${state})/"${buffer}": ${message}`)
const keys: ParamKey[] = []
for (const segment of segments) {
- if (!segment.length) pattern += '/'
+ // allow an empty path to be different from slash
+ // if (!segment.length) pattern += '/'
for (let tokenIndex = 0; tokenIndex < segment.length; tokenIndex++) {
const token = segment[tokenIndex]