@ereo/bundler 0.1.21 → 0.1.23

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/hmr.d.ts CHANGED
@@ -50,7 +50,7 @@ export interface ModuleDependencyGraph {
50
50
  * HMR client code injected into the page.
51
51
  * Supports granular JS updates for islands and components.
52
52
  */
53
- export declare const HMR_CLIENT_CODE = "\n(function() {\n const ws = new WebSocket('ws://' + location.host + '/__hmr');\n\n // Module registry for hot updates\n window.__EREO_HMR__ = window.__EREO_HMR__ || {\n modules: new Map(),\n islands: new Map(),\n acceptedModules: new Set(),\n };\n\n ws.onmessage = function(event) {\n const update = JSON.parse(event.data);\n const startTime = performance.now();\n\n // Log with timing info\n const logUpdate = (msg) => {\n const duration = (performance.now() - startTime).toFixed(1);\n console.log('[HMR] ' + msg + ' (' + duration + 'ms)');\n };\n\n switch (update.type) {\n case 'full-reload':\n logHMRReason(update);\n location.reload();\n break;\n\n case 'css-update':\n updateCSS(update.path);\n logUpdate('CSS updated: ' + update.path);\n break;\n\n case 'island-update':\n if (handleIslandUpdate(update)) {\n logUpdate('Island hot-updated: ' + (update.module?.id || update.path));\n } else {\n logHMRReason(update);\n location.reload();\n }\n break;\n\n case 'component-update':\n if (handleComponentUpdate(update)) {\n logUpdate('Component hot-updated: ' + (update.module?.id || update.path));\n } else {\n logHMRReason(update);\n location.reload();\n }\n break;\n\n case 'loader-update':\n // Loaders require data refetch, do soft reload\n logUpdate('Loader changed, refreshing data...');\n refreshLoaderData(update.path);\n break;\n\n case 'js-update':\n // Check if we can do granular update\n if (update.module?.isIsland && handleIslandUpdate(update)) {\n logUpdate('Island hot-updated: ' + (update.module?.id || update.path));\n } else if (update.module?.isComponent && handleComponentUpdate(update)) {\n logUpdate('Component hot-updated: ' + (update.module?.id || update.path));\n } else {\n logHMRReason(update);\n location.reload();\n }\n break;\n\n case 'error':\n showErrorOverlay(update.error);\n break;\n }\n };\n\n ws.onclose = function() {\n console.log('[HMR] Connection lost, attempting reconnect...');\n setTimeout(function() {\n location.reload();\n }, 1000);\n };\n\n function updateCSS(path) {\n const links = document.querySelectorAll('link[rel=\"stylesheet\"]');\n for (const link of links) {\n if (link.href.includes(path)) {\n const newHref = link.href.split('?')[0] + '?t=' + Date.now();\n link.href = newHref;\n }\n }\n }\n\n function handleIslandUpdate(update) {\n const moduleId = update.module?.id || update.path;\n if (!moduleId) return false;\n\n // Find all island elements for this component\n const componentName = moduleId.split('/').pop()?.replace(/\\.[jt]sx?$/, '');\n if (!componentName) return false;\n\n const islands = document.querySelectorAll('[data-component=\"' + componentName + '\"]');\n if (islands.length === 0) return false;\n\n // Fetch the updated module and re-hydrate islands\n return fetchAndRehydrate(moduleId, islands);\n }\n\n function handleComponentUpdate(update) {\n // For now, component updates trigger a soft reload\n // Future: implement React Fast Refresh integration\n return false;\n }\n\n function fetchAndRehydrate(moduleId, islands) {\n // Dynamic import with cache busting\n const importUrl = '/' + moduleId + '?t=' + Date.now();\n\n import(importUrl)\n .then(function(module) {\n const Component = module.default;\n if (!Component) return;\n\n // Re-render each island\n islands.forEach(function(element) {\n const propsJson = element.getAttribute('data-props');\n const props = propsJson ? JSON.parse(propsJson) : {};\n\n // Use React to re-render\n if (window.__EREO_REACT__) {\n const { createRoot } = window.__EREO_REACT_DOM__;\n const { createElement } = window.__EREO_REACT__;\n\n // Unmount existing\n const existingRoot = window.__EREO_HMR__.islands.get(element);\n if (existingRoot) {\n existingRoot.unmount();\n }\n\n // Create new root and render\n const root = createRoot(element);\n root.render(createElement(Component, props));\n window.__EREO_HMR__.islands.set(element, root);\n }\n });\n })\n .catch(function(err) {\n console.error('[HMR] Failed to hot-update island:', err);\n location.reload();\n });\n\n return true;\n }\n\n function refreshLoaderData(path) {\n // Fetch fresh loader data and update the page\n const routePath = path.replace(/\\/routes\\//, '/').replace(/\\.[jt]sx?$/, '');\n fetch('/__ereo/loader-data' + routePath + '?t=' + Date.now())\n .then(function(res) { return res.json(); })\n .then(function(data) {\n // Emit event for components to update\n window.dispatchEvent(new CustomEvent('ereo:loader-update', {\n detail: { path: routePath, data: data }\n }));\n })\n .catch(function() {\n location.reload();\n });\n }\n\n function logHMRReason(update) {\n if (update.reason) {\n console.log('[HMR] ' + update.reason);\n }\n }\n\n function showErrorOverlay(error) {\n if (!error) return;\n\n let overlay = document.getElementById('ereo-error-overlay');\n if (!overlay) {\n overlay = document.createElement('div');\n overlay.id = 'ereo-error-overlay';\n overlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.9);color:#ff5555;padding:2rem;font-family:monospace;white-space:pre-wrap;overflow:auto;z-index:99999';\n document.body.appendChild(overlay);\n }\n\n overlay.innerHTML = '<h2 style=\"color:#ff5555;margin:0 0 1rem\">Error</h2>' +\n '<p style=\"color:#fff\">' + escapeHtml(error.message) + '</p>' +\n (error.stack ? '<pre style=\"color:#888;margin-top:1rem\">' + escapeHtml(error.stack) + '</pre>' : '') +\n '<button onclick=\"this.parentElement.remove()\" style=\"position:absolute;top:1rem;right:1rem;background:none;border:1px solid #666;color:#fff;padding:0.5rem 1rem;cursor:pointer\">Close</button>';\n }\n\n function escapeHtml(str) {\n return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n }\n\n // Clear error overlay on successful update\n ws.addEventListener('message', function(event) {\n const update = JSON.parse(event.data);\n if (update.type !== 'error') {\n const overlay = document.getElementById('ereo-error-overlay');\n if (overlay) overlay.remove();\n }\n });\n})();\n";
53
+ export declare const HMR_CLIENT_CODE = "\n(function() {\n // Module registry for hot updates\n window.__EREO_HMR__ = window.__EREO_HMR__ || {\n modules: new Map(),\n islands: new Map(),\n acceptedModules: new Set(),\n };\n\n var MAX_RETRIES = 20;\n var retryCount = 0;\n var retryDelay = 1000;\n var intentionalClose = false;\n\n function connect() {\n var ws = new WebSocket('ws://' + location.host + '/__hmr');\n\n ws.onopen = function() {\n if (retryCount > 0) {\n console.log('[HMR] Reconnected after ' + retryCount + ' retries');\n // Server came back \u2014 reload to pick up changes\n location.reload();\n return;\n }\n console.log('[HMR] Connected');\n retryCount = 0;\n retryDelay = 1000;\n };\n\n ws.onmessage = function(event) {\n var update = JSON.parse(event.data);\n var startTime = performance.now();\n\n var logUpdate = function(msg) {\n var duration = (performance.now() - startTime).toFixed(1);\n console.log('[HMR] ' + msg + ' (' + duration + 'ms)');\n };\n\n switch (update.type) {\n case 'full-reload':\n logHMRReason(update);\n location.reload();\n break;\n\n case 'css-update':\n updateCSS(update.path);\n logUpdate('CSS updated: ' + update.path);\n break;\n\n case 'island-update':\n if (handleIslandUpdate(update)) {\n logUpdate('Island hot-updated: ' + (update.module?.id || update.path));\n } else {\n logHMRReason(update);\n location.reload();\n }\n break;\n\n case 'component-update':\n if (handleComponentUpdate(update)) {\n logUpdate('Component hot-updated: ' + (update.module?.id || update.path));\n } else {\n logHMRReason(update);\n location.reload();\n }\n break;\n\n case 'loader-update':\n logUpdate('Loader changed, refreshing data...');\n refreshLoaderData(update.path);\n break;\n\n case 'js-update':\n if (update.module?.isIsland && handleIslandUpdate(update)) {\n logUpdate('Island hot-updated: ' + (update.module?.id || update.path));\n } else if (update.module?.isComponent && handleComponentUpdate(update)) {\n logUpdate('Component hot-updated: ' + (update.module?.id || update.path));\n } else {\n logHMRReason(update);\n location.reload();\n }\n break;\n\n case 'error':\n showErrorOverlay(update.error);\n break;\n }\n\n // Clear error overlay on successful (non-error) updates\n if (update.type !== 'error') {\n var overlay = document.getElementById('ereo-error-overlay');\n if (overlay) overlay.remove();\n }\n };\n\n ws.onclose = function() {\n if (intentionalClose) return;\n if (retryCount >= MAX_RETRIES) {\n console.log('[HMR] Max retries reached, giving up. Reload manually when server is back.');\n return;\n }\n retryCount++;\n console.log('[HMR] Connection lost, retrying (' + retryCount + '/' + MAX_RETRIES + ')...');\n setTimeout(connect, retryDelay);\n retryDelay = Math.min(retryDelay * 1.5, 5000);\n };\n\n ws.onerror = function() {\n // onclose will fire after this\n };\n\n window.addEventListener('beforeunload', function() {\n intentionalClose = true;\n ws.close();\n });\n }\n\n connect();\n\n function updateCSS(path) {\n const links = document.querySelectorAll('link[rel=\"stylesheet\"]');\n for (const link of links) {\n if (link.href.includes(path)) {\n const newHref = link.href.split('?')[0] + '?t=' + Date.now();\n link.href = newHref;\n }\n }\n }\n\n function handleIslandUpdate(update) {\n const moduleId = update.module?.id || update.path;\n if (!moduleId) return false;\n\n // Find all island elements for this component\n const componentName = moduleId.split('/').pop()?.replace(/\\.[jt]sx?$/, '');\n if (!componentName) return false;\n\n const islands = document.querySelectorAll('[data-component=\"' + componentName + '\"]');\n if (islands.length === 0) return false;\n\n // Fetch the updated module and re-hydrate islands\n return fetchAndRehydrate(moduleId, islands);\n }\n\n function handleComponentUpdate(update) {\n // For now, component updates trigger a soft reload\n // Future: implement React Fast Refresh integration\n return false;\n }\n\n function fetchAndRehydrate(moduleId, islands) {\n // Dynamic import with cache busting\n const importUrl = '/' + moduleId + '?t=' + Date.now();\n\n import(importUrl)\n .then(function(module) {\n const Component = module.default;\n if (!Component) return;\n\n // Re-render each island\n islands.forEach(function(element) {\n const propsJson = element.getAttribute('data-props');\n const props = propsJson ? JSON.parse(propsJson) : {};\n\n // Use React to re-render\n if (window.__EREO_REACT__) {\n const { createRoot } = window.__EREO_REACT_DOM__;\n const { createElement } = window.__EREO_REACT__;\n\n // Unmount existing\n const existingRoot = window.__EREO_HMR__.islands.get(element);\n if (existingRoot) {\n existingRoot.unmount();\n }\n\n // Create new root and render\n const root = createRoot(element);\n root.render(createElement(Component, props));\n window.__EREO_HMR__.islands.set(element, root);\n }\n });\n })\n .catch(function(err) {\n console.error('[HMR] Failed to hot-update island:', err);\n location.reload();\n });\n\n return true;\n }\n\n function refreshLoaderData(path) {\n // Fetch fresh loader data and update the page\n const routePath = path.replace(/\\/routes\\//, '/').replace(/\\.[jt]sx?$/, '');\n fetch('/__ereo/loader-data' + routePath + '?t=' + Date.now())\n .then(function(res) { return res.json(); })\n .then(function(data) {\n // Emit event for components to update\n window.dispatchEvent(new CustomEvent('ereo:loader-update', {\n detail: { path: routePath, data: data }\n }));\n })\n .catch(function() {\n location.reload();\n });\n }\n\n function logHMRReason(update) {\n if (update.reason) {\n console.log('[HMR] ' + update.reason);\n }\n }\n\n function showErrorOverlay(error) {\n if (!error) return;\n\n let overlay = document.getElementById('ereo-error-overlay');\n if (!overlay) {\n overlay = document.createElement('div');\n overlay.id = 'ereo-error-overlay';\n overlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.9);color:#ff5555;padding:2rem;font-family:monospace;white-space:pre-wrap;overflow:auto;z-index:99999';\n document.body.appendChild(overlay);\n }\n\n overlay.innerHTML = '<h2 style=\"color:#ff5555;margin:0 0 1rem\">Error</h2>' +\n '<p style=\"color:#fff\">' + escapeHtml(error.message) + '</p>' +\n (error.stack ? '<pre style=\"color:#888;margin-top:1rem\">' + escapeHtml(error.stack) + '</pre>' : '') +\n '<button onclick=\"this.parentElement.remove()\" style=\"position:absolute;top:1rem;right:1rem;background:none;border:1px solid #666;color:#fff;padding:0.5rem 1rem;cursor:pointer\">Close</button>';\n }\n\n function escapeHtml(str) {\n return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n }\n\n})();\n";
54
54
  /**
55
55
  * HMR server for WebSocket connections.
56
56
  * Supports granular JS updates through module analysis.
@@ -1 +1 @@
1
- {"version":3,"file":"hmr.d.ts","sourceRoot":"","sources":["../../src/dev/hmr.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hmr.d.ts","sourceRoot":"","sources":["../../src/dev/hmr.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -4,8 +4,6 @@ var __require = import.meta.require;
4
4
  // src/dev/hmr.ts
5
5
  var HMR_CLIENT_CODE = `
6
6
  (function() {
7
- const ws = new WebSocket('ws://' + location.host + '/__hmr');
8
-
9
7
  // Module registry for hot updates
10
8
  window.__EREO_HMR__ = window.__EREO_HMR__ || {
11
9
  modules: new Map(),
@@ -13,75 +11,115 @@ var HMR_CLIENT_CODE = `
13
11
  acceptedModules: new Set(),
14
12
  };
15
13
 
16
- ws.onmessage = function(event) {
17
- const update = JSON.parse(event.data);
18
- const startTime = performance.now();
14
+ var MAX_RETRIES = 20;
15
+ var retryCount = 0;
16
+ var retryDelay = 1000;
17
+ var intentionalClose = false;
19
18
 
20
- // Log with timing info
21
- const logUpdate = (msg) => {
22
- const duration = (performance.now() - startTime).toFixed(1);
23
- console.log('[HMR] ' + msg + ' (' + duration + 'ms)');
24
- };
19
+ function connect() {
20
+ var ws = new WebSocket('ws://' + location.host + '/__hmr');
25
21
 
26
- switch (update.type) {
27
- case 'full-reload':
28
- logHMRReason(update);
22
+ ws.onopen = function() {
23
+ if (retryCount > 0) {
24
+ console.log('[HMR] Reconnected after ' + retryCount + ' retries');
25
+ // Server came back \u2014 reload to pick up changes
29
26
  location.reload();
30
- break;
27
+ return;
28
+ }
29
+ console.log('[HMR] Connected');
30
+ retryCount = 0;
31
+ retryDelay = 1000;
32
+ };
31
33
 
32
- case 'css-update':
33
- updateCSS(update.path);
34
- logUpdate('CSS updated: ' + update.path);
35
- break;
34
+ ws.onmessage = function(event) {
35
+ var update = JSON.parse(event.data);
36
+ var startTime = performance.now();
36
37
 
37
- case 'island-update':
38
- if (handleIslandUpdate(update)) {
39
- logUpdate('Island hot-updated: ' + (update.module?.id || update.path));
40
- } else {
41
- logHMRReason(update);
42
- location.reload();
43
- }
44
- break;
38
+ var logUpdate = function(msg) {
39
+ var duration = (performance.now() - startTime).toFixed(1);
40
+ console.log('[HMR] ' + msg + ' (' + duration + 'ms)');
41
+ };
45
42
 
46
- case 'component-update':
47
- if (handleComponentUpdate(update)) {
48
- logUpdate('Component hot-updated: ' + (update.module?.id || update.path));
49
- } else {
50
- logHMRReason(update);
51
- location.reload();
52
- }
53
- break;
54
-
55
- case 'loader-update':
56
- // Loaders require data refetch, do soft reload
57
- logUpdate('Loader changed, refreshing data...');
58
- refreshLoaderData(update.path);
59
- break;
60
-
61
- case 'js-update':
62
- // Check if we can do granular update
63
- if (update.module?.isIsland && handleIslandUpdate(update)) {
64
- logUpdate('Island hot-updated: ' + (update.module?.id || update.path));
65
- } else if (update.module?.isComponent && handleComponentUpdate(update)) {
66
- logUpdate('Component hot-updated: ' + (update.module?.id || update.path));
67
- } else {
43
+ switch (update.type) {
44
+ case 'full-reload':
68
45
  logHMRReason(update);
69
46
  location.reload();
70
- }
71
- break;
47
+ break;
72
48
 
73
- case 'error':
74
- showErrorOverlay(update.error);
75
- break;
76
- }
77
- };
49
+ case 'css-update':
50
+ updateCSS(update.path);
51
+ logUpdate('CSS updated: ' + update.path);
52
+ break;
78
53
 
79
- ws.onclose = function() {
80
- console.log('[HMR] Connection lost, attempting reconnect...');
81
- setTimeout(function() {
82
- location.reload();
83
- }, 1000);
84
- };
54
+ case 'island-update':
55
+ if (handleIslandUpdate(update)) {
56
+ logUpdate('Island hot-updated: ' + (update.module?.id || update.path));
57
+ } else {
58
+ logHMRReason(update);
59
+ location.reload();
60
+ }
61
+ break;
62
+
63
+ case 'component-update':
64
+ if (handleComponentUpdate(update)) {
65
+ logUpdate('Component hot-updated: ' + (update.module?.id || update.path));
66
+ } else {
67
+ logHMRReason(update);
68
+ location.reload();
69
+ }
70
+ break;
71
+
72
+ case 'loader-update':
73
+ logUpdate('Loader changed, refreshing data...');
74
+ refreshLoaderData(update.path);
75
+ break;
76
+
77
+ case 'js-update':
78
+ if (update.module?.isIsland && handleIslandUpdate(update)) {
79
+ logUpdate('Island hot-updated: ' + (update.module?.id || update.path));
80
+ } else if (update.module?.isComponent && handleComponentUpdate(update)) {
81
+ logUpdate('Component hot-updated: ' + (update.module?.id || update.path));
82
+ } else {
83
+ logHMRReason(update);
84
+ location.reload();
85
+ }
86
+ break;
87
+
88
+ case 'error':
89
+ showErrorOverlay(update.error);
90
+ break;
91
+ }
92
+
93
+ // Clear error overlay on successful (non-error) updates
94
+ if (update.type !== 'error') {
95
+ var overlay = document.getElementById('ereo-error-overlay');
96
+ if (overlay) overlay.remove();
97
+ }
98
+ };
99
+
100
+ ws.onclose = function() {
101
+ if (intentionalClose) return;
102
+ if (retryCount >= MAX_RETRIES) {
103
+ console.log('[HMR] Max retries reached, giving up. Reload manually when server is back.');
104
+ return;
105
+ }
106
+ retryCount++;
107
+ console.log('[HMR] Connection lost, retrying (' + retryCount + '/' + MAX_RETRIES + ')...');
108
+ setTimeout(connect, retryDelay);
109
+ retryDelay = Math.min(retryDelay * 1.5, 5000);
110
+ };
111
+
112
+ ws.onerror = function() {
113
+ // onclose will fire after this
114
+ };
115
+
116
+ window.addEventListener('beforeunload', function() {
117
+ intentionalClose = true;
118
+ ws.close();
119
+ });
120
+ }
121
+
122
+ connect();
85
123
 
86
124
  function updateCSS(path) {
87
125
  const links = document.querySelectorAll('link[rel="stylesheet"]');
@@ -197,14 +235,6 @@ var HMR_CLIENT_CODE = `
197
235
  return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
198
236
  }
199
237
 
200
- // Clear error overlay on successful update
201
- ws.addEventListener('message', function(event) {
202
- const update = JSON.parse(event.data);
203
- if (update.type !== 'error') {
204
- const overlay = document.getElementById('ereo-error-overlay');
205
- if (overlay) overlay.remove();
206
- }
207
- });
208
238
  })();
209
239
  `;
210
240
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ereo/bundler",
3
- "version": "0.1.21",
3
+ "version": "0.1.23",
4
4
  "license": "MIT",
5
5
  "author": "Ereo Team",
6
6
  "homepage": "https://ereo.dev",
@@ -32,8 +32,8 @@
32
32
  "typecheck": "tsc --noEmit"
33
33
  },
34
34
  "dependencies": {
35
- "@ereo/core": "^0.1.21",
36
- "@ereo/router": "^0.1.21",
35
+ "@ereo/core": "^0.1.23",
36
+ "@ereo/router": "^0.1.23",
37
37
  "postcss": "^8.4.35",
38
38
  "tailwindcss": "^3.4.1",
39
39
  "autoprefixer": "^10.4.17",