Skip to content

Commit b61d074

Browse files
authored
Improve dom walker algorithm (#348)
2 parents 198aca6 + ea6c1c2 commit b61d074

File tree

8 files changed

+308
-210
lines changed

8 files changed

+308
-210
lines changed

.changeset/bright-rabbits-nail.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@solid-devtools/debugger": minor
3+
---
4+
5+
Require `ElementInterface.getChildren` to return `ArrayLike<T>`. (76ab4096a2ad531ae015e35c2475b78f08ac45a7)

.changeset/orange-islands-drop.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+
Improve tree walking algorithm for mapping elements. (#348)

examples/sandbox/src/App.tsx

Lines changed: 61 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -163,69 +163,67 @@ const App: s.Component = () => {
163163

164164
const [showBroken, setShowBroken] = s.createSignal(false)
165165

166-
return (
167-
<>
168-
{header()}
169-
{objmemo().subheader}
170-
<div>
171-
<header>
172-
<Button onClick={() => setCount(p => ++p)} text={`Count: ${count()}`} />
173-
<Button onClick={() => setCount(p => ++p)} text={`Count: ${count()}`} />
174-
</header>
175-
<sweb.Dynamic
176-
component='div'
177-
style={{height: '1rem', 'margin-top': '1rem'}}
178-
>
179-
<s.Show when={showEven()}>
180-
{s.createComponent(() => <>
181-
<BoldWrapper/>
182-
</>, {})}
183-
</s.Show>
184-
</sweb.Dynamic>
185-
{/* <button onClick={() => disposeApp()}>Dispose whole application</button>
186-
<br /> */}
187-
<button onClick={() => setShowBroken(p => !p)}>
188-
{showBroken() ? 'Hide' : 'Show'} broken component.
189-
</button>
190-
<s.ErrorBoundary
191-
fallback={(err, reset) => <>
192-
{err.toString()}
193-
<button
194-
onClick={() => {
195-
setShowBroken(false)
196-
reset()
197-
}}
198-
>
199-
Reset
200-
</button>
201-
</>}
202-
>
203-
<s.Show when={showBroken()}>
204-
<Broken />
205-
</s.Show>
206-
</s.ErrorBoundary>
207-
<br />
208-
<br />
209-
</div>
210-
<DynamicSpreadParent />
211-
<button onClick={() => setRootCount(p => ++p)}>Update root count</button>
212-
<button onClick={() => disposeOuterRoot()}>Dispose OUTSIDE_ROOT</button>
213-
<Article />
214-
<Todos title='Simple Todos Example' />
215-
{s.untrack(() => {
216-
const MARGIN = '24px'
217-
return <>
218-
<div style={{margin: MARGIN}}>
219-
<CountingComponent />
220-
</div>
221-
<div style={{margin: MARGIN}}>
222-
<ThemeExample />
223-
</div>
224-
</>
225-
})}
226-
<Recursive />
227-
</>
228-
)
166+
return <>
167+
{header()}
168+
{objmemo().subheader}
169+
<div>
170+
<header>
171+
<Button onClick={() => setCount(p => ++p)} text={`Count: ${count()}`} />
172+
<Button onClick={() => setCount(p => ++p)} text={`Count: ${count()}`} />
173+
</header>
174+
<sweb.Dynamic
175+
component='div'
176+
style={{height: '1rem', 'margin-top': '1rem'}}
177+
>
178+
<s.Show when={showEven()} fallback={<span>Count is very odd</span>}>
179+
{s.createComponent(() => <>
180+
<BoldWrapper/>
181+
</>, {})}
182+
</s.Show>
183+
</sweb.Dynamic>
184+
{/* <button onClick={() => disposeApp()}>Dispose whole application</button>
185+
<br /> */}
186+
<button onClick={() => setShowBroken(p => !p)}>
187+
{showBroken() ? 'Hide' : 'Show'} broken component.
188+
</button>
189+
<s.ErrorBoundary
190+
fallback={(err, reset) => <>
191+
{err.toString()}
192+
<button
193+
onClick={() => {
194+
setShowBroken(false)
195+
reset()
196+
}}
197+
>
198+
Reset
199+
</button>
200+
</>}
201+
>
202+
<s.Show when={showBroken()}>
203+
<Broken />
204+
</s.Show>
205+
</s.ErrorBoundary>
206+
<br />
207+
<br />
208+
</div>
209+
<DynamicSpreadParent />
210+
<button onClick={() => setRootCount(p => ++p)}>Update root count</button>
211+
<button onClick={() => disposeOuterRoot()}>Dispose OUTSIDE_ROOT</button>
212+
<Article />
213+
<Todos title='Simple Todos Example' />
214+
{s.untrack(() => {
215+
const MARGIN = '24px'
216+
return <>
217+
<div style={{margin: MARGIN}}>
218+
<CountingComponent />
219+
</div>
220+
<div style={{margin: MARGIN}}>
221+
<ThemeExample />
222+
</div>
223+
</>
224+
})}
225+
<Recursive />
226+
</>
229227
}
230228

231229
const CountingComponent = () => {

packages/debugger/src/main/id.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ export function getSdtId<T extends ObjectType>(obj: ValueMap[T], objType: T): No
4949
return id
5050
}
5151

52+
export const get_id_owner = (o: Solid.Owner): NodeID => getSdtId(o, ObjectType.Owner)
53+
export const get_id_el = (o: object): NodeID => getSdtId(o, ObjectType.Element)
54+
export const get_id_signal = (o: Solid.Signal): NodeID => getSdtId(o, ObjectType.Signal)
55+
export const get_id_store = (o: Solid.Store): NodeID => getSdtId(o, ObjectType.Store)
56+
export const get_id_store_node = (o: Solid.StoreNode): NodeID => getSdtId(o, ObjectType.StoreNode)
57+
export const get_id_custom_value = (o: Solid.SourceMapValue): NodeID => getSdtId(o, ObjectType.CustomValue)
58+
5259
export function getObjectById<T extends ObjectType>(id: NodeID, objType: T): ValueMap[T] | null {
5360
const ref = RefMapMap[objType].get(id)
5461
return ref?.deref() ?? null

packages/debugger/src/main/types.ts

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ export type Rect = {
143143
height: number
144144
}
145145

146+
export type ElementChildren<T extends object> = Iterable<T> & ArrayLike<T>
147+
146148
/**
147149
* When using a custom solid renderer, you should provide a custom element interface.
148150
* By default the debugger assumes that rendered elements are DOM elements.
@@ -151,7 +153,7 @@ export type ElementInterface<T extends object> = {
151153
isElement: (obj: object | T) => obj is T,
152154
getElementAt: (e: MouseEvent) => T | null,
153155
getName: (el: T) => string | null,
154-
getChildren: (el: T) => Iterable<T>,
156+
getChildren: (el: T) => ElementChildren<T>,
155157
getParent: (el: T) => T | null,
156158
getLocation: (el: T) => SourceLocation | null,
157159
getRect: (el: T) => Rect | null,
@@ -196,17 +198,14 @@ export const getValueItemId = <T extends ValueItemType>(
196198
export type ValueUpdateListener = (newValue: unknown, oldValue: unknown) => void
197199

198200
export namespace Solid {
199-
export type OwnerBase = import('solid-js').Owner
200-
export type SourceMapValue = import('solid-js/types/reactive/signal.d.ts').SourceMapValue
201-
export type Signal = import('solid-js/types/reactive/signal.d.ts').SignalState<unknown>
202-
export type Computation = import('solid-js/types/reactive/signal.d.ts').Computation<unknown>
203-
export type Memo = import('solid-js/types/reactive/signal.d.ts').Memo<unknown>
201+
export type OwnerBase = import('solid-js').Owner
202+
export type SourceMapValue = import('solid-js/types/reactive/signal.d.ts').SourceMapValue
203+
export type Signal = import('solid-js/types/reactive/signal.d.ts').SignalState<unknown>
204+
export type Computation = import('solid-js/types/reactive/signal.d.ts').Computation<unknown>
205+
export type Memo = import('solid-js/types/reactive/signal.d.ts').Memo<unknown>
204206
export type RootFunction<T> = import('solid-js/types/reactive/signal.d.ts').RootFunction<T>
205-
export type EffectFunction =
206-
import('solid-js/types/reactive/signal.d.ts').EffectFunction<unknown>
207-
export type Component = import('solid-js/types/reactive/signal.d.ts').DevComponent<{
208-
[key: string]: unknown
209-
}>
207+
export type EffectFunction = import('solid-js/types/reactive/signal.d.ts').EffectFunction<unknown>
208+
export type Component = import('solid-js/types/reactive/signal.d.ts').DevComponent<{[key: string]: unknown}>
210209

211210
export type CatchError = Omit<Computation, 'fn'> & {fn: undefined}
212211

@@ -230,10 +229,10 @@ export namespace Solid {
230229
// STORE
231230
//
232231

233-
export type StoreNode = import('solid-js/store').StoreNode
234-
export type NotWrappable = import('solid-js/store').NotWrappable
232+
export type StoreNode = import('solid-js/store').StoreNode
233+
export type NotWrappable = import('solid-js/store').NotWrappable
235234
export type OnStoreNodeUpdate = import('solid-js/store/types/store.d.ts').OnStoreNodeUpdate
236-
export type Store = SourceMapValue & {value: StoreNode}
235+
export type Store = SourceMapValue & {value: StoreNode}
237236
}
238237

239238
declare module 'solid-js/types/reactive/signal.d.ts' {

packages/debugger/src/structure/index.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,14 @@ export function createStructure<TEl extends object>(props: {
5858
let shouldUpdateAllRoots = true
5959

6060
const onComputationUpdate: walker.ComputationUpdateHandler = (
61-
rootId, owner, changedStructure,
61+
root_id, owner, changed_structure,
6262
) => {
6363
// separate the callback from the computation
6464
queueMicrotask(() => {
6565
if (!props.enabled()) return
66-
if (changedStructure) {
67-
updateOwner(owner, rootId)
66+
if (changed_structure) {
67+
let owner_to_update = getClosestIncludedOwner(owner, treeWalkerMode) ?? owner
68+
updateOwner(owner_to_update, root_id)
6869
}
6970
let id = getSdtId(owner, ObjectType.Owner)
7071
props.onNodeUpdate(id)

packages/debugger/src/structure/walker.test.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -362,10 +362,10 @@ test.describe('TreeWalkerMode.DOM', () => {
362362

363363
let to_trigger: (() => void)[] = []
364364
let test_components: Solid.Component[] = []
365-
365+
366366
let el_header!: HTMLElement
367367
let el_h1!: HTMLHeadingElement
368-
368+
369369
let el_footer!: HTMLElement
370370
let el_main!: HTMLElement
371371
let el_h2!: HTMLHeadingElement
@@ -392,7 +392,7 @@ test.describe('TreeWalkerMode.DOM', () => {
392392
<span ref={el_span}>Click me</span>
393393
</button>
394394
}
395-
395+
396396
const App = () => {
397397
return (
398398
<>

0 commit comments

Comments
 (0)