@tanstack/redact 0.0.13 → 0.0.15

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 (93) hide show
  1. package/dist/core/internal.d.ts +23 -39
  2. package/dist/core/internal.js +7 -23
  3. package/dist/core/internal.js.map +3 -3
  4. package/dist/dom/dispatcher.d.ts +3 -1
  5. package/dist/dom/dispatcher.js +104 -97
  6. package/dist/dom/dispatcher.js.map +2 -2
  7. package/dist/dom/dom.js +1 -1
  8. package/dist/dom/dom.js.map +2 -2
  9. package/dist/dom/event-replay.js +7 -29
  10. package/dist/dom/event-replay.js.map +2 -2
  11. package/dist/dom/features/class/full.js +16 -16
  12. package/dist/dom/features/class/full.js.map +2 -2
  13. package/dist/dom/features/class/stub.js +7 -7
  14. package/dist/dom/features/class/stub.js.map +2 -2
  15. package/dist/dom/features/context/full.js +7 -7
  16. package/dist/dom/features/context/full.js.map +2 -2
  17. package/dist/dom/features/context/stub.js +3 -3
  18. package/dist/dom/features/context/stub.js.map +2 -2
  19. package/dist/dom/features/forward-ref/full.js +11 -11
  20. package/dist/dom/features/forward-ref/full.js.map +2 -2
  21. package/dist/dom/features/hydration/full.d.ts +26 -14
  22. package/dist/dom/features/hydration/full.js +412 -92
  23. package/dist/dom/features/hydration/full.js.map +2 -2
  24. package/dist/dom/features/hydration/stub.d.ts +13 -12
  25. package/dist/dom/features/hydration/stub.js +23 -8
  26. package/dist/dom/features/hydration/stub.js.map +2 -2
  27. package/dist/dom/features/lazy/full.js +3 -43
  28. package/dist/dom/features/lazy/full.js.map +2 -2
  29. package/dist/dom/features/lazy/stub.js +1 -1
  30. package/dist/dom/features/lazy/stub.js.map +2 -2
  31. package/dist/dom/features/memo/full.js +3 -3
  32. package/dist/dom/features/memo/full.js.map +2 -2
  33. package/dist/dom/features/memo/stub.js +1 -1
  34. package/dist/dom/features/memo/stub.js.map +2 -2
  35. package/dist/dom/features/portal/full.js +2 -2
  36. package/dist/dom/features/portal/full.js.map +2 -2
  37. package/dist/dom/features/suspense/full.js +151 -109
  38. package/dist/dom/features/suspense/full.js.map +2 -2
  39. package/dist/dom/reconcile.d.ts +3 -1
  40. package/dist/dom/reconcile.js +141 -129
  41. package/dist/dom/reconcile.js.map +2 -2
  42. package/dist/dom/root-internal.d.ts +9 -0
  43. package/dist/dom/root-internal.js +31 -0
  44. package/dist/dom/root-internal.js.map +7 -0
  45. package/dist/dom/root.d.ts +1 -1
  46. package/dist/dom/root.js +4 -136
  47. package/dist/dom/root.js.map +2 -2
  48. package/dist/dom/test-utils.js +1 -1
  49. package/dist/dom/test-utils.js.map +2 -2
  50. package/dist/react/children.js +4 -1
  51. package/dist/react/children.js.map +2 -2
  52. package/dist/react/class.js +8 -2
  53. package/dist/react/class.js.map +2 -2
  54. package/dist/react/context.js +4 -1
  55. package/dist/react/context.js.map +2 -2
  56. package/dist/react/index.js +1 -1
  57. package/dist/react/index.js.map +2 -2
  58. package/dist/react/shared-internals.d.ts +4 -7
  59. package/dist/react/shared-internals.js +4 -7
  60. package/dist/react/shared-internals.js.map +2 -2
  61. package/dist/server/stream.d.ts +5 -1
  62. package/dist/server/stream.js.map +2 -2
  63. package/dist/vite/index.js +19 -3
  64. package/dist/vite/index.js.map +2 -2
  65. package/package.json +1 -1
  66. package/src/core/internal.ts +30 -50
  67. package/src/dom/dispatcher.ts +114 -105
  68. package/src/dom/dom.ts +1 -1
  69. package/src/dom/event-replay.ts +14 -30
  70. package/src/dom/features/class/full.ts +16 -16
  71. package/src/dom/features/class/stub.ts +7 -7
  72. package/src/dom/features/context/full.ts +7 -7
  73. package/src/dom/features/context/stub.ts +3 -3
  74. package/src/dom/features/forward-ref/full.ts +11 -11
  75. package/src/dom/features/hydration/full.ts +516 -104
  76. package/src/dom/features/hydration/stub.ts +28 -14
  77. package/src/dom/features/lazy/full.ts +3 -56
  78. package/src/dom/features/lazy/stub.ts +1 -1
  79. package/src/dom/features/memo/full.ts +3 -3
  80. package/src/dom/features/memo/stub.ts +1 -1
  81. package/src/dom/features/portal/full.ts +2 -2
  82. package/src/dom/features/suspense/full.ts +176 -129
  83. package/src/dom/reconcile.ts +164 -170
  84. package/src/dom/root-internal.ts +40 -0
  85. package/src/dom/root.ts +5 -165
  86. package/src/dom/test-utils.ts +1 -1
  87. package/src/react/children.ts +4 -1
  88. package/src/react/class.ts +8 -2
  89. package/src/react/context.ts +4 -1
  90. package/src/react/index.ts +1 -1
  91. package/src/react/shared-internals.ts +8 -14
  92. package/src/server/stream.ts +9 -4
  93. package/src/vite/index.ts +34 -3
