diff --git a/package.json b/package.json index ed066c7..d914b63 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "size-limit": [ { "path": "dist/index.js", - "limit": "2.2 kB" + "limit": "2.1 kB" } ], "ts-scripts": { diff --git a/src/index.spec.ts b/src/index.spec.ts index 2ea29e6..d9673f8 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -99,7 +99,7 @@ const TESTS: Test[] = [ [ "/test", { - strict: true, + trailing: false, }, ["/test"], [ @@ -112,13 +112,14 @@ const TESTS: Test[] = [ [ "/test/", { - strict: true, + trailing: false, }, ["/test/"], [ ["/test", null], ["/test/", ["/test/"]], - ["/test//", null], + ["/test//", ["/test//"]], + ["/test/route", null], ], [[null, "/test/"]], ], @@ -148,9 +149,10 @@ const TESTS: Test[] = [ ["/test/"], [ ["/test", null], - ["/test/route", ["/test/"]], - ["/test//", ["/test//"]], + ["/test/route", null], ["/test//route", ["/test/"]], + ["/test//", ["/test//"]], + ["/foo//bar", null], ], [[null, "/test/"]], ], @@ -165,7 +167,7 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [ @@ -205,7 +207,7 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, "/", ], @@ -224,7 +226,7 @@ const TESTS: Test[] = [ [ ["", [""]], ["/", ["/"]], - ["route", [""]], + ["route", null], ["/route", [""]], ["/route/", [""]], ], @@ -276,7 +278,7 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [["/route", ["/route", "route"]]], @@ -299,7 +301,7 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, "/", ], @@ -332,7 +334,7 @@ const TESTS: Test[] = [ "/test", { end: false, - strict: true, + trailing: false, }, ["/test"], [ @@ -346,14 +348,15 @@ const TESTS: Test[] = [ "/test/", { end: false, - strict: true, + trailing: false, }, ["/test/"], [ ["/test", null], ["/test/", ["/test/"]], - ["/test//", ["/test/"]], - ["/test/route", ["/test/"]], + ["/test//", ["/test//"]], + ["/test/route", null], + ["/test//route", ["/test/"]], ], [[null, "/test/"]], ], @@ -361,7 +364,7 @@ const TESTS: Test[] = [ "/test.json", { end: false, - strict: true, + trailing: false, }, ["/test.json"], [ @@ -375,7 +378,7 @@ const TESTS: Test[] = [ "/:test", { end: false, - strict: true, + trailing: false, }, [ { @@ -383,7 +386,7 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [ @@ -399,7 +402,7 @@ const TESTS: Test[] = [ "/:test/", { end: false, - strict: true, + trailing: false, }, [ { @@ -407,7 +410,7 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, "/", ], @@ -443,8 +446,8 @@ const TESTS: Test[] = [ ["/test", null], ["/test/", ["/test/"]], ["/test//", ["/test//"]], - ["/test/route", ["/test/"]], - ["/route/test/deep", ["/test/"]], + ["/test/route", null], + ["/route/test/deep", null], ], [[null, "/test/"]], ], @@ -475,7 +478,7 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [ @@ -491,7 +494,7 @@ const TESTS: Test[] = [ "/:test/", { end: false, - strict: true, + trailing: false, }, [ { @@ -499,7 +502,7 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, "/", ], @@ -551,7 +554,7 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [ @@ -583,7 +586,7 @@ const TESTS: Test[] = [ [ "/:test", { - strict: true, + trailing: false, }, [ { @@ -591,7 +594,7 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [ @@ -603,7 +606,7 @@ const TESTS: Test[] = [ [ "/:test/", { - strict: true, + trailing: false, }, [ { @@ -611,13 +614,13 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, "/", ], [ ["/route/", ["/route/", "route"]], - ["/route//", null], + ["/route//", ["/route//", "route"]], ], [[{ test: "route" }, "/route/"]], ], @@ -632,12 +635,14 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [ ["/route.json", ["/route.json", "route.json"]], - ["/route//", ["/route", "route"]], + ["/route//", ["/route//", "route"]], + ["/foo/bar", ["/foo", "foo"]], + ["/foo//bar", ["/foo/", "foo"]], ], [[{ test: "route" }, "/route"]], ], @@ -654,7 +659,7 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "?", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [ @@ -665,7 +670,7 @@ const TESTS: Test[] = [ ], ["/route/nested", null, false], ["/", ["/", undefined], { path: "/", index: 0, params: {} }], - ["//", null], + ["//", ["//", undefined], { path: "//", index: 0, params: {} }], ], [ [null, ""], @@ -675,7 +680,7 @@ const TESTS: Test[] = [ [ "/:test?", { - strict: true, + trailing: false, }, [ { @@ -683,7 +688,7 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "?", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [ @@ -699,7 +704,7 @@ const TESTS: Test[] = [ [ "/:test?/", { - strict: true, + trailing: false, }, [ { @@ -707,7 +712,7 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "?", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, "/", ], @@ -715,7 +720,7 @@ const TESTS: Test[] = [ ["/route", null], ["/route/", ["/route/", "route"]], ["/", ["/", undefined]], - ["//", null], + ["//", ["//", undefined]], ], [ [null, "/"], @@ -731,7 +736,7 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "?", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, "/bar", ], @@ -753,7 +758,7 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "?", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, "-bar", ], @@ -776,7 +781,7 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "*", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, "-bar", ], @@ -801,7 +806,7 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "+", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [ @@ -910,12 +915,12 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "*", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [ ["/", ["/", undefined], { path: "/", index: 0, params: {} }], - ["//", null, false], + ["//", ["//", undefined], { path: "//", index: 0, params: {} }], [ "/route", ["/route", "route"], @@ -1141,7 +1146,7 @@ const TESTS: Test[] = [ prefix: "", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [ @@ -1159,7 +1164,7 @@ const TESTS: Test[] = [ [ ":test", { - strict: true, + trailing: false, }, [ { @@ -1167,7 +1172,7 @@ const TESTS: Test[] = [ prefix: "", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [ @@ -1188,7 +1193,7 @@ const TESTS: Test[] = [ prefix: "", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [ @@ -1208,7 +1213,7 @@ const TESTS: Test[] = [ prefix: "", suffix: "", modifier: "?", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [ @@ -1232,7 +1237,7 @@ const TESTS: Test[] = [ prefix: "", suffix: "/", modifier: "+", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [ @@ -1271,7 +1276,7 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ".json", ], @@ -1352,7 +1357,7 @@ const TESTS: Test[] = [ prefix: ".", suffix: "", modifier: "+", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [ @@ -1396,7 +1401,7 @@ const TESTS: Test[] = [ prefix: ".", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ".", ], @@ -1422,14 +1427,14 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, { name: "format", prefix: ".", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [ @@ -1451,14 +1456,14 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, { name: "format", prefix: ".", suffix: "", modifier: "?", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [ @@ -1483,14 +1488,14 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, { name: "format", prefix: ".", suffix: "", modifier: "?", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [ @@ -1709,7 +1714,7 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "*", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [ @@ -1732,7 +1737,7 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "+", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [ @@ -1757,7 +1762,7 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "?", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [ @@ -1865,14 +1870,14 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, { name: "test", prefix: "/", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [ @@ -2015,14 +2020,14 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, { name: "bar", prefix: "/", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [["/match/route", ["/match/route", "match", "route"]]], @@ -2037,7 +2042,7 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, "(test)/bar", ], @@ -2085,7 +2090,7 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, "?", ], @@ -2101,7 +2106,7 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "+", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, "baz", ], @@ -2126,7 +2131,7 @@ const TESTS: Test[] = [ prefix: "", suffix: "", modifier: "?", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, "baz", ], @@ -2148,7 +2153,7 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, "(", { @@ -2156,7 +2161,7 @@ const TESTS: Test[] = [ prefix: "", suffix: "", modifier: "?", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ")", ], @@ -2207,14 +2212,14 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "?", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, { name: "bar", prefix: "/", suffix: "", modifier: "?", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, "-ext", ], @@ -2241,14 +2246,14 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, { name: "optional", prefix: "/", suffix: "", modifier: "?", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, "-ext", ], @@ -2272,7 +2277,7 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [["/café", ["/café", "café"]]], @@ -2362,7 +2367,7 @@ const TESTS: Test[] = [ prefix: ".", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [ @@ -2388,37 +2393,6 @@ const TESTS: Test[] = [ [[null, "this is"]], ], - /** - * Ends with. - */ - [ - "/test", - { - endsWith: "?", - }, - ["/test"], - [ - ["/test", ["/test"]], - ["/test?query=string", ["/test"]], - ["/test/?query=string", ["/test/"]], - ["/testx", null], - ], - [[null, "/test"]], - ], - [ - "/test", - { - endsWith: "?", - strict: true, - }, - ["/test"], - [ - ["/test?query=string", ["/test"]], - ["/test/?query=string", null], - ], - [[null, "/test"]], - ], - /** * Custom prefixes. */ @@ -2428,14 +2402,14 @@ const TESTS: Test[] = [ [ { name: "foo", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", prefix: "$", suffix: "", modifier: "", }, { name: "bar", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", prefix: "$", suffix: "", modifier: "?", @@ -2457,21 +2431,21 @@ const TESTS: Test[] = [ "name", { name: "attr1", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", prefix: "/", suffix: "", modifier: "?", }, { name: "attr2", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", prefix: "-", suffix: "", modifier: "?", }, { name: "attr3", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", prefix: "-", suffix: "", modifier: "?", @@ -2613,7 +2587,7 @@ const TESTS: Test[] = [ prefix: "/", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [ @@ -2623,52 +2597,6 @@ const TESTS: Test[] = [ [[{ user: "123" }, "/user/123"]], ], - /** - * https://github.com/pillarjs/path-to-regexp/issues/209 - */ - [ - "/whatever/:foo\\?query=str", - undefined, - [ - "/whatever", - { - name: "foo", - prefix: "/", - suffix: "", - modifier: "", - pattern: "[^\\/#\\?]+?", - }, - "?query=str", - ], - [["/whatever/123?query=str", ["/whatever/123?query=str", "123"]]], - [[{ foo: "123" }, "/whatever/123?query=str"]], - ], - [ - "/whatever/:foo", - { - end: false, - }, - [ - "/whatever", - { - name: "foo", - prefix: "/", - suffix: "", - modifier: "", - pattern: "[^\\/#\\?]+?", - }, - ], - [ - ["/whatever/123", ["/whatever/123", "123"]], - ["/whatever/123/path", ["/whatever/123", "123"]], - ["/whatever/123#fragment", ["/whatever/123", "123"]], - ["/whatever/123?query=str", ["/whatever/123", "123"]], - ], - [ - [{ foo: "123" }, "/whatever/123"], - [{ foo: "#" }, null], - ], - ], /** * https://github.com/pillarjs/path-to-regexp/issues/260 */ @@ -2681,7 +2609,7 @@ const TESTS: Test[] = [ prefix: "", suffix: "", modifier: "*", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [["foobar", ["foobar", "foobar"]]], @@ -2696,7 +2624,7 @@ const TESTS: Test[] = [ prefix: "", suffix: "", modifier: "+", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }, ], [["foobar", ["foobar", "foobar"]]], @@ -2800,7 +2728,7 @@ describe("path-to-regexp", () => { prefix: "/", suffix: "", modifier: "", - pattern: "[^\\/#\\?]+?", + pattern: "[^\\/]+?", }; describe("arguments", () => { diff --git a/src/index.ts b/src/index.ts index 142a2a0..28f7357 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,10 @@ const DEFAULT_PREFIXES = "./"; -const DEFAULT_DELIMITERS = "/#?"; +const DEFAULT_DELIMITER = "/"; const NOOP_ENCODE = (x: string) => x; const NOOP_DECODE = (x: string) => x; +const GROUPS_RE = /\((?:\?<(.*?)>)?(?!\?)/g; + /** * Tokenizer results. */ @@ -128,21 +130,6 @@ function lexer(str: string) { return new Iter(tokens); } -export interface ParseOptions { - /** - * Set the default delimiter for repeat parameters. (default: `'/'`) - */ - delimiter?: string; - /** - * List of characters to automatically consider prefixes when parsing. - */ - prefixes?: string; - /** - * Function for encoding input strings for output into path. - */ - encode?: Encode; -} - class Iter { index = 0; @@ -178,13 +165,28 @@ class Iter { } } +export interface ParseOptions { + /** + * Set the default delimiter for repeat parameters. (default: `'/'`) + */ + delimiter?: string; + /** + * List of characters to automatically consider prefixes when parsing. + */ + prefixes?: string; + /** + * Function for encoding input strings for output into path. + */ + encode?: Encode; +} + /** * Parse a string for the raw tokens. */ export function parse(str: string, options: ParseOptions = {}): Token[] { const { prefixes = DEFAULT_PREFIXES, - delimiter = DEFAULT_DELIMITERS, + delimiter = DEFAULT_DELIMITER, encode = NOOP_ENCODE, } = options; const defaultPattern = `[^${escape(delimiter)}]+?`; @@ -203,7 +205,7 @@ export function parse(str: string, options: ParseOptions = {}): Token[] { if (name || pattern || modifier) { let prefix = char || ""; - if (prefixes.indexOf(prefix) === -1) { + if (!prefixes.includes(prefix)) { path += prefix; prefix = ""; } @@ -281,16 +283,16 @@ export interface TokensToFunctionOptions { * Compile a string to a template function for the path. */ export function compile

( - str: string, + value: string, options?: ParseOptions & TokensToFunctionOptions, ) { - return tokensToFunction

(parse(str, options), options); + return tokensToFunction

(parse(value, options), options); } export type PathFunction

= (data?: P) => string; /** - * Expose a method for transforming tokens into the path function. + * Transform tokens into a path building function. */ export function tokensToFunction

( tokens: Token[], @@ -423,8 +425,12 @@ export function regexpToFunction

( const { decode = NOOP_DECODE } = options; const decoders = keys.map((key) => { if (key.split) { - const splitRe = new RegExp(key.split, "g"); - return (value: string) => value.split(splitRe).map(decode); + const re = new RegExp(`(${key.pattern})(?:${key.split}|$)`, "g"); + return (value: string) => { + const result: string[] = []; + for (const m of value.matchAll(re)) result.push(decode(m[1])); + return result; + }; } return decode; @@ -480,8 +486,8 @@ function encoder( /** * Get the flags for a regexp from the options. */ -function flags(options?: { sensitive?: boolean }) { - return options && options.sensitive ? "" : "i"; +function flags(options: { sensitive?: boolean }) { + return options.sensitive ? "" : "i"; } export interface TokenKey { @@ -510,13 +516,11 @@ export interface Key extends TokenKey { /** * Pull out keys from a regexp. */ -function regexpToRegexp(path: RegExp, keys?: Key[]): RegExp { +function regexpToRegexp(path: RegExp, keys: Key[]): RegExp { if (!keys) return path; - const groupsRegex = /\((?:\?<(.*?)>)?(?!\?)/g; let index = 0; - let execResult: RegExpExecArray | null = null; - while ((execResult = groupsRegex.exec(path.source))) { + for (const execResult of path.source.matchAll(GROUPS_RE)) { keys.push({ // Use parenthesized substring match if available, index otherwise. name: execResult[1] || index++, @@ -535,8 +539,8 @@ function regexpToRegexp(path: RegExp, keys?: Key[]): RegExp { */ function arrayToRegexp( paths: Array, - keys?: Key[], - options?: TokensToRegexpOptions & ParseOptions, + keys: Key[], + options: TokensToRegexpOptions & ParseOptions, ): RegExp { const parts = paths.map((path) => pathToRegexp(path, keys, options).source); return new RegExp(`(?:${parts.join("|")})`, flags(options)); @@ -547,8 +551,8 @@ function arrayToRegexp( */ function stringToRegexp( path: string, - keys?: Key[], - options?: TokensToRegexpOptions & ParseOptions, + keys: Key[], + options: TokensToRegexpOptions & ParseOptions, ) { return tokensToRegexp(parse(path, options), keys, options); } @@ -559,9 +563,13 @@ export interface TokensToRegexpOptions { */ sensitive?: boolean; /** - * When `true` the regexp won't allow an optional trailing delimiter to match. (default: `false`) + * When `true` the regexp allows an optional trailing delimiter to match. (default: `true`) */ - strict?: boolean; + trailing?: boolean; + /** + * When `true` all delimiters can be repeated one or more times. (default: `true`) + */ + loose?: boolean; /** * When `true` the regexp will match to the end of the string. (default: `true`) */ @@ -571,17 +579,9 @@ export interface TokensToRegexpOptions { */ start?: boolean; /** - * Sets the final character for non-ending optimistic matches. (default: `/`) + * Sets the final character for non-ending optimistic matches. (default: `"/"`) */ delimiter?: string; - /** - * List of characters that can also be "end" characters. - */ - endsWith?: string; - /** - * When `true` the regexp will allow repeated delimiters. (default: `false`) - */ - loose?: boolean; } /** @@ -593,22 +593,20 @@ export function tokensToRegexp( options: TokensToRegexpOptions = {}, ): RegExp { const { - strict = false, - loose = false, + trailing = true, + loose = true, start = true, end = true, - delimiter = DEFAULT_DELIMITERS, - endsWith = "", + delimiter = DEFAULT_DELIMITER, } = options; - const endsWithRe = endsWith ? `[${escape(endsWith)}]|$` : "$"; - const delimiterRe = `[${escape(delimiter)}]`; + const delimiterRe = escape(delimiter); const stringify = loose ? encoder(delimiter, escape, repeat) : escape; - let route = start ? "^" : ""; + let pattern = start ? "^" : ""; // Iterate over the tokens and create our regexp string. for (const token of tokens) { if (typeof token === "string") { - route += stringify(token); + pattern += stringify(token); } else { const prefix = stringify(token.prefix); const suffix = stringify(token.suffix); @@ -616,39 +614,23 @@ export function tokensToRegexp( if (token.pattern) { if (token.modifier === "+" || token.modifier === "*") { const mod = token.modifier === "*" ? "?" : ""; - const split = `${suffix}${prefix}`; - keys.push({ ...token, split }); - route += `(?:${prefix}((?:${token.pattern})(?:${split}(?:${token.pattern}))*)${suffix})${mod}`; + const split = `${suffix}${prefix}` || delimiterRe; // Fallback to split on delimiter. + keys.push(Object.assign({}, token, { split })); + pattern += `(?:${prefix}((?:${token.pattern})(?:${split}(?:${token.pattern}))*)${suffix})${mod}`; } else { keys.push(token); - route += `(?:${prefix}(${token.pattern})${suffix})${token.modifier}`; + pattern += `(?:${prefix}(${token.pattern})${suffix})${token.modifier}`; } } else { - route += `(?:${prefix}${suffix})${token.modifier}`; + pattern += `(?:${prefix}${suffix})${token.modifier}`; } } } - if (end) { - if (!strict) route += `${delimiterRe}${loose ? "*" : "?"}`; - route += `(?=${endsWithRe})`; - } else { - const endToken = tokens[tokens.length - 1]; - const isEndDelimited = - typeof endToken === "string" - ? delimiter.indexOf(endToken[endToken.length - 1]) > -1 - : !endToken; - - if (!strict) { - route += `(?:${delimiterRe}${loose ? "+" : ""}(?=${endsWithRe}))?`; - } + if (trailing) pattern += `${delimiterRe}${loose ? "*" : "?"}`; + pattern += end ? "$" : `(?=${delimiterRe}|$)`; - if (!isEndDelimited) { - route += `(?=${delimiterRe}|${endsWithRe})`; - } - } - - return new RegExp(route, flags(options)); + return new RegExp(pattern, flags(options)); } /** @@ -665,8 +647,8 @@ export type Path = string | RegExp | Array; */ export function pathToRegexp( path: Path, - keys?: Key[], - options?: TokensToRegexpOptions & ParseOptions, + keys: Key[] = [], + options: TokensToRegexpOptions & ParseOptions = {}, ) { if (path instanceof RegExp) return regexpToRegexp(path, keys); if (Array.isArray(path)) return arrayToRegexp(path, keys, options); diff --git a/tsconfig.json b/tsconfig.json index 03b0dd0..83a86d2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,8 @@ { "extends": "@borderless/ts-scripts/configs/tsconfig.json", "compilerOptions": { - "target": "ES2015", - "lib": ["ES2015"], + "target": "ES2020", + "lib": ["ES2020"], "rootDir": "src", "outDir": "dist", "module": "NodeNext",