juxscript 1.0.20 → 1.0.22

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.
Files changed (78) hide show
  1. package/bin/cli.js +166 -114
  2. package/lib/components/alert.ts +143 -92
  3. package/lib/components/badge.ts +93 -94
  4. package/lib/components/base/BaseComponent.ts +397 -0
  5. package/lib/components/base/FormInput.ts +322 -0
  6. package/lib/components/button.ts +40 -131
  7. package/lib/components/card.ts +57 -79
  8. package/lib/components/charts/areachart.ts +315 -0
  9. package/lib/components/charts/barchart.ts +421 -0
  10. package/lib/components/charts/doughnutchart.ts +263 -0
  11. package/lib/components/charts/lib/BaseChart.ts +402 -0
  12. package/lib/components/{chart-types.ts → charts/lib/chart-types.ts} +1 -1
  13. package/lib/components/{chart-utils.ts → charts/lib/chart-utils.ts} +1 -1
  14. package/lib/components/{chart.ts → charts/lib/chart.ts} +3 -3
  15. package/lib/components/checkbox.ts +255 -204
  16. package/lib/components/code.ts +31 -78
  17. package/lib/components/container.ts +113 -130
  18. package/lib/components/data.ts +37 -5
  19. package/lib/components/datepicker.ts +180 -147
  20. package/lib/components/dialog.ts +218 -221
  21. package/lib/components/divider.ts +63 -87
  22. package/lib/components/docs-data.json +498 -2404
  23. package/lib/components/dropdown.ts +191 -236
  24. package/lib/components/element.ts +196 -145
  25. package/lib/components/fileupload.ts +253 -167
  26. package/lib/components/guard.ts +92 -0
  27. package/lib/components/heading.ts +31 -97
  28. package/lib/components/helpers.ts +13 -6
  29. package/lib/components/hero.ts +51 -114
  30. package/lib/components/icon.ts +33 -120
  31. package/lib/components/icons.ts +2 -1
  32. package/lib/components/include.ts +76 -3
  33. package/lib/components/input.ts +155 -407
  34. package/lib/components/kpicard.ts +16 -16
  35. package/lib/components/list.ts +358 -261
  36. package/lib/components/loading.ts +142 -211
  37. package/lib/components/menu.ts +63 -152
  38. package/lib/components/modal.ts +42 -129
  39. package/lib/components/nav.ts +79 -101
  40. package/lib/components/paragraph.ts +38 -102
  41. package/lib/components/progress.ts +108 -166
  42. package/lib/components/radio.ts +283 -234
  43. package/lib/components/script.ts +19 -87
  44. package/lib/components/select.ts +189 -199
  45. package/lib/components/sidebar.ts +110 -141
  46. package/lib/components/style.ts +19 -82
  47. package/lib/components/switch.ts +254 -183
  48. package/lib/components/table.ts +1078 -208
  49. package/lib/components/tabs.ts +42 -106
  50. package/lib/components/theme-toggle.ts +73 -165
  51. package/lib/components/tooltip.ts +85 -316
  52. package/lib/components/write.ts +108 -127
  53. package/lib/jux.ts +67 -41
  54. package/machinery/build.js +466 -0
  55. package/machinery/compiler.js +354 -105
  56. package/machinery/server.js +50 -103
  57. package/machinery/watcher.js +153 -130
  58. package/package.json +1 -1
  59. package/presets/hey.jux +46 -0
  60. package/presets/styles/base.css +1380 -0
  61. package/presets/styles/notion.css +127 -0
  62. package/lib/adapters/base-adapter.js +0 -35
  63. package/lib/adapters/index.js +0 -33
  64. package/lib/adapters/mysql-adapter.js +0 -65
  65. package/lib/adapters/postgres-adapter.js +0 -70
  66. package/lib/adapters/sqlite-adapter.js +0 -56
  67. package/lib/components/areachart.ts +0 -1128
  68. package/lib/components/areachartsmooth.ts +0 -1380
  69. package/lib/components/barchart.ts +0 -1322
  70. package/lib/components/doughnutchart.ts +0 -1259
  71. package/lib/components/footer.ts +0 -165
  72. package/lib/components/header.ts +0 -187
  73. package/lib/components/layout.ts +0 -239
  74. package/lib/components/main.ts +0 -137
  75. package/lib/layouts/default.jux +0 -8
  76. package/lib/layouts/figma.jux +0 -0
  77. package/presets/notion.css +0 -2100
  78. /package/lib/{themes → components/charts/lib}/charts.js +0 -0
