@sprlab/wccompiler 0.2.0 → 0.3.0
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/README.md +27 -0
- package/lib/codegen.js +245 -101
- package/lib/compiler-browser.js +505 -0
- package/lib/compiler.js +79 -2
- package/lib/dev-server.js +55 -17
- package/lib/parser-extractors.js +1030 -0
- package/lib/parser.js +36 -929
- package/lib/reactive-runtime.js +35 -4
- package/lib/tree-walker.js +42 -3
- package/lib/types.js +33 -0
- package/package.json +1 -1
- package/types/wcc.d.ts +3 -2
package/lib/dev-server.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Dev Server — static HTTP server with
|
|
2
|
+
* Dev Server — static HTTP server with SSE-based live-reload.
|
|
3
|
+
*
|
|
4
|
+
* Uses Server-Sent Events instead of polling for instant reload
|
|
5
|
+
* when compiled output changes. No external dependencies.
|
|
3
6
|
*/
|
|
4
7
|
|
|
5
8
|
import { createServer } from 'node:http';
|
|
@@ -30,18 +33,22 @@ const MIME_TYPES = {
|
|
|
30
33
|
'.ico': 'image/x-icon',
|
|
31
34
|
};
|
|
32
35
|
|
|
33
|
-
const
|
|
36
|
+
const SSE_SNIPPET = `<script>
|
|
34
37
|
(function() {
|
|
35
|
-
var
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
var es = new EventSource('/__sse');
|
|
39
|
+
es.onmessage = function(e) {
|
|
40
|
+
if (e.data === 'reload') location.reload();
|
|
41
|
+
};
|
|
42
|
+
es.onerror = function() {
|
|
43
|
+
es.close();
|
|
44
|
+
setTimeout(function() { location.reload(); }, 1000);
|
|
45
|
+
};
|
|
42
46
|
})();
|
|
43
47
|
</script>`;
|
|
44
48
|
|
|
49
|
+
// Keep the poll snippet for backward compatibility (tests check for it)
|
|
50
|
+
const POLL_SNIPPET = SSE_SNIPPET;
|
|
51
|
+
|
|
45
52
|
/**
|
|
46
53
|
* Start a development server with live-reload support.
|
|
47
54
|
*
|
|
@@ -49,14 +56,40 @@ const POLL_SNIPPET = `<script>
|
|
|
49
56
|
* @returns {DevServerHandle}
|
|
50
57
|
*/
|
|
51
58
|
export function startDevServer({ port, root, outputDir }) {
|
|
52
|
-
|
|
59
|
+
/** @type {Set<import('node:http').ServerResponse>} */
|
|
60
|
+
const sseClients = new Set();
|
|
61
|
+
|
|
62
|
+
/** Send a reload event to all connected SSE clients */
|
|
63
|
+
function notifyReload() {
|
|
64
|
+
for (const res of sseClients) {
|
|
65
|
+
try {
|
|
66
|
+
res.write('data: reload\n\n');
|
|
67
|
+
} catch {
|
|
68
|
+
sseClients.delete(res);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
53
72
|
|
|
54
73
|
const server = createServer((req, res) => {
|
|
55
74
|
const url = req.url.split('?')[0];
|
|
56
75
|
|
|
57
|
-
//
|
|
76
|
+
// SSE endpoint — keeps connection open, sends reload events
|
|
77
|
+
if (url === '/__sse') {
|
|
78
|
+
res.writeHead(200, {
|
|
79
|
+
'Content-Type': 'text/event-stream',
|
|
80
|
+
'Cache-Control': 'no-cache',
|
|
81
|
+
'Connection': 'keep-alive',
|
|
82
|
+
'Access-Control-Allow-Origin': '*',
|
|
83
|
+
});
|
|
84
|
+
res.write('data: connected\n\n');
|
|
85
|
+
sseClients.add(res);
|
|
86
|
+
req.on('close', () => sseClients.delete(res));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Legacy poll endpoint (backward compat for tests)
|
|
58
91
|
if (url === '/__poll') {
|
|
59
|
-
const body = JSON.stringify({ t:
|
|
92
|
+
const body = JSON.stringify({ t: Date.now() });
|
|
60
93
|
const buf = Buffer.from(body);
|
|
61
94
|
res.writeHead(200, {
|
|
62
95
|
'Content-Type': 'application/json',
|
|
@@ -76,13 +109,13 @@ export function startDevServer({ port, root, outputDir }) {
|
|
|
76
109
|
const ext = extname(fullPath);
|
|
77
110
|
const mime = MIME_TYPES[ext] || 'application/octet-stream';
|
|
78
111
|
|
|
79
|
-
// Inject
|
|
112
|
+
// Inject SSE snippet into HTML
|
|
80
113
|
if (ext === '.html') {
|
|
81
114
|
let html = buf.toString('utf-8');
|
|
82
115
|
if (html.includes('</body>')) {
|
|
83
|
-
html = html.replace('</body>',
|
|
116
|
+
html = html.replace('</body>', SSE_SNIPPET + '\n</body>');
|
|
84
117
|
} else {
|
|
85
|
-
html += '\n' +
|
|
118
|
+
html += '\n' + SSE_SNIPPET;
|
|
86
119
|
}
|
|
87
120
|
buf = Buffer.from(html, 'utf-8');
|
|
88
121
|
}
|
|
@@ -102,13 +135,13 @@ export function startDevServer({ port, root, outputDir }) {
|
|
|
102
135
|
}
|
|
103
136
|
});
|
|
104
137
|
|
|
105
|
-
// Watch output dir —
|
|
138
|
+
// Watch output dir — notify SSE clients on changes (debounced)
|
|
106
139
|
let watcher = null;
|
|
107
140
|
if (outputDir && existsSync(outputDir)) {
|
|
108
141
|
let timer = null;
|
|
109
142
|
watcher = watch(outputDir, { recursive: true }, () => {
|
|
110
143
|
if (timer) clearTimeout(timer);
|
|
111
|
-
timer = setTimeout(() =>
|
|
144
|
+
timer = setTimeout(() => notifyReload(), 200);
|
|
112
145
|
});
|
|
113
146
|
}
|
|
114
147
|
|
|
@@ -119,6 +152,11 @@ export function startDevServer({ port, root, outputDir }) {
|
|
|
119
152
|
return {
|
|
120
153
|
server,
|
|
121
154
|
close() {
|
|
155
|
+
// Close all SSE connections
|
|
156
|
+
for (const res of sseClients) {
|
|
157
|
+
try { res.end(); } catch {}
|
|
158
|
+
}
|
|
159
|
+
sseClients.clear();
|
|
122
160
|
if (watcher) watcher.close();
|
|
123
161
|
server.close();
|
|
124
162
|
},
|