SevdanurGenc's picture
Upload 300 files
9438a57 verified
Raw History Blame Contribute Delete
5.08 kB
import { unzipSync, strFromU8 } from 'fflate';
import type { Language } from './types';
/** Çok dosyalı girdi: seçilen dosyaları ve zip arşivlerini okur, test edilecek dosyayı ve yardımcıları ayırır. */
export const EXT_LANG: Record<string, Language | 'html'> = {
py: 'python', js: 'javascript', mjs: 'javascript', cjs: 'javascript', jsx: 'javascript',
ts: 'typescript', tsx: 'typescript', java: 'java', html: 'html', htm: 'html',
};
/** Test edilmez ama kodun okuyabilmesi için yanına konur */
const DATA_EXT = new Set(['json', 'txt', 'csv', 'css', 'xml', 'yaml', 'yml', 'properties', 'md']);
const SKIP_DIR = /(^|\/)(node_modules|__pycache__|\.git|\.venv|venv|dist|build|target|out|\.idea|\.vscode)\//;
export const ACCEPT = [...Object.keys(EXT_LANG), 'zip', ...DATA_EXT].map((e) => '.' + e).join(',');
export const MAX_FILE = 200_000;
export const MAX_TOTAL = 600_000;
export const MAX_EXTRA = 30;
export const extOf = (name: string) => (name.split('.').pop() || '').toLowerCase();
export const baseName = (p: string) => p.split('/').pop() || p;
export const isTestFile = (p: string) =>
/(^|\/)(tests?|__tests__|spec)\//i.test(p)
|| /(^|\/)(test_[^/]*\.py|[^/]*_test\.py|[^/]*\.(test|spec)\.[jt]sx?|[^/]*Tests?\.java)$/i.test(p);
/** Araç/yapılandırma dosyaları: koşucunun kendi dosyalarıyla çakışır, yardımcı olarak gönderilmez */
const isToolingFile = (p: string) =>
/(^|\/)(package(-lock)?\.json|yarn\.lock|pnpm-lock\.yaml|tsconfig\.json|jest\.config\.[cm]?[jt]s|babel\.config\.[cm]?js|\.babelrc|playwright\.config\.[jt]s|conftest\.py|pytest\.ini|setup\.cfg|tox\.ini|pyproject\.toml|pom\.xml|build\.gradle(\.kts)?|vite\.config\.[jt]s)$/i.test(p);
export interface InFile { path: string; text: string }
export interface ReadResult { files: InFile[]; skipped: string[]; tooBig: string[] }
/** Dosyaları ve zip'leri okur; ikili/çok büyük/desteklenmeyen dosyaları ayıklar. */
export async function readInputs(list: FileList | File[]): Promise<ReadResult> {
const files: InFile[] = [];
const skipped: string[] = [];
const tooBig: string[] = [];
const add = (path: string, bytes: Uint8Array | string) => {
const ext = extOf(path);
if (SKIP_DIR.test(path + '/') || SKIP_DIR.test(path)) return;
if (!EXT_LANG[ext] && !DATA_EXT.has(ext)) { skipped.push(path); return; }
if (isToolingFile(path)) { skipped.push(path); return; }
const size = typeof bytes === 'string' ? bytes.length : bytes.length;
if (size > MAX_FILE) { (EXT_LANG[ext] ? tooBig : skipped).push(path); return; }
const text = typeof bytes === 'string' ? bytes : strFromU8(bytes);
if (text.includes('\u0000')) { skipped.push(path); return; }
files.push({ path: path.replace(/\\/g, '/').replace(/^\.\//, ''), text });
};
for (const f of [...list]) {
if (extOf(f.name) === 'zip') {
const entries = unzipSync(new Uint8Array(await f.arrayBuffer()));
for (const [p, data] of Object.entries(entries)) if (!p.endsWith('/')) add(p, data);
} else {
const rel = (f as File & { webkitRelativePath?: string }).webkitRelativePath || f.name;
add(rel, await f.text());
}
}
// zip'teki ortak üst klasörü kaldır (proje/ src/a.py → src/a.py)
const parts = files.map((f) => f.path.split('/'));
while (parts.length && parts.every((p) => p.length > 1 && p[0] === parts[0][0])) parts.forEach((p) => p.shift());
files.forEach((f, i) => { f.path = parts[i].join('/'); });
return { files, skipped, tooBig };
}
export const langOf = (path: string) => EXT_LANG[extOf(path)];
/** Varsayılan hedef: test dosyası olmayan, en uzun kaynak dosyası (çoğunlukla asıl modül). */
export function pickTarget(files: InFile[]): string | null {
const code = files.filter((f) => { const l = langOf(f.path); return l && l !== 'html' && !isTestFile(f.path); });
const pool = code.length ? code : files.filter((f) => langOf(f.path) && langOf(f.path) !== 'html');
if (!pool.length) return null;
return [...pool].sort((a, b) => b.text.length - a.text.length)[0].path;
}
/** Hedefe göre yardımcı dosyalar: aynı dil ailesindeki kaynaklar + veri dosyaları; yollar hedefin klasörüne göre. */
export function helpersFor(files: InFile[], target: string): { extra: Record<string, string>; ignored: string[] } {
const tl = langOf(target);
const family = (l: string | undefined) => (l === 'typescript' || l === 'javascript' ? 'js' : l);
const dir = target.includes('/') ? target.slice(0, target.lastIndexOf('/') + 1) : '';
const extra: Record<string, string> = {};
const ignored: string[] = [];
for (const f of files) {
if (f.path === target) continue;
const l = langOf(f.path);
const ok = !l ? DATA_EXT.has(extOf(f.path)) : family(l) === family(tl) || (l === 'html' && tl === 'javascript');
if (!ok || isTestFile(f.path)) { ignored.push(f.path); continue; }
const rel = dir && f.path.startsWith(dir) ? f.path.slice(dir.length) : f.path;
if (Object.keys(extra).length < MAX_EXTRA) extra[rel] = f.text; else ignored.push(f.path);
}
return { extra, ignored };
}