litestar-vite 0.8.2__tar.gz → 0.9.0__tar.gz

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.

Potentially problematic release.


This version of litestar-vite might be problematic. Click here for more details.

Files changed (72) hide show
  1. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/PKG-INFO +1 -1
  2. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/pyproject.toml +2 -2
  3. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/js/src/index.ts +41 -5
  4. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/js/src/inertia-helpers/index.ts +2 -1
  5. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/js/tests/index.test.ts +221 -144
  6. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/litestar_vite/commands.py +1 -1
  7. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/.gitignore +0 -0
  8. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/LICENSE +0 -0
  9. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/README.md +0 -0
  10. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/js/LICENSE +0 -0
  11. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/js/Makefile +0 -0
  12. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/js/NOTICE +0 -0
  13. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/js/README.md +0 -0
  14. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/js/src/dev-server-index.html +0 -0
  15. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/js/tests/__data__/dummy.ts +0 -0
  16. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/js/tsconfig.inertia-helpers.json +0 -0
  17. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/js/tsconfig.json +0 -0
  18. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/js/vitest.config.ts +0 -0
  19. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/js/vitest.workspace.ts +0 -0
  20. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/litestar_vite/__init__.py +0 -0
  21. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/litestar_vite/__metadata__.py +0 -0
  22. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/litestar_vite/cli.py +0 -0
  23. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/litestar_vite/config.py +0 -0
  24. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/litestar_vite/inertia/__init__.py +0 -0
  25. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/litestar_vite/inertia/_utils.py +0 -0
  26. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/litestar_vite/inertia/config.py +0 -0
  27. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/litestar_vite/inertia/exception_handler.py +0 -0
  28. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/litestar_vite/inertia/middleware.py +0 -0
  29. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/litestar_vite/inertia/plugin.py +0 -0
  30. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/litestar_vite/inertia/request.py +0 -0
  31. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/litestar_vite/inertia/response.py +0 -0
  32. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/litestar_vite/inertia/routes.py +0 -0
  33. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/litestar_vite/inertia/types.py +0 -0
  34. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/litestar_vite/loader.py +0 -0
  35. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/litestar_vite/plugin.py +0 -0
  36. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/litestar_vite/py.typed +0 -0
  37. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/litestar_vite/template_engine.py +0 -0
  38. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/litestar_vite/templates/__init__.py +0 -0
  39. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/litestar_vite/templates/index.html.j2 +0 -0
  40. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/litestar_vite/templates/main.ts.j2 +0 -0
  41. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/litestar_vite/templates/package.json.j2 +0 -0
  42. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/litestar_vite/templates/styles.css.j2 +0 -0
  43. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/litestar_vite/templates/tsconfig.json.j2 +0 -0
  44. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/litestar_vite/templates/vite.config.ts.j2 +0 -0
  45. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/__init__.py +0 -0
  46. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/conftest.py +0 -0
  47. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/templates/__init__.py +0 -0
  48. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/templates/index.html.j2 +0 -0
  49. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/test_app/__init__.py +0 -0
  50. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/test_app/app.py +0 -0
  51. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/test_app/web/__init__.py +0 -0
  52. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/test_app/web/public/.gitkeep +0 -0
  53. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/test_app/web/public/assets/main-l0sNRNKZ.js +0 -0
  54. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/test_app/web/public/assets/styles-l0sNRNKZ.js +0 -0
  55. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/test_app/web/public/manifest.json +0 -0
  56. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/test_app/web/resources/.gitkeep +0 -0
  57. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/test_app/web/resources/main.ts +0 -0
  58. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/test_app/web/resources/styles.css +0 -0
  59. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/test_app/web/templates/.gitkeep +0 -0
  60. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/test_app/web/templates/index.html +0 -0
  61. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/test_cli/__init__.py +0 -0
  62. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/test_cli/conftest.py +0 -0
  63. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/test_cli/test_init.py +0 -0
  64. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/test_commands.py +0 -0
  65. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/test_config.py +0 -0
  66. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/test_inertia/__init__.py +0 -0
  67. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/test_inertia/conftest.py +0 -0
  68. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/test_inertia/templates/index.html.j2 +0 -0
  69. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/test_inertia/test_inertia_request.py +0 -0
  70. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/test_inertia/test_inertia_response.py +0 -0
  71. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/test_inertia/test_routes.py +0 -0
  72. {litestar_vite-0.8.2 → litestar_vite-0.9.0}/src/py/tests/test_template_engine.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: litestar-vite
