Skip to content

Commit 964404d

Browse files
committed
Add custom ParseError class
1 parent 265a2a7 commit 964404d

File tree

2 files changed

+62
-51
lines changed

2 files changed

+62
-51
lines changed

src/index.spec.ts

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
stringify,
77
pathToRegexp,
88
TokenData,
9+
ParseError,
910
} from "./index.js";
1011
import {
1112
PARSER_TESTS,
@@ -18,44 +19,65 @@ import {
1819
* Dynamically generate the entire test suite.
1920
*/
2021
describe("path-to-regexp", () => {
22+
describe("ParseError", () => {
23+
it("should contain original path and debug url", () => {
24+
const error = new ParseError(
25+
"Unexpected END at index 7, expected }",
26+
"/{:foo,",
27+
);
28+
29+
expect(error).toBeInstanceOf(TypeError);
30+
expect(error.message).toBe(
31+
"Unexpected END at index 7, expected }: /{:foo,; visit https://git.new/pathToRegexpError for info",
32+
);
33+
expect(error.originalPath).toBe("/{:foo,");
34+
});
35+
36+
it("should omit original url when undefined", () => {
37+
const error = new ParseError(
38+
"Unexpected END at index 7, expected }",
39+
undefined,
40+
);
41+
42+
expect(error).toBeInstanceOf(TypeError);
43+
expect(error.message).toBe(
44+
"Unexpected END at index 7, expected }; visit https://git.new/pathToRegexpError for info",
45+
);
46+
expect(error.originalPath).toBeUndefined();
47+
});
48+
});
49+
2150
describe("parse errors", () => {
2251
it("should throw on unbalanced group", () => {
2352
expect(() => parse("/{:foo,")).toThrow(
24-
new TypeError(
25-
"Unexpected END at index 7, expected }: /{:foo,; visit https://git.new/pathToRegexpError for info",
26-
),
53+
new ParseError("Unexpected END at index 7, expected }", "/{:foo,"),
2754
);
2855
});
2956

3057
it("should throw on nested unbalanced group", () => {
3158
expect(() => parse("/{:foo/{x,y}")).toThrow(
32-
new TypeError(
33-
"Unexpected END at index 12, expected }: /{:foo/{x,y}; visit https://git.new/pathToRegexpError for info",
59+
new ParseError(
60+
"Unexpected END at index 12, expected }",
61+
"/{:foo/{x,y}",
3462
),
3563
);
3664
});
3765

3866
it("should throw on missing param name", () => {
3967
expect(() => parse("/:/")).toThrow(
40-
new TypeError(
41-
"Missing parameter name at index 2: /:/; visit https://git.new/pathToRegexpError for info",
42-
),
68+
new ParseError("Missing parameter name at index 2", "/:/"),
4369
);
4470
});
4571

4672
it("should throw on missing wildcard name", () => {
4773
expect(() => parse("/*/")).toThrow(
48-
new TypeError(
49-
"Missing parameter name at index 2: /*/; visit https://git.new/pathToRegexpError for info",
50-
),
74+
new ParseError("Missing parameter name at index 2", "/*/"),
5175
);
5276
});
5377

5478
it("should throw on unterminated quote", () => {
5579
expect(() => parse('/:"foo')).toThrow(
56-
new TypeError(
57-
'Unterminated quote at index 2: /:"foo; visit https://git.new/pathToRegexpError for info',
58-
),
80+
new ParseError("Unterminated quote at index 2", '/:"foo'),
5981
);
6082
});
6183
});
@@ -105,9 +127,7 @@ describe("path-to-regexp", () => {
105127
describe("pathToRegexp errors", () => {
106128
it("should throw when missing text between params", () => {
107129
expect(() => pathToRegexp("/:foo:bar")).toThrow(
108-
new TypeError(
109-
'Missing text before "bar": /:foo:bar; visit https://git.new/pathToRegexpError for info',
110-
),
130+
new ParseError('Missing text before "bar" param', "/:foo:bar"),
111131
);
112132
});
113133

@@ -119,11 +139,7 @@ describe("path-to-regexp", () => {
119139
{ type: "param", name: "b" },
120140
]),
121141
),
122-
).toThrow(
123-
new TypeError(
124-
'Missing text before "b"; visit https://git.new/pathToRegexpError for info',
125-
),
126-
);
142+
).toThrow(new ParseError('Missing text before "b" param', undefined));
127143
});
128144

129145
it("should throw with `originalPath` when missing text between params using TokenData", () => {
@@ -137,11 +153,7 @@ describe("path-to-regexp", () => {
137153
"/[a][b]",
138154
),
139155
),
140-
).toThrow(
141-
new TypeError(
142-
'Missing text before "b": /[a][b]; visit https://git.new/pathToRegexpError for info',
143-
),
144-
);
156+
).toThrow(new ParseError('Missing text before "b" param', "/[a][b]"));
145157
});
146158

147159
it("should contain the error line", () => {

src/index.ts

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ const DEFAULT_DELIMITER = "/";
22
const NOOP_VALUE = (value: string) => value;
33
const ID_START = /^[$_\p{ID_Start}]$/u;
44
const ID_CONTINUE = /^[$\u200c\u200d\p{ID_Continue}]$/u;
5-
const DEBUG_URL = "https://git.new/pathToRegexpError";
65

76
/**
87
* Encode a string into another string.
@@ -112,16 +111,6 @@ function escape(str: string) {
112111
return str.replace(/[.+*?^${}()[\]|/\\]/g, "\\$&");
113112
}
114113

115-
/**
116-
* Format error so it's easier to debug.
117-
*/
118-
function errorMessage(text: string, originalPath: string | undefined) {
119-
let message = text;
120-
if (originalPath !== undefined) message += `: ${originalPath}`;
121-
message += `; visit ${DEBUG_URL} for info`;
122-
return message;
123-
}
124-
125114
/**
126115
* Plain text.
127116
*/
@@ -179,6 +168,21 @@ export class TokenData {
179168
) {}
180169
}
181170

171+
/**
172+
* ParseError is thrown when there is an error processing the path.
173+
*/
174+
export class ParseError extends TypeError {
175+
constructor(
176+
message: string,
177+
public readonly originalPath: string | undefined,
178+
) {
179+
let text = message;
180+
if (originalPath) text += `: ${originalPath}`;
181+
text += `; visit https://git.new/pathToRegexpError for info`;
182+
super(text);
183+
}
184+
}
185+
182186
/**
183187
* Parse a string for the raw tokens.
184188
*/
@@ -215,16 +219,12 @@ export function parse(str: string, options: ParseOptions = {}): TokenData {
215219
}
216220

217221
if (pos) {
218-
throw new TypeError(
219-
errorMessage(`Unterminated quote at index ${pos}`, str),
220-
);
222+
throw new ParseError(`Unterminated quote at index ${pos}`, str);
221223
}
222224
}
223225

224226
if (!value) {
225-
throw new TypeError(
226-
errorMessage(`Missing parameter name at index ${index}`, str),
227-
);
227+
throw new ParseError(`Missing parameter name at index ${index}`, str);
228228
}
229229

230230
return value;
@@ -292,11 +292,9 @@ export function parse(str: string, options: ParseOptions = {}): TokenData {
292292
continue;
293293
}
294294

295-
throw new TypeError(
296-
errorMessage(
297-
`Unexpected ${type} at index ${index}, expected ${endType}`,
298-
str,
299-
),
295+
throw new ParseError(
296+
`Unexpected ${type} at index ${index}, expected ${endType}`,
297+
str,
300298
);
301299
}
302300

@@ -564,8 +562,9 @@ function toRegExpSource(
564562

565563
if (token.type === "param" || token.type === "wildcard") {
566564
if (!isSafeSegmentParam && !backtrack) {
567-
throw new TypeError(
568-
errorMessage(`Missing text before "${token.name}"`, originalPath),
565+
throw new ParseError(
566+
`Missing text before "${token.name}" ${token.type}`,
567+
originalPath,
569568
);
570569
}
571570

0 commit comments

Comments
 (0)