litestar-vite 0.13.1__tar.gz → 0.13.2__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 (80) hide show
  1. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/.gitignore +5 -0
  2. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/PKG-INFO +1 -1
  3. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/pyproject.toml +3 -2
  4. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/js/src/index.ts +12 -8
  5. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/js/tests/index.test.ts +166 -102
  6. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/litestar_vite/cli.py +0 -2
  7. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/litestar_vite/commands.py +5 -5
  8. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/litestar_vite/inertia/config.py +2 -2
  9. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/litestar_vite/loader.py +3 -1
  10. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/litestar_vite/plugin.py +2 -6
  11. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/LICENSE +0 -0
  12. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/README.md +0 -0
  13. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/js/LICENSE +0 -0
  14. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/js/Makefile +0 -0
  15. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/js/NOTICE +0 -0
  16. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/js/README.md +0 -0
  17. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/js/src/dev-server-index.html +0 -0
  18. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/js/src/inertia-helpers/index.ts +0 -0
  19. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/js/tests/__data__/dummy.ts +0 -0
  20. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/js/tsconfig.inertia-helpers.json +0 -0
  21. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/js/tsconfig.json +0 -0
  22. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/js/vitest.config.ts +0 -0
  23. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/js/vitest.workspace.ts +0 -0
  24. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/litestar_vite/__init__.py +0 -0
  25. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/litestar_vite/__metadata__.py +0 -0
  26. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/litestar_vite/config.py +0 -0
  27. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/litestar_vite/inertia/__init__.py +0 -0
  28. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/litestar_vite/inertia/_utils.py +0 -0
  29. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/litestar_vite/inertia/exception_handler.py +0 -0
  30. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/litestar_vite/inertia/helpers.py +0 -0
  31. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/litestar_vite/inertia/middleware.py +0 -0
  32. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/litestar_vite/inertia/plugin.py +0 -0
  33. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/litestar_vite/inertia/request.py +0 -0
  34. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/litestar_vite/inertia/response.py +0 -0
  35. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/litestar_vite/inertia/routes.py +0 -0
  36. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/litestar_vite/inertia/types.py +0 -0
  37. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/litestar_vite/py.typed +0 -0
  38. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/litestar_vite/templates/__init__.py +0 -0
  39. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/litestar_vite/templates/index.html.j2 +0 -0
  40. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/litestar_vite/templates/main.ts.j2 +0 -0
  41. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/litestar_vite/templates/package.json.j2 +0 -0
  42. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/litestar_vite/templates/styles.css.j2 +0 -0
  43. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/litestar_vite/templates/tsconfig.json.j2 +0 -0
  44. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/litestar_vite/templates/vite.config.ts.j2 +0 -0
  45. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/__init__.py +0 -0
  46. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/conftest.py +0 -0
  47. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/py.typed +0 -0
  48. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/templates/__init__.py +0 -0
  49. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/templates/index.html.j2 +0 -0
  50. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/test_app/__init__.py +0 -0
  51. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/test_app/app.py +0 -0
  52. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/test_app/web/__init__.py +0 -0
  53. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/test_app/web/public/.gitkeep +0 -0
  54. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/test_app/web/public/assets/main-l0sNRNKZ.js +0 -0
  55. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/test_app/web/public/assets/styles-l0sNRNKZ.js +0 -0
  56. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/test_app/web/public/manifest.json +0 -0
  57. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/test_app/web/resources/.gitkeep +0 -0
  58. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/test_app/web/resources/main.ts +0 -0
  59. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/test_app/web/resources/styles.css +0 -0
  60. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/test_app/web/templates/.gitkeep +0 -0
  61. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/test_app/web/templates/index.html +0 -0
  62. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/test_asset_loader.py +0 -0
  63. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/test_cli/__init__.py +0 -0
  64. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/test_cli/conftest.py +0 -0
  65. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/test_cli/test_init.py +0 -0
  66. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/test_commands.py +0 -0
  67. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/test_config.py +0 -0
  68. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/test_inertia/__init__.py +0 -0
  69. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/test_inertia/conftest.py +0 -0
  70. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/test_inertia/templates/index.html.j2 +0 -0
  71. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/test_inertia/test_request.py +0 -0
  72. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/test_inertia/test_response.py +0 -0
  73. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/src/py/tests/test_inertia/test_routes.py +0 -0
  74. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/tools/__init__.py +0 -0
  75. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/tools/build_docs.py +0 -0
  76. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/tools/clean.js +0 -0
  77. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/tools/pypi_readme.py +0 -0
  78. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/tools/sphinx_ext/__init__.py +0 -0
  79. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/tools/sphinx_ext/changelog.py +0 -0
  80. {litestar_vite-0.13.1 → litestar_vite-0.13.2}/tools/sphinx_ext/missing_references.py +0 -0
