@zerospacegg/vynthra
Version:
Discord bot for ZeroSpace.gg data
270 lines (239 loc) • 8.81 kB
JavaScript
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!");