Files
linkedin_skills/scripts/lib/csv.js
T
2026-04-01 21:59:13 +08:00

86 lines
1.7 KiB
JavaScript

function parseCsv(content) {
const rows = [];
let row = [];
let field = "";
let inQuotes = false;
// Character-level parser to correctly handle commas/newlines inside quotes.
for (let i = 0; i < content.length; i += 1) {
const char = content[i];
const next = content[i + 1];
if (char === "\"") {
if (inQuotes && next === "\"") {
field += "\"";
i += 1;
} else {
inQuotes = !inQuotes;
}
continue;
}
if (char === "," && !inQuotes) {
row.push(field);
field = "";
continue;
}
if ((char === "\n" || char === "\r") && !inQuotes) {
if (char === "\r" && next === "\n") {
i += 1;
}
row.push(field);
field = "";
if (row.length > 1 || (row.length === 1 && row[0] !== "")) {
rows.push(row);
}
row = [];
continue;
}
field += char;
}
if (field.length || row.length) {
row.push(field);
rows.push(row);
}
if (!rows.length) {
return { headers: [], records: [] };
}
const headers = rows[0].map((h) => h.trim());
const records = rows.slice(1).map((r) => {
const obj = {};
headers.forEach((h, idx) => {
obj[h] = (r[idx] || "").trim();
});
return obj;
});
return { headers, records };
}
function escapeCsvValue(value) {
const s = String(value ?? "");
if (/[",\n\r]/.test(s)) {
return `"${s.replace(/"/g, "\"\"")}"`;
}
return s;
}
function toCsv(headers, records) {
const lines = [headers.map(escapeCsvValue).join(",")];
records.forEach((record) => {
const row = headers.map((h) => escapeCsvValue(record[h] ?? ""));
lines.push(row.join(","));
});
return `${lines.join("\n")}\n`;
}
module.exports = {
parseCsv,
toCsv
};