@jasonshimmy/vite-plugin-cer-app 0.1.6 → 0.3.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/workflows/publish.yml +56 -5
- package/CHANGELOG.md +8 -0
- package/README.md +2 -0
- package/commits.txt +1 -1
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/build.js +19 -5
- package/dist/cli/commands/build.js.map +1 -1
- package/dist/cli/commands/dev.js +1 -1
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/preview.d.ts.map +1 -1
- package/dist/cli/commands/preview.js +0 -1
- package/dist/cli/commands/preview.js.map +1 -1
- package/dist/cli/create/index.js +7 -3
- package/dist/cli/create/index.js.map +1 -1
- package/dist/cli/create/templates/spa/.gitignore.tpl +25 -0
- package/dist/cli/create/templates/spa/index.html.tpl +1 -1
- package/dist/cli/create/templates/ssg/.gitignore.tpl +25 -0
- package/dist/cli/create/templates/ssg/index.html.tpl +1 -1
- package/dist/cli/create/templates/ssr/.gitignore.tpl +25 -0
- package/dist/cli/create/templates/ssr/cer.config.ts.tpl +0 -1
- package/dist/cli/create/templates/ssr/index.html.tpl +1 -1
- package/dist/plugin/build-ssg.d.ts.map +1 -1
- package/dist/plugin/build-ssg.js.map +1 -1
- package/dist/plugin/build-ssr.d.ts +10 -0
- package/dist/plugin/build-ssr.d.ts.map +1 -1
- package/dist/plugin/build-ssr.js +21 -8
- package/dist/plugin/build-ssr.js.map +1 -1
- package/dist/plugin/dev-server.d.ts +0 -1
- package/dist/plugin/dev-server.d.ts.map +1 -1
- package/dist/plugin/dev-server.js +0 -2
- package/dist/plugin/dev-server.js.map +1 -1
- package/dist/plugin/dts-generator.d.ts +4 -4
- package/dist/plugin/dts-generator.d.ts.map +1 -1
- package/dist/plugin/dts-generator.js +39 -19
- package/dist/plugin/dts-generator.js.map +1 -1
- package/dist/plugin/generated-dir.d.ts +28 -0
- package/dist/plugin/generated-dir.d.ts.map +1 -0
- package/dist/plugin/generated-dir.js +106 -0
- package/dist/plugin/generated-dir.js.map +1 -0
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +27 -1
- package/dist/plugin/index.js.map +1 -1
- package/dist/plugin/path-utils.js.map +1 -1
- package/dist/plugin/virtual/loading.d.ts.map +1 -1
- package/dist/plugin/virtual/loading.js.map +1 -1
- package/dist/runtime/app-template.d.ts +9 -0
- package/dist/runtime/app-template.d.ts.map +1 -0
- package/dist/runtime/app-template.js +159 -0
- package/dist/runtime/app-template.js.map +1 -0
- package/dist/types/config.d.ts +0 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js.map +1 -1
- package/docs/cli.md +1 -1
- package/docs/configuration.md +2 -11
- package/docs/getting-started.md +2 -100
- package/docs/rendering-modes.md +4 -5
- package/docs/routing.md +1 -1
- package/e2e/kitchen-sink/tsconfig.json +3 -0
- package/eslint.config.ts +22 -0
- package/package.json +6 -1
- package/src/__tests__/plugin/build-ssr.test.ts +24 -10
- package/src/__tests__/plugin/cer-app-plugin.test.ts +35 -0
- package/src/__tests__/plugin/dev-server.test.ts +1 -1
- package/src/__tests__/plugin/dts-generator.test.ts +15 -6
- package/src/__tests__/plugin/generated-dir.test.ts +137 -0
- package/src/__tests__/plugin/resolve-config.test.ts +0 -5
- package/src/__tests__/types/config.test.ts +1 -1
- package/src/cli/commands/build.ts +19 -5
- package/src/cli/commands/dev.ts +2 -2
- package/src/cli/commands/preview.ts +7 -5
- package/src/cli/create/index.ts +12 -8
- package/src/cli/create/templates/spa/.gitignore.tpl +25 -0
- package/src/cli/create/templates/spa/index.html.tpl +1 -1
- package/src/cli/create/templates/ssg/.gitignore.tpl +25 -0
- package/src/cli/create/templates/ssg/index.html.tpl +1 -1
- package/src/cli/create/templates/ssr/.gitignore.tpl +25 -0
- package/src/cli/create/templates/ssr/cer.config.ts.tpl +0 -1
- package/src/cli/create/templates/ssr/index.html.tpl +1 -1
- package/src/plugin/build-ssg.ts +2 -2
- package/src/plugin/build-ssr.ts +22 -8
- package/src/plugin/dev-server.ts +5 -4
- package/src/plugin/dts-generator.ts +43 -19
- package/src/plugin/generated-dir.ts +115 -0
- package/src/plugin/index.ts +32 -2
- package/src/plugin/path-utils.ts +1 -1
- package/src/plugin/virtual/loading.ts +0 -1
- package/{e2e/kitchen-sink/app/app.ts → src/runtime/app-template.ts} +24 -7
- package/src/types/config.ts +0 -1
- package/dist/cli/create/templates/spa/app/app.ts.tpl +0 -93
- package/dist/cli/create/templates/ssg/app/app.ts.tpl +0 -97
- package/dist/cli/create/templates/ssr/app/app.ts.tpl +0 -97
- package/e2e/kitchen-sink/index.html +0 -12
- package/src/cli/create/templates/spa/app/app.ts.tpl +0 -93
- package/src/cli/create/templates/ssg/app/app.ts.tpl +0 -97
- package/src/cli/create/templates/ssr/app/app.ts.tpl +0 -97
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { writeFileSync, existsSync, mkdirSync, readFileSync, appendFileSync } from 'node:fs'
|
|
2
|
+
import { join } from 'pathe'
|
|
3
|
+
import type { ResolvedCerConfig } from './dev-server.js'
|
|
4
|
+
import { APP_ENTRY_TEMPLATE } from '../runtime/app-template.js'
|
|
5
|
+
|
|
6
|
+
/** The name of the generated directory relative to the project root. */
|
|
7
|
+
export const GENERATED_DIR_NAME = '.cer'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Returns the absolute path to the .cer/ generated directory.
|
|
11
|
+
*/
|
|
12
|
+
export function getGeneratedDir(root: string): string {
|
|
13
|
+
return join(root, GENERATED_DIR_NAME)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Returns the HTML entry path to use for builds.
|
|
18
|
+
* Prefers the consumer's root-level `index.html` when it exists;
|
|
19
|
+
* falls back to `.cer/index.html`.
|
|
20
|
+
*/
|
|
21
|
+
export function resolveHtmlEntry(config: ResolvedCerConfig): string {
|
|
22
|
+
const userHtml = join(config.root, 'index.html')
|
|
23
|
+
if (existsSync(userHtml)) return userHtml
|
|
24
|
+
return join(getGeneratedDir(config.root), 'index.html')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Generates the content for the default `.cer/index.html`.
|
|
29
|
+
* Always points to the generated `/.cer/app.ts` entry.
|
|
30
|
+
*/
|
|
31
|
+
export function generateDefaultHtml(): string {
|
|
32
|
+
return `<!DOCTYPE html>
|
|
33
|
+
<html lang="en">
|
|
34
|
+
<head>
|
|
35
|
+
<meta charset="UTF-8" />
|
|
36
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
37
|
+
<title>CER App</title>
|
|
38
|
+
</head>
|
|
39
|
+
<body>
|
|
40
|
+
<cer-layout-view></cer-layout-view>
|
|
41
|
+
<script type="module" src="/.cer/app.ts"></script>
|
|
42
|
+
</body>
|
|
43
|
+
</html>
|
|
44
|
+
`
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const GITIGNORE_DEFAULTS = `# Dependencies
|
|
48
|
+
node_modules/
|
|
49
|
+
|
|
50
|
+
# Build output
|
|
51
|
+
dist/
|
|
52
|
+
|
|
53
|
+
# CER App generated directory
|
|
54
|
+
.cer/
|
|
55
|
+
|
|
56
|
+
# Environment variables
|
|
57
|
+
.env.local
|
|
58
|
+
.env.*.local
|
|
59
|
+
|
|
60
|
+
# Editor
|
|
61
|
+
.vscode/
|
|
62
|
+
.idea/
|
|
63
|
+
*.suo
|
|
64
|
+
*.sw?
|
|
65
|
+
|
|
66
|
+
# OS
|
|
67
|
+
.DS_Store
|
|
68
|
+
Thumbs.db
|
|
69
|
+
|
|
70
|
+
# Logs
|
|
71
|
+
*.log
|
|
72
|
+
`
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Ensures `.cer/`, `node_modules/`, `dist/`, and other common entries are
|
|
76
|
+
* listed in the project's `.gitignore`. Creates `.gitignore` if it does not exist.
|
|
77
|
+
*/
|
|
78
|
+
function ensureGitignore(root: string): void {
|
|
79
|
+
const gitignorePath = join(root, '.gitignore')
|
|
80
|
+
const cerEntry = `${GENERATED_DIR_NAME}/`
|
|
81
|
+
|
|
82
|
+
if (existsSync(gitignorePath)) {
|
|
83
|
+
const content = readFileSync(gitignorePath, 'utf-8')
|
|
84
|
+
if (!content.includes(cerEntry) && !content.includes(`${GENERATED_DIR_NAME}\n`)) {
|
|
85
|
+
appendFileSync(gitignorePath, `\n# CER App generated directory\n${cerEntry}\n`)
|
|
86
|
+
}
|
|
87
|
+
} else {
|
|
88
|
+
writeFileSync(gitignorePath, GITIGNORE_DEFAULTS)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Writes all generated files to `.cer/`:
|
|
94
|
+
* - `.cer/app.ts` — framework entry (always regenerated)
|
|
95
|
+
* - `.cer/index.html` — default HTML shell (always regenerated)
|
|
96
|
+
* - `.cer/tsconfig.json` — written by dts-generator via writeTsconfigPaths
|
|
97
|
+
*
|
|
98
|
+
* Also ensures `.cer/` is listed in `.gitignore`.
|
|
99
|
+
*/
|
|
100
|
+
export function writeGeneratedDir(config: ResolvedCerConfig): void {
|
|
101
|
+
const dir = getGeneratedDir(config.root)
|
|
102
|
+
if (!existsSync(dir)) {
|
|
103
|
+
mkdirSync(dir, { recursive: true })
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Always write the generated app.ts — this is the framework entry point and
|
|
107
|
+
// is never user-owned. Regenerating it on every dev/build ensures consumers
|
|
108
|
+
// automatically get the latest bootstrap code on plugin update (Nuxt-style).
|
|
109
|
+
writeFileSync(join(dir, 'app.ts'), APP_ENTRY_TEMPLATE, 'utf-8')
|
|
110
|
+
|
|
111
|
+
// Always write the default index.html so builds and the dev server can use it.
|
|
112
|
+
writeFileSync(join(dir, 'index.html'), generateDefaultHtml(), 'utf-8')
|
|
113
|
+
|
|
114
|
+
ensureGitignore(config.root)
|
|
115
|
+
}
|
package/src/plugin/index.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { resolve, join } from 'pathe'
|
|
2
|
-
import
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
3
|
+
import type { Plugin, ViteDevServer } from 'vite'
|
|
3
4
|
import type { CerAppConfig } from '../types/config.js'
|
|
4
5
|
import type { ResolvedCerConfig } from './dev-server.js'
|
|
5
6
|
import { cerPlugin } from '@jasonshimmy/custom-elements-runtime/vite-plugin'
|
|
6
7
|
import { autoImportTransform } from './transforms/auto-import.js'
|
|
7
8
|
import { scanComposableExports, writeAutoImportDts, writeTsconfigPaths } from './dts-generator.js'
|
|
8
9
|
import { configureCerDevServer } from './dev-server.js'
|
|
10
|
+
import { writeGeneratedDir, getGeneratedDir } from './generated-dir.js'
|
|
9
11
|
import { generateRoutesCode } from './virtual/routes.js'
|
|
10
12
|
import { generateLayoutsCode } from './virtual/layouts.js'
|
|
11
13
|
import { generateComponentsCode } from './virtual/components.js'
|
|
@@ -60,7 +62,6 @@ export function resolveConfig(userConfig: CerAppConfig, root: string = process.c
|
|
|
60
62
|
port: userConfig.port ?? 3000,
|
|
61
63
|
ssr: {
|
|
62
64
|
dsd: userConfig.ssr?.dsd ?? true,
|
|
63
|
-
streaming: userConfig.ssr?.streaming ?? false,
|
|
64
65
|
},
|
|
65
66
|
ssg: {
|
|
66
67
|
routes: userConfig.ssg?.routes ?? 'auto',
|
|
@@ -256,11 +257,38 @@ export function cerApp(userConfig: CerAppConfig = {}): Plugin[] {
|
|
|
256
257
|
// config might not be set yet; resolve with cwd
|
|
257
258
|
config = resolveConfig(userConfig, process.cwd())
|
|
258
259
|
}
|
|
260
|
+
|
|
261
|
+
// Write .cer/ generated files (app.ts fallback, index.html, .gitignore)
|
|
262
|
+
writeGeneratedDir(config)
|
|
263
|
+
|
|
259
264
|
// Scan composables and write .d.ts + tsconfig paths on dev server start
|
|
260
265
|
composableExports = await scanComposableExports(config.composablesDir)
|
|
261
266
|
await writeAutoImportDts(config.root, config.composablesDir, composableExports)
|
|
262
267
|
writeTsconfigPaths(config.root, config.srcDir)
|
|
263
268
|
|
|
269
|
+
// Serve a generated index.html for HTML requests when the consumer has
|
|
270
|
+
// not provided one. This runs BEFORE configureCerDevServer so that the
|
|
271
|
+
// Vite HTML pipeline (HMR injection, module preprocessing) is applied.
|
|
272
|
+
const userHtml = resolve(config.root, 'index.html')
|
|
273
|
+
if (!existsSync(userHtml)) {
|
|
274
|
+
const cerHtmlPath = join(getGeneratedDir(config.root), 'index.html')
|
|
275
|
+
server.middlewares.use(async (req, res, next) => {
|
|
276
|
+
const url = (req as { url?: string }).url ?? '/'
|
|
277
|
+
const isHtmlRequest =
|
|
278
|
+
url === '/' ||
|
|
279
|
+
url === '/index.html' ||
|
|
280
|
+
(!url.includes('.') && !url.startsWith('/api/'))
|
|
281
|
+
if (isHtmlRequest && existsSync(cerHtmlPath)) {
|
|
282
|
+
const rawHtml = readFileSync(cerHtmlPath, 'utf-8')
|
|
283
|
+
const transformed = await server.transformIndexHtml(url, rawHtml)
|
|
284
|
+
res.setHeader('Content-Type', 'text/html; charset=utf-8')
|
|
285
|
+
res.end(transformed)
|
|
286
|
+
return
|
|
287
|
+
}
|
|
288
|
+
next()
|
|
289
|
+
})
|
|
290
|
+
}
|
|
291
|
+
|
|
264
292
|
// Watch app/ and server/ directories for file changes
|
|
265
293
|
const watchDirs = [
|
|
266
294
|
config.pagesDir,
|
|
@@ -302,6 +330,8 @@ export function cerApp(userConfig: CerAppConfig = {}): Plugin[] {
|
|
|
302
330
|
if (!config) {
|
|
303
331
|
config = resolveConfig(userConfig, process.cwd())
|
|
304
332
|
}
|
|
333
|
+
// Write .cer/ generated files before the build begins
|
|
334
|
+
writeGeneratedDir(config)
|
|
305
335
|
// Scan composables and generate type declarations + tsconfig paths
|
|
306
336
|
composableExports = await scanComposableExports(config.composablesDir)
|
|
307
337
|
await writeAutoImportDts(config.root, config.composablesDir, composableExports)
|
package/src/plugin/path-utils.ts
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template string for `.cer/app.ts` — the framework client entry point.
|
|
3
|
+
*
|
|
4
|
+
* Always written to `.cer/app.ts` on every dev/build so consumers
|
|
5
|
+
* automatically receive the latest bootstrap code on plugin update.
|
|
6
|
+
* This file is gitignored and should never be edited directly.
|
|
7
|
+
*/
|
|
8
|
+
export const APP_ENTRY_TEMPLATE = `// AUTO-GENERATED by @jasonshimmy/vite-plugin-cer-app — do not edit.
|
|
9
|
+
// Regenerated automatically on every dev server start and build.
|
|
10
|
+
|
|
1
11
|
import '@jasonshimmy/custom-elements-runtime/css'
|
|
2
12
|
import 'virtual:cer-jit-css'
|
|
3
13
|
import 'virtual:cer-components'
|
|
@@ -27,10 +37,11 @@ const router = initRouter({ routes })
|
|
|
27
37
|
const isNavigating = ref(false)
|
|
28
38
|
const currentError = ref(null)
|
|
29
39
|
|
|
30
|
-
|
|
40
|
+
const resetError = (): void => {
|
|
31
41
|
currentError.value = null
|
|
32
|
-
router.replace(router.getCurrent().path)
|
|
42
|
+
void router.replace(router.getCurrent().path)
|
|
33
43
|
}
|
|
44
|
+
;(globalThis as Record<string, unknown>).resetError = resetError
|
|
34
45
|
|
|
35
46
|
const _push = router.push.bind(router)
|
|
36
47
|
const _replace = router.replace.bind(router)
|
|
@@ -68,8 +79,8 @@ router.replace = async (path) => {
|
|
|
68
79
|
// synchronously, calling the render function immediately.
|
|
69
80
|
const _pluginProvides = new Map<string, unknown>()
|
|
70
81
|
// Expose plugin provides globally so page components can read them synchronously
|
|
71
|
-
// regardless of render order
|
|
72
|
-
;(globalThis as
|
|
82
|
+
// regardless of render order.
|
|
83
|
+
;(globalThis as Record<string, unknown>).__cerPluginProvides = _pluginProvides
|
|
73
84
|
|
|
74
85
|
// ─── <cer-layout-view> ───────────────────────────────────────────────────────
|
|
75
86
|
|
|
@@ -100,7 +111,8 @@ component('cer-layout-view', () => {
|
|
|
100
111
|
}
|
|
101
112
|
|
|
102
113
|
const matched = router.matchRoute(current.value.path)
|
|
103
|
-
const
|
|
114
|
+
const routeMeta = matched?.route?.meta as { layout?: string } | undefined
|
|
115
|
+
const layoutName = routeMeta?.layout ?? 'default'
|
|
104
116
|
const layoutTag = (layouts as Record<string, string>)[layoutName]
|
|
105
117
|
const routerView = { tag: 'router-view', props: {}, children: [] }
|
|
106
118
|
|
|
@@ -110,7 +122,11 @@ component('cer-layout-view', () => {
|
|
|
110
122
|
|
|
111
123
|
for (const plugin of plugins) {
|
|
112
124
|
if (plugin && typeof plugin.setup === 'function') {
|
|
113
|
-
await plugin.setup({
|
|
125
|
+
await plugin.setup({
|
|
126
|
+
router,
|
|
127
|
+
provide: (key: string, value: unknown) => { _pluginProvides.set(key, value) },
|
|
128
|
+
config: {},
|
|
129
|
+
})
|
|
114
130
|
}
|
|
115
131
|
}
|
|
116
132
|
|
|
@@ -135,7 +151,8 @@ if (typeof window !== 'undefined') {
|
|
|
135
151
|
await _replace(window.location.pathname + window.location.search + window.location.hash)
|
|
136
152
|
// Clear SSR loader data after initial navigation so subsequent client-side
|
|
137
153
|
// navigations don't accidentally reuse stale server data.
|
|
138
|
-
delete (globalThis as
|
|
154
|
+
delete (globalThis as Record<string, unknown>).__CER_DATA__
|
|
139
155
|
}
|
|
140
156
|
|
|
141
157
|
export { router }
|
|
158
|
+
`
|
package/src/types/config.ts
CHANGED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import '@jasonshimmy/custom-elements-runtime/css'
|
|
2
|
-
import 'virtual:cer-jit-css'
|
|
3
|
-
import 'virtual:cer-components'
|
|
4
|
-
import routes from 'virtual:cer-routes'
|
|
5
|
-
import layouts from 'virtual:cer-layouts'
|
|
6
|
-
import plugins from 'virtual:cer-plugins'
|
|
7
|
-
import { hasLoading, loadingTag } from 'virtual:cer-loading'
|
|
8
|
-
import { hasError, errorTag } from 'virtual:cer-error'
|
|
9
|
-
import {
|
|
10
|
-
component,
|
|
11
|
-
ref,
|
|
12
|
-
provide,
|
|
13
|
-
useOnConnected,
|
|
14
|
-
useOnDisconnected,
|
|
15
|
-
registerBuiltinComponents,
|
|
16
|
-
} from '@jasonshimmy/custom-elements-runtime'
|
|
17
|
-
import { initRouter } from '@jasonshimmy/custom-elements-runtime/router'
|
|
18
|
-
import { enableJITCSS } from '@jasonshimmy/custom-elements-runtime/jit-css'
|
|
19
|
-
|
|
20
|
-
registerBuiltinComponents()
|
|
21
|
-
enableJITCSS()
|
|
22
|
-
|
|
23
|
-
const router = initRouter({ routes })
|
|
24
|
-
|
|
25
|
-
const isNavigating = ref(false)
|
|
26
|
-
const currentError = ref(null)
|
|
27
|
-
;(globalThis as any).resetError = () => {
|
|
28
|
-
currentError.value = null
|
|
29
|
-
router.replace(router.getCurrent().path)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const _push = router.push.bind(router)
|
|
33
|
-
const _replace = router.replace.bind(router)
|
|
34
|
-
router.push = async (path) => {
|
|
35
|
-
isNavigating.value = true
|
|
36
|
-
currentError.value = null
|
|
37
|
-
try { await _push(path) } catch (err) { currentError.value = err instanceof Error ? err.message : String(err) } finally { isNavigating.value = false }
|
|
38
|
-
}
|
|
39
|
-
router.replace = async (path) => {
|
|
40
|
-
isNavigating.value = true
|
|
41
|
-
currentError.value = null
|
|
42
|
-
try { await _replace(path) } catch (err) { currentError.value = err instanceof Error ? err.message : String(err) } finally { isNavigating.value = false }
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const _pluginProvides = new Map<string, unknown>()
|
|
46
|
-
;(globalThis as any).__cerPluginProvides = _pluginProvides
|
|
47
|
-
|
|
48
|
-
component('cer-layout-view', () => {
|
|
49
|
-
for (const [key, value] of _pluginProvides) {
|
|
50
|
-
provide(key, value)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const current = ref(router.getCurrent())
|
|
54
|
-
let unsub: (() => void) | undefined
|
|
55
|
-
useOnConnected(() => { unsub = router.subscribe((s: typeof current.value) => { current.value = s }) })
|
|
56
|
-
useOnDisconnected(() => { unsub?.(); unsub = undefined })
|
|
57
|
-
|
|
58
|
-
if (currentError.value !== null) {
|
|
59
|
-
if (hasError && errorTag) return { tag: errorTag, props: { attrs: { error: String(currentError.value) } }, children: [] }
|
|
60
|
-
return { tag: 'div', props: { attrs: { style: 'padding:2rem;font-family:monospace' } }, children: String(currentError.value) }
|
|
61
|
-
}
|
|
62
|
-
if (isNavigating.value && hasLoading && loadingTag) return { tag: loadingTag, props: {}, children: [] }
|
|
63
|
-
|
|
64
|
-
const matched = router.matchRoute(current.value.path)
|
|
65
|
-
const layoutName = (matched?.route as any)?.meta?.layout ?? 'default'
|
|
66
|
-
const layoutTag = (layouts as Record<string, string>)[layoutName]
|
|
67
|
-
const routerView = { tag: 'router-view', props: {}, children: [] }
|
|
68
|
-
return layoutTag ? { tag: layoutTag, props: {}, children: [routerView] } : routerView
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
for (const plugin of plugins) {
|
|
72
|
-
if (plugin && typeof plugin.setup === 'function') {
|
|
73
|
-
await plugin.setup({ router, provide: (key: string, value: unknown) => { _pluginProvides.set(key, value) }, config: {} })
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Pre-load the current page's route chunk AFTER plugins run so that
|
|
78
|
-
// cer-layout-view's first render (which calls provide()) completes before
|
|
79
|
-
// page components are defined. This ensures inject() in child components
|
|
80
|
-
// can find values stored by provide().
|
|
81
|
-
if (typeof window !== 'undefined') {
|
|
82
|
-
const _initMatch = router.matchRoute(window.location.pathname)
|
|
83
|
-
if (_initMatch?.route?.load) {
|
|
84
|
-
try { await _initMatch.route.load() } catch { /* non-fatal */ }
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (typeof window !== 'undefined') {
|
|
89
|
-
// Use the original (unwrapped) replace so isNavigating stays false on first paint.
|
|
90
|
-
await _replace(window.location.pathname + window.location.search + window.location.hash)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export { router }
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import '@jasonshimmy/custom-elements-runtime/css'
|
|
2
|
-
import 'virtual:cer-jit-css'
|
|
3
|
-
import 'virtual:cer-components'
|
|
4
|
-
import routes from 'virtual:cer-routes'
|
|
5
|
-
import layouts from 'virtual:cer-layouts'
|
|
6
|
-
import plugins from 'virtual:cer-plugins'
|
|
7
|
-
import { hasLoading, loadingTag } from 'virtual:cer-loading'
|
|
8
|
-
import { hasError, errorTag } from 'virtual:cer-error'
|
|
9
|
-
import {
|
|
10
|
-
component,
|
|
11
|
-
ref,
|
|
12
|
-
provide,
|
|
13
|
-
useOnConnected,
|
|
14
|
-
useOnDisconnected,
|
|
15
|
-
registerBuiltinComponents,
|
|
16
|
-
} from '@jasonshimmy/custom-elements-runtime'
|
|
17
|
-
import { initRouter } from '@jasonshimmy/custom-elements-runtime/router'
|
|
18
|
-
import { enableJITCSS } from '@jasonshimmy/custom-elements-runtime/jit-css'
|
|
19
|
-
|
|
20
|
-
registerBuiltinComponents()
|
|
21
|
-
enableJITCSS()
|
|
22
|
-
|
|
23
|
-
const router = initRouter({ routes })
|
|
24
|
-
|
|
25
|
-
const isNavigating = ref(false)
|
|
26
|
-
const currentError = ref(null)
|
|
27
|
-
;(globalThis as any).resetError = () => {
|
|
28
|
-
currentError.value = null
|
|
29
|
-
router.replace(router.getCurrent().path)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const _push = router.push.bind(router)
|
|
33
|
-
const _replace = router.replace.bind(router)
|
|
34
|
-
router.push = async (path) => {
|
|
35
|
-
isNavigating.value = true
|
|
36
|
-
currentError.value = null
|
|
37
|
-
try { await _push(path) } catch (err) { currentError.value = err instanceof Error ? err.message : String(err) } finally { isNavigating.value = false }
|
|
38
|
-
}
|
|
39
|
-
router.replace = async (path) => {
|
|
40
|
-
isNavigating.value = true
|
|
41
|
-
currentError.value = null
|
|
42
|
-
try { await _replace(path) } catch (err) { currentError.value = err instanceof Error ? err.message : String(err) } finally { isNavigating.value = false }
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const _pluginProvides = new Map<string, unknown>()
|
|
46
|
-
;(globalThis as any).__cerPluginProvides = _pluginProvides
|
|
47
|
-
|
|
48
|
-
component('cer-layout-view', () => {
|
|
49
|
-
for (const [key, value] of _pluginProvides) {
|
|
50
|
-
provide(key, value)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const current = ref(router.getCurrent())
|
|
54
|
-
let unsub: (() => void) | undefined
|
|
55
|
-
useOnConnected(() => { unsub = router.subscribe((s: typeof current.value) => { current.value = s }) })
|
|
56
|
-
useOnDisconnected(() => { unsub?.(); unsub = undefined })
|
|
57
|
-
|
|
58
|
-
if (currentError.value !== null) {
|
|
59
|
-
if (hasError && errorTag) return { tag: errorTag, props: { attrs: { error: String(currentError.value) } }, children: [] }
|
|
60
|
-
return { tag: 'div', props: { attrs: { style: 'padding:2rem;font-family:monospace' } }, children: String(currentError.value) }
|
|
61
|
-
}
|
|
62
|
-
if (isNavigating.value && hasLoading && loadingTag) return { tag: loadingTag, props: {}, children: [] }
|
|
63
|
-
|
|
64
|
-
const matched = router.matchRoute(current.value.path)
|
|
65
|
-
const layoutName = (matched?.route as any)?.meta?.layout ?? 'default'
|
|
66
|
-
const layoutTag = (layouts as Record<string, string>)[layoutName]
|
|
67
|
-
const routerView = { tag: 'router-view', props: {}, children: [] }
|
|
68
|
-
return layoutTag ? { tag: layoutTag, props: {}, children: [routerView] } : routerView
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
for (const plugin of plugins) {
|
|
72
|
-
if (plugin && typeof plugin.setup === 'function') {
|
|
73
|
-
await plugin.setup({ router, provide: (key: string, value: unknown) => { _pluginProvides.set(key, value) }, config: {} })
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Pre-load the current page's route chunk AFTER plugins run so that
|
|
78
|
-
// cer-layout-view's first render (which calls provide()) completes before
|
|
79
|
-
// page components are defined. This ensures inject() in child components
|
|
80
|
-
// can find values stored by provide().
|
|
81
|
-
if (typeof window !== 'undefined') {
|
|
82
|
-
const _initMatch = router.matchRoute(window.location.pathname)
|
|
83
|
-
if (_initMatch?.route?.load) {
|
|
84
|
-
try { await _initMatch.route.load() } catch { /* non-fatal */ }
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (typeof window !== 'undefined') {
|
|
89
|
-
// Use the original (unwrapped) replace so isNavigating stays false on first
|
|
90
|
-
// paint — the loading component must not flash over pre-rendered SSG content.
|
|
91
|
-
await _replace(window.location.pathname + window.location.search + window.location.hash)
|
|
92
|
-
// Clear SSR hydration data after initial navigation so subsequent navigations
|
|
93
|
-
// don't accidentally reuse it.
|
|
94
|
-
delete (globalThis as any).__CER_DATA__
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export { router }
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import '@jasonshimmy/custom-elements-runtime/css'
|
|
2
|
-
import 'virtual:cer-jit-css'
|
|
3
|
-
import 'virtual:cer-components'
|
|
4
|
-
import routes from 'virtual:cer-routes'
|
|
5
|
-
import layouts from 'virtual:cer-layouts'
|
|
6
|
-
import plugins from 'virtual:cer-plugins'
|
|
7
|
-
import { hasLoading, loadingTag } from 'virtual:cer-loading'
|
|
8
|
-
import { hasError, errorTag } from 'virtual:cer-error'
|
|
9
|
-
import {
|
|
10
|
-
component,
|
|
11
|
-
ref,
|
|
12
|
-
provide,
|
|
13
|
-
useOnConnected,
|
|
14
|
-
useOnDisconnected,
|
|
15
|
-
registerBuiltinComponents,
|
|
16
|
-
} from '@jasonshimmy/custom-elements-runtime'
|
|
17
|
-
import { initRouter } from '@jasonshimmy/custom-elements-runtime/router'
|
|
18
|
-
import { enableJITCSS } from '@jasonshimmy/custom-elements-runtime/jit-css'
|
|
19
|
-
|
|
20
|
-
registerBuiltinComponents()
|
|
21
|
-
enableJITCSS()
|
|
22
|
-
|
|
23
|
-
const router = initRouter({ routes })
|
|
24
|
-
|
|
25
|
-
const isNavigating = ref(false)
|
|
26
|
-
const currentError = ref(null)
|
|
27
|
-
;(globalThis as any).resetError = () => {
|
|
28
|
-
currentError.value = null
|
|
29
|
-
router.replace(router.getCurrent().path)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const _push = router.push.bind(router)
|
|
33
|
-
const _replace = router.replace.bind(router)
|
|
34
|
-
router.push = async (path) => {
|
|
35
|
-
isNavigating.value = true
|
|
36
|
-
currentError.value = null
|
|
37
|
-
try { await _push(path) } catch (err) { currentError.value = err instanceof Error ? err.message : String(err) } finally { isNavigating.value = false }
|
|
38
|
-
}
|
|
39
|
-
router.replace = async (path) => {
|
|
40
|
-
isNavigating.value = true
|
|
41
|
-
currentError.value = null
|
|
42
|
-
try { await _replace(path) } catch (err) { currentError.value = err instanceof Error ? err.message : String(err) } finally { isNavigating.value = false }
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const _pluginProvides = new Map<string, unknown>()
|
|
46
|
-
;(globalThis as any).__cerPluginProvides = _pluginProvides
|
|
47
|
-
|
|
48
|
-
component('cer-layout-view', () => {
|
|
49
|
-
for (const [key, value] of _pluginProvides) {
|
|
50
|
-
provide(key, value)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const current = ref(router.getCurrent())
|
|
54
|
-
let unsub: (() => void) | undefined
|
|
55
|
-
useOnConnected(() => { unsub = router.subscribe((s: typeof current.value) => { current.value = s }) })
|
|
56
|
-
useOnDisconnected(() => { unsub?.(); unsub = undefined })
|
|
57
|
-
|
|
58
|
-
if (currentError.value !== null) {
|
|
59
|
-
if (hasError && errorTag) return { tag: errorTag, props: { attrs: { error: String(currentError.value) } }, children: [] }
|
|
60
|
-
return { tag: 'div', props: { attrs: { style: 'padding:2rem;font-family:monospace' } }, children: String(currentError.value) }
|
|
61
|
-
}
|
|
62
|
-
if (isNavigating.value && hasLoading && loadingTag) return { tag: loadingTag, props: {}, children: [] }
|
|
63
|
-
|
|
64
|
-
const matched = router.matchRoute(current.value.path)
|
|
65
|
-
const layoutName = (matched?.route as any)?.meta?.layout ?? 'default'
|
|
66
|
-
const layoutTag = (layouts as Record<string, string>)[layoutName]
|
|
67
|
-
const routerView = { tag: 'router-view', props: {}, children: [] }
|
|
68
|
-
return layoutTag ? { tag: layoutTag, props: {}, children: [routerView] } : routerView
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
for (const plugin of plugins) {
|
|
72
|
-
if (plugin && typeof plugin.setup === 'function') {
|
|
73
|
-
await plugin.setup({ router, provide: (key: string, value: unknown) => { _pluginProvides.set(key, value) }, config: {} })
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Pre-load the current page's route chunk AFTER plugins run so that
|
|
78
|
-
// cer-layout-view's first render (which calls provide()) completes before
|
|
79
|
-
// page components are defined. This ensures inject() in child components
|
|
80
|
-
// can find values stored by provide().
|
|
81
|
-
if (typeof window !== 'undefined') {
|
|
82
|
-
const _initMatch = router.matchRoute(window.location.pathname)
|
|
83
|
-
if (_initMatch?.route?.load) {
|
|
84
|
-
try { await _initMatch.route.load() } catch { /* non-fatal */ }
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (typeof window !== 'undefined') {
|
|
89
|
-
// Use the original (unwrapped) replace so isNavigating stays false on first
|
|
90
|
-
// paint — the loading component must not flash over pre-rendered SSR content.
|
|
91
|
-
await _replace(window.location.pathname + window.location.search + window.location.hash)
|
|
92
|
-
// Clear SSR hydration data after initial navigation so subsequent navigations
|
|
93
|
-
// don't accidentally reuse it.
|
|
94
|
-
delete (globalThis as any).__CER_DATA__
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export { router }
|
|
@@ -1,12 +0,0 @@
|
|
|
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>Kitchen Sink</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>
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import '@jasonshimmy/custom-elements-runtime/css'
|
|
2
|
-
import 'virtual:cer-jit-css'
|
|
3
|
-
import 'virtual:cer-components'
|
|
4
|
-
import routes from 'virtual:cer-routes'
|
|
5
|
-
import layouts from 'virtual:cer-layouts'
|
|
6
|
-
import plugins from 'virtual:cer-plugins'
|
|
7
|
-
import { hasLoading, loadingTag } from 'virtual:cer-loading'
|
|
8
|
-
import { hasError, errorTag } from 'virtual:cer-error'
|
|
9
|
-
import {
|
|
10
|
-
component,
|
|
11
|
-
ref,
|
|
12
|
-
provide,
|
|
13
|
-
useOnConnected,
|
|
14
|
-
useOnDisconnected,
|
|
15
|
-
registerBuiltinComponents,
|
|
16
|
-
} from '@jasonshimmy/custom-elements-runtime'
|
|
17
|
-
import { initRouter } from '@jasonshimmy/custom-elements-runtime/router'
|
|
18
|
-
import { enableJITCSS } from '@jasonshimmy/custom-elements-runtime/jit-css'
|
|
19
|
-
|
|
20
|
-
registerBuiltinComponents()
|
|
21
|
-
enableJITCSS()
|
|
22
|
-
|
|
23
|
-
const router = initRouter({ routes })
|
|
24
|
-
|
|
25
|
-
const isNavigating = ref(false)
|
|
26
|
-
const currentError = ref(null)
|
|
27
|
-
;(globalThis as any).resetError = () => {
|
|
28
|
-
currentError.value = null
|
|
29
|
-
router.replace(router.getCurrent().path)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const _push = router.push.bind(router)
|
|
33
|
-
const _replace = router.replace.bind(router)
|
|
34
|
-
router.push = async (path) => {
|
|
35
|
-
isNavigating.value = true
|
|
36
|
-
currentError.value = null
|
|
37
|
-
try { await _push(path) } catch (err) { currentError.value = err instanceof Error ? err.message : String(err) } finally { isNavigating.value = false }
|
|
38
|
-
}
|
|
39
|
-
router.replace = async (path) => {
|
|
40
|
-
isNavigating.value = true
|
|
41
|
-
currentError.value = null
|
|
42
|
-
try { await _replace(path) } catch (err) { currentError.value = err instanceof Error ? err.message : String(err) } finally { isNavigating.value = false }
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const _pluginProvides = new Map<string, unknown>()
|
|
46
|
-
;(globalThis as any).__cerPluginProvides = _pluginProvides
|
|
47
|
-
|
|
48
|
-
component('cer-layout-view', () => {
|
|
49
|
-
for (const [key, value] of _pluginProvides) {
|
|
50
|
-
provide(key, value)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const current = ref(router.getCurrent())
|
|
54
|
-
let unsub: (() => void) | undefined
|
|
55
|
-
useOnConnected(() => { unsub = router.subscribe((s: typeof current.value) => { current.value = s }) })
|
|
56
|
-
useOnDisconnected(() => { unsub?.(); unsub = undefined })
|
|
57
|
-
|
|
58
|
-
if (currentError.value !== null) {
|
|
59
|
-
if (hasError && errorTag) return { tag: errorTag, props: { attrs: { error: String(currentError.value) } }, children: [] }
|
|
60
|
-
return { tag: 'div', props: { attrs: { style: 'padding:2rem;font-family:monospace' } }, children: String(currentError.value) }
|
|
61
|
-
}
|
|
62
|
-
if (isNavigating.value && hasLoading && loadingTag) return { tag: loadingTag, props: {}, children: [] }
|
|
63
|
-
|
|
64
|
-
const matched = router.matchRoute(current.value.path)
|
|
65
|
-
const layoutName = (matched?.route as any)?.meta?.layout ?? 'default'
|
|
66
|
-
const layoutTag = (layouts as Record<string, string>)[layoutName]
|
|
67
|
-
const routerView = { tag: 'router-view', props: {}, children: [] }
|
|
68
|
-
return layoutTag ? { tag: layoutTag, props: {}, children: [routerView] } : routerView
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
for (const plugin of plugins) {
|
|
72
|
-
if (plugin && typeof plugin.setup === 'function') {
|
|
73
|
-
await plugin.setup({ router, provide: (key: string, value: unknown) => { _pluginProvides.set(key, value) }, config: {} })
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Pre-load the current page's route chunk AFTER plugins run so that
|
|
78
|
-
// cer-layout-view's first render (which calls provide()) completes before
|
|
79
|
-
// page components are defined. This ensures inject() in child components
|
|
80
|
-
// can find values stored by provide().
|
|
81
|
-
if (typeof window !== 'undefined') {
|
|
82
|
-
const _initMatch = router.matchRoute(window.location.pathname)
|
|
83
|
-
if (_initMatch?.route?.load) {
|
|
84
|
-
try { await _initMatch.route.load() } catch { /* non-fatal */ }
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (typeof window !== 'undefined') {
|
|
89
|
-
// Use the original (unwrapped) replace so isNavigating stays false on first paint.
|
|
90
|
-
await _replace(window.location.pathname + window.location.search + window.location.hash)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export { router }
|