one 1.25.7 → 1.25.8

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.
Files changed (85) hide show
  1. package/dist/cjs/cli/prebuild.cjs +2 -0
  2. package/dist/cjs/cli/prebuild.native.js +2 -0
  3. package/dist/cjs/cli/prebuild.native.js.map +1 -1
  4. package/dist/cjs/cli/prebuild.test.cjs +45 -0
  5. package/dist/cjs/cli/prebuild.test.native.js +54 -0
  6. package/dist/cjs/cli/prebuild.test.native.js.map +1 -0
  7. package/dist/cjs/createHandleRequest.cjs +5 -1
  8. package/dist/cjs/createHandleRequest.native.js +5 -1
  9. package/dist/cjs/createHandleRequest.native.js.map +1 -1
  10. package/dist/cjs/createHandleRequest.test.cjs +59 -1
  11. package/dist/cjs/createHandleRequest.test.native.js +70 -0
  12. package/dist/cjs/createHandleRequest.test.native.js.map +1 -1
  13. package/dist/cjs/server/oneServe.cjs +0 -2
  14. package/dist/cjs/server/oneServe.native.js +0 -2
  15. package/dist/cjs/server/oneServe.native.js.map +1 -1
  16. package/dist/cjs/server/workerHandler.cjs +1 -1
  17. package/dist/cjs/server/workerHandler.native.js +1 -1
  18. package/dist/cjs/server/workerHandler.native.js.map +1 -1
  19. package/dist/cjs/server/workerHandler.test.cjs +117 -0
  20. package/dist/cjs/server/workerHandler.test.native.js +137 -0
  21. package/dist/cjs/server/workerHandler.test.native.js.map +1 -0
  22. package/dist/cjs/views/ScrollBehavior.cjs +10 -6
  23. package/dist/cjs/views/ScrollBehavior.native.js +10 -6
  24. package/dist/cjs/views/ScrollBehavior.native.js.map +1 -1
  25. package/dist/cjs/views/ScrollBehavior.test.cjs +132 -0
  26. package/dist/cjs/views/ScrollBehavior.test.native.js +184 -0
  27. package/dist/cjs/views/ScrollBehavior.test.native.js.map +1 -0
  28. package/dist/esm/cli/prebuild.mjs +2 -0
  29. package/dist/esm/cli/prebuild.mjs.map +1 -1
  30. package/dist/esm/cli/prebuild.native.js +2 -0
  31. package/dist/esm/cli/prebuild.native.js.map +1 -1
  32. package/dist/esm/cli/prebuild.test.mjs +46 -0
  33. package/dist/esm/cli/prebuild.test.mjs.map +1 -0
  34. package/dist/esm/cli/prebuild.test.native.js +52 -0
  35. package/dist/esm/cli/prebuild.test.native.js.map +1 -0
  36. package/dist/esm/createHandleRequest.mjs +5 -2
  37. package/dist/esm/createHandleRequest.mjs.map +1 -1
  38. package/dist/esm/createHandleRequest.native.js +5 -2
  39. package/dist/esm/createHandleRequest.native.js.map +1 -1
  40. package/dist/esm/createHandleRequest.test.mjs +59 -1
  41. package/dist/esm/createHandleRequest.test.mjs.map +1 -1
  42. package/dist/esm/createHandleRequest.test.native.js +70 -0
  43. package/dist/esm/createHandleRequest.test.native.js.map +1 -1
  44. package/dist/esm/server/oneServe.mjs +0 -2
  45. package/dist/esm/server/oneServe.mjs.map +1 -1
  46. package/dist/esm/server/oneServe.native.js +0 -2
  47. package/dist/esm/server/oneServe.native.js.map +1 -1
  48. package/dist/esm/server/workerHandler.mjs +2 -2
  49. package/dist/esm/server/workerHandler.mjs.map +1 -1
  50. package/dist/esm/server/workerHandler.native.js +2 -2
  51. package/dist/esm/server/workerHandler.native.js.map +1 -1
  52. package/dist/esm/server/workerHandler.test.mjs +118 -0
  53. package/dist/esm/server/workerHandler.test.mjs.map +1 -0
  54. package/dist/esm/server/workerHandler.test.native.js +135 -0
  55. package/dist/esm/server/workerHandler.test.native.js.map +1 -0
  56. package/dist/esm/views/ScrollBehavior.mjs +10 -6
  57. package/dist/esm/views/ScrollBehavior.mjs.map +1 -1
  58. package/dist/esm/views/ScrollBehavior.native.js +10 -6
  59. package/dist/esm/views/ScrollBehavior.native.js.map +1 -1
  60. package/dist/esm/views/ScrollBehavior.test.mjs +109 -0
  61. package/dist/esm/views/ScrollBehavior.test.mjs.map +1 -0
  62. package/dist/esm/views/ScrollBehavior.test.native.js +158 -0
  63. package/dist/esm/views/ScrollBehavior.test.native.js.map +1 -0
  64. package/package.json +9 -9
  65. package/src/cli/prebuild.test.ts +48 -0
  66. package/src/cli/prebuild.ts +3 -0
  67. package/src/createHandleRequest.test.ts +55 -1
  68. package/src/createHandleRequest.ts +8 -1
  69. package/src/server/oneServe.ts +0 -2
  70. package/src/server/workerHandler.test.ts +125 -0
  71. package/src/server/workerHandler.ts +3 -2
  72. package/src/views/ScrollBehavior.test.tsx +137 -0
  73. package/src/views/ScrollBehavior.tsx +18 -8
  74. package/types/cli/prebuild.d.ts.map +1 -1
  75. package/types/cli/prebuild.test.d.ts +2 -0
  76. package/types/cli/prebuild.test.d.ts.map +1 -0
  77. package/types/createHandleRequest.d.ts +1 -0
  78. package/types/createHandleRequest.d.ts.map +1 -1
  79. package/types/server/oneServe.d.ts.map +1 -1
  80. package/types/server/workerHandler.d.ts.map +1 -1
  81. package/types/server/workerHandler.test.d.ts +2 -0
  82. package/types/server/workerHandler.test.d.ts.map +1 -0
  83. package/types/views/ScrollBehavior.d.ts.map +1 -1
  84. package/types/views/ScrollBehavior.test.d.ts +2 -0
  85. package/types/views/ScrollBehavior.test.d.ts.map +1 -0
