init
This commit is contained in:
91
convert-xlsx-to-csv.ts
Normal file
91
convert-xlsx-to-csv.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import XLSX from "xlsx";
|
||||
import { mkdir, readdir, writeFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
|
||||
const INPUT_DIR = process.env.BIZMAX_OUTPUT_DIR ?? path.resolve(process.cwd(), "output");
|
||||
const CSV_DIR = process.env.BIZMAX_CSV_DIR ?? path.join(INPUT_DIR, "csv");
|
||||
const CSV_NAME = process.env.BIZMAX_CSV_NAME ?? "bizmax-all.csv";
|
||||
|
||||
function toText(value: unknown): string {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
return String(value);
|
||||
}
|
||||
|
||||
function escapeCsv(value: string): string {
|
||||
if (/[",\n\r]/.test(value)) {
|
||||
return `"${value.replace(/"/g, "\"\"")}"`;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function rowToCsv(row: string[]): string {
|
||||
return row.map(escapeCsv).join(",");
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const entries = await readdir(INPUT_DIR, { withFileTypes: true });
|
||||
const xlsxFiles = entries
|
||||
.filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith(".xlsx"))
|
||||
.map((entry) => path.join(INPUT_DIR, entry.name))
|
||||
.sort();
|
||||
|
||||
if (xlsxFiles.length === 0) {
|
||||
throw new Error(`No .xlsx files found in ${INPUT_DIR}`);
|
||||
}
|
||||
|
||||
const outputRows: string[] = [];
|
||||
let wroteHeader = false;
|
||||
|
||||
for (const xlsxFile of xlsxFiles) {
|
||||
const workbook = XLSX.readFile(xlsxFile, { cellDates: false });
|
||||
|
||||
for (const sheetName of workbook.SheetNames) {
|
||||
const worksheet = workbook.Sheets[sheetName];
|
||||
const rows = XLSX.utils.sheet_to_json<unknown[]>(worksheet, {
|
||||
header: 1,
|
||||
raw: false,
|
||||
defval: "",
|
||||
});
|
||||
|
||||
if (rows.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const headerRow = (rows[0] ?? []).map(toText);
|
||||
if (!wroteHeader) {
|
||||
outputRows.push(
|
||||
rowToCsv(["source_file", "source_sheet", ...headerRow]),
|
||||
);
|
||||
wroteHeader = true;
|
||||
}
|
||||
|
||||
for (const row of rows.slice(1)) {
|
||||
const values = (row as unknown[]).map(toText);
|
||||
outputRows.push(
|
||||
rowToCsv([
|
||||
path.basename(xlsxFile),
|
||||
sheetName,
|
||||
...values,
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!wroteHeader) {
|
||||
throw new Error(`No worksheet rows found in ${INPUT_DIR}`);
|
||||
}
|
||||
|
||||
await mkdir(CSV_DIR, { recursive: true });
|
||||
const outputPath = path.join(CSV_DIR, CSV_NAME);
|
||||
await writeFile(outputPath, `${outputRows.join("\n")}\n`, "utf8");
|
||||
process.stdout.write(`${outputPath}\n`);
|
||||
}
|
||||
|
||||
main().catch((error: unknown) => {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
process.stderr.write(`${message}\n`);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
Reference in New Issue
Block a user