@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,307 +0,0 @@
|
|
|
1
|
-
import type {} from '../../types/globals';
|
|
2
|
-
/* CSS reload/preload utilities for HMR */
|
|
3
|
-
|
|
4
|
-
import type { CSSUpdateResult } from '../../types/client';
|
|
5
|
-
import {
|
|
6
|
-
CSS_ERROR_RESOLVE_DELAY_MS,
|
|
7
|
-
CSS_MAX_CHECK_ATTEMPTS,
|
|
8
|
-
CSS_MAX_PARSE_TIMEOUT_MS,
|
|
9
|
-
CSS_SHEET_READY_TIMEOUT_MS,
|
|
10
|
-
DOM_UPDATE_DELAY_MS,
|
|
11
|
-
RAF_BATCH_COUNT
|
|
12
|
-
} from './constants';
|
|
13
|
-
import { hmrState } from './hmrState';
|
|
14
|
-
|
|
15
|
-
export const getCSSBaseName = (href: string) => {
|
|
16
|
-
const fileName = href.split('?')[0]?.split('/').pop() || '';
|
|
17
|
-
|
|
18
|
-
return fileName.split('.')[0] ?? '';
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const baseNamesMatch = (baseA: string, baseB: string) =>
|
|
22
|
-
baseA === baseB || baseA.includes(baseB) || baseB.includes(baseA);
|
|
23
|
-
|
|
24
|
-
const findMatchingLink = (baseNew: string) => {
|
|
25
|
-
const links = document.head.querySelectorAll('link[rel="stylesheet"]');
|
|
26
|
-
for (const existing of links) {
|
|
27
|
-
if (!(existing instanceof HTMLLinkElement)) continue;
|
|
28
|
-
const existingHref = existing.getAttribute('href') || '';
|
|
29
|
-
const baseExisting = getCSSBaseName(existingHref);
|
|
30
|
-
if (baseNamesMatch(baseExisting, baseNew)) {
|
|
31
|
-
return existing;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return null;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const createTimestampedLink = (href: string) => {
|
|
39
|
-
const newLinkElement = document.createElement('link');
|
|
40
|
-
newLinkElement.rel = 'stylesheet';
|
|
41
|
-
newLinkElement.media = 'print';
|
|
42
|
-
const newHref = `${href + (href.includes('?') ? '&' : '?')}t=${Date.now()}`;
|
|
43
|
-
newLinkElement.href = newHref;
|
|
44
|
-
|
|
45
|
-
return { newHref, newLinkElement };
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
const processNewLink = (
|
|
49
|
-
newLink: Element,
|
|
50
|
-
linksToRemove: HTMLLinkElement[],
|
|
51
|
-
linksToActivate: HTMLLinkElement[],
|
|
52
|
-
linksToWaitFor: Promise<void>[]
|
|
53
|
-
) => {
|
|
54
|
-
const href = newLink.getAttribute('href');
|
|
55
|
-
if (!href) return;
|
|
56
|
-
|
|
57
|
-
const baseNew = getCSSBaseName(href);
|
|
58
|
-
const existingLink = findMatchingLink(baseNew);
|
|
59
|
-
|
|
60
|
-
if (!existingLink) {
|
|
61
|
-
const { newHref, newLinkElement } = createTimestampedLink(href);
|
|
62
|
-
linksToActivate.push(newLinkElement);
|
|
63
|
-
const loadPromise = createCSSLoadPromise(newLinkElement, newHref);
|
|
64
|
-
document.head.appendChild(newLinkElement);
|
|
65
|
-
linksToWaitFor.push(loadPromise);
|
|
66
|
-
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const existingHrefAttr = existingLink.getAttribute('href');
|
|
71
|
-
const existingHref = existingHrefAttr ? existingHrefAttr.split('?')[0] : '';
|
|
72
|
-
const [newHrefBase] = href.split('?');
|
|
73
|
-
if (existingHref === newHrefBase) return;
|
|
74
|
-
|
|
75
|
-
const { newHref, newLinkElement } = createTimestampedLink(href);
|
|
76
|
-
linksToRemove.push(existingLink);
|
|
77
|
-
linksToActivate.push(newLinkElement);
|
|
78
|
-
const loadPromise = createCSSLoadPromise(newLinkElement, newHref);
|
|
79
|
-
document.head.appendChild(newLinkElement);
|
|
80
|
-
linksToWaitFor.push(loadPromise);
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
export const processCSSLinks = (headHTML: string) => {
|
|
84
|
-
const tempDiv = document.createElement('div');
|
|
85
|
-
tempDiv.innerHTML = headHTML;
|
|
86
|
-
const newStylesheets = tempDiv.querySelectorAll('link[rel="stylesheet"]');
|
|
87
|
-
const existingStylesheets = Array.from(
|
|
88
|
-
document.head.querySelectorAll<HTMLLinkElement>(
|
|
89
|
-
'link[rel="stylesheet"]'
|
|
90
|
-
)
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
const newHrefs = Array.from(newStylesheets).map((link) => {
|
|
94
|
-
const href = link.getAttribute('href') || '';
|
|
95
|
-
|
|
96
|
-
return getCSSBaseName(href);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
const linksToRemove: HTMLLinkElement[] = [];
|
|
100
|
-
const linksToWaitFor: Promise<void>[] = [];
|
|
101
|
-
const linksToActivate: HTMLLinkElement[] = [];
|
|
102
|
-
|
|
103
|
-
newStylesheets.forEach((newLink) => {
|
|
104
|
-
processNewLink(newLink, linksToRemove, linksToActivate, linksToWaitFor);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
existingStylesheets.forEach((existingLink) => {
|
|
108
|
-
const existingHref = existingLink.getAttribute('href') || '';
|
|
109
|
-
const baseExisting = getCSSBaseName(existingHref);
|
|
110
|
-
const stillExists = newHrefs.some((newBase) =>
|
|
111
|
-
baseNamesMatch(baseExisting, newBase)
|
|
112
|
-
);
|
|
113
|
-
if (stillExists) return;
|
|
114
|
-
|
|
115
|
-
const wasHandled = Array.from(newStylesheets).some((newLink) => {
|
|
116
|
-
const newHref = newLink.getAttribute('href') || '';
|
|
117
|
-
const baseNewLocal = getCSSBaseName(newHref);
|
|
118
|
-
|
|
119
|
-
return baseNamesMatch(baseExisting, baseNewLocal);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
if (!wasHandled) {
|
|
123
|
-
linksToRemove.push(existingLink);
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
return { linksToActivate, linksToRemove, linksToWaitFor };
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
const findManifestHref = (
|
|
131
|
-
manifest: Record<string, string>,
|
|
132
|
-
baseName: string
|
|
133
|
-
) => {
|
|
134
|
-
const manifestKey = `${baseName
|
|
135
|
-
.split('-')
|
|
136
|
-
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
137
|
-
.join('')}CSS`;
|
|
138
|
-
|
|
139
|
-
if (manifest[manifestKey]) {
|
|
140
|
-
return manifest[manifestKey];
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
for (const [key, value] of Object.entries(manifest)) {
|
|
144
|
-
if (key.endsWith('CSS') && value.includes(baseName)) {
|
|
145
|
-
return value;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return null;
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
const updateStylesheetLink = (
|
|
153
|
-
link: Element,
|
|
154
|
-
manifest: Record<string, string>
|
|
155
|
-
) => {
|
|
156
|
-
if (!(link instanceof HTMLLinkElement)) return;
|
|
157
|
-
const href = link.getAttribute('href');
|
|
158
|
-
if (!href || href.includes('htmx.min.js')) return;
|
|
159
|
-
|
|
160
|
-
let newHref: string | null = null;
|
|
161
|
-
if (manifest) {
|
|
162
|
-
const baseName = getCSSBaseName(href);
|
|
163
|
-
newHref = findManifestHref(manifest, baseName);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if (newHref && newHref !== href) {
|
|
167
|
-
link.href = `${newHref}?t=${Date.now()}`;
|
|
168
|
-
} else {
|
|
169
|
-
const url = new URL(href, window.location.origin);
|
|
170
|
-
url.searchParams.set('t', Date.now().toString());
|
|
171
|
-
link.href = url.toString();
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
export const reloadCSSStylesheets = (manifest: Record<string, string>) => {
|
|
176
|
-
const stylesheets = document.querySelectorAll('link[rel="stylesheet"]');
|
|
177
|
-
stylesheets.forEach((link) => {
|
|
178
|
-
updateStylesheetLink(link, manifest);
|
|
179
|
-
});
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
const createCSSLoadPromise = (linkElement: HTMLLinkElement, newHref: string) =>
|
|
183
|
-
// eslint-disable-next-line promise/avoid-new
|
|
184
|
-
new Promise<void>((resolve) => {
|
|
185
|
-
let resolved = false;
|
|
186
|
-
const doResolve = function () {
|
|
187
|
-
if (resolved) return;
|
|
188
|
-
resolved = true;
|
|
189
|
-
resolve();
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
const verifyCSSOM = function () {
|
|
193
|
-
try {
|
|
194
|
-
const sheets = Array.from(document.styleSheets);
|
|
195
|
-
|
|
196
|
-
return sheets.some(
|
|
197
|
-
(sheet) =>
|
|
198
|
-
sheet.href &&
|
|
199
|
-
sheet.href.includes(newHref.split('?')[0] ?? '')
|
|
200
|
-
);
|
|
201
|
-
} catch {
|
|
202
|
-
return false;
|
|
203
|
-
}
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
linkElement.onload = function () {
|
|
207
|
-
let checkCount = 0;
|
|
208
|
-
const checkCSSOM = function () {
|
|
209
|
-
checkCount++;
|
|
210
|
-
if (verifyCSSOM() || checkCount > CSS_MAX_CHECK_ATTEMPTS) {
|
|
211
|
-
doResolve();
|
|
212
|
-
} else {
|
|
213
|
-
requestAnimationFrame(checkCSSOM);
|
|
214
|
-
}
|
|
215
|
-
};
|
|
216
|
-
requestAnimationFrame(checkCSSOM);
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
linkElement.onerror = function () {
|
|
220
|
-
setTimeout(() => {
|
|
221
|
-
doResolve();
|
|
222
|
-
}, CSS_ERROR_RESOLVE_DELAY_MS);
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
setTimeout(() => {
|
|
226
|
-
if (linkElement.sheet && !resolved) {
|
|
227
|
-
doResolve();
|
|
228
|
-
}
|
|
229
|
-
}, CSS_SHEET_READY_TIMEOUT_MS);
|
|
230
|
-
|
|
231
|
-
setTimeout(() => {
|
|
232
|
-
if (!resolved) {
|
|
233
|
-
doResolve();
|
|
234
|
-
}
|
|
235
|
-
}, CSS_MAX_PARSE_TIMEOUT_MS);
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
const removeLinks = (linksToRemove: HTMLLinkElement[]) => {
|
|
239
|
-
linksToRemove.forEach((link) => {
|
|
240
|
-
if (link.parentNode) {
|
|
241
|
-
link.remove();
|
|
242
|
-
}
|
|
243
|
-
});
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
const activateLinks = (linksToActivate: HTMLLinkElement[]) => {
|
|
247
|
-
linksToActivate.forEach((link) => {
|
|
248
|
-
link.media = 'all';
|
|
249
|
-
});
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
const chainRAF = (depth: number, callback: () => void) => {
|
|
253
|
-
if (depth <= 0) {
|
|
254
|
-
callback();
|
|
255
|
-
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
requestAnimationFrame(() => {
|
|
259
|
-
chainRAF(depth - 1, callback);
|
|
260
|
-
});
|
|
261
|
-
};
|
|
262
|
-
|
|
263
|
-
/* Coordinate CSS load with body update: waits for CSS, patches body,
|
|
264
|
-
activates new CSS, removes old CSS. Handles first-update delay. */
|
|
265
|
-
export const waitForCSSAndUpdate = (
|
|
266
|
-
cssResult: CSSUpdateResult,
|
|
267
|
-
updateBody: () => void
|
|
268
|
-
) => {
|
|
269
|
-
const { linksToActivate, linksToRemove, linksToWaitFor } = cssResult;
|
|
270
|
-
|
|
271
|
-
if (linksToWaitFor.length > 0) {
|
|
272
|
-
void Promise.all(linksToWaitFor).then(() => {
|
|
273
|
-
setTimeout(() => {
|
|
274
|
-
chainRAF(RAF_BATCH_COUNT, () => {
|
|
275
|
-
updateBody();
|
|
276
|
-
activateLinks(linksToActivate);
|
|
277
|
-
requestAnimationFrame(() => {
|
|
278
|
-
removeLinks(linksToRemove);
|
|
279
|
-
if (hmrState.isFirstHMRUpdate) {
|
|
280
|
-
hmrState.isFirstHMRUpdate = false;
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
});
|
|
284
|
-
}, DOM_UPDATE_DELAY_MS);
|
|
285
|
-
|
|
286
|
-
return undefined;
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
const doUpdate = function () {
|
|
293
|
-
chainRAF(RAF_BATCH_COUNT, () => {
|
|
294
|
-
updateBody();
|
|
295
|
-
requestAnimationFrame(() => {
|
|
296
|
-
removeLinks(linksToRemove);
|
|
297
|
-
});
|
|
298
|
-
});
|
|
299
|
-
};
|
|
300
|
-
|
|
301
|
-
if (hmrState.isFirstHMRUpdate) {
|
|
302
|
-
hmrState.isFirstHMRUpdate = false;
|
|
303
|
-
setTimeout(doUpdate, DOM_UPDATE_DELAY_MS);
|
|
304
|
-
} else {
|
|
305
|
-
doUpdate();
|
|
306
|
-
}
|
|
307
|
-
};
|
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
import type {} from '../../types/globals';
|
|
2
|
-
/* DOM diffing/patching for in-place updates (zero flicker) */
|
|
3
|
-
|
|
4
|
-
import { UNFOUND_INDEX } from './constants';
|
|
5
|
-
|
|
6
|
-
type KeyedEntry = {
|
|
7
|
-
index: number;
|
|
8
|
-
node: Node;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
const getElementKey = (elem: Node, index: number) => {
|
|
12
|
-
if (elem.nodeType !== Node.ELEMENT_NODE) return `text_${index}`;
|
|
13
|
-
if (!(elem instanceof Element)) return `text_${index}`;
|
|
14
|
-
if (elem.id) return `id_${elem.id}`;
|
|
15
|
-
if (elem.hasAttribute('data-key'))
|
|
16
|
-
return `key_${elem.getAttribute('data-key')}`;
|
|
17
|
-
|
|
18
|
-
return `tag_${elem.tagName}_${index}`;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const updateElementAttributes = (oldEl: Element, newEl: Element) => {
|
|
22
|
-
const newAttrs = Array.from(newEl.attributes);
|
|
23
|
-
const oldAttrs = Array.from(oldEl.attributes);
|
|
24
|
-
const runtimeAttrs = ['data-hmr-listeners-attached'];
|
|
25
|
-
|
|
26
|
-
oldAttrs.forEach((oldAttr) => {
|
|
27
|
-
if (
|
|
28
|
-
!newEl.hasAttribute(oldAttr.name) &&
|
|
29
|
-
runtimeAttrs.indexOf(oldAttr.name) === UNFOUND_INDEX
|
|
30
|
-
) {
|
|
31
|
-
oldEl.removeAttribute(oldAttr.name);
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
newAttrs.forEach((newAttr) => {
|
|
36
|
-
if (
|
|
37
|
-
runtimeAttrs.indexOf(newAttr.name) !== UNFOUND_INDEX &&
|
|
38
|
-
oldEl.hasAttribute(newAttr.name)
|
|
39
|
-
) {
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
const oldValue = oldEl.getAttribute(newAttr.name);
|
|
43
|
-
if (oldValue !== newAttr.value) {
|
|
44
|
-
oldEl.setAttribute(newAttr.name, newAttr.value);
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const updateTextNode = (oldNode: Node, newNode: Node) => {
|
|
50
|
-
if (oldNode.nodeValue !== newNode.nodeValue) {
|
|
51
|
-
oldNode.nodeValue = newNode.nodeValue;
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const matchChildren = (oldChildren: Node[], newChildren: Node[]) => {
|
|
56
|
-
const oldMap = new Map<string, KeyedEntry[]>();
|
|
57
|
-
const newMap = new Map<string, KeyedEntry[]>();
|
|
58
|
-
|
|
59
|
-
oldChildren.forEach((child, idx) => {
|
|
60
|
-
const key = getElementKey(child, idx);
|
|
61
|
-
if (!oldMap.has(key)) {
|
|
62
|
-
oldMap.set(key, []);
|
|
63
|
-
}
|
|
64
|
-
oldMap.get(key)?.push({ index: idx, node: child });
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
newChildren.forEach((child, idx) => {
|
|
68
|
-
const key = getElementKey(child, idx);
|
|
69
|
-
if (!newMap.has(key)) {
|
|
70
|
-
newMap.set(key, []);
|
|
71
|
-
}
|
|
72
|
-
newMap.get(key)?.push({ index: idx, node: child });
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
return { newMap, oldMap };
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const isHMRScript = (elem: Node) =>
|
|
79
|
-
elem instanceof Element && elem.hasAttribute('data-hmr-client');
|
|
80
|
-
|
|
81
|
-
const isHMRPreserved = (elem: Node) =>
|
|
82
|
-
isHMRScript(elem) ||
|
|
83
|
-
(elem instanceof Element && elem.hasAttribute('data-hmr-overlay'));
|
|
84
|
-
|
|
85
|
-
const isNonHMRScript = (child: Node) =>
|
|
86
|
-
child instanceof Element && child.tagName === 'SCRIPT';
|
|
87
|
-
|
|
88
|
-
const findBestMatch = (oldMatches: KeyedEntry[], matchedOld: Set<Node>) => {
|
|
89
|
-
const unmatched = oldMatches.find((entry) => !matchedOld.has(entry.node));
|
|
90
|
-
if (unmatched) return unmatched;
|
|
91
|
-
if (oldMatches.length > 0) return oldMatches[0] ?? null;
|
|
92
|
-
|
|
93
|
-
return null;
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
const reconcileChild = (
|
|
97
|
-
newChild: Node,
|
|
98
|
-
newIndex: number,
|
|
99
|
-
oldMap: Map<string, KeyedEntry[]>,
|
|
100
|
-
matchedOld: Set<Node>,
|
|
101
|
-
parentNode: Node,
|
|
102
|
-
oldChildrenFiltered: Node[]
|
|
103
|
-
) => {
|
|
104
|
-
const newKey = getElementKey(newChild, newIndex);
|
|
105
|
-
const oldMatches = oldMap.get(newKey) || [];
|
|
106
|
-
|
|
107
|
-
if (oldMatches.length === 0) {
|
|
108
|
-
const clone = newChild.cloneNode(true);
|
|
109
|
-
parentNode.insertBefore(clone, oldChildrenFiltered[newIndex] || null);
|
|
110
|
-
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const bestMatch = findBestMatch(oldMatches, matchedOld);
|
|
115
|
-
if (bestMatch && !matchedOld.has(bestMatch.node)) {
|
|
116
|
-
matchedOld.add(bestMatch.node);
|
|
117
|
-
patchNode(bestMatch.node, newChild);
|
|
118
|
-
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const clone = newChild.cloneNode(true);
|
|
123
|
-
parentNode.insertBefore(clone, oldChildrenFiltered[newIndex] || null);
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
const patchNode = (oldNode: Node, newNode: Node) => {
|
|
127
|
-
if (
|
|
128
|
-
oldNode.nodeType === Node.TEXT_NODE &&
|
|
129
|
-
newNode.nodeType === Node.TEXT_NODE
|
|
130
|
-
) {
|
|
131
|
-
updateTextNode(oldNode, newNode);
|
|
132
|
-
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (
|
|
137
|
-
oldNode.nodeType !== Node.ELEMENT_NODE ||
|
|
138
|
-
newNode.nodeType !== Node.ELEMENT_NODE
|
|
139
|
-
) {
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (!(oldNode instanceof Element) || !(newNode instanceof Element)) return;
|
|
144
|
-
const oldEl = oldNode;
|
|
145
|
-
const newEl = newNode;
|
|
146
|
-
|
|
147
|
-
if (oldEl.tagName !== newEl.tagName) {
|
|
148
|
-
const clone = newEl.cloneNode(true);
|
|
149
|
-
oldEl.replaceWith(clone);
|
|
150
|
-
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
updateElementAttributes(oldEl, newEl);
|
|
155
|
-
|
|
156
|
-
const oldChildren = Array.from(oldNode.childNodes);
|
|
157
|
-
const newChildren = Array.from(newNode.childNodes);
|
|
158
|
-
|
|
159
|
-
const oldChildrenFiltered = oldChildren.filter(
|
|
160
|
-
(child) => !isHMRScript(child) && !isNonHMRScript(child)
|
|
161
|
-
);
|
|
162
|
-
const newChildrenFiltered = newChildren.filter(
|
|
163
|
-
(child) => !isHMRScript(child) && !isNonHMRScript(child)
|
|
164
|
-
);
|
|
165
|
-
|
|
166
|
-
const { oldMap } = matchChildren(oldChildrenFiltered, newChildrenFiltered);
|
|
167
|
-
const matchedOld = new Set<Node>();
|
|
168
|
-
|
|
169
|
-
newChildrenFiltered.forEach((newChild, newIndex) => {
|
|
170
|
-
reconcileChild(
|
|
171
|
-
newChild,
|
|
172
|
-
newIndex,
|
|
173
|
-
oldMap,
|
|
174
|
-
matchedOld,
|
|
175
|
-
oldNode,
|
|
176
|
-
oldChildrenFiltered
|
|
177
|
-
);
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
oldChildrenFiltered.forEach((oldChild) => {
|
|
181
|
-
if (!matchedOld.has(oldChild) && !isHMRPreserved(oldChild)) {
|
|
182
|
-
oldChild.remove();
|
|
183
|
-
}
|
|
184
|
-
});
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
export const patchDOMInPlace = (oldContainer: HTMLElement, newHTML: string) => {
|
|
188
|
-
const tempDiv = document.createElement('div');
|
|
189
|
-
tempDiv.innerHTML = newHTML;
|
|
190
|
-
const newContainer = tempDiv;
|
|
191
|
-
|
|
192
|
-
const oldChildren = Array.from(oldContainer.childNodes);
|
|
193
|
-
const newChildren = Array.from(newContainer.childNodes);
|
|
194
|
-
|
|
195
|
-
const oldChildrenFiltered = oldChildren.filter(
|
|
196
|
-
(child) =>
|
|
197
|
-
!(
|
|
198
|
-
child instanceof Element &&
|
|
199
|
-
child.tagName === 'SCRIPT' &&
|
|
200
|
-
!child.hasAttribute('data-hmr-client')
|
|
201
|
-
)
|
|
202
|
-
);
|
|
203
|
-
const newChildrenFiltered = newChildren.filter(
|
|
204
|
-
(child) => !isNonHMRScript(child)
|
|
205
|
-
);
|
|
206
|
-
|
|
207
|
-
const { oldMap } = matchChildren(oldChildrenFiltered, newChildrenFiltered);
|
|
208
|
-
const matchedOld = new Set<Node>();
|
|
209
|
-
|
|
210
|
-
newChildrenFiltered.forEach((newChild, newIndex) => {
|
|
211
|
-
reconcileChild(
|
|
212
|
-
newChild,
|
|
213
|
-
newIndex,
|
|
214
|
-
oldMap,
|
|
215
|
-
matchedOld,
|
|
216
|
-
oldContainer,
|
|
217
|
-
oldChildrenFiltered
|
|
218
|
-
);
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
oldChildrenFiltered.forEach((oldChild) => {
|
|
222
|
-
if (matchedOld.has(oldChild)) return;
|
|
223
|
-
if (isHMRPreserved(oldChild)) return;
|
|
224
|
-
oldChild.remove();
|
|
225
|
-
});
|
|
226
|
-
};
|