@absolutejs/absolute 0.20.0-beta.0 → 0.20.0-beta.2
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/components/core/streamingSlotRegistrar.js +1 -1
- package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
- package/dist/angular/index.js +14 -1
- package/dist/angular/index.js.map +3 -3
- package/dist/angular/server.js +14 -1
- package/dist/angular/server.js.map +3 -3
- package/dist/build.js +206 -47
- package/dist/build.js.map +20 -19
- package/dist/cli/config/server.js +2 -2
- package/dist/cli/index.js +16506 -11326
- package/dist/dev/client/cssUtils.ts +98 -41
- package/dist/dev/client/handlers/html.ts +75 -15
- package/dist/dev/client/handlers/htmx.ts +43 -8
- package/dist/dev/client/handlers/react.ts +114 -32
- package/dist/dev/client/handlers/rebuild.ts +28 -8
- package/dist/dev/client/handlers/svelte.ts +33 -50
- package/dist/dev/client/handlers/vue.ts +36 -45
- package/dist/dev/client/hmrClient.ts +38 -6
- package/dist/dev/client/hmrTiming.ts +141 -0
- package/dist/dev/serverBootstrap.js +20 -11
- package/dist/index.js +953 -423
- package/dist/index.js.map +27 -23
- package/dist/mobile/browser.js.map +1 -1
- package/dist/mobile/index.js +2667 -260
- package/dist/mobile/index.js.map +27 -7
- package/dist/react/index.js +16 -3
- package/dist/react/index.js.map +4 -4
- package/dist/react/jsxDevRuntimeCompat.js +22 -2
- package/dist/react/jsxDevRuntimeCompat.js.map +5 -4
- package/dist/react/jsxRuntimeCompat.js +90 -0
- package/dist/react/jsxRuntimeCompat.js.map +10 -0
- package/dist/react/server.js +16 -3
- package/dist/react/server.js.map +4 -4
- package/dist/src/build/compileVue.d.ts +1 -1
- package/dist/src/build/resolveBuildMode.d.ts +7 -0
- package/dist/src/cli/scripts/dev.d.ts +4 -1
- package/dist/src/cli/scripts/mobile.d.ts +1 -0
- package/dist/src/cli/utils.d.ts +2 -2
- package/dist/src/core/build.d.ts +1 -1
- package/dist/src/dev/clientManager.d.ts +7 -0
- package/dist/src/dev/configResolver.d.ts +2 -0
- package/dist/src/dev/moduleMapper.d.ts +1 -1
- package/dist/src/dev/moduleServer.d.ts +2 -1
- package/dist/src/dev/pathUtils.d.ts +1 -1
- package/dist/src/dev/reactComponentClassifier.d.ts +1 -1
- package/dist/src/mobile/androidConformance.d.ts +42 -0
- package/dist/src/mobile/androidEmulatorController.d.ts +100 -0
- package/dist/src/mobile/androidNativeWatcher.d.ts +19 -0
- package/dist/src/mobile/androidRelease.d.ts +51 -0
- package/dist/src/mobile/androidWebView.d.ts +46 -0
- package/dist/src/mobile/associationFiles.d.ts +49 -0
- package/dist/src/mobile/buildMetadata.d.ts +10 -0
- package/dist/src/mobile/buildPipeline.d.ts +8 -0
- package/dist/src/mobile/buildRelease.d.ts +24 -0
- package/dist/src/mobile/capacitorBundle.d.ts +28 -0
- package/dist/src/mobile/capacitorProject.d.ts +9 -0
- package/dist/src/mobile/client.d.ts +1 -1
- package/dist/src/mobile/config.d.ts +17 -0
- package/dist/src/mobile/emulatorDoctor.d.ts +25 -0
- package/dist/src/mobile/emulatorInstaller.d.ts +41 -0
- package/dist/src/mobile/index.d.ts +14 -0
- package/dist/src/mobile/iosRelease.d.ts +61 -0
- package/dist/src/mobile/materializedBundle.d.ts +14 -0
- package/dist/src/mobile/nativeDeepLinks.d.ts +5 -0
- package/dist/src/mobile/releaseArtifact.d.ts +1 -0
- package/dist/src/mobile/releaseDoctor.d.ts +13 -0
- package/dist/src/mobile/releasePublisher.d.ts +130 -0
- package/dist/src/mobile/routeMatcher.d.ts +3 -0
- package/dist/src/mobile/routeMetadataTransform.d.ts +13 -0
- package/dist/src/mobile/shellBootstrap.d.ts +1 -0
- package/dist/src/mobile/transport.d.ts +31 -0
- package/dist/src/react/hooks/useMediaQuery.d.ts +1 -1
- package/dist/src/react/jsxDevRuntimeCompat.d.ts +1 -1
- package/dist/src/react/jsxRuntimeCompat.d.ts +7 -0
- package/dist/src/react/pageHandler.d.ts +3 -0
- package/dist/src/utils/loadConfig.d.ts +1 -0
- package/dist/src/utils/logger.d.ts +2 -0
- package/dist/src/utils/userAgentFunctions.d.ts +1 -1
- package/dist/svelte/index.js +14 -1
- package/dist/svelte/index.js.map +3 -3
- package/dist/svelte/server.js +14 -1
- package/dist/svelte/server.js.map +3 -3
- package/dist/types/build.d.ts +45 -0
- package/dist/types/cli.d.ts +2 -0
- package/dist/types/globals.d.ts +14 -0
- package/dist/types/messages.d.ts +12 -0
- package/dist/vue/index.js +14 -1
- package/dist/vue/index.js.map +3 -3
- package/dist/vue/server.js +14 -1
- package/dist/vue/server.js.map +3 -3
- package/package.json +35 -7
|
@@ -7,8 +7,20 @@ import {
|
|
|
7
7
|
isRuntimeErrorOverlay,
|
|
8
8
|
showErrorOverlay
|
|
9
9
|
} from '../errorOverlay';
|
|
10
|
+
import { sendAbsoluteHmrTiming } from '../hmrTiming';
|
|
10
11
|
|
|
11
|
-
export const handleFullReload = (
|
|
12
|
+
export const handleFullReload = (message?: {
|
|
13
|
+
data: { serverDuration?: number };
|
|
14
|
+
timestamp?: number;
|
|
15
|
+
}) => {
|
|
16
|
+
const clientStart = performance.now();
|
|
17
|
+
sendAbsoluteHmrTiming({
|
|
18
|
+
clientStart,
|
|
19
|
+
kind: 'full-reload',
|
|
20
|
+
outcome: 'reloaded',
|
|
21
|
+
serverMs: message?.data.serverDuration,
|
|
22
|
+
updateId: message?.timestamp
|
|
23
|
+
});
|
|
12
24
|
setTimeout(() => {
|
|
13
25
|
window.location.reload();
|
|
14
26
|
}, REBUILD_RELOAD_DELAY_MS);
|
|
@@ -33,7 +45,18 @@ export const handleManifest = (message: {
|
|
|
33
45
|
window.__HMR_MODULE_UPDATES__ = [];
|
|
34
46
|
};
|
|
35
47
|
|
|
36
|
-
const HMR_FRAMEWORKS = [
|
|
48
|
+
const HMR_FRAMEWORKS = [
|
|
49
|
+
'angular',
|
|
50
|
+
'assets',
|
|
51
|
+
'ember',
|
|
52
|
+
'html',
|
|
53
|
+
'htmx',
|
|
54
|
+
'react',
|
|
55
|
+
'styles',
|
|
56
|
+
'svelte',
|
|
57
|
+
'tailwind',
|
|
58
|
+
'vue'
|
|
59
|
+
];
|
|
37
60
|
|
|
38
61
|
const mergeRecord = (
|
|
39
62
|
source: Record<string, string | number>,
|
|
@@ -115,12 +138,9 @@ export const handleRebuildComplete = (message: {
|
|
|
115
138
|
|
|
116
139
|
if (
|
|
117
140
|
message.data.affectedFrameworks &&
|
|
118
|
-
!message.data.affectedFrameworks.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
!message.data.affectedFrameworks.includes('htmx') &&
|
|
122
|
-
!message.data.affectedFrameworks.includes('vue') &&
|
|
123
|
-
!message.data.affectedFrameworks.includes('svelte')
|
|
141
|
+
!message.data.affectedFrameworks.some((framework) =>
|
|
142
|
+
HMR_FRAMEWORKS.includes(framework)
|
|
143
|
+
)
|
|
124
144
|
) {
|
|
125
145
|
const url = new URL(window.location.href);
|
|
126
146
|
url.searchParams.set('_cb', Date.now().toString());
|
|
@@ -9,43 +9,13 @@ import {
|
|
|
9
9
|
restoreScrollState
|
|
10
10
|
} from '../domState';
|
|
11
11
|
import { detectCurrentFramework, findIndexPath } from '../frameworkDetect';
|
|
12
|
+
import { sendAbsoluteHmrTiming } from '../hmrTiming';
|
|
13
|
+
import { swapCSSStylesheet } from '../cssUtils';
|
|
12
14
|
|
|
13
15
|
type SvelteHmrWindow = Window & {
|
|
14
16
|
__SVELTE_HMR_ACCEPT__?: Record<string, (mod: unknown) => void>;
|
|
15
17
|
};
|
|
16
18
|
|
|
17
|
-
/* Swap a stylesheet link by matching cssBaseName or framework name */
|
|
18
|
-
const swapStylesheet = (
|
|
19
|
-
cssUrl: string,
|
|
20
|
-
cssBaseName: string,
|
|
21
|
-
framework: string
|
|
22
|
-
) => {
|
|
23
|
-
let existingLink: HTMLLinkElement | null = null;
|
|
24
|
-
document
|
|
25
|
-
.querySelectorAll<HTMLLinkElement>('link[rel="stylesheet"]')
|
|
26
|
-
.forEach((link) => {
|
|
27
|
-
const href = link.getAttribute('href') ?? '';
|
|
28
|
-
if (href.includes(cssBaseName) || href.includes(framework)) {
|
|
29
|
-
existingLink = link;
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
if (!existingLink) {
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const capturedExisting: HTMLLinkElement = existingLink;
|
|
38
|
-
const newLink = document.createElement('link');
|
|
39
|
-
newLink.rel = 'stylesheet';
|
|
40
|
-
newLink.href = `${cssUrl}?t=${Date.now()}`;
|
|
41
|
-
newLink.onload = () => {
|
|
42
|
-
if (capturedExisting && capturedExisting.parentNode) {
|
|
43
|
-
capturedExisting.remove();
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
document.head.appendChild(newLink);
|
|
47
|
-
};
|
|
48
|
-
|
|
49
19
|
const extractCountFromDOM = () => {
|
|
50
20
|
const countButton = document.querySelector('button');
|
|
51
21
|
if (!countButton || !countButton.textContent) {
|
|
@@ -222,17 +192,27 @@ export const handleSvelteUpdate = (message: {
|
|
|
222
192
|
sourceFile?: string;
|
|
223
193
|
updateType?: string;
|
|
224
194
|
};
|
|
195
|
+
timestamp?: number;
|
|
225
196
|
}) => {
|
|
226
197
|
const svelteFrameworkCheck = detectCurrentFramework();
|
|
227
198
|
if (svelteFrameworkCheck !== 'svelte') return;
|
|
228
199
|
|
|
229
200
|
/* CSS-only update: hot-swap stylesheet, no remount needed */
|
|
230
201
|
if (message.data.updateType === 'css-only' && message.data.cssUrl) {
|
|
231
|
-
|
|
202
|
+
const clientStart = performance.now();
|
|
203
|
+
const cssBaseName = message.data.cssBaseName || '';
|
|
204
|
+
void swapCSSStylesheet(
|
|
232
205
|
message.data.cssUrl,
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
206
|
+
(href) => href.includes(cssBaseName) || href.includes('svelte')
|
|
207
|
+
).then((applied) => {
|
|
208
|
+
sendAbsoluteHmrTiming({
|
|
209
|
+
clientStart,
|
|
210
|
+
kind: 'css',
|
|
211
|
+
outcome: applied ? 'applied' : 'failed',
|
|
212
|
+
serverMs: message.data.serverDuration,
|
|
213
|
+
updateId: message.timestamp
|
|
214
|
+
});
|
|
215
|
+
});
|
|
236
216
|
|
|
237
217
|
return;
|
|
238
218
|
}
|
|
@@ -255,10 +235,10 @@ export const handleSvelteUpdate = (message: {
|
|
|
255
235
|
|
|
256
236
|
/* CSS pre-update: swap stylesheet BEFORE importing to prevent FOUC */
|
|
257
237
|
if (message.data.cssUrl) {
|
|
258
|
-
|
|
238
|
+
const cssBaseName = message.data.cssBaseName || '';
|
|
239
|
+
void swapCSSStylesheet(
|
|
259
240
|
message.data.cssUrl,
|
|
260
|
-
|
|
261
|
-
'svelte'
|
|
241
|
+
(href) => href.includes(cssBaseName) || href.includes('svelte')
|
|
262
242
|
);
|
|
263
243
|
}
|
|
264
244
|
|
|
@@ -340,23 +320,26 @@ export const handleSvelteUpdate = (message: {
|
|
|
340
320
|
}
|
|
341
321
|
window.__HMR_PRESERVED_STATE__ = undefined;
|
|
342
322
|
|
|
343
|
-
if (
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
);
|
|
350
|
-
const total = (message.data.serverDuration ?? 0) + clientMs;
|
|
351
|
-
window.__HMR_WS__.send(
|
|
352
|
-
JSON.stringify({ duration: total, type: 'hmr-timing' })
|
|
353
|
-
);
|
|
323
|
+
if (message.data.serverDuration !== undefined) {
|
|
324
|
+
sendAbsoluteHmrTiming({
|
|
325
|
+
clientStart,
|
|
326
|
+
kind: 'component',
|
|
327
|
+
serverMs: message.data.serverDuration,
|
|
328
|
+
updateId: message.timestamp
|
|
329
|
+
});
|
|
354
330
|
}
|
|
355
331
|
|
|
356
332
|
return undefined;
|
|
357
333
|
})
|
|
358
334
|
.catch((err: unknown) => {
|
|
359
335
|
console.warn('[HMR] Svelte HMR failed, reloading:', err);
|
|
336
|
+
sendAbsoluteHmrTiming({
|
|
337
|
+
clientStart,
|
|
338
|
+
kind: 'component',
|
|
339
|
+
outcome: 'reloaded',
|
|
340
|
+
serverMs: message.data.serverDuration,
|
|
341
|
+
updateId: message.timestamp
|
|
342
|
+
});
|
|
360
343
|
window.location.reload();
|
|
361
344
|
});
|
|
362
345
|
|
|
@@ -4,6 +4,8 @@ import type {} from '../../../types/globals';
|
|
|
4
4
|
import type { VueComponentInstance, VueVNode } from '../../../types/vue';
|
|
5
5
|
import { saveDOMState, restoreDOMState } from '../domState';
|
|
6
6
|
import { detectCurrentFramework, findIndexPath } from '../frameworkDetect';
|
|
7
|
+
import { sendAbsoluteHmrTiming } from '../hmrTiming';
|
|
8
|
+
import { swapCSSStylesheet } from '../cssUtils';
|
|
7
9
|
|
|
8
10
|
/* Collect reactive value from a setup state entry into the target record */
|
|
9
11
|
const collectSetupValue = (
|
|
@@ -67,38 +69,6 @@ const extractChildComponentState = (
|
|
|
67
69
|
walkVNode(instance.subTree, state);
|
|
68
70
|
};
|
|
69
71
|
|
|
70
|
-
/* Find an existing stylesheet link matching the given base name */
|
|
71
|
-
const findMatchingStylesheetLink = (cssBaseName: string) => {
|
|
72
|
-
let found: HTMLLinkElement | null = null;
|
|
73
|
-
document
|
|
74
|
-
.querySelectorAll<HTMLLinkElement>('link[rel="stylesheet"]')
|
|
75
|
-
.forEach((link) => {
|
|
76
|
-
const href = link.getAttribute('href') ?? '';
|
|
77
|
-
if (cssBaseName && href.includes(cssBaseName)) {
|
|
78
|
-
found = link;
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
return found;
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
/* Swap a stylesheet link with a new one, removing the old on load */
|
|
86
|
-
const swapStylesheet = (cssUrl: string, cssBaseName: string) => {
|
|
87
|
-
const existingLink = findMatchingStylesheetLink(cssBaseName);
|
|
88
|
-
if (!existingLink) return;
|
|
89
|
-
|
|
90
|
-
const capturedExisting: HTMLLinkElement = existingLink;
|
|
91
|
-
const newLink = document.createElement('link');
|
|
92
|
-
newLink.rel = 'stylesheet';
|
|
93
|
-
newLink.href = `${cssUrl}?t=${Date.now()}`;
|
|
94
|
-
newLink.onload = function () {
|
|
95
|
-
if (capturedExisting && capturedExisting.parentNode) {
|
|
96
|
-
capturedExisting.remove();
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
document.head.appendChild(newLink);
|
|
100
|
-
};
|
|
101
|
-
|
|
102
72
|
/* Extract Vue reactive state from app instance */
|
|
103
73
|
const extractVueAppState = (vuePreservedState: Record<string, unknown>) => {
|
|
104
74
|
if (!window.__VUE_APP__ || !window.__VUE_APP__._instance) return;
|
|
@@ -163,12 +133,26 @@ export const handleVueUpdate = (message: {
|
|
|
163
133
|
sourceFile?: string;
|
|
164
134
|
updateType?: string;
|
|
165
135
|
};
|
|
136
|
+
timestamp?: number;
|
|
166
137
|
}) => {
|
|
167
138
|
const vueFrameworkCheck = detectCurrentFramework();
|
|
168
139
|
if (vueFrameworkCheck !== 'vue') return;
|
|
169
140
|
|
|
170
141
|
if (message.data.updateType === 'css-only' && message.data.cssUrl) {
|
|
171
|
-
|
|
142
|
+
const clientStart = performance.now();
|
|
143
|
+
const cssBaseName = message.data.cssBaseName || '';
|
|
144
|
+
void swapCSSStylesheet(
|
|
145
|
+
message.data.cssUrl,
|
|
146
|
+
(href) => Boolean(cssBaseName) && href.includes(cssBaseName)
|
|
147
|
+
).then((applied) => {
|
|
148
|
+
sendAbsoluteHmrTiming({
|
|
149
|
+
clientStart,
|
|
150
|
+
kind: 'css',
|
|
151
|
+
outcome: applied ? 'applied' : 'failed',
|
|
152
|
+
serverMs: message.data.serverDuration,
|
|
153
|
+
updateId: message.timestamp
|
|
154
|
+
});
|
|
155
|
+
});
|
|
172
156
|
|
|
173
157
|
return;
|
|
174
158
|
}
|
|
@@ -224,17 +208,13 @@ export const handleVueUpdate = (message: {
|
|
|
224
208
|
}
|
|
225
209
|
sessionStorage.removeItem('__HMR_ACTIVE__');
|
|
226
210
|
|
|
227
|
-
if (
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
);
|
|
234
|
-
const total = (message.data.serverDuration ?? 0) + clientMs;
|
|
235
|
-
window.__HMR_WS__.send(
|
|
236
|
-
JSON.stringify({ duration: total, type: 'hmr-timing' })
|
|
237
|
-
);
|
|
211
|
+
if (message.data.serverDuration !== undefined) {
|
|
212
|
+
sendAbsoluteHmrTiming({
|
|
213
|
+
clientStart,
|
|
214
|
+
kind: 'component',
|
|
215
|
+
serverMs: message.data.serverDuration,
|
|
216
|
+
updateId: message.timestamp
|
|
217
|
+
});
|
|
238
218
|
}
|
|
239
219
|
|
|
240
220
|
return undefined;
|
|
@@ -242,6 +222,13 @@ export const handleVueUpdate = (message: {
|
|
|
242
222
|
.catch((err: unknown) => {
|
|
243
223
|
console.warn('[HMR] Vue HMR failed, reloading:', err);
|
|
244
224
|
sessionStorage.removeItem('__HMR_ACTIVE__');
|
|
225
|
+
sendAbsoluteHmrTiming({
|
|
226
|
+
clientStart,
|
|
227
|
+
kind: 'component',
|
|
228
|
+
outcome: 'reloaded',
|
|
229
|
+
serverMs: message.data.serverDuration,
|
|
230
|
+
updateId: message.timestamp
|
|
231
|
+
});
|
|
245
232
|
window.location.reload();
|
|
246
233
|
});
|
|
247
234
|
|
|
@@ -250,7 +237,11 @@ export const handleVueUpdate = (message: {
|
|
|
250
237
|
|
|
251
238
|
/* CSS pre-update: swap stylesheet BEFORE unmounting to prevent FOUC */
|
|
252
239
|
if (message.data.cssUrl) {
|
|
253
|
-
|
|
240
|
+
const cssBaseName = message.data.cssBaseName || '';
|
|
241
|
+
void swapCSSStylesheet(
|
|
242
|
+
message.data.cssUrl,
|
|
243
|
+
(href) => Boolean(cssBaseName) && href.includes(cssBaseName)
|
|
244
|
+
);
|
|
254
245
|
}
|
|
255
246
|
|
|
256
247
|
/* Unmount old Vue app but keep DOM visually intact during async import.
|
|
@@ -12,6 +12,11 @@ import {
|
|
|
12
12
|
WEBSOCKET_NORMAL_CLOSURE
|
|
13
13
|
} from './constants';
|
|
14
14
|
import { detectCurrentFramework } from './frameworkDetect';
|
|
15
|
+
import {
|
|
16
|
+
absoluteHmrClientTarget,
|
|
17
|
+
restoreAbsoluteHmrApply,
|
|
18
|
+
sendAbsoluteHmrTiming
|
|
19
|
+
} from './hmrTiming';
|
|
15
20
|
import { hideErrorOverlay, showErrorOverlay } from './errorOverlay';
|
|
16
21
|
import {
|
|
17
22
|
dispatchAngularComponentRemount,
|
|
@@ -39,6 +44,8 @@ const isStringRecord = (value: unknown): value is Record<string, string> =>
|
|
|
39
44
|
isRecord(value) &&
|
|
40
45
|
Object.values(value).every((entry) => typeof entry === 'string');
|
|
41
46
|
|
|
47
|
+
restoreAbsoluteHmrApply();
|
|
48
|
+
|
|
42
49
|
/* Lightweight "server disconnected" banner. When the dev server is
|
|
43
50
|
* genuinely down (process restarting or crashed) the browser would
|
|
44
51
|
* otherwise show its own blank "site can't be reached" page — this keeps
|
|
@@ -77,6 +84,7 @@ const hideConnectionLostBanner = () => {
|
|
|
77
84
|
|
|
78
85
|
// Initialize HMR globals
|
|
79
86
|
if (typeof window !== 'undefined') {
|
|
87
|
+
window.__ABS_HMR_TARGET__ = absoluteHmrClientTarget();
|
|
80
88
|
installAngularRemountGlobal();
|
|
81
89
|
if (!window.__HMR_MANIFEST__) {
|
|
82
90
|
window.__HMR_MANIFEST__ = {};
|
|
@@ -119,6 +127,7 @@ const hmrUpdateTypes = new Set([
|
|
|
119
127
|
'angular:component-update',
|
|
120
128
|
'angular:component-remount',
|
|
121
129
|
'angular:rebootstrap',
|
|
130
|
+
'angular-update',
|
|
122
131
|
'react-update',
|
|
123
132
|
'html-update',
|
|
124
133
|
'htmx-update',
|
|
@@ -134,6 +143,7 @@ type HMRMessage = {
|
|
|
134
143
|
affectedFrameworks?: string[];
|
|
135
144
|
column?: number;
|
|
136
145
|
error?: string;
|
|
146
|
+
fastRefreshSupported?: boolean;
|
|
137
147
|
file?: string;
|
|
138
148
|
framework?: string;
|
|
139
149
|
hasCSSChanges?: boolean;
|
|
@@ -149,9 +159,23 @@ type HMRMessage = {
|
|
|
149
159
|
serverDuration?: number;
|
|
150
160
|
serverVersions?: Record<string, number>;
|
|
151
161
|
};
|
|
162
|
+
timestamp?: number;
|
|
152
163
|
type: string;
|
|
153
164
|
};
|
|
154
165
|
|
|
166
|
+
const handleStylesheetUpdate = (message: HMRMessage) => {
|
|
167
|
+
const clientStart = performance.now();
|
|
168
|
+
void reloadCSSStylesheets(message.data.manifest ?? {}).then((applied) => {
|
|
169
|
+
sendAbsoluteHmrTiming({
|
|
170
|
+
clientStart,
|
|
171
|
+
kind: 'css',
|
|
172
|
+
outcome: applied ? 'applied' : 'failed',
|
|
173
|
+
serverMs: message.data.serverDuration,
|
|
174
|
+
updateId: message.timestamp
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
};
|
|
178
|
+
|
|
155
179
|
const handleHMRMessage = (message: HMRMessage) => {
|
|
156
180
|
if (hmrUpdateTypes.has(message.type)) {
|
|
157
181
|
hmrState.isHMRUpdating = true;
|
|
@@ -208,12 +232,13 @@ const handleHMRMessage = (message: HMRMessage) => {
|
|
|
208
232
|
const { data } = message;
|
|
209
233
|
if (isRecord(data)) {
|
|
210
234
|
const id = Reflect.get(data, 'id');
|
|
211
|
-
const timestamp = Reflect.get(data, 'timestamp');
|
|
212
235
|
if (typeof id !== 'string') break;
|
|
213
236
|
dispatchAngularComponentUpdate({
|
|
214
237
|
id,
|
|
215
238
|
timestamp:
|
|
216
|
-
typeof timestamp === 'number'
|
|
239
|
+
typeof message.timestamp === 'number'
|
|
240
|
+
? message.timestamp
|
|
241
|
+
: Date.now()
|
|
217
242
|
});
|
|
218
243
|
}
|
|
219
244
|
break;
|
|
@@ -230,12 +255,13 @@ const handleHMRMessage = (message: HMRMessage) => {
|
|
|
230
255
|
const { data } = message;
|
|
231
256
|
if (isRecord(data)) {
|
|
232
257
|
const id = Reflect.get(data, 'id');
|
|
233
|
-
const timestamp = Reflect.get(data, 'timestamp');
|
|
234
258
|
if (typeof id !== 'string') break;
|
|
235
259
|
dispatchAngularComponentRemount({
|
|
236
260
|
id,
|
|
237
261
|
timestamp:
|
|
238
|
-
typeof timestamp === 'number'
|
|
262
|
+
typeof message.timestamp === 'number'
|
|
263
|
+
? message.timestamp
|
|
264
|
+
: Date.now()
|
|
239
265
|
});
|
|
240
266
|
}
|
|
241
267
|
break;
|
|
@@ -278,12 +304,17 @@ const handleHMRMessage = (message: HMRMessage) => {
|
|
|
278
304
|
handleRebuildError(message);
|
|
279
305
|
break;
|
|
280
306
|
case 'full-reload':
|
|
281
|
-
handleFullReload();
|
|
307
|
+
handleFullReload(message);
|
|
282
308
|
break;
|
|
283
309
|
case 'pong':
|
|
284
310
|
break;
|
|
285
311
|
case 'style-update':
|
|
286
|
-
|
|
312
|
+
handleStylesheetUpdate(message);
|
|
313
|
+
break;
|
|
314
|
+
case 'angular-update':
|
|
315
|
+
if (detectCurrentFramework() === 'angular') {
|
|
316
|
+
handleStylesheetUpdate(message);
|
|
317
|
+
}
|
|
287
318
|
break;
|
|
288
319
|
default:
|
|
289
320
|
break;
|
|
@@ -311,6 +342,7 @@ if (!(window.__HMR_WS__ && window.__HMR_WS__.readyState === WebSocket.OPEN)) {
|
|
|
311
342
|
wsc.send(
|
|
312
343
|
JSON.stringify({
|
|
313
344
|
framework: currentFramework,
|
|
345
|
+
target: absoluteHmrClientTarget(),
|
|
314
346
|
type: 'ready'
|
|
315
347
|
})
|
|
316
348
|
);
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import type {} from '../../types/globals';
|
|
2
|
+
import type {
|
|
3
|
+
HMRApplyKind,
|
|
4
|
+
HMRApplyOutcome,
|
|
5
|
+
HMRClientTarget
|
|
6
|
+
} from '../../../types/messages';
|
|
7
|
+
|
|
8
|
+
const MAX_RETAINED_HMR_APPLIES = 50;
|
|
9
|
+
const HMR_APPLY_STORAGE_KEY = '__absolute_hmr_last_apply__';
|
|
10
|
+
|
|
11
|
+
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
12
|
+
typeof value === 'object' && value !== null;
|
|
13
|
+
|
|
14
|
+
const isHmrTarget = (value: unknown): value is HMRClientTarget =>
|
|
15
|
+
value === 'capacitor-android' ||
|
|
16
|
+
value === 'capacitor-ios' ||
|
|
17
|
+
value === 'capacitor-native' ||
|
|
18
|
+
value === 'web';
|
|
19
|
+
|
|
20
|
+
const isHmrKind = (value: unknown): value is HMRApplyKind =>
|
|
21
|
+
value === 'component' ||
|
|
22
|
+
value === 'css' ||
|
|
23
|
+
value === 'full-reload' ||
|
|
24
|
+
value === 'html' ||
|
|
25
|
+
value === 'htmx' ||
|
|
26
|
+
value === 'script';
|
|
27
|
+
|
|
28
|
+
const isHmrOutcome = (value: unknown): value is HMRApplyOutcome =>
|
|
29
|
+
value === 'applied' || value === 'failed' || value === 'reloaded';
|
|
30
|
+
|
|
31
|
+
type HmrApply = NonNullable<Window['__ABS_HMR_LAST_APPLY__']>;
|
|
32
|
+
|
|
33
|
+
const isHmrApply = (value: unknown): value is HmrApply =>
|
|
34
|
+
isRecord(value) &&
|
|
35
|
+
typeof value.clientMs === 'number' &&
|
|
36
|
+
typeof value.duration === 'number' &&
|
|
37
|
+
(value.kind === undefined || isHmrKind(value.kind)) &&
|
|
38
|
+
isHmrOutcome(value.outcome) &&
|
|
39
|
+
typeof value.serverMs === 'number' &&
|
|
40
|
+
isHmrTarget(value.target) &&
|
|
41
|
+
(value.updateId === undefined || typeof value.updateId === 'number');
|
|
42
|
+
|
|
43
|
+
const getSessionStorage = () => {
|
|
44
|
+
const storage = Reflect.get(window, 'sessionStorage');
|
|
45
|
+
if (!isRecord(storage)) return undefined;
|
|
46
|
+
|
|
47
|
+
return storage;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const persistHmrApply = (timing: HmrApply) => {
|
|
51
|
+
try {
|
|
52
|
+
const storage = getSessionStorage();
|
|
53
|
+
const setItem = storage && Reflect.get(storage, 'setItem');
|
|
54
|
+
if (typeof setItem !== 'function') return;
|
|
55
|
+
Reflect.apply(setItem, storage, [
|
|
56
|
+
HMR_APPLY_STORAGE_KEY,
|
|
57
|
+
JSON.stringify(timing)
|
|
58
|
+
]);
|
|
59
|
+
} catch {
|
|
60
|
+
// Storage may be unavailable in sandboxed or privacy-restricted documents.
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const absoluteHmrClientTarget = () => {
|
|
65
|
+
const currentLocation = Reflect.get(globalThis, 'location');
|
|
66
|
+
if (typeof currentLocation === 'object' && currentLocation !== null) {
|
|
67
|
+
const search = Reflect.get(currentLocation, 'search');
|
|
68
|
+
if (typeof search === 'string') {
|
|
69
|
+
const marker = new URLSearchParams(search).get('__absolute_target');
|
|
70
|
+
if (marker === 'capacitor-android') return marker;
|
|
71
|
+
if (marker === 'capacitor-ios') return marker;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const capacitor = Reflect.get(globalThis, 'Capacitor');
|
|
75
|
+
if (!isRecord(capacitor)) return 'web';
|
|
76
|
+
const getPlatform = Reflect.get(capacitor, 'getPlatform');
|
|
77
|
+
const isNativePlatform = Reflect.get(capacitor, 'isNativePlatform');
|
|
78
|
+
const platform =
|
|
79
|
+
typeof getPlatform === 'function'
|
|
80
|
+
? Reflect.apply(getPlatform, capacitor, [])
|
|
81
|
+
: undefined;
|
|
82
|
+
if (platform === 'android') return 'capacitor-android';
|
|
83
|
+
if (platform === 'ios') return 'capacitor-ios';
|
|
84
|
+
if (
|
|
85
|
+
typeof isNativePlatform === 'function' &&
|
|
86
|
+
Reflect.apply(isNativePlatform, capacitor, []) === true
|
|
87
|
+
) {
|
|
88
|
+
return 'capacitor-native';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return 'web';
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export const restoreAbsoluteHmrApply = () => {
|
|
95
|
+
try {
|
|
96
|
+
const storage = getSessionStorage();
|
|
97
|
+
const getItem = storage && Reflect.get(storage, 'getItem');
|
|
98
|
+
if (typeof getItem !== 'function') return;
|
|
99
|
+
const serialized = Reflect.apply(getItem, storage, [
|
|
100
|
+
HMR_APPLY_STORAGE_KEY
|
|
101
|
+
]);
|
|
102
|
+
if (typeof serialized !== 'string') return;
|
|
103
|
+
const timing: unknown = JSON.parse(serialized);
|
|
104
|
+
if (!isHmrApply(timing)) return;
|
|
105
|
+
window.__ABS_HMR_LAST_APPLY__ = timing;
|
|
106
|
+
window.__ABS_HMR_APPLIES__ = [timing];
|
|
107
|
+
} catch {
|
|
108
|
+
// A malformed or inaccessible entry should never affect HMR startup.
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
type SendHmrTimingOptions = {
|
|
113
|
+
clientStart: number;
|
|
114
|
+
kind?: HMRApplyKind;
|
|
115
|
+
outcome?: HMRApplyOutcome;
|
|
116
|
+
serverMs?: number;
|
|
117
|
+
updateId?: number;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export const sendAbsoluteHmrTiming = (options: SendHmrTimingOptions) => {
|
|
121
|
+
const clientMs = Math.round(performance.now() - options.clientStart);
|
|
122
|
+
const serverMs = options.serverMs ?? 0;
|
|
123
|
+
const timing = {
|
|
124
|
+
clientMs,
|
|
125
|
+
duration: serverMs + clientMs,
|
|
126
|
+
kind: options.kind,
|
|
127
|
+
outcome: options.outcome ?? 'applied',
|
|
128
|
+
serverMs,
|
|
129
|
+
target: absoluteHmrClientTarget(),
|
|
130
|
+
updateId: options.updateId
|
|
131
|
+
} satisfies NonNullable<Window['__ABS_HMR_LAST_APPLY__']>;
|
|
132
|
+
window.__ABS_HMR_LAST_APPLY__ = timing;
|
|
133
|
+
const applies = (window.__ABS_HMR_APPLIES__ ??= []);
|
|
134
|
+
applies.push(timing);
|
|
135
|
+
if (applies.length > MAX_RETAINED_HMR_APPLIES) {
|
|
136
|
+
applies.splice(0, applies.length - MAX_RETAINED_HMR_APPLIES);
|
|
137
|
+
}
|
|
138
|
+
persistHmrApply(timing);
|
|
139
|
+
if (!window.__HMR_WS__) return;
|
|
140
|
+
window.__HMR_WS__.send(JSON.stringify({ ...timing, type: 'hmr-timing' }));
|
|
141
|
+
};
|
|
@@ -18,19 +18,28 @@ var removeEntryCopy = (path) => {
|
|
|
18
18
|
unlinkSync(path);
|
|
19
19
|
} catch {}
|
|
20
20
|
};
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
var isHotReevaluation = globalThis.__absoluteEntryCopies !== undefined;
|
|
22
|
+
if (!isHotReevaluation) {
|
|
23
|
+
for (const name of readdirSync(entryDir)) {
|
|
24
|
+
if (!name.startsWith(copyPrefix) || !name.endsWith(entryExtension)) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
removeEntryCopy(join(entryDir, name));
|
|
24
28
|
}
|
|
25
|
-
removeEntryCopy(join(entryDir, name));
|
|
26
29
|
}
|
|
27
|
-
var
|
|
30
|
+
var bootstrapSequence = (globalThis.__absoluteEntryBootstrapSequence ?? 0) + 1;
|
|
31
|
+
globalThis.__absoluteEntryBootstrapSequence = bootstrapSequence;
|
|
32
|
+
var bootstrapCopy = join(entryDir, `${copyPrefix}${process.pid}-bootstrap-${bootstrapSequence}${entryExtension}`);
|
|
28
33
|
copyFileSync(entryPath, bootstrapCopy);
|
|
29
|
-
var entryCopies = new Set
|
|
34
|
+
var entryCopies = globalThis.__absoluteEntryCopies ?? new Set;
|
|
35
|
+
entryCopies.add(bootstrapCopy);
|
|
30
36
|
globalThis.__absoluteEntryCopies = entryCopies;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
if (!globalThis.__absoluteEntryCleanupRegistered) {
|
|
38
|
+
globalThis.__absoluteEntryCleanupRegistered = true;
|
|
39
|
+
process.on("exit", () => {
|
|
40
|
+
for (const path of entryCopies) {
|
|
41
|
+
removeEntryCopy(path);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
36
45
|
await import(bootstrapCopy);
|