package/bin/cli.js CHANGED
@@ -1,6 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { compileJuxFile, copyLibToOutput, copyProjectAssets, transpileProjectTypeScript, copyPresetsToOutput } from '../machinery/compiler.js';
3
+ import {
4
+ copyLibToOutput,
5
+ copyProjectAssets,
6
+ transpileProjectTypeScript,
7
+ copyPresetsToOutput,
8
+ bundleJuxFilesToRouter,
9
+ generateIndexHtml
10
+ } from '../machinery/compiler.js';
4
11
  import { generateDocs } from '../machinery/doc-generator.js';
5
12
  import { start } from '../machinery/server.js';
6
13
  import path from 'path';
@@ -42,13 +49,12 @@ console.log(` Output: ${PATHS.frontendDist}`);
42
49
  console.log(` Lib: ${PATHS.juxLib}\n`);
43
50
 
44
51
  const command = process.argv[2];
52
+ const subCommand = process.argv[3]; // For serve <pagename>
53
+ const watchMode = process.argv.includes('--watch');
54
+ const bundleMode = process.argv.includes('--bundle');
45
55
 
46
56
  /**
47
57
  * Recursively find .jux files in a directory
48
- *
49
- * @param {string} dir - Directory to search
50
- * @param {string[]} fileList - Accumulator for found files
51
- * @returns {string[]} Array of .jux file paths
52
58
  */
