@tanstack/redact 0.0.21 → 0.1.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/dist/core/attributes.js +2 -1
- package/dist/core/attributes.js.map +2 -2
- package/dist/core/browser.d.ts +9 -0
- package/dist/core/browser.js +35 -0
- package/dist/core/browser.js.map +7 -0
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +3 -3
- package/dist/core/internal.d.ts +31 -8
- package/dist/core/internal.js +3 -1
- package/dist/core/internal.js.map +2 -2
- package/dist/core/resource-hints.d.ts +7 -0
- package/dist/core/resource-hints.js +19 -0
- package/dist/core/resource-hints.js.map +7 -0
- package/dist/core/types.js +1 -1
- package/dist/dom/_all.d.ts +11 -16
- package/dist/dom/_all.js +9 -20
- package/dist/dom/_all.js.map +2 -2
- package/dist/dom/client.d.ts +4 -4
- package/dist/dom/client.js +4 -4
- package/dist/dom/commit.d.ts +23 -0
- package/dist/dom/commit.js +191 -0
- package/dist/dom/commit.js.map +7 -0
- package/dist/dom/dispatcher.d.ts +4 -22
- package/dist/dom/dispatcher.js +184 -126
- package/dist/dom/dispatcher.js.map +3 -3
- package/dist/dom/dom.d.ts +2 -0
- package/dist/dom/dom.js +72 -52
- package/dist/dom/dom.js.map +2 -2
- package/dist/dom/error-info.d.ts +2 -0
- package/dist/dom/error-info.js +16 -0
- package/dist/dom/error-info.js.map +7 -0
- package/dist/dom/event-replay.js +1 -1
- package/dist/dom/features/activity/full.d.ts +1 -0
- package/dist/dom/features/activity/full.js +162 -0
- package/dist/dom/features/activity/full.js.map +7 -0
- package/dist/dom/features/activity/index.d.ts +1 -0
- package/dist/dom/features/activity/index.js +3 -0
- package/dist/dom/features/activity/index.js.map +7 -0
- package/dist/dom/features/activity/stub.d.ts +1 -0
- package/dist/dom/features/activity/stub.js +12 -0
- package/dist/dom/features/activity/stub.js.map +7 -0
- package/dist/dom/features/class/full.js +87 -27
- package/dist/dom/features/class/full.js.map +2 -2
- package/dist/dom/features/class/index.d.ts +1 -1
- package/dist/dom/features/class/index.js +2 -2
- package/dist/dom/features/class/stub.js +3 -3
- package/dist/dom/features/context/full.d.ts +4 -1
- package/dist/dom/features/context/full.js +72 -15
- package/dist/dom/features/context/full.js.map +2 -2
- package/dist/dom/features/context/index.d.ts +1 -1
- package/dist/dom/features/context/index.js +2 -2
- package/dist/dom/features/context/stub.d.ts +4 -1
- package/dist/dom/features/context/stub.js +17 -8
- package/dist/dom/features/context/stub.js.map +2 -2
- package/dist/dom/features/forward-ref/full.js +12 -21
- package/dist/dom/features/forward-ref/full.js.map +2 -2
- package/dist/dom/features/forward-ref/index.d.ts +1 -1
- package/dist/dom/features/forward-ref/index.js +2 -2
- package/dist/dom/features/forward-ref/stub.js +4 -4
- package/dist/dom/features/fragment-refs/index.d.ts +1 -0
- package/dist/dom/features/fragment-refs/index.js +8 -0
- package/dist/dom/features/fragment-refs/index.js.map +7 -0
- package/dist/dom/features/fragment-refs/stub.d.ts +3 -0
- package/dist/dom/features/fragment-refs/stub.js +14 -0
- package/dist/dom/features/fragment-refs/stub.js.map +7 -0
- package/dist/dom/features/hydration/full.d.ts +11 -6
- package/dist/dom/features/hydration/full.js +76 -77
- package/dist/dom/features/hydration/full.js.map +2 -2
- package/dist/dom/features/hydration/index.d.ts +1 -1
- package/dist/dom/features/hydration/index.js +2 -2
- package/dist/dom/features/hydration/stub.d.ts +7 -2
- package/dist/dom/features/hydration/stub.js +1 -1
- package/dist/dom/features/hydration/stub.js.map +1 -1
- package/dist/dom/features/index.d.ts +8 -7
- package/dist/dom/features/index.js +9 -8
- package/dist/dom/features/index.js.map +2 -2
- package/dist/dom/features/lazy/full.js +4 -4
- package/dist/dom/features/lazy/index.d.ts +1 -1
- package/dist/dom/features/lazy/index.js +2 -2
- package/dist/dom/features/lazy/stub.js +4 -4
- package/dist/dom/features/memo/full.js +8 -6
- package/dist/dom/features/memo/full.js.map +2 -2
- package/dist/dom/features/memo/index.d.ts +1 -1
- package/dist/dom/features/memo/index.js +2 -2
- package/dist/dom/features/memo/stub.js +4 -4
- package/dist/dom/features/portal/full.js +4 -4
- package/dist/dom/features/portal/index.d.ts +1 -1
- package/dist/dom/features/portal/index.js +2 -2
- package/dist/dom/features/portal/stub.js +4 -4
- package/dist/dom/features/suspense/full.js +197 -52
- package/dist/dom/features/suspense/full.js.map +2 -2
- package/dist/dom/features/suspense/index.d.ts +1 -1
- package/dist/dom/features/suspense/index.js +2 -2
- package/dist/dom/features/suspense/stub.js +4 -4
- package/dist/dom/features/view-transition/full.d.ts +6 -0
- package/dist/dom/features/view-transition/full.js +280 -0
- package/dist/dom/features/view-transition/full.js.map +7 -0
- package/dist/dom/features/view-transition/index.d.ts +1 -0
- package/dist/dom/features/view-transition/index.js +3 -0
- package/dist/dom/features/view-transition/index.js.map +7 -0
- package/dist/dom/features/view-transition/resources.d.ts +11 -0
- package/dist/dom/features/view-transition/resources.js +108 -0
- package/dist/dom/features/view-transition/resources.js.map +7 -0
- package/dist/dom/features/view-transition/stub.d.ts +6 -0
- package/dist/dom/features/view-transition/stub.js +22 -0
- package/dist/dom/features/view-transition/stub.js.map +7 -0
- package/dist/dom/features/view-transition/visual.d.ts +14 -0
- package/dist/dom/features/view-transition/visual.js +356 -0
- package/dist/dom/features/view-transition/visual.js.map +7 -0
- package/dist/dom/fragment-instance.d.ts +40 -0
- package/dist/dom/fragment-instance.js +324 -0
- package/dist/dom/fragment-instance.js.map +7 -0
- package/dist/dom/index.d.ts +11 -11
- package/dist/dom/index.js +11 -18
- package/dist/dom/index.js.map +3 -3
- package/dist/dom/portal.d.ts +2 -2
- package/dist/dom/portal.js +2 -2
- package/dist/dom/portal.js.map +1 -1
- package/dist/dom/reconcile.d.ts +32 -3
- package/dist/dom/reconcile.js +689 -221
- package/dist/dom/reconcile.js.map +2 -2
- package/dist/dom/resource-hints.d.ts +6 -0
- package/dist/dom/resource-hints.js +115 -0
- package/dist/dom/resource-hints.js.map +7 -0
- package/dist/dom/resource-store.d.ts +13 -0
- package/dist/dom/resource-store.js +43 -0
- package/dist/dom/resource-store.js.map +7 -0
- package/dist/dom/resources.d.ts +1 -0
- package/dist/dom/resources.js +44 -0
- package/dist/dom/resources.js.map +7 -0
- package/dist/dom/retained-effects.d.ts +6 -0
- package/dist/dom/retained-effects.js +49 -0
- package/dist/dom/retained-effects.js.map +7 -0
- package/dist/dom/root-internal.d.ts +4 -4
- package/dist/dom/root-internal.js +2 -2
- package/dist/dom/root-internal.js.map +2 -2
- package/dist/dom/root.d.ts +5 -5
- package/dist/dom/root.js +11 -11
- package/dist/dom/root.js.map +2 -2
- package/dist/dom/test-utils.js +1 -1
- package/dist/react/activity.d.ts +7 -0
- package/dist/react/activity.js +8 -0
- package/dist/react/activity.js.map +7 -0
- package/dist/react/children.d.ts +1 -1
- package/dist/react/children.js +3 -3
- package/dist/react/children.js.map +1 -1
- package/dist/react/class.d.ts +1 -1
- package/dist/react/class.js +3 -3
- package/dist/react/class.js.map +1 -1
- package/dist/react/compiler-runtime.js +2 -2
- package/dist/react/context.d.ts +7 -2
- package/dist/react/context.js +3 -11
- package/dist/react/context.js.map +3 -3
- package/dist/react/element.d.ts +6 -3
- package/dist/react/element.js +7 -11
- package/dist/react/element.js.map +2 -2
- package/dist/react/hooks.d.ts +26 -7
- package/dist/react/hooks.js +13 -3
- package/dist/react/hooks.js.map +2 -2
- package/dist/react/index.d.ts +39 -24
- package/dist/react/index.js +39 -19
- package/dist/react/index.js.map +3 -3
- package/dist/react/jsx-runtime.d.ts +2 -2
- package/dist/react/jsx-runtime.js +3 -3
- package/dist/react/memo.d.ts +1 -1
- package/dist/react/memo.js +1 -1
- package/dist/react/portal.js +1 -1
- package/dist/react/shared-internals.d.ts +15 -5
- package/dist/react/shared-internals.js +2 -3
- package/dist/react/shared-internals.js.map +2 -2
- package/dist/react/suspense.js +1 -1
- package/dist/react/view-transition.d.ts +32 -0
- package/dist/react/view-transition.js +13 -0
- package/dist/react/view-transition.js.map +7 -0
- package/dist/scheduler/index.js +1 -1
- package/dist/server/bootstrap-script.d.ts +1 -23
- package/dist/server/bootstrap-script.js +7 -6
- package/dist/server/bootstrap-script.js.map +2 -2
- package/dist/server/dispatcher.d.ts +28 -6
- package/dist/server/dispatcher.js +33 -15
- package/dist/server/dispatcher.js.map +2 -2
- package/dist/server/escape.js +16 -15
- package/dist/server/escape.js.map +2 -2
- package/dist/server/index.d.ts +6 -6
- package/dist/server/index.js +7 -7
- package/dist/server/renderToString.d.ts +1 -1
- package/dist/server/renderToString.js +12 -8
- package/dist/server/renderToString.js.map +2 -2
- package/dist/server/resource-hints.d.ts +22 -0
- package/dist/server/resource-hints.js +148 -0
- package/dist/server/resource-hints.js.map +7 -0
- package/dist/server/stream.d.ts +15 -2
- package/dist/server/stream.js +155 -63
- package/dist/server/stream.js.map +2 -2
- package/dist/server/walk.d.ts +15 -2
- package/dist/server/walk.js +150 -110
- package/dist/server/walk.js.map +2 -2
- package/dist/vite/index.d.ts +6 -0
- package/dist/vite/index.js +22 -6
- package/dist/vite/index.js.map +2 -2
- package/package.json +1 -1
- package/src/core/attributes.ts +1 -0
- package/src/core/browser.ts +33 -0
- package/src/core/internal.ts +24 -6
- package/src/core/resource-hints.ts +36 -0
- package/src/dom/_all.ts +1 -7
- package/src/dom/commit.ts +186 -0
- package/src/dom/dispatcher.ts +190 -134
- package/src/dom/dom.ts +70 -54
- package/src/dom/error-info.ts +15 -0
- package/src/dom/features/activity/full.ts +166 -0
- package/src/dom/features/activity/index.ts +1 -0
- package/src/dom/features/activity/stub.ts +14 -0
- package/src/dom/features/class/full.ts +72 -28
- package/src/dom/features/context/full.ts +70 -13
- package/src/dom/features/context/stub.ts +10 -5
- package/src/dom/features/forward-ref/full.ts +9 -19
- package/src/dom/features/fragment-refs/index.ts +1 -0
- package/src/dom/features/fragment-refs/stub.ts +3 -0
- package/src/dom/features/hydration/full.ts +84 -85
- package/src/dom/features/hydration/stub.ts +1 -1
- package/src/dom/features/index.ts +1 -0
- package/src/dom/features/memo/full.ts +4 -3
- package/src/dom/features/suspense/full.ts +192 -55
- package/src/dom/features/view-transition/full.ts +288 -0
- package/src/dom/features/view-transition/index.ts +1 -0
- package/src/dom/features/view-transition/resources.ts +107 -0
- package/src/dom/features/view-transition/stub.ts +7 -0
- package/src/dom/features/view-transition/visual.ts +406 -0
- package/src/dom/fragment-instance.ts +337 -0
- package/src/dom/index.ts +6 -7
- package/src/dom/portal.ts +1 -1
- package/src/dom/reconcile.ts +695 -242
- package/src/dom/resource-hints.ts +115 -0
- package/src/dom/resource-store.ts +52 -0
- package/src/dom/resources.ts +39 -0
- package/src/dom/retained-effects.ts +38 -0
- package/src/dom/root-internal.ts +4 -4
- package/src/dom/root.ts +12 -14
- package/src/react/activity.ts +10 -0
- package/src/react/context.ts +4 -11
- package/src/react/element.ts +11 -12
- package/src/react/hooks.ts +12 -3
- package/src/react/index.ts +18 -0
- package/src/react/shared-internals.ts +3 -3
- package/src/react/view-transition.ts +41 -0
- package/src/server/bootstrap-script.ts +36 -5
- package/src/server/dispatcher.ts +35 -13
- package/src/server/escape.ts +15 -12
- package/src/server/renderToString.ts +7 -5
- package/src/server/resource-hints.ts +165 -0
- package/src/server/stream.ts +154 -62
- package/src/server/walk.ts +79 -26
- package/src/vite/index.ts +30 -13
package/src/server/walk.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from '../core'
|
|
8
8
|
import {
|
|
9
9
|
REACT_SUSPENSE_TYPE,
|
|
10
|
+
REACT_CONTEXT_TYPE,
|
|
10
11
|
REACT_PROVIDER_TYPE,
|
|
11
12
|
REACT_CONSUMER_TYPE,
|
|
12
13
|
REACT_FORWARD_REF_TYPE,
|
|
@@ -15,6 +16,8 @@ import {
|
|
|
15
16
|
REACT_STRICT_MODE_TYPE,
|
|
16
17
|
REACT_PROFILER_TYPE,
|
|
17
18
|
REACT_PORTAL_TYPE,
|
|
19
|
+
REACT_ACTIVITY_TYPE,
|
|
20
|
+
REACT_VIEW_TRANSITION_TYPE,
|
|
18
21
|
} from '../react'
|
|
19
22
|
import {
|
|
20
23
|
attrToHtml,
|
|
@@ -27,8 +30,19 @@ import {
|
|
|
27
30
|
pushContext,
|
|
28
31
|
popContext,
|
|
29
32
|
snapshotContexts,
|
|
33
|
+
recordHostResource,
|
|
34
|
+
currentSSRFrame,
|
|
30
35
|
type ContextSnapshot,
|
|
31
36
|
} from './dispatcher'
|
|
37
|
+
import { isBrowserError } from '../core/browser'
|
|
38
|
+
import { resourceKind } from '../core/resource-hints'
|
|
39
|
+
import type { ResourceScope } from './resource-hints'
|
|
40
|
+
|
|
41
|
+
export interface ServerErrorInfo {
|
|
42
|
+
componentStack: string
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type BrowserBailoutCallback = (error: Error, errorInfo: ServerErrorInfo) => void
|
|
32
46
|
|
|
33
47
|
export interface SuspendedBoundary {
|
|
34
48
|
id: number
|
|
@@ -36,15 +50,21 @@ export interface SuspendedBoundary {
|
|
|
36
50
|
children: ReactNode
|
|
37
51
|
thenable: Promise<any>
|
|
38
52
|
contextSnapshot: ContextSnapshot
|
|
53
|
+
componentStack: string
|
|
39
54
|
}
|
|
40
55
|
|
|
41
56
|
export interface WalkOptions {
|
|
42
57
|
emit: (chunk: string) => void
|
|
58
|
+
onHead?: () => void
|
|
43
59
|
onSuspend?: ((boundary: SuspendedBoundary) => void) | undefined
|
|
60
|
+
onBrowserBailout?: BrowserBailoutCallback | undefined
|
|
61
|
+
componentStack?: string | undefined
|
|
62
|
+
thrownInfo?: { componentStack?: string | undefined }
|
|
44
63
|
nextBoundaryId: () => number
|
|
45
64
|
bootstrapped?: boolean | undefined
|
|
46
65
|
isBoundaryResolution?: boolean | undefined
|
|
47
66
|
isSvg?: boolean | undefined
|
|
67
|
+
resourceScope?: ResourceScope | undefined
|
|
48
68
|
/**
|
|
49
69
|
* Tracks whether the most recent emission within the *current text flow*
|
|
50
70
|
* ended with a text node. When the next emission is also text, we emit a
|
|
@@ -150,6 +170,11 @@ function walkElement(el: ReactElement, opts: WalkOptions): void {
|
|
|
150
170
|
const type = el.type
|
|
151
171
|
const props = el.props ?? {}
|
|
152
172
|
|
|
173
|
+
if (typeof type === 'string') {
|
|
174
|
+
walkHost(type, props, opts)
|
|
175
|
+
return
|
|
176
|
+
}
|
|
177
|
+
|
|
153
178
|
if (type === REACT_FRAGMENT_TYPE || type === REACT_STRICT_MODE_TYPE || type === REACT_PROFILER_TYPE) {
|
|
154
179
|
walkNode(props.children, opts)
|
|
155
180
|
return
|
|
@@ -160,8 +185,12 @@ function walkElement(el: ReactElement, opts: WalkOptions): void {
|
|
|
160
185
|
return
|
|
161
186
|
}
|
|
162
187
|
|
|
163
|
-
if (
|
|
164
|
-
|
|
188
|
+
if (type === REACT_ACTIVITY_TYPE) {
|
|
189
|
+
if (props.mode !== 'hidden') walkNode(props.children, opts)
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
if (type === REACT_VIEW_TRANSITION_TYPE) {
|
|
193
|
+
walkNode(props.children, opts)
|
|
165
194
|
return
|
|
166
195
|
}
|
|
167
196
|
|
|
@@ -172,7 +201,7 @@ function walkElement(el: ReactElement, opts: WalkOptions): void {
|
|
|
172
201
|
return
|
|
173
202
|
}
|
|
174
203
|
|
|
175
|
-
if (marker === REACT_PROVIDER_TYPE) {
|
|
204
|
+
if (marker === REACT_CONTEXT_TYPE || marker === REACT_PROVIDER_TYPE) {
|
|
176
205
|
const ctx = (type as any)._context
|
|
177
206
|
pushContext(ctx, props.value)
|
|
178
207
|
try {
|
|
@@ -184,10 +213,9 @@ function walkElement(el: ReactElement, opts: WalkOptions): void {
|
|
|
184
213
|
}
|
|
185
214
|
|
|
186
215
|
if (marker === REACT_CONSUMER_TYPE) {
|
|
187
|
-
const ctx = (type as any)._context
|
|
188
216
|
const render = props.children
|
|
189
217
|
if (typeof render === 'function') {
|
|
190
|
-
walkNode(render(
|
|
218
|
+
walkNode(render((type as any)._context._currentValue), opts)
|
|
191
219
|
}
|
|
192
220
|
return
|
|
193
221
|
}
|
|
@@ -196,8 +224,7 @@ function walkElement(el: ReactElement, opts: WalkOptions): void {
|
|
|
196
224
|
const render = (type as any).render
|
|
197
225
|
const ref = (props as any).ref ?? null
|
|
198
226
|
const { ref: _omit, ...rest } = props as any
|
|
199
|
-
|
|
200
|
-
walkNode(rendered, opts)
|
|
227
|
+
walkComponent(render, rest, opts, ref)
|
|
201
228
|
return
|
|
202
229
|
}
|
|
203
230
|
|
|
@@ -209,16 +236,8 @@ function walkElement(el: ReactElement, opts: WalkOptions): void {
|
|
|
209
236
|
|
|
210
237
|
if (marker === REACT_LAZY_TYPE) {
|
|
211
238
|
const { _payload, _init } = type as any
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
walkElement({ ...el, type: resolved } as ReactElement, opts)
|
|
215
|
-
} catch (thenable: any) {
|
|
216
|
-
if (isThenable(thenable)) {
|
|
217
|
-
// Suspend this point
|
|
218
|
-
throw thenable
|
|
219
|
-
}
|
|
220
|
-
throw thenable
|
|
221
|
-
}
|
|
239
|
+
const resolved = _init(_payload)
|
|
240
|
+
walkElement({ ...el, type: resolved } as ReactElement, opts)
|
|
222
241
|
return
|
|
223
242
|
}
|
|
224
243
|
|
|
@@ -234,6 +253,7 @@ function walkHost(
|
|
|
234
253
|
opts: WalkOptions,
|
|
235
254
|
): void {
|
|
236
255
|
const isSvg = opts.isSvg || tag === 'svg'
|
|
256
|
+
if (!isSvg && resourceKind(tag, props) && recordHostResource(tag, props, opts.resourceScope)) return
|
|
237
257
|
// <textarea value="..."> serializes its value as a TEXT CHILD, not an
|
|
238
258
|
// attribute. `defaultValue` is the fallback when `value` is absent. This
|
|
239
259
|
// matches React and the HTML spec — `<textarea value="x">` is not valid
|
|
@@ -270,6 +290,7 @@ function walkHost(
|
|
|
270
290
|
pushSelectContext(val)
|
|
271
291
|
}
|
|
272
292
|
const isOption = tag === 'option'
|
|
293
|
+
try {
|
|
273
294
|
|
|
274
295
|
// Prepend the HTML5 doctype to the stream when rendering an <html> root.
|
|
275
296
|
// Without it the browser parses the document in quirks mode, which breaks
|
|
@@ -312,6 +333,7 @@ function walkHost(
|
|
|
312
333
|
return
|
|
313
334
|
}
|
|
314
335
|
opts.emit('>')
|
|
336
|
+
if (tag === 'head') opts.onHead?.()
|
|
315
337
|
// Opening a host element starts a fresh text flow context for its children.
|
|
316
338
|
// Children's text separator tracking is independent of the outer context.
|
|
317
339
|
const parentTextState = opts.textState
|
|
@@ -322,7 +344,9 @@ function walkHost(
|
|
|
322
344
|
}
|
|
323
345
|
|
|
324
346
|
if (tag === 'html' && !hasHeadChild(props.children)) {
|
|
325
|
-
opts.emit('<head
|
|
347
|
+
opts.emit('<head>')
|
|
348
|
+
opts.onHead?.()
|
|
349
|
+
opts.emit('</head>')
|
|
326
350
|
}
|
|
327
351
|
|
|
328
352
|
const dangerouslyHtml = props.dangerouslySetInnerHTML?.__html
|
|
@@ -358,9 +382,11 @@ function walkHost(
|
|
|
358
382
|
walkNode(props.children, childOpts)
|
|
359
383
|
}
|
|
360
384
|
opts.emit(`</${tag}>`)
|
|
361
|
-
if (isSelect) popSelectContext()
|
|
362
385
|
// Host element closing resets outer flow — next sibling text starts fresh.
|
|
363
386
|
if (parentTextState) parentTextState.lastWasText = false
|
|
387
|
+
} finally {
|
|
388
|
+
if (isSelect) popSelectContext()
|
|
389
|
+
}
|
|
364
390
|
}
|
|
365
391
|
|
|
366
392
|
function hasHeadChild(children: unknown): boolean {
|
|
@@ -393,7 +419,12 @@ function walkComponent(
|
|
|
393
419
|
fn: Function,
|
|
394
420
|
props: Record<string, any>,
|
|
395
421
|
opts: WalkOptions,
|
|
422
|
+
ref?: unknown,
|
|
396
423
|
): void {
|
|
424
|
+
if (opts.onBrowserBailout) {
|
|
425
|
+
opts = { ...opts, componentStack: `\n at ${(fn as any).displayName || fn.name || 'Anonymous'}${opts.componentStack || ''}` }
|
|
426
|
+
}
|
|
427
|
+
try {
|
|
397
428
|
if ((fn as any).prototype?.isReactComponent) {
|
|
398
429
|
const ctxType = (fn as any).contextType
|
|
399
430
|
const ctxValue = ctxType ? ctxType._currentValue : undefined
|
|
@@ -407,8 +438,15 @@ function walkComponent(
|
|
|
407
438
|
walkNode(instance.render(), opts)
|
|
408
439
|
return
|
|
409
440
|
}
|
|
410
|
-
const rendered = (fn as any)(props)
|
|
441
|
+
const rendered = (fn as any)(props, ref)
|
|
411
442
|
walkNode(rendered, opts)
|
|
443
|
+
} catch (error) {
|
|
444
|
+
if (opts.thrownInfo && !opts.thrownInfo.componentStack) opts.thrownInfo.componentStack = opts.componentStack
|
|
445
|
+
if (isBrowserError(error) && !(error as any).componentStack) {
|
|
446
|
+
Object.defineProperty(error, 'componentStack', { value: opts.componentStack })
|
|
447
|
+
}
|
|
448
|
+
throw error
|
|
449
|
+
}
|
|
412
450
|
}
|
|
413
451
|
|
|
414
452
|
function walkSuspense(
|
|
@@ -423,26 +461,37 @@ function walkSuspense(
|
|
|
423
461
|
// Try to render the children synchronously. If a thenable is thrown,
|
|
424
462
|
// record the boundary and emit the fallback.
|
|
425
463
|
const childParts: string[] = []
|
|
464
|
+
const nested: SuspendedBoundary[] = []
|
|
465
|
+
const thrownInfo: { componentStack?: string | undefined } = {}
|
|
426
466
|
const childEmit = (s: string) => childParts.push(s)
|
|
467
|
+
const resourceScope: ResourceScope | undefined = currentSSRFrame().shellFlushed ? {} : undefined
|
|
427
468
|
try {
|
|
428
469
|
walkNode(props.children, {
|
|
470
|
+
...opts,
|
|
471
|
+
resourceScope,
|
|
472
|
+
thrownInfo,
|
|
429
473
|
emit: childEmit,
|
|
430
|
-
onSuspend: opts.onSuspend,
|
|
474
|
+
onSuspend: opts.onSuspend ? (boundary) => nested.push(boundary) : undefined,
|
|
431
475
|
nextBoundaryId: opts.nextBoundaryId,
|
|
432
476
|
})
|
|
433
477
|
} catch (thenable: any) {
|
|
434
|
-
|
|
478
|
+
nested.length = 0
|
|
479
|
+
if (isThenable(thenable) || isBrowserError(thenable)) {
|
|
435
480
|
const fallbackParts: string[] = []
|
|
436
|
-
try {
|
|
437
481
|
walkNode(props.fallback, {
|
|
482
|
+
...opts,
|
|
438
483
|
emit: (s) => fallbackParts.push(s),
|
|
439
|
-
onSuspend: opts.onSuspend,
|
|
484
|
+
onSuspend: opts.onSuspend ? (boundary) => nested.push(boundary) : undefined,
|
|
440
485
|
nextBoundaryId: opts.nextBoundaryId,
|
|
441
486
|
})
|
|
442
|
-
|
|
443
|
-
|
|
487
|
+
if (isBrowserError(thenable)) {
|
|
488
|
+
opts.onBrowserBailout?.(thenable, { componentStack: (thenable as any).componentStack || opts.componentStack || '' })
|
|
489
|
+
opts.emit(`<!--$!${id}-->${fallbackParts.join('')}<!--/$-->`)
|
|
490
|
+
for (const boundary of nested) opts.onSuspend?.(boundary)
|
|
491
|
+
return
|
|
444
492
|
}
|
|
445
493
|
emitBoundary(opts, id, fallbackParts.join(''))
|
|
494
|
+
for (const boundary of nested) opts.onSuspend?.(boundary)
|
|
446
495
|
|
|
447
496
|
if (opts.onSuspend) {
|
|
448
497
|
opts.onSuspend({
|
|
@@ -451,6 +500,7 @@ function walkSuspense(
|
|
|
451
500
|
children: props.children,
|
|
452
501
|
thenable,
|
|
453
502
|
contextSnapshot,
|
|
503
|
+
componentStack: thrownInfo.componentStack || opts.componentStack || '',
|
|
454
504
|
})
|
|
455
505
|
}
|
|
456
506
|
return
|
|
@@ -458,6 +508,8 @@ function walkSuspense(
|
|
|
458
508
|
throw thenable
|
|
459
509
|
}
|
|
460
510
|
|
|
511
|
+
if (resourceScope) currentSSRFrame().resources?.mergeScope(resourceScope, opts.resourceScope)
|
|
512
|
+
|
|
461
513
|
// Children rendered fully — emit them wrapped in resolved-boundary markers
|
|
462
514
|
// (`<!--$N-->` / `<!--/$-->`). Without markers, the client hydrator has no
|
|
463
515
|
// way to know this subtree is inside a Suspense, so if the client version
|
|
@@ -468,6 +520,7 @@ function walkSuspense(
|
|
|
468
520
|
opts.emit(`<!--$${id}-->`)
|
|
469
521
|
opts.emit(childParts.join(''))
|
|
470
522
|
opts.emit(`<!--/$-->`)
|
|
523
|
+
for (const boundary of nested) opts.onSuspend?.(boundary)
|
|
471
524
|
}
|
|
472
525
|
|
|
473
526
|
function emitBoundary(opts: WalkOptions, id: number, fallbackHTML: string): void {
|
package/src/vite/index.ts
CHANGED
|
@@ -12,6 +12,12 @@ export type RedactPreset = 'nano' | 'full'
|
|
|
12
12
|
* configs as autocompleted options.
|
|
13
13
|
*/
|
|
14
14
|
export interface RedactFeatures {
|
|
15
|
+
/** Activity visibility, retained state and disconnected effects. */
|
|
16
|
+
activity?: boolean
|
|
17
|
+
/** DOM instances, events, focus, layout and observers for Fragment refs. */
|
|
18
|
+
fragmentRefs?: boolean
|
|
19
|
+
/** Experimental native ViewTransition animations. Opt-in until snapshot parity is complete. */
|
|
20
|
+
viewTransitions?: boolean
|
|
15
21
|
/**
|
|
16
22
|
* `createPortal`. When `false`, portal elements render in place as a
|
|
17
23
|
* Fragment (the `container` prop is ignored). `renderPortal` and its
|
|
@@ -72,6 +78,9 @@ export interface RedactFeatures {
|
|
|
72
78
|
}
|
|
73
79
|
|
|
74
80
|
interface ResolvedFeatures {
|
|
81
|
+
activity: boolean
|
|
82
|
+
fragmentRefs: boolean
|
|
83
|
+
viewTransitions: boolean
|
|
75
84
|
portal: boolean
|
|
76
85
|
context: boolean
|
|
77
86
|
suspense: boolean
|
|
@@ -85,11 +94,13 @@ interface ResolvedFeatures {
|
|
|
85
94
|
const PRESET_DEFAULTS: Record<RedactPreset, ResolvedFeatures> = {
|
|
86
95
|
// Opt-in: everything off. Turn individual features on via `features`.
|
|
87
96
|
nano: {
|
|
97
|
+
activity: false, fragmentRefs: false, viewTransitions: false,
|
|
88
98
|
portal: false, context: false, suspense: false, memo: false,
|
|
89
99
|
forwardRef: false, lazy: false, classComponents: false, hydration: false,
|
|
90
100
|
},
|
|
91
101
|
// Opt-out: everything on (drop-in React parity). Turn features off via `features`.
|
|
92
102
|
full: {
|
|
103
|
+
activity: true, fragmentRefs: true, viewTransitions: false,
|
|
93
104
|
portal: true, context: true, suspense: true, memo: true,
|
|
94
105
|
forwardRef: true, lazy: true, classComponents: true, hydration: true,
|
|
95
106
|
},
|
|
@@ -101,6 +112,9 @@ function resolveFeatures(
|
|
|
101
112
|
): ResolvedFeatures {
|
|
102
113
|
const p = PRESET_DEFAULTS[preset]
|
|
103
114
|
return {
|
|
115
|
+
activity: overrides.activity ?? p.activity,
|
|
116
|
+
fragmentRefs: overrides.fragmentRefs ?? p.fragmentRefs,
|
|
117
|
+
viewTransitions: overrides.viewTransitions ?? p.viewTransitions,
|
|
104
118
|
portal: overrides.portal ?? p.portal,
|
|
105
119
|
context: overrides.context ?? p.context,
|
|
106
120
|
suspense: overrides.suspense ?? p.suspense,
|
|
@@ -166,7 +180,10 @@ const ALIASES: Record<string, string> = {
|
|
|
166
180
|
'react/jsx-dev-runtime': '@tanstack/redact/jsx-dev-runtime',
|
|
167
181
|
'react/compiler-runtime': '@tanstack/redact/compiler-runtime',
|
|
168
182
|
'react-dom/client': '@tanstack/redact/dom-client',
|
|
183
|
+
'react-dom/server.browser': '@tanstack/redact/server',
|
|
184
|
+
'react-dom/server.bun': '@tanstack/redact/server',
|
|
169
185
|
'react-dom/server.edge': '@tanstack/redact/server',
|
|
186
|
+
'react-dom/server.node': '@tanstack/redact/server',
|
|
170
187
|
'react-dom/static.edge': '@tanstack/redact/server',
|
|
171
188
|
'react-dom/server': '@tanstack/redact/server',
|
|
172
189
|
'react-dom/test-utils': '@tanstack/redact/dom-test-utils',
|
|
@@ -225,10 +242,8 @@ function resolveExport(packageDir: string, sub: string): string | null {
|
|
|
225
242
|
}
|
|
226
243
|
const key = sub ? './' + sub : '.'
|
|
227
244
|
const exp = pkg.exports?.[key]
|
|
228
|
-
// Prefer published
|
|
229
|
-
//
|
|
230
|
-
// individual source files. The package keeps cross-subpath imports
|
|
231
|
-
// external, so there's still only one runtime instance.
|
|
245
|
+
// Prefer published JavaScript over TypeScript sources. Dist preserves one
|
|
246
|
+
// file per module, so subpath imports share state and feature boundaries.
|
|
232
247
|
const pick = (v: any): string | null => {
|
|
233
248
|
if (typeof v === 'string') return v
|
|
234
249
|
if (v && typeof v === 'object') {
|
|
@@ -388,11 +403,12 @@ export function redact(options: RedactOptions = {}): any {
|
|
|
388
403
|
// if the flag is off. The stub registers a graceful-degradation
|
|
389
404
|
// matcher (e.g. Portal → Fragment) so user code keeps working.
|
|
390
405
|
if (importer && /[\\/]features[\\/]index\.[jt]sx?$/.test(importer)) {
|
|
391
|
-
const m = id.match(/^\.\/([a-z-]+)
|
|
406
|
+
const m = id.match(/^\.\/([a-z-]+)(\/index\.js)?$/)
|
|
392
407
|
if (m) {
|
|
393
|
-
const
|
|
408
|
+
const dir = m[1]!
|
|
409
|
+
const name = (dir === 'forward-ref' ? 'forwardRef' : dir === 'fragment-refs' ? 'fragmentRefs' : dir === 'view-transition' ? 'viewTransitions' : dir === 'class' ? 'classComponents' : dir) as keyof ResolvedFeatures
|
|
394
410
|
if (name in features && !features[name]) {
|
|
395
|
-
const r = await this.resolve(`./${
|
|
411
|
+
const r = await this.resolve(`./${dir}/stub${m[2] ? '.js' : ''}`, importer, {
|
|
396
412
|
...opts,
|
|
397
413
|
skipSelf: true,
|
|
398
414
|
})
|
|
@@ -401,13 +417,14 @@ export function redact(options: RedactOptions = {}): any {
|
|
|
401
417
|
}
|
|
402
418
|
}
|
|
403
419
|
|
|
404
|
-
//
|
|
405
|
-
//
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
if (
|
|
420
|
+
// Peer imports must use the same feature selection as the registration
|
|
421
|
+
// entrypoint, or an enabled feature can bring a disabled one back in.
|
|
422
|
+
const peer = id.match(/[\\/](context|hydration|fragment-refs|view-transition)(?:[\\/]index\.js)?$/)
|
|
423
|
+
const peerKey = peer?.[1] === 'fragment-refs' ? 'fragmentRefs' : peer?.[1] === 'view-transition' ? 'viewTransitions' : peer?.[1] as 'context' | 'hydration' | undefined
|
|
424
|
+
if (importer && peer && peerKey && !features[peerKey]) {
|
|
409
425
|
const r = await this.resolve(id, importer, { ...opts, skipSelf: true })
|
|
410
|
-
|
|
426
|
+
const resolvedFeature = r?.id.match(/features[\\/](context|hydration|fragment-refs|view-transition)[\\/]index\.(ts|js)$/)
|
|
427
|
+
if (resolvedFeature?.[1] === peer[1]) {
|
|
411
428
|
return r.id.replace(/index\.(ts|js)$/, 'stub.$1')
|
|
412
429
|
}
|
|
413
430
|
}
|