olova 2.0.61 → 2.0.63
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/CHANGELOG.md +5 -0
- package/README.md +42 -61
- package/dist/compiler.d.ts +44 -0
- package/dist/compiler.js +2139 -0
- package/dist/compiler.js.map +1 -0
- package/dist/core.d.ts +4 -0
- package/dist/core.js +859 -0
- package/dist/core.js.map +1 -0
- package/dist/global.d.ts +15 -0
- package/dist/global.js +226 -0
- package/dist/global.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2302 -0
- package/dist/index.js.map +1 -0
- package/dist/runtime.d.ts +89 -0
- package/dist/runtime.js +633 -0
- package/dist/runtime.js.map +1 -0
- package/dist/signals-core-BdfWh1Yt.d.ts +43 -0
- package/dist/vite.d.ts +5 -0
- package/dist/vite.js +2302 -0
- package/dist/vite.js.map +1 -0
- package/package.json +83 -65
- package/dist/chunk-D7SIC5TC.js +0 -367
- package/dist/chunk-D7SIC5TC.js.map +0 -1
- package/dist/entry-server.cjs +0 -120
- package/dist/entry-server.cjs.map +0 -1
- package/dist/entry-server.js +0 -115
- package/dist/entry-server.js.map +0 -1
- package/dist/entry-worker.cjs +0 -133
- package/dist/entry-worker.cjs.map +0 -1
- package/dist/entry-worker.js +0 -127
- package/dist/entry-worker.js.map +0 -1
- package/dist/main.cjs +0 -18
- package/dist/main.cjs.map +0 -1
- package/dist/main.js +0 -16
- package/dist/main.js.map +0 -1
- package/dist/olova.cjs +0 -1680
- package/dist/olova.cjs.map +0 -1
- package/dist/olova.d.cts +0 -72
- package/dist/olova.d.ts +0 -72
- package/dist/olova.js +0 -1321
- package/dist/olova.js.map +0 -1
- package/dist/performance.cjs +0 -386
- package/dist/performance.cjs.map +0 -1
- package/dist/performance.js +0 -3
- package/dist/performance.js.map +0 -1
- package/dist/router.cjs +0 -646
- package/dist/router.cjs.map +0 -1
- package/dist/router.d.cts +0 -113
- package/dist/router.d.ts +0 -113
- package/dist/router.js +0 -632
- package/dist/router.js.map +0 -1
- package/main.tsx +0 -76
- package/olova.ts +0 -619
- package/src/entry-server.tsx +0 -165
- package/src/entry-worker.tsx +0 -201
- package/src/generator/index.ts +0 -409
- package/src/hydration/flight.ts +0 -320
- package/src/hydration/index.ts +0 -12
- package/src/hydration/types.ts +0 -225
- package/src/logger.ts +0 -182
- package/src/main.tsx +0 -24
- package/src/performance.ts +0 -488
- package/src/plugin/index.ts +0 -204
- package/src/router/ErrorBoundary.tsx +0 -145
- package/src/router/Link.tsx +0 -117
- package/src/router/OlovaRouter.tsx +0 -354
- package/src/router/Outlet.tsx +0 -8
- package/src/router/context.ts +0 -117
- package/src/router/index.ts +0 -29
- package/src/router/matching.ts +0 -63
- package/src/router/router.tsx +0 -23
- package/src/router/search-params.ts +0 -29
- package/src/scanner/index.ts +0 -114
- package/src/types/index.ts +0 -190
- package/src/utils/export.ts +0 -85
- package/src/utils/index.ts +0 -4
- package/src/utils/naming.ts +0 -54
- package/src/utils/path.ts +0 -45
- package/tsup.config.ts +0 -35
package/dist/olova.js
DELETED
|
@@ -1,1321 +0,0 @@
|
|
|
1
|
-
import { logger_default, generatePreloadHints, generatePreloadTags, createManualChunks, olovaPerformance } from './chunk-D7SIC5TC.js';
|
|
2
|
-
import fs3, { existsSync } from 'fs';
|
|
3
|
-
import fs4 from 'fs/promises';
|
|
4
|
-
import path4 from 'path';
|
|
5
|
-
import { pathToFileURL, fileURLToPath } from 'url';
|
|
6
|
-
import mdx from '@mdx-js/rollup';
|
|
7
|
-
import { build } from 'vite';
|
|
8
|
-
|
|
9
|
-
function extractBalancedBraces(content, startIndex) {
|
|
10
|
-
if (content[startIndex] !== "{") return null;
|
|
11
|
-
let depth = 0;
|
|
12
|
-
for (let i = startIndex; i < content.length; i++) {
|
|
13
|
-
if (content[i] === "{") depth++;
|
|
14
|
-
else if (content[i] === "}") {
|
|
15
|
-
depth--;
|
|
16
|
-
if (depth === 0) return content.substring(startIndex, i + 1);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
function extractMetadataSource(content) {
|
|
22
|
-
const match = content.match(/export\s+(?:const|let|var)\s+metadata\s*(?::\s*\w+\s*)?=\s*/);
|
|
23
|
-
if (!match || match.index === void 0) return void 0;
|
|
24
|
-
const afterEquals = match.index + match[0].length;
|
|
25
|
-
return extractBalancedBraces(content, afterEquals) || void 0;
|
|
26
|
-
}
|
|
27
|
-
function detectExportType(filePath) {
|
|
28
|
-
try {
|
|
29
|
-
const content = fs3.readFileSync(filePath, "utf-8");
|
|
30
|
-
const hasMetadata = /export\s+(?:const|let|var)\s+metadata\s*/.test(content) || /export\s*\{\s*metadata\s*\}/.test(content);
|
|
31
|
-
const hasRoute = /export\s+(?:const|let|var)\s+route\s*=/.test(content) || /export\s*\{\s*route\s*\}/.test(content);
|
|
32
|
-
const hasGetStaticPaths = /export\s+(?:async\s+)?function\s+getStaticPaths/.test(content) || /export\s+(?:const|let|var)\s+getStaticPaths\s*=/.test(content) || /export\s*\{[^}]*getStaticPaths[^}]*\}/.test(content);
|
|
33
|
-
const hasLoader = /export\s+(?:async\s+)?function\s+loader/.test(content) || /export\s+(?:const|let|var)\s+loader\s*=/.test(content) || /export\s*\{[^}]*loader[^}]*\}/.test(content);
|
|
34
|
-
const metadataSource = extractMetadataSource(content);
|
|
35
|
-
if (filePath.toLowerCase().endsWith(".mdx")) {
|
|
36
|
-
return {
|
|
37
|
-
hasDefault: true,
|
|
38
|
-
namedExport: null,
|
|
39
|
-
hasMetadata: !!metadataSource,
|
|
40
|
-
hasRoute: false,
|
|
41
|
-
hasGetStaticPaths: false,
|
|
42
|
-
hasLoader: false,
|
|
43
|
-
metadataSource
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
if (/export\s+default\s+/.test(content)) {
|
|
47
|
-
return { hasDefault: true, namedExport: null, hasMetadata, metadataSource, hasRoute, hasGetStaticPaths, hasLoader };
|
|
48
|
-
}
|
|
49
|
-
const namedMatch = content.match(/export\s+(?:const|function|class)\s+(\w+)/);
|
|
50
|
-
if (namedMatch) {
|
|
51
|
-
return { hasDefault: false, namedExport: namedMatch[1], hasMetadata, metadataSource, hasRoute, hasGetStaticPaths, hasLoader };
|
|
52
|
-
}
|
|
53
|
-
const exportMatch = content.match(/export\s*\{\s*(\w+)(?:\s+as\s+default)?\s*\}/);
|
|
54
|
-
if (exportMatch) {
|
|
55
|
-
if (content.includes("as default")) {
|
|
56
|
-
return { hasDefault: true, namedExport: null, hasMetadata, metadataSource, hasRoute, hasGetStaticPaths, hasLoader };
|
|
57
|
-
}
|
|
58
|
-
return { hasDefault: false, namedExport: exportMatch[1], hasMetadata, metadataSource, hasRoute, hasGetStaticPaths, hasLoader };
|
|
59
|
-
}
|
|
60
|
-
return { hasDefault: false, namedExport: null, hasMetadata, metadataSource, hasRoute, hasGetStaticPaths, hasLoader };
|
|
61
|
-
} catch {
|
|
62
|
-
return { hasDefault: true, namedExport: null, hasMetadata: false, hasRoute: false, hasGetStaticPaths: false, hasLoader: false };
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// src/utils/naming.ts
|
|
67
|
-
function getRouteName(path5) {
|
|
68
|
-
if (path5 === "/" || path5 === "") return "Root";
|
|
69
|
-
const segments = path5.replace(/^\/|\/$/g, "").split(/[\\/]/);
|
|
70
|
-
const nameSegments = segments.map((segment) => {
|
|
71
|
-
let cleanSegment = segment;
|
|
72
|
-
if (cleanSegment.startsWith(":")) {
|
|
73
|
-
cleanSegment = cleanSegment.slice(1);
|
|
74
|
-
}
|
|
75
|
-
if (cleanSegment.startsWith("[") && cleanSegment.endsWith("]")) {
|
|
76
|
-
cleanSegment = cleanSegment.slice(1, -1);
|
|
77
|
-
if (cleanSegment.startsWith("...")) {
|
|
78
|
-
cleanSegment = cleanSegment.slice(3);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
if (cleanSegment === "*") {
|
|
82
|
-
return "All";
|
|
83
|
-
}
|
|
84
|
-
if (cleanSegment.toLowerCase() === "index") {
|
|
85
|
-
return "";
|
|
86
|
-
}
|
|
87
|
-
if (cleanSegment.startsWith("(") && cleanSegment.endsWith(")")) {
|
|
88
|
-
cleanSegment = cleanSegment.slice(1, -1);
|
|
89
|
-
}
|
|
90
|
-
return capitalize(cleanSegment);
|
|
91
|
-
});
|
|
92
|
-
const finalName = nameSegments.join("");
|
|
93
|
-
if (/^\d/.test(finalName)) {
|
|
94
|
-
return "Page" + finalName;
|
|
95
|
-
}
|
|
96
|
-
return finalName;
|
|
97
|
-
}
|
|
98
|
-
function capitalize(str) {
|
|
99
|
-
return str.charAt(0).toUpperCase() + str.slice(1).replace(/[^a-zA-Z0-9]/g, "");
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// src/utils/path.ts
|
|
103
|
-
function parseDynamicSegment(segment) {
|
|
104
|
-
if (segment.match(/^\[\.\.\.(.+)\]$/)) {
|
|
105
|
-
const paramName = segment.match(/^\[\.\.\.(.+)\]$/)?.[1] || "slug";
|
|
106
|
-
return { isDynamic: true, paramName, isCatchAll: true };
|
|
107
|
-
}
|
|
108
|
-
const bracketMatch = segment.match(/^\[(.+)\]$/);
|
|
109
|
-
if (bracketMatch) {
|
|
110
|
-
return { isDynamic: true, paramName: bracketMatch[1], isCatchAll: false };
|
|
111
|
-
}
|
|
112
|
-
return { isDynamic: false, paramName: null, isCatchAll: false };
|
|
113
|
-
}
|
|
114
|
-
function isRouteGroup(segment) {
|
|
115
|
-
return /^\(.+\)$/.test(segment);
|
|
116
|
-
}
|
|
117
|
-
function pathToRoute(relativePath, sep) {
|
|
118
|
-
const params = [];
|
|
119
|
-
let hasCatchAll = false;
|
|
120
|
-
const segments = relativePath.split(sep).filter(Boolean);
|
|
121
|
-
const routeSegments = segments.filter((segment) => !isRouteGroup(segment)).map((segment) => {
|
|
122
|
-
const { isDynamic, paramName, isCatchAll } = parseDynamicSegment(segment);
|
|
123
|
-
if (isDynamic && paramName) {
|
|
124
|
-
params.push(paramName);
|
|
125
|
-
if (isCatchAll) {
|
|
126
|
-
hasCatchAll = true;
|
|
127
|
-
return `*`;
|
|
128
|
-
}
|
|
129
|
-
return `:${paramName}`;
|
|
130
|
-
}
|
|
131
|
-
return segment;
|
|
132
|
-
});
|
|
133
|
-
const routePath = "/" + routeSegments.join("/");
|
|
134
|
-
return { routePath: routePath === "/" ? "/" : routePath, params, hasCatchAll };
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// src/generator/index.ts
|
|
138
|
-
function stripImportExtension(filePath) {
|
|
139
|
-
return filePath.replace(/\.tsx?$/, "");
|
|
140
|
-
}
|
|
141
|
-
function pathToRouteId(routePath) {
|
|
142
|
-
if (routePath === "/") return "_root";
|
|
143
|
-
return routePath.replace(/^\//, "").replace(/\//g, "_").replace(/:/g, "$").replace(/\*/g, "$catchall");
|
|
144
|
-
}
|
|
145
|
-
function extractParamNames(routePath) {
|
|
146
|
-
const params = [];
|
|
147
|
-
for (const segment of routePath.split("/")) {
|
|
148
|
-
if (segment.startsWith(":")) params.push(segment.slice(1));
|
|
149
|
-
else if (segment === "*") params.push("*");
|
|
150
|
-
}
|
|
151
|
-
return params;
|
|
152
|
-
}
|
|
153
|
-
function generateParamType(routePath) {
|
|
154
|
-
const params = extractParamNames(routePath);
|
|
155
|
-
if (params.length === 0) return null;
|
|
156
|
-
const fields = params.map(
|
|
157
|
-
(p) => p === "*" ? "'*': string" : `${p}: string`
|
|
158
|
-
);
|
|
159
|
-
return `{ ${fields.join("; ")} }`;
|
|
160
|
-
}
|
|
161
|
-
function generateRouteTree(routes, notFoundPages, layouts, loadingPages, errorPages, middlewares, srcDir, packageName = "olovastart") {
|
|
162
|
-
const usedNames = /* @__PURE__ */ new Map();
|
|
163
|
-
const getUniqueName = (baseName) => {
|
|
164
|
-
const count = usedNames.get(baseName) || 0;
|
|
165
|
-
usedNames.set(baseName, count + 1);
|
|
166
|
-
return count === 0 ? baseName : `${baseName}${count}`;
|
|
167
|
-
};
|
|
168
|
-
const getRouteName2 = (filePath, suffix = "") => {
|
|
169
|
-
const relPath = path4.relative(srcDir, filePath);
|
|
170
|
-
const pathNoExt = relPath.replace(/\.(tsx?|mdx)$/, "");
|
|
171
|
-
if (pathNoExt === "" || pathNoExt === "." || pathNoExt === "index") {
|
|
172
|
-
return getUniqueName("Root" + suffix);
|
|
173
|
-
}
|
|
174
|
-
const name = getRouteName(pathNoExt);
|
|
175
|
-
if (!name) return getUniqueName("Root" + suffix);
|
|
176
|
-
if (suffix && name.toLowerCase().endsWith(suffix.toLowerCase())) {
|
|
177
|
-
return getUniqueName(name);
|
|
178
|
-
}
|
|
179
|
-
return getUniqueName(name + suffix);
|
|
180
|
-
};
|
|
181
|
-
const findClosestEntry = (routePath, entries) => {
|
|
182
|
-
let best = null;
|
|
183
|
-
for (const entry of entries) {
|
|
184
|
-
const ep = entry.path;
|
|
185
|
-
if (ep === "/" || routePath === ep || routePath.startsWith(ep + "/")) {
|
|
186
|
-
if (!best || ep.length > best.path.length) best = entry;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
return best;
|
|
190
|
-
};
|
|
191
|
-
const routeNames = [];
|
|
192
|
-
const routeImports = routes.map((route) => {
|
|
193
|
-
const relativePath = stripImportExtension("./app/" + path4.relative(srcDir, route.component).replace(/\\/g, "/"));
|
|
194
|
-
const moduleName = getRouteName2(route.component, "RouteModule");
|
|
195
|
-
const lazyName = getRouteName2(route.component, "Route");
|
|
196
|
-
routeNames.push({ moduleName, lazyName, eager: true });
|
|
197
|
-
return `import * as ${moduleName} from '${relativePath}';`;
|
|
198
|
-
}).join("\n");
|
|
199
|
-
const notFoundNames = [];
|
|
200
|
-
const notFoundImports = notFoundPages.map((nf) => {
|
|
201
|
-
const relativePath = stripImportExtension("./app/" + path4.relative(srcDir, nf.filePath).replace(/\\/g, "/"));
|
|
202
|
-
if (nf.hasMetadata) {
|
|
203
|
-
const moduleName = getRouteName2(nf.filePath, "NotFoundModule");
|
|
204
|
-
notFoundNames.push(moduleName);
|
|
205
|
-
return `import * as ${moduleName} from '${relativePath}';`;
|
|
206
|
-
}
|
|
207
|
-
const importName = getRouteName2(nf.filePath, "NotFound");
|
|
208
|
-
notFoundNames.push(importName);
|
|
209
|
-
if (nf.hasDefault) return `import ${importName} from '${relativePath}';`;
|
|
210
|
-
if (nf.namedExport) return `import { ${nf.namedExport} as ${importName} } from '${relativePath}';`;
|
|
211
|
-
return `import ${importName} from '${relativePath}';`;
|
|
212
|
-
}).join("\n");
|
|
213
|
-
const layoutNames = [];
|
|
214
|
-
const layoutImports = layouts.map((layout) => {
|
|
215
|
-
const relativePath = stripImportExtension("./app/" + path4.relative(srcDir, layout.filePath).replace(/\\/g, "/"));
|
|
216
|
-
if (layout.hasMetadata) {
|
|
217
|
-
const moduleName = getRouteName2(layout.filePath, "LayoutModule");
|
|
218
|
-
layoutNames.push(moduleName);
|
|
219
|
-
return `import * as ${moduleName} from '${relativePath}';`;
|
|
220
|
-
}
|
|
221
|
-
const importName = getRouteName2(layout.filePath, "Layout");
|
|
222
|
-
layoutNames.push(importName);
|
|
223
|
-
if (layout.hasDefault) return `import ${importName} from '${relativePath}';`;
|
|
224
|
-
if (layout.namedExport) return `import { ${layout.namedExport} as ${importName} } from '${relativePath}';`;
|
|
225
|
-
return `import ${importName} from '${relativePath}';`;
|
|
226
|
-
}).join("\n");
|
|
227
|
-
const loadingNames = [];
|
|
228
|
-
const loadingImports = loadingPages.map((lp) => {
|
|
229
|
-
const relativePath = stripImportExtension("./app/" + path4.relative(srcDir, lp.filePath).replace(/\\/g, "/"));
|
|
230
|
-
const importName = getRouteName2(lp.filePath, "Loading");
|
|
231
|
-
loadingNames.push(importName);
|
|
232
|
-
if (lp.hasDefault) return `import ${importName} from '${relativePath}';`;
|
|
233
|
-
if (lp.namedExport) return `import { ${lp.namedExport} as ${importName} } from '${relativePath}';`;
|
|
234
|
-
return `import ${importName} from '${relativePath}';`;
|
|
235
|
-
}).join("\n");
|
|
236
|
-
const errorNames = [];
|
|
237
|
-
const errorImports = errorPages.map((ep) => {
|
|
238
|
-
const relativePath = stripImportExtension("./app/" + path4.relative(srcDir, ep.filePath).replace(/\\/g, "/"));
|
|
239
|
-
const importName = getRouteName2(ep.filePath, "Error");
|
|
240
|
-
errorNames.push(importName);
|
|
241
|
-
if (ep.hasDefault) return `import ${importName} from '${relativePath}';`;
|
|
242
|
-
if (ep.namedExport) return `import { ${ep.namedExport} as ${importName} } from '${relativePath}';`;
|
|
243
|
-
return `import ${importName} from '${relativePath}';`;
|
|
244
|
-
}).join("\n");
|
|
245
|
-
const middlewareNames = [];
|
|
246
|
-
const middlewareImports = middlewares.map((mw) => {
|
|
247
|
-
const relativePath = stripImportExtension("./app/" + path4.relative(srcDir, mw.filePath).replace(/\\/g, "/"));
|
|
248
|
-
const importName = getRouteName2(mw.filePath, "Middleware");
|
|
249
|
-
middlewareNames.push(importName);
|
|
250
|
-
if (mw.hasDefault) return `import ${importName} from '${relativePath}';`;
|
|
251
|
-
if (mw.namedExport) return `import { ${mw.namedExport} as ${importName} } from '${relativePath}';`;
|
|
252
|
-
return `import ${importName} from '${relativePath}';`;
|
|
253
|
-
}).join("\n");
|
|
254
|
-
const routeObjects = routes.map((route, index) => {
|
|
255
|
-
const { moduleName, lazyName, eager } = routeNames[index];
|
|
256
|
-
const component = eager ? `${moduleName}.default` : lazyName;
|
|
257
|
-
const routeId = pathToRouteId(route.path);
|
|
258
|
-
const fields = [
|
|
259
|
-
`id: '${routeId}'`,
|
|
260
|
-
`path: '${route.path}'`,
|
|
261
|
-
`component: ${component}`
|
|
262
|
-
];
|
|
263
|
-
if (route.params && route.params.length > 0) {
|
|
264
|
-
fields.push(`params: [${route.params.map((p) => `'${p}'`).join(", ")}]`);
|
|
265
|
-
}
|
|
266
|
-
if (route.metadataSource) {
|
|
267
|
-
fields.push(`metadata: ${route.metadataSource}`);
|
|
268
|
-
} else if (route.hasMetadata) {
|
|
269
|
-
fields.push(`metadata: ${moduleName}.metadata`);
|
|
270
|
-
}
|
|
271
|
-
if (route.hasGetStaticPaths) {
|
|
272
|
-
fields.push(`getStaticPaths: ${moduleName}.getStaticPaths`);
|
|
273
|
-
}
|
|
274
|
-
if (route.hasLoader) {
|
|
275
|
-
fields.push(`loader: ${moduleName}.loader`);
|
|
276
|
-
}
|
|
277
|
-
const closestLoading = findClosestEntry(route.path, loadingPages);
|
|
278
|
-
if (closestLoading) {
|
|
279
|
-
const idx = loadingPages.indexOf(closestLoading);
|
|
280
|
-
fields.push(`loading: ${loadingNames[idx]}`);
|
|
281
|
-
}
|
|
282
|
-
const closestError = findClosestEntry(route.path, errorPages);
|
|
283
|
-
if (closestError) {
|
|
284
|
-
const idx = errorPages.indexOf(closestError);
|
|
285
|
-
fields.push(`error: ${errorNames[idx]}`);
|
|
286
|
-
}
|
|
287
|
-
return ` {
|
|
288
|
-
${fields.join(",\n ")}
|
|
289
|
-
}`;
|
|
290
|
-
}).join(",\n");
|
|
291
|
-
const notFoundObjects = notFoundPages.map((nf, index) => {
|
|
292
|
-
const name = notFoundNames[index];
|
|
293
|
-
const isModule = nf.hasMetadata;
|
|
294
|
-
const component = isModule ? `${name}.default` : name;
|
|
295
|
-
const fields = [
|
|
296
|
-
`pathPrefix: '${nf.pathPrefix}'`,
|
|
297
|
-
`component: ${component}`
|
|
298
|
-
];
|
|
299
|
-
if (isModule) fields.push(`metadata: ${name}.metadata`);
|
|
300
|
-
return ` {
|
|
301
|
-
${fields.join(",\n ")}
|
|
302
|
-
}`;
|
|
303
|
-
}).join(",\n");
|
|
304
|
-
const layoutObjects = layouts.map((layout, index) => {
|
|
305
|
-
const name = layoutNames[index];
|
|
306
|
-
const isModule = layout.hasMetadata;
|
|
307
|
-
const component = isModule ? `${name}.default` : name;
|
|
308
|
-
const childRouteIds = routes.filter((r) => {
|
|
309
|
-
if (layout.path === "/") return true;
|
|
310
|
-
return r.path === layout.path || r.path.startsWith(layout.path + "/");
|
|
311
|
-
}).map((r) => `'${pathToRouteId(r.path)}'`);
|
|
312
|
-
const fields = [
|
|
313
|
-
`path: '${layout.path}'`,
|
|
314
|
-
`layout: ${component}`,
|
|
315
|
-
`children: [${childRouteIds.join(", ")}]`
|
|
316
|
-
];
|
|
317
|
-
if (isModule) fields.push(`metadata: ${name}.metadata`);
|
|
318
|
-
return ` {
|
|
319
|
-
${fields.join(",\n ")}
|
|
320
|
-
}`;
|
|
321
|
-
}).join(",\n");
|
|
322
|
-
const middlewareEntries = middlewares.map((mw, index) => {
|
|
323
|
-
return ` '${mw.path}': ${middlewareNames[index]}`;
|
|
324
|
-
}).join(",\n");
|
|
325
|
-
const routeParamTypes = routes.filter((r) => r.params && r.params.length > 0).map((r) => {
|
|
326
|
-
const paramType = generateParamType(r.path);
|
|
327
|
-
return paramType ? ` '${r.path}': ${paramType};` : null;
|
|
328
|
-
}).filter(Boolean).join("\n");
|
|
329
|
-
const routePaths = routes.length > 0 ? routes.map((r) => `'${r.path}'`).join(" | ") : "never";
|
|
330
|
-
const routeIds = routes.length > 0 ? routes.map((r) => `'${pathToRouteId(r.path)}'`).join(" | ") : "never";
|
|
331
|
-
const manifestEntries = routes.map((r) => {
|
|
332
|
-
const paramNames = extractParamNames(r.path);
|
|
333
|
-
const isDynamic = paramNames.length > 0;
|
|
334
|
-
const hasCatchAll = r.path.includes("*");
|
|
335
|
-
return ` '${pathToRouteId(r.path)}': { id: '${pathToRouteId(r.path)}', path: '${r.path}', params: [${paramNames.map((p) => `'${p}'`).join(", ")}], isDynamic: ${isDynamic}, hasCatchAll: ${hasCatchAll}, hasMetadata: ${r.hasMetadata}, hasGetStaticPaths: ${r.hasGetStaticPaths} }`;
|
|
336
|
-
}).join(",\n");
|
|
337
|
-
const allImports = [
|
|
338
|
-
routeImports,
|
|
339
|
-
notFoundImports,
|
|
340
|
-
layoutImports,
|
|
341
|
-
loadingImports,
|
|
342
|
-
errorImports,
|
|
343
|
-
middlewareImports
|
|
344
|
-
].filter(Boolean).join("\n");
|
|
345
|
-
return `/* prettier-ignore-start */
|
|
346
|
-
|
|
347
|
-
/* eslint-disable */
|
|
348
|
-
|
|
349
|
-
// @ts-nocheck
|
|
350
|
-
|
|
351
|
-
// noinspection JSUnusedGlobalSymbols
|
|
352
|
-
|
|
353
|
-
/**
|
|
354
|
-
* This file was automatically generated by Olova Router.
|
|
355
|
-
* DO NOT MODIFY IT BY HAND. Instead, modify the source route files
|
|
356
|
-
* and regenerate this file by running the dev server or build command.
|
|
357
|
-
*
|
|
358
|
-
* @generated Olova Route Tree
|
|
359
|
-
* @routes ${routes.length}
|
|
360
|
-
* @layouts ${layouts.length}
|
|
361
|
-
* @404s ${notFoundPages.length}
|
|
362
|
-
* @loading ${loadingPages.length}
|
|
363
|
-
* @errors ${errorPages.length}
|
|
364
|
-
* @middleware ${middlewares.length}
|
|
365
|
-
*/
|
|
366
|
-
|
|
367
|
-
import { lazy } from 'react';
|
|
368
|
-
import {
|
|
369
|
-
createLink,
|
|
370
|
-
OlovaRouter,
|
|
371
|
-
Outlet,
|
|
372
|
-
redirect,
|
|
373
|
-
useIsNavigating,
|
|
374
|
-
useNavigationEvent,
|
|
375
|
-
useParams,
|
|
376
|
-
usePathname,
|
|
377
|
-
useRedirect,
|
|
378
|
-
useRouter,
|
|
379
|
-
useSearchParams,
|
|
380
|
-
useSelectedLayoutSegment,
|
|
381
|
-
useSelectedLayoutSegments,
|
|
382
|
-
} from '${packageName}/router';
|
|
383
|
-
${allImports}
|
|
384
|
-
|
|
385
|
-
/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
386
|
-
* Route Definitions
|
|
387
|
-
* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
388
|
-
|
|
389
|
-
export const routes = [
|
|
390
|
-
${routeObjects}
|
|
391
|
-
];
|
|
392
|
-
|
|
393
|
-
export const notFoundPages = [
|
|
394
|
-
${notFoundObjects}
|
|
395
|
-
];
|
|
396
|
-
|
|
397
|
-
export const layouts = [
|
|
398
|
-
${layoutObjects}
|
|
399
|
-
];
|
|
400
|
-
${middlewares.length > 0 ? `
|
|
401
|
-
export const middlewares = {
|
|
402
|
-
${middlewareEntries}
|
|
403
|
-
};
|
|
404
|
-
` : `
|
|
405
|
-
export const middlewares = {};
|
|
406
|
-
`}
|
|
407
|
-
/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
408
|
-
* Route Manifest (build-time introspection, sitemap generation, etc.)
|
|
409
|
-
* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
410
|
-
|
|
411
|
-
export const routeManifest = {
|
|
412
|
-
${manifestEntries}
|
|
413
|
-
} as const;
|
|
414
|
-
|
|
415
|
-
/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
416
|
-
* Type-Safe Route Types
|
|
417
|
-
* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
418
|
-
|
|
419
|
-
export type RoutePaths = ${routePaths};
|
|
420
|
-
|
|
421
|
-
export type RouteIds = ${routeIds};
|
|
422
|
-
|
|
423
|
-
export interface RouteParams {
|
|
424
|
-
${routeParamTypes || " // No dynamic routes"}
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
export type ParamsFor<P extends RoutePaths> = P extends keyof RouteParams ? RouteParams[P] : Record<string, never>;
|
|
428
|
-
|
|
429
|
-
/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
430
|
-
* Route Lookup Helpers
|
|
431
|
-
* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
432
|
-
|
|
433
|
-
const _routeById = new Map(routes.map(r => [r.id, r]));
|
|
434
|
-
|
|
435
|
-
export function getRouteById(id: RouteIds) {
|
|
436
|
-
return _routeById.get(id);
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
export function getRouteByPath(path: RoutePaths) {
|
|
440
|
-
return routes.find(r => r.path === path);
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
444
|
-
* Exports
|
|
445
|
-
* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
446
|
-
|
|
447
|
-
export const Link = createLink<RoutePaths>();
|
|
448
|
-
|
|
449
|
-
export {
|
|
450
|
-
OlovaRouter,
|
|
451
|
-
Outlet,
|
|
452
|
-
redirect,
|
|
453
|
-
useIsNavigating,
|
|
454
|
-
useNavigationEvent,
|
|
455
|
-
useParams,
|
|
456
|
-
usePathname,
|
|
457
|
-
useRedirect,
|
|
458
|
-
useRouter,
|
|
459
|
-
useSearchParams,
|
|
460
|
-
useSelectedLayoutSegment,
|
|
461
|
-
useSelectedLayoutSegments,
|
|
462
|
-
};
|
|
463
|
-
|
|
464
|
-
export type {
|
|
465
|
-
NavigateOptions,
|
|
466
|
-
NotFoundPageConfig,
|
|
467
|
-
SearchParams,
|
|
468
|
-
SetSearchParamsOptions,
|
|
469
|
-
LayoutRoute,
|
|
470
|
-
Metadata,
|
|
471
|
-
} from '${packageName}/router';
|
|
472
|
-
|
|
473
|
-
/* prettier-ignore-end */
|
|
474
|
-
`;
|
|
475
|
-
}
|
|
476
|
-
var RESERVED_NAMES = /* @__PURE__ */ new Set([
|
|
477
|
-
"page",
|
|
478
|
-
"layout",
|
|
479
|
-
"loading",
|
|
480
|
-
"error",
|
|
481
|
-
"not-found",
|
|
482
|
-
"middleware",
|
|
483
|
-
"route.tree"
|
|
484
|
-
]);
|
|
485
|
-
function scanDirectory(dir, rootDir, extensions, routes, notFoundPages, layouts, loadingPages, errorPages, middlewares, isRoot = false) {
|
|
486
|
-
if (!fs3.existsSync(dir)) return;
|
|
487
|
-
const entries = fs3.readdirSync(dir, { withFileTypes: true });
|
|
488
|
-
for (const entry of entries) {
|
|
489
|
-
const fullPath = path4.join(dir, entry.name);
|
|
490
|
-
if (entry.isDirectory()) {
|
|
491
|
-
if (entry.name === "node_modules" || entry.name === "assets" || entry.name.startsWith("_")) continue;
|
|
492
|
-
scanDirectory(fullPath, rootDir, extensions, routes, notFoundPages, layouts, loadingPages, errorPages, middlewares, false);
|
|
493
|
-
} else if (entry.isFile()) {
|
|
494
|
-
const ext = path4.extname(entry.name);
|
|
495
|
-
const baseName = path4.basename(entry.name, ext);
|
|
496
|
-
if (baseName === "layout" && extensions.includes(ext)) {
|
|
497
|
-
const relativePath = path4.relative(rootDir, dir);
|
|
498
|
-
const { routePath } = pathToRoute(relativePath, path4.sep);
|
|
499
|
-
layouts.push({
|
|
500
|
-
path: isRoot ? "/" : routePath,
|
|
501
|
-
filePath: fullPath
|
|
502
|
-
});
|
|
503
|
-
} else if (baseName === "loading" && extensions.includes(ext)) {
|
|
504
|
-
const relativePath = path4.relative(rootDir, dir);
|
|
505
|
-
const { routePath } = pathToRoute(relativePath, path4.sep);
|
|
506
|
-
loadingPages.push({
|
|
507
|
-
path: isRoot ? "/" : routePath,
|
|
508
|
-
filePath: fullPath
|
|
509
|
-
});
|
|
510
|
-
} else if (baseName === "error" && extensions.includes(ext)) {
|
|
511
|
-
const relativePath = path4.relative(rootDir, dir);
|
|
512
|
-
const { routePath } = pathToRoute(relativePath, path4.sep);
|
|
513
|
-
errorPages.push({
|
|
514
|
-
path: isRoot ? "/" : routePath,
|
|
515
|
-
filePath: fullPath
|
|
516
|
-
});
|
|
517
|
-
} else if (baseName === "middleware" && extensions.includes(ext)) {
|
|
518
|
-
const relativePath = path4.relative(rootDir, dir);
|
|
519
|
-
const { routePath } = pathToRoute(relativePath, path4.sep);
|
|
520
|
-
middlewares.push({
|
|
521
|
-
path: isRoot ? "/" : routePath,
|
|
522
|
-
filePath: fullPath
|
|
523
|
-
});
|
|
524
|
-
} else if ((baseName === "not-found" || baseName === "404") && extensions.includes(ext)) {
|
|
525
|
-
const relativeParts = path4.relative(rootDir, dir).split(path4.sep).filter(Boolean);
|
|
526
|
-
const filteredParts = relativeParts.filter((p) => !isRouteGroup(p));
|
|
527
|
-
const pathPrefix = isRoot ? "" : "/" + filteredParts.join("/");
|
|
528
|
-
notFoundPages.push({ pathPrefix: pathPrefix || "", filePath: fullPath });
|
|
529
|
-
} else if (baseName === "page" && extensions.includes(ext)) {
|
|
530
|
-
const relativePath = path4.relative(rootDir, dir);
|
|
531
|
-
const { routePath, params } = pathToRoute(relativePath, path4.sep);
|
|
532
|
-
routes.push({ path: routePath, filePath: fullPath, isDynamic: params.length > 0, params });
|
|
533
|
-
} else if (!RESERVED_NAMES.has(baseName) && !baseName.endsWith(".d") && !baseName.startsWith("_") && extensions.includes(ext)) {
|
|
534
|
-
const relativePath = path4.relative(rootDir, fullPath);
|
|
535
|
-
const relativePathNoExt = relativePath.substring(0, relativePath.length - ext.length);
|
|
536
|
-
const { routePath, params } = pathToRoute(relativePathNoExt, path4.sep);
|
|
537
|
-
routes.push({ path: routePath, filePath: fullPath, isDynamic: params.length > 0, params });
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
function scanRoutes(rootDir, extensions) {
|
|
543
|
-
const routes = [];
|
|
544
|
-
const notFoundPages = [];
|
|
545
|
-
const layouts = [];
|
|
546
|
-
const loadingPages = [];
|
|
547
|
-
const errorPages = [];
|
|
548
|
-
const middlewares = [];
|
|
549
|
-
const absoluteRoot = path4.isAbsolute(rootDir) ? rootDir : path4.resolve(rootDir);
|
|
550
|
-
if (!fs3.existsSync(absoluteRoot)) {
|
|
551
|
-
throw new Error(`Olova Router: Root directory does not exist: ${absoluteRoot}`);
|
|
552
|
-
}
|
|
553
|
-
scanDirectory(absoluteRoot, absoluteRoot, extensions, routes, notFoundPages, layouts, loadingPages, errorPages, middlewares, true);
|
|
554
|
-
routes.sort((a, b) => a.isDynamic !== b.isDynamic ? a.isDynamic ? 1 : -1 : a.path.localeCompare(b.path));
|
|
555
|
-
notFoundPages.sort((a, b) => b.pathPrefix.length - a.pathPrefix.length);
|
|
556
|
-
layouts.sort((a, b) => a.path.length - b.path.length);
|
|
557
|
-
loadingPages.sort((a, b) => b.path.length - a.path.length);
|
|
558
|
-
errorPages.sort((a, b) => b.path.length - a.path.length);
|
|
559
|
-
middlewares.sort((a, b) => a.path.length - b.path.length);
|
|
560
|
-
return { routes, notFoundPages, layouts, loadingPages, errorPages, middlewares };
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
// src/plugin/index.ts
|
|
564
|
-
function olovaRouter(options = {}) {
|
|
565
|
-
const rootDir = options.rootDir || "src/app";
|
|
566
|
-
const extensions = options.extensions || [".tsx", ".ts", ".mdx"];
|
|
567
|
-
const packageName = options.packageName || "olovastart";
|
|
568
|
-
let config;
|
|
569
|
-
let absoluteRootDir;
|
|
570
|
-
let watcher = null;
|
|
571
|
-
let timer = null;
|
|
572
|
-
function generateRouteTreeFile() {
|
|
573
|
-
const { routes, notFoundPages, layouts, loadingPages, errorPages, middlewares } = scanRoutes(absoluteRootDir, extensions);
|
|
574
|
-
const routeConfigs = routes.map((r) => {
|
|
575
|
-
let exportInfo = detectExportType(r.filePath);
|
|
576
|
-
if (r.filePath.toLowerCase().endsWith(".mdx")) {
|
|
577
|
-
exportInfo = {
|
|
578
|
-
...exportInfo,
|
|
579
|
-
hasDefault: true,
|
|
580
|
-
namedExport: null
|
|
581
|
-
};
|
|
582
|
-
}
|
|
583
|
-
return {
|
|
584
|
-
path: r.path,
|
|
585
|
-
component: r.filePath.replace(/\\/g, "/"),
|
|
586
|
-
params: r.params.length > 0 ? r.params : void 0,
|
|
587
|
-
hasDefault: exportInfo.hasDefault,
|
|
588
|
-
namedExport: exportInfo.namedExport,
|
|
589
|
-
hasMetadata: exportInfo.hasMetadata,
|
|
590
|
-
metadataSource: exportInfo.metadataSource,
|
|
591
|
-
hasRoute: exportInfo.hasRoute,
|
|
592
|
-
hasGetStaticPaths: exportInfo.hasGetStaticPaths,
|
|
593
|
-
hasLoader: exportInfo.hasLoader
|
|
594
|
-
};
|
|
595
|
-
});
|
|
596
|
-
const notFoundConfigs = notFoundPages.map((nf) => {
|
|
597
|
-
const exportInfo = detectExportType(nf.filePath);
|
|
598
|
-
return {
|
|
599
|
-
pathPrefix: nf.pathPrefix,
|
|
600
|
-
filePath: nf.filePath.replace(/\\/g, "/"),
|
|
601
|
-
hasDefault: exportInfo.hasDefault,
|
|
602
|
-
namedExport: exportInfo.namedExport,
|
|
603
|
-
hasMetadata: exportInfo.hasMetadata
|
|
604
|
-
};
|
|
605
|
-
});
|
|
606
|
-
const layoutConfigs = layouts.map((l) => {
|
|
607
|
-
const exportInfo = detectExportType(l.filePath);
|
|
608
|
-
return {
|
|
609
|
-
path: l.path,
|
|
610
|
-
filePath: l.filePath.replace(/\\/g, "/"),
|
|
611
|
-
hasDefault: exportInfo.hasDefault,
|
|
612
|
-
namedExport: exportInfo.namedExport,
|
|
613
|
-
hasMetadata: exportInfo.hasMetadata
|
|
614
|
-
};
|
|
615
|
-
});
|
|
616
|
-
const loadingConfigs = loadingPages.map((lp) => {
|
|
617
|
-
const exportInfo = detectExportType(lp.filePath);
|
|
618
|
-
return {
|
|
619
|
-
path: lp.path,
|
|
620
|
-
filePath: lp.filePath.replace(/\\/g, "/"),
|
|
621
|
-
hasDefault: exportInfo.hasDefault,
|
|
622
|
-
namedExport: exportInfo.namedExport
|
|
623
|
-
};
|
|
624
|
-
});
|
|
625
|
-
const errorConfigs = errorPages.map((ep) => {
|
|
626
|
-
const exportInfo = detectExportType(ep.filePath);
|
|
627
|
-
return {
|
|
628
|
-
path: ep.path,
|
|
629
|
-
filePath: ep.filePath.replace(/\\/g, "/"),
|
|
630
|
-
hasDefault: exportInfo.hasDefault,
|
|
631
|
-
namedExport: exportInfo.namedExport
|
|
632
|
-
};
|
|
633
|
-
});
|
|
634
|
-
const middlewareConfigs = middlewares.map((mw) => {
|
|
635
|
-
const exportInfo = detectExportType(mw.filePath);
|
|
636
|
-
return {
|
|
637
|
-
path: mw.path,
|
|
638
|
-
filePath: mw.filePath.replace(/\\/g, "/"),
|
|
639
|
-
hasDefault: exportInfo.hasDefault,
|
|
640
|
-
namedExport: exportInfo.namedExport
|
|
641
|
-
};
|
|
642
|
-
});
|
|
643
|
-
const content = generateRouteTree(routeConfigs, notFoundConfigs, layoutConfigs, loadingConfigs, errorConfigs, middlewareConfigs, absoluteRootDir, packageName);
|
|
644
|
-
const treePath = path4.resolve(config.root, "src", "route.tree.ts");
|
|
645
|
-
const existing = fs3.existsSync(treePath) ? fs3.readFileSync(treePath, "utf-8") : "";
|
|
646
|
-
if (existing !== content) {
|
|
647
|
-
fs3.writeFileSync(treePath, content);
|
|
648
|
-
console.log("\x1B[32m[olova]\x1B[0m Route tree updated");
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
function startWatcher() {
|
|
652
|
-
if (watcher) return;
|
|
653
|
-
watcher = fs3.watch(absoluteRootDir, { recursive: true }, (eventType, filename) => {
|
|
654
|
-
if (!filename) return;
|
|
655
|
-
if (filename.includes("route.tree.ts")) return;
|
|
656
|
-
const ext = path4.extname(filename);
|
|
657
|
-
const isConfiguredExtension = extensions.includes(ext);
|
|
658
|
-
const isPageFile = filename.includes("page") && isConfiguredExtension;
|
|
659
|
-
const is404File = filename.includes("404") && isConfiguredExtension;
|
|
660
|
-
const isNotFoundFile = filename.includes("not-found") && isConfiguredExtension;
|
|
661
|
-
const isLayoutFile = filename.includes("layout") && isConfiguredExtension;
|
|
662
|
-
const isLoadingFile = filename.includes("loading") && isConfiguredExtension;
|
|
663
|
-
const isErrorFile = filename.includes("error") && isConfiguredExtension;
|
|
664
|
-
const isMiddlewareFile = filename.includes("middleware") && isConfiguredExtension;
|
|
665
|
-
const isDirectory = !filename.includes(".");
|
|
666
|
-
const isDynamicSegment = filename.includes("[");
|
|
667
|
-
const isRenameEvent = eventType === "rename";
|
|
668
|
-
if (isPageFile || is404File || isNotFoundFile || isLayoutFile || isLoadingFile || isErrorFile || isMiddlewareFile || isDirectory || isDynamicSegment || isRenameEvent) {
|
|
669
|
-
if (isPageFile && filename) {
|
|
670
|
-
const fullPath = path4.join(absoluteRootDir, filename);
|
|
671
|
-
if (fs3.existsSync(fullPath)) {
|
|
672
|
-
const stat = fs3.statSync(fullPath);
|
|
673
|
-
if (stat.size === 0 && !filename.endsWith(".mdx")) {
|
|
674
|
-
const relativeDir = path4.relative(absoluteRootDir, path4.dirname(fullPath));
|
|
675
|
-
const pascalCaseName = getRouteName(relativeDir);
|
|
676
|
-
const boilerplate = `
|
|
677
|
-
export const metadata = {
|
|
678
|
-
title: "${pascalCaseName}",
|
|
679
|
-
description: "${pascalCaseName} page",
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
export default function ${pascalCaseName}() {
|
|
683
|
-
return (
|
|
684
|
-
<div>
|
|
685
|
-
<h1>${pascalCaseName}</h1>
|
|
686
|
-
</div>
|
|
687
|
-
);
|
|
688
|
-
}
|
|
689
|
-
`;
|
|
690
|
-
fs3.writeFileSync(fullPath, boilerplate);
|
|
691
|
-
console.log(`\x1B[32m[olova]\x1B[0m Generated boilerplate for ${filename}`);
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
if (timer) clearTimeout(timer);
|
|
696
|
-
timer = setTimeout(() => {
|
|
697
|
-
try {
|
|
698
|
-
generateRouteTreeFile();
|
|
699
|
-
} catch (error) {
|
|
700
|
-
console.error("\x1B[31m[olova]\x1B[0m Error generating route tree:", error);
|
|
701
|
-
}
|
|
702
|
-
}, 100);
|
|
703
|
-
}
|
|
704
|
-
});
|
|
705
|
-
console.log("\x1B[32m[olova]\x1B[0m Watching for route changes...");
|
|
706
|
-
}
|
|
707
|
-
const routerPlugin = {
|
|
708
|
-
name: "olova-router",
|
|
709
|
-
configResolved(resolvedConfig) {
|
|
710
|
-
config = resolvedConfig;
|
|
711
|
-
absoluteRootDir = path4.resolve(config.root, rootDir);
|
|
712
|
-
},
|
|
713
|
-
buildStart() {
|
|
714
|
-
generateRouteTreeFile();
|
|
715
|
-
if (config.command === "serve") {
|
|
716
|
-
startWatcher();
|
|
717
|
-
}
|
|
718
|
-
},
|
|
719
|
-
buildEnd() {
|
|
720
|
-
if (watcher) {
|
|
721
|
-
watcher.close();
|
|
722
|
-
watcher = null;
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
};
|
|
726
|
-
return [
|
|
727
|
-
{ enforce: "pre", ...mdx() },
|
|
728
|
-
routerPlugin
|
|
729
|
-
];
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
// src/hydration/flight.ts
|
|
733
|
-
function safeStringify(data) {
|
|
734
|
-
return JSON.stringify(data).replace(/[<>\/\u2028\u2029]/g, (char) => {
|
|
735
|
-
switch (char) {
|
|
736
|
-
case "<":
|
|
737
|
-
return "\\u003c";
|
|
738
|
-
case ">":
|
|
739
|
-
return "\\u003e";
|
|
740
|
-
case "/":
|
|
741
|
-
return "\\u002f";
|
|
742
|
-
case "\u2028":
|
|
743
|
-
return "\\u2028";
|
|
744
|
-
case "\u2029":
|
|
745
|
-
return "\\u2029";
|
|
746
|
-
default:
|
|
747
|
-
return char;
|
|
748
|
-
}
|
|
749
|
-
});
|
|
750
|
-
}
|
|
751
|
-
function generatePageName(route) {
|
|
752
|
-
if (route === "/") return "HomePage";
|
|
753
|
-
return route.slice(1).split("/").filter(Boolean).map((s) => s.charAt(0).toUpperCase() + s.slice(1).replace(/[^a-zA-Z0-9]/g, "")).join("") + "Page";
|
|
754
|
-
}
|
|
755
|
-
function generatePattern(route) {
|
|
756
|
-
if (route === "/") return "/";
|
|
757
|
-
return route.replace(/\[\.\.\.([^\]]+)\]/g, "*").replace(/\[([^\]]+)\]/g, ":$1");
|
|
758
|
-
}
|
|
759
|
-
function generateFlightRuntime() {
|
|
760
|
-
return `<script>
|
|
761
|
-
(function(){
|
|
762
|
-
var f=self.__olova_f||[];
|
|
763
|
-
var p={R:'$route',M:'$meta',T:'$tree',D:'$schema',A:'$assets',H:'$hints',S:'$state',L:'$loader',Q:'$query'};
|
|
764
|
-
var g=self.$OLOVA={};
|
|
765
|
-
function h(c){var t=c[1],d=c[2];if(t==='E')return;var k=p[t];if(k){if(t==='L'||t==='Q'){g[k]={data:d,timestamp:c[3]||Date.now()}}else{g[k]=d}}}
|
|
766
|
-
for(var i=0;i<f.length;i++)h(f[i]);
|
|
767
|
-
self.__olova_f={push:h,length:0};
|
|
768
|
-
})();
|
|
769
|
-
</script>`;
|
|
770
|
-
}
|
|
771
|
-
function generateOlovaHydration(data, buildId) {
|
|
772
|
-
const meta = data.metadata || {};
|
|
773
|
-
const chunks = [];
|
|
774
|
-
const routeData = {
|
|
775
|
-
path: data.route,
|
|
776
|
-
params: data.params || {},
|
|
777
|
-
pattern: generatePattern(data.route),
|
|
778
|
-
isStatic: data.isStatic ?? true,
|
|
779
|
-
buildId
|
|
780
|
-
};
|
|
781
|
-
chunks.push(`<script>(self.__olova_f=self.__olova_f||[]).push([0,"R",${safeStringify(routeData)}])</script>`);
|
|
782
|
-
const metadataData = {
|
|
783
|
-
title: meta.title || "Olova App",
|
|
784
|
-
description: meta.description || "",
|
|
785
|
-
keywords: Array.isArray(meta.keywords) ? meta.keywords : [],
|
|
786
|
-
robots: meta.robots || "index, follow",
|
|
787
|
-
canonical: meta.canonical || null,
|
|
788
|
-
og: {
|
|
789
|
-
type: "website",
|
|
790
|
-
locale: "en_US",
|
|
791
|
-
...meta.openGraph || {}
|
|
792
|
-
},
|
|
793
|
-
twitter: {
|
|
794
|
-
card: "summary_large_image",
|
|
795
|
-
...meta.twitter || {}
|
|
796
|
-
}
|
|
797
|
-
};
|
|
798
|
-
chunks.push(`<script>(self.__olova_f).push([1,"M",${safeStringify(metadataData)}])</script>`);
|
|
799
|
-
const treeData = {
|
|
800
|
-
layout: "RootLayout",
|
|
801
|
-
page: generatePageName(data.route),
|
|
802
|
-
template: null,
|
|
803
|
-
loading: null,
|
|
804
|
-
error: null,
|
|
805
|
-
notFound: null
|
|
806
|
-
};
|
|
807
|
-
chunks.push(`<script>(self.__olova_f).push([2,"T",${safeStringify(treeData)}])</script>`);
|
|
808
|
-
const assetsData = {
|
|
809
|
-
chunks: data.chunks || [],
|
|
810
|
-
styles: [],
|
|
811
|
-
prefetch: (data.chunks || []).slice(0, 5)
|
|
812
|
-
};
|
|
813
|
-
chunks.push(`<script>(self.__olova_f).push([3,"A",${safeStringify(assetsData)}])</script>`);
|
|
814
|
-
const hintsData = {
|
|
815
|
-
dnsPrefetch: ["fonts.googleapis.com", "fonts.gstatic.com"],
|
|
816
|
-
preconnect: ["https://fonts.googleapis.com", "https://fonts.gstatic.com"],
|
|
817
|
-
modulePreload: (data.chunks || []).slice(0, 3)
|
|
818
|
-
};
|
|
819
|
-
chunks.push(`<script>(self.__olova_f).push([4,"H",${safeStringify(hintsData)}])</script>`);
|
|
820
|
-
const stateData = {
|
|
821
|
-
hydrated: false,
|
|
822
|
-
streaming: false,
|
|
823
|
-
ready: true,
|
|
824
|
-
timestamp: Date.now(),
|
|
825
|
-
version: "1.0.0",
|
|
826
|
-
buildId
|
|
827
|
-
};
|
|
828
|
-
chunks.push(`<script>(self.__olova_f).push([5,"S",${safeStringify(stateData)}])</script>`);
|
|
829
|
-
if (data.loaderData) {
|
|
830
|
-
chunks.push(`<script>(self.__olova_f).push([6,"L",${safeStringify(data.loaderData)},${Date.now()}])</script>`);
|
|
831
|
-
}
|
|
832
|
-
if (data.queryState) {
|
|
833
|
-
chunks.push(`<script>(self.__olova_f).push([7,"Q",${safeStringify(data.queryState)},${Date.now()}])</script>`);
|
|
834
|
-
}
|
|
835
|
-
chunks.push(`<script>(self.__olova_f).push([8,"E",null])</script>`);
|
|
836
|
-
chunks.push(generateFlightRuntime());
|
|
837
|
-
return chunks.join("");
|
|
838
|
-
}
|
|
839
|
-
function generateJsonLd(data) {
|
|
840
|
-
const meta = data.metadata || {};
|
|
841
|
-
const jsonLd = {
|
|
842
|
-
"@context": "https://schema.org",
|
|
843
|
-
"@graph": [
|
|
844
|
-
{
|
|
845
|
-
"@type": "WebPage",
|
|
846
|
-
"@id": data.route,
|
|
847
|
-
name: meta.title || "Olova App",
|
|
848
|
-
description: meta.description || "",
|
|
849
|
-
url: data.route,
|
|
850
|
-
isPartOf: {
|
|
851
|
-
"@type": "WebSite",
|
|
852
|
-
name: "Olova App"
|
|
853
|
-
}
|
|
854
|
-
},
|
|
855
|
-
{
|
|
856
|
-
"@type": "BreadcrumbList",
|
|
857
|
-
itemListElement: data.route.split("/").filter(Boolean).map((segment, index, arr) => ({
|
|
858
|
-
"@type": "ListItem",
|
|
859
|
-
position: index + 1,
|
|
860
|
-
name: segment.charAt(0).toUpperCase() + segment.slice(1),
|
|
861
|
-
item: "/" + arr.slice(0, index + 1).join("/")
|
|
862
|
-
}))
|
|
863
|
-
}
|
|
864
|
-
]
|
|
865
|
-
};
|
|
866
|
-
if (meta.schema) {
|
|
867
|
-
const graph = jsonLd["@graph"];
|
|
868
|
-
if (Array.isArray(meta.schema)) {
|
|
869
|
-
graph.push(...meta.schema);
|
|
870
|
-
} else {
|
|
871
|
-
graph.push(meta.schema);
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
return `<script type="application/ld+json">${JSON.stringify(jsonLd)}</script>`;
|
|
875
|
-
}
|
|
876
|
-
function generateResourceHints(data) {
|
|
877
|
-
const hints = [];
|
|
878
|
-
hints.push(`<link rel="dns-prefetch" href="//fonts.googleapis.com">`);
|
|
879
|
-
hints.push(`<link rel="dns-prefetch" href="//fonts.gstatic.com">`);
|
|
880
|
-
hints.push(`<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin>`);
|
|
881
|
-
hints.push(`<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>`);
|
|
882
|
-
if (data.chunks) {
|
|
883
|
-
for (const chunk of data.chunks.slice(0, 3)) {
|
|
884
|
-
hints.push(`<link rel="modulepreload" href="/${chunk}">`);
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
return hints.join("");
|
|
888
|
-
}
|
|
889
|
-
function generateBuildId() {
|
|
890
|
-
const timestamp = Date.now().toString(36);
|
|
891
|
-
const random = Math.random().toString(36).substring(2, 8);
|
|
892
|
-
return `${timestamp}-${random}`;
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
// olova.ts
|
|
896
|
-
function olova(options = {}) {
|
|
897
|
-
const virtualModuleId = "virtual:olova-entry";
|
|
898
|
-
const serverVirtualModuleId = "virtual:olova-server-entry";
|
|
899
|
-
const workerVirtualModuleId = "virtual:olova-worker-entry";
|
|
900
|
-
const appVirtualModuleId = "virtual:olova-app";
|
|
901
|
-
const resolvedAppVirtualModuleId = "\0" + appVirtualModuleId;
|
|
902
|
-
let config;
|
|
903
|
-
const htmlContent = `<!--app-html-->`;
|
|
904
|
-
const minifyHtml = (html) => {
|
|
905
|
-
return html.replace(/>\s+</g, "><").replace(/\s{2,}/g, " ").replace(/<!--[\s\S]*?-->/g, "").trim();
|
|
906
|
-
};
|
|
907
|
-
return [
|
|
908
|
-
olovaRouter({ packageName: options.packageName }),
|
|
909
|
-
{
|
|
910
|
-
name: "vite-plugin-olova",
|
|
911
|
-
config(userConfig, { isSsrBuild }) {
|
|
912
|
-
const perfEnabled = options.performance?.enabled !== false;
|
|
913
|
-
if (isSsrBuild) {
|
|
914
|
-
return {
|
|
915
|
-
build: {
|
|
916
|
-
rollupOptions: {
|
|
917
|
-
input: userConfig.build?.rollupOptions?.input || virtualModuleId,
|
|
918
|
-
output: {
|
|
919
|
-
// Simple output for SSR
|
|
920
|
-
entryFileNames: "index.js",
|
|
921
|
-
chunkFileNames: "[name].js",
|
|
922
|
-
assetFileNames: "[name].[ext]",
|
|
923
|
-
format: "esm",
|
|
924
|
-
manualChunks: void 0
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
};
|
|
929
|
-
}
|
|
930
|
-
const buildConfig = {
|
|
931
|
-
// Output directory for assets
|
|
932
|
-
assetsDir: "assets/_olova",
|
|
933
|
-
// Report compressed size in build output
|
|
934
|
-
reportCompressedSize: true,
|
|
935
|
-
rollupOptions: {
|
|
936
|
-
input: userConfig.build?.rollupOptions?.input || virtualModuleId,
|
|
937
|
-
output: {
|
|
938
|
-
// Optimized chunk naming for caching
|
|
939
|
-
chunkFileNames: "assets/_olova/[name]-[hash].js",
|
|
940
|
-
entryFileNames: "assets/_olova/[name]-[hash].js",
|
|
941
|
-
assetFileNames: "assets/_olova/[name]-[hash].[ext]",
|
|
942
|
-
// Enable smart code splitting
|
|
943
|
-
...perfEnabled && {
|
|
944
|
-
manualChunks: createManualChunks()
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
},
|
|
948
|
-
// Increase warning limit since we're doing smart chunking
|
|
949
|
-
chunkSizeWarningLimit: 500,
|
|
950
|
-
// Inline small assets (< 4KB)
|
|
951
|
-
assetsInlineLimit: 4096,
|
|
952
|
-
// Enable terser minification for smaller bundles
|
|
953
|
-
minify: "terser",
|
|
954
|
-
terserOptions: {
|
|
955
|
-
compress: {
|
|
956
|
-
// Aggressive optimizations for production
|
|
957
|
-
drop_console: false,
|
|
958
|
-
// Keep console for debugging
|
|
959
|
-
drop_debugger: true,
|
|
960
|
-
pure_funcs: ["console.debug"],
|
|
961
|
-
passes: 2
|
|
962
|
-
},
|
|
963
|
-
mangle: {
|
|
964
|
-
properties: false
|
|
965
|
-
},
|
|
966
|
-
format: {
|
|
967
|
-
comments: false
|
|
968
|
-
}
|
|
969
|
-
},
|
|
970
|
-
// Disable source maps for smaller builds
|
|
971
|
-
sourcemap: false,
|
|
972
|
-
// CSS code splitting
|
|
973
|
-
cssCodeSplit: true,
|
|
974
|
-
// Target modern browsers (smaller bundles)
|
|
975
|
-
target: "es2020"
|
|
976
|
-
};
|
|
977
|
-
return {
|
|
978
|
-
build: buildConfig,
|
|
979
|
-
// Optimize deps for faster dev startup
|
|
980
|
-
optimizeDeps: {
|
|
981
|
-
include: ["react", "react-dom"],
|
|
982
|
-
exclude: ["olova"]
|
|
983
|
-
},
|
|
984
|
-
// SSR options
|
|
985
|
-
ssr: {
|
|
986
|
-
noExternal: ["olova"]
|
|
987
|
-
},
|
|
988
|
-
// esbuild optimizations
|
|
989
|
-
esbuild: {
|
|
990
|
-
treeShaking: true,
|
|
991
|
-
legalComments: "none"
|
|
992
|
-
},
|
|
993
|
-
// Preview server configuration (for testing production builds)
|
|
994
|
-
preview: {
|
|
995
|
-
headers: {
|
|
996
|
-
// Long-term caching for static assets
|
|
997
|
-
"Cache-Control": "public, max-age=31536000"
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1000
|
-
};
|
|
1001
|
-
},
|
|
1002
|
-
async configResolved(resolvedConfig) {
|
|
1003
|
-
config = resolvedConfig;
|
|
1004
|
-
},
|
|
1005
|
-
async resolveId(id) {
|
|
1006
|
-
if (id === virtualModuleId || id === serverVirtualModuleId || id === workerVirtualModuleId) {
|
|
1007
|
-
const isServer = id === serverVirtualModuleId;
|
|
1008
|
-
const isWorker = id === workerVirtualModuleId;
|
|
1009
|
-
const fileName = isWorker ? "entry-worker" : isServer ? "entry-server" : "main";
|
|
1010
|
-
const possibleLocalPaths = [
|
|
1011
|
-
path4.resolve(config.root, `src/${fileName}.tsx`),
|
|
1012
|
-
path4.resolve(config.root, `src/${fileName}.ts`),
|
|
1013
|
-
path4.resolve(config.root, `${fileName}.tsx`),
|
|
1014
|
-
path4.resolve(config.root, `plugins/${fileName}.tsx`)
|
|
1015
|
-
];
|
|
1016
|
-
for (const p of possibleLocalPaths) {
|
|
1017
|
-
if (existsSync(p)) return p;
|
|
1018
|
-
}
|
|
1019
|
-
const exportName = isWorker ? "olova/entry-worker" : isServer ? "olova/entry-server" : "olova/main";
|
|
1020
|
-
try {
|
|
1021
|
-
const resolved = await this.resolve(exportName, void 0, { skipSelf: true });
|
|
1022
|
-
if (resolved && !resolved.external) {
|
|
1023
|
-
return resolved.id;
|
|
1024
|
-
}
|
|
1025
|
-
} catch (e) {
|
|
1026
|
-
}
|
|
1027
|
-
const pkgDir = path4.dirname(fileURLToPath(import.meta.url));
|
|
1028
|
-
const possiblePkgPaths = [
|
|
1029
|
-
path4.resolve(pkgDir, `dist/${fileName}.js`),
|
|
1030
|
-
path4.resolve(pkgDir, `${fileName}.js`),
|
|
1031
|
-
path4.resolve(pkgDir, `${fileName}.mjs`),
|
|
1032
|
-
path4.resolve(pkgDir, `${fileName}.ts`),
|
|
1033
|
-
path4.resolve(pkgDir, `${fileName}.tsx`),
|
|
1034
|
-
path4.resolve(pkgDir, `src/${fileName}.tsx`),
|
|
1035
|
-
path4.resolve(pkgDir, `src/${fileName}.ts`),
|
|
1036
|
-
// If running from dist, check parent directories
|
|
1037
|
-
path4.resolve(pkgDir, "..", `dist/${fileName}.js`),
|
|
1038
|
-
path4.resolve(pkgDir, "..", `${fileName}.js`),
|
|
1039
|
-
path4.resolve(pkgDir, "..", `${fileName}.tsx`),
|
|
1040
|
-
path4.resolve(pkgDir, "..", `src/${fileName}.tsx`)
|
|
1041
|
-
];
|
|
1042
|
-
for (const p of possiblePkgPaths) {
|
|
1043
|
-
if (existsSync(p)) return p;
|
|
1044
|
-
}
|
|
1045
|
-
}
|
|
1046
|
-
if (id === appVirtualModuleId) {
|
|
1047
|
-
return resolvedAppVirtualModuleId;
|
|
1048
|
-
}
|
|
1049
|
-
return null;
|
|
1050
|
-
},
|
|
1051
|
-
load(id) {
|
|
1052
|
-
if (id === resolvedAppVirtualModuleId) {
|
|
1053
|
-
return `
|
|
1054
|
-
export { OlovaRouter, Outlet, routes, layouts, notFoundPages } from '@/route.tree';
|
|
1055
|
-
import '@/index.css';
|
|
1056
|
-
`;
|
|
1057
|
-
}
|
|
1058
|
-
return null;
|
|
1059
|
-
},
|
|
1060
|
-
configureServer(server) {
|
|
1061
|
-
const devBuildId = generateBuildId();
|
|
1062
|
-
logger_default.printBanner();
|
|
1063
|
-
server.middlewares.use(async (req, res, next) => {
|
|
1064
|
-
const url = req.url?.split("?")[0];
|
|
1065
|
-
if (url === "/" || url === "/index.html" || req.headers.accept?.includes("text/html") && !url?.match(/\.[a-z]+$/)) {
|
|
1066
|
-
try {
|
|
1067
|
-
logger_default.printSSRRender(url || "/");
|
|
1068
|
-
let template = htmlContent;
|
|
1069
|
-
const { render } = await server.ssrLoadModule(
|
|
1070
|
-
serverVirtualModuleId
|
|
1071
|
-
);
|
|
1072
|
-
const renderResult = await render(url || "/");
|
|
1073
|
-
const appHtml = typeof renderResult === "string" ? renderResult : renderResult.html;
|
|
1074
|
-
const routeMetadata = typeof renderResult === "string" ? {} : renderResult.metadata || {};
|
|
1075
|
-
const loaderData = typeof renderResult === "string" ? void 0 : renderResult.loaderData;
|
|
1076
|
-
const queryState = typeof renderResult === "string" ? void 0 : renderResult.queryState;
|
|
1077
|
-
let fullHtml = template.replace("<!--app-html-->", appHtml);
|
|
1078
|
-
const hydrationData = {
|
|
1079
|
-
route: url || "/",
|
|
1080
|
-
params: {},
|
|
1081
|
-
metadata: routeMetadata,
|
|
1082
|
-
chunks: [],
|
|
1083
|
-
// Dev mode doesn't have pre-built chunks
|
|
1084
|
-
isStatic: false,
|
|
1085
|
-
loaderData,
|
|
1086
|
-
queryState
|
|
1087
|
-
};
|
|
1088
|
-
const flightScripts = generateOlovaHydration(hydrationData, devBuildId);
|
|
1089
|
-
const jsonLdScript = generateJsonLd(hydrationData);
|
|
1090
|
-
const resourceHints = `<link rel="dns-prefetch" href="//fonts.googleapis.com"><link rel="preconnect" href="https://fonts.googleapis.com" crossorigin>`;
|
|
1091
|
-
if (fullHtml.includes("</head>")) {
|
|
1092
|
-
fullHtml = fullHtml.replace("</head>", `${jsonLdScript}${resourceHints}</head>`);
|
|
1093
|
-
}
|
|
1094
|
-
if (fullHtml.includes("</body>")) {
|
|
1095
|
-
fullHtml = fullHtml.replace(
|
|
1096
|
-
"</body>",
|
|
1097
|
-
`${flightScripts}<script type="module" src="/@id/${virtualModuleId}"></script></body>`
|
|
1098
|
-
);
|
|
1099
|
-
} else {
|
|
1100
|
-
fullHtml += `${flightScripts}<script type="module" src="/@id/${virtualModuleId}"></script>`;
|
|
1101
|
-
}
|
|
1102
|
-
fullHtml = await server.transformIndexHtml(url || "/", fullHtml);
|
|
1103
|
-
if (!fullHtml.trim().toLowerCase().startsWith("<!doctype html>")) {
|
|
1104
|
-
fullHtml = `<!DOCTYPE html>${fullHtml}`;
|
|
1105
|
-
}
|
|
1106
|
-
res.statusCode = 200;
|
|
1107
|
-
res.setHeader("Content-Type", "text/html");
|
|
1108
|
-
res.end(minifyHtml(fullHtml));
|
|
1109
|
-
return;
|
|
1110
|
-
} catch (e) {
|
|
1111
|
-
server.ssrFixStacktrace(e);
|
|
1112
|
-
console.error(e);
|
|
1113
|
-
res.statusCode = 500;
|
|
1114
|
-
res.end(e.stack);
|
|
1115
|
-
return;
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
next();
|
|
1119
|
-
});
|
|
1120
|
-
},
|
|
1121
|
-
async writeBundle(_options, bundle) {
|
|
1122
|
-
if (config.command === "serve" || config.build.ssr) return;
|
|
1123
|
-
const buildStartTime = Date.now();
|
|
1124
|
-
logger_default.printBuildStart();
|
|
1125
|
-
const outDir = config.build.outDir;
|
|
1126
|
-
const serverOutDir = path4.resolve(config.root, "dist/server");
|
|
1127
|
-
const clientEntry = Object.values(bundle).find(
|
|
1128
|
-
(chunk) => chunk.type === "chunk" && chunk.isEntry && (chunk.facadeModuleId?.includes("main") || chunk.name === "main")
|
|
1129
|
-
);
|
|
1130
|
-
const clientEntryFileName = clientEntry ? clientEntry.fileName : "assets/index.js";
|
|
1131
|
-
const cssAssets = Object.values(bundle).filter(
|
|
1132
|
-
(chunk) => chunk.type === "asset" && chunk.fileName.endsWith(".css")
|
|
1133
|
-
);
|
|
1134
|
-
const cssLinks = cssAssets.map(
|
|
1135
|
-
(css) => `<link rel="stylesheet" crossorigin href="/${css.fileName}">`
|
|
1136
|
-
).join("");
|
|
1137
|
-
await build({
|
|
1138
|
-
configFile: config.configFile,
|
|
1139
|
-
root: config.root,
|
|
1140
|
-
build: {
|
|
1141
|
-
ssr: true,
|
|
1142
|
-
emptyOutDir: false,
|
|
1143
|
-
outDir: serverOutDir,
|
|
1144
|
-
minify: false,
|
|
1145
|
-
// Keep readable for debugging
|
|
1146
|
-
rollupOptions: {
|
|
1147
|
-
input: { index: serverVirtualModuleId },
|
|
1148
|
-
output: {
|
|
1149
|
-
entryFileNames: "index.js",
|
|
1150
|
-
chunkFileNames: "[name].js",
|
|
1151
|
-
assetFileNames: "[name].[ext]",
|
|
1152
|
-
format: "esm",
|
|
1153
|
-
// Explicitly set to undefined to prevent client-side chunking config
|
|
1154
|
-
manualChunks: void 0
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
},
|
|
1158
|
-
logLevel: "error"
|
|
1159
|
-
});
|
|
1160
|
-
const serverEntryPath = path4.resolve(serverOutDir, "index.js");
|
|
1161
|
-
const serverEntryUrl = pathToFileURL(serverEntryPath).toString();
|
|
1162
|
-
const { routes } = await import(serverEntryUrl);
|
|
1163
|
-
const paths = ["/"];
|
|
1164
|
-
function extractPaths(routesArr) {
|
|
1165
|
-
routesArr.forEach((r) => {
|
|
1166
|
-
if (r.path && !r.path.includes("*") && !r.path.includes(":")) {
|
|
1167
|
-
paths.push(r.path);
|
|
1168
|
-
}
|
|
1169
|
-
});
|
|
1170
|
-
}
|
|
1171
|
-
if (Array.isArray(routes)) extractPaths(routes);
|
|
1172
|
-
if (Array.isArray(routes)) {
|
|
1173
|
-
for (const r of routes) {
|
|
1174
|
-
const isDynamic = r.path.includes(":") || r.path.includes("*");
|
|
1175
|
-
if (r.getStaticPaths && isDynamic) {
|
|
1176
|
-
try {
|
|
1177
|
-
const staticPaths = await r.getStaticPaths();
|
|
1178
|
-
if (Array.isArray(staticPaths)) {
|
|
1179
|
-
for (const entry of staticPaths) {
|
|
1180
|
-
let expandedPath = r.path;
|
|
1181
|
-
if (entry.params) {
|
|
1182
|
-
for (const [key, value] of Object.entries(entry.params)) {
|
|
1183
|
-
expandedPath = expandedPath.replace(`:${key}`, value);
|
|
1184
|
-
expandedPath = expandedPath.replace("*", value);
|
|
1185
|
-
}
|
|
1186
|
-
}
|
|
1187
|
-
paths.push(expandedPath);
|
|
1188
|
-
}
|
|
1189
|
-
logger_default.info(`getStaticPaths for ${r.path}: ${staticPaths.length} paths expanded`);
|
|
1190
|
-
}
|
|
1191
|
-
} catch (e) {
|
|
1192
|
-
logger_default.warn(`getStaticPaths() failed for ${r.path}: ${e.message}`);
|
|
1193
|
-
}
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1196
|
-
}
|
|
1197
|
-
if (options.staticPaths) {
|
|
1198
|
-
options.staticPaths.forEach((p) => paths.push(p));
|
|
1199
|
-
}
|
|
1200
|
-
const uniquePaths = [...new Set(paths)];
|
|
1201
|
-
const buildId = generateBuildId();
|
|
1202
|
-
logger_default.printSSGStart(buildId);
|
|
1203
|
-
const routeInfo = uniquePaths.map((p) => ({
|
|
1204
|
-
path: p,
|
|
1205
|
-
type: p.includes(":") || p.includes("*") ? "dynamic" : "static"
|
|
1206
|
-
}));
|
|
1207
|
-
logger_default.printRoutes(routeInfo);
|
|
1208
|
-
logger_default.printFlightInfo();
|
|
1209
|
-
const jsChunks = Object.values(bundle).filter((chunk) => chunk.type === "chunk" && chunk.fileName.endsWith(".js")).map((chunk) => chunk.fileName);
|
|
1210
|
-
let successCount = 0;
|
|
1211
|
-
let failCount = 0;
|
|
1212
|
-
for (const p of uniquePaths) {
|
|
1213
|
-
try {
|
|
1214
|
-
if (typeof globalThis.window !== "undefined") {
|
|
1215
|
-
globalThis.window.location.pathname = p;
|
|
1216
|
-
}
|
|
1217
|
-
const cacheBuster = `?t=${Date.now()}-${Math.random()}`;
|
|
1218
|
-
const { render } = await import(serverEntryUrl + cacheBuster);
|
|
1219
|
-
const renderResult = await render(p);
|
|
1220
|
-
const appHtml = typeof renderResult === "string" ? renderResult : renderResult.html;
|
|
1221
|
-
const routeMetadata = typeof renderResult === "string" ? {} : renderResult.metadata || {};
|
|
1222
|
-
const loaderData = typeof renderResult === "string" ? void 0 : renderResult.loaderData;
|
|
1223
|
-
const queryState = typeof renderResult === "string" ? void 0 : renderResult.queryState;
|
|
1224
|
-
let html = htmlContent.replace("<!--app-html-->", appHtml);
|
|
1225
|
-
const preloadHints = generatePreloadHints(jsChunks, clientEntryFileName);
|
|
1226
|
-
const preloadTags = generatePreloadTags(preloadHints);
|
|
1227
|
-
const hydrationData = {
|
|
1228
|
-
route: p,
|
|
1229
|
-
params: {},
|
|
1230
|
-
metadata: routeMetadata,
|
|
1231
|
-
chunks: jsChunks,
|
|
1232
|
-
isStatic: true,
|
|
1233
|
-
loaderData,
|
|
1234
|
-
queryState
|
|
1235
|
-
};
|
|
1236
|
-
const flightScripts = generateOlovaHydration(hydrationData, buildId);
|
|
1237
|
-
const jsonLdScript = generateJsonLd(hydrationData);
|
|
1238
|
-
const resourceHints = generateResourceHints(hydrationData);
|
|
1239
|
-
if (html.includes("</head>")) {
|
|
1240
|
-
html = html.replace("</head>", `${jsonLdScript}${resourceHints}${cssLinks}${preloadTags}</head>`);
|
|
1241
|
-
}
|
|
1242
|
-
if (html.includes("</body>")) {
|
|
1243
|
-
html = html.replace(
|
|
1244
|
-
"</body>",
|
|
1245
|
-
`${flightScripts}<script type="module" src="/${clientEntryFileName}"></script></body>`
|
|
1246
|
-
);
|
|
1247
|
-
} else {
|
|
1248
|
-
html += `${flightScripts}<script type="module" src="/${clientEntryFileName}"></script>`;
|
|
1249
|
-
}
|
|
1250
|
-
if (!html.trim().toLowerCase().startsWith("<!doctype html>")) {
|
|
1251
|
-
html = `<!DOCTYPE html>${html}`;
|
|
1252
|
-
}
|
|
1253
|
-
const itemPath = p === "/" ? "index.html" : `${p.substring(1)}/index.html`;
|
|
1254
|
-
const finalPath = path4.resolve(outDir, itemPath);
|
|
1255
|
-
await fs4.mkdir(path4.dirname(finalPath), { recursive: true });
|
|
1256
|
-
await fs4.writeFile(finalPath, minifyHtml(html));
|
|
1257
|
-
logger_default.printPageGenerated(itemPath, true);
|
|
1258
|
-
successCount++;
|
|
1259
|
-
} catch (e) {
|
|
1260
|
-
logger_default.printPageError(p, e.message);
|
|
1261
|
-
failCount++;
|
|
1262
|
-
}
|
|
1263
|
-
}
|
|
1264
|
-
try {
|
|
1265
|
-
const notFoundPath = "/__olova_404__";
|
|
1266
|
-
if (typeof globalThis.window !== "undefined") {
|
|
1267
|
-
globalThis.window.location.pathname = notFoundPath;
|
|
1268
|
-
}
|
|
1269
|
-
const cacheBuster404 = `?t=${Date.now()}-${Math.random()}`;
|
|
1270
|
-
const { render: render404 } = await import(serverEntryUrl + cacheBuster404);
|
|
1271
|
-
const result404 = await render404(notFoundPath);
|
|
1272
|
-
const html404Content = typeof result404 === "string" ? result404 : result404.html;
|
|
1273
|
-
let html404 = htmlContent.replace("<!--app-html-->", html404Content);
|
|
1274
|
-
const preloadHints404 = generatePreloadHints(jsChunks, clientEntryFileName);
|
|
1275
|
-
const preloadTags404 = generatePreloadTags(preloadHints404);
|
|
1276
|
-
const hydrationData404 = {
|
|
1277
|
-
route: "/404",
|
|
1278
|
-
params: {},
|
|
1279
|
-
metadata: { title: "404 - Page Not Found" },
|
|
1280
|
-
chunks: jsChunks,
|
|
1281
|
-
isStatic: true
|
|
1282
|
-
};
|
|
1283
|
-
const flightScripts404 = generateOlovaHydration(hydrationData404, buildId);
|
|
1284
|
-
const jsonLd404 = generateJsonLd(hydrationData404);
|
|
1285
|
-
const resourceHints404 = generateResourceHints(hydrationData404);
|
|
1286
|
-
if (html404.includes("</head>")) {
|
|
1287
|
-
html404 = html404.replace("</head>", `${jsonLd404}${resourceHints404}${cssLinks}${preloadTags404}</head>`);
|
|
1288
|
-
}
|
|
1289
|
-
if (html404.includes("</body>")) {
|
|
1290
|
-
html404 = html404.replace("</body>", `${flightScripts404}<script type="module" src="/${clientEntryFileName}"></script></body>`);
|
|
1291
|
-
} else {
|
|
1292
|
-
html404 += `${flightScripts404}<script type="module" src="/${clientEntryFileName}"></script>`;
|
|
1293
|
-
}
|
|
1294
|
-
if (!html404.trim().toLowerCase().startsWith("<!doctype html>")) {
|
|
1295
|
-
html404 = `<!DOCTYPE html>${html404}`;
|
|
1296
|
-
}
|
|
1297
|
-
const notFoundFinalPath = path4.resolve(outDir, "404.html");
|
|
1298
|
-
await fs4.writeFile(notFoundFinalPath, minifyHtml(html404));
|
|
1299
|
-
logger_default.printPageGenerated("404.html", true);
|
|
1300
|
-
successCount++;
|
|
1301
|
-
} catch (e) {
|
|
1302
|
-
logger_default.warn(`Failed to generate 404.html: ${e.message}`);
|
|
1303
|
-
}
|
|
1304
|
-
await fs4.rm(serverOutDir, { recursive: true, force: true });
|
|
1305
|
-
const buildTime = Date.now() - buildStartTime;
|
|
1306
|
-
logger_default.printSSGComplete({
|
|
1307
|
-
totalPages: successCount,
|
|
1308
|
-
successPages: successCount,
|
|
1309
|
-
failedPages: failCount,
|
|
1310
|
-
buildTime
|
|
1311
|
-
});
|
|
1312
|
-
}
|
|
1313
|
-
},
|
|
1314
|
-
// Add performance optimization plugins
|
|
1315
|
-
...options.performance?.enabled !== false ? olovaPerformance(options.performance) : []
|
|
1316
|
-
];
|
|
1317
|
-
}
|
|
1318
|
-
|
|
1319
|
-
export { olova };
|
|
1320
|
-
//# sourceMappingURL=olova.js.map
|
|
1321
|
-
//# sourceMappingURL=olova.js.map
|