juxscript 1.0.7 → 1.0.9
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/README.md +12 -4
- package/bin/cli.js +66 -38
- package/lib/components/divider.ts +233 -0
- package/lib/components/docs-data.json +82 -1
- package/lib/components/hero1/hero1.ts +196 -0
- package/lib/components/hero1/index.js +4 -0
- package/lib/components/kpicard.ts +209 -70
- package/lib/components/sidebar.ts +13 -5
- package/lib/jux.ts +8 -4
- package/lib/presets/global.css +0 -1
- package/machinery/compiler.js +21 -4
- package/machinery/server.js +19 -16
- package/package.json +32 -52
- package/machinery/generators/css.js +0 -128
package/machinery/server.js
CHANGED
|
@@ -12,7 +12,7 @@ const __dirname = path.dirname(__filename);
|
|
|
12
12
|
|
|
13
13
|
let db = null;
|
|
14
14
|
|
|
15
|
-
async function serve(port = 3000, distDir = './jux-dist') {
|
|
15
|
+
async function serve(port = 3000, distDir = './jux-dist') {
|
|
16
16
|
const app = express();
|
|
17
17
|
const absoluteDistDir = path.resolve(distDir);
|
|
18
18
|
const projectRoot = path.resolve('.');
|
|
@@ -29,14 +29,14 @@ async function serve(port = 3000, distDir = './jux-dist') { // Changed default
|
|
|
29
29
|
app.post('/api/query', async (req, res) => {
|
|
30
30
|
try {
|
|
31
31
|
const { sql, params = [] } = req.body;
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
if (!db) {
|
|
34
34
|
return res.status(500).json({ error: 'Database not initialized' });
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
const stmt = db.prepare(sql);
|
|
38
38
|
stmt.bind(params);
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
const rows = [];
|
|
41
41
|
while (stmt.step()) {
|
|
42
42
|
rows.push(stmt.getAsObject());
|
|
@@ -61,16 +61,20 @@ async function serve(port = 3000, distDir = './jux-dist') { // Changed default
|
|
|
61
61
|
// Serve HTML files with clean URLs
|
|
62
62
|
const heyPath = path.join(absoluteDistDir, 'hey.html');
|
|
63
63
|
const indexPath = path.join(absoluteDistDir, 'index.html');
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
app.use((req, res, next) => {
|
|
66
|
-
let requestPath = req.path.endsWith('/') && req.path.length > 1
|
|
67
|
-
? req.path.slice(0, -1)
|
|
66
|
+
let requestPath = req.path.endsWith('/') && req.path.length > 1
|
|
67
|
+
? req.path.slice(0, -1)
|
|
68
68
|
: req.path;
|
|
69
69
|
|
|
70
70
|
// Root path - serve hey.html or index.html
|
|
71
71
|
if (requestPath === '/') {
|
|
72
|
-
if (fs.existsSync(heyPath))
|
|
73
|
-
|
|
72
|
+
if (fs.existsSync(heyPath)) {
|
|
73
|
+
return res.sendFile(heyPath);
|
|
74
|
+
}
|
|
75
|
+
if (fs.existsSync(indexPath)) {
|
|
76
|
+
return res.sendFile(indexPath);
|
|
77
|
+
}
|
|
74
78
|
}
|
|
75
79
|
|
|
76
80
|
// Try to serve as HTML file
|
|
@@ -87,7 +91,7 @@ async function serve(port = 3000, distDir = './jux-dist') { // Changed default
|
|
|
87
91
|
|
|
88
92
|
next();
|
|
89
93
|
});
|
|
90
|
-
|
|
94
|
+
|
|
91
95
|
// Serve static files (CSS, JS, images, etc.)
|
|
92
96
|
app.use(express.static(absoluteDistDir));
|
|
93
97
|
|
|
@@ -96,25 +100,24 @@ async function serve(port = 3000, distDir = './jux-dist') { // Changed default
|
|
|
96
100
|
const notFoundPath = path.join(absoluteDistDir, '404.html');
|
|
97
101
|
const requestedPath = path.join(absoluteDistDir, req.path);
|
|
98
102
|
const fileType = path.extname(req.path) || 'directory';
|
|
99
|
-
|
|
103
|
+
|
|
100
104
|
// Log to console for debugging
|
|
101
105
|
console.log(`❌ 404: ${req.path}`);
|
|
102
106
|
console.log(` Looked for: ${requestedPath}`);
|
|
103
107
|
console.log(` Type: ${fileType}`);
|
|
104
108
|
console.log(` Referer: ${req.get('referer') || 'direct'}`);
|
|
105
|
-
|
|
109
|
+
|
|
106
110
|
// If custom 404.html exists and this isn't already /404
|
|
107
111
|
if (fs.existsSync(notFoundPath) && req.path !== '/404') {
|
|
108
|
-
// Add debug info as query params
|
|
109
112
|
const debugUrl = `/404?path=${encodeURIComponent(req.path)}&type=${fileType}&from=${encodeURIComponent(req.get('referer') || 'direct')}`;
|
|
110
113
|
return res.redirect(debugUrl);
|
|
111
114
|
}
|
|
112
|
-
|
|
115
|
+
|
|
113
116
|
// Serve custom 404 page
|
|
114
117
|
if (fs.existsSync(notFoundPath)) {
|
|
115
118
|
return res.status(404).sendFile(notFoundPath);
|
|
116
119
|
}
|
|
117
|
-
|
|
120
|
+
|
|
118
121
|
// Fallback: minimal 404 response
|
|
119
122
|
res.status(404).send('<h1>404 - Not Found</h1>');
|
|
120
123
|
});
|
|
@@ -175,7 +178,7 @@ async function serve(port = 3000, distDir = './jux-dist') { // Changed default
|
|
|
175
178
|
async function initDatabase() {
|
|
176
179
|
const SQL = await initSqlJs();
|
|
177
180
|
const dbPath = path.join(__dirname, '../db/jux.db');
|
|
178
|
-
|
|
181
|
+
|
|
179
182
|
if (fs.existsSync(dbPath)) {
|
|
180
183
|
const buffer = fs.readFileSync(dbPath);
|
|
181
184
|
db = new SQL.Database(buffer);
|
|
@@ -188,5 +191,5 @@ async function initDatabase() {
|
|
|
188
191
|
|
|
189
192
|
export async function start(port = 3000) {
|
|
190
193
|
await initDatabase();
|
|
191
|
-
return serve(port, './jux-dist');
|
|
194
|
+
return serve(port, './jux-dist');
|
|
192
195
|
}
|
package/package.json
CHANGED
|
@@ -1,84 +1,64 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "juxscript",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A JavaScript UX authorship platform",
|
|
6
6
|
"main": "lib/jux.js",
|
|
7
7
|
"types": "lib/jux.d.ts",
|
|
8
|
+
"access": "public",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/jux/juxscript.git"
|
|
12
|
+
},
|
|
8
13
|
"exports": {
|
|
9
14
|
".": {
|
|
10
15
|
"types": "./lib/jux.d.ts",
|
|
11
16
|
"import": "./lib/jux.js",
|
|
12
17
|
"default": "./lib/jux.js"
|
|
13
18
|
},
|
|
14
|
-
"./
|
|
15
|
-
|
|
19
|
+
"./reactivity": {
|
|
20
|
+
"types": "./lib/reactivity/index.d.ts",
|
|
21
|
+
"import": "./lib/reactivity/index.js",
|
|
22
|
+
"default": "./lib/reactivity/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./components/*": "./lib/components/*/index.js",
|
|
25
|
+
"./presets/*": "./lib/presets/*.js",
|
|
16
26
|
"./package.json": "./package.json"
|
|
17
27
|
},
|
|
18
|
-
"typesVersions": {
|
|
19
|
-
"*": {
|
|
20
|
-
"*": [
|
|
21
|
-
"lib/*"
|
|
22
|
-
],
|
|
23
|
-
"lib/*": [
|
|
24
|
-
"lib/*"
|
|
25
|
-
],
|
|
26
|
-
"lib/components/*": [
|
|
27
|
-
"lib/components/*"
|
|
28
|
-
]
|
|
29
|
-
}
|
|
30
|
-
},
|
|
31
|
-
"bin": {
|
|
32
|
-
"jux": "./bin/cli.js"
|
|
33
|
-
},
|
|
34
|
-
"scripts": {
|
|
35
|
-
"dev": "cd examples && npx jux serve",
|
|
36
|
-
"build:examples": "cd examples && rm -rf dist && npx jux build",
|
|
37
|
-
"build": "tsc",
|
|
38
|
-
"test": "node test/run-tests.js",
|
|
39
|
-
"generate:icons": "node scripts/generate-icon-types.js"
|
|
40
|
-
},
|
|
41
28
|
"files": [
|
|
42
29
|
"lib",
|
|
43
30
|
"bin",
|
|
44
31
|
"machinery",
|
|
45
32
|
"types",
|
|
46
|
-
"lib/**/*.d.ts",
|
|
47
33
|
"README.md",
|
|
48
34
|
"LICENSE"
|
|
49
35
|
],
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
},
|
|
53
|
-
"repository": {
|
|
54
|
-
"type": "git",
|
|
55
|
-
"url": "https://github.com/juxscript/jux.git"
|
|
36
|
+
"bin": {
|
|
37
|
+
"jux": "./bin/cli.js"
|
|
56
38
|
},
|
|
57
39
|
"keywords": [
|
|
58
|
-
"jux",
|
|
59
40
|
"ui",
|
|
60
|
-
"
|
|
41
|
+
"ux",
|
|
42
|
+
"components",
|
|
43
|
+
"framework",
|
|
44
|
+
"reactive",
|
|
61
45
|
"javascript"
|
|
62
46
|
],
|
|
63
|
-
"
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
"
|
|
67
|
-
"chokidar": "^5.0.0",
|
|
68
|
-
"clean-css": "^5.3.3",
|
|
69
|
-
"esbuild": "^0.27.2",
|
|
70
|
-
"express": "^5.2.1",
|
|
71
|
-
"glob": "^13.0.0",
|
|
72
|
-
"node": "^24.12.0",
|
|
73
|
-
"sql.js": "^1.10.3",
|
|
74
|
-
"terser": "^5.44.1",
|
|
75
|
-
"ws": "^8.19.0"
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsc",
|
|
49
|
+
"dev": "tsc --watch",
|
|
50
|
+
"prepublishOnly": "npm run build"
|
|
76
51
|
},
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"express": "^4.18.2",
|
|
54
|
+
"chokidar": "^3.5.3",
|
|
55
|
+
"ws": "^8.13.0",
|
|
56
|
+
"sql.js": "^1.8.0"
|
|
80
57
|
},
|
|
81
58
|
"devDependencies": {
|
|
82
|
-
"typescript": "^5.
|
|
59
|
+
"typescript": "^5.0.0",
|
|
60
|
+
"@types/express": "^4.17.17",
|
|
61
|
+
"@types/node": "^20.0.0",
|
|
62
|
+
"@types/ws": "^8.5.5"
|
|
83
63
|
}
|
|
84
64
|
}
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
|
-
import CleanCSS from 'clean-css';
|
|
5
|
-
import { FileValidator } from '../validators/file-validator.js';
|
|
6
|
-
|
|
7
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
-
const __dirname = path.dirname(__filename);
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Generates CSS content from Jux configuration
|
|
12
|
-
* Handles: global.css, themes, imports, styleImports, and styleInline blocks
|
|
13
|
-
*/
|
|
14
|
-
export function generateCSS(juxConfig, layoutParsed = null) {
|
|
15
|
-
const fileValidator = new FileValidator();
|
|
16
|
-
let cssOutput = '';
|
|
17
|
-
|
|
18
|
-
// 1. Add global.css (always first)
|
|
19
|
-
const globalCssPath = path.join(__dirname, '../../lib/global.css');
|
|
20
|
-
if (fs.existsSync(globalCssPath)) {
|
|
21
|
-
cssOutput += `/* Global Styles */\n`;
|
|
22
|
-
cssOutput += fs.readFileSync(globalCssPath, 'utf-8') + '\n\n';
|
|
23
|
-
console.log(` ✓ Included global.css`);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// 2. Add theme CSS (layout theme first, then page theme if different)
|
|
27
|
-
const themesToLoad = [];
|
|
28
|
-
|
|
29
|
-
if (layoutParsed?.config?.theme) {
|
|
30
|
-
themesToLoad.push({ theme: layoutParsed.config.theme, source: 'layout' });
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (juxConfig.theme && juxConfig.theme !== layoutParsed?.config?.theme) {
|
|
34
|
-
themesToLoad.push({ theme: juxConfig.theme, source: 'page' });
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
for (const { theme, source } of themesToLoad) {
|
|
38
|
-
const themePath = path.join(__dirname, '../../lib/themes', `${theme}.css`);
|
|
39
|
-
if (fs.existsSync(themePath)) {
|
|
40
|
-
cssOutput += `/* Theme: ${theme} (${source}) */\n`;
|
|
41
|
-
cssOutput += fs.readFileSync(themePath, 'utf-8') + '\n\n';
|
|
42
|
-
console.log(` ✓ Included theme: ${theme} (${source})`);
|
|
43
|
-
} else {
|
|
44
|
-
console.warn(` ⚠️ Theme not found: ${theme}`);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// 3. Process @import directives (CSS files only from layout, then page)
|
|
49
|
-
const allImports = [
|
|
50
|
-
...(layoutParsed?.config?.import || []),
|
|
51
|
-
...(juxConfig.import || [])
|
|
52
|
-
];
|
|
53
|
-
|
|
54
|
-
if (allImports.length > 0) {
|
|
55
|
-
const { categorized } = fileValidator.categorizeImports(allImports);
|
|
56
|
-
|
|
57
|
-
// Only process CSS files
|
|
58
|
-
for (const cssImport of categorized.css) {
|
|
59
|
-
const resolvedPath = path.join(__dirname, '../../', cssImport);
|
|
60
|
-
if (fs.existsSync(resolvedPath)) {
|
|
61
|
-
cssOutput += `/* Import: ${cssImport} */\n`;
|
|
62
|
-
cssOutput += fs.readFileSync(resolvedPath, 'utf-8') + '\n\n';
|
|
63
|
-
console.log(` ✓ Included import: ${cssImport}`);
|
|
64
|
-
} else {
|
|
65
|
-
console.warn(` ⚠️ Import not found: ${cssImport} (resolved to ${resolvedPath})`);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Log skipped JS imports (will be handled in HTML)
|
|
70
|
-
if (categorized.js.length > 0) {
|
|
71
|
-
console.log(` ℹ️ Skipped JS imports (will be added to HTML): ${categorized.js.length} file(s)`);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// 4. Process @style imports (CSS file references from layout, then page)
|
|
76
|
-
const allStyleImports = [
|
|
77
|
-
...(layoutParsed?.config?.styleImports || []),
|
|
78
|
-
...(juxConfig.styleImports || [])
|
|
79
|
-
];
|
|
80
|
-
|
|
81
|
-
for (const styleImport of allStyleImports) {
|
|
82
|
-
// Handle URLs (CDN)
|
|
83
|
-
if (styleImport.startsWith('http://') || styleImport.startsWith('https://')) {
|
|
84
|
-
cssOutput += `/* External CSS: ${styleImport} */\n`;
|
|
85
|
-
cssOutput += `@import url('${styleImport}');\n\n`;
|
|
86
|
-
console.log(` ✓ Added CDN import: ${styleImport}`);
|
|
87
|
-
} else {
|
|
88
|
-
// Handle local files
|
|
89
|
-
const resolvedPath = path.join(__dirname, '../../', styleImport);
|
|
90
|
-
if (fs.existsSync(resolvedPath)) {
|
|
91
|
-
cssOutput += `/* Style Import: ${styleImport} */\n`;
|
|
92
|
-
cssOutput += fs.readFileSync(resolvedPath, 'utf-8') + '\n\n';
|
|
93
|
-
console.log(` ✓ Included style import: ${styleImport}`);
|
|
94
|
-
} else {
|
|
95
|
-
console.warn(` ⚠️ Style import not found: ${styleImport} (resolved to ${resolvedPath})`);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// 5. Process inline @style blocks (layout first, then page)
|
|
101
|
-
const allInlineStyles = [
|
|
102
|
-
...(layoutParsed?.config?.styleInline || []),
|
|
103
|
-
...(juxConfig.styleInline || [])
|
|
104
|
-
];
|
|
105
|
-
|
|
106
|
-
if (allInlineStyles.length > 0) {
|
|
107
|
-
cssOutput += `/* Inline Styles (${allInlineStyles.length} block(s)) */\n`;
|
|
108
|
-
|
|
109
|
-
allInlineStyles.forEach((styleBlock, index) => {
|
|
110
|
-
const source = index < (layoutParsed?.config?.styleInline?.length || 0) ? 'layout' : 'page';
|
|
111
|
-
|
|
112
|
-
try {
|
|
113
|
-
const validatedStyle = fileValidator.validateStyleContent(styleBlock, `inline block #${index + 1}`);
|
|
114
|
-
|
|
115
|
-
if (!fileValidator.isEmptyStyle(validatedStyle)) {
|
|
116
|
-
cssOutput += `/* Inline Block #${index + 1} (${source}) */\n`;
|
|
117
|
-
cssOutput += validatedStyle + '\n\n';
|
|
118
|
-
}
|
|
119
|
-
} catch (error) {
|
|
120
|
-
console.error(` ❌ ${error.message}`);
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
console.log(` ✓ Included ${allInlineStyles.length} inline style block(s)`);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return cssOutput.trim();
|
|
128
|
-
}
|