@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.
Files changed (73) hide show
  1. package/dist/build-analyzer.d.ts +44 -0
  2. package/dist/build-analyzer.js +87 -0
  3. package/dist/bundler.d.ts +31 -0
  4. package/dist/bundler.js +92 -0
  5. package/dist/core/config/index.d.ts +11 -0
  6. package/dist/core/config/index.js +10 -0
  7. package/dist/core/config/loader.d.ts +17 -0
  8. package/dist/core/config/loader.js +60 -0
  9. package/dist/core/config/types.d.ts +98 -0
  10. package/dist/core/config/types.js +32 -0
  11. package/dist/core/index.d.ts +7 -0
  12. package/dist/core/index.js +6 -0
  13. package/dist/core/plugins/bridge.d.ts +116 -0
  14. package/dist/core/plugins/bridge.js +121 -0
  15. package/dist/core/plugins/index.d.ts +6 -0
  16. package/dist/core/plugins/index.js +6 -0
  17. package/dist/core/plugins/registry.d.ts +67 -0
  18. package/dist/core/plugins/registry.js +112 -0
  19. package/dist/css/index.d.ts +73 -0
  20. package/dist/css/index.js +246 -0
  21. package/dist/discovery/componentDiscovery.d.ts +41 -0
  22. package/dist/discovery/componentDiscovery.js +66 -0
  23. package/dist/discovery/layouts.d.ts +14 -0
  24. package/dist/discovery/layouts.js +36 -0
  25. package/dist/errors/compilerError.d.ts +31 -0
  26. package/dist/errors/compilerError.js +51 -0
  27. package/dist/finalize/generateFinalBundle.d.ts +24 -0
  28. package/dist/finalize/generateFinalBundle.js +68 -0
  29. package/dist/index.d.ts +34 -0
  30. package/dist/index.js +44 -0
  31. package/dist/ir/types.d.ts +206 -0
  32. package/dist/ir/types.js +8 -0
  33. package/dist/output/types.d.ts +39 -0
  34. package/dist/output/types.js +6 -0
  35. package/dist/parseZenFile.d.ts +17 -0
  36. package/dist/parseZenFile.js +52 -0
  37. package/dist/runtime/build.d.ts +6 -0
  38. package/dist/runtime/build.js +13 -0
  39. package/dist/runtime/bundle-generator.d.ts +27 -0
  40. package/dist/runtime/bundle-generator.js +1438 -0
  41. package/dist/runtime/client-runtime.d.ts +41 -0
  42. package/dist/runtime/client-runtime.js +397 -0
  43. package/dist/runtime/hydration.d.ts +53 -0
  44. package/dist/runtime/hydration.js +271 -0
  45. package/dist/runtime/navigation.d.ts +58 -0
  46. package/dist/runtime/navigation.js +372 -0
  47. package/dist/runtime/serve.d.ts +13 -0
  48. package/dist/runtime/serve.js +76 -0
  49. package/dist/runtime/thinRuntime.d.ts +23 -0
  50. package/dist/runtime/thinRuntime.js +158 -0
  51. package/dist/spa-build.d.ts +26 -0
  52. package/dist/spa-build.js +849 -0
  53. package/dist/ssg-build.d.ts +31 -0
  54. package/dist/ssg-build.js +429 -0
  55. package/dist/test/bundler-contract.test.d.ts +1 -0
  56. package/dist/test/bundler-contract.test.js +137 -0
  57. package/dist/test/compiler-entry.test.d.ts +1 -0
  58. package/dist/test/compiler-entry.test.js +28 -0
  59. package/dist/test/error-native-bridge.test.d.ts +1 -0
  60. package/dist/test/error-native-bridge.test.js +31 -0
  61. package/dist/test/error-serialization.test.d.ts +1 -0
  62. package/dist/test/error-serialization.test.js +38 -0
  63. package/dist/test/phase5-boundary.test.d.ts +1 -0
  64. package/dist/test/phase5-boundary.test.js +51 -0
  65. package/dist/transform/layoutProcessor.d.ts +26 -0
  66. package/dist/transform/layoutProcessor.js +34 -0
  67. package/dist/validate/invariants.d.ts +23 -0
  68. package/dist/validate/invariants.js +55 -0
  69. package/native/compiler-native/compiler-native.node +0 -0
  70. package/native/compiler-native/index.d.ts +2 -46
  71. package/native/compiler-native/index.js +1 -16
  72. package/native/compiler-native/package.json +1 -1
  73. 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,6 @@
1
+ /**
2
+ * Zenith Plugins
3
+ *
4
+ * Public exports for plugin system
5
+ */
6
+ export { PluginRegistry, createPluginContext } from './registry';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Zenith Plugins
3
+ *
4
+ * Public exports for plugin system
5
+ */
6
+ export { PluginRegistry, createPluginContext } from './registry';
@@ -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;