@absolutejs/absolute 0.20.0-beta.176 → 0.20.0-beta.178

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.
Files changed (82) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/changelog.json +62 -0
  3. package/dist/angular/browser.js +17 -2
  4. package/dist/angular/browser.js.map +4 -4
  5. package/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
  6. package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
  7. package/dist/angular/index.js +57 -33
  8. package/dist/angular/index.js.map +9 -8
  9. package/dist/angular/server.js +57 -33
  10. package/dist/angular/server.js.map +9 -8
  11. package/dist/build.js +532 -416
  12. package/dist/build.js.map +15 -15
  13. package/dist/cli/{compile-w2z6c9vt.js → compile-p5yv6ecp.js} +2 -2
  14. package/dist/cli/config/server.js +45 -12
  15. package/dist/cli/{dev-8br5mr7t.js → dev-2hbcsvkp.js} +1 -1
  16. package/dist/cli/{index-420h9hq7.js → index-1ewba8ye.js} +32 -3
  17. package/dist/cli/{index-7z1hh3fg.js → index-9dzdbbfc.js} +1 -1
  18. package/dist/cli/index.js +7 -7
  19. package/dist/cli/{islands-bx67g15t.js → islands-6zgb7501.js} +1 -1
  20. package/dist/cli/{mobile-we5q51ey.js → mobile-c92dh8s2.js} +2 -2
  21. package/dist/cli/{start-srs11bm8.js → start-asxb79rc.js} +2 -2
  22. package/dist/cli/{typecheck-7fvww3b7.js → typecheck-m2j4tkdk.js} +165 -35
  23. package/dist/client/chunks/{index-8m2qq61n.js → index-58svs66p.js} +13 -3
  24. package/dist/client/chunks/{index-8m2qq61n.js.map → index-58svs66p.js.map} +5 -4
  25. package/dist/client/chunks/{islands-mfjmdwnb.js → islands-tcadmgcx.js} +3 -6
  26. package/dist/client/chunks/{islands-mfjmdwnb.js.map → islands-tcadmgcx.js.map} +3 -4
  27. package/dist/client/chunks/{react-sprprqzs.js → react-khr6ewhr.js} +2 -2
  28. package/dist/client/index.js +82 -6
  29. package/dist/client/index.js.map +7 -6
  30. package/dist/dev/client/handlers/islands.ts +2 -0
  31. package/dist/dev/client/handlers/react.ts +92 -111
  32. package/dist/dev/client/handlers/reactUpdateQueue.ts +153 -0
  33. package/dist/index.js +506 -389
  34. package/dist/index.js.map +16 -16
  35. package/dist/islands/browser.js +2 -2
  36. package/dist/islands/browser.js.map +3 -3
  37. package/dist/islands/index.js +13 -6
  38. package/dist/islands/index.js.map +6 -5
  39. package/dist/react/browser.js +113 -12
  40. package/dist/react/browser.js.map +10 -7
  41. package/dist/react/components/index.js +31 -7
  42. package/dist/react/components/index.js.map +5 -4
  43. package/dist/react/components/server/index.js +22 -6
  44. package/dist/react/index.js +190 -46
  45. package/dist/react/index.js.map +15 -11
  46. package/dist/react/server.js +82 -23
  47. package/dist/react/server.js.map +11 -9
  48. package/dist/src/build/compileSvelte.d.ts +12 -1
  49. package/dist/src/build/compileVue.d.ts +1 -0
  50. package/dist/src/build/islandEntries.d.ts +3 -1
  51. package/dist/src/cli/scripts/typecheck.d.ts +8 -0
  52. package/dist/src/cli/scripts/typecheckDiagnostics.d.ts +17 -0
  53. package/dist/src/client/preserveIslandMarkup.d.ts +4 -0
  54. package/dist/src/core/islandManifest.d.ts +6 -0
  55. package/dist/src/core/islandStylesheet.d.ts +17 -0
  56. package/dist/src/react/Island.d.ts +9 -2
  57. package/dist/src/react/IslandStyleLink.d.ts +11 -0
  58. package/dist/src/react/createIsland.d.ts +1 -1
  59. package/dist/src/react/serverComponents/nodeRendererRef.d.ts +13 -0
  60. package/dist/src/utils/requestRenderUrl.d.ts +1 -0
  61. package/dist/src/utils/resolveConvention.d.ts +1 -0
  62. package/dist/src/vue/Island.browser.d.ts +3 -1
  63. package/dist/src/vue/Island.d.ts +2 -2
  64. package/dist/src/vue/createIsland.browser.d.ts +2 -2
  65. package/dist/src/vue/createIsland.d.ts +2 -2
  66. package/dist/src/vue/islandStyleLink.d.ts +30 -0
  67. package/dist/svelte/browser.js +2 -2
  68. package/dist/svelte/browser.js.map +3 -3
  69. package/dist/svelte/index.js +51 -49
  70. package/dist/svelte/index.js.map +9 -8
  71. package/dist/svelte/server.js +48 -45
  72. package/dist/svelte/server.js.map +8 -7
  73. package/dist/types/globals.d.ts +3 -0
  74. package/dist/vue/browser.js +105 -10
  75. package/dist/vue/browser.js.map +10 -7
  76. package/dist/vue/index.js +170 -47
  77. package/dist/vue/index.js.map +14 -11
  78. package/dist/vue/server.js +81 -22
  79. package/dist/vue/server.js.map +9 -7
  80. package/package.json +1 -1
  81. /package/dist/cli/{index-6522t968.js → index-yd672jyq.js} +0 -0
  82. /package/dist/client/chunks/{react-sprprqzs.js.map → react-khr6ewhr.js.map} +0 -0
