UNPKG

@zerospacegg/vynthra

Version:
270 lines (239 loc) 8.81 kB
#!/usr/bin/env node const fs = require("fs"); const path = require("path"); /** * Transform ESM syntax to CommonJS in JavaScript files */ function transformEsmToCjs(content) { let transformed = content; // Track used variable names to avoid duplicates const usedVarNames = new Set(); // Transform export * from "./module.js" transformed = transformed.replace( /export \* from ["']([^"']+)["'];?/g, (match, modulePath) => { let varName = `_${modulePath.replace(/[^a-zA-Z0-9]/g, "_")}`; let counter = 1; while (usedVarNames.has(varName)) { varName = `_${modulePath.replace(/[^a-zA-Z0-9]/g, "_")}_${counter}`; counter++; } usedVarNames.add(varName); const cjsModulePath = modulePath.replace(/\.js$/, ".cjs"); return `const ${varName} = require("${cjsModulePath}");\nObject.assign(exports, ${varName});`; }, ); // Transform export { ... } from "./module.js" transformed = transformed.replace( /export \{([^}]+)\} from ["']([^"']+)["'];?/g, (match, exports, modulePath) => { let varName = `_${modulePath.replace(/[^a-zA-Z0-9]/g, "_")}`; let counter = 1; while (usedVarNames.has(varName)) { varName = `_${modulePath.replace(/[^a-zA-Z0-9]/g, "_")}_${counter}`; counter++; } usedVarNames.add(varName); const cjsModulePath = modulePath.replace(/\.js$/, ".cjs"); const exportsList = exports.split(",").map((e) => e.trim()); const assignments = exportsList .map((exp) => { const [imported, exported] = exp.includes(" as ") ? exp.split(" as ").map((s) => s.trim()) : [exp, exp]; return `exports.${exported} = ${varName}.${imported};`; }) .join("\n"); return `const ${varName} = require("${cjsModulePath}");\n${assignments}`; }, ); // Transform individual named exports transformed = transformed.replace( /export \{([^}]+)\};?/g, (match, exports) => { const exportsList = exports.split(",").map((e) => e.trim()); return exportsList .map((exp) => { const [local, exported] = exp.includes(" as ") ? exp.split(" as ").map((s) => s.trim()) : [exp, exp]; return `exports.${exported} = ${local};`; }) .join("\n"); }, ); // Transform export const/let/var/function/class declarations // First, handle function and class exports (including async functions) transformed = transformed.replace( /export (async\s+function|function|class) ([a-zA-Z_$][a-zA-Z0-9_$]*)/g, (match, type, name) => { return `${type} ${name}`; }, ); // Then handle const/let/var exports transformed = transformed.replace( /export (const|let|var) ([a-zA-Z_$][a-zA-Z0-9_$]*)/g, (match, type, name) => { return `${type} ${name}`; }, ); // Handle export interface and export type (TypeScript - remove them as they're compile-time only) transformed = transformed.replace( /export (interface|type) [^;{]+[;{][^}]*}?/g, "", ); // Add exports for all exported declarations const originalContent = content; const exportedDeclarations = []; // Find all exported functions/classes (including async functions) const exportedFunctionClass = originalContent.match( /export (async\s+function|function|class) ([a-zA-Z_$][a-zA-Z0-9_$]*)/g, ); if (exportedFunctionClass) { exportedFunctionClass.forEach((match) => { const parts = match.split(" "); const name = parts[parts.length - 1]; exportedDeclarations.push(name); }); } // Find all exported const/let/var const exportedVars = originalContent.match( /export (const|let|var) ([a-zA-Z_$][a-zA-Z0-9_$]*)/g, ); if (exportedVars) { exportedVars.forEach((match) => { const name = match.split(" ")[2]; exportedDeclarations.push(name); }); } // Add exports at the end if (exportedDeclarations.length > 0) { const exportStatements = exportedDeclarations .map((name) => `exports.${name} = ${name};`) .join("\n"); transformed += "\n" + exportStatements; } // Transform import statements transformed = transformed.replace( /import \{([^}]+)\} from ["']([^"']+)["'];?/g, (match, imports, modulePath) => { // Check if it's a relative import (internal module) if (modulePath.startsWith("./") || modulePath.startsWith("../")) { const cjsModulePath = modulePath.replace(/\.js$/, ".cjs"); const importsList = imports.split(",").map((i) => i.trim()); const destructuring = importsList .map((imp) => { const [imported, local] = imp.includes(" as ") ? imp.split(" as ").map((s) => s.trim()) : [imp, imp]; return local; }) .join(", "); return `const { ${destructuring} } = require("${cjsModulePath}");`; } else { // External dependency - use regular require const importsList = imports.split(",").map((i) => i.trim()); const destructuring = importsList .map((imp) => { const [imported, local] = imp.includes(" as ") ? imp.split(" as ").map((s) => s.trim()) : [imp, imp]; return local; }) .join(", "); return `const { ${destructuring} } = require("${modulePath}");`; } }, ); // Transform import * as name from "module" transformed = transformed.replace( /import \* as ([a-zA-Z_$][a-zA-Z0-9_$]*) from ["']([^"']+)["'];?/g, (match, varName, modulePath) => { if (modulePath.startsWith("./") || modulePath.startsWith("../")) { const cjsModulePath = modulePath.replace(/\.js$/, ".cjs"); return `const ${varName} = require("${cjsModulePath}");`; } else { return `const ${varName} = require("${modulePath}");`; } }, ); // Transform import name from "module" transformed = transformed.replace( /import ([a-zA-Z_$][a-zA-Z0-9_$]*) from ["']([^"']+)["'];?/g, (match, varName, modulePath) => { if (modulePath.startsWith("./") || modulePath.startsWith("../")) { const cjsModulePath = modulePath.replace(/\.js$/, ".cjs"); return `const ${varName} = require("${cjsModulePath}");`; } else { return `const ${varName} = require("${modulePath}");`; } }, ); // Transform import type statements (remove them as they're TypeScript only) transformed = transformed.replace( /import type \{[^}]+\} from ["'][^"']+["'];?/g, "", ); transformed = transformed.replace( /import type [a-zA-Z_$][a-zA-Z0-9_$]* from ["'][^"']+["'];?/g, "", ); // Remove empty export statements transformed = transformed.replace(/export \{\s*\};?/g, ""); // Transform export default (preserve named exports) // Handle multiline object literals specially const defaultExportMatch = transformed.match( /export default \{[\s\S]*?\};?$/m, ); if (defaultExportMatch) { const fullMatch = defaultExportMatch[0]; const objectContent = fullMatch .replace(/^export default /, "") .replace(/;?$/, ""); transformed = transformed.replace( fullMatch, `module.exports = Object.assign(${objectContent}, exports);`, ); } else { // Handle single line default exports transformed = transformed.replace( /export default (.+);?$/gm, (match, defaultExport) => { return `module.exports = Object.assign({ default: ${defaultExport} }, exports);`; }, ); } // Clean up multiple empty lines transformed = transformed.replace(/\n\s*\n\s*\n/g, "\n\n"); return transformed; } /** * Process all .js files in a directory recursively */ function processDirectory(dirPath) { const entries = fs.readdirSync(dirPath, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dirPath, entry.name); if (entry.isDirectory()) { processDirectory(fullPath); } else if (entry.isFile() && entry.name.endsWith(".js")) { console.log(`Transforming ${fullPath}`); const content = fs.readFileSync(fullPath, "utf8"); const transformed = transformEsmToCjs(content); // Write as .cjs file const cjsPath = fullPath.replace(/\.js$/, ".cjs"); fs.writeFileSync(cjsPath, transformed, "utf8"); // Remove the original .js file fs.unlinkSync(fullPath); } } } // Main execution const cjsDistPath = path.join(__dirname, "..", "dist", "cjs"); if (!fs.existsSync(cjsDistPath)) { console.error("CJS dist directory does not exist:", cjsDistPath); process.exit(1); } console.log("Transforming ESM to CJS in:", cjsDistPath); processDirectory(cjsDistPath); console.log("Transformation complete!");