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.
- package/dom-structure-map.json +1 -1
- package/lib/components/base/LayoutExtensions.d.ts +7 -0
- package/lib/components/base/LayoutExtensions.d.ts.map +1 -1
- package/lib/components/base/LayoutExtensions.js +8 -0
- package/lib/components/base/LayoutExtensions.ts +17 -0
- package/lib/styles/nav.css +322 -0
- package/lib/styles/stacks.css +0 -2
- package/machinery/compiler3.js +623 -127
- package/package.json +1 -1
package/dom-structure-map.json
CHANGED
|
@@ -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; }
|
package/lib/styles/stacks.css
CHANGED
|
@@ -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; }
|
package/machinery/compiler3.js
CHANGED
|
@@ -568,176 +568,672 @@ navigate(location.pathname);
|
|
|
568
568
|
}
|
|
569
569
|
|
|
570
570
|
/**
|
|
571
|
-
*
|
|
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
|
|
636
|
+
console.log('🔨 Building JUX project...\n');
|
|
575
637
|
|
|
576
638
|
try {
|
|
577
|
-
|
|
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
|
|
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
|
-
|
|
649
|
+
// 3. ✅ Bundle all .jux files → entry.js
|
|
650
|
+
await this.bundleJuxFiles(juxFiles);
|
|
583
651
|
|
|
584
|
-
//
|
|
585
|
-
await this.
|
|
652
|
+
// 4. ✅ Bundle vendor libraries → bundle.js
|
|
653
|
+
await this.bundleVendorFiles();
|
|
586
654
|
|
|
587
|
-
//
|
|
588
|
-
|
|
589
|
-
|
|
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
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
console.
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
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
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
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
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
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
|
-
*
|
|
856
|
+
* ✅ Generate routes based on folder structure (SINGLE DEFINITION)
|
|
667
857
|
*/
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
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
|
-
|
|
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, '<').replace(/>/g, '>') || ' ';
|
|
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
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
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
|
|
1040
|
+
* Recursively copy .jux and .js files preserving folder structure
|
|
690
1041
|
*/
|
|
691
|
-
|
|
1042
|
+
_copySourceFilesRecursive(src, baseDir, distBase) {
|
|
692
1043
|
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
693
1044
|
|
|
694
1045
|
entries.forEach(entry => {
|
|
695
|
-
// Skip hidden
|
|
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
|
|
1049
|
+
const relativePath = path.relative(baseDir, srcPath);
|
|
1050
|
+
const destPath = path.join(distBase, relativePath);
|
|
700
1051
|
|
|
701
1052
|
if (entry.isDirectory()) {
|
|
702
|
-
// Create directory
|
|
1053
|
+
// Create directory if it doesn't exist
|
|
703
1054
|
if (!fs.existsSync(destPath)) {
|
|
704
1055
|
fs.mkdirSync(destPath, { recursive: true });
|
|
705
1056
|
}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
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
|
-
*
|
|
1075
|
+
* Copy ONLY public assets (CSS, images, fonts) to dist
|
|
1076
|
+
* ❌ Does NOT copy .jux files (they are compiled separately)
|
|
723
1077
|
*/
|
|
724
|
-
|
|
725
|
-
const
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
'.
|
|
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
|
-
|
|
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
|
}
|