Skip to content

Commit 2a3d359

Browse files
committed
100% test coverage
1 parent 85e5e2c commit 2a3d359

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
@@ -1645,6 +1645,35 @@ export const MATCH_TESTS: MatchTestSet[] = [
16451645
},
16461646
],
16471647
},
1648+
{
1649+
path: "%25:foo..:bar",
1650+
options: {
1651+
delimiter: "%25",
1652+
},
1653+
tests: [
1654+
{
1655+
input: "%25hello..world",
1656+
expected: {
1657+
path: "%25hello..world",
1658+
params: { foo: "hello", bar: "world" },
1659+
},
1660+
},
1661+
{
1662+
input: "%25555..222",
1663+
expected: {
1664+
path: "%25555..222",
1665+
params: { foo: "555", bar: "222" },
1666+
},
1667+
},
1668+
{
1669+
input: "%25555....222%25",
1670+
expected: {
1671+
path: "%25555....222%25",
1672+
params: { foo: "555..", bar: "222" },
1673+
},
1674+
},
1675+
],
1676+
},
16481677

16491678
/**
16501679
* 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
@@ -619,40 +619,65 @@ function negate(delimiter: string, backtrack: string) {
619619
}
620620

621621
/**
622-
* Stringify token data into a path string.
622+
* Stringify an array of tokens into a path string.
623623
*/
624-
export function stringify(data: TokenData) {
625-
return data.tokens
626-
.map(function stringifyToken(token, index, tokens): string {
627-
if (token.type === "text") return escapeText(token.value);
628-
if (token.type === "group") {
629-
return `{${token.tokens.map(stringifyToken).join("")}}`;
630-
}
624+
function stringifyTokens(tokens: Token[]): string {
625+
let value = "";
626+
let i = 0;
627+
628+
function name(value: string) {
629+
const isSafe = isNameSafe(value) && isNextNameSafe(tokens[i]);
630+
return isSafe ? value : JSON.stringify(value);
631+
}
632+
633+
while (i < tokens.length) {
634+
const token = tokens[i++];
631635

632-
const isSafe =
633-
isNameSafe(token.name) && isNextNameSafe(tokens[index + 1]);
634-
const key = isSafe ? token.name : JSON.stringify(token.name);
636+
if (token.type === "text") {
637+
value += escapeText(token.value);
638+
continue;
639+
}
635640

636-
if (token.type === "param") return `:${key}`;
637-
if (token.type === "wildcard") return `*${key}`;
638-
throw new TypeError(`Unexpected token: ${token}`);
639-
})
640-
.join("");
641+
if (token.type === "group") {
642+
value += `{${stringifyTokens(token.tokens)}}`;
643+
continue;
644+
}
645+
646+
if (token.type === "param") {
647+
value += `:${name(token.name)}`;
648+
continue;
649+
}
650+
651+
if (token.type === "wildcard") {
652+
value += `*${name(token.name)}`;
653+
continue;
654+
}
655+
656+
throw new TypeError(`Unknown token type: ${(token as any).type}`);
657+
}
658+
659+
return value;
660+
}
661+
662+
/**
663+
* Stringify token data into a path string.
664+
*/
665+
export function stringify(data: TokenData) {
666+
return stringifyTokens(data.tokens);
641667
}
642668

643669
/**
644670
* Validate the parameter name contains valid ID characters.
645671
*/
646672
function isNameSafe(name: string) {
647673
const [first, ...rest] = name;
648-
if (!ID_START.test(first)) return false;
649-
return rest.every((char) => ID_CONTINUE.test(char));
674+
return ID_START.test(first) && rest.every((char) => ID_CONTINUE.test(char));
650675
}
651676

652677
/**
653678
* Validate the next token does not interfere with the current param name.
654679
*/
655680
function isNextNameSafe(token: Token | undefined) {
656-
if (!token || token.type !== "text") return true;
657-
return !ID_CONTINUE.test(token.value[0]);
681+
if (token && token.type === "text") return !ID_CONTINUE.test(token.value[0]);
682+
return true;
658683
}

0 commit comments

Comments
 (0)