@tanstack/redact 0.0.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.
Files changed (233) hide show
  1. package/dist/core/index.d.ts +2 -0
  2. package/dist/core/index.js +4 -0
  3. package/dist/core/index.js.map +7 -0
  4. package/dist/core/internal.d.ts +76 -0
  5. package/dist/core/internal.js +59 -0
  6. package/dist/core/internal.js.map +7 -0
  7. package/dist/core/types.d.ts +21 -0
  8. package/dist/core/types.js +10 -0
  9. package/dist/core/types.js.map +7 -0
  10. package/dist/dom/_all.d.ts +17 -0
  11. package/dist/dom/_all.js +51 -0
  12. package/dist/dom/_all.js.map +7 -0
  13. package/dist/dom/client.d.ts +3 -0
  14. package/dist/dom/client.js +8 -0
  15. package/dist/dom/client.js.map +7 -0
  16. package/dist/dom/dispatcher.d.ts +20 -0
  17. package/dist/dom/dispatcher.js +267 -0
  18. package/dist/dom/dispatcher.js.map +7 -0
  19. package/dist/dom/dom.d.ts +10 -0
  20. package/dist/dom/dom.js +262 -0
  21. package/dist/dom/dom.js.map +7 -0
  22. package/dist/dom/event-replay.d.ts +13 -0
  23. package/dist/dom/event-replay.js +46 -0
  24. package/dist/dom/event-replay.js.map +7 -0
  25. package/dist/dom/features/class/full.d.ts +1 -0
  26. package/dist/dom/features/class/full.js +91 -0
  27. package/dist/dom/features/class/full.js.map +7 -0
  28. package/dist/dom/features/class/index.d.ts +1 -0
  29. package/dist/dom/features/class/index.js +3 -0
  30. package/dist/dom/features/class/index.js.map +7 -0
  31. package/dist/dom/features/class/stub.d.ts +1 -0
  32. package/dist/dom/features/class/stub.js +50 -0
  33. package/dist/dom/features/class/stub.js.map +7 -0
  34. package/dist/dom/features/context/full.d.ts +1 -0
  35. package/dist/dom/features/context/full.js +49 -0
  36. package/dist/dom/features/context/full.js.map +7 -0
  37. package/dist/dom/features/context/index.d.ts +1 -0
  38. package/dist/dom/features/context/index.js +3 -0
  39. package/dist/dom/features/context/index.js.map +7 -0
  40. package/dist/dom/features/context/stub.d.ts +1 -0
  41. package/dist/dom/features/context/stub.js +22 -0
  42. package/dist/dom/features/context/stub.js.map +7 -0
  43. package/dist/dom/features/forward-ref/full.d.ts +1 -0
  44. package/dist/dom/features/forward-ref/full.js +51 -0
  45. package/dist/dom/features/forward-ref/full.js.map +7 -0
  46. package/dist/dom/features/forward-ref/index.d.ts +1 -0
  47. package/dist/dom/features/forward-ref/index.js +3 -0
  48. package/dist/dom/features/forward-ref/index.js.map +7 -0
  49. package/dist/dom/features/forward-ref/stub.d.ts +1 -0
  50. package/dist/dom/features/forward-ref/stub.js +22 -0
  51. package/dist/dom/features/forward-ref/stub.js.map +7 -0
  52. package/dist/dom/features/hydration/full.d.ts +61 -0
  53. package/dist/dom/features/hydration/full.js +264 -0
  54. package/dist/dom/features/hydration/full.js.map +7 -0
  55. package/dist/dom/features/hydration/index.d.ts +1 -0
  56. package/dist/dom/features/hydration/index.js +3 -0
  57. package/dist/dom/features/hydration/index.js.map +7 -0
  58. package/dist/dom/features/hydration/stub.d.ts +28 -0
  59. package/dist/dom/features/hydration/stub.js +66 -0
  60. package/dist/dom/features/hydration/stub.js.map +7 -0
  61. package/dist/dom/features/index.d.ts +7 -0
  62. package/dist/dom/features/index.js +9 -0
  63. package/dist/dom/features/index.js.map +7 -0
  64. package/dist/dom/features/lazy/full.d.ts +1 -0
  65. package/dist/dom/features/lazy/full.js +78 -0
  66. package/dist/dom/features/lazy/full.js.map +7 -0
  67. package/dist/dom/features/lazy/index.d.ts +1 -0
  68. package/dist/dom/features/lazy/index.js +3 -0
  69. package/dist/dom/features/lazy/index.js.map +7 -0
  70. package/dist/dom/features/lazy/stub.d.ts +1 -0
  71. package/dist/dom/features/lazy/stub.js +36 -0
  72. package/dist/dom/features/lazy/stub.js.map +7 -0
  73. package/dist/dom/features/memo/full.d.ts +1 -0
  74. package/dist/dom/features/memo/full.js +59 -0
  75. package/dist/dom/features/memo/full.js.map +7 -0
  76. package/dist/dom/features/memo/index.d.ts +1 -0
  77. package/dist/dom/features/memo/index.js +3 -0
  78. package/dist/dom/features/memo/index.js.map +7 -0
  79. package/dist/dom/features/memo/stub.d.ts +1 -0
  80. package/dist/dom/features/memo/stub.js +36 -0
  81. package/dist/dom/features/memo/stub.js.map +7 -0
  82. package/dist/dom/features/portal/full.d.ts +1 -0
  83. package/dist/dom/features/portal/full.js +19 -0
  84. package/dist/dom/features/portal/full.js.map +7 -0
  85. package/dist/dom/features/portal/index.d.ts +1 -0
  86. package/dist/dom/features/portal/index.js +3 -0
  87. package/dist/dom/features/portal/index.js.map +7 -0
  88. package/dist/dom/features/portal/stub.d.ts +1 -0
  89. package/dist/dom/features/portal/stub.js +7 -0
  90. package/dist/dom/features/portal/stub.js.map +7 -0
  91. package/dist/dom/features/suspense/full.d.ts +1 -0
  92. package/dist/dom/features/suspense/full.js +146 -0
  93. package/dist/dom/features/suspense/full.js.map +7 -0
  94. package/dist/dom/features/suspense/index.d.ts +1 -0
  95. package/dist/dom/features/suspense/index.js +3 -0
  96. package/dist/dom/features/suspense/index.js.map +7 -0
  97. package/dist/dom/features/suspense/stub.d.ts +1 -0
  98. package/dist/dom/features/suspense/stub.js +6 -0
  99. package/dist/dom/features/suspense/stub.js.map +7 -0
  100. package/dist/dom/index.d.ts +25 -0
  101. package/dist/dom/index.js +45 -0
  102. package/dist/dom/index.js.map +7 -0
  103. package/dist/dom/portal.d.ts +2 -0
  104. package/dist/dom/portal.js +15 -0
  105. package/dist/dom/portal.js.map +7 -0
  106. package/dist/dom/reconcile.d.ts +40 -0
  107. package/dist/dom/reconcile.js +795 -0
  108. package/dist/dom/reconcile.js.map +7 -0
  109. package/dist/dom/root.d.ts +15 -0
  110. package/dist/dom/root.js +85 -0
  111. package/dist/dom/root.js.map +7 -0
  112. package/dist/dom/test-utils.d.ts +1 -0
  113. package/dist/dom/test-utils.js +10 -0
  114. package/dist/dom/test-utils.js.map +7 -0
  115. package/dist/react/children.d.ts +8 -0
  116. package/dist/react/children.js +75 -0
  117. package/dist/react/children.js.map +7 -0
  118. package/dist/react/class.d.ts +31 -0
  119. package/dist/react/class.js +40 -0
  120. package/dist/react/class.js.map +7 -0
  121. package/dist/react/context.d.ts +27 -0
  122. package/dist/react/context.js +31 -0
  123. package/dist/react/context.js.map +7 -0
  124. package/dist/react/element.d.ts +10 -0
  125. package/dist/react/element.js +64 -0
  126. package/dist/react/element.js.map +7 -0
  127. package/dist/react/hooks.d.ts +37 -0
  128. package/dist/react/hooks.js +146 -0
  129. package/dist/react/hooks.js.map +7 -0
  130. package/dist/react/index.d.ts +76 -0
  131. package/dist/react/index.js +187 -0
  132. package/dist/react/index.js.map +7 -0
  133. package/dist/react/jsx-runtime.d.ts +5 -0
  134. package/dist/react/jsx-runtime.js +21 -0
  135. package/dist/react/jsx-runtime.js.map +7 -0
  136. package/dist/react/memo.d.ts +29 -0
  137. package/dist/react/memo.js +47 -0
  138. package/dist/react/memo.js.map +7 -0
  139. package/dist/react/portal.d.ts +1 -0
  140. package/dist/react/portal.js +6 -0
  141. package/dist/react/portal.js.map +7 -0
  142. package/dist/react/shared-internals.d.ts +33 -0
  143. package/dist/react/shared-internals.js +29 -0
  144. package/dist/react/shared-internals.js.map +7 -0
  145. package/dist/react/suspense.d.ts +15 -0
  146. package/dist/react/suspense.js +16 -0
  147. package/dist/react/suspense.js.map +7 -0
  148. package/dist/scheduler/index.d.ts +44 -0
  149. package/dist/scheduler/index.js +109 -0
  150. package/dist/scheduler/index.js.map +7 -0
  151. package/dist/server/bootstrap-script.d.ts +22 -0
  152. package/dist/server/bootstrap-script.js +16 -0
  153. package/dist/server/bootstrap-script.js.map +7 -0
  154. package/dist/server/dispatcher.d.ts +37 -0
  155. package/dist/server/dispatcher.js +140 -0
  156. package/dist/server/dispatcher.js.map +7 -0
  157. package/dist/server/escape.d.ts +7 -0
  158. package/dist/server/escape.js +159 -0
  159. package/dist/server/escape.js.map +7 -0
  160. package/dist/server/index.d.ts +16 -0
  161. package/dist/server/index.js +26 -0
  162. package/dist/server/index.js.map +7 -0
  163. package/dist/server/renderToString.d.ts +7 -0
  164. package/dist/server/renderToString.js +29 -0
  165. package/dist/server/renderToString.js.map +7 -0
  166. package/dist/server/stream.d.ts +40 -0
  167. package/dist/server/stream.js +200 -0
  168. package/dist/server/stream.js.map +7 -0
  169. package/dist/server/walk.d.ts +33 -0
  170. package/dist/server/walk.js +298 -0
  171. package/dist/server/walk.js.map +7 -0
  172. package/dist/vite/index.d.ts +96 -0
  173. package/dist/vite/index.js +217 -0
  174. package/dist/vite/index.js.map +7 -0
  175. package/package.json +83 -0
  176. package/src/core/index.ts +2 -0
  177. package/src/core/internal.ts +107 -0
  178. package/src/core/types.ts +36 -0
  179. package/src/dom/_all.ts +54 -0
  180. package/src/dom/client.ts +4 -0
  181. package/src/dom/dispatcher.ts +318 -0
  182. package/src/dom/dom.ts +316 -0
  183. package/src/dom/event-replay.ts +55 -0
  184. package/src/dom/features/class/full.ts +104 -0
  185. package/src/dom/features/class/index.ts +1 -0
  186. package/src/dom/features/class/stub.ts +65 -0
  187. package/src/dom/features/context/full.ts +57 -0
  188. package/src/dom/features/context/index.ts +1 -0
  189. package/src/dom/features/context/stub.ts +32 -0
  190. package/src/dom/features/forward-ref/full.ts +54 -0
  191. package/src/dom/features/forward-ref/index.ts +1 -0
  192. package/src/dom/features/forward-ref/stub.ts +28 -0
  193. package/src/dom/features/hydration/full.ts +345 -0
  194. package/src/dom/features/hydration/index.ts +1 -0
  195. package/src/dom/features/hydration/stub.ts +72 -0
  196. package/src/dom/features/index.ts +11 -0
  197. package/src/dom/features/lazy/full.ts +98 -0
  198. package/src/dom/features/lazy/index.ts +1 -0
  199. package/src/dom/features/lazy/stub.ts +45 -0
  200. package/src/dom/features/memo/full.ts +74 -0
  201. package/src/dom/features/memo/index.ts +1 -0
  202. package/src/dom/features/memo/stub.ts +42 -0
  203. package/src/dom/features/portal/full.ts +22 -0
  204. package/src/dom/features/portal/index.ts +1 -0
  205. package/src/dom/features/portal/stub.ts +11 -0
  206. package/src/dom/features/suspense/full.ts +191 -0
  207. package/src/dom/features/suspense/index.ts +1 -0
  208. package/src/dom/features/suspense/stub.ts +12 -0
  209. package/src/dom/index.ts +33 -0
  210. package/src/dom/portal.ts +16 -0
  211. package/src/dom/reconcile.ts +1169 -0
  212. package/src/dom/root.ts +107 -0
  213. package/src/dom/test-utils.ts +6 -0
  214. package/src/react/children.ts +85 -0
  215. package/src/react/class.ts +56 -0
  216. package/src/react/context.ts +50 -0
  217. package/src/react/element.ts +80 -0
  218. package/src/react/hooks.ts +190 -0
  219. package/src/react/index.ts +132 -0
  220. package/src/react/jsx-runtime.ts +16 -0
  221. package/src/react/memo.ts +45 -0
  222. package/src/react/portal.ts +1 -0
  223. package/src/react/shared-internals.ts +79 -0
  224. package/src/react/suspense.ts +18 -0
  225. package/src/scheduler/index.ts +124 -0
  226. package/src/server/bootstrap-script.ts +52 -0
  227. package/src/server/dispatcher.ts +158 -0
  228. package/src/server/escape.ts +187 -0
  229. package/src/server/index.ts +26 -0
  230. package/src/server/renderToString.ts +33 -0
  231. package/src/server/stream.ts +302 -0
  232. package/src/server/walk.ts +448 -0
  233. package/src/vite/index.ts +401 -0
