juxscript 1.0.132 → 1.1.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.
Files changed (133) hide show
  1. package/README.md +1 -32
  2. package/bin/cli.js +4 -2
  3. package/index.d.ts +200 -0
  4. package/index.js +96 -22
  5. package/juxconfig.example.js +58 -63
  6. package/lib/components/alert.ts +200 -0
  7. package/lib/components/app.ts +247 -0
  8. package/lib/components/badge.ts +101 -0
  9. package/lib/components/base/BaseComponent.ts +421 -0
  10. package/lib/components/base/FormInput.ts +227 -0
  11. package/lib/components/button.ts +178 -0
  12. package/lib/components/card.ts +173 -0
  13. package/lib/components/chart.ts +231 -0
  14. package/lib/components/checkbox.ts +242 -0
  15. package/lib/components/code.ts +123 -0
  16. package/lib/components/container.ts +140 -0
  17. package/lib/components/data.ts +135 -0
  18. package/lib/components/datepicker.ts +234 -0
  19. package/lib/components/dialog.ts +172 -0
  20. package/lib/components/divider.ts +100 -0
  21. package/lib/components/dropdown.ts +186 -0
  22. package/lib/components/element.ts +267 -0
  23. package/lib/components/fileupload.ts +309 -0
  24. package/lib/components/grid.ts +291 -0
  25. package/lib/components/guard.ts +92 -0
  26. package/lib/components/heading.ts +96 -0
  27. package/lib/components/helpers.ts +41 -0
  28. package/lib/components/hero.ts +224 -0
  29. package/lib/components/icon.ts +178 -0
  30. package/lib/components/icons.ts +464 -0
  31. package/lib/components/include.ts +410 -0
  32. package/lib/components/input.ts +457 -0
  33. package/lib/components/list.ts +419 -0
  34. package/lib/components/loading.ts +100 -0
  35. package/lib/components/menu.ts +275 -0
  36. package/lib/components/modal.ts +284 -0
  37. package/lib/components/nav.ts +257 -0
  38. package/lib/components/paragraph.ts +97 -0
  39. package/lib/components/progress.ts +159 -0
  40. package/lib/components/radio.ts +278 -0
  41. package/lib/components/req.ts +303 -0
  42. package/lib/components/script.ts +41 -0
  43. package/lib/components/select.ts +252 -0
  44. package/lib/components/sidebar.ts +275 -0
  45. package/lib/components/style.ts +41 -0
  46. package/lib/components/switch.ts +246 -0
  47. package/lib/components/table.ts +1249 -0
  48. package/lib/components/tabs.ts +250 -0
  49. package/lib/components/theme-toggle.ts +293 -0
  50. package/lib/components/tooltip.ts +144 -0
  51. package/lib/components/view.ts +190 -0
  52. package/lib/components/write.ts +272 -0
  53. package/lib/globals.d.ts +19 -5
  54. package/lib/layouts/default.css +260 -0
  55. package/lib/layouts/figma.css +334 -0
  56. package/lib/reactivity/state.ts +78 -0
  57. package/lib/utils/{fetch.js → fetch.ts} +206 -81
  58. package/machinery/ast.js +347 -0
  59. package/machinery/build.js +466 -0
  60. package/machinery/compiler3.js +6 -66
  61. package/machinery/config.js +6 -93
  62. package/machinery/doc-generator.js +136 -0
  63. package/machinery/imports.js +155 -0
  64. package/machinery/server.js +166 -0
  65. package/machinery/ts-shim.js +46 -0
  66. package/machinery/watcher.js +162 -50
  67. package/package.json +9 -30
  68. package/create/index.jux +0 -77
  69. package/create/layout.jux +0 -18
  70. package/create/style.css +0 -57
  71. package/create/themes/assets/jux.svg +0 -34
  72. package/create/themes/base.css +0 -197
  73. package/create/themes/base2.css +0 -54
  74. package/create/themes/layouts/base.jux +0 -16
  75. package/create/themes/layouts/base_marketing.jux +0 -0
  76. package/create/themes/layouts/base_saas.jux +0 -0
  77. package/lib/componentsv2/base/BaseEngine.d.ts +0 -112
  78. package/lib/componentsv2/base/BaseEngine.js +0 -279
  79. package/lib/componentsv2/base/BaseSkin.d.ts +0 -74
  80. package/lib/componentsv2/base/BaseSkin.js +0 -130
  81. package/lib/componentsv2/base/Neighborhood.d.ts +0 -22
  82. package/lib/componentsv2/base/Neighborhood.js +0 -56
  83. package/lib/componentsv2/base/OptionsContract.d.ts +0 -20
  84. package/lib/componentsv2/base/OptionsContract.js +0 -107
  85. package/lib/componentsv2/base/State.d.ts +0 -18
  86. package/lib/componentsv2/base/State.js +0 -68
  87. package/lib/componentsv2/element/Element.d.ts +0 -30
  88. package/lib/componentsv2/element/Element.js +0 -50
  89. package/lib/componentsv2/element/ElementEngine.d.ts +0 -59
  90. package/lib/componentsv2/element/ElementEngine.js +0 -118
  91. package/lib/componentsv2/element/ElementSkin.d.ts +0 -10
  92. package/lib/componentsv2/element/ElementSkin.js +0 -56
  93. package/lib/componentsv2/element/structure.css +0 -261
  94. package/lib/componentsv2/grid/Grid.d.ts +0 -13
  95. package/lib/componentsv2/grid/Grid.js +0 -27
  96. package/lib/componentsv2/grid/GridEngine.d.ts +0 -77
  97. package/lib/componentsv2/grid/GridEngine.js +0 -153
  98. package/lib/componentsv2/grid/GridSkin.d.ts +0 -11
  99. package/lib/componentsv2/grid/GridSkin.js +0 -84
  100. package/lib/componentsv2/grid/structure.css +0 -27
  101. package/lib/componentsv2/input/Input.d.ts +0 -6
  102. package/lib/componentsv2/input/Input.js +0 -21
  103. package/lib/componentsv2/input/InputEngine.d.ts +0 -70
  104. package/lib/componentsv2/input/InputEngine.js +0 -143
  105. package/lib/componentsv2/input/InputSkin.d.ts +0 -11
  106. package/lib/componentsv2/input/InputSkin.js +0 -89
  107. package/lib/componentsv2/input/structure.css +0 -47
  108. package/lib/componentsv2/list/List.d.ts +0 -49
  109. package/lib/componentsv2/list/List.js +0 -105
  110. package/lib/componentsv2/list/ListEngine.d.ts +0 -121
  111. package/lib/componentsv2/list/ListEngine.js +0 -322
  112. package/lib/componentsv2/list/ListSkin.d.ts +0 -20
  113. package/lib/componentsv2/list/ListSkin.js +0 -345
  114. package/lib/componentsv2/list/structure.css +0 -359
  115. package/lib/componentsv2/plugins/ClientSQLitePlugin.d.ts +0 -21
  116. package/lib/componentsv2/plugins/ClientSQLitePlugin.js +0 -130
  117. package/lib/componentsv2/plugins/IndexedDBPlugin.d.ts +0 -18
  118. package/lib/componentsv2/plugins/IndexedDBPlugin.js +0 -75
  119. package/lib/componentsv2/plugins/LocalStoragePlugin.d.ts +0 -20
  120. package/lib/componentsv2/plugins/LocalStoragePlugin.js +0 -65
  121. package/lib/componentsv2/plugins/ServerSQLitePlugin.d.ts +0 -25
  122. package/lib/componentsv2/plugins/ServerSQLitePlugin.js +0 -70
  123. package/lib/componentsv2/stubs/ComponentComposition.ts.stub +0 -32
  124. package/lib/componentsv2/stubs/ComponentEngine.ts.stub +0 -36
  125. package/lib/componentsv2/stubs/ComponentSkin.ts.stub +0 -35
  126. package/lib/componentsv2/stubs/ComponentStructure.css.d.ts.stub +0 -2
  127. package/lib/componentsv2/stubs/ComponentStructure.css.stub +0 -13
  128. package/lib/utils/fetch.d.ts +0 -176
  129. package/machinery/serve.js +0 -255
  130. package/types/css.d.ts +0 -10
  131. /package/{create/themes/layouts/base_blog.jux → machinery/bundleAssets.js} +0 -0
  132. /package/{create/themes/layouts/base_docs.jux → machinery/bundleJux.js} +0 -0
  133. /package/{create/themes/layouts/base_login.jux → machinery/bundleVendors.js} +0 -0
