@tanstack/router-devtools-core 1.167.0 → 1.167.2
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/{BaseTanStackRouterDevtoolsPanel-DwUaC87U.js → BaseTanStackRouterDevtoolsPanel-BYamTwOT.js} +263 -59
- package/dist/BaseTanStackRouterDevtoolsPanel-BYamTwOT.js.map +1 -0
- package/dist/{BaseTanStackRouterDevtoolsPanel-BlI6Kawa.cjs → BaseTanStackRouterDevtoolsPanel-CKR5l6C5.cjs} +263 -60
- package/dist/BaseTanStackRouterDevtoolsPanel-CKR5l6C5.cjs.map +1 -0
- package/dist/{FloatingTanStackRouterDevtools-U4pxMObm.js → FloatingTanStackRouterDevtools-5lmIMjR6.js} +2 -2
- package/dist/{FloatingTanStackRouterDevtools-U4pxMObm.js.map → FloatingTanStackRouterDevtools-5lmIMjR6.js.map} +1 -1
- package/dist/{FloatingTanStackRouterDevtools-M-UhaKLc.cjs → FloatingTanStackRouterDevtools-eF_9_NhU.cjs} +2 -2
- package/dist/{FloatingTanStackRouterDevtools-M-UhaKLc.cjs.map → FloatingTanStackRouterDevtools-eF_9_NhU.cjs.map} +1 -1
- package/dist/cjs/Explorer.d.cts +1 -1
- package/dist/cjs/index.cjs +2 -2
- package/dist/cjs/utils.d.cts +24 -0
- package/dist/esm/Explorer.d.ts +1 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/utils.d.ts +24 -0
- package/package.json +5 -6
- package/src/BaseTanStackRouterDevtoolsPanel.tsx +6 -12
- package/src/Explorer.tsx +155 -5
- package/src/utils.tsx +129 -1
- package/dist/BaseTanStackRouterDevtoolsPanel-BlI6Kawa.cjs.map +0 -1
- package/dist/BaseTanStackRouterDevtoolsPanel-DwUaC87U.js.map +0 -1
package/dist/cjs/Explorer.d.cts
CHANGED
|
@@ -3,7 +3,7 @@ type ExpanderProps = {
|
|
|
3
3
|
expanded: boolean;
|
|
4
4
|
style?: JSX.CSSProperties;
|
|
5
5
|
};
|
|
6
|
-
export declare const Expander: ({ expanded, style }: ExpanderProps) => JSX.Element;
|
|
6
|
+
export declare const Expander: ({ expanded, style: _style }: ExpanderProps) => JSX.Element;
|
|
7
7
|
type Entry = {
|
|
8
8
|
label: string;
|
|
9
9
|
};
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -40,7 +40,7 @@ var TanStackRouterDevtoolsCore = class {
|
|
|
40
40
|
let Devtools;
|
|
41
41
|
if (this.#Component) Devtools = this.#Component;
|
|
42
42
|
else {
|
|
43
|
-
Devtools = require_context.lazy(() => Promise.resolve().then(() => require("../FloatingTanStackRouterDevtools-
|
|
43
|
+
Devtools = require_context.lazy(() => Promise.resolve().then(() => require("../FloatingTanStackRouterDevtools-eF_9_NhU.cjs")));
|
|
44
44
|
this.#Component = Devtools;
|
|
45
45
|
}
|
|
46
46
|
return require_context.createComponent(require_context.ShadowDomTargetContext.Provider, {
|
|
@@ -114,7 +114,7 @@ var TanStackRouterDevtoolsPanelCore = class {
|
|
|
114
114
|
let BaseTanStackRouterDevtoolsPanel;
|
|
115
115
|
if (this.#Component) BaseTanStackRouterDevtoolsPanel = this.#Component;
|
|
116
116
|
else {
|
|
117
|
-
BaseTanStackRouterDevtoolsPanel = require_context.lazy(() => Promise.resolve().then(() => require("../BaseTanStackRouterDevtoolsPanel-
|
|
117
|
+
BaseTanStackRouterDevtoolsPanel = require_context.lazy(() => Promise.resolve().then(() => require("../BaseTanStackRouterDevtoolsPanel-CKR5l6C5.cjs")));
|
|
118
118
|
this.#Component = BaseTanStackRouterDevtoolsPanel;
|
|
119
119
|
}
|
|
120
120
|
return require_context.createComponent(require_context.ShadowDomTargetContext.Provider, {
|
package/dist/cjs/utils.d.cts
CHANGED
|
@@ -10,6 +10,30 @@ export declare function styled<T extends keyof HTMLElementTagNameMap>(type: T, n
|
|
|
10
10
|
ref?: HTMLElementTagNameMap[T] | undefined;
|
|
11
11
|
}) => JSX.Element;
|
|
12
12
|
export declare function useIsMounted(): import('solid-js').Accessor<boolean>;
|
|
13
|
+
export type RscSlotUsageEvent = {
|
|
14
|
+
slot: string;
|
|
15
|
+
args?: Array<any>;
|
|
16
|
+
};
|
|
17
|
+
export type ServerComponentType = 'compositeSource' | 'renderableValue' | null;
|
|
18
|
+
/**
|
|
19
|
+
* Checks if a value is any kind of server component
|
|
20
|
+
*/
|
|
21
|
+
export declare const isServerComponent: (value: unknown) => boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Gets the type of server component.
|
|
24
|
+
* - RENDERABLE_RSC === true → renderable (from renderServerComponent)
|
|
25
|
+
* - RENDERABLE_RSC === false or not present → composite (from createCompositeComponent)
|
|
26
|
+
*/
|
|
27
|
+
export declare const getServerComponentType: (value: unknown) => ServerComponentType;
|
|
28
|
+
/**
|
|
29
|
+
* Gets the slot names from a composite server component (dev only)
|
|
30
|
+
*/
|
|
31
|
+
export declare const getServerComponentSlots: (value: unknown) => Array<string>;
|
|
32
|
+
export declare const getServerComponentSlotUsages: (value: unknown) => Array<RscSlotUsageEvent>;
|
|
33
|
+
export declare const getServerComponentSlotUsageSummary: (value: unknown) => Record<string, {
|
|
34
|
+
count: number;
|
|
35
|
+
invocations: Array<Array<any>>;
|
|
36
|
+
}>;
|
|
13
37
|
/**
|
|
14
38
|
* Displays a string regardless the type of the data
|
|
15
39
|
* @param {unknown} value Value to be stringified
|
package/dist/esm/Explorer.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ type ExpanderProps = {
|
|
|
3
3
|
expanded: boolean;
|
|
4
4
|
style?: JSX.CSSProperties;
|
|
5
5
|
};
|
|
6
|
-
export declare const Expander: ({ expanded, style }: ExpanderProps) => JSX.Element;
|
|
6
|
+
export declare const Expander: ({ expanded, style: _style }: ExpanderProps) => JSX.Element;
|
|
7
7
|
type Entry = {
|
|
8
8
|
label: string;
|
|
9
9
|
};
|
package/dist/esm/index.js
CHANGED
|
@@ -39,7 +39,7 @@ var TanStackRouterDevtoolsCore = class {
|
|
|
39
39
|
let Devtools;
|
|
40
40
|
if (this.#Component) Devtools = this.#Component;
|
|
41
41
|
else {
|
|
42
|
-
Devtools = lazy(() => import("../FloatingTanStackRouterDevtools-
|
|
42
|
+
Devtools = lazy(() => import("../FloatingTanStackRouterDevtools-5lmIMjR6.js"));
|
|
43
43
|
this.#Component = Devtools;
|
|
44
44
|
}
|
|
45
45
|
return createComponent(ShadowDomTargetContext.Provider, {
|
|
@@ -113,7 +113,7 @@ var TanStackRouterDevtoolsPanelCore = class {
|
|
|
113
113
|
let BaseTanStackRouterDevtoolsPanel;
|
|
114
114
|
if (this.#Component) BaseTanStackRouterDevtoolsPanel = this.#Component;
|
|
115
115
|
else {
|
|
116
|
-
BaseTanStackRouterDevtoolsPanel = lazy(() => import("../BaseTanStackRouterDevtoolsPanel-
|
|
116
|
+
BaseTanStackRouterDevtoolsPanel = lazy(() => import("../BaseTanStackRouterDevtoolsPanel-BYamTwOT.js"));
|
|
117
117
|
this.#Component = BaseTanStackRouterDevtoolsPanel;
|
|
118
118
|
}
|
|
119
119
|
return createComponent(ShadowDomTargetContext.Provider, {
|
package/dist/esm/utils.d.ts
CHANGED
|
@@ -10,6 +10,30 @@ export declare function styled<T extends keyof HTMLElementTagNameMap>(type: T, n
|
|
|
10
10
|
ref?: HTMLElementTagNameMap[T] | undefined;
|
|
11
11
|
}) => JSX.Element;
|
|
12
12
|
export declare function useIsMounted(): import('solid-js').Accessor<boolean>;
|
|
13
|
+
export type RscSlotUsageEvent = {
|
|
14
|
+
slot: string;
|
|
15
|
+
args?: Array<any>;
|
|
16
|
+
};
|
|
17
|
+
export type ServerComponentType = 'compositeSource' | 'renderableValue' | null;
|
|
18
|
+
/**
|
|
19
|
+
* Checks if a value is any kind of server component
|
|
20
|
+
*/
|
|
21
|
+
export declare const isServerComponent: (value: unknown) => boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Gets the type of server component.
|
|
24
|
+
* - RENDERABLE_RSC === true → renderable (from renderServerComponent)
|
|
25
|
+
* - RENDERABLE_RSC === false or not present → composite (from createCompositeComponent)
|
|
26
|
+
*/
|
|
27
|
+
export declare const getServerComponentType: (value: unknown) => ServerComponentType;
|
|
28
|
+
/**
|
|
29
|
+
* Gets the slot names from a composite server component (dev only)
|
|
30
|
+
*/
|
|
31
|
+
export declare const getServerComponentSlots: (value: unknown) => Array<string>;
|
|
32
|
+
export declare const getServerComponentSlotUsages: (value: unknown) => Array<RscSlotUsageEvent>;
|
|
33
|
+
export declare const getServerComponentSlotUsageSummary: (value: unknown) => Record<string, {
|
|
34
|
+
count: number;
|
|
35
|
+
invocations: Array<Array<any>>;
|
|
36
|
+
}>;
|
|
13
37
|
/**
|
|
14
38
|
* Displays a string regardless the type of the data
|
|
15
39
|
* @param {unknown} value Value to be stringified
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/router-devtools-core",
|
|
3
|
-
"version": "1.167.
|
|
3
|
+
"version": "1.167.2",
|
|
4
4
|
"description": "Modern and scalable routing for Web applications",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -50,8 +50,7 @@
|
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"clsx": "^2.1.1",
|
|
53
|
-
"goober": "^2.1.16"
|
|
54
|
-
"tiny-invariant": "^1.3.3"
|
|
53
|
+
"goober": "^2.1.16"
|
|
55
54
|
},
|
|
56
55
|
"devDependencies": {
|
|
57
56
|
"solid-js": "^1.9.10",
|
|
@@ -60,7 +59,7 @@
|
|
|
60
59
|
},
|
|
61
60
|
"peerDependencies": {
|
|
62
61
|
"csstype": "^3.0.10",
|
|
63
|
-
"@tanstack/router-core": "^1.168.
|
|
62
|
+
"@tanstack/router-core": "^1.168.10"
|
|
64
63
|
},
|
|
65
64
|
"peerDependenciesMeta": {
|
|
66
65
|
"csstype": {
|
|
@@ -71,12 +70,12 @@
|
|
|
71
70
|
"clean": "rimraf ./dist && rimraf ./coverage",
|
|
72
71
|
"test:eslint": "eslint ./src",
|
|
73
72
|
"test:types": "pnpm run \"/^test:types:ts[0-9]{2}$/\"",
|
|
74
|
-
"test:types:ts54": "node ../../node_modules/typescript54/lib/tsc.js",
|
|
75
73
|
"test:types:ts55": "node ../../node_modules/typescript55/lib/tsc.js",
|
|
76
74
|
"test:types:ts56": "node ../../node_modules/typescript56/lib/tsc.js",
|
|
77
75
|
"test:types:ts57": "node ../../node_modules/typescript57/lib/tsc.js",
|
|
78
76
|
"test:types:ts58": "node ../../node_modules/typescript58/lib/tsc.js",
|
|
79
|
-
"test:types:ts59": "tsc",
|
|
77
|
+
"test:types:ts59": "node ../../node_modules/typescript59/lib/tsc.js",
|
|
78
|
+
"test:types:ts60": "tsc",
|
|
80
79
|
"test:build": "publint --strict && attw --ignore-rules no-resolution --pack .",
|
|
81
80
|
"build": "vite build"
|
|
82
81
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { clsx as cx } from 'clsx'
|
|
2
|
-
import { default as invariant } from 'tiny-invariant'
|
|
3
2
|
import { interpolatePath, rootRouteId, trimPath } from '@tanstack/router-core'
|
|
4
3
|
import {
|
|
5
4
|
For,
|
|
@@ -264,11 +263,6 @@ export const BaseTanStackRouterDevtoolsPanel =
|
|
|
264
263
|
const styles = useStyles()
|
|
265
264
|
const { className, style, ...otherPanelProps } = panelProps
|
|
266
265
|
|
|
267
|
-
invariant(
|
|
268
|
-
router,
|
|
269
|
-
'No router was found for the TanStack Router Devtools. Please place the devtools in the <RouterProvider> component tree or pass the router instance to the devtools manually.',
|
|
270
|
-
)
|
|
271
|
-
|
|
272
266
|
// useStore(router.stores.__store)
|
|
273
267
|
|
|
274
268
|
const [currentTab, setCurrentTab] = useLocalStorage<
|
|
@@ -300,30 +294,30 @@ export const BaseTanStackRouterDevtoolsPanel =
|
|
|
300
294
|
type Subscribe = (fn: () => void) => { unsubscribe: () => void }
|
|
301
295
|
createEffect(() => {
|
|
302
296
|
const pendingMatchesStore = router().stores.pendingMatchesSnapshot
|
|
303
|
-
setPendingMatches(pendingMatchesStore.
|
|
297
|
+
setPendingMatches(pendingMatchesStore.get())
|
|
304
298
|
const subscription = (
|
|
305
299
|
(pendingMatchesStore as any).subscribe as Subscribe
|
|
306
300
|
)(() => {
|
|
307
|
-
setPendingMatches(pendingMatchesStore.
|
|
301
|
+
setPendingMatches(pendingMatchesStore.get())
|
|
308
302
|
})
|
|
309
303
|
onCleanup(() => subscription.unsubscribe())
|
|
310
304
|
})
|
|
311
305
|
|
|
312
306
|
createEffect(() => {
|
|
313
307
|
const cachedMatchesStore = router().stores.cachedMatchesSnapshot
|
|
314
|
-
setCachedMatches(cachedMatchesStore.
|
|
308
|
+
setCachedMatches(cachedMatchesStore.get())
|
|
315
309
|
const subscription = (
|
|
316
310
|
(cachedMatchesStore as any).subscribe as Subscribe
|
|
317
311
|
)(() => {
|
|
318
|
-
setCachedMatches(cachedMatchesStore.
|
|
312
|
+
setCachedMatches(cachedMatchesStore.get())
|
|
319
313
|
})
|
|
320
314
|
onCleanup(() => subscription.unsubscribe())
|
|
321
315
|
})
|
|
322
316
|
}
|
|
323
317
|
// signal implementation
|
|
324
318
|
else {
|
|
325
|
-
pendingMatches = () => router().stores.pendingMatchesSnapshot.
|
|
326
|
-
cachedMatches = () => router().stores.cachedMatchesSnapshot.
|
|
319
|
+
pendingMatches = () => router().stores.pendingMatchesSnapshot.get()
|
|
320
|
+
cachedMatches = () => router().stores.cachedMatchesSnapshot.get()
|
|
327
321
|
}
|
|
328
322
|
|
|
329
323
|
createEffect(() => {
|
package/src/Explorer.tsx
CHANGED
|
@@ -3,7 +3,12 @@ import { clsx as cx } from 'clsx'
|
|
|
3
3
|
import * as goober from 'goober'
|
|
4
4
|
import { createMemo, createSignal, useContext } from 'solid-js'
|
|
5
5
|
import { tokens } from './tokens'
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
displayValue,
|
|
8
|
+
getServerComponentSlotUsageSummary,
|
|
9
|
+
getServerComponentSlots,
|
|
10
|
+
getServerComponentType,
|
|
11
|
+
} from './utils'
|
|
7
12
|
import { ShadowDomTargetContext } from './context'
|
|
8
13
|
import type { Accessor, JSX } from 'solid-js'
|
|
9
14
|
|
|
@@ -12,7 +17,7 @@ type ExpanderProps = {
|
|
|
12
17
|
style?: JSX.CSSProperties
|
|
13
18
|
}
|
|
14
19
|
|
|
15
|
-
export const Expander = ({ expanded, style = {} }: ExpanderProps) => {
|
|
20
|
+
export const Expander = ({ expanded, style: _style = {} }: ExpanderProps) => {
|
|
16
21
|
const styles = useStyles()
|
|
17
22
|
return (
|
|
18
23
|
<span class={styles().expander}>
|
|
@@ -90,6 +95,12 @@ function isIterable(x: any): x is Iterable<unknown> {
|
|
|
90
95
|
return Symbol.iterator in x
|
|
91
96
|
}
|
|
92
97
|
|
|
98
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
99
|
+
if (!value || typeof value !== 'object') return false
|
|
100
|
+
const proto = Object.getPrototypeOf(value)
|
|
101
|
+
return proto === Object.prototype || proto === null
|
|
102
|
+
}
|
|
103
|
+
|
|
93
104
|
export function Explorer({
|
|
94
105
|
value,
|
|
95
106
|
defaultExpanded,
|
|
@@ -116,7 +127,22 @@ export function Explorer({
|
|
|
116
127
|
}
|
|
117
128
|
}
|
|
118
129
|
|
|
119
|
-
if (
|
|
130
|
+
if (
|
|
131
|
+
Array.isArray(value()) &&
|
|
132
|
+
(value() as Array<any>).length === 2 &&
|
|
133
|
+
(value() as Array<any>)[0] === 'React element' &&
|
|
134
|
+
isPlainObject((value() as Array<any>)[1])
|
|
135
|
+
) {
|
|
136
|
+
// Special case: treat `["React element", { ...meta }]` as sibling entries
|
|
137
|
+
// to avoid the meta object being rendered as a deeper nested tree.
|
|
138
|
+
const v = value() as ['React element', Record<string, unknown>]
|
|
139
|
+
entries = [
|
|
140
|
+
makeProperty({ label: '0', value: v[0] }),
|
|
141
|
+
...Object.entries(v[1]).map(([key, val]) =>
|
|
142
|
+
makeProperty({ label: key, value: val }),
|
|
143
|
+
),
|
|
144
|
+
]
|
|
145
|
+
} else if (Array.isArray(value())) {
|
|
120
146
|
// any[]
|
|
121
147
|
entries = (value() as Array<any>).map((d, i) =>
|
|
122
148
|
makeProperty({
|
|
@@ -169,9 +195,72 @@ export function Explorer({
|
|
|
169
195
|
/>
|
|
170
196
|
)
|
|
171
197
|
|
|
198
|
+
const serverComponentType = createMemo(() => getServerComponentType(value()))
|
|
199
|
+
const serverComponentSlots = createMemo(() =>
|
|
200
|
+
getServerComponentSlots(value()),
|
|
201
|
+
)
|
|
202
|
+
const serverComponentSlotUsageSummary = createMemo(() =>
|
|
203
|
+
getServerComponentSlotUsageSummary(value()),
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
const isCompositeWithSlots = createMemo(
|
|
207
|
+
() =>
|
|
208
|
+
serverComponentType() === 'compositeSource' &&
|
|
209
|
+
serverComponentSlots().length > 0,
|
|
210
|
+
)
|
|
211
|
+
|
|
172
212
|
return (
|
|
173
213
|
<div class={styles().entry}>
|
|
174
|
-
{
|
|
214
|
+
{serverComponentType() !== null ? (
|
|
215
|
+
isCompositeWithSlots() ? (
|
|
216
|
+
<>
|
|
217
|
+
<button
|
|
218
|
+
class={styles().expandButton}
|
|
219
|
+
onClick={() => toggleExpanded()}
|
|
220
|
+
>
|
|
221
|
+
<Expander expanded={expanded() ?? false} />
|
|
222
|
+
<span>{rest.label}:</span>
|
|
223
|
+
<span class={styles().compositeComponent}>
|
|
224
|
+
{displayValue(value())}
|
|
225
|
+
</span>
|
|
226
|
+
</button>
|
|
227
|
+
{(expanded() ?? false) ? (
|
|
228
|
+
<div class={styles().rscMetaRow}>
|
|
229
|
+
<span class={styles().rscMetaLabel}>slots</span>
|
|
230
|
+
<div class={styles().subEntries}>
|
|
231
|
+
{serverComponentSlots().map((name) => {
|
|
232
|
+
const usage = serverComponentSlotUsageSummary()[name]
|
|
233
|
+
if (!usage) return null
|
|
234
|
+
return (
|
|
235
|
+
<Explorer
|
|
236
|
+
label={`${name}:`}
|
|
237
|
+
value={() =>
|
|
238
|
+
usage.invocations.map((args) =>
|
|
239
|
+
args.length === 1 ? args[0] : args,
|
|
240
|
+
)
|
|
241
|
+
}
|
|
242
|
+
/>
|
|
243
|
+
)
|
|
244
|
+
})}
|
|
245
|
+
</div>
|
|
246
|
+
</div>
|
|
247
|
+
) : null}
|
|
248
|
+
</>
|
|
249
|
+
) : (
|
|
250
|
+
<>
|
|
251
|
+
<span>{rest.label}:</span>{' '}
|
|
252
|
+
<span
|
|
253
|
+
class={
|
|
254
|
+
serverComponentType() === 'compositeSource'
|
|
255
|
+
? styles().compositeComponent
|
|
256
|
+
: styles().renderableComponent
|
|
257
|
+
}
|
|
258
|
+
>
|
|
259
|
+
{displayValue(value())}
|
|
260
|
+
</span>
|
|
261
|
+
</>
|
|
262
|
+
)
|
|
263
|
+
) : subEntryPages().length ? (
|
|
175
264
|
<>
|
|
176
265
|
<button
|
|
177
266
|
class={styles().expandButton}
|
|
@@ -250,7 +339,7 @@ export function Explorer({
|
|
|
250
339
|
}
|
|
251
340
|
|
|
252
341
|
const stylesFactory = (shadowDOMTarget?: ShadowRoot) => {
|
|
253
|
-
const { colors, font, size,
|
|
342
|
+
const { colors, font, size, border } = tokens
|
|
254
343
|
const { fontFamily, lineHeight, size: fontSize } = font
|
|
255
344
|
const css = shadowDOMTarget
|
|
256
345
|
? goober.css.bind({ target: shadowDOMTarget })
|
|
@@ -309,6 +398,67 @@ const stylesFactory = (shadowDOMTarget?: ShadowRoot) => {
|
|
|
309
398
|
value: css`
|
|
310
399
|
color: ${colors.purple[400]};
|
|
311
400
|
`,
|
|
401
|
+
compositeComponent: css`
|
|
402
|
+
display: inline-flex;
|
|
403
|
+
align-items: center;
|
|
404
|
+
padding: 1px ${size[1]};
|
|
405
|
+
border-radius: ${border.radius.full};
|
|
406
|
+
border: 1px solid ${colors.darkGray[500]};
|
|
407
|
+
background: ${colors.darkGray[700]};
|
|
408
|
+
color: ${colors.cyan[300]};
|
|
409
|
+
font-style: normal;
|
|
410
|
+
font-weight: ${font.weight.medium};
|
|
411
|
+
`,
|
|
412
|
+
renderableComponent: css`
|
|
413
|
+
display: inline-flex;
|
|
414
|
+
align-items: center;
|
|
415
|
+
padding: 1px ${size[1]};
|
|
416
|
+
border-radius: ${border.radius.full};
|
|
417
|
+
border: 1px solid ${colors.darkGray[500]};
|
|
418
|
+
background: ${colors.darkGray[700]};
|
|
419
|
+
color: ${colors.teal[300]};
|
|
420
|
+
font-style: normal;
|
|
421
|
+
font-weight: ${font.weight.medium};
|
|
422
|
+
`,
|
|
423
|
+
rscMetaRow: css`
|
|
424
|
+
display: flex;
|
|
425
|
+
gap: ${size[1]};
|
|
426
|
+
align-items: flex-start;
|
|
427
|
+
margin-left: calc(${size[3]} + ${size[1]});
|
|
428
|
+
margin-top: ${size[0.5]};
|
|
429
|
+
flex-wrap: wrap;
|
|
430
|
+
`,
|
|
431
|
+
rscMetaLabel: css`
|
|
432
|
+
color: ${colors.gray[500]};
|
|
433
|
+
font-size: ${fontSize['2xs']};
|
|
434
|
+
text-transform: uppercase;
|
|
435
|
+
letter-spacing: 0.06em;
|
|
436
|
+
padding-top: 2px;
|
|
437
|
+
`,
|
|
438
|
+
rscChipRow: css`
|
|
439
|
+
display: flex;
|
|
440
|
+
gap: ${size[1]};
|
|
441
|
+
flex-wrap: wrap;
|
|
442
|
+
`,
|
|
443
|
+
rscChip: css`
|
|
444
|
+
display: inline-flex;
|
|
445
|
+
align-items: center;
|
|
446
|
+
gap: ${size[0.5]};
|
|
447
|
+
padding: 1px ${size[1]};
|
|
448
|
+
border-radius: ${border.radius.full};
|
|
449
|
+
border: 1px solid ${colors.darkGray[500]};
|
|
450
|
+
background: ${colors.darkGray[800]};
|
|
451
|
+
color: ${colors.gray[200]};
|
|
452
|
+
font-size: ${fontSize['2xs']};
|
|
453
|
+
line-height: ${lineHeight.xs};
|
|
454
|
+
`,
|
|
455
|
+
rscChipName: css`
|
|
456
|
+
color: ${colors.gray[100]};
|
|
457
|
+
`,
|
|
458
|
+
rscChipMeta: css`
|
|
459
|
+
color: ${colors.gray[400]};
|
|
460
|
+
font-size: ${fontSize['2xs']};
|
|
461
|
+
`,
|
|
312
462
|
subEntries: css`
|
|
313
463
|
margin-left: ${size[2]};
|
|
314
464
|
padding-left: ${size[2]};
|
package/src/utils.tsx
CHANGED
|
@@ -104,16 +104,144 @@ export function useIsMounted() {
|
|
|
104
104
|
return isMounted
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
// Symbols for RSC detection
|
|
108
|
+
const SERVER_COMPONENT_STREAM = Symbol.for('tanstack.rsc.stream')
|
|
109
|
+
const RENDERABLE_RSC = Symbol.for('tanstack.rsc.renderable')
|
|
110
|
+
const RSC_SLOT_USAGES = Symbol.for('tanstack.rsc.slotUsages')
|
|
111
|
+
|
|
112
|
+
export type RscSlotUsageEvent = {
|
|
113
|
+
slot: string
|
|
114
|
+
args?: Array<any>
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function trimTrailingUndefined<T>(arr: Array<T>): Array<T> {
|
|
118
|
+
let end = arr.length
|
|
119
|
+
while (end > 0 && arr[end - 1] === undefined) end--
|
|
120
|
+
if (end === 0) return arr
|
|
121
|
+
return end === arr.length ? arr : arr.slice(0, end)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export type ServerComponentType =
|
|
125
|
+
| 'compositeSource' // createCompositeComponent result (render via <CompositeComponent src={...} />)
|
|
126
|
+
| 'renderableValue' // renderServerComponent result (inline renderable value)
|
|
127
|
+
| null // not a server component
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Checks if a value is any kind of server component
|
|
131
|
+
*/
|
|
132
|
+
export const isServerComponent = (value: unknown): boolean => {
|
|
133
|
+
return (
|
|
134
|
+
(typeof value === 'object' || typeof value === 'function') &&
|
|
135
|
+
value !== null &&
|
|
136
|
+
SERVER_COMPONENT_STREAM in value
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Gets the type of server component.
|
|
142
|
+
* - RENDERABLE_RSC === true → renderable (from renderServerComponent)
|
|
143
|
+
* - RENDERABLE_RSC === false or not present → composite (from createCompositeComponent)
|
|
144
|
+
*/
|
|
145
|
+
export const getServerComponentType = (value: unknown): ServerComponentType => {
|
|
146
|
+
if (!isServerComponent(value)) {
|
|
147
|
+
return null
|
|
148
|
+
}
|
|
149
|
+
const v = value as Record<symbol, unknown>
|
|
150
|
+
if (RENDERABLE_RSC in v && v[RENDERABLE_RSC] === true) {
|
|
151
|
+
return 'renderableValue'
|
|
152
|
+
}
|
|
153
|
+
// RENDERABLE_RSC is false or not present → composite
|
|
154
|
+
return 'compositeSource'
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Gets the slot names from a composite server component (dev only)
|
|
159
|
+
*/
|
|
160
|
+
export const getServerComponentSlots = (value: unknown): Array<string> => {
|
|
161
|
+
if (!isServerComponent(value)) {
|
|
162
|
+
return []
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const v = value as Record<symbol, unknown>
|
|
166
|
+
const out: Array<string> = []
|
|
167
|
+
// Include any slot names observed via dev-only slot usage events
|
|
168
|
+
if (RSC_SLOT_USAGES in v) {
|
|
169
|
+
const usages = v[RSC_SLOT_USAGES]
|
|
170
|
+
if (Array.isArray(usages)) {
|
|
171
|
+
for (const evt of usages) {
|
|
172
|
+
const name = evt?.slot
|
|
173
|
+
if (typeof name === 'string' && !out.includes(name)) {
|
|
174
|
+
out.push(name)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return out
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export const getServerComponentSlotUsages = (
|
|
184
|
+
value: unknown,
|
|
185
|
+
): Array<RscSlotUsageEvent> => {
|
|
186
|
+
if (!isServerComponent(value)) {
|
|
187
|
+
return []
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const v = value as Record<symbol, unknown>
|
|
191
|
+
if (!(RSC_SLOT_USAGES in v)) return []
|
|
192
|
+
const usages = v[RSC_SLOT_USAGES]
|
|
193
|
+
if (!Array.isArray(usages)) return []
|
|
194
|
+
|
|
195
|
+
return usages.filter((d): d is RscSlotUsageEvent => {
|
|
196
|
+
return (
|
|
197
|
+
d &&
|
|
198
|
+
typeof d === 'object' &&
|
|
199
|
+
typeof d.slot === 'string' &&
|
|
200
|
+
(d.args === undefined || Array.isArray(d.args))
|
|
201
|
+
)
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export const getServerComponentSlotUsageSummary = (
|
|
206
|
+
value: unknown,
|
|
207
|
+
): Record<string, { count: number; invocations: Array<Array<any>> }> => {
|
|
208
|
+
const usages = getServerComponentSlotUsages(value)
|
|
209
|
+
const out: Record<string, { count: number; invocations: Array<Array<any>> }> =
|
|
210
|
+
{}
|
|
211
|
+
for (const evt of usages) {
|
|
212
|
+
const args = trimTrailingUndefined(evt.args ?? [])
|
|
213
|
+
const prev =
|
|
214
|
+
out[evt.slot] ?? (out[evt.slot] = { count: 0, invocations: [] })
|
|
215
|
+
prev.count++
|
|
216
|
+
prev.invocations.push(args)
|
|
217
|
+
}
|
|
218
|
+
return out
|
|
219
|
+
}
|
|
220
|
+
|
|
107
221
|
/**
|
|
108
222
|
* Displays a string regardless the type of the data
|
|
109
223
|
* @param {unknown} value Value to be stringified
|
|
110
224
|
*/
|
|
111
225
|
export const displayValue = (value: unknown) => {
|
|
226
|
+
if (value === 'React element') return 'React element'
|
|
227
|
+
const componentType = getServerComponentType(value)
|
|
228
|
+
if (componentType === 'compositeSource') {
|
|
229
|
+
const slots = getServerComponentSlots(value)
|
|
230
|
+
if (slots.length > 0) {
|
|
231
|
+
return `RSC composite source (${slots.length} ${
|
|
232
|
+
slots.length === 1 ? 'slot' : 'slots'
|
|
233
|
+
})`
|
|
234
|
+
}
|
|
235
|
+
return 'RSC composite source'
|
|
236
|
+
}
|
|
237
|
+
if (componentType === 'renderableValue') {
|
|
238
|
+
return 'RSC renderable value'
|
|
239
|
+
}
|
|
112
240
|
const name = Object.getOwnPropertyNames(Object(value))
|
|
113
241
|
const newValue = typeof value === 'bigint' ? `${value.toString()}n` : value
|
|
114
242
|
try {
|
|
115
243
|
return JSON.stringify(newValue, name)
|
|
116
|
-
} catch
|
|
244
|
+
} catch {
|
|
117
245
|
return `unable to stringify`
|
|
118
246
|
}
|
|
119
247
|
}
|