@absolutejs/absolute 0.19.0-beta.4 → 0.19.0-beta.40

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/cli/index.js CHANGED
@@ -598,7 +598,7 @@ var dev = async (serverEntry, configPath2) => {
598
598
  interactive?.showPrompt();
599
599
  };
600
600
  const spawnServer = () => {
601
- const proc = Bun.spawn(["bun", "--hot", "--no-clear-screen", serverEntry], {
601
+ const proc = Bun.spawn(["bun", "--no-clear-screen", serverEntry], {
602
602
  cwd: process.cwd(),
603
603
  env: {
604
604
  ...process.env,
@@ -633,6 +633,7 @@ var dev = async (serverEntry, configPath2) => {
633
633
  let serverProcess = spawnServer();
634
634
  const sessionStart = Date.now();
635
635
  let frameworks = [];
636
+ let frameworkDirs = [];
636
637
  try {
637
638
  const cfg = await loadConfig(configPath2);
638
639
  frameworks = [
@@ -643,7 +644,34 @@ var dev = async (serverEntry, configPath2) => {
643
644
  cfg.vueDirectory && "vue",
644
645
  cfg.angularDirectory && "angular"
645
646
  ].filter((val) => Boolean(val));
647
+ frameworkDirs = [
648
+ cfg.reactDirectory,
649
+ cfg.htmlDirectory,
650
+ cfg.htmxDirectory,
651
+ cfg.svelteDirectory,
652
+ cfg.vueDirectory,
653
+ cfg.angularDirectory
654
+ ].filter((val) => Boolean(val)).map((dir) => resolve3(dir));
646
655
  } catch {}
656
+ const { watch } = await import("fs");
657
+ const serverDir = resolve3(serverEntry, "..");
658
+ const isFrameworkFile = (filePath) => frameworkDirs.some((dir) => resolve3(filePath).startsWith(dir));
659
+ let restartTimeout = null;
660
+ watch(serverDir, { recursive: true }, (_event, filename) => {
661
+ if (!filename)
662
+ return;
663
+ const fullPath = resolve3(serverDir, filename);
664
+ if (isFrameworkFile(fullPath))
665
+ return;
666
+ if (!filename.endsWith(".ts") && !filename.endsWith(".tsx"))
667
+ return;
668
+ if (restartTimeout)
669
+ clearTimeout(restartTimeout);
670
+ restartTimeout = setTimeout(() => {
671
+ console.log(`\x1B[2m${formatTimestamp()}\x1B[0m \x1B[36m[cli]\x1B[0m \x1B[36mServer file changed, restarting...\x1B[0m`);
672
+ restartServer();
673
+ }, 100);
674
+ });
647
675
  sendTelemetryEvent("dev:start", { entry: serverEntry, frameworks });
648
676
  const cleanup = async (exitCode = 0) => {
649
677
  if (cleaning)
@@ -2,15 +2,14 @@
2
2
 
3
3
  export const detectCurrentFramework = () => {
4
4
  if (window.__HMR_FRAMEWORK__) return window.__HMR_FRAMEWORK__;
5
+ if (window.__REACT_ROOT__) return 'react';
5
6
  const path = window.location.pathname;
6
7
  if (path === '/vue' || path.startsWith('/vue/')) return 'vue';
7
8
  if (path === '/svelte' || path.startsWith('/svelte/')) return 'svelte';
8
9
  if (path === '/angular' || path.startsWith('/angular/')) return 'angular';
9
10
  if (path === '/htmx' || path.startsWith('/htmx/')) return 'htmx';
10
11
  if (path === '/html' || path.startsWith('/html/')) return 'html';
11
- if (path === '/') return 'html';
12
12
  if (path === '/react' || path.startsWith('/react/')) return 'react';
13
- if (window.__REACT_ROOT__) return 'react';
14
13
 
15
14
  return null;
16
15
  };
@@ -8,11 +8,13 @@ import { detectCurrentFramework } from '../frameworkDetect';
8
8
 
9
9
  export const handleReactUpdate = (message: {
10
10
  data: {
11
+ code?: string;
11
12
  hasCSSChanges?: boolean;
12
13
  hasComponentChanges?: boolean;
13
14
  manifest?: Record<string, string>;
14
15
  pageModuleUrl?: string;
15
16
  primarySource?: string;
17
+ serverDuration?: number;
16
18
  };
17
19
  }) => {
18
20
  const currentFramework = detectCurrentFramework();
@@ -31,12 +33,21 @@ export const handleReactUpdate = (message: {
31
33
  }
32
34
 
33
35
  const refreshRuntime = window.$RefreshRuntime$;
36
+ const serverDuration = message.data.serverDuration;
34
37
 
35
- // ESM fast path: import the page module directly (no index re-import)
38
+ // Inline code path: transpiled code sent via WebSocket.
39
+ // Import from blob URL — no HTTP fetch, immune to bun --hot restarts.
40
+ if (message.data.code && refreshRuntime) {
41
+ applyInlineCode(message.data.code, refreshRuntime, serverDuration);
42
+
43
+ return;
44
+ }
45
+
46
+ // ESM fast path: import the page module directly
36
47
  const pageModuleUrl = message.data.pageModuleUrl;
37
48
 
38
49
  if (pageModuleUrl && refreshRuntime) {
39
- applyRefreshImport(pageModuleUrl, refreshRuntime);
50
+ applyRefreshImport(pageModuleUrl, refreshRuntime, serverDuration);
40
51
 
41
52
  return;
42
53
  }
@@ -46,7 +57,7 @@ export const handleReactUpdate = (message: {
46
57
  const newUrl = componentKey && message.data.manifest?.[componentKey];
47
58
 
48
59
  if (newUrl && refreshRuntime) {
49
- applyRefreshImport(newUrl, refreshRuntime);
60
+ applyRefreshImport(newUrl, refreshRuntime, serverDuration);
50
61
 
51
62
  return;
52
63
  }
@@ -55,13 +66,94 @@ export const handleReactUpdate = (message: {
55
66
  window.location.reload();
56
67
  };
57
68
 
69
+ // Import transpiled code from a blob URL — no HTTP fetch needed.
70
+ // Blob URLs resolve absolute imports (like /react/vendor/react.js)
71
+ // against the page's origin, so vendor imports work correctly.
72
+ const applyInlineCode = (
73
+ code: string,
74
+ refreshRuntime: { performReactRefresh: () => void },
75
+ serverDuration?: number
76
+ ) => {
77
+ const clientStart = performance.now();
78
+
79
+ // Convert absolute paths to full URLs so blob can resolve them
80
+ const origin = window.location.origin;
81
+ const fullCode = code.replace(
82
+ /(from\s*["'])(\/[^"']+)(["'])/g,
83
+ `$1${origin}$2$3`
84
+ );
85
+
86
+ const blob = new Blob([fullCode], { type: 'text/javascript' });
87
+ const blobUrl = URL.createObjectURL(blob);
88
+
89
+ import(blobUrl)
90
+ .then(() => {
91
+ URL.revokeObjectURL(blobUrl);
92
+ refreshRuntime.performReactRefresh();
93
+
94
+ if (window.__HMR_WS__) {
95
+ const fetchMs = Math.round(performance.now() - clientStart);
96
+ const total = (serverDuration ?? 0) + fetchMs;
97
+ window.__HMR_WS__.send(
98
+ JSON.stringify({
99
+ duration: total,
100
+ fetchMs,
101
+ refreshMs: 0,
102
+ serverMs: serverDuration ?? 0,
103
+ type: 'hmr-timing'
104
+ })
105
+ );
106
+ }
107
+
108
+ if (window.__ERROR_BOUNDARY__) {
109
+ window.__ERROR_BOUNDARY__.reset();
110
+ } else {
111
+ hideErrorOverlay();
112
+ }
113
+
114
+ return undefined;
115
+ })
116
+ .catch((err) => {
117
+ URL.revokeObjectURL(blobUrl);
118
+ console.warn(
119
+ '[HMR] Inline code failed, falling back to fetch:',
120
+ err
121
+ );
122
+ applyRefreshImport(
123
+ '',
124
+ refreshRuntime,
125
+ serverDuration
126
+ );
127
+ });
128
+ };
129
+
58
130
  const applyRefreshImport = (
59
131
  moduleUrl: string,
60
- refreshRuntime: { performReactRefresh: () => void }
132
+ refreshRuntime: { performReactRefresh: () => void },
133
+ serverDuration?: number
61
134
  ) => {
135
+ const clientStart = performance.now();
62
136
  import(`${moduleUrl}?t=${Date.now()}`)
63
137
  .then(() => {
138
+ const fetchDone = performance.now();
64
139
  refreshRuntime.performReactRefresh();
140
+ const refreshDone = performance.now();
141
+
142
+ if (window.__HMR_WS__) {
143
+ const fetchMs = Math.round(fetchDone - clientStart);
144
+ const refreshMs = Math.round(refreshDone - fetchDone);
145
+ const total = (serverDuration ?? 0) + fetchMs + refreshMs;
146
+ window.__HMR_WS__.send(
147
+ JSON.stringify({
148
+ duration: total,
149
+ fetchMs,
150
+ refreshMs,
151
+ serverMs: serverDuration ?? 0,
152
+ type: 'hmr-timing'
153
+ })
154
+ );
155
+ }
156
+
65
157
  if (window.__ERROR_BOUNDARY__) {
66
158
  window.__ERROR_BOUNDARY__.reset();
67
159
  } else {