@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
|
@@ -1,320 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from 'vitest'
|
|
2
|
-
import type { IncomingMessage, ServerResponse } from 'node:http'
|
|
3
|
-
import {
|
|
4
|
-
isPathBounded,
|
|
5
|
-
matchRoutePattern,
|
|
6
|
-
findRevalidate,
|
|
7
|
-
findRenderMode,
|
|
8
|
-
renderForIsr,
|
|
9
|
-
serveFromIsrCache,
|
|
10
|
-
type IsrCacheEntry,
|
|
11
|
-
} from '../../cli/commands/preview-isr.js'
|
|
12
|
-
|
|
13
|
-
// ─── isPathBounded ────────────────────────────────────────────────────────────
|
|
14
|
-
|
|
15
|
-
describe('isPathBounded', () => {
|
|
16
|
-
it('allows normal file paths inside the root', () => {
|
|
17
|
-
expect(isPathBounded('/dist', '/index.html')).toBe(true)
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
it('allows nested paths inside the root', () => {
|
|
21
|
-
expect(isPathBounded('/dist', '/assets/app.js')).toBe(true)
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
it('allows the root path itself', () => {
|
|
25
|
-
expect(isPathBounded('/dist', '/')).toBe(true)
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
it('blocks simple path traversal (../ escape)', () => {
|
|
29
|
-
expect(isPathBounded('/dist', '/../../../../etc/passwd')).toBe(false)
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
it('blocks a path that escapes by one level', () => {
|
|
33
|
-
expect(isPathBounded('/dist', '/../secret')).toBe(false)
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
it('does not confuse a sibling directory for the root', () => {
|
|
37
|
-
// /dist-other starts with /dist but is NOT inside /dist
|
|
38
|
-
expect(isPathBounded('/dist', '/../dist-other/file')).toBe(false)
|
|
39
|
-
})
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
// ─── matchRoutePattern ────────────────────────────────────────────────────────
|
|
43
|
-
|
|
44
|
-
describe('matchRoutePattern', () => {
|
|
45
|
-
it('matches identical paths', () => {
|
|
46
|
-
expect(matchRoutePattern('/about', '/about')).toBe(true)
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it('matches root path', () => {
|
|
50
|
-
expect(matchRoutePattern('/', '/')).toBe(true)
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
it('does not match different static paths', () => {
|
|
54
|
-
expect(matchRoutePattern('/about', '/contact')).toBe(false)
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
it('matches :param segments', () => {
|
|
58
|
-
expect(matchRoutePattern('/blog/:slug', '/blog/hello-world')).toBe(true)
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
it('does not match when param segment is missing', () => {
|
|
62
|
-
expect(matchRoutePattern('/blog/:slug', '/blog')).toBe(false)
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
it('matches :param* catch-all against single segment', () => {
|
|
66
|
-
expect(matchRoutePattern('/:all*', '/about')).toBe(true)
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
it('matches :param* catch-all against multi-segment path', () => {
|
|
70
|
-
expect(matchRoutePattern('/:all*', '/deeply/nested/path')).toBe(true)
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
it('matches :param* catch-all against root', () => {
|
|
74
|
-
expect(matchRoutePattern('/:all*', '/')).toBe(true)
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
it('does not match longer path against shorter static pattern', () => {
|
|
78
|
-
expect(matchRoutePattern('/blog', '/blog/extra')).toBe(false)
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
it('handles trailing slashes gracefully', () => {
|
|
82
|
-
expect(matchRoutePattern('/about/', '/about')).toBe(true)
|
|
83
|
-
expect(matchRoutePattern('/about', '/about/')).toBe(true)
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
it('matches multiple :param segments', () => {
|
|
87
|
-
expect(matchRoutePattern('/users/:id/posts/:postId', '/users/42/posts/99')).toBe(true)
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
it('does not match paths that differ only by a regex wildcard character (dot-safety)', () => {
|
|
91
|
-
// Without escaping, '/api.v1' regex would be '^/apixv1$' which matches '/apixv1'.
|
|
92
|
-
// With escaping, the dot is literal and '/apixv1' must not match '/api.v1'.
|
|
93
|
-
expect(matchRoutePattern('/api.v1/users', '/apixv1/users')).toBe(false)
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
it('matches route with literal dot in static segment', () => {
|
|
97
|
-
expect(matchRoutePattern('/api.v1/users', '/api.v1/users')).toBe(true)
|
|
98
|
-
})
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
// ─── findRevalidate ───────────────────────────────────────────────────────────
|
|
102
|
-
|
|
103
|
-
describe('findRevalidate', () => {
|
|
104
|
-
it('returns null for an empty routes array', () => {
|
|
105
|
-
expect(findRevalidate([], '/about')).toBeNull()
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
it('returns null when no route matches', () => {
|
|
109
|
-
const routes = [{ path: '/contact', meta: { ssg: { revalidate: 60 } } }]
|
|
110
|
-
expect(findRevalidate(routes, '/about')).toBeNull()
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
it('returns null when matched route has no meta', () => {
|
|
114
|
-
const routes = [{ path: '/about' }]
|
|
115
|
-
expect(findRevalidate(routes, '/about')).toBeNull()
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
it('returns null when matched route has no ssg.revalidate', () => {
|
|
119
|
-
const routes = [{ path: '/about', meta: { layout: 'default' } }]
|
|
120
|
-
expect(findRevalidate(routes, '/about')).toBeNull()
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
it('returns revalidate value for a matching static route', () => {
|
|
124
|
-
const routes = [{ path: '/about', meta: { ssg: { revalidate: 60 } } }]
|
|
125
|
-
expect(findRevalidate(routes, '/about')).toBe(60)
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
it('returns revalidate value for a matching dynamic route', () => {
|
|
129
|
-
const routes = [{ path: '/blog/:slug', meta: { ssg: { revalidate: 300 } } }]
|
|
130
|
-
expect(findRevalidate(routes, '/blog/hello')).toBe(300)
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
it('returns revalidate value for a catch-all route', () => {
|
|
134
|
-
const routes = [{ path: '/:all*', meta: { ssg: { revalidate: 120 } } }]
|
|
135
|
-
expect(findRevalidate(routes, '/some/unmatched/path')).toBe(120)
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
it('picks the first matching route when multiple match', () => {
|
|
139
|
-
const routes = [
|
|
140
|
-
{ path: '/blog/:slug', meta: { ssg: { revalidate: 300 } } },
|
|
141
|
-
{ path: '/:all*', meta: { ssg: { revalidate: 60 } } },
|
|
142
|
-
]
|
|
143
|
-
// /blog/:slug matches first
|
|
144
|
-
expect(findRevalidate(routes, '/blog/post')).toBe(300)
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
it('ignores non-numeric revalidate values', () => {
|
|
148
|
-
const routes = [{ path: '/about', meta: { ssg: { revalidate: 'invalid' } } }]
|
|
149
|
-
expect(findRevalidate(routes, '/about')).toBeNull()
|
|
150
|
-
})
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
// ─── findRenderMode ───────────────────────────────────────────────────────────
|
|
154
|
-
|
|
155
|
-
describe('findRenderMode', () => {
|
|
156
|
-
it('returns null for an empty routes array', () => {
|
|
157
|
-
expect(findRenderMode([], '/about')).toBeNull()
|
|
158
|
-
})
|
|
159
|
-
|
|
160
|
-
it('returns null when no route matches', () => {
|
|
161
|
-
const routes = [{ path: '/contact', meta: { render: 'server' } }]
|
|
162
|
-
expect(findRenderMode(routes, '/about')).toBeNull()
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
it('returns null when matched route has no render meta', () => {
|
|
166
|
-
const routes = [{ path: '/about' }]
|
|
167
|
-
expect(findRenderMode(routes, '/about')).toBeNull()
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
it('returns "server" for a matching route with render: server', () => {
|
|
171
|
-
const routes = [{ path: '/dashboard', meta: { render: 'server' } }]
|
|
172
|
-
expect(findRenderMode(routes, '/dashboard')).toBe('server')
|
|
173
|
-
})
|
|
174
|
-
|
|
175
|
-
it('returns "spa" for a matching route with render: spa', () => {
|
|
176
|
-
const routes = [{ path: '/profile', meta: { render: 'spa' } }]
|
|
177
|
-
expect(findRenderMode(routes, '/profile')).toBe('spa')
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
it('returns "static" for a matching route with render: static', () => {
|
|
181
|
-
const routes = [{ path: '/about', meta: { render: 'static' } }]
|
|
182
|
-
expect(findRenderMode(routes, '/about')).toBe('static')
|
|
183
|
-
})
|
|
184
|
-
|
|
185
|
-
it('returns null for unrecognised render values', () => {
|
|
186
|
-
const routes = [{ path: '/about', meta: { render: 'unknown' } }]
|
|
187
|
-
expect(findRenderMode(routes, '/about')).toBeNull()
|
|
188
|
-
})
|
|
189
|
-
|
|
190
|
-
it('matches dynamic routes', () => {
|
|
191
|
-
const routes = [{ path: '/blog/:slug', meta: { render: 'server' } }]
|
|
192
|
-
expect(findRenderMode(routes, '/blog/my-post')).toBe('server')
|
|
193
|
-
})
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
// ─── renderForIsr ─────────────────────────────────────────────────────────────
|
|
197
|
-
|
|
198
|
-
describe('renderForIsr', () => {
|
|
199
|
-
it('captures HTML from the handler', async () => {
|
|
200
|
-
const handler = (_req: IncomingMessage, res: ServerResponse) => {
|
|
201
|
-
res.setHeader('Content-Type', 'text/html')
|
|
202
|
-
res.end('<html>hello</html>')
|
|
203
|
-
}
|
|
204
|
-
const entry = await renderForIsr('/about', handler, 60)
|
|
205
|
-
expect(entry).not.toBeNull()
|
|
206
|
-
expect(entry!.html).toBe('<html>hello</html>')
|
|
207
|
-
})
|
|
208
|
-
|
|
209
|
-
it('captures status code from the handler', async () => {
|
|
210
|
-
const handler = (_req: IncomingMessage, res: ServerResponse) => {
|
|
211
|
-
res.statusCode = 404
|
|
212
|
-
res.end('Not Found')
|
|
213
|
-
}
|
|
214
|
-
const entry = await renderForIsr('/missing', handler, 30)
|
|
215
|
-
expect(entry!.statusCode).toBe(404)
|
|
216
|
-
})
|
|
217
|
-
|
|
218
|
-
it('captures response headers from the handler', async () => {
|
|
219
|
-
const handler = (_req: IncomingMessage, res: ServerResponse) => {
|
|
220
|
-
res.setHeader('Content-Type', 'text/html; charset=utf-8')
|
|
221
|
-
res.end('<html/>')
|
|
222
|
-
}
|
|
223
|
-
const entry = await renderForIsr('/about', handler, 60)
|
|
224
|
-
expect(entry!.headers['content-type']).toBe('text/html; charset=utf-8')
|
|
225
|
-
})
|
|
226
|
-
|
|
227
|
-
it('sets revalidate to the provided TTL', async () => {
|
|
228
|
-
const handler = (_req: IncomingMessage, res: ServerResponse) => { res.end('ok') }
|
|
229
|
-
const entry = await renderForIsr('/about', handler, 300)
|
|
230
|
-
expect(entry!.revalidate).toBe(300)
|
|
231
|
-
})
|
|
232
|
-
|
|
233
|
-
it('sets revalidating to false on the returned entry', async () => {
|
|
234
|
-
const handler = (_req: IncomingMessage, res: ServerResponse) => { res.end('ok') }
|
|
235
|
-
const entry = await renderForIsr('/about', handler, 60)
|
|
236
|
-
expect(entry!.revalidating).toBe(false)
|
|
237
|
-
})
|
|
238
|
-
|
|
239
|
-
it('returns null when the handler throws', async () => {
|
|
240
|
-
const handler = () => { throw new Error('boom') }
|
|
241
|
-
const entry = await renderForIsr('/about', handler as SsrHandlerFn, 60)
|
|
242
|
-
expect(entry).toBeNull()
|
|
243
|
-
})
|
|
244
|
-
|
|
245
|
-
it('passes the correct URL to the synthetic request', async () => {
|
|
246
|
-
let capturedUrl = ''
|
|
247
|
-
const handler = (req: IncomingMessage, res: ServerResponse) => {
|
|
248
|
-
capturedUrl = req.url ?? ''
|
|
249
|
-
res.end('ok')
|
|
250
|
-
}
|
|
251
|
-
await renderForIsr('/blog/hello', handler, 60)
|
|
252
|
-
expect(capturedUrl).toBe('/blog/hello')
|
|
253
|
-
})
|
|
254
|
-
})
|
|
255
|
-
|
|
256
|
-
type SsrHandlerFn = (req: IncomingMessage, res: ServerResponse) => void | Promise<void>
|
|
257
|
-
|
|
258
|
-
// ─── serveFromIsrCache ────────────────────────────────────────────────────────
|
|
259
|
-
|
|
260
|
-
describe('serveFromIsrCache', () => {
|
|
261
|
-
function makeFakeRes() {
|
|
262
|
-
const headers: Record<string, string> = {}
|
|
263
|
-
let statusCode = 200
|
|
264
|
-
let body = ''
|
|
265
|
-
const res = {
|
|
266
|
-
get statusCode() { return statusCode },
|
|
267
|
-
set statusCode(v: number) { statusCode = v },
|
|
268
|
-
setHeader: vi.fn((name: string, value: string) => { headers[name] = value }),
|
|
269
|
-
end: vi.fn((chunk: string) => { body = chunk }),
|
|
270
|
-
_headers: headers,
|
|
271
|
-
_body: () => body,
|
|
272
|
-
_status: () => statusCode,
|
|
273
|
-
}
|
|
274
|
-
return res
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
function makeEntry(overrides: Partial<IsrCacheEntry> = {}): IsrCacheEntry {
|
|
278
|
-
return {
|
|
279
|
-
html: '<html>cached</html>',
|
|
280
|
-
headers: { 'content-type': 'text/html' },
|
|
281
|
-
statusCode: 200,
|
|
282
|
-
builtAt: Date.now(),
|
|
283
|
-
revalidate: 60,
|
|
284
|
-
revalidating: false,
|
|
285
|
-
...overrides,
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
it('writes the cached HTML to the response', () => {
|
|
290
|
-
const res = makeFakeRes()
|
|
291
|
-
serveFromIsrCache(makeEntry(), res as unknown as ServerResponse, 'HIT')
|
|
292
|
-
expect(res.end).toHaveBeenCalledWith('<html>cached</html>')
|
|
293
|
-
})
|
|
294
|
-
|
|
295
|
-
it('sets X-Cache: HIT header', () => {
|
|
296
|
-
const res = makeFakeRes()
|
|
297
|
-
serveFromIsrCache(makeEntry(), res as unknown as ServerResponse, 'HIT')
|
|
298
|
-
expect(res.setHeader).toHaveBeenCalledWith('X-Cache', 'HIT')
|
|
299
|
-
})
|
|
300
|
-
|
|
301
|
-
it('sets X-Cache: STALE header', () => {
|
|
302
|
-
const res = makeFakeRes()
|
|
303
|
-
serveFromIsrCache(makeEntry(), res as unknown as ServerResponse, 'STALE')
|
|
304
|
-
expect(res.setHeader).toHaveBeenCalledWith('X-Cache', 'STALE')
|
|
305
|
-
})
|
|
306
|
-
|
|
307
|
-
it('forwards cached headers to the response', () => {
|
|
308
|
-
const entry = makeEntry({ headers: { 'content-type': 'text/html; charset=utf-8' } })
|
|
309
|
-
const res = makeFakeRes()
|
|
310
|
-
serveFromIsrCache(entry, res as unknown as ServerResponse, 'HIT')
|
|
311
|
-
expect(res.setHeader).toHaveBeenCalledWith('content-type', 'text/html; charset=utf-8')
|
|
312
|
-
})
|
|
313
|
-
|
|
314
|
-
it('sets the status code from the cache entry', () => {
|
|
315
|
-
const entry = makeEntry({ statusCode: 404 })
|
|
316
|
-
const res = makeFakeRes()
|
|
317
|
-
serveFromIsrCache(entry, res as unknown as ServerResponse, 'HIT')
|
|
318
|
-
expect(res._status()).toBe(404)
|
|
319
|
-
})
|
|
320
|
-
})
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest'
|
|
2
|
-
|
|
3
|
-
// Import from the main package entry to trigger coverage of src/index.ts
|
|
4
|
-
// re-exports. These are all re-exports of other modules so we just verify
|
|
5
|
-
// the named exports resolve without errors.
|
|
6
|
-
import { defineConfig, cerApp } from '../index.js'
|
|
7
|
-
|
|
8
|
-
describe('package main entry', () => {
|
|
9
|
-
it('exports defineConfig', () => {
|
|
10
|
-
expect(typeof defineConfig).toBe('function')
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
it('defineConfig is a pass-through', () => {
|
|
14
|
-
const cfg = { mode: 'spa' as const }
|
|
15
|
-
expect(defineConfig(cfg)).toBe(cfg)
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
it('exports cerApp', () => {
|
|
19
|
-
expect(typeof cerApp).toBe('function')
|
|
20
|
-
})
|
|
21
|
-
})
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import { readFileSync } from 'node:fs'
|
|
3
|
-
import { resolve } from 'pathe'
|
|
4
|
-
import { generateAppEntryTemplate } from '../../runtime/app-template.js'
|
|
5
|
-
import { generateJitInitModule } from '../../plugin/index.js'
|
|
6
|
-
|
|
7
|
-
const src = readFileSync(
|
|
8
|
-
resolve(import.meta.dirname, '../../runtime/app-template.ts'),
|
|
9
|
-
'utf-8',
|
|
10
|
-
)
|
|
11
|
-
|
|
12
|
-
describe('app-template (source content)', () => {
|
|
13
|
-
it('does not import virtual:cer-components (components injected per-file by cerComponentImports)', () => {
|
|
14
|
-
expect(src).not.toContain('virtual:cer-components')
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
it('imports virtual:cer-routes', () => {
|
|
18
|
-
expect(src).toContain('virtual:cer-routes')
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
it('imports virtual:cer-layouts', () => {
|
|
22
|
-
expect(src).toContain('virtual:cer-layouts')
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
it('imports virtual:cer-plugins', () => {
|
|
26
|
-
expect(src).toContain('virtual:cer-plugins')
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
it('imports registerBuiltinComponents from custom-elements-runtime', () => {
|
|
30
|
-
expect(src).toContain('registerBuiltinComponents')
|
|
31
|
-
expect(src).toContain('setDevMode')
|
|
32
|
-
expect(src).toContain('@jasonshimmy/custom-elements-runtime')
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
it('enables runtime dev logging from the downstream Vite env', () => {
|
|
36
|
-
expect(src).toContain('const _cerRuntimeDev')
|
|
37
|
-
expect(src).toContain('import.meta.env?.DEV')
|
|
38
|
-
expect(src).toContain('_cerProcess?.env')
|
|
39
|
-
expect(src).toContain('(globalThis).__CE_RUNTIME_DEV__ = _cerRuntimeDev')
|
|
40
|
-
expect(src).toContain('setDevMode(_cerRuntimeDev)')
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
it('imports initRouter from the router subpath', () => {
|
|
44
|
-
expect(src).toContain('initRouter')
|
|
45
|
-
expect(src).toContain('custom-elements-runtime/router')
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
it('imports virtual:cer-jit-css', () => {
|
|
49
|
-
expect(src).toContain('virtual:cer-jit-css')
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
it('imports virtual:cer-jit-init before virtual:cer-layouts so JIT CSS is enabled before elements upgrade', () => {
|
|
53
|
-
expect(src).toContain('virtual:cer-jit-init')
|
|
54
|
-
const initIdx = src.indexOf('virtual:cer-jit-init')
|
|
55
|
-
const layoutsIdx = src.indexOf('virtual:cer-layouts')
|
|
56
|
-
const pluginsIdx = src.indexOf('virtual:cer-plugins')
|
|
57
|
-
expect(initIdx).toBeLessThan(layoutsIdx)
|
|
58
|
-
expect(initIdx).toBeLessThan(pluginsIdx)
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
it('does not import enableJITCSS in the module body (delegated to virtual:cer-jit-init)', () => {
|
|
62
|
-
// The enableJITCSS import must not appear in the template literal — it belongs
|
|
63
|
-
// in virtual:cer-jit-init which runs during the static import phase, before
|
|
64
|
-
// virtual:cer-layouts and virtual:cer-plugins upgrade custom elements.
|
|
65
|
-
const templateStart = src.indexOf('return `')
|
|
66
|
-
expect(src.slice(templateStart)).not.toContain("import { enableJITCSS }")
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
it('imports virtual:cer-content-components for markdown-backed custom elements', () => {
|
|
70
|
-
expect(src).toContain('virtual:cer-content-components')
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
it('exports router', () => {
|
|
74
|
-
expect(src).toContain('export { router }')
|
|
75
|
-
})
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
describe('generateAppEntryTemplate', () => {
|
|
79
|
-
it('includes virtual:cer-jit-init import', () => {
|
|
80
|
-
const out = generateAppEntryTemplate()
|
|
81
|
-
expect(out).toContain("import 'virtual:cer-jit-init'")
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
it('places virtual:cer-jit-init before virtual:cer-layouts and virtual:cer-plugins', () => {
|
|
85
|
-
const out = generateAppEntryTemplate()
|
|
86
|
-
const initIdx = out.indexOf('virtual:cer-jit-init')
|
|
87
|
-
const layoutsIdx = out.indexOf('virtual:cer-layouts')
|
|
88
|
-
const pluginsIdx = out.indexOf('virtual:cer-plugins')
|
|
89
|
-
expect(initIdx).toBeGreaterThanOrEqual(0)
|
|
90
|
-
expect(initIdx).toBeLessThan(layoutsIdx)
|
|
91
|
-
expect(initIdx).toBeLessThan(pluginsIdx)
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
it('does not import enableJITCSS in the generated output (delegated to virtual:cer-jit-init)', () => {
|
|
95
|
-
const out = generateAppEntryTemplate()
|
|
96
|
-
expect(out).not.toContain("import { enableJITCSS }")
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
it('still includes all standard template content', () => {
|
|
100
|
-
const out = generateAppEntryTemplate()
|
|
101
|
-
expect(out).toContain('virtual:cer-jit-css')
|
|
102
|
-
expect(out).toContain('virtual:cer-routes')
|
|
103
|
-
expect(out).toContain('export { router }')
|
|
104
|
-
})
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
describe('generateJitInitModule', () => {
|
|
108
|
-
it('calls enableJITCSS() with no arguments when no options are set', () => {
|
|
109
|
-
const out = generateJitInitModule({ content: [], extendedColors: false, customColors: undefined })
|
|
110
|
-
expect(out).toContain('enableJITCSS()')
|
|
111
|
-
expect(out).not.toContain('customColors')
|
|
112
|
-
expect(out).not.toContain('extendedColors')
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
it('calls enableJITCSS() with no arguments when customColors is an empty object', () => {
|
|
116
|
-
const out = generateJitInitModule({ content: [], extendedColors: false, customColors: {} })
|
|
117
|
-
expect(out).toContain('enableJITCSS()')
|
|
118
|
-
expect(out).not.toContain('customColors')
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
it('serializes a single color family into the enableJITCSS call', () => {
|
|
122
|
-
const out = generateJitInitModule({ content: [], extendedColors: false, customColors: { brand: { '500': '#7c3aed' } } })
|
|
123
|
-
expect(out).toContain('enableJITCSS({ customColors:')
|
|
124
|
-
expect(out).toContain('"brand"')
|
|
125
|
-
expect(out).toContain('"500"')
|
|
126
|
-
expect(out).toContain('"#7c3aed"')
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
it('serializes multiple color families correctly', () => {
|
|
130
|
-
const out = generateJitInitModule({
|
|
131
|
-
content: [],
|
|
132
|
-
extendedColors: false,
|
|
133
|
-
customColors: { brand: { '100': '#ede9fe', '900': '#4c1d95' }, accent: { DEFAULT: '#f59e0b' } },
|
|
134
|
-
})
|
|
135
|
-
expect(out).toContain('"brand"')
|
|
136
|
-
expect(out).toContain('"accent"')
|
|
137
|
-
expect(out).toContain('"DEFAULT"')
|
|
138
|
-
expect(out).toContain('"#f59e0b"')
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
it('serializes CSS variable references as color values', () => {
|
|
142
|
-
const out = generateJitInitModule({
|
|
143
|
-
content: [],
|
|
144
|
-
extendedColors: false,
|
|
145
|
-
customColors: { surface: { DEFAULT: 'var(--md-sys-color-surface)' } },
|
|
146
|
-
})
|
|
147
|
-
expect(out).toContain('"surface"')
|
|
148
|
-
expect(out).toContain('var(--md-sys-color-surface)')
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
it('includes extendedColors: true when set', () => {
|
|
152
|
-
const out = generateJitInitModule({ content: [], extendedColors: true, customColors: undefined })
|
|
153
|
-
expect(out).toContain('extendedColors: true')
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
it('includes extendedColors array when set', () => {
|
|
157
|
-
const out = generateJitInitModule({ content: [], extendedColors: ['slate', 'blue'], customColors: undefined })
|
|
158
|
-
expect(out).toContain('extendedColors: ["slate","blue"]')
|
|
159
|
-
})
|
|
160
|
-
|
|
161
|
-
it('imports enableJITCSS from the jit-css subpath', () => {
|
|
162
|
-
const out = generateJitInitModule({ content: [], extendedColors: false, customColors: undefined })
|
|
163
|
-
expect(out).toContain('enableJITCSS')
|
|
164
|
-
expect(out).toContain('custom-elements-runtime/jit-css')
|
|
165
|
-
})
|
|
166
|
-
})
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Integration tests for the build-ssg renderPath success path.
|
|
3
|
-
*
|
|
4
|
-
* These tests use a real temporary ESM module as the "server bundle" so that
|
|
5
|
-
* the dynamic import() inside renderPath actually resolves, exercising the
|
|
6
|
-
* success branches (writeRenderedPath call, generatedPaths.push, etc.) that
|
|
7
|
-
* are unreachable when the bundle is absent.
|
|
8
|
-
*
|
|
9
|
-
* A separate test file is required so the node:fs/promises mock does NOT
|
|
10
|
-
* shadow the real writeFile/mkdir used to set up the temp bundle.
|
|
11
|
-
*/
|
|
12
|
-
import { vi, describe, it, expect, beforeAll, afterAll } from 'vitest'
|
|
13
|
-
import { mkdirSync, writeFileSync, rmSync } from 'node:fs'
|
|
14
|
-
import { tmpdir } from 'node:os'
|
|
15
|
-
import { join } from 'pathe'
|
|
16
|
-
|
|
17
|
-
// Mock buildSSR so the SSG pipeline skips the Vite build step.
|
|
18
|
-
vi.mock('../../plugin/build-ssr.js', () => ({ buildSSR: vi.fn().mockResolvedValue(undefined) }))
|
|
19
|
-
vi.mock('fast-glob', () => ({ default: vi.fn().mockResolvedValue([]) }))
|
|
20
|
-
// Intentionally NOT mocking node:fs or node:fs/promises so real writes work.
|
|
21
|
-
|
|
22
|
-
import type { ResolvedCerConfig } from '../../plugin/dev-server.js'
|
|
23
|
-
|
|
24
|
-
let tmpRoot: string
|
|
25
|
-
|
|
26
|
-
beforeAll(() => {
|
|
27
|
-
// Create a minimal "server bundle" that exports a handler.
|
|
28
|
-
// The handler writes a minimal HTML string to res.end so renderPath captures it.
|
|
29
|
-
tmpRoot = join(tmpdir(), `cer-ssg-render-${Date.now()}`)
|
|
30
|
-
const serverDir = join(tmpRoot, 'dist', 'server')
|
|
31
|
-
mkdirSync(serverDir, { recursive: true })
|
|
32
|
-
writeFileSync(
|
|
33
|
-
join(serverDir, 'server.js'),
|
|
34
|
-
// ESM module — works because Vitest runs with Node's native ESM loader.
|
|
35
|
-
`export const handler = async (req, res) => {
|
|
36
|
-
res.setHeader('Content-Type', 'text/html');
|
|
37
|
-
res.end('<!DOCTYPE html><html><head></head><body>Hello from ' + req.url + '</body></html>');
|
|
38
|
-
};
|
|
39
|
-
export const apiRoutes = [];
|
|
40
|
-
export const plugins = [];
|
|
41
|
-
export const layouts = {};
|
|
42
|
-
`,
|
|
43
|
-
'utf-8',
|
|
44
|
-
)
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
afterAll(() => {
|
|
48
|
-
rmSync(tmpRoot, { recursive: true, force: true })
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
function makeConfig(overrides: Partial<ResolvedCerConfig> = {}): ResolvedCerConfig {
|
|
52
|
-
return {
|
|
53
|
-
root: tmpRoot,
|
|
54
|
-
srcDir: join(tmpRoot, 'app'),
|
|
55
|
-
pagesDir: join(tmpRoot, 'app', 'pages'),
|
|
56
|
-
mode: 'ssg',
|
|
57
|
-
ssg: { routes: ['/'], concurrency: 1 },
|
|
58
|
-
...overrides,
|
|
59
|
-
} as unknown as ResolvedCerConfig
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
describe('buildSSG — renderPath success (real server bundle)', () => {
|
|
63
|
-
it('writes rendered HTML for the root path to dist/index.html', async () => {
|
|
64
|
-
// Reset module registry so _serverMod cache is cleared between test runs.
|
|
65
|
-
await vi.resetModules()
|
|
66
|
-
const { buildSSG } = await import('../../plugin/build-ssg.js')
|
|
67
|
-
await buildSSG(makeConfig())
|
|
68
|
-
|
|
69
|
-
const { readFileSync, existsSync } = await import('node:fs')
|
|
70
|
-
const outPath = join(tmpRoot, 'dist', 'index.html')
|
|
71
|
-
expect(existsSync(outPath)).toBe(true)
|
|
72
|
-
const html = readFileSync(outPath, 'utf-8')
|
|
73
|
-
expect(html).toContain('Hello from /')
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
it('records the path in the ssg-manifest paths array', async () => {
|
|
77
|
-
await vi.resetModules()
|
|
78
|
-
const { buildSSG } = await import('../../plugin/build-ssg.js')
|
|
79
|
-
await buildSSG(makeConfig())
|
|
80
|
-
|
|
81
|
-
const { readFileSync } = await import('node:fs')
|
|
82
|
-
const manifest = JSON.parse(readFileSync(join(tmpRoot, 'dist', 'ssg-manifest.json'), 'utf-8'))
|
|
83
|
-
expect(manifest.paths).toContain('/')
|
|
84
|
-
expect(manifest.errors).toHaveLength(0)
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
it('renders multiple paths with concurrency > 1', async () => {
|
|
88
|
-
await vi.resetModules()
|
|
89
|
-
const { buildSSG } = await import('../../plugin/build-ssg.js')
|
|
90
|
-
const config = makeConfig({ ssg: { routes: ['/', '/about'], concurrency: 2 } } as Partial<ResolvedCerConfig>)
|
|
91
|
-
await buildSSG(config)
|
|
92
|
-
|
|
93
|
-
const { readFileSync, existsSync } = await import('node:fs')
|
|
94
|
-
expect(existsSync(join(tmpRoot, 'dist', 'index.html'))).toBe(true)
|
|
95
|
-
expect(existsSync(join(tmpRoot, 'dist', 'about', 'index.html'))).toBe(true)
|
|
96
|
-
const manifest = JSON.parse(readFileSync(join(tmpRoot, 'dist', 'ssg-manifest.json'), 'utf-8'))
|
|
97
|
-
expect(manifest.paths).toHaveLength(2)
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
it('captures HTML from a streaming handler that calls write() then end()', async () => {
|
|
101
|
-
// Simulate the renderToStreamWithJITCSSDSD-based handler which calls
|
|
102
|
-
// res.write(firstChunk) for sync content and res.end(polyfill + tail).
|
|
103
|
-
// Use a completely separate tmpdir with a unique path so Node's native ESM
|
|
104
|
-
// import cache cannot return a previously-loaded module for the same URL.
|
|
105
|
-
const streamRoot = join(tmpdir(), `cer-ssg-stream-${Date.now()}`)
|
|
106
|
-
const streamServerDir = join(streamRoot, 'dist', 'server')
|
|
107
|
-
mkdirSync(streamServerDir, { recursive: true })
|
|
108
|
-
writeFileSync(
|
|
109
|
-
join(streamServerDir, 'server.js'),
|
|
110
|
-
`export const handler = async (req, res) => {
|
|
111
|
-
res.setHeader('Content-Type', 'text/html');
|
|
112
|
-
res.setHeader('Transfer-Encoding', 'chunked');
|
|
113
|
-
res.write('<html><head></head>');
|
|
114
|
-
res.write('<body>streamed</body>');
|
|
115
|
-
res.end('</html>');
|
|
116
|
-
};
|
|
117
|
-
export const apiRoutes = [];
|
|
118
|
-
export const plugins = [];
|
|
119
|
-
export const layouts = {};
|
|
120
|
-
`,
|
|
121
|
-
'utf-8',
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
await vi.resetModules()
|
|
125
|
-
const { buildSSG } = await import('../../plugin/build-ssg.js')
|
|
126
|
-
const config = {
|
|
127
|
-
root: streamRoot,
|
|
128
|
-
srcDir: join(streamRoot, 'app'),
|
|
129
|
-
pagesDir: join(streamRoot, 'app', 'pages'),
|
|
130
|
-
mode: 'ssg',
|
|
131
|
-
ssg: { routes: ['/'], concurrency: 1 },
|
|
132
|
-
} as unknown as ResolvedCerConfig
|
|
133
|
-
await buildSSG(config)
|
|
134
|
-
|
|
135
|
-
const { readFileSync, existsSync } = await import('node:fs')
|
|
136
|
-
const outPath = join(streamRoot, 'dist', 'index.html')
|
|
137
|
-
expect(existsSync(outPath)).toBe(true)
|
|
138
|
-
const html = readFileSync(outPath, 'utf-8')
|
|
139
|
-
expect(html).toContain('<html><head></head>')
|
|
140
|
-
expect(html).toContain('<body>streamed</body>')
|
|
141
|
-
expect(html).toContain('</html>')
|
|
142
|
-
|
|
143
|
-
rmSync(streamRoot, { recursive: true, force: true })
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
it('uses cached _serverMod on second renderPath call (no double import)', async () => {
|
|
147
|
-
// In a fresh module instance, render two paths sequentially.
|
|
148
|
-
// The second renderPath call hits the !_serverMod === false branch (cache).
|
|
149
|
-
await vi.resetModules()
|
|
150
|
-
const { buildSSG } = await import('../../plugin/build-ssg.js')
|
|
151
|
-
const config = makeConfig({ ssg: { routes: ['/', '/page2'], concurrency: 1 } } as Partial<ResolvedCerConfig>)
|
|
152
|
-
// Should complete without error — second call uses cached module
|
|
153
|
-
await expect(buildSSG(config)).resolves.not.toThrow()
|
|
154
|
-
})
|
|
155
|
-
})
|