@@ -167,3 +167,8 @@ cython_debug/
167
167
 
168
168
  node_modules
169
169
  .aider*
170
+
171
+ .DS_Store
172
+ *.swo
173
+ *.swp
174
+ .venv
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: litestar-vite
3
- Version: 0.13.1
3
+ Version: 0.13.2
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.9"
28
- version = "0.13.1"
28
+ version = "0.13.2"
29
29
 
30
30
  [project.urls]
31
31
  Changelog = "https://cofin.github.io/litestar-vite/latest/changelog"
@@ -83,7 +83,7 @@ test = [
83
83
  allow_dirty = true
84
84
  commit = true
85
85
  commit_args = "--no-verify"
86
- current_version = "0.13.1"
86
+ current_version = "0.13.2"
87
87
  ignore_missing_files = false
88
88
  ignore_missing_version = false
89
89
  message = "chore(release): bump to v{new_version}"
@@ -279,6 +279,7 @@ lint.ignore = [
279
279
  "PLC2701", # private import
280
280
  "S704",
281
281
  "S404",
282
+ "PLR6301"
282
283
  ]
283
284
  lint.select = ["ALL"]
284
285
  src = ["src/py/litestar_vite", "src/py/tests"]
@@ -291,17 +291,12 @@ function resolveLitestarPlugin(pluginConfig: Required<PluginConfig>): LitestarPl
291
291
  // Run middleware early to intercept before Vite's base/HTML handlers
292
292
  server.middlewares.use(async (req, res, next) => {
293
293
  const indexPath = await findIndexHtmlPath(server, pluginConfig)
294
-
295
- // Check if index.html exists AND the request is for the root or /index.html
296
294
  if (indexPath && (req.url === "/" || req.url === "/index.html")) {
295
+ const currentUrl = req.url
297
296
  try {
298
297
  const htmlContent = await fs.promises.readFile(indexPath, "utf-8")
299
298
  // Transform the HTML using Vite's pipeline
300
- const transformedHtml = await server.transformIndexHtml(
301
- "/", // Use '/' as the URL for transformation context to ensure scripts are injected correctly relative to root
302
- htmlContent,
303
- req.originalUrl,
304
- )
299
+ const transformedHtml = await server.transformIndexHtml(req.originalUrl ?? currentUrl, htmlContent, req.originalUrl)
305
300
  res.statusCode = 200
306
301
  res.setHeader("Content-Type", "text/html")
307
302
  res.end(transformedHtml)
@@ -673,5 +668,14 @@ function resolveDevelopmentEnvironmentTld(configPath: string): string {
673
668
  * The directory of the current file.
674
669
  */
675
670
  function dirname(): string {
676
- return fileURLToPath(new URL(".", import.meta.url))
671
+ // Use path.resolve relative to process.cwd() as a more robust alternative
672
+ // Assumes the script runs from the project root or similar predictable location.
673
+ // Adjust the relative path if necessary based on actual execution context.
674
+ try {
675
+ // Attempt original method first
676
+ return fileURLToPath(new URL(".", import.meta.url))
677
+ } catch {
678
+ // Fallback for environments where import.meta.url is problematic (like some test runners)
679
+ return path.resolve(process.cwd(), "src/js/src")
680
+ }
677
681
  }
@@ -1,4 +1,5 @@
1
1
  import fs from "node:fs"
2
+ import path from "node:path"
2
3
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
3
4
  import litestar from "../src"
4
5
  import { getRelativeUrlPath, isCurrentRoute, isRoute, resolvePageComponent, route, toRoute } from "../src/inertia-helpers"
@@ -8,22 +9,35 @@ vi.mock("fs", async () => {
8
9
  const actual = await vi.importActual<typeof import("fs")>("fs")
9
10
 
10
11
  return {
12
+ promises: actual.promises,
11
13
  default: {
12
14
  ...actual,
13
15
  existsSync: (path: string) => ["resources/", "assets/", "src/"].includes(path) || actual.existsSync(path),
16
+ readFileSync: actual.readFileSync,
17
+ mkdirSync: actual.mkdirSync,
18
+ writeFileSync: actual.writeFileSync,
19
+ rmSync: actual.rmSync,
14
20
  },
15
21
  }
16
22
  })
23
+
24
+ // Read actual placeholder content for assertions
25
+ const actualPlaceholderContent = fs.readFileSync(path.resolve(__dirname, "../src/dev-server-index.html"), "utf-8")
26
+
17
27
  // Mock process.env
18
28
  const originalEnv = process.env
19
29
  beforeEach(() => {
20
30
  vi.resetModules()
21
31
  process.env = { ...originalEnv }
32
+ vi.clearAllMocks()
33
+ vi.spyOn(fs.promises, "access").mockRestore()
34
+ vi.spyOn(fs.promises, "readFile").mockRestore()
22
35
  })
23
36
 
24
37
  afterEach(() => {
25
38
  process.env = originalEnv
26
39
  vi.clearAllMocks()
40
+ vi.restoreAllMocks()
27
41
  })
28
42
 
29
43
  // Mock routes for testing
@@ -526,159 +540,217 @@ describe("litestar-vite-plugin", () => {
526
540
  let mockRes: any
527
541
  let mockNext: any
528
542
  let plugin: any
543
+ let serverHook: any
544
+ const testRootDir = "/test/root"
545
+ const testResourceDir = "resources"
546
+ const testPublicDir = "public"
547
+
548
+ const rootIndexPath = path.join(testRootDir, "index.html")
549
+ const resourceIndexPath = path.join(testRootDir, testResourceDir, "index.html")
550
+ const publicIndexPath = path.join(testRootDir, testPublicDir, "index.html")
551
+ // Use original placeholder path logic
552
+ const placeholderPath = path.resolve(__dirname, "..", "src", "dev-server-index.html")
529
553
 
530
554
  beforeEach(() => {
531
- // Mock Vite dev server with complete config
555
+ vi.clearAllMocks()
556
+ })
557
+
558
+ const setupServer = async (pluginOptions = {}, serverConfig = {}) => {
532
559
  mockServer = {
533
560
  config: {
534
- root: "/test/root",
561
+ root: testRootDir,
535
562
  envDir: process.cwd(),
536
563
  mode: "development",
537
564
  base: "/",
538
565
  server: {
539
566
  origin: "http://localhost:5173",
540
567
  },
568
+ logger: { info: vi.fn(), error: vi.fn() },
569
+ ...serverConfig,
541
570
  },
542
- transformIndexHtml: vi.fn().mockResolvedValue("<html>transformed</html>"),
571
+ transformIndexHtml: vi.fn().mockImplementation(async (_url, html) => `<html>transformed ${html}</html>`),
543
572
  middlewares: {
544
573
  use: vi.fn().mockImplementation((middleware) => {
545
574
  mockMiddleware = middleware
546
575
  }),
547
576
  },
548
577
  }
549
-
550
- // Mock response object
551
578
  mockRes = {
552
579
  statusCode: 0,
553
580
  setHeader: vi.fn(),
554
581
  end: vi.fn(),
555
582
  }
556
-
557
- // Mock next function
558
583
  mockNext = vi.fn()
559
584
 
560
- // Mock fs promises
561
- vi.spyOn(fs.promises, "access").mockResolvedValue(undefined)
562
- vi.spyOn(fs.promises, "readFile").mockResolvedValue("<html>test</html>")
563
-
564
- // Initialize plugin and call configResolved
565
585
  plugin = litestar({
566
586
  input: "resources/js/app.js",
567
- resourceDirectory: "resources",
587
+ resourceDirectory: testResourceDir,
588
+ autoDetectIndex: true,
589
+ ...pluginOptions,
568
590
  })[0]
569
591
 
570
- // Call configResolved to set up resolvedConfig
571
592
  plugin.configResolved?.(mockServer.config)
572
- })
573
593
 
574
- it("serves index.html when detected", async () => {
575
- // Mock findIndexHtmlPath to return a path
576
- vi.spyOn(fs.promises, "access").mockResolvedValueOnce(undefined)
594
+ const hookResult = await plugin.configureServer?.(mockServer)
595
+ serverHook = typeof hookResult === "function" ? hookResult : hookResult?.()
577
596
 
578
- // Configure the server
579
- const serverHook = await plugin.configureServer?.(mockServer)
580
- // @ts-ignore - Server hook can be a function or object with handler
581
- if (serverHook && typeof serverHook === "function") {
582
- await serverHook()
597
+ if (typeof serverHook === "function") {
598
+ serverHook()
583
599
  }
600
+ }
584
601
 
585
- // Call the middleware with root path
586
- await mockMiddleware(
587
- {
588
- url: "/",
589
- originalUrl: "/",
590
- },
591
- mockRes,
592
- mockNext,
593
- )
602
+ const mockFs = (foundPath: string | null) => {
603
+ // Only mock access here
604
+ vi.spyOn(fs.promises, "access").mockImplementation(async (p) => {
605
+ if (p === foundPath) return undefined
606
+ throw new Error("File not found")
607
+ })
608
+ }
609
+
610
+ it("serves index.html from root when detected", async () => {
611
+ await setupServer()
612
+ mockFs(rootIndexPath)
613
+ vi.spyOn(fs.promises, "readFile").mockResolvedValue("<html>root</html>")
614
+ await mockMiddleware({ url: "/", originalUrl: "/" }, mockRes, mockNext)
594
615
 
595
- // Verify the response
596
616
  expect(mockRes.statusCode).toBe(200)
597
617
  expect(mockRes.setHeader).toHaveBeenCalledWith("Content-Type", "text/html")
598
- expect(mockRes.end).toHaveBeenCalledWith("<html>transformed</html>")
618
+ expect(mockServer.transformIndexHtml).toHaveBeenCalledWith("/", "<html>root</html>", "/")
619
+ expect(mockRes.end).toHaveBeenCalledWith("<html>transformed <html>root</html></html>")
599
620
  expect(mockNext).not.toHaveBeenCalled()
600
621
  })
601
622
 
602
- it("serves placeholder when index.html is not detected", async () => {
603
- // Mock findIndexHtmlPath to return null
604
- vi.spyOn(fs.promises, "access").mockRejectedValueOnce(new Error("File not found"))
605
-
606
- // Mock the placeholder file read
607
- const placeholderContent = `<html>
608
- <head>
609
- <title>Litestar Vite</title>
610
- </head>
611
- <body>
612
- <div id="app"></div>
613
- </body>
614
- </html>`
615
-
616
- // Mock transformIndexHtml to only transform non-placeholder content
617
- mockServer.transformIndexHtml = vi.fn().mockImplementation((url, html) => {
618
- if (html.includes("Litestar Vite")) {
619
- return html // Don't transform placeholder content
620
- }
621
- return "<html>transformed</html>" // Transform other content
622
- })
623
+ it("serves index.html from resource directory when detected", async () => {
624
+ await setupServer()
625
+ mockFs(resourceIndexPath)
626
+ vi.spyOn(fs.promises, "readFile").mockResolvedValue("<html>resource</html>")
627
+ await mockMiddleware({ url: "/index.html", originalUrl: "/index.html" }, mockRes, mockNext)
623
628
 
624
- vi.spyOn(fs.promises, "readFile").mockResolvedValueOnce(placeholderContent)
629
+ expect(mockRes.statusCode).toBe(200)
630
+ expect(mockRes.setHeader).toHaveBeenCalledWith("Content-Type", "text/html")
631
+ expect(mockServer.transformIndexHtml).toHaveBeenCalledWith("/index.html", "<html>resource</html>", "/index.html")
632
+ expect(mockRes.end).toHaveBeenCalledWith("<html>transformed <html>resource</html></html>")
633
+ expect(mockNext).not.toHaveBeenCalled()
634
+ })
625
635
 
626
- // Configure the server
627
- const serverHook = await plugin.configureServer?.(mockServer)
628
- // @ts-ignore - Server hook can be a function or object with handler
629
- if (serverHook && typeof serverHook === "function") {
630
- await serverHook()
631
- }
636
+ it("serves index.html from public directory when detected", async () => {
637
+ await setupServer()
638
+ mockFs(publicIndexPath)
639
+ vi.spyOn(fs.promises, "readFile").mockResolvedValue("<html>public</html>")
640
+ await mockMiddleware({ url: "/", originalUrl: "/" }, mockRes, mockNext)
632
641
 
633
- // Call the middleware with /index.html path
634
- await mockMiddleware(
635
- {
636
- url: "/index.html",
637
- originalUrl: "/index.html",
638
- },
639
- mockRes,
640
- mockNext,
641
- )
642
+ expect(mockRes.statusCode).toBe(200)
643
+ expect(mockRes.setHeader).toHaveBeenCalledWith("Content-Type", "text/html")
644
+ expect(mockServer.transformIndexHtml).toHaveBeenCalledWith("/", "<html>public</html>", "/")
645
+ expect(mockRes.end).toHaveBeenCalledWith("<html>transformed <html>public</html></html>")
646
+ expect(mockNext).not.toHaveBeenCalled()
647
+ })
648
+
649
+ it("serves placeholder when index.html is not detected and url is /index.html", async () => {
650
+ const appUrl = "http://test.app"
651
+ process.env.APP_URL = appUrl // Set env BEFORE setupServer
652
+ await setupServer()
653
+ mockFs(null)
654
+ // No specific readFile mock needed, relies on actual file read via robust dirname
655
+
656
+ await mockMiddleware({ url: "/index.html", originalUrl: "/index.html" }, mockRes, mockNext)
642
657
 
643
- // Verify the response
644
658
  expect(mockRes.statusCode).toBe(200)
645
659
  expect(mockRes.setHeader).toHaveBeenCalledWith("Content-Type", "text/html")
646
- expect(mockRes.end).toHaveBeenCalledWith(expect.stringContaining("Litestar Vite"))
660
+ // Expect actual content with replaced URL
661
+ expect(mockRes.end).toHaveBeenCalledWith(actualPlaceholderContent.replace(/{{ APP_URL }}/g, appUrl))
662
+ expect(mockServer.transformIndexHtml).not.toHaveBeenCalled()
647
663
  expect(mockNext).not.toHaveBeenCalled()
648
664
  })
649
665
 
650
- it("passes through non-index.html requests", async () => {
651
- // Configure the server
652
- const serverHook = await plugin.configureServer?.(mockServer)
653
- // @ts-ignore - Server hook can be a function or object with handler
654
- if (serverHook && typeof serverHook === "function") {
655
- await serverHook()
656
- }
666
+ it("calls next() when index.html is not detected and url is /", async () => {
667
+ await setupServer()
668
+ mockFs(null)
657
669
 
658
- // Call the middleware with a non-index path
659
- await mockMiddleware(
660
- {
661
- url: "/other",
662
- originalUrl: "/other",
663
- },
664
- mockRes,
665
- mockNext,
666
- )
670
+ await mockMiddleware({ url: "/", originalUrl: "/" }, mockRes, mockNext)
671
+
672
+ expect(mockRes.statusCode).toBe(0)
673
+ expect(mockRes.setHeader).not.toHaveBeenCalled()
674
+ expect(mockRes.end).not.toHaveBeenCalled()
675
+ expect(mockNext).toHaveBeenCalledTimes(1)
676
+ })
677
+
678
+ it("calls next() for non-root and non-/index.html requests", async () => {
679
+ await setupServer()
680
+ mockFs(rootIndexPath)
681
+
682
+ await mockMiddleware({ url: "/other/path", originalUrl: "/other/path" }, mockRes, mockNext)
667
683
 
668
- // Verify the request was passed through
669
684
  expect(mockRes.statusCode).toBe(0)
670
685
  expect(mockRes.setHeader).not.toHaveBeenCalled()
671
686
  expect(mockRes.end).not.toHaveBeenCalled()
672
- expect(mockNext).toHaveBeenCalled()
687
+ expect(mockNext).toHaveBeenCalledTimes(1)
688
+ })
689
+
690
+ it("calls next() for / when autoDetectIndex is false, even if index exists", async () => {
691
+ await setupServer({ autoDetectIndex: false })
692
+ mockFs(rootIndexPath)
693
+
694
+ await mockMiddleware({ url: "/", originalUrl: "/" }, mockRes, mockNext)
695
+
696
+ expect(mockRes.statusCode).toBe(0)
697
+ expect(mockRes.setHeader).not.toHaveBeenCalled()
698
+ expect(mockRes.end).not.toHaveBeenCalled()
699
+ expect(mockNext).toHaveBeenCalledTimes(1)
700
+ })
701
+
702
+ it("serves placeholder for /index.html when autoDetectIndex is false", async () => {
703
+ const appUrl = "http://test.app:8000"
704
+ process.env.APP_URL = appUrl // Set env BEFORE setupServer
705
+ await setupServer({ autoDetectIndex: false })
706
+ mockFs(null)
707
+ // No specific readFile mock needed, relies on actual file read via robust dirname
708
+
709
+ await mockMiddleware({ url: "/index.html", originalUrl: "/index.html" }, mockRes, mockNext)
710
+
711
+ expect(mockRes.statusCode).toBe(200)
712
+ expect(mockRes.setHeader).toHaveBeenCalledWith("Content-Type", "text/html")
713
+ // Expect actual content with replaced URL
714
+ expect(mockRes.end).toHaveBeenCalledWith(actualPlaceholderContent.replace(/{{ APP_URL }}/g, appUrl))
715
+ expect(mockNext).not.toHaveBeenCalled()
716
+ })
717
+
718
+ it("handles errors during index.html reading", async () => {
719
+ await setupServer()
720
+ mockFs(rootIndexPath) // Access mock allows finding the path
721
+ // No specific readFile mock needed, let actual read fail
722
+
723
+ await mockMiddleware({ url: "/", originalUrl: "/" }, mockRes, mockNext)
724
+
725
+ // Check for any error and specific code
726
+ expect(mockNext).toHaveBeenCalledWith(expect.any(Error))
727
+ expect(mockNext.mock.calls[0][0].code).toBe("ENOENT")
728
+ expect(mockRes.end).not.toHaveBeenCalled()
729
+ expect(mockServer.config.logger.error).toHaveBeenCalledWith(expect.stringContaining("Error serving index.html"))
730
+ })
731
+
732
+ it("handles errors during placeholder reading", async () => {
733
+ await setupServer()
734
+ mockFs(null) // Access mock prevents finding a path
735
+ const placeholderError = new Error("Cannot read placeholder")
736
+ // Specific mock to force placeholder read error
737
+ vi.spyOn(fs.promises, "readFile").mockRejectedValue(placeholderError)
738
+
739
+ await mockMiddleware({ url: "/index.html", originalUrl: "/index.html" }, mockRes, mockNext)
740
+
741
+ expect(mockRes.statusCode).toBe(404)
742
+ expect(mockRes.end).toHaveBeenCalledWith(expect.stringContaining("Error loading placeholder"))
743
+ expect(mockNext).not.toHaveBeenCalled()
744
+ expect(mockServer.config.logger.error).toHaveBeenCalledWith(expect.stringContaining("Error serving placeholder index.html"))
673
745
  })
674
746
  })
675
747
  })
748
+
676
749
  describe("inertia-helpers", () => {
677
750
  const testPath = "./__data__/dummy.ts"
678
751
 
679
752
  beforeEach(() => {
680
753
  vi.resetModules()
681
- // Mock the import.meta.glob functionality
682
754
  vi.mock("./__data__/dummy.ts", () => ({
683
755
  default: "Dummy File",
684
756
  }))
@@ -697,8 +769,7 @@ describe("inertia-helpers", () => {
697
769
  const pages = {
698
770
  [testPath]: { default: "Dummy File" },
699
771
  }
700
- // @ts-ignore
701
- const file = await resolvePageComponent<{ default: string }>(testPath, pages)
772
+ const file = await resolvePageComponent<{ default: string }>(testPath, pages as any)
702
773
  expect(file.default).toBe("Dummy File")
703
774
  })
704
775
 
@@ -706,12 +777,7 @@ describe("inertia-helpers", () => {
706
777
  const pages = {
707
778
  [testPath]: { default: "Dummy File" },
708
779
  }
709
-
710
- const file = await resolvePageComponent<{ default: string }>(
711
- ["missing-page", testPath],
712
- // @ts-ignore
713
- pages,
714
- )
780
+ const file = await resolvePageComponent<{ default: string }>(["missing-page", testPath], pages as any)
715
781
  expect(file.default).toBe("Dummy File")
716
782
  })
717
783
 
@@ -773,7 +839,6 @@ describe("inertia-helpers", () => {
773
839
 
774
840
  describe("currentRoute()", () => {
775
841
  beforeEach(() => {
776
- // Mock window.location
777
842
  Object.defineProperty(window, "location", {
778
843
  value: {
779
844
  pathname: "/api/users/list",
@@ -812,7 +877,6 @@ describe("inertia-helpers", () => {
812
877
 
813
878
  describe("isCurrentRoute()", () => {
814
879
  beforeEach(() => {
815
- // Mock window.location
816
880
  Object.defineProperty(window, "location", {
817
881
  value: {
818
882
  pathname: "/api/users/list",
@@ -168,7 +168,6 @@ def vite_init(
168
168
  )
169
169
  install_dir = os.environ.get("VIRTUAL_ENV", sys.prefix)
170
170
  console.rule("[yellow]Starting Nodeenv installation process[/]", align="left")
171
- console.print(f"Installing Node environment into {install_dir}")
172
171
  execute_command(command_to_run=[nodeenv_command, install_dir, "--force", "--quiet"], cwd=root_path)
173
172
 
174
173
  console.rule("[yellow]Starting package installation process[/]", align="left")
@@ -206,7 +205,6 @@ def vite_install(app: "Litestar", verbose: "bool") -> None:
206
205
  )
207
206
  install_dir = os.environ.get("VIRTUAL_ENV", sys.prefix)
208
207
  console.rule("[yellow]Starting Nodeenv installation process[/]", align="left")
209
- console.print("Installing Node environment to %s:", install_dir)
210
208
  execute_command(command_to_run=[nodeenv_command, install_dir, "--force", "--quiet"], cwd=plugin.config.root_dir)
211
209
 
212
210
  console.rule("[yellow]Starting package installation process[/]", align="left")
@@ -13,12 +13,12 @@ if TYPE_CHECKING:
13
13
  VITE_INIT_TEMPLATES: "set[str]" = {"package.json.j2", "tsconfig.json.j2", "vite.config.ts.j2"}
14
14
  DEFAULT_RESOURCES: "set[str]" = {"styles.css.j2", "main.ts.j2"}
15
15
  DEFAULT_DEV_DEPENDENCIES: "dict[str, str]" = {
16
- "typescript": "^5.7.2",
17
- "vite": "^6.0.6",
18
- "litestar-vite-plugin": "^0.13.1",
19
- "@types/node": "^22.10.2",
16
+ "typescript": "^5.8.3",
17
+ "vite": "^6.3.5",
18
+ "litestar-vite-plugin": "^0.13.2",
19
+ "@types/node": "^22.15.3",
20
20
  }
21
- DEFAULT_DEPENDENCIES: "dict[str, str]" = {"axios": "^1.7.9"}
21
+ DEFAULT_DEPENDENCIES: "dict[str, str]" = {"axios": "^1.9.0"}
22
22
 
23
23
 
24
24
  def to_json(value: "Any") -> str:
@@ -21,7 +21,7 @@ class InertiaConfig:
21
21
  """Optionally supply a path where unauthorized requests should redirect."""
22
22
  redirect_404: "Optional[str]" = None
23
23
  """Optionally supply a path where 404 requests should redirect."""
24
- extra_static_page_props: "dict[str, Any]" = field(default_factory=dict)
24
+ extra_static_page_props: "dict[str, Any]" = field(default_factory=dict) # pyright: ignore
25
25
  """A dictionary of values to automatically add in to page props on every response."""
26
- extra_session_page_props: "set[str]" = field(default_factory=set)
26
+ extra_session_page_props: "set[str]" = field(default_factory=set) # pyright: ignore
27
27
  """A set of session keys for which the value automatically be added (if it exists) to the response."""
@@ -104,7 +104,9 @@ class ViteAssetLoader(metaclass=SingletonMeta):
104
104
  Returns:
105
105
  The manifest loader.
106
106
  """
107
- return cls(config=config)
107
+ loader = cls(config=config)
108
+ loader.parse_manifest()
109
+ return loader
108
110
 
109
111
  @cached_property
110
112
  def version_id(self) -> str:
@@ -78,7 +78,6 @@ class ViteProcess:
78
78
  if self.process and self.process.poll() is None: # pyright: ignore[reportUnknownMemberType]
79
79
  return
80
80
 
81
- console.print(f"Starting Vite process with command: {command}")
82
81
  self.process = subprocess.Popen(
83
82
  command,
84
83
  cwd=cwd,
@@ -104,7 +103,6 @@ class ViteProcess:
104
103
  if hasattr(signal, "SIGKILL"):
105
104
  self.process.kill() # pyright: ignore[reportUnknownMemberType]
106
105
  self.process.wait(timeout=1.0) # pyright: ignore[reportUnknownMemberType]
107
- console.print("Stopping Vite process")
108
106
  except Exception as e:
109
107
  console.print(f"[red]Failed to stop Vite process: {e!s}[/]")
110
108
  raise
@@ -200,7 +198,8 @@ class VitePlugin(InitPluginProtocol, CLIPlugin):
200
198
  Yields:
201
199
  An iterator of None.
202
200
  """
203
-
201
+ if self._config.set_environment:
202
+ set_environment(config=self._config)
204
203
  if self._config.use_server_lifespan and self._config.dev_mode:
205
204
  command_to_run = self._config.run_command if self._config.hot_reload else self._config.build_watch_command
206
205
 
@@ -209,9 +208,6 @@ class VitePlugin(InitPluginProtocol, CLIPlugin):
209
208
  else:
210
209
  console.rule("[yellow]Starting Vite watch and build process[/]", align="left")
211
210
 
212
- if self._config.set_environment:
213
- set_environment(config=self._config)
214
-
215
211
  try:
216
212
  self._vite_process.start(command_to_run, self._config.root_dir)
217
213
  yield
File without changes
File without changes