react-router 8.1.0 → 8.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/dist/development/dom-export.js +1 -1
- package/dist/development/index-react-server-client.js +1 -1
- package/dist/development/index-react-server.d.ts +3 -2
- package/dist/development/index-react-server.js +11 -6
- package/dist/development/index.js +1 -1
- package/dist/development/lib/actions.js +1 -1
- package/dist/development/lib/components.js +1 -1
- package/dist/development/lib/context.js +1 -1
- package/dist/development/lib/dom/dom.js +1 -1
- package/dist/development/lib/dom/lib.js +2 -2
- package/dist/development/lib/dom/server.js +1 -1
- package/dist/development/lib/dom/ssr/components.js +1 -1
- package/dist/development/lib/dom/ssr/data.js +1 -1
- package/dist/development/lib/dom/ssr/errorBoundaries.js +1 -1
- package/dist/development/lib/dom/ssr/fallback.js +1 -1
- package/dist/development/lib/dom/ssr/fog-of-war.js +1 -1
- package/dist/development/lib/dom/ssr/hydration.js +1 -1
- package/dist/development/lib/dom/ssr/invariant.js +1 -1
- package/dist/development/lib/dom/ssr/links.js +1 -1
- package/dist/development/lib/dom/ssr/markup.js +1 -1
- package/dist/development/lib/dom/ssr/routeModules.js +1 -1
- package/dist/development/lib/dom/ssr/routes-test-stub.js +1 -1
- package/dist/development/lib/dom/ssr/routes.js +1 -1
- package/dist/development/lib/dom/ssr/server.js +1 -1
- package/dist/development/lib/dom/ssr/single-fetch.js +1 -1
- package/dist/development/lib/dom-export/dom-router-provider.js +1 -1
- package/dist/development/lib/dom-export/hydrated-router.js +1 -1
- package/dist/development/lib/errors.js +1 -1
- package/dist/development/lib/hooks.d.ts +33 -6
- package/dist/development/lib/hooks.js +34 -7
- package/dist/development/lib/href.js +6 -3
- package/dist/development/lib/router/history.js +1 -1
- package/dist/development/lib/router/instrumentation.js +1 -1
- package/dist/development/lib/router/router.js +2 -2
- package/dist/development/lib/router/url.js +1 -1
- package/dist/development/lib/router/utils.js +5 -3
- package/dist/development/lib/rsc/browser.js +1 -1
- package/dist/development/lib/rsc/errorBoundaries.js +1 -1
- package/dist/development/lib/rsc/html-stream/browser.js +1 -1
- package/dist/development/lib/rsc/html-stream/server.js +1 -1
- package/dist/development/lib/rsc/route-modules.js +1 -1
- package/dist/development/lib/rsc/server.rsc.d.ts +3 -2
- package/dist/development/lib/rsc/server.ssr.js +1 -1
- package/dist/development/lib/server-runtime/cookies.js +1 -1
- package/dist/development/lib/server-runtime/crypto.js +1 -1
- package/dist/development/lib/server-runtime/data.js +1 -1
- package/dist/development/lib/server-runtime/dev.js +1 -1
- package/dist/development/lib/server-runtime/entry.js +1 -1
- package/dist/development/lib/server-runtime/errors.js +1 -1
- package/dist/development/lib/server-runtime/headers.js +1 -1
- package/dist/development/lib/server-runtime/invariant.js +1 -1
- package/dist/development/lib/server-runtime/mode.js +1 -1
- package/dist/development/lib/server-runtime/routeMatching.js +1 -1
- package/dist/development/lib/server-runtime/routes.js +1 -1
- package/dist/development/lib/server-runtime/server.js +1 -1
- package/dist/development/lib/server-runtime/serverHandoff.js +1 -1
- package/dist/development/lib/server-runtime/sessions/cookieStorage.js +1 -1
- package/dist/development/lib/server-runtime/sessions/memoryStorage.js +1 -1
- package/dist/development/lib/server-runtime/sessions.js +1 -1
- package/dist/development/lib/server-runtime/single-fetch.js +1 -1
- package/dist/development/lib/server-runtime/urls.js +1 -1
- package/dist/development/lib/server-runtime/warnings.js +1 -1
- package/dist/development/lib/types/internal.js +1 -1
- package/dist/development/vendor/turbo-stream-v2/flatten.js +1 -1
- package/dist/development/vendor/turbo-stream-v2/turbo-stream.js +1 -1
- package/dist/development/vendor/turbo-stream-v2/unflatten.js +1 -1
- package/dist/development/vendor/turbo-stream-v2/utils.js +1 -1
- package/dist/production/dom-export.js +1 -1
- package/dist/production/index-react-server-client.js +1 -1
- package/dist/production/index-react-server.d.ts +3 -2
- package/dist/production/index-react-server.js +11 -6
- package/dist/production/index.js +1 -1
- package/dist/production/lib/actions.js +1 -1
- package/dist/production/lib/components.js +1 -1
- package/dist/production/lib/context.js +1 -1
- package/dist/production/lib/dom/dom.js +1 -1
- package/dist/production/lib/dom/lib.js +2 -2
- package/dist/production/lib/dom/server.js +1 -1
- package/dist/production/lib/dom/ssr/components.js +1 -1
- package/dist/production/lib/dom/ssr/data.js +1 -1
- package/dist/production/lib/dom/ssr/errorBoundaries.js +1 -1
- package/dist/production/lib/dom/ssr/fallback.js +1 -1
- package/dist/production/lib/dom/ssr/fog-of-war.js +1 -1
- package/dist/production/lib/dom/ssr/hydration.js +1 -1
- package/dist/production/lib/dom/ssr/invariant.js +1 -1
- package/dist/production/lib/dom/ssr/links.js +1 -1
- package/dist/production/lib/dom/ssr/markup.js +1 -1
- package/dist/production/lib/dom/ssr/routeModules.js +1 -1
- package/dist/production/lib/dom/ssr/routes-test-stub.js +1 -1
- package/dist/production/lib/dom/ssr/routes.js +1 -1
- package/dist/production/lib/dom/ssr/server.js +1 -1
- package/dist/production/lib/dom/ssr/single-fetch.js +1 -1
- package/dist/production/lib/dom-export/dom-router-provider.js +1 -1
- package/dist/production/lib/dom-export/hydrated-router.js +1 -1
- package/dist/production/lib/errors.js +1 -1
- package/dist/production/lib/hooks.d.ts +33 -6
- package/dist/production/lib/hooks.js +34 -7
- package/dist/production/lib/href.js +6 -3
- package/dist/production/lib/router/history.js +1 -1
- package/dist/production/lib/router/instrumentation.js +1 -1
- package/dist/production/lib/router/router.js +2 -2
- package/dist/production/lib/router/url.js +1 -1
- package/dist/production/lib/router/utils.js +5 -3
- package/dist/production/lib/rsc/browser.js +1 -1
- package/dist/production/lib/rsc/errorBoundaries.js +1 -1
- package/dist/production/lib/rsc/html-stream/browser.js +1 -1
- package/dist/production/lib/rsc/html-stream/server.js +1 -1
- package/dist/production/lib/rsc/route-modules.js +1 -1
- package/dist/production/lib/rsc/server.rsc.d.ts +3 -2
- package/dist/production/lib/rsc/server.ssr.js +1 -1
- package/dist/production/lib/server-runtime/cookies.js +1 -1
- package/dist/production/lib/server-runtime/crypto.js +1 -1
- package/dist/production/lib/server-runtime/data.js +1 -1
- package/dist/production/lib/server-runtime/dev.js +1 -1
- package/dist/production/lib/server-runtime/entry.js +1 -1
- package/dist/production/lib/server-runtime/errors.js +1 -1
- package/dist/production/lib/server-runtime/headers.js +1 -1
- package/dist/production/lib/server-runtime/invariant.js +1 -1
- package/dist/production/lib/server-runtime/mode.js +1 -1
- package/dist/production/lib/server-runtime/routeMatching.js +1 -1
- package/dist/production/lib/server-runtime/routes.js +1 -1
- package/dist/production/lib/server-runtime/server.js +1 -1
- package/dist/production/lib/server-runtime/serverHandoff.js +1 -1
- package/dist/production/lib/server-runtime/sessions/cookieStorage.js +1 -1
- package/dist/production/lib/server-runtime/sessions/memoryStorage.js +1 -1
- package/dist/production/lib/server-runtime/sessions.js +1 -1
- package/dist/production/lib/server-runtime/single-fetch.js +1 -1
- package/dist/production/lib/server-runtime/urls.js +1 -1
- package/dist/production/lib/server-runtime/warnings.js +1 -1
- package/dist/production/lib/types/internal.js +1 -1
- package/dist/production/vendor/turbo-stream-v2/flatten.js +1 -1
- package/dist/production/vendor/turbo-stream-v2/turbo-stream.js +1 -1
- package/dist/production/vendor/turbo-stream-v2/unflatten.js +1 -1
- package/dist/production/vendor/turbo-stream-v2/utils.js +1 -1
- package/docs/explanation/hot-module-replacement.md +1 -1
- package/docs/how-to/data-strategy.md +1 -1
- package/docs/start/framework/deploying.md +4 -0
- package/docs/upgrading/component-routes.md +1 -1
- package/docs/upgrading/future.md +33 -0
- package/docs/upgrading/router-provider.md +5 -5
- package/docs/upgrading/v7.md +39 -22
- package/package.json +1 -1
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { CookieParseOptions, CookieParseOptions as CookieParseOptions$1, CookieSerializeOptions, CookieSerializeOptions as CookieSerializeOptions$1 } from "cookie-es";
|
|
4
4
|
import { BrowserRouter, Form, HashRouter, Link, Links, MemoryRouter, Meta, NavLink, Navigate, Outlet, Route, Router, RouterProvider, Routes, ScrollRestoration, StaticRouter, StaticRouterProvider, unstable_HistoryRouter } from "react-router/internal/react-server-client";
|
|
5
|
+
import { ReactFormState } from "react-dom/client";
|
|
5
6
|
|
|
6
7
|
//#region lib/router/history.d.ts
|
|
7
8
|
/**
|
|
@@ -2324,7 +2325,7 @@ type RSCRenderPayload = {
|
|
|
2324
2325
|
matches: RSCRouteMatch[];
|
|
2325
2326
|
patches?: Promise<RSCRouteManifest[]>;
|
|
2326
2327
|
nonce?: string;
|
|
2327
|
-
formState?:
|
|
2328
|
+
formState?: ReactFormState;
|
|
2328
2329
|
};
|
|
2329
2330
|
type RSCManifestPayload = {
|
|
2330
2331
|
type: "manifest";
|
|
@@ -2350,7 +2351,7 @@ type RSCMatch = {
|
|
|
2350
2351
|
payload: RSCPayload;
|
|
2351
2352
|
};
|
|
2352
2353
|
type DecodeActionFunction = (formData: FormData) => Promise<() => Promise<unknown>>;
|
|
2353
|
-
type DecodeFormStateFunction = (result: unknown, formData: FormData) =>
|
|
2354
|
+
type DecodeFormStateFunction = (result: unknown, formData: FormData) => Promise<ReactFormState | undefined>;
|
|
2354
2355
|
type DecodeReplyFunction = (reply: FormData | string, options: {
|
|
2355
2356
|
temporaryReferences: unknown;
|
|
2356
2357
|
}) => Promise<unknown[]>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* react-router v8.
|
|
2
|
+
* react-router v8.2.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -380,6 +380,8 @@ function rankRouteBranches(branches) {
|
|
|
380
380
|
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)));
|
|
381
381
|
}
|
|
382
382
|
const paramRe = /^:[\w-]+$/;
|
|
383
|
+
const partialParamRe = /^:[\w-]+/;
|
|
384
|
+
const partialDynamicSegmentValue = 3.5;
|
|
383
385
|
const dynamicSegmentValue = 3;
|
|
384
386
|
const indexRouteValue = 2;
|
|
385
387
|
const emptySegmentValue = 1;
|
|
@@ -391,7 +393,7 @@ function computeScore(path, index) {
|
|
|
391
393
|
let initialScore = segments.length;
|
|
392
394
|
if (segments.some(isSplat)) initialScore += splatPenalty;
|
|
393
395
|
if (index) initialScore += indexRouteValue;
|
|
394
|
-
return segments.filter((s) => !isSplat(s)).reduce((score, segment) => score + (paramRe.test(segment) ? dynamicSegmentValue : segment === "" ? emptySegmentValue : staticSegmentValue), initialScore);
|
|
396
|
+
return segments.filter((s) => !isSplat(s)).reduce((score, segment) => score + (paramRe.test(segment) ? dynamicSegmentValue : partialParamRe.test(segment) ? partialDynamicSegmentValue : segment === "" ? emptySegmentValue : staticSegmentValue), initialScore);
|
|
395
397
|
}
|
|
396
398
|
function compareIndexes(a, b) {
|
|
397
399
|
return a.length === b.length && a.slice(0, -1).every((n, i) => n === b[i]) ? a[a.length - 1] - b[b.length - 1] : 0;
|
|
@@ -488,7 +490,7 @@ function compilePath(path, caseSensitive = false, end = true) {
|
|
|
488
490
|
return "(?:/([^\\/]*))?";
|
|
489
491
|
}
|
|
490
492
|
return "/([^\\/]+)";
|
|
491
|
-
}).replace(/\/([\w-]+)\?(
|
|
493
|
+
}).replace(/\/([\w-]+)\?(?=\/|$|\()/g, "(?:/$1)?");
|
|
492
494
|
if (path.endsWith("*")) {
|
|
493
495
|
params.push({ paramName: "*" });
|
|
494
496
|
regexpSource += path === "*" || path === "/*" ? "(.*)$" : "(?:\\/(.+)|\\/*)$";
|
|
@@ -2459,7 +2461,7 @@ async function processServerAction(request, basename, decodeReply, loadServerAct
|
|
|
2459
2461
|
try {
|
|
2460
2462
|
let result = await action();
|
|
2461
2463
|
if (isRedirectResponse(result)) result = prependBasenameToRedirectResponse(result, basename);
|
|
2462
|
-
formState = decodeFormState?.(result, formData);
|
|
2464
|
+
formState = await decodeFormState?.(result, formData);
|
|
2463
2465
|
} catch (error) {
|
|
2464
2466
|
if (isRedirectResponse(error)) return prependBasenameToRedirectResponse(error, basename);
|
|
2465
2467
|
if (isResponse(error)) return error;
|
|
@@ -2797,6 +2799,9 @@ function canDecodeWithFormData(contentType) {
|
|
|
2797
2799
|
}
|
|
2798
2800
|
//#endregion
|
|
2799
2801
|
//#region lib/href.ts
|
|
2802
|
+
function stringify(p) {
|
|
2803
|
+
return p == null ? "" : typeof p === "string" ? p : String(p);
|
|
2804
|
+
}
|
|
2800
2805
|
/**
|
|
2801
2806
|
Returns a resolved URL path for the specified route.
|
|
2802
2807
|
|
|
@@ -2813,11 +2818,11 @@ function href(path, ...args) {
|
|
|
2813
2818
|
const isRequired = questionMark === void 0;
|
|
2814
2819
|
const value = params?.[param];
|
|
2815
2820
|
if (isRequired && value === void 0) throw new Error(`Path '${path}' requires param '${param}' but it was not provided`);
|
|
2816
|
-
return value
|
|
2821
|
+
return value == null ? "" : "/" + encodeURIComponent(stringify(value));
|
|
2817
2822
|
});
|
|
2818
2823
|
if (path.endsWith("*")) {
|
|
2819
2824
|
const value = params?.["*"];
|
|
2820
|
-
if (value !== void 0) result += "/" + value;
|
|
2825
|
+
if (value !== void 0) result += "/" + stringify(value).split("/").map(encodeURIComponent).join("/");
|
|
2821
2826
|
}
|
|
2822
2827
|
return result || "/";
|
|
2823
2828
|
}
|
package/dist/production/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* react-router v8.
|
|
2
|
+
* react-router v8.2.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -22,7 +22,7 @@ import * as React$1 from "react";
|
|
|
22
22
|
//#region lib/dom/lib.tsx
|
|
23
23
|
const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined";
|
|
24
24
|
try {
|
|
25
|
-
if (isBrowser) window.__reactRouterVersion = "8.
|
|
25
|
+
if (isBrowser) window.__reactRouterVersion = "8.2.0";
|
|
26
26
|
} catch (e) {}
|
|
27
27
|
/**
|
|
28
28
|
* Create a new {@link DataRouter| data router} that manages the application
|
|
@@ -143,12 +143,16 @@ interface NavigateFunction {
|
|
|
143
143
|
* All properties are optional.
|
|
144
144
|
*
|
|
145
145
|
* ```tsx
|
|
146
|
-
* navigate(
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
146
|
+
* navigate(
|
|
147
|
+
* {
|
|
148
|
+
* pathname: "/some/route",
|
|
149
|
+
* search: "?search=param",
|
|
150
|
+
* hash: "#hash",
|
|
151
|
+
* },
|
|
152
|
+
* {
|
|
153
|
+
* state: { some: "state" },
|
|
154
|
+
* },
|
|
155
|
+
* );
|
|
152
156
|
* ```
|
|
153
157
|
*
|
|
154
158
|
* If you use `state`, that will be available on the {@link Location} object on
|
|
@@ -576,6 +580,29 @@ declare function useRevalidator(): {
|
|
|
576
580
|
* parent/child routes or the route [`handle`](../../start/framework/route-module#handle)
|
|
577
581
|
* property
|
|
578
582
|
*
|
|
583
|
+
* Pairing the route `handle` with `useMatches` gets very powerful since you can put
|
|
584
|
+
* whatever you want on a route handle and have access to `useMatches` anywhere.
|
|
585
|
+
* Please see the [handle](../../how-to/using-handle) documentation for an example
|
|
586
|
+
* of breadcrumbs via `useMatches`/`handle`.
|
|
587
|
+
*
|
|
588
|
+
* ```tsx
|
|
589
|
+
* import { useMatches } from "react-router";
|
|
590
|
+
*
|
|
591
|
+
* function SomeComponent() {
|
|
592
|
+
* const matches = useMatches();
|
|
593
|
+
* // matches[i].id // route id
|
|
594
|
+
* // matches[i].pathname // the portion of the URL the route matched
|
|
595
|
+
* // matches[i].params // the parsed params from the URL
|
|
596
|
+
* // matches[i].loaderData // the data from the loader
|
|
597
|
+
* // matches[i].handle // the route handle with any app specific data
|
|
598
|
+
* }
|
|
599
|
+
* ```
|
|
600
|
+
*
|
|
601
|
+
* <docs-info>useMatches only works with a data router like `createBrowserRouter`,
|
|
602
|
+
* since they know the full route tree up front and can provide all of the current
|
|
603
|
+
* matches. Additionally, `useMatches` will not match down into any descendant route
|
|
604
|
+
* trees since the router isn't aware of the descendant routes.</docs-info>
|
|
605
|
+
*
|
|
579
606
|
* @public
|
|
580
607
|
* @category Hooks
|
|
581
608
|
* @mode framework
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* react-router v8.
|
|
2
|
+
* react-router v8.2.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -162,12 +162,16 @@ const navigateEffectWarning = "You should call navigate() in a React.useEffect()
|
|
|
162
162
|
* All properties are optional.
|
|
163
163
|
*
|
|
164
164
|
* ```tsx
|
|
165
|
-
* navigate(
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
165
|
+
* navigate(
|
|
166
|
+
* {
|
|
167
|
+
* pathname: "/some/route",
|
|
168
|
+
* search: "?search=param",
|
|
169
|
+
* hash: "#hash",
|
|
170
|
+
* },
|
|
171
|
+
* {
|
|
172
|
+
* state: { some: "state" },
|
|
173
|
+
* },
|
|
174
|
+
* );
|
|
171
175
|
* ```
|
|
172
176
|
*
|
|
173
177
|
* If you use `state`, that will be available on the {@link Location} object on
|
|
@@ -904,6 +908,29 @@ function useRevalidator() {
|
|
|
904
908
|
* parent/child routes or the route [`handle`](../../start/framework/route-module#handle)
|
|
905
909
|
* property
|
|
906
910
|
*
|
|
911
|
+
* Pairing the route `handle` with `useMatches` gets very powerful since you can put
|
|
912
|
+
* whatever you want on a route handle and have access to `useMatches` anywhere.
|
|
913
|
+
* Please see the [handle](../../how-to/using-handle) documentation for an example
|
|
914
|
+
* of breadcrumbs via `useMatches`/`handle`.
|
|
915
|
+
*
|
|
916
|
+
* ```tsx
|
|
917
|
+
* import { useMatches } from "react-router";
|
|
918
|
+
*
|
|
919
|
+
* function SomeComponent() {
|
|
920
|
+
* const matches = useMatches();
|
|
921
|
+
* // matches[i].id // route id
|
|
922
|
+
* // matches[i].pathname // the portion of the URL the route matched
|
|
923
|
+
* // matches[i].params // the parsed params from the URL
|
|
924
|
+
* // matches[i].loaderData // the data from the loader
|
|
925
|
+
* // matches[i].handle // the route handle with any app specific data
|
|
926
|
+
* }
|
|
927
|
+
* ```
|
|
928
|
+
*
|
|
929
|
+
* <docs-info>useMatches only works with a data router like `createBrowserRouter`,
|
|
930
|
+
* since they know the full route tree up front and can provide all of the current
|
|
931
|
+
* matches. Additionally, `useMatches` will not match down into any descendant route
|
|
932
|
+
* trees since the router isn't aware of the descendant routes.</docs-info>
|
|
933
|
+
*
|
|
907
934
|
* @public
|
|
908
935
|
* @category Hooks
|
|
909
936
|
* @mode framework
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* react-router v8.
|
|
2
|
+
* react-router v8.2.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
11
|
//#region lib/href.ts
|
|
12
|
+
function stringify(p) {
|
|
13
|
+
return p == null ? "" : typeof p === "string" ? p : String(p);
|
|
14
|
+
}
|
|
12
15
|
/**
|
|
13
16
|
Returns a resolved URL path for the specified route.
|
|
14
17
|
|
|
@@ -25,11 +28,11 @@ function href(path, ...args) {
|
|
|
25
28
|
const isRequired = questionMark === void 0;
|
|
26
29
|
const value = params?.[param];
|
|
27
30
|
if (isRequired && value === void 0) throw new Error(`Path '${path}' requires param '${param}' but it was not provided`);
|
|
28
|
-
return value
|
|
31
|
+
return value == null ? "" : "/" + encodeURIComponent(stringify(value));
|
|
29
32
|
});
|
|
30
33
|
if (path.endsWith("*")) {
|
|
31
34
|
const value = params?.["*"];
|
|
32
|
-
if (value !== void 0) result += "/" + value;
|
|
35
|
+
if (value !== void 0) result += "/" + stringify(value).split("/").map(encodeURIComponent).join("/");
|
|
33
36
|
}
|
|
34
37
|
return result || "/";
|
|
35
38
|
}
|