Skip to content

Commit ee2d2cf

Browse files
committed
Encode parser tokens
1 parent 246ec9f commit ee2d2cf

File tree

3 files changed

+43
-30
lines changed

3 files changed

+43
-30
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
"size-limit": [
4747
{
4848
"path": "dist.es2015/index.js",
49-
"limit": "2 kB"
49+
"limit": "2.1 kB"
5050
}
5151
],
5252
"ts-scripts": {

src/index.spec.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ const TESTS: Test[] = [
191191
[{}, null],
192192
[{ test: "abc" }, "/abc"],
193193
[{ test: "a+b" }, "/a+b"],
194-
[{ test: "a+b" }, "/test", { encode: (_, token) => String(token.name) }],
194+
[{ test: "a+b" }, "/test", { encode: () => "test" }],
195195
[{ test: "a+b" }, "/a%2Bb", { encode: encodeURIComponent }],
196196
],
197197
],
@@ -285,7 +285,7 @@ const TESTS: Test[] = [
285285
[{}, null],
286286
[{ test: "abc" }, "/abc"],
287287
[{ test: "a+b" }, "/a+b"],
288-
[{ test: "a+b" }, "/test", { encode: (_, token) => String(token.name) }],
288+
[{ test: "a+b" }, "/test", { encode: () => "test" }],
289289
[{ test: "a+b" }, "/a%2Bb", { encode: encodeURIComponent }],
290290
],
291291
],
@@ -2285,10 +2285,10 @@ const TESTS: Test[] = [
22852285
["/café", undefined, ["/café"], [["/café", ["/café"]]], [[null, "/café"]]],
22862286
[
22872287
"/café",
2288-
{ encode: encodeURI },
2289-
["/café"],
2288+
{ encode: encodeURIComponent },
2289+
["/caf%C3%A9"],
22902290
[["/caf%C3%A9", ["/caf%C3%A9"]]],
2291-
[[null, "/café"]],
2291+
[[null, "/caf%C3%A9"]],
22922292
],
22932293
[
22942294
"packages/",

src/index.ts

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,10 @@ export interface ParseOptions {
137137
* List of characters to automatically consider prefixes when parsing.
138138
*/
139139
prefixes?: string;
140+
/**
141+
* Function for encoding input strings for output into path.
142+
*/
143+
encode?: Encode;
140144
}
141145

142146
class Iter {
@@ -178,11 +182,15 @@ class Iter {
178182
* Parse a string for the raw tokens.
179183
*/
180184
export function parse(str: string, options: ParseOptions = {}): Token[] {
181-
const { prefixes = DEFAULT_PREFIXES, delimiter = DEFAULT_DELIMITER } =
182-
options;
183-
const defaultPattern = `[^${escapeString(delimiter)}]+?`;
185+
const {
186+
prefixes = DEFAULT_PREFIXES,
187+
delimiter = DEFAULT_DELIMITER,
188+
encode = DEFAULT_ENCODE,
189+
} = options;
190+
const defaultPattern = `[^${escape(delimiter)}]+?`;
184191
const result: Token[] = [];
185192
const tokens = lexer(str);
193+
const stringify = encoder(delimiter, encode);
186194
let key = 0;
187195
let path = "";
188196

@@ -207,7 +215,7 @@ export function parse(str: string, options: ParseOptions = {}): Token[] {
207215

208216
result.push({
209217
name: name || key++,
210-
prefix,
218+
prefix: stringify(prefix),
211219
suffix: "",
212220
pattern: pattern || defaultPattern,
213221
modifier: modifier || "",
@@ -222,7 +230,7 @@ export function parse(str: string, options: ParseOptions = {}): Token[] {
222230
}
223231

224232
if (path) {
225-
result.push(path);
233+
result.push(stringify(path));
226234
path = "";
227235
}
228236

@@ -238,8 +246,8 @@ export function parse(str: string, options: ParseOptions = {}): Token[] {
238246
result.push({
239247
name: name || (pattern ? key++ : ""),
240248
pattern: name && !pattern ? defaultPattern : pattern,
241-
prefix,
242-
suffix,
249+
prefix: stringify(prefix),
250+
suffix: stringify(suffix),
243251
modifier: tokens.tryConsume("MODIFIER") || "",
244252
});
245253
continue;
@@ -252,19 +260,21 @@ export function parse(str: string, options: ParseOptions = {}): Token[] {
252260
return result;
253261
}
254262

263+
export type Encode = (value: string) => string;
264+
255265
export interface TokensToFunctionOptions {
256266
/**
257267
* When `true` the regexp will be case sensitive. (default: `false`)
258268
*/
259269
sensitive?: boolean;
260-
/**
261-
* Function for encoding input strings for output.
262-
*/
263-
encode?: (value: string, token: Key) => string;
264270
/**
265271
* When `false` the function can produce an invalid (unmatched) path. (default: `true`)
266272
*/
267273
validate?: boolean;
274+
/**
275+
* Function for encoding input strings for output.
276+
*/
277+
encode?: Encode;
268278
}
269279

270280
/**
@@ -325,7 +335,7 @@ export function tokensToFunction<P extends object = object>(
325335
}
326336

327337
for (let j = 0; j < value.length; j++) {
328-
const segment = encode(value[j], token);
338+
const segment = encode(value[j]);
329339

330340
if (validate && !(matches[i] as RegExp).test(segment)) {
331341
throw new TypeError(
@@ -340,7 +350,7 @@ export function tokensToFunction<P extends object = object>(
340350
}
341351

342352
if (typeof value === "string" || typeof value === "number") {
343-
const segment = encode(String(value), token);
353+
const segment = encode(String(value));
344354

345355
if (validate && !(matches[i] as RegExp).test(segment)) {
346356
throw new TypeError(
@@ -440,10 +450,18 @@ export function regexpToFunction<P extends object = object>(
440450
/**
441451
* Escape a regular expression string.
442452
*/
443-
function escapeString(str: string) {
453+
function escape(str: string) {
444454
return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, "\\$1");
445455
}
446456

457+
/**
458+
* Encode all non-delimiter characters using the encode function.
459+
*/
460+
function encoder(delimiter: string, encode: Encode) {
461+
const re = new RegExp(`[^${escape(delimiter)}]+`, "g");
462+
return (value: string) => value.replace(re, encode);
463+
}
464+
447465
/**
448466
* Get the flags for a regexp from the options.
449467
*/
@@ -538,10 +556,6 @@ export interface TokensToRegexpOptions {
538556
* List of characters that can also be "end" characters.
539557
*/
540558
endsWith?: string;
541-
/**
542-
* Encode path tokens for use in the `RegExp`.
543-
*/
544-
encode?: (value: string) => string;
545559
}
546560

547561
/**
@@ -556,21 +570,20 @@ export function tokensToRegexp(
556570
strict = false,
557571
start = true,
558572
end = true,
559-
encode = DEFAULT_ENCODE,
560573
delimiter = DEFAULT_DELIMITER,
561574
endsWith = "",
562575
} = options;
563-
const endsWithRe = `[${escapeString(endsWith)}]|$`;
564-
const delimiterRe = `[${escapeString(delimiter)}]`;
576+
const endsWithRe = `[${escape(endsWith)}]|$`;
577+
const delimiterRe = `[${escape(delimiter)}]`;
565578
let route = start ? "^" : "";
566579

567580
// Iterate over the tokens and create our regexp string.
568581
for (const token of tokens) {
569582
if (typeof token === "string") {
570-
route += escapeString(encode(token));
583+
route += escape(token);
571584
} else {
572-
const prefix = escapeString(encode(token.prefix));
573-
const suffix = escapeString(encode(token.suffix));
585+
const prefix = escape(token.prefix);
586+
const suffix = escape(token.suffix);
574587

575588
if (token.pattern) {
576589
if (keys) keys.push(token);

0 commit comments

Comments
 (0)