juxscript 1.0.0

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 (61) hide show
  1. package/README.md +292 -0
  2. package/bin/cli.js +149 -0
  3. package/lib/adapters/base-adapter.js +35 -0
  4. package/lib/adapters/index.js +33 -0
  5. package/lib/adapters/mysql-adapter.js +65 -0
  6. package/lib/adapters/postgres-adapter.js +70 -0
  7. package/lib/adapters/sqlite-adapter.js +56 -0
  8. package/lib/components/app.ts +124 -0
  9. package/lib/components/button.ts +136 -0
  10. package/lib/components/card.ts +205 -0
  11. package/lib/components/chart.ts +125 -0
  12. package/lib/components/code.ts +242 -0
  13. package/lib/components/container.ts +282 -0
  14. package/lib/components/data.ts +105 -0
  15. package/lib/components/docs-data.json +1211 -0
  16. package/lib/components/error-handler.ts +285 -0
  17. package/lib/components/footer.ts +146 -0
  18. package/lib/components/header.ts +167 -0
  19. package/lib/components/hero.ts +170 -0
  20. package/lib/components/import.ts +430 -0
  21. package/lib/components/input.ts +175 -0
  22. package/lib/components/layout.ts +113 -0
  23. package/lib/components/list.ts +392 -0
  24. package/lib/components/main.ts +111 -0
  25. package/lib/components/menu.ts +170 -0
  26. package/lib/components/modal.ts +216 -0
  27. package/lib/components/nav.ts +136 -0
  28. package/lib/components/node.ts +200 -0
  29. package/lib/components/reactivity.js +104 -0
  30. package/lib/components/script.ts +152 -0
  31. package/lib/components/sidebar.ts +168 -0
  32. package/lib/components/style.ts +129 -0
  33. package/lib/components/table.ts +279 -0
  34. package/lib/components/tabs.ts +191 -0
  35. package/lib/components/theme.ts +97 -0
  36. package/lib/components/view.ts +174 -0
  37. package/lib/jux.ts +203 -0
  38. package/lib/layouts/default.css +260 -0
  39. package/lib/layouts/default.jux +8 -0
  40. package/lib/layouts/figma.css +334 -0
  41. package/lib/layouts/figma.jux +0 -0
  42. package/lib/layouts/notion.css +258 -0
  43. package/lib/styles/base-theme.css +186 -0
  44. package/lib/styles/dark-theme.css +144 -0
  45. package/lib/styles/global.css +1131 -0
  46. package/lib/styles/light-theme.css +144 -0
  47. package/lib/styles/tokens/dark.css +86 -0
  48. package/lib/styles/tokens/light.css +86 -0
  49. package/lib/themes/dark.css +86 -0
  50. package/lib/themes/light.css +86 -0
  51. package/lib/utils/path-resolver.js +23 -0
  52. package/machinery/compiler.js +262 -0
  53. package/machinery/doc-generator.js +160 -0
  54. package/machinery/generators/css.js +128 -0
  55. package/machinery/generators/html.js +108 -0
  56. package/machinery/imports.js +155 -0
  57. package/machinery/server.js +185 -0
  58. package/machinery/validators/file-validator.js +123 -0
  59. package/machinery/watcher.js +148 -0
  60. package/package.json +58 -0
  61. package/types/globals.d.ts +16 -0
