@jasonshimmy/vite-plugin-cer-app 0.23.2 → 0.25.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/adapters/netlify.js +4 -2
- package/dist/cli/adapters/netlify.js.map +1 -1
- 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/commands/preview.d.ts.map +1 -1
- package/dist/cli/commands/preview.js +4 -2
- package/dist/cli/commands/preview.js.map +1 -1
- 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 +4 -2
- 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 +226 -17
- package/dist/plugin/build-ssg.js.map +1 -1
- package/dist/plugin/content/index.d.ts +1 -1
- package/dist/plugin/content/index.d.ts.map +1 -1
- package/dist/plugin/content/index.js +8 -8
- package/dist/plugin/content/index.js.map +1 -1
- package/dist/plugin/content/parser.d.ts +3 -3
- package/dist/plugin/content/parser.d.ts.map +1 -1
- package/dist/plugin/content/parser.js +103 -24
- 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 +7 -0
- package/dist/plugin/html-post-process.d.ts.map +1 -1
- package/dist/plugin/html-post-process.js +26 -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 +181 -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/content.d.ts +18 -1
- package/dist/types/content.d.ts.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,884 +0,0 @@
|
|
|
1
|
-
import { vi, describe, it, expect, beforeEach } from 'vitest'
|
|
2
|
-
|
|
3
|
-
vi.mock('node:fs', () => ({ existsSync: vi.fn() }))
|
|
4
|
-
vi.mock('node:fs/promises', () => ({ readFile: vi.fn().mockResolvedValue('') }))
|
|
5
|
-
vi.mock('../../../plugin/scanner.js', () => ({ scanDirectory: vi.fn() }))
|
|
6
|
-
|
|
7
|
-
import { existsSync } from 'node:fs'
|
|
8
|
-
import { readFile } from 'node:fs/promises'
|
|
9
|
-
import { scanDirectory } from '../../../plugin/scanner.js'
|
|
10
|
-
import { generateRoutesCode } from '../../../plugin/virtual/routes.js'
|
|
11
|
-
|
|
12
|
-
const PAGES = '/project/app/pages'
|
|
13
|
-
|
|
14
|
-
describe('generateRoutesCode', () => {
|
|
15
|
-
beforeEach(() => {
|
|
16
|
-
vi.mocked(existsSync).mockReturnValue(true)
|
|
17
|
-
vi.mocked(scanDirectory).mockResolvedValue([])
|
|
18
|
-
vi.mocked(readFile).mockResolvedValue('' as never)
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
it('returns empty routes when directory does not exist', async () => {
|
|
22
|
-
vi.mocked(existsSync).mockReturnValue(false)
|
|
23
|
-
const code = await generateRoutesCode(PAGES)
|
|
24
|
-
expect(code).toContain('const routes = []')
|
|
25
|
-
expect(code).toContain('export default routes')
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
it('returns empty routes when no files found', async () => {
|
|
29
|
-
const code = await generateRoutesCode(PAGES)
|
|
30
|
-
expect(code).toContain('const routes = []')
|
|
31
|
-
expect(code).toContain('export default routes')
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
it('includes AUTO-GENERATED comment', async () => {
|
|
35
|
-
const code = await generateRoutesCode(PAGES)
|
|
36
|
-
expect(code).toContain('AUTO-GENERATED by @jasonshimmy/vite-plugin-cer-app')
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
it('generates a lazy load() route for a static page', async () => {
|
|
40
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
41
|
-
const code = await generateRoutesCode(PAGES)
|
|
42
|
-
expect(code).toContain('path: \"/about\"')
|
|
43
|
-
expect(code).toContain('load:')
|
|
44
|
-
expect(code).toContain(`import("${PAGES}/about.ts")`)
|
|
45
|
-
expect(code).toContain('"page-about"')
|
|
46
|
-
// No eager static imports
|
|
47
|
-
expect(code).not.toContain('import * as')
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
it('load() also forwards the page module loader export', async () => {
|
|
51
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
52
|
-
const code = await generateRoutesCode(PAGES)
|
|
53
|
-
expect(code).toContain('loader: mod.loader')
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
it('generates a lazy load() route for index.ts → /', async () => {
|
|
57
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/index.ts`])
|
|
58
|
-
const code = await generateRoutesCode(PAGES)
|
|
59
|
-
expect(code).toContain('path: \"/\"')
|
|
60
|
-
expect(code).toContain('load:')
|
|
61
|
-
expect(code).toContain('"page-index"')
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
it('generates a lazy load() route for a dynamic route', async () => {
|
|
65
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/blog/[slug].ts`])
|
|
66
|
-
const code = await generateRoutesCode(PAGES)
|
|
67
|
-
expect(code).toContain('path: \"/blog/:slug\"')
|
|
68
|
-
expect(code).toContain('load:')
|
|
69
|
-
expect(code).toContain('"page-blog-slug"')
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
it('generates a lazy load() route for a catch-all route', async () => {
|
|
73
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/[...all].ts`])
|
|
74
|
-
const code = await generateRoutesCode(PAGES)
|
|
75
|
-
expect(code).toContain('path: \"/:all*\"')
|
|
76
|
-
expect(code).toContain('load:')
|
|
77
|
-
expect(code).toContain('"page-all"')
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
it('sorts routes: static before dynamic before catch-all', async () => {
|
|
81
|
-
vi.mocked(scanDirectory).mockResolvedValue([
|
|
82
|
-
`${PAGES}/[...all].ts`,
|
|
83
|
-
`${PAGES}/[id].ts`,
|
|
84
|
-
`${PAGES}/about.ts`,
|
|
85
|
-
])
|
|
86
|
-
const code = await generateRoutesCode(PAGES)
|
|
87
|
-
const aboutIdx = code.indexOf('\"/about\"')
|
|
88
|
-
const idIdx = code.indexOf('\"/:id\"')
|
|
89
|
-
const allIdx = code.indexOf('\"/:all*\"')
|
|
90
|
-
expect(aboutIdx).toBeLessThan(idIdx)
|
|
91
|
-
expect(idIdx).toBeLessThan(allIdx)
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
it('includes export default routes', async () => {
|
|
95
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
96
|
-
const code = await generateRoutesCode(PAGES)
|
|
97
|
-
expect(code).toContain('export default routes')
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
it('generates multiple lazy routes', async () => {
|
|
101
|
-
vi.mocked(scanDirectory).mockResolvedValue([
|
|
102
|
-
`${PAGES}/index.ts`,
|
|
103
|
-
`${PAGES}/about.ts`,
|
|
104
|
-
`${PAGES}/contact.ts`,
|
|
105
|
-
])
|
|
106
|
-
const code = await generateRoutesCode(PAGES)
|
|
107
|
-
expect(code).toContain('\"/about\"')
|
|
108
|
-
expect(code).toContain('\"/contact\"')
|
|
109
|
-
expect(code).toContain('\"/\"')
|
|
110
|
-
expect(code).not.toContain('import * as')
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
it('inlines middleware names and adds beforeEnter when middleware is present', async () => {
|
|
114
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/dashboard.ts`])
|
|
115
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
116
|
-
`component('page-dashboard', () => html\`<div>Dashboard</div>\`)\nexport const meta = { middleware: ['auth'] }` as never,
|
|
117
|
-
)
|
|
118
|
-
const code = await generateRoutesCode(PAGES)
|
|
119
|
-
expect(code).toContain('beforeEnter')
|
|
120
|
-
expect(code).toContain('"auth"')
|
|
121
|
-
expect(code).toContain('virtual:cer-middleware')
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
it('omits beforeEnter when no middleware is declared', async () => {
|
|
125
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
126
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
127
|
-
`component('page-about', () => html\`<h1>About</h1>\`)\nexport const meta = { layout: 'default' }` as never,
|
|
128
|
-
)
|
|
129
|
-
const code = await generateRoutesCode(PAGES)
|
|
130
|
-
expect(code).not.toContain('beforeEnter')
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
it('uses return-value API (await handler) not callback-style (new Promise)', async () => {
|
|
134
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/dashboard.ts`])
|
|
135
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
136
|
-
`component('page-dashboard', () => html\`<div/>\`)\nexport const meta = { middleware: ['auth'] }` as never,
|
|
137
|
-
)
|
|
138
|
-
const code = await generateRoutesCode(PAGES)
|
|
139
|
-
expect(code).toContain('await handler(to, from, next)')
|
|
140
|
-
expect(code).not.toContain('new Promise')
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
it('passes a next() function as third arg to each middleware handler', async () => {
|
|
144
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/dashboard.ts`])
|
|
145
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
146
|
-
`component('page-dashboard', () => html\`<div/>\`)\nexport const meta = { middleware: ['auth'] }` as never,
|
|
147
|
-
)
|
|
148
|
-
const code = await generateRoutesCode(PAGES)
|
|
149
|
-
expect(code).toContain('const next = async () =>')
|
|
150
|
-
expect(code).toContain('_calledNext = true')
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
it('runs downstream middleware when next() is called', async () => {
|
|
154
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/dashboard.ts`])
|
|
155
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
156
|
-
`component('page-dashboard', () => html\`<div/>\`)\nexport const meta = { middleware: ['logger', 'auth'] }` as never,
|
|
157
|
-
)
|
|
158
|
-
const code = await generateRoutesCode(PAGES)
|
|
159
|
-
expect(code).toContain('_runNext')
|
|
160
|
-
expect(code).toContain('"logger"')
|
|
161
|
-
expect(code).toContain('"auth"')
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
it('wraps handler call in try-catch (blocks navigation on error)', async () => {
|
|
165
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/dashboard.ts`])
|
|
166
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
167
|
-
`component('page-dashboard', () => html\`<div/>\`)\nexport const meta = { middleware: ['auth'] }` as never,
|
|
168
|
-
)
|
|
169
|
-
const code = await generateRoutesCode(PAGES)
|
|
170
|
-
expect(code).toContain('try {')
|
|
171
|
-
expect(code).toContain('} catch (err) {')
|
|
172
|
-
// New chain pattern: sets _guardResult = false and returns from the inner _runNext
|
|
173
|
-
expect(code).toContain('_guardResult = false')
|
|
174
|
-
})
|
|
175
|
-
|
|
176
|
-
it('logs the middleware name in the error message', async () => {
|
|
177
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/dashboard.ts`])
|
|
178
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
179
|
-
`component('page-dashboard', () => html\`<div/>\`)\nexport const meta = { middleware: ['auth'] }` as never,
|
|
180
|
-
)
|
|
181
|
-
const code = await generateRoutesCode(PAGES)
|
|
182
|
-
expect(code).toContain('console.error')
|
|
183
|
-
expect(code).toContain('Middleware')
|
|
184
|
-
expect(code).toContain('err)')
|
|
185
|
-
})
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
// ─── meta.layout ─────────────────────────────────────────────────────────────
|
|
189
|
-
|
|
190
|
-
describe('generateRoutesCode — meta.layout', () => {
|
|
191
|
-
beforeEach(() => {
|
|
192
|
-
// Return true for directory existence checks but false for .error.ts and
|
|
193
|
-
// _error.ts so resolveRouteErrorTag doesn't inject spurious errorTag fields
|
|
194
|
-
// into routes that aren't testing error boundaries.
|
|
195
|
-
vi.mocked(existsSync).mockImplementation((p: unknown) => {
|
|
196
|
-
const path = String(p)
|
|
197
|
-
if (path.endsWith('.error.ts') || path.endsWith('/_error.ts') || path.endsWith('\\_error.ts')) return false
|
|
198
|
-
return true
|
|
199
|
-
})
|
|
200
|
-
vi.mocked(scanDirectory).mockResolvedValue([])
|
|
201
|
-
vi.mocked(readFile).mockResolvedValue('' as never)
|
|
202
|
-
})
|
|
203
|
-
|
|
204
|
-
it('emits meta.layout when layout is declared in page source', async () => {
|
|
205
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
206
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
207
|
-
`component('page-about', () => html\`<h1>About</h1>\`)\nexport const meta = { layout: 'minimal' }` as never,
|
|
208
|
-
)
|
|
209
|
-
const code = await generateRoutesCode(PAGES)
|
|
210
|
-
expect(code).toContain('meta: { layout: "minimal" }')
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
it('omits meta field when no layout is declared', async () => {
|
|
214
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
215
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
216
|
-
`component('page-about', () => html\`<h1>About</h1>\`)` as never,
|
|
217
|
-
)
|
|
218
|
-
const code = await generateRoutesCode(PAGES)
|
|
219
|
-
expect(code).toContain('path: "/about"')
|
|
220
|
-
expect(code).not.toContain('path: "/about",\n load: () => import("/project/app/pages/about.ts").then(mod => ({ default: "page-about", loader: mod.loader ?? null })),\n meta:')
|
|
221
|
-
})
|
|
222
|
-
|
|
223
|
-
it('emits meta.layout alongside beforeEnter when both layout and middleware are present', async () => {
|
|
224
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/dashboard.ts`])
|
|
225
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
226
|
-
`component('page-dashboard', () => html\`<div/>\`)\nexport const meta = { layout: 'admin', middleware: ['auth'] }` as never,
|
|
227
|
-
)
|
|
228
|
-
const code = await generateRoutesCode(PAGES)
|
|
229
|
-
expect(code).toContain('meta: { layout: "admin" }')
|
|
230
|
-
expect(code).toContain('beforeEnter')
|
|
231
|
-
expect(code).toContain('"auth"')
|
|
232
|
-
})
|
|
233
|
-
})
|
|
234
|
-
|
|
235
|
-
// ─── 404 page convention ──────────────────────────────────────────────────────
|
|
236
|
-
|
|
237
|
-
describe('generateRoutesCode — 404.ts convention', () => {
|
|
238
|
-
beforeEach(() => {
|
|
239
|
-
vi.mocked(existsSync).mockReturnValue(true)
|
|
240
|
-
vi.mocked(scanDirectory).mockResolvedValue([])
|
|
241
|
-
vi.mocked(readFile).mockResolvedValue('' as never)
|
|
242
|
-
})
|
|
243
|
-
|
|
244
|
-
it('maps 404.ts to path /:all* (catch-all)', async () => {
|
|
245
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/404.ts`])
|
|
246
|
-
const code = await generateRoutesCode(PAGES)
|
|
247
|
-
expect(code).toContain('path: \"/:all*\"')
|
|
248
|
-
expect(code).toContain('"page-404"')
|
|
249
|
-
expect(code).toContain('_cerNotFound: true')
|
|
250
|
-
})
|
|
251
|
-
|
|
252
|
-
it('sorts 404.ts catch-all after all other routes', async () => {
|
|
253
|
-
vi.mocked(scanDirectory).mockResolvedValue([
|
|
254
|
-
`${PAGES}/404.ts`,
|
|
255
|
-
`${PAGES}/about.ts`,
|
|
256
|
-
`${PAGES}/[id].ts`,
|
|
257
|
-
])
|
|
258
|
-
const code = await generateRoutesCode(PAGES)
|
|
259
|
-
const aboutIdx = code.indexOf('\"/about\"')
|
|
260
|
-
const idIdx = code.indexOf('\"/:id\"')
|
|
261
|
-
const allIdx = code.indexOf('\"/:all*\"')
|
|
262
|
-
expect(aboutIdx).toBeLessThan(idIdx)
|
|
263
|
-
expect(idIdx).toBeLessThan(allIdx)
|
|
264
|
-
})
|
|
265
|
-
})
|
|
266
|
-
|
|
267
|
-
// ─── _layout.ts filtering ─────────────────────────────────────────────────────
|
|
268
|
-
|
|
269
|
-
describe('generateRoutesCode — _layout.ts filtering', () => {
|
|
270
|
-
beforeEach(() => {
|
|
271
|
-
vi.mocked(existsSync).mockReturnValue(true)
|
|
272
|
-
vi.mocked(scanDirectory).mockResolvedValue([])
|
|
273
|
-
vi.mocked(readFile).mockResolvedValue('' as never)
|
|
274
|
-
})
|
|
275
|
-
|
|
276
|
-
it('excludes _layout.ts files from the generated routes', async () => {
|
|
277
|
-
vi.mocked(scanDirectory).mockResolvedValue([
|
|
278
|
-
`${PAGES}/admin/index.ts`,
|
|
279
|
-
`${PAGES}/admin/_layout.ts`,
|
|
280
|
-
])
|
|
281
|
-
const code = await generateRoutesCode(PAGES)
|
|
282
|
-
expect(code).not.toContain('_layout')
|
|
283
|
-
expect(code).toContain('/admin')
|
|
284
|
-
})
|
|
285
|
-
|
|
286
|
-
it('generates a route for non-underscore files in the same directory', async () => {
|
|
287
|
-
vi.mocked(scanDirectory).mockResolvedValue([
|
|
288
|
-
`${PAGES}/admin/users.ts`,
|
|
289
|
-
`${PAGES}/admin/_layout.ts`,
|
|
290
|
-
])
|
|
291
|
-
const code = await generateRoutesCode(PAGES)
|
|
292
|
-
expect(code).toContain('/admin/users')
|
|
293
|
-
expect(code).not.toContain('_layout')
|
|
294
|
-
})
|
|
295
|
-
})
|
|
296
|
-
|
|
297
|
-
// ─── meta.revalidate ─────────────────────────────────────────────────────────
|
|
298
|
-
|
|
299
|
-
describe('generateRoutesCode — meta.ssg.revalidate', () => {
|
|
300
|
-
beforeEach(() => {
|
|
301
|
-
vi.mocked(existsSync).mockReturnValue(true)
|
|
302
|
-
vi.mocked(scanDirectory).mockResolvedValue([])
|
|
303
|
-
vi.mocked(readFile).mockResolvedValue('' as never)
|
|
304
|
-
})
|
|
305
|
-
|
|
306
|
-
it('emits meta.ssg.revalidate when declared in page source', async () => {
|
|
307
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/blog.ts`])
|
|
308
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
309
|
-
`export const meta = { ssg: { revalidate: 60 } }` as never,
|
|
310
|
-
)
|
|
311
|
-
const code = await generateRoutesCode(PAGES)
|
|
312
|
-
expect(code).toContain('ssg: { revalidate: 60 }')
|
|
313
|
-
})
|
|
314
|
-
|
|
315
|
-
it('emits larger revalidate values correctly', async () => {
|
|
316
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/products.ts`])
|
|
317
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
318
|
-
`export const meta = { ssg: { revalidate: 3600 } }` as never,
|
|
319
|
-
)
|
|
320
|
-
const code = await generateRoutesCode(PAGES)
|
|
321
|
-
expect(code).toContain('ssg: { revalidate: 3600 }')
|
|
322
|
-
})
|
|
323
|
-
|
|
324
|
-
it('omits ssg meta when no revalidate is declared', async () => {
|
|
325
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
326
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
327
|
-
`component('page-about', () => html\`<h1>About</h1>\`)` as never,
|
|
328
|
-
)
|
|
329
|
-
const code = await generateRoutesCode(PAGES)
|
|
330
|
-
expect(code).not.toContain('revalidate')
|
|
331
|
-
})
|
|
332
|
-
|
|
333
|
-
it('can combine revalidate with layout in meta', async () => {
|
|
334
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/blog.ts`])
|
|
335
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
336
|
-
`export const meta = { layout: 'minimal', ssg: { revalidate: 120 } }` as never,
|
|
337
|
-
)
|
|
338
|
-
const code = await generateRoutesCode(PAGES)
|
|
339
|
-
expect(code).toContain('layout: "minimal"')
|
|
340
|
-
expect(code).toContain('ssg: { revalidate: 120 }')
|
|
341
|
-
})
|
|
342
|
-
})
|
|
343
|
-
|
|
344
|
-
// ─── meta.transition ─────────────────────────────────────────────────────────
|
|
345
|
-
|
|
346
|
-
describe('generateRoutesCode — meta.transition', () => {
|
|
347
|
-
beforeEach(() => {
|
|
348
|
-
vi.mocked(existsSync).mockReturnValue(true)
|
|
349
|
-
vi.mocked(scanDirectory).mockResolvedValue([])
|
|
350
|
-
vi.mocked(readFile).mockResolvedValue('' as never)
|
|
351
|
-
})
|
|
352
|
-
|
|
353
|
-
it('emits meta.transition string when declared', async () => {
|
|
354
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
355
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
356
|
-
`export const meta = { transition: 'fade' }` as never,
|
|
357
|
-
)
|
|
358
|
-
const code = await generateRoutesCode(PAGES)
|
|
359
|
-
expect(code).toContain('transition: "fade"')
|
|
360
|
-
})
|
|
361
|
-
|
|
362
|
-
it('emits meta.transition true (boolean) when declared', async () => {
|
|
363
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
364
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
365
|
-
`export const meta = { transition: true }` as never,
|
|
366
|
-
)
|
|
367
|
-
const code = await generateRoutesCode(PAGES)
|
|
368
|
-
expect(code).toContain('transition: true')
|
|
369
|
-
})
|
|
370
|
-
|
|
371
|
-
it('omits transition meta when not declared', async () => {
|
|
372
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
373
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
374
|
-
`component('page-about', () => html\`<h1>About</h1>\`)` as never,
|
|
375
|
-
)
|
|
376
|
-
const code = await generateRoutesCode(PAGES)
|
|
377
|
-
expect(code).not.toContain('transition')
|
|
378
|
-
})
|
|
379
|
-
|
|
380
|
-
it('emits meta.transition false (boolean) when declared', async () => {
|
|
381
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
382
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
383
|
-
`export const meta = { transition: false }` as never,
|
|
384
|
-
)
|
|
385
|
-
const code = await generateRoutesCode(PAGES)
|
|
386
|
-
expect(code).toContain('transition: false')
|
|
387
|
-
})
|
|
388
|
-
|
|
389
|
-
it('can combine transition with middleware', async () => {
|
|
390
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/dashboard.ts`])
|
|
391
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
392
|
-
`export const meta = { transition: 'slide', middleware: ['auth'] }` as never,
|
|
393
|
-
)
|
|
394
|
-
const code = await generateRoutesCode(PAGES)
|
|
395
|
-
expect(code).toContain('transition: "slide"')
|
|
396
|
-
expect(code).toContain('beforeEnter')
|
|
397
|
-
expect(code).toContain('"auth"')
|
|
398
|
-
})
|
|
399
|
-
})
|
|
400
|
-
|
|
401
|
-
// ─── meta.render ──────────────────────────────────────────────────────────────
|
|
402
|
-
|
|
403
|
-
describe('generateRoutesCode — meta.render', () => {
|
|
404
|
-
beforeEach(() => {
|
|
405
|
-
vi.mocked(existsSync).mockReturnValue(true)
|
|
406
|
-
vi.mocked(scanDirectory).mockResolvedValue([])
|
|
407
|
-
vi.mocked(readFile).mockResolvedValue('' as never)
|
|
408
|
-
})
|
|
409
|
-
|
|
410
|
-
it('emits meta.render "server" when declared', async () => {
|
|
411
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/dashboard.ts`])
|
|
412
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
413
|
-
`export const meta = { render: 'server' }` as never,
|
|
414
|
-
)
|
|
415
|
-
const code = await generateRoutesCode(PAGES)
|
|
416
|
-
expect(code).toContain('render: "server"')
|
|
417
|
-
})
|
|
418
|
-
|
|
419
|
-
it('emits meta.render "spa" when declared', async () => {
|
|
420
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/profile.ts`])
|
|
421
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
422
|
-
`export const meta = { render: 'spa' }` as never,
|
|
423
|
-
)
|
|
424
|
-
const code = await generateRoutesCode(PAGES)
|
|
425
|
-
expect(code).toContain('render: "spa"')
|
|
426
|
-
})
|
|
427
|
-
|
|
428
|
-
it('emits meta.render "static" when declared', async () => {
|
|
429
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
430
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
431
|
-
`export const meta = { render: 'static' }` as never,
|
|
432
|
-
)
|
|
433
|
-
const code = await generateRoutesCode(PAGES)
|
|
434
|
-
expect(code).toContain('render: "static"')
|
|
435
|
-
})
|
|
436
|
-
|
|
437
|
-
it('omits render meta when not declared', async () => {
|
|
438
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
439
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
440
|
-
`component('page-about', () => html\`<h1>About</h1>\`)` as never,
|
|
441
|
-
)
|
|
442
|
-
const code = await generateRoutesCode(PAGES)
|
|
443
|
-
expect(code).not.toContain('render:')
|
|
444
|
-
})
|
|
445
|
-
|
|
446
|
-
it('ignores unknown render values', async () => {
|
|
447
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
448
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
449
|
-
`export const meta = { render: 'unknown' }` as never,
|
|
450
|
-
)
|
|
451
|
-
const code = await generateRoutesCode(PAGES)
|
|
452
|
-
expect(code).not.toContain('render:')
|
|
453
|
-
})
|
|
454
|
-
|
|
455
|
-
it('can combine render with layout', async () => {
|
|
456
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/dashboard.ts`])
|
|
457
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
458
|
-
`export const meta = { render: 'server', layout: 'admin' }` as never,
|
|
459
|
-
)
|
|
460
|
-
const code = await generateRoutesCode(PAGES)
|
|
461
|
-
expect(code).toContain('render: "server"')
|
|
462
|
-
expect(code).toContain('layout: "admin"')
|
|
463
|
-
})
|
|
464
|
-
})
|
|
465
|
-
|
|
466
|
-
// ─── meta.hydrate ─────────────────────────────────────────────────────────────
|
|
467
|
-
|
|
468
|
-
describe('generateRoutesCode — meta.hydrate', () => {
|
|
469
|
-
beforeEach(() => {
|
|
470
|
-
vi.mocked(existsSync).mockReturnValue(true)
|
|
471
|
-
vi.mocked(scanDirectory).mockResolvedValue([])
|
|
472
|
-
vi.mocked(readFile).mockResolvedValue('' as never)
|
|
473
|
-
})
|
|
474
|
-
|
|
475
|
-
it('emits meta.hydrate "idle" when declared', async () => {
|
|
476
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
477
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
478
|
-
`export const meta = { hydrate: 'idle' }` as never,
|
|
479
|
-
)
|
|
480
|
-
const code = await generateRoutesCode(PAGES)
|
|
481
|
-
expect(code).toContain('hydrate: "idle"')
|
|
482
|
-
})
|
|
483
|
-
|
|
484
|
-
it('emits meta.hydrate "visible" when declared', async () => {
|
|
485
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/hero.ts`])
|
|
486
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
487
|
-
`export const meta = { hydrate: 'visible' }` as never,
|
|
488
|
-
)
|
|
489
|
-
const code = await generateRoutesCode(PAGES)
|
|
490
|
-
expect(code).toContain('hydrate: "visible"')
|
|
491
|
-
})
|
|
492
|
-
|
|
493
|
-
it('emits meta.hydrate "none" when declared', async () => {
|
|
494
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/static-page.ts`])
|
|
495
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
496
|
-
`export const meta = { hydrate: 'none' }` as never,
|
|
497
|
-
)
|
|
498
|
-
const code = await generateRoutesCode(PAGES)
|
|
499
|
-
expect(code).toContain('hydrate: "none"')
|
|
500
|
-
})
|
|
501
|
-
|
|
502
|
-
it('does NOT emit meta.hydrate when "load" (default — no-op)', async () => {
|
|
503
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
504
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
505
|
-
`export const meta = { hydrate: 'load' }` as never,
|
|
506
|
-
)
|
|
507
|
-
const code = await generateRoutesCode(PAGES)
|
|
508
|
-
expect(code).not.toContain('hydrate:')
|
|
509
|
-
})
|
|
510
|
-
|
|
511
|
-
it('omits hydrate meta when not declared', async () => {
|
|
512
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
513
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
514
|
-
`component('page-about', () => html\`<h1>About</h1>\`)` as never,
|
|
515
|
-
)
|
|
516
|
-
const code = await generateRoutesCode(PAGES)
|
|
517
|
-
expect(code).not.toContain('hydrate:')
|
|
518
|
-
})
|
|
519
|
-
|
|
520
|
-
it('ignores unknown hydrate values', async () => {
|
|
521
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
522
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
523
|
-
`export const meta = { hydrate: 'lazy' }` as never,
|
|
524
|
-
)
|
|
525
|
-
const code = await generateRoutesCode(PAGES)
|
|
526
|
-
expect(code).not.toContain('hydrate:')
|
|
527
|
-
})
|
|
528
|
-
|
|
529
|
-
it('can combine hydrate with layout and render', async () => {
|
|
530
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/dashboard.ts`])
|
|
531
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
532
|
-
`export const meta = { hydrate: 'idle', layout: 'minimal', render: 'server' }` as never,
|
|
533
|
-
)
|
|
534
|
-
const code = await generateRoutesCode(PAGES)
|
|
535
|
-
expect(code).toContain('hydrate: "idle"')
|
|
536
|
-
expect(code).toContain('layout: "minimal"')
|
|
537
|
-
expect(code).toContain('render: "server"')
|
|
538
|
-
})
|
|
539
|
-
})
|
|
540
|
-
|
|
541
|
-
// ─── meta.title ───────────────────────────────────────────────────────────────
|
|
542
|
-
|
|
543
|
-
describe('generateRoutesCode — meta.title', () => {
|
|
544
|
-
beforeEach(() => {
|
|
545
|
-
vi.mocked(existsSync).mockReturnValue(true)
|
|
546
|
-
vi.mocked(scanDirectory).mockResolvedValue([])
|
|
547
|
-
vi.mocked(readFile).mockResolvedValue('' as never)
|
|
548
|
-
})
|
|
549
|
-
|
|
550
|
-
it('emits meta.title when declared in page source', async () => {
|
|
551
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
552
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
553
|
-
`export const meta = { title: 'About Us' }` as never,
|
|
554
|
-
)
|
|
555
|
-
const code = await generateRoutesCode(PAGES)
|
|
556
|
-
expect(code).toContain('title: "About Us"')
|
|
557
|
-
})
|
|
558
|
-
|
|
559
|
-
it('omits title meta when not declared', async () => {
|
|
560
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
561
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
562
|
-
`component('page-about', () => html\`<h1>About</h1>\`)` as never,
|
|
563
|
-
)
|
|
564
|
-
const code = await generateRoutesCode(PAGES)
|
|
565
|
-
expect(code).not.toContain('title:')
|
|
566
|
-
})
|
|
567
|
-
|
|
568
|
-
it('can combine title with layout', async () => {
|
|
569
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
570
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
571
|
-
`export const meta = { layout: 'minimal', title: 'About Us' }` as never,
|
|
572
|
-
)
|
|
573
|
-
const code = await generateRoutesCode(PAGES)
|
|
574
|
-
expect(code).toContain('layout: "minimal"')
|
|
575
|
-
expect(code).toContain('title: "About Us"')
|
|
576
|
-
})
|
|
577
|
-
})
|
|
578
|
-
|
|
579
|
-
// ─── nested layouts (layoutChain) ────────────────────────────────────────────
|
|
580
|
-
|
|
581
|
-
describe('generateRoutesCode — layoutChain (nested layouts)', () => {
|
|
582
|
-
beforeEach(() => {
|
|
583
|
-
vi.mocked(existsSync).mockReturnValue(true)
|
|
584
|
-
vi.mocked(scanDirectory).mockResolvedValue([])
|
|
585
|
-
vi.mocked(readFile).mockResolvedValue('' as never)
|
|
586
|
-
})
|
|
587
|
-
|
|
588
|
-
it('emits meta.layoutChain when a _layout.ts exists in ancestor directory', async () => {
|
|
589
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/admin/users.ts`])
|
|
590
|
-
vi.mocked(readFile).mockImplementation(async (path: unknown) => {
|
|
591
|
-
if (String(path).endsWith('_layout.ts')) return 'export default \'minimal\''
|
|
592
|
-
return ''
|
|
593
|
-
})
|
|
594
|
-
const code = await generateRoutesCode(PAGES)
|
|
595
|
-
expect(code).toContain('layoutChain:')
|
|
596
|
-
expect(code).toContain('"default"')
|
|
597
|
-
expect(code).toContain('"minimal"')
|
|
598
|
-
})
|
|
599
|
-
|
|
600
|
-
it('uses outerLayout from page meta when layoutChain is emitted', async () => {
|
|
601
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/admin/users.ts`])
|
|
602
|
-
vi.mocked(readFile).mockImplementation(async (path: unknown) => {
|
|
603
|
-
const p = String(path)
|
|
604
|
-
if (p.endsWith('_layout.ts')) return 'export default \'sidebar\''
|
|
605
|
-
// page source has its own layout
|
|
606
|
-
return `export const meta = { layout: 'clean' }`
|
|
607
|
-
})
|
|
608
|
-
const code = await generateRoutesCode(PAGES)
|
|
609
|
-
expect(code).toContain('layoutChain:')
|
|
610
|
-
expect(code).toContain('"clean"')
|
|
611
|
-
expect(code).toContain('"sidebar"')
|
|
612
|
-
// Should not emit a plain meta.layout — the chain replaces it
|
|
613
|
-
expect(code).not.toContain('layout: "clean"')
|
|
614
|
-
})
|
|
615
|
-
|
|
616
|
-
it('omits layoutChain for top-level pages with no _layout.ts', async () => {
|
|
617
|
-
vi.mocked(existsSync).mockImplementation((p: unknown) => {
|
|
618
|
-
// Return false for any _layout.ts path so no chain is resolved
|
|
619
|
-
if (String(p).endsWith('_layout.ts')) return false
|
|
620
|
-
return true
|
|
621
|
-
})
|
|
622
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
623
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
624
|
-
`export const meta = { layout: 'default' }` as never,
|
|
625
|
-
)
|
|
626
|
-
const code = await generateRoutesCode(PAGES)
|
|
627
|
-
expect(code).not.toContain('layoutChain')
|
|
628
|
-
expect(code).toContain('layout: "default"')
|
|
629
|
-
})
|
|
630
|
-
|
|
631
|
-
it('omits layoutChain for top-level pages even when existsSync returns true', async () => {
|
|
632
|
-
// top-level page has no subdirectory segments, so resolveLayoutChain returns null immediately
|
|
633
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/index.ts`])
|
|
634
|
-
vi.mocked(readFile).mockResolvedValue('' as never)
|
|
635
|
-
const code = await generateRoutesCode(PAGES)
|
|
636
|
-
expect(code).not.toContain('layoutChain')
|
|
637
|
-
})
|
|
638
|
-
})
|
|
639
|
-
|
|
640
|
-
// ─── i18n route prefixing ─────────────────────────────────────────────────────
|
|
641
|
-
|
|
642
|
-
describe('generateRoutesCode — i18n route prefixing', () => {
|
|
643
|
-
const i18nPrefix = { locales: ['en', 'fr'], defaultLocale: 'en', strategy: 'prefix' as const }
|
|
644
|
-
const i18nPrefixExcept = { locales: ['en', 'fr'], defaultLocale: 'en', strategy: 'prefix_except_default' as const }
|
|
645
|
-
const i18nNoPrefix = { locales: ['en', 'fr'], defaultLocale: 'en', strategy: 'no_prefix' as const }
|
|
646
|
-
|
|
647
|
-
beforeEach(() => {
|
|
648
|
-
vi.mocked(existsSync).mockReturnValue(true)
|
|
649
|
-
vi.mocked(scanDirectory).mockResolvedValue([])
|
|
650
|
-
vi.mocked(readFile).mockResolvedValue('' as never)
|
|
651
|
-
})
|
|
652
|
-
|
|
653
|
-
it('prefix strategy: generates /en/about and /fr/about for about.ts', async () => {
|
|
654
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
655
|
-
const code = await generateRoutesCode(PAGES, i18nPrefix)
|
|
656
|
-
expect(code).toContain('"/en/about"')
|
|
657
|
-
expect(code).toContain('"/fr/about"')
|
|
658
|
-
expect(code).not.toContain('path: "/about"')
|
|
659
|
-
})
|
|
660
|
-
|
|
661
|
-
it('prefix_except_default strategy: generates /about and /fr/about for about.ts', async () => {
|
|
662
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
663
|
-
const code = await generateRoutesCode(PAGES, i18nPrefixExcept)
|
|
664
|
-
expect(code).toContain('"/about"')
|
|
665
|
-
expect(code).toContain('"/fr/about"')
|
|
666
|
-
expect(code).not.toContain('"/en/about"')
|
|
667
|
-
})
|
|
668
|
-
|
|
669
|
-
it('no_prefix strategy: does not add locale prefixes', async () => {
|
|
670
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
671
|
-
const code = await generateRoutesCode(PAGES, i18nNoPrefix)
|
|
672
|
-
expect(code).toContain('"/about"')
|
|
673
|
-
expect(code).not.toContain('"/en/about"')
|
|
674
|
-
expect(code).not.toContain('"/fr/about"')
|
|
675
|
-
})
|
|
676
|
-
|
|
677
|
-
it('catch-all routes are never prefixed', async () => {
|
|
678
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/404.ts`])
|
|
679
|
-
const code = await generateRoutesCode(PAGES, i18nPrefix)
|
|
680
|
-
expect(code).toContain('"/:all*"')
|
|
681
|
-
expect(code).not.toContain('"/en/:all*"')
|
|
682
|
-
expect(code).not.toContain('"/fr/:all*"')
|
|
683
|
-
})
|
|
684
|
-
|
|
685
|
-
it('emits meta.locale for each generated locale route', async () => {
|
|
686
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
687
|
-
const code = await generateRoutesCode(PAGES, i18nPrefix)
|
|
688
|
-
expect(code).toContain('locale: "en"')
|
|
689
|
-
expect(code).toContain('locale: "fr"')
|
|
690
|
-
})
|
|
691
|
-
|
|
692
|
-
it('without i18n config, generates a single route as before', async () => {
|
|
693
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
694
|
-
const code = await generateRoutesCode(PAGES)
|
|
695
|
-
expect(code).toContain('"/about"')
|
|
696
|
-
expect(code).not.toContain('"/en/about"')
|
|
697
|
-
expect(code).not.toContain('locale:')
|
|
698
|
-
})
|
|
699
|
-
})
|
|
700
|
-
|
|
701
|
-
// ─── P1-1: Synthetic 404 catch-all ───────────────────────────────────────────
|
|
702
|
-
|
|
703
|
-
describe('generateRoutesCode — synthetic 404 catch-all (P1-1)', () => {
|
|
704
|
-
beforeEach(() => {
|
|
705
|
-
vi.mocked(existsSync).mockReturnValue(true)
|
|
706
|
-
vi.mocked(scanDirectory).mockResolvedValue([])
|
|
707
|
-
vi.mocked(readFile).mockResolvedValue('' as never)
|
|
708
|
-
})
|
|
709
|
-
|
|
710
|
-
it('adds a synthetic /:all* route when no user-defined catch-all exists', async () => {
|
|
711
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/about.ts`])
|
|
712
|
-
const code = await generateRoutesCode(PAGES)
|
|
713
|
-
// Synthetic route uses single quotes in the generated code
|
|
714
|
-
expect(code).toContain("path: '/:all*'")
|
|
715
|
-
// Synthetic route returns default: null
|
|
716
|
-
expect(code).toContain('default: null')
|
|
717
|
-
expect(code).toContain('meta: { _cerNotFound: true }')
|
|
718
|
-
})
|
|
719
|
-
|
|
720
|
-
it('does NOT add a synthetic catch-all when a user 404.ts already exists', async () => {
|
|
721
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/404.ts`, `${PAGES}/about.ts`])
|
|
722
|
-
const code = await generateRoutesCode(PAGES)
|
|
723
|
-
// Should have exactly one /:all* (the real 404 page, with double quotes)
|
|
724
|
-
expect(code).toContain('"/:all*"')
|
|
725
|
-
expect(code).not.toContain('default: null')
|
|
726
|
-
})
|
|
727
|
-
|
|
728
|
-
it('does NOT add a synthetic catch-all when a user [...all].ts already exists', async () => {
|
|
729
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/[...all].ts`])
|
|
730
|
-
const code = await generateRoutesCode(PAGES)
|
|
731
|
-
// Real catch-all uses double quotes
|
|
732
|
-
expect(code).toContain('"/:all*"')
|
|
733
|
-
expect(code).not.toContain('default: null')
|
|
734
|
-
expect(code).not.toContain('_cerNotFound: true')
|
|
735
|
-
})
|
|
736
|
-
|
|
737
|
-
it('does not add a synthetic catch-all for an empty pages directory (no routes)', async () => {
|
|
738
|
-
const code = await generateRoutesCode(PAGES)
|
|
739
|
-
// Empty routes array — no synthetic catch-all needed when there are no routes
|
|
740
|
-
expect(code).toContain('const routes = []')
|
|
741
|
-
expect(code).not.toContain("path: '/:all*'")
|
|
742
|
-
})
|
|
743
|
-
})
|
|
744
|
-
|
|
745
|
-
// ─── hydration middleware replay skip ───────────────────────────────────────
|
|
746
|
-
|
|
747
|
-
describe('generateRoutesCode — hydration middleware replay skip', () => {
|
|
748
|
-
beforeEach(() => {
|
|
749
|
-
vi.mocked(existsSync).mockReturnValue(true)
|
|
750
|
-
vi.mocked(scanDirectory).mockResolvedValue([])
|
|
751
|
-
vi.mocked(readFile).mockResolvedValue('' as never)
|
|
752
|
-
})
|
|
753
|
-
|
|
754
|
-
it('emits a one-shot hydration replay skip before importing middleware', async () => {
|
|
755
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/dashboard.ts`])
|
|
756
|
-
vi.mocked(readFile).mockResolvedValue(
|
|
757
|
-
`export const meta = { middleware: ['auth'] }` as never,
|
|
758
|
-
)
|
|
759
|
-
const code = await generateRoutesCode(PAGES)
|
|
760
|
-
expect(code).toContain(`const _hydrationEntry = (globalThis).__CER_HYDRATION_ENTRY__`)
|
|
761
|
-
expect(code).toContain(`from.path === to.path`)
|
|
762
|
-
expect(code).toContain(`_hydrationEntry.path === to.path`)
|
|
763
|
-
expect(code).toContain(`delete (globalThis).__CER_HYDRATION_ENTRY__`)
|
|
764
|
-
const hydrationIdx = code.indexOf(`const _hydrationEntry = (globalThis).__CER_HYDRATION_ENTRY__`)
|
|
765
|
-
const importIdx = code.indexOf(`const { middleware } = await import('virtual:cer-middleware')`)
|
|
766
|
-
expect(hydrationIdx).toBeGreaterThanOrEqual(0)
|
|
767
|
-
expect(importIdx).toBeGreaterThan(hydrationIdx)
|
|
768
|
-
})
|
|
769
|
-
})
|
|
770
|
-
|
|
771
|
-
// ─── P2-1: _layout.ts group meta inheritance ─────────────────────────────────
|
|
772
|
-
|
|
773
|
-
describe('generateRoutesCode — group meta from _layout.ts (P2-1)', () => {
|
|
774
|
-
beforeEach(() => {
|
|
775
|
-
vi.mocked(existsSync).mockReturnValue(true)
|
|
776
|
-
vi.mocked(scanDirectory).mockResolvedValue([])
|
|
777
|
-
vi.mocked(readFile).mockResolvedValue('' as never)
|
|
778
|
-
})
|
|
779
|
-
|
|
780
|
-
it('inherits middleware from ancestor _layout.ts when page has none', async () => {
|
|
781
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/admin/dashboard.ts`])
|
|
782
|
-
vi.mocked(readFile).mockImplementation(async (path: unknown) => {
|
|
783
|
-
if (String(path).endsWith('_layout.ts')) {
|
|
784
|
-
return `export const meta = { middleware: ['auth'] }`
|
|
785
|
-
}
|
|
786
|
-
return ''
|
|
787
|
-
})
|
|
788
|
-
const code = await generateRoutesCode(PAGES)
|
|
789
|
-
expect(code).toContain('beforeEnter')
|
|
790
|
-
expect(code).toContain('"auth"')
|
|
791
|
-
})
|
|
792
|
-
|
|
793
|
-
it('page-level middleware overrides group middleware from _layout.ts', async () => {
|
|
794
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/admin/profile.ts`])
|
|
795
|
-
vi.mocked(readFile).mockImplementation(async (path: unknown) => {
|
|
796
|
-
if (String(path).endsWith('_layout.ts')) {
|
|
797
|
-
return `export const meta = { middleware: ['auth'] }`
|
|
798
|
-
}
|
|
799
|
-
// page declares its own middleware
|
|
800
|
-
return `export const meta = { middleware: ['verified'] }`
|
|
801
|
-
})
|
|
802
|
-
const code = await generateRoutesCode(PAGES)
|
|
803
|
-
expect(code).toContain('"verified"')
|
|
804
|
-
// Page overrides group — group middleware should NOT appear
|
|
805
|
-
expect(code).not.toContain('"auth"')
|
|
806
|
-
})
|
|
807
|
-
|
|
808
|
-
it('inherits layout from ancestor _layout.ts meta.layout when page has no explicit layout', async () => {
|
|
809
|
-
vi.mocked(scanDirectory).mockResolvedValue([`${PAGES}/docs/intro.ts`])
|
|
810
|
-
vi.mocked(readFile).mockImplementation(async (path: unknown) => {
|
|
811
|
-
if (String(path).endsWith('_layout.ts')) {
|
|
812
|
-
// _layout.ts exports meta.layout (group default layout)
|
|
813
|
-
return `export const meta = { layout: 'docs' }`
|
|
814
|
-
}
|
|
815
|
-
return ''
|
|
816
|
-
})
|
|
817
|
-
const code = await generateRoutesCode(PAGES)
|
|
818
|
-
// The layout from group meta should appear in the route
|
|
819
|
-
expect(code).toContain('"docs"')
|
|
820
|
-
})
|
|
821
|
-
})
|
|
822
|
-
|
|
823
|
-
// ─── P2-2: Per-route error tags ───────────────────────────────────────────────
|
|
824
|
-
|
|
825
|
-
describe('generateRoutesCode — per-route error tags (P2-2)', () => {
|
|
826
|
-
beforeEach(() => {
|
|
827
|
-
vi.mocked(existsSync).mockReturnValue(true)
|
|
828
|
-
vi.mocked(scanDirectory).mockResolvedValue([])
|
|
829
|
-
vi.mocked(readFile).mockResolvedValue('' as never)
|
|
830
|
-
})
|
|
831
|
-
|
|
832
|
-
it('excludes *.error.ts files from generating their own routes', async () => {
|
|
833
|
-
vi.mocked(scanDirectory).mockResolvedValue([
|
|
834
|
-
`${PAGES}/about.ts`,
|
|
835
|
-
`${PAGES}/about.error.ts`,
|
|
836
|
-
])
|
|
837
|
-
const code = await generateRoutesCode(PAGES)
|
|
838
|
-
// The about route should exist
|
|
839
|
-
expect(code).toContain('"/about"')
|
|
840
|
-
// The error file must NOT produce a separate route path like /about-error or /about.error
|
|
841
|
-
expect(code).not.toContain('path: "/about-error"')
|
|
842
|
-
expect(code).not.toContain('path: "/about.error"')
|
|
843
|
-
})
|
|
844
|
-
|
|
845
|
-
it('excludes _error.ts files from generated routes', async () => {
|
|
846
|
-
vi.mocked(scanDirectory).mockResolvedValue([
|
|
847
|
-
`${PAGES}/admin/users.ts`,
|
|
848
|
-
`${PAGES}/admin/_error.ts`,
|
|
849
|
-
])
|
|
850
|
-
const code = await generateRoutesCode(PAGES)
|
|
851
|
-
expect(code).toContain('/admin/users')
|
|
852
|
-
expect(code).not.toContain('_error')
|
|
853
|
-
})
|
|
854
|
-
|
|
855
|
-
it('load() includes co-located error file import when present', async () => {
|
|
856
|
-
vi.mocked(scanDirectory).mockResolvedValue([
|
|
857
|
-
`${PAGES}/dashboard.ts`,
|
|
858
|
-
`${PAGES}/dashboard.error.ts`,
|
|
859
|
-
])
|
|
860
|
-
vi.mocked(existsSync).mockImplementation((p: unknown) => {
|
|
861
|
-
const path = String(p)
|
|
862
|
-
// The co-located error file exists
|
|
863
|
-
if (path.endsWith('dashboard.error.ts')) return true
|
|
864
|
-
return true
|
|
865
|
-
})
|
|
866
|
-
const code = await generateRoutesCode(PAGES)
|
|
867
|
-
// load() should import the error component alongside the page
|
|
868
|
-
expect(code).toContain('dashboard.error.ts')
|
|
869
|
-
expect(code).toContain('Promise.all')
|
|
870
|
-
})
|
|
871
|
-
|
|
872
|
-
it('emits meta.errorTag when co-located *.error.ts is present', async () => {
|
|
873
|
-
vi.mocked(scanDirectory).mockResolvedValue([
|
|
874
|
-
`${PAGES}/settings.ts`,
|
|
875
|
-
`${PAGES}/settings.error.ts`,
|
|
876
|
-
])
|
|
877
|
-
vi.mocked(existsSync).mockImplementation((p: unknown) => {
|
|
878
|
-
if (String(p).endsWith('settings.error.ts')) return true
|
|
879
|
-
return true
|
|
880
|
-
})
|
|
881
|
-
const code = await generateRoutesCode(PAGES)
|
|
882
|
-
expect(code).toContain('errorTag')
|
|
883
|
-
})
|
|
884
|
-
})
|