Skip to content

Commit 5cece46

Browse files
authored
Merge pull request #131 from thetarnav/context
Context support
2 parents aa7fde4 + fb8b3c4 commit 5cece46

File tree

28 files changed

+529
-284
lines changed

28 files changed

+529
-284
lines changed

.changeset/sour-geese-tell.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@solid-devtools/debugger": minor
3+
"solid-devtools": patch
4+
"solid-devtools-extension": patch
5+
"@solid-devtools/shared": patch
6+
---
7+
8+
Improve displaying the context node on the structure graph and it's value on the inspector.

configs/tsconfig.base.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
"resolveJsonModule": true,
99
"esModuleInterop": true,
1010
"noEmit": true,
11-
"isolatedModules": true,
1211
"skipLibCheck": true
1312
}
1413
}

packages/debugger/src/inspect.ts

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { Mapped, NodeID, Solid, NodeType } from "@solid-devtools/shared/graph"
2-
import { ElementMap, EncodedValue, encodeValue, ValueType } from "@solid-devtools/shared/serialize"
1+
import { Mapped, NodeID, Solid, NodeType, ValueUpdateListener } from "@solid-devtools/shared/graph"
2+
import { ElementMap, encodeValue, ValueType } from "@solid-devtools/shared/serialize"
33
import { $PROXY } from "solid-js"
44
import { observeValueUpdate, removeValueUpdateObserver } from "./update"
55
import {
@@ -16,7 +16,6 @@ import {
1616
} from "./utils"
1717

1818
export type SignalUpdateHandler = (nodeId: NodeID, value: unknown) => void
19-
export type ValueUpdateHandler = (value: unknown) => void
2019

2120
// Globals set before collecting the owner details
2221
let $elementMap!: ElementMap
@@ -74,42 +73,48 @@ export function encodeComponentProps(
7473
return { proxy, record }
7574
}
7675

77-
export function encodeOwnerValue(
78-
owner: Solid.Owner,
79-
deep: boolean,
80-
elementMap: ElementMap,
81-
): EncodedValue<boolean> {
82-
let refresh: Solid.Memo | null
83-
if (isSolidComponent(owner) && (refresh = getComponentRefreshNode(owner))) {
84-
owner = refresh
85-
}
86-
return encodeValue(owner.value, deep, elementMap)
87-
}
88-
8976
export function collectOwnerDetails(
9077
owner: Solid.Owner,
9178
config: {
9279
onSignalUpdate: SignalUpdateHandler
93-
onValueUpdate: ValueUpdateHandler
80+
onValueUpdate: ValueUpdateListener
9481
},
9582
): {
9683
details: Mapped.OwnerDetails
9784
signalMap: Record<NodeID, Solid.Signal>
9885
elementMap: ElementMap
86+
getOwnerValue: () => unknown
9987
} {
10088
const { onSignalUpdate, onValueUpdate } = config
10189

10290
// Set globals
10391
$elementMap = new ElementMap()
10492
$signalMap = {}
10593

106-
let { sourceMap, owned, value } = owner
94+
const type = markOwnerType(owner)
95+
let { sourceMap, owned } = owner
96+
let getValue = () => owner.value
97+
98+
// handle context node specially
99+
if (type === NodeType.Context) {
100+
sourceMap = undefined
101+
owned = null
102+
const symbols = Object.getOwnPropertySymbols(owner.context)
103+
if (symbols.length !== 1) {
104+
console.warn("Context field has more than one symbol. This is not expected.")
105+
getValue = () => undefined
106+
} else {
107+
const contextValue = owner.context[symbols[0]]
108+
getValue = () => contextValue
109+
}
110+
}
111+
107112
// marge component with refresh memo
108113
let refresh: Solid.Memo | null
109114
if (isSolidComponent(owner) && (refresh = getComponentRefreshNode(owner))) {
110115
sourceMap = refresh.sourceMap
111116
owned = refresh.owned
112-
value = refresh.value
117+
getValue = () => refresh!.value
113118
}
114119

115120
// map signals
@@ -137,7 +142,7 @@ export function collectOwnerDetails(
137142
}
138143

139144
if (isSolidComputation(owner)) {
140-
details.value = encodeValue(value, false, $elementMap)
145+
details.value = encodeValue(getValue(), false, $elementMap)
141146
observeValueUpdate(owner, onValueUpdate, INSPECTOR)
142147
details.sources = markNodesID(owner.sources)
143148
if (isSolidMemo(owner)) {
@@ -148,5 +153,5 @@ export function collectOwnerDetails(
148153
if (props) details.props = props
149154
}
150155

151-
return { details, signalMap: $signalMap, elementMap: $elementMap }
156+
return { details, signalMap: $signalMap, elementMap: $elementMap, getOwnerValue: getValue }
152157
}

packages/debugger/src/plugin.ts

Lines changed: 89 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { Accessor, createEffect, createSignal, untrack } from "solid-js"
2-
import { createSimpleEmitter, Listen } from "@solid-primitives/event-bus"
2+
import {
3+
createEventHub,
4+
createSimpleEmitter,
5+
EventBus,
6+
EventHubOn,
7+
} from "@solid-primitives/event-bus"
38
import { throttle } from "@solid-primitives/scheduled"
49
import {
510
Mapped,
@@ -17,9 +22,7 @@ import {
1722
clearOwnerObservers,
1823
collectOwnerDetails,
1924
encodeComponentProps,
20-
encodeOwnerValue,
2125
SignalUpdateHandler,
22-
ValueUpdateHandler,
2326
} from "./inspect"
2427
import { makeSolidUpdateListener } from "./update"
2528

@@ -41,52 +44,30 @@ DETAILS:
4144

4245
export type BatchComputationUpdatesHandler = (payload: ComputationUpdate[]) => void
4346

47+
export type EventHubChannels = {
48+
ComputationUpdates: EventBus<ComputationUpdate[]>
49+
SignalUpdates: EventBus<{ id: NodeID; value: EncodedValue<boolean> }[]>
50+
PropsUpdate: EventBus<Mapped.Props>
51+
ValueUpdate: EventBus<{ value: EncodedValue<boolean>; update: boolean }>
52+
StructureUpdates: EventBus<RootsUpdates>
53+
}
54+
4455
export type PluginData = {
4556
readonly triggerUpdate: VoidFunction
4657
readonly forceTriggerUpdate: VoidFunction
47-
readonly handleComputationUpdates: (listener: BatchComputationUpdatesHandler) => VoidFunction
48-
readonly handleSignalUpdates: (
49-
listener: (payload: { id: NodeID; value: EncodedValue<boolean> }[]) => void,
50-
) => VoidFunction
51-
readonly handlePropsUpdate: Listen<Mapped.Props>
52-
readonly handleValueUpdate: Listen<EncodedValue<boolean>>
53-
readonly handleStructureUpdates: Listen<RootsUpdates>
58+
readonly listenTo: EventHubOn<EventHubChannels>
5459
readonly components: Accessor<Record<NodeID, Mapped.ResolvedComponent[]>>
5560
readonly findComponent: (rootId: NodeID, nodeId: NodeID) => Mapped.ResolvedComponent | undefined
5661
readonly inspectedDetails: Accessor<Mapped.OwnerDetails | null>
5762
readonly setInspectedOwner: (payload: { rootId: NodeID; nodeId: NodeID } | null) => void
5863
readonly getElementById: (id: NodeID) => HTMLElement | undefined
5964
readonly setInspectedSignal: (id: NodeID, selected: boolean) => EncodedValue<boolean> | null
6065
readonly setInspectedProp: (key: NodeID, selected: boolean) => void
61-
readonly setInspectedValue: (selected: boolean) => EncodedValue<boolean> | null
66+
readonly setInspectedValue: (selected: boolean) => void
6267
}
6368

6469
type RootUpdate = { removed: NodeID } | { updated: Mapped.Root }
6570

66-
const [handleStructureUpdates, pushStructureUpdate] = (() => {
67-
const [handleStructureUpdate, emitStructureUpdate] = createSimpleEmitter<RootsUpdates>()
68-
const updates: Mapped.Root[] = []
69-
const removedIds = new Set<NodeID>()
70-
const trigger = throttle(() => {
71-
const updated: Record<NodeID, Mapped.Root> = {}
72-
for (let i = updates.length - 1; i >= 0; i--) {
73-
const update = updates[i]
74-
const { id } = update
75-
if (!removedIds.has(id) && !updated[id]) updated[id] = update
76-
}
77-
emitStructureUpdate({ updated, removed: [...removedIds] })
78-
updates.length = 0
79-
removedIds.clear()
80-
}, 50)
81-
const pushStructureUpdate = (update: RootUpdate) => {
82-
if ("removed" in update) removedIds.add(update.removed)
83-
else if (removedIds.has(update.updated.id)) return
84-
else updates.push(update.updated)
85-
trigger()
86-
}
87-
return [handleStructureUpdate, pushStructureUpdate]
88-
})()
89-
9071
export const debuggerConfig = {
9172
gatherComponents: false,
9273
}
@@ -97,6 +78,14 @@ const exported = createInternalRoot(() => {
9778
/** forced — immediate global update */
9879
const [onForceUpdate, forceTriggerUpdate] = createSimpleEmitter()
9980

81+
const eventHub = createEventHub<EventHubChannels>(bus => ({
82+
ComputationUpdates: bus(),
83+
SignalUpdates: bus(),
84+
PropsUpdate: bus(),
85+
ValueUpdate: bus(),
86+
StructureUpdates: bus(),
87+
}))
88+
10089
//
10190
// Consumers:
10291
//
@@ -128,6 +117,32 @@ const exported = createInternalRoot(() => {
128117
pushStructureUpdate({ updated: newRoot })
129118
}
130119

120+
//
121+
// Structure updates:
122+
//
123+
const pushStructureUpdate = (() => {
124+
const updates: Mapped.Root[] = []
125+
const removedIds = new Set<NodeID>()
126+
const trigger = throttle(() => {
127+
const updated: Record<NodeID, Mapped.Root> = {}
128+
for (let i = updates.length - 1; i >= 0; i--) {
129+
const update = updates[i]
130+
const { id } = update
131+
if (!removedIds.has(id) && !updated[id]) updated[id] = update
132+
}
133+
eventHub.emit("StructureUpdates", { updated, removed: [...removedIds] })
134+
updates.length = 0
135+
removedIds.clear()
136+
}, 50)
137+
const pushStructureUpdate = (update: RootUpdate) => {
138+
if ("removed" in update) removedIds.add(update.removed)
139+
else if (removedIds.has(update.updated.id)) return
140+
else updates.push(update.updated)
141+
trigger()
142+
}
143+
return pushStructureUpdate
144+
})()
145+
131146
//
132147
// Inspected Owner details:
133148
//
@@ -138,38 +153,56 @@ const exported = createInternalRoot(() => {
138153
signals: new Set<NodeID>(),
139154
props: new Set<string>(),
140155
value: false,
156+
getValue: () => null as unknown,
141157
}
142158

143159
const [details, setDetails] = createSignal<Mapped.OwnerDetails | null>(null)
144160

145161
const getElementById = (id: NodeID): HTMLElement | undefined => inspected.elementMap.get(id)
146162

147-
const [handleSignalUpdates, pushSignalUpdate] = createBatchedUpdateEmitter<{
163+
const pushSignalUpdate = createBatchedUpdateEmitter<{
148164
id: NodeID
149165
value: EncodedValue<boolean>
150-
}>()
166+
}>(updates => eventHub.emit("SignalUpdates", updates))
151167
const onSignalUpdate: SignalUpdateHandler = untrackedCallback((id, value) => {
152168
if (!enabled() || !inspected.owner) return
153169
const isSelected = inspected.signals.has(id)
154170
pushSignalUpdate({ id, value: encodeValue(value, isSelected, inspected.elementMap) })
155171
})
156172

157-
const [handleValueUpdate, emitValueUpdate] = createSimpleEmitter<EncodedValue<boolean>>()
158-
const onValueUpdate: ValueUpdateHandler = throttle(value => {
159-
if (!enabled() || !inspected.owner) return
160-
emitValueUpdate(encodeValue(value, inspected.value, inspected.elementMap))
161-
})
173+
const triggerValueUpdate = (() => {
174+
let updateNext = false
175+
const forceValueUpdate = () => {
176+
if (!enabled() || !inspected.owner) return (updateNext = false)
177+
eventHub.emit("ValueUpdate", {
178+
value: encodeValue(inspected.getValue(), inspected.value, inspected.elementMap),
179+
update: updateNext,
180+
})
181+
updateNext = false
182+
}
183+
const triggerValueUpdate = throttle(forceValueUpdate)
184+
function handleValueUpdate(update: boolean, force = false) {
185+
if (update) updateNext = true
186+
if (force) forceValueUpdate()
187+
else triggerValueUpdate()
188+
}
189+
return handleValueUpdate
190+
})()
162191

163192
const setInspectedDetails = untrackedCallback((owner: Solid.Owner) => {
164193
inspected.owner && clearOwnerObservers(inspected.owner)
165194
inspected.props.clear()
166195
inspected.signals.clear()
167196
inspected.owner = owner
168197
inspected.value = false
169-
const result = collectOwnerDetails(owner, { onSignalUpdate, onValueUpdate })
198+
const result = collectOwnerDetails(owner, {
199+
onSignalUpdate,
200+
onValueUpdate: () => triggerValueUpdate(true),
201+
})
170202
setDetails(result.details)
171203
inspected.signalMap = result.signalMap
172204
inspected.elementMap = result.elementMap
205+
inspected.getValue = result.getOwnerValue
173206
})
174207
const clearInspectedDetails = () => {
175208
inspected.owner && clearOwnerObservers(inspected.owner)
@@ -180,15 +213,13 @@ const exported = createInternalRoot(() => {
180213
inspected.value = false
181214
}
182215

183-
const [handlePropsUpdate, emitPropsUpdate] = createSimpleEmitter<Mapped.Props>()
184-
185216
function updateInspectedProps() {
186217
if (!inspected.owner) return
187218
const props = encodeComponentProps(inspected.owner, {
188219
inspectedProps: inspected.props,
189220
elementMap: inspected.elementMap,
190221
})
191-
props && emitPropsUpdate(props)
222+
props && eventHub.emit("PropsUpdate", props)
192223
}
193224

194225
createEffect(() => {
@@ -198,7 +229,12 @@ const exported = createInternalRoot(() => {
198229
else inspected.owner && setInspectedDetails(inspected.owner)
199230

200231
// update the owner details whenever there is a change in solid's internals
201-
makeSolidUpdateListener(throttle(updateInspectedProps, 150))
232+
makeSolidUpdateListener(
233+
throttle(() => {
234+
updateInspectedProps()
235+
triggerValueUpdate(false)
236+
}, 150),
237+
)
202238
})
203239

204240
const setInspectedOwner: PluginData["setInspectedOwner"] = payload => {
@@ -225,24 +261,22 @@ const exported = createInternalRoot(() => {
225261
}
226262
const setInspectedValue: PluginData["setInspectedValue"] = selected => {
227263
if (!inspected.owner) return null
228-
return encodeOwnerValue(inspected.owner, (inspected.value = selected), inspected.elementMap)
264+
inspected.value = selected
265+
triggerValueUpdate(false, true)
229266
}
230267

231268
//
232269
// Computation updates:
233270
//
234-
const [handleComputationUpdates, _pushComputationUpdate] =
235-
createBatchedUpdateEmitter<ComputationUpdate>()
271+
const _pushComputationUpdate = createBatchedUpdateEmitter<ComputationUpdate>(updates =>
272+
eventHub.emit("ComputationUpdates", updates),
273+
)
236274
const pushComputationUpdate: ComputationUpdateHandler = (rootId, id) => {
237275
_pushComputationUpdate({ rootId, id })
238276
}
239277

240278
const pluginData: PluginData = {
241-
handleComputationUpdates,
242-
handleSignalUpdates,
243-
handlePropsUpdate,
244-
handleValueUpdate,
245-
handleStructureUpdates,
279+
listenTo: eventHub.on,
246280
components,
247281
findComponent,
248282
triggerUpdate,

packages/debugger/src/server.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,14 @@ export const attachDebugger: typeof API.attachDebugger = () => {}
1212
export const useDebugger: typeof API.useDebugger = () => ({
1313
triggerUpdate: () => {},
1414
forceTriggerUpdate: () => {},
15-
handleComputationUpdates: () => () => {},
16-
handleSignalUpdates: () => () => {},
17-
handleStructureUpdates: () => () => {},
15+
listenTo: () => () => {},
1816
components: () => ({}),
1917
findComponent: () => undefined,
2018
setInspectedOwner: () => {},
2119
getElementById: () => undefined,
2220
handlePropsUpdate: () => () => {},
2321
setInspectedSignal: () => null,
2422
setInspectedProp: () => {},
25-
handleValueUpdate: () => () => {},
2623
inspectedDetails: () => null,
2724
setInspectedValue: () => null,
2825
})

0 commit comments

Comments
 (0)