juxscript 1.0.67 → 1.0.69
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 +66 -307
- package/create/index.jux +79 -90
- package/create/layout.jux +38 -18
- package/lib/componentsv2/element/component.js +23 -0
- package/lib/componentsv2/element/component.js.map +1 -0
- package/lib/componentsv2/element/component.ts +32 -0
- package/lib/componentsv2/element/engine.js +66 -0
- package/lib/componentsv2/element/engine.js.map +1 -0
- package/lib/componentsv2/element/engine.ts +88 -0
- package/lib/componentsv2/element/skin.js +53 -0
- package/lib/componentsv2/element/skin.js.map +1 -0
- package/lib/componentsv2/element/skin.ts +64 -0
- package/lib/componentsv2/element/structure.css +16 -0
- package/lib/componentsv2/index.js +3 -2
- package/lib/componentsv2/index.js.map +1 -1
- package/lib/componentsv2/index.ts +4 -6
- package/lib/componentsv2/tools/Scaffold.js +0 -16
- package/machinery/compiler.js +103 -739
- package/machinery/server.js +16 -112
- package/machinery/watcher.js +16 -106
- package/package.json +1 -1
- package/create/all.jux +0 -343
package/bin/cli.js
CHANGED
|
@@ -6,362 +6,121 @@ import { fileURLToPath } from 'url';
|
|
|
6
6
|
|
|
7
7
|
const __filename = fileURLToPath(import.meta.url);
|
|
8
8
|
const __dirname = path.dirname(__filename);
|
|
9
|
-
|
|
10
9
|
const command = process.argv[2];
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
function scaffoldProject(targetDir, packageRoot) {
|
|
12
|
+
const juxDir = path.join(targetDir, 'jux');
|
|
13
|
+
if (!fs.existsSync(juxDir)) fs.mkdirSync(juxDir, { recursive: true });
|
|
14
|
+
const presetDir = path.join(packageRoot, 'create');
|
|
15
|
+
let copied = 0;
|
|
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
|
+
});
|
|
24
|
+
}
|
|
25
|
+
if (copied === 0 && !fs.existsSync(path.join(juxDir, 'index.jux'))) {
|
|
26
|
+
fs.writeFileSync(path.join(juxDir, 'index.jux'), `import { Element } from 'juxscript';\nElement('welcome', { tag: 'h1' }).text('Welcome to JUX!').render('app');\n`);
|
|
27
|
+
}
|
|
28
|
+
const configSrc = path.join(packageRoot, 'juxconfig.example.js');
|
|
29
|
+
const configDest = path.join(targetDir, 'juxconfig.js');
|
|
30
|
+
if (fs.existsSync(configSrc) && !fs.existsSync(configDest)) fs.copyFileSync(configSrc, configDest);
|
|
31
|
+
const gitIgnore = path.join(targetDir, '.gitignore');
|
|
32
|
+
if (!fs.existsSync(gitIgnore)) fs.writeFileSync(gitIgnore, `.jux-dist/\nnode_modules/\n.DS_Store\n.env\n*.log\n`);
|
|
33
|
+
const pkgPath = path.join(targetDir, 'package.json');
|
|
34
|
+
if (fs.existsSync(pkgPath)) {
|
|
35
|
+
try {
|
|
36
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
37
|
+
pkg.scripts = pkg.scripts || {};
|
|
38
|
+
let modified = false;
|
|
39
|
+
if (!pkg.scripts.dev) { pkg.scripts.dev = 'jux serve'; modified = true; }
|
|
40
|
+
if (!pkg.scripts.build) { pkg.scripts.build = 'jux build'; modified = true; }
|
|
41
|
+
if (modified) fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
|
|
42
|
+
} catch (e) { }
|
|
43
|
+
}
|
|
44
|
+
}
|
|
15
45
|
|
|
16
46
|
if (command === 'create') {
|
|
17
|
-
// ✅ FIX: Removed timestamp fallback from default name
|
|
18
47
|
const projectName = process.argv[3] || 'my-jux-app';
|
|
19
|
-
|
|
20
48
|
const projectPath = path.join(process.cwd(), projectName);
|
|
21
|
-
|
|
22
|
-
console.log(`
|
|
23
|
-
╔═══════════════════════════════════════════════════════╗
|
|
24
|
-
║ ║
|
|
25
|
-
║ 🎨 Welcome to JUX ║
|
|
26
|
-
║ ║
|
|
27
|
-
║ Creating your new JUX project... ║
|
|
28
|
-
║ ║
|
|
29
|
-
╚═══════════════════════════════════════════════════════╝
|
|
30
|
-
`);
|
|
31
|
-
|
|
32
|
-
if (fs.existsSync(projectPath)) {
|
|
33
|
-
console.error(`❌ Directory "${projectName}" already exists.`);
|
|
34
|
-
console.error(` Please choose a different name or remove the existing directory.\n`);
|
|
35
|
-
process.exit(1);
|
|
36
|
-
}
|
|
37
|
-
|
|
49
|
+
if (fs.existsSync(projectPath)) { console.error(`❌ Directory "${projectName}" already exists.`); process.exit(1); }
|
|
38
50
|
try {
|
|
39
51
|
const { execSync } = await import('child_process');
|
|
40
|
-
|
|
41
|
-
console.log(`📁 Creating directory: ${projectName}`);
|
|
42
52
|
fs.mkdirSync(projectPath, { recursive: true });
|
|
43
53
|
process.chdir(projectPath);
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
build: 'jux build'
|
|
53
|
-
},
|
|
54
|
-
dependencies: {
|
|
55
|
-
juxscript: 'latest'
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
fs.writeFileSync('package.json', JSON.stringify(packageJson, null, 2));
|
|
59
|
-
console.log(` ✓ package.json created`);
|
|
60
|
-
|
|
61
|
-
console.log(`\n📥 Installing juxscript...\n`);
|
|
62
|
-
try {
|
|
63
|
-
execSync('npm install', { stdio: 'inherit' });
|
|
64
|
-
console.log(`\n ✓ Dependencies installed`);
|
|
65
|
-
} catch (err) {
|
|
66
|
-
console.error(`\n ⚠️ npm install failed, but continuing...`);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
execSync('npx jux init', { stdio: 'inherit' });
|
|
70
|
-
|
|
71
|
-
console.log(`\n📝 Creating .gitignore...`);
|
|
72
|
-
fs.writeFileSync('.gitignore', `.jux-dist/\nnode_modules/\n.DS_Store\n.env\n*.log\n`);
|
|
73
|
-
console.log(` ✓ .gitignore created`);
|
|
74
|
-
|
|
75
|
-
console.log(`
|
|
76
|
-
╔═══════════════════════════════════════════════════════╗
|
|
77
|
-
║ ║
|
|
78
|
-
║ ✅ Project created successfully! ║
|
|
79
|
-
║ ║
|
|
80
|
-
╚═══════════════════════════════════════════════════════╝
|
|
81
|
-
|
|
82
|
-
📚 Resources:
|
|
83
|
-
Documentation: [coming soon]
|
|
84
|
-
GitHub: https://github.com/juxscript/jux
|
|
85
|
-
Examples: https://github.com/juxscript/examples
|
|
86
|
-
|
|
87
|
-
⭐ If you find JUX useful, please star us on GitHub!
|
|
88
|
-
🔒 Security: Report issues to security@juxscript.com [placeholder]
|
|
89
|
-
|
|
90
|
-
Say goodbye to markup </</>>>.
|
|
91
|
-
Happy javascripting your frontend! 🎉
|
|
92
|
-
|
|
93
|
-
Next steps:
|
|
94
|
-
cd ${projectName}
|
|
95
|
-
npm run dev
|
|
96
|
-
`);
|
|
97
|
-
|
|
98
|
-
} catch (err) {
|
|
99
|
-
console.error(`\n❌ Project creation failed:`, err.message);
|
|
100
|
-
|
|
101
|
-
if (fs.existsSync(projectPath)) {
|
|
102
|
-
try {
|
|
103
|
-
process.chdir('..');
|
|
104
|
-
fs.rmSync(projectPath, { recursive: true, force: true });
|
|
105
|
-
console.log(` ✓ Cleaned up failed project directory\n`);
|
|
106
|
-
} catch (cleanupErr) {
|
|
107
|
-
console.error(` ⚠️ Could not clean up directory\n`);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
process.exit(1);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
process.exit(0); // ✅ Exit after create completes
|
|
54
|
+
const pkg = { name: projectName, version: '0.1.0', type: 'module', scripts: { dev: 'jux serve', build: 'jux build' }, dependencies: { juxscript: 'latest' } };
|
|
55
|
+
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2));
|
|
56
|
+
const packageRoot = path.resolve(__dirname, '..');
|
|
57
|
+
scaffoldProject(projectPath, packageRoot);
|
|
58
|
+
try { execSync('npm install', { stdio: 'inherit' }); } catch (err) { }
|
|
59
|
+
console.log(`\n✅ Project created! Run: cd ${projectName} && npm run dev\n`);
|
|
60
|
+
} catch (err) { console.error(`\n❌ Failed:`, err.message); process.exit(1); }
|
|
61
|
+
process.exit(0);
|
|
115
62
|
}
|
|
116
63
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
import {
|
|
122
|
-
copyLibToOutput,
|
|
123
|
-
copyProjectAssets,
|
|
124
|
-
transpileProjectTypeScript,
|
|
125
|
-
copyPresetsToOutput,
|
|
126
|
-
bundleJuxFilesToRouter,
|
|
127
|
-
generateIndexHtml
|
|
128
|
-
} from '../machinery/compiler.js';
|
|
129
|
-
import { verifyStaticBuild } from '../machinery/verifier.js'; // ✅ Import Verifier
|
|
130
|
-
import { resolveConfig } from '../machinery/config.js'; // ✅ Import Config Resolver
|
|
64
|
+
import { copyLibToOutput, copyProjectAssets, transpileProjectTypeScript, copyPresetsToOutput, bundleJuxFilesToRouter, generateIndexHtml } from '../machinery/compiler.js';
|
|
65
|
+
import { verifyStaticBuild } from '../machinery/verifier.js';
|
|
66
|
+
import { resolveConfig } from '../machinery/config.js';
|
|
131
67
|
import { start } from '../machinery/server.js';
|
|
132
|
-
// ❌ REMOVED static import of config to prevent crash on incorrect path
|
|
133
68
|
|
|
134
|
-
/**
|
|
135
|
-
* Dynamically load and normalize juxconfig.js from the user's project root
|
|
136
|
-
*/
|
|
137
69
|
async function loadUserConfig(projectRoot) {
|
|
138
70
|
const configPath = path.join(projectRoot, 'juxconfig.js');
|
|
139
71
|
let rawConfig = {};
|
|
140
|
-
|
|
141
|
-
if (fs.existsSync(configPath)) {
|
|
142
|
-
try {
|
|
143
|
-
const module = await import(configPath);
|
|
144
|
-
// Support export default defineConfig({...}) OR export const config = {...}
|
|
145
|
-
rawConfig = module.default || module.config || {};
|
|
146
|
-
} catch (err) {
|
|
147
|
-
console.warn(`⚠️ Error loading juxconfig.js: ${err.message}. Using defaults.`);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// ✅ Normalize DX sugar (short paths, missing extensions, aliases) into strict config
|
|
72
|
+
if (fs.existsSync(configPath)) { try { const module = await import(configPath); rawConfig = module.default || module.config || {}; } catch (err) { } }
|
|
152
73
|
return resolveConfig(rawConfig, projectRoot);
|
|
153
74
|
}
|
|
154
75
|
|
|
155
|
-
// Recursively find .jux files
|
|
156
|
-
function findJuxFiles(dir, fileList = []) {
|
|
157
|
-
if (!fs.existsSync(dir)) return fileList;
|
|
158
|
-
const files = fs.readdirSync(dir);
|
|
159
|
-
files.forEach(file => {
|
|
160
|
-
const filePath = path.join(dir, file);
|
|
161
|
-
const stat = fs.statSync(filePath);
|
|
162
|
-
if (stat.isDirectory()) {
|
|
163
|
-
if (file !== 'node_modules' && file !== 'jux-dist' && file !== '.git' && file !== 'server') {
|
|
164
|
-
findJuxFiles(filePath, fileList);
|
|
165
|
-
}
|
|
166
|
-
} else if (file.endsWith('.jux')) {
|
|
167
|
-
fileList.push(filePath);
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
return fileList;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
174
|
-
// MAIN EXECUTION BLOCK (Async)
|
|
175
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
176
76
|
(async () => {
|
|
177
|
-
// 1. Resolve Configuration relative to CWD
|
|
178
77
|
const projectRoot = process.cwd();
|
|
179
78
|
const config = await loadUserConfig(projectRoot);
|
|
180
|
-
|
|
181
|
-
// 2. Define PATHS Contract
|
|
182
79
|
const PATHS = {
|
|
183
|
-
packageRoot: path.resolve(__dirname, '..'),
|
|
80
|
+
packageRoot: path.resolve(__dirname, '..'),
|
|
184
81
|
projectRoot: projectRoot,
|
|
185
|
-
|
|
186
|
-
// Use config values
|
|
187
82
|
get juxSource() { return path.join(this.projectRoot, config.directories?.source || 'jux'); },
|
|
188
83
|
get juxLib() { return path.resolve(this.packageRoot, 'lib'); },
|
|
189
84
|
get frontendDist() { return path.join(this.projectRoot, config.directories?.distribution || '.jux-dist'); }
|
|
190
85
|
};
|
|
191
86
|
|
|
192
|
-
console.log('📍 JUX Paths:');
|
|
193
|
-
console.log(` Package: ${PATHS.packageRoot}`);
|
|
194
|
-
console.log(` Project: ${PATHS.projectRoot}`);
|
|
195
|
-
console.log(` Source: ${PATHS.juxSource}`);
|
|
196
|
-
console.log(` Output: ${PATHS.frontendDist}`);
|
|
197
|
-
console.log(` Lib: ${PATHS.juxLib}\n`);
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Build the entire JUX project
|
|
201
|
-
*/
|
|
202
87
|
async function buildProject(isServe = false, wsPort = 3001) {
|
|
203
|
-
|
|
204
|
-
|
|
88
|
+
if (!fs.existsSync(PATHS.juxSource)) { console.error(`❌ Source not found: ${PATHS.juxSource}`); process.exit(1); }
|
|
89
|
+
if (fs.existsSync(PATHS.frontendDist)) fs.rmSync(PATHS.frontendDist, { recursive: true, force: true });
|
|
90
|
+
fs.mkdirSync(PATHS.frontendDist, { recursive: true });
|
|
205
91
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
if (!fs.existsSync(PATHS.juxSource)) {
|
|
209
|
-
console.error(`❌ Source directory not found: ${PATHS.juxSource}`);
|
|
210
|
-
console.error(` Please create a directory named '${config.directories?.source || 'jux'}'`);
|
|
211
|
-
process.exit(1);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Clean and create frontend dist
|
|
215
|
-
if (fs.existsSync(PATHS.frontendDist)) {
|
|
216
|
-
fs.rmSync(PATHS.frontendDist, { recursive: true, force: true });
|
|
217
|
-
}
|
|
218
|
-
fs.mkdirSync(PATHS.frontendDist, { recursive: true });
|
|
92
|
+
// ✅ Clean Log
|
|
93
|
+
process.stdout.write('🔨 Building JUX... ');
|
|
219
94
|
|
|
220
|
-
|
|
221
|
-
const libStartTime = performance.now();
|
|
95
|
+
try {
|
|
222
96
|
await copyLibToOutput(PATHS.juxLib, PATHS.frontendDist);
|
|
223
|
-
const libTime = performance.now() - libStartTime;
|
|
224
|
-
console.log(`⏱️ Lib copy time: ${libTime.toFixed(0)}ms\n`);
|
|
225
|
-
|
|
226
|
-
const presetsStartTime = performance.now();
|
|
227
97
|
await copyPresetsToOutput(PATHS.packageRoot, PATHS.frontendDist);
|
|
228
|
-
const presetsTime = performance.now() - presetsStartTime;
|
|
229
|
-
console.log(`⏱️ Presets copy time: ${presetsTime.toFixed(0)}ms\n`);
|
|
230
|
-
|
|
231
|
-
const assetsStartTime = performance.now();
|
|
232
98
|
await copyProjectAssets(PATHS.juxSource, PATHS.frontendDist);
|
|
233
|
-
const assetsTime = performance.now() - assetsStartTime;
|
|
234
|
-
console.log(`⏱️ Assets copy time: ${assetsTime.toFixed(0)}ms\n`);
|
|
235
|
-
|
|
236
|
-
const tsStartTime = performance.now();
|
|
237
99
|
await transpileProjectTypeScript(PATHS.juxSource, PATHS.frontendDist);
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
// Bundle Routers
|
|
242
|
-
const projectJuxFiles = findJuxFiles(PATHS.juxSource);
|
|
243
|
-
console.log(`📝 Found ${projectJuxFiles.length} .jux file(s)\n`);
|
|
244
|
-
|
|
245
|
-
if (projectJuxFiles.length === 0) {
|
|
246
|
-
console.warn('⚠️ No .jux files found to bundle');
|
|
247
|
-
process.exit(1);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
const bundleStartTime = performance.now();
|
|
251
|
-
const bundleResult = await bundleJuxFilesToRouter(PATHS.juxSource, PATHS.frontendDist, {
|
|
252
|
-
routePrefix: '',
|
|
253
|
-
config // Pass config to bundler
|
|
254
|
-
});
|
|
255
|
-
const bundleTime = performance.now() - bundleStartTime;
|
|
256
|
-
|
|
257
|
-
const indexStartTime = performance.now();
|
|
258
|
-
generateIndexHtml(PATHS.frontendDist, bundleResult, {
|
|
259
|
-
isDev: isServe,
|
|
260
|
-
wsPort: isServe ? wsPort : undefined
|
|
261
|
-
});
|
|
262
|
-
const indexTime = performance.now() - indexStartTime;
|
|
263
|
-
|
|
264
|
-
// ✅ NEW: Post-Compilation Verification
|
|
265
|
-
if (!verifyStaticBuild(PATHS.frontendDist)) {
|
|
266
|
-
console.error('🛑 Critical Build Failure. Aborting.');
|
|
267
|
-
process.exit(1);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
const totalBuildTime = performance.now() - buildStartTime;
|
|
271
|
-
console.log(`\n✅ Bundled → ${PATHS.frontendDist}/${bundleResult.mainJsFilename}\n`);
|
|
272
|
-
|
|
273
|
-
// Build Summary Log
|
|
274
|
-
console.log(`📊 Build Summary:`);
|
|
275
|
-
console.log(` ━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
|
|
276
|
-
console.log(` Library copy: ${libTime.toFixed(0)}ms`);
|
|
277
|
-
console.log(` Router bundle: ${bundleTime.toFixed(0)}ms`);
|
|
278
|
-
console.log(` Total build time: ${totalBuildTime.toFixed(0)}ms\n`);
|
|
279
|
-
|
|
280
|
-
if (!isServe) {
|
|
281
|
-
// ...existing code...
|
|
282
|
-
}
|
|
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); }
|
|
283
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)`);
|
|
284
106
|
} catch (err) {
|
|
285
|
-
console.
|
|
286
|
-
console.error(err.
|
|
107
|
+
console.log('❌ Failed.');
|
|
108
|
+
console.error(err.message);
|
|
287
109
|
process.exit(1);
|
|
288
110
|
}
|
|
289
111
|
}
|
|
290
112
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
if (command === 'init') {
|
|
296
|
-
console.log('🎨 Initializing JUX project...\n');
|
|
297
|
-
const juxDir = PATHS.juxSource;
|
|
298
|
-
if (fs.existsSync(juxDir)) {
|
|
299
|
-
console.error('❌ Source directory already exists');
|
|
300
|
-
process.exit(1);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
fs.mkdirSync(juxDir, { recursive: true }); // create it.
|
|
304
|
-
|
|
305
|
-
let filesCopied = 0;
|
|
306
|
-
// Copy default presets
|
|
307
|
-
const defaultPresetSrc = path.join(PATHS.packageRoot, 'create');
|
|
308
|
-
if (fs.existsSync(defaultPresetSrc)) {
|
|
309
|
-
const entries = fs.readdirSync(defaultPresetSrc, { withFileTypes: true });
|
|
310
|
-
for (const entry of entries) {
|
|
311
|
-
if (entry.isFile()) {
|
|
312
|
-
fs.copyFileSync(path.join(defaultPresetSrc, entry.name), path.join(juxDir, entry.name));
|
|
313
|
-
filesCopied++;
|
|
314
|
-
// console.log(`+ Created ${entry.name}`);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// ✅ ADD: Safe fallback if presets are missing so build doesn't fail
|
|
320
|
-
if (filesCopied === 0) {
|
|
321
|
-
console.warn('⚠️ Install pages not available. Creating default index.jux...');
|
|
322
|
-
const indexContent = `import { jux } from 'juxscript';
|
|
323
|
-
|
|
324
|
-
jux.hero('welcome', {
|
|
325
|
-
title: 'Welcome to JUX',
|
|
326
|
-
subtitle: 'Start building in jux/index.jux'
|
|
327
|
-
}).render('#app');
|
|
328
|
-
`;
|
|
329
|
-
fs.writeFileSync(path.join(juxDir, 'index.jux'), indexContent);
|
|
330
|
-
console.log('+ Created index.jux');
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// Create juxconfig.js
|
|
334
|
-
const configExampleSrc = path.join(PATHS.packageRoot, 'juxconfig.example.js');
|
|
335
|
-
const configDest = path.join(PATHS.projectRoot, 'juxconfig.js');
|
|
336
|
-
if (fs.existsSync(configExampleSrc) && !fs.existsSync(configDest)) {
|
|
337
|
-
fs.copyFileSync(configExampleSrc, configDest);
|
|
338
|
-
console.log('+ Created juxconfig.js');
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
console.log('\n✅ JUX project initialized!');
|
|
342
|
-
|
|
113
|
+
if (command === 'init' || command === 'install') {
|
|
114
|
+
scaffoldProject(PATHS.projectRoot, PATHS.packageRoot);
|
|
115
|
+
console.log('✅ Initialized. Next: npm run dev');
|
|
343
116
|
} else if (command === 'build') {
|
|
344
117
|
await buildProject(false);
|
|
345
|
-
console.log(`✅ Build complete: ${PATHS.frontendDist}`);
|
|
346
|
-
|
|
347
118
|
} else if (command === 'serve') {
|
|
348
119
|
const httpPort = parseInt(process.argv[3]) || config.defaults?.httpPort || 3000;
|
|
349
120
|
const wsPort = parseInt(process.argv[4]) || config.defaults?.wsPort || 3001;
|
|
350
|
-
|
|
351
121
|
await buildProject(true, wsPort);
|
|
352
|
-
await start(httpPort, wsPort, PATHS.frontendDist, config);
|
|
353
|
-
|
|
122
|
+
await start(httpPort, wsPort, PATHS.frontendDist, config);
|
|
354
123
|
} else {
|
|
355
|
-
|
|
356
|
-
// (Help text)
|
|
357
|
-
console.log(`
|
|
358
|
-
JUX CLI - A JavaScript UX authorship platform
|
|
359
|
-
Usage:
|
|
360
|
-
npx jux create [name]
|
|
361
|
-
npx jux init
|
|
362
|
-
npx jux build
|
|
363
|
-
npx jux serve
|
|
364
|
-
`);
|
|
124
|
+
console.log(`Usage: npx jux [create|init|build|serve]`);
|
|
365
125
|
}
|
|
366
|
-
|
|
367
126
|
})();
|
package/create/index.jux
CHANGED
|
@@ -1,90 +1,79 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
// Initialize
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
.text('
|
|
30
|
-
.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}).render('
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
.
|
|
50
|
-
.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
.
|
|
80
|
-
.style('font-size: 1.25rem; font-weight: 600; color: var(--color-brand);')
|
|
81
|
-
.render('#appmain-content');
|
|
82
|
-
|
|
83
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
84
|
-
// FOOTER CONTENT
|
|
85
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
86
|
-
|
|
87
|
-
jux.paragraph('footer-text')
|
|
88
|
-
.text('© 2026 JUX Authoring Framework')
|
|
89
|
-
.style('color: var(--color-text-tertiary);')
|
|
90
|
-
.render('#appfooter-content');
|
|
1
|
+
import { Element, List } from 'juxscript';
|
|
2
|
+
import { LandingLayout } from './layout.jux';
|
|
3
|
+
|
|
4
|
+
// 1. Initialize Layout
|
|
5
|
+
LandingLayout();
|
|
6
|
+
|
|
7
|
+
// 2. Header: Logo & Nav
|
|
8
|
+
Element('logo', { tag: 'h2' })
|
|
9
|
+
.text('JUX STUDIO')
|
|
10
|
+
.style('margin: 0; padding: 25px 40px; font-weight: 800; letter-spacing: -1px; display: flex; align-items: center; height: 100%; box-sizing: border-box;')
|
|
11
|
+
.render('landing-layout-0-0');
|
|
12
|
+
|
|
13
|
+
// 3. Hero Section
|
|
14
|
+
const hero = Element('hero-content', { tag: 'div' })
|
|
15
|
+
.style('display: flex; flex-direction: column; justify-content: center; align-items: center; height: 100%; text-align: center; background: #f8f9fa;')
|
|
16
|
+
.render('landing-layout-1-0');
|
|
17
|
+
|
|
18
|
+
Element('hero-h1', { tag: 'h1' })
|
|
19
|
+
.text('Build Web Apps. Simply.')
|
|
20
|
+
.style('font-size: 4rem; margin: 0 0 20px 0; font-weight: 900; background: linear-gradient(45deg, #111, #555); -webkit-background-clip: text; -webkit-text-fill-color: transparent;')
|
|
21
|
+
.render('hero-content');
|
|
22
|
+
|
|
23
|
+
Element('hero-p', { tag: 'p' })
|
|
24
|
+
.text('No complex build steps. No magic. Just standard JavaScript components.')
|
|
25
|
+
.style('font-size: 1.5rem; color: #666; max-width: 600px; line-height: 1.5;')
|
|
26
|
+
.render('hero-content');
|
|
27
|
+
|
|
28
|
+
Element('cta-btn', { tag: 'button' })
|
|
29
|
+
.text('Get Started')
|
|
30
|
+
.style('margin-top: 30px; padding: 15px 40px; font-size: 1.2rem; background: #000; color: #fff; border: none; border-radius: 50px; cursor: pointer; transition: transform 0.2s;')
|
|
31
|
+
.render('hero-content');
|
|
32
|
+
|
|
33
|
+
// 4. Features Section (Nested Content)
|
|
34
|
+
const featuresWrapper = Element('features-wrapper', { tag: 'div' })
|
|
35
|
+
.style('max-width: 1000px; margin: 60px auto; display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 40px; padding: 0 20px;')
|
|
36
|
+
.render('landing-layout-2-0');
|
|
37
|
+
|
|
38
|
+
// Component Showcase within Landing Page
|
|
39
|
+
const listDemo = Element('list-demo-card', { tag: 'div' })
|
|
40
|
+
.style('background: #fff; padding: 30px; border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.05); border: 1px solid #eee;')
|
|
41
|
+
.render('features-wrapper');
|
|
42
|
+
|
|
43
|
+
Element('list-label', { tag: 'h3' }).text('Interactive Components').render('list-demo-card');
|
|
44
|
+
Element('list-desc', { tag: 'p' }).text('Try dragging or sorting this list:').style('color:#666; margin-bottom:20px;').render('list-demo-card');
|
|
45
|
+
|
|
46
|
+
List('feature-list', { items: ['Zero Config', 'Standard Web APIs', 'Hot Reloading', 'TypeScript Ready'] })
|
|
47
|
+
.listType('unordered')
|
|
48
|
+
.enableSort()
|
|
49
|
+
.enableMove()
|
|
50
|
+
.render('list-demo-card');
|
|
51
|
+
|
|
52
|
+
// Feature Cards
|
|
53
|
+
const cardData = [
|
|
54
|
+
{ title: 'Reactive State', desc: 'Granular binding means high performance without a virtual DOM.' },
|
|
55
|
+
{ title: 'Time Travel', desc: 'Built-in undo/redo capabilities for complex application state.' }
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
cardData.forEach((f, i) => {
|
|
59
|
+
const card = Element(`card-${i}`, { tag: 'div' })
|
|
60
|
+
.style('background: #fff; padding: 30px; border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.05); border: 1px solid #eee;')
|
|
61
|
+
.render('features-wrapper');
|
|
62
|
+
|
|
63
|
+
Element(`ct-${i}`, { tag: 'h3' }).text(f.title).render(`card-${i}`);
|
|
64
|
+
Element(`cd-${i}`, { tag: 'p' }).text(f.desc).style('color: #666; line-height: 1.6;').render(`card-${i}`);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// 5. Footer
|
|
68
|
+
const footer = Element('footer-content', { tag: 'div' })
|
|
69
|
+
.html('© 2025 Jux Project. Open Source.')
|
|
70
|
+
.style('display:flex; align-items:center; justify-content:center; height:100%; background:#111; color:#666;')
|
|
71
|
+
.render('landing-layout-3-0');
|
|
72
|
+
|
|
73
|
+
// 6. Global Reset (injected via Element)
|
|
74
|
+
Element('reset-styles', { tag: 'style' })
|
|
75
|
+
.text(`
|
|
76
|
+
body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; background: #fff; color: #111; }
|
|
77
|
+
button:hover { transform: scale(1.05); }
|
|
78
|
+
`)
|
|
79
|
+
.render('app');
|