@solidjs/router 0.4.3 → 0.5.1

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.
package/dist/index.js CHANGED
@@ -1,15 +1,13 @@
1
- import { isServer, delegateEvents, createComponent as createComponent$1, mergeProps as mergeProps$1, spread, insert, effect, setAttribute, template } from 'solid-js/web';
2
- import { createSignal, onCleanup, runWithOwner, createMemo, getOwner, createContext, useContext, untrack, useTransition, on, resetErrorBoundaries, createRenderEffect, createComponent, children, createRoot, Show, mergeProps, splitProps } from 'solid-js';
1
+ import { isServer, delegateEvents, createComponent as createComponent$1, spread, mergeProps as mergeProps$1, template } from 'solid-js/web';
2
+ import { createSignal, onCleanup, getOwner, runWithOwner, createMemo, createContext, useContext, untrack, createRenderEffect, createComponent, on, startTransition, resetErrorBoundaries, children, createRoot, Show, mergeProps, splitProps } from 'solid-js';
3
3
 
4
4
  function bindEvent(target, type, handler) {
5
5
  target.addEventListener(type, handler);
6
6
  return () => target.removeEventListener(type, handler);
7
7
  }
8
-
9
8
  function intercept([value, setValue], get, set) {
10
9
  return [get ? () => get(value()) : value, set ? v => setValue(set(v)) : setValue];
11
10
  }
