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