@jasonshimmy/vite-plugin-cer-app 0.23.2 → 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 (392) 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/create/index.d.ts +11 -1
  18. package/dist/cli/create/index.d.ts.map +1 -1
  19. package/dist/cli/create/index.js +135 -65
  20. package/dist/cli/create/index.js.map +1 -1
  21. package/dist/cli/create/templates/spa/package.json.tpl +5 -3
  22. package/dist/cli/create/templates/ssg/cer.config.ts.tpl +2 -0
  23. package/dist/cli/create/templates/ssg/package.json.tpl +5 -3
  24. package/dist/cli/create/templates/ssr/package.json.tpl +5 -3
  25. package/dist/cli/index.js +2 -0
  26. package/dist/cli/index.js.map +1 -1
  27. package/dist/index.d.ts +3 -1
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/plugin/build-ssg.d.ts +22 -6
  30. package/dist/plugin/build-ssg.d.ts.map +1 -1
  31. package/dist/plugin/build-ssg.js +227 -17
  32. package/dist/plugin/build-ssg.js.map +1 -1
  33. package/dist/plugin/content/parser.d.ts.map +1 -1
  34. package/dist/plugin/content/parser.js +54 -10
  35. package/dist/plugin/content/parser.js.map +1 -1
  36. package/dist/plugin/dev-server.d.ts +4 -1
  37. package/dist/plugin/dev-server.d.ts.map +1 -1
  38. package/dist/plugin/dev-server.js.map +1 -1
  39. package/dist/plugin/dts-generator.d.ts.map +1 -1
  40. package/dist/plugin/dts-generator.js +50 -8
  41. package/dist/plugin/dts-generator.js.map +1 -1
  42. package/dist/plugin/generated-dir.d.ts +2 -0
  43. package/dist/plugin/generated-dir.d.ts.map +1 -1
  44. package/dist/plugin/generated-dir.js +17 -6
  45. package/dist/plugin/generated-dir.js.map +1 -1
  46. package/dist/plugin/html-post-process.d.ts +6 -0
  47. package/dist/plugin/html-post-process.d.ts.map +1 -1
  48. package/dist/plugin/html-post-process.js +24 -0
  49. package/dist/plugin/html-post-process.js.map +1 -1
  50. package/dist/plugin/index.d.ts.map +1 -1
  51. package/dist/plugin/index.js +60 -13
  52. package/dist/plugin/index.js.map +1 -1
  53. package/dist/plugin/transforms/auto-import.d.ts.map +1 -1
  54. package/dist/plugin/transforms/auto-import.js +7 -0
  55. package/dist/plugin/transforms/auto-import.js.map +1 -1
  56. package/dist/plugin/transforms/prune-runtime-css.d.ts +8 -0
  57. package/dist/plugin/transforms/prune-runtime-css.d.ts.map +1 -0
  58. package/dist/plugin/transforms/prune-runtime-css.js +40 -0
  59. package/dist/plugin/transforms/prune-runtime-css.js.map +1 -0
  60. package/dist/runtime/app-template.d.ts +1 -1
  61. package/dist/runtime/app-template.d.ts.map +1 -1
  62. package/dist/runtime/app-template.js +206 -36
  63. package/dist/runtime/app-template.js.map +1 -1
  64. package/dist/runtime/composables/index.d.ts +4 -0
  65. package/dist/runtime/composables/index.d.ts.map +1 -1
  66. package/dist/runtime/composables/index.js +2 -0
  67. package/dist/runtime/composables/index.js.map +1 -1
  68. package/dist/runtime/composables/use-active-headings.d.ts +24 -0
  69. package/dist/runtime/composables/use-active-headings.d.ts.map +1 -0
  70. package/dist/runtime/composables/use-active-headings.js +112 -0
  71. package/dist/runtime/composables/use-active-headings.js.map +1 -0
  72. package/dist/runtime/composables/use-content-page.d.ts +45 -0
  73. package/dist/runtime/composables/use-content-page.d.ts.map +1 -0
  74. package/dist/runtime/composables/use-content-page.js +179 -0
  75. package/dist/runtime/composables/use-content-page.js.map +1 -0
  76. package/dist/runtime/composables/use-content-search.d.ts.map +1 -1
  77. package/dist/runtime/composables/use-content-search.js +1 -5
  78. package/dist/runtime/composables/use-content-search.js.map +1 -1
  79. package/dist/runtime/composables/use-fetch.d.ts +2 -1
  80. package/dist/runtime/composables/use-fetch.d.ts.map +1 -1
  81. package/dist/runtime/composables/use-fetch.js +112 -43
  82. package/dist/runtime/composables/use-fetch.js.map +1 -1
  83. package/dist/runtime/composables/use-route.d.ts.map +1 -1
  84. package/dist/runtime/composables/use-route.js +39 -11
  85. package/dist/runtime/composables/use-route.js.map +1 -1
  86. package/dist/runtime/content/client.js +2 -2
  87. package/dist/runtime/content/client.js.map +1 -1
  88. package/dist/runtime/entry-server-template.d.ts +16 -1
  89. package/dist/runtime/entry-server-template.d.ts.map +1 -1
  90. package/dist/runtime/entry-server-template.js +120 -13
  91. package/dist/runtime/entry-server-template.js.map +1 -1
  92. package/dist/types/config.d.ts +45 -4
  93. package/dist/types/config.d.ts.map +1 -1
  94. package/dist/types/config.js.map +1 -1
  95. package/dist/types/middleware.d.ts +2 -2
  96. package/dist/types/middleware.d.ts.map +1 -1
  97. package/package.json +22 -19
  98. package/.github/copilot-instructions.md +0 -169
  99. package/.github/workflows/publish.yml +0 -257
  100. package/.nvmrc +0 -1
  101. package/CHANGELOG.md +0 -236
  102. package/CLAUDE.md +0 -113
  103. package/commits.txt +0 -1
  104. package/cypress.config.ts +0 -16
  105. package/docs/authentication.md +0 -285
  106. package/docs/cli.md +0 -324
  107. package/docs/components.md +0 -237
  108. package/docs/composables.md +0 -907
  109. package/docs/configuration.md +0 -696
  110. package/docs/content.md +0 -570
  111. package/docs/data-loading.md +0 -336
  112. package/docs/getting-started.md +0 -237
  113. package/docs/head-management.md +0 -212
  114. package/docs/i18n.md +0 -159
  115. package/docs/layouts.md +0 -224
  116. package/docs/middleware.md +0 -234
  117. package/docs/plugins.md +0 -170
  118. package/docs/rendering-modes.md +0 -377
  119. package/docs/routing.md +0 -485
  120. package/docs/server-api.md +0 -253
  121. package/docs/testing.md +0 -455
  122. package/docs/use-fetch.md +0 -183
  123. package/e2e/cypress/e2e/api.cy.ts +0 -123
  124. package/e2e/cypress/e2e/async-component.cy.ts +0 -27
  125. package/e2e/cypress/e2e/component-splitting.cy.ts +0 -50
  126. package/e2e/cypress/e2e/content.cy.ts +0 -425
  127. package/e2e/cypress/e2e/cookie.cy.ts +0 -85
  128. package/e2e/cypress/e2e/data.cy.ts +0 -158
  129. package/e2e/cypress/e2e/error-boundary.cy.ts +0 -88
  130. package/e2e/cypress/e2e/fouc.cy.ts +0 -254
  131. package/e2e/cypress/e2e/group-meta.cy.ts +0 -36
  132. package/e2e/cypress/e2e/head.cy.ts +0 -89
  133. package/e2e/cypress/e2e/i18n.cy.ts +0 -79
  134. package/e2e/cypress/e2e/interactive.cy.ts +0 -137
  135. package/e2e/cypress/e2e/isr-nested-runtime.cy.ts +0 -102
  136. package/e2e/cypress/e2e/jit-css-custom-colors.cy.ts +0 -43
  137. package/e2e/cypress/e2e/middleware-chain.cy.ts +0 -89
  138. package/e2e/cypress/e2e/middleware-status.cy.ts +0 -30
  139. package/e2e/cypress/e2e/middleware.cy.ts +0 -45
  140. package/e2e/cypress/e2e/navigate.cy.ts +0 -39
  141. package/e2e/cypress/e2e/observability.cy.ts +0 -77
  142. package/e2e/cypress/e2e/per-route-error.cy.ts +0 -42
  143. package/e2e/cypress/e2e/per-route-render.cy.ts +0 -70
  144. package/e2e/cypress/e2e/preview-hardening.cy.ts +0 -88
  145. package/e2e/cypress/e2e/routes.cy.ts +0 -128
  146. package/e2e/cypress/e2e/seo-meta.cy.ts +0 -108
  147. package/e2e/cypress/e2e/server-middleware.cy.ts +0 -46
  148. package/e2e/cypress/e2e/session-rotation.cy.ts +0 -25
  149. package/e2e/cypress/e2e/session.cy.ts +0 -73
  150. package/e2e/cypress/e2e/ssr-render-error.cy.ts +0 -68
  151. package/e2e/cypress/e2e/synthetic-404.cy.ts +0 -45
  152. package/e2e/cypress/e2e/use-page-data.cy.ts +0 -122
  153. package/e2e/cypress/e2e/use-state.cy.ts +0 -138
  154. package/e2e/cypress/support/commands.ts +0 -120
  155. package/e2e/cypress/support/e2e.ts +0 -10
  156. package/e2e/cypress/tsconfig.json +0 -14
  157. package/e2e/kitchen-sink/app/components/ks-badge.ts +0 -8
  158. package/e2e/kitchen-sink/app/composables/useKsCounter.ts +0 -9
  159. package/e2e/kitchen-sink/app/error.ts +0 -18
  160. package/e2e/kitchen-sink/app/layouts/admin.ts +0 -13
  161. package/e2e/kitchen-sink/app/layouts/default.ts +0 -25
  162. package/e2e/kitchen-sink/app/layouts/group.ts +0 -9
  163. package/e2e/kitchen-sink/app/layouts/minimal.ts +0 -7
  164. package/e2e/kitchen-sink/app/loading.ts +0 -9
  165. package/e2e/kitchen-sink/app/middleware/auth.ts +0 -9
  166. package/e2e/kitchen-sink/app/middleware/custom-log.ts +0 -4
  167. package/e2e/kitchen-sink/app/middleware/group-auth.ts +0 -7
  168. package/e2e/kitchen-sink/app/middleware/logger.ts +0 -14
  169. package/e2e/kitchen-sink/app/pages/(auth)/login.ts +0 -13
  170. package/e2e/kitchen-sink/app/pages/(auth)/protected.ts +0 -16
  171. package/e2e/kitchen-sink/app/pages/404.ts +0 -9
  172. package/e2e/kitchen-sink/app/pages/about.ts +0 -17
  173. package/e2e/kitchen-sink/app/pages/admin/_layout.ts +0 -1
  174. package/e2e/kitchen-sink/app/pages/admin/dashboard.ts +0 -11
  175. package/e2e/kitchen-sink/app/pages/async-component-error-test.ts +0 -18
  176. package/e2e/kitchen-sink/app/pages/async-component-test.ts +0 -18
  177. package/e2e/kitchen-sink/app/pages/blog/[slug].ts +0 -59
  178. package/e2e/kitchen-sink/app/pages/blog/index.ts +0 -51
  179. package/e2e/kitchen-sink/app/pages/content-blog.ts +0 -37
  180. package/e2e/kitchen-sink/app/pages/content-doc.ts +0 -42
  181. package/e2e/kitchen-sink/app/pages/content-fallback.ts +0 -36
  182. package/e2e/kitchen-sink/app/pages/content-guides.ts +0 -35
  183. package/e2e/kitchen-sink/app/pages/content-index.ts +0 -39
  184. package/e2e/kitchen-sink/app/pages/content-search.ts +0 -36
  185. package/e2e/kitchen-sink/app/pages/cookie-test.ts +0 -22
  186. package/e2e/kitchen-sink/app/pages/counter.ts +0 -17
  187. package/e2e/kitchen-sink/app/pages/group-auth-blocked.ts +0 -9
  188. package/e2e/kitchen-sink/app/pages/group-meta-test/_layout.ts +0 -6
  189. package/e2e/kitchen-sink/app/pages/group-meta-test/custom-mw.ts +0 -14
  190. package/e2e/kitchen-sink/app/pages/group-meta-test/page.ts +0 -9
  191. package/e2e/kitchen-sink/app/pages/group-meta-test/protected.ts +0 -8
  192. package/e2e/kitchen-sink/app/pages/head.ts +0 -20
  193. package/e2e/kitchen-sink/app/pages/i18n-test.ts +0 -14
  194. package/e2e/kitchen-sink/app/pages/index.ts +0 -27
  195. package/e2e/kitchen-sink/app/pages/isr-test.ts +0 -17
  196. package/e2e/kitchen-sink/app/pages/items/[id].ts +0 -20
  197. package/e2e/kitchen-sink/app/pages/jit-css-custom-colors-test.ts +0 -14
  198. package/e2e/kitchen-sink/app/pages/loader-error-test.ts +0 -13
  199. package/e2e/kitchen-sink/app/pages/loader-props-test.ts +0 -19
  200. package/e2e/kitchen-sink/app/pages/loader-response-error-test.ts +0 -13
  201. package/e2e/kitchen-sink/app/pages/middleware-chain-test.ts +0 -12
  202. package/e2e/kitchen-sink/app/pages/middleware-status-test.ts +0 -9
  203. package/e2e/kitchen-sink/app/pages/navigate-test.ts +0 -16
  204. package/e2e/kitchen-sink/app/pages/observability-test.ts +0 -25
  205. package/e2e/kitchen-sink/app/pages/per-route-error-test.error.ts +0 -13
  206. package/e2e/kitchen-sink/app/pages/per-route-error-test.ts +0 -15
  207. package/e2e/kitchen-sink/app/pages/plugin-dsd-test.ts +0 -10
  208. package/e2e/kitchen-sink/app/pages/render-error-test.ts +0 -12
  209. package/e2e/kitchen-sink/app/pages/render-server-test.ts +0 -12
  210. package/e2e/kitchen-sink/app/pages/render-spa-test.ts +0 -12
  211. package/e2e/kitchen-sink/app/pages/route-info.ts +0 -14
  212. package/e2e/kitchen-sink/app/pages/seo-test.ts +0 -23
  213. package/e2e/kitchen-sink/app/pages/session-rotation-test.ts +0 -25
  214. package/e2e/kitchen-sink/app/pages/use-state-test.ts +0 -37
  215. package/e2e/kitchen-sink/app/plugins/01.setup.ts +0 -7
  216. package/e2e/kitchen-sink/app/plugins/02.ks-plugin-components.ts +0 -27
  217. package/e2e/kitchen-sink/cer-auto-imports.d.ts +0 -51
  218. package/e2e/kitchen-sink/cer-env.d.ts +0 -35
  219. package/e2e/kitchen-sink/cer-tsconfig.json +0 -30
  220. package/e2e/kitchen-sink/cer.config.ts +0 -43
  221. package/e2e/kitchen-sink/content/01.guides/01.index.md +0 -8
  222. package/e2e/kitchen-sink/content/01.guides/02.intro.md +0 -8
  223. package/e2e/kitchen-sink/content/01.guides/10.advanced.md +0 -8
  224. package/e2e/kitchen-sink/content/blog/2026-04-01-hello.md +0 -26
  225. package/e2e/kitchen-sink/content/blog/2026-04-02-draft.md +0 -10
  226. package/e2e/kitchen-sink/content/blog/index.md +0 -8
  227. package/e2e/kitchen-sink/content/blog/no-frontmatter.md +0 -7
  228. package/e2e/kitchen-sink/content/docs/getting-started.md +0 -48
  229. package/e2e/kitchen-sink/content/index.md +0 -16
  230. package/e2e/kitchen-sink/server/api/echo.ts +0 -12
  231. package/e2e/kitchen-sink/server/api/health.ts +0 -3
  232. package/e2e/kitchen-sink/server/api/posts/[slug].ts +0 -11
  233. package/e2e/kitchen-sink/server/api/posts/index.ts +0 -5
  234. package/e2e/kitchen-sink/server/api/session.ts +0 -23
  235. package/e2e/kitchen-sink/server/data/posts.ts +0 -21
  236. package/e2e/kitchen-sink/server/middleware/01-headers.ts +0 -6
  237. package/e2e/kitchen-sink/server/middleware/02-status-test.ts +0 -10
  238. package/e2e/kitchen-sink/tsconfig.json +0 -3
  239. package/e2e/scripts/clean.mjs +0 -14
  240. package/eslint.config.ts +0 -22
  241. package/src/__tests__/cli/adapters/cloudflare-worker.integration.test.ts +0 -132
  242. package/src/__tests__/cli/adapters/cloudflare.test.ts +0 -332
  243. package/src/__tests__/cli/adapters/netlify-bridge.integration.test.ts +0 -139
  244. package/src/__tests__/cli/adapters/netlify.test.ts +0 -286
  245. package/src/__tests__/cli/adapters/vercel-launcher.integration.test.ts +0 -129
  246. package/src/__tests__/cli/adapters/vercel.test.ts +0 -263
  247. package/src/__tests__/cli/preview-hardening.test.ts +0 -265
  248. package/src/__tests__/cli/preview-isr.test.ts +0 -320
  249. package/src/__tests__/index.test.ts +0 -21
  250. package/src/__tests__/plugin/app-template.test.ts +0 -166
  251. package/src/__tests__/plugin/build-ssg-render.test.ts +0 -155
  252. package/src/__tests__/plugin/build-ssg.test.ts +0 -598
  253. package/src/__tests__/plugin/build-ssr.test.ts +0 -192
  254. package/src/__tests__/plugin/cer-app-plugin.test.ts +0 -552
  255. package/src/__tests__/plugin/content/emitter.test.ts +0 -117
  256. package/src/__tests__/plugin/content/loader.test.ts +0 -154
  257. package/src/__tests__/plugin/content/parser.test.ts +0 -381
  258. package/src/__tests__/plugin/content/path-utils.test.ts +0 -69
  259. package/src/__tests__/plugin/content/search.test.ts +0 -119
  260. package/src/__tests__/plugin/dev-server.test.ts +0 -813
  261. package/src/__tests__/plugin/dts-generator.test.ts +0 -394
  262. package/src/__tests__/plugin/entry-server-template.test.ts +0 -385
  263. package/src/__tests__/plugin/generated-dir.test.ts +0 -144
  264. package/src/__tests__/plugin/html-post-process.test.ts +0 -146
  265. package/src/__tests__/plugin/path-utils.test.ts +0 -411
  266. package/src/__tests__/plugin/resolve-config.test.ts +0 -233
  267. package/src/__tests__/plugin/scanner.test.ts +0 -186
  268. package/src/__tests__/plugin/transforms/auto-import.test.ts +0 -510
  269. package/src/__tests__/plugin/transforms/head-inject.test.ts +0 -178
  270. package/src/__tests__/plugin/virtual/composables.test.ts +0 -57
  271. package/src/__tests__/plugin/virtual/content-components.test.ts +0 -66
  272. package/src/__tests__/plugin/virtual/error.test.ts +0 -71
  273. package/src/__tests__/plugin/virtual/layouts.test.ts +0 -70
  274. package/src/__tests__/plugin/virtual/loading.test.ts +0 -72
  275. package/src/__tests__/plugin/virtual/middleware.test.ts +0 -83
  276. package/src/__tests__/plugin/virtual/plugins.test.ts +0 -129
  277. package/src/__tests__/plugin/virtual/routes.test.ts +0 -884
  278. package/src/__tests__/plugin/virtual/server-api.test.ts +0 -164
  279. package/src/__tests__/plugin/virtual/server-middleware.test.ts +0 -102
  280. package/src/__tests__/runtime/app-template.test.ts +0 -208
  281. package/src/__tests__/runtime/define-middleware.test.ts +0 -64
  282. package/src/__tests__/runtime/define-server-middleware.test.ts +0 -54
  283. package/src/__tests__/runtime/entry-client-template.test.ts +0 -61
  284. package/src/__tests__/runtime/isr-handler.test.ts +0 -338
  285. package/src/__tests__/runtime/oauth/handler.test.ts +0 -358
  286. package/src/__tests__/runtime/oauth/providers.test.ts +0 -47
  287. package/src/__tests__/runtime/oauth-index.test.ts +0 -43
  288. package/src/__tests__/runtime/use-auth.test.ts +0 -169
  289. package/src/__tests__/runtime/use-content-search-composable.test.ts +0 -405
  290. package/src/__tests__/runtime/use-content-search.test.ts +0 -163
  291. package/src/__tests__/runtime/use-content.test.ts +0 -226
  292. package/src/__tests__/runtime/use-cookie.test.ts +0 -277
  293. package/src/__tests__/runtime/use-fetch-component.test.ts +0 -214
  294. package/src/__tests__/runtime/use-fetch.test.ts +0 -377
  295. package/src/__tests__/runtime/use-head.test.ts +0 -311
  296. package/src/__tests__/runtime/use-inject-client.test.ts +0 -67
  297. package/src/__tests__/runtime/use-inject.test.ts +0 -66
  298. package/src/__tests__/runtime/use-locale.test.ts +0 -152
  299. package/src/__tests__/runtime/use-navigate.test.ts +0 -121
  300. package/src/__tests__/runtime/use-page-data.test.ts +0 -298
  301. package/src/__tests__/runtime/use-route.test.ts +0 -156
  302. package/src/__tests__/runtime/use-runtime-config.test.ts +0 -182
  303. package/src/__tests__/runtime/use-seo-meta.test.ts +0 -109
  304. package/src/__tests__/runtime/use-session.test.ts +0 -320
  305. package/src/__tests__/runtime/use-state.test.ts +0 -340
  306. package/src/__tests__/types/config.test.ts +0 -18
  307. package/src/cli/adapters/cloudflare.ts +0 -335
  308. package/src/cli/adapters/netlify.ts +0 -326
  309. package/src/cli/adapters/vercel.ts +0 -311
  310. package/src/cli/commands/adapt.ts +0 -84
  311. package/src/cli/commands/build.ts +0 -165
  312. package/src/cli/commands/dev.ts +0 -125
  313. package/src/cli/commands/generate.ts +0 -91
  314. package/src/cli/commands/preview-isr.ts +0 -172
  315. package/src/cli/commands/preview.ts +0 -523
  316. package/src/cli/create/index.ts +0 -275
  317. package/src/cli/create/templates/shared/.gitignore.tpl +0 -25
  318. package/src/cli/create/templates/shared/app/layouts/default.ts.tpl +0 -15
  319. package/src/cli/create/templates/shared/app/pages/index.ts.tpl +0 -8
  320. package/src/cli/create/templates/shared/index.html.tpl +0 -12
  321. package/src/cli/create/templates/shared/tsconfig.json.tpl +0 -6
  322. package/src/cli/create/templates/spa/cer.config.ts.tpl +0 -6
  323. package/src/cli/create/templates/spa/package.json.tpl +0 -18
  324. package/src/cli/create/templates/ssg/cer.config.ts.tpl +0 -10
  325. package/src/cli/create/templates/ssg/package.json.tpl +0 -19
  326. package/src/cli/create/templates/ssr/cer.config.ts.tpl +0 -6
  327. package/src/cli/create/templates/ssr/package.json.tpl +0 -18
  328. package/src/cli/index.ts +0 -22
  329. package/src/index.ts +0 -20
  330. package/src/plugin/build-ssg.ts +0 -403
  331. package/src/plugin/build-ssr.ts +0 -134
  332. package/src/plugin/content/emitter.ts +0 -50
  333. package/src/plugin/content/index.ts +0 -233
  334. package/src/plugin/content/parser.ts +0 -259
  335. package/src/plugin/content/path-utils.ts +0 -55
  336. package/src/plugin/content/scanner.ts +0 -26
  337. package/src/plugin/content/search.ts +0 -28
  338. package/src/plugin/dev-server.ts +0 -379
  339. package/src/plugin/dts-generator.ts +0 -273
  340. package/src/plugin/generated-dir.ts +0 -121
  341. package/src/plugin/html-post-process.ts +0 -96
  342. package/src/plugin/index.ts +0 -584
  343. package/src/plugin/path-utils.ts +0 -186
  344. package/src/plugin/scanner.ts +0 -65
  345. package/src/plugin/transforms/auto-import.ts +0 -206
  346. package/src/plugin/transforms/head-inject.ts +0 -161
  347. package/src/plugin/virtual/composables.ts +0 -28
  348. package/src/plugin/virtual/content-components.ts +0 -83
  349. package/src/plugin/virtual/error.ts +0 -34
  350. package/src/plugin/virtual/layouts.ts +0 -41
  351. package/src/plugin/virtual/loading.ts +0 -32
  352. package/src/plugin/virtual/middleware.ts +0 -45
  353. package/src/plugin/virtual/plugins.ts +0 -46
  354. package/src/plugin/virtual/routes.ts +0 -514
  355. package/src/plugin/virtual/server-api.ts +0 -85
  356. package/src/plugin/virtual/server-middleware.ts +0 -44
  357. package/src/runtime/app-template.ts +0 -377
  358. package/src/runtime/composables/define-middleware.ts +0 -17
  359. package/src/runtime/composables/define-server-middleware.ts +0 -18
  360. package/src/runtime/composables/index.ts +0 -27
  361. package/src/runtime/composables/use-auth.ts +0 -125
  362. package/src/runtime/composables/use-content-search.ts +0 -205
  363. package/src/runtime/composables/use-content.ts +0 -146
  364. package/src/runtime/composables/use-cookie.ts +0 -135
  365. package/src/runtime/composables/use-fetch.ts +0 -438
  366. package/src/runtime/composables/use-head.ts +0 -234
  367. package/src/runtime/composables/use-inject.ts +0 -49
  368. package/src/runtime/composables/use-locale.ts +0 -109
  369. package/src/runtime/composables/use-navigate.ts +0 -58
  370. package/src/runtime/composables/use-page-data.ts +0 -93
  371. package/src/runtime/composables/use-route.ts +0 -71
  372. package/src/runtime/composables/use-runtime-config.ts +0 -123
  373. package/src/runtime/composables/use-seo-meta.ts +0 -80
  374. package/src/runtime/composables/use-session.ts +0 -186
  375. package/src/runtime/composables/use-state.ts +0 -92
  376. package/src/runtime/content/client.ts +0 -168
  377. package/src/runtime/entry-client-template.ts +0 -39
  378. package/src/runtime/entry-server-template.ts +0 -493
  379. package/src/runtime/isr-handler.ts +0 -184
  380. package/src/runtime/oauth/handler.ts +0 -407
  381. package/src/runtime/oauth/index.ts +0 -49
  382. package/src/runtime/oauth/providers.ts +0 -38
  383. package/src/types/api.ts +0 -40
  384. package/src/types/config.ts +0 -379
  385. package/src/types/content.ts +0 -66
  386. package/src/types/index.ts +0 -7
  387. package/src/types/middleware.ts +0 -63
  388. package/src/types/page.ts +0 -117
  389. package/src/types/plugin.ts +0 -34
  390. package/tsconfig.build.json +0 -10
  391. package/tsconfig.json +0 -19
  392. package/vitest.config.ts +0 -33
