one 1.15.10 → 1.16.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/dist/cjs/cli/build.cjs +98 -58
- package/dist/cjs/cli/build.native.js +106 -63
- package/dist/cjs/cli/build.native.js.map +1 -1
- package/dist/cjs/cli/buildPage.cjs +3 -3
- package/dist/cjs/cli/buildPage.native.js +3 -3
- package/dist/cjs/cli/buildPage.native.js.map +1 -1
- package/dist/cjs/constants.cjs +2 -0
- package/dist/cjs/constants.native.js +2 -0
- package/dist/cjs/constants.native.js.map +1 -1
- package/dist/cjs/createAPIRoute.native.js.map +1 -1
- package/dist/cjs/createApp.cjs +2 -6
- package/dist/cjs/createHandleRequest.cjs +2 -2
- package/dist/cjs/createHandleRequest.native.js +2 -2
- package/dist/cjs/createHandleRequest.native.js.map +1 -1
- package/dist/cjs/hooks.cjs +5 -1
- package/dist/cjs/hooks.native.js +7 -1
- package/dist/cjs/hooks.native.js.map +1 -1
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/metro-config/getViteMetroPluginOptions.cjs +10 -7
- package/dist/cjs/metro-config/getViteMetroPluginOptions.native.js +13 -10
- package/dist/cjs/metro-config/getViteMetroPluginOptions.native.js.map +1 -1
- package/dist/cjs/metro-config/getViteMetroPluginOptions.test.cjs +23 -0
- package/dist/cjs/metro-config/getViteMetroPluginOptions.test.native.js +26 -0
- package/dist/cjs/metro-config/getViteMetroPluginOptions.test.native.js.map +1 -0
- package/dist/cjs/router/Route.cjs +21 -1
- package/dist/cjs/router/Route.native.js +22 -1
- package/dist/cjs/router/Route.native.js.map +1 -1
- package/dist/cjs/router/interceptRoutes.cjs +6 -6
- package/dist/cjs/router/interceptRoutes.native.js +6 -6
- package/dist/cjs/router/interceptRoutes.native.js.map +1 -1
- package/dist/cjs/router/params.cjs +46 -0
- package/dist/cjs/router/params.native.js +70 -0
- package/dist/cjs/router/params.native.js.map +1 -0
- package/dist/cjs/router/useScreens.cjs +3 -0
- package/dist/cjs/router/useScreens.native.js +2 -0
- package/dist/cjs/router/useScreens.native.js.map +1 -1
- package/dist/cjs/serve-worker.native.js.map +1 -1
- package/dist/cjs/server/getServerManifest.cjs +6 -6
- package/dist/cjs/server/getServerManifest.native.js +6 -6
- package/dist/cjs/server/getServerManifest.native.js.map +1 -1
- package/dist/cjs/server/oneServe.cjs +9 -5
- package/dist/cjs/server/oneServe.native.js +9 -5
- package/dist/cjs/server/oneServe.native.js.map +1 -1
- package/dist/cjs/server/workerHandler.cjs +2 -2
- package/dist/cjs/server/workerHandler.native.js +2 -2
- package/dist/cjs/server/workerHandler.native.js.map +1 -1
- package/dist/cjs/skewProtection.cjs +48 -7
- package/dist/cjs/skewProtection.native.js +48 -7
- package/dist/cjs/skewProtection.native.js.map +1 -1
- package/dist/cjs/useLoader.cjs +67 -69
- package/dist/cjs/useLoader.native.js +113 -117
- package/dist/cjs/useLoader.native.js.map +1 -1
- package/dist/cjs/utils/dynamicImport.cjs +3 -1
- package/dist/cjs/utils/dynamicImport.native.js +22 -1
- package/dist/cjs/utils/dynamicImport.native.js.map +1 -1
- package/dist/cjs/utils/toAbsolute.cjs +5 -2
- package/dist/cjs/utils/toAbsolute.native.js +6 -1
- package/dist/cjs/utils/toAbsolute.native.js.map +1 -1
- package/dist/cjs/utils/workerImport.cjs +1 -1
- package/dist/cjs/utils/workerImport.native.js +1 -1
- package/dist/cjs/utils/workerImport.native.js.map +1 -1
- package/dist/cjs/views/RootErrorBoundary.cjs +114 -117
- package/dist/cjs/views/RootErrorBoundary.native.js +126 -146
- package/dist/cjs/views/RootErrorBoundary.native.js.map +1 -1
- package/dist/cjs/views/Try.cjs +17 -18
- package/dist/cjs/views/Try.native.js +28 -42
- package/dist/cjs/views/Try.native.js.map +1 -1
- package/dist/cjs/vite/plugins/criticalCSSPlugin.cjs +2 -1
- package/dist/cjs/vite/plugins/criticalCSSPlugin.native.js +2 -1
- package/dist/cjs/vite/plugins/criticalCSSPlugin.native.js.map +1 -1
- package/dist/cjs/vite/plugins/criticalCSSPlugin.test.cjs +26 -20
- package/dist/cjs/vite/plugins/criticalCSSPlugin.test.native.js +28 -20
- package/dist/cjs/vite/plugins/criticalCSSPlugin.test.native.js.map +1 -1
- package/dist/cjs/vite/plugins/imageDataPlugin.cjs +3 -2
- package/dist/cjs/vite/plugins/imageDataPlugin.native.js +3 -2
- package/dist/cjs/vite/plugins/imageDataPlugin.native.js.map +1 -1
- package/dist/cjs/vite/plugins/imageDataPlugin.test.cjs +34 -69
- package/dist/cjs/vite/plugins/imageDataPlugin.test.native.js +34 -69
- package/dist/cjs/vite/plugins/imageDataPlugin.test.native.js.map +1 -1
- package/dist/cjs/vite/plugins/sourceInspectorPlugin.cjs +20 -2
- package/dist/cjs/vite/plugins/sourceInspectorPlugin.native.js +23 -2
- package/dist/cjs/vite/plugins/sourceInspectorPlugin.native.js.map +1 -1
- package/dist/cjs/vite/plugins/sourceInspectorPlugin.test.cjs +21 -0
- package/dist/cjs/vite/plugins/sourceInspectorPlugin.test.native.js +28 -0
- package/dist/cjs/vite/plugins/sourceInspectorPlugin.test.native.js.map +1 -0
- package/dist/cjs/vite/resolveResponse.cjs +7 -2
- package/dist/cjs/vite/resolveResponse.native.js +7 -2
- package/dist/cjs/vite/resolveResponse.native.js.map +1 -1
- package/dist/esm/cli/build.mjs +100 -60
- package/dist/esm/cli/build.mjs.map +1 -1
- package/dist/esm/cli/build.native.js +108 -65
- package/dist/esm/cli/build.native.js.map +1 -1
- package/dist/esm/cli/buildPage.mjs +4 -4
- package/dist/esm/cli/buildPage.mjs.map +1 -1
- package/dist/esm/cli/buildPage.native.js +4 -4
- package/dist/esm/cli/buildPage.native.js.map +1 -1
- package/dist/esm/constants.mjs +2 -1
- package/dist/esm/constants.mjs.map +1 -1
- package/dist/esm/constants.native.js +2 -1
- package/dist/esm/constants.native.js.map +1 -1
- package/dist/esm/createAPIRoute.mjs.map +1 -1
- package/dist/esm/createAPIRoute.native.js.map +1 -1
- package/dist/esm/createApp.mjs +2 -6
- package/dist/esm/createApp.mjs.map +1 -1
- package/dist/esm/createHandleRequest.mjs +2 -2
- package/dist/esm/createHandleRequest.mjs.map +1 -1
- package/dist/esm/createHandleRequest.native.js +2 -2
- package/dist/esm/createHandleRequest.native.js.map +1 -1
- package/dist/esm/hooks.mjs +5 -1
- package/dist/esm/hooks.mjs.map +1 -1
- package/dist/esm/hooks.native.js +7 -1
- package/dist/esm/hooks.native.js.map +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/metro-config/getViteMetroPluginOptions.mjs +9 -7
- package/dist/esm/metro-config/getViteMetroPluginOptions.mjs.map +1 -1
- package/dist/esm/metro-config/getViteMetroPluginOptions.native.js +12 -10
- package/dist/esm/metro-config/getViteMetroPluginOptions.native.js.map +1 -1
- package/dist/esm/metro-config/getViteMetroPluginOptions.test.mjs +24 -0
- package/dist/esm/metro-config/getViteMetroPluginOptions.test.mjs.map +1 -0
- package/dist/esm/metro-config/getViteMetroPluginOptions.test.native.js +24 -0
- package/dist/esm/metro-config/getViteMetroPluginOptions.test.native.js.map +1 -0
- package/dist/esm/router/Route.mjs +21 -1
- package/dist/esm/router/Route.mjs.map +1 -1
- package/dist/esm/router/Route.native.js +22 -1
- package/dist/esm/router/Route.native.js.map +1 -1
- package/dist/esm/router/interceptRoutes.mjs +7 -7
- package/dist/esm/router/interceptRoutes.mjs.map +1 -1
- package/dist/esm/router/interceptRoutes.native.js +7 -7
- package/dist/esm/router/interceptRoutes.native.js.map +1 -1
- package/dist/esm/router/params.mjs +21 -0
- package/dist/esm/router/params.mjs.map +1 -0
- package/dist/esm/router/params.native.js +42 -0
- package/dist/esm/router/params.native.js.map +1 -0
- package/dist/esm/router/useScreens.mjs +3 -0
- package/dist/esm/router/useScreens.mjs.map +1 -1
- package/dist/esm/router/useScreens.native.js +2 -0
- package/dist/esm/router/useScreens.native.js.map +1 -1
- package/dist/esm/serve-worker.mjs.map +1 -1
- package/dist/esm/serve-worker.native.js.map +1 -1
- package/dist/esm/server/getServerManifest.mjs +6 -6
- package/dist/esm/server/getServerManifest.mjs.map +1 -1
- package/dist/esm/server/getServerManifest.native.js +6 -6
- package/dist/esm/server/getServerManifest.native.js.map +1 -1
- package/dist/esm/server/oneServe.mjs +11 -7
- package/dist/esm/server/oneServe.mjs.map +1 -1
- package/dist/esm/server/oneServe.native.js +11 -7
- package/dist/esm/server/oneServe.native.js.map +1 -1
- package/dist/esm/server/workerHandler.mjs +2 -2
- package/dist/esm/server/workerHandler.mjs.map +1 -1
- package/dist/esm/server/workerHandler.native.js +2 -2
- package/dist/esm/server/workerHandler.native.js.map +1 -1
- package/dist/esm/skewProtection.mjs +48 -8
- package/dist/esm/skewProtection.mjs.map +1 -1
- package/dist/esm/skewProtection.native.js +48 -8
- package/dist/esm/skewProtection.native.js.map +1 -1
- package/dist/esm/useLoader.mjs +64 -66
- package/dist/esm/useLoader.mjs.map +1 -1
- package/dist/esm/useLoader.native.js +112 -116
- package/dist/esm/useLoader.native.js.map +1 -1
- package/dist/esm/utils/dynamicImport.mjs +1 -1
- package/dist/esm/utils/dynamicImport.mjs.map +1 -1
- package/dist/esm/utils/dynamicImport.native.js +20 -1
- package/dist/esm/utils/dynamicImport.native.js.map +1 -1
- package/dist/esm/utils/toAbsolute.mjs +3 -1
- package/dist/esm/utils/toAbsolute.mjs.map +1 -1
- package/dist/esm/utils/toAbsolute.native.js +5 -1
- package/dist/esm/utils/toAbsolute.native.js.map +1 -1
- package/dist/esm/utils/workerImport.mjs +2 -2
- package/dist/esm/utils/workerImport.mjs.map +1 -1
- package/dist/esm/utils/workerImport.native.js +2 -2
- package/dist/esm/utils/workerImport.native.js.map +1 -1
- package/dist/esm/views/RootErrorBoundary.mjs +114 -117
- package/dist/esm/views/RootErrorBoundary.mjs.map +1 -1
- package/dist/esm/views/RootErrorBoundary.native.js +126 -146
- package/dist/esm/views/RootErrorBoundary.native.js.map +1 -1
- package/dist/esm/views/Try.mjs +17 -18
- package/dist/esm/views/Try.mjs.map +1 -1
- package/dist/esm/views/Try.native.js +28 -42
- package/dist/esm/views/Try.native.js.map +1 -1
- package/dist/esm/vite/plugins/criticalCSSPlugin.mjs +2 -1
- package/dist/esm/vite/plugins/criticalCSSPlugin.mjs.map +1 -1
- package/dist/esm/vite/plugins/criticalCSSPlugin.native.js +2 -1
- package/dist/esm/vite/plugins/criticalCSSPlugin.native.js.map +1 -1
- package/dist/esm/vite/plugins/criticalCSSPlugin.test.mjs +26 -20
- package/dist/esm/vite/plugins/criticalCSSPlugin.test.mjs.map +1 -1
- package/dist/esm/vite/plugins/criticalCSSPlugin.test.native.js +28 -20
- package/dist/esm/vite/plugins/criticalCSSPlugin.test.native.js.map +1 -1
- package/dist/esm/vite/plugins/imageDataPlugin.mjs +4 -3
- package/dist/esm/vite/plugins/imageDataPlugin.mjs.map +1 -1
- package/dist/esm/vite/plugins/imageDataPlugin.native.js +4 -3
- package/dist/esm/vite/plugins/imageDataPlugin.native.js.map +1 -1
- package/dist/esm/vite/plugins/imageDataPlugin.test.mjs +34 -69
- package/dist/esm/vite/plugins/imageDataPlugin.test.mjs.map +1 -1
- package/dist/esm/vite/plugins/imageDataPlugin.test.native.js +34 -69
- package/dist/esm/vite/plugins/imageDataPlugin.test.native.js.map +1 -1
- package/dist/esm/vite/plugins/sourceInspectorPlugin.mjs +19 -3
- package/dist/esm/vite/plugins/sourceInspectorPlugin.mjs.map +1 -1
- package/dist/esm/vite/plugins/sourceInspectorPlugin.native.js +22 -3
- package/dist/esm/vite/plugins/sourceInspectorPlugin.native.js.map +1 -1
- package/dist/esm/vite/plugins/sourceInspectorPlugin.test.mjs +22 -0
- package/dist/esm/vite/plugins/sourceInspectorPlugin.test.mjs.map +1 -0
- package/dist/esm/vite/plugins/sourceInspectorPlugin.test.native.js +26 -0
- package/dist/esm/vite/plugins/sourceInspectorPlugin.test.native.js.map +1 -0
- package/dist/esm/vite/resolveResponse.mjs +7 -2
- package/dist/esm/vite/resolveResponse.mjs.map +1 -1
- package/dist/esm/vite/resolveResponse.native.js +7 -2
- package/dist/esm/vite/resolveResponse.native.js.map +1 -1
- package/expo-plugin.cjs +1 -0
- package/package.json +15 -10
- package/react-native-commands.cjs +1 -0
- package/src/cli/build.ts +156 -99
- package/src/cli/buildPage.ts +4 -4
- package/src/constants.ts +15 -0
- package/src/createAPIRoute.ts +35 -2
- package/src/createApp.tsx +2 -6
- package/src/createHandleRequest.ts +6 -2
- package/src/hooks.tsx +9 -1
- package/src/index.ts +8 -1
- package/src/metro-config/getViteMetroPluginOptions.test.ts +34 -0
- package/src/metro-config/getViteMetroPluginOptions.ts +14 -11
- package/src/router/Route.tsx +52 -2
- package/src/router/interceptRoutes.ts +7 -7
- package/src/router/params.ts +32 -0
- package/src/router/useScreens.tsx +18 -0
- package/src/serve-worker.ts +4 -2
- package/src/server/getServerManifest.ts +11 -7
- package/src/server/oneServe.ts +12 -14
- package/src/server/workerHandler.ts +13 -2
- package/src/skewProtection.ts +45 -5
- package/src/useLoader.ts +6 -4
- package/src/utils/dynamicImport.ts +2 -2
- package/src/utils/toAbsolute.ts +5 -0
- package/src/utils/workerImport.ts +2 -2
- package/src/views/RootErrorBoundary.tsx +18 -0
- package/src/views/Try.tsx +18 -0
- package/src/vite/plugins/criticalCSSPlugin.test.ts +34 -24
- package/src/vite/plugins/criticalCSSPlugin.ts +3 -2
- package/src/vite/plugins/imageDataPlugin.test.ts +39 -83
- package/src/vite/plugins/imageDataPlugin.ts +5 -4
- package/src/vite/plugins/sourceInspectorPlugin.test.ts +40 -0
- package/src/vite/plugins/sourceInspectorPlugin.ts +30 -2
- package/src/vite/resolveResponse.ts +6 -2
- package/types/cli/build.d.ts.map +1 -1
- package/types/constants.d.ts +9 -0
- package/types/constants.d.ts.map +1 -1
- package/types/createAPIRoute.d.ts +28 -3
- package/types/createAPIRoute.d.ts.map +1 -1
- package/types/createApp.d.ts.map +1 -1
- package/types/createHandleRequest.d.ts +1 -1
- package/types/createHandleRequest.d.ts.map +1 -1
- package/types/headless-server.d.ts +1 -1
- package/types/hooks.d.ts.map +1 -1
- package/types/index.d.ts +1 -1
- package/types/index.d.ts.map +1 -1
- package/types/metro-config/getViteMetroPluginOptions.d.ts +5 -0
- package/types/metro-config/getViteMetroPluginOptions.d.ts.map +1 -1
- package/types/metro-config/getViteMetroPluginOptions.test.d.ts +2 -0
- package/types/metro-config/getViteMetroPluginOptions.test.d.ts.map +1 -0
- package/types/router/Route.d.ts +1 -0
- package/types/router/Route.d.ts.map +1 -1
- package/types/router/params.d.ts +3 -0
- package/types/router/params.d.ts.map +1 -0
- package/types/router/useScreens.d.ts.map +1 -1
- package/types/serve-worker.d.ts +5 -3
- package/types/serve-worker.d.ts.map +1 -1
- package/types/server/getServerManifest.d.ts.map +1 -1
- package/types/server/oneServe.d.ts.map +1 -1
- package/types/server/workerHandler.d.ts +1 -1
- package/types/server/workerHandler.d.ts.map +1 -1
- package/types/skewProtection.d.ts +1 -0
- package/types/skewProtection.d.ts.map +1 -1
- package/types/useLoader.d.ts.map +1 -1
- package/types/utils/dynamicImport.d.ts +2 -0
- package/types/utils/dynamicImport.d.ts.map +1 -1
- package/types/utils/toAbsolute.d.ts +3 -0
- package/types/utils/toAbsolute.d.ts.map +1 -1
- package/types/views/RootErrorBoundary.d.ts.map +1 -1
- package/types/views/Try.d.ts.map +1 -1
- package/types/vite/plugins/criticalCSSPlugin.d.ts.map +1 -1
- package/types/vite/plugins/imageDataPlugin.d.ts.map +1 -1
- package/types/vite/plugins/sourceInspectorPlugin.d.ts +2 -0
- package/types/vite/plugins/sourceInspectorPlugin.d.ts.map +1 -1
- package/types/vite/plugins/sourceInspectorPlugin.test.d.ts +2 -0
- package/types/vite/plugins/sourceInspectorPlugin.test.d.ts.map +1 -0
- package/types/vite/resolveResponse.d.ts +1 -1
- package/types/vite/resolveResponse.d.ts.map +1 -1
|
@@ -8,6 +8,14 @@ import {
|
|
|
8
8
|
ROUTE_NATIVE_EXCLUSION_GLOB_PATTERNS,
|
|
9
9
|
} from '../router/glob-patterns'
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* On Windows, micromatch.makeRe() produces regex patterns with `[\\/]` or `[^\\/]`
|
|
13
|
+
* instead of `\/` and `[^/]`. Normalize them so the startsWith check works.
|
|
14
|
+
*/
|
|
15
|
+
export function normalizeReSource(source: string): string {
|
|
16
|
+
return source.replace(/\[\\\\\/\]/g, '\\/').replace(/\[\^\\\\\/\]/g, '[^/]')
|
|
17
|
+
}
|
|
18
|
+
|
|
11
19
|
export function getViteMetroPluginOptions({
|
|
12
20
|
projectRoot,
|
|
13
21
|
relativeRouterRoot,
|
|
@@ -56,7 +64,7 @@ export function getViteMetroPluginOptions({
|
|
|
56
64
|
* ^(?:(?:^|\/|(?:(?:(?!(?:^|\/)\.).)*?)\/)(?!\.)(?=.)[^/]*?\+api\.(ts|tsx))$
|
|
57
65
|
* ```
|
|
58
66
|
*/
|
|
59
|
-
const reSource = re.source
|
|
67
|
+
const reSource = normalizeReSource(re.source)
|
|
60
68
|
|
|
61
69
|
if (
|
|
62
70
|
!(
|
|
@@ -112,13 +120,7 @@ export function getViteMetroPluginOptions({
|
|
|
112
120
|
// .filter((i): i is NonNullable<typeof i> => !!i)
|
|
113
121
|
// ),
|
|
114
122
|
},
|
|
115
|
-
nodeModulesPaths:
|
|
116
|
-
? [
|
|
117
|
-
// "vite-tsconfig-paths" for Metro
|
|
118
|
-
tsconfigPathsConfigLoadResult.absoluteBaseUrl,
|
|
119
|
-
...(defaultConfig?.resolver?.nodeModulesPaths || []),
|
|
120
|
-
]
|
|
121
|
-
: defaultConfig?.resolver?.nodeModulesPaths,
|
|
123
|
+
nodeModulesPaths: defaultConfig?.resolver?.nodeModulesPaths,
|
|
122
124
|
resolveRequest: (context, moduleName, platform) => {
|
|
123
125
|
if (moduleName.endsWith('.css')) {
|
|
124
126
|
return {
|
|
@@ -153,12 +155,13 @@ export function getViteMetroPluginOptions({
|
|
|
153
155
|
const defaultResolveRequest =
|
|
154
156
|
defaultConfig?.resolver?.resolveRequest || context.resolveRequest
|
|
155
157
|
const res = defaultResolveRequest(context, moduleName, platform)
|
|
156
|
-
|
|
158
|
+
const svgSrcSuffix = `${path.sep}src${path.sep}index.ts`
|
|
159
|
+
if (res && 'filePath' in res && res.filePath.includes(svgSrcSuffix)) {
|
|
157
160
|
return {
|
|
158
161
|
...res,
|
|
159
162
|
filePath: res.filePath.replace(
|
|
160
|
-
|
|
161
|
-
|
|
163
|
+
svgSrcSuffix,
|
|
164
|
+
`${path.sep}lib${path.sep}commonjs${path.sep}index.js`
|
|
162
165
|
),
|
|
163
166
|
}
|
|
164
167
|
}
|
package/src/router/Route.tsx
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import React, { createContext, type ReactNode, useContext } from 'react'
|
|
2
|
+
import { findFocusedRoute } from '../fork/findFocusedRoute'
|
|
2
3
|
import type { ErrorBoundaryProps } from '../views/Try'
|
|
3
4
|
import type { LoaderProps } from '../types'
|
|
4
5
|
import type { One } from '../vite/types'
|
|
5
6
|
import type { ParamValidator, RouteValidationFn } from '../validateParams'
|
|
7
|
+
import { getLinking } from './linkingConfig'
|
|
6
8
|
import { getContextKey } from './matchers'
|
|
9
|
+
import { mergeDynamicParams } from './params'
|
|
10
|
+
import { routeInfo } from './router'
|
|
7
11
|
import { RouteInfoContextProvider } from './RouteInfoContext'
|
|
8
12
|
|
|
9
13
|
export type DynamicConvention = {
|
|
@@ -136,6 +140,34 @@ export function useContextKey(): string {
|
|
|
136
140
|
return getContextKey(node.contextKey)
|
|
137
141
|
}
|
|
138
142
|
|
|
143
|
+
/**
|
|
144
|
+
* Resolve path params from the current URL using the same linking
|
|
145
|
+
* config (getStateFromPath) that the router uses for navigation.
|
|
146
|
+
* Returns `undefined` if linking isn't set up yet (SSR, pre-init) or
|
|
147
|
+
* the URL doesn't produce a focused route with params.
|
|
148
|
+
*
|
|
149
|
+
* Reuses the router's existing URL-parsing rather than re-implementing
|
|
150
|
+
* segment matching, so group/catch-all/index semantics stay consistent.
|
|
151
|
+
*/
|
|
152
|
+
function getParamsFromCurrentUrl(route?: {
|
|
153
|
+
path?: string
|
|
154
|
+
params?: Record<string, string | undefined>
|
|
155
|
+
}): Record<string, any> | undefined {
|
|
156
|
+
const linking = getLinking()
|
|
157
|
+
if (!linking?.getStateFromPath) return undefined
|
|
158
|
+
const path =
|
|
159
|
+
routeInfo?.unstable_globalHref ||
|
|
160
|
+
route?.path ||
|
|
161
|
+
(typeof window !== 'undefined' && window.location
|
|
162
|
+
? window.location.pathname + window.location.search
|
|
163
|
+
: undefined)
|
|
164
|
+
if (!path) return undefined
|
|
165
|
+
const state = linking.getStateFromPath(path, linking.config)
|
|
166
|
+
if (!state) return undefined
|
|
167
|
+
const focused = findFocusedRoute(state)
|
|
168
|
+
return focused?.params as Record<string, any> | undefined
|
|
169
|
+
}
|
|
170
|
+
|
|
139
171
|
/** Provides the matching routes and filename to the children. */
|
|
140
172
|
export function Route({
|
|
141
173
|
children,
|
|
@@ -144,10 +176,28 @@ export function Route({
|
|
|
144
176
|
}: {
|
|
145
177
|
children: ReactNode
|
|
146
178
|
node: RouteNode
|
|
147
|
-
route?: {
|
|
179
|
+
route?: {
|
|
180
|
+
path?: string
|
|
181
|
+
params?: Record<string, string | undefined>
|
|
182
|
+
}
|
|
148
183
|
}) {
|
|
184
|
+
// url is the source of truth for path params. react navigation can provide
|
|
185
|
+
// a `route` whose `params` are missing or stale for the dynamic segments
|
|
186
|
+
// this node expects (observed in spa-shell mode under strictmode, and when
|
|
187
|
+
// navigating between sibling dynamic routes under the same layout).
|
|
188
|
+
//
|
|
189
|
+
// to keep useParams() aligned with the current route, recover dynamic
|
|
190
|
+
// segment params by re-parsing the router path through the linking config.
|
|
191
|
+
// non-dynamic params keep flowing from React Navigation.
|
|
192
|
+
const resolvedParams = React.useMemo(() => {
|
|
193
|
+
const rp = route?.params
|
|
194
|
+
if (!node.dynamic?.length) return rp
|
|
195
|
+
const fromUrl = getParamsFromCurrentUrl(route)
|
|
196
|
+
return mergeDynamicParams(rp, node.dynamic, fromUrl)
|
|
197
|
+
}, [node, route, routeInfo?.unstable_globalHref])
|
|
198
|
+
|
|
149
199
|
return (
|
|
150
|
-
<RouteParamsContext.Provider value={
|
|
200
|
+
<RouteParamsContext.Provider value={resolvedParams}>
|
|
151
201
|
<CurrentRouteContext.Provider value={node}>
|
|
152
202
|
<RouteInfoContextProvider>{children}</RouteInfoContextProvider>
|
|
153
203
|
</CurrentRouteContext.Provider>
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
matchRoutePattern,
|
|
5
5
|
stripGroupSegmentsFromPath,
|
|
6
6
|
} from './matchers'
|
|
7
|
-
import { isNative } from '../constants'
|
|
7
|
+
import { hasWebHistory, isNative } from '../constants'
|
|
8
8
|
|
|
9
9
|
// ============================================
|
|
10
10
|
// Navigation Type Tracking
|
|
@@ -333,7 +333,7 @@ let preInterceptUrl: string | null = null
|
|
|
333
333
|
* Used when activating an intercept route to show the target URL.
|
|
334
334
|
*/
|
|
335
335
|
export function updateURLWithoutNavigation(href: string) {
|
|
336
|
-
if (
|
|
336
|
+
if (hasWebHistory) {
|
|
337
337
|
// Store the URL before we change it
|
|
338
338
|
preInterceptUrl = window.location.pathname + window.location.search
|
|
339
339
|
|
|
@@ -361,7 +361,7 @@ export function registerClearSlotStates(callback: () => void) {
|
|
|
361
361
|
* This should be called from modal close handlers instead of router.back().
|
|
362
362
|
*/
|
|
363
363
|
export function closeIntercept(): boolean {
|
|
364
|
-
if (
|
|
364
|
+
if (!hasWebHistory) return false
|
|
365
365
|
|
|
366
366
|
const state = window.history.state
|
|
367
367
|
if (!state?.__intercepted) {
|
|
@@ -385,7 +385,7 @@ export function closeIntercept(): boolean {
|
|
|
385
385
|
* Check if the current navigation state is from an interception
|
|
386
386
|
*/
|
|
387
387
|
export function isInterceptedNavigation(): boolean {
|
|
388
|
-
if (
|
|
388
|
+
if (!hasWebHistory) return false
|
|
389
389
|
return window.history.state?.__intercepted === true
|
|
390
390
|
}
|
|
391
391
|
|
|
@@ -393,7 +393,7 @@ export function isInterceptedNavigation(): boolean {
|
|
|
393
393
|
* Get the actual path from an intercepted navigation
|
|
394
394
|
*/
|
|
395
395
|
export function getInterceptedActualPath(): string | null {
|
|
396
|
-
if (
|
|
396
|
+
if (!hasWebHistory) return null
|
|
397
397
|
return window.history.state?.__actualPath ?? null
|
|
398
398
|
}
|
|
399
399
|
|
|
@@ -401,7 +401,7 @@ export function getInterceptedActualPath(): string | null {
|
|
|
401
401
|
* Get the URL from before the interception
|
|
402
402
|
*/
|
|
403
403
|
export function getPreInterceptUrl(): string | null {
|
|
404
|
-
if (
|
|
404
|
+
if (!hasWebHistory) return null
|
|
405
405
|
return window.history.state?.__preInterceptUrl ?? preInterceptUrl
|
|
406
406
|
}
|
|
407
407
|
|
|
@@ -456,7 +456,7 @@ export function storeInterceptState(
|
|
|
456
456
|
* Returns true if an intercept was restored, false otherwise.
|
|
457
457
|
*/
|
|
458
458
|
export function restoreInterceptFromHistory(): boolean {
|
|
459
|
-
if (
|
|
459
|
+
if (!hasWebHistory) return false
|
|
460
460
|
|
|
461
461
|
const state = window.history.state
|
|
462
462
|
if (!state?.__intercepted) {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { DynamicConvention } from './Route'
|
|
2
|
+
|
|
3
|
+
function paramValueEqual(a: unknown, b: unknown) {
|
|
4
|
+
if (Array.isArray(a) || Array.isArray(b)) {
|
|
5
|
+
return (
|
|
6
|
+
Array.isArray(a) &&
|
|
7
|
+
Array.isArray(b) &&
|
|
8
|
+
a.length === b.length &&
|
|
9
|
+
a.every((value, index) => value === b[index])
|
|
10
|
+
)
|
|
11
|
+
}
|
|
12
|
+
return a === b
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function mergeDynamicParams<TParams extends Record<string, any> | undefined>(
|
|
16
|
+
params: TParams,
|
|
17
|
+
dynamic: DynamicConvention[] | null | undefined,
|
|
18
|
+
source: Record<string, any> | undefined
|
|
19
|
+
): TParams {
|
|
20
|
+
if (!dynamic?.length || !source) return params
|
|
21
|
+
|
|
22
|
+
let next: Record<string, any> | undefined
|
|
23
|
+
|
|
24
|
+
for (const segment of dynamic) {
|
|
25
|
+
const value = source[segment.name]
|
|
26
|
+
if (value == null || paramValueEqual(params?.[segment.name], value)) continue
|
|
27
|
+
next ??= { ...params }
|
|
28
|
+
next[segment.name] = value
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return (next ?? params) as TParams
|
|
32
|
+
}
|
|
@@ -13,6 +13,8 @@ import { ServerContextScript } from '../server/ServerContextScript'
|
|
|
13
13
|
import { getPageExport } from '../utils/getPageExport'
|
|
14
14
|
import { EmptyRoute } from '../views/EmptyRoute'
|
|
15
15
|
import { Try } from '../views/Try'
|
|
16
|
+
import { checkSkewAndReload } from '../skewProtection'
|
|
17
|
+
import { handleSkewError, isChunkLoadError } from '../utils/dynamicImport'
|
|
16
18
|
import { DevHead } from '../vite/DevHead'
|
|
17
19
|
import { useServerContext } from '../vite/one-server-only'
|
|
18
20
|
import { filterRootHTML } from './filterRootHTML'
|
|
@@ -626,6 +628,22 @@ class RouteErrorBoundary extends React.Component<
|
|
|
626
628
|
}\n\n${error.stack}\n\nComponent Stack:\n${errorInfo.componentStack}`
|
|
627
629
|
)
|
|
628
630
|
this.setState({ errorInfo })
|
|
631
|
+
|
|
632
|
+
// skew protection: chunk-load errors at the route level are unambiguous,
|
|
633
|
+
// reload immediately. for any other render error, do a one-shot version
|
|
634
|
+
// check and only reload if the deployed build actually changed. genuine
|
|
635
|
+
// bugs fall through to the route error UI.
|
|
636
|
+
if (
|
|
637
|
+
process.env.TAMAGUI_TARGET !== 'native' &&
|
|
638
|
+
process.env.NODE_ENV === 'production' &&
|
|
639
|
+
process.env.ONE_SKEW_PROTECTION !== 'false'
|
|
640
|
+
) {
|
|
641
|
+
if (isChunkLoadError(error)) {
|
|
642
|
+
handleSkewError()
|
|
643
|
+
} else {
|
|
644
|
+
checkSkewAndReload()
|
|
645
|
+
}
|
|
646
|
+
}
|
|
629
647
|
}
|
|
630
648
|
|
|
631
649
|
clearError() {
|
package/src/serve-worker.ts
CHANGED
|
@@ -14,8 +14,10 @@ export type { LazyRoutes }
|
|
|
14
14
|
* Creates a fetch handler for edge/worker environments (Cloudflare Workers, service workers, etc.)
|
|
15
15
|
* No Hono dependency — routes are matched dynamically via compiled regexes against a mutable table.
|
|
16
16
|
*
|
|
17
|
-
* @returns `{ fetch, updateRoutes }` — call `fetch(request)` to handle requests
|
|
18
|
-
* `
|
|
17
|
+
* @returns `{ fetch, updateRoutes }` — call `fetch(request, env?, ctx?)` to handle requests
|
|
18
|
+
* (`env` and `ctx` are forwarded from the worker's fetch handler and surfaced on
|
|
19
|
+
* API route handler contexts as `{ env, executionCtx }`).
|
|
20
|
+
* Call `updateRoutes(newBuildInfo, newLazyRoutes?)` to hot-swap the route table.
|
|
19
21
|
*/
|
|
20
22
|
export async function serve(buildInfo: One.BuildInfo, lazyRoutes?: LazyRoutes) {
|
|
21
23
|
setupBuildInfo(buildInfo)
|
|
@@ -150,13 +150,10 @@ export function getServerManifest(route: RouteNode): OneRouterServerManifestV1 {
|
|
|
150
150
|
const addedMiddlewares: Record<string, boolean> = {}
|
|
151
151
|
|
|
152
152
|
for (const [path, node] of flat) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
continue
|
|
158
|
-
}
|
|
159
|
-
|
|
153
|
+
// collect middlewares for every route type, not just pages. a middleware
|
|
154
|
+
// nested under api/ (e.g. app/api/admin/_middleware.ts) must still end up
|
|
155
|
+
// in middlewareRoutes so the build pipeline compiles it — otherwise the
|
|
156
|
+
// compiled file never exists and runtime lookups return undefined.
|
|
160
157
|
if (node.middlewares?.length) {
|
|
161
158
|
for (const middleware of node.middlewares) {
|
|
162
159
|
if (!addedMiddlewares[middleware.contextKey]) {
|
|
@@ -166,6 +163,13 @@ export function getServerManifest(route: RouteNode): OneRouterServerManifestV1 {
|
|
|
166
163
|
}
|
|
167
164
|
}
|
|
168
165
|
|
|
166
|
+
if (node.type === 'api') {
|
|
167
|
+
const route = getGeneratedNamedRouteRegex(path, node)
|
|
168
|
+
apiRoutes.push(route)
|
|
169
|
+
allRoutes.push(route)
|
|
170
|
+
continue
|
|
171
|
+
}
|
|
172
|
+
|
|
169
173
|
const route = getGeneratedNamedRouteRegex(path, node)
|
|
170
174
|
pageRoutes.push(route)
|
|
171
175
|
allRoutes.push(route)
|
package/src/server/oneServe.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Hono, MiddlewareHandler } from 'hono'
|
|
2
2
|
import type { BlankEnv } from 'hono/types'
|
|
3
3
|
import { readFile } from 'node:fs/promises'
|
|
4
|
-
import { join
|
|
4
|
+
import { join } from 'node:path'
|
|
5
5
|
import {
|
|
6
6
|
CSS_PRELOAD_JS_POSTFIX,
|
|
7
7
|
LOADER_JS_POSTFIX_UNCACHED,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
} from '../createHandleRequest'
|
|
17
17
|
import type { RenderAppProps } from '../types'
|
|
18
18
|
import { getPathFromLoaderPath } from '../utils/cleanUrl'
|
|
19
|
-
import {
|
|
19
|
+
import { toAbsoluteUrl } from '../utils/toAbsolute'
|
|
20
20
|
import type { One } from '../vite/types'
|
|
21
21
|
import type { RouteInfoCompiled } from './createRoutesManifest'
|
|
22
22
|
import { setSSRLoaderData } from './ssrLoaderData'
|
|
@@ -175,8 +175,8 @@ export async function oneServe(
|
|
|
175
175
|
routeExported = lazyKey
|
|
176
176
|
? options?.lazyRoutes?.pages?.[lazyKey]
|
|
177
177
|
? await options.lazyRoutes.pages[lazyKey]()
|
|
178
|
-
: await import(
|
|
179
|
-
: await import(
|
|
178
|
+
: await import(toAbsoluteUrl(resolvedPath))
|
|
179
|
+
: await import(toAbsoluteUrl(serverPath!))
|
|
180
180
|
moduleImportCache.set(cacheKey, routeExported)
|
|
181
181
|
}
|
|
182
182
|
|
|
@@ -279,8 +279,7 @@ export async function oneServe(
|
|
|
279
279
|
const entry = options?.lazyRoutes?.serverEntry
|
|
280
280
|
? await options.lazyRoutes.serverEntry()
|
|
281
281
|
: await import(
|
|
282
|
-
|
|
283
|
-
process.cwd(),
|
|
282
|
+
toAbsoluteUrl(
|
|
284
283
|
`${serverOptions.root}/${outDir}/server/_virtual_one-entry.${typeof oneOptions.build?.server === 'object' && oneOptions.build.server.outputFormat === 'cjs' ? 'c' : ''}js`
|
|
285
284
|
)
|
|
286
285
|
)
|
|
@@ -315,13 +314,8 @@ export async function oneServe(
|
|
|
315
314
|
}
|
|
316
315
|
// both vite and rolldown-vite replace brackets with underscores in output filenames
|
|
317
316
|
const fileName = route.page.slice(1).replace(/\[/g, '_').replace(/\]/g, '_')
|
|
318
|
-
const apiFile = join(
|
|
319
|
-
|
|
320
|
-
outDir,
|
|
321
|
-
'api',
|
|
322
|
-
fileName + (apiCJS ? '.cjs' : '.js')
|
|
323
|
-
)
|
|
324
|
-
return await import(apiFile)
|
|
317
|
+
const apiFile = join(outDir, 'api', fileName + (apiCJS ? '.cjs' : '.js'))
|
|
318
|
+
return await import(toAbsoluteUrl(apiFile))
|
|
325
319
|
},
|
|
326
320
|
|
|
327
321
|
async loadMiddleware(route) {
|
|
@@ -329,7 +323,7 @@ export async function oneServe(
|
|
|
329
323
|
if (options?.lazyRoutes?.middlewares?.[route.contextKey]) {
|
|
330
324
|
return await options.lazyRoutes.middlewares[route.contextKey]()
|
|
331
325
|
}
|
|
332
|
-
return await import(
|
|
326
|
+
return await import(toAbsoluteUrl(route.contextKey))
|
|
333
327
|
},
|
|
334
328
|
|
|
335
329
|
async handleLoader({ route, loaderProps }) {
|
|
@@ -916,17 +910,21 @@ url: ${url}`)
|
|
|
916
910
|
|
|
917
911
|
for (const route of compiledManifest.apiRoutes) {
|
|
918
912
|
app.get(route.urlPath, createHonoHandler(route))
|
|
913
|
+
app.on('HEAD', route.urlPath, createHonoHandler(route))
|
|
919
914
|
app.put(route.urlPath, createHonoHandler(route))
|
|
920
915
|
app.post(route.urlPath, createHonoHandler(route))
|
|
921
916
|
app.delete(route.urlPath, createHonoHandler(route))
|
|
922
917
|
app.patch(route.urlPath, createHonoHandler(route))
|
|
918
|
+
app.options(route.urlPath, createHonoHandler(route))
|
|
923
919
|
|
|
924
920
|
if (route.urlPath !== route.urlCleanPath) {
|
|
925
921
|
app.get(route.urlCleanPath, createHonoHandler(route))
|
|
922
|
+
app.on('HEAD', route.urlCleanPath, createHonoHandler(route))
|
|
926
923
|
app.put(route.urlCleanPath, createHonoHandler(route))
|
|
927
924
|
app.post(route.urlCleanPath, createHonoHandler(route))
|
|
928
925
|
app.delete(route.urlCleanPath, createHonoHandler(route))
|
|
929
926
|
app.patch(route.urlCleanPath, createHonoHandler(route))
|
|
927
|
+
app.options(route.urlCleanPath, createHonoHandler(route))
|
|
930
928
|
}
|
|
931
929
|
}
|
|
932
930
|
|
|
@@ -566,7 +566,11 @@ export function createWorkerHandler(options: WorkerHandlerOptions) {
|
|
|
566
566
|
}
|
|
567
567
|
|
|
568
568
|
// the main fetch handler - matches request to route and dispatches
|
|
569
|
-
async function handleRequest(
|
|
569
|
+
async function handleRequest(
|
|
570
|
+
request: Request,
|
|
571
|
+
env?: unknown,
|
|
572
|
+
executionCtx?: unknown
|
|
573
|
+
): Promise<Response | null> {
|
|
570
574
|
const url = getURLfromRequestURL(request)
|
|
571
575
|
const pathname = url.pathname
|
|
572
576
|
const method = request.method
|
|
@@ -683,7 +687,14 @@ export function createWorkerHandler(options: WorkerHandlerOptions) {
|
|
|
683
687
|
if (route.compiledRegex.test(pathname)) {
|
|
684
688
|
if (debugRouter)
|
|
685
689
|
console.info(`[one] ⚡ ${pathname} → matched API route: ${route.page}`)
|
|
686
|
-
const response = await resolveAPIRoute(
|
|
690
|
+
const response = await resolveAPIRoute(
|
|
691
|
+
requestHandlers,
|
|
692
|
+
request,
|
|
693
|
+
url,
|
|
694
|
+
route,
|
|
695
|
+
env,
|
|
696
|
+
executionCtx
|
|
697
|
+
)
|
|
687
698
|
if (response && isResponse(response)) {
|
|
688
699
|
return setCacheHeaders(response, route, true)
|
|
689
700
|
}
|
package/src/skewProtection.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { CACHE_KEY } from './constants'
|
|
2
2
|
import { getURL } from './getURL'
|
|
3
|
+
import { handleSkewError } from './utils/dynamicImport'
|
|
3
4
|
|
|
4
5
|
let stale = false
|
|
5
6
|
let polling: ReturnType<typeof setTimeout> | null = null
|
|
7
|
+
let inFlightCheck: Promise<boolean> | null = null
|
|
6
8
|
|
|
7
9
|
export function isVersionStale() {
|
|
8
10
|
if (stale) return true
|
|
@@ -11,6 +13,48 @@ export function isVersionStale() {
|
|
|
11
13
|
return false
|
|
12
14
|
}
|
|
13
15
|
|
|
16
|
+
function markStale(version?: string) {
|
|
17
|
+
stale = true
|
|
18
|
+
if (typeof window !== 'undefined') {
|
|
19
|
+
;(window as any).__oneVersionStale = true
|
|
20
|
+
window.dispatchEvent(new CustomEvent('one-version-update', { detail: { version } }))
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// one-shot version check for use when something already looks wrong
|
|
25
|
+
// (e.g. an error reached the root boundary). reloads if the deployed
|
|
26
|
+
// version no longer matches our build, otherwise leaves the error UI alone.
|
|
27
|
+
// dedupes concurrent calls so a render-loop crash only fetches once.
|
|
28
|
+
export function checkSkewAndReload(): Promise<boolean> {
|
|
29
|
+
if (typeof window === 'undefined') return Promise.resolve(false)
|
|
30
|
+
if (process.env.ONE_SKEW_PROTECTION === 'false') return Promise.resolve(false)
|
|
31
|
+
if (isVersionStale()) {
|
|
32
|
+
handleSkewError()
|
|
33
|
+
return Promise.resolve(true)
|
|
34
|
+
}
|
|
35
|
+
if (inFlightCheck) return inFlightCheck
|
|
36
|
+
inFlightCheck = (async () => {
|
|
37
|
+
try {
|
|
38
|
+
const res = await fetch(`${getURL()}/version.json`, {
|
|
39
|
+
headers: { 'cache-control': 'no-cache', pragma: 'no-cache' },
|
|
40
|
+
})
|
|
41
|
+
if (!res.ok) return false
|
|
42
|
+
const data = await res.json()
|
|
43
|
+
if (data.version && data.version !== CACHE_KEY) {
|
|
44
|
+
markStale(data.version)
|
|
45
|
+
handleSkewError()
|
|
46
|
+
return true
|
|
47
|
+
}
|
|
48
|
+
return false
|
|
49
|
+
} catch {
|
|
50
|
+
return false
|
|
51
|
+
} finally {
|
|
52
|
+
inFlightCheck = null
|
|
53
|
+
}
|
|
54
|
+
})()
|
|
55
|
+
return inFlightCheck
|
|
56
|
+
}
|
|
57
|
+
|
|
14
58
|
export function setupSkewProtection() {
|
|
15
59
|
if (typeof window === 'undefined') return
|
|
16
60
|
if (process.env.NODE_ENV === 'development') return
|
|
@@ -31,11 +75,7 @@ export function setupSkewProtection() {
|
|
|
31
75
|
}
|
|
32
76
|
const data = await res.json()
|
|
33
77
|
if (data.version !== CACHE_KEY) {
|
|
34
|
-
|
|
35
|
-
;(window as any).__oneVersionStale = true
|
|
36
|
-
window.dispatchEvent(
|
|
37
|
-
new CustomEvent('one-version-update', { detail: { version: data.version } })
|
|
38
|
-
)
|
|
78
|
+
markStale(data.version)
|
|
39
79
|
// stop polling once stale detected
|
|
40
80
|
return
|
|
41
81
|
}
|
package/src/useLoader.ts
CHANGED
|
@@ -517,10 +517,12 @@ export function useLoaderState<
|
|
|
517
517
|
} finally {
|
|
518
518
|
clearTimeout(timeoutId)
|
|
519
519
|
}
|
|
520
|
-
//
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
520
|
+
// use Function constructor instead of direct eval. hermes treats
|
|
521
|
+
// direct eval as indirect (no lexical scope) and rolldown warns
|
|
522
|
+
// about it; new Function is predictable across both engines and
|
|
523
|
+
// explicitly threads the exports object through as a parameter.
|
|
524
|
+
const result: { loader?: () => any } = {}
|
|
525
|
+
new Function('exports', loaderJsCode)(result)
|
|
524
526
|
const moduleLoadTime = performance.now() - moduleLoadStart
|
|
525
527
|
|
|
526
528
|
if (typeof result.loader !== 'function') {
|
|
@@ -28,12 +28,12 @@ const CHUNK_ERROR_PATTERNS = [
|
|
|
28
28
|
'Importing a module script failed', // safari
|
|
29
29
|
]
|
|
30
30
|
|
|
31
|
-
function isChunkLoadError(err: unknown): boolean {
|
|
31
|
+
export function isChunkLoadError(err: unknown): boolean {
|
|
32
32
|
const msg = err instanceof Error ? err.message : String(err)
|
|
33
33
|
return CHUNK_ERROR_PATTERNS.some((p) => msg.includes(p))
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
function handleSkewError() {
|
|
36
|
+
export function handleSkewError() {
|
|
37
37
|
if (typeof window === 'undefined') return
|
|
38
38
|
const key = '__one_skew_reload'
|
|
39
39
|
const last = sessionStorage.getItem(key)
|
package/src/utils/toAbsolute.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
import { resolve } from 'node:path'
|
|
2
|
+
import { pathToFileURL } from 'node:url'
|
|
2
3
|
|
|
4
|
+
/** Resolve to native filesystem path — for fs operations (readFile, writeFile, join). */
|
|
3
5
|
export const toAbsolute = (p: string) => resolve(process.cwd(), p)
|
|
6
|
+
|
|
7
|
+
/** Resolve to file:// URL — for dynamic import() which requires URLs on Windows. */
|
|
8
|
+
export const toAbsoluteUrl = (p: string) => pathToFileURL(resolve(process.cwd(), p)).href
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Usage: Pass the caller's import.meta.url to resolve relative paths correctly.
|
|
6
6
|
*/
|
|
7
|
-
import { fileURLToPath } from 'node:url'
|
|
7
|
+
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
8
8
|
import { dirname, resolve } from 'node:path'
|
|
9
9
|
|
|
10
10
|
type ModuleCache = Map<string, any>
|
|
@@ -33,7 +33,7 @@ export async function workerImport<T = any>(
|
|
|
33
33
|
const mjsPath = absolutePath.endsWith('.mjs') ? absolutePath : `${absolutePath}.mjs`
|
|
34
34
|
|
|
35
35
|
// @ts-ignore - runtime needs .mjs extension for proper ESM resolution
|
|
36
|
-
const mod = await import(mjsPath)
|
|
36
|
+
const mod = await import(pathToFileURL(mjsPath).href)
|
|
37
37
|
cache.set(cacheKey, mod)
|
|
38
38
|
return mod
|
|
39
39
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { Platform, Text, View } from 'react-native'
|
|
3
|
+
import { checkSkewAndReload } from '../skewProtection'
|
|
4
|
+
import { handleSkewError, isChunkLoadError } from '../utils/dynamicImport'
|
|
3
5
|
|
|
4
6
|
type RootErrorBoundaryState = {
|
|
5
7
|
hasError: boolean
|
|
@@ -28,6 +30,22 @@ export class RootErrorBoundary extends React.Component<
|
|
|
28
30
|
`[One] Root error boundary caught error:\n${printError(error)}\n${info.componentStack}`
|
|
29
31
|
)
|
|
30
32
|
|
|
33
|
+
// skew protection: chunk-load errors are unambiguous → reload immediately.
|
|
34
|
+
// for any other render error, do a one-shot version check and only reload
|
|
35
|
+
// if the deployed build actually changed. genuine bugs fall through to the
|
|
36
|
+
// fallback UI below.
|
|
37
|
+
if (
|
|
38
|
+
process.env.TAMAGUI_TARGET !== 'native' &&
|
|
39
|
+
process.env.NODE_ENV === 'production' &&
|
|
40
|
+
process.env.ONE_SKEW_PROTECTION !== 'false'
|
|
41
|
+
) {
|
|
42
|
+
if (isChunkLoadError(error)) {
|
|
43
|
+
handleSkewError()
|
|
44
|
+
} else {
|
|
45
|
+
checkSkewAndReload()
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
31
49
|
// Dispatch error event for devtools (web only - CustomEvent doesn't exist on native)
|
|
32
50
|
if (typeof window !== 'undefined' && typeof CustomEvent !== 'undefined') {
|
|
33
51
|
window.dispatchEvent(
|
package/src/views/Try.tsx
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
+
import { checkSkewAndReload } from '../skewProtection'
|
|
3
|
+
import { handleSkewError, isChunkLoadError } from '../utils/dynamicImport'
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Route context information passed to error boundaries.
|
|
@@ -65,6 +67,22 @@ export class Try extends React.Component<TryProps, TryState> {
|
|
|
65
67
|
)
|
|
66
68
|
}
|
|
67
69
|
|
|
70
|
+
// skew protection: chunk-load errors at the route level are unambiguous,
|
|
71
|
+
// reload immediately. for any other render error, do a one-shot version
|
|
72
|
+
// check and only reload if the deployed build actually changed. genuine
|
|
73
|
+
// bugs fall through to the route's own ErrorBoundary UI below.
|
|
74
|
+
if (
|
|
75
|
+
process.env.TAMAGUI_TARGET !== 'native' &&
|
|
76
|
+
process.env.NODE_ENV === 'production' &&
|
|
77
|
+
process.env.ONE_SKEW_PROTECTION !== 'false'
|
|
78
|
+
) {
|
|
79
|
+
if (isChunkLoadError(error)) {
|
|
80
|
+
handleSkewError()
|
|
81
|
+
} else {
|
|
82
|
+
checkSkewAndReload()
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
68
86
|
// Dispatch error event for devtools integration (web only - CustomEvent doesn't exist on native)
|
|
69
87
|
if (typeof window !== 'undefined' && typeof CustomEvent !== 'undefined') {
|
|
70
88
|
window.dispatchEvent(
|