53
59
  function findJuxFiles(dir, fileList = []) {
54
60
  if (!fs.existsSync(dir)) return fileList;
@@ -72,11 +78,12 @@ function findJuxFiles(dir, fileList = []) {
72
78
  }
73
79
 
74
80
  /**
75
- * Build the entire JUX project
81
+ * Build the entire JUX project (ALWAYS uses router bundle)
76
82
  *
77
- * @param {boolean} isServe - Whether building for dev server with hot reload
83
+ * @param {boolean} isServe - Whether building for dev server
78
84
  */
79
85
  async function buildProject(isServe = false) {
86
+ const buildStartTime = performance.now();
80
87
  console.log('🔨 Building JUX frontend...\n');
81
88
 
82
89
  try {
@@ -93,80 +100,121 @@ async function buildProject(isServe = false) {
93
100
  }
94
101
  fs.mkdirSync(PATHS.frontendDist, { recursive: true });
95
102
 
96
- // Step 1: Generate documentation from jux lib
103
+ // Step 1: Generate documentation
104
+ const docsStartTime = performance.now();
105
+ let docsTime = 0; // ✅ Declare with default value
97
106
  console.log('📚 Generating documentation...');
98
107
  try {
99
108
  await generateDocs(PATHS.juxLib);
100
- console.log('✅ Documentation generated\n');
109
+ docsTime = performance.now() - docsStartTime;
110
+ console.log(`✅ Documentation generated (${docsTime.toFixed(0)}ms)\n`);
101
111
  } catch (error) {
102
- console.warn('⚠️ Failed to generate docs:', error.message);
112
+ docsTime = performance.now() - docsStartTime; // ✅ Still calculate time even on error
113
+ console.warn(`⚠️ Failed to generate docs (${docsTime.toFixed(0)}ms):`, error.message);
103
114
  }
104
115
 
105
116
  // Step 2: Copy jux lib to frontend dist
117
+ const libStartTime = performance.now();
106
118
  await copyLibToOutput(PATHS.juxLib, PATHS.frontendDist);
119
+ const libTime = performance.now() - libStartTime;
120
+ console.log(`⏱️ Lib copy time: ${libTime.toFixed(0)}ms\n`);
107
121
 
108
- // Step 2.5: Copy presets folder
122
+ // Step 3: Copy presets folder
123
+ const presetsStartTime = performance.now();
109
124
  await copyPresetsToOutput(PATHS.packageRoot, PATHS.frontendDist);
125
+ const presetsTime = performance.now() - presetsStartTime;
126
+ console.log(`⏱️ Presets copy time: ${presetsTime.toFixed(0)}ms\n`);
110
127
 
111
- // Step 3: Copy project assets from jux/ (CSS, JS, images)
128
+ // Step 4: Copy project assets (CSS, JS, images)
129
+ const assetsStartTime = performance.now();
112
130
  await copyProjectAssets(PATHS.juxSource, PATHS.frontendDist);
131
+ const assetsTime = performance.now() - assetsStartTime;
132
+ console.log(`⏱️ Assets copy time: ${assetsTime.toFixed(0)}ms\n`);
113
133
 
114
- // Step 4: Transpile TypeScript files from jux/ to jux-dist/
134
+ // Step 5: Transpile TypeScript files
135
+ const tsStartTime = performance.now();
115
136
  await transpileProjectTypeScript(PATHS.juxSource, PATHS.frontendDist);
137
+ const tsTime = performance.now() - tsStartTime;
138
+ console.log(`⏱️ TypeScript transpile time: ${tsTime.toFixed(0)}ms\n`);
116
139
 
117
- // Step 5: Compile .jux files from jux/ directory ONLY
140
+ // Step 6: Bundle all .jux files into router
118
141
  const projectJuxFiles = findJuxFiles(PATHS.juxSource);
119
142
  console.log(`📝 Found ${projectJuxFiles.length} .jux file(s) in /jux\n`);
120
143
 
121
- for (const file of projectJuxFiles) {
122
- try {
123
- await compileJuxFile(file, {
124
- distDir: PATHS.frontendDist,
125
- projectRoot: PATHS.juxSource,
126
- isServe
127
- });
128
- } catch (err) {
129
- console.error(`Error compiling ${file}:`, err.message);
130
- }
144
+ if (projectJuxFiles.length === 0) {
145
+ console.warn('⚠️ No .jux files found to bundle');
146
+ process.exit(1);
131
147
  }
132
148
 
133
- // Step 6: Compile vendor layouts
134
- const layoutsDir = path.join(PATHS.juxLib, 'layouts');
135
-
136
- if (fs.existsSync(layoutsDir)) {
137
- console.log('\n📐 Compiling vendor layouts...');
138
- const vendorJuxFiles = findJuxFiles(layoutsDir);
139
- console.log(`Found ${vendorJuxFiles.length} vendor layout(s)\n`);
140
-
141
- for (const file of vendorJuxFiles) {
142
- try {
143
- const relPath = path.relative(PATHS.juxLib, file);
144
-
145
- await compileJuxFile(file, {
146
- distDir: path.join(PATHS.frontendDist, 'lib'),
147
- projectRoot: PATHS.juxLib,
148
- isServe
149
- });
150
-
151
- console.log(` ✓ Compiled: ${relPath}`);
152
- } catch (err) {
153
- console.error(`Error compiling vendor layout ${file}:`, err.message);
154
- }
155
- }
149
+ // Bundle and get the generated filename
150
+ const bundleStartTime = performance.now();
151
+ const mainJsFilename = await bundleJuxFilesToRouter(PATHS.juxSource, PATHS.frontendDist, {
152
+ routePrefix: ''
153
+ });
154
+ const bundleTime = performance.now() - bundleStartTime;
155
+
156
+ // Generate routes for index.html
157
+ const routes = projectJuxFiles.map(juxFile => {
158
+ const relativePath = path.relative(PATHS.juxSource, juxFile);
159
+ const parsedPath = path.parse(relativePath);
160
+
161
+ const rawFunctionName = parsedPath.dir
162
+ ? `${parsedPath.dir.replace(/\//g, '_')}_${parsedPath.name}`
163
+ : parsedPath.name;
164
+
165
+ const functionName = rawFunctionName
166
+ .replace(/[-_]/g, ' ')
167
+ .split(' ')
168
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
169
+ .join('');
170
+
171
+ const routePath = '/' + (parsedPath.dir ? `${parsedPath.dir}/` : '') + parsedPath.name;
172
+
173
+ return {
174
+ path: routePath.replace(/\/+/g, '/'),
175
+ functionName
176
+ };
177
+ });
178
+
179
+ // ✅ Generate unified index.html
180
+ const indexStartTime = performance.now();
181
+ generateIndexHtml(PATHS.frontendDist, routes, mainJsFilename);
182
+ const indexTime = performance.now() - indexStartTime;
183
+
184
+ const totalBuildTime = performance.now() - buildStartTime;
185
+
186
+ console.log(`\n✅ Bundled ${projectJuxFiles.length} page(s) → ${PATHS.frontendDist}/${mainJsFilename}\n`);
187
+
188
+ // ✅ Build summary with timing breakdown
189
+ console.log(`📊 Build Summary:`);
190
+ console.log(` ━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
191
+ console.log(` Documentation: ${docsTime.toFixed(0)}ms`);
192
+ console.log(` Library copy: ${libTime.toFixed(0)}ms`);
193
+ console.log(` Presets copy: ${presetsTime.toFixed(0)}ms`);
194
+ console.log(` Assets copy: ${assetsTime.toFixed(0)}ms`);
195
+ console.log(` TypeScript: ${tsTime.toFixed(0)}ms`);
196
+ console.log(` Router bundle: ${bundleTime.toFixed(0)}ms`);
197
+ console.log(` Index generation: ${indexTime.toFixed(0)}ms`);
198
+ console.log(` ━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
199
+ console.log(` Total build time: ${totalBuildTime.toFixed(0)}ms\n`);
200
+
201
+ // Show usage
202
+ if (!isServe) {
203
+ console.log('📦 Serve from your backend:');
204
+ console.log(` Express: app.use(express.static('jux-dist'))`);
205
+ console.log(` Flask: app = Flask(__name__, static_folder='jux-dist')`);
206
+ console.log(` FastAPI: app.mount("/", StaticFiles(directory="jux-dist"), name="static")`);
207
+ console.log('');
208
+ console.log('📍 Available routes:');
209
+ routes.forEach(r => {
210
+ console.log(` ${r.path}`);
211
+ });
212
+ console.log('');
156
213
  }
157
214
 
158
- console.log(`\n✅ Built ${projectJuxFiles.length} file(s) → ${PATHS.frontendDist}\n`);
159
-
160
- // Show backend integration examples
161
- console.log('📦 Serve from your backend:');
162
- console.log(` Express: app.use(express.static('jux-dist'))`);
163
- console.log(` Flask: app = Flask(__name__, static_folder='jux-dist')`);
164
- console.log(` FastAPI: app.mount("/", StaticFiles(directory="jux-dist"), name="static")`);
165
- console.log(` Laravel: Route::view('/', 'jux-dist/index.html')`);
166
- console.log('');
167
-
168
215
  } catch (err) {
169
- console.error('❌ Build error:', err.message);
216
+ const failTime = performance.now() - buildStartTime;
217
+ console.error(`❌ Build error after ${failTime.toFixed(0)}ms:`, err.message);
170
218
  console.error(err.stack);
171
219
  process.exit(1);
172
220
  }
@@ -186,41 +234,34 @@ async function buildProject(isServe = false) {
186
234
  // Create structure
187
235
  fs.mkdirSync(juxDir, { recursive: true });
188
236
 
189
- // Create index.jux with proper imports
190
- const indexContent = `// Welcome to JUX!
191
- import { jux } from 'juxscript';
192
-
193
- // Apply a layout preset (optional)
194
- // import 'juxscript/presets/notion.js';
195
-
196
- // Create your app structure
197
- jux.container('app-container')
198
- .direction('column')
199
- .gap(20)
200
- .style('padding: 40px;')
201
- .render('body');
202
-
203
- jux.hero('welcome-hero', {
204
- title: 'Welcome to JUX',
205
- subtitle: 'A JavaScript UX authorship platform'
206
- }).render('#app-container');
207
-
208
- jux.divider({}).render('#app-container');
209
-
210
- jux.write(\`
211
- <h2>Getting Started</h2>
212
- <p>Edit <code>jux/index.jux</code> to build your app.</p>
213
- <ul>
214
- <li>Run <code>npx jux build</code> to compile</li>
215
- <li>Run <code>npx jux serve</code> for dev mode</li>
216
- <li>Serve <code>jux-dist/</code> from your backend</li>
217
- </ul>
218
- \`).render('#app-container');
219
- `;
237
+ // Copy hey.jux from presets as starter template
238
+ const heyTemplatePath = path.join(PATHS.packageRoot, 'presets', 'hey.jux');
239
+ const heyTargetPath = path.join(juxDir, 'hey.jux');
240
+
241
+ if (fs.existsSync(heyTemplatePath)) {
242
+ fs.copyFileSync(heyTemplatePath, heyTargetPath);
243
+ console.log('+ Created jux/hey.jux (starter template)');
244
+ } else {
245
+ console.warn('⚠️ hey.jux template not found in presets/');
246
+ }
220
247
 
221
- const targetPath = path.join(juxDir, 'index.jux');
222
- fs.writeFileSync(targetPath, indexContent);
223
- console.log('✅ Created jux/index.jux');
248
+ // Copy over presets/*.css as styles/ to jux/
249
+ const presetsSrc = path.join(PATHS.packageRoot, 'presets');
250
+ const stylesDest = path.join(juxDir, 'styles');
251
+
252
+ if (fs.existsSync(presetsSrc)) {
253
+ fs.mkdirSync(stylesDest, { recursive: true });
254
+
255
+ const presetFiles = fs.readdirSync(presetsSrc);
256
+ presetFiles.forEach(file => {
257
+ if (file.endsWith('.css')) {
258
+ const srcFile = path.join(presetsSrc, file);
259
+ const destFile = path.join(stylesDest, file);
260
+ fs.copyFileSync(srcFile, destFile);
261
+ console.log(`+ Copied preset style: styles/${file}`);
262
+ }
263
+ });
264
+ }
224
265
 
225
266
  // Create package.json if it doesn't exist
226
267
  const pkgPath = path.join(PATHS.projectRoot, 'package.json');
@@ -250,25 +291,33 @@ node_modules/
250
291
 
251
292
  if (!fs.existsSync(gitignorePath)) {
252
293
  fs.writeFileSync(gitignorePath, gitignoreContent);
253
- console.log(' Created .gitignore');
294
+ console.log('+ Created .gitignore');
254
295
  }
255
296
 
256
- console.log(' Created jux/ directory\n');
297
+ console.log('+ Created jux/ directory\n');
257
298
  console.log('Next steps:');
258
- console.log(' 1. npm install # Install juxscript');
259
- console.log(' 2. Edit jux/index.jux # Build your app');
260
- console.log(' 3. npx jux build # Compile to jux-dist/');
261
- console.log(' 4. Serve jux-dist/ from your backend\n');
299
+ console.log(' npx jux serve hey # Start dev server for hey.jux\n');
300
+ console.log('Check out the docs: https://juxscript.com/docs\n');
262
301
 
263
302
  } else if (command === 'build') {
303
+ // ✅ Always builds router bundle
264
304
  await buildProject(false);
265
305
  console.log(`✅ Build complete: ${PATHS.frontendDist}`);
266
306
 
267
307
  } else if (command === 'serve') {
308
+ // ✅ Serve with optional page parameter
309
+ const pageName = subCommand; // e.g., "hey" from "npx jux serve hey"
310
+
311
+ if (pageName) {
312
+ console.log(`🎯 Serving specific page: ${pageName}\n`);
313
+ }
314
+
268
315
  await buildProject(true);
269
316
 
270
- const port = parseInt(process.argv[3]) || 3000;
271
- await start(port);
317
+ const port = pageName ? 3000 : (parseInt(subCommand) || 3000);
318
+
319
+ // Start server
320
+ await start(port, pageName);
272
321
 
273
322
  } else {
274
323
  console.log(`
@@ -276,16 +325,17 @@ JUX CLI - A JavaScript UX authorship platform
276
325
 
277
326
  Usage:
278
327
  npx jux init Initialize a new JUX project
279
- npx jux build Compile .jux files from ./jux/ to ./jux-dist/
280
- npx jux serve [port] Start dev server with hot reload (default: 3000)
328
+ npx jux build Build router bundle to ./jux-dist/
329
+ npx jux serve [page] Start dev server (optionally for specific page)
330
+ npx jux serve [port] Start dev server on custom port
281
331
 
282
332
  Project Structure:
283
333
  my-project/
284
- ├── jux/ # Your .jux source files
285
- │ ├── index.jux # Entry point (uses 'juxscript' imports)
286
- │ └── pages/ # Additional pages
287
- ├── jux-dist/ # Build output (git-ignore this)
288
- ├── server/ # Your backend
334
+ ├── jux/ # Your .jux source files
335
+ │ ├── hey.jux # Starter page (created by init)
336
+ │ └── pages/ # Additional pages
337
+ ├── jux-dist/ # Build output (git-ignore this)
338
+ ├── server/ # Your backend
289
339
  └── package.json
290
340
 
291
341
  Import Style:
@@ -294,15 +344,17 @@ Import Style:
294
344
  import 'juxscript/presets/notion.js';
295
345
 
296
346
  Getting Started:
297
- 1. npx jux init # Create project structure
298
- 2. npm install # Install dependencies
299
- 3. npx jux build # Build to jux-dist/
300
- 4. Serve jux-dist/ from your backend
347
+ 1. npx jux init # Create project structure
348
+ 2. npm install # Install dependencies
349
+ 3. npx jux serve hey # Dev server for hey.jux at localhost:3000/hey
350
+ 4. npx jux serve # Dev server for all pages
351
+ 5. Serve jux-dist/ from your backend
301
352
 
302
353
  Examples:
303
- npx jux build Build for production
304
- npx jux serve Start dev server
305
- npx jux serve 8080 Start on port 8080
354
+ npx jux build Build production bundle
355
+ npx jux serve Start dev server on port 3000 (all pages)
356
+ npx jux serve hey Start dev server at localhost:3000/hey
357
+ npx jux serve 8080 Start dev server on port 8080
306
358
  `);
307
359
  }
308
360
  })();