uascript
Version:
Javascript in Ukrainian
135 lines (134 loc) • 5.32 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.compileByPath = void 0;
const interpreter_1 = require("../interpreter/interpreter");
const extensions_1 = require("../constants/extensions");
const shell_command_1 = require("../utils/shell-command");
const fs = require('fs/promises');
const path = require('path');
const shellCommand = new shell_command_1.ShellCommand();
function validatePath(inputPath, basePath) {
const resolvedInput = path.resolve(inputPath);
const resolvedBase = path.resolve(basePath);
const relative = path.relative(resolvedBase, resolvedInput);
if (relative.startsWith('..') || path.isAbsolute(relative)) {
throw new Error(`Path traversal detected: ${inputPath} is outside ${basePath}`);
}
return resolvedInput;
}
async function compileByPath(inPath, outPath) {
const cwd = process.cwd();
const resolvedInPath = path.resolve(inPath);
const resolvedOutPath = path.resolve(outPath);
if (!resolvedInPath.startsWith(cwd)) {
throw new Error(`Input path must be within current directory: ${inPath}`);
}
if (!resolvedOutPath.startsWith(cwd)) {
throw new Error(`Output path must be within current directory: ${outPath}`);
}
try {
await fs.access(resolvedInPath);
}
catch {
throw new Error(`Input path does not exist: ${inPath}`);
}
try {
await shellCommand.rmrf(resolvedOutPath, cwd);
}
catch (error) {
if (!error.message.includes('ENOENT')) {
throw error;
}
}
try {
const stats = await fs.stat(resolvedInPath);
if (stats.isDirectory()) {
const filePaths = (await getFilesDeep(resolvedInPath)).flat(Infinity);
const compiledSources = await compileFiles(filePaths);
await writeCompiledFiles(resolvedInPath, resolvedOutPath, filePaths, compiledSources);
}
else {
const compiledSources = await compileFiles([resolvedInPath]);
await writeCompiledFiles(resolvedInPath, resolvedOutPath, [resolvedInPath], compiledSources);
}
}
catch (e) {
throw new Error(`Compilation failed: ${e.message}`);
}
}
exports.compileByPath = compileByPath;
async function getFilesDeep(dirPath) {
try {
const entries = await fs.readdir(dirPath, { withFileTypes: true });
const promises = entries.map(async (entry) => {
const filePath = path.join(dirPath, entry.name);
if (entry.isDirectory()) {
return getFilesDeep(filePath);
}
else if (entry.isFile() && filePath.endsWith(extensions_1.Extensions.UAS)) {
return filePath;
}
return null;
});
const results = await Promise.all(promises);
const flattened = results.flat(Infinity).filter((file) => file !== null);
return flattened;
}
catch (error) {
throw new Error(`Failed to read directory ${dirPath}: ${error.message}`);
}
}
async function compileFiles(files) {
const MAX_FILE_SIZE = 10 * 1024 * 1024;
const promises = files.map(async (file) => {
try {
const stats = await fs.stat(file);
if (stats.size > MAX_FILE_SIZE) {
throw new Error(`File ${file} exceeds maximum size of ${MAX_FILE_SIZE} bytes`);
}
const source = await fs.readFile(file, 'utf-8');
const compiledSource = await interpreter_1.compile(source);
return compiledSource;
}
catch (e) {
throw new Error(`Failed to compile ${file}: ${e.message}`);
}
});
return Promise.all(promises);
}
async function writeCompiledFiles(inPath, outPath, filePaths, compiledSource) {
const cwd = process.cwd();
for (let i = 0; i < filePaths.length; i++) {
const filePath = filePaths[i];
const outCompiledPath = prepareCompiledFilePath(filePath, inPath, outPath);
const resolvedOutPath = path.resolve(outCompiledPath);
if (!resolvedOutPath.startsWith(cwd)) {
throw new Error(`Output path traversal detected: ${outCompiledPath}`);
}
await prepareFolders(outPath, outCompiledPath, cwd);
try {
await fs.writeFile(resolvedOutPath, compiledSource[i], { flag: 'wx' });
}
catch (error) {
if (error.code === 'EEXIST') {
await fs.writeFile(resolvedOutPath, compiledSource[i], { flag: 'w' });
}
else {
throw new Error(`Failed to write file ${outCompiledPath}: ${error.message}`);
}
}
}
}
function prepareCompiledFilePath(filePath, inPath, outPath) {
const relativePath = path.relative(inPath, filePath);
const outputPath = path.join(outPath, relativePath);
return outputPath.replace(extensions_1.Extensions.UAS, extensions_1.Extensions.JS);
}
async function prepareFolders(rootPath, filePath, basePath) {
const dirPath = path.dirname(filePath);
const relativeDir = path.relative(rootPath, dirPath);
if (relativeDir && !relativeDir.startsWith('..')) {
const fullDirPath = path.join(rootPath, relativeDir);
await shellCommand.mkdir(fullDirPath, true, basePath);
}
}