@remix-run/ui 0.4.0 → 0.6.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/README.md +74 -1
- package/dist/accordion/index.js +2 -2
- package/dist/accordion/primitives.d.ts +1 -1
- package/dist/accordion/primitives.js.map +1 -1
- package/dist/anchor/index.js.map +1 -1
- package/dist/animation/animate-layout-mixin.js +1 -1
- package/dist/animation/animate-layout-mixin.js.map +1 -1
- package/dist/animation/animate-mixins.js +2 -2
- package/dist/animation/animate-mixins.js.map +1 -1
- package/dist/animation/demos/color-interpolation.js +1 -1
- package/dist/animation/demos/color-interpolation.js.map +1 -1
- package/dist/animation/demos/drag-release.d.ts +1 -1
- package/dist/animation/demos/hold-to-confirm.d.ts +3 -3
- package/dist/animation/demos/material-ripple.js +1 -3
- package/dist/animation/demos/material-ripple.js.map +1 -1
- package/dist/animation/index.js +4 -4
- package/dist/animation/spring.js.map +1 -1
- package/dist/breadcrumbs/index.js +2 -2
- package/dist/button/index.js +25 -25
- package/dist/button/index.js.map +1 -1
- package/dist/checkbox/index.js +11 -9
- package/dist/checkbox/index.js.map +1 -1
- package/dist/combobox/index.js +4 -4
- package/dist/combobox/index.js.map +1 -1
- package/dist/combobox/primitives.d.ts +1 -1
- package/dist/combobox/primitives.js +5 -5
- package/dist/combobox/primitives.js.map +1 -1
- package/dist/dev/refresh.d.ts +1 -0
- package/dist/dev/refresh.js +2 -0
- package/dist/dev/refresh.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +14 -14
- package/dist/input/index.js +9 -9
- package/dist/input/index.js.map +1 -1
- package/dist/jsx-runtime.js +2 -2
- package/dist/listbox/index.js +2 -2
- package/dist/menu/hover-aim.js.map +1 -1
- package/dist/menu/index.js +3 -3
- package/dist/menu/primitives.d.ts +1 -1
- package/dist/menu/primitives.js +6 -6
- package/dist/menu/primitives.js.map +1 -1
- package/dist/popover/index.js +3 -3
- package/dist/popover/scroll-lock.js.map +1 -1
- package/dist/radio/index.js +7 -7
- package/dist/radio/index.js.map +1 -1
- package/dist/runtime/client-entry-boundary.d.ts +14 -0
- package/dist/runtime/client-entry-boundary.js +29 -0
- package/dist/runtime/client-entry-boundary.js.map +1 -0
- package/dist/runtime/component.d.ts +19 -4
- package/dist/runtime/component.js +39 -9
- package/dist/runtime/component.js.map +1 -1
- package/dist/runtime/core/attributes.js +36 -3
- package/dist/runtime/core/attributes.js.map +1 -1
- package/dist/runtime/core/children.d.ts +2 -0
- package/dist/runtime/core/children.js +26 -2
- package/dist/runtime/core/children.js.map +1 -1
- package/dist/runtime/core/props.js +2 -2
- package/dist/runtime/core/vnode.d.ts +3 -3
- package/dist/runtime/core/vnode.js +22 -1
- package/dist/runtime/core/vnode.js.map +1 -1
- package/dist/runtime/create-element.d.ts +2 -2
- package/dist/runtime/create-element.js +3 -2
- package/dist/runtime/create-element.js.map +1 -1
- package/dist/runtime/diff-dom.js +307 -220
- package/dist/runtime/diff-dom.js.map +1 -1
- package/dist/runtime/diff-props.js +3 -3
- package/dist/runtime/dom.d.ts +12 -0
- package/dist/runtime/element-function.d.ts +3 -0
- package/dist/runtime/element-function.js +2 -0
- package/dist/runtime/element-function.js.map +1 -0
- package/dist/runtime/form-navigation.d.ts +25 -0
- package/dist/runtime/form-navigation.js +132 -0
- package/dist/runtime/form-navigation.js.map +1 -0
- package/dist/runtime/frame-resolution.d.ts +5 -0
- package/dist/runtime/frame-resolution.js +10 -0
- package/dist/runtime/frame-resolution.js.map +1 -0
- package/dist/runtime/frame.d.ts +59 -15
- package/dist/runtime/frame.js +383 -114
- package/dist/runtime/frame.js.map +1 -1
- package/dist/runtime/jsx.d.ts +1 -1
- package/dist/runtime/jsx.js +1 -1
- package/dist/runtime/jsx.js.map +1 -1
- package/dist/runtime/key.d.ts +1 -0
- package/dist/runtime/key.js +2 -0
- package/dist/runtime/key.js.map +1 -0
- package/dist/runtime/mixins/attrs-mixin.js +2 -2
- package/dist/runtime/mixins/link-mixin.js +4 -3
- package/dist/runtime/mixins/link-mixin.js.map +1 -1
- package/dist/runtime/mixins/mixin.d.ts +15 -10
- package/dist/runtime/mixins/mixin.js +4 -4
- package/dist/runtime/mixins/mixin.js.map +1 -1
- package/dist/runtime/mixins/on-mixin.js +1 -1
- package/dist/runtime/mixins/ref-mixin.js +1 -1
- package/dist/runtime/module-preloader.d.ts +8 -0
- package/dist/runtime/module-preloader.js +95 -0
- package/dist/runtime/module-preloader.js.map +1 -0
- package/dist/runtime/navigation.d.ts +4 -1
- package/dist/runtime/navigation.js +157 -32
- package/dist/runtime/navigation.js.map +1 -1
- package/dist/runtime/reconcile.d.ts +11 -10
- package/dist/runtime/reconcile.js +549 -421
- package/dist/runtime/reconcile.js.map +1 -1
- package/dist/runtime/refresh.d.ts +28 -0
- package/dist/runtime/refresh.js +31 -0
- package/dist/runtime/refresh.js.map +1 -0
- package/dist/runtime/render.js +1 -1
- package/dist/runtime/render.js.map +1 -1
- package/dist/runtime/run.d.ts +3 -1
- package/dist/runtime/run.js +15 -7
- package/dist/runtime/run.js.map +1 -1
- package/dist/runtime/scheduler.js +8 -16
- package/dist/runtime/scheduler.js.map +1 -1
- package/dist/runtime/to-vnode.d.ts +2 -2
- package/dist/runtime/to-vnode.js +68 -20
- package/dist/runtime/to-vnode.js.map +1 -1
- package/dist/runtime/vdom.d.ts +4 -2
- package/dist/runtime/vdom.js +87 -42
- package/dist/runtime/vdom.js.map +1 -1
- package/dist/runtime/vnode.d.ts +138 -67
- package/dist/runtime/vnode.js +17 -12
- package/dist/runtime/vnode.js.map +1 -1
- package/dist/select/index.js +11 -11
- package/dist/select/index.js.map +1 -1
- package/dist/select/primitives.d.ts +1 -1
- package/dist/select/primitives.js +6 -6
- package/dist/server/stream.d.ts +2 -0
- package/dist/server/stream.js +74 -29
- package/dist/server/stream.js.map +1 -1
- package/dist/shared/focus-styles.d.ts +1 -1
- package/dist/shared/focus-styles.js +1 -1
- package/dist/shared/focus-styles.js.map +1 -1
- package/dist/shared/listbox-popover-styles.js +1 -1
- package/dist/shared/style-values.js +26 -26
- package/dist/shared/style-values.js.map +1 -1
- package/dist/style/css-mixin.js +3 -3
- package/dist/style/index.js +2 -2
- package/dist/style/style.js +25 -6
- package/dist/style/style.js.map +1 -1
- package/dist/style/stylesheet.d.ts +0 -1
- package/dist/style/stylesheet.js +13 -54
- package/dist/style/stylesheet.js.map +1 -1
- package/dist/tabs/index.js +11 -11
- package/dist/tabs/index.js.map +1 -1
- package/dist/tabs/primitives.d.ts +1 -1
- package/dist/test.js +1 -1
- package/dist/toggle/index.js +9 -9
- package/dist/toggle/index.js.map +1 -1
- package/dist/toggle/primitives.d.ts +1 -1
- package/package.json +10 -7
- package/src/animation/animate-mixins.ts +3 -3
- package/src/animation/demos/color-interpolation.tsx +1 -1
- package/src/button/index.ts +24 -24
- package/src/checkbox/index.ts +10 -8
- package/src/combobox/index.tsx +1 -1
- package/src/dev/refresh.ts +1 -0
- package/src/index.ts +1 -1
- package/src/input/index.ts +8 -8
- package/src/radio/index.ts +6 -5
- package/src/runtime/client-entry-boundary.ts +54 -0
- package/src/runtime/component.ts +65 -16
- package/src/runtime/core/attributes.ts +35 -1
- package/src/runtime/core/children.ts +24 -2
- package/src/runtime/core/vnode.ts +25 -4
- package/src/runtime/create-element.ts +6 -5
- package/src/runtime/demos/readme.demo.tsx +4 -4
- package/src/runtime/diff-dom.ts +370 -244
- package/src/runtime/dom.ts +331 -214
- package/src/runtime/element-function.ts +1 -0
- package/src/runtime/event-listeners.ts +1 -1
- package/src/runtime/form-navigation.ts +184 -0
- package/src/runtime/frame-resolution.ts +13 -0
- package/src/runtime/frame.ts +500 -124
- package/src/runtime/jsx.ts +3 -5
- package/src/runtime/key.ts +1 -0
- package/src/runtime/mixins/link-mixin.ts +1 -0
- package/src/runtime/mixins/mixin.ts +21 -18
- package/src/runtime/module-preloader.ts +115 -0
- package/src/runtime/navigation.ts +204 -30
- package/src/runtime/reconcile.ts +762 -759
- package/src/runtime/refresh.ts +46 -0
- package/src/runtime/run.ts +12 -2
- package/src/runtime/scheduler.ts +9 -29
- package/src/runtime/to-vnode.ts +85 -23
- package/src/runtime/vdom.ts +89 -70
- package/src/runtime/vnode.ts +192 -88
- package/src/select/index.tsx +9 -9
- package/src/server/README.md +13 -0
- package/src/server/stream.ts +93 -30
- package/src/shared/focus-styles.ts +1 -1
- package/src/shared/style-values.ts +26 -26
- package/src/style/style.ts +25 -6
- package/src/style/stylesheet.ts +32 -57
- package/src/tabs/index.tsx +12 -11
- package/src/test/README.md +151 -60
- package/src/toggle/index.tsx +9 -7
package/src/runtime/vnode.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import type { ComponentHandle,
|
|
2
|
-
import { Fragment, Frame } from './component.ts'
|
|
1
|
+
import type { ComponentHandle, Fragment, Frame, FrameHandle, FrameProps } from './component.ts'
|
|
3
2
|
import { isRemixElement } from './core/vnode.ts'
|
|
4
|
-
import type {
|
|
3
|
+
import type { Frame as FrameInstance } from './frame.ts'
|
|
4
|
+
import type { RemixNode } from './jsx.ts'
|
|
5
|
+
import type { ElementFunction } from './element-function.ts'
|
|
6
|
+
import type { Key } from './key.ts'
|
|
7
|
+
import type { MixinRuntimeState, MixinRuntimeValue } from './mixins/mixin.ts'
|
|
8
|
+
import type { OnMixinDescriptor } from './mixins/on-mixin.ts'
|
|
9
|
+
import type { Scheduler } from './scheduler.ts'
|
|
10
|
+
import type { StyleManager } from '../style/index.ts'
|
|
5
11
|
|
|
6
12
|
export { isRemixElement }
|
|
7
13
|
|
|
@@ -9,136 +15,234 @@ export const TEXT_NODE = Symbol('TEXT_NODE')
|
|
|
9
15
|
export const NON_RENDER_NODE = Symbol('NON_RENDER_NODE')
|
|
10
16
|
export const ROOT_VNODE = Symbol('ROOT_VNODE')
|
|
11
17
|
|
|
12
|
-
export type
|
|
13
|
-
| typeof ROOT_VNODE
|
|
14
|
-
| string // host element
|
|
15
|
-
| Function // component
|
|
16
|
-
| typeof TEXT_NODE
|
|
17
|
-
| typeof NON_RENDER_NODE
|
|
18
|
-
| typeof Fragment
|
|
19
|
-
| typeof Frame
|
|
20
|
-
|
|
21
|
-
export type VNode<T extends VNodeType = VNodeType> = {
|
|
22
|
-
type: T
|
|
23
|
-
props?: ElementProps
|
|
24
|
-
_mixedProps?: ElementProps
|
|
25
|
-
key?: string
|
|
26
|
-
|
|
27
|
-
// _prefixes assigned during reconciliation
|
|
28
|
-
_parent?: VNode
|
|
29
|
-
_children?: VNode[]
|
|
30
|
-
_dom?: unknown
|
|
31
|
-
_controller?: AbortController
|
|
32
|
-
_mixState?: unknown
|
|
33
|
-
_directEventDescriptors?: unknown
|
|
34
|
-
_directEventState?: unknown
|
|
35
|
-
_controlledState?: unknown
|
|
36
|
-
_svg?: boolean
|
|
37
|
-
// Range roots render between comment boundary markers
|
|
38
|
-
_rangeStart?: Node
|
|
39
|
-
_rangeEnd?: Node
|
|
40
|
-
_pendingHydrationComponentId?: string
|
|
41
|
-
_frameInstance?: unknown
|
|
42
|
-
_frameFallbackRoot?: { render: (element: RemixNode) => void; dispose: () => void }
|
|
43
|
-
_frameResolveToken?: number
|
|
44
|
-
_frameResolveController?: AbortController
|
|
45
|
-
_frameResolved?: boolean
|
|
18
|
+
export type VNodeKind = 'root' | 'empty' | 'text' | 'fragment' | 'host' | 'component' | 'frame'
|
|
46
19
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
// Component
|
|
51
|
-
_handle?: ComponentHandle
|
|
52
|
-
_id?: string
|
|
53
|
-
_content?: VNode
|
|
20
|
+
export type RuntimeElementProps = {
|
|
21
|
+
[name: string]: unknown
|
|
22
|
+
}
|
|
54
23
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
24
|
+
export type RuntimeHostProps = RuntimeElementProps & {
|
|
25
|
+
children?: RemixNode
|
|
26
|
+
innerHTML?: string
|
|
27
|
+
mix?: MixinRuntimeValue
|
|
59
28
|
}
|
|
60
29
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
30
|
+
type InputNodeBase<kind extends VNodeKind, type> = {
|
|
31
|
+
kind: kind
|
|
32
|
+
type: type
|
|
33
|
+
key?: Key
|
|
34
|
+
_parent?: never
|
|
64
35
|
}
|
|
65
36
|
|
|
66
|
-
export type
|
|
67
|
-
|
|
37
|
+
export type NonRenderNode = InputNodeBase<'empty', typeof NON_RENDER_NODE>
|
|
38
|
+
|
|
39
|
+
export type TextNode = InputNodeBase<'text', typeof TEXT_NODE> & {
|
|
68
40
|
_text: string
|
|
69
41
|
}
|
|
70
42
|
|
|
71
|
-
export type
|
|
72
|
-
|
|
43
|
+
export type FragmentNode = InputNodeBase<'fragment', typeof Fragment> & {
|
|
44
|
+
_children: VNodeInput[]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type HostNode = InputNodeBase<'host', string> & {
|
|
48
|
+
props: RuntimeHostProps
|
|
49
|
+
_children: VNodeInput[]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type ComponentNode = InputNodeBase<'component', ElementFunction> & {
|
|
53
|
+
props: RuntimeElementProps
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type FrameNode = InputNodeBase<'frame', typeof Frame> & {
|
|
57
|
+
props: RuntimeElementProps & FrameProps
|
|
73
58
|
}
|
|
74
59
|
|
|
75
|
-
export type
|
|
60
|
+
export type VNodeInput =
|
|
61
|
+
| NonRenderNode
|
|
62
|
+
| TextNode
|
|
63
|
+
| FragmentNode
|
|
64
|
+
| HostNode
|
|
65
|
+
| ComponentNode
|
|
66
|
+
| FrameNode
|
|
67
|
+
|
|
68
|
+
type MountedNodeBase = {
|
|
69
|
+
_parent: VNodeParent
|
|
70
|
+
_svg: boolean
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
type CommittedNodeBase<kind extends VNodeKind, type> = {
|
|
74
|
+
kind: kind
|
|
75
|
+
type: type
|
|
76
|
+
key?: Key
|
|
77
|
+
} & MountedNodeBase
|
|
78
|
+
|
|
79
|
+
export type CommittedNonRenderNode = CommittedNodeBase<'empty', typeof NON_RENDER_NODE>
|
|
80
|
+
|
|
81
|
+
export type CommittedTextNode = CommittedNodeBase<'text', typeof TEXT_NODE> & {
|
|
82
|
+
_text: string
|
|
76
83
|
_dom: Text
|
|
77
84
|
}
|
|
78
85
|
|
|
79
|
-
export type
|
|
86
|
+
export type ControlledReflectionState = {
|
|
87
|
+
disposed: boolean
|
|
88
|
+
listenersAttached: boolean
|
|
89
|
+
pendingRestoreVersion: number
|
|
90
|
+
managesValue: boolean
|
|
91
|
+
managesChecked: boolean
|
|
92
|
+
hasControlledValue: boolean
|
|
93
|
+
controlledValue: unknown
|
|
94
|
+
hasControlledChecked: boolean
|
|
95
|
+
controlledChecked: unknown
|
|
96
|
+
onInput(): void
|
|
97
|
+
onChange(): void
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export type DirectEventBinding = {
|
|
80
101
|
type: string
|
|
81
|
-
|
|
82
|
-
|
|
102
|
+
handler: (event: Event, signal: AbortSignal) => void | Promise<void>
|
|
103
|
+
capture: boolean
|
|
104
|
+
stableHandler: ((event: Event) => void) | null
|
|
105
|
+
reentry: AbortController | null
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export type DirectEventState = {
|
|
109
|
+
bindings: DirectEventBinding[]
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export type HostPersistenceState = {
|
|
113
|
+
parent: ParentNode
|
|
114
|
+
token: number
|
|
83
115
|
}
|
|
84
116
|
|
|
85
|
-
export type CommittedHostNode =
|
|
117
|
+
export type CommittedHostNode = CommittedNodeBase<'host', string> & {
|
|
118
|
+
props: RuntimeHostProps
|
|
119
|
+
_children: CommittedVNode[]
|
|
86
120
|
_dom: Element
|
|
87
|
-
|
|
121
|
+
_mixedProps: RuntimeHostProps
|
|
122
|
+
_mixState?: MixinRuntimeState
|
|
123
|
+
_directEventDescriptors?: OnMixinDescriptor[]
|
|
124
|
+
_directEventState?: DirectEventState
|
|
125
|
+
_controlledState?: ControlledReflectionState
|
|
126
|
+
_persistence?: HostPersistenceState
|
|
88
127
|
}
|
|
89
128
|
|
|
90
|
-
export type
|
|
91
|
-
|
|
92
|
-
props: ElementProps
|
|
93
|
-
_handle: ComponentHandle
|
|
129
|
+
export type CommittedFragmentNode = CommittedNodeBase<'fragment', typeof Fragment> & {
|
|
130
|
+
_children: CommittedVNode[]
|
|
94
131
|
}
|
|
95
132
|
|
|
96
|
-
export type
|
|
97
|
-
|
|
98
|
-
props: ElementProps
|
|
99
|
-
_content: VNode
|
|
133
|
+
export type MountingComponentNode = CommittedNodeBase<'component', ElementFunction> & {
|
|
134
|
+
props: RuntimeElementProps
|
|
100
135
|
_handle: ComponentHandle
|
|
136
|
+
_context: ReconcileContext
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export type CommittedComponentNode = MountingComponentNode & {
|
|
140
|
+
_content: CommittedVNode
|
|
101
141
|
}
|
|
102
142
|
|
|
103
|
-
export
|
|
104
|
-
|
|
143
|
+
export interface FrameFallbackRoot {
|
|
144
|
+
render(element: RemixNode): void
|
|
145
|
+
dispose(): void
|
|
105
146
|
}
|
|
106
147
|
|
|
107
|
-
export
|
|
108
|
-
|
|
148
|
+
export type FrameMountState = {
|
|
149
|
+
instance: FrameInstance
|
|
150
|
+
fallbackRoot?: FrameFallbackRoot
|
|
151
|
+
resolveToken: number
|
|
152
|
+
resolveController?: AbortController
|
|
153
|
+
resolved: boolean
|
|
109
154
|
}
|
|
110
155
|
|
|
111
|
-
export
|
|
112
|
-
|
|
156
|
+
export type CommittedFrameNode = CommittedNodeBase<'frame', typeof Frame> & {
|
|
157
|
+
props: RuntimeElementProps & FrameProps
|
|
158
|
+
_rangeStart: Comment
|
|
159
|
+
_rangeEnd: Comment
|
|
160
|
+
_state: FrameMountState
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export type CommittedVNode =
|
|
164
|
+
| CommittedNonRenderNode
|
|
165
|
+
| CommittedTextNode
|
|
166
|
+
| CommittedFragmentNode
|
|
167
|
+
| CommittedHostNode
|
|
168
|
+
| CommittedComponentNode
|
|
169
|
+
| CommittedFrameNode
|
|
170
|
+
|
|
171
|
+
export type RootVNode = {
|
|
172
|
+
kind: 'root'
|
|
173
|
+
type: typeof ROOT_VNODE
|
|
174
|
+
_children: CommittedVNode[]
|
|
175
|
+
_svg: boolean
|
|
176
|
+
_rangeStart?: Node
|
|
177
|
+
_rangeEnd?: Node
|
|
178
|
+
_pendingHydrationComponentId?: string
|
|
113
179
|
}
|
|
114
180
|
|
|
115
|
-
export
|
|
116
|
-
|
|
181
|
+
export interface ReconcileContext {
|
|
182
|
+
frame: FrameHandle
|
|
183
|
+
scheduler: Scheduler
|
|
184
|
+
styles: StyleManager
|
|
185
|
+
rootTarget: EventTarget
|
|
117
186
|
}
|
|
118
187
|
|
|
119
|
-
export
|
|
120
|
-
|
|
188
|
+
export type VNodeParent =
|
|
189
|
+
| RootVNode
|
|
190
|
+
| CommittedFragmentNode
|
|
191
|
+
| CommittedHostNode
|
|
192
|
+
| MountingComponentNode
|
|
193
|
+
| CommittedComponentNode
|
|
194
|
+
|
|
195
|
+
export type VNode = VNodeInput | CommittedVNode | RootVNode | MountingComponentNode
|
|
196
|
+
|
|
197
|
+
export function isFragmentNode(node: VNode): node is FragmentNode | CommittedFragmentNode {
|
|
198
|
+
return node.kind === 'fragment'
|
|
121
199
|
}
|
|
122
200
|
|
|
123
|
-
export function
|
|
124
|
-
return
|
|
201
|
+
export function isTextNode(node: VNode): node is TextNode | CommittedTextNode {
|
|
202
|
+
return node.kind === 'text'
|
|
125
203
|
}
|
|
126
204
|
|
|
127
|
-
export function
|
|
128
|
-
return
|
|
205
|
+
export function isNonRenderNode(node: VNode): node is NonRenderNode | CommittedNonRenderNode {
|
|
206
|
+
return node.kind === 'empty'
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function isCommittedTextNode(node: CommittedVNode): node is CommittedTextNode {
|
|
210
|
+
return node.kind === 'text'
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function isHostNode(node: VNode): node is HostNode | CommittedHostNode {
|
|
214
|
+
return node.kind === 'host'
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function isCommittedHostNode(node: CommittedVNode): node is CommittedHostNode {
|
|
218
|
+
return node.kind === 'host'
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export function isComponentNode(
|
|
222
|
+
node: VNode,
|
|
223
|
+
): node is ComponentNode | MountingComponentNode | CommittedComponentNode {
|
|
224
|
+
return node.kind === 'component'
|
|
129
225
|
}
|
|
130
226
|
|
|
131
227
|
export function isCommittedComponentNode(node: VNode): node is CommittedComponentNode {
|
|
132
|
-
return
|
|
228
|
+
return node.kind === 'component' && '_content' in node
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export function isFrameNode(node: VNode): node is FrameNode | CommittedFrameNode {
|
|
232
|
+
return node.kind === 'frame'
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function isCommittedFrameNode(node: CommittedVNode): node is CommittedFrameNode {
|
|
236
|
+
return node.kind === 'frame'
|
|
133
237
|
}
|
|
134
238
|
|
|
135
|
-
export function findContextFromAncestry(node:
|
|
136
|
-
let current:
|
|
239
|
+
export function findContextFromAncestry(node: VNodeParent, type: ElementFunction): unknown {
|
|
240
|
+
let current: VNodeParent | undefined = node
|
|
137
241
|
while (current) {
|
|
138
|
-
if (current.
|
|
242
|
+
if (current.kind === 'component' && current.type === type) {
|
|
139
243
|
return current._handle.getContextValue()
|
|
140
244
|
}
|
|
141
|
-
current = current._parent
|
|
245
|
+
current = '_parent' in current ? current._parent : undefined
|
|
142
246
|
}
|
|
143
247
|
return undefined
|
|
144
248
|
}
|
package/src/select/index.tsx
CHANGED
|
@@ -30,10 +30,10 @@ export type SelectOptionProps = Props<'div'> & {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
const selectTriggerShadow =
|
|
33
|
-
'0 2px 3px -1px rgba(0, 0, 0, 0.04), 0 3px 4px -1.5px rgba(0, 0, 0, 0.04), 0 4px 5px -2px rgba(0, 0, 0, 0.04), 0 0 0 1px rgba(0, 0, 0, 0.12)'
|
|
33
|
+
'0 2px 3px -1px rgba(0, 0, 0, 0.04), 0 3px 4px -1.5px rgba(0, 0, 0, 0.04), 0 4px 5px -2px rgba(0, 0, 0, 0.04), 0 0 0 1px light-dark(rgba(0, 0, 0, 0.12), rgba(255, 255, 255, 0.2))'
|
|
34
34
|
|
|
35
35
|
const selectTriggerFocusShadow =
|
|
36
|
-
'0 2px 3px -1px rgba(0, 0, 0, 0.04), 0 3px 4px -1.5px rgba(0, 0, 0, 0.04), 0 4px 5px -2px rgba(0, 0, 0, 0.04), 0 0 0 1px #3573F6, 0 0 0 4px rgba(53, 115, 246, 0.1), 0 6px 32px 4px rgba(53, 115, 246, 0.08), inset 0 0 8px 1px rgba(53, 115, 246, 0.05)'
|
|
36
|
+
'0 2px 3px -1px rgba(0, 0, 0, 0.04), 0 3px 4px -1.5px rgba(0, 0, 0, 0.04), 0 4px 5px -2px rgba(0, 0, 0, 0.04), 0 0 0 1px light-dark(#3573F6, #6eaaff), 0 0 0 4px light-dark(rgba(53, 115, 246, 0.1), rgba(110, 170, 255, 0.18)), 0 6px 32px 4px light-dark(rgba(53, 115, 246, 0.08), rgba(110, 170, 255, 0.14)), inset 0 0 8px 1px light-dark(rgba(53, 115, 246, 0.05), rgba(110, 170, 255, 0.1))'
|
|
37
37
|
|
|
38
38
|
const selectTriggerCss: CSSMixinDescriptor = css({
|
|
39
39
|
appearance: 'none',
|
|
@@ -51,9 +51,9 @@ const selectTriggerCss: CSSMixinDescriptor = css({
|
|
|
51
51
|
gap: '6px',
|
|
52
52
|
border: 0,
|
|
53
53
|
borderRadius: '8px',
|
|
54
|
-
background: '#FFFFFF',
|
|
54
|
+
background: 'light-dark(#FFFFFF, #1a1a1a)',
|
|
55
55
|
boxShadow: selectTriggerShadow,
|
|
56
|
-
color: '#101010',
|
|
56
|
+
color: 'light-dark(#101010, #ececec)',
|
|
57
57
|
fontFamily: '"Inter Variable", Inter, ui-sans-serif, system-ui, sans-serif',
|
|
58
58
|
fontStyle: 'normal',
|
|
59
59
|
fontWeight: 400,
|
|
@@ -62,15 +62,15 @@ const selectTriggerCss: CSSMixinDescriptor = css({
|
|
|
62
62
|
fontFeatureSettings: '"ss01" on, "cv01" on',
|
|
63
63
|
letterSpacing: 0,
|
|
64
64
|
textAlign: 'left',
|
|
65
|
-
textShadow: '0 1px 0 #FFFFFF',
|
|
65
|
+
textShadow: '0 1px 0 light-dark(#FFFFFF, rgb(0 0 0 / 0.35))',
|
|
66
66
|
whiteSpace: 'nowrap',
|
|
67
67
|
'&:hover, &:focus-visible, &[aria-expanded="true"], &[aria-expanded="true"]:hover, &[aria-expanded="true"]:focus-visible':
|
|
68
68
|
{
|
|
69
|
-
background: '#FFFFFF',
|
|
70
|
-
color: '#101010',
|
|
69
|
+
background: 'light-dark(#FFFFFF, #1a1a1a)',
|
|
70
|
+
color: 'light-dark(#101010, #ececec)',
|
|
71
71
|
},
|
|
72
72
|
'&:active': {
|
|
73
|
-
background: '#FFFFFF',
|
|
73
|
+
background: 'light-dark(#FFFFFF, #1a1a1a)',
|
|
74
74
|
},
|
|
75
75
|
'&:focus-visible': {
|
|
76
76
|
outline: 0,
|
|
@@ -99,7 +99,7 @@ const triggerIconCss: CSSMixinDescriptor = css({
|
|
|
99
99
|
justifyContent: 'center',
|
|
100
100
|
width: '16px',
|
|
101
101
|
height: '16px',
|
|
102
|
-
color: '#707070',
|
|
102
|
+
color: 'light-dark(#707070, #b3b3b3)',
|
|
103
103
|
flex: 'none',
|
|
104
104
|
'& > svg': {
|
|
105
105
|
display: 'block',
|
package/src/server/README.md
CHANGED
|
@@ -23,6 +23,7 @@ Renders a component tree to a streaming response. The initial HTML is sent immed
|
|
|
23
23
|
|
|
24
24
|
```tsx
|
|
25
25
|
import { renderToStream } from 'remix/ui/server'
|
|
26
|
+
import { assetServer } from './assets.ts'
|
|
26
27
|
|
|
27
28
|
let stream = renderToStream(<App />, {
|
|
28
29
|
frameSrc: request.url,
|
|
@@ -31,6 +32,17 @@ let stream = renderToStream(<App />, {
|
|
|
31
32
|
let frameUrl = new URL(src, context?.currentFrameSrc ?? request.url)
|
|
32
33
|
return fetchHtml(frameUrl)
|
|
33
34
|
},
|
|
35
|
+
async resolveClientEntry(entryId, component) {
|
|
36
|
+
let [href, preloads] = await Promise.all([
|
|
37
|
+
assetServer.getHref(entryId),
|
|
38
|
+
assetServer.getPreloads(entryId),
|
|
39
|
+
])
|
|
40
|
+
return {
|
|
41
|
+
href,
|
|
42
|
+
exportName: entryId.split('#')[1] || component.name,
|
|
43
|
+
preloads,
|
|
44
|
+
}
|
|
45
|
+
},
|
|
34
46
|
onError(error) {
|
|
35
47
|
console.error(error)
|
|
36
48
|
},
|
|
@@ -47,6 +59,7 @@ return new Response(stream, {
|
|
|
47
59
|
- **`topFrameSrc`** - Overrides the root frame URL used for `handle.frames.top.src`. This is mainly useful when calling `renderToStream()` from inside `resolveFrame()` for a nested frame render.
|
|
48
60
|
- **`signal`** - Cancels pending server rendering work. Pass `request.signal` so client disconnects can stop unresolved frame work without invoking `onError` for the disconnect itself.
|
|
49
61
|
- **`resolveFrame(src, target, context)`** - Called when a `<Frame>` needs its content. Return a string of HTML, a `ReadableStream<Uint8Array>`, or a promise of either. `context.currentFrameSrc` is the URL for the frame that contains the `<Frame>`, and `context.topFrameSrc` is the outer document URL. Required if your component tree contains `<Frame>` elements.
|
|
62
|
+
- **`resolveClientEntry(entryId, component)`** - Resolves the public module URL, export name, and optional module preload hrefs for a hydrated client entry.
|
|
50
63
|
- **`onError(error)`** - Called when a rendering error occurs. If not provided, the stream rejects with the error.
|
|
51
64
|
|
|
52
65
|
When you render nested frame responses with `renderToStream()` inside `resolveFrame()`, pass `frameSrc` for the frame being rendered and carry `topFrameSrc` forward from the parent context. That preserves `handle.frames.top.src` across the whole SSR frame tree.
|
package/src/server/stream.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ComponentHandle, FrameHandle, Key, RemixNode } from '../runtime/component.ts'
|
|
2
2
|
import type { ElementType, ElementProps, RemixElement } from '../runtime/jsx.ts'
|
|
3
|
+
import type { ElementFunction } from '../runtime/element-function.ts'
|
|
3
4
|
import { Fragment, createComponent, createFrameHandle, Frame } from '../runtime/component.ts'
|
|
4
5
|
import { isEntry, type EntryComponent } from '../runtime/client-entries.ts'
|
|
5
6
|
import {
|
|
@@ -76,6 +77,8 @@ interface UnresolvedHydrationData {
|
|
|
76
77
|
interface ResolvedClientEntry {
|
|
77
78
|
href: string
|
|
78
79
|
exportName: string
|
|
80
|
+
/** Browser module hrefs to begin preloading before hydrating this entry. */
|
|
81
|
+
preloads?: readonly string[]
|
|
79
82
|
}
|
|
80
83
|
|
|
81
84
|
interface FrameData {
|
|
@@ -89,7 +92,6 @@ interface RenderContext {
|
|
|
89
92
|
onError: (error: unknown) => void
|
|
90
93
|
parentVNode?: VNode
|
|
91
94
|
styleCache: Map<string, { selector: string; css: string }>
|
|
92
|
-
emittedStyles: Set<string>
|
|
93
95
|
resolveFrame: (
|
|
94
96
|
src: string,
|
|
95
97
|
target?: string,
|
|
@@ -99,6 +101,7 @@ interface RenderContext {
|
|
|
99
101
|
hydrationData: Map<string, HydrationData>
|
|
100
102
|
unresolvedHydrationData: Map<string, UnresolvedHydrationData>
|
|
101
103
|
frameData: Map<string, FrameData>
|
|
104
|
+
modulePreloadTags: Set<string>
|
|
102
105
|
blockingFrameTails: ReadableStream<Uint8Array>[]
|
|
103
106
|
signal: AbortSignal
|
|
104
107
|
flushKind: FlushKind
|
|
@@ -203,11 +206,11 @@ export function renderToStream(
|
|
|
203
206
|
onError,
|
|
204
207
|
resolveFrame: options?.resolveFrame ?? defaultResolveFrame,
|
|
205
208
|
styleCache: new Map(),
|
|
206
|
-
emittedStyles: new Set(),
|
|
207
209
|
pendingFrames: [],
|
|
208
210
|
hydrationData: new Map(),
|
|
209
211
|
unresolvedHydrationData: new Map(),
|
|
210
212
|
frameData: new Map(),
|
|
213
|
+
modulePreloadTags: new Set(),
|
|
211
214
|
blockingFrameTails: [],
|
|
212
215
|
signal: renderAbortController.signal,
|
|
213
216
|
flushKind: 'fragment',
|
|
@@ -436,7 +439,7 @@ function buildSegment(node: RemixNode, context: RenderContext, frameState: SsrFr
|
|
|
436
439
|
return buildElementSegment(tag, props, context, frameState)
|
|
437
440
|
}
|
|
438
441
|
|
|
439
|
-
if (
|
|
442
|
+
if (isElementFunction(type)) {
|
|
440
443
|
if (type === Frame) {
|
|
441
444
|
return buildFrameSegment(node, context, frameState)
|
|
442
445
|
}
|
|
@@ -493,6 +496,7 @@ function buildFrameSegment(
|
|
|
493
496
|
context.resolveFrame(props.src, props.name, resolveFrameContext),
|
|
494
497
|
).then(async (resolved) => {
|
|
495
498
|
let { html, tail } = await resolveFrameHtml(resolved)
|
|
499
|
+
html = hoistModulePreloadsFromFrameHead(html, context)
|
|
496
500
|
seg.content = staticSeg(html)
|
|
497
501
|
if (tail) {
|
|
498
502
|
context.blockingFrameTails.push(tail)
|
|
@@ -788,8 +792,8 @@ function sanitizeReturnedSsrMixinProps(props: ElementProps): ElementProps {
|
|
|
788
792
|
|
|
789
793
|
function resolveReturnedSsrMixDescriptors(
|
|
790
794
|
value: unknown,
|
|
791
|
-
): Array<{ type:
|
|
792
|
-
let descriptors: Array<{ type:
|
|
795
|
+
): Array<{ type: ElementFunction; args: unknown[] }> | null {
|
|
796
|
+
let descriptors: Array<{ type: ElementFunction; args: unknown[] }> = []
|
|
793
797
|
if (!collectReturnedSsrMixDescriptors(value, descriptors)) {
|
|
794
798
|
return null
|
|
795
799
|
}
|
|
@@ -799,7 +803,7 @@ function resolveReturnedSsrMixDescriptors(
|
|
|
799
803
|
|
|
800
804
|
function collectReturnedSsrMixDescriptors(
|
|
801
805
|
value: unknown,
|
|
802
|
-
output: Array<{ type:
|
|
806
|
+
output: Array<{ type: ElementFunction; args: unknown[] }>,
|
|
803
807
|
): boolean {
|
|
804
808
|
if (!value) {
|
|
805
809
|
return true
|
|
@@ -829,7 +833,11 @@ function isSsrMixinElement(
|
|
|
829
833
|
return '__rmxMixinElementType' in value
|
|
830
834
|
}
|
|
831
835
|
|
|
832
|
-
function
|
|
836
|
+
function isElementFunction(value: unknown): value is ElementFunction {
|
|
837
|
+
return typeof value === 'function'
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
function isSsrMixinDescriptor(value: unknown): value is { type: ElementFunction; args: unknown[] } {
|
|
833
841
|
if (!value || typeof value !== 'object' || isRemixElement(value)) {
|
|
834
842
|
return false
|
|
835
843
|
}
|
|
@@ -839,7 +847,7 @@ function isSsrMixinDescriptor(value: unknown): value is { type: Function; args:
|
|
|
839
847
|
}
|
|
840
848
|
|
|
841
849
|
function buildComponentSegment(
|
|
842
|
-
type:
|
|
850
|
+
type: ElementFunction,
|
|
843
851
|
props: any,
|
|
844
852
|
context: RenderContext,
|
|
845
853
|
componentId: string,
|
|
@@ -923,7 +931,7 @@ function createHydrationPropsReplacer(context: RenderContext, frameState: SsrFra
|
|
|
923
931
|
}
|
|
924
932
|
|
|
925
933
|
// Component function: render synchronously, then unwrap its result
|
|
926
|
-
if (
|
|
934
|
+
if (isElementFunction(type)) {
|
|
927
935
|
let vnode = createVNode(type, props)
|
|
928
936
|
if (context.parentVNode) {
|
|
929
937
|
vnode._parent = context.parentVNode
|
|
@@ -1070,6 +1078,10 @@ async function resolveClientEntries(
|
|
|
1070
1078
|
moduleUrl: resolvedEntry.href,
|
|
1071
1079
|
props,
|
|
1072
1080
|
})
|
|
1081
|
+
|
|
1082
|
+
for (let preload of resolvedEntry.preloads ?? []) {
|
|
1083
|
+
context.modulePreloadTags.add(createModulePreloadTag(preload))
|
|
1084
|
+
}
|
|
1073
1085
|
}
|
|
1074
1086
|
|
|
1075
1087
|
context.unresolvedHydrationData.clear()
|
|
@@ -1092,6 +1104,19 @@ function validateResolvedClientEntry(
|
|
|
1092
1104
|
if (!resolvedEntry.exportName) {
|
|
1093
1105
|
throw new Error(`resolveClientEntry must return a non-empty exportName. Received "${entryId}".`)
|
|
1094
1106
|
}
|
|
1107
|
+
|
|
1108
|
+
if (resolvedEntry.preloads !== undefined) {
|
|
1109
|
+
if (!Array.isArray(resolvedEntry.preloads)) {
|
|
1110
|
+
throw new Error(`resolveClientEntry preloads must be an array. Received "${entryId}".`)
|
|
1111
|
+
}
|
|
1112
|
+
for (let preload of resolvedEntry.preloads) {
|
|
1113
|
+
if (typeof preload !== 'string' || preload.length === 0) {
|
|
1114
|
+
throw new Error(
|
|
1115
|
+
`resolveClientEntry preloads must contain non-empty strings. Received "${entryId}".`,
|
|
1116
|
+
)
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1095
1120
|
}
|
|
1096
1121
|
|
|
1097
1122
|
function validateClientEntriesForHydration(context: RenderContext): void {
|
|
@@ -1156,9 +1181,10 @@ function transformAttributeName(name: string, isSvg: boolean): string {
|
|
|
1156
1181
|
function finalizeHtml(html: string, context: RenderContext): string {
|
|
1157
1182
|
let hasHtmlRoot = context.flushKind === 'document'
|
|
1158
1183
|
|
|
1184
|
+
let preloads = collectModulePreloadTags(context)
|
|
1159
1185
|
let styles = collectStyleTags(context)
|
|
1160
|
-
if (styles) {
|
|
1161
|
-
let headContent = styles
|
|
1186
|
+
if (preloads || styles) {
|
|
1187
|
+
let headContent = preloads + styles
|
|
1162
1188
|
if (hasHtmlRoot) {
|
|
1163
1189
|
// For HTML root, inject into existing head or create one
|
|
1164
1190
|
let headCloseIndex = html.indexOf('</head>')
|
|
@@ -1180,8 +1206,6 @@ function finalizeHtml(html: string, context: RenderContext): string {
|
|
|
1180
1206
|
}
|
|
1181
1207
|
}
|
|
1182
1208
|
|
|
1183
|
-
html = dedupeServerStyleTagsInHtml(html, context.emittedStyles)
|
|
1184
|
-
|
|
1185
1209
|
// Append aggregated hydration/frame data script at the end
|
|
1186
1210
|
let rmxData = buildRmxDataScript(context)
|
|
1187
1211
|
if (rmxData) {
|
|
@@ -1206,6 +1230,52 @@ function finalizeHtml(html: string, context: RenderContext): string {
|
|
|
1206
1230
|
return html
|
|
1207
1231
|
}
|
|
1208
1232
|
|
|
1233
|
+
const FRAME_HEAD_OPEN_TAG = '<head>'
|
|
1234
|
+
const FRAME_HEAD_CLOSE_TAG = '</head>'
|
|
1235
|
+
const MARKED_MODULE_PRELOAD_START = '<link data-rmx rel="modulepreload" href="'
|
|
1236
|
+
const MODULE_PRELOAD_END = '" />'
|
|
1237
|
+
|
|
1238
|
+
function createModulePreloadTag(href: string): string {
|
|
1239
|
+
return `${MARKED_MODULE_PRELOAD_START}${escapeHtml(href)}${MODULE_PRELOAD_END}`
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
function collectModulePreloadTags(context: RenderContext): string {
|
|
1243
|
+
return Array.from(context.modulePreloadTags).join('')
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
function hoistModulePreloadsFromFrameHead(html: string, context: RenderContext): string {
|
|
1247
|
+
if (!html.startsWith(FRAME_HEAD_OPEN_TAG)) return html
|
|
1248
|
+
|
|
1249
|
+
let tags: string[] = []
|
|
1250
|
+
let remainingHeadStart = FRAME_HEAD_OPEN_TAG.length
|
|
1251
|
+
while (html.startsWith(MARKED_MODULE_PRELOAD_START, remainingHeadStart)) {
|
|
1252
|
+
let tagEnd = html.indexOf(
|
|
1253
|
+
MODULE_PRELOAD_END,
|
|
1254
|
+
remainingHeadStart + MARKED_MODULE_PRELOAD_START.length,
|
|
1255
|
+
)
|
|
1256
|
+
if (tagEnd === -1) return html
|
|
1257
|
+
|
|
1258
|
+
tagEnd += MODULE_PRELOAD_END.length
|
|
1259
|
+
tags.push(html.slice(remainingHeadStart, tagEnd))
|
|
1260
|
+
remainingHeadStart = tagEnd
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
if (tags.length === 0) return html
|
|
1264
|
+
|
|
1265
|
+
let headClose = html.indexOf(FRAME_HEAD_CLOSE_TAG, remainingHeadStart)
|
|
1266
|
+
if (headClose === -1) return html
|
|
1267
|
+
|
|
1268
|
+
for (let tag of tags) {
|
|
1269
|
+
context.modulePreloadTags.add(tag)
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
if (remainingHeadStart === headClose) {
|
|
1273
|
+
return html.slice(headClose + FRAME_HEAD_CLOSE_TAG.length)
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
return FRAME_HEAD_OPEN_TAG + html.slice(remainingHeadStart)
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1209
1279
|
function processStyleProps(props: any): any {
|
|
1210
1280
|
let processedProps = { ...props }
|
|
1211
1281
|
let classAttr = typeof props.class === 'string' ? props.class : ''
|
|
@@ -1252,24 +1322,12 @@ function renderStyleTag(
|
|
|
1252
1322
|
): string {
|
|
1253
1323
|
let wrappedCss = wrapStyleForLayer(selector, css, layer)
|
|
1254
1324
|
if (!wrappedCss) return ''
|
|
1255
|
-
return `<style data-rmx="${escapeHtml(selector)}">${wrappedCss}</style>`
|
|
1325
|
+
return `<style data-rmx="${escapeHtml(selector)}">${escapeStyleText(wrappedCss)}</style>`
|
|
1256
1326
|
}
|
|
1257
1327
|
|
|
1258
|
-
function
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
return match[1] ?? match[2] ?? null
|
|
1262
|
-
}
|
|
1263
|
-
|
|
1264
|
-
function dedupeServerStyleTagsInHtml(html: string, seenStyles: Set<string>): string {
|
|
1265
|
-
return html.replace(/<style\b([^>]*)>[\s\S]*?<\/style>/gi, (match, attrs) => {
|
|
1266
|
-
let selector = readStyleTagAttribute(attrs, 'data-rmx')
|
|
1267
|
-
if (!selector) return match
|
|
1268
|
-
|
|
1269
|
-
if (seenStyles.has(selector)) return ''
|
|
1270
|
-
seenStyles.add(selector)
|
|
1271
|
-
return match
|
|
1272
|
-
})
|
|
1328
|
+
function escapeStyleText(css: string): string {
|
|
1329
|
+
// A literal "</style" closes an HTML style element even when it appears inside a CSS string.
|
|
1330
|
+
return css.replace(/</g, '\\3C ')
|
|
1273
1331
|
}
|
|
1274
1332
|
|
|
1275
1333
|
function buildRmxDataScript(context: RenderContext): string {
|
|
@@ -1303,6 +1361,12 @@ function escapeScriptJson(json: string): string {
|
|
|
1303
1361
|
// the handler's `finalizeHtml` emits selector-addressed `<style>` tags in its HTML, and on the client,
|
|
1304
1362
|
// the `adoptServerStyleTag` MutationObserver (stylesheet.ts) picks it up anywhere in the
|
|
1305
1363
|
// document and adopts the CSS into an adopted stylesheet.
|
|
1364
|
+
//
|
|
1365
|
+
// Style tags are intentionally NOT deduped across frame boundaries: each frame
|
|
1366
|
+
// owns its style rules independently on the client (per-frame refcounted
|
|
1367
|
+
// adoption), so every frame's HTML must carry the full set of style tags its
|
|
1368
|
+
// content references — even when a sibling frame or the enclosing document
|
|
1369
|
+
// already emitted the same selector.
|
|
1306
1370
|
async function streamPendingFrames(
|
|
1307
1371
|
context: RenderContext,
|
|
1308
1372
|
controller: ReadableStreamDefaultController,
|
|
@@ -1323,7 +1387,6 @@ async function streamPendingFrames(
|
|
|
1323
1387
|
try {
|
|
1324
1388
|
let { html, tail } = await promise
|
|
1325
1389
|
if (context.signal.aborted) return
|
|
1326
|
-
html = dedupeServerStyleTagsInHtml(html, context.emittedStyles)
|
|
1327
1390
|
|
|
1328
1391
|
// Stream as a template element (first chunk only)
|
|
1329
1392
|
let templateHtml = `<template id="${frameId}">${escapeTemplateContent(html)}</template>`
|