From 77d6189de275433d9e459ddf670e3245bbdaadb0 Mon Sep 17 00:00:00 2001 From: Blake Embrey Date: Mon, 25 Aug 2025 12:32:55 -0700 Subject: [PATCH] Remove more bytes from parser --- src/index.spec.ts | 12 +++---- src/index.ts | 88 ++++++++++++++++++++++------------------------- 2 files changed, 47 insertions(+), 53 deletions(-) diff --git a/src/index.spec.ts b/src/index.spec.ts index a1a9a13..a8e2a0f 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -22,26 +22,26 @@ describe("path-to-regexp", () => { describe("ParseError", () => { it("should contain original path and debug url", () => { const error = new PathError( - "Unexpected END at index 7, expected }", + "Unexpected end at index 7, expected }", "/{:foo,", ); expect(error).toBeInstanceOf(TypeError); expect(error.message).toBe( - "Unexpected END at index 7, expected }: /{:foo,; visit https://git.new/pathToRegexpError for info", + "Unexpected end at index 7, expected }: /{:foo,; visit https://git.new/pathToRegexpError for info", ); expect(error.originalPath).toBe("/{:foo,"); }); it("should omit original url when undefined", () => { const error = new PathError( - "Unexpected END at index 7, expected }", + "Unexpected end at index 7, expected }", undefined, ); expect(error).toBeInstanceOf(TypeError); expect(error.message).toBe( - "Unexpected END at index 7, expected }; visit https://git.new/pathToRegexpError for info", + "Unexpected end at index 7, expected }; visit https://git.new/pathToRegexpError for info", ); expect(error.originalPath).toBeUndefined(); }); @@ -50,13 +50,13 @@ describe("path-to-regexp", () => { describe("parse errors", () => { it("should throw on unbalanced group", () => { expect(() => parse("/{:foo,")).toThrow( - new PathError("Unexpected END at index 7, expected }", "/{:foo,"), + new PathError("Unexpected end at index 7, expected }", "/{:foo,"), ); }); it("should throw on nested unbalanced group", () => { expect(() => parse("/{:foo/{x,y}")).toThrow( - new PathError("Unexpected END at index 12, expected }", "/{:foo/{x,y}"), + new PathError("Unexpected end at index 12, expected }", "/{:foo/{x,y}"), ); }); diff --git a/src/index.ts b/src/index.ts index 0df51f9..052cfb5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -60,11 +60,11 @@ export interface CompileOptions { type TokenType = | "{" | "}" - | "WILDCARD" - | "PARAM" - | "CHAR" - | "ESCAPED" - | "END" + | "wildcard" + | "param" + | "char" + | "escape" + | "end" // Reserved for use or ambiguous due to past use. | "(" | ")" @@ -197,29 +197,27 @@ export function parse(str: string, options: ParseOptions = {}): TokenData { let value = ""; if (ID_START.test(chars[index])) { - value += chars[index]; - while (ID_CONTINUE.test(chars[++index])) { - value += chars[index]; - } + do { + value += chars[index++]; + } while (ID_CONTINUE.test(chars[index])); } else if (chars[index] === '"') { - let pos = index; + let quoteStart = index; - while (index < chars.length) { - if (chars[++index] === '"') { + while (index++ < chars.length) { + if (chars[index] === '"') { index++; - pos = 0; + quoteStart = 0; break; } - if (chars[index] === "\\") { - value += chars[++index]; - } else { - value += chars[index]; - } + // Increment over escape characters. + if (chars[index] === "\\") index++; + + value += chars[index]; } - if (pos) { - throw new PathError(`Unterminated quote at index ${pos}`, str); + if (quoteStart) { + throw new PathError(`Unterminated quote at index ${quoteStart}`, str); } } @@ -237,54 +235,50 @@ export function parse(str: string, options: ParseOptions = {}): TokenData { if (type) { tokens.push({ type, index: index++, value }); } else if (value === "\\") { - tokens.push({ type: "ESCAPED", index: index++, value: chars[index++] }); + tokens.push({ type: "escape", index: index++, value: chars[index++] }); } else if (value === ":") { - tokens.push({ type: "PARAM", index: index++, value: name() }); + tokens.push({ type: "param", index: index++, value: name() }); } else if (value === "*") { - tokens.push({ type: "WILDCARD", index: index++, value: name() }); + tokens.push({ type: "wildcard", index: index++, value: name() }); } else { - tokens.push({ type: "CHAR", index: index++, value }); + tokens.push({ type: "char", index: index++, value }); } } - tokens.push({ type: "END", index, value: "" }); + tokens.push({ type: "end", index, value: "" }); function consumeUntil(endType: TokenType): Token[] { const output: Token[] = []; while (true) { - const { type, value, index } = tokens[pos++]; - if (type === endType) break; - - if (type === "CHAR" || type === "ESCAPED") { - let path = value; - while (true) { - const next = tokens[pos]; - if (next.type !== "CHAR" && next.type !== "ESCAPED") break; - pos++; - path += next.value; + const token = tokens[pos++]; + if (token.type === endType) break; + + if (token.type === "char" || token.type === "escape") { + let path = token.value; + let cur = tokens[pos]; + + while (cur.type === "char" || cur.type === "escape") { + path += cur.value; + cur = tokens[++pos]; } - output.push({ type: "text", value: encodePath(path) }); - continue; - } - if (type === "PARAM") { output.push({ - type: "param", - name: value, + type: "text", + value: encodePath(path), }); continue; } - if (type === "WILDCARD") { + if (token.type === "param" || token.type === "wildcard") { output.push({ - type: "wildcard", - name: value, + type: token.type, + name: token.value, }); continue; } - if (type === "{") { + if (token.type === "{") { output.push({ type: "group", tokens: consumeUntil("}"), @@ -293,7 +287,7 @@ export function parse(str: string, options: ParseOptions = {}): TokenData { } throw new PathError( - `Unexpected ${type} at index ${index}, expected ${endType}`, + `Unexpected ${token.type} at index ${token.index}, expected ${endType}`, str, ); } @@ -301,7 +295,7 @@ export function parse(str: string, options: ParseOptions = {}): TokenData { return output; } - return new TokenData(consumeUntil("END"), str); + return new TokenData(consumeUntil("end"), str); } /**