mnfst-run 1.0.1 → 1.0.3
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/package.json +1 -1
- package/serve.mjs +25 -14
package/package.json
CHANGED
package/serve.mjs
CHANGED
|
@@ -13,12 +13,15 @@
|
|
|
13
13
|
* SPA vs MPA is auto-detected: if the root index.html contains
|
|
14
14
|
* <meta name="manifest:prerendered"> the server disables SPA fallback.
|
|
15
15
|
*
|
|
16
|
-
* Live reload:
|
|
17
|
-
*
|
|
16
|
+
* Live reload:
|
|
17
|
+
* .css → hot-swaps the matching stylesheet href (no reload, no flash)
|
|
18
|
+
* .csv/.json/etc. → dispatches manifest:dev-reload; data plugin re-fetches local
|
|
19
|
+
* sources and updates Alpine store reactively (no reload)
|
|
20
|
+
* other → full page reload
|
|
18
21
|
*/
|
|
19
22
|
import { createServer } from 'http';
|
|
20
23
|
import { readFileSync, statSync, watch } from 'fs';
|
|
21
|
-
import { join, extname, resolve, basename
|
|
24
|
+
import { join, extname, resolve, basename } from 'path';
|
|
22
25
|
import { exec } from 'child_process';
|
|
23
26
|
|
|
24
27
|
const MIME = {
|
|
@@ -44,8 +47,10 @@ const MIME = {
|
|
|
44
47
|
'.map': 'application/json',
|
|
45
48
|
};
|
|
46
49
|
|
|
47
|
-
//
|
|
48
|
-
// CSS hot-
|
|
50
|
+
// Built once at startup. Injected only into full HTML documents (not fragments).
|
|
51
|
+
// - CSS changes → hot-swaps the matching <link> href (no reload, no flash)
|
|
52
|
+
// - data file changes → dispatches manifest:dev-reload (data plugin re-fetches)
|
|
53
|
+
// - other changes → full page reload
|
|
49
54
|
const LIVE_RELOAD_SCRIPT = `<script>
|
|
50
55
|
(function () {
|
|
51
56
|
var es = new EventSource('/__mnfst_sse__');
|
|
@@ -56,13 +61,15 @@ const LIVE_RELOAD_SCRIPT = `<script>
|
|
|
56
61
|
var base = l.href.split('?')[0];
|
|
57
62
|
if (base.endsWith(d.file)) l.href = base + '?t=' + Date.now();
|
|
58
63
|
});
|
|
64
|
+
} else if (d.type === 'data') {
|
|
65
|
+
window.dispatchEvent(new CustomEvent('manifest:dev-reload'));
|
|
59
66
|
} else {
|
|
60
67
|
location.reload();
|
|
61
68
|
}
|
|
62
69
|
};
|
|
63
70
|
es.onerror = function () { es.close(); };
|
|
64
71
|
})();
|
|
65
|
-
|
|
72
|
+
\x3c/script>`;
|
|
66
73
|
|
|
67
74
|
// --- CLI args ---
|
|
68
75
|
const args = process.argv.slice(2);
|
|
@@ -82,8 +89,7 @@ function detectMPA(rootDir) {
|
|
|
82
89
|
return /name=["']manifest:prerendered["']/i.test(readFileSync(join(rootDir, 'index.html'), 'utf8'));
|
|
83
90
|
} catch { return false; }
|
|
84
91
|
}
|
|
85
|
-
const spa
|
|
86
|
-
const mode = spa ? 'SPA' : 'MPA';
|
|
92
|
+
const spa = !detectMPA(root);
|
|
87
93
|
|
|
88
94
|
// --- SSE clients ---
|
|
89
95
|
let clients = [];
|
|
@@ -104,6 +110,8 @@ try {
|
|
|
104
110
|
const ext = extname(filename).toLowerCase();
|
|
105
111
|
if (ext === '.css') {
|
|
106
112
|
broadcast({ type: 'css', file: '/' + filename.replace(/\\/g, '/') });
|
|
113
|
+
} else if (['.csv', '.json', '.yaml', '.yml', '.md'].includes(ext)) {
|
|
114
|
+
broadcast({ type: 'data' });
|
|
107
115
|
} else {
|
|
108
116
|
broadcast({ type: 'reload' });
|
|
109
117
|
}
|
|
@@ -123,12 +131,15 @@ function serveFile(res, filePath) {
|
|
|
123
131
|
const mime = MIME[ext] || 'application/octet-stream';
|
|
124
132
|
let body = readFileSync(filePath);
|
|
125
133
|
if (ext === '.html') {
|
|
126
|
-
|
|
127
|
-
//
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
134
|
+
const html = body.toString('utf8');
|
|
135
|
+
// Only inject into full HTML documents — not component fragments
|
|
136
|
+
const isFullDoc = /<!doctype\s/i.test(html) || /<html[\s>]/i.test(html);
|
|
137
|
+
if (isFullDoc) {
|
|
138
|
+
const injected = html.includes('</body>')
|
|
139
|
+
? html.replace('</body>', LIVE_RELOAD_SCRIPT + '</body>')
|
|
140
|
+
: html + LIVE_RELOAD_SCRIPT;
|
|
141
|
+
body = Buffer.from(injected, 'utf8');
|
|
142
|
+
}
|
|
132
143
|
}
|
|
133
144
|
res.writeHead(200, { 'Content-Type': mime });
|
|
134
145
|
res.end(body);
|