@useavalon/avalon 0.1.13 → 0.1.14
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/dist/mod.js +1 -0
- package/dist/src/build/integration-bundler-plugin.js +1 -0
- package/dist/src/build/integration-config.js +1 -0
- package/dist/src/build/integration-detection-plugin.js +1 -0
- package/dist/src/build/integration-resolver-plugin.js +1 -0
- package/dist/src/build/island-manifest.js +1 -0
- package/dist/src/build/island-types-generator.js +5 -0
- package/dist/src/build/mdx-island-transform.js +2 -0
- package/dist/src/build/mdx-plugin.js +1 -0
- package/dist/src/build/page-island-transform.js +3 -0
- package/dist/src/build/prop-extractors/index.js +1 -0
- package/dist/src/build/prop-extractors/lit.js +1 -0
- package/dist/src/build/prop-extractors/qwik.js +1 -0
- package/dist/src/build/prop-extractors/solid.js +1 -0
- package/dist/src/build/prop-extractors/svelte.js +1 -0
- package/dist/src/build/prop-extractors/vue.js +1 -0
- package/dist/src/build/sidecar-file-manager.js +1 -0
- package/dist/src/build/sidecar-renderer.js +6 -0
- package/dist/src/client/adapters/index.js +1 -0
- package/dist/src/client/components.js +1 -0
- package/dist/src/client/css-hmr-handler.js +1 -0
- package/dist/src/client/framework-adapter.js +13 -0
- package/dist/src/client/hmr-coordinator.js +1 -0
- package/dist/src/client/hmr-error-overlay.js +214 -0
- package/dist/src/client/main.js +39 -0
- package/dist/src/components/Image.js +1 -0
- package/dist/src/components/IslandErrorBoundary.js +1 -0
- package/dist/src/components/LayoutDataErrorBoundary.js +1 -0
- package/dist/src/components/LayoutErrorBoundary.js +1 -0
- package/dist/src/components/PersistentIsland.js +1 -0
- package/dist/src/components/StreamingErrorBoundary.js +1 -0
- package/dist/src/components/StreamingLayout.js +29 -0
- package/dist/src/core/components/component-analyzer.js +1 -0
- package/dist/src/core/components/component-detection.js +5 -0
- package/dist/src/core/components/enhanced-framework-detector.js +1 -0
- package/dist/src/core/components/framework-registry.js +1 -0
- package/dist/src/core/content/mdx-processor.js +1 -0
- package/dist/src/core/integrations/index.js +1 -0
- package/dist/src/core/integrations/loader.js +1 -0
- package/dist/src/core/integrations/registry.js +1 -0
- package/dist/src/core/islands/island-persistence.js +1 -0
- package/dist/src/core/islands/island-state-serializer.js +1 -0
- package/dist/src/core/islands/persistent-island-context.js +1 -0
- package/dist/src/core/islands/use-persistent-state.js +1 -0
- package/dist/src/core/layout/enhanced-layout-resolver.js +1 -0
- package/dist/src/core/layout/layout-cache-manager.js +1 -0
- package/dist/src/core/layout/layout-composer.js +1 -0
- package/dist/src/core/layout/layout-data-loader.js +1 -0
- package/dist/src/core/layout/layout-discovery.js +1 -0
- package/dist/src/core/layout/layout-matcher.js +1 -0
- package/dist/src/core/layout/layout-types.js +1 -0
- package/dist/src/core/modules/framework-module-resolver.js +1 -0
- package/dist/src/islands/component-analysis.js +1 -0
- package/dist/src/islands/css-utils.js +17 -0
- package/dist/src/islands/discovery/index.js +1 -0
- package/dist/src/islands/discovery/registry.js +1 -0
- package/dist/src/islands/discovery/resolver.js +2 -0
- package/dist/src/islands/discovery/scanner.js +1 -0
- package/dist/src/islands/discovery/types.js +1 -0
- package/dist/src/islands/discovery/validator.js +18 -0
- package/dist/src/islands/discovery/watcher.js +1 -0
- package/dist/src/islands/framework-detection.js +1 -0
- package/dist/src/islands/integration-loader.js +1 -0
- package/dist/src/islands/island.js +1 -0
- package/dist/src/islands/render-cache.js +1 -0
- package/dist/src/islands/types.js +1 -0
- package/dist/src/islands/universal-css-collector.js +5 -0
- package/dist/src/islands/universal-head-collector.js +2 -0
- package/dist/src/layout-system.js +1 -0
- package/dist/src/middleware/discovery.js +1 -0
- package/dist/src/middleware/executor.js +1 -0
- package/dist/src/middleware/index.js +1 -0
- package/dist/src/middleware/types.js +1 -0
- package/dist/src/nitro/build-config.js +1 -0
- package/dist/src/nitro/config.js +1 -0
- package/dist/src/nitro/error-handler.js +198 -0
- package/dist/src/nitro/index.js +1 -0
- package/dist/src/nitro/island-manifest.js +2 -0
- package/dist/src/nitro/middleware-adapter.js +1 -0
- package/dist/src/nitro/renderer.js +183 -0
- package/dist/src/nitro/route-discovery.js +1 -0
- package/dist/src/nitro/types.js +1 -0
- package/dist/src/render/collect-css.js +3 -0
- package/dist/src/render/error-pages.js +48 -0
- package/dist/src/render/isolated-ssr-renderer.js +1 -0
- package/dist/src/render/ssr.js +90 -0
- package/dist/src/schemas/api.js +1 -0
- package/dist/src/schemas/core.js +1 -0
- package/dist/src/schemas/index.js +1 -0
- package/dist/src/schemas/layout.js +1 -0
- package/dist/src/schemas/routing/index.js +1 -0
- package/dist/src/schemas/routing.js +1 -0
- package/dist/src/types/as-island.js +1 -0
- package/dist/src/types/layout.js +1 -0
- package/dist/src/types/routing.js +1 -0
- package/dist/src/types/types.js +1 -0
- package/dist/src/utils/dev-logger.js +12 -0
- package/dist/src/utils/fs.js +1 -0
- package/dist/src/vite-plugin/auto-discover.js +1 -0
- package/dist/src/vite-plugin/config.js +1 -0
- package/dist/src/vite-plugin/errors.js +1 -0
- package/dist/src/vite-plugin/image-optimization.js +45 -0
- package/dist/src/vite-plugin/integration-activator.js +1 -0
- package/dist/src/vite-plugin/island-sidecar-plugin.js +1 -0
- package/dist/src/vite-plugin/module-discovery.js +1 -0
- package/dist/src/vite-plugin/nitro-integration.js +42 -0
- package/dist/src/vite-plugin/plugin.js +1 -0
- package/dist/src/vite-plugin/types.js +1 -0
- package/dist/src/vite-plugin/validation.js +2 -0
- package/package.json +14 -20
- package/mod.ts +0 -302
- package/src/build/integration-bundler-plugin.ts +0 -116
- package/src/build/integration-config.ts +0 -168
- package/src/build/integration-detection-plugin.ts +0 -117
- package/src/build/integration-resolver-plugin.ts +0 -90
- package/src/build/island-manifest.ts +0 -269
- package/src/build/island-types-generator.ts +0 -476
- package/src/build/mdx-island-transform.ts +0 -464
- package/src/build/mdx-plugin.ts +0 -98
- package/src/build/page-island-transform.ts +0 -598
- package/src/build/prop-extractors/index.ts +0 -21
- package/src/build/prop-extractors/lit.ts +0 -140
- package/src/build/prop-extractors/qwik.ts +0 -16
- package/src/build/prop-extractors/solid.ts +0 -125
- package/src/build/prop-extractors/svelte.ts +0 -194
- package/src/build/prop-extractors/vue.ts +0 -111
- package/src/build/sidecar-file-manager.ts +0 -104
- package/src/build/sidecar-renderer.ts +0 -30
- package/src/client/adapters/index.ts +0 -21
- package/src/client/components.ts +0 -35
- package/src/client/css-hmr-handler.ts +0 -344
- package/src/client/framework-adapter.ts +0 -462
- package/src/client/hmr-coordinator.ts +0 -396
- package/src/client/hmr-error-overlay.js +0 -533
- package/src/client/main.js +0 -824
- package/src/components/Image.tsx +0 -123
- package/src/components/IslandErrorBoundary.tsx +0 -145
- package/src/components/LayoutDataErrorBoundary.tsx +0 -141
- package/src/components/LayoutErrorBoundary.tsx +0 -127
- package/src/components/PersistentIsland.tsx +0 -52
- package/src/components/StreamingErrorBoundary.tsx +0 -233
- package/src/components/StreamingLayout.tsx +0 -538
- package/src/core/components/component-analyzer.ts +0 -192
- package/src/core/components/component-detection.ts +0 -508
- package/src/core/components/enhanced-framework-detector.ts +0 -500
- package/src/core/components/framework-registry.ts +0 -563
- package/src/core/content/mdx-processor.ts +0 -46
- package/src/core/integrations/index.ts +0 -19
- package/src/core/integrations/loader.ts +0 -125
- package/src/core/integrations/registry.ts +0 -175
- package/src/core/islands/island-persistence.ts +0 -325
- package/src/core/islands/island-state-serializer.ts +0 -258
- package/src/core/islands/persistent-island-context.tsx +0 -80
- package/src/core/islands/use-persistent-state.ts +0 -68
- package/src/core/layout/enhanced-layout-resolver.ts +0 -322
- package/src/core/layout/layout-cache-manager.ts +0 -485
- package/src/core/layout/layout-composer.ts +0 -357
- package/src/core/layout/layout-data-loader.ts +0 -516
- package/src/core/layout/layout-discovery.ts +0 -243
- package/src/core/layout/layout-matcher.ts +0 -299
- package/src/core/layout/layout-types.ts +0 -110
- package/src/core/modules/framework-module-resolver.ts +0 -273
- package/src/islands/component-analysis.ts +0 -213
- package/src/islands/css-utils.ts +0 -565
- package/src/islands/discovery/index.ts +0 -80
- package/src/islands/discovery/registry.ts +0 -340
- package/src/islands/discovery/resolver.ts +0 -477
- package/src/islands/discovery/scanner.ts +0 -386
- package/src/islands/discovery/types.ts +0 -117
- package/src/islands/discovery/validator.ts +0 -544
- package/src/islands/discovery/watcher.ts +0 -368
- package/src/islands/framework-detection.ts +0 -428
- package/src/islands/integration-loader.ts +0 -490
- package/src/islands/island.tsx +0 -565
- package/src/islands/render-cache.ts +0 -550
- package/src/islands/types.ts +0 -80
- package/src/islands/universal-css-collector.ts +0 -157
- package/src/islands/universal-head-collector.ts +0 -137
- package/src/layout-system.ts +0 -218
- package/src/middleware/discovery.ts +0 -268
- package/src/middleware/executor.ts +0 -315
- package/src/middleware/index.ts +0 -76
- package/src/middleware/types.ts +0 -99
- package/src/nitro/build-config.ts +0 -576
- package/src/nitro/config.ts +0 -483
- package/src/nitro/error-handler.ts +0 -636
- package/src/nitro/index.ts +0 -173
- package/src/nitro/island-manifest.ts +0 -584
- package/src/nitro/middleware-adapter.ts +0 -260
- package/src/nitro/renderer.ts +0 -1471
- package/src/nitro/route-discovery.ts +0 -439
- package/src/nitro/types.ts +0 -321
- package/src/render/collect-css.ts +0 -198
- package/src/render/error-pages.ts +0 -79
- package/src/render/isolated-ssr-renderer.ts +0 -654
- package/src/render/ssr.ts +0 -1030
- package/src/schemas/api.ts +0 -30
- package/src/schemas/core.ts +0 -64
- package/src/schemas/index.ts +0 -212
- package/src/schemas/layout.ts +0 -279
- package/src/schemas/routing/index.ts +0 -38
- package/src/schemas/routing.ts +0 -376
- package/src/types/as-island.ts +0 -20
- package/src/types/layout.ts +0 -285
- package/src/types/routing.ts +0 -555
- package/src/types/types.ts +0 -5
- package/src/utils/dev-logger.ts +0 -299
- package/src/utils/fs.ts +0 -151
- package/src/vite-plugin/auto-discover.ts +0 -551
- package/src/vite-plugin/config.ts +0 -266
- package/src/vite-plugin/errors.ts +0 -127
- package/src/vite-plugin/image-optimization.ts +0 -156
- package/src/vite-plugin/integration-activator.ts +0 -126
- package/src/vite-plugin/island-sidecar-plugin.ts +0 -176
- package/src/vite-plugin/module-discovery.ts +0 -189
- package/src/vite-plugin/nitro-integration.ts +0 -1354
- package/src/vite-plugin/plugin.ts +0 -403
- package/src/vite-plugin/types.ts +0 -327
- package/src/vite-plugin/validation.ts +0 -228
- /package/{src → dist/src}/client/types/framework-runtime.d.ts +0 -0
- /package/{src → dist/src}/client/types/vite-hmr.d.ts +0 -0
- /package/{src → dist/src}/client/types/vite-virtual-modules.d.ts +0 -0
- /package/{src → dist/src}/layout-system.d.ts +0 -0
- /package/{src → dist/src}/types/image.d.ts +0 -0
- /package/{src → dist/src}/types/index.d.ts +0 -0
- /package/{src → dist/src}/types/island-jsx.d.ts +0 -0
- /package/{src → dist/src}/types/island-prop.d.ts +0 -0
- /package/{src → dist/src}/types/mdx.d.ts +0 -0
- /package/{src → dist/src}/types/urlpattern.d.ts +0 -0
- /package/{src → dist/src}/types/vite-env.d.ts +0 -0
|
@@ -1,439 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Route Discovery Module for Nitro
|
|
3
|
-
*
|
|
4
|
-
* This module provides minimal route discovery for Avalon's SSR pages.
|
|
5
|
-
*
|
|
6
|
-
* IMPORTANT: This module is simplified to complement Nitro's native routing:
|
|
7
|
-
* - API routes: Handled by Nitro's auto-discovery from `api/` directory
|
|
8
|
-
* - Page routes: Discovered here for SSR rendering (pages are components, not h3 handlers)
|
|
9
|
-
* - Middleware: Handled by Nitro's auto-discovery from `middleware/` directory
|
|
10
|
-
*
|
|
11
|
-
* The page discovery is needed because Avalon pages are React/Vue/Svelte components
|
|
12
|
-
* that require SSR rendering, which is different from Nitro's h3 route handlers.
|
|
13
|
-
*
|
|
14
|
-
* @module nitro/route-discovery
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import { stat } from "node:fs/promises";
|
|
18
|
-
import { basename, dirname, relative } from "node:path";
|
|
19
|
-
import { walk } from "../utils/fs.ts";
|
|
20
|
-
import type { DiscoveredRoute } from "./types.ts";
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Supported page file extensions
|
|
24
|
-
*/
|
|
25
|
-
export const PAGE_EXTENSIONS = [
|
|
26
|
-
".tsx",
|
|
27
|
-
".ts",
|
|
28
|
-
".jsx",
|
|
29
|
-
".js",
|
|
30
|
-
".vue",
|
|
31
|
-
".svelte",
|
|
32
|
-
".md",
|
|
33
|
-
".mdx",
|
|
34
|
-
];
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Options for page route discovery
|
|
38
|
-
*/
|
|
39
|
-
export interface PageDiscoveryOptions {
|
|
40
|
-
/** Pages directory path (absolute or relative to project root) */
|
|
41
|
-
pagesDir: string;
|
|
42
|
-
/** Enable development mode logging */
|
|
43
|
-
developmentMode?: boolean;
|
|
44
|
-
/** Directories to exclude from scanning */
|
|
45
|
-
excludeDirectories?: string[];
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Result of file path to pattern conversion
|
|
50
|
-
*/
|
|
51
|
-
export interface FilePathPatternResult {
|
|
52
|
-
/** Route pattern (e.g., /users/:id) */
|
|
53
|
-
pattern: string;
|
|
54
|
-
/** Extracted parameter names */
|
|
55
|
-
params: string[];
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Discovers page routes from multiple directories with route prefixes
|
|
60
|
-
*
|
|
61
|
-
* This supports modular architecture where pages can be in different modules,
|
|
62
|
-
* each with their own route prefix.
|
|
63
|
-
*
|
|
64
|
-
* @param pageDirs - Array of page directories with their route prefixes
|
|
65
|
-
* @param options - Discovery options
|
|
66
|
-
* @returns Array of discovered page routes
|
|
67
|
-
*/
|
|
68
|
-
export async function discoverPageRoutesFromMultipleDirs(
|
|
69
|
-
pageDirs: Array<{ dir: string; prefix: string }>,
|
|
70
|
-
options?: Pick<PageDiscoveryOptions, "developmentMode" | "excludeDirectories">
|
|
71
|
-
): Promise<DiscoveredRoute[]> {
|
|
72
|
-
const allRoutes: DiscoveredRoute[] = [];
|
|
73
|
-
|
|
74
|
-
for (const { dir, prefix } of pageDirs) {
|
|
75
|
-
const routes = await discoverPageRoutes(dir, options);
|
|
76
|
-
|
|
77
|
-
// Apply prefix to routes (except for root prefix '/')
|
|
78
|
-
for (const route of routes) {
|
|
79
|
-
if (prefix !== '/') {
|
|
80
|
-
// Combine prefix with pattern
|
|
81
|
-
// /prefix + /path -> /prefix/path
|
|
82
|
-
// /prefix + / -> /prefix
|
|
83
|
-
const combinedPattern = route.pattern === '/'
|
|
84
|
-
? prefix
|
|
85
|
-
: prefix + route.pattern;
|
|
86
|
-
route.pattern = combinedPattern;
|
|
87
|
-
}
|
|
88
|
-
allRoutes.push(route);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Sort all routes by specificity
|
|
93
|
-
return sortRoutesBySpecificity(allRoutes);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Discovers page routes from the pages directory for SSR rendering
|
|
98
|
-
*
|
|
99
|
-
* NOTE: This is specifically for page components that need SSR rendering.
|
|
100
|
-
* API routes should be placed in the `api/` directory and are auto-discovered
|
|
101
|
-
* by Nitro's native file-system routing.
|
|
102
|
-
*
|
|
103
|
-
* @param pagesDir - Path to the pages directory
|
|
104
|
-
* @param options - Discovery options
|
|
105
|
-
* @returns Array of discovered page routes
|
|
106
|
-
*
|
|
107
|
-
* @example
|
|
108
|
-
* ```ts
|
|
109
|
-
* const routes = await discoverPageRoutes('src/pages', {
|
|
110
|
-
* developmentMode: true,
|
|
111
|
-
* });
|
|
112
|
-
* ```
|
|
113
|
-
*/
|
|
114
|
-
export async function discoverPageRoutes(
|
|
115
|
-
pagesDir: string,
|
|
116
|
-
options?: Pick<PageDiscoveryOptions, "developmentMode" | "excludeDirectories">
|
|
117
|
-
): Promise<DiscoveredRoute[]> {
|
|
118
|
-
const routes: DiscoveredRoute[] = [];
|
|
119
|
-
const excludeDirs = options?.excludeDirectories ?? ["node_modules", ".git"];
|
|
120
|
-
|
|
121
|
-
try {
|
|
122
|
-
// Check if directory exists
|
|
123
|
-
const statResult = await stat(pagesDir);
|
|
124
|
-
if (!statResult.isDirectory()) {
|
|
125
|
-
if (options?.developmentMode) {
|
|
126
|
-
console.warn(
|
|
127
|
-
`[route-discovery] Pages path is not a directory: ${pagesDir}`
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
return [];
|
|
131
|
-
}
|
|
132
|
-
} catch (error) {
|
|
133
|
-
if (error instanceof Error && (error as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
134
|
-
if (options?.developmentMode) {
|
|
135
|
-
console.warn(
|
|
136
|
-
`[route-discovery] Pages directory not found: ${pagesDir}`
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
return [];
|
|
140
|
-
}
|
|
141
|
-
throw error;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Walk through the pages directory
|
|
145
|
-
const extensions = PAGE_EXTENSIONS.map((e) => e.slice(1)); // Remove leading dot
|
|
146
|
-
|
|
147
|
-
for await (
|
|
148
|
-
const entry of walk(pagesDir, {
|
|
149
|
-
includeDirs: false,
|
|
150
|
-
includeSymlinks: false,
|
|
151
|
-
exts: extensions,
|
|
152
|
-
})
|
|
153
|
-
) {
|
|
154
|
-
if (!entry.isFile) continue;
|
|
155
|
-
|
|
156
|
-
const relativePath = relative(pagesDir, entry.path);
|
|
157
|
-
|
|
158
|
-
// Skip files in excluded directories
|
|
159
|
-
if (excludeDirs.some((dir) => relativePath.includes(dir))) {
|
|
160
|
-
continue;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Skip private files (in folders starting with _)
|
|
164
|
-
if (isPrivateFile(relativePath)) {
|
|
165
|
-
continue;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Convert file path to route pattern
|
|
169
|
-
const { pattern, params } = filePathToPattern(relativePath);
|
|
170
|
-
|
|
171
|
-
routes.push({
|
|
172
|
-
type: "page",
|
|
173
|
-
filePath: entry.path,
|
|
174
|
-
pattern,
|
|
175
|
-
params,
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Sort routes by specificity (more specific routes first)
|
|
180
|
-
return sortRoutesBySpecificity(routes);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Converts a file path to a route pattern
|
|
185
|
-
*
|
|
186
|
-
* Handles:
|
|
187
|
-
* - Index files (index.tsx -> /)
|
|
188
|
-
* - Dynamic segments ([param] -> :param)
|
|
189
|
-
* - Catch-all segments ([...slug] -> **)
|
|
190
|
-
* - Route groups ((group) -> removed from path)
|
|
191
|
-
*
|
|
192
|
-
* @param filePath - Relative file path from pages directory
|
|
193
|
-
* @returns Pattern and extracted parameter names
|
|
194
|
-
*
|
|
195
|
-
* @example
|
|
196
|
-
* ```ts
|
|
197
|
-
* filePathToPattern('users/[id].tsx')
|
|
198
|
-
* // { pattern: '/users/:id', params: ['id'] }
|
|
199
|
-
*
|
|
200
|
-
* filePathToPattern('blog/[...slug].tsx')
|
|
201
|
-
* // { pattern: '/blog/**', params: ['slug'] }
|
|
202
|
-
* ```
|
|
203
|
-
*/
|
|
204
|
-
export function filePathToPattern(filePath: string): FilePathPatternResult {
|
|
205
|
-
const params: string[] = [];
|
|
206
|
-
|
|
207
|
-
let pattern = filePath
|
|
208
|
-
// Normalize path separators
|
|
209
|
-
.replace(/\\/g, "/")
|
|
210
|
-
// Remove file extension
|
|
211
|
-
.replace(/\.(tsx|ts|jsx|js|vue|svelte|md|mdx)$/, "")
|
|
212
|
-
// Remove route groups (parentheses)
|
|
213
|
-
.replace(/\([^)]+\)\//g, "")
|
|
214
|
-
.replace(/\([^)]+\)$/, "");
|
|
215
|
-
|
|
216
|
-
// Handle index files
|
|
217
|
-
if (basename(pattern) === "index") {
|
|
218
|
-
pattern = dirname(pattern);
|
|
219
|
-
if (pattern === ".") {
|
|
220
|
-
pattern = "";
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// Convert dynamic segments [param] to :param
|
|
225
|
-
// Convert catch-all segments [...slug] to **
|
|
226
|
-
pattern = pattern.replace(/\[([^\]]+)\]/g, (_, param) => {
|
|
227
|
-
if (param.startsWith("...")) {
|
|
228
|
-
// Catch-all segment
|
|
229
|
-
const paramName = param.slice(3);
|
|
230
|
-
params.push(paramName);
|
|
231
|
-
return "**";
|
|
232
|
-
} else {
|
|
233
|
-
// Dynamic segment
|
|
234
|
-
params.push(param);
|
|
235
|
-
return `:${param}`;
|
|
236
|
-
}
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
// Ensure leading slash
|
|
240
|
-
if (!pattern.startsWith("/")) {
|
|
241
|
-
pattern = "/" + pattern;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Handle root path
|
|
245
|
-
if (pattern === "/" || pattern === "") {
|
|
246
|
-
pattern = "/";
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// Remove trailing slash (except for root)
|
|
250
|
-
if (pattern.length > 1 && pattern.endsWith("/")) {
|
|
251
|
-
pattern = pattern.slice(0, -1);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
return { pattern, params };
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* Checks if a file is in a private folder (starts with _)
|
|
259
|
-
*
|
|
260
|
-
* @param relativePath - Relative file path
|
|
261
|
-
* @returns True if the file is private
|
|
262
|
-
*/
|
|
263
|
-
export function isPrivateFile(relativePath: string): boolean {
|
|
264
|
-
const pathParts = relativePath.split(/[/\\]/);
|
|
265
|
-
return pathParts.some((part) => part.startsWith("_"));
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* Calculates route specificity score for sorting
|
|
270
|
-
* Lower score = more specific = higher priority
|
|
271
|
-
*
|
|
272
|
-
* @param route - Discovered route
|
|
273
|
-
* @returns Specificity score
|
|
274
|
-
*/
|
|
275
|
-
export function calculateRouteSpecificity(route: DiscoveredRoute): number {
|
|
276
|
-
const segments = route.pattern.split("/").filter((s) => s.length > 0);
|
|
277
|
-
let score = 0;
|
|
278
|
-
|
|
279
|
-
for (const segment of segments) {
|
|
280
|
-
if (segment === "**") {
|
|
281
|
-
// Catch-all has lowest priority
|
|
282
|
-
score += 1000;
|
|
283
|
-
} else if (segment.startsWith(":")) {
|
|
284
|
-
// Dynamic segment has medium priority
|
|
285
|
-
score += 100;
|
|
286
|
-
} else {
|
|
287
|
-
// Static segment has highest priority
|
|
288
|
-
score += 1;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// Shorter paths are more specific (for same type of segments)
|
|
293
|
-
score += segments.length;
|
|
294
|
-
|
|
295
|
-
return score;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Sorts routes by specificity (most specific first)
|
|
300
|
-
*
|
|
301
|
-
* @param routes - Array of discovered routes
|
|
302
|
-
* @returns Sorted array of routes
|
|
303
|
-
*/
|
|
304
|
-
export function sortRoutesBySpecificity(
|
|
305
|
-
routes: DiscoveredRoute[]
|
|
306
|
-
): DiscoveredRoute[] {
|
|
307
|
-
return [...routes].sort((a, b) => {
|
|
308
|
-
const scoreA = calculateRouteSpecificity(a);
|
|
309
|
-
const scoreB = calculateRouteSpecificity(b);
|
|
310
|
-
return scoreA - scoreB;
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* Validates a route pattern for correctness
|
|
316
|
-
*
|
|
317
|
-
* @param pattern - Route pattern to validate
|
|
318
|
-
* @returns Array of validation errors (empty if valid)
|
|
319
|
-
*/
|
|
320
|
-
export function validateRoutePattern(pattern: string): string[] {
|
|
321
|
-
const errors: string[] = [];
|
|
322
|
-
|
|
323
|
-
// Check for leading slash
|
|
324
|
-
if (!pattern.startsWith("/")) {
|
|
325
|
-
errors.push("Route pattern must start with /");
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// Check for malformed dynamic segments
|
|
329
|
-
const malformedSegments = pattern.match(/\[[^\]]*$/g);
|
|
330
|
-
if (malformedSegments) {
|
|
331
|
-
errors.push(
|
|
332
|
-
`Malformed dynamic segments: ${malformedSegments.join(", ")}. Dynamic segments must be properly closed with ]`
|
|
333
|
-
);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// Check for empty dynamic segments
|
|
337
|
-
const emptySegments = pattern.match(/\[\]/g);
|
|
338
|
-
if (emptySegments) {
|
|
339
|
-
errors.push(
|
|
340
|
-
"Empty dynamic segments [] are not allowed. Use [param] for dynamic segments or [...rest] for catch-all"
|
|
341
|
-
);
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// Check for nested dynamic segments
|
|
345
|
-
const nestedSegments = pattern.match(/\[[^\]]*\[[^\]]*\]/g);
|
|
346
|
-
if (nestedSegments) {
|
|
347
|
-
errors.push(
|
|
348
|
-
`Nested dynamic segments are not supported: ${nestedSegments.join(", ")}`
|
|
349
|
-
);
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
return errors;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
* Extracts parameter names from a route pattern
|
|
357
|
-
*
|
|
358
|
-
* @param pattern - Route pattern (e.g., /users/:id/posts/:postId)
|
|
359
|
-
* @returns Array of parameter names
|
|
360
|
-
*/
|
|
361
|
-
export function extractParamsFromPattern(pattern: string): string[] {
|
|
362
|
-
const params: string[] = [];
|
|
363
|
-
|
|
364
|
-
// Match :param patterns
|
|
365
|
-
const dynamicMatches = pattern.matchAll(/:([^/]+)/g);
|
|
366
|
-
for (const match of dynamicMatches) {
|
|
367
|
-
params.push(match[1]);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
// Match ** catch-all (represented as unnamed param)
|
|
371
|
-
if (pattern.includes("**")) {
|
|
372
|
-
params.push("slug");
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
return params;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* Checks if a route pattern matches a given URL path
|
|
380
|
-
*
|
|
381
|
-
* @param pattern - Route pattern
|
|
382
|
-
* @param path - URL path to match
|
|
383
|
-
* @returns True if the pattern matches the path
|
|
384
|
-
*/
|
|
385
|
-
export function matchRoutePattern(
|
|
386
|
-
pattern: string,
|
|
387
|
-
path: string
|
|
388
|
-
): { matches: boolean; params: Record<string, string> } {
|
|
389
|
-
const params: Record<string, string> = {};
|
|
390
|
-
|
|
391
|
-
// Normalize paths
|
|
392
|
-
const normalizedPattern = pattern.replace(/\/$/, "") || "/";
|
|
393
|
-
const normalizedPath = path.replace(/\/$/, "") || "/";
|
|
394
|
-
|
|
395
|
-
const patternSegments = normalizedPattern.split("/").filter((s) => s);
|
|
396
|
-
const pathSegments = normalizedPath.split("/").filter((s) => s);
|
|
397
|
-
|
|
398
|
-
let patternIndex = 0;
|
|
399
|
-
let pathIndex = 0;
|
|
400
|
-
|
|
401
|
-
while (patternIndex < patternSegments.length) {
|
|
402
|
-
const patternSegment = patternSegments[patternIndex];
|
|
403
|
-
|
|
404
|
-
if (patternSegment === "**") {
|
|
405
|
-
// Catch-all: match remaining path segments
|
|
406
|
-
const remainingPath = pathSegments.slice(pathIndex).join("/");
|
|
407
|
-
params["slug"] = remainingPath;
|
|
408
|
-
return { matches: true, params };
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
if (pathIndex >= pathSegments.length) {
|
|
412
|
-
// No more path segments but pattern has more
|
|
413
|
-
return { matches: false, params: {} };
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
const pathSegment = pathSegments[pathIndex];
|
|
417
|
-
|
|
418
|
-
if (patternSegment.startsWith(":")) {
|
|
419
|
-
// Dynamic segment: extract param value
|
|
420
|
-
const paramName = patternSegment.slice(1);
|
|
421
|
-
params[paramName] = pathSegment;
|
|
422
|
-
} else if (patternSegment !== pathSegment) {
|
|
423
|
-
// Static segment: must match exactly
|
|
424
|
-
return { matches: false, params: {} };
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
patternIndex++;
|
|
428
|
-
pathIndex++;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
// Check if all path segments were consumed
|
|
432
|
-
if (pathIndex < pathSegments.length) {
|
|
433
|
-
return { matches: false, params: {} };
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
return { matches: true, params };
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
|