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