Skip to content

Commit e9a7e01

Browse files
committed
Use optional groups
1 parent 43cbe49 commit e9a7e01

File tree

4 files changed

+25
-65
lines changed

4 files changed

+25
-65
lines changed

Readme.md

+11-11
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,18 @@ fn("/bar/baz");
4646
//=> { path: '/bar/baz', params: { splat: [ 'bar', 'baz' ] } }
4747
```
4848

49-
### Braces
49+
### Optional
5050

51-
Simple brace expansion can be used to define multiple versions of a patch to match. It's also an effective way to create optional things to match.
51+
Braces can be used to define parts of the path that are optional.
5252

5353
```js
54-
const fn = match("/{a,b,:other}");
54+
const fn = match("/users{/:id}/delete");
5555

56-
fn("/a");
57-
//=> { path: '/a', params: {} }
56+
fn("/users/delete");
57+
//=> { path: '/users/delete', params: {} }
5858

59-
fn("/c");
60-
//=> { path: '/c', params: { other: 'c' } }
59+
fn("/users/123/delete");
60+
//=> { path: '/users/123/delete', params: { id: '123' } }
6161
```
6262

6363
## Match
@@ -146,9 +146,9 @@ An effort has been made to ensure ambiguous paths from previous releases throw a
146146

147147
In past releases, `?`, `*`, and `+` were used to denote optional or repeating parameters. As an alternative, try these:
148148

149-
- For optional (`?`), use an empty segment in a group such as `/:file{.:ext,}`.
150-
- For repeating (`+`), only wildcard matching is supported, such as `/*glob`.
151-
- For optional repeating (`*`), use a group and a wildcard parameter such as `/{*glob,}`.
149+
- For optional (`?`), use an empty segment in a group such as `/:file{.:ext}`.
150+
- For repeating (`+`), only wildcard matching is supported, such as `/*path`.
151+
- For optional repeating (`*`), use a group and a wildcard parameter such as `/files{/*path}`.
152152

153153
### Unexpected `(`, `)`, `[`, `]`, etc.
154154

@@ -167,7 +167,7 @@ Parameter names can be wrapped in double quote characters, and this error means
167167
Path-To-RegExp breaks compatibility with Express <= `4.x` in the following ways:
168168

169169
- Regexp characters can no longer be provided.
170-
- The optional character `?` is no longer supported, use brace expansion instead: `/:file{.:ext,}`.
170+
- The optional character `?` is no longer supported, use braces instead: `/:file{.:ext}`.
171171
- Some characters have new meaning or have been reserved (`{}?*+@!;`).
172172
- The parameter name now supports all JavaScript identifier characters, previously it was only `[a-z0-9]`.
173173

scripts/redos.ts

+3-10
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,9 @@ const TESTS = [
1515
"/*foo/:bar/*baz",
1616
"/@:foo-:baz@",
1717
"/:foo{.:ext}",
18-
"/:foo{.:ext,}",
19-
"/:foo{|:ext|,}",
20-
"/:foo{/:bar,}/:baz",
21-
"/user{,s}/:id",
22-
"/user/{en,de,cn}/:id",
23-
"/user/{en,de,cn}/{1,2,3}",
24-
"/user/{en,de,cn/{1,2,3}}",
25-
"/user/{en,de,cn/{}}",
26-
"/user/{en,de,cn/{}/test}",
27-
"/user/{en,de,cn}/{x}",
18+
"/:foo{|:ext|}",
19+
"/:foo{/:bar}/:baz",
20+
"/user{s}/:id",
2821
"/books/*section/:title",
2922
];
3023

src/cases.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2229,7 +2229,7 @@ export const MATCH_TESTS: MatchTestSet[] = [
22292229
* Multi character delimiters.
22302230
*/
22312231
{
2232-
path: "%25:foo{%25:bar,}",
2232+
path: "%25:foo{%25:bar}",
22332233
options: {
22342234
delimiter: "%25",
22352235
},

src/index.ts

+10-43
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ export interface CompileOptions extends PathOptions {
6161
type TokenType =
6262
| "{"
6363
| "}"
64-
| ","
6564
| "WILDCARD"
6665
| "PARAM"
6766
| "CHAR"
@@ -89,7 +88,6 @@ const SIMPLE_TOKENS: Record<string, TokenType> = {
8988
// Groups.
9089
"{": "{",
9190
"}": "}",
92-
",": ",",
9391
// Reserved.
9492
"(": "(",
9593
")": ")",
@@ -248,7 +246,7 @@ export interface Wildcard {
248246
*/
249247
export interface Group {
250248
type: "group";
251-
children: Array<Token[]>;
249+
tokens: Token[];
252250
}
253251

254252
/**
@@ -284,7 +282,7 @@ export function parse(str: string, options: ParseOptions = {}): TokenData {
284282
options;
285283
const it = new Iter(lexer(str));
286284

287-
function consume(brace: boolean): [end: boolean, tokens: Token[]] {
285+
function consume(endType: TokenType): Token[] {
288286
const tokens: Token[] = [];
289287

290288
while (true) {
@@ -311,34 +309,19 @@ export function parse(str: string, options: ParseOptions = {}): TokenData {
311309

312310
const open = it.tryConsume("{");
313311
if (open) {
314-
const children: Array<Token[]> = [];
315-
316-
while (true) {
317-
const [end, tokens] = consume(true);
318-
children.push(tokens);
319-
if (end) break;
320-
}
321-
322312
tokens.push({
323313
type: "group",
324-
children: children,
314+
tokens: consume("}"),
325315
});
326316
continue;
327317
}
328318

329-
if (brace) {
330-
const comma = it.tryConsume(",");
331-
if (comma) return [false, tokens];
332-
it.consume("}");
333-
} else {
334-
it.consume("END");
335-
}
336-
337-
return [true, tokens];
319+
it.consume(endType);
320+
return tokens;
338321
}
339322
}
340323

341-
const [, tokens] = consume(false);
324+
const tokens = consume("END");
342325
return new TokenData(tokens, delimiter);
343326
}
344327

@@ -408,19 +391,7 @@ function tokenToFunction(
408391
if (token.type === "text") return () => [token.value];
409392

410393
if (token.type === "group") {
411-
const fns = token.children.map((child) =>
412-
tokensToFunction(child, delimiter, encode),
413-
);
414-
415-
return (data) => {
416-
const allMissing: string[] = [];
417-
for (const fn of fns) {
418-
const [value, ...missing] = fn(data);
419-
if (!missing.length) return [value];
420-
allMissing.push(...missing);
421-
}
422-
return ["", ...allMissing];
423-
};
394+
return tokensToFunction(token.tokens, delimiter, encode);
424395
}
425396

426397
const encodeValue = encode || NOOP_VALUE;
@@ -569,14 +540,10 @@ function* flatten(
569540
const token = tokens[index];
570541

571542
if (token.type === "group") {
572-
for (const child of token.children) {
573-
const fork = init.slice();
574-
for (const seq of flatten(child, 0, fork)) {
575-
yield* flatten(tokens, index + 1, seq);
576-
}
543+
const fork = init.slice();
544+
for (const seq of flatten(token.tokens, 0, fork)) {
545+
yield* flatten(tokens, index + 1, seq);
577546
}
578-
579-
if (token.children.length) return;
580547
} else {
581548
init.push(token);
582549
}

0 commit comments

Comments
 (0)