one 1.16.8 → 1.16.10
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/dist/cjs/Root.cjs +7 -6
- package/dist/cjs/Root.native.js +7 -6
- package/dist/cjs/Root.native.js.map +1 -1
- package/dist/cjs/babel-plugins/one-router-metro.cjs +5 -0
- package/dist/cjs/babel-plugins/one-router-metro.native.js +6 -0
- package/dist/cjs/babel-plugins/one-router-metro.native.js.map +1 -1
- package/dist/cjs/babel-plugins/one-router-metro.test.cjs +20 -0
- package/dist/cjs/babel-plugins/one-router-metro.test.native.js +20 -0
- package/dist/cjs/babel-plugins/one-router-metro.test.native.js.map +1 -1
- package/dist/cjs/cli/build.cjs +16 -1
- package/dist/cjs/cli/build.native.js +16 -1
- package/dist/cjs/cli/build.native.js.map +1 -1
- package/dist/cjs/cli/dev.cjs +12 -2
- package/dist/cjs/cli/dev.native.js +12 -2
- package/dist/cjs/cli/dev.native.js.map +1 -1
- package/dist/cjs/cli/install-error-handlers.cjs +132 -0
- package/dist/cjs/cli/install-error-handlers.native.js +144 -0
- package/dist/cjs/cli/install-error-handlers.native.js.map +1 -0
- package/dist/cjs/createApp.cjs +3 -0
- package/dist/cjs/createApp.native.js +1 -0
- package/dist/cjs/createApp.native.js.map +1 -1
- package/dist/cjs/fork/extractPathFromURL.cjs +19 -0
- package/dist/cjs/fork/extractPathFromURL.native.js +25 -0
- package/dist/cjs/fork/extractPathFromURL.native.js.map +1 -1
- package/dist/cjs/fork/extractPathFromURL.test.cjs +37 -0
- package/dist/cjs/fork/extractPathFromURL.test.native.js +40 -0
- package/dist/cjs/fork/extractPathFromURL.test.native.js.map +1 -0
- package/dist/cjs/headless.cjs +1 -0
- package/dist/cjs/headless.native.js +1 -0
- package/dist/cjs/headless.native.js.map +1 -1
- package/dist/cjs/index.cjs +4 -0
- package/dist/cjs/index.native.js +4 -0
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/link/getLinking.cjs +48 -0
- package/dist/cjs/link/getLinking.native.js +54 -0
- package/dist/cjs/link/getLinking.native.js.map +1 -0
- package/dist/cjs/link/linking.cjs +4 -0
- package/dist/cjs/link/linking.native.js +9 -0
- package/dist/cjs/link/linking.native.js.map +1 -1
- package/dist/cjs/metro-config/getViteMetroPluginOptions.cjs +2 -0
- package/dist/cjs/metro-config/getViteMetroPluginOptions.native.js +2 -0
- package/dist/cjs/metro-config/getViteMetroPluginOptions.native.js.map +1 -1
- package/dist/cjs/router/Route.cjs +1 -1
- package/dist/cjs/router/Route.native.js +1 -1
- package/dist/cjs/router/Route.native.js.map +1 -1
- package/dist/cjs/router/getLinkingConfig.cjs +13 -2
- package/dist/cjs/router/getLinkingConfig.native.js +14 -2
- package/dist/cjs/router/getLinkingConfig.native.js.map +1 -1
- package/dist/cjs/router/getLinkingConfig.test.cjs +41 -0
- package/dist/cjs/router/getLinkingConfig.test.native.js +44 -0
- package/dist/cjs/router/getLinkingConfig.test.native.js.map +1 -0
- package/dist/cjs/router/getRouteInfo.cjs +1 -1
- package/dist/cjs/router/getRouteInfo.native.js +2 -2
- package/dist/cjs/router/getRouteInfo.native.js.map +1 -1
- package/dist/cjs/router/linkingConfig.cjs +34 -9
- package/dist/cjs/router/linkingConfig.native.js +35 -9
- package/dist/cjs/router/linkingConfig.native.js.map +1 -1
- package/dist/cjs/router/router.cjs +6 -6
- package/dist/cjs/router/router.native.js +6 -6
- package/dist/cjs/router/router.native.js.map +1 -1
- package/dist/cjs/router/sitemap.cjs +65 -0
- package/dist/cjs/router/sitemap.native.js +75 -0
- package/dist/cjs/router/sitemap.native.js.map +1 -0
- package/dist/cjs/router/sitemap.test.cjs +34 -0
- package/dist/cjs/router/sitemap.test.native.js +37 -0
- package/dist/cjs/router/sitemap.test.native.js.map +1 -0
- package/dist/cjs/router/useInitializeOneRouter.cjs +5 -5
- package/dist/cjs/router/useInitializeOneRouter.native.js +5 -5
- package/dist/cjs/router/useInitializeOneRouter.native.js.map +1 -1
- package/dist/cjs/serve.cjs +14 -1
- package/dist/cjs/serve.native.js +14 -1
- package/dist/cjs/serve.native.js.map +1 -1
- package/dist/cjs/utils/isStatus.cjs +1 -1
- package/dist/cjs/utils/isStatus.native.js +1 -1
- package/dist/cjs/utils/isStatus.native.js.map +1 -1
- package/dist/cjs/views/Navigator.cjs +1 -1
- package/dist/cjs/views/Navigator.native.js +1 -1
- package/dist/cjs/views/Navigator.native.js.map +1 -1
- package/dist/cjs/vite/one.cjs +2 -0
- package/dist/cjs/vite/one.native.js +4 -2
- package/dist/cjs/vite/one.native.js.map +1 -1
- package/dist/cjs/vite/plugins/virtualEntryPlugin.cjs +4 -0
- package/dist/cjs/vite/plugins/virtualEntryPlugin.native.js +6 -0
- package/dist/cjs/vite/plugins/virtualEntryPlugin.native.js.map +1 -1
- package/dist/cjs/vite/plugins/virtualEntryPlugin.test.cjs +13 -0
- package/dist/cjs/vite/plugins/virtualEntryPlugin.test.native.js +13 -0
- package/dist/cjs/vite/plugins/virtualEntryPlugin.test.native.js.map +1 -1
- package/dist/esm/Root.mjs +8 -7
- package/dist/esm/Root.mjs.map +1 -1
- package/dist/esm/Root.native.js +8 -7
- package/dist/esm/Root.native.js.map +1 -1
- package/dist/esm/babel-plugins/one-router-metro.mjs +5 -0
- package/dist/esm/babel-plugins/one-router-metro.mjs.map +1 -1
- package/dist/esm/babel-plugins/one-router-metro.native.js +6 -0
- package/dist/esm/babel-plugins/one-router-metro.native.js.map +1 -1
- package/dist/esm/babel-plugins/one-router-metro.test.mjs +20 -0
- package/dist/esm/babel-plugins/one-router-metro.test.mjs.map +1 -1
- package/dist/esm/babel-plugins/one-router-metro.test.native.js +20 -0
- package/dist/esm/babel-plugins/one-router-metro.test.native.js.map +1 -1
- package/dist/esm/cli/build.mjs +16 -1
- package/dist/esm/cli/build.mjs.map +1 -1
- package/dist/esm/cli/build.native.js +16 -1
- package/dist/esm/cli/build.native.js.map +1 -1
- package/dist/esm/cli/dev.mjs +12 -2
- package/dist/esm/cli/dev.mjs.map +1 -1
- package/dist/esm/cli/dev.native.js +12 -2
- package/dist/esm/cli/dev.native.js.map +1 -1
- package/dist/esm/cli/install-error-handlers.mjs +105 -0
- package/dist/esm/cli/install-error-handlers.mjs.map +1 -0
- package/dist/esm/cli/install-error-handlers.native.js +114 -0
- package/dist/esm/cli/install-error-handlers.native.js.map +1 -0
- package/dist/esm/createApp.mjs +3 -0
- package/dist/esm/createApp.mjs.map +1 -1
- package/dist/esm/createApp.native.js +1 -0
- package/dist/esm/createApp.native.js.map +1 -1
- package/dist/esm/fork/extractPathFromURL.mjs +19 -0
- package/dist/esm/fork/extractPathFromURL.mjs.map +1 -1
- package/dist/esm/fork/extractPathFromURL.native.js +25 -0
- package/dist/esm/fork/extractPathFromURL.native.js.map +1 -1
- package/dist/esm/fork/extractPathFromURL.test.mjs +38 -0
- package/dist/esm/fork/extractPathFromURL.test.mjs.map +1 -0
- package/dist/esm/fork/extractPathFromURL.test.native.js +38 -0
- package/dist/esm/fork/extractPathFromURL.test.native.js.map +1 -0
- package/dist/esm/headless.mjs +1 -0
- package/dist/esm/headless.mjs.map +1 -1
- package/dist/esm/headless.native.js +1 -0
- package/dist/esm/headless.native.js.map +1 -1
- package/dist/esm/index.js +3 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index.mjs +3 -1
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +3 -1
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/link/getLinking.mjs +22 -0
- package/dist/esm/link/getLinking.mjs.map +1 -0
- package/dist/esm/link/getLinking.native.js +25 -0
- package/dist/esm/link/getLinking.native.js.map +1 -0
- package/dist/esm/link/linking.mjs +4 -1
- package/dist/esm/link/linking.mjs.map +1 -1
- package/dist/esm/link/linking.native.js +9 -1
- package/dist/esm/link/linking.native.js.map +1 -1
- package/dist/esm/metro-config/getViteMetroPluginOptions.mjs +2 -0
- package/dist/esm/metro-config/getViteMetroPluginOptions.mjs.map +1 -1
- package/dist/esm/metro-config/getViteMetroPluginOptions.native.js +2 -0
- package/dist/esm/metro-config/getViteMetroPluginOptions.native.js.map +1 -1
- package/dist/esm/router/Route.mjs +2 -2
- package/dist/esm/router/Route.mjs.map +1 -1
- package/dist/esm/router/Route.native.js +2 -2
- package/dist/esm/router/Route.native.js.map +1 -1
- package/dist/esm/router/getLinkingConfig.mjs +14 -4
- package/dist/esm/router/getLinkingConfig.mjs.map +1 -1
- package/dist/esm/router/getLinkingConfig.native.js +15 -4
- package/dist/esm/router/getLinkingConfig.native.js.map +1 -1
- package/dist/esm/router/getLinkingConfig.test.mjs +42 -0
- package/dist/esm/router/getLinkingConfig.test.mjs.map +1 -0
- package/dist/esm/router/getLinkingConfig.test.native.js +42 -0
- package/dist/esm/router/getLinkingConfig.test.native.js.map +1 -0
- package/dist/esm/router/getRouteInfo.mjs +2 -2
- package/dist/esm/router/getRouteInfo.mjs.map +1 -1
- package/dist/esm/router/getRouteInfo.native.js +3 -3
- package/dist/esm/router/getRouteInfo.native.js.map +1 -1
- package/dist/esm/router/linkingConfig.mjs +35 -10
- package/dist/esm/router/linkingConfig.mjs.map +1 -1
- package/dist/esm/router/linkingConfig.native.js +36 -10
- package/dist/esm/router/linkingConfig.native.js.map +1 -1
- package/dist/esm/router/router.mjs +7 -7
- package/dist/esm/router/router.mjs.map +1 -1
- package/dist/esm/router/router.native.js +7 -7
- package/dist/esm/router/router.native.js.map +1 -1
- package/dist/esm/router/sitemap.mjs +39 -0
- package/dist/esm/router/sitemap.mjs.map +1 -0
- package/dist/esm/router/sitemap.native.js +46 -0
- package/dist/esm/router/sitemap.native.js.map +1 -0
- package/dist/esm/router/sitemap.test.mjs +35 -0
- package/dist/esm/router/sitemap.test.mjs.map +1 -0
- package/dist/esm/router/sitemap.test.native.js +35 -0
- package/dist/esm/router/sitemap.test.native.js.map +1 -0
- package/dist/esm/router/useInitializeOneRouter.mjs +5 -5
- package/dist/esm/router/useInitializeOneRouter.mjs.map +1 -1
- package/dist/esm/router/useInitializeOneRouter.native.js +5 -5
- package/dist/esm/router/useInitializeOneRouter.native.js.map +1 -1
- package/dist/esm/serve.mjs +14 -1
- package/dist/esm/serve.mjs.map +1 -1
- package/dist/esm/serve.native.js +14 -1
- package/dist/esm/serve.native.js.map +1 -1
- package/dist/esm/utils/isStatus.mjs +1 -1
- package/dist/esm/utils/isStatus.mjs.map +1 -1
- package/dist/esm/utils/isStatus.native.js +1 -1
- package/dist/esm/utils/isStatus.native.js.map +1 -1
- package/dist/esm/views/Navigator.mjs +2 -2
- package/dist/esm/views/Navigator.mjs.map +1 -1
- package/dist/esm/views/Navigator.native.js +2 -2
- package/dist/esm/views/Navigator.native.js.map +1 -1
- package/dist/esm/vite/one.mjs +2 -0
- package/dist/esm/vite/one.mjs.map +1 -1
- package/dist/esm/vite/one.native.js +4 -2
- package/dist/esm/vite/one.native.js.map +1 -1
- package/dist/esm/vite/plugins/virtualEntryPlugin.mjs +4 -0
- package/dist/esm/vite/plugins/virtualEntryPlugin.mjs.map +1 -1
- package/dist/esm/vite/plugins/virtualEntryPlugin.native.js +6 -0
- package/dist/esm/vite/plugins/virtualEntryPlugin.native.js.map +1 -1
- package/dist/esm/vite/plugins/virtualEntryPlugin.test.mjs +13 -0
- package/dist/esm/vite/plugins/virtualEntryPlugin.test.mjs.map +1 -1
- package/dist/esm/vite/plugins/virtualEntryPlugin.test.native.js +13 -0
- package/dist/esm/vite/plugins/virtualEntryPlugin.test.native.js.map +1 -1
- package/metro-entry.js +3 -0
- package/package.json +9 -9
- package/src/Root.tsx +15 -6
- package/src/babel-plugins/one-router-metro.test.ts +33 -0
- package/src/babel-plugins/one-router-metro.ts +9 -0
- package/src/cli/build.ts +25 -1
- package/src/cli/dev.ts +16 -5
- package/src/cli/install-error-handlers.ts +172 -0
- package/src/createApp.native.tsx +1 -0
- package/src/createApp.tsx +5 -0
- package/src/fork/extractPathFromURL.test.ts +80 -0
- package/src/fork/extractPathFromURL.ts +28 -0
- package/src/headless.tsx +3 -0
- package/src/index.ts +2 -0
- package/src/link/getLinking.ts +56 -0
- package/src/link/linking.native.ts +9 -0
- package/src/link/linking.ts +4 -0
- package/src/metro-config/getViteMetroPluginOptions.ts +3 -0
- package/src/router/Route.tsx +2 -2
- package/src/router/getLinkingConfig.test.ts +57 -0
- package/src/router/getLinkingConfig.ts +21 -3
- package/src/router/getRouteInfo.ts +2 -2
- package/src/router/linkingConfig.ts +52 -8
- package/src/router/router.ts +9 -7
- package/src/router/sitemap.test.ts +50 -0
- package/src/router/sitemap.ts +66 -0
- package/src/router/useInitializeOneRouter.ts +7 -5
- package/src/serve.ts +22 -1
- package/src/utils/isStatus.ts +11 -2
- package/src/views/Navigator.tsx +4 -8
- package/src/vite/one.ts +2 -0
- package/src/vite/plugins/virtualEntryPlugin.test.ts +17 -0
- package/src/vite/plugins/virtualEntryPlugin.ts +8 -0
- package/src/vite/types.ts +23 -0
- package/types/Root.d.ts +2 -0
- package/types/Root.d.ts.map +1 -1
- package/types/babel-plugins/one-router-metro.d.ts +1 -0
- package/types/babel-plugins/one-router-metro.d.ts.map +1 -1
- package/types/cli/build.d.ts.map +1 -1
- package/types/cli/dev.d.ts.map +1 -1
- package/types/cli/install-error-handlers.d.ts +4 -0
- package/types/cli/install-error-handlers.d.ts.map +1 -0
- package/types/createApp.d.ts +2 -0
- package/types/createApp.d.ts.map +1 -1
- package/types/createApp.native.d.ts.map +1 -1
- package/types/fork/extractPathFromURL.d.ts.map +1 -1
- package/types/fork/extractPathFromURL.test.d.ts +2 -0
- package/types/fork/extractPathFromURL.test.d.ts.map +1 -0
- package/types/headless.d.ts +2 -0
- package/types/headless.d.ts.map +1 -1
- package/types/index.d.ts +2 -0
- package/types/index.d.ts.map +1 -1
- package/types/link/getLinking.d.ts +22 -0
- package/types/link/getLinking.d.ts.map +1 -0
- package/types/link/linking.d.ts +1 -0
- package/types/link/linking.d.ts.map +1 -1
- package/types/link/linking.native.d.ts +1 -0
- package/types/link/linking.native.d.ts.map +1 -1
- package/types/metro-config/getViteMetroPluginOptions.d.ts +2 -1
- package/types/metro-config/getViteMetroPluginOptions.d.ts.map +1 -1
- package/types/router/getLinkingConfig.d.ts +3 -1
- package/types/router/getLinkingConfig.d.ts.map +1 -1
- package/types/router/getLinkingConfig.test.d.ts +2 -0
- package/types/router/getLinkingConfig.test.d.ts.map +1 -0
- package/types/router/linkingConfig.d.ts +5 -4
- package/types/router/linkingConfig.d.ts.map +1 -1
- package/types/router/router.d.ts +2 -1
- package/types/router/router.d.ts.map +1 -1
- package/types/router/sitemap.d.ts +14 -0
- package/types/router/sitemap.d.ts.map +1 -0
- package/types/router/sitemap.test.d.ts +2 -0
- package/types/router/sitemap.test.d.ts.map +1 -0
- package/types/router/useInitializeOneRouter.d.ts +2 -1
- package/types/router/useInitializeOneRouter.d.ts.map +1 -1
- package/types/serve.d.ts.map +1 -1
- package/types/utils/isStatus.d.ts +2 -1
- package/types/utils/isStatus.d.ts.map +1 -1
- package/types/views/Navigator.d.ts.map +1 -1
- package/types/vite/one.d.ts.map +1 -1
- package/types/vite/plugins/virtualEntryPlugin.d.ts.map +1 -1
- package/types/vite/types.d.ts +23 -0
- package/types/vite/types.d.ts.map +1 -1
|
@@ -129,6 +129,11 @@ function fromDeepLink(url: string): string {
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
export function extractExpoPathFromURL(_prefixes: string[], url = '') {
|
|
132
|
+
const pathFromPrefix = extractPathFromPrefix(_prefixes, url)
|
|
133
|
+
if (pathFromPrefix !== undefined) {
|
|
134
|
+
return pathFromPrefix.replace(/^\//, '')
|
|
135
|
+
}
|
|
136
|
+
|
|
132
137
|
return (
|
|
133
138
|
extractExactPathFromURL(url)
|
|
134
139
|
// TODO: We should get rid of this, dropping specificities is not good
|
|
@@ -136,6 +141,29 @@ export function extractExpoPathFromURL(_prefixes: string[], url = '') {
|
|
|
136
141
|
)
|
|
137
142
|
}
|
|
138
143
|
|
|
144
|
+
function extractPathFromPrefix(prefixes: string[] = [], url: string) {
|
|
145
|
+
const prefix = getMatchingPrefix(prefixes, url)
|
|
146
|
+
if (!prefix) return undefined
|
|
147
|
+
|
|
148
|
+
return url.slice(prefix.length)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const PREFIX_BOUNDARY_CHARS = ['/', '?', '#']
|
|
152
|
+
|
|
153
|
+
function getMatchingPrefix(prefixes: string[], url: string) {
|
|
154
|
+
return prefixes
|
|
155
|
+
.filter((prefix) => matchesPrefix(prefix, url))
|
|
156
|
+
.sort((a, b) => b.length - a.length)[0]
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function matchesPrefix(prefix: string, url: string) {
|
|
160
|
+
if (!url.startsWith(prefix)) return false
|
|
161
|
+
if (url.length === prefix.length) return true
|
|
162
|
+
// a prefix already ending in '/' has consumed its boundary
|
|
163
|
+
if (prefix.endsWith('/')) return true
|
|
164
|
+
return PREFIX_BOUNDARY_CHARS.includes(url[prefix.length])
|
|
165
|
+
}
|
|
166
|
+
|
|
139
167
|
export function adjustPathname(url: { hostname?: string | null; pathname: string }) {
|
|
140
168
|
if (url.hostname === 'exp.host' || url.hostname === 'u.expo.dev') {
|
|
141
169
|
// drop the first two segments from pathname:
|
package/src/headless.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import './setup'
|
|
2
2
|
|
|
3
|
+
import type { OneLinkingConfig } from './link/getLinking'
|
|
3
4
|
import { Root } from './Root'
|
|
4
5
|
import { resolveClientLoader } from './clientLoaderResolver'
|
|
5
6
|
import { render } from './render'
|
|
@@ -13,6 +14,7 @@ export type CreateHeadlessAppProps = {
|
|
|
13
14
|
routerRoot: string
|
|
14
15
|
path?: string
|
|
15
16
|
flags?: One.Flags
|
|
17
|
+
linking?: OneLinkingConfig
|
|
16
18
|
getSetupPromise?: () => Promise<unknown>
|
|
17
19
|
}
|
|
18
20
|
|
|
@@ -34,6 +36,7 @@ export function createApp(options: CreateHeadlessAppProps) {
|
|
|
34
36
|
flags={options.flags}
|
|
35
37
|
routes={options.routes}
|
|
36
38
|
routerRoot={options.routerRoot}
|
|
39
|
+
linking={options.linking}
|
|
37
40
|
path={
|
|
38
41
|
options.path || (typeof window !== 'undefined' ? window.location.href : '/')
|
|
39
42
|
}
|
package/src/index.ts
CHANGED
|
@@ -77,7 +77,9 @@ export {
|
|
|
77
77
|
useSegments,
|
|
78
78
|
useUnstableGlobalHref,
|
|
79
79
|
} from './hooks'
|
|
80
|
+
export { useSitemap, type SitemapType } from './router/sitemap'
|
|
80
81
|
export { href } from './href'
|
|
82
|
+
export { getLinking, type OneLinkingConfig } from './link/getLinking'
|
|
81
83
|
// components
|
|
82
84
|
export { Stack } from './layouts/Stack'
|
|
83
85
|
export { Tabs } from './layouts/Tabs'
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export type OneLinkingConfig = {
|
|
2
|
+
/**
|
|
3
|
+
* Custom app scheme or schemes. Each scheme is expanded to double- and
|
|
4
|
+
* triple-slashed URL prefixes.
|
|
5
|
+
*/
|
|
6
|
+
scheme?: string | string[]
|
|
7
|
+
/**
|
|
8
|
+
* Fully qualified URL prefixes to strip before matching routes.
|
|
9
|
+
*
|
|
10
|
+
* For host-bearing custom scheme URLs, include the host:
|
|
11
|
+
* `myapp://app`.
|
|
12
|
+
*/
|
|
13
|
+
prefixes?: string[]
|
|
14
|
+
filter?: (url: string) => boolean
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type NormalizedOneLinkingConfig = {
|
|
18
|
+
prefixes: string[]
|
|
19
|
+
filter?: (url: string) => boolean
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function getLinking(config: OneLinkingConfig = {}): OneLinkingConfig {
|
|
23
|
+
return config
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function normalizeLinkingConfig(
|
|
27
|
+
config: OneLinkingConfig | undefined,
|
|
28
|
+
defaultPrefixes: string[] = []
|
|
29
|
+
): NormalizedOneLinkingConfig {
|
|
30
|
+
// merge: defaults from the native manifest combine with whatever the user
|
|
31
|
+
// provides via scheme/prefixes, so URLs from any registered scheme are
|
|
32
|
+
// recognized even when the user only mentions a subset
|
|
33
|
+
const merged = [
|
|
34
|
+
...defaultPrefixes,
|
|
35
|
+
...getSchemePrefixes(config?.scheme),
|
|
36
|
+
...(config?.prefixes ?? []),
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
prefixes: dedupe(merged),
|
|
41
|
+
filter: config?.filter,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getSchemePrefixes(scheme: string | string[] | undefined): string[] {
|
|
46
|
+
const schemes = Array.isArray(scheme) ? scheme : scheme ? [scheme] : []
|
|
47
|
+
|
|
48
|
+
return schemes.flatMap((value) => {
|
|
49
|
+
const normalized = value.replace(/:\/+$/, '')
|
|
50
|
+
return [`${normalized}://`, `${normalized}:///`]
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function dedupe(values: string[]): string[] {
|
|
55
|
+
return [...new Set(values.filter(Boolean))]
|
|
56
|
+
}
|
|
@@ -62,6 +62,15 @@ export function getRootURL(): string {
|
|
|
62
62
|
return _rootURL
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
export function getDefaultLinkingPrefixes(): string[] {
|
|
66
|
+
return (
|
|
67
|
+
Linking.collectManifestSchemes?.().flatMap((scheme) => {
|
|
68
|
+
const normalized = scheme.replace(/:\/+$/, '')
|
|
69
|
+
return [`${normalized}://`, `${normalized}:///`]
|
|
70
|
+
}) ?? []
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
65
74
|
export function addEventListener(listener: (url: string) => void) {
|
|
66
75
|
let callback: (({ url }: { url: string }) => void) | undefined
|
|
67
76
|
|
package/src/link/linking.ts
CHANGED
|
@@ -12,6 +12,10 @@ export function getRootURL(): string {
|
|
|
12
12
|
return '/'
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
export function getDefaultLinkingPrefixes(): string[] {
|
|
16
|
+
return []
|
|
17
|
+
}
|
|
18
|
+
|
|
15
19
|
export function addEventListener(listener: (url: string) => void) {
|
|
16
20
|
if (typeof window === 'undefined') {
|
|
17
21
|
return () => {}
|
|
@@ -20,12 +20,14 @@ export function getViteMetroPluginOptions({
|
|
|
20
20
|
projectRoot,
|
|
21
21
|
relativeRouterRoot,
|
|
22
22
|
ignoredRouteFiles,
|
|
23
|
+
linking,
|
|
23
24
|
userDefaultConfigOverrides,
|
|
24
25
|
setupFile,
|
|
25
26
|
}: {
|
|
26
27
|
projectRoot: string
|
|
27
28
|
relativeRouterRoot: string
|
|
28
29
|
ignoredRouteFiles?: Array<`**/*${string}`>
|
|
30
|
+
linking?: unknown
|
|
29
31
|
userDefaultConfigOverrides?: NonNullable<
|
|
30
32
|
Parameters<typeof metroPlugin>[0]
|
|
31
33
|
>['defaultConfigOverrides']
|
|
@@ -236,6 +238,7 @@ export function getViteMetroPluginOptions({
|
|
|
236
238
|
),
|
|
237
239
|
ONE_ROUTER_ROOT_FOLDER_NAME: relativeRouterRoot,
|
|
238
240
|
ONE_ROUTER_REQUIRE_CONTEXT_REGEX_STRING: routerRequireContextRegexString,
|
|
241
|
+
ONE_ROUTER_LINKING_CONFIG: linking,
|
|
239
242
|
ONE_SETUP_FILE_NATIVE: (() => {
|
|
240
243
|
if (!setupFile) return undefined
|
|
241
244
|
// Extract native setup file path
|
package/src/router/Route.tsx
CHANGED
|
@@ -4,7 +4,7 @@ import type { ErrorBoundaryProps } from '../views/Try'
|
|
|
4
4
|
import type { LoaderProps } from '../types'
|
|
5
5
|
import type { One } from '../vite/types'
|
|
6
6
|
import type { ParamValidator, RouteValidationFn } from '../validateParams'
|
|
7
|
-
import {
|
|
7
|
+
import { getResolvedLinking } from './linkingConfig'
|
|
8
8
|
import { getContextKey } from './matchers'
|
|
9
9
|
import { mergeDynamicParams } from './params'
|
|
10
10
|
import { routeInfo } from './router'
|
|
@@ -153,7 +153,7 @@ function getParamsFromCurrentUrl(route?: {
|
|
|
153
153
|
path?: string
|
|
154
154
|
params?: Record<string, string | undefined>
|
|
155
155
|
}): Record<string, any> | undefined {
|
|
156
|
-
const linking =
|
|
156
|
+
const linking = getResolvedLinking()
|
|
157
157
|
if (!linking?.getStateFromPath) return undefined
|
|
158
158
|
const path =
|
|
159
159
|
routeInfo?.unstable_globalHref ||
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { normalizeLinkingConfig } from '../link/getLinking'
|
|
3
|
+
import { getRoutes } from './getRoutes'
|
|
4
|
+
import { getLinkingConfig } from './getLinkingConfig'
|
|
5
|
+
import { getMockContext } from '../testing-utils'
|
|
6
|
+
|
|
7
|
+
describe('getLinkingConfig', () => {
|
|
8
|
+
it('expands configured schemes into prefixes', () => {
|
|
9
|
+
const routes = getRoutes(getMockContext(['_layout.tsx', 'index.tsx']))!
|
|
10
|
+
const linking = getLinkingConfig(routes, true, {
|
|
11
|
+
scheme: 'threepunchconvo',
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
expect(linking.prefixes).toEqual(['threepunchconvo://', 'threepunchconvo:///'])
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('merges explicit prefixes with scheme-derived prefixes', () => {
|
|
18
|
+
const routes = getRoutes(getMockContext(['_layout.tsx', 'index.tsx']))!
|
|
19
|
+
const linking = getLinkingConfig(routes, true, {
|
|
20
|
+
scheme: 'threepunchconvo',
|
|
21
|
+
prefixes: ['threepunchconvo://app'],
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
expect(linking.prefixes).toEqual([
|
|
25
|
+
'threepunchconvo://',
|
|
26
|
+
'threepunchconvo:///',
|
|
27
|
+
'threepunchconvo://app',
|
|
28
|
+
])
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
describe('normalizeLinkingConfig', () => {
|
|
33
|
+
it('merges manifest-default prefixes with user-supplied scheme/prefixes', () => {
|
|
34
|
+
const result = normalizeLinkingConfig(
|
|
35
|
+
{ scheme: 'foo', prefixes: ['https://example.test/app'] },
|
|
36
|
+
['bar://', 'bar:///']
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
expect(result.prefixes).toEqual([
|
|
40
|
+
'bar://',
|
|
41
|
+
'bar:///',
|
|
42
|
+
'foo://',
|
|
43
|
+
'foo:///',
|
|
44
|
+
'https://example.test/app',
|
|
45
|
+
])
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('falls back to manifest-default prefixes when nothing is configured', () => {
|
|
49
|
+
const result = normalizeLinkingConfig(undefined, ['bar://', 'bar:///'])
|
|
50
|
+
expect(result.prefixes).toEqual(['bar://', 'bar:///'])
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('dedupes overlapping defaults and configured prefixes', () => {
|
|
54
|
+
const result = normalizeLinkingConfig({ scheme: 'foo' }, ['foo://', 'foo:///'])
|
|
55
|
+
expect(result.prefixes).toEqual(['foo://', 'foo:///'])
|
|
56
|
+
})
|
|
57
|
+
})
|
|
@@ -3,10 +3,13 @@ import type { State } from '../fork/getPathFromState'
|
|
|
3
3
|
import { getReactNavigationConfig, type Screen } from '../getReactNavigationConfig'
|
|
4
4
|
import {
|
|
5
5
|
addEventListener,
|
|
6
|
+
getDefaultLinkingPrefixes,
|
|
6
7
|
getInitialURL,
|
|
7
8
|
getPathFromState,
|
|
8
9
|
getStateFromPath,
|
|
9
10
|
} from '../link/linking'
|
|
11
|
+
import { normalizeLinkingConfig, type OneLinkingConfig } from '../link/getLinking'
|
|
12
|
+
import { evictOldest } from '../utils/evictOldest'
|
|
10
13
|
import type { RouteNode } from './Route'
|
|
11
14
|
|
|
12
15
|
export function getNavigationConfig(
|
|
@@ -23,10 +26,16 @@ export type OneLinkingOptions = LinkingOptions<object> & {
|
|
|
23
26
|
getPathFromState?: typeof getPathFromState
|
|
24
27
|
}
|
|
25
28
|
|
|
26
|
-
export function getLinkingConfig(
|
|
29
|
+
export function getLinkingConfig(
|
|
30
|
+
routes: RouteNode,
|
|
31
|
+
metaOnly = true,
|
|
32
|
+
linking?: OneLinkingConfig
|
|
33
|
+
): OneLinkingOptions {
|
|
27
34
|
const config = getNavigationConfig(routes, metaOnly)
|
|
35
|
+
const resolvedLinking = normalizeLinkingConfig(linking, getDefaultLinkingPrefixes())
|
|
28
36
|
return {
|
|
29
|
-
prefixes:
|
|
37
|
+
prefixes: resolvedLinking.prefixes,
|
|
38
|
+
filter: resolvedLinking.filter,
|
|
30
39
|
// @ts-expect-error
|
|
31
40
|
config,
|
|
32
41
|
// A custom getInitialURL is used on native to ensure the app always starts at
|
|
@@ -53,7 +62,15 @@ export function getLinkingConfig(routes: RouteNode, metaOnly = true): OneLinking
|
|
|
53
62
|
|
|
54
63
|
export const stateCache = new Map<string, any>()
|
|
55
64
|
|
|
56
|
-
|
|
65
|
+
const STATE_CACHE_THRESHOLD = 5000
|
|
66
|
+
const STATE_CACHE_EVICTION = 1000
|
|
67
|
+
|
|
68
|
+
export function clearStateCache() {
|
|
69
|
+
stateCache.clear()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** memoize getStateFromPath by pathname. cache is cleared when the route tree
|
|
73
|
+
* or linking config changes (see ensureBaseLinkingConfig in linkingConfig.ts). */
|
|
57
74
|
function getStateFromPathMemoized(
|
|
58
75
|
path: string,
|
|
59
76
|
options: Parameters<typeof getStateFromPath>[1]
|
|
@@ -63,6 +80,7 @@ function getStateFromPathMemoized(
|
|
|
63
80
|
return cached
|
|
64
81
|
}
|
|
65
82
|
const result = getStateFromPath(path, options)
|
|
83
|
+
evictOldest(stateCache, STATE_CACHE_THRESHOLD, STATE_CACHE_EVICTION)
|
|
66
84
|
stateCache.set(path, result)
|
|
67
85
|
return result
|
|
68
86
|
}
|
|
@@ -4,14 +4,14 @@ import { stripBaseUrl } from '../fork/getStateFromPath-mods'
|
|
|
4
4
|
import type { OneRouter } from '../interfaces/router'
|
|
5
5
|
import { getNormalizedStatePath, type UrlObject } from './getNormalizedStatePath'
|
|
6
6
|
import { isIndexPath } from './isIndexPath'
|
|
7
|
-
import {
|
|
7
|
+
import { getResolvedLinking } from './linkingConfig'
|
|
8
8
|
|
|
9
9
|
export function getRouteInfo(state: OneRouter.ResultState) {
|
|
10
10
|
return getRouteInfoFromState(
|
|
11
11
|
(state: Parameters<typeof originalGetPathFromState>[0], asPath: boolean) => {
|
|
12
12
|
return getPathDataFromState(state, {
|
|
13
13
|
screens: [],
|
|
14
|
-
...
|
|
14
|
+
...getResolvedLinking()?.config,
|
|
15
15
|
preserveDynamicRoutes: asPath,
|
|
16
16
|
preserveGroups: asPath,
|
|
17
17
|
})
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { isWebClient } from '../constants'
|
|
2
2
|
import type { OneRouter } from '../interfaces/router'
|
|
3
|
+
import type { OneLinkingConfig } from '../link/getLinking'
|
|
3
4
|
import { evictOldest } from '../utils/evictOldest'
|
|
4
5
|
import {
|
|
6
|
+
clearStateCache,
|
|
5
7
|
getLinkingConfig as createLinkingConfig,
|
|
6
8
|
type OneLinkingOptions,
|
|
7
9
|
} from './getLinkingConfig'
|
|
@@ -12,12 +14,13 @@ let linkingConfig: OneLinkingOptions | undefined
|
|
|
12
14
|
// cache the base linking config (route-tree dependent, not URL-dependent)
|
|
13
15
|
let cachedBaseLinkingConfig: OneLinkingOptions | undefined
|
|
14
16
|
let cachedRouteNodeForLinking: RouteNode | null = null
|
|
17
|
+
let cachedLinkingConfigKey = ''
|
|
15
18
|
|
|
16
19
|
// cache getStateFromPath results by path for SSR performance
|
|
17
20
|
// same path always produces the same navigation state (route tree is static in prod)
|
|
18
21
|
const ssrStateCache = new Map<string, OneRouter.ResultState | undefined>()
|
|
19
22
|
|
|
20
|
-
export function
|
|
23
|
+
export function getResolvedLinking() {
|
|
21
24
|
return linkingConfig
|
|
22
25
|
}
|
|
23
26
|
|
|
@@ -33,13 +36,25 @@ export function resetLinking() {
|
|
|
33
36
|
* Ensure the base linking config is initialized for a given route tree.
|
|
34
37
|
* Does not set any per-request state.
|
|
35
38
|
*/
|
|
36
|
-
export function ensureBaseLinkingConfig(
|
|
39
|
+
export function ensureBaseLinkingConfig(
|
|
40
|
+
routeNode: RouteNode | null,
|
|
41
|
+
linking?: OneLinkingConfig
|
|
42
|
+
) {
|
|
43
|
+
const linkingConfigKey = getLinkingConfigKey(linking)
|
|
37
44
|
if (
|
|
38
45
|
routeNode &&
|
|
39
|
-
(routeNode !== cachedRouteNodeForLinking ||
|
|
46
|
+
(routeNode !== cachedRouteNodeForLinking ||
|
|
47
|
+
linkingConfigKey !== cachedLinkingConfigKey ||
|
|
48
|
+
!cachedBaseLinkingConfig)
|
|
40
49
|
) {
|
|
41
|
-
|
|
50
|
+
// route tree or linking config changed — drop memoized navigation states
|
|
51
|
+
// so getStateFromPathMemoized doesn't return entries computed against
|
|
52
|
+
// the previous configuration
|
|
53
|
+
clearStateCache()
|
|
54
|
+
ssrStateCache.clear()
|
|
55
|
+
cachedBaseLinkingConfig = createLinkingConfig(routeNode, true, linking)
|
|
42
56
|
cachedRouteNodeForLinking = routeNode
|
|
57
|
+
cachedLinkingConfigKey = linkingConfigKey
|
|
43
58
|
}
|
|
44
59
|
}
|
|
45
60
|
|
|
@@ -49,11 +64,12 @@ export function ensureBaseLinkingConfig(routeNode: RouteNode | null) {
|
|
|
49
64
|
*/
|
|
50
65
|
export function getSSRInitialState(
|
|
51
66
|
routeNode: RouteNode | null,
|
|
52
|
-
initialLocation: URL
|
|
67
|
+
initialLocation: URL,
|
|
68
|
+
linking?: OneLinkingConfig
|
|
53
69
|
): OneRouter.ResultState | undefined {
|
|
54
70
|
if (!routeNode) return undefined
|
|
55
71
|
|
|
56
|
-
ensureBaseLinkingConfig(routeNode)
|
|
72
|
+
ensureBaseLinkingConfig(routeNode, linking)
|
|
57
73
|
if (!cachedBaseLinkingConfig) return undefined
|
|
58
74
|
|
|
59
75
|
const path = initialLocation.pathname + (initialLocation.search || '')
|
|
@@ -70,13 +86,14 @@ export function getSSRInitialState(
|
|
|
70
86
|
|
|
71
87
|
export function setupLinking(
|
|
72
88
|
routeNode: RouteNode | null,
|
|
73
|
-
initialLocation?: URL
|
|
89
|
+
initialLocation?: URL,
|
|
90
|
+
linking?: OneLinkingConfig
|
|
74
91
|
): OneRouter.ResultState | undefined {
|
|
75
92
|
let initialState: OneRouter.ResultState | undefined
|
|
76
93
|
|
|
77
94
|
if (routeNode) {
|
|
78
95
|
// reuse ensureBaseLinkingConfig to avoid duplicating cache logic
|
|
79
|
-
ensureBaseLinkingConfig(routeNode)
|
|
96
|
+
ensureBaseLinkingConfig(routeNode, linking)
|
|
80
97
|
|
|
81
98
|
// shallow copy so per-request mutations (getInitialURL) don't affect cache
|
|
82
99
|
linkingConfig = { ...cachedBaseLinkingConfig! }
|
|
@@ -101,3 +118,30 @@ export function setupLinking(
|
|
|
101
118
|
|
|
102
119
|
return initialState
|
|
103
120
|
}
|
|
121
|
+
|
|
122
|
+
// each unique filter function gets a stable id so reference-equal filters
|
|
123
|
+
// share a cache key while distinct filters invalidate it
|
|
124
|
+
const filterIds = new WeakMap<NonNullable<OneLinkingConfig['filter']>, number>()
|
|
125
|
+
let filterIdCounter = 0
|
|
126
|
+
function getFilterId(filter: OneLinkingConfig['filter']) {
|
|
127
|
+
if (!filter) return 0
|
|
128
|
+
let id = filterIds.get(filter)
|
|
129
|
+
if (id === undefined) {
|
|
130
|
+
id = ++filterIdCounter
|
|
131
|
+
filterIds.set(filter, id)
|
|
132
|
+
}
|
|
133
|
+
return id
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function getLinkingConfigKey(linking: OneLinkingConfig | undefined) {
|
|
137
|
+
// sort prefixes/schemes so ordering doesn't cause spurious cache misses
|
|
138
|
+
const schemes = Array.isArray(linking?.scheme)
|
|
139
|
+
? [...linking.scheme].sort()
|
|
140
|
+
: (linking?.scheme ?? null)
|
|
141
|
+
const prefixes = linking?.prefixes ? [...linking.prefixes].sort() : null
|
|
142
|
+
return JSON.stringify({
|
|
143
|
+
scheme: schemes,
|
|
144
|
+
prefixes,
|
|
145
|
+
filterId: getFilterId(linking?.filter),
|
|
146
|
+
})
|
|
147
|
+
}
|
package/src/router/router.ts
CHANGED
|
@@ -20,6 +20,7 @@ import { Platform } from 'react-native'
|
|
|
20
20
|
import { devtoolsRegistry } from '../devtools/registry'
|
|
21
21
|
import type { OneRouter } from '../interfaces/router'
|
|
22
22
|
import { resolveHref } from '../link/href'
|
|
23
|
+
import type { OneLinkingConfig } from '../link/getLinking'
|
|
23
24
|
import { openExternalURL } from '../link/openExternalURL'
|
|
24
25
|
import { resolve } from '../link/path'
|
|
25
26
|
import { checkBlocker } from '../useBlocker'
|
|
@@ -45,7 +46,7 @@ import type { UrlObject } from './getNormalizedStatePath'
|
|
|
45
46
|
import { getRouteInfo } from './getRouteInfo'
|
|
46
47
|
import { getRoutes } from './getRoutes'
|
|
47
48
|
import { setLastAction } from './lastAction'
|
|
48
|
-
import {
|
|
49
|
+
import { getResolvedLinking, resetLinking, setupLinking } from './linkingConfig'
|
|
49
50
|
import type { RouteNode } from './Route'
|
|
50
51
|
import { sortRoutes } from './sortRoutes'
|
|
51
52
|
import { getQualifiedRouteComponent } from './useScreens'
|
|
@@ -214,7 +215,8 @@ let cachedContext: One.RouteContext | null = null
|
|
|
214
215
|
export function initialize(
|
|
215
216
|
context: One.RouteContext,
|
|
216
217
|
ref: NavigationContainerRefWithCurrent<ReactNavigation.RootParamList>,
|
|
217
|
-
initialLocation?: URL
|
|
218
|
+
initialLocation?: URL,
|
|
219
|
+
linking?: OneLinkingConfig
|
|
218
220
|
) {
|
|
219
221
|
cleanUpState()
|
|
220
222
|
|
|
@@ -281,7 +283,7 @@ export function initialize(
|
|
|
281
283
|
}
|
|
282
284
|
|
|
283
285
|
navigationRef = ref as unknown as OneRouter.NavigationRef
|
|
284
|
-
setupLinkingAndRouteInfo(initialLocation)
|
|
286
|
+
setupLinkingAndRouteInfo(initialLocation, linking)
|
|
285
287
|
subscribeToNavigationChanges()
|
|
286
288
|
}
|
|
287
289
|
|
|
@@ -297,8 +299,8 @@ function cleanUpState() {
|
|
|
297
299
|
storeSubscribers.clear()
|
|
298
300
|
}
|
|
299
301
|
|
|
300
|
-
function setupLinkingAndRouteInfo(initialLocation?: URL) {
|
|
301
|
-
initialState = setupLinking(routeNode, initialLocation)
|
|
302
|
+
function setupLinkingAndRouteInfo(initialLocation?: URL, linking?: OneLinkingConfig) {
|
|
303
|
+
initialState = setupLinking(routeNode, initialLocation, linking)
|
|
302
304
|
|
|
303
305
|
// capture the original pathname before React Navigation's linking can modify it
|
|
304
306
|
initialPathname =
|
|
@@ -557,7 +559,7 @@ function getSnapshot() {
|
|
|
557
559
|
linkTo,
|
|
558
560
|
routeNode,
|
|
559
561
|
rootComponent,
|
|
560
|
-
linking:
|
|
562
|
+
linking: getResolvedLinking(),
|
|
561
563
|
hasAttemptedToHideSplash,
|
|
562
564
|
initialState,
|
|
563
565
|
rootState,
|
|
@@ -1059,7 +1061,7 @@ export async function linkTo(
|
|
|
1059
1061
|
)
|
|
1060
1062
|
}
|
|
1061
1063
|
|
|
1062
|
-
const linking =
|
|
1064
|
+
const linking = getResolvedLinking()
|
|
1063
1065
|
|
|
1064
1066
|
if (!linking) {
|
|
1065
1067
|
throw new Error('Attempted to link to route when no routes are present')
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { getRoutes } from './getRoutes'
|
|
3
|
+
import { getSitemap } from './sitemap'
|
|
4
|
+
import { getMockContext } from '../testing-utils'
|
|
5
|
+
|
|
6
|
+
describe('getSitemap', () => {
|
|
7
|
+
it('maps the route tree to a public sitemap shape', () => {
|
|
8
|
+
const routes = getRoutes(
|
|
9
|
+
getMockContext([
|
|
10
|
+
'_layout.tsx',
|
|
11
|
+
'index.tsx',
|
|
12
|
+
'(app)/_layout.tsx',
|
|
13
|
+
'(app)/thread/[id].tsx',
|
|
14
|
+
'docs/[...slug].tsx',
|
|
15
|
+
])
|
|
16
|
+
)!
|
|
17
|
+
const sitemap = getSitemap(routes)!
|
|
18
|
+
|
|
19
|
+
expect(sitemap).toMatchObject({
|
|
20
|
+
contextKey: './_layout.tsx',
|
|
21
|
+
href: '/',
|
|
22
|
+
isInternal: false,
|
|
23
|
+
isGenerated: false,
|
|
24
|
+
children: expect.arrayContaining([
|
|
25
|
+
expect.objectContaining({
|
|
26
|
+
contextKey: './index.tsx',
|
|
27
|
+
filename: 'index',
|
|
28
|
+
href: '/',
|
|
29
|
+
}),
|
|
30
|
+
expect.objectContaining({
|
|
31
|
+
contextKey: './(app)/_layout.tsx',
|
|
32
|
+
filename: '(app)/_layout',
|
|
33
|
+
href: '/(app)',
|
|
34
|
+
children: expect.arrayContaining([
|
|
35
|
+
expect.objectContaining({
|
|
36
|
+
contextKey: './(app)/thread/[id].tsx',
|
|
37
|
+
filename: 'thread/[id]',
|
|
38
|
+
href: '/(app)/thread/[id]',
|
|
39
|
+
}),
|
|
40
|
+
]),
|
|
41
|
+
}),
|
|
42
|
+
expect.objectContaining({
|
|
43
|
+
contextKey: './docs/[...slug].tsx',
|
|
44
|
+
filename: 'docs/[...slug]',
|
|
45
|
+
href: '/docs/[...slug]',
|
|
46
|
+
}),
|
|
47
|
+
]),
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
})
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { useMemo } from 'react'
|
|
2
|
+
import type { OneRouter } from '../interfaces/router'
|
|
3
|
+
import type { RouteNode } from './Route'
|
|
4
|
+
import { removeSupportedExtensions } from './matchers'
|
|
5
|
+
import { routeNode } from './router'
|
|
6
|
+
import { sortRoutes } from './sortRoutes'
|
|
7
|
+
|
|
8
|
+
export type SitemapType = {
|
|
9
|
+
contextKey: string
|
|
10
|
+
filename: string
|
|
11
|
+
href: string | OneRouter.Href
|
|
12
|
+
isInitial: boolean
|
|
13
|
+
isInternal: boolean
|
|
14
|
+
isGenerated: boolean
|
|
15
|
+
children: SitemapType[]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function useSitemap(): SitemapType | null {
|
|
19
|
+
return useMemo(() => getSitemap(routeNode), [routeNode])
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function getSitemap(root: RouteNode | null): SitemapType | null {
|
|
23
|
+
return root ? mapRouteToSitemap(root, []) : null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function mapRouteToSitemap(route: RouteNode, parents: string[]): SitemapType {
|
|
27
|
+
return {
|
|
28
|
+
contextKey: route.contextKey,
|
|
29
|
+
filename: getRouteFilename(route),
|
|
30
|
+
href: getRouteHref(route, parents),
|
|
31
|
+
isInitial: route.initialRouteName === route.route,
|
|
32
|
+
isInternal: route.internal ?? false,
|
|
33
|
+
isGenerated: route.generated ?? false,
|
|
34
|
+
children: [...route.children]
|
|
35
|
+
.sort(sortRoutes)
|
|
36
|
+
.map((child) => mapRouteToSitemap(child, getRouteSegments(route, parents))),
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getRouteSegments(route: RouteNode, parents: string[]) {
|
|
41
|
+
return [...parents, ...route.route.split('/')]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// the sitemap exposes route patterns rather than concrete sample URLs:
|
|
45
|
+
// dynamic segments stay as `[id]` / `[...slug]`, and `index` collapses to '/'.
|
|
46
|
+
// callers wanting clickable links should fill in their own params.
|
|
47
|
+
function getRouteHref(route: RouteNode, parents: string[]) {
|
|
48
|
+
const path = getRouteSegments(route, parents)
|
|
49
|
+
.map((segment) => (segment === 'index' ? '' : segment))
|
|
50
|
+
.filter(Boolean)
|
|
51
|
+
.join('/')
|
|
52
|
+
|
|
53
|
+
return `/${path}`
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function getRouteFilename(route: RouteNode) {
|
|
57
|
+
const contextKey = removeSupportedExtensions(route.contextKey)
|
|
58
|
+
const segments = contextKey.split('/')
|
|
59
|
+
|
|
60
|
+
if (route.contextKey.match(/_layout\.[jt]sx?$/)) {
|
|
61
|
+
return segments.slice(-2).join('/')
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const routeSegmentsCount = route.route.split('/').length
|
|
65
|
+
return segments.slice(-routeSegmentsCount).join('/')
|
|
66
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useNavigationContainerRef } from '@react-navigation/native'
|
|
2
2
|
import { resetLoaderState } from '../useLoader'
|
|
3
3
|
import type { One } from '../vite/types'
|
|
4
|
+
import type { OneLinkingConfig } from '../link/getLinking'
|
|
4
5
|
import * as routerStore from './router'
|
|
5
6
|
import { initialize } from './router'
|
|
6
7
|
import { getSSRInitialState, ensureBaseLinkingConfig } from './linkingConfig'
|
|
@@ -15,7 +16,8 @@ let ssrRouteTreeInitialized = false
|
|
|
15
16
|
|
|
16
17
|
export function useInitializeOneRouter(
|
|
17
18
|
context: One.RouteContext,
|
|
18
|
-
initialLocation: URL | undefined
|
|
19
|
+
initialLocation: URL | undefined,
|
|
20
|
+
linking?: OneLinkingConfig
|
|
19
21
|
) {
|
|
20
22
|
const navigationRef = useNavigationContainerRef()
|
|
21
23
|
|
|
@@ -23,15 +25,15 @@ export function useInitializeOneRouter(
|
|
|
23
25
|
if (typeof window === 'undefined') {
|
|
24
26
|
if (!ssrRouteTreeInitialized) {
|
|
25
27
|
// first SSR request: full initialization to set up route tree, root component, etc.
|
|
26
|
-
initialize(context, navigationRef, initialLocation)
|
|
28
|
+
initialize(context, navigationRef, initialLocation, linking)
|
|
27
29
|
ssrRouteTreeInitialized = true
|
|
28
30
|
// also ensure linking config base is cached
|
|
29
|
-
ensureBaseLinkingConfig(routerStore.routeNode)
|
|
31
|
+
ensureBaseLinkingConfig(routerStore.routeNode, linking)
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
// per-request: compute initialState from URL (cached by path)
|
|
33
35
|
const initialState = initialLocation
|
|
34
|
-
? getSSRInitialState(routerStore.routeNode, initialLocation)
|
|
36
|
+
? getSSRInitialState(routerStore.routeNode, initialLocation, linking)
|
|
35
37
|
: routerStore.initialState
|
|
36
38
|
|
|
37
39
|
// return per-request snapshot to prevent concurrent request trampling
|
|
@@ -48,7 +50,7 @@ export function useInitializeOneRouter(
|
|
|
48
50
|
const contexts = '__react_navigation__elements_contexts'
|
|
49
51
|
globalThis[contexts] = new Map<string, React.Context<any>>()
|
|
50
52
|
|
|
51
|
-
initialize(context, navigationRef, initialLocation)
|
|
53
|
+
initialize(context, navigationRef, initialLocation, linking)
|
|
52
54
|
lastInitVersion = initVersion
|
|
53
55
|
}
|
|
54
56
|
|