From 17ce0be0662afef63c277b549ba0be0112581510 Mon Sep 17 00:00:00 2001 From: Blake Embrey Date: Thu, 20 Jun 2024 15:42:03 -0700 Subject: [PATCH] Make key prefix/suffix/modifier/separator optional --- Readme.md | 21 +++++++++++---------- src/index.spec.ts | 25 +++++-------------------- src/index.ts | 35 +++++++++++++++-------------------- 3 files changed, 31 insertions(+), 50 deletions(-) diff --git a/Readme.md b/Readme.md index 0061eaf..801e6ef 100644 --- a/Readme.md +++ b/Readme.md @@ -242,13 +242,12 @@ A `parse` function is available and returns `TokenData`, the set of tokens and o ### Token Information -- `name` The name of the token (`string` for named or `number` for unnamed index) -- `prefix` The prefix string for the segment (e.g. `"/"`) -- `suffix` The suffix string for the segment (e.g. `""`) -- `pattern` The RegExp used to match this token (`string`) -- `modifier` The modifier character used for the segment (e.g. `?`) -- `separator` _(optional)_ The string used to separate repeated parameters (modifier is `+` or `*`) -- `optional` _(optional)_ A boolean used to indicate whether the parameter is optional (modifier is `?` or `*`) +- `name` The name of the token +- `prefix` _(optional)_ The prefix string for the segment (e.g. `"/"`) +- `suffix` _(optional)_ The suffix string for the segment (e.g. `""`) +- `pattern` _(optional)_ The pattern defined to match this token +- `modifier` _(optional)_ The modifier character used for the segment (e.g. `?`) +- `separator` _(optional)_ The string used to separate repeated parameters ## Errors @@ -256,11 +255,13 @@ An effort has been made to ensure ambiguous paths from previous releases throw a ### Unexpected `?`, `*`, or `+` -In previous major versions, `/` or `.` were used as implicit prefixes of parameters. So `/:key?` was implicitly `{/:key}?`. +In previous major versions `/` and `.` were used as implicit prefixes of parameters. So `/:key?` was implicitly `{/:key}?`. For example: -This has been made explicit. Assuming `?` as the modifier, if you have a `/` or `.` before the parameter, you want `{.:ext}?` or `{/:ext}?`. If not, you want `{:ext}?`. +- `/:key?` → `{/:key}?` or `/:key*` → `{/:key}*` or `/:key+` → `{/:key}+` +- `.:key?` → `{.:key}?` or `.:key*` → `{.:key}*` or `.:key+` → `{.:key}+` +- `:key?` → `{:key}?` or `:key*` → `{:key}*` or `:key+` → `{:key}+` -### Unexpected `!`, `@`, or `;` +### Unexpected `!`, `@`, `,`, or `;` These characters have been reserved for future use. diff --git a/src/index.spec.ts b/src/index.spec.ts index 7f85628..3f26a97 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -36,31 +36,19 @@ const PARSER_TESTS: ParserTestSet[] = [ }, { path: "/:test", - expected: [ - "/", - { name: "test", prefix: "", suffix: "", pattern: "", modifier: "" }, - ], + expected: ["/", { name: "test" }], }, { path: "/:0", - expected: [ - "/", - { name: "0", prefix: "", suffix: "", pattern: "", modifier: "" }, - ], + expected: ["/", { name: "0" }], }, { path: "/:_", - expected: [ - "/", - { name: "_", prefix: "", suffix: "", pattern: "", modifier: "" }, - ], + expected: ["/", { name: "_" }], }, { path: "/:café", - expected: [ - "/", - { name: "café", prefix: "", suffix: "", pattern: "", modifier: "" }, - ], + expected: ["/", { name: "café" }], }, ]; @@ -2913,10 +2901,7 @@ describe("path-to-regexp", () => { const expectedKeys = [ { name: "id", - prefix: "", - suffix: "", - modifier: "", - pattern: "", + pattern: undefined, }, ]; diff --git a/src/index.ts b/src/index.ts index b42f6db..e727841 100644 --- a/src/index.ts +++ b/src/index.ts @@ -258,15 +258,12 @@ export function parse(str: string, options: ParseOptions = {}): TokenData { if (path) tokens.push(encodePath(path)); const name = it.tryConsume("NAME"); - const pattern = it.tryConsume("PATTERN") || ""; + const pattern = it.tryConsume("PATTERN"); if (name || pattern) { tokens.push({ name: name || String(key++), - prefix: "", - suffix: "", pattern, - modifier: "", }); const next = it.peek(); @@ -283,8 +280,6 @@ export function parse(str: string, options: ParseOptions = {}): TokenData { if (asterisk) { tokens.push({ name: String(key++), - prefix: "", - suffix: "", pattern: `[^${escape(delimiter)}]*`, modifier: "*", separator: delimiter, @@ -296,7 +291,7 @@ export function parse(str: string, options: ParseOptions = {}): TokenData { if (open) { const prefix = it.text(); const name = it.tryConsume("NAME"); - const pattern = it.tryConsume("PATTERN") || ""; + const pattern = it.tryConsume("PATTERN"); const suffix = it.text(); const separator = it.tryConsume(";") ? it.text() : prefix + suffix; @@ -350,6 +345,7 @@ function tokenToFunction( const encodeValue = encode || NOOP_VALUE; const repeated = token.modifier === "+" || token.modifier === "*"; const optional = token.modifier === "?" || token.modifier === "*"; + const { prefix = "", suffix = "", separator = "" } = token; if (encode && repeated) { const stringify = (value: string, index: number) => { @@ -366,9 +362,7 @@ function tokenToFunction( if (value.length === 0) return ""; - return ( - token.prefix + value.map(stringify).join(token.separator) + token.suffix - ); + return prefix + value.map(stringify).join(separator) + suffix; }; if (optional) { @@ -389,7 +383,7 @@ function tokenToFunction( if (typeof value !== "string") { throw new TypeError(`Expected "${token.name}" to be a string`); } - return token.prefix + encodeValue(value) + token.suffix; + return prefix + encodeValue(value) + suffix; }; if (optional) { @@ -546,10 +540,10 @@ function flags(options: { sensitive?: boolean }) { */ export interface Key { name: string; - prefix: string; - suffix: string; - pattern: string; - modifier: string; + prefix?: string; + suffix?: string; + pattern?: string; + modifier?: string; separator?: string; } @@ -593,20 +587,21 @@ function toKeyRegexp(stringify: Encode, delimiter: string) { const segmentPattern = `[^${escape(delimiter)}]+?`; return (key: Key) => { - const prefix = stringify(key.prefix); - const suffix = stringify(key.suffix); + const prefix = key.prefix ? stringify(key.prefix) : ""; + const suffix = key.suffix ? stringify(key.suffix) : ""; + const modifier = key.modifier || ""; if (key.name) { const pattern = key.pattern || segmentPattern; if (key.modifier === "+" || key.modifier === "*") { const mod = key.modifier === "*" ? "?" : ""; - const split = stringify(key.separator || ""); + const split = key.separator ? stringify(key.separator) : ""; return `(?:${prefix}((?:${pattern})(?:${split}(?:${pattern}))*)${suffix})${mod}`; } - return `(?:${prefix}(${pattern})${suffix})${key.modifier}`; + return `(?:${prefix}(${pattern})${suffix})${modifier}`; } - return `(?:${prefix}${suffix})${key.modifier}`; + return `(?:${prefix}${suffix})${modifier}`; }; }