Skip to content

Commit 5de5056

Browse files
committed
100% test coverage
1 parent 4a91505 commit 5de5056

File tree

3 files changed

+82
-20
lines changed

3 files changed

+82
-20
lines changed

src/cases.spec.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1681,6 +1681,35 @@ export const MATCH_TESTS: MatchTestSet[] = [
16811681
},
16821682
],
16831683
},
1684+
{
1685+
path: "%25:foo..:bar",
1686+
options: {
1687+
delimiter: "%25",
1688+
},
1689+
tests: [
1690+
{
1691+
input: "%25hello..world",
1692+
expected: {
1693+
path: "%25hello..world",
1694+
params: { foo: "hello", bar: "world" },
1695+
},
1696+
},
1697+
{
1698+
input: "%25555..222",
1699+
expected: {
1700+
path: "%25555..222",
1701+
params: { foo: "555", bar: "222" },
1702+
},
1703+
},
1704+
{
1705+
input: "%25555....222%25",
1706+
expected: {
1707+
path: "%25555....222%25",
1708+
params: { foo: "555..", bar: "222" },
1709+
},
1710+
},
1711+
],
1712+
},
16841713

16851714
/**
16861715
* Array input is normalized.

src/index.spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,14 @@ describe("path-to-regexp", () => {
159159
});
160160
});
161161

162+
describe("stringify errors", () => {
163+
it("should error on unknown token", () => {
164+
expect(() =>
165+
stringify({ tokens: [{ type: "unknown", value: "test" } as any] }),
166+
).toThrow(new TypeError("Unknown token type: unknown"));
167+
});
168+
});
169+
162170
describe.each(PARSER_TESTS)(
163171
"parse $path with $options",
164172
({ path, options, expected }) => {

src/index.ts

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -600,40 +600,65 @@ function negate(delimiter: string, backtrack: string) {
600600
}
601601

602602
/**
603-
* Stringify token data into a path string.
603+
* Stringify an array of tokens into a path string.
604604
*/
605-
export function stringify(data: TokenData) {
606-
return data.tokens
607-
.map(function stringifyToken(token, index, tokens): string {
608-
if (token.type === "text") return escapeText(token.value);
609-
if (token.type === "group") {
610-
return `{${token.tokens.map(stringifyToken).join("")}}`;
611-
}
605+
function stringifyTokens(tokens: Token[]): string {
606+
let value = "";
607+
let i = 0;
608+
609+
function name(value: string) {
610+
const isSafe = isNameSafe(value) && isNextNameSafe(tokens[i]);
611+
return isSafe ? value : JSON.stringify(value);
612+
}
613+
614+
while (i < tokens.length) {
615+
const token = tokens[i++];
616+
617+
if (token.type === "text") {
618+
value += escapeText(token.value);
619+
continue;
620+
}
621+
622+
if (token.type === "group") {
623+
value += `{${stringifyTokens(token.tokens)}}`;
624+
continue;
625+
}
612626

613-
const isSafe =
614-
isNameSafe(token.name) && isNextNameSafe(tokens[index + 1]);
615-
const key = isSafe ? token.name : JSON.stringify(token.name);
627+
if (token.type === "param") {
628+
value += `:${name(token.name)}`;
629+
continue;
630+
}
631+
632+
if (token.type === "wildcard") {
633+
value += `*${name(token.name)}`;
634+
continue;
635+
}
636+
637+
throw new TypeError(`Unknown token type: ${(token as any).type}`);
638+
}
616639

617-
if (token.type === "param") return `:${key}`;
618-
if (token.type === "wildcard") return `*${key}`;
619-
throw new TypeError(`Unexpected token: ${token}`);
620-
})
621-
.join("");
640+
return value;
641+
}
642+
643+
/**
644+
* Stringify token data into a path string.
645+
*/
646+
export function stringify(data: TokenData) {
647+
return stringifyTokens(data.tokens);
622648
}
623649

624650
/**
625651
* Validate the parameter name contains valid ID characters.
626652
*/
627653
function isNameSafe(name: string) {
628654
const [first, ...rest] = name;
629-
if (!ID_START.test(first)) return false;
630-
return rest.every((char) => ID_CONTINUE.test(char));
655+
return ID_START.test(first) && rest.every((char) => ID_CONTINUE.test(char));
631656
}
632657

633658
/**
634659
* Validate the next token does not interfere with the current param name.
635660
*/
636661
function isNextNameSafe(token: Token | undefined) {
637-
if (!token || token.type !== "text") return true;
638-
return !ID_CONTINUE.test(token.value[0]);
662+
if (token && token.type === "text") return !ID_CONTINUE.test(token.value[0]);
663+
return true;
639664
}

0 commit comments

Comments
 (0)