@jasonshimmy/vite-plugin-cer-app 0.23.1 → 0.24.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.
Files changed (397) hide show
  1. package/dist/cli/checks/lighthouse.d.ts +26 -0
  2. package/dist/cli/checks/lighthouse.d.ts.map +1 -0
  3. package/dist/cli/checks/lighthouse.js +121 -0
  4. package/dist/cli/checks/lighthouse.js.map +1 -0
  5. package/dist/cli/checks/links.d.ts +12 -0
  6. package/dist/cli/checks/links.d.ts.map +1 -0
  7. package/dist/cli/checks/links.js +89 -0
  8. package/dist/cli/checks/links.js.map +1 -0
  9. package/dist/cli/checks/performance.d.ts +17 -0
  10. package/dist/cli/checks/performance.d.ts.map +1 -0
  11. package/dist/cli/checks/performance.js +61 -0
  12. package/dist/cli/checks/performance.js.map +1 -0
  13. package/dist/cli/commands/check.d.ts +3 -0
  14. package/dist/cli/commands/check.d.ts.map +1 -0
  15. package/dist/cli/commands/check.js +85 -0
  16. package/dist/cli/commands/check.js.map +1 -0
  17. package/dist/cli/commands/preview.d.ts.map +1 -1
  18. package/dist/cli/commands/preview.js +37 -4
  19. package/dist/cli/commands/preview.js.map +1 -1
  20. package/dist/cli/create/index.d.ts +11 -1
  21. package/dist/cli/create/index.d.ts.map +1 -1
  22. package/dist/cli/create/index.js +135 -65
  23. package/dist/cli/create/index.js.map +1 -1
  24. package/dist/cli/create/templates/spa/package.json.tpl +5 -3
  25. package/dist/cli/create/templates/ssg/cer.config.ts.tpl +2 -0
  26. package/dist/cli/create/templates/ssg/package.json.tpl +5 -3
  27. package/dist/cli/create/templates/ssr/package.json.tpl +5 -3
  28. package/dist/cli/index.js +2 -0
  29. package/dist/cli/index.js.map +1 -1
  30. package/dist/index.d.ts +3 -1
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/plugin/build-ssg.d.ts +22 -4
  33. package/dist/plugin/build-ssg.d.ts.map +1 -1
  34. package/dist/plugin/build-ssg.js +273 -13
  35. package/dist/plugin/build-ssg.js.map +1 -1
  36. package/dist/plugin/content/parser.d.ts.map +1 -1
  37. package/dist/plugin/content/parser.js +54 -10
  38. package/dist/plugin/content/parser.js.map +1 -1
  39. package/dist/plugin/dev-server.d.ts +6 -1
  40. package/dist/plugin/dev-server.d.ts.map +1 -1
  41. package/dist/plugin/dev-server.js.map +1 -1
  42. package/dist/plugin/dts-generator.d.ts.map +1 -1
  43. package/dist/plugin/dts-generator.js +50 -8
  44. package/dist/plugin/dts-generator.js.map +1 -1
  45. package/dist/plugin/generated-dir.d.ts +2 -0
  46. package/dist/plugin/generated-dir.d.ts.map +1 -1
  47. package/dist/plugin/generated-dir.js +17 -6
  48. package/dist/plugin/generated-dir.js.map +1 -1
  49. package/dist/plugin/html-post-process.d.ts +35 -0
  50. package/dist/plugin/html-post-process.d.ts.map +1 -0
  51. package/dist/plugin/html-post-process.js +112 -0
  52. package/dist/plugin/html-post-process.js.map +1 -0
  53. package/dist/plugin/index.d.ts +9 -0
  54. package/dist/plugin/index.d.ts.map +1 -1
  55. package/dist/plugin/index.js +89 -14
  56. package/dist/plugin/index.js.map +1 -1
  57. package/dist/plugin/transforms/auto-import.d.ts.map +1 -1
  58. package/dist/plugin/transforms/auto-import.js +7 -0
  59. package/dist/plugin/transforms/auto-import.js.map +1 -1
  60. package/dist/plugin/transforms/prune-runtime-css.d.ts +8 -0
  61. package/dist/plugin/transforms/prune-runtime-css.d.ts.map +1 -0
  62. package/dist/plugin/transforms/prune-runtime-css.js +40 -0
  63. package/dist/plugin/transforms/prune-runtime-css.js.map +1 -0
  64. package/dist/runtime/app-template.d.ts +1 -4
  65. package/dist/runtime/app-template.d.ts.map +1 -1
  66. package/dist/runtime/app-template.js +211 -40
  67. package/dist/runtime/app-template.js.map +1 -1
  68. package/dist/runtime/composables/index.d.ts +4 -0
  69. package/dist/runtime/composables/index.d.ts.map +1 -1
  70. package/dist/runtime/composables/index.js +2 -0
  71. package/dist/runtime/composables/index.js.map +1 -1
  72. package/dist/runtime/composables/use-active-headings.d.ts +24 -0
  73. package/dist/runtime/composables/use-active-headings.d.ts.map +1 -0
  74. package/dist/runtime/composables/use-active-headings.js +112 -0
  75. package/dist/runtime/composables/use-active-headings.js.map +1 -0
  76. package/dist/runtime/composables/use-content-page.d.ts +45 -0
  77. package/dist/runtime/composables/use-content-page.d.ts.map +1 -0
  78. package/dist/runtime/composables/use-content-page.js +179 -0
  79. package/dist/runtime/composables/use-content-page.js.map +1 -0
  80. package/dist/runtime/composables/use-content-search.d.ts.map +1 -1
  81. package/dist/runtime/composables/use-content-search.js +1 -5
  82. package/dist/runtime/composables/use-content-search.js.map +1 -1
  83. package/dist/runtime/composables/use-fetch.d.ts +2 -1
  84. package/dist/runtime/composables/use-fetch.d.ts.map +1 -1
  85. package/dist/runtime/composables/use-fetch.js +112 -43
  86. package/dist/runtime/composables/use-fetch.js.map +1 -1
  87. package/dist/runtime/composables/use-head.d.ts.map +1 -1
  88. package/dist/runtime/composables/use-head.js +30 -6
  89. package/dist/runtime/composables/use-head.js.map +1 -1
  90. package/dist/runtime/composables/use-route.d.ts.map +1 -1
  91. package/dist/runtime/composables/use-route.js +39 -11
  92. package/dist/runtime/composables/use-route.js.map +1 -1
  93. package/dist/runtime/content/client.js +2 -2
  94. package/dist/runtime/content/client.js.map +1 -1
  95. package/dist/runtime/entry-server-template.d.ts +16 -1
  96. package/dist/runtime/entry-server-template.d.ts.map +1 -1
  97. package/dist/runtime/entry-server-template.js +120 -13
  98. package/dist/runtime/entry-server-template.js.map +1 -1
  99. package/dist/types/config.d.ts +53 -4
  100. package/dist/types/config.d.ts.map +1 -1
  101. package/dist/types/config.js.map +1 -1
  102. package/dist/types/middleware.d.ts +2 -2
  103. package/dist/types/middleware.d.ts.map +1 -1
  104. package/package.json +22 -19
  105. package/.github/copilot-instructions.md +0 -169
  106. package/.github/workflows/publish.yml +0 -257
  107. package/.nvmrc +0 -1
  108. package/CHANGELOG.md +0 -232
  109. package/CLAUDE.md +0 -113
  110. package/commits.txt +0 -1
  111. package/cypress.config.ts +0 -16
  112. package/docs/authentication.md +0 -285
  113. package/docs/cli.md +0 -324
  114. package/docs/components.md +0 -237
  115. package/docs/composables.md +0 -907
  116. package/docs/configuration.md +0 -667
  117. package/docs/content.md +0 -570
  118. package/docs/data-loading.md +0 -336
  119. package/docs/getting-started.md +0 -237
  120. package/docs/head-management.md +0 -212
  121. package/docs/i18n.md +0 -159
  122. package/docs/layouts.md +0 -224
  123. package/docs/middleware.md +0 -234
  124. package/docs/plugins.md +0 -170
  125. package/docs/rendering-modes.md +0 -377
  126. package/docs/routing.md +0 -485
  127. package/docs/server-api.md +0 -253
  128. package/docs/testing.md +0 -455
  129. package/docs/use-fetch.md +0 -183
  130. package/e2e/cypress/e2e/api.cy.ts +0 -123
  131. package/e2e/cypress/e2e/async-component.cy.ts +0 -27
  132. package/e2e/cypress/e2e/component-splitting.cy.ts +0 -50
  133. package/e2e/cypress/e2e/content.cy.ts +0 -425
  134. package/e2e/cypress/e2e/cookie.cy.ts +0 -85
  135. package/e2e/cypress/e2e/data.cy.ts +0 -158
  136. package/e2e/cypress/e2e/error-boundary.cy.ts +0 -88
  137. package/e2e/cypress/e2e/fouc.cy.ts +0 -254
  138. package/e2e/cypress/e2e/group-meta.cy.ts +0 -36
  139. package/e2e/cypress/e2e/head.cy.ts +0 -89
  140. package/e2e/cypress/e2e/i18n.cy.ts +0 -79
  141. package/e2e/cypress/e2e/interactive.cy.ts +0 -137
  142. package/e2e/cypress/e2e/isr-nested-runtime.cy.ts +0 -102
  143. package/e2e/cypress/e2e/jit-css-custom-colors.cy.ts +0 -43
  144. package/e2e/cypress/e2e/middleware-chain.cy.ts +0 -89
  145. package/e2e/cypress/e2e/middleware-status.cy.ts +0 -30
  146. package/e2e/cypress/e2e/middleware.cy.ts +0 -45
  147. package/e2e/cypress/e2e/navigate.cy.ts +0 -39
  148. package/e2e/cypress/e2e/observability.cy.ts +0 -77
  149. package/e2e/cypress/e2e/per-route-error.cy.ts +0 -42
  150. package/e2e/cypress/e2e/per-route-render.cy.ts +0 -70
  151. package/e2e/cypress/e2e/preview-hardening.cy.ts +0 -88
  152. package/e2e/cypress/e2e/routes.cy.ts +0 -128
  153. package/e2e/cypress/e2e/seo-meta.cy.ts +0 -108
  154. package/e2e/cypress/e2e/server-middleware.cy.ts +0 -46
  155. package/e2e/cypress/e2e/session-rotation.cy.ts +0 -25
  156. package/e2e/cypress/e2e/session.cy.ts +0 -73
  157. package/e2e/cypress/e2e/ssr-render-error.cy.ts +0 -68
  158. package/e2e/cypress/e2e/synthetic-404.cy.ts +0 -45
  159. package/e2e/cypress/e2e/use-page-data.cy.ts +0 -122
  160. package/e2e/cypress/e2e/use-state.cy.ts +0 -138
  161. package/e2e/cypress/support/commands.ts +0 -120
  162. package/e2e/cypress/support/e2e.ts +0 -10
  163. package/e2e/cypress/tsconfig.json +0 -14
  164. package/e2e/kitchen-sink/app/components/ks-badge.ts +0 -8
  165. package/e2e/kitchen-sink/app/composables/useKsCounter.ts +0 -9
  166. package/e2e/kitchen-sink/app/error.ts +0 -18
  167. package/e2e/kitchen-sink/app/layouts/admin.ts +0 -13
  168. package/e2e/kitchen-sink/app/layouts/default.ts +0 -25
  169. package/e2e/kitchen-sink/app/layouts/group.ts +0 -9
  170. package/e2e/kitchen-sink/app/layouts/minimal.ts +0 -7
  171. package/e2e/kitchen-sink/app/loading.ts +0 -9
  172. package/e2e/kitchen-sink/app/middleware/auth.ts +0 -9
  173. package/e2e/kitchen-sink/app/middleware/custom-log.ts +0 -4
  174. package/e2e/kitchen-sink/app/middleware/group-auth.ts +0 -7
  175. package/e2e/kitchen-sink/app/middleware/logger.ts +0 -14
  176. package/e2e/kitchen-sink/app/pages/(auth)/login.ts +0 -13
  177. package/e2e/kitchen-sink/app/pages/(auth)/protected.ts +0 -16
  178. package/e2e/kitchen-sink/app/pages/404.ts +0 -9
  179. package/e2e/kitchen-sink/app/pages/about.ts +0 -17
  180. package/e2e/kitchen-sink/app/pages/admin/_layout.ts +0 -1
  181. package/e2e/kitchen-sink/app/pages/admin/dashboard.ts +0 -11
  182. package/e2e/kitchen-sink/app/pages/async-component-error-test.ts +0 -18
  183. package/e2e/kitchen-sink/app/pages/async-component-test.ts +0 -18
  184. package/e2e/kitchen-sink/app/pages/blog/[slug].ts +0 -59
  185. package/e2e/kitchen-sink/app/pages/blog/index.ts +0 -51
  186. package/e2e/kitchen-sink/app/pages/content-blog.ts +0 -37
  187. package/e2e/kitchen-sink/app/pages/content-doc.ts +0 -42
  188. package/e2e/kitchen-sink/app/pages/content-fallback.ts +0 -36
  189. package/e2e/kitchen-sink/app/pages/content-guides.ts +0 -35
  190. package/e2e/kitchen-sink/app/pages/content-index.ts +0 -39
  191. package/e2e/kitchen-sink/app/pages/content-search.ts +0 -36
  192. package/e2e/kitchen-sink/app/pages/cookie-test.ts +0 -22
  193. package/e2e/kitchen-sink/app/pages/counter.ts +0 -17
  194. package/e2e/kitchen-sink/app/pages/group-auth-blocked.ts +0 -9
  195. package/e2e/kitchen-sink/app/pages/group-meta-test/_layout.ts +0 -6
  196. package/e2e/kitchen-sink/app/pages/group-meta-test/custom-mw.ts +0 -14
  197. package/e2e/kitchen-sink/app/pages/group-meta-test/page.ts +0 -9
  198. package/e2e/kitchen-sink/app/pages/group-meta-test/protected.ts +0 -8
  199. package/e2e/kitchen-sink/app/pages/head.ts +0 -20
  200. package/e2e/kitchen-sink/app/pages/i18n-test.ts +0 -14
  201. package/e2e/kitchen-sink/app/pages/index.ts +0 -27
  202. package/e2e/kitchen-sink/app/pages/isr-test.ts +0 -17
  203. package/e2e/kitchen-sink/app/pages/items/[id].ts +0 -20
  204. package/e2e/kitchen-sink/app/pages/jit-css-custom-colors-test.ts +0 -14
  205. package/e2e/kitchen-sink/app/pages/loader-error-test.ts +0 -13
  206. package/e2e/kitchen-sink/app/pages/loader-props-test.ts +0 -19
  207. package/e2e/kitchen-sink/app/pages/loader-response-error-test.ts +0 -13
  208. package/e2e/kitchen-sink/app/pages/middleware-chain-test.ts +0 -12
  209. package/e2e/kitchen-sink/app/pages/middleware-status-test.ts +0 -9
  210. package/e2e/kitchen-sink/app/pages/navigate-test.ts +0 -16
  211. package/e2e/kitchen-sink/app/pages/observability-test.ts +0 -25
  212. package/e2e/kitchen-sink/app/pages/per-route-error-test.error.ts +0 -13
  213. package/e2e/kitchen-sink/app/pages/per-route-error-test.ts +0 -15
  214. package/e2e/kitchen-sink/app/pages/plugin-dsd-test.ts +0 -10
  215. package/e2e/kitchen-sink/app/pages/render-error-test.ts +0 -12
  216. package/e2e/kitchen-sink/app/pages/render-server-test.ts +0 -12
  217. package/e2e/kitchen-sink/app/pages/render-spa-test.ts +0 -12
  218. package/e2e/kitchen-sink/app/pages/route-info.ts +0 -14
  219. package/e2e/kitchen-sink/app/pages/seo-test.ts +0 -23
  220. package/e2e/kitchen-sink/app/pages/session-rotation-test.ts +0 -25
  221. package/e2e/kitchen-sink/app/pages/use-state-test.ts +0 -37
  222. package/e2e/kitchen-sink/app/plugins/01.setup.ts +0 -7
  223. package/e2e/kitchen-sink/app/plugins/02.ks-plugin-components.ts +0 -27
  224. package/e2e/kitchen-sink/cer-auto-imports.d.ts +0 -51
  225. package/e2e/kitchen-sink/cer-env.d.ts +0 -35
  226. package/e2e/kitchen-sink/cer-tsconfig.json +0 -30
  227. package/e2e/kitchen-sink/cer.config.ts +0 -43
  228. package/e2e/kitchen-sink/content/01.guides/01.index.md +0 -8
  229. package/e2e/kitchen-sink/content/01.guides/02.intro.md +0 -8
  230. package/e2e/kitchen-sink/content/01.guides/10.advanced.md +0 -8
  231. package/e2e/kitchen-sink/content/blog/2026-04-01-hello.md +0 -26
  232. package/e2e/kitchen-sink/content/blog/2026-04-02-draft.md +0 -10
  233. package/e2e/kitchen-sink/content/blog/index.md +0 -8
  234. package/e2e/kitchen-sink/content/blog/no-frontmatter.md +0 -7
  235. package/e2e/kitchen-sink/content/docs/getting-started.md +0 -48
  236. package/e2e/kitchen-sink/content/index.md +0 -16
  237. package/e2e/kitchen-sink/server/api/echo.ts +0 -12
  238. package/e2e/kitchen-sink/server/api/health.ts +0 -3
  239. package/e2e/kitchen-sink/server/api/posts/[slug].ts +0 -11
  240. package/e2e/kitchen-sink/server/api/posts/index.ts +0 -5
  241. package/e2e/kitchen-sink/server/api/session.ts +0 -23
  242. package/e2e/kitchen-sink/server/data/posts.ts +0 -21
  243. package/e2e/kitchen-sink/server/middleware/01-headers.ts +0 -6
  244. package/e2e/kitchen-sink/server/middleware/02-status-test.ts +0 -10
  245. package/e2e/kitchen-sink/tsconfig.json +0 -3
  246. package/e2e/scripts/clean.mjs +0 -14
  247. package/eslint.config.ts +0 -22
  248. package/src/__tests__/cli/adapters/cloudflare-worker.integration.test.ts +0 -132
  249. package/src/__tests__/cli/adapters/cloudflare.test.ts +0 -332
  250. package/src/__tests__/cli/adapters/netlify-bridge.integration.test.ts +0 -139
  251. package/src/__tests__/cli/adapters/netlify.test.ts +0 -286
  252. package/src/__tests__/cli/adapters/vercel-launcher.integration.test.ts +0 -129
  253. package/src/__tests__/cli/adapters/vercel.test.ts +0 -263
  254. package/src/__tests__/cli/preview-hardening.test.ts +0 -265
  255. package/src/__tests__/cli/preview-isr.test.ts +0 -320
  256. package/src/__tests__/index.test.ts +0 -21
  257. package/src/__tests__/plugin/app-template.test.ts +0 -112
  258. package/src/__tests__/plugin/build-ssg-render.test.ts +0 -155
  259. package/src/__tests__/plugin/build-ssg.test.ts +0 -598
  260. package/src/__tests__/plugin/build-ssr.test.ts +0 -192
  261. package/src/__tests__/plugin/cer-app-plugin.test.ts +0 -552
  262. package/src/__tests__/plugin/content/emitter.test.ts +0 -117
  263. package/src/__tests__/plugin/content/loader.test.ts +0 -154
  264. package/src/__tests__/plugin/content/parser.test.ts +0 -381
  265. package/src/__tests__/plugin/content/path-utils.test.ts +0 -69
  266. package/src/__tests__/plugin/content/search.test.ts +0 -119
  267. package/src/__tests__/plugin/dev-server.test.ts +0 -813
  268. package/src/__tests__/plugin/dts-generator.test.ts +0 -394
  269. package/src/__tests__/plugin/entry-server-template.test.ts +0 -385
  270. package/src/__tests__/plugin/generated-dir.test.ts +0 -144
  271. package/src/__tests__/plugin/path-utils.test.ts +0 -411
  272. package/src/__tests__/plugin/resolve-config.test.ts +0 -200
  273. package/src/__tests__/plugin/scanner.test.ts +0 -186
  274. package/src/__tests__/plugin/transforms/auto-import.test.ts +0 -510
  275. package/src/__tests__/plugin/transforms/head-inject.test.ts +0 -178
  276. package/src/__tests__/plugin/virtual/composables.test.ts +0 -57
  277. package/src/__tests__/plugin/virtual/content-components.test.ts +0 -66
  278. package/src/__tests__/plugin/virtual/error.test.ts +0 -71
  279. package/src/__tests__/plugin/virtual/layouts.test.ts +0 -70
  280. package/src/__tests__/plugin/virtual/loading.test.ts +0 -72
  281. package/src/__tests__/plugin/virtual/middleware.test.ts +0 -83
  282. package/src/__tests__/plugin/virtual/plugins.test.ts +0 -129
  283. package/src/__tests__/plugin/virtual/routes.test.ts +0 -884
  284. package/src/__tests__/plugin/virtual/server-api.test.ts +0 -164
  285. package/src/__tests__/plugin/virtual/server-middleware.test.ts +0 -102
  286. package/src/__tests__/runtime/app-template.test.ts +0 -198
  287. package/src/__tests__/runtime/define-middleware.test.ts +0 -64
  288. package/src/__tests__/runtime/define-server-middleware.test.ts +0 -54
  289. package/src/__tests__/runtime/entry-client-template.test.ts +0 -61
  290. package/src/__tests__/runtime/isr-handler.test.ts +0 -338
  291. package/src/__tests__/runtime/oauth/handler.test.ts +0 -358
  292. package/src/__tests__/runtime/oauth/providers.test.ts +0 -47
  293. package/src/__tests__/runtime/oauth-index.test.ts +0 -43
  294. package/src/__tests__/runtime/use-auth.test.ts +0 -169
  295. package/src/__tests__/runtime/use-content-search-composable.test.ts +0 -405
  296. package/src/__tests__/runtime/use-content-search.test.ts +0 -163
  297. package/src/__tests__/runtime/use-content.test.ts +0 -226
  298. package/src/__tests__/runtime/use-cookie.test.ts +0 -277
  299. package/src/__tests__/runtime/use-fetch-component.test.ts +0 -214
  300. package/src/__tests__/runtime/use-fetch.test.ts +0 -377
  301. package/src/__tests__/runtime/use-head.test.ts +0 -266
  302. package/src/__tests__/runtime/use-inject-client.test.ts +0 -67
  303. package/src/__tests__/runtime/use-inject.test.ts +0 -66
  304. package/src/__tests__/runtime/use-locale.test.ts +0 -152
  305. package/src/__tests__/runtime/use-navigate.test.ts +0 -121
  306. package/src/__tests__/runtime/use-page-data.test.ts +0 -298
  307. package/src/__tests__/runtime/use-route.test.ts +0 -156
  308. package/src/__tests__/runtime/use-runtime-config.test.ts +0 -182
  309. package/src/__tests__/runtime/use-seo-meta.test.ts +0 -109
  310. package/src/__tests__/runtime/use-session.test.ts +0 -320
  311. package/src/__tests__/runtime/use-state.test.ts +0 -340
  312. package/src/__tests__/types/config.test.ts +0 -18
  313. package/src/cli/adapters/cloudflare.ts +0 -335
  314. package/src/cli/adapters/netlify.ts +0 -326
  315. package/src/cli/adapters/vercel.ts +0 -311
  316. package/src/cli/commands/adapt.ts +0 -84
  317. package/src/cli/commands/build.ts +0 -165
  318. package/src/cli/commands/dev.ts +0 -125
  319. package/src/cli/commands/generate.ts +0 -91
  320. package/src/cli/commands/preview-isr.ts +0 -172
  321. package/src/cli/commands/preview.ts +0 -489
  322. package/src/cli/create/index.ts +0 -275
  323. package/src/cli/create/templates/shared/.gitignore.tpl +0 -25
  324. package/src/cli/create/templates/shared/app/layouts/default.ts.tpl +0 -15
  325. package/src/cli/create/templates/shared/app/pages/index.ts.tpl +0 -8
  326. package/src/cli/create/templates/shared/index.html.tpl +0 -12
  327. package/src/cli/create/templates/shared/tsconfig.json.tpl +0 -6
  328. package/src/cli/create/templates/spa/cer.config.ts.tpl +0 -6
  329. package/src/cli/create/templates/spa/package.json.tpl +0 -18
  330. package/src/cli/create/templates/ssg/cer.config.ts.tpl +0 -10
  331. package/src/cli/create/templates/ssg/package.json.tpl +0 -19
  332. package/src/cli/create/templates/ssr/cer.config.ts.tpl +0 -6
  333. package/src/cli/create/templates/ssr/package.json.tpl +0 -18
  334. package/src/cli/index.ts +0 -22
  335. package/src/index.ts +0 -20
  336. package/src/plugin/build-ssg.ts +0 -336
  337. package/src/plugin/build-ssr.ts +0 -134
  338. package/src/plugin/content/emitter.ts +0 -50
  339. package/src/plugin/content/index.ts +0 -233
  340. package/src/plugin/content/parser.ts +0 -259
  341. package/src/plugin/content/path-utils.ts +0 -55
  342. package/src/plugin/content/scanner.ts +0 -26
  343. package/src/plugin/content/search.ts +0 -28
  344. package/src/plugin/dev-server.ts +0 -377
  345. package/src/plugin/dts-generator.ts +0 -273
  346. package/src/plugin/generated-dir.ts +0 -121
  347. package/src/plugin/index.ts +0 -553
  348. package/src/plugin/path-utils.ts +0 -186
  349. package/src/plugin/scanner.ts +0 -65
  350. package/src/plugin/transforms/auto-import.ts +0 -206
  351. package/src/plugin/transforms/head-inject.ts +0 -161
  352. package/src/plugin/virtual/composables.ts +0 -28
  353. package/src/plugin/virtual/content-components.ts +0 -83
  354. package/src/plugin/virtual/error.ts +0 -34
  355. package/src/plugin/virtual/layouts.ts +0 -41
  356. package/src/plugin/virtual/loading.ts +0 -32
  357. package/src/plugin/virtual/middleware.ts +0 -45
  358. package/src/plugin/virtual/plugins.ts +0 -46
  359. package/src/plugin/virtual/routes.ts +0 -514
  360. package/src/plugin/virtual/server-api.ts +0 -85
  361. package/src/plugin/virtual/server-middleware.ts +0 -44
  362. package/src/runtime/app-template.ts +0 -380
  363. package/src/runtime/composables/define-middleware.ts +0 -17
  364. package/src/runtime/composables/define-server-middleware.ts +0 -18
  365. package/src/runtime/composables/index.ts +0 -27
  366. package/src/runtime/composables/use-auth.ts +0 -125
  367. package/src/runtime/composables/use-content-search.ts +0 -205
  368. package/src/runtime/composables/use-content.ts +0 -146
  369. package/src/runtime/composables/use-cookie.ts +0 -135
  370. package/src/runtime/composables/use-fetch.ts +0 -438
  371. package/src/runtime/composables/use-head.ts +0 -212
  372. package/src/runtime/composables/use-inject.ts +0 -49
  373. package/src/runtime/composables/use-locale.ts +0 -109
  374. package/src/runtime/composables/use-navigate.ts +0 -58
  375. package/src/runtime/composables/use-page-data.ts +0 -93
  376. package/src/runtime/composables/use-route.ts +0 -71
  377. package/src/runtime/composables/use-runtime-config.ts +0 -123
  378. package/src/runtime/composables/use-seo-meta.ts +0 -80
  379. package/src/runtime/composables/use-session.ts +0 -186
  380. package/src/runtime/composables/use-state.ts +0 -92
  381. package/src/runtime/content/client.ts +0 -168
  382. package/src/runtime/entry-client-template.ts +0 -39
  383. package/src/runtime/entry-server-template.ts +0 -493
  384. package/src/runtime/isr-handler.ts +0 -184
  385. package/src/runtime/oauth/handler.ts +0 -407
  386. package/src/runtime/oauth/index.ts +0 -49
  387. package/src/runtime/oauth/providers.ts +0 -38
  388. package/src/types/api.ts +0 -40
  389. package/src/types/config.ts +0 -371
  390. package/src/types/content.ts +0 -66
  391. package/src/types/index.ts +0 -7
  392. package/src/types/middleware.ts +0 -63
  393. package/src/types/page.ts +0 -117
  394. package/src/types/plugin.ts +0 -34
  395. package/tsconfig.build.json +0 -10
  396. package/tsconfig.json +0 -19
  397. package/vitest.config.ts +0 -33
