litestar-vite-plugin 0.15.0-rc.1 → 0.15.0-rc.2

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.
@@ -6,23 +6,33 @@
6
6
  *
7
7
  * @example
8
8
  * ```ts
9
+ * // CSRF utilities
9
10
  * import { getCsrfToken, csrfFetch } from 'litestar-vite-plugin/helpers'
10
11
  *
11
- * // Get CSRF token
12
12
  * const token = getCsrfToken()
13
+ * await csrfFetch('/api/submit', { method: 'POST', body: JSON.stringify(data) })
14
+ * ```
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * // Route matching utilities
19
+ * import { createRouteHelpers } from 'litestar-vite-plugin/helpers'
20
+ * import { routeDefinitions } from '@/generated/routes'
13
21
  *
14
- * // Make a fetch request with CSRF token
15
- * await csrfFetch('/api/submit', {
16
- * method: 'POST',
17
- * body: JSON.stringify(data),
18
- * })
22
+ * const { isCurrentRoute, currentRoute, toRoute, isRoute } = createRouteHelpers(routeDefinitions)
23
+ *
24
+ * // Highlight active nav items
25
+ * if (isCurrentRoute('dashboard')) { ... }
26
+ *
27
+ * // Match with wildcards
28
+ * if (isCurrentRoute('book_*')) { ... }
19
29
  * ```
20
30
  *
21
- * For type-safe routing, import from your generated routes file:
31
+ * @example
22
32
  * ```ts
23
- * import { route, routeDefinitions, type RouteName } from '@/generated/routes'
33
+ * // Type-safe URL generation (from generated routes)
34
+ * import { route } from '@/generated/routes'
24
35
  *
25
- * // Type-safe URL generation
26
36
  * const url = route('user_detail', { user_id: 123 }) // Compile-time checked!
27
37
  * ```
28
38
  *
@@ -30,3 +40,4 @@
30
40
  */
31
41
  export { csrfFetch, csrfHeaders, getCsrfToken } from "./csrf.js";
32
42
  export { addDirective, registerHtmxExtension, setDebug as setHtmxDebug, swapJson } from "./htmx.js";
43
+ export { createRouteHelpers, currentRoute, isCurrentRoute, isRoute, type RouteDefinition, type RouteDefinitions, type RouteHelpers, toRoute, } from "./routes.js";
@@ -6,23 +6,33 @@
6
6
  *
7
7
  * @example
8
8
  * ```ts
9
+ * // CSRF utilities
9
10
  * import { getCsrfToken, csrfFetch } from 'litestar-vite-plugin/helpers'
10
11
  *
11
- * // Get CSRF token
12
12
  * const token = getCsrfToken()
13
+ * await csrfFetch('/api/submit', { method: 'POST', body: JSON.stringify(data) })
14
+ * ```
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * // Route matching utilities
19
+ * import { createRouteHelpers } from 'litestar-vite-plugin/helpers'
20
+ * import { routeDefinitions } from '@/generated/routes'
13
21
  *
14
- * // Make a fetch request with CSRF token
15
- * await csrfFetch('/api/submit', {
16
- * method: 'POST',
17
- * body: JSON.stringify(data),
18
- * })
22
+ * const { isCurrentRoute, currentRoute, toRoute, isRoute } = createRouteHelpers(routeDefinitions)
23
+ *
24
+ * // Highlight active nav items
25
+ * if (isCurrentRoute('dashboard')) { ... }
26
+ *
27
+ * // Match with wildcards
28
+ * if (isCurrentRoute('book_*')) { ... }
19
29
  * ```
20
30
  *
21
- * For type-safe routing, import from your generated routes file:
31
+ * @example
22
32
  * ```ts
23
- * import { route, routeDefinitions, type RouteName } from '@/generated/routes'
33
+ * // Type-safe URL generation (from generated routes)
34
+ * import { route } from '@/generated/routes'
24
35
  *
25
- * // Type-safe URL generation
26
36
  * const url = route('user_detail', { user_id: 123 }) // Compile-time checked!
27
37
  * ```