3
- Version: 0.8.2
3
+ Version: 0.9.0
4
4
  Summary: Vite plugin for Litestar
5
5
  Project-URL: Changelog, https://cofin.github.io/litestar-vite/latest/changelog
6
6
  Project-URL: Discord, https://discord.gg/X3FJqy8d2j
@@ -25,7 +25,7 @@ license = { text = "MIT" }
25
25
  name = "litestar-vite"
26
26
  readme = "README.md"
27
27
  requires-python = ">=3.8"
28
- version = "0.8.2"
28
+ version = "0.9.0"
29
29
 
30
30
  [project.urls]
31
31
  Changelog = "https://cofin.github.io/litestar-vite/latest/changelog"
@@ -85,7 +85,7 @@ test = [
85
85
  allow_dirty = true
86
86
  commit = true
87
87
  commit_args = "--no-verify"
88
- current_version = "0.8.2"
88
+ current_version = "0.9.0"
89
89
  ignore_missing_files = false
90
90
  ignore_missing_version = false
91
91
  message = "chore(release): bump to v{new_version}"
@@ -63,6 +63,12 @@ interface PluginConfig {
63
63
  * @default null
64
64
  */
65
65
  detectTls?: string | boolean | null
66
+ /**
67
+ * Automatically detect the index.html file.
68
+ *
69
+ * @default true
70
+ */
71
+ autoDetectIndex?: boolean
66
72
  /**
67
73
  * Transform the code while serving.
68
74
  */
@@ -82,7 +88,7 @@ type DevServerUrl = `${"http" | "https"}://${string}:${number}`
82
88
 
83
89
  let exitHandlersBound = false
84
90
 
85
- export const refreshPaths = ["**/*.py", "**/*.j2", "**/*.html.j2", "**/*.html", "**/assets/**/*"]
91
+ export const refreshPaths = ["src/**", "resources/**", "assets/**"].filter((path) => fs.existsSync(path.replace(/\*\*$/, "")))
86
92
 
87
93
  /**
88
94
  * Litestar plugin for Vite.
@@ -184,10 +190,30 @@ function resolveLitestarPlugin(pluginConfig: Required<PluginConfig>): LitestarPl
184
190
  }
185
191
  return undefined
186
192
  },
187
- configureServer(server) {
193
+ async configureServer(server) {
188
194
  const envDir = resolvedConfig.envDir || process.cwd()
189
195
  const appUrl = loadEnv(resolvedConfig.mode, envDir, "APP_URL").APP_URL ?? "undefined"
190
196
 
197
+ // Check if we should serve SPA directly
198
+ const shouldServeIndex = () => {
199
+ if (!pluginConfig.autoDetectIndex) return false
200
+
201
+ // Check various common locations for index.html
202
+ const possiblePaths = [
203
+ path.join(server.config.root, "index.html"),
204
+ path.join(server.config.root, pluginConfig.resourceDirectory, "index.html"),
205
+ path.join(server.config.root, "public", "index.html"),
206
+ ]
207
+
208
+ for (const indexPath of possiblePaths) {
209
+ try {
210
+ fs.accessSync(indexPath)
211
+ return true
212
+ } catch {}
213
+ }
214
+ return false
215
+ }
216
+
191
217
  server.httpServer?.once("listening", () => {
192
218
  const address = server.httpServer?.address()
193
219
 
@@ -197,14 +223,23 @@ function resolveLitestarPlugin(pluginConfig: Required<PluginConfig>): LitestarPl
197
223
  fs.mkdirSync(path.dirname(pluginConfig.hotFile), { recursive: true })
198
224
  fs.writeFileSync(pluginConfig.hotFile, viteDevServerUrl)
199
225
 
226
+ const hasIndex = shouldServeIndex()
227
+
200
228
  setTimeout(() => {
201
229
  server.config.logger.info(`\n ${colors.red(`${colors.bold("LITESTAR")} ${litestarVersion()}`)} ${colors.dim("plugin")} ${colors.bold(`v${pluginVersion()}`)}`)
202
230
  server.config.logger.info("")
203
- server.config.logger.info(` ${colors.green("➜")} ${colors.bold("APP_URL")}: ${colors.cyan(appUrl.replace(/:(\d+)/, (_, port) => `:${colors.bold(port)}`))}`)
231
+ if (hasIndex) {
232
+ server.config.logger.info(` ${colors.green("➜")} ${colors.bold("Serve Index")}: Serving application index with Vite`)
233
+ server.config.logger.info(` ${colors.green("➜")} ${colors.bold("DEV URL")}: ${colors.cyan(viteDevServerUrl)}`)
234
+ server.config.logger.info(` ${colors.green("➜")} ${colors.bold("APP_URL")}: ${colors.cyan(appUrl.replace(/:(\d+)/, (_, port) => `:${colors.bold(port)}`))}`)
235
+ } else {
236
+ server.config.logger.info(` ${colors.green("➜")} ${colors.bold("Serve Index")}: Serving Litestar index with Vite`)
237
+ server.config.logger.info(` ${colors.green("➜")} ${colors.bold("DEV URL")}: ${colors.cyan(viteDevServerUrl)}`)
238
+ server.config.logger.info(` ${colors.green("➜")} ${colors.bold("APP_URL")}: ${colors.cyan(appUrl.replace(/:(\d+)/, (_, port) => `:${colors.bold(port)}`))}`)
239
+ }
204
240
  }, 100)
205
241
  }
206
242
  })
207
-
208
243
  if (!exitHandlersBound) {
209
244
  const clean = () => {
210
245
  if (fs.existsSync(pluginConfig.hotFile)) {
@@ -222,7 +257,7 @@ function resolveLitestarPlugin(pluginConfig: Required<PluginConfig>): LitestarPl
222
257
 
223
258
  return () =>
224
259
  server.middlewares.use((req, res, next) => {
225
- if (req.url === "/index.html") {
260
+ if (!shouldServeIndex() && req.url === "/index.html") {
226
261
  res.statusCode = 404
227
262
 
228
263
  res.end(
@@ -325,6 +360,7 @@ function resolvePluginConfig(config: string | string[] | PluginConfig): Required
325
360
  refresh: resolvedConfig.refresh ?? false,
326
361
  hotFile: resolvedConfig.hotFile ?? path.join(resolvedConfig.bundleDirectory ?? "public", "hot"),
327
362
  detectTls: resolvedConfig.detectTls ?? false,
363
+ autoDetectIndex: resolvedConfig.autoDetectIndex ?? true,
328
364
  transformOnServe: resolvedConfig.transformOnServe ?? ((code) => code),
329
365
  }
330
366
  }
@@ -125,8 +125,9 @@ export function isRoute(url: string, routeName: string): boolean {
125
125
  const regexPattern = routePattern.replace(/\//g, "\\/").replace(/\{([^}]+):([^}]+)\}/g, (match, paramName, paramType) => {
126
126
  switch (paramType) {
127
127
  case "str":
128
- case "path":
129
128
  return "([^/]+)"
129
+ case "path":
130
+ return "(.*)"
130
131
  case "uuid":
131
132
  return "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
132
133
  default:
@@ -1,13 +1,93 @@
1
+ import fs from "node:fs"
2
+ import path from "node:path"
3
+ import { loadEnv } from "vite"
4
+ import { Plugin } from "vite"
1
5
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
2
6
  import litestar from "../src"
3
- import { currentRoute, getRelativeUrlPath, isCurrentRoute, isRoute, resolvePageComponent, route, toRoute } from "../src/inertia-helpers"
7
+ import { getRelativeUrlPath, isCurrentRoute, isRoute, resolvePageComponent, route, toRoute } from "../src/inertia-helpers"
8
+
9
+ // Mock the fs module
10
+ vi.mock("fs", async () => {
11
+ const actual = await vi.importActual<typeof import("fs")>("fs")
12
+
13
+ return {
14
+ default: {
15
+ ...actual,
16
+ existsSync: (path: string) => ["resources/", "assets/", "src/"].includes(path) || actual.existsSync(path),
17
+ },
18
+ }
19
+ })
20
+ // Mock process.env
21
+ const originalEnv = process.env
22
+ beforeEach(() => {
23
+ vi.resetModules()
24
+ process.env = { ...originalEnv }
25
+ })
26
+
27
+ afterEach(() => {
28
+ process.env = originalEnv
29
+ vi.clearAllMocks()
30
+ })
31
+
32
+ // Mock routes for testing
33
+ beforeEach(() => {
34
+ globalThis.routes = {
35
+ home: "/",
36
+ about: "/about",
37
+ "users:assign-role": "/api/roles/{role_slug:str}/assign",
38
+ "users:revoke-role": "/api/roles/{role_slug:str}/revoke",
39
+ "tags:create": "/api/tags",
40
+ "tags:list": "/api/tags",
41
+ "tags:get": "/api/tags/{tag_id:uuid}",
42
+ "tags:update": "/api/tags/{tag_id:uuid}",
43
+ "tags:delete": "/api/tags/{tag_id:uuid}",
44
+ "teams:add-member": "/api/teams/{team_id:uuid}/members/add",
45
+ "teams:remove-member": "/api/teams/{team_id:uuid}/members/remove",
46
+ "users:create": "/api/users/create",
47
+ "users:list": "/api/users/list",
48
+ "users:update": "/api/users/update/{user_id:uuid}",
49
+ "users:delete": "/api/users/delete/{user_id:uuid}",
50
+ "users:get": "/api/users/get/{user_id:uuid}",
51
+ dashboard: "/dashboard",
52
+ favicon: "/favicon.ico",
53
+ landing: "/landing",
54
+ "login.check": "/login/check",
55
+ login: "/login",
56
+ logout: "/logout",
57
+ "github.complete": "/o/github/complete",
58
+ "google.complete": "/o/google/complete",
59
+ "privacy-policy": "/privacy-policy",
60
+ "account.remove": "/profile/remove",
61
+ "profile.show": "/profile",
62
+ "profile.update": "/profile/update",
63
+ "password.update": "/profile/password-update",
64
+ register: "/register",
65
+ "register.add": "/register/add",
66
+ "github.register": "/register/github",
67
+ "google.register": "/register/google",
68
+ "worker:index": "/saq/queues/{queue_id:str}/jobs/{job_id:str}",
69
+ "worker:queue-list": "/saq/api/queues/list",
70
+ "worker:queue-detail": "/saq/api/queues/{queue_id:str}",
71
+ "worker:job-detail": "/saq/api/queues/{queue_id:str}/jobs/{job_id:str}",
72
+ "worker:job-abort": "/saq/api/queues/{queue_id:str}/jobs/{job_id:str}/abort",
73
+ "worker:job-retry": "/saq/api/queues/{queue_id:str}/jobs/{job_id:str}/retry",
74
+ saq: "/saq/static/{file_path:path}",
75
+ vite: "/static/{file_path:path}",
76
+ "teams.add": "/teams/add",
77
+ "teams.list": "/teams/list",
78
+ "teams.show": "/teams/show/{team_id:uuid}",
79
+ "teams.remove": "/teams/remove/{team_id:uuid}",
80
+ "teams.edit": "/teams/edit/{team_id:uuid}",
81
+ "terms-of-service": "/terms-of-service",
82
+ }
83
+ })
4
84
 
5
85
  describe("litestar-vite-plugin", () => {
6
86
  const originalEnv = { ...process.env }
7
87
 
8
88
  afterEach(() => {
9
89
  process.env = { ...originalEnv }
10
- vi.clearAllMocks()
90
+ vi.resetAllMocks()
11
91
  })
12
92
 
13
93
  it("handles missing configuration", () => {
@@ -43,20 +123,20 @@ describe("litestar-vite-plugin", () => {
43
123
  it("accepts a full configuration", () => {
44
124
  const plugin = litestar({
45
125
  input: "resources/js/app.ts",
46
- assetUrl: "other-build",
126
+ assetUrl: "other-static",
47
127
  bundleDirectory: "other-build",
48
128
  ssr: "resources/js/ssr.ts",
49
129
  ssrOutputDirectory: "other-ssr-output",
50
130
  })[0]
51
131
 
52
132
  const config = plugin.config({}, { command: "build", mode: "production" })
53
- expect(config.base).toBe("other-build/")
133
+ expect(config.base).toBe("other-static/")
54
134
  expect(config.build?.manifest).toBe("manifest.json")
55
135
  expect(config.build?.outDir).toBe("other-build")
56
136
  expect(config.build?.rollupOptions?.input).toBe("resources/js/app.ts")
57
137
 
58
138
  const ssrConfig = plugin.config({ build: { ssr: true } }, { command: "build", mode: "production" })
59
- expect(ssrConfig.base).toBe("other-build/")
139
+ expect(ssrConfig.base).toBe("other-static/")
60
140
  expect(ssrConfig.build?.manifest).toBe(false)
61
141
  expect(ssrConfig.build?.outDir).toBe("other-ssr-output")
62
142
  expect(ssrConfig.build?.rollupOptions?.input).toBe("resources/js/ssr.ts")
@@ -233,7 +313,7 @@ describe("litestar-vite-plugin", () => {
233
313
  ])
234
314
  })
235
315
 
236
- it("configures the Vite server when running remotely or in a container", () => {
316
+ it("configures the Vite server when running remotely", () => {
237
317
  process.env.VITE_ALLOW_REMOTE = "1"
238
318
  const plugin = litestar("resources/js/app.js")[0]
239
319
 
@@ -241,9 +321,11 @@ describe("litestar-vite-plugin", () => {
241
321
  expect(config.server?.host).toBe("0.0.0.0")
242
322
  expect(config.server?.port).toBe(5173)
243
323
  expect(config.server?.strictPort).toBe(true)
324
+
325
+ process.env.VITE_ALLOW_REMOTE = undefined
244
326
  })
245
327
 
246
- it("allows the Vite port to be configured when inside a container", () => {
328
+ it("allows the Vite port to be configured when running remotely", () => {
247
329
  process.env.VITE_ALLOW_REMOTE = "1"
248
330
  process.env.VITE_PORT = "1234"
249
331
  const plugin = litestar("resources/js/app.js")[0]
@@ -328,7 +410,7 @@ describe("litestar-vite-plugin", () => {
328
410
  expect(plugins.length).toBe(1)
329
411
  })
330
412
 
331
- it("configures full reload with routes and views when refresh is true", () => {
413
+ it("configures full reload with python and template files when refresh is true", () => {
332
414
  const plugins = litestar({
333
415
  input: "resources/js/app.js",
334
416
  refresh: true,
@@ -337,7 +419,7 @@ describe("litestar-vite-plugin", () => {
337
419
  expect(plugins.length).toBe(2)
338
420
  /** @ts-ignore */
339
421
  expect(plugins[1].__litestar_plugin_config).toEqual({
340
- paths: ["**/*.py", "**/*.j2", "**/*.html.j2", "**/*.html", "**/assets/**/*"],
422
+ paths: ["src/**", "resources/**", "assets/**"],
341
423
  })
342
424
  })
