@cloudwerk/vite-plugin 0.1.3 → 0.3.0

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/index.d.ts CHANGED
@@ -52,6 +52,11 @@ interface CloudwerkVitePluginOptions {
52
52
  * @default 'hono-jsx'
53
53
  */
54
54
  renderer?: 'hono-jsx' | 'react';
55
+ /**
56
+ * Directory for static assets served at root.
57
+ * @default 'public'
58
+ */
59
+ publicDir?: string;
55
60
  }
56
61
  /**
57
62
  * Resolved plugin options after applying defaults and loading config.
@@ -73,6 +78,8 @@ interface ResolvedCloudwerkOptions {
73
78
  hydrationEndpoint: string;
74
79
  /** UI renderer name */
75
80
  renderer: 'hono-jsx' | 'react';
81
+ /** Directory for static assets (relative to root) */
82
+ publicDir: string;
76
83
  /** Vite root directory (absolute path) */
77
84
  root: string;
78
85
  }
@@ -149,7 +156,7 @@ declare function cloudwerkPlugin(options?: CloudwerkVitePluginOptions): Plugin;
149
156
  * - Layouts applied to pages in correct order
150
157
  * - Middleware chains applied
151
158
  * - Route config support
152
- * - Error and 404 handling
159
+ * - Error and 404 handling with error.tsx and not-found.tsx support
153
160
  *
154
161
  * @param manifest - Route manifest from @cloudwerk/core
155
162
  * @param scanResult - Scan result with file information
package/dist/index.js CHANGED
@@ -1,11 +1,13 @@
1
1
  // src/plugin.ts
2
- import * as path from "path";
2
+ import * as path2 from "path";
3
3
  import * as fs from "fs";
