@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,49 +0,0 @@
1
- import { inject } from '@jasonshimmy/custom-elements-runtime'
2
-
3
- const _g = globalThis as Record<string, unknown>
4
- const _PROVIDES_KEY = '__cerPluginProvides'
5
-
6
- /**
7
- * useInject — reads a value provided by a plugin via plugin.setup()'s provide().
8
- *
9
- * Works consistently across all rendering modes:
10
- *
11
- * - **SPA/Client**: Uses inject() from the component context tree (established
12
- * by cer-layout-view calling provide() for each plugin-provided value).
13
- *
14
- * - **SSR/SSG**: Reads from globalThis.__cerPluginProvides, populated when the
15
- * server entry runs plugin.setup() before rendering.
16
- *
17
- * @example
18
- * ```ts
19
- * // In a plugin (app/plugins/my-plugin.ts):
20
- * export default {
21
- * name: 'my-plugin',
22
- * setup({ provide }) {
23
- * provide('my-service', { greet: () => 'hello' })
24
- * }
25
- * }
26
- *
27
- * // In a component:
28
- * component('my-page', () => {
29
- * const service = useInject<{ greet(): string }>('my-service')
30
- * })
31
- * ```
32
- */
33
- export function useInject<T = unknown>(key: string, defaultValue?: T): T | undefined {
34
- // Server-side (SSR/SSG): read from the global plugin provides map.
35
- // __cerPluginProvides is populated by the server entry before the render pass.
36
- if (typeof document === 'undefined') {
37
- const pluginProvides = _g[_PROVIDES_KEY] as Map<PropertyKey, unknown> | undefined
38
- const value = pluginProvides?.get(key)
39
- return value !== undefined ? (value as T) : defaultValue
40
- }
41
-
42
- // Client-side: inject() walks the component context tree established by
43
- // cer-layout-view's provide() calls. Falls back to __cerPluginProvides for
44
- // reads before cer-layout-view mounts (e.g. during plugin-registered components).
45
- const value = inject<T>(key)
46
- if (value !== undefined) return value
47
- const pluginProvides = _g[_PROVIDES_KEY] as Map<PropertyKey, unknown> | undefined
48
- return (pluginProvides?.get(key) as T | undefined) ?? defaultValue
49
- }
@@ -1,109 +0,0 @@
1
- import type { IncomingMessage } from 'node:http'
2
-
3
- /** Return value of `useLocale()`. Exposes the active locale and helpers for locale-aware navigation. */
4
- export interface LocaleComposable {
5
- /** The active locale code for the current request / page. */
6
- readonly locale: string
7
- /** All configured locale codes. */
8
- readonly locales: string[]
9
- /** The default locale code. */
10
- readonly defaultLocale: string
11
- /**
12
- * Returns the localised version of `path` for `targetLocale`.
13
- *
14
- * @example
15
- * switchLocalePath('fr', '/about') // → '/fr/about'
16
- * switchLocalePath('en', '/fr/about') // → '/about' (when en is default + prefix_except_default)
17
- */
18
- switchLocalePath(targetLocale: string, path?: string): string
19
- }
20
-
21
- interface I18nVirtualConfig {
22
- locales: string[]
23
- defaultLocale: string
24
- strategy: 'prefix' | 'prefix_except_default' | 'no_prefix'
25
- }
26
-
27
- // ─── Internal helpers ─────────────────────────────────────────────────────────
28
-
29
- function _extractLocaleFromPath(pathname: string, locales: string[]): string | null {
30
- const first = pathname.split('/')[1]
31
- return locales.includes(first) ? first : null
32
- }
33
-
34
- function _stripLocalePrefix(pathname: string, locale: string): string {
35
- const prefix = `/${locale}`
36
- if (pathname === prefix) return '/'
37
- if (pathname.startsWith(`${prefix}/`)) return pathname.slice(prefix.length)
38
- return pathname
39
- }
40
-
41
- function _buildLocalePath(
42
- pathname: string,
43
- targetLocale: string,
44
- currentLocale: string,
45
- config: I18nVirtualConfig,
46
- ): string {
47
- // Strip the current locale prefix (if any) to get the bare path
48
- const bare = _stripLocalePrefix(pathname, currentLocale)
49
- if (config.strategy === 'no_prefix') return bare
50
- if (config.strategy === 'prefix_except_default' && targetLocale === config.defaultLocale) return bare
51
- return `/${targetLocale}${bare === '/' ? '' : bare}`
52
- }
53
-
54
- // ─── Composable ───────────────────────────────────────────────────────────────
55
-
56
- /**
57
- * Internationalisation composable. Returns the active locale and helpers for
58
- * building locale-aware URLs. Requires `i18n` to be configured in `cer.config.ts`.
59
- *
60
- * Works isomorphically: on the server it reads the URL path via AsyncLocalStorage;
61
- * on the client it reads `window.location.pathname`.
62
- *
63
- * When `i18n` is not configured, `locale` equals `'default'` and `locales` is `['default']`.
64
- *
65
- * @example
66
- * ```ts
67
- * const { locale, switchLocalePath } = useLocale()
68
- * console.log(locale) // 'fr'
69
- * console.log(switchLocalePath('en', '/fr/about')) // '/about'
70
- * ```
71
- */
72
- export function useLocale(): LocaleComposable {
73
- // Read i18n config from the virtual module at runtime.
74
- // The virtual module is replaced by the bundler at build time.
75
- const g = globalThis as Record<string, unknown>
76
- const i18nConfig = g['__CER_I18N_CONFIG__'] as I18nVirtualConfig | null | undefined
77
-
78
- const locales = i18nConfig?.locales ?? ['default']
79
- const defaultLocale = i18nConfig?.defaultLocale ?? 'default'
80
- const strategy = i18nConfig?.strategy ?? 'no_prefix'
81
-
82
- // Resolve the current pathname from the server request context or window.
83
- let pathname = '/'
84
- const reqStore = g['__CER_REQ_STORE__'] as
85
- | { getStore(): { req: IncomingMessage } | null }
86
- | undefined
87
- if (reqStore) {
88
- const ctx = reqStore.getStore()
89
- if (ctx?.req.url) {
90
- try { pathname = new URL(ctx.req.url, 'http://x').pathname } catch { /* ignore */ }
91
- }
92
- } else if (typeof window !== 'undefined') {
93
- pathname = window.location.pathname
94
- }
95
-
96
- const detectedLocale = strategy !== 'no_prefix'
97
- ? (_extractLocaleFromPath(pathname, locales) ?? defaultLocale)
98
- : defaultLocale
99
-
100
- return {
101
- get locale() { return detectedLocale },
102
- get locales() { return locales },
103
- get defaultLocale() { return defaultLocale },
104
- switchLocalePath(targetLocale: string, path?: string): string {
105
- const base = path ?? pathname
106
- return _buildLocalePath(base, targetLocale, detectedLocale, { locales, defaultLocale, strategy })
107
- },
108
- }
109
- }
@@ -1,58 +0,0 @@
1
- /**
2
- * navigateTo — programmatic navigation.
3
- *
4
- * Works isomorphically:
5
- * - **Server context** (inside a loader or middleware): sends a 302 redirect
6
- * immediately by writing to the current request's `res` object via
7
- * `AsyncLocalStorage`. The response is ended and the function returns.
8
- * - **Client context**: delegates to the framework router so the SPA
9
- * navigation pipeline (middleware, loaders, isNavigating) runs normally.
10
- * Falls back to `window.location.href` if the router is not available.
11
- *
12
- * @example
13
- * ```ts
14
- * // app/middleware/require-auth.ts
15
- * export default defineMiddleware(() => {
16
- * const { loggedIn } = useAuth()
17
- * if (!loggedIn) return navigateTo('/login')
18
- * })
19
- * ```
20
- *
21
- * @example
22
- * ```ts
23
- * // app/pages/dashboard.ts
24
- * component('page-dashboard', () => {
25
- * useOnConnected(() => {
26
- * if (someCondition) navigateTo('/home')
27
- * })
28
- * })
29
- * ```
30
- */
31
-
32
- export function navigateTo(path: string): void | Promise<void> {
33
- const g = globalThis as Record<string, unknown>
34
-
35
- // Server: redirect via the request-scoped req/res store.
36
- const reqStore = g['__CER_REQ_STORE__'] as {
37
- getStore(): { res: { statusCode: number; setHeader(k: string, v: string): void; end(): void; writableEnded?: boolean } } | null
38
- } | undefined
39
-
40
- if (reqStore) {
41
- const store = reqStore.getStore()
42
- if (store?.res && !store.res.writableEnded) {
43
- store.res.statusCode = 302
44
- store.res.setHeader('Location', path)
45
- store.res.end()
46
- return
47
- }
48
- }
49
-
50
- // Client: use the framework router so the full navigation pipeline runs.
51
- const router = g['__cerRouter'] as { push(path: string): Promise<void> } | undefined
52
- if (router) return router.push(path)
53
-
54
- // Fallback: hard redirect.
55
- if (typeof window !== 'undefined') {
56
- window.location.href = path
57
- }
58
- }
@@ -1,93 +0,0 @@
1
- /**
2
- * usePageData — reads SSR-injected loader data for the current page.
3
- *
4
- * During SSR/SSG the server calls the matched route's `loader()` function
5
- * and makes the result available in two ways:
6
- *
7
- * 1. **Server render pass** — the data is stored in a per-request
8
- * `AsyncLocalStorage` context (set via `_cerDataStore.run(data, ...)` in the
9
- * entry-server template). `usePageData()` reads this store so the component
10
- * renders with real data in the initial SSR/SSG HTML.
11
- *
12
- * 2. **Client hydration** — the server also serializes the data as
13
- * `window.__CER_DATA__` in the page `<head>`. On first component render
14
- * `usePageData()` returns that value, caches it on the component context, and
15
- * returns the same cached value on all subsequent re-renders of that component
16
- * instance. The data remains available for the initial hydrated route until
17
- * the next client-side navigation clears it before loading new page data.
18
- *
19
- * **Why caching is necessary**: the render function passed to `component()` is
20
- * called on every re-render (it is the render function, not a setup function).
21
- * Without caching, `usePageData()` could return `null` on later re-renders if a
22
- * browser upgrades or re-renders the hydrated page after the initial read,
23
- * flip the `ssrData` guard from truthy → null, and re-trigger client-side fetches.
24
- *
25
- * @returns The serialized loader result, or `null` if no SSR data is present.
26
- *
27
- * @example
28
- * ```ts
29
- * component('page-blog-slug', () => {
30
- * const ssrData = usePageData<{ title: string; body: string }>()
31
- * const title = ref(ssrData?.title ?? '')
32
- * const body = ref(ssrData?.body ?? '')
33
- *
34
- * useOnConnected(() => {
35
- * if (ssrData) return // already hydrated from SSR — skip fetch
36
- * fetch(`/api/posts/${props.slug}`).then(r => r.json()).then(post => {
37
- * title.value = post.title
38
- * body.value = post.body
39
- * })
40
- * })
41
- * })
42
- * ```
43
- */
44
- import { getCurrentComponentContext } from '@jasonshimmy/custom-elements-runtime'
45
-
46
- // Key used to cache the page data on the component context object across re-renders.
47
- const _PAGE_DATA_KEY = '_cerPageData'
48
-
49
- export function usePageData<T = unknown>(): T | null {
50
- const g = globalThis as Record<string, unknown>
51
-
52
- // Server-side: read from the per-request AsyncLocalStorage context.
53
- // __CER_DATA_STORE__ is set by the entry-server template and is only present
54
- // in Node.js, so this branch is tree-shaken out of the client bundle.
55
- const store = g['__CER_DATA_STORE__'] as { getStore(): unknown } | undefined
56
- if (store) {
57
- const ssrData = store.getStore() as T | null | undefined
58
- if (ssrData !== undefined && ssrData !== null) return ssrData
59
- }
60
-
61
- // Client-side: check the component context cache first.
62
- // getCurrentComponentContext() returns the context object for the component
63
- // whose renderFn is currently executing (set by the component() runtime
64
- // before calling renderFn). Caching on the context ensures the same value is
65
- // returned on every re-render of the same element instance even if the
66
- // browser performs a later hydration-time re-render.
67
- const ctx = getCurrentComponentContext() as Record<string, unknown> | null
68
- if (ctx) {
69
- if (_PAGE_DATA_KEY in ctx) {
70
- return ctx[_PAGE_DATA_KEY] as T | null
71
- }
72
- }
73
-
74
- // Read the raw value from the global set by the server-rendered <script> tag.
75
- const data = g['__CER_DATA__'] as T | undefined | null
76
- const result: T | null = (data === undefined || data === null) ? null : data
77
-
78
- // Cache on the component context so subsequent re-renders of this element
79
- // instance return the same value without reading __CER_DATA__ again.
80
- // Use Object.defineProperty to bypass the reactive Proxy set-trap on context,
81
- // which would otherwise schedule a spurious re-render (and risk an infinite loop)
82
- // every time we first cache the value.
83
- if (ctx) {
84
- Object.defineProperty(ctx, _PAGE_DATA_KEY, {
85
- value: result,
86
- writable: false,
87
- enumerable: false,
88
- configurable: true,
89
- })
90
- }
91
-
92
- return result
93
- }
@@ -1,71 +0,0 @@
1
- /**
2
- * useRoute — returns the current route's path, params, query, and meta.
3
- *
4
- * Works isomorphically:
5
- * - **Server render pass** — reads from `globalThis.__CER_ROUTE_STORE__`
6
- * (AsyncLocalStorage populated by the entry-server handler before rendering).
7
- * - **Client** — reads from the global router instance exposed by the app
8
- * entry (`globalThis.__cerRouter`).
9
- *
10
- * @example
11
- * ```ts
12
- * // app/layouts/default.ts
13
- * component('layout-default', () => {
14
- * const route = useRoute()
15
- * return html`
16
- * <nav>${route.meta?.title ?? 'My App'}</nav>
17
- * <slot></slot>
18
- * `
19
- * })
20
- * ```
21
- *
22
- * @example
23
- * ```ts
24
- * // app/pages/post.ts
25
- * component('page-post', () => {
26
- * const { params } = useRoute()
27
- * const { data: post } = useFetch(\`/api/posts/\${params.id}\`)
28
- * })
29
- * ```
30
- */
31
-
32
- export interface RouteInfo {
33
- /** Current URL path, e.g. `'/posts/42'`. */
34
- path: string
35
- /** Dynamic route params, e.g. `{ id: '42' }`. */
36
- params: Record<string, string>
37
- /** Parsed query string params, e.g. `{ page: '2' }`. */
38
- query: Record<string, string>
39
- /** Static route meta object exported by the matched page module. */
40
- meta: Record<string, unknown> | null
41
- }
42
-
43
- export function useRoute(): RouteInfo {
44
- const g = globalThis as Record<string, unknown>
45
-
46
- // Server: read from the per-request AsyncLocalStorage context.
47
- const routeStore = g['__CER_ROUTE_STORE__'] as { getStore(): unknown } | undefined
48
- if (routeStore) {
49
- const info = routeStore.getStore() as RouteInfo | null
50
- if (info) return info
51
- }
52
-
53
- // Client: read from the global router set by the app entry.
54
- const router = g['__cerRouter'] as {
55
- getCurrent(): { path: string; query?: Record<string, string> }
56
- matchRoute(path: string): { route?: { meta?: unknown }; params?: Record<string, string> } | null
57
- } | undefined
58
-
59
- if (router) {
60
- const current = router.getCurrent()
61
- const matched = router.matchRoute(current.path)
62
- return {
63
- path: current.path,
64
- params: (matched?.params ?? {}) as Record<string, string>,
65
- query: (current.query ?? {}) as Record<string, string>,
66
- meta: (matched?.route?.meta as Record<string, unknown>) ?? null,
67
- }
68
- }
69
-
70
- return { path: '/', params: {}, query: {}, meta: null }
71
- }
@@ -1,123 +0,0 @@
1
- /** Resolved public runtime config values. Available on both server and client. */
2
- export interface RuntimeConfigPublic {
3
- [key: string]: unknown
4
- }
5
-
6
- /** Resolved private runtime config values. Available on the server only — never present on the client. */
7
- export interface RuntimeConfigPrivate {
8
- [key: string]: string | string[]
9
- }
10
-
11
- /** Return value of `useRuntimeConfig()`. */
12
- export interface RuntimeConfigResult {
13
- public: RuntimeConfigPublic
14
- private?: RuntimeConfigPrivate
15
- }
16
-
17
- /**
18
- * Returns the runtime configuration set in `cer.config.ts`.
19
- *
20
- * - `public` — available on both server and client.
21
- * - `private` — available on the server only (resolved from `process.env` at
22
- * startup). Never present on the client.
23
- *
24
- * @example
25
- * // cer.config.ts
26
- * export default defineConfig({
27
- * runtimeConfig: {
28
- * public: { apiBase: process.env.VITE_API_BASE ?? '/api' },
29
- * private: { dbUrl: '', secretKey: '' },
30
- * },
31
- * })
32
- *
33
- * // app/pages/index.ts (loader — server-only)
34
- * const { private: priv } = useRuntimeConfig()
35
- * const rows = await db.query(priv.dbUrl)
36
- */
37
- export function useRuntimeConfig(): RuntimeConfigResult {
38
- // Dynamic import resolved at runtime — avoids a static circular dependency
39
- // between the composable and the virtual module.
40
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
41
- const mod = (globalThis as any).__cerRuntimeConfig
42
- const config: RuntimeConfigResult = mod ? (mod as RuntimeConfigResult) : { public: {} }
43
-
44
- // In browser contexts, wrap the result in a Proxy that throws a clear,
45
- // actionable error if any code attempts to read `runtimeConfig.private`.
46
- // Private values are server-only secrets and are never serialized into the
47
- // client bundle — accessing them client-side is always a bug.
48
- if (typeof window !== 'undefined') {
49
- return new Proxy(config, {
50
- get(target, prop) {
51
- if (prop === 'private') {
52
- throw new Error(
53
- '[cer-app] runtimeConfig.private is not available in the browser. ' +
54
- 'Move this access into a server-only loader, middleware, or API handler.',
55
- )
56
- }
57
- return Reflect.get(target, prop)
58
- },
59
- })
60
- }
61
-
62
- return config
63
- }
64
-
65
- /**
66
- * Called once during app bootstrap to store the resolved runtimeConfig on
67
- * globalThis so useRuntimeConfig() can access it synchronously in any context
68
- * (component render, composable, server handler).
69
- */
70
- export function initRuntimeConfig(config: RuntimeConfigResult): void {
71
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
72
- ;(globalThis as any).__cerRuntimeConfig = config
73
- }
74
-
75
- /**
76
- * Converts a camelCase or mixed key to UPPER_SNAKE_CASE for env var lookup.
77
- * Examples: `dbUrl` → `DB_URL`, `secretKey` → `SECRET_KEY`, `API_KEY` → `API_KEY`.
78
- */
79
- function toUpperSnakeCase(key: string): string {
80
- return key
81
- .replace(/([a-z])([A-Z])/g, '$1_$2')
82
- .toUpperCase()
83
- }
84
-
85
- /**
86
- * Resolves a private config object by looking up each key in the supplied
87
- * environment variable map, with the following precedence:
88
- *
89
- * 1. `env[key]` — exact case (e.g. `process.env.dbUrl`)
90
- * 2. `env[UPPER_SNAKE_CASE(key)]` — conventional env var form (e.g. `process.env.DB_URL`)
91
- * 3. `defaultValue` — the declared default from `cer.config.ts`
92
- *
93
- * Array values (e.g. `sessionSecret: [newSecret, oldSecret]`) are passed
94
- * through unchanged — they are already resolved by the caller at config time.
95
- *
96
- * Accepts an optional `env` parameter so the function is unit-testable
97
- * without mutating `process.env`.
98
- */
99
- export function resolvePrivateConfig(
100
- defaults: Record<string, string | string[]>,
101
- env: Record<string, string | undefined> = process.env as Record<string, string | undefined>,
102
- ): Record<string, string | string[]> {
103
- return Object.fromEntries(
104
- Object.entries(defaults).map(([key, defaultValue]) => {
105
- // Array values are already fully resolved at config build time — pass through.
106
- if (Array.isArray(defaultValue)) {
107
- return [key, defaultValue.filter(Boolean)]
108
- }
109
- const envKey = toUpperSnakeCase(key)
110
- const resolved = env[key] ?? env[envKey] ?? defaultValue
111
- // Warn when no env var was found and the declared default is an empty string.
112
- // An empty-string default is the conventional way to declare a required secret
113
- // (the key exists for typing purposes but has no safe default value).
114
- if (resolved === '' && env[key] === undefined && env[envKey] === undefined) {
115
- console.warn(
116
- `[cer-app] runtimeConfig.private: "${key}" is an empty string — ` +
117
- `set ${envKey} in the environment to provide a value.`,
118
- )
119
- }
120
- return [key, resolved]
121
- }),
122
- )
123
- }
@@ -1,80 +0,0 @@
1
- import { useHead } from './use-head.js'
2
-
3
- /**
4
- * Convenience input for common SEO meta tags.
5
- * Pass to `useSeoMeta()` — it maps each field to the correct `<meta>` or `<title>` tag automatically.
6
- * All fields are optional.
7
- */
8
- export interface SeoMetaInput {
9
- /** Sets the document title. */
10
- title?: string
11
- /** Sets the meta description. */
12
- description?: string
13
- // Open Graph
14
- ogTitle?: string
15
- ogDescription?: string
16
- ogImage?: string
17
- ogUrl?: string
18
- /** e.g. `'website'`, `'article'`. No tag is emitted when omitted. */
19
- ogType?: string
20
- ogSiteName?: string
21
- // Twitter / X
22
- /** e.g. `'summary'`, `'summary_large_image'`. No tag is emitted when omitted. */
23
- twitterCard?: string
24
- twitterTitle?: string
25
- twitterDescription?: string
26
- twitterImage?: string
27
- /** Twitter/X site handle, e.g. '@mysite'. */
28
- twitterSite?: string
29
- /** Canonical URL injected as <link rel="canonical">. */
30
- canonical?: string
31
- }
32
-
33
- /**
34
- * Thin wrapper over `useHead()` for common SEO tags.
35
- *
36
- * Sets the page title, meta description, Open Graph tags, Twitter Card tags,
37
- * and a canonical link element. Only tags with a non-undefined value are emitted.
38
- *
39
- * It is auto-imported, so you don't need to import it manually.
40
- *
41
- * @example
42
- * ```ts
43
- * useSeoMeta({
44
- * title: 'My page',
45
- * description: 'A great page.',
46
- * ogImage: 'https://example.com/og.png',
47
- * canonical: 'https://example.com/my-page',
48
- * })
49
- * ```
50
- */
51
- export function useSeoMeta(input: SeoMetaInput): void {
52
- const meta: Array<Record<string, string>> = []
53
- const link: Array<Record<string, string>> = []
54
-
55
- if (input.description !== undefined) meta.push({ name: 'description', content: input.description })
56
-
57
- // Open Graph
58
- if (input.ogTitle !== undefined) meta.push({ property: 'og:title', content: input.ogTitle })
59
- if (input.ogDescription !== undefined) meta.push({ property: 'og:description', content: input.ogDescription })
60
- if (input.ogImage !== undefined) meta.push({ property: 'og:image', content: input.ogImage })
61
- if (input.ogUrl !== undefined) meta.push({ property: 'og:url', content: input.ogUrl })
62
- if (input.ogType !== undefined) meta.push({ property: 'og:type', content: input.ogType })
63
- if (input.ogSiteName !== undefined) meta.push({ property: 'og:site_name', content: input.ogSiteName })
64
-
65
- // Twitter / X
66
- if (input.twitterCard !== undefined) meta.push({ name: 'twitter:card', content: input.twitterCard })
67
- if (input.twitterTitle !== undefined) meta.push({ name: 'twitter:title', content: input.twitterTitle })
68
- if (input.twitterDescription !== undefined) meta.push({ name: 'twitter:description', content: input.twitterDescription })
69
- if (input.twitterImage !== undefined) meta.push({ name: 'twitter:image', content: input.twitterImage })
70
- if (input.twitterSite !== undefined) meta.push({ name: 'twitter:site', content: input.twitterSite })
71
-
72
- // Canonical link
73
- if (input.canonical !== undefined) link.push({ rel: 'canonical', href: input.canonical })
74
-
75
- useHead({
76
- title: input.title,
77
- meta: meta.length > 0 ? meta : undefined,
78
- link: link.length > 0 ? link : undefined,
79
- })
80
- }