@miaskiewicz/turbo-dom 0.1.33 → 0.1.35
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miaskiewicz/turbo-dom",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.35",
|
|
4
4
|
"description": "Faster, more spec-correct DOM for test runners — native html5ever (Rust/WASM) parser + lazy copy-on-write DOM. A drop-in-style alternative to jsdom/happy-dom for vitest & jest.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "index.js",
|
package/src/runtime/dom.mjs
CHANGED
|
@@ -363,6 +363,16 @@ export class Element extends Node {
|
|
|
363
363
|
this.__attrIdx = -1; // buffer index for lazy attr inflation
|
|
364
364
|
this.content = null; // <template> content fragment
|
|
365
365
|
this.shadowRoot = null; // open shadow root, if attached
|
|
366
|
+
// Predeclare the hot lazy fields (left at their "absent" sentinel) so every
|
|
367
|
+
// Element shares ONE hidden class. Without this, fields added on demand in
|
|
368
|
+
// varying order across elements make the shared query/match/style/event code
|
|
369
|
+
// megamorphic on property loads. Costs a few in-object slots, buys monomorphism.
|
|
370
|
+
this.__childNodesList = undefined; // childNodes NodeList (memoized live view)
|
|
371
|
+
this.__childrenList = undefined; // children HTMLCollection (memoized live view)
|
|
372
|
+
this.__qaCache = undefined; // querySelectorAll cache (selector → {v,r})
|
|
373
|
+
this.__qsCache = undefined; // querySelector cache
|
|
374
|
+
this.__computedStyle = undefined; // getComputedStyle snapshot (version-keyed)
|
|
375
|
+
this.__shadow = undefined; // attached shadow root (open or closed)
|
|
366
376
|
}
|
|
367
377
|
|
|
368
378
|
get nodeType() { return ELEMENT_NODE; }
|
package/src/runtime/window.mjs
CHANGED
|
@@ -79,6 +79,41 @@ function tagClass(matcher) {
|
|
|
79
79
|
// the bare names on globalThis (which would make these delegates call themselves).
|
|
80
80
|
const hostSetTimeout = globalThis.setTimeout;
|
|
81
81
|
const hostClearTimeout = globalThis.clearTimeout;
|
|
82
|
+
|
|
83
|
+
// Env-independent lazy global factories — built ONCE at module load, shared by
|
|
84
|
+
// every window (none capture document/url/windowProxy). Materialization still
|
|
85
|
+
// self-replaces onto the per-env base, so each env gets its own instance; only
|
|
86
|
+
// the factory objects/closures are shared, saving ~28 allocations per test file.
|
|
87
|
+
const SHARED_LAZY = {
|
|
88
|
+
localStorage: () => new Storage(),
|
|
89
|
+
sessionStorage: () => new Storage(),
|
|
90
|
+
matchMedia: () => makeMatchMedia(),
|
|
91
|
+
getComputedStyle: () => makeGetComputedStyle(),
|
|
92
|
+
IntersectionObserver: () => IntersectionObserver,
|
|
93
|
+
ResizeObserver: () => ResizeObserver,
|
|
94
|
+
requestAnimationFrame: () => (cb) => hostSetTimeout(() => cb(performanceNow()), 0),
|
|
95
|
+
cancelAnimationFrame: () => (id) => hostClearTimeout(id),
|
|
96
|
+
navigator: () => ({
|
|
97
|
+
userAgent: 'Mozilla/5.0 (turbo-dom) AppleWebKit/537.36',
|
|
98
|
+
platform: 'turbo-dom', vendor: '', language: 'en-US', languages: ['en-US'],
|
|
99
|
+
onLine: true, cookieEnabled: true, doNotTrack: null, maxTouchPoints: 0,
|
|
100
|
+
hardwareConcurrency: 4, deviceMemory: 8, webdriver: false,
|
|
101
|
+
clipboard: { readText: async () => '', writeText: async () => {}, read: async () => [], write: async () => {} },
|
|
102
|
+
permissions: { query: async () => ({ state: 'prompt', addEventListener() {}, removeEventListener() {} }) },
|
|
103
|
+
sendBeacon: () => true, vibrate: () => false,
|
|
104
|
+
}),
|
|
105
|
+
performance: () => ({ now: performanceNow, timeOrigin: 0, mark() {}, measure() {}, getEntriesByName: () => [], getEntriesByType: () => [], clearMarks() {}, clearMeasures() {} }),
|
|
106
|
+
Storage: () => Storage,
|
|
107
|
+
devicePixelRatio: () => 1,
|
|
108
|
+
innerWidth: () => 1024,
|
|
109
|
+
innerHeight: () => 768,
|
|
110
|
+
outerWidth: () => 1024,
|
|
111
|
+
outerHeight: () => 768,
|
|
112
|
+
scrollX: () => 0, scrollY: () => 0, pageXOffset: () => 0, pageYOffset: () => 0,
|
|
113
|
+
screenX: () => 0, screenY: () => 0, screenLeft: () => 0, screenTop: () => 0,
|
|
114
|
+
screen: () => ({ width: 1024, height: 768, availWidth: 1024, availHeight: 768, colorDepth: 24, pixelDepth: 24, orientation: { type: 'landscape-primary', angle: 0, addEventListener() {}, removeEventListener() {} } }),
|
|
115
|
+
visualViewport: () => ({ width: 1024, height: 768, scale: 1, offsetLeft: 0, offsetTop: 0, pageLeft: 0, pageTop: 0, addEventListener() {}, removeEventListener() {} }),
|
|
116
|
+
};
|
|
82
117
|
const hostSetInterval = globalThis.setInterval;
|
|
83
118
|
const hostClearInterval = globalThis.clearInterval;
|
|
84
119
|
const hostQueueMicrotask = globalThis.queueMicrotask;
|
|
@@ -107,39 +142,13 @@ export function createWindow(document, { url = 'http://localhost/' } = {}) {
|
|
|
107
142
|
removeEventListener: (...a) => document.removeEventListener(...a),
|
|
108
143
|
};
|
|
109
144
|
|
|
110
|
-
//
|
|
145
|
+
// Per-env lazy globals: ONLY those that capture document/url/windowProxy.
|
|
146
|
+
// Everything env-independent lives in the module-level SHARED_LAZY (built once,
|
|
147
|
+
// not re-allocated per test file) — see below. None constructed until touched.
|
|
111
148
|
const lazy = {
|
|
112
|
-
localStorage: () => new Storage(),
|
|
113
|
-
sessionStorage: () => new Storage(),
|
|
114
|
-
matchMedia: () => makeMatchMedia(),
|
|
115
|
-
getComputedStyle: () => makeGetComputedStyle(),
|
|
116
|
-
IntersectionObserver: () => IntersectionObserver,
|
|
117
|
-
ResizeObserver: () => ResizeObserver,
|
|
118
|
-
requestAnimationFrame: () => (cb) => hostSetTimeout(() => cb(performanceNow()), 0),
|
|
119
|
-
cancelAnimationFrame: () => (id) => hostClearTimeout(id),
|
|
120
149
|
// subsystem grouping: history co-materializes with (and shares) location
|
|
121
150
|
location: () => makeLocation(url),
|
|
122
151
|
history: () => makeHistory(windowProxy.location),
|
|
123
|
-
navigator: () => ({
|
|
124
|
-
userAgent: 'Mozilla/5.0 (turbo-dom) AppleWebKit/537.36',
|
|
125
|
-
platform: 'turbo-dom', vendor: '', language: 'en-US', languages: ['en-US'],
|
|
126
|
-
onLine: true, cookieEnabled: true, doNotTrack: null, maxTouchPoints: 0,
|
|
127
|
-
hardwareConcurrency: 4, deviceMemory: 8, webdriver: false,
|
|
128
|
-
clipboard: { readText: async () => '', writeText: async () => {}, read: async () => [], write: async () => {} },
|
|
129
|
-
permissions: { query: async () => ({ state: 'prompt', addEventListener() {}, removeEventListener() {} }) },
|
|
130
|
-
sendBeacon: () => true, vibrate: () => false,
|
|
131
|
-
}),
|
|
132
|
-
performance: () => ({ now: performanceNow, timeOrigin: 0, mark() {}, measure() {}, getEntriesByName: () => [], getEntriesByType: () => [], clearMarks() {}, clearMeasures() {} }),
|
|
133
|
-
Storage: () => Storage,
|
|
134
|
-
devicePixelRatio: () => 1,
|
|
135
|
-
innerWidth: () => 1024,
|
|
136
|
-
innerHeight: () => 768,
|
|
137
|
-
outerWidth: () => 1024,
|
|
138
|
-
outerHeight: () => 768,
|
|
139
|
-
scrollX: () => 0, scrollY: () => 0, pageXOffset: () => 0, pageYOffset: () => 0,
|
|
140
|
-
screenX: () => 0, screenY: () => 0, screenLeft: () => 0, screenTop: () => 0,
|
|
141
|
-
screen: () => ({ width: 1024, height: 768, availWidth: 1024, availHeight: 768, colorDepth: 24, pixelDepth: 24, orientation: { type: 'landscape-primary', angle: 0, addEventListener() {}, removeEventListener() {} } }),
|
|
142
|
-
visualViewport: () => ({ width: 1024, height: 768, scale: 1, offsetLeft: 0, offsetTop: 0, pageLeft: 0, pageTop: 0, addEventListener() {}, removeEventListener() {} }),
|
|
143
152
|
};
|
|
144
153
|
|
|
145
154
|
windowProxy = new Proxy(base, {
|
|
@@ -147,7 +156,7 @@ export function createWindow(document, { url = 'http://localhost/' } = {}) {
|
|
|
147
156
|
if (k === 'window' || k === 'self' || k === 'globalThis' || k === 'parent' || k === 'top') return windowProxy;
|
|
148
157
|
if (k in t) return t[k]; // per-env (incl. overrides + materialized lazy)
|
|
149
158
|
if (k in STATIC_BASE) return STATIC_BASE[k]; // shared stateless globals
|
|
150
|
-
const factory = lazy[k];
|
|
159
|
+
const factory = lazy[k] || SHARED_LAZY[k];
|
|
151
160
|
if (factory) {
|
|
152
161
|
touched.add(k);
|
|
153
162
|
const v = factory();
|
|
@@ -158,7 +167,7 @@ export function createWindow(document, { url = 'http://localhost/' } = {}) {
|
|
|
158
167
|
},
|
|
159
168
|
set(t, k, v) { t[k] = v; return true; }, // writes to base → shadows STATIC_BASE per-env
|
|
160
169
|
has(t, k) {
|
|
161
|
-
return k in t || k in STATIC_BASE || k in lazy ||
|
|
170
|
+
return k in t || k in STATIC_BASE || k in lazy || k in SHARED_LAZY ||
|
|
162
171
|
k === 'window' || k === 'self' || k === 'globalThis' || k === 'parent' || k === 'top';
|
|
163
172
|
},
|
|
164
173
|
// so vi.spyOn(window, 'scrollTo'/'open'/…) finds STATIC_BASE methods as own
|
|
@@ -178,7 +187,7 @@ export function createWindow(document, { url = 'http://localhost/' } = {}) {
|
|
|
178
187
|
// which lazy globals this test materialized (the "DOM surface used" report)
|
|
179
188
|
touched: () => [...touched],
|
|
180
189
|
// every global name this window can provide (for environment adapters)
|
|
181
|
-
globalKeys: [...Object.keys(base), ...Object.keys(STATIC_BASE), ...Object.keys(lazy)],
|
|
190
|
+
globalKeys: [...Object.keys(base), ...Object.keys(STATIC_BASE), ...Object.keys(lazy), ...Object.keys(SHARED_LAZY)],
|
|
182
191
|
// Layer 5: drop materialized global slots, keep the class machinery warm.
|
|
183
192
|
resetGlobals() {
|
|
184
193
|
for (const k of touched) delete base[k];
|
|
Binary file
|