juxscript 1.0.82 → 1.0.84
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/machinery/compiler.js +90 -39
- package/package.json +1 -1
package/machinery/compiler.js
CHANGED
|
@@ -238,7 +238,8 @@ export async function bundleJuxFilesToRouter(projectRoot, distDir, options = {})
|
|
|
238
238
|
const hasExports = /^\s*export\s+(const|let|function|class|{)/m.test(fileContent);
|
|
239
239
|
if (hasExports) {
|
|
240
240
|
const exportKey = relativePath;
|
|
241
|
-
|
|
241
|
+
// ✅ FIX: Pass bundledPaths so imports can be resolved and rewritten in shared code
|
|
242
|
+
const exportCode = extractSharedModule(fileContent, rawFunctionName, relativePath, bundledPaths);
|
|
242
243
|
if (exportCode.trim()) sharedModules.set(exportKey, exportCode);
|
|
243
244
|
}
|
|
244
245
|
|
|
@@ -300,45 +301,97 @@ export async function bundleJuxFilesToRouter(projectRoot, distDir, options = {})
|
|
|
300
301
|
};
|
|
301
302
|
}
|
|
302
303
|
|
|
303
|
-
function extractSharedModule(juxContent, moduleName, sourceFilePath) {
|
|
304
|
-
|
|
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
|
+
function extractSharedModule(juxContent, moduleName, sourceFilePath, bundledPaths) {
|
|
305
|
+
// 1. Calculate Namespace
|
|
324
306
|
const namespace = sourceFilePath.replace(/\.(jux|js)$/, '').replace(/[\/\\]/g, '$').replace(/[^a-zA-Z0-9$]/g, '_');
|
|
307
|
+
|
|
308
|
+
let ast;
|
|
325
309
|
try {
|
|
326
|
-
|
|
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
|
-
|
|
312
|
+
throw new Error(`Invalid JavaScript syntax in ${sourceFilePath}: ${err.message}`);
|
|
341
313
|
}
|
|
314
|
+
|
|
315
|
+
// 2. Identify ALL Identifiers to rename (Local defs + Imports)
|
|
316
|
+
// We must rename everything to avoid global collision in the flat bundle
|
|
317
|
+
const identifiersToRename = new Map();
|
|
318
|
+
|
|
319
|
+
// A. Local Declarations (Self-namespacing)
|
|
320
|
+
const addId = (idNode) => {
|
|
321
|
+
if (idNode && idNode.type === 'Identifier') {
|
|
322
|
+
identifiersToRename.set(idNode.name, `${idNode.name}$${namespace}`);
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
ast.body.forEach(node => {
|
|
327
|
+
if (node.type === 'FunctionDeclaration' || node.type === 'ClassDeclaration') {
|
|
328
|
+
addId(node.id);
|
|
329
|
+
} else if (node.type === 'VariableDeclaration') {
|
|
330
|
+
node.declarations.forEach(d => addId(d.id));
|
|
331
|
+
} else if ((node.type === 'ExportNamedDeclaration' || node.type === 'ExportDefaultDeclaration') && node.declaration) {
|
|
332
|
+
const d = node.declaration;
|
|
333
|
+
if (d.type === 'FunctionDeclaration' || d.type === 'ClassDeclaration') addId(d.id);
|
|
334
|
+
else if (d.type === 'VariableDeclaration') d.declarations.forEach(v => addId(v.id));
|
|
335
|
+
} else if (node.type === 'ImportDeclaration') {
|
|
336
|
+
// B. Imports (Rewriting references to other bundled modules)
|
|
337
|
+
const importPath = node.source.value;
|
|
338
|
+
const resolvedPath = resolveImportPath(importPath, sourceFilePath);
|
|
339
|
+
// Check if this import is pointing to a bundled file
|
|
340
|
+
const isBundled = (bundledPaths && bundledPaths.has(resolvedPath)) || importPath.endsWith('.jux');
|
|
341
|
+
|
|
342
|
+
if (isBundled) {
|
|
343
|
+
const targetNamespace = resolvedPath.replace(/\.(jux|js)$/, '').replace(/[\/\\]/g, '$').replace(/[^a-zA-Z0-9$]/g, '_');
|
|
344
|
+
node.specifiers.forEach(spec => {
|
|
345
|
+
if (spec.type === 'ImportSpecifier') {
|
|
346
|
+
identifiersToRename.set(spec.local.name, `${spec.imported.name}$${targetNamespace}`);
|
|
347
|
+
} else if (spec.type === 'ImportDefaultSpecifier') {
|
|
348
|
+
// Best effort for defaults in this flat bundle scheme usually implies named export match or similar convention
|
|
349
|
+
// For now, assuming standard View exports which don't usually default export to shared
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
// 3. Reconstruct Code Line-by-Line (Node-by-Node)
|
|
357
|
+
const outputLines = [`// From: ${sourceFilePath}`];
|
|
358
|
+
|
|
359
|
+
// Sort keys by length desc to avoid partial replacement issues
|
|
360
|
+
const sortedKeys = Array.from(identifiersToRename.keys()).sort((a, b) => b.length - a.length);
|
|
361
|
+
|
|
362
|
+
ast.body.forEach(node => {
|
|
363
|
+
// Skip Imports (handled globally in bundling phase)
|
|
364
|
+
if (node.type === 'ImportDeclaration') return;
|
|
365
|
+
// Skip "export { a, b }" (re-exports) as 'a' and 'b' are already declared/renamed locally
|
|
366
|
+
if (node.type === 'ExportNamedDeclaration' && !node.declaration) return;
|
|
367
|
+
|
|
368
|
+
// Extract relevant code slice
|
|
369
|
+
let code = '';
|
|
370
|
+
if (node.type === 'ExportNamedDeclaration' || node.type === 'ExportDefaultDeclaration') {
|
|
371
|
+
if (node.declaration) {
|
|
372
|
+
code = juxContent.slice(node.declaration.start, node.declaration.end);
|
|
373
|
+
}
|
|
374
|
+
} else {
|
|
375
|
+
code = juxContent.slice(node.start, node.end);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (!code) return;
|
|
379
|
+
|
|
380
|
+
// Replace Identifiers in this block
|
|
381
|
+
// Regex:
|
|
382
|
+
// (?<!\.) -> Negative lookbehind to avoid property access (obj.foo)
|
|
383
|
+
// \bkey\b -> Word boundary
|
|
384
|
+
// (?!\s*:) -> Negative lookahead to avoid object keys ({ foo: 1 })
|
|
385
|
+
for (const key of sortedKeys) {
|
|
386
|
+
const namespaced = identifiersToRename.get(key);
|
|
387
|
+
const regex = new RegExp(`(?<!\\.)\\b${key}\\b(?!\\s*:)`, 'g');
|
|
388
|
+
code = code.replace(regex, namespaced);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
outputLines.push(code);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
return outputLines.join('\n\n');
|
|
342
395
|
}
|
|
343
396
|
|
|
344
397
|
function transformJuxToViewFunction(juxContent, functionName, pageName, relativePath, sharedModules, bundledPaths) {
|
|
@@ -367,9 +420,7 @@ function transformJuxToViewFunction(juxContent, functionName, pageName, relative
|
|
|
367
420
|
}
|
|
368
421
|
|
|
369
422
|
// 🔥 FIX: Always remove ImportDeclaration from the view function contents.
|
|
370
|
-
//
|
|
371
|
-
// Local/Bundled imports are hoisted via sharedModules mechanism with namespacing.
|
|
372
|
-
// Leaving import statements inside the function body causes SyntaxError.
|
|
423
|
+
// Leftover imports inside a function body cause SyntaxError.
|
|
373
424
|
nodesToRemove.push({ start: node.start, end: node.end });
|
|
374
425
|
}
|
|
375
426
|
if (node.type === 'ExportNamedDeclaration' || node.type === 'ExportDefaultDeclaration') {
|