one 2.7.7 → 2.8.1
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/One.podspec +3 -0
- package/android/build.gradle +8 -1
- package/android/consumer-rules.pro +6 -0
- package/dist/cjs/cli/build.cjs +2 -2
- package/dist/cjs/cli/build.native.cjs +2 -2
- package/dist/cjs/cli/build.native.js +2 -2
- package/dist/cjs/cli/build.native.js.map +1 -1
- package/dist/cjs/cli/buildPage.cjs +1 -1
- package/dist/cjs/cli/buildPage.native.cjs +1 -1
- package/dist/cjs/cli/buildPage.native.js +1 -1
- package/dist/cjs/cli/buildPage.native.js.map +1 -1
- package/dist/cjs/constants.cjs +2 -0
- package/dist/cjs/constants.native.cjs +2 -0
- package/dist/cjs/constants.native.js +2 -0
- package/dist/cjs/constants.native.js.map +1 -1
- package/dist/cjs/fork/useLinking.cjs +1 -1
- package/dist/cjs/layouts/Stack.cjs +2 -2
- package/dist/cjs/layouts/Stack.native.cjs +3 -3
- package/dist/cjs/layouts/Stack.native.js +3 -3
- package/dist/cjs/layouts/Stack.native.js.map +1 -1
- package/dist/cjs/layouts/stack-navigator.cjs +5 -2
- package/dist/cjs/layouts/stack-navigator.native.cjs +14 -2
- package/dist/cjs/layouts/stack-navigator.native.js +14 -2
- package/dist/cjs/layouts/stack-navigator.native.js.map +1 -1
- package/dist/cjs/metro-config/buildOneMetroResolverOverrides.cjs +1 -1
- package/dist/cjs/metro-config/buildOneMetroResolverOverrides.native.cjs +1 -1
- package/dist/cjs/metro-config/buildOneMetroResolverOverrides.native.js +1 -1
- package/dist/cjs/metro-config/buildOneMetroResolverOverrides.native.js.map +1 -1
- package/dist/cjs/metro-config/getViteMetroPluginOptions.cjs +10 -4
- package/dist/cjs/metro-config/getViteMetroPluginOptions.native.cjs +10 -4
- package/dist/cjs/metro-config/getViteMetroPluginOptions.native.js +10 -4
- package/dist/cjs/metro-config/getViteMetroPluginOptions.native.js.map +1 -1
- package/dist/cjs/metro-config/withOne.cjs +2 -2
- package/dist/cjs/metro-config/withOne.native.cjs +2 -2
- package/dist/cjs/metro-config/withOne.native.js +2 -2
- package/dist/cjs/metro-config/withOne.native.js.map +1 -1
- package/dist/cjs/router/linkingConfig.cjs +3 -3
- package/dist/cjs/router/linkingConfig.native.cjs +3 -3
- package/dist/cjs/router/linkingConfig.native.js +3 -3
- package/dist/cjs/router/linkingConfig.native.js.map +1 -1
- package/dist/cjs/useLoader.cjs +0 -2
- package/dist/cjs/useLoader.native.cjs +1 -3
- package/dist/cjs/useLoader.native.js +1 -3
- package/dist/cjs/useLoader.native.js.map +1 -1
- package/dist/cjs/utils/cleanUrl.cjs +8 -3
- package/dist/cjs/utils/cleanUrl.native.cjs +8 -3
- package/dist/cjs/utils/cleanUrl.native.js +8 -3
- package/dist/cjs/utils/cleanUrl.native.js.map +1 -1
- package/dist/cjs/vite/one.cjs +5 -5
- package/dist/cjs/vite/one.native.cjs +5 -5
- package/dist/cjs/vite/one.native.js +5 -5
- package/dist/cjs/vite/one.native.js.map +1 -1
- package/dist/cjs/vite/plugins/workerdDevPlugin.cjs +3 -3
- package/dist/cjs/vite/plugins/workerdDevPlugin.native.cjs +3 -3
- package/dist/cjs/vite/plugins/workerdDevPlugin.native.js +3 -3
- package/dist/cjs/vite/plugins/workerdDevPlugin.native.js.map +1 -1
- package/dist/esm/cli/build.mjs +2 -2
- package/dist/esm/cli/build.mjs.map +1 -1
- package/dist/esm/cli/build.native.js +2 -2
- package/dist/esm/cli/build.native.js.map +1 -1
- package/dist/esm/cli/buildPage.mjs +2 -2
- package/dist/esm/cli/buildPage.mjs.map +1 -1
- package/dist/esm/cli/buildPage.native.js +2 -2
- 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/fork/useLinking.mjs +1 -1
- package/dist/esm/fork/useLinking.mjs.map +1 -1
- package/dist/esm/layouts/Stack.mjs +2 -2
- package/dist/esm/layouts/Stack.mjs.map +1 -1
- package/dist/esm/layouts/Stack.native.js +3 -3
- package/dist/esm/layouts/Stack.native.js.map +1 -1
- package/dist/esm/layouts/stack-navigator.mjs +5 -2
- package/dist/esm/layouts/stack-navigator.mjs.map +1 -1
- package/dist/esm/layouts/stack-navigator.native.js +14 -2
- package/dist/esm/layouts/stack-navigator.native.js.map +1 -1
- package/dist/esm/metro-config/buildOneMetroResolverOverrides.mjs +1 -1
- package/dist/esm/metro-config/buildOneMetroResolverOverrides.mjs.map +1 -1
- package/dist/esm/metro-config/buildOneMetroResolverOverrides.native.js +1 -1
- package/dist/esm/metro-config/buildOneMetroResolverOverrides.native.js.map +1 -1
- package/dist/esm/metro-config/getViteMetroPluginOptions.mjs +10 -4
- package/dist/esm/metro-config/getViteMetroPluginOptions.mjs.map +1 -1
- package/dist/esm/metro-config/getViteMetroPluginOptions.native.js +10 -4
- package/dist/esm/metro-config/getViteMetroPluginOptions.native.js.map +1 -1
- package/dist/esm/metro-config/withOne.mjs +1 -1
- package/dist/esm/metro-config/withOne.mjs.map +1 -1
- package/dist/esm/metro-config/withOne.native.js +1 -1
- package/dist/esm/metro-config/withOne.native.js.map +1 -1
- package/dist/esm/router/linkingConfig.mjs +3 -3
- package/dist/esm/router/linkingConfig.mjs.map +1 -1
- package/dist/esm/router/linkingConfig.native.js +3 -3
- package/dist/esm/router/linkingConfig.native.js.map +1 -1
- package/dist/esm/useLoader.native.js +2 -4
- package/dist/esm/useLoader.native.js.map +1 -1
- package/dist/esm/utils/cleanUrl.mjs +9 -5
- package/dist/esm/utils/cleanUrl.mjs.map +1 -1
- package/dist/esm/utils/cleanUrl.native.js +9 -5
- package/dist/esm/utils/cleanUrl.native.js.map +1 -1
- package/dist/esm/vite/one.mjs +1 -1
- package/dist/esm/vite/one.mjs.map +1 -1
- package/dist/esm/vite/one.native.js +1 -1
- package/dist/esm/vite/one.native.js.map +1 -1
- package/dist/esm/vite/plugins/workerdDevPlugin.mjs +2 -2
- package/dist/esm/vite/plugins/workerdDevPlugin.mjs.map +1 -1
- package/dist/esm/vite/plugins/workerdDevPlugin.native.js +2 -2
- package/dist/esm/vite/plugins/workerdDevPlugin.native.js.map +1 -1
- package/ios/PrivacyInfo.xcprivacy +52 -0
- package/metro-entry.js +1 -1
- package/package.json +79 -42
- package/src/__tests__/polyfills-mobile-metro.test.ts +2 -2
- package/src/cli/build.ts +2 -2
- package/src/cli/buildPage.ts +5 -4
- package/src/constants.ts +2 -0
- package/src/fork/useLinking.ts +4 -1
- package/src/layouts/Stack.tsx +5 -2
- package/src/layouts/stack-navigator.tsx +34 -0
- package/src/layouts/stack-navigator.web.ts +5 -2
- package/src/metro-config/buildOneMetroResolverOverrides.ts +1 -1
- package/src/metro-config/getViteMetroPluginOptions.integration.test.ts +5 -3
- package/src/metro-config/getViteMetroPluginOptions.ts +10 -4
- package/src/metro-config/withOne.ts +3 -1
- package/src/native/packedClosure.test.ts +68 -6
- package/src/native/shippedAndroid.test.ts +21 -0
- package/src/native/shippedArtifacts.test.ts +81 -0
- package/src/router/linkingConfig.test.ts +144 -2
- package/src/router/linkingConfig.ts +6 -3
- package/src/useLoader.ts +3 -15
- package/src/utils/cleanUrl.test.ts +28 -0
- package/src/utils/cleanUrl.ts +11 -4
- package/src/vite/one.ts +2 -1
- package/src/vite/plugins/workerdDevPlugin.ts +2 -2
- package/src/vite/types.ts +4 -4
- package/types/Frozen.d.ts +1 -1
- package/types/Frozen.d.ts.map +1 -1
- package/types/Frozen.native.d.ts +1 -1
- package/types/Frozen.native.d.ts.map +1 -1
- package/types/Root.d.ts +1 -1
- package/types/Root.d.ts.map +1 -1
- package/types/cli/buildPage.d.ts.map +1 -1
- package/types/constants.d.ts +1 -0
- package/types/constants.d.ts.map +1 -1
- package/types/fallbackViews/Sitemap.d.ts +1 -1
- package/types/fallbackViews/Sitemap.d.ts.map +1 -1
- package/types/fallbackViews/Sitemap.web.d.ts +2 -1
- package/types/fallbackViews/Sitemap.web.d.ts.map +1 -1
- package/types/fallbackViews/Unmatched.d.ts +2 -1
- package/types/fallbackViews/Unmatched.d.ts.map +1 -1
- package/types/fallbackViews/Unmatched.web.d.ts +2 -1
- package/types/fallbackViews/Unmatched.web.d.ts.map +1 -1
- package/types/fork/useLinking.d.ts.map +1 -1
- package/types/headless/Presentations.d.ts +1 -1
- package/types/headless/Presentations.d.ts.map +1 -1
- package/types/headless/keepMounted.d.ts +1 -1
- package/types/headless/keepMounted.d.ts.map +1 -1
- package/types/headless/useDrawer.d.ts +1 -1
- package/types/headless/useDrawer.d.ts.map +1 -1
- package/types/layouts/Stack.d.ts.map +1 -1
- package/types/layouts/stack-navigator.d.ts +6 -2
- package/types/layouts/stack-navigator.d.ts.map +1 -1
- package/types/layouts/stack-navigator.web.d.ts +3 -4
- package/types/layouts/stack-navigator.web.d.ts.map +1 -1
- package/types/layouts/stack-utils/StackScreen.d.ts +1 -1
- package/types/layouts/stack-utils/StackScreen.d.ts.map +1 -1
- package/types/layouts/stack-utils/StackToolbar.d.ts +1 -1
- package/types/layouts/stack-utils/StackToolbar.d.ts.map +1 -1
- package/types/layouts/stack-utils/StackToolbarBottomHost.d.ts +1 -1
- package/types/layouts/stack-utils/StackToolbarBottomHost.d.ts.map +1 -1
- package/types/metro-config/getViteMetroPluginOptions.d.ts.map +1 -1
- package/types/metro-config/withOne.d.ts.map +1 -1
- package/types/platform/AndroidMenu.d.ts +2 -2
- package/types/platform/AndroidMenu.d.ts.map +1 -1
- package/types/platform/Animators.native.d.ts +2 -2
- package/types/platform/Animators.native.d.ts.map +1 -1
- package/types/platform/ArrangementView.d.ts +1 -1
- package/types/platform/ArrangementView.d.ts.map +1 -1
- package/types/platform/ArrangementView.native.d.ts +1 -1
- package/types/platform/ArrangementView.native.d.ts.map +1 -1
- package/types/platform/Containers.native.d.ts +34 -34
- package/types/platform/Containers.native.d.ts.map +1 -1
- package/types/platform/Menu.native.d.ts +2 -2
- package/types/platform/Menu.native.d.ts.map +1 -1
- package/types/platform/NavigationSplitView.native.d.ts +1 -1
- package/types/platform/NavigationSplitView.native.d.ts.map +1 -1
- package/types/platform/NavigationStack.native.d.ts +2 -2
- package/types/platform/NavigationStack.native.d.ts.map +1 -1
- package/types/platform/Pager.native.d.ts +1 -1
- package/types/platform/Pager.native.d.ts.map +1 -1
- package/types/platform/Popover.native.d.ts +1 -1
- package/types/platform/Popover.native.d.ts.map +1 -1
- package/types/platform/Sheet.native.d.ts +2 -2
- package/types/platform/Sheet.native.d.ts.map +1 -1
- package/types/platform/Tabs.native.d.ts +1 -1
- package/types/platform/Tabs.native.d.ts.map +1 -1
- package/types/platform/adaptive/ReservedRegions.d.ts +1 -1
- package/types/platform/adaptive/ReservedRegions.d.ts.map +1 -1
- package/types/platform/adaptive/ReservedRegions.native.d.ts +1 -1
- package/types/platform/adaptive/ReservedRegions.native.d.ts.map +1 -1
- package/types/platform/compose.android.d.ts +101 -101
- package/types/platform/compose.android.d.ts.map +1 -1
- package/types/platform/effects/Blur.d.ts +1 -1
- package/types/platform/effects/Blur.d.ts.map +1 -1
- package/types/platform/effects/Blur.native.d.ts +1 -1
- package/types/platform/effects/Blur.native.d.ts.map +1 -1
- package/types/platform/effects/EdgeFade.d.ts +1 -1
- package/types/platform/effects/EdgeFade.d.ts.map +1 -1
- package/types/platform/effects/EdgeFade.native.d.ts +1 -1
- package/types/platform/effects/EdgeFade.native.d.ts.map +1 -1
- package/types/platform/effects/Mask.d.ts +1 -1
- package/types/platform/effects/Mask.d.ts.map +1 -1
- package/types/platform/effects/Mask.native.d.ts +1 -1
- package/types/platform/effects/Mask.native.d.ts.map +1 -1
- package/types/platform/generated/Controls.native.d.ts +55 -55
- package/types/platform/generated/Controls.native.d.ts.map +1 -1
- package/types/platform/index.native.d.ts +58 -58
- package/types/platform/menu/index.native.d.ts +1 -1
- package/types/platform/menu/index.native.d.ts.map +1 -1
- package/types/platform/native-source/view.native.d.ts +1 -1
- package/types/platform/native-source/view.native.d.ts.map +1 -1
- package/types/platform/split-view/split-view.d.ts +2 -2
- package/types/platform/split-view/split-view.d.ts.map +1 -1
- package/types/platform/split-view/split-view.native.d.ts +3 -3
- package/types/platform/split-view/split-view.native.d.ts.map +1 -1
- package/types/platform/swift/index.native.d.ts +1 -1
- package/types/platform/swift/index.native.d.ts.map +1 -1
- package/types/platform/toolbar/index.native.d.ts +2 -2
- package/types/platform/toolbar/index.native.d.ts.map +1 -1
- package/types/platform/ui/Icon.android.d.ts +1 -1
- package/types/platform/ui/Icon.android.d.ts.map +1 -1
- package/types/platform/ui/Image.d.ts +1 -1
- package/types/platform/ui/Image.d.ts.map +1 -1
- package/types/platform/ui/Image.native.d.ts +1 -1
- package/types/platform/ui/Image.native.d.ts.map +1 -1
- package/types/platform/ui/Map.android.d.ts +1 -1
- package/types/platform/ui/Map.android.d.ts.map +1 -1
- package/types/platform/ui/Map.ios.d.ts +1 -1
- package/types/platform/ui/Map.ios.d.ts.map +1 -1
- package/types/platform/ui/PictureInPicture.d.ts +1 -1
- package/types/platform/ui/PictureInPicture.d.ts.map +1 -1
- package/types/platform/ui/PictureInPicture.native.d.ts +1 -1
- package/types/platform/ui/PictureInPicture.native.d.ts.map +1 -1
- package/types/platform/ui/Portal.d.ts +2 -2
- package/types/platform/ui/Portal.d.ts.map +1 -1
- package/types/platform/ui/Portal.native.d.ts +2 -2
- package/types/platform/ui/Portal.native.d.ts.map +1 -1
- package/types/platform/universal/TextInput/index.android.d.ts +1 -1
- package/types/platform/universal/TextInput/index.android.d.ts.map +1 -1
- package/types/platform/universal/TextInput/index.d.ts +1 -1
- package/types/platform/universal/TextInput/index.d.ts.map +1 -1
- package/types/platform/universal/TextInput/index.ios.d.ts +1 -1
- package/types/platform/universal/TextInput/index.ios.d.ts.map +1 -1
- package/types/platform/web/DomView.d.ts +1 -1
- package/types/platform/web/DomView.d.ts.map +1 -1
- package/types/platform/zoom/index.native.d.ts +3 -3
- package/types/platform/zoom/index.native.d.ts.map +1 -1
- package/types/router/Route.d.ts +1 -1
- package/types/router/Route.d.ts.map +1 -1
- package/types/router/RouteInfoContext.d.ts +1 -1
- package/types/router/RouteInfoContext.d.ts.map +1 -1
- package/types/router/linkingConfig.d.ts.map +1 -1
- package/types/router/web/WebStackView.d.ts +1 -1
- package/types/router/web/WebStackView.d.ts.map +1 -1
- package/types/safe-area-context/NativeSafeAreaProvider.d.ts +1 -1
- package/types/safe-area-context/NativeSafeAreaProvider.d.ts.map +1 -1
- package/types/safe-area-context/NativeSafeAreaProvider.native.d.ts +2 -1
- package/types/safe-area-context/NativeSafeAreaProvider.native.d.ts.map +1 -1
- package/types/safe-area-context/SafeAreaContext.d.ts +1 -1
- package/types/safe-area-context/SafeAreaContext.d.ts.map +1 -1
- package/types/server/ServerContextScript.d.ts +1 -1
- package/types/server/ServerContextScript.d.ts.map +1 -1
- package/types/ui/TabList.d.ts +1 -1
- package/types/ui/TabList.d.ts.map +1 -1
- package/types/ui/TabSlot.d.ts +3 -3
- package/types/ui/TabSlot.d.ts.map +1 -1
- package/types/ui/TabTrigger.d.ts +1 -1
- package/types/ui/TabTrigger.d.ts.map +1 -1
- package/types/ui/Tabs.d.ts +1 -1
- package/types/ui/Tabs.d.ts.map +1 -1
- package/types/useLoader.d.ts.map +1 -1
- package/types/utils/cleanUrl.d.ts +1 -0
- package/types/utils/cleanUrl.d.ts.map +1 -1
- package/types/views/ErrorBoundary.d.ts +2 -1
- package/types/views/ErrorBoundary.d.ts.map +1 -1
- package/types/views/ErrorBoundary.web.d.ts +2 -1
- package/types/views/ErrorBoundary.web.d.ts.map +1 -1
- package/types/views/LoadProgressBar.d.ts +1 -1
- package/types/views/LoadProgressBar.d.ts.map +1 -1
- package/types/views/LoadProgressBar.web.d.ts +2 -1
- package/types/views/LoadProgressBar.web.d.ts.map +1 -1
- package/types/views/Navigator.d.ts +6 -6
- package/types/views/Navigator.d.ts.map +1 -1
- package/types/views/RootErrorBoundary.d.ts +1 -1
- package/types/views/RootErrorBoundary.d.ts.map +1 -1
- package/types/views/RootErrorBoundary.web.d.ts +1 -1
- package/types/views/RootErrorBoundary.web.d.ts.map +1 -1
- package/types/views/RouteErrorView.d.ts +1 -1
- package/types/views/RouteErrorView.d.ts.map +1 -1
- package/types/views/RouteErrorView.web.d.ts +1 -1
- package/types/views/RouteErrorView.web.d.ts.map +1 -1
- package/types/views/Try.d.ts +1 -1
- package/types/views/Try.d.ts.map +1 -1
- package/types/vite/DevHead.d.ts +1 -1
- package/types/vite/DevHead.d.ts.map +1 -1
- package/types/vite/one.d.ts.map +1 -1
- package/types/vite/types.d.ts +4 -4
- package/src/layouts/stack-navigator.ts +0 -13
|
@@ -5,7 +5,10 @@ import { createWebStackNavigator } from '../router/web/WebStackNavigator'
|
|
|
5
5
|
|
|
6
6
|
export const createStackNavigator = createWebStackNavigator
|
|
7
7
|
|
|
8
|
-
export function getStackNavigatorProps(children: ReactNode) {
|
|
8
|
+
export function getStackNavigatorProps(children: ReactNode, screenLayout?: unknown) {
|
|
9
9
|
const headlessChildren = getCustomNavigatorChildren(children)
|
|
10
|
-
return
|
|
10
|
+
return {
|
|
11
|
+
...(screenLayout ? { screenLayout } : null),
|
|
12
|
+
...(headlessChildren.length ? { headlessChildren } : null),
|
|
13
|
+
}
|
|
11
14
|
}
|
|
@@ -27,7 +27,7 @@ export function buildOneMetroResolverOverrides({
|
|
|
27
27
|
defaultConfig: T
|
|
28
28
|
) => T {
|
|
29
29
|
const require = module.createRequire(projectRoot + '/')
|
|
30
|
-
const emptyPath = require.resolve('@vxrn/
|
|
30
|
+
const emptyPath = require.resolve('@vxrn/utils/empty', {
|
|
31
31
|
paths: [projectRoot],
|
|
32
32
|
})
|
|
33
33
|
const projectPackagePath = path.join(projectRoot, 'package.json')
|
|
@@ -15,7 +15,7 @@ describe('getViteMetroPluginOptions babel-config coexistence', () => {
|
|
|
15
15
|
|
|
16
16
|
beforeAll(() => {
|
|
17
17
|
// place the fixture under the workspace root so Node can walk up to
|
|
18
|
-
// node_modules and resolve `one/metro-entry` + `@vxrn/
|
|
18
|
+
// node_modules and resolve `one/metro-entry` + `@vxrn/utils/empty`
|
|
19
19
|
const workspaceRoot = path.resolve(__dirname, '../../../../')
|
|
20
20
|
tmpDir = fs.mkdtempSync(path.join(workspaceRoot, '.tmp-one-vite-metro-test-'))
|
|
21
21
|
fs.writeFileSync(
|
|
@@ -29,7 +29,7 @@ describe('getViteMetroPluginOptions babel-config coexistence', () => {
|
|
|
29
29
|
fs.rmSync(tmpDir, { recursive: true, force: true })
|
|
30
30
|
})
|
|
31
31
|
|
|
32
|
-
it('explains the missing Metro
|
|
32
|
+
it('explains the missing Metro plugin in an isolated app', () => {
|
|
33
33
|
const isolatedApp = fs.mkdtempSync(path.join(os.tmpdir(), 'one-metro-no-preset-'))
|
|
34
34
|
try {
|
|
35
35
|
expect(() =>
|
|
@@ -37,7 +37,9 @@ describe('getViteMetroPluginOptions babel-config coexistence', () => {
|
|
|
37
37
|
projectRoot: isolatedApp,
|
|
38
38
|
relativeRouterRoot: 'app',
|
|
39
39
|
})
|
|
40
|
-
).toThrow(
|
|
40
|
+
).toThrow(
|
|
41
|
+
/Metro requires @vxrn\/vite-plugin-metro and babel-preset-expo in the app.*Install with/
|
|
42
|
+
)
|
|
41
43
|
} finally {
|
|
42
44
|
fs.rmSync(isolatedApp, { recursive: true, force: true })
|
|
43
45
|
}
|
|
@@ -21,11 +21,17 @@ export function getViteMetroPluginOptions({
|
|
|
21
21
|
setupFile?: string | { native?: string; ios?: string; android?: string }
|
|
22
22
|
}): Parameters<typeof metroPlugin>[0] {
|
|
23
23
|
const require = createRequire(projectRoot + '/')
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
const missing = ['@vxrn/vite-plugin-metro', 'babel-preset-expo'].filter((name) => {
|
|
25
|
+
try {
|
|
26
|
+
require.resolve(name)
|
|
27
|
+
return false
|
|
28
|
+
} catch {
|
|
29
|
+
return true
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
if (missing.length) {
|
|
27
33
|
throw new Error(
|
|
28
|
-
`[one] Metro requires
|
|
34
|
+
`[one] Metro requires ${missing.join(' and ')} in the app at ${projectRoot}. Install with your package manager, or remove native.bundler: 'metro' to use One's default native bundler.`
|
|
29
35
|
)
|
|
30
36
|
}
|
|
31
37
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
|
-
import { buildMetroConfigInputFromViteConfig } from '@vxrn/vite-plugin-metro'
|
|
3
2
|
import { getViteMetroPluginOptions } from './getViteMetroPluginOptions'
|
|
4
3
|
import { loadUserOneOptions } from '../vite/loadConfig'
|
|
5
4
|
|
|
@@ -93,6 +92,9 @@ export async function withOne(
|
|
|
93
92
|
root: path.resolve(loaded?.config?.config?.root ?? projectRoot),
|
|
94
93
|
} as any
|
|
95
94
|
|
|
95
|
+
// imported after getViteMetroPluginOptions so a missing optional peer gets its
|
|
96
|
+
// install message instead of a bare module-not-found
|
|
97
|
+
const { buildMetroConfigInputFromViteConfig } = await import('@vxrn/vite-plugin-metro')
|
|
96
98
|
const { defaultConfig } = await buildMetroConfigInputFromViteConfig(viteConfig, {
|
|
97
99
|
...metroPluginOptions,
|
|
98
100
|
mainModuleName: 'one/metro-entry',
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
writeFileSync,
|
|
14
14
|
} from 'node:fs'
|
|
15
15
|
import { tmpdir } from 'node:os'
|
|
16
|
-
import { dirname, join, resolve } from 'node:path'
|
|
16
|
+
import { dirname, join, relative, resolve } from 'node:path'
|
|
17
17
|
import { fileURLToPath } from 'node:url'
|
|
18
18
|
import { describe, expect, it } from 'vitest'
|
|
19
19
|
import { auditUnpackedManifest, findForbiddenDependencies } from './closure'
|
|
@@ -235,6 +235,15 @@ console.log('one/native ok ' + url);
|
|
|
235
235
|
const appDir = join(tmp, 'app')
|
|
236
236
|
mkdirSync(join(appDir, 'app'), { recursive: true })
|
|
237
237
|
writeFileSync(join(appDir, '.watchmanconfig'), '{}\n')
|
|
238
|
+
// the ejected babel config runs one/babel-preset, whose babel packages are
|
|
239
|
+
// optional peers the app installs (docs/metro-mode)
|
|
240
|
+
const onePeers = JSON.parse(readFileSync(join(oneDir, 'package.json'), 'utf8'))
|
|
241
|
+
.peerDependencies as Record<string, string>
|
|
242
|
+
const babelPresetPeers = Object.fromEntries(
|
|
243
|
+
Object.entries(onePeers).filter(
|
|
244
|
+
([name]) => name.startsWith('@babel/') || name.startsWith('babel-')
|
|
245
|
+
)
|
|
246
|
+
)
|
|
238
247
|
|
|
239
248
|
writeFileSync(
|
|
240
249
|
join(appDir, 'package.json'),
|
|
@@ -247,12 +256,13 @@ console.log('one/native ok ' + url);
|
|
|
247
256
|
vxrn: `file:${packedClosure.vxrn}`,
|
|
248
257
|
'@vxrn/vite-plugin-metro': `file:${packedClosure['@vxrn/vite-plugin-metro']}`,
|
|
249
258
|
'@vxrn/utils': `file:${packedClosure['@vxrn/utils']}`,
|
|
250
|
-
react: '19.
|
|
251
|
-
'react-native': '0.
|
|
259
|
+
react: '19.3.0',
|
|
260
|
+
'react-native': '0.88.0',
|
|
252
261
|
'react-native-web': '^0.21.2',
|
|
253
262
|
vite: '^8.2.2',
|
|
254
|
-
metro: '^0.87.
|
|
255
|
-
'metro-config': '^0.87.
|
|
263
|
+
metro: '^0.87.1',
|
|
264
|
+
'metro-config': '^0.87.1',
|
|
265
|
+
...babelPresetPeers,
|
|
256
266
|
},
|
|
257
267
|
overrides: Object.fromEntries(
|
|
258
268
|
Object.entries(packedClosure).map(([name, tarball]) => [
|
|
@@ -379,7 +389,7 @@ if (!config.dependencies['react-native-nitro-modules']) {
|
|
|
379
389
|
['-e', "require('./metro.config.cjs').catch((error) => { console.error(error.message); process.exitCode = 1 })"],
|
|
380
390
|
{ cwd: appDir, encoding: 'utf8' }
|
|
381
391
|
)
|
|
382
|
-
).toThrow(/Metro requires babel-preset-expo.*Install
|
|
392
|
+
).toThrow(/Metro requires babel-preset-expo in the app.*Install with/)
|
|
383
393
|
|
|
384
394
|
const expoPresetVersion = JSON.parse(
|
|
385
395
|
readFileSync(join(workspaceRoot, '../node_modules/babel-preset-expo/package.json'), 'utf8')
|
|
@@ -440,4 +450,56 @@ if (!config.dependencies['react-native-nitro-modules']) {
|
|
|
440
450
|
process.chdir(previousCwd)
|
|
441
451
|
}
|
|
442
452
|
})
|
|
453
|
+
|
|
454
|
+
it('packs every files entry and non-dist export target of each published package', () => {
|
|
455
|
+
const tmp = realpathSync(mkdtempSync(join(tmpdir(), 'one-packed-files-')))
|
|
456
|
+
// workspaceRoot here is the packages directory (see packWorkspaceClosure).
|
|
457
|
+
for (const entry of readdirSync(workspaceRoot, { withFileTypes: true })) {
|
|
458
|
+
if (!entry.isDirectory()) continue
|
|
459
|
+
const packageDir = join(workspaceRoot, entry.name)
|
|
460
|
+
const manifestPath = join(packageDir, 'package.json')
|
|
461
|
+
if (!existsSync(manifestPath)) continue
|
|
462
|
+
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'))
|
|
463
|
+
if (manifest.private) continue
|
|
464
|
+
const extracted = unpack(packToDir(packageDir, tmp), join(tmp, entry.name))
|
|
465
|
+
const packed: string[] = []
|
|
466
|
+
const collect = (dir: string) => {
|
|
467
|
+
for (const child of readdirSync(dir, { withFileTypes: true })) {
|
|
468
|
+
if (child.isDirectory()) collect(join(dir, child.name))
|
|
469
|
+
else packed.push(relative(extracted, join(dir, child.name)))
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
collect(extracted)
|
|
473
|
+
// npm silently ignores files entries that match nothing, so a stale
|
|
474
|
+
// entry looks packed until something references the missing path.
|
|
475
|
+
for (const pattern of manifest.files ?? []) {
|
|
476
|
+
// dist and types only exist after the release build; everything
|
|
477
|
+
// else must already match at least one packed path.
|
|
478
|
+
if (pattern === 'dist' || pattern === 'types') continue
|
|
479
|
+
expect(
|
|
480
|
+
packed.some((file) => file === pattern || file.startsWith(`${pattern}/`)),
|
|
481
|
+
`${manifest.name}: files entry ${pattern} packs nothing`
|
|
482
|
+
).toBe(true)
|
|
483
|
+
}
|
|
484
|
+
const targets = new Set<string>()
|
|
485
|
+
const walk = (value: unknown) => {
|
|
486
|
+
if (typeof value === 'string' && value.startsWith('./') && !value.includes('*')) {
|
|
487
|
+
if (value !== './') targets.add(value.slice(2))
|
|
488
|
+
} else if (value && typeof value === 'object') {
|
|
489
|
+
// comment keys (// ...) carry placeholder values, not targets.
|
|
490
|
+
Object.entries(value).forEach(([key, entry]) => {
|
|
491
|
+
if (!key.startsWith('//')) walk(entry)
|
|
492
|
+
})
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
walk(manifest.exports)
|
|
496
|
+
walk(manifest.bin)
|
|
497
|
+
for (const target of targets) {
|
|
498
|
+
if (target.startsWith('dist/') || target.startsWith('types/')) continue
|
|
499
|
+
expect(packed, `${manifest.name}: export target ${target} is missing`).toContain(
|
|
500
|
+
target
|
|
501
|
+
)
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
})
|
|
443
505
|
}, 180000)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// guards for what one ships into real android app builds: the native
|
|
2
|
+
// library must stay 16 kb page aligned.
|
|
3
|
+
import { readFileSync } from 'node:fs'
|
|
4
|
+
import { dirname, join, resolve } from 'node:path'
|
|
5
|
+
import { fileURLToPath } from 'node:url'
|
|
6
|
+
import { describe, expect, it } from 'vitest'
|
|
7
|
+
|
|
8
|
+
const here = dirname(fileURLToPath(import.meta.url))
|
|
9
|
+
const oneDir = resolve(here, '../..')
|
|
10
|
+
|
|
11
|
+
describe('shipped android artifacts', () => {
|
|
12
|
+
it('builds the native library 16 kb page aligned', () => {
|
|
13
|
+
// google play requires 16 kb page support for targetSdk 35+, and the
|
|
14
|
+
// pinned ndk 27 defaults to 4 kb. react native's gradle plugin injects
|
|
15
|
+
// this flag into app modules only, never libraries, so the library
|
|
16
|
+
// must pass it to cmake itself or libOne.so fails to load on 16 kb
|
|
17
|
+
// page devices.
|
|
18
|
+
const gradle = readFileSync(join(oneDir, 'android/build.gradle'), 'utf8')
|
|
19
|
+
expect(gradle).toContain('-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON')
|
|
20
|
+
})
|
|
21
|
+
})
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// guards for what one ships into real ios app builds: the pod must
|
|
2
|
+
// carry a privacy manifest covering the required-reason apis its
|
|
3
|
+
// sources use, and reference only resources that exist.
|
|
4
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs'
|
|
5
|
+
import { dirname, join, resolve } from 'node:path'
|
|
6
|
+
import { fileURLToPath } from 'node:url'
|
|
7
|
+
import { describe, expect, it } from 'vitest'
|
|
8
|
+
|
|
9
|
+
const here = dirname(fileURLToPath(import.meta.url))
|
|
10
|
+
const oneDir = resolve(here, '../..')
|
|
11
|
+
|
|
12
|
+
function readSources(dir: string): string[] {
|
|
13
|
+
const out: string[] = []
|
|
14
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
15
|
+
if (entry.name === 'Tests') continue
|
|
16
|
+
const full = join(dir, entry.name)
|
|
17
|
+
if (entry.isDirectory()) out.push(...readSources(full))
|
|
18
|
+
else if (/\.(swift|m|mm|h|cpp)$/.test(entry.name)) out.push(readFileSync(full, 'utf8'))
|
|
19
|
+
}
|
|
20
|
+
return out
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe('shipped ios artifacts', () => {
|
|
24
|
+
it('ships a privacy manifest covering the required-reason apis in use', () => {
|
|
25
|
+
const manifestPath = join(oneDir, 'ios/PrivacyInfo.xcprivacy')
|
|
26
|
+
expect(existsSync(manifestPath)).toBe(true)
|
|
27
|
+
const manifest = readFileSync(manifestPath, 'utf8')
|
|
28
|
+
expect(manifest).toContain('<plist version="1.0">')
|
|
29
|
+
// reasons mirror expo's shipped manifests for the same api surface:
|
|
30
|
+
// user defaults (expo-constants), boot time (expo-device), file
|
|
31
|
+
// timestamps and disk space (expo-file-system).
|
|
32
|
+
const expected: Record<string, string[]> = {
|
|
33
|
+
NSPrivacyAccessedAPICategoryUserDefaults: ['CA92.1'],
|
|
34
|
+
NSPrivacyAccessedAPICategorySystemBootTime: ['35F9.1'],
|
|
35
|
+
NSPrivacyAccessedAPICategoryFileTimestamp: ['0A2A.1', '3B52.1'],
|
|
36
|
+
NSPrivacyAccessedAPICategoryDiskSpace: ['E174.1', '85F4.1'],
|
|
37
|
+
}
|
|
38
|
+
for (const [category, reasons] of Object.entries(expected)) {
|
|
39
|
+
expect(manifest).toContain(category)
|
|
40
|
+
for (const reason of reasons) expect(manifest).toContain(reason)
|
|
41
|
+
}
|
|
42
|
+
// every declared category must have a use in the shipped sources, and
|
|
43
|
+
// no other required-reason category may appear without one.
|
|
44
|
+
const sources = readSources(join(oneDir, 'ios'))
|
|
45
|
+
const uses: Record<string, RegExp> = {
|
|
46
|
+
NSPrivacyAccessedAPICategoryUserDefaults: /UserDefaults|AppStorage/,
|
|
47
|
+
NSPrivacyAccessedAPICategorySystemBootTime: /systemUptime|kern\.boottime/,
|
|
48
|
+
NSPrivacyAccessedAPICategoryFileTimestamp:
|
|
49
|
+
/attributesOfItem|\.modificationDate|\.creationDate|\.fileCreationDate/,
|
|
50
|
+
NSPrivacyAccessedAPICategoryDiskSpace:
|
|
51
|
+
/attributesOfFileSystem|systemFreeSize|systemSize|volumeAvailableCapacity/,
|
|
52
|
+
}
|
|
53
|
+
for (const [category, pattern] of Object.entries(uses)) {
|
|
54
|
+
expect(sources.some((source) => pattern.test(source))).toBe(true)
|
|
55
|
+
}
|
|
56
|
+
expect(sources.some((source) => /activeKeyboards/.test(source))).toBe(false)
|
|
57
|
+
// the pod must bundle the manifest, and the npm tarball must include it.
|
|
58
|
+
const podspec = readFileSync(join(oneDir, 'One.podspec'), 'utf8')
|
|
59
|
+
expect(podspec).toContain('PrivacyInfo.xcprivacy')
|
|
60
|
+
const files: string[] = JSON.parse(
|
|
61
|
+
readFileSync(join(oneDir, 'package.json'), 'utf8')
|
|
62
|
+
).files
|
|
63
|
+
expect(files).toContain('ios')
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('references only podspec headers that exist', () => {
|
|
67
|
+
const podspec = readFileSync(join(oneDir, 'One.podspec'), 'utf8')
|
|
68
|
+
const headers = podspec.match(/'ios\/[^']+\.h'/g) ?? []
|
|
69
|
+
expect(headers.length).toBeGreaterThan(0)
|
|
70
|
+
for (const quoted of headers) {
|
|
71
|
+
expect(existsSync(join(oneDir, quoted.slice(1, -1)))).toBe(true)
|
|
72
|
+
}
|
|
73
|
+
for (const generated of [
|
|
74
|
+
'nitrogen/generated/ios/One+autolinking.rb',
|
|
75
|
+
'nitrogen/generated/ios/One-Swift-Cxx-Bridge.hpp',
|
|
76
|
+
'schema.json',
|
|
77
|
+
]) {
|
|
78
|
+
expect(existsSync(join(oneDir, generated))).toBe(true)
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
})
|
|
@@ -1,8 +1,52 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { findFocusedRoute } from '@react-navigation/core'
|
|
2
|
+
import { createElement } from 'react'
|
|
3
|
+
import { renderToString } from 'react-dom/server'
|
|
4
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
5
|
+
import { useLinking } from '../fork/useLinking'
|
|
2
6
|
import { getMockContext } from '../testing-utils'
|
|
3
7
|
import { getLinkingConfig } from './getLinkingConfig'
|
|
4
8
|
import { getRoutes } from './getRoutes'
|
|
5
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
getResolvedLinking,
|
|
11
|
+
getSSRInitialState,
|
|
12
|
+
resetLinking,
|
|
13
|
+
setupLinking,
|
|
14
|
+
} from './linkingConfig'
|
|
15
|
+
|
|
16
|
+
const browser = vi.hoisted<{
|
|
17
|
+
location: URL
|
|
18
|
+
history: {
|
|
19
|
+
state: {
|
|
20
|
+
__tempLocation?: { pathname: string; search?: string; hash?: string }
|
|
21
|
+
__tempKey?: string
|
|
22
|
+
} | null
|
|
23
|
+
}
|
|
24
|
+
}>(() => {
|
|
25
|
+
const browser = {
|
|
26
|
+
history: { state: null },
|
|
27
|
+
location: new URL('https://example.test/'),
|
|
28
|
+
}
|
|
29
|
+
Object.defineProperty(globalThis, 'window', {
|
|
30
|
+
configurable: true,
|
|
31
|
+
value: browser,
|
|
32
|
+
})
|
|
33
|
+
return browser
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
browser.history.state = null
|
|
38
|
+
resetLinking()
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const nestedRoutes = () =>
|
|
42
|
+
getRoutes(
|
|
43
|
+
getMockContext([
|
|
44
|
+
'_layout.tsx',
|
|
45
|
+
'home/_layout.tsx',
|
|
46
|
+
'home/[projectId]/_layout.tsx',
|
|
47
|
+
'home/[projectId]/files/[...path].tsx',
|
|
48
|
+
])
|
|
49
|
+
)!
|
|
6
50
|
|
|
7
51
|
describe('setupLinking', () => {
|
|
8
52
|
const routes = () => getRoutes(getMockContext(['_layout.tsx', 'index.tsx']))!
|
|
@@ -39,3 +83,101 @@ describe('setupLinking', () => {
|
|
|
39
83
|
)
|
|
40
84
|
})
|
|
41
85
|
})
|
|
86
|
+
|
|
87
|
+
describe.each([
|
|
88
|
+
{ name: 'client', initialize: setupLinking },
|
|
89
|
+
{ name: 'SSR', initialize: getSSRInitialState },
|
|
90
|
+
])('$name initial location', ({ initialize }) => {
|
|
91
|
+
it.each([
|
|
92
|
+
['/home/retained/files/first/second#details', 'details'],
|
|
93
|
+
['/home/retained/files/first/second?mode=local#details', 'details'],
|
|
94
|
+
['/home/retained/files/first/second?mode=left%23right#part%202', 'part%202'],
|
|
95
|
+
['/home/retained/files/first/second?mode=local', undefined],
|
|
96
|
+
])('round-trips %s through the actual route parser', (href, fragment) => {
|
|
97
|
+
const tree = nestedRoutes()
|
|
98
|
+
const state = initialize(tree, new URL(href, 'https://example.test'))!
|
|
99
|
+
const focused = findFocusedRoute(state)!
|
|
100
|
+
const linking = getLinkingConfig(tree, true)
|
|
101
|
+
|
|
102
|
+
expect(focused.params).toMatchObject({
|
|
103
|
+
projectId: 'retained',
|
|
104
|
+
path: ['first', 'second'],
|
|
105
|
+
})
|
|
106
|
+
if (fragment === undefined) {
|
|
107
|
+
expect(focused.params).not.toHaveProperty('#')
|
|
108
|
+
} else {
|
|
109
|
+
expect(focused.params).toHaveProperty('#', fragment)
|
|
110
|
+
}
|
|
111
|
+
expect(linking.getPathFromState!(state, linking.config)).toBe(href)
|
|
112
|
+
})
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('keeps fragment-specific SSR cache entries separate and clears the fragment', () => {
|
|
116
|
+
const tree = nestedRoutes()
|
|
117
|
+
const base = 'https://example.test/home/retained/files/first/second?mode=local'
|
|
118
|
+
const first = getSSRInitialState(tree, new URL(`${base}#first`))!
|
|
119
|
+
const second = getSSRInitialState(tree, new URL(`${base}#second`))!
|
|
120
|
+
const cleared = getSSRInitialState(tree, new URL(base))!
|
|
121
|
+
|
|
122
|
+
expect(findFocusedRoute(first)?.params).toHaveProperty('#', 'first')
|
|
123
|
+
expect(findFocusedRoute(second)?.params).toHaveProperty('#', 'second')
|
|
124
|
+
expect(findFocusedRoute(cleared)?.params).not.toHaveProperty('#')
|
|
125
|
+
expect(second).not.toBe(first)
|
|
126
|
+
expect(cleared).not.toBe(first)
|
|
127
|
+
expect(getSSRInitialState(tree, new URL(`${base}#first`))).toBe(first)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('restores a masked initial location with its own query and fragment', () => {
|
|
131
|
+
browser.history.state = {
|
|
132
|
+
__tempLocation: {
|
|
133
|
+
pathname: '/home/retained/files/first/second',
|
|
134
|
+
search: '?mode=local',
|
|
135
|
+
hash: '#masked',
|
|
136
|
+
},
|
|
137
|
+
}
|
|
138
|
+
const tree = nestedRoutes()
|
|
139
|
+
const state = setupLinking(tree, new URL('https://example.test/display#visible'))!
|
|
140
|
+
const linking = getLinkingConfig(tree, true)
|
|
141
|
+
|
|
142
|
+
expect(linking.getPathFromState!(state, linking.config)).toBe(
|
|
143
|
+
'/home/retained/files/first/second?mode=local#masked'
|
|
144
|
+
)
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
it('keeps the visible fragment when a mask is configured to unmask on reload', () => {
|
|
148
|
+
browser.history.state = {
|
|
149
|
+
__tempLocation: { pathname: '/other', hash: '#masked' },
|
|
150
|
+
__tempKey: 'unmask',
|
|
151
|
+
}
|
|
152
|
+
const state = setupLinking(
|
|
153
|
+
nestedRoutes(),
|
|
154
|
+
new URL('https://example.test/home/retained/files/first/second#visible')
|
|
155
|
+
)!
|
|
156
|
+
|
|
157
|
+
expect(findFocusedRoute(state)?.params).toHaveProperty('#', 'visible')
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('restores the same masked fragment through the browser linking hook', async () => {
|
|
161
|
+
browser.history.state = {
|
|
162
|
+
__tempLocation: {
|
|
163
|
+
pathname: '/home/retained/files/first/second',
|
|
164
|
+
search: '?mode=local',
|
|
165
|
+
hash: '#masked',
|
|
166
|
+
},
|
|
167
|
+
}
|
|
168
|
+
const linking = getLinkingConfig(nestedRoutes(), true)
|
|
169
|
+
let getInitialState: ReturnType<typeof useLinking>['getInitialState'] | undefined
|
|
170
|
+
function Harness() {
|
|
171
|
+
const linkingState = useLinking({ current: null }, linking)
|
|
172
|
+
getInitialState = linkingState.getInitialState
|
|
173
|
+
return null
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
renderToString(createElement(Harness))
|
|
177
|
+
if (!getInitialState) throw new Error('linking hook did not initialize')
|
|
178
|
+
const state = (await getInitialState())!
|
|
179
|
+
|
|
180
|
+
expect(linking.getPathFromState!(state, linking.config)).toBe(
|
|
181
|
+
'/home/retained/files/first/second?mode=local#masked'
|
|
182
|
+
)
|
|
183
|
+
})
|
|
@@ -72,7 +72,8 @@ export function getSSRInitialState(
|
|
|
72
72
|
ensureBaseLinkingConfig(routeNode, linking)
|
|
73
73
|
if (!cachedBaseLinkingConfig) return undefined
|
|
74
74
|
|
|
75
|
-
const path =
|
|
75
|
+
const path =
|
|
76
|
+
initialLocation.pathname + (initialLocation.search || '') + initialLocation.hash
|
|
76
77
|
if (ssrStateCache.has(path)) return ssrStateCache.get(path)
|
|
77
78
|
|
|
78
79
|
const state = cachedBaseLinkingConfig.getStateFromPath?.(
|
|
@@ -101,14 +102,16 @@ export function setupLinking(
|
|
|
101
102
|
if (initialLocation) {
|
|
102
103
|
linkingConfig.getInitialURL = () => initialLocation.toString()
|
|
103
104
|
|
|
104
|
-
let path =
|
|
105
|
+
let path =
|
|
106
|
+
initialLocation.pathname + (initialLocation.search || '') + initialLocation.hash
|
|
105
107
|
|
|
106
108
|
if (isWebClient) {
|
|
107
109
|
const historyState = window.history.state
|
|
108
110
|
if (historyState?.__tempLocation?.pathname && !historyState.__tempKey) {
|
|
109
111
|
path =
|
|
110
112
|
historyState.__tempLocation.pathname +
|
|
111
|
-
(historyState.__tempLocation.search || '')
|
|
113
|
+
(historyState.__tempLocation.search || '') +
|
|
114
|
+
(historyState.__tempLocation.hash || '')
|
|
112
115
|
}
|
|
113
116
|
}
|
|
114
117
|
|
package/src/useLoader.ts
CHANGED
|
@@ -12,9 +12,7 @@ import {
|
|
|
12
12
|
getClientMatchesSnapshot,
|
|
13
13
|
updateMatchLoaderData,
|
|
14
14
|
} from './useMatches'
|
|
15
|
-
import { getLoaderPath } from './utils/cleanUrl'
|
|
16
|
-
import { getURL } from './getURL'
|
|
17
|
-
import { LOADER_JS_POSTFIX_UNCACHED } from './constants'
|
|
15
|
+
import { getLoaderPath, getNativeLoaderPath } from './utils/cleanUrl'
|
|
18
16
|
import { dynamicImport } from './utils/dynamicImport'
|
|
19
17
|
import { weakKey } from './utils/weakKey'
|
|
20
18
|
import { useServerContext } from './vite/one-server-only'
|
|
@@ -513,18 +511,8 @@ export function useLoaderState<
|
|
|
513
511
|
// dev: ?platform=native tells vite plugin to return CJS
|
|
514
512
|
nativeLoaderJSUrl = `${getLoaderPath(currentPath, true)}?platform=native`
|
|
515
513
|
} else {
|
|
516
|
-
// prod:
|
|
517
|
-
|
|
518
|
-
// must use the static import — a dynamic require('./getURL') is
|
|
519
|
-
// left as a runtime string require by metro and resolves to
|
|
520
|
-
// undefined in the prod native bundle (crashes the route).
|
|
521
|
-
const base = getURL()
|
|
522
|
-
const cleanedPath = currentPath
|
|
523
|
-
.slice(1)
|
|
524
|
-
.replace(/\/$/, '')
|
|
525
|
-
.replaceAll('_', '__')
|
|
526
|
-
.replaceAll('/', '_')
|
|
527
|
-
nativeLoaderJSUrl = `${base}/assets/${cleanedPath}${LOADER_JS_POSTFIX_UNCACHED.replace('.js', '.native.js')}`
|
|
514
|
+
// prod: the uncached .native.js file, since metro can't inline ONE_CACHE_KEY
|
|
515
|
+
nativeLoaderJSUrl = getNativeLoaderPath(currentPath)
|
|
528
516
|
}
|
|
529
517
|
|
|
530
518
|
try {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { LOADER_JS_POSTFIX, NATIVE_LOADER_JS_POSTFIX_UNCACHED } from '../constants'
|
|
2
3
|
import {
|
|
3
4
|
encodeReservedFilenameChars,
|
|
4
5
|
getLoaderPath,
|
|
6
|
+
getNativeLoaderPath,
|
|
5
7
|
getPathFromLoaderPath,
|
|
6
8
|
getPreloadPath,
|
|
7
9
|
} from './cleanUrl'
|
|
@@ -153,6 +155,32 @@ describe('NTFS-reserved characters in route-derived names', () => {
|
|
|
153
155
|
})
|
|
154
156
|
})
|
|
155
157
|
|
|
158
|
+
describe('getNativeLoaderPath', () => {
|
|
159
|
+
// the uncached .native.js file buildPage writes next to each loader
|
|
160
|
+
const builtNativeLoader = (path: string) =>
|
|
161
|
+
getLoaderPath(path, false).replace(LOADER_JS_POSTFIX, NATIVE_LOADER_JS_POSTFIX_UNCACHED)
|
|
162
|
+
|
|
163
|
+
it.each([
|
|
164
|
+
'/',
|
|
165
|
+
'/docs/intro',
|
|
166
|
+
'/my_page/test',
|
|
167
|
+
'/events/2026-10-17T10:00',
|
|
168
|
+
'/posts/YWJj==',
|
|
169
|
+
'/blog/a:b*c',
|
|
170
|
+
])('requests the file the build writes for %s', (path) => {
|
|
171
|
+
expect(getNativeLoaderPath(path).endsWith(builtNativeLoader(path))).toBe(true)
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
it('encodes reserved characters in route params', () => {
|
|
175
|
+
expect(getNativeLoaderPath('/events/2026-10-17T10:00')).toMatch(
|
|
176
|
+
/\/assets\/events_2026-10-17T10=3a00_vxrn_loader\.native\.js$/
|
|
177
|
+
)
|
|
178
|
+
expect(getNativeLoaderPath('/posts/YWJj==')).toMatch(
|
|
179
|
+
/\/assets\/posts_YWJj=3d=3d_vxrn_loader\.native\.js$/
|
|
180
|
+
)
|
|
181
|
+
})
|
|
182
|
+
})
|
|
183
|
+
|
|
156
184
|
describe('getPathFromLoaderPath', () => {
|
|
157
185
|
it('strips /_one/assets prefix', () => {
|
|
158
186
|
expect(getPathFromLoaderPath('/_one/assets/docs_intro_999_vxrn_loader.js')).toBe(
|
package/src/utils/cleanUrl.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
isNative,
|
|
4
4
|
LOADER_JS_POSTFIX,
|
|
5
5
|
LOADER_JS_POSTFIX_REGEX,
|
|
6
|
+
NATIVE_LOADER_JS_POSTFIX_UNCACHED,
|
|
6
7
|
PRELOAD_JS_POSTFIX,
|
|
7
8
|
} from '../constants'
|
|
8
9
|
import { getURL } from '../getURL'
|
|
@@ -56,12 +57,18 @@ export function getLoaderPath(
|
|
|
56
57
|
const baseURL = includeUrl ? getURL() : ''
|
|
57
58
|
const devPath = process.env.NODE_ENV === 'development' ? '/_one' : ''
|
|
58
59
|
|
|
59
|
-
const currentPathUrl = new URL(currentPath, 'http://example.com')
|
|
60
|
-
|
|
61
|
-
const cleanedUrl = cleanUrl(currentPathUrl.pathname.slice(1))
|
|
62
60
|
const cacheBustSegment = cacheBust ? `_refetch_${cacheBust}_` : ''
|
|
63
61
|
|
|
64
|
-
return `${baseURL}${devPath}/assets/${
|
|
62
|
+
return `${baseURL}${devPath}/assets/${getLoaderFileStem(currentPath)}${cacheBustSegment}${LOADER_JS_POSTFIX}`
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// native prod reads the uncached .native.js copy the build writes beside each loader
|
|
66
|
+
export function getNativeLoaderPath(currentPath: string) {
|
|
67
|
+
return `${getURL()}/assets/${getLoaderFileStem(currentPath)}${NATIVE_LOADER_JS_POSTFIX_UNCACHED}`
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function getLoaderFileStem(currentPath: string) {
|
|
71
|
+
return cleanUrl(new URL(currentPath, 'http://example.com').pathname.slice(1))
|
|
65
72
|
}
|
|
66
73
|
|
|
67
74
|
export function getPathFromLoaderPath(loaderPath: string) {
|
package/src/vite/one.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { configureVXRNCompilerPlugin } from '@vxrn/compiler'
|
|
2
2
|
import { resolvePath } from '@vxrn/resolve'
|
|
3
|
-
import {
|
|
3
|
+
import type { MetroPluginOptions } from '@vxrn/vite-plugin-metro'
|
|
4
|
+
import { getPlatformEnvDefine } from '@vxrn/utils'
|
|
4
5
|
import events from 'node:events'
|
|
5
6
|
import { existsSync, readFileSync } from 'node:fs'
|
|
6
7
|
import path from 'node:path'
|
|
@@ -3,7 +3,7 @@ import { join, resolve } from 'node:path'
|
|
|
3
3
|
import { pathToFileURL } from 'node:url'
|
|
4
4
|
import { resolvePath } from '@vxrn/resolve'
|
|
5
5
|
import { cloudflare } from '@cloudflare/vite-plugin'
|
|
6
|
-
import { getPlatformEnvDefine } from '@vxrn/
|
|
6
|
+
import { getPlatformEnvDefine } from '@vxrn/utils'
|
|
7
7
|
import { exactRegex } from 'rolldown/filter'
|
|
8
8
|
import { type Plugin, type PluginOption } from 'vite'
|
|
9
9
|
import { CACHE_KEY } from '../../constants'
|
|
@@ -285,7 +285,7 @@ export function createWorkerdDevPlugins(
|
|
|
285
285
|
? ['/bundledDevClient.mjs', '/assets/_virtual_one-entry.js']
|
|
286
286
|
: ['/@vite/client', virtalEntryIdClient]
|
|
287
287
|
|
|
288
|
-
const empty = resolvePath('@vxrn/
|
|
288
|
+
const empty = resolvePath('@vxrn/utils/empty', root)
|
|
289
289
|
const absorbed = absorbedPackageAliases(root, 'web')
|
|
290
290
|
|
|
291
291
|
const preparePlugin: Plugin = {
|
package/src/vite/types.ts
CHANGED
|
@@ -674,11 +674,11 @@ export namespace One {
|
|
|
674
674
|
api?: VXRNBuildOptions
|
|
675
675
|
|
|
676
676
|
/**
|
|
677
|
-
* maximum number of static pages to build concurrently.
|
|
678
|
-
*
|
|
679
|
-
* `ONE_BUILD_CONCURRENCY` overrides this value.
|
|
677
|
+
* maximum number of static pages to build concurrently. two keeps peak
|
|
678
|
+
* memory near half of eight workers for a modest slowdown; raise it on a
|
|
679
|
+
* machine with memory to spare. `ONE_BUILD_CONCURRENCY` overrides this value.
|
|
680
680
|
*
|
|
681
|
-
* @default Math.min(available CPU cores,
|
|
681
|
+
* @default Math.min(available CPU cores, 2)
|
|
682
682
|
*/
|
|
683
683
|
concurrency?: number
|
|
684
684
|
|
package/types/Frozen.d.ts
CHANGED
package/types/Frozen.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Frozen.d.ts","sourceRoot":"","sources":["../src/Frozen.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,wBAAgB,MAAM,CAAC,EAAE,EAAU,EAAE,QAAQ,EAAE,EAAE;IAAE,EAAE,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,SAAS,CAAA;CAAE
|
|
1
|
+
{"version":3,"file":"Frozen.d.ts","sourceRoot":"","sources":["../src/Frozen.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,wBAAgB,MAAM,CAAC,EAAE,EAAU,EAAE,QAAQ,EAAE,EAAE;IAAE,EAAE,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,SAAS,CAAA;CAAE,+BASrF"}
|