12
-
13
11
  function querySelector(selector) {
14
12
  // Guard against selector being an invalid CSS selector
15
13
  try {
@@ -18,24 +16,19 @@ function querySelector(selector) {
18
16
  return null;
19
17
  }
20
18
  }
21
-
22
19
  function scrollToHash(hash, fallbackTop) {
23
20
  const el = querySelector(`#${hash}`);
24
-
25
21
  if (el) {
26
22
  el.scrollIntoView();
27
23
  } else if (fallbackTop) {
28
24
  window.scrollTo(0, 0);
29
25
  }
30
26
  }
31
-
32
27
  function createIntegration(get, set, init, utils) {
33
28
  let ignore = false;
34
-
35
29
  const wrap = value => typeof value === "string" ? {
36
30
  value
37
31
  } : value;
38
-
39
32
  const signal = intercept(createSignal(wrap(get()), {
40
33
  equals: (a, b) => a.value === b.value
41
34
  }), undefined, next => {
@@ -64,7 +57,6 @@ function normalizeIntegration(integration) {
64
57
  signal: integration
65
58
  };
66
59
  }
67
-
68
60
  return integration;
69
61
  }
70
62
  function staticIntegration(obj) {
@@ -87,7 +79,6 @@ function pathIntegration() {
87
79
  } else {
88
80
  window.history.pushState(state, "", value);
89
81
  }
90
-
91
82
  scrollToHash(window.location.hash.slice(1), scroll);
92
83
  }, notify => bindEvent(window, "popstate", () => notify()), {
93
84
  go: delta => window.history.go(delta)
@@ -105,7 +96,6 @@ function hashIntegration() {
105
96
  } else {
106
97
  window.location.hash = value;
107
98
  }
108
-
109
99
  const hashIndex = value.indexOf("#");
110
100
  const hash = hashIndex >= 0 ? value.slice(hashIndex + 1) : "";
111
101
  scrollToHash(hash, scroll);
@@ -113,37 +103,63 @@ function hashIntegration() {
113
103
  go: delta => window.history.go(delta),
114
104
  renderPath: path => `#${path}`,
115
105
  parsePath: str => {
116
- const to = str.replace(/^.*?#/, ""); // Hash-only hrefs like `#foo` from plain anchors will come in as `/#foo` whereas a link to
106
+ const to = str.replace(/^.*?#/, "");
107
+ // Hash-only hrefs like `#foo` from plain anchors will come in as `/#foo` whereas a link to
117
108
  // `/foo` will be `/#/foo`. Check if the to starts with a `/` and if not append it as a hash
118
109
  // to the current path so we can handle these in-page anchors correctly.
119
-
120
110
  if (!to.startsWith("/")) {
121
111
  const [, path = "/"] = window.location.hash.split("#", 2);
122
112
  return `${path}#${to}`;
123
113
  }
124
-
125
114
  return to;
126
115
  }
127
116
  });
128
117
  }
129
118
 
119
+ function createBeforeLeave() {
120
+ let listeners = new Set();
121
+ function subscribe(listener) {
122
+ listeners.add(listener);
123
+ return () => listeners.delete(listener);
124
+ }
125
+ let ignore = false;
126
+ function confirm(to, options) {
127
+ if (ignore) return !(ignore = false);
128
+ const e = {
129
+ to,
130
+ options,
131
+ defaultPrevented: false,
132
+ preventDefault: () => e.defaultPrevented = true
133
+ };
134
+ for (const l of listeners) l.listener({
135
+ ...e,
136
+ from: l.location,
137
+ retry: force => {
138
+ force && (ignore = true);
139
+ l.navigate(to, options);
140
+ }
141
+ });
142
+ return !e.defaultPrevented;
143
+ }
144
+ return {
145
+ subscribe,
146
+ confirm
147
+ };
148
+ }
149
+
130
150
  const hasSchemeRegex = /^(?:[a-z0-9]+:)?\/\//i;
131
151
  const trimPathRegex = /^\/+|\/+$/g;
132
-
133
- function normalize(path, omitSlash = false) {
152
+ function normalizePath(path, omitSlash = false) {
134
153
  const s = path.replace(trimPathRegex, "");
135
154
  return s ? omitSlash || /^[?#]/.test(s) ? s : "/" + s : "";
136
155
  }
137
-
138
156
  function resolvePath(base, path, from) {
139
157
  if (hasSchemeRegex.test(path)) {
140
158
  return undefined;
141
159
  }
142
-
143
- const basePath = normalize(base);
144
- const fromPath = from && normalize(from);
160
+ const basePath = normalizePath(base);
161
+ const fromPath = from && normalizePath(from);
145
162
  let result = "";
146
-
147
163
  if (!fromPath || path.startsWith("/")) {
148
164
  result = basePath;
149
165
  } else if (fromPath.toLowerCase().indexOf(basePath.toLowerCase()) !== 0) {
@@ -151,18 +167,16 @@ function resolvePath(base, path, from) {
151
167
  } else {
152
168
  result = fromPath;
153
169
  }
154
-
155
- return (result || "/") + normalize(path, !result);
170
+ return (result || "/") + normalizePath(path, !result);
156
171
  }
157
172
  function invariant(value, message) {
158
173
  if (value == null) {
159
174
  throw new Error(message);
160
175
  }
161
-
162
176
  return value;
163
177
  }
164
178
  function joinPaths(from, to) {
165
- return normalize(from).replace(/\/*(\*.*)?$/g, "") + normalize(to);
179
+ return normalizePath(from).replace(/\/*(\*.*)?$/g, "") + normalizePath(to);
166
180
  }
167
181
  function extractSearchParams(url) {
168
182
  const params = {};
@@ -181,20 +195,16 @@ function createMatcher(path, partial) {
181
195
  return location => {
182
196
  const locSegments = location.split("/").filter(Boolean);
183
197
  const lenDiff = locSegments.length - len;
184
-
185
198
  if (lenDiff < 0 || lenDiff > 0 && splat === undefined && !partial) {
186
199
  return null;
187
200
  }
188
-
189
201
  const match = {
190
202
  path: len ? "" : "/",
191
203
  params: {}
192
204
  };
193
-
194
205
  for (let i = 0; i < len; i++) {
195
206
  const segment = segments[i];
196
207
  const locSegment = locSegments[i];
197
-
198
208
  if (segment[0] === ":") {
199
209
  match.params[segment.slice(1)] = locSegment;
200
210
  } else if (segment.localeCompare(locSegment, undefined, {
@@ -202,14 +212,11 @@ function createMatcher(path, partial) {
202
212
  }) !== 0) {
203
213
  return null;
204
214
  }
205
-
206
215
  match.path += `/${locSegment}`;
207
216
  }
208
-
209
217
  if (splat) {
210
218
  match.params[splat] = lenDiff ? locSegments.slice(-lenDiff).join("/") : "";
211
219
  }
212
-
213
220
  return match;
214
221
  };
215
222
  }
@@ -226,21 +233,17 @@ function createMemoObject(fn) {
226
233
  if (!map.has(property)) {
227
234
  runWithOwner(owner, () => map.set(property, createMemo(() => fn()[property])));
228
235
  }
229
-
230
236
  return map.get(property)();
231
237
  },
232
-
233
238
  getOwnPropertyDescriptor() {
234
239
  return {
235
240
  enumerable: true,
236
241
  configurable: true
237
242
  };
238
243
  },
239
-
240
244
  ownKeys() {
241
245
  return Reflect.ownKeys(fn());
242
246
  }
243
-
244
247
  });
245
248
  }
246
249
  function mergeSearchString(search, params) {
@@ -260,17 +263,17 @@ function expandOptionals(pattern) {
260
263
  if (!match) return [pattern];
261
264
  let prefix = pattern.slice(0, match.index);
262
265
  let suffix = pattern.slice(match.index + match[0].length);
263
- const prefixes = [prefix, prefix += match[1]]; // This section handles adjacent optional params. We don't actually want all permuations since
266
+ const prefixes = [prefix, prefix += match[1]];
267
+
268
+ // This section handles adjacent optional params. We don't actually want all permuations since
264
269
  // that will lead to equivalent routes which have the same number of params. For example
265
270
  // `/:a?/:b?/:c`? only has the unique expansion: `/`, `/:a`, `/:a/:b`, `/:a/:b/:c` and we can
266
271
  // discard `/:b`, `/:c`, `/:b/:c` by building them up in order and not recursing. This also helps
267
272
  // ensure predictability where earlier params have precidence.
268
-
269
273
  while (match = /^(\/\:[^\/]+)\?/.exec(suffix)) {
270
274
  prefixes.push(prefix += match[1]);
271
275
  suffix = suffix.slice(match[0].length);
272
276
  }
273
-
274
277
  return expandOptionals(suffix).reduce((results, expansion) => [...results, ...prefixes.map(p => p + expansion)], []);
275
278
  }
276
279
 
@@ -304,18 +307,24 @@ const useRouteData = () => useRoute().data;
304
307
  const useSearchParams = () => {
305
308
  const location = useLocation();
306
309
  const navigate = useNavigate();
307
-
308
310
  const setSearchParams = (params, options) => {
309
311
  const searchString = untrack(() => mergeSearchString(location.search, params));
310
- navigate(searchString, {
312
+ navigate(location.pathname + searchString + location.hash, {
311
313
  scroll: false,
312
- ...options,
313
- resolve: true
314
+ resolve: false,
315
+ ...options
314
316
  });
315
317
  };
316
-
317
318
  return [location.query, setSearchParams];
318
319
  };
320
+ const useBeforeLeave = listener => {
321
+ const s = useRouter().beforeLeave.subscribe({
322
+ listener,
323
+ location: useLocation(),
324
+ navigate: useNavigate()
325
+ });
326
+ onCleanup(s);
327
+ };
319
328
  function createRoutes(routeDef, base = "", fallback) {
320
329
  const {
321
330
  component,
@@ -338,13 +347,13 @@ function createRoutes(routeDef, base = "", fallback) {
338
347
  for (const originalPath of expandOptionals(path)) {
339
348
  const path = joinPaths(base, originalPath);
340
349
  const pattern = isLeaf ? path : path.split("/*", 1)[0];
341
- acc.push({ ...shared,
350
+ acc.push({
351
+ ...shared,
342
352
  originalPath,
343
353
  pattern,
344
354
  matcher: createMatcher(pattern, !isLeaf)
345
355
  });
346
356
  }
347
-
348
357
  return acc;
349
358
  }, []);
350
359
  }
@@ -352,76 +361,62 @@ function createBranch(routes, index = 0) {
352
361
  return {
353
362
  routes,
354
363
  score: scoreRoute(routes[routes.length - 1]) * 10000 - index,
355
-
356
364
  matcher(location) {
357
365
  const matches = [];
358
-
359
366
  for (let i = routes.length - 1; i >= 0; i--) {
360
367
  const route = routes[i];
361
368
  const match = route.matcher(location);
362
-
363
369
  if (!match) {
364
370
  return null;
365
371
  }
366
-
367
- matches.unshift({ ...match,
372
+ matches.unshift({
373
+ ...match,
368
374
  route
369
375
  });
370
376
  }
371
-
372
377
  return matches;
373
378
  }
374
-
375
379
  };
376
380
  }
377
-
378
381
  function asArray(value) {
379
382
  return Array.isArray(value) ? value : [value];
380
383
  }
381
-
382
384
  function createBranches(routeDef, base = "", fallback, stack = [], branches = []) {
383
385
  const routeDefs = asArray(routeDef);
384
-
385
386
  for (let i = 0, len = routeDefs.length; i < len; i++) {
386
387
  const def = routeDefs[i];
387
-
388
388
  if (def && typeof def === "object" && def.hasOwnProperty("path")) {
389
389
  const routes = createRoutes(def, base, fallback);
390
-
391
390
  for (const route of routes) {
392
391
  stack.push(route);
393
-
394
- if (def.children) {
392
+ const isEmptyArray = Array.isArray(def.children) && def.children.length === 0;
393
+ if (def.children && !isEmptyArray) {
395
394
  createBranches(def.children, route.pattern, fallback, stack, branches);
396
395
  } else {
397
396
  const branch = createBranch([...stack], branches.length);
398
397
  branches.push(branch);
399
398
  }
400
-
401
399
  stack.pop();
402
400
  }
403
401
  }
404
- } // Stack will be empty on final return
405
-
402
+ }
406
403
 
404
+ // Stack will be empty on final return
407
405
  return stack.length ? branches : branches.sort((a, b) => b.score - a.score);
408
406
  }
409
407
  function getRouteMatches(branches, location) {
410
408
  for (let i = 0, len = branches.length; i < len; i++) {
411
409
  const match = branches[i].matcher(location);
412
-
413
410
  if (match) {
414
411
  return match;
415
412
  }
416
413
  }
417
-
418
414
  return [];
419
415
  }
420
416
  function createLocation(path, state) {
421
417
  const origin = new URL("http://sar");
422
418
  const url = createMemo(prev => {
423
419
  const path_ = path();
424
-
425
420
  try {
426
421
  return new URL(path_, origin);
427
422
  } catch (err) {
@@ -439,23 +434,18 @@ function createLocation(path, state) {
439
434
  get pathname() {
440
435
  return pathname();
441
436
  },
442
-
443
437
  get search() {
444
438
  return search();
445
439
  },
446
-
447
440
  get hash() {
448
441
  return hash();
449
442
  },
450
-
451
443
  get state() {
452
444
  return state();
453
445
  },
454
-
455
446
  get key() {
456
447
  return key();
457
448
  },
458
-
459
449
  query: createMemoObject(on(search, () => extractSearchParams(url())))
460
450
  };
461
451
  }
@@ -464,17 +454,14 @@ function createRouterContext(integration, base = "", data, out) {
464
454
  signal: [source, setSource],
465
455
  utils = {}
466
456
  } = normalizeIntegration(integration);
467
-
468
457
  const parsePath = utils.parsePath || (p => p);
469
-
470
458
  const renderPath = utils.renderPath || (p => p);
471
-
459
+ const beforeLeave = utils.beforeLeave || createBeforeLeave();
472
460
  const basePath = resolvePath("", base);
473
461
  const output = isServer && out ? Object.assign(out, {
474
462
  matches: [],
475
463
  url: undefined
476
464
  }) : undefined;
477
-
478
465
  if (basePath === undefined) {
479
466
  throw new Error(`${basePath} is not a valid base path`);
480
467
  } else if (basePath && !source().value) {
@@ -484,8 +471,15 @@ function createRouterContext(integration, base = "", data, out) {
484
471
  scroll: false
485
472
  });
486
473
  }
487
-
488
- const [isRouting, start] = useTransition();
474
+ const [isRouting, setIsRouting] = createSignal(false);
475
+ const start = async callback => {
476
+ setIsRouting(true);
477
+ try {
478
+ await startTransition(callback);
479
+ } finally {
480
+ setIsRouting(false);
481
+ }
482
+ };
489
483
  const [reference, setReference] = createSignal(source().value);
490
484
  const [state, setState] = createSignal(source().state);
491
485
  const location = createLocation(reference, state);
@@ -495,13 +489,10 @@ function createRouterContext(integration, base = "", data, out) {
495
489
  params: {},
496
490
  path: () => basePath,
497
491
  outlet: () => null,
498
-
499
492
  resolvePath(to) {
500
493
  return resolvePath(basePath, to);
501
494
  }
502
-
503
495
  };
504
-
505
496
  if (data) {
506
497
  try {
507
498
  TempRoute = baseRoute;
@@ -515,20 +506,17 @@ function createRouterContext(integration, base = "", data, out) {
515
506
  TempRoute = undefined;
516
507
  }
517
508
  }
518
-
519
509
  function navigateFromRoute(route, to, options) {
520
510
  // Untrack in case someone navigates in an effect - don't want to track `reference` or route paths
521
511
  untrack(() => {
522
512
  if (typeof to === "number") {
523
513
  if (!to) ; else if (utils.go) {
524
- utils.go(to);
514
+ beforeLeave.confirm(to, options) && utils.go(to);
525
515
  } else {
526
516
  console.warn("Router integration does not support relative routing");
527
517
  }
528
-
529
518
  return;
530
519
  }
531
-
532
520
  const {
533
521
  replace,
534
522
  resolve,
@@ -541,28 +529,24 @@ function createRouterContext(integration, base = "", data, out) {
541
529
  ...options
542
530
  };
543
531
  const resolvedTo = resolve ? route.resolvePath(to) : resolvePath("", to);
544
-
545
532
  if (resolvedTo === undefined) {
546
533
  throw new Error(`Path '${to}' is not a routable path`);
547
534
  } else if (referrers.length >= MAX_REDIRECTS) {
548
535
  throw new Error("Too many redirects");
549
536
  }
550
-
551
537
  const current = reference();
552
-
553
538
  if (resolvedTo !== current || nextState !== state()) {
554
539
  if (isServer) {
555
540
  if (output) {
556
541
  output.url = resolvedTo;
557
542
  }
558
-
559
543
  setSource({
560
544
  value: resolvedTo,
561
545
  replace,
562
546
  scroll,
563
547
  state: nextState
564
548
  });
565
- } else {
549
+ } else if (beforeLeave.confirm(resolvedTo, options)) {
566
550
  const len = referrers.push({
567
551
  value: current,
568
552
  replace,
@@ -585,34 +569,30 @@ function createRouterContext(integration, base = "", data, out) {
585
569
  }
586
570
  });
587
571
  }
588
-
589
572
  function navigatorFactory(route) {
590
573
  // Workaround for vite issue (https://github.com/vitejs/vite/issues/3803)
591
574
  route = route || useContext(RouteContextObj) || baseRoute;
592
575
  return (to, options) => navigateFromRoute(route, to, options);
593
576
  }
594
-
595
577
  function navigateEnd(next) {
596
578
  const first = referrers[0];
597
-
598
579
  if (first) {
599
580
  if (next.value !== first.value || next.state !== first.state) {
600
- setSource({ ...next,
581
+ setSource({
582
+ ...next,
601
583
  replace: first.replace,
602
584
  scroll: first.scroll
603
585
  });
604
586
  }
605
-
606
587
  referrers.length = 0;
607
588
  }
608
589
  }
609
-
610
590
  createRenderEffect(() => {
611
591
  const {
612
592
  value,
613
593
  state
614
- } = source(); // Untrack this whole block so `start` doesn't cause Solid's Listener to be preserved
615
-
594
+ } = source();
595
+ // Untrack this whole block so `start` doesn't cause Solid's Listener to be preserved
616
596
  untrack(() => {
617
597
  if (value !== reference()) {
618
598
  start(() => {
@@ -622,26 +602,19 @@ function createRouterContext(integration, base = "", data, out) {
622
602
  }
623
603
  });
624
604
  });
625
-
626
605
  if (!isServer) {
627
- function isSvg(el) {
628
- return el.namespaceURI === "http://www.w3.org/2000/svg";
629
- }
630
-
631
606
  function handleAnchorClick(evt) {
632
607
  if (evt.defaultPrevented || evt.button !== 0 || evt.metaKey || evt.altKey || evt.ctrlKey || evt.shiftKey) return;
633
608
  const a = evt.composedPath().find(el => el instanceof Node && el.nodeName.toUpperCase() === "A");
634
- if (!a) return;
635
- const svg = isSvg(a);
636
- const href = svg ? a.href.baseVal : a.href;
637
- const target = svg ? a.target.baseVal : a.target;
638
- if (target || !href && !a.hasAttribute("state")) return;
609
+ if (!a || !a.hasAttribute("link")) return;
610
+ const href = a.href;
611
+ if (a.target || !href && !a.hasAttribute("state")) return;
639
612
  const rel = (a.getAttribute("rel") || "").split(/\s+/);
640
613
  if (a.hasAttribute("download") || rel && rel.includes("external")) return;
641
- const url = svg ? new URL(href, document.baseURI) : new URL(href);
614
+ const url = new URL(href);
642
615
  const pathname = urlDecode(url.pathname);
643
616
  if (url.origin !== window.location.origin || basePath && pathname && !pathname.toLowerCase().startsWith(basePath.toLowerCase())) return;
644
- const to = parsePath(pathname + urlDecode(url.search, true) + urlDecode(url.hash));
617
+ const to = parsePath(url.pathname + url.search + url.hash);
645
618
  const state = a.getAttribute("state");
646
619
  evt.preventDefault();
647
620
  navigateFromRoute(baseRoute, to, {
@@ -650,14 +623,13 @@ function createRouterContext(integration, base = "", data, out) {
650
623
  scroll: !a.hasAttribute("noscroll"),
651
624
  state: state && JSON.parse(state)
652
625
  });
653
- } // ensure delegated events run first
654
-
626
+ }
655
627
 
628
+ // ensure delegated events run first
656
629
  delegateEvents(["click"]);
657
630
  document.addEventListener("click", handleAnchorClick);
658
631
  onCleanup(() => document.removeEventListener("click", handleAnchorClick));
659
632
  }
660
-
661
633
  return {
662
634
  base: baseRoute,
663
635
  out: output,
@@ -665,7 +637,8 @@ function createRouterContext(integration, base = "", data, out) {
665
637
  isRouting,
666
638
  renderPath,
667
639
  parsePath,
668
- navigatorFactory
640
+ navigatorFactory,
641
+ beforeLeave
669
642
  };
670
643
  }
671
644
  function createRouteContext(router, parent, child, match) {
@@ -686,22 +659,17 @@ function createRouteContext(router, parent, child, match) {
686
659
  const route = {
687
660
  parent,
688
661
  pattern,
689
-
690
662
  get child() {
691
663
  return child();
692
664
  },
693
-
694
665
  path,
695
666
  params,
696
667
  data: parent.data,
697
668
  outlet,
698
-
699
669
  resolvePath(to) {
700
670
  return resolvePath(base.path(), to, path());
701
671
  }
702
-
703
672
  };
704
-
705
673
  if (data) {
706
674
  try {
707
675
  TempRoute = route;
@@ -715,11 +683,10 @@ function createRouteContext(router, parent, child, match) {
715
683
  TempRoute = undefined;
716
684
  }
717
685
  }
718
-
719
686
  return route;
720
687
  }
721
688
 
722
- const _tmpl$ = /*#__PURE__*/template(`<a></a>`, 2);
689
+ const _tmpl$ = /*#__PURE__*/template(`<a link></a>`, 2);
723
690
  const Router = props => {
724
691
  const {
725
692
  source,
@@ -734,11 +701,9 @@ const Router = props => {
734
701
  const routerState = createRouterContext(integration, base, data, out);
735
702
  return createComponent$1(RouterContextObj.Provider, {
736
703
  value: routerState,
737
-
738
704
  get children() {
739
705
  return props.children;
740
706
  }
741
-
742
707
  });
743
708
  };
744
709
  const Routes = props => {
@@ -747,7 +712,6 @@ const Routes = props => {
747
712
  const routeDefs = children(() => props.children);
748
713
  const branches = createMemo(() => createBranches(routeDefs(), joinPaths(parentRoute.pattern, props.base || ""), Outlet));
749
714
  const matches = createMemo(() => getRouteMatches(branches(), router.location.pathname));
750
-
751
715
  if (router.out) {
752
716
  router.out.matches.push(matches().map(({
753
717
  route,
@@ -760,39 +724,31 @@ const Routes = props => {
760
724
  params
761
725
  })));
762
726
  }
763
-
764
727
  const disposers = [];
765
728
  let root;
766
729
  const routeStates = createMemo(on(matches, (nextMatches, prevMatches, prev) => {
767
730
  let equal = prevMatches && nextMatches.length === prevMatches.length;
768
731
  const next = [];
769
-
770
732
  for (let i = 0, len = nextMatches.length; i < len; i++) {
771
733
  const prevMatch = prevMatches && prevMatches[i];
772
734
  const nextMatch = nextMatches[i];
773
-
774
735
  if (prev && prevMatch && nextMatch.route.key === prevMatch.route.key) {
775
736
  next[i] = prev[i];
776
737
  } else {
777
738
  equal = false;
778
-
779
739
  if (disposers[i]) {
780
740
  disposers[i]();
781
741
  }
782
-
783
742
  createRoot(dispose => {
784
743
  disposers[i] = dispose;
785
744
  next[i] = createRouteContext(router, next[i - 1] || parentRoute, () => routeStates()[i + 1], () => matches()[i]);
786
745
  });
787
746
  }
788
747
  }
789
-
790
748
  disposers.splice(nextMatches.length).forEach(dispose => dispose());
791
-
792
749
  if (prev && equal) {
793
750
  return prev;
794
751
  }
795
-
796
752
  root = next[0];
797
753
  return next;
798
754
  }));
@@ -800,14 +756,11 @@ const Routes = props => {
800
756
  get when() {
801
757
  return routeStates() && root;
802
758
  },
803
-
804
759
  children: route => createComponent$1(RouteContextObj.Provider, {
805
760
  value: route,
806
-
807
761
  get children() {
808
762
  return route.outlet();
809
763
  }
810
-
811
764
  })
812
765
  });
813
766
  };
@@ -823,7 +776,6 @@ const Route = props => {
823
776
  get children() {
824
777
  return childRoutes();
825
778
  }
826
-
827
779
  });
828
780
  };
829
781
  const Outlet = () => {
@@ -832,90 +784,55 @@ const Outlet = () => {
832
784
  get when() {
833
785
  return route.child;
834
786
  },
835
-
836
787
  children: child => createComponent$1(RouteContextObj.Provider, {
837
788
  value: child,
838
-
839
789
  get children() {
840
790
  return child.outlet();
841
791
  }
842
-
843
792
  })
844
793
  });
845
794
  };
846
-
847
- function LinkBase(props) {
848
- const [, rest] = splitProps(props, ["children", "to", "href", "state"]);
849
- const href = useHref(() => props.to);
850
- return (() => {
851
- const _el$ = _tmpl$.cloneNode(true);
852
-
853
- spread(_el$, rest, false, true);
854
-
855
- insert(_el$, () => props.children);
856
-
857
- effect(_p$ => {
858
- const _v$ = href() || props.href,
859
- _v$2 = JSON.stringify(props.state);
860
-
861
- _v$ !== _p$._v$ && setAttribute(_el$, "href", _p$._v$ = _v$);
862
- _v$2 !== _p$._v$2 && setAttribute(_el$, "state", _p$._v$2 = _v$2);
863
- return _p$;
864
- }, {
865
- _v$: undefined,
866
- _v$2: undefined
867
- });
868
-
869
- return _el$;
870
- })();
871
- }
872
-
873
- function Link(props) {
874
- const to = useResolvedPath(() => props.href);
875
- return createComponent$1(LinkBase, mergeProps$1(props, {
876
- get to() {
877
- return to();
878
- }
879
-
880
- }));
881
- }
882
- function NavLink(props) {
795
+ function A(props) {
883
796
  props = mergeProps({
884
797
  inactiveClass: "inactive",
885
798
  activeClass: "active"
886
799
  }, props);
887
- const [, rest] = splitProps(props, ["activeClass", "inactiveClass", "end"]);
888
- const location = useLocation();
800
+ const [, rest] = splitProps(props, ["href", "state", "class", "activeClass", "inactiveClass", "end"]);
889
801
  const to = useResolvedPath(() => props.href);
802
+ const href = useHref(to);
803
+ const location = useLocation();
890
804
  const isActive = createMemo(() => {
891
805
  const to_ = to();
892
-
893
- if (to_ === undefined) {
894
- return false;
895
- }
896
-
897
- const path = to_.split(/[?#]/, 1)[0].toLowerCase();
898
- const loc = location.pathname.toLowerCase();
806
+ if (to_ === undefined) return false;
807
+ const path = normalizePath(to_.split(/[?#]/, 1)[0]).toLowerCase();
808
+ const loc = normalizePath(location.pathname).toLowerCase();
899
809
  return props.end ? path === loc : loc.startsWith(path);
900
810
  });
901
- return createComponent$1(LinkBase, mergeProps$1(rest, {
902
- get to() {
903
- return to();
904
- },
905
-
906
- get classList() {
907
- return {
908
- [props.inactiveClass]: !isActive(),
909
- [props.activeClass]: isActive(),
910
- ...rest.classList
911
- };
912
- },
913
-
914
- get ["aria-current"]() {
915
- return isActive() ? "page" : undefined;
916
- }
917
-
918
- }));
811
+ return (() => {
812
+ const _el$ = _tmpl$.cloneNode(true);
813
+ spread(_el$, mergeProps$1(rest, {
814
+ get href() {
815
+ return href() || props.href;
816
+ },
817
+ get state() {
818
+ return JSON.stringify(props.state);
819
+ },
820
+ get classList() {
821
+ return {
822
+ ...(props.class && {
823
+ [props.class]: true
824
+ }),
825
+ [props.inactiveClass]: !isActive(),
826
+ [props.activeClass]: isActive(),
827
+ ...rest.classList
828
+ };
829
+ },
830
+ get ["aria-current"]() {
831
+ return isActive() ? "page" : undefined;
832
+ }
833
+ }), false, false);
834
+ return _el$;
835
+ })();
919
836
  }
920
837
  function Navigate(props) {
921
838
  const navigate = useNavigate();
@@ -935,4 +852,4 @@ function Navigate(props) {
935
852
  return null;
936
853
  }
937
854
 
938
- export { Link, NavLink, Navigate, Outlet, Route, Router, Routes, mergeSearchString as _mergeSearchString, createIntegration, hashIntegration, normalizeIntegration, pathIntegration, staticIntegration, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useRouteData, useRoutes, useSearchParams };
855
+ export { A, A as Link, A as NavLink, Navigate, Outlet, Route, Router, Routes, mergeSearchString as _mergeSearchString, createBeforeLeave, createIntegration, hashIntegration, normalizeIntegration, pathIntegration, staticIntegration, useBeforeLeave, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useRouteData, useRoutes, useSearchParams };