Skip to content

Commit dddf24f

Browse files
committed
Pre-observe all components props
1 parent 27e9d3e commit dddf24f

File tree

7 files changed

+138
-107
lines changed

7 files changed

+138
-107
lines changed

.changeset/forty-nights-find.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@solid-devtools/debugger": patch
3+
---
4+
5+
Pre-observe all components props, to sometimes get actual values when inspected.

packages/debugger/src/inspector/index.ts

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ import {type InspectedState, type Mapped, type NodeID, type OutputEmit, type Sol
77
import {onOwnerDispose} from '../main/utils.ts'
88
import setup from '../main/setup.ts'
99
import {UNOWNED_ROOT} from '../main/roots.ts'
10-
import {type ObservedPropsMap, ValueNodeMap, clearOwnerObservers, collectOwnerDetails} from './inspector.ts'
10+
import * as inspector from './inspector.ts'
1111
import {encodeValue} from './serialize.ts'
12-
import {type StoreNodeProperty, type StoreUpdateData, observeStoreNode, setOnStoreNodeUpdate} from './store.ts'
12+
import * as store from './store.ts'
1313
import {GLOBAL_GET_VALUE, type InspectorUpdate, type InspectorUpdateMap, PropGetterState} from './types.ts'
1414

1515
export * from './types.ts'
@@ -25,11 +25,11 @@ export function createInspector(props: {
2525
resetInspectedNode: VoidFunction
2626
emit: OutputEmit
2727
}) {
28-
28+
2929
let lastDetails: Mapped.OwnerDetails | undefined
3030
let inspectedOwner: Solid.Owner | null
31-
let valueMap = new ValueNodeMap()
32-
const propsMap: ObservedPropsMap = new WeakMap()
31+
let valueMap = new inspector.ValueNodeMap()
32+
const propsMap: inspector.ObservedPropsMap = new WeakMap()
3333
/** compare props object with the previous one to see whats changed */
3434
let checkProxyProps: (() => InspectorUpdateMap['propKeys'] | null) | null
3535

@@ -46,7 +46,7 @@ export function createInspector(props: {
4646
const {pushPropState, pushValueUpdate, pushInspectToggle, triggerPropsCheck, clearUpdates} =
4747
(() => {
4848
const valueUpdates = new Map<ValueItemID, boolean | null>()
49-
let storeUpdates: [storeProperty: StoreNodeProperty, data: StoreUpdateData][] = []
49+
let storeUpdates: [storeProperty: store.StoreNodeProperty, data: store.StoreUpdateData][] = []
5050
let checkProps = false
5151
let propStates: InspectorUpdateMap['propState'] = {}
5252

@@ -64,7 +64,7 @@ export function createInspector(props: {
6464
selected,
6565
setup.eli,
6666
selected &&
67-
(storeNode => node.addStoreObserver(observeStoreNode(storeNode))),
67+
(storeNode => node.addStoreObserver(store.observeStoreNode(storeNode))),
6868
)
6969
batchedUpdates.push([
7070
toggleChange === null ? 'value' : 'inspectToggle',
@@ -109,7 +109,7 @@ export function createInspector(props: {
109109

110110
// Subscribe to any store updates
111111
// observed stores are managed by the store.ts module (only stores in selected values get observed)
112-
setOnStoreNodeUpdate((...payload) => {
112+
store.setOnStoreNodeUpdate((...payload) => {
113113
storeUpdates.push(payload)
114114
flush()
115115
})
@@ -148,45 +148,44 @@ export function createInspector(props: {
148148

149149
let clearPrevDisposeListener: VoidFunction | undefined
150150

151-
151+
152152

153153
function inspectOwnerId(id: NodeID | null): void {
154-
154+
155155
const owner = id && getObjectById(id, ObjectType.Owner)
156-
if (inspectedOwner) clearOwnerObservers(inspectedOwner, propsMap)
156+
if (inspectedOwner) inspector.clearOwnerObservers(inspectedOwner, propsMap)
157157
inspectedOwner = owner
158-
158+
159159
valueMap.reset()
160160
clearUpdates()
161-
161+
162162
if (owner) {
163-
const result = collectOwnerDetails(owner, {
163+
const result = inspector.collectOwnerDetails(owner, {
164164
onValueUpdate: pushValueUpdate,
165165
onPropStateChange: pushPropState,
166166
observedPropsMap: propsMap,
167167
eli: setup.eli,
168168
})
169-
169+
170170
props.emit(msg('InspectedNodeDetails', result.details))
171-
171+
172172
valueMap = result.valueMap
173173
lastDetails = result.details
174174
checkProxyProps = result.checkProxyProps || null
175175
} else {
176176
lastDetails = undefined
177177
checkProxyProps = null
178178
}
179-
179+
180180
clearPrevDisposeListener?.()
181181
clearPrevDisposeListener = owner
182182
? onOwnerDispose(owner, props.resetInspectedNode)
183183
: undefined
184184
}
185185

186-
const inspectedOwnerId = s.createMemo(
187-
() => props.enabled()
188-
? props.inspectedState().ownerId
189-
: null
186+
const inspectedOwnerId = s.createMemo(() => props.enabled()
187+
? props.inspectedState().ownerId
188+
: null
190189
)
191190
s.createEffect(() => {
192191
let id = inspectedOwnerId()
@@ -219,6 +218,9 @@ export function createInspector(props: {
219218
consoleLogValue(value_id: ValueItemID): void {
220219
// eslint-disable-next-line no-console
221220
console.log(getValue(value_id))
221+
},
222+
preObserveComponent(component: Solid.Component): void {
223+
inspector.preObserveComponentProps(component, propsMap)
222224
}
223225
}
224226
}

packages/debugger/src/inspector/inspector.ts

Lines changed: 51 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,9 @@ export class ValueNodeMap {
4646
}
4747
}
4848

49-
export namespace Inspector {
50-
/** Prop becomes stale or live (is being currently listened to reactively or not) */
51-
export type OnPropStateChange = (key: string, state: PropGetterState) => void
52-
export type OnValueUpdate = (id: ValueItemID) => void
53-
}
49+
/** Prop becomes stale or live (is being currently listened to reactively or not) */
50+
export type OnPropStateChange = (key: string, state: PropGetterState) => void
51+
export type OnValueUpdate = (id: ValueItemID) => void
5452

5553
export type ObservedPropsMap = WeakMap<Solid.Component['props'], ObservedProps>
5654

@@ -63,13 +61,13 @@ const $NOT_SET = Symbol('not-set')
6361
export class ObservedProps {
6462
constructor(readonly props: Solid.Component['props']) {}
6563

66-
private onPropStateChange?: Inspector.OnPropStateChange | undefined
67-
private onValueUpdate?: Inspector.OnValueUpdate | undefined
64+
private onPropStateChange?: OnPropStateChange | undefined
65+
private onValueUpdate?: OnValueUpdate | undefined
6866
private observedGetters = {} as Record<string, {v: unknown | typeof $NOT_SET; n: number}>
6967

7068
observe(
71-
onPropStateChange: Inspector.OnPropStateChange,
72-
onValueUpdate: Inspector.OnValueUpdate,
69+
onPropStateChange: OnPropStateChange,
70+
onValueUpdate: OnValueUpdate,
7371
) {
7472
this.onPropStateChange = onPropStateChange
7573
this.onValueUpdate = onValueUpdate
@@ -157,8 +155,8 @@ export function clearOwnerObservers(owner: Solid.Owner, observedPropsMap: Observ
157155

158156
// Globals set before collecting the owner details
159157
let ValueMap!: ValueNodeMap
160-
let OnValueUpdate: Inspector.OnValueUpdate
161-
let OnPropStateChange: Inspector.OnPropStateChange
158+
let OnValueUpdate: OnValueUpdate
159+
let OnPropStateChange: OnPropStateChange
162160
let PropsMap: ObservedPropsMap
163161

164162
const $INSPECTOR = Symbol('inspector')
@@ -199,26 +197,48 @@ function mapSourceValue<TEl extends object>(
199197
}
200198
}
201199

200+
/**
201+
* Pre-observe component props without gathering data.
202+
* To get fresh values when the component is later inspected.
203+
*/
204+
export function preObserveComponentProps(
205+
component: Solid.Component,
206+
props_map: ObservedPropsMap,
207+
): void {
208+
let props = component.props
209+
210+
// Only observe static shape props (not proxy props)
211+
if (utils.is_solid_proxy(props)) return
212+
213+
let observed = props_map.get(props)
214+
if (!observed) props_map.set(props, (observed = new ObservedProps(props)))
215+
216+
// Set up getters for all props to enable tracking
217+
for (let [key, desc] of Object.entries(Object.getOwnPropertyDescriptors(props))) {
218+
if (desc.get) {
219+
let id: ValueItemID = `prop:${key}`
220+
observed.observeProp(key, id, desc.get)
221+
}
222+
}
223+
}
224+
202225
function mapProps<TEl extends object>(
203226
props: Solid.Component['props'],
204227
eli: ElementInterface<TEl>,
205228
) {
206229
// proxy props need to be checked for changes in keys
207-
const isProxy = !!(props as any)[setup.solid.$PROXY]
208-
const record: Mapped.Props['record'] = {}
209-
230+
let is_proxy = utils.is_solid_proxy(props)
210231
let checkProxyProps: (() => ReturnType<typeof compareProxyPropKeys>) | undefined
232+
233+
let record: Mapped.Props['record'] = {}
211234

212235
// PROXY PROPS
213-
if (isProxy) {
214-
let propsKeys = Object.keys(props)
236+
if (is_proxy) {
237+
let keys = Object.keys(props)
215238

216-
for (const key of propsKeys) record[key] = {getter: PropGetterState.Stale, value: null}
239+
for (let key of keys) record[key] = {getter: PropGetterState.Stale, value: null}
217240

218-
checkProxyProps = () => {
219-
const _oldKeys = propsKeys
220-
return compareProxyPropKeys(_oldKeys, (propsKeys = Object.keys(props)))
221-
}
241+
checkProxyProps = () => compareProxyPropKeys(keys, (keys = Object.keys(props)))
222242
}
223243
// STATIC SHAPE
224244
else {
@@ -227,13 +247,13 @@ function mapProps<TEl extends object>(
227247

228248
observed.observe(OnPropStateChange, OnValueUpdate)
229249

230-
for (const [key, desc] of Object.entries(Object.getOwnPropertyDescriptors(props))) {
231-
const id: ValueItemID = `prop:${key}`
250+
for (let [key, desc] of Object.entries(Object.getOwnPropertyDescriptors(props))) {
251+
let id: ValueItemID = `prop:${key}`
232252
// GETTER
233253
if (desc.get) {
234-
const {getValue, isStale} = observed.observeProp(key, id, desc.get)
254+
let {getValue, isStale} = observed.observeProp(key, id, desc.get)
235255
ValueMap.add(id, getValue)
236-
const lastValue = getValue()
256+
let lastValue = getValue()
237257
record[key] = {
238258
getter: isStale ? PropGetterState.Stale : PropGetterState.Live,
239259
value: lastValue !== $NOT_SET ? encodeValue(getValue(), false, eli) : null,
@@ -252,12 +272,12 @@ function mapProps<TEl extends object>(
252272
}
253273
}
254274

255-
return {props: {proxy: isProxy, record}, checkProxyProps}
275+
return {props: {proxy: is_proxy, record}, checkProxyProps}
256276
}
257277

258278
export type CollectDetailsConfig<TEl extends object> = {
259-
onPropStateChange: Inspector.OnPropStateChange
260-
onValueUpdate: Inspector.OnValueUpdate
279+
onPropStateChange: OnPropStateChange
280+
onValueUpdate: OnValueUpdate
261281
observedPropsMap: ObservedPropsMap
262282
eli: ElementInterface<TEl>,
263283
}
@@ -270,10 +290,10 @@ export function collectOwnerDetails<TEl extends object>(
270290
const {onValueUpdate, eli} = config
271291

272292
// Set globals
273-
ValueMap = new ValueNodeMap()
274-
OnValueUpdate = onValueUpdate
293+
ValueMap = new ValueNodeMap()
294+
OnValueUpdate = onValueUpdate
275295
OnPropStateChange = config.onPropStateChange
276-
PropsMap = config.observedPropsMap
296+
PropsMap = config.observedPropsMap
277297

278298
const id = getSdtId(owner, ObjectType.Owner)
279299
const type = utils.markOwnerType(owner)

0 commit comments

Comments
 (0)