@pyreon/zero 0.12.3 → 0.12.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.js +314 -4047
- package/lib/index.js.map +1 -1
- package/lib/meta.js +22 -48
- package/lib/meta.js.map +1 -1
- package/lib/server.js +1534 -0
- package/lib/server.js.map +1 -0
- package/lib/types/index.d.ts +173 -1540
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/meta.d.ts +11 -46
- package/lib/types/meta.d.ts.map +1 -1
- package/lib/types/server.d.ts +455 -0
- package/lib/types/server.d.ts.map +1 -0
- package/package.json +15 -10
- package/src/index.ts +19 -182
- package/src/meta.tsx +37 -3
- package/src/server.ts +70 -0
- package/lib/fs-router-Dil4IKZR.js +0 -290
- package/lib/fs-router-Dil4IKZR.js.map +0 -1
package/src/index.ts
CHANGED
|
@@ -1,46 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
export {
|
|
16
|
-
filePathToUrlPath,
|
|
17
|
-
generateMiddlewareModule,
|
|
18
|
-
generateRouteModule,
|
|
19
|
-
parseFileRoutes,
|
|
20
|
-
scanRouteFiles,
|
|
21
|
-
} from './fs-router'
|
|
22
|
-
|
|
23
|
-
// ─── Config ──────────────────────────────────────────────────────────────────
|
|
24
|
-
|
|
25
|
-
export { defineConfig, resolveConfig } from "./config";
|
|
26
|
-
|
|
27
|
-
// ─── ISR ─────────────────────────────────────────────────────────────────────
|
|
28
|
-
|
|
29
|
-
export { createISRHandler } from "./isr";
|
|
30
|
-
|
|
31
|
-
// ─── Adapters ────────────────────────────────────────────────────────────────
|
|
32
|
-
|
|
33
|
-
export {
|
|
34
|
-
bunAdapter,
|
|
35
|
-
cloudflareAdapter,
|
|
36
|
-
netlifyAdapter,
|
|
37
|
-
nodeAdapter,
|
|
38
|
-
resolveAdapter,
|
|
39
|
-
staticAdapter,
|
|
40
|
-
vercelAdapter,
|
|
41
|
-
} from "./adapters";
|
|
42
|
-
|
|
43
|
-
// ─── Components ─────────────────────────────────────────────────────────────
|
|
1
|
+
/**
|
|
2
|
+
* @pyreon/zero — client-safe exports.
|
|
3
|
+
*
|
|
4
|
+
* This entry contains only browser-safe components and hooks.
|
|
5
|
+
* No node:fs, node:path, or other server-only imports.
|
|
6
|
+
*
|
|
7
|
+
* For server/build-time features, use subpath imports:
|
|
8
|
+
* import { faviconPlugin } from "@pyreon/zero/favicon"
|
|
9
|
+
* import { createServer } from "@pyreon/zero/server"
|
|
10
|
+
* import { defineConfig } from "@pyreon/zero/config"
|
|
11
|
+
* import { validateEnv } from "@pyreon/zero/env"
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// ─── Components (browser-safe) ──────────────────────────────────────────────
|
|
44
15
|
|
|
45
16
|
export type { ImageProps, ImageSource } from "./image";
|
|
46
17
|
export { Image } from "./image";
|
|
@@ -48,41 +19,10 @@ export type { LinkProps, LinkRenderProps, UseLinkReturn } from "./link";
|
|
|
48
19
|
export { createLink, Link, prefetchRoute, useLink } from "./link";
|
|
49
20
|
export type { ScriptProps, ScriptStrategy } from "./script";
|
|
50
21
|
export { Script } from "./script";
|
|
22
|
+
export type { MetaProps } from "./meta";
|
|
23
|
+
export { buildMetaTags, Meta } from "./meta";
|
|
51
24
|
|
|
52
|
-
// ───
|
|
53
|
-
|
|
54
|
-
export { render404Page } from "./not-found";
|
|
55
|
-
|
|
56
|
-
// ─── Middleware ──────────────────────────────────────────────────────────────
|
|
57
|
-
|
|
58
|
-
export type { CacheConfig, CacheRule } from "./cache";
|
|
59
|
-
export { cacheMiddleware, securityHeaders, varyEncoding } from "./cache";
|
|
60
|
-
export { compose, getContext } from "./middleware";
|
|
61
|
-
|
|
62
|
-
// ─── Font optimization ─────────────────────────────────────────────────────
|
|
63
|
-
|
|
64
|
-
export type {
|
|
65
|
-
FallbackMetrics,
|
|
66
|
-
FontConfig,
|
|
67
|
-
FontDisplay,
|
|
68
|
-
GoogleFontInput,
|
|
69
|
-
GoogleFontStatic,
|
|
70
|
-
GoogleFontVariable,
|
|
71
|
-
LocalFont,
|
|
72
|
-
} from "./font";
|
|
73
|
-
export { fontPlugin, fontVariables } from "./font";
|
|
74
|
-
|
|
75
|
-
// ─── Image processing ──────────────────────────────────────────────────────
|
|
76
|
-
|
|
77
|
-
export type {
|
|
78
|
-
FormatSource,
|
|
79
|
-
ImageFormat,
|
|
80
|
-
ImagePluginConfig,
|
|
81
|
-
ProcessedImage,
|
|
82
|
-
} from "./image-plugin";
|
|
83
|
-
export { imagePlugin } from "./image-plugin";
|
|
84
|
-
|
|
85
|
-
// ─── Theme ──────────────────────────────────────────────────────────────────
|
|
25
|
+
// ─── Theme (browser-safe) ───────────────────────────────────────────────────
|
|
86
26
|
|
|
87
27
|
export type { Theme } from "./theme";
|
|
88
28
|
export {
|
|
@@ -96,120 +36,17 @@ export {
|
|
|
96
36
|
toggleTheme,
|
|
97
37
|
} from "./theme";
|
|
98
38
|
|
|
99
|
-
// ───
|
|
100
|
-
|
|
101
|
-
export type {
|
|
102
|
-
ChangeFreq,
|
|
103
|
-
JsonLdType,
|
|
104
|
-
RobotsConfig,
|
|
105
|
-
RobotsRule,
|
|
106
|
-
SeoPluginConfig,
|
|
107
|
-
SitemapConfig,
|
|
108
|
-
SitemapEntry,
|
|
109
|
-
} from "./seo";
|
|
110
|
-
export {
|
|
111
|
-
generateRobots,
|
|
112
|
-
generateSitemap,
|
|
113
|
-
jsonLd,
|
|
114
|
-
seoMiddleware,
|
|
115
|
-
seoPlugin,
|
|
116
|
-
} from "./seo";
|
|
117
|
-
|
|
118
|
-
// ─── API routes ──────────────────────────────────────────────────────────────
|
|
119
|
-
|
|
120
|
-
export type {
|
|
121
|
-
ApiContext,
|
|
122
|
-
ApiHandler,
|
|
123
|
-
ApiRouteEntry,
|
|
124
|
-
ApiRouteModule,
|
|
125
|
-
HttpMethod,
|
|
126
|
-
} from "./api-routes";
|
|
127
|
-
export { createApiMiddleware, generateApiRouteModule } from "./api-routes";
|
|
128
|
-
|
|
129
|
-
// ─── CORS ────────────────────────────────────────────────────────────────────
|
|
130
|
-
|
|
131
|
-
export type { CorsConfig } from "./cors";
|
|
132
|
-
export { corsMiddleware } from "./cors";
|
|
133
|
-
|
|
134
|
-
// ─── Rate limiting ──────────────────────────────────────────────────────────
|
|
135
|
-
|
|
136
|
-
export type { RateLimitConfig } from "./rate-limit";
|
|
137
|
-
export { rateLimitMiddleware } from "./rate-limit";
|
|
138
|
-
|
|
139
|
-
// ─── Compression ────────────────────────────────────────────────────────────
|
|
140
|
-
|
|
141
|
-
export type { CompressionConfig } from "./compression";
|
|
142
|
-
export {
|
|
143
|
-
compressionMiddleware,
|
|
144
|
-
compressResponse,
|
|
145
|
-
isCompressible,
|
|
146
|
-
} from "./compression";
|
|
147
|
-
|
|
148
|
-
// ─── Actions ─────────────────────────────────────────────────────────────────
|
|
149
|
-
|
|
150
|
-
export type { Action, ActionContext, ActionHandler } from "./actions";
|
|
151
|
-
export { createActionMiddleware, defineAction } from "./actions";
|
|
152
|
-
|
|
153
|
-
// ─── Favicon ────────────────────────────────────────────────────────────────
|
|
154
|
-
|
|
155
|
-
export type { FaviconLocaleConfig, FaviconPluginConfig } from "./favicon";
|
|
156
|
-
export { faviconLinks, faviconPlugin } from "./favicon";
|
|
157
|
-
|
|
158
|
-
// ─── OG Image ───────────────────────────────────────────────────────────────
|
|
159
|
-
|
|
160
|
-
export type {
|
|
161
|
-
OgImageLayer,
|
|
162
|
-
OgImagePluginConfig,
|
|
163
|
-
OgImageTemplate,
|
|
164
|
-
} from "./og-image";
|
|
165
|
-
export { ogImagePath, ogImagePlugin } from "./og-image";
|
|
166
|
-
|
|
167
|
-
// ─── Meta ───────────────────────────────────────────────────────────────────
|
|
168
|
-
|
|
169
|
-
export type { MetaProps } from "./meta";
|
|
170
|
-
export { buildMetaTags, Meta } from "./meta";
|
|
171
|
-
|
|
172
|
-
// ─── I18n routing ───────────────────────────────────────────────────────────
|
|
39
|
+
// ─── I18n hooks (browser-safe) ──────────────────────────────────────────────
|
|
173
40
|
|
|
174
41
|
export type { I18nRoutingConfig, LocaleContext } from "./i18n-routing";
|
|
175
42
|
export {
|
|
176
43
|
buildLocalePath,
|
|
177
|
-
createLocaleContext,
|
|
178
|
-
detectLocaleFromHeader,
|
|
179
44
|
extractLocaleFromPath,
|
|
180
|
-
i18nRouting,
|
|
181
45
|
setLocale,
|
|
182
46
|
useLocale,
|
|
183
47
|
} from "./i18n-routing";
|
|
184
48
|
|
|
185
|
-
// ───
|
|
186
|
-
|
|
187
|
-
export type { CspConfig, CspDirectives } from "./csp";
|
|
188
|
-
export { buildCspHeader, cspMiddleware, useNonce } from "./csp";
|
|
189
|
-
|
|
190
|
-
// ─── Environment validation ─────────────────────────────────────────────────
|
|
191
|
-
|
|
192
|
-
export type { LogEntry, LoggerConfig } from "./logger";
|
|
193
|
-
export { loggerMiddleware } from "./logger";
|
|
194
|
-
|
|
195
|
-
// ─── Request logging ────────────────────────────────────────────────────────
|
|
196
|
-
|
|
197
|
-
export type { EnvValidator } from "./env";
|
|
198
|
-
export { bool, num, oneOf, publicEnv, schema, str, url, validateEnv } from "./env";
|
|
199
|
-
|
|
200
|
-
// ─── AI integration ─────────────────────────────────────────────────────────
|
|
201
|
-
|
|
202
|
-
export type { AiPluginConfig, InferJsonLdOptions } from "./ai";
|
|
203
|
-
export {
|
|
204
|
-
aiPlugin,
|
|
205
|
-
generateAiPluginManifest,
|
|
206
|
-
generateLlmsFullTxt,
|
|
207
|
-
generateLlmsTxt,
|
|
208
|
-
generateOpenApiSpec,
|
|
209
|
-
inferJsonLd,
|
|
210
|
-
} from "./ai";
|
|
211
|
-
|
|
212
|
-
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
49
|
+
// ─── Types (no runtime, safe everywhere) ────────────────────────────────────
|
|
213
50
|
|
|
214
51
|
export type {
|
|
215
52
|
Adapter,
|
package/src/meta.tsx
CHANGED
|
@@ -1,11 +1,45 @@
|
|
|
1
1
|
import type { VNodeChild } from '@pyreon/core'
|
|
2
2
|
import type { UseHeadInput } from '@pyreon/head'
|
|
3
3
|
import { useHead } from '@pyreon/head'
|
|
4
|
-
import type { FaviconPluginConfig } from './favicon'
|
|
5
|
-
import { faviconLinks } from './favicon'
|
|
6
4
|
import type { I18nRoutingConfig } from './i18n-routing'
|
|
7
5
|
import { extractLocaleFromPath } from './i18n-routing'
|
|
8
|
-
|
|
6
|
+
|
|
7
|
+
// ─── Inline helpers (no node:fs dependency) ─────────────────────────────────
|
|
8
|
+
// These are inlined to avoid importing from favicon.ts/og-image.ts which
|
|
9
|
+
// pull in node:fs at the top level — making Meta unsafe for client bundles.
|
|
10
|
+
|
|
11
|
+
/** Favicon plugin config shape (type-only). */
|
|
12
|
+
interface FaviconPluginConfig {
|
|
13
|
+
source: string
|
|
14
|
+
themeColor?: string
|
|
15
|
+
manifest?: boolean
|
|
16
|
+
locales?: Record<string, { source: string; darkSource?: string }>
|
|
17
|
+
[key: string]: unknown
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function faviconLinks(
|
|
21
|
+
locale: string | undefined,
|
|
22
|
+
config: FaviconPluginConfig,
|
|
23
|
+
): Array<{ rel: string; type?: string; sizes?: string; href: string }> {
|
|
24
|
+
const hasLocaleOverride = locale && config.locales?.[locale]
|
|
25
|
+
const prefix = hasLocaleOverride ? `/${locale}` : ''
|
|
26
|
+
const isSvg = (hasLocaleOverride ? config.locales![locale]!.source : config.source).endsWith('.svg')
|
|
27
|
+
const links: Array<{ rel: string; type?: string; sizes?: string; href: string }> = []
|
|
28
|
+
if (isSvg) links.push({ rel: 'icon', type: 'image/svg+xml', href: `${prefix}/favicon.svg` })
|
|
29
|
+
links.push(
|
|
30
|
+
{ rel: 'icon', type: 'image/png', sizes: '32x32', href: `${prefix}/favicon-32x32.png` },
|
|
31
|
+
{ rel: 'icon', type: 'image/png', sizes: '16x16', href: `${prefix}/favicon-16x16.png` },
|
|
32
|
+
{ rel: 'apple-touch-icon', sizes: '180x180', href: `${prefix}/apple-touch-icon.png` },
|
|
33
|
+
)
|
|
34
|
+
if (config.manifest !== false) links.push({ rel: 'manifest', href: `${prefix}/site.webmanifest` })
|
|
35
|
+
return links
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function ogImagePath(templateName: string, locale?: string, outDir = 'og', format: 'png' | 'jpeg' = 'png'): string {
|
|
39
|
+
const ext = format === 'jpeg' ? 'jpg' : 'png'
|
|
40
|
+
const suffix = locale ? `-${locale}` : ''
|
|
41
|
+
return `/${outDir}/${templateName}${suffix}.${ext}`
|
|
42
|
+
}
|
|
9
43
|
|
|
10
44
|
// ─── Meta component ────────────────────────────────────────────────────────
|
|
11
45
|
|
package/src/server.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pyreon/zero/server — server-only exports.
|
|
3
|
+
*
|
|
4
|
+
* Import from this subpath for SSR, middleware, adapters, and build tools.
|
|
5
|
+
* These modules use node:fs, node:path, etc. and must NOT be imported
|
|
6
|
+
* in client-side code.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { createServer, createApp } from "@pyreon/zero/server"
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// ─── Server entry ───────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
export type { CreateAppOptions } from "./app";
|
|
17
|
+
export { createApp } from "./app";
|
|
18
|
+
export type { CreateServerOptions } from "./entry-server";
|
|
19
|
+
export { createServer } from "./entry-server";
|
|
20
|
+
|
|
21
|
+
// ─── Config ─────────────────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
export { defineConfig, resolveConfig } from "./config";
|
|
24
|
+
|
|
25
|
+
// ─── File-system routing ────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
export type { GenerateRouteModuleOptions } from './fs-router'
|
|
28
|
+
export {
|
|
29
|
+
filePathToUrlPath,
|
|
30
|
+
generateMiddlewareModule,
|
|
31
|
+
generateRouteModule,
|
|
32
|
+
parseFileRoutes,
|
|
33
|
+
scanRouteFiles,
|
|
34
|
+
} from './fs-router'
|
|
35
|
+
|
|
36
|
+
// ─── ISR ────────────────────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
export { createISRHandler } from "./isr";
|
|
39
|
+
|
|
40
|
+
// ─── Adapters ───────────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
export {
|
|
43
|
+
bunAdapter,
|
|
44
|
+
cloudflareAdapter,
|
|
45
|
+
netlifyAdapter,
|
|
46
|
+
nodeAdapter,
|
|
47
|
+
resolveAdapter,
|
|
48
|
+
staticAdapter,
|
|
49
|
+
vercelAdapter,
|
|
50
|
+
} from "./adapters";
|
|
51
|
+
|
|
52
|
+
// ─── 404 ────────────────────────────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
export { render404Page } from "./not-found";
|
|
55
|
+
|
|
56
|
+
// ─── Middleware ──────────────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
export { compose, getContext } from "./middleware";
|
|
59
|
+
|
|
60
|
+
// ─── Vite plugin ────────────────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
export { zeroPlugin as default } from "./vite-plugin";
|
|
63
|
+
|
|
64
|
+
// ─── I18n server-only ───────────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
export {
|
|
67
|
+
createLocaleContext,
|
|
68
|
+
detectLocaleFromHeader,
|
|
69
|
+
i18nRouting,
|
|
70
|
+
} from "./i18n-routing";
|
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
//#region \0rolldown/runtime.js
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __exportAll = (all, no_symbols) => {
|
|
4
|
-
let target = {};
|
|
5
|
-
for (var name in all) {
|
|
6
|
-
__defProp(target, name, {
|
|
7
|
-
get: all[name],
|
|
8
|
-
enumerable: true
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
if (!no_symbols) {
|
|
12
|
-
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
13
|
-
}
|
|
14
|
-
return target;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
//#endregion
|
|
18
|
-
//#region src/fs-router.ts
|
|
19
|
-
var fs_router_exports = /* @__PURE__ */ __exportAll({
|
|
20
|
-
filePathToUrlPath: () => filePathToUrlPath,
|
|
21
|
-
generateMiddlewareModule: () => generateMiddlewareModule,
|
|
22
|
-
generateRouteModule: () => generateRouteModule,
|
|
23
|
-
parseFileRoutes: () => parseFileRoutes,
|
|
24
|
-
scanRouteFiles: () => scanRouteFiles
|
|
25
|
-
});
|
|
26
|
-
const ROUTE_EXTENSIONS = [
|
|
27
|
-
".tsx",
|
|
28
|
-
".jsx",
|
|
29
|
-
".ts",
|
|
30
|
-
".js"
|
|
31
|
-
];
|
|
32
|
-
/**
|
|
33
|
-
* Parse a set of file paths (relative to routes dir) into FileRoute objects.
|
|
34
|
-
*
|
|
35
|
-
* @param files Array of file paths like ["index.tsx", "users/[id].tsx"]
|
|
36
|
-
* @param defaultMode Default rendering mode from config
|
|
37
|
-
*/
|
|
38
|
-
function parseFileRoutes(files, defaultMode = "ssr") {
|
|
39
|
-
return files.filter((f) => ROUTE_EXTENSIONS.some((ext) => f.endsWith(ext))).map((filePath) => parseFilePath(filePath, defaultMode)).sort(sortRoutes);
|
|
40
|
-
}
|
|
41
|
-
function parseFilePath(filePath, defaultMode) {
|
|
42
|
-
let route = filePath;
|
|
43
|
-
for (const ext of ROUTE_EXTENSIONS) if (route.endsWith(ext)) {
|
|
44
|
-
route = route.slice(0, -ext.length);
|
|
45
|
-
break;
|
|
46
|
-
}
|
|
47
|
-
const fileName = getFileName(route);
|
|
48
|
-
const isLayout = fileName === "_layout";
|
|
49
|
-
const isError = fileName === "_error";
|
|
50
|
-
const isLoading = fileName === "_loading";
|
|
51
|
-
const isNotFound = fileName === "_404" || fileName === "_not-found";
|
|
52
|
-
const isCatchAll = route.includes("[...");
|
|
53
|
-
const parts = route.split("/");
|
|
54
|
-
parts.pop();
|
|
55
|
-
const dirPath = parts.filter((s) => !(s.startsWith("(") && s.endsWith(")"))).join("/");
|
|
56
|
-
const urlPath = filePathToUrlPath(route);
|
|
57
|
-
return {
|
|
58
|
-
filePath,
|
|
59
|
-
urlPath,
|
|
60
|
-
dirPath,
|
|
61
|
-
depth: urlPath === "/" ? 0 : urlPath.split("/").filter(Boolean).length,
|
|
62
|
-
isLayout,
|
|
63
|
-
isError,
|
|
64
|
-
isLoading,
|
|
65
|
-
isNotFound,
|
|
66
|
-
isCatchAll,
|
|
67
|
-
renderMode: defaultMode
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Convert a file path (without extension) to a URL path pattern.
|
|
72
|
-
*
|
|
73
|
-
* Examples:
|
|
74
|
-
* "index" → "/"
|
|
75
|
-
* "about" → "/about"
|
|
76
|
-
* "users/index" → "/users"
|
|
77
|
-
* "users/[id]" → "/users/:id"
|
|
78
|
-
* "blog/[...slug]" → "/blog/:slug*"
|
|
79
|
-
* "(auth)/login" → "/login" (group stripped)
|
|
80
|
-
* "_layout" → "/" (layout marker)
|
|
81
|
-
*/
|
|
82
|
-
function filePathToUrlPath(filePath) {
|
|
83
|
-
const segments = filePath.split("/");
|
|
84
|
-
const urlSegments = [];
|
|
85
|
-
for (const seg of segments) {
|
|
86
|
-
if (seg.startsWith("(") && seg.endsWith(")")) continue;
|
|
87
|
-
if (seg === "_layout" || seg === "_error" || seg === "_loading" || seg === "_404" || seg === "_not-found") continue;
|
|
88
|
-
if (seg === "index") continue;
|
|
89
|
-
const catchAll = seg.match(/^\[\.\.\.(\w+)\]$/);
|
|
90
|
-
if (catchAll) {
|
|
91
|
-
urlSegments.push(`:${catchAll[1]}*`);
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
const dynamic = seg.match(/^\[(\w+)\]$/);
|
|
95
|
-
if (dynamic) {
|
|
96
|
-
urlSegments.push(`:${dynamic[1]}`);
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
urlSegments.push(seg);
|
|
100
|
-
}
|
|
101
|
-
return `/${urlSegments.join("/")}` || "/";
|
|
102
|
-
}
|
|
103
|
-
/** Sort routes: static before dynamic, catch-all last. */
|
|
104
|
-
function sortRoutes(a, b) {
|
|
105
|
-
if (a.isCatchAll !== b.isCatchAll) return a.isCatchAll ? 1 : -1;
|
|
106
|
-
if (a.isLayout !== b.isLayout) return a.isLayout ? -1 : 1;
|
|
107
|
-
const aDynamic = a.urlPath.includes(":");
|
|
108
|
-
if (aDynamic !== b.urlPath.includes(":")) return aDynamic ? 1 : -1;
|
|
109
|
-
return a.urlPath.localeCompare(b.urlPath);
|
|
110
|
-
}
|
|
111
|
-
function getFileName(filePath) {
|
|
112
|
-
const parts = filePath.split("/");
|
|
113
|
-
return parts[parts.length - 1] ?? "";
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Group flat file routes into a directory tree.
|
|
117
|
-
*/
|
|
118
|
-
function getOrCreateChild(node, segment) {
|
|
119
|
-
let child = node.children.get(segment);
|
|
120
|
-
if (!child) {
|
|
121
|
-
child = {
|
|
122
|
-
pages: [],
|
|
123
|
-
children: /* @__PURE__ */ new Map()
|
|
124
|
-
};
|
|
125
|
-
node.children.set(segment, child);
|
|
126
|
-
}
|
|
127
|
-
return child;
|
|
128
|
-
}
|
|
129
|
-
function resolveNode(root, dirPath) {
|
|
130
|
-
let node = root;
|
|
131
|
-
if (dirPath) for (const segment of dirPath.split("/")) node = getOrCreateChild(node, segment);
|
|
132
|
-
return node;
|
|
133
|
-
}
|
|
134
|
-
function placeRoute(node, route) {
|
|
135
|
-
if (route.isLayout) node.layout = route;
|
|
136
|
-
else if (route.isError) node.error = route;
|
|
137
|
-
else if (route.isLoading) node.loading = route;
|
|
138
|
-
else if (route.isNotFound) node.notFound = route;
|
|
139
|
-
else node.pages.push(route);
|
|
140
|
-
}
|
|
141
|
-
function buildRouteTree(routes) {
|
|
142
|
-
const root = {
|
|
143
|
-
pages: [],
|
|
144
|
-
children: /* @__PURE__ */ new Map()
|
|
145
|
-
};
|
|
146
|
-
for (const route of routes) placeRoute(resolveNode(root, route.dirPath), route);
|
|
147
|
-
return root;
|
|
148
|
-
}
|
|
149
|
-
function generateRouteModule(files, routesDir, options) {
|
|
150
|
-
const tree = buildRouteTree(parseFileRoutes(files));
|
|
151
|
-
const imports = [];
|
|
152
|
-
let importCounter = 0;
|
|
153
|
-
const useStaticImports = options?.staticImports ?? false;
|
|
154
|
-
function nextImport(filePath, exportName = "default") {
|
|
155
|
-
const name = `_${importCounter++}`;
|
|
156
|
-
const fullPath = `${routesDir}/${filePath}`;
|
|
157
|
-
if (exportName === "default") imports.push(`import ${name} from "${fullPath}"`);
|
|
158
|
-
else imports.push(`import { ${exportName} as ${name} } from "${fullPath}"`);
|
|
159
|
-
return name;
|
|
160
|
-
}
|
|
161
|
-
function nextLazy(filePath, loadingName, errorName) {
|
|
162
|
-
const name = `_${importCounter++}`;
|
|
163
|
-
const fullPath = `${routesDir}/${filePath}`;
|
|
164
|
-
if (useStaticImports) imports.push(`import ${name} from "${fullPath}"`);
|
|
165
|
-
else {
|
|
166
|
-
const opts = [];
|
|
167
|
-
if (loadingName) opts.push(`loading: ${loadingName}`);
|
|
168
|
-
if (errorName) opts.push(`error: ${errorName}`);
|
|
169
|
-
const optsStr = opts.length > 0 ? `, { ${opts.join(", ")} }` : "";
|
|
170
|
-
imports.push(`const ${name} = lazy(() => import("${fullPath}")${optsStr})`);
|
|
171
|
-
}
|
|
172
|
-
return name;
|
|
173
|
-
}
|
|
174
|
-
function nextModuleImport(filePath) {
|
|
175
|
-
const name = `_m${importCounter++}`;
|
|
176
|
-
const fullPath = `${routesDir}/${filePath}`;
|
|
177
|
-
imports.push(`import * as ${name} from "${fullPath}"`);
|
|
178
|
-
return name;
|
|
179
|
-
}
|
|
180
|
-
function generatePageRoute(page, indent, loadingName, errorName, notFoundName) {
|
|
181
|
-
const mod = nextModuleImport(page.filePath);
|
|
182
|
-
const comp = nextLazy(page.filePath, loadingName, errorName);
|
|
183
|
-
const props = [
|
|
184
|
-
`${indent} path: ${JSON.stringify(page.urlPath)}`,
|
|
185
|
-
`${indent} component: ${comp}`,
|
|
186
|
-
`${indent} loader: ${mod}.loader`,
|
|
187
|
-
`${indent} beforeEnter: ${mod}.guard`,
|
|
188
|
-
`${indent} meta: { ...${mod}.meta, renderMode: ${mod}.renderMode }`
|
|
189
|
-
];
|
|
190
|
-
if (errorName) props.push(`${indent} errorComponent: ${mod}.error || ${errorName}`);
|
|
191
|
-
if (notFoundName) props.push(`${indent} notFoundComponent: ${notFoundName}`);
|
|
192
|
-
return `${indent}{\n${props.join(",\n")}\n${indent}}`;
|
|
193
|
-
}
|
|
194
|
-
function wrapWithLayout(node, children, indent, errorName, notFoundName) {
|
|
195
|
-
const layout = node.layout;
|
|
196
|
-
const layoutMod = nextModuleImport(layout.filePath);
|
|
197
|
-
const layoutComp = nextImport(layout.filePath, "layout");
|
|
198
|
-
const props = [
|
|
199
|
-
`${indent}path: ${JSON.stringify(layout.urlPath)}`,
|
|
200
|
-
`${indent}component: ${layoutComp}`,
|
|
201
|
-
`${indent}loader: ${layoutMod}.loader`,
|
|
202
|
-
`${indent}beforeEnter: ${layoutMod}.guard`,
|
|
203
|
-
`${indent}meta: { ...${layoutMod}.meta, renderMode: ${layoutMod}.renderMode }`
|
|
204
|
-
];
|
|
205
|
-
if (errorName) props.push(`${indent}errorComponent: ${errorName}`);
|
|
206
|
-
if (notFoundName) props.push(`${indent}notFoundComponent: ${notFoundName}`);
|
|
207
|
-
if (children.length > 0) props.push(`${indent}children: [\n${children.join(",\n")}\n${indent}]`);
|
|
208
|
-
return `${indent}{\n${props.map((p) => ` ${p}`).join(",\n")}\n${indent}}`;
|
|
209
|
-
}
|
|
210
|
-
/**
|
|
211
|
-
* Generate route definitions for a tree node.
|
|
212
|
-
*/
|
|
213
|
-
function generateNode(node, depth) {
|
|
214
|
-
const indent = " ".repeat(depth + 1);
|
|
215
|
-
const errorName = node.error ? nextImport(node.error.filePath) : void 0;
|
|
216
|
-
const loadingName = node.loading ? nextImport(node.loading.filePath) : void 0;
|
|
217
|
-
const notFoundName = node.notFound ? nextImport(node.notFound.filePath) : void 0;
|
|
218
|
-
const childRouteDefs = [];
|
|
219
|
-
for (const [, childNode] of node.children) childRouteDefs.push(...generateNode(childNode, depth + 1));
|
|
220
|
-
const allChildren = [...node.pages.map((page) => generatePageRoute(page, indent, loadingName, errorName, notFoundName)), ...childRouteDefs];
|
|
221
|
-
if (node.layout) return [wrapWithLayout(node, allChildren, indent, errorName, notFoundName)];
|
|
222
|
-
return allChildren;
|
|
223
|
-
}
|
|
224
|
-
const routeDefs = generateNode(tree, 0);
|
|
225
|
-
return [
|
|
226
|
-
`import { lazy } from "@pyreon/router"`,
|
|
227
|
-
"",
|
|
228
|
-
...imports,
|
|
229
|
-
"",
|
|
230
|
-
`function clean(routes) {`,
|
|
231
|
-
` return routes.map(r => {`,
|
|
232
|
-
` const c = {}`,
|
|
233
|
-
` for (const k in r) if (r[k] !== undefined) c[k] = r[k]`,
|
|
234
|
-
` if (c.children) c.children = clean(c.children)`,
|
|
235
|
-
` return c`,
|
|
236
|
-
` })`,
|
|
237
|
-
`}`,
|
|
238
|
-
"",
|
|
239
|
-
`export const routes = clean([`,
|
|
240
|
-
routeDefs.join(",\n"),
|
|
241
|
-
`])`
|
|
242
|
-
].join("\n");
|
|
243
|
-
}
|
|
244
|
-
/**
|
|
245
|
-
* Generate a virtual module that maps URL patterns to their middleware exports.
|
|
246
|
-
* Used by the server entry to dispatch per-route middleware.
|
|
247
|
-
*/
|
|
248
|
-
function generateMiddlewareModule(files, routesDir) {
|
|
249
|
-
const routes = parseFileRoutes(files);
|
|
250
|
-
const imports = [];
|
|
251
|
-
const entries = [];
|
|
252
|
-
let counter = 0;
|
|
253
|
-
for (const route of routes) {
|
|
254
|
-
if (route.isLayout || route.isError || route.isLoading || route.isNotFound) continue;
|
|
255
|
-
const name = `_mw${counter++}`;
|
|
256
|
-
const fullPath = `${routesDir}/${route.filePath}`;
|
|
257
|
-
imports.push(`import { middleware as ${name} } from "${fullPath}"`);
|
|
258
|
-
entries.push(` { pattern: ${JSON.stringify(route.urlPath)}, middleware: ${name} }`);
|
|
259
|
-
}
|
|
260
|
-
return [
|
|
261
|
-
...imports,
|
|
262
|
-
"",
|
|
263
|
-
`export const routeMiddleware = [`,
|
|
264
|
-
entries.join(",\n"),
|
|
265
|
-
`].filter(e => e.middleware)`
|
|
266
|
-
].join("\n");
|
|
267
|
-
}
|
|
268
|
-
/**
|
|
269
|
-
* Scan a directory for route files.
|
|
270
|
-
* Returns paths relative to the routes directory.
|
|
271
|
-
*/
|
|
272
|
-
async function scanRouteFiles(routesDir) {
|
|
273
|
-
const { readdir } = await import("node:fs/promises");
|
|
274
|
-
const { join, relative } = await import("node:path");
|
|
275
|
-
const files = [];
|
|
276
|
-
async function walk(dir) {
|
|
277
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
278
|
-
for (const entry of entries) {
|
|
279
|
-
const fullPath = join(dir, entry.name);
|
|
280
|
-
if (entry.isDirectory()) await walk(fullPath);
|
|
281
|
-
else if (ROUTE_EXTENSIONS.some((ext) => entry.name.endsWith(ext))) files.push(relative(routesDir, fullPath));
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
await walk(routesDir);
|
|
285
|
-
return files;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
//#endregion
|
|
289
|
-
export { parseFileRoutes as a, generateRouteModule as i, fs_router_exports as n, scanRouteFiles as o, generateMiddlewareModule as r, filePathToUrlPath as t };
|
|
290
|
-
//# sourceMappingURL=fs-router-Dil4IKZR.js.map
|