defuss-ssg 0.1.2 → 0.2.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 +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/index.cjs +369 -4
- package/dist/index.d.cts +134 -3
- package/dist/index.d.mts +134 -3
- package/dist/index.mjs +1 -1
- package/dist/plugins/index.d.cts +1 -1
- package/dist/plugins/index.d.mts +1 -1
- package/dist/runtime.cjs +37 -4
- package/dist/runtime.mjs +37 -4
- package/dist/{serve-CMNixAKF.mjs → serve-BZhZ9J90.mjs} +366 -8
- package/dist/{types-C0p7aCdN.d.cts → types-QQrIkB7n.d.cts} +2 -2
- package/dist/{types-C0p7aCdN.d.mts → types-QQrIkB7n.d.mts} +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Usage
|
|
|
23
23
|
Simply generate a static site from a content directory to an output directory with full defuss-MDX (GFM + Frontmatter) support:
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
|
-
|
|
26
|
+
bunx defuss-ssg build ./folder
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
Or install globally or locally in a project:
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { v as validateProjectDir, b as build, s as serve } from './serve-
|
|
2
|
+
import { v as validateProjectDir, b as build, s as serve } from './serve-BZhZ9J90.mjs';
|
|
3
3
|
import { join, dirname, resolve } from 'node:path';
|
|
4
4
|
import { existsSync, readFileSync } from 'node:fs';
|
|
5
5
|
import { spawn } from 'node:child_process';
|
package/dist/index.cjs
CHANGED
|
@@ -83,14 +83,21 @@ const autoHydratePlugin = {
|
|
|
83
83
|
;(async function(){
|
|
84
84
|
|
|
85
85
|
const props = ${JSON.stringify(props || {})};
|
|
86
|
-
const
|
|
87
|
-
|
|
86
|
+
const cacheBust = "?v=" + Date.now();
|
|
87
|
+
console.log("[hydrate:${id}] Starting hydration, cacheBust=" + cacheBust);
|
|
88
|
+
console.log("[hydrate:${id}] Importing runtime from /components/runtime.js" + cacheBust);
|
|
89
|
+
const { hydrate } = await import("/components/runtime.js" + cacheBust);
|
|
90
|
+
console.log("[hydrate:${id}] Importing component from ${clientSrcFile}" + cacheBust);
|
|
91
|
+
const expports = (await import("${clientSrcFile}" + cacheBust));
|
|
92
|
+
|
|
93
|
+
console.log("[hydrate:${id}] Module keys:", Object.keys(expports));
|
|
88
94
|
|
|
89
95
|
if (!expports || typeof expports.default !== "function") {
|
|
90
96
|
console.error("Hydration error: No default export function found in", "${clientSrcFile}");
|
|
91
97
|
return;
|
|
92
98
|
}
|
|
93
99
|
const Component = expports.default;
|
|
100
|
+
console.log("[hydrate:${id}] Component:", Component.name || "(anonymous)");
|
|
94
101
|
|
|
95
102
|
let roots = null;
|
|
96
103
|
try {
|
|
@@ -105,7 +112,11 @@ const autoHydratePlugin = {
|
|
|
105
112
|
} else {
|
|
106
113
|
console.error("Hydration error: Component MUST return a single root element, not an array of elements! (no fragments allowed)");
|
|
107
114
|
}
|
|
115
|
+
|
|
116
|
+
console.log("[hydrate:${id}] Rendered VDOM roots:", roots.length, JSON.stringify(roots).slice(0, 200));
|
|
117
|
+
|
|
108
118
|
const wrapper = document.querySelector('div[data-hydrate-id="${id}"]');
|
|
119
|
+
console.log("[hydrate:${id}] Wrapper found:", !!wrapper, wrapper?.childNodes?.length, "children");
|
|
109
120
|
|
|
110
121
|
// remove <script> itself
|
|
111
122
|
document.getElementById("${id}")?.remove();
|
|
@@ -119,6 +130,7 @@ const autoHydratePlugin = {
|
|
|
119
130
|
}
|
|
120
131
|
// unwrap children
|
|
121
132
|
wrapper.replaceWith(...wrapper.childNodes);
|
|
133
|
+
console.log("[hydrate:${id}] Hydration complete, wrapper unwrapped");
|
|
122
134
|
} else {
|
|
123
135
|
console.warn("No wrapper found for hydration id ${id}");
|
|
124
136
|
}
|
|
@@ -203,6 +215,289 @@ const validateProjectDir = (projectDir) => {
|
|
|
203
215
|
return { code: "OK", message: "Project directory is valid." };
|
|
204
216
|
};
|
|
205
217
|
|
|
218
|
+
const HTTP_METHODS = [
|
|
219
|
+
"GET",
|
|
220
|
+
"POST",
|
|
221
|
+
"PUT",
|
|
222
|
+
"DELETE",
|
|
223
|
+
"PATCH",
|
|
224
|
+
"HEAD",
|
|
225
|
+
"OPTIONS",
|
|
226
|
+
"ALL"
|
|
227
|
+
];
|
|
228
|
+
const createRedirect = (url, status = 302) => new Response(null, {
|
|
229
|
+
status,
|
|
230
|
+
headers: { Location: url }
|
|
231
|
+
});
|
|
232
|
+
const endpointFileToRoute = (filePath, pagesDir) => {
|
|
233
|
+
let route = node_path.relative(pagesDir, filePath).replace(/\\/g, "/").replace(/\.(ts|js)$/, "");
|
|
234
|
+
if (!route.startsWith("/")) {
|
|
235
|
+
route = `/${route}`;
|
|
236
|
+
}
|
|
237
|
+
return route;
|
|
238
|
+
};
|
|
239
|
+
const routeToExpressPattern = (route) => route.replace(/\[([^\]]+)\]/g, ":$1");
|
|
240
|
+
const extractParamNames = (route) => Array.from(route.matchAll(/\[([^\]]+)\]/g), (m) => m[1]);
|
|
241
|
+
const discoverEndpointSourceFiles = async (pagesDir) => {
|
|
242
|
+
if (!node_fs.existsSync(pagesDir)) return [];
|
|
243
|
+
return glob.async(node_path.join(pagesDir, "**/*.{ts,js}"), {
|
|
244
|
+
ignore: ["**/*.d.ts"]
|
|
245
|
+
});
|
|
246
|
+
};
|
|
247
|
+
const compileEndpoints = async (sourceFiles, pagesDir, outDir, debug = false) => {
|
|
248
|
+
if (sourceFiles.length === 0) return /* @__PURE__ */ new Map();
|
|
249
|
+
if (debug) {
|
|
250
|
+
console.log(`Compiling ${sourceFiles.length} endpoint source file(s)\u2026`);
|
|
251
|
+
}
|
|
252
|
+
console.time("[endpoints] esbuild-compile");
|
|
253
|
+
await esbuild.build({
|
|
254
|
+
entryPoints: sourceFiles,
|
|
255
|
+
format: "esm",
|
|
256
|
+
bundle: true,
|
|
257
|
+
platform: "node",
|
|
258
|
+
target: ["esnext"],
|
|
259
|
+
outdir: outDir,
|
|
260
|
+
outbase: pagesDir,
|
|
261
|
+
outExtension: { ".js": ".mjs" }
|
|
262
|
+
});
|
|
263
|
+
console.timeEnd("[endpoints] esbuild-compile");
|
|
264
|
+
const mapping = /* @__PURE__ */ new Map();
|
|
265
|
+
for (const src of sourceFiles) {
|
|
266
|
+
const rel = node_path.relative(pagesDir, src).replace(/\.(ts|js)$/, ".mjs");
|
|
267
|
+
mapping.set(src, node_path.join(outDir, rel));
|
|
268
|
+
}
|
|
269
|
+
return mapping;
|
|
270
|
+
};
|
|
271
|
+
const loadEndpointModule = async (filePath) => {
|
|
272
|
+
const code = await promises.readFile(filePath, "utf-8");
|
|
273
|
+
const encoded = Buffer.from(code).toString("base64");
|
|
274
|
+
const dataUrl = `data:text/javascript;base64,${encoded}`;
|
|
275
|
+
return import(dataUrl);
|
|
276
|
+
};
|
|
277
|
+
const resolveEndpoints = async (pagesDir, endpointsDir, debug = false) => {
|
|
278
|
+
const sourceFiles = await discoverEndpointSourceFiles(pagesDir);
|
|
279
|
+
if (sourceFiles.length === 0) return [];
|
|
280
|
+
console.time("[endpoints] compile-all");
|
|
281
|
+
const mapping = await compileEndpoints(sourceFiles, pagesDir, endpointsDir, debug);
|
|
282
|
+
console.timeEnd("[endpoints] compile-all");
|
|
283
|
+
const endpoints = [];
|
|
284
|
+
console.time("[endpoints] load-modules");
|
|
285
|
+
for (const [sourceFile, compiledFile] of mapping) {
|
|
286
|
+
const routePattern = endpointFileToRoute(sourceFile, pagesDir);
|
|
287
|
+
const paramNames = extractParamNames(routePattern);
|
|
288
|
+
const isDynamic = paramNames.length > 0;
|
|
289
|
+
if (debug) {
|
|
290
|
+
console.log(
|
|
291
|
+
`Endpoint: ${sourceFile} \u2192 ${compiledFile} \u2192 ${routePattern}` + (isDynamic ? ` (params: ${paramNames.join(", ")})` : "")
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
const module = await loadEndpointModule(compiledFile);
|
|
295
|
+
const prerender = module.prerender === true;
|
|
296
|
+
endpoints.push({
|
|
297
|
+
sourceFile,
|
|
298
|
+
compiledFile,
|
|
299
|
+
routePattern,
|
|
300
|
+
module,
|
|
301
|
+
isDynamic,
|
|
302
|
+
paramNames,
|
|
303
|
+
prerender
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
console.timeEnd("[endpoints] load-modules");
|
|
307
|
+
return endpoints;
|
|
308
|
+
};
|
|
309
|
+
const buildEndpoints = async (projectDir, config, debug = false) => {
|
|
310
|
+
const pagesDir = node_path.join(projectDir, config.pages);
|
|
311
|
+
const outputDir = node_path.join(projectDir, config.output);
|
|
312
|
+
const endpointsDir = node_path.join(projectDir, ".endpoints");
|
|
313
|
+
const endpoints = await resolveEndpoints(pagesDir, endpointsDir, debug);
|
|
314
|
+
if (endpoints.length === 0) return;
|
|
315
|
+
const prerenderEndpoints = endpoints.filter((e) => e.prerender);
|
|
316
|
+
const dynamicEndpoints = endpoints.filter((e) => !e.prerender);
|
|
317
|
+
console.log(
|
|
318
|
+
`Endpoints: ${endpoints.length} compiled` + (prerenderEndpoints.length ? `, ${prerenderEndpoints.length} pre-rendered` : "") + (dynamicEndpoints.length ? `, ${dynamicEndpoints.length} dynamic` : "")
|
|
319
|
+
);
|
|
320
|
+
for (const endpoint of prerenderEndpoints) {
|
|
321
|
+
const { module, routePattern, isDynamic } = endpoint;
|
|
322
|
+
const handler = module.GET;
|
|
323
|
+
if (!handler) {
|
|
324
|
+
if (debug) {
|
|
325
|
+
console.log(
|
|
326
|
+
`Endpoint ${routePattern}: prerender=true but no GET export \u2013 skipping`
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
let paramSets = [{}];
|
|
332
|
+
if (isDynamic) {
|
|
333
|
+
if (!module.getStaticPaths) {
|
|
334
|
+
console.warn(
|
|
335
|
+
`Dynamic endpoint ${routePattern} has no getStaticPaths() \u2013 skipping`
|
|
336
|
+
);
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
const paths = await module.getStaticPaths();
|
|
340
|
+
paramSets = paths.map((p) => p.params);
|
|
341
|
+
}
|
|
342
|
+
for (const params of paramSets) {
|
|
343
|
+
let resolvedRoute = routePattern;
|
|
344
|
+
for (const [key, value] of Object.entries(params)) {
|
|
345
|
+
resolvedRoute = resolvedRoute.replace(`[${key}]`, value);
|
|
346
|
+
}
|
|
347
|
+
const requestUrl = `http://localhost${resolvedRoute}`;
|
|
348
|
+
const context = {
|
|
349
|
+
params,
|
|
350
|
+
request: new Request(requestUrl),
|
|
351
|
+
redirect: createRedirect
|
|
352
|
+
};
|
|
353
|
+
if (debug) {
|
|
354
|
+
console.log(`Pre-rendering endpoint: ${resolvedRoute}`);
|
|
355
|
+
}
|
|
356
|
+
try {
|
|
357
|
+
const response = await handler(context);
|
|
358
|
+
const outputFile = node_path.join(outputDir, resolvedRoute);
|
|
359
|
+
const outputFileDir = node_path.dirname(outputFile);
|
|
360
|
+
if (!node_fs.existsSync(outputFileDir)) {
|
|
361
|
+
node_fs.mkdirSync(outputFileDir, { recursive: true });
|
|
362
|
+
}
|
|
363
|
+
if (response.status >= 300 && response.status < 400) {
|
|
364
|
+
const location = response.headers.get("Location") || "/";
|
|
365
|
+
await promises.writeFile(
|
|
366
|
+
outputFile,
|
|
367
|
+
`<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0;url=${location}"><link rel="canonical" href="${location}"></head><body>Redirecting to <a href="${location}">${location}</a></body></html>`
|
|
368
|
+
);
|
|
369
|
+
} else {
|
|
370
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
371
|
+
await promises.writeFile(outputFile, buffer);
|
|
372
|
+
}
|
|
373
|
+
if (debug) {
|
|
374
|
+
console.log(` \u2192 ${outputFile}`);
|
|
375
|
+
}
|
|
376
|
+
} catch (error) {
|
|
377
|
+
console.error(`Error pre-rendering endpoint ${resolvedRoute}:`, error);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
const handleEndpointRoute = async (req, res, compiledFile, routePattern, method) => {
|
|
383
|
+
let currentModule;
|
|
384
|
+
try {
|
|
385
|
+
currentModule = await loadEndpointModule(compiledFile);
|
|
386
|
+
} catch (err) {
|
|
387
|
+
console.error(`Failed to load endpoint: ${compiledFile}`, err);
|
|
388
|
+
res.status(500).send("Internal Server Error");
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
const handlerFn = currentModule[method] ?? currentModule.ALL;
|
|
392
|
+
if (!handlerFn) {
|
|
393
|
+
res.status(405).send("Method Not Allowed");
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
const protocol = req.protocol || "http";
|
|
397
|
+
const host = req.get?.("host") || req.headers?.host || "localhost";
|
|
398
|
+
const url = `${protocol}://${host}${req.originalUrl}`;
|
|
399
|
+
const reqInit = {
|
|
400
|
+
method: req.method,
|
|
401
|
+
headers: req.headers
|
|
402
|
+
};
|
|
403
|
+
if (["POST", "PUT", "PATCH", "DELETE"].includes(req.method)) {
|
|
404
|
+
try {
|
|
405
|
+
const chunks = [];
|
|
406
|
+
await new Promise((resolve, reject) => {
|
|
407
|
+
req.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
|
|
408
|
+
req.on("end", resolve);
|
|
409
|
+
req.on("error", reject);
|
|
410
|
+
});
|
|
411
|
+
if (chunks.length > 0) {
|
|
412
|
+
reqInit.body = Buffer.concat(chunks);
|
|
413
|
+
}
|
|
414
|
+
} catch {
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
const request = new Request(url, reqInit);
|
|
418
|
+
const context = {
|
|
419
|
+
params: req.params || {},
|
|
420
|
+
request,
|
|
421
|
+
redirect: createRedirect
|
|
422
|
+
};
|
|
423
|
+
try {
|
|
424
|
+
const response = await handlerFn(context);
|
|
425
|
+
res.status(response.status);
|
|
426
|
+
response.headers.forEach((value, key) => {
|
|
427
|
+
res.set(key, value);
|
|
428
|
+
});
|
|
429
|
+
if (req.method === "HEAD") {
|
|
430
|
+
res.end();
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
if (response.body) {
|
|
434
|
+
const buf = Buffer.from(await response.arrayBuffer());
|
|
435
|
+
res.send(buf);
|
|
436
|
+
} else {
|
|
437
|
+
res.end();
|
|
438
|
+
}
|
|
439
|
+
} catch (error) {
|
|
440
|
+
console.error(`Endpoint error ${req.method} ${routePattern}:`, error);
|
|
441
|
+
res.status(500).send("Internal Server Error");
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
const registerEndpoints = async (app, projectDir, config, debug = false) => {
|
|
445
|
+
const endpointsDir = node_path.join(projectDir, ".endpoints");
|
|
446
|
+
const mjsFiles = await glob.async(node_path.join(endpointsDir, "**/*.mjs"));
|
|
447
|
+
if (mjsFiles.length === 0) return;
|
|
448
|
+
let registered = 0;
|
|
449
|
+
for (const compiledFile of mjsFiles) {
|
|
450
|
+
let route = node_path.relative(endpointsDir, compiledFile).replace(/\\/g, "/").replace(/\.mjs$/, "");
|
|
451
|
+
if (!route.startsWith("/")) route = `/${route}`;
|
|
452
|
+
let module;
|
|
453
|
+
try {
|
|
454
|
+
module = await loadEndpointModule(compiledFile);
|
|
455
|
+
} catch (err) {
|
|
456
|
+
console.error(`Failed to load endpoint: ${compiledFile}`, err);
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
if (module.prerender === true) {
|
|
460
|
+
if (debug) {
|
|
461
|
+
console.log(`Skipping prerender endpoint: ${route}`);
|
|
462
|
+
}
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
const expressRoute = routeToExpressPattern(route);
|
|
466
|
+
const hasExportedMethods = HTTP_METHODS.some(
|
|
467
|
+
(m) => m in module && typeof module[m] === "function"
|
|
468
|
+
);
|
|
469
|
+
if (!hasExportedMethods) continue;
|
|
470
|
+
if (debug) {
|
|
471
|
+
console.log(`Registering dynamic endpoint: ${expressRoute}`);
|
|
472
|
+
}
|
|
473
|
+
for (const method of HTTP_METHODS) {
|
|
474
|
+
if (method in module && typeof module[method] === "function") {
|
|
475
|
+
const expressMethod = method === "ALL" ? "all" : method.toLowerCase();
|
|
476
|
+
app[expressMethod](
|
|
477
|
+
expressRoute,
|
|
478
|
+
async (req, res) => handleEndpointRoute(req, res, compiledFile, route, method)
|
|
479
|
+
);
|
|
480
|
+
if (debug) {
|
|
481
|
+
console.log(` ${method} ${expressRoute}`);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
if (module.GET && !module.HEAD) {
|
|
486
|
+
app.head(
|
|
487
|
+
expressRoute,
|
|
488
|
+
async (req, res) => handleEndpointRoute(req, res, compiledFile, route, "GET")
|
|
489
|
+
);
|
|
490
|
+
if (debug) {
|
|
491
|
+
console.log(` HEAD ${expressRoute} (auto from GET)`);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
registered++;
|
|
495
|
+
}
|
|
496
|
+
if (registered > 0) {
|
|
497
|
+
console.log(`Registered ${registered} dynamic endpoint(s)`);
|
|
498
|
+
}
|
|
499
|
+
};
|
|
500
|
+
|
|
206
501
|
const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
|
|
207
502
|
const __dirname$1 = node_path.dirname(__filename$1);
|
|
208
503
|
const build = async ({
|
|
@@ -214,7 +509,9 @@ const build = async ({
|
|
|
214
509
|
const projectDirStatus = validateProjectDir(projectDir);
|
|
215
510
|
if (projectDirStatus.code !== "OK") return projectDirStatus;
|
|
216
511
|
const startTime = performance.now();
|
|
512
|
+
console.time("[build] readConfig");
|
|
217
513
|
const config = await readConfig(projectDir, debug);
|
|
514
|
+
console.timeEnd("[build] readConfig");
|
|
218
515
|
if (debug) {
|
|
219
516
|
console.log("Using config:", config);
|
|
220
517
|
}
|
|
@@ -264,29 +561,44 @@ const build = async ({
|
|
|
264
561
|
const isFullBuild = changeKind === "full" || changeKind === "config";
|
|
265
562
|
const tempExists = node_fs.existsSync(config.tmp);
|
|
266
563
|
if (isFullBuild) {
|
|
564
|
+
console.time("[build] pre-plugins");
|
|
267
565
|
for (const plugin of config.plugins || []) {
|
|
268
566
|
if (plugin.phase === "pre" && (plugin.mode === mode || plugin.mode === "both")) {
|
|
567
|
+
console.time(`[build] pre-plugin:${plugin.name}`);
|
|
269
568
|
if (debug) {
|
|
270
569
|
console.log(`Running pre-plugin: ${plugin.name}`);
|
|
271
570
|
}
|
|
272
571
|
await plugin.fn(projectDir, config);
|
|
572
|
+
console.timeEnd(`[build] pre-plugin:${plugin.name}`);
|
|
273
573
|
}
|
|
274
574
|
}
|
|
575
|
+
console.timeEnd("[build] pre-plugins");
|
|
275
576
|
}
|
|
577
|
+
console.time("[build] prepare-temp");
|
|
276
578
|
if (isFullBuild || !tempExists) {
|
|
277
579
|
if (tempExists) {
|
|
580
|
+
console.time("[build] prepare-temp:rmSync");
|
|
278
581
|
node_fs.rmSync(config.tmp, { recursive: true });
|
|
582
|
+
console.timeEnd("[build] prepare-temp:rmSync");
|
|
279
583
|
}
|
|
584
|
+
console.time("[build] prepare-temp:cp");
|
|
280
585
|
await promises.cp(projectDir, config.tmp, {
|
|
281
586
|
recursive: true,
|
|
282
587
|
filter: (src) => {
|
|
283
|
-
const relative = src.
|
|
284
|
-
|
|
588
|
+
const relative = src.startsWith(projectDir) ? src.slice(projectDir.length).replace(/^\//, "") : src;
|
|
589
|
+
const firstSegment = relative.split("/")[0];
|
|
590
|
+
if (firstSegment === "node_modules" || firstSegment === config.output || firstSegment === ".endpoints" || firstSegment === ".ssg-temp" || firstSegment === ".git" || firstSegment === "assets") {
|
|
285
591
|
return false;
|
|
286
592
|
}
|
|
287
593
|
return true;
|
|
288
594
|
}
|
|
289
595
|
});
|
|
596
|
+
console.timeEnd("[build] prepare-temp:cp");
|
|
597
|
+
const srcNodeModules = node_path.join(projectDir, "node_modules");
|
|
598
|
+
const tmpNodeModules = node_path.join(config.tmp, "node_modules");
|
|
599
|
+
if (node_fs.existsSync(srcNodeModules) && !node_fs.existsSync(tmpNodeModules)) {
|
|
600
|
+
node_fs.symlinkSync(srcNodeModules, tmpNodeModules, "dir");
|
|
601
|
+
}
|
|
290
602
|
} else {
|
|
291
603
|
const srcFile = node_path.join(projectDir, changedRelative);
|
|
292
604
|
const destFile = node_path.join(config.tmp, changedRelative);
|
|
@@ -298,6 +610,8 @@ const build = async ({
|
|
|
298
610
|
await promises.cp(srcFile, destFile);
|
|
299
611
|
}
|
|
300
612
|
}
|
|
613
|
+
console.timeEnd("[build] prepare-temp");
|
|
614
|
+
console.time("[build] copy-hydration");
|
|
301
615
|
await promises.cp(
|
|
302
616
|
node_path.resolve(node_path.join(__dirname$1, "components", "index.mjs")),
|
|
303
617
|
node_path.join(tmpComponentsDir, "hydrate.tsx")
|
|
@@ -306,7 +620,9 @@ const build = async ({
|
|
|
306
620
|
node_path.resolve(node_path.join(__dirname$1, "runtime.mjs")),
|
|
307
621
|
node_path.join(tmpComponentsDir, "runtime.ts")
|
|
308
622
|
);
|
|
623
|
+
console.timeEnd("[build] copy-hydration");
|
|
309
624
|
if (changeKind !== "asset") {
|
|
625
|
+
console.time("[build] esbuild-pages");
|
|
310
626
|
const pageEntryPoints = changeKind === "page" ? [node_path.join(config.tmp, changedRelative)] : [node_path.join(tmpPagesDir, "**/*.mdx")];
|
|
311
627
|
await esbuild.build({
|
|
312
628
|
entryPoints: pageEntryPoints,
|
|
@@ -325,8 +641,10 @@ const build = async ({
|
|
|
325
641
|
})
|
|
326
642
|
]
|
|
327
643
|
});
|
|
644
|
+
console.timeEnd("[build] esbuild-pages");
|
|
328
645
|
}
|
|
329
646
|
if (isFullBuild || changeKind === "component") {
|
|
647
|
+
console.time("[build] esbuild-components");
|
|
330
648
|
await esbuild.build({
|
|
331
649
|
entryPoints: [
|
|
332
650
|
node_path.join(tmpComponentsDir, "**/*.tsx"),
|
|
@@ -338,8 +656,10 @@ const build = async ({
|
|
|
338
656
|
target: ["esnext"],
|
|
339
657
|
outdir: tmpComponentsDir
|
|
340
658
|
});
|
|
659
|
+
console.timeEnd("[build] esbuild-components");
|
|
341
660
|
}
|
|
342
661
|
if (changeKind !== "asset") {
|
|
662
|
+
console.time("[build] render-pages");
|
|
343
663
|
let outputFiles;
|
|
344
664
|
if (changeKind === "page") {
|
|
345
665
|
const jsFile = node_path.join(config.tmp, changedRelative.replace(/\.mdx$/, ".js"));
|
|
@@ -356,20 +676,26 @@ const build = async ({
|
|
|
356
676
|
`${tmpPagesDir}${node_path.sep}`,
|
|
357
677
|
""
|
|
358
678
|
);
|
|
679
|
+
const pageLabel = relativeOutputHtmlFilePath;
|
|
359
680
|
if (debug) {
|
|
360
681
|
console.log("Processing output file (JS):", outputFile);
|
|
361
682
|
console.log("Relative output HTML path:", relativeOutputHtmlFilePath);
|
|
362
683
|
}
|
|
684
|
+
console.time(`[build] page:${pageLabel} import`);
|
|
363
685
|
const code = await promises.readFile(outputFile, "utf-8");
|
|
364
686
|
const encoded = Buffer.from(code).toString("base64");
|
|
365
687
|
const dataUrl = `data:text/javascript;base64,${encoded}`;
|
|
366
688
|
const exports$1 = await import(dataUrl);
|
|
689
|
+
console.timeEnd(`[build] page:${pageLabel} import`);
|
|
367
690
|
if (debug) {
|
|
368
691
|
console.log("exports", exports$1);
|
|
369
692
|
}
|
|
693
|
+
console.time(`[build] page:${pageLabel} vdom`);
|
|
370
694
|
let vdom = exports$1.default(exports$1);
|
|
695
|
+
console.timeEnd(`[build] page:${pageLabel} vdom`);
|
|
371
696
|
for (const plugin of config.plugins || []) {
|
|
372
697
|
if (plugin.phase === "page-vdom" && (plugin.mode === mode || plugin.mode === "both")) {
|
|
698
|
+
console.time(`[build] page:${pageLabel} plugin:${plugin.name}`);
|
|
373
699
|
if (debug) {
|
|
374
700
|
console.log(`Running page-vdom plugin: ${plugin.name}`);
|
|
375
701
|
}
|
|
@@ -380,16 +706,20 @@ const build = async ({
|
|
|
380
706
|
config,
|
|
381
707
|
exports$1
|
|
382
708
|
);
|
|
709
|
+
console.timeEnd(`[build] page:${pageLabel} plugin:${plugin.name}`);
|
|
383
710
|
}
|
|
384
711
|
}
|
|
712
|
+
console.time(`[build] page:${pageLabel} renderSync`);
|
|
385
713
|
const browserGlobals = server.getBrowserGlobals();
|
|
386
714
|
const document = server.getDocument(false, browserGlobals);
|
|
387
715
|
browserGlobals.document = document;
|
|
388
716
|
let el = server.renderSync(vdom, document.documentElement, {
|
|
389
717
|
browserGlobals
|
|
390
718
|
});
|
|
719
|
+
console.timeEnd(`[build] page:${pageLabel} renderSync`);
|
|
391
720
|
for (const plugin of config.plugins || []) {
|
|
392
721
|
if (plugin.phase === "page-dom" && (plugin.mode === mode || plugin.mode === "both")) {
|
|
722
|
+
console.time(`[build] page:${pageLabel} dom-plugin:${plugin.name}`);
|
|
393
723
|
if (debug) {
|
|
394
724
|
console.log(`Running page-dom plugin: ${plugin.name}`);
|
|
395
725
|
}
|
|
@@ -399,11 +729,15 @@ const build = async ({
|
|
|
399
729
|
projectDir,
|
|
400
730
|
config
|
|
401
731
|
);
|
|
732
|
+
console.timeEnd(`[build] page:${pageLabel} dom-plugin:${plugin.name}`);
|
|
402
733
|
}
|
|
403
734
|
}
|
|
735
|
+
console.time(`[build] page:${pageLabel} renderToString`);
|
|
404
736
|
let html = server.renderToString(el);
|
|
737
|
+
console.timeEnd(`[build] page:${pageLabel} renderToString`);
|
|
405
738
|
for (const plugin of config.plugins || []) {
|
|
406
739
|
if (plugin.phase === "page-html" && (plugin.mode === mode || plugin.mode === "both")) {
|
|
740
|
+
console.time(`[build] page:${pageLabel} html-plugin:${plugin.name}`);
|
|
407
741
|
if (debug) {
|
|
408
742
|
console.log(`Running page-html plugin: ${plugin.name}`);
|
|
409
743
|
}
|
|
@@ -413,6 +747,7 @@ const build = async ({
|
|
|
413
747
|
projectDir,
|
|
414
748
|
config
|
|
415
749
|
);
|
|
750
|
+
console.timeEnd(`[build] page:${pageLabel} html-plugin:${plugin.name}`);
|
|
416
751
|
}
|
|
417
752
|
}
|
|
418
753
|
const finalOutputFile = node_path.join(
|
|
@@ -424,16 +759,27 @@ const build = async ({
|
|
|
424
759
|
if (!node_fs.existsSync(finalOutputDir)) {
|
|
425
760
|
node_fs.mkdirSync(finalOutputDir, { recursive: true });
|
|
426
761
|
}
|
|
762
|
+
console.time(`[build] page:${pageLabel} writeFile`);
|
|
427
763
|
await promises.writeFile(finalOutputFile, html);
|
|
764
|
+
console.timeEnd(`[build] page:${pageLabel} writeFile`);
|
|
428
765
|
}
|
|
766
|
+
console.timeEnd("[build] render-pages");
|
|
429
767
|
}
|
|
768
|
+
if (isFullBuild || changeKind === "page") {
|
|
769
|
+
console.time("[build] build-endpoints");
|
|
770
|
+
await buildEndpoints(projectDir, config, debug);
|
|
771
|
+
console.timeEnd("[build] build-endpoints");
|
|
772
|
+
}
|
|
773
|
+
console.time("[build] copy-outputs");
|
|
430
774
|
if (isFullBuild || changeKind === "component") {
|
|
431
775
|
await promises.cp(tmpComponentsDir, outputComponentsDir, { recursive: true });
|
|
432
776
|
}
|
|
433
777
|
if (isFullBuild || changeKind === "asset") {
|
|
434
778
|
await promises.cp(inputAssetsDir, outputAssetsDir, { recursive: true });
|
|
435
779
|
}
|
|
780
|
+
console.timeEnd("[build] copy-outputs");
|
|
436
781
|
if (isFullBuild || changeKind === "component") {
|
|
782
|
+
console.time("[build] post-plugins");
|
|
437
783
|
for (const plugin of config.plugins || []) {
|
|
438
784
|
if (plugin.phase === "post" && (plugin.mode === mode || plugin.mode === "both")) {
|
|
439
785
|
if (debug) {
|
|
@@ -442,6 +788,7 @@ const build = async ({
|
|
|
442
788
|
await plugin.fn(projectDir, config);
|
|
443
789
|
}
|
|
444
790
|
}
|
|
791
|
+
console.timeEnd("[build] post-plugins");
|
|
445
792
|
}
|
|
446
793
|
if (mode === "build" && !debug) {
|
|
447
794
|
node_fs.rmSync(config.tmp, { recursive: true });
|
|
@@ -501,6 +848,15 @@ const serve = async ({
|
|
|
501
848
|
message: `Port ${port} is already in use. Please stop the process using this port or choose a different port.`
|
|
502
849
|
};
|
|
503
850
|
}
|
|
851
|
+
console.time("[serve] register-endpoints");
|
|
852
|
+
await registerEndpoints(app, projectDir, config, debug);
|
|
853
|
+
console.timeEnd("[serve] register-endpoints");
|
|
854
|
+
app.use((_req, res, next) => {
|
|
855
|
+
res.set("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate");
|
|
856
|
+
res.set("Pragma", "no-cache");
|
|
857
|
+
res.set("Expires", "0");
|
|
858
|
+
next();
|
|
859
|
+
});
|
|
504
860
|
app.use(express.static(outputDir));
|
|
505
861
|
const server = app.listen(port, (listenedPort) => {
|
|
506
862
|
console.log(
|
|
@@ -523,7 +879,9 @@ const serve = async ({
|
|
|
523
879
|
}
|
|
524
880
|
isBuilding = true;
|
|
525
881
|
try {
|
|
882
|
+
console.time("[serve] rebuild");
|
|
526
883
|
await build({ projectDir, debug, mode: "serve", changedFile: filePath });
|
|
884
|
+
console.timeEnd("[serve] rebuild");
|
|
527
885
|
liveReloadServer.clients.forEach((client) => {
|
|
528
886
|
if (client.readyState === 1) {
|
|
529
887
|
client.send(
|
|
@@ -576,9 +934,16 @@ const serve = async ({
|
|
|
576
934
|
return { code: "OK", message: "Server is running" };
|
|
577
935
|
};
|
|
578
936
|
|
|
937
|
+
exports.HTTP_METHODS = HTTP_METHODS;
|
|
579
938
|
exports.build = build;
|
|
939
|
+
exports.buildEndpoints = buildEndpoints;
|
|
940
|
+
exports.compileEndpoints = compileEndpoints;
|
|
580
941
|
exports.configDefaults = configDefaults;
|
|
942
|
+
exports.discoverEndpointSourceFiles = discoverEndpointSourceFiles;
|
|
943
|
+
exports.endpointFileToRoute = endpointFileToRoute;
|
|
581
944
|
exports.readConfig = readConfig;
|
|
945
|
+
exports.registerEndpoints = registerEndpoints;
|
|
582
946
|
exports.rehypePlugins = rehypePlugins;
|
|
583
947
|
exports.remarkPlugins = remarkPlugins;
|
|
948
|
+
exports.resolveEndpoints = resolveEndpoints;
|
|
584
949
|
exports.serve = serve;
|