@zenithbuild/compiler 1.3.2 → 1.3.9
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/build-analyzer.d.ts +44 -0
- package/dist/build-analyzer.js +87 -0
- package/dist/bundler.d.ts +31 -0
- package/dist/bundler.js +92 -0
- package/dist/core/config/index.d.ts +11 -0
- package/dist/core/config/index.js +10 -0
- package/dist/core/config/loader.d.ts +17 -0
- package/dist/core/config/loader.js +60 -0
- package/dist/core/config/types.d.ts +98 -0
- package/dist/core/config/types.js +32 -0
- package/dist/core/index.d.ts +7 -0
- package/dist/core/index.js +6 -0
- package/dist/core/plugins/bridge.d.ts +116 -0
- package/dist/core/plugins/bridge.js +121 -0
- package/dist/core/plugins/index.d.ts +6 -0
- package/dist/core/plugins/index.js +6 -0
- package/dist/core/plugins/registry.d.ts +67 -0
- package/dist/core/plugins/registry.js +112 -0
- package/dist/css/index.d.ts +73 -0
- package/dist/css/index.js +246 -0
- package/dist/discovery/componentDiscovery.d.ts +41 -0
- package/dist/discovery/componentDiscovery.js +66 -0
- package/dist/discovery/layouts.d.ts +14 -0
- package/dist/discovery/layouts.js +36 -0
- package/dist/errors/compilerError.d.ts +31 -0
- package/dist/errors/compilerError.js +51 -0
- package/dist/finalize/generateFinalBundle.d.ts +24 -0
- package/dist/finalize/generateFinalBundle.js +68 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +44 -0
- package/dist/ir/types.d.ts +206 -0
- package/dist/ir/types.js +8 -0
- package/dist/output/types.d.ts +39 -0
- package/dist/output/types.js +6 -0
- package/dist/parseZenFile.d.ts +17 -0
- package/dist/parseZenFile.js +52 -0
- package/dist/runtime/build.d.ts +6 -0
- package/dist/runtime/build.js +13 -0
- package/dist/runtime/bundle-generator.d.ts +27 -0
- package/dist/runtime/bundle-generator.js +1438 -0
- package/dist/runtime/client-runtime.d.ts +41 -0
- package/dist/runtime/client-runtime.js +397 -0
- package/dist/runtime/hydration.d.ts +53 -0
- package/dist/runtime/hydration.js +271 -0
- package/dist/runtime/navigation.d.ts +58 -0
- package/dist/runtime/navigation.js +372 -0
- package/dist/runtime/serve.d.ts +13 -0
- package/dist/runtime/serve.js +76 -0
- package/dist/runtime/thinRuntime.d.ts +23 -0
- package/dist/runtime/thinRuntime.js +158 -0
- package/dist/spa-build.d.ts +26 -0
- package/dist/spa-build.js +849 -0
- package/dist/ssg-build.d.ts +31 -0
- package/dist/ssg-build.js +429 -0
- package/dist/test/bundler-contract.test.d.ts +1 -0
- package/dist/test/bundler-contract.test.js +137 -0
- package/dist/test/compiler-entry.test.d.ts +1 -0
- package/dist/test/compiler-entry.test.js +28 -0
- package/dist/test/error-native-bridge.test.d.ts +1 -0
- package/dist/test/error-native-bridge.test.js +31 -0
- package/dist/test/error-serialization.test.d.ts +1 -0
- package/dist/test/error-serialization.test.js +38 -0
- package/dist/test/phase5-boundary.test.d.ts +1 -0
- package/dist/test/phase5-boundary.test.js +51 -0
- package/dist/transform/layoutProcessor.d.ts +26 -0
- package/dist/transform/layoutProcessor.js +34 -0
- package/dist/validate/invariants.d.ts +23 -0
- package/dist/validate/invariants.js +55 -0
- package/native/compiler-native/compiler-native.node +0 -0
- package/native/compiler-native/index.d.ts +2 -46
- package/native/compiler-native/index.js +1 -16
- package/native/compiler-native/package.json +1 -1
- package/package.json +15 -5
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zenith CLI Bridge
|
|
3
|
+
*
|
|
4
|
+
* The ONLY interface between CLI and plugins.
|
|
5
|
+
*
|
|
6
|
+
* ═══════════════════════════════════════════════════════════════════════════════
|
|
7
|
+
* CLI BRIDGE RULES (CANONICAL)
|
|
8
|
+
* ═══════════════════════════════════════════════════════════════════════════════
|
|
9
|
+
*
|
|
10
|
+
* 1. No runtime emitters - plugins return data, CLI serializes blindly
|
|
11
|
+
* 2. No plugin typing - all data is unknown
|
|
12
|
+
* 3. No semantic helpers - CLI is blind to what data means
|
|
13
|
+
*
|
|
14
|
+
* The CLI dispatches hooks and collects returns. It never inspects payloads.
|
|
15
|
+
* ═══════════════════════════════════════════════════════════════════════════════
|
|
16
|
+
*/
|
|
17
|
+
const hookRegistry = new Map();
|
|
18
|
+
/**
|
|
19
|
+
* Register a hook handler
|
|
20
|
+
*
|
|
21
|
+
* @internal Called by CLIBridgeAPI.on()
|
|
22
|
+
*/
|
|
23
|
+
export function registerHook(hook, handler) {
|
|
24
|
+
if (!hookRegistry.has(hook)) {
|
|
25
|
+
hookRegistry.set(hook, []);
|
|
26
|
+
}
|
|
27
|
+
hookRegistry.get(hook).push(handler);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Clear all registered hooks
|
|
31
|
+
*
|
|
32
|
+
* @internal Used for testing and cleanup
|
|
33
|
+
*/
|
|
34
|
+
export function clearHooks() {
|
|
35
|
+
hookRegistry.clear();
|
|
36
|
+
}
|
|
37
|
+
// ============================================
|
|
38
|
+
// Hook Execution (CLI-facing)
|
|
39
|
+
// ============================================
|
|
40
|
+
/**
|
|
41
|
+
* Run all handlers for a hook (fire-and-forget)
|
|
42
|
+
*
|
|
43
|
+
* CLI calls this for lifecycle events.
|
|
44
|
+
* No return values are collected.
|
|
45
|
+
*
|
|
46
|
+
* @param hook - Hook name to dispatch
|
|
47
|
+
* @param ctx - Hook context
|
|
48
|
+
*/
|
|
49
|
+
export async function runPluginHooks(hook, ctx) {
|
|
50
|
+
const handlers = hookRegistry.get(hook) || [];
|
|
51
|
+
for (const handler of handlers) {
|
|
52
|
+
try {
|
|
53
|
+
await handler(ctx);
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
console.error(`[Zenith] Hook "${hook}" error:`, error);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Collect return values from all handlers for a hook
|
|
62
|
+
*
|
|
63
|
+
* CLI calls this for 'cli:runtime:collect' to gather plugin payloads.
|
|
64
|
+
* Only RuntimePayload-shaped returns are collected.
|
|
65
|
+
*
|
|
66
|
+
* @param hook - Hook name to dispatch
|
|
67
|
+
* @param ctx - Hook context
|
|
68
|
+
* @returns Array of runtime payloads from plugins
|
|
69
|
+
*/
|
|
70
|
+
export async function collectHookReturns(hook, ctx) {
|
|
71
|
+
const handlers = hookRegistry.get(hook) || [];
|
|
72
|
+
const results = [];
|
|
73
|
+
for (const handler of handlers) {
|
|
74
|
+
try {
|
|
75
|
+
const result = await handler(ctx);
|
|
76
|
+
// Only collect properly shaped payloads
|
|
77
|
+
if (result &&
|
|
78
|
+
typeof result === 'object' &&
|
|
79
|
+
'namespace' in result &&
|
|
80
|
+
'payload' in result &&
|
|
81
|
+
typeof result.namespace === 'string') {
|
|
82
|
+
results.push(result);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
console.error(`[Zenith] Hook "${hook}" collection error:`, error);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return results;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Build runtime envelope from collected payloads
|
|
93
|
+
*
|
|
94
|
+
* CLI calls this to serialize plugin data for injection.
|
|
95
|
+
* CLI never inspects the envelope contents.
|
|
96
|
+
*
|
|
97
|
+
* @param payloads - Array of runtime payloads from collectHookReturns
|
|
98
|
+
* @returns Envelope object: { [namespace]: payload }
|
|
99
|
+
*/
|
|
100
|
+
export function buildRuntimeEnvelope(payloads) {
|
|
101
|
+
const envelope = {};
|
|
102
|
+
for (const { namespace, payload } of payloads) {
|
|
103
|
+
envelope[namespace] = payload;
|
|
104
|
+
}
|
|
105
|
+
return envelope;
|
|
106
|
+
}
|
|
107
|
+
// ============================================
|
|
108
|
+
// Bridge API Factory
|
|
109
|
+
// ============================================
|
|
110
|
+
/**
|
|
111
|
+
* Create a CLI Bridge API for plugin registration
|
|
112
|
+
*
|
|
113
|
+
* CLI calls this once and passes to each plugin's registerCLI method.
|
|
114
|
+
*
|
|
115
|
+
* @returns CLIBridgeAPI instance
|
|
116
|
+
*/
|
|
117
|
+
export function createBridgeAPI() {
|
|
118
|
+
return {
|
|
119
|
+
on: registerHook
|
|
120
|
+
};
|
|
121
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zenith Plugin Registry
|
|
3
|
+
*
|
|
4
|
+
* Manages plugin registration and initialization
|
|
5
|
+
*
|
|
6
|
+
* ═══════════════════════════════════════════════════════════════════════════════
|
|
7
|
+
* HOOK OWNERSHIP RULE (CANONICAL)
|
|
8
|
+
* ═══════════════════════════════════════════════════════════════════════════════
|
|
9
|
+
*
|
|
10
|
+
* The plugin registry is part of core infrastructure.
|
|
11
|
+
* It MUST remain plugin-agnostic:
|
|
12
|
+
* - No plugin-specific types
|
|
13
|
+
* - No plugin-specific logic
|
|
14
|
+
* - Generic data handling only
|
|
15
|
+
*
|
|
16
|
+
* Plugins own their data structures; core provides the storage mechanism.
|
|
17
|
+
* ═══════════════════════════════════════════════════════════════════════════════
|
|
18
|
+
*/
|
|
19
|
+
import type { ZenithPlugin, PluginContext } from '../config/types';
|
|
20
|
+
/**
|
|
21
|
+
* Get all plugin data (for runtime access)
|
|
22
|
+
*/
|
|
23
|
+
export declare function getPluginData(): Record<string, unknown[]>;
|
|
24
|
+
/**
|
|
25
|
+
* Get plugin data by namespace
|
|
26
|
+
*/
|
|
27
|
+
export declare function getPluginDataByNamespace(namespace: string): unknown[];
|
|
28
|
+
/**
|
|
29
|
+
* Plugin registry for managing Zenith plugins
|
|
30
|
+
*/
|
|
31
|
+
export declare class PluginRegistry {
|
|
32
|
+
private plugins;
|
|
33
|
+
/**
|
|
34
|
+
* Register a plugin
|
|
35
|
+
*/
|
|
36
|
+
register(plugin: ZenithPlugin): void;
|
|
37
|
+
/**
|
|
38
|
+
* Get a plugin by name
|
|
39
|
+
*/
|
|
40
|
+
get(name: string): ZenithPlugin | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Check if a plugin is registered
|
|
43
|
+
*/
|
|
44
|
+
has(name: string): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Get all registered plugins
|
|
47
|
+
*/
|
|
48
|
+
all(): ZenithPlugin[];
|
|
49
|
+
/**
|
|
50
|
+
* Initialize all plugins with the provided context
|
|
51
|
+
*/
|
|
52
|
+
initAll(ctx: PluginContext): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Clear all registered plugins
|
|
55
|
+
*/
|
|
56
|
+
clear(): void;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Create a plugin context for initialization
|
|
60
|
+
*
|
|
61
|
+
* Uses a generic data setter that stores data by namespace.
|
|
62
|
+
* Plugins define their own data structures internally.
|
|
63
|
+
*
|
|
64
|
+
* @param projectRoot - Absolute path to the project root
|
|
65
|
+
* @returns A PluginContext for plugin initialization
|
|
66
|
+
*/
|
|
67
|
+
export declare function createPluginContext(projectRoot: string): PluginContext;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zenith Plugin Registry
|
|
3
|
+
*
|
|
4
|
+
* Manages plugin registration and initialization
|
|
5
|
+
*
|
|
6
|
+
* ═══════════════════════════════════════════════════════════════════════════════
|
|
7
|
+
* HOOK OWNERSHIP RULE (CANONICAL)
|
|
8
|
+
* ═══════════════════════════════════════════════════════════════════════════════
|
|
9
|
+
*
|
|
10
|
+
* The plugin registry is part of core infrastructure.
|
|
11
|
+
* It MUST remain plugin-agnostic:
|
|
12
|
+
* - No plugin-specific types
|
|
13
|
+
* - No plugin-specific logic
|
|
14
|
+
* - Generic data handling only
|
|
15
|
+
*
|
|
16
|
+
* Plugins own their data structures; core provides the storage mechanism.
|
|
17
|
+
* ═══════════════════════════════════════════════════════════════════════════════
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Global plugin data store
|
|
21
|
+
*
|
|
22
|
+
* Plugins store their data here using namespaced keys.
|
|
23
|
+
* Core does not interpret this data - it just stores and serves it.
|
|
24
|
+
*/
|
|
25
|
+
const pluginDataStore = {};
|
|
26
|
+
/**
|
|
27
|
+
* Get all plugin data (for runtime access)
|
|
28
|
+
*/
|
|
29
|
+
export function getPluginData() {
|
|
30
|
+
return { ...pluginDataStore };
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Get plugin data by namespace
|
|
34
|
+
*/
|
|
35
|
+
export function getPluginDataByNamespace(namespace) {
|
|
36
|
+
return pluginDataStore[namespace] || [];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Plugin registry for managing Zenith plugins
|
|
40
|
+
*/
|
|
41
|
+
export class PluginRegistry {
|
|
42
|
+
plugins = new Map();
|
|
43
|
+
/**
|
|
44
|
+
* Register a plugin
|
|
45
|
+
*/
|
|
46
|
+
register(plugin) {
|
|
47
|
+
if (this.plugins.has(plugin.name)) {
|
|
48
|
+
console.warn(`[Zenith] Plugin "${plugin.name}" is already registered. Overwriting.`);
|
|
49
|
+
}
|
|
50
|
+
this.plugins.set(plugin.name, plugin);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get a plugin by name
|
|
54
|
+
*/
|
|
55
|
+
get(name) {
|
|
56
|
+
return this.plugins.get(name);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Check if a plugin is registered
|
|
60
|
+
*/
|
|
61
|
+
has(name) {
|
|
62
|
+
return this.plugins.has(name);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get all registered plugins
|
|
66
|
+
*/
|
|
67
|
+
all() {
|
|
68
|
+
return Array.from(this.plugins.values());
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Initialize all plugins with the provided context
|
|
72
|
+
*/
|
|
73
|
+
async initAll(ctx) {
|
|
74
|
+
for (const plugin of this.plugins.values()) {
|
|
75
|
+
try {
|
|
76
|
+
await plugin.setup(ctx);
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
80
|
+
console.error(`[Zenith] Failed to initialize plugin "${plugin.name}":`, message);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Clear all registered plugins
|
|
86
|
+
*/
|
|
87
|
+
clear() {
|
|
88
|
+
this.plugins.clear();
|
|
89
|
+
// Also clear plugin data
|
|
90
|
+
for (const key of Object.keys(pluginDataStore)) {
|
|
91
|
+
delete pluginDataStore[key];
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Create a plugin context for initialization
|
|
97
|
+
*
|
|
98
|
+
* Uses a generic data setter that stores data by namespace.
|
|
99
|
+
* Plugins define their own data structures internally.
|
|
100
|
+
*
|
|
101
|
+
* @param projectRoot - Absolute path to the project root
|
|
102
|
+
* @returns A PluginContext for plugin initialization
|
|
103
|
+
*/
|
|
104
|
+
export function createPluginContext(projectRoot) {
|
|
105
|
+
return {
|
|
106
|
+
projectRoot,
|
|
107
|
+
setPluginData: (namespace, data) => {
|
|
108
|
+
pluginDataStore[namespace] = data;
|
|
109
|
+
},
|
|
110
|
+
options: {}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zenith CSS Compiler Module
|
|
3
|
+
*
|
|
4
|
+
* Compiler-owned CSS processing that integrates Tailwind CSS v4 JIT
|
|
5
|
+
* at compile time. This module ensures:
|
|
6
|
+
*
|
|
7
|
+
* 1. All CSS is processed at build time (no runtime generation)
|
|
8
|
+
* 2. Tailwind sees all .zen templates for class scanning
|
|
9
|
+
* 3. HMR support for instant CSS updates in dev mode
|
|
10
|
+
* 4. Deterministic, cacheable output for production
|
|
11
|
+
*
|
|
12
|
+
* Per Zenith CSS Directive: The compiler owns styles.
|
|
13
|
+
*/
|
|
14
|
+
export interface CSSCompileOptions {
|
|
15
|
+
/** Input CSS file path (e.g., src/styles/globals.css) */
|
|
16
|
+
input: string;
|
|
17
|
+
/** Output CSS file path, or ':memory:' for in-memory result */
|
|
18
|
+
output: string;
|
|
19
|
+
/** Enable minification for production */
|
|
20
|
+
minify?: boolean;
|
|
21
|
+
/** Watch mode for HMR */
|
|
22
|
+
watch?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface CSSCompileResult {
|
|
25
|
+
/** Compiled CSS content */
|
|
26
|
+
css: string;
|
|
27
|
+
/** Compilation time in milliseconds */
|
|
28
|
+
duration: number;
|
|
29
|
+
/** Whether compilation succeeded */
|
|
30
|
+
success: boolean;
|
|
31
|
+
/** Error message if failed */
|
|
32
|
+
error?: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Compile CSS using Tailwind CSS v4 CLI
|
|
36
|
+
*
|
|
37
|
+
* This function synchronously compiles CSS for use in:
|
|
38
|
+
* - Dev server startup
|
|
39
|
+
* - SSG build
|
|
40
|
+
* - On-demand recompilation
|
|
41
|
+
*
|
|
42
|
+
* @param options Compilation options
|
|
43
|
+
* @returns Compiled CSS result
|
|
44
|
+
*/
|
|
45
|
+
export declare function compileCss(options: CSSCompileOptions): CSSCompileResult;
|
|
46
|
+
/**
|
|
47
|
+
* Compile CSS asynchronously (non-blocking)
|
|
48
|
+
*
|
|
49
|
+
* Used for HMR updates where we don't want to block the main thread.
|
|
50
|
+
*/
|
|
51
|
+
export declare function compileCssAsync(options: CSSCompileOptions): Promise<CSSCompileResult>;
|
|
52
|
+
export interface CSSWatchOptions extends CSSCompileOptions {
|
|
53
|
+
/** Callback when CSS changes */
|
|
54
|
+
onChange: (result: CSSCompileResult) => void;
|
|
55
|
+
/** Debounce delay in ms */
|
|
56
|
+
debounce?: number;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Watch CSS and source files for changes, recompile on change
|
|
60
|
+
*
|
|
61
|
+
* This is used by the dev server for HMR support.
|
|
62
|
+
* It watches both the CSS entry point AND all .zen files
|
|
63
|
+
* that Tailwind scans for class names.
|
|
64
|
+
*/
|
|
65
|
+
export declare function watchCss(options: CSSWatchOptions): () => void;
|
|
66
|
+
/**
|
|
67
|
+
* Resolve the canonical globals.css path for a Zenith project
|
|
68
|
+
*/
|
|
69
|
+
export declare function resolveGlobalsCss(projectRoot: string): string | null;
|
|
70
|
+
/**
|
|
71
|
+
* Get the output path for compiled CSS
|
|
72
|
+
*/
|
|
73
|
+
export declare function getCompiledCssPath(projectRoot: string, mode: 'dev' | 'build'): string;
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zenith CSS Compiler Module
|
|
3
|
+
*
|
|
4
|
+
* Compiler-owned CSS processing that integrates Tailwind CSS v4 JIT
|
|
5
|
+
* at compile time. This module ensures:
|
|
6
|
+
*
|
|
7
|
+
* 1. All CSS is processed at build time (no runtime generation)
|
|
8
|
+
* 2. Tailwind sees all .zen templates for class scanning
|
|
9
|
+
* 3. HMR support for instant CSS updates in dev mode
|
|
10
|
+
* 4. Deterministic, cacheable output for production
|
|
11
|
+
*
|
|
12
|
+
* Per Zenith CSS Directive: The compiler owns styles.
|
|
13
|
+
*/
|
|
14
|
+
import { spawn, spawnSync } from 'child_process';
|
|
15
|
+
import path from 'path';
|
|
16
|
+
import fs from 'fs';
|
|
17
|
+
// ============================================
|
|
18
|
+
// CSS Compilation
|
|
19
|
+
// ============================================
|
|
20
|
+
/**
|
|
21
|
+
* Compile CSS using Tailwind CSS v4 CLI
|
|
22
|
+
*
|
|
23
|
+
* This function synchronously compiles CSS for use in:
|
|
24
|
+
* - Dev server startup
|
|
25
|
+
* - SSG build
|
|
26
|
+
* - On-demand recompilation
|
|
27
|
+
*
|
|
28
|
+
* @param options Compilation options
|
|
29
|
+
* @returns Compiled CSS result
|
|
30
|
+
*/
|
|
31
|
+
export function compileCss(options) {
|
|
32
|
+
const startTime = performance.now();
|
|
33
|
+
const { input, output, minify = false } = options;
|
|
34
|
+
// Validate input exists
|
|
35
|
+
if (!fs.existsSync(input)) {
|
|
36
|
+
return {
|
|
37
|
+
css: '',
|
|
38
|
+
duration: 0,
|
|
39
|
+
success: false,
|
|
40
|
+
error: `CSS input file not found: ${input}`
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
// Build Tailwind CLI arguments
|
|
45
|
+
const args = [
|
|
46
|
+
'@tailwindcss/cli',
|
|
47
|
+
'-i', input
|
|
48
|
+
];
|
|
49
|
+
// For in-memory compilation, use stdout
|
|
50
|
+
const useStdout = output === ':memory:';
|
|
51
|
+
if (!useStdout) {
|
|
52
|
+
args.push('-o', output);
|
|
53
|
+
}
|
|
54
|
+
if (minify) {
|
|
55
|
+
args.push('--minify');
|
|
56
|
+
}
|
|
57
|
+
// Execute Tailwind CLI synchronously
|
|
58
|
+
const result = spawnSync('bunx', args, {
|
|
59
|
+
cwd: path.dirname(input),
|
|
60
|
+
encoding: 'utf-8',
|
|
61
|
+
stdio: useStdout ? ['pipe', 'pipe', 'pipe'] : ['pipe', 'inherit', 'pipe'],
|
|
62
|
+
env: { ...process.env }
|
|
63
|
+
});
|
|
64
|
+
const duration = Math.round(performance.now() - startTime);
|
|
65
|
+
if (result.status !== 0) {
|
|
66
|
+
const errorMsg = result.stderr?.toString() || 'Unknown compilation error';
|
|
67
|
+
return {
|
|
68
|
+
css: '',
|
|
69
|
+
duration,
|
|
70
|
+
success: false,
|
|
71
|
+
error: `Tailwind compilation failed: ${errorMsg}`
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
// Get CSS content
|
|
75
|
+
let css = '';
|
|
76
|
+
if (useStdout) {
|
|
77
|
+
css = result.stdout?.toString() || '';
|
|
78
|
+
}
|
|
79
|
+
else if (fs.existsSync(output)) {
|
|
80
|
+
css = fs.readFileSync(output, 'utf-8');
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
css,
|
|
84
|
+
duration,
|
|
85
|
+
success: true
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
return {
|
|
90
|
+
css: '',
|
|
91
|
+
duration: Math.round(performance.now() - startTime),
|
|
92
|
+
success: false,
|
|
93
|
+
error: error.message
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Compile CSS asynchronously (non-blocking)
|
|
99
|
+
*
|
|
100
|
+
* Used for HMR updates where we don't want to block the main thread.
|
|
101
|
+
*/
|
|
102
|
+
export async function compileCssAsync(options) {
|
|
103
|
+
return new Promise((resolve) => {
|
|
104
|
+
const startTime = performance.now();
|
|
105
|
+
const { input, output, minify = false } = options;
|
|
106
|
+
if (!fs.existsSync(input)) {
|
|
107
|
+
resolve({
|
|
108
|
+
css: '',
|
|
109
|
+
duration: 0,
|
|
110
|
+
success: false,
|
|
111
|
+
error: `CSS input file not found: ${input}`
|
|
112
|
+
});
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const args = ['@tailwindcss/cli', '-i', input];
|
|
116
|
+
const useStdout = output === ':memory:';
|
|
117
|
+
if (!useStdout) {
|
|
118
|
+
args.push('-o', output);
|
|
119
|
+
}
|
|
120
|
+
if (minify) {
|
|
121
|
+
args.push('--minify');
|
|
122
|
+
}
|
|
123
|
+
const child = spawn('bunx', args, {
|
|
124
|
+
cwd: path.dirname(input),
|
|
125
|
+
stdio: useStdout ? ['pipe', 'pipe', 'pipe'] : ['pipe', 'inherit', 'pipe'],
|
|
126
|
+
env: { ...process.env }
|
|
127
|
+
});
|
|
128
|
+
let stdout = '';
|
|
129
|
+
let stderr = '';
|
|
130
|
+
if (useStdout && child.stdout) {
|
|
131
|
+
child.stdout.on('data', (data) => { stdout += data.toString(); });
|
|
132
|
+
}
|
|
133
|
+
if (child.stderr) {
|
|
134
|
+
child.stderr.on('data', (data) => { stderr += data.toString(); });
|
|
135
|
+
}
|
|
136
|
+
child.on('close', (code) => {
|
|
137
|
+
const duration = Math.round(performance.now() - startTime);
|
|
138
|
+
if (code !== 0) {
|
|
139
|
+
resolve({
|
|
140
|
+
css: '',
|
|
141
|
+
duration,
|
|
142
|
+
success: false,
|
|
143
|
+
error: `Tailwind compilation failed: ${stderr}`
|
|
144
|
+
});
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
let css = '';
|
|
148
|
+
if (useStdout) {
|
|
149
|
+
css = stdout;
|
|
150
|
+
}
|
|
151
|
+
else if (fs.existsSync(output)) {
|
|
152
|
+
css = fs.readFileSync(output, 'utf-8');
|
|
153
|
+
}
|
|
154
|
+
resolve({
|
|
155
|
+
css,
|
|
156
|
+
duration,
|
|
157
|
+
success: true
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
child.on('error', (err) => {
|
|
161
|
+
resolve({
|
|
162
|
+
css: '',
|
|
163
|
+
duration: Math.round(performance.now() - startTime),
|
|
164
|
+
success: false,
|
|
165
|
+
error: err.message
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Watch CSS and source files for changes, recompile on change
|
|
172
|
+
*
|
|
173
|
+
* This is used by the dev server for HMR support.
|
|
174
|
+
* It watches both the CSS entry point AND all .zen files
|
|
175
|
+
* that Tailwind scans for class names.
|
|
176
|
+
*/
|
|
177
|
+
export function watchCss(options) {
|
|
178
|
+
const { input, output, minify, onChange, debounce = 100 } = options;
|
|
179
|
+
let timeout = null;
|
|
180
|
+
let isCompiling = false;
|
|
181
|
+
const recompile = async () => {
|
|
182
|
+
if (isCompiling)
|
|
183
|
+
return;
|
|
184
|
+
isCompiling = true;
|
|
185
|
+
const result = await compileCssAsync({ input, output, minify });
|
|
186
|
+
onChange(result);
|
|
187
|
+
isCompiling = false;
|
|
188
|
+
};
|
|
189
|
+
const debouncedRecompile = () => {
|
|
190
|
+
if (timeout)
|
|
191
|
+
clearTimeout(timeout);
|
|
192
|
+
timeout = setTimeout(recompile, debounce);
|
|
193
|
+
};
|
|
194
|
+
// Watch the styles directory
|
|
195
|
+
const stylesDir = path.dirname(input);
|
|
196
|
+
const stylesWatcher = fs.watch(stylesDir, { recursive: true }, (event, filename) => {
|
|
197
|
+
if (filename?.endsWith('.css')) {
|
|
198
|
+
debouncedRecompile();
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
// Watch source files that Tailwind scans (for class changes)
|
|
202
|
+
// This assumes standard Zenith structure: src/pages, src/components, src/layouts
|
|
203
|
+
const srcDir = path.resolve(stylesDir, '..');
|
|
204
|
+
let srcWatcher = null;
|
|
205
|
+
if (fs.existsSync(srcDir)) {
|
|
206
|
+
srcWatcher = fs.watch(srcDir, { recursive: true }, (event, filename) => {
|
|
207
|
+
if (filename?.endsWith('.zen') || filename?.endsWith('.tsx') || filename?.endsWith('.jsx')) {
|
|
208
|
+
debouncedRecompile();
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
// Return cleanup function
|
|
213
|
+
return () => {
|
|
214
|
+
if (timeout)
|
|
215
|
+
clearTimeout(timeout);
|
|
216
|
+
stylesWatcher.close();
|
|
217
|
+
srcWatcher?.close();
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
// ============================================
|
|
221
|
+
// Path Utilities
|
|
222
|
+
// ============================================
|
|
223
|
+
/**
|
|
224
|
+
* Resolve the canonical globals.css path for a Zenith project
|
|
225
|
+
*/
|
|
226
|
+
export function resolveGlobalsCss(projectRoot) {
|
|
227
|
+
// Check for globals.css (canonical)
|
|
228
|
+
const globalsPath = path.join(projectRoot, 'src', 'styles', 'globals.css');
|
|
229
|
+
if (fs.existsSync(globalsPath))
|
|
230
|
+
return globalsPath;
|
|
231
|
+
// Check for global.css (legacy)
|
|
232
|
+
const globalPath = path.join(projectRoot, 'src', 'styles', 'global.css');
|
|
233
|
+
if (fs.existsSync(globalPath))
|
|
234
|
+
return globalPath;
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Get the output path for compiled CSS
|
|
239
|
+
*/
|
|
240
|
+
export function getCompiledCssPath(projectRoot, mode) {
|
|
241
|
+
if (mode === 'build') {
|
|
242
|
+
return path.join(projectRoot, 'dist', 'assets', 'styles.css');
|
|
243
|
+
}
|
|
244
|
+
// In dev mode, we use in-memory compilation
|
|
245
|
+
return ':memory:';
|
|
246
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
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
|
+
import type { TemplateNode, ExpressionIR } from '../ir/types';
|
|
8
|
+
export interface SlotDefinition {
|
|
9
|
+
name: string | null;
|
|
10
|
+
location: {
|
|
11
|
+
line: number;
|
|
12
|
+
column: number;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export interface ComponentMetadata {
|
|
16
|
+
name: string;
|
|
17
|
+
path: string;
|
|
18
|
+
template: string;
|
|
19
|
+
nodes: TemplateNode[];
|
|
20
|
+
expressions: ExpressionIR[];
|
|
21
|
+
slots: SlotDefinition[];
|
|
22
|
+
props: string[];
|
|
23
|
+
states: Record<string, string>;
|
|
24
|
+
styles: string[];
|
|
25
|
+
script: string | null;
|
|
26
|
+
scriptAttributes: Record<string, string> | null;
|
|
27
|
+
hasScript: boolean;
|
|
28
|
+
hasStyles: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Discover all components in a directory recursively
|
|
32
|
+
*/
|
|
33
|
+
export declare function discoverComponents(baseDir: string): Map<string, ComponentMetadata>;
|
|
34
|
+
/**
|
|
35
|
+
* Universal Zenith Component Tag Rule: PascalCase
|
|
36
|
+
*/
|
|
37
|
+
export declare function isComponentTag(tagName: string): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Get component metadata by name
|
|
40
|
+
*/
|
|
41
|
+
export declare function getComponent(components: Map<string, ComponentMetadata>, name: string): ComponentMetadata | undefined;
|