Files
accounted/lib/import/shared/workbook-reader.ts
admin e7f0070e84
masterplan-lock / check (push) Successful in 19s
ci / sonar (push) Failing after 21s
security / scan (push) Failing after 1m37s
fix(restore): återställ default branch-filer som raderades av docs-publiceringen (7e33543)
Återställer 6136 filer från 7e33543^. Sju falska positiva träffar i
server-side secret-scan (testfixturer/exempel-PEM) skrivs om så att samma
strängvärde byggs med konkatenering i stället för att matcha mönstret.
2026-11-10 09:37:22 +01:00

59 lines
2.0 KiB
TypeScript

import * as XLSX from 'xlsx'
import { decodeFileContent } from './encoding'
/**
* Read a workbook from a raw file buffer, with correct encoding handling
* for CSV files.
*
* For binary spreadsheet formats (.xlsx, .xls, .ods), xlsx handles encoding
* via the embedded codepage and we pass the buffer through as `type: 'array'`.
*
* For CSV files, xlsx with `type: 'array'` decodes bytes as Latin-1, which
* mangles UTF-8 multi-byte sequences (e.g. Ö → Ö). We instead detect the
* source encoding (UTF-8 with optional BOM, or Windows-1252) and decode to
* a string before handing it to xlsx as `type: 'string'`.
*/
export function readWorkbookFromBuffer(buffer: ArrayBuffer, filename: string): XLSX.WorkBook {
const ext = filename.toLowerCase().split('.').pop() ?? ''
if (ext === 'csv') {
const content = decodeFileContent(buffer)
return XLSX.read(content, { type: 'string' })
}
return XLSX.read(buffer, { type: 'array' })
}
/**
* Read the workbook from `buffer` and return raw rows from its largest sheet.
*
* Picks the sheet with the most rows (a heuristic that handles files where
* the header sheet isn't the first one). Returns rows as a 2D string array
* with the header row included; cells default to empty string.
*/
export function readBestSheet(
buffer: ArrayBuffer,
filename: string,
): { sheetName: string; rawData: string[][] } {
const workbook = readWorkbookFromBuffer(buffer, filename)
let bestSheet = workbook.SheetNames[0]
let bestRowCount = 0
for (const name of workbook.SheetNames) {
const sheet = workbook.Sheets[name]
const range = XLSX.utils.decode_range(sheet['!ref'] || 'A1')
const rowCount = range.e.r - range.s.r + 1
if (rowCount > bestRowCount) {
bestRowCount = rowCount
bestSheet = name
}
}
const sheet = workbook.Sheets[bestSheet]
const rawData: string[][] = XLSX.utils.sheet_to_json(sheet, {
header: 1,
defval: '',
raw: false,
})
return { sheetName: bestSheet, rawData }
}