@@ -0,0 +1,40 @@
1
+ import { FiberTag, createFiber, type Fiber, type FiberRoot } from '../core'
2
+
3
+ export interface RootOptionsInternal {
4
+ identifierPrefix?: string
5
+ onRecoverableError?: (error: unknown) => void
6
+ onCaughtError?: (error: unknown) => void
7
+ onUncaughtError?: (error: unknown) => void
8
+ }
9
+
10
+ export function createFiberRoot(
11
+ container: Element | Document | DocumentFragment,
12
+ options: RootOptionsInternal,
13
+ ): FiberRoot {
14
+ const rootFiber = createFiber(FiberTag.Root, null, null)
15
+ const root: FiberRoot = {
16
+ c: container as any,
17
+ r: rootFiber,
18
+ p: new Set(),
19
+ s: false,
20
+ re: options.onRecoverableError,
21
+ ce: options.onCaughtError,
22
+ ue: options.onUncaughtError,
23
+ i: options.identifierPrefix ?? ':r',
24
+ h: false,
25
+ }
26
+ rootFiber.root = root
27
+ rootFiber.sn = container
28
+ return root
29
+ }
30
+
31
+ export function attachRootFiber(
32
+ root: FiberRoot,
33
+ container: Element | Document | DocumentFragment,
34
+ ): void {
35
+ const rootFiber = createFiber(FiberTag.Root, null, null)
36
+ root.c = container as any
37
+ rootFiber.root = root
38
+ rootFiber.sn = container
39
+ root.r = rootFiber
40
+ }
package/src/dom/root.ts CHANGED
@@ -1,18 +1,7 @@
1
- import {
2
- FiberTag,
3
- REACT_ELEMENT_TYPE,
4
- createFiber,
5
- type FiberRoot,
6
- type ReactElement,
7
- type ReactNode,
8
- } from '../core'
1
+ import type { ReactNode } from '../core'
9
2
  import { renderRoot, flushSyncWork, batchedUpdates } from './reconcile'
10
- import {
11
- beginHydration,
12
- endHydration,
13
- drainReplayQueue,
14
- installHydrationScrollGuard,
15
- } from './features/hydration'
3
+ import { hydrateRootImpl } from './features/hydration'
4
+ import { createFiberRoot } from './root-internal'
16
5
 
