@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/src/index.ts CHANGED
@@ -1,46 +1,17 @@
1
- // ─── Core ─────────────────────────────────────────────────────────────────────
2
-
3
- export type { CreateAppOptions } from "./app";
4
- export { createApp } from "./app";
5
- export type { CreateServerOptions } from "./entry-server";
6
- export { createServer } from "./entry-server";
7
-
8
- // ─── Vite plugin ─────────────────────────────────────────────────────────────
9
-
10
- export { zeroPlugin as default } from "./vite-plugin";
11
-
12
- // ─── File-system routing ─────────────────────────────────────────────────────
13
-
14
- export type { GenerateRouteModuleOptions } from './fs-router'
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
- // ─── 404 Not Found ──────────────────────────────────────────────────────────
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
- // ─── SEO ────────────────────────────────────────────────────────────────────
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
- // ─── CSP ────────────────────────────────────────────────────────────────────
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
- import { ogImagePath } from './og-image'
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