All files / packages/icons/.claude/skills/icons-release/scripts/lib csv.js

0% Statements 0/64
0% Branches 0/26
0% Functions 0/7
0% Lines 0/61

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109                                                                                                                                                                                                                         
'use strict'
 
/**
 * Minimal, deterministic RFC-4180-style CSV parser.
 *
 * Handles: quoted fields, commas inside quotes, escaped quotes (""),
 * embedded newlines inside quotes, and CRLF/CR/LF line endings.
 *
 * Returns rows as plain string arrays; header mapping is done in columns.js.
 */
 
function parseCsv(text) {
  const rows = []
  let field = ''
  let row = []
  let inQuotes = false
  let i = 0
  const len = text.length
 
  const pushField = () => {
    row.push(field)
    field = ''
  }
  const pushRow = () => {
    pushField()
    rows.push(row)
    row = []
  }
 
  while (i < len) {
    const char = text[i]
 
    if (inQuotes) {
      if (char === '"') {
        if (text[i + 1] === '"') {
          field += '"'
          i += 2
          continue
        }
        inQuotes = false
        i += 1
        continue
      }
      field += char
      i += 1
      continue
    }
 
    if (char === '"') {
      inQuotes = true
      i += 1
      continue
    }
    if (char === ',') {
      pushField()
      i += 1
      continue
    }
    if (char === '\r') {
      // swallow a following \n (CRLF)
      if (text[i + 1] === '\n') i += 1
      pushRow()
      i += 1
      continue
    }
    if (char === '\n') {
      pushRow()
      i += 1
      continue
    }
 
    field += char
    i += 1
  }
 
  // flush trailing field/row if the file does not end with a newline
  if (field.length > 0 || row.length > 0) {
    pushRow()
  }
 
  // drop fully-empty trailing rows
  while (rows.length > 0 && rows[rows.length - 1].every(cell => cell.trim() === '')) {
    rows.pop()
  }
 
  return rows
}
 
/**
 * Serialize an array-of-objects back to CSV using an explicit ordered header
 * list. Values are quoted when they contain a comma, quote, or newline.
 */
function toCsv(headers, records) {
  const escape = value => {
    const str = value == null ? '' : String(value)
    if (/[",\r\n]/.test(str)) {
      return '"' + str.replace(/"/g, '""') + '"'
    }
    return str
  }
  const lines = [headers.map(escape).join(',')]
  for (const record of records) {
    lines.push(headers.map(h => escape(record[h])).join(','))
  }
  return lines.join('\n') + '\n'
}
 
module.exports = { parseCsv, toCsv }