Skip to content

Commit a6bdf40

Browse files
authored
Improved error messages and stack size (#363)
1 parent c6d7244 commit a6bdf40

File tree

4 files changed

+294
-167
lines changed

4 files changed

+294
-167
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
"size-limit": [
5454
{
5555
"path": "dist/index.js",
56-
"limit": "2.2 kB"
56+
"limit": "2 kB"
5757
}
5858
],
5959
"ts-scripts": {

src/cases.spec.ts

Lines changed: 88 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
type CompileOptions,
77
type ParamData,
88
TokenData,
9+
Path,
910
} from "./index.js";
1011

1112
export interface ParserTestSet {
@@ -21,7 +22,7 @@ export interface StringifyTestSet {
2122
}
2223

2324
export interface CompileTestSet {
24-
path: string;
25+
path: Path;
2526
options?: CompileOptions & ParseOptions;
2627
tests: Array<{
2728
input: ParamData | undefined;
@@ -30,7 +31,7 @@ export interface CompileTestSet {
3031
}
3132

3233
export interface MatchTestSet {
33-
path: string;
34+
path: Path | Path[];
3435
options?: MatchOptions & ParseOptions;
3536
tests: Array<{
3637
input: string;
@@ -41,64 +42,99 @@ export interface MatchTestSet {
4142
export const PARSER_TESTS: ParserTestSet[] = [
4243
{
4344
path: "/",
44-
expected: new TokenData([{ type: "text", value: "/" }]),
45+
expected: new TokenData([{ type: "text", value: "/" }], "/"),
4546
},
4647
{
4748
path: "/:test",
48-
expected: new TokenData([
49-
{ type: "text", value: "/" },
50-
{ type: "param", name: "test" },
51-
]),
49+
expected: new TokenData(
50+
[
51+
{ type: "text", value: "/" },
52+
{ type: "param", name: "test" },
53+
],
54+
"/:test",
55+
),
56+
},
57+
{
58+
path: "/:a:b",
59+
expected: new TokenData(
60+
[
61+
{ type: "text", value: "/" },
62+
{ type: "param", name: "a" },
63+
{ type: "param", name: "b" },
64+
],
65+
"/:a:b",
66+
),
5267
},
5368
{
5469
path: '/:"0"',
55-
expected: new TokenData([
56-
{ type: "text", value: "/" },
57-
{ type: "param", name: "0" },
58-
]),
70+
expected: new TokenData(
71+
[
72+
{ type: "text", value: "/" },
73+
{ type: "param", name: "0" },
74+
],
75+
'/:"0"',
76+
),
5977
},
6078
{
6179
path: "/:_",
62-
expected: new TokenData([
63-
{ type: "text", value: "/" },
64-
{ type: "param", name: "_" },
65-
]),
80+
expected: new TokenData(
81+
[
82+
{ type: "text", value: "/" },
83+
{ type: "param", name: "_" },
84+
],
85+
"/:_",
86+
),
6687
},
6788
{
6889
path: "/:café",
69-
expected: new TokenData([
70-
{ type: "text", value: "/" },
71-
{ type: "param", name: "café" },
72-
]),
90+
expected: new TokenData(
91+
[
92+
{ type: "text", value: "/" },
93+
{ type: "param", name: "café" },
94+
],
95+
"/:café",
96+
),
7397
},
7498
{
7599
path: '/:"123"',
76-
expected: new TokenData([
77-
{ type: "text", value: "/" },
78-
{ type: "param", name: "123" },
79-
]),
100+
expected: new TokenData(
101+
[
102+
{ type: "text", value: "/" },
103+
{ type: "param", name: "123" },
104+
],
105+
'/:"123"',
106+
),
80107
},
81108
{
82109
path: '/:"1\\"\\2\\"3"',
83-
expected: new TokenData([
84-
{ type: "text", value: "/" },
85-
{ type: "param", name: '1"2"3' },
86-
]),
110+
expected: new TokenData(
111+
[
112+
{ type: "text", value: "/" },
113+
{ type: "param", name: '1"2"3' },
114+
],
115+
'/:"1\\"\\2\\"3"',
116+
),
87117
},
88118
{
89119
path: "/*path",
90-
expected: new TokenData([
91-
{ type: "text", value: "/" },
92-
{ type: "wildcard", name: "path" },
93-
]),
120+
expected: new TokenData(
121+
[
122+
{ type: "text", value: "/" },
123+
{ type: "wildcard", name: "path" },
124+
],
125+
"/*path",
126+
),
94127
},
95128
{
96129
path: '/:"test"stuff',
97-
expected: new TokenData([
98-
{ type: "text", value: "/" },
99-
{ type: "param", name: "test" },
100-
{ type: "text", value: "stuff" },
101-
]),
130+
expected: new TokenData(
131+
[
132+
{ type: "text", value: "/" },
133+
{ type: "param", name: "test" },
134+
{ type: "text", value: "stuff" },
135+
],
136+
'/:"test"stuff',
137+
),
102138
},
103139
];
104140

@@ -1609,4 +1645,20 @@ export const MATCH_TESTS: MatchTestSet[] = [
16091645
},
16101646
],
16111647
},
1648+
1649+
/**
1650+
* Array input is normalized.
1651+
*/
1652+
{
1653+
path: ["/:foo/:bar", "/:foo/:baz"],
1654+
tests: [
1655+
{
1656+
input: "/hello/world",
1657+
expected: {
1658+
path: "/hello/world",
1659+
params: { foo: "hello", bar: "world" },
1660+
},
1661+
},
1662+
],
1663+
},
16121664
];

src/index.spec.ts

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import { describe, it, expect } from "vitest";
2-
import { parse, compile, match, stringify } from "./index.js";
2+
import {
3+
parse,
4+
compile,
5+
match,
6+
stringify,
7+
pathToRegexp,
8+
TokenData,
9+
} from "./index.js";
310
import {
411
PARSER_TESTS,
512
COMPILE_TESTS,
@@ -15,38 +22,39 @@ describe("path-to-regexp", () => {
1522
it("should throw on unbalanced group", () => {
1623
expect(() => parse("/{:foo,")).toThrow(
1724
new TypeError(
18-
"Unexpected END at 7, expected }: https://git.new/pathToRegexpError",
25+
"Unexpected END at index 7, expected }: /{:foo,; visit https://git.new/pathToRegexpError for info",
1926
),
2027
);
2128
});
29+
2230
it("should throw on nested unbalanced group", () => {
2331
expect(() => parse("/{:foo/{x,y}")).toThrow(
2432
new TypeError(
25-
"Unexpected END at 12, expected }: https://git.new/pathToRegexpError",
33+
"Unexpected END at index 12, expected }: /{:foo/{x,y}; visit https://git.new/pathToRegexpError for info",
2634
),
2735
);
2836
});
2937

3038
it("should throw on missing param name", () => {
3139
expect(() => parse("/:/")).toThrow(
3240
new TypeError(
33-
"Missing parameter name at 2: https://git.new/pathToRegexpError",
41+
"Missing parameter name at index 2: /:/; visit https://git.new/pathToRegexpError for info",
3442
),
3543
);
3644
});
3745

3846
it("should throw on missing wildcard name", () => {
3947
expect(() => parse("/*/")).toThrow(
4048
new TypeError(
41-
"Missing parameter name at 2: https://git.new/pathToRegexpError",
49+
"Missing parameter name at index 2: /*/; visit https://git.new/pathToRegexpError for info",
4250
),
4351
);
4452
});
4553

4654
it("should throw on unterminated quote", () => {
4755
expect(() => parse('/:"foo')).toThrow(
4856
new TypeError(
49-
"Unterminated quote at 2: https://git.new/pathToRegexpError",
57+
'Unterminated quote at index 2: /:"foo; visit https://git.new/pathToRegexpError for info',
5058
),
5159
);
5260
});
@@ -94,6 +102,63 @@ describe("path-to-regexp", () => {
94102
});
95103
});
96104

105+
describe("pathToRegexp errors", () => {
106+
it("should throw when missing text between params", () => {
107+
expect(() => pathToRegexp("/:foo:bar")).toThrow(
108+
new TypeError(
109+
'Missing text before "bar": /:foo:bar; visit https://git.new/pathToRegexpError for info',
110+
),
111+
);
112+
});
113+
114+
it("should throw when missing text between params using TokenData", () => {
115+
expect(() =>
116+
pathToRegexp(
117+
new TokenData([
118+
{ type: "param", name: "a" },
119+
{ type: "param", name: "b" },
120+
]),
121+
),
122+
).toThrow(
123+
new TypeError(
124+
'Missing text before "b"; visit https://git.new/pathToRegexpError for info',
125+
),
126+
);
127+
});
128+
129+
it("should throw with `originalPath` when missing text between params using TokenData", () => {
130+
expect(() =>
131+
pathToRegexp(
132+
new TokenData(
133+
[
134+
{ type: "param", name: "a" },
135+
{ type: "param", name: "b" },
136+
],
137+
"/[a][b]",
138+
),
139+
),
140+
).toThrow(
141+
new TypeError(
142+
'Missing text before "b": /[a][b]; visit https://git.new/pathToRegexpError for info',
143+
),
144+
);
145+
});
146+
147+
it("should contain the error line", () => {
148+
expect.hasAssertions();
149+
150+
try {
151+
pathToRegexp("/:");
152+
} catch (error) {
153+
const stack = (error as Error).stack
154+
?.split("\n")
155+
.slice(0, 5)
156+
.join("\n");
157+
expect(stack).toContain("index.spec.ts");
158+
}
159+
});
160+
});
161+
97162
describe.each(PARSER_TESTS)(
98163
"parse $path with $options",
99164
({ path, options, expected }) => {

0 commit comments

Comments
 (0)