Merge pull request #1 from C4illin/main

merge new
This commit is contained in:
Kosztyk 2026-01-11 23:09:48 +02:00 committed by GitHub
commit ddb7292116
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 181 additions and 2 deletions

View file

@ -22,6 +22,7 @@ import { convert as convertPotrace, properties as propertiesPotrace } from "./po
import { convert as convertresvg, properties as propertiesresvg } from "./resvg";
import { convert as convertImage, properties as propertiesImage } from "./vips";
import { convert as convertVtracer, properties as propertiesVtracer } from "./vtracer";
import { convert as convertVcf, properties as propertiesVcf } from "./vcf";
import { convert as convertxelatex, properties as propertiesxelatex } from "./xelatex";
import { convert as convertMarkitdown, properties as propertiesMarkitdown } from "./markitdown";
@ -128,6 +129,10 @@ const properties: Record<
properties: propertiesVtracer,
converter: convertVtracer,
},
vcf: {
properties: propertiesVcf,
converter: convertVcf,
},
markitDown: {
properties: propertiesMarkitdown,
converter: convertMarkitdown,

69
src/converters/vcf.ts Normal file
View file

@ -0,0 +1,69 @@
import { readFile, writeFile } from "fs/promises";
export const properties = {
from: {
contacts: ["vcf"],
},
to: {
contacts: ["csv"],
},
};
export function parseVCF(data: string): Record<string, string>[] {
const cards = data
.split(/BEGIN:VCARD/)
.slice(1)
.map((card) => card.split(/END:VCARD/)[0])
.filter((card) => card);
return cards
.map((card) => {
if (!card) return {};
const lines = card.split("\n").filter((line) => line.trim());
const contact: Record<string, string> = {};
for (const line of lines) {
const colonIndex = line.indexOf(":");
if (colonIndex === -1) continue;
const key = line.slice(0, colonIndex).trim();
const value = line.slice(colonIndex + 1).trim();
if (key === "FN") {
contact["Full Name"] = value;
} else if (key === "N") {
const parts = value.split(";");
contact["Last Name"] = parts[0] || "";
contact["First Name"] = parts[1] || "";
} else if (key.startsWith("TEL")) {
contact["Phone"] = value;
} else if (key.startsWith("EMAIL")) {
contact["Email"] = value;
} else if (key === "ORG") {
contact["Organization"] = value.split(";")[0] || "";
}
}
return contact;
})
.filter((contact) => Object.keys(contact).length > 0);
}
export function toCSV(data: Record<string, string>[]): string {
if (!data.length) return "";
const first = data[0];
if (!first) return "";
const headers = Object.keys(first);
const escape = (str: string) => `"${str.replace(/"/g, '""')}"`;
const rows = data.map((row) => headers.map((h) => escape(row[h] || "")).join(","));
return [headers.join(","), ...rows].join("\n");
}
export async function convert(
filePath: string,
fileType: string,
convertTo: string,
targetPath: string,
options?: unknown, // eslint-disable-line @typescript-eslint/no-unused-vars
): Promise<string> {
const vcfData = await readFile(filePath, "utf-8");
const contacts = parseVCF(vcfData);
const csvData = toCSV(contacts);
await writeFile(targetPath, csvData, "utf-8");
return "Done";
}