juxscript 1.0.46 → 1.0.47

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 CHANGED
@@ -1,83 +1,271 @@
1
- # JUX - JavaScript UX Authorship Platform
1
+ # The web needs a higher level of abstraction: Meet **JUX**
2
2
 
3
- > Say goodbye to markup `</</>>>`. Build reactive UIs with pure JavaScript.
4
3
 
5
- [![npm version](https://img.shields.io/npm/v/juxscript.svg)](https://www.npmjs.com/package/juxscript)
6
- [![License](https://img.shields.io/npm/l/juxscript.svg)](https://github.com/jux/juxscript/blob/main/LICENSE)
4
+ ```diff
5
+ - IF YOU ARE CURRENTLY USING THIS PACKAGE COOL! But it is still a little fragile.
6
+ - We are working on opening our repo to public and working on our documentation pages.
7
+ + Stay tuned! For now, you will see our roadmap here!
7
8
 
8
- ## 🚀 Quick Start
9
+ ```
10
+ - [X] Router
11
+ - [ ] Cross Page Store.
12
+ - [ ] Distributable Bundle (Static Sites)
13
+ - [ ] Tree Shake/Efficiencies.
9
14
 
10
- Create a new JUX app in one command:
15
+ - [ ] Data
16
+ - [ ] Drivers: File, S3, Database.
17
+ - [ ] const d = jux.data('id',{});
18
+ - [ ] d.driver(file|s3|database)
19
+ - [ ] d.items([] | juxitem)
20
+ - [ ] d.store(callback)
11
21
 
12
- ```bash
13
- npx jux create my-app
14
- cd my-app
15
- npm run dev
22
+ - [X] Layouts (100% done.)
23
+ - [ ] *Authoring Layout Pages* - `docs`
24
+ - [ ] *Authoring Application Pages* - `docs`
25
+ - [ ] *Authoring Custom Components* - `docs`
26
+
27
+ - [ ] Render Dependency Tree
28
+ > Idea here is, one element may be a predicate for another. Will need promises. **predicting problems with slow-loading components that depend on containers from other components. May to to separate concerns with container "building" vs. content addition OR use async processes (promises).
29
+ - [X] Reactivity (90% done.)
30
+ - [ ] Client Components (99% of what would be needed.)
31
+ - [X] Charts
32
+ - [X] Poor Intellisense support? Could be this issue. - fixed.
33
+ - [ ] Api Wrapper
34
+ - [X] Params/Active State for Menu/Nav matching - built in.
35
+ - [ ] CDN Bundle (import CDN/'state', 'jux' from cdn.)
36
+ - [ ] Icon
37
+
38
+ - [ ] Quickstart Boilerplates (20% done,notion.jux)
39
+ - [ ] Mobile Nav
40
+ - [ ] `npx jux present notion|default` etc..
41
+ - [ ] Server side components (api and database)
42
+ - [ ] Quick deploy option
43
+
44
+
45
+ ## *JUX* Authoring UI's in pure javascript.
46
+
47
+ > Build beautiful, reactive UI's using only javascript. **No markup required.**
48
+ > A clean authorship layer capable of being taught to non-developers, strong enough to be used by real developers.
49
+
50
+ >> **jux** challenges the idea that markup based systems are the most efficient way to author web applications.
51
+ ## AI friendly - **use less tokens!**
52
+
53
+
54
+ ```diff
55
+ - Markup is expensive!
56
+ Have you ever considered the energy requirements to ship chunks of HTML markup like Vue or React components?
16
57
  ```
17
58
 
18
- Or add JUX to an existing project:
59
+ - [ ] Build a code calculator (token calculator)
60
+ > As a user, I want to be efficient with the LLM's. I want my code to be clean. I want to avoid repeating myself. I ultimately, want to write less code. Less code is better code.
61
+
62
+ ## Ecosystem friendly (NPM)
63
+
64
+ > Because it is just javascript, .jux files work with any npm package you want to throw at it with standard ESM syntax.
65
+
66
+ ## GETTING STARTED
19
67
 
20
68
  ```bash
69
+ # New project
70
+ mkdir my-project
71
+ cd my-project
72
+ npm init -y
21
73
  npm install juxscript
74
+
75
+ # Initialize (creates jux/ directory)
22
76
  npx jux init
77
+
78
+ # Builds jux-dist and serves index.jux
23
79
  npx jux serve
24
80
  ```
81
+ > install
82
+ `npm i juxscript`
25
83
 
26
- ## ✨ Features
84
+ >> Your IDE must be setup to read .jux files as .js files. In VS Code, this is do in the `.vscode/settings.json`. Here is an example.
27
85
 
28
- - 🎨 **No markup** - Build UIs with pure JavaScript
29
- - ⚡ **Reactive** - Built-in state management with automatic DOM updates
30
- - 🧩 **Component-based** - Reusable, composable UI components
31
- - đŸ”Ĩ **Hot reload** - Instant feedback during development
32
- - đŸ“Ļ **Zero config** - Works out of the box
33
- - đŸŽ¯ **Type-safe** - Full TypeScript support
86
+ ```json
87
+ //.vscode/settings.json
88
+ {
89
+ "files.associations": {
90
+ "*.jux": "javascript",
91
+ "*.juxt": "javascript"
92
+ },
93
+ "javascript.validate.enable": false,
94
+ "[javascript]": {
95
+ "editor.defaultFormatter": "vscode.typescript-language-features"
96
+ }
97
+ }
98
+ ```
34
99
 
35
- ## 📖 Example
100
+ > building and serving the `examples` project directory.
101
+ `npm run dev`
36
102
 
37
- ```javascript
38
- import { jux, state } from 'juxscript';
103
+ > building only (the `examples` project directory)
104
+ `npm run build:examples`
39
105
 
40
- // Create reactive state
41
- const count = state(0);
42
106
 
43
- // Build UI with fluent API
44
- jux.button('increment')
45
- .label('Click me!')
46
- .bind('click', () => count.value++)
47
- .render('#app');
107
+ # Authoring in Jux
48
108
 
49
- jux.paragraph('counter')
50
- .sync('text', count, val => `Count: ${val}`)
51
- .render('#app');
52
- ```
109
+ 1. Build a Layout Page you can reuse.
110
+ Check out the presets, they speed this process up. Just modify them with your goals, such as logo, menu options, nav etc.
111
+ 2. Build your Pages
112
+ Just get to work, using the **jux** component library to build most of what you need, filling in any details with *javascript* logically arranged the way you want. Think, authoring SFC but with only the composition layer.
113
+ 3. Test it out!
114
+ Jux comes with a watcher system built in, so you can see your edits to *.jux* files live.
53
115
 
54
- ## 📚 Documentation
116
+ # GOAL: Eliminating the Markup System of building UI's.
117
+ > HTML markup is the equivalent of still writing in `C` ro `C++` despite the availability of more functional levels of abstraction like `python`.
118
+ > To remove the requirement for markup, **jux** must address the utility of markup.
55
119
 
56
- - [Getting Started](https://juxscript.com/docs) _(coming soon)_
57
- - [API Reference](https://juxscript.com/api) _(coming soon)_
58
- - [Examples](https://github.com/juxscript/examples)
120
+ ## Layouts
59
121
 
60
- ## đŸ› ī¸ CLI Commands
122
+ ### Grid System
123
+ - [ ] Laying out JUX files in a rational way.
124
+ > Develop a higher-level layout javascript grammar for building layouts and palettes of style using *ZERO markup*.
61
125
 
62
- ```bash
63
- npx jux create <name> # Create new project
64
- npx jux init # Initialize in existing directory
65
- npx jux build # Build for production
66
- npx jux serve # Start dev server with hot reload
67
- ```
126
+ - [ ] A Partial View Strategy
127
+ >>> As a dev I may need to load partial pages/components into other pages. This should avoid noisy wrappers and ideally be just javascript imports of jux objects.
128
+
129
+ ### Components
130
+
131
+ We have constructed the most helpful components for building 99% of core business/saas web applications, that present beautifully on the page and are created purely with javascript.
132
+ > https://www.shadcn-vue.com/docs/components
133
+ > Run time slots.. Example, `table.slot(columnName -> object);`
134
+
135
+ # Backend
136
+
137
+ ### Routing & Views
138
+ - [X] Subviews architecture
139
+ - [X] Nested routing support
140
+ - [X] View composition
141
+ - [X] Layout system
142
+ - [X] User views
143
+ - [X] `profile.jux` - User
144
+
145
+ ### Server Docs/Documentation.
146
+
147
+ - [ ] Should *jux* ship with a server at all? Or merely for demos.
68
148
 
69
- ## 🤝 Contributing
149
+ > What is the server even doing? Is it merely a matter of understanding that JUX builder/compilation gives you a mirror of how you setup your project. It can be as flat or deep as you would like.
70
150
 
71
- We welcome contributions! Please see [CONTRIBUTING.md](https://github.com/jux/juxscript/blob/main/CONTRIBUTING.md) for details.
151
+ > *At this point*, we are speaking almost 100% towards UI interfaces.
152
+ > Servers should be 'loosely coupled.'. This allows for a standard.
153
+ >>> In the future, we could introduce an *auto api* feature, similar to *next.js* api heuristic for folders and files.
154
+ ---
155
+
156
+
157
+ [ **@jesseallenrxtrail -> Discuss.** ]
158
+
159
+ ### Chore: Commit to building more in package directory.
160
+
161
+ > We can experiment with our example project, using a simple `express` server, running. What third-party integration do we need?
162
+ - [ ] Chore: Cleanup our `machinery/server.js` to separate concerns and build our actual endpoints we need to build software here.
163
+
164
+ #### Alternative Chore: package this for `npmjs.org`, combine with our existing project, re-author frontend in *jux*.
165
+
166
+ - [ ] Alternative Chore: package this for `npmjs.org` and ship it.
167
+ - [ ] Start a brand new project.
168
+ - [ ] Build the server aspect in `flask` (already done).
169
+ - [ ] Author the frontend in **jux** instead of **vue**.
170
+
171
+ > This may require us to move our 'complilation error module', `lib/components/error-handler.ts` to the front-end for display without a 'server call' at all?
172
+ - [ ] Chore for this? This takes more analysis, discussion.
173
+
174
+ > How and what will we wire for backend and what **jux** components will we support for working with backends.
175
+ - [ X ] SQL Database (query component)
176
+ - [ ] Chore: This is currently supported. Needs revisiting and testing
177
+ > API's
178
+ - [ ] Base payload creater and consumer as a **jux** component. *jux.api*??
179
+
180
+ ---
181
+
182
+ # Jux Configs
183
+
184
+ > Jux configs.. `jux.config.js` what should this entail?
185
+ Currently it looks like this:
186
+ - [ ] Chore: **WHAT IS ACTUALLY BEING USED AND WHERE?**
72
187
 
73
- ## 📄 License
188
+ ```javascript
189
+ export default {
190
+ projectRoot: __dirname,
191
+ distDir: path.join(__dirname, 'dist'),
192
+ build: {
193
+ minify: false
194
+ },
195
+
196
+ database: {
197
+ // Default connection
198
+ default: 'sqlite',
199
+
200
+ // Named connections
201
+ connections: {
202
+ sqlite: {
203
+ driver: 'sqlite',
204
+ database: path.join(__dirname, 'db', 'jux.db')
205
+ },
206
+ ... // props to feed drivers for mysql/postgres
207
+ }
208
+ },
209
+
210
+ server: {
211
+ port: process.env.PORT || 3000,
212
+ host: process.env.HOST || 'localhost'
213
+ },
214
+
215
+ };
216
+ ```
217
+ **As a user, I will want to control where JUX sends queries.**
218
+ **As a user, I may want to specify a default proxy for backend. I may also want to specify, explicitly, URL's to hit other service endpoints**
219
+ - [ ] Escape hatch (backend API proxy vs default)
220
+ - [ ] As a user, I may want to run my own server for backend requests/proxies.
221
+ - [ ] As a user, I may want to hit multiple endpoints in the same JUX file.
74
222
 
75
- MIT Š [Your Name]
223
+ ---
76
224
 
77
- ## ⭐ Support
225
+ # CORE
78
226
 
79
- If you find JUX useful, please star us on [GitHub](https://github.com/jux/juxscript)!
227
+ ### STATIC SERVING (your local file system, s3 bucket static sites, any host that allows serving etc...)
228
+ **Should JUX function, as a 100% client side app (STATIC WEBSITES)**
229
+ - [ ] Chore: Discovery... I think the answer is yes. I think what might be missing is bundling.
230
+ ![alt text](local.png)
80
231
 
81
232
  ---
233
+ ## REACTIVITY LAYER.
234
+ ### Goal for Reactivity
235
+ > We are shooting for better than nasty amounts of javascript, not as sophisticated as vue/react.
236
+
237
+ > As a user, I want changes in state of my objects to be consumable by other objects on the page so they can paint changes and interface with each other seamlessly.
238
+ - [ ] Build a basic `v-model` strategy in pure javascript. Different than jquery, javascript etc.
239
+ - [ ] Build standard eventing systems, listeners and emitters on components where they make sense.
240
+ `
241
+ # ROADMAP
242
+
243
+ ### KEY UI PAGES
244
+ - [ ] JWT Authentication (`authJWT`) (demo `examples` only)
245
+ - [ ] Token generation (demo `examples` only)
246
+ - [ ] Token validation middleware (demo `examples` only)
247
+ - [ ] Refresh token logic (demo `examples` only)
248
+ - [ ] *Login system PAGES! Ships with `vendor` assets like lib files/layouts etc.*
249
+ - [ ] Login
250
+ - [ ] Logout
251
+ - [ ] Profile
252
+
253
+
254
+ ### Additional Features
255
+ - [ ] API documentation
256
+ - [ ] Testing setup
257
+
258
+
259
+
260
+
261
+
262
+ ### LAYOUT NOTES...
263
+ ### Jux Standard Layout Regions
264
+
265
+ - [ ] CHORE : REVISIT! I think maybe we ship with our figma, notion, default etc. But then document how to build and reuse a .jux page as a layout using the `jux.layout` class.
266
+
267
+ ### Start with a layout page
268
+
269
+ You can build a simple one, or copy and use one of the Jux presets.
82
270
 
83
- **Need help?** Join our [Discord](https://discord.gg/juxscript) _(placeholder)_ or open an [issue](https://github.com/jux/juxscript/issues).
271
+ `npx jux preset notion|default|figma|slack|other`
package/bin/cli.js CHANGED
@@ -1,5 +1,123 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ import path from 'path';
4
+ import fs from 'fs';
5
+ import { fileURLToPath } from 'url';
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+
10
+ const command = process.argv[2];
11
+
12
+ // ═══════════════════════════════════════════════════════════════════
13
+ // CREATE COMMAND - Runs BEFORE dependencies are installed
14
+ // ═══════════════════════════════════════════════════════════════════
15
+
16
+ if (command === 'create') {
17
+ const projectName = process.argv[3] || 'my-jux-app';
18
+ const projectPath = path.join(process.cwd(), projectName);
19
+
20
+ console.log(`
21
+ ╔═══════════════════════════════════════════════════════╗
22
+ ║ ║
23
+ ║ 🎨 Welcome to JUX ║
24
+ ║ ║
25
+ ║ Creating your new JUX project... ║
26
+ ║ ║
27
+ ╚═══════════════════════════════════════════════════════╝
28
+ `);
29
+
30
+ if (fs.existsSync(projectPath)) {
31
+ console.error(`❌ Directory "${projectName}" already exists.`);
32
+ console.error(` Please choose a different name or remove the existing directory.\n`);
33
+ process.exit(1);
34
+ }
35
+
36
+ try {
37
+ const { execSync } = await import('child_process');
38
+
39
+ console.log(`📁 Creating directory: ${projectName}`);
40
+ fs.mkdirSync(projectPath, { recursive: true });
41
+ process.chdir(projectPath);
42
+
43
+ console.log(`đŸ“Ļ Initializing package.json...`);
44
+ const packageJson = {
45
+ name: projectName,
46
+ version: '0.1.0',
47
+ type: 'module',
48
+ scripts: {
49
+ dev: 'jux serve',
50
+ build: 'jux build'
51
+ },
52
+ dependencies: {
53
+ juxscript: 'latest'
54
+ }
55
+ };
56
+ fs.writeFileSync('package.json', JSON.stringify(packageJson, null, 2));
57
+ console.log(` ✓ package.json created`);
58
+
59
+ console.log(`\nđŸ“Ĩ Installing juxscript...\n`);
60
+ try {
61
+ execSync('npm install', { stdio: 'inherit' });
62
+ console.log(`\n ✓ Dependencies installed`);
63
+ } catch (err) {
64
+ console.error(`\n âš ī¸ npm install failed, but continuing...`);
65
+ }
66
+
67
+ console.log(`\n🎨 Initializing JUX project structure...`);
68
+ execSync('npx jux init', { stdio: 'inherit' });
69
+
70
+ console.log(`\n📝 Creating .gitignore...`);
71
+ fs.writeFileSync('.gitignore', `jux-dist/\nnode_modules/\n.DS_Store\n.env\n*.log\n`);
72
+ console.log(` ✓ .gitignore created`);
73
+
74
+ console.log(`
75
+ ╔═══════════════════════════════════════════════════════╗
76
+ ║ ║
77
+ ║ ✅ Project created successfully! ║
78
+ ║ ║
79
+ ╚═══════════════════════════════════════════════════════╝
80
+
81
+ 📚 Resources:
82
+ Documentation: [coming soon]
83
+ GitHub: https://github.com/juxscript/jux
84
+ Examples: https://github.com/juxscript/examples
85
+
86
+ ⭐ If you find JUX useful, please star us on GitHub!
87
+ 🔒 Security: Report issues to security@juxscript.com [placeholder]
88
+
89
+ Say goodbye to markup </</>>>.
90
+ Happy javascripting your frontend! 🎉
91
+
92
+ Next steps:
93
+ cd ${projectName}
94
+ npm run dev
95
+ `);
96
+
97
+ } catch (err) {
98
+ console.error(`\n❌ Project creation failed:`, err.message);
99
+
100
+ if (fs.existsSync(projectPath)) {
101
+ try {
102
+ process.chdir('..');
103
+ fs.rmSync(projectPath, { recursive: true, force: true });
104
+ console.log(` ✓ Cleaned up failed project directory\n`);
105
+ } catch (cleanupErr) {
106
+ console.error(` âš ī¸ Could not clean up directory\n`);
107
+ }
108
+ }
109
+
110
+ process.exit(1);
111
+ }
112
+
113
+ process.exit(0); // ✅ Exit after create completes
114
+ }
115
+
116
+ // ═══════════════════════════════════════════════════════════════════
117
+ // ALL OTHER COMMANDS - Require dependencies to be installed
118
+ // ═══════════════════════════════════════════════════════════════════
119
+
120
+ // ✅ Now import dependencies (only needed for init, build, serve)
3
121
  import {
4
122
  copyLibToOutput,
5
123
  copyProjectAssets,
@@ -10,16 +128,6 @@ import {
10
128
  } from '../machinery/compiler.js';
11
129
  import { generateDocs } from '../machinery/doc-generator.js';
12
130
  import { start } from '../machinery/server.js';
13
- import path from 'path';
14
- import fs from 'fs';
15
- import { fileURLToPath } from 'url';
16
- import { loadConfig, runBootstrap } from '../machinery/config.js';
17
-
18
- const __filename = fileURLToPath(import.meta.url);
19
- const __dirname = path.dirname(__filename);
20
-
21
- // Load configuration first (before PATHS)
22
- const config = await loadConfig(process.cwd());
23
131
 
24
132
  // CLEAR PATH CONTRACT - CONVENTIONS
25
133
  const PATHS = {
@@ -29,14 +137,20 @@ const PATHS = {
29
137
  // Where the user's project root is (where they run `npx jux`)
30
138
  projectRoot: process.cwd(),
31
139
 
32
- // Where user's .jux source files live (from config or default)
33
- juxSource: path.join(process.cwd(), config.sourceDir),
140
+ // Where user's .jux source files live (CONVENTION: ./jux/)
141
+ get juxSource() {
142
+ return path.join(this.projectRoot, 'jux');
143
+ },
34
144
 
35
145
  // Where jux lib files are (components, layouts, etc.)
36
- juxLib: path.resolve(__dirname, '..', 'lib'),
146
+ get juxLib() {
147
+ return path.join(this.packageRoot, 'lib');
148
+ },
37
149
 
38
- // Where frontend build output goes (from config or default)
39
- frontendDist: path.join(process.cwd(), config.distDir)
150
+ // Where frontend build output goes (CONVENTION: ./jux-dist/)
151
+ get frontendDist() {
152
+ return path.join(this.projectRoot, 'jux-dist');
153
+ }
40
154
  };
41
155
 
42
156
  console.log('📍 JUX Paths:');
@@ -46,10 +160,6 @@ console.log(` Source: ${PATHS.juxSource}`);
46
160
  console.log(` Output: ${PATHS.frontendDist}`);
47
161
  console.log(` Lib: ${PATHS.juxLib}\n`);
48
162
 
49
- const command = process.argv[2];
50
- const watchMode = process.argv.includes('--watch');
51
- const bundleMode = process.argv.includes('--bundle');
52
-
53
163
  /**
54
164
  * Recursively find .jux files in a directory
55
165
  */
@@ -78,9 +188,8 @@ function findJuxFiles(dir, fileList = []) {
78
188
  * Build the entire JUX project (ALWAYS uses router bundle)
79
189
  *
80
190
  * @param {boolean} isServe - Whether building for dev server
81
- * @param {number} wsPort - WebSocket port for hot reload
82
191
  */
83
- async function buildProject(isServe = false, wsPort = 3001) {
192
+ async function buildProject(isServe = false) {
84
193
  const buildStartTime = performance.now();
85
194
  console.log('🔨 Building JUX frontend...\n');
86
195
 
@@ -100,14 +209,14 @@ async function buildProject(isServe = false, wsPort = 3001) {
100
209
 
101
210
  // Step 1: Generate documentation
102
211
  const docsStartTime = performance.now();
103
- let docsTime = 0;
212
+ let docsTime = 0; // ✅ Declare with default value
104
213
  console.log('📚 Generating documentation...');
105
214
  try {
106
215
  await generateDocs(PATHS.juxLib);
107
216
  docsTime = performance.now() - docsStartTime;
108
217
  console.log(`✅ Documentation generated (${docsTime.toFixed(0)}ms)\n`);
109
218
  } catch (error) {
110
- docsTime = performance.now() - docsStartTime;
219
+ docsTime = performance.now() - docsStartTime; // ✅ Still calculate time even on error
111
220
  console.warn(`âš ī¸ Failed to generate docs (${docsTime.toFixed(0)}ms):`, error.message);
112
221
  }
113
222
 
@@ -123,7 +232,7 @@ async function buildProject(isServe = false, wsPort = 3001) {
123
232
  const presetsTime = performance.now() - presetsStartTime;
124
233
  console.log(`âąī¸ Presets copy time: ${presetsTime.toFixed(0)}ms\n`);
125
234
 
126
- // Step 4: Copy project assets
235
+ // Step 4: Copy project assets (CSS, JS, images)
127
236
  const assetsStartTime = performance.now();
128
237
  await copyProjectAssets(PATHS.juxSource, PATHS.frontendDist);
129
238
  const assetsTime = performance.now() - assetsStartTime;
@@ -144,6 +253,7 @@ async function buildProject(isServe = false, wsPort = 3001) {
144
253
  process.exit(1);
145
254
  }
146
255
 
256
+ // ✅ Bundle and get the generated filename
147
257
  const bundleStartTime = performance.now();
148
258
  const mainJsFilename = await bundleJuxFilesToRouter(PATHS.juxSource, PATHS.frontendDist, {
149
259
  routePrefix: ''
@@ -173,14 +283,16 @@ async function buildProject(isServe = false, wsPort = 3001) {
173
283
  };
174
284
  });
175
285
 
286
+ // ✅ Generate unified index.html
176
287
  const indexStartTime = performance.now();
177
- generateIndexHtml(PATHS.frontendDist, routes, mainJsFilename, wsPort);
288
+ generateIndexHtml(PATHS.frontendDist, routes, mainJsFilename);
178
289
  const indexTime = performance.now() - indexStartTime;
179
290
 
180
291
  const totalBuildTime = performance.now() - buildStartTime;
181
292
 
182
293
  console.log(`\n✅ Bundled ${projectJuxFiles.length} page(s) → ${PATHS.frontendDist}/${mainJsFilename}\n`);
183
294
 
295
+ // ✅ Build summary with timing breakdown
184
296
  console.log(`📊 Build Summary:`);
185
297
  console.log(` ━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
186
298
  console.log(` Documentation: ${docsTime.toFixed(0)}ms`);
@@ -193,6 +305,7 @@ async function buildProject(isServe = false, wsPort = 3001) {
193
305
  console.log(` ━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
194
306
  console.log(` Total build time: ${totalBuildTime.toFixed(0)}ms\n`);
195
307
 
308
+ // Show usage
196
309
  if (!isServe) {
197
310
  console.log('đŸ“Ļ Serve from your backend:');
198
311
  console.log(` Express: app.use(express.static('jux-dist'))`);
@@ -215,102 +328,7 @@ async function buildProject(isServe = false, wsPort = 3001) {
215
328
  }
216
329
 
217
330
  (async () => {
218
- if (command === 'create') {
219
- const projectName = process.argv[3] || 'my-jux-app';
220
- const projectPath = path.join(PATHS.projectRoot, projectName);
221
-
222
- console.log(`
223
- ╔═══════════════════════════════════════════════════════╗
224
- ║ ║
225
- ║ 🎨 Welcome to JUX ║
226
- ║ ║
227
- ║ Creating your new JUX project... ║
228
- ║ ║
229
- ╚═══════════════════════════════════════════════════════╝
230
- `);
231
-
232
- if (fs.existsSync(projectPath)) {
233
- console.error(`❌ Directory "${projectName}" already exists.`);
234
- console.error(` Please choose a different name or remove the existing directory.\n`);
235
- process.exit(1);
236
- }
237
-
238
- try {
239
- const { execSync } = await import('child_process');
240
-
241
- console.log(`📁 Creating directory: ${projectName}`);
242
- fs.mkdirSync(projectPath, { recursive: true });
243
- process.chdir(projectPath);
244
-
245
- console.log(`đŸ“Ļ Initializing package.json...`);
246
- const packageJson = {
247
- name: projectName,
248
- version: '0.1.0',
249
- type: 'module',
250
- scripts: {
251
- dev: 'jux serve',
252
- build: 'jux build'
253
- },
254
- dependencies: {
255
- juxscript: 'latest' // ✅ Changed from '^1.0.8' to 'latest'
256
- }
257
- };
258
- fs.writeFileSync('package.json', JSON.stringify(packageJson, null, 2));
259
- console.log(` ✓ package.json created`);
260
-
261
- console.log(`\nđŸ“Ĩ Installing juxscript...\n`);
262
- try {
263
- execSync('npm install', { stdio: 'inherit' });
264
- console.log(`\n ✓ Dependencies installed`);
265
- } catch (err) {
266
- console.error(`\n âš ī¸ npm install failed, but continuing...`);
267
- }
268
-
269
- console.log(`\n🎨 Initializing JUX project structure...`);
270
- execSync('npx jux init', { stdio: 'inherit' });
271
-
272
- console.log(`\n📝 Creating .gitignore...`);
273
- fs.writeFileSync('.gitignore', `jux-dist/\nnode_modules/\n.DS_Store\n.env\n*.log\n`);
274
- console.log(` ✓ .gitignore created`);
275
-
276
- console.log(`
277
- ╔═══════════════════════════════════════════════════════╗
278
- ║ ║
279
- ║ ✅ Project created successfully! ║
280
- ║ ║
281
- ╚═══════════════════════════════════════════════════════╝
282
-
283
- 📚 Resources:
284
- Documentation: [coming soon]
285
- GitHub: https://github.com/juxscript/jux
286
- Examples: https://github.com/juxscript/examples
287
-
288
- ⭐ If you find JUX useful, please star us on GitHub!
289
- 🔒 Security: Report issues to security@juxscript.com [placeholder]
290
-
291
- Say goodbye to markup </</>>>.
292
- Happy javascripting your frontend! 🎉
293
-
294
- Next steps, run: cd ${projectName} && npm run dev
295
- `);
296
-
297
- } catch (err) {
298
- console.error(`\n❌ Project creation failed:`, err.message);
299
-
300
- if (fs.existsSync(projectPath)) {
301
- try {
302
- process.chdir('..');
303
- fs.rmSync(projectPath, { recursive: true, force: true });
304
- console.log(` ✓ Cleaned up failed project directory\n`);
305
- } catch (cleanupErr) {
306
- console.error(` âš ī¸ Could not clean up directory\n`);
307
- }
308
- }
309
-
310
- process.exit(1);
311
- }
312
-
313
- } else if (command === 'init') {
331
+ if (command === 'init') {
314
332
  console.log('🎨 Initializing JUX project...\n');
315
333
 
316
334
  const juxDir = PATHS.juxSource;
@@ -320,8 +338,10 @@ Next steps, run: cd ${projectName} && npm run dev
320
338
  process.exit(1);
321
339
  }
322
340
 
341
+ // Create structure
323
342
  fs.mkdirSync(juxDir, { recursive: true });
324
343
 
344
+ // Copy jux.jux as the starter index.jux (if it exists)
325
345
  const juxJuxSrc = path.join(PATHS.packageRoot, 'presets', 'jux.jux');
326
346
  const indexJuxDest = path.join(juxDir, 'index.jux');
327
347
 
@@ -329,6 +349,7 @@ Next steps, run: cd ${projectName} && npm run dev
329
349
  fs.copyFileSync(juxJuxSrc, indexJuxDest);
330
350
  console.log('+ Created jux/index.jux from jux.jux template');
331
351
  } else {
352
+ // Fallback to hey.jux if jux.jux doesn't exist
332
353
  const heyJuxSrc = path.join(PATHS.packageRoot, 'presets', 'hey.jux');
333
354
  if (fs.existsSync(heyJuxSrc)) {
334
355
  fs.copyFileSync(heyJuxSrc, indexJuxDest);
@@ -341,6 +362,7 @@ Next steps, run: cd ${projectName} && npm run dev
341
362
  }
342
363
  }
343
364
 
365
+ // Copy entire presets folder to jux/presets/ (excluding jux.jux)
344
366
  const presetsSrc = path.join(PATHS.packageRoot, 'presets');
345
367
  const presetsDest = path.join(juxDir, 'presets');
346
368
 
@@ -354,6 +376,7 @@ Next steps, run: cd ${projectName} && npm run dev
354
376
  const srcPath = path.join(src, entry.name);
355
377
  const destPath = path.join(dest, entry.name);
356
378
 
379
+ // Skip jux.jux since we already copied it to index.jux
357
380
  if (entry.isFile() && entry.name === 'jux.jux') {
358
381
  continue;
359
382
  }
@@ -378,51 +401,37 @@ Next steps, run: cd ${projectName} && npm run dev
378
401
  }
379
402
  }
380
403
 
404
+ // Create package.json if it doesn't exist
381
405
  const pkgPath = path.join(PATHS.projectRoot, 'package.json');
382
406
  if (!fs.existsSync(pkgPath)) {
383
- // ✅ Get project name from current directory or default
384
- const projectName = path.basename(PATHS.projectRoot).toLowerCase().replace(/[^a-z0-9-]/g, '-');
385
-
386
407
  const pkgContent = {
387
- "name": projectName,
388
- "version": "0.1.0",
408
+ "name": "my-jux-project",
409
+ "version": "1.0.0",
389
410
  "type": "module",
390
411
  "scripts": {
391
- "dev": "jux serve",
392
- "build": "jux build"
412
+ "build": "jux build",
413
+ "serve": "jux serve"
393
414
  },
394
415
  "dependencies": {
395
- "juxscript": "latest" // ✅ Always use latest stable
416
+ "juxscript": "^1.0.8"
396
417
  }
397
418
  };
398
419
  fs.writeFileSync(pkgPath, JSON.stringify(pkgContent, null, 2));
399
420
  console.log('+ Created package.json');
400
421
  }
401
422
 
423
+ // Create .gitignore
402
424
  const gitignorePath = path.join(PATHS.projectRoot, '.gitignore');
403
- const gitignoreContent = `jux-dist/\nnode_modules/\n.DS_Store\n`;
425
+ const gitignoreContent = `jux-dist/
426
+ node_modules/
427
+ .DS_Store
428
+ `;
404
429
 
405
430
  if (!fs.existsSync(gitignorePath)) {
406
431
  fs.writeFileSync(gitignorePath, gitignoreContent);
407
432
  console.log('+ Created .gitignore');
408
433
  }
409
434
 
410
- // ✅ Create actual juxconfig.js (not just example)
411
- const configSrc = path.join(PATHS.packageRoot, 'juxconfig.example.js');
412
- const configDest = path.join(PATHS.projectRoot, 'juxconfig.js');
413
-
414
- if (fs.existsSync(configSrc) && !fs.existsSync(configDest)) {
415
- fs.copyFileSync(configSrc, configDest);
416
- console.log('+ Created juxconfig.js (customize as needed)');
417
- }
418
-
419
- // Also copy example as reference
420
- const configExampleDest = path.join(PATHS.projectRoot, 'juxconfig.example.js');
421
- if (fs.existsSync(configSrc) && !fs.existsSync(configExampleDest)) {
422
- fs.copyFileSync(configSrc, configExampleDest);
423
- console.log('+ Created juxconfig.example.js (reference)');
424
- }
425
-
426
435
  console.log('\n✅ JUX project initialized!\n');
427
436
  console.log('Next steps:');
428
437
  console.log(' npm install # Install dependencies');
@@ -430,15 +439,18 @@ Next steps, run: cd ${projectName} && npm run dev
430
439
  console.log('Check out the docs: https://juxscript.com/docs\n');
431
440
 
432
441
  } else if (command === 'build') {
442
+ // ✅ Always builds router bundle
433
443
  await buildProject(false);
434
444
  console.log(`✅ Build complete: ${PATHS.frontendDist}`);
435
445
 
436
446
  } else if (command === 'serve') {
437
- const httpPort = parseInt(process.argv[3]) || config.ports.http;
438
- const wsPort = parseInt(process.argv[4]) || config.ports.ws;
447
+ // ✅ Always serves router bundle
448
+ await buildProject(true);
449
+
450
+ // Parse port arguments: npx jux serve [httpPort] [wsPort]
451
+ const httpPort = parseInt(process.argv[3]) || 3000;
452
+ const wsPort = parseInt(process.argv[4]) || 3001;
439
453
 
440
- await runBootstrap(config.bootstrap);
441
- await buildProject(true, wsPort);
442
454
  await start(httpPort, wsPort);
443
455
 
444
456
  } else {
@@ -446,15 +458,39 @@ Next steps, run: cd ${projectName} && npm run dev
446
458
  JUX CLI - A JavaScript UX authorship platform
447
459
 
448
460
  Usage:
449
- npx jux create [name] Create a new JUX project
450
- npx jux init Initialize JUX in current directory
461
+ npx jux init Initialize a new JUX project
451
462
  npx jux build Build router bundle to ./jux-dist/
452
463
  npx jux serve [http] [ws] Start dev server with hot reload
453
464
 
465
+ Arguments:
466
+ [http] HTTP server port (default: 3000)
467
+ [ws] WebSocket port (default: 3001)
468
+
469
+ Project Structure:
470
+ my-project/
471
+ ├── jux/ # Your .jux source files
472
+ │ ├── index.jux # Entry point
473
+ │ └── pages/ # Additional pages
474
+ ├── jux-dist/ # Build output (git-ignore this)
475
+ ├── server/ # Your backend
476
+ └── package.json
477
+
478
+ Import Style:
479
+ // In your project's .jux files
480
+ import { jux, state } from 'juxscript';
481
+ import 'juxscript/presets/notion.js';
482
+
483
+ Getting Started:
484
+ 1. npx jux init # Create project structure
485
+ 2. npm install # Install dependencies
486
+ 3. npx jux serve # Dev server with hot reload
487
+ 4. Serve jux-dist/ from your backend
488
+
454
489
  Examples:
455
- npx jux create my-app Create new project
456
- npx jux serve Dev server (ports 3000/3001)
457
- npx jux serve 8080 8081 Custom ports
490
+ npx jux build # Build production bundle
491
+ npx jux serve # Dev server (ports 3000/3001)
492
+ npx jux serve 8080 # HTTP on 8080, WS on 3001
493
+ npx jux serve 8080 8081 # HTTP on 8080, WS on 8081
458
494
  `);
459
495
  }
460
496
  })();
@@ -2053,5 +2053,5 @@
2053
2053
  }
2054
2054
  ],
2055
2055
  "version": "1.0.0",
2056
- "lastUpdated": "2026-01-28T20:10:47.892Z"
2056
+ "lastUpdated": "2026-01-28T20:46:06.367Z"
2057
2057
  }
@@ -673,12 +673,16 @@ render();
673
673
  *
674
674
  * @param {string} distDir - Destination directory
675
675
  * @param {Array<{path: string, functionName: string}>} routes - Route definitions
676
- * @param {string} mainJsFilename - The generated main.js filename
677
- * @param {number} wsPort - WebSocket port for hot reload
676
+ * @param {string} mainJsFilename - The generated main.js filename (e.g., 'main.1234567890.js')
678
677
  */
679
- export function generateIndexHtml(distDir, routes, mainJsFilename = 'main.js', wsPort = 3001) {
678
+ export function generateIndexHtml(distDir, routes, mainJsFilename = 'main.js') {
680
679
  console.log('📄 Generating index.html...');
681
680
 
681
+ // Generate navigation links
682
+ const navLinks = routes
683
+ .map(r => ` <a href="${r.path}">${r.functionName.replace(/_/g, ' ')}</a>`)
684
+ .join(' |\n');
685
+
682
686
  const importMapScript = generateImportMapScript();
683
687
 
684
688
  const html = `<!DOCTYPE html>
@@ -693,62 +697,6 @@ export function generateIndexHtml(distDir, routes, mainJsFilename = 'main.js', w
693
697
  <div id="app"></div>
694
698
  ${importMapScript}
695
699
  <script type="module" src="/${mainJsFilename}"></script>
696
-
697
- <!-- Hot Reload Script -->
698
- <script>
699
- (function() {
700
- const ws = new WebSocket('ws://' + location.hostname + ':${wsPort}');
701
-
702
- ws.onopen = () => {
703
- console.log('🔌 Hot reload connected');
704
- };
705
-
706
- ws.onmessage = (event) => {
707
- const data = JSON.parse(event.data);
708
-
709
- if (data.type === 'reload') {
710
- console.log('🔄 Hot reload triggered - reloading page...');
711
- location.reload();
712
- } else if (data.type === 'css-reload') {
713
- console.log('🎨 CSS hot reload:', data.path);
714
-
715
- // Find all link tags pointing to this CSS file
716
- const links = document.querySelectorAll('link[rel="stylesheet"]');
717
- links.forEach(link => {
718
- if (link.href.includes(data.path)) {
719
- const newLink = link.cloneNode();
720
- newLink.href = data.path + '?t=' + Date.now();
721
- link.parentNode.insertBefore(newLink, link.nextSibling);
722
- setTimeout(() => link.remove(), 100);
723
- }
724
- });
725
-
726
- // Also reload any @import in style tags
727
- const styles = document.querySelectorAll('style');
728
- styles.forEach(style => {
729
- if (style.textContent.includes(data.path)) {
730
- style.textContent = style.textContent.replace(
731
- new RegExp(data.path + '(\\\\?t=\\\\d+)?', 'g'),
732
- data.path + '?t=' + Date.now()
733
- );
734
- }
735
- });
736
- }
737
- };
738
-
739
- ws.onclose = () => {
740
- console.log('🔌 Hot reload disconnected');
741
- // Try to reconnect after 1 second
742
- setTimeout(() => {
743
- location.reload();
744
- }, 1000);
745
- };
746
-
747
- ws.onerror = (error) => {
748
- console.error('🔌 Hot reload error:', error);
749
- };
750
- })();
751
- </script>
752
700
  </body>
753
701
  </html>`;
754
702
 
@@ -149,66 +149,4 @@ async function serve(httpPort = 3000, wsPort = 3001, distDir = './jux-dist') {
149
149
 
150
150
  export async function start(httpPort = 3000, wsPort = 3001) {
151
151
  return serve(httpPort, wsPort, './jux-dist');
152
- }
153
-
154
- function generateIndexHtml(distDir, routes, mainJsFilename = 'main.js') {
155
- const html = `<!DOCTYPE html>
156
- <html lang="en">
157
- <head>
158
- <meta charset="UTF-8">
159
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
160
- <title>Jux Application</title>
161
- </head>
162
- <body data-theme="">
163
- <div id="app"></div>
164
- ${importMapScript}
165
- <script type="module" src="/${mainJsFilename}"></script>
166
-
167
- <!-- Hot Reload Script -->
168
- <script>
169
- (function() {
170
- const ws = new WebSocket('ws://' + location.hostname + ':${wsPort}');
171
-
172
- ws.onmessage = (event) => {
173
- const data = JSON.parse(event.data);
174
-
175
- if (data.type === 'reload') {
176
- console.log('🔄 Hot reload triggered');
177
- location.reload();
178
- } else if (data.type === 'css-reload') {
179
- console.log('🎨 CSS hot reload:', data.path);
180
-
181
- // Find all link tags pointing to this CSS file
182
- const links = document.querySelectorAll('link[rel="stylesheet"]');
183
- links.forEach(link => {
184
- if (link.href.includes(data.path)) {
185
- const newLink = link.cloneNode();
186
- newLink.href = data.path + '?t=' + Date.now();
187
- link.parentNode.insertBefore(newLink, link.nextSibling);
188
- setTimeout(() => link.remove(), 100);
189
- }
190
- });
191
-
192
- // Also reload any @import in style tags
193
- const styles = document.querySelectorAll('style');
194
- styles.forEach(style => {
195
- if (style.textContent.includes(data.path)) {
196
- style.textContent = style.textContent.replace(
197
- new RegExp(data.path + '(\\?t=\\d+)?', 'g'),
198
- data.path + '?t=' + Date.now()
199
- );
200
- }
201
- });
202
- }
203
- };
204
-
205
- ws.onclose = () => {
206
- console.log('🔌 Hot reload disconnected');
207
- };
208
- })();
209
- </script>
210
- </body>
211
- </html>`;
212
-
213
- return html;
214
152
  }
@@ -1,6 +1,10 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import { bundleJuxFilesToRouter } from './compiler.js';
3
+ import {
4
+ copyLibToOutput,
5
+ bundleJuxFilesToRouter,
6
+ generateIndexHtml
7
+ } from './compiler.js';
4
8
 
5
9
  let isRebuilding = false;
6
10
  let rebuildQueued = false;
@@ -115,130 +119,53 @@ async function fullRebuild(juxSource, distDir) {
115
119
  * Start watching for file changes and rebuild on change
116
120
  */
117
121
  export function startWatcher(juxSource, distDir, wsClients) {
118
- console.log(`👀 Watching for changes in: ${juxSource}`);
119
-
120
- // Debounce map to prevent multiple rapid triggers
121
- const debounceTimers = new Map();
122
-
123
- function debounce(key, fn, delay = 100) {
124
- if (debounceTimers.has(key)) {
125
- clearTimeout(debounceTimers.get(key));
122
+ console.log(`👀 Watching: ${juxSource}`);
123
+
124
+ const watcher = fs.watch(juxSource, { recursive: true }, async (eventType, filename) => {
125
+ // Ignore non-.jux files and certain patterns
126
+ if (!filename ||
127
+ !filename.endsWith('.jux') ||
128
+ filename.includes('node_modules') ||
129
+ filename.includes('jux-dist') ||
130
+ filename.startsWith('.')) {
131
+ return;
126
132
  }
127
- debounceTimers.set(key, setTimeout(() => {
128
- debounceTimers.delete(key);
129
- fn();
130
- }, delay));
131
- }
132
-
133
- // Recursively watch directories
134
- function watchRecursive(dir) {
135
- if (!fs.existsSync(dir)) return;
136
-
137
- try {
138
- fs.watch(dir, { recursive: true }, (eventType, filename) => {
139
- if (!filename) return;
140
-
141
- const filePath = path.join(dir, filename);
142
- const ext = path.extname(filename);
143
-
144
- // Skip certain patterns
145
- if (filename.includes('node_modules') ||
146
- filename.includes('jux-dist') ||
147
- filename.includes('.git') ||
148
- filename.startsWith('.')) {
149
- return;
150
- }
151
-
152
- // Debounce to avoid multiple rapid events
153
- debounce(filePath, async () => {
154
- // ✅ Handle CSS files
155
- if (ext === '.css') {
156
- console.log(`\n🎨 CSS changed: ${filename}`);
157
-
158
- try {
159
- const relativePath = path.relative(juxSource, filePath);
160
- const destPath = path.join(distDir, relativePath);
161
- const destDir = path.dirname(destPath);
162
-
163
- if (!fs.existsSync(destDir)) {
164
- fs.mkdirSync(destDir, { recursive: true });
165
- }
166
-
167
- fs.copyFileSync(filePath, destPath);
168
- console.log(` ✓ Copied to: ${path.relative(process.cwd(), destPath)}`);
169
-
170
- // Notify browser to reload CSS
171
- wsClients.forEach(client => {
172
- if (client.readyState === 1) {
173
- client.send(JSON.stringify({
174
- type: 'css-reload',
175
- path: `/${relativePath}`
176
- }));
177
- }
178
- });
179
-
180
- console.log(` 🔄 Browser CSS reloaded\n`);
181
- } catch (err) {
182
- console.error(` ❌ CSS copy failed:`, err.message);
183
- }
184
- return;
185
- }
186
-
187
- // ✅ Handle .jux files
188
- if (ext === '.jux') {
189
- console.log(`\n📝 File changed: ${filename}`);
190
- console.log(' 🔨 Rebuilding...');
191
-
192
- try {
193
- await bundleJuxFilesToRouter(juxSource, distDir, { routePrefix: '' });
194
-
195
- wsClients.forEach(client => {
196
- if (client.readyState === 1) {
197
- client.send(JSON.stringify({ type: 'reload' }));
198
- }
199
- });
200
-
201
- console.log(' ✅ Rebuild complete');
202
- console.log(' 🔄 Browser reloaded\n');
203
- } catch (err) {
204
- console.error(' ❌ Rebuild failed:', err.message);
205
- }
206
- }
207
133
 
208
- // ✅ Handle .js files (non-_dev-imports.js)
209
- if (ext === '.js' && !filename.includes('_dev-imports.js')) {
210
- console.log(`\nđŸ“Ļ JS asset changed: ${filename}`);
134
+ // Debounce: If already rebuilding, queue another rebuild
135
+ if (isRebuilding) {
136
+ rebuildQueued = true;
137
+ return;
138
+ }
211
139
 
212
- try {
213
- const relativePath = path.relative(juxSource, filePath);
214
- const destPath = path.join(distDir, relativePath);
215
- const destDir = path.dirname(destPath);
140
+ isRebuilding = true;
141
+ console.log(`\n📝 File changed: ${filename}`);
216
142
 
217
- if (!fs.existsSync(destDir)) {
218
- fs.mkdirSync(destDir, { recursive: true });
219
- }
143
+ // Rebuild the entire bundle
144
+ const success = await fullRebuild(juxSource, distDir);
220
145
 
221
- fs.copyFileSync(filePath, destPath);
222
- console.log(` ✓ Copied to: ${path.relative(process.cwd(), destPath)}`);
146
+ isRebuilding = false;
223
147
 
224
- wsClients.forEach(client => {
225
- if (client.readyState === 1) {
226
- client.send(JSON.stringify({ type: 'reload' }));
227
- }
228
- });
148
+ // Notify all WebSocket clients to reload
149
+ if (success && wsClients && wsClients.length > 0) {
150
+ console.log(`🔌 Notifying ${wsClients.length} client(s) to reload`);
229
151
 
230
- console.log(` 🔄 Browser reloaded\n`);
231
- } catch (err) {
232
- console.error(` ❌ JS copy failed:`, err.message);
233
- }
152
+ // ✅ Add small delay to ensure file is fully written
153
+ setTimeout(() => {
154
+ wsClients.forEach(client => {
155
+ if (client.readyState === 1) { // OPEN
156
+ client.send(JSON.stringify({ type: 'reload' }));
234
157
  }
235
158
  });
236
- });
237
- } catch (err) {
238
- console.error(` âš ī¸ Failed to watch ${dir}:`, err.message);
159
+ }, 100);
239
160
  }
240
- }
241
161
 
242
- // Start watching
243
- watchRecursive(juxSource);
162
+ // Process queued rebuild if needed
163
+ if (rebuildQueued) {
164
+ rebuildQueued = false;
165
+ console.log('🔄 Processing queued rebuild...');
166
+ setTimeout(() => watcher.emit('change', 'change', filename), 500);
167
+ }
168
+ });
169
+
170
+ return watcher;
244
171
  }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.0.46",
4
- "description": "Build reactive UIs with pure JavaScript - no markup needed. Say goodbye to </</>>>",
3
+ "version": "1.0.47",
5
4
  "type": "module",
5
+ "description": "A JavaScript UX authorship platform",
6
6
  "main": "lib/jux.js",
7
7
  "types": "lib/jux.d.ts",
8
8
  "access": "public",
@@ -39,20 +39,29 @@
39
39
  "jux": "./bin/cli.js"
40
40
  },
41
41
  "keywords": [
42
- "jux",
43
- "ui-framework",
44
- "reactive",
42
+ "ui",
43
+ "ux",
45
44
  "components",
46
- "no-markup",
47
- "fluent-api",
48
- "state-management",
49
- "hot-reload",
50
- "typescript"
45
+ "framework",
46
+ "reactive",
47
+ "javascript"
51
48
  ],
52
- "homepage": "https://juxscript.com",
53
- "bugs": {
54
- "url": "https://github.com/jux/juxscript/issues"
49
+ "scripts": {
50
+ "build": "tsc",
51
+ "dev": "tsc --watch",
52
+ "prepublishOnly": "npm run build"
53
+ },
54
+ "dependencies": {
55
+ "chokidar": "^3.5.3",
56
+ "esbuild": "^0.19.0",
57
+ "express": "^4.18.2",
58
+ "glob": "^13.0.0",
59
+ "ws": "^8.13.0"
55
60
  },
56
- "author": "Your Name <your.email@example.com>",
57
- "license": "MIT"
61
+ "devDependencies": {
62
+ "@types/express": "^4.17.17",
63
+ "@types/node": "^20.0.0",
64
+ "@types/ws": "^8.5.5",
65
+ "typescript": "^5.0.0"
66
+ }
58
67
  }
@@ -1,94 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
-
4
- /**
5
- * Default JUX configuration
6
- */
7
- export const defaultConfig = {
8
- // Source directory for .jux files
9
- sourceDir: 'jux',
10
-
11
- // Output directory for built files
12
- distDir: 'jux-dist',
13
-
14
- // Dev server ports
15
- ports: {
16
- http: 3000,
17
- ws: 3001
18
- },
19
-
20
- // Build options
21
- build: {
22
- minify: false,
23
- sourcemap: true
24
- },
25
-
26
- // Bootstrap functions (run before app starts)
27
- bootstrap: []
28
- };
29
-
30
- /**
31
- * Load juxconfig.js from project root
32
- * @param {string} projectRoot - Project root directory
33
- * @returns {object} Merged configuration
34
- */
35
- export async function loadConfig(projectRoot) {
36
- const configPath = path.join(projectRoot, 'juxconfig.js');
37
-
38
- if (!fs.existsSync(configPath)) {
39
- console.log('â„šī¸ No juxconfig.js found, using defaults');
40
- return defaultConfig;
41
- }
42
-
43
- try {
44
- console.log(`📋 Loading config from: ${configPath}`);
45
-
46
- // Dynamic import for ES modules
47
- const configUrl = `file://${configPath}`;
48
- const { default: userConfig } = await import(configUrl);
49
-
50
- // Merge with defaults
51
- const config = {
52
- ...defaultConfig,
53
- ...userConfig,
54
- ports: {
55
- ...defaultConfig.ports,
56
- ...(userConfig.ports || {})
57
- },
58
- build: {
59
- ...defaultConfig.build,
60
- ...(userConfig.build || {})
61
- }
62
- };
63
-
64
- console.log(` ✓ Config loaded`);
65
- console.log(` Source: ${config.sourceDir}`);
66
- console.log(` Output: ${config.distDir}\n`);
67
-
68
- return config;
69
- } catch (err) {
70
- console.warn(`âš ī¸ Failed to load juxconfig.js:`, err.message);
71
- console.warn(` Using default configuration\n`);
72
- return defaultConfig;
73
- }
74
- }
75
-
76
- /**
77
- * Run bootstrap functions from config
78
- * @param {Array<Function>} bootstrapFns - Array of bootstrap functions
79
- */
80
- export async function runBootstrap(bootstrapFns) {
81
- if (!bootstrapFns || bootstrapFns.length === 0) return;
82
-
83
- console.log(`🚀 Running ${bootstrapFns.length} bootstrap function(s)...`);
84
-
85
- for (const fn of bootstrapFns) {
86
- try {
87
- await fn();
88
- } catch (err) {
89
- console.error(` ❌ Bootstrap function failed:`, err.message);
90
- }
91
- }
92
-
93
- console.log(` ✓ Bootstrap complete\n`);
94
- }