juxscript 1.1.3 → 1.1.4
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/machinery/build3.js +7 -91
- package/machinery/compiler3.js +3 -209
- package/package.json +19 -5
- package/lib/components/alert.ts +0 -200
- package/lib/components/app.ts +0 -247
- package/lib/components/badge.ts +0 -101
- package/lib/components/base/BaseComponent.ts +0 -421
- package/lib/components/base/FormInput.ts +0 -227
- package/lib/components/button.ts +0 -178
- package/lib/components/card.ts +0 -173
- package/lib/components/chart.ts +0 -231
- package/lib/components/checkbox.ts +0 -242
- package/lib/components/code.ts +0 -123
- package/lib/components/container.ts +0 -140
- package/lib/components/data.ts +0 -135
- package/lib/components/datepicker.ts +0 -234
- package/lib/components/dialog.ts +0 -172
- package/lib/components/divider.ts +0 -100
- package/lib/components/dropdown.ts +0 -186
- package/lib/components/element.ts +0 -267
- package/lib/components/fileupload.ts +0 -309
- package/lib/components/grid.ts +0 -291
- package/lib/components/guard.ts +0 -92
- package/lib/components/heading.ts +0 -96
- package/lib/components/helpers.ts +0 -41
- package/lib/components/hero.ts +0 -224
- package/lib/components/icon.ts +0 -178
- package/lib/components/icons.ts +0 -464
- package/lib/components/include.ts +0 -410
- package/lib/components/input.ts +0 -457
- package/lib/components/list.ts +0 -419
- package/lib/components/loading.ts +0 -100
- package/lib/components/menu.ts +0 -275
- package/lib/components/modal.ts +0 -284
- package/lib/components/nav.ts +0 -257
- package/lib/components/paragraph.ts +0 -97
- package/lib/components/progress.ts +0 -159
- package/lib/components/radio.ts +0 -278
- package/lib/components/req.ts +0 -303
- package/lib/components/script.ts +0 -41
- package/lib/components/select.ts +0 -252
- package/lib/components/sidebar.ts +0 -275
- package/lib/components/style.ts +0 -41
- package/lib/components/switch.ts +0 -246
- package/lib/components/table.ts +0 -1249
- package/lib/components/tabs.ts +0 -250
- package/lib/components/theme-toggle.ts +0 -293
- package/lib/components/tooltip.ts +0 -144
- package/lib/components/view.ts +0 -190
- package/lib/components/write.ts +0 -272
- package/lib/layouts/default.css +0 -260
- package/lib/layouts/figma.css +0 -334
- package/lib/reactivity/state.ts +0 -78
- package/lib/utils/fetch.ts +0 -553
- package/machinery/ast.js +0 -347
- package/machinery/build.js +0 -466
- package/machinery/bundleAssets.js +0 -0
- package/machinery/bundleJux.js +0 -0
- package/machinery/bundleVendors.js +0 -0
- package/machinery/doc-generator.js +0 -136
- package/machinery/imports.js +0 -155
- package/machinery/ts-shim.js +0 -46
- package/machinery/validators/file-validator.js +0 -123
package/machinery/build3.js
CHANGED
|
@@ -18,31 +18,23 @@ try {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
// ═══════════════════════════════════════════════════════════════
|
|
21
|
-
// EXPLODE CONFIG
|
|
21
|
+
// EXPLODE CONFIG
|
|
22
22
|
// ═══════════════════════════════════════════════════════════════
|
|
23
23
|
const directories = {
|
|
24
24
|
source: rawConfig.directories?.source || './jux',
|
|
25
|
-
distribution: rawConfig.directories?.distribution || './.jux-dist'
|
|
26
|
-
themes: rawConfig.directories?.themes || './themes',
|
|
27
|
-
layouts: rawConfig.directories?.layouts || './themes/layouts',
|
|
28
|
-
assets: rawConfig.directories?.assets || './themes/assets'
|
|
25
|
+
distribution: rawConfig.directories?.distribution || './.jux-dist'
|
|
29
26
|
};
|
|
30
27
|
|
|
31
28
|
const defaults = {
|
|
32
29
|
httpPort: rawConfig.defaults?.httpPort || 3000,
|
|
33
30
|
wsPort: rawConfig.defaults?.wsPort || 3001,
|
|
34
|
-
autoRoute: rawConfig.defaults?.autoRoute ?? true
|
|
35
|
-
layout: rawConfig.defaults?.layout || null,
|
|
36
|
-
theme: rawConfig.defaults?.theme || null
|
|
31
|
+
autoRoute: rawConfig.defaults?.autoRoute ?? true
|
|
37
32
|
};
|
|
38
33
|
|
|
39
34
|
// Resolve absolute paths
|
|
40
35
|
const paths = {
|
|
41
36
|
source: path.resolve(PROJECT_ROOT, directories.source),
|
|
42
|
-
distribution: path.resolve(PROJECT_ROOT, directories.distribution)
|
|
43
|
-
themes: path.resolve(PROJECT_ROOT, directories.source, directories.themes),
|
|
44
|
-
layouts: path.resolve(PROJECT_ROOT, directories.source, directories.layouts),
|
|
45
|
-
assets: path.resolve(PROJECT_ROOT, directories.source, directories.assets)
|
|
37
|
+
distribution: path.resolve(PROJECT_ROOT, directories.distribution)
|
|
46
38
|
};
|
|
47
39
|
|
|
48
40
|
// ═══════════════════════════════════════════════════════════════
|
|
@@ -50,84 +42,10 @@ const paths = {
|
|
|
50
42
|
// ═══════════════════════════════════════════════════════════════
|
|
51
43
|
console.log(`\n📁 Directory Check:`);
|
|
52
44
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (name === 'distribution') continue;
|
|
56
|
-
|
|
57
|
-
const exists = fs.existsSync(dirPath);
|
|
58
|
-
dirStatus[name] = exists;
|
|
59
|
-
|
|
60
|
-
const icon = exists ? '✓' : '✗';
|
|
61
|
-
const suffix = exists ? '' : ' (will be skipped)';
|
|
62
|
-
console.log(` ${icon} ${name}: ${dirPath}${suffix}`);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// ═══════════════════════════════════════════════════════════════
|
|
66
|
-
// RESOLVE DEFAULT LAYOUT & THEME
|
|
67
|
-
// ═══════════════════════════════════════════════════════════════
|
|
68
|
-
function resolveFile(filename, ...searchPaths) {
|
|
69
|
-
if (!filename) return null;
|
|
70
|
-
|
|
71
|
-
// First try the filename as-is
|
|
72
|
-
if (fs.existsSync(filename)) {
|
|
73
|
-
return path.resolve(filename);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Try each search path
|
|
77
|
-
for (const searchPath of searchPaths) {
|
|
78
|
-
if (!searchPath || !fs.existsSync(searchPath)) continue;
|
|
79
|
-
|
|
80
|
-
const fullPath = path.resolve(searchPath, filename);
|
|
81
|
-
if (fs.existsSync(fullPath)) {
|
|
82
|
-
return fullPath;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
console.log(`\n🎨 Defaults Resolution:`);
|
|
90
|
-
|
|
91
|
-
// Resolve layout
|
|
92
|
-
const layoutPath = defaults.layout ? resolveFile(
|
|
93
|
-
defaults.layout,
|
|
94
|
-
paths.layouts,
|
|
95
|
-
paths.source,
|
|
96
|
-
PROJECT_ROOT
|
|
97
|
-
) : null;
|
|
98
|
-
|
|
99
|
-
if (defaults.layout) {
|
|
100
|
-
if (layoutPath) {
|
|
101
|
-
console.log(` ✓ layout: ${layoutPath}`);
|
|
102
|
-
} else {
|
|
103
|
-
console.log(` ✗ layout: "${defaults.layout}" not found, will be skipped`);
|
|
104
|
-
}
|
|
45
|
+
if (fs.existsSync(paths.source)) {
|
|
46
|
+
console.log(` ✓ source: ${paths.source}`);
|
|
105
47
|
} else {
|
|
106
|
-
console.log(`
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Resolve theme
|
|
110
|
-
const themePath = defaults.theme ? resolveFile(
|
|
111
|
-
defaults.theme,
|
|
112
|
-
paths.themes,
|
|
113
|
-
paths.source,
|
|
114
|
-
PROJECT_ROOT
|
|
115
|
-
) : null;
|
|
116
|
-
|
|
117
|
-
if (defaults.theme) {
|
|
118
|
-
if (themePath) {
|
|
119
|
-
console.log(` ✓ theme: ${themePath}`);
|
|
120
|
-
} else {
|
|
121
|
-
console.log(` ✗ theme: "${defaults.theme}" not found, will be skipped`);
|
|
122
|
-
}
|
|
123
|
-
} else {
|
|
124
|
-
console.log(` - theme: not configured`);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// ═══════════════════════════════════════════════════════════════
|
|
128
|
-
// VALIDATE SOURCE DIRECTORY EXISTS
|
|
129
|
-
// ═══════════════════════════════════════════════════════════════
|
|
130
|
-
if (!dirStatus.source) {
|
|
48
|
+
console.log(` ✗ source: ${paths.source} (not found)`);
|
|
131
49
|
console.error(`\n❌ Source directory not found: ${paths.source}`);
|
|
132
50
|
console.error(` Create the directory or update juxconfig.js`);
|
|
133
51
|
process.exit(1);
|
|
@@ -141,8 +59,6 @@ console.log(`\n`);
|
|
|
141
59
|
const compiler = new JuxCompiler({
|
|
142
60
|
srcDir: paths.source,
|
|
143
61
|
distDir: paths.distribution,
|
|
144
|
-
layoutPath,
|
|
145
|
-
themePath,
|
|
146
62
|
defaults,
|
|
147
63
|
paths
|
|
148
64
|
});
|
package/machinery/compiler3.js
CHANGED
|
@@ -13,8 +13,6 @@ export class JuxCompiler {
|
|
|
13
13
|
this.config = config;
|
|
14
14
|
this.srcDir = config.srcDir || './jux';
|
|
15
15
|
this.distDir = config.distDir || './.jux-dist';
|
|
16
|
-
this.layoutPath = config.layoutPath || null;
|
|
17
|
-
this.themePath = config.themePath || null;
|
|
18
16
|
this.defaults = config.defaults || {};
|
|
19
17
|
this.paths = config.paths || {};
|
|
20
18
|
this._juxscriptExports = null;
|
|
@@ -94,163 +92,8 @@ export class JuxCompiler {
|
|
|
94
92
|
}
|
|
95
93
|
|
|
96
94
|
/**
|
|
97
|
-
*
|
|
95
|
+
* Generate entry point without layout/theme logic
|
|
98
96
|
*/
|
|
99
|
-
copySourceAssets() {
|
|
100
|
-
const copied = { themes: 0, layouts: 0, assets: 0, other: 0 };
|
|
101
|
-
|
|
102
|
-
const copyDir = (srcDir, destDir, category) => {
|
|
103
|
-
if (!fs.existsSync(srcDir)) return 0;
|
|
104
|
-
|
|
105
|
-
let count = 0;
|
|
106
|
-
fs.mkdirSync(destDir, { recursive: true });
|
|
107
|
-
|
|
108
|
-
const entries = fs.readdirSync(srcDir, { withFileTypes: true });
|
|
109
|
-
for (const entry of entries) {
|
|
110
|
-
const srcPath = path.join(srcDir, entry.name);
|
|
111
|
-
const destPath = path.join(destDir, entry.name);
|
|
112
|
-
|
|
113
|
-
if (entry.isDirectory()) {
|
|
114
|
-
count += copyDir(srcPath, destPath, category);
|
|
115
|
-
} else {
|
|
116
|
-
fs.copyFileSync(srcPath, destPath);
|
|
117
|
-
count++;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
return count;
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
// Copy themes folder
|
|
124
|
-
if (this.paths.themes && fs.existsSync(this.paths.themes)) {
|
|
125
|
-
copied.themes = copyDir(this.paths.themes, path.join(this.distDir, 'themes'), 'themes');
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Copy layouts folder (if separate from themes)
|
|
129
|
-
if (this.paths.layouts && fs.existsSync(this.paths.layouts) && this.paths.layouts !== this.paths.themes) {
|
|
130
|
-
copied.layouts = copyDir(this.paths.layouts, path.join(this.distDir, 'layouts'), 'layouts');
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Copy assets folder
|
|
134
|
-
if (this.paths.assets && fs.existsSync(this.paths.assets)) {
|
|
135
|
-
copied.assets = copyDir(this.paths.assets, path.join(this.distDir, 'assets'), 'assets');
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const total = copied.themes + copied.layouts + copied.assets;
|
|
139
|
-
if (total > 0) {
|
|
140
|
-
console.log(`📂 Copied source assets: ${copied.themes} themes, ${copied.layouts} layouts, ${copied.assets} assets`);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return copied;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Generate CSS links including theme if configured
|
|
148
|
-
*/
|
|
149
|
-
generateCssLinks() {
|
|
150
|
-
const links = [];
|
|
151
|
-
|
|
152
|
-
// Note: Component CSS is no longer bundled (handled by Styler)
|
|
153
|
-
|
|
154
|
-
// User theme CSS
|
|
155
|
-
if (this.themePath) {
|
|
156
|
-
const themeName = path.basename(this.themePath);
|
|
157
|
-
links.push(` <link rel="stylesheet" href="./themes/${themeName}" id="jux-theme">`);
|
|
158
|
-
console.log(`🎨 🎨 🎨 🎨 Included theme: ${themeName} 🎨 🎨 🎨 🎨 `);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return links.join('\n');
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
async loadJuxscriptExports() {
|
|
165
|
-
if (this._juxscriptExports) return this._juxscriptExports;
|
|
166
|
-
|
|
167
|
-
try {
|
|
168
|
-
const juxscriptPath = this.findJuxscriptPath();
|
|
169
|
-
if (juxscriptPath) {
|
|
170
|
-
const indexContent = fs.readFileSync(juxscriptPath, 'utf8');
|
|
171
|
-
const exports = new Set();
|
|
172
|
-
|
|
173
|
-
for (const match of indexContent.matchAll(/export\s*\{\s*([^}]+)\s*\}/g)) {
|
|
174
|
-
match[1].split(',').forEach(exp => {
|
|
175
|
-
const name = exp.trim().split(/\s+as\s+/)[0].trim();
|
|
176
|
-
if (name) exports.add(name);
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
this._juxscriptExports = [...exports];
|
|
181
|
-
if (this._juxscriptExports.length > 0) {
|
|
182
|
-
console.log(`📦 juxscript exports: ${this._juxscriptExports.join(', ')}`);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
} catch (err) {
|
|
186
|
-
this._juxscriptExports = [];
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
return this._juxscriptExports;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
validateViewCode(viewName, code) {
|
|
193
|
-
const issues = [];
|
|
194
|
-
|
|
195
|
-
let ast;
|
|
196
|
-
try {
|
|
197
|
-
ast = acorn.parse(code, { ecmaVersion: 'latest', sourceType: 'module', locations: true });
|
|
198
|
-
} catch (parseError) {
|
|
199
|
-
issues.push({
|
|
200
|
-
type: 'error',
|
|
201
|
-
view: viewName,
|
|
202
|
-
line: parseError.loc?.line || 0,
|
|
203
|
-
message: `Syntax error: ${parseError.message}`,
|
|
204
|
-
code: ''
|
|
205
|
-
});
|
|
206
|
-
return issues;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const allImports = new Set();
|
|
210
|
-
|
|
211
|
-
walk(ast, {
|
|
212
|
-
ImportDeclaration(node) {
|
|
213
|
-
node.specifiers.forEach(spec => {
|
|
214
|
-
allImports.add(spec.local.name);
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
// Default known facades/components if load fails
|
|
220
|
-
const knownComponents = this._juxscriptExports || ['element', 'input', 'buttonGroup'];
|
|
221
|
-
|
|
222
|
-
walk(ast, {
|
|
223
|
-
Identifier(node, parent) {
|
|
224
|
-
if (parent?.type === 'CallExpression' && parent.callee === node) {
|
|
225
|
-
const name = node.name;
|
|
226
|
-
// Updated: Removed strict Capitalization check to allow facades (lowercase)
|
|
227
|
-
if (!allImports.has(name) && knownComponents.includes(name)) {
|
|
228
|
-
issues.push({
|
|
229
|
-
type: 'warning',
|
|
230
|
-
view: viewName,
|
|
231
|
-
line: node.loc?.start?.line || 0,
|
|
232
|
-
message: `"${name}" is used but not imported from juxscript`,
|
|
233
|
-
code: ''
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
return issues;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Extract layout function name from layout file
|
|
245
|
-
*/
|
|
246
|
-
getLayoutFunctionName() {
|
|
247
|
-
if (!this.layoutPath || !fs.existsSync(this.layoutPath)) return null;
|
|
248
|
-
|
|
249
|
-
const content = fs.readFileSync(this.layoutPath, 'utf8');
|
|
250
|
-
const match = content.match(/export\s+function\s+(\w+)/);
|
|
251
|
-
return match ? match[1] : null;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
97
|
generateEntryPoint(views, dataModules, sharedModules) {
|
|
255
98
|
let entry = `// Auto-generated JUX entry point\n\n`;
|
|
256
99
|
const allIssues = [];
|
|
@@ -265,16 +108,6 @@ export class JuxCompiler {
|
|
|
265
108
|
}
|
|
266
109
|
});
|
|
267
110
|
|
|
268
|
-
// Check layout file for juxscript imports too
|
|
269
|
-
if (this.layoutPath && fs.existsSync(this.layoutPath)) {
|
|
270
|
-
const layoutContent = fs.readFileSync(this.layoutPath, 'utf8');
|
|
271
|
-
for (const match of layoutContent.matchAll(/import\s*\{\s*([^}]+)\s*\}\s*from\s*['"]juxscript['"]/g)) {
|
|
272
|
-
match[1].split(',').map(s => s.trim()).forEach(imp => {
|
|
273
|
-
if (imp) juxImports.add(imp);
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
111
|
if (juxImports.size > 0) {
|
|
279
112
|
entry += `import { ${[...juxImports].sort().join(', ')} } from 'juxscript';\n\n`;
|
|
280
113
|
}
|
|
@@ -294,25 +127,6 @@ export class JuxCompiler {
|
|
|
294
127
|
entry += `\nObject.assign(window, { ${[...juxImports].join(', ')} });\n`;
|
|
295
128
|
}
|
|
296
129
|
|
|
297
|
-
// Add layout function if configured
|
|
298
|
-
const layoutFnName = this.getLayoutFunctionName();
|
|
299
|
-
if (layoutFnName && this.layoutPath) {
|
|
300
|
-
const layoutContent = fs.readFileSync(this.layoutPath, 'utf8');
|
|
301
|
-
const layoutCode = this.removeImports(layoutContent);
|
|
302
|
-
entry += `\n// --- DEFAULT LAYOUT ---\n`;
|
|
303
|
-
entry += layoutCode;
|
|
304
|
-
entry += `\n`;
|
|
305
|
-
|
|
306
|
-
// Store in source snapshot
|
|
307
|
-
const layoutFile = path.basename(this.layoutPath);
|
|
308
|
-
sourceSnapshot[layoutFile] = {
|
|
309
|
-
name: 'layout',
|
|
310
|
-
file: layoutFile,
|
|
311
|
-
content: layoutContent,
|
|
312
|
-
lines: layoutContent.split('\n')
|
|
313
|
-
};
|
|
314
|
-
}
|
|
315
|
-
|
|
316
130
|
entry += `\n// --- VIEW FUNCTIONS ---\n`;
|
|
317
131
|
|
|
318
132
|
views.forEach(v => {
|
|
@@ -341,7 +155,6 @@ export class JuxCompiler {
|
|
|
341
155
|
|
|
342
156
|
this._sourceSnapshot = sourceSnapshot;
|
|
343
157
|
this._validationIssues = allIssues;
|
|
344
|
-
this._layoutFnName = layoutFnName;
|
|
345
158
|
entry += this._generateRouter(views);
|
|
346
159
|
return entry;
|
|
347
160
|
}
|
|
@@ -371,11 +184,6 @@ export class JuxCompiler {
|
|
|
371
184
|
routeMap += ` '/${v.name.toLowerCase()}': render${cap},\n`;
|
|
372
185
|
});
|
|
373
186
|
|
|
374
|
-
// Initialize layout call if configured
|
|
375
|
-
const layoutInit = this._layoutFnName
|
|
376
|
-
? `\n// Initialize default layout\nif (typeof ${this._layoutFnName} === 'function') {\n ${this._layoutFnName}();\n}\n`
|
|
377
|
-
: '';
|
|
378
|
-
|
|
379
187
|
return `
|
|
380
188
|
// --- JUX SOURCE LOADER ---
|
|
381
189
|
var __juxSources = null;
|
|
@@ -400,14 +208,6 @@ function __juxFindSource(stack) {
|
|
|
400
208
|
}
|
|
401
209
|
}
|
|
402
210
|
}
|
|
403
|
-
// Also check for layout
|
|
404
|
-
if (stack.indexOf('Layout') > -1) {
|
|
405
|
-
for (var file in __juxSources || {}) {
|
|
406
|
-
if (file.indexOf('layout') > -1) {
|
|
407
|
-
return { file: file, source: __juxSources[file] };
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
211
|
return null;
|
|
412
212
|
}
|
|
413
213
|
|
|
@@ -508,7 +308,7 @@ window.addEventListener('unhandledrejection', function(e) { __juxErrorOverlay.sh
|
|
|
508
308
|
|
|
509
309
|
// --- JUX ROUTER ---
|
|
510
310
|
const routes = {\n${routeMap}};
|
|
511
|
-
|
|
311
|
+
|
|
512
312
|
async function navigate(path) {
|
|
513
313
|
const view = routes[path];
|
|
514
314
|
if (!view) {
|
|
@@ -555,9 +355,6 @@ navigate(location.pathname);
|
|
|
555
355
|
const { views, dataModules, sharedModules } = this.scanFiles();
|
|
556
356
|
console.log(`📁 Found ${views.length} views, ${sharedModules.length} shared, ${dataModules.length} data`);
|
|
557
357
|
|
|
558
|
-
// Copy source assets (themes, layouts, assets)
|
|
559
|
-
this.copySourceAssets();
|
|
560
|
-
|
|
561
358
|
// Copy data/shared modules to dist
|
|
562
359
|
const juxDistDir = path.join(this.distDir, 'jux');
|
|
563
360
|
fs.mkdirSync(juxDistDir, { recursive: true });
|
|
@@ -602,7 +399,6 @@ navigate(location.pathname);
|
|
|
602
399
|
return { success: false, errors: [{ message: err.message }], warnings: [] };
|
|
603
400
|
}
|
|
604
401
|
|
|
605
|
-
// Generate HTML with layout container sibling to app
|
|
606
402
|
const html = `<!DOCTYPE html>
|
|
607
403
|
<html lang="en">
|
|
608
404
|
<head>
|
|
@@ -612,9 +408,7 @@ navigate(location.pathname);
|
|
|
612
408
|
<script type="module" src="./bundle.js"></script>
|
|
613
409
|
</head>
|
|
614
410
|
<body>
|
|
615
|
-
|
|
616
|
-
<div id="app"></div>
|
|
617
|
-
</div>
|
|
411
|
+
<div id="app"></div>
|
|
618
412
|
</body>
|
|
619
413
|
</html>`;
|
|
620
414
|
fs.writeFileSync(path.join(this.distDir, 'index.html'), html);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "juxscript",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A JavaScript UX authorship platform",
|
|
6
6
|
"main": "index.js",
|
|
@@ -19,14 +19,25 @@
|
|
|
19
19
|
"./package.json": "./package.json"
|
|
20
20
|
},
|
|
21
21
|
"files": [
|
|
22
|
-
"bin",
|
|
23
|
-
"lib",
|
|
24
|
-
"machinery",
|
|
25
22
|
"index.js",
|
|
26
23
|
"index.d.ts",
|
|
24
|
+
"bin",
|
|
25
|
+
"create/*.jux",
|
|
26
|
+
"lib/**/*.js",
|
|
27
|
+
"lib/**/*.d.ts",
|
|
28
|
+
"machinery/build3.js",
|
|
29
|
+
"machinery/compiler3.js",
|
|
30
|
+
"machinery/config.js",
|
|
31
|
+
"machinery/serve.js",
|
|
32
|
+
"machinery/watcher.js",
|
|
33
|
+
"types",
|
|
27
34
|
"juxconfig.example.js",
|
|
28
35
|
"README.md",
|
|
29
|
-
"LICENSE"
|
|
36
|
+
"LICENSE",
|
|
37
|
+
"!lib/**/*.test.js",
|
|
38
|
+
"!lib/**/*.spec.js",
|
|
39
|
+
"!lib/previews/**",
|
|
40
|
+
"!**/*.map"
|
|
30
41
|
],
|
|
31
42
|
"bin": {
|
|
32
43
|
"jux": "./bin/cli.js"
|
|
@@ -46,6 +57,7 @@
|
|
|
46
57
|
},
|
|
47
58
|
"dependencies": {
|
|
48
59
|
"acorn": "^8.15.0",
|
|
60
|
+
"astray": "^1.1.1",
|
|
49
61
|
"axios": "^1.6.0",
|
|
50
62
|
"chart.js": "^4.5.1",
|
|
51
63
|
"esbuild": "^0.19.0",
|
|
@@ -54,10 +66,12 @@
|
|
|
54
66
|
},
|
|
55
67
|
"devDependencies": {
|
|
56
68
|
"@types/express": "^4.17.17",
|
|
69
|
+
"@types/jsdom": "^27.0.0",
|
|
57
70
|
"@types/node": "^20.0.0",
|
|
58
71
|
"@types/ws": "^8.5.5",
|
|
59
72
|
"acorn-walk": "^8.3.4",
|
|
60
73
|
"jsdom": "^27.4.0",
|
|
74
|
+
"ts-node": "^10.9.2",
|
|
61
75
|
"typescript": "^5.0.0"
|
|
62
76
|
}
|
|
63
77
|
}
|
package/lib/components/alert.ts
DELETED
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
import { BaseComponent } from './base/BaseComponent.js';
|
|
2
|
-
import { renderIcon } from './icons.js';
|
|
3
|
-
|
|
4
|
-
// Event definitions
|
|
5
|
-
const TRIGGER_EVENTS = [] as const;
|
|
6
|
-
const CALLBACK_EVENTS = ['dismiss'] as const;
|
|
7
|
-
|
|
8
|
-
export interface AlertOptions {
|
|
9
|
-
content?: string;
|
|
10
|
-
type?: 'info' | 'success' | 'warning' | 'error';
|
|
11
|
-
dismissible?: boolean;
|
|
12
|
-
icon?: string;
|
|
13
|
-
style?: string;
|
|
14
|
-
class?: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
type AlertState = {
|
|
18
|
-
content: string;
|
|
19
|
-
type: string;
|
|
20
|
-
dismissible: boolean;
|
|
21
|
-
icon: string;
|
|
22
|
-
visible: boolean;
|
|
23
|
-
style: string;
|
|
24
|
-
class: string;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export class Alert extends BaseComponent<AlertState> {
|
|
28
|
-
private _alert: HTMLElement | null = null;
|
|
29
|
-
|
|
30
|
-
constructor(id: string, options: AlertOptions = {}) {
|
|
31
|
-
super(id, {
|
|
32
|
-
content: options.content ?? '',
|
|
33
|
-
type: options.type ?? 'info',
|
|
34
|
-
dismissible: options.dismissible ?? true,
|
|
35
|
-
icon: options.icon ?? '',
|
|
36
|
-
visible: true,
|
|
37
|
-
style: options.style ?? '',
|
|
38
|
-
class: options.class ?? ''
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
protected getTriggerEvents(): readonly string[] {
|
|
43
|
-
return TRIGGER_EVENTS;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
protected getCallbackEvents(): readonly string[] {
|
|
47
|
-
return CALLBACK_EVENTS;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/* ═════════════════════════════════════════════════════════════════
|
|
51
|
-
* FLUENT API
|
|
52
|
-
* ═════════════════════════════════════════════════════════════════ */
|
|
53
|
-
|
|
54
|
-
// ✅ Inherited from BaseComponent:
|
|
55
|
-
// - style(), class()
|
|
56
|
-
// - bind(), sync(), renderTo()
|
|
57
|
-
// - addClass(), removeClass(), toggleClass()
|
|
58
|
-
// - visible(), show(), hide(), toggleVisibility()
|
|
59
|
-
// - attr(), attrs(), removeAttr()
|
|
60
|
-
// - disabled(), enable(), disable()
|
|
61
|
-
// - loading(), focus(), blur(), remove()
|
|
62
|
-
|
|
63
|
-
content(value: string): this {
|
|
64
|
-
this.state.content = value;
|
|
65
|
-
return this;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
type(value: 'info' | 'success' | 'warning' | 'error'): this {
|
|
69
|
-
this.state.type = value;
|
|
70
|
-
return this;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
dismissible(value: boolean): this {
|
|
74
|
-
this.state.dismissible = value;
|
|
75
|
-
return this;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
icon(value: string): this {
|
|
79
|
-
this.state.icon = value;
|
|
80
|
-
return this;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/* ═════════════════════════════════════════════════════════════════
|
|
84
|
-
* RENDER
|
|
85
|
-
* ═════════════════════════════════════════════════════════════════ */
|
|
86
|
-
|
|
87
|
-
render(targetId?: string): this {
|
|
88
|
-
const container = this._setupContainer(targetId);
|
|
89
|
-
|
|
90
|
-
const { content, type, dismissible, icon, style, class: className } = this.state;
|
|
91
|
-
const hasVisibleSync = this._syncBindings.some(b => b.property === 'visible');
|
|
92
|
-
|
|
93
|
-
// Build alert element
|
|
94
|
-
const alert = document.createElement('div');
|
|
95
|
-
alert.className = `jux-alert jux-alert-${type}`;
|
|
96
|
-
alert.id = this._id;
|
|
97
|
-
if (className) alert.className += ` ${className}`;
|
|
98
|
-
if (style) alert.setAttribute('style', style);
|
|
99
|
-
|
|
100
|
-
if (icon) {
|
|
101
|
-
const iconEl = document.createElement('span');
|
|
102
|
-
iconEl.className = 'jux-alert-icon';
|
|
103
|
-
iconEl.appendChild(renderIcon(icon));
|
|
104
|
-
alert.appendChild(iconEl);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const contentEl = document.createElement('div');
|
|
108
|
-
contentEl.className = 'jux-alert-content';
|
|
109
|
-
contentEl.textContent = content;
|
|
110
|
-
alert.appendChild(contentEl);
|
|
111
|
-
|
|
112
|
-
if (dismissible) {
|
|
113
|
-
const closeBtn = document.createElement('button');
|
|
114
|
-
closeBtn.className = 'jux-alert-close';
|
|
115
|
-
closeBtn.innerHTML = '×';
|
|
116
|
-
alert.appendChild(closeBtn);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Default dismiss behavior (only if NOT using sync)
|
|
120
|
-
if (!hasVisibleSync && dismissible) {
|
|
121
|
-
const closeBtn = alert.querySelector('.jux-alert-close');
|
|
122
|
-
closeBtn?.addEventListener('click', () => {
|
|
123
|
-
// 🎯 Fire the dismiss callback event
|
|
124
|
-
this._triggerCallback('dismiss');
|
|
125
|
-
alert.remove();
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Wire events using inherited method
|
|
130
|
-
this._wireStandardEvents(alert);
|
|
131
|
-
|
|
132
|
-
// Wire sync bindings
|
|
133
|
-
this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
|
|
134
|
-
if (property === 'content') {
|
|
135
|
-
const transform = toComponent || ((v: any) => String(v));
|
|
136
|
-
|
|
137
|
-
stateObj.subscribe((val: any) => {
|
|
138
|
-
const transformed = transform(val);
|
|
139
|
-
contentEl.textContent = transformed;
|
|
140
|
-
this.state.content = transformed;
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
else if (property === 'type') {
|
|
144
|
-
const transform = toComponent || ((v: any) => String(v));
|
|
145
|
-
|
|
146
|
-
stateObj.subscribe((val: any) => {
|
|
147
|
-
const transformed = transform(val);
|
|
148
|
-
alert.className = `jux-alert jux-alert-${transformed}`;
|
|
149
|
-
if (className) alert.className += ` ${className}`;
|
|
150
|
-
this.state.type = transformed;
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
else if (property === 'visible') {
|
|
154
|
-
const transformToState = toState || ((v: any) => Boolean(v));
|
|
155
|
-
const transform = toComponent || ((v: any) => Boolean(v));
|
|
156
|
-
|
|
157
|
-
let isUpdating = false;
|
|
158
|
-
|
|
159
|
-
// State → Component
|
|
160
|
-
stateObj.subscribe((val: any) => {
|
|
161
|
-
if (isUpdating) return;
|
|
162
|
-
const transformed = transform(val);
|
|
163
|
-
alert.style.display = transformed ? 'flex' : 'none';
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
// Component → State (close button)
|
|
167
|
-
if (dismissible) {
|
|
168
|
-
const closeBtn = alert.querySelector('.jux-alert-close');
|
|
169
|
-
closeBtn?.addEventListener('click', () => {
|
|
170
|
-
if (isUpdating) return;
|
|
171
|
-
isUpdating = true;
|
|
172
|
-
|
|
173
|
-
alert.style.display = 'none';
|
|
174
|
-
stateObj.set(transformToState(false));
|
|
175
|
-
|
|
176
|
-
// 🎯 Fire the dismiss callback event
|
|
177
|
-
this._triggerCallback('dismiss');
|
|
178
|
-
|
|
179
|
-
setTimeout(() => { isUpdating = false; }, 0);
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
container.appendChild(alert);
|
|
186
|
-
this._alert = alert;
|
|
187
|
-
|
|
188
|
-
requestAnimationFrame(() => {
|
|
189
|
-
if ((window as any).lucide) {
|
|
190
|
-
(window as any).lucide.createIcons();
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
return this;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
export function alert(id: string, options: AlertOptions = {}): Alert {
|
|
199
|
-
return new Alert(id, options);
|
|
200
|
-
}
|