-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathfile-parser.ts
More file actions
93 lines (75 loc) · 2.83 KB
/
Copy pathfile-parser.ts
File metadata and controls
93 lines (75 loc) · 2.83 KB
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
/**
* Client-side file parsing for PDF, DOCX, and Excel files.
* Extracts text content that can be included in AI prompts.
*/
const MAX_FILE_CHARS = 50000
export const SUPPORTED_EXTENSIONS = new Set(["pdf", "docx", "xlsx", "xls", "txt", "md", "csv"])
export type ParseWarning = "truncated" | "empty"
export interface ParseResult {
text: string
warning?: ParseWarning
}
export async function parseFile(file: File): Promise<ParseResult> {
const ext = file.name.split('.').pop()?.toLowerCase() ?? ''
let result: string
if (ext === 'pdf') result = await parsePDF(file)
else if (ext === 'docx') result = await parseDOCX(file)
else if (ext === 'xlsx' || ext === 'xls') result = await parseExcel(file)
else if (ext === 'txt' || ext === 'md' || ext === 'csv') result = await parseText(file)
else return { text: `[Unsupported file type: ${file.name}]` }
if (!result.trim()) {
return { text: '', warning: 'empty' }
}
if (result.length > MAX_FILE_CHARS) {
return { text: result.slice(0, MAX_FILE_CHARS) + '\n[...file truncated]', warning: 'truncated' }
}
return { text: result }
}
async function parseText(file: File): Promise<string> {
return file.text()
}
async function parsePDF(file: File): Promise<string> {
const pdfjsLib = await import('pdfjs-dist')
pdfjsLib.GlobalWorkerOptions.workerSrc = `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjsLib.version}/pdf.worker.min.mjs`
const arrayBuffer = await file.arrayBuffer()
const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise
const pages: string[] = []
let totalLength = 0
for (let i = 1; i <= pdf.numPages; i++) {
const page = await pdf.getPage(i)
const content = await page.getTextContent()
const text = content.items
// eslint-disable-next-line @typescript-eslint/no-explicit-any
.map((item: any) => item.str ?? '')
.join(' ')
if (text.trim()) {
pages.push(text.trim())
totalLength += text.trim().length
if (totalLength >= MAX_FILE_CHARS) break
}
}
return pages.join('\n\n')
}
async function parseDOCX(file: File): Promise<string> {
const mammoth = await import('mammoth')
const arrayBuffer = await file.arrayBuffer()
const result = await mammoth.extractRawText({ arrayBuffer })
return result.value
}
async function parseExcel(file: File): Promise<string> {
const XLSX = await import('xlsx')
const arrayBuffer = await file.arrayBuffer()
const workbook = XLSX.read(arrayBuffer, { type: 'array' })
const sheets: string[] = []
let totalLength = 0
for (const sheetName of workbook.SheetNames) {
const sheet = workbook.Sheets[sheetName]
const csv = XLSX.utils.sheet_to_csv(sheet)
if (csv.trim()) {
sheets.push(`[Sheet: ${sheetName}]\n${csv}`)
totalLength += csv.length
if (totalLength >= MAX_FILE_CHARS) break
}
}
return sheets.join('\n\n')
}