17
6
  export interface RootOptions {
18
7
  identifierPrefix?: string
@@ -27,21 +16,7 @@ export interface Root {
27
16
  }
28
17
 
29
18
  export function createRoot(container: Element | DocumentFragment, options: RootOptions = {}): Root {
30
- const rootFiber = createFiber(FiberTag.Root, null, null)
31
- rootFiber.dom = container
32
- const root: FiberRoot = {
33
- container,
34
- current: rootFiber,
35
- pending: new Set(),
36
- scheduled: false,
37
- onRecoverableError: options.onRecoverableError,
38
- onCaughtError: options.onCaughtError,
39
- onUncaughtError: options.onUncaughtError,
40
- identifierPrefix: options.identifierPrefix ?? ':r',
41
- hydrating: false,
42
- }
43
- rootFiber.root = root
44
- rootFiber.stateNode = container
19
+ const root = createFiberRoot(container, options)
45
20
 
46
21
  let firstRender = true
47
22
  return {
@@ -72,142 +47,7 @@ export function hydrateRoot(
72
47
  initialChildren: ReactNode,
73
48
  options: RootOptions = {},
74
49
  ): Root {
75
- // `container` may be the Document when the React tree renders <html>...</html>
76
- // (e.g. TanStack Start's default client entry). In that case we adopt
77
- // documentElement as a CHILD of the root, not as the root itself — otherwise
78
- // we'd try to render <html> inside <html>.
79
- const target = container as any as Element | Document
80
- const rootFiber = createFiber(FiberTag.Root, null, null)
81
- rootFiber.dom = target as unknown as Node
82
- const root: FiberRoot = {
83
- container: target as any,
84
- current: rootFiber,
85
- pending: new Set(),
86
- scheduled: false,
87
- onRecoverableError: options.onRecoverableError,
88
- onCaughtError: options.onCaughtError,
89
- onUncaughtError: options.onUncaughtError,
90
- identifierPrefix: options.identifierPrefix ?? ':r',
91
- hydrating: false,
92
- }
93
- rootFiber.root = root
94
- rootFiber.stateNode = target
95
-
96
- // Preserve the user's scroll position across hydration (see feature impl
97
- // for the details). No-op in SSR; no-op in the stub.
98
- installHydrationScrollGuard()
99
-
100
- beginHydration(root)
101
- try {
102
- const normalizedInitialChildren =
103
- isDocumentContainer(container) ? normalizeDocumentChildren(initialChildren) : initialChildren
104
- flushSyncWork(() => {
105
- renderRoot(root, normalizedInitialChildren)
106
- })
107
- } finally {
108
- endHydration(root)
109
- }
110
- drainReplayQueue()
111
-
112
- return {
113
- render(children) {
114
- flushSyncWork(() => {
115
- renderRoot(root, isDocumentContainer(container) ? normalizeDocumentChildren(children) : children)
116
- })
117
- },
118
- unmount() {
119
- flushSyncWork(() => {
120
- renderRoot(root, null)
121
- })
122
- },
123
- }
124
- }
125
-
126
- const HEAD_TAGS = new Set(['base', 'link', 'meta', 'script', 'style', 'title'])
127
-
128
- function isDocumentContainer(container: unknown): container is Document {
129
- return !!container && (container as Node).nodeType === 9
130
- }
131
-
132
- function normalizeDocumentChildren(children: ReactNode): ReactNode {
133
- const list = toChildArray(children)
134
- const htmlIndex = list.findIndex((child) => isHostElement(child, 'html'))
135
- if (htmlIndex === -1) return children
136
-
137
- const headNodes = list.filter(isHeadElement)
138
- if (headNodes.length === 0) return children
139
-
140
- const htmlElement = list[htmlIndex] as ReactElement
141
- const normalizedHtml = hoistIntoHtmlHead(htmlElement, headNodes)
142
- return list
143
- .filter((child, index) => index === htmlIndex || !isHeadElement(child))
144
- .map((child) => (child === htmlElement ? normalizedHtml : child))
145
- }
146
-
147
- function hoistIntoHtmlHead(htmlElement: ReactElement, headNodes: ReactNode[]): ReactElement {
148
- const htmlChildren = toChildArray(htmlElement.props?.children)
149
- const headIndex = htmlChildren.findIndex((child) => isHostElement(child, 'head'))
150
- let nextChildren: ReactNode[]
151
-
152
- if (headIndex === -1) {
153
- nextChildren = [
154
- createHostElement('head', { children: headNodes }),
155
- ...htmlChildren,
156
- ]
157
- } else {
158
- const headElement = htmlChildren[headIndex] as ReactElement
159
- const existingHeadChildren = toChildArray(headElement.props?.children)
160
- const nextHead = {
161
- ...headElement,
162
- props: {
163
- ...headElement.props,
164
- children: [...headNodes, ...existingHeadChildren],
165
- },
166
- }
167
- nextChildren = htmlChildren.map((child, index) => (index === headIndex ? nextHead : child))
168
- }
169
-
170
- return {
171
- ...htmlElement,
172
- props: {
173
- ...htmlElement.props,
174
- children: nextChildren,
175
- },
176
- }
177
- }
178
-
179
- function toChildArray(children: unknown): ReactNode[] {
180
- if (children == null || typeof children === 'boolean') return []
181
- if (Array.isArray(children)) return children as ReactNode[]
182
- if (isReactElement(children)) return [children]
183
- if (typeof children !== 'string' && isIterable(children)) return Array.from(children) as ReactNode[]
184
- return [children as ReactNode]
185
- }
186
-
187
- function isHeadElement(value: ReactNode): boolean {
188
- return isReactElement(value) && typeof value.type === 'string' && HEAD_TAGS.has(value.type)
189
- }
190
-
191
- function isHostElement(value: ReactNode, tag: string): boolean {
192
- return isReactElement(value) && value.type === tag
193
- }
194
-
195
- function isReactElement(value: unknown): value is ReactElement {
196
- return !!value && typeof value === 'object' && (value as ReactElement).$$typeof === REACT_ELEMENT_TYPE
197
- }
198
-
199
- function isIterable(value: unknown): value is Iterable<ReactNode> {
200
- return !!value && typeof (value as { [Symbol.iterator]?: unknown })[Symbol.iterator] === 'function'
201
- }
202
-
203
- function createHostElement(type: string, props: Record<string, unknown>): ReactElement {
204
- return {
205
- $$typeof: REACT_ELEMENT_TYPE,
206
- type,
207
- key: null,
208
- ref: null,
209
- props,
210
- }
50
+ return hydrateRootImpl(container, initialChildren, options)
211
51
  }
212
52
 
213
53
  export { flushSyncWork as flushSync, batchedUpdates }
@@ -1,6 +1,6 @@
1
1
  export async function act(fn: () => any): Promise<void> {
2
2
  const r = fn()
3
- if (r && typeof r.then === 'function') await r
3
+ if (r && typeof r.then == 'function') await r
4
4
  // Drain pending microtasks
5
5
  for (let i = 0; i < 10; i++) await Promise.resolve()
6
6
  }
@@ -78,7 +78,10 @@ export const Children = {
78
78
  },
79
79
  only(children: ReactNode): ReactElement {
80
80
  if (!isValidElement(children)) {
81
- throw new Error('Children.only expected a single React element.')
81
+ if (process.env.NODE_ENV !== 'production') {
82
+ throw new Error('Children.only expected a single React element.')
83
+ }
84
+ throw new Error()
82
85
  }
83
86
  return children
84
87
  },
@@ -29,7 +29,10 @@ export class Component<P = {}, S = {}> {
29
29
 
30
30
  setState(updater: SetStateCallback<S, P>, callback?: () => void): void {
31
31
  if (!this._enqueueUpdate) {
32
- throw new Error('Cannot call setState on an unmounted component.')
32
+ if (process.env.NODE_ENV !== 'production') {
33
+ throw new Error('Cannot call setState on an um component.')
34
+ }
35
+ throw new Error()
33
36
  }
34
37
  this._enqueueUpdate(updater, callback)
35
38
  }
@@ -40,7 +43,10 @@ export class Component<P = {}, S = {}> {
40
43
  }
41
44
 
42
45
  render(): ReactNode {
43
- throw new Error('Component subclass must implement render().')
46
+ if (process.env.NODE_ENV !== 'production') {
47
+ throw new Error('Component subclass must implement render().')
48
+ }
49
+ throw new Error()
44
50
  }
45
51
 
46
52
  componentDidMount?(): void
@@ -32,7 +32,10 @@ export function createContext<T>(defaultValue: T): Context<T> {
32
32
  } as Context<T>
33
33
 
34
34
  const Provider: any = function Provider(_props: any): any {
35
- throw new Error('Provider components are handled by the renderer.')
35
+ if (process.env.NODE_ENV !== 'production') {
36
+ throw new Error('Provider components are handled by the renderer.')
37
+ }
38
+ throw new Error()
36
39
  }
37
40
  Provider.$$typeof = REACT_PROVIDER_TYPE
38
41
  Provider._context = context
@@ -70,7 +70,7 @@ export { ReactSharedInternals } from './shared-internals'
70
70
  export const cache = <T extends Function>(fn: T): T => fn
71
71
  export const act = async (fn: () => any) => {
72
72
  const r = fn()
73
- if (r && typeof r.then === 'function') await r
73
+ if (r && typeof r.then == 'function') await r
74
74
  }
75
75
  export function taintUniqueValue(_msg: string, _lifetime: any, _value: any): void {}
76
76
  export function taintObjectReference(_msg: string, _object: any): void {}
@@ -1,4 +1,4 @@
1
- import type { Fiber, FiberRoot, Hook } from '../core'
1
+ import type { Fiber, Hook } from '../core'
2
2
 
3
3
  export interface Dispatcher {
4
4
  useState<S>(initial: S | (() => S)): [S, (s: S | ((p: S) => S)) => void]
@@ -29,12 +29,9 @@ export interface Dispatcher {
29
29
 
30
30
  interface SharedInternals {
31
31
  H: Dispatcher | null
32
- T: any
33
- S: ((fn: () => void) => void) | null
34
- currentFiber: Fiber | null
35
- currentRoot: FiberRoot | null
36
- currentHook: Hook | null
37
- hookIndex: number
32
+ F: Fiber | null
33
+ K: Hook | null
34
+ I: number
38
35
  }
39
36
 
40
37
  // Stash the singleton on `globalThis` under a registered symbol. Module-scoped
@@ -54,12 +51,9 @@ export const ReactSharedInternals: SharedInternals =
54
51
  g[KEY] ??
55
52
  (g[KEY] = {
56
53
  H: null,
57
- T: null,
58
- S: null,
59
- currentFiber: null,
60
- currentRoot: null,
61
- currentHook: null,
62
- hookIndex: 0,
54
+ F: null,
55
+ K: null,
56
+ I: 0,
63
57
  })
64
58
 
65
59
  export function getDispatcher(): Dispatcher {
@@ -73,7 +67,7 @@ export function getDispatcher(): Dispatcher {
73
67
  '"use client" directive at the top of its file.',
74
68
  )
75
69
  }
76
- throw new Error('Hooks can only be called inside a function component.')
70
+ throw new Error()
77
71
  }
78
72
  return d
79
73
  }
@@ -25,6 +25,11 @@ export interface ReadableStreamResult extends ReadableStream<Uint8Array> {
25
25
  allReady: Promise<void>
26
26
  }
27
27
 
28
+ export interface PipeableWritable {
29
+ write(chunk: string): unknown
30
+ end(): unknown
31
+ }
32
+
28
33
  export interface OrchestratorState {
29
34
  nextId: number
30
35
  pending: Set<Promise<void>>
@@ -302,7 +307,7 @@ export function renderToReadableStream(
302
307
  // ---------------------------------------------------------------------------
303
308
 
304
309
  export interface PipeableHandle {
305
- pipe<T extends NodeJS.WritableStream>(dest: T): T
310
+ pipe<T extends PipeableWritable>(dest: T): T
306
311
  abort(reason?: unknown): void
307
312
  }
308
313
 
@@ -324,12 +329,12 @@ export function renderToPipeableStream(
324
329
  }
325
330
 
326
331
  const buffers: string[] = []
327
- let dest: NodeJS.WritableStream | null = null
332
+ let dest: PipeableWritable | null = null
328
333
  let shellReady = false
329
334
  let finished = false
330
335
  let aborted = false
331
336
 
332
- const flushTo = (w: NodeJS.WritableStream) => {
337
+ const flushTo = (w: PipeableWritable) => {
333
338
  if (!buffers.length) return
334
339
  for (const b of buffers) w.write(b)
335
340
  buffers.length = 0
@@ -368,7 +373,7 @@ export function renderToPipeableStream(
368
373
  })
369
374
 
370
375
  return {
371
- pipe<T extends NodeJS.WritableStream>(target: T): T {
376
+ pipe<T extends PipeableWritable>(target: T): T {
372
377
  dest = target
373
378
  flushTo(target)
374
379
  if (finished) target.end()
package/src/vite/index.ts CHANGED
@@ -283,9 +283,27 @@ function resolveSpecifier(
283
283
  }
284
284
  }
285
285
 
286
+ interface OptimizeDepsWithInclude {
287
+ include?: Array<string>
288
+ }
289
+
290
+ function pruneOptimizeDepsInclude(
291
+ optimizeDeps: OptimizeDepsWithInclude | undefined,
292
+ blocked: ReadonlySet<string>,
293
+ ): void {
294
+ if (!optimizeDeps?.include) return
295
+
296
+ optimizeDeps.include = optimizeDeps.include.filter((id) => {
297
+ const tailSpecifier = id.split('>').pop()?.trim() ?? id
298
+ return !blocked.has(id) && !blocked.has(tailSpecifier)
299
+ })
300
+ }
301
+
286
302
  export function redact(options: RedactOptions = {}): any {
287
303
  const skip = new Set(options.skip ?? [])
288
304
  const entries = Object.entries(ALIASES).filter(([k]) => !skip.has(k))
305
+ const excludeList = entries.map(([k]) => k)
306
+ const optimizeDepsBlocked = new Set(excludeList)
289
307
  const features = resolveFeatures(options.preset ?? 'full', options.features ?? {})
290
308
 
291
309
  const resolvedMap: Record<string, string> = {}
@@ -302,12 +320,11 @@ export function redact(options: RedactOptions = {}): any {
302
320
  done = true
303
321
  }
304
322
 
305
- return {
323
+ return [{
306
324
  name: 'redact',
307
325
  enforce: 'pre',
308
326
 
309
327
  config() {
310
- const excludeList = entries.map(([k]) => k)
311
328
  // Single package — only one name to dedupe / no-external.
312
329
  const noExt = ['@tanstack/redact']
313
330
  const aliasMap = Object.fromEntries(entries.filter(([from, to]) => from !== to))
@@ -397,7 +414,21 @@ export function redact(options: RedactOptions = {}): any {
397
414
 
398
415
  return resolvedMap[id] ?? null
399
416
  },
400
- }
417
+ }, {
418
+ name: 'redact:optimize-deps-guard',
419
+ enforce: 'post',
420
+
421
+ configResolved(config: any) {
422
+ // `optimizeDeps.exclude` loses when another plugin force-adds the same
423
+ // ids to `include`. That lets Vite pre-bundle real React next to Redact,
424
+ // producing duplicate dispatcher state. Prune after config settles so
425
+ // Redact's client/SSR shims stay canonical.
426
+ pruneOptimizeDepsInclude(config.optimizeDeps, optimizeDepsBlocked)
427
+ pruneOptimizeDepsInclude(config.environments?.client?.optimizeDeps, optimizeDepsBlocked)
428
+ pruneOptimizeDepsInclude(config.environments?.ssr?.optimizeDeps, optimizeDepsBlocked)
429
+ // Leave `environments.rsc` untouched. RSC depends on real React internals.
430
+ },
431
+ }]
401
432
  }
402
433
 
403
434
  export default redact