olova 2.0.55 → 2.0.56
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 +28 -288
- package/dist/chunk-23UAGQ6N.js +2208 -0
- package/dist/chunk-23UAGQ6N.js.map +1 -0
- package/dist/chunk-D7SIC5TC.js +367 -0
- package/dist/chunk-D7SIC5TC.js.map +1 -0
- package/dist/entry-server.cjs +2341 -0
- package/dist/entry-server.cjs.map +1 -0
- package/dist/entry-server.js +114 -0
- package/dist/entry-server.js.map +1 -0
- package/dist/entry-worker.cjs +2354 -0
- package/dist/entry-worker.cjs.map +1 -0
- package/dist/entry-worker.js +126 -0
- package/dist/entry-worker.js.map +1 -0
- package/dist/main.cjs +18 -0
- package/dist/main.cjs.map +1 -0
- package/dist/main.js +16 -0
- package/dist/main.js.map +1 -0
- package/dist/olova.cjs +1684 -0
- package/dist/olova.cjs.map +1 -0
- package/dist/olova.d.cts +72 -0
- package/dist/olova.d.ts +72 -0
- package/dist/olova.js +1325 -0
- package/dist/olova.js.map +1 -0
- package/dist/performance.cjs +386 -0
- package/dist/performance.cjs.map +1 -0
- package/dist/performance.js +3 -0
- package/dist/performance.js.map +1 -0
- package/dist/router.cjs +646 -0
- package/dist/router.cjs.map +1 -0
- package/dist/router.d.cts +113 -0
- package/dist/router.d.ts +113 -0
- package/dist/router.js +632 -0
- package/dist/router.js.map +1 -0
- package/main.tsx +76 -0
- package/olova.ts +619 -0
- package/package.json +42 -61
- package/src/entry-server.tsx +165 -0
- package/src/entry-worker.tsx +201 -0
- package/src/generator/index.ts +409 -0
- package/src/hydration/flight.ts +320 -0
- package/src/hydration/index.ts +12 -0
- package/src/hydration/types.ts +225 -0
- package/src/logger.ts +182 -0
- package/src/main.tsx +24 -0
- package/src/performance.ts +488 -0
- package/src/plugin/index.ts +204 -0
- package/src/router/ErrorBoundary.tsx +145 -0
- package/src/router/Link.tsx +117 -0
- package/src/router/OlovaRouter.tsx +354 -0
- package/src/router/Outlet.tsx +8 -0
- package/src/router/context.ts +117 -0
- package/src/router/index.ts +29 -0
- package/src/router/matching.ts +63 -0
- package/src/router/router.tsx +23 -0
- package/src/router/search-params.ts +29 -0
- package/src/scanner/index.ts +116 -0
- package/src/types/index.ts +191 -0
- package/src/utils/export.ts +85 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/naming.ts +54 -0
- package/src/utils/path.ts +45 -0
- package/tsup.config.ts +35 -0
- package/CHANGELOG.md +0 -31
- package/LICENSE +0 -21
- package/dist/index.cjs +0 -883
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -138
- package/dist/index.d.ts +0 -138
- package/dist/index.js +0 -832
- package/dist/index.js.map +0 -1
- package/dist/plugin.cjs +0 -927
- package/dist/plugin.cjs.map +0 -1
- package/dist/plugin.d.cts +0 -18
- package/dist/plugin.d.ts +0 -18
- package/dist/plugin.js +0 -894
- package/dist/plugin.js.map +0 -1
- package/dist/ssg.cjs +0 -637
- package/dist/ssg.cjs.map +0 -1
- package/dist/ssg.d.cts +0 -191
- package/dist/ssg.d.ts +0 -191
- package/dist/ssg.js +0 -585
- package/dist/ssg.js.map +0 -1
- package/dist/types-BT6YsBGO.d.cts +0 -143
- package/dist/types-BT6YsBGO.d.ts +0 -143
package/dist/ssg.cjs
DELETED
|
@@ -1,637 +0,0 @@
|
|
|
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
|
-
// src/ssg/index.ts
|
|
31
|
-
var ssg_exports = {};
|
|
32
|
-
__export(ssg_exports, {
|
|
33
|
-
cleanupSSREntry: () => cleanupSSREntry,
|
|
34
|
-
crawlRoutes: () => crawlRoutes,
|
|
35
|
-
createSSGConfig: () => createSSGConfig,
|
|
36
|
-
discoverBuildAssets: () => discoverBuildAssets,
|
|
37
|
-
generateHTML: () => generateHTML,
|
|
38
|
-
generateHydrationScript: () => generateHydrationScript,
|
|
39
|
-
generateMetaTags: () => generateMetaTags,
|
|
40
|
-
generateRobotsTxt: () => generateRobotsTxt,
|
|
41
|
-
generateSSREntry: () => generateSSREntry,
|
|
42
|
-
generateSitemap: () => generateSitemap,
|
|
43
|
-
getDynamicPages: () => getDynamicPages,
|
|
44
|
-
getStaticPages: () => getStaticPages,
|
|
45
|
-
injectContentIntoHTML: () => injectContentIntoHTML,
|
|
46
|
-
renderPage: () => renderPage,
|
|
47
|
-
renderPages: () => renderPages,
|
|
48
|
-
runSSG: () => runSSG
|
|
49
|
-
});
|
|
50
|
-
module.exports = __toCommonJS(ssg_exports);
|
|
51
|
-
|
|
52
|
-
// src/ssg/prerender.ts
|
|
53
|
-
var import_fs5 = __toESM(require("fs"), 1);
|
|
54
|
-
var import_path5 = __toESM(require("path"), 1);
|
|
55
|
-
|
|
56
|
-
// src/ssg/crawler.ts
|
|
57
|
-
var import_fs = __toESM(require("fs"), 1);
|
|
58
|
-
var import_path = __toESM(require("path"), 1);
|
|
59
|
-
async function crawlRoutes(config) {
|
|
60
|
-
const appPath = import_path.default.resolve(config.root, config.appDir);
|
|
61
|
-
if (!import_fs.default.existsSync(appPath)) {
|
|
62
|
-
console.warn(`[olova-ssg] App directory not found: ${appPath}`);
|
|
63
|
-
return [];
|
|
64
|
-
}
|
|
65
|
-
const pages = [];
|
|
66
|
-
await scanDirectory(appPath, "/", config, pages);
|
|
67
|
-
return pages;
|
|
68
|
-
}
|
|
69
|
-
async function scanDirectory(dir, routePath, config, pages) {
|
|
70
|
-
const entries = import_fs.default.readdirSync(dir, { withFileTypes: true });
|
|
71
|
-
for (const entry of entries) {
|
|
72
|
-
const fullPath = import_path.default.join(dir, entry.name);
|
|
73
|
-
if (entry.isDirectory()) {
|
|
74
|
-
const segment = entry.name;
|
|
75
|
-
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
76
|
-
await scanDirectory(fullPath, routePath, config, pages);
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
if (segment.startsWith("@")) {
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
if (segment.startsWith("[")) {
|
|
83
|
-
const newRoutePath2 = buildDynamicRoutePath(routePath, segment);
|
|
84
|
-
await scanDirectory(fullPath, newRoutePath2, config, pages);
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
const newRoutePath = routePath === "/" ? `/${segment}` : `${routePath}/${segment}`;
|
|
88
|
-
await scanDirectory(fullPath, newRoutePath, config, pages);
|
|
89
|
-
} else if (entry.isFile()) {
|
|
90
|
-
const isPage = config.extensions.some((ext) => entry.name === `page${ext}`);
|
|
91
|
-
if (isPage) {
|
|
92
|
-
const isDynamicRoute = routePath.includes(":") || routePath.includes("*");
|
|
93
|
-
const { metadata, hasGenerateMetadata } = await extractPageMetadata(fullPath);
|
|
94
|
-
pages.push({
|
|
95
|
-
path: routePath || "/",
|
|
96
|
-
componentPath: fullPath,
|
|
97
|
-
isStatic: !isDynamicRoute,
|
|
98
|
-
params: {},
|
|
99
|
-
metadata,
|
|
100
|
-
hasGenerateMetadata
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
function buildDynamicRoutePath(basePath, segment) {
|
|
107
|
-
let pathSegment;
|
|
108
|
-
if (segment.startsWith("[[...") && segment.endsWith("]]")) {
|
|
109
|
-
const paramName = segment.slice(5, -2);
|
|
110
|
-
pathSegment = `:${paramName}*`;
|
|
111
|
-
} else if (segment.startsWith("[...") && segment.endsWith("]")) {
|
|
112
|
-
const paramName = segment.slice(4, -1);
|
|
113
|
-
pathSegment = `:${paramName}*`;
|
|
114
|
-
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
115
|
-
const paramName = segment.slice(1, -1);
|
|
116
|
-
pathSegment = `:${paramName}`;
|
|
117
|
-
} else {
|
|
118
|
-
pathSegment = segment;
|
|
119
|
-
}
|
|
120
|
-
return basePath === "/" ? `/${pathSegment}` : `${basePath}/${pathSegment}`;
|
|
121
|
-
}
|
|
122
|
-
async function extractPageMetadata(filePath) {
|
|
123
|
-
try {
|
|
124
|
-
const content = import_fs.default.readFileSync(filePath, "utf-8");
|
|
125
|
-
const hasGenerateMetadata = /export\s+(async\s+)?function\s+generateMetadata/.test(content);
|
|
126
|
-
const metadataMatch = content.match(/export\s+const\s+metadata\s*=\s*(\{[\s\S]*?\n\};?)/);
|
|
127
|
-
if (metadataMatch) {
|
|
128
|
-
try {
|
|
129
|
-
const metadataStr = metadataMatch[1];
|
|
130
|
-
const metadata = Function('"use strict"; return (' + metadataStr + ")")();
|
|
131
|
-
return { metadata, hasGenerateMetadata };
|
|
132
|
-
} catch {
|
|
133
|
-
return { metadata: extractMetadataSimple(content), hasGenerateMetadata };
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
return { metadata: null, hasGenerateMetadata };
|
|
137
|
-
} catch (e) {
|
|
138
|
-
console.warn(`[olova-ssg] Failed to extract metadata from ${filePath}`);
|
|
139
|
-
return { metadata: null, hasGenerateMetadata: false };
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
function extractMetadataSimple(content) {
|
|
143
|
-
const metadata = {};
|
|
144
|
-
const titleMatch = content.match(/title:\s*['"`]([^'"`]+)['"`]/);
|
|
145
|
-
if (titleMatch) metadata.title = titleMatch[1];
|
|
146
|
-
const descMatch = content.match(/description:\s*['"`]([^'"`]+)['"`]/);
|
|
147
|
-
if (descMatch) metadata.description = descMatch[1];
|
|
148
|
-
const keywordsMatch = content.match(/keywords:\s*\[([^\]]+)\]/);
|
|
149
|
-
if (keywordsMatch) {
|
|
150
|
-
const keywords = keywordsMatch[1].split(",").map((k) => k.trim().replace(/['"`]/g, "")).filter(Boolean);
|
|
151
|
-
if (keywords.length) metadata.keywords = keywords;
|
|
152
|
-
}
|
|
153
|
-
return Object.keys(metadata).length > 0 ? metadata : null;
|
|
154
|
-
}
|
|
155
|
-
function getStaticPages(pages) {
|
|
156
|
-
return pages.filter((page) => page.isStatic);
|
|
157
|
-
}
|
|
158
|
-
function getDynamicPages(pages) {
|
|
159
|
-
return pages.filter((page) => !page.isStatic);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// src/ssg/generator.ts
|
|
163
|
-
var import_fs2 = __toESM(require("fs"), 1);
|
|
164
|
-
var import_path2 = __toESM(require("path"), 1);
|
|
165
|
-
function generateSSREntry(pages, config) {
|
|
166
|
-
const entryPath = import_path2.default.resolve(config.root, ".olova-ssg-entry.tsx");
|
|
167
|
-
const imports = [];
|
|
168
|
-
const routeMap = [];
|
|
169
|
-
const appDir = import_path2.default.resolve(config.root, config.appDir);
|
|
170
|
-
const layoutPath = findLayoutFile(appDir, config.extensions);
|
|
171
|
-
if (layoutPath) {
|
|
172
|
-
const relativePath = import_path2.default.relative(config.root, layoutPath).replace(/\\/g, "/");
|
|
173
|
-
imports.push(`import Layout from './${relativePath}';`);
|
|
174
|
-
}
|
|
175
|
-
pages.forEach((page, index) => {
|
|
176
|
-
const relativePath = import_path2.default.relative(config.root, page.componentPath).replace(/\\/g, "/");
|
|
177
|
-
imports.push(`import Page${index} from './${relativePath}';`);
|
|
178
|
-
routeMap.push(` '${page.path}': Page${index},`);
|
|
179
|
-
});
|
|
180
|
-
const entryContent = `
|
|
181
|
-
// Auto-generated SSR entry for Olova SSG
|
|
182
|
-
import React from 'react';
|
|
183
|
-
import { renderToString } from 'react-dom/server';
|
|
184
|
-
import { setSSRContext } from 'olova';
|
|
185
|
-
|
|
186
|
-
${imports.join("\n")}
|
|
187
|
-
|
|
188
|
-
const routeComponents: Record<string, React.ComponentType<any>> = {
|
|
189
|
-
${routeMap.join("\n")}
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
export async function render(routePath: string, params: Record<string, string> = {}) {
|
|
193
|
-
const Component = routeComponents[routePath];
|
|
194
|
-
|
|
195
|
-
if (!Component) {
|
|
196
|
-
console.warn('[olova-ssg] No component found for route:', routePath);
|
|
197
|
-
return '';
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
try {
|
|
201
|
-
// Set the SSR context before rendering
|
|
202
|
-
setSSRContext(routePath, params);
|
|
203
|
-
|
|
204
|
-
// Build component hierarchy
|
|
205
|
-
const pageElement = React.createElement(Component, { params, searchParams: {} });
|
|
206
|
-
${layoutPath ? `const element = React.createElement(Layout, { children: pageElement, params });` : `const element = pageElement;`}
|
|
207
|
-
|
|
208
|
-
return renderToString(element);
|
|
209
|
-
} catch (error: any) {
|
|
210
|
-
console.error('[olova-ssg] SSR Error for', routePath, ':', error.message);
|
|
211
|
-
throw error;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
export const routes = ${JSON.stringify(pages.map((p) => ({ path: p.path, isStatic: p.isStatic })), null, 2)};
|
|
216
|
-
`;
|
|
217
|
-
import_fs2.default.writeFileSync(entryPath, entryContent, "utf-8");
|
|
218
|
-
return entryPath;
|
|
219
|
-
}
|
|
220
|
-
function findLayoutFile(dir, extensions) {
|
|
221
|
-
for (const ext of extensions) {
|
|
222
|
-
const layoutPath = import_path2.default.join(dir, `layout${ext}`);
|
|
223
|
-
if (import_fs2.default.existsSync(layoutPath)) {
|
|
224
|
-
return layoutPath;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
return null;
|
|
228
|
-
}
|
|
229
|
-
function cleanupSSREntry(entryPath) {
|
|
230
|
-
try {
|
|
231
|
-
if (import_fs2.default.existsSync(entryPath)) {
|
|
232
|
-
import_fs2.default.unlinkSync(entryPath);
|
|
233
|
-
}
|
|
234
|
-
} catch (e) {
|
|
235
|
-
console.warn("[olova-ssg] Failed to cleanup SSR entry file");
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
function generateHydrationScript(routePath, params) {
|
|
239
|
-
return `
|
|
240
|
-
<script>
|
|
241
|
-
window.__OLOVA_DATA__ = ${JSON.stringify({ route: routePath, params, hydrated: false })};
|
|
242
|
-
</script>
|
|
243
|
-
`;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// src/ssg/renderer.ts
|
|
247
|
-
var import_fs3 = __toESM(require("fs"), 1);
|
|
248
|
-
var import_path3 = __toESM(require("path"), 1);
|
|
249
|
-
var import_url = require("url");
|
|
250
|
-
var import_vite = require("vite");
|
|
251
|
-
async function renderPage(page, config, ssrEntryPath) {
|
|
252
|
-
const tempSSRDir = import_path3.default.join(config.root, ".olova", "ssr");
|
|
253
|
-
if (!import_fs3.default.existsSync(tempSSRDir)) {
|
|
254
|
-
import_fs3.default.mkdirSync(tempSSRDir, { recursive: true });
|
|
255
|
-
}
|
|
256
|
-
const pkgPath = import_path3.default.join(tempSSRDir, "package.json");
|
|
257
|
-
if (!import_fs3.default.existsSync(pkgPath)) {
|
|
258
|
-
import_fs3.default.writeFileSync(pkgPath, JSON.stringify({ type: "module" }));
|
|
259
|
-
}
|
|
260
|
-
const outputFileName = `ssr-entry-${Date.now()}.js`;
|
|
261
|
-
const outputPath = import_path3.default.join(tempSSRDir, outputFileName);
|
|
262
|
-
try {
|
|
263
|
-
await (0, import_vite.build)({
|
|
264
|
-
configFile: false,
|
|
265
|
-
root: config.root,
|
|
266
|
-
logLevel: "silent",
|
|
267
|
-
build: {
|
|
268
|
-
ssr: true,
|
|
269
|
-
write: true,
|
|
270
|
-
outDir: tempSSRDir,
|
|
271
|
-
emptyOutDir: false,
|
|
272
|
-
rollupOptions: {
|
|
273
|
-
input: ssrEntryPath,
|
|
274
|
-
output: {
|
|
275
|
-
format: "esm",
|
|
276
|
-
entryFileNames: outputFileName
|
|
277
|
-
},
|
|
278
|
-
onwarn: (warning, handler) => {
|
|
279
|
-
if (warning.code === "MODULE_LEVEL_DIRECTIVE") return;
|
|
280
|
-
handler(warning);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
},
|
|
284
|
-
plugins: [
|
|
285
|
-
{
|
|
286
|
-
name: "olova-ssr-transforms",
|
|
287
|
-
transform(code) {
|
|
288
|
-
return code.replace(/['"]use client['"];?/g, "").replace(/['"]use static['"];?/g, "");
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
]
|
|
292
|
-
});
|
|
293
|
-
if (import_fs3.default.existsSync(outputPath)) {
|
|
294
|
-
const moduleUrl = (0, import_url.pathToFileURL)(outputPath).href;
|
|
295
|
-
const mod = await import(moduleUrl);
|
|
296
|
-
const content = await mod.render(page.path, page.params);
|
|
297
|
-
return {
|
|
298
|
-
content: content || "",
|
|
299
|
-
metadata: page.metadata
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
throw new Error("Built SSR artifact not found");
|
|
303
|
-
} finally {
|
|
304
|
-
if (import_fs3.default.existsSync(outputPath)) {
|
|
305
|
-
try {
|
|
306
|
-
import_fs3.default.unlinkSync(outputPath);
|
|
307
|
-
} catch {
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
async function renderPages(pages, config, ssrEntryPath, onProgress) {
|
|
313
|
-
const results = /* @__PURE__ */ new Map();
|
|
314
|
-
for (let i = 0; i < pages.length; i++) {
|
|
315
|
-
const page = pages[i];
|
|
316
|
-
if (onProgress) {
|
|
317
|
-
onProgress(page, i, pages.length);
|
|
318
|
-
}
|
|
319
|
-
try {
|
|
320
|
-
const result = await renderPage(page, config, ssrEntryPath);
|
|
321
|
-
results.set(page.path, result);
|
|
322
|
-
} catch (error) {
|
|
323
|
-
console.error(`[olova-ssg] Failed to render ${page.path}:`, error.message);
|
|
324
|
-
results.set(page.path, { content: "", metadata: page.metadata });
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
return results;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// src/ssg/html.ts
|
|
331
|
-
var import_fs4 = __toESM(require("fs"), 1);
|
|
332
|
-
var import_path4 = __toESM(require("path"), 1);
|
|
333
|
-
function generateHTML(options) {
|
|
334
|
-
const {
|
|
335
|
-
content,
|
|
336
|
-
jsEntry,
|
|
337
|
-
cssEntries,
|
|
338
|
-
metadata,
|
|
339
|
-
routePath,
|
|
340
|
-
params
|
|
341
|
-
} = options;
|
|
342
|
-
const metaTags = metadata ? generateMetaTags(metadata) : "";
|
|
343
|
-
const cssLinks = cssEntries.map((css) => `<link rel="stylesheet" href="${css}">`).join("\n ");
|
|
344
|
-
return `<!doctype html>
|
|
345
|
-
<html lang="en">
|
|
346
|
-
<head>
|
|
347
|
-
<meta charset="UTF-8" />
|
|
348
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
349
|
-
${metaTags}
|
|
350
|
-
${cssLinks}
|
|
351
|
-
</head>
|
|
352
|
-
<body>
|
|
353
|
-
<div id="root">${content}</div>
|
|
354
|
-
<script>
|
|
355
|
-
window.__OLOVA_DATA__ = ${JSON.stringify({ route: routePath, params, hydrated: false })};
|
|
356
|
-
</script>
|
|
357
|
-
${jsEntry ? `<script type="module" src="${jsEntry}"></script>` : ""}
|
|
358
|
-
</body>
|
|
359
|
-
</html>`;
|
|
360
|
-
}
|
|
361
|
-
function generateMetaTags(metadata) {
|
|
362
|
-
const tags = [];
|
|
363
|
-
if (metadata.title) {
|
|
364
|
-
tags.push(`<title>${escapeHtml(metadata.title)}</title>`);
|
|
365
|
-
}
|
|
366
|
-
if (metadata.description) {
|
|
367
|
-
tags.push(`<meta name="description" content="${escapeHtml(metadata.description)}" />`);
|
|
368
|
-
}
|
|
369
|
-
if (metadata.keywords?.length) {
|
|
370
|
-
tags.push(`<meta name="keywords" content="${escapeHtml(metadata.keywords.join(", "))}" />`);
|
|
371
|
-
}
|
|
372
|
-
if (metadata.robots) {
|
|
373
|
-
tags.push(`<meta name="robots" content="${escapeHtml(metadata.robots)}" />`);
|
|
374
|
-
}
|
|
375
|
-
if (metadata.canonical) {
|
|
376
|
-
tags.push(`<link rel="canonical" href="${escapeHtml(metadata.canonical)}" />`);
|
|
377
|
-
}
|
|
378
|
-
if (metadata.openGraph) {
|
|
379
|
-
const og = metadata.openGraph;
|
|
380
|
-
if (og.title) {
|
|
381
|
-
tags.push(`<meta property="og:title" content="${escapeHtml(og.title)}" />`);
|
|
382
|
-
}
|
|
383
|
-
if (og.description) {
|
|
384
|
-
tags.push(`<meta property="og:description" content="${escapeHtml(og.description)}" />`);
|
|
385
|
-
}
|
|
386
|
-
if (og.url) {
|
|
387
|
-
tags.push(`<meta property="og:url" content="${escapeHtml(og.url)}" />`);
|
|
388
|
-
}
|
|
389
|
-
if (og.siteName) {
|
|
390
|
-
tags.push(`<meta property="og:site_name" content="${escapeHtml(og.siteName)}" />`);
|
|
391
|
-
}
|
|
392
|
-
if (og.type) {
|
|
393
|
-
tags.push(`<meta property="og:type" content="${og.type}" />`);
|
|
394
|
-
}
|
|
395
|
-
if (og.images?.length) {
|
|
396
|
-
for (const image of og.images) {
|
|
397
|
-
tags.push(`<meta property="og:image" content="${escapeHtml(image.url)}" />`);
|
|
398
|
-
if (image.width) {
|
|
399
|
-
tags.push(`<meta property="og:image:width" content="${image.width}" />`);
|
|
400
|
-
}
|
|
401
|
-
if (image.height) {
|
|
402
|
-
tags.push(`<meta property="og:image:height" content="${image.height}" />`);
|
|
403
|
-
}
|
|
404
|
-
if (image.alt) {
|
|
405
|
-
tags.push(`<meta property="og:image:alt" content="${escapeHtml(image.alt)}" />`);
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
if (metadata.twitter) {
|
|
411
|
-
const tw = metadata.twitter;
|
|
412
|
-
if (tw.card) {
|
|
413
|
-
tags.push(`<meta name="twitter:card" content="${tw.card}" />`);
|
|
414
|
-
}
|
|
415
|
-
if (tw.title) {
|
|
416
|
-
tags.push(`<meta name="twitter:title" content="${escapeHtml(tw.title)}" />`);
|
|
417
|
-
}
|
|
418
|
-
if (tw.description) {
|
|
419
|
-
tags.push(`<meta name="twitter:description" content="${escapeHtml(tw.description)}" />`);
|
|
420
|
-
}
|
|
421
|
-
if (tw.creator) {
|
|
422
|
-
tags.push(`<meta name="twitter:creator" content="${escapeHtml(tw.creator)}" />`);
|
|
423
|
-
}
|
|
424
|
-
if (tw.site) {
|
|
425
|
-
tags.push(`<meta name="twitter:site" content="${escapeHtml(tw.site)}" />`);
|
|
426
|
-
}
|
|
427
|
-
if (tw.images?.length) {
|
|
428
|
-
tags.push(`<meta name="twitter:image" content="${escapeHtml(tw.images[0])}" />`);
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
if (metadata.authors?.length) {
|
|
432
|
-
for (const author of metadata.authors) {
|
|
433
|
-
tags.push(`<meta name="author" content="${escapeHtml(author.name)}" />`);
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
return tags.join("\n ");
|
|
437
|
-
}
|
|
438
|
-
function discoverBuildAssets(distDir) {
|
|
439
|
-
let jsEntry = "";
|
|
440
|
-
const cssEntries = [];
|
|
441
|
-
const assetsDir = import_path4.default.join(distDir, "assets");
|
|
442
|
-
if (import_fs4.default.existsSync(assetsDir)) {
|
|
443
|
-
const assets = import_fs4.default.readdirSync(assetsDir);
|
|
444
|
-
for (const asset of assets) {
|
|
445
|
-
if (asset.startsWith("index") && asset.endsWith(".js")) {
|
|
446
|
-
jsEntry = `/assets/${asset}`;
|
|
447
|
-
} else if (asset.endsWith(".css")) {
|
|
448
|
-
cssEntries.push(`/assets/${asset}`);
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
return { jsEntry, cssEntries };
|
|
453
|
-
}
|
|
454
|
-
function injectContentIntoHTML(baseHtml, content, metadata) {
|
|
455
|
-
let html = baseHtml;
|
|
456
|
-
html = html.replace(
|
|
457
|
-
/<div id="root"[^>]*>[\s\S]*?<\/div>/,
|
|
458
|
-
`<div id="root">${content}</div>`
|
|
459
|
-
);
|
|
460
|
-
if (metadata) {
|
|
461
|
-
const metaTags = generateMetaTags(metadata);
|
|
462
|
-
if (metadata.title) {
|
|
463
|
-
html = html.replace(/<title>[^<]*<\/title>/g, "");
|
|
464
|
-
}
|
|
465
|
-
if (metadata.description) {
|
|
466
|
-
html = html.replace(/<meta\s+name="description"[^>]*>/g, "");
|
|
467
|
-
}
|
|
468
|
-
if (metadata.openGraph) {
|
|
469
|
-
html = html.replace(/<meta\s+property="og:[^"]*"[^>]*>/g, "");
|
|
470
|
-
}
|
|
471
|
-
if (metadata.twitter) {
|
|
472
|
-
html = html.replace(/<meta\s+name="twitter:[^"]*"[^>]*>/g, "");
|
|
473
|
-
}
|
|
474
|
-
html = html.replace(/<head>/, `<head>
|
|
475
|
-
${metaTags}`);
|
|
476
|
-
}
|
|
477
|
-
return html;
|
|
478
|
-
}
|
|
479
|
-
function escapeHtml(text) {
|
|
480
|
-
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
481
|
-
}
|
|
482
|
-
function generateSitemap(pages, siteUrl) {
|
|
483
|
-
const urls = pages.map((page) => ` <url>
|
|
484
|
-
<loc>${siteUrl}${page.path === "/" ? "" : page.path}</loc>
|
|
485
|
-
<lastmod>${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}</lastmod>
|
|
486
|
-
<changefreq>daily</changefreq>
|
|
487
|
-
<priority>${page.path === "/" ? "1.0" : "0.8"}</priority>
|
|
488
|
-
</url>`).join("\n");
|
|
489
|
-
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
490
|
-
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
491
|
-
${urls}
|
|
492
|
-
</urlset>`;
|
|
493
|
-
}
|
|
494
|
-
function generateRobotsTxt(siteUrl) {
|
|
495
|
-
return `User-agent: *
|
|
496
|
-
Allow: /
|
|
497
|
-
|
|
498
|
-
Sitemap: ${siteUrl}/sitemap.xml`;
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
// src/ssg/prerender.ts
|
|
502
|
-
async function runSSG(config) {
|
|
503
|
-
const startTime = Date.now();
|
|
504
|
-
const failedPages = [];
|
|
505
|
-
console.log("\n\u{1F4E6} Olova SSG - Starting static generation...\n");
|
|
506
|
-
console.log("\u{1F50D} Discovering routes...");
|
|
507
|
-
const allPages = await crawlRoutes(config);
|
|
508
|
-
const staticPages = getStaticPages(allPages);
|
|
509
|
-
console.log(` Found ${allPages.length} total routes`);
|
|
510
|
-
console.log(` ${staticPages.length} static routes will be pre-rendered
|
|
511
|
-
`);
|
|
512
|
-
if (staticPages.length === 0) {
|
|
513
|
-
console.log("\u26A0\uFE0F No static pages found to pre-render\n");
|
|
514
|
-
return {
|
|
515
|
-
totalPages: allPages.length,
|
|
516
|
-
staticPages: 0,
|
|
517
|
-
failedPages: [],
|
|
518
|
-
duration: Date.now() - startTime
|
|
519
|
-
};
|
|
520
|
-
}
|
|
521
|
-
console.log("\u{1F4DD} Generating SSR entry...");
|
|
522
|
-
const ssrEntryPath = generateSSREntry(staticPages, config);
|
|
523
|
-
const distDir = import_path5.default.resolve(config.root, config.outDir);
|
|
524
|
-
const { jsEntry, cssEntries } = discoverBuildAssets(distDir);
|
|
525
|
-
console.log(` JS entry: ${jsEntry || "(none)"}`);
|
|
526
|
-
console.log(` CSS files: ${cssEntries.length}
|
|
527
|
-
`);
|
|
528
|
-
const baseHtmlPath = import_path5.default.join(distDir, "index.html");
|
|
529
|
-
let baseHtml = "";
|
|
530
|
-
if (import_fs5.default.existsSync(baseHtmlPath)) {
|
|
531
|
-
baseHtml = import_fs5.default.readFileSync(baseHtmlPath, "utf-8");
|
|
532
|
-
} else {
|
|
533
|
-
console.warn("\u26A0\uFE0F No index.html found in dist, generating from scratch");
|
|
534
|
-
}
|
|
535
|
-
console.log("\u{1F528} Pre-rendering pages...\n");
|
|
536
|
-
for (const page of staticPages) {
|
|
537
|
-
const pageStartTime = Date.now();
|
|
538
|
-
try {
|
|
539
|
-
const result = await renderPage(page, config, ssrEntryPath);
|
|
540
|
-
const outputPath = page.path === "/" ? import_path5.default.join(distDir, "index.html") : import_path5.default.join(distDir, page.path, "index.html");
|
|
541
|
-
const outputDir = import_path5.default.dirname(outputPath);
|
|
542
|
-
if (!import_fs5.default.existsSync(outputDir)) {
|
|
543
|
-
import_fs5.default.mkdirSync(outputDir, { recursive: true });
|
|
544
|
-
}
|
|
545
|
-
let html;
|
|
546
|
-
if (baseHtml) {
|
|
547
|
-
html = injectContentIntoHTML(baseHtml, result.content, result.metadata);
|
|
548
|
-
} else {
|
|
549
|
-
html = generateHTML({
|
|
550
|
-
content: result.content,
|
|
551
|
-
jsEntry,
|
|
552
|
-
cssEntries,
|
|
553
|
-
metadata: result.metadata,
|
|
554
|
-
routePath: page.path,
|
|
555
|
-
params: page.params
|
|
556
|
-
});
|
|
557
|
-
}
|
|
558
|
-
if (!html.includes("__OLOVA_DATA__")) {
|
|
559
|
-
html = html.replace(
|
|
560
|
-
"</body>",
|
|
561
|
-
`<script>window.__OLOVA_DATA__ = ${JSON.stringify({ route: page.path, params: page.params, hydrated: false })};</script>
|
|
562
|
-
</body>`
|
|
563
|
-
);
|
|
564
|
-
}
|
|
565
|
-
import_fs5.default.writeFileSync(outputPath, html, "utf-8");
|
|
566
|
-
const duration2 = Date.now() - pageStartTime;
|
|
567
|
-
console.log(` \u2705 ${page.path} (${duration2}ms)`);
|
|
568
|
-
} catch (error) {
|
|
569
|
-
failedPages.push(page.path);
|
|
570
|
-
console.log(` \u274C ${page.path} - ${error.message}`);
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
console.log("\n\u{1F4C4} Generating SEO files...");
|
|
574
|
-
const siteUrl = "https://example.com";
|
|
575
|
-
const sitemap = generateSitemap(staticPages, siteUrl);
|
|
576
|
-
import_fs5.default.writeFileSync(import_path5.default.join(distDir, "sitemap.xml"), sitemap, "utf-8");
|
|
577
|
-
console.log(" \u2705 sitemap.xml");
|
|
578
|
-
const robotsPath = import_path5.default.join(distDir, "robots.txt");
|
|
579
|
-
if (!import_fs5.default.existsSync(robotsPath)) {
|
|
580
|
-
import_fs5.default.writeFileSync(robotsPath, generateRobotsTxt(siteUrl), "utf-8");
|
|
581
|
-
console.log(" \u2705 robots.txt");
|
|
582
|
-
}
|
|
583
|
-
cleanupSSREntry(ssrEntryPath);
|
|
584
|
-
const buildManifest = {
|
|
585
|
-
version: Date.now().toString(36),
|
|
586
|
-
buildTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
587
|
-
pages: allPages.map((p) => ({ path: p.path, isStatic: p.isStatic })),
|
|
588
|
-
assets: { js: jsEntry, css: cssEntries }
|
|
589
|
-
};
|
|
590
|
-
import_fs5.default.writeFileSync(
|
|
591
|
-
import_path5.default.join(distDir, "build-manifest.json"),
|
|
592
|
-
JSON.stringify(buildManifest, null, 2),
|
|
593
|
-
"utf-8"
|
|
594
|
-
);
|
|
595
|
-
const duration = Date.now() - startTime;
|
|
596
|
-
const successCount = staticPages.length - failedPages.length;
|
|
597
|
-
console.log(`
|
|
598
|
-
\u2728 SSG Complete!`);
|
|
599
|
-
console.log(` ${successCount}/${staticPages.length} pages generated`);
|
|
600
|
-
console.log(` Duration: ${duration}ms
|
|
601
|
-
`);
|
|
602
|
-
return {
|
|
603
|
-
totalPages: allPages.length,
|
|
604
|
-
staticPages: successCount,
|
|
605
|
-
failedPages,
|
|
606
|
-
duration
|
|
607
|
-
};
|
|
608
|
-
}
|
|
609
|
-
function createSSGConfig(root, options = {}) {
|
|
610
|
-
return {
|
|
611
|
-
root,
|
|
612
|
-
appDir: options.appDir || "src/app",
|
|
613
|
-
outDir: options.outDir || "dist",
|
|
614
|
-
extensions: options.extensions || [".tsx", ".ts", ".jsx", ".js"],
|
|
615
|
-
basePath: options.basePath || ""
|
|
616
|
-
};
|
|
617
|
-
}
|
|
618
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
619
|
-
0 && (module.exports = {
|
|
620
|
-
cleanupSSREntry,
|
|
621
|
-
crawlRoutes,
|
|
622
|
-
createSSGConfig,
|
|
623
|
-
discoverBuildAssets,
|
|
624
|
-
generateHTML,
|
|
625
|
-
generateHydrationScript,
|
|
626
|
-
generateMetaTags,
|
|
627
|
-
generateRobotsTxt,
|
|
628
|
-
generateSSREntry,
|
|
629
|
-
generateSitemap,
|
|
630
|
-
getDynamicPages,
|
|
631
|
-
getStaticPages,
|
|
632
|
-
injectContentIntoHTML,
|
|
633
|
-
renderPage,
|
|
634
|
-
renderPages,
|
|
635
|
-
runSSG
|
|
636
|
-
});
|
|
637
|
-
//# sourceMappingURL=ssg.cjs.map
|