olovaplugin 1.0.4 → 1.0.6

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.
@@ -0,0 +1,1601 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // olova-plugins.ts
31
+ var olova_plugins_exports = {};
32
+ __export(olova_plugins_exports, {
33
+ autoGeneratePlugin: () => autoGeneratePlugin,
34
+ cleanUrlPlugin: () => cleanUrlPlugin,
35
+ colors: () => colors3,
36
+ configPlugin: () => configPlugin,
37
+ createTimer: () => createTimer,
38
+ formatBytes: () => formatBytes2,
39
+ formatTime: () => formatTime2,
40
+ frameworkPlugin: () => frameworkPlugin,
41
+ generateBuildId: () => generateBuildId,
42
+ generateCriticalCSS: () => generateCriticalCSS,
43
+ generateJsonLd: () => generateJsonLd,
44
+ generateOlovaHydration: () => generateOlovaHydration,
45
+ generatePerformanceMeta: () => generatePerformanceMeta,
46
+ generateResourceHints: () => generateResourceHints,
47
+ generateServiceWorkerContent: () => generateServiceWorkerContent,
48
+ generateServiceWorkerScript: () => generateServiceWorkerScript,
49
+ logger: () => logger2,
50
+ minifyHtml: () => minifyHtml,
51
+ olovaPlugins: () => olovaPlugins,
52
+ parseFlightData: () => parseFlightData,
53
+ proactiveErrorPlugin: () => proactiveErrorPlugin,
54
+ routerPlugin: () => routerPlugin,
55
+ ssgPlugin: () => ssgPlugin,
56
+ symbols: () => symbols2,
57
+ virtualHtmlPlugin: () => virtualHtmlPlugin
58
+ });
59
+ module.exports = __toCommonJS(olova_plugins_exports);
60
+
61
+ // router/config.ts
62
+ var import_fs = __toESM(require("fs"));
63
+ var import_path = __toESM(require("path"));
64
+ var colors = {
65
+ reset: "\x1B[0m",
66
+ bold: "\x1B[1m",
67
+ dim: "\x1B[2m",
68
+ cyan: "\x1B[36m",
69
+ green: "\x1B[32m",
70
+ white: "\x1B[37m"
71
+ };
72
+ function configPlugin() {
73
+ const olovaDir = import_path.default.resolve(".olova");
74
+ return {
75
+ name: "olova-config",
76
+ config(_config, { command }) {
77
+ if (!import_fs.default.existsSync(olovaDir)) {
78
+ import_fs.default.mkdirSync(olovaDir, { recursive: true });
79
+ }
80
+ if (process.env.IS_SSG_BUILD) {
81
+ return {
82
+ appType: "custom",
83
+ cacheDir: "./.olova/cache"
84
+ };
85
+ }
86
+ if (command === "serve") {
87
+ console.log("");
88
+ console.log(` ${colors.bold}${colors.cyan}\u25B2 Olova${colors.reset} ${colors.dim}v1.0.0${colors.reset}`);
89
+ console.log("");
90
+ }
91
+ return {
92
+ appType: "custom",
93
+ cacheDir: "./.olova/cache",
94
+ build: {
95
+ outDir: "./.olova/dist",
96
+ sourcemap: false,
97
+ chunkSizeWarningLimit: 500,
98
+ rollupOptions: {
99
+ onwarn(warning, warn) {
100
+ if (warning.code === "PLUGIN_WARNING" && warning.message?.includes("dynamic import will not move")) {
101
+ return;
102
+ }
103
+ if (warning.message?.includes("Module level directives") && warning.message?.includes("static")) {
104
+ return;
105
+ }
106
+ if (warning.message?.includes("sourcemap") && warning.message?.includes("Can't resolve original location")) {
107
+ return;
108
+ }
109
+ warn(warning);
110
+ },
111
+ output: {
112
+ chunkFileNames: "pro_olova_static/chunks/[name]-[hash].js",
113
+ entryFileNames: "pro_olova_static/olova-[hash].js",
114
+ assetFileNames: "pro_olova_static/[name]-[hash][extname]",
115
+ manualChunks(id) {
116
+ if (id.includes("node_modules")) {
117
+ if (id.includes("react-dom")) {
118
+ return "vendor-react-dom";
119
+ }
120
+ if (id.includes("react")) {
121
+ return "vendor-react";
122
+ }
123
+ return "vendor";
124
+ }
125
+ }
126
+ }
127
+ }
128
+ },
129
+ preview: {
130
+ port: 4173,
131
+ strictPort: false
132
+ }
133
+ };
134
+ },
135
+ configureServer(server) {
136
+ server.httpServer?.once("listening", () => {
137
+ const address = server.httpServer?.address();
138
+ const port = typeof address === "object" && address ? address.port : 5173;
139
+ setTimeout(() => {
140
+ console.log(` ${colors.green}\u2713${colors.reset} Ready in ${colors.dim}${Math.round(performance.now())}ms${colors.reset}`);
141
+ console.log("");
142
+ console.log(` ${colors.dim}\u279C${colors.reset} ${colors.bold}Local:${colors.reset} ${colors.cyan}http://localhost:${port}/${colors.reset}`);
143
+ console.log("");
144
+ }, 100);
145
+ });
146
+ }
147
+ };
148
+ }
149
+
150
+ // router/router.ts
151
+ var import_fs2 = __toESM(require("fs"));
152
+ var import_path2 = __toESM(require("path"));
153
+ function routerPlugin() {
154
+ const virtualModuleId = "olova/routes";
155
+ const resolvedVirtualModuleId = "\0" + virtualModuleId;
156
+ let server = null;
157
+ const invalidateRoutes = () => {
158
+ if (server) {
159
+ const mod = server.moduleGraph.getModuleById(resolvedVirtualModuleId);
160
+ if (mod) {
161
+ server.moduleGraph.invalidateModule(mod);
162
+ server.ws.send({
163
+ type: "full-reload",
164
+ path: "*"
165
+ });
166
+ }
167
+ }
168
+ };
169
+ return {
170
+ name: "olova-router",
171
+ // Configure dev server to watch for file changes
172
+ configureServer(devServer) {
173
+ server = devServer;
174
+ const srcDir = import_path2.default.resolve(__dirname, "..", "src");
175
+ const watcher = devServer.watcher;
176
+ watcher.on("add", (filePath) => {
177
+ if (filePath.startsWith(srcDir) && /\.(tsx|jsx|html|md)$/.test(filePath)) {
178
+ console.log("\x1B[36m[olova]\x1B[0m New route detected:", import_path2.default.relative(srcDir, filePath));
179
+ invalidateRoutes();
180
+ }
181
+ });
182
+ watcher.on("unlink", (filePath) => {
183
+ if (filePath.startsWith(srcDir) && /\.(tsx|jsx|html|md)$/.test(filePath)) {
184
+ console.log("\x1B[36m[olova]\x1B[0m Route removed:", import_path2.default.relative(srcDir, filePath));
185
+ invalidateRoutes();
186
+ }
187
+ });
188
+ watcher.on("addDir", (dirPath) => {
189
+ if (dirPath.startsWith(srcDir) && dirPath !== srcDir) {
190
+ console.log("\x1B[36m[olova]\x1B[0m New route folder detected:", import_path2.default.relative(srcDir, dirPath));
191
+ }
192
+ });
193
+ watcher.on("unlinkDir", (dirPath) => {
194
+ if (dirPath.startsWith(srcDir) && dirPath !== srcDir) {
195
+ console.log("\x1B[36m[olova]\x1B[0m Route folder removed:", import_path2.default.relative(srcDir, dirPath));
196
+ invalidateRoutes();
197
+ }
198
+ });
199
+ },
200
+ resolveId(id) {
201
+ if (id === virtualModuleId) {
202
+ return resolvedVirtualModuleId;
203
+ }
204
+ },
205
+ // Mark the virtual module as having side effects for proper HMR
206
+ handleHotUpdate({ file, server: devServer }) {
207
+ const srcDir = import_path2.default.resolve(__dirname, "..", "src");
208
+ if (file.startsWith(srcDir) && /\.(tsx|jsx)$/.test(file)) {
209
+ const mod = devServer.moduleGraph.getModuleById(resolvedVirtualModuleId);
210
+ if (mod) {
211
+ devServer.moduleGraph.invalidateModule(mod);
212
+ }
213
+ }
214
+ },
215
+ load(id) {
216
+ if (id === resolvedVirtualModuleId) {
217
+ const srcDir = import_path2.default.resolve(__dirname, "..", "src");
218
+ const getRoutes = (dir, baseRoute = "", baseImportPath = "") => {
219
+ const entries = import_fs2.default.readdirSync(dir, { withFileTypes: true });
220
+ let routes = [];
221
+ for (const entry of entries) {
222
+ const entryName = entry.name;
223
+ const fullPath = import_path2.default.join(dir, entryName);
224
+ if (entry.isDirectory()) {
225
+ const isRouteGroup = /^\(.+\)$/.test(entryName);
226
+ if (isRouteGroup) {
227
+ routes = routes.concat(getRoutes(
228
+ fullPath,
229
+ baseRoute,
230
+ // URL path stays the same (group is ignored)
231
+ `${baseImportPath}/${entryName}`
232
+ // Import path includes the folder
233
+ ));
234
+ } else {
235
+ routes = routes.concat(getRoutes(
236
+ fullPath,
237
+ `${baseRoute}/${entryName}`,
238
+ `${baseImportPath}/${entryName}`
239
+ ));
240
+ }
241
+ } else {
242
+ const ext = import_path2.default.extname(entryName);
243
+ const supportedExts = [".tsx", ".jsx", ".html", ".md"];
244
+ if (supportedExts.includes(ext)) {
245
+ const nameNoExt = entryName.replace(/\.(tsx|jsx|html|md)$/, "");
246
+ let routePath = baseRoute;
247
+ if (nameNoExt !== "index" && nameNoExt !== "App") {
248
+ routePath = `${baseRoute}/${nameNoExt}`;
249
+ }
250
+ if (routePath === "") routePath = "/";
251
+ const normalizedRoutePath = routePath.replace(/\$(.+?)(?=\/|$)/g, ":$1");
252
+ const importPath = `/src${baseImportPath}/${entryName}`;
253
+ if (ext === ".html") {
254
+ routes.push(` "${normalizedRoutePath}": () => import("${importPath}?raw").then(m => ({ default: () => { const div = document.createElement('div'); div.innerHTML = m.default; return div.innerHTML; }, __isHtml: true, __isStatic: true, __rawHtml: m.default })),`);
255
+ } else if (ext === ".md") {
256
+ routes.push(` "${normalizedRoutePath}": () => import("${importPath}?raw").then(m => ({ default: m.default, __isMd: true, __isStatic: true })),`);
257
+ } else {
258
+ const fileContent = import_fs2.default.readFileSync(fullPath, "utf-8");
259
+ const firstLine = fileContent.trim().split("\n")[0].trim();
260
+ const hasStaticDirective = firstLine === '"static"' || firstLine === "'static'";
261
+ if (hasStaticDirective) {
262
+ routes.push(` "${normalizedRoutePath}": () => import("${importPath}").then(m => Object.assign({}, m, { __isStatic: true })),`);
263
+ } else {
264
+ routes.push(` "${normalizedRoutePath}": () => import("${importPath}"),`);
265
+ }
266
+ }
267
+ }
268
+ }
269
+ }
270
+ return routes;
271
+ };
272
+ let routeLines = [];
273
+ if (import_fs2.default.existsSync(srcDir)) {
274
+ routeLines = getRoutes(srcDir);
275
+ }
276
+ return `export const routes = {
277
+ ${routeLines.join("\n")}
278
+ };`;
279
+ }
280
+ }
281
+ };
282
+ }
283
+
284
+ // router/framework.ts
285
+ var import_vite = require("vite");
286
+ function frameworkPlugin() {
287
+ const virtualClientEntry = "olova/client";
288
+ const resolvedVirtualClientEntry = "\0" + virtualClientEntry;
289
+ const virtualServerEntry = "olova/server";
290
+ const resolvedVirtualServerEntry = "\0" + virtualServerEntry;
291
+ return {
292
+ name: "olova-framework",
293
+ resolveId(id) {
294
+ if (id === virtualClientEntry || id === "/olova/client" || id === "olova/client.tsx") return resolvedVirtualClientEntry;
295
+ if (id === virtualServerEntry || id === "/olova/server" || id === "olova/server.tsx") return resolvedVirtualServerEntry;
296
+ },
297
+ async load(id) {
298
+ if (id === resolvedVirtualClientEntry) {
299
+ const code = `
300
+ import React from 'react';
301
+ import { hydrateRoot, createRoot } from 'react-dom/client';
302
+ import Layout, { metadata as defaultMetadata } from '/src/root.tsx';
303
+ import { Router, loadRoute } from '/route.tsx';
304
+
305
+ // Helper to generate SEO meta tags
306
+ function generateSeoTags(metadata) {
307
+ const meta = { ...defaultMetadata, ...metadata };
308
+ const tags = [];
309
+
310
+ // Title
311
+ if (meta.title) {
312
+ document.title = meta.title;
313
+ }
314
+
315
+ // Description
316
+ if (meta.description) {
317
+ let tag = document.querySelector('meta[name="description"]');
318
+ if (!tag) {
319
+ tag = document.createElement('meta');
320
+ tag.setAttribute('name', 'description');
321
+ document.head.appendChild(tag);
322
+ }
323
+ tag.setAttribute('content', meta.description);
324
+ }
325
+
326
+ // Keywords
327
+ if (meta.keywords) {
328
+ const content = Array.isArray(meta.keywords) ? meta.keywords.join(', ') : meta.keywords;
329
+ let tag = document.querySelector('meta[name="keywords"]');
330
+ if (!tag) {
331
+ tag = document.createElement('meta');
332
+ tag.setAttribute('name', 'keywords');
333
+ document.head.appendChild(tag);
334
+ }
335
+ tag.setAttribute('content', content);
336
+ }
337
+
338
+ // Open Graph
339
+ if (meta.openGraph) {
340
+ const og = meta.openGraph;
341
+ const ogTags = [
342
+ ['og:title', og.title || meta.title],
343
+ ['og:description', og.description],
344
+ ['og:url', og.url],
345
+ ['og:site_name', og.siteName],
346
+ ['og:type', og.type],
347
+ ];
348
+ ogTags.forEach(([prop, content]) => {
349
+ if (content) {
350
+ let tag = document.querySelector(\`meta[property="\${prop}"]\`);
351
+ if (!tag) {
352
+ tag = document.createElement('meta');
353
+ tag.setAttribute('property', prop);
354
+ document.head.appendChild(tag);
355
+ }
356
+ tag.setAttribute('content', content);
357
+ }
358
+ });
359
+ }
360
+
361
+ // Twitter Card
362
+ if (meta.twitter) {
363
+ const tw = meta.twitter;
364
+ const twTags = [
365
+ ['twitter:card', tw.card || 'summary'],
366
+ ['twitter:site', tw.site],
367
+ ['twitter:creator', tw.creator],
368
+ ['twitter:title', tw.title || meta.title],
369
+ ['twitter:description', tw.description],
370
+ ];
371
+ twTags.forEach(([name, content]) => {
372
+ if (content) {
373
+ let tag = document.querySelector(\`meta[name="\${name}"]\`);
374
+ if (!tag) {
375
+ tag = document.createElement('meta');
376
+ tag.setAttribute('name', name);
377
+ document.head.appendChild(tag);
378
+ }
379
+ tag.setAttribute('content', content);
380
+ }
381
+ });
382
+ }
383
+ }
384
+
385
+ const path = window.location.pathname;
386
+ loadRoute(path).then((result) => {
387
+ const Component = result ? result.module.default : () => <div>404 Not Found</div>;
388
+ // @ts-ignore
389
+ const serverData = window.__OLOVA_DATA__ || {};
390
+ const params = serverData.params || (result ? result.params : {});
391
+ const metadata = serverData.metadata || (result ? result.metadata : undefined);
392
+
393
+ // Framework handles SEO automatically (like Next.js)
394
+ generateSeoTags(metadata);
395
+
396
+ const rootElement = document.getElementById('root');
397
+ const app = (
398
+ <React.StrictMode>
399
+ <Layout>
400
+ <Router url={path} initialComponent={Component} initialParams={params} onRouteChange={(newMetadata) => generateSeoTags(newMetadata)} />
401
+ </Layout>
402
+ </React.StrictMode>
403
+ );
404
+
405
+ if (rootElement && rootElement.innerHTML.trim() !== "") {
406
+ console.log("[Olova] Hydrating pre-rendered content");
407
+ hydrateRoot(document, app);
408
+ } else {
409
+ console.log("[Olova] Rendering client-side (no SSR content found)");
410
+ createRoot(document).render(app);
411
+ }
412
+ });`;
413
+ const result = await (0, import_vite.transformWithEsbuild)(code, "olova-client.tsx", {
414
+ loader: "tsx",
415
+ jsx: "automatic"
416
+ });
417
+ return result.code;
418
+ }
419
+ if (id === resolvedVirtualServerEntry) {
420
+ const code = `
421
+ import React from 'react';
422
+ import { renderToString } from 'react-dom/server';
423
+ import Layout, { metadata as defaultMetadata } from '/src/root.tsx';
424
+ import { Router, loadRoute } from '/route.tsx';
425
+
426
+ // Generate SEO head content
427
+ function generateSeoHead(metadata) {
428
+ const meta = { ...defaultMetadata, ...metadata };
429
+ let head = '';
430
+
431
+ // Title
432
+ if (meta.title) {
433
+ head += \`<title>\${meta.title}</title>\\n\`;
434
+ }
435
+
436
+ // Basic meta
437
+ if (meta.description) {
438
+ head += \`<meta name="description" content="\${meta.description}" />\\n\`;
439
+ }
440
+ if (meta.keywords) {
441
+ const content = Array.isArray(meta.keywords) ? meta.keywords.join(', ') : meta.keywords;
442
+ head += \`<meta name="keywords" content="\${content}" />\\n\`;
443
+ }
444
+ if (meta.robots) {
445
+ head += \`<meta name="robots" content="\${meta.robots}" />\\n\`;
446
+ }
447
+ if (meta.canonical) {
448
+ head += \`<link rel="canonical" href="\${meta.canonical}" />\\n\`;
449
+ }
450
+
451
+ // Open Graph
452
+ if (meta.openGraph) {
453
+ const og = meta.openGraph;
454
+ head += \`<meta property="og:title" content="\${og.title || meta.title}" />\\n\`;
455
+ if (og.description) head += \`<meta property="og:description" content="\${og.description}" />\\n\`;
456
+ if (og.url) head += \`<meta property="og:url" content="\${og.url}" />\\n\`;
457
+ if (og.siteName) head += \`<meta property="og:site_name" content="\${og.siteName}" />\\n\`;
458
+ if (og.type) head += \`<meta property="og:type" content="\${og.type}" />\\n\`;
459
+ if (og.images) {
460
+ og.images.forEach(img => {
461
+ head += \`<meta property="og:image" content="\${img.url}" />\\n\`;
462
+ });
463
+ }
464
+ }
465
+
466
+ // Twitter Card
467
+ if (meta.twitter) {
468
+ const tw = meta.twitter;
469
+ head += \`<meta name="twitter:card" content="\${tw.card || 'summary'}" />\\n\`;
470
+ if (tw.site) head += \`<meta name="twitter:site" content="\${tw.site}" />\\n\`;
471
+ if (tw.creator) head += \`<meta name="twitter:creator" content="\${tw.creator}" />\\n\`;
472
+ head += \`<meta name="twitter:title" content="\${tw.title || meta.title}" />\\n\`;
473
+ if (tw.description) head += \`<meta name="twitter:description" content="\${tw.description}" />\\n\`;
474
+ if (tw.images) {
475
+ tw.images.forEach(img => {
476
+ head += \`<meta name="twitter:image" content="\${img}" />\\n\`;
477
+ });
478
+ }
479
+ }
480
+
481
+ return head;
482
+ }
483
+
484
+ export async function render(url) {
485
+ const result = await loadRoute(url);
486
+ const Component = result ? result.module.default : () => <div>404 Not Found</div>;
487
+ const params = result ? result.params : {};
488
+ const metadata = result ? result.metadata : undefined;
489
+
490
+ // Generate the SEO head content
491
+ const seoHead = generateSeoHead(metadata);
492
+
493
+ let html = renderToString(
494
+ <Layout>
495
+ <Router url={url} initialComponent={Component} initialParams={params} />
496
+ </Layout>
497
+ );
498
+
499
+ // Inject SEO tags into head (framework handles this automatically)
500
+ if (html.includes('</head>')) {
501
+ html = html.replace('</head>', seoHead + '</head>');
502
+ }
503
+
504
+ return { html, hydrationData: { params, metadata } };
505
+ }
506
+
507
+ // Render empty shell from Layout (for client-only pages)
508
+ export function renderShell() {
509
+ const seoHead = generateSeoHead({});
510
+ let html = renderToString(<Layout>{null}</Layout>);
511
+ if (html.includes('</head>')) {
512
+ html = html.replace('</head>', seoHead + '</head>');
513
+ }
514
+ return html;
515
+ }
516
+
517
+ // Render shell WITH metadata for client-only pages that have metadata exports
518
+ // This enables SEO pre-rendering even for pages without "static" directive
519
+ export function renderShellWithMetadata(metadata) {
520
+ const seoHead = generateSeoHead(metadata);
521
+ let html = renderToString(<Layout>{null}</Layout>);
522
+ if (html.includes('</head>')) {
523
+ html = html.replace('</head>', seoHead + '</head>');
524
+ }
525
+ return html;
526
+ }
527
+
528
+ // Re-export loadRoute so it can be used during SSG to extract metadata from any route
529
+ export { loadRoute };`;
530
+ const result = await (0, import_vite.transformWithEsbuild)(code, "olova-server.tsx", {
531
+ loader: "tsx",
532
+ jsx: "automatic"
533
+ });
534
+ return result.code;
535
+ }
536
+ }
537
+ };
538
+ }
539
+
540
+ // router/virtual-html.ts
541
+ var import_fs3 = __toESM(require("fs"));
542
+ var import_path3 = __toESM(require("path"));
543
+
544
+ // router/hydration.ts
545
+ function generateOlovaHydration(data, buildId) {
546
+ const meta = data.metadata || {};
547
+ const scripts = [];
548
+ scripts.push(`<script>self.__olova_f=self.__olova_f||[];function _oF(d){self.__olova_f.push(d)}</script>`);
549
+ const routeChunk = {
550
+ type: "R",
551
+ data: {
552
+ path: data.route,
553
+ params: data.params || {},
554
+ pattern: data.route === "/" ? "/" : data.route.replace(/\/[^/]+$/, "/:slug"),
555
+ isStatic: data.isStatic ?? true,
556
+ buildId
557
+ }
558
+ };
559
+ scripts.push(`<script>_oF([0,"${routeChunk.type}",${JSON.stringify(routeChunk.data)}])</script>`);
560
+ const metadataChunk = {
561
+ type: "M",
562
+ data: {
563
+ title: meta.title || "Olova App",
564
+ description: meta.description || "",
565
+ keywords: Array.isArray(meta.keywords) ? meta.keywords : [],
566
+ robots: meta.robots || "index, follow",
567
+ canonical: meta.canonical || null,
568
+ og: {
569
+ type: "website",
570
+ locale: "en_US",
571
+ ...meta.openGraph || {}
572
+ },
573
+ twitter: {
574
+ card: "summary_large_image",
575
+ ...meta.twitter || {}
576
+ }
577
+ }
578
+ };
579
+ scripts.push(`<script>_oF([1,"${metadataChunk.type}",${JSON.stringify(metadataChunk.data)}])</script>`);
580
+ const pageName = data.route === "/" ? "HomePage" : data.route.slice(1).split("/").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("") + "Page";
581
+ const treeChunk = {
582
+ type: "T",
583
+ data: {
584
+ layout: "RootLayout",
585
+ page: pageName,
586
+ template: null,
587
+ loading: null,
588
+ error: null,
589
+ notFound: null
590
+ }
591
+ };
592
+ scripts.push(`<script>_oF([2,"${treeChunk.type}",${JSON.stringify(treeChunk.data)}])</script>`);
593
+ const structuredData = {
594
+ type: "D",
595
+ data: [
596
+ {
597
+ "@context": "https://schema.org",
598
+ "@type": "WebPage",
599
+ name: meta.title || "Olova App",
600
+ description: meta.description || "",
601
+ url: data.route
602
+ },
603
+ {
604
+ "@context": "https://schema.org",
605
+ "@type": "WebApplication",
606
+ name: "Olova",
607
+ applicationCategory: "WebApplication",
608
+ operatingSystem: "Any"
609
+ }
610
+ ]
611
+ };
612
+ scripts.push(`<script>_oF([3,"${structuredData.type}",${JSON.stringify(structuredData.data)}])</script>`);
613
+ const assetsChunk = {
614
+ type: "A",
615
+ data: {
616
+ chunks: data.chunks || [],
617
+ styles: [],
618
+ // Prefetch hints for next likely navigations
619
+ prefetch: (data.chunks || []).slice(0, 5)
620
+ }
621
+ };
622
+ scripts.push(`<script>_oF([4,"${assetsChunk.type}",${JSON.stringify(assetsChunk.data)}])</script>`);
623
+ const hintsChunk = {
624
+ type: "H",
625
+ data: {
626
+ dnsPrefetch: ["fonts.googleapis.com", "fonts.gstatic.com"],
627
+ preconnect: ["https://fonts.googleapis.com", "https://fonts.gstatic.com"],
628
+ modulePreload: (data.chunks || []).slice(0, 3)
629
+ }
630
+ };
631
+ scripts.push(`<script>_oF([5,"${hintsChunk.type}",${JSON.stringify(hintsChunk.data)}])</script>`);
632
+ const stateChunk = {
633
+ type: "S",
634
+ data: {
635
+ hydrated: false,
636
+ streaming: false,
637
+ ready: true,
638
+ timestamp: Date.now(),
639
+ version: "1.0.0",
640
+ buildId
641
+ }
642
+ };
643
+ scripts.push(`<script>_oF([6,"${stateChunk.type}",${JSON.stringify(stateChunk.data)}])</script>`);
644
+ scripts.push(`<script>_oF([7,"E",null])</script>`);
645
+ const globalPayload = {
646
+ $route: routeChunk.data,
647
+ $meta: metadataChunk.data,
648
+ $tree: treeChunk.data,
649
+ $schema: structuredData.data,
650
+ $assets: assetsChunk.data,
651
+ $hints: hintsChunk.data,
652
+ $state: stateChunk.data,
653
+ $build: {
654
+ id: buildId,
655
+ version: "1.0.0",
656
+ timestamp: Date.now(),
657
+ env: "production"
658
+ }
659
+ };
660
+ scripts.push(`<script>$OLOVA=${JSON.stringify(globalPayload)}</script>`);
661
+ return scripts.join("");
662
+ }
663
+ function generateJsonLd(data) {
664
+ const meta = data.metadata || {};
665
+ const jsonLd = {
666
+ "@context": "https://schema.org",
667
+ "@graph": [
668
+ {
669
+ "@type": "WebPage",
670
+ "@id": data.route,
671
+ name: meta.title || "Olova App",
672
+ description: meta.description || "",
673
+ url: data.route,
674
+ isPartOf: {
675
+ "@type": "WebSite",
676
+ name: "Olova App"
677
+ }
678
+ },
679
+ {
680
+ "@type": "BreadcrumbList",
681
+ itemListElement: data.route.split("/").filter(Boolean).map((segment, index, arr) => ({
682
+ "@type": "ListItem",
683
+ position: index + 1,
684
+ name: segment.charAt(0).toUpperCase() + segment.slice(1),
685
+ item: "/" + arr.slice(0, index + 1).join("/")
686
+ }))
687
+ }
688
+ ]
689
+ };
690
+ return `<script type="application/ld+json">${JSON.stringify(jsonLd)}</script>`;
691
+ }
692
+ function parseFlightData() {
693
+ if (typeof globalThis === "undefined" || typeof globalThis.document === "undefined") {
694
+ return null;
695
+ }
696
+ const flightArray = globalThis.__olova_f;
697
+ if (!flightArray) return null;
698
+ const result = {};
699
+ const typeMap = {
700
+ "M": "$meta",
701
+ "T": "$tree",
702
+ "R": "$route",
703
+ "P": "$params",
704
+ "A": "$assets",
705
+ "S": "$state",
706
+ "D": "$schema",
707
+ "H": "$hints",
708
+ "E": "$end"
709
+ };
710
+ for (const chunk of flightArray) {
711
+ if (Array.isArray(chunk) && chunk.length >= 3) {
712
+ const [_index, type, data] = chunk;
713
+ const key = typeMap[type];
714
+ if (key && key !== "$end") {
715
+ result[key] = data;
716
+ }
717
+ }
718
+ }
719
+ return result;
720
+ }
721
+
722
+ // router/virtual-html.ts
723
+ function virtualHtmlPlugin() {
724
+ const htmlTemplate = `<!DOCTYPE html>
725
+ <html lang="en">
726
+ <head>
727
+ <meta charset="UTF-8" />
728
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
729
+ <title>My Framework</title>
730
+ </head>
731
+ <body>
732
+ <div id="root"></div>
733
+ <script type="module">
734
+ import '/olova/client';
735
+ </script>
736
+ </body>
737
+ </html>`;
738
+ return {
739
+ name: "olova-virtual-html",
740
+ enforce: "pre",
741
+ // Handle virtual index.html resolution for build
742
+ resolveId(id) {
743
+ if (id === "index.html" || id === "virtual:index.html" || id === "olova.html") {
744
+ return "olova.html";
745
+ }
746
+ },
747
+ load(id) {
748
+ if (id === "olova.html" || id === "virtual:index.html") {
749
+ return htmlTemplate;
750
+ }
751
+ },
752
+ // Serve HTML for all routes in dev mode with SSR (like Next.js)
753
+ configureServer(server) {
754
+ const isStaticRoute = (routePath) => {
755
+ const srcDir = import_path3.default.resolve("src");
756
+ let filePath = routePath === "/" ? "index" : routePath.slice(1);
757
+ const staticPaths = [
758
+ import_path3.default.join(srcDir, filePath, "index.html"),
759
+ import_path3.default.join(srcDir, filePath, "index.md"),
760
+ import_path3.default.join(srcDir, filePath + ".html"),
761
+ import_path3.default.join(srcDir, filePath + ".md")
762
+ ];
763
+ for (const p of staticPaths) {
764
+ if (import_fs3.default.existsSync(p)) {
765
+ return true;
766
+ }
767
+ }
768
+ const possiblePaths = [
769
+ import_path3.default.join(srcDir, filePath, "index.tsx"),
770
+ import_path3.default.join(srcDir, filePath, "index.jsx"),
771
+ import_path3.default.join(srcDir, filePath + ".tsx"),
772
+ import_path3.default.join(srcDir, filePath + ".jsx"),
773
+ import_path3.default.join(srcDir, "App.tsx"),
774
+ import_path3.default.join(srcDir, "App.jsx")
775
+ ];
776
+ for (const p of possiblePaths) {
777
+ if (import_fs3.default.existsSync(p)) {
778
+ const content = import_fs3.default.readFileSync(p, "utf-8");
779
+ const firstLine = content.trim().split("\n")[0].trim();
780
+ return firstLine === '"static"' || firstLine === "'static'";
781
+ }
782
+ }
783
+ return false;
784
+ };
785
+ return () => {
786
+ server.middlewares.use(async (req, res, next) => {
787
+ const url = req.url || "/";
788
+ const accept = req.headers.accept || "";
789
+ if (!accept.includes("text/html")) {
790
+ return next();
791
+ }
792
+ if (url.startsWith("/@") || url.startsWith("/__") || url.startsWith("/node_modules")) {
793
+ return next();
794
+ }
795
+ if (/\.\w+$/.test(url) && !url.endsWith(".html")) {
796
+ return next();
797
+ }
798
+ const routePath = url.split("?")[0];
799
+ const shouldSSR = isStaticRoute(routePath);
800
+ try {
801
+ if (shouldSSR) {
802
+ const { render } = await server.ssrLoadModule("olova/server");
803
+ const { html: ssrHtml, hydrationData } = await render(routePath);
804
+ let fullHtml = ssrHtml;
805
+ const hydrationScript = `<script>window.__OLOVA_DATA__ = ${JSON.stringify(hydrationData)};</script>`;
806
+ fullHtml = fullHtml.replace("</body>", `${hydrationScript}
807
+ </body>`);
808
+ const clientScript = `<script type="module" src="/olova/client"></script>`;
809
+ fullHtml = fullHtml.replace("</body>", `${clientScript}
810
+ </body>`);
811
+ const devBuildId = "dev-" + Date.now().toString(36);
812
+ const flightScripts = generateOlovaHydration({
813
+ route: routePath,
814
+ metadata: hydrationData.metadata,
815
+ params: hydrationData.params,
816
+ chunks: []
817
+ }, devBuildId);
818
+ fullHtml = fullHtml.replace("</body>", `${flightScripts}</body>`);
819
+ const jsonLdScript = generateJsonLd({ route: routePath, metadata: hydrationData.metadata });
820
+ fullHtml = fullHtml.replace("</head>", `${jsonLdScript}</head>`);
821
+ const html = await server.transformIndexHtml(url, fullHtml);
822
+ res.setHeader("Content-Type", "text/html");
823
+ res.statusCode = 200;
824
+ res.end(html);
825
+ } else {
826
+ const { renderShellWithMetadata } = await server.ssrLoadModule("olova/server");
827
+ const { loadRoute: clientLoadRoute } = await server.ssrLoadModule("/route.tsx");
828
+ let pageMetadata = {};
829
+ try {
830
+ const result = await clientLoadRoute(routePath);
831
+ if (result && result.metadata) {
832
+ pageMetadata = result.metadata;
833
+ }
834
+ } catch (e) {
835
+ }
836
+ let fullHtml = renderShellWithMetadata(pageMetadata);
837
+ const clientScript = `<script type="module" src="/olova/client"></script>`;
838
+ fullHtml = fullHtml.replace("</body>", `${clientScript}
839
+ </body>`);
840
+ const devBuildId = "dev-" + Date.now().toString(36);
841
+ const flightScripts = generateOlovaHydration({
842
+ route: routePath,
843
+ metadata: pageMetadata,
844
+ chunks: []
845
+ }, devBuildId);
846
+ fullHtml = fullHtml.replace("</body>", `${flightScripts}</body>`);
847
+ const jsonLdScript = generateJsonLd({ route: routePath, metadata: pageMetadata });
848
+ fullHtml = fullHtml.replace("</head>", `${jsonLdScript}</head>`);
849
+ const html = await server.transformIndexHtml(url, fullHtml);
850
+ res.setHeader("Content-Type", "text/html");
851
+ res.statusCode = 200;
852
+ res.end(html);
853
+ }
854
+ } catch (e) {
855
+ console.error("[Olova] Error:", e);
856
+ try {
857
+ const html = await server.transformIndexHtml(url, htmlTemplate);
858
+ res.setHeader("Content-Type", "text/html");
859
+ res.statusCode = 200;
860
+ res.end(html);
861
+ } catch (e2) {
862
+ next(e2);
863
+ }
864
+ }
865
+ });
866
+ };
867
+ }
868
+ };
869
+ }
870
+
871
+ // router/ssg.ts
872
+ var import_vite2 = require("vite");
873
+ var import_fs4 = __toESM(require("fs"));
874
+ var import_path4 = __toESM(require("path"));
875
+ var import_url = require("url");
876
+
877
+ // router/utils.ts
878
+ function minifyHtml(html) {
879
+ return html.replace(/<!--(?!\[if).*?-->/gs, "").replace(/\s+/g, " ").replace(/> </g, "><").replace(/\s*(<[^>]+>)\s*/g, "$1").trim();
880
+ }
881
+ function generateBuildId() {
882
+ return Math.random().toString(36).substring(2, 15) + Date.now().toString(36);
883
+ }
884
+ function generateResourceHints() {
885
+ return `
886
+ <link rel="dns-prefetch" href="//fonts.googleapis.com">
887
+ <link rel="dns-prefetch" href="//fonts.gstatic.com">
888
+ <link rel="dns-prefetch" href="//cdn.jsdelivr.net">
889
+ <link rel="preconnect" href="https://fonts.googleapis.com" crossorigin>
890
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
891
+ `.trim().replace(/\n\s+/g, "");
892
+ }
893
+ function generateCriticalCSS() {
894
+ return `<style id="olova-critical">
895
+ *{box-sizing:border-box;margin:0;padding:0}
896
+ html{-webkit-text-size-adjust:100%;line-height:1.5}
897
+ body{font-family:system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif}
898
+ img,video{max-width:100%;height:auto}
899
+ [hidden]{display:none!important}
900
+ </style>`.replace(/\n\s*/g, "");
901
+ }
902
+ function generateServiceWorkerScript() {
903
+ return `<script>if('serviceWorker'in navigator){window.addEventListener('load',()=>{navigator.serviceWorker.register('/sw.js').catch(()=>{})})}</script>`;
904
+ }
905
+ function generateServiceWorkerContent(buildId, assets) {
906
+ return `// Olova Service Worker v${buildId}
907
+ const CACHE_NAME = 'olova-cache-${buildId}';
908
+ const ASSETS = ${JSON.stringify(assets)};
909
+
910
+ self.addEventListener('install', (e) => {
911
+ e.waitUntil(caches.open(CACHE_NAME).then(cache => cache.addAll(ASSETS)));
912
+ self.skipWaiting();
913
+ });
914
+
915
+ self.addEventListener('activate', (e) => {
916
+ e.waitUntil(caches.keys().then(keys => Promise.all(
917
+ keys.filter(k => k !== CACHE_NAME).map(k => caches.delete(k))
918
+ )));
919
+ });
920
+
921
+ self.addEventListener('fetch', (e) => {
922
+ e.respondWith(caches.match(e.request).then(r => r || fetch(e.request)));
923
+ });
924
+ `;
925
+ }
926
+ function generatePerformanceMeta() {
927
+ return `<meta http-equiv="x-dns-prefetch-control" content="on"><meta name="color-scheme" content="light dark">`;
928
+ }
929
+
930
+ // router/ssg.ts
931
+ var colors2 = {
932
+ reset: "\x1B[0m",
933
+ bold: "\x1B[1m",
934
+ dim: "\x1B[2m",
935
+ green: "\x1B[32m",
936
+ cyan: "\x1B[36m",
937
+ yellow: "\x1B[33m",
938
+ magenta: "\x1B[35m",
939
+ blue: "\x1B[34m",
940
+ red: "\x1B[31m",
941
+ white: "\x1B[37m",
942
+ gray: "\x1B[90m",
943
+ bgGreen: "\x1B[42m",
944
+ bgCyan: "\x1B[46m",
945
+ bgMagenta: "\x1B[45m"
946
+ };
947
+ var symbols = {
948
+ success: "\u2713",
949
+ arrow: "\u2192",
950
+ bullet: "\u25CB",
951
+ filled: "\u25CF",
952
+ lambda: "\u03BB",
953
+ static: "\u25CB",
954
+ ssr: "\u25CF",
955
+ info: "\u2139"
956
+ };
957
+ function formatTime(ms) {
958
+ if (ms < 1) return "<1ms";
959
+ if (ms < 10) return `${ms.toFixed(1)}ms`;
960
+ if (ms < 1e3) return `${Math.round(ms)}ms`;
961
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(2)}s`;
962
+ const mins = Math.floor(ms / 6e4);
963
+ const secs = Math.round(ms % 6e4 / 1e3);
964
+ return `${mins}m ${secs}s`;
965
+ }
966
+ function formatBytes(bytes) {
967
+ if (bytes < 1024) return `${bytes} B`;
968
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} kB`;
969
+ return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
970
+ }
971
+ var logger = {
972
+ header: (text) => {
973
+ console.log(`
974
+ ${colors2.bold}${colors2.white}${text}${colors2.reset}`);
975
+ },
976
+ info: (text) => {
977
+ console.log(`${colors2.cyan}${symbols.info}${colors2.reset} ${text}`);
978
+ },
979
+ success: (text) => {
980
+ console.log(`${colors2.green}${symbols.success}${colors2.reset} ${text}`);
981
+ },
982
+ route: (route, type, size, time) => {
983
+ const symbol = type === "static" ? symbols.static : symbols.ssr;
984
+ const color = type === "static" ? colors2.white : colors2.magenta;
985
+ const sizeStr = size ? ` ${colors2.dim}${formatBytes(size)}${colors2.reset}` : "";
986
+ const timeStr = time ? ` ${colors2.gray}(${formatTime(time)})${colors2.reset}` : "";
987
+ console.log(`${color}${symbol}${colors2.reset} ${route}${sizeStr}${timeStr}`);
988
+ },
989
+ step: (step, time) => {
990
+ const timeStr = time ? ` ${colors2.gray}${formatTime(time)}${colors2.reset}` : "";
991
+ console.log(` ${colors2.dim}${symbols.arrow}${colors2.reset} ${step}${timeStr}`);
992
+ },
993
+ buildComplete: (totalRoutes, staticRoutes, totalTime) => {
994
+ console.log("");
995
+ console.log(`${colors2.bold}${colors2.green}${symbols.success} Build completed!${colors2.reset}`);
996
+ console.log("");
997
+ console.log(` ${colors2.dim}Routes:${colors2.reset} ${totalRoutes} total`);
998
+ console.log(` ${colors2.dim}Static:${colors2.reset} ${staticRoutes} prerendered`);
999
+ console.log(` ${colors2.dim}Time:${colors2.reset} ${formatTime(totalTime)}`);
1000
+ console.log("");
1001
+ },
1002
+ legend: () => {
1003
+ console.log(`${colors2.dim}${symbols.static} Static${colors2.reset} ${colors2.dim}${symbols.ssr} SSR${colors2.reset} ${colors2.dim}\u03BB ISR${colors2.reset}`);
1004
+ },
1005
+ banner: (buildId) => {
1006
+ console.log("");
1007
+ console.log(`${colors2.bold}${colors2.cyan} \u25B2 Olova${colors2.reset} ${colors2.dim}Static Export${colors2.reset}`);
1008
+ console.log(`${colors2.dim} Build ID: ${buildId}${colors2.reset}`);
1009
+ console.log("");
1010
+ },
1011
+ error: (text) => {
1012
+ console.log(`${colors2.red}\u2717${colors2.reset} ${colors2.red}${text}${colors2.reset}`);
1013
+ },
1014
+ warn: (text) => {
1015
+ console.log(`${colors2.yellow}\u26A0${colors2.reset} ${colors2.yellow}${text}${colors2.reset}`);
1016
+ }
1017
+ };
1018
+ function ssgPlugin() {
1019
+ return {
1020
+ name: "olova-ssg",
1021
+ apply: "build",
1022
+ config() {
1023
+ if (process.env.IS_SSG_BUILD) {
1024
+ return {};
1025
+ }
1026
+ return {
1027
+ build: {
1028
+ manifest: true,
1029
+ rollupOptions: {
1030
+ input: "index.html"
1031
+ }
1032
+ }
1033
+ };
1034
+ },
1035
+ async closeBundle() {
1036
+ if (process.env.IS_SSG_BUILD) return;
1037
+ process.env.IS_SSG_BUILD = "true";
1038
+ const totalStartTime = performance.now();
1039
+ const buildId = generateBuildId();
1040
+ logger.banner(buildId);
1041
+ logger.header("Generating static pages...");
1042
+ const serverBuildStart = performance.now();
1043
+ try {
1044
+ await (0, import_vite2.build)({
1045
+ configFile: "./vite.config.ts",
1046
+ build: {
1047
+ ssr: true,
1048
+ outDir: ".olova/server",
1049
+ rollupOptions: {
1050
+ input: "olova/server"
1051
+ },
1052
+ emptyOutDir: true,
1053
+ reportCompressedSize: false
1054
+ },
1055
+ logLevel: "silent"
1056
+ });
1057
+ logger.step("Server bundle compiled", performance.now() - serverBuildStart);
1058
+ } catch (e) {
1059
+ logger.error("SSG Build Failed");
1060
+ console.error(e);
1061
+ process.env.IS_SSG_BUILD = "";
1062
+ return;
1063
+ } finally {
1064
+ process.env.IS_SSG_BUILD = "";
1065
+ }
1066
+ const scanStart = performance.now();
1067
+ const allRoutes = [];
1068
+ const scan = (dir, base = "") => {
1069
+ if (!import_fs4.default.existsSync(dir)) return;
1070
+ const entries = import_fs4.default.readdirSync(dir, { withFileTypes: true });
1071
+ for (const entry of entries) {
1072
+ const fullPath = import_path4.default.join(dir, entry.name);
1073
+ if (entry.isDirectory()) {
1074
+ const isRouteGroup = /^\(.+\)$/.test(entry.name);
1075
+ if (isRouteGroup) {
1076
+ scan(fullPath, base);
1077
+ } else {
1078
+ scan(fullPath, `${base}/${entry.name}`);
1079
+ }
1080
+ } else {
1081
+ const ext = import_path4.default.extname(entry.name);
1082
+ const supportedExts = [".tsx", ".jsx", ".html", ".md"];
1083
+ if (supportedExts.includes(ext)) {
1084
+ const nameNoExt = entry.name.replace(/\.(tsx|jsx|html|md)$/, "");
1085
+ let route = base;
1086
+ if (nameNoExt !== "index" && nameNoExt !== "App") {
1087
+ route = `${base}/${nameNoExt}`;
1088
+ }
1089
+ if (route === "") route = "/";
1090
+ if (route.includes("$")) continue;
1091
+ if (ext === ".html" || ext === ".md") {
1092
+ allRoutes.push({ route, isStatic: true, filePath: fullPath });
1093
+ } else {
1094
+ const fileContent = import_fs4.default.readFileSync(fullPath, "utf-8");
1095
+ const firstLine = fileContent.trim().split("\n")[0].trim();
1096
+ const isStatic = firstLine === '"static"' || firstLine === "'static'";
1097
+ allRoutes.push({ route, isStatic, filePath: fullPath });
1098
+ }
1099
+ }
1100
+ }
1101
+ }
1102
+ };
1103
+ scan(import_path4.default.resolve("src"));
1104
+ logger.step(`Found ${allRoutes.length} routes`, performance.now() - scanStart);
1105
+ const serverDir = import_path4.default.resolve(".olova/server");
1106
+ let serverFile = import_fs4.default.readdirSync(serverDir).find(
1107
+ (f) => (f === "server.js" || f.includes("server-entry")) && f.endsWith(".js")
1108
+ );
1109
+ if (!serverFile) {
1110
+ logger.error("Could not find server build artifact");
1111
+ return;
1112
+ }
1113
+ const serverEntryPath = import_path4.default.join(serverDir, serverFile);
1114
+ const { render, renderShell, renderShellWithMetadata, loadRoute } = await import((0, import_url.pathToFileURL)(serverEntryPath).href);
1115
+ const assetsStart = performance.now();
1116
+ const distDir = import_path4.default.resolve(".olova/dist");
1117
+ const clientHtmlPath = import_path4.default.join(distDir, "olova.html");
1118
+ let shellHtml = renderShell();
1119
+ if (!shellHtml.startsWith("<!DOCTYPE html>")) {
1120
+ shellHtml = `<!DOCTYPE html>
1121
+ ${shellHtml}`;
1122
+ }
1123
+ let template = "";
1124
+ if (import_fs4.default.existsSync(clientHtmlPath)) {
1125
+ template = import_fs4.default.readFileSync(clientHtmlPath, "utf-8");
1126
+ }
1127
+ const scripts = template.match(/<script[\s\S]*?>[\s\S]*?<\/script>/gi) || [];
1128
+ const links = template.match(/<link[\s\S]*?>/gi) || [];
1129
+ const chunksDir = import_path4.default.join(distDir, "pro_olova_static", "chunks");
1130
+ let preloadLinks = [];
1131
+ if (import_fs4.default.existsSync(chunksDir)) {
1132
+ const chunks = import_fs4.default.readdirSync(chunksDir).filter((f) => f.endsWith(".js"));
1133
+ preloadLinks = chunks.map(
1134
+ (chunk) => `<link rel="modulepreload" crossorigin href="/pro_olova_static/chunks/${chunk}" />`
1135
+ );
1136
+ }
1137
+ const mainScriptMatch = template.match(/src="(\/pro_olova_static\/olova-[^"]+\.js)"/);
1138
+ if (mainScriptMatch) {
1139
+ preloadLinks.unshift(`<link rel="modulepreload" crossorigin href="${mainScriptMatch[1]}" />`);
1140
+ }
1141
+ const chunkList = [];
1142
+ if (import_fs4.default.existsSync(chunksDir)) {
1143
+ const chunks = import_fs4.default.readdirSync(chunksDir).filter((f) => f.endsWith(".js"));
1144
+ chunks.forEach((chunk) => chunkList.push(`/pro_olova_static/chunks/${chunk}`));
1145
+ }
1146
+ const resourceHints = generateResourceHints();
1147
+ const criticalCSS = generateCriticalCSS();
1148
+ const performanceMeta = generatePerformanceMeta();
1149
+ const swScript = generateServiceWorkerScript();
1150
+ const performanceHead = [performanceMeta, resourceHints, criticalCSS].join("");
1151
+ const allAssetsForSW = [
1152
+ "/",
1153
+ "/index.html",
1154
+ ...chunkList,
1155
+ ...links.map((l) => l.match(/href="([^"]+)"/)?.[1]).filter(Boolean)
1156
+ ];
1157
+ const swContent = generateServiceWorkerContent(buildId, allAssetsForSW);
1158
+ import_fs4.default.writeFileSync(import_path4.default.join(distDir, "sw.js"), swContent);
1159
+ const assets = [performanceHead, ...links, ...preloadLinks, ...scripts, swScript].join("\n");
1160
+ logger.step(`Assets optimized (${preloadLinks.length} preloads, SW ready)`, performance.now() - assetsStart);
1161
+ console.log("");
1162
+ logger.header("Route");
1163
+ logger.legend();
1164
+ console.log("");
1165
+ let staticCount = 0;
1166
+ const routeResults = [];
1167
+ for (const { route, isStatic } of allRoutes) {
1168
+ const routeStart = performance.now();
1169
+ let finalHtml = "";
1170
+ if (isStatic) {
1171
+ staticCount++;
1172
+ const { html, hydrationData } = await render(route);
1173
+ finalHtml = html;
1174
+ if (Object.keys(hydrationData.params || {}).length > 0) {
1175
+ const scriptTag = `<script>window.__OLOVA_DATA__ = ${JSON.stringify(hydrationData)};</script>`;
1176
+ if (finalHtml.includes("</body>")) {
1177
+ finalHtml = finalHtml.replace("</body>", `${scriptTag}
1178
+ </body>`);
1179
+ } else {
1180
+ finalHtml += `
1181
+ ${scriptTag}`;
1182
+ }
1183
+ }
1184
+ const jsonLdScript = generateJsonLd({ route, metadata: hydrationData.metadata });
1185
+ if (finalHtml.includes("</head>")) {
1186
+ finalHtml = finalHtml.replace("</head>", `${jsonLdScript}${assets}
1187
+ </head>`);
1188
+ } else {
1189
+ finalHtml = finalHtml.replace("</body>", `${assets}
1190
+ </body>`);
1191
+ }
1192
+ if (!finalHtml.startsWith("<!DOCTYPE html>")) {
1193
+ finalHtml = `<!DOCTYPE html>
1194
+ ${finalHtml}`;
1195
+ }
1196
+ const flightScripts = generateOlovaHydration({
1197
+ route,
1198
+ metadata: hydrationData.metadata,
1199
+ params: hydrationData.params,
1200
+ chunks: chunkList,
1201
+ isStatic: true
1202
+ }, buildId);
1203
+ if (finalHtml.includes("</body>")) {
1204
+ finalHtml = finalHtml.replace("</body>", `${flightScripts}</body>`);
1205
+ }
1206
+ finalHtml = minifyHtml(finalHtml);
1207
+ } else {
1208
+ let pageMetadata = {};
1209
+ try {
1210
+ const routeResult = await loadRoute(route);
1211
+ if (routeResult && routeResult.metadata) {
1212
+ pageMetadata = routeResult.metadata;
1213
+ }
1214
+ } catch (e) {
1215
+ }
1216
+ finalHtml = renderShellWithMetadata(pageMetadata);
1217
+ if (!finalHtml.startsWith("<!DOCTYPE html>")) {
1218
+ finalHtml = `<!DOCTYPE html>
1219
+ ${finalHtml}`;
1220
+ }
1221
+ const jsonLdScript = generateJsonLd({ route, metadata: pageMetadata });
1222
+ if (finalHtml.includes("</head>")) {
1223
+ finalHtml = finalHtml.replace("</head>", `${jsonLdScript}${assets}
1224
+ </head>`);
1225
+ } else {
1226
+ finalHtml = finalHtml.replace("</body>", `${assets}
1227
+ </body>`);
1228
+ }
1229
+ const flightScripts = generateOlovaHydration({
1230
+ route,
1231
+ metadata: pageMetadata,
1232
+ chunks: chunkList,
1233
+ isStatic: false
1234
+ }, buildId);
1235
+ if (finalHtml.includes("</body>")) {
1236
+ finalHtml = finalHtml.replace("</body>", `${flightScripts}</body>`);
1237
+ }
1238
+ finalHtml = minifyHtml(finalHtml);
1239
+ }
1240
+ const outPath = import_path4.default.join(distDir, route === "/" ? "index.html" : `${route}/index.html`);
1241
+ import_fs4.default.mkdirSync(import_path4.default.dirname(outPath), { recursive: true });
1242
+ import_fs4.default.writeFileSync(outPath, finalHtml);
1243
+ const routeTime = performance.now() - routeStart;
1244
+ const routeSize = Buffer.byteLength(finalHtml, "utf8");
1245
+ routeResults.push({ route, type: isStatic ? "static" : "ssr", size: routeSize, time: routeTime });
1246
+ logger.route(route, isStatic ? "static" : "ssr", routeSize, routeTime);
1247
+ }
1248
+ const fallbackPath = import_path4.default.join(distDir, "404.html");
1249
+ if (import_fs4.default.existsSync(clientHtmlPath)) {
1250
+ import_fs4.default.copyFileSync(clientHtmlPath, fallbackPath);
1251
+ }
1252
+ const totalTime = performance.now() - totalStartTime;
1253
+ logger.buildComplete(allRoutes.length, staticCount, totalTime);
1254
+ console.log(`${colors2.dim} Output:${colors2.reset} .olova/dist`);
1255
+ console.log("");
1256
+ }
1257
+ };
1258
+ }
1259
+
1260
+ // router/clean-url.ts
1261
+ var import_fs5 = __toESM(require("fs"));
1262
+ var import_path5 = __toESM(require("path"));
1263
+ function cleanUrlPlugin() {
1264
+ return {
1265
+ name: "olova-clean-url",
1266
+ configurePreviewServer(server) {
1267
+ server.middlewares.use((req, res, next) => {
1268
+ const urlPath = (req.url || "/").split("?")[0];
1269
+ const distDir = import_path5.default.resolve(".olova/dist");
1270
+ if (urlPath === "/") {
1271
+ const indexPath2 = import_path5.default.join(distDir, "index.html");
1272
+ if (import_fs5.default.existsSync(indexPath2)) {
1273
+ const content = import_fs5.default.readFileSync(indexPath2, "utf-8");
1274
+ res.setHeader("Content-Type", "text/html");
1275
+ res.end(content);
1276
+ return;
1277
+ }
1278
+ }
1279
+ if (urlPath.includes(".")) return next();
1280
+ const indexPath = import_path5.default.join(distDir, urlPath, "index.html");
1281
+ if (import_fs5.default.existsSync(indexPath)) {
1282
+ const content = import_fs5.default.readFileSync(indexPath, "utf-8");
1283
+ res.setHeader("Content-Type", "text/html");
1284
+ res.end(content);
1285
+ return;
1286
+ }
1287
+ next();
1288
+ });
1289
+ }
1290
+ };
1291
+ }
1292
+
1293
+ // router/auto-generate.ts
1294
+ var import_fs6 = __toESM(require("fs"));
1295
+ var import_path6 = __toESM(require("path"));
1296
+ function toPascalCase(str) {
1297
+ const cleanStr = str.replace(/^\$/, "");
1298
+ return cleanStr.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
1299
+ }
1300
+ function generateBoilerplate(folderName, isDynamic) {
1301
+ const componentName = toPascalCase(folderName);
1302
+ if (isDynamic) {
1303
+ const paramName = folderName.replace(/^\$/, "");
1304
+ return `import { useParams } from '../../route';
1305
+
1306
+ export default function ${componentName}() {
1307
+ const { ${paramName} } = useParams();
1308
+ return (
1309
+ <div>
1310
+ <h1>${componentName} Page</h1>
1311
+ <p>{${paramName}}</p>
1312
+ </div>
1313
+ );
1314
+ }
1315
+ `;
1316
+ }
1317
+ return `export default function ${componentName}() {
1318
+ return (
1319
+ <div>
1320
+ <h1>${componentName} Page</h1>
1321
+ <p>Welcome to ${componentName}</p>
1322
+ </div>
1323
+ );
1324
+ }
1325
+ `;
1326
+ }
1327
+ function autoGeneratePlugin() {
1328
+ let srcDir;
1329
+ return {
1330
+ name: "olova-auto-generate",
1331
+ configResolved(config) {
1332
+ srcDir = import_path6.default.resolve(config.root, "src");
1333
+ },
1334
+ configureServer(server) {
1335
+ console.log(
1336
+ "\x1B[36m[olova] Auto-generate plugin active - watching for new route files\x1B[0m"
1337
+ );
1338
+ server.watcher.on("add", (filePath) => {
1339
+ const normalizedPath = import_path6.default.normalize(filePath);
1340
+ const normalizedSrcDir = import_path6.default.normalize(srcDir);
1341
+ const ext = import_path6.default.extname(normalizedPath);
1342
+ if (ext !== ".tsx" && ext !== ".jsx") {
1343
+ return;
1344
+ }
1345
+ if (!normalizedPath.startsWith(normalizedSrcDir)) {
1346
+ return;
1347
+ }
1348
+ const fileName = import_path6.default.basename(normalizedPath, ext);
1349
+ if (fileName !== "index") {
1350
+ return;
1351
+ }
1352
+ const folderPath = import_path6.default.dirname(normalizedPath);
1353
+ const folderName = import_path6.default.basename(folderPath);
1354
+ if (folderName === "src" || folderName.startsWith("(")) {
1355
+ return;
1356
+ }
1357
+ try {
1358
+ import_fs6.default.statSync(normalizedPath);
1359
+ const content = import_fs6.default.readFileSync(normalizedPath, "utf-8");
1360
+ if (content.trim().length > 10) {
1361
+ return;
1362
+ }
1363
+ } catch (err) {
1364
+ return;
1365
+ }
1366
+ const isDynamic = folderName.startsWith("$");
1367
+ const boilerplate = generateBoilerplate(folderName, isDynamic);
1368
+ try {
1369
+ import_fs6.default.writeFileSync(normalizedPath, boilerplate, "utf-8");
1370
+ console.log(
1371
+ `\x1B[32m\u2713 [olova] Auto-generated: ${folderName}/index${ext}\x1B[0m`
1372
+ );
1373
+ } catch (err) {
1374
+ console.error(
1375
+ `\x1B[31m\u2717 [olova] Failed to write boilerplate:\x1B[0m`,
1376
+ err
1377
+ );
1378
+ }
1379
+ });
1380
+ }
1381
+ };
1382
+ }
1383
+
1384
+ // router/error-overlay.ts
1385
+ var import_path7 = __toESM(require("path"));
1386
+ function proactiveErrorPlugin() {
1387
+ return {
1388
+ name: "olova-proactive-error",
1389
+ enforce: "post",
1390
+ configureServer(devServer) {
1391
+ const srcDir = import_path7.default.resolve(process.cwd(), "src");
1392
+ const validateFile = async (filePath) => {
1393
+ if (!filePath.startsWith(srcDir)) return;
1394
+ if (!/\.(tsx?|jsx?)$/.test(filePath)) return;
1395
+ const relativePath = import_path7.default.relative(srcDir, filePath);
1396
+ console.log("\x1B[36m[olova]\x1B[0m Checking:", relativePath);
1397
+ try {
1398
+ const url = "/" + import_path7.default.relative(process.cwd(), filePath).replace(/\\/g, "/");
1399
+ await devServer.transformRequest(url);
1400
+ console.log("\x1B[32m[olova]\x1B[0m \u2713", relativePath, "OK");
1401
+ } catch (err) {
1402
+ console.log("\x1B[31m[olova]\x1B[0m \u2717", relativePath);
1403
+ devServer.ws.send({
1404
+ type: "error",
1405
+ err: {
1406
+ message: err.message || String(err),
1407
+ stack: err.stack || "",
1408
+ id: err.id || filePath,
1409
+ frame: err.frame || "",
1410
+ plugin: err.plugin || "olova",
1411
+ loc: err.loc || void 0
1412
+ }
1413
+ });
1414
+ }
1415
+ };
1416
+ devServer.watcher.on("change", async (filePath) => {
1417
+ await new Promise((r) => setTimeout(r, 50));
1418
+ await validateFile(filePath);
1419
+ });
1420
+ devServer.watcher.on("add", async (filePath) => {
1421
+ await new Promise((r) => setTimeout(r, 100));
1422
+ await validateFile(filePath);
1423
+ });
1424
+ }
1425
+ };
1426
+ }
1427
+
1428
+ // router/terminal.ts
1429
+ var colors3 = {
1430
+ // Reset
1431
+ reset: "\x1B[0m",
1432
+ // Styles
1433
+ bold: "\x1B[1m",
1434
+ dim: "\x1B[2m",
1435
+ italic: "\x1B[3m",
1436
+ underline: "\x1B[4m",
1437
+ // Foreground colors
1438
+ black: "\x1B[30m",
1439
+ red: "\x1B[31m",
1440
+ green: "\x1B[32m",
1441
+ yellow: "\x1B[33m",
1442
+ blue: "\x1B[34m",
1443
+ magenta: "\x1B[35m",
1444
+ cyan: "\x1B[36m",
1445
+ white: "\x1B[37m",
1446
+ gray: "\x1B[90m",
1447
+ // Background colors
1448
+ bgBlack: "\x1B[40m",
1449
+ bgRed: "\x1B[41m",
1450
+ bgGreen: "\x1B[42m",
1451
+ bgYellow: "\x1B[43m",
1452
+ bgBlue: "\x1B[44m",
1453
+ bgMagenta: "\x1B[45m",
1454
+ bgCyan: "\x1B[46m",
1455
+ bgWhite: "\x1B[47m"
1456
+ };
1457
+ var symbols2 = {
1458
+ success: "\u2713",
1459
+ error: "\u2717",
1460
+ warning: "\u26A0",
1461
+ info: "\u2139",
1462
+ arrow: "\u2192",
1463
+ arrowRight: "\u279C",
1464
+ bullet: "\u25CB",
1465
+ filled: "\u25CF",
1466
+ lambda: "\u03BB",
1467
+ triangle: "\u25B2",
1468
+ square: "\u25A0",
1469
+ diamond: "\u25C6",
1470
+ star: "\u2605",
1471
+ check: "\u2714",
1472
+ cross: "\u2716",
1473
+ pointer: "\u276F"
1474
+ };
1475
+ function formatTime2(ms) {
1476
+ if (ms < 1) return "<1ms";
1477
+ if (ms < 10) return `${ms.toFixed(1)}ms`;
1478
+ if (ms < 1e3) return `${Math.round(ms)}ms`;
1479
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(2)}s`;
1480
+ const mins = Math.floor(ms / 6e4);
1481
+ const secs = Math.round(ms % 6e4 / 1e3);
1482
+ return `${mins}m ${secs}s`;
1483
+ }
1484
+ function formatBytes2(bytes) {
1485
+ if (bytes === 0) return "0 B";
1486
+ const k = 1024;
1487
+ const sizes = ["B", "kB", "MB", "GB"];
1488
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
1489
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;
1490
+ }
1491
+ var logger2 = {
1492
+ // Print banner
1493
+ banner: (name, version = "1.0.0") => {
1494
+ console.log("");
1495
+ console.log(` ${colors3.bold}${colors3.cyan}${symbols2.triangle} ${name}${colors3.reset} ${colors3.dim}v${version}${colors3.reset}`);
1496
+ console.log("");
1497
+ },
1498
+ // Print header
1499
+ header: (text) => {
1500
+ console.log(`
1501
+ ${colors3.bold}${colors3.white}${text}${colors3.reset}`);
1502
+ },
1503
+ // Info message
1504
+ info: (text) => {
1505
+ console.log(`${colors3.cyan}${symbols2.info}${colors3.reset} ${text}`);
1506
+ },
1507
+ // Success message
1508
+ success: (text, time) => {
1509
+ const timeStr = time !== void 0 ? ` ${colors3.dim}${formatTime2(time)}${colors3.reset}` : "";
1510
+ console.log(`${colors3.green}${symbols2.success}${colors3.reset} ${text}${timeStr}`);
1511
+ },
1512
+ // Error message
1513
+ error: (text) => {
1514
+ console.log(`${colors3.red}${symbols2.error}${colors3.reset} ${colors3.red}${text}${colors3.reset}`);
1515
+ },
1516
+ // Warning message
1517
+ warn: (text) => {
1518
+ console.log(`${colors3.yellow}${symbols2.warning}${colors3.reset} ${colors3.yellow}${text}${colors3.reset}`);
1519
+ },
1520
+ // Step in a process
1521
+ step: (text, time) => {
1522
+ const timeStr = time !== void 0 ? ` ${colors3.gray}${formatTime2(time)}${colors3.reset}` : "";
1523
+ console.log(` ${colors3.dim}${symbols2.arrow}${colors3.reset} ${text}${timeStr}`);
1524
+ },
1525
+ // Route log (for SSG)
1526
+ route: (route, type, size, time) => {
1527
+ const symbol = type === "static" ? symbols2.bullet : type === "ssr" ? symbols2.filled : symbols2.lambda;
1528
+ const color = type === "static" ? colors3.white : type === "ssr" ? colors3.magenta : colors3.cyan;
1529
+ const sizeStr = size ? ` ${colors3.dim}${formatBytes2(size)}${colors3.reset}` : "";
1530
+ const timeStr = time ? ` ${colors3.gray}(${formatTime2(time)})${colors3.reset}` : "";
1531
+ console.log(`${color}${symbol}${colors3.reset} ${route}${sizeStr}${timeStr}`);
1532
+ },
1533
+ // Indent text
1534
+ indent: (text, level = 1) => {
1535
+ console.log(`${" ".repeat(level)}${text}`);
1536
+ },
1537
+ // Empty line
1538
+ newline: () => console.log(""),
1539
+ // Dim text
1540
+ dim: (text) => `${colors3.dim}${text}${colors3.reset}`,
1541
+ // Bold text
1542
+ bold: (text) => `${colors3.bold}${text}${colors3.reset}`,
1543
+ // Colored text
1544
+ cyan: (text) => `${colors3.cyan}${text}${colors3.reset}`,
1545
+ green: (text) => `${colors3.green}${text}${colors3.reset}`,
1546
+ yellow: (text) => `${colors3.yellow}${text}${colors3.reset}`,
1547
+ red: (text) => `${colors3.red}${text}${colors3.reset}`,
1548
+ magenta: (text) => `${colors3.magenta}${text}${colors3.reset}`
1549
+ };
1550
+ function createTimer() {
1551
+ const start = performance.now();
1552
+ return {
1553
+ elapsed: () => performance.now() - start,
1554
+ format: () => formatTime2(performance.now() - start)
1555
+ };
1556
+ }
1557
+
1558
+ // router/index.ts
1559
+ function olovaPlugins() {
1560
+ return [
1561
+ configPlugin(),
1562
+ // Must be first - creates .olova folder and sets config
1563
+ routerPlugin(),
1564
+ frameworkPlugin(),
1565
+ virtualHtmlPlugin(),
1566
+ ssgPlugin(),
1567
+ cleanUrlPlugin(),
1568
+ autoGeneratePlugin(),
1569
+ // Auto-generates boilerplate for new route files
1570
+ proactiveErrorPlugin()
1571
+ // Checks all files on save for instant error detection
1572
+ ];
1573
+ }
1574
+ // Annotate the CommonJS export names for ESM import in node:
1575
+ 0 && (module.exports = {
1576
+ autoGeneratePlugin,
1577
+ cleanUrlPlugin,
1578
+ colors,
1579
+ configPlugin,
1580
+ createTimer,
1581
+ formatBytes,
1582
+ formatTime,
1583
+ frameworkPlugin,
1584
+ generateBuildId,
1585
+ generateCriticalCSS,
1586
+ generateJsonLd,
1587
+ generateOlovaHydration,
1588
+ generatePerformanceMeta,
1589
+ generateResourceHints,
1590
+ generateServiceWorkerContent,
1591
+ generateServiceWorkerScript,
1592
+ logger,
1593
+ minifyHtml,
1594
+ olovaPlugins,
1595
+ parseFlightData,
1596
+ proactiveErrorPlugin,
1597
+ routerPlugin,
1598
+ ssgPlugin,
1599
+ symbols,
1600
+ virtualHtmlPlugin
1601
+ });