@wular/pnext 0.1.3 → 0.1.5

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 (44) hide show
  1. package/config/ts/react.json +2 -1
  2. package/package.json +1 -1
  3. package/reference/navigation.md +3 -1
  4. package/src/api/client-navigation.ts +53 -4
  5. package/src/api/link.tsx +6 -7
  6. package/src/cli/adapters/vercel-warm.ts +6 -1
  7. package/src/cli/adapters/vercel.ts +1 -1
  8. package/src/cli/build/cache.ts +8 -3
  9. package/src/cli/build.ts +3 -2
  10. package/src/cli/serve/pipeline.ts +11 -3
  11. package/src/cli/typegen.ts +65 -3
  12. package/src/client/build.ts +10 -7
  13. package/src/client/entry.ts +50 -29
  14. package/src/client/reference-stub.ts +1 -1
  15. package/src/client/router/hub.ts +17 -9
  16. package/src/client/router/page-slot.ts +58 -0
  17. package/src/client/router/runtime.ts +387 -388
  18. package/src/compat/bundler/symlink-imports.ts +1 -1
  19. package/src/compat/client/errors/bare-boundary.ts +1 -1
  20. package/src/compat/client/errors/error-boundary.ts +1 -1
  21. package/src/compat/client/errors/global-error.ts +40 -79
  22. package/src/compat/client/errors/install.ts +1 -1
  23. package/src/compat/client/errors/lazy.ts +7 -0
  24. package/src/compat/client/errors/primitive-throw.ts +36 -53
  25. package/src/compat/client/segment-cache-policy.ts +5 -22
  26. package/src/compat/client/segment-cache.ts +7 -26
  27. package/src/compat/css/modules.ts +1 -7
  28. package/src/compat/css/sass.ts +1 -7
  29. package/src/compat/react/parity.ts +1 -1
  30. package/src/compat/register/bundler.ts +9 -1
  31. package/src/compat/tsconfig-defaults.ts +4 -7
  32. package/src/compat-bootstrap.ts +15 -0
  33. package/src/css/build.ts +18 -15
  34. package/src/dev/server.ts +2 -2
  35. package/src/index.ts +2 -0
  36. package/src/render/renderer.ts +209 -24
  37. package/src/render/resource-hints.ts +4 -1
  38. package/src/resolve/imports.ts +13 -8
  39. package/src/routing/routes.ts +32 -18
  40. package/src/runtime/loader.ts +29 -10
  41. package/src/runtime/modules.ts +28 -7
  42. package/src/runtime/vendor-build.ts +2 -2
  43. package/src/types.ts +2 -0
  44. package/src/utils/fs.ts +122 -0
@@ -14,24 +14,32 @@ import type { EntryModule } from './types'
14
14
  // region (install, soft nav, popstate), so it lives on one record: a module-level `let` can only be
15
15
  // reassigned inside the module that declares it. State only the runtime touches stays in ./runtime.
16
16
 
17
- export const routerState = {
18
- // pathname+search of the URL the router last observed. A popstate that leaves both unchanged is a
19
- // same-document FRAGMENT traversal - Chrome fires popstate, not just hashchange, for those - and
20
- // treating one as a history traversal would refetch and swap the body, remounting the live tree
21
- // under the user's feet.
22
- observedLocationKey: locationKey(),
17
+ export const routerState: {
18
+ observedLocationKey: string
23
19
  // The route key (pathname+search) of the currently committed navigation, used
24
20
  // as the departing key on popstate (where `location` already points at the
25
21
  // target). Seeded on router init and updated at each committed navigation.
26
- activeRouteKey: undefined as string | undefined,
22
+ activeRouteKey?: string
27
23
  // The entry id whose document is currently on screen. A traversal to that
28
24
  // same id — an app-level shallow pushState now carries the id forward — needs
29
25
  // no fetch and no swap: the DOM already is this entry's.
30
- renderedEntryId: undefined as string | undefined,
26
+ renderedEntryId?: string
27
+ /** The navigation that last painted a fallback, and when. */
28
+ fallbackPaint?: [sequence: number, at: number]
29
+ /** Ends the clicked link's pending state; set once the runtime installs. */
30
+ settleLink?: () => void
31
+ } = {
32
+ // pathname+search of the URL the router last observed. A popstate that leaves both unchanged is a
33
+ // same-document FRAGMENT traversal - Chrome fires popstate, not just hashchange, for those - and
34
+ // treating one as a history traversal would refetch and swap the body, remounting the live tree
35
+ // under the user's feet.
36
+ observedLocationKey: locationKey(),
31
37
  }
32
38
 
33
39
  export function locationKey() {
34
- return typeof location === 'undefined' ? '' : location.pathname + location.search
40
+ return process.browser || typeof location !== 'undefined'
41
+ ? location.pathname + location.search
42
+ : ''
35
43
  }
36
44
 
37
45
  // Installers that only the navigation runtime ever observes — compat's policy
@@ -27,6 +27,64 @@ export function elementInPageSlot(element: Element): boolean {
27
27
  )
28
28
  }