@@ -0,0 +1,298 @@
1
+ // packages/redact/src/server/walk.ts
2
+ import {
3
+ REACT_ELEMENT_TYPE,
4
+ REACT_LEGACY_ELEMENT_TYPE,
5
+ REACT_FRAGMENT_TYPE
6
+ } from "../core";
7
+ import {
8
+ REACT_SUSPENSE_TYPE,
9
+ REACT_PROVIDER_TYPE,
10
+ REACT_CONSUMER_TYPE,
11
+ REACT_FORWARD_REF_TYPE,
12
+ REACT_MEMO_TYPE,
13
+ REACT_LAZY_TYPE,
14
+ REACT_STRICT_MODE_TYPE,
15
+ REACT_PROFILER_TYPE,
16
+ REACT_PORTAL_TYPE
17
+ } from "../react";
18
+ import {
19
+ attrToHtml,
20
+ escapeText,
21
+ escapeScript,
22
+ VOID_ELEMENTS,
23
+ RAW_TEXT_ELEMENTS
24
+ } from "./escape";
25
+ import {
26
+ pushContext,
27
+ popContext,
28
+ snapshotContexts
29
+ } from "./dispatcher";
30
+ function walk(node, opts) {
31
+ if (!opts.textState) opts = { ...opts, textState: { lastWasText: false } };
32
+ walkNode(node, opts);
33
+ }
34
+ var selectValueStack = [];
35
+ function pushSelectContext(value) {
36
+ selectValueStack.push(value);
37
+ }
38
+ function popSelectContext() {
39
+ selectValueStack.pop();
40
+ }
41
+ function currentSelectValue() {
42
+ return selectValueStack.length ? selectValueStack[selectValueStack.length - 1] : void 0;
43
+ }
44
+ function optionChildText(children) {
45
+ if (children == null) return "";
46
+ if (typeof children === "string" || typeof children === "number") return "" + children;
47
+ if (Array.isArray(children)) return children.map(optionChildText).join("");
48
+ return "";
49
+ }
50
+ function emitText(text, opts) {
51
+ if (text === "") return;
52
+ if (opts.textState?.lastWasText) opts.emit("<!-- -->");
53
+ opts.emit(escapeText(text));
54
+ if (opts.textState) opts.textState.lastWasText = true;
55
+ }
56
+ function walkNode(node, opts) {
57
+ if (node == null || node === false || node === true) return;
58
+ if (typeof node === "string") {
59
+ emitText(node, opts);
60
+ return;
61
+ }
62
+ if (typeof node === "number") {
63
+ emitText(String(node), opts);
64
+ return;
65
+ }
66
+ if (Array.isArray(node)) {
67
+ for (const c of node) walkNode(c, opts);
68
+ return;
69
+ }
70
+ if (typeof node[Symbol.iterator] === "function") {
71
+ for (const item of node) walkNode(item, opts);
72
+ return;
73
+ }
74
+ if (typeof node !== "object") return;
75
+ const t = node.$$typeof;
76
+ if (t === REACT_LAZY_TYPE) {
77
+ const lazy = node;
78
+ const resolved = lazy._init(lazy._payload);
79
+ walkNode(resolved, opts);
80
+ return;
81
+ }
82
+ if (t !== REACT_ELEMENT_TYPE && t !== REACT_LEGACY_ELEMENT_TYPE) return;
83
+ const el = node;
84
+ walkElement(el, opts);
85
+ }
86
+ function walkElement(el, opts) {
87
+ const type = el.type;
88
+ const props = el.props ?? {};
89
+ if (type === REACT_FRAGMENT_TYPE || type === REACT_STRICT_MODE_TYPE || type === REACT_PROFILER_TYPE) {
90
+ walkNode(props.children, opts);
91
+ return;
92
+ }
93
+ if (type === REACT_SUSPENSE_TYPE) {
94
+ walkSuspense(props, opts);
95
+ return;
96
+ }
97
+ if (typeof type === "string") {
98
+ walkHost(type, props, opts);
99
+ return;
100
+ }
101
+ const marker = type?.$$typeof;
102
+ if (marker === REACT_PORTAL_TYPE) {
103
+ return;
104
+ }
105
+ if (marker === REACT_PROVIDER_TYPE) {
106
+ const ctx = type._context;
107
+ pushContext(ctx, props.value);
108
+ try {
109
+ walkNode(props.children, opts);
110
+ } finally {
111
+ popContext(ctx);
112
+ }
113
+ return;
114
+ }
115
+ if (marker === REACT_CONSUMER_TYPE) {
116
+ const ctx = type._context;
117
+ const render = props.children;
118
+ if (typeof render === "function") {
119
+ walkNode(render(ctx._currentValue), opts);
120
+ }
121
+ return;
122
+ }
123
+ if (marker === REACT_FORWARD_REF_TYPE) {
124
+ const render = type.render;
125
+ const ref = props.ref ?? null;
126
+ const { ref: _omit, ...rest } = props;
127
+ const rendered = render(rest, ref);
128
+ walkNode(rendered, opts);
129
+ return;
130
+ }
131
+ if (marker === REACT_MEMO_TYPE) {
132
+ const inner = type.type;
133
+ walkElement({ ...el, type: inner }, opts);
134
+ return;
135
+ }
136
+ if (marker === REACT_LAZY_TYPE) {
137
+ const { _payload, _init } = type;
138
+ try {
139
+ const resolved = _init(_payload);
140
+ walkElement({ ...el, type: resolved }, opts);
141
+ } catch (thenable) {
142
+ if (isThenable(thenable)) {
143
+ throw thenable;
144
+ }
145
+ throw thenable;
146
+ }
147
+ return;
148
+ }
149
+ if (typeof type === "function") {
150
+ walkComponent(type, props, opts);
151
+ return;
152
+ }
153
+ }
154
+ function walkHost(tag, props, opts) {
155
+ const isTextarea = tag === "textarea";
156
+ const textareaValue = isTextarea ? props.value != null ? props.value : props.defaultValue : void 0;
157
+ const isInput = tag === "input";
158
+ const inputValueAttr = isInput && props.value == null && props.defaultValue != null ? props.defaultValue : void 0;
159
+ const inputCheckedAttr = isInput && props.checked == null && props.defaultChecked != null ? props.defaultChecked : void 0;
160
+ const isSelect = tag === "select";
161
+ if (isSelect) {
162
+ const val = props.value != null ? props.value : props.defaultValue;
163
+ pushSelectContext(val);
164
+ }
165
+ const isOption = tag === "option";
166
+ if (tag === "html") opts.emit("<!DOCTYPE html>");
167
+ opts.emit("<" + tag);
168
+ for (const k in props) {
169
+ if (isTextarea && (k === "value" || k === "defaultValue")) continue;
170
+ if (isInput && (k === "defaultValue" || k === "defaultChecked")) continue;
171
+ if (isSelect && (k === "value" || k === "defaultValue")) continue;
172
+ if (isOption && k === "selected") continue;
173
+ opts.emit(attrToHtml(k, props[k]));
174
+ }
175
+ if (inputValueAttr !== void 0) {
176
+ opts.emit(attrToHtml("value", inputValueAttr));
177
+ }
178
+ if (inputCheckedAttr !== void 0) {
179
+ opts.emit(attrToHtml("checked", inputCheckedAttr));
180
+ }
181
+ if (isOption) {
182
+ const selectVal = currentSelectValue();
183
+ if (selectVal !== void 0) {
184
+ const optionValue = props.value != null ? props.value : optionChildText(props.children);
185
+ const matches = Array.isArray(selectVal) ? selectVal.some((v) => "" + v === "" + optionValue) : "" + selectVal === "" + optionValue;
186
+ if (matches) opts.emit(' selected=""');
187
+ } else if (props.selected) {
188
+ opts.emit(' selected=""');
189
+ }
190
+ }
191
+ if (VOID_ELEMENTS.has(tag)) {
192
+ opts.emit("/>");
193
+ if (opts.textState) opts.textState.lastWasText = false;
194
+ return;
195
+ }
196
+ opts.emit(">");
197
+ const parentTextState = opts.textState;
198
+ const childOpts = { ...opts, textState: { lastWasText: false } };
199
+ const dangerouslyHtml = props.dangerouslySetInnerHTML?.__html;
200
+ if (isTextarea && textareaValue != null) {
201
+ opts.emit(escapeText(String(textareaValue)));
202
+ opts.emit(`</${tag}>`);
203
+ if (parentTextState) parentTextState.lastWasText = false;
204
+ return;
205
+ }
206
+ if (RAW_TEXT_ELEMENTS.has(tag)) {
207
+ if (dangerouslyHtml != null) {
208
+ opts.emit(escapeScript(String(dangerouslyHtml)));
209
+ } else {
210
+ const children = props.children;
211
+ if (typeof children === "string" || typeof children === "number") {
212
+ opts.emit(escapeScript(String(children)));
213
+ } else if (Array.isArray(children)) {
214
+ opts.emit(escapeScript(children.filter((c) => c != null).join("")));
215
+ }
216
+ }
217
+ opts.emit(`</${tag}>`);
218
+ if (parentTextState) parentTextState.lastWasText = false;
219
+ return;
220
+ }
221
+ if (dangerouslyHtml != null) {
222
+ opts.emit(String(dangerouslyHtml));
223
+ } else {
224
+ walkNode(props.children, childOpts);
225
+ }
226
+ opts.emit(`</${tag}>`);
227
+ if (isSelect) popSelectContext();
228
+ if (parentTextState) parentTextState.lastWasText = false;
229
+ }
230
+ function walkComponent(fn, props, opts) {
231
+ if (fn.prototype?.isReactComponent) {
232
+ const ctxType = fn.contextType;
233
+ const ctxValue = ctxType ? ctxType._currentValue : void 0;
234
+ const instance = new fn(props, ctxValue);
235
+ instance.props = props;
236
+ instance.context = ctxValue;
237
+ if (fn.getDerivedStateFromProps) {
238
+ const d = fn.getDerivedStateFromProps(props, instance.state);
239
+ if (d) instance.state = { ...instance.state, ...d };
240
+ }
241
+ walkNode(instance.render(), opts);
242
+ return;
243
+ }
244
+ const rendered = fn(props);
245
+ walkNode(rendered, opts);
246
+ }
247
+ function walkSuspense(props, opts) {
248
+ const id = opts.nextBoundaryId();
249
+ const contextSnapshot = snapshotContexts();
250
+ const childParts = [];
251
+ const childEmit = (s) => childParts.push(s);
252
+ try {
253
+ walkNode(props.children, {
254
+ emit: childEmit,
255
+ onSuspend: opts.onSuspend,
256
+ nextBoundaryId: opts.nextBoundaryId
257
+ });
258
+ } catch (thenable) {
259
+ if (isThenable(thenable)) {
260
+ const fallbackParts = [];
261
+ try {
262
+ walkNode(props.fallback, {
263
+ emit: (s) => fallbackParts.push(s),
264
+ onSuspend: opts.onSuspend,
265
+ nextBoundaryId: opts.nextBoundaryId
266
+ });
267
+ } catch {
268
+ }
269
+ emitBoundary(opts, id, fallbackParts.join(""));
270
+ if (opts.onSuspend) {
271
+ opts.onSuspend({
272
+ id,
273
+ fallbackHTML: fallbackParts.join(""),
274
+ children: props.children,
275
+ thenable,
276
+ contextSnapshot
277
+ });
278
+ }
279
+ return;
280
+ }
281
+ throw thenable;
282
+ }
283
+ opts.emit(`<!--$${id}-->`);
284
+ opts.emit(childParts.join(""));
285
+ opts.emit(`<!--/$-->`);
286
+ }
287
+ function emitBoundary(opts, id, fallbackHTML) {
288
+ opts.emit(`<!--$?${id}--><div id="B:${id}">`);
289
+ opts.emit(fallbackHTML);
290
+ opts.emit(`</div><!--/$-->`);
291
+ }
292
+ function isThenable(x) {
293
+ return x != null && typeof x.then === "function";
294
+ }
295
+ export {
296
+ walk
297
+ };
298
+ //# sourceMappingURL=walk.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/server/walk.ts"],
4
+ "sourcesContent": ["import {\n REACT_ELEMENT_TYPE,\n REACT_LEGACY_ELEMENT_TYPE,\n REACT_FRAGMENT_TYPE,\n type ReactNode,\n type ReactElement,\n} from '../core'\nimport {\n REACT_SUSPENSE_TYPE,\n REACT_PROVIDER_TYPE,\n REACT_CONSUMER_TYPE,\n REACT_FORWARD_REF_TYPE,\n REACT_MEMO_TYPE,\n REACT_LAZY_TYPE,\n REACT_STRICT_MODE_TYPE,\n REACT_PROFILER_TYPE,\n REACT_PORTAL_TYPE,\n} from '../react'\nimport {\n attrToHtml,\n escapeText,\n escapeScript,\n VOID_ELEMENTS,\n RAW_TEXT_ELEMENTS,\n} from './escape'\nimport {\n pushContext,\n popContext,\n snapshotContexts,\n type ContextSnapshot,\n} from './dispatcher'\n\nexport interface SuspendedBoundary {\n id: number\n fallbackHTML: string\n children: ReactNode\n thenable: Promise<any>\n contextSnapshot: ContextSnapshot\n}\n\nexport interface WalkOptions {\n emit: (chunk: string) => void\n onSuspend?: (boundary: SuspendedBoundary) => void\n nextBoundaryId: () => number\n bootstrapped?: boolean\n isBoundaryResolution?: boolean\n /**\n * Tracks whether the most recent emission within the *current text flow*\n * ended with a text node. When the next emission is also text, we emit a\n * `<!-- -->` separator so the browser's HTML parser doesn't merge them\n * into a single text node \u2014 required for hydration to line up text\n * boundaries with the React tree. Reset to `false` whenever we enter a\n * new host element (`<tag>` opens a fresh text flow).\n */\n textState?: { lastWasText: boolean }\n}\n\n/**\n * Synchronously walk a React node and emit HTML string pieces to `opts.emit`.\n * Suspended boundaries are emitted as fallbacks with marker IDs; if `opts.onSuspend`\n * is provided, the suspension is recorded for later streaming.\n */\nexport function walk(node: ReactNode, opts: WalkOptions): void {\n // Seed a text state if the caller didn't provide one so the separator logic\n // is active for the whole tree.\n if (!opts.textState) opts = { ...opts, textState: { lastWasText: false } }\n walkNode(node, opts)\n}\n\n// --- <select> selection context ----------------------------------------------\n// Stack of active select values (or `undefined` when the current select has\n// no controlled value). `<option>` walks read the top of stack to decide\n// whether to emit `selected=\"\"`.\nconst selectValueStack: unknown[] = []\nfunction pushSelectContext(value: unknown): void {\n selectValueStack.push(value)\n}\nfunction popSelectContext(): void {\n selectValueStack.pop()\n}\nfunction currentSelectValue(): unknown {\n return selectValueStack.length ? selectValueStack[selectValueStack.length - 1] : undefined\n}\n\nfunction optionChildText(children: unknown): string {\n // `<option>Text</option>` \u2014 if no `value` prop, the option's value is its\n // flat string/number child content. Matches DOM semantics (`option.value`\n // defaults to `textContent` when no attribute is set).\n if (children == null) return ''\n if (typeof children === 'string' || typeof children === 'number') return '' + children\n if (Array.isArray(children)) return children.map(optionChildText).join('')\n return ''\n}\n\nfunction emitText(text: string, opts: WalkOptions): void {\n // Empty string renders no text node and doesn't start/extend a text flow \u2014\n // skip entirely so sibling text isn't separated by a stray `<!-- -->`.\n if (text === '') return\n if (opts.textState?.lastWasText) opts.emit('<!-- -->')\n opts.emit(escapeText(text))\n if (opts.textState) opts.textState.lastWasText = true\n}\n\nfunction walkNode(node: ReactNode, opts: WalkOptions): void {\n if (node == null || node === false || node === true) return\n\n if (typeof node === 'string') {\n emitText(node, opts)\n return\n }\n if (typeof node === 'number') {\n emitText(String(node), opts)\n return\n }\n if (Array.isArray(node)) {\n for (const c of node) walkNode(c, opts)\n return\n }\n if (typeof (node as any)[Symbol.iterator] === 'function') {\n for (const item of node as Iterable<ReactNode>) walkNode(item, opts)\n return\n }\n\n if (typeof node !== 'object') return\n const t = (node as any).$$typeof\n\n // Raw React.lazy in the tree (RSC Flight encodes 'use client' components \u2014\n // CodeBlock, CodeExplorer, etc. \u2014 as bare Lazy objects directly in the\n // tree, not wrapped in REACT_ELEMENT_TYPE). SSR previously dropped these\n // here, so code snippets never made it into the server HTML. The RSC\n // decoder server-side awaits payloads before rendering, so status is\n // 'fulfilled' and `_init()` returns the resolved element synchronously.\n // If still pending (shouldn't happen post-awaitLazyElements), throw the\n // thenable so streaming SSR suspends the current boundary and retries.\n if (t === REACT_LAZY_TYPE) {\n const lazy = node as any\n const resolved = lazy._init(lazy._payload)\n walkNode(resolved, opts)\n return\n }\n\n if (t !== REACT_ELEMENT_TYPE && t !== REACT_LEGACY_ELEMENT_TYPE) return\n\n const el = node as ReactElement\n walkElement(el, opts)\n}\n\nfunction walkElement(el: ReactElement, opts: WalkOptions): void {\n const type = el.type\n const props = el.props ?? {}\n\n if (type === REACT_FRAGMENT_TYPE || type === REACT_STRICT_MODE_TYPE || type === REACT_PROFILER_TYPE) {\n walkNode(props.children, opts)\n return\n }\n\n if (type === REACT_SUSPENSE_TYPE) {\n walkSuspense(props, opts)\n return\n }\n\n if (typeof type === 'string') {\n walkHost(type, props, opts)\n return\n }\n\n const marker = (type as any)?.$$typeof\n\n if (marker === REACT_PORTAL_TYPE) {\n // Portals don't render to the main HTML output on the server.\n return\n }\n\n if (marker === REACT_PROVIDER_TYPE) {\n const ctx = (type as any)._context\n pushContext(ctx, props.value)\n try {\n walkNode(props.children, opts)\n } finally {\n popContext(ctx)\n }\n return\n }\n\n if (marker === REACT_CONSUMER_TYPE) {\n const ctx = (type as any)._context\n const render = props.children\n if (typeof render === 'function') {\n walkNode(render(ctx._currentValue), opts)\n }\n return\n }\n\n if (marker === REACT_FORWARD_REF_TYPE) {\n const render = (type as any).render\n const ref = (props as any).ref ?? null\n const { ref: _omit, ...rest } = props as any\n const rendered = render(rest, ref)\n walkNode(rendered, opts)\n return\n }\n\n if (marker === REACT_MEMO_TYPE) {\n const inner = (type as any).type\n walkElement({ ...el, type: inner } as ReactElement, opts)\n return\n }\n\n if (marker === REACT_LAZY_TYPE) {\n const { _payload, _init } = type as any\n try {\n const resolved = _init(_payload)\n walkElement({ ...el, type: resolved } as ReactElement, opts)\n } catch (thenable: any) {\n if (isThenable(thenable)) {\n // Suspend this point\n throw thenable\n }\n throw thenable\n }\n return\n }\n\n if (typeof type === 'function') {\n walkComponent(type, props, opts)\n return\n }\n}\n\nfunction walkHost(\n tag: string,\n props: Record<string, any>,\n opts: WalkOptions,\n): void {\n // <textarea value=\"...\"> serializes its value as a TEXT CHILD, not an\n // attribute. `defaultValue` is the fallback when `value` is absent. This\n // matches React and the HTML spec \u2014 `<textarea value=\"x\">` is not valid\n // HTML; the value is the element's textContent.\n const isTextarea = tag === 'textarea'\n const textareaValue = isTextarea\n ? props.value != null\n ? props.value\n : props.defaultValue\n : undefined\n\n // <input defaultValue=\"...\"> should parse with that value \u2014 emit it as a\n // `value` attribute. Similarly `defaultChecked` becomes `checked`. This\n // keeps hydration consistent: the browser parser sees the initial value,\n // and on client commit our setProp seeds `.defaultValue`/`.defaultChecked`\n // without stomping the user-typed value.\n const isInput = tag === 'input'\n const inputValueAttr =\n isInput && props.value == null && props.defaultValue != null\n ? props.defaultValue\n : undefined\n const inputCheckedAttr =\n isInput && props.checked == null && props.defaultChecked != null\n ? props.defaultChecked\n : undefined\n\n // <select value=\"...\"> does NOT become an attribute on `<select>` \u2014 the\n // HTML spec has no such attribute. React resolves the selection by stamping\n // `selected` on the matching `<option>` children during render. Stash the\n // target value(s) on the walk state and the child `<option>` walk reads it.\n const isSelect = tag === 'select'\n if (isSelect) {\n const val = props.value != null ? props.value : props.defaultValue\n pushSelectContext(val)\n }\n const isOption = tag === 'option'\n\n // Prepend the HTML5 doctype to the stream when rendering an <html> root.\n // Without it the browser parses the document in quirks mode, which breaks\n // CSS sizing (documentElement.clientHeight returns the content height, not\n // the viewport) \u2014 and Floating-UI-based libraries (Radix dropdowns etc.)\n // then compute off-screen positions for overlays.\n if (tag === 'html') opts.emit('<!DOCTYPE html>')\n\n opts.emit('<' + tag)\n for (const k in props) {\n if (isTextarea && (k === 'value' || k === 'defaultValue')) continue\n if (isInput && (k === 'defaultValue' || k === 'defaultChecked')) continue\n if (isSelect && (k === 'value' || k === 'defaultValue')) continue\n if (isOption && k === 'selected') continue\n opts.emit(attrToHtml(k, props[k]))\n }\n if (inputValueAttr !== undefined) {\n opts.emit(attrToHtml('value', inputValueAttr))\n }\n if (inputCheckedAttr !== undefined) {\n opts.emit(attrToHtml('checked', inputCheckedAttr))\n }\n if (isOption) {\n const selectVal = currentSelectValue()\n if (selectVal !== undefined) {\n const optionValue =\n props.value != null ? props.value : optionChildText(props.children)\n const matches = Array.isArray(selectVal)\n ? selectVal.some((v) => '' + v === '' + optionValue)\n : '' + selectVal === '' + optionValue\n if (matches) opts.emit(' selected=\"\"')\n } else if (props.selected) {\n opts.emit(' selected=\"\"')\n }\n }\n\n if (VOID_ELEMENTS.has(tag)) {\n opts.emit('/>')\n if (opts.textState) opts.textState.lastWasText = false\n return\n }\n opts.emit('>')\n // Opening a host element starts a fresh text flow context for its children.\n // Children's text separator tracking is independent of the outer context.\n const parentTextState = opts.textState\n const childOpts: WalkOptions = { ...opts, textState: { lastWasText: false } }\n\n const dangerouslyHtml = props.dangerouslySetInnerHTML?.__html\n\n if (isTextarea && textareaValue != null) {\n opts.emit(escapeText(String(textareaValue)))\n opts.emit(`</${tag}>`)\n if (parentTextState) parentTextState.lastWasText = false\n return\n }\n\n if (RAW_TEXT_ELEMENTS.has(tag)) {\n // script/style: raw-text. React allows either a string/number child or\n // dangerouslySetInnerHTML \u2014 some libs (Start's Scripts) use the latter.\n if (dangerouslyHtml != null) {\n opts.emit(escapeScript(String(dangerouslyHtml)))\n } else {\n const children = props.children\n if (typeof children === 'string' || typeof children === 'number') {\n opts.emit(escapeScript(String(children)))\n } else if (Array.isArray(children)) {\n opts.emit(escapeScript(children.filter((c) => c != null).join('')))\n }\n }\n opts.emit(`</${tag}>`)\n if (parentTextState) parentTextState.lastWasText = false\n return\n }\n\n if (dangerouslyHtml != null) {\n opts.emit(String(dangerouslyHtml))\n } else {\n walkNode(props.children, childOpts)\n }\n opts.emit(`</${tag}>`)\n if (isSelect) popSelectContext()\n // Host element closing resets outer flow \u2014 next sibling text starts fresh.\n if (parentTextState) parentTextState.lastWasText = false\n}\n\nfunction walkComponent(\n fn: Function,\n props: Record<string, any>,\n opts: WalkOptions,\n): void {\n if ((fn as any).prototype?.isReactComponent) {\n const ctxType = (fn as any).contextType\n const ctxValue = ctxType ? ctxType._currentValue : undefined\n const instance = new (fn as any)(props, ctxValue)\n instance.props = props\n instance.context = ctxValue\n if ((fn as any).getDerivedStateFromProps) {\n const d = (fn as any).getDerivedStateFromProps(props, instance.state)\n if (d) instance.state = { ...instance.state, ...d }\n }\n walkNode(instance.render(), opts)\n return\n }\n const rendered = (fn as any)(props)\n walkNode(rendered, opts)\n}\n\nfunction walkSuspense(\n props: Record<string, any>,\n opts: WalkOptions,\n): void {\n const id = opts.nextBoundaryId()\n // Snapshot contexts BEFORE attempting children, so if a descendant suspends\n // we can replay the same provider stack when re-rendering the boundary.\n const contextSnapshot = snapshotContexts()\n\n // Try to render the children synchronously. If a thenable is thrown,\n // record the boundary and emit the fallback.\n const childParts: string[] = []\n const childEmit = (s: string) => childParts.push(s)\n try {\n walkNode(props.children, {\n emit: childEmit,\n onSuspend: opts.onSuspend,\n nextBoundaryId: opts.nextBoundaryId,\n })\n } catch (thenable: any) {\n if (isThenable(thenable)) {\n const fallbackParts: string[] = []\n try {\n walkNode(props.fallback, {\n emit: (s) => fallbackParts.push(s),\n onSuspend: opts.onSuspend,\n nextBoundaryId: opts.nextBoundaryId,\n })\n } catch {\n // Fallback suspending is unsupported; emit nothing\n }\n emitBoundary(opts, id, fallbackParts.join(''))\n\n if (opts.onSuspend) {\n opts.onSuspend({\n id,\n fallbackHTML: fallbackParts.join(''),\n children: props.children,\n thenable,\n contextSnapshot,\n })\n }\n return\n }\n throw thenable\n }\n\n // Children rendered fully \u2014 emit them wrapped in resolved-boundary markers\n // (`<!--$N-->` / `<!--/$-->`). Without markers, the client hydrator has no\n // way to know this subtree is inside a Suspense, so if the client version\n // of a descendant (e.g. `React.lazy`) suspends it can't pinpoint which DOM\n // range to adopt on resolve \u2014 it creates fresh DOM next to the SSR content,\n // producing visible duplicates (e.g. double navbar logos). Markers let the\n // client treat this as a resolved boundary and hydrate in-place.\n opts.emit(`<!--$${id}-->`)\n opts.emit(childParts.join(''))\n opts.emit(`<!--/$-->`)\n}\n\nfunction emitBoundary(opts: WalkOptions, id: number, fallbackHTML: string): void {\n // Visible div wrapper so the fallback UI shows; B: id lets $RC locate it on\n // reveal. The leading/trailing comments let hydration detect a pending\n // boundary and register a reveal callback.\n opts.emit(`<!--$?${id}--><div id=\"B:${id}\">`)\n opts.emit(fallbackHTML)\n opts.emit(`</div><!--/$-->`)\n}\n\nfunction isThenable(x: any): x is Promise<any> {\n return x != null && typeof x.then === 'function'\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,96 @@
1
+ export type RedactPreset = 'nano' | 'full';
2
+ /**
3
+ * Opt-in feature set. Each flag toggles whether the feature's real
4
+ * implementation ships (`true`) or is swapped with a stub module that
5
+ * degrades gracefully (`false`). Missing keys fall back to the preset's
6
+ * default. Adding a feature to this interface propagates to consumer
7
+ * configs as autocompleted options.
8
+ */
9
+ export interface RedactFeatures {
10
+ /**
11
+ * `createPortal`. When `false`, portal elements render in place as a
12
+ * Fragment (the `container` prop is ignored). `renderPortal` and its
13
+ * deps are stripped from the bundle.
14
+ */
15
+ portal?: boolean;
16
+ /**
17
+ * `createContext` / `useContext` / `<Provider>` / `<Consumer>`. When
18
+ * `false`, Providers render as Fragments (value never propagates),
19
+ * Consumers invoke their function-children with the context's default
20
+ * value, and `useContext` returns the default. Provider-walk logic and
21
+ * `renderProvider` are stripped.
22
+ */
23
+ context?: boolean;
24
+ /**
25
+ * `<Suspense>` boundaries + streaming hydration. When `false`, Suspense
26
+ * elements render as Fragments (children mount inline, `fallback` is
27
+ * ignored). Thrown thenables still schedule a re-render on settle, so
28
+ * eventual consistency works — just no fallback UI during the pending
29
+ * window. Boundary-handler stack and hydration integration are stripped.
30
+ */
31
+ suspense?: boolean;
32
+ /**
33
+ * `React.memo`. When `false`, memoized components still render but without
34
+ * the prop-equality gate — every parent rerender passes through.
35
+ * `shallowEqual` and the force-rerender bypass are stripped.
36
+ */
37
+ memo?: boolean;
38
+ /**
39
+ * `React.forwardRef`. When `false`, forwardRef components still render but
40
+ * the ref prop isn't forwarded to the inner function. React 19+ treats
41
+ * refs as normal props on function components anyway, so most apps can
42
+ * drop this. The dispatcher save/restore machinery is stripped.
43
+ */
44
+ forwardRef?: boolean;
45
+ /**
46
+ * `React.lazy`. When `false`, lazy elements still resolve if their payload
47
+ * is already available synchronously (e.g. pre-awaited RSC Flight); async
48
+ * resolution throws a clear error. The hydration-deferred-reveal path and
49
+ * Suspense coordination are stripped.
50
+ */
51
+ lazy?: boolean;
52
+ /**
53
+ * Class components (`extends Component`). When `false`, class components
54
+ * still render but only honor the core contract: constructor + `render()`
55
+ * + `setState`. Dropped: `contextType`, `getDerivedStateFromProps`,
56
+ * `shouldComponentUpdate`, `componentDidMount`/`Update`/`WillUnmount`,
57
+ * `getDerivedStateFromError`/`componentDidCatch` (error boundaries).
58
+ */
59
+ classComponents?: boolean;
60
+ /**
61
+ * SSR hydration (`hydrateRoot`). When `false`, `hydrateRoot` throws
62
+ * (use `createRoot` for SPAs). The HydrationCursor / DOM adoption /
63
+ * streaming-boundary coordination / event-replay / scroll-guard
64
+ * machinery is stripped — the biggest single chunk of reducible code.
65
+ */
66
+ hydration?: boolean;
67
+ }
68
+ export interface RedactOptions {
69
+ /** Skip aliasing specific specifiers, e.g. if a consumer wants real React somewhere. */
70
+ skip?: ReadonlyArray<string>;
71
+ /**
72
+ * Override package resolution root. Defaults to the Vite config root. Useful
73
+ * for monorepos where the plugin lives in a different workspace than the
74
+ * consumer app.
75
+ */
76
+ resolveFrom?: string;
77
+ /**
78
+ * Explicit package roots, bypassing node_modules lookup. Keys are package
79
+ * names (e.g. `@tanstack/redact`), values are absolute paths to the package
80
+ * directory. Handy for cross-workspace testing / bring-your-own-build setups.
81
+ */
82
+ packageRoots?: Record<string, string>;
83
+ /**
84
+ * Starting point for feature selection. `'full'` (default) turns every
85
+ * feature on — drop-in React parity, opt-out individual features via
86
+ * `features`. `'nano'` turns everything off — opt in to what you need.
87
+ */
88
+ preset?: RedactPreset;
89
+ /**
90
+ * Per-feature overrides merged on top of the preset's defaults. Enables
91
+ * fine-grained "preset minus X" or "preset plus Y" configurations.
92
+ */
93
+ features?: RedactFeatures;
94
+ }
95
+ export declare function redact(options?: RedactOptions): any;
96
+ export default redact;
@@ -0,0 +1,217 @@
1
+ // packages/redact/src/vite/index.ts
2
+ import { existsSync, readFileSync, realpathSync } from "node:fs";
3
+ import { dirname, resolve as resolvePath } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ var PRESET_DEFAULTS = {
6
+ // Opt-in: everything off. Turn individual features on via `features`.
7
+ nano: {
8
+ portal: false,
9
+ context: false,
10
+ suspense: false,
11
+ memo: false,
12
+ forwardRef: false,
13
+ lazy: false,
14
+ classComponents: false,
15
+ hydration: false
16
+ },
17
+ // Opt-out: everything on (drop-in React parity). Turn features off via `features`.
18
+ full: {
19
+ portal: true,
20
+ context: true,
21
+ suspense: true,
22
+ memo: true,
23
+ forwardRef: true,
24
+ lazy: true,
25
+ classComponents: true,
26
+ hydration: true
27
+ }
28
+ };
29
+ function resolveFeatures(preset, overrides) {
30
+ const p = PRESET_DEFAULTS[preset];
31
+ return {
32
+ portal: overrides.portal ?? p.portal,
33
+ context: overrides.context ?? p.context,
34
+ suspense: overrides.suspense ?? p.suspense,
35
+ memo: overrides.memo ?? p.memo,
36
+ forwardRef: overrides.forwardRef ?? p.forwardRef,
37
+ lazy: overrides.lazy ?? p.lazy,
38
+ classComponents: overrides.classComponents ?? p.classComponents,
39
+ hydration: overrides.hydration ?? p.hydration
40
+ };
41
+ }
42
+ var ALIASES = {
43
+ // ---- most-specific first ----
44
+ "use-sync-external-store/shim/with-selector": "@tanstack/redact",
45
+ "use-sync-external-store/shim/with-selector.js": "@tanstack/redact",
46
+ "use-sync-external-store/with-selector": "@tanstack/redact",
47
+ "use-sync-external-store/with-selector.js": "@tanstack/redact",
48
+ "use-sync-external-store/shim": "@tanstack/redact",
49
+ "use-sync-external-store": "@tanstack/redact",
50
+ // React drop-in shim targets. Subpaths first.
51
+ "react/jsx-runtime": "@tanstack/redact/jsx-runtime",
52
+ "react/jsx-dev-runtime": "@tanstack/redact/jsx-dev-runtime",
53
+ "react-dom/client": "@tanstack/redact/dom-client",
54
+ "react-dom/server": "@tanstack/redact/server",
55
+ "react-dom/test-utils": "@tanstack/redact/dom-test-utils",
56
+ "react-dom": "@tanstack/redact/dom",
57
+ react: "@tanstack/redact",
58
+ scheduler: "@tanstack/redact/scheduler",
59
+ // Self-aliases so Vite resolves `@tanstack/redact/*` imports to the same
60
+ // canonical file path no matter where they originate (worker bundle vs
61
+ // deps_ssr pre-bundle vs source). Without these, Cloudflare's
62
+ // `noExternal: true` worker config inlines one copy while Vite's
63
+ // optimizeDeps pre-bundles another, ending up with two separate
64
+ // ReactSharedInternals instances and a null dispatcher in user hooks.
65
+ // Subpaths first here too.
66
+ "@tanstack/redact/jsx-runtime": "@tanstack/redact/jsx-runtime",
67
+ "@tanstack/redact/jsx-dev-runtime": "@tanstack/redact/jsx-dev-runtime",
68
+ "@tanstack/redact/dom-client": "@tanstack/redact/dom-client",
69
+ "@tanstack/redact/dom-test-utils": "@tanstack/redact/dom-test-utils",
70
+ "@tanstack/redact/server": "@tanstack/redact/server",
71
+ "@tanstack/redact/scheduler": "@tanstack/redact/scheduler",
72
+ "@tanstack/redact/dom": "@tanstack/redact/dom",
73
+ "@tanstack/redact": "@tanstack/redact"
74
+ };
75
+ function splitSpecifier(specifier) {
76
+ if (specifier.startsWith("@")) {
77
+ const slash1 = specifier.indexOf("/");
78
+ const slash2 = specifier.indexOf("/", slash1 + 1);
79
+ if (slash2 < 0) return { pkg: specifier, sub: "" };
80
+ return { pkg: specifier.slice(0, slash2), sub: specifier.slice(slash2 + 1) };
81
+ }
82
+ const slash = specifier.indexOf("/");
83
+ if (slash < 0) return { pkg: specifier, sub: "" };
84
+ return { pkg: specifier.slice(0, slash), sub: specifier.slice(slash + 1) };
85
+ }
86
+ function findPackageDir(pkg, fromDir) {
87
+ let dir = fromDir;
88
+ while (true) {
89
+ const candidate = resolvePath(dir, "node_modules", pkg);
90
+ if (existsSync(resolvePath(candidate, "package.json"))) return candidate;
91
+ const parent = dirname(dir);
92
+ if (parent === dir) return null;
93
+ dir = parent;
94
+ }
95
+ }
96
+ function resolveExport(packageDir, sub) {
97
+ const pkgJsonPath = resolvePath(packageDir, "package.json");
98
+ let pkg;
99
+ try {
100
+ pkg = JSON.parse(readFileSync(pkgJsonPath, "utf8"));
101
+ } catch {
102
+ return null;
103
+ }
104
+ const key = sub ? "./" + sub : ".";
105
+ const exp = pkg.exports?.[key];
106
+ const pick = (v) => {
107
+ if (typeof v === "string") return v;
108
+ if (v && typeof v === "object") {
109
+ return pick(v.import ?? v.module ?? v.source ?? v.default ?? null);
110
+ }
111
+ return null;
112
+ };
113
+ const target = pick(exp);
114
+ if (target) return resolvePath(packageDir, target);
115
+ if (!sub) {
116
+ const main = pkg.module ?? pkg.main;
117
+ if (typeof main === "string") return resolvePath(packageDir, main);
118
+ }
119
+ return null;
120
+ }
121
+ var pluginDir = dirname(fileURLToPath(import.meta.url));
122
+ function resolveSpecifier(specifier, fromDir, packageRoots) {
123
+ const { pkg, sub } = splitSpecifier(specifier);
124
+ const packageDir = packageRoots[pkg] ?? findPackageDir(pkg, pluginDir) ?? findPackageDir(pkg, fromDir);
125
+ if (!packageDir) return null;
126
+ const target = resolveExport(packageDir, sub);
127
+ if (!target) return null;
128
+ try {
129
+ return realpathSync(target);
130
+ } catch {
131
+ return target;
132
+ }
133
+ }
134
+ function redact(options = {}) {
135
+ const skip = new Set(options.skip ?? []);
136
+ const entries = Object.entries(ALIASES).filter(([k]) => !skip.has(k));
137
+ const features = resolveFeatures(options.preset ?? "full", options.features ?? {});
138
+ const resolvedMap = {};
139
+ let done = false;
140
+ function resolveAll(root) {
141
+ if (done) return;
142
+ const fromDir = options.resolveFrom ?? root;
143
+ const packageRoots = options.packageRoots ?? {};
144
+ for (const [from, to] of entries) {
145
+ const resolved = resolveSpecifier(to, fromDir, packageRoots);
146
+ if (resolved) resolvedMap[from] = resolved;
147
+ }
148
+ done = true;
149
+ }
150
+ return {
151
+ name: "redact",
152
+ enforce: "pre",
153
+ config() {
154
+ const excludeList = entries.map(([k]) => k);
155
+ const noExt = ["@tanstack/redact"];
156
+ const aliasMap = Object.fromEntries(entries.filter(([from, to]) => from !== to));
157
+ const dedupe = noExt;
158
+ return {
159
+ resolve: {
160
+ alias: aliasMap,
161
+ dedupe
162
+ },
163
+ environments: {
164
+ client: {
165
+ optimizeDeps: { exclude: excludeList }
166
+ },
167
+ ssr: {
168
+ optimizeDeps: { exclude: excludeList },
169
+ resolve: { noExternal: noExt }
170
+ }
171
+ },
172
+ ssr: { noExternal: noExt }
173
+ };
174
+ },
175
+ configResolved(config) {
176
+ resolveAll(config.root);
177
+ const fsAllow = Object.values(options.packageRoots ?? {});
178
+ if (fsAllow.length && config.server?.fs?.allow) {
179
+ for (const p of fsAllow) {
180
+ if (!config.server.fs.allow.includes(p)) {
181
+ config.server.fs.allow.push(p);
182
+ }
183
+ }
184
+ }
185
+ },
186
+ async resolveId(id, importer, opts) {
187
+ const envName = this?.environment?.name;
188
+ if (envName === "rsc") return null;
189
+ if (importer && /[\\/]features[\\/]index\.[jt]sx?$/.test(importer)) {
190
+ const m = id.match(/^\.\/([a-z-]+)$/);
191
+ if (m) {
192
+ const name = m[1];
193
+ if (name in features && !features[name]) {
194
+ const r = await this.resolve(`./${name}/stub`, importer, {
195
+ ...opts,
196
+ skipSelf: true
197
+ });
198
+ if (r) return r.id;
199
+ }
200
+ }
201
+ }
202
+ if (!features.hydration && importer && /[\\/]hydration$/.test(id)) {
203
+ const r = await this.resolve(id, importer, { ...opts, skipSelf: true });
204
+ if (r && /features[\\/]hydration[\\/]index\.(ts|js)$/.test(r.id)) {
205
+ return r.id.replace(/index\.(ts|js)$/, "stub.$1");
206
+ }
207
+ }
208
+ return resolvedMap[id] ?? null;
209
+ }
210
+ };
211
+ }
212
+ var vite_default = redact;
213
+ export {
214
+ vite_default as default,
215
+ redact
216
+ };
217
+ //# sourceMappingURL=index.js.map