juxscript 1.0.66 → 1.0.67

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.
@@ -1,14 +1,13 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import http from 'http';
4
- import * as acorn from 'acorn'; // Needed to parse main.js
4
+ import * as acorn from 'acorn';
5
5
 
6
6
  /**
7
7
  * Checks the static `distDir` for critical files and valid import map references.
8
8
  * Returns true if healthy, false if broken.
9
9
  */
10
10
  export function verifyStaticBuild(distDir) {
11
- // console.log('šŸ•µļø Verifying build integrity...');
12
11
  const errors = [];
13
12
 
14
13
  if (!fs.existsSync(distDir)) {
@@ -17,7 +16,8 @@ export function verifyStaticBuild(distDir) {
17
16
  }
18
17
 
19
18
  // 1. Critical Files Existence
20
- const critical = ['index.html', 'main.js', 'lib/jux.js'];
19
+ // āœ… FIX: Removed lib/jux.js as it is no longer the primary artifact with v2 components
20
+ const critical = ['index.html', 'main.js'];
21
21
  for (const f of critical) {
22
22
  if (!fs.existsSync(path.join(distDir, f))) {
23
23
  errors.push(`Missing critical artifact: ${f}`);
@@ -41,6 +41,8 @@ export function verifyStaticBuild(distDir) {
41
41
  const fullPath = path.join(distDir, fsPath);
42
42
 
43
43
  if (!fs.existsSync(fullPath)) {
44
+ // Downgrade missing imports to warnings if they look like vendored libs?
45
+ // For now, keep as error to be safe.
44
46
  errors.push(`Broken Import Map: "${key}" -> ${url} (File not found)`);
45
47
  }
46
48
  }
@@ -62,14 +64,10 @@ export function verifyStaticBuild(distDir) {
62
64
  if (node.type === 'ImportDeclaration') {
63
65
  const source = node.source.value;
64
66
 
65
- // Allow internal relative paths if they exist
66
67
  if (source.startsWith('/') || source.startsWith('.')) {
67
- // basic existence check (simplified)
68
68
  return;
69
69
  }
70
70
 
71
- // Check if bare specifier exists in Import Map
72
- // e.g. import axios from 'axios' -> must be in map
73
71
  if (!importMap[source] && !importMap[source + '/']) {
74
72
  errors.push(`Ghost Dependency: '${source}' is imported in main.js but missing from Import Map.`);
75
73
  }
@@ -87,33 +85,26 @@ export function verifyStaticBuild(distDir) {
87
85
  return false;
88
86
  }
89
87
 
90
- // console.log(' āœ“ Static verification passed');
91
88
  return true;
92
89
  }
93
90
 
94
- /**
95
- * Pings the running server to ensure correct MIME types and Route availability.
96
- * Detects "serving JS as HTML" (MIME type mismatch) errors.
97
- */
98
91
  export function verifyRuntime(port) {
99
92
  return new Promise((resolve) => {
100
- console.log('🩺 Performing runtime diagnostics...');
93
+ // console.log('🩺 Performing runtime diagnostics...');
101
94
  const errors = [];
102
95
 
103
96
  // Helper to check a single URL
104
97
  const check = (urlPath, expectedMime) => new Promise(r => {
105
98
  const req = http.get(`http://localhost:${port}${urlPath}`, res => {
106
- // Check Status
107
99
  if (res.statusCode !== 200 && res.statusCode !== 304) {
108
100
  errors.push(`GET ${urlPath} : Status ${res.statusCode} (Expected 200)`);
109
101
  } else {
110
- // Check MIME type
111
102
  const ct = res.headers['content-type'] || '';
112
103
  if (expectedMime && !ct.includes(expectedMime)) {
113
- errors.push(`GET ${urlPath} : Bad MIME type. Got "${ct}", expected "${expectedMime}". (Did you get index.html fallback?)`);
104
+ errors.push(`GET ${urlPath} : Bad MIME type. Got "${ct}", expected "${expectedMime}".`);
114
105
  }
115
106
  }
116
- res.resume(); // Consume stream
107
+ res.resume();
117
108
  r();
118
109
  });
119
110
 
@@ -125,9 +116,9 @@ export function verifyRuntime(port) {
125
116
 
126
117
  // Run checks in parallel
127
118
  Promise.all([
128
- check('/', 'text/html'), // Route should be HTML
129
- check('/main.js', 'application/javascript'), // Bundle must be JS
130
- check('/lib/jux.js', 'application/javascript') // Library must be JS
119
+ check('/', 'text/html'),
120
+ check('/main.js', 'application/javascript'),
121
+ // check('/lib/jux.js', 'application/javascript') // Removed check
131
122
  ]).then(() => {
132
123
  if (errors.length > 0) {
133
124
  console.error(`\nšŸ’„ RUNTIME VERIFICATION FAILED (` + errors.length + ` errors):`);
@@ -135,7 +126,7 @@ export function verifyRuntime(port) {
135
126
  console.log('');
136
127
  resolve(false);
137
128
  } else {
138
- console.log(' āœ“ Runtime health healthy (Routes & MIME types OK)\n');
129
+ // console.log(' āœ“ Runtime health healthy\n');
139
130
  resolve(true);
140
131
  }
141
132
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.0.66",
3
+ "version": "1.0.67",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "lib/jux.js",
@@ -1,467 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import esbuild from 'esbuild';
4
- import {
5
- bundleJuxFilesToRouter,
6
- generateRouterIndex
7
- } from './compiler.js';
8
-
9
- /**
10
- * Generate import map script tag
11
- */
12
- function generateImportMapScript() {
13
- return `<script type="importmap">
14
- {
15
- "imports": {
16
- "juxscript": "./lib/jux.js",
17
- "juxscript/": "./lib/",
18
- "juxscript/reactivity": "./lib/reactivity/state.js",
19
- "juxscript/presets/": "./presets/",
20
- "juxscript/components/": "./lib/components/",
21
- "juxscript/componentsv2/": "./lib/componentsv2/"
22
- }
23
- }
24
- </script>`;
25
- }
26
-
27
- /**
28
- * Compile a .jux file to .js and .html
29
- *
30
- * @param {string} juxFilePath - Path to the .jux file
31
- * @param {Object} options - Compilation options
32
- * @param {string} options.distDir - Output directory
33
- * @param {string} options.projectRoot - Project root directory
34
- * @param {boolean} options.isServe - Whether serving for development
35
- * @returns {Promise<{jsOutputPath: string, htmlOutputPath: string}>}
36
- */
37
- export async function compileJuxFile(juxFilePath, options = {}) {
38
- const { distDir, projectRoot, isServe = false } = options;
39
-
40
- const relativePath = path.relative(projectRoot, juxFilePath);
41
- const parsedPath = path.parse(relativePath);
42
-
43
- // Output paths
44
- const outputDir = path.join(distDir, parsedPath.dir);
45
- const jsOutputPath = path.join(outputDir, `${parsedPath.name}.js`);
46
- const htmlOutputPath = path.join(outputDir, `${parsedPath.name}.html`);
47
-
48
- // Ensure output directory exists
49
- if (!fs.existsSync(outputDir)) {
50
- fs.mkdirSync(outputDir, { recursive: true });
51
- }
52
-
53
- console.log(`šŸ“ Compiling: ${relativePath}`);
54
-
55
- // Read the .jux file
56
- const juxContent = fs.readFileSync(juxFilePath, 'utf-8');
57
-
58
- // Calculate depth for relative paths
59
- const depth = parsedPath.dir.split(path.sep).filter(p => p).length;
60
- const libPath = depth === 0 ? './lib/jux.js' : '../'.repeat(depth) + 'lib/jux.js';
61
-
62
- // Transform imports
63
- let transformedContent = juxContent;
64
-
65
- // Replace common import patterns with calculated path
66
- transformedContent = transformedContent.replace(
67
- /from\s+['"]\.\.?\/lib\/jux\.js['"]/g,
68
- `from '${libPath}'`
69
- );
70
-
71
- // Only inject import if:
72
- // 1. File is not empty (ignoring whitespace and comments)
73
- // 2. File uses 'jux.' but has no import statement
74
- const contentWithoutComments = transformedContent
75
- .replace(/\/\*[\s\S]*?\*\//g, '') // Remove block comments
76
- .replace(/\/\/.*/g, '') // Remove line comments
77
- .trim();
78
-
79
- const hasContent = contentWithoutComments.length > 0;
80
- const usesJux = /\bjux\./g.test(contentWithoutComments);
81
- const hasImport = /import\s+.*from/.test(transformedContent);
82
-
83
- if (hasContent && usesJux && !hasImport) {
84
- transformedContent = `import { jux } from '${libPath}';\n\n${transformedContent}`;
85
- }
86
-
87
- // Write the transformed JS
88
- fs.writeFileSync(jsOutputPath, transformedContent);
89
-
90
- console.log(` āœ“ JS: ${path.relative(projectRoot, jsOutputPath)}`);
91
-
92
- // Generate HTML with import map and correct script path
93
- const scriptPath = `./${parsedPath.name}.js`;
94
- const importMapScript = generateImportMapScript();
95
-
96
- const html = `<!DOCTYPE html>
97
- <html lang="en">
98
- <head>
99
- <meta charset="UTF-8">
100
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
101
- <title>${parsedPath.name}</title>
102
- </head>
103
- <body data-theme="">
104
- <!-- App container -->
105
- <div id="app" data-jux-page="${parsedPath.name}"></div>
106
-
107
- ${importMapScript}
108
- <script type="module" src="${scriptPath}"></script>
109
- ${isServe ? `
110
- <!-- Hot reload -->
111
- <script type="module">
112
- const ws = new WebSocket('ws://localhost:3001');
113
- ws.onmessage = (msg) => {
114
- const data = JSON.parse(msg.data);
115
- if (data.type === 'reload') {
116
- console.log('šŸ”„ Reloading page...');
117
- location.reload();
118
- }
119
- };
120
- ws.onerror = () => console.warn('āš ļø WebSocket connection failed');
121
- </script>
122
- ` : ''}
123
- </body>
124
- </html>`;
125
-
126
- fs.writeFileSync(htmlOutputPath, html);
127
- console.log(` āœ“ HTML: ${path.relative(projectRoot, htmlOutputPath)}`);
128
-
129
- return { jsOutputPath, htmlOutputPath };
130
- }
131
-
132
- /**
133
- * Copy and build the JUX library from TypeScript to JavaScript
134
- *
135
- * @param {string} projectRoot - Root directory containing lib/
136
- * @param {string} distDir - Destination directory for built files
137
- */
138
- export async function copyLibToOutput(projectRoot, distDir) {
139
- // Simplified lib path resolution
140
- const libSrc = path.resolve(projectRoot, '../lib');
141
-
142
- if (!fs.existsSync(libSrc)) {
143
- throw new Error(`lib/ directory not found at ${libSrc}`);
144
- }
145
-
146
- const libDest = path.join(distDir, 'lib');
147
-
148
- console.log('šŸ“¦ Building TypeScript library...');
149
- console.log(` From: ${libSrc}`);
150
- console.log(` To: ${libDest}`);
151
-
152
- if (fs.existsSync(libDest)) {
153
- fs.rmSync(libDest, { recursive: true });
154
- }
155
-
156
- fs.mkdirSync(libDest, { recursive: true });
157
-
158
- // Find all TypeScript entry points
159
- const tsFiles = findFiles(libSrc, '.ts');
160
-
161
- if (tsFiles.length === 0) {
162
- console.warn('āš ļø No TypeScript files found in lib/');
163
- return;
164
- }
165
-
166
- console.log(` Found ${tsFiles.length} TypeScript files`);
167
-
168
- // Build all TypeScript files with esbuild
169
- try {
170
- await esbuild.build({
171
- entryPoints: tsFiles,
172
- bundle: false,
173
- format: 'esm',
174
- outdir: libDest,
175
- outbase: libSrc,
176
- platform: 'browser',
177
- target: 'es2020',
178
- loader: {
179
- '.ts': 'ts'
180
- },
181
- logLevel: 'warning'
182
- });
183
-
184
- console.log(' āœ“ TypeScript compiled to JavaScript');
185
-
186
- // Copy non-TS files (CSS, HTML, etc.)
187
- console.log(' Copying lib assets...');
188
- copyNonTsFiles(libSrc, libDest);
189
- console.log(' āœ“ Lib assets copied');
190
-
191
- } catch (err) {
192
- console.error('āŒ Failed to build TypeScript:', err.message);
193
- throw err;
194
- }
195
-
196
- console.log('āœ… Library ready\n');
197
- }
198
-
199
- /**
200
- * Copy project assets (CSS, JS, images) from jux/ to dist/
201
- *
202
- * @param {string} projectRoot - Source directory (jux/)
203
- * @param {string} distDir - Destination directory (jux-dist/)
204
- */
205
- export async function copyProjectAssets(projectRoot, distDir) {
206
- console.log('šŸ“¦ Copying project assets...');
207
-
208
- // Find all CSS and JS files in project root (excluding node_modules, dist, .git)
209
- const allFiles = [];
210
- findProjectFiles(projectRoot, ['.css', '.js'], allFiles, projectRoot);
211
-
212
- console.log(` Found ${allFiles.length} asset file(s)`);
213
-
214
- for (const srcPath of allFiles) {
215
- const relativePath = path.relative(projectRoot, srcPath);
216
- const destPath = path.join(distDir, relativePath);
217
- const destDir = path.dirname(destPath);
218
-
219
- // Create destination directory if needed
220
- if (!fs.existsSync(destDir)) {
221
- fs.mkdirSync(destDir, { recursive: true });
222
- }
223
-
224
- // Copy file
225
- fs.copyFileSync(srcPath, destPath);
226
- console.log(` āœ“ ${relativePath}`);
227
- }
228
-
229
- console.log('āœ… Project assets copied\n');
230
- }
231
-
232
- /**
233
- * Transpile TypeScript files from jux/ to jux-dist/, preserving folder structure
234
- *
235
- * @param {string} srcDir - Source directory (jux/)
236
- * @param {string} destDir - Destination directory (jux-dist/)
237
- * @example
238
- * // jux/samples/mypage.ts -> jux-dist/samples/mypage.js
239
- * await transpileProjectTypeScript('jux/', 'jux-dist/');
240
- */
241
- export async function transpileProjectTypeScript(srcDir, destDir) {
242
- console.log('šŸ”· Transpiling TypeScript files...');
243
-
244
- // Find all TypeScript files in the project
245
- const tsFiles = findFiles(srcDir, '.ts');
246
-
247
- if (tsFiles.length === 0) {
248
- console.log(' No TypeScript files found in project');
249
- return;
250
- }
251
-
252
- console.log(` Found ${tsFiles.length} TypeScript file(s)`);
253
-
254
- try {
255
- // Build all TypeScript files with esbuild
256
- await esbuild.build({
257
- entryPoints: tsFiles,
258
- bundle: false,
259
- format: 'esm',
260
- outdir: destDir,
261
- outbase: srcDir,
262
- platform: 'browser',
263
- target: 'es2020',
264
- loader: {
265
- '.ts': 'ts'
266
- },
267
- logLevel: 'warning'
268
- });
269
-
270
- // Log each transpiled file
271
- tsFiles.forEach(tsFile => {
272
- const relativePath = path.relative(srcDir, tsFile);
273
- const jsPath = relativePath.replace(/\.ts$/, '.js');
274
- console.log(` āœ“ ${relativePath} → ${jsPath}`);
275
- });
276
-
277
- console.log('āœ… TypeScript transpiled\n');
278
-
279
- } catch (err) {
280
- console.error('āŒ Failed to transpile TypeScript:', err.message);
281
- throw err;
282
- }
283
- }
284
-
285
- /**
286
- * Copy presets folder from lib to dist
287
- *
288
- * @param {string} packageRoot - Source package root directory
289
- * @param {string} distDir - Destination directory
290
- */
291
- export async function copyPresetsToOutput(packageRoot, distDir) {
292
- console.log('šŸ“¦ Copying presets...');
293
-
294
- const presetsSrc = path.join(packageRoot, 'presets');
295
- const presetsDest = path.join(distDir, 'presets');
296
-
297
- if (!fs.existsSync(presetsSrc)) {
298
- console.log(' No presets directory found');
299
- return;
300
- }
301
-
302
- if (fs.existsSync(presetsDest)) {
303
- fs.rmSync(presetsDest, { recursive: true });
304
- }
305
-
306
- fs.mkdirSync(presetsDest, { recursive: true });
307
-
308
- // Copy all files in presets directory
309
- const files = fs.readdirSync(presetsSrc);
310
- let copiedCount = 0;
311
-
312
- for (const file of files) {
313
- const srcPath = path.join(presetsSrc, file);
314
- const destPath = path.join(presetsDest, file);
315
-
316
- if (fs.statSync(srcPath).isFile()) {
317
- fs.copyFileSync(srcPath, destPath);
318
- console.log(` āœ“ ${file}`);
319
- copiedCount++;
320
- }
321
- }
322
-
323
- console.log(`āœ… Copied ${copiedCount} preset file(s)\n`);
324
- }
325
-
326
- /**
327
- * Recursively find files with a specific extension
328
- *
329
- * @param {string} dir - Directory to search
330
- * @param {string} extension - File extension (e.g., '.ts')
331
- * @param {string[]} fileList - Accumulator for found files
332
- * @returns {string[]} Array of file paths
333
- */
334
- async function findFiles(dir, extension, fileList = []) {
335
- const fs = await import('fs');
336
- const path = await import('path');
337
- const files = fs.readdirSync(dir);
338
-
339
- files.forEach(file => {
340
- const filePath = path.join(dir, file);
341
- const stat = fs.statSync(filePath);
342
-
343
- if (stat.isDirectory() && !['node_modules', 'jux-dist', '.git', 'lib'].includes(file)) {
344
- findFiles(filePath, extension, fileList);
345
- } else if (file.endsWith(extension)) {
346
- fileList.push(filePath);
347
- }
348
- });
349
-
350
- return fileList;
351
- }
352
-
353
- /**
354
- * Copy non-TypeScript files (CSS, JSON, JS, SVG, etc.)
355
- *
356
- * @param {string} src - Source directory
357
- * @param {string} dest - Destination directory
358
- */
359
- function copyNonTsFiles(src, dest) {
360
- const entries = fs.readdirSync(src, { withFileTypes: true });
361
-
362
- for (const entry of entries) {
363
- const srcPath = path.join(src, entry.name);
364
- const destPath = path.join(dest, entry.name);
365
-
366
- if (entry.isDirectory()) {
367
- if (!fs.existsSync(destPath)) {
368
- fs.mkdirSync(destPath, { recursive: true });
369
- }
370
- copyNonTsFiles(srcPath, destPath);
371
- } else if (entry.isFile()) {
372
- const ext = path.extname(entry.name);
373
- // Copy CSS, JSON, SVG, and JS files (but not .ts files)
374
- if (['.css', '.json', '.js', '.svg', '.png', '.jpg', '.jpeg', '.gif', '.webp'].includes(ext)) {
375
- fs.copyFileSync(srcPath, destPath);
376
- }
377
- }
378
- }
379
- }
380
-
381
- /**
382
- * Find project files with specific extensions, excluding certain directories
383
- *
384
- * @param {string} dir - Directory to search
385
- * @param {string[]} extensions - File extensions to find
386
- * @param {string[]} fileList - Accumulator for found files
387
- * @param {string} rootDir - Root directory for relative paths
388
- * @param {string[]} excludeDirs - Directories to exclude
389
- * @returns {string[]} Array of file paths
390
- */
391
- function findProjectFiles(dir, extensions, fileList = [], rootDir = dir, excludeDirs = ['node_modules', 'jux-dist', '.git', 'lib']) {
392
- if (!fs.existsSync(dir)) return fileList;
393
-
394
- const entries = fs.readdirSync(dir, { withFileTypes: true });
395
-
396
- for (const entry of entries) {
397
- const fullPath = path.join(dir, entry.name);
398
-
399
- if (entry.isDirectory()) {
400
- // Skip excluded directories
401
- if (excludeDirs.includes(entry.name)) {
402
- continue;
403
- }
404
- findProjectFiles(fullPath, extensions, fileList, rootDir, excludeDirs);
405
- } else {
406
- // Check if file has one of the desired extensions
407
- const hasExtension = extensions.some(ext => entry.name.endsWith(ext));
408
- if (hasExtension) {
409
- fileList.push(fullPath);
410
- }
411
- }
412
- }
413
-
414
- return fileList;
415
- }
416
-
417
- /**
418
- * Build a project
419
- *
420
- * @param {string} projectRoot - Root directory containing lib/
421
- * @param {string} distDir - Destination directory for built files
422
- * @param {Object} options - Compilation options
423
- * @param {string} options.routePrefix - Route prefix
424
- * @returns {Promise<{jsOutputPath: string, htmlOutputPath: string}>}
425
- */
426
- export async function buildProject(options = {}) {
427
- const { projectRoot, distDir } = options;
428
-
429
- // Ensure project root and dist directory exist
430
- if (!fs.existsSync(projectRoot)) {
431
- throw new Error(`Project root directory not found: ${projectRoot}`);
432
- }
433
- if (!fs.existsSync(distDir)) {
434
- fs.mkdirSync(distDir, { recursive: true });
435
- }
436
-
437
- // Option to use router mode
438
- if (options.router) {
439
- console.log('šŸ”€ Building in router mode...\n');
440
-
441
- await bundleJuxFilesToRouter(projectRoot, distDir, {
442
- routePrefix: options.routePrefix || ''
443
- });
444
-
445
- // Get route information for index generation
446
- const juxFiles = findFiles(projectRoot, '.jux');
447
- const routes = juxFiles.map(juxFile => {
448
- const relativePath = path.relative(projectRoot, juxFile);
449
- const parsedPath = path.parse(relativePath);
450
- const functionName = parsedPath.dir
451
- ? `${parsedPath.dir.replace(/\//g, '_')}_${parsedPath.name}`
452
- : parsedPath.name;
453
- const routePath = (options.routePrefix || '') + '/' +
454
- (parsedPath.dir ? `${parsedPath.dir}/` : '') + parsedPath.name;
455
- return {
456
- path: routePath.replace(/\/+/g, '/'),
457
- functionName
458
- };
459
- });
460
-
461
- generateRouterIndex(distDir, routes);
462
- } else {
463
- // ...existing individual page compilation...
464
- }
465
-
466
- // ...existing code...
467
- }