juxscript 1.1.115 → 1.1.117

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "totalComponents": 69,
3
- "generatedAt": "2026-02-13T03:14:49.691Z",
3
+ "generatedAt": "2026-02-13T04:55:13.466Z",
4
4
  "components": [
5
5
  {
6
6
  "file": "alert.js",
@@ -63,6 +63,13 @@ export interface LayoutExtensions {
63
63
  flexShrink(value: string | number): this;
64
64
  flexBasis(value: string): this;
65
65
  alignSelf(value: 'auto' | 'flex-start' | 'flex-end' | 'center' | 'baseline' | 'stretch'): this;
66
+ flexDirection(value: 'row' | 'column' | 'row-reverse' | 'column-reverse'): this;
67
+ flexWrap(value: 'nowrap' | 'wrap' | 'wrap-reverse'): this;
68
+ alignItems(value: 'flex-start' | 'flex-end' | 'center' | 'baseline' | 'stretch'): this;
69
+ justifyContent(value: 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'space-evenly'): this;
70
+ gap(value: string): this;
71
+ rowGap(value: string): this;
72
+ columnGap(value: string): this;
66
73
  transform(value: string): this;
67
74
  transformOrigin(value: string): this;
68
75
  rotate(degrees: number): this;
@@ -1 +1 @@
1
- {"version":3,"file":"LayoutExtensions.d.ts","sourceRoot":"","sources":["LayoutExtensions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"LayoutExtensions.d.ts","sourceRoot":"","sources":["LayoutExtensions.ts"],"names":[],"mappings":"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"}
@@ -134,6 +134,14 @@ export function applyLayoutExtensions(component) {
134
134
  ext.flexShrink = (v) => { addInlineStyle(component, 'flex-shrink', String(v)); return ext; };
135
135
  ext.flexBasis = (v) => { addInlineStyle(component, 'flex-basis', v); return ext; };
136
136
  ext.alignSelf = (v) => { addInlineStyle(component, 'align-self', v); return ext; };
137
+ // ✅ NEW flex container properties
138
+ ext.flexDirection = (v) => { addInlineStyle(component, 'flex-direction', v); return ext; };
139
+ ext.flexWrap = (v) => { addInlineStyle(component, 'flex-wrap', v); return ext; };
140
+ ext.alignItems = (v) => { addInlineStyle(component, 'align-items', v); return ext; };
141
+ ext.justifyContent = (v) => { addInlineStyle(component, 'justify-content', v); return ext; };
142
+ ext.gap = (v) => { addInlineStyle(component, 'gap', v); return ext; };
143
+ ext.rowGap = (v) => { addInlineStyle(component, 'row-gap', v); return ext; };
144
+ ext.columnGap = (v) => { addInlineStyle(component, 'column-gap', v); return ext; };
137
145
  // Transform
138
146
  ext.transform = (v) => { addInlineStyle(component, 'transform', v); return ext; };
139
147
  ext.transformOrigin = (v) => { addInlineStyle(component, 'transform-origin', v); return ext; };
@@ -85,6 +85,14 @@ export interface LayoutExtensions {
85
85
  flexShrink(value: string | number): this;
86
86
  flexBasis(value: string): this;
87
87
  alignSelf(value: 'auto' | 'flex-start' | 'flex-end' | 'center' | 'baseline' | 'stretch'): this;
88
+ // ✅ NEW flex container properties
89
+ flexDirection(value: 'row' | 'column' | 'row-reverse' | 'column-reverse'): this;
90
+ flexWrap(value: 'nowrap' | 'wrap' | 'wrap-reverse'): this;
91
+ alignItems(value: 'flex-start' | 'flex-end' | 'center' | 'baseline' | 'stretch'): this;
92
+ justifyContent(value: 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'space-evenly'): this;
93
+ gap(value: string): this;
94
+ rowGap(value: string): this;
95
+ columnGap(value: string): this;
88
96
 
89
97
  // Transform (inline styles)
90
98
  transform(value: string): this;
@@ -275,6 +283,15 @@ export function applyLayoutExtensions<T extends BaseComponent<any>>(component: T
275
283
  ext.flexBasis = (v) => { addInlineStyle(component, 'flex-basis', v); return ext; };
276
284
  ext.alignSelf = (v) => { addInlineStyle(component, 'align-self', v); return ext; };
277
285
 
286
+ // ✅ NEW flex container properties
287
+ ext.flexDirection = (v) => { addInlineStyle(component, 'flex-direction', v); return ext; };
288
+ ext.flexWrap = (v) => { addInlineStyle(component, 'flex-wrap', v); return ext; };
289
+ ext.alignItems = (v) => { addInlineStyle(component, 'align-items', v); return ext; };
290
+ ext.justifyContent = (v) => { addInlineStyle(component, 'justify-content', v); return ext; };
291
+ ext.gap = (v) => { addInlineStyle(component, 'gap', v); return ext; };
292
+ ext.rowGap = (v) => { addInlineStyle(component, 'row-gap', v); return ext; };
293
+ ext.columnGap = (v) => { addInlineStyle(component, 'column-gap', v); return ext; };
294
+
278
295
  // Transform
279
296
  ext.transform = (v) => { addInlineStyle(component, 'transform', v); return ext; };
280
297
  ext.transformOrigin = (v) => { addInlineStyle(component, 'transform-origin', v); return ext; };
@@ -0,0 +1,322 @@
1
+ /* ═══════════════════════════════════════════════════════════════════
2
+ * NAV COMPONENT STYLES
3
+ * Modern navigation with horizontal/vertical layouts
4
+ * ═══════════════════════════════════════════════════════════════════ */
5
+
6
+ /* Base Nav Container */
7
+ .jux-nav {
8
+ display: flex;
9
+ align-items: center;
10
+ position: relative;
11
+ }
12
+
13
+ /* Nav Items Container */
14
+ .jux-nav-items {
15
+ display: flex;
16
+ list-style: none;
17
+ margin: 0;
18
+ padding: 0;
19
+ gap: 0;
20
+ }
21
+
22
+ /* Individual Nav Item Wrapper */
23
+ .jux-nav-item-wrapper {
24
+ position: relative;
25
+ display: flex;
26
+ align-items: center;
27
+ }
28
+
29
+ /* Nav Links */
30
+ .jux-nav-link {
31
+ display: inline-flex;
32
+ align-items: center;
33
+ padding: 8px 20px;
34
+ color: #374151; /* gray-700 */
35
+ font-weight: 500;
36
+ font-size: 15px;
37
+ text-decoration: none;
38
+ transition: all 0.2s ease;
39
+ border-radius: 6px;
40
+ position: relative;
41
+ }
42
+
43
+ .jux-nav-link:hover {
44
+ color: #7c3aed; /* purple-600 */
45
+ background-color: rgba(124, 58, 237, 0.05);
46
+ }
47
+
48
+ .jux-nav-link:active {
49
+ transform: scale(0.98);
50
+ }
51
+
52
+ /* Active/Current Link State */
53
+ .jux-nav-link.active,
54
+ .jux-nav-link[aria-current="page"] {
55
+ color: #7c3aed;
56
+ font-weight: 600;
57
+ }
58
+
59
+ /* Underline effect on hover */
60
+ .jux-nav-link::after {
61
+ content: '';
62
+ position: absolute;
63
+ bottom: 0;
64
+ left: 50%;
65
+ width: 0;
66
+ height: 2px;
67
+ background-color: #7c3aed;
68
+ transform: translateX(-50%);
69
+ transition: width 0.3s ease;
70
+ }
71
+
72
+ .jux-nav-link:hover::after,
73
+ .jux-nav-link.active::after {
74
+ width: 80%;
75
+ }
76
+
77
+ /* ═══════════════════════════════════════════════════════════════════
78
+ * HORIZONTAL NAV (Default)
79
+ * ═══════════════════════════════════════════════════════════════════ */
80
+
81
+ .jux-nav-horizontal .jux-nav-items {
82
+ flex-direction: row;
83
+ gap: 8px;
84
+ }
85
+
86
+ /* ═══════════════════════════════════════════════════════════════════
87
+ * VERTICAL NAV
88
+ * ═══════════════════════════════════════════════════════════════════ */
89
+
90
+ .jux-nav-vertical {
91
+ flex-direction: column;
92
+ align-items: stretch;
93
+ }
94
+
95
+ .jux-nav-vertical .jux-nav-items {
96
+ flex-direction: column;
97
+ gap: 4px;
98
+ width: 100%;
99
+ }
100
+
101
+ .jux-nav-vertical .jux-nav-item-wrapper {
102
+ width: 100%;
103
+ }
104
+
105
+ .jux-nav-vertical .jux-nav-link {
106
+ width: 100%;
107
+ justify-content: flex-start;
108
+ }
109
+
110
+ .jux-nav-vertical .jux-nav-link::after {
111
+ display: none; /* No underline in vertical mode */
112
+ }
113
+
114
+ /* ═══════════════════════════════════════════════════════════════════
115
+ * SIZE VARIANTS
116
+ * ═══════════════════════════════════════════════════════════════════ */
117
+
118
+ /* Small Nav */
119
+ .jux-nav-sm .jux-nav-link {
120
+ padding: 6px 14px;
121
+ font-size: 13px;
122
+ }
123
+
124
+ /* Large Nav */
125
+ .jux-nav-lg .jux-nav-link {
126
+ padding: 12px 24px;
127
+ font-size: 16px;
128
+ }
129
+
130
+ /* ═══════════════════════════════════════════════════════════════════
131
+ * STYLE VARIANTS
132
+ * ═══════════════════════════════════════════════════════════════════ */
133
+
134
+ /* Pills Style */
135
+ .jux-nav-pills .jux-nav-link {
136
+ border-radius: 50px;
137
+ }
138
+
139
+ .jux-nav-pills .jux-nav-link:hover,
140
+ .jux-nav-pills .jux-nav-link.active {
141
+ background-color: #7c3aed;
142
+ color: white;
143
+ }
144
+
145
+ .jux-nav-pills .jux-nav-link::after {
146
+ display: none; /* No underline for pills */
147
+ }
148
+
149
+ /* Tabs Style */
150
+ .jux-nav-tabs {
151
+ border-bottom: 2px solid #e5e7eb;
152
+ }
153
+
154
+ .jux-nav-tabs .jux-nav-link {
155
+ border-radius: 0;
156
+ border-bottom: 2px solid transparent;
157
+ margin-bottom: -2px;
158
+ padding-bottom: 10px;
159
+ }
160
+
161
+ .jux-nav-tabs .jux-nav-link::after {
162
+ display: none;
163
+ }
164
+
165
+ .jux-nav-tabs .jux-nav-link:hover {
166
+ background-color: transparent;
167
+ border-bottom-color: #d1d5db;
168
+ }
169
+
170
+ .jux-nav-tabs .jux-nav-link.active {
171
+ border-bottom-color: #7c3aed;
172
+ background-color: transparent;
173
+ }
174
+
175
+ /* Minimal Style (no background on hover) */
176
+ .jux-nav-minimal .jux-nav-link:hover {
177
+ background-color: transparent;
178
+ }
179
+
180
+ /* ═══════════════════════════════════════════════════════════════════
181
+ * DROPDOWN SUPPORT
182
+ * ═══════════════════════════════════════════════════════════════════ */
183
+
184
+ .jux-nav-item-wrapper.has-dropdown {
185
+ position: relative;
186
+ }
187
+
188
+ .jux-nav-dropdown {
189
+ position: absolute;
190
+ top: 100%;
191
+ left: 0;
192
+ min-width: 200px;
193
+ margin-top: 8px;
194
+ padding: 8px;
195
+ background: white;
196
+ border-radius: 8px;
197
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
198
+ opacity: 0;
199
+ visibility: hidden;
200
+ transform: translateY(-10px);
201
+ transition: all 0.2s ease;
202
+ z-index: 1000;
203
+ }
204
+
205
+ .jux-nav-item-wrapper.has-dropdown:hover .jux-nav-dropdown,
206
+ .jux-nav-item-wrapper.has-dropdown:focus-within .jux-nav-dropdown {
207
+ opacity: 1;
208
+ visibility: visible;
209
+ transform: translateY(0);
210
+ }
211
+
212
+ .jux-nav-dropdown-item {
213
+ display: block;
214
+ padding: 10px 16px;
215
+ color: #374151;
216
+ text-decoration: none;
217
+ border-radius: 6px;
218
+ transition: all 0.15s ease;
219
+ }
220
+
221
+ .jux-nav-dropdown-item:hover {
222
+ background-color: rgba(124, 58, 237, 0.08);
223
+ color: #7c3aed;
224
+ }
225
+
226
+ /* Dropdown Arrow Indicator */
227
+ .jux-nav-link.has-dropdown-arrow::after {
228
+ content: '▼';
229
+ margin-left: 6px;
230
+ font-size: 10px;
231
+ transition: transform 0.2s ease;
232
+ }
233
+
234
+ .jux-nav-item-wrapper.has-dropdown:hover .jux-nav-link.has-dropdown-arrow::after {
235
+ transform: rotate(180deg);
236
+ }
237
+
238
+ /* ═══════════════════════════════════════════════════════════════════
239
+ * MOBILE RESPONSIVE
240
+ * ═══════════════════════════════════════════════════════════════════ */
241
+
242
+ @media (max-width: 768px) {
243
+ .jux-nav-horizontal.jux-nav-mobile-vertical .jux-nav-items {
244
+ flex-direction: column;
245
+ width: 100%;
246
+ gap: 4px;
247
+ }
248
+
249
+ .jux-nav-horizontal.jux-nav-mobile-vertical .jux-nav-item-wrapper {
250
+ width: 100%;
251
+ }
252
+
253
+ .jux-nav-horizontal.jux-nav-mobile-vertical .jux-nav-link {
254
+ width: 100%;
255
+ justify-content: flex-start;
256
+ }
257
+ }
258
+
259
+ /* ═══════════════════════════════════════════════════════════════════
260
+ * DARK MODE SUPPORT
261
+ * ═══════════════════════════════════════════════════════════════════ */
262
+
263
+ .dark .jux-nav-link {
264
+ color: #d1d5db; /* gray-300 */
265
+ }
266
+
267
+ .dark .jux-nav-link:hover {
268
+ color: #a78bfa; /* purple-400 */
269
+ background-color: rgba(167, 139, 250, 0.1);
270
+ }
271
+
272
+ .dark .jux-nav-link.active {
273
+ color: #a78bfa;
274
+ }
275
+
276
+ .dark .jux-nav-tabs {
277
+ border-bottom-color: #374151;
278
+ }
279
+
280
+ .dark .jux-nav-dropdown {
281
+ background: #1f2937;
282
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5);
283
+ }
284
+
285
+ .dark .jux-nav-dropdown-item {
286
+ color: #d1d5db;
287
+ }
288
+
289
+ .dark .jux-nav-dropdown-item:hover {
290
+ background-color: rgba(167, 139, 250, 0.15);
291
+ color: #a78bfa;
292
+ }
293
+
294
+ /* ═══════════════════════════════════════════════════════════════════
295
+ * ANIMATIONS
296
+ * ═══════════════════════════════════════════════════════════════════ */
297
+
298
+ @keyframes nav-fade-in {
299
+ from {
300
+ opacity: 0;
301
+ transform: translateY(-5px);
302
+ }
303
+ to {
304
+ opacity: 1;
305
+ transform: translateY(0);
306
+ }
307
+ }
308
+
309
+ .jux-nav-items.animate-in {
310
+ animation: nav-fade-in 0.3s ease-out;
311
+ }
312
+
313
+ .jux-nav-item-wrapper {
314
+ animation: nav-fade-in 0.3s ease-out backwards;
315
+ }
316
+
317
+ .jux-nav-item-wrapper:nth-child(1) { animation-delay: 0ms; }
318
+ .jux-nav-item-wrapper:nth-child(2) { animation-delay: 50ms; }
319
+ .jux-nav-item-wrapper:nth-child(3) { animation-delay: 100ms; }
320
+ .jux-nav-item-wrapper:nth-child(4) { animation-delay: 150ms; }
321
+ .jux-nav-item-wrapper:nth-child(5) { animation-delay: 200ms; }
322
+ .jux-nav-item-wrapper:nth-child(6) { animation-delay: 250ms; }
@@ -7,7 +7,6 @@
7
7
  .jux-vstack {
8
8
  display: flex;
9
9
  flex-direction: column;
10
- gap: 1rem;
11
10
  }
12
11
 
13
12
  .jux-vstack-tight { gap: 0.5rem; }
@@ -19,7 +18,6 @@
19
18
  display: flex;
20
19
  flex-direction: row;
21
20
  align-items: center;
22
- gap: 1rem;
23
21
  }
24
22
 
25
23
  .jux-hstack-tight { gap: 0.5rem; }
@@ -568,176 +568,672 @@ navigate(location.pathname);
568
568
  }
569
569
 
570
570
  /**
571
- * Build method with source copy step
571
+ * Copy ONLY public assets (CSS, images, fonts) to dist
572
+ * ❌ Does NOT copy .jux files (they are compiled separately)
573
+ */
574
+ async copyPublicAssets() {
575
+ const { publicDir, distDir, paths } = this.config;
576
+
577
+ // Resolve public folder path
578
+ const publicPath = paths?.public || path.resolve(this.config.srcDir, '..', publicDir);
579
+
580
+ if (!fs.existsSync(publicPath)) {
581
+ console.log(`ℹ️ No public folder found at ${publicPath}, skipping asset copy`);
582
+ return;
583
+ }
584
+
585
+ console.log(`📦 Copying public assets from ${publicPath}...`);
586
+
587
+ // ✅ ONLY copy known asset file types
588
+ const assetTypes = [
589
+ '.css', '.scss', '.sass', '.less', // Stylesheets
590
+ '.jpg', '.jpeg', '.png', '.gif', '.svg', // Images
591
+ '.ico', '.webp', '.avif', // Icons/modern images
592
+ '.woff', '.woff2', '.ttf', '.eot', '.otf', // Fonts
593
+ '.mp4', '.webm', '.ogg', // Video
594
+ '.mp3', '.wav', '.m4a', // Audio
595
+ '.pdf', '.txt', '.json', '.xml' // Documents
596
+ ];
597
+
598
+ const copyRecursive = (src, dest) => {
599
+ if (!fs.existsSync(src)) return;
600
+
601
+ const entries = fs.readdirSync(src, { withFileTypes: true });
602
+
603
+ for (const entry of entries) {
604
+ const srcPath = path.join(src, entry.name);
605
+ const destPath = path.join(dest, entry.name);
606
+
607
+ if (entry.isDirectory()) {
608
+ fs.mkdirSync(destPath, { recursive: true });
609
+ copyRecursive(srcPath, destPath);
610
+ } else {
611
+ const ext = path.extname(entry.name).toLowerCase();
612
+
613
+ // ✅ ONLY copy known asset types
614
+ // ❌ SKIP .jux files (they're bundled into entry.js)
615
+ if (assetTypes.includes(ext)) {
616
+ fs.copyFileSync(srcPath, destPath);
617
+ console.log(` ✓ ${entry.name}`);
618
+ } else if (ext === '.jux') {
619
+ // Silently skip .jux files
620
+ continue;
621
+ } else {
622
+ // Warn about unknown file types
623
+ console.warn(` ⚠️ Skipped: ${entry.name} (unknown type: ${ext})`);
624
+ }
625
+ }
626
+ }
627
+ };
628
+
629
+ copyRecursive(publicPath, distDir);
630
+ }
631
+
632
+ /**
633
+ * Main build pipeline
572
634
  */
573
635
  async build() {
574
- console.log('🔨 Building JUX application...\n');
636
+ console.log('🔨 Building JUX project...\n');
575
637
 
576
638
  try {
577
- const startTime = Date.now();
639
+ // 1. Clean dist
640
+ if (fs.existsSync(this.config.distDir)) {
641
+ fs.rmSync(this.config.distDir, { recursive: true, force: true });
642
+ }
643
+ fs.mkdirSync(this.config.distDir, { recursive: true });
578
644
 
579
- // Scan files
580
- const { views, dataModules, sharedModules } = this.scanFiles();
645
+ // 2. Scan .jux files
646
+ const juxFiles = this.scanJuxFiles(this.config.srcDir);
647
+ console.log(`📂 Found ${juxFiles.length} .jux files\n`);
581
648
 
582
- console.log(`📂 Found ${views.length} views, ${dataModules.length} data modules, ${sharedModules.length} shared modules\n`);
649
+ // 3. Bundle all .jux files entry.js
650
+ await this.bundleJuxFiles(juxFiles);
583
651
 
584
- // Load juxscript exports for validation
585
- await this.loadJuxscriptExports();
652
+ // 4. Bundle vendor libraries → bundle.js
653
+ await this.bundleVendorFiles();
586
654
 
587
- // Ensure dist directory exists
588
- if (!fs.existsSync(this.distDir)) {
589
- fs.mkdirSync(this.distDir, { recursive: true });
590
- }
655
+ // 5. Copy public assets (CSS, images, etc.)
656
+ await this.copyPublicAssets();
657
+
658
+ // 6. Generate index.html (with entry.js + bundle.js)
659
+ await this.generateIndexHtml();
660
+
661
+ console.log('\n✅ Build completed successfully!\n');
662
+ console.log(`📁 Output: ${this.config.distDir}`);
663
+ console.log(` ✓ entry.js (${juxFiles.length} .jux files bundled)`);
664
+ console.log(` ✓ bundle.js (vendor libraries)`);
665
+ console.log(` ✓ index.html`);
666
+ console.log(` ✓ Public assets copied\n`);
591
667
 
592
- // Copy source files to dist/jux BEFORE generating entry point
593
- console.log('📋 Copying source files to dist...');
594
- this._copySourceToDist();
595
- console.log(' Source files copied\n');
596
-
597
- // Generate entry point
598
- const entryCode = this.generateEntryPoint(views, dataModules, sharedModules);
599
- const entryPath = path.join(this.distDir, 'entry.js');
600
- fs.writeFileSync(entryPath, entryCode);
601
-
602
- // Bundle with esbuild
603
- console.log('📦 Bundling with esbuild...');
604
- await esbuild.build({
605
- entryPoints: [entryPath],
606
- bundle: true,
607
- format: 'esm',
608
- outfile: path.join(this.distDir, 'bundle.js'),
609
- platform: 'browser',
610
- target: 'es2020',
611
- sourcemap: true,
612
- external: [],
613
- // ✅ Tell esbuild how to resolve .jux files
614
- loader: {
615
- '.jux': 'js' // Treat .jux files as JavaScript
668
+ return { success: true };
669
+
670
+ } catch (error) {
671
+ console.error('\n❌ Build failed:', error.message);
672
+ console.error(error.stack);
673
+ return { success: false, error };
674
+ }
675
+ }
676
+
677
+ /**
678
+ * Scan for .jux files ONLY in source directory
679
+ */
680
+ scanJuxFiles(dir) {
681
+ const juxFiles = [];
682
+
683
+ const scan = (currentDir) => {
684
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
685
+
686
+ for (const entry of entries) {
687
+ const fullPath = path.join(currentDir, entry.name);
688
+
689
+ if (entry.isDirectory()) {
690
+ scan(fullPath);
691
+ } else if (entry.name.endsWith('.jux')) {
692
+ juxFiles.push(fullPath);
616
693
  }
694
+ }
695
+ };
696
+
697
+ scan(dir);
698
+ return juxFiles;
699
+ }
700
+
701
+ /**
702
+ * Compile a single .jux file to .js
703
+ */
704
+ async compileFile(juxFilePath) {
705
+ const relativePath = path.relative(this.config.srcDir, juxFilePath);
706
+ const outputPath = path.join(this.config.distDir, relativePath.replace(/\.jux$/, '.js'));
707
+
708
+ // Ensure output directory exists
709
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
710
+
711
+ // Read .jux source
712
+ const juxCode = fs.readFileSync(juxFilePath, 'utf8');
713
+
714
+ // Compile to JavaScript
715
+ const jsCode = this.transformJuxToJs(juxCode);
716
+
717
+ // Write compiled .js file
718
+ fs.writeFileSync(outputPath, jsCode, 'utf8');
719
+
720
+ console.log(` ✓ ${relativePath} → ${path.basename(outputPath)}`);
721
+ }
722
+
723
+ /**
724
+ * ✅ Generate name from folder structure
725
+ * Example: abc/juxabc.jux -> abc_juxabc
726
+ */
727
+ _generateNameFromPath(path) {
728
+ return path
729
+ .replace(/\.[^/.]+$/, '') // Remove extension
730
+ .replace(/\\/g, '/') // Normalize Windows paths
731
+ .replace(/\//g, '_') // Folder separator -> underscore
732
+ .replace(/[^a-zA-Z0-9_]/g, '_'); // Sanitize
733
+ }
734
+
735
+ /**
736
+ * ✅ Generate PascalCase function name from sanitized name
737
+ * Examples:
738
+ * abc_juxabc -> AbcJuxabc
739
+ * index -> Index
740
+ */
741
+ _generateFunctionName(name) {
742
+ return name
743
+ .split('_')
744
+ .map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
745
+ .join('');
746
+ }
747
+
748
+ /**
749
+ * ✅ Generate entry point with nested folder support
750
+ */
751
+ generateEntryPoint(views, dataModules, sharedModules) {
752
+ let entry = `// Auto-generated JUX entry point\n\n`;
753
+ const allIssues = [];
754
+ const sourceSnapshot = {};
755
+
756
+ const juxImports = new Set();
757
+ const layoutImports = new Set(); // ✅ Track layout imports separately
758
+
759
+ // Scan for imports
760
+ [...views, ...dataModules, ...sharedModules].forEach(m => {
761
+ // Regular juxscript imports
762
+ for (const match of m.content.matchAll(/import\s*\{\s*([^}]+)\s*\}\s*from\s*['"]juxscript['"]/g)) {
763
+ match[1].split(',').map(s => s.trim()).forEach(imp => {
764
+ if (imp) {
765
+ // ✅ Separate layout imports
766
+ if (imp === 'VStack' || imp === 'HStack' || imp === 'ZStack') {
767
+ layoutImports.add(imp);
768
+ } else {
769
+ juxImports.add(imp);
770
+ }
771
+ }
772
+ });
773
+ }
774
+ });
775
+
776
+ // ✅ Import layouts separately
777
+ if (layoutImports.size > 0) {
778
+ entry += `import { ${[...layoutImports].sort().join(', ')} } from 'juxscript';\n`;
779
+ }
780
+
781
+ // ✅ Import regular components
782
+ if (juxImports.size > 0) {
783
+ entry += `import { ${[...juxImports].sort().join(', ')} } from 'juxscript';\n\n`;
784
+ }
785
+
786
+ // Data and shared modules
787
+ dataModules.forEach(m => {
788
+ entry += `import * as ${this.sanitizeName(m.name)}Data from './jux/${m.file}';\n`;
789
+ });
790
+ sharedModules.forEach(m => {
791
+ entry += `import * as ${this.sanitizeName(m.name)}Shared from './jux/${m.file}';\n`;
792
+ });
793
+
794
+ entry += `\n// Expose to window\n`;
795
+ dataModules.forEach(m => entry += `Object.assign(window, ${this.sanitizeName(m.name)}Data);\n`);
796
+ sharedModules.forEach(m => entry += `Object.assign(window, ${this.sanitizeName(m.name)}Shared);\n`);
797
+
798
+ // ✅ Expose layouts to window
799
+ if (layoutImports.size > 0) {
800
+ entry += `\n// Expose layout components\n`;
801
+ layoutImports.forEach(layout => {
802
+ entry += `window.${layout} = ${layout};\n`;
617
803
  });
804
+ }
618
805
 
619
- // Generate index.html
620
- console.log('📄 Generating index.html...');
621
- const indexHtml = `<!DOCTYPE html>
622
- <html lang="en">
623
- <head>
624
- <meta charset="UTF-8">
625
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
626
- <title>JUX App</title>
627
- </head>
628
- <body>
629
- <div id="app"></div>
630
- <script type="module" src="/bundle.js"></script>
631
- </body>
632
- </html>`;
633
-
634
- fs.writeFileSync(path.join(this.distDir, 'index.html'), indexHtml);
635
-
636
- // Copy public folder
637
- this.copyPublicFolder();
638
-
639
- // Write source snapshot for error overlay
640
- const snapshotPath = path.join(this.distDir, '__jux_sources.json');
641
- fs.writeFileSync(snapshotPath, JSON.stringify(this._sourceSnapshot, null, 2));
642
-
643
- const endTime = Date.now();
644
- console.log(`\n✅ Build complete in ${endTime - startTime}ms`);
645
- console.log(` Output: ${this.distDir}`);
646
-
647
- this.reportValidationIssues();
648
-
649
- return {
650
- success: true,
651
- errors: this._validationIssues || [],
652
- distDir: this.distDir
653
- };
806
+ // Expose regular components
807
+ if (juxImports.size > 0) {
808
+ entry += `\n// Expose components\n`;
809
+ entry += `Object.assign(window, { ${[...juxImports].join(', ')} });\n`;
810
+ }
654
811
 
655
- } catch (err) {
656
- console.error('❌ Build failed:', err.message);
657
- console.error(err.stack);
658
- return {
659
- success: false,
660
- errors: [err.message]
812
+ entry += `\n// --- VIEW FUNCTIONS ---\n`;
813
+
814
+ views.forEach(v => {
815
+ const functionName = this._generateFunctionName(v.name);
816
+ allIssues.push(...this.validateViewCode(v.name, v.content));
817
+
818
+ sourceSnapshot[v.file] = {
819
+ name: v.name,
820
+ file: v.file,
821
+ content: v.content,
822
+ lines: v.content.split('\n')
661
823
  };
662
- }
824
+
825
+ let viewCode = this.removeImports(v.content).replace(/^\s*export\s+default\s+.*$/gm, '');
826
+ const asyncPrefix = viewCode.includes('await ') ? 'async ' : '';
827
+
828
+ entry += `\n${asyncPrefix}function render${functionName}() {\n${viewCode}\n}\n`;
829
+ });
830
+
831
+ dataModules.forEach(m => {
832
+ sourceSnapshot[m.file] = { name: m.name, file: m.file, content: m.content, lines: m.content.split('\n') };
833
+ });
834
+ sharedModules.forEach(m => {
835
+ sourceSnapshot[m.file] = { name: m.name, file: m.file, content: m.content, lines: m.content.split('\n') };
836
+ });
837
+
838
+ this._sourceSnapshot = sourceSnapshot;
839
+ this._validationIssues = allIssues;
840
+ entry += this._generateRouter(views);
841
+ return entry;
842
+ }
843
+
844
+ reportValidationIssues() {
845
+ if (!this._validationIssues || this._validationIssues.length === 0) return;
846
+
847
+ console.log('\n⚠️ Validation Issues:\n');
848
+ this._validationIssues.forEach(issue => {
849
+ const icon = issue.type === 'error' ? '❌' : '⚠️';
850
+ console.log(` ${icon} ${issue.view}:${issue.line} - ${issue.message}`);
851
+ });
852
+ console.log('');
663
853
  }
664
854
 
665
855
  /**
666
- * Copy public folder contents to dist
856
+ * Generate routes based on folder structure (SINGLE DEFINITION)
667
857
  */
668
- copyPublicFolder() {
669
- // Use configured public path or resolve from paths object
670
- const publicSrc = this.paths.public
671
- ? this.paths.public
672
- : path.resolve(process.cwd(), this.publicDir);
673
-
674
- if (!fs.existsSync(publicSrc)) {
675
- return; // No public folder, skip
858
+ _generateRouter(views) {
859
+ let routeMap = '';
860
+
861
+ views.forEach(v => {
862
+ const routePath = this._generateRoutePath(v.file);
863
+ const functionName = this._generateFunctionName(v.name);
864
+ routeMap += ` '${routePath}': render${functionName},\n`;
865
+ });
866
+
867
+ return `
868
+ // --- JUX SOURCE LOADER ---
869
+ var __juxSources = null;
870
+ async function __juxLoadSources() {
871
+ if (__juxSources) return __juxSources;
872
+ try {
873
+ var res = await fetch('/__jux_sources.json');
874
+ __juxSources = await res.json();
875
+ } catch (e) {
876
+ __juxSources = {};
877
+ }
878
+ return __juxSources;
879
+ }
880
+
881
+ function __juxFindSource(stack) {
882
+ var match = stack.match(/render([A-Z][a-zA-Z0-9_]*)/);
883
+ if (match) {
884
+ var funcName = match[1];
885
+ for (var file in __juxSources || {}) {
886
+ var normalized = __juxSources[file].name
887
+ .split('_')
888
+ .map(s => s.charAt(0).toUpperCase() + s.slice(1).toLowerCase())
889
+ .join('');
890
+
891
+ if (normalized === funcName) {
892
+ return { file: file, source: __juxSources[file], viewName: funcName };
893
+ }
894
+ }
895
+ }
896
+ return null;
897
+ }
898
+
899
+ // --- JUX RUNTIME ERROR OVERLAY ---
900
+ var __juxErrorOverlay = {
901
+ styles: \`
902
+ #__jux-error-overlay {
903
+ position: fixed; inset: 0; z-index: 99999;
904
+ background: rgba(0, 0, 0, 0.4);
905
+ display: flex; align-items: center; justify-content: center;
906
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
907
+ opacity: 0; transition: opacity 0.2s ease-out; backdrop-filter: blur(2px);
676
908
  }
909
+ #__jux-error-overlay.visible { opacity: 1; }
910
+ #__jux-error-overlay * { box-sizing: border-box; }
911
+ .__jux-modal {
912
+ background: #f8f9fa; border-radius: 4px;
913
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.4);
914
+ max-width: 80vw; width: 90%; max-height: 90vh;
915
+ overflow: hidden; display: flex; flex-direction: column;
916
+ transform: translateY(10px); transition: transform 0.2s ease-out;
917
+ }
918
+ #__jux-error-overlay.visible .__jux-modal { transform: translateY(0); }
919
+ .__jux-header { background: #fff; padding: 20px 24px; border-bottom: 1px solid #e5e7eb; }
920
+ .__jux-header h3 { margin: 0 0 6px; font-weight: 600; font-size: 11px; color: #9ca3af; text-transform: uppercase; }
921
+ .__jux-header h1 { margin: 0 0 8px; font-size: 18px; font-weight: 600; color: #dc2626; line-height: 1.3; }
922
+ .__jux-header .file-info { color: #6b7280; font-size: 13px; }
923
+ .__jux-header .file-info strong { color: #dc2626; font-weight: 600; }
924
+ .__jux-source { padding: 16px 24px; overflow: auto; flex: 1; background: #f8f9fa; }
925
+ .__jux-code { background: #fff; border-radius: 8px; overflow: hidden; border: 1px solid #e5e7eb; }
926
+ .__jux-code-line { display: flex; font-size: 13px; line-height: 1.7; }
927
+ .__jux-code-line.error { background: #fef2f2; }
928
+ .__jux-code-line.error .__jux-line-code { color: #dc2626; font-weight: 500; }
929
+ .__jux-code-line.context { background: #fefce8; }
930
+ .__jux-line-num { min-width: 44px; padding: 4px 12px; text-align: right; color: #9ca3af; background: #f9fafb; border-right: 1px solid #e5e7eb; font-size: 12px; }
931
+ .__jux-code-line.error .__jux-line-num { background: #fef2f2; color: #dc2626; }
932
+ .__jux-line-code { flex: 1; padding: 4px 16px; color: #374151; white-space: pre; overflow-x: auto; }
933
+ .__jux-footer { padding: 16px 24px; background: #fff; border-top: 1px solid #e5e7eb; display: flex; justify-content: space-between; align-items: center; }
934
+ .__jux-tip { color: #6b7280; font-size: 12px; }
935
+ .__jux-dismiss { background: #f3f4f6; color: #374151; border: 1px solid #d1d5db; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 500; }
936
+ .__jux-dismiss:hover { background: #e5e7eb; }
937
+ .__jux-no-source { color: #6b7280; padding: 24px; text-align: center; }
938
+ .__jux-no-source pre { background: #fff; padding: 16px; border-radius: 8px; margin-top: 16px; font-size: 11px; color: #6b7280; overflow-x: auto; text-align: left; border: 1px solid #e5e7eb; }
939
+ \`,
677
940
 
678
- console.log('📦 Copying public assets...');
941
+ show: async function(error, title) {
942
+ title = title || 'Runtime Error';
943
+ var existing = document.getElementById('__jux-error-overlay');
944
+ if (existing) existing.remove();
945
+ await __juxLoadSources();
946
+
947
+ var overlay = document.createElement('div');
948
+ overlay.id = '__jux-error-overlay';
949
+ var stack = error && error.stack ? error.stack : '';
950
+ var msg = error && error.message ? error.message : String(error);
951
+ var found = __juxFindSource(stack);
952
+ var sourceHtml = '', fileInfo = '';
953
+
954
+ if (found && found.source && found.source.lines) {
955
+ var lines = found.source.lines;
956
+ fileInfo = '<span class="file-info">in <strong>' + found.file + '</strong></span>';
957
+ var errorLineIndex = -1;
958
+ var errorMethod = msg.match(/\\.([a-zA-Z]+)\\s*is not a function/);
959
+ if (errorMethod) {
960
+ for (var i = 0; i < lines.length; i++) {
961
+ if (lines[i].indexOf('.' + errorMethod[1]) > -1) { errorLineIndex = i; break; }
962
+ }
963
+ }
964
+ if (errorLineIndex === -1) {
965
+ for (var i = 0; i < lines.length; i++) {
966
+ if (lines[i].indexOf('throw ') > -1) { errorLineIndex = i; break; }
967
+ }
968
+ }
969
+ var contextStart = Math.max(0, errorLineIndex - 3);
970
+ var contextEnd = Math.min(lines.length - 1, errorLineIndex + 5);
971
+ if (errorLineIndex === -1) { contextStart = 0; contextEnd = Math.min(14, lines.length - 1); }
972
+
973
+ for (var i = contextStart; i <= contextEnd; i++) {
974
+ var isError = (i === errorLineIndex);
975
+ var isContext = !isError && errorLineIndex > -1 && Math.abs(i - errorLineIndex) <= 2;
976
+ var lineClass = isError ? ' error' : (isContext ? ' context' : '');
977
+ var lineCode = lines[i].replace(/</g, '&lt;').replace(/>/g, '&gt;') || ' ';
978
+ sourceHtml += '<div class="__jux-code-line' + lineClass + '"><span class="__jux-line-num">' + (i + 1) + '</span><span class="__jux-line-code">' + lineCode + '</span></div>';
979
+ }
980
+ } else {
981
+ sourceHtml = '<div class="__jux-no-source"><p>Could not locate source file.</p><pre>' + stack + '</pre></div>';
982
+ }
983
+
984
+ overlay.innerHTML = '<style>' + this.styles + '</style><div class="__jux-modal"><div class="__jux-header"><h3>' + title + '</h3><h1>' + msg + '</h1>' + fileInfo + '</div><div class="__jux-source"><div class="__jux-code">' + sourceHtml + '</div></div><div class="__jux-footer"><span class="__jux-tip">💡 Fix the error and save to reload</span><button class="__jux-dismiss" onclick="document.getElementById(\\'__jux-error-overlay\\').remove()">Dismiss</button></div></div>';
985
+ document.body.appendChild(overlay);
986
+ requestAnimationFrame(function() { overlay.classList.add('visible'); });
987
+ console.error(title + ':', error);
988
+ }
989
+ };
679
990
 
680
- try {
681
- this._copyDirRecursive(publicSrc, this.distDir, 0);
682
- console.log('✅ Public assets copied');
683
- } catch (err) {
684
- console.warn('⚠️ Error copying public folder:', err.message);
991
+ window.addEventListener('error', function(e) { __juxErrorOverlay.show(e.error || new Error(e.message), 'Uncaught Error'); }, true);
992
+ window.addEventListener('unhandledrejection', function(e) { __juxErrorOverlay.show(e.reason || new Error('Promise rejected'), 'Unhandled Promise Rejection'); }, true);
993
+
994
+ // --- JUX ROUTER ---
995
+ const routes = {
996
+ ${routeMap}};
997
+
998
+ async function navigate(path) {
999
+ const view = routes[path];
1000
+ if (!view) {
1001
+ document.getElementById('app').innerHTML = '<h1 style="padding:40px;">404 - Not Found</h1>';
1002
+ return;
1003
+ }
1004
+ document.getElementById('app').innerHTML = '';
1005
+ var overlay = document.getElementById('__jux-error-overlay');
1006
+ if (overlay) overlay.remove();
1007
+ try { await view(); } catch (err) { __juxErrorOverlay.show(err, 'Jux Render Error'); }
1008
+ }
1009
+
1010
+ document.addEventListener('click', e => {
1011
+ const a = e.target.closest('a');
1012
+ if (!a || a.dataset.router === 'false') return;
1013
+ try { if (new URL(a.href, location.origin).origin !== location.origin) return; } catch { return; }
1014
+ e.preventDefault();
1015
+ history.pushState({}, '', a.href);
1016
+ navigate(new URL(a.href, location.origin).pathname);
1017
+ });
1018
+
1019
+ window.addEventListener('popstate', () => navigate(location.pathname));
1020
+ navigate(location.pathname);
1021
+ `;
1022
+ }
1023
+
1024
+ /**
1025
+ * ✅ Copy source files to dist/jux for esbuild to resolve
1026
+ */
1027
+ _copySourceToDist() {
1028
+ const distJuxDir = path.join(this.distDir, 'jux');
1029
+
1030
+ // Ensure dist/jux directory exists
1031
+ if (!fs.existsSync(distJuxDir)) {
1032
+ fs.mkdirSync(distJuxDir, { recursive: true });
685
1033
  }
1034
+
1035
+ // Copy all .jux and .js files from source to dist
1036
+ this._copySourceFilesRecursive(this.srcDir, this.srcDir, distJuxDir);
686
1037
  }
687
1038
 
688
1039
  /**
689
- * Recursively copy directory contents
1040
+ * Recursively copy .jux and .js files preserving folder structure
690
1041
  */
691
- _copyDirRecursive(src, dest, depth = 0) {
1042
+ _copySourceFilesRecursive(src, baseDir, distBase) {
692
1043
  const entries = fs.readdirSync(src, { withFileTypes: true });
693
1044
 
694
1045
  entries.forEach(entry => {
695
- // Skip hidden files and directories
696
- if (entry.name.startsWith('.')) return;
1046
+ if (entry.name.startsWith('.')) return; // Skip hidden
697
1047
 
698
1048
  const srcPath = path.join(src, entry.name);
699
- const destPath = path.join(dest, entry.name);
1049
+ const relativePath = path.relative(baseDir, srcPath);
1050
+ const destPath = path.join(distBase, relativePath);
700
1051
 
701
1052
  if (entry.isDirectory()) {
702
- // Create directory and recurse
1053
+ // Create directory if it doesn't exist
703
1054
  if (!fs.existsSync(destPath)) {
704
1055
  fs.mkdirSync(destPath, { recursive: true });
705
1056
  }
706
- this._copyDirRecursive(srcPath, destPath, depth + 1);
707
- } else {
708
- // Copy file
709
- fs.copyFileSync(srcPath, destPath);
710
-
711
- // Log files at root level only
712
- if (depth === 0) {
713
- const ext = path.extname(entry.name);
714
- const icon = this._getFileIcon(ext);
715
- console.log(` ${icon} ${entry.name}`);
1057
+ // Recurse into subdirectory
1058
+ this._copySourceFilesRecursive(srcPath, baseDir, distBase);
1059
+ } else if (entry.name.endsWith('.jux') || entry.name.endsWith('.js')) {
1060
+ // Copy .jux and .js files (skip assets)
1061
+ if (!this.isAssetFile(entry.name)) {
1062
+ // Ensure parent directory exists
1063
+ const destDir = path.dirname(destPath);
1064
+ if (!fs.existsSync(destDir)) {
1065
+ fs.mkdirSync(destDir, { recursive: true });
1066
+ }
1067
+ fs.copyFileSync(srcPath, destPath);
1068
+ console.log(` 📋 Copied: ${relativePath}`);
716
1069
  }
717
1070
  }
718
1071
  });
719
1072
  }
720
1073
 
721
1074
  /**
722
- * Get icon for file type
1075
+ * Copy ONLY public assets (CSS, images, fonts) to dist
1076
+ * ❌ Does NOT copy .jux files (they are compiled separately)
723
1077
  */
724
- _getFileIcon(ext) {
725
- const icons = {
726
- '.html': '📄',
727
- '.css': '🎨',
728
- '.js': '📜',
729
- '.json': '📋',
730
- '.png': '🖼️',
731
- '.jpg': '🖼️',
732
- '.jpeg': '🖼️',
733
- '.gif': '🖼️',
734
- '.svg': '🎨',
735
- '.ico': '🔖',
736
- '.woff': '🔤',
737
- '.woff2': '🔤',
738
- '.ttf': '🔤',
739
- '.eot': '🔤'
1078
+ async copyPublicAssets() {
1079
+ const { publicDir, distDir, paths } = this.config;
1080
+
1081
+ // Resolve public folder path
1082
+ const publicPath = paths?.public || path.resolve(this.config.srcDir, '..', publicDir);
1083
+
1084
+ if (!fs.existsSync(publicPath)) {
1085
+ console.log(`ℹ️ No public folder found at ${publicPath}, skipping asset copy`);
1086
+ return;
1087
+ }
1088
+
1089
+ console.log(`📦 Copying public assets from ${publicPath}...`);
1090
+
1091
+ // ✅ ONLY copy known asset file types
1092
+ const assetTypes = [
1093
+ '.css', '.scss', '.sass', '.less', // Stylesheets
1094
+ '.jpg', '.jpeg', '.png', '.gif', '.svg', // Images
1095
+ '.ico', '.webp', '.avif', // Icons/modern images
1096
+ '.woff', '.woff2', '.ttf', '.eot', '.otf', // Fonts
1097
+ '.mp4', '.webm', '.ogg', // Video
1098
+ '.mp3', '.wav', '.m4a', // Audio
1099
+ '.pdf', '.txt', '.json', '.xml' // Documents
1100
+ ];
1101
+
1102
+ const copyRecursive = (src, dest) => {
1103
+ if (!fs.existsSync(src)) return;
1104
+
1105
+ const entries = fs.readdirSync(src, { withFileTypes: true });
1106
+
1107
+ for (const entry of entries) {
1108
+ const srcPath = path.join(src, entry.name);
1109
+ const destPath = path.join(dest, entry.name);
1110
+
1111
+ if (entry.isDirectory()) {
1112
+ fs.mkdirSync(destPath, { recursive: true });
1113
+ copyRecursive(srcPath, destPath);
1114
+ } else {
1115
+ const ext = path.extname(entry.name).toLowerCase();
1116
+
1117
+ // ✅ ONLY copy known asset types
1118
+ // ❌ SKIP .jux files (they're bundled into entry.js)
1119
+ if (assetTypes.includes(ext)) {
1120
+ fs.copyFileSync(srcPath, destPath);
1121
+ console.log(` ✓ ${entry.name}`);
1122
+ } else if (ext === '.jux') {
1123
+ // Silently skip .jux files
1124
+ continue;
1125
+ } else {
1126
+ // Warn about unknown file types
1127
+ console.warn(` ⚠️ Skipped: ${entry.name} (unknown type: ${ext})`);
1128
+ }
1129
+ }
1130
+ }
1131
+ };
1132
+
1133
+ copyRecursive(publicPath, distDir);
1134
+ }
1135
+
1136
+ /**
1137
+ * Main build pipeline
1138
+ */
1139
+ async build() {
1140
+ console.log('🔨 Building JUX project...\n');
1141
+
1142
+ try {
1143
+ // 1. Clean dist
1144
+ if (fs.existsSync(this.config.distDir)) {
1145
+ fs.rmSync(this.config.distDir, { recursive: true, force: true });
1146
+ }
1147
+ fs.mkdirSync(this.config.distDir, { recursive: true });
1148
+
1149
+ // 2. Scan .jux files
1150
+ const juxFiles = this.scanJuxFiles(this.config.srcDir);
1151
+ console.log(`📂 Found ${juxFiles.length} .jux files\n`);
1152
+
1153
+ // 3. ✅ Bundle all .jux files → entry.js
1154
+ await this.bundleJuxFiles(juxFiles);
1155
+
1156
+ // 4. ✅ Bundle vendor libraries → bundle.js
1157
+ await this.bundleVendorFiles();
1158
+
1159
+ // 5. Copy public assets (CSS, images, etc.)
1160
+ await this.copyPublicAssets();
1161
+
1162
+ // 6. Generate index.html (with entry.js + bundle.js)
1163
+ await this.generateIndexHtml();
1164
+
1165
+ console.log('\n✅ Build completed successfully!\n');
1166
+ console.log(`📁 Output: ${this.config.distDir}`);
1167
+ console.log(` ✓ entry.js (${juxFiles.length} .jux files bundled)`);
1168
+ console.log(` ✓ bundle.js (vendor libraries)`);
1169
+ console.log(` ✓ index.html`);
1170
+ console.log(` ✓ Public assets copied\n`);
1171
+
1172
+ return { success: true };
1173
+
1174
+ } catch (error) {
1175
+ console.error('\n❌ Build failed:', error.message);
1176
+ console.error(error.stack);
1177
+ return { success: false, error };
1178
+ }
1179
+ }
1180
+
1181
+ /**
1182
+ * Scan for .jux files ONLY in source directory
1183
+ */
1184
+ scanJuxFiles(dir) {
1185
+ const juxFiles = [];
1186
+
1187
+ const scan = (currentDir) => {
1188
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
1189
+
1190
+ for (const entry of entries) {
1191
+ const fullPath = path.join(currentDir, entry.name);
1192
+
1193
+ if (entry.isDirectory()) {
1194
+ scan(fullPath);
1195
+ } else if (entry.name.endsWith('.jux')) {
1196
+ juxFiles.push(fullPath);
1197
+ }
1198
+ }
740
1199
  };
741
- return icons[ext.toLowerCase()] || '📦';
1200
+
1201
+ scan(dir);
1202
+ return juxFiles;
1203
+ }
1204
+
1205
+ /**
1206
+ * Compile a single .jux file to .js
1207
+ */
1208
+ async compileFile(juxFilePath) {
1209
+ const relativePath = path.relative(this.config.srcDir, juxFilePath);
1210
+ const outputPath = path.join(this.config.distDir, relativePath.replace(/\.jux$/, '.js'));
1211
+
1212
+ // Ensure output directory exists
1213
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
1214
+
1215
+ // Read .jux source
1216
+ const juxCode = fs.readFileSync(juxFilePath, 'utf8');
1217
+
1218
+ // Compile to JavaScript
1219
+ const jsCode = this.transformJuxToJs(juxCode);
1220
+
1221
+ // Write compiled .js file
1222
+ fs.writeFileSync(outputPath, jsCode, 'utf8');
1223
+
1224
+ console.log(` ✓ ${relativePath} → ${path.basename(outputPath)}`);
1225
+ }
1226
+
1227
+ /**
1228
+ * ✅ Generate name from folder structure
1229
+ * Example: abc/juxabc.jux -> abc_juxabc
1230
+ */
1231
+ _generateNameFromPath(filepath) {
1232
+ // Convert file path to module name
1233
+ // e.g., "pages/about.jux" → "pages_about"
1234
+ return filepath
1235
+ .replace(/\.jux$/, '')
1236
+ .replace(/[\/\\]/g, '_')
1237
+ .replace(/[^a-zA-Z0-9_]/g, '');
742
1238
  }
743
1239
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.1.115",
3
+ "version": "1.1.117",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "index.js",