@devp0nt/route0 1.0.0-next.72 → 1.0.0-next.74

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -24,12 +24,12 @@ __export(index_exports, {
24
24
  module.exports = __toCommonJS(index_exports);
25
25
  var import_flat0 = require("@devp0nt/flat0");
26
26
  const escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
27
- const getPathSegments = (definition) => {
27
+ const getRouteSegments = (definition) => {
28
28
  if (definition === "" || definition === "/") return [];
29
29
  return definition.split("/").filter(Boolean);
30
30
  };
31
- const getPathTokens = (definition) => {
32
- const segments = getPathSegments(definition);
31
+ const getRouteTokens = (definition) => {
32
+ const segments = getRouteSegments(definition);
33
33
  return segments.map((segment) => {
34
34
  const param = segment.match(/^:([A-Za-z0-9_]+)(\?)?$/);
35
35
  if (param) {
@@ -45,8 +45,8 @@ const getPathTokens = (definition) => {
45
45
  return { kind: "static", value: segment };
46
46
  });
47
47
  };
48
- const getPathRegexBaseStrictString = (definition) => {
49
- const tokens = getPathTokens(definition);
48
+ const getRouteRegexBaseStrictString = (definition) => {
49
+ const tokens = getRouteTokens(definition);
50
50
  if (tokens.length === 0) return "";
51
51
  let pattern = "";
52
52
  for (const token of tokens) {
@@ -66,20 +66,20 @@ const getPathRegexBaseStrictString = (definition) => {
66
66
  }
67
67
  return pattern;
68
68
  };
69
- const getPathCaptureKeys = (definition) => {
69
+ const getRouteCaptureKeys = (definition) => {
70
70
  const keys = [];
71
- for (const token of getPathTokens(definition)) {
71
+ for (const token of getRouteTokens(definition)) {
72
72
  if (token.kind === "param") keys.push(token.name);
73
73
  if (token.kind === "wildcard") keys.push("*");
74
74
  }
75
75
  return keys;
76
76
  };
77
77
  const getPathParamsDefinition = (definition) => {
78
- const entries = getPathTokens(definition).filter((t) => t.kind !== "static").map((t) => t.kind === "param" ? [t.name, !t.optional] : ["*", !t.optional]);
78
+ const entries = getRouteTokens(definition).filter((t) => t.kind !== "static").map((t) => t.kind === "param" ? [t.name, !t.optional] : ["*", !t.optional]);
79
79
  return Object.fromEntries(entries);
80
80
  };
81
- const validatePathDefinition = (definition) => {
82
- const segments = getPathSegments(definition);
81
+ const validateRouteDefinition = (definition) => {
82
+ const segments = getRouteSegments(definition);
83
83
  const wildcardSegments = segments.filter((segment) => segment.includes("*"));
84
84
  if (wildcardSegments.length === 0) return;
85
85
  if (wildcardSegments.length > 1) {
@@ -95,11 +95,42 @@ const validatePathDefinition = (definition) => {
95
95
  }
96
96
  };
97
97
  const stripTrailingWildcard = (definition) => definition.replace(/\*\??$/, "");
98
+ const normalizePathname = (pathname) => {
99
+ if (pathname.length > 1 && pathname.endsWith("/")) {
100
+ return pathname.slice(0, -1);
101
+ }
102
+ return pathname;
103
+ };
104
+ const getNormalizedPathnameFromInput = (hrefOrHrefRelOrLocation) => {
105
+ if (hrefOrHrefRelOrLocation instanceof URL) {
106
+ return normalizePathname(hrefOrHrefRelOrLocation.pathname);
107
+ }
108
+ if (typeof hrefOrHrefRelOrLocation !== "string") {
109
+ if (typeof hrefOrHrefRelOrLocation.pathname === "string") {
110
+ return normalizePathname(hrefOrHrefRelOrLocation.pathname);
111
+ }
112
+ hrefOrHrefRelOrLocation = hrefOrHrefRelOrLocation.href || hrefOrHrefRelOrLocation.hrefRel;
113
+ }
114
+ const abs = /^[a-zA-Z][a-zA-Z\d+\-.]*:\/\//.test(hrefOrHrefRelOrLocation);
115
+ const base = abs ? void 0 : "http://example.com";
116
+ const url = new URL(hrefOrHrefRelOrLocation, base);
117
+ return normalizePathname(url.pathname);
118
+ };
98
119
  class Route0 {
99
120
  definition;
100
121
  params;
101
122
  _origin;
102
123
  _callable;
124
+ _regexBaseStrictString;
125
+ _regexBaseString;
126
+ _regexStrictString;
127
+ _regexString;
128
+ _regexStrict;
129
+ _regex;
130
+ _regexAncestor;
131
+ _captureKeys;
132
+ _normalizedDefinition;
133
+ _definitionParts;
103
134
  Infer = null;
104
135
  /** Base URL used when generating absolute URLs (`abs: true`). */
105
136
  get origin() {
@@ -114,7 +145,7 @@ class Route0 {
114
145
  this._origin = origin;
115
146
  }
116
147
  constructor(definition, config = {}) {
117
- validatePathDefinition(definition);
148
+ validateRouteDefinition(definition);
118
149
  this.definition = definition;
119
150
  this.params = Route0._getParamsDefinitionByDefinition(definition);
120
151
  const { origin } = config;
@@ -257,34 +288,80 @@ class Route0 {
257
288
  getParamsKeys() {
258
289
  return Object.keys(this.params);
259
290
  }
260
- getPathTokens() {
261
- return getPathTokens(this.definition);
291
+ getTokens() {
292
+ return getRouteTokens(this.definition);
262
293
  }
263
294
  /** Clones route with optional config override. */
264
295
  clone(config) {
265
296
  return Route0.create(this.definition, config);
266
297
  }
267
- getRegexBaseStrictString() {
268
- return getPathRegexBaseStrictString(this.definition);
298
+ get regexBaseStrictString() {
299
+ if (this._regexBaseStrictString === void 0) {
300
+ this._regexBaseStrictString = getRouteRegexBaseStrictString(this.definition);
301
+ }
302
+ return this._regexBaseStrictString;
269
303
  }
270
- getRegexBaseString() {
271
- return this.getRegexBaseStrictString().replace(/\/+$/, "") + "/?";
304
+ get regexBaseString() {
305
+ if (this._regexBaseString === void 0) {
306
+ this._regexBaseString = this.regexBaseStrictString.replace(/\/+$/, "") + "/?";
307
+ }
308
+ return this._regexBaseString;
272
309
  }
273
- getRegexStrictString() {
274
- return `^${this.getRegexBaseStrictString()}$`;
310
+ get regexStrictString() {
311
+ if (this._regexStrictString === void 0) {
312
+ this._regexStrictString = `^${this.regexBaseStrictString}$`;
313
+ }
314
+ return this._regexStrictString;
275
315
  }
276
- getRegexString() {
277
- return `^${this.getRegexBaseString()}$`;
316
+ get regexString() {
317
+ if (this._regexString === void 0) {
318
+ this._regexString = `^${this.regexBaseString}$`;
319
+ }
320
+ return this._regexString;
278
321
  }
279
- getRegexStrict() {
280
- return new RegExp(this.getRegexStrictString());
322
+ get regexStrict() {
323
+ if (this._regexStrict === void 0) {
324
+ this._regexStrict = new RegExp(this.regexStrictString);
325
+ }
326
+ return this._regexStrict;
281
327
  }
282
- getRegex() {
283
- return new RegExp(this.getRegexString());
328
+ get regex() {
329
+ if (this._regex === void 0) {
330
+ this._regex = new RegExp(this.regexString);
331
+ }
332
+ return this._regex;
333
+ }
334
+ get regexAncestor() {
335
+ if (this._regexAncestor === void 0) {
336
+ this._regexAncestor = new RegExp(`^${this.regexBaseString}(?:/.*)?$`);
337
+ }
338
+ return this._regexAncestor;
339
+ }
340
+ get captureKeys() {
341
+ if (this._captureKeys === void 0) {
342
+ this._captureKeys = getRouteCaptureKeys(this.definition);
343
+ }
344
+ return this._captureKeys;
345
+ }
346
+ get normalizedDefinition() {
347
+ if (this._normalizedDefinition === void 0) {
348
+ this._normalizedDefinition = this.definition.length > 1 && this.definition.endsWith("/") ? this.definition.slice(0, -1) : this.definition;
349
+ }
350
+ return this._normalizedDefinition;
351
+ }
352
+ get definitionParts() {
353
+ if (this._definitionParts === void 0) {
354
+ this._definitionParts = this.normalizedDefinition === "/" ? ["/"] : this.normalizedDefinition.split("/").filter(Boolean);
355
+ }
356
+ return this._definitionParts;
357
+ }
358
+ /** Fast pathname check without building a full location object. */
359
+ isExactPathnameMatch(pathname) {
360
+ return this.regex.test(normalizePathname(pathname));
284
361
  }
285
362
  /** Creates a grouped strict regex pattern string from many routes. */
286
363
  static getRegexStrictStringGroup(routes) {
287
- const patterns = routes.map((route) => route.getRegexStrictString()).join("|");
364
+ const patterns = routes.map((route) => route.regexStrictString).join("|");
288
365
  return `(${patterns})`;
289
366
  }
290
367
  /** Creates a strict grouped regex from many routes. */
@@ -294,7 +371,7 @@ class Route0 {
294
371
  }
295
372
  /** Creates a grouped regex pattern string from many routes. */
296
373
  static getRegexStringGroup(routes) {
297
- const patterns = routes.map((route) => route.getRegexString()).join("|");
374
+ const patterns = routes.map((route) => route.regexString).join("|");
298
375
  return `(${patterns})`;
299
376
  }
300
377
  /** Creates a grouped regex from many routes. */
@@ -378,11 +455,11 @@ class Route0 {
378
455
  const location = Route0.getLocation(hrefOrHrefRelOrLocation);
379
456
  location.route = this.definition;
380
457
  location.params = {};
381
- const pathname = location.pathname.length > 1 && location.pathname.endsWith("/") ? location.pathname.slice(0, -1) : location.pathname;
382
- const paramNames = getPathCaptureKeys(this.definition);
383
- const def = this.definition.length > 1 && this.definition.endsWith("/") ? this.definition.slice(0, -1) : this.definition;
384
- const exactRe = new RegExp(`^${this.getRegexBaseString()}$`);
385
- const ancestorRe = new RegExp(`^${this.getRegexBaseString()}(?:/.*)?$`);
458
+ const pathname = normalizePathname(location.pathname);
459
+ const paramNames = this.captureKeys;
460
+ const defParts = this.definitionParts;
461
+ const exactRe = this.regex;
462
+ const ancestorRe = this.regexAncestor;
386
463
  const exactMatch = pathname.match(exactRe);
387
464
  const ancestorMatch = pathname.match(ancestorRe);
388
465
  const exact = !!exactMatch;
@@ -400,9 +477,7 @@ class Route0 {
400
477
  } else {
401
478
  location.params = {};
402
479
  }
403
- const getParts = (path) => path === "/" ? ["/"] : path.split("/").filter(Boolean);
404
- const defParts = getParts(def);
405
- const pathParts = getParts(pathname);
480
+ const pathParts = pathname === "/" ? ["/"] : pathname.split("/").filter(Boolean);
406
481
  let isPrefix = true;
407
482
  if (pathParts.length > defParts.length) {
408
483
  isPrefix = false;
@@ -540,11 +615,11 @@ class Route0 {
540
615
  };
541
616
  /** True when path structure is equal (param names are ignored). */
542
617
  isSame(other) {
543
- return getPathTokens(this.definition).map((t) => {
618
+ return getRouteTokens(this.definition).map((t) => {
544
619
  if (t.kind === "static") return `s:${t.value}`;
545
620
  if (t.kind === "param") return `p:${t.optional ? "o" : "r"}`;
546
621
  return `w:${t.prefix}:${t.optional ? "o" : "r"}`;
547
- }).join("/") === getPathTokens(other.definition).map((t) => {
622
+ }).join("/") === getRouteTokens(other.definition).map((t) => {
548
623
  if (t.kind === "static") return `s:${t.value}`;
549
624
  if (t.kind === "param") return `p:${t.optional ? "o" : "r"}`;
550
625
  return `w:${t.prefix}:${t.optional ? "o" : "r"}`;
@@ -603,10 +678,10 @@ class Route0 {
603
678
  isConflict(other) {
604
679
  if (!other) return false;
605
680
  other = Route0.create(other);
606
- const thisRegex = this.getRegex();
607
- const otherRegex = other.getRegex();
681
+ const thisRegex = this.regex;
682
+ const otherRegex = other.regex;
608
683
  const makeCandidates = (definition) => {
609
- const tokens = getPathTokens(definition);
684
+ const tokens = getRouteTokens(definition);
610
685
  const values = (token) => {
611
686
  if (token.kind === "static") return [token.value];
612
687
  if (token.kind === "param") return token.optional ? ["", "x"] : ["x"];
@@ -733,8 +808,12 @@ class Routes {
733
808
  }
734
809
  _getLocation(hrefOrHrefRelOrLocation) {
735
810
  const input = hrefOrHrefRelOrLocation;
811
+ const pathname = getNormalizedPathnameFromInput(input);
736
812
  for (const route of this._ordered) {
737
- const loc = route.getLocation(hrefOrHrefRelOrLocation);
813
+ if (!route.isExactPathnameMatch(pathname)) {
814
+ continue;
815
+ }
816
+ const loc = route.getLocation(input);
738
817
  if (loc.exact) {
739
818
  return loc;
740
819
  }