@netrojs/create-vono 0.0.1

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.
@@ -0,0 +1,279 @@
1
+ /* ── Reset & base ──────────────────────────────────────────────── */
2
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0 }
3
+
4
+ :root {
5
+ --bg: #0d0f17;
6
+ --surface: #13161f;
7
+ --surface2: #181c27;
8
+ --border: #1e2330;
9
+ --text: #c8cde0;
10
+ --muted: #555972;
11
+ --accent: #7c6af7;
12
+ --accent2: #5eead4;
13
+ --green: #34d399;
14
+ --red: #f87171;
15
+ --yellow: #fbbf24;
16
+ --radius: 10px;
17
+ --font: 'Inter', system-ui, sans-serif;
18
+ }
19
+
20
+ html { font-size: 16px; scroll-behavior: smooth }
21
+ body { background: var(--bg); color: var(--text); font-family: var(--font);
22
+ line-height: 1.6; min-height: 100vh }
23
+ a { color: var(--accent); text-decoration: none }
24
+ a:hover { text-decoration: underline }
25
+ code { background: var(--surface); border: 1px solid var(--border);
26
+ border-radius: 4px; padding: 2px 6px; font-size: .88em; font-family: monospace }
27
+ .sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden;
28
+ clip: rect(0,0,0,0); white-space: nowrap }
29
+ .muted { color: var(--muted) }
30
+
31
+ /* ── App shell ─────────────────────────────────────────────────── */
32
+ .app { display: flex; flex-direction: column; min-height: 100vh }
33
+ .main { flex: 1; padding: 2.5rem 1.25rem; max-width: 900px; margin: 0 auto; width: 100% }
34
+ .footer { display: flex; align-items: center; justify-content: center; gap: .5rem;
35
+ padding: 1.25rem; font-size: .82rem; color: var(--muted);
36
+ border-top: 1px solid var(--border) }
37
+ .footer-sep { color: var(--border) }
38
+
39
+ /* ── Sticky nav ────────────────────────────────────────────────── */
40
+ .nav { display: flex; align-items: center; gap: 1rem; padding: .9rem 1.5rem;
41
+ background: var(--surface); border-bottom: 1px solid var(--border);
42
+ position: sticky; top: 0; z-index: 100;
43
+ transition: box-shadow .2s }
44
+ .nav.scrolled { box-shadow: 0 4px 20px rgba(0,0,0,.4) }
45
+ .logo { font-weight: 700; font-size: 1.1rem; color: var(--accent2); letter-spacing: -.01em;
46
+ text-decoration: none }
47
+ .nav-links { display: flex; gap: .25rem; margin-left: auto }
48
+ .nav-link { padding: .35rem .75rem; border-radius: 6px; font-size: .9rem; color: var(--text);
49
+ transition: background .15s, color .15s; text-decoration: none }
50
+ .nav-link:hover, .nav-link.active { background: var(--border); color: var(--accent2);
51
+ text-decoration: none }
52
+
53
+ /* Hamburger (mobile) */
54
+ .menu-btn { display: none; background: none; border: none; cursor: pointer;
55
+ padding: .4rem; border-radius: 6px; color: var(--text) }
56
+ .hamburger { display: block; width: 22px; height: 2px; background: currentColor;
57
+ position: relative; transition: background .2s }
58
+ .hamburger::before, .hamburger::after { content: ''; display: block; width: 100%; height: 2px;
59
+ background: currentColor; position: absolute; transition: transform .2s }
60
+ .hamburger::before { top: -7px } .hamburger::after { top: 7px }
61
+ .hamburger.open { background: transparent }
62
+ .hamburger.open::before { transform: rotate(45deg) translate(0, 7px) }
63
+ .hamburger.open::after { transform: rotate(-45deg) translate(0, -7px) }
64
+
65
+ @media (max-width: 640px) {
66
+ .menu-btn { display: block }
67
+ .nav-links { display: none; position: absolute; top: 100%; left: 0; right: 0;
68
+ background: var(--surface); border-bottom: 1px solid var(--border);
69
+ flex-direction: column; padding: .75rem 1rem }
70
+ .nav-links.open { display: flex }
71
+ }
72
+
73
+ /* ── Page ──────────────────────────────────────────────────────── */
74
+ .page { padding: 1rem 0 }
75
+ .page h1 { font-size: 2rem; font-weight: 700; color: #fff; margin-bottom: .75rem;
76
+ letter-spacing: -.02em }
77
+ .lead { font-size: 1.1rem; color: var(--text); margin-bottom: 1.5rem }
78
+
79
+ /* ── Section ───────────────────────────────────────────────────── */
80
+ .section { margin-bottom: 3.5rem }
81
+ .section-title { font-size: 1.3rem; font-weight: 600; color: #fff;
82
+ margin-bottom: 1.25rem; letter-spacing: -.01em }
83
+
84
+ /* ── Hero ──────────────────────────────────────────────────────── */
85
+ .hero { padding: 3.5rem 0 2.5rem; text-align: center }
86
+ .hero-badge { display: inline-block; font-size: .8rem; font-weight: 600;
87
+ padding: .25rem .85rem; border-radius: 99px;
88
+ background: rgba(124,106,247,.15); color: var(--accent);
89
+ border: 1px solid rgba(124,106,247,.3); margin-bottom: 1.5rem }
90
+ .hero-headline { font-size: clamp(2rem, 5vw, 3rem); font-weight: 800;
91
+ color: #fff; letter-spacing: -.03em; margin-bottom: .75rem;
92
+ line-height: 1.1 }
93
+ .hero-sub { font-size: 1.05rem; color: var(--muted); margin-bottom: 2rem;
94
+ max-width: 500px; margin-inline: auto }
95
+ .hero-actions { display: flex; gap: .75rem; justify-content: center;
96
+ flex-wrap: wrap; margin-bottom: 1.75rem }
97
+
98
+ .stat-row { display: flex; gap: .75rem; justify-content: center; flex-wrap: wrap }
99
+ .stat-chip { font-size: .82rem; padding: .35rem .85rem; border-radius: 99px;
100
+ background: var(--surface); border: 1px solid var(--border); color: var(--text) }
101
+
102
+ /* ── Buttons ───────────────────────────────────────────────────── */
103
+ .btn { display: inline-block; padding: .55rem 1.25rem; border-radius: 8px;
104
+ font-weight: 600; font-size: .9rem; cursor: pointer;
105
+ transition: opacity .15s, background .15s; text-decoration: none !important;
106
+ border: none; line-height: 1.4 }
107
+ .btn:hover { opacity: .88 }
108
+ .btn-primary { background: var(--accent); color: #fff }
109
+ .btn-ghost { background: var(--surface); color: var(--text);
110
+ border: 1px solid var(--border) }
111
+ .btn:disabled { opacity: .5; cursor: not-allowed }
112
+
113
+ /* ── Feature grid ──────────────────────────────────────────────── */
114
+ .feature-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
115
+ gap: 1rem }
116
+ .feature-card { background: var(--surface); border: 1px solid var(--border);
117
+ border-radius: var(--radius); padding: 1.1rem 1.25rem;
118
+ transition: border-color .25s, box-shadow .25s }
119
+ .feature-card.highlight { border-color: var(--accent);
120
+ box-shadow: 0 0 0 1px var(--accent), 0 4px 20px rgba(124,106,247,.15) }
121
+ .feature-icon { font-size: 1.5rem; display: block; margin-bottom: .5rem }
122
+ .feature-title { font-size: .95rem; font-weight: 600; color: #fff; margin-bottom: .3rem }
123
+ .feature-desc { font-size: .83rem; color: var(--muted); line-height: 1.5 }
124
+
125
+ /* ── Post cards ────────────────────────────────────────────────── */
126
+ .post-list { display: flex; flex-direction: column; gap: 1rem }
127
+ .post-card { display: block; background: var(--surface); border: 1px solid var(--border);
128
+ border-radius: var(--radius); padding: 1.1rem 1.25rem; color: inherit;
129
+ text-decoration: none !important; transition: border-color .15s }
130
+ .post-card:hover { border-color: var(--accent); text-decoration: none }
131
+ .post-meta { display: flex; align-items: center; gap: .5rem; font-size: .8rem;
132
+ color: var(--muted); margin-bottom: .5rem; flex-wrap: wrap }
133
+ .post-card-title { font-size: 1.05rem; font-weight: 600; color: #fff; margin-bottom: .35rem }
134
+ .post-card-excerpt { font-size: .9rem; color: var(--text); margin-bottom: .5rem }
135
+ .post-views { font-size: .78rem; color: var(--muted) }
136
+
137
+ /* ── Tags ──────────────────────────────────────────────────────── */
138
+ .tag { font-size: .75rem; padding: .15rem .5rem; border-radius: 4px;
139
+ background: rgba(94,234,212,.1); color: var(--accent2);
140
+ border: 1px solid rgba(94,234,212,.2); cursor: pointer;
141
+ transition: background .15s }
142
+ .tag.active { background: rgba(94,234,212,.2) }
143
+ .tag-warn { background: rgba(251,191,36,.1); color: var(--yellow);
144
+ border-color: rgba(251,191,36,.2) }
145
+
146
+ /* ── Search ────────────────────────────────────────────────────── */
147
+ .search-row { margin-bottom: 1.5rem }
148
+ .search-input { width: 100%; padding: .55rem 1rem; border-radius: 8px;
149
+ background: var(--surface); border: 1px solid var(--border);
150
+ color: var(--text); font-size: .95rem; outline: none;
151
+ margin-bottom: .75rem; transition: border-color .15s }
152
+ .search-input:focus { border-color: var(--accent) }
153
+ .tag-row { display: flex; flex-wrap: wrap; gap: .4rem }
154
+
155
+ /* ── Blog post ─────────────────────────────────────────────────── */
156
+ .post-byline { font-size: .85rem; color: var(--muted); display: flex; gap: .5rem;
157
+ flex-wrap: wrap; margin-bottom: 1rem; align-items: center }
158
+ .prose { margin-top: 1.5rem; line-height: 1.75; color: var(--text) }
159
+ .prose p { margin-bottom: 1.2rem }
160
+ .prose blockquote { border-left: 3px solid var(--accent); padding-left: 1rem;
161
+ color: var(--muted); font-style: italic; margin: 1.5rem 0 }
162
+ .post-footer { margin-top: 2.5rem; padding-top: 1.5rem; border-top: 1px solid var(--border) }
163
+
164
+ /* ── Code block ────────────────────────────────────────────────── */
165
+ .code-block { background: var(--surface); border: 1px solid var(--border);
166
+ border-radius: var(--radius); padding: 1.25rem 1.5rem;
167
+ overflow-x: auto; font-size: .84rem; line-height: 1.7;
168
+ color: var(--accent2); font-family: monospace }
169
+
170
+ /* ── Dashboard layout ──────────────────────────────────────────── */
171
+ .dash-shell { display: flex; min-height: 100vh }
172
+
173
+ .sidebar { width: 220px; flex-shrink: 0; background: var(--surface);
174
+ border-right: 1px solid var(--border);
175
+ display: flex; flex-direction: column; padding: 1.25rem 0 }
176
+ .sidebar-logo { font-weight: 700; color: var(--accent2); font-size: 1.05rem;
177
+ padding: 0 1.25rem 1.5rem; display: block; text-decoration: none }
178
+ .sidebar-nav { flex: 1; display: flex; flex-direction: column; gap: .2rem; padding: 0 .75rem }
179
+ .sidebar-link { display: flex; align-items: center; gap: .65rem; padding: .55rem .75rem;
180
+ border-radius: 7px; font-size: .9rem; color: var(--text);
181
+ text-decoration: none; transition: background .15s, color .15s }
182
+ .sidebar-link:hover, .sidebar-link.active { background: var(--border); color: var(--accent2) }
183
+ .sidebar-icon { font-size: 1rem }
184
+ .sidebar-footer { padding: .75rem; border-top: 1px solid var(--border); margin-top: .75rem }
185
+
186
+ .dash-body { flex: 1; display: flex; flex-direction: column; overflow: hidden }
187
+ .dash-header { display: flex; align-items: center; gap: 1rem; padding: 1rem 1.5rem;
188
+ border-bottom: 1px solid var(--border); background: var(--surface) }
189
+ .dash-title { font-size: 1.1rem; font-weight: 600; color: #fff }
190
+ .dash-badge { font-size: .75rem; padding: .2rem .65rem; border-radius: 99px;
191
+ background: rgba(251,191,36,.1); color: var(--yellow);
192
+ border: 1px solid rgba(251,191,36,.2); margin-left: auto }
193
+ .dash-content { flex: 1; padding: 1.5rem; overflow-y: auto }
194
+ .dash-section { max-width: 860px }
195
+
196
+ /* ── KPI grid ──────────────────────────────────────────────────── */
197
+ .kpi-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
198
+ gap: 1rem; margin-bottom: 1.5rem }
199
+ .kpi-card { background: var(--surface2); border: 1px solid var(--border);
200
+ border-radius: var(--radius); padding: 1rem 1.25rem;
201
+ display: flex; align-items: center; gap: .85rem }
202
+ .kpi-icon { font-size: 1.6rem }
203
+ .kpi-value { font-size: 1.3rem; font-weight: 700; color: #fff; line-height: 1 }
204
+ .kpi-label { font-size: .78rem; color: var(--muted); margin-top: .2rem }
205
+
206
+ /* ── Bar chart ─────────────────────────────────────────────────── */
207
+ .chart-card { background: var(--surface); border: 1px solid var(--border);
208
+ border-radius: var(--radius); padding: 1.25rem; margin-bottom: 1.5rem }
209
+ .chart-header { display: flex; align-items: center; margin-bottom: 1.25rem }
210
+ .chart-title { font-size: .95rem; font-weight: 600; color: #fff; flex: 1 }
211
+ .chart-tabs { display: flex; gap: .25rem }
212
+ .chart-tab { font-size: .8rem; padding: .25rem .65rem; border-radius: 5px; cursor: pointer;
213
+ background: transparent; border: 1px solid var(--border); color: var(--muted);
214
+ transition: background .15s, color .15s }
215
+ .chart-tab.active { background: var(--accent); color: #fff; border-color: var(--accent) }
216
+ .chart-bars { display: flex; align-items: flex-end; gap: .75rem; height: 140px }
217
+ .bar-col { flex: 1; display: flex; flex-direction: column; align-items: center; height: 100% }
218
+ .bar-value { font-size: .7rem; color: var(--muted); margin-bottom: .25rem }
219
+ .bar-wrap { flex: 1; width: 100%; display: flex; align-items: flex-end; min-height: 0 }
220
+ .bar { width: 100%; background: var(--accent); border-radius: 4px 4px 0 0;
221
+ min-height: 4px; transition: height .4s cubic-bezier(.22,1,.36,1) }
222
+ .bar-label { font-size: .72rem; color: var(--muted); margin-top: .4rem }
223
+
224
+ /* ── Info banner ───────────────────────────────────────────────── */
225
+ .info-banner { background: rgba(94,234,212,.06); border: 1px solid rgba(94,234,212,.18);
226
+ border-radius: var(--radius); padding: 1rem 1.25rem; font-size: .85rem;
227
+ color: var(--text); line-height: 1.6; margin-top: 1rem }
228
+
229
+ /* ── Table ─────────────────────────────────────────────────────── */
230
+ .table-toolbar { display: flex; align-items: center; gap: 1rem; margin-bottom: 1rem; flex-wrap: wrap }
231
+ .sort-tabs { display: flex; gap: .25rem }
232
+ .data-table { width: 100%; border-collapse: collapse; font-size: .88rem }
233
+ .data-table th { text-align: left; font-size: .8rem; color: var(--muted); font-weight: 500;
234
+ padding: .6rem .75rem; border-bottom: 1px solid var(--border) }
235
+ .data-table td { padding: .65rem .75rem; border-bottom: 1px solid var(--border);
236
+ vertical-align: middle }
237
+ .data-table tr:last-child td { border-bottom: none }
238
+ .data-table tbody tr:hover { background: var(--surface2) }
239
+
240
+ /* ── Settings form ─────────────────────────────────────────────── */
241
+ .settings-form { max-width: 520px }
242
+ .form-group { margin-bottom: 1.1rem }
243
+ .form-label { display: flex; align-items: center; gap: .5rem; font-size: .9rem;
244
+ color: var(--text); margin-bottom: .4rem; font-weight: 500; cursor: pointer }
245
+ .form-input { width: 100%; padding: .55rem 1rem; border-radius: 8px;
246
+ background: var(--surface); border: 1px solid var(--border);
247
+ color: var(--text); font-size: .95rem; outline: none;
248
+ transition: border-color .15s }
249
+ .form-input:focus { border-color: var(--accent) }
250
+ .form-toggle .form-label { flex-direction: row; gap: .6rem }
251
+ .form-footer { display: flex; align-items: center; gap: 1rem; margin-top: 1.5rem;
252
+ padding-top: 1.25rem; border-top: 1px solid var(--border) }
253
+ .save-confirm { font-size: .88rem; color: var(--green) }
254
+
255
+ /* ── Login page ────────────────────────────────────────────────── */
256
+ .login-shell { min-height: 100vh; display: flex; align-items: center;
257
+ justify-content: center; padding: 1.5rem; background: var(--bg) }
258
+ .login-card { width: 100%; max-width: 380px; background: var(--surface);
259
+ border: 1px solid var(--border); border-radius: 14px; padding: 2rem }
260
+ .login-logo { font-size: 1.3rem; font-weight: 700; color: var(--accent2);
261
+ margin-bottom: 1.25rem; letter-spacing: -.01em }
262
+ .login-title { font-size: 1.4rem; font-weight: 700; color: #fff; margin-bottom: .35rem }
263
+ .login-sub { font-size: .85rem; color: var(--muted); margin-bottom: 1.5rem }
264
+ .login-hint { font-size: .8rem; color: var(--muted); margin-top: 1.25rem; line-height: 1.55 }
265
+ .alert-error { background: rgba(248,113,113,.1); border: 1px solid rgba(248,113,113,.25);
266
+ color: var(--red); border-radius: 7px; padding: .65rem 1rem;
267
+ font-size: .88rem; margin-bottom: 1rem }
268
+
269
+ /* ── 404 ────────────────────────────────────────────────────────── */
270
+ .not-found { display: flex; flex-direction: column; align-items: center;
271
+ justify-content: center; min-height: 100vh; text-align: center; padding: 2rem }
272
+ .not-found-code { font-size: 5rem; font-weight: 800; color: var(--border);
273
+ line-height: 1; margin-bottom: .5rem }
274
+ .not-found h1 { font-size: 1.5rem; color: #fff; margin-bottom: .75rem }
275
+ .not-found p { color: var(--muted); margin-bottom: 2rem }
276
+
277
+ /* ── Transitions ───────────────────────────────────────────────── */
278
+ .fade-enter-active, .fade-leave-active { transition: opacity .25s }
279
+ .fade-enter-from, .fade-leave-to { opacity: 0 }
package/files/app.ts ADDED
@@ -0,0 +1,36 @@
1
+ // ─────────────────────────────────────────────────────────────────────────────
2
+ // app.ts · Hono app factory — used by dev server and imported by server.ts
3
+ //
4
+ // @hono/vite-dev-server requires a Hono instance as the default export.
5
+ // createVono returns { app, handler }; we export both.
6
+ // ─────────────────────────────────────────────────────────────────────────────
7
+
8
+ import { createVono } from '@netrojs/vono/server'
9
+ import { routes, NotFoundPage } from './app/routes'
10
+
11
+ export const vono = createVono({
12
+ routes,
13
+
14
+ // Global SEO defaults — merged with per-page overrides (page wins)
15
+ seo: {
16
+ ogType: 'website',
17
+ ogSiteName: 'Vono Demo',
18
+ twitterCard: 'summary_large_image',
19
+ robots: 'index, follow',
20
+ },
21
+
22
+ // Custom <html> attributes
23
+ htmlAttrs: { lang: 'en', 'data-theme': 'dark' },
24
+
25
+ // Extra <head> HTML injected on every page (e.g. font preloads, analytics)
26
+ head: `
27
+ <link rel="preconnect" href="https://fonts.googleapis.com">
28
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg">
29
+ `,
30
+
31
+ // Rendered for any unmatched URL (server-side 404)
32
+ notFound: NotFoundPage,
33
+ })
34
+
35
+ // Default export: the raw Hono instance — required by @hono/vite-dev-server
36
+ export default vono.app
@@ -0,0 +1,17 @@
1
+ // ─────────────────────────────────────────────────────────────────────────────
2
+ // client.ts · Browser hydration entry
3
+ //
4
+ // boot() hydrates the server-rendered HTML into a Vue SPA.
5
+ // It reads SSR-injected state from window.__VONO_STATE__ so the first
6
+ // paint needs zero network requests.
7
+ // ─────────────────────────────────────────────────────────────────────────────
8
+
9
+ import { boot } from '@netrojs/vono/client'
10
+ import { routes } from './app/routes'
11
+ import './app/style.css'
12
+
13
+ boot({
14
+ routes,
15
+ // Warm the SPA data cache on link hover — snappy navigation feel
16
+ prefetchOnHover: true,
17
+ })
@@ -0,0 +1,13 @@
1
+ // ─────────────────────────────────────────────────────────────────────────────
2
+ // global.d.ts · Ambient type declarations for the Vono demo project
3
+ // ─────────────────────────────────────────────────────────────────────────────
4
+
5
+ // Allow importing CSS files in TypeScript
6
+ declare module '*.css'
7
+
8
+ // Augment the global Window object so TypeScript understands SSR-injected keys
9
+ interface Window {
10
+ __VONO_STATE__: Record<string, Record<string, unknown>>
11
+ __VONO_PARAMS__: Record<string, string>
12
+ __VONO_SEO__: import('@netrojs/vono').SEOMeta
13
+ }
@@ -0,0 +1,18 @@
1
+ // ─────────────────────────────────────────────────────────────────────────────
2
+ // server.ts · Production server entry
3
+ //
4
+ // Top-level await is used here because the SSR bundle is built with
5
+ // target: 'node18' by vonoVitePlugin — which enables it in the output.
6
+ // See packages/vono/server.ts → vonoVitePlugin → config() → target.
7
+ // ─────────────────────────────────────────────────────────────────────────────
8
+
9
+ import { serve } from '@netrojs/vono/server'
10
+ import { vono } from './app'
11
+
12
+ await serve({
13
+ app: vono,
14
+ port: Number(process.env['PORT'] ?? 3000),
15
+ runtime: 'node',
16
+ // Points to your built output folder — serve-static serves /assets/* from here
17
+ staticDir: './dist',
18
+ })
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "lib": ["ESNext", "DOM"],
7
+ "jsx": "preserve",
8
+ "jsxImportSource": "vue",
9
+ "strict": true,
10
+ "skipLibCheck": true,
11
+ "noEmit": true,
12
+ "allowImportingTsExtensions": true,
13
+ "resolveJsonModule": true,
14
+ "isolatedModules": true,
15
+ "verbatimModuleSyntax": true,
16
+ "forceConsistentCasingInFileNames": true
17
+ },
18
+ "include": ["**/*.ts", "**/*.tsx", "**/*.vue", "global.d.ts"],
19
+ "exclude": ["node_modules", "dist"]
20
+ }
@@ -0,0 +1,47 @@
1
+ // ─────────────────────────────────────────────────────────────────────────────
2
+ // vite.config.ts
3
+ //
4
+ // Plugin execution order matters:
5
+ // 1. vue() — transforms .vue files in dev mode + SSR build
6
+ // 2. vonoVitePlugin() — orchestrates the dual build (SSR → client SPA)
7
+ // 3. devServer() — routes dev requests through the Hono app
8
+ //
9
+ // The `target: 'node18'` in vonoVitePlugin's server build config is what
10
+ // enables top-level await in dist/server/server.js. Do not override the
11
+ // build.target here for SSR builds — let the plugin manage it.
12
+ // ─────────────────────────────────────────────────────────────────────────────
13
+
14
+ import { defineConfig } from 'vite'
15
+ import vue from '@vitejs/plugin-vue'
16
+ import { vonoVitePlugin } from '@netrojs/vono/vite'
17
+ import devServer from '@hono/vite-dev-server'
18
+
19
+ export default defineConfig({
20
+ plugins: [
21
+ // Handles .vue SFC transforms in both dev mode and the server SSR build
22
+ vue(),
23
+
24
+ // Orchestrates dual-bundle production build:
25
+ // vite build → dist/server/server.js (SSR, target node18, ES module)
26
+ // closeBundle → dist/assets/… (client SPA, manifest.json)
27
+ vonoVitePlugin({
28
+ serverEntry: 'server.ts',
29
+ clientEntry: 'client.ts',
30
+ serverOutDir: 'dist/server',
31
+ clientOutDir: 'dist/assets',
32
+ }),
33
+
34
+ // Routes all dev-mode requests through the Vono Hono app (app.ts default export)
35
+ devServer({ entry: 'app.ts' }),
36
+ ],
37
+
38
+ server: {
39
+ // Prevent Vite from restarting when dist/ changes during a production build
40
+ watch: { ignored: ['**/dist/**'] },
41
+ },
42
+
43
+ // Optimise these packages in dev so Vite only processes them once
44
+ optimizeDeps: {
45
+ include: ['vue', 'vue-router'],
46
+ },
47
+ })
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@netrojs/create-vono",
3
+ "version": "0.0.1",
4
+ "description": "Create a new Vono app \u2014 Vue 3 streaming SSR + SPA, Hono, TypeScript",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Netro Solutions<info@netrosolutions.com>",
8
+ "homepage": "https://github.com/netrosolutions/vono",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/netrosolutions/vono.git",
12
+ "directory": "packages/create-vono"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/netrosolutions/vono/issues"
16
+ },
17
+ "keywords": [
18
+ "create-vono",
19
+ "vono",
20
+ "hono",
21
+ "vue",
22
+ "vue3",
23
+ "fullstack",
24
+ "ssr",
25
+ "spa"
26
+ ],
27
+ "bin": {
28
+ "create-vono": "./dist/index.js"
29
+ },
30
+ "files": [
31
+ "dist",
32
+ "files",
33
+ "README.md",
34
+ "LICENSE"
35
+ ],
36
+ "scripts": {
37
+ "build": "tsup index.ts --format esm --target node18 --dts --clean --outDir dist",
38
+ "dev": "tsup index.ts --format esm --target node18 --watch --outDir dist",
39
+ "typecheck": "tsc --noEmit"
40
+ },
41
+ "dependencies": {
42
+ "kolorist": "^1.8.0",
43
+ "prompts": "^2.4.2"
44
+ },
45
+ "devDependencies": {
46
+ "@types/node": "^22.0.0",
47
+ "@types/prompts": "^2.4.9",
48
+ "tsup": "^8.5.1",
49
+ "typescript": "^5.9.3"
50
+ },
51
+ "engines": {
52
+ "node": ">=18.0.0"
53
+ }
54
+ }