juxscript 1.0.82 → 1.0.83

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.
@@ -301,44 +301,78 @@ export async function bundleJuxFilesToRouter(projectRoot, distDir, options = {})
301
301
  }
302
302
 
303
303
  function extractSharedModule(juxContent, moduleName, sourceFilePath) {
304
- const exportLines = [];
305
- try {
306
- const ast = acorn.parse(juxContent, { ecmaVersion: 'latest', sourceType: 'module' });
307
- ast.body.forEach(node => {
308
- if (node.type === 'ExportNamedDeclaration' && node.declaration) {
309
- const exportCode = juxContent.slice(node.start, node.end).replace(/^export\s+/, '');
310
- exportLines.push(`// From: ${sourceFilePath}\n${namespaceExportedIdentifiers(exportCode, sourceFilePath)}`);
311
- } else if (node.type === 'ExportDefaultDeclaration') {
312
- const exportCode = juxContent.slice(node.start, node.end).replace(/^export\s+default\s+/, '');
313
- exportLines.push(`// From: ${sourceFilePath}\n${namespaceExportedIdentifiers(exportCode, sourceFilePath)}`);
314
- }
315
- });
316
- } catch (err) {
317
- throw new Error(`Invalid JavaScript syntax in ${sourceFilePath}.`);
318
- }
319
- return exportLines.join('\n\n');
320
- }
321
-
322
- function namespaceExportedIdentifiers(code, sourceFilePath) {
323
- // ✅ FIX: Strip .jux AND .js extensions for stable namespacing
304
+ // 1. Calculate Namespace
305
+ // Ensure we have a valid JS identifier suffix
324
306
  const namespace = sourceFilePath.replace(/\.(jux|js)$/, '').replace(/[\/\\]/g, '$').replace(/[^a-zA-Z0-9$]/g, '_');
307
+
308
+ let ast;
325
309
  try {
326
- const ast = acorn.parse(code, { ecmaVersion: 'latest', sourceType: 'module' });
327
- const identifiers = [];
328
- ast.body.forEach(node => {
329
- if (node.type === 'FunctionDeclaration' && node.id) identifiers.push(node.id);
330
- else if (node.type === 'VariableDeclaration') node.declarations.forEach(d => { if (d.id.type === 'Identifier') identifiers.push(d.id); });
331
- else if (node.type === 'ClassDeclaration' && node.id) identifiers.push(node.id);
332
- });
333
- identifiers.sort((a, b) => b.start - a.start);
334
- let result = code;
335
- identifiers.forEach(id => {
336
- result = result.slice(0, id.start) + `${id.name}$${namespace}` + result.slice(id.end);
337
- });
338
- return result;
310
+ ast = acorn.parse(juxContent, { ecmaVersion: 'latest', sourceType: 'module' });
339
311
  } catch (err) {
340
- return code;
312
+ throw new Error(`Invalid JavaScript syntax in ${sourceFilePath}: ${err.message}`);
341
313
  }
314
+
315
+ // 2. Identify ALL Top-Level Identifiers (Exported or Local) to rename
316
+ // We must rename everything to avoid global collision in the flat bundle
317
+ const identifiersToRename = new Map();
318
+
319
+ const addId = (idNode) => {
320
+ if (idNode && idNode.type === 'Identifier') {
321
+ identifiersToRename.set(idNode.name, `${idNode.name}$${namespace}`);
322
+ }
323
+ };
324
+
325
+ ast.body.forEach(node => {
326
+ if (node.type === 'FunctionDeclaration' || node.type === 'ClassDeclaration') {
327
+ addId(node.id);
328
+ } else if (node.type === 'VariableDeclaration') {
329
+ node.declarations.forEach(d => addId(d.id));
330
+ } else if ((node.type === 'ExportNamedDeclaration' || node.type === 'ExportDefaultDeclaration') && node.declaration) {
331
+ const d = node.declaration;
332
+ if (d.type === 'FunctionDeclaration' || d.type === 'ClassDeclaration') addId(d.id);
333
+ else if (d.type === 'VariableDeclaration') d.declarations.forEach(v => addId(v.id));
334
+ }
335
+ });
336
+
337
+ // 3. Reconstruct Code Line-by-Line (Node-by-Node)
338
+ const outputLines = [`// From: ${sourceFilePath}`];
339
+
340
+ // Sort keys by length desc to avoid partial replacement issues
341
+ const sortedKeys = Array.from(identifiersToRename.keys()).sort((a, b) => b.length - a.length);
342
+
343
+ ast.body.forEach(node => {
344
+ // Skip Imports (handled globally in bundling phase)
345
+ if (node.type === 'ImportDeclaration') return;
346
+ // Skip "export { a, b }" (re-exports) as 'a' and 'b' are already declared/renamed locally
347
+ if (node.type === 'ExportNamedDeclaration' && !node.declaration) return;
348
+
349
+ // Extract relevant code slice
350
+ let code = '';
351
+ if (node.type === 'ExportNamedDeclaration' || node.type === 'ExportDefaultDeclaration') {
352
+ if (node.declaration) {
353
+ code = juxContent.slice(node.declaration.start, node.declaration.end);
354
+ }
355
+ } else {
356
+ code = juxContent.slice(node.start, node.end);
357
+ }
358
+
359
+ if (!code) return;
360
+
361
+ // Replace Identifiers in this block
362
+ // Regex:
363
+ // (?<!\.) -> Negative lookbehind to avoid property access (obj.foo)
364
+ // \bkey\b -> Word boundary
365
+ // (?!\s*:) -> Negative lookahead to avoid object keys ({ foo: 1 })
366
+ for (const key of sortedKeys) {
367
+ const namespaced = identifiersToRename.get(key);
368
+ const regex = new RegExp(`(?<!\\.)\\b${key}\\b(?!\\s*:)`, 'g');
369
+ code = code.replace(regex, namespaced);
370
+ }
371
+
372
+ outputLines.push(code);
373
+ });
374
+
375
+ return outputLines.join('\n\n');
342
376
  }
343
377
 
344
378
  function transformJuxToViewFunction(juxContent, functionName, pageName, relativePath, sharedModules, bundledPaths) {
@@ -367,9 +401,7 @@ function transformJuxToViewFunction(juxContent, functionName, pageName, relative
367
401
  }
368
402
 
369
403
  // 🔥 FIX: Always remove ImportDeclaration from the view function contents.
370
- // External imports are hoisted to 'allImports' and placed at the top of main.js.
371
- // Local/Bundled imports are hoisted via sharedModules mechanism with namespacing.
372
- // Leaving import statements inside the function body causes SyntaxError.
404
+ // Leftover imports inside a function body cause SyntaxError.
373
405
  nodesToRemove.push({ start: node.start, end: node.end });
374
406
  }
375
407
  if (node.type === 'ExportNamedDeclaration' || node.type === 'ExportDefaultDeclaration') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.0.82",
3
+ "version": "1.0.83",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "lib/jux.js",