@qwik.dev/router 2.0.0-beta.35 → 2.0.0-beta.37

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.
@@ -1,381 +1,6 @@
1
- import { createAsyncQrl, inlinedQrl, _captures, isBrowser, withLocale } from '@qwik.dev/core';
2
- import { _deserialize, _UNINITIALIZED } from '@qwik.dev/core/internal';
3
- import { p } from '@qwik.dev/core/preloader';
4
- import { d as deepFreeze } from './deepFreeze.qwik.mjs';
5
-
6
- const CLIENT_DATA_CACHE = /* @__PURE__ */ new Map();
7
- const QACTION_KEY = "qaction";
8
- const QLOADER_KEY = "qloaders";
9
- const QFN_KEY = "qfunc";
10
- const QDATA_KEY = "qdata";
11
- const Q_ROUTE = "q:route";
12
- const DEFAULT_LOADERS_SERIALIZATION_STRATEGY = globalThis.__DEFAULT_LOADERS_SERIALIZATION_STRATEGY__ || "never";
13
-
14
- const MAX_Q_DATA_RETRY_COUNT = 3;
15
- const getFetchOptions = (action, noCache) => {
16
- const actionData = action?.data;
17
- if (!actionData) {
18
- if (noCache) {
19
- return {
20
- cache: "no-cache",
21
- headers: {
22
- "Cache-Control": "no-cache",
23
- Pragma: "no-cache"
24
- }
25
- };
26
- }
27
- return void 0;
28
- }
29
- if (actionData instanceof FormData) {
30
- return {
31
- method: "POST",
32
- body: actionData
33
- };
34
- } else {
35
- return {
36
- method: "POST",
37
- body: JSON.stringify(actionData),
38
- headers: {
39
- "Content-Type": "application/json; charset=UTF-8"
40
- }
41
- };
42
- }
43
- };
44
- const loadClientData = async (url, opts, retryCount = 0) => {
45
- const pagePathname = url.pathname;
46
- const pageSearch = url.search;
47
- const clientDataPath = getClientDataPath(pagePathname, pageSearch, {
48
- actionId: opts?.action?.id,
49
- loaderIds: opts?.loaderIds
50
- });
51
- let qData;
52
- if (!opts?.action) {
53
- qData = CLIENT_DATA_CACHE.get(clientDataPath);
54
- }
55
- if (opts?.preloadRouteBundles !== false) {
56
- preloadRouteBundles(pagePathname, 0.8);
57
- }
58
- let resolveFn;
59
- if (!qData) {
60
- const fetchOptions = getFetchOptions(opts?.action, opts?.clearCache);
61
- if (opts?.action) {
62
- opts.action.data = void 0;
63
- }
64
- qData = fetch(clientDataPath, fetchOptions).then((rsp) => {
65
- if (rsp.status === 404 && opts?.loaderIds && retryCount < MAX_Q_DATA_RETRY_COUNT) {
66
- opts.loaderIds = void 0;
67
- return loadClientData(url, opts, retryCount + 1);
68
- }
69
- if (rsp.redirected) {
70
- const redirectedURL = new URL(rsp.url);
71
- const isQData = redirectedURL.pathname.endsWith("/q-data.json");
72
- if (!isQData || redirectedURL.origin !== location.origin) {
73
- if (!opts?.isPrefetch) {
74
- location.href = redirectedURL.href;
75
- }
76
- return;
77
- }
78
- }
79
- if ((rsp.headers.get("content-type") || "").includes("json")) {
80
- return rsp.text().then((text) => {
81
- const clientData = _deserialize(text);
82
- if (!clientData) {
83
- location.href = url.href;
84
- return;
85
- }
86
- if (opts?.clearCache) {
87
- CLIENT_DATA_CACHE.delete(clientDataPath);
88
- }
89
- if (clientData.redirect) {
90
- location.href = clientData.redirect;
91
- } else if (opts?.action) {
92
- const { action } = opts;
93
- const actionData = clientData.loaders[action.id];
94
- resolveFn = () => {
95
- action.resolve({
96
- status: rsp.status,
97
- result: actionData
98
- });
99
- };
100
- }
101
- return clientData;
102
- });
103
- } else {
104
- if (opts?.isPrefetch !== true) {
105
- location.href = url.href;
106
- }
107
- return void 0;
108
- }
109
- });
110
- if (!opts?.action) {
111
- CLIENT_DATA_CACHE.set(clientDataPath, qData);
112
- }
113
- }
114
- return qData.then((v) => {
115
- if (!v) {
116
- CLIENT_DATA_CACHE.delete(clientDataPath);
117
- }
118
- resolveFn && resolveFn();
119
- return v;
120
- });
121
- };
122
-
123
- const toPath = (url) => url.pathname + url.search + url.hash;
124
- const toUrl = (url, baseUrl) => new URL(url, baseUrl.href);
125
- const isSameOrigin = (a, b) => a.origin === b.origin;
126
- const withSlash = (path) => path.endsWith("/") ? path : path + "/";
127
- const isSamePathname = ({ pathname: a }, { pathname: b }) => {
128
- const lDiff = Math.abs(a.length - b.length);
129
- return lDiff === 0 ? a === b : lDiff === 1 && withSlash(a) === withSlash(b);
130
- };
131
- const isSameSearchQuery = (a, b) => a.search === b.search;
132
- const isSamePath = (a, b) => isSameSearchQuery(a, b) && isSamePathname(a, b);
133
- const getClientDataPath = (pathname, pageSearch, options) => {
134
- let search = pageSearch ?? "";
135
- if (options?.actionId) {
136
- search += (search ? "&" : "?") + QACTION_KEY + "=" + encodeURIComponent(options.actionId);
137
- }
138
- if (options?.loaderIds) {
139
- for (const loaderId of options.loaderIds) {
140
- search += (search ? "&" : "?") + QLOADER_KEY + "=" + encodeURIComponent(loaderId);
141
- }
142
- }
143
- return pathname + (pathname.endsWith("/") ? "" : "/") + "q-data.json" + search;
144
- };
145
- const getClientNavPath = (props, baseUrl) => {
146
- const href = props.href;
147
- if (typeof href === "string" && typeof props.target !== "string" && !props.reload) {
148
- try {
149
- const linkUrl = toUrl(href.trim(), baseUrl.url);
150
- const currentUrl = toUrl("", baseUrl.url);
151
- if (isSameOrigin(linkUrl, currentUrl)) {
152
- return toPath(linkUrl);
153
- }
154
- } catch (e) {
155
- console.error(e);
156
- }
157
- } else if (props.reload) {
158
- return toPath(toUrl("", baseUrl.url));
159
- }
160
- return null;
161
- };
162
- const shouldPreload = (clientNavPath, currentLoc) => {
163
- if (clientNavPath) {
164
- const prefetchUrl = toUrl(clientNavPath, currentLoc.url);
165
- const currentUrl = toUrl("", currentLoc.url);
166
- return !isSamePathname(prefetchUrl, currentUrl);
167
- }
168
- return false;
169
- };
170
- const isPromise = (value) => {
171
- return value && typeof value.then === "function";
172
- };
173
- const createLoaderSignal = (loadersObject, loaderId, url, serializationStrategy, container) => {
174
- return createAsyncQrl(/* @__PURE__ */ inlinedQrl(async () => {
175
- const loaderId2 = _captures[0], loadersObject2 = _captures[1], url2 = _captures[2];
176
- if (isBrowser && loadersObject2[loaderId2] === _UNINITIALIZED) {
177
- const data = await loadClientData(url2, {
178
- loaderIds: [
179
- loaderId2
180
- ]
181
- });
182
- loadersObject2[loaderId2] = data?.loaders[loaderId2] ?? _UNINITIALIZED;
183
- }
184
- return loadersObject2[loaderId2];
185
- }, "createLoaderSignal_createAsync_CFLMoh8rnzw", [
186
- loaderId,
187
- loadersObject,
188
- url
189
- ]), {
190
- container,
191
- serializationStrategy
192
- });
193
- };
194
-
195
- const newScrollState = () => {
196
- return {
197
- x: 0,
198
- y: 0,
199
- w: 0,
200
- h: 0
201
- };
202
- };
203
- const clientNavigate = (win, navType, fromURL, toURL, replaceState = false) => {
204
- if (navType !== "popstate") {
205
- const samePath = isSamePath(fromURL, toURL);
206
- const sameHash = fromURL.hash === toURL.hash;
207
- if (!samePath || !sameHash) {
208
- const newState = {
209
- _qRouterScroll: newScrollState()
210
- };
211
- if (replaceState) {
212
- win.history.replaceState(newState, "", toPath(toURL));
213
- } else {
214
- win.history.pushState(newState, "", toPath(toURL));
215
- }
216
- }
217
- }
218
- };
219
- const preloadRouteBundles = (path, probability = 0.8) => {
220
- if (isBrowser) {
221
- path = path.endsWith("/") ? path : path + "/";
222
- path = path.length > 1 && path.startsWith("/") ? path.slice(1) : path;
223
- p(path, probability);
224
- }
225
- };
226
-
227
- const mergeArray = (existingArr, newArr) => {
228
- if (Array.isArray(newArr)) {
229
- for (const newItem of newArr) {
230
- if (typeof newItem.key === "string") {
231
- const existingIndex = existingArr.findIndex((i) => i.key === newItem.key);
232
- if (existingIndex > -1) {
233
- existingArr[existingIndex] = newItem;
234
- continue;
235
- }
236
- }
237
- existingArr.push(newItem);
238
- }
239
- }
240
- };
241
- const resolveDocumentHead = (resolvedHead, updatedHead) => {
242
- if (typeof updatedHead.title === "string") {
243
- resolvedHead.title = updatedHead.title;
244
- }
245
- mergeArray(resolvedHead.meta, updatedHead.meta);
246
- mergeArray(resolvedHead.links, updatedHead.links);
247
- mergeArray(resolvedHead.styles, updatedHead.styles);
248
- mergeArray(resolvedHead.scripts, updatedHead.scripts);
249
- Object.assign(resolvedHead.frontmatter, updatedHead.frontmatter);
250
- };
251
- const createDocumentHead = (defaults, manifestHash) => ({
252
- title: defaults?.title || "",
253
- meta: [
254
- ...defaults?.meta || []
255
- ],
256
- links: [
257
- ...defaults?.links || []
258
- ],
259
- styles: [
260
- ...defaults?.styles || []
261
- ],
262
- scripts: [
263
- ...defaults?.scripts || []
264
- ],
265
- frontmatter: {
266
- ...defaults?.frontmatter
267
- },
268
- manifestHash: manifestHash || "dev"
269
- });
270
- const resolveRouteConfig = (resolveValue, routeLocation, contentModules, locale, status, defaults) => withLocale(locale, () => {
271
- const head = createDocumentHead(defaults);
272
- let eTag;
273
- let cacheKey;
274
- const fns = [];
275
- for (let i = 0; i < contentModules.length; i++) {
276
- const contentModule = contentModules[i];
277
- if (!contentModule) {
278
- continue;
279
- }
280
- const isLast = i === contentModules.length - 1;
281
- let config;
282
- if (contentModule.routeConfig) {
283
- config = contentModule.routeConfig;
284
- } else if (contentModule.head) {
285
- const synthetic = {
286
- head: void 0
287
- };
288
- const headExport = contentModule.head;
289
- if (typeof headExport === "function") {
290
- const mod = contentModule;
291
- config = (props) => ({
292
- head: headExport(props),
293
- eTag: mod.eTag,
294
- cacheKey: mod.cacheKey
295
- });
296
- } else {
297
- synthetic.head = headExport;
298
- const mod = contentModule;
299
- if (mod.eTag !== void 0) {
300
- synthetic.eTag = mod.eTag;
301
- }
302
- if (mod.cacheKey !== void 0) {
303
- synthetic.cacheKey = mod.cacheKey;
304
- }
305
- config = synthetic;
306
- }
307
- } else {
308
- const mod = contentModule;
309
- if (mod.eTag !== void 0 || mod.cacheKey !== void 0) {
310
- config = {
311
- eTag: mod.eTag,
312
- cacheKey: mod.cacheKey
313
- };
314
- }
315
- }
316
- if (!config) {
317
- continue;
318
- }
319
- if (typeof config === "function") {
320
- fns.unshift({
321
- fn: config,
322
- isLast
323
- });
324
- } else {
325
- if (config.head) {
326
- resolveDocumentHead(head, config.head);
327
- }
328
- if (config.eTag !== void 0) {
329
- eTag = config.eTag;
330
- }
331
- if (config.cacheKey !== void 0) {
332
- cacheKey = config.cacheKey;
333
- }
334
- }
335
- }
336
- if (fns.length) {
337
- const headProps = {
338
- head,
339
- status,
340
- withLocale: (fn) => fn(),
341
- resolveValue,
342
- ...routeLocation
343
- };
344
- for (const { fn } of fns) {
345
- const result = fn(headProps);
346
- if (result.head) {
347
- resolveDocumentHead(head, result.head);
348
- }
349
- if (result.eTag !== void 0) {
350
- eTag = result.eTag;
351
- }
352
- if (result.cacheKey !== void 0) {
353
- cacheKey = result.cacheKey;
354
- }
355
- }
356
- }
357
- return {
358
- head,
359
- eTag,
360
- cacheKey
361
- };
362
- });
363
- const resolveHead = (endpoint, routeLocation, contentModules, locale, defaults) => {
364
- const getData = (loaderOrAction) => {
365
- const id = loaderOrAction.__id;
366
- if (loaderOrAction.__brand === "server_loader") {
367
- if (!(id in endpoint.loaders)) {
368
- throw new Error("You can not get the returned data of a loader that has not been executed for this request.");
369
- }
370
- }
371
- const data = endpoint.loaders[id];
372
- if (isPromise(data)) {
373
- throw new Error("Loaders returning a promise can not be resolved for the head function.");
374
- }
375
- return data;
376
- };
377
- return resolveRouteConfig(getData, routeLocation, contentModules, locale, endpoint.status, defaults).head;
378
- };
1
+ import { e as ensureSlash } from './pathname.mjs';
2
+ import { v as deepFreeze } from './deepFreeze.qwik.mjs';
3
+ import { withLocale } from '@qwik.dev/core';
379
4
 