@@ -0,0 +1,48 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
2
+ import { run } from './prebuild'
3
+
4
+ const { loadUserOneOptionsMock, prebuildMock } = vi.hoisted(() => ({
5
+ loadUserOneOptionsMock: vi.fn(),
6
+ prebuildMock: vi.fn(),
7
+ }))
8
+
9
+ vi.mock('../vite/loadConfig', () => ({
10
+ loadUserOneOptions: loadUserOneOptionsMock,
11
+ }))
12
+
13
+ vi.mock('vxrn', () => ({
14
+ prebuild: prebuildMock,
15
+ }))
16
+
17
+ describe('one prebuild', () => {
18
+ beforeEach(() => {
19
+ loadUserOneOptionsMock.mockReset()
20
+ prebuildMock.mockReset()
21
+ })
22
+
23
+ it('loads the app bundler configuration before native prebuild', async () => {
24
+ loadUserOneOptionsMock.mockResolvedValueOnce({})
25
+
26
+ await run({ platform: 'ios', expo: true, 'no-install': true })
27
+
28
+ expect(loadUserOneOptionsMock).toHaveBeenCalledWith('build', true)
29
+ expect(loadUserOneOptionsMock.mock.invocationCallOrder[0]).toBeLessThan(
30
+ prebuildMock.mock.invocationCallOrder[0]
31
+ )
32
+ expect(prebuildMock).toHaveBeenCalledWith({
33
+ root: process.cwd(),
34
+ platform: 'ios',
35
+ expo: true,
36
+ 'no-install': true,
37
+ })
38
+ })
39
+
40
+ it('does not prebuild when the app configuration fails to load', async () => {
41
+ loadUserOneOptionsMock.mockRejectedValueOnce(new Error('invalid vite config'))
42
+
43
+ await expect(run({ platform: 'ios', expo: true })).rejects.toThrow(
44
+ 'invalid vite config'
45
+ )
46
+ expect(prebuildMock).not.toHaveBeenCalled()
47
+ })
48
+ })
@@ -1,8 +1,11 @@
1
+ import { loadUserOneOptions } from '../vite/loadConfig'
2
+
1
3
  export async function run(args: {
2
4
  platform?: string
3
5
  expo: boolean
4
6
  'no-install'?: boolean
5
7
  }) {
8
+ await loadUserOneOptions('build', true)
6
9
  const { prebuild } = await import('vxrn')
7
10
 
8
11
  await prebuild({
@@ -62,8 +62,9 @@ vi.mock('./vite/getManifest', () => ({
62
62
  }),
63
63
  }))
64
64
 
65
- function createRequest(path: string) {
65
+ function createRequest(path: string, method = 'GET') {
66
66
  return new Request(`http://localhost:3000${path}`, {
67
+ method,
67
68
  headers: {
68
69
  host: 'localhost:3000',
69
70
  },
@@ -262,6 +263,59 @@ describe('createHandleRequest', () => {
262
263
  })
263
264
  })
264
265
 
266
+ describe('request method dispatch', () => {
267
+ it('answers HEAD page requests exactly like GET', async () => {
268
+ const handlePage = vi.fn(
269
+ async () =>
270
+ new Response('<html></html>', { headers: { 'content-type': 'text/html' } })
271
+ )
272
+ const { handler } = createHandleRequest({ handlePage }, { routerRoot: '/app' })
273
+
274
+ const get = await handler(createRequest('/my-page', 'GET'))
275
+ const head = await handler(createRequest('/my-page', 'HEAD'))
276
+
277
+ if (!(get instanceof Response) || !(head instanceof Response)) {
278
+ throw new Error('expected both GET and HEAD to return a Response')
279
+ }
280
+ // the transport drops the body for HEAD, so the handler layer must agree
281
+ // with GET on everything else: status and content type.
282
+ expect(head.status).toBe(get.status)
283
+ expect(head.headers.get('content-type')).toBe(get.headers.get('content-type'))
284
+ expect(handlePage).toHaveBeenCalledTimes(2)
285
+ })
286
+
287
+ it('continues routing HEAD requests to API handlers', async () => {
288
+ const mockHandlersWithAPI = {
289
+ handlePage: vi.fn().mockResolvedValue('<html></html>'),
290
+ handleAPI: vi.fn().mockResolvedValue({
291
+ HEAD: () => new Response(null, { status: 204 }),
292
+ }),
293
+ }
294
+ const { handler } = createHandleRequest(mockHandlersWithAPI, {
295
+ routerRoot: '/app',
296
+ })
297
+
298
+ const result = await handler(createRequest('/api/github/agent/run.sh', 'HEAD'))
299
+
300
+ expect(result).toBeInstanceOf(Response)
301
+ if (!(result instanceof Response)) {
302
+ throw new Error('expected HEAD API request to return a Response')
303
+ }
304
+ expect(result.status).toBe(204)
305
+ expect(mockHandlersWithAPI.handleAPI).toHaveBeenCalledOnce()
306
+ expect(mockHandlersWithAPI.handlePage).not.toHaveBeenCalled()
307
+ })
308
+
309
+ it('does not route POST requests to page handlers', async () => {
310
+ const { handler } = createHandleRequest(mockHandlers, { routerRoot: '/app' })
311
+
312
+ const result = await handler(createRequest('/my-page', 'POST'))
313
+
314
+ expect(result).toBeNull()
315
+ expect(mockHandlers.handlePage).not.toHaveBeenCalled()
316
+ })
317
+ })
318
+
265
319
  describe('special paths', () => {
266
320
  it('should skip __vxrnhmr path', async () => {
267
321
  const { handler } = createHandleRequest(mockHandlers, { routerRoot: '/app' })
@@ -35,6 +35,13 @@ function isStaticAssetRequestPath(pathname: string): boolean {
35
35
  )
36
36
  }
37
37
 
38
+ // page (document) routes answer HEAD exactly like GET. the transport drops the
39
+ // body: node's ServerResponse suppresses it for HEAD and workerd does the same.
40
+ // without this a HEAD document request falls through to a bare 404 while GET is 200.
41
+ export function isPageRequestMethod(method: string) {
42
+ return method === 'GET' || method === 'HEAD'
43
+ }
44
+
38
45
  // ensure handler results are always a proper Response so middleware
39
46
  // can safely use response.body / response.headers / new Response(response.body, ...)
40
47
  function ensureResponse(value: any): Response {
@@ -425,7 +432,7 @@ export function createHandleRequest(
425
432
  }
426
433
  }
427
434
 
428
- if (request.method !== 'GET') {
435
+ if (!isPageRequestMethod(request.method)) {
429
436
  return null
430
437
  }
431
438
 
@@ -924,7 +924,6 @@ url: ${url}`)
924
924
 
925
925
  for (const route of compiledManifest.apiRoutes) {
926
926
  app.get(route.urlPath, createHonoHandler(route))
927
- app.on('HEAD', route.urlPath, createHonoHandler(route))
928
927
  app.put(route.urlPath, createHonoHandler(route))
929
928
  app.post(route.urlPath, createHonoHandler(route))
930
929
  app.delete(route.urlPath, createHonoHandler(route))
@@ -933,7 +932,6 @@ url: ${url}`)
933
932
 
934
933
  if (route.urlPath !== route.urlCleanPath) {
935
934
  app.get(route.urlCleanPath, createHonoHandler(route))
936
- app.on('HEAD', route.urlCleanPath, createHonoHandler(route))
937
935
  app.put(route.urlCleanPath, createHonoHandler(route))
938
936
  app.post(route.urlCleanPath, createHonoHandler(route))
939
937
  app.delete(route.urlCleanPath, createHonoHandler(route))
@@ -0,0 +1,125 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest'
2
+ import type { One } from '../vite/types'
3
+ import { createWorkerHandler, type LazyRoutes } from './workerHandler'
4
+
5
+ const pageRoute = {
6
+ file: 'app/some-page.tsx',
7
+ page: '/some-page',
8
+ namedRegex: '^/some-page(?:/)?$',
9
+ urlPath: '/some-page',
10
+ urlCleanPath: '/some-page',
11
+ routeKeys: {},
12
+ type: 'ssr',
13
+ middlewares: [],
14
+ } satisfies One.BuildInfo['manifest']['pageRoutes'][number]
15
+
16
+ const apiRoute = {
17
+ file: 'app/api/status+api.ts',
18
+ page: '/api/status',
19
+ namedRegex: '^/api/status(?:/)?$',
20
+ urlPath: '/api/status',
21
+ urlCleanPath: '/api/status',
22
+ routeKeys: {},
23
+ type: 'api',
24
+ middlewares: [],
25
+ } satisfies One.BuildInfo['manifest']['apiRoutes'][number]
26
+
27
+ const buildInfo = {
28
+ constants: { CACHE_KEY: 'test' },
29
+ routeToBuildInfo: {
30
+ [pageRoute.file]: {
31
+ type: 'ssr',
32
+ path: pageRoute.page,
33
+ routeFile: pageRoute.file,
34
+ middlewares: [],
35
+ preloadPath: '',
36
+ cssPreloadPath: '',
37
+ loaderPath: '',
38
+ cleanPath: pageRoute.page,
39
+ htmlPath: '',
40
+ clientJsPath: '',
41
+ serverJsPath: '',
42
+ params: {},
43
+ preloads: [],
44
+ css: [],
45
+ layoutCSS: [],
46
+ },
47
+ },
48
+ pathToRoute: {},
49
+ routeMap: {},
50
+ manifest: {
51
+ pageRoutes: [pageRoute],
52
+ apiRoutes: [apiRoute],
53
+ allRoutes: [pageRoute, apiRoute],
54
+ },
55
+ preloads: {},
56
+ cssPreloads: {},
57
+ loaders: {},
58
+ } satisfies One.BuildInfo
59
+
60
+ const lazyRoutes = {
61
+ serverEntry: async () => ({
62
+ default: {
63
+ render: () => '<html></html>',
64
+ },
65
+ }),
66
+ pages: {
67
+ [pageRoute.file]: async () => ({}),
68
+ },
69
+ api: {
70
+ [apiRoute.page]: async () => ({
71
+ HEAD: () => new Response(null, { status: 204 }),
72
+ }),
73
+ },
74
+ middlewares: {},
75
+ } satisfies LazyRoutes
76
+
77
+ function createHandler() {
78
+ vi.stubEnv('ONE_BUFFERED_SSR', '1')
79
+ return createWorkerHandler({
80
+ oneOptions: { web: { defaultRenderMode: 'ssr' } },
81
+ buildInfo,
82
+ lazyRoutes,
83
+ }).handleRequest
84
+ }
85
+
86
+ afterEach(() => {
87
+ vi.unstubAllEnvs()
88
+ })
89
+
90
+ describe('createWorkerHandler', () => {
91
+ it('answers HEAD page requests exactly like GET', async () => {
92
+ const handleRequest = createHandler()
93
+ const get = await handleRequest(new Request('https://example.com/some-page'))
94
+ const head = await handleRequest(
95
+ new Request('https://example.com/some-page', { method: 'HEAD' })
96
+ )
97
+
98
+ if (!(get instanceof Response) || !(head instanceof Response)) {
99
+ throw new Error('expected both GET and HEAD to return a Response')
100
+ }
101
+ // workerd drops the body for HEAD, so the handler must agree with GET on
102
+ // status and content type. asserting an empty body here would only be
103
+ // asserting the transport's job.
104
+ expect(get.status).toBe(200)
105
+ expect(head.status).toBe(get.status)
106
+ expect(head.headers.get('content-type')).toBe(get.headers.get('content-type'))
107
+ })
108
+
109
+ it('does not route POST requests to page handlers', async () => {
110
+ const response = await createHandler()(
111
+ new Request('https://example.com/some-page', { method: 'POST' })
112
+ )
113
+
114
+ expect(response).toBeNull()
115
+ })
116
+
117
+ it('continues routing HEAD requests to API handlers', async () => {
118
+ const response = await createHandler()(
119
+ new Request('https://example.com/api/status', { method: 'HEAD' })
120
+ )
121
+
122
+ expect(response).toBeInstanceOf(Response)
123
+ expect(response?.status).toBe(204)
124
+ })
125
+ })
@@ -7,6 +7,7 @@ import {
7
7
  compileManifest,
8
8
  getSubdomain,
9
9
  getURLfromRequestURL,
10
+ isPageRequestMethod,
10
11
  type RequestHandlers,
11
12
  resolveAPIRoute,
12
13
  resolveLoaderRoute,
@@ -706,8 +707,8 @@ export function createWorkerHandler(options: WorkerHandlerOptions) {
706
707
  }
707
708
  }
708
709
 
709
- // 6. page routes (GET only)
710
- if (method === 'GET') {
710
+ // 6. page routes
711
+ if (isPageRequestMethod(method)) {
711
712
  for (const route of compiledManifest.pageRoutes) {
712
713
  if (!route.compiledRegex.test(pathname)) continue
713
714
 
@@ -0,0 +1,137 @@
1
+ import React from 'react'
2
+ import TestRenderer, { act } from 'react-test-renderer'
3
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
4
+
5
+ const subscriptions = vi.hoisted(() => ({
6
+ loading: undefined as ((state: 'loading' | 'loaded') => void) | undefined,
7
+ root: undefined as ((state: any) => void) | undefined,
8
+ }))
9
+
10
+ vi.mock('../router/lastAction', () => ({
11
+ setLastAction: vi.fn(),
12
+ }))
13
+
14
+ vi.mock('../router/router', () => ({
15
+ subscribeToLoadingState: (subscriber: typeof subscriptions.loading) => {
16
+ subscriptions.loading = subscriber
17
+ return () => {
18
+ subscriptions.loading = undefined
19
+ }
20
+ },
21
+ subscribeToRootState: (subscriber: typeof subscriptions.root) => {
22
+ subscriptions.root = subscriber
23
+ return () => {
24
+ subscriptions.root = undefined
25
+ }
26
+ },
27
+ }))
28
+
29
+ import { registerScrollGroup, ScrollBehavior } from './ScrollBehavior'
30
+
31
+ ;(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true
32
+
33
+ type MockLocation = {
34
+ pathname: string
35
+ search: string
36
+ hash: string
37
+ }
38
+
39
+ let renderer: TestRenderer.ReactTestRenderer | undefined
40
+ let location: MockLocation
41
+ let scrollTo: ReturnType<typeof vi.fn>
42
+
43
+ beforeEach(() => {
44
+ process.env.VITE_ENVIRONMENT = 'client'
45
+ location = { pathname: '/', search: '', hash: '' }
46
+ scrollTo = vi.fn()
47
+
48
+ const windowMock = Object.assign(new EventTarget(), {
49
+ location,
50
+ scrollTo,
51
+ scrollY: 500,
52
+ })
53
+ const storage = new Map<string, string>()
54
+
55
+ vi.stubGlobal('window', windowMock)
56
+ vi.stubGlobal('document', { getElementById: vi.fn() })
57
+ vi.stubGlobal('sessionStorage', {
58
+ getItem: (key: string) => storage.get(key) ?? null,
59
+ setItem: (key: string, value: string) => storage.set(key, value),
60
+ removeItem: (key: string) => storage.delete(key),
61
+ })
62
+
63
+ act(() => {
64
+ renderer = TestRenderer.create(<ScrollBehavior />)
65
+ })
66
+ })
67
+
68
+ afterEach(() => {
69
+ act(() => renderer?.unmount())
70
+ renderer = undefined
71
+ vi.useRealTimers()
72
+ vi.unstubAllGlobals()
73
+ delete process.env.VITE_ENVIRONMENT
74
+ })
75
+
76
+ describe('ScrollBehavior initial navigation', () => {
77
+ it('does not scroll when the navigator publishes its initial state after mount', () => {
78
+ act(() => subscriptions.root?.({}))
79
+
80
+ expect(scrollTo).not.toHaveBeenCalled()
81
+ })
82
+
83
+ it('scrolls to the top when the first state notification is a real navigation', () => {
84
+ location.pathname = '/compat/react-native-gesture-handler'
85
+
86
+ act(() => subscriptions.root?.({}))
87
+
88
+ expect(scrollTo).toHaveBeenCalledWith(0, 0)
89
+ })
90
+
91
+ it('handles a query change as a first navigation', () => {
92
+ location.search = '?platform=ios'
93
+
94
+ act(() => subscriptions.root?.({}))
95
+
96
+ expect(scrollTo).toHaveBeenCalledWith(0, 0)
97
+ })
98
+ })
99
+
100
+ describe('ScrollBehavior groups', () => {
101
+ it('preserves position between routes in the same group', () => {
102
+ vi.useFakeTimers()
103
+ const unregister = registerScrollGroup('/compat/react-native-gesture-handler')
104
+
105
+ act(() => subscriptions.root?.({}))
106
+ location.pathname = '/compat/react-native-gesture-handler'
107
+ act(() => subscriptions.root?.({}))
108
+ scrollTo.mockClear()
109
+
110
+ act(() => subscriptions.loading?.('loading'))
111
+ location.pathname = '/compat/react-native-gesture-handler/pan-gesture'
112
+ act(() => subscriptions.root?.({}))
113
+ vi.runAllTimers()
114
+
115
+ expect(scrollTo).toHaveBeenCalledWith(0, 500)
116
+
117
+ unregister()
118
+ vi.useRealTimers()
119
+ })
120
+
121
+ it('does not match a different route whose slug has the same prefix', () => {
122
+ const unregister = registerScrollGroup('/compat/react-native')
123
+
124
+ act(() => subscriptions.root?.({}))
125
+ location.pathname = '/compat/react-native/core'
126
+ act(() => subscriptions.root?.({}))
127
+ scrollTo.mockClear()
128
+
129
+ act(() => subscriptions.loading?.('loading'))
130
+ location.pathname = '/compat/react-native-gesture-handler'
131
+ act(() => subscriptions.root?.({}))
132
+
133
+ expect(scrollTo).toHaveBeenCalledWith(0, 0)
134
+
135
+ unregister()
136
+ })
137
+ })
@@ -8,9 +8,6 @@ const GROUP_KEY = 'one-sr-groups'
8
8
  const getState = () => JSON.parse(sessionStorage.getItem(KEY) || '{}')
9
9
  const getGroupState = () => JSON.parse(sessionStorage.getItem(GROUP_KEY) || '{}')
10
10
 
11
- // prevent scroll to top on first load
12
- let isFirstLoad = true
13
-
14
11
  // Active scroll groups - defined by layouts
15
12
  let activeGroups: Set<string> = new Set()
16
13
 
@@ -33,7 +30,7 @@ function getGroupKey(pathname: string): string | null {
33
30
  let longestMatch: string | null = null
34
31
  for (const group of activeGroups) {
35
32
  if (
36
- pathname.startsWith(group) &&
33
+ (pathname === group || pathname.startsWith(`${group}/`)) &&
37
34
  (!longestMatch || group.length > longestMatch.length)
38
35
  ) {
39
36
  longestMatch = group
@@ -108,6 +105,11 @@ function configure(props: ScrollBehaviorProps) {
108
105
 
109
106
  disable?.()
110
107
 
108
+ const initialLocation =
109
+ `${window.location.pathname}${window.location.search}${window.location.hash}`
110
+ let isFirstStateChange = true
111
+ previousPathname = window.location.pathname
112
+
111
113
  const popStateController = new AbortController()
112
114
 
113
115
  window.addEventListener(
@@ -128,10 +130,18 @@ function configure(props: ScrollBehaviorProps) {
128
130
  })
129
131
 
130
132
  const disposeOnRootState = subscribeToRootState((state) => {
131
- if (isFirstLoad) {
132
- isFirstLoad = false
133
- previousPathname = window.location.pathname
134
- return
133
+ if (isFirstStateChange) {
134
+ isFirstStateChange = false
135
+
136
+ // Depending on navigator mount timing, the initial state notification may
137
+ // happen before or after this effect subscribes. Only ignore it when the
138
+ // browser is still at the URL where ScrollBehavior mounted. Otherwise this
139
+ // is the first real navigation and must receive normal scroll handling.
140
+ const currentLocation =
141
+ `${window.location.pathname}${window.location.search}${window.location.hash}`
142
+ if (currentLocation === initialLocation) {
143
+ return
144
+ }
135
145
  }
136
146
 
137
147
  if (state.linkOptions?.scroll === false) {
@@ -1 +1 @@
1
- {"version":3,"file":"prebuild.d.ts","sourceRoot":"","sources":["../../src/cli/prebuild.ts"],"names":[],"mappings":"AAAA,wBAAsB,GAAG,CAAC,IAAI,EAAE;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB,iBAOA"}
1
+ {"version":3,"file":"prebuild.d.ts","sourceRoot":"","sources":["../../src/cli/prebuild.ts"],"names":[],"mappings":"AAEA,wBAAsB,GAAG,CAAC,IAAI,EAAE;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB,iBAQA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=prebuild.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prebuild.test.d.ts","sourceRoot":"","sources":["../../src/cli/prebuild.test.ts"],"names":[],"mappings":""}
@@ -16,6 +16,7 @@ type RequestHandlerProps<RouteExtraProps extends object = {}> = {
16
16
  loaderProps?: LoaderProps;
17
17
  };
18
18
  type RequestHandlerResponse = null | string | Response;
19
+ export declare function isPageRequestMethod(method: string): method is "GET" | "HEAD";
19
20
  export declare function runMiddlewares(handlers: RequestHandlers, request: Request, route: RouteInfo, getResponse: () => Promise<Response>): Promise<Response>;
20
21
  export declare function resolveAPIRoute(handlers: RequestHandlers, request: Request, url: URL, route: RouteInfoCompiled, env?: unknown, executionCtx?: unknown): Promise<Response>;
21
22
  export declare function resolveLoaderRoute(handlers: RequestHandlers, request: Request, url: URL, route: RouteInfoCompiled): Promise<Response>;
@@ -1 +1 @@
1
- {"version":3,"file":"createHandleRequest.d.ts","sourceRoot":"","sources":["../src/createHandleRequest.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"createHandleRequest.d.ts","sourceRoot":"","sources":["../src/createHandleRequest.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAA;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAK1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAE7C,MAAM,MAAM,eAAe,GAAG;IAC5B,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;IACzD,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;IAC3D,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;IACxD,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAA;IAC7D,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;CACpD,CAAA;AAED,KAAK,mBAAmB,CAAC,eAAe,SAAS,MAAM,GAAG,EAAE,IAAI;IAC9D,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,SAAS,CAAC,MAAM,CAAC,GAAG,eAAe,CAAA;IAC1C,GAAG,EAAE,GAAG,CAAA;IACR,WAAW,CAAC,EAAE,WAAW,CAAA;CAC1B,CAAA;AAED,KAAK,sBAAsB,GAAG,IAAI,GAAG,MAAM,GAAG,QAAQ,CAAA;AActD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,4BAEjD;AAmBD,wBAAsB,cAAc,CAClC,QAAQ,EAAE,eAAe,EACzB,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,SAAS,EAChB,WAAW,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,GACnC,OAAO,CAAC,QAAQ,CAAC,CAgEnB;AAED,wBAAsB,eAAe,CACnC,QAAQ,EAAE,eAAe,EACzB,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,iBAAiB,EACxB,GAAG,CAAC,EAAE,OAAO,EACb,YAAY,CAAC,EAAE,OAAO,qBA+CvB;AAED,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,eAAe,EACzB,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,iBAAiB,qBAkFzB;AAuBD,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,eAAe,EACzB,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,iBAAiB,gBA4BzB;AAKD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,OAcpD;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,GAAG,SAAS,CAazD;AASD,wBAAgB,eAAe,CAAC,QAAQ,EAAE;IACxC,UAAU,EAAE,SAAS,EAAE,CAAA;IACvB,SAAS,EAAE,SAAS,EAAE,CAAA;CACvB,GAAG;IACF,UAAU,EAAE,iBAAiB,EAAE,CAAA;IAC/B,SAAS,EAAE,iBAAiB,EAAE,CAAA;CAC/B,CAKA;AAGD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,eAAe,EACzB,EACE,UAAU,EACV,iBAAiB,EACjB,UAAU,GACX,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE;;uBAWjE,OAAO,KACf,OAAO,CAAC,sBAAsB,CAAC;EAmJrC;AAED,wBAAgB,eAAe,CAC7B,GAAG,EAAE,GAAG,EACR,MAAM,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,0BAWrE"}
@@ -1 +1 @@
1
- {"version":3,"file":"oneServe.d.ts","sourceRoot":"","sources":["../../src/server/oneServe.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAqB,MAAM,MAAM,CAAA;AAoBnD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAA;AAmDxC;;;GAGG;AACH,KAAK,UAAU,GAAG;IAChB,WAAW,EAAE,MAAM,OAAO,CAAC;QAAE,OAAO,EAAE;YAAE,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,CAAA;SAAE,CAAA;KAAE,CAAC,CAAA;IACxE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;IACzC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;IACvC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;CAChD,CAAA;AAED,wBAAsB,QAAQ,CAC5B,UAAU,EAAE,GAAG,CAAC,aAAa,EAC7B,SAAS,EAAE,GAAG,CAAC,SAAS,EACxB,GAAG,EAAE,IAAI,EACT,OAAO,CAAC,EAAE;IACR,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,OAAO,EAAE,GAAG,CAAA;KAAE,KAAK,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAA;IAC5E,UAAU,CAAC,EAAE,UAAU,CAAA;CACxB,iBAw7BF"}
1
+ {"version":3,"file":"oneServe.d.ts","sourceRoot":"","sources":["../../src/server/oneServe.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAqB,MAAM,MAAM,CAAA;AAoBnD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAA;AAmDxC;;;GAGG;AACH,KAAK,UAAU,GAAG;IAChB,WAAW,EAAE,MAAM,OAAO,CAAC;QAAE,OAAO,EAAE;YAAE,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,CAAA;SAAE,CAAA;KAAE,CAAC,CAAA;IACxE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;IACzC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;IACvC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;CAChD,CAAA;AAED,wBAAsB,QAAQ,CAC5B,UAAU,EAAE,GAAG,CAAC,aAAa,EAC7B,SAAS,EAAE,GAAG,CAAC,SAAS,EACxB,GAAG,EAAE,IAAI,EACT,OAAO,CAAC,EAAE;IACR,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,OAAO,EAAE,GAAG,CAAA;KAAE,KAAK,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAA;IAC5E,UAAU,CAAC,EAAE,UAAU,CAAA;CACxB,iBAs7BF"}
@@ -1 +1 @@
1
- {"version":3,"file":"workerHandler.d.ts","sourceRoot":"","sources":["../../src/server/workerHandler.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAA;AAKxC,MAAM,MAAM,UAAU,GAAG;IACvB,WAAW,EAAE,MAAM,OAAO,CAAC;QACzB,OAAO,EAAE;YACP,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,CAAA;YAC3B,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,CAAC,cAAc,CAAC,CAAA;SACvD,CAAA;KACF,CAAC,CAAA;IACF,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;IACzC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;IACvC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;CAChD,CAAA;AAED,KAAK,oBAAoB,GAAG;IAC1B,UAAU,EAAE,GAAG,CAAC,aAAa,CAAA;IAC7B,SAAS,EAAE,GAAG,CAAC,SAAS,CAAA;IACxB,UAAU,EAAE,UAAU,CAAA;CACvB,CAAA;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB;6BAghBpD,OAAO,QACV,OAAO,iBACE,OAAO,KACrB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;iCA0MS,GAAG,CAAC,SAAS,kBAAkB,UAAU;EAmB9E"}
1
+ {"version":3,"file":"workerHandler.d.ts","sourceRoot":"","sources":["../../src/server/workerHandler.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAA;AAKxC,MAAM,MAAM,UAAU,GAAG;IACvB,WAAW,EAAE,MAAM,OAAO,CAAC;QACzB,OAAO,EAAE;YACP,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,CAAA;YAC3B,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,CAAC,cAAc,CAAC,CAAA;SACvD,CAAA;KACF,CAAC,CAAA;IACF,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;IACzC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;IACvC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;CAChD,CAAA;AAED,KAAK,oBAAoB,GAAG;IAC1B,UAAU,EAAE,GAAG,CAAC,aAAa,CAAA;IAC7B,SAAS,EAAE,GAAG,CAAC,SAAS,CAAA;IACxB,UAAU,EAAE,UAAU,CAAA;CACvB,CAAA;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB;6BAghBpD,OAAO,QACV,OAAO,iBACE,OAAO,KACrB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;iCA0MS,GAAG,CAAC,SAAS,kBAAkB,UAAU;EAmB9E"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=workerHandler.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workerHandler.test.d.ts","sourceRoot":"","sources":["../../src/server/workerHandler.test.ts"],"names":[],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"ScrollBehavior.d.ts","sourceRoot":"","sources":["../../src/views/ScrollBehavior.tsx"],"names":[],"mappings":"AAgBA;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,cAKlD;AAqED,KAAK,mBAAmB,GAAG;IACzB,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAC9B,CAAA;AA2FD,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,QAcxD"}
1
+ {"version":3,"file":"ScrollBehavior.d.ts","sourceRoot":"","sources":["../../src/views/ScrollBehavior.tsx"],"names":[],"mappings":"AAaA;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,cAKlD;AAqED,KAAK,mBAAmB,GAAG;IACzB,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAC9B,CAAA;AAwGD,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,QAcxD"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ScrollBehavior.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScrollBehavior.test.d.ts","sourceRoot":"","sources":["../../src/views/ScrollBehavior.test.tsx"],"names":[],"mappings":""}