@@ -1,25 +0,0 @@
1
- import { JuxServiceContract, BaseEngine } from '../base/BaseEngine.js';
2
- export interface ServerSQLiteConfig {
3
- /** The Database Driver Instance (e.g. better-sqlite3) */
4
- database: any;
5
- /** Optional: SQL Query to fetch data automatically */
6
- query?: string;
7
- /**
8
- * State Binding: The property key on the Engine's state to populate.
9
- * e.g., 'items' for a ListEngine, or 'rows' for a GridEngine.
10
- */
11
- bindTo?: string;
12
- /**
13
- * Transformation Strategy:
14
- * Converts a raw DB row into the shape expected by the Engine's state.
15
- */
16
- mapRow?: (row: any) => any;
17
- /** If true, runs the query immediately on install */
18
- autoLoad?: boolean;
19
- }
20
- /**
21
- * Server-Side SQLite Plugin
22
- * Decoupled Service that injects SQL capabilities and binds data to ANY Engine state.
23
- */
24
- export declare const ServerSQLitePlugin: (config: ServerSQLiteConfig) => JuxServiceContract<BaseEngine<any>>;
25
- //# sourceMappingURL=ServerSQLitePlugin.d.ts.map
@@ -1,70 +0,0 @@
1
- /**
2
- * Server-Side SQLite Plugin
3
- * Decoupled Service that injects SQL capabilities and binds data to ANY Engine state.
4
- */
5
- export const ServerSQLitePlugin = (config) => ({
6
- name: 'server-sqlite-driver',
7
- version: '2.0.0',
8
- targetEnv: 'server',
9
- install: (engine) => {
10
- // 1. Expose SQL Capability (Mixin)
11
- // Adds a .query() method to the engine instance
12
- Object.assign(engine, {
13
- query: (sql, params = []) => {
14
- if (!config.database || typeof config.database.prepare !== 'function') {
15
- throw new Error('Invalid Database Driver provided to ServerSQLitePlugin.');
16
- }
17
- const stmt = config.database.prepare(sql);
18
- const rows = stmt.all(params);
19
- // Emit audit event for queries
20
- // @ts-ignore - access protected emit
21
- if (typeof engine.emit === 'function')
22
- engine.emit('sql:query', { sql, rows: rows.length });
23
- return rows;
24
- }
25
- });
26
- // 2. Hydration Logic
27
- const hydrate = () => {
28
- if (!config.query)
29
- return;
30
- try {
31
- // Use the newly injected capability
32
- // @ts-ignore
33
- const rows = engine.query(config.query);
34
- if (config.bindTo) {
35
- const data = config.mapRow ? rows.map(config.mapRow) : rows;
36
- // Generic State Update (Decoupled from ListEngine)
37
- // @ts-ignore - access protected updateState
38
- if (typeof engine.updateState === 'function') {
39
- // @ts-ignore
40
- engine.updateState({ [config.bindTo]: data });
41
- }
42
- // Optional: Update loading state if the engine supports it
43
- // @ts-ignore
44
- if (typeof engine.loading === 'function')
45
- engine.loading(false);
46
- // Emit data event for loose coupling
47
- // @ts-ignore
48
- if (typeof engine.emit === 'function')
49
- engine.emit('plugin:data', { source: 'sqlite', count: data.length });
50
- }
51
- }
52
- catch (err) {
53
- console.error('[ServerSQLitePlugin] Hydration Error:', err.message);
54
- // @ts-ignore
55
- if (typeof engine.loading === 'function')
56
- engine.loading(false);
57
- }
58
- };
59
- // 3. Auto-Start
60
- if (config.autoLoad) {
61
- // @ts-ignore
62
- if (typeof engine.loading === 'function')
63
- engine.loading(true);
64
- hydrate();
65
- }
66
- // 4. Listen for Reload Signal
67
- engine.on('db:reload', hydrate);
68
- }
69
- });
70
- //# sourceMappingURL=ServerSQLitePlugin.js.map
@@ -1,32 +0,0 @@
1
- import { [% ComponentName %]Engine, [% ComponentName %]Options } from './engine.js';
2
- import { [% ComponentName %]Skin } from './skin.js';
3
-
4
- export type [% ComponentName %]Component = [% ComponentName %]Engine & {
5
- render: (targetId: string | HTMLElement) => [% ComponentName %]Component;
6
- injectCSS: (id: string, cssContent: string) => void;
7
- };
8
-
9
- export function [% ComponentName %](id: string, options: [% ComponentName %]Options = {}): [% ComponentName %]Component {
10
- const engine = new [% ComponentName %]Engine(id, options);
11
-
12
- if (typeof window !== 'undefined') {
13
- // @ts-ignore
14
- window.juxEngines = window.juxEngines || {};
15
- // @ts-ignore
16
- window.juxEngines[id] = engine;
17
- }
18
-
19
- const skin = new [% ComponentName %]Skin(engine);
20
-
21
- // @ts-ignore
22
- engine.render = (targetId: string | HTMLElement) => {
23
- const target = typeof targetId === 'string' ? document.getElementById(targetId) : targetId;
24
- if (target) skin.renderSkin(target);
25
- return engine as [% ComponentName %]Component;
26
- };
27
-
28
- // @ts-ignore
29
- engine.injectCSS = (id, css) => skin.injectCSS(id, css);
30
-
31
- return engine as [% ComponentName %]Component;
32
- }
@@ -1,36 +0,0 @@
1
- import { BaseEngine, BaseState } from '../base/BaseEngine.js';
2
-
3
- export interface [% ComponentName %]State extends BaseState {
4
- // TODO: Define your state properties
5
- text: string;
6
- }
7
-
8
- export interface [% ComponentName %]Options {
9
- // TODO: Define configuration options
10
- text?: string;
11
- }
12
-
13
- export class [% ComponentName %]Engine extends BaseEngine<[% ComponentName %]State, [% ComponentName %]Options> {
14
- constructor(id: string, options: [% ComponentName %]Options = {}) {
15
- super(id, options);
16
- }
17
-
18
- protected prepareState(id: string, options: [% ComponentName %]Options): [% ComponentName %]State {
19
- return {
20
- id,
21
- classes: ['jux-[% component-name %]'],
22
- visible: true,
23
- disabled: false,
24
- loading: false,
25
- attributes: {},
26
- text: options.text || 'Hello World'
27
- };
28
- }
29
-
30
- // Example Mutator
31
- setText(value: string): this {
32
- this.updateState({ text: value });
33
- this.emit('textChange', { value });
34
- return this;
35
- }
36
- }
@@ -1,35 +0,0 @@
1
- import { BaseSkin } from '../base/BaseSkin.js';
2
- import { [% ComponentName %]Engine, [% ComponentName %]State } from './engine.js';
3
- import structureCss from './structure.css';
4
-
5
- export class [% ComponentName %]Skin extends BaseSkin<[% ComponentName %]State, [% ComponentName %]Engine> {
6
- constructor(engine: [% ComponentName %]Engine) {
7
- super(engine);
8
- }
9
-
10
- /**
11
- * Define the structural CSS for this component.
12
- */
13
- protected get structureCss(): string {
14
- return structureCss;
15
- }
16
-
17
- protected bindEvents(root: HTMLElement): void {
18
- // TODO: Bind DOM events to Engine methods
19
- root.addEventListener('click', () => {
20
- console.log('[% ComponentName %] clicked');
21
- });
22
- }
23
-
24
- protected updateSkin(state: [% ComponentName %]State): void {
25
- if (!this.root) return;
26
- this.applySkinAttributes(this.root, state);
27
-
28
- // TODO: Render state to DOM
29
- this.root.textContent = state.text;
30
- }
31
-
32
- protected createRoot(): HTMLElement {
33
- return document.createElement('div');
34
- }
35
- }
@@ -1,2 +0,0 @@
1
- declare const styles: string;
2
- export default styles;
@@ -1,13 +0,0 @@
1
- .jux-[% component-name %] {
2
- display: block;
3
- padding: var(--space-md);
4
- border: 1px solid var(--color-border);
5
- border-radius: var(--radius-sm);
6
- background-color: var(--color-surface-base);
7
- color: var(--color-text-primary);
8
- transition: all var(--transition-fast);
9
- }
10
-
11
- .jux-[% component-name %]:hover {
12
- background-color: var(--color-surface-hover);
13
- }
@@ -1,176 +0,0 @@
1
- /**
2
- * Jux Fetch Utility
3
- *
4
- * A lightweight fetch wrapper with sensible defaults, error handling, and method chaining.
5
- * Includes configuration helpers for juxconfig.js integration.
6
- *
7
- * Usage:
8
- * // Configure once (optional)
9
- * jux.fetch.config({
10
- * baseUrl: 'https://api.example.com',
11
- * credentials: 'include',
12
- * timeout: 10000,
13
- * log: 'errors',
14
- * onUnauthorized: () => window.location.href = '/login'
15
- * });
16
- *
17
- * // Simple GET
18
- * const { data, error } = await jux.fetch('/users').send();
19
- *
20
- * // Method chaining
21
- * const { data, error } = await jux.fetch('/users')
22
- * .method('POST')
23
- * .body({ name: 'John' })
24
- * .params({ limit: 10 })
25
- * .timeout(5000)
26
- * .log(true)
27
- * .send();
28
- *
29
- * // With juxconfig services
30
- * const { data } = await jux.fetch('/users')
31
- * .baseUrl(jux.fetch.getServiceUrl('database'))
32
- * .send();
33
- *
34
- * // Service client helper
35
- * const db = jux.fetch.serviceClient('database');
36
- * const { data } = await db.fetch('/users').send();
37
- */
38
- export type LogLevel = boolean | 'errors';
39
- export interface FetchConfig {
40
- baseUrl?: string;
41
- credentials?: RequestCredentials;
42
- headers?: Record<string, string>;
43
- timeout?: number;
44
- log?: LogLevel;
45
- onUnauthorized?: () => void;
46
- onError?: (error: FetchError) => void;
47
- }
48
- export interface FetchOptions extends Omit<RequestInit, 'body'> {
49
- params?: Record<string, any>;
50
- body?: any;
51
- timeout?: number;
52
- log?: LogLevel;
53
- onUnauthorized?: () => void;
54
- onError?: (error: FetchError) => void;
55
- parseResponse?: boolean;
56
- }
57
- export interface FetchError {
58
- message: string;
59
- status?: number;
60
- statusText?: string;
61
- data?: any;
62
- }
63
- export interface FetchResult<T = any> {
64
- data: T | null;
65
- error: FetchError | null;
66
- status: number;
67
- response: Response;
68
- }
69
- /**
70
- * Configure global fetch defaults
71
- */
72
- export declare function configureFetch(config: FetchConfig): void;
73
- /**
74
- * FetchBuilder class for method chaining
75
- */
76
- declare class FetchBuilder<T = any> {
77
- url: string;
78
- options: FetchOptions;
79
- constructor(url: string, options?: FetchOptions);
80
- method(value: string): this;
81
- body(value: any): this;
82
- params(value: Record<string, any>): this;
83
- headers(value: Record<string, string>): this;
84
- header(key: string, value: string): this;
85
- timeout(value: number): this;
86
- credentials(value: RequestCredentials): this;
87
- log(value: LogLevel): this;
88
- parseResponse(value: boolean): this;
89
- onUnauthorized(callback: () => void): this;
90
- onError(callback: (error: FetchError) => void): this;
91
- /**
92
- * Execute the fetch request
93
- */
94
- send(): Promise<FetchResult<T>>;
95
- /**
96
- * Alias for send()
97
- */
98
- fetch(): Promise<FetchResult<T>>;
99
- /**
100
- * Make the builder thenable (Promise-like) so it can be awaited directly
101
- * This allows: await jux.fetch('/users') without needing .send()
102
- */
103
- then<TResult1 = FetchResult<T>, TResult2 = never>(onfulfilled?: ((value: FetchResult<T>) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
104
- /**
105
- * Make the builder catchable for Promise.catch()
106
- */
107
- catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null): Promise<FetchResult<T> | TResult>;
108
- /**
109
- * Make the builder finally-able for Promise.finally()
110
- */
111
- finally(onfinally?: (() => void) | null): Promise<FetchResult<T>>;
112
- }
113
- /**
114
- * Create a fetch builder
115
- */
116
- export declare function juxFetch<T = any>(url: string, options?: FetchOptions): FetchBuilder<T>;
117
- /**
118
- * Convenience methods for common HTTP verbs
119
- */
120
- export declare const fetchHelpers: {
121
- get: <T = any>(url: string, options?: Omit<FetchOptions, "method">) => FetchBuilder<T>;
122
- post: <T = any>(url: string, body?: any, options?: Omit<FetchOptions, "method" | "body">) => FetchBuilder<T>;
123
- put: <T = any>(url: string, body?: any, options?: Omit<FetchOptions, "method" | "body">) => FetchBuilder<T>;
124
- patch: <T = any>(url: string, body?: any, options?: Omit<FetchOptions, "method" | "body">) => FetchBuilder<T>;
125
- delete: <T = any>(url: string, options?: Omit<FetchOptions, "method">) => FetchBuilder<T>;
126
- };
127
- /**
128
- * Fetch multiple URLs in parallel
129
- */
130
- export declare function fetchAll<T = any>(urls: string[], options?: FetchOptions): Promise<FetchResult<T>[]>;
131
- /**
132
- * Get a service URL from window.juxConfig
133
- */
134
- export declare function getServiceUrl(serviceName: string): string | null;
135
- /**
136
- * Create a fetch instance preconfigured for a specific service
137
- *
138
- * Usage:
139
- * const api = jux.fetch.serviceClient('database');
140
- * const { data } = await api.fetch('/users').send();
141
- */
142
- export declare function serviceClient(serviceName: string): {
143
- fetch: (url: string, options?: any) => FetchBuilder<any>;
144
- baseUrl: string | null;
145
- };
146
- /**
147
- * Setup fetch from juxconfig
148
- * Call this in your bootstrap function
149
- *
150
- * Usage:
151
- * export default {
152
- * bootstrap: [
153
- * async function initFetch() {
154
- * await jux.fetch.setupConfig();
155
- * }
156
- * ]
157
- * };
158
- */
159
- export declare function setupConfig(): Promise<{
160
- services: any;
161
- getServiceUrl: typeof getServiceUrl;
162
- } | undefined>;
163
- export declare const fetchAPI: typeof juxFetch & {
164
- all: typeof fetchAll;
165
- get: <T = any>(url: string, options?: Omit<FetchOptions, "method">) => FetchBuilder<T>;
166
- post: <T = any>(url: string, body?: any, options?: Omit<FetchOptions, "method" | "body">) => FetchBuilder<T>;
167
- put: <T = any>(url: string, body?: any, options?: Omit<FetchOptions, "method" | "body">) => FetchBuilder<T>;
168
- patch: <T = any>(url: string, body?: any, options?: Omit<FetchOptions, "method" | "body">) => FetchBuilder<T>;
169
- delete: <T = any>(url: string, options?: Omit<FetchOptions, "method">) => FetchBuilder<T>;
170
- config: typeof configureFetch;
171
- setupConfig: typeof setupConfig;
172
- getServiceUrl: typeof getServiceUrl;
173
- serviceClient: typeof serviceClient;
174
- };
175
- export {};
176
- //# sourceMappingURL=fetch.d.ts.map
@@ -1,255 +0,0 @@
1
- import express from 'express';
2
- import path from 'path';
3
- import fs from 'fs';
4
- import http from 'http';
5
- import { fileURLToPath } from 'url';
6
- import { WebSocketServer } from 'ws';
7
- import { createWatcher } from './watcher.js';
8
- import { JuxCompiler } from './compiler3.js';
9
-
10
- const __filename = fileURLToPath(import.meta.url);
11
- const __dirname = path.dirname(__filename);
12
-
13
- // ═══════════════════════════════════════════════════════════════
14
- // CONFIGURATION
15
- // ═══════════════════════════════════════════════════════════════
16
- const args = process.argv.slice(2);
17
- const HOT_RELOAD = args.includes('--hot') || args.includes('-h') || args.includes('--watch');
18
-
19
- // Extract port from args (e.g., --port=3000 or -p 3000)
20
- function getArgValue(flag, shortFlag, defaultValue) {
21
- for (let i = 0; i < args.length; i++) {
22
- if (args[i].startsWith(`${flag}=`)) return args[i].split('=')[1];
23
- if (args[i].startsWith(`${shortFlag}=`)) return args[i].split('=')[1];
24
- if ((args[i] === flag || args[i] === shortFlag) && args[i + 1]) return args[i + 1];
25
- }
26
- return defaultValue;
27
- }
28
-
29
- const PORT = parseInt(getArgValue('--port', '-p', process.env.PORT || '3000'));
30
- const WS_PORT = parseInt(getArgValue('--ws-port', '-w', process.env.WS_PORT || String(PORT + 1)));
31
-
32
- // Resolve paths relative to CWD (user's project)
33
- const PROJECT_ROOT = process.cwd();
34
- const DIST_DIR = path.resolve(PROJECT_ROOT, '.jux-dist');
35
- const SRC_DIR = path.resolve(PROJECT_ROOT, 'jux');
36
-
37
- const app = express();
38
- let lastBuildResult = { success: true, errors: [] };
39
-
40
- // Check if dist directory exists - if not, try to build first
41
- if (!fs.existsSync(DIST_DIR) || !fs.existsSync(path.join(DIST_DIR, 'index.html'))) {
42
- console.log('āš ļø Dist directory not found. Running initial build...');
43
-
44
- const compiler = new JuxCompiler({
45
- srcDir: SRC_DIR,
46
- distDir: DIST_DIR
47
- });
48
-
49
- try {
50
- const result = await compiler.build();
51
- lastBuildResult = result;
52
-
53
- if (!result.success) {
54
- console.log('\nāŒ Initial build failed. Starting server to show error overlay.\n');
55
- }
56
- } catch (err) {
57
- console.error('āŒ Initial build failed:', err.message);
58
- lastBuildResult = { success: false, errors: [{ message: err.message }] };
59
- }
60
- }
61
-
62
- if (!fs.existsSync(DIST_DIR)) {
63
- fs.mkdirSync(DIST_DIR, { recursive: true });
64
- }
65
-
66
- app.use((req, res, next) => {
67
- res.setHeader('Cache-Control', 'no-store');
68
- next();
69
- });
70
-
71
- app.use((req, res, next) => {
72
- if (req.path.endsWith('.js')) {
73
- res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
74
- }
75
- next();
76
- });
77
-
78
- app.get('/favicon.ico', (req, res) => res.status(204).end());
79
- app.get('/favicon.png', (req, res) => res.status(204).end());
80
-
81
- app.get('/__jux_sources.json', (req, res) => {
82
- const snapshotPath = path.join(DIST_DIR, '__jux_sources.json');
83
- if (fs.existsSync(snapshotPath)) {
84
- res.setHeader('Content-Type', 'application/json');
85
- res.sendFile(snapshotPath);
86
- } else {
87
- res.json({});
88
- }
89
- });
90
-
91
- const hotReloadScript = `
92
- <script>
93
- (function() {
94
- var ws = new WebSocket('ws://localhost:${WS_PORT}');
95
- ws.onopen = function() { console.log('šŸ”„ Hot reload connected'); };
96
- ws.onmessage = function(e) {
97
- var data = JSON.parse(e.data);
98
- if (data.type === 'reload') location.reload();
99
- else if (data.type === 'build-error') console.error('āŒ Build error:', data.errors);
100
- };
101
- ws.onclose = function() { setTimeout(function() { location.reload(); }, 2000); };
102
- })();
103
- </script>
104
- `;
105
-
106
- function getErrorPageHtml(errors) {
107
- return `<!DOCTYPE html>
108
- <html lang="en">
109
- <head>
110
- <meta charset="UTF-8">
111
- <title>JUX Build Error</title>
112
- <style>
113
- * { margin: 0; padding: 0; box-sizing: border-box; }
114
- body { font-family: monospace; background: #1a1a2e; color: #eee; min-height: 100vh; padding: 40px; }
115
- .container { max-width: 800px; margin: 0 auto; }
116
- h1 { color: #ff6b6b; margin-bottom: 8px; }
117
- .subtitle { color: #888; margin-bottom: 30px; }
118
- .error-card { background: #16213e; padding: 16px 20px; border-radius: 8px; margin-bottom: 12px; border-left: 4px solid #ff6b6b; }
119
- .error-header { color: #ff6b6b; font-weight: bold; margin-bottom: 8px; }
120
- .error-code { background: #0f0f23; padding: 12px; border-radius: 4px; font-size: 13px; color: #888; }
121
- </style>
122
- </head>
123
- <body>
124
- <div class="container">
125
- <h1>šŸ›‘ Build Failed</h1>
126
- <p class="subtitle">Fix the errors below and save to rebuild.</p>
127
- ${errors.map(err => `
128
- <div class="error-card">
129
- <div class="error-header">${err.view ? `[${err.view}] ` : ''}${err.message}</div>
130
- ${err.code ? `<pre class="error-code">${err.code}</pre>` : ''}
131
- </div>
132
- `).join('')}
133
- </div>
134
- ${HOT_RELOAD ? `<script>
135
- var ws = new WebSocket('ws://localhost:${WS_PORT}');
136
- ws.onmessage = function(e) { if (JSON.parse(e.data).type === 'reload') location.reload(); };
137
- </script>` : ''}
138
- </body>
139
- </html>`;
140
- }
141
-
142
- if (HOT_RELOAD) {
143
- app.get(['/', '/index.html'], (req, res) => {
144
- const indexPath = path.join(DIST_DIR, 'index.html');
145
-
146
- if (!fs.existsSync(indexPath) || !lastBuildResult.success) {
147
- return res.send(getErrorPageHtml(lastBuildResult.errors));
148
- }
149
-
150
- let html = fs.readFileSync(indexPath, 'utf8');
151
- html = html.replace('</body>', hotReloadScript + '</body>');
152
- res.setHeader('Content-Type', 'text/html');
153
- res.send(html);
154
- });
155
- }
156
-
157
- app.use(express.static(DIST_DIR));
158
-
159
- app.get('*', (req, res) => {
160
- if (path.extname(req.path) && req.path !== '/') {
161
- return res.status(404).send('Not found');
162
- }
163
-
164
- const indexPath = path.join(DIST_DIR, 'index.html');
165
-
166
- if (!fs.existsSync(indexPath) || !lastBuildResult.success) {
167
- return res.send(getErrorPageHtml(lastBuildResult.errors));
168
- }
169
-
170
- if (HOT_RELOAD) {
171
- let html = fs.readFileSync(indexPath, 'utf8');
172
- html = html.replace('</body>', hotReloadScript + '</body>');
173
- res.setHeader('Content-Type', 'text/html');
174
- res.send(html);
175
- } else {
176
- res.sendFile(indexPath);
177
- }
178
- });
179
-
180
- const server = http.createServer(app);
181
- let wss = null;
182
- let watcher = null;
183
-
184
- if (HOT_RELOAD) {
185
- wss = new WebSocketServer({ port: WS_PORT });
186
- const clients = new Set();
187
-
188
- wss.on('connection', (ws) => {
189
- clients.add(ws);
190
- ws.on('close', () => clients.delete(ws));
191
- });
192
-
193
- const broadcast = (message) => {
194
- const data = JSON.stringify(message);
195
- clients.forEach(client => {
196
- if (client.readyState === 1) client.send(data);
197
- });
198
- };
199
-
200
- const compiler = new JuxCompiler({
201
- srcDir: SRC_DIR,
202
- distDir: DIST_DIR
203
- });
204
-
205
- watcher = createWatcher(SRC_DIR, {
206
- onChange: async (changedFiles) => {
207
- console.log('šŸ”„ Rebuilding...');
208
-
209
- try {
210
- const result = await compiler.build();
211
- lastBuildResult = result;
212
-
213
- if (!result.success) {
214
- console.error('āŒ Rebuild failed');
215
- broadcast({ type: 'build-error', errors: result.errors });
216
- return;
217
- }
218
-
219
- console.log('āœ… Rebuild complete');
220
- broadcast({ type: 'reload', files: changedFiles });
221
- } catch (err) {
222
- console.error('āŒ Rebuild failed:', err.message);
223
- lastBuildResult = { success: false, errors: [{ message: err.message }] };
224
- broadcast({ type: 'build-error', errors: [{ message: err.message }] });
225
- }
226
- },
227
- onReady: () => {
228
- console.log(`šŸ‘€ Watching: ${SRC_DIR}`);
229
- }
230
- });
231
- }
232
-
233
- server.listen(PORT, () => {
234
- console.log(`\nšŸš€ JUX Server running at http://localhost:${PORT}`);
235
- if (HOT_RELOAD) {
236
- console.log(`šŸ”„ Hot reload: ws://localhost:${WS_PORT}`);
237
- }
238
- if (!lastBuildResult.success) {
239
- console.log(`āš ļø Build has errors - fix them and save\n`);
240
- }
241
- });
242
-
243
- const shutdown = () => {
244
- console.log('\nšŸ‘‹ Shutting down...');
245
- if (watcher) watcher.close();
246
- if (wss) {
247
- wss.clients.forEach(client => client.terminate());
248
- wss.close();
249
- }
250
- server.close(() => process.exit(0));
251
- setTimeout(() => process.exit(0), 2000);
252
- };
253
-
254
- process.on('SIGINT', shutdown);
255
- process.on('SIGTERM', shutdown);
package/types/css.d.ts DELETED
@@ -1,10 +0,0 @@
1
- /**
2
- * CSS Module Type Declaration
3
- *
4
- * This single declaration handles ALL .css imports in the project.
5
- * No need for individual .d.ts files next to each CSS file.
6
- */
7
- declare module '*.css' {
8
- const content: string;
9
- export default content;
10
- }