@qwik.dev/router 2.0.0-beta.28 → 2.0.0-beta.29
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/lib/adapters/azure-swa/vite/index.mjs +31 -36
- package/lib/adapters/bun-server/vite/index.mjs +0 -3
- package/lib/adapters/cloud-run/vite/index.mjs +0 -3
- package/lib/adapters/cloudflare-pages/vite/index.mjs +15 -9
- package/lib/adapters/deno-server/vite/index.mjs +7 -5
- package/lib/adapters/netlify-edge/vite/index.mjs +13 -23
- package/lib/adapters/node-server/vite/index.mjs +0 -3
- package/lib/adapters/shared/vite/index.d.ts +1 -7
- package/lib/adapters/shared/vite/index.mjs +164 -136
- package/lib/adapters/ssg/vite/index.mjs +3 -4
- package/lib/adapters/vercel-edge/vite/index.mjs +25 -9
- package/lib/chunks/error-handler.mjs +26 -26
- package/lib/chunks/fs.mjs +28 -138
- package/lib/chunks/http-error.qwik.mjs +27 -0
- package/lib/chunks/not-found-wrapper.qwik.mjs +25 -0
- package/lib/chunks/pathname.mjs +105 -0
- package/lib/chunks/routing.qwik.mjs +592 -216
- package/lib/chunks/system.mjs +328 -0
- package/lib/chunks/use-functions.qwik.mjs +35 -0
- package/lib/chunks/worker-thread.mjs +271 -0
- package/lib/index.d.ts +136 -102
- package/lib/index.qwik.mjs +699 -751
- package/lib/middleware/aws-lambda/index.mjs +7 -1
- package/lib/middleware/azure-swa/index.mjs +7 -2
- package/lib/middleware/bun/index.mjs +20 -5
- package/lib/middleware/cloudflare-pages/index.mjs +8 -2
- package/lib/middleware/deno/index.mjs +19 -5
- package/lib/middleware/netlify-edge/index.mjs +8 -2
- package/lib/middleware/node/index.mjs +10 -14
- package/lib/middleware/request-handler/index.d.ts +82 -12
- package/lib/middleware/request-handler/index.mjs +661 -524
- package/lib/middleware/vercel-edge/index.mjs +8 -2
- package/lib/modules.d.ts +7 -4
- package/lib/ssg/index.d.ts +48 -16
- package/lib/ssg/index.mjs +320 -7
- package/lib/vite/index.d.ts +6 -0
- package/lib/vite/index.mjs +1098 -641
- package/modules.d.ts +7 -4
- package/package.json +4 -4
- package/lib/chunks/format-error.mjs +0 -137
- package/lib/chunks/index.mjs +0 -896
- package/lib/chunks/types.qwik.mjs +0 -22
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { isAbsolute, resolve, join, dirname } from 'node:path';
|
|
3
|
+
import { cpus } from 'node:os';
|
|
4
|
+
import { Worker } from 'node:worker_threads';
|
|
5
|
+
import { k as normalizePath } from './fs.mjs';
|
|
6
|
+
|
|
7
|
+
function ssgWorkerCompare(a, b) {
|
|
8
|
+
if (a.activeTasks < b.activeTasks) {
|
|
9
|
+
return -1;
|
|
10
|
+
}
|
|
11
|
+
if (a.activeTasks > b.activeTasks) {
|
|
12
|
+
return 1;
|
|
13
|
+
}
|
|
14
|
+
return a.totalTasks < b.totalTasks ? -1 : 1;
|
|
15
|
+
}
|
|
16
|
+
async function createWorkerPool(sys, opts) {
|
|
17
|
+
const ssgWorkers = [];
|
|
18
|
+
const sitemapBuffer = [];
|
|
19
|
+
let sitemapStream = null;
|
|
20
|
+
opts = {
|
|
21
|
+
...opts
|
|
22
|
+
};
|
|
23
|
+
let outDir = opts.outDir;
|
|
24
|
+
if (typeof outDir !== "string") {
|
|
25
|
+
throw new Error(`Missing "outDir" option`);
|
|
26
|
+
}
|
|
27
|
+
if (!isAbsolute(outDir)) {
|
|
28
|
+
throw new Error(`"outDir" must be an absolute file path, received: ${outDir}`);
|
|
29
|
+
}
|
|
30
|
+
outDir = normalizePath(outDir);
|
|
31
|
+
let maxWorkers = cpus().length;
|
|
32
|
+
if (typeof opts.maxWorkers === "number") {
|
|
33
|
+
maxWorkers = Math.max(1, Math.min(opts.maxWorkers, maxWorkers));
|
|
34
|
+
}
|
|
35
|
+
let maxTasksPerWorker = 20;
|
|
36
|
+
if (typeof opts.maxTasksPerWorker === "number") {
|
|
37
|
+
maxTasksPerWorker = Math.max(1, Math.min(opts.maxTasksPerWorker, 50));
|
|
38
|
+
}
|
|
39
|
+
let sitemapOutFile = opts.sitemapOutFile;
|
|
40
|
+
if (sitemapOutFile !== null) {
|
|
41
|
+
if (typeof sitemapOutFile !== "string") {
|
|
42
|
+
sitemapOutFile = "sitemap.xml";
|
|
43
|
+
}
|
|
44
|
+
if (!isAbsolute(sitemapOutFile)) {
|
|
45
|
+
sitemapOutFile = resolve(outDir, sitemapOutFile);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (!opts.workerFilePath) {
|
|
49
|
+
throw new Error('Missing "workerFilePath" option for SSG worker creation');
|
|
50
|
+
}
|
|
51
|
+
const workerFilePath = typeof opts.workerFilePath === "string" && opts.workerFilePath.startsWith("file://") ? new URL(opts.workerFilePath) : opts.workerFilePath;
|
|
52
|
+
const { render: _r, qwikRouterConfig: _c, workerFilePath: _w, ...workerData } = opts;
|
|
53
|
+
const createWorker = () => {
|
|
54
|
+
let terminateResolve = null;
|
|
55
|
+
const mainTasks = /* @__PURE__ */ new Map();
|
|
56
|
+
let terminateTimeout = null;
|
|
57
|
+
const nodeWorker = new Worker(workerFilePath, {
|
|
58
|
+
workerData
|
|
59
|
+
});
|
|
60
|
+
nodeWorker.unref();
|
|
61
|
+
const ssgWorker = {
|
|
62
|
+
activeTasks: 0,
|
|
63
|
+
totalTasks: 0,
|
|
64
|
+
render: (staticRoute) => {
|
|
65
|
+
return new Promise((resolve2, reject) => {
|
|
66
|
+
try {
|
|
67
|
+
ssgWorker.activeTasks++;
|
|
68
|
+
ssgWorker.totalTasks++;
|
|
69
|
+
mainTasks.set(staticRoute.pathname, resolve2);
|
|
70
|
+
nodeWorker.postMessage(staticRoute);
|
|
71
|
+
} catch (e) {
|
|
72
|
+
ssgWorker.activeTasks--;
|
|
73
|
+
mainTasks.delete(staticRoute.pathname);
|
|
74
|
+
reject(e);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
},
|
|
78
|
+
terminate: async () => {
|
|
79
|
+
mainTasks.clear();
|
|
80
|
+
const msg = {
|
|
81
|
+
type: "close"
|
|
82
|
+
};
|
|
83
|
+
await new Promise((resolve2) => {
|
|
84
|
+
terminateResolve = () => {
|
|
85
|
+
resolve2();
|
|
86
|
+
};
|
|
87
|
+
terminateTimeout = setTimeout(async () => {
|
|
88
|
+
terminateTimeout = null;
|
|
89
|
+
terminateResolve = null;
|
|
90
|
+
await nodeWorker.terminate();
|
|
91
|
+
resolve2();
|
|
92
|
+
}, 1e3);
|
|
93
|
+
nodeWorker.postMessage(msg);
|
|
94
|
+
});
|
|
95
|
+
if (terminateTimeout) {
|
|
96
|
+
clearTimeout(terminateTimeout);
|
|
97
|
+
terminateTimeout = null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
nodeWorker.on("message", (msg) => {
|
|
102
|
+
switch (msg.type) {
|
|
103
|
+
case "render": {
|
|
104
|
+
const mainTask = mainTasks.get(msg.pathname);
|
|
105
|
+
if (mainTask) {
|
|
106
|
+
mainTasks.delete(msg.pathname);
|
|
107
|
+
ssgWorker.activeTasks--;
|
|
108
|
+
mainTask(msg);
|
|
109
|
+
}
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
case "close": {
|
|
113
|
+
if (terminateResolve) {
|
|
114
|
+
terminateResolve();
|
|
115
|
+
terminateResolve = null;
|
|
116
|
+
}
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
nodeWorker.on("error", (e) => {
|
|
122
|
+
console.error(`worker error`, e);
|
|
123
|
+
});
|
|
124
|
+
nodeWorker.on("exit", (code) => {
|
|
125
|
+
if (terminateTimeout) {
|
|
126
|
+
clearTimeout(terminateTimeout);
|
|
127
|
+
terminateTimeout = null;
|
|
128
|
+
}
|
|
129
|
+
if (mainTasks.size > 0) {
|
|
130
|
+
for (const [pathname, resolve2] of mainTasks) {
|
|
131
|
+
ssgWorker.activeTasks--;
|
|
132
|
+
resolve2({
|
|
133
|
+
type: "render",
|
|
134
|
+
pathname,
|
|
135
|
+
url: "",
|
|
136
|
+
ok: false,
|
|
137
|
+
error: {
|
|
138
|
+
message: `Worker exited with code ${code}`,
|
|
139
|
+
stack: void 0
|
|
140
|
+
},
|
|
141
|
+
filePath: null,
|
|
142
|
+
contentType: null,
|
|
143
|
+
resourceType: null
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
mainTasks.clear();
|
|
147
|
+
}
|
|
148
|
+
if (terminateResolve) {
|
|
149
|
+
terminateResolve();
|
|
150
|
+
terminateResolve = null;
|
|
151
|
+
}
|
|
152
|
+
if (code !== 0) {
|
|
153
|
+
console.error(`worker exit ${code}`);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
return ssgWorker;
|
|
157
|
+
};
|
|
158
|
+
const getNextWorker = () => ssgWorkers.sort(ssgWorkerCompare)[0];
|
|
159
|
+
const hasAvailableWorker = () => {
|
|
160
|
+
const ssgWorker = getNextWorker();
|
|
161
|
+
return ssgWorker.activeTasks < maxTasksPerWorker;
|
|
162
|
+
};
|
|
163
|
+
const render = async (ssgRoute) => {
|
|
164
|
+
const ssgWorker = getNextWorker();
|
|
165
|
+
const result = await ssgWorker.render(ssgRoute);
|
|
166
|
+
if (sitemapOutFile && result.ok && result.resourceType === "page") {
|
|
167
|
+
sitemapBuffer.push(`<url><loc>${result.url}</loc></url>`);
|
|
168
|
+
if (sitemapBuffer.length > 50) {
|
|
169
|
+
const siteMapUrls = sitemapBuffer.join("\n") + "\n";
|
|
170
|
+
sitemapBuffer.length = 0;
|
|
171
|
+
if (sitemapStream) {
|
|
172
|
+
sitemapStream.write(siteMapUrls);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return result;
|
|
177
|
+
};
|
|
178
|
+
const close = async () => {
|
|
179
|
+
const promises = [];
|
|
180
|
+
if (sitemapStream) {
|
|
181
|
+
sitemapBuffer.push(`</urlset>`);
|
|
182
|
+
sitemapStream.write(sitemapBuffer.join("\n"));
|
|
183
|
+
sitemapBuffer.length = 0;
|
|
184
|
+
await new Promise((resolve2, reject) => {
|
|
185
|
+
if (sitemapStream) {
|
|
186
|
+
sitemapStream.end((err) => {
|
|
187
|
+
if (err) {
|
|
188
|
+
reject(err);
|
|
189
|
+
} else {
|
|
190
|
+
resolve2();
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
} else {
|
|
194
|
+
resolve2();
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
sitemapStream = null;
|
|
198
|
+
}
|
|
199
|
+
for (const ssgWorker of ssgWorkers) {
|
|
200
|
+
try {
|
|
201
|
+
promises.push(ssgWorker.terminate());
|
|
202
|
+
} catch (e) {
|
|
203
|
+
console.error(e);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
await Promise.all(promises);
|
|
207
|
+
ssgWorkers.length = 0;
|
|
208
|
+
if (process.platform === "win32") {
|
|
209
|
+
await new Promise((resolve2) => setTimeout(resolve2, 300));
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
if (sitemapOutFile) {
|
|
213
|
+
await ensureDir(sitemapOutFile);
|
|
214
|
+
sitemapStream = fs.createWriteStream(sitemapOutFile, {
|
|
215
|
+
flags: "w"
|
|
216
|
+
});
|
|
217
|
+
sitemapStream.write(`<?xml version="1.0" encoding="UTF-8"?>
|
|
218
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
219
|
+
`);
|
|
220
|
+
}
|
|
221
|
+
for (let i = 0; i < maxWorkers; i++) {
|
|
222
|
+
ssgWorkers.push(createWorker());
|
|
223
|
+
if (process.platform === "win32" && i < maxWorkers - 1) {
|
|
224
|
+
await new Promise((resolve2) => setTimeout(resolve2, 100));
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const mainCtx = {
|
|
228
|
+
hasAvailableWorker,
|
|
229
|
+
render,
|
|
230
|
+
close
|
|
231
|
+
};
|
|
232
|
+
return mainCtx;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const ensureDir = async (filePath) => {
|
|
236
|
+
await fs.promises.mkdir(dirname(filePath), {
|
|
237
|
+
recursive: true
|
|
238
|
+
});
|
|
239
|
+
};
|
|
240
|
+
const access = async (path) => {
|
|
241
|
+
try {
|
|
242
|
+
await fs.promises.access(path);
|
|
243
|
+
return true;
|
|
244
|
+
} catch {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
async function createSystem(opts, threadId) {
|
|
249
|
+
const createWriteStream = (filePath) => {
|
|
250
|
+
return fs.createWriteStream(filePath, {
|
|
251
|
+
flags: "w"
|
|
252
|
+
});
|
|
253
|
+
};
|
|
254
|
+
const NS_PER_SEC = 1e9;
|
|
255
|
+
const MS_PER_NS = 1e-6;
|
|
256
|
+
const createTimer = () => {
|
|
257
|
+
const start = process.hrtime();
|
|
258
|
+
return () => {
|
|
259
|
+
const diff = process.hrtime(start);
|
|
260
|
+
return (diff[0] * NS_PER_SEC + diff[1]) * MS_PER_NS;
|
|
261
|
+
};
|
|
262
|
+
};
|
|
263
|
+
const createLogger = async () => {
|
|
264
|
+
if (threadId !== void 0) {
|
|
265
|
+
return {
|
|
266
|
+
debug: opts.log === "debug" ? console.debug.bind(console, `[${threadId}]`) : () => {
|
|
267
|
+
},
|
|
268
|
+
error: console.error.bind(console, `[${threadId}]`),
|
|
269
|
+
info: console.info.bind(console, `[${threadId}]`)
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
return {
|
|
273
|
+
debug: opts.log === "debug" ? console.debug.bind(console) : () => {
|
|
274
|
+
},
|
|
275
|
+
error: console.error.bind(console),
|
|
276
|
+
info: console.info.bind(console)
|
|
277
|
+
};
|
|
278
|
+
};
|
|
279
|
+
const outDir = normalizePath(opts.outDir);
|
|
280
|
+
const basePathname = opts.basePathname || "/";
|
|
281
|
+
const basenameLen = basePathname.length;
|
|
282
|
+
const getRouteFilePath = (pathname, isHtml) => {
|
|
283
|
+
pathname = decodeURIComponent(pathname.slice(basenameLen));
|
|
284
|
+
if (isHtml) {
|
|
285
|
+
if (!pathname.endsWith(".html")) {
|
|
286
|
+
if (pathname.endsWith("/")) {
|
|
287
|
+
pathname += "index.html";
|
|
288
|
+
} else {
|
|
289
|
+
pathname += "/index.html";
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
} else {
|
|
293
|
+
if (pathname.endsWith("/")) {
|
|
294
|
+
pathname = pathname.slice(0, -1);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return join(outDir, pathname);
|
|
298
|
+
};
|
|
299
|
+
const getDataFilePath = (pathname) => {
|
|
300
|
+
pathname = decodeURIComponent(pathname.slice(basenameLen));
|
|
301
|
+
if (pathname.endsWith("/")) {
|
|
302
|
+
pathname += "q-data.json";
|
|
303
|
+
} else {
|
|
304
|
+
pathname += "/q-data.json";
|
|
305
|
+
}
|
|
306
|
+
return join(outDir, pathname);
|
|
307
|
+
};
|
|
308
|
+
const sys = {
|
|
309
|
+
createMainProcess: null,
|
|
310
|
+
createLogger,
|
|
311
|
+
getOptions: () => opts,
|
|
312
|
+
ensureDir,
|
|
313
|
+
createWriteStream,
|
|
314
|
+
createTimer,
|
|
315
|
+
access,
|
|
316
|
+
getRouteFilePath,
|
|
317
|
+
getDataFilePath,
|
|
318
|
+
getEnv: (key) => process.env[key],
|
|
319
|
+
platform: {
|
|
320
|
+
static: true,
|
|
321
|
+
node: process.versions.node
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
sys.createMainProcess = () => createWorkerPool(sys, opts);
|
|
325
|
+
return sys;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export { access, createSystem, ensureDir };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { createContextId, useContext, implicit$FirstArg, useVisibleTaskQrl, inlinedQrl, _captures, noSerialize, useServerData } from '@qwik.dev/core';
|
|
2
|
+
|
|
3
|
+
const RouteStateContext = /* @__PURE__ */ createContextId("qr-s");
|
|
4
|
+
const ContentContext = /* @__PURE__ */ createContextId("qr-c");
|
|
5
|
+
const ContentInternalContext = /* @__PURE__ */ createContextId("qr-ic");
|
|
6
|
+
const DocumentHeadContext = /* @__PURE__ */ createContextId("qr-h");
|
|
7
|
+
const RouteLocationContext = /* @__PURE__ */ createContextId("qr-l");
|
|
8
|
+
const RouteNavigateContext = /* @__PURE__ */ createContextId("qr-n");
|
|
9
|
+
const RouteActionContext = /* @__PURE__ */ createContextId("qr-a");
|
|
10
|
+
const RoutePreventNavigateContext = /* @__PURE__ */ createContextId("qr-p");
|
|
11
|
+
const HttpStatusContext = /* @__PURE__ */ createContextId("qr-hs");
|
|
12
|
+
|
|
13
|
+
const useHttpStatus = () => useContext(HttpStatusContext).value;
|
|
14
|
+
const useContent = () => useContext(ContentContext);
|
|
15
|
+
const useDocumentHead = () => useContext(DocumentHeadContext);
|
|
16
|
+
const useLocation = () => useContext(RouteLocationContext);
|
|
17
|
+
const useNavigate = () => useContext(RouteNavigateContext);
|
|
18
|
+
const usePreventNavigateQrl = (fn) => {
|
|
19
|
+
if (!__EXPERIMENTAL__.preventNavigate) {
|
|
20
|
+
throw new Error('usePreventNavigate$ is experimental and must be enabled with `experimental: ["preventNavigate"]` in the `qwikVite` plugin.');
|
|
21
|
+
}
|
|
22
|
+
const registerPreventNav = useContext(RoutePreventNavigateContext);
|
|
23
|
+
useVisibleTaskQrl(/* @__PURE__ */ inlinedQrl(() => {
|
|
24
|
+
const fn2 = _captures[0], registerPreventNav2 = _captures[1];
|
|
25
|
+
return registerPreventNav2(fn2);
|
|
26
|
+
}, "usePreventNavigateQrl_useVisibleTask_BsYUaz9XhUs", [
|
|
27
|
+
fn,
|
|
28
|
+
registerPreventNav
|
|
29
|
+
]));
|
|
30
|
+
};
|
|
31
|
+
const usePreventNavigate$ = implicit$FirstArg(usePreventNavigateQrl);
|
|
32
|
+
const useAction = () => useContext(RouteActionContext);
|
|
33
|
+
const useQwikRouterEnv = () => noSerialize(useServerData("qwikrouter"));
|
|
34
|
+
|
|
35
|
+
export { ContentContext as C, DocumentHeadContext as D, HttpStatusContext as H, RouteLocationContext as R, useLocation as a, useQwikRouterEnv as b, ContentInternalContext as c, RouteNavigateContext as d, RouteStateContext as e, RouteActionContext as f, RoutePreventNavigateContext as g, useAction as h, useDocumentHead as i, useContent as j, useHttpStatus as k, usePreventNavigate$ as l, usePreventNavigateQrl as m, useNavigate as u };
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { _serialize } from '@qwik.dev/core/internal';
|
|
2
|
+
import { RequestEvShareQData, requestHandler, RedirectMessage } from '@qwik.dev/router/middleware/request-handler';
|
|
3
|
+
import { parentPort } from 'node:worker_threads';
|
|
4
|
+
|
|
5
|
+
const createNoopWritableStream = () => new WritableStream();
|
|
6
|
+
async function workerRender(sys, opts, staticRoute, pendingPromises, callback) {
|
|
7
|
+
const url = new URL(staticRoute.pathname, opts.origin);
|
|
8
|
+
const result = {
|
|
9
|
+
type: "render",
|
|
10
|
+
pathname: staticRoute.pathname,
|
|
11
|
+
url: url.href,
|
|
12
|
+
ok: false,
|
|
13
|
+
error: null,
|
|
14
|
+
filePath: null,
|
|
15
|
+
contentType: null,
|
|
16
|
+
resourceType: null
|
|
17
|
+
};
|
|
18
|
+
try {
|
|
19
|
+
let routeWriter = null;
|
|
20
|
+
let closeResolved;
|
|
21
|
+
const closePromise = new Promise((closePromiseResolve) => {
|
|
22
|
+
closeResolved = closePromiseResolve;
|
|
23
|
+
});
|
|
24
|
+
const request = new Request(url);
|
|
25
|
+
const requestCtx = {
|
|
26
|
+
mode: "static",
|
|
27
|
+
locale: void 0,
|
|
28
|
+
url,
|
|
29
|
+
request,
|
|
30
|
+
env: {
|
|
31
|
+
get(key) {
|
|
32
|
+
return sys.getEnv(key);
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
platform: sys.platform,
|
|
36
|
+
getClientConn: () => {
|
|
37
|
+
return {};
|
|
38
|
+
},
|
|
39
|
+
getWritableStream: (status, headers, _, _r, requestEv) => {
|
|
40
|
+
result.ok = status >= 200 && status < 300;
|
|
41
|
+
if (!result.ok) {
|
|
42
|
+
return createNoopWritableStream();
|
|
43
|
+
}
|
|
44
|
+
result.contentType = (headers.get("Content-Type") || "").toLowerCase();
|
|
45
|
+
const isHtml = result.contentType.includes("text/html");
|
|
46
|
+
const is404ErrorPage = url.pathname.endsWith("/404.html");
|
|
47
|
+
const routeFilePath = sys.getRouteFilePath(url.pathname, isHtml);
|
|
48
|
+
if (is404ErrorPage) {
|
|
49
|
+
result.resourceType = "404";
|
|
50
|
+
} else if (isHtml) {
|
|
51
|
+
result.resourceType = "page";
|
|
52
|
+
}
|
|
53
|
+
const hasRouteWriter = isHtml ? opts.emitHtml !== false : true;
|
|
54
|
+
const writeQDataEnabled = isHtml && opts.emitData !== false;
|
|
55
|
+
const stream = new WritableStream({
|
|
56
|
+
async start() {
|
|
57
|
+
try {
|
|
58
|
+
if (hasRouteWriter || writeQDataEnabled) {
|
|
59
|
+
await sys.ensureDir(routeFilePath);
|
|
60
|
+
}
|
|
61
|
+
if (hasRouteWriter) {
|
|
62
|
+
routeWriter = sys.createWriteStream(routeFilePath);
|
|
63
|
+
routeWriter.on("error", (e) => {
|
|
64
|
+
console.error(e);
|
|
65
|
+
routeWriter = null;
|
|
66
|
+
result.error = {
|
|
67
|
+
message: e.message,
|
|
68
|
+
stack: e.stack
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
} catch (e) {
|
|
73
|
+
console.error("Error during stream start", staticRoute.pathname, e);
|
|
74
|
+
routeWriter = null;
|
|
75
|
+
result.error = {
|
|
76
|
+
message: String(e),
|
|
77
|
+
stack: e.stack || ""
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
write(chunk) {
|
|
82
|
+
try {
|
|
83
|
+
if (routeWriter) {
|
|
84
|
+
routeWriter.write(Buffer.from(chunk.buffer));
|
|
85
|
+
}
|
|
86
|
+
} catch (e) {
|
|
87
|
+
console.error("Error during stream write", staticRoute.pathname, e);
|
|
88
|
+
routeWriter = null;
|
|
89
|
+
result.error = {
|
|
90
|
+
message: String(e),
|
|
91
|
+
stack: e.stack || ""
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
async close() {
|
|
96
|
+
const writePromises = [];
|
|
97
|
+
try {
|
|
98
|
+
if (writeQDataEnabled) {
|
|
99
|
+
const qData = requestEv.sharedMap.get(RequestEvShareQData);
|
|
100
|
+
if (qData && !is404ErrorPage) {
|
|
101
|
+
const qDataFilePath = sys.getDataFilePath(url.pathname);
|
|
102
|
+
const dataWriter = sys.createWriteStream(qDataFilePath);
|
|
103
|
+
dataWriter.on("error", (e) => {
|
|
104
|
+
console.error(e);
|
|
105
|
+
result.error = {
|
|
106
|
+
message: e.message,
|
|
107
|
+
stack: e.stack
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
const serialized = await _serialize(qData);
|
|
111
|
+
dataWriter.write(serialized);
|
|
112
|
+
writePromises.push(new Promise((resolve) => {
|
|
113
|
+
result.filePath = routeFilePath;
|
|
114
|
+
dataWriter.end(resolve);
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (routeWriter) {
|
|
119
|
+
writePromises.push(new Promise((resolve) => {
|
|
120
|
+
result.filePath = routeFilePath;
|
|
121
|
+
routeWriter.end(resolve);
|
|
122
|
+
}).finally(closeResolved));
|
|
123
|
+
}
|
|
124
|
+
if (writePromises.length > 0) {
|
|
125
|
+
await Promise.all(writePromises);
|
|
126
|
+
}
|
|
127
|
+
} catch (e) {
|
|
128
|
+
console.error("Error during stream close", staticRoute.pathname, e);
|
|
129
|
+
routeWriter = null;
|
|
130
|
+
result.error = {
|
|
131
|
+
message: String(e),
|
|
132
|
+
stack: e.stack || ""
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
return stream;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
const promise = requestHandler(requestCtx, opts).then((rsp) => {
|
|
141
|
+
if (rsp != null) {
|
|
142
|
+
return rsp.completion.then((r) => {
|
|
143
|
+
if (routeWriter) {
|
|
144
|
+
return closePromise.then(() => r);
|
|
145
|
+
}
|
|
146
|
+
return r;
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}).then((e) => {
|
|
150
|
+
if (e !== void 0) {
|
|
151
|
+
if (e instanceof RedirectMessage) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (e instanceof Error) {
|
|
155
|
+
result.error = {
|
|
156
|
+
message: e.message,
|
|
157
|
+
stack: e.stack
|
|
158
|
+
};
|
|
159
|
+
} else {
|
|
160
|
+
result.error = {
|
|
161
|
+
message: String(e),
|
|
162
|
+
stack: void 0
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
console.error("Error during request handling", staticRoute.pathname, e);
|
|
166
|
+
}
|
|
167
|
+
}).catch((e) => {
|
|
168
|
+
console.error("Unhandled error during request handling", staticRoute.pathname, e);
|
|
169
|
+
result.error = {
|
|
170
|
+
message: String(e),
|
|
171
|
+
stack: e.stack || ""
|
|
172
|
+
};
|
|
173
|
+
}).finally(() => {
|
|
174
|
+
pendingPromises.delete(promise);
|
|
175
|
+
callback(result);
|
|
176
|
+
});
|
|
177
|
+
pendingPromises.add(promise);
|
|
178
|
+
} catch (e) {
|
|
179
|
+
console.error("Error during render", staticRoute.pathname, e);
|
|
180
|
+
if (e instanceof Error) {
|
|
181
|
+
result.error = {
|
|
182
|
+
message: e.message,
|
|
183
|
+
stack: e.stack
|
|
184
|
+
};
|
|
185
|
+
} else {
|
|
186
|
+
result.error = {
|
|
187
|
+
message: String(e),
|
|
188
|
+
stack: void 0
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
callback(result);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
async function workerThread(sys) {
|
|
195
|
+
delete globalThis.__qwik;
|
|
196
|
+
const opts = sys.getOptions();
|
|
197
|
+
const pendingPromises = /* @__PURE__ */ new Set();
|
|
198
|
+
const log = await sys.createLogger();
|
|
199
|
+
process.on("uncaughtException", (e) => {
|
|
200
|
+
console.error("Worker uncaught exception (suppressed):", e.message);
|
|
201
|
+
});
|
|
202
|
+
process.on("unhandledRejection", (e) => {
|
|
203
|
+
console.error("Worker unhandled rejection (suppressed):", e instanceof Error ? e.message : e);
|
|
204
|
+
});
|
|
205
|
+
const onMessage = async (msg) => {
|
|
206
|
+
switch (msg.type) {
|
|
207
|
+
case "render": {
|
|
208
|
+
log.debug(`Worker thread rendering: ${msg.pathname}`);
|
|
209
|
+
return new Promise((resolve) => {
|
|
210
|
+
workerRender(sys, opts, msg, pendingPromises, resolve).catch((e) => {
|
|
211
|
+
console.error("Error during render", msg.pathname, e);
|
|
212
|
+
resolve({
|
|
213
|
+
type: "render",
|
|
214
|
+
pathname: msg.pathname,
|
|
215
|
+
url: "",
|
|
216
|
+
ok: false,
|
|
217
|
+
error: {
|
|
218
|
+
message: e instanceof Error ? e.message : String(e),
|
|
219
|
+
stack: e instanceof Error ? e.stack : void 0
|
|
220
|
+
},
|
|
221
|
+
filePath: null,
|
|
222
|
+
contentType: null,
|
|
223
|
+
resourceType: null
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
case "close": {
|
|
229
|
+
if (pendingPromises.size) {
|
|
230
|
+
log.debug(`Worker thread closing, waiting for ${pendingPromises.size} pending renders`);
|
|
231
|
+
const promises = Array.from(pendingPromises);
|
|
232
|
+
pendingPromises.clear();
|
|
233
|
+
await Promise.all(promises);
|
|
234
|
+
}
|
|
235
|
+
log.debug(`Worker thread closed`);
|
|
236
|
+
return {
|
|
237
|
+
type: "close"
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
parentPort?.on("message", async (msg) => {
|
|
243
|
+
try {
|
|
244
|
+
parentPort?.postMessage(await onMessage(msg));
|
|
245
|
+
} catch (e) {
|
|
246
|
+
if (msg.type === "render") {
|
|
247
|
+
const error = e instanceof Error ? e : new Error(String(e));
|
|
248
|
+
parentPort?.postMessage({
|
|
249
|
+
type: "render",
|
|
250
|
+
pathname: msg.pathname,
|
|
251
|
+
url: "",
|
|
252
|
+
ok: false,
|
|
253
|
+
error: {
|
|
254
|
+
message: error.message,
|
|
255
|
+
stack: error.stack
|
|
256
|
+
},
|
|
257
|
+
filePath: null,
|
|
258
|
+
contentType: null,
|
|
259
|
+
resourceType: null
|
|
260
|
+
});
|
|
261
|
+
} else {
|
|
262
|
+
console.error("Worker message handler error", e);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (msg.type === "close") {
|
|
266
|
+
parentPort?.close();
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export { workerThread };
|