@@ -0,0 +1,170 @@
1
+ import { Reactive, getOrCreateContainer } from './reactivity.js';
2
+
3
+ /**
4
+ * Hero component options
5
+ */
6
+ export interface HeroOptions {
7
+ title?: string;
8
+ subtitle?: string;
9
+ cta?: string;
10
+ ctaLink?: string;
11
+ backgroundImage?: string;
12
+ variant?: 'default' | 'centered' | 'split';
13
+ }
14
+
15
+ /**
16
+ * Hero component state
17
+ */
18
+ type HeroState = {
19
+ title: string;
20
+ subtitle: string;
21
+ cta: string;
22
+ ctaLink: string;
23
+ backgroundImage: string;
24
+ variant: string;
25
+ };
26
+
27
+ /**
28
+ * Hero component
29
+ *
30
+ * Usage:
31
+ * const hero = jux.hero('myHero', {
32
+ * title: 'Welcome',
33
+ * subtitle: 'Get started today',
34
+ * cta: 'Learn More'
35
+ * });
36
+ * hero.render();
37
+ */
38
+ export class Hero extends Reactive {
39
+ state!: HeroState;
40
+ container: HTMLElement | null = null;
41
+
42
+ constructor(componentId: string, options: HeroOptions = {}) {
43
+ super();
44
+ this._setComponentId(componentId);
45
+
46
+ this.state = this._createReactiveState({
47
+ title: options.title ?? '',
48
+ subtitle: options.subtitle ?? '',
49
+ cta: options.cta ?? '',
50
+ ctaLink: options.ctaLink ?? '#',
51
+ backgroundImage: options.backgroundImage ?? '',
52
+ variant: options.variant ?? 'default'
53
+ }) as HeroState;
54
+ }
55
+
56
+ /* -------------------------
57
+ * Fluent API
58
+ * ------------------------- */
59
+
60
+ title(value: string): this {
61
+ this.state.title = value;
62
+ return this;
63
+ }
64
+
65
+ subtitle(value: string): this {
66
+ this.state.subtitle = value;
67
+ return this;
68
+ }
69
+
70
+ cta(value: string): this {
71
+ this.state.cta = value;
72
+ return this;
73
+ }
74
+
75
+ ctaLink(value: string): this {
76
+ this.state.ctaLink = value;
77
+ return this;
78
+ }
79
+
80
+ backgroundImage(value: string): this {
81
+ this.state.backgroundImage = value;
82
+ return this;
83
+ }
84
+
85
+ variant(value: string): this {
86
+ this.state.variant = value;
87
+ return this;
88
+ }
89
+
90
+ /* -------------------------
91
+ * Render
92
+ * ------------------------- */
93
+
94
+ render(targetId?: string): this {
95
+ let container: HTMLElement;
96
+
97
+ if (targetId) {
98
+ const target = document.querySelector(targetId);
99
+ if (!target || !(target instanceof HTMLElement)) {
100
+ throw new Error(`Hero: Target element "${targetId}" not found`);
101
+ }
102
+ container = target;
103
+ } else {
104
+ container = getOrCreateContainer(this._componentId) as HTMLElement;
105
+ }
106
+
107
+ this.container = container;
108
+ const { title, subtitle, cta, ctaLink, backgroundImage, variant } = this.state;
109
+
110
+ const hero = document.createElement('div');
111
+ hero.className = `jux-hero jux-hero-${variant}`;
112
+ hero.id = this._componentId;
113
+
114
+ if (backgroundImage) {
115
+ hero.style.backgroundImage = `url(${backgroundImage})`;
116
+ }
117
+
118
+ const content = document.createElement('div');
119
+ content.className = 'jux-hero-content';
120
+
121
+ if (title) {
122
+ const titleEl = document.createElement('h1');
123
+ titleEl.className = 'jux-hero-title';
124
+ titleEl.textContent = title;
125
+ content.appendChild(titleEl);
126
+ }
127
+
128
+ if (subtitle) {
129
+ const subtitleEl = document.createElement('p');
130
+ subtitleEl.className = 'jux-hero-subtitle';
131
+ subtitleEl.textContent = subtitle;
132
+ content.appendChild(subtitleEl);
133
+ }
134
+
135
+ if (cta) {
136
+ const ctaEl = document.createElement('a');
137
+ ctaEl.className = 'jux-hero-cta jux-button jux-button-primary';
138
+ ctaEl.href = ctaLink;
139
+ ctaEl.textContent = cta;
140
+ content.appendChild(ctaEl);
141
+ }
142
+
143
+ hero.appendChild(content);
144
+ container.appendChild(hero);
145
+
146
+ return this;
147
+ }
148
+
149
+ /**
150
+ * Render to another Jux component's container
151
+ */
152
+ renderTo(juxComponent: any): this {
153
+ if (!juxComponent || typeof juxComponent !== 'object') {
154
+ throw new Error('Hero.renderTo: Invalid component - not an object');
155
+ }
156
+
157
+ if (!juxComponent._componentId || typeof juxComponent._componentId !== 'string') {
158
+ throw new Error('Hero.renderTo: Invalid component - missing _componentId (not a Jux component)');
159
+ }
160
+
161
+ return this.render(`#${juxComponent._componentId}`);
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Factory helper
167
+ */
168
+ export function hero(componentId: string, options: HeroOptions = {}): Hero {
169
+ return new Hero(componentId, options);
170
+ }
@@ -0,0 +1,430 @@
1
+ import { ErrorHandler } from './error-handler.js';
2
+
3
+ /**
4
+ * Import - Import external resources (CSS, JS, images, fonts, etc.)
5
+ * Provides flexible resource loading with control over injection location
6
+ */
7
+
8
+ export type ImportType = 'stylesheet' | 'script' | 'image' | 'font' | 'preload' | 'prefetch' | 'module';
9
+ export type ImportLocation = 'head' | 'body-start' | 'body-end';
10
+
11
+ export interface ImportOptions {
12
+ type?: ImportType;
13
+ href?: string;
14
+ src?: string;
15
+ as?: string;
16
+ crossOrigin?: 'anonymous' | 'use-credentials';
17
+ integrity?: string;
18
+ async?: boolean;
19
+ defer?: boolean;
20
+ module?: boolean;
21
+ location?: ImportLocation;
22
+ onLoad?: () => void;
23
+ onError?: (error: Error) => void;
24
+ }
25
+
26
+ export class Import {
27
+ private _type: ImportType = 'stylesheet';
28
+ private _href: string = '';
29
+ private _src: string = '';
30
+ private _as: string = '';
31
+ private _crossOrigin?: 'anonymous' | 'use-credentials';
32
+ private _integrity?: string;
33
+ private _async: boolean = false;
34
+ private _defer: boolean = false;
35
+ private _module: boolean = false;
36
+ private _location: ImportLocation = 'head';
37
+ private _onLoad?: () => void;
38
+ private _onError?: (error: Error) => void;
39
+ private _element: HTMLElement | null = null;
40
+
41
+ constructor(url: string, options: ImportOptions = {}) {
42
+ // Auto-detect type from URL if not specified
43
+ if (!options.type) {
44
+ if (url.endsWith('.css')) {
45
+ this._type = 'stylesheet';
46
+ } else if (url.endsWith('.js') || url.endsWith('.mjs')) {
47
+ this._type = 'script';
48
+ } else if (url.match(/\.(png|jpg|jpeg|gif|svg|webp)$/i)) {
49
+ this._type = 'image';
50
+ } else if (url.match(/\.(woff|woff2|ttf|otf|eot)$/i)) {
51
+ this._type = 'font';
52
+ } else {
53
+ this._type = 'preload';
54
+ }
55
+ } else {
56
+ this._type = options.type;
57
+ }
58
+
59
+ // Set URL based on type
60
+ if (this._type === 'script') {
61
+ this._src = url;
62
+ } else {
63
+ this._href = url;
64
+ }
65
+
66
+ // Apply options
67
+ if (options.as) this._as = options.as;
68
+ if (options.crossOrigin) this._crossOrigin = options.crossOrigin;
69
+ if (options.integrity) this._integrity = options.integrity;
70
+ if (options.async !== undefined) this._async = options.async;
71
+ if (options.defer !== undefined) this._defer = options.defer;
72
+ if (options.module !== undefined) this._module = options.module;
73
+ if (options.location) this._location = options.location;
74
+ if (options.onLoad) this._onLoad = options.onLoad;
75
+ if (options.onError) this._onError = options.onError;
76
+
77
+ // Auto-render
78
+ this.render();
79
+ }
80
+
81
+ /**
82
+ * Set the resource type
83
+ */
84
+ type(type: ImportType): this {
85
+ this._type = type;
86
+ return this;
87
+ }
88
+
89
+ /**
90
+ * Set the URL (for link elements)
91
+ */
92
+ href(url: string): this {
93
+ this._href = url;
94
+ return this;
95
+ }
96
+
97
+ /**
98
+ * Set the URL (for script elements)
99
+ */
100
+ src(url: string): this {
101
+ this._src = url;
102
+ return this;
103
+ }
104
+
105
+ /**
106
+ * Set the 'as' attribute (for preload/prefetch)
107
+ */
108
+ as(value: string): this {
109
+ this._as = value;
110
+ return this;
111
+ }
112
+
113
+ /**
114
+ * Set crossorigin attribute
115
+ */
116
+ crossOrigin(value: 'anonymous' | 'use-credentials'): this {
117
+ this._crossOrigin = value;
118
+ return this;
119
+ }
120
+
121
+ /**
122
+ * Set integrity hash for SRI (Subresource Integrity)
123
+ */
124
+ integrity(hash: string): this {
125
+ this._integrity = hash;
126
+ return this;
127
+ }
128
+
129
+ /**
130
+ * Enable async loading (for scripts)
131
+ */
132
+ async(value: boolean = true): this {
133
+ this._async = value;
134
+ return this;
135
+ }
136
+
137
+ /**
138
+ * Enable defer loading (for scripts)
139
+ */
140
+ defer(value: boolean = true): this {
141
+ this._defer = value;
142
+ return this;
143
+ }
144
+
145
+ /**
146
+ * Mark as ES module (for scripts)
147
+ */
148
+ module(value: boolean = true): this {
149
+ this._module = value;
150
+ return this;
151
+ }
152
+
153
+ /**
154
+ * Set injection location
155
+ */
156
+ location(loc: ImportLocation): this {
157
+ this._location = loc;
158
+ return this;
159
+ }
160
+
161
+ /**
162
+ * Set onload callback
163
+ */
164
+ onLoad(callback: () => void): this {
165
+ this._onLoad = callback;
166
+ return this;
167
+ }
168
+
169
+ /**
170
+ * Set onerror callback
171
+ */
172
+ onError(callback: (error: Error) => void): this {
173
+ this._onError = callback;
174
+ return this;
175
+ }
176
+
177
+ /**
178
+ * Get the target container for injection
179
+ */
180
+ private getContainer(): HTMLElement {
181
+ switch (this._location) {
182
+ case 'head':
183
+ return document.head;
184
+ case 'body-start':
185
+ return document.body;
186
+ case 'body-end':
187
+ return document.body;
188
+ default:
189
+ return document.head;
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Render the import element
195
+ */
196
+ render(): this {
197
+ if (typeof document === 'undefined') {
198
+ return this;
199
+ }
200
+
201
+ try {
202
+ // Remove existing element
203
+ this.remove();
204
+
205
+ let element: HTMLElement;
206
+
207
+ switch (this._type) {
208
+ case 'stylesheet':
209
+ element = this.createStylesheet();
210
+ break;
211
+ case 'script':
212
+ element = this.createScript();
213
+ break;
214
+ case 'image':
215
+ element = this.createImage();
216
+ break;
217
+ case 'font':
218
+ element = this.createFont();
219
+ break;
220
+ case 'preload':
221
+ element = this.createPreload();
222
+ break;
223
+ case 'prefetch':
224
+ element = this.createPrefetch();
225
+ break;
226
+ case 'module':
227
+ element = this.createModule();
228
+ break;
229
+ default:
230
+ throw new Error(`Unknown import type: ${this._type}`);
231
+ }
232
+
233
+ const container = this.getContainer();
234
+
235
+ if (this._location === 'body-end') {
236
+ container.appendChild(element);
237
+ } else {
238
+ container.insertBefore(element, container.firstChild);
239
+ }
240
+
241
+ this._element = element;
242
+ } catch (error: any) {
243
+ ErrorHandler.captureError({
244
+ component: 'Import',
245
+ method: 'render',
246
+ message: error.message,
247
+ stack: error.stack,
248
+ timestamp: new Date(),
249
+ context: {
250
+ type: this._type,
251
+ href: this._href,
252
+ src: this._src,
253
+ location: this._location,
254
+ error: 'runtime_exception'
255
+ }
256
+ });
257
+ }
258
+
259
+ return this;
260
+ }
261
+
262
+ /**
263
+ * Create a stylesheet link element
264
+ */
265
+ private createStylesheet(): HTMLLinkElement {
266
+ const link = document.createElement('link');
267
+ link.rel = 'stylesheet';
268
+ link.href = this._href;
269
+
270
+ if (this._crossOrigin) link.crossOrigin = this._crossOrigin;
271
+ if (this._integrity) link.integrity = this._integrity;
272
+
273
+ link.onload = () => {
274
+ console.log(`✓ Stylesheet loaded: ${this._href}`);
275
+ if (this._onLoad) this._onLoad();
276
+ };
277
+
278
+ link.onerror = () => {
279
+ const error = new Error(`Failed to load stylesheet: ${this._href}`);
280
+ ErrorHandler.captureError({
281
+ component: 'Import',
282
+ method: 'createStylesheet',
283
+ message: error.message,
284
+ timestamp: new Date(),
285
+ context: { href: this._href, type: 'stylesheet', error: 'load_failed' }
286
+ });
287
+ if (this._onError) this._onError(error);
288
+ };
289
+
290
+ return link;
291
+ }
292
+
293
+ /**
294
+ * Create a script element
295
+ */
296
+ private createScript(): HTMLScriptElement {
297
+ const script = document.createElement('script');
298
+ script.src = this._src;
299
+
300
+ if (this._async) script.async = true;
301
+ if (this._defer) script.defer = true;
302
+ if (this._module) script.type = 'module';
303
+ if (this._crossOrigin) script.crossOrigin = this._crossOrigin;
304
+ if (this._integrity) script.integrity = this._integrity;
305
+
306
+ script.onload = () => {
307
+ console.log(`✓ Script loaded: ${this._src}`);
308
+ if (this._onLoad) this._onLoad();
309
+ };
310
+
311
+ script.onerror = () => {
312
+ const error = new Error(`Failed to load script: ${this._src}`);
313
+ ErrorHandler.captureError({
314
+ component: 'Import',
315
+ method: 'createScript',
316
+ message: error.message,
317
+ timestamp: new Date(),
318
+ context: { src: this._src, type: 'script', error: 'load_failed' }
319
+ });
320
+ if (this._onError) this._onError(error);
321
+ };
322
+
323
+ return script;
324
+ }
325
+
326
+ /**
327
+ * Create an image preload link
328
+ */
329
+ private createImage(): HTMLLinkElement {
330
+ const link = document.createElement('link');
331
+ link.rel = 'preload';
332
+ link.as = 'image';
333
+ link.href = this._href;
334
+
335
+ if (this._crossOrigin) link.crossOrigin = this._crossOrigin;
336
+
337
+ return link;
338
+ }
339
+
340
+ /**
341
+ * Create a font preload link
342
+ */
343
+ private createFont(): HTMLLinkElement {
344
+ const link = document.createElement('link');
345
+ link.rel = 'preload';
346
+ link.as = 'font';
347
+ link.href = this._href;
348
+ link.crossOrigin = this._crossOrigin || 'anonymous';
349
+
350
+ return link;
351
+ }
352
+
353
+ /**
354
+ * Create a preload link
355
+ */
356
+ private createPreload(): HTMLLinkElement {
357
+ const link = document.createElement('link');
358
+ link.rel = 'preload';
359
+ link.href = this._href;
360
+
361
+ if (this._as) link.as = this._as;
362
+ if (this._crossOrigin) link.crossOrigin = this._crossOrigin;
363
+ if (this._integrity) link.integrity = this._integrity;
364
+
365
+ return link;
366
+ }
367
+
368
+ /**
369
+ * Create a prefetch link
370
+ */
371
+ private createPrefetch(): HTMLLinkElement {
372
+ const link = document.createElement('link');
373
+ link.rel = 'prefetch';
374
+ link.href = this._href;
375
+
376
+ if (this._as) link.as = this._as;
377
+ if (this._crossOrigin) link.crossOrigin = this._crossOrigin;
378
+
379
+ return link;
380
+ }
381
+
382
+ /**
383
+ * Create a module script
384
+ */
385
+ private createModule(): HTMLScriptElement {
386
+ const script = document.createElement('script');
387
+ script.type = 'module';
388
+ script.src = this._src;
389
+
390
+ if (this._crossOrigin) script.crossOrigin = this._crossOrigin;
391
+ if (this._integrity) script.integrity = this._integrity;
392
+
393
+ script.onload = () => {
394
+ console.log(`✓ Module loaded: ${this._src}`);
395
+ if (this._onLoad) this._onLoad();
396
+ };
397
+
398
+ script.onerror = () => {
399
+ const error = new Error(`Failed to load module: ${this._src}`);
400
+ ErrorHandler.captureError({
401
+ component: 'Import',
402
+ method: 'createModule',
403
+ message: error.message,
404
+ timestamp: new Date(),
405
+ context: { src: this._src, type: 'module', error: 'load_failed' }
406
+ });
407
+ if (this._onError) this._onError(error);
408
+ };
409
+
410
+ return script;
411
+ }
412
+
413
+ /**
414
+ * Remove the element from the document
415
+ */
416
+ remove(): this {
417
+ if (this._element && this._element.parentNode) {
418
+ this._element.parentNode.removeChild(this._element);
419
+ this._element = null;
420
+ }
421
+ return this;
422
+ }
423
+ }
424
+
425
+ /**
426
+ * Factory function for creating imports
427
+ */
428
+ export function importResource(url: string, options?: ImportOptions): Import {
429
+ return new Import(url, options);
430
+ }