28
38
  *
@@ -32,3 +42,5 @@
32
42
  export { csrfFetch, csrfHeaders, getCsrfToken } from "./csrf.js";
33
43
  // HTMX utilities
34
44
  export { addDirective, registerHtmxExtension, setDebug as setHtmxDebug, swapJson } from "./htmx.js";
45
+ // Route matching utilities
46
+ export { createRouteHelpers, currentRoute, isCurrentRoute, isRoute, toRoute, } from "./routes.js";
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Route matching utilities for Litestar applications.
3
+ *
4
+ * These helpers work with the generated `routeDefinitions` from your routes.ts file
5
+ * to provide runtime route matching capabilities.
6
+ *
7
+ * **IMPORTANT**: This file serves as both the runtime library AND the reference
8
+ * implementation for the Python code generator. The same logic is also generated
9
+ * inline in `routes.ts` by `src/py/litestar_vite/_codegen/routes.py`.
10
+ *
11
+ * **If you modify the logic here, you MUST also update the Python generator!**
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * import { routeDefinitions } from '@/generated/routes'
16
+ * import { createRouteHelpers } from 'litestar-vite-plugin/helpers'
17
+ *
18
+ * const { isCurrentRoute, currentRoute, toRoute, isRoute } = createRouteHelpers(routeDefinitions)
19
+ *
20
+ * // Check if current URL matches a route
21
+ * if (isCurrentRoute('dashboard')) {
22
+ * // highlight nav item
23
+ * }
24
+ * ```
25
+ *
26
+ * @module
27
+ */
28
+ /**
29
+ * Route definition structure from generated routes.
30
+ */
31
+ export interface RouteDefinition {
32
+ path: string;
33
+ methods: readonly string[];
34
+ pathParams: readonly string[];
35
+ queryParams: readonly string[];
36
+ component?: string;
37
+ }
38
+ /**
39
+ * Map of route names to their definitions.
40
+ */
41
+ export type RouteDefinitions = Record<string, RouteDefinition>;
42
+ /**
43
+ * Convert a URL to its corresponding route name.
44
+ *
45
+ * @param url - URL or path to match (query strings and hashes are stripped)
46
+ * @param routes - The routeDefinitions object from generated routes
47
+ * @returns The matching route name, or null if no match found
48
+ *
49
+ * @example
50
+ * toRoute('/api/books', routeDefinitions) // 'books'
51
+ * toRoute('/api/books/123', routeDefinitions) // 'book_detail'
52
+ * toRoute('/unknown', routeDefinitions) // null
53
+ */
54
+ export declare function toRoute<T extends string>(url: string, routes: Record<T, RouteDefinition>): T | null;
55
+ /**
56
+ * Get the current route name based on the browser URL.
57
+ *
58
+ * Returns null in SSR/non-browser environments.
59
+ *
60
+ * @param routes - The routeDefinitions object from generated routes
61
+ * @returns Current route name, or null if no match or not in browser
62
+ *
63
+ * @example
64
+ * // On page /api/books/123
65
+ * currentRoute(routeDefinitions) // 'book_detail'
66
+ */
67
+ export declare function currentRoute<T extends string>(routes: Record<T, RouteDefinition>): T | null;
68
+ /**
69
+ * Check if a URL matches a route name or pattern.
70
+ *
71
+ * Supports wildcard patterns with `*` to match multiple routes.
72
+ *
73
+ * @param url - URL or path to check
74
+ * @param pattern - Route name or pattern (e.g., 'books', 'book_*', '*_detail')
75
+ * @param routes - The routeDefinitions object from generated routes
76
+ * @returns True if the URL matches the route pattern
77
+ *
78
+ * @example
79
+ * isRoute('/api/books', 'books', routeDefinitions) // true
80
+ * isRoute('/api/books/123', 'book_detail', routeDefinitions) // true
81
+ * isRoute('/api/books/123', 'book_*', routeDefinitions) // true (wildcard)
82
+ * isRoute('/api/users', 'book_*', routeDefinitions) // false
83
+ */
84
+ export declare function isRoute<T extends string>(url: string, pattern: string, routes: Record<T, RouteDefinition>): boolean;
85
+ /**
86
+ * Check if the current browser URL matches a route name or pattern.
87
+ *
88
+ * Supports wildcard patterns with `*` to match multiple routes.
89
+ * Returns false in SSR/non-browser environments.
90
+ *
91
+ * @param pattern - Route name or pattern (e.g., 'books', 'book_*', '*_page')
92
+ * @param routes - The routeDefinitions object from generated routes
93
+ * @returns True if current URL matches the route pattern
94
+ *
95
+ * @example
96
+ * // On page /books
97
+ * isCurrentRoute('books_page', routeDefinitions) // true
98
+ * isCurrentRoute('book_*', routeDefinitions) // false (no match)
99
+ * isCurrentRoute('*_page', routeDefinitions) // true (wildcard)
100
+ */
101
+ export declare function isCurrentRoute<T extends string>(pattern: string, routes: Record<T, RouteDefinition>): boolean;
102
+ /**
103
+ * Route helpers interface returned by createRouteHelpers.
104
+ */
105
+ export interface RouteHelpers<T extends string> {
106
+ /** Convert URL to route name */
107
+ toRoute: (url: string) => T | null;
108
+ /** Get current route name (SSR-safe) */
109
+ currentRoute: () => T | null;
110
+ /** Check if URL matches route pattern */
111
+ isRoute: (url: string, pattern: string) => boolean;
112
+ /** Check if current URL matches route pattern */
113
+ isCurrentRoute: (pattern: string) => boolean;
114
+ }
115
+ /**
116
+ * Create route helpers bound to a specific routeDefinitions object.
117
+ *
118
+ * This is the recommended way to use route helpers - it creates bound functions
119
+ * so you don't need to pass routeDefinitions to every call.
120
+ *
121
+ * @param routes - The routeDefinitions object from your generated routes
122
+ * @returns Object with bound route helper functions
123
+ *
124
+ * @example
125
+ * ```ts
126
+ * import { routeDefinitions } from '@/generated/routes'
127
+ * import { createRouteHelpers } from 'litestar-vite-plugin/helpers'
128
+ *
129
+ * export const { isCurrentRoute, currentRoute, toRoute, isRoute } = createRouteHelpers(routeDefinitions)
130
+ *
131
+ * // Now use without passing routes:
132
+ * if (isCurrentRoute('dashboard')) {
133
+ * // ...
134
+ * }
135
+ * ```
136
+ */
137
+ export declare function createRouteHelpers<T extends string>(routes: Record<T, RouteDefinition>): RouteHelpers<T>;
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Route matching utilities for Litestar applications.
3
+ *
4
+ * These helpers work with the generated `routeDefinitions` from your routes.ts file
5
+ * to provide runtime route matching capabilities.
6
+ *
7
+ * **IMPORTANT**: This file serves as both the runtime library AND the reference
8
+ * implementation for the Python code generator. The same logic is also generated
9
+ * inline in `routes.ts` by `src/py/litestar_vite/_codegen/routes.py`.
10
+ *
11
+ * **If you modify the logic here, you MUST also update the Python generator!**
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * import { routeDefinitions } from '@/generated/routes'
16
+ * import { createRouteHelpers } from 'litestar-vite-plugin/helpers'
17
+ *
18
+ * const { isCurrentRoute, currentRoute, toRoute, isRoute } = createRouteHelpers(routeDefinitions)
19
+ *
20
+ * // Check if current URL matches a route
21
+ * if (isCurrentRoute('dashboard')) {
22
+ * // highlight nav item
23
+ * }
24
+ * ```
25
+ *
26
+ * @module
27
+ */
28
+ /** Cache for compiled route patterns */
29
+ const patternCache = new Map();
30
+ /**
31
+ * Compile a route path pattern to a regex for URL matching.
32
+ * Results are cached for performance.
33
+ *
34
+ * @param path - Route path with {param} placeholders
35
+ * @returns Compiled regex pattern
36
+ */
37
+ function compilePattern(path) {
38
+ const cached = patternCache.get(path);
39
+ if (cached)
40
+ return cached;
41
+ // Escape special regex characters except { }
42
+ let pattern = path.replace(/[.*+?^$|()[\]]/g, "\\$&");
43
+ // Replace {param} or {param:type} with matchers
44
+ pattern = pattern.replace(/\{([^}]+)\}/g, (_match, paramSpec) => {
45
+ const paramType = paramSpec.includes(":") ? paramSpec.split(":")[1] : "str";
46
+ switch (paramType) {
47
+ case "uuid":
48
+ return "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
49
+ case "path":
50
+ return ".*";
51
+ case "int":
52
+ return "\\d+";
53
+ default:
54
+ return "[^/]+";
55
+ }
56
+ });
57
+ const regex = new RegExp(`^${pattern}$`, "i");
58
+ patternCache.set(path, regex);
59
+ return regex;
60
+ }
61
+ /**
62
+ * Convert a URL to its corresponding route name.
63
+ *
64
+ * @param url - URL or path to match (query strings and hashes are stripped)
65
+ * @param routes - The routeDefinitions object from generated routes
66
+ * @returns The matching route name, or null if no match found
67
+ *
68
+ * @example
69
+ * toRoute('/api/books', routeDefinitions) // 'books'
70
+ * toRoute('/api/books/123', routeDefinitions) // 'book_detail'
71
+ * toRoute('/unknown', routeDefinitions) // null
72
+ */
73
+ export function toRoute(url, routes) {
74
+ // Strip query string and hash
75
+ const path = url.split("?")[0].split("#")[0];
76
+ // Normalize: remove trailing slash except for root
77
+ const normalized = path === "/" ? path : path.replace(/\/$/, "");
78
+ for (const [name, def] of Object.entries(routes)) {
79
+ if (compilePattern(def.path).test(normalized)) {
80
+ return name;
81
+ }
82
+ }
83
+ return null;
84
+ }
85
+ /**
86
+ * Get the current route name based on the browser URL.
87
+ *
88
+ * Returns null in SSR/non-browser environments.
89
+ *
90
+ * @param routes - The routeDefinitions object from generated routes
91
+ * @returns Current route name, or null if no match or not in browser
92
+ *
93
+ * @example
94
+ * // On page /api/books/123
95
+ * currentRoute(routeDefinitions) // 'book_detail'
96
+ */
97
+ export function currentRoute(routes) {
98
+ if (typeof window === "undefined")
99
+ return null;
100
+ return toRoute(window.location.pathname, routes);
101
+ }
102
+ /**
103
+ * Check if a URL matches a route name or pattern.
104
+ *
105
+ * Supports wildcard patterns with `*` to match multiple routes.
106
+ *
107
+ * @param url - URL or path to check
108
+ * @param pattern - Route name or pattern (e.g., 'books', 'book_*', '*_detail')
109
+ * @param routes - The routeDefinitions object from generated routes
110
+ * @returns True if the URL matches the route pattern
111
+ *
112
+ * @example
113
+ * isRoute('/api/books', 'books', routeDefinitions) // true
114
+ * isRoute('/api/books/123', 'book_detail', routeDefinitions) // true
115
+ * isRoute('/api/books/123', 'book_*', routeDefinitions) // true (wildcard)
116
+ * isRoute('/api/users', 'book_*', routeDefinitions) // false
117
+ */
118
+ export function isRoute(url, pattern, routes) {
119
+ const routeName = toRoute(url, routes);
120
+ if (!routeName)
121
+ return false;
122
+ // Escape special regex chars (except *), then convert * to .*
123
+ const escaped = pattern.replace(/[.+?^$|()[\]{}]/g, "\\$&");
124
+ const regex = new RegExp(`^${escaped.replace(/\*/g, ".*")}$`);
125
+ return regex.test(routeName);
126
+ }
127
+ /**
128
+ * Check if the current browser URL matches a route name or pattern.
129
+ *
130
+ * Supports wildcard patterns with `*` to match multiple routes.
131
+ * Returns false in SSR/non-browser environments.
132
+ *
133
+ * @param pattern - Route name or pattern (e.g., 'books', 'book_*', '*_page')
134
+ * @param routes - The routeDefinitions object from generated routes
135
+ * @returns True if current URL matches the route pattern
136
+ *
137
+ * @example
138
+ * // On page /books
139
+ * isCurrentRoute('books_page', routeDefinitions) // true
140
+ * isCurrentRoute('book_*', routeDefinitions) // false (no match)
141
+ * isCurrentRoute('*_page', routeDefinitions) // true (wildcard)
142
+ */
143
+ export function isCurrentRoute(pattern, routes) {
144
+ const current = currentRoute(routes);
145
+ if (!current)
146
+ return false;
147
+ // Escape special regex chars (except *), then convert * to .*
148
+ const escaped = pattern.replace(/[.+?^$|()[\]{}]/g, "\\$&");
149
+ const regex = new RegExp(`^${escaped.replace(/\*/g, ".*")}$`);
150
+ return regex.test(current);
151
+ }
152
+ /**
153
+ * Create route helpers bound to a specific routeDefinitions object.
154
+ *
155
+ * This is the recommended way to use route helpers - it creates bound functions
156
+ * so you don't need to pass routeDefinitions to every call.
157
+ *
158
+ * @param routes - The routeDefinitions object from your generated routes
159
+ * @returns Object with bound route helper functions
160
+ *
161
+ * @example
162
+ * ```ts
163
+ * import { routeDefinitions } from '@/generated/routes'
164
+ * import { createRouteHelpers } from 'litestar-vite-plugin/helpers'
165
+ *
166
+ * export const { isCurrentRoute, currentRoute, toRoute, isRoute } = createRouteHelpers(routeDefinitions)
167
+ *
168
+ * // Now use without passing routes:
169
+ * if (isCurrentRoute('dashboard')) {
170
+ * // ...
171
+ * }
172
+ * ```
173
+ */
174
+ export function createRouteHelpers(routes) {
175
+ return {
176
+ toRoute: (url) => toRoute(url, routes),
177
+ currentRoute: () => currentRoute(routes),
178
+ isRoute: (url, pattern) => isRoute(url, pattern, routes),
179
+ isCurrentRoute: (pattern) => isCurrentRoute(pattern, routes),
180
+ };
181
+ }
package/dist/js/index.js CHANGED
@@ -510,7 +510,7 @@ function resolvePluginConfig(config) {
510
510
  typesConfig.pagePropsPath = path.join(typesConfig.output, "inertia-pages.json");
511
511
  }
512
512
  }
513
- const inertiaMode = resolvedConfig.inertiaMode ?? pythonDefaults?.mode === "inertia";
513
+ const inertiaMode = resolvedConfig.inertiaMode ?? (pythonDefaults?.mode === "hybrid" || pythonDefaults?.mode === "inertia");
514
514
  const effectiveResourceDir = resolvedConfig.resourceDir ?? pythonDefaults?.resourceDir ?? "src";
515
515
  const result = {
516
516
  input: resolvedConfig.input,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "litestar-vite-plugin",
3
- "version": "0.15.0-rc.1",
3
+ "version": "0.15.0-rc.2",
4
4
  "type": "module",
5
5
  "description": "Litestar plugin for Vite.",
6
6
  "keywords": [