@absolutejs/absolute 0.19.0-beta.705 → 0.19.0-beta.706
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/dist/angular/browser.js +6 -4
- package/dist/angular/browser.js.map +3 -3
- package/dist/angular/index.js +19 -10
- package/dist/angular/index.js.map +3 -3
- package/dist/angular/server.js +19 -10
- package/dist/angular/server.js.map +3 -3
- package/dist/build.js +145 -78
- package/dist/build.js.map +10 -10
- package/dist/cli/index.js +18 -11
- package/dist/client/index.js +4 -2
- package/dist/client/index.js.map +2 -2
- package/dist/index.js +166 -90
- package/dist/index.js.map +12 -12
- package/dist/islands/index.js +5 -3
- package/dist/islands/index.js.map +2 -2
- package/dist/react/browser.js +7 -7
- package/dist/react/browser.js.map +2 -2
- package/dist/react/components/browser/index.js +101 -101
- package/dist/react/components/index.js +104 -104
- package/dist/react/components/index.js.map +2 -2
- package/dist/react/index.js +29 -20
- package/dist/react/index.js.map +3 -3
- package/dist/react/server.js +15 -8
- package/dist/react/server.js.map +3 -3
- package/dist/src/angular/components/defer-slot-templates.directive.d.ts +0 -7
- package/dist/src/angular/components/defer-slot.component.d.ts +2 -5
- package/dist/src/angular/components/image.component.d.ts +2 -5
- package/dist/src/angular/components/index.d.ts +4 -4
- package/dist/src/angular/components/stream-slot.component.d.ts +0 -3
- package/dist/src/build/buildAngularVendor.d.ts +3 -4
- package/dist/src/utils/imageProcessing.d.ts +1 -1
- package/dist/src/vue/components/Image.d.ts +1 -1
- package/dist/svelte/index.js +19 -10
- package/dist/svelte/index.js.map +3 -3
- package/dist/svelte/server.js +16 -9
- package/dist/svelte/server.js.map +3 -3
- package/dist/vue/index.js +19 -10
- package/dist/vue/index.js.map +3 -3
- package/dist/vue/server.js +15 -8
- package/dist/vue/server.js.map +3 -3
- package/package.json +1 -1
- package/dist/angular/components/constants.js +0 -77
- package/dist/angular/components/core/streamingSlotRegistrar.js +0 -58
- package/dist/angular/components/core/streamingSlotRegistry.js +0 -114
- package/dist/angular/components/defer-slot-payload.js +0 -6
- package/dist/angular/components/defer-slot-templates.directive.js +0 -44
- package/dist/angular/components/defer-slot.component.js +0 -149
- package/dist/angular/components/image.component.js +0 -202
- package/dist/angular/components/index.js +0 -4
- package/dist/angular/components/stream-slot.component.js +0 -103
- package/dist/dev/client/constants.ts +0 -26
- package/dist/dev/client/cssUtils.ts +0 -307
- package/dist/dev/client/domDiff.ts +0 -226
- package/dist/dev/client/domState.ts +0 -421
- package/dist/dev/client/domTracker.ts +0 -61
- package/dist/dev/client/errorOverlay.ts +0 -184
- package/dist/dev/client/frameworkDetect.ts +0 -63
- package/dist/dev/client/handlers/angular.ts +0 -551
- package/dist/dev/client/handlers/angularRuntime.ts +0 -206
- package/dist/dev/client/handlers/html.ts +0 -363
- package/dist/dev/client/handlers/htmx.ts +0 -272
- package/dist/dev/client/handlers/react.ts +0 -108
- package/dist/dev/client/handlers/rebuild.ts +0 -153
- package/dist/dev/client/handlers/svelte.ts +0 -332
- package/dist/dev/client/handlers/vue.ts +0 -292
- package/dist/dev/client/headPatch.ts +0 -233
- package/dist/dev/client/hmrClient.ts +0 -251
- package/dist/dev/client/hmrState.ts +0 -14
- package/dist/dev/client/moduleVersions.ts +0 -62
- package/dist/dev/client/reactRefreshSetup.ts +0 -33
- package/dist/src/angular/components/constants.d.ts +0 -74
- package/dist/svelte/components/AwaitSlot.svelte +0 -39
- package/dist/svelte/components/AwaitSlot.svelte.d.ts +0 -2
- package/dist/svelte/components/Head.svelte +0 -144
- package/dist/svelte/components/Head.svelte.d.ts +0 -2
- package/dist/svelte/components/Image.svelte +0 -164
- package/dist/svelte/components/Image.svelte.d.ts +0 -5
- package/dist/svelte/components/Island.svelte +0 -71
- package/dist/svelte/components/Island.svelte.d.ts +0 -5
- package/dist/svelte/components/JsonLd.svelte +0 -21
- package/dist/svelte/components/JsonLd.svelte.d.ts +0 -2
- package/dist/svelte/components/StreamSlot.svelte +0 -41
- package/dist/svelte/components/StreamSlot.svelte.d.ts +0 -2
- package/dist/types/globals.d.ts +0 -121
|
@@ -1,421 +0,0 @@
|
|
|
1
|
-
import type {} from '../../types/globals';
|
|
2
|
-
/* DOM state snapshot/restore to preserve user-visible state across HMR */
|
|
3
|
-
|
|
4
|
-
import type { DOMStateEntry, DOMStateSnapshot } from '../../types/client';
|
|
5
|
-
import {
|
|
6
|
-
FOCUS_ID_PREFIX_LENGTH,
|
|
7
|
-
FOCUS_IDX_PREFIX_LENGTH,
|
|
8
|
-
FOCUS_NAME_PREFIX_LENGTH,
|
|
9
|
-
UNFOUND_INDEX
|
|
10
|
-
} from './constants';
|
|
11
|
-
|
|
12
|
-
const trySetSelectionRange = (
|
|
13
|
-
element: HTMLInputElement | HTMLTextAreaElement,
|
|
14
|
-
start: number,
|
|
15
|
-
end: number
|
|
16
|
-
) => {
|
|
17
|
-
try {
|
|
18
|
-
element.setSelectionRange(start, end);
|
|
19
|
-
} catch {
|
|
20
|
-
/* ignore */
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const restoreSelectionRange = (
|
|
25
|
-
element: HTMLInputElement | HTMLTextAreaElement,
|
|
26
|
-
entry: DOMStateEntry
|
|
27
|
-
) => {
|
|
28
|
-
if (
|
|
29
|
-
entry.selStart === undefined ||
|
|
30
|
-
entry.selEnd === undefined ||
|
|
31
|
-
!element.setSelectionRange
|
|
32
|
-
)
|
|
33
|
-
return;
|
|
34
|
-
trySetSelectionRange(element, entry.selStart, entry.selEnd);
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const restoreInputEntry = (target: Element, entry: DOMStateEntry) => {
|
|
38
|
-
if (!(target instanceof HTMLInputElement)) return;
|
|
39
|
-
const input = target;
|
|
40
|
-
const type = entry.type || input.getAttribute('type') || 'text';
|
|
41
|
-
if (type === 'checkbox' || type === 'radio') {
|
|
42
|
-
if (entry.checked !== undefined) input.checked = entry.checked;
|
|
43
|
-
} else if (entry.value !== undefined) {
|
|
44
|
-
input.value = entry.value;
|
|
45
|
-
}
|
|
46
|
-
restoreSelectionRange(input, entry);
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const restoreTextareaEntry = (target: Element, entry: DOMStateEntry) => {
|
|
50
|
-
if (!(target instanceof HTMLTextAreaElement)) return;
|
|
51
|
-
const textarea = target;
|
|
52
|
-
if (entry.value !== undefined) textarea.value = entry.value;
|
|
53
|
-
restoreSelectionRange(textarea, entry);
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const restoreSelectEntry = (target: Element, entry: DOMStateEntry) => {
|
|
57
|
-
if (!Array.isArray(entry.values)) return;
|
|
58
|
-
if (!(target instanceof HTMLSelectElement)) return;
|
|
59
|
-
const select = target;
|
|
60
|
-
const { values } = entry;
|
|
61
|
-
Array.from(select.options).forEach((opt) => {
|
|
62
|
-
opt.selected = values.indexOf(opt.value) !== UNFOUND_INDEX;
|
|
63
|
-
});
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
const restoreEntry = (target: Element, entry: DOMStateEntry) => {
|
|
67
|
-
if (target.tagName === 'INPUT') {
|
|
68
|
-
restoreInputEntry(target, entry);
|
|
69
|
-
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
if (target.tagName === 'TEXTAREA') {
|
|
73
|
-
restoreTextareaEntry(target, entry);
|
|
74
|
-
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
if (target.tagName === 'SELECT') {
|
|
78
|
-
restoreSelectEntry(target, entry);
|
|
79
|
-
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
if (target.tagName === 'OPTION') {
|
|
83
|
-
if (entry.selected !== undefined && target instanceof HTMLOptionElement)
|
|
84
|
-
target.selected = entry.selected;
|
|
85
|
-
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
if (target.tagName === 'DETAILS') {
|
|
89
|
-
if (entry.open !== undefined && target instanceof HTMLDetailsElement)
|
|
90
|
-
target.open = entry.open;
|
|
91
|
-
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
if (target.getAttribute('contenteditable') === 'true') {
|
|
95
|
-
if (entry.text !== undefined) target.textContent = entry.text;
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
const findEntryTarget = (
|
|
100
|
-
root: HTMLElement,
|
|
101
|
-
elements: NodeListOf<Element>,
|
|
102
|
-
entry: DOMStateEntry
|
|
103
|
-
) => {
|
|
104
|
-
if (entry.id) return root.querySelector(`#${CSS.escape(entry.id)}`);
|
|
105
|
-
if (entry.name)
|
|
106
|
-
return root.querySelector(`[name="${CSS.escape(entry.name)}"]`);
|
|
107
|
-
if (elements[entry.idx]) return elements[entry.idx] ?? null;
|
|
108
|
-
|
|
109
|
-
return null;
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
const resolveFocusElement = (
|
|
113
|
-
root: HTMLElement,
|
|
114
|
-
elements: NodeListOf<Element>,
|
|
115
|
-
activeKey: string
|
|
116
|
-
) => {
|
|
117
|
-
if (activeKey.startsWith('id:'))
|
|
118
|
-
return root.querySelector(
|
|
119
|
-
`#${CSS.escape(activeKey.slice(FOCUS_ID_PREFIX_LENGTH))}`
|
|
120
|
-
);
|
|
121
|
-
if (activeKey.startsWith('name:'))
|
|
122
|
-
return root.querySelector(
|
|
123
|
-
`[name="${CSS.escape(activeKey.slice(FOCUS_NAME_PREFIX_LENGTH))}"]`
|
|
124
|
-
);
|
|
125
|
-
if (!activeKey.startsWith('idx:')) return null;
|
|
126
|
-
const idx = parseInt(activeKey.slice(FOCUS_IDX_PREFIX_LENGTH), 10);
|
|
127
|
-
if (isNaN(idx) || !elements[idx]) return null;
|
|
128
|
-
|
|
129
|
-
return elements[idx];
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
export const restoreDOMState = (
|
|
133
|
-
root: HTMLElement,
|
|
134
|
-
snapshot: DOMStateSnapshot
|
|
135
|
-
) => {
|
|
136
|
-
if (!snapshot || !snapshot.items) return;
|
|
137
|
-
const selector =
|
|
138
|
-
'input, textarea, select, option, [contenteditable="true"], details';
|
|
139
|
-
const elements = root.querySelectorAll(selector);
|
|
140
|
-
|
|
141
|
-
snapshot.items.forEach((entry) => {
|
|
142
|
-
const target = findEntryTarget(root, elements, entry);
|
|
143
|
-
if (!target) return;
|
|
144
|
-
restoreEntry(target, entry);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
if (!snapshot.activeKey) return;
|
|
148
|
-
const focusEl = resolveFocusElement(root, elements, snapshot.activeKey);
|
|
149
|
-
if (focusEl instanceof HTMLElement) {
|
|
150
|
-
focusEl.focus();
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
const resolveFormElement = (
|
|
155
|
-
isStandalone: boolean,
|
|
156
|
-
form: Element | null,
|
|
157
|
-
name: string
|
|
158
|
-
) => {
|
|
159
|
-
if (isStandalone) {
|
|
160
|
-
const element: HTMLInputElement | null = document.querySelector(
|
|
161
|
-
`input[name="${name}"], textarea[name="${name}"], select[name="${name}"]`
|
|
162
|
-
);
|
|
163
|
-
if (element) return element;
|
|
164
|
-
|
|
165
|
-
const byId = document.getElementById(name);
|
|
166
|
-
if (byId instanceof HTMLInputElement) return byId;
|
|
167
|
-
|
|
168
|
-
return null;
|
|
169
|
-
}
|
|
170
|
-
if (!form) return null;
|
|
171
|
-
|
|
172
|
-
const found = form.querySelector(`[name="${name}"], #${name}`);
|
|
173
|
-
if (
|
|
174
|
-
found instanceof HTMLInputElement ||
|
|
175
|
-
found instanceof HTMLTextAreaElement ||
|
|
176
|
-
found instanceof HTMLSelectElement
|
|
177
|
-
)
|
|
178
|
-
return found;
|
|
179
|
-
|
|
180
|
-
return null;
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
const applyFormValue = (
|
|
184
|
-
element: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,
|
|
185
|
-
value: boolean | string
|
|
186
|
-
) => {
|
|
187
|
-
if (
|
|
188
|
-
element instanceof HTMLInputElement &&
|
|
189
|
-
(element.type === 'checkbox' || element.type === 'radio')
|
|
190
|
-
) {
|
|
191
|
-
element.checked = value === true;
|
|
192
|
-
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
element.value = String(value);
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
const resolveForm = (formId: string) => {
|
|
199
|
-
const formIndex = parseInt(formId.replace('form-', ''));
|
|
200
|
-
const form = document.getElementById(formId);
|
|
201
|
-
if (form) return form;
|
|
202
|
-
if (isNaN(formIndex)) return null;
|
|
203
|
-
try {
|
|
204
|
-
return document.querySelector(`form:nth-of-type(${formIndex + 1})`);
|
|
205
|
-
} catch {
|
|
206
|
-
return null;
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
const restoreRadioGroup = (
|
|
211
|
-
isStandalone: boolean,
|
|
212
|
-
form: Element | null,
|
|
213
|
-
groupName: string,
|
|
214
|
-
selectedValue: string
|
|
215
|
-
) => {
|
|
216
|
-
const scope = isStandalone ? document : form;
|
|
217
|
-
if (!scope) return;
|
|
218
|
-
|
|
219
|
-
const escapedName = CSS.escape(groupName);
|
|
220
|
-
const escapedValue = CSS.escape(selectedValue);
|
|
221
|
-
const radio = scope.querySelector<HTMLInputElement>(
|
|
222
|
-
`input[type="radio"][name="${escapedName}"][value="${escapedValue}"]`
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
if (radio) {
|
|
226
|
-
radio.checked = true;
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
const RADIO_PREFIX = '__radio__';
|
|
231
|
-
|
|
232
|
-
export const restoreFormState = (
|
|
233
|
-
formState: Record<string, Record<string, boolean | string>>
|
|
234
|
-
) => {
|
|
235
|
-
Object.keys(formState).forEach((formId) => {
|
|
236
|
-
const isStandalone = formId === '__standalone__';
|
|
237
|
-
const form = isStandalone ? null : resolveForm(formId);
|
|
238
|
-
const formData = formState[formId];
|
|
239
|
-
if (!formData) return;
|
|
240
|
-
Object.keys(formData).forEach((name) => {
|
|
241
|
-
if (name.startsWith(RADIO_PREFIX)) {
|
|
242
|
-
const groupName = name.slice(RADIO_PREFIX.length);
|
|
243
|
-
const value = formData[name];
|
|
244
|
-
if (value === undefined) return;
|
|
245
|
-
restoreRadioGroup(isStandalone, form, groupName, String(value));
|
|
246
|
-
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
const element = resolveFormElement(isStandalone, form, name);
|
|
250
|
-
if (!element) return;
|
|
251
|
-
const value = formData[name];
|
|
252
|
-
if (value === undefined) return;
|
|
253
|
-
applyFormValue(element, value);
|
|
254
|
-
});
|
|
255
|
-
});
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
export const restoreScrollState = (scrollState: {
|
|
259
|
-
window: { x: number; y: number };
|
|
260
|
-
}) => {
|
|
261
|
-
if (scrollState && scrollState.window) {
|
|
262
|
-
window.scrollTo(scrollState.window.x, scrollState.window.y);
|
|
263
|
-
}
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
const saveInputEntry = (elem: Element, entry: DOMStateEntry) => {
|
|
267
|
-
if (!(elem instanceof HTMLInputElement)) return;
|
|
268
|
-
const input = elem;
|
|
269
|
-
const type = input.getAttribute('type') || 'text';
|
|
270
|
-
entry.type = type;
|
|
271
|
-
if (type === 'checkbox' || type === 'radio') {
|
|
272
|
-
entry.checked = input.checked;
|
|
273
|
-
} else {
|
|
274
|
-
entry.value = input.value;
|
|
275
|
-
}
|
|
276
|
-
if (input.selectionStart !== null && input.selectionEnd !== null) {
|
|
277
|
-
entry.selStart = input.selectionStart;
|
|
278
|
-
entry.selEnd = input.selectionEnd;
|
|
279
|
-
}
|
|
280
|
-
};
|
|
281
|
-
|
|
282
|
-
const saveTextareaEntry = (elem: Element, entry: DOMStateEntry) => {
|
|
283
|
-
if (!(elem instanceof HTMLTextAreaElement)) return;
|
|
284
|
-
const textarea = elem;
|
|
285
|
-
entry.value = textarea.value;
|
|
286
|
-
if (textarea.selectionStart !== null && textarea.selectionEnd !== null) {
|
|
287
|
-
entry.selStart = textarea.selectionStart;
|
|
288
|
-
entry.selEnd = textarea.selectionEnd;
|
|
289
|
-
}
|
|
290
|
-
};
|
|
291
|
-
|
|
292
|
-
const saveSelectEntry = (elem: Element, entry: DOMStateEntry) => {
|
|
293
|
-
if (!(elem instanceof HTMLSelectElement)) return;
|
|
294
|
-
const select = elem;
|
|
295
|
-
const vals: string[] = [];
|
|
296
|
-
Array.from(select.options).forEach((opt) => {
|
|
297
|
-
if (opt.selected) vals.push(opt.value);
|
|
298
|
-
});
|
|
299
|
-
entry.values = vals;
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
const saveElementEntry = (elem: Element, entry: DOMStateEntry) => {
|
|
303
|
-
if (elem.tagName === 'INPUT') {
|
|
304
|
-
saveInputEntry(elem, entry);
|
|
305
|
-
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
|
-
if (elem.tagName === 'TEXTAREA') {
|
|
309
|
-
saveTextareaEntry(elem, entry);
|
|
310
|
-
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
if (elem.tagName === 'SELECT') {
|
|
314
|
-
saveSelectEntry(elem, entry);
|
|
315
|
-
|
|
316
|
-
return;
|
|
317
|
-
}
|
|
318
|
-
if (elem.tagName === 'OPTION') {
|
|
319
|
-
if (elem instanceof HTMLOptionElement) entry.selected = elem.selected;
|
|
320
|
-
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
if (elem.tagName === 'DETAILS') {
|
|
324
|
-
if (elem instanceof HTMLDetailsElement) entry.open = elem.open;
|
|
325
|
-
|
|
326
|
-
return;
|
|
327
|
-
}
|
|
328
|
-
if (elem.getAttribute('contenteditable') === 'true') {
|
|
329
|
-
entry.text = elem.textContent || undefined;
|
|
330
|
-
}
|
|
331
|
-
};
|
|
332
|
-
|
|
333
|
-
export const saveDOMState = (root: HTMLElement) => {
|
|
334
|
-
const snapshot: DOMStateSnapshot = { activeKey: null, items: [] };
|
|
335
|
-
const selector =
|
|
336
|
-
'input, textarea, select, option, [contenteditable="true"], details';
|
|
337
|
-
const elements = root.querySelectorAll(selector);
|
|
338
|
-
|
|
339
|
-
elements.forEach((el, idx) => {
|
|
340
|
-
const entry: DOMStateEntry = {
|
|
341
|
-
idx,
|
|
342
|
-
tag: el.tagName.toLowerCase()
|
|
343
|
-
};
|
|
344
|
-
const id = el.getAttribute('id');
|
|
345
|
-
const name = el.getAttribute('name');
|
|
346
|
-
if (id) entry.id = id;
|
|
347
|
-
else if (name) entry.name = name;
|
|
348
|
-
saveElementEntry(el, entry);
|
|
349
|
-
snapshot.items.push(entry);
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
const active = document.activeElement;
|
|
353
|
-
if (!active || !root.contains(active)) return snapshot;
|
|
354
|
-
const id = active.getAttribute('id');
|
|
355
|
-
const name = active.getAttribute('name');
|
|
356
|
-
if (id) snapshot.activeKey = `id:${id}`;
|
|
357
|
-
else if (name) snapshot.activeKey = `name:${name}`;
|
|
358
|
-
else
|
|
359
|
-
snapshot.activeKey = `idx:${Array.prototype.indexOf.call(elements, active)}`;
|
|
360
|
-
|
|
361
|
-
return snapshot;
|
|
362
|
-
};
|
|
363
|
-
|
|
364
|
-
const collectInputState = (
|
|
365
|
-
element: HTMLInputElement,
|
|
366
|
-
name: string,
|
|
367
|
-
target: Record<string, boolean | string>
|
|
368
|
-
) => {
|
|
369
|
-
if (element.type === 'radio') {
|
|
370
|
-
if (element.checked) target[`__radio__${name}`] = element.value;
|
|
371
|
-
|
|
372
|
-
return;
|
|
373
|
-
}
|
|
374
|
-
if (element.type === 'checkbox') {
|
|
375
|
-
target[name] = element.checked;
|
|
376
|
-
|
|
377
|
-
return;
|
|
378
|
-
}
|
|
379
|
-
target[name] = element.value;
|
|
380
|
-
};
|
|
381
|
-
|
|
382
|
-
export const saveFormState = () => {
|
|
383
|
-
const formState: Record<string, Record<string, boolean | string>> = {};
|
|
384
|
-
const forms = document.querySelectorAll('form');
|
|
385
|
-
forms.forEach((form, formIndex) => {
|
|
386
|
-
const formId = form.id || `form-${formIndex}`;
|
|
387
|
-
const formData: Record<string, boolean | string> = {};
|
|
388
|
-
formState[formId] = formData;
|
|
389
|
-
const inputs = form.querySelectorAll('input, textarea, select');
|
|
390
|
-
inputs.forEach((input) => {
|
|
391
|
-
if (!(input instanceof HTMLInputElement)) return;
|
|
392
|
-
const name =
|
|
393
|
-
input.name || input.id || `input-${formIndex}-${inputs.length}`;
|
|
394
|
-
collectInputState(input, name, formData);
|
|
395
|
-
});
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
const standaloneInputs = document.querySelectorAll(
|
|
399
|
-
'input:not(form input), textarea:not(form textarea), select:not(form select)'
|
|
400
|
-
);
|
|
401
|
-
if (standaloneInputs.length <= 0) return formState;
|
|
402
|
-
const standaloneData: Record<string, boolean | string> = {};
|
|
403
|
-
formState['__standalone__'] = standaloneData;
|
|
404
|
-
standaloneInputs.forEach((input) => {
|
|
405
|
-
if (!(input instanceof HTMLInputElement)) return;
|
|
406
|
-
const name =
|
|
407
|
-
input.name || input.id || `standalone-${standaloneInputs.length}`;
|
|
408
|
-
collectInputState(input, name, standaloneData);
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
return formState;
|
|
412
|
-
};
|
|
413
|
-
|
|
414
|
-
export const saveScrollState = () => {
|
|
415
|
-
const scrollX = window.scrollX || window.pageXOffset;
|
|
416
|
-
const scrollY = window.scrollY || window.pageYOffset;
|
|
417
|
-
|
|
418
|
-
return {
|
|
419
|
-
window: { x: scrollX, y: scrollY }
|
|
420
|
-
};
|
|
421
|
-
};
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import type {} from '../../types/globals';
|
|
2
|
-
/* Snapshot/restore for JS-modified DOM state across HMR updates.
|
|
3
|
-
* Before patching, captures text and dynamic children of elements with IDs.
|
|
4
|
-
* After patching, restores values that were changed by user scripts. */
|
|
5
|
-
|
|
6
|
-
type DOMSnapshot = {
|
|
7
|
-
children: Map<string, string>;
|
|
8
|
-
text: Map<string, string>;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export const restoreDOMChanges = (
|
|
12
|
-
root: HTMLElement,
|
|
13
|
-
snapshot: DOMSnapshot,
|
|
14
|
-
newHTML: string
|
|
15
|
-
) => {
|
|
16
|
-
const tempDiv = document.createElement('div');
|
|
17
|
-
tempDiv.innerHTML = newHTML;
|
|
18
|
-
|
|
19
|
-
/* Restore JS-modified text on leaf elements */
|
|
20
|
-
snapshot.text.forEach((liveText, elId) => {
|
|
21
|
-
const newEl = tempDiv.querySelector(`#${CSS.escape(elId)}`);
|
|
22
|
-
const newText = newEl ? newEl.textContent || '' : '';
|
|
23
|
-
if (liveText === newText) return;
|
|
24
|
-
|
|
25
|
-
const liveEl = root.querySelector(`#${CSS.escape(elId)}`);
|
|
26
|
-
if (liveEl) {
|
|
27
|
-
liveEl.textContent = liveText;
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
/* Restore JS-added children (e.g. dynamically appended list items) */
|
|
32
|
-
snapshot.children.forEach((liveHTML, elId) => {
|
|
33
|
-
const newEl = tempDiv.querySelector(`#${CSS.escape(elId)}`);
|
|
34
|
-
const newInner = newEl ? newEl.innerHTML : '';
|
|
35
|
-
if (liveHTML === newInner || liveHTML.length <= newInner.length) return;
|
|
36
|
-
|
|
37
|
-
const liveEl = root.querySelector(`#${CSS.escape(elId)}`);
|
|
38
|
-
if (liveEl) {
|
|
39
|
-
liveEl.innerHTML = liveHTML;
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
};
|
|
43
|
-
export const snapshotDOMChanges = (root: HTMLElement): DOMSnapshot => {
|
|
44
|
-
const text = new Map<string, string>();
|
|
45
|
-
const children = new Map<string, string>();
|
|
46
|
-
|
|
47
|
-
root.querySelectorAll('[id]').forEach((elem) => {
|
|
48
|
-
const { childNodes } = elem;
|
|
49
|
-
const isTextLeaf = Array.from(childNodes).every(
|
|
50
|
-
(child) => child.nodeType === Node.TEXT_NODE
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
if (isTextLeaf && childNodes.length > 0) {
|
|
54
|
-
text.set(elem.id, elem.textContent || '');
|
|
55
|
-
} else if (elem.children.length > 0) {
|
|
56
|
-
children.set(elem.id, elem.innerHTML);
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
return { children, text };
|
|
61
|
-
};
|
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
import type {} from '../../types/globals';
|
|
2
|
-
/* AbsoluteJS Error Overlay - branded, per-framework, modern styling */
|
|
3
|
-
|
|
4
|
-
import type { ErrorOverlayOptions } from '../../types/client';
|
|
5
|
-
import { OVERLAY_FADE_DURATION_MS } from './constants';
|
|
6
|
-
|
|
7
|
-
let errorOverlayElement: HTMLDivElement | null = null;
|
|
8
|
-
let currentOverlayKind: 'compilation' | 'runtime' | null = null;
|
|
9
|
-
|
|
10
|
-
const frameworkLabels: Record<string, string> = {
|
|
11
|
-
angular: 'Angular',
|
|
12
|
-
assets: 'Assets',
|
|
13
|
-
html: 'HTML',
|
|
14
|
-
htmx: 'HTMX',
|
|
15
|
-
react: 'React',
|
|
16
|
-
svelte: 'Svelte',
|
|
17
|
-
unknown: 'Unknown',
|
|
18
|
-
vue: 'Vue'
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const frameworkColors: Record<string, string> = {
|
|
22
|
-
angular: '#dd0031',
|
|
23
|
-
assets: '#563d7c',
|
|
24
|
-
html: '#e34c26',
|
|
25
|
-
htmx: '#1a365d',
|
|
26
|
-
react: '#61dafb',
|
|
27
|
-
svelte: '#ff3e00',
|
|
28
|
-
unknown: '#94a3b8',
|
|
29
|
-
vue: '#42b883'
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const removeOverlayElement = () => {
|
|
33
|
-
if (errorOverlayElement && errorOverlayElement.parentNode) {
|
|
34
|
-
errorOverlayElement.parentNode.removeChild(errorOverlayElement);
|
|
35
|
-
}
|
|
36
|
-
errorOverlayElement = null;
|
|
37
|
-
currentOverlayKind = null;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
export const hideErrorOverlay = () => {
|
|
41
|
-
const elm = errorOverlayElement;
|
|
42
|
-
if (!elm || !elm.parentNode) {
|
|
43
|
-
removeOverlayElement();
|
|
44
|
-
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
elm.style.transition = 'opacity 150ms ease-out';
|
|
48
|
-
elm.style.opacity = '0';
|
|
49
|
-
errorOverlayElement = null;
|
|
50
|
-
currentOverlayKind = null;
|
|
51
|
-
setTimeout(() => {
|
|
52
|
-
if (elm.parentNode) elm.parentNode.removeChild(elm);
|
|
53
|
-
}, OVERLAY_FADE_DURATION_MS);
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
export const isRuntimeErrorOverlay = () => currentOverlayKind === 'runtime';
|
|
57
|
-
|
|
58
|
-
const buildLocationSection = (
|
|
59
|
-
file: string | undefined,
|
|
60
|
-
line: number | undefined,
|
|
61
|
-
column: number | undefined,
|
|
62
|
-
lineText: string | undefined
|
|
63
|
-
) => {
|
|
64
|
-
if (!file && line === undefined && column === undefined && !lineText) {
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const locSection = document.createElement('div');
|
|
69
|
-
locSection.style.cssText = 'margin-bottom:20px;';
|
|
70
|
-
|
|
71
|
-
const locLabel = document.createElement('div');
|
|
72
|
-
locLabel.style.cssText =
|
|
73
|
-
'font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.08em;color:#94a3b8;margin-bottom:8px;';
|
|
74
|
-
locLabel.textContent = 'Where';
|
|
75
|
-
locSection.appendChild(locLabel);
|
|
76
|
-
|
|
77
|
-
const locParts: string[] = [];
|
|
78
|
-
if (file) locParts.push(file);
|
|
79
|
-
if (line !== undefined) locParts.push(String(line));
|
|
80
|
-
if (column !== undefined) locParts.push(String(column));
|
|
81
|
-
const loc = locParts.join(':') || 'Unknown location';
|
|
82
|
-
|
|
83
|
-
const locEl = document.createElement('div');
|
|
84
|
-
locEl.style.cssText =
|
|
85
|
-
'padding:12px 20px;background:rgba(71,85,105,0.3);border-radius:10px;border:1px solid rgba(71,85,105,0.4);color:#cbd5e1;font-size:13px;';
|
|
86
|
-
locEl.textContent = loc;
|
|
87
|
-
locSection.appendChild(locEl);
|
|
88
|
-
|
|
89
|
-
if (lineText) {
|
|
90
|
-
const codeBlock = document.createElement('pre');
|
|
91
|
-
codeBlock.style.cssText =
|
|
92
|
-
'margin:8px 0 0;padding:14px 20px;background:rgba(15,23,42,0.8);border-radius:10px;border:1px solid rgba(71,85,105,0.4);color:#94a3b8;font-size:13px;overflow-x:auto;white-space:pre;';
|
|
93
|
-
codeBlock.textContent = lineText;
|
|
94
|
-
locSection.appendChild(codeBlock);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return locSection;
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
export const showErrorOverlay = (opts: ErrorOverlayOptions) => {
|
|
101
|
-
const message = opts.message || 'Build failed';
|
|
102
|
-
const { file } = opts;
|
|
103
|
-
const { line } = opts;
|
|
104
|
-
const { column } = opts;
|
|
105
|
-
const { lineText } = opts;
|
|
106
|
-
const framework = (opts.framework || 'unknown').toLowerCase();
|
|
107
|
-
const frameworkLabel = frameworkLabels[framework] || framework;
|
|
108
|
-
const accent = frameworkColors[framework] || '#94a3b8';
|
|
109
|
-
|
|
110
|
-
removeOverlayElement();
|
|
111
|
-
currentOverlayKind = opts.kind || 'compilation';
|
|
112
|
-
|
|
113
|
-
const overlay = document.createElement('div');
|
|
114
|
-
overlay.id = 'absolutejs-error-overlay';
|
|
115
|
-
overlay.setAttribute('data-hmr-overlay', 'true');
|
|
116
|
-
overlay.style.cssText =
|
|
117
|
-
'position:fixed;inset:0;z-index:2147483647;background:linear-gradient(135deg,rgba(15,23,42,0.98) 0%,rgba(30,41,59,0.98) 100%);backdrop-filter:blur(12px);color:#e2e8f0;font-family:"JetBrains Mono","Fira Code",ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:14px;line-height:1.6;overflow:auto;padding:32px;box-sizing:border-box;display:flex;align-items:flex-start;justify-content:center;';
|
|
118
|
-
|
|
119
|
-
const card = document.createElement('div');
|
|
120
|
-
card.style.cssText =
|
|
121
|
-
'max-width:720px;width:100%;background:rgba(30,41,59,0.6);border:1px solid rgba(71,85,105,0.5);border-radius:16px;box-shadow:0 25px 50px -12px rgba(0,0,0,0.5),0 0 0 1px rgba(255,255,255,0.05);overflow:hidden;';
|
|
122
|
-
|
|
123
|
-
const header = document.createElement('div');
|
|
124
|
-
header.style.cssText =
|
|
125
|
-
'display:flex;align-items:center;justify-content:space-between;gap:16px;padding:20px 24px;background:rgba(15,23,42,0.5);border-bottom:1px solid rgba(71,85,105,0.4);';
|
|
126
|
-
header.innerHTML = `<div style="display:flex;align-items:center;gap:12px;"><span style="font-weight:700;font-size:20px;color:#fff;letter-spacing:-0.02em;">AbsoluteJS</span><span style="padding:5px 10px;border-radius:8px;font-size:12px;font-weight:600;background:${
|
|
127
|
-
accent
|
|
128
|
-
};color:#fff;opacity:0.95;box-shadow:0 2px 4px rgba(0,0,0,0.2);">${
|
|
129
|
-
frameworkLabel
|
|
130
|
-
}</span></div><span style="color:#94a3b8;font-size:13px;font-weight:500;">${
|
|
131
|
-
opts.kind === 'runtime' ? 'Runtime Error' : 'Compilation Error'
|
|
132
|
-
}</span>`;
|
|
133
|
-
card.appendChild(header);
|
|
134
|
-
|
|
135
|
-
const content = document.createElement('div');
|
|
136
|
-
content.style.cssText = 'padding:24px;';
|
|
137
|
-
|
|
138
|
-
const errorSection = document.createElement('div');
|
|
139
|
-
errorSection.style.cssText = 'margin-bottom:20px;';
|
|
140
|
-
|
|
141
|
-
const errorLabel = document.createElement('div');
|
|
142
|
-
errorLabel.style.cssText =
|
|
143
|
-
'font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.08em;color:#94a3b8;margin-bottom:8px;';
|
|
144
|
-
errorLabel.textContent = 'What went wrong';
|
|
145
|
-
errorSection.appendChild(errorLabel);
|
|
146
|
-
|
|
147
|
-
const msgEl = document.createElement('pre');
|
|
148
|
-
msgEl.style.cssText =
|
|
149
|
-
'margin:0;padding:16px 20px;background:rgba(239,68,68,0.12);border:1px solid rgba(239,68,68,0.25);border-radius:10px;overflow-x:auto;white-space:pre-wrap;word-break:break-word;color:#fca5a5;font-size:13px;line-height:1.5;';
|
|
150
|
-
msgEl.textContent = message;
|
|
151
|
-
errorSection.appendChild(msgEl);
|
|
152
|
-
content.appendChild(errorSection);
|
|
153
|
-
|
|
154
|
-
const locSection = buildLocationSection(file, line, column, lineText);
|
|
155
|
-
if (locSection) {
|
|
156
|
-
content.appendChild(locSection);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const footer = document.createElement('div');
|
|
160
|
-
footer.style.cssText =
|
|
161
|
-
'display:flex;justify-content:flex-end;padding-top:8px;';
|
|
162
|
-
|
|
163
|
-
const dismiss = document.createElement('button');
|
|
164
|
-
dismiss.textContent = 'Dismiss';
|
|
165
|
-
dismiss.style.cssText = `padding:10px 20px;background:${
|
|
166
|
-
accent
|
|
167
|
-
};color:#fff;border:none;border-radius:10px;font-size:13px;font-weight:600;cursor:pointer;box-shadow:0 2px 8px rgba(0,0,0,0.2);transition:opacity 0.15s,transform 0.15s;`;
|
|
168
|
-
dismiss.onmouseover = function () {
|
|
169
|
-
dismiss.style.opacity = '0.9';
|
|
170
|
-
dismiss.style.transform = 'translateY(-1px)';
|
|
171
|
-
};
|
|
172
|
-
dismiss.onmouseout = function () {
|
|
173
|
-
dismiss.style.opacity = '1';
|
|
174
|
-
dismiss.style.transform = 'translateY(0)';
|
|
175
|
-
};
|
|
176
|
-
dismiss.onclick = hideErrorOverlay;
|
|
177
|
-
footer.appendChild(dismiss);
|
|
178
|
-
content.appendChild(footer);
|
|
179
|
-
card.appendChild(content);
|
|
180
|
-
overlay.appendChild(card);
|
|
181
|
-
if (!document.body) return;
|
|
182
|
-
document.body.appendChild(overlay);
|
|
183
|
-
errorOverlayElement = overlay;
|
|
184
|
-
};
|