4
4
  import {
5
5
  scanRoutes,
6
6
  buildRouteManifest,
7
7
  resolveLayouts,
8
8
  resolveMiddleware,
9
+ resolveErrorBoundary,
10
+ resolveNotFoundBoundary,
9
11
  loadConfig,
10
12
  resolveRoutesPath,
11
13
  hasUseClientDirective,
@@ -26,19 +28,56 @@ var RESOLVED_VIRTUAL_IDS = {
26
28
  };
27
29
 
28
30
  // src/virtual-modules/server-entry.ts
31
+ import * as path from "path";
29
32
  function generateServerEntry(manifest, scanResult, options) {
30
33
  const imports = [];
31
34
  const pageRegistrations = [];
32
35
  const routeRegistrations = [];
33
36
  const layoutImports = [];
34
37
  const middlewareImports = [];
38
+ const errorImports = [];
39
+ const notFoundImports = [];
35
40
  const importedModules = /* @__PURE__ */ new Set();
36
41
  const layoutModules = /* @__PURE__ */ new Map();
37
42
  const middlewareModules = /* @__PURE__ */ new Map();
43
+ const errorModules = /* @__PURE__ */ new Map();
44
+ const notFoundModules = /* @__PURE__ */ new Map();
38
45
  let pageIndex = 0;
39
46
  let routeIndex = 0;
40
47
  let layoutIndex = 0;
41
48
  let middlewareIndex = 0;
49
+ let errorIndex = 0;
50
+ let notFoundIndex = 0;
51
+ for (const err of scanResult.errors) {
52
+ if (!importedModules.has(err.absolutePath)) {
53
+ const varName = `error_${errorIndex++}`;
54
+ errorImports.push(`import * as ${varName} from '${err.absolutePath}'`);
55
+ errorModules.set(err.absolutePath, varName);
56
+ importedModules.add(err.absolutePath);
57
+ }
58
+ }
59
+ for (const nf of scanResult.notFound) {
60
+ if (!importedModules.has(nf.absolutePath)) {
61
+ const varName = `notFound_${notFoundIndex++}`;
62
+ notFoundImports.push(`import * as ${varName} from '${nf.absolutePath}'`);
63
+ notFoundModules.set(nf.absolutePath, varName);
64
+ importedModules.add(nf.absolutePath);
65
+ }
66
+ }
67
+ const errorBoundaryMapEntries = [];
68
+ for (const err of scanResult.errors) {
69
+ const dir = path.posix.dirname(err.relativePath);
70
+ const normalizedDir = dir === "." ? "" : dir;
71
+ const varName = errorModules.get(err.absolutePath);
72
+ errorBoundaryMapEntries.push(` ['${normalizedDir}', ${varName}]`);
73
+ }
74
+ const notFoundBoundaryMapEntries = [];
75
+ for (const nf of scanResult.notFound) {
76
+ const dir = path.posix.dirname(nf.relativePath);
77
+ const normalizedDir = dir === "." ? "" : dir;
78
+ const varName = notFoundModules.get(nf.absolutePath);
79
+ notFoundBoundaryMapEntries.push(` ['${normalizedDir}', ${varName}]`);
80
+ }
42
81
  for (const route of manifest.routes) {
43
82
  for (const middlewarePath of route.middleware) {
44
83
  if (!importedModules.has(middlewarePath)) {
@@ -63,15 +102,17 @@ function generateServerEntry(manifest, scanResult, options) {
63
102
  imports.push(`import * as ${varName} from '${route.absolutePath}'`);
64
103
  const layoutChain = route.layouts.map((p) => layoutModules.get(p)).join(", ");
65
104
  const middlewareChain = route.middleware.map((p) => middlewareModules.get(p)).join(", ");
105
+ const errorModule = route.errorBoundary ? errorModules.get(route.errorBoundary) : null;
106
+ const notFoundModule = route.notFoundBoundary ? notFoundModules.get(route.notFoundBoundary) : null;
66
107
  const hasOptionalCatchAll = route.segments.some((s) => s.type === "optionalCatchAll");
67
108
  if (hasOptionalCatchAll) {
68
109
  const basePath = route.urlPattern.replace(/\/:[^/]+\{\.\*\}$/, "") || "/";
69
110
  pageRegistrations.push(
70
- ` registerPage(app, '${basePath}', ${varName}, [${layoutChain}], [${middlewareChain}])`
111
+ ` registerPage(app, '${basePath}', ${varName}, [${layoutChain}], [${middlewareChain}], ${errorModule || "null"}, ${notFoundModule || "null"})`
71
112
  );
72
113
  }
73
114
  pageRegistrations.push(
74
- ` registerPage(app, '${route.urlPattern}', ${varName}, [${layoutChain}], [${middlewareChain}])`
115
+ ` registerPage(app, '${route.urlPattern}', ${varName}, [${layoutChain}], [${middlewareChain}], ${errorModule || "null"}, ${notFoundModule || "null"})`
75
116
  );
76
117
  } else if (route.fileType === "route") {
77
118
  const varName = `route_${routeIndex++}`;
@@ -89,7 +130,7 @@ function generateServerEntry(manifest, scanResult, options) {
89
130
  */
90
131
 
91
132
  import { Hono } from 'hono'
92
- import { contextMiddleware, createHandlerAdapter, createMiddlewareAdapter, setRouteConfig } from '@cloudwerk/core/runtime'
133
+ import { contextMiddleware, createHandlerAdapter, createMiddlewareAdapter, setRouteConfig, NotFoundError, RedirectError } from '@cloudwerk/core/runtime'
93
134
  import { setActiveRenderer } from '@cloudwerk/ui'
94
135
 
95
136
  // Page and Route Imports
@@ -101,13 +142,151 @@ ${layoutImports.join("\n")}
101
142
  // Middleware Imports
102
143
  ${middlewareImports.join("\n")}
103
144
 
145
+ // Error Boundary Imports
146
+ ${errorImports.join("\n")}
147
+
148
+ // Not-Found Boundary Imports
149
+ ${notFoundImports.join("\n")}
150
+
151
+ // ============================================================================
152
+ // Boundary Maps for Runtime Lookup
153
+ // ============================================================================
154
+
155
+ const errorBoundaryMap = new Map([
156
+ ${errorBoundaryMapEntries.join(",\n")}
157
+ ])
158
+
159
+ const notFoundBoundaryMap = new Map([
160
+ ${notFoundBoundaryMapEntries.join(",\n")}
161
+ ])
162
+
163
+ // ============================================================================
164
+ // Helper Functions
165
+ // ============================================================================
166
+
167
+ /**
168
+ * Generate a unique error digest for matching server logs.
169
+ */
170
+ function generateErrorDigest() {
171
+ return Date.now().toString(36) + Math.random().toString(36).substring(2, 8)
172
+ }
173
+
174
+ /**
175
+ * Find the closest error boundary for a given URL path.
176
+ * Walks from closest directory to root, returning first match.
177
+ */
178
+ function findClosestErrorBoundary(urlPath) {
179
+ // Convert URL path to directory segments
180
+ const segments = urlPath.split('/').filter(Boolean)
181
+
182
+ // Walk from closest to root
183
+ while (segments.length >= 0) {
184
+ const dir = segments.join('/')
185
+ const boundary = errorBoundaryMap.get(dir)
186
+ if (boundary) {
187
+ return boundary
188
+ }
189
+ if (segments.length === 0) break
190
+ segments.pop()
191
+ }
192
+
193
+ return null
194
+ }
195
+
196
+ /**
197
+ * Find the closest not-found boundary for a given URL path.
198
+ * Walks from closest directory to root, returning first match.
199
+ */
200
+ function findClosestNotFoundBoundary(urlPath) {
201
+ // Convert URL path to directory segments
202
+ const segments = urlPath.split('/').filter(Boolean)
203
+
204
+ // Walk from closest to root
205
+ while (segments.length >= 0) {
206
+ const dir = segments.join('/')
207
+ const boundary = notFoundBoundaryMap.get(dir)
208
+ if (boundary) {
209
+ return boundary
210
+ }
211
+ if (segments.length === 0) break
212
+ segments.pop()
213
+ }
214
+
215
+ return null
216
+ }
217
+
218
+ /**
219
+ * Render an error page with the given error boundary module.
220
+ */
221
+ async function renderErrorPage(error, errorModule, layoutModules, layoutLoaderData, params, searchParams, errorType) {
222
+ // Add digest to error for log matching
223
+ const digest = generateErrorDigest()
224
+ error.digest = digest
225
+
226
+ // Build error boundary props
227
+ const errorProps = {
228
+ error: {
229
+ message: error.message,
230
+ digest,
231
+ stack: error.stack,
232
+ },
233
+ errorType,
234
+ reset: () => {}, // No-op on server
235
+ params,
236
+ searchParams,
237
+ }
238
+
239
+ // Render error boundary
240
+ let element = await Promise.resolve(errorModule.default(errorProps))
241
+
242
+ // Wrap with layouts if available
243
+ for (let i = layoutModules.length - 1; i >= 0; i--) {
244
+ const Layout = layoutModules[i].default
245
+ const layoutProps = {
246
+ children: element,
247
+ params,
248
+ ...layoutLoaderData[i],
249
+ }
250
+ element = await Promise.resolve(Layout(layoutProps))
251
+ }
252
+
253
+ return renderWithHydration(element, 500)
254
+ }
255
+
256
+ /**
257
+ * Render a not-found page with the given not-found boundary module.
258
+ */
259
+ async function renderNotFoundPage(notFoundModule, layoutModules, layoutLoaderData, params, searchParams) {
260
+ // Build not-found props
261
+ const notFoundProps = {
262
+ params,
263
+ searchParams,
264
+ }
265
+
266
+ // Render not-found boundary
267
+ let element = await Promise.resolve(notFoundModule.default(notFoundProps))
268
+
269
+ // Wrap with layouts if available
270
+ for (let i = layoutModules.length - 1; i >= 0; i--) {
271
+ const Layout = layoutModules[i].default
272
+ const layoutProps = {
273
+ children: element,
274
+ params,
275
+ ...layoutLoaderData[i],
276
+ }
277
+ element = await Promise.resolve(Layout(layoutProps))
278
+ }
279
+
280
+ return renderWithHydration(element, 404)
281
+ }
282
+
104
283
  // ============================================================================
105
284
  // Route Registration Helpers
106
285
  // ============================================================================
107
286
 
108
287
  const HTTP_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD']
109
288
 
110
- function registerPage(app, pattern, pageModule, layoutModules, middlewareModules) {
289
+ function registerPage(app, pattern, pageModule, layoutModules, middlewareModules, errorModule, notFoundModule) {
111
290
  // Apply middleware (wrap with adapter to convert Cloudwerk middleware to Hono middleware)
112
291
  for (const mw of middlewareModules) {
113
292
  app.use(pattern, createMiddlewareAdapter(mw))
@@ -128,51 +307,76 @@ function registerPage(app, pattern, pageModule, layoutModules, middlewareModules
128
307
  const url = new URL(request.url)
129
308
  const searchParams = Object.fromEntries(url.searchParams.entries())
130
309
 
131
- // Execute layout loaders
310
+ // Track layout loader data for use in error boundaries
132
311
  const layoutLoaderData = []
133
312
  const loaderArgs = { params, request, context: c }
134
313
 
135
- for (const layoutModule of layoutModules) {
136
- if (layoutModule.loader) {
137
- const data = await Promise.resolve(layoutModule.loader(loaderArgs))
138
- layoutLoaderData.push(data ?? {})
139
- } else {
140
- layoutLoaderData.push({})
314
+ try {
315
+ // Execute layout loaders
316
+ for (const layoutModule of layoutModules) {
317
+ if (layoutModule.loader) {
318
+ const data = await Promise.resolve(layoutModule.loader(loaderArgs))
319
+ layoutLoaderData.push(data ?? {})
320
+ } else {
321
+ layoutLoaderData.push({})
322
+ }
141
323
  }
142
- }
143
324
 
144
- // Execute page loader
145
- let pageLoaderData = {}
146
- if (pageModule.loader) {
147
- pageLoaderData = (await Promise.resolve(pageModule.loader(loaderArgs))) ?? {}
148
- }
325
+ // Execute page loader
326
+ let pageLoaderData = {}
327
+ if (pageModule.loader) {
328
+ pageLoaderData = (await Promise.resolve(pageModule.loader(loaderArgs))) ?? {}
329
+ }
149
330
 
150
- // Build page props
151
- const pageProps = { params, searchParams, ...pageLoaderData }
331
+ // Build page props
332
+ const pageProps = { params, searchParams, ...pageLoaderData }
152
333
 
153
- // Render page
154
- let element = await Promise.resolve(pageModule.default(pageProps))
334
+ // Render page
335
+ let element = await Promise.resolve(pageModule.default(pageProps))
155
336
 
156
- // Wrap with layouts (inside-out)
157
- for (let i = layoutModules.length - 1; i >= 0; i--) {
158
- const Layout = layoutModules[i].default
159
- const layoutProps = {
160
- children: element,
161
- params,
162
- ...layoutLoaderData[i],
337
+ // Wrap with layouts (inside-out)
338
+ for (let i = layoutModules.length - 1; i >= 0; i--) {
339
+ const Layout = layoutModules[i].default
340
+ const layoutProps = {
341
+ children: element,
342
+ params,
343
+ ...layoutLoaderData[i],
344
+ }
345
+ element = await Promise.resolve(Layout(layoutProps))
346
+ }
347
+
348
+ // Render the page with hydration script injection
349
+ return renderWithHydration(element)
350
+ } catch (error) {
351
+ // Handle NotFoundError (check both instanceof and name for module duplication)
352
+ if (error instanceof NotFoundError || error?.name === 'NotFoundError') {
353
+ if (notFoundModule) {
354
+ return renderNotFoundPage(notFoundModule, layoutModules, layoutLoaderData, params, searchParams)
355
+ }
356
+ // Re-throw to trigger global not-found handler
357
+ throw error
358
+ }
359
+
360
+ // Handle RedirectError (check both instanceof and name for module duplication)
361
+ if (error instanceof RedirectError || error?.name === 'RedirectError') {
362
+ return c.redirect(error.url, error.status)
163
363
  }
164
- element = await Promise.resolve(Layout(layoutProps))
165
- }
166
364
 
167
- // Render the page with hydration script injection
168
- return renderWithHydration(element)
365
+ // Handle other errors
366
+ console.error('Page render error:', error.message)
367
+ if (errorModule) {
368
+ return renderErrorPage(error, errorModule, layoutModules, layoutLoaderData, params, searchParams, 'loader')
369
+ }
370
+ // Re-throw to trigger global error handler
371
+ throw error
372
+ }
169
373
  })
170
374
  }
171
375
 
172
376
  /**
173
377
  * Render element to a Response, injecting hydration script before </body>.
174
378
  */
175
- function renderWithHydration(element) {
379
+ function renderWithHydration(element, status = 200) {
176
380
  // Hono JSX elements have toString() for synchronous rendering
177
381
  const html = '<!DOCTYPE html>' + String(element)
178
382
 
@@ -184,6 +388,7 @@ function renderWithHydration(element) {
184
388
  : html + hydrationScript
185
389
 
186
390
  return new Response(injectedHtml, {
391
+ status,
187
392
  headers: {
188
393
  'Content-Type': 'text/html; charset=utf-8',
189
394
  },
@@ -240,13 +445,64 @@ ${pageRegistrations.join("\n")}
240
445
  ${routeRegistrations.join("\n")}
241
446
 
242
447
  // 404 handler
243
- app.notFound((c) => {
244
- return c.json({ error: 'Not Found', path: c.req.path }, 404)
448
+ app.notFound(async (c) => {
449
+ const path = c.req.path
450
+
451
+ // API routes return JSON 404
452
+ if (path.startsWith('/api')) {
453
+ return c.json({ error: 'Not Found', path }, 404)
454
+ }
455
+
456
+ // Try to find a not-found boundary for this path
457
+ const notFoundModule = findClosestNotFoundBoundary(path)
458
+ if (notFoundModule) {
459
+ return renderNotFoundPage(notFoundModule, [], [], {}, {})
460
+ }
461
+
462
+ // Fallback to JSON 404
463
+ return c.json({ error: 'Not Found', path }, 404)
245
464
  })
246
465
 
247
466
  // Error handler
248
- app.onError((err, c) => {
467
+ app.onError(async (err, c) => {
468
+ const path = c.req.path
469
+
470
+ // Handle NotFoundError (check both instanceof and name for module duplication)
471
+ if (err instanceof NotFoundError || err?.name === 'NotFoundError') {
472
+ // API routes return JSON 404
473
+ if (path.startsWith('/api')) {
474
+ return c.json({ error: 'Not Found', path }, 404)
475
+ }
476
+
477
+ // Try to find a not-found boundary
478
+ const notFoundModule = findClosestNotFoundBoundary(path)
479
+ if (notFoundModule) {
480
+ return renderNotFoundPage(notFoundModule, [], [], {}, {})
481
+ }
482
+
483
+ return c.json({ error: 'Not Found', path }, 404)
484
+ }
485
+
486
+ // Handle RedirectError (check both instanceof and name for module duplication)
487
+ if (err instanceof RedirectError || err?.name === 'RedirectError') {
488
+ return c.redirect(err.url, err.status)
489
+ }
490
+
491
+ // Log the error
249
492
  console.error('Request error:', err.message)
493
+
494
+ // API routes return JSON 500
495
+ if (path.startsWith('/api')) {
496
+ return c.json({ error: 'Internal Server Error', message: err.message }, 500)
497
+ }
498
+
499
+ // Try to find an error boundary for this path
500
+ const errorModule = findClosestErrorBoundary(path)
501
+ if (errorModule) {
502
+ return renderErrorPage(err, errorModule, [], [], {}, {}, 'unknown')
503
+ }
504
+
505
+ // Fallback to JSON 500
250
506
  return c.json({ error: 'Internal Server Error', message: err.message }, 500)
251
507
  })
252
508
 
@@ -674,7 +930,9 @@ function cloudwerkPlugin(options = {}) {
674
930
  state.scanResult,
675
931
  routesPath,
676
932
  resolveLayouts,
677
- resolveMiddleware
933
+ resolveMiddleware,
934
+ resolveErrorBoundary,
935
+ resolveNotFoundBoundary
678
936
  );
679
937
  state.serverEntryCache = null;
680
938
  state.clientEntryCache = null;
@@ -684,9 +942,9 @@ function cloudwerkPlugin(options = {}) {
684
942
  }
685
943
  function isRouteFile(filePath) {
686
944
  if (!state) return false;
687
- const appDir = path.resolve(state.options.root, state.options.appDir);
945
+ const appDir = path2.resolve(state.options.root, state.options.appDir);
688
946
  if (!filePath.startsWith(appDir)) return false;
689
- const basename2 = path.basename(filePath);
947
+ const basename2 = path2.basename(filePath);
690
948
  const nameWithoutExt = basename2.replace(/\.(ts|tsx|js|jsx)$/, "");
691
949
  return ROUTE_FILE_NAMES.includes(nameWithoutExt);
692
950
  }
@@ -707,18 +965,32 @@ function cloudwerkPlugin(options = {}) {
707
965
  }
708
966
  return {
709
967
  name: "cloudwerk",
968
+ /**
969
+ * Pass publicDir configuration to Vite.
970
+ * This enables Vite's built-in static file serving for the public directory.
971
+ */
972
+ async config(userConfig) {
973
+ if (userConfig.publicDir !== void 0) {
974
+ return {};
975
+ }
976
+ const root = userConfig.root ?? process.cwd();
977
+ const cloudwerkConfig = await loadConfig(root);
978
+ return {
979
+ publicDir: options.publicDir ?? cloudwerkConfig.publicDir ?? "public"
980
+ };
981
+ },
710
982
  /**
711
983
  * Resolve configuration and build initial manifest.
712
984
  */
713
985
  async configResolved(config) {
714
986
  const root = config.root;
715
- let detectedServerEntry = options.serverEntry ? path.resolve(root, options.serverEntry) : null;
987
+ let detectedServerEntry = options.serverEntry ? path2.resolve(root, options.serverEntry) : null;
716
988
  if (!detectedServerEntry) {
717
989
  const conventionalPaths = [
718
- path.resolve(root, "app/server.ts"),
719
- path.resolve(root, "app/server.tsx"),
720
- path.resolve(root, "src/server.ts"),
721
- path.resolve(root, "src/server.tsx")
990
+ path2.resolve(root, "app/server.ts"),
991
+ path2.resolve(root, "app/server.tsx"),
992
+ path2.resolve(root, "src/server.ts"),
993
+ path2.resolve(root, "src/server.tsx")
722
994
  ];
723
995
  for (const p of conventionalPaths) {
724
996
  if (fs.existsSync(p)) {
@@ -740,6 +1012,7 @@ function cloudwerkPlugin(options = {}) {
740
1012
  verbose: options.verbose ?? false,
741
1013
  hydrationEndpoint: options.hydrationEndpoint ?? "/__cloudwerk",
742
1014
  renderer: options.renderer ?? cloudwerkConfig.ui?.renderer ?? "hono-jsx",
1015
+ publicDir: options.publicDir ?? cloudwerkConfig.publicDir ?? "public",
743
1016
  root
744
1017
  };
745
1018
  state = {
@@ -748,6 +1021,8 @@ function cloudwerkPlugin(options = {}) {
748
1021
  routes: [],
749
1022
  layouts: /* @__PURE__ */ new Map(),
750
1023
  middleware: /* @__PURE__ */ new Map(),
1024
+ errorBoundaries: /* @__PURE__ */ new Map(),
1025
+ notFoundBoundaries: /* @__PURE__ */ new Map(),
751
1026
  errors: [],
752
1027
  warnings: [],
753
1028
  generatedAt: /* @__PURE__ */ new Date(),
@@ -773,11 +1048,11 @@ function cloudwerkPlugin(options = {}) {
773
1048
  configureServer(devServer) {
774
1049
  server = devServer;
775
1050
  if (!state) return;
776
- const appDir = path.resolve(state.options.root, state.options.appDir);
1051
+ const appDir = path2.resolve(state.options.root, state.options.appDir);
777
1052
  devServer.watcher.on("add", async (filePath) => {
778
1053
  if (isRouteFile(filePath)) {
779
1054
  if (state?.options.verbose) {
780
- console.log(`[cloudwerk] Route added: ${path.relative(appDir, filePath)}`);
1055
+ console.log(`[cloudwerk] Route added: ${path2.relative(appDir, filePath)}`);
781
1056
  }
782
1057
  await buildManifest(state.options.root);
783
1058
  invalidateVirtualModules();
@@ -786,7 +1061,7 @@ function cloudwerkPlugin(options = {}) {
786
1061
  devServer.watcher.on("unlink", async (filePath) => {
787
1062
  if (isRouteFile(filePath)) {
788
1063
  if (state?.options.verbose) {
789
- console.log(`[cloudwerk] Route removed: ${path.relative(appDir, filePath)}`);
1064
+ console.log(`[cloudwerk] Route removed: ${path2.relative(appDir, filePath)}`);
790
1065
  }
791
1066
  await buildManifest(state.options.root);
792
1067
  invalidateVirtualModules();
@@ -795,7 +1070,7 @@ function cloudwerkPlugin(options = {}) {
795
1070
  devServer.watcher.on("change", async (filePath) => {
796
1071
  if (isRouteFile(filePath)) {
797
1072
  if (state?.options.verbose) {
798
- console.log(`[cloudwerk] Route changed: ${path.relative(appDir, filePath)}`);
1073
+ console.log(`[cloudwerk] Route changed: ${path2.relative(appDir, filePath)}`);
799
1074
  }
800
1075
  await buildManifest(state.options.root);
801
1076
  invalidateVirtualModules();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudwerk/vite-plugin",
3
- "version": "0.1.3",
3
+ "version": "0.3.0",
4
4
  "description": "Vite plugin for Cloudwerk file-based routing with virtual entry generation",
5
5
  "repository": {
6
6
  "type": "git",
@@ -19,8 +19,8 @@
19
19
  ],
20
20
  "dependencies": {
21
21
  "@swc/core": "^1.3.100",
22
- "@cloudwerk/core": "^0.7.2",
23
- "@cloudwerk/ui": "^0.7.2"
22
+ "@cloudwerk/core": "^0.9.0",
23
+ "@cloudwerk/ui": "^0.9.0"
24
24
  },
25
25
  "peerDependencies": {
26
26
  "vite": "^5.0.0 || ^6.0.0",
@@ -33,7 +33,9 @@
33
33
  }
34
34
  },
35
35
  "devDependencies": {
36
+ "@hono/vite-dev-server": ">=0.18.0",
36
37
  "@types/node": "^20.0.0",
38
+ "hono": "^4.0.0",
37
39
  "tsup": "^8.0.0",
38
40
  "typescript": "^5.0.0",
39
41
  "vite": "^6.0.0",