evolit 0.1.0-alpha.3 → 0.1.0-alpha.5
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/src/server.js +60 -4
package/package.json
CHANGED
package/src/server.js
CHANGED
|
@@ -35,6 +35,33 @@ async function writeResponseBody(response, body) {
|
|
|
35
35
|
response.end();
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
const LIVE_RELOAD_PATHNAME = "/_evolit/live-reload";
|
|
39
|
+
const LIVE_RELOAD_SNIPPET = `<script data-evolit-live-reload>if (typeof EventSource !== "undefined") { const source = new EventSource(${JSON.stringify(LIVE_RELOAD_PATHNAME)}); source.addEventListener("reload", () => window.location.reload()); }</script>`;
|
|
40
|
+
|
|
41
|
+
function getResponseHeader(headers, name) {
|
|
42
|
+
if (headers?.get) {
|
|
43
|
+
return headers.get(name);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return headers?.[name] ?? headers?.[name.toLowerCase()] ?? null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function injectLiveReloadSnippet(response) {
|
|
50
|
+
if (
|
|
51
|
+
typeof response.body !== "string"
|
|
52
|
+
|| !getResponseHeader(response.headers, "content-type")?.includes("text/html")
|
|
53
|
+
) {
|
|
54
|
+
return response;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
...response,
|
|
59
|
+
body: response.body.includes("</body>")
|
|
60
|
+
? response.body.replace("</body>", `${LIVE_RELOAD_SNIPPET}</body>`)
|
|
61
|
+
: `${response.body}${LIVE_RELOAD_SNIPPET}`,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
38
65
|
async function createRecursiveDirectoryWatcher(rootDirectory, onChange) {
|
|
39
66
|
const watchers = new Map();
|
|
40
67
|
let closed = false;
|
|
@@ -122,6 +149,7 @@ async function createServer(projectRoot, mode, explicitPort, options = {}) {
|
|
|
122
149
|
responseCacheRuntime,
|
|
123
150
|
});
|
|
124
151
|
const port = getPort(explicitPort);
|
|
152
|
+
const liveReloadConnections = new Set();
|
|
125
153
|
let invalidationTimer = null;
|
|
126
154
|
let pendingInvalidation = null;
|
|
127
155
|
let resolvePendingInvalidation = null;
|
|
@@ -146,7 +174,15 @@ async function createServer(projectRoot, mode, explicitPort, options = {}) {
|
|
|
146
174
|
invalidationPromise = invalidationPromise
|
|
147
175
|
.catch(() => {})
|
|
148
176
|
.then(() => deploymentRuntime.invalidateDevelopmentState());
|
|
149
|
-
invalidationPromise.then(
|
|
177
|
+
invalidationPromise.then(
|
|
178
|
+
() => {
|
|
179
|
+
for (const connection of liveReloadConnections) {
|
|
180
|
+
connection.write("event: reload\\ndata: update\\n\\n");
|
|
181
|
+
}
|
|
182
|
+
resolve();
|
|
183
|
+
},
|
|
184
|
+
resolve,
|
|
185
|
+
);
|
|
150
186
|
}, 40);
|
|
151
187
|
}
|
|
152
188
|
|
|
@@ -159,9 +195,24 @@ async function createServer(projectRoot, mode, explicitPort, options = {}) {
|
|
|
159
195
|
|
|
160
196
|
const server = http.createServer(async (req, res) => {
|
|
161
197
|
try {
|
|
198
|
+
const origin = `http://${req.headers.host ?? `localhost:${port}`}`;
|
|
199
|
+
const pathname = new URL(req.url ?? "/", origin).pathname;
|
|
200
|
+
if (mode === "development" && pathname === LIVE_RELOAD_PATHNAME) {
|
|
201
|
+
res.writeHead(200, {
|
|
202
|
+
"cache-control": "no-cache, no-transform",
|
|
203
|
+
connection: "keep-alive",
|
|
204
|
+
"content-type": "text/event-stream",
|
|
205
|
+
});
|
|
206
|
+
res.write("retry: 1000\\n\\n");
|
|
207
|
+
liveReloadConnections.add(res);
|
|
208
|
+
res.on("close", () => {
|
|
209
|
+
liveReloadConnections.delete(res);
|
|
210
|
+
});
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
162
214
|
await pendingInvalidation;
|
|
163
215
|
await invalidationPromise;
|
|
164
|
-
const origin = `http://${req.headers.host ?? `localhost:${port}`}`;
|
|
165
216
|
const method = req.method ?? "GET";
|
|
166
217
|
const request = new Request(new URL(req.url ?? "/", origin), {
|
|
167
218
|
method,
|
|
@@ -169,9 +220,10 @@ async function createServer(projectRoot, mode, explicitPort, options = {}) {
|
|
|
169
220
|
...(method === "GET" || method === "HEAD" ? {} : { body: req, duplex: "half" }),
|
|
170
221
|
});
|
|
171
222
|
const response = await deploymentRuntime.handle(request);
|
|
223
|
+
const servedResponse = mode === "development" ? injectLiveReloadSnippet(response) : response;
|
|
172
224
|
|
|
173
|
-
res.writeHead(
|
|
174
|
-
await writeResponseBody(res,
|
|
225
|
+
res.writeHead(servedResponse.status, servedResponse.headers);
|
|
226
|
+
await writeResponseBody(res, servedResponse.body);
|
|
175
227
|
} catch (error) {
|
|
176
228
|
res.writeHead(500, { "content-type": "text/plain; charset=utf-8" });
|
|
177
229
|
res.end(error instanceof Error ? error.stack ?? error.message : String(error));
|
|
@@ -192,6 +244,10 @@ async function createServer(projectRoot, mode, explicitPort, options = {}) {
|
|
|
192
244
|
clearTimeout(invalidationTimer);
|
|
193
245
|
}
|
|
194
246
|
watcher?.close();
|
|
247
|
+
for (const connection of liveReloadConnections) {
|
|
248
|
+
connection.end();
|
|
249
|
+
}
|
|
250
|
+
liveReloadConnections.clear();
|
|
195
251
|
await new Promise((resolve, reject) => {
|
|
196
252
|
server.close((error) => {
|
|
197
253
|
if (error) {
|