@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,250 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* create-cer-app scaffold CLI.
|
|
4
|
+
* Usage: create-cer-app [project-name] [--mode spa|ssr|ssg]
|
|
5
|
+
*/
|
|
6
|
+
import { Command } from 'commander'
|
|
7
|
+
import { resolve, join, dirname, basename } from 'pathe'
|
|
8
|
+
import { existsSync } from 'node:fs'
|
|
9
|
+
import { mkdir, readFile, writeFile, readdir, stat } from 'node:fs/promises'
|
|
10
|
+
import { createInterface } from 'node:readline'
|
|
11
|
+
import { fileURLToPath } from 'node:url'
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
14
|
+
const __dirname = dirname(__filename)
|
|
15
|
+
|
|
16
|
+
type AppMode = 'spa' | 'ssr' | 'ssg'
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Prompts the user for input on stdin.
|
|
20
|
+
*/
|
|
21
|
+
function prompt(question: string, defaultValue?: string): Promise<string> {
|
|
22
|
+
return new Promise((resolve) => {
|
|
23
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout })
|
|
24
|
+
const displayQuestion = defaultValue
|
|
25
|
+
? `${question} (${defaultValue}): `
|
|
26
|
+
: `${question}: `
|
|
27
|
+
|
|
28
|
+
rl.question(displayQuestion, (answer) => {
|
|
29
|
+
rl.close()
|
|
30
|
+
resolve(answer.trim() || defaultValue || '')
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Prompts the user to choose a mode from a list.
|
|
37
|
+
*/
|
|
38
|
+
async function promptMode(): Promise<AppMode> {
|
|
39
|
+
console.log('Select app mode:')
|
|
40
|
+
console.log(' 1. spa — Single-Page App (client-side rendering)')
|
|
41
|
+
console.log(' 2. ssr — Server-Side Rendering')
|
|
42
|
+
console.log(' 3. ssg — Static Site Generation')
|
|
43
|
+
|
|
44
|
+
const answer = await prompt('Mode [1/2/3]', '1')
|
|
45
|
+
|
|
46
|
+
const modeMap: Record<string, AppMode> = {
|
|
47
|
+
'1': 'spa',
|
|
48
|
+
'2': 'ssr',
|
|
49
|
+
'3': 'ssg',
|
|
50
|
+
spa: 'spa',
|
|
51
|
+
ssr: 'ssr',
|
|
52
|
+
ssg: 'ssg',
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return modeMap[answer] ?? 'spa'
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Recursively reads all files from the template directory.
|
|
60
|
+
*/
|
|
61
|
+
async function readTemplateFiles(dir: string): Promise<Map<string, string>> {
|
|
62
|
+
const files = new Map<string, string>()
|
|
63
|
+
|
|
64
|
+
async function walk(currentDir: string, prefix: string): Promise<void> {
|
|
65
|
+
const entries = await readdir(currentDir)
|
|
66
|
+
for (const entry of entries) {
|
|
67
|
+
const fullPath = join(currentDir, entry)
|
|
68
|
+
const relativePath = prefix ? `${prefix}/${entry}` : entry
|
|
69
|
+
const info = await stat(fullPath)
|
|
70
|
+
|
|
71
|
+
if (info.isDirectory()) {
|
|
72
|
+
await walk(fullPath, relativePath)
|
|
73
|
+
} else {
|
|
74
|
+
const content = await readFile(fullPath, 'utf-8')
|
|
75
|
+
// Strip .tpl extension from key
|
|
76
|
+
const key = relativePath.endsWith('.tpl') ? relativePath.slice(0, -4) : relativePath
|
|
77
|
+
files.set(key, content)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
await walk(dir, '')
|
|
83
|
+
return files
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Replaces template tokens in a content string.
|
|
88
|
+
*/
|
|
89
|
+
function applyTokens(content: string, tokens: Record<string, string>): string {
|
|
90
|
+
let result = content
|
|
91
|
+
for (const [key, value] of Object.entries(tokens)) {
|
|
92
|
+
result = result.replaceAll(`{{${key}}}`, value)
|
|
93
|
+
}
|
|
94
|
+
return result
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Writes template files to the target directory.
|
|
99
|
+
*/
|
|
100
|
+
async function writeTemplateFiles(
|
|
101
|
+
files: Map<string, string>,
|
|
102
|
+
targetDir: string,
|
|
103
|
+
tokens: Record<string, string>,
|
|
104
|
+
): Promise<void> {
|
|
105
|
+
for (const [relativePath, rawContent] of files) {
|
|
106
|
+
const content = applyTokens(rawContent, tokens)
|
|
107
|
+
const outputPath = join(targetDir, relativePath)
|
|
108
|
+
await mkdir(dirname(outputPath), { recursive: true })
|
|
109
|
+
await writeFile(outputPath, content, 'utf-8')
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Returns the path to the template directory for the given mode.
|
|
115
|
+
* Resolves relative to the compiled dist output.
|
|
116
|
+
*/
|
|
117
|
+
function getTemplateDir(mode: AppMode): string {
|
|
118
|
+
// When running from compiled dist/, templates are in create/templates/
|
|
119
|
+
// This file is at dist/cli/create/index.js, so templates are at dist/cli/create/templates/
|
|
120
|
+
return join(__dirname, 'templates', mode)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function main(): Promise<void> {
|
|
124
|
+
const program = new Command()
|
|
125
|
+
|
|
126
|
+
program
|
|
127
|
+
.name('create-cer-app')
|
|
128
|
+
.description('Scaffold a new CER App project')
|
|
129
|
+
.argument('[project-name]', 'Name of the project to create')
|
|
130
|
+
.option('--mode <mode>', 'App mode: spa, ssr, or ssg')
|
|
131
|
+
.option('--dir <dir>', 'Directory to create the project in (defaults to project name)')
|
|
132
|
+
.action(async (projectNameArg?: string, options?: { mode?: string; dir?: string }) => {
|
|
133
|
+
console.log('\nWelcome to create-cer-app!\n')
|
|
134
|
+
|
|
135
|
+
// Gather inputs
|
|
136
|
+
const projectName = projectNameArg ?? (await prompt('Project name', 'my-cer-app'))
|
|
137
|
+
const mode: AppMode = (options?.mode as AppMode | undefined) ?? (await promptMode())
|
|
138
|
+
const targetDir = resolve(options?.dir ?? projectName)
|
|
139
|
+
|
|
140
|
+
console.log(`\nCreating ${mode.toUpperCase()} project: ${projectName}`)
|
|
141
|
+
console.log(` Directory: ${targetDir}\n`)
|
|
142
|
+
|
|
143
|
+
if (existsSync(targetDir)) {
|
|
144
|
+
const overwrite = await prompt(`Directory "${targetDir}" already exists. Overwrite? [y/N]`, 'N')
|
|
145
|
+
if (!overwrite.toLowerCase().startsWith('y')) {
|
|
146
|
+
console.log('Aborted.')
|
|
147
|
+
process.exit(0)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Load template files
|
|
152
|
+
const templateDir = getTemplateDir(mode)
|
|
153
|
+
|
|
154
|
+
if (!existsSync(templateDir)) {
|
|
155
|
+
// Fallback: generate minimal template inline
|
|
156
|
+
console.warn(`[create-cer-app] Template directory not found at ${templateDir}, using inline template.`)
|
|
157
|
+
await generateInlineTemplate(targetDir, projectName, mode)
|
|
158
|
+
} else {
|
|
159
|
+
const files = await readTemplateFiles(templateDir)
|
|
160
|
+
await writeTemplateFiles(files, targetDir, { projectName })
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
console.log(`\nProject created! To get started:\n`)
|
|
164
|
+
console.log(` cd ${basename(targetDir)}`)
|
|
165
|
+
console.log(` npm install`)
|
|
166
|
+
console.log(` npm run dev\n`)
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
await program.parseAsync(process.argv)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Generates a minimal project structure inline when templates aren't available.
|
|
174
|
+
*/
|
|
175
|
+
async function generateInlineTemplate(
|
|
176
|
+
targetDir: string,
|
|
177
|
+
projectName: string,
|
|
178
|
+
mode: AppMode,
|
|
179
|
+
): Promise<void> {
|
|
180
|
+
await mkdir(join(targetDir, 'app/pages'), { recursive: true })
|
|
181
|
+
await mkdir(join(targetDir, 'app/layouts'), { recursive: true })
|
|
182
|
+
|
|
183
|
+
// package.json
|
|
184
|
+
await writeFile(
|
|
185
|
+
join(targetDir, 'package.json'),
|
|
186
|
+
JSON.stringify(
|
|
187
|
+
{
|
|
188
|
+
name: projectName,
|
|
189
|
+
version: '0.1.0',
|
|
190
|
+
type: 'module',
|
|
191
|
+
scripts: {
|
|
192
|
+
dev: 'cer-app dev',
|
|
193
|
+
build: 'cer-app build',
|
|
194
|
+
preview: 'cer-app preview',
|
|
195
|
+
},
|
|
196
|
+
dependencies: {
|
|
197
|
+
'@jasonshimmy/custom-elements-runtime': '^3.1.1',
|
|
198
|
+
},
|
|
199
|
+
devDependencies: {
|
|
200
|
+
vite: '^5.0.0',
|
|
201
|
+
'vite-plugin-cer-app': '^0.1.0',
|
|
202
|
+
typescript: '^5.4.0',
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
null,
|
|
206
|
+
2,
|
|
207
|
+
),
|
|
208
|
+
'utf-8',
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
// cer.config.ts
|
|
212
|
+
await writeFile(
|
|
213
|
+
join(targetDir, 'cer.config.ts'),
|
|
214
|
+
`import { defineConfig } from 'vite-plugin-cer-app'\n\nexport default defineConfig({\n mode: '${mode}',\n autoImports: { components: true, composables: true, directives: true, runtime: true },\n})\n`,
|
|
215
|
+
'utf-8',
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
// app/app.ts
|
|
219
|
+
await writeFile(
|
|
220
|
+
join(targetDir, 'app/app.ts'),
|
|
221
|
+
`import '@jasonshimmy/custom-elements-runtime/css'\nimport 'virtual:cer-components'\nimport routes from 'virtual:cer-routes'\nimport layouts from 'virtual:cer-layouts'\nimport plugins from 'virtual:cer-plugins'\nimport { hasLoading, loadingTag } from 'virtual:cer-loading'\nimport { hasError, errorTag } from 'virtual:cer-error'\nimport { component, ref, useOnConnected, useOnDisconnected, registerBuiltinComponents } from '@jasonshimmy/custom-elements-runtime'\nimport { initRouter } from '@jasonshimmy/custom-elements-runtime/router'\nimport { enableJITCSS } from '@jasonshimmy/custom-elements-runtime/jit-css'\nimport { createDOMJITCSS } from '@jasonshimmy/custom-elements-runtime/dom-jit-css'\n\nregisterBuiltinComponents()\nenableJITCSS()\n\nconst router = initRouter({ routes })\n\nconst isNavigating = ref(false)\nconst currentError = ref(null)\n;(globalThis as any).resetError = () => { currentError.value = null; router.replace(router.getCurrent().path) }\nconst _push = router.push.bind(router)\nconst _replace = router.replace.bind(router)\nrouter.push = async (path) => { isNavigating.value = true; currentError.value = null; try { await _push(path) } catch (err) { currentError.value = err instanceof Error ? err.message : String(err) } finally { isNavigating.value = false } }\nrouter.replace = async (path) => { isNavigating.value = true; currentError.value = null; try { await _replace(path) } catch (err) { currentError.value = err instanceof Error ? err.message : String(err) } finally { isNavigating.value = false } }\n\ncomponent('cer-layout-view', () => {\n const current = ref(router.getCurrent())\n let unsub: (() => void) | undefined\n useOnConnected(() => { unsub = router.subscribe((s: typeof current.value) => { current.value = s }) })\n useOnDisconnected(() => { unsub?.(); unsub = undefined })\n if (currentError.value !== null) {\n if (hasError && errorTag) return { tag: errorTag, props: { attrs: { error: String(currentError.value) } }, children: [] }\n return { tag: 'div', props: { attrs: { style: 'padding:2rem;font-family:monospace' } }, children: [String(currentError.value)] }\n }\n if (isNavigating.value && hasLoading && loadingTag) return { tag: loadingTag, props: {}, children: [] }\n const matched = router.matchRoute(current.value.path)\n const layoutName = (matched?.route as any)?.meta?.layout ?? 'default'\n const layoutTag = (layouts as Record<string, string>)[layoutName]\n const routerView = { tag: 'router-view', props: {}, children: [] }\n return layoutTag ? { tag: layoutTag, props: {}, children: [routerView] } : routerView\n})\n\nfor (const plugin of plugins ?? []) {\n if (plugin && typeof plugin.setup === 'function') {\n await plugin.setup({ router, provide: (key, value) => { (globalThis as any)[key] = value }, config: {} })\n }\n}\n\nif (typeof window !== 'undefined') {\n await router.replace(window.location.pathname + window.location.search + window.location.hash)\n createDOMJITCSS().mount()\n}\n\nexport { router }\n`,
|
|
222
|
+
'utf-8',
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
// app/pages/index.ts
|
|
226
|
+
await writeFile(
|
|
227
|
+
join(targetDir, 'app/pages/index.ts'),
|
|
228
|
+
`component('page-index', () => {\n return html\`\n <div>\n <h1>Welcome to ${projectName}</h1>\n <p>Edit <code>app/pages/index.ts</code> to get started.</p>\n </div>\n \`\n})\n`,
|
|
229
|
+
'utf-8',
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
// app/layouts/default.ts
|
|
233
|
+
await writeFile(
|
|
234
|
+
join(targetDir, 'app/layouts/default.ts'),
|
|
235
|
+
`component('layout-default', () => {\n return html\`\n <header><nav><router-link to="/">Home</router-link></nav></header>\n <main><slot></slot></main>\n <footer><p>Built with CER App</p></footer>\n \`\n})\n`,
|
|
236
|
+
'utf-8',
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
// index.html
|
|
240
|
+
await writeFile(
|
|
241
|
+
join(targetDir, 'index.html'),
|
|
242
|
+
`<!DOCTYPE html>\n<html lang="en">\n <head>\n <meta charset="UTF-8">\n <meta name="viewport" content="width=device-width, initial-scale=1.0">\n <title>${projectName}</title>\n </head>\n <body>\n <cer-layout-view></cer-layout-view>\n <script type="module" src="/app/app.ts"></script>\n </body>\n</html>\n`,
|
|
243
|
+
'utf-8',
|
|
244
|
+
)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
main().catch((err) => {
|
|
248
|
+
console.error('[create-cer-app] Fatal error:', err)
|
|
249
|
+
process.exit(1)
|
|
250
|
+
})
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import '@jasonshimmy/custom-elements-runtime/css'
|
|
2
|
+
import 'virtual:cer-components'
|
|
3
|
+
import routes from 'virtual:cer-routes'
|
|
4
|
+
import layouts from 'virtual:cer-layouts'
|
|
5
|
+
import plugins from 'virtual:cer-plugins'
|
|
6
|
+
import { hasLoading, loadingTag } from 'virtual:cer-loading'
|
|
7
|
+
import { hasError, errorTag } from 'virtual:cer-error'
|
|
8
|
+
import {
|
|
9
|
+
component,
|
|
10
|
+
ref,
|
|
11
|
+
useOnConnected,
|
|
12
|
+
useOnDisconnected,
|
|
13
|
+
registerBuiltinComponents,
|
|
14
|
+
} from '@jasonshimmy/custom-elements-runtime'
|
|
15
|
+
import { initRouter } from '@jasonshimmy/custom-elements-runtime/router'
|
|
16
|
+
import { enableJITCSS } from '@jasonshimmy/custom-elements-runtime/jit-css'
|
|
17
|
+
import { createDOMJITCSS } from '@jasonshimmy/custom-elements-runtime/dom-jit-css'
|
|
18
|
+
|
|
19
|
+
registerBuiltinComponents()
|
|
20
|
+
enableJITCSS()
|
|
21
|
+
|
|
22
|
+
const router = initRouter({ routes })
|
|
23
|
+
|
|
24
|
+
const isNavigating = ref(false)
|
|
25
|
+
const currentError = ref(null)
|
|
26
|
+
;(globalThis as any).resetError = () => {
|
|
27
|
+
currentError.value = null
|
|
28
|
+
router.replace(router.getCurrent().path)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const _push = router.push.bind(router)
|
|
32
|
+
const _replace = router.replace.bind(router)
|
|
33
|
+
router.push = async (path) => {
|
|
34
|
+
isNavigating.value = true
|
|
35
|
+
currentError.value = null
|
|
36
|
+
try { await _push(path) } catch (err) { currentError.value = err instanceof Error ? err.message : String(err) } finally { isNavigating.value = false }
|
|
37
|
+
}
|
|
38
|
+
router.replace = async (path) => {
|
|
39
|
+
isNavigating.value = true
|
|
40
|
+
currentError.value = null
|
|
41
|
+
try { await _replace(path) } catch (err) { currentError.value = err instanceof Error ? err.message : String(err) } finally { isNavigating.value = false }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
component('cer-layout-view', () => {
|
|
45
|
+
const current = ref(router.getCurrent())
|
|
46
|
+
let unsub: (() => void) | undefined
|
|
47
|
+
useOnConnected(() => { unsub = router.subscribe((s: typeof current.value) => { current.value = s }) })
|
|
48
|
+
useOnDisconnected(() => { unsub?.(); unsub = undefined })
|
|
49
|
+
|
|
50
|
+
if (currentError.value !== null) {
|
|
51
|
+
if (hasError && errorTag) return { tag: errorTag, props: { attrs: { error: String(currentError.value) } }, children: [] }
|
|
52
|
+
return { tag: 'div', props: { attrs: { style: 'padding:2rem;font-family:monospace' } }, children: [String(currentError.value)] }
|
|
53
|
+
}
|
|
54
|
+
if (isNavigating.value && hasLoading && loadingTag) return { tag: loadingTag, props: {}, children: [] }
|
|
55
|
+
|
|
56
|
+
const matched = router.matchRoute(current.value.path)
|
|
57
|
+
const layoutName = (matched?.route as any)?.meta?.layout ?? 'default'
|
|
58
|
+
const layoutTag = (layouts as Record<string, string>)[layoutName]
|
|
59
|
+
const routerView = { tag: 'router-view', props: {}, children: [] }
|
|
60
|
+
return layoutTag ? { tag: layoutTag, props: {}, children: [routerView] } : routerView
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
for (const plugin of plugins) {
|
|
64
|
+
if (plugin && typeof plugin.setup === 'function') {
|
|
65
|
+
await plugin.setup({ router, provide: (key, value) => { (globalThis as any)[key] = value }, config: {} })
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (typeof window !== 'undefined') {
|
|
70
|
+
await router.replace(window.location.pathname + window.location.search + window.location.hash)
|
|
71
|
+
createDOMJITCSS().mount()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export { router }
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>{{projectName}}</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<cer-layout-view></cer-layout-view>
|
|
10
|
+
<script type="module" src="/app/app.ts"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{projectName}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "cer-app dev",
|
|
7
|
+
"build": "cer-app build",
|
|
8
|
+
"preview": "cer-app preview"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@jasonshimmy/custom-elements-runtime": "^3.1.1"
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"vite": "^5.0.0",
|
|
15
|
+
"vite-plugin-cer-app": "^0.1.0",
|
|
16
|
+
"typescript": "^5.4.0"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import '@jasonshimmy/custom-elements-runtime/css'
|
|
2
|
+
import 'virtual:cer-components'
|
|
3
|
+
import routes from 'virtual:cer-routes'
|
|
4
|
+
import layouts from 'virtual:cer-layouts'
|
|
5
|
+
import plugins from 'virtual:cer-plugins'
|
|
6
|
+
import { hasLoading, loadingTag } from 'virtual:cer-loading'
|
|
7
|
+
import { hasError, errorTag } from 'virtual:cer-error'
|
|
8
|
+
import {
|
|
9
|
+
component,
|
|
10
|
+
ref,
|
|
11
|
+
useOnConnected,
|
|
12
|
+
useOnDisconnected,
|
|
13
|
+
registerBuiltinComponents,
|
|
14
|
+
} from '@jasonshimmy/custom-elements-runtime'
|
|
15
|
+
import { initRouter } from '@jasonshimmy/custom-elements-runtime/router'
|
|
16
|
+
import { enableJITCSS } from '@jasonshimmy/custom-elements-runtime/jit-css'
|
|
17
|
+
import { createDOMJITCSS } from '@jasonshimmy/custom-elements-runtime/dom-jit-css'
|
|
18
|
+
|
|
19
|
+
registerBuiltinComponents()
|
|
20
|
+
enableJITCSS()
|
|
21
|
+
|
|
22
|
+
const router = initRouter({ routes })
|
|
23
|
+
|
|
24
|
+
const isNavigating = ref(false)
|
|
25
|
+
const currentError = ref(null)
|
|
26
|
+
;(globalThis as any).resetError = () => {
|
|
27
|
+
currentError.value = null
|
|
28
|
+
router.replace(router.getCurrent().path)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const _push = router.push.bind(router)
|
|
32
|
+
const _replace = router.replace.bind(router)
|
|
33
|
+
router.push = async (path) => {
|
|
34
|
+
isNavigating.value = true
|
|
35
|
+
currentError.value = null
|
|
36
|
+
try { await _push(path) } catch (err) { currentError.value = err instanceof Error ? err.message : String(err) } finally { isNavigating.value = false }
|
|
37
|
+
}
|
|
38
|
+
router.replace = async (path) => {
|
|
39
|
+
isNavigating.value = true
|
|
40
|
+
currentError.value = null
|
|
41
|
+
try { await _replace(path) } catch (err) { currentError.value = err instanceof Error ? err.message : String(err) } finally { isNavigating.value = false }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
component('cer-layout-view', () => {
|
|
45
|
+
const current = ref(router.getCurrent())
|
|
46
|
+
let unsub: (() => void) | undefined
|
|
47
|
+
useOnConnected(() => { unsub = router.subscribe((s: typeof current.value) => { current.value = s }) })
|
|
48
|
+
useOnDisconnected(() => { unsub?.(); unsub = undefined })
|
|
49
|
+
|
|
50
|
+
if (currentError.value !== null) {
|
|
51
|
+
if (hasError && errorTag) return { tag: errorTag, props: { attrs: { error: String(currentError.value) } }, children: [] }
|
|
52
|
+
return { tag: 'div', props: { attrs: { style: 'padding:2rem;font-family:monospace' } }, children: [String(currentError.value)] }
|
|
53
|
+
}
|
|
54
|
+
if (isNavigating.value && hasLoading && loadingTag) return { tag: loadingTag, props: {}, children: [] }
|
|
55
|
+
|
|
56
|
+
const matched = router.matchRoute(current.value.path)
|
|
57
|
+
const layoutName = (matched?.route as any)?.meta?.layout ?? 'default'
|
|
58
|
+
const layoutTag = (layouts as Record<string, string>)[layoutName]
|
|
59
|
+
const routerView = { tag: 'router-view', props: {}, children: [] }
|
|
60
|
+
return layoutTag ? { tag: layoutTag, props: {}, children: [routerView] } : routerView
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
for (const plugin of plugins) {
|
|
64
|
+
if (plugin && typeof plugin.setup === 'function') {
|
|
65
|
+
await plugin.setup({ router, provide: (key, value) => { (globalThis as any)[key] = value }, config: {} })
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (typeof window !== 'undefined') {
|
|
70
|
+
await router.replace(window.location.pathname + window.location.search + window.location.hash)
|
|
71
|
+
createDOMJITCSS().mount()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export { router }
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
component('page-index', () => {
|
|
2
|
+
return html`
|
|
3
|
+
<div>
|
|
4
|
+
<h1>Welcome to {{projectName}}</h1>
|
|
5
|
+
<p>Edit <code>app/pages/index.ts</code> to get started.</p>
|
|
6
|
+
</div>
|
|
7
|
+
`
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
// Export page metadata for SSG
|
|
11
|
+
export const meta = {
|
|
12
|
+
layout: 'default',
|
|
13
|
+
ssg: {
|
|
14
|
+
// No dynamic paths needed for the index page
|
|
15
|
+
paths: async () => [],
|
|
16
|
+
},
|
|
17
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineConfig } from 'vite-plugin-cer-app'
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
mode: 'ssg',
|
|
5
|
+
ssg: {
|
|
6
|
+
routes: 'auto',
|
|
7
|
+
concurrency: 4,
|
|
8
|
+
},
|
|
9
|
+
ssr: {
|
|
10
|
+
dsd: true,
|
|
11
|
+
},
|
|
12
|
+
autoImports: { components: true, composables: true, directives: true, runtime: true },
|
|
13
|
+
})
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>{{projectName}}</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<cer-layout-view></cer-layout-view>
|
|
10
|
+
<script type="module" src="/app/app.ts"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{projectName}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "cer-app dev",
|
|
7
|
+
"build": "cer-app build",
|
|
8
|
+
"generate": "cer-app generate",
|
|
9
|
+
"preview": "cer-app preview"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@jasonshimmy/custom-elements-runtime": "^3.1.1"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"vite": "^5.0.0",
|
|
16
|
+
"vite-plugin-cer-app": "^0.1.0",
|
|
17
|
+
"typescript": "^5.4.0"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import '@jasonshimmy/custom-elements-runtime/css'
|
|
2
|
+
import 'virtual:cer-components'
|
|
3
|
+
import routes from 'virtual:cer-routes'
|
|
4
|
+
import layouts from 'virtual:cer-layouts'
|
|
5
|
+
import plugins from 'virtual:cer-plugins'
|
|
6
|
+
import { hasLoading, loadingTag } from 'virtual:cer-loading'
|
|
7
|
+
import { hasError, errorTag } from 'virtual:cer-error'
|
|
8
|
+
import {
|
|
9
|
+
component,
|
|
10
|
+
ref,
|
|
11
|
+
useOnConnected,
|
|
12
|
+
useOnDisconnected,
|
|
13
|
+
registerBuiltinComponents,
|
|
14
|
+
} from '@jasonshimmy/custom-elements-runtime'
|
|
15
|
+
import { initRouter } from '@jasonshimmy/custom-elements-runtime/router'
|
|
16
|
+
import { enableJITCSS } from '@jasonshimmy/custom-elements-runtime/jit-css'
|
|
17
|
+
import { createDOMJITCSS } from '@jasonshimmy/custom-elements-runtime/dom-jit-css'
|
|
18
|
+
|
|
19
|
+
registerBuiltinComponents()
|
|
20
|
+
enableJITCSS()
|
|
21
|
+
|
|
22
|
+
const router = initRouter({ routes })
|
|
23
|
+
|
|
24
|
+
const isNavigating = ref(false)
|
|
25
|
+
const currentError = ref(null)
|
|
26
|
+
;(globalThis as any).resetError = () => {
|
|
27
|
+
currentError.value = null
|
|
28
|
+
router.replace(router.getCurrent().path)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const _push = router.push.bind(router)
|
|
32
|
+
const _replace = router.replace.bind(router)
|
|
33
|
+
router.push = async (path) => {
|
|
34
|
+
isNavigating.value = true
|
|
35
|
+
currentError.value = null
|
|
36
|
+
try { await _push(path) } catch (err) { currentError.value = err instanceof Error ? err.message : String(err) } finally { isNavigating.value = false }
|
|
37
|
+
}
|
|
38
|
+
router.replace = async (path) => {
|
|
39
|
+
isNavigating.value = true
|
|
40
|
+
currentError.value = null
|
|
41
|
+
try { await _replace(path) } catch (err) { currentError.value = err instanceof Error ? err.message : String(err) } finally { isNavigating.value = false }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
component('cer-layout-view', () => {
|
|
45
|
+
const current = ref(router.getCurrent())
|
|
46
|
+
let unsub: (() => void) | undefined
|
|
47
|
+
useOnConnected(() => { unsub = router.subscribe((s: typeof current.value) => { current.value = s }) })
|
|
48
|
+
useOnDisconnected(() => { unsub?.(); unsub = undefined })
|
|
49
|
+
|
|
50
|
+
if (currentError.value !== null) {
|
|
51
|
+
if (hasError && errorTag) return { tag: errorTag, props: { attrs: { error: String(currentError.value) } }, children: [] }
|
|
52
|
+
return { tag: 'div', props: { attrs: { style: 'padding:2rem;font-family:monospace' } }, children: [String(currentError.value)] }
|
|
53
|
+
}
|
|
54
|
+
if (isNavigating.value && hasLoading && loadingTag) return { tag: loadingTag, props: {}, children: [] }
|
|
55
|
+
|
|
56
|
+
const matched = router.matchRoute(current.value.path)
|
|
57
|
+
const layoutName = (matched?.route as any)?.meta?.layout ?? 'default'
|
|
58
|
+
const layoutTag = (layouts as Record<string, string>)[layoutName]
|
|
59
|
+
const routerView = { tag: 'router-view', props: {}, children: [] }
|
|
60
|
+
return layoutTag ? { tag: layoutTag, props: {}, children: [routerView] } : routerView
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
for (const plugin of plugins) {
|
|
64
|
+
if (plugin && typeof plugin.setup === 'function') {
|
|
65
|
+
await plugin.setup({ router, provide: (key, value) => { (globalThis as any)[key] = value }, config: {} })
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (typeof window !== 'undefined') {
|
|
70
|
+
await router.replace(window.location.pathname + window.location.search + window.location.hash)
|
|
71
|
+
createDOMJITCSS().mount()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export { router }
|