@@ -1,884 +0,0 @@
1
- import { vi, describe, it, expect, beforeEach } from 'vitest'
2
-
3
- vi.mock('node:fs', () => ({ existsSync: vi.fn() }))
4
- vi.mock('node:fs/promises', () => ({ readFile: vi.fn().mockResolvedValue('') }))
5
- vi.mock('../../../plugin/scanner.js', () => ({ scanDirectory: vi.fn() }))
6
-
7
- import { existsSync } from 'node:fs'
8
- import { readFile } from 'node:fs/promises'
9
- import { scanDirectory } from '../../../plugin/scanner.js'
10
- import { generateRoutesCode } from '../../../plugin/virtual/routes.js'
11
-
12
- const PAGES = '/project/app/pages'
13
-
14
- describe('generateRoutesCode', () => {
15
- beforeEach(() => {
16
- vi.mocked(existsSync).mockReturnValue(true)
17
- vi.mocked(scanDirectory).mockResolvedValue([])
18
- vi.mocked(readFile).mockResolvedValue('' as never)
19
- })
20
-
21
- it('returns empty routes when directory does not exist', async () => {
22
- vi.mocked(existsSync).mockReturnValue(false)
23
- const code = await generateRoutesCode(PAGES)
24
- expect(code).toContain('const routes = []')
25
- expect(code).toContain('export default routes')
26
- })
27
-
28
- it('returns empty routes when no files found', async () => {
29
- const code = await generateRoutesCode(PAGES)
30
- expect(code).toContain('const routes = []')
31
- expect(code).toContain('export default routes')
32
- })
33
-
34
- it('includes AUTO-GENERATED comment', async () => {
35
- const code = await generateRoutesCode(PAGES)
36
- expect(code).toContain('AUTO-GENERATED by @jasonshimmy/vite-plugin-cer-app')
37
- })
38
-
39
- it('generates a lazy load() route for a static page', async () => {
40
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
41
- const code = await generateRoutesCode(PAGES)
42
- expect(code).toContain('path: \"/about\"')
43
- expect(code).toContain('load:')
44
- expect(code).toContain(`import("${PAGES}/about.ts")`)
45
- expect(code).toContain('"page-about"')
46
- // No eager static imports
47
- expect(code).not.toContain('import * as')
48
- })
49
-
50
- it('load() also forwards the page module loader export', async () => {
51
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
52
- const code = await generateRoutesCode(PAGES)
53
- expect(code).toContain('loader: mod.loader')
54
- })
55
-
56
- it('generates a lazy load() route for index.ts → /', async () => {
57
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/index.ts`])
58
- const code = await generateRoutesCode(PAGES)
59
- expect(code).toContain('path: \"/\"')
60
- expect(code).toContain('load:')
61
- expect(code).toContain('"page-index"')
62
- })
63
-
64
- it('generates a lazy load() route for a dynamic route', async () => {
65
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/blog/[slug].ts`])
66
- const code = await generateRoutesCode(PAGES)
67
- expect(code).toContain('path: \"/blog/:slug\"')
68
- expect(code).toContain('load:')
69
- expect(code).toContain('"page-blog-slug"')
70
- })
71
-
72
- it('generates a lazy load() route for a catch-all route', async () => {
73
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/[...all].ts`])
74
- const code = await generateRoutesCode(PAGES)
75
- expect(code).toContain('path: \"/:all*\"')
76
- expect(code).toContain('load:')
77
- expect(code).toContain('"page-all"')
78
- })
79
-
80
- it('sorts routes: static before dynamic before catch-all', async () => {
81
- vi.mocked(scanDirectory).mockResolvedValue([
82
- `${PAGES}/[...all].ts`,
83
- `${PAGES}/[id].ts`,
84
- `${PAGES}/about.ts`,
85
- ])
86
- const code = await generateRoutesCode(PAGES)
87
- const aboutIdx = code.indexOf('\"/about\"')
88
- const idIdx = code.indexOf('\"/:id\"')
89
- const allIdx = code.indexOf('\"/:all*\"')
90
- expect(aboutIdx).toBeLessThan(idIdx)
91
- expect(idIdx).toBeLessThan(allIdx)
92
- })
93
-
94
- it('includes export default routes', async () => {
95
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
96
- const code = await generateRoutesCode(PAGES)
97
- expect(code).toContain('export default routes')
98
- })
99
-
100
- it('generates multiple lazy routes', async () => {
101
- vi.mocked(scanDirectory).mockResolvedValue([
102
- `${PAGES}/index.ts`,
103
- `${PAGES}/about.ts`,
104
- `${PAGES}/contact.ts`,
105
- ])
106
- const code = await generateRoutesCode(PAGES)
107
- expect(code).toContain('\"/about\"')
108
- expect(code).toContain('\"/contact\"')
109
- expect(code).toContain('\"/\"')
110
- expect(code).not.toContain('import * as')
111
- })
112
-
113
- it('inlines middleware names and adds beforeEnter when middleware is present', async () => {
114
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/dashboard.ts`])
115
- vi.mocked(readFile).mockResolvedValue(
116
- `component('page-dashboard', () => html\`<div>Dashboard</div>\`)\nexport const meta = { middleware: ['auth'] }` as never,
117
- )
118
- const code = await generateRoutesCode(PAGES)
119
- expect(code).toContain('beforeEnter')
120
- expect(code).toContain('"auth"')
121
- expect(code).toContain('virtual:cer-middleware')
122
- })
123
-
124
- it('omits beforeEnter when no middleware is declared', async () => {
125
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
126
- vi.mocked(readFile).mockResolvedValue(
127
- `component('page-about', () => html\`<h1>About</h1>\`)\nexport const meta = { layout: 'default' }` as never,
128
- )
129
- const code = await generateRoutesCode(PAGES)
130
- expect(code).not.toContain('beforeEnter')
131
- })
132
-
133
- it('uses return-value API (await handler) not callback-style (new Promise)', async () => {
134
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/dashboard.ts`])
135
- vi.mocked(readFile).mockResolvedValue(
136
- `component('page-dashboard', () => html\`<div/>\`)\nexport const meta = { middleware: ['auth'] }` as never,
137
- )
138
- const code = await generateRoutesCode(PAGES)
139
- expect(code).toContain('await handler(to, from, next)')
140
- expect(code).not.toContain('new Promise')
141
- })
142
-
143
- it('passes a next() function as third arg to each middleware handler', async () => {
144
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/dashboard.ts`])
145
- vi.mocked(readFile).mockResolvedValue(
146
- `component('page-dashboard', () => html\`<div/>\`)\nexport const meta = { middleware: ['auth'] }` as never,
147
- )
148
- const code = await generateRoutesCode(PAGES)
149
- expect(code).toContain('const next = async () =>')
150
- expect(code).toContain('_calledNext = true')
151
- })
152
-
153
- it('runs downstream middleware when next() is called', async () => {
154
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/dashboard.ts`])
155
- vi.mocked(readFile).mockResolvedValue(
156
- `component('page-dashboard', () => html\`<div/>\`)\nexport const meta = { middleware: ['logger', 'auth'] }` as never,
157
- )
158
- const code = await generateRoutesCode(PAGES)
159
- expect(code).toContain('_runNext')
160
- expect(code).toContain('"logger"')
161
- expect(code).toContain('"auth"')
162
- })
163
-
164
- it('wraps handler call in try-catch (blocks navigation on error)', async () => {
165
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/dashboard.ts`])
166
- vi.mocked(readFile).mockResolvedValue(
167
- `component('page-dashboard', () => html\`<div/>\`)\nexport const meta = { middleware: ['auth'] }` as never,
168
- )
169
- const code = await generateRoutesCode(PAGES)
170
- expect(code).toContain('try {')
171
- expect(code).toContain('} catch (err) {')
172
- // New chain pattern: sets _guardResult = false and returns from the inner _runNext
173
- expect(code).toContain('_guardResult = false')
174
- })
175
-
176
- it('logs the middleware name in the error message', async () => {
177
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/dashboard.ts`])
178
- vi.mocked(readFile).mockResolvedValue(
179
- `component('page-dashboard', () => html\`<div/>\`)\nexport const meta = { middleware: ['auth'] }` as never,
180
- )
181
- const code = await generateRoutesCode(PAGES)
182
- expect(code).toContain('console.error')
183
- expect(code).toContain('Middleware')
184
- expect(code).toContain('err)')
185
- })
186
- })
187
-
188
- // ─── meta.layout ─────────────────────────────────────────────────────────────
189
-
190
- describe('generateRoutesCode — meta.layout', () => {
191
- beforeEach(() => {
192
- // Return true for directory existence checks but false for .error.ts and
193
- // _error.ts so resolveRouteErrorTag doesn't inject spurious errorTag fields
194
- // into routes that aren't testing error boundaries.
195
- vi.mocked(existsSync).mockImplementation((p: unknown) => {
196
- const path = String(p)
197
- if (path.endsWith('.error.ts') || path.endsWith('/_error.ts') || path.endsWith('\\_error.ts')) return false
198
- return true
199
- })
200
- vi.mocked(scanDirectory).mockResolvedValue([])
201
- vi.mocked(readFile).mockResolvedValue('' as never)
202
- })
203
-
204
- it('emits meta.layout when layout is declared in page source', async () => {
205
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
206
- vi.mocked(readFile).mockResolvedValue(
207
- `component('page-about', () => html\`<h1>About</h1>\`)\nexport const meta = { layout: 'minimal' }` as never,
208
- )
209
- const code = await generateRoutesCode(PAGES)
210
- expect(code).toContain('meta: { layout: "minimal" }')
211
- })
212
-
213
- it('omits meta field when no layout is declared', async () => {
214
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
215
- vi.mocked(readFile).mockResolvedValue(
216
- `component('page-about', () => html\`<h1>About</h1>\`)` as never,
217
- )
218
- const code = await generateRoutesCode(PAGES)
219
- expect(code).toContain('path: "/about"')
220
- expect(code).not.toContain('path: "/about",\n load: () => import("/project/app/pages/about.ts").then(mod => ({ default: "page-about", loader: mod.loader ?? null })),\n meta:')
221
- })
222
-
223
- it('emits meta.layout alongside beforeEnter when both layout and middleware are present', async () => {
224
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/dashboard.ts`])
225
- vi.mocked(readFile).mockResolvedValue(
226
- `component('page-dashboard', () => html\`<div/>\`)\nexport const meta = { layout: 'admin', middleware: ['auth'] }` as never,
227
- )
228
- const code = await generateRoutesCode(PAGES)
229
- expect(code).toContain('meta: { layout: "admin" }')
230
- expect(code).toContain('beforeEnter')
231
- expect(code).toContain('"auth"')
232
- })
233
- })
234
-
235
- // ─── 404 page convention ──────────────────────────────────────────────────────
236
-
237
- describe('generateRoutesCode — 404.ts convention', () => {
238
- beforeEach(() => {
239
- vi.mocked(existsSync).mockReturnValue(true)
240
- vi.mocked(scanDirectory).mockResolvedValue([])
241
- vi.mocked(readFile).mockResolvedValue('' as never)
242
- })
243
-
244
- it('maps 404.ts to path /:all* (catch-all)', async () => {
245
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/404.ts`])
246
- const code = await generateRoutesCode(PAGES)
247
- expect(code).toContain('path: \"/:all*\"')
248
- expect(code).toContain('"page-404"')
249
- expect(code).toContain('_cerNotFound: true')
250
- })
251
-
252
- it('sorts 404.ts catch-all after all other routes', async () => {
253
- vi.mocked(scanDirectory).mockResolvedValue([
254
- `${PAGES}/404.ts`,
255
- `${PAGES}/about.ts`,
256
- `${PAGES}/[id].ts`,
257
- ])
258
- const code = await generateRoutesCode(PAGES)
259
- const aboutIdx = code.indexOf('\"/about\"')
260
- const idIdx = code.indexOf('\"/:id\"')
261
- const allIdx = code.indexOf('\"/:all*\"')
262
- expect(aboutIdx).toBeLessThan(idIdx)
263
- expect(idIdx).toBeLessThan(allIdx)
264
- })
265
- })
266
-
267
- // ─── _layout.ts filtering ─────────────────────────────────────────────────────
268
-
269
- describe('generateRoutesCode — _layout.ts filtering', () => {
270
- beforeEach(() => {
271
- vi.mocked(existsSync).mockReturnValue(true)
272
- vi.mocked(scanDirectory).mockResolvedValue([])
273
- vi.mocked(readFile).mockResolvedValue('' as never)
274
- })
275
-
276
- it('excludes _layout.ts files from the generated routes', async () => {
277
- vi.mocked(scanDirectory).mockResolvedValue([
278
- `${PAGES}/admin/index.ts`,
279
- `${PAGES}/admin/_layout.ts`,
280
- ])
281
- const code = await generateRoutesCode(PAGES)
282
- expect(code).not.toContain('_layout')
283
- expect(code).toContain('/admin')
284
- })
285
-
286
- it('generates a route for non-underscore files in the same directory', async () => {
287
- vi.mocked(scanDirectory).mockResolvedValue([
288
- `${PAGES}/admin/users.ts`,
289
- `${PAGES}/admin/_layout.ts`,
290
- ])
291
- const code = await generateRoutesCode(PAGES)
292
- expect(code).toContain('/admin/users')
293
- expect(code).not.toContain('_layout')
294
- })
295
- })
296
-
297
- // ─── meta.revalidate ─────────────────────────────────────────────────────────
298
-
299
- describe('generateRoutesCode — meta.ssg.revalidate', () => {
300
- beforeEach(() => {
301
- vi.mocked(existsSync).mockReturnValue(true)
302
- vi.mocked(scanDirectory).mockResolvedValue([])
303
- vi.mocked(readFile).mockResolvedValue('' as never)
304
- })
305
-
306
- it('emits meta.ssg.revalidate when declared in page source', async () => {
307
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/blog.ts`])
308
- vi.mocked(readFile).mockResolvedValue(
309
- `export const meta = { ssg: { revalidate: 60 } }` as never,
310
- )
311
- const code = await generateRoutesCode(PAGES)
312
- expect(code).toContain('ssg: { revalidate: 60 }')
313
- })
314
-
315
- it('emits larger revalidate values correctly', async () => {
316
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/products.ts`])
317
- vi.mocked(readFile).mockResolvedValue(
318
- `export const meta = { ssg: { revalidate: 3600 } }` as never,
319
- )
320
- const code = await generateRoutesCode(PAGES)
321
- expect(code).toContain('ssg: { revalidate: 3600 }')
322
- })
323
-
324
- it('omits ssg meta when no revalidate is declared', async () => {
325
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
326
- vi.mocked(readFile).mockResolvedValue(
327
- `component('page-about', () => html\`<h1>About</h1>\`)` as never,
328
- )
329
- const code = await generateRoutesCode(PAGES)
330
- expect(code).not.toContain('revalidate')
331
- })
332
-
333
- it('can combine revalidate with layout in meta', async () => {
334
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/blog.ts`])
335
- vi.mocked(readFile).mockResolvedValue(
336
- `export const meta = { layout: 'minimal', ssg: { revalidate: 120 } }` as never,
337
- )
338
- const code = await generateRoutesCode(PAGES)
339
- expect(code).toContain('layout: "minimal"')
340
- expect(code).toContain('ssg: { revalidate: 120 }')
341
- })
342
- })
343
-
344
- // ─── meta.transition ─────────────────────────────────────────────────────────
345
-
346
- describe('generateRoutesCode — meta.transition', () => {
347
- beforeEach(() => {
348
- vi.mocked(existsSync).mockReturnValue(true)
349
- vi.mocked(scanDirectory).mockResolvedValue([])
350
- vi.mocked(readFile).mockResolvedValue('' as never)
351
- })
352
-
353
- it('emits meta.transition string when declared', async () => {
354
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
355
- vi.mocked(readFile).mockResolvedValue(
356
- `export const meta = { transition: 'fade' }` as never,
357
- )
358
- const code = await generateRoutesCode(PAGES)
359
- expect(code).toContain('transition: "fade"')
360
- })
361
-
362
- it('emits meta.transition true (boolean) when declared', async () => {
363
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
364
- vi.mocked(readFile).mockResolvedValue(
365
- `export const meta = { transition: true }` as never,
366
- )
367
- const code = await generateRoutesCode(PAGES)
368
- expect(code).toContain('transition: true')
369
- })
370
-
371
- it('omits transition meta when not declared', async () => {
372
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
373
- vi.mocked(readFile).mockResolvedValue(
374
- `component('page-about', () => html\`<h1>About</h1>\`)` as never,
375
- )
376
- const code = await generateRoutesCode(PAGES)
377
- expect(code).not.toContain('transition')
378
- })
379
-
380
- it('emits meta.transition false (boolean) when declared', async () => {
381
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
382
- vi.mocked(readFile).mockResolvedValue(
383
- `export const meta = { transition: false }` as never,
384
- )
385
- const code = await generateRoutesCode(PAGES)
386
- expect(code).toContain('transition: false')
387
- })
388
-
389
- it('can combine transition with middleware', async () => {
390
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/dashboard.ts`])
391
- vi.mocked(readFile).mockResolvedValue(
392
- `export const meta = { transition: 'slide', middleware: ['auth'] }` as never,
393
- )
394
- const code = await generateRoutesCode(PAGES)
395
- expect(code).toContain('transition: "slide"')
396
- expect(code).toContain('beforeEnter')
397
- expect(code).toContain('"auth"')
398
- })
399
- })
400
-
401
- // ─── meta.render ──────────────────────────────────────────────────────────────
402
-
403
- describe('generateRoutesCode — meta.render', () => {
404
- beforeEach(() => {
405
- vi.mocked(existsSync).mockReturnValue(true)
406
- vi.mocked(scanDirectory).mockResolvedValue([])
407
- vi.mocked(readFile).mockResolvedValue('' as never)
408
- })
409
-
410
- it('emits meta.render "server" when declared', async () => {
411
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/dashboard.ts`])
412
- vi.mocked(readFile).mockResolvedValue(
413
- `export const meta = { render: 'server' }` as never,
414
- )
415
- const code = await generateRoutesCode(PAGES)
416
- expect(code).toContain('render: "server"')
417
- })
418
-
419
- it('emits meta.render "spa" when declared', async () => {
420
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/profile.ts`])
421
- vi.mocked(readFile).mockResolvedValue(
422
- `export const meta = { render: 'spa' }` as never,
423
- )
424
- const code = await generateRoutesCode(PAGES)
425
- expect(code).toContain('render: "spa"')
426
- })
427
-
428
- it('emits meta.render "static" when declared', async () => {
429
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
430
- vi.mocked(readFile).mockResolvedValue(
431
- `export const meta = { render: 'static' }` as never,
432
- )
433
- const code = await generateRoutesCode(PAGES)
434
- expect(code).toContain('render: "static"')
435
- })
436
-
437
- it('omits render meta when not declared', async () => {
438
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
439
- vi.mocked(readFile).mockResolvedValue(
440
- `component('page-about', () => html\`<h1>About</h1>\`)` as never,
441
- )
442
- const code = await generateRoutesCode(PAGES)
443
- expect(code).not.toContain('render:')
444
- })
445
-
446
- it('ignores unknown render values', async () => {
447
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
448
- vi.mocked(readFile).mockResolvedValue(
449
- `export const meta = { render: 'unknown' }` as never,
450
- )
451
- const code = await generateRoutesCode(PAGES)
452
- expect(code).not.toContain('render:')
453
- })
454
-
455
- it('can combine render with layout', async () => {
456
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/dashboard.ts`])
457
- vi.mocked(readFile).mockResolvedValue(
458
- `export const meta = { render: 'server', layout: 'admin' }` as never,
459
- )
460
- const code = await generateRoutesCode(PAGES)
461
- expect(code).toContain('render: "server"')
462
- expect(code).toContain('layout: "admin"')
463
- })
464
- })
465
-
466
- // ─── meta.hydrate ─────────────────────────────────────────────────────────────
467
-
468
- describe('generateRoutesCode — meta.hydrate', () => {
469
- beforeEach(() => {
470
- vi.mocked(existsSync).mockReturnValue(true)
471
- vi.mocked(scanDirectory).mockResolvedValue([])
472
- vi.mocked(readFile).mockResolvedValue('' as never)
473
- })
474
-
475
- it('emits meta.hydrate "idle" when declared', async () => {
476
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
477
- vi.mocked(readFile).mockResolvedValue(
478
- `export const meta = { hydrate: 'idle' }` as never,
479
- )
480
- const code = await generateRoutesCode(PAGES)
481
- expect(code).toContain('hydrate: "idle"')
482
- })
483
-
484
- it('emits meta.hydrate "visible" when declared', async () => {
485
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/hero.ts`])
486
- vi.mocked(readFile).mockResolvedValue(
487
- `export const meta = { hydrate: 'visible' }` as never,
488
- )
489
- const code = await generateRoutesCode(PAGES)
490
- expect(code).toContain('hydrate: "visible"')
491
- })
492
-
493
- it('emits meta.hydrate "none" when declared', async () => {
494
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/static-page.ts`])
495
- vi.mocked(readFile).mockResolvedValue(
496
- `export const meta = { hydrate: 'none' }` as never,
497
- )
498
- const code = await generateRoutesCode(PAGES)
499
- expect(code).toContain('hydrate: "none"')
500
- })
501
-
502
- it('does NOT emit meta.hydrate when "load" (default — no-op)', async () => {
503
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
504
- vi.mocked(readFile).mockResolvedValue(
505
- `export const meta = { hydrate: 'load' }` as never,
506
- )
507
- const code = await generateRoutesCode(PAGES)
508
- expect(code).not.toContain('hydrate:')
509
- })
510
-
511
- it('omits hydrate meta when not declared', async () => {
512
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
513
- vi.mocked(readFile).mockResolvedValue(
514
- `component('page-about', () => html\`<h1>About</h1>\`)` as never,
515
- )
516
- const code = await generateRoutesCode(PAGES)
517
- expect(code).not.toContain('hydrate:')
518
- })
519
-
520
- it('ignores unknown hydrate values', async () => {
521
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
522
- vi.mocked(readFile).mockResolvedValue(
523
- `export const meta = { hydrate: 'lazy' }` as never,
524
- )
525
- const code = await generateRoutesCode(PAGES)
526
- expect(code).not.toContain('hydrate:')
527
- })
528
-
529
- it('can combine hydrate with layout and render', async () => {
530
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/dashboard.ts`])
531
- vi.mocked(readFile).mockResolvedValue(
532
- `export const meta = { hydrate: 'idle', layout: 'minimal', render: 'server' }` as never,
533
- )
534
- const code = await generateRoutesCode(PAGES)
535
- expect(code).toContain('hydrate: "idle"')
536
- expect(code).toContain('layout: "minimal"')
537
- expect(code).toContain('render: "server"')
538
- })
539
- })
540
-
541
- // ─── meta.title ───────────────────────────────────────────────────────────────
542
-
543
- describe('generateRoutesCode — meta.title', () => {
544
- beforeEach(() => {
545
- vi.mocked(existsSync).mockReturnValue(true)
546
- vi.mocked(scanDirectory).mockResolvedValue([])
547
- vi.mocked(readFile).mockResolvedValue('' as never)
548
- })
549
-
550
- it('emits meta.title when declared in page source', async () => {
551
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
552
- vi.mocked(readFile).mockResolvedValue(
553
- `export const meta = { title: 'About Us' }` as never,
554
- )
555
- const code = await generateRoutesCode(PAGES)
556
- expect(code).toContain('title: "About Us"')
557
- })
558
-
559
- it('omits title meta when not declared', async () => {
560
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
561
- vi.mocked(readFile).mockResolvedValue(
562
- `component('page-about', () => html\`<h1>About</h1>\`)` as never,
563
- )
564
- const code = await generateRoutesCode(PAGES)
565
- expect(code).not.toContain('title:')
566
- })
567
-
568
- it('can combine title with layout', async () => {
569
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
570
- vi.mocked(readFile).mockResolvedValue(
571
- `export const meta = { layout: 'minimal', title: 'About Us' }` as never,
572
- )
573
- const code = await generateRoutesCode(PAGES)
574
- expect(code).toContain('layout: "minimal"')
575
- expect(code).toContain('title: "About Us"')
576
- })
577
- })
578
-
579
- // ─── nested layouts (layoutChain) ────────────────────────────────────────────
580
-
581
- describe('generateRoutesCode — layoutChain (nested layouts)', () => {
582
- beforeEach(() => {
583
- vi.mocked(existsSync).mockReturnValue(true)
584
- vi.mocked(scanDirectory).mockResolvedValue([])
585
- vi.mocked(readFile).mockResolvedValue('' as never)
586
- })
587
-
588
- it('emits meta.layoutChain when a _layout.ts exists in ancestor directory', async () => {
589
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/admin/users.ts`])
590
- vi.mocked(readFile).mockImplementation(async (path: unknown) => {
591
- if (String(path).endsWith('_layout.ts')) return 'export default \'minimal\''
592
- return ''
593
- })
594
- const code = await generateRoutesCode(PAGES)
595
- expect(code).toContain('layoutChain:')
596
- expect(code).toContain('"default"')
597
- expect(code).toContain('"minimal"')
598
- })
599
-
600
- it('uses outerLayout from page meta when layoutChain is emitted', async () => {
601
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/admin/users.ts`])
602
- vi.mocked(readFile).mockImplementation(async (path: unknown) => {
603
- const p = String(path)
604
- if (p.endsWith('_layout.ts')) return 'export default \'sidebar\''
605
- // page source has its own layout
606
- return `export const meta = { layout: 'clean' }`
607
- })
608
- const code = await generateRoutesCode(PAGES)
609
- expect(code).toContain('layoutChain:')
610
- expect(code).toContain('"clean"')
611
- expect(code).toContain('"sidebar"')
612
- // Should not emit a plain meta.layout — the chain replaces it
613
- expect(code).not.toContain('layout: "clean"')
614
- })
615
-
616
- it('omits layoutChain for top-level pages with no _layout.ts', async () => {
617
- vi.mocked(existsSync).mockImplementation((p: unknown) => {
618
- // Return false for any _layout.ts path so no chain is resolved
619
- if (String(p).endsWith('_layout.ts')) return false
620
- return true
621
- })
622
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
623
- vi.mocked(readFile).mockResolvedValue(
624
- `export const meta = { layout: 'default' }` as never,
625
- )
626
- const code = await generateRoutesCode(PAGES)
627
- expect(code).not.toContain('layoutChain')
628
- expect(code).toContain('layout: "default"')
629
- })
630
-
631
- it('omits layoutChain for top-level pages even when existsSync returns true', async () => {
632
- // top-level page has no subdirectory segments, so resolveLayoutChain returns null immediately
633
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/index.ts`])
634
- vi.mocked(readFile).mockResolvedValue('' as never)
635
- const code = await generateRoutesCode(PAGES)
636
- expect(code).not.toContain('layoutChain')
637
- })
638
- })
639
-
640
- // ─── i18n route prefixing ─────────────────────────────────────────────────────
641
-
642
- describe('generateRoutesCode — i18n route prefixing', () => {
643
- const i18nPrefix = { locales: ['en', 'fr'], defaultLocale: 'en', strategy: 'prefix' as const }
644
- const i18nPrefixExcept = { locales: ['en', 'fr'], defaultLocale: 'en', strategy: 'prefix_except_default' as const }
645
- const i18nNoPrefix = { locales: ['en', 'fr'], defaultLocale: 'en', strategy: 'no_prefix' as const }
646
-
647
- beforeEach(() => {
648
- vi.mocked(existsSync).mockReturnValue(true)
649
- vi.mocked(scanDirectory).mockResolvedValue([])
650
- vi.mocked(readFile).mockResolvedValue('' as never)
651
- })
652
-
653
- it('prefix strategy: generates /en/about and /fr/about for about.ts', async () => {
654
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
655
- const code = await generateRoutesCode(PAGES, i18nPrefix)
656
- expect(code).toContain('"/en/about"')
657
- expect(code).toContain('"/fr/about"')
658
- expect(code).not.toContain('path: "/about"')
659
- })
660
-
661
- it('prefix_except_default strategy: generates /about and /fr/about for about.ts', async () => {
662
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
663
- const code = await generateRoutesCode(PAGES, i18nPrefixExcept)
664
- expect(code).toContain('"/about"')
665
- expect(code).toContain('"/fr/about"')
666
- expect(code).not.toContain('"/en/about"')
667
- })
668
-
669
- it('no_prefix strategy: does not add locale prefixes', async () => {
670
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
671
- const code = await generateRoutesCode(PAGES, i18nNoPrefix)
672
- expect(code).toContain('"/about"')
673
- expect(code).not.toContain('"/en/about"')
674
- expect(code).not.toContain('"/fr/about"')
675
- })
676
-
677
- it('catch-all routes are never prefixed', async () => {
678
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/404.ts`])
679
- const code = await generateRoutesCode(PAGES, i18nPrefix)
680
- expect(code).toContain('"/:all*"')
681
- expect(code).not.toContain('"/en/:all*"')
682
- expect(code).not.toContain('"/fr/:all*"')
683
- })
684
-
685
- it('emits meta.locale for each generated locale route', async () => {
686
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
687
- const code = await generateRoutesCode(PAGES, i18nPrefix)
688
- expect(code).toContain('locale: "en"')
689
- expect(code).toContain('locale: "fr"')
690
- })
691
-
692
- it('without i18n config, generates a single route as before', async () => {
693
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
694
- const code = await generateRoutesCode(PAGES)
695
- expect(code).toContain('"/about"')
696
- expect(code).not.toContain('"/en/about"')
697
- expect(code).not.toContain('locale:')
698
- })
699
- })
700
-
701
- // ─── P1-1: Synthetic 404 catch-all ───────────────────────────────────────────
702
-
703
- describe('generateRoutesCode — synthetic 404 catch-all (P1-1)', () => {
704
- beforeEach(() => {
705
- vi.mocked(existsSync).mockReturnValue(true)
706
- vi.mocked(scanDirectory).mockResolvedValue([])
707
- vi.mocked(readFile).mockResolvedValue('' as never)
708
- })
709
-
710
- it('adds a synthetic /:all* route when no user-defined catch-all exists', async () => {
711
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
712
- const code = await generateRoutesCode(PAGES)
713
- // Synthetic route uses single quotes in the generated code
714
- expect(code).toContain("path: '/:all*'")
715
- // Synthetic route returns default: null
716
- expect(code).toContain('default: null')
717
- expect(code).toContain('meta: { _cerNotFound: true }')
718
- })
719
-
720
- it('does NOT add a synthetic catch-all when a user 404.ts already exists', async () => {
721
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/404.ts`, `${PAGES}/about.ts`])
722
- const code = await generateRoutesCode(PAGES)
723
- // Should have exactly one /:all* (the real 404 page, with double quotes)
724
- expect(code).toContain('"/:all*"')
725
- expect(code).not.toContain('default: null')
726
- })
727
-
728
- it('does NOT add a synthetic catch-all when a user [...all].ts already exists', async () => {
729
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/[...all].ts`])
730
- const code = await generateRoutesCode(PAGES)
731
- // Real catch-all uses double quotes
732
- expect(code).toContain('"/:all*"')
733
- expect(code).not.toContain('default: null')
734
- expect(code).not.toContain('_cerNotFound: true')
735
- })
736
-
737
- it('does not add a synthetic catch-all for an empty pages directory (no routes)', async () => {
738
- const code = await generateRoutesCode(PAGES)
739
- // Empty routes array — no synthetic catch-all needed when there are no routes
740
- expect(code).toContain('const routes = []')
741
- expect(code).not.toContain("path: '/:all*'")
742
- })
743
- })
744
-
745
- // ─── hydration middleware replay skip ───────────────────────────────────────
746
-
747
- describe('generateRoutesCode — hydration middleware replay skip', () => {
748
- beforeEach(() => {
749
- vi.mocked(existsSync).mockReturnValue(true)
750
- vi.mocked(scanDirectory).mockResolvedValue([])
751
- vi.mocked(readFile).mockResolvedValue('' as never)
752
- })
753
-
754
- it('emits a one-shot hydration replay skip before importing middleware', async () => {
755
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/dashboard.ts`])
756
- vi.mocked(readFile).mockResolvedValue(
757
- `export const meta = { middleware: ['auth'] }` as never,
758
- )
759
- const code = await generateRoutesCode(PAGES)
760
- expect(code).toContain(`const _hydrationEntry = (globalThis).__CER_HYDRATION_ENTRY__`)
761
- expect(code).toContain(`from.path === to.path`)
762
- expect(code).toContain(`_hydrationEntry.path === to.path`)
763
- expect(code).toContain(`delete (globalThis).__CER_HYDRATION_ENTRY__`)
764
- const hydrationIdx = code.indexOf(`const _hydrationEntry = (globalThis).__CER_HYDRATION_ENTRY__`)
765
- const importIdx = code.indexOf(`const { middleware } = await import('virtual:cer-middleware')`)
766
- expect(hydrationIdx).toBeGreaterThanOrEqual(0)
767
- expect(importIdx).toBeGreaterThan(hydrationIdx)
768
- })
769
- })
770
-
771
- // ─── P2-1: _layout.ts group meta inheritance ─────────────────────────────────
772
-
773
- describe('generateRoutesCode — group meta from _layout.ts (P2-1)', () => {
774
- beforeEach(() => {
775
- vi.mocked(existsSync).mockReturnValue(true)
776
- vi.mocked(scanDirectory).mockResolvedValue([])
777
- vi.mocked(readFile).mockResolvedValue('' as never)
778
- })
779
-
780
- it('inherits middleware from ancestor _layout.ts when page has none', async () => {
781
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/admin/dashboard.ts`])
782
- vi.mocked(readFile).mockImplementation(async (path: unknown) => {
783
- if (String(path).endsWith('_layout.ts')) {
784
- return `export const meta = { middleware: ['auth'] }`
785
- }
786
- return ''
787
- })
788
- const code = await generateRoutesCode(PAGES)
789
- expect(code).toContain('beforeEnter')
790
- expect(code).toContain('"auth"')
791
- })
792
-
793
- it('page-level middleware overrides group middleware from _layout.ts', async () => {
794
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/admin/profile.ts`])
795
- vi.mocked(readFile).mockImplementation(async (path: unknown) => {
796
- if (String(path).endsWith('_layout.ts')) {
797
- return `export const meta = { middleware: ['auth'] }`
798
- }
799
- // page declares its own middleware
800
- return `export const meta = { middleware: ['verified'] }`
801
- })
802
- const code = await generateRoutesCode(PAGES)
803
- expect(code).toContain('"verified"')
804
- // Page overrides group — group middleware should NOT appear
805
- expect(code).not.toContain('"auth"')
806
- })
807
-
808
- it('inherits layout from ancestor _layout.ts meta.layout when page has no explicit layout', async () => {
809
- vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/docs/intro.ts`])
810
- vi.mocked(readFile).mockImplementation(async (path: unknown) => {
811
- if (String(path).endsWith('_layout.ts')) {
812
- // _layout.ts exports meta.layout (group default layout)
813
- return `export const meta = { layout: 'docs' }`
814
- }
815
- return ''
816
- })
817
- const code = await generateRoutesCode(PAGES)
818
- // The layout from group meta should appear in the route
819
- expect(code).toContain('"docs"')
820
- })
821
- })
822
-
823
- // ─── P2-2: Per-route error tags ───────────────────────────────────────────────
824
-
825
- describe('generateRoutesCode — per-route error tags (P2-2)', () => {
826
- beforeEach(() => {
827
- vi.mocked(existsSync).mockReturnValue(true)
828
- vi.mocked(scanDirectory).mockResolvedValue([])
829
- vi.mocked(readFile).mockResolvedValue('' as never)
830
- })
831
-
832
- it('excludes *.error.ts files from generating their own routes', async () => {
833
- vi.mocked(scanDirectory).mockResolvedValue([
834
- `${PAGES}/about.ts`,
835
- `${PAGES}/about.error.ts`,
836
- ])
837
- const code = await generateRoutesCode(PAGES)
838
- // The about route should exist
839
- expect(code).toContain('"/about"')
840
- // The error file must NOT produce a separate route path like /about-error or /about.error
841
- expect(code).not.toContain('path: "/about-error"')
842
- expect(code).not.toContain('path: "/about.error"')
843
- })
844
-
845
- it('excludes _error.ts files from generated routes', async () => {
846
- vi.mocked(scanDirectory).mockResolvedValue([
847
- `${PAGES}/admin/users.ts`,
848
- `${PAGES}/admin/_error.ts`,
849
- ])
850
- const code = await generateRoutesCode(PAGES)
851
- expect(code).toContain('/admin/users')
852
- expect(code).not.toContain('_error')
853
- })
854
-
855
- it('load() includes co-located error file import when present', async () => {
856
- vi.mocked(scanDirectory).mockResolvedValue([
857
- `${PAGES}/dashboard.ts`,
858
- `${PAGES}/dashboard.error.ts`,
859
- ])
860
- vi.mocked(existsSync).mockImplementation((p: unknown) => {
861
- const path = String(p)
862
- // The co-located error file exists
863
- if (path.endsWith('dashboard.error.ts')) return true
864
- return true
865
- })
866
- const code = await generateRoutesCode(PAGES)
867
- // load() should import the error component alongside the page
868
- expect(code).toContain('dashboard.error.ts')
869
- expect(code).toContain('Promise.all')
870
- })
871
-
872
- it('emits meta.errorTag when co-located *.error.ts is present', async () => {
873
- vi.mocked(scanDirectory).mockResolvedValue([
874
- `${PAGES}/settings.ts`,
875
- `${PAGES}/settings.error.ts`,
876
- ])
877
- vi.mocked(existsSync).mockImplementation((p: unknown) => {
878
- if (String(p).endsWith('settings.error.ts')) return true
879
- return true
880
- })
881
- const code = await generateRoutesCode(PAGES)
882
- expect(code).toContain('errorTag')
883
- })
884
- })