343
425
 
@@ -411,204 +493,199 @@ describe("litestar-vite-plugin", () => {
411
493
  config: { delay: 123 },
412
494
  })
413
495
  })
414
- })
415
496
 
497
+ it("handles TLS configuration", () => {
498
+ process.env.VITE_SERVER_KEY = "path/to/key"
499
+ process.env.VITE_SERVER_CERT = "path/to/cert"
500
+ process.env.APP_URL = "https://example.com"
501
+
502
+ const plugin = litestar("resources/js/app.js")[0]
503
+
504
+ expect(() => plugin.config({}, { command: "serve", mode: "development" })).toThrow(/Unable to find the certificate files/)
505
+ })
506
+
507
+ it("handles invalid APP_URL", () => {
508
+ process.env.VITE_SERVER_KEY = "path/to/key"
509
+ process.env.VITE_SERVER_CERT = "path/to/cert"
510
+ process.env.APP_URL = "invalid-url"
511
+
512
+ const plugin = litestar("resources/js/app.js")[0]
513
+
514
+ expect(() => plugin.config({}, { command: "serve", mode: "development" })).toThrow(/Unable to find the certificate files specified in your environment/)
515
+ })
516
+
517
+ it("handles missing config directory", () => {
518
+ const plugin = litestar({
519
+ input: "resources/js/app.js",
520
+ detectTls: true,
521
+ })[0]
522
+
523
+ expect(() => plugin.config({}, { command: "serve", mode: "development" })).toThrow(/Unable to find the configuration file/)
524
+ })
525
+ })
416
526
  describe("inertia-helpers", () => {
417
- const path = "./__data__/dummy.ts"
527
+ const testPath = "./__data__/dummy.ts"
528
+
529
+ beforeEach(() => {
530
+ vi.resetModules()
531
+ // Mock the import.meta.glob functionality
532
+ vi.mock("./__data__/dummy.ts", () => ({
533
+ default: "Dummy File",
534
+ }))
535
+ })
536
+
418
537
  it("pass glob value to resolvePageComponent", async () => {
419
- const file = await resolvePageComponent<{ default: string }>(
420
- path,
421
- /* @ts-ignore */
422
- import.meta.glob("./__data__/*.ts"),
423
- )
538
+ const pages = {
539
+ [testPath]: Promise.resolve({ default: "Dummy File" }),
540
+ }
541
+
542
+ const file = await resolvePageComponent<{ default: string }>(testPath, pages)
424
543
  expect(file.default).toBe("Dummy File")
425
544
  })
426
545
 
427
546
  it("pass eagerly globed value to resolvePageComponent", async () => {
428
- const file = await resolvePageComponent<{ default: string }>(
429
- path,
430
- /* @ts-ignore */
431
- import.meta.glob("./__data__/*.ts", { eager: true }),
432
- )
547
+ const pages = {
548
+ [testPath]: { default: "Dummy File" },
549
+ }
550
+ // @ts-ignore
551
+ const file = await resolvePageComponent<{ default: string }>(testPath, pages)
433
552
  expect(file.default).toBe("Dummy File")
434
553
  })
435
554
 
436
555
  it("accepts array of paths", async () => {
556
+ const pages = {
557
+ [testPath]: { default: "Dummy File" },
558
+ }
559
+
437
560
  const file = await resolvePageComponent<{ default: string }>(
438
- ["missing-page", path],
439
- /* @ts-ignore */
440
- import.meta.glob("./__data__/*.ts", { eager: true }),
441
- /* @ts-ignore */
442
- path,
561
+ ["missing-page", testPath],
562
+ // @ts-ignore
563
+ pages,
443
564
  )
444
565
  expect(file.default).toBe("Dummy File")
445
566
  })
446
567
 
447
568
  it("throws an error when a page is not found", async () => {
448
- const callback = () =>
449
- resolvePageComponent<{ default: string }>(
450
- "missing-page",
451
- /* @ts-ignore */
452
- import.meta.glob("./__data__/*.ts"),
453
- )
454
- await expect(callback).rejects.toThrowError(new Error("Page not found: missing-page"))
569
+ const pages = {}
570
+ await expect(resolvePageComponent<{ default: string }>("missing-page", pages)).rejects.toThrow("Page not found: missing-page")
455
571
  })
456
572
 
457
- it("throws an error when a page is not found", async () => {
458
- const callback = () =>
459
- resolvePageComponent<{ default: string }>(
460
- ["missing-page-1", "missing-page-2"],
461
- /* @ts-ignore */
462
- import.meta.glob("./__data__/*.ts"),
463
- )
464
- await expect(callback).rejects.toThrowError(new Error("Page not found: missing-page-1,missing-page-2"))
465
- })
466
- })
467
-
468
- describe("route helpers", () => {
469
- beforeEach(() => {
470
- // Setup mock routes based on the provided JSON
471
- globalThis.routes = {
472
- home: "/",
473
- about: "/about",
474
- "users:assign-role": "/api/roles/{role_slug:str}/assign",
475
- "users:revoke-role": "/api/roles/{role_slug:str}/revoke",
476
- "tags:create": "/api/tags",
477
- "tags:list": "/api/tags",
478
- "tags:get": "/api/tags/{tag_id:uuid}",
479
- "tags:update": "/api/tags/{tag_id:uuid}",
480
- "tags:delete": "/api/tags/{tag_id:uuid}",
481
- "teams:add-member": "/api/teams/{team_id:uuid}/members/add",
482
- "teams:remove-member": "/api/teams/{team_id:uuid}/members/remove",
483
- "users:create": "/api/users/create",
484
- "users:list": "/api/users/list",
485
- "users:update": "/api/users/update/{user_id:uuid}",
486
- "users:delete": "/api/users/delete/{user_id:uuid}",
487
- "users:get": "/api/users/get/{user_id:uuid}",
488
- dashboard: "/dashboard",
489
- favicon: "/favicon.ico",
490
- landing: "/landing",
491
- "login.check": "/login/check",
492
- login: "/login",
493
- logout: "/logout",
494
- "github.complete": "/o/github/complete",
495
- "google.complete": "/o/google/complete",
496
- "privacy-policy": "/privacy-policy",
497
- "account.remove": "/profile/remove",
498
- "profile.show": "/profile",
499
- "profile.update": "/profile/update",
500
- "password.update": "/profile/password-update",
501
- register: "/register",
502
- "register.add": "/register/add",
503
- "github.register": "/register/github",
504
- "google.register": "/register/google",
505
- "worker:index": "/saq/queues/{queue_id:str}/jobs/{job_id:str}",
506
- "worker:queue-list": "/saq/api/queues/list",
507
- "worker:queue-detail": "/saq/api/queues/{queue_id:str}",
508
- "worker:job-detail": "/saq/api/queues/{queue_id:str}/jobs/{job_id:str}",
509
- "worker:job-abort": "/saq/api/queues/{queue_id:str}/jobs/{job_id:str}/abort",
510
- "worker:job-retry": "/saq/api/queues/{queue_id:str}/jobs/{job_id:str}/retry",
511
- saq: "/saq/static/{file_path:path}",
512
- vite: "/static/{file_path:path}",
513
- "teams.add": "/teams/add",
514
- "teams.list": "/teams/list",
515
- "teams.show": "/teams/show/{team_id:uuid}",
516
- "teams.remove": "/teams/remove/{team_id:uuid}",
517
- "teams.edit": "/teams/edit/{team_id:uuid}",
518
- "terms-of-service": "/terms-of-service",
519
- }
520
- })
521
-
522
- describe("route()", () => {
523
- it("generates URLs from route names with named parameters", () => {
524
- expect(route("users:get", { user_id: "123e4567-e89b-12d3-a456-426614174000" })).toContain("/api/users/get/123e4567-e89b-12d3-a456-426614174000")
573
+ describe("route() edge cases", () => {
574
+ it("handles missing route names", () => {
575
+ expect(route("non-existent-route")).toBe("#")
576
+ })
525
577
 
526
- expect(route("worker:queue-detail", { queue_id: "default" })).toContain("/saq/api/queues/default")
578
+ it("handles array arguments", () => {
579
+ const result = route("users:get", ["123e4567-e89b-12d3-a456-426614174000"])
580
+ expect(result).toContain("/api/users/get/123e4567-e89b-12d3-a456-426614174000")
527
581
  })
528
582
 
529
- it('returns "#" for invalid routes', () => {
530
- expect(route("invalid.route")).toBe("#")
583
+ it("handles wrong number of arguments", () => {
584
+ expect(route("users:get", [])).toBe("#")
531
585
  })
532
586
 
533
- it("handles missing parameters", () => {
534
- expect(route("users:get")).toBe("#")
587
+ it("handles missing arguments in object", () => {
588
+ expect(route("users:get", { wrong_id: "123" })).toBe("#")
535
589
  })
536
590
  })
537
591
 
538
592
  describe("getRelativeUrlPath()", () => {
539
- it("extracts relative path from full URL", () => {
540
- expect(getRelativeUrlPath("http://example.com/api/users/get/123e4567-e89b-12d3-a456-426614174000?page=1#section")).toBe(
541
- "/api/users/get/123e4567-e89b-12d3-a456-426614174000?page=1#section",
542
- )
593
+ it("handles invalid URLs", () => {
594
+ expect(getRelativeUrlPath("invalid-url")).toBe("invalid-url")
543
595
  })
544
596
 
545
- it("returns original string for relative paths", () => {
546
- expect(getRelativeUrlPath("/api/users/get/123e4567-e89b-12d3-a456-426614174000")).toBe("/api/users/get/123e4567-e89b-12d3-a456-426614174000")
597
+ it("preserves query parameters and hash", () => {
598
+ expect(getRelativeUrlPath("http://example.com/path?query=1#hash")).toBe("/path?query=1#hash")
547
599
  })
548
600
  })
549
601
 
550
602
  describe("toRoute()", () => {
551
- it("matches URLs to route names", () => {
552
- expect(toRoute("/api/users/list")).toBe("users:list")
603
+ it("handles root path", () => {
604
+ expect(toRoute("/")).toBe("home")
605
+ })
606
+
607
+ it("handles UUID parameters", () => {
553
608
  expect(toRoute("/api/users/get/123e4567-e89b-12d3-a456-426614174000")).toBe("users:get")
554
- expect(toRoute("/teams/list")).toBe("teams.list")
555
- expect(toRoute("/teams/show/123e4567-e89b-12d3-a456-426614174000")).toBe("teams.show")
556
- expect(toRoute("/saq/api/queues/list")).toBe("worker:queue-list")
557
- expect(toRoute("/saq/api/queues/default/jobs/123")).toBe("worker:job-detail")
558
- expect(toRoute("/saq/static/path/to/file.pdf")).toBe("saq")
559
609
  })
560
610
 
561
- it("returns null for unmatched routes", () => {
562
- expect(toRoute("/not/a/real/route")).toBeNull()
611
+ it("handles path parameters", () => {
612
+ expect(toRoute("/saq/static/some/deep/path")).toBe("saq")
613
+ })
614
+
615
+ it("handles non-matching routes", () => {
616
+ expect(toRoute("/non-existent")).toBe(null)
617
+ })
618
+
619
+ it("handles trailing slashes", () => {
620
+ expect(toRoute("/api/users/list/")).toBe("users:list")
563
621
  })
564
622
  })
565
623
 
566
624
  describe("currentRoute()", () => {
567
- it("returns current route name based on window location", () => {
625
+ beforeEach(() => {
568
626
  // Mock window.location
569
627
  Object.defineProperty(window, "location", {
570
- value: { pathname: "/api/users/get/123e4567-e89b-12d3-a456-426614174000" },
628
+ value: {
629
+ pathname: "/api/users/list",
630
+ },
571
631
  writable: true,
572
632
  })
573
-
574
- expect(currentRoute()).toBe("users:get")
575
633
  })
576
634
 
577
- it("returns null when current location does not match any route", () => {
578
- Object.defineProperty(window, "location", {
579
- value: { pathname: "/not/a/real/route" },
580
- writable: true,
581
- })
635
+ it("returns current route name", () => {
636
+ expect(currentRoute()).toBe("users:list")
637
+ })
582
638
 
583
- expect(currentRoute()).toBeNull()
639
+ it("returns null for non-matching routes", () => {
640
+ window.location.pathname = "/non-existent"
641
+ expect(currentRoute()).toBe(null)
584
642
  })
585
643
  })
586
644
 
587
645
  describe("isRoute()", () => {
588
- it("checks if URL matches route pattern", () => {
589
- expect(isRoute("/api/users/get/123e4567-e89b-12d3-a456-426614174000", "users:get")).toBe(true)
590
- expect(isRoute("/invalid/path", "users:get")).toBe(false)
646
+ it("matches exact routes", () => {
647
+ expect(isRoute("/api/users/list", "users:list")).toBe(true)
648
+ })
649
+
650
+ it("matches routes with parameters", () => {
651
+ expect(isRoute("/api/users/get/123e4567-e89b-12d3-a456-426614174000", "users:*")).toBe(true)
652
+ })
653
+
654
+ it("handles non-matching routes", () => {
655
+ expect(isRoute("/non-existent", "users:*")).toBe(false)
656
+ })
657
+
658
+ it("matches routes with path parameters", () => {
659
+ expect(isRoute("/saq/static/deep/nested/path", "saq")).toBe(true)
591
660
  })
592
661
  })
593
662
 
594
663
  describe("isCurrentRoute()", () => {
595
- it("checks if current location matches route pattern", () => {
664
+ beforeEach(() => {
665
+ // Mock window.location
596
666
  Object.defineProperty(window, "location", {
597
- value: { pathname: "/api/users/get/123e4567-e89b-12d3-a456-426614174000" },
667
+ value: {
668
+ pathname: "/api/users/list",
669
+ },
598
670
  writable: true,
599
671
  })
672
+ })
600
673
 
601
- expect(isCurrentRoute("users:get")).toBe(true)
602
- expect(isCurrentRoute("users.*")).toBe(true)
674
+ it("matches current route with pattern", () => {
675
+ expect(isCurrentRoute("users:*")).toBe(true)
603
676
  })
604
677
 
605
- it("returns false when current location does not match route pattern", () => {
606
- Object.defineProperty(window, "location", {
607
- value: { pathname: "/not/a/real/route" },
608
- writable: true,
609
- })
678
+ it("handles exact matches", () => {
679
+ expect(isCurrentRoute("users:list")).toBe(true)
680
+ })
681
+
682
+ it("handles non-matching routes", () => {
683
+ expect(isCurrentRoute("teams:*")).toBe(false)
684
+ })
610
685
 
611
- expect(isCurrentRoute("users:get")).toBe(false)
686
+ it("handles invalid current route", () => {
687
+ window.location.pathname = "/non-existent"
688
+ expect(isCurrentRoute("users:*")).toBe(false)
612
689
  })
613
690
  })
614
691
  })
@@ -14,7 +14,7 @@ DEFAULT_RESOURCES: set[str] = {"styles.css.j2", "main.ts.j2"}
14
14
  DEFAULT_DEV_DEPENDENCIES: dict[str, str] = {
15
15
  "typescript": "^5.7.2",
16
16
  "vite": "^6.0.3",
17
- "litestar-vite-plugin": "^0.8.2",
17
+ "litestar-vite-plugin": "^0.9.0",
18
18
  "@types/node": "^22.10.1",
19
19
  }
20
20
  DEFAULT_DEPENDENCIES: dict[str, str] = {"axios": "^1.7.2"}
File without changes
File without changes
File without changes