@ztimson/utils 0.23.15 → 0.23.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -684,24 +684,29 @@ function validateEmail(email) {
684
684
  return /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/.test(email);
685
685
  }
686
686
  function fromCsv(csv, hasHeaders = true) {
687
- const row = csv.split("\n");
687
+ function parseLine(line) {
688
+ const columns = [];
689
+ let current = "", inQuotes = false;
690
+ for (let i = 0; i < line.length; i++) {
691
+ const char = line[i];
692
+ const nextChar = line[i + 1];
693
+ if (char === '"') {
694
+ if (inQuotes && nextChar === '"') {
695
+ current += '"';
696
+ i++;
697
+ } else inQuotes = !inQuotes;
698
+ } else if (char === "," && !inQuotes) {
699
+ columns.push(current);
700
+ current = "";
701
+ } else current += char;
702
+ }
703
+ columns.push(current);
704
+ return columns.map((col) => col.replace(/^"|"$/g, "").replace(/""/g, '"'));
705
+ }
706
+ const row = csv.split(/\r?\n/);
688
707
  let headers = hasHeaders ? row.splice(0, 1)[0] : null;
689
- if (headers) headers = headers.match(/(?:[^,"']+|"[^"]*"|'[^']*')+/g);
708
+ if (headers) headers = headers.match(/(?:[^,"']+|"(?:[^"]|"")*"|'(?:[^']|'')*')+/g);
690
709
  return row.map((r2) => {
691
- function parseLine(line) {
692
- const parts = line.split(","), columns = [];
693
- let quoted = false;
694
- for (const p2 of parts) {
695
- if (quoted) columns[columns.length - 1] = columns.at(-1) + "," + p2;
696
- else columns.push(p2);
697
- if (/[^"]"$/g.test(p2)) {
698
- quoted = false;
699
- } else if (/^"[^"]/g.test(p2)) {
700
- quoted = true;
701
- }
702
- }
703
- return columns;
704
- }
705
710
  const props = parseLine(r2);
706
711
  const h = headers || Array(props.length).fill(null).map((r22, i) => {
707
712
  let letter = "";
@@ -717,12 +722,16 @@ function fromCsv(csv, hasHeaders = true) {
717
722
  });
718
723
  }
719
724
  function toCsv(target, flatten = true) {
720
- const headers = new ASet(target.reduce((acc, row) => [...acc, ...Object.keys(flatten ? flattenObj(row) : row)], []));
725
+ const t = makeArray(target);
726
+ const headers = new ASet(t.reduce((acc, row) => [...acc, ...Object.keys(flatten ? flattenObj(row) : row)], []));
721
727
  return [
722
728
  headers.join(","),
723
- ...target.map((row) => headers.map((h) => {
729
+ ...t.map((row) => headers.map((h) => {
724
730
  const value2 = dotNotation(row, h);
725
- return typeof value2 == "object" && value2 != null ? '"' + JSONSanitize(value2).replaceAll('"', '""') + '"' : value2;
731
+ if (value2 == null) return "";
732
+ if (typeof value2 == "object") return `"${JSONSanitize(value2).replaceAll("`", '""')}"`;
733
+ if (typeof value2 == "string" && /[\n"]/g.test(value2)) return `"${value2.replaceAll('"', '""')}"`;
734
+ return value2;
726
735
  }).join(","))
727
736
  ].join("\n");
728
737
  }