@jasonshimmy/vite-plugin-cer-app 0.1.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/.github/copilot-instructions.md +130 -0
- package/.github/workflows/publish.yml +206 -0
- package/.nvmrc +1 -0
- package/CHANGELOG.md +10 -0
- package/IMPLEMENTATION_PLAN.md +391 -0
- package/README.md +231 -0
- package/VITE_PLUGIN_FRAMEWORK_PLAN.md +594 -0
- package/commits.txt +3 -0
- package/dist/__tests__/plugin/path-utils.test.d.ts +2 -0
- package/dist/__tests__/plugin/path-utils.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/path-utils.test.js +305 -0
- package/dist/__tests__/plugin/path-utils.test.js.map +1 -0
- package/dist/__tests__/plugin/scanner.test.d.ts +2 -0
- package/dist/__tests__/plugin/scanner.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/scanner.test.js +143 -0
- package/dist/__tests__/plugin/scanner.test.js.map +1 -0
- package/dist/__tests__/plugin/transforms/auto-import.test.d.ts +2 -0
- package/dist/__tests__/plugin/transforms/auto-import.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/transforms/auto-import.test.js +151 -0
- package/dist/__tests__/plugin/transforms/auto-import.test.js.map +1 -0
- package/dist/__tests__/plugin/transforms/head-inject.test.d.ts +2 -0
- package/dist/__tests__/plugin/transforms/head-inject.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/transforms/head-inject.test.js +151 -0
- package/dist/__tests__/plugin/transforms/head-inject.test.js.map +1 -0
- package/dist/__tests__/plugin/virtual/components.test.d.ts +2 -0
- package/dist/__tests__/plugin/virtual/components.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/virtual/components.test.js +47 -0
- package/dist/__tests__/plugin/virtual/components.test.js.map +1 -0
- package/dist/__tests__/plugin/virtual/composables.test.d.ts +2 -0
- package/dist/__tests__/plugin/virtual/composables.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/virtual/composables.test.js +48 -0
- package/dist/__tests__/plugin/virtual/composables.test.js.map +1 -0
- package/dist/__tests__/plugin/virtual/layouts.test.d.ts +2 -0
- package/dist/__tests__/plugin/virtual/layouts.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/virtual/layouts.test.js +59 -0
- package/dist/__tests__/plugin/virtual/layouts.test.js.map +1 -0
- package/dist/__tests__/plugin/virtual/middleware.test.d.ts +2 -0
- package/dist/__tests__/plugin/virtual/middleware.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/virtual/middleware.test.js +58 -0
- package/dist/__tests__/plugin/virtual/middleware.test.js.map +1 -0
- package/dist/__tests__/plugin/virtual/plugins.test.d.ts +2 -0
- package/dist/__tests__/plugin/virtual/plugins.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/virtual/plugins.test.js +73 -0
- package/dist/__tests__/plugin/virtual/plugins.test.js.map +1 -0
- package/dist/__tests__/plugin/virtual/routes.test.d.ts +2 -0
- package/dist/__tests__/plugin/virtual/routes.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/virtual/routes.test.js +167 -0
- package/dist/__tests__/plugin/virtual/routes.test.js.map +1 -0
- package/dist/__tests__/plugin/virtual/server-api.test.d.ts +2 -0
- package/dist/__tests__/plugin/virtual/server-api.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/virtual/server-api.test.js +72 -0
- package/dist/__tests__/plugin/virtual/server-api.test.js.map +1 -0
- package/dist/__tests__/runtime/use-head.test.d.ts +2 -0
- package/dist/__tests__/runtime/use-head.test.d.ts.map +1 -0
- package/dist/__tests__/runtime/use-head.test.js +202 -0
- package/dist/__tests__/runtime/use-head.test.js.map +1 -0
- package/dist/__tests__/runtime/use-page-data.test.d.ts +2 -0
- package/dist/__tests__/runtime/use-page-data.test.d.ts.map +1 -0
- package/dist/__tests__/runtime/use-page-data.test.js +41 -0
- package/dist/__tests__/runtime/use-page-data.test.js.map +1 -0
- package/dist/cli/commands/build.d.ts +3 -0
- package/dist/cli/commands/build.d.ts.map +1 -0
- package/dist/cli/commands/build.js +103 -0
- package/dist/cli/commands/build.js.map +1 -0
- package/dist/cli/commands/dev.d.ts +3 -0
- package/dist/cli/commands/dev.d.ts.map +1 -0
- package/dist/cli/commands/dev.js +92 -0
- package/dist/cli/commands/dev.js.map +1 -0
- package/dist/cli/commands/generate.d.ts +7 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/generate.js +72 -0
- package/dist/cli/commands/generate.js.map +1 -0
- package/dist/cli/commands/preview.d.ts +3 -0
- package/dist/cli/commands/preview.d.ts.map +1 -0
- package/dist/cli/commands/preview.js +191 -0
- package/dist/cli/commands/preview.js.map +1 -0
- package/dist/cli/create/index.d.ts +3 -0
- package/dist/cli/create/index.d.ts.map +1 -0
- package/dist/cli/create/index.js +184 -0
- package/dist/cli/create/index.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +17 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin/build-ssg.d.ts +12 -0
- package/dist/plugin/build-ssg.d.ts.map +1 -0
- package/dist/plugin/build-ssg.js +212 -0
- package/dist/plugin/build-ssg.js.map +1 -0
- package/dist/plugin/build-ssr.d.ts +10 -0
- package/dist/plugin/build-ssr.d.ts.map +1 -0
- package/dist/plugin/build-ssr.js +139 -0
- package/dist/plugin/build-ssr.js.map +1 -0
- package/dist/plugin/dev-server.d.ts +46 -0
- package/dist/plugin/dev-server.d.ts.map +1 -0
- package/dist/plugin/dev-server.js +194 -0
- package/dist/plugin/dev-server.js.map +1 -0
- package/dist/plugin/dts-generator.d.ts +27 -0
- package/dist/plugin/dts-generator.d.ts.map +1 -0
- package/dist/plugin/dts-generator.js +180 -0
- package/dist/plugin/dts-generator.js.map +1 -0
- package/dist/plugin/index.d.ts +13 -0
- package/dist/plugin/index.d.ts.map +1 -0
- package/dist/plugin/index.js +298 -0
- package/dist/plugin/index.js.map +1 -0
- package/dist/plugin/path-utils.d.ts +57 -0
- package/dist/plugin/path-utils.d.ts.map +1 -0
- package/dist/plugin/path-utils.js +160 -0
- package/dist/plugin/path-utils.js.map +1 -0
- package/dist/plugin/scanner.d.ts +17 -0
- package/dist/plugin/scanner.d.ts.map +1 -0
- package/dist/plugin/scanner.js +54 -0
- package/dist/plugin/scanner.js.map +1 -0
- package/dist/plugin/transforms/auto-import.d.ts +14 -0
- package/dist/plugin/transforms/auto-import.d.ts.map +1 -0
- package/dist/plugin/transforms/auto-import.js +154 -0
- package/dist/plugin/transforms/auto-import.js.map +1 -0
- package/dist/plugin/transforms/head-inject.d.ts +29 -0
- package/dist/plugin/transforms/head-inject.d.ts.map +1 -0
- package/dist/plugin/transforms/head-inject.js +127 -0
- package/dist/plugin/transforms/head-inject.js.map +1 -0
- package/dist/plugin/virtual/components.d.ts +6 -0
- package/dist/plugin/virtual/components.d.ts.map +1 -0
- package/dist/plugin/virtual/components.js +22 -0
- package/dist/plugin/virtual/components.js.map +1 -0
- package/dist/plugin/virtual/composables.d.ts +6 -0
- package/dist/plugin/virtual/composables.d.ts.map +1 -0
- package/dist/plugin/virtual/composables.js +22 -0
- package/dist/plugin/virtual/composables.js.map +1 -0
- package/dist/plugin/virtual/error.d.ts +13 -0
- package/dist/plugin/virtual/error.d.ts.map +1 -0
- package/dist/plugin/virtual/error.js +32 -0
- package/dist/plugin/virtual/error.js.map +1 -0
- package/dist/plugin/virtual/layouts.d.ts +6 -0
- package/dist/plugin/virtual/layouts.d.ts.map +1 -0
- package/dist/plugin/virtual/layouts.js +33 -0
- package/dist/plugin/virtual/layouts.js.map +1 -0
- package/dist/plugin/virtual/loading.d.ts +11 -0
- package/dist/plugin/virtual/loading.d.ts.map +1 -0
- package/dist/plugin/virtual/loading.js +30 -0
- package/dist/plugin/virtual/loading.js.map +1 -0
- package/dist/plugin/virtual/middleware.d.ts +6 -0
- package/dist/plugin/virtual/middleware.d.ts.map +1 -0
- package/dist/plugin/virtual/middleware.js +36 -0
- package/dist/plugin/virtual/middleware.js.map +1 -0
- package/dist/plugin/virtual/plugins.d.ts +6 -0
- package/dist/plugin/virtual/plugins.d.ts.map +1 -0
- package/dist/plugin/virtual/plugins.js +30 -0
- package/dist/plugin/virtual/plugins.js.map +1 -0
- package/dist/plugin/virtual/routes.d.ts +16 -0
- package/dist/plugin/virtual/routes.d.ts.map +1 -0
- package/dist/plugin/virtual/routes.js +131 -0
- package/dist/plugin/virtual/routes.js.map +1 -0
- package/dist/plugin/virtual/server-api.d.ts +6 -0
- package/dist/plugin/virtual/server-api.d.ts.map +1 -0
- package/dist/plugin/virtual/server-api.js +41 -0
- package/dist/plugin/virtual/server-api.js.map +1 -0
- package/dist/plugin/virtual/server-middleware.d.ts +6 -0
- package/dist/plugin/virtual/server-middleware.d.ts.map +1 -0
- package/dist/plugin/virtual/server-middleware.js +36 -0
- package/dist/plugin/virtual/server-middleware.js.map +1 -0
- package/dist/runtime/app-template.d.ts +10 -0
- package/dist/runtime/app-template.d.ts.map +1 -0
- package/dist/runtime/app-template.js +143 -0
- package/dist/runtime/app-template.js.map +1 -0
- package/dist/runtime/composables/index.d.ts +4 -0
- package/dist/runtime/composables/index.d.ts.map +1 -0
- package/dist/runtime/composables/index.js +3 -0
- package/dist/runtime/composables/index.js.map +1 -0
- package/dist/runtime/composables/use-head.d.ts +30 -0
- package/dist/runtime/composables/use-head.d.ts.map +1 -0
- package/dist/runtime/composables/use-head.js +182 -0
- package/dist/runtime/composables/use-head.js.map +1 -0
- package/dist/runtime/composables/use-page-data.d.ts +32 -0
- package/dist/runtime/composables/use-page-data.d.ts.map +1 -0
- package/dist/runtime/composables/use-page-data.js +41 -0
- package/dist/runtime/composables/use-page-data.js.map +1 -0
- package/dist/runtime/entry-client-template.d.ts +8 -0
- package/dist/runtime/entry-client-template.d.ts.map +1 -0
- package/dist/runtime/entry-client-template.js +18 -0
- package/dist/runtime/entry-client-template.js.map +1 -0
- package/dist/runtime/entry-server-template.d.ts +9 -0
- package/dist/runtime/entry-server-template.d.ts.map +1 -0
- package/dist/runtime/entry-server-template.js +72 -0
- package/dist/runtime/entry-server-template.js.map +1 -0
- package/dist/types/api.d.ts +16 -0
- package/dist/types/api.d.ts.map +1 -0
- package/dist/types/api.js +2 -0
- package/dist/types/api.js.map +1 -0
- package/dist/types/config.d.ts +32 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +4 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/middleware.d.ts +6 -0
- package/dist/types/middleware.d.ts.map +1 -0
- package/dist/types/middleware.js +2 -0
- package/dist/types/middleware.js.map +1 -0
- package/dist/types/page.d.ts +21 -0
- package/dist/types/page.d.ts.map +1 -0
- package/dist/types/page.js +2 -0
- package/dist/types/page.js.map +1 -0
- package/dist/types/plugin.d.ts +12 -0
- package/dist/types/plugin.d.ts.map +1 -0
- package/dist/types/plugin.js +2 -0
- package/dist/types/plugin.js.map +1 -0
- package/docs/cli.md +233 -0
- package/docs/components.md +114 -0
- package/docs/composables.md +99 -0
- package/docs/configuration.md +270 -0
- package/docs/data-loading.md +165 -0
- package/docs/getting-started.md +235 -0
- package/docs/head-management.md +206 -0
- package/docs/layouts.md +112 -0
- package/docs/middleware.md +140 -0
- package/docs/plugins.md +138 -0
- package/docs/rendering-modes.md +251 -0
- package/docs/routing.md +180 -0
- package/docs/server-api.md +172 -0
- package/docs/testing.md +462 -0
- package/package.json +75 -0
- package/src/__tests__/plugin/path-utils.test.ts +399 -0
- package/src/__tests__/plugin/scanner.test.ts +172 -0
- package/src/__tests__/plugin/transforms/auto-import.test.ts +229 -0
- package/src/__tests__/plugin/transforms/head-inject.test.ts +178 -0
- package/src/__tests__/plugin/virtual/components.test.ts +56 -0
- package/src/__tests__/plugin/virtual/composables.test.ts +57 -0
- package/src/__tests__/plugin/virtual/layouts.test.ts +70 -0
- package/src/__tests__/plugin/virtual/middleware.test.ts +68 -0
- package/src/__tests__/plugin/virtual/plugins.test.ts +84 -0
- package/src/__tests__/plugin/virtual/routes.test.ts +202 -0
- package/src/__tests__/plugin/virtual/server-api.test.ts +85 -0
- package/src/__tests__/runtime/use-head.test.ts +243 -0
- package/src/__tests__/runtime/use-page-data.test.ts +45 -0
- package/src/cli/commands/build.ts +114 -0
- package/src/cli/commands/dev.ts +101 -0
- package/src/cli/commands/generate.ts +81 -0
- package/src/cli/commands/preview.ts +218 -0
- package/src/cli/create/index.ts +250 -0
- package/src/cli/create/templates/spa/app/app.ts.tpl +74 -0
- package/src/cli/create/templates/spa/app/layouts/default.ts.tpl +15 -0
- package/src/cli/create/templates/spa/app/pages/index.ts.tpl +8 -0
- package/src/cli/create/templates/spa/cer.config.ts.tpl +6 -0
- package/src/cli/create/templates/spa/index.html.tpl +12 -0
- package/src/cli/create/templates/spa/package.json.tpl +18 -0
- package/src/cli/create/templates/ssg/app/app.ts.tpl +74 -0
- package/src/cli/create/templates/ssg/app/layouts/default.ts.tpl +15 -0
- package/src/cli/create/templates/ssg/app/pages/index.ts.tpl +17 -0
- package/src/cli/create/templates/ssg/cer.config.ts.tpl +13 -0
- package/src/cli/create/templates/ssg/index.html.tpl +12 -0
- package/src/cli/create/templates/ssg/package.json.tpl +19 -0
- package/src/cli/create/templates/ssr/app/app.ts.tpl +74 -0
- package/src/cli/create/templates/ssr/app/layouts/default.ts.tpl +15 -0
- package/src/cli/create/templates/ssr/app/pages/index.ts.tpl +8 -0
- package/src/cli/create/templates/ssr/cer.config.ts.tpl +10 -0
- package/src/cli/create/templates/ssr/index.html.tpl +12 -0
- package/src/cli/create/templates/ssr/package.json.tpl +18 -0
- package/src/cli/index.ts +20 -0
- package/src/index.ts +13 -0
- package/src/plugin/build-ssg.ts +259 -0
- package/src/plugin/build-ssr.ts +147 -0
- package/src/plugin/dev-server.ts +266 -0
- package/src/plugin/dts-generator.ts +214 -0
- package/src/plugin/index.ts +330 -0
- package/src/plugin/path-utils.ts +186 -0
- package/src/plugin/scanner.ts +65 -0
- package/src/plugin/transforms/auto-import.ts +190 -0
- package/src/plugin/transforms/head-inject.ts +161 -0
- package/src/plugin/virtual/components.ts +28 -0
- package/src/plugin/virtual/composables.ts +28 -0
- package/src/plugin/virtual/error.ts +34 -0
- package/src/plugin/virtual/layouts.ts +41 -0
- package/src/plugin/virtual/loading.ts +33 -0
- package/src/plugin/virtual/middleware.ts +45 -0
- package/src/plugin/virtual/plugins.ts +36 -0
- package/src/plugin/virtual/routes.ts +147 -0
- package/src/plugin/virtual/server-api.ts +52 -0
- package/src/plugin/virtual/server-middleware.ts +44 -0
- package/src/runtime/app-template.ts +142 -0
- package/src/runtime/composables/index.ts +3 -0
- package/src/runtime/composables/use-head.ts +204 -0
- package/src/runtime/composables/use-page-data.ts +39 -0
- package/src/runtime/entry-client-template.ts +17 -0
- package/src/runtime/entry-server-template.ts +71 -0
- package/src/types/api.ts +19 -0
- package/src/types/config.ts +39 -0
- package/src/types/index.ts +6 -0
- package/src/types/middleware.ts +16 -0
- package/src/types/page.ts +29 -0
- package/src/types/plugin.ts +13 -0
- package/tsconfig.build.json +10 -0
- package/tsconfig.json +19 -0
- package/vitest.config.ts +29 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import type { ViteDevServer } from 'vite'
|
|
2
|
+
import type { IncomingMessage, ServerResponse } from 'node:http'
|
|
3
|
+
import { resolve } from 'pathe'
|
|
4
|
+
|
|
5
|
+
export interface ResolvedCerConfig {
|
|
6
|
+
mode: 'spa' | 'ssr' | 'ssg'
|
|
7
|
+
srcDir: string
|
|
8
|
+
root: string
|
|
9
|
+
pagesDir: string
|
|
10
|
+
layoutsDir: string
|
|
11
|
+
componentsDir: string
|
|
12
|
+
composablesDir: string
|
|
13
|
+
pluginsDir: string
|
|
14
|
+
middlewareDir: string
|
|
15
|
+
serverApiDir: string
|
|
16
|
+
serverMiddlewareDir: string
|
|
17
|
+
port: number
|
|
18
|
+
ssr: { dsd: boolean; streaming: boolean }
|
|
19
|
+
ssg: { routes: 'auto' | string[]; concurrency: number; fallback: boolean }
|
|
20
|
+
router: { base?: string; scrollToFragment?: boolean | object }
|
|
21
|
+
jitCss: { content: string[]; extendedColors: boolean }
|
|
22
|
+
autoImports: { components: boolean; composables: boolean; directives: boolean; runtime: boolean }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Reads the raw body from an IncomingMessage as a Buffer.
|
|
27
|
+
*/
|
|
28
|
+
function readBody(req: IncomingMessage): Promise<Buffer> {
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
const chunks: Buffer[] = []
|
|
31
|
+
req.on('data', (chunk: Buffer) => chunks.push(chunk))
|
|
32
|
+
req.on('end', () => resolve(Buffer.concat(chunks)))
|
|
33
|
+
req.on('error', reject)
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Parses the request body for JSON content types.
|
|
39
|
+
* Attaches the parsed body (or raw buffer) to `(req as any).body`.
|
|
40
|
+
*/
|
|
41
|
+
async function parseBody(req: IncomingMessage): Promise<unknown> {
|
|
42
|
+
const contentType = req.headers['content-type'] ?? ''
|
|
43
|
+
const method = req.method?.toUpperCase() ?? 'GET'
|
|
44
|
+
|
|
45
|
+
if (!['POST', 'PUT', 'PATCH'].includes(method)) {
|
|
46
|
+
return undefined
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const buf = await readBody(req)
|
|
50
|
+
|
|
51
|
+
if (contentType.includes('application/json')) {
|
|
52
|
+
try {
|
|
53
|
+
return JSON.parse(buf.toString('utf-8'))
|
|
54
|
+
} catch {
|
|
55
|
+
return undefined
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return buf
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Extracts route params from a path pattern.
|
|
64
|
+
* e.g. pattern="/api/users/:id", path="/api/users/42" -> { id: "42" }
|
|
65
|
+
*/
|
|
66
|
+
function matchApiPath(
|
|
67
|
+
pattern: string,
|
|
68
|
+
urlPath: string,
|
|
69
|
+
): Record<string, string> | null {
|
|
70
|
+
const patternParts = pattern.split('/')
|
|
71
|
+
const urlParts = urlPath.split('?')[0].split('/')
|
|
72
|
+
|
|
73
|
+
if (patternParts.length !== urlParts.length) return null
|
|
74
|
+
|
|
75
|
+
const params: Record<string, string> = {}
|
|
76
|
+
for (let i = 0; i < patternParts.length; i++) {
|
|
77
|
+
const p = patternParts[i]
|
|
78
|
+
const u = urlParts[i]
|
|
79
|
+
if (p.startsWith(':')) {
|
|
80
|
+
params[p.slice(1)] = decodeURIComponent(u)
|
|
81
|
+
} else if (p !== u) {
|
|
82
|
+
return null
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return params
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Parses URL query string into a plain object.
|
|
91
|
+
*/
|
|
92
|
+
function parseQuery(url: string): Record<string, string> {
|
|
93
|
+
const qIndex = url.indexOf('?')
|
|
94
|
+
if (qIndex === -1) return {}
|
|
95
|
+
const qs = url.slice(qIndex + 1)
|
|
96
|
+
const result: Record<string, string> = {}
|
|
97
|
+
for (const part of qs.split('&')) {
|
|
98
|
+
if (!part) continue
|
|
99
|
+
const eqIdx = part.indexOf('=')
|
|
100
|
+
if (eqIdx === -1) {
|
|
101
|
+
result[decodeURIComponent(part)] = ''
|
|
102
|
+
} else {
|
|
103
|
+
result[decodeURIComponent(part.slice(0, eqIdx))] = decodeURIComponent(
|
|
104
|
+
part.slice(eqIdx + 1),
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return result
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Configures the Vite dev server with:
|
|
113
|
+
* 1. API route handlers from server/api/
|
|
114
|
+
* 2. Server middleware from server/middleware/
|
|
115
|
+
* 3. SSR HTML rendering (when mode is 'ssr')
|
|
116
|
+
*/
|
|
117
|
+
export function configureCerDevServer(
|
|
118
|
+
server: ViteDevServer,
|
|
119
|
+
config: ResolvedCerConfig,
|
|
120
|
+
): void {
|
|
121
|
+
server.middlewares.use(async (req: IncomingMessage, res: ServerResponse, next: () => void) => {
|
|
122
|
+
const url = req.url ?? '/'
|
|
123
|
+
const method = req.method?.toUpperCase() ?? 'GET'
|
|
124
|
+
|
|
125
|
+
// 1. Server middleware from server/middleware/ runs first (CORS, auth, logging, etc.)
|
|
126
|
+
try {
|
|
127
|
+
const smMod = await server.ssrLoadModule('virtual:cer-server-middleware')
|
|
128
|
+
const serverMiddleware = (smMod.serverMiddleware ?? smMod.default ?? []) as Array<{
|
|
129
|
+
name: string
|
|
130
|
+
handler: (req: IncomingMessage, res: ServerResponse, next: () => void) => void | Promise<void>
|
|
131
|
+
}>
|
|
132
|
+
|
|
133
|
+
for (const { handler } of serverMiddleware) {
|
|
134
|
+
if (typeof handler === 'function') {
|
|
135
|
+
let calledNext = false
|
|
136
|
+
await handler(req, res, () => {
|
|
137
|
+
calledNext = true
|
|
138
|
+
})
|
|
139
|
+
if (!calledNext) return
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
} catch {
|
|
143
|
+
// middleware module not ready — continue
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// 2. API route handlers from server/api/
|
|
147
|
+
try {
|
|
148
|
+
const mod = await server.ssrLoadModule('virtual:cer-server-api')
|
|
149
|
+
const apiRoutes: Array<{ path: string; handlers: Record<string, unknown> }> =
|
|
150
|
+
mod.apiRoutes ?? mod.default?.apiRoutes ?? []
|
|
151
|
+
|
|
152
|
+
for (const route of apiRoutes) {
|
|
153
|
+
const params = matchApiPath(route.path, url.split('?')[0])
|
|
154
|
+
if (params === null) continue
|
|
155
|
+
|
|
156
|
+
const query = parseQuery(url)
|
|
157
|
+
const body = await parseBody(req)
|
|
158
|
+
|
|
159
|
+
// Augment request
|
|
160
|
+
const augmentedReq = req as IncomingMessage & {
|
|
161
|
+
params: Record<string, string>
|
|
162
|
+
query: Record<string, string>
|
|
163
|
+
body: unknown
|
|
164
|
+
}
|
|
165
|
+
augmentedReq.params = params
|
|
166
|
+
augmentedReq.query = query
|
|
167
|
+
augmentedReq.body = body
|
|
168
|
+
|
|
169
|
+
// Augment response with json() and status() helpers
|
|
170
|
+
const augmentedRes = res as ServerResponse & {
|
|
171
|
+
json(data: unknown): void
|
|
172
|
+
status(code: number): typeof augmentedRes
|
|
173
|
+
_statusCode?: number
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
augmentedRes.json = function (data: unknown) {
|
|
177
|
+
const json = JSON.stringify(data)
|
|
178
|
+
this.setHeader('Content-Type', 'application/json')
|
|
179
|
+
this.end(json)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
augmentedRes.status = function (code: number) {
|
|
183
|
+
this.statusCode = code
|
|
184
|
+
return this
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Try to find a handler for the HTTP method. Exports may be GET/POST (uppercase)
|
|
188
|
+
// or get/post (lowercase); try both plus a 'default' fallback.
|
|
189
|
+
const handlerKey = method.toLowerCase()
|
|
190
|
+
const handler =
|
|
191
|
+
(route.handlers[handlerKey] as Function | undefined) ??
|
|
192
|
+
(route.handlers[method.toUpperCase()] as Function | undefined) ??
|
|
193
|
+
(route.handlers['default'] as Function | undefined)
|
|
194
|
+
|
|
195
|
+
if (typeof handler === 'function') {
|
|
196
|
+
try {
|
|
197
|
+
await handler(augmentedReq, augmentedRes)
|
|
198
|
+
} catch (err) {
|
|
199
|
+
server.ssrFixStacktrace(err as Error)
|
|
200
|
+
console.error(`[cer-app] API handler error at ${route.path}:`, err)
|
|
201
|
+
res.statusCode = 500
|
|
202
|
+
res.setHeader('Content-Type', 'application/json')
|
|
203
|
+
res.end(JSON.stringify({ error: 'Internal Server Error' }))
|
|
204
|
+
}
|
|
205
|
+
return
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
} catch {
|
|
209
|
+
// virtual:cer-server-api not yet ready or empty — continue
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// 3. SSR mode: intercept HTML requests
|
|
213
|
+
if (config.mode === 'ssr') {
|
|
214
|
+
const acceptsHtml =
|
|
215
|
+
(req.headers['accept'] ?? '').includes('text/html') ||
|
|
216
|
+
url === '/' ||
|
|
217
|
+
(!url.includes('.') && !url.startsWith('/api/'))
|
|
218
|
+
|
|
219
|
+
if (acceptsHtml) {
|
|
220
|
+
try {
|
|
221
|
+
// Load the SSR entry module
|
|
222
|
+
const ssrEntry = await server.ssrLoadModule(
|
|
223
|
+
resolve(config.srcDir, 'entry-server.ts'),
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
const handler =
|
|
227
|
+
ssrEntry.handler ?? ssrEntry.default?.handler
|
|
228
|
+
|
|
229
|
+
if (typeof handler === 'function') {
|
|
230
|
+
await handler(req, res)
|
|
231
|
+
return
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Fallback: render using template + ssrEntry render function
|
|
235
|
+
const renderFn = ssrEntry.render ?? ssrEntry.default?.render
|
|
236
|
+
|
|
237
|
+
if (typeof renderFn === 'function') {
|
|
238
|
+
const template = await server.transformIndexHtml(
|
|
239
|
+
url,
|
|
240
|
+
`<!DOCTYPE html><html><head></head><body><div id="app"></div></body></html>`,
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
const result = await renderFn({ url, req })
|
|
244
|
+
const rendered = template.replace(
|
|
245
|
+
'<div id="app"></div>',
|
|
246
|
+
`<div id="app">${result.html ?? ''}</div>`,
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
res.setHeader('Content-Type', 'text/html; charset=utf-8')
|
|
250
|
+
res.end(rendered)
|
|
251
|
+
return
|
|
252
|
+
}
|
|
253
|
+
} catch (err) {
|
|
254
|
+
server.ssrFixStacktrace(err as Error)
|
|
255
|
+
console.error('[cer-app] SSR render error:', err)
|
|
256
|
+
res.statusCode = 500
|
|
257
|
+
res.setHeader('Content-Type', 'text/plain')
|
|
258
|
+
res.end('SSR Error: ' + String(err))
|
|
259
|
+
return
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
next()
|
|
265
|
+
})
|
|
266
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { writeFileSync } from 'node:fs'
|
|
2
|
+
import { readFileSync, existsSync } from 'node:fs'
|
|
3
|
+
import { join, relative } from 'pathe'
|
|
4
|
+
import { scanDirectory } from './scanner.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Writes `cer-tsconfig.json` to the project root containing path aliases for
|
|
8
|
+
* the `~/` prefix. Users extend it from their `tsconfig.json`:
|
|
9
|
+
* { "extends": "./cer-tsconfig.json" }
|
|
10
|
+
*/
|
|
11
|
+
export function writeTsconfigPaths(root: string, srcDir: string): void {
|
|
12
|
+
const rel = './' + relative(root, srcDir).replace(/\\/g, '/')
|
|
13
|
+
const paths: Record<string, string[]> = {
|
|
14
|
+
'~/*': [`${rel}/*`],
|
|
15
|
+
'~/pages/*': [`${rel}/pages/*`],
|
|
16
|
+
'~/layouts/*': [`${rel}/layouts/*`],
|
|
17
|
+
'~/components/*': [`${rel}/components/*`],
|
|
18
|
+
'~/composables/*': [`${rel}/composables/*`],
|
|
19
|
+
'~/plugins/*': [`${rel}/plugins/*`],
|
|
20
|
+
'~/middleware/*': [`${rel}/middleware/*`],
|
|
21
|
+
'~/assets/*': [`${rel}/assets/*`],
|
|
22
|
+
}
|
|
23
|
+
const content = JSON.stringify({ compilerOptions: { paths } }, null, 2) + '\n'
|
|
24
|
+
writeFileSync(join(root, 'cer-tsconfig.json'), content, 'utf-8')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const RUNTIME_GLOBALS = [
|
|
28
|
+
'component',
|
|
29
|
+
'html',
|
|
30
|
+
'css',
|
|
31
|
+
'ref',
|
|
32
|
+
'computed',
|
|
33
|
+
'watch',
|
|
34
|
+
'watchEffect',
|
|
35
|
+
'useProps',
|
|
36
|
+
'useEmit',
|
|
37
|
+
'useOnConnected',
|
|
38
|
+
'useOnDisconnected',
|
|
39
|
+
'useOnAttributeChanged',
|
|
40
|
+
'useOnError',
|
|
41
|
+
'useStyle',
|
|
42
|
+
'useDesignTokens',
|
|
43
|
+
'useGlobalStyle',
|
|
44
|
+
'useExpose',
|
|
45
|
+
'useSlots',
|
|
46
|
+
'provide',
|
|
47
|
+
'inject',
|
|
48
|
+
'createComposable',
|
|
49
|
+
'nextTick',
|
|
50
|
+
'defineModel',
|
|
51
|
+
'getCurrentComponentContext',
|
|
52
|
+
'isReactiveState',
|
|
53
|
+
'unsafeHTML',
|
|
54
|
+
'decodeEntities',
|
|
55
|
+
'useTeleport',
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
const DIRECTIVE_GLOBALS = ['when', 'each', 'match', 'anchorBlock']
|
|
59
|
+
|
|
60
|
+
const FRAMEWORK_GLOBALS = ['useHead', 'usePageData']
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Scans a composables directory and returns a map of export name → file path.
|
|
64
|
+
* Uses a simple regex to find exported identifiers.
|
|
65
|
+
*/
|
|
66
|
+
export async function scanComposableExports(composablesDir: string): Promise<Map<string, string>> {
|
|
67
|
+
const result = new Map<string, string>()
|
|
68
|
+
if (!existsSync(composablesDir)) return result
|
|
69
|
+
|
|
70
|
+
const files = await scanDirectory('**/*.ts', composablesDir)
|
|
71
|
+
const exportPattern = /export\s+(?:async\s+)?(?:function|const|let|var|class)\s+(\w+)/g
|
|
72
|
+
|
|
73
|
+
for (const file of files) {
|
|
74
|
+
const code = readFileSync(file, 'utf-8')
|
|
75
|
+
exportPattern.lastIndex = 0
|
|
76
|
+
let match
|
|
77
|
+
while ((match = exportPattern.exec(code)) !== null) {
|
|
78
|
+
result.set(match[1], file)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return result
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Generates the content of `cer-auto-imports.d.ts`.
|
|
87
|
+
* @param root - project root (where the .d.ts file will be written)
|
|
88
|
+
*/
|
|
89
|
+
export async function generateAutoImportDts(
|
|
90
|
+
root: string,
|
|
91
|
+
composablesDir: string,
|
|
92
|
+
composableExports?: Map<string, string>,
|
|
93
|
+
): Promise<string> {
|
|
94
|
+
const exports = composableExports ?? await scanComposableExports(composablesDir)
|
|
95
|
+
|
|
96
|
+
const lines: string[] = [
|
|
97
|
+
'// AUTO-GENERATED by vite-plugin-cer-app',
|
|
98
|
+
'// Do not edit — regenerated automatically on dev server start and build.',
|
|
99
|
+
'',
|
|
100
|
+
'export {}',
|
|
101
|
+
'',
|
|
102
|
+
'declare global {',
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
for (const name of RUNTIME_GLOBALS) {
|
|
106
|
+
lines.push(` const ${name}: typeof import('@jasonshimmy/custom-elements-runtime')['${name}']`)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
lines.push('')
|
|
110
|
+
|
|
111
|
+
for (const name of DIRECTIVE_GLOBALS) {
|
|
112
|
+
lines.push(` const ${name}: typeof import('@jasonshimmy/custom-elements-runtime/directives')['${name}']`)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
lines.push('')
|
|
116
|
+
|
|
117
|
+
for (const name of FRAMEWORK_GLOBALS) {
|
|
118
|
+
lines.push(` const ${name}: typeof import('vite-plugin-cer-app/composables')['${name}']`)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (exports.size > 0) {
|
|
122
|
+
lines.push('')
|
|
123
|
+
for (const [name, filePath] of exports) {
|
|
124
|
+
// Use a path relative to the project root so the .d.ts is portable
|
|
125
|
+
const rel = './' + relative(root, filePath).replace(/\.ts$/, '')
|
|
126
|
+
lines.push(` const ${name}: typeof import('${rel}')['${name}']`)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
lines.push('')
|
|
131
|
+
lines.push(' // SSR loader data injected as window.__CER_DATA__ by the server.')
|
|
132
|
+
lines.push(' // Consumed once by usePageData() during client hydration.')
|
|
133
|
+
lines.push(' // eslint-disable-next-line @typescript-eslint/no-explicit-any')
|
|
134
|
+
lines.push(' var __CER_DATA__: any')
|
|
135
|
+
lines.push('}')
|
|
136
|
+
lines.push('')
|
|
137
|
+
|
|
138
|
+
return lines.join('\n')
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Generates the content of `cer-env.d.ts` — a script (non-module) file
|
|
143
|
+
* containing ambient `declare module` declarations for all virtual modules.
|
|
144
|
+
* Must NOT contain `export {}` or any top-level imports/exports.
|
|
145
|
+
*/
|
|
146
|
+
export async function generateVirtualModuleDts(
|
|
147
|
+
root: string,
|
|
148
|
+
composablesDir: string,
|
|
149
|
+
composableExports?: Map<string, string>,
|
|
150
|
+
): Promise<string> {
|
|
151
|
+
const exports = composableExports ?? await scanComposableExports(composablesDir)
|
|
152
|
+
|
|
153
|
+
const lines: string[] = [
|
|
154
|
+
'// AUTO-GENERATED by vite-plugin-cer-app',
|
|
155
|
+
'// Do not edit — regenerated automatically on dev server start and build.',
|
|
156
|
+
'',
|
|
157
|
+
`declare module 'virtual:cer-routes' {`,
|
|
158
|
+
` import type { Route } from '@jasonshimmy/custom-elements-runtime/router'`,
|
|
159
|
+
` const routes: Route[]`,
|
|
160
|
+
` export default routes`,
|
|
161
|
+
`}`,
|
|
162
|
+
'',
|
|
163
|
+
`declare module 'virtual:cer-layouts' {}`,
|
|
164
|
+
`declare module 'virtual:cer-components' {}`,
|
|
165
|
+
'',
|
|
166
|
+
`declare module 'virtual:cer-plugins' {`,
|
|
167
|
+
` import type { AppPlugin } from 'vite-plugin-cer-app/types'`,
|
|
168
|
+
` const plugins: AppPlugin[]`,
|
|
169
|
+
` export default plugins`,
|
|
170
|
+
`}`,
|
|
171
|
+
'',
|
|
172
|
+
`declare module 'virtual:cer-composables' {`,
|
|
173
|
+
]
|
|
174
|
+
|
|
175
|
+
for (const [name, filePath] of exports) {
|
|
176
|
+
const rel = './' + relative(root, filePath).replace(/\.ts$/, '')
|
|
177
|
+
lines.push(` export { ${name} } from '${rel}'`)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
lines.push(`}`)
|
|
181
|
+
lines.push('')
|
|
182
|
+
lines.push(`declare module 'virtual:cer-middleware' {`)
|
|
183
|
+
lines.push(` const middleware: Record<string, Function>`)
|
|
184
|
+
lines.push(` export { middleware }`)
|
|
185
|
+
lines.push(`}`)
|
|
186
|
+
lines.push('')
|
|
187
|
+
lines.push(`declare module 'virtual:cer-loading' {`)
|
|
188
|
+
lines.push(` export const hasLoading: boolean`)
|
|
189
|
+
lines.push(` export const loadingTag: string | null`)
|
|
190
|
+
lines.push(`}`)
|
|
191
|
+
lines.push('')
|
|
192
|
+
lines.push(`declare module 'virtual:cer-error' {`)
|
|
193
|
+
lines.push(` export const hasError: boolean`)
|
|
194
|
+
lines.push(` export const errorTag: string | null`)
|
|
195
|
+
lines.push(`}`)
|
|
196
|
+
lines.push('')
|
|
197
|
+
|
|
198
|
+
return lines.join('\n')
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Writes both `cer-auto-imports.d.ts` and `cer-env.d.ts` to the project root.
|
|
203
|
+
*/
|
|
204
|
+
export async function writeAutoImportDts(
|
|
205
|
+
root: string,
|
|
206
|
+
composablesDir: string,
|
|
207
|
+
composableExports?: Map<string, string>,
|
|
208
|
+
): Promise<void> {
|
|
209
|
+
const scanned = composableExports ?? await scanComposableExports(composablesDir)
|
|
210
|
+
const autoImportsContent = await generateAutoImportDts(root, composablesDir, scanned)
|
|
211
|
+
const envContent = await generateVirtualModuleDts(root, composablesDir, scanned)
|
|
212
|
+
writeFileSync(join(root, 'cer-auto-imports.d.ts'), autoImportsContent, 'utf-8')
|
|
213
|
+
writeFileSync(join(root, 'cer-env.d.ts'), envContent, 'utf-8')
|
|
214
|
+
}
|