@zenithbuild/cli 1.3.4 → 1.3.7
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/dist/zen-build.js +10302 -11036
- package/dist/zen-dev.js +10302 -11036
- package/dist/zen-preview.js +10302 -11036
- package/dist/zenith.js +10302 -11036
- package/package.json +7 -6
- package/src/commands/build.ts +1 -1
- package/src/commands/dev.ts +28 -9
- package/src/discovery/componentDiscovery.ts +98 -0
- package/src/discovery/layouts.ts +50 -0
- package/src/serve.ts +92 -0
- package/src/spa-build.ts +931 -0
- package/src/ssg-build.ts +548 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zenithbuild/cli",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.7",
|
|
4
4
|
"description": "CLI for Zenith framework - dev server, build tools, and plugin management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -50,15 +50,16 @@
|
|
|
50
50
|
},
|
|
51
51
|
"private": false,
|
|
52
52
|
"peerDependencies": {
|
|
53
|
-
"@zenithbuild/core": "^1.3.0"
|
|
53
|
+
"@zenithbuild/core": "^1.3.0",
|
|
54
|
+
"@zenithbuild/compiler": "^1.3.0"
|
|
54
55
|
},
|
|
55
56
|
"devDependencies": {
|
|
56
|
-
"@types/bun": "latest"
|
|
57
|
+
"@types/bun": "latest",
|
|
58
|
+
"@zenithbuild/compiler": "^1.3.15"
|
|
57
59
|
},
|
|
58
60
|
"dependencies": {
|
|
59
|
-
"@zenithbuild/
|
|
60
|
-
"@zenithbuild/
|
|
61
|
-
"@zenithbuild/router": "1.3.0",
|
|
61
|
+
"@zenithbuild/bundler": "^1.3.15",
|
|
62
|
+
"@zenithbuild/router": "^1.3.8",
|
|
62
63
|
"glob": "^13.0.0",
|
|
63
64
|
"picocolors": "^1.0.0"
|
|
64
65
|
}
|
package/src/commands/build.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import { requireProject } from '../utils/project'
|
|
8
8
|
import * as logger from '../utils/logger'
|
|
9
|
-
import { buildSSG } from '
|
|
9
|
+
import { buildSSG } from '../ssg-build.ts'
|
|
10
10
|
|
|
11
11
|
export interface BuildOptions {
|
|
12
12
|
outDir?: string
|
package/src/commands/dev.ts
CHANGED
|
@@ -24,18 +24,25 @@ import { serve, type ServerWebSocket } from 'bun'
|
|
|
24
24
|
import { requireProject } from '../utils/project'
|
|
25
25
|
import * as logger from '../utils/logger'
|
|
26
26
|
import * as brand from '../utils/branding'
|
|
27
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
generateRuntime,
|
|
29
|
+
generateBundleJS,
|
|
30
|
+
compileCssAsync,
|
|
31
|
+
resolveGlobalsCss,
|
|
32
|
+
bundlePageScript
|
|
33
|
+
} from '@zenithbuild/bundler'
|
|
34
|
+
import type { ZenManifest } from '@zenithbuild/bundler'
|
|
35
|
+
import { generateRouteDefinition } from '@zenithbuild/router/manifest'
|
|
28
36
|
import { compile } from '@zenithbuild/compiler'
|
|
29
|
-
import { discoverLayouts } from '
|
|
37
|
+
import { discoverLayouts } from '../discovery/layouts.ts'
|
|
38
|
+
import { discoverComponents } from '../discovery/componentDiscovery.ts'
|
|
30
39
|
import { processLayout } from '@zenithbuild/compiler/transform'
|
|
31
|
-
import { generateBundleJS } from '@zenithbuild/compiler/runtime'
|
|
32
40
|
import { loadZenithConfig } from '@zenithbuild/compiler/config'
|
|
33
41
|
import {
|
|
34
42
|
PluginRegistry,
|
|
35
43
|
createPluginContext,
|
|
36
44
|
getPluginDataByNamespace
|
|
37
45
|
} from '@zenithbuild/compiler/registry'
|
|
38
|
-
import { compileCssAsync, resolveGlobalsCss } from '@zenithbuild/compiler/css'
|
|
39
46
|
import {
|
|
40
47
|
createBridgeAPI,
|
|
41
48
|
runPluginHooks,
|
|
@@ -63,8 +70,6 @@ const pageCache = new Map<string, CompiledPage>()
|
|
|
63
70
|
* Bundle page script using Rolldown to resolve npm imports at compile time.
|
|
64
71
|
* Only called when compiler emits a BundlePlan - bundler performs no inference.
|
|
65
72
|
*/
|
|
66
|
-
import { bundlePageScript } from '@zenithbuild/compiler/bundler'
|
|
67
|
-
import { generateRouteDefinition } from '@zenithbuild/compiler/bundler'
|
|
68
73
|
import type { BundlePlan } from '@zenithbuild/compiler'
|
|
69
74
|
|
|
70
75
|
export async function dev(options: DevOptions = {}): Promise<void> {
|
|
@@ -157,19 +162,33 @@ export async function dev(options: DevOptions = {}): Promise<void> {
|
|
|
157
162
|
const layoutsDir = path.join(pagesDir, '../layouts')
|
|
158
163
|
const componentsDir = path.join(pagesDir, '../components')
|
|
159
164
|
const layouts = discoverLayouts(layoutsDir)
|
|
165
|
+
|
|
166
|
+
// Manual component discovery since compiler is pure
|
|
167
|
+
const components = new Map<string, any>([...layouts])
|
|
168
|
+
if (fs.existsSync(componentsDir)) {
|
|
169
|
+
const discovered = discoverComponents(componentsDir)
|
|
170
|
+
for (const [k, v] of discovered) {
|
|
171
|
+
components.set(k, v)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
160
175
|
const source = fs.readFileSync(pagePath, 'utf-8')
|
|
161
176
|
|
|
162
177
|
const result = await compile(source, pagePath, {
|
|
163
|
-
|
|
164
|
-
components: layouts
|
|
178
|
+
components: components
|
|
165
179
|
})
|
|
166
180
|
if (!result.finalized || !result.finalized.manifest) throw new Error('Compilation failed')
|
|
167
181
|
|
|
182
|
+
|
|
168
183
|
const routeDef = generateRouteDefinition(pagePath, pagesDir)
|
|
169
184
|
|
|
170
185
|
// Use the new bundler to generate the runtime + author script
|
|
171
186
|
// This replaces all the manual regex patching and string concatenation
|
|
172
|
-
const manifest: ZenManifest =
|
|
187
|
+
const manifest: ZenManifest = {
|
|
188
|
+
routes: [routeDef],
|
|
189
|
+
layouts: {}, // Dev server handles layouts dynamically
|
|
190
|
+
components: {}
|
|
191
|
+
}
|
|
173
192
|
const { code } = generateRuntime(manifest, true)
|
|
174
193
|
|
|
175
194
|
return {
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component Discovery
|
|
3
|
+
*
|
|
4
|
+
* Discovers and catalogs components in a Zenith project using standard
|
|
5
|
+
* file system walking and the unified native "syscall" for metadata.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as fs from 'fs'
|
|
9
|
+
import * as path from 'path'
|
|
10
|
+
import { parseZenFile, type TemplateNode, type ExpressionIR } from '@zenithbuild/compiler'
|
|
11
|
+
|
|
12
|
+
export interface SlotDefinition {
|
|
13
|
+
name: string | null // null = default slot, string = named slot
|
|
14
|
+
location: {
|
|
15
|
+
line: number
|
|
16
|
+
column: number
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ComponentMetadata {
|
|
21
|
+
name: string // Component name (e.g., "Card", "Button")
|
|
22
|
+
path: string // Absolute path to .zen file
|
|
23
|
+
template: string // Raw template HTML
|
|
24
|
+
nodes: TemplateNode[] // Parsed template nodes
|
|
25
|
+
expressions: ExpressionIR[] // Component-level expressions
|
|
26
|
+
slots: SlotDefinition[]
|
|
27
|
+
props: string[] // Declared props
|
|
28
|
+
states: Record<string, string> // Declared state (name -> initializer)
|
|
29
|
+
styles: string[] // Raw CSS from <style> blocks
|
|
30
|
+
script: string | null // Raw script content for bundling
|
|
31
|
+
scriptAttributes: Record<string, string> | null // Script attributes (setup, lang)
|
|
32
|
+
hasScript: boolean
|
|
33
|
+
hasStyles: boolean
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Discover all components in a directory recursively
|
|
38
|
+
*/
|
|
39
|
+
export function discoverComponents(baseDir: string): Map<string, ComponentMetadata> {
|
|
40
|
+
const components = new Map<string, ComponentMetadata>()
|
|
41
|
+
|
|
42
|
+
if (!fs.existsSync(baseDir)) return components;
|
|
43
|
+
|
|
44
|
+
const walk = (dir: string) => {
|
|
45
|
+
const files = fs.readdirSync(dir);
|
|
46
|
+
for (const file of files) {
|
|
47
|
+
const fullPath = path.join(dir, file);
|
|
48
|
+
if (fs.statSync(fullPath).isDirectory()) {
|
|
49
|
+
walk(fullPath);
|
|
50
|
+
} else if (file.endsWith('.zen')) {
|
|
51
|
+
const name = path.basename(file, '.zen');
|
|
52
|
+
try {
|
|
53
|
+
// Call the "One True Bridge" in metadata mode
|
|
54
|
+
const ir = parseZenFile(fullPath, undefined, { mode: 'metadata' });
|
|
55
|
+
|
|
56
|
+
// Map IR to ComponentMetadata format
|
|
57
|
+
components.set(name, {
|
|
58
|
+
name,
|
|
59
|
+
path: fullPath,
|
|
60
|
+
template: ir.template.raw,
|
|
61
|
+
nodes: ir.template.nodes,
|
|
62
|
+
expressions: ir.template.expressions,
|
|
63
|
+
slots: [], // Native bridge needs to return slot info in IR if used
|
|
64
|
+
props: ir.props || [],
|
|
65
|
+
states: ir.script?.states || {},
|
|
66
|
+
styles: ir.styles?.map((s: any) => s.raw) || [],
|
|
67
|
+
script: ir.script?.raw || null,
|
|
68
|
+
scriptAttributes: ir.script?.attributes || null,
|
|
69
|
+
hasScript: !!ir.script,
|
|
70
|
+
hasStyles: ir.styles?.length > 0
|
|
71
|
+
});
|
|
72
|
+
} catch (e) {
|
|
73
|
+
console.error(`[Zenith Discovery] Failed to parse component ${file}:`, e);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
walk(baseDir);
|
|
80
|
+
return components;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Universal Zenith Component Tag Rule: PascalCase
|
|
85
|
+
*/
|
|
86
|
+
export function isComponentTag(tagName: string): boolean {
|
|
87
|
+
return tagName.length > 0 && tagName[0] === tagName[0]?.toUpperCase()
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get component metadata by name
|
|
92
|
+
*/
|
|
93
|
+
export function getComponent(
|
|
94
|
+
components: Map<string, ComponentMetadata>,
|
|
95
|
+
name: string
|
|
96
|
+
): ComponentMetadata | undefined {
|
|
97
|
+
return components.get(name)
|
|
98
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as fs from 'fs'
|
|
2
|
+
import * as path from 'path'
|
|
3
|
+
import { parseZenFile } from '@zenithbuild/compiler'
|
|
4
|
+
|
|
5
|
+
export interface LayoutMetadata {
|
|
6
|
+
name: string
|
|
7
|
+
filePath: string
|
|
8
|
+
props: string[]
|
|
9
|
+
states: Map<string, any>
|
|
10
|
+
html: string
|
|
11
|
+
scripts: string[]
|
|
12
|
+
styles: string[]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Discover layouts in a directory using standard file system walking
|
|
17
|
+
* and the unified native bridge for metadata.
|
|
18
|
+
*/
|
|
19
|
+
export function discoverLayouts(layoutsDir: string): Map<string, LayoutMetadata> {
|
|
20
|
+
const layouts = new Map<string, LayoutMetadata>()
|
|
21
|
+
|
|
22
|
+
if (!fs.existsSync(layoutsDir)) return layouts
|
|
23
|
+
|
|
24
|
+
const files = fs.readdirSync(layoutsDir)
|
|
25
|
+
for (const file of files) {
|
|
26
|
+
if (file.endsWith('.zen')) {
|
|
27
|
+
const fullPath = path.join(layoutsDir, file)
|
|
28
|
+
const name = path.basename(file, '.zen')
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
// Call the "One True Bridge" in metadata mode
|
|
32
|
+
const ir = parseZenFile(fullPath, undefined, { mode: 'metadata' })
|
|
33
|
+
|
|
34
|
+
layouts.set(name, {
|
|
35
|
+
name,
|
|
36
|
+
filePath: fullPath,
|
|
37
|
+
props: ir.props || [],
|
|
38
|
+
states: new Map(),
|
|
39
|
+
html: ir.template.raw,
|
|
40
|
+
scripts: ir.script ? [ir.script.content] : [],
|
|
41
|
+
styles: ir.styles?.map((s: any) => s.raw) || []
|
|
42
|
+
})
|
|
43
|
+
} catch (e) {
|
|
44
|
+
console.error(`[Zenith Layout Discovery] Failed to parse layout ${file}:`, e)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return layouts
|
|
50
|
+
}
|
package/src/serve.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zenith Development Server
|
|
3
|
+
*
|
|
4
|
+
* SPA-compatible server that:
|
|
5
|
+
* - Serves static assets directly (js, css, ico, images)
|
|
6
|
+
* - Serves index.html for all other routes (SPA fallback)
|
|
7
|
+
*
|
|
8
|
+
* This enables client-side routing to work on:
|
|
9
|
+
* - Direct URL entry
|
|
10
|
+
* - Hard refresh
|
|
11
|
+
* - Back/forward navigation
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { serve } from "bun"
|
|
15
|
+
import path from "path"
|
|
16
|
+
|
|
17
|
+
const distDir = path.resolve(process.cwd(), "dist")
|
|
18
|
+
|
|
19
|
+
// File extensions that should be served as static assets
|
|
20
|
+
const STATIC_EXTENSIONS = new Set([
|
|
21
|
+
".js",
|
|
22
|
+
".css",
|
|
23
|
+
".ico",
|
|
24
|
+
".png",
|
|
25
|
+
".jpg",
|
|
26
|
+
".jpeg",
|
|
27
|
+
".gif",
|
|
28
|
+
".svg",
|
|
29
|
+
".webp",
|
|
30
|
+
".woff",
|
|
31
|
+
".woff2",
|
|
32
|
+
".ttf",
|
|
33
|
+
".eot",
|
|
34
|
+
".json",
|
|
35
|
+
".map"
|
|
36
|
+
])
|
|
37
|
+
|
|
38
|
+
serve({
|
|
39
|
+
port: 3000,
|
|
40
|
+
|
|
41
|
+
async fetch(req) {
|
|
42
|
+
const url = new URL(req.url)
|
|
43
|
+
const pathname = url.pathname
|
|
44
|
+
|
|
45
|
+
// Get file extension
|
|
46
|
+
const ext = path.extname(pathname).toLowerCase()
|
|
47
|
+
|
|
48
|
+
// Check if this is a static asset request
|
|
49
|
+
if (STATIC_EXTENSIONS.has(ext)) {
|
|
50
|
+
const filePath = path.join(distDir, pathname)
|
|
51
|
+
const file = Bun.file(filePath)
|
|
52
|
+
|
|
53
|
+
// Check if file exists
|
|
54
|
+
if (await file.exists()) {
|
|
55
|
+
return new Response(file)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Static file not found
|
|
59
|
+
return new Response("Not found", { status: 404 })
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// For all other routes, serve index.html (SPA fallback)
|
|
63
|
+
const indexPath = path.join(distDir, "index.html")
|
|
64
|
+
const indexFile = Bun.file(indexPath)
|
|
65
|
+
|
|
66
|
+
if (await indexFile.exists()) {
|
|
67
|
+
return new Response(indexFile, {
|
|
68
|
+
headers: {
|
|
69
|
+
"Content-Type": "text/html; charset=utf-8"
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// No index.html found - likely need to run build first
|
|
75
|
+
return new Response(
|
|
76
|
+
`<html>
|
|
77
|
+
<head><title>Zenith - Build Required</title></head>
|
|
78
|
+
<body style="font-family: system-ui; padding: 2rem; text-align: center;">
|
|
79
|
+
<h1>Build Required</h1>
|
|
80
|
+
<p>Run <code>zenith build</code> first to compile the pages.</p>
|
|
81
|
+
</body>
|
|
82
|
+
</html>`,
|
|
83
|
+
{
|
|
84
|
+
status: 500,
|
|
85
|
+
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
86
|
+
}
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
console.log("🚀 Zenith dev server running at http://localhost:3000")
|
|
92
|
+
console.log(" SPA mode: All routes serve index.html")
|