@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.
- package/dist/cli/checks/lighthouse.d.ts +26 -0
- package/dist/cli/checks/lighthouse.d.ts.map +1 -0
- package/dist/cli/checks/lighthouse.js +121 -0
- package/dist/cli/checks/lighthouse.js.map +1 -0
- package/dist/cli/checks/links.d.ts +12 -0
- package/dist/cli/checks/links.d.ts.map +1 -0
- package/dist/cli/checks/links.js +89 -0
- package/dist/cli/checks/links.js.map +1 -0
- package/dist/cli/checks/performance.d.ts +17 -0
- package/dist/cli/checks/performance.d.ts.map +1 -0
- package/dist/cli/checks/performance.js +61 -0
- package/dist/cli/checks/performance.js.map +1 -0
- package/dist/cli/commands/check.d.ts +3 -0
- package/dist/cli/commands/check.d.ts.map +1 -0
- package/dist/cli/commands/check.js +85 -0
- package/dist/cli/commands/check.js.map +1 -0
- package/dist/cli/create/index.d.ts +11 -1
- package/dist/cli/create/index.d.ts.map +1 -1
- package/dist/cli/create/index.js +135 -65
- package/dist/cli/create/index.js.map +1 -1
- package/dist/cli/create/templates/spa/package.json.tpl +5 -3
- package/dist/cli/create/templates/ssg/cer.config.ts.tpl +2 -0
- package/dist/cli/create/templates/ssg/package.json.tpl +5 -3
- package/dist/cli/create/templates/ssr/package.json.tpl +5 -3
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/plugin/build-ssg.d.ts +22 -6
- package/dist/plugin/build-ssg.d.ts.map +1 -1
- package/dist/plugin/build-ssg.js +227 -17
- package/dist/plugin/build-ssg.js.map +1 -1
- package/dist/plugin/content/parser.d.ts.map +1 -1
- package/dist/plugin/content/parser.js +54 -10
- package/dist/plugin/content/parser.js.map +1 -1
- package/dist/plugin/dev-server.d.ts +4 -1
- package/dist/plugin/dev-server.d.ts.map +1 -1
- package/dist/plugin/dev-server.js.map +1 -1
- package/dist/plugin/dts-generator.d.ts.map +1 -1
- package/dist/plugin/dts-generator.js +50 -8
- package/dist/plugin/dts-generator.js.map +1 -1
- package/dist/plugin/generated-dir.d.ts +2 -0
- package/dist/plugin/generated-dir.d.ts.map +1 -1
- package/dist/plugin/generated-dir.js +17 -6
- package/dist/plugin/generated-dir.js.map +1 -1
- package/dist/plugin/html-post-process.d.ts +6 -0
- package/dist/plugin/html-post-process.d.ts.map +1 -1
- package/dist/plugin/html-post-process.js +24 -0
- package/dist/plugin/html-post-process.js.map +1 -1
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +60 -13
- package/dist/plugin/index.js.map +1 -1
- package/dist/plugin/transforms/auto-import.d.ts.map +1 -1
- package/dist/plugin/transforms/auto-import.js +7 -0
- package/dist/plugin/transforms/auto-import.js.map +1 -1
- package/dist/plugin/transforms/prune-runtime-css.d.ts +8 -0
- package/dist/plugin/transforms/prune-runtime-css.d.ts.map +1 -0
- package/dist/plugin/transforms/prune-runtime-css.js +40 -0
- package/dist/plugin/transforms/prune-runtime-css.js.map +1 -0
- package/dist/runtime/app-template.d.ts +1 -1
- package/dist/runtime/app-template.d.ts.map +1 -1
- package/dist/runtime/app-template.js +206 -36
- package/dist/runtime/app-template.js.map +1 -1
- package/dist/runtime/composables/index.d.ts +4 -0
- package/dist/runtime/composables/index.d.ts.map +1 -1
- package/dist/runtime/composables/index.js +2 -0
- package/dist/runtime/composables/index.js.map +1 -1
- package/dist/runtime/composables/use-active-headings.d.ts +24 -0
- package/dist/runtime/composables/use-active-headings.d.ts.map +1 -0
- package/dist/runtime/composables/use-active-headings.js +112 -0
- package/dist/runtime/composables/use-active-headings.js.map +1 -0
- package/dist/runtime/composables/use-content-page.d.ts +45 -0
- package/dist/runtime/composables/use-content-page.d.ts.map +1 -0
- package/dist/runtime/composables/use-content-page.js +179 -0
- package/dist/runtime/composables/use-content-page.js.map +1 -0
- package/dist/runtime/composables/use-content-search.d.ts.map +1 -1
- package/dist/runtime/composables/use-content-search.js +1 -5
- package/dist/runtime/composables/use-content-search.js.map +1 -1
- package/dist/runtime/composables/use-fetch.d.ts +2 -1
- package/dist/runtime/composables/use-fetch.d.ts.map +1 -1
- package/dist/runtime/composables/use-fetch.js +112 -43
- package/dist/runtime/composables/use-fetch.js.map +1 -1
- package/dist/runtime/composables/use-route.d.ts.map +1 -1
- package/dist/runtime/composables/use-route.js +39 -11
- package/dist/runtime/composables/use-route.js.map +1 -1
- package/dist/runtime/content/client.js +2 -2
- package/dist/runtime/content/client.js.map +1 -1
- package/dist/runtime/entry-server-template.d.ts +16 -1
- package/dist/runtime/entry-server-template.d.ts.map +1 -1
- package/dist/runtime/entry-server-template.js +120 -13
- package/dist/runtime/entry-server-template.js.map +1 -1
- package/dist/types/config.d.ts +45 -4
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js.map +1 -1
- package/dist/types/middleware.d.ts +2 -2
- package/dist/types/middleware.d.ts.map +1 -1
- package/package.json +22 -19
- package/.github/copilot-instructions.md +0 -169
- package/.github/workflows/publish.yml +0 -257
- package/.nvmrc +0 -1
- package/CHANGELOG.md +0 -236
- package/CLAUDE.md +0 -113
- package/commits.txt +0 -1
- package/cypress.config.ts +0 -16
- package/docs/authentication.md +0 -285
- package/docs/cli.md +0 -324
- package/docs/components.md +0 -237
- package/docs/composables.md +0 -907
- package/docs/configuration.md +0 -696
- package/docs/content.md +0 -570
- package/docs/data-loading.md +0 -336
- package/docs/getting-started.md +0 -237
- package/docs/head-management.md +0 -212
- package/docs/i18n.md +0 -159
- package/docs/layouts.md +0 -224
- package/docs/middleware.md +0 -234
- package/docs/plugins.md +0 -170
- package/docs/rendering-modes.md +0 -377
- package/docs/routing.md +0 -485
- package/docs/server-api.md +0 -253
- package/docs/testing.md +0 -455
- package/docs/use-fetch.md +0 -183
- package/e2e/cypress/e2e/api.cy.ts +0 -123
- package/e2e/cypress/e2e/async-component.cy.ts +0 -27
- package/e2e/cypress/e2e/component-splitting.cy.ts +0 -50
- package/e2e/cypress/e2e/content.cy.ts +0 -425
- package/e2e/cypress/e2e/cookie.cy.ts +0 -85
- package/e2e/cypress/e2e/data.cy.ts +0 -158
- package/e2e/cypress/e2e/error-boundary.cy.ts +0 -88
- package/e2e/cypress/e2e/fouc.cy.ts +0 -254
- package/e2e/cypress/e2e/group-meta.cy.ts +0 -36
- package/e2e/cypress/e2e/head.cy.ts +0 -89
- package/e2e/cypress/e2e/i18n.cy.ts +0 -79
- package/e2e/cypress/e2e/interactive.cy.ts +0 -137
- package/e2e/cypress/e2e/isr-nested-runtime.cy.ts +0 -102
- package/e2e/cypress/e2e/jit-css-custom-colors.cy.ts +0 -43
- package/e2e/cypress/e2e/middleware-chain.cy.ts +0 -89
- package/e2e/cypress/e2e/middleware-status.cy.ts +0 -30
- package/e2e/cypress/e2e/middleware.cy.ts +0 -45
- package/e2e/cypress/e2e/navigate.cy.ts +0 -39
- package/e2e/cypress/e2e/observability.cy.ts +0 -77
- package/e2e/cypress/e2e/per-route-error.cy.ts +0 -42
- package/e2e/cypress/e2e/per-route-render.cy.ts +0 -70
- package/e2e/cypress/e2e/preview-hardening.cy.ts +0 -88
- package/e2e/cypress/e2e/routes.cy.ts +0 -128
- package/e2e/cypress/e2e/seo-meta.cy.ts +0 -108
- package/e2e/cypress/e2e/server-middleware.cy.ts +0 -46
- package/e2e/cypress/e2e/session-rotation.cy.ts +0 -25
- package/e2e/cypress/e2e/session.cy.ts +0 -73
- package/e2e/cypress/e2e/ssr-render-error.cy.ts +0 -68
- package/e2e/cypress/e2e/synthetic-404.cy.ts +0 -45
- package/e2e/cypress/e2e/use-page-data.cy.ts +0 -122
- package/e2e/cypress/e2e/use-state.cy.ts +0 -138
- package/e2e/cypress/support/commands.ts +0 -120
- package/e2e/cypress/support/e2e.ts +0 -10
- package/e2e/cypress/tsconfig.json +0 -14
- package/e2e/kitchen-sink/app/components/ks-badge.ts +0 -8
- package/e2e/kitchen-sink/app/composables/useKsCounter.ts +0 -9
- package/e2e/kitchen-sink/app/error.ts +0 -18
- package/e2e/kitchen-sink/app/layouts/admin.ts +0 -13
- package/e2e/kitchen-sink/app/layouts/default.ts +0 -25
- package/e2e/kitchen-sink/app/layouts/group.ts +0 -9
- package/e2e/kitchen-sink/app/layouts/minimal.ts +0 -7
- package/e2e/kitchen-sink/app/loading.ts +0 -9
- package/e2e/kitchen-sink/app/middleware/auth.ts +0 -9
- package/e2e/kitchen-sink/app/middleware/custom-log.ts +0 -4
- package/e2e/kitchen-sink/app/middleware/group-auth.ts +0 -7
- package/e2e/kitchen-sink/app/middleware/logger.ts +0 -14
- package/e2e/kitchen-sink/app/pages/(auth)/login.ts +0 -13
- package/e2e/kitchen-sink/app/pages/(auth)/protected.ts +0 -16
- package/e2e/kitchen-sink/app/pages/404.ts +0 -9
- package/e2e/kitchen-sink/app/pages/about.ts +0 -17
- package/e2e/kitchen-sink/app/pages/admin/_layout.ts +0 -1
- package/e2e/kitchen-sink/app/pages/admin/dashboard.ts +0 -11
- package/e2e/kitchen-sink/app/pages/async-component-error-test.ts +0 -18
- package/e2e/kitchen-sink/app/pages/async-component-test.ts +0 -18
- package/e2e/kitchen-sink/app/pages/blog/[slug].ts +0 -59
- package/e2e/kitchen-sink/app/pages/blog/index.ts +0 -51
- package/e2e/kitchen-sink/app/pages/content-blog.ts +0 -37
- package/e2e/kitchen-sink/app/pages/content-doc.ts +0 -42
- package/e2e/kitchen-sink/app/pages/content-fallback.ts +0 -36
- package/e2e/kitchen-sink/app/pages/content-guides.ts +0 -35
- package/e2e/kitchen-sink/app/pages/content-index.ts +0 -39
- package/e2e/kitchen-sink/app/pages/content-search.ts +0 -36
- package/e2e/kitchen-sink/app/pages/cookie-test.ts +0 -22
- package/e2e/kitchen-sink/app/pages/counter.ts +0 -17
- package/e2e/kitchen-sink/app/pages/group-auth-blocked.ts +0 -9
- package/e2e/kitchen-sink/app/pages/group-meta-test/_layout.ts +0 -6
- package/e2e/kitchen-sink/app/pages/group-meta-test/custom-mw.ts +0 -14
- package/e2e/kitchen-sink/app/pages/group-meta-test/page.ts +0 -9
- package/e2e/kitchen-sink/app/pages/group-meta-test/protected.ts +0 -8
- package/e2e/kitchen-sink/app/pages/head.ts +0 -20
- package/e2e/kitchen-sink/app/pages/i18n-test.ts +0 -14
- package/e2e/kitchen-sink/app/pages/index.ts +0 -27
- package/e2e/kitchen-sink/app/pages/isr-test.ts +0 -17
- package/e2e/kitchen-sink/app/pages/items/[id].ts +0 -20
- package/e2e/kitchen-sink/app/pages/jit-css-custom-colors-test.ts +0 -14
- package/e2e/kitchen-sink/app/pages/loader-error-test.ts +0 -13
- package/e2e/kitchen-sink/app/pages/loader-props-test.ts +0 -19
- package/e2e/kitchen-sink/app/pages/loader-response-error-test.ts +0 -13
- package/e2e/kitchen-sink/app/pages/middleware-chain-test.ts +0 -12
- package/e2e/kitchen-sink/app/pages/middleware-status-test.ts +0 -9
- package/e2e/kitchen-sink/app/pages/navigate-test.ts +0 -16
- package/e2e/kitchen-sink/app/pages/observability-test.ts +0 -25
- package/e2e/kitchen-sink/app/pages/per-route-error-test.error.ts +0 -13
- package/e2e/kitchen-sink/app/pages/per-route-error-test.ts +0 -15
- package/e2e/kitchen-sink/app/pages/plugin-dsd-test.ts +0 -10
- package/e2e/kitchen-sink/app/pages/render-error-test.ts +0 -12
- package/e2e/kitchen-sink/app/pages/render-server-test.ts +0 -12
- package/e2e/kitchen-sink/app/pages/render-spa-test.ts +0 -12
- package/e2e/kitchen-sink/app/pages/route-info.ts +0 -14
- package/e2e/kitchen-sink/app/pages/seo-test.ts +0 -23
- package/e2e/kitchen-sink/app/pages/session-rotation-test.ts +0 -25
- package/e2e/kitchen-sink/app/pages/use-state-test.ts +0 -37
- package/e2e/kitchen-sink/app/plugins/01.setup.ts +0 -7
- package/e2e/kitchen-sink/app/plugins/02.ks-plugin-components.ts +0 -27
- package/e2e/kitchen-sink/cer-auto-imports.d.ts +0 -51
- package/e2e/kitchen-sink/cer-env.d.ts +0 -35
- package/e2e/kitchen-sink/cer-tsconfig.json +0 -30
- package/e2e/kitchen-sink/cer.config.ts +0 -43
- package/e2e/kitchen-sink/content/01.guides/01.index.md +0 -8
- package/e2e/kitchen-sink/content/01.guides/02.intro.md +0 -8
- package/e2e/kitchen-sink/content/01.guides/10.advanced.md +0 -8
- package/e2e/kitchen-sink/content/blog/2026-04-01-hello.md +0 -26
- package/e2e/kitchen-sink/content/blog/2026-04-02-draft.md +0 -10
- package/e2e/kitchen-sink/content/blog/index.md +0 -8
- package/e2e/kitchen-sink/content/blog/no-frontmatter.md +0 -7
- package/e2e/kitchen-sink/content/docs/getting-started.md +0 -48
- package/e2e/kitchen-sink/content/index.md +0 -16
- package/e2e/kitchen-sink/server/api/echo.ts +0 -12
- package/e2e/kitchen-sink/server/api/health.ts +0 -3
- package/e2e/kitchen-sink/server/api/posts/[slug].ts +0 -11
- package/e2e/kitchen-sink/server/api/posts/index.ts +0 -5
- package/e2e/kitchen-sink/server/api/session.ts +0 -23
- package/e2e/kitchen-sink/server/data/posts.ts +0 -21
- package/e2e/kitchen-sink/server/middleware/01-headers.ts +0 -6
- package/e2e/kitchen-sink/server/middleware/02-status-test.ts +0 -10
- package/e2e/kitchen-sink/tsconfig.json +0 -3
- package/e2e/scripts/clean.mjs +0 -14
- package/eslint.config.ts +0 -22
- package/src/__tests__/cli/adapters/cloudflare-worker.integration.test.ts +0 -132
- package/src/__tests__/cli/adapters/cloudflare.test.ts +0 -332
- package/src/__tests__/cli/adapters/netlify-bridge.integration.test.ts +0 -139
- package/src/__tests__/cli/adapters/netlify.test.ts +0 -286
- package/src/__tests__/cli/adapters/vercel-launcher.integration.test.ts +0 -129
- package/src/__tests__/cli/adapters/vercel.test.ts +0 -263
- package/src/__tests__/cli/preview-hardening.test.ts +0 -265
- package/src/__tests__/cli/preview-isr.test.ts +0 -320
- package/src/__tests__/index.test.ts +0 -21
- package/src/__tests__/plugin/app-template.test.ts +0 -166
- package/src/__tests__/plugin/build-ssg-render.test.ts +0 -155
- package/src/__tests__/plugin/build-ssg.test.ts +0 -598
- package/src/__tests__/plugin/build-ssr.test.ts +0 -192
- package/src/__tests__/plugin/cer-app-plugin.test.ts +0 -552
- package/src/__tests__/plugin/content/emitter.test.ts +0 -117
- package/src/__tests__/plugin/content/loader.test.ts +0 -154
- package/src/__tests__/plugin/content/parser.test.ts +0 -381
- package/src/__tests__/plugin/content/path-utils.test.ts +0 -69
- package/src/__tests__/plugin/content/search.test.ts +0 -119
- package/src/__tests__/plugin/dev-server.test.ts +0 -813
- package/src/__tests__/plugin/dts-generator.test.ts +0 -394
- package/src/__tests__/plugin/entry-server-template.test.ts +0 -385
- package/src/__tests__/plugin/generated-dir.test.ts +0 -144
- package/src/__tests__/plugin/html-post-process.test.ts +0 -146
- package/src/__tests__/plugin/path-utils.test.ts +0 -411
- package/src/__tests__/plugin/resolve-config.test.ts +0 -233
- package/src/__tests__/plugin/scanner.test.ts +0 -186
- package/src/__tests__/plugin/transforms/auto-import.test.ts +0 -510
- package/src/__tests__/plugin/transforms/head-inject.test.ts +0 -178
- package/src/__tests__/plugin/virtual/composables.test.ts +0 -57
- package/src/__tests__/plugin/virtual/content-components.test.ts +0 -66
- package/src/__tests__/plugin/virtual/error.test.ts +0 -71
- package/src/__tests__/plugin/virtual/layouts.test.ts +0 -70
- package/src/__tests__/plugin/virtual/loading.test.ts +0 -72
- package/src/__tests__/plugin/virtual/middleware.test.ts +0 -83
- package/src/__tests__/plugin/virtual/plugins.test.ts +0 -129
- package/src/__tests__/plugin/virtual/routes.test.ts +0 -884
- package/src/__tests__/plugin/virtual/server-api.test.ts +0 -164
- package/src/__tests__/plugin/virtual/server-middleware.test.ts +0 -102
- package/src/__tests__/runtime/app-template.test.ts +0 -208
- package/src/__tests__/runtime/define-middleware.test.ts +0 -64
- package/src/__tests__/runtime/define-server-middleware.test.ts +0 -54
- package/src/__tests__/runtime/entry-client-template.test.ts +0 -61
- package/src/__tests__/runtime/isr-handler.test.ts +0 -338
- package/src/__tests__/runtime/oauth/handler.test.ts +0 -358
- package/src/__tests__/runtime/oauth/providers.test.ts +0 -47
- package/src/__tests__/runtime/oauth-index.test.ts +0 -43
- package/src/__tests__/runtime/use-auth.test.ts +0 -169
- package/src/__tests__/runtime/use-content-search-composable.test.ts +0 -405
- package/src/__tests__/runtime/use-content-search.test.ts +0 -163
- package/src/__tests__/runtime/use-content.test.ts +0 -226
- package/src/__tests__/runtime/use-cookie.test.ts +0 -277
- package/src/__tests__/runtime/use-fetch-component.test.ts +0 -214
- package/src/__tests__/runtime/use-fetch.test.ts +0 -377
- package/src/__tests__/runtime/use-head.test.ts +0 -311
- package/src/__tests__/runtime/use-inject-client.test.ts +0 -67
- package/src/__tests__/runtime/use-inject.test.ts +0 -66
- package/src/__tests__/runtime/use-locale.test.ts +0 -152
- package/src/__tests__/runtime/use-navigate.test.ts +0 -121
- package/src/__tests__/runtime/use-page-data.test.ts +0 -298
- package/src/__tests__/runtime/use-route.test.ts +0 -156
- package/src/__tests__/runtime/use-runtime-config.test.ts +0 -182
- package/src/__tests__/runtime/use-seo-meta.test.ts +0 -109
- package/src/__tests__/runtime/use-session.test.ts +0 -320
- package/src/__tests__/runtime/use-state.test.ts +0 -340
- package/src/__tests__/types/config.test.ts +0 -18
- package/src/cli/adapters/cloudflare.ts +0 -335
- package/src/cli/adapters/netlify.ts +0 -326
- package/src/cli/adapters/vercel.ts +0 -311
- package/src/cli/commands/adapt.ts +0 -84
- package/src/cli/commands/build.ts +0 -165
- package/src/cli/commands/dev.ts +0 -125
- package/src/cli/commands/generate.ts +0 -91
- package/src/cli/commands/preview-isr.ts +0 -172
- package/src/cli/commands/preview.ts +0 -523
- package/src/cli/create/index.ts +0 -275
- package/src/cli/create/templates/shared/.gitignore.tpl +0 -25
- package/src/cli/create/templates/shared/app/layouts/default.ts.tpl +0 -15
- package/src/cli/create/templates/shared/app/pages/index.ts.tpl +0 -8
- package/src/cli/create/templates/shared/index.html.tpl +0 -12
- package/src/cli/create/templates/shared/tsconfig.json.tpl +0 -6
- package/src/cli/create/templates/spa/cer.config.ts.tpl +0 -6
- package/src/cli/create/templates/spa/package.json.tpl +0 -18
- package/src/cli/create/templates/ssg/cer.config.ts.tpl +0 -10
- package/src/cli/create/templates/ssg/package.json.tpl +0 -19
- package/src/cli/create/templates/ssr/cer.config.ts.tpl +0 -6
- package/src/cli/create/templates/ssr/package.json.tpl +0 -18
- package/src/cli/index.ts +0 -22
- package/src/index.ts +0 -20
- package/src/plugin/build-ssg.ts +0 -403
- package/src/plugin/build-ssr.ts +0 -134
- package/src/plugin/content/emitter.ts +0 -50
- package/src/plugin/content/index.ts +0 -233
- package/src/plugin/content/parser.ts +0 -259
- package/src/plugin/content/path-utils.ts +0 -55
- package/src/plugin/content/scanner.ts +0 -26
- package/src/plugin/content/search.ts +0 -28
- package/src/plugin/dev-server.ts +0 -379
- package/src/plugin/dts-generator.ts +0 -273
- package/src/plugin/generated-dir.ts +0 -121
- package/src/plugin/html-post-process.ts +0 -96
- package/src/plugin/index.ts +0 -584
- package/src/plugin/path-utils.ts +0 -186
- package/src/plugin/scanner.ts +0 -65
- package/src/plugin/transforms/auto-import.ts +0 -206
- package/src/plugin/transforms/head-inject.ts +0 -161
- package/src/plugin/virtual/composables.ts +0 -28
- package/src/plugin/virtual/content-components.ts +0 -83
- package/src/plugin/virtual/error.ts +0 -34
- package/src/plugin/virtual/layouts.ts +0 -41
- package/src/plugin/virtual/loading.ts +0 -32
- package/src/plugin/virtual/middleware.ts +0 -45
- package/src/plugin/virtual/plugins.ts +0 -46
- package/src/plugin/virtual/routes.ts +0 -514
- package/src/plugin/virtual/server-api.ts +0 -85
- package/src/plugin/virtual/server-middleware.ts +0 -44
- package/src/runtime/app-template.ts +0 -377
- package/src/runtime/composables/define-middleware.ts +0 -17
- package/src/runtime/composables/define-server-middleware.ts +0 -18
- package/src/runtime/composables/index.ts +0 -27
- package/src/runtime/composables/use-auth.ts +0 -125
- package/src/runtime/composables/use-content-search.ts +0 -205
- package/src/runtime/composables/use-content.ts +0 -146
- package/src/runtime/composables/use-cookie.ts +0 -135
- package/src/runtime/composables/use-fetch.ts +0 -438
- package/src/runtime/composables/use-head.ts +0 -234
- package/src/runtime/composables/use-inject.ts +0 -49
- package/src/runtime/composables/use-locale.ts +0 -109
- package/src/runtime/composables/use-navigate.ts +0 -58
- package/src/runtime/composables/use-page-data.ts +0 -93
- package/src/runtime/composables/use-route.ts +0 -71
- package/src/runtime/composables/use-runtime-config.ts +0 -123
- package/src/runtime/composables/use-seo-meta.ts +0 -80
- package/src/runtime/composables/use-session.ts +0 -186
- package/src/runtime/composables/use-state.ts +0 -92
- package/src/runtime/content/client.ts +0 -168
- package/src/runtime/entry-client-template.ts +0 -39
- package/src/runtime/entry-server-template.ts +0 -493
- package/src/runtime/isr-handler.ts +0 -184
- package/src/runtime/oauth/handler.ts +0 -407
- package/src/runtime/oauth/index.ts +0 -49
- package/src/runtime/oauth/providers.ts +0 -38
- package/src/types/api.ts +0 -40
- package/src/types/config.ts +0 -379
- package/src/types/content.ts +0 -66
- package/src/types/index.ts +0 -7
- package/src/types/middleware.ts +0 -63
- package/src/types/page.ts +0 -117
- package/src/types/plugin.ts +0 -34
- package/tsconfig.build.json +0 -10
- package/tsconfig.json +0 -19
- package/vitest.config.ts +0 -33
package/src/plugin/build-ssg.ts
DELETED
|
@@ -1,403 +0,0 @@
|
|
|
1
|
-
import { writeFile, mkdir, readFile } from 'node:fs/promises'
|
|
2
|
-
import { existsSync } from 'node:fs'
|
|
3
|
-
import { join } from 'pathe'
|
|
4
|
-
import {
|
|
5
|
-
injectFaviconLink,
|
|
6
|
-
injectCanonicalLink,
|
|
7
|
-
addNoopenerToExternalLinks,
|
|
8
|
-
generateRobotsTxt,
|
|
9
|
-
} from './html-post-process.js'
|
|
10
|
-
import { createServer, type UserConfig } from 'vite'
|
|
11
|
-
import type { ResolvedCerConfig } from './dev-server.js'
|
|
12
|
-
import type { ContentItem } from '../types/content.js'
|
|
13
|
-
import { buildSSR } from './build-ssr.js'
|
|
14
|
-
import { buildRouteEntry } from './path-utils.js'
|
|
15
|
-
import fg from 'fast-glob'
|
|
16
|
-
|
|
17
|
-
interface SsgManifest {
|
|
18
|
-
generatedAt: string
|
|
19
|
-
paths: string[]
|
|
20
|
-
errors: Array<{ path: string; error: string }>
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const CONTENT_STORE_KEY = '__CER_CONTENT_STORE__'
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Collects all static paths to generate.
|
|
28
|
-
* For 'auto' mode: scans app/pages/ and collects only static (non-dynamic) routes
|
|
29
|
-
* plus any dynamic routes that provide ssg.paths().
|
|
30
|
-
* For explicit string[] mode: uses those paths directly.
|
|
31
|
-
*/
|
|
32
|
-
/**
|
|
33
|
-
* Expands a set of base paths into locale-prefixed variants when i18n is
|
|
34
|
-
* configured. Skips expansion for `no_prefix` strategy.
|
|
35
|
-
*/
|
|
36
|
-
function _expandWithLocales(
|
|
37
|
-
paths: string[],
|
|
38
|
-
i18n: ResolvedCerConfig['i18n'],
|
|
39
|
-
): string[] {
|
|
40
|
-
if (!i18n || i18n.strategy === 'no_prefix') return paths
|
|
41
|
-
const { locales, defaultLocale, strategy } = i18n
|
|
42
|
-
const expanded: string[] = []
|
|
43
|
-
for (const p of paths) {
|
|
44
|
-
for (const locale of locales) {
|
|
45
|
-
const isDefault = locale === defaultLocale
|
|
46
|
-
if (isDefault && strategy === 'prefix_except_default') {
|
|
47
|
-
expanded.push(p)
|
|
48
|
-
} else {
|
|
49
|
-
expanded.push(`/${locale}${p === '/' ? '' : p}`)
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return [...new Set(expanded)]
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async function collectSsgPaths(
|
|
57
|
-
config: ResolvedCerConfig,
|
|
58
|
-
viteUserConfig: UserConfig,
|
|
59
|
-
): Promise<string[]> {
|
|
60
|
-
const ssgConfig = config.ssg
|
|
61
|
-
|
|
62
|
-
if (Array.isArray(ssgConfig.routes) && ssgConfig.routes.length > 0) {
|
|
63
|
-
return _expandWithLocales(ssgConfig.routes, config.i18n)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Auto-discover paths
|
|
67
|
-
const paths: string[] = ['/']
|
|
68
|
-
|
|
69
|
-
if (!existsSync(config.pagesDir)) return paths
|
|
70
|
-
|
|
71
|
-
const files = await fg('**/*.ts', {
|
|
72
|
-
cwd: config.pagesDir,
|
|
73
|
-
absolute: true,
|
|
74
|
-
onlyFiles: true,
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
const staticFiles: string[] = []
|
|
78
|
-
const dynamicFiles: Array<{
|
|
79
|
-
file: string
|
|
80
|
-
entry: ReturnType<typeof buildRouteEntry>
|
|
81
|
-
usesQueryContent: boolean
|
|
82
|
-
}> = []
|
|
83
|
-
|
|
84
|
-
for (const file of files) {
|
|
85
|
-
// Skip routes that declare render: 'server' or render: 'spa' — they are
|
|
86
|
-
// either always-SSR or client-only and must not be pre-rendered.
|
|
87
|
-
let src = ''
|
|
88
|
-
try {
|
|
89
|
-
src = await readFile(file, 'utf-8')
|
|
90
|
-
const renderMatch = src.match(/render\s*:\s*['"]([^'"]+)['"]/)
|
|
91
|
-
const renderMode = renderMatch ? renderMatch[1] : null
|
|
92
|
-
if (renderMode === 'server' || renderMode === 'spa') continue
|
|
93
|
-
} catch { /* ignore read errors */ }
|
|
94
|
-
|
|
95
|
-
const entry = buildRouteEntry(file, config.pagesDir)
|
|
96
|
-
const usesQueryContent = /\bqueryContent\s*\(/.test(src)
|
|
97
|
-
|
|
98
|
-
if (!entry.isDynamic && !entry.isCatchAll) {
|
|
99
|
-
staticFiles.push(file)
|
|
100
|
-
if (entry.routePath !== '/') {
|
|
101
|
-
paths.push(entry.routePath)
|
|
102
|
-
}
|
|
103
|
-
} else if (entry.isDynamic) {
|
|
104
|
-
dynamicFiles.push({ file, entry, usesQueryContent })
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (dynamicFiles.length > 0) {
|
|
109
|
-
// Use a Vite SSR dev server to load page modules with auto-imports applied
|
|
110
|
-
const viteServer = await createServer({
|
|
111
|
-
...viteUserConfig,
|
|
112
|
-
root: config.root,
|
|
113
|
-
server: { middlewareMode: true },
|
|
114
|
-
appType: 'custom',
|
|
115
|
-
logLevel: 'silent',
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
try {
|
|
119
|
-
for (const { file, entry, usesQueryContent } of dynamicFiles) {
|
|
120
|
-
try {
|
|
121
|
-
const pageMod = await viteServer.ssrLoadModule(file)
|
|
122
|
-
const pageMeta = pageMod.meta ?? pageMod.pageMeta
|
|
123
|
-
|
|
124
|
-
if (pageMeta?.render === 'server' || pageMeta?.render === 'spa') continue
|
|
125
|
-
if (pageMeta?.ssg?.paths) {
|
|
126
|
-
const pathsResult = await pageMeta.ssg.paths()
|
|
127
|
-
for (const ctx of pathsResult) {
|
|
128
|
-
let resolvedPath = entry.routePath
|
|
129
|
-
for (const [key, value] of Object.entries(ctx.params as Record<string, unknown>)) {
|
|
130
|
-
resolvedPath = resolvedPath.replace(`:${key}*`, String(value))
|
|
131
|
-
resolvedPath = resolvedPath.replace(`:${key}`, String(value))
|
|
132
|
-
}
|
|
133
|
-
paths.push(resolvedPath)
|
|
134
|
-
}
|
|
135
|
-
continue
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (entry.isCatchAll && usesQueryContent) {
|
|
139
|
-
const contentStore = (globalThis as Record<string, unknown>)[CONTENT_STORE_KEY] as ContentItem[] | undefined
|
|
140
|
-
const catchAllPaths = _collectCatchAllContentPaths(entry.routePath, contentStore ?? [])
|
|
141
|
-
paths.push(...catchAllPaths)
|
|
142
|
-
}
|
|
143
|
-
} catch {
|
|
144
|
-
console.warn(`[cer-app] Could not enumerate paths for ${file}`)
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
} finally {
|
|
148
|
-
await viteServer.close()
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// When i18n is configured, expand every collected path into locale-prefixed
|
|
153
|
-
// variants so each locale gets its own pre-rendered HTML file.
|
|
154
|
-
// strategy 'prefix' → /en/about, /fr/about
|
|
155
|
-
// strategy 'prefix_except_default' → /about, /fr/about
|
|
156
|
-
// strategy 'no_prefix' → no expansion (locale detected from header/cookie only)
|
|
157
|
-
if (config.i18n && config.i18n.strategy !== 'no_prefix') {
|
|
158
|
-
const { locales, defaultLocale, strategy } = config.i18n
|
|
159
|
-
const expanded: string[] = []
|
|
160
|
-
for (const p of paths) {
|
|
161
|
-
for (const locale of locales) {
|
|
162
|
-
const isDefault = locale === defaultLocale
|
|
163
|
-
if (isDefault && strategy === 'prefix_except_default') {
|
|
164
|
-
expanded.push(p)
|
|
165
|
-
} else {
|
|
166
|
-
expanded.push(`/${locale}${p === '/' ? '' : p}`)
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
return [...new Set(expanded)]
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return [...new Set(paths)] // deduplicate
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function _collectCatchAllContentPaths(routePath: string, store: ContentItem[]): string[] {
|
|
177
|
-
if (store.length === 0) return []
|
|
178
|
-
|
|
179
|
-
const prefix = routePath
|
|
180
|
-
.replace(/:[^/]+\*$/, '')
|
|
181
|
-
.replace(/\/+$/, '') || '/'
|
|
182
|
-
|
|
183
|
-
return store
|
|
184
|
-
.map((item) => item._path)
|
|
185
|
-
.filter((contentPath) => {
|
|
186
|
-
if (prefix === '/') return true
|
|
187
|
-
return contentPath === prefix || contentPath.startsWith(prefix + '/')
|
|
188
|
-
})
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Cache the server module across renderPath calls (loaded once per SSG run)
|
|
192
|
-
let _serverMod: Record<string, unknown> | null = null
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Renders a single path using the SSR server bundle and returns the HTML.
|
|
196
|
-
*
|
|
197
|
-
* The server bundle's handler already merges the SSR output with
|
|
198
|
-
* dist/client/index.html internally (via the _mergeWithClientTemplate helper
|
|
199
|
-
* it embeds at build time), so this function simply captures the response.
|
|
200
|
-
*/
|
|
201
|
-
async function renderPath(
|
|
202
|
-
path: string,
|
|
203
|
-
serverBundlePath: string,
|
|
204
|
-
): Promise<string> {
|
|
205
|
-
// Load server bundle once
|
|
206
|
-
if (!_serverMod) {
|
|
207
|
-
try {
|
|
208
|
-
_serverMod = await import(serverBundlePath) as Record<string, unknown>
|
|
209
|
-
} catch (err) {
|
|
210
|
-
throw new Error(`Failed to load server bundle at ${serverBundlePath}: ${err}`)
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
const handlerFn =
|
|
215
|
-
(typeof _serverMod['handler'] === 'function' ? _serverMod['handler'] : null) ??
|
|
216
|
-
(typeof (_serverMod['default'] as Record<string, unknown> | undefined)?.['handler'] === 'function'
|
|
217
|
-
? (_serverMod['default'] as Record<string, unknown>)['handler']
|
|
218
|
-
: null)
|
|
219
|
-
|
|
220
|
-
if (typeof handlerFn !== 'function') {
|
|
221
|
-
console.warn(`[cer-app] No handler function found in server bundle for path: ${path}`)
|
|
222
|
-
return ''
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Mock req/res for the Express-style handler.
|
|
226
|
-
// The handler internally merges with dist/client/index.html, so we just
|
|
227
|
-
// capture whatever it writes/ends with.
|
|
228
|
-
const mockReq = { url: path, headers: {} }
|
|
229
|
-
return new Promise<string>((resolve, reject) => {
|
|
230
|
-
const chunks: string[] = []
|
|
231
|
-
const mockRes = {
|
|
232
|
-
setHeader: () => {},
|
|
233
|
-
write: (chunk: string) => { chunks.push(chunk) },
|
|
234
|
-
end: (body?: string) => resolve(chunks.join('') + (body ?? '')),
|
|
235
|
-
}
|
|
236
|
-
;(handlerFn as (req: unknown, res: unknown) => Promise<void>)(mockReq, mockRes).catch(reject)
|
|
237
|
-
})
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Writes the rendered HTML to the output directory.
|
|
242
|
-
* path '/' -> dist/index.html
|
|
243
|
-
* path '/about' -> dist/about/index.html
|
|
244
|
-
* @internal exported for unit testing
|
|
245
|
-
*/
|
|
246
|
-
export async function writeRenderedPath(
|
|
247
|
-
path: string,
|
|
248
|
-
html: string,
|
|
249
|
-
distDir: string,
|
|
250
|
-
): Promise<void> {
|
|
251
|
-
let outputPath: string
|
|
252
|
-
if (path === '/') {
|
|
253
|
-
outputPath = join(distDir, 'index.html')
|
|
254
|
-
} else {
|
|
255
|
-
// Normalize path and create directory
|
|
256
|
-
const cleanPath = path.replace(/^\//, '').replace(/\/$/, '')
|
|
257
|
-
outputPath = join(distDir, cleanPath, 'index.html')
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
await mkdir(join(outputPath, '..'), { recursive: true })
|
|
261
|
-
await writeFile(outputPath, html, 'utf-8')
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Detects which favicon href to use from the project's public directory.
|
|
266
|
-
* Prefers .svg > .ico > .png. Returns null when none is found.
|
|
267
|
-
*/
|
|
268
|
-
function detectFaviconHref(root: string): string | null {
|
|
269
|
-
const candidates = ['/favicon.svg', '/favicon.ico', '/favicon.png']
|
|
270
|
-
for (const href of candidates) {
|
|
271
|
-
if (existsSync(join(root, 'public', href.slice(1)))) return href
|
|
272
|
-
}
|
|
273
|
-
return null
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* Applies all HTML post-processing transforms to a rendered page.
|
|
278
|
-
*
|
|
279
|
-
* - Injects `<link rel="icon">` when a favicon exists in public/ and none
|
|
280
|
-
* is already declared in the HTML.
|
|
281
|
-
* - Injects `<link rel="canonical">` when `config.siteUrl` is set and none
|
|
282
|
-
* is already declared.
|
|
283
|
-
* - Adds `rel="noopener noreferrer"` to every `<a target="_blank">` link
|
|
284
|
-
* that is missing it.
|
|
285
|
-
*/
|
|
286
|
-
function postProcessHtml(
|
|
287
|
-
html: string,
|
|
288
|
-
path: string,
|
|
289
|
-
config: ResolvedCerConfig,
|
|
290
|
-
faviconHref: string | null,
|
|
291
|
-
): string {
|
|
292
|
-
let out = html
|
|
293
|
-
|
|
294
|
-
if (faviconHref) {
|
|
295
|
-
out = injectFaviconLink(out, faviconHref)
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
if (config.siteUrl) {
|
|
299
|
-
const canonicalUrl = config.siteUrl + (path === '/' ? '' : path)
|
|
300
|
-
out = injectCanonicalLink(out, canonicalUrl)
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
out = addNoopenerToExternalLinks(out)
|
|
304
|
-
|
|
305
|
-
return out
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* Full SSG build pipeline:
|
|
310
|
-
* 1. Run the SSR dual-build (client + server bundles)
|
|
311
|
-
* 2. Enumerate all paths to generate
|
|
312
|
-
* 3. Render each path using the server bundle
|
|
313
|
-
* 4. Post-process HTML (favicon, canonical, noopener)
|
|
314
|
-
* 5. Write HTML files to dist/
|
|
315
|
-
* 6. Write robots.txt (when not already present in public/)
|
|
316
|
-
* 7. Write ssg-manifest.json
|
|
317
|
-
*/
|
|
318
|
-
export async function buildSSG(
|
|
319
|
-
config: ResolvedCerConfig,
|
|
320
|
-
viteUserConfig: UserConfig = {},
|
|
321
|
-
): Promise<void> {
|
|
322
|
-
const distDir = join(config.root, 'dist')
|
|
323
|
-
const serverDistDir = join(distDir, 'server')
|
|
324
|
-
const serverBundlePath = join(serverDistDir, 'server.js')
|
|
325
|
-
|
|
326
|
-
console.log('[cer-app] Starting SSG build...')
|
|
327
|
-
|
|
328
|
-
// Step 1: Run the SSR build to produce client + server bundles
|
|
329
|
-
await buildSSR(config, viteUserConfig)
|
|
330
|
-
|
|
331
|
-
// Step 2: Collect paths to generate
|
|
332
|
-
console.log('[cer-app] Collecting SSG paths...')
|
|
333
|
-
const paths = await collectSsgPaths(config, viteUserConfig)
|
|
334
|
-
console.log(`[cer-app] Found ${paths.length} path(s) to generate:`, paths)
|
|
335
|
-
|
|
336
|
-
// Detect favicon once for use in every page's post-processing pass.
|
|
337
|
-
const faviconHref = detectFaviconHref(config.root)
|
|
338
|
-
|
|
339
|
-
// Step 3+4+5: Render, post-process, and write paths with bounded concurrency.
|
|
340
|
-
// The server bundle uses per-request router instances (initRouter returns the
|
|
341
|
-
// router; the factory passes it to createStreamingSSRHandler as { vnode, router })
|
|
342
|
-
// so concurrent renders are safe — each request carries its own router with its
|
|
343
|
-
// own URL state and never reads from the shared activeRouterProxy singleton.
|
|
344
|
-
const concurrency = config.ssg?.concurrency ?? 4
|
|
345
|
-
console.log(`[cer-app] Rendering ${paths.length} path(s) with concurrency ${concurrency}...`)
|
|
346
|
-
|
|
347
|
-
const generatedPaths: string[] = []
|
|
348
|
-
const errors: Array<{ path: string; error: string }> = []
|
|
349
|
-
|
|
350
|
-
// Process paths in chunks of `concurrency` so we don't overwhelm the process
|
|
351
|
-
// with hundreds of simultaneous renders on very large sites.
|
|
352
|
-
for (let i = 0; i < paths.length; i += concurrency) {
|
|
353
|
-
const chunk = paths.slice(i, i + concurrency)
|
|
354
|
-
const results = await Promise.allSettled(
|
|
355
|
-
chunk.map(async (path) => {
|
|
356
|
-
console.log(`[cer-app] Generating: ${path}`)
|
|
357
|
-
const raw = await renderPath(path, serverBundlePath)
|
|
358
|
-
const html = postProcessHtml(raw, path, config, faviconHref)
|
|
359
|
-
await writeRenderedPath(path, html, distDir)
|
|
360
|
-
return path
|
|
361
|
-
}),
|
|
362
|
-
)
|
|
363
|
-
for (let j = 0; j < results.length; j++) {
|
|
364
|
-
const result = results[j]
|
|
365
|
-
if (result.status === 'fulfilled') {
|
|
366
|
-
generatedPaths.push(result.value)
|
|
367
|
-
} else {
|
|
368
|
-
const errorMsg = String(result.reason)
|
|
369
|
-
console.error(`[cer-app] Failed to generate ${chunk[j]}: ${errorMsg}`)
|
|
370
|
-
errors.push({ path: chunk[j], error: errorMsg })
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// Step 6: Write robots.txt unless the project supplies its own via public/robots.txt,
|
|
376
|
-
// which Vite copies to dist/ as-is. Always overwrite any previously generated file so
|
|
377
|
-
// that siteUrl changes (e.g. adding the Sitemap directive) take effect on rebuild.
|
|
378
|
-
const publicRobots = join(config.root, 'public', 'robots.txt')
|
|
379
|
-
if (!existsSync(publicRobots)) {
|
|
380
|
-
const distRobots = join(distDir, 'robots.txt')
|
|
381
|
-
await mkdir(distDir, { recursive: true })
|
|
382
|
-
await writeFile(distRobots, generateRobotsTxt(config.siteUrl), 'utf-8')
|
|
383
|
-
console.log('[cer-app] Generated robots.txt')
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
// Step 7: Write SSG manifest
|
|
387
|
-
const manifest: SsgManifest = {
|
|
388
|
-
generatedAt: new Date().toISOString(),
|
|
389
|
-
paths: generatedPaths,
|
|
390
|
-
errors,
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
const manifestPath = join(distDir, 'ssg-manifest.json')
|
|
394
|
-
await mkdir(distDir, { recursive: true })
|
|
395
|
-
await writeFile(manifestPath, JSON.stringify(manifest, null, 2), 'utf-8')
|
|
396
|
-
|
|
397
|
-
console.log(`[cer-app] SSG build complete.`)
|
|
398
|
-
console.log(` Generated ${generatedPaths.length} page(s).`)
|
|
399
|
-
if (errors.length > 0) {
|
|
400
|
-
console.warn(` ${errors.length} error(s) — see ${manifestPath} for details.`)
|
|
401
|
-
}
|
|
402
|
-
console.log(` Manifest: ${manifestPath}`)
|
|
403
|
-
}
|
package/src/plugin/build-ssr.ts
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import { build, type UserConfig } from 'vite'
|
|
2
|
-
import { join, resolve } from 'pathe'
|
|
3
|
-
import { existsSync, renameSync } from 'node:fs'
|
|
4
|
-
import type { ResolvedCerConfig } from './dev-server.js'
|
|
5
|
-
import { getGeneratedDir, writeGeneratedDir } from './generated-dir.js'
|
|
6
|
-
import { ENTRY_SERVER_TEMPLATE } from '../runtime/entry-server-template.js'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Resolves the client build entry point for an SSR/SSG build.
|
|
10
|
-
*
|
|
11
|
-
* Priority order:
|
|
12
|
-
* 1. `index.html` at the project root — consumer-provided HTML shell.
|
|
13
|
-
* 2. `.cer/index.html` — auto-generated HTML shell (Nuxt-style magic).
|
|
14
|
-
* 3. `app/entry-client.ts` — fallback for projects that manage HTML externally.
|
|
15
|
-
* 4. `app/app.ts` — last resort (same bundle, no DSD hydration preamble).
|
|
16
|
-
*/
|
|
17
|
-
export function resolveClientEntry(config: ResolvedCerConfig): string {
|
|
18
|
-
const indexHtml = resolve(config.root, 'index.html')
|
|
19
|
-
if (existsSync(indexHtml)) return indexHtml
|
|
20
|
-
const cerIndexHtml = join(getGeneratedDir(config.root), 'index.html')
|
|
21
|
-
if (existsSync(cerIndexHtml)) return cerIndexHtml
|
|
22
|
-
const entryClient = resolve(config.srcDir, 'entry-client.ts')
|
|
23
|
-
if (existsSync(entryClient)) return entryClient
|
|
24
|
-
return resolve(config.srcDir, 'app.ts')
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function generateServerEntryCode(): string {
|
|
28
|
-
return ENTRY_SERVER_TEMPLATE
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Runs the dual (client + server) SSR build using Vite's programmatic API.
|
|
33
|
-
*
|
|
34
|
-
* 1. Client bundle: normal Vite build (outputs to dist/client)
|
|
35
|
-
* 2. Server bundle: SSR build (outputs to dist/server), uses a generated entry
|
|
36
|
-
*/
|
|
37
|
-
export async function buildSSR(
|
|
38
|
-
config: ResolvedCerConfig,
|
|
39
|
-
viteUserConfig: UserConfig = {},
|
|
40
|
-
): Promise<void> {
|
|
41
|
-
const clientOutDir = join(config.root, 'dist/client')
|
|
42
|
-
const serverOutDir = join(config.root, 'dist/server')
|
|
43
|
-
|
|
44
|
-
// Write .cer/ generated files BEFORE resolving the client entry so that
|
|
45
|
-
// .cer/index.html is on disk when resolveClientEntry checks for it.
|
|
46
|
-
writeGeneratedDir(config)
|
|
47
|
-
|
|
48
|
-
// Resolve the client entry — index.html is preferred so Vite writes a
|
|
49
|
-
// processed index.html to dist/client/ for use as the SSG shell template.
|
|
50
|
-
const clientEntry = resolveClientEntry(config)
|
|
51
|
-
|
|
52
|
-
// Build the client bundle
|
|
53
|
-
console.log('[cer-app] Building client bundle...')
|
|
54
|
-
await build({
|
|
55
|
-
...viteUserConfig,
|
|
56
|
-
root: config.root,
|
|
57
|
-
build: {
|
|
58
|
-
...viteUserConfig.build,
|
|
59
|
-
outDir: clientOutDir,
|
|
60
|
-
ssrManifest: true,
|
|
61
|
-
rollupOptions: {
|
|
62
|
-
input: clientEntry,
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
// If the client entry was .cer/index.html, Vite outputs it as
|
|
68
|
-
// dist/client/.cer/index.html (preserving relative path). The SSR server
|
|
69
|
-
// template expects dist/client/index.html, so rename it into place.
|
|
70
|
-
const generatedHtmlOut = join(clientOutDir, '.cer/index.html')
|
|
71
|
-
const rootHtmlOut = join(clientOutDir, 'index.html')
|
|
72
|
-
if (existsSync(generatedHtmlOut) && !existsSync(rootHtmlOut)) {
|
|
73
|
-
renameSync(generatedHtmlOut, rootHtmlOut)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Generate server entry source inline via a virtual plugin
|
|
77
|
-
const serverEntryCode = generateServerEntryCode()
|
|
78
|
-
const VIRTUAL_SERVER_ENTRY = 'virtual:cer-server-entry'
|
|
79
|
-
const RESOLVED_SERVER_ENTRY = '\0virtual:cer-server-entry'
|
|
80
|
-
|
|
81
|
-
// Build the server (SSR) bundle
|
|
82
|
-
console.log('[cer-app] Building server bundle...')
|
|
83
|
-
await build({
|
|
84
|
-
...viteUserConfig,
|
|
85
|
-
root: config.root,
|
|
86
|
-
plugins: [
|
|
87
|
-
...(viteUserConfig.plugins ?? []),
|
|
88
|
-
{
|
|
89
|
-
name: 'vite-plugin-cer-server-entry',
|
|
90
|
-
enforce: 'pre' as const,
|
|
91
|
-
resolveId(id: string) {
|
|
92
|
-
if (id === VIRTUAL_SERVER_ENTRY) return RESOLVED_SERVER_ENTRY
|
|
93
|
-
},
|
|
94
|
-
load(id: string) {
|
|
95
|
-
if (id === RESOLVED_SERVER_ENTRY) return serverEntryCode
|
|
96
|
-
},
|
|
97
|
-
},
|
|
98
|
-
],
|
|
99
|
-
build: {
|
|
100
|
-
...viteUserConfig.build,
|
|
101
|
-
outDir: serverOutDir,
|
|
102
|
-
ssr: true,
|
|
103
|
-
rollupOptions: {
|
|
104
|
-
input: VIRTUAL_SERVER_ENTRY,
|
|
105
|
-
output: {
|
|
106
|
-
entryFileNames: 'server.js',
|
|
107
|
-
},
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
ssr: {
|
|
111
|
-
// Keep vite-plugin-cer-app inlined so its virtual-module composables
|
|
112
|
-
// (useRoute, useState, useFetch, etc.) are available in the server bundle.
|
|
113
|
-
//
|
|
114
|
-
// Do NOT add @jasonshimmy/custom-elements-runtime here. Inlining it
|
|
115
|
-
// creates a second, isolated copy of the component registry (the module-
|
|
116
|
-
// level Map in namespace-helpers). Third-party CER component libraries
|
|
117
|
-
// (e.g. @jasonshimmy/cer-material) are external and resolve the runtime
|
|
118
|
-
// from node_modules at runtime, giving them a *different* Map instance.
|
|
119
|
-
// That means components registered by plugins never appear in the
|
|
120
|
-
// renderer's registry → renderToStreamWithJITCSSDSD emits bare stubs
|
|
121
|
-
// with no DSD → FOUC when the browser upgrades those elements.
|
|
122
|
-
//
|
|
123
|
-
// By keeping the runtime external both the server bundle and all
|
|
124
|
-
// third-party plugins resolve it from node_modules at runtime, sharing
|
|
125
|
-
// one Map and one registry, so DSD is generated for all registered
|
|
126
|
-
// components regardless of which package called component().
|
|
127
|
-
noExternal: ['@jasonshimmy/vite-plugin-cer-app'],
|
|
128
|
-
},
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
console.log('[cer-app] SSR build complete.')
|
|
132
|
-
console.log(` Client: ${clientOutDir}`)
|
|
133
|
-
console.log(` Server: ${serverOutDir}`)
|
|
134
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { writeFileSync, mkdirSync } from 'node:fs'
|
|
2
|
-
import { join, dirname } from 'pathe'
|
|
3
|
-
import type { ContentItem, ContentMeta } from '../../types/content.js'
|
|
4
|
-
import { toContentMeta } from './parser.js'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Converts a `_path` to the relative file path under `_content/`.
|
|
8
|
-
*
|
|
9
|
-
* Special case: `_path === '/'` writes to `_content/index.json` rather than
|
|
10
|
-
* `_content/.json` (which would be invalid on most filesystems and confusing).
|
|
11
|
-
*/
|
|
12
|
-
export function contentPathToFile(path: string): string {
|
|
13
|
-
if (path === '/') return 'index.json'
|
|
14
|
-
// Remove leading slash, append .json
|
|
15
|
-
return path.slice(1) + '.json'
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Writes all content output files to `<outDir>/_content/`:
|
|
20
|
-
*
|
|
21
|
-
* - `manifest.json` — `ContentMeta[]` (no body, no toc, no excerpt)
|
|
22
|
-
* - `search-index.json` — serialised MiniSearch index string
|
|
23
|
-
* - `[path].json` — full `ContentItem` per document
|
|
24
|
-
*
|
|
25
|
-
* `router.base` is NOT prepended here — that is a client-side fetch concern.
|
|
26
|
-
*/
|
|
27
|
-
export function emitContentFiles(
|
|
28
|
-
items: ContentItem[],
|
|
29
|
-
outDir: string,
|
|
30
|
-
searchIndexJson: string,
|
|
31
|
-
): void {
|
|
32
|
-
const contentDir = join(outDir, '_content')
|
|
33
|
-
mkdirSync(contentDir, { recursive: true })
|
|
34
|
-
|
|
35
|
-
// manifest.json — lean metadata only
|
|
36
|
-
const manifest: ContentMeta[] = items.map(toContentMeta)
|
|
37
|
-
writeFileSync(join(contentDir, 'manifest.json'), JSON.stringify(manifest), 'utf-8')
|
|
38
|
-
|
|
39
|
-
// search-index.json
|
|
40
|
-
writeFileSync(join(contentDir, 'search-index.json'), searchIndexJson, 'utf-8')
|
|
41
|
-
|
|
42
|
-
// Per-document full JSON files
|
|
43
|
-
for (const item of items) {
|
|
44
|
-
const relFile = contentPathToFile(item._path)
|
|
45
|
-
const absFile = join(contentDir, relFile)
|
|
46
|
-
// Ensure subdirectory exists (e.g. _content/blog/ for _path: /blog/hello)
|
|
47
|
-
mkdirSync(dirname(absFile), { recursive: true })
|
|
48
|
-
writeFileSync(absFile, JSON.stringify(item), 'utf-8')
|
|
49
|
-
}
|
|
50
|
-
}
|