@@ -7,6 +7,10 @@ import type {} from '../../../types/globals';
7
7
  import { hideErrorOverlay } from '../errorOverlay';
8
8
  import { detectCurrentFramework } from '../frameworkDetect';
9
9
  import { sendAbsoluteHmrTiming } from '../hmrTiming';
10
+ import {
11
+ createReactUpdateQueue,
12
+ type ReactUpdateTiming
13
+ } from './reactUpdateQueue';
10
14
 
11
15
  const reloadReactPage = () => {
12
16
  const url = new URL(window.location.href);
@@ -14,6 +18,68 @@ const reloadReactPage = () => {
14
18
  window.location.replace(url.href);
15
19
  };
16
20
 
21
+ const isModuleRecord = (value: unknown): value is Record<string, unknown> =>
22
+ typeof value === 'object' && value !== null;
23
+
24
+ const reportReloaded = (timings: ReactUpdateTiming[]) => {
25
+ for (const timing of timings)
26
+ sendAbsoluteHmrTiming({
27
+ clientStart: timing.clientStart,
28
+ kind: 'component',
29
+ outcome: 'reloaded',
30
+ serverMs: timing.serverDuration,
31
+ updateId: timing.updateId
32
+ });
33
+ };
34
+
35
+ /* One queue per page: an update that arrives while another is still
36
+ * applying waits for it, and the newest modules are the ones applied (see
37
+ * reactUpdateQueue.ts). */
38
+ const reactUpdates = createReactUpdateQueue({
39
+ apply: (update, modules) => {
40
+ if (update.kind === 'remount') {
41
+ const [module] = modules;
42
+ const remount = window.__ABS_REACT_REMOUNT__;
43
+ if (!remount || !isModuleRecord(module))
44
+ throw new Error('React remount is unavailable');
45
+ remount(module);
46
+
47
+ return;
48
+ }
49
+ const refreshRuntime = window.$RefreshRuntime$;
50
+ if (!refreshRuntime)
51
+ throw new Error('React Fast Refresh runtime is unavailable');
52
+ // Every edited module has run by now, so a page that now renders a
53
+ // new child refreshes with it.
54
+ refreshRuntime.performReactRefresh();
55
+ },
56
+ importModule: (url) => import(url),
57
+ onApplied: (_update, timings) => {
58
+ for (const timing of timings)
59
+ sendAbsoluteHmrTiming({
60
+ clientStart: timing.clientStart,
61
+ kind: 'component',
62
+ serverMs: timing.serverDuration,
63
+ updateId: timing.updateId
64
+ });
65
+ if (window.__ERROR_BOUNDARY__) {
66
+ window.__ERROR_BOUNDARY__.reset();
67
+ }
68
+ hideErrorOverlay();
69
+ },
70
+ onFailed: (update, timings, error) => {
71
+ console.warn(
72
+ update.kind === 'remount'
73
+ ? '[HMR] React remount failed, falling back to reload:'
74
+ : '[HMR] React Fast Refresh failed, falling back to reload:',
75
+ error
76
+ );
77
+ reportReloaded(timings);
78
+ if (update.kind === 'remount') reloadReactPage();
79
+ else window.location.reload();
80
+ }
81
+ });
82
+
17
83
  export const handleReactUpdate = (message: {
18
84
  data: {
19
85
  fastRefreshSupported?: boolean;
@@ -30,6 +96,8 @@ export const handleReactUpdate = (message: {
30
96
  }) => {
31
97
  const currentFramework = detectCurrentFramework();
32
98
  if (currentFramework !== 'react') return;
99
+ const clientStart = performance.now();
100
+ const { serverDuration } = message.data;
33
101
 
34
102
  // BUN-REACT-REFRESH-LEGACY: the remount/reload fallback for a dev server on
35
103
  // stock Bun, which cannot emit React Fast Refresh registrations. Remove it,
@@ -47,30 +115,25 @@ export const handleReactUpdate = (message: {
47
115
  pageUrls && componentKey
48
116
  ? pageUrls[componentKey]
49
117
  : message.data.pageModuleUrl;
50
- const remount = window.__ABS_REACT_REMOUNT__;
51
- if (remountUrl && remount) {
52
- applyRemountImport(
53
- remountUrl,
54
- remount,
55
- message.data.serverDuration,
56
- message.timestamp
118
+ if (remountUrl && window.__ABS_REACT_REMOUNT__) {
119
+ reactUpdates.enqueue(
120
+ {
121
+ kind: 'remount',
122
+ moduleUrls: [remountUrl],
123
+ serverDuration,
124
+ updateId: message.timestamp
125
+ },
126
+ clientStart
57
127
  );
58
128
  } else {
59
- const clientStart = performance.now();
60
- sendAbsoluteHmrTiming({
61
- clientStart,
62
- kind: 'component',
63
- outcome: 'reloaded',
64
- serverMs: message.data.serverDuration,
65
- updateId: message.timestamp
66
- });
129
+ reportReloaded([
130
+ { clientStart, serverDuration, updateId: message.timestamp }
131
+ ]);
67
132
  reloadReactPage();
68
133
  }
69
134
 
70
135
  return;
71
136
  }
72
- const refreshRuntime = window.$RefreshRuntime$;
73
- const { serverDuration } = message.data;
74
137
  const { pageModuleUrl } = message.data;
75
138
  const fallbackUrls = pageModuleUrl ? [pageModuleUrl] : [];
76
139
  const moduleUrls =
@@ -78,105 +141,23 @@ export const handleReactUpdate = (message: {
78
141
  ? message.data.moduleUrls
79
142
  : fallbackUrls;
80
143
 
81
- if (moduleUrls.length > 0 && refreshRuntime) {
82
- applyRefreshImport(
83
- moduleUrls,
84
- refreshRuntime,
85
- serverDuration,
86
- message.timestamp
144
+ if (moduleUrls.length > 0 && window.$RefreshRuntime$) {
145
+ reactUpdates.enqueue(
146
+ {
147
+ kind: 'refresh',
148
+ moduleUrls,
149
+ serverDuration,
150
+ updateId: message.timestamp
151
+ },
152
+ clientStart
87
153
  );
88
154
 
89
155
  return;
90
156
  }
91
157
 
92
158
  // No module URL — shouldn't happen, but reload as safety fallback
93
- const clientStart = performance.now();
94
- sendAbsoluteHmrTiming({
95
- clientStart,
96
- kind: 'component',
97
- outcome: 'reloaded',
98
- serverMs: message.data.serverDuration,
99
- updateId: message.timestamp
100
- });
159
+ reportReloaded([
160
+ { clientStart, serverDuration, updateId: message.timestamp }
161
+ ]);
101
162
  window.location.reload();
102
163
  };
103
-
104
- const finishUpdate = (
105
- clientStart: number,
106
- serverDuration?: number,
107
- updateId?: number
108
- ) => {
109
- sendAbsoluteHmrTiming({
110
- clientStart,
111
- kind: 'component',
112
- serverMs: serverDuration,
113
- updateId
114
- });
115
- if (window.__ERROR_BOUNDARY__) {
116
- window.__ERROR_BOUNDARY__.reset();
117
- }
118
- hideErrorOverlay();
119
- };
120
-
121
- const applyRefreshImport = (
122
- moduleUrls: string[],
123
- refreshRuntime: { performReactRefresh: () => unknown },
124
- serverDuration?: number,
125
- updateId?: number
126
- ) => {
127
- const clientStart = performance.now();
128
- const stamp = Date.now();
129
- // Every edited module has to run before the refresh, or a page that
130
- // now renders a new child refreshes without it.
131
- Promise.all(moduleUrls.map((url) => import(`${url}?t=${stamp}`)))
132
- .then(() => {
133
- refreshRuntime.performReactRefresh();
134
- finishUpdate(clientStart, serverDuration, updateId);
135
-
136
- return undefined;
137
- })
138
- .catch((err) => {
139
- console.warn(
140
- '[HMR] React Fast Refresh failed, falling back to reload:',
141
- err
142
- );
143
- sendAbsoluteHmrTiming({
144
- clientStart,
145
- kind: 'component',
146
- outcome: 'reloaded',
147
- serverMs: serverDuration,
148
- updateId
149
- });
150
- window.location.reload();
151
- });
152
- };
153
-
154
- const applyRemountImport = (
155
- moduleUrl: string,
156
- remount: (module: Record<string, unknown>) => void,
157
- serverDuration?: number,
158
- updateId?: number
159
- ) => {
160
- const clientStart = performance.now();
161
- import(`${moduleUrl}?t=${Date.now()}`)
162
- .then((module) => {
163
- remount(module);
164
- finishUpdate(clientStart, serverDuration, updateId);
165
-
166
- return undefined;
167
- })
168
- .catch((err) => {
169
- console.warn(
170
- '[HMR] React remount failed, falling back to reload:',
171
- err
172
- );
173
- sendAbsoluteHmrTiming({
174
- clientStart,
175
- kind: 'component',
176
- outcome: 'reloaded',
177
- serverMs: serverDuration,
178
- updateId
179
- });
180
- reloadReactPage();
181
- });
182
- };
@@ -0,0 +1,153 @@
1
+ import type {} from '../../../types/globals';
2
+ /* Applies React HMR updates one at a time, newest last.
3
+ *
4
+ * Each update re-imports its modules and then refreshes (or remounts) the
5
+ * page. Run unserialized, two updates for one save raced: both imports were
6
+ * in flight together, each refreshed whenever its own import settled, and a
7
+ * cache-busting stamp taken from `Date.now()` could repeat within a
8
+ * millisecond, handing the second update the module instance the first had
9
+ * already evaluated. Here an update that arrives while another is applying
10
+ * waits; updates that pile up meanwhile merge into one, and an update whose
11
+ * import settles after a newer one arrived is not applied on its own: the
12
+ * newer update re-imports every module of both and applies once. */
13
+
14
+ export type ReactUpdateKind = 'refresh' | 'remount';
15
+
16
+ export type ReactUpdate = {
17
+ kind: ReactUpdateKind;
18
+ moduleUrls: string[];
19
+ serverDuration?: number;
20
+ updateId?: number;
21
+ };
22
+
23
+ export type ReactUpdateTiming = {
24
+ clientStart: number;
25
+ serverDuration?: number;
26
+ updateId?: number;
27
+ };
28
+
29
+ export type ReactUpdateRunner = {
30
+ apply: (update: ReactUpdate, modules: unknown[]) => void;
31
+ importModule: (url: string) => Promise<unknown>;
32
+ now?: () => number;
33
+ onApplied: (update: ReactUpdate, timings: ReactUpdateTiming[]) => void;
34
+ onFailed: (
35
+ update: ReactUpdate,
36
+ timings: ReactUpdateTiming[],
37
+ error: unknown
38
+ ) => void;
39
+ };
40
+
41
+ type Batch = {
42
+ timings: ReactUpdateTiming[];
43
+ update: ReactUpdate;
44
+ };
45
+
46
+ /* A remount renders one page module, so the newer one wins. A refresh
47
+ * needs every edited module re-run: keep the union, the newer update's
48
+ * URLs first (its first URL is the primary one). */
49
+ const mergeUpdates = (older: ReactUpdate, newer: ReactUpdate) =>
50
+ older.kind === 'refresh' && newer.kind === 'refresh'
51
+ ? {
52
+ ...newer,
53
+ moduleUrls: [
54
+ ...new Set([...newer.moduleUrls, ...older.moduleUrls])
55
+ ]
56
+ }
57
+ : newer;
58
+
59
+ const mergeBatches = (older: Batch, newer: Batch): Batch => ({
60
+ timings: [...older.timings, ...newer.timings],
61
+ update: mergeUpdates(older.update, newer.update)
62
+ });
63
+
64
+ const withStamp = (url: string, stamp: number) =>
65
+ `${url}${url.includes('?') ? '&' : '?'}t=${stamp}`;
66
+
67
+ export const createReactUpdateQueue = (runner: ReactUpdateRunner) => {
68
+ const now = runner.now ?? (() => Date.now());
69
+ let pending: Batch | undefined;
70
+ let running = false;
71
+ let lastStamp = 0;
72
+
73
+ // Strictly increasing: a repeated stamp re-imports nothing.
74
+ const nextStamp = () => {
75
+ lastStamp = Math.max(lastStamp + 1, now());
76
+
77
+ return lastStamp;
78
+ };
79
+
80
+ const importAll = (update: ReactUpdate) => {
81
+ const stamp = nextStamp();
82
+
83
+ return Promise.all(
84
+ update.moduleUrls.map((url) =>
85
+ runner.importModule(withStamp(url, stamp))
86
+ )
87
+ );
88
+ };
89
+
90
+ const applyBatch = async (batch: Batch) => {
91
+ let modules: unknown[];
92
+ try {
93
+ modules = await importAll(batch.update);
94
+ } catch (error) {
95
+ // A newer update may already fix what failed to load.
96
+ if (pending) {
97
+ pending = mergeBatches(batch, pending);
98
+
99
+ return;
100
+ }
101
+ runner.onFailed(batch.update, batch.timings, error);
102
+
103
+ return;
104
+ }
105
+ if (pending) {
106
+ // Superseded while loading: the newer update loads these modules
107
+ // again, at their newest version, and applies once.
108
+ pending = mergeBatches(batch, pending);
109
+
110
+ return;
111
+ }
112
+ try {
113
+ runner.apply(batch.update, modules);
114
+ } catch (error) {
115
+ runner.onFailed(batch.update, batch.timings, error);
116
+
117
+ return;
118
+ }
119
+ runner.onApplied(batch.update, batch.timings);
120
+ };
121
+
122
+ const drain = async () => {
123
+ while (pending) {
124
+ const batch = pending;
125
+ pending = undefined;
126
+ // eslint-disable-next-line no-await-in-loop -- one at a time on purpose
127
+ await applyBatch(batch);
128
+ }
129
+ running = false;
130
+ };
131
+
132
+ const enqueue = (update: ReactUpdate, clientStart: number) => {
133
+ const batch: Batch = {
134
+ timings: [
135
+ {
136
+ clientStart,
137
+ serverDuration: update.serverDuration,
138
+ updateId: update.updateId
139
+ }
140
+ ],
141
+ update
142
+ };
143
+ pending = pending ? mergeBatches(pending, batch) : batch;
144
+ if (running) return;
145
+ running = true;
146
+ void drain();
147
+ };
148
+
149
+ return {
150
+ enqueue,
151
+ isIdle: () => !running
152
+ };
153
+ };