igboscript
Version:
A lightweight programming language for writing JavaScript in Igbo
286 lines (245 loc) • 10.5 kB
JavaScript
const chalk = require("chalk");
const { translations } = require("../constant/TranslationConstant");
function translate(code) {
try {
let jsCode = code.normalize("NFC");
// Protect string literals, template literals, and JSX
const stringPlaceholder = "__STRING__";
const templatePlaceholder = "__TEMPLATE__";
const jsxPlaceholder = "__JSX__";
const strings = [];
const templates = [];
const jsxFragments = [];
// Protect template literals FIRST
jsCode = jsCode.replace(/`([^`\\]*(?:\\.[^`\\]*)*)`/g, (match, p1) => {
templates.push(p1);
return `\`${templatePlaceholder}${templates.length - 1}\``;
});
// Protect JSX fragments
jsCode = jsCode.replace(/(tagi[^tagiMechie]*tagiMechie|mechieOnwe)/g, (match) => {
jsxFragments.push(match.replace("tagi", "<").replace("tagiMechie", ">").replace("mechieOnwe", "/>"));
return `${jsxPlaceholder}${jsxFragments.length - 1}`;
});
// Protect string literals
jsCode = jsCode.replace(/"([^"\\]*(?:\\.[^"\\]*)*)"/g, (match, p1) => {
strings.push(p1);
return `"${stringPlaceholder}${strings.length - 1}"`;
});
// FRAMEWORK-SPECIFIC TRANSLATIONS FIRST
const expressTranslations = {
"ngwaExpress": "express",
"jiNgwa": "use",
"njikwa": "middleware",
"ụzọGet": "get",
"ụzọPost": "post",
"ụzọPut": "put",
"ụzọDelete": "delete",
"arịrịọ": "req",
"nzipụta": "res",
"arịrịọQuery": "req.query",
"arịrịọBody": "req.body",
"arịrịọParams": "req.params",
"ozi": "message",
"ok": "success",
"ndi": "users",
"onye": "user",
"obodo": "city",
"aha": "name",
"id": "id"
};
// Apply Express-specific translations first
Object.entries(expressTranslations).forEach(([igbo, js]) => {
const escapedIgbo = igbo.replace(/[-[\]{}()*+?.,\\^$|#]/g, "\\$&");
const regex = new RegExp(`(^|[^a-zA-Z0-9])${escapedIgbo}(?![a-zA-Z0-9])`, "g");
jsCode = jsCode.replace(regex, `$1${js}`);
});
// *** ENHANCED IMPORT TRANSLATION - Handle ALL import patterns ***
// Pattern 1: nwetakwa express na "express"
jsCode = jsCode.replace(
/\bnwetakwa\s+(\w+)\s+na\s+"([^"]+)"\s*;?/gi,
'const $1 = require("$2");'
);
// Pattern 2: nwetakwa * na "module" site X
jsCode = jsCode.replace(
/\bnwetakwa\s*\*\s+na\s+"([^"]+)"\s+site\s+(\w+)\s*;?/gi,
'const $2 = require("$1");'
);
// Pattern 3: nwetakwa "module" site X (default import)
jsCode = jsCode.replace(
/\bnwetakwa\s+"([^"]+)"\s+site\s+(\w+)\s*;?/gi,
'const $2 = require("$1");'
);
// Pattern 4: Direct module name without site keyword
jsCode = jsCode.replace(
/\bnwetakwa\s+(\w+)\s+na\s+"(\w+)"\s*;?/gi,
'const $1 = require("$2");'
);
// Pattern 5: Handle "import X as X" style (if user wrote it that way)
jsCode = jsCode.replace(
/import\s+(\w+)\s+as\s+(\w+);?/gi,
'const $1 = require("$1");'
);
// Handle export statements
jsCode = jsCode.replace(
/\bnwapụtaNke\s+(\w+)\s*;?/gi,
'module.exports = $1;'
);
jsCode = jsCode.replace(
/\bnwapụtaNwere\s+(\w+)\s*;?/gi,
'exports.$1 = $1;'
);
jsCode = jsCode.replace(
/\bnwapụta\s+default\s*\(\s*(\w+)\s*\)\s*;?/gi,
'module.exports = $1;'
);
jsCode = jsCode.replace(
/\bexport\s+default\s+(\w+)\s*;?/gi,
'module.exports = $1;'
);
// IMPROVED: Handle Express method calls with better context awareness
const expressMethods = {
'nzipụtaStatus': 'status',
'nzipụtaJSON': 'json',
'nzipụta': 'send',
'nzipụtaHeader': 'setHeader'
};
// Replace method names in method chains and standalone calls
Object.entries(expressMethods).forEach(([igbo, js]) => {
const escapedIgbo = igbo.replace(/[-[\]{}()*+?.,\\^$|#]/g, "\\$&");
// Handle method chaining: res.status(200).json(data)
jsCode = jsCode.replace(
new RegExp(`(nzipụta|res)\\.${escapedIgbo}\\s*\\(([^)]*)\\)`, "gi"),
`$1.${js}($2)`
);
// Handle chained methods: res.status(200).json(data)
jsCode = jsCode.replace(
new RegExp(`(nzipụta|res)\\.${escapedIgbo}\\s*\\(\\s*([^)]+)\\s*\\)\\s*\\.\\s*(${escapedIgbo})\\s*\\(([^)]*)\\)`, "gi"),
`$1.${js}($2).$3($4)`
);
});
// Handle array methods
const arrayMethods = [
{ igbo: "tinye", js: "push" },
{ igbo: "wepụ", js: "pop" },
{ igbo: "nyefee", js: "shift" },
{ igbo: "tinyeN'ihu", js: "unshift" },
{ igbo: "haziUdi", js: "map" },
{ igbo: "nyo", js: "filter" },
{ igbo: "belata", js: "reduce" },
{ igbo: "chọọ", js: "find" },
{ igbo: "haziNdepụta", js: "join" }
];
for (const { igbo, js } of arrayMethods) {
const escapedIgbo = igbo.replace(/[-[\]{}()*+?.,\\^$|#]/g, "\\$&");
jsCode = jsCode.replace(
new RegExp(`(\\w+)\\.${escapedIgbo}\\s*\\(([^)]*)\\)`, "gi"),
`$1.${js}($2)`
);
jsCode = jsCode.replace(
new RegExp(`(\\w+)\\.${escapedIgbo}\\s*\\(\\s*\\)`, "gi"),
`$1.${js}()`
);
}
// Replace other general keywords (excluding Express and array methods)
const sortedKeys = Object.keys(translations)
.filter(key => !key.includes('tagi')) // Skip JSX tags
.filter(key => !arrayMethods.some(method => method.igbo === key)) // Skip array methods
.filter(key => !Object.keys(expressTranslations).includes(key)) // Skip Express translations
.filter(key => !Object.keys(expressMethods).includes(key)) // Skip Express methods
.sort((a, b) => b.length - a.length);
for (const igbo of sortedKeys) {
const jsEquivalent = translations[igbo];
const escapedIgbo = igbo
.replace(/[-[\]{}()*+?.,\\^$|#]/g, "\\$&")
.replace(/\s+/g, "\\s+");
const regex = new RegExp(`(^|[^a-zA-Z0-9])${escapedIgbo}(?![a-zA-Z0-9])`, "gi");
jsCode = jsCode.replace(regex, `$1${jsEquivalent}`);
}
// CRITICAL FIX: Remove semicolons inside object literals
// Fix: { key: value; } → { key: value }
jsCode = jsCode.replace(/([{,]\s*[\w]+:\s*[^,}]+);(?=[,}])/gi, '$1');
// Fix: ...newUser; → ...newUser
jsCode = jsCode.replace(/(\.\.\.\w+);/gi, '$1');
// Fix specific issues
jsCode = jsCode.replace(/(\w+)\.nke\.length/gi, '$1.length');
jsCode = jsCode.replace(/modul\.nwapụta\s+default\s*\(([^)]+)\)/gi, 'module.exports = $1');
jsCode = jsCode.replace(/nwetakwa\s+(\w+)\s+site\s+\w+;/gi, 'const $1 = require("$1");');
// Fix Number.parseInt translation
jsCode = jsCode.replace(/nọmba\s*\(\s*([^)]+)\s*\)/gi, 'Number.parseInt($1)');
// Restore protected content
jsCode = jsCode.replace(new RegExp(`"${stringPlaceholder}(\\d+)"`, "gi"), (match, index) => {
return `"${strings[index] || ''}"`;
});
jsCode = jsCode.replace(new RegExp(`\\\`${templatePlaceholder}(\\d+)\\\``, "gi"), (match, index) => {
return `\`${templates[index] || ''}\``;
});
jsCode = jsCode.replace(new RegExp(`${jsxPlaceholder}(\\d+)`, "gi"), (match, index) => {
return jsxFragments[index] || match;
});
// IMPROVED: Add missing app variable declaration
if (jsCode.includes('express') && !jsCode.includes('const app =')) {
const firstImportIndex = jsCode.indexOf('const express');
if (firstImportIndex !== -1) {
const appDeclaration = '\nconst app = express();\n';
jsCode = jsCode.slice(0, firstImportIndex + 'const express'.length) + appDeclaration + jsCode.slice(firstImportIndex + 'const express'.length);
}
}
// IMPROVED: Intelligent semicolon management - MORE CAREFUL
const lines = jsCode.split("\n");
const fixedLines = lines.map((line, index) => {
const trimmed = line.trim();
if (!trimmed) return line;
// Skip lines that already end with semicolon or are structural
if (
trimmed.endsWith(";") ||
trimmed.endsWith("{") ||
trimmed.endsWith("}") ||
trimmed.endsWith(",") ||
trimmed.endsWith(")") || // Function calls
trimmed.match(/^(const|let|var|function|if|else|for|while|switch|return|export|import|class)\b/) ||
trimmed.includes("=>") ||
trimmed.match(/^\/\/.*$/) || // Comments
trimmed.match(/^\s*\//) || // Block comments
// Skip method calls
trimmed.includes('.json(') ||
trimmed.includes('.status(') ||
trimmed.includes('.send(') ||
trimmed.includes('.listen(') ||
// Skip object literals and array declarations
(trimmed.includes('{') && trimmed.includes('}')) ||
(trimmed.includes('[') && trimmed.includes(']'))
) {
return line;
}
// Add semicolon only for standalone statements that need it
if (
// Variable assignments
trimmed.includes('=') && !trimmed.includes('=>') &&
!trimmed.includes('{') && !trimmed.includes('[') &&
!trimmed.includes('(')
) {
const indent = line.match(/^\s*/)[0];
return indent + trimmed + ";";
}
return line;
});
jsCode = fixedLines.join("\n");
// Final cleanup for Express app
jsCode = jsCode.replace(
/const\s+ngwaExpress\s*=\s*require\s*\(\s*"express"\s*\)/gi,
'const express = require("express")'
);
// Ensure app variable is properly named
jsCode = jsCode.replace(/const\s+ngwa\s*=/gi, 'const app = ');
// Fix any remaining res.res or req.req patterns
jsCode = jsCode.replace(/\bres\.res\b/gi, 'res');
jsCode = jsCode.replace(/\breq\.req\b/gi, 'req');
// FINAL FIX: Ensure proper export syntax
jsCode = jsCode.replace(/export\s+default\s*\(\s*app\s*\)/gi, 'module.exports = app');
jsCode = jsCode.replace(/export\s+default\s*\(\s*(\w+)\s*\)/gi, 'module.exports = $1');
return jsCode;
} catch (error) {
throw new Error(`Nsogbu na ntụgharị: ${error.message} (Translation Error)`);
}
}
module.exports = { translate };