Install react-router-dom to support client-side navigation. This is a prerequisite for implementing the multi-step authentication flow and other routed features. Additionally, this commit includes minor copy improvements to the password reset forms for better clarity.
8221 lines
268 KiB
JavaScript
8221 lines
268 KiB
JavaScript
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }/**
|
|
* react-router v7.7.1
|
|
*
|
|
* Copyright (c) Remix Software Inc.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE.md file in the root directory of this source tree.
|
|
*
|
|
* @license MIT
|
|
*/
|
|
var __typeError = (msg) => {
|
|
throw TypeError(msg);
|
|
};
|
|
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
|
|
// lib/router/history.ts
|
|
var Action = /* @__PURE__ */ ((Action2) => {
|
|
Action2["Pop"] = "POP";
|
|
Action2["Push"] = "PUSH";
|
|
Action2["Replace"] = "REPLACE";
|
|
return Action2;
|
|
})(Action || {});
|
|
var PopStateEventType = "popstate";
|
|
function createMemoryHistory(options = {}) {
|
|
let { initialEntries = ["/"], initialIndex, v5Compat = false } = options;
|
|
let entries;
|
|
entries = initialEntries.map(
|
|
(entry, index2) => createMemoryLocation(
|
|
entry,
|
|
typeof entry === "string" ? null : entry.state,
|
|
index2 === 0 ? "default" : void 0
|
|
)
|
|
);
|
|
let index = clampIndex(
|
|
initialIndex == null ? entries.length - 1 : initialIndex
|
|
);
|
|
let action = "POP" /* Pop */;
|
|
let listener = null;
|
|
function clampIndex(n) {
|
|
return Math.min(Math.max(n, 0), entries.length - 1);
|
|
}
|
|
function getCurrentLocation() {
|
|
return entries[index];
|
|
}
|
|
function createMemoryLocation(to, state = null, key) {
|
|
let location = createLocation(
|
|
entries ? getCurrentLocation().pathname : "/",
|
|
to,
|
|
state,
|
|
key
|
|
);
|
|
warning(
|
|
location.pathname.charAt(0) === "/",
|
|
`relative pathnames are not supported in memory history: ${JSON.stringify(
|
|
to
|
|
)}`
|
|
);
|
|
return location;
|
|
}
|
|
function createHref(to) {
|
|
return typeof to === "string" ? to : createPath(to);
|
|
}
|
|
let history = {
|
|
get index() {
|
|
return index;
|
|
},
|
|
get action() {
|
|
return action;
|
|
},
|
|
get location() {
|
|
return getCurrentLocation();
|
|
},
|
|
createHref,
|
|
createURL(to) {
|
|
return new URL(createHref(to), "http://localhost");
|
|
},
|
|
encodeLocation(to) {
|
|
let path = typeof to === "string" ? parsePath(to) : to;
|
|
return {
|
|
pathname: path.pathname || "",
|
|
search: path.search || "",
|
|
hash: path.hash || ""
|
|
};
|
|
},
|
|
push(to, state) {
|
|
action = "PUSH" /* Push */;
|
|
let nextLocation = createMemoryLocation(to, state);
|
|
index += 1;
|
|
entries.splice(index, entries.length, nextLocation);
|
|
if (v5Compat && listener) {
|
|
listener({ action, location: nextLocation, delta: 1 });
|
|
}
|
|
},
|
|
replace(to, state) {
|
|
action = "REPLACE" /* Replace */;
|
|
let nextLocation = createMemoryLocation(to, state);
|
|
entries[index] = nextLocation;
|
|
if (v5Compat && listener) {
|
|
listener({ action, location: nextLocation, delta: 0 });
|
|
}
|
|
},
|
|
go(delta) {
|
|
action = "POP" /* Pop */;
|
|
let nextIndex = clampIndex(index + delta);
|
|
let nextLocation = entries[nextIndex];
|
|
index = nextIndex;
|
|
if (listener) {
|
|
listener({ action, location: nextLocation, delta });
|
|
}
|
|
},
|
|
listen(fn) {
|
|
listener = fn;
|
|
return () => {
|
|
listener = null;
|
|
};
|
|
}
|
|
};
|
|
return history;
|
|
}
|
|
function createBrowserHistory(options = {}) {
|
|
function createBrowserLocation(window2, globalHistory) {
|
|
let { pathname, search, hash } = window2.location;
|
|
return createLocation(
|
|
"",
|
|
{ pathname, search, hash },
|
|
// state defaults to `null` because `window.history.state` does
|
|
globalHistory.state && globalHistory.state.usr || null,
|
|
globalHistory.state && globalHistory.state.key || "default"
|
|
);
|
|
}
|
|
function createBrowserHref(window2, to) {
|
|
return typeof to === "string" ? to : createPath(to);
|
|
}
|
|
return getUrlBasedHistory(
|
|
createBrowserLocation,
|
|
createBrowserHref,
|
|
null,
|
|
options
|
|
);
|
|
}
|
|
function createHashHistory(options = {}) {
|
|
function createHashLocation(window2, globalHistory) {
|
|
let {
|
|
pathname = "/",
|
|
search = "",
|
|
hash = ""
|
|
} = parsePath(window2.location.hash.substring(1));
|
|
if (!pathname.startsWith("/") && !pathname.startsWith(".")) {
|
|
pathname = "/" + pathname;
|
|
}
|
|
return createLocation(
|
|
"",
|
|
{ pathname, search, hash },
|
|
// state defaults to `null` because `window.history.state` does
|
|
globalHistory.state && globalHistory.state.usr || null,
|
|
globalHistory.state && globalHistory.state.key || "default"
|
|
);
|
|
}
|
|
function createHashHref(window2, to) {
|
|
let base = window2.document.querySelector("base");
|
|
let href = "";
|
|
if (base && base.getAttribute("href")) {
|
|
let url = window2.location.href;
|
|
let hashIndex = url.indexOf("#");
|
|
href = hashIndex === -1 ? url : url.slice(0, hashIndex);
|
|
}
|
|
return href + "#" + (typeof to === "string" ? to : createPath(to));
|
|
}
|
|
function validateHashLocation(location, to) {
|
|
warning(
|
|
location.pathname.charAt(0) === "/",
|
|
`relative pathnames are not supported in hash history.push(${JSON.stringify(
|
|
to
|
|
)})`
|
|
);
|
|
}
|
|
return getUrlBasedHistory(
|
|
createHashLocation,
|
|
createHashHref,
|
|
validateHashLocation,
|
|
options
|
|
);
|
|
}
|
|
function invariant(value, message) {
|
|
if (value === false || value === null || typeof value === "undefined") {
|
|
throw new Error(message);
|
|
}
|
|
}
|
|
function warning(cond, message) {
|
|
if (!cond) {
|
|
if (typeof console !== "undefined") console.warn(message);
|
|
try {
|
|
throw new Error(message);
|
|
} catch (e) {
|
|
}
|
|
}
|
|
}
|
|
function createKey() {
|
|
return Math.random().toString(36).substring(2, 10);
|
|
}
|
|
function getHistoryState(location, index) {
|
|
return {
|
|
usr: location.state,
|
|
key: location.key,
|
|
idx: index
|
|
};
|
|
}
|
|
function createLocation(current, to, state = null, key) {
|
|
let location = {
|
|
pathname: typeof current === "string" ? current : current.pathname,
|
|
search: "",
|
|
hash: "",
|
|
...typeof to === "string" ? parsePath(to) : to,
|
|
state,
|
|
// TODO: This could be cleaned up. push/replace should probably just take
|
|
// full Locations now and avoid the need to run through this flow at all
|
|
// But that's a pretty big refactor to the current test suite so going to
|
|
// keep as is for the time being and just let any incoming keys take precedence
|
|
key: to && to.key || key || createKey()
|
|
};
|
|
return location;
|
|
}
|
|
function createPath({
|
|
pathname = "/",
|
|
search = "",
|
|
hash = ""
|
|
}) {
|
|
if (search && search !== "?")
|
|
pathname += search.charAt(0) === "?" ? search : "?" + search;
|
|
if (hash && hash !== "#")
|
|
pathname += hash.charAt(0) === "#" ? hash : "#" + hash;
|
|
return pathname;
|
|
}
|
|
function parsePath(path) {
|
|
let parsedPath = {};
|
|
if (path) {
|
|
let hashIndex = path.indexOf("#");
|
|
if (hashIndex >= 0) {
|
|
parsedPath.hash = path.substring(hashIndex);
|
|
path = path.substring(0, hashIndex);
|
|
}
|
|
let searchIndex = path.indexOf("?");
|
|
if (searchIndex >= 0) {
|
|
parsedPath.search = path.substring(searchIndex);
|
|
path = path.substring(0, searchIndex);
|
|
}
|
|
if (path) {
|
|
parsedPath.pathname = path;
|
|
}
|
|
}
|
|
return parsedPath;
|
|
}
|
|
function getUrlBasedHistory(getLocation, createHref, validateLocation, options = {}) {
|
|
let { window: window2 = document.defaultView, v5Compat = false } = options;
|
|
let globalHistory = window2.history;
|
|
let action = "POP" /* Pop */;
|
|
let listener = null;
|
|
let index = getIndex();
|
|
if (index == null) {
|
|
index = 0;
|
|
globalHistory.replaceState({ ...globalHistory.state, idx: index }, "");
|
|
}
|
|
function getIndex() {
|
|
let state = globalHistory.state || { idx: null };
|
|
return state.idx;
|
|
}
|
|
function handlePop() {
|
|
action = "POP" /* Pop */;
|
|
let nextIndex = getIndex();
|
|
let delta = nextIndex == null ? null : nextIndex - index;
|
|
index = nextIndex;
|
|
if (listener) {
|
|
listener({ action, location: history.location, delta });
|
|
}
|
|
}
|
|
function push(to, state) {
|
|
action = "PUSH" /* Push */;
|
|
let location = createLocation(history.location, to, state);
|
|
if (validateLocation) validateLocation(location, to);
|
|
index = getIndex() + 1;
|
|
let historyState = getHistoryState(location, index);
|
|
let url = history.createHref(location);
|
|
try {
|
|
globalHistory.pushState(historyState, "", url);
|
|
} catch (error) {
|
|
if (error instanceof DOMException && error.name === "DataCloneError") {
|
|
throw error;
|
|
}
|
|
window2.location.assign(url);
|
|
}
|
|
if (v5Compat && listener) {
|
|
listener({ action, location: history.location, delta: 1 });
|
|
}
|
|
}
|
|
function replace2(to, state) {
|
|
action = "REPLACE" /* Replace */;
|
|
let location = createLocation(history.location, to, state);
|
|
if (validateLocation) validateLocation(location, to);
|
|
index = getIndex();
|
|
let historyState = getHistoryState(location, index);
|
|
let url = history.createHref(location);
|
|
globalHistory.replaceState(historyState, "", url);
|
|
if (v5Compat && listener) {
|
|
listener({ action, location: history.location, delta: 0 });
|
|
}
|
|
}
|
|
function createURL(to) {
|
|
return createBrowserURLImpl(to);
|
|
}
|
|
let history = {
|
|
get action() {
|
|
return action;
|
|
},
|
|
get location() {
|
|
return getLocation(window2, globalHistory);
|
|
},
|
|
listen(fn) {
|
|
if (listener) {
|
|
throw new Error("A history only accepts one active listener");
|
|
}
|
|
window2.addEventListener(PopStateEventType, handlePop);
|
|
listener = fn;
|
|
return () => {
|
|
window2.removeEventListener(PopStateEventType, handlePop);
|
|
listener = null;
|
|
};
|
|
},
|
|
createHref(to) {
|
|
return createHref(window2, to);
|
|
},
|
|
createURL,
|
|
encodeLocation(to) {
|
|
let url = createURL(to);
|
|
return {
|
|
pathname: url.pathname,
|
|
search: url.search,
|
|
hash: url.hash
|
|
};
|
|
},
|
|
push,
|
|
replace: replace2,
|
|
go(n) {
|
|
return globalHistory.go(n);
|
|
}
|
|
};
|
|
return history;
|
|
}
|
|
function createBrowserURLImpl(to, isAbsolute = false) {
|
|
let base = "http://localhost";
|
|
if (typeof window !== "undefined") {
|
|
base = window.location.origin !== "null" ? window.location.origin : window.location.href;
|
|
}
|
|
invariant(base, "No window.location.(origin|href) available to create URL");
|
|
let href = typeof to === "string" ? to : createPath(to);
|
|
href = href.replace(/ $/, "%20");
|
|
if (!isAbsolute && href.startsWith("//")) {
|
|
href = base + href;
|
|
}
|
|
return new URL(href, base);
|
|
}
|
|
|
|
// lib/router/utils.ts
|
|
function unstable_createContext(defaultValue) {
|
|
return { defaultValue };
|
|
}
|
|
var _map;
|
|
var unstable_RouterContextProvider = class {
|
|
constructor(init) {
|
|
__privateAdd(this, _map, /* @__PURE__ */ new Map());
|
|
if (init) {
|
|
for (let [context, value] of init) {
|
|
this.set(context, value);
|
|
}
|
|
}
|
|
}
|
|
get(context) {
|
|
if (__privateGet(this, _map).has(context)) {
|
|
return __privateGet(this, _map).get(context);
|
|
}
|
|
if (context.defaultValue !== void 0) {
|
|
return context.defaultValue;
|
|
}
|
|
throw new Error("No value found for context");
|
|
}
|
|
set(context, value) {
|
|
__privateGet(this, _map).set(context, value);
|
|
}
|
|
};
|
|
_map = new WeakMap();
|
|
var unsupportedLazyRouteObjectKeys = /* @__PURE__ */ new Set([
|
|
"lazy",
|
|
"caseSensitive",
|
|
"path",
|
|
"id",
|
|
"index",
|
|
"children"
|
|
]);
|
|
function isUnsupportedLazyRouteObjectKey(key) {
|
|
return unsupportedLazyRouteObjectKeys.has(
|
|
key
|
|
);
|
|
}
|
|
var unsupportedLazyRouteFunctionKeys = /* @__PURE__ */ new Set([
|
|
"lazy",
|
|
"caseSensitive",
|
|
"path",
|
|
"id",
|
|
"index",
|
|
"unstable_middleware",
|
|
"children"
|
|
]);
|
|
function isUnsupportedLazyRouteFunctionKey(key) {
|
|
return unsupportedLazyRouteFunctionKeys.has(
|
|
key
|
|
);
|
|
}
|
|
function isIndexRoute(route) {
|
|
return route.index === true;
|
|
}
|
|
function convertRoutesToDataRoutes(routes, mapRouteProperties, parentPath = [], manifest = {}, allowInPlaceMutations = false) {
|
|
return routes.map((route, index) => {
|
|
let treePath = [...parentPath, String(index)];
|
|
let id = typeof route.id === "string" ? route.id : treePath.join("-");
|
|
invariant(
|
|
route.index !== true || !route.children,
|
|
`Cannot specify children on an index route`
|
|
);
|
|
invariant(
|
|
allowInPlaceMutations || !manifest[id],
|
|
`Found a route id collision on id "${id}". Route id's must be globally unique within Data Router usages`
|
|
);
|
|
if (isIndexRoute(route)) {
|
|
let indexRoute = {
|
|
...route,
|
|
...mapRouteProperties(route),
|
|
id
|
|
};
|
|
manifest[id] = indexRoute;
|
|
return indexRoute;
|
|
} else {
|
|
let pathOrLayoutRoute = {
|
|
...route,
|
|
...mapRouteProperties(route),
|
|
id,
|
|
children: void 0
|
|
};
|
|
manifest[id] = pathOrLayoutRoute;
|
|
if (route.children) {
|
|
pathOrLayoutRoute.children = convertRoutesToDataRoutes(
|
|
route.children,
|
|
mapRouteProperties,
|
|
treePath,
|
|
manifest,
|
|
allowInPlaceMutations
|
|
);
|
|
}
|
|
return pathOrLayoutRoute;
|
|
}
|
|
});
|
|
}
|
|
function matchRoutes(routes, locationArg, basename = "/") {
|
|
return matchRoutesImpl(routes, locationArg, basename, false);
|
|
}
|
|
function matchRoutesImpl(routes, locationArg, basename, allowPartial) {
|
|
let location = typeof locationArg === "string" ? parsePath(locationArg) : locationArg;
|
|
let pathname = stripBasename(location.pathname || "/", basename);
|
|
if (pathname == null) {
|
|
return null;
|
|
}
|
|
let branches = flattenRoutes(routes);
|
|
rankRouteBranches(branches);
|
|
let matches = null;
|
|
for (let i = 0; matches == null && i < branches.length; ++i) {
|
|
let decoded = decodePath(pathname);
|
|
matches = matchRouteBranch(
|
|
branches[i],
|
|
decoded,
|
|
allowPartial
|
|
);
|
|
}
|
|
return matches;
|
|
}
|
|
function convertRouteMatchToUiMatch(match, loaderData) {
|
|
let { route, pathname, params } = match;
|
|
return {
|
|
id: route.id,
|
|
pathname,
|
|
params,
|
|
data: loaderData[route.id],
|
|
handle: route.handle
|
|
};
|
|
}
|
|
function flattenRoutes(routes, branches = [], parentsMeta = [], parentPath = "") {
|
|
let flattenRoute = (route, index, relativePath) => {
|
|
let meta = {
|
|
relativePath: relativePath === void 0 ? route.path || "" : relativePath,
|
|
caseSensitive: route.caseSensitive === true,
|
|
childrenIndex: index,
|
|
route
|
|
};
|
|
if (meta.relativePath.startsWith("/")) {
|
|
invariant(
|
|
meta.relativePath.startsWith(parentPath),
|
|
`Absolute route path "${meta.relativePath}" nested under path "${parentPath}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`
|
|
);
|
|
meta.relativePath = meta.relativePath.slice(parentPath.length);
|
|
}
|
|
let path = joinPaths([parentPath, meta.relativePath]);
|
|
let routesMeta = parentsMeta.concat(meta);
|
|
if (route.children && route.children.length > 0) {
|
|
invariant(
|
|
// Our types know better, but runtime JS may not!
|
|
// @ts-expect-error
|
|
route.index !== true,
|
|
`Index routes must not have child routes. Please remove all child routes from route path "${path}".`
|
|
);
|
|
flattenRoutes(route.children, branches, routesMeta, path);
|
|
}
|
|
if (route.path == null && !route.index) {
|
|
return;
|
|
}
|
|
branches.push({
|
|
path,
|
|
score: computeScore(path, route.index),
|
|
routesMeta
|
|
});
|
|
};
|
|
routes.forEach((route, index) => {
|
|
if (route.path === "" || !_optionalChain([route, 'access', _2 => _2.path, 'optionalAccess', _3 => _3.includes, 'call', _4 => _4("?")])) {
|
|
flattenRoute(route, index);
|
|
} else {
|
|
for (let exploded of explodeOptionalSegments(route.path)) {
|
|
flattenRoute(route, index, exploded);
|
|
}
|
|
}
|
|
});
|
|
return branches;
|
|
}
|
|
function explodeOptionalSegments(path) {
|
|
let segments = path.split("/");
|
|
if (segments.length === 0) return [];
|
|
let [first, ...rest] = segments;
|
|
let isOptional = first.endsWith("?");
|
|
let required = first.replace(/\?$/, "");
|
|
if (rest.length === 0) {
|
|
return isOptional ? [required, ""] : [required];
|
|
}
|
|
let restExploded = explodeOptionalSegments(rest.join("/"));
|
|
let result = [];
|
|
result.push(
|
|
...restExploded.map(
|
|
(subpath) => subpath === "" ? required : [required, subpath].join("/")
|
|
)
|
|
);
|
|
if (isOptional) {
|
|
result.push(...restExploded);
|
|
}
|
|
return result.map(
|
|
(exploded) => path.startsWith("/") && exploded === "" ? "/" : exploded
|
|
);
|
|
}
|
|
function rankRouteBranches(branches) {
|
|
branches.sort(
|
|
(a, b) => a.score !== b.score ? b.score - a.score : compareIndexes(
|
|
a.routesMeta.map((meta) => meta.childrenIndex),
|
|
b.routesMeta.map((meta) => meta.childrenIndex)
|
|
)
|
|
);
|
|
}
|
|
var paramRe = /^:[\w-]+$/;
|
|
var dynamicSegmentValue = 3;
|
|
var indexRouteValue = 2;
|
|
var emptySegmentValue = 1;
|
|
var staticSegmentValue = 10;
|
|
var splatPenalty = -2;
|
|
var isSplat = (s) => s === "*";
|
|
function computeScore(path, index) {
|
|
let segments = path.split("/");
|
|
let initialScore = segments.length;
|
|
if (segments.some(isSplat)) {
|
|
initialScore += splatPenalty;
|
|
}
|
|
if (index) {
|
|
initialScore += indexRouteValue;
|
|
}
|
|
return segments.filter((s) => !isSplat(s)).reduce(
|
|
(score, segment) => score + (paramRe.test(segment) ? dynamicSegmentValue : segment === "" ? emptySegmentValue : staticSegmentValue),
|
|
initialScore
|
|
);
|
|
}
|
|
function compareIndexes(a, b) {
|
|
let siblings = a.length === b.length && a.slice(0, -1).every((n, i) => n === b[i]);
|
|
return siblings ? (
|
|
// If two routes are siblings, we should try to match the earlier sibling
|
|
// first. This allows people to have fine-grained control over the matching
|
|
// behavior by simply putting routes with identical paths in the order they
|
|
// want them tried.
|
|
a[a.length - 1] - b[b.length - 1]
|
|
) : (
|
|
// Otherwise, it doesn't really make sense to rank non-siblings by index,
|
|
// so they sort equally.
|
|
0
|
|
);
|
|
}
|
|
function matchRouteBranch(branch, pathname, allowPartial = false) {
|
|
let { routesMeta } = branch;
|
|
let matchedParams = {};
|
|
let matchedPathname = "/";
|
|
let matches = [];
|
|
for (let i = 0; i < routesMeta.length; ++i) {
|
|
let meta = routesMeta[i];
|
|
let end = i === routesMeta.length - 1;
|
|
let remainingPathname = matchedPathname === "/" ? pathname : pathname.slice(matchedPathname.length) || "/";
|
|
let match = matchPath(
|
|
{ path: meta.relativePath, caseSensitive: meta.caseSensitive, end },
|
|
remainingPathname
|
|
);
|
|
let route = meta.route;
|
|
if (!match && end && allowPartial && !routesMeta[routesMeta.length - 1].route.index) {
|
|
match = matchPath(
|
|
{
|
|
path: meta.relativePath,
|
|
caseSensitive: meta.caseSensitive,
|
|
end: false
|
|
},
|
|
remainingPathname
|
|
);
|
|
}
|
|
if (!match) {
|
|
return null;
|
|
}
|
|
Object.assign(matchedParams, match.params);
|
|
matches.push({
|
|
// TODO: Can this as be avoided?
|
|
params: matchedParams,
|
|
pathname: joinPaths([matchedPathname, match.pathname]),
|
|
pathnameBase: normalizePathname(
|
|
joinPaths([matchedPathname, match.pathnameBase])
|
|
),
|
|
route
|
|
});
|
|
if (match.pathnameBase !== "/") {
|
|
matchedPathname = joinPaths([matchedPathname, match.pathnameBase]);
|
|
}
|
|
}
|
|
return matches;
|
|
}
|
|
function generatePath(originalPath, params = {}) {
|
|
let path = originalPath;
|
|
if (path.endsWith("*") && path !== "*" && !path.endsWith("/*")) {
|
|
warning(
|
|
false,
|
|
`Route path "${path}" will be treated as if it were "${path.replace(/\*$/, "/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${path.replace(/\*$/, "/*")}".`
|
|
);
|
|
path = path.replace(/\*$/, "/*");
|
|
}
|
|
const prefix = path.startsWith("/") ? "/" : "";
|
|
const stringify2 = (p) => p == null ? "" : typeof p === "string" ? p : String(p);
|
|
const segments = path.split(/\/+/).map((segment, index, array) => {
|
|
const isLastSegment = index === array.length - 1;
|
|
if (isLastSegment && segment === "*") {
|
|
const star = "*";
|
|
return stringify2(params[star]);
|
|
}
|
|
const keyMatch = segment.match(/^:([\w-]+)(\??)$/);
|
|
if (keyMatch) {
|
|
const [, key, optional] = keyMatch;
|
|
let param = params[key];
|
|
invariant(optional === "?" || param != null, `Missing ":${key}" param`);
|
|
return stringify2(param);
|
|
}
|
|
return segment.replace(/\?$/g, "");
|
|
}).filter((segment) => !!segment);
|
|
return prefix + segments.join("/");
|
|
}
|
|
function matchPath(pattern, pathname) {
|
|
if (typeof pattern === "string") {
|
|
pattern = { path: pattern, caseSensitive: false, end: true };
|
|
}
|
|
let [matcher, compiledParams] = compilePath(
|
|
pattern.path,
|
|
pattern.caseSensitive,
|
|
pattern.end
|
|
);
|
|
let match = pathname.match(matcher);
|
|
if (!match) return null;
|
|
let matchedPathname = match[0];
|
|
let pathnameBase = matchedPathname.replace(/(.)\/+$/, "$1");
|
|
let captureGroups = match.slice(1);
|
|
let params = compiledParams.reduce(
|
|
(memo, { paramName, isOptional }, index) => {
|
|
if (paramName === "*") {
|
|
let splatValue = captureGroups[index] || "";
|
|
pathnameBase = matchedPathname.slice(0, matchedPathname.length - splatValue.length).replace(/(.)\/+$/, "$1");
|
|
}
|
|
const value = captureGroups[index];
|
|
if (isOptional && !value) {
|
|
memo[paramName] = void 0;
|
|
} else {
|
|
memo[paramName] = (value || "").replace(/%2F/g, "/");
|
|
}
|
|
return memo;
|
|
},
|
|
{}
|
|
);
|
|
return {
|
|
params,
|
|
pathname: matchedPathname,
|
|
pathnameBase,
|
|
pattern
|
|
};
|
|
}
|
|
function compilePath(path, caseSensitive = false, end = true) {
|
|
warning(
|
|
path === "*" || !path.endsWith("*") || path.endsWith("/*"),
|
|
`Route path "${path}" will be treated as if it were "${path.replace(/\*$/, "/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${path.replace(/\*$/, "/*")}".`
|
|
);
|
|
let params = [];
|
|
let regexpSource = "^" + path.replace(/\/*\*?$/, "").replace(/^\/*/, "/").replace(/[\\.*+^${}|()[\]]/g, "\\$&").replace(
|
|
/\/:([\w-]+)(\?)?/g,
|
|
(_, paramName, isOptional) => {
|
|
params.push({ paramName, isOptional: isOptional != null });
|
|
return isOptional ? "/?([^\\/]+)?" : "/([^\\/]+)";
|
|
}
|
|
);
|
|
if (path.endsWith("*")) {
|
|
params.push({ paramName: "*" });
|
|
regexpSource += path === "*" || path === "/*" ? "(.*)$" : "(?:\\/(.+)|\\/*)$";
|
|
} else if (end) {
|
|
regexpSource += "\\/*$";
|
|
} else if (path !== "" && path !== "/") {
|
|
regexpSource += "(?:(?=\\/|$))";
|
|
} else {
|
|
}
|
|
let matcher = new RegExp(regexpSource, caseSensitive ? void 0 : "i");
|
|
return [matcher, params];
|
|
}
|
|
function decodePath(value) {
|
|
try {
|
|
return value.split("/").map((v) => decodeURIComponent(v).replace(/\//g, "%2F")).join("/");
|
|
} catch (error) {
|
|
warning(
|
|
false,
|
|
`The URL path "${value}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${error}).`
|
|
);
|
|
return value;
|
|
}
|
|
}
|
|
function stripBasename(pathname, basename) {
|
|
if (basename === "/") return pathname;
|
|
if (!pathname.toLowerCase().startsWith(basename.toLowerCase())) {
|
|
return null;
|
|
}
|
|
let startIndex = basename.endsWith("/") ? basename.length - 1 : basename.length;
|
|
let nextChar = pathname.charAt(startIndex);
|
|
if (nextChar && nextChar !== "/") {
|
|
return null;
|
|
}
|
|
return pathname.slice(startIndex) || "/";
|
|
}
|
|
function prependBasename({
|
|
basename,
|
|
pathname
|
|
}) {
|
|
return pathname === "/" ? basename : joinPaths([basename, pathname]);
|
|
}
|
|
function resolvePath(to, fromPathname = "/") {
|
|
let {
|
|
pathname: toPathname,
|
|
search = "",
|
|
hash = ""
|
|
} = typeof to === "string" ? parsePath(to) : to;
|
|
let pathname = toPathname ? toPathname.startsWith("/") ? toPathname : resolvePathname(toPathname, fromPathname) : fromPathname;
|
|
return {
|
|
pathname,
|
|
search: normalizeSearch(search),
|
|
hash: normalizeHash(hash)
|
|
};
|
|
}
|
|
function resolvePathname(relativePath, fromPathname) {
|
|
let segments = fromPathname.replace(/\/+$/, "").split("/");
|
|
let relativeSegments = relativePath.split("/");
|
|
relativeSegments.forEach((segment) => {
|
|
if (segment === "..") {
|
|
if (segments.length > 1) segments.pop();
|
|
} else if (segment !== ".") {
|
|
segments.push(segment);
|
|
}
|
|
});
|
|
return segments.length > 1 ? segments.join("/") : "/";
|
|
}
|
|
function getInvalidPathError(char, field, dest, path) {
|
|
return `Cannot include a '${char}' character in a manually specified \`to.${field}\` field [${JSON.stringify(
|
|
path
|
|
)}]. Please separate it out to the \`to.${dest}\` field. Alternatively you may provide the full path as a string in <Link to="..."> and the router will parse it for you.`;
|
|
}
|
|
function getPathContributingMatches(matches) {
|
|
return matches.filter(
|
|
(match, index) => index === 0 || match.route.path && match.route.path.length > 0
|
|
);
|
|
}
|
|
function getResolveToMatches(matches) {
|
|
let pathMatches = getPathContributingMatches(matches);
|
|
return pathMatches.map(
|
|
(match, idx) => idx === pathMatches.length - 1 ? match.pathname : match.pathnameBase
|
|
);
|
|
}
|
|
function resolveTo(toArg, routePathnames, locationPathname, isPathRelative = false) {
|
|
let to;
|
|
if (typeof toArg === "string") {
|
|
to = parsePath(toArg);
|
|
} else {
|
|
to = { ...toArg };
|
|
invariant(
|
|
!to.pathname || !to.pathname.includes("?"),
|
|
getInvalidPathError("?", "pathname", "search", to)
|
|
);
|
|
invariant(
|
|
!to.pathname || !to.pathname.includes("#"),
|
|
getInvalidPathError("#", "pathname", "hash", to)
|
|
);
|
|
invariant(
|
|
!to.search || !to.search.includes("#"),
|
|
getInvalidPathError("#", "search", "hash", to)
|
|
);
|
|
}
|
|
let isEmptyPath = toArg === "" || to.pathname === "";
|
|
let toPathname = isEmptyPath ? "/" : to.pathname;
|
|
let from;
|
|
if (toPathname == null) {
|
|
from = locationPathname;
|
|
} else {
|
|
let routePathnameIndex = routePathnames.length - 1;
|
|
if (!isPathRelative && toPathname.startsWith("..")) {
|
|
let toSegments = toPathname.split("/");
|
|
while (toSegments[0] === "..") {
|
|
toSegments.shift();
|
|
routePathnameIndex -= 1;
|
|
}
|
|
to.pathname = toSegments.join("/");
|
|
}
|
|
from = routePathnameIndex >= 0 ? routePathnames[routePathnameIndex] : "/";
|
|
}
|
|
let path = resolvePath(to, from);
|
|
let hasExplicitTrailingSlash = toPathname && toPathname !== "/" && toPathname.endsWith("/");
|
|
let hasCurrentTrailingSlash = (isEmptyPath || toPathname === ".") && locationPathname.endsWith("/");
|
|
if (!path.pathname.endsWith("/") && (hasExplicitTrailingSlash || hasCurrentTrailingSlash)) {
|
|
path.pathname += "/";
|
|
}
|
|
return path;
|
|
}
|
|
var joinPaths = (paths) => paths.join("/").replace(/\/\/+/g, "/");
|
|
var normalizePathname = (pathname) => pathname.replace(/\/+$/, "").replace(/^\/*/, "/");
|
|
var normalizeSearch = (search) => !search || search === "?" ? "" : search.startsWith("?") ? search : "?" + search;
|
|
var normalizeHash = (hash) => !hash || hash === "#" ? "" : hash.startsWith("#") ? hash : "#" + hash;
|
|
var DataWithResponseInit = class {
|
|
constructor(data2, init) {
|
|
this.type = "DataWithResponseInit";
|
|
this.data = data2;
|
|
this.init = init || null;
|
|
}
|
|
};
|
|
function data(data2, init) {
|
|
return new DataWithResponseInit(
|
|
data2,
|
|
typeof init === "number" ? { status: init } : init
|
|
);
|
|
}
|
|
var redirect = (url, init = 302) => {
|
|
let responseInit = init;
|
|
if (typeof responseInit === "number") {
|
|
responseInit = { status: responseInit };
|
|
} else if (typeof responseInit.status === "undefined") {
|
|
responseInit.status = 302;
|
|
}
|
|
let headers = new Headers(responseInit.headers);
|
|
headers.set("Location", url);
|
|
return new Response(null, { ...responseInit, headers });
|
|
};
|
|
var redirectDocument = (url, init) => {
|
|
let response = redirect(url, init);
|
|
response.headers.set("X-Remix-Reload-Document", "true");
|
|
return response;
|
|
};
|
|
var replace = (url, init) => {
|
|
let response = redirect(url, init);
|
|
response.headers.set("X-Remix-Replace", "true");
|
|
return response;
|
|
};
|
|
var ErrorResponseImpl = class {
|
|
constructor(status, statusText, data2, internal = false) {
|
|
this.status = status;
|
|
this.statusText = statusText || "";
|
|
this.internal = internal;
|
|
if (data2 instanceof Error) {
|
|
this.data = data2.toString();
|
|
this.error = data2;
|
|
} else {
|
|
this.data = data2;
|
|
}
|
|
}
|
|
};
|
|
function isRouteErrorResponse(error) {
|
|
return error != null && typeof error.status === "number" && typeof error.statusText === "string" && typeof error.internal === "boolean" && "data" in error;
|
|
}
|
|
|
|
// lib/router/router.ts
|
|
var validMutationMethodsArr = [
|
|
"POST",
|
|
"PUT",
|
|
"PATCH",
|
|
"DELETE"
|
|
];
|
|
var validMutationMethods = new Set(
|
|
validMutationMethodsArr
|
|
);
|
|
var validRequestMethodsArr = [
|
|
"GET",
|
|
...validMutationMethodsArr
|
|
];
|
|
var validRequestMethods = new Set(validRequestMethodsArr);
|
|
var redirectStatusCodes = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
|
|
var redirectPreserveMethodStatusCodes = /* @__PURE__ */ new Set([307, 308]);
|
|
var IDLE_NAVIGATION = {
|
|
state: "idle",
|
|
location: void 0,
|
|
formMethod: void 0,
|
|
formAction: void 0,
|
|
formEncType: void 0,
|
|
formData: void 0,
|
|
json: void 0,
|
|
text: void 0
|
|
};
|
|
var IDLE_FETCHER = {
|
|
state: "idle",
|
|
data: void 0,
|
|
formMethod: void 0,
|
|
formAction: void 0,
|
|
formEncType: void 0,
|
|
formData: void 0,
|
|
json: void 0,
|
|
text: void 0
|
|
};
|
|
var IDLE_BLOCKER = {
|
|
state: "unblocked",
|
|
proceed: void 0,
|
|
reset: void 0,
|
|
location: void 0
|
|
};
|
|
var ABSOLUTE_URL_REGEX = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;
|
|
var isAbsoluteUrl = (url) => ABSOLUTE_URL_REGEX.test(url);
|
|
var defaultMapRouteProperties = (route) => ({
|
|
hasErrorBoundary: Boolean(route.hasErrorBoundary)
|
|
});
|
|
var TRANSITIONS_STORAGE_KEY = "remix-router-transitions";
|
|
var ResetLoaderDataSymbol = Symbol("ResetLoaderData");
|
|
function createRouter(init) {
|
|
const routerWindow = init.window ? init.window : typeof window !== "undefined" ? window : void 0;
|
|
const isBrowser = typeof routerWindow !== "undefined" && typeof routerWindow.document !== "undefined" && typeof routerWindow.document.createElement !== "undefined";
|
|
invariant(
|
|
init.routes.length > 0,
|
|
"You must provide a non-empty routes array to createRouter"
|
|
);
|
|
let hydrationRouteProperties = init.hydrationRouteProperties || [];
|
|
let mapRouteProperties = init.mapRouteProperties || defaultMapRouteProperties;
|
|
let manifest = {};
|
|
let dataRoutes = convertRoutesToDataRoutes(
|
|
init.routes,
|
|
mapRouteProperties,
|
|
void 0,
|
|
manifest
|
|
);
|
|
let inFlightDataRoutes;
|
|
let basename = init.basename || "/";
|
|
let dataStrategyImpl = init.dataStrategy || defaultDataStrategyWithMiddleware;
|
|
let future = {
|
|
unstable_middleware: false,
|
|
...init.future
|
|
};
|
|
let unlistenHistory = null;
|
|
let subscribers = /* @__PURE__ */ new Set();
|
|
let savedScrollPositions = null;
|
|
let getScrollRestorationKey = null;
|
|
let getScrollPosition = null;
|
|
let initialScrollRestored = init.hydrationData != null;
|
|
let initialMatches = matchRoutes(dataRoutes, init.history.location, basename);
|
|
let initialMatchesIsFOW = false;
|
|
let initialErrors = null;
|
|
let initialized;
|
|
if (initialMatches == null && !init.patchRoutesOnNavigation) {
|
|
let error = getInternalRouterError(404, {
|
|
pathname: init.history.location.pathname
|
|
});
|
|
let { matches, route } = getShortCircuitMatches(dataRoutes);
|
|
initialized = true;
|
|
initialMatches = matches;
|
|
initialErrors = { [route.id]: error };
|
|
} else {
|
|
if (initialMatches && !init.hydrationData) {
|
|
let fogOfWar = checkFogOfWar(
|
|
initialMatches,
|
|
dataRoutes,
|
|
init.history.location.pathname
|
|
);
|
|
if (fogOfWar.active) {
|
|
initialMatches = null;
|
|
}
|
|
}
|
|
if (!initialMatches) {
|
|
initialized = false;
|
|
initialMatches = [];
|
|
let fogOfWar = checkFogOfWar(
|
|
null,
|
|
dataRoutes,
|
|
init.history.location.pathname
|
|
);
|
|
if (fogOfWar.active && fogOfWar.matches) {
|
|
initialMatchesIsFOW = true;
|
|
initialMatches = fogOfWar.matches;
|
|
}
|
|
} else if (initialMatches.some((m) => m.route.lazy)) {
|
|
initialized = false;
|
|
} else if (!initialMatches.some((m) => m.route.loader)) {
|
|
initialized = true;
|
|
} else {
|
|
let loaderData = init.hydrationData ? init.hydrationData.loaderData : null;
|
|
let errors = init.hydrationData ? init.hydrationData.errors : null;
|
|
if (errors) {
|
|
let idx = initialMatches.findIndex(
|
|
(m) => errors[m.route.id] !== void 0
|
|
);
|
|
initialized = initialMatches.slice(0, idx + 1).every(
|
|
(m) => !shouldLoadRouteOnHydration(m.route, loaderData, errors)
|
|
);
|
|
} else {
|
|
initialized = initialMatches.every(
|
|
(m) => !shouldLoadRouteOnHydration(m.route, loaderData, errors)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
let router;
|
|
let state = {
|
|
historyAction: init.history.action,
|
|
location: init.history.location,
|
|
matches: initialMatches,
|
|
initialized,
|
|
navigation: IDLE_NAVIGATION,
|
|
// Don't restore on initial updateState() if we were SSR'd
|
|
restoreScrollPosition: init.hydrationData != null ? false : null,
|
|
preventScrollReset: false,
|
|
revalidation: "idle",
|
|
loaderData: init.hydrationData && init.hydrationData.loaderData || {},
|
|
actionData: init.hydrationData && init.hydrationData.actionData || null,
|
|
errors: init.hydrationData && init.hydrationData.errors || initialErrors,
|
|
fetchers: /* @__PURE__ */ new Map(),
|
|
blockers: /* @__PURE__ */ new Map()
|
|
};
|
|
let pendingAction = "POP" /* Pop */;
|
|
let pendingPreventScrollReset = false;
|
|
let pendingNavigationController;
|
|
let pendingViewTransitionEnabled = false;
|
|
let appliedViewTransitions = /* @__PURE__ */ new Map();
|
|
let removePageHideEventListener = null;
|
|
let isUninterruptedRevalidation = false;
|
|
let isRevalidationRequired = false;
|
|
let cancelledFetcherLoads = /* @__PURE__ */ new Set();
|
|
let fetchControllers = /* @__PURE__ */ new Map();
|
|
let incrementingLoadId = 0;
|
|
let pendingNavigationLoadId = -1;
|
|
let fetchReloadIds = /* @__PURE__ */ new Map();
|
|
let fetchRedirectIds = /* @__PURE__ */ new Set();
|
|
let fetchLoadMatches = /* @__PURE__ */ new Map();
|
|
let activeFetchers = /* @__PURE__ */ new Map();
|
|
let fetchersQueuedForDeletion = /* @__PURE__ */ new Set();
|
|
let blockerFunctions = /* @__PURE__ */ new Map();
|
|
let unblockBlockerHistoryUpdate = void 0;
|
|
let pendingRevalidationDfd = null;
|
|
function initialize() {
|
|
unlistenHistory = init.history.listen(
|
|
({ action: historyAction, location, delta }) => {
|
|
if (unblockBlockerHistoryUpdate) {
|
|
unblockBlockerHistoryUpdate();
|
|
unblockBlockerHistoryUpdate = void 0;
|
|
return;
|
|
}
|
|
warning(
|
|
blockerFunctions.size === 0 || delta != null,
|
|
"You are trying to use a blocker on a POP navigation to a location that was not created by @remix-run/router. This will fail silently in production. This can happen if you are navigating outside the router via `window.history.pushState`/`window.location.hash` instead of using router navigation APIs. This can also happen if you are using createHashRouter and the user manually changes the URL."
|
|
);
|
|
let blockerKey = shouldBlockNavigation({
|
|
currentLocation: state.location,
|
|
nextLocation: location,
|
|
historyAction
|
|
});
|
|
if (blockerKey && delta != null) {
|
|
let nextHistoryUpdatePromise = new Promise((resolve) => {
|
|
unblockBlockerHistoryUpdate = resolve;
|
|
});
|
|
init.history.go(delta * -1);
|
|
updateBlocker(blockerKey, {
|
|
state: "blocked",
|
|
location,
|
|
proceed() {
|
|
updateBlocker(blockerKey, {
|
|
state: "proceeding",
|
|
proceed: void 0,
|
|
reset: void 0,
|
|
location
|
|
});
|
|
nextHistoryUpdatePromise.then(() => init.history.go(delta));
|
|
},
|
|
reset() {
|
|
let blockers = new Map(state.blockers);
|
|
blockers.set(blockerKey, IDLE_BLOCKER);
|
|
updateState({ blockers });
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
return startNavigation(historyAction, location);
|
|
}
|
|
);
|
|
if (isBrowser) {
|
|
restoreAppliedTransitions(routerWindow, appliedViewTransitions);
|
|
let _saveAppliedTransitions = () => persistAppliedTransitions(routerWindow, appliedViewTransitions);
|
|
routerWindow.addEventListener("pagehide", _saveAppliedTransitions);
|
|
removePageHideEventListener = () => routerWindow.removeEventListener("pagehide", _saveAppliedTransitions);
|
|
}
|
|
if (!state.initialized) {
|
|
startNavigation("POP" /* Pop */, state.location, {
|
|
initialHydration: true
|
|
});
|
|
}
|
|
return router;
|
|
}
|
|
function dispose() {
|
|
if (unlistenHistory) {
|
|
unlistenHistory();
|
|
}
|
|
if (removePageHideEventListener) {
|
|
removePageHideEventListener();
|
|
}
|
|
subscribers.clear();
|
|
pendingNavigationController && pendingNavigationController.abort();
|
|
state.fetchers.forEach((_, key) => deleteFetcher(key));
|
|
state.blockers.forEach((_, key) => deleteBlocker(key));
|
|
}
|
|
function subscribe(fn) {
|
|
subscribers.add(fn);
|
|
return () => subscribers.delete(fn);
|
|
}
|
|
function updateState(newState, opts = {}) {
|
|
if (newState.matches) {
|
|
newState.matches = newState.matches.map((m) => {
|
|
let route = manifest[m.route.id];
|
|
let matchRoute = m.route;
|
|
if (matchRoute.element !== route.element || matchRoute.errorElement !== route.errorElement || matchRoute.hydrateFallbackElement !== route.hydrateFallbackElement) {
|
|
return {
|
|
...m,
|
|
route
|
|
};
|
|
}
|
|
return m;
|
|
});
|
|
}
|
|
state = {
|
|
...state,
|
|
...newState
|
|
};
|
|
let unmountedFetchers = [];
|
|
let mountedFetchers = [];
|
|
state.fetchers.forEach((fetcher, key) => {
|
|
if (fetcher.state === "idle") {
|
|
if (fetchersQueuedForDeletion.has(key)) {
|
|
unmountedFetchers.push(key);
|
|
} else {
|
|
mountedFetchers.push(key);
|
|
}
|
|
}
|
|
});
|
|
fetchersQueuedForDeletion.forEach((key) => {
|
|
if (!state.fetchers.has(key) && !fetchControllers.has(key)) {
|
|
unmountedFetchers.push(key);
|
|
}
|
|
});
|
|
[...subscribers].forEach(
|
|
(subscriber) => subscriber(state, {
|
|
deletedFetchers: unmountedFetchers,
|
|
viewTransitionOpts: opts.viewTransitionOpts,
|
|
flushSync: opts.flushSync === true
|
|
})
|
|
);
|
|
unmountedFetchers.forEach((key) => deleteFetcher(key));
|
|
mountedFetchers.forEach((key) => state.fetchers.delete(key));
|
|
}
|
|
function completeNavigation(location, newState, { flushSync } = {}) {
|
|
let isActionReload = state.actionData != null && state.navigation.formMethod != null && isMutationMethod(state.navigation.formMethod) && state.navigation.state === "loading" && _optionalChain([location, 'access', _5 => _5.state, 'optionalAccess', _6 => _6._isRedirect]) !== true;
|
|
let actionData;
|
|
if (newState.actionData) {
|
|
if (Object.keys(newState.actionData).length > 0) {
|
|
actionData = newState.actionData;
|
|
} else {
|
|
actionData = null;
|
|
}
|
|
} else if (isActionReload) {
|
|
actionData = state.actionData;
|
|
} else {
|
|
actionData = null;
|
|
}
|
|
let loaderData = newState.loaderData ? mergeLoaderData(
|
|
state.loaderData,
|
|
newState.loaderData,
|
|
newState.matches || [],
|
|
newState.errors
|
|
) : state.loaderData;
|
|
let blockers = state.blockers;
|
|
if (blockers.size > 0) {
|
|
blockers = new Map(blockers);
|
|
blockers.forEach((_, k) => blockers.set(k, IDLE_BLOCKER));
|
|
}
|
|
let restoreScrollPosition = isUninterruptedRevalidation ? false : getSavedScrollPosition(location, newState.matches || state.matches);
|
|
let preventScrollReset = pendingPreventScrollReset === true || state.navigation.formMethod != null && isMutationMethod(state.navigation.formMethod) && _optionalChain([location, 'access', _7 => _7.state, 'optionalAccess', _8 => _8._isRedirect]) !== true;
|
|
if (inFlightDataRoutes) {
|
|
dataRoutes = inFlightDataRoutes;
|
|
inFlightDataRoutes = void 0;
|
|
}
|
|
if (isUninterruptedRevalidation) {
|
|
} else if (pendingAction === "POP" /* Pop */) {
|
|
} else if (pendingAction === "PUSH" /* Push */) {
|
|
init.history.push(location, location.state);
|
|
} else if (pendingAction === "REPLACE" /* Replace */) {
|
|
init.history.replace(location, location.state);
|
|
}
|
|
let viewTransitionOpts;
|
|
if (pendingAction === "POP" /* Pop */) {
|
|
let priorPaths = appliedViewTransitions.get(state.location.pathname);
|
|
if (priorPaths && priorPaths.has(location.pathname)) {
|
|
viewTransitionOpts = {
|
|
currentLocation: state.location,
|
|
nextLocation: location
|
|
};
|
|
} else if (appliedViewTransitions.has(location.pathname)) {
|
|
viewTransitionOpts = {
|
|
currentLocation: location,
|
|
nextLocation: state.location
|
|
};
|
|
}
|
|
} else if (pendingViewTransitionEnabled) {
|
|
let toPaths = appliedViewTransitions.get(state.location.pathname);
|
|
if (toPaths) {
|
|
toPaths.add(location.pathname);
|
|
} else {
|
|
toPaths = /* @__PURE__ */ new Set([location.pathname]);
|
|
appliedViewTransitions.set(state.location.pathname, toPaths);
|
|
}
|
|
viewTransitionOpts = {
|
|
currentLocation: state.location,
|
|
nextLocation: location
|
|
};
|
|
}
|
|
updateState(
|
|
{
|
|
...newState,
|
|
// matches, errors, fetchers go through as-is
|
|
actionData,
|
|
loaderData,
|
|
historyAction: pendingAction,
|
|
location,
|
|
initialized: true,
|
|
navigation: IDLE_NAVIGATION,
|
|
revalidation: "idle",
|
|
restoreScrollPosition,
|
|
preventScrollReset,
|
|
blockers
|
|
},
|
|
{
|
|
viewTransitionOpts,
|
|
flushSync: flushSync === true
|
|
}
|
|
);
|
|
pendingAction = "POP" /* Pop */;
|
|
pendingPreventScrollReset = false;
|
|
pendingViewTransitionEnabled = false;
|
|
isUninterruptedRevalidation = false;
|
|
isRevalidationRequired = false;
|
|
_optionalChain([pendingRevalidationDfd, 'optionalAccess', _9 => _9.resolve, 'call', _10 => _10()]);
|
|
pendingRevalidationDfd = null;
|
|
}
|
|
async function navigate(to, opts) {
|
|
if (typeof to === "number") {
|
|
init.history.go(to);
|
|
return;
|
|
}
|
|
let normalizedPath = normalizeTo(
|
|
state.location,
|
|
state.matches,
|
|
basename,
|
|
to,
|
|
_optionalChain([opts, 'optionalAccess', _11 => _11.fromRouteId]),
|
|
_optionalChain([opts, 'optionalAccess', _12 => _12.relative])
|
|
);
|
|
let { path, submission, error } = normalizeNavigateOptions(
|
|
false,
|
|
normalizedPath,
|
|
opts
|
|
);
|
|
let currentLocation = state.location;
|
|
let nextLocation = createLocation(state.location, path, opts && opts.state);
|
|
nextLocation = {
|
|
...nextLocation,
|
|
...init.history.encodeLocation(nextLocation)
|
|
};
|
|
let userReplace = opts && opts.replace != null ? opts.replace : void 0;
|
|
let historyAction = "PUSH" /* Push */;
|
|
if (userReplace === true) {
|
|
historyAction = "REPLACE" /* Replace */;
|
|
} else if (userReplace === false) {
|
|
} else if (submission != null && isMutationMethod(submission.formMethod) && submission.formAction === state.location.pathname + state.location.search) {
|
|
historyAction = "REPLACE" /* Replace */;
|
|
}
|
|
let preventScrollReset = opts && "preventScrollReset" in opts ? opts.preventScrollReset === true : void 0;
|
|
let flushSync = (opts && opts.flushSync) === true;
|
|
let blockerKey = shouldBlockNavigation({
|
|
currentLocation,
|
|
nextLocation,
|
|
historyAction
|
|
});
|
|
if (blockerKey) {
|
|
updateBlocker(blockerKey, {
|
|
state: "blocked",
|
|
location: nextLocation,
|
|
proceed() {
|
|
updateBlocker(blockerKey, {
|
|
state: "proceeding",
|
|
proceed: void 0,
|
|
reset: void 0,
|
|
location: nextLocation
|
|
});
|
|
navigate(to, opts);
|
|
},
|
|
reset() {
|
|
let blockers = new Map(state.blockers);
|
|
blockers.set(blockerKey, IDLE_BLOCKER);
|
|
updateState({ blockers });
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
await startNavigation(historyAction, nextLocation, {
|
|
submission,
|
|
// Send through the formData serialization error if we have one so we can
|
|
// render at the right error boundary after we match routes
|
|
pendingError: error,
|
|
preventScrollReset,
|
|
replace: opts && opts.replace,
|
|
enableViewTransition: opts && opts.viewTransition,
|
|
flushSync
|
|
});
|
|
}
|
|
function revalidate() {
|
|
if (!pendingRevalidationDfd) {
|
|
pendingRevalidationDfd = createDeferred();
|
|
}
|
|
interruptActiveLoads();
|
|
updateState({ revalidation: "loading" });
|
|
let promise = pendingRevalidationDfd.promise;
|
|
if (state.navigation.state === "submitting") {
|
|
return promise;
|
|
}
|
|
if (state.navigation.state === "idle") {
|
|
startNavigation(state.historyAction, state.location, {
|
|
startUninterruptedRevalidation: true
|
|
});
|
|
return promise;
|
|
}
|
|
startNavigation(
|
|
pendingAction || state.historyAction,
|
|
state.navigation.location,
|
|
{
|
|
overrideNavigation: state.navigation,
|
|
// Proxy through any rending view transition
|
|
enableViewTransition: pendingViewTransitionEnabled === true
|
|
}
|
|
);
|
|
return promise;
|
|
}
|
|
async function startNavigation(historyAction, location, opts) {
|
|
pendingNavigationController && pendingNavigationController.abort();
|
|
pendingNavigationController = null;
|
|
pendingAction = historyAction;
|
|
isUninterruptedRevalidation = (opts && opts.startUninterruptedRevalidation) === true;
|
|
saveScrollPosition(state.location, state.matches);
|
|
pendingPreventScrollReset = (opts && opts.preventScrollReset) === true;
|
|
pendingViewTransitionEnabled = (opts && opts.enableViewTransition) === true;
|
|
let routesToUse = inFlightDataRoutes || dataRoutes;
|
|
let loadingNavigation = opts && opts.overrideNavigation;
|
|
let matches = _optionalChain([opts, 'optionalAccess', _13 => _13.initialHydration]) && state.matches && state.matches.length > 0 && !initialMatchesIsFOW ? (
|
|
// `matchRoutes()` has already been called if we're in here via `router.initialize()`
|
|
state.matches
|
|
) : matchRoutes(routesToUse, location, basename);
|
|
let flushSync = (opts && opts.flushSync) === true;
|
|
if (matches && state.initialized && !isRevalidationRequired && isHashChangeOnly(state.location, location) && !(opts && opts.submission && isMutationMethod(opts.submission.formMethod))) {
|
|
completeNavigation(location, { matches }, { flushSync });
|
|
return;
|
|
}
|
|
let fogOfWar = checkFogOfWar(matches, routesToUse, location.pathname);
|
|
if (fogOfWar.active && fogOfWar.matches) {
|
|
matches = fogOfWar.matches;
|
|
}
|
|
if (!matches) {
|
|
let { error, notFoundMatches, route } = handleNavigational404(
|
|
location.pathname
|
|
);
|
|
completeNavigation(
|
|
location,
|
|
{
|
|
matches: notFoundMatches,
|
|
loaderData: {},
|
|
errors: {
|
|
[route.id]: error
|
|
}
|
|
},
|
|
{ flushSync }
|
|
);
|
|
return;
|
|
}
|
|
pendingNavigationController = new AbortController();
|
|
let request = createClientSideRequest(
|
|
init.history,
|
|
location,
|
|
pendingNavigationController.signal,
|
|
opts && opts.submission
|
|
);
|
|
let scopedContext = new unstable_RouterContextProvider(
|
|
init.unstable_getContext ? await init.unstable_getContext() : void 0
|
|
);
|
|
let pendingActionResult;
|
|
if (opts && opts.pendingError) {
|
|
pendingActionResult = [
|
|
findNearestBoundary(matches).route.id,
|
|
{ type: "error" /* error */, error: opts.pendingError }
|
|
];
|
|
} else if (opts && opts.submission && isMutationMethod(opts.submission.formMethod)) {
|
|
let actionResult = await handleAction(
|
|
request,
|
|
location,
|
|
opts.submission,
|
|
matches,
|
|
scopedContext,
|
|
fogOfWar.active,
|
|
opts && opts.initialHydration === true,
|
|
{ replace: opts.replace, flushSync }
|
|
);
|
|
if (actionResult.shortCircuited) {
|
|
return;
|
|
}
|
|
if (actionResult.pendingActionResult) {
|
|
let [routeId, result] = actionResult.pendingActionResult;
|
|
if (isErrorResult(result) && isRouteErrorResponse(result.error) && result.error.status === 404) {
|
|
pendingNavigationController = null;
|
|
completeNavigation(location, {
|
|
matches: actionResult.matches,
|
|
loaderData: {},
|
|
errors: {
|
|
[routeId]: result.error
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
matches = actionResult.matches || matches;
|
|
pendingActionResult = actionResult.pendingActionResult;
|
|
loadingNavigation = getLoadingNavigation(location, opts.submission);
|
|
flushSync = false;
|
|
fogOfWar.active = false;
|
|
request = createClientSideRequest(
|
|
init.history,
|
|
request.url,
|
|
request.signal
|
|
);
|
|
}
|
|
let {
|
|
shortCircuited,
|
|
matches: updatedMatches,
|
|
loaderData,
|
|
errors
|
|
} = await handleLoaders(
|
|
request,
|
|
location,
|
|
matches,
|
|
scopedContext,
|
|
fogOfWar.active,
|
|
loadingNavigation,
|
|
opts && opts.submission,
|
|
opts && opts.fetcherSubmission,
|
|
opts && opts.replace,
|
|
opts && opts.initialHydration === true,
|
|
flushSync,
|
|
pendingActionResult
|
|
);
|
|
if (shortCircuited) {
|
|
return;
|
|
}
|
|
pendingNavigationController = null;
|
|
completeNavigation(location, {
|
|
matches: updatedMatches || matches,
|
|
...getActionDataForCommit(pendingActionResult),
|
|
loaderData,
|
|
errors
|
|
});
|
|
}
|
|
async function handleAction(request, location, submission, matches, scopedContext, isFogOfWar, initialHydration, opts = {}) {
|
|
interruptActiveLoads();
|
|
let navigation = getSubmittingNavigation(location, submission);
|
|
updateState({ navigation }, { flushSync: opts.flushSync === true });
|
|
if (isFogOfWar) {
|
|
let discoverResult = await discoverRoutes(
|
|
matches,
|
|
location.pathname,
|
|
request.signal
|
|
);
|
|
if (discoverResult.type === "aborted") {
|
|
return { shortCircuited: true };
|
|
} else if (discoverResult.type === "error") {
|
|
let boundaryId = findNearestBoundary(discoverResult.partialMatches).route.id;
|
|
return {
|
|
matches: discoverResult.partialMatches,
|
|
pendingActionResult: [
|
|
boundaryId,
|
|
{
|
|
type: "error" /* error */,
|
|
error: discoverResult.error
|
|
}
|
|
]
|
|
};
|
|
} else if (!discoverResult.matches) {
|
|
let { notFoundMatches, error, route } = handleNavigational404(
|
|
location.pathname
|
|
);
|
|
return {
|
|
matches: notFoundMatches,
|
|
pendingActionResult: [
|
|
route.id,
|
|
{
|
|
type: "error" /* error */,
|
|
error
|
|
}
|
|
]
|
|
};
|
|
} else {
|
|
matches = discoverResult.matches;
|
|
}
|
|
}
|
|
let result;
|
|
let actionMatch = getTargetMatch(matches, location);
|
|
if (!actionMatch.route.action && !actionMatch.route.lazy) {
|
|
result = {
|
|
type: "error" /* error */,
|
|
error: getInternalRouterError(405, {
|
|
method: request.method,
|
|
pathname: location.pathname,
|
|
routeId: actionMatch.route.id
|
|
})
|
|
};
|
|
} else {
|
|
let dsMatches = getTargetedDataStrategyMatches(
|
|
mapRouteProperties,
|
|
manifest,
|
|
request,
|
|
matches,
|
|
actionMatch,
|
|
initialHydration ? [] : hydrationRouteProperties,
|
|
scopedContext
|
|
);
|
|
let results = await callDataStrategy(
|
|
request,
|
|
dsMatches,
|
|
scopedContext,
|
|
null
|
|
);
|
|
result = results[actionMatch.route.id];
|
|
if (!result) {
|
|
for (let match of matches) {
|
|
if (results[match.route.id]) {
|
|
result = results[match.route.id];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (request.signal.aborted) {
|
|
return { shortCircuited: true };
|
|
}
|
|
}
|
|
if (isRedirectResult(result)) {
|
|
let replace2;
|
|
if (opts && opts.replace != null) {
|
|
replace2 = opts.replace;
|
|
} else {
|
|
let location2 = normalizeRedirectLocation(
|
|
result.response.headers.get("Location"),
|
|
new URL(request.url),
|
|
basename
|
|
);
|
|
replace2 = location2 === state.location.pathname + state.location.search;
|
|
}
|
|
await startRedirectNavigation(request, result, true, {
|
|
submission,
|
|
replace: replace2
|
|
});
|
|
return { shortCircuited: true };
|
|
}
|
|
if (isErrorResult(result)) {
|
|
let boundaryMatch = findNearestBoundary(matches, actionMatch.route.id);
|
|
if ((opts && opts.replace) !== true) {
|
|
pendingAction = "PUSH" /* Push */;
|
|
}
|
|
return {
|
|
matches,
|
|
pendingActionResult: [
|
|
boundaryMatch.route.id,
|
|
result,
|
|
actionMatch.route.id
|
|
]
|
|
};
|
|
}
|
|
return {
|
|
matches,
|
|
pendingActionResult: [actionMatch.route.id, result]
|
|
};
|
|
}
|
|
async function handleLoaders(request, location, matches, scopedContext, isFogOfWar, overrideNavigation, submission, fetcherSubmission, replace2, initialHydration, flushSync, pendingActionResult) {
|
|
let loadingNavigation = overrideNavigation || getLoadingNavigation(location, submission);
|
|
let activeSubmission = submission || fetcherSubmission || getSubmissionFromNavigation(loadingNavigation);
|
|
let shouldUpdateNavigationState = !isUninterruptedRevalidation && !initialHydration;
|
|
if (isFogOfWar) {
|
|
if (shouldUpdateNavigationState) {
|
|
let actionData = getUpdatedActionData(pendingActionResult);
|
|
updateState(
|
|
{
|
|
navigation: loadingNavigation,
|
|
...actionData !== void 0 ? { actionData } : {}
|
|
},
|
|
{
|
|
flushSync
|
|
}
|
|
);
|
|
}
|
|
let discoverResult = await discoverRoutes(
|
|
matches,
|
|
location.pathname,
|
|
request.signal
|
|
);
|
|
if (discoverResult.type === "aborted") {
|
|
return { shortCircuited: true };
|
|
} else if (discoverResult.type === "error") {
|
|
let boundaryId = findNearestBoundary(discoverResult.partialMatches).route.id;
|
|
return {
|
|
matches: discoverResult.partialMatches,
|
|
loaderData: {},
|
|
errors: {
|
|
[boundaryId]: discoverResult.error
|
|
}
|
|
};
|
|
} else if (!discoverResult.matches) {
|
|
let { error, notFoundMatches, route } = handleNavigational404(
|
|
location.pathname
|
|
);
|
|
return {
|
|
matches: notFoundMatches,
|
|
loaderData: {},
|
|
errors: {
|
|
[route.id]: error
|
|
}
|
|
};
|
|
} else {
|
|
matches = discoverResult.matches;
|
|
}
|
|
}
|
|
let routesToUse = inFlightDataRoutes || dataRoutes;
|
|
let { dsMatches, revalidatingFetchers } = getMatchesToLoad(
|
|
request,
|
|
scopedContext,
|
|
mapRouteProperties,
|
|
manifest,
|
|
init.history,
|
|
state,
|
|
matches,
|
|
activeSubmission,
|
|
location,
|
|
initialHydration ? [] : hydrationRouteProperties,
|
|
initialHydration === true,
|
|
isRevalidationRequired,
|
|
cancelledFetcherLoads,
|
|
fetchersQueuedForDeletion,
|
|
fetchLoadMatches,
|
|
fetchRedirectIds,
|
|
routesToUse,
|
|
basename,
|
|
init.patchRoutesOnNavigation != null,
|
|
pendingActionResult
|
|
);
|
|
pendingNavigationLoadId = ++incrementingLoadId;
|
|
if (!init.dataStrategy && !dsMatches.some((m) => m.shouldLoad) && revalidatingFetchers.length === 0) {
|
|
let updatedFetchers2 = markFetchRedirectsDone();
|
|
completeNavigation(
|
|
location,
|
|
{
|
|
matches,
|
|
loaderData: {},
|
|
// Commit pending error if we're short circuiting
|
|
errors: pendingActionResult && isErrorResult(pendingActionResult[1]) ? { [pendingActionResult[0]]: pendingActionResult[1].error } : null,
|
|
...getActionDataForCommit(pendingActionResult),
|
|
...updatedFetchers2 ? { fetchers: new Map(state.fetchers) } : {}
|
|
},
|
|
{ flushSync }
|
|
);
|
|
return { shortCircuited: true };
|
|
}
|
|
if (shouldUpdateNavigationState) {
|
|
let updates = {};
|
|
if (!isFogOfWar) {
|
|
updates.navigation = loadingNavigation;
|
|
let actionData = getUpdatedActionData(pendingActionResult);
|
|
if (actionData !== void 0) {
|
|
updates.actionData = actionData;
|
|
}
|
|
}
|
|
if (revalidatingFetchers.length > 0) {
|
|
updates.fetchers = getUpdatedRevalidatingFetchers(revalidatingFetchers);
|
|
}
|
|
updateState(updates, { flushSync });
|
|
}
|
|
revalidatingFetchers.forEach((rf) => {
|
|
abortFetcher(rf.key);
|
|
if (rf.controller) {
|
|
fetchControllers.set(rf.key, rf.controller);
|
|
}
|
|
});
|
|
let abortPendingFetchRevalidations = () => revalidatingFetchers.forEach((f) => abortFetcher(f.key));
|
|
if (pendingNavigationController) {
|
|
pendingNavigationController.signal.addEventListener(
|
|
"abort",
|
|
abortPendingFetchRevalidations
|
|
);
|
|
}
|
|
let { loaderResults, fetcherResults } = await callLoadersAndMaybeResolveData(
|
|
dsMatches,
|
|
revalidatingFetchers,
|
|
request,
|
|
scopedContext
|
|
);
|
|
if (request.signal.aborted) {
|
|
return { shortCircuited: true };
|
|
}
|
|
if (pendingNavigationController) {
|
|
pendingNavigationController.signal.removeEventListener(
|
|
"abort",
|
|
abortPendingFetchRevalidations
|
|
);
|
|
}
|
|
revalidatingFetchers.forEach((rf) => fetchControllers.delete(rf.key));
|
|
let redirect2 = findRedirect(loaderResults);
|
|
if (redirect2) {
|
|
await startRedirectNavigation(request, redirect2.result, true, {
|
|
replace: replace2
|
|
});
|
|
return { shortCircuited: true };
|
|
}
|
|
redirect2 = findRedirect(fetcherResults);
|
|
if (redirect2) {
|
|
fetchRedirectIds.add(redirect2.key);
|
|
await startRedirectNavigation(request, redirect2.result, true, {
|
|
replace: replace2
|
|
});
|
|
return { shortCircuited: true };
|
|
}
|
|
let { loaderData, errors } = processLoaderData(
|
|
state,
|
|
matches,
|
|
loaderResults,
|
|
pendingActionResult,
|
|
revalidatingFetchers,
|
|
fetcherResults
|
|
);
|
|
if (initialHydration && state.errors) {
|
|
errors = { ...state.errors, ...errors };
|
|
}
|
|
let updatedFetchers = markFetchRedirectsDone();
|
|
let didAbortFetchLoads = abortStaleFetchLoads(pendingNavigationLoadId);
|
|
let shouldUpdateFetchers = updatedFetchers || didAbortFetchLoads || revalidatingFetchers.length > 0;
|
|
return {
|
|
matches,
|
|
loaderData,
|
|
errors,
|
|
...shouldUpdateFetchers ? { fetchers: new Map(state.fetchers) } : {}
|
|
};
|
|
}
|
|
function getUpdatedActionData(pendingActionResult) {
|
|
if (pendingActionResult && !isErrorResult(pendingActionResult[1])) {
|
|
return {
|
|
[pendingActionResult[0]]: pendingActionResult[1].data
|
|
};
|
|
} else if (state.actionData) {
|
|
if (Object.keys(state.actionData).length === 0) {
|
|
return null;
|
|
} else {
|
|
return state.actionData;
|
|
}
|
|
}
|
|
}
|
|
function getUpdatedRevalidatingFetchers(revalidatingFetchers) {
|
|
revalidatingFetchers.forEach((rf) => {
|
|
let fetcher = state.fetchers.get(rf.key);
|
|
let revalidatingFetcher = getLoadingFetcher(
|
|
void 0,
|
|
fetcher ? fetcher.data : void 0
|
|
);
|
|
state.fetchers.set(rf.key, revalidatingFetcher);
|
|
});
|
|
return new Map(state.fetchers);
|
|
}
|
|
async function fetch2(key, routeId, href, opts) {
|
|
abortFetcher(key);
|
|
let flushSync = (opts && opts.flushSync) === true;
|
|
let routesToUse = inFlightDataRoutes || dataRoutes;
|
|
let normalizedPath = normalizeTo(
|
|
state.location,
|
|
state.matches,
|
|
basename,
|
|
href,
|
|
routeId,
|
|
_optionalChain([opts, 'optionalAccess', _14 => _14.relative])
|
|
);
|
|
let matches = matchRoutes(routesToUse, normalizedPath, basename);
|
|
let fogOfWar = checkFogOfWar(matches, routesToUse, normalizedPath);
|
|
if (fogOfWar.active && fogOfWar.matches) {
|
|
matches = fogOfWar.matches;
|
|
}
|
|
if (!matches) {
|
|
setFetcherError(
|
|
key,
|
|
routeId,
|
|
getInternalRouterError(404, { pathname: normalizedPath }),
|
|
{ flushSync }
|
|
);
|
|
return;
|
|
}
|
|
let { path, submission, error } = normalizeNavigateOptions(
|
|
true,
|
|
normalizedPath,
|
|
opts
|
|
);
|
|
if (error) {
|
|
setFetcherError(key, routeId, error, { flushSync });
|
|
return;
|
|
}
|
|
let scopedContext = new unstable_RouterContextProvider(
|
|
init.unstable_getContext ? await init.unstable_getContext() : void 0
|
|
);
|
|
let preventScrollReset = (opts && opts.preventScrollReset) === true;
|
|
if (submission && isMutationMethod(submission.formMethod)) {
|
|
await handleFetcherAction(
|
|
key,
|
|
routeId,
|
|
path,
|
|
matches,
|
|
scopedContext,
|
|
fogOfWar.active,
|
|
flushSync,
|
|
preventScrollReset,
|
|
submission
|
|
);
|
|
return;
|
|
}
|
|
fetchLoadMatches.set(key, { routeId, path });
|
|
await handleFetcherLoader(
|
|
key,
|
|
routeId,
|
|
path,
|
|
matches,
|
|
scopedContext,
|
|
fogOfWar.active,
|
|
flushSync,
|
|
preventScrollReset,
|
|
submission
|
|
);
|
|
}
|
|
async function handleFetcherAction(key, routeId, path, requestMatches, scopedContext, isFogOfWar, flushSync, preventScrollReset, submission) {
|
|
interruptActiveLoads();
|
|
fetchLoadMatches.delete(key);
|
|
let existingFetcher = state.fetchers.get(key);
|
|
updateFetcherState(key, getSubmittingFetcher(submission, existingFetcher), {
|
|
flushSync
|
|
});
|
|
let abortController = new AbortController();
|
|
let fetchRequest = createClientSideRequest(
|
|
init.history,
|
|
path,
|
|
abortController.signal,
|
|
submission
|
|
);
|
|
if (isFogOfWar) {
|
|
let discoverResult = await discoverRoutes(
|
|
requestMatches,
|
|
new URL(fetchRequest.url).pathname,
|
|
fetchRequest.signal,
|
|
key
|
|
);
|
|
if (discoverResult.type === "aborted") {
|
|
return;
|
|
} else if (discoverResult.type === "error") {
|
|
setFetcherError(key, routeId, discoverResult.error, { flushSync });
|
|
return;
|
|
} else if (!discoverResult.matches) {
|
|
setFetcherError(
|
|
key,
|
|
routeId,
|
|
getInternalRouterError(404, { pathname: path }),
|
|
{ flushSync }
|
|
);
|
|
return;
|
|
} else {
|
|
requestMatches = discoverResult.matches;
|
|
}
|
|
}
|
|
let match = getTargetMatch(requestMatches, path);
|
|
if (!match.route.action && !match.route.lazy) {
|
|
let error = getInternalRouterError(405, {
|
|
method: submission.formMethod,
|
|
pathname: path,
|
|
routeId
|
|
});
|
|
setFetcherError(key, routeId, error, { flushSync });
|
|
return;
|
|
}
|
|
fetchControllers.set(key, abortController);
|
|
let originatingLoadId = incrementingLoadId;
|
|
let fetchMatches = getTargetedDataStrategyMatches(
|
|
mapRouteProperties,
|
|
manifest,
|
|
fetchRequest,
|
|
requestMatches,
|
|
match,
|
|
hydrationRouteProperties,
|
|
scopedContext
|
|
);
|
|
let actionResults = await callDataStrategy(
|
|
fetchRequest,
|
|
fetchMatches,
|
|
scopedContext,
|
|
key
|
|
);
|
|
let actionResult = actionResults[match.route.id];
|
|
if (fetchRequest.signal.aborted) {
|
|
if (fetchControllers.get(key) === abortController) {
|
|
fetchControllers.delete(key);
|
|
}
|
|
return;
|
|
}
|
|
if (fetchersQueuedForDeletion.has(key)) {
|
|
if (isRedirectResult(actionResult) || isErrorResult(actionResult)) {
|
|
updateFetcherState(key, getDoneFetcher(void 0));
|
|
return;
|
|
}
|
|
} else {
|
|
if (isRedirectResult(actionResult)) {
|
|
fetchControllers.delete(key);
|
|
if (pendingNavigationLoadId > originatingLoadId) {
|
|
updateFetcherState(key, getDoneFetcher(void 0));
|
|
return;
|
|
} else {
|
|
fetchRedirectIds.add(key);
|
|
updateFetcherState(key, getLoadingFetcher(submission));
|
|
return startRedirectNavigation(fetchRequest, actionResult, false, {
|
|
fetcherSubmission: submission,
|
|
preventScrollReset
|
|
});
|
|
}
|
|
}
|
|
if (isErrorResult(actionResult)) {
|
|
setFetcherError(key, routeId, actionResult.error);
|
|
return;
|
|
}
|
|
}
|
|
let nextLocation = state.navigation.location || state.location;
|
|
let revalidationRequest = createClientSideRequest(
|
|
init.history,
|
|
nextLocation,
|
|
abortController.signal
|
|
);
|
|
let routesToUse = inFlightDataRoutes || dataRoutes;
|
|
let matches = state.navigation.state !== "idle" ? matchRoutes(routesToUse, state.navigation.location, basename) : state.matches;
|
|
invariant(matches, "Didn't find any matches after fetcher action");
|
|
let loadId = ++incrementingLoadId;
|
|
fetchReloadIds.set(key, loadId);
|
|
let loadFetcher = getLoadingFetcher(submission, actionResult.data);
|
|
state.fetchers.set(key, loadFetcher);
|
|
let { dsMatches, revalidatingFetchers } = getMatchesToLoad(
|
|
revalidationRequest,
|
|
scopedContext,
|
|
mapRouteProperties,
|
|
manifest,
|
|
init.history,
|
|
state,
|
|
matches,
|
|
submission,
|
|
nextLocation,
|
|
hydrationRouteProperties,
|
|
false,
|
|
isRevalidationRequired,
|
|
cancelledFetcherLoads,
|
|
fetchersQueuedForDeletion,
|
|
fetchLoadMatches,
|
|
fetchRedirectIds,
|
|
routesToUse,
|
|
basename,
|
|
init.patchRoutesOnNavigation != null,
|
|
[match.route.id, actionResult]
|
|
);
|
|
revalidatingFetchers.filter((rf) => rf.key !== key).forEach((rf) => {
|
|
let staleKey = rf.key;
|
|
let existingFetcher2 = state.fetchers.get(staleKey);
|
|
let revalidatingFetcher = getLoadingFetcher(
|
|
void 0,
|
|
existingFetcher2 ? existingFetcher2.data : void 0
|
|
);
|
|
state.fetchers.set(staleKey, revalidatingFetcher);
|
|
abortFetcher(staleKey);
|
|
if (rf.controller) {
|
|
fetchControllers.set(staleKey, rf.controller);
|
|
}
|
|
});
|
|
updateState({ fetchers: new Map(state.fetchers) });
|
|
let abortPendingFetchRevalidations = () => revalidatingFetchers.forEach((rf) => abortFetcher(rf.key));
|
|
abortController.signal.addEventListener(
|
|
"abort",
|
|
abortPendingFetchRevalidations
|
|
);
|
|
let { loaderResults, fetcherResults } = await callLoadersAndMaybeResolveData(
|
|
dsMatches,
|
|
revalidatingFetchers,
|
|
revalidationRequest,
|
|
scopedContext
|
|
);
|
|
if (abortController.signal.aborted) {
|
|
return;
|
|
}
|
|
abortController.signal.removeEventListener(
|
|
"abort",
|
|
abortPendingFetchRevalidations
|
|
);
|
|
fetchReloadIds.delete(key);
|
|
fetchControllers.delete(key);
|
|
revalidatingFetchers.forEach((r) => fetchControllers.delete(r.key));
|
|
if (state.fetchers.has(key)) {
|
|
let doneFetcher = getDoneFetcher(actionResult.data);
|
|
state.fetchers.set(key, doneFetcher);
|
|
}
|
|
let redirect2 = findRedirect(loaderResults);
|
|
if (redirect2) {
|
|
return startRedirectNavigation(
|
|
revalidationRequest,
|
|
redirect2.result,
|
|
false,
|
|
{ preventScrollReset }
|
|
);
|
|
}
|
|
redirect2 = findRedirect(fetcherResults);
|
|
if (redirect2) {
|
|
fetchRedirectIds.add(redirect2.key);
|
|
return startRedirectNavigation(
|
|
revalidationRequest,
|
|
redirect2.result,
|
|
false,
|
|
{ preventScrollReset }
|
|
);
|
|
}
|
|
let { loaderData, errors } = processLoaderData(
|
|
state,
|
|
matches,
|
|
loaderResults,
|
|
void 0,
|
|
revalidatingFetchers,
|
|
fetcherResults
|
|
);
|
|
abortStaleFetchLoads(loadId);
|
|
if (state.navigation.state === "loading" && loadId > pendingNavigationLoadId) {
|
|
invariant(pendingAction, "Expected pending action");
|
|
pendingNavigationController && pendingNavigationController.abort();
|
|
completeNavigation(state.navigation.location, {
|
|
matches,
|
|
loaderData,
|
|
errors,
|
|
fetchers: new Map(state.fetchers)
|
|
});
|
|
} else {
|
|
updateState({
|
|
errors,
|
|
loaderData: mergeLoaderData(
|
|
state.loaderData,
|
|
loaderData,
|
|
matches,
|
|
errors
|
|
),
|
|
fetchers: new Map(state.fetchers)
|
|
});
|
|
isRevalidationRequired = false;
|
|
}
|
|
}
|
|
async function handleFetcherLoader(key, routeId, path, matches, scopedContext, isFogOfWar, flushSync, preventScrollReset, submission) {
|
|
let existingFetcher = state.fetchers.get(key);
|
|
updateFetcherState(
|
|
key,
|
|
getLoadingFetcher(
|
|
submission,
|
|
existingFetcher ? existingFetcher.data : void 0
|
|
),
|
|
{ flushSync }
|
|
);
|
|
let abortController = new AbortController();
|
|
let fetchRequest = createClientSideRequest(
|
|
init.history,
|
|
path,
|
|
abortController.signal
|
|
);
|
|
if (isFogOfWar) {
|
|
let discoverResult = await discoverRoutes(
|
|
matches,
|
|
new URL(fetchRequest.url).pathname,
|
|
fetchRequest.signal,
|
|
key
|
|
);
|
|
if (discoverResult.type === "aborted") {
|
|
return;
|
|
} else if (discoverResult.type === "error") {
|
|
setFetcherError(key, routeId, discoverResult.error, { flushSync });
|
|
return;
|
|
} else if (!discoverResult.matches) {
|
|
setFetcherError(
|
|
key,
|
|
routeId,
|
|
getInternalRouterError(404, { pathname: path }),
|
|
{ flushSync }
|
|
);
|
|
return;
|
|
} else {
|
|
matches = discoverResult.matches;
|
|
}
|
|
}
|
|
let match = getTargetMatch(matches, path);
|
|
fetchControllers.set(key, abortController);
|
|
let originatingLoadId = incrementingLoadId;
|
|
let dsMatches = getTargetedDataStrategyMatches(
|
|
mapRouteProperties,
|
|
manifest,
|
|
fetchRequest,
|
|
matches,
|
|
match,
|
|
hydrationRouteProperties,
|
|
scopedContext
|
|
);
|
|
let results = await callDataStrategy(
|
|
fetchRequest,
|
|
dsMatches,
|
|
scopedContext,
|
|
key
|
|
);
|
|
let result = results[match.route.id];
|
|
if (fetchControllers.get(key) === abortController) {
|
|
fetchControllers.delete(key);
|
|
}
|
|
if (fetchRequest.signal.aborted) {
|
|
return;
|
|
}
|
|
if (fetchersQueuedForDeletion.has(key)) {
|
|
updateFetcherState(key, getDoneFetcher(void 0));
|
|
return;
|
|
}
|
|
if (isRedirectResult(result)) {
|
|
if (pendingNavigationLoadId > originatingLoadId) {
|
|
updateFetcherState(key, getDoneFetcher(void 0));
|
|
return;
|
|
} else {
|
|
fetchRedirectIds.add(key);
|
|
await startRedirectNavigation(fetchRequest, result, false, {
|
|
preventScrollReset
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
if (isErrorResult(result)) {
|
|
setFetcherError(key, routeId, result.error);
|
|
return;
|
|
}
|
|
updateFetcherState(key, getDoneFetcher(result.data));
|
|
}
|
|
async function startRedirectNavigation(request, redirect2, isNavigation, {
|
|
submission,
|
|
fetcherSubmission,
|
|
preventScrollReset,
|
|
replace: replace2
|
|
} = {}) {
|
|
if (redirect2.response.headers.has("X-Remix-Revalidate")) {
|
|
isRevalidationRequired = true;
|
|
}
|
|
let location = redirect2.response.headers.get("Location");
|
|
invariant(location, "Expected a Location header on the redirect Response");
|
|
location = normalizeRedirectLocation(
|
|
location,
|
|
new URL(request.url),
|
|
basename
|
|
);
|
|
let redirectLocation = createLocation(state.location, location, {
|
|
_isRedirect: true
|
|
});
|
|
if (isBrowser) {
|
|
let isDocumentReload = false;
|
|
if (redirect2.response.headers.has("X-Remix-Reload-Document")) {
|
|
isDocumentReload = true;
|
|
} else if (isAbsoluteUrl(location)) {
|
|
const url = createBrowserURLImpl(location, true);
|
|
isDocumentReload = // Hard reload if it's an absolute URL to a new origin
|
|
url.origin !== routerWindow.location.origin || // Hard reload if it's an absolute URL that does not match our basename
|
|
stripBasename(url.pathname, basename) == null;
|
|
}
|
|
if (isDocumentReload) {
|
|
if (replace2) {
|
|
routerWindow.location.replace(location);
|
|
} else {
|
|
routerWindow.location.assign(location);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
pendingNavigationController = null;
|
|
let redirectNavigationType = replace2 === true || redirect2.response.headers.has("X-Remix-Replace") ? "REPLACE" /* Replace */ : "PUSH" /* Push */;
|
|
let { formMethod, formAction, formEncType } = state.navigation;
|
|
if (!submission && !fetcherSubmission && formMethod && formAction && formEncType) {
|
|
submission = getSubmissionFromNavigation(state.navigation);
|
|
}
|
|
let activeSubmission = submission || fetcherSubmission;
|
|
if (redirectPreserveMethodStatusCodes.has(redirect2.response.status) && activeSubmission && isMutationMethod(activeSubmission.formMethod)) {
|
|
await startNavigation(redirectNavigationType, redirectLocation, {
|
|
submission: {
|
|
...activeSubmission,
|
|
formAction: location
|
|
},
|
|
// Preserve these flags across redirects
|
|
preventScrollReset: preventScrollReset || pendingPreventScrollReset,
|
|
enableViewTransition: isNavigation ? pendingViewTransitionEnabled : void 0
|
|
});
|
|
} else {
|
|
let overrideNavigation = getLoadingNavigation(
|
|
redirectLocation,
|
|
submission
|
|
);
|
|
await startNavigation(redirectNavigationType, redirectLocation, {
|
|
overrideNavigation,
|
|
// Send fetcher submissions through for shouldRevalidate
|
|
fetcherSubmission,
|
|
// Preserve these flags across redirects
|
|
preventScrollReset: preventScrollReset || pendingPreventScrollReset,
|
|
enableViewTransition: isNavigation ? pendingViewTransitionEnabled : void 0
|
|
});
|
|
}
|
|
}
|
|
async function callDataStrategy(request, matches, scopedContext, fetcherKey) {
|
|
let results;
|
|
let dataResults = {};
|
|
try {
|
|
results = await callDataStrategyImpl(
|
|
dataStrategyImpl,
|
|
request,
|
|
matches,
|
|
fetcherKey,
|
|
scopedContext,
|
|
false
|
|
);
|
|
} catch (e) {
|
|
matches.filter((m) => m.shouldLoad).forEach((m) => {
|
|
dataResults[m.route.id] = {
|
|
type: "error" /* error */,
|
|
error: e
|
|
};
|
|
});
|
|
return dataResults;
|
|
}
|
|
if (request.signal.aborted) {
|
|
return dataResults;
|
|
}
|
|
for (let [routeId, result] of Object.entries(results)) {
|
|
if (isRedirectDataStrategyResult(result)) {
|
|
let response = result.result;
|
|
dataResults[routeId] = {
|
|
type: "redirect" /* redirect */,
|
|
response: normalizeRelativeRoutingRedirectResponse(
|
|
response,
|
|
request,
|
|
routeId,
|
|
matches,
|
|
basename
|
|
)
|
|
};
|
|
} else {
|
|
dataResults[routeId] = await convertDataStrategyResultToDataResult(result);
|
|
}
|
|
}
|
|
return dataResults;
|
|
}
|
|
async function callLoadersAndMaybeResolveData(matches, fetchersToLoad, request, scopedContext) {
|
|
let loaderResultsPromise = callDataStrategy(
|
|
request,
|
|
matches,
|
|
scopedContext,
|
|
null
|
|
);
|
|
let fetcherResultsPromise = Promise.all(
|
|
fetchersToLoad.map(async (f) => {
|
|
if (f.matches && f.match && f.request && f.controller) {
|
|
let results = await callDataStrategy(
|
|
f.request,
|
|
f.matches,
|
|
scopedContext,
|
|
f.key
|
|
);
|
|
let result = results[f.match.route.id];
|
|
return { [f.key]: result };
|
|
} else {
|
|
return Promise.resolve({
|
|
[f.key]: {
|
|
type: "error" /* error */,
|
|
error: getInternalRouterError(404, {
|
|
pathname: f.path
|
|
})
|
|
}
|
|
});
|
|
}
|
|
})
|
|
);
|
|
let loaderResults = await loaderResultsPromise;
|
|
let fetcherResults = (await fetcherResultsPromise).reduce(
|
|
(acc, r) => Object.assign(acc, r),
|
|
{}
|
|
);
|
|
return {
|
|
loaderResults,
|
|
fetcherResults
|
|
};
|
|
}
|
|
function interruptActiveLoads() {
|
|
isRevalidationRequired = true;
|
|
fetchLoadMatches.forEach((_, key) => {
|
|
if (fetchControllers.has(key)) {
|
|
cancelledFetcherLoads.add(key);
|
|
}
|
|
abortFetcher(key);
|
|
});
|
|
}
|
|
function updateFetcherState(key, fetcher, opts = {}) {
|
|
state.fetchers.set(key, fetcher);
|
|
updateState(
|
|
{ fetchers: new Map(state.fetchers) },
|
|
{ flushSync: (opts && opts.flushSync) === true }
|
|
);
|
|
}
|
|
function setFetcherError(key, routeId, error, opts = {}) {
|
|
let boundaryMatch = findNearestBoundary(state.matches, routeId);
|
|
deleteFetcher(key);
|
|
updateState(
|
|
{
|
|
errors: {
|
|
[boundaryMatch.route.id]: error
|
|
},
|
|
fetchers: new Map(state.fetchers)
|
|
},
|
|
{ flushSync: (opts && opts.flushSync) === true }
|
|
);
|
|
}
|
|
function getFetcher(key) {
|
|
activeFetchers.set(key, (activeFetchers.get(key) || 0) + 1);
|
|
if (fetchersQueuedForDeletion.has(key)) {
|
|
fetchersQueuedForDeletion.delete(key);
|
|
}
|
|
return state.fetchers.get(key) || IDLE_FETCHER;
|
|
}
|
|
function deleteFetcher(key) {
|
|
let fetcher = state.fetchers.get(key);
|
|
if (fetchControllers.has(key) && !(fetcher && fetcher.state === "loading" && fetchReloadIds.has(key))) {
|
|
abortFetcher(key);
|
|
}
|
|
fetchLoadMatches.delete(key);
|
|
fetchReloadIds.delete(key);
|
|
fetchRedirectIds.delete(key);
|
|
fetchersQueuedForDeletion.delete(key);
|
|
cancelledFetcherLoads.delete(key);
|
|
state.fetchers.delete(key);
|
|
}
|
|
function queueFetcherForDeletion(key) {
|
|
let count = (activeFetchers.get(key) || 0) - 1;
|
|
if (count <= 0) {
|
|
activeFetchers.delete(key);
|
|
fetchersQueuedForDeletion.add(key);
|
|
} else {
|
|
activeFetchers.set(key, count);
|
|
}
|
|
updateState({ fetchers: new Map(state.fetchers) });
|
|
}
|
|
function abortFetcher(key) {
|
|
let controller = fetchControllers.get(key);
|
|
if (controller) {
|
|
controller.abort();
|
|
fetchControllers.delete(key);
|
|
}
|
|
}
|
|
function markFetchersDone(keys) {
|
|
for (let key of keys) {
|
|
let fetcher = getFetcher(key);
|
|
let doneFetcher = getDoneFetcher(fetcher.data);
|
|
state.fetchers.set(key, doneFetcher);
|
|
}
|
|
}
|
|
function markFetchRedirectsDone() {
|
|
let doneKeys = [];
|
|
let updatedFetchers = false;
|
|
for (let key of fetchRedirectIds) {
|
|
let fetcher = state.fetchers.get(key);
|
|
invariant(fetcher, `Expected fetcher: ${key}`);
|
|
if (fetcher.state === "loading") {
|
|
fetchRedirectIds.delete(key);
|
|
doneKeys.push(key);
|
|
updatedFetchers = true;
|
|
}
|
|
}
|
|
markFetchersDone(doneKeys);
|
|
return updatedFetchers;
|
|
}
|
|
function abortStaleFetchLoads(landedId) {
|
|
let yeetedKeys = [];
|
|
for (let [key, id] of fetchReloadIds) {
|
|
if (id < landedId) {
|
|
let fetcher = state.fetchers.get(key);
|
|
invariant(fetcher, `Expected fetcher: ${key}`);
|
|
if (fetcher.state === "loading") {
|
|
abortFetcher(key);
|
|
fetchReloadIds.delete(key);
|
|
yeetedKeys.push(key);
|
|
}
|
|
}
|
|
}
|
|
markFetchersDone(yeetedKeys);
|
|
return yeetedKeys.length > 0;
|
|
}
|
|
function getBlocker(key, fn) {
|
|
let blocker = state.blockers.get(key) || IDLE_BLOCKER;
|
|
if (blockerFunctions.get(key) !== fn) {
|
|
blockerFunctions.set(key, fn);
|
|
}
|
|
return blocker;
|
|
}
|
|
function deleteBlocker(key) {
|
|
state.blockers.delete(key);
|
|
blockerFunctions.delete(key);
|
|
}
|
|
function updateBlocker(key, newBlocker) {
|
|
let blocker = state.blockers.get(key) || IDLE_BLOCKER;
|
|
invariant(
|
|
blocker.state === "unblocked" && newBlocker.state === "blocked" || blocker.state === "blocked" && newBlocker.state === "blocked" || blocker.state === "blocked" && newBlocker.state === "proceeding" || blocker.state === "blocked" && newBlocker.state === "unblocked" || blocker.state === "proceeding" && newBlocker.state === "unblocked",
|
|
`Invalid blocker state transition: ${blocker.state} -> ${newBlocker.state}`
|
|
);
|
|
let blockers = new Map(state.blockers);
|
|
blockers.set(key, newBlocker);
|
|
updateState({ blockers });
|
|
}
|
|
function shouldBlockNavigation({
|
|
currentLocation,
|
|
nextLocation,
|
|
historyAction
|
|
}) {
|
|
if (blockerFunctions.size === 0) {
|
|
return;
|
|
}
|
|
if (blockerFunctions.size > 1) {
|
|
warning(false, "A router only supports one blocker at a time");
|
|
}
|
|
let entries = Array.from(blockerFunctions.entries());
|
|
let [blockerKey, blockerFunction] = entries[entries.length - 1];
|
|
let blocker = state.blockers.get(blockerKey);
|
|
if (blocker && blocker.state === "proceeding") {
|
|
return;
|
|
}
|
|
if (blockerFunction({ currentLocation, nextLocation, historyAction })) {
|
|
return blockerKey;
|
|
}
|
|
}
|
|
function handleNavigational404(pathname) {
|
|
let error = getInternalRouterError(404, { pathname });
|
|
let routesToUse = inFlightDataRoutes || dataRoutes;
|
|
let { matches, route } = getShortCircuitMatches(routesToUse);
|
|
return { notFoundMatches: matches, route, error };
|
|
}
|
|
function enableScrollRestoration(positions, getPosition, getKey) {
|
|
savedScrollPositions = positions;
|
|
getScrollPosition = getPosition;
|
|
getScrollRestorationKey = getKey || null;
|
|
if (!initialScrollRestored && state.navigation === IDLE_NAVIGATION) {
|
|
initialScrollRestored = true;
|
|
let y = getSavedScrollPosition(state.location, state.matches);
|
|
if (y != null) {
|
|
updateState({ restoreScrollPosition: y });
|
|
}
|
|
}
|
|
return () => {
|
|
savedScrollPositions = null;
|
|
getScrollPosition = null;
|
|
getScrollRestorationKey = null;
|
|
};
|
|
}
|
|
function getScrollKey(location, matches) {
|
|
if (getScrollRestorationKey) {
|
|
let key = getScrollRestorationKey(
|
|
location,
|
|
matches.map((m) => convertRouteMatchToUiMatch(m, state.loaderData))
|
|
);
|
|
return key || location.key;
|
|
}
|
|
return location.key;
|
|
}
|
|
function saveScrollPosition(location, matches) {
|
|
if (savedScrollPositions && getScrollPosition) {
|
|
let key = getScrollKey(location, matches);
|
|
savedScrollPositions[key] = getScrollPosition();
|
|
}
|
|
}
|
|
function getSavedScrollPosition(location, matches) {
|
|
if (savedScrollPositions) {
|
|
let key = getScrollKey(location, matches);
|
|
let y = savedScrollPositions[key];
|
|
if (typeof y === "number") {
|
|
return y;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
function checkFogOfWar(matches, routesToUse, pathname) {
|
|
if (init.patchRoutesOnNavigation) {
|
|
if (!matches) {
|
|
let fogMatches = matchRoutesImpl(
|
|
routesToUse,
|
|
pathname,
|
|
basename,
|
|
true
|
|
);
|
|
return { active: true, matches: fogMatches || [] };
|
|
} else {
|
|
if (Object.keys(matches[0].params).length > 0) {
|
|
let partialMatches = matchRoutesImpl(
|
|
routesToUse,
|
|
pathname,
|
|
basename,
|
|
true
|
|
);
|
|
return { active: true, matches: partialMatches };
|
|
}
|
|
}
|
|
}
|
|
return { active: false, matches: null };
|
|
}
|
|
async function discoverRoutes(matches, pathname, signal, fetcherKey) {
|
|
if (!init.patchRoutesOnNavigation) {
|
|
return { type: "success", matches };
|
|
}
|
|
let partialMatches = matches;
|
|
while (true) {
|
|
let isNonHMR = inFlightDataRoutes == null;
|
|
let routesToUse = inFlightDataRoutes || dataRoutes;
|
|
let localManifest = manifest;
|
|
try {
|
|
await init.patchRoutesOnNavigation({
|
|
signal,
|
|
path: pathname,
|
|
matches: partialMatches,
|
|
fetcherKey,
|
|
patch: (routeId, children) => {
|
|
if (signal.aborted) return;
|
|
patchRoutesImpl(
|
|
routeId,
|
|
children,
|
|
routesToUse,
|
|
localManifest,
|
|
mapRouteProperties,
|
|
false
|
|
);
|
|
}
|
|
});
|
|
} catch (e) {
|
|
return { type: "error", error: e, partialMatches };
|
|
} finally {
|
|
if (isNonHMR && !signal.aborted) {
|
|
dataRoutes = [...dataRoutes];
|
|
}
|
|
}
|
|
if (signal.aborted) {
|
|
return { type: "aborted" };
|
|
}
|
|
let newMatches = matchRoutes(routesToUse, pathname, basename);
|
|
if (newMatches) {
|
|
return { type: "success", matches: newMatches };
|
|
}
|
|
let newPartialMatches = matchRoutesImpl(
|
|
routesToUse,
|
|
pathname,
|
|
basename,
|
|
true
|
|
);
|
|
if (!newPartialMatches || partialMatches.length === newPartialMatches.length && partialMatches.every(
|
|
(m, i) => m.route.id === newPartialMatches[i].route.id
|
|
)) {
|
|
return { type: "success", matches: null };
|
|
}
|
|
partialMatches = newPartialMatches;
|
|
}
|
|
}
|
|
function _internalSetRoutes(newRoutes) {
|
|
manifest = {};
|
|
inFlightDataRoutes = convertRoutesToDataRoutes(
|
|
newRoutes,
|
|
mapRouteProperties,
|
|
void 0,
|
|
manifest
|
|
);
|
|
}
|
|
function patchRoutes(routeId, children, unstable_allowElementMutations = false) {
|
|
let isNonHMR = inFlightDataRoutes == null;
|
|
let routesToUse = inFlightDataRoutes || dataRoutes;
|
|
patchRoutesImpl(
|
|
routeId,
|
|
children,
|
|
routesToUse,
|
|
manifest,
|
|
mapRouteProperties,
|
|
unstable_allowElementMutations
|
|
);
|
|
if (isNonHMR) {
|
|
dataRoutes = [...dataRoutes];
|
|
updateState({});
|
|
}
|
|
}
|
|
router = {
|
|
get basename() {
|
|
return basename;
|
|
},
|
|
get future() {
|
|
return future;
|
|
},
|
|
get state() {
|
|
return state;
|
|
},
|
|
get routes() {
|
|
return dataRoutes;
|
|
},
|
|
get window() {
|
|
return routerWindow;
|
|
},
|
|
initialize,
|
|
subscribe,
|
|
enableScrollRestoration,
|
|
navigate,
|
|
fetch: fetch2,
|
|
revalidate,
|
|
// Passthrough to history-aware createHref used by useHref so we get proper
|
|
// hash-aware URLs in DOM paths
|
|
createHref: (to) => init.history.createHref(to),
|
|
encodeLocation: (to) => init.history.encodeLocation(to),
|
|
getFetcher,
|
|
deleteFetcher: queueFetcherForDeletion,
|
|
dispose,
|
|
getBlocker,
|
|
deleteBlocker,
|
|
patchRoutes,
|
|
_internalFetchControllers: fetchControllers,
|
|
// TODO: Remove setRoutes, it's temporary to avoid dealing with
|
|
// updating the tree while validating the update algorithm.
|
|
_internalSetRoutes,
|
|
_internalSetStateDoNotUseOrYouWillBreakYourApp(newState) {
|
|
updateState(newState);
|
|
}
|
|
};
|
|
return router;
|
|
}
|
|
function createStaticHandler(routes, opts) {
|
|
invariant(
|
|
routes.length > 0,
|
|
"You must provide a non-empty routes array to createStaticHandler"
|
|
);
|
|
let manifest = {};
|
|
let basename = (opts ? opts.basename : null) || "/";
|
|
let mapRouteProperties = _optionalChain([opts, 'optionalAccess', _15 => _15.mapRouteProperties]) || defaultMapRouteProperties;
|
|
let dataRoutes = convertRoutesToDataRoutes(
|
|
routes,
|
|
mapRouteProperties,
|
|
void 0,
|
|
manifest
|
|
);
|
|
async function query(request, {
|
|
requestContext,
|
|
filterMatchesToLoad,
|
|
skipLoaderErrorBubbling,
|
|
skipRevalidation,
|
|
dataStrategy,
|
|
unstable_stream: stream,
|
|
unstable_respond: respond
|
|
} = {}) {
|
|
let url = new URL(request.url);
|
|
let method = request.method;
|
|
let location = createLocation("", createPath(url), null, "default");
|
|
let matches = matchRoutes(dataRoutes, location, basename);
|
|
requestContext = requestContext != null ? requestContext : new unstable_RouterContextProvider();
|
|
let respondOrStreamStaticContext = (ctx) => {
|
|
return stream ? stream(
|
|
requestContext,
|
|
() => Promise.resolve(ctx)
|
|
) : respond ? respond(ctx) : ctx;
|
|
};
|
|
if (!isValidMethod(method) && method !== "HEAD") {
|
|
let error = getInternalRouterError(405, { method });
|
|
let { matches: methodNotAllowedMatches, route } = getShortCircuitMatches(dataRoutes);
|
|
let staticContext = {
|
|
basename,
|
|
location,
|
|
matches: methodNotAllowedMatches,
|
|
loaderData: {},
|
|
actionData: null,
|
|
errors: {
|
|
[route.id]: error
|
|
},
|
|
statusCode: error.status,
|
|
loaderHeaders: {},
|
|
actionHeaders: {}
|
|
};
|
|
return respondOrStreamStaticContext(staticContext);
|
|
} else if (!matches) {
|
|
let error = getInternalRouterError(404, { pathname: location.pathname });
|
|
let { matches: notFoundMatches, route } = getShortCircuitMatches(dataRoutes);
|
|
let staticContext = {
|
|
basename,
|
|
location,
|
|
matches: notFoundMatches,
|
|
loaderData: {},
|
|
actionData: null,
|
|
errors: {
|
|
[route.id]: error
|
|
},
|
|
statusCode: error.status,
|
|
loaderHeaders: {},
|
|
actionHeaders: {}
|
|
};
|
|
return respondOrStreamStaticContext(staticContext);
|
|
}
|
|
if (stream || respond && matches.some(
|
|
(m) => m.route.unstable_middleware || typeof m.route.lazy === "object" && m.route.lazy.unstable_middleware
|
|
)) {
|
|
invariant(
|
|
requestContext instanceof unstable_RouterContextProvider,
|
|
"When using middleware in `staticHandler.query()`, any provided `requestContext` must be an instance of `unstable_RouterContextProvider`"
|
|
);
|
|
try {
|
|
await loadLazyMiddlewareForMatches(
|
|
matches,
|
|
manifest,
|
|
mapRouteProperties
|
|
);
|
|
let renderedStaticContext;
|
|
let response = await runMiddlewarePipeline(
|
|
{
|
|
request,
|
|
matches,
|
|
params: matches[0].params,
|
|
// If we're calling middleware then it must be enabled so we can cast
|
|
// this to the proper type knowing it's not an `AppLoadContext`
|
|
context: requestContext
|
|
},
|
|
true,
|
|
async () => {
|
|
if (stream) {
|
|
let res2 = await stream(
|
|
requestContext,
|
|
async (revalidationRequest) => {
|
|
let result3 = await queryImpl(
|
|
revalidationRequest,
|
|
location,
|
|
matches,
|
|
requestContext,
|
|
dataStrategy || null,
|
|
skipLoaderErrorBubbling === true,
|
|
null,
|
|
filterMatchesToLoad || null,
|
|
skipRevalidation === true
|
|
);
|
|
return isResponse(result3) ? result3 : { location, basename, ...result3 };
|
|
}
|
|
);
|
|
return res2;
|
|
}
|
|
invariant(respond, "Expected respond to be defined");
|
|
let result2 = await queryImpl(
|
|
request,
|
|
location,
|
|
matches,
|
|
requestContext,
|
|
dataStrategy || null,
|
|
skipLoaderErrorBubbling === true,
|
|
null,
|
|
filterMatchesToLoad || null,
|
|
skipRevalidation === true
|
|
);
|
|
if (isResponse(result2)) {
|
|
return result2;
|
|
}
|
|
renderedStaticContext = { location, basename, ...result2 };
|
|
let res = await respond(renderedStaticContext);
|
|
return res;
|
|
},
|
|
async (error, routeId) => {
|
|
if (isResponse(error)) {
|
|
return error;
|
|
}
|
|
if (renderedStaticContext) {
|
|
if (routeId in renderedStaticContext.loaderData) {
|
|
renderedStaticContext.loaderData[routeId] = void 0;
|
|
}
|
|
let staticContext = getStaticContextFromError(
|
|
dataRoutes,
|
|
renderedStaticContext,
|
|
error,
|
|
skipLoaderErrorBubbling ? routeId : findNearestBoundary(matches, routeId).route.id
|
|
);
|
|
return respondOrStreamStaticContext(staticContext);
|
|
} else {
|
|
let boundaryRouteId = skipLoaderErrorBubbling ? routeId : findNearestBoundary(
|
|
matches,
|
|
_optionalChain([matches, 'access', _16 => _16.find, 'call', _17 => _17(
|
|
(m) => m.route.id === routeId || m.route.loader
|
|
), 'optionalAccess', _18 => _18.route, 'access', _19 => _19.id]) || routeId
|
|
).route.id;
|
|
let staticContext = {
|
|
matches,
|
|
location,
|
|
basename,
|
|
loaderData: {},
|
|
actionData: null,
|
|
errors: {
|
|
[boundaryRouteId]: error
|
|
},
|
|
statusCode: isRouteErrorResponse(error) ? error.status : 500,
|
|
actionHeaders: {},
|
|
loaderHeaders: {}
|
|
};
|
|
return respondOrStreamStaticContext(staticContext);
|
|
}
|
|
}
|
|
);
|
|
invariant(isResponse(response), "Expected a response in query()");
|
|
return response;
|
|
} catch (e) {
|
|
if (isResponse(e)) {
|
|
return e;
|
|
}
|
|
throw e;
|
|
}
|
|
}
|
|
let result = await queryImpl(
|
|
request,
|
|
location,
|
|
matches,
|
|
requestContext,
|
|
dataStrategy || null,
|
|
skipLoaderErrorBubbling === true,
|
|
null,
|
|
filterMatchesToLoad || null,
|
|
skipRevalidation === true
|
|
);
|
|
if (isResponse(result)) {
|
|
return result;
|
|
}
|
|
return { location, basename, ...result };
|
|
}
|
|
async function queryRoute(request, {
|
|
routeId,
|
|
requestContext,
|
|
dataStrategy,
|
|
unstable_respond: respond
|
|
} = {}) {
|
|
let url = new URL(request.url);
|
|
let method = request.method;
|
|
let location = createLocation("", createPath(url), null, "default");
|
|
let matches = matchRoutes(dataRoutes, location, basename);
|
|
requestContext = requestContext != null ? requestContext : new unstable_RouterContextProvider();
|
|
if (!isValidMethod(method) && method !== "HEAD" && method !== "OPTIONS") {
|
|
throw getInternalRouterError(405, { method });
|
|
} else if (!matches) {
|
|
throw getInternalRouterError(404, { pathname: location.pathname });
|
|
}
|
|
let match = routeId ? matches.find((m) => m.route.id === routeId) : getTargetMatch(matches, location);
|
|
if (routeId && !match) {
|
|
throw getInternalRouterError(403, {
|
|
pathname: location.pathname,
|
|
routeId
|
|
});
|
|
} else if (!match) {
|
|
throw getInternalRouterError(404, { pathname: location.pathname });
|
|
}
|
|
if (respond && matches.some(
|
|
(m) => m.route.unstable_middleware || typeof m.route.lazy === "object" && m.route.lazy.unstable_middleware
|
|
)) {
|
|
invariant(
|
|
requestContext instanceof unstable_RouterContextProvider,
|
|
"When using middleware in `staticHandler.queryRoute()`, any provided `requestContext` must be an instance of `unstable_RouterContextProvider`"
|
|
);
|
|
await loadLazyMiddlewareForMatches(matches, manifest, mapRouteProperties);
|
|
let response = await runMiddlewarePipeline(
|
|
{
|
|
request,
|
|
matches,
|
|
params: matches[0].params,
|
|
// If we're calling middleware then it must be enabled so we can cast
|
|
// this to the proper type knowing it's not an `AppLoadContext`
|
|
context: requestContext
|
|
},
|
|
true,
|
|
async () => {
|
|
let result2 = await queryImpl(
|
|
request,
|
|
location,
|
|
matches,
|
|
requestContext,
|
|
dataStrategy || null,
|
|
false,
|
|
match,
|
|
null,
|
|
false
|
|
);
|
|
if (isResponse(result2)) {
|
|
return respond(result2);
|
|
}
|
|
let error2 = result2.errors ? Object.values(result2.errors)[0] : void 0;
|
|
if (error2 !== void 0) {
|
|
throw error2;
|
|
}
|
|
let value = result2.actionData ? Object.values(result2.actionData)[0] : Object.values(result2.loaderData)[0];
|
|
return typeof value === "string" ? new Response(value) : Response.json(value);
|
|
},
|
|
(error2) => {
|
|
if (isResponse(error2)) {
|
|
return respond(error2);
|
|
}
|
|
return new Response(String(error2), {
|
|
status: 500,
|
|
statusText: "Unexpected Server Error"
|
|
});
|
|
}
|
|
);
|
|
return response;
|
|
}
|
|
let result = await queryImpl(
|
|
request,
|
|
location,
|
|
matches,
|
|
requestContext,
|
|
dataStrategy || null,
|
|
false,
|
|
match,
|
|
null,
|
|
false
|
|
);
|
|
if (isResponse(result)) {
|
|
return result;
|
|
}
|
|
let error = result.errors ? Object.values(result.errors)[0] : void 0;
|
|
if (error !== void 0) {
|
|
throw error;
|
|
}
|
|
if (result.actionData) {
|
|
return Object.values(result.actionData)[0];
|
|
}
|
|
if (result.loaderData) {
|
|
return Object.values(result.loaderData)[0];
|
|
}
|
|
return void 0;
|
|
}
|
|
async function queryImpl(request, location, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch, filterMatchesToLoad, skipRevalidation) {
|
|
invariant(
|
|
request.signal,
|
|
"query()/queryRoute() requests must contain an AbortController signal"
|
|
);
|
|
try {
|
|
if (isMutationMethod(request.method)) {
|
|
let result2 = await submit(
|
|
request,
|
|
matches,
|
|
routeMatch || getTargetMatch(matches, location),
|
|
requestContext,
|
|
dataStrategy,
|
|
skipLoaderErrorBubbling,
|
|
routeMatch != null,
|
|
filterMatchesToLoad,
|
|
skipRevalidation
|
|
);
|
|
return result2;
|
|
}
|
|
let result = await loadRouteData(
|
|
request,
|
|
matches,
|
|
requestContext,
|
|
dataStrategy,
|
|
skipLoaderErrorBubbling,
|
|
routeMatch,
|
|
filterMatchesToLoad
|
|
);
|
|
return isResponse(result) ? result : {
|
|
...result,
|
|
actionData: null,
|
|
actionHeaders: {}
|
|
};
|
|
} catch (e) {
|
|
if (isDataStrategyResult(e) && isResponse(e.result)) {
|
|
if (e.type === "error" /* error */) {
|
|
throw e.result;
|
|
}
|
|
return e.result;
|
|
}
|
|
if (isRedirectResponse(e)) {
|
|
return e;
|
|
}
|
|
throw e;
|
|
}
|
|
}
|
|
async function submit(request, matches, actionMatch, requestContext, dataStrategy, skipLoaderErrorBubbling, isRouteRequest, filterMatchesToLoad, skipRevalidation) {
|
|
let result;
|
|
if (!actionMatch.route.action && !actionMatch.route.lazy) {
|
|
let error = getInternalRouterError(405, {
|
|
method: request.method,
|
|
pathname: new URL(request.url).pathname,
|
|
routeId: actionMatch.route.id
|
|
});
|
|
if (isRouteRequest) {
|
|
throw error;
|
|
}
|
|
result = {
|
|
type: "error" /* error */,
|
|
error
|
|
};
|
|
} else {
|
|
let dsMatches = getTargetedDataStrategyMatches(
|
|
mapRouteProperties,
|
|
manifest,
|
|
request,
|
|
matches,
|
|
actionMatch,
|
|
[],
|
|
requestContext
|
|
);
|
|
let results = await callDataStrategy(
|
|
request,
|
|
dsMatches,
|
|
isRouteRequest,
|
|
requestContext,
|
|
dataStrategy
|
|
);
|
|
result = results[actionMatch.route.id];
|
|
if (request.signal.aborted) {
|
|
throwStaticHandlerAbortedError(request, isRouteRequest);
|
|
}
|
|
}
|
|
if (isRedirectResult(result)) {
|
|
throw new Response(null, {
|
|
status: result.response.status,
|
|
headers: {
|
|
Location: result.response.headers.get("Location")
|
|
}
|
|
});
|
|
}
|
|
if (isRouteRequest) {
|
|
if (isErrorResult(result)) {
|
|
throw result.error;
|
|
}
|
|
return {
|
|
matches: [actionMatch],
|
|
loaderData: {},
|
|
actionData: { [actionMatch.route.id]: result.data },
|
|
errors: null,
|
|
// Note: statusCode + headers are unused here since queryRoute will
|
|
// return the raw Response or value
|
|
statusCode: 200,
|
|
loaderHeaders: {},
|
|
actionHeaders: {}
|
|
};
|
|
}
|
|
if (skipRevalidation) {
|
|
if (isErrorResult(result)) {
|
|
let boundaryMatch = skipLoaderErrorBubbling ? actionMatch : findNearestBoundary(matches, actionMatch.route.id);
|
|
return {
|
|
statusCode: isRouteErrorResponse(result.error) ? result.error.status : result.statusCode != null ? result.statusCode : 500,
|
|
actionData: null,
|
|
actionHeaders: {
|
|
...result.headers ? { [actionMatch.route.id]: result.headers } : {}
|
|
},
|
|
matches,
|
|
loaderData: {},
|
|
errors: {
|
|
[boundaryMatch.route.id]: result.error
|
|
},
|
|
loaderHeaders: {}
|
|
};
|
|
} else {
|
|
return {
|
|
actionData: {
|
|
[actionMatch.route.id]: result.data
|
|
},
|
|
actionHeaders: result.headers ? { [actionMatch.route.id]: result.headers } : {},
|
|
matches,
|
|
loaderData: {},
|
|
errors: null,
|
|
statusCode: result.statusCode || 200,
|
|
loaderHeaders: {}
|
|
};
|
|
}
|
|
}
|
|
let loaderRequest = new Request(request.url, {
|
|
headers: request.headers,
|
|
redirect: request.redirect,
|
|
signal: request.signal
|
|
});
|
|
if (isErrorResult(result)) {
|
|
let boundaryMatch = skipLoaderErrorBubbling ? actionMatch : findNearestBoundary(matches, actionMatch.route.id);
|
|
let handlerContext2 = await loadRouteData(
|
|
loaderRequest,
|
|
matches,
|
|
requestContext,
|
|
dataStrategy,
|
|
skipLoaderErrorBubbling,
|
|
null,
|
|
filterMatchesToLoad,
|
|
[boundaryMatch.route.id, result]
|
|
);
|
|
return {
|
|
...handlerContext2,
|
|
statusCode: isRouteErrorResponse(result.error) ? result.error.status : result.statusCode != null ? result.statusCode : 500,
|
|
actionData: null,
|
|
actionHeaders: {
|
|
...result.headers ? { [actionMatch.route.id]: result.headers } : {}
|
|
}
|
|
};
|
|
}
|
|
let handlerContext = await loadRouteData(
|
|
loaderRequest,
|
|
matches,
|
|
requestContext,
|
|
dataStrategy,
|
|
skipLoaderErrorBubbling,
|
|
null,
|
|
filterMatchesToLoad
|
|
);
|
|
return {
|
|
...handlerContext,
|
|
actionData: {
|
|
[actionMatch.route.id]: result.data
|
|
},
|
|
// action status codes take precedence over loader status codes
|
|
...result.statusCode ? { statusCode: result.statusCode } : {},
|
|
actionHeaders: result.headers ? { [actionMatch.route.id]: result.headers } : {}
|
|
};
|
|
}
|
|
async function loadRouteData(request, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch, filterMatchesToLoad, pendingActionResult) {
|
|
let isRouteRequest = routeMatch != null;
|
|
if (isRouteRequest && !_optionalChain([routeMatch, 'optionalAccess', _20 => _20.route, 'access', _21 => _21.loader]) && !_optionalChain([routeMatch, 'optionalAccess', _22 => _22.route, 'access', _23 => _23.lazy])) {
|
|
throw getInternalRouterError(400, {
|
|
method: request.method,
|
|
pathname: new URL(request.url).pathname,
|
|
routeId: _optionalChain([routeMatch, 'optionalAccess', _24 => _24.route, 'access', _25 => _25.id])
|
|
});
|
|
}
|
|
let dsMatches;
|
|
if (routeMatch) {
|
|
dsMatches = getTargetedDataStrategyMatches(
|
|
mapRouteProperties,
|
|
manifest,
|
|
request,
|
|
matches,
|
|
routeMatch,
|
|
[],
|
|
requestContext
|
|
);
|
|
} else {
|
|
let maxIdx = pendingActionResult && isErrorResult(pendingActionResult[1]) ? (
|
|
// Up to but not including the boundary
|
|
matches.findIndex((m) => m.route.id === pendingActionResult[0]) - 1
|
|
) : void 0;
|
|
dsMatches = matches.map((match, index) => {
|
|
if (maxIdx != null && index > maxIdx) {
|
|
return getDataStrategyMatch(
|
|
mapRouteProperties,
|
|
manifest,
|
|
request,
|
|
match,
|
|
[],
|
|
requestContext,
|
|
false
|
|
);
|
|
}
|
|
return getDataStrategyMatch(
|
|
mapRouteProperties,
|
|
manifest,
|
|
request,
|
|
match,
|
|
[],
|
|
requestContext,
|
|
(match.route.loader || match.route.lazy) != null && (!filterMatchesToLoad || filterMatchesToLoad(match))
|
|
);
|
|
});
|
|
}
|
|
if (!dataStrategy && !dsMatches.some((m) => m.shouldLoad)) {
|
|
return {
|
|
matches,
|
|
loaderData: {},
|
|
errors: pendingActionResult && isErrorResult(pendingActionResult[1]) ? {
|
|
[pendingActionResult[0]]: pendingActionResult[1].error
|
|
} : null,
|
|
statusCode: 200,
|
|
loaderHeaders: {}
|
|
};
|
|
}
|
|
let results = await callDataStrategy(
|
|
request,
|
|
dsMatches,
|
|
isRouteRequest,
|
|
requestContext,
|
|
dataStrategy
|
|
);
|
|
if (request.signal.aborted) {
|
|
throwStaticHandlerAbortedError(request, isRouteRequest);
|
|
}
|
|
let handlerContext = processRouteLoaderData(
|
|
matches,
|
|
results,
|
|
pendingActionResult,
|
|
true,
|
|
skipLoaderErrorBubbling
|
|
);
|
|
return {
|
|
...handlerContext,
|
|
matches
|
|
};
|
|
}
|
|
async function callDataStrategy(request, matches, isRouteRequest, requestContext, dataStrategy) {
|
|
let results = await callDataStrategyImpl(
|
|
dataStrategy || defaultDataStrategy,
|
|
request,
|
|
matches,
|
|
null,
|
|
requestContext,
|
|
true
|
|
);
|
|
let dataResults = {};
|
|
await Promise.all(
|
|
matches.map(async (match) => {
|
|
if (!(match.route.id in results)) {
|
|
return;
|
|
}
|
|
let result = results[match.route.id];
|
|
if (isRedirectDataStrategyResult(result)) {
|
|
let response = result.result;
|
|
throw normalizeRelativeRoutingRedirectResponse(
|
|
response,
|
|
request,
|
|
match.route.id,
|
|
matches,
|
|
basename
|
|
);
|
|
}
|
|
if (isResponse(result.result) && isRouteRequest) {
|
|
throw result;
|
|
}
|
|
dataResults[match.route.id] = await convertDataStrategyResultToDataResult(result);
|
|
})
|
|
);
|
|
return dataResults;
|
|
}
|
|
return {
|
|
dataRoutes,
|
|
query,
|
|
queryRoute
|
|
};
|
|
}
|
|
function getStaticContextFromError(routes, handlerContext, error, boundaryId) {
|
|
let errorBoundaryId = boundaryId || handlerContext._deepestRenderedBoundaryId || routes[0].id;
|
|
return {
|
|
...handlerContext,
|
|
statusCode: isRouteErrorResponse(error) ? error.status : 500,
|
|
errors: {
|
|
[errorBoundaryId]: error
|
|
}
|
|
};
|
|
}
|
|
function throwStaticHandlerAbortedError(request, isRouteRequest) {
|
|
if (request.signal.reason !== void 0) {
|
|
throw request.signal.reason;
|
|
}
|
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
throw new Error(
|
|
`${method}() call aborted without an \`AbortSignal.reason\`: ${request.method} ${request.url}`
|
|
);
|
|
}
|
|
function isSubmissionNavigation(opts) {
|
|
return opts != null && ("formData" in opts && opts.formData != null || "body" in opts && opts.body !== void 0);
|
|
}
|
|
function normalizeTo(location, matches, basename, to, fromRouteId, relative) {
|
|
let contextualMatches;
|
|
let activeRouteMatch;
|
|
if (fromRouteId) {
|
|
contextualMatches = [];
|
|
for (let match of matches) {
|
|
contextualMatches.push(match);
|
|
if (match.route.id === fromRouteId) {
|
|
activeRouteMatch = match;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
contextualMatches = matches;
|
|
activeRouteMatch = matches[matches.length - 1];
|
|
}
|
|
let path = resolveTo(
|
|
to ? to : ".",
|
|
getResolveToMatches(contextualMatches),
|
|
stripBasename(location.pathname, basename) || location.pathname,
|
|
relative === "path"
|
|
);
|
|
if (to == null) {
|
|
path.search = location.search;
|
|
path.hash = location.hash;
|
|
}
|
|
if ((to == null || to === "" || to === ".") && activeRouteMatch) {
|
|
let nakedIndex = hasNakedIndexQuery(path.search);
|
|
if (activeRouteMatch.route.index && !nakedIndex) {
|
|
path.search = path.search ? path.search.replace(/^\?/, "?index&") : "?index";
|
|
} else if (!activeRouteMatch.route.index && nakedIndex) {
|
|
let params = new URLSearchParams(path.search);
|
|
let indexValues = params.getAll("index");
|
|
params.delete("index");
|
|
indexValues.filter((v) => v).forEach((v) => params.append("index", v));
|
|
let qs = params.toString();
|
|
path.search = qs ? `?${qs}` : "";
|
|
}
|
|
}
|
|
if (basename !== "/") {
|
|
path.pathname = prependBasename({ basename, pathname: path.pathname });
|
|
}
|
|
return createPath(path);
|
|
}
|
|
function normalizeNavigateOptions(isFetcher, path, opts) {
|
|
if (!opts || !isSubmissionNavigation(opts)) {
|
|
return { path };
|
|
}
|
|
if (opts.formMethod && !isValidMethod(opts.formMethod)) {
|
|
return {
|
|
path,
|
|
error: getInternalRouterError(405, { method: opts.formMethod })
|
|
};
|
|
}
|
|
let getInvalidBodyError = () => ({
|
|
path,
|
|
error: getInternalRouterError(400, { type: "invalid-body" })
|
|
});
|
|
let rawFormMethod = opts.formMethod || "get";
|
|
let formMethod = rawFormMethod.toUpperCase();
|
|
let formAction = stripHashFromPath(path);
|
|
if (opts.body !== void 0) {
|
|
if (opts.formEncType === "text/plain") {
|
|
if (!isMutationMethod(formMethod)) {
|
|
return getInvalidBodyError();
|
|
}
|
|
let text = typeof opts.body === "string" ? opts.body : opts.body instanceof FormData || opts.body instanceof URLSearchParams ? (
|
|
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#plain-text-form-data
|
|
Array.from(opts.body.entries()).reduce(
|
|
(acc, [name, value]) => `${acc}${name}=${value}
|
|
`,
|
|
""
|
|
)
|
|
) : String(opts.body);
|
|
return {
|
|
path,
|
|
submission: {
|
|
formMethod,
|
|
formAction,
|
|
formEncType: opts.formEncType,
|
|
formData: void 0,
|
|
json: void 0,
|
|
text
|
|
}
|
|
};
|
|
} else if (opts.formEncType === "application/json") {
|
|
if (!isMutationMethod(formMethod)) {
|
|
return getInvalidBodyError();
|
|
}
|
|
try {
|
|
let json = typeof opts.body === "string" ? JSON.parse(opts.body) : opts.body;
|
|
return {
|
|
path,
|
|
submission: {
|
|
formMethod,
|
|
formAction,
|
|
formEncType: opts.formEncType,
|
|
formData: void 0,
|
|
json,
|
|
text: void 0
|
|
}
|
|
};
|
|
} catch (e) {
|
|
return getInvalidBodyError();
|
|
}
|
|
}
|
|
}
|
|
invariant(
|
|
typeof FormData === "function",
|
|
"FormData is not available in this environment"
|
|
);
|
|
let searchParams;
|
|
let formData;
|
|
if (opts.formData) {
|
|
searchParams = convertFormDataToSearchParams(opts.formData);
|
|
formData = opts.formData;
|
|
} else if (opts.body instanceof FormData) {
|
|
searchParams = convertFormDataToSearchParams(opts.body);
|
|
formData = opts.body;
|
|
} else if (opts.body instanceof URLSearchParams) {
|
|
searchParams = opts.body;
|
|
formData = convertSearchParamsToFormData(searchParams);
|
|
} else if (opts.body == null) {
|
|
searchParams = new URLSearchParams();
|
|
formData = new FormData();
|
|
} else {
|
|
try {
|
|
searchParams = new URLSearchParams(opts.body);
|
|
formData = convertSearchParamsToFormData(searchParams);
|
|
} catch (e) {
|
|
return getInvalidBodyError();
|
|
}
|
|
}
|
|
let submission = {
|
|
formMethod,
|
|
formAction,
|
|
formEncType: opts && opts.formEncType || "application/x-www-form-urlencoded",
|
|
formData,
|
|
json: void 0,
|
|
text: void 0
|
|
};
|
|
if (isMutationMethod(submission.formMethod)) {
|
|
return { path, submission };
|
|
}
|
|
let parsedPath = parsePath(path);
|
|
if (isFetcher && parsedPath.search && hasNakedIndexQuery(parsedPath.search)) {
|
|
searchParams.append("index", "");
|
|
}
|
|
parsedPath.search = `?${searchParams}`;
|
|
return { path: createPath(parsedPath), submission };
|
|
}
|
|
function getMatchesToLoad(request, scopedContext, mapRouteProperties, manifest, history, state, matches, submission, location, lazyRoutePropertiesToSkip, initialHydration, isRevalidationRequired, cancelledFetcherLoads, fetchersQueuedForDeletion, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, hasPatchRoutesOnNavigation, pendingActionResult) {
|
|
let actionResult = pendingActionResult ? isErrorResult(pendingActionResult[1]) ? pendingActionResult[1].error : pendingActionResult[1].data : void 0;
|
|
let currentUrl = history.createURL(state.location);
|
|
let nextUrl = history.createURL(location);
|
|
let maxIdx;
|
|
if (initialHydration && state.errors) {
|
|
let boundaryId = Object.keys(state.errors)[0];
|
|
maxIdx = matches.findIndex((m) => m.route.id === boundaryId);
|
|
} else if (pendingActionResult && isErrorResult(pendingActionResult[1])) {
|
|
let boundaryId = pendingActionResult[0];
|
|
maxIdx = matches.findIndex((m) => m.route.id === boundaryId) - 1;
|
|
}
|
|
let actionStatus = pendingActionResult ? pendingActionResult[1].statusCode : void 0;
|
|
let shouldSkipRevalidation = actionStatus && actionStatus >= 400;
|
|
let baseShouldRevalidateArgs = {
|
|
currentUrl,
|
|
currentParams: _optionalChain([state, 'access', _26 => _26.matches, 'access', _27 => _27[0], 'optionalAccess', _28 => _28.params]) || {},
|
|
nextUrl,
|
|
nextParams: matches[0].params,
|
|
...submission,
|
|
actionResult,
|
|
actionStatus
|
|
};
|
|
let dsMatches = matches.map((match, index) => {
|
|
let { route } = match;
|
|
let forceShouldLoad = null;
|
|
if (maxIdx != null && index > maxIdx) {
|
|
forceShouldLoad = false;
|
|
} else if (route.lazy) {
|
|
forceShouldLoad = true;
|
|
} else if (route.loader == null) {
|
|
forceShouldLoad = false;
|
|
} else if (initialHydration) {
|
|
forceShouldLoad = shouldLoadRouteOnHydration(
|
|
route,
|
|
state.loaderData,
|
|
state.errors
|
|
);
|
|
} else if (isNewLoader(state.loaderData, state.matches[index], match)) {
|
|
forceShouldLoad = true;
|
|
}
|
|
if (forceShouldLoad !== null) {
|
|
return getDataStrategyMatch(
|
|
mapRouteProperties,
|
|
manifest,
|
|
request,
|
|
match,
|
|
lazyRoutePropertiesToSkip,
|
|
scopedContext,
|
|
forceShouldLoad
|
|
);
|
|
}
|
|
let defaultShouldRevalidate = shouldSkipRevalidation ? false : (
|
|
// Forced revalidation due to submission, useRevalidator, or X-Remix-Revalidate
|
|
isRevalidationRequired || currentUrl.pathname + currentUrl.search === nextUrl.pathname + nextUrl.search || // Search params affect all loaders
|
|
currentUrl.search !== nextUrl.search || isNewRouteInstance(state.matches[index], match)
|
|
);
|
|
let shouldRevalidateArgs = {
|
|
...baseShouldRevalidateArgs,
|
|
defaultShouldRevalidate
|
|
};
|
|
let shouldLoad = shouldRevalidateLoader(match, shouldRevalidateArgs);
|
|
return getDataStrategyMatch(
|
|
mapRouteProperties,
|
|
manifest,
|
|
request,
|
|
match,
|
|
lazyRoutePropertiesToSkip,
|
|
scopedContext,
|
|
shouldLoad,
|
|
shouldRevalidateArgs
|
|
);
|
|
});
|
|
let revalidatingFetchers = [];
|
|
fetchLoadMatches.forEach((f, key) => {
|
|
if (initialHydration || !matches.some((m) => m.route.id === f.routeId) || fetchersQueuedForDeletion.has(key)) {
|
|
return;
|
|
}
|
|
let fetcher = state.fetchers.get(key);
|
|
let isMidInitialLoad = fetcher && fetcher.state !== "idle" && fetcher.data === void 0;
|
|
let fetcherMatches = matchRoutes(routesToUse, f.path, basename);
|
|
if (!fetcherMatches) {
|
|
if (hasPatchRoutesOnNavigation && isMidInitialLoad) {
|
|
return;
|
|
}
|
|
revalidatingFetchers.push({
|
|
key,
|
|
routeId: f.routeId,
|
|
path: f.path,
|
|
matches: null,
|
|
match: null,
|
|
request: null,
|
|
controller: null
|
|
});
|
|
return;
|
|
}
|
|
if (fetchRedirectIds.has(key)) {
|
|
return;
|
|
}
|
|
let fetcherMatch = getTargetMatch(fetcherMatches, f.path);
|
|
let fetchController = new AbortController();
|
|
let fetchRequest = createClientSideRequest(
|
|
history,
|
|
f.path,
|
|
fetchController.signal
|
|
);
|
|
let fetcherDsMatches = null;
|
|
if (cancelledFetcherLoads.has(key)) {
|
|
cancelledFetcherLoads.delete(key);
|
|
fetcherDsMatches = getTargetedDataStrategyMatches(
|
|
mapRouteProperties,
|
|
manifest,
|
|
fetchRequest,
|
|
fetcherMatches,
|
|
fetcherMatch,
|
|
lazyRoutePropertiesToSkip,
|
|
scopedContext
|
|
);
|
|
} else if (isMidInitialLoad) {
|
|
if (isRevalidationRequired) {
|
|
fetcherDsMatches = getTargetedDataStrategyMatches(
|
|
mapRouteProperties,
|
|
manifest,
|
|
fetchRequest,
|
|
fetcherMatches,
|
|
fetcherMatch,
|
|
lazyRoutePropertiesToSkip,
|
|
scopedContext
|
|
);
|
|
}
|
|
} else {
|
|
let shouldRevalidateArgs = {
|
|
...baseShouldRevalidateArgs,
|
|
defaultShouldRevalidate: shouldSkipRevalidation ? false : isRevalidationRequired
|
|
};
|
|
if (shouldRevalidateLoader(fetcherMatch, shouldRevalidateArgs)) {
|
|
fetcherDsMatches = getTargetedDataStrategyMatches(
|
|
mapRouteProperties,
|
|
manifest,
|
|
fetchRequest,
|
|
fetcherMatches,
|
|
fetcherMatch,
|
|
lazyRoutePropertiesToSkip,
|
|
scopedContext,
|
|
shouldRevalidateArgs
|
|
);
|
|
}
|
|
}
|
|
if (fetcherDsMatches) {
|
|
revalidatingFetchers.push({
|
|
key,
|
|
routeId: f.routeId,
|
|
path: f.path,
|
|
matches: fetcherDsMatches,
|
|
match: fetcherMatch,
|
|
request: fetchRequest,
|
|
controller: fetchController
|
|
});
|
|
}
|
|
});
|
|
return { dsMatches, revalidatingFetchers };
|
|
}
|
|
function shouldLoadRouteOnHydration(route, loaderData, errors) {
|
|
if (route.lazy) {
|
|
return true;
|
|
}
|
|
if (!route.loader) {
|
|
return false;
|
|
}
|
|
let hasData = loaderData != null && route.id in loaderData;
|
|
let hasError = errors != null && errors[route.id] !== void 0;
|
|
if (!hasData && hasError) {
|
|
return false;
|
|
}
|
|
if (typeof route.loader === "function" && route.loader.hydrate === true) {
|
|
return true;
|
|
}
|
|
return !hasData && !hasError;
|
|
}
|
|
function isNewLoader(currentLoaderData, currentMatch, match) {
|
|
let isNew = (
|
|
// [a] -> [a, b]
|
|
!currentMatch || // [a, b] -> [a, c]
|
|
match.route.id !== currentMatch.route.id
|
|
);
|
|
let isMissingData = !currentLoaderData.hasOwnProperty(match.route.id);
|
|
return isNew || isMissingData;
|
|
}
|
|
function isNewRouteInstance(currentMatch, match) {
|
|
let currentPath = currentMatch.route.path;
|
|
return (
|
|
// param change for this match, /users/123 -> /users/456
|
|
currentMatch.pathname !== match.pathname || // splat param changed, which is not present in match.path
|
|
// e.g. /files/images/avatar.jpg -> files/finances.xls
|
|
currentPath != null && currentPath.endsWith("*") && currentMatch.params["*"] !== match.params["*"]
|
|
);
|
|
}
|
|
function shouldRevalidateLoader(loaderMatch, arg) {
|
|
if (loaderMatch.route.shouldRevalidate) {
|
|
let routeChoice = loaderMatch.route.shouldRevalidate(arg);
|
|
if (typeof routeChoice === "boolean") {
|
|
return routeChoice;
|
|
}
|
|
}
|
|
return arg.defaultShouldRevalidate;
|
|
}
|
|
function patchRoutesImpl(routeId, children, routesToUse, manifest, mapRouteProperties, allowElementMutations) {
|
|
let childrenToPatch;
|
|
if (routeId) {
|
|
let route = manifest[routeId];
|
|
invariant(
|
|
route,
|
|
`No route found to patch children into: routeId = ${routeId}`
|
|
);
|
|
if (!route.children) {
|
|
route.children = [];
|
|
}
|
|
childrenToPatch = route.children;
|
|
} else {
|
|
childrenToPatch = routesToUse;
|
|
}
|
|
let uniqueChildren = [];
|
|
let existingChildren = [];
|
|
children.forEach((newRoute) => {
|
|
let existingRoute = childrenToPatch.find(
|
|
(existingRoute2) => isSameRoute(newRoute, existingRoute2)
|
|
);
|
|
if (existingRoute) {
|
|
existingChildren.push({ existingRoute, newRoute });
|
|
} else {
|
|
uniqueChildren.push(newRoute);
|
|
}
|
|
});
|
|
if (uniqueChildren.length > 0) {
|
|
let newRoutes = convertRoutesToDataRoutes(
|
|
uniqueChildren,
|
|
mapRouteProperties,
|
|
[routeId || "_", "patch", String(_optionalChain([childrenToPatch, 'optionalAccess', _29 => _29.length]) || "0")],
|
|
manifest
|
|
);
|
|
childrenToPatch.push(...newRoutes);
|
|
}
|
|
if (allowElementMutations && existingChildren.length > 0) {
|
|
for (let i = 0; i < existingChildren.length; i++) {
|
|
let { existingRoute, newRoute } = existingChildren[i];
|
|
let existingRouteTyped = existingRoute;
|
|
let [newRouteTyped] = convertRoutesToDataRoutes(
|
|
[newRoute],
|
|
mapRouteProperties,
|
|
[],
|
|
// Doesn't matter for mutated routes since they already have an id
|
|
{},
|
|
// Don't touch the manifest here since we're updating in place
|
|
true
|
|
);
|
|
Object.assign(existingRouteTyped, {
|
|
element: newRouteTyped.element ? newRouteTyped.element : existingRouteTyped.element,
|
|
errorElement: newRouteTyped.errorElement ? newRouteTyped.errorElement : existingRouteTyped.errorElement,
|
|
hydrateFallbackElement: newRouteTyped.hydrateFallbackElement ? newRouteTyped.hydrateFallbackElement : existingRouteTyped.hydrateFallbackElement
|
|
});
|
|
}
|
|
}
|
|
}
|
|
function isSameRoute(newRoute, existingRoute) {
|
|
if ("id" in newRoute && "id" in existingRoute && newRoute.id === existingRoute.id) {
|
|
return true;
|
|
}
|
|
if (!(newRoute.index === existingRoute.index && newRoute.path === existingRoute.path && newRoute.caseSensitive === existingRoute.caseSensitive)) {
|
|
return false;
|
|
}
|
|
if ((!newRoute.children || newRoute.children.length === 0) && (!existingRoute.children || existingRoute.children.length === 0)) {
|
|
return true;
|
|
}
|
|
return newRoute.children.every(
|
|
(aChild, i) => _optionalChain([existingRoute, 'access', _30 => _30.children, 'optionalAccess', _31 => _31.some, 'call', _32 => _32((bChild) => isSameRoute(aChild, bChild))])
|
|
);
|
|
}
|
|
var lazyRoutePropertyCache = /* @__PURE__ */ new WeakMap();
|
|
var loadLazyRouteProperty = ({
|
|
key,
|
|
route,
|
|
manifest,
|
|
mapRouteProperties
|
|
}) => {
|
|
let routeToUpdate = manifest[route.id];
|
|
invariant(routeToUpdate, "No route found in manifest");
|
|
if (!routeToUpdate.lazy || typeof routeToUpdate.lazy !== "object") {
|
|
return;
|
|
}
|
|
let lazyFn = routeToUpdate.lazy[key];
|
|
if (!lazyFn) {
|
|
return;
|
|
}
|
|
let cache = lazyRoutePropertyCache.get(routeToUpdate);
|
|
if (!cache) {
|
|
cache = {};
|
|
lazyRoutePropertyCache.set(routeToUpdate, cache);
|
|
}
|
|
let cachedPromise = cache[key];
|
|
if (cachedPromise) {
|
|
return cachedPromise;
|
|
}
|
|
let propertyPromise = (async () => {
|
|
let isUnsupported = isUnsupportedLazyRouteObjectKey(key);
|
|
let staticRouteValue = routeToUpdate[key];
|
|
let isStaticallyDefined = staticRouteValue !== void 0 && key !== "hasErrorBoundary";
|
|
if (isUnsupported) {
|
|
warning(
|
|
!isUnsupported,
|
|
"Route property " + key + " is not a supported lazy route property. This property will be ignored."
|
|
);
|
|
cache[key] = Promise.resolve();
|
|
} else if (isStaticallyDefined) {
|
|
warning(
|
|
false,
|
|
`Route "${routeToUpdate.id}" has a static property "${key}" defined. The lazy property will be ignored.`
|
|
);
|
|
} else {
|
|
let value = await lazyFn();
|
|
if (value != null) {
|
|
Object.assign(routeToUpdate, { [key]: value });
|
|
Object.assign(routeToUpdate, mapRouteProperties(routeToUpdate));
|
|
}
|
|
}
|
|
if (typeof routeToUpdate.lazy === "object") {
|
|
routeToUpdate.lazy[key] = void 0;
|
|
if (Object.values(routeToUpdate.lazy).every((value) => value === void 0)) {
|
|
routeToUpdate.lazy = void 0;
|
|
}
|
|
}
|
|
})();
|
|
cache[key] = propertyPromise;
|
|
return propertyPromise;
|
|
};
|
|
var lazyRouteFunctionCache = /* @__PURE__ */ new WeakMap();
|
|
function loadLazyRoute(route, type, manifest, mapRouteProperties, lazyRoutePropertiesToSkip) {
|
|
let routeToUpdate = manifest[route.id];
|
|
invariant(routeToUpdate, "No route found in manifest");
|
|
if (!route.lazy) {
|
|
return {
|
|
lazyRoutePromise: void 0,
|
|
lazyHandlerPromise: void 0
|
|
};
|
|
}
|
|
if (typeof route.lazy === "function") {
|
|
let cachedPromise = lazyRouteFunctionCache.get(routeToUpdate);
|
|
if (cachedPromise) {
|
|
return {
|
|
lazyRoutePromise: cachedPromise,
|
|
lazyHandlerPromise: cachedPromise
|
|
};
|
|
}
|
|
let lazyRoutePromise2 = (async () => {
|
|
invariant(
|
|
typeof route.lazy === "function",
|
|
"No lazy route function found"
|
|
);
|
|
let lazyRoute = await route.lazy();
|
|
let routeUpdates = {};
|
|
for (let lazyRouteProperty in lazyRoute) {
|
|
let lazyValue = lazyRoute[lazyRouteProperty];
|
|
if (lazyValue === void 0) {
|
|
continue;
|
|
}
|
|
let isUnsupported = isUnsupportedLazyRouteFunctionKey(lazyRouteProperty);
|
|
let staticRouteValue = routeToUpdate[lazyRouteProperty];
|
|
let isStaticallyDefined = staticRouteValue !== void 0 && // This property isn't static since it should always be updated based
|
|
// on the route updates
|
|
lazyRouteProperty !== "hasErrorBoundary";
|
|
if (isUnsupported) {
|
|
warning(
|
|
!isUnsupported,
|
|
"Route property " + lazyRouteProperty + " is not a supported property to be returned from a lazy route function. This property will be ignored."
|
|
);
|
|
} else if (isStaticallyDefined) {
|
|
warning(
|
|
!isStaticallyDefined,
|
|
`Route "${routeToUpdate.id}" has a static property "${lazyRouteProperty}" defined but its lazy function is also returning a value for this property. The lazy route property "${lazyRouteProperty}" will be ignored.`
|
|
);
|
|
} else {
|
|
routeUpdates[lazyRouteProperty] = lazyValue;
|
|
}
|
|
}
|
|
Object.assign(routeToUpdate, routeUpdates);
|
|
Object.assign(routeToUpdate, {
|
|
// To keep things framework agnostic, we use the provided `mapRouteProperties`
|
|
// function to set the framework-aware properties (`element`/`hasErrorBoundary`)
|
|
// since the logic will differ between frameworks.
|
|
...mapRouteProperties(routeToUpdate),
|
|
lazy: void 0
|
|
});
|
|
})();
|
|
lazyRouteFunctionCache.set(routeToUpdate, lazyRoutePromise2);
|
|
lazyRoutePromise2.catch(() => {
|
|
});
|
|
return {
|
|
lazyRoutePromise: lazyRoutePromise2,
|
|
lazyHandlerPromise: lazyRoutePromise2
|
|
};
|
|
}
|
|
let lazyKeys = Object.keys(route.lazy);
|
|
let lazyPropertyPromises = [];
|
|
let lazyHandlerPromise = void 0;
|
|
for (let key of lazyKeys) {
|
|
if (lazyRoutePropertiesToSkip && lazyRoutePropertiesToSkip.includes(key)) {
|
|
continue;
|
|
}
|
|
let promise = loadLazyRouteProperty({
|
|
key,
|
|
route,
|
|
manifest,
|
|
mapRouteProperties
|
|
});
|
|
if (promise) {
|
|
lazyPropertyPromises.push(promise);
|
|
if (key === type) {
|
|
lazyHandlerPromise = promise;
|
|
}
|
|
}
|
|
}
|
|
let lazyRoutePromise = lazyPropertyPromises.length > 0 ? Promise.all(lazyPropertyPromises).then(() => {
|
|
}) : void 0;
|
|
_optionalChain([lazyRoutePromise, 'optionalAccess', _33 => _33.catch, 'call', _34 => _34(() => {
|
|
})]);
|
|
_optionalChain([lazyHandlerPromise, 'optionalAccess', _35 => _35.catch, 'call', _36 => _36(() => {
|
|
})]);
|
|
return {
|
|
lazyRoutePromise,
|
|
lazyHandlerPromise
|
|
};
|
|
}
|
|
function isNonNullable(value) {
|
|
return value !== void 0;
|
|
}
|
|
function loadLazyMiddlewareForMatches(matches, manifest, mapRouteProperties) {
|
|
let promises = matches.map(({ route }) => {
|
|
if (typeof route.lazy !== "object" || !route.lazy.unstable_middleware) {
|
|
return void 0;
|
|
}
|
|
return loadLazyRouteProperty({
|
|
key: "unstable_middleware",
|
|
route,
|
|
manifest,
|
|
mapRouteProperties
|
|
});
|
|
}).filter(isNonNullable);
|
|
return promises.length > 0 ? Promise.all(promises) : void 0;
|
|
}
|
|
async function defaultDataStrategy(args) {
|
|
let matchesToLoad = args.matches.filter((m) => m.shouldLoad);
|
|
let keyedResults = {};
|
|
let results = await Promise.all(matchesToLoad.map((m) => m.resolve()));
|
|
results.forEach((result, i) => {
|
|
keyedResults[matchesToLoad[i].route.id] = result;
|
|
});
|
|
return keyedResults;
|
|
}
|
|
async function defaultDataStrategyWithMiddleware(args) {
|
|
if (!args.matches.some((m) => m.route.unstable_middleware)) {
|
|
return defaultDataStrategy(args);
|
|
}
|
|
return runMiddlewarePipeline(
|
|
args,
|
|
false,
|
|
() => defaultDataStrategy(args),
|
|
(error, routeId) => ({ [routeId]: { type: "error", result: error } })
|
|
);
|
|
}
|
|
async function runMiddlewarePipeline(args, propagateResult, handler, errorHandler) {
|
|
let { matches, request, params, context } = args;
|
|
let middlewareState = {
|
|
handlerResult: void 0
|
|
};
|
|
try {
|
|
let tuples = matches.flatMap(
|
|
(m) => m.route.unstable_middleware ? m.route.unstable_middleware.map((fn) => [m.route.id, fn]) : []
|
|
);
|
|
let result = await callRouteMiddleware(
|
|
{ request, params, context },
|
|
tuples,
|
|
propagateResult,
|
|
middlewareState,
|
|
handler
|
|
);
|
|
return propagateResult ? result : middlewareState.handlerResult;
|
|
} catch (e) {
|
|
if (!middlewareState.middlewareError) {
|
|
throw e;
|
|
}
|
|
let result = await errorHandler(
|
|
middlewareState.middlewareError.error,
|
|
middlewareState.middlewareError.routeId
|
|
);
|
|
if (propagateResult || !middlewareState.handlerResult) {
|
|
return result;
|
|
}
|
|
return Object.assign(middlewareState.handlerResult, result);
|
|
}
|
|
}
|
|
async function callRouteMiddleware(args, middlewares, propagateResult, middlewareState, handler, idx = 0) {
|
|
let { request } = args;
|
|
if (request.signal.aborted) {
|
|
if (request.signal.reason) {
|
|
throw request.signal.reason;
|
|
}
|
|
throw new Error(
|
|
`Request aborted without an \`AbortSignal.reason\`: ${request.method} ${request.url}`
|
|
);
|
|
}
|
|
let tuple = middlewares[idx];
|
|
if (!tuple) {
|
|
middlewareState.handlerResult = await handler();
|
|
return middlewareState.handlerResult;
|
|
}
|
|
let [routeId, middleware] = tuple;
|
|
let nextCalled = false;
|
|
let nextResult = void 0;
|
|
let next = async () => {
|
|
if (nextCalled) {
|
|
throw new Error("You may only call `next()` once per middleware");
|
|
}
|
|
nextCalled = true;
|
|
let result = await callRouteMiddleware(
|
|
args,
|
|
middlewares,
|
|
propagateResult,
|
|
middlewareState,
|
|
handler,
|
|
idx + 1
|
|
);
|
|
if (propagateResult) {
|
|
nextResult = result;
|
|
return nextResult;
|
|
}
|
|
};
|
|
try {
|
|
let result = await middleware(
|
|
{
|
|
request: args.request,
|
|
params: args.params,
|
|
context: args.context
|
|
},
|
|
next
|
|
);
|
|
if (nextCalled) {
|
|
if (result === void 0) {
|
|
return nextResult;
|
|
} else {
|
|
return result;
|
|
}
|
|
} else {
|
|
return next();
|
|
}
|
|
} catch (error) {
|
|
if (!middlewareState.middlewareError) {
|
|
middlewareState.middlewareError = { routeId, error };
|
|
} else if (middlewareState.middlewareError.error !== error) {
|
|
middlewareState.middlewareError = { routeId, error };
|
|
}
|
|
throw error;
|
|
}
|
|
}
|
|
function getDataStrategyMatchLazyPromises(mapRouteProperties, manifest, request, match, lazyRoutePropertiesToSkip) {
|
|
let lazyMiddlewarePromise = loadLazyRouteProperty({
|
|
key: "unstable_middleware",
|
|
route: match.route,
|
|
manifest,
|
|
mapRouteProperties
|
|
});
|
|
let lazyRoutePromises = loadLazyRoute(
|
|
match.route,
|
|
isMutationMethod(request.method) ? "action" : "loader",
|
|
manifest,
|
|
mapRouteProperties,
|
|
lazyRoutePropertiesToSkip
|
|
);
|
|
return {
|
|
middleware: lazyMiddlewarePromise,
|
|
route: lazyRoutePromises.lazyRoutePromise,
|
|
handler: lazyRoutePromises.lazyHandlerPromise
|
|
};
|
|
}
|
|
function getDataStrategyMatch(mapRouteProperties, manifest, request, match, lazyRoutePropertiesToSkip, scopedContext, shouldLoad, unstable_shouldRevalidateArgs = null) {
|
|
let isUsingNewApi = false;
|
|
let _lazyPromises = getDataStrategyMatchLazyPromises(
|
|
mapRouteProperties,
|
|
manifest,
|
|
request,
|
|
match,
|
|
lazyRoutePropertiesToSkip
|
|
);
|
|
return {
|
|
...match,
|
|
_lazyPromises,
|
|
shouldLoad,
|
|
unstable_shouldRevalidateArgs,
|
|
unstable_shouldCallHandler(defaultShouldRevalidate) {
|
|
isUsingNewApi = true;
|
|
if (!unstable_shouldRevalidateArgs) {
|
|
return shouldLoad;
|
|
}
|
|
if (typeof defaultShouldRevalidate === "boolean") {
|
|
return shouldRevalidateLoader(match, {
|
|
...unstable_shouldRevalidateArgs,
|
|
defaultShouldRevalidate
|
|
});
|
|
}
|
|
return shouldRevalidateLoader(match, unstable_shouldRevalidateArgs);
|
|
},
|
|
resolve(handlerOverride) {
|
|
if (isUsingNewApi || shouldLoad || handlerOverride && !isMutationMethod(request.method) && (match.route.lazy || match.route.loader)) {
|
|
return callLoaderOrAction({
|
|
request,
|
|
match,
|
|
lazyHandlerPromise: _optionalChain([_lazyPromises, 'optionalAccess', _37 => _37.handler]),
|
|
lazyRoutePromise: _optionalChain([_lazyPromises, 'optionalAccess', _38 => _38.route]),
|
|
handlerOverride,
|
|
scopedContext
|
|
});
|
|
}
|
|
return Promise.resolve({ type: "data" /* data */, result: void 0 });
|
|
}
|
|
};
|
|
}
|
|
function getTargetedDataStrategyMatches(mapRouteProperties, manifest, request, matches, targetMatch, lazyRoutePropertiesToSkip, scopedContext, shouldRevalidateArgs = null) {
|
|
return matches.map((match) => {
|
|
if (match.route.id !== targetMatch.route.id) {
|
|
return {
|
|
...match,
|
|
shouldLoad: false,
|
|
unstable_shouldRevalidateArgs: shouldRevalidateArgs,
|
|
unstable_shouldCallHandler: () => false,
|
|
_lazyPromises: getDataStrategyMatchLazyPromises(
|
|
mapRouteProperties,
|
|
manifest,
|
|
request,
|
|
match,
|
|
lazyRoutePropertiesToSkip
|
|
),
|
|
resolve: () => Promise.resolve({ type: "data", result: void 0 })
|
|
};
|
|
}
|
|
return getDataStrategyMatch(
|
|
mapRouteProperties,
|
|
manifest,
|
|
request,
|
|
match,
|
|
lazyRoutePropertiesToSkip,
|
|
scopedContext,
|
|
true,
|
|
shouldRevalidateArgs
|
|
);
|
|
});
|
|
}
|
|
async function callDataStrategyImpl(dataStrategyImpl, request, matches, fetcherKey, scopedContext, isStaticHandler) {
|
|
if (matches.some((m) => _optionalChain([m, 'access', _39 => _39._lazyPromises, 'optionalAccess', _40 => _40.middleware]))) {
|
|
await Promise.all(matches.map((m) => _optionalChain([m, 'access', _41 => _41._lazyPromises, 'optionalAccess', _42 => _42.middleware])));
|
|
}
|
|
let dataStrategyArgs = {
|
|
request,
|
|
params: matches[0].params,
|
|
context: scopedContext,
|
|
matches
|
|
};
|
|
let unstable_runClientMiddleware = isStaticHandler ? () => {
|
|
throw new Error(
|
|
"You cannot call `unstable_runClientMiddleware()` from a static handler `dataStrategy`. Middleware is run outside of `dataStrategy` during SSR in order to bubble up the Response. You can enable middleware via the `respond` API in `query`/`queryRoute`"
|
|
);
|
|
} : (cb) => {
|
|
let typedDataStrategyArgs = dataStrategyArgs;
|
|
return runMiddlewarePipeline(
|
|
typedDataStrategyArgs,
|
|
false,
|
|
() => cb({
|
|
...typedDataStrategyArgs,
|
|
fetcherKey,
|
|
unstable_runClientMiddleware: () => {
|
|
throw new Error(
|
|
"Cannot call `unstable_runClientMiddleware()` from within an `unstable_runClientMiddleware` handler"
|
|
);
|
|
}
|
|
}),
|
|
(error, routeId) => ({
|
|
[routeId]: { type: "error", result: error }
|
|
})
|
|
);
|
|
};
|
|
let results = await dataStrategyImpl({
|
|
...dataStrategyArgs,
|
|
fetcherKey,
|
|
unstable_runClientMiddleware
|
|
});
|
|
try {
|
|
await Promise.all(
|
|
matches.flatMap((m) => [
|
|
_optionalChain([m, 'access', _43 => _43._lazyPromises, 'optionalAccess', _44 => _44.handler]),
|
|
_optionalChain([m, 'access', _45 => _45._lazyPromises, 'optionalAccess', _46 => _46.route])
|
|
])
|
|
);
|
|
} catch (e) {
|
|
}
|
|
return results;
|
|
}
|
|
async function callLoaderOrAction({
|
|
request,
|
|
match,
|
|
lazyHandlerPromise,
|
|
lazyRoutePromise,
|
|
handlerOverride,
|
|
scopedContext
|
|
}) {
|
|
let result;
|
|
let onReject;
|
|
let isAction = isMutationMethod(request.method);
|
|
let type = isAction ? "action" : "loader";
|
|
let runHandler = (handler) => {
|
|
let reject;
|
|
let abortPromise = new Promise((_, r) => reject = r);
|
|
onReject = () => reject();
|
|
request.signal.addEventListener("abort", onReject);
|
|
let actualHandler = (ctx) => {
|
|
if (typeof handler !== "function") {
|
|
return Promise.reject(
|
|
new Error(
|
|
`You cannot call the handler for a route which defines a boolean "${type}" [routeId: ${match.route.id}]`
|
|
)
|
|
);
|
|
}
|
|
return handler(
|
|
{
|
|
request,
|
|
params: match.params,
|
|
context: scopedContext
|
|
},
|
|
...ctx !== void 0 ? [ctx] : []
|
|
);
|
|
};
|
|
let handlerPromise = (async () => {
|
|
try {
|
|
let val = await (handlerOverride ? handlerOverride((ctx) => actualHandler(ctx)) : actualHandler());
|
|
return { type: "data", result: val };
|
|
} catch (e) {
|
|
return { type: "error", result: e };
|
|
}
|
|
})();
|
|
return Promise.race([handlerPromise, abortPromise]);
|
|
};
|
|
try {
|
|
let handler = isAction ? match.route.action : match.route.loader;
|
|
if (lazyHandlerPromise || lazyRoutePromise) {
|
|
if (handler) {
|
|
let handlerError;
|
|
let [value] = await Promise.all([
|
|
// If the handler throws, don't let it immediately bubble out,
|
|
// since we need to let the lazy() execution finish so we know if this
|
|
// route has a boundary that can handle the error
|
|
runHandler(handler).catch((e) => {
|
|
handlerError = e;
|
|
}),
|
|
// Ensure all lazy route promises are resolved before continuing
|
|
lazyHandlerPromise,
|
|
lazyRoutePromise
|
|
]);
|
|
if (handlerError !== void 0) {
|
|
throw handlerError;
|
|
}
|
|
result = value;
|
|
} else {
|
|
await lazyHandlerPromise;
|
|
let handler2 = isAction ? match.route.action : match.route.loader;
|
|
if (handler2) {
|
|
[result] = await Promise.all([runHandler(handler2), lazyRoutePromise]);
|
|
} else if (type === "action") {
|
|
let url = new URL(request.url);
|
|
let pathname = url.pathname + url.search;
|
|
throw getInternalRouterError(405, {
|
|
method: request.method,
|
|
pathname,
|
|
routeId: match.route.id
|
|
});
|
|
} else {
|
|
return { type: "data" /* data */, result: void 0 };
|
|
}
|
|
}
|
|
} else if (!handler) {
|
|
let url = new URL(request.url);
|
|
let pathname = url.pathname + url.search;
|
|
throw getInternalRouterError(404, {
|
|
pathname
|
|
});
|
|
} else {
|
|
result = await runHandler(handler);
|
|
}
|
|
} catch (e) {
|
|
return { type: "error" /* error */, result: e };
|
|
} finally {
|
|
if (onReject) {
|
|
request.signal.removeEventListener("abort", onReject);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
async function convertDataStrategyResultToDataResult(dataStrategyResult) {
|
|
let { result, type } = dataStrategyResult;
|
|
if (isResponse(result)) {
|
|
let data2;
|
|
try {
|
|
let contentType = result.headers.get("Content-Type");
|
|
if (contentType && /\bapplication\/json\b/.test(contentType)) {
|
|
if (result.body == null) {
|
|
data2 = null;
|
|
} else {
|
|
data2 = await result.json();
|
|
}
|
|
} else {
|
|
data2 = await result.text();
|
|
}
|
|
} catch (e) {
|
|
return { type: "error" /* error */, error: e };
|
|
}
|
|
if (type === "error" /* error */) {
|
|
return {
|
|
type: "error" /* error */,
|
|
error: new ErrorResponseImpl(result.status, result.statusText, data2),
|
|
statusCode: result.status,
|
|
headers: result.headers
|
|
};
|
|
}
|
|
return {
|
|
type: "data" /* data */,
|
|
data: data2,
|
|
statusCode: result.status,
|
|
headers: result.headers
|
|
};
|
|
}
|
|
if (type === "error" /* error */) {
|
|
if (isDataWithResponseInit(result)) {
|
|
if (result.data instanceof Error) {
|
|
return {
|
|
type: "error" /* error */,
|
|
error: result.data,
|
|
statusCode: _optionalChain([result, 'access', _47 => _47.init, 'optionalAccess', _48 => _48.status]),
|
|
headers: _optionalChain([result, 'access', _49 => _49.init, 'optionalAccess', _50 => _50.headers]) ? new Headers(result.init.headers) : void 0
|
|
};
|
|
}
|
|
return {
|
|
type: "error" /* error */,
|
|
error: new ErrorResponseImpl(
|
|
_optionalChain([result, 'access', _51 => _51.init, 'optionalAccess', _52 => _52.status]) || 500,
|
|
void 0,
|
|
result.data
|
|
),
|
|
statusCode: isRouteErrorResponse(result) ? result.status : void 0,
|
|
headers: _optionalChain([result, 'access', _53 => _53.init, 'optionalAccess', _54 => _54.headers]) ? new Headers(result.init.headers) : void 0
|
|
};
|
|
}
|
|
return {
|
|
type: "error" /* error */,
|
|
error: result,
|
|
statusCode: isRouteErrorResponse(result) ? result.status : void 0
|
|
};
|
|
}
|
|
if (isDataWithResponseInit(result)) {
|
|
return {
|
|
type: "data" /* data */,
|
|
data: result.data,
|
|
statusCode: _optionalChain([result, 'access', _55 => _55.init, 'optionalAccess', _56 => _56.status]),
|
|
headers: _optionalChain([result, 'access', _57 => _57.init, 'optionalAccess', _58 => _58.headers]) ? new Headers(result.init.headers) : void 0
|
|
};
|
|
}
|
|
return { type: "data" /* data */, data: result };
|
|
}
|
|
function normalizeRelativeRoutingRedirectResponse(response, request, routeId, matches, basename) {
|
|
let location = response.headers.get("Location");
|
|
invariant(
|
|
location,
|
|
"Redirects returned/thrown from loaders/actions must have a Location header"
|
|
);
|
|
if (!isAbsoluteUrl(location)) {
|
|
let trimmedMatches = matches.slice(
|
|
0,
|
|
matches.findIndex((m) => m.route.id === routeId) + 1
|
|
);
|
|
location = normalizeTo(
|
|
new URL(request.url),
|
|
trimmedMatches,
|
|
basename,
|
|
location
|
|
);
|
|
response.headers.set("Location", location);
|
|
}
|
|
return response;
|
|
}
|
|
function normalizeRedirectLocation(location, currentUrl, basename) {
|
|
if (isAbsoluteUrl(location)) {
|
|
let normalizedLocation = location;
|
|
let url = normalizedLocation.startsWith("//") ? new URL(currentUrl.protocol + normalizedLocation) : new URL(normalizedLocation);
|
|
let isSameBasename = stripBasename(url.pathname, basename) != null;
|
|
if (url.origin === currentUrl.origin && isSameBasename) {
|
|
return url.pathname + url.search + url.hash;
|
|
}
|
|
}
|
|
return location;
|
|
}
|
|
function createClientSideRequest(history, location, signal, submission) {
|
|
let url = history.createURL(stripHashFromPath(location)).toString();
|
|
let init = { signal };
|
|
if (submission && isMutationMethod(submission.formMethod)) {
|
|
let { formMethod, formEncType } = submission;
|
|
init.method = formMethod.toUpperCase();
|
|
if (formEncType === "application/json") {
|
|
init.headers = new Headers({ "Content-Type": formEncType });
|
|
init.body = JSON.stringify(submission.json);
|
|
} else if (formEncType === "text/plain") {
|
|
init.body = submission.text;
|
|
} else if (formEncType === "application/x-www-form-urlencoded" && submission.formData) {
|
|
init.body = convertFormDataToSearchParams(submission.formData);
|
|
} else {
|
|
init.body = submission.formData;
|
|
}
|
|
}
|
|
return new Request(url, init);
|
|
}
|
|
function convertFormDataToSearchParams(formData) {
|
|
let searchParams = new URLSearchParams();
|
|
for (let [key, value] of formData.entries()) {
|
|
searchParams.append(key, typeof value === "string" ? value : value.name);
|
|
}
|
|
return searchParams;
|
|
}
|
|
function convertSearchParamsToFormData(searchParams) {
|
|
let formData = new FormData();
|
|
for (let [key, value] of searchParams.entries()) {
|
|
formData.append(key, value);
|
|
}
|
|
return formData;
|
|
}
|
|
function processRouteLoaderData(matches, results, pendingActionResult, isStaticHandler = false, skipLoaderErrorBubbling = false) {
|
|
let loaderData = {};
|
|
let errors = null;
|
|
let statusCode;
|
|
let foundError = false;
|
|
let loaderHeaders = {};
|
|
let pendingError = pendingActionResult && isErrorResult(pendingActionResult[1]) ? pendingActionResult[1].error : void 0;
|
|
matches.forEach((match) => {
|
|
if (!(match.route.id in results)) {
|
|
return;
|
|
}
|
|
let id = match.route.id;
|
|
let result = results[id];
|
|
invariant(
|
|
!isRedirectResult(result),
|
|
"Cannot handle redirect results in processLoaderData"
|
|
);
|
|
if (isErrorResult(result)) {
|
|
let error = result.error;
|
|
if (pendingError !== void 0) {
|
|
error = pendingError;
|
|
pendingError = void 0;
|
|
}
|
|
errors = errors || {};
|
|
if (skipLoaderErrorBubbling) {
|
|
errors[id] = error;
|
|
} else {
|
|
let boundaryMatch = findNearestBoundary(matches, id);
|
|
if (errors[boundaryMatch.route.id] == null) {
|
|
errors[boundaryMatch.route.id] = error;
|
|
}
|
|
}
|
|
if (!isStaticHandler) {
|
|
loaderData[id] = ResetLoaderDataSymbol;
|
|
}
|
|
if (!foundError) {
|
|
foundError = true;
|
|
statusCode = isRouteErrorResponse(result.error) ? result.error.status : 500;
|
|
}
|
|
if (result.headers) {
|
|
loaderHeaders[id] = result.headers;
|
|
}
|
|
} else {
|
|
loaderData[id] = result.data;
|
|
if (result.statusCode && result.statusCode !== 200 && !foundError) {
|
|
statusCode = result.statusCode;
|
|
}
|
|
if (result.headers) {
|
|
loaderHeaders[id] = result.headers;
|
|
}
|
|
}
|
|
});
|
|
if (pendingError !== void 0 && pendingActionResult) {
|
|
errors = { [pendingActionResult[0]]: pendingError };
|
|
if (pendingActionResult[2]) {
|
|
loaderData[pendingActionResult[2]] = void 0;
|
|
}
|
|
}
|
|
return {
|
|
loaderData,
|
|
errors,
|
|
statusCode: statusCode || 200,
|
|
loaderHeaders
|
|
};
|
|
}
|
|
function processLoaderData(state, matches, results, pendingActionResult, revalidatingFetchers, fetcherResults) {
|
|
let { loaderData, errors } = processRouteLoaderData(
|
|
matches,
|
|
results,
|
|
pendingActionResult
|
|
);
|
|
revalidatingFetchers.filter((f) => !f.matches || f.matches.some((m) => m.shouldLoad)).forEach((rf) => {
|
|
let { key, match, controller } = rf;
|
|
let result = fetcherResults[key];
|
|
invariant(result, "Did not find corresponding fetcher result");
|
|
if (controller && controller.signal.aborted) {
|
|
return;
|
|
} else if (isErrorResult(result)) {
|
|
let boundaryMatch = findNearestBoundary(state.matches, _optionalChain([match, 'optionalAccess', _59 => _59.route, 'access', _60 => _60.id]));
|
|
if (!(errors && errors[boundaryMatch.route.id])) {
|
|
errors = {
|
|
...errors,
|
|
[boundaryMatch.route.id]: result.error
|
|
};
|
|
}
|
|
state.fetchers.delete(key);
|
|
} else if (isRedirectResult(result)) {
|
|
invariant(false, "Unhandled fetcher revalidation redirect");
|
|
} else {
|
|
let doneFetcher = getDoneFetcher(result.data);
|
|
state.fetchers.set(key, doneFetcher);
|
|
}
|
|
});
|
|
return { loaderData, errors };
|
|
}
|
|
function mergeLoaderData(loaderData, newLoaderData, matches, errors) {
|
|
let mergedLoaderData = Object.entries(newLoaderData).filter(([, v]) => v !== ResetLoaderDataSymbol).reduce((merged, [k, v]) => {
|
|
merged[k] = v;
|
|
return merged;
|
|
}, {});
|
|
for (let match of matches) {
|
|
let id = match.route.id;
|
|
if (!newLoaderData.hasOwnProperty(id) && loaderData.hasOwnProperty(id) && match.route.loader) {
|
|
mergedLoaderData[id] = loaderData[id];
|
|
}
|
|
if (errors && errors.hasOwnProperty(id)) {
|
|
break;
|
|
}
|
|
}
|
|
return mergedLoaderData;
|
|
}
|
|
function getActionDataForCommit(pendingActionResult) {
|
|
if (!pendingActionResult) {
|
|
return {};
|
|
}
|
|
return isErrorResult(pendingActionResult[1]) ? {
|
|
// Clear out prior actionData on errors
|
|
actionData: {}
|
|
} : {
|
|
actionData: {
|
|
[pendingActionResult[0]]: pendingActionResult[1].data
|
|
}
|
|
};
|
|
}
|
|
function findNearestBoundary(matches, routeId) {
|
|
let eligibleMatches = routeId ? matches.slice(0, matches.findIndex((m) => m.route.id === routeId) + 1) : [...matches];
|
|
return eligibleMatches.reverse().find((m) => m.route.hasErrorBoundary === true) || matches[0];
|
|
}
|
|
function getShortCircuitMatches(routes) {
|
|
let route = routes.length === 1 ? routes[0] : routes.find((r) => r.index || !r.path || r.path === "/") || {
|
|
id: `__shim-error-route__`
|
|
};
|
|
return {
|
|
matches: [
|
|
{
|
|
params: {},
|
|
pathname: "",
|
|
pathnameBase: "",
|
|
route
|
|
}
|
|
],
|
|
route
|
|
};
|
|
}
|
|
function getInternalRouterError(status, {
|
|
pathname,
|
|
routeId,
|
|
method,
|
|
type,
|
|
message
|
|
} = {}) {
|
|
let statusText = "Unknown Server Error";
|
|
let errorMessage = "Unknown @remix-run/router error";
|
|
if (status === 400) {
|
|
statusText = "Bad Request";
|
|
if (method && pathname && routeId) {
|
|
errorMessage = `You made a ${method} request to "${pathname}" but did not provide a \`loader\` for route "${routeId}", so there is no way to handle the request.`;
|
|
} else if (type === "invalid-body") {
|
|
errorMessage = "Unable to encode submission body";
|
|
}
|
|
} else if (status === 403) {
|
|
statusText = "Forbidden";
|
|
errorMessage = `Route "${routeId}" does not match URL "${pathname}"`;
|
|
} else if (status === 404) {
|
|
statusText = "Not Found";
|
|
errorMessage = `No route matches URL "${pathname}"`;
|
|
} else if (status === 405) {
|
|
statusText = "Method Not Allowed";
|
|
if (method && pathname && routeId) {
|
|
errorMessage = `You made a ${method.toUpperCase()} request to "${pathname}" but did not provide an \`action\` for route "${routeId}", so there is no way to handle the request.`;
|
|
} else if (method) {
|
|
errorMessage = `Invalid request method "${method.toUpperCase()}"`;
|
|
}
|
|
}
|
|
return new ErrorResponseImpl(
|
|
status || 500,
|
|
statusText,
|
|
new Error(errorMessage),
|
|
true
|
|
);
|
|
}
|
|
function findRedirect(results) {
|
|
let entries = Object.entries(results);
|
|
for (let i = entries.length - 1; i >= 0; i--) {
|
|
let [key, result] = entries[i];
|
|
if (isRedirectResult(result)) {
|
|
return { key, result };
|
|
}
|
|
}
|
|
}
|
|
function stripHashFromPath(path) {
|
|
let parsedPath = typeof path === "string" ? parsePath(path) : path;
|
|
return createPath({ ...parsedPath, hash: "" });
|
|
}
|
|
function isHashChangeOnly(a, b) {
|
|
if (a.pathname !== b.pathname || a.search !== b.search) {
|
|
return false;
|
|
}
|
|
if (a.hash === "") {
|
|
return b.hash !== "";
|
|
} else if (a.hash === b.hash) {
|
|
return true;
|
|
} else if (b.hash !== "") {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
function isDataStrategyResult(result) {
|
|
return result != null && typeof result === "object" && "type" in result && "result" in result && (result.type === "data" /* data */ || result.type === "error" /* error */);
|
|
}
|
|
function isRedirectDataStrategyResult(result) {
|
|
return isResponse(result.result) && redirectStatusCodes.has(result.result.status);
|
|
}
|
|
function isErrorResult(result) {
|
|
return result.type === "error" /* error */;
|
|
}
|
|
function isRedirectResult(result) {
|
|
return (result && result.type) === "redirect" /* redirect */;
|
|
}
|
|
function isDataWithResponseInit(value) {
|
|
return typeof value === "object" && value != null && "type" in value && "data" in value && "init" in value && value.type === "DataWithResponseInit";
|
|
}
|
|
function isResponse(value) {
|
|
return value != null && typeof value.status === "number" && typeof value.statusText === "string" && typeof value.headers === "object" && typeof value.body !== "undefined";
|
|
}
|
|
function isRedirectStatusCode(statusCode) {
|
|
return redirectStatusCodes.has(statusCode);
|
|
}
|
|
function isRedirectResponse(result) {
|
|
return isResponse(result) && isRedirectStatusCode(result.status) && result.headers.has("Location");
|
|
}
|
|
function isValidMethod(method) {
|
|
return validRequestMethods.has(method.toUpperCase());
|
|
}
|
|
function isMutationMethod(method) {
|
|
return validMutationMethods.has(method.toUpperCase());
|
|
}
|
|
function hasNakedIndexQuery(search) {
|
|
return new URLSearchParams(search).getAll("index").some((v) => v === "");
|
|
}
|
|
function getTargetMatch(matches, location) {
|
|
let search = typeof location === "string" ? parsePath(location).search : location.search;
|
|
if (matches[matches.length - 1].route.index && hasNakedIndexQuery(search || "")) {
|
|
return matches[matches.length - 1];
|
|
}
|
|
let pathMatches = getPathContributingMatches(matches);
|
|
return pathMatches[pathMatches.length - 1];
|
|
}
|
|
function getSubmissionFromNavigation(navigation) {
|
|
let { formMethod, formAction, formEncType, text, formData, json } = navigation;
|
|
if (!formMethod || !formAction || !formEncType) {
|
|
return;
|
|
}
|
|
if (text != null) {
|
|
return {
|
|
formMethod,
|
|
formAction,
|
|
formEncType,
|
|
formData: void 0,
|
|
json: void 0,
|
|
text
|
|
};
|
|
} else if (formData != null) {
|
|
return {
|
|
formMethod,
|
|
formAction,
|
|
formEncType,
|
|
formData,
|
|
json: void 0,
|
|
text: void 0
|
|
};
|
|
} else if (json !== void 0) {
|
|
return {
|
|
formMethod,
|
|
formAction,
|
|
formEncType,
|
|
formData: void 0,
|
|
json,
|
|
text: void 0
|
|
};
|
|
}
|
|
}
|
|
function getLoadingNavigation(location, submission) {
|
|
if (submission) {
|
|
let navigation = {
|
|
state: "loading",
|
|
location,
|
|
formMethod: submission.formMethod,
|
|
formAction: submission.formAction,
|
|
formEncType: submission.formEncType,
|
|
formData: submission.formData,
|
|
json: submission.json,
|
|
text: submission.text
|
|
};
|
|
return navigation;
|
|
} else {
|
|
let navigation = {
|
|
state: "loading",
|
|
location,
|
|
formMethod: void 0,
|
|
formAction: void 0,
|
|
formEncType: void 0,
|
|
formData: void 0,
|
|
json: void 0,
|
|
text: void 0
|
|
};
|
|
return navigation;
|
|
}
|
|
}
|
|
function getSubmittingNavigation(location, submission) {
|
|
let navigation = {
|
|
state: "submitting",
|
|
location,
|
|
formMethod: submission.formMethod,
|
|
formAction: submission.formAction,
|
|
formEncType: submission.formEncType,
|
|
formData: submission.formData,
|
|
json: submission.json,
|
|
text: submission.text
|
|
};
|
|
return navigation;
|
|
}
|
|
function getLoadingFetcher(submission, data2) {
|
|
if (submission) {
|
|
let fetcher = {
|
|
state: "loading",
|
|
formMethod: submission.formMethod,
|
|
formAction: submission.formAction,
|
|
formEncType: submission.formEncType,
|
|
formData: submission.formData,
|
|
json: submission.json,
|
|
text: submission.text,
|
|
data: data2
|
|
};
|
|
return fetcher;
|
|
} else {
|
|
let fetcher = {
|
|
state: "loading",
|
|
formMethod: void 0,
|
|
formAction: void 0,
|
|
formEncType: void 0,
|
|
formData: void 0,
|
|
json: void 0,
|
|
text: void 0,
|
|
data: data2
|
|
};
|
|
return fetcher;
|
|
}
|
|
}
|
|
function getSubmittingFetcher(submission, existingFetcher) {
|
|
let fetcher = {
|
|
state: "submitting",
|
|
formMethod: submission.formMethod,
|
|
formAction: submission.formAction,
|
|
formEncType: submission.formEncType,
|
|
formData: submission.formData,
|
|
json: submission.json,
|
|
text: submission.text,
|
|
data: existingFetcher ? existingFetcher.data : void 0
|
|
};
|
|
return fetcher;
|
|
}
|
|
function getDoneFetcher(data2) {
|
|
let fetcher = {
|
|
state: "idle",
|
|
formMethod: void 0,
|
|
formAction: void 0,
|
|
formEncType: void 0,
|
|
formData: void 0,
|
|
json: void 0,
|
|
text: void 0,
|
|
data: data2
|
|
};
|
|
return fetcher;
|
|
}
|
|
function restoreAppliedTransitions(_window, transitions) {
|
|
try {
|
|
let sessionPositions = _window.sessionStorage.getItem(
|
|
TRANSITIONS_STORAGE_KEY
|
|
);
|
|
if (sessionPositions) {
|
|
let json = JSON.parse(sessionPositions);
|
|
for (let [k, v] of Object.entries(json || {})) {
|
|
if (v && Array.isArray(v)) {
|
|
transitions.set(k, new Set(v || []));
|
|
}
|
|
}
|
|
}
|
|
} catch (e) {
|
|
}
|
|
}
|
|
function persistAppliedTransitions(_window, transitions) {
|
|
if (transitions.size > 0) {
|
|
let json = {};
|
|
for (let [k, v] of transitions) {
|
|
json[k] = [...v];
|
|
}
|
|
try {
|
|
_window.sessionStorage.setItem(
|
|
TRANSITIONS_STORAGE_KEY,
|
|
JSON.stringify(json)
|
|
);
|
|
} catch (error) {
|
|
warning(
|
|
false,
|
|
`Failed to save applied view transitions in sessionStorage (${error}).`
|
|
);
|
|
}
|
|
}
|
|
}
|
|
function createDeferred() {
|
|
let resolve;
|
|
let reject;
|
|
let promise = new Promise((res, rej) => {
|
|
resolve = async (val) => {
|
|
res(val);
|
|
try {
|
|
await promise;
|
|
} catch (e) {
|
|
}
|
|
};
|
|
reject = async (error) => {
|
|
rej(error);
|
|
try {
|
|
await promise;
|
|
} catch (e) {
|
|
}
|
|
};
|
|
});
|
|
return {
|
|
promise,
|
|
//@ts-ignore
|
|
resolve,
|
|
//@ts-ignore
|
|
reject
|
|
};
|
|
}
|
|
|
|
// lib/dom/ssr/single-fetch.tsx
|
|
var _react = require('react'); var React = _interopRequireWildcard(_react); var React2 = _interopRequireWildcard(_react); var React3 = _interopRequireWildcard(_react); var React8 = _interopRequireWildcard(_react); var React7 = _interopRequireWildcard(_react); var React6 = _interopRequireWildcard(_react); var React5 = _interopRequireWildcard(_react); var React4 = _interopRequireWildcard(_react);
|
|
|
|
// vendor/turbo-stream-v2/utils.ts
|
|
var HOLE = -1;
|
|
var NAN = -2;
|
|
var NEGATIVE_INFINITY = -3;
|
|
var NEGATIVE_ZERO = -4;
|
|
var NULL = -5;
|
|
var POSITIVE_INFINITY = -6;
|
|
var UNDEFINED = -7;
|
|
var TYPE_BIGINT = "B";
|
|
var TYPE_DATE = "D";
|
|
var TYPE_ERROR = "E";
|
|
var TYPE_MAP = "M";
|
|
var TYPE_NULL_OBJECT = "N";
|
|
var TYPE_PROMISE = "P";
|
|
var TYPE_REGEXP = "R";
|
|
var TYPE_SET = "S";
|
|
var TYPE_SYMBOL = "Y";
|
|
var TYPE_URL = "U";
|
|
var TYPE_PREVIOUS_RESOLVED = "Z";
|
|
var Deferred = class {
|
|
constructor() {
|
|
this.promise = new Promise((resolve, reject) => {
|
|
this.resolve = resolve;
|
|
this.reject = reject;
|
|
});
|
|
}
|
|
};
|
|
function createLineSplittingTransform() {
|
|
const decoder = new TextDecoder();
|
|
let leftover = "";
|
|
return new TransformStream({
|
|
transform(chunk, controller) {
|
|
const str = decoder.decode(chunk, { stream: true });
|
|
const parts = (leftover + str).split("\n");
|
|
leftover = parts.pop() || "";
|
|
for (const part of parts) {
|
|
controller.enqueue(part);
|
|
}
|
|
},
|
|
flush(controller) {
|
|
if (leftover) {
|
|
controller.enqueue(leftover);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// vendor/turbo-stream-v2/flatten.ts
|
|
function flatten(input) {
|
|
const { indices } = this;
|
|
const existing = indices.get(input);
|
|
if (existing) return [existing];
|
|
if (input === void 0) return UNDEFINED;
|
|
if (input === null) return NULL;
|
|
if (Number.isNaN(input)) return NAN;
|
|
if (input === Number.POSITIVE_INFINITY) return POSITIVE_INFINITY;
|
|
if (input === Number.NEGATIVE_INFINITY) return NEGATIVE_INFINITY;
|
|
if (input === 0 && 1 / input < 0) return NEGATIVE_ZERO;
|
|
const index = this.index++;
|
|
indices.set(input, index);
|
|
stringify.call(this, input, index);
|
|
return index;
|
|
}
|
|
function stringify(input, index) {
|
|
const { deferred, plugins, postPlugins } = this;
|
|
const str = this.stringified;
|
|
const stack = [[input, index]];
|
|
while (stack.length > 0) {
|
|
const [input2, index2] = stack.pop();
|
|
const partsForObj = (obj) => Object.keys(obj).map((k) => `"_${flatten.call(this, k)}":${flatten.call(this, obj[k])}`).join(",");
|
|
let error = null;
|
|
switch (typeof input2) {
|
|
case "boolean":
|
|
case "number":
|
|
case "string":
|
|
str[index2] = JSON.stringify(input2);
|
|
break;
|
|
case "bigint":
|
|
str[index2] = `["${TYPE_BIGINT}","${input2}"]`;
|
|
break;
|
|
case "symbol": {
|
|
const keyFor = Symbol.keyFor(input2);
|
|
if (!keyFor) {
|
|
error = new Error(
|
|
"Cannot encode symbol unless created with Symbol.for()"
|
|
);
|
|
} else {
|
|
str[index2] = `["${TYPE_SYMBOL}",${JSON.stringify(keyFor)}]`;
|
|
}
|
|
break;
|
|
}
|
|
case "object": {
|
|
if (!input2) {
|
|
str[index2] = `${NULL}`;
|
|
break;
|
|
}
|
|
const isArray = Array.isArray(input2);
|
|
let pluginHandled = false;
|
|
if (!isArray && plugins) {
|
|
for (const plugin of plugins) {
|
|
const pluginResult = plugin(input2);
|
|
if (Array.isArray(pluginResult)) {
|
|
pluginHandled = true;
|
|
const [pluginIdentifier, ...rest] = pluginResult;
|
|
str[index2] = `[${JSON.stringify(pluginIdentifier)}`;
|
|
if (rest.length > 0) {
|
|
str[index2] += `,${rest.map((v) => flatten.call(this, v)).join(",")}`;
|
|
}
|
|
str[index2] += "]";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!pluginHandled) {
|
|
let result = isArray ? "[" : "{";
|
|
if (isArray) {
|
|
for (let i = 0; i < input2.length; i++)
|
|
result += (i ? "," : "") + (i in input2 ? flatten.call(this, input2[i]) : HOLE);
|
|
str[index2] = `${result}]`;
|
|
} else if (input2 instanceof Date) {
|
|
const dateTime = input2.getTime();
|
|
str[index2] = `["${TYPE_DATE}",${Number.isNaN(dateTime) ? JSON.stringify("invalid") : dateTime}]`;
|
|
} else if (input2 instanceof URL) {
|
|
str[index2] = `["${TYPE_URL}",${JSON.stringify(input2.href)}]`;
|
|
} else if (input2 instanceof RegExp) {
|
|
str[index2] = `["${TYPE_REGEXP}",${JSON.stringify(
|
|
input2.source
|
|
)},${JSON.stringify(input2.flags)}]`;
|
|
} else if (input2 instanceof Set) {
|
|
if (input2.size > 0) {
|
|
str[index2] = `["${TYPE_SET}",${[...input2].map((val) => flatten.call(this, val)).join(",")}]`;
|
|
} else {
|
|
str[index2] = `["${TYPE_SET}"]`;
|
|
}
|
|
} else if (input2 instanceof Map) {
|
|
if (input2.size > 0) {
|
|
str[index2] = `["${TYPE_MAP}",${[...input2].flatMap(([k, v]) => [
|
|
flatten.call(this, k),
|
|
flatten.call(this, v)
|
|
]).join(",")}]`;
|
|
} else {
|
|
str[index2] = `["${TYPE_MAP}"]`;
|
|
}
|
|
} else if (input2 instanceof Promise) {
|
|
str[index2] = `["${TYPE_PROMISE}",${index2}]`;
|
|
deferred[index2] = input2;
|
|
} else if (input2 instanceof Error) {
|
|
str[index2] = `["${TYPE_ERROR}",${JSON.stringify(input2.message)}`;
|
|
if (input2.name !== "Error") {
|
|
str[index2] += `,${JSON.stringify(input2.name)}`;
|
|
}
|
|
str[index2] += "]";
|
|
} else if (Object.getPrototypeOf(input2) === null) {
|
|
str[index2] = `["${TYPE_NULL_OBJECT}",{${partsForObj(input2)}}]`;
|
|
} else if (isPlainObject(input2)) {
|
|
str[index2] = `{${partsForObj(input2)}}`;
|
|
} else {
|
|
error = new Error("Cannot encode object with prototype");
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
const isArray = Array.isArray(input2);
|
|
let pluginHandled = false;
|
|
if (!isArray && plugins) {
|
|
for (const plugin of plugins) {
|
|
const pluginResult = plugin(input2);
|
|
if (Array.isArray(pluginResult)) {
|
|
pluginHandled = true;
|
|
const [pluginIdentifier, ...rest] = pluginResult;
|
|
str[index2] = `[${JSON.stringify(pluginIdentifier)}`;
|
|
if (rest.length > 0) {
|
|
str[index2] += `,${rest.map((v) => flatten.call(this, v)).join(",")}`;
|
|
}
|
|
str[index2] += "]";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!pluginHandled) {
|
|
error = new Error("Cannot encode function or unexpected type");
|
|
}
|
|
}
|
|
}
|
|
if (error) {
|
|
let pluginHandled = false;
|
|
if (postPlugins) {
|
|
for (const plugin of postPlugins) {
|
|
const pluginResult = plugin(input2);
|
|
if (Array.isArray(pluginResult)) {
|
|
pluginHandled = true;
|
|
const [pluginIdentifier, ...rest] = pluginResult;
|
|
str[index2] = `[${JSON.stringify(pluginIdentifier)}`;
|
|
if (rest.length > 0) {
|
|
str[index2] += `,${rest.map((v) => flatten.call(this, v)).join(",")}`;
|
|
}
|
|
str[index2] += "]";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!pluginHandled) {
|
|
throw error;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
var objectProtoNames = Object.getOwnPropertyNames(Object.prototype).sort().join("\0");
|
|
function isPlainObject(thing) {
|
|
const proto = Object.getPrototypeOf(thing);
|
|
return proto === Object.prototype || proto === null || Object.getOwnPropertyNames(proto).sort().join("\0") === objectProtoNames;
|
|
}
|
|
|
|
// vendor/turbo-stream-v2/unflatten.ts
|
|
var globalObj = typeof window !== "undefined" ? window : typeof globalThis !== "undefined" ? globalThis : void 0;
|
|
function unflatten(parsed) {
|
|
const { hydrated, values } = this;
|
|
if (typeof parsed === "number") return hydrate.call(this, parsed);
|
|
if (!Array.isArray(parsed) || !parsed.length) throw new SyntaxError();
|
|
const startIndex = values.length;
|
|
for (const value of parsed) {
|
|
values.push(value);
|
|
}
|
|
hydrated.length = values.length;
|
|
return hydrate.call(this, startIndex);
|
|
}
|
|
function hydrate(index) {
|
|
const { hydrated, values, deferred, plugins } = this;
|
|
let result;
|
|
const stack = [
|
|
[
|
|
index,
|
|
(v) => {
|
|
result = v;
|
|
}
|
|
]
|
|
];
|
|
let postRun = [];
|
|
while (stack.length > 0) {
|
|
const [index2, set] = stack.pop();
|
|
switch (index2) {
|
|
case UNDEFINED:
|
|
set(void 0);
|
|
continue;
|
|
case NULL:
|
|
set(null);
|
|
continue;
|
|
case NAN:
|
|
set(NaN);
|
|
continue;
|
|
case POSITIVE_INFINITY:
|
|
set(Infinity);
|
|
continue;
|
|
case NEGATIVE_INFINITY:
|
|
set(-Infinity);
|
|
continue;
|
|
case NEGATIVE_ZERO:
|
|
set(-0);
|
|
continue;
|
|
}
|
|
if (hydrated[index2]) {
|
|
set(hydrated[index2]);
|
|
continue;
|
|
}
|
|
const value = values[index2];
|
|
if (!value || typeof value !== "object") {
|
|
hydrated[index2] = value;
|
|
set(value);
|
|
continue;
|
|
}
|
|
if (Array.isArray(value)) {
|
|
if (typeof value[0] === "string") {
|
|
const [type, b, c] = value;
|
|
switch (type) {
|
|
case TYPE_DATE:
|
|
set(hydrated[index2] = new Date(b));
|
|
continue;
|
|
case TYPE_URL:
|
|
set(hydrated[index2] = new URL(b));
|
|
continue;
|
|
case TYPE_BIGINT:
|
|
set(hydrated[index2] = BigInt(b));
|
|
continue;
|
|
case TYPE_REGEXP:
|
|
set(hydrated[index2] = new RegExp(b, c));
|
|
continue;
|
|
case TYPE_SYMBOL:
|
|
set(hydrated[index2] = Symbol.for(b));
|
|
continue;
|
|
case TYPE_SET:
|
|
const newSet = /* @__PURE__ */ new Set();
|
|
hydrated[index2] = newSet;
|
|
for (let i = value.length - 1; i > 0; i--)
|
|
stack.push([
|
|
value[i],
|
|
(v) => {
|
|
newSet.add(v);
|
|
}
|
|
]);
|
|
set(newSet);
|
|
continue;
|
|
case TYPE_MAP:
|
|
const map = /* @__PURE__ */ new Map();
|
|
hydrated[index2] = map;
|
|
for (let i = value.length - 2; i > 0; i -= 2) {
|
|
const r = [];
|
|
stack.push([
|
|
value[i + 1],
|
|
(v) => {
|
|
r[1] = v;
|
|
}
|
|
]);
|
|
stack.push([
|
|
value[i],
|
|
(k) => {
|
|
r[0] = k;
|
|
}
|
|
]);
|
|
postRun.push(() => {
|
|
map.set(r[0], r[1]);
|
|
});
|
|
}
|
|
set(map);
|
|
continue;
|
|
case TYPE_NULL_OBJECT:
|
|
const obj = /* @__PURE__ */ Object.create(null);
|
|
hydrated[index2] = obj;
|
|
for (const key of Object.keys(b).reverse()) {
|
|
const r = [];
|
|
stack.push([
|
|
b[key],
|
|
(v) => {
|
|
r[1] = v;
|
|
}
|
|
]);
|
|
stack.push([
|
|
Number(key.slice(1)),
|
|
(k) => {
|
|
r[0] = k;
|
|
}
|
|
]);
|
|
postRun.push(() => {
|
|
obj[r[0]] = r[1];
|
|
});
|
|
}
|
|
set(obj);
|
|
continue;
|
|
case TYPE_PROMISE:
|
|
if (hydrated[b]) {
|
|
set(hydrated[index2] = hydrated[b]);
|
|
} else {
|
|
const d = new Deferred();
|
|
deferred[b] = d;
|
|
set(hydrated[index2] = d.promise);
|
|
}
|
|
continue;
|
|
case TYPE_ERROR:
|
|
const [, message, errorType] = value;
|
|
let error = errorType && globalObj && globalObj[errorType] ? new globalObj[errorType](message) : new Error(message);
|
|
hydrated[index2] = error;
|
|
set(error);
|
|
continue;
|
|
case TYPE_PREVIOUS_RESOLVED:
|
|
set(hydrated[index2] = hydrated[b]);
|
|
continue;
|
|
default:
|
|
if (Array.isArray(plugins)) {
|
|
const r = [];
|
|
const vals = value.slice(1);
|
|
for (let i = 0; i < vals.length; i++) {
|
|
const v = vals[i];
|
|
stack.push([
|
|
v,
|
|
(v2) => {
|
|
r[i] = v2;
|
|
}
|
|
]);
|
|
}
|
|
postRun.push(() => {
|
|
for (const plugin of plugins) {
|
|
const result2 = plugin(value[0], ...r);
|
|
if (result2) {
|
|
set(hydrated[index2] = result2.value);
|
|
return;
|
|
}
|
|
}
|
|
throw new SyntaxError();
|
|
});
|
|
continue;
|
|
}
|
|
throw new SyntaxError();
|
|
}
|
|
} else {
|
|
const array = [];
|
|
hydrated[index2] = array;
|
|
for (let i = 0; i < value.length; i++) {
|
|
const n = value[i];
|
|
if (n !== HOLE) {
|
|
stack.push([
|
|
n,
|
|
(v) => {
|
|
array[i] = v;
|
|
}
|
|
]);
|
|
}
|
|
}
|
|
set(array);
|
|
continue;
|
|
}
|
|
} else {
|
|
const object = {};
|
|
hydrated[index2] = object;
|
|
for (const key of Object.keys(value).reverse()) {
|
|
const r = [];
|
|
stack.push([
|
|
value[key],
|
|
(v) => {
|
|
r[1] = v;
|
|
}
|
|
]);
|
|
stack.push([
|
|
Number(key.slice(1)),
|
|
(k) => {
|
|
r[0] = k;
|
|
}
|
|
]);
|
|
postRun.push(() => {
|
|
object[r[0]] = r[1];
|
|
});
|
|
}
|
|
set(object);
|
|
continue;
|
|
}
|
|
}
|
|
while (postRun.length > 0) {
|
|
postRun.pop()();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// vendor/turbo-stream-v2/turbo-stream.ts
|
|
async function decode(readable, options) {
|
|
const { plugins } = _nullishCoalesce(options, () => ( {}));
|
|
const done = new Deferred();
|
|
const reader = readable.pipeThrough(createLineSplittingTransform()).getReader();
|
|
const decoder = {
|
|
values: [],
|
|
hydrated: [],
|
|
deferred: {},
|
|
plugins
|
|
};
|
|
const decoded = await decodeInitial.call(decoder, reader);
|
|
let donePromise = done.promise;
|
|
if (decoded.done) {
|
|
done.resolve();
|
|
} else {
|
|
donePromise = decodeDeferred.call(decoder, reader).then(done.resolve).catch((reason) => {
|
|
for (const deferred of Object.values(decoder.deferred)) {
|
|
deferred.reject(reason);
|
|
}
|
|
done.reject(reason);
|
|
});
|
|
}
|
|
return {
|
|
done: donePromise.then(() => reader.closed),
|
|
value: decoded.value
|
|
};
|
|
}
|
|
async function decodeInitial(reader) {
|
|
const read = await reader.read();
|
|
if (!read.value) {
|
|
throw new SyntaxError();
|
|
}
|
|
let line;
|
|
try {
|
|
line = JSON.parse(read.value);
|
|
} catch (reason) {
|
|
throw new SyntaxError();
|
|
}
|
|
return {
|
|
done: read.done,
|
|
value: unflatten.call(this, line)
|
|
};
|
|
}
|
|
async function decodeDeferred(reader) {
|
|
let read = await reader.read();
|
|
while (!read.done) {
|
|
if (!read.value) continue;
|
|
const line = read.value;
|
|
switch (line[0]) {
|
|
case TYPE_PROMISE: {
|
|
const colonIndex = line.indexOf(":");
|
|
const deferredId = Number(line.slice(1, colonIndex));
|
|
const deferred = this.deferred[deferredId];
|
|
if (!deferred) {
|
|
throw new Error(`Deferred ID ${deferredId} not found in stream`);
|
|
}
|
|
const lineData = line.slice(colonIndex + 1);
|
|
let jsonLine;
|
|
try {
|
|
jsonLine = JSON.parse(lineData);
|
|
} catch (reason) {
|
|
throw new SyntaxError();
|
|
}
|
|
const value = unflatten.call(this, jsonLine);
|
|
deferred.resolve(value);
|
|
break;
|
|
}
|
|
case TYPE_ERROR: {
|
|
const colonIndex = line.indexOf(":");
|
|
const deferredId = Number(line.slice(1, colonIndex));
|
|
const deferred = this.deferred[deferredId];
|
|
if (!deferred) {
|
|
throw new Error(`Deferred ID ${deferredId} not found in stream`);
|
|
}
|
|
const lineData = line.slice(colonIndex + 1);
|
|
let jsonLine;
|
|
try {
|
|
jsonLine = JSON.parse(lineData);
|
|
} catch (reason) {
|
|
throw new SyntaxError();
|
|
}
|
|
const value = unflatten.call(this, jsonLine);
|
|
deferred.reject(value);
|
|
break;
|
|
}
|
|
default:
|
|
throw new SyntaxError();
|
|
}
|
|
read = await reader.read();
|
|
}
|
|
}
|
|
function encode(input, options) {
|
|
const { plugins, postPlugins, signal } = _nullishCoalesce(options, () => ( {}));
|
|
const encoder = {
|
|
deferred: {},
|
|
index: 0,
|
|
indices: /* @__PURE__ */ new Map(),
|
|
stringified: [],
|
|
plugins,
|
|
postPlugins,
|
|
signal
|
|
};
|
|
const textEncoder = new TextEncoder();
|
|
let lastSentIndex = 0;
|
|
const readable = new ReadableStream({
|
|
async start(controller) {
|
|
const id = flatten.call(encoder, input);
|
|
if (Array.isArray(id)) {
|
|
throw new Error("This should never happen");
|
|
}
|
|
if (id < 0) {
|
|
controller.enqueue(textEncoder.encode(`${id}
|
|
`));
|
|
} else {
|
|
controller.enqueue(
|
|
textEncoder.encode(`[${encoder.stringified.join(",")}]
|
|
`)
|
|
);
|
|
lastSentIndex = encoder.stringified.length - 1;
|
|
}
|
|
const seenPromises = /* @__PURE__ */ new WeakSet();
|
|
if (Object.keys(encoder.deferred).length) {
|
|
let raceDone;
|
|
const racePromise = new Promise((resolve, reject) => {
|
|
raceDone = resolve;
|
|
if (signal) {
|
|
const rejectPromise = () => reject(signal.reason || new Error("Signal was aborted."));
|
|
if (signal.aborted) {
|
|
rejectPromise();
|
|
} else {
|
|
signal.addEventListener("abort", (event) => {
|
|
rejectPromise();
|
|
});
|
|
}
|
|
}
|
|
});
|
|
while (Object.keys(encoder.deferred).length > 0) {
|
|
for (const [deferredId, deferred] of Object.entries(
|
|
encoder.deferred
|
|
)) {
|
|
if (seenPromises.has(deferred)) continue;
|
|
seenPromises.add(
|
|
// biome-ignore lint/suspicious/noAssignInExpressions: <explanation>
|
|
encoder.deferred[Number(deferredId)] = Promise.race([
|
|
racePromise,
|
|
deferred
|
|
]).then(
|
|
(resolved) => {
|
|
const id2 = flatten.call(encoder, resolved);
|
|
if (Array.isArray(id2)) {
|
|
controller.enqueue(
|
|
textEncoder.encode(
|
|
`${TYPE_PROMISE}${deferredId}:[["${TYPE_PREVIOUS_RESOLVED}",${id2[0]}]]
|
|
`
|
|
)
|
|
);
|
|
encoder.index++;
|
|
lastSentIndex++;
|
|
} else if (id2 < 0) {
|
|
controller.enqueue(
|
|
textEncoder.encode(
|
|
`${TYPE_PROMISE}${deferredId}:${id2}
|
|
`
|
|
)
|
|
);
|
|
} else {
|
|
const values = encoder.stringified.slice(lastSentIndex + 1).join(",");
|
|
controller.enqueue(
|
|
textEncoder.encode(
|
|
`${TYPE_PROMISE}${deferredId}:[${values}]
|
|
`
|
|
)
|
|
);
|
|
lastSentIndex = encoder.stringified.length - 1;
|
|
}
|
|
},
|
|
(reason) => {
|
|
if (!reason || typeof reason !== "object" || !(reason instanceof Error)) {
|
|
reason = new Error("An unknown error occurred");
|
|
}
|
|
const id2 = flatten.call(encoder, reason);
|
|
if (Array.isArray(id2)) {
|
|
controller.enqueue(
|
|
textEncoder.encode(
|
|
`${TYPE_ERROR}${deferredId}:[["${TYPE_PREVIOUS_RESOLVED}",${id2[0]}]]
|
|
`
|
|
)
|
|
);
|
|
encoder.index++;
|
|
lastSentIndex++;
|
|
} else if (id2 < 0) {
|
|
controller.enqueue(
|
|
textEncoder.encode(
|
|
`${TYPE_ERROR}${deferredId}:${id2}
|
|
`
|
|
)
|
|
);
|
|
} else {
|
|
const values = encoder.stringified.slice(lastSentIndex + 1).join(",");
|
|
controller.enqueue(
|
|
textEncoder.encode(
|
|
`${TYPE_ERROR}${deferredId}:[${values}]
|
|
`
|
|
)
|
|
);
|
|
lastSentIndex = encoder.stringified.length - 1;
|
|
}
|
|
}
|
|
).finally(() => {
|
|
delete encoder.deferred[Number(deferredId)];
|
|
})
|
|
);
|
|
}
|
|
await Promise.race(Object.values(encoder.deferred));
|
|
}
|
|
raceDone();
|
|
}
|
|
await Promise.all(Object.values(encoder.deferred));
|
|
controller.close();
|
|
}
|
|
});
|
|
return readable;
|
|
}
|
|
|
|
// lib/dom/ssr/data.ts
|
|
async function createRequestInit(request) {
|
|
let init = { signal: request.signal };
|
|
if (request.method !== "GET") {
|
|
init.method = request.method;
|
|
let contentType = request.headers.get("Content-Type");
|
|
if (contentType && /\bapplication\/json\b/.test(contentType)) {
|
|
init.headers = { "Content-Type": contentType };
|
|
init.body = JSON.stringify(await request.json());
|
|
} else if (contentType && /\btext\/plain\b/.test(contentType)) {
|
|
init.headers = { "Content-Type": contentType };
|
|
init.body = await request.text();
|
|
} else if (contentType && /\bapplication\/x-www-form-urlencoded\b/.test(contentType)) {
|
|
init.body = new URLSearchParams(await request.text());
|
|
} else {
|
|
init.body = await request.formData();
|
|
}
|
|
}
|
|
return init;
|
|
}
|
|
|
|
// lib/dom/ssr/markup.ts
|
|
var ESCAPE_LOOKUP = {
|
|
"&": "\\u0026",
|
|
">": "\\u003e",
|
|
"<": "\\u003c",
|
|
"\u2028": "\\u2028",
|
|
"\u2029": "\\u2029"
|
|
};
|
|
var ESCAPE_REGEX = /[&><\u2028\u2029]/g;
|
|
function escapeHtml(html) {
|
|
return html.replace(ESCAPE_REGEX, (match) => ESCAPE_LOOKUP[match]);
|
|
}
|
|
function createHtml(html) {
|
|
return { __html: html };
|
|
}
|
|
|
|
// lib/dom/ssr/invariant.ts
|
|
function invariant2(value, message) {
|
|
if (value === false || value === null || typeof value === "undefined") {
|
|
throw new Error(message);
|
|
}
|
|
}
|
|
|
|
// lib/dom/ssr/single-fetch.tsx
|
|
var SingleFetchRedirectSymbol = Symbol("SingleFetchRedirect");
|
|
var SingleFetchNoResultError = class extends Error {
|
|
};
|
|
var SINGLE_FETCH_REDIRECT_STATUS = 202;
|
|
var NO_BODY_STATUS_CODES = /* @__PURE__ */ new Set([100, 101, 204, 205]);
|
|
function StreamTransfer({
|
|
context,
|
|
identifier,
|
|
reader,
|
|
textDecoder,
|
|
nonce
|
|
}) {
|
|
if (!context.renderMeta || !context.renderMeta.didRenderScripts) {
|
|
return null;
|
|
}
|
|
if (!context.renderMeta.streamCache) {
|
|
context.renderMeta.streamCache = {};
|
|
}
|
|
let { streamCache } = context.renderMeta;
|
|
let promise = streamCache[identifier];
|
|
if (!promise) {
|
|
promise = streamCache[identifier] = reader.read().then((result) => {
|
|
streamCache[identifier].result = {
|
|
done: result.done,
|
|
value: textDecoder.decode(result.value, { stream: true })
|
|
};
|
|
}).catch((e) => {
|
|
streamCache[identifier].error = e;
|
|
});
|
|
}
|
|
if (promise.error) {
|
|
throw promise.error;
|
|
}
|
|
if (promise.result === void 0) {
|
|
throw promise;
|
|
}
|
|
let { done, value } = promise.result;
|
|
let scriptTag = value ? /* @__PURE__ */ React.createElement(
|
|
"script",
|
|
{
|
|
nonce,
|
|
dangerouslySetInnerHTML: {
|
|
__html: `window.__reactRouterContext.streamController.enqueue(${escapeHtml(
|
|
JSON.stringify(value)
|
|
)});`
|
|
}
|
|
}
|
|
) : null;
|
|
if (done) {
|
|
return /* @__PURE__ */ React.createElement(React.Fragment, null, scriptTag, /* @__PURE__ */ React.createElement(
|
|
"script",
|
|
{
|
|
nonce,
|
|
dangerouslySetInnerHTML: {
|
|
__html: `window.__reactRouterContext.streamController.close();`
|
|
}
|
|
}
|
|
));
|
|
} else {
|
|
return /* @__PURE__ */ React.createElement(React.Fragment, null, scriptTag, /* @__PURE__ */ React.createElement(React.Suspense, null, /* @__PURE__ */ React.createElement(
|
|
StreamTransfer,
|
|
{
|
|
context,
|
|
identifier: identifier + 1,
|
|
reader,
|
|
textDecoder,
|
|
nonce
|
|
}
|
|
)));
|
|
}
|
|
}
|
|
function getTurboStreamSingleFetchDataStrategy(getRouter, manifest, routeModules, ssr, basename) {
|
|
let dataStrategy = getSingleFetchDataStrategyImpl(
|
|
getRouter,
|
|
(match) => {
|
|
let manifestRoute = manifest.routes[match.route.id];
|
|
invariant2(manifestRoute, "Route not found in manifest");
|
|
let routeModule = routeModules[match.route.id];
|
|
return {
|
|
hasLoader: manifestRoute.hasLoader,
|
|
hasClientLoader: manifestRoute.hasClientLoader,
|
|
hasShouldRevalidate: Boolean(_optionalChain([routeModule, 'optionalAccess', _61 => _61.shouldRevalidate]))
|
|
};
|
|
},
|
|
fetchAndDecodeViaTurboStream,
|
|
ssr,
|
|
basename
|
|
);
|
|
return async (args) => args.unstable_runClientMiddleware(dataStrategy);
|
|
}
|
|
function getSingleFetchDataStrategyImpl(getRouter, getRouteInfo, fetchAndDecode, ssr, basename, shouldAllowOptOut = () => true) {
|
|
return async (args) => {
|
|
let { request, matches, fetcherKey } = args;
|
|
let router = getRouter();
|
|
if (request.method !== "GET") {
|
|
return singleFetchActionStrategy(args, fetchAndDecode, basename);
|
|
}
|
|
let foundRevalidatingServerLoader = matches.some((m) => {
|
|
let { hasLoader, hasClientLoader } = getRouteInfo(m);
|
|
return m.unstable_shouldCallHandler() && hasLoader && !hasClientLoader;
|
|
});
|
|
if (!ssr && !foundRevalidatingServerLoader) {
|
|
return nonSsrStrategy(args, getRouteInfo, fetchAndDecode, basename);
|
|
}
|
|
if (fetcherKey) {
|
|
return singleFetchLoaderFetcherStrategy(args, fetchAndDecode, basename);
|
|
}
|
|
return singleFetchLoaderNavigationStrategy(
|
|
args,
|
|
router,
|
|
getRouteInfo,
|
|
fetchAndDecode,
|
|
ssr,
|
|
basename,
|
|
shouldAllowOptOut
|
|
);
|
|
};
|
|
}
|
|
async function singleFetchActionStrategy(args, fetchAndDecode, basename) {
|
|
let actionMatch = args.matches.find((m) => m.unstable_shouldCallHandler());
|
|
invariant2(actionMatch, "No action match found");
|
|
let actionStatus = void 0;
|
|
let result = await actionMatch.resolve(async (handler) => {
|
|
let result2 = await handler(async () => {
|
|
let { data: data2, status } = await fetchAndDecode(args, basename, [
|
|
actionMatch.route.id
|
|
]);
|
|
actionStatus = status;
|
|
return unwrapSingleFetchResult(data2, actionMatch.route.id);
|
|
});
|
|
return result2;
|
|
});
|
|
if (isResponse(result.result) || isRouteErrorResponse(result.result) || isDataWithResponseInit(result.result)) {
|
|
return { [actionMatch.route.id]: result };
|
|
}
|
|
return {
|
|
[actionMatch.route.id]: {
|
|
type: result.type,
|
|
result: data(result.result, actionStatus)
|
|
}
|
|
};
|
|
}
|
|
async function nonSsrStrategy(args, getRouteInfo, fetchAndDecode, basename) {
|
|
let matchesToLoad = args.matches.filter(
|
|
(m) => m.unstable_shouldCallHandler()
|
|
);
|
|
let results = {};
|
|
await Promise.all(
|
|
matchesToLoad.map(
|
|
(m) => m.resolve(async (handler) => {
|
|
try {
|
|
let { hasClientLoader } = getRouteInfo(m);
|
|
let routeId = m.route.id;
|
|
let result = hasClientLoader ? await handler(async () => {
|
|
let { data: data2 } = await fetchAndDecode(args, basename, [routeId]);
|
|
return unwrapSingleFetchResult(data2, routeId);
|
|
}) : await handler();
|
|
results[m.route.id] = { type: "data", result };
|
|
} catch (e) {
|
|
results[m.route.id] = { type: "error", result: e };
|
|
}
|
|
})
|
|
)
|
|
);
|
|
return results;
|
|
}
|
|
async function singleFetchLoaderNavigationStrategy(args, router, getRouteInfo, fetchAndDecode, ssr, basename, shouldAllowOptOut = () => true) {
|
|
let routesParams = /* @__PURE__ */ new Set();
|
|
let foundOptOutRoute = false;
|
|
let routeDfds = args.matches.map(() => createDeferred2());
|
|
let singleFetchDfd = createDeferred2();
|
|
let results = {};
|
|
let resolvePromise = Promise.all(
|
|
args.matches.map(
|
|
async (m, i) => m.resolve(async (handler) => {
|
|
routeDfds[i].resolve();
|
|
let routeId = m.route.id;
|
|
let { hasLoader, hasClientLoader, hasShouldRevalidate } = getRouteInfo(m);
|
|
let defaultShouldRevalidate = !m.unstable_shouldRevalidateArgs || m.unstable_shouldRevalidateArgs.actionStatus == null || m.unstable_shouldRevalidateArgs.actionStatus < 400;
|
|
let shouldCall = m.unstable_shouldCallHandler(defaultShouldRevalidate);
|
|
if (!shouldCall) {
|
|
foundOptOutRoute || (foundOptOutRoute = m.unstable_shouldRevalidateArgs != null && // This is a revalidation,
|
|
hasLoader && // for a route with a server loader,
|
|
hasShouldRevalidate === true);
|
|
return;
|
|
}
|
|
if (shouldAllowOptOut(m) && hasClientLoader) {
|
|
if (hasLoader) {
|
|
foundOptOutRoute = true;
|
|
}
|
|
try {
|
|
let result = await handler(async () => {
|
|
let { data: data2 } = await fetchAndDecode(args, basename, [routeId]);
|
|
return unwrapSingleFetchResult(data2, routeId);
|
|
});
|
|
results[routeId] = { type: "data", result };
|
|
} catch (e) {
|
|
results[routeId] = { type: "error", result: e };
|
|
}
|
|
return;
|
|
}
|
|
if (hasLoader) {
|
|
routesParams.add(routeId);
|
|
}
|
|
try {
|
|
let result = await handler(async () => {
|
|
let data2 = await singleFetchDfd.promise;
|
|
return unwrapSingleFetchResult(data2, routeId);
|
|
});
|
|
results[routeId] = { type: "data", result };
|
|
} catch (e) {
|
|
results[routeId] = { type: "error", result: e };
|
|
}
|
|
})
|
|
)
|
|
);
|
|
await Promise.all(routeDfds.map((d) => d.promise));
|
|
let isInitialLoad = !router.state.initialized && router.state.navigation.state === "idle";
|
|
if ((isInitialLoad || routesParams.size === 0) && !window.__reactRouterHdrActive) {
|
|
singleFetchDfd.resolve({ routes: {} });
|
|
} else {
|
|
let targetRoutes = ssr && foundOptOutRoute && routesParams.size > 0 ? [...routesParams.keys()] : void 0;
|
|
try {
|
|
let data2 = await fetchAndDecode(args, basename, targetRoutes);
|
|
singleFetchDfd.resolve(data2.data);
|
|
} catch (e) {
|
|
singleFetchDfd.reject(e);
|
|
}
|
|
}
|
|
await resolvePromise;
|
|
await bubbleMiddlewareErrors(
|
|
singleFetchDfd.promise,
|
|
args.matches,
|
|
routesParams,
|
|
results
|
|
);
|
|
return results;
|
|
}
|
|
async function bubbleMiddlewareErrors(singleFetchPromise, matches, routesParams, results) {
|
|
try {
|
|
let middlewareError;
|
|
let fetchedData = await singleFetchPromise;
|
|
if ("routes" in fetchedData) {
|
|
for (let match of matches) {
|
|
if (match.route.id in fetchedData.routes) {
|
|
let routeResult = fetchedData.routes[match.route.id];
|
|
if ("error" in routeResult) {
|
|
middlewareError = routeResult.error;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (middlewareError !== void 0) {
|
|
Array.from(routesParams.values()).forEach((routeId) => {
|
|
if (results[routeId].result instanceof SingleFetchNoResultError) {
|
|
results[routeId].result = middlewareError;
|
|
}
|
|
});
|
|
}
|
|
} catch (e) {
|
|
}
|
|
}
|
|
async function singleFetchLoaderFetcherStrategy(args, fetchAndDecode, basename) {
|
|
let fetcherMatch = args.matches.find((m) => m.unstable_shouldCallHandler());
|
|
invariant2(fetcherMatch, "No fetcher match found");
|
|
let routeId = fetcherMatch.route.id;
|
|
let result = await fetcherMatch.resolve(
|
|
async (handler) => handler(async () => {
|
|
let { data: data2 } = await fetchAndDecode(args, basename, [routeId]);
|
|
return unwrapSingleFetchResult(data2, routeId);
|
|
})
|
|
);
|
|
return { [fetcherMatch.route.id]: result };
|
|
}
|
|
function stripIndexParam(url) {
|
|
let indexValues = url.searchParams.getAll("index");
|
|
url.searchParams.delete("index");
|
|
let indexValuesToKeep = [];
|
|
for (let indexValue of indexValues) {
|
|
if (indexValue) {
|
|
indexValuesToKeep.push(indexValue);
|
|
}
|
|
}
|
|
for (let toKeep of indexValuesToKeep) {
|
|
url.searchParams.append("index", toKeep);
|
|
}
|
|
return url;
|
|
}
|
|
function singleFetchUrl(reqUrl, basename, extension) {
|
|
let url = typeof reqUrl === "string" ? new URL(
|
|
reqUrl,
|
|
// This can be called during the SSR flow via PrefetchPageLinksImpl so
|
|
// don't assume window is available
|
|
typeof window === "undefined" ? "server://singlefetch/" : window.location.origin
|
|
) : reqUrl;
|
|
if (url.pathname === "/") {
|
|
url.pathname = `_root.${extension}`;
|
|
} else if (basename && stripBasename(url.pathname, basename) === "/") {
|
|
url.pathname = `${basename.replace(/\/$/, "")}/_root.${extension}`;
|
|
} else {
|
|
url.pathname = `${url.pathname.replace(/\/$/, "")}.${extension}`;
|
|
}
|
|
return url;
|
|
}
|
|
async function fetchAndDecodeViaTurboStream(args, basename, targetRoutes) {
|
|
let { request } = args;
|
|
let url = singleFetchUrl(request.url, basename, "data");
|
|
if (request.method === "GET") {
|
|
url = stripIndexParam(url);
|
|
if (targetRoutes) {
|
|
url.searchParams.set("_routes", targetRoutes.join(","));
|
|
}
|
|
}
|
|
let res = await fetch(url, await createRequestInit(request));
|
|
if (res.status === 404 && !res.headers.has("X-Remix-Response")) {
|
|
throw new ErrorResponseImpl(404, "Not Found", true);
|
|
}
|
|
if (res.status === 204 && res.headers.has("X-Remix-Redirect")) {
|
|
return {
|
|
status: SINGLE_FETCH_REDIRECT_STATUS,
|
|
data: {
|
|
redirect: {
|
|
redirect: res.headers.get("X-Remix-Redirect"),
|
|
status: Number(res.headers.get("X-Remix-Status") || "302"),
|
|
revalidate: res.headers.get("X-Remix-Revalidate") === "true",
|
|
reload: res.headers.get("X-Remix-Reload-Document") === "true",
|
|
replace: res.headers.get("X-Remix-Replace") === "true"
|
|
}
|
|
}
|
|
};
|
|
}
|
|
if (NO_BODY_STATUS_CODES.has(res.status)) {
|
|
let routes = {};
|
|
if (targetRoutes && request.method !== "GET") {
|
|
routes[targetRoutes[0]] = { data: void 0 };
|
|
}
|
|
return {
|
|
status: res.status,
|
|
data: { routes }
|
|
};
|
|
}
|
|
invariant2(res.body, "No response body to decode");
|
|
try {
|
|
let decoded = await decodeViaTurboStream(res.body, window);
|
|
let data2;
|
|
if (request.method === "GET") {
|
|
let typed = decoded.value;
|
|
if (SingleFetchRedirectSymbol in typed) {
|
|
data2 = { redirect: typed[SingleFetchRedirectSymbol] };
|
|
} else {
|
|
data2 = { routes: typed };
|
|
}
|
|
} else {
|
|
let typed = decoded.value;
|
|
let routeId = _optionalChain([targetRoutes, 'optionalAccess', _62 => _62[0]]);
|
|
invariant2(routeId, "No routeId found for single fetch call decoding");
|
|
if ("redirect" in typed) {
|
|
data2 = { redirect: typed };
|
|
} else {
|
|
data2 = { routes: { [routeId]: typed } };
|
|
}
|
|
}
|
|
return { status: res.status, data: data2 };
|
|
} catch (e) {
|
|
throw new Error("Unable to decode turbo-stream response");
|
|
}
|
|
}
|
|
function decodeViaTurboStream(body, global) {
|
|
return decode(body, {
|
|
plugins: [
|
|
(type, ...rest) => {
|
|
if (type === "SanitizedError") {
|
|
let [name, message, stack] = rest;
|
|
let Constructor = Error;
|
|
if (name && name in global && typeof global[name] === "function") {
|
|
Constructor = global[name];
|
|
}
|
|
let error = new Constructor(message);
|
|
error.stack = stack;
|
|
return { value: error };
|
|
}
|
|
if (type === "ErrorResponse") {
|
|
let [data2, status, statusText] = rest;
|
|
return {
|
|
value: new ErrorResponseImpl(status, statusText, data2)
|
|
};
|
|
}
|
|
if (type === "SingleFetchRedirect") {
|
|
return { value: { [SingleFetchRedirectSymbol]: rest[0] } };
|
|
}
|
|
if (type === "SingleFetchClassInstance") {
|
|
return { value: rest[0] };
|
|
}
|
|
if (type === "SingleFetchFallback") {
|
|
return { value: void 0 };
|
|
}
|
|
}
|
|
]
|
|
});
|
|
}
|
|
function unwrapSingleFetchResult(result, routeId) {
|
|
if ("redirect" in result) {
|
|
let {
|
|
redirect: location,
|
|
revalidate,
|
|
reload,
|
|
replace: replace2,
|
|
status
|
|
} = result.redirect;
|
|
throw redirect(location, {
|
|
status,
|
|
headers: {
|
|
// Three R's of redirecting (lol Veep)
|
|
...revalidate ? { "X-Remix-Revalidate": "yes" } : null,
|
|
...reload ? { "X-Remix-Reload-Document": "yes" } : null,
|
|
...replace2 ? { "X-Remix-Replace": "yes" } : null
|
|
}
|
|
});
|
|
}
|
|
let routeResult = result.routes[routeId];
|
|
if (routeResult == null) {
|
|
throw new SingleFetchNoResultError(
|
|
`No result found for routeId "${routeId}"`
|
|
);
|
|
} else if ("error" in routeResult) {
|
|
throw routeResult.error;
|
|
} else if ("data" in routeResult) {
|
|
return routeResult.data;
|
|
} else {
|
|
throw new Error(`Invalid response found for routeId "${routeId}"`);
|
|
}
|
|
}
|
|
function createDeferred2() {
|
|
let resolve;
|
|
let reject;
|
|
let promise = new Promise((res, rej) => {
|
|
resolve = async (val) => {
|
|
res(val);
|
|
try {
|
|
await promise;
|
|
} catch (e) {
|
|
}
|
|
};
|
|
reject = async (error) => {
|
|
rej(error);
|
|
try {
|
|
await promise;
|
|
} catch (e) {
|
|
}
|
|
};
|
|
});
|
|
return {
|
|
promise,
|
|
//@ts-ignore
|
|
resolve,
|
|
//@ts-ignore
|
|
reject
|
|
};
|
|
}
|
|
|
|
// lib/context.ts
|
|
|
|
var DataRouterContext = React2.createContext(null);
|
|
DataRouterContext.displayName = "DataRouter";
|
|
var DataRouterStateContext = React2.createContext(null);
|
|
DataRouterStateContext.displayName = "DataRouterState";
|
|
var RSCRouterContext = React2.createContext(false);
|
|
function useIsRSCRouterContext() {
|
|
return React2.useContext(RSCRouterContext);
|
|
}
|
|
var ViewTransitionContext = React2.createContext({
|
|
isTransitioning: false
|
|
});
|
|
ViewTransitionContext.displayName = "ViewTransition";
|
|
var FetchersContext = React2.createContext(
|
|
/* @__PURE__ */ new Map()
|
|
);
|
|
FetchersContext.displayName = "Fetchers";
|
|
var AwaitContext = React2.createContext(null);
|
|
AwaitContext.displayName = "Await";
|
|
var NavigationContext = React2.createContext(
|
|
null
|
|
);
|
|
NavigationContext.displayName = "Navigation";
|
|
var LocationContext = React2.createContext(
|
|
null
|
|
);
|
|
LocationContext.displayName = "Location";
|
|
var RouteContext = React2.createContext({
|
|
outlet: null,
|
|
matches: [],
|
|
isDataRoute: false
|
|
});
|
|
RouteContext.displayName = "Route";
|
|
var RouteErrorContext = React2.createContext(null);
|
|
RouteErrorContext.displayName = "RouteError";
|
|
var ENABLE_DEV_WARNINGS = false;
|
|
|
|
// lib/hooks.tsx
|
|
|
|
function useHref(to, { relative } = {}) {
|
|
invariant(
|
|
useInRouterContext(),
|
|
// TODO: This error is probably because they somehow have 2 versions of the
|
|
// router loaded. We can help them understand how to avoid that.
|
|
`useHref() may be used only in the context of a <Router> component.`
|
|
);
|
|
let { basename, navigator } = React3.useContext(NavigationContext);
|
|
let { hash, pathname, search } = useResolvedPath(to, { relative });
|
|
let joinedPathname = pathname;
|
|
if (basename !== "/") {
|
|
joinedPathname = pathname === "/" ? basename : joinPaths([basename, pathname]);
|
|
}
|
|
return navigator.createHref({ pathname: joinedPathname, search, hash });
|
|
}
|
|
function useInRouterContext() {
|
|
return React3.useContext(LocationContext) != null;
|
|
}
|
|
function useLocation() {
|
|
invariant(
|
|
useInRouterContext(),
|
|
// TODO: This error is probably because they somehow have 2 versions of the
|
|
// router loaded. We can help them understand how to avoid that.
|
|
`useLocation() may be used only in the context of a <Router> component.`
|
|
);
|
|
return React3.useContext(LocationContext).location;
|
|
}
|
|
function useNavigationType() {
|
|
return React3.useContext(LocationContext).navigationType;
|
|
}
|
|
function useMatch(pattern) {
|
|
invariant(
|
|
useInRouterContext(),
|
|
// TODO: This error is probably because they somehow have 2 versions of the
|
|
// router loaded. We can help them understand how to avoid that.
|
|
`useMatch() may be used only in the context of a <Router> component.`
|
|
);
|
|
let { pathname } = useLocation();
|
|
return React3.useMemo(
|
|
() => matchPath(pattern, decodePath(pathname)),
|
|
[pathname, pattern]
|
|
);
|
|
}
|
|
var navigateEffectWarning = `You should call navigate() in a React.useEffect(), not when your component is first rendered.`;
|
|
function useIsomorphicLayoutEffect(cb) {
|
|
let isStatic = React3.useContext(NavigationContext).static;
|
|
if (!isStatic) {
|
|
React3.useLayoutEffect(cb);
|
|
}
|
|
}
|
|
function useNavigate() {
|
|
let { isDataRoute } = React3.useContext(RouteContext);
|
|
return isDataRoute ? useNavigateStable() : useNavigateUnstable();
|
|
}
|
|
function useNavigateUnstable() {
|
|
invariant(
|
|
useInRouterContext(),
|
|
// TODO: This error is probably because they somehow have 2 versions of the
|
|
// router loaded. We can help them understand how to avoid that.
|
|
`useNavigate() may be used only in the context of a <Router> component.`
|
|
);
|
|
let dataRouterContext = React3.useContext(DataRouterContext);
|
|
let { basename, navigator } = React3.useContext(NavigationContext);
|
|
let { matches } = React3.useContext(RouteContext);
|
|
let { pathname: locationPathname } = useLocation();
|
|
let routePathnamesJson = JSON.stringify(getResolveToMatches(matches));
|
|
let activeRef = React3.useRef(false);
|
|
useIsomorphicLayoutEffect(() => {
|
|
activeRef.current = true;
|
|
});
|
|
let navigate = React3.useCallback(
|
|
(to, options = {}) => {
|
|
warning(activeRef.current, navigateEffectWarning);
|
|
if (!activeRef.current) return;
|
|
if (typeof to === "number") {
|
|
navigator.go(to);
|
|
return;
|
|
}
|
|
let path = resolveTo(
|
|
to,
|
|
JSON.parse(routePathnamesJson),
|
|
locationPathname,
|
|
options.relative === "path"
|
|
);
|
|
if (dataRouterContext == null && basename !== "/") {
|
|
path.pathname = path.pathname === "/" ? basename : joinPaths([basename, path.pathname]);
|
|
}
|
|
(!!options.replace ? navigator.replace : navigator.push)(
|
|
path,
|
|
options.state,
|
|
options
|
|
);
|
|
},
|
|
[
|
|
basename,
|
|
navigator,
|
|
routePathnamesJson,
|
|
locationPathname,
|
|
dataRouterContext
|
|
]
|
|
);
|
|
return navigate;
|
|
}
|
|
var OutletContext = React3.createContext(null);
|
|
function useOutletContext() {
|
|
return React3.useContext(OutletContext);
|
|
}
|
|
function useOutlet(context) {
|
|
let outlet = React3.useContext(RouteContext).outlet;
|
|
if (outlet) {
|
|
return /* @__PURE__ */ React3.createElement(OutletContext.Provider, { value: context }, outlet);
|
|
}
|
|
return outlet;
|
|
}
|
|
function useParams() {
|
|
let { matches } = React3.useContext(RouteContext);
|
|
let routeMatch = matches[matches.length - 1];
|
|
return routeMatch ? routeMatch.params : {};
|
|
}
|
|
function useResolvedPath(to, { relative } = {}) {
|
|
let { matches } = React3.useContext(RouteContext);
|
|
let { pathname: locationPathname } = useLocation();
|
|
let routePathnamesJson = JSON.stringify(getResolveToMatches(matches));
|
|
return React3.useMemo(
|
|
() => resolveTo(
|
|
to,
|
|
JSON.parse(routePathnamesJson),
|
|
locationPathname,
|
|
relative === "path"
|
|
),
|
|
[to, routePathnamesJson, locationPathname, relative]
|
|
);
|
|
}
|
|
function useRoutes(routes, locationArg) {
|
|
return useRoutesImpl(routes, locationArg);
|
|
}
|
|
function useRoutesImpl(routes, locationArg, dataRouterState, future) {
|
|
invariant(
|
|
useInRouterContext(),
|
|
// TODO: This error is probably because they somehow have 2 versions of the
|
|
// router loaded. We can help them understand how to avoid that.
|
|
`useRoutes() may be used only in the context of a <Router> component.`
|
|
);
|
|
let { navigator } = React3.useContext(NavigationContext);
|
|
let { matches: parentMatches } = React3.useContext(RouteContext);
|
|
let routeMatch = parentMatches[parentMatches.length - 1];
|
|
let parentParams = routeMatch ? routeMatch.params : {};
|
|
let parentPathname = routeMatch ? routeMatch.pathname : "/";
|
|
let parentPathnameBase = routeMatch ? routeMatch.pathnameBase : "/";
|
|
let parentRoute = routeMatch && routeMatch.route;
|
|
if (ENABLE_DEV_WARNINGS) {
|
|
let parentPath = parentRoute && parentRoute.path || "";
|
|
warningOnce(
|
|
parentPathname,
|
|
!parentRoute || parentPath.endsWith("*") || parentPath.endsWith("*?"),
|
|
`You rendered descendant <Routes> (or called \`useRoutes()\`) at "${parentPathname}" (under <Route path="${parentPath}">) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render.
|
|
|
|
Please change the parent <Route path="${parentPath}"> to <Route path="${parentPath === "/" ? "*" : `${parentPath}/*`}">.`
|
|
);
|
|
}
|
|
let locationFromContext = useLocation();
|
|
let location;
|
|
if (locationArg) {
|
|
let parsedLocationArg = typeof locationArg === "string" ? parsePath(locationArg) : locationArg;
|
|
invariant(
|
|
parentPathnameBase === "/" || _optionalChain([parsedLocationArg, 'access', _63 => _63.pathname, 'optionalAccess', _64 => _64.startsWith, 'call', _65 => _65(parentPathnameBase)]),
|
|
`When overriding the location using \`<Routes location>\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "${parentPathnameBase}" but pathname "${parsedLocationArg.pathname}" was given in the \`location\` prop.`
|
|
);
|
|
location = parsedLocationArg;
|
|
} else {
|
|
location = locationFromContext;
|
|
}
|
|
let pathname = location.pathname || "/";
|
|
let remainingPathname = pathname;
|
|
if (parentPathnameBase !== "/") {
|
|
let parentSegments = parentPathnameBase.replace(/^\//, "").split("/");
|
|
let segments = pathname.replace(/^\//, "").split("/");
|
|
remainingPathname = "/" + segments.slice(parentSegments.length).join("/");
|
|
}
|
|
let matches = matchRoutes(routes, { pathname: remainingPathname });
|
|
if (ENABLE_DEV_WARNINGS) {
|
|
warning(
|
|
parentRoute || matches != null,
|
|
`No routes matched location "${location.pathname}${location.search}${location.hash}" `
|
|
);
|
|
warning(
|
|
matches == null || matches[matches.length - 1].route.element !== void 0 || matches[matches.length - 1].route.Component !== void 0 || matches[matches.length - 1].route.lazy !== void 0,
|
|
`Matched leaf route at location "${location.pathname}${location.search}${location.hash}" does not have an element or Component. This means it will render an <Outlet /> with a null value by default resulting in an "empty" page.`
|
|
);
|
|
}
|
|
let renderedMatches = _renderMatches(
|
|
matches && matches.map(
|
|
(match) => Object.assign({}, match, {
|
|
params: Object.assign({}, parentParams, match.params),
|
|
pathname: joinPaths([
|
|
parentPathnameBase,
|
|
// Re-encode pathnames that were decoded inside matchRoutes
|
|
navigator.encodeLocation ? navigator.encodeLocation(match.pathname).pathname : match.pathname
|
|
]),
|
|
pathnameBase: match.pathnameBase === "/" ? parentPathnameBase : joinPaths([
|
|
parentPathnameBase,
|
|
// Re-encode pathnames that were decoded inside matchRoutes
|
|
navigator.encodeLocation ? navigator.encodeLocation(match.pathnameBase).pathname : match.pathnameBase
|
|
])
|
|
})
|
|
),
|
|
parentMatches,
|
|
dataRouterState,
|
|
future
|
|
);
|
|
if (locationArg && renderedMatches) {
|
|
return /* @__PURE__ */ React3.createElement(
|
|
LocationContext.Provider,
|
|
{
|
|
value: {
|
|
location: {
|
|
pathname: "/",
|
|
search: "",
|
|
hash: "",
|
|
state: null,
|
|
key: "default",
|
|
...location
|
|
},
|
|
navigationType: "POP" /* Pop */
|
|
}
|
|
},
|
|
renderedMatches
|
|
);
|
|
}
|
|
return renderedMatches;
|
|
}
|
|
function DefaultErrorComponent() {
|
|
let error = useRouteError();
|
|
let message = isRouteErrorResponse(error) ? `${error.status} ${error.statusText}` : error instanceof Error ? error.message : JSON.stringify(error);
|
|
let stack = error instanceof Error ? error.stack : null;
|
|
let lightgrey = "rgba(200,200,200, 0.5)";
|
|
let preStyles = { padding: "0.5rem", backgroundColor: lightgrey };
|
|
let codeStyles = { padding: "2px 4px", backgroundColor: lightgrey };
|
|
let devInfo = null;
|
|
if (ENABLE_DEV_WARNINGS) {
|
|
console.error(
|
|
"Error handled by React Router default ErrorBoundary:",
|
|
error
|
|
);
|
|
devInfo = /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement("p", null, "\u{1F4BF} Hey developer \u{1F44B}"), /* @__PURE__ */ React3.createElement("p", null, "You can provide a way better UX than this when your app throws errors by providing your own ", /* @__PURE__ */ React3.createElement("code", { style: codeStyles }, "ErrorBoundary"), " or", " ", /* @__PURE__ */ React3.createElement("code", { style: codeStyles }, "errorElement"), " prop on your route."));
|
|
}
|
|
return /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement("h2", null, "Unexpected Application Error!"), /* @__PURE__ */ React3.createElement("h3", { style: { fontStyle: "italic" } }, message), stack ? /* @__PURE__ */ React3.createElement("pre", { style: preStyles }, stack) : null, devInfo);
|
|
}
|
|
var defaultErrorElement = /* @__PURE__ */ React3.createElement(DefaultErrorComponent, null);
|
|
var RenderErrorBoundary = class extends React3.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = {
|
|
location: props.location,
|
|
revalidation: props.revalidation,
|
|
error: props.error
|
|
};
|
|
}
|
|
static getDerivedStateFromError(error) {
|
|
return { error };
|
|
}
|
|
static getDerivedStateFromProps(props, state) {
|
|
if (state.location !== props.location || state.revalidation !== "idle" && props.revalidation === "idle") {
|
|
return {
|
|
error: props.error,
|
|
location: props.location,
|
|
revalidation: props.revalidation
|
|
};
|
|
}
|
|
return {
|
|
error: props.error !== void 0 ? props.error : state.error,
|
|
location: state.location,
|
|
revalidation: props.revalidation || state.revalidation
|
|
};
|
|
}
|
|
componentDidCatch(error, errorInfo) {
|
|
console.error(
|
|
"React Router caught the following error during render",
|
|
error,
|
|
errorInfo
|
|
);
|
|
}
|
|
render() {
|
|
return this.state.error !== void 0 ? /* @__PURE__ */ React3.createElement(RouteContext.Provider, { value: this.props.routeContext }, /* @__PURE__ */ React3.createElement(
|
|
RouteErrorContext.Provider,
|
|
{
|
|
value: this.state.error,
|
|
children: this.props.component
|
|
}
|
|
)) : this.props.children;
|
|
}
|
|
};
|
|
function RenderedRoute({ routeContext, match, children }) {
|
|
let dataRouterContext = React3.useContext(DataRouterContext);
|
|
if (dataRouterContext && dataRouterContext.static && dataRouterContext.staticContext && (match.route.errorElement || match.route.ErrorBoundary)) {
|
|
dataRouterContext.staticContext._deepestRenderedBoundaryId = match.route.id;
|
|
}
|
|
return /* @__PURE__ */ React3.createElement(RouteContext.Provider, { value: routeContext }, children);
|
|
}
|
|
function _renderMatches(matches, parentMatches = [], dataRouterState = null, future = null) {
|
|
if (matches == null) {
|
|
if (!dataRouterState) {
|
|
return null;
|
|
}
|
|
if (dataRouterState.errors) {
|
|
matches = dataRouterState.matches;
|
|
} else if (parentMatches.length === 0 && !dataRouterState.initialized && dataRouterState.matches.length > 0) {
|
|
matches = dataRouterState.matches;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
let renderedMatches = matches;
|
|
let errors = _optionalChain([dataRouterState, 'optionalAccess', _66 => _66.errors]);
|
|
if (errors != null) {
|
|
let errorIndex = renderedMatches.findIndex(
|
|
(m) => m.route.id && _optionalChain([errors, 'optionalAccess', _67 => _67[m.route.id]]) !== void 0
|
|
);
|
|
invariant(
|
|
errorIndex >= 0,
|
|
`Could not find a matching route for errors on route IDs: ${Object.keys(
|
|
errors
|
|
).join(",")}`
|
|
);
|
|
renderedMatches = renderedMatches.slice(
|
|
0,
|
|
Math.min(renderedMatches.length, errorIndex + 1)
|
|
);
|
|
}
|
|
let renderFallback = false;
|
|
let fallbackIndex = -1;
|
|
if (dataRouterState) {
|
|
for (let i = 0; i < renderedMatches.length; i++) {
|
|
let match = renderedMatches[i];
|
|
if (match.route.HydrateFallback || match.route.hydrateFallbackElement) {
|
|
fallbackIndex = i;
|
|
}
|
|
if (match.route.id) {
|
|
let { loaderData, errors: errors2 } = dataRouterState;
|
|
let needsToRunLoader = match.route.loader && !loaderData.hasOwnProperty(match.route.id) && (!errors2 || errors2[match.route.id] === void 0);
|
|
if (match.route.lazy || needsToRunLoader) {
|
|
renderFallback = true;
|
|
if (fallbackIndex >= 0) {
|
|
renderedMatches = renderedMatches.slice(0, fallbackIndex + 1);
|
|
} else {
|
|
renderedMatches = [renderedMatches[0]];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return renderedMatches.reduceRight(
|
|
(outlet, match, index) => {
|
|
let error;
|
|
let shouldRenderHydrateFallback = false;
|
|
let errorElement = null;
|
|
let hydrateFallbackElement = null;
|
|
if (dataRouterState) {
|
|
error = errors && match.route.id ? errors[match.route.id] : void 0;
|
|
errorElement = match.route.errorElement || defaultErrorElement;
|
|
if (renderFallback) {
|
|
if (fallbackIndex < 0 && index === 0) {
|
|
warningOnce(
|
|
"route-fallback",
|
|
false,
|
|
"No `HydrateFallback` element provided to render during initial hydration"
|
|
);
|
|
shouldRenderHydrateFallback = true;
|
|
hydrateFallbackElement = null;
|
|
} else if (fallbackIndex === index) {
|
|
shouldRenderHydrateFallback = true;
|
|
hydrateFallbackElement = match.route.hydrateFallbackElement || null;
|
|
}
|
|
}
|
|
}
|
|
let matches2 = parentMatches.concat(renderedMatches.slice(0, index + 1));
|
|
let getChildren = () => {
|
|
let children;
|
|
if (error) {
|
|
children = errorElement;
|
|
} else if (shouldRenderHydrateFallback) {
|
|
children = hydrateFallbackElement;
|
|
} else if (match.route.Component) {
|
|
children = /* @__PURE__ */ React3.createElement(match.route.Component, null);
|
|
} else if (match.route.element) {
|
|
children = match.route.element;
|
|
} else {
|
|
children = outlet;
|
|
}
|
|
return /* @__PURE__ */ React3.createElement(
|
|
RenderedRoute,
|
|
{
|
|
match,
|
|
routeContext: {
|
|
outlet,
|
|
matches: matches2,
|
|
isDataRoute: dataRouterState != null
|
|
},
|
|
children
|
|
}
|
|
);
|
|
};
|
|
return dataRouterState && (match.route.ErrorBoundary || match.route.errorElement || index === 0) ? /* @__PURE__ */ React3.createElement(
|
|
RenderErrorBoundary,
|
|
{
|
|
location: dataRouterState.location,
|
|
revalidation: dataRouterState.revalidation,
|
|
component: errorElement,
|
|
error,
|
|
children: getChildren(),
|
|
routeContext: { outlet: null, matches: matches2, isDataRoute: true }
|
|
}
|
|
) : getChildren();
|
|
},
|
|
null
|
|
);
|
|
}
|
|
function getDataRouterConsoleError(hookName) {
|
|
return `${hookName} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`;
|
|
}
|
|
function useDataRouterContext(hookName) {
|
|
let ctx = React3.useContext(DataRouterContext);
|
|
invariant(ctx, getDataRouterConsoleError(hookName));
|
|
return ctx;
|
|
}
|
|
function useDataRouterState(hookName) {
|
|
let state = React3.useContext(DataRouterStateContext);
|
|
invariant(state, getDataRouterConsoleError(hookName));
|
|
return state;
|
|
}
|
|
function useRouteContext(hookName) {
|
|
let route = React3.useContext(RouteContext);
|
|
invariant(route, getDataRouterConsoleError(hookName));
|
|
return route;
|
|
}
|
|
function useCurrentRouteId(hookName) {
|
|
let route = useRouteContext(hookName);
|
|
let thisRoute = route.matches[route.matches.length - 1];
|
|
invariant(
|
|
thisRoute.route.id,
|
|
`${hookName} can only be used on routes that contain a unique "id"`
|
|
);
|
|
return thisRoute.route.id;
|
|
}
|
|
function useRouteId() {
|
|
return useCurrentRouteId("useRouteId" /* UseRouteId */);
|
|
}
|
|
function useNavigation() {
|
|
let state = useDataRouterState("useNavigation" /* UseNavigation */);
|
|
return state.navigation;
|
|
}
|
|
function useRevalidator() {
|
|
let dataRouterContext = useDataRouterContext("useRevalidator" /* UseRevalidator */);
|
|
let state = useDataRouterState("useRevalidator" /* UseRevalidator */);
|
|
let revalidate = React3.useCallback(async () => {
|
|
await dataRouterContext.router.revalidate();
|
|
}, [dataRouterContext.router]);
|
|
return React3.useMemo(
|
|
() => ({ revalidate, state: state.revalidation }),
|
|
[revalidate, state.revalidation]
|
|
);
|
|
}
|
|
function useMatches() {
|
|
let { matches, loaderData } = useDataRouterState(
|
|
"useMatches" /* UseMatches */
|
|
);
|
|
return React3.useMemo(
|
|
() => matches.map((m) => convertRouteMatchToUiMatch(m, loaderData)),
|
|
[matches, loaderData]
|
|
);
|
|
}
|
|
function useLoaderData() {
|
|
let state = useDataRouterState("useLoaderData" /* UseLoaderData */);
|
|
let routeId = useCurrentRouteId("useLoaderData" /* UseLoaderData */);
|
|
return state.loaderData[routeId];
|
|
}
|
|
function useRouteLoaderData(routeId) {
|
|
let state = useDataRouterState("useRouteLoaderData" /* UseRouteLoaderData */);
|
|
return state.loaderData[routeId];
|
|
}
|
|
function useActionData() {
|
|
let state = useDataRouterState("useActionData" /* UseActionData */);
|
|
let routeId = useCurrentRouteId("useLoaderData" /* UseLoaderData */);
|
|
return state.actionData ? state.actionData[routeId] : void 0;
|
|
}
|
|
function useRouteError() {
|
|
let error = React3.useContext(RouteErrorContext);
|
|
let state = useDataRouterState("useRouteError" /* UseRouteError */);
|
|
let routeId = useCurrentRouteId("useRouteError" /* UseRouteError */);
|
|
if (error !== void 0) {
|
|
return error;
|
|
}
|
|
return _optionalChain([state, 'access', _68 => _68.errors, 'optionalAccess', _69 => _69[routeId]]);
|
|
}
|
|
function useAsyncValue() {
|
|
let value = React3.useContext(AwaitContext);
|
|
return _optionalChain([value, 'optionalAccess', _70 => _70._data]);
|
|
}
|
|
function useAsyncError() {
|
|
let value = React3.useContext(AwaitContext);
|
|
return _optionalChain([value, 'optionalAccess', _71 => _71._error]);
|
|
}
|
|
var blockerId = 0;
|
|
function useBlocker(shouldBlock) {
|
|
let { router, basename } = useDataRouterContext("useBlocker" /* UseBlocker */);
|
|
let state = useDataRouterState("useBlocker" /* UseBlocker */);
|
|
let [blockerKey, setBlockerKey] = React3.useState("");
|
|
let blockerFunction = React3.useCallback(
|
|
(arg) => {
|
|
if (typeof shouldBlock !== "function") {
|
|
return !!shouldBlock;
|
|
}
|
|
if (basename === "/") {
|
|
return shouldBlock(arg);
|
|
}
|
|
let { currentLocation, nextLocation, historyAction } = arg;
|
|
return shouldBlock({
|
|
currentLocation: {
|
|
...currentLocation,
|
|
pathname: stripBasename(currentLocation.pathname, basename) || currentLocation.pathname
|
|
},
|
|
nextLocation: {
|
|
...nextLocation,
|
|
pathname: stripBasename(nextLocation.pathname, basename) || nextLocation.pathname
|
|
},
|
|
historyAction
|
|
});
|
|
},
|
|
[basename, shouldBlock]
|
|
);
|
|
React3.useEffect(() => {
|
|
let key = String(++blockerId);
|
|
setBlockerKey(key);
|
|
return () => router.deleteBlocker(key);
|
|
}, [router]);
|
|
React3.useEffect(() => {
|
|
if (blockerKey !== "") {
|
|
router.getBlocker(blockerKey, blockerFunction);
|
|
}
|
|
}, [router, blockerKey, blockerFunction]);
|
|
return blockerKey && state.blockers.has(blockerKey) ? state.blockers.get(blockerKey) : IDLE_BLOCKER;
|
|
}
|
|
function useNavigateStable() {
|
|
let { router } = useDataRouterContext("useNavigate" /* UseNavigateStable */);
|
|
let id = useCurrentRouteId("useNavigate" /* UseNavigateStable */);
|
|
let activeRef = React3.useRef(false);
|
|
useIsomorphicLayoutEffect(() => {
|
|
activeRef.current = true;
|
|
});
|
|
let navigate = React3.useCallback(
|
|
async (to, options = {}) => {
|
|
warning(activeRef.current, navigateEffectWarning);
|
|
if (!activeRef.current) return;
|
|
if (typeof to === "number") {
|
|
router.navigate(to);
|
|
} else {
|
|
await router.navigate(to, { fromRouteId: id, ...options });
|
|
}
|
|
},
|
|
[router, id]
|
|
);
|
|
return navigate;
|
|
}
|
|
var alreadyWarned = {};
|
|
function warningOnce(key, cond, message) {
|
|
if (!cond && !alreadyWarned[key]) {
|
|
alreadyWarned[key] = true;
|
|
warning(false, message);
|
|
}
|
|
}
|
|
|
|
// lib/dom/ssr/errorBoundaries.tsx
|
|
|
|
|
|
// lib/dom/ssr/components.tsx
|
|
|
|
|
|
// lib/dom/ssr/routeModules.ts
|
|
async function loadRouteModule(route, routeModulesCache) {
|
|
if (route.id in routeModulesCache) {
|
|
return routeModulesCache[route.id];
|
|
}
|
|
try {
|
|
let routeModule = await Promise.resolve().then(() => _interopRequireWildcard(require(
|
|
/* @vite-ignore */
|
|
/* webpackIgnore: true */
|
|
route.module
|
|
)));
|
|
routeModulesCache[route.id] = routeModule;
|
|
return routeModule;
|
|
} catch (error) {
|
|
console.error(
|
|
`Error loading route module \`${route.module}\`, reloading page...`
|
|
);
|
|
console.error(error);
|
|
if (window.__reactRouterContext && window.__reactRouterContext.isSpaMode && // @ts-expect-error
|
|
void 0) {
|
|
throw error;
|
|
}
|
|
window.location.reload();
|
|
return new Promise(() => {
|
|
});
|
|
}
|
|
}
|
|
|
|
// lib/dom/ssr/links.ts
|
|
function getKeyedLinksForMatches(matches, routeModules, manifest) {
|
|
let descriptors = matches.map((match) => {
|
|
let module = routeModules[match.route.id];
|
|
let route = manifest.routes[match.route.id];
|
|
return [
|
|
route && route.css ? route.css.map((href) => ({ rel: "stylesheet", href })) : [],
|
|
_optionalChain([module, 'optionalAccess', _72 => _72.links, 'optionalCall', _73 => _73()]) || []
|
|
];
|
|
}).flat(2);
|
|
let preloads = getModuleLinkHrefs(matches, manifest);
|
|
return dedupeLinkDescriptors(descriptors, preloads);
|
|
}
|
|
function getRouteCssDescriptors(route) {
|
|
if (!route.css) return [];
|
|
return route.css.map((href) => ({ rel: "stylesheet", href }));
|
|
}
|
|
async function prefetchRouteCss(route) {
|
|
if (!route.css) return;
|
|
let descriptors = getRouteCssDescriptors(route);
|
|
await Promise.all(descriptors.map(prefetchStyleLink));
|
|
}
|
|
async function prefetchStyleLinks(route, routeModule) {
|
|
if (!route.css && !routeModule.links || !isPreloadSupported()) return;
|
|
let descriptors = [];
|
|
if (route.css) {
|
|
descriptors.push(...getRouteCssDescriptors(route));
|
|
}
|
|
if (routeModule.links) {
|
|
descriptors.push(...routeModule.links());
|
|
}
|
|
if (descriptors.length === 0) return;
|
|
let styleLinks = [];
|
|
for (let descriptor of descriptors) {
|
|
if (!isPageLinkDescriptor(descriptor) && descriptor.rel === "stylesheet") {
|
|
styleLinks.push({
|
|
...descriptor,
|
|
rel: "preload",
|
|
as: "style"
|
|
});
|
|
}
|
|
}
|
|
await Promise.all(styleLinks.map(prefetchStyleLink));
|
|
}
|
|
async function prefetchStyleLink(descriptor) {
|
|
return new Promise((resolve) => {
|
|
if (descriptor.media && !window.matchMedia(descriptor.media).matches || document.querySelector(
|
|
`link[rel="stylesheet"][href="${descriptor.href}"]`
|
|
)) {
|
|
return resolve();
|
|
}
|
|
let link = document.createElement("link");
|
|
Object.assign(link, descriptor);
|
|
function removeLink() {
|
|
if (document.head.contains(link)) {
|
|
document.head.removeChild(link);
|
|
}
|
|
}
|
|
link.onload = () => {
|
|
removeLink();
|
|
resolve();
|
|
};
|
|
link.onerror = () => {
|
|
removeLink();
|
|
resolve();
|
|
};
|
|
document.head.appendChild(link);
|
|
});
|
|
}
|
|
function isPageLinkDescriptor(object) {
|
|
return object != null && typeof object.page === "string";
|
|
}
|
|
function isHtmlLinkDescriptor(object) {
|
|
if (object == null) {
|
|
return false;
|
|
}
|
|
if (object.href == null) {
|
|
return object.rel === "preload" && typeof object.imageSrcSet === "string" && typeof object.imageSizes === "string";
|
|
}
|
|
return typeof object.rel === "string" && typeof object.href === "string";
|
|
}
|
|
async function getKeyedPrefetchLinks(matches, manifest, routeModules) {
|
|
let links = await Promise.all(
|
|
matches.map(async (match) => {
|
|
let route = manifest.routes[match.route.id];
|
|
if (route) {
|
|
let mod = await loadRouteModule(route, routeModules);
|
|
return mod.links ? mod.links() : [];
|
|
}
|
|
return [];
|
|
})
|
|
);
|
|
return dedupeLinkDescriptors(
|
|
links.flat(1).filter(isHtmlLinkDescriptor).filter((link) => link.rel === "stylesheet" || link.rel === "preload").map(
|
|
(link) => link.rel === "stylesheet" ? { ...link, rel: "prefetch", as: "style" } : { ...link, rel: "prefetch" }
|
|
)
|
|
);
|
|
}
|
|
function getNewMatchesForLinks(page, nextMatches, currentMatches, manifest, location, mode) {
|
|
let isNew = (match, index) => {
|
|
if (!currentMatches[index]) return true;
|
|
return match.route.id !== currentMatches[index].route.id;
|
|
};
|
|
let matchPathChanged = (match, index) => {
|
|
return (
|
|
// param change, /users/123 -> /users/456
|
|
currentMatches[index].pathname !== match.pathname || // splat param changed, which is not present in match.path
|
|
// e.g. /files/images/avatar.jpg -> files/finances.xls
|
|
_optionalChain([currentMatches, 'access', _74 => _74[index], 'access', _75 => _75.route, 'access', _76 => _76.path, 'optionalAccess', _77 => _77.endsWith, 'call', _78 => _78("*")]) && currentMatches[index].params["*"] !== match.params["*"]
|
|
);
|
|
};
|
|
if (mode === "assets") {
|
|
return nextMatches.filter(
|
|
(match, index) => isNew(match, index) || matchPathChanged(match, index)
|
|
);
|
|
}
|
|
if (mode === "data") {
|
|
return nextMatches.filter((match, index) => {
|
|
let manifestRoute = manifest.routes[match.route.id];
|
|
if (!manifestRoute || !manifestRoute.hasLoader) {
|
|
return false;
|
|
}
|
|
if (isNew(match, index) || matchPathChanged(match, index)) {
|
|
return true;
|
|
}
|
|
if (match.route.shouldRevalidate) {
|
|
let routeChoice = match.route.shouldRevalidate({
|
|
currentUrl: new URL(
|
|
location.pathname + location.search + location.hash,
|
|
window.origin
|
|
),
|
|
currentParams: _optionalChain([currentMatches, 'access', _79 => _79[0], 'optionalAccess', _80 => _80.params]) || {},
|
|
nextUrl: new URL(page, window.origin),
|
|
nextParams: match.params,
|
|
defaultShouldRevalidate: true
|
|
});
|
|
if (typeof routeChoice === "boolean") {
|
|
return routeChoice;
|
|
}
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
return [];
|
|
}
|
|
function getModuleLinkHrefs(matches, manifest, { includeHydrateFallback } = {}) {
|
|
return dedupeHrefs(
|
|
matches.map((match) => {
|
|
let route = manifest.routes[match.route.id];
|
|
if (!route) return [];
|
|
let hrefs = [route.module];
|
|
if (route.clientActionModule) {
|
|
hrefs = hrefs.concat(route.clientActionModule);
|
|
}
|
|
if (route.clientLoaderModule) {
|
|
hrefs = hrefs.concat(route.clientLoaderModule);
|
|
}
|
|
if (includeHydrateFallback && route.hydrateFallbackModule) {
|
|
hrefs = hrefs.concat(route.hydrateFallbackModule);
|
|
}
|
|
if (route.imports) {
|
|
hrefs = hrefs.concat(route.imports);
|
|
}
|
|
return hrefs;
|
|
}).flat(1)
|
|
);
|
|
}
|
|
function dedupeHrefs(hrefs) {
|
|
return [...new Set(hrefs)];
|
|
}
|
|
function sortKeys(obj) {
|
|
let sorted = {};
|
|
let keys = Object.keys(obj).sort();
|
|
for (let key of keys) {
|
|
sorted[key] = obj[key];
|
|
}
|
|
return sorted;
|
|
}
|
|
function dedupeLinkDescriptors(descriptors, preloads) {
|
|
let set = /* @__PURE__ */ new Set();
|
|
let preloadsSet = new Set(preloads);
|
|
return descriptors.reduce((deduped, descriptor) => {
|
|
let alreadyModulePreload = preloads && !isPageLinkDescriptor(descriptor) && descriptor.as === "script" && descriptor.href && preloadsSet.has(descriptor.href);
|
|
if (alreadyModulePreload) {
|
|
return deduped;
|
|
}
|
|
let key = JSON.stringify(sortKeys(descriptor));
|
|
if (!set.has(key)) {
|
|
set.add(key);
|
|
deduped.push({ key, link: descriptor });
|
|
}
|
|
return deduped;
|
|
}, []);
|
|
}
|
|
var _isPreloadSupported;
|
|
function isPreloadSupported() {
|
|
if (_isPreloadSupported !== void 0) {
|
|
return _isPreloadSupported;
|
|
}
|
|
let el = document.createElement("link");
|
|
_isPreloadSupported = el.relList.supports("preload");
|
|
el = null;
|
|
return _isPreloadSupported;
|
|
}
|
|
|
|
// lib/server-runtime/warnings.ts
|
|
var alreadyWarned2 = {};
|
|
function warnOnce(condition, message) {
|
|
if (!condition && !alreadyWarned2[message]) {
|
|
alreadyWarned2[message] = true;
|
|
console.warn(message);
|
|
}
|
|
}
|
|
|
|
// lib/dom/ssr/fog-of-war.ts
|
|
|
|
|
|
// lib/dom/ssr/routes.tsx
|
|
|
|
|
|
// lib/dom/ssr/fallback.tsx
|
|
|
|
function RemixRootDefaultHydrateFallback() {
|
|
return /* @__PURE__ */ React4.createElement(BoundaryShell, { title: "Loading...", renderScripts: true }, ENABLE_DEV_WARNINGS ? /* @__PURE__ */ React4.createElement(
|
|
"script",
|
|
{
|
|
dangerouslySetInnerHTML: {
|
|
__html: `
|
|
console.log(
|
|
"\u{1F4BF} Hey developer \u{1F44B}. You can provide a way better UX than this " +
|
|
"when your app is loading JS modules and/or running \`clientLoader\` " +
|
|
"functions. Check out https://reactrouter.com/start/framework/route-module#hydratefallback " +
|
|
"for more information."
|
|
);
|
|
`
|
|
}
|
|
}
|
|
) : null);
|
|
}
|
|
|
|
// lib/dom/ssr/routes.tsx
|
|
function groupRoutesByParentId(manifest) {
|
|
let routes = {};
|
|
Object.values(manifest).forEach((route) => {
|
|
if (route) {
|
|
let parentId = route.parentId || "";
|
|
if (!routes[parentId]) {
|
|
routes[parentId] = [];
|
|
}
|
|
routes[parentId].push(route);
|
|
}
|
|
});
|
|
return routes;
|
|
}
|
|
function getRouteComponents(route, routeModule, isSpaMode) {
|
|
let Component3 = getRouteModuleComponent(routeModule);
|
|
let HydrateFallback = routeModule.HydrateFallback && (!isSpaMode || route.id === "root") ? routeModule.HydrateFallback : route.id === "root" ? RemixRootDefaultHydrateFallback : void 0;
|
|
let ErrorBoundary = routeModule.ErrorBoundary ? routeModule.ErrorBoundary : route.id === "root" ? () => /* @__PURE__ */ React5.createElement(RemixRootDefaultErrorBoundary, { error: useRouteError() }) : void 0;
|
|
if (route.id === "root" && routeModule.Layout) {
|
|
return {
|
|
...Component3 ? {
|
|
element: /* @__PURE__ */ React5.createElement(routeModule.Layout, null, /* @__PURE__ */ React5.createElement(Component3, null))
|
|
} : { Component: Component3 },
|
|
...ErrorBoundary ? {
|
|
errorElement: /* @__PURE__ */ React5.createElement(routeModule.Layout, null, /* @__PURE__ */ React5.createElement(ErrorBoundary, null))
|
|
} : { ErrorBoundary },
|
|
...HydrateFallback ? {
|
|
hydrateFallbackElement: /* @__PURE__ */ React5.createElement(routeModule.Layout, null, /* @__PURE__ */ React5.createElement(HydrateFallback, null))
|
|
} : { HydrateFallback }
|
|
};
|
|
}
|
|
return { Component: Component3, ErrorBoundary, HydrateFallback };
|
|
}
|
|
function createServerRoutes(manifest, routeModules, future, isSpaMode, parentId = "", routesByParentId = groupRoutesByParentId(manifest), spaModeLazyPromise = Promise.resolve({ Component: () => null })) {
|
|
return (routesByParentId[parentId] || []).map((route) => {
|
|
let routeModule = routeModules[route.id];
|
|
invariant2(
|
|
routeModule,
|
|
"No `routeModule` available to create server routes"
|
|
);
|
|
let dataRoute = {
|
|
...getRouteComponents(route, routeModule, isSpaMode),
|
|
caseSensitive: route.caseSensitive,
|
|
id: route.id,
|
|
index: route.index,
|
|
path: route.path,
|
|
handle: routeModule.handle,
|
|
// For SPA Mode, all routes are lazy except root. However we tell the
|
|
// router root is also lazy here too since we don't need a full
|
|
// implementation - we just need a `lazy` prop to tell the RR rendering
|
|
// where to stop which is always at the root route in SPA mode
|
|
lazy: isSpaMode ? () => spaModeLazyPromise : void 0,
|
|
// For partial hydration rendering, we need to indicate when the route
|
|
// has a loader/clientLoader, but it won't ever be called during the static
|
|
// render, so just give it a no-op function so we can render down to the
|
|
// proper fallback
|
|
loader: route.hasLoader || route.hasClientLoader ? () => null : void 0
|
|
// We don't need middleware/action/shouldRevalidate on these routes since
|
|
// they're for a static render
|
|
};
|
|
let children = createServerRoutes(
|
|
manifest,
|
|
routeModules,
|
|
future,
|
|
isSpaMode,
|
|
route.id,
|
|
routesByParentId,
|
|
spaModeLazyPromise
|
|
);
|
|
if (children.length > 0) dataRoute.children = children;
|
|
return dataRoute;
|
|
});
|
|
}
|
|
function createClientRoutesWithHMRRevalidationOptOut(needsRevalidation, manifest, routeModulesCache, initialState, ssr, isSpaMode) {
|
|
return createClientRoutes(
|
|
manifest,
|
|
routeModulesCache,
|
|
initialState,
|
|
ssr,
|
|
isSpaMode,
|
|
"",
|
|
groupRoutesByParentId(manifest),
|
|
needsRevalidation
|
|
);
|
|
}
|
|
function preventInvalidServerHandlerCall(type, route) {
|
|
if (type === "loader" && !route.hasLoader || type === "action" && !route.hasAction) {
|
|
let fn = type === "action" ? "serverAction()" : "serverLoader()";
|
|
let msg = `You are trying to call ${fn} on a route that does not have a server ${type} (routeId: "${route.id}")`;
|
|
console.error(msg);
|
|
throw new ErrorResponseImpl(400, "Bad Request", new Error(msg), true);
|
|
}
|
|
}
|
|
function noActionDefinedError(type, routeId) {
|
|
let article = type === "clientAction" ? "a" : "an";
|
|
let msg = `Route "${routeId}" does not have ${article} ${type}, but you are trying to submit to it. To fix this, please add ${article} \`${type}\` function to the route`;
|
|
console.error(msg);
|
|
throw new ErrorResponseImpl(405, "Method Not Allowed", new Error(msg), true);
|
|
}
|
|
function createClientRoutes(manifest, routeModulesCache, initialState, ssr, isSpaMode, parentId = "", routesByParentId = groupRoutesByParentId(manifest), needsRevalidation) {
|
|
return (routesByParentId[parentId] || []).map((route) => {
|
|
let routeModule = routeModulesCache[route.id];
|
|
function fetchServerHandler(singleFetch) {
|
|
invariant2(
|
|
typeof singleFetch === "function",
|
|
"No single fetch function available for route handler"
|
|
);
|
|
return singleFetch();
|
|
}
|
|
function fetchServerLoader(singleFetch) {
|
|
if (!route.hasLoader) return Promise.resolve(null);
|
|
return fetchServerHandler(singleFetch);
|
|
}
|
|
function fetchServerAction(singleFetch) {
|
|
if (!route.hasAction) {
|
|
throw noActionDefinedError("action", route.id);
|
|
}
|
|
return fetchServerHandler(singleFetch);
|
|
}
|
|
function prefetchModule(modulePath) {
|
|
Promise.resolve().then(() => _interopRequireWildcard(require(
|
|
/* @vite-ignore */
|
|
/* webpackIgnore: true */
|
|
modulePath
|
|
)));
|
|
}
|
|
function prefetchRouteModuleChunks(route2) {
|
|
if (route2.clientActionModule) {
|
|
prefetchModule(route2.clientActionModule);
|
|
}
|
|
if (route2.clientLoaderModule) {
|
|
prefetchModule(route2.clientLoaderModule);
|
|
}
|
|
}
|
|
async function prefetchStylesAndCallHandler(handler) {
|
|
let cachedModule = routeModulesCache[route.id];
|
|
let linkPrefetchPromise = cachedModule ? prefetchStyleLinks(route, cachedModule) : Promise.resolve();
|
|
try {
|
|
return handler();
|
|
} finally {
|
|
await linkPrefetchPromise;
|
|
}
|
|
}
|
|
let dataRoute = {
|
|
id: route.id,
|
|
index: route.index,
|
|
path: route.path
|
|
};
|
|
if (routeModule) {
|
|
Object.assign(dataRoute, {
|
|
...dataRoute,
|
|
...getRouteComponents(route, routeModule, isSpaMode),
|
|
unstable_middleware: routeModule.unstable_clientMiddleware,
|
|
handle: routeModule.handle,
|
|
shouldRevalidate: getShouldRevalidateFunction(
|
|
dataRoute.path,
|
|
routeModule,
|
|
route,
|
|
ssr,
|
|
needsRevalidation
|
|
)
|
|
});
|
|
let hasInitialData = initialState && initialState.loaderData && route.id in initialState.loaderData;
|
|
let initialData = hasInitialData ? _optionalChain([initialState, 'optionalAccess', _81 => _81.loaderData, 'optionalAccess', _82 => _82[route.id]]) : void 0;
|
|
let hasInitialError = initialState && initialState.errors && route.id in initialState.errors;
|
|
let initialError = hasInitialError ? _optionalChain([initialState, 'optionalAccess', _83 => _83.errors, 'optionalAccess', _84 => _84[route.id]]) : void 0;
|
|
let isHydrationRequest = needsRevalidation == null && (_optionalChain([routeModule, 'access', _85 => _85.clientLoader, 'optionalAccess', _86 => _86.hydrate]) === true || !route.hasLoader);
|
|
dataRoute.loader = async ({ request, params, context }, singleFetch) => {
|
|
try {
|
|
let result = await prefetchStylesAndCallHandler(async () => {
|
|
invariant2(
|
|
routeModule,
|
|
"No `routeModule` available for critical-route loader"
|
|
);
|
|
if (!routeModule.clientLoader) {
|
|
return fetchServerLoader(singleFetch);
|
|
}
|
|
return routeModule.clientLoader({
|
|
request,
|
|
params,
|
|
context,
|
|
async serverLoader() {
|
|
preventInvalidServerHandlerCall("loader", route);
|
|
if (isHydrationRequest) {
|
|
if (hasInitialData) {
|
|
return initialData;
|
|
}
|
|
if (hasInitialError) {
|
|
throw initialError;
|
|
}
|
|
}
|
|
return fetchServerLoader(singleFetch);
|
|
}
|
|
});
|
|
});
|
|
return result;
|
|
} finally {
|
|
isHydrationRequest = false;
|
|
}
|
|
};
|
|
dataRoute.loader.hydrate = shouldHydrateRouteLoader(
|
|
route.id,
|
|
routeModule.clientLoader,
|
|
route.hasLoader,
|
|
isSpaMode
|
|
);
|
|
dataRoute.action = ({ request, params, context }, singleFetch) => {
|
|
return prefetchStylesAndCallHandler(async () => {
|
|
invariant2(
|
|
routeModule,
|
|
"No `routeModule` available for critical-route action"
|
|
);
|
|
if (!routeModule.clientAction) {
|
|
if (isSpaMode) {
|
|
throw noActionDefinedError("clientAction", route.id);
|
|
}
|
|
return fetchServerAction(singleFetch);
|
|
}
|
|
return routeModule.clientAction({
|
|
request,
|
|
params,
|
|
context,
|
|
async serverAction() {
|
|
preventInvalidServerHandlerCall("action", route);
|
|
return fetchServerAction(singleFetch);
|
|
}
|
|
});
|
|
});
|
|
};
|
|
} else {
|
|
if (!route.hasClientLoader) {
|
|
dataRoute.loader = (_, singleFetch) => prefetchStylesAndCallHandler(() => {
|
|
return fetchServerLoader(singleFetch);
|
|
});
|
|
}
|
|
if (!route.hasClientAction) {
|
|
dataRoute.action = (_, singleFetch) => prefetchStylesAndCallHandler(() => {
|
|
if (isSpaMode) {
|
|
throw noActionDefinedError("clientAction", route.id);
|
|
}
|
|
return fetchServerAction(singleFetch);
|
|
});
|
|
}
|
|
let lazyRoutePromise;
|
|
async function getLazyRoute() {
|
|
if (lazyRoutePromise) {
|
|
return await lazyRoutePromise;
|
|
}
|
|
lazyRoutePromise = (async () => {
|
|
if (route.clientLoaderModule || route.clientActionModule) {
|
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
}
|
|
let routeModulePromise = loadRouteModuleWithBlockingLinks(
|
|
route,
|
|
routeModulesCache
|
|
);
|
|
prefetchRouteModuleChunks(route);
|
|
return await routeModulePromise;
|
|
})();
|
|
return await lazyRoutePromise;
|
|
}
|
|
dataRoute.lazy = {
|
|
loader: route.hasClientLoader ? async () => {
|
|
let { clientLoader } = route.clientLoaderModule ? await Promise.resolve().then(() => _interopRequireWildcard(require(
|
|
/* @vite-ignore */
|
|
/* webpackIgnore: true */
|
|
route.clientLoaderModule
|
|
))) : await getLazyRoute();
|
|
invariant2(clientLoader, "No `clientLoader` export found");
|
|
return (args, singleFetch) => clientLoader({
|
|
...args,
|
|
async serverLoader() {
|
|
preventInvalidServerHandlerCall("loader", route);
|
|
return fetchServerLoader(singleFetch);
|
|
}
|
|
});
|
|
} : void 0,
|
|
action: route.hasClientAction ? async () => {
|
|
let clientActionPromise = route.clientActionModule ? Promise.resolve().then(() => _interopRequireWildcard(require(
|
|
/* @vite-ignore */
|
|
/* webpackIgnore: true */
|
|
route.clientActionModule
|
|
))) : getLazyRoute();
|
|
prefetchRouteModuleChunks(route);
|
|
let { clientAction } = await clientActionPromise;
|
|
invariant2(clientAction, "No `clientAction` export found");
|
|
return (args, singleFetch) => clientAction({
|
|
...args,
|
|
async serverAction() {
|
|
preventInvalidServerHandlerCall("action", route);
|
|
return fetchServerAction(singleFetch);
|
|
}
|
|
});
|
|
} : void 0,
|
|
unstable_middleware: route.hasClientMiddleware ? async () => {
|
|
let { unstable_clientMiddleware } = route.clientMiddlewareModule ? await Promise.resolve().then(() => _interopRequireWildcard(require(
|
|
/* @vite-ignore */
|
|
/* webpackIgnore: true */
|
|
route.clientMiddlewareModule
|
|
))) : await getLazyRoute();
|
|
invariant2(
|
|
unstable_clientMiddleware,
|
|
"No `unstable_clientMiddleware` export found"
|
|
);
|
|
return unstable_clientMiddleware;
|
|
} : void 0,
|
|
shouldRevalidate: async () => {
|
|
let lazyRoute = await getLazyRoute();
|
|
return getShouldRevalidateFunction(
|
|
dataRoute.path,
|
|
lazyRoute,
|
|
route,
|
|
ssr,
|
|
needsRevalidation
|
|
);
|
|
},
|
|
handle: async () => (await getLazyRoute()).handle,
|
|
// No need to wrap these in layout since the root route is never
|
|
// loaded via route.lazy()
|
|
Component: async () => (await getLazyRoute()).Component,
|
|
ErrorBoundary: route.hasErrorBoundary ? async () => (await getLazyRoute()).ErrorBoundary : void 0
|
|
};
|
|
}
|
|
let children = createClientRoutes(
|
|
manifest,
|
|
routeModulesCache,
|
|
initialState,
|
|
ssr,
|
|
isSpaMode,
|
|
route.id,
|
|
routesByParentId,
|
|
needsRevalidation
|
|
);
|
|
if (children.length > 0) dataRoute.children = children;
|
|
return dataRoute;
|
|
});
|
|
}
|
|
function getShouldRevalidateFunction(path, route, manifestRoute, ssr, needsRevalidation) {
|
|
if (needsRevalidation) {
|
|
return wrapShouldRevalidateForHdr(
|
|
manifestRoute.id,
|
|
route.shouldRevalidate,
|
|
needsRevalidation
|
|
);
|
|
}
|
|
if (!ssr && manifestRoute.hasLoader && !manifestRoute.hasClientLoader) {
|
|
let myParams = path ? compilePath(path)[1].map((p) => p.paramName) : [];
|
|
const didParamsChange = (opts) => myParams.some((p) => opts.currentParams[p] !== opts.nextParams[p]);
|
|
if (route.shouldRevalidate) {
|
|
let fn = route.shouldRevalidate;
|
|
return (opts) => fn({
|
|
...opts,
|
|
defaultShouldRevalidate: didParamsChange(opts)
|
|
});
|
|
} else {
|
|
return (opts) => didParamsChange(opts);
|
|
}
|
|
}
|
|
if (ssr && route.shouldRevalidate) {
|
|
let fn = route.shouldRevalidate;
|
|
return (opts) => fn({ ...opts, defaultShouldRevalidate: true });
|
|
}
|
|
return route.shouldRevalidate;
|
|
}
|
|
function wrapShouldRevalidateForHdr(routeId, routeShouldRevalidate, needsRevalidation) {
|
|
let handledRevalidation = false;
|
|
return (arg) => {
|
|
if (!handledRevalidation) {
|
|
handledRevalidation = true;
|
|
return needsRevalidation.has(routeId);
|
|
}
|
|
return routeShouldRevalidate ? routeShouldRevalidate(arg) : arg.defaultShouldRevalidate;
|
|
};
|
|
}
|
|
async function loadRouteModuleWithBlockingLinks(route, routeModules) {
|
|
let routeModulePromise = loadRouteModule(route, routeModules);
|
|
let prefetchRouteCssPromise = prefetchRouteCss(route);
|
|
let routeModule = await routeModulePromise;
|
|
await Promise.all([
|
|
prefetchRouteCssPromise,
|
|
prefetchStyleLinks(route, routeModule)
|
|
]);
|
|
return {
|
|
Component: getRouteModuleComponent(routeModule),
|
|
ErrorBoundary: routeModule.ErrorBoundary,
|
|
unstable_clientMiddleware: routeModule.unstable_clientMiddleware,
|
|
clientAction: routeModule.clientAction,
|
|
clientLoader: routeModule.clientLoader,
|
|
handle: routeModule.handle,
|
|
links: routeModule.links,
|
|
meta: routeModule.meta,
|
|
shouldRevalidate: routeModule.shouldRevalidate
|
|
};
|
|
}
|
|
function getRouteModuleComponent(routeModule) {
|
|
if (routeModule.default == null) return void 0;
|
|
let isEmptyObject = typeof routeModule.default === "object" && Object.keys(routeModule.default).length === 0;
|
|
if (!isEmptyObject) {
|
|
return routeModule.default;
|
|
}
|
|
}
|
|
function shouldHydrateRouteLoader(routeId, clientLoader, hasLoader, isSpaMode) {
|
|
return isSpaMode && routeId !== "root" || clientLoader != null && (clientLoader.hydrate === true || hasLoader !== true);
|
|
}
|
|
|
|
// lib/dom/ssr/fog-of-war.ts
|
|
var nextPaths = /* @__PURE__ */ new Set();
|
|
var discoveredPathsMaxSize = 1e3;
|
|
var discoveredPaths = /* @__PURE__ */ new Set();
|
|
var URL_LIMIT = 7680;
|
|
function isFogOfWarEnabled(routeDiscovery, ssr) {
|
|
return routeDiscovery.mode === "lazy" && ssr === true;
|
|
}
|
|
function getPartialManifest({ sri, ...manifest }, router) {
|
|
let routeIds = new Set(router.state.matches.map((m) => m.route.id));
|
|
let segments = router.state.location.pathname.split("/").filter(Boolean);
|
|
let paths = ["/"];
|
|
segments.pop();
|
|
while (segments.length > 0) {
|
|
paths.push(`/${segments.join("/")}`);
|
|
segments.pop();
|
|
}
|
|
paths.forEach((path) => {
|
|
let matches = matchRoutes(router.routes, path, router.basename);
|
|
if (matches) {
|
|
matches.forEach((m) => routeIds.add(m.route.id));
|
|
}
|
|
});
|
|
let initialRoutes = [...routeIds].reduce(
|
|
(acc, id) => Object.assign(acc, { [id]: manifest.routes[id] }),
|
|
{}
|
|
);
|
|
return {
|
|
...manifest,
|
|
routes: initialRoutes,
|
|
sri: sri ? true : void 0
|
|
};
|
|
}
|
|
function getPatchRoutesOnNavigationFunction(manifest, routeModules, ssr, routeDiscovery, isSpaMode, basename) {
|
|
if (!isFogOfWarEnabled(routeDiscovery, ssr)) {
|
|
return void 0;
|
|
}
|
|
return async ({ path, patch, signal, fetcherKey }) => {
|
|
if (discoveredPaths.has(path)) {
|
|
return;
|
|
}
|
|
await fetchAndApplyManifestPatches(
|
|
[path],
|
|
fetcherKey ? window.location.href : path,
|
|
manifest,
|
|
routeModules,
|
|
ssr,
|
|
isSpaMode,
|
|
basename,
|
|
routeDiscovery.manifestPath,
|
|
patch,
|
|
signal
|
|
);
|
|
};
|
|
}
|
|
function useFogOFWarDiscovery(router, manifest, routeModules, ssr, routeDiscovery, isSpaMode) {
|
|
React6.useEffect(() => {
|
|
if (!isFogOfWarEnabled(routeDiscovery, ssr) || // @ts-expect-error - TS doesn't know about this yet
|
|
_optionalChain([window, 'access', _87 => _87.navigator, 'optionalAccess', _88 => _88.connection, 'optionalAccess', _89 => _89.saveData]) === true) {
|
|
return;
|
|
}
|
|
function registerElement(el) {
|
|
let path = el.tagName === "FORM" ? el.getAttribute("action") : el.getAttribute("href");
|
|
if (!path) {
|
|
return;
|
|
}
|
|
let pathname = el.tagName === "A" ? el.pathname : new URL(path, window.location.origin).pathname;
|
|
if (!discoveredPaths.has(pathname)) {
|
|
nextPaths.add(pathname);
|
|
}
|
|
}
|
|
async function fetchPatches() {
|
|
document.querySelectorAll("a[data-discover], form[data-discover]").forEach(registerElement);
|
|
let lazyPaths = Array.from(nextPaths.keys()).filter((path) => {
|
|
if (discoveredPaths.has(path)) {
|
|
nextPaths.delete(path);
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
if (lazyPaths.length === 0) {
|
|
return;
|
|
}
|
|
try {
|
|
await fetchAndApplyManifestPatches(
|
|
lazyPaths,
|
|
null,
|
|
manifest,
|
|
routeModules,
|
|
ssr,
|
|
isSpaMode,
|
|
router.basename,
|
|
routeDiscovery.manifestPath,
|
|
router.patchRoutes
|
|
);
|
|
} catch (e) {
|
|
console.error("Failed to fetch manifest patches", e);
|
|
}
|
|
}
|
|
let debouncedFetchPatches = debounce(fetchPatches, 100);
|
|
fetchPatches();
|
|
let observer = new MutationObserver(() => debouncedFetchPatches());
|
|
observer.observe(document.documentElement, {
|
|
subtree: true,
|
|
childList: true,
|
|
attributes: true,
|
|
attributeFilter: ["data-discover", "href", "action"]
|
|
});
|
|
return () => observer.disconnect();
|
|
}, [ssr, isSpaMode, manifest, routeModules, router, routeDiscovery]);
|
|
}
|
|
function getManifestPath(_manifestPath, basename) {
|
|
let manifestPath = _manifestPath || "/__manifest";
|
|
if (basename == null) {
|
|
return manifestPath;
|
|
}
|
|
return `${basename}${manifestPath}`.replace(/\/+/g, "/");
|
|
}
|
|
var MANIFEST_VERSION_STORAGE_KEY = "react-router-manifest-version";
|
|
async function fetchAndApplyManifestPatches(paths, errorReloadPath, manifest, routeModules, ssr, isSpaMode, basename, manifestPath, patchRoutes, signal) {
|
|
let url = new URL(
|
|
getManifestPath(manifestPath, basename),
|
|
window.location.origin
|
|
);
|
|
paths.sort().forEach((path) => url.searchParams.append("p", path));
|
|
url.searchParams.set("version", manifest.version);
|
|
if (url.toString().length > URL_LIMIT) {
|
|
nextPaths.clear();
|
|
return;
|
|
}
|
|
let serverPatches;
|
|
try {
|
|
let res = await fetch(url, { signal });
|
|
if (!res.ok) {
|
|
throw new Error(`${res.status} ${res.statusText}`);
|
|
} else if (res.status === 204 && res.headers.has("X-Remix-Reload-Document")) {
|
|
if (!errorReloadPath) {
|
|
console.warn(
|
|
"Detected a manifest version mismatch during eager route discovery. The next navigation/fetch to an undiscovered route will result in a new document navigation to sync up with the latest manifest."
|
|
);
|
|
return;
|
|
}
|
|
if (sessionStorage.getItem(MANIFEST_VERSION_STORAGE_KEY) === manifest.version) {
|
|
console.error(
|
|
"Unable to discover routes due to manifest version mismatch."
|
|
);
|
|
return;
|
|
}
|
|
sessionStorage.setItem(MANIFEST_VERSION_STORAGE_KEY, manifest.version);
|
|
window.location.href = errorReloadPath;
|
|
console.warn("Detected manifest version mismatch, reloading...");
|
|
await new Promise(() => {
|
|
});
|
|
} else if (res.status >= 400) {
|
|
throw new Error(await res.text());
|
|
}
|
|
sessionStorage.removeItem(MANIFEST_VERSION_STORAGE_KEY);
|
|
serverPatches = await res.json();
|
|
} catch (e) {
|
|
if (_optionalChain([signal, 'optionalAccess', _90 => _90.aborted])) return;
|
|
throw e;
|
|
}
|
|
let knownRoutes = new Set(Object.keys(manifest.routes));
|
|
let patches = Object.values(serverPatches).reduce((acc, route) => {
|
|
if (route && !knownRoutes.has(route.id)) {
|
|
acc[route.id] = route;
|
|
}
|
|
return acc;
|
|
}, {});
|
|
Object.assign(manifest.routes, patches);
|
|
paths.forEach((p) => addToFifoQueue(p, discoveredPaths));
|
|
let parentIds = /* @__PURE__ */ new Set();
|
|
Object.values(patches).forEach((patch) => {
|
|
if (patch && (!patch.parentId || !patches[patch.parentId])) {
|
|
parentIds.add(patch.parentId);
|
|
}
|
|
});
|
|
parentIds.forEach(
|
|
(parentId) => patchRoutes(
|
|
parentId || null,
|
|
createClientRoutes(patches, routeModules, null, ssr, isSpaMode, parentId)
|
|
)
|
|
);
|
|
}
|
|
function addToFifoQueue(path, queue) {
|
|
if (queue.size >= discoveredPathsMaxSize) {
|
|
let first = queue.values().next().value;
|
|
queue.delete(first);
|
|
}
|
|
queue.add(path);
|
|
}
|
|
function debounce(callback, wait) {
|
|
let timeoutId;
|
|
return (...args) => {
|
|
window.clearTimeout(timeoutId);
|
|
timeoutId = window.setTimeout(() => callback(...args), wait);
|
|
};
|
|
}
|
|
|
|
// lib/dom/ssr/components.tsx
|
|
function useDataRouterContext2() {
|
|
let context = React7.useContext(DataRouterContext);
|
|
invariant2(
|
|
context,
|
|
"You must render this element inside a <DataRouterContext.Provider> element"
|
|
);
|
|
return context;
|
|
}
|
|
function useDataRouterStateContext() {
|
|
let context = React7.useContext(DataRouterStateContext);
|
|
invariant2(
|
|
context,
|
|
"You must render this element inside a <DataRouterStateContext.Provider> element"
|
|
);
|
|
return context;
|
|
}
|
|
var FrameworkContext = React7.createContext(void 0);
|
|
FrameworkContext.displayName = "FrameworkContext";
|
|
function useFrameworkContext() {
|
|
let context = React7.useContext(FrameworkContext);
|
|
invariant2(
|
|
context,
|
|
"You must render this element inside a <HydratedRouter> element"
|
|
);
|
|
return context;
|
|
}
|
|
function usePrefetchBehavior(prefetch, theirElementProps) {
|
|
let frameworkContext = React7.useContext(FrameworkContext);
|
|
let [maybePrefetch, setMaybePrefetch] = React7.useState(false);
|
|
let [shouldPrefetch, setShouldPrefetch] = React7.useState(false);
|
|
let { onFocus, onBlur, onMouseEnter, onMouseLeave, onTouchStart } = theirElementProps;
|
|
let ref = React7.useRef(null);
|
|
React7.useEffect(() => {
|
|
if (prefetch === "render") {
|
|
setShouldPrefetch(true);
|
|
}
|
|
if (prefetch === "viewport") {
|
|
let callback = (entries) => {
|
|
entries.forEach((entry) => {
|
|
setShouldPrefetch(entry.isIntersecting);
|
|
});
|
|
};
|
|
let observer = new IntersectionObserver(callback, { threshold: 0.5 });
|
|
if (ref.current) observer.observe(ref.current);
|
|
return () => {
|
|
observer.disconnect();
|
|
};
|
|
}
|
|
}, [prefetch]);
|
|
React7.useEffect(() => {
|
|
if (maybePrefetch) {
|
|
let id = setTimeout(() => {
|
|
setShouldPrefetch(true);
|
|
}, 100);
|
|
return () => {
|
|
clearTimeout(id);
|
|
};
|
|
}
|
|
}, [maybePrefetch]);
|
|
let setIntent = () => {
|
|
setMaybePrefetch(true);
|
|
};
|
|
let cancelIntent = () => {
|
|
setMaybePrefetch(false);
|
|
setShouldPrefetch(false);
|
|
};
|
|
if (!frameworkContext) {
|
|
return [false, ref, {}];
|
|
}
|
|
if (prefetch !== "intent") {
|
|
return [shouldPrefetch, ref, {}];
|
|
}
|
|
return [
|
|
shouldPrefetch,
|
|
ref,
|
|
{
|
|
onFocus: composeEventHandlers(onFocus, setIntent),
|
|
onBlur: composeEventHandlers(onBlur, cancelIntent),
|
|
onMouseEnter: composeEventHandlers(onMouseEnter, setIntent),
|
|
onMouseLeave: composeEventHandlers(onMouseLeave, cancelIntent),
|
|
onTouchStart: composeEventHandlers(onTouchStart, setIntent)
|
|
}
|
|
];
|
|
}
|
|
function composeEventHandlers(theirHandler, ourHandler) {
|
|
return (event) => {
|
|
theirHandler && theirHandler(event);
|
|
if (!event.defaultPrevented) {
|
|
ourHandler(event);
|
|
}
|
|
};
|
|
}
|
|
function getActiveMatches(matches, errors, isSpaMode) {
|
|
if (isSpaMode && !isHydrated) {
|
|
return [matches[0]];
|
|
}
|
|
if (errors) {
|
|
let errorIdx = matches.findIndex((m) => errors[m.route.id] !== void 0);
|
|
return matches.slice(0, errorIdx + 1);
|
|
}
|
|
return matches;
|
|
}
|
|
var CRITICAL_CSS_DATA_ATTRIBUTE = "data-react-router-critical-css";
|
|
function Links() {
|
|
let { isSpaMode, manifest, routeModules, criticalCss } = useFrameworkContext();
|
|
let { errors, matches: routerMatches } = useDataRouterStateContext();
|
|
let matches = getActiveMatches(routerMatches, errors, isSpaMode);
|
|
let keyedLinks = React7.useMemo(
|
|
() => getKeyedLinksForMatches(matches, routeModules, manifest),
|
|
[matches, routeModules, manifest]
|
|
);
|
|
return /* @__PURE__ */ React7.createElement(React7.Fragment, null, typeof criticalCss === "string" ? /* @__PURE__ */ React7.createElement(
|
|
"style",
|
|
{
|
|
...{ [CRITICAL_CSS_DATA_ATTRIBUTE]: "" },
|
|
dangerouslySetInnerHTML: { __html: criticalCss }
|
|
}
|
|
) : null, typeof criticalCss === "object" ? /* @__PURE__ */ React7.createElement(
|
|
"link",
|
|
{
|
|
...{ [CRITICAL_CSS_DATA_ATTRIBUTE]: "" },
|
|
rel: "stylesheet",
|
|
href: criticalCss.href
|
|
}
|
|
) : null, keyedLinks.map(
|
|
({ key, link }) => isPageLinkDescriptor(link) ? /* @__PURE__ */ React7.createElement(PrefetchPageLinks, { key, ...link }) : /* @__PURE__ */ React7.createElement("link", { key, ...link })
|
|
));
|
|
}
|
|
function PrefetchPageLinks({ page, ...linkProps }) {
|
|
let { router } = useDataRouterContext2();
|
|
let matches = React7.useMemo(
|
|
() => matchRoutes(router.routes, page, router.basename),
|
|
[router.routes, page, router.basename]
|
|
);
|
|
if (!matches) {
|
|
return null;
|
|
}
|
|
return /* @__PURE__ */ React7.createElement(PrefetchPageLinksImpl, { page, matches, ...linkProps });
|
|
}
|
|
function useKeyedPrefetchLinks(matches) {
|
|
let { manifest, routeModules } = useFrameworkContext();
|
|
let [keyedPrefetchLinks, setKeyedPrefetchLinks] = React7.useState([]);
|
|
React7.useEffect(() => {
|
|
let interrupted = false;
|
|
void getKeyedPrefetchLinks(matches, manifest, routeModules).then(
|
|
(links) => {
|
|
if (!interrupted) {
|
|
setKeyedPrefetchLinks(links);
|
|
}
|
|
}
|
|
);
|
|
return () => {
|
|
interrupted = true;
|
|
};
|
|
}, [matches, manifest, routeModules]);
|
|
return keyedPrefetchLinks;
|
|
}
|
|
function PrefetchPageLinksImpl({
|
|
page,
|
|
matches: nextMatches,
|
|
...linkProps
|
|
}) {
|
|
let location = useLocation();
|
|
let { manifest, routeModules } = useFrameworkContext();
|
|
let { basename } = useDataRouterContext2();
|
|
let { loaderData, matches } = useDataRouterStateContext();
|
|
let newMatchesForData = React7.useMemo(
|
|
() => getNewMatchesForLinks(
|
|
page,
|
|
nextMatches,
|
|
matches,
|
|
manifest,
|
|
location,
|
|
"data"
|
|
),
|
|
[page, nextMatches, matches, manifest, location]
|
|
);
|
|
let newMatchesForAssets = React7.useMemo(
|
|
() => getNewMatchesForLinks(
|
|
page,
|
|
nextMatches,
|
|
matches,
|
|
manifest,
|
|
location,
|
|
"assets"
|
|
),
|
|
[page, nextMatches, matches, manifest, location]
|
|
);
|
|
let dataHrefs = React7.useMemo(() => {
|
|
if (page === location.pathname + location.search + location.hash) {
|
|
return [];
|
|
}
|
|
let routesParams = /* @__PURE__ */ new Set();
|
|
let foundOptOutRoute = false;
|
|
nextMatches.forEach((m) => {
|
|
let manifestRoute = manifest.routes[m.route.id];
|
|
if (!manifestRoute || !manifestRoute.hasLoader) {
|
|
return;
|
|
}
|
|
if (!newMatchesForData.some((m2) => m2.route.id === m.route.id) && m.route.id in loaderData && _optionalChain([routeModules, 'access', _91 => _91[m.route.id], 'optionalAccess', _92 => _92.shouldRevalidate])) {
|
|
foundOptOutRoute = true;
|
|
} else if (manifestRoute.hasClientLoader) {
|
|
foundOptOutRoute = true;
|
|
} else {
|
|
routesParams.add(m.route.id);
|
|
}
|
|
});
|
|
if (routesParams.size === 0) {
|
|
return [];
|
|
}
|
|
let url = singleFetchUrl(page, basename, "data");
|
|
if (foundOptOutRoute && routesParams.size > 0) {
|
|
url.searchParams.set(
|
|
"_routes",
|
|
nextMatches.filter((m) => routesParams.has(m.route.id)).map((m) => m.route.id).join(",")
|
|
);
|
|
}
|
|
return [url.pathname + url.search];
|
|
}, [
|
|
basename,
|
|
loaderData,
|
|
location,
|
|
manifest,
|
|
newMatchesForData,
|
|
nextMatches,
|
|
page,
|
|
routeModules
|
|
]);
|
|
let moduleHrefs = React7.useMemo(
|
|
() => getModuleLinkHrefs(newMatchesForAssets, manifest),
|
|
[newMatchesForAssets, manifest]
|
|
);
|
|
let keyedPrefetchLinks = useKeyedPrefetchLinks(newMatchesForAssets);
|
|
return /* @__PURE__ */ React7.createElement(React7.Fragment, null, dataHrefs.map((href) => /* @__PURE__ */ React7.createElement("link", { key: href, rel: "prefetch", as: "fetch", href, ...linkProps })), moduleHrefs.map((href) => /* @__PURE__ */ React7.createElement("link", { key: href, rel: "modulepreload", href, ...linkProps })), keyedPrefetchLinks.map(({ key, link }) => (
|
|
// these don't spread `linkProps` because they are full link descriptors
|
|
// already with their own props
|
|
/* @__PURE__ */ React7.createElement("link", { key, ...link })
|
|
)));
|
|
}
|
|
function Meta() {
|
|
let { isSpaMode, routeModules } = useFrameworkContext();
|
|
let {
|
|
errors,
|
|
matches: routerMatches,
|
|
loaderData
|
|
} = useDataRouterStateContext();
|
|
let location = useLocation();
|
|
let _matches = getActiveMatches(routerMatches, errors, isSpaMode);
|
|
let error = null;
|
|
if (errors) {
|
|
error = errors[_matches[_matches.length - 1].route.id];
|
|
}
|
|
let meta = [];
|
|
let leafMeta = null;
|
|
let matches = [];
|
|
for (let i = 0; i < _matches.length; i++) {
|
|
let _match = _matches[i];
|
|
let routeId = _match.route.id;
|
|
let data2 = loaderData[routeId];
|
|
let params = _match.params;
|
|
let routeModule = routeModules[routeId];
|
|
let routeMeta = [];
|
|
let match = {
|
|
id: routeId,
|
|
data: data2,
|
|
meta: [],
|
|
params: _match.params,
|
|
pathname: _match.pathname,
|
|
handle: _match.route.handle,
|
|
error
|
|
};
|
|
matches[i] = match;
|
|
if (_optionalChain([routeModule, 'optionalAccess', _93 => _93.meta])) {
|
|
routeMeta = typeof routeModule.meta === "function" ? routeModule.meta({
|
|
data: data2,
|
|
params,
|
|
location,
|
|
matches,
|
|
error
|
|
}) : Array.isArray(routeModule.meta) ? [...routeModule.meta] : routeModule.meta;
|
|
} else if (leafMeta) {
|
|
routeMeta = [...leafMeta];
|
|
}
|
|
routeMeta = routeMeta || [];
|
|
if (!Array.isArray(routeMeta)) {
|
|
throw new Error(
|
|
"The route at " + _match.route.path + " returns an invalid value. All route meta functions must return an array of meta objects.\n\nTo reference the meta function API, see https://remix.run/route/meta"
|
|
);
|
|
}
|
|
match.meta = routeMeta;
|
|
matches[i] = match;
|
|
meta = [...routeMeta];
|
|
leafMeta = meta;
|
|
}
|
|
return /* @__PURE__ */ React7.createElement(React7.Fragment, null, meta.flat().map((metaProps) => {
|
|
if (!metaProps) {
|
|
return null;
|
|
}
|
|
if ("tagName" in metaProps) {
|
|
let { tagName, ...rest } = metaProps;
|
|
if (!isValidMetaTag(tagName)) {
|
|
console.warn(
|
|
`A meta object uses an invalid tagName: ${tagName}. Expected either 'link' or 'meta'`
|
|
);
|
|
return null;
|
|
}
|
|
let Comp = tagName;
|
|
return /* @__PURE__ */ React7.createElement(Comp, { key: JSON.stringify(rest), ...rest });
|
|
}
|
|
if ("title" in metaProps) {
|
|
return /* @__PURE__ */ React7.createElement("title", { key: "title" }, String(metaProps.title));
|
|
}
|
|
if ("charset" in metaProps) {
|
|
_nullishCoalesce(metaProps.charSet, () => ( (metaProps.charSet = metaProps.charset)));
|
|
delete metaProps.charset;
|
|
}
|
|
if ("charSet" in metaProps && metaProps.charSet != null) {
|
|
return typeof metaProps.charSet === "string" ? /* @__PURE__ */ React7.createElement("meta", { key: "charSet", charSet: metaProps.charSet }) : null;
|
|
}
|
|
if ("script:ld+json" in metaProps) {
|
|
try {
|
|
let json = JSON.stringify(metaProps["script:ld+json"]);
|
|
return /* @__PURE__ */ React7.createElement(
|
|
"script",
|
|
{
|
|
key: `script:ld+json:${json}`,
|
|
type: "application/ld+json",
|
|
dangerouslySetInnerHTML: { __html: json }
|
|
}
|
|
);
|
|
} catch (err) {
|
|
return null;
|
|
}
|
|
}
|
|
return /* @__PURE__ */ React7.createElement("meta", { key: JSON.stringify(metaProps), ...metaProps });
|
|
}));
|
|
}
|
|
function isValidMetaTag(tagName) {
|
|
return typeof tagName === "string" && /^(meta|link)$/.test(tagName);
|
|
}
|
|
var isHydrated = false;
|
|
function Scripts(scriptProps) {
|
|
let {
|
|
manifest,
|
|
serverHandoffString,
|
|
isSpaMode,
|
|
renderMeta,
|
|
routeDiscovery,
|
|
ssr
|
|
} = useFrameworkContext();
|
|
let { router, static: isStatic, staticContext } = useDataRouterContext2();
|
|
let { matches: routerMatches } = useDataRouterStateContext();
|
|
let isRSCRouterContext = useIsRSCRouterContext();
|
|
let enableFogOfWar = isFogOfWarEnabled(routeDiscovery, ssr);
|
|
if (renderMeta) {
|
|
renderMeta.didRenderScripts = true;
|
|
}
|
|
let matches = getActiveMatches(routerMatches, null, isSpaMode);
|
|
React7.useEffect(() => {
|
|
isHydrated = true;
|
|
}, []);
|
|
let initialScripts = React7.useMemo(() => {
|
|
if (isRSCRouterContext) {
|
|
return null;
|
|
}
|
|
let streamScript = "window.__reactRouterContext.stream = new ReadableStream({start(controller){window.__reactRouterContext.streamController = controller;}}).pipeThrough(new TextEncoderStream());";
|
|
let contextScript = staticContext ? `window.__reactRouterContext = ${serverHandoffString};${streamScript}` : " ";
|
|
let routeModulesScript = !isStatic ? " " : `${_optionalChain([manifest, 'access', _94 => _94.hmr, 'optionalAccess', _95 => _95.runtime]) ? `import ${JSON.stringify(manifest.hmr.runtime)};` : ""}${!enableFogOfWar ? `import ${JSON.stringify(manifest.url)}` : ""};
|
|
${matches.map((match, routeIndex) => {
|
|
let routeVarName = `route${routeIndex}`;
|
|
let manifestEntry = manifest.routes[match.route.id];
|
|
invariant2(manifestEntry, `Route ${match.route.id} not found in manifest`);
|
|
let {
|
|
clientActionModule,
|
|
clientLoaderModule,
|
|
clientMiddlewareModule,
|
|
hydrateFallbackModule,
|
|
module
|
|
} = manifestEntry;
|
|
let chunks = [
|
|
...clientActionModule ? [
|
|
{
|
|
module: clientActionModule,
|
|
varName: `${routeVarName}_clientAction`
|
|
}
|
|
] : [],
|
|
...clientLoaderModule ? [
|
|
{
|
|
module: clientLoaderModule,
|
|
varName: `${routeVarName}_clientLoader`
|
|
}
|
|
] : [],
|
|
...clientMiddlewareModule ? [
|
|
{
|
|
module: clientMiddlewareModule,
|
|
varName: `${routeVarName}_clientMiddleware`
|
|
}
|
|
] : [],
|
|
...hydrateFallbackModule ? [
|
|
{
|
|
module: hydrateFallbackModule,
|
|
varName: `${routeVarName}_HydrateFallback`
|
|
}
|
|
] : [],
|
|
{ module, varName: `${routeVarName}_main` }
|
|
];
|
|
if (chunks.length === 1) {
|
|
return `import * as ${routeVarName} from ${JSON.stringify(module)};`;
|
|
}
|
|
let chunkImportsSnippet = chunks.map((chunk) => `import * as ${chunk.varName} from "${chunk.module}";`).join("\n");
|
|
let mergedChunksSnippet = `const ${routeVarName} = {${chunks.map((chunk) => `...${chunk.varName}`).join(",")}};`;
|
|
return [chunkImportsSnippet, mergedChunksSnippet].join("\n");
|
|
}).join("\n")}
|
|
${enableFogOfWar ? (
|
|
// Inline a minimal manifest with the SSR matches
|
|
`window.__reactRouterManifest = ${JSON.stringify(
|
|
getPartialManifest(manifest, router),
|
|
null,
|
|
2
|
|
)};`
|
|
) : ""}
|
|
window.__reactRouterRouteModules = {${matches.map((match, index) => `${JSON.stringify(match.route.id)}:route${index}`).join(",")}};
|
|
|
|
import(${JSON.stringify(manifest.entry.module)});`;
|
|
return /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(
|
|
"script",
|
|
{
|
|
...scriptProps,
|
|
suppressHydrationWarning: true,
|
|
dangerouslySetInnerHTML: createHtml(contextScript),
|
|
type: void 0
|
|
}
|
|
), /* @__PURE__ */ React7.createElement(
|
|
"script",
|
|
{
|
|
...scriptProps,
|
|
suppressHydrationWarning: true,
|
|
dangerouslySetInnerHTML: createHtml(routeModulesScript),
|
|
type: "module",
|
|
async: true
|
|
}
|
|
));
|
|
}, []);
|
|
let preloads = isHydrated || isRSCRouterContext ? [] : dedupe(
|
|
manifest.entry.imports.concat(
|
|
getModuleLinkHrefs(matches, manifest, {
|
|
includeHydrateFallback: true
|
|
})
|
|
)
|
|
);
|
|
let sri = typeof manifest.sri === "object" ? manifest.sri : {};
|
|
warnOnce(
|
|
!isRSCRouterContext,
|
|
"The <Scripts /> element is a no-op when using RSC and can be safely removed."
|
|
);
|
|
return isHydrated || isRSCRouterContext ? null : /* @__PURE__ */ React7.createElement(React7.Fragment, null, typeof manifest.sri === "object" ? /* @__PURE__ */ React7.createElement(
|
|
"script",
|
|
{
|
|
"rr-importmap": "",
|
|
type: "importmap",
|
|
suppressHydrationWarning: true,
|
|
dangerouslySetInnerHTML: {
|
|
__html: JSON.stringify({
|
|
integrity: sri
|
|
})
|
|
}
|
|
}
|
|
) : null, !enableFogOfWar ? /* @__PURE__ */ React7.createElement(
|
|
"link",
|
|
{
|
|
rel: "modulepreload",
|
|
href: manifest.url,
|
|
crossOrigin: scriptProps.crossOrigin,
|
|
integrity: sri[manifest.url],
|
|
suppressHydrationWarning: true
|
|
}
|
|
) : null, /* @__PURE__ */ React7.createElement(
|
|
"link",
|
|
{
|
|
rel: "modulepreload",
|
|
href: manifest.entry.module,
|
|
crossOrigin: scriptProps.crossOrigin,
|
|
integrity: sri[manifest.entry.module],
|
|
suppressHydrationWarning: true
|
|
}
|
|
), preloads.map((path) => /* @__PURE__ */ React7.createElement(
|
|
"link",
|
|
{
|
|
key: path,
|
|
rel: "modulepreload",
|
|
href: path,
|
|
crossOrigin: scriptProps.crossOrigin,
|
|
integrity: sri[path],
|
|
suppressHydrationWarning: true
|
|
}
|
|
)), initialScripts);
|
|
}
|
|
function dedupe(array) {
|
|
return [...new Set(array)];
|
|
}
|
|
function mergeRefs(...refs) {
|
|
return (value) => {
|
|
refs.forEach((ref) => {
|
|
if (typeof ref === "function") {
|
|
ref(value);
|
|
} else if (ref != null) {
|
|
ref.current = value;
|
|
}
|
|
});
|
|
};
|
|
}
|
|
|
|
// lib/dom/ssr/errorBoundaries.tsx
|
|
var RemixErrorBoundary = class extends React8.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = { error: props.error || null, location: props.location };
|
|
}
|
|
static getDerivedStateFromError(error) {
|
|
return { error };
|
|
}
|
|
static getDerivedStateFromProps(props, state) {
|
|
if (state.location !== props.location) {
|
|
return { error: props.error || null, location: props.location };
|
|
}
|
|
return { error: props.error || state.error, location: state.location };
|
|
}
|
|
render() {
|
|
if (this.state.error) {
|
|
return /* @__PURE__ */ React8.createElement(
|
|
RemixRootDefaultErrorBoundary,
|
|
{
|
|
error: this.state.error,
|
|
isOutsideRemixApp: true
|
|
}
|
|
);
|
|
} else {
|
|
return this.props.children;
|
|
}
|
|
}
|
|
};
|
|
function RemixRootDefaultErrorBoundary({
|
|
error,
|
|
isOutsideRemixApp
|
|
}) {
|
|
console.error(error);
|
|
let heyDeveloper = /* @__PURE__ */ React8.createElement(
|
|
"script",
|
|
{
|
|
dangerouslySetInnerHTML: {
|
|
__html: `
|
|
console.log(
|
|
"\u{1F4BF} Hey developer \u{1F44B}. You can provide a way better UX than this when your app throws errors. Check out https://reactrouter.com/how-to/error-boundary for more information."
|
|
);
|
|
`
|
|
}
|
|
}
|
|
);
|
|
if (isRouteErrorResponse(error)) {
|
|
return /* @__PURE__ */ React8.createElement(BoundaryShell, { title: "Unhandled Thrown Response!" }, /* @__PURE__ */ React8.createElement("h1", { style: { fontSize: "24px" } }, error.status, " ", error.statusText), ENABLE_DEV_WARNINGS ? heyDeveloper : null);
|
|
}
|
|
let errorInstance;
|
|
if (error instanceof Error) {
|
|
errorInstance = error;
|
|
} else {
|
|
let errorString = error == null ? "Unknown Error" : typeof error === "object" && "toString" in error ? error.toString() : JSON.stringify(error);
|
|
errorInstance = new Error(errorString);
|
|
}
|
|
return /* @__PURE__ */ React8.createElement(
|
|
BoundaryShell,
|
|
{
|
|
title: "Application Error!",
|
|
isOutsideRemixApp
|
|
},
|
|
/* @__PURE__ */ React8.createElement("h1", { style: { fontSize: "24px" } }, "Application Error"),
|
|
/* @__PURE__ */ React8.createElement(
|
|
"pre",
|
|
{
|
|
style: {
|
|
padding: "2rem",
|
|
background: "hsla(10, 50%, 50%, 0.1)",
|
|
color: "red",
|
|
overflow: "auto"
|
|
}
|
|
},
|
|
errorInstance.stack
|
|
),
|
|
heyDeveloper
|
|
);
|
|
}
|
|
function BoundaryShell({
|
|
title,
|
|
renderScripts,
|
|
isOutsideRemixApp,
|
|
children
|
|
}) {
|
|
let { routeModules } = useFrameworkContext();
|
|
if (_optionalChain([routeModules, 'access', _96 => _96.root, 'optionalAccess', _97 => _97.Layout]) && !isOutsideRemixApp) {
|
|
return children;
|
|
}
|
|
return /* @__PURE__ */ React8.createElement("html", { lang: "en" }, /* @__PURE__ */ React8.createElement("head", null, /* @__PURE__ */ React8.createElement("meta", { charSet: "utf-8" }), /* @__PURE__ */ React8.createElement(
|
|
"meta",
|
|
{
|
|
name: "viewport",
|
|
content: "width=device-width,initial-scale=1,viewport-fit=cover"
|
|
}
|
|
), /* @__PURE__ */ React8.createElement("title", null, title)), /* @__PURE__ */ React8.createElement("body", null, /* @__PURE__ */ React8.createElement("main", { style: { fontFamily: "system-ui, sans-serif", padding: "2rem" } }, children, renderScripts ? /* @__PURE__ */ React8.createElement(Scripts, null) : null)));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
exports.Action = Action; exports.createMemoryHistory = createMemoryHistory; exports.createBrowserHistory = createBrowserHistory; exports.createHashHistory = createHashHistory; exports.invariant = invariant; exports.warning = warning; exports.createPath = createPath; exports.parsePath = parsePath; exports.unstable_createContext = unstable_createContext; exports.unstable_RouterContextProvider = unstable_RouterContextProvider; exports.convertRoutesToDataRoutes = convertRoutesToDataRoutes; exports.matchRoutes = matchRoutes; exports.generatePath = generatePath; exports.matchPath = matchPath; exports.stripBasename = stripBasename; exports.resolvePath = resolvePath; exports.getResolveToMatches = getResolveToMatches; exports.resolveTo = resolveTo; exports.joinPaths = joinPaths; exports.data = data; exports.redirect = redirect; exports.redirectDocument = redirectDocument; exports.replace = replace; exports.ErrorResponseImpl = ErrorResponseImpl; exports.isRouteErrorResponse = isRouteErrorResponse; exports.encode = encode; exports.IDLE_NAVIGATION = IDLE_NAVIGATION; exports.IDLE_FETCHER = IDLE_FETCHER; exports.IDLE_BLOCKER = IDLE_BLOCKER; exports.createRouter = createRouter; exports.createStaticHandler = createStaticHandler; exports.getStaticContextFromError = getStaticContextFromError; exports.isDataWithResponseInit = isDataWithResponseInit; exports.isResponse = isResponse; exports.isRedirectStatusCode = isRedirectStatusCode; exports.isRedirectResponse = isRedirectResponse; exports.isMutationMethod = isMutationMethod; exports.createRequestInit = createRequestInit; exports.SingleFetchRedirectSymbol = SingleFetchRedirectSymbol; exports.SINGLE_FETCH_REDIRECT_STATUS = SINGLE_FETCH_REDIRECT_STATUS; exports.NO_BODY_STATUS_CODES = NO_BODY_STATUS_CODES; exports.StreamTransfer = StreamTransfer; exports.getTurboStreamSingleFetchDataStrategy = getTurboStreamSingleFetchDataStrategy; exports.getSingleFetchDataStrategyImpl = getSingleFetchDataStrategyImpl; exports.stripIndexParam = stripIndexParam; exports.singleFetchUrl = singleFetchUrl; exports.decodeViaTurboStream = decodeViaTurboStream; exports.DataRouterContext = DataRouterContext; exports.DataRouterStateContext = DataRouterStateContext; exports.RSCRouterContext = RSCRouterContext; exports.ViewTransitionContext = ViewTransitionContext; exports.FetchersContext = FetchersContext; exports.AwaitContext = AwaitContext; exports.NavigationContext = NavigationContext; exports.LocationContext = LocationContext; exports.RouteContext = RouteContext; exports.ENABLE_DEV_WARNINGS = ENABLE_DEV_WARNINGS; exports.warnOnce = warnOnce; exports.useHref = useHref; exports.useInRouterContext = useInRouterContext; exports.useLocation = useLocation; exports.useNavigationType = useNavigationType; exports.useMatch = useMatch; exports.useNavigate = useNavigate; exports.useOutletContext = useOutletContext; exports.useOutlet = useOutlet; exports.useParams = useParams; exports.useResolvedPath = useResolvedPath; exports.useRoutes = useRoutes; exports.useRoutesImpl = useRoutesImpl; exports._renderMatches = _renderMatches; exports.useRouteId = useRouteId; exports.useNavigation = useNavigation; exports.useRevalidator = useRevalidator; exports.useMatches = useMatches; exports.useLoaderData = useLoaderData; exports.useRouteLoaderData = useRouteLoaderData; exports.useActionData = useActionData; exports.useRouteError = useRouteError; exports.useAsyncValue = useAsyncValue; exports.useAsyncError = useAsyncError; exports.useBlocker = useBlocker; exports.RemixErrorBoundary = RemixErrorBoundary; exports.createServerRoutes = createServerRoutes; exports.createClientRoutesWithHMRRevalidationOptOut = createClientRoutesWithHMRRevalidationOptOut; exports.noActionDefinedError = noActionDefinedError; exports.createClientRoutes = createClientRoutes; exports.shouldHydrateRouteLoader = shouldHydrateRouteLoader; exports.getPatchRoutesOnNavigationFunction = getPatchRoutesOnNavigationFunction; exports.useFogOFWarDiscovery = useFogOFWarDiscovery; exports.getManifestPath = getManifestPath; exports.FrameworkContext = FrameworkContext; exports.usePrefetchBehavior = usePrefetchBehavior; exports.CRITICAL_CSS_DATA_ATTRIBUTE = CRITICAL_CSS_DATA_ATTRIBUTE; exports.Links = Links; exports.PrefetchPageLinks = PrefetchPageLinks; exports.Meta = Meta; exports.Scripts = Scripts; exports.mergeRefs = mergeRefs;
|