- This one cannot use internals nor dev-only apis because the goal is to detect solid on any page. — source code
- The test is complicated, brittle, might have false-positives and doesn't tell you the solid version.
By node I mean any solid internal object, like owners, signals, roots, store-nodes, and anything user registers with solid.DEV.registerGraph()
.
- There are many internal owner properties that are inspected to determine kind of node. — source code
- This is super brittle, some properties are set some time after the owner is created, most of the properties accessed are not used for anything else.
owner.name
orsignal.name
orowner.component.name
orowner.component.displayName
-
No one good way to give custom names to components.
This works but it's a side-effect.
Bar.displayName = "Foo.Bar"
While this is ugly
if (isDev) { getOwner()!.name = "Foo.Bar" }
solid.DEV.hooks.afterCreateOwner()
for attaching the debugger to roots- to gather top-level roots like
render()
- but also to "re-attach roots back to the owner tree" when
createRoot
is used under owners, like inmapArray
- source code
- access to
owner.owner
andowner.cleanups
to listen to parent's (root's detached owner) cleanup- so that if the root's parent disposes before the root, the root will need to be "reattached" to first found "alive" owner
- this is rather uncommon
- to gather top-level roots like
- Any root created before
solid.DEV.hooks.afterCreateOwner
is set will be missed.
This is annoying because the extension's content-script isn't garanteed to run before user scripts. Making thesolid-devtools
npm package andimport "solid-devtools"
required to capture all the roots.
If there was access to any unowned root, the extension would work with any solid app with no setup.
- Reads
owner.owner
andowner.owned
to walk the tree in both directions
- Writing to
owner.cleanups
to listen to cleanup of any owner — source code
-
The cleanups are executed depth first—if I try to walk the tree on child's cleanup, I cannot tell if the parent is getting cleaned up as well or not. There is no
isDisposed
property. -
There is no difference between disposing and rerunning—I have to listen to parents cleanup to when targetting disposal.
-
owner.fn
is patched to observe when the computation reruns — source code -
owner.value
is read to get the current owner value and patched to listen to changes — source code- signals are being observed the same way
- this is also separate from observing
owner.fn
because not every rerun will cause the value to update (thanks toequals
option)
-
owner.sources
— by listening to each of the sources, I'm able to tell which source caused the computation to rerun. — source code
-
owner.sources
andowner.observers
to crawl the graph -
sourceValue.graph
— for signals to know to which owner they "belong to" -
solid.DEV.hooks.afterUpdate()
is used as a trigger to update the dependency graph
-
solid.$PROXY
is used to check if the props object is a proxy -
If it's not a proxy, I can patch it with
Object.defineProperty(props, key, {get})
to intercept accessing props—since accessing props might cause side-effects I have to wait for the user.- source code
solid.getListener() + solid.onCleanup()
is useful there to know when the prop is no longer being listened to—I might not have the latest value anymore.
-
For proxy props I can only get keys with
Object.keys(props)
-
I cannot intercept proxy props—users just see
foo: unknown
without the value -
props object cannot be changed, just monkeypatched—replacing it would probably let me intercept reads to proxy props
-
solid.DEV.hooks.afterCreateOwner()
gets called beforeprops
are attached to the owner, so I cannot get to them then.- so currently I cannot intercept the initial reads from props—which are the most common
- I would have to do
Object.defineProperty(owner, "props", {set})
to be able to patch props before component function executes probably - source code
-
store.unwrap()
with type reflection when serializing and reading stores -
store.isWrappable()
-
store.DEV.hooks.onStoreNodeUpdate()
for observing changes to stores — source code
- There is no connection between signals and the store-nodes they are being used in.
So if there is an effect that listens to some store property (e.g.createEffect(() => store.foo)
), from devtools perspective this is just some random signal being observed inowner.sources
.
-
owner.sourceMap
for getting which signals, stores or custom values belong to an owner. -
solid.DEV.hooks.afterCreateSignal()
is useful for getting unowned signals- which is important for the
export const [state, setState] = solid.createSignal({...})
style of state management — source code
- which is important for the
-
The two issues with
afterCreateSignal
are addressed by solidjs/solid#2396 and solidjs/solid#2393 -
The other is similar to
afterCreateOwner
— signals created before the hook callback is set will be missed. This one is trickier to solve because signals don't have a clear lifecycle unlike roots.
-
There is no way to connect a render effect to the dom property it updates, so I cannot give any meaningfull info about them, or to which element they even belong.
-
There is no connection between a signal accessor and the signal object it belongs to.
This is an usually an issue with jsx and components returning signals. I cannot show what the value is without calling the accessor which might have side effects.
-
No tools to mark "custom primitives" for devtools. For example
createResource
doesn't have a single instance that I can inspect—it's made up from multiple separate signals, computations and other values.