| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
| 9 | |
| 10 |
|
| 11 | import { ABSOLUTE_URL_REGEX, PROTOCOL_RELATIVE_URL_REGEX, normalizeProtocolRelativeUrl } from "./url.js";
|
| 12 | import { invariant, parsePath, warning } from "./history.js";
|
| 13 | import * as React$1 from "react";
|
| 14 |
|
| 15 | |
| 16 | |
| 17 | |
| 18 | |
| 19 | |
| 20 | |
| 21 | |
| 22 | |
| 23 | |
| 24 | |
| 25 | |
| 26 | |
| 27 | |
| 28 | |
| 29 | |
| 30 | |
| 31 | |
| 32 | |
| 33 | |
| 34 | |
| 35 | |
| 36 | |
| 37 | |
| 38 | |
| 39 | |
| 40 | |
| 41 | |
| 42 | |
| 43 | |
| 44 | |
| 45 | |
| 46 | |
| 47 | |
| 48 | |
| 49 | |
| 50 | |
| 51 | |
| 52 | |
| 53 | |
| 54 | |
| 55 | |
| 56 | |
| 57 | |
| 58 | |
| 59 | |
| 60 | |
| 61 | |
| 62 | |
| 63 | |
| 64 | |
| 65 | |
| 66 | |
| 67 | |
| 68 | |
| 69 | |
| 70 | |
| 71 | |
| 72 |
|
| 73 | function createContext(defaultValue) {
|
| 74 | return { defaultValue };
|
| 75 | }
|
| 76 | |
| 77 | |
| 78 | |
| 79 | |
| 80 | |
| 81 | |
| 82 | |
| 83 | |
| 84 | |
| 85 | |
| 86 | |
| 87 | |
| 88 | |
| 89 | |
| 90 | |
| 91 | |
| 92 | |
| 93 | |
| 94 | |
| 95 | |
| 96 | |
| 97 |
|
| 98 | var RouterContextProvider = class {
|
| 99 | #map = /* @__PURE__ */ new Map();
|
| 100 | |
| 101 | |
| 102 | |
| 103 |
|
| 104 | constructor(init) {
|
| 105 | if (init) for (let [context, value] of init) this.set(context, value);
|
| 106 | }
|
| 107 | |
| 108 | |
| 109 | |
| 110 | |
| 111 | |
| 112 | |
| 113 | |
| 114 |
|
| 115 | get(context) {
|
| 116 | if (this.#map.has(context)) return this.#map.get(context);
|
| 117 | if (context.defaultValue !== void 0) return context.defaultValue;
|
| 118 | throw new Error("No value found for context");
|
| 119 | }
|
| 120 | |
| 121 | |
| 122 | |
| 123 | |
| 124 | |
| 125 | |
| 126 | |
| 127 |
|
| 128 | set(context, value) {
|
| 129 | this.#map.set(context, value);
|
| 130 | }
|
| 131 | };
|
| 132 | const unsupportedLazyRouteObjectKeys = new Set([
|
| 133 | "lazy",
|
| 134 | "caseSensitive",
|
| 135 | "path",
|
| 136 | "id",
|
| 137 | "index",
|
| 138 | "children"
|
| 139 | ]);
|
| 140 | function isUnsupportedLazyRouteObjectKey(key) {
|
| 141 | return unsupportedLazyRouteObjectKeys.has(key);
|
| 142 | }
|
| 143 | const unsupportedLazyRouteFunctionKeys = new Set([
|
| 144 | "lazy",
|
| 145 | "caseSensitive",
|
| 146 | "path",
|
| 147 | "id",
|
| 148 | "index",
|
| 149 | "middleware",
|
| 150 | "children"
|
| 151 | ]);
|
| 152 | function isUnsupportedLazyRouteFunctionKey(key) {
|
| 153 | return unsupportedLazyRouteFunctionKeys.has(key);
|
| 154 | }
|
| 155 | function isIndexRoute(route) {
|
| 156 | return route.index === true;
|
| 157 | }
|
| 158 | function defaultMapRouteProperties(route) {
|
| 159 | let updates = {};
|
| 160 | if (route.Component) {
|
| 161 | if (route.element) warning(false, "You should not include both `Component` and `element` on your route - `Component` will be used.");
|
| 162 | Object.assign(updates, {
|
| 163 | element: React$1.createElement(route.Component),
|
| 164 | Component: void 0
|
| 165 | });
|
| 166 | }
|
| 167 | if (route.HydrateFallback) {
|
| 168 | if (route.hydrateFallbackElement) warning(false, "You should not include both `HydrateFallback` and `hydrateFallbackElement` on your route - `HydrateFallback` will be used.");
|
| 169 | Object.assign(updates, {
|
| 170 | hydrateFallbackElement: React$1.createElement(route.HydrateFallback),
|
| 171 | HydrateFallback: void 0
|
| 172 | });
|
| 173 | }
|
| 174 | if (route.ErrorBoundary) {
|
| 175 | if (route.errorElement) warning(false, "You should not include both `ErrorBoundary` and `errorElement` on your route - `ErrorBoundary` will be used.");
|
| 176 | Object.assign(updates, {
|
| 177 | errorElement: React$1.createElement(route.ErrorBoundary),
|
| 178 | ErrorBoundary: void 0
|
| 179 | });
|
| 180 | }
|
| 181 | return updates;
|
| 182 | }
|
| 183 | function convertRoutesToDataRoutes(routes, mapRouteProperties = defaultMapRouteProperties, parentPath = [], manifest = {}, allowInPlaceMutations = false) {
|
| 184 | return routes.map((route, index) => {
|
| 185 | let treePath = [...parentPath, String(index)];
|
| 186 | let id = typeof route.id === "string" ? route.id : treePath.join("-");
|
| 187 | invariant(route.index !== true || !route.children, `Cannot specify children on an index route`);
|
| 188 | invariant(allowInPlaceMutations || !manifest[id], `Found a route id collision on id "${id}". Route id's must be globally unique within Data Router usages`);
|
| 189 | if (isIndexRoute(route)) {
|
| 190 | let indexRoute = {
|
| 191 | ...route,
|
| 192 | id
|
| 193 | };
|
| 194 | manifest[id] = mergeRouteUpdates(indexRoute, mapRouteProperties(indexRoute));
|
| 195 | return indexRoute;
|
| 196 | } else {
|
| 197 | let pathOrLayoutRoute = {
|
| 198 | ...route,
|
| 199 | id,
|
| 200 | children: void 0
|
| 201 | };
|
| 202 | manifest[id] = mergeRouteUpdates(pathOrLayoutRoute, mapRouteProperties(pathOrLayoutRoute));
|
| 203 | if (route.children) pathOrLayoutRoute.children = convertRoutesToDataRoutes(route.children, mapRouteProperties, treePath, manifest, allowInPlaceMutations);
|
| 204 | return pathOrLayoutRoute;
|
| 205 | }
|
| 206 | });
|
| 207 | }
|
| 208 | function mergeRouteUpdates(route, updates) {
|
| 209 | return Object.assign(route, {
|
| 210 | ...updates,
|
| 211 | ...typeof updates.lazy === "object" && updates.lazy != null ? { lazy: {
|
| 212 | ...route.lazy,
|
| 213 | ...updates.lazy
|
| 214 | } } : {}
|
| 215 | });
|
| 216 | }
|
| 217 | |
| 218 | |
| 219 | |
| 220 | |
| 221 | |
| 222 | |
| 223 | |
| 224 | |
| 225 | |
| 226 | |
| 227 | |
| 228 | |
| 229 | |
| 230 | |
| 231 | |
| 232 | |
| 233 | |
| 234 | |
| 235 | |
| 236 | |
| 237 | |
| 238 | |
| 239 | |
| 240 | |
| 241 | |
| 242 |
|
| 243 | function matchRoutes(routes, locationArg, basename = "/") {
|
| 244 | return matchRoutesImpl(routes, locationArg, basename, false);
|
| 245 | }
|
| 246 | function matchRoutesImpl(routes, locationArg, basename, allowPartial, precomputedBranches) {
|
| 247 | let pathname = stripBasename((typeof locationArg === "string" ? parsePath(locationArg) : locationArg).pathname || "/", basename);
|
| 248 | if (pathname == null) return null;
|
| 249 | let branches = precomputedBranches ?? flattenAndRankRoutes(routes);
|
| 250 | let matches = null;
|
| 251 | let decoded = decodePath(pathname);
|
| 252 | for (let i = 0; matches == null && i < branches.length; ++i) matches = matchRouteBranch(branches[i], decoded, allowPartial);
|
| 253 | return matches;
|
| 254 | }
|
| 255 | function convertRouteMatchToUiMatch(match, loaderData) {
|
| 256 | let { route, pathname, params } = match;
|
| 257 | return {
|
| 258 | id: route.id,
|
| 259 | pathname,
|
| 260 | params,
|
| 261 | loaderData: loaderData[route.id],
|
| 262 | handle: route.handle
|
| 263 | };
|
| 264 | }
|
| 265 | function flattenAndRankRoutes(routes) {
|
| 266 | let branches = flattenRoutes(routes);
|
| 267 | rankRouteBranches(branches);
|
| 268 | return branches;
|
| 269 | }
|
| 270 | function flattenRoutes(routes, branches = [], parentsMeta = [], parentPath = "", _hasParentOptionalSegments = false) {
|
| 271 | let flattenRoute = (route, index, hasParentOptionalSegments = _hasParentOptionalSegments, relativePath) => {
|
| 272 | let meta = {
|
| 273 | relativePath: relativePath === void 0 ? route.path || "" : relativePath,
|
| 274 | caseSensitive: route.caseSensitive === true,
|
| 275 | childrenIndex: index,
|
| 276 | route
|
| 277 | };
|
| 278 | if (meta.relativePath.startsWith("/")) {
|
| 279 | if (!meta.relativePath.startsWith(parentPath) && hasParentOptionalSegments) return;
|
| 280 | 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.`);
|
| 281 | meta.relativePath = meta.relativePath.slice(parentPath.length);
|
| 282 | }
|
| 283 | let path = joinPaths([parentPath, meta.relativePath]);
|
| 284 | let routesMeta = parentsMeta.concat(meta);
|
| 285 | if (route.children && route.children.length > 0) {
|
| 286 | invariant(route.index !== true, `Index routes must not have child routes. Please remove all child routes from route path "${path}".`);
|
| 287 | flattenRoutes(route.children, branches, routesMeta, path, hasParentOptionalSegments);
|
| 288 | }
|
| 289 | if (route.path == null && !route.index) return;
|
| 290 | branches.push({
|
| 291 | path,
|
| 292 | score: computeScore(path, route.index),
|
| 293 | routesMeta: routesMeta.map((meta, i) => {
|
| 294 | let [matcher, params] = compilePath(meta.relativePath, meta.caseSensitive, i === routesMeta.length - 1);
|
| 295 | return {
|
| 296 | ...meta,
|
| 297 | matcher,
|
| 298 | compiledParams: params
|
| 299 | };
|
| 300 | })
|
| 301 | });
|
| 302 | };
|
| 303 | routes.forEach((route, index) => {
|
| 304 | if (route.path === "" || !route.path?.includes("?")) flattenRoute(route, index);
|
| 305 | else for (let exploded of explodeOptionalSegments(route.path)) flattenRoute(route, index, true, exploded);
|
| 306 | });
|
| 307 | return branches;
|
| 308 | }
|
| 309 | function explodeOptionalSegments(path) {
|
| 310 | let segments = path.split("/");
|
| 311 | if (segments.length === 0) return [];
|
| 312 | let [first, ...rest] = segments;
|
| 313 | let isOptional = first.endsWith("?");
|
| 314 | let required = first.replace(/\?$/, "");
|
| 315 | if (rest.length === 0) return isOptional ? [required, ""] : [required];
|
| 316 | let restExploded = explodeOptionalSegments(rest.join("/"));
|
| 317 | let result = [];
|
| 318 | result.push(...restExploded.map((subpath) => subpath === "" ? required : [required, subpath].join("/")));
|
| 319 | if (isOptional) result.push(...restExploded);
|
| 320 | return result.map((exploded) => path.startsWith("/") && exploded === "" ? "/" : exploded);
|
| 321 | }
|
| 322 | function rankRouteBranches(branches) {
|
| 323 | 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)));
|
| 324 | }
|
| 325 | const paramRe = /^:[\w-]+$/;
|
| 326 | const dynamicSegmentValue = 3;
|
| 327 | const indexRouteValue = 2;
|
| 328 | const emptySegmentValue = 1;
|
| 329 | const staticSegmentValue = 10;
|
| 330 | const splatPenalty = -2;
|
| 331 | const isSplat = (s) => s === "*";
|
| 332 | function computeScore(path, index) {
|
| 333 | let segments = path.split("/");
|
| 334 | let initialScore = segments.length;
|
| 335 | if (segments.some(isSplat)) initialScore += splatPenalty;
|
| 336 | if (index) initialScore += indexRouteValue;
|
| 337 | return segments.filter((s) => !isSplat(s)).reduce((score, segment) => score + (paramRe.test(segment) ? dynamicSegmentValue : segment === "" ? emptySegmentValue : staticSegmentValue), initialScore);
|
| 338 | }
|
| 339 | function compareIndexes(a, b) {
|
| 340 | return a.length === b.length && a.slice(0, -1).every((n, i) => n === b[i]) ? a[a.length - 1] - b[b.length - 1] : 0;
|
| 341 | }
|
| 342 | function matchRouteBranch(branch, pathname, allowPartial = false) {
|
| 343 | let { routesMeta } = branch;
|
| 344 | let matchedParams = {};
|
| 345 | let matchedPathname = "/";
|
| 346 | let matches = [];
|
| 347 | for (let i = 0; i < routesMeta.length; ++i) {
|
| 348 | let meta = routesMeta[i];
|
| 349 | let end = i === routesMeta.length - 1;
|
| 350 | let remainingPathname = matchedPathname === "/" ? pathname : pathname.slice(matchedPathname.length) || "/";
|
| 351 | let pattern = {
|
| 352 | path: meta.relativePath,
|
| 353 | caseSensitive: meta.caseSensitive,
|
| 354 | end
|
| 355 | };
|
| 356 | let match = meta.matcher && meta.compiledParams ? matchPathImpl(pattern, remainingPathname, meta.matcher, meta.compiledParams) : matchPath(pattern, remainingPathname);
|
| 357 | let route = meta.route;
|
| 358 | if (!match && end && allowPartial && !routesMeta[routesMeta.length - 1].route.index) match = matchPath({
|
| 359 | path: meta.relativePath,
|
| 360 | caseSensitive: meta.caseSensitive,
|
| 361 | end: false
|
| 362 | }, remainingPathname);
|
| 363 | if (!match) return null;
|
| 364 | Object.assign(matchedParams, match.params);
|
| 365 | matches.push({
|
| 366 | params: matchedParams,
|
| 367 | pathname: joinPaths([matchedPathname, match.pathname]),
|
| 368 | pathnameBase: normalizePathname(joinPaths([matchedPathname, match.pathnameBase])),
|
| 369 | route
|
| 370 | });
|
| 371 | if (match.pathnameBase !== "/") matchedPathname = joinPaths([matchedPathname, match.pathnameBase]);
|
| 372 | }
|
| 373 | return matches;
|
| 374 | }
|
| 375 | |
| 376 | |
| 377 | |
| 378 | |
| 379 | |
| 380 | |
| 381 | |
| 382 | |
| 383 | |
| 384 | |
| 385 | |
| 386 | |
| 387 | |
| 388 |
|
| 389 | function generatePath(originalPath, params = {}) {
|
| 390 | let path = originalPath;
|
| 391 | if (path.endsWith("*") && path !== "*" && !path.endsWith("/*")) {
|
| 392 | 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(/\*$/, "/*")}".`);
|
| 393 | path = path.replace(/\*$/, "/*");
|
| 394 | }
|
| 395 | const prefix = path.startsWith("/") ? "/" : "";
|
| 396 | const stringify = (p) => p == null ? "" : typeof p === "string" ? p : String(p);
|
| 397 | return prefix + path.split(/\/+/).map((segment, index, array) => {
|
| 398 | if (index === array.length - 1 && segment === "*") return stringify(params["*"]);
|
| 399 | const keyMatch = segment.match(/^:([\w-]+)(\??)(.*)/);
|
| 400 | if (keyMatch) {
|
| 401 | const [, key, optional, suffix] = keyMatch;
|
| 402 | let param = params[key];
|
| 403 | invariant(optional === "?" || param != null, `Missing ":${key}" param`);
|
| 404 | return encodeURIComponent(stringify(param)) + suffix;
|
| 405 | }
|
| 406 | return segment.replace(/\?$/g, "");
|
| 407 | }).filter((segment) => !!segment).join("/");
|
| 408 | }
|
| 409 | |
| 410 | |
| 411 | |
| 412 | |
| 413 | |
| 414 | |
| 415 | |
| 416 | |
| 417 | |
| 418 | |
| 419 | |
| 420 | |
| 421 | |
| 422 |
|
| 423 | function matchPath(pattern, pathname) {
|
| 424 | if (typeof pattern === "string") pattern = {
|
| 425 | path: pattern,
|
| 426 | caseSensitive: false,
|
| 427 | end: true
|
| 428 | };
|
| 429 | let [matcher, compiledParams] = compilePath(pattern.path, pattern.caseSensitive, pattern.end);
|
| 430 | return matchPathImpl(pattern, pathname, matcher, compiledParams);
|
| 431 | }
|
| 432 | function matchPathImpl(pattern, pathname, matcher, compiledParams) {
|
| 433 | let match = pathname.match(matcher);
|
| 434 | if (!match) return null;
|
| 435 | let matchedPathname = match[0];
|
| 436 | let pathnameBase = matchedPathname.replace(/(.)\/+$/, "$1");
|
| 437 | let captureGroups = match.slice(1);
|
| 438 | return {
|
| 439 | params: compiledParams.reduce((memo, { paramName, isOptional }, index) => {
|
| 440 | if (paramName === "*") {
|
| 441 | let splatValue = captureGroups[index] || "";
|
| 442 | pathnameBase = matchedPathname.slice(0, matchedPathname.length - splatValue.length).replace(/(.)\/+$/, "$1");
|
| 443 | }
|
| 444 | const value = captureGroups[index];
|
| 445 | if (isOptional && !value) memo[paramName] = void 0;
|
| 446 | else memo[paramName] = (value || "").replace(/%2F/g, "/");
|
| 447 | return memo;
|
| 448 | }, {}),
|
| 449 | pathname: matchedPathname,
|
| 450 | pathnameBase,
|
| 451 | pattern
|
| 452 | };
|
| 453 | }
|
| 454 | function compilePath(path, caseSensitive = false, end = true) {
|
| 455 | 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(/\*$/, "/*")}".`);
|
| 456 | let params = [];
|
| 457 | let regexpSource = "^" + path.replace(/\/*\*?$/, "").replace(/^\/*/, "/").replace(/[\\.*+^${}|()[\]]/g, "\\$&").replace(/\/:([\w-]+)(\?)?/g, (match, paramName, isOptional, index, str) => {
|
| 458 | params.push({
|
| 459 | paramName,
|
| 460 | isOptional: isOptional != null
|
| 461 | });
|
| 462 | if (isOptional) {
|
| 463 | let nextChar = str.charAt(index + match.length);
|
| 464 | if (nextChar && nextChar !== "/") return "/([^\\/]*)";
|
| 465 | return "(?:/([^\\/]*))?";
|
| 466 | }
|
| 467 | return "/([^\\/]+)";
|
| 468 | }).replace(/\/([\w-]+)\?(\/|$)/g, "(/$1)?$2");
|
| 469 | if (path.endsWith("*")) {
|
| 470 | params.push({ paramName: "*" });
|
| 471 | regexpSource += path === "*" || path === "/*" ? "(.*)$" : "(?:\\/(.+)|\\/*)$";
|
| 472 | } else if (end) regexpSource += "\\/*$";
|
| 473 | else if (path !== "" && path !== "/") regexpSource += "(?:(?=\\/|$))";
|
| 474 | return [new RegExp(regexpSource, caseSensitive ? void 0 : "i"), params];
|
| 475 | }
|
| 476 | function decodePath(value) {
|
| 477 | try {
|
| 478 | return value.split("/").map((v) => decodeURIComponent(v).replace(/\//g, "%2F")).join("/");
|
| 479 | } catch (error) {
|
| 480 | 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}).`);
|
| 481 | return value;
|
| 482 | }
|
| 483 | }
|
| 484 | function stripBasename(pathname, basename) {
|
| 485 | if (basename === "/") return pathname;
|
| 486 | if (!pathname.toLowerCase().startsWith(basename.toLowerCase())) return null;
|
| 487 | let startIndex = basename.endsWith("/") ? basename.length - 1 : basename.length;
|
| 488 | let nextChar = pathname.charAt(startIndex);
|
| 489 | if (nextChar && nextChar !== "/") return null;
|
| 490 | return pathname.slice(startIndex) || "/";
|
| 491 | }
|
| 492 | function prependBasename({ basename, pathname }) {
|
| 493 | return pathname === "/" ? basename : joinPaths([basename, pathname]);
|
| 494 | }
|
| 495 | const isAbsoluteUrl = (url) => ABSOLUTE_URL_REGEX.test(url);
|
| 496 | |
| 497 | |
| 498 | |
| 499 | |
| 500 | |
| 501 | |
| 502 | |
| 503 | |
| 504 | |
| 505 |
|
| 506 | function resolvePath(to, fromPathname = "/") {
|
| 507 | let { pathname: toPathname, search = "", hash = "" } = typeof to === "string" ? parsePath(to) : to;
|
| 508 | let pathname;
|
| 509 | if (toPathname) {
|
| 510 | toPathname = removeDoubleSlashes(toPathname);
|
| 511 | if (toPathname.startsWith("/")) pathname = resolvePathname(toPathname.substring(1), "/");
|
| 512 | else pathname = resolvePathname(toPathname, fromPathname);
|
| 513 | } else pathname = fromPathname;
|
| 514 | return {
|
| 515 | pathname,
|
| 516 | search: normalizeSearch(search),
|
| 517 | hash: normalizeHash(hash)
|
| 518 | };
|
| 519 | }
|
| 520 | function resolvePathname(relativePath, fromPathname) {
|
| 521 | let segments = removeTrailingSlash(fromPathname).split("/");
|
| 522 | relativePath.split("/").forEach((segment) => {
|
| 523 | if (segment === "..") {
|
| 524 | if (segments.length > 1) segments.pop();
|
| 525 | } else if (segment !== ".") segments.push(segment);
|
| 526 | });
|
| 527 | return segments.length > 1 ? segments.join("/") : "/";
|
| 528 | }
|
| 529 | function getInvalidPathError(char, field, dest, path) {
|
| 530 | 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.`;
|
| 531 | }
|
| 532 | function getPathContributingMatches(matches) {
|
| 533 | return matches.filter((match, index) => index === 0 || match.route.path && match.route.path.length > 0);
|
| 534 | }
|
| 535 | function getResolveToMatches(matches) {
|
| 536 | let pathMatches = getPathContributingMatches(matches);
|
| 537 | return pathMatches.map((match, idx) => idx === pathMatches.length - 1 ? match.pathname : match.pathnameBase);
|
| 538 | }
|
| 539 | function resolveTo(toArg, routePathnames, locationPathname, isPathRelative = false) {
|
| 540 | let to;
|
| 541 | if (typeof toArg === "string") to = parsePath(toArg);
|
| 542 | else {
|
| 543 | to = { ...toArg };
|
| 544 | invariant(!to.pathname || !to.pathname.includes("?"), getInvalidPathError("?", "pathname", "search", to));
|
| 545 | invariant(!to.pathname || !to.pathname.includes("#"), getInvalidPathError("#", "pathname", "hash", to));
|
| 546 | invariant(!to.search || !to.search.includes("#"), getInvalidPathError("#", "search", "hash", to));
|
| 547 | }
|
| 548 | let isEmptyPath = toArg === "" || to.pathname === "";
|
| 549 | let toPathname = isEmptyPath ? "/" : to.pathname;
|
| 550 | let from;
|
| 551 | if (toPathname == null) from = locationPathname;
|
| 552 | else {
|
| 553 | let routePathnameIndex = routePathnames.length - 1;
|
| 554 | if (!isPathRelative && toPathname.startsWith("..")) {
|
| 555 | let toSegments = toPathname.split("/");
|
| 556 | while (toSegments[0] === "..") {
|
| 557 | toSegments.shift();
|
| 558 | routePathnameIndex -= 1;
|
| 559 | }
|
| 560 | to.pathname = toSegments.join("/");
|
| 561 | }
|
| 562 | from = routePathnameIndex >= 0 ? routePathnames[routePathnameIndex] : "/";
|
| 563 | }
|
| 564 | let path = resolvePath(to, from);
|
| 565 | let hasExplicitTrailingSlash = toPathname && toPathname !== "/" && toPathname.endsWith("/");
|
| 566 | let hasCurrentTrailingSlash = (isEmptyPath || toPathname === ".") && locationPathname.endsWith("/");
|
| 567 | if (!path.pathname.endsWith("/") && (hasExplicitTrailingSlash || hasCurrentTrailingSlash)) path.pathname += "/";
|
| 568 | return path;
|
| 569 | }
|
| 570 | const removeDoubleSlashes = (path) => path.replace(/[\\/]{2,}/g, "/");
|
| 571 | const joinPaths = (paths) => removeDoubleSlashes(paths.join("/"));
|
| 572 | const removeTrailingSlash = (path) => path.replace(/\/+$/, "");
|
| 573 | const normalizePathname = (pathname) => removeTrailingSlash(pathname).replace(/^\/*/, "/");
|
| 574 | const normalizeSearch = (search) => !search || search === "?" ? "" : search.startsWith("?") ? search : "?" + search;
|
| 575 | const normalizeHash = (hash) => !hash || hash === "#" ? "" : hash.startsWith("#") ? hash : "#" + hash;
|
| 576 | var DataWithResponseInit = class {
|
| 577 | type = "DataWithResponseInit";
|
| 578 | data;
|
| 579 | init;
|
| 580 | constructor(data, init) {
|
| 581 | this.data = data;
|
| 582 | this.init = init || null;
|
| 583 | }
|
| 584 | };
|
| 585 | |
| 586 | |
| 587 | |
| 588 | |
| 589 | |
| 590 | |
| 591 | |
| 592 | |
| 593 | |
| 594 | |
| 595 | |
| 596 | |
| 597 | |
| 598 | |
| 599 | |
| 600 | |
| 601 | |
| 602 | |
| 603 | |
| 604 | |
| 605 | |
| 606 | |
| 607 | |
| 608 | |
| 609 | |
| 610 |
|
| 611 | function data(data, init) {
|
| 612 | return new DataWithResponseInit(data, typeof init === "number" ? { status: init } : init);
|
| 613 | }
|
| 614 | |
| 615 | |
| 616 | |
| 617 | |
| 618 | |
| 619 | |
| 620 | |
| 621 | |
| 622 | |
| 623 | |
| 624 | |
| 625 | |
| 626 | |
| 627 | |
| 628 | |
| 629 | |
| 630 | |
| 631 | |
| 632 | |
| 633 | |
| 634 | |
| 635 | |
| 636 | |
| 637 | |
| 638 | |
| 639 | |
| 640 | |
| 641 | |
| 642 | |
| 643 |
|
| 644 | const redirect = (url, init = 302) => {
|
| 645 | let responseInit = init;
|
| 646 | if (typeof responseInit === "number") responseInit = { status: responseInit };
|
| 647 | else if (typeof responseInit.status === "undefined") responseInit.status = 302;
|
| 648 | let headers = new Headers(responseInit.headers);
|
| 649 | headers.set("Location", url);
|
| 650 | return new Response(null, {
|
| 651 | ...responseInit,
|
| 652 | headers
|
| 653 | });
|
| 654 | };
|
| 655 | |
| 656 | |
| 657 | |
| 658 | |
| 659 | |
| 660 | |
| 661 | |
| 662 | |
| 663 | |
| 664 | |
| 665 | |
| 666 | |
| 667 | |
| 668 | |
| 669 | |
| 670 | |
| 671 | |
| 672 | |
| 673 | |
| 674 | |
| 675 | |
| 676 | |
| 677 | |
| 678 | |
| 679 | |
| 680 | |
| 681 | |
| 682 | |
| 683 | |
| 684 | |
| 685 | |
| 686 | |
| 687 |
|
| 688 | const redirectDocument = (url, init) => {
|
| 689 | let response = redirect(url, init);
|
| 690 | response.headers.set("X-Remix-Reload-Document", "true");
|
| 691 | return response;
|
| 692 | };
|
| 693 | |
| 694 | |
| 695 | |
| 696 | |
| 697 | |
| 698 | |
| 699 | |
| 700 | |
| 701 | |
| 702 | |
| 703 | |
| 704 | |
| 705 | |
| 706 | |
| 707 | |
| 708 | |
| 709 | |
| 710 | |
| 711 | |
| 712 | |
| 713 | |
| 714 | |
| 715 | |
| 716 | |
| 717 |
|
| 718 | const replace = (url, init) => {
|
| 719 | let response = redirect(url, init);
|
| 720 | response.headers.set("X-Remix-Replace", "true");
|
| 721 | return response;
|
| 722 | };
|
| 723 | const SUPPORTED_ERROR_TYPES = [
|
| 724 | "EvalError",
|
| 725 | "RangeError",
|
| 726 | "ReferenceError",
|
| 727 | "SyntaxError",
|
| 728 | "TypeError",
|
| 729 | "URIError"
|
| 730 | ];
|
| 731 | var ErrorResponseImpl = class {
|
| 732 | status;
|
| 733 | statusText;
|
| 734 | data;
|
| 735 | error;
|
| 736 | internal;
|
| 737 | constructor(status, statusText, data, internal = false) {
|
| 738 | this.status = status;
|
| 739 | this.statusText = statusText || "";
|
| 740 | this.internal = internal;
|
| 741 | if (data instanceof Error) {
|
| 742 | this.data = data.toString();
|
| 743 | this.error = data;
|
| 744 | } else this.data = data;
|
| 745 | }
|
| 746 | };
|
| 747 | |
| 748 | |
| 749 | |
| 750 | |
| 751 | |
| 752 | |
| 753 | |
| 754 | |
| 755 | |
| 756 | |
| 757 | |
| 758 | |
| 759 | |
| 760 | |
| 761 | |
| 762 | |
| 763 | |
| 764 | |
| 765 | |
| 766 | |
| 767 | |
| 768 | |
| 769 | |
| 770 | |
| 771 | |
| 772 | |
| 773 | |
| 774 | |
| 775 | |
| 776 | |
| 777 |
|
| 778 | function isRouteErrorResponse(error) {
|
| 779 | return error != null && typeof error.status === "number" && typeof error.statusText === "string" && typeof error.internal === "boolean" && "data" in error;
|
| 780 | }
|
| 781 | function getRoutePattern(matches) {
|
| 782 | return joinPaths(matches.map((m) => m.route.path).filter(Boolean)) || "/";
|
| 783 | }
|
| 784 | const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined";
|
| 785 | function parseToInfo(_to, basename) {
|
| 786 | let to = _to;
|
| 787 | if (typeof to !== "string" || !ABSOLUTE_URL_REGEX.test(to)) return {
|
| 788 | absoluteURL: void 0,
|
| 789 | isExternal: false,
|
| 790 | to
|
| 791 | };
|
| 792 | let absoluteURL = to;
|
| 793 | let isExternal = false;
|
| 794 | if (isBrowser) try {
|
| 795 | let currentUrl = new URL(window.location.href);
|
| 796 | let targetUrl = PROTOCOL_RELATIVE_URL_REGEX.test(to) ? new URL(normalizeProtocolRelativeUrl(to, currentUrl.protocol)) : new URL(to);
|
| 797 | let path = stripBasename(targetUrl.pathname, basename);
|
| 798 | if (targetUrl.origin === currentUrl.origin && path != null) to = path + targetUrl.search + targetUrl.hash;
|
| 799 | else isExternal = true;
|
| 800 | } catch (e) {
|
| 801 | warning(false, `<Link to="${to}"> contains an invalid URL which will probably break when clicked - please update to a valid URL path.`);
|
| 802 | }
|
| 803 | return {
|
| 804 | absoluteURL,
|
| 805 | isExternal,
|
| 806 | to
|
| 807 | };
|
| 808 | }
|
| 809 |
|
| 810 | export { ErrorResponseImpl, RouterContextProvider, SUPPORTED_ERROR_TYPES, compilePath, convertRouteMatchToUiMatch, convertRoutesToDataRoutes, createContext, data, decodePath, defaultMapRouteProperties, flattenAndRankRoutes, generatePath, getPathContributingMatches, getResolveToMatches, getRoutePattern, isAbsoluteUrl, isBrowser, isRouteErrorResponse, isUnsupportedLazyRouteFunctionKey, isUnsupportedLazyRouteObjectKey, joinPaths, matchPath, matchRoutes, matchRoutesImpl, parseToInfo, prependBasename, redirect, redirectDocument, removeDoubleSlashes, removeTrailingSlash, replace, resolvePath, resolveTo, stripBasename };
|