UNPKG

uascript

Version:
135 lines (134 loc) 5.32 kB
"use strict"; 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); } }