47 lines
1.7 KiB
TypeScript
47 lines
1.7 KiB
TypeScript
// @ts-nocheck
|
|
import fs from "node:fs";
|
|
import { createRequire } from "node:module";
|
|
|
|
const require = createRequire(import.meta.url);
|
|
const JSZip = require("jszip");
|
|
|
|
const workbookPath = process.argv[2] || "xxx_endpoint_bo_2026-08-04_v1.xlsm";
|
|
const fromRow = Number.parseInt(process.argv[3] || "1", 10);
|
|
const toRow = Number.parseInt(process.argv[4] || String(fromRow), 10);
|
|
|
|
const zip = await JSZip.loadAsync(fs.readFileSync(workbookPath));
|
|
const sharedStringsXml = await zip.file("xl/sharedStrings.xml").async("string");
|
|
const sharedStrings = [...sharedStringsXml.matchAll(/<si[\s\S]*?<\/si>/g)].map((match) =>
|
|
decodeXml([...match[0].matchAll(/<t[^>]*>([\s\S]*?)<\/t>/g)].map((part) => part[1]).join("")),
|
|
);
|
|
|
|
const sqlSheetXml = await zip.file("xl/worksheets/sheet5.xml").async("string");
|
|
|
|
for (const rowMatch of sqlSheetXml.matchAll(/<row[^>]* r="(\d+)"[\s\S]*?<\/row>/g)) {
|
|
const rowNumber = Number(rowMatch[1]);
|
|
if (rowNumber < fromRow || rowNumber > toRow) continue;
|
|
|
|
const cells = {};
|
|
for (const cellMatch of rowMatch[0].matchAll(
|
|
/<c[^>]* r="([A-Z]+)\d+"([^>]*)>([\s\S]*?)<\/c>/g,
|
|
)) {
|
|
const [, column, attributes, body] = cellMatch;
|
|
const inlineString = body.match(/<is>[\s\S]*?<t[^>]*>([\s\S]*?)<\/t>[\s\S]*?<\/is>/);
|
|
const rawValue = body.match(/<v>([\s\S]*?)<\/v>/)?.[1] ?? inlineString?.[1] ?? "";
|
|
cells[column] = attributes.includes('t="s"')
|
|
? sharedStrings[Number(rawValue)] || ""
|
|
: decodeXml(rawValue);
|
|
}
|
|
|
|
console.log(JSON.stringify({ row: rowNumber, cells }, null, 2));
|
|
}
|
|
|
|
function decodeXml(value) {
|
|
return String(value ?? "")
|
|
.replaceAll("<", "<")
|
|
.replaceAll(">", ">")
|
|
.replaceAll("&", "&")
|
|
.replaceAll(""", '"')
|
|
.replaceAll("'", "'");
|
|
}
|