@@ -1,385 +0,0 @@
1
- import { describe, it, expect } from 'vitest'
2
- import { readFileSync } from 'node:fs'
3
- import { resolve } from 'pathe'
4
-
5
- const src = readFileSync(
6
- resolve(import.meta.dirname, '../../runtime/entry-server-template.ts'),
7
- 'utf-8',
8
- )
9
-
10
- describe('entry-server-template (ENTRY_SERVER_TEMPLATE content)', () => {
11
- it('does not import virtual:cer-components (components injected per-file by cerComponentImports)', () => {
12
- expect(src).not.toContain('virtual:cer-components')
13
- })
14
-
15
- it('imports virtual:cer-routes', () => {
16
- expect(src).toContain('virtual:cer-routes')
17
- })
18
-
19
- it('imports virtual:cer-layouts', () => {
20
- expect(src).toContain('virtual:cer-layouts')
21
- })
22
-
23
- it('imports virtual:cer-plugins', () => {
24
- expect(src).toContain('virtual:cer-plugins')
25
- })
26
-
27
- it('imports virtual:cer-server-api', () => {
28
- expect(src).toContain('virtual:cer-server-api')
29
- })
30
-
31
- it('imports virtual:cer-content-components for markdown-backed custom elements', () => {
32
- expect(src).toContain('virtual:cer-content-components')
33
- })
34
-
35
- it('imports registerBuiltinComponents from custom-elements-runtime', () => {
36
- expect(src).toContain('registerBuiltinComponents')
37
- expect(src).toContain('setDevMode')
38
- expect(src).toContain('@jasonshimmy/custom-elements-runtime')
39
- })
40
-
41
- it('enables runtime dev logging from the downstream server env', () => {
42
- expect(src).toContain('const _cerRuntimeDev')
43
- expect(src).toContain('import.meta.env?.DEV')
44
- expect(src).toContain('_cerProcess?.env')
45
- expect(src).toContain('(globalThis).__CE_RUNTIME_DEV__ = _cerRuntimeDev')
46
- expect(src).toContain('setDevMode(_cerRuntimeDev)')
47
- })
48
-
49
- it('imports renderToStreamWithJITCSSDSD and DSD_POLYFILL_SCRIPT from ssr subpath', () => {
50
- expect(src).toContain('renderToStreamWithJITCSSDSD')
51
- expect(src).toContain('DSD_POLYFILL_SCRIPT')
52
- expect(src).toContain('custom-elements-runtime/ssr')
53
- })
54
-
55
- it('imports initRouter from router subpath', () => {
56
- expect(src).toContain('initRouter')
57
- expect(src).toContain('custom-elements-runtime/router')
58
- })
59
-
60
- it('imports beginHeadCollection, endHeadCollection, serializeHeadTags from composables', () => {
61
- expect(src).toContain('beginHeadCollection')
62
- expect(src).toContain('endHeadCollection')
63
- expect(src).toContain('serializeHeadTags')
64
- expect(src).toContain('vite-plugin-cer-app/composables')
65
- })
66
-
67
- it('uses AsyncLocalStorage for request-scoped data isolation', () => {
68
- expect(src).toContain('AsyncLocalStorage')
69
- expect(src).toContain('node:async_hooks')
70
- expect(src).toContain('_cerDataStore')
71
- expect(src).toContain('__CER_DATA_STORE__')
72
- })
73
-
74
- it('scopes each request in _cerDataStore.run()', () => {
75
- expect(src).toContain('_cerDataStore.run(')
76
- })
77
-
78
- it('scopes loader data in _cerDataStore.run(loaderData) for rendering', () => {
79
- // enterWith() does not propagate across await boundaries to a parent
80
- // async continuation; run() is used instead so usePageData() in
81
- // renderToStreamWithJITCSSDSD sees the correct store value.
82
- expect(src).toContain('_cerDataStore.run(loaderData')
83
- expect(src).toContain('loaderData = data')
84
- expect(src).not.toContain('_cerDataStore.enterWith(data)')
85
- })
86
-
87
- it('initializes plugins and sets globalThis.__cerPluginProvides', () => {
88
- expect(src).toContain('__cerPluginProvides')
89
- expect(src).toContain('_pluginProvides')
90
- expect(src).toContain('_pluginsReady')
91
- })
92
-
93
- it('awaits _pluginsReady before handling each request', () => {
94
- expect(src).toContain('await _pluginsReady')
95
- })
96
-
97
- it('calls registerEntityMap with entities.json', () => {
98
- expect(src).toContain('registerEntityMap(entitiesJson)')
99
- expect(src).toContain('entities.json')
100
- })
101
-
102
- it('loads client index.html for merging', () => {
103
- expect(src).toContain('_clientTemplate')
104
- expect(src).toContain('../client/index.html')
105
- })
106
-
107
- it('defines _mergeWithClientTemplate helper', () => {
108
- expect(src).toContain('_mergeWithClientTemplate')
109
- })
110
-
111
- it('defines _prepareRequest async function', () => {
112
- expect(src).toContain('_prepareRequest')
113
- })
114
-
115
- it('calls endHeadCollection() synchronously before any await to avoid race conditions', () => {
116
- expect(src).toContain('beginHeadCollection()')
117
- expect(src).toContain('endHeadCollection()')
118
- // endHeadCollection must come before reader.read() so concurrent requests
119
- // (SSG concurrency > 1) cannot reset the shared globalThis collector between
120
- // beginHeadCollection and endHeadCollection.
121
- const endIdx = src.indexOf('endHeadCollection()')
122
- const readIdx = src.indexOf('reader.read()')
123
- expect(endIdx).toBeGreaterThan(-1)
124
- expect(readIdx).toBeGreaterThan(-1)
125
- expect(endIdx).toBeLessThan(readIdx)
126
- })
127
-
128
- it('passes dsdPolyfill: false to suppress inline polyfill', () => {
129
- expect(src).toContain('dsdPolyfill: false')
130
- })
131
-
132
- it('injects DSD_POLYFILL_SCRIPT before </body>', () => {
133
- expect(src).toContain("lastIndexOf('</body>')")
134
- expect(src).toContain('DSD_POLYFILL_SCRIPT + fromBodyClose')
135
- })
136
-
137
- it('merges SSR html with client template when available', () => {
138
- // Dev mode: per-request global takes precedence over module-level _clientTemplate
139
- expect(src).toContain('_resolvedClientTemplate')
140
- expect(src).toContain('(globalThis).__CER_CLIENT_TEMPLATE__ ?? _clientTemplate')
141
- expect(src).toContain('_mergeWithClientTemplate(ssrHtml, _resolvedClientTemplate)')
142
- })
143
-
144
- it('exports handler as both named and default export', () => {
145
- expect(src).toContain('export const handler')
146
- expect(src).toContain('export default handler')
147
- })
148
-
149
- it('exports apiRoutes, plugins, layouts, and routes', () => {
150
- expect(src).toContain('export { apiRoutes, plugins, layouts, routes, serverMiddleware }')
151
- })
152
-
153
- it('exports runServerMiddleware for adapter use', () => {
154
- expect(src).toContain('export async function runServerMiddleware(req, res)')
155
- })
156
-
157
- it('exports runWithRequestContext for wrapping API handlers in request context', () => {
158
- expect(src).toContain('export function runWithRequestContext(req, res, fn)')
159
- expect(src).toContain('_cerReqStore.run({ req, res }, fn)')
160
- })
161
-
162
- it('sets Content-Type header on response', () => {
163
- expect(src).toContain('text/html; charset=utf-8')
164
- })
165
-
166
- it('sets Transfer-Encoding: chunked header for streaming', () => {
167
- expect(src).toContain('Transfer-Encoding')
168
- expect(src).toContain('chunked')
169
- })
170
-
171
- it('reads the stream using a reader loop', () => {
172
- expect(src).toContain('stream.getReader()')
173
- expect(src).toContain('reader.read()')
174
- })
175
-
176
- // ─── Error boundary ──────────────────────────────────────────────────────────
177
-
178
- it('imports errorTag from virtual:cer-error', () => {
179
- expect(src).toContain('errorTag')
180
- expect(src).toContain('virtual:cer-error')
181
- })
182
-
183
- it('catches loader errors in _prepareRequest', () => {
184
- expect(src).toContain('catch (err)')
185
- // The catch block must exist inside _prepareRequest (after the function definition)
186
- const prepareIdx = src.indexOf('const _prepareRequest')
187
- const catchIdx = src.indexOf('catch (err)', prepareIdx)
188
- expect(prepareIdx).toBeGreaterThan(-1)
189
- expect(catchIdx).toBeGreaterThan(prepareIdx)
190
- })
191
-
192
- it('extracts .status from the thrown error for HTTP status code', () => {
193
- expect(src).toContain("'status' in err")
194
- expect(src).toContain('err.status')
195
- })
196
-
197
- it('guards err.status with typeof === "number" so Response-like thrown values work', () => {
198
- // `new Response("...", { status: 404 })` has a numeric `.status` property.
199
- // The typeof guard ensures that non-numeric status values (e.g. a string "404")
200
- // do not pass through — only genuine numbers are accepted.
201
- expect(src).toContain("typeof err.status === 'number'")
202
- })
203
-
204
- it('defaults to status 500 when thrown error has no .status', () => {
205
- expect(src).toContain(': 500')
206
- })
207
-
208
- it('renders error component when loader throws and an error tag is available', () => {
209
- // P2-2: effectiveErrorTag (route-level or global) is used as the tag
210
- expect(src).toContain('tag: effectiveErrorTag')
211
- })
212
-
213
- it('logs to console.error when loader throws and no error tag is defined', () => {
214
- expect(src).toContain('console.error')
215
- expect(src).toContain('!effectiveErrorTag')
216
- })
217
-
218
- it('propagates status to res.statusCode', () => {
219
- expect(src).toContain('res.statusCode = status')
220
- })
221
-
222
- it('returns status: null on the happy path when the route is not marked as not-found', () => {
223
- expect(src).toContain('isNotFoundRoute ? 404 : null')
224
- })
225
-
226
- it('sets status 404 only when the matched route is marked as framework not-found', () => {
227
- expect(src).toContain('route?.meta?._cerNotFound === true')
228
- })
229
-
230
- // ─── Render error handling (P0-1) ────────────────────────────────────────────
231
- // Note: the custom-elements runtime catches *component-level* render errors
232
- // internally (see ssr-context.ts runComponentSSRRender). The try/catch here
233
- // guards against *infrastructure-level* failures that escape the runtime.
234
-
235
- it('wraps the render + stream loop in a try/catch block', () => {
236
- expect(src).toContain('} catch (_renderErr) {')
237
- })
238
-
239
- it('calls endHeadCollection() in the catch block to prevent collector leaks', () => {
240
- const catchIdx = src.indexOf('} catch (_renderErr) {')
241
- const endCollectorInCatch = src.indexOf('endHeadCollection()', catchIdx)
242
- expect(catchIdx).toBeGreaterThan(-1)
243
- expect(endCollectorInCatch).toBeGreaterThan(catchIdx)
244
- })
245
-
246
- it('sends a 500 response when an infrastructure error occurs before headers are sent', () => {
247
- expect(src).toContain('res.headersSent')
248
- expect(src).toContain('res.statusCode = 500')
249
- })
250
-
251
- it('closes the connection with res.end() when headers were already sent during streaming', () => {
252
- const catchIdx = src.indexOf('} catch (_renderErr) {')
253
- const elseEndIdx = src.indexOf('res.end()', catchIdx)
254
- expect(elseEndIdx).toBeGreaterThan(catchIdx)
255
- })
256
-
257
- it('includes an HTML error page body in the 500 infrastructure-error response', () => {
258
- expect(src).toContain('500 Internal Server Error')
259
- })
260
-
261
- // ─── ISR production export ───────────────────────────────────────────────────
262
-
263
- it('imports createIsrHandler from the isr subpath', () => {
264
- expect(src).toContain('createIsrHandler')
265
- expect(src).toContain('vite-plugin-cer-app/isr')
266
- })
267
-
268
- it('exports isrHandler wrapping handler with ISR caching', () => {
269
- expect(src).toContain('export const isrHandler')
270
- expect(src).toContain('createIsrHandler(routes, handler)')
271
- })
272
-
273
- // ─── useCookie req/res store ──────────────────────────────────────────────────
274
-
275
- it('creates _cerReqStore AsyncLocalStorage for request-scoped cookie access', () => {
276
- expect(src).toContain('_cerReqStore')
277
- expect(src).toContain('__CER_REQ_STORE__')
278
- })
279
-
280
- it('exposes _cerReqStore on globalThis as __CER_REQ_STORE__', () => {
281
- expect(src).toContain('__CER_REQ_STORE__ = _cerReqStore')
282
- })
283
-
284
- it('wraps handler body in _cerReqStore.run({ req, res }, ...) so useCookie can access req/res', () => {
285
- expect(src).toContain('_cerReqStore.run({ req, res }')
286
- // req/res store wraps the data store — it must appear before _cerDataStore.run
287
- const reqStoreIdx = src.indexOf('_cerReqStore.run(')
288
- const dataStoreIdx = src.indexOf('_cerDataStore.run(')
289
- expect(reqStoreIdx).toBeGreaterThan(-1)
290
- expect(dataStoreIdx).toBeGreaterThan(-1)
291
- expect(reqStoreIdx).toBeLessThan(dataStoreIdx)
292
- })
293
-
294
- // ─── P1-1: Synthetic 404 from null pageTag ───────────────────────────────────
295
-
296
- it('handles null pageTag from the synthetic catch-all route (returns 404 status)', () => {
297
- // The synthetic catch-all resolves to { default: null }; _prepareRequest must
298
- // check for this and return 404 rather than crashing.
299
- expect(src).toContain('!pageTag')
300
- // Should return status: 404 for null pageTag
301
- expect(src).toContain('status: 404')
302
- })
303
-
304
- it('uses the per-route errorTag for 404 display when available', () => {
305
- // If the synthetic 404 route has an errorTag, it should be used as the 404 vnode tag
306
- expect(src).toContain('routeErrorTag')
307
- // Falls back to global errorTag when no per-route error component exists
308
- const routeErrIdx = src.indexOf('routeErrorTag')
309
- const globalErrIdx = src.indexOf('errorTag')
310
- expect(routeErrIdx).toBeGreaterThan(-1)
311
- expect(globalErrIdx).toBeGreaterThan(-1)
312
- })
313
-
314
- // ─── P1-2: runServerMiddleware status code extraction ──────────────────────
315
-
316
- it('runServerMiddleware reads err.status to pick the response status code', () => {
317
- // The catch block should extract err.status before defaulting to 500
318
- expect(src).toContain('err.status')
319
- // isNaN guard ensures non-numeric status values default to 500
320
- expect(src).toContain('isNaN')
321
- })
322
-
323
- it('runServerMiddleware defaults to 500 when err has no .status property', () => {
324
- expect(src).toContain(': 500')
325
- })
326
-
327
- // ─── P2-2: Per-route error component ────────────────────────────────────────
328
-
329
- it('uses route.meta.errorTag over the global errorTag when available', () => {
330
- // The error catch inside _prepareRequest should prefer the per-route error tag
331
- expect(src).toContain('route?.meta?.errorTag')
332
- // Fallback to global errorTag
333
- expect(src).toContain('?? errorTag')
334
- })
335
-
336
- // ─── Observability hooks ─────────────────────────────────────────────────────
337
-
338
- it('imports _hooks from virtual:cer-app-config', () => {
339
- expect(src).toContain('_hooks')
340
- expect(src).toContain('virtual:cer-app-config')
341
- })
342
-
343
- it('fires onRequest at the start of the handler with path, method, and req', () => {
344
- expect(src).toContain('_hooks?.onRequest')
345
- expect(src).toContain('_requestPath')
346
- expect(src).toContain('_requestStart')
347
- })
348
-
349
- it('fires onError in the middleware catch with type: middleware', () => {
350
- expect(src).toContain("type: 'middleware'")
351
- // onError fires inside runServerMiddleware catch
352
- const mwIdx = src.indexOf('runServerMiddleware')
353
- const middlewareErrIdx = src.indexOf("type: 'middleware'", mwIdx)
354
- expect(middlewareErrIdx).toBeGreaterThan(mwIdx)
355
- })
356
-
357
- it('fires onError in the loader catch with type: loader', () => {
358
- expect(src).toContain("type: 'loader'")
359
- })
360
-
361
- it('fires onError in the render catch with type: render', () => {
362
- expect(src).toContain("type: 'render'")
363
- const catchIdx = src.indexOf('} catch (_renderErr) {')
364
- const renderErrIdx = src.indexOf("type: 'render'", catchIdx)
365
- expect(renderErrIdx).toBeGreaterThan(catchIdx)
366
- })
367
-
368
- it('fires onResponse after the success res.end()', () => {
369
- expect(src).toContain('_hooks?.onResponse')
370
- // onResponse must appear after DSD_POLYFILL_SCRIPT + fromBodyClose
371
- const endIdx = src.indexOf('DSD_POLYFILL_SCRIPT + fromBodyClose')
372
- const responseIdx = src.indexOf('_hooks?.onResponse', endIdx)
373
- expect(responseIdx).toBeGreaterThan(endIdx)
374
- })
375
-
376
- it('fires onResponse in the render error catch so it fires on both success and error paths', () => {
377
- const catchIdx = src.indexOf('} catch (_renderErr) {')
378
- const responseInCatchIdx = src.indexOf('_hooks?.onResponse', catchIdx)
379
- expect(responseInCatchIdx).toBeGreaterThan(catchIdx)
380
- })
381
-
382
- it('swallows exceptions thrown by onError so hooks cannot crash the handler', () => {
383
- expect(src).toContain('/* hooks must not crash the handler */')
384
- })
385
- })
@@ -1,144 +0,0 @@
1
- import { vi, describe, it, expect, beforeEach } from 'vitest'
2
-
3
- vi.mock('node:fs', async (importOriginal) => {
4
- const actual = await importOriginal<typeof import('node:fs')>()
5
- return {
6
- ...actual,
7
- existsSync: vi.fn().mockReturnValue(false),
8
- writeFileSync: vi.fn(),
9
- mkdirSync: vi.fn(),
10
- readFileSync: vi.fn().mockReturnValue(''),
11
- appendFileSync: vi.fn(),
12
- }
13
- })
14
- vi.mock('../../runtime/app-template.js', () => ({ generateAppEntryTemplate: vi.fn().mockReturnValue('// app template') }))
15
-
16
- import { existsSync, writeFileSync, mkdirSync, readFileSync, appendFileSync } from 'node:fs'
17
- import {
18
- GENERATED_DIR_NAME,
19
- getGeneratedDir,
20
- resolveHtmlEntry,
21
- generateDefaultHtml,
22
- writeGeneratedDir,
23
- } from '../../plugin/generated-dir.js'
24
-
25
- const ROOT = '/project'
26
- const mockConfig = {
27
- root: ROOT,
28
- srcDir: `${ROOT}/app`,
29
- jitCss: { content: [], extendedColors: false },
30
- } as Parameters<typeof writeGeneratedDir>[0]
31
-
32
- beforeEach(() => {
33
- vi.mocked(existsSync).mockReturnValue(false)
34
- vi.mocked(writeFileSync).mockClear()
35
- vi.mocked(mkdirSync).mockClear()
36
- vi.mocked(readFileSync).mockReturnValue('')
37
- vi.mocked(appendFileSync).mockClear()
38
- })
39
-
40
- describe('GENERATED_DIR_NAME', () => {
41
- it('is .cer', () => {
42
- expect(GENERATED_DIR_NAME).toBe('.cer')
43
- })
44
- })
45
-
46
- describe('getGeneratedDir', () => {
47
- it('returns <root>/.cer', () => {
48
- expect(getGeneratedDir(ROOT)).toBe(`${ROOT}/.cer`)
49
- })
50
- })
51
-
52
- describe('resolveHtmlEntry', () => {
53
- it('returns user index.html when it exists', () => {
54
- vi.mocked(existsSync).mockReturnValue(true)
55
- expect(resolveHtmlEntry(mockConfig)).toBe(`${ROOT}/index.html`)
56
- })
57
-
58
- it('returns .cer/index.html when user index.html is absent', () => {
59
- vi.mocked(existsSync).mockReturnValue(false)
60
- expect(resolveHtmlEntry(mockConfig)).toBe(`${ROOT}/.cer/index.html`)
61
- })
62
- })
63
-
64
- describe('generateDefaultHtml', () => {
65
- it('always references /@cer/app.ts', () => {
66
- const html = generateDefaultHtml()
67
- expect(html).toContain('/@cer/app.ts')
68
- expect(html).not.toContain('/app/app.ts')
69
- })
70
-
71
- it('includes <cer-layout-view> mount point', () => {
72
- const html = generateDefaultHtml()
73
- expect(html).toContain('<cer-layout-view>')
74
- })
75
-
76
- it('is valid HTML with doctype', () => {
77
- const html = generateDefaultHtml()
78
- expect(html).toContain('<!DOCTYPE html>')
79
- })
80
- })
81
-
82
- describe('writeGeneratedDir', () => {
83
- it('creates the .cer directory when absent', () => {
84
- // existsSync returns false for everything → dir is created
85
- writeGeneratedDir(mockConfig)
86
- expect(mkdirSync).toHaveBeenCalledWith(`${ROOT}/.cer`, { recursive: true })
87
- })
88
-
89
- it('does not re-create the directory when it already exists', () => {
90
- // existsSync returns true → dir already present
91
- vi.mocked(existsSync).mockReturnValue(true)
92
- writeGeneratedDir(mockConfig)
93
- expect(mkdirSync).not.toHaveBeenCalled()
94
- })
95
-
96
- it('always writes .cer/app.ts', () => {
97
- writeGeneratedDir(mockConfig)
98
- const paths = vi.mocked(writeFileSync).mock.calls.map(([p]) => String(p))
99
- expect(paths.some(p => p.endsWith('/.cer/app.ts'))).toBe(true)
100
- })
101
-
102
- it('always writes .cer/app.ts even when .cer/ dir already exists', () => {
103
- vi.mocked(existsSync).mockReturnValue(true)
104
- writeGeneratedDir(mockConfig)
105
- const paths = vi.mocked(writeFileSync).mock.calls.map(([p]) => String(p))
106
- expect(paths.some(p => p.endsWith('/.cer/app.ts'))).toBe(true)
107
- })
108
-
109
- it('always writes .cer/entry-server.ts', () => {
110
- writeGeneratedDir(mockConfig)
111
- const paths = vi.mocked(writeFileSync).mock.calls.map(([p]) => String(p))
112
- expect(paths.some(p => p.endsWith('/.cer/entry-server.ts'))).toBe(true)
113
- })
114
-
115
- it('always writes .cer/index.html', () => {
116
- writeGeneratedDir(mockConfig)
117
- const paths = vi.mocked(writeFileSync).mock.calls.map(([p]) => String(p))
118
- expect(paths.some(p => p.endsWith('/.cer/index.html'))).toBe(true)
119
- })
120
-
121
- it('creates .gitignore when absent', () => {
122
- // existsSync returns false → .cer/ dir created, app.ts written, .gitignore created
123
- writeGeneratedDir(mockConfig)
124
- const paths = vi.mocked(writeFileSync).mock.calls.map(([p]) => String(p))
125
- expect(paths.some(p => p.endsWith('/.gitignore'))).toBe(true)
126
- })
127
-
128
- it('appends .cer/ to existing .gitignore that does not contain it', () => {
129
- // .gitignore exists (readFileSync returns '' — no .cer/ entry)
130
- vi.mocked(existsSync).mockImplementation((p) => String(p).endsWith('.gitignore'))
131
- vi.mocked(readFileSync).mockReturnValue('node_modules/\ndist/\n')
132
- writeGeneratedDir(mockConfig)
133
- expect(appendFileSync).toHaveBeenCalled()
134
- const appendArg = vi.mocked(appendFileSync).mock.calls[0][1] as string
135
- expect(appendArg).toContain('.cer/')
136
- })
137
-
138
- it('does not append to .gitignore when .cer/ is already present', () => {
139
- vi.mocked(existsSync).mockImplementation((p) => String(p).endsWith('.gitignore'))
140
- vi.mocked(readFileSync).mockReturnValue('node_modules/\n.cer/\ndist/\n')
141
- writeGeneratedDir(mockConfig)
142
- expect(appendFileSync).not.toHaveBeenCalled()
143
- })
144
- })
@@ -1,146 +0,0 @@
1
- import { describe, it, expect } from 'vitest'
2
- import {
3
- injectFaviconLink,
4
- injectCanonicalLink,
5
- addNoopenerToExternalLinks,
6
- generateRobotsTxt,
7
- } from '../../plugin/html-post-process.js'
8
-
9
- // ---------------------------------------------------------------------------
10
- // injectFaviconLink
11
- // ---------------------------------------------------------------------------
12
-
13
- describe('injectFaviconLink', () => {
14
- it('injects link before </head> when none exists', () => {
15
- const html = '<html><head><title>Test</title></head><body></body></html>'
16
- const result = injectFaviconLink(html, '/favicon.ico')
17
- expect(result).toContain('<link rel="icon" href="/favicon.ico">')
18
- expect(result.indexOf('<link rel="icon"')).toBeLessThan(result.indexOf('</head>'))
19
- })
20
-
21
- it('does not inject when rel="icon" already present', () => {
22
- const html = '<html><head><link rel="icon" href="/custom.ico"></head></html>'
23
- expect(injectFaviconLink(html, '/favicon.ico')).toBe(html)
24
- })
25
-
26
- it('does not inject when rel="shortcut icon" already present', () => {
27
- const html = '<html><head><link rel="shortcut icon" href="/old.ico"></head></html>'
28
- expect(injectFaviconLink(html, '/favicon.ico')).toBe(html)
29
- })
30
-
31
- it('prepends tag when no </head> is found', () => {
32
- const html = '<html><body></body></html>'
33
- const result = injectFaviconLink(html, '/favicon.svg')
34
- expect(result).toContain('<link rel="icon" href="/favicon.svg">')
35
- })
36
-
37
- it('supports svg favicon href', () => {
38
- const html = '<html><head></head></html>'
39
- expect(injectFaviconLink(html, '/favicon.svg')).toContain('href="/favicon.svg"')
40
- })
41
- })
42
-
43
- // ---------------------------------------------------------------------------
44
- // injectCanonicalLink
45
- // ---------------------------------------------------------------------------
46
-
47
- describe('injectCanonicalLink', () => {
48
- it('injects canonical before </head>', () => {
49
- const html = '<html><head><title>T</title></head><body></body></html>'
50
- const result = injectCanonicalLink(html, 'https://example.com/about')
51
- expect(result).toContain('<link rel="canonical" href="https://example.com/about">')
52
- expect(result.indexOf('<link rel="canonical"')).toBeLessThan(result.indexOf('</head>'))
53
- })
54
-
55
- it('does not inject when canonical already present', () => {
56
- const html = '<head><link rel="canonical" href="https://example.com/"></head>'
57
- expect(injectCanonicalLink(html, 'https://example.com/')).toBe(html)
58
- })
59
-
60
- it('escapes & in the URL', () => {
61
- const html = '<html><head></head></html>'
62
- const result = injectCanonicalLink(html, 'https://example.com/?a=1&b=2')
63
- expect(result).toContain('href="https://example.com/?a=1&amp;b=2"')
64
- })
65
-
66
- it('prepends tag when no </head> is found', () => {
67
- const html = '<html><body></body></html>'
68
- const result = injectCanonicalLink(html, 'https://example.com/')
69
- expect(result).toContain('<link rel="canonical"')
70
- })
71
- })
72
-
73
- // ---------------------------------------------------------------------------
74
- // addNoopenerToExternalLinks
75
- // ---------------------------------------------------------------------------
76
-
77
- describe('addNoopenerToExternalLinks', () => {
78
- it('adds rel to target="_blank" link with no rel', () => {
79
- const html = '<a href="https://example.com" target="_blank">Link</a>'
80
- const result = addNoopenerToExternalLinks(html)
81
- expect(result).toContain('rel="noopener noreferrer"')
82
- })
83
-
84
- it('does not modify links without target="_blank"', () => {
85
- const html = '<a href="https://example.com">Link</a>'
86
- expect(addNoopenerToExternalLinks(html)).toBe(html)
87
- })
88
-
89
- it('does not duplicate when both values already present', () => {
90
- const html = '<a href="https://x.com" target="_blank" rel="noopener noreferrer">X</a>'
91
- const result = addNoopenerToExternalLinks(html)
92
- expect(result).toBe(html)
93
- })
94
-
95
- it('appends missing noopener to existing rel', () => {
96
- const html = '<a href="https://x.com" target="_blank" rel="noreferrer">X</a>'
97
- const result = addNoopenerToExternalLinks(html)
98
- expect(result).toContain('noopener')
99
- expect(result).toContain('noreferrer')
100
- })
101
-
102
- it('appends missing noreferrer to existing rel', () => {
103
- const html = '<a href="https://x.com" target="_blank" rel="noopener">X</a>'
104
- const result = addNoopenerToExternalLinks(html)
105
- expect(result).toContain('noreferrer')
106
- })
107
-
108
- it('handles target="_blank" after href in attributes', () => {
109
- const html = '<a href="https://x.com" class="btn" target="_blank">X</a>'
110
- const result = addNoopenerToExternalLinks(html)
111
- expect(result).toContain('rel="noopener noreferrer"')
112
- })
113
-
114
- it('handles multiple links in the same document', () => {
115
- const html = [
116
- '<a href="https://a.com" target="_blank">A</a>',
117
- '<a href="/internal">Internal</a>',
118
- '<a href="https://b.com" target="_blank">B</a>',
119
- ].join('')
120
- const result = addNoopenerToExternalLinks(html)
121
- expect(result.match(/rel="noopener noreferrer"/g)?.length).toBe(2)
122
- })
123
- })
124
-
125
- // ---------------------------------------------------------------------------
126
- // generateRobotsTxt
127
- // ---------------------------------------------------------------------------
128
-
129
- describe('generateRobotsTxt', () => {
130
- it('generates allow-all rules without siteUrl', () => {
131
- const txt = generateRobotsTxt(null)
132
- expect(txt).toContain('User-agent: *')
133
- expect(txt).toContain('Allow: /')
134
- expect(txt).not.toContain('Sitemap:')
135
- })
136
-
137
- it('includes Sitemap directive when siteUrl is provided', () => {
138
- const txt = generateRobotsTxt('https://example.com')
139
- expect(txt).toContain('Sitemap: https://example.com/sitemap.xml')
140
- })
141
-
142
- it('ends with a newline', () => {
143
- expect(generateRobotsTxt(null).endsWith('\n')).toBe(true)
144
- expect(generateRobotsTxt('https://example.com').endsWith('\n')).toBe(true)
145
- })
146
- })