import type { Dependency } from "./manifests-json";
const REQUIREMENT_LINE = /^([A-Za-z0-9][A-Za-z0-9._-]*)(?:\[[^\]]*\])?\s*(.*)$/;
function pep508(spec: string, scope: Dependency["scope"], direct: boolean): Dependency | null {
const clean = spec.split(";")[0].trim();
const match = REQUIREMENT_LINE.exec(clean);
if (!match) return null;
return { name: match[1].toLowerCase(), requirement: match[2].trim() || null, scope, direct };
}
export function parseRequirementsTxt(text: string, scope: Dependency["scope"]): Dependency[] {
return text.split("\n").flatMap((raw) => {
const line = raw.split(" #")[0].trim();
if (!line || line.startsWith("#") || line.startsWith("-") || line.includes("://")) return [];
const dependency = pep508(line, scope, true);
return dependency ? [dependency] : [];
});
}
type TomlTable = { name: string; lines: string[] };
/** Linear comment stripper: `\s+#.*$` backtracks quadratically on long runs of whitespace (INT-A10). */
export function stripTomlComment(raw: string): string {
let quote: string | null = null;
for (let index = 0; index < raw.length; index++) {
const char = raw[index];
if (quote) {
if (char === "\\" && quote === '"') index++;
else if (char === quote) quote = null;
} else if (char === '"' || char === "'") quote = char;
else if (char === "#") return raw.slice(0, index);
}
return raw;
}
/** Splits TOML into tables by header; enough for dependency sections without a full TOML parser. */
function tomlTables(text: string): TomlTable[] {
const tables: TomlTable[] = [{ name: "", lines: [] }];
for (const raw of text.split("\n")) {