29
29
 
30
+ const SEGMENT = 'pnext-layout[data-pnext-segment]'
31
+
32
+ /** A server layout's identity and the URL prefix it rendered for. */
33
+ export function segmentKey(segment: Element): string {
34
+ return `${segment.getAttribute('data-pnext-segment')}\u0001${segment.getAttribute('data-pnext-scope')}`
35
+ }
36
+
37
+ /** The server layout directly below `segment`, outside parallel-route slots. */
38
+ export function childSegment(segment: Element): Element | null {
39
+ for (const child of segment.querySelectorAll(SEGMENT))
40
+ if (!segment.contains(child.closest('[data-pnext-slot]'))) return child
41
+ return null
42
+ }
43
+
44
+ /**
45
+ * The live layout below the deepest layout `boundary` shares with `slot` (null when that layout
46
+ * holds `slot`), and the incoming node that replaces it: the boundary, or the new layout holding
47
+ * it. Null when either sits in an island.
48
+ */
49
+ export function divergingLayout(slot: Node, boundary: Element): [Element | null, Element] | null {
50
+ let paint = boundary
51
+ let owner = boundary.closest(SEGMENT)
52
+ let below: Element | null
53
+ for (;;) {
54
+ const key = owner && segmentKey(owner)
55
+ below = null
56
+ let layout = slot.parentElement?.closest(SEGMENT)
57
+ for (; layout && segmentKey(layout) !== key; layout = layout.parentElement?.closest(SEGMENT))
58
+ below = layout
59
+ if (layout || !owner) break
60
+ paint = owner
61
+ owner = owner.parentElement!.closest(SEGMENT)
62
+ }
63
+ return !boundary.closest('pnext-client') &&
64
+ !(below ?? slot).parentElement?.closest('pnext-client')
65
+ ? [below, paint]
66
+ : null
67
+ }
68
+
69
+ function childNodes(segment: Element): ChildNode[] | null {
70
+ const child = childSegment(segment)
71
+ if (child) return [child]
72
+ const range = pageSlotRange(segment)
73
+ return range && (range[1] ? [range[0], ...range[2], range[1]] : [range[0]])
74
+ }
75
+
76
+ /** Replace a kept layout's children (its child layout or page slot) with the incoming ones. */
77
+ export function graftSegmentChildren(liveSegment: Element, incomingSegment: Element): boolean {
78
+ if (!childSegment(liveSegment) && !childSegment(incomingSegment))
79
+ return graftPageSlot(liveSegment, incomingSegment)
80
+ const live = childNodes(liveSegment)
81
+ const incoming = childNodes(incomingSegment)
82
+ if (!live || !incoming) return false
83
+ live[0]!.before(...incoming)
84
+ for (const stale of live) stale.remove()
85
+ return true
86
+ }
87
+
30
88
  /**
31
89
  * Graft a destination page into a preserved layout without changing the destination's mount shape.
32
90
  * Client pages need the real `#pnext-page` element; server/island-only pages need the marker range.