@cloudwerk/vite-plugin 0.1.2 → 0.2.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/README.md ADDED
@@ -0,0 +1,66 @@
1
+ # @cloudwerk/vite-plugin
2
+
3
+ Vite plugin for Cloudwerk file-based routing with virtual entry generation.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @cloudwerk/vite-plugin vite
9
+ ```
10
+
11
+ ## Setup
12
+
13
+ ```typescript
14
+ // vite.config.ts
15
+ import { defineConfig } from 'vite'
16
+ import cloudwerk from '@cloudwerk/vite-plugin'
17
+
18
+ export default defineConfig({
19
+ plugins: [
20
+ cloudwerk({
21
+ // Options (all optional)
22
+ appDir: 'app', // Directory containing app files
23
+ routesDir: 'routes', // Subdirectory for routes
24
+ renderer: 'hono-jsx', // 'hono-jsx' or 'react'
25
+ verbose: false, // Enable debug logging
26
+ })
27
+ ]
28
+ })
29
+ ```
30
+
31
+ ## Virtual Modules
32
+
33
+ The plugin generates virtual modules for your application:
34
+
35
+ | Module | Description |
36
+ |--------|-------------|
37
+ | `virtual:cloudwerk/server-entry` | Server entry with route registration |
38
+ | `virtual:cloudwerk/client-entry` | Client entry for hydration |
39
+ | `virtual:cloudwerk/manifest` | Compiled route manifest |
40
+
41
+ Import in your code:
42
+
43
+ ```typescript
44
+ import app from 'virtual:cloudwerk/server-entry'
45
+ import manifest from 'virtual:cloudwerk/manifest'
46
+ ```
47
+
48
+ ## Options
49
+
50
+ | Option | Type | Default | Description |
51
+ |--------|------|---------|-------------|
52
+ | `appDir` | `string` | `'app'` | Directory containing route files |
53
+ | `routesDir` | `string` | `'routes'` | Subdirectory within appDir for routes |
54
+ | `config` | `object` | - | Override Cloudwerk configuration |
55
+ | `serverEntry` | `string` | - | Custom server entry file path |
56
+ | `clientEntry` | `string` | - | Custom client entry file path |
57
+ | `renderer` | `string` | `'hono-jsx'` | UI renderer (`'hono-jsx'` or `'react'`) |
58
+ | `verbose` | `boolean` | `false` | Enable verbose logging |
59
+
60
+ ## Documentation
61
+
62
+ For full documentation, visit: https://github.com/squirrelsoft-dev/cloudwerk
63
+
64
+ ## Part of Cloudwerk
65
+
66
+ This package is part of the [Cloudwerk](https://github.com/squirrelsoft-dev/cloudwerk) monorepo.
package/dist/index.d.ts CHANGED
@@ -149,7 +149,7 @@ declare function cloudwerkPlugin(options?: CloudwerkVitePluginOptions): Plugin;
149
149
  * - Layouts applied to pages in correct order
150
150
  * - Middleware chains applied
151
151
  * - Route config support
152
- * - Error and 404 handling
152
+ * - Error and 404 handling with error.tsx and not-found.tsx support
153
153
  *
154
154
  * @param manifest - Route manifest from @cloudwerk/core
155
155
  * @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
163
358
  }
164
- element = await Promise.resolve(Layout(layoutProps))
165
- }
166
359
 
167
- // Render the page with hydration script injection
168
- return renderWithHydration(element)
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)
363
+ }
364
+
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
  }
@@ -712,13 +970,13 @@ function cloudwerkPlugin(options = {}) {
712
970
  */
713
971
  async configResolved(config) {
714
972
  const root = config.root;
715
- let detectedServerEntry = options.serverEntry ? path.resolve(root, options.serverEntry) : null;
973
+ let detectedServerEntry = options.serverEntry ? path2.resolve(root, options.serverEntry) : null;
716
974
  if (!detectedServerEntry) {
717
975
  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")
976
+ path2.resolve(root, "app/server.ts"),
977
+ path2.resolve(root, "app/server.tsx"),
978
+ path2.resolve(root, "src/server.ts"),
979
+ path2.resolve(root, "src/server.tsx")
722
980
  ];
723
981
  for (const p of conventionalPaths) {
724
982
  if (fs.existsSync(p)) {
@@ -748,6 +1006,8 @@ function cloudwerkPlugin(options = {}) {
748
1006
  routes: [],
749
1007
  layouts: /* @__PURE__ */ new Map(),
750
1008
  middleware: /* @__PURE__ */ new Map(),
1009
+ errorBoundaries: /* @__PURE__ */ new Map(),
1010
+ notFoundBoundaries: /* @__PURE__ */ new Map(),
751
1011
  errors: [],
752
1012
  warnings: [],
753
1013
  generatedAt: /* @__PURE__ */ new Date(),
@@ -773,11 +1033,11 @@ function cloudwerkPlugin(options = {}) {
773
1033
  configureServer(devServer) {
774
1034
  server = devServer;
775
1035
  if (!state) return;
776
- const appDir = path.resolve(state.options.root, state.options.appDir);
1036
+ const appDir = path2.resolve(state.options.root, state.options.appDir);
777
1037
  devServer.watcher.on("add", async (filePath) => {
778
1038
  if (isRouteFile(filePath)) {
779
1039
  if (state?.options.verbose) {
780
- console.log(`[cloudwerk] Route added: ${path.relative(appDir, filePath)}`);
1040
+ console.log(`[cloudwerk] Route added: ${path2.relative(appDir, filePath)}`);
781
1041
  }
782
1042
  await buildManifest(state.options.root);
783
1043
  invalidateVirtualModules();
@@ -786,7 +1046,7 @@ function cloudwerkPlugin(options = {}) {
786
1046
  devServer.watcher.on("unlink", async (filePath) => {
787
1047
  if (isRouteFile(filePath)) {
788
1048
  if (state?.options.verbose) {
789
- console.log(`[cloudwerk] Route removed: ${path.relative(appDir, filePath)}`);
1049
+ console.log(`[cloudwerk] Route removed: ${path2.relative(appDir, filePath)}`);
790
1050
  }
791
1051
  await buildManifest(state.options.root);
792
1052
  invalidateVirtualModules();
@@ -795,7 +1055,7 @@ function cloudwerkPlugin(options = {}) {
795
1055
  devServer.watcher.on("change", async (filePath) => {
796
1056
  if (isRouteFile(filePath)) {
797
1057
  if (state?.options.verbose) {
798
- console.log(`[cloudwerk] Route changed: ${path.relative(appDir, filePath)}`);
1058
+ console.log(`[cloudwerk] Route changed: ${path2.relative(appDir, filePath)}`);
799
1059
  }
800
1060
  await buildManifest(state.options.root);
801
1061
  invalidateVirtualModules();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudwerk/vite-plugin",
3
- "version": "0.1.2",
3
+ "version": "0.2.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.1",
23
- "@cloudwerk/ui": "^0.7.1"
22
+ "@cloudwerk/core": "^0.8.0",
23
+ "@cloudwerk/ui": "^0.8.0"
24
24
  },
25
25
  "peerDependencies": {
26
26
  "vite": "^5.0.0 || ^6.0.0",