Skip to content

Commit 4e32e04

Browse files
committed
feat: Display custom values added with registerGraph() in the inspector
1 parent ac09ddd commit 4e32e04

File tree

9 files changed

+257
-221
lines changed

9 files changed

+257
-221
lines changed

.changeset/moody-ants-yawn.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@solid-devtools/debugger": minor
3+
"@solid-devtools/frontend": minor
4+
---
5+
6+
Display custom values added with `registerGraph()` in the inspector

examples/sandbox/src/App.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ const createComponent = (content: () => s.JSX.Element) => {
120120
}
121121

122122
const App: s.Component = () => {
123+
124+
s.DEV?.registerGraph({value: {foo: 123}, name: 'my_custom_value'})
125+
123126
const [count, setCount] = s.createSignal(0)
124127
const [showEven, setShowEven] = s.createSignal(false)
125128
const fnSig = s.createSignal({fn: () => {}}, {equals: (a, b) => a.fn === b.fn})

packages/debugger/src/inspector/inspector.ts

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -164,38 +164,35 @@ let PropsMap: ObservedPropsMap
164164

165165
const $INSPECTOR = Symbol('inspector')
166166

167-
const typeToObjectTypeMap = {
168-
[NodeType.Signal]: ObjectType.Signal,
169-
[NodeType.Memo]: ObjectType.Owner,
170-
[NodeType.Store]: ObjectType.Store,
171-
}
172-
173167
function mapSourceValue(
174-
node: Solid.SourceMapValue | Solid.Memo | Solid.Store,
168+
node: Solid.SourceMapValue | Solid.Computation,
175169
handler: (nodeId: NodeID, value: unknown) => void,
176-
isMemo: boolean,
177-
): Mapped.Signal | null {
178-
const type = isMemo
179-
? NodeType.Memo
180-
: utils.isSolidStore(node)
181-
? NodeType.Store
182-
: utils.isSolidSignal(node)
183-
? NodeType.Signal
184-
: null
185-
186-
if (!type) return null
187-
188-
const {value} = node,
189-
id = getSdtId(node, typeToObjectTypeMap[type])
170+
): Mapped.SourceValue | null {
171+
172+
let type = utils.getNodeType(node)
173+
let {value} = node
174+
let id: NodeID
175+
176+
// eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
177+
switch (type) {
178+
case NodeType.Memo: id = getSdtId(node as Solid.Memo, ObjectType.Owner) ;break
179+
case NodeType.Signal: id = getSdtId(node as Solid.Signal, ObjectType.Signal) ;break
180+
case NodeType.Store: id = getSdtId(node as Solid.Store, ObjectType.Store) ;break
181+
case NodeType.CustomValue: id = getSdtId(node as Solid.SourceMapValue, ObjectType.CustomValue) ;break
182+
default:
183+
return null
184+
}
190185

191186
ValueMap.add(`${ValueItemType.Signal}:${id}`, () => node.value)
192187

193-
if (type !== NodeType.Store) observeValueUpdate(node, v => handler(id, v), $INSPECTOR)
188+
if (type === NodeType.Memo || type === NodeType.Signal) {
189+
observeValueUpdate(node, v => handler(id, v), $INSPECTOR)
190+
}
194191

195192
return {
196-
type,
197-
name: utils.getNodeName(node),
198-
id,
193+
type: type,
194+
name: utils.getNodeName(node),
195+
id: id,
199196
value: encodeValue(value, false),
200197
}
201198
}
@@ -299,7 +296,7 @@ export const collectOwnerDetails = /*#__PURE__*/ untrackedCallback(function (
299296
// marge component with refresh memo
300297
const refresh = utils.getComponentRefreshNode(owner)
301298
if (refresh) {
302-
299+
303300
sourceMap = refresh.sourceMap
304301
owned = refresh.owned
305302
getValue = () => refresh.value
@@ -334,16 +331,15 @@ export const collectOwnerDetails = /*#__PURE__*/ untrackedCallback(function (
334331
// map signals
335332
if (sourceMap) {
336333
for (const signal of sourceMap) {
337-
const mapped = mapSourceValue(signal, onSignalUpdate, false)
334+
const mapped = mapSourceValue(signal, onSignalUpdate)
338335
mapped && details.signals.push(mapped)
339336
}
340337
}
341338

342339
// map memos
343340
if (owned) {
344341
for (const node of owned) {
345-
if (!utils.isSolidMemo(node)) continue
346-
const mapped = mapSourceValue(node, onSignalUpdate, true)
342+
const mapped = mapSourceValue(node, onSignalUpdate)
347343
mapped && details.signals.push(mapped)
348344
}
349345
}

packages/debugger/src/inspector/test/index.test.tsx

Lines changed: 47 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
import '../../setup.ts'
22

3-
import {
4-
createComputed,
5-
createMemo,
6-
createRenderEffect,
7-
createRoot,
8-
createSignal,
9-
JSX,
10-
} from 'solid-js'
3+
import * as s from 'solid-js'
114
import {beforeEach, describe, expect, it, vi} from 'vitest'
125
import {getObjectById, getSdtId, ObjectType} from '../../main/id.ts'
136
import setup from '../../main/setup.ts'
@@ -22,33 +15,36 @@ vi.mock('../../main/get-id', () => ({getNewSdtId: () => '#' + mockLAST_ID++}))
2215

2316
describe('collectOwnerDetails', () => {
2417
it('collects focused owner details', () => {
25-
createRoot(dispose => {
26-
const [s] = createSignal(0, {name: 'source'})
18+
s.createRoot(dispose => {
19+
const [source] = s.createSignal(0, {name: 'source'})
2720

2821
let memo!: Solid.Owner
2922
const div = document.createElement('div')
3023

31-
createComputed(
32-
() => {
33-
const focused = createMemo(
34-
() => {
35-
memo = setup.solid.getOwner()!
36-
s()
37-
createSignal(div, {name: 'element'})
38-
const m = createMemo(() => 0, undefined, {name: 'memo'})
39-
createRenderEffect(m, undefined, {name: 'render'})
40-
return 'value'
41-
},
42-
undefined,
43-
{name: 'focused'},
44-
)
45-
focused()
46-
},
47-
undefined,
48-
{name: 'WRAPPER'},
49-
)
24+
s.createComputed(() => {
25+
26+
const focused = s.createMemo(() => {
27+
28+
memo = setup.solid.getOwner()!
29+
30+
source()
31+
32+
s.DEV!.registerGraph({
33+
value: {foo: 123},
34+
name: 'custom value',
35+
})
36+
s.createSignal(div, {name: 'element'})
37+
const m = s.createMemo(() => 0, undefined, {name: 'memo'})
38+
s.createRenderEffect(m, undefined, {name: 'render'})
39+
40+
return 'value'
41+
}, undefined, {name: 'focused'})
5042

51-
const [signalB] = memo.sourceMap as [Solid.Signal]
43+
focused()
44+
45+
}, undefined, {name: 'WRAPPER'})
46+
47+
const [customValue, signalB] = memo.sourceMap as [Solid.SourceMapValue, Solid.Signal]
5248
const [innerMemo] = memo.owned as [Solid.Memo, Solid.Computation]
5349

5450
const {details, valueMap} = collectOwnerDetails(memo, {
@@ -67,11 +63,17 @@ describe('collectOwnerDetails', () => {
6763
type: NodeType.Memo,
6864
value: [[ValueType.String, 'value']],
6965
signals: [
66+
{
67+
type: NodeType.CustomValue,
68+
id: getSdtId(customValue, ObjectType.CustomValue),
69+
name: 'custom value',
70+
value: [[ValueType.Object, 1]],
71+
},
7072
{
7173
type: NodeType.Signal,
7274
id: getSdtId(signalB, ObjectType.Signal),
7375
name: 'element',
74-
value: [[ValueType.Element, '#3:div']],
76+
value: [[ValueType.Element, '#4:div']],
7577
},
7678
{
7779
type: NodeType.Memo,
@@ -82,27 +84,28 @@ describe('collectOwnerDetails', () => {
8284
],
8385
} satisfies Mapped.OwnerDetails)
8486

87+
expect(valueMap.get(`signal:${getSdtId(customValue, ObjectType.CustomValue)}`)).toBeTruthy()
8588
expect(valueMap.get(`signal:${getSdtId(signalB, ObjectType.Signal)}`)).toBeTruthy()
8689
expect(valueMap.get(`signal:${getSdtId(innerMemo, ObjectType.Owner)}`)).toBeTruthy()
8790

88-
expect(getObjectById('#3', ObjectType.Element)).toBe(div)
91+
expect(getObjectById('#4', ObjectType.Element)).toBe(div)
8992

9093
dispose()
9194
})
9295
})
9396

9497
it('component props', () => {
95-
createRoot(dispose => {
98+
s.createRoot(dispose => {
9699
let owner!: Solid.Owner
97100
const TestComponent = (props: {
98101
count: number
99-
children: JSX.Element
102+
children: s.JSX.Element
100103
nested: {foo: number; bar: string}
101104
}) => {
102105
owner = setup.solid.getOwner()!
103106
return <div>{props.children}</div>
104107
}
105-
createRenderEffect(() => (
108+
s.createRenderEffect(() => (
106109
<TestComponent count={123} nested={{foo: 1, bar: '2'}}>
107110
<button>Click me</button>
108111
</TestComponent>
@@ -150,13 +153,13 @@ describe('collectOwnerDetails', () => {
150153
})
151154

152155
it('dynamic component props', () => {
153-
createRoot(dispose => {
156+
s.createRoot(dispose => {
154157
let owner!: Solid.Owner
155-
const Button = (props: JSX.ButtonHTMLAttributes<HTMLButtonElement>) => {
158+
const Button = (props: s.JSX.ButtonHTMLAttributes<HTMLButtonElement>) => {
156159
owner = setup.solid.getOwner()!
157160
return <button {...props}>Click me</button>
158161
}
159-
createRenderEffect(() => {
162+
s.createRenderEffect(() => {
160163
const props = () =>
161164
({
162165
onClick: () => {
@@ -205,11 +208,11 @@ describe('collectOwnerDetails', () => {
205208
})
206209

207210
it('listens to value updates', () => {
208-
createRoot(dispose => {
211+
s.createRoot(dispose => {
209212
let owner!: Solid.Owner
210213

211-
const [count, setCount] = createSignal(0)
212-
createMemo(() => {
214+
const [count, setCount] = s.createSignal(0)
215+
s.createMemo(() => {
213216
owner = setup.solid.getOwner()!
214217
return count()
215218
})
@@ -241,10 +244,10 @@ describe('collectOwnerDetails', () => {
241244
})
242245

243246
it('listens to signal updates', () => {
244-
createRoot(dispose => {
247+
s.createRoot(dispose => {
245248
const owner = setup.solid.getOwner()!
246-
const [, setCount] = createSignal(0) // id: "0"
247-
const [, setCount2] = createSignal(0) // id: "1"
249+
const [, setCount] = s.createSignal(0) // id: "0"
250+
const [, setCount2] = s.createSignal(0) // id: "1"
248251

249252
const onValueUpdate = vi.fn()
250253
collectOwnerDetails(owner, {

packages/debugger/src/main/constants.ts

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,39 +20,41 @@ export enum TreeWalkerMode {
2020
export const DEFAULT_WALKER_MODE = TreeWalkerMode.Components
2121

2222
export enum NodeType {
23-
Root = 'root',
24-
Component = 'component',
25-
Element = 'element',
26-
Effect = 'effect',
27-
Render = 'render',
28-
Memo = 'memo',
29-
Computation = 'computation',
30-
Refresh = 'refresh',
31-
Context = 'context',
32-
CatchError = 'catchError',
33-
Signal = 'signal',
34-
Store = 'store',
23+
Root = 'ROOT',
24+
Component = 'COMPONENT',
25+
Element = 'ELEMENT',
26+
Effect = 'EFFECT',
27+
Render = 'RENDER',
28+
Memo = 'MEMO',
29+
Computation = 'COMPUTATION',
30+
Refresh = 'REFRESH',
31+
Context = 'CONTEXT',
32+
CatchError = 'CATCH_ERROR',
33+
Signal = 'SIGNAL',
34+
Store = 'STORE',
35+
CustomValue = 'CUSTOM_VALUE',
3536
}
3637

3738
export const NODE_TYPE_NAMES: Readonly<Record<NodeType, string>> = {
38-
[NodeType.Root]: 'Root',
39-
[NodeType.Component]: 'Component',
40-
[NodeType.Element]: 'Element',
41-
[NodeType.Effect]: 'Effect',
42-
[NodeType.Render]: 'Render Effect',
43-
[NodeType.Memo]: 'Memo',
39+
[NodeType.Root]: 'Root',
40+
[NodeType.Component]: 'Component',
41+
[NodeType.Element]: 'Element',
42+
[NodeType.Effect]: 'Effect',
43+
[NodeType.Render]: 'Render Effect',
44+
[NodeType.Memo]: 'Memo',
4445
[NodeType.Computation]: 'Computation',
45-
[NodeType.Refresh]: 'Refresh',
46-
[NodeType.Context]: 'Context',
47-
[NodeType.CatchError]: 'CatchError',
48-
[NodeType.Signal]: 'Signal',
49-
[NodeType.Store]: 'Store',
46+
[NodeType.Refresh]: 'Refresh',
47+
[NodeType.Context]: 'Context',
48+
[NodeType.CatchError]: 'CatchError',
49+
[NodeType.Signal]: 'Signal',
50+
[NodeType.Store]: 'Store',
51+
[NodeType.CustomValue]: 'Custom Value',
5052
}
5153

5254
export enum ValueItemType {
5355
Signal = 'signal',
54-
Prop = 'prop',
55-
Value = 'value',
56+
Prop = 'prop',
57+
Value = 'value',
5658
}
5759

5860
export const UNKNOWN = 'unknown'

packages/debugger/src/main/id.ts

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,34 @@ import {getNewSdtId} from './get-id.ts'
22
import {type NodeID, type Solid} from './types.ts'
33

44
export const enum ObjectType {
5-
Owner = 'owner',
6-
Element = 'element',
7-
Signal = 'signal',
8-
Store = 'store',
9-
StoreNode = 'store-node',
5+
Owner = 'OWNER',
6+
Element = 'ELEMENT',
7+
Signal = 'SIGNAL',
8+
Store = 'STORE',
9+
StoreNode = 'STORE_NODE',
10+
CustomValue = 'CUSTOM_VALUE',
1011
}
1112

1213
type ValueMap = {
13-
[ObjectType.Owner]: Solid.Owner
14-
[ObjectType.Element]: Element
15-
[ObjectType.Signal]: Solid.Signal
16-
[ObjectType.Store]: Solid.Store
17-
[ObjectType.StoreNode]: Solid.StoreNode
14+
[ObjectType.Owner]: Solid.Owner
15+
[ObjectType.Element]: Element
16+
[ObjectType.Signal]: Solid.Signal
17+
[ObjectType.Store]: Solid.Store
18+
[ObjectType.StoreNode]: Solid.StoreNode
19+
[ObjectType.CustomValue]: Solid.SourceMapValue
1820
}
1921

2022
const WeakIdMap = new WeakMap<ValueMap[ObjectType], NodeID>()
2123

2224
const RefMapMap: {
2325
readonly [T in ObjectType]: Map<NodeID, WeakRef<ValueMap[T]>>
2426
} = {
25-
[ObjectType.Owner]: new Map(),
26-
[ObjectType.Element]: new Map(),
27-
[ObjectType.Signal]: new Map(),
28-
[ObjectType.Store]: new Map(),
29-
[ObjectType.StoreNode]: new Map(),
27+
[ObjectType.Owner]: new Map(),
28+
[ObjectType.Element]: new Map(),
29+
[ObjectType.Signal]: new Map(),
30+
[ObjectType.Store]: new Map(),
31+
[ObjectType.StoreNode]: new Map(),
32+
[ObjectType.CustomValue]: new Map(),
3033
}
3134

3235
const CleanupRegistry = new FinalizationRegistry((data: {map: ObjectType; id: NodeID}) => {

0 commit comments

Comments
 (0)