@absolutejs/absolute 0.15.8 → 0.15.10

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.
@@ -3,6 +3,7 @@
3
3
  import type { ErrorOverlayOptions } from '../../../types/client';
4
4
 
5
5
  let errorOverlayElement: HTMLDivElement | null = null;
6
+ let currentOverlayKind: 'compilation' | 'runtime' | null = null;
6
7
 
7
8
  const frameworkLabels: Record<string, string> = {
8
9
  angular: 'Angular',
@@ -26,13 +27,32 @@ const frameworkColors: Record<string, string> = {
26
27
  vue: '#42b883'
27
28
  };
28
29
 
29
- export const hideErrorOverlay = () => {
30
+ const removeOverlayElement = () => {
30
31
  if (errorOverlayElement && errorOverlayElement.parentNode) {
31
32
  errorOverlayElement.parentNode.removeChild(errorOverlayElement);
32
- errorOverlayElement = null;
33
33
  }
34
+ errorOverlayElement = null;
35
+ currentOverlayKind = null;
36
+ };
37
+
38
+ export const hideErrorOverlay = () => {
39
+ const elm = errorOverlayElement;
40
+ if (!elm || !elm.parentNode) {
41
+ removeOverlayElement();
42
+ return;
43
+ }
44
+ elm.style.transition = 'opacity 150ms ease-out';
45
+ elm.style.opacity = '0';
46
+ errorOverlayElement = null;
47
+ currentOverlayKind = null;
48
+ setTimeout(() => {
49
+ if (elm.parentNode) elm.parentNode.removeChild(elm);
50
+ }, 150);
34
51
  };
35
52
 
53
+ export const isRuntimeErrorOverlay = (): boolean =>
54
+ currentOverlayKind === 'runtime';
55
+
36
56
  export const showErrorOverlay = (opts: ErrorOverlayOptions) => {
37
57
  const message = opts.message || 'Build failed';
38
58
  const file = opts.file;
@@ -43,7 +63,8 @@ export const showErrorOverlay = (opts: ErrorOverlayOptions) => {
43
63
  const frameworkLabel = frameworkLabels[framework] || framework;
44
64
  const accent = frameworkColors[framework] || '#94a3b8';
45
65
 
46
- hideErrorOverlay();
66
+ removeOverlayElement();
67
+ currentOverlayKind = opts.kind || 'compilation';
47
68
 
48
69
  const overlay = document.createElement('div');
49
70
  overlay.id = 'absolutejs-error-overlay';
@@ -63,7 +84,9 @@ export const showErrorOverlay = (opts: ErrorOverlayOptions) => {
63
84
  accent +
64
85
  ';color:#fff;opacity:0.95;box-shadow:0 2px 4px rgba(0,0,0,0.2);">' +
65
86
  frameworkLabel +
66
- '</span></div><span style="color:#94a3b8;font-size:13px;font-weight:500;">Compilation Error</span>';
87
+ '</span></div><span style="color:#94a3b8;font-size:13px;font-weight:500;">' +
88
+ (opts.kind === 'runtime' ? 'Runtime Error' : 'Compilation Error') +
89
+ '</span>';
67
90
  card.appendChild(header);
68
91
 
69
92
  const content = document.createElement('div');
@@ -3,6 +3,7 @@
3
3
  Code splitting ensures React lives in a shared chunk that stays cached,
4
4
  so dynamic import of the rebuilt entry reuses the same React instance. */
5
5
 
6
+ import { hideErrorOverlay } from '../errorOverlay';
6
7
  import { detectCurrentFramework } from '../frameworkDetect';
7
8
 
8
9
  export const handleReactUpdate = (message: {
@@ -39,6 +40,11 @@ export const handleReactUpdate = (message: {
39
40
  import(newUrl + '?t=' + Date.now())
40
41
  .then(() => {
41
42
  refreshRuntime.performReactRefresh();
43
+ if (window.__ERROR_BOUNDARY__) {
44
+ window.__ERROR_BOUNDARY__.reset();
45
+ } else {
46
+ hideErrorOverlay();
47
+ }
42
48
  })
43
49
  .catch((err) => {
44
50
  console.warn(
@@ -1,6 +1,10 @@
1
1
  /* Rebuild, manifest, module-update, and error handlers */
2
2
 
3
- import { hideErrorOverlay, showErrorOverlay } from '../errorOverlay';
3
+ import {
4
+ hideErrorOverlay,
5
+ isRuntimeErrorOverlay,
6
+ showErrorOverlay
7
+ } from '../errorOverlay';
4
8
 
5
9
  export const handleManifest = (message: {
6
10
  data: {
@@ -29,7 +33,9 @@ export const handleRebuildComplete = (message: {
29
33
  manifest?: Record<string, string>;
30
34
  };
31
35
  }) => {
32
- hideErrorOverlay();
36
+ if (!isRuntimeErrorOverlay()) {
37
+ hideErrorOverlay();
38
+ }
33
39
  if (window.__HMR_MANIFEST__) {
34
40
  window.__HMR_MANIFEST__ = message.data.manifest;
35
41
  }
@@ -5,6 +5,7 @@ import '../../../types/client'; // Window global type extensions
5
5
 
6
6
  import { hmrState } from '../../../types/client';
7
7
  import { detectCurrentFramework } from './frameworkDetect';
8
+ import { hideErrorOverlay, showErrorOverlay } from './errorOverlay';
8
9
  import { handleReactUpdate } from './handlers/react';
9
10
  import { handleHTMLUpdate, handleScriptUpdate } from './handlers/html';
10
11
  import { handleHTMXUpdate } from './handlers/htmx';
@@ -34,6 +35,31 @@ if (typeof window !== 'undefined') {
34
35
  }
35
36
  }
36
37
 
38
+ // Catch uncaught runtime errors and show the error overlay
39
+ window.addEventListener('error', function (evt) {
40
+ if (!evt.error) return;
41
+ showErrorOverlay({
42
+ framework: detectCurrentFramework() || undefined,
43
+ kind: 'runtime',
44
+ message:
45
+ evt.error instanceof Error
46
+ ? evt.error.stack || evt.error.message
47
+ : String(evt.error)
48
+ });
49
+ });
50
+
51
+ window.addEventListener('unhandledrejection', function (evt) {
52
+ if (!evt.reason) return;
53
+ showErrorOverlay({
54
+ framework: detectCurrentFramework() || undefined,
55
+ kind: 'runtime',
56
+ message:
57
+ evt.reason instanceof Error
58
+ ? evt.reason.stack || evt.reason.message
59
+ : String(evt.reason)
60
+ });
61
+ });
62
+
37
63
  // Prevent multiple WebSocket connections
38
64
  if (!(window.__HMR_WS__ && window.__HMR_WS__.readyState === WebSocket.OPEN)) {
39
65
  // Determine WebSocket URL
@@ -105,6 +131,7 @@ if (!(window.__HMR_WS__ && window.__HMR_WS__.readyState === WebSocket.OPEN)) {
105
131
  break;
106
132
 
107
133
  case 'module-update':
134
+ hideErrorOverlay();
108
135
  handleModuleUpdate(message);
109
136
  break;
110
137
 
@@ -113,22 +140,27 @@ if (!(window.__HMR_WS__ && window.__HMR_WS__.readyState === WebSocket.OPEN)) {
113
140
  break;
114
141
 
115
142
  case 'script-update':
143
+ hideErrorOverlay();
116
144
  handleScriptUpdate(message);
117
145
  break;
118
146
 
119
147
  case 'html-update':
148
+ hideErrorOverlay();
120
149
  handleHTMLUpdate(message);
121
150
  break;
122
151
 
123
152
  case 'htmx-update':
153
+ hideErrorOverlay();
124
154
  handleHTMXUpdate(message);
125
155
  break;
126
156
 
127
157
  case 'svelte-update':
158
+ hideErrorOverlay();
128
159
  handleSvelteUpdate(message);
129
160
  break;
130
161
 
131
162
  case 'vue-update':
163
+ hideErrorOverlay();
132
164
  handleVueUpdate(message);
133
165
  break;
134
166
 
@@ -195,6 +227,7 @@ if (!(window.__HMR_WS__ && window.__HMR_WS__.readyState === WebSocket.OPEN)) {
195
227
  if (hmrState.pingInterval) clearInterval(hmrState.pingInterval);
196
228
  if (hmrState.reconnectTimeout)
197
229
  clearTimeout(hmrState.reconnectTimeout);
230
+
198
231
  return;
199
232
  }
200
233
 
package/dist/index.js CHANGED
@@ -959,6 +959,7 @@ var devClientDir3 = (() => {
959
959
  return fromSource;
960
960
  return resolve3(import.meta.dir, "./dev/client");
961
961
  })();
962
+ var errorOverlayPath = join3(devClientDir3, "errorOverlay.ts").replace(/\\/g, "/");
962
963
  var hmrClientPath3 = join3(devClientDir3, "hmrClient.ts").replace(/\\/g, "/");
963
964
  var refreshSetupPath = join3(devClientDir3, "reactRefreshSetup.ts").replace(/\\/g, "/");
964
965
  var generateReactIndexFiles = async (reactPagesDirectory, reactIndexesDirectory, isDev = false) => {
@@ -976,13 +977,55 @@ var generateReactIndexFiles = async (reactPagesDirectory, reactIndexesDirectory,
976
977
  `window.__HMR_FRAMEWORK__ = "react";`,
977
978
  `window.__REACT_COMPONENT_KEY__ = "${componentName}Index";`,
978
979
  `import '${refreshSetupPath}';`,
979
- `import '${hmrClientPath3}';
980
+ `import '${hmrClientPath3}';`,
981
+ `import { showErrorOverlay, hideErrorOverlay } from '${errorOverlayPath}';
982
+ `
983
+ ] : [];
984
+ const reactImports = isDev ? [
985
+ `import { hydrateRoot, createRoot } from 'react-dom/client';`,
986
+ `import { createElement, Component } from 'react';`
987
+ ] : [
988
+ `import { hydrateRoot, createRoot } from 'react-dom/client';`,
989
+ `import { createElement } from 'react';`
990
+ ];
991
+ const errorBoundaryDef = isDev ? [
992
+ `
993
+ // Dev-only Error Boundary to catch React render errors`,
994
+ `class ErrorBoundary extends Component {`,
995
+ ` constructor(props) {`,
996
+ ` super(props);`,
997
+ ` this.state = { hasError: false };`,
998
+ ` window.__ERROR_BOUNDARY__ = this;`,
999
+ ` }`,
1000
+ ` static getDerivedStateFromError() {`,
1001
+ ` return { hasError: true };`,
1002
+ ` }`,
1003
+ ` componentDidCatch(error) {`,
1004
+ ` showErrorOverlay({`,
1005
+ ` framework: 'react',`,
1006
+ ` kind: 'runtime',`,
1007
+ ` message: error && error.stack ? error.stack : String(error)`,
1008
+ ` });`,
1009
+ ` }`,
1010
+ ` componentDidUpdate(prevProps, prevState) {`,
1011
+ ` if (prevState.hasError && !this.state.hasError) {`,
1012
+ ` hideErrorOverlay();`,
1013
+ ` }`,
1014
+ ` }`,
1015
+ ` reset() {`,
1016
+ ` this.setState({ hasError: false });`,
1017
+ ` }`,
1018
+ ` render() {`,
1019
+ ` if (this.state.hasError) return null;`,
1020
+ ``,
1021
+ ` return this.props.children;`,
1022
+ ` }`,
1023
+ `}
980
1024
  `
981
1025
  ] : [];
982
1026
  const content = [
983
1027
  ...hmrPreamble,
984
- `import { hydrateRoot, createRoot } from 'react-dom/client';`,
985
- `import { createElement } from 'react';`,
1028
+ ...reactImports,
986
1029
  `import type { ComponentType } from 'react'`,
987
1030
  `import { ${componentName} } from '../pages/${componentName}';
988
1031
  `,
@@ -996,6 +1039,7 @@ var generateReactIndexFiles = async (reactPagesDirectory, reactIndexesDirectory,
996
1039
  ` }`,
997
1040
  `}
998
1041
  `,
1042
+ ...errorBoundaryDef,
999
1043
  `// Hydration with error handling and fallback`,
1000
1044
  `const isDev = ${isDev};`,
1001
1045
  `const componentPath = '../pages/${componentName}';
@@ -1070,7 +1114,7 @@ var generateReactIndexFiles = async (reactPagesDirectory, reactIndexesDirectory,
1070
1114
  `,
1071
1115
  ` // Render into the same root container when falling back to client-only`,
1072
1116
  ` const root = createRoot(container);`,
1073
- ` root.render(createElement(${componentName}, mergedProps));`,
1117
+ ` root.render(${isDev ? `createElement(ErrorBoundary, null, createElement(${componentName}, mergedProps))` : `createElement(${componentName}, mergedProps)`});`,
1074
1118
  ` window.__REACT_ROOT__ = root;`,
1075
1119
  ` window.__HMR_CLIENT_ONLY_MODE__ = true;`,
1076
1120
  ` } catch (fallbackError) {`,
@@ -1116,7 +1160,7 @@ var generateReactIndexFiles = async (reactPagesDirectory, reactIndexesDirectory,
1116
1160
  ` // Use onRecoverableError to catch hydration errors (React 19)`,
1117
1161
  ` root = hydrateRoot(`,
1118
1162
  ` container,`,
1119
- ` createElement(${componentName}, mergedProps),`,
1163
+ ` ${isDev ? `createElement(ErrorBoundary, null, createElement(${componentName}, mergedProps))` : `createElement(${componentName}, mergedProps)`},`,
1120
1164
  ` {`,
1121
1165
  ` onRecoverableError: (error) => {`,
1122
1166
  ` // Check if this is a hydration error (isHydrationError filters out whitespace-only head mismatches)`,
@@ -2886,7 +2930,6 @@ var triggerRebuild = async (state, config, onRebuildComplete, filesToRebuild) =>
2886
2930
  options: {
2887
2931
  ...config.options,
2888
2932
  injectHMR: true,
2889
- preserveIntermediateFiles: true,
2890
2933
  throwOnError: true
2891
2934
  }
2892
2935
  });
@@ -3322,8 +3365,7 @@ var devBuild = async (config) => {
3322
3365
  ...config,
3323
3366
  options: {
3324
3367
  ...config.options,
3325
- injectHMR: true,
3326
- preserveIntermediateFiles: true
3368
+ injectHMR: true
3327
3369
  }
3328
3370
  });
3329
3371
  if (!manifest || Object.keys(manifest).length === 0) {
@@ -3594,5 +3636,5 @@ export {
3594
3636
  BUN_BUILD_WARNING_SUPPRESSION
3595
3637
  };
3596
3638
 
3597
- //# debugId=8FDC61834737AD4664756E2164756E21
3639
+ //# debugId=D00BE7A74B5C034564756E2164756E21
3598
3640
  //# sourceMappingURL=index.js.map