@solidrt/core 0.0.28 → 0.0.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/renderer.ts +40 -0
- package/src/window.ts +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidrt/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.29",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Antoine van Wel",
|
|
6
6
|
"type": "module",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"colord": "^2.9.3"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@solidrt/flux-types": "0.0.
|
|
30
|
+
"@solidrt/flux-types": "0.0.29"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"@solidjs/signals": "2.0.0-beta.17",
|
package/src/renderer.ts
CHANGED
|
@@ -88,6 +88,46 @@ function removeNode(parent: ProxyNode, node: ProxyNode): void {
|
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
// ------ Leak sentinel (dev only) --------
|
|
92
|
+
|
|
93
|
+
// A node created but never inserted is unreachable by the remove -> destroy
|
|
94
|
+
// sweep, so it leaks permanently, natively and in the maps here. The usual
|
|
95
|
+
// cause is an element-valued prop read more than once: every read builds a
|
|
96
|
+
// fresh subtree and only the mounted one is ever freed. Rather than
|
|
97
|
+
// bookkeeping on the hot create/insert paths, orphans are derived from the
|
|
98
|
+
// proxy map itself: parentless, not the window root, and not awaiting the
|
|
99
|
+
// destroy sweep. window.ts runs the scan on a rendered frame every few
|
|
100
|
+
// seconds; dev bundles only (srt always defines process.env.NODE_ENV, so a
|
|
101
|
+
// production bundle folds the check into a constant early return).
|
|
102
|
+
const SENTINEL_INTERVAL_MS = 5000
|
|
103
|
+
let sentinelDue = 0
|
|
104
|
+
let warnedLeakTypes = new Set<string>()
|
|
105
|
+
|
|
106
|
+
export function scanForOrphans(now: number): void {
|
|
107
|
+
if (process.env.NODE_ENV === "production") return
|
|
108
|
+
if (now < sentinelDue) return
|
|
109
|
+
sentinelDue = now + SENTINEL_INTERVAL_MS
|
|
110
|
+
let counts = new Map<string, number>()
|
|
111
|
+
let total = 0
|
|
112
|
+
for (let node of nodes.values()) {
|
|
113
|
+
if (node.parent !== undefined || node.elementType === "window" || pendingDestroy.has(node.id)) continue
|
|
114
|
+
total += 1
|
|
115
|
+
counts.set(node.elementType, (counts.get(node.elementType) ?? 0) + 1)
|
|
116
|
+
}
|
|
117
|
+
if (total === 0) return
|
|
118
|
+
let fresh = [...counts].filter(([type]) => !warnedLeakTypes.has(type))
|
|
119
|
+
if (fresh.length === 0) return
|
|
120
|
+
for (let [type] of fresh) warnedLeakTypes.add(type)
|
|
121
|
+
let list = fresh.map(([type, n]) => `<${type}> x${n}`).join(", ")
|
|
122
|
+
console.warn(
|
|
123
|
+
`Leak sentinel: ${total} nodes are unreachable and will never be freed: ${list}. ` +
|
|
124
|
+
`The usual cause is reading an element-valued prop more than once (every read ` +
|
|
125
|
+
`builds a new subtree); read it once where it mounts, or resolve it with ` +
|
|
126
|
+
`children(). If these nodes are intentionally kept for later mounting, ignore ` +
|
|
127
|
+
`this. Element types already reported are not reported again.`,
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
|
|
91
131
|
// A property the native tree rejected must not take down the reactive system:
|
|
92
132
|
// a typo'd or not-yet-implemented prop poisons only itself. Warn once per
|
|
93
133
|
// element kind + property with a stack (the dev server remaps its frames to
|
package/src/window.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { requestFrame } from "flux:rendertree"
|
|
|
3
3
|
import { renderFrame } from "srt:render"
|
|
4
4
|
import { on, once } from "srt:events"
|
|
5
5
|
import { getEventHandler, getFocusedNodeId, setFocus } from "./core"
|
|
6
|
+
import { scanForOrphans } from "./renderer"
|
|
6
7
|
|
|
7
8
|
// ------ Pointer capture -----------------
|
|
8
9
|
|
|
@@ -212,6 +213,7 @@ export function attachWindow(_nodeId: number) {
|
|
|
212
213
|
for (let fn of frames.values()) fn(t, frame, refreshRate)
|
|
213
214
|
}
|
|
214
215
|
flush()
|
|
216
|
+
scanForOrphans(t)
|
|
215
217
|
renderFrame()
|
|
216
218
|
}
|
|
217
219
|
|