UNPKG

6.31 kBJavaScriptView Raw
1/**
2 * react-router v8.0.0
3 *
4 * Copyright (c) Remix Software Inc.
5 *
6 * This source code is licensed under the MIT license found in the
7 * LICENSE.md file in the root directory of this source tree.
8 *
9 * @license MIT
10 */
11import { loadRouteModule } from "./routeModules.js";
12//#region lib/dom/ssr/links.ts
13/**
14* Gets all the links for a set of matches. The modules are assumed to have been
15* loaded already.
16*/
17function getKeyedLinksForMatches(matches, routeModules, manifest) {
18 return dedupeLinkDescriptors(matches.map((match) => {
19 let module = routeModules[match.route.id];
20 let route = manifest.routes[match.route.id];
21 return [route && route.css ? route.css.map((href) => ({
22 rel: "stylesheet",
23 href
24 })) : [], module?.links?.() || []];
25 }).flat(2), getModuleLinkHrefs(matches, manifest));
26}
27function getRouteCssDescriptors(route) {
28 if (!route.css) return [];
29 return route.css.map((href) => ({
30 rel: "stylesheet",
31 href
32 }));
33}
34async function prefetchRouteCss(route) {
35 if (!route.css) return;
36 let descriptors = getRouteCssDescriptors(route);
37 await Promise.all(descriptors.map(prefetchStyleLink));
38}
39async function prefetchStyleLinks(route, routeModule) {
40 if (!route.css && !routeModule.links || !isPreloadSupported()) return;
41 let descriptors = [];
42 if (route.css) descriptors.push(...getRouteCssDescriptors(route));
43 if (routeModule.links) descriptors.push(...routeModule.links());
44 if (descriptors.length === 0) return;
45 let styleLinks = [];
46 for (let descriptor of descriptors) if (!isPageLinkDescriptor(descriptor) && descriptor.rel === "stylesheet") styleLinks.push({
47 ...descriptor,
48 rel: "preload",
49 as: "style"
50 });
51 await Promise.all(styleLinks.map(prefetchStyleLink));
52}
53async function prefetchStyleLink(descriptor) {
54 return new Promise((resolve) => {
55 if (descriptor.media && !window.matchMedia(descriptor.media).matches || document.querySelector(`link[rel="stylesheet"][href="${descriptor.href}"]`)) return resolve();
56 let link = document.createElement("link");
57 Object.assign(link, descriptor);
58 function removeLink() {
59 if (document.head.contains(link)) document.head.removeChild(link);
60 }
61 link.onload = () => {
62 removeLink();
63 resolve();
64 };
65 link.onerror = () => {
66 removeLink();
67 resolve();
68 };
69 document.head.appendChild(link);
70 });
71}
72function isPageLinkDescriptor(object) {
73 return object != null && typeof object.page === "string";
74}
75function isHtmlLinkDescriptor(object) {
76 if (object == null) return false;
77 if (object.href == null) return object.rel === "preload" && typeof object.imageSrcSet === "string" && typeof object.imageSizes === "string";
78 return typeof object.rel === "string" && typeof object.href === "string";
79}
80async function getKeyedPrefetchLinks(matches, manifest, routeModules) {
81 return dedupeLinkDescriptors((await Promise.all(matches.map(async (match) => {
82 let route = manifest.routes[match.route.id];
83 if (route) {
84 let mod = await loadRouteModule(route, routeModules);
85 return mod.links ? mod.links() : [];
86 }
87 return [];
88 }))).flat(1).filter(isHtmlLinkDescriptor).filter((link) => link.rel === "stylesheet" || link.rel === "preload").map((link) => link.rel === "stylesheet" ? {
89 ...link,
90 rel: "prefetch",
91 as: "style"
92 } : {
93 ...link,
94 rel: "prefetch"
95 }));
96}
97function getNewMatchesForLinks(page, nextMatches, currentMatches, manifest, location, mode) {
98 let isNew = (match, index) => {
99 if (!currentMatches[index]) return true;
100 return match.route.id !== currentMatches[index].route.id;
101 };
102 let matchPathChanged = (match, index) => {
103 return currentMatches[index].pathname !== match.pathname || currentMatches[index].route.path?.endsWith("*") && currentMatches[index].params["*"] !== match.params["*"];
104 };
105 if (mode === "assets") return nextMatches.filter((match, index) => isNew(match, index) || matchPathChanged(match, index));
106 if (mode === "data") return nextMatches.filter((match, index) => {
107 let manifestRoute = manifest.routes[match.route.id];
108 if (!manifestRoute || !manifestRoute.hasLoader) return false;
109 if (isNew(match, index) || matchPathChanged(match, index)) return true;
110 if (match.route.shouldRevalidate) {
111 let routeChoice = match.route.shouldRevalidate({
112 currentUrl: new URL(location.pathname + location.search + location.hash, window.origin),
113 currentParams: currentMatches[0]?.params || {},
114 nextUrl: new URL(page, window.origin),
115 nextParams: match.params,
116 defaultShouldRevalidate: true
117 });
118 if (typeof routeChoice === "boolean") return routeChoice;
119 }
120 return true;
121 });
122 return [];
123}
124function getModuleLinkHrefs(matches, manifest, { includeHydrateFallback } = {}) {
125 return dedupeHrefs(matches.map((match) => {
126 let route = manifest.routes[match.route.id];
127 if (!route) return [];
128 let hrefs = [route.module];
129 if (route.clientActionModule) hrefs = hrefs.concat(route.clientActionModule);
130 if (route.clientLoaderModule) hrefs = hrefs.concat(route.clientLoaderModule);
131 if (includeHydrateFallback && route.hydrateFallbackModule) hrefs = hrefs.concat(route.hydrateFallbackModule);
132 if (route.imports) hrefs = hrefs.concat(route.imports);
133 return hrefs;
134 }).flat(1));
135}
136function dedupeHrefs(hrefs) {
137 return [...new Set(hrefs)];
138}
139function sortKeys(obj) {
140 let sorted = {};
141 let keys = Object.keys(obj).sort();
142 for (let key of keys) sorted[key] = obj[key];
143 return sorted;
144}
145function dedupeLinkDescriptors(descriptors, preloads) {
146 let set = /* @__PURE__ */ new Set();
147 let preloadsSet = new Set(preloads);
148 return descriptors.reduce((deduped, descriptor) => {
149 if (preloads && !isPageLinkDescriptor(descriptor) && descriptor.as === "script" && descriptor.href && preloadsSet.has(descriptor.href)) return deduped;
150 let key = JSON.stringify(sortKeys(descriptor));
151 if (!set.has(key)) {
152 set.add(key);
153 deduped.push({
154 key,
155 link: descriptor
156 });
157 }
158 return deduped;
159 }, []);
160}
161let _isPreloadSupported;
162function isPreloadSupported() {
163 if (_isPreloadSupported !== void 0) return _isPreloadSupported;
164 let el = document.createElement("link");
165 _isPreloadSupported = el.relList.supports("preload");
166 el = null;
167 return _isPreloadSupported;
168}
169//#endregion
170export { getKeyedLinksForMatches, getKeyedPrefetchLinks, getModuleLinkHrefs, getNewMatchesForLinks, isPageLinkDescriptor, prefetchRouteCss, prefetchStyleLinks };