juxscript 1.0.89 → 1.0.91
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/bin/cli.js +243 -92
- package/index.js +22 -0
- package/lib/globals.d.ts +7 -5
- package/machinery/build3.js +21 -0
- package/machinery/compiler3.js +638 -0
- package/machinery/serve.js +255 -0
- package/machinery/watcher.js +53 -64
- package/package.json +11 -3
- package/types/css.d.ts +8 -0
- package/lib/componentsv2/index.d.ts +0 -39
- package/lib/componentsv2/index.d.ts.map +0 -1
- package/lib/componentsv2/index.js +0 -221
- package/lib/componentsv2/index.js.map +0 -1
- package/lib/componentsv2/index.ts +0 -253
- package/machinery/ast.js +0 -347
- package/machinery/compiler.js +0 -706
- package/machinery/diagnose.js +0 -72
- package/machinery/doc-generator.js +0 -136
- package/machinery/imports.js +0 -155
- package/machinery/jux-module-pattern.md +0 -118
- package/machinery/server.js +0 -86
- package/machinery/ts-shim.js +0 -46
- package/machinery/verifier.js +0 -135
- package/tests/dropdown-test.js +0 -25
- package/tests/juxerrors/bad_syntax.jux +0 -8
- package/tests/juxerrors/ghost_dep.jux +0 -10
- package/tests/server_plugin_test.ts +0 -191
package/bin/cli.js
CHANGED
|
@@ -3,124 +3,275 @@
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import fs from 'fs';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
|
+
import { spawn } from 'child_process';
|
|
6
7
|
|
|
7
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
8
9
|
const __dirname = path.dirname(__filename);
|
|
9
|
-
const
|
|
10
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
const [, , command, ...args] = process.argv;
|
|
13
|
+
|
|
14
|
+
// ═══════════════════════════════════════════════════════════════
|
|
15
|
+
// COMMAND: create <project-name>
|
|
16
|
+
// Creates a new JUX project from the template
|
|
17
|
+
// ═══════════════════════════════════════════════════════════════
|
|
18
|
+
async function createProject(projectName) {
|
|
19
|
+
if (!projectName) {
|
|
20
|
+
console.error('❌ Usage: jux create <project-name>');
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const targetDir = path.join(process.cwd(), projectName);
|
|
25
|
+
|
|
26
|
+
if (fs.existsSync(targetDir)) {
|
|
27
|
+
console.error(`❌ Directory "${projectName}" already exists.`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
console.log(`\n📦 Creating JUX project: ${projectName}\n`);
|
|
32
|
+
|
|
33
|
+
// 1. Create project directory
|
|
34
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
35
|
+
|
|
36
|
+
// 2. Copy template from /create folder
|
|
37
|
+
const templateDir = path.join(PACKAGE_ROOT, 'create');
|
|
38
|
+
if (fs.existsSync(templateDir)) {
|
|
39
|
+
copyDirRecursive(templateDir, path.join(targetDir, 'jux'));
|
|
40
|
+
console.log(' ✓ Copied template files');
|
|
41
|
+
} else {
|
|
42
|
+
// Fallback: create minimal starter
|
|
43
|
+
const juxDir = path.join(targetDir, 'jux');
|
|
44
|
+
fs.mkdirSync(juxDir, { recursive: true });
|
|
45
|
+
fs.writeFileSync(
|
|
46
|
+
path.join(juxDir, 'index.jux'),
|
|
47
|
+
`import { Element } from 'juxscript';\n\nElement('welcome', { tagName: 'h1' })\n .text('Welcome to JUX!')\n .render('app');\n`
|
|
48
|
+
);
|
|
49
|
+
console.log(' ✓ Created starter file');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 3. Copy example config
|
|
53
|
+
const configSrc = path.join(PACKAGE_ROOT, 'juxconfig.example.js');
|
|
54
|
+
const configDest = path.join(targetDir, 'juxconfig.js');
|
|
55
|
+
if (fs.existsSync(configSrc)) {
|
|
56
|
+
fs.copyFileSync(configSrc, configDest);
|
|
57
|
+
console.log(' ✓ Created juxconfig.js');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 4. Create package.json
|
|
61
|
+
const pkg = {
|
|
62
|
+
name: projectName,
|
|
63
|
+
version: '0.1.0',
|
|
64
|
+
type: 'module',
|
|
65
|
+
scripts: {
|
|
66
|
+
dev: 'jux serve --hot',
|
|
67
|
+
build: 'jux build',
|
|
68
|
+
start: 'jux serve'
|
|
69
|
+
},
|
|
70
|
+
dependencies: {
|
|
71
|
+
juxscript: 'latest'
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify(pkg, null, 2));
|
|
75
|
+
console.log(' ✓ Created package.json');
|
|
76
|
+
|
|
77
|
+
// 5. Create .gitignore
|
|
78
|
+
fs.writeFileSync(
|
|
79
|
+
path.join(targetDir, '.gitignore'),
|
|
80
|
+
`.jux-dist/\nnode_modules/\n.DS_Store\n.env\n*.log\n`
|
|
81
|
+
);
|
|
82
|
+
console.log(' ✓ Created .gitignore');
|
|
83
|
+
|
|
84
|
+
// 6. Install dependencies
|
|
85
|
+
console.log('\n📥 Installing dependencies...\n');
|
|
86
|
+
try {
|
|
87
|
+
const { execSync } = await import('child_process');
|
|
88
|
+
execSync('npm install', { cwd: targetDir, stdio: 'inherit' });
|
|
89
|
+
} catch (err) {
|
|
90
|
+
console.warn('⚠️ npm install failed. Run it manually.');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log(`
|
|
94
|
+
✅ Project created successfully!
|
|
95
|
+
|
|
96
|
+
cd ${projectName}
|
|
97
|
+
npm run dev
|
|
98
|
+
|
|
99
|
+
`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ═══════════════════════════════════════════════════════════════
|
|
103
|
+
// COMMAND: init
|
|
104
|
+
// Initializes JUX in an existing project
|
|
105
|
+
// ═══════════════════════════════════════════════════════════════
|
|
106
|
+
function initProject() {
|
|
107
|
+
const targetDir = process.cwd();
|
|
108
|
+
|
|
109
|
+
console.log('\n📦 Initializing JUX in current directory\n');
|
|
110
|
+
|
|
111
|
+
// 1. Copy template to ./jux
|
|
112
|
+
const templateDir = path.join(PACKAGE_ROOT, 'create');
|
|
12
113
|
const juxDir = path.join(targetDir, 'jux');
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (fs.existsSync(presetDir)) {
|
|
17
|
-
fs.readdirSync(presetDir).forEach(file => {
|
|
18
|
-
if (file.startsWith('.')) return;
|
|
19
|
-
if (fs.statSync(path.join(presetDir, file)).isFile()) {
|
|
20
|
-
fs.copyFileSync(path.join(presetDir, file), path.join(juxDir, file));
|
|
21
|
-
copied++;
|
|
22
|
-
}
|
|
23
|
-
});
|
|
114
|
+
|
|
115
|
+
if (!fs.existsSync(juxDir)) {
|
|
116
|
+
fs.mkdirSync(juxDir, { recursive: true });
|
|
24
117
|
}
|
|
25
|
-
|
|
26
|
-
|
|
118
|
+
|
|
119
|
+
if (fs.existsSync(templateDir)) {
|
|
120
|
+
copyDirRecursive(templateDir, juxDir);
|
|
121
|
+
console.log(' ✓ Copied template files to ./jux');
|
|
122
|
+
} else {
|
|
123
|
+
fs.writeFileSync(
|
|
124
|
+
path.join(juxDir, 'index.jux'),
|
|
125
|
+
`import { Element } from 'juxscript';\n\nElement('welcome', { tagName: 'h1' })\n .text('Welcome to JUX!')\n .render('app');\n`
|
|
126
|
+
);
|
|
127
|
+
console.log(' ✓ Created starter file');
|
|
27
128
|
}
|
|
28
|
-
|
|
129
|
+
|
|
130
|
+
// 2. Copy config if not exists
|
|
29
131
|
const configDest = path.join(targetDir, 'juxconfig.js');
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
|
|
132
|
+
if (!fs.existsSync(configDest)) {
|
|
133
|
+
const configSrc = path.join(PACKAGE_ROOT, 'juxconfig.example.js');
|
|
134
|
+
if (fs.existsSync(configSrc)) {
|
|
135
|
+
fs.copyFileSync(configSrc, configDest);
|
|
136
|
+
console.log(' ✓ Created juxconfig.js');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 3. Update package.json scripts if exists
|
|
33
141
|
const pkgPath = path.join(targetDir, 'package.json');
|
|
34
142
|
if (fs.existsSync(pkgPath)) {
|
|
35
143
|
try {
|
|
36
144
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
37
145
|
pkg.scripts = pkg.scripts || {};
|
|
38
146
|
let modified = false;
|
|
39
|
-
|
|
147
|
+
|
|
148
|
+
if (!pkg.scripts.dev) { pkg.scripts.dev = 'jux serve --hot'; modified = true; }
|
|
40
149
|
if (!pkg.scripts.build) { pkg.scripts.build = 'jux build'; modified = true; }
|
|
41
|
-
if (
|
|
42
|
-
|
|
150
|
+
if (!pkg.scripts.start) { pkg.scripts.start = 'jux serve'; modified = true; }
|
|
151
|
+
|
|
152
|
+
if (modified) {
|
|
153
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
|
|
154
|
+
console.log(' ✓ Updated package.json scripts');
|
|
155
|
+
}
|
|
156
|
+
} catch (e) {
|
|
157
|
+
console.warn(' ⚠️ Could not update package.json');
|
|
158
|
+
}
|
|
43
159
|
}
|
|
160
|
+
|
|
161
|
+
console.log('\n✅ Initialized. Run: npm run dev\n');
|
|
44
162
|
}
|
|
45
163
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
164
|
+
// ═══════════════════════════════════════════════════════════════
|
|
165
|
+
// COMMAND: build
|
|
166
|
+
// Delegates to machinery/build3.js
|
|
167
|
+
// ═══════════════════════════════════════════════════════════════
|
|
168
|
+
function runBuild() {
|
|
169
|
+
const buildScript = path.join(PACKAGE_ROOT, 'machinery', 'build3.js');
|
|
170
|
+
|
|
171
|
+
if (!fs.existsSync(buildScript)) {
|
|
172
|
+
console.error('❌ Build script not found:', buildScript);
|
|
173
|
+
process.exit(1);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const child = spawn('node', [buildScript, ...args], {
|
|
177
|
+
cwd: process.cwd(),
|
|
178
|
+
stdio: 'inherit',
|
|
179
|
+
env: process.env
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
child.on('close', (code) => process.exit(code || 0));
|
|
62
183
|
}
|
|
63
184
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
185
|
+
// ═══════════════════════════════════════════════════════════════
|
|
186
|
+
// COMMAND: serve [--hot]
|
|
187
|
+
// Delegates to machinery/serve.js
|
|
188
|
+
// ═══════════════════════════════════════════════════════════════
|
|
189
|
+
function runServe() {
|
|
190
|
+
const serveScript = path.join(PACKAGE_ROOT, 'machinery', 'serve.js');
|
|
191
|
+
|
|
192
|
+
if (!fs.existsSync(serveScript)) {
|
|
193
|
+
console.error('❌ Serve script not found:', serveScript);
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Pass through all args (including --hot)
|
|
198
|
+
const child = spawn('node', [serveScript, ...args], {
|
|
199
|
+
cwd: process.cwd(),
|
|
200
|
+
stdio: 'inherit',
|
|
201
|
+
env: process.env
|
|
202
|
+
});
|
|
68
203
|
|
|
69
|
-
|
|
70
|
-
const configPath = path.join(projectRoot, 'juxconfig.js');
|
|
71
|
-
let rawConfig = {};
|
|
72
|
-
if (fs.existsSync(configPath)) { try { const module = await import(configPath); rawConfig = module.default || module.config || {}; } catch (err) { } }
|
|
73
|
-
return resolveConfig(rawConfig, projectRoot);
|
|
204
|
+
child.on('close', (code) => process.exit(code || 0));
|
|
74
205
|
}
|
|
75
206
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
get juxLib() { return path.resolve(this.packageRoot, 'lib'); },
|
|
84
|
-
get frontendDist() { return path.join(this.projectRoot, config.directories?.distribution || '.jux-dist'); }
|
|
85
|
-
};
|
|
207
|
+
// ═══════════════════════════════════════════════════════════════
|
|
208
|
+
// UTILITIES
|
|
209
|
+
// ═══════════════════════════════════════════════════════════════
|
|
210
|
+
function copyDirRecursive(src, dest) {
|
|
211
|
+
if (!fs.existsSync(dest)) {
|
|
212
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
213
|
+
}
|
|
86
214
|
|
|
87
|
-
|
|
88
|
-
if (
|
|
89
|
-
if (fs.existsSync(PATHS.frontendDist)) fs.rmSync(PATHS.frontendDist, { recursive: true, force: true });
|
|
90
|
-
fs.mkdirSync(PATHS.frontendDist, { recursive: true });
|
|
215
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
216
|
+
if (entry.name.startsWith('.')) continue; // Skip hidden files
|
|
91
217
|
|
|
92
|
-
|
|
93
|
-
|
|
218
|
+
const srcPath = path.join(src, entry.name);
|
|
219
|
+
const destPath = path.join(dest, entry.name);
|
|
94
220
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
await transpileProjectTypeScript(PATHS.juxSource, PATHS.frontendDist);
|
|
100
|
-
const bundleResult = await bundleJuxFilesToRouter(PATHS.juxSource, PATHS.frontendDist, { routePrefix: '', config });
|
|
101
|
-
generateIndexHtml(PATHS.frontendDist, bundleResult, { isDev: isServe, wsPort: isServe ? wsPort : undefined });
|
|
102
|
-
if (!verifyStaticBuild(PATHS.frontendDist)) { console.error('🛑 Critical Build Failure.'); process.exit(1); }
|
|
103
|
-
|
|
104
|
-
const fileCount = fs.readdirSync(PATHS.juxSource).filter(f => f.endsWith('.jux')).length; // Approximate check
|
|
105
|
-
console.log(`✅ Done. (${fileCount} files, ${(fs.statSync(path.join(PATHS.frontendDist, 'main.js')).size / 1024).toFixed(1)}KB)`);
|
|
106
|
-
} catch (err) {
|
|
107
|
-
console.log('❌ Failed.');
|
|
108
|
-
console.error(err.message);
|
|
109
|
-
process.exit(1);
|
|
221
|
+
if (entry.isDirectory()) {
|
|
222
|
+
copyDirRecursive(srcPath, destPath);
|
|
223
|
+
} else {
|
|
224
|
+
fs.copyFileSync(srcPath, destPath);
|
|
110
225
|
}
|
|
111
226
|
}
|
|
227
|
+
}
|
|
112
228
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
229
|
+
function showHelp() {
|
|
230
|
+
console.log(`
|
|
231
|
+
JUX CLI
|
|
232
|
+
|
|
233
|
+
Usage:
|
|
234
|
+
jux create <name> Create a new JUX project
|
|
235
|
+
jux init Initialize JUX in current directory
|
|
236
|
+
jux build Build for production
|
|
237
|
+
jux serve Start production server
|
|
238
|
+
jux serve --hot Start dev server with hot reload
|
|
239
|
+
|
|
240
|
+
Options:
|
|
241
|
+
--help, -h Show this help message
|
|
242
|
+
`);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// ═══════════════════════════════════════════════════════════════
|
|
246
|
+
// MAIN ROUTER
|
|
247
|
+
// ═══════════════════════════════════════════════════════════════
|
|
248
|
+
switch (command) {
|
|
249
|
+
case 'create':
|
|
250
|
+
createProject(args[0]);
|
|
251
|
+
break;
|
|
252
|
+
|
|
253
|
+
case 'init':
|
|
254
|
+
case 'install':
|
|
255
|
+
initProject();
|
|
256
|
+
break;
|
|
257
|
+
|
|
258
|
+
case 'build':
|
|
259
|
+
runBuild();
|
|
260
|
+
break;
|
|
261
|
+
|
|
262
|
+
case 'serve':
|
|
263
|
+
case 'dev':
|
|
264
|
+
runServe();
|
|
265
|
+
break;
|
|
266
|
+
|
|
267
|
+
case '--help':
|
|
268
|
+
case '-h':
|
|
269
|
+
case undefined:
|
|
270
|
+
showHelp();
|
|
271
|
+
break;
|
|
272
|
+
|
|
273
|
+
default:
|
|
274
|
+
console.error(`❌ Unknown command: ${command}`);
|
|
275
|
+
showHelp();
|
|
276
|
+
process.exit(1);
|
|
277
|
+
}
|
package/index.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JUX - JavaScript UX Authorship Platform
|
|
3
|
+
*
|
|
4
|
+
* Main entry point for the juxscript package.
|
|
5
|
+
* Re-exports all public components and utilities.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Core Components
|
|
9
|
+
export { Element } from './lib/componentsv2/element/component.js';
|
|
10
|
+
export { Grid } from './lib/componentsv2/grid/component.js';
|
|
11
|
+
export { Input } from './lib/componentsv2/input/component.js';
|
|
12
|
+
export { List } from './lib/componentsv2/list/component.js';
|
|
13
|
+
|
|
14
|
+
// Base Classes (for extension)
|
|
15
|
+
// Note: BaseState is a TypeScript interface, not exported at runtime
|
|
16
|
+
export { BaseEngine } from './lib/componentsv2/base/BaseEngine.js';
|
|
17
|
+
export { BaseSkin } from './lib/componentsv2/base/BaseSkin.js';
|
|
18
|
+
export { State } from './lib/componentsv2/base/State.js';
|
|
19
|
+
export { GlobalBus } from './lib/componentsv2/base/GlobalBus.js';
|
|
20
|
+
|
|
21
|
+
// Utilities
|
|
22
|
+
export { validateOptions } from './lib/componentsv2/base/OptionsContract.js';
|
package/lib/globals.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* JUX Type Exports
|
|
3
|
+
* Re-export types for TypeScript consumers
|
|
4
|
+
*/
|
|
5
|
+
export type { BaseState } from './componentsv2/base/BaseEngine.js';
|
|
6
|
+
export type { JuxServiceContract } from './componentsv2/base/BaseEngine.js';
|
|
7
|
+
export type { OptionsContractSchema, OptionDefinition, ValidationResult } from './componentsv2/base/OptionsContract.js';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { JuxCompiler } from './compiler3.js';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
// Resolve paths relative to CWD (user's project)
|
|
5
|
+
const PROJECT_ROOT = process.cwd();
|
|
6
|
+
|
|
7
|
+
const compiler = new JuxCompiler({
|
|
8
|
+
srcDir: path.resolve(PROJECT_ROOT, 'jux'),
|
|
9
|
+
distDir: path.resolve(PROJECT_ROOT, '.jux-dist')
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
compiler.build()
|
|
13
|
+
.then(result => {
|
|
14
|
+
if (!result.success) {
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
})
|
|
18
|
+
.catch(err => {
|
|
19
|
+
console.error('❌ Build failed:', err.message);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
});
|