380
5
  const MODULE_CACHE = /* @__PURE__ */ new WeakMap();
381
6
  const httpErrorLoader = () => import('./http-error.qwik.mjs');
@@ -385,10 +10,12 @@ function walkTrieKeys(root, keys) {
385
10
  if (node._L) {
386
11
  layouts.push(node._L);
387
12
  }
388
- for (const key of keys) {
13
+ for (let i = 0; i < keys.length; i++) {
14
+ const key = keys[i];
389
15
  let next = node[key];
390
16
  if (!next && node._M) {
391
- for (const group of node._M) {
17
+ for (let j = 0; j < node._M.length; j++) {
18
+ const group = node._M[j];
392
19
  next = group[key];
393
20
  if (next) {
394
21
  if (group._L) {
@@ -411,11 +38,21 @@ function walkTrieKeys(root, keys) {
411
38
  layouts
412
39
  };
413
40
  }
414
- function collectNodeMeta(node, groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef) {
415
- for (const g of groups) {
41
+ function collectNodeMeta(node, groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash, matchedPathname = "/") {
42
+ for (let j = 0; j < groups.length; j++) {
43
+ const g = groups[j];
416
44
  if (g._L) {
417
45
  layouts.push(g._L);
418
46
  }
47
+ if (g._R && loaderHashes) {
48
+ loaderHashes.push(...g._R);
49
+ if (loaderPathsByHash) {
50
+ for (let i = 0; i < g._R.length; i++) {
51
+ const hash = g._R[i];
52
+ loaderPathsByHash[hash] = matchedPathname;
53
+ }
54
+ }
55
+ }
419
56
  if (g._E) {
420
57
  errorLoaderRef.v = g._E;
421
58
  }
@@ -429,6 +66,15 @@ function collectNodeMeta(node, groups, layouts, errorLoaderRef, notFoundLoaderRe
429
66
  if (node._L) {
430
67
  layouts.push(node._L);
431
68
  }
69
+ if (node._R && loaderHashes) {
70
+ loaderHashes.push(...node._R);
71
+ if (loaderPathsByHash) {
72
+ for (let i = 0; i < node._R.length; i++) {
73
+ const hash = node._R[i];
74
+ loaderPathsByHash[hash] = matchedPathname;
75
+ }
76
+ }
77
+ }
432
78
  if (node._E) {
433
79
  errorLoaderRef.v = node._E;
434
80
  }
@@ -439,7 +85,7 @@ function collectNodeMeta(node, groups, layouts, errorLoaderRef, notFoundLoaderRe
439
85
  menuLoaderRef.v = node._N;
440
86
  }
441
87
  }
442
- function tryWildcardMatch(node, part, partLower, parts, partIndex, params) {
88
+ function tryWildcardMatch(node, part, partLower, parts, partIndex) {
443
89
  let next = node._W;
444
90
  if (next) {
445
91
  const prefix = next._0;
@@ -450,20 +96,24 @@ function tryWildcardMatch(node, part, partLower, parts, partIndex, params) {
450
96
  if (partLower.length > pre.length + suf.length && (!pre || partLower.startsWith(pre.toLowerCase())) && (!suf || partLower.endsWith(suf.toLowerCase()))) {
451
97
  const paramName = next._P;
452
98
  const value = part.slice(pre.length, suf ? part.length - suf.length : void 0);
453
- params[paramName] = value;
454
99
  return {
455
100
  next,
456
101
  routePart: `${pre}[${paramName}]${suf}`,
457
- done: false
102
+ done: false,
103
+ kind: 1,
104
+ paramName,
105
+ paramValue: value
458
106
  };
459
107
  }
460
108
  } else {
461
109
  const paramName = next._P;
462
- params[paramName] = part;
463
110
  return {
464
111
  next,
465
112
  routePart: `[${paramName}]`,
466
- done: false
113
+ done: false,
114
+ kind: 1,
115
+ paramName,
116
+ paramValue: part
467
117
  };
468
118
  }
469
119
  }
@@ -471,35 +121,39 @@ function tryWildcardMatch(node, part, partLower, parts, partIndex, params) {
471
121
  if (next) {
472
122
  const paramName = next._P;
473
123
  const restValue = parts.slice(partIndex).join("/");
474
- params[paramName] = restValue;
475
124
  return {
476
125
  next,
477
126
  routePart: `[...${paramName}]`,
478
- done: true
127
+ done: true,
128
+ kind: 2,
129
+ paramName,
130
+ paramValue: restValue
479
131
  };
480
132
  }
481
133
  return void 0;
482
134
  }
483
- function findChild(node, part, partLower, parts, partIndex, params) {
135
+ function findChild(node, part, partLower, parts, partIndex) {
484
136
  const exact = node[partLower];
485
137
  if (exact) {
486
138
  return {
487
139
  next: exact,
488
140
  groups: [],
489
141
  routePart: part,
490
- done: false
142
+ done: false,
143
+ kind: 0
491
144
  };
492
145
  }
493
146
  if (node._M) {
494
- for (const group of node._M) {
495
- const groupResult = findChild(group, part, partLower, parts, partIndex, params);
147
+ for (let j = 0; j < node._M.length; j++) {
148
+ const group = node._M[j];
149
+ const groupResult = findChild(group, part, partLower, parts, partIndex);
496
150
  if (groupResult) {
497
151
  groupResult.groups.unshift(group);
498
152
  return groupResult;
499
153
  }
500
154
  }
501
155
  }
502
- const wildcard = tryWildcardMatch(node, part, partLower, parts, partIndex, params);
156
+ const wildcard = tryWildcardMatch(node, part, partLower, parts, partIndex);
503
157
  if (wildcard) {
504
158
  return {
505
159
  ...wildcard,
@@ -516,7 +170,8 @@ function findIndexNode(node) {
516
170
  };
517
171
  }
518
172
  if (node._M) {
519
- for (const group of node._M) {
173
+ for (let j = 0; j < node._M.length; j++) {
174
+ const group = node._M[j];
520
175
  const result = findIndexNode(group);
521
176
  if (result) {
522
177
  if (result.target !== group) {
@@ -540,7 +195,8 @@ function resolveLoaders(root, node, gatheredLayouts) {
540
195
  if (!targetNode._I && targetNode._G == null) {
541
196
  const indexResult = findIndexNode(targetNode);
542
197
  if (indexResult) {
543
- for (const g of indexResult.groups) {
198
+ for (let j = 0; j < indexResult.groups.length; j++) {
199
+ const g = indexResult.groups[j];
544
200
  if (g._L) {
545
201
  targetLayouts.push(g._L);
546
202
  }
@@ -573,7 +229,8 @@ function findRestNode(node) {
573
229
  };
574
230
  }
575
231
  if (node._M) {
576
- for (const group of node._M) {
232
+ for (let j = 0; j < node._M.length; j++) {
233
+ const group = node._M[j];
577
234
  const result = findRestNode(group);
578
235
  if (result) {
579
236
  result.groups.unshift(group);
@@ -588,6 +245,8 @@ function matchRouteTree(root = {}, pathname) {
588
245
  const params = {};
589
246
  const routeParts = [];
590
247
  const layouts = [];
248
+ const loaderHashes = [];
249
+ const loaderPathsByHash = {};
591
250
  const errorLoaderRef = {
592
251
  v: void 0
593
252
  };
@@ -600,6 +259,13 @@ function matchRouteTree(root = {}, pathname) {
600
259
  if (root._L) {
601
260
  layouts.push(root._L);
602
261
  }
262
+ if (root._R) {
263
+ loaderHashes.push(...root._R);
264
+ for (let i2 = 0; i2 < root._R.length; i2++) {
265
+ const hash = root._R[i2];
266
+ loaderPathsByHash[hash] = "/";
267
+ }
268
+ }
603
269
  if (root._E) {
604
270
  errorLoaderRef.v = root._E;
605
271
  }
@@ -618,7 +284,12 @@ function matchRouteTree(root = {}, pathname) {
618
284
  for (; !done && i < len; i++) {
619
285
  const part = parts[i];
620
286
  const partLower = part.toLowerCase();
621
- const restInfo = findRestNode(node);
287
+ const found = findChild(node, part, partLower, parts, i);
288
+ if (!found) {
289
+ matched = false;
290
+ break;
291
+ }
292
+ const restInfo = found.kind === 1 ? findRestNode(node) : void 0;
622
293
  if (restInfo) {
623
294
  restFallback = {
624
295
  aNode: restInfo.next,
@@ -634,26 +305,28 @@ function matchRouteTree(root = {}, pathname) {
634
305
  layouts: [
635
306
  ...layouts
636
307
  ],
308
+ loaderPathsByHash: {
309
+ ...loaderPathsByHash
310
+ },
637
311
  errorLoader: errorLoaderRef.v,
638
312
  notFoundLoader: notFoundLoaderRef.v,
639
313
  menuLoader: menuLoaderRef.v
640
314
  };
641
315
  }
642
- const found = findChild(node, part, partLower, parts, i, params);
643
- if (!found) {
644
- matched = false;
645
- break;
316
+ if (found.paramName) {
317
+ params[found.paramName] = found.paramValue;
646
318
  }
647
319
  routeParts.push(found.routePart);
648
320
  done = found.done;
649
321
  node = found.next;
650
- collectNodeMeta(node, found.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef);
322
+ const matchedPathname = `/${parts.slice(0, found.done ? len : i + 1).join("/")}/`;
323
+ collectNodeMeta(node, found.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash, matchedPathname);
651
324
  }
652
325
  if (matched && !done && i === len) {
653
326
  if (!node._I && node._G == null) {
654
327
  const indexResult = findIndexNode(node);
655
328
  if (indexResult) {
656
- collectNodeMeta(indexResult.target, indexResult.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef);
329
+ collectNodeMeta(indexResult.target, indexResult.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash, pathname);
657
330
  node = indexResult.target;
658
331
  }
659
332
  }
@@ -677,7 +350,7 @@ function matchRouteTree(root = {}, pathname) {
677
350
  const paramName = next._P;
678
351
  params[paramName] = "";
679
352
  routeParts.push(`[...${paramName}]`);
680
- collectNodeMeta(next, restInfo.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef);
353
+ collectNodeMeta(next, restInfo.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash, pathname);
681
354
  node = next;
682
355
  }
683
356
  }
@@ -696,6 +369,9 @@ function matchRouteTree(root = {}, pathname) {
696
369
  const fbLayouts = [
697
370
  ...fb.layouts
698
371
  ];
372
+ const fbLoaderPathsByHash = {
373
+ ...fb.loaderPathsByHash
374
+ };
699
375
  const fbErrorRef = {
700
376
  v: fb.errorLoader
701
377
  };
@@ -705,7 +381,8 @@ function matchRouteTree(root = {}, pathname) {
705
381
  const fbMenuRef = {
706
382
  v: fb.menuLoader
707
383
  };
708
- collectNodeMeta(fb.aNode, fb.groups, fbLayouts, fbErrorRef, fbNotFoundRef, fbMenuRef);
384
+ const fbLoaderHashes = [];
385
+ collectNodeMeta(fb.aNode, fb.groups, fbLayouts, fbErrorRef, fbNotFoundRef, fbMenuRef, fbLoaderHashes, fbLoaderPathsByHash, pathname);
709
386
  const fbLoaders = resolveLoaders(root, fb.aNode, fbLayouts);
710
387
  if (fbLoaders) {
711
388
  return {
@@ -714,6 +391,8 @@ function matchRouteTree(root = {}, pathname) {
714
391
  routeParts: fbRouteParts,
715
392
  notFound: false,
716
393
  routeBundleNames: fb.aNode._B,
394
+ loaderHashes: fbLoaderHashes.length > 0 ? fbLoaderHashes : void 0,
395
+ loaderPathsByHash: Object.keys(fbLoaderPathsByHash).length > 0 ? fbLoaderPathsByHash : void 0,
717
396
  menuLoader: fbMenuRef.v,
718
397
  errorLoader: fbErrorRef.v,
719
398
  notFoundLoader: fbNotFoundRef.v
@@ -733,17 +412,29 @@ function matchRouteTree(root = {}, pathname) {
733
412
  routeParts,
734
413
  notFound: true,
735
414
  routeBundleNames: void 0,
415
+ loaderHashes: void 0,
416
+ loaderPathsByHash: void 0,
736
417
  menuLoader: menuLoaderRef.v,
737
418
  errorLoader: errorLoaderRef.v,
738
419
  notFoundLoader: notFoundLoaderRef.v
739
420
  };
740
421
  }
422
+ if (node._R) {
423
+ loaderHashes.push(...node._R);
424
+ const matchedPathname = ensureSlash(pathname);
425
+ for (let i2 = 0; i2 < node._R.length; i2++) {
426
+ const hash = node._R[i2];
427
+ loaderPathsByHash[hash] = matchedPathname;
428
+ }
429
+ }
741
430
  return {
742
431
  loaders,
743
432
  params,
744
433
  routeParts,
745
434
  notFound: false,
746
435
  routeBundleNames: node._B,
436
+ loaderHashes: loaderHashes.length > 0 ? loaderHashes : void 0,
437
+ loaderPathsByHash: Object.keys(loaderPathsByHash).length > 0 ? loaderPathsByHash : void 0,
747
438
  menuLoader: menuLoaderRef.v,
748
439
  errorLoader: errorLoaderRef.v,
749
440
  notFoundLoader: notFoundLoaderRef.v
@@ -769,19 +460,18 @@ const loadModule = (moduleLoader, pendingLoads, moduleSetter, cacheModules) => {
769
460
  }
770
461
  }
771
462
  };
772
- const loadRoute = async (routes, cacheModules, pathname, isInternal) => {
463
+ const loadRoute = async (routes, cacheModules, pathname) => {
773
464
  const result = matchRouteTree(routes, pathname);
774
- const { loaders, params, routeParts, notFound, routeBundleNames, menuLoader, errorLoader, notFoundLoader } = result;
465
+ const { loaders, params, routeParts, notFound, routeBundleNames, loaderHashes, loaderPathsByHash, menuLoader, errorLoader, notFoundLoader } = result;
775
466
  const routeName = "/" + routeParts.join("/");
776
467
  const modules = new Array(loaders.length);
777
468
  const pendingLoads = [];
778
- loaders.forEach((moduleLoader, i) => {
469
+ for (let i = 0; i < loaders.length; i++) {
470
+ const moduleLoader = loaders[i];
779
471
  loadModule(moduleLoader, pendingLoads, (routeModule) => modules[i] = routeModule, cacheModules);
780
- });
781
- let menu = void 0;
782
- if (!isInternal) {
783
- loadModule(menuLoader, pendingLoads, (menuModule) => menu = menuModule?.default, cacheModules);
784
472
  }
473
+ let menu = void 0;
474
+ loadModule(menuLoader, pendingLoads, (menuModule) => menu = menuModule?.default, cacheModules);
785
475
  if (notFound && notFoundLoader) {
786
476
  let notFoundMod;
787
477
  let errorMod;
@@ -801,7 +491,9 @@ const loadRoute = async (routes, cacheModules, pathname, isInternal) => {
801
491
  $menu$: deepFreeze(menu),
802
492
  $routeBundleNames$: routeBundleNames,
803
493
  $notFound$: notFound,
804
- $errorLoader$: errorLoader
494
+ $errorLoader$: errorLoader,
495
+ $loaders$: loaderHashes,
496
+ $loaderPaths$: loaderPathsByHash
805
497
  };
806
498
  }
807
499
  if (pendingLoads.length > 0) {
@@ -814,8 +506,199 @@ const loadRoute = async (routes, cacheModules, pathname, isInternal) => {
814
506
  $menu$: deepFreeze(menu),
815
507
  $routeBundleNames$: routeBundleNames,
816
508
  $notFound$: notFound,
817
- $errorLoader$: errorLoader
509
+ $errorLoader$: errorLoader,
510
+ $loaders$: loaderHashes,
511
+ $loaderPaths$: loaderPathsByHash
512
+ };
513
+ };
514
+
515
+ const toPath = (url) => url.pathname + url.search + url.hash;
516
+ const toUrl = (url, baseUrl) => new URL(url, baseUrl.href);
517
+ const isSameOrigin = (a, b) => a.origin === b.origin;
518
+ const isSamePathname = ({ pathname: a }, { pathname: b }) => {
519
+ const lDiff = Math.abs(a.length - b.length);
520
+ return lDiff === 0 ? a === b : lDiff === 1 && ensureSlash(a) === ensureSlash(b);
521
+ };
522
+ const isSameSearchQuery = (a, b) => a.search === b.search;
523
+ const isSamePath = (a, b) => isSameSearchQuery(a, b) && isSamePathname(a, b);
524
+ const getClientNavPath = (props, baseUrl) => {
525
+ const href = props.href;
526
+ if (typeof href === "string" && typeof props.target !== "string" && !props.reload) {
527
+ try {
528
+ const linkUrl = toUrl(href.trim(), baseUrl.url);
529
+ const currentUrl = toUrl("", baseUrl.url);
530
+ if (isSameOrigin(linkUrl, currentUrl)) {
531
+ return toPath(linkUrl);
532
+ }
533
+ } catch (e) {
534
+ console.error(e);
535
+ }
536
+ } else if (props.reload) {
537
+ return toPath(toUrl("", baseUrl.url));
538
+ }
539
+ return null;
540
+ };
541
+ const shouldPreload = (clientNavPath, currentLoc) => {
542
+ if (clientNavPath) {
543
+ const prefetchUrl = toUrl(clientNavPath, currentLoc.url);
544
+ const currentUrl = toUrl("", currentLoc.url);
545
+ return !isSamePathname(prefetchUrl, currentUrl);
546
+ }
547
+ return false;
548
+ };
549
+ const isPromise = (value) => {
550
+ return value && typeof value.then === "function";
551
+ };
552
+
553
+ const mergeArray = (existingArr, newArr) => {
554
+ if (Array.isArray(newArr)) {
555
+ for (const newItem of newArr) {
556
+ if (typeof newItem.key === "string") {
557
+ const existingIndex = existingArr.findIndex((i) => i.key === newItem.key);
558
+ if (existingIndex > -1) {
559
+ existingArr[existingIndex] = newItem;
560
+ continue;
561
+ }
562
+ }
563
+ existingArr.push(newItem);
564
+ }
565
+ }
566
+ };
567
+ const resolveDocumentHead = (resolvedHead, updatedHead) => {
568
+ if (typeof updatedHead.title === "string") {
569
+ resolvedHead.title = updatedHead.title;
570
+ }
571
+ mergeArray(resolvedHead.meta, updatedHead.meta);
572
+ mergeArray(resolvedHead.links, updatedHead.links);
573
+ mergeArray(resolvedHead.styles, updatedHead.styles);
574
+ mergeArray(resolvedHead.scripts, updatedHead.scripts);
575
+ Object.assign(resolvedHead.frontmatter, updatedHead.frontmatter);
576
+ };
577
+ const createDocumentHead = (defaults, manifestHash) => ({
578
+ title: defaults?.title || "",
579
+ meta: [
580
+ ...defaults?.meta || []
581
+ ],
582
+ links: [
583
+ ...defaults?.links || []
584
+ ],
585
+ styles: [
586
+ ...defaults?.styles || []
587
+ ],
588
+ scripts: [
589
+ ...defaults?.scripts || []
590
+ ],
591
+ frontmatter: {
592
+ ...defaults?.frontmatter
593
+ },
594
+ manifestHash: manifestHash || "dev"
595
+ });
596
+ const resolveRouteConfig = (resolveValue, routeLocation, contentModules, locale, status, defaults) => withLocale(locale, () => {
597
+ const head = createDocumentHead(defaults);
598
+ let eTag;
599
+ let cacheKey;
600
+ const fns = [];
601
+ for (let i = 0; i < contentModules.length; i++) {
602
+ const contentModule = contentModules[i];
603
+ if (!contentModule) {
604
+ continue;
605
+ }
606
+ const isLast = i === contentModules.length - 1;
607
+ let config;
608
+ if (contentModule.routeConfig) {
609
+ config = contentModule.routeConfig;
610
+ } else if (contentModule.head) {
611
+ const synthetic = {
612
+ head: void 0
613
+ };
614
+ const headExport = contentModule.head;
615
+ if (typeof headExport === "function") {
616
+ const mod = contentModule;
617
+ config = (props) => ({
618
+ head: headExport(props),
619
+ eTag: mod.eTag,
620
+ cacheKey: mod.cacheKey
621
+ });
622
+ } else {
623
+ synthetic.head = headExport;
624
+ const mod = contentModule;
625
+ if (mod.eTag !== void 0) {
626
+ synthetic.eTag = mod.eTag;
627
+ }
628
+ if (mod.cacheKey !== void 0) {
629
+ synthetic.cacheKey = mod.cacheKey;
630
+ }
631
+ config = synthetic;
632
+ }
633
+ } else {
634
+ const mod = contentModule;
635
+ if (mod.eTag !== void 0 || mod.cacheKey !== void 0) {
636
+ config = {
637
+ eTag: mod.eTag,
638
+ cacheKey: mod.cacheKey
639
+ };
640
+ }
641
+ }
642
+ if (!config) {
643
+ continue;
644
+ }
645
+ if (typeof config === "function") {
646
+ fns.unshift({
647
+ fn: config,
648
+ isLast
649
+ });
650
+ } else {
651
+ if (config.head) {
652
+ resolveDocumentHead(head, config.head);
653
+ }
654
+ if (config.eTag !== void 0) {
655
+ eTag = config.eTag;
656
+ }
657
+ if (config.cacheKey !== void 0) {
658
+ cacheKey = config.cacheKey;
659
+ }
660
+ }
661
+ }
662
+ if (fns.length) {
663
+ const headProps = {
664
+ head,
665
+ status,
666
+ withLocale: (fn) => fn(),
667
+ resolveValue,
668
+ ...routeLocation
669
+ };
670
+ for (const { fn } of fns) {
671
+ const result = fn(headProps);
672
+ if (result.head) {
673
+ resolveDocumentHead(head, result.head);
674
+ }
675
+ if (result.eTag !== void 0) {
676
+ eTag = result.eTag;
677
+ }
678
+ if (result.cacheKey !== void 0) {
679
+ cacheKey = result.cacheKey;
680
+ }
681
+ }
682
+ }
683
+ return {
684
+ head,
685
+ eTag,
686
+ cacheKey
687
+ };
688
+ });
689
+ const resolveHead = (actionData, loaderState, routeLocation, contentModules, locale, defaults) => {
690
+ const getData = (loaderOrAction) => {
691
+ const id = loaderOrAction.__id;
692
+ const signal = loaderState?.[id];
693
+ if (signal) {
694
+ return signal.value;
695
+ }
696
+ if (actionData?.action === id) {
697
+ return actionData.actionResult;
698
+ }
699
+ return void 0;
818
700
  };
701
+ return resolveRouteConfig(getData, routeLocation, contentModules, locale, actionData?.status ?? 200, defaults).head;
819
702
  };
820
703
 
821
- export { CLIENT_DATA_CACHE as C, DEFAULT_LOADERS_SERIALIZATION_STRATEGY as D, Q_ROUTE as Q, createDocumentHead as a, loadRoute as b, createLoaderSignal as c, isSameOrigin as d, clientNavigate as e, toPath as f, getClientNavPath as g, QFN_KEY as h, isSamePath as i, QACTION_KEY as j, QDATA_KEY as k, loadClientData as l, resolveRouteConfig as m, QLOADER_KEY as n, isPromise as o, preloadRouteBundles as p, resolveHead as r, shouldPreload as s, toUrl as t };
704
+ export { toUrl as a, isSameOrigin as b, createDocumentHead as c, resolveRouteConfig as d, isPromise as e, getClientNavPath as g, isSamePath as i, loadRoute as l, resolveHead as r, shouldPreload as s, toPath as t };