@zenithbuild/core 0.4.7 → 0.5.0-beta.2.2
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/CORE_CONTRACT.md +143 -0
- package/README.md +11 -31
- package/bin/zenith.js +68 -0
- package/package.json +41 -53
- package/src/config.js +134 -0
- package/src/core-template.js +30 -0
- package/src/errors.js +54 -0
- package/src/guards.js +61 -0
- package/src/hash.js +52 -0
- package/src/index.js +26 -0
- package/src/ir/index.js +1 -0
- package/src/order.js +69 -0
- package/src/path.js +131 -0
- package/src/schema.js +28 -0
- package/src/version.js +67 -0
- package/bin/zen-build.ts +0 -2
- package/bin/zen-dev.ts +0 -2
- package/bin/zen-preview.ts +0 -2
- package/bin/zenith.ts +0 -2
- package/cli/commands/add.ts +0 -37
- package/cli/commands/build.ts +0 -37
- package/cli/commands/create.ts +0 -702
- package/cli/commands/dev.ts +0 -335
- package/cli/commands/index.ts +0 -112
- package/cli/commands/preview.ts +0 -62
- package/cli/commands/remove.ts +0 -33
- package/cli/index.ts +0 -10
- package/cli/main.ts +0 -101
- package/cli/utils/branding.ts +0 -178
- package/cli/utils/content.ts +0 -112
- package/cli/utils/logger.ts +0 -46
- package/cli/utils/plugin-manager.ts +0 -114
- package/cli/utils/project.ts +0 -77
- package/compiler/README.md +0 -380
- package/compiler/build-analyzer.ts +0 -122
- package/compiler/css/index.ts +0 -317
- package/compiler/discovery/componentDiscovery.ts +0 -178
- package/compiler/discovery/layouts.ts +0 -70
- package/compiler/errors/compilerError.ts +0 -56
- package/compiler/finalize/finalizeOutput.ts +0 -192
- package/compiler/finalize/generateFinalBundle.ts +0 -82
- package/compiler/index.ts +0 -82
- package/compiler/ir/types.ts +0 -162
- package/compiler/output/types.ts +0 -34
- package/compiler/parse/detectMapExpressions.ts +0 -102
- package/compiler/parse/parseScript.ts +0 -46
- package/compiler/parse/parseTemplate.ts +0 -599
- package/compiler/parse/parseZenFile.ts +0 -66
- package/compiler/parse/scriptAnalysis.ts +0 -91
- package/compiler/parse/trackLoopContext.ts +0 -82
- package/compiler/runtime/dataExposure.ts +0 -317
- package/compiler/runtime/generateDOM.ts +0 -246
- package/compiler/runtime/generateHydrationBundle.ts +0 -407
- package/compiler/runtime/hydration.ts +0 -309
- package/compiler/runtime/navigation.ts +0 -432
- package/compiler/runtime/thinRuntime.ts +0 -160
- package/compiler/runtime/transformIR.ts +0 -363
- package/compiler/runtime/wrapExpression.ts +0 -95
- package/compiler/runtime/wrapExpressionWithLoop.ts +0 -83
- package/compiler/spa-build.ts +0 -917
- package/compiler/ssg-build.ts +0 -422
- package/compiler/test/validate-test.ts +0 -104
- package/compiler/transform/classifyExpression.ts +0 -444
- package/compiler/transform/componentResolver.ts +0 -312
- package/compiler/transform/componentScriptTransformer.ts +0 -147
- package/compiler/transform/expressionTransformer.ts +0 -385
- package/compiler/transform/fragmentLowering.ts +0 -634
- package/compiler/transform/generateBindings.ts +0 -47
- package/compiler/transform/generateHTML.ts +0 -28
- package/compiler/transform/layoutProcessor.ts +0 -132
- package/compiler/transform/slotResolver.ts +0 -292
- package/compiler/transform/transformNode.ts +0 -126
- package/compiler/transform/transformTemplate.ts +0 -38
- package/compiler/validate/invariants.ts +0 -292
- package/compiler/validate/validateExpressions.ts +0 -168
- package/core/config/index.ts +0 -16
- package/core/config/loader.ts +0 -69
- package/core/config/types.ts +0 -89
- package/core/index.ts +0 -135
- package/core/lifecycle/index.ts +0 -49
- package/core/lifecycle/zen-mount.ts +0 -182
- package/core/lifecycle/zen-unmount.ts +0 -88
- package/core/plugins/index.ts +0 -7
- package/core/plugins/registry.ts +0 -81
- package/core/reactivity/index.ts +0 -54
- package/core/reactivity/tracking.ts +0 -167
- package/core/reactivity/zen-batch.ts +0 -57
- package/core/reactivity/zen-effect.ts +0 -139
- package/core/reactivity/zen-memo.ts +0 -146
- package/core/reactivity/zen-ref.ts +0 -52
- package/core/reactivity/zen-signal.ts +0 -121
- package/core/reactivity/zen-state.ts +0 -180
- package/core/reactivity/zen-untrack.ts +0 -44
- package/dist/cli.js +0 -11659
- package/dist/zen-build.js +0 -15633
- package/dist/zen-dev.js +0 -15633
- package/dist/zen-preview.js +0 -15633
- package/dist/zenith.js +0 -15633
- package/router/index.ts +0 -76
- package/router/manifest.ts +0 -314
- package/router/navigation/ZenLink.zen +0 -231
- package/router/navigation/index.ts +0 -78
- package/router/navigation/zen-link.ts +0 -584
- package/router/runtime.ts +0 -458
- package/router/types.ts +0 -168
- package/runtime/build.ts +0 -17
- package/runtime/bundle-generator.ts +0 -943
- package/runtime/client-runtime.ts +0 -549
- package/runtime/serve.ts +0 -93
- package/tsconfig.json +0 -28
package/cli/commands/dev.ts
DELETED
|
@@ -1,335 +0,0 @@
|
|
|
1
|
-
import path from 'path'
|
|
2
|
-
import fs from 'fs'
|
|
3
|
-
import { serve, type ServerWebSocket } from 'bun'
|
|
4
|
-
import { requireProject } from '../utils/project'
|
|
5
|
-
import * as logger from '../utils/logger'
|
|
6
|
-
import * as brand from '../utils/branding'
|
|
7
|
-
import { compileZenSource } from '../../compiler/index'
|
|
8
|
-
import { discoverLayouts } from '../../compiler/discovery/layouts'
|
|
9
|
-
import { processLayout } from '../../compiler/transform/layoutProcessor'
|
|
10
|
-
import { generateRouteDefinition } from '../../router/manifest'
|
|
11
|
-
import { generateBundleJS } from '../../runtime/bundle-generator'
|
|
12
|
-
import { loadContent } from '../utils/content'
|
|
13
|
-
import { loadZenithConfig } from '../../core/config/loader'
|
|
14
|
-
import { PluginRegistry, createPluginContext } from '../../core/plugins/registry'
|
|
15
|
-
import type { ContentItem } from '../../core/config/types'
|
|
16
|
-
import { compileCssAsync, resolveGlobalsCss } from '../../compiler/css'
|
|
17
|
-
|
|
18
|
-
export interface DevOptions {
|
|
19
|
-
port?: number
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
interface CompiledPage {
|
|
23
|
-
html: string
|
|
24
|
-
script: string
|
|
25
|
-
styles: string[]
|
|
26
|
-
route: string
|
|
27
|
-
lastModified: number
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const pageCache = new Map<string, CompiledPage>()
|
|
31
|
-
|
|
32
|
-
export async function dev(options: DevOptions = {}): Promise<void> {
|
|
33
|
-
const project = requireProject()
|
|
34
|
-
const port = options.port || parseInt(process.env.PORT || '3000', 10)
|
|
35
|
-
const pagesDir = project.pagesDir
|
|
36
|
-
const rootDir = project.root
|
|
37
|
-
const contentDir = path.join(rootDir, 'content')
|
|
38
|
-
|
|
39
|
-
// Load zenith.config.ts if present
|
|
40
|
-
const config = await loadZenithConfig(rootDir)
|
|
41
|
-
const registry = new PluginRegistry()
|
|
42
|
-
|
|
43
|
-
console.log('[Zenith] Config plugins:', config.plugins?.length ?? 0)
|
|
44
|
-
|
|
45
|
-
// Register plugins from config
|
|
46
|
-
for (const plugin of config.plugins || []) {
|
|
47
|
-
console.log('[Zenith] Registering plugin:', plugin.name)
|
|
48
|
-
registry.register(plugin)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Initialize content data
|
|
52
|
-
let contentData: Record<string, ContentItem[]> = {}
|
|
53
|
-
|
|
54
|
-
// Initialize plugins with context
|
|
55
|
-
const hasContentPlugin = registry.has('zenith-content')
|
|
56
|
-
console.log('[Zenith] Has zenith-content plugin:', hasContentPlugin)
|
|
57
|
-
|
|
58
|
-
if (hasContentPlugin) {
|
|
59
|
-
await registry.initAll(createPluginContext(rootDir, (data) => {
|
|
60
|
-
console.log('[Zenith] Content plugin set data, collections:', Object.keys(data))
|
|
61
|
-
contentData = data
|
|
62
|
-
}))
|
|
63
|
-
} else {
|
|
64
|
-
// Fallback to legacy content loading if no content plugin configured
|
|
65
|
-
console.log('[Zenith] Using legacy content loading from:', contentDir)
|
|
66
|
-
contentData = loadContent(contentDir)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
console.log('[Zenith] Content collections loaded:', Object.keys(contentData))
|
|
70
|
-
|
|
71
|
-
// ============================================
|
|
72
|
-
// CSS Compilation (Compiler-Owned)
|
|
73
|
-
// ============================================
|
|
74
|
-
const globalsCssPath = resolveGlobalsCss(rootDir)
|
|
75
|
-
let compiledCss = ''
|
|
76
|
-
|
|
77
|
-
if (globalsCssPath) {
|
|
78
|
-
console.log('[Zenith] Compiling CSS:', path.relative(rootDir, globalsCssPath))
|
|
79
|
-
const cssResult = await compileCssAsync({ input: globalsCssPath, output: ':memory:' })
|
|
80
|
-
if (cssResult.success) {
|
|
81
|
-
compiledCss = cssResult.css
|
|
82
|
-
console.log(`[Zenith] CSS compiled in ${cssResult.duration}ms`)
|
|
83
|
-
} else {
|
|
84
|
-
console.error('[Zenith] CSS compilation failed:', cssResult.error)
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const clients = new Set<ServerWebSocket<unknown>>()
|
|
89
|
-
|
|
90
|
-
// Branded Startup Panel
|
|
91
|
-
brand.showServerPanel({
|
|
92
|
-
project: project.root,
|
|
93
|
-
pages: project.pagesDir,
|
|
94
|
-
url: `http://localhost:${port}`,
|
|
95
|
-
hmr: true,
|
|
96
|
-
mode: 'In-memory compilation'
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
// File extensions that should be served as static assets
|
|
100
|
-
const STATIC_EXTENSIONS = new Set([
|
|
101
|
-
'.js', '.css', '.ico', '.png', '.jpg', '.jpeg', '.gif', '.svg',
|
|
102
|
-
'.webp', '.woff', '.woff2', '.ttf', '.eot', '.json', '.map'
|
|
103
|
-
])
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Compile a .zen page in memory
|
|
107
|
-
*/
|
|
108
|
-
function compilePageInMemory(pagePath: string): CompiledPage | null {
|
|
109
|
-
try {
|
|
110
|
-
const layoutsDir = path.join(pagesDir, '../layouts')
|
|
111
|
-
const componentsDir = path.join(pagesDir, '../components')
|
|
112
|
-
const layouts = discoverLayouts(layoutsDir)
|
|
113
|
-
const source = fs.readFileSync(pagePath, 'utf-8')
|
|
114
|
-
|
|
115
|
-
let processedSource = source
|
|
116
|
-
let layoutToUse = layouts.get('DefaultLayout')
|
|
117
|
-
|
|
118
|
-
if (layoutToUse) processedSource = processLayout(source, layoutToUse)
|
|
119
|
-
|
|
120
|
-
const result = compileZenSource(processedSource, pagePath, {
|
|
121
|
-
componentsDir: fs.existsSync(componentsDir) ? componentsDir : undefined
|
|
122
|
-
})
|
|
123
|
-
if (!result.finalized) throw new Error('Compilation failed')
|
|
124
|
-
|
|
125
|
-
const routeDef = generateRouteDefinition(pagePath, pagesDir)
|
|
126
|
-
|
|
127
|
-
return {
|
|
128
|
-
html: result.finalized.html,
|
|
129
|
-
script: result.finalized.js,
|
|
130
|
-
styles: result.finalized.styles,
|
|
131
|
-
route: routeDef.path,
|
|
132
|
-
lastModified: Date.now()
|
|
133
|
-
}
|
|
134
|
-
} catch (error: any) {
|
|
135
|
-
logger.error(`Compilation error: ${error.message}`)
|
|
136
|
-
return null
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Set up file watching for HMR
|
|
141
|
-
const watcher = fs.watch(path.join(pagesDir, '..'), { recursive: true }, async (event, filename) => {
|
|
142
|
-
if (!filename) return
|
|
143
|
-
|
|
144
|
-
if (filename.endsWith('.zen')) {
|
|
145
|
-
logger.hmr('Page', filename)
|
|
146
|
-
|
|
147
|
-
// Clear page cache to force fresh compilation on next request
|
|
148
|
-
pageCache.clear()
|
|
149
|
-
|
|
150
|
-
// Recompile CSS for new Tailwind classes in .zen files
|
|
151
|
-
if (globalsCssPath) {
|
|
152
|
-
const cssResult = await compileCssAsync({ input: globalsCssPath, output: ':memory:' })
|
|
153
|
-
if (cssResult.success) {
|
|
154
|
-
compiledCss = cssResult.css
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Broadcast page reload AFTER cache cleared and CSS ready
|
|
159
|
-
for (const client of clients) {
|
|
160
|
-
client.send(JSON.stringify({ type: 'reload' }))
|
|
161
|
-
}
|
|
162
|
-
} else if (filename.endsWith('.css')) {
|
|
163
|
-
logger.hmr('CSS', filename)
|
|
164
|
-
// Recompile CSS
|
|
165
|
-
if (globalsCssPath) {
|
|
166
|
-
const cssResult = await compileCssAsync({ input: globalsCssPath, output: ':memory:' })
|
|
167
|
-
if (cssResult.success) {
|
|
168
|
-
compiledCss = cssResult.css
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
for (const client of clients) {
|
|
172
|
-
client.send(JSON.stringify({ type: 'style-update', url: '/assets/styles.css' }))
|
|
173
|
-
}
|
|
174
|
-
} else if (filename.startsWith('content') || filename.includes('zenith-docs')) {
|
|
175
|
-
logger.hmr('Content', filename)
|
|
176
|
-
// Reinitialize content plugin to reload data
|
|
177
|
-
if (registry.has('zenith-content')) {
|
|
178
|
-
registry.initAll(createPluginContext(rootDir, (data) => {
|
|
179
|
-
contentData = data
|
|
180
|
-
}))
|
|
181
|
-
} else {
|
|
182
|
-
contentData = loadContent(contentDir)
|
|
183
|
-
}
|
|
184
|
-
for (const client of clients) {
|
|
185
|
-
client.send(JSON.stringify({ type: 'reload' }))
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
})
|
|
189
|
-
|
|
190
|
-
const server = serve({
|
|
191
|
-
port,
|
|
192
|
-
fetch(req, server) {
|
|
193
|
-
const startTime = performance.now()
|
|
194
|
-
const url = new URL(req.url)
|
|
195
|
-
const pathname = url.pathname
|
|
196
|
-
const ext = path.extname(pathname).toLowerCase()
|
|
197
|
-
|
|
198
|
-
// Upgrade to WebSocket for HMR
|
|
199
|
-
if (pathname === '/hmr') {
|
|
200
|
-
const upgraded = server.upgrade(req)
|
|
201
|
-
if (upgraded) return undefined
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Handle Zenith assets
|
|
205
|
-
if (pathname === '/runtime.js') {
|
|
206
|
-
const response = new Response(generateBundleJS(), {
|
|
207
|
-
headers: { 'Content-Type': 'application/javascript; charset=utf-8' }
|
|
208
|
-
})
|
|
209
|
-
logger.route('GET', pathname, 200, Math.round(performance.now() - startTime), 0, Math.round(performance.now() - startTime))
|
|
210
|
-
return response
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Serve compiler-owned CSS (Tailwind compiled)
|
|
214
|
-
if (pathname === '/assets/styles.css') {
|
|
215
|
-
const response = new Response(compiledCss, {
|
|
216
|
-
headers: { 'Content-Type': 'text/css; charset=utf-8' }
|
|
217
|
-
})
|
|
218
|
-
logger.route('GET', pathname, 200, Math.round(performance.now() - startTime), 0, Math.round(performance.now() - startTime))
|
|
219
|
-
return response
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// Legacy: also support /styles/globals.css or /styles/global.css for backwards compat
|
|
223
|
-
if (pathname === '/styles/globals.css' || pathname === '/styles/global.css') {
|
|
224
|
-
const response = new Response(compiledCss, {
|
|
225
|
-
headers: { 'Content-Type': 'text/css; charset=utf-8' }
|
|
226
|
-
})
|
|
227
|
-
logger.route('GET', pathname, 200, Math.round(performance.now() - startTime), 0, Math.round(performance.now() - startTime))
|
|
228
|
-
return response
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Static files
|
|
232
|
-
if (STATIC_EXTENSIONS.has(ext)) {
|
|
233
|
-
const publicPath = path.join(pagesDir, '../public', pathname)
|
|
234
|
-
if (fs.existsSync(publicPath)) {
|
|
235
|
-
const response = new Response(Bun.file(publicPath))
|
|
236
|
-
logger.route('GET', pathname, 200, Math.round(performance.now() - startTime), 0, Math.round(performance.now() - startTime))
|
|
237
|
-
return response
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// Zenith Pages
|
|
242
|
-
const pagePath = findPageForRoute(pathname, pagesDir)
|
|
243
|
-
if (pagePath) {
|
|
244
|
-
const compileStart = performance.now()
|
|
245
|
-
let cached = pageCache.get(pagePath)
|
|
246
|
-
const stat = fs.statSync(pagePath)
|
|
247
|
-
|
|
248
|
-
if (!cached || stat.mtimeMs > cached.lastModified) {
|
|
249
|
-
cached = compilePageInMemory(pagePath) || undefined
|
|
250
|
-
if (cached) pageCache.set(pagePath, cached)
|
|
251
|
-
}
|
|
252
|
-
const compileEnd = performance.now()
|
|
253
|
-
|
|
254
|
-
if (cached) {
|
|
255
|
-
const renderStart = performance.now()
|
|
256
|
-
const html = generateDevHTML(cached, contentData)
|
|
257
|
-
const renderEnd = performance.now()
|
|
258
|
-
|
|
259
|
-
const totalTime = Math.round(performance.now() - startTime)
|
|
260
|
-
const compileTime = Math.round(compileEnd - compileStart)
|
|
261
|
-
const renderTime = Math.round(renderEnd - renderStart)
|
|
262
|
-
|
|
263
|
-
logger.route('GET', pathname, 200, totalTime, compileTime, renderTime)
|
|
264
|
-
return new Response(html, { headers: { 'Content-Type': 'text/html' } })
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
logger.route('GET', pathname, 404, Math.round(performance.now() - startTime), 0, 0)
|
|
269
|
-
return new Response('Not Found', { status: 404 })
|
|
270
|
-
},
|
|
271
|
-
websocket: {
|
|
272
|
-
open(ws) {
|
|
273
|
-
clients.add(ws)
|
|
274
|
-
},
|
|
275
|
-
close(ws) {
|
|
276
|
-
clients.delete(ws)
|
|
277
|
-
},
|
|
278
|
-
message() { }
|
|
279
|
-
}
|
|
280
|
-
})
|
|
281
|
-
|
|
282
|
-
process.on('SIGINT', () => {
|
|
283
|
-
watcher.close()
|
|
284
|
-
server.stop()
|
|
285
|
-
process.exit(0)
|
|
286
|
-
})
|
|
287
|
-
|
|
288
|
-
await new Promise(() => { })
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
function findPageForRoute(route: string, pagesDir: string): string | null {
|
|
292
|
-
// 1. Try exact match first (e.g., /about -> about.zen)
|
|
293
|
-
const exactPath = path.join(pagesDir, route === '/' ? 'index.zen' : `${route.slice(1)}.zen`)
|
|
294
|
-
if (fs.existsSync(exactPath)) return exactPath
|
|
295
|
-
|
|
296
|
-
// 2. Try index.zen in directory (e.g., /about -> about/index.zen)
|
|
297
|
-
const indexPath = path.join(pagesDir, route === '/' ? 'index.zen' : `${route.slice(1)}/index.zen`)
|
|
298
|
-
if (fs.existsSync(indexPath)) return indexPath
|
|
299
|
-
|
|
300
|
-
// 3. Try dynamic routes [slug].zen, [...slug].zen
|
|
301
|
-
// Walk up the path looking for dynamic segments
|
|
302
|
-
const segments = route === '/' ? [] : route.slice(1).split('/').filter(Boolean)
|
|
303
|
-
|
|
304
|
-
// Try matching with dynamic [slug].zen at each level
|
|
305
|
-
for (let i = segments.length - 1; i >= 0; i--) {
|
|
306
|
-
const staticPart = segments.slice(0, i).join('/')
|
|
307
|
-
const baseDir = staticPart ? path.join(pagesDir, staticPart) : pagesDir
|
|
308
|
-
|
|
309
|
-
// Check for [slug].zen (single segment catch)
|
|
310
|
-
const singleDynamicPath = path.join(baseDir, '[slug].zen')
|
|
311
|
-
if (fs.existsSync(singleDynamicPath)) return singleDynamicPath
|
|
312
|
-
|
|
313
|
-
// Check for [...slug].zen (catch-all)
|
|
314
|
-
const catchAllPath = path.join(baseDir, '[...slug].zen')
|
|
315
|
-
if (fs.existsSync(catchAllPath)) return catchAllPath
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// 4. Check for catch-all at root
|
|
319
|
-
const rootCatchAll = path.join(pagesDir, '[...slug].zen')
|
|
320
|
-
if (fs.existsSync(rootCatchAll)) return rootCatchAll
|
|
321
|
-
|
|
322
|
-
return null
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
function generateDevHTML(page: CompiledPage, contentData: any = {}): string {
|
|
326
|
-
const runtimeTag = `<script src="/runtime.js"></script>`
|
|
327
|
-
// Escape </script> sequences in JSON content to prevent breaking the script tag
|
|
328
|
-
const contentJson = JSON.stringify(contentData).replace(/<\//g, '<\\/')
|
|
329
|
-
const contentTag = `<script>window.__ZENITH_CONTENT__ = ${contentJson};</script>`
|
|
330
|
-
const scriptTag = `<script>\n${page.script}\n</script>`
|
|
331
|
-
const allScripts = `${runtimeTag}\n${contentTag}\n${scriptTag}`
|
|
332
|
-
return page.html.includes('</body>')
|
|
333
|
-
? page.html.replace('</body>', `${allScripts}\n</body>`)
|
|
334
|
-
: `${page.html}\n${allScripts}`
|
|
335
|
-
}
|
package/cli/commands/index.ts
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @zenith/cli - Command Registry
|
|
3
|
-
*
|
|
4
|
-
* Central registry for all CLI commands
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { dev, type DevOptions } from './dev'
|
|
8
|
-
import { preview, type PreviewOptions } from './preview'
|
|
9
|
-
import { build, type BuildOptions } from './build'
|
|
10
|
-
import { add, type AddOptions } from './add'
|
|
11
|
-
import { remove } from './remove'
|
|
12
|
-
import { create } from './create'
|
|
13
|
-
import * as logger from '../utils/logger'
|
|
14
|
-
|
|
15
|
-
export interface Command {
|
|
16
|
-
name: string
|
|
17
|
-
description: string
|
|
18
|
-
usage: string
|
|
19
|
-
run: (args: string[], options: Record<string, string>) => Promise<void>
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export const commands: Command[] = [
|
|
23
|
-
{
|
|
24
|
-
name: 'create',
|
|
25
|
-
description: 'Create a new Zenith project',
|
|
26
|
-
usage: 'zenith create [project-name]',
|
|
27
|
-
async run(args) {
|
|
28
|
-
const projectName = args[0]
|
|
29
|
-
await create(projectName)
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
name: 'dev',
|
|
34
|
-
description: 'Start development server',
|
|
35
|
-
usage: 'zenith dev [--port <port>]',
|
|
36
|
-
async run(args, options) {
|
|
37
|
-
const opts: DevOptions = {}
|
|
38
|
-
if (options.port) opts.port = parseInt(options.port, 10)
|
|
39
|
-
await dev(opts)
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
name: 'preview',
|
|
44
|
-
description: 'Preview production build',
|
|
45
|
-
usage: 'zenith preview [--port <port>]',
|
|
46
|
-
async run(args, options) {
|
|
47
|
-
const opts: PreviewOptions = {}
|
|
48
|
-
if (options.port) opts.port = parseInt(options.port, 10)
|
|
49
|
-
await preview(opts)
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
name: 'build',
|
|
54
|
-
description: 'Build for production',
|
|
55
|
-
usage: 'zenith build [--outDir <dir>]',
|
|
56
|
-
async run(args, options) {
|
|
57
|
-
const opts: BuildOptions = {}
|
|
58
|
-
if (options.outDir) opts.outDir = options.outDir
|
|
59
|
-
await build(opts)
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
name: 'add',
|
|
64
|
-
description: 'Add a plugin',
|
|
65
|
-
usage: 'zenith add <plugin>',
|
|
66
|
-
async run(args) {
|
|
67
|
-
const pluginName = args[0]
|
|
68
|
-
if (!pluginName) {
|
|
69
|
-
logger.error('Plugin name required')
|
|
70
|
-
process.exit(1)
|
|
71
|
-
}
|
|
72
|
-
await add(pluginName)
|
|
73
|
-
}
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
name: 'remove',
|
|
77
|
-
description: 'Remove a plugin',
|
|
78
|
-
usage: 'zenith remove <plugin>',
|
|
79
|
-
async run(args) {
|
|
80
|
-
const pluginName = args[0]
|
|
81
|
-
if (!pluginName) {
|
|
82
|
-
logger.error('Plugin name required')
|
|
83
|
-
process.exit(1)
|
|
84
|
-
}
|
|
85
|
-
await remove(pluginName)
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
]
|
|
89
|
-
|
|
90
|
-
// Placeholder commands for future expansion
|
|
91
|
-
export const placeholderCommands = ['test', 'export', 'deploy']
|
|
92
|
-
|
|
93
|
-
export function getCommand(name: string): Command | undefined {
|
|
94
|
-
return commands.find(c => c.name === name)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export function showHelp(): void {
|
|
98
|
-
logger.header('Zenith CLI')
|
|
99
|
-
console.log('Usage: zenith <command> [options]\n')
|
|
100
|
-
console.log('Commands:')
|
|
101
|
-
|
|
102
|
-
for (const cmd of commands) {
|
|
103
|
-
console.log(` ${cmd.name.padEnd(12)} ${cmd.description}`)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
console.log('\nComing soon:')
|
|
107
|
-
for (const cmd of placeholderCommands) {
|
|
108
|
-
console.log(` ${cmd.padEnd(12)} (not yet implemented)`)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
console.log('\nRun `zenith <command> --help` for command-specific help.')
|
|
112
|
-
}
|
package/cli/commands/preview.ts
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @zenith/cli - Preview Command
|
|
3
|
-
*
|
|
4
|
-
* Serves the production build from the distribution directory.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import path from 'path'
|
|
8
|
-
import { serve } from 'bun'
|
|
9
|
-
import { requireProject } from '../utils/project'
|
|
10
|
-
import * as logger from '../utils/logger'
|
|
11
|
-
|
|
12
|
-
export interface PreviewOptions {
|
|
13
|
-
port?: number
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export async function preview(options: PreviewOptions = {}): Promise<void> {
|
|
17
|
-
const project = requireProject()
|
|
18
|
-
const distDir = project.distDir
|
|
19
|
-
const port = options.port || parseInt(process.env.PORT || '4173', 10)
|
|
20
|
-
|
|
21
|
-
logger.header('Zenith Preview Server')
|
|
22
|
-
logger.log(`Serving: ${distDir}`)
|
|
23
|
-
|
|
24
|
-
// File extensions that should be served as static assets
|
|
25
|
-
const STATIC_EXTENSIONS = new Set([
|
|
26
|
-
'.js', '.css', '.ico', '.png', '.jpg', '.jpeg', '.gif', '.svg',
|
|
27
|
-
'.webp', '.woff', '.woff2', '.ttf', '.eot', '.json', '.map'
|
|
28
|
-
])
|
|
29
|
-
|
|
30
|
-
const server = serve({
|
|
31
|
-
port,
|
|
32
|
-
async fetch(req) {
|
|
33
|
-
const url = new URL(req.url)
|
|
34
|
-
const pathname = url.pathname
|
|
35
|
-
const ext = path.extname(pathname).toLowerCase()
|
|
36
|
-
|
|
37
|
-
if (STATIC_EXTENSIONS.has(ext)) {
|
|
38
|
-
const filePath = path.join(distDir, pathname)
|
|
39
|
-
const file = Bun.file(filePath)
|
|
40
|
-
if (await file.exists()) {
|
|
41
|
-
return new Response(file)
|
|
42
|
-
}
|
|
43
|
-
return new Response('Not found', { status: 404 })
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const indexPath = path.join(distDir, 'index.html')
|
|
47
|
-
const indexFile = Bun.file(indexPath)
|
|
48
|
-
if (await indexFile.exists()) {
|
|
49
|
-
return new Response(indexFile, {
|
|
50
|
-
headers: { 'Content-Type': 'text/html; charset=utf-8' }
|
|
51
|
-
})
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return new Response('No production build found. Run `zenith build` first.', { status: 500 })
|
|
55
|
-
}
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
logger.success(`Preview server running at http://localhost:${server.port}`)
|
|
59
|
-
logger.info('Press Ctrl+C to stop')
|
|
60
|
-
|
|
61
|
-
await new Promise(() => { })
|
|
62
|
-
}
|
package/cli/commands/remove.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @zenith/cli - Remove Command
|
|
3
|
-
*
|
|
4
|
-
* Removes a plugin from the project registry
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { requireProject } from '../utils/project'
|
|
8
|
-
import { removePlugin, hasPlugin } from '../utils/plugin-manager'
|
|
9
|
-
import * as logger from '../utils/logger'
|
|
10
|
-
|
|
11
|
-
export async function remove(pluginName: string): Promise<void> {
|
|
12
|
-
requireProject()
|
|
13
|
-
|
|
14
|
-
logger.header('Remove Plugin')
|
|
15
|
-
|
|
16
|
-
if (!pluginName) {
|
|
17
|
-
logger.error('Plugin name required. Usage: zenith remove <plugin>')
|
|
18
|
-
process.exit(1)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (!hasPlugin(pluginName)) {
|
|
22
|
-
logger.warn(`Plugin "${pluginName}" is not registered`)
|
|
23
|
-
return
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const success = removePlugin(pluginName)
|
|
27
|
-
|
|
28
|
-
if (success) {
|
|
29
|
-
logger.info(`Plugin "${pluginName}" has been unregistered.`)
|
|
30
|
-
logger.info('Note: You may want to remove the package manually:')
|
|
31
|
-
logger.log(` bun remove @zenith/plugin-${pluginName}`)
|
|
32
|
-
}
|
|
33
|
-
}
|
package/cli/index.ts
DELETED
package/cli/main.ts
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @zenith/cli - Shared CLI Execution Logic
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import process from 'node:process'
|
|
6
|
-
import { getCommand, showHelp, placeholderCommands } from './commands/index'
|
|
7
|
-
import * as logger from './utils/logger'
|
|
8
|
-
import { execSync } from 'node:child_process'
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Check if Bun is available in the environment
|
|
12
|
-
*/
|
|
13
|
-
function checkBun() {
|
|
14
|
-
try {
|
|
15
|
-
execSync('bun --version', { stdio: 'pipe' })
|
|
16
|
-
return true
|
|
17
|
-
} catch {
|
|
18
|
-
return false
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface CLIOptions {
|
|
23
|
-
defaultCommand?: string
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Main CLI execution entry point
|
|
28
|
-
*/
|
|
29
|
-
export async function runCLI(options: CLIOptions = {}) {
|
|
30
|
-
// 1. Check for Bun
|
|
31
|
-
if (!checkBun()) {
|
|
32
|
-
logger.error('Bun is required to run Zenith.')
|
|
33
|
-
logger.info('Please install Bun: https://bun.sh/install')
|
|
34
|
-
process.exit(1)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const args = process.argv.slice(2)
|
|
38
|
-
const VERSION = '0.3.0'
|
|
39
|
-
|
|
40
|
-
// 2. Handle global version flag
|
|
41
|
-
if (args.includes('--version') || args.includes('-v')) {
|
|
42
|
-
console.log(`Zenith CLI v${VERSION}`)
|
|
43
|
-
process.exit(0)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Determine command name: either from args or default (for aliases)
|
|
47
|
-
let commandName = args[0]
|
|
48
|
-
let commandArgs = args.slice(1)
|
|
49
|
-
|
|
50
|
-
if (options.defaultCommand) {
|
|
51
|
-
if (!commandName || commandName.startsWith('-')) {
|
|
52
|
-
commandName = options.defaultCommand
|
|
53
|
-
commandArgs = args
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Handle help
|
|
58
|
-
if (!commandName || ((commandName === '--help' || commandName === '-h') && !options.defaultCommand)) {
|
|
59
|
-
showHelp()
|
|
60
|
-
process.exit(0)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Parse options (--key value format) for internal use if needed
|
|
64
|
-
const cliOptions: Record<string, string> = {}
|
|
65
|
-
for (let i = 0; i < commandArgs.length; i++) {
|
|
66
|
-
const arg = commandArgs[i]!
|
|
67
|
-
if (arg.startsWith('--')) {
|
|
68
|
-
const key = arg.slice(2)
|
|
69
|
-
const value = commandArgs[i + 1]
|
|
70
|
-
if (value && !value.startsWith('--')) {
|
|
71
|
-
cliOptions[key] = value
|
|
72
|
-
i++
|
|
73
|
-
} else {
|
|
74
|
-
cliOptions[key] = 'true'
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Check for placeholder commands
|
|
80
|
-
if (placeholderCommands.includes(commandName)) {
|
|
81
|
-
logger.warn(`Command "${commandName}" is not yet implemented.`)
|
|
82
|
-
logger.info('This feature is planned for a future release.')
|
|
83
|
-
process.exit(0)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const command = getCommand(commandName)
|
|
87
|
-
|
|
88
|
-
if (!command) {
|
|
89
|
-
logger.error(`Unknown command: ${commandName}`)
|
|
90
|
-
showHelp()
|
|
91
|
-
process.exit(1)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
try {
|
|
95
|
-
await command!.run(commandArgs, cliOptions)
|
|
96
|
-
} catch (err: unknown) {
|
|
97
|
-
const message = err instanceof Error ? err.message : String(err)
|
|
98
|
-
logger.error(message)
|
|
99
|
-
process.exit(1)
|
|
100
|
-
}
|
|
101
|
-
}
|