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.
@@ -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') { // Changed default
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)) return res.sendFile(heyPath);
73
- if (fs.existsSync(indexPath)) return res.sendFile(indexPath);
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'); // Changed default
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.7",
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
- "./lib/*": "./lib/*",
15
- "./lib/components/*": "./lib/components/*",
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
- "publishConfig": {
51
- "access": "public"
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
- "authoring",
41
+ "ux",
42
+ "components",
43
+ "framework",
44
+ "reactive",
61
45
  "javascript"
62
46
  ],
63
- "author": "Tim Kerr",
64
- "license": "MIT",
65
- "dependencies": {
66
- "acorn": "^8.15.0",
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
- "optionalDependencies": {
78
- "mysql2": "^3.6.5",
79
- "pg": "^8.11.3"
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.9.3"
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
- }