xlsx-for-ai 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/index.js +55 -5
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -1,5 +1,24 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ // Self-respawn with a larger V8 heap before loading anything else.
4
+ // Some real-world .xlsx files (sub-1MB on disk but with huge calc chains or
5
+ // shared-string tables) blow Node's default ~4GB heap during parse. Re-execing
6
+ // with --max-old-space-size=8192 fixes this transparently. The sentinel env
7
+ // var prevents an infinite respawn loop.
8
+ if (!process.env.XLSX_FOR_AI_RESPAWNED) {
9
+ const v8 = require('v8');
10
+ const heapLimitMB = v8.getHeapStatistics().heap_size_limit / 1024 / 1024;
11
+ if (heapLimitMB < 8000) {
12
+ const { spawnSync } = require('child_process');
13
+ const r = spawnSync(
14
+ process.execPath,
15
+ ['--max-old-space-size=8192', __filename, ...process.argv.slice(2)],
16
+ { stdio: 'inherit', env: { ...process.env, XLSX_FOR_AI_RESPAWNED: '1' } }
17
+ );
18
+ process.exit(r.status ?? 1);
19
+ }
20
+ }
21
+
3
22
  const path = require('path');
4
23
  const fs = require('fs');
5
24
  const ExcelJS = require('exceljs');
@@ -537,8 +556,32 @@ async function main() {
537
556
  process.exit(1);
538
557
  }
539
558
 
559
+ const stat = fs.statSync(xlsxPath);
560
+ if (stat.size === 0) {
561
+ console.error(`File is empty (0 bytes), not a valid xlsx: ${xlsxPath}`);
562
+ process.exit(1);
563
+ }
564
+ // Minimum valid zip is a 22-byte end-of-central-directory record. Anything
565
+ // smaller cannot be an xlsx; ExcelJS would crash with a misleading
566
+ // "Corrupted zip" error from deep in its parser.
567
+ if (stat.size < 22) {
568
+ console.error(`File is too small (${stat.size} bytes) to be a valid xlsx: ${xlsxPath}`);
569
+ process.exit(1);
570
+ }
571
+
540
572
  const wb = new ExcelJS.Workbook();
541
- await wb.xlsx.readFile(xlsxPath);
573
+ try {
574
+ await wb.xlsx.readFile(xlsxPath);
575
+ } catch (err) {
576
+ const msg = err && err.message ? err.message : String(err);
577
+ console.error(`Failed to read ${xlsxPath}: ${msg}`);
578
+ if (/End of data reached|Corrupted zip|invalid signature/i.test(msg)) {
579
+ console.error('Hint: file may be truncated or not a real xlsx. Try opening it in Excel to confirm.');
580
+ } else if (/Cannot read propert/i.test(msg)) {
581
+ console.error('Hint: file parsed as a zip but a workbook part is malformed. Try --list-sheets for a lighter probe.');
582
+ }
583
+ process.exit(1);
584
+ }
542
585
 
543
586
  // --list-sheets: print summary and exit
544
587
  if (opts.listSheets) {
@@ -551,9 +594,12 @@ async function main() {
551
594
  : wb.worksheets;
552
595
 
553
596
  if (sheets.length === 0) {
554
- console.error(sheetFilter
555
- ? `Sheet "${sheetFilter}" not found. Available: ${wb.worksheets.map(s => s.name).join(', ')}`
556
- : 'No sheets in workbook');
597
+ if (sheetFilter) {
598
+ console.error(`Sheet "${sheetFilter}" not found. Available: ${wb.worksheets.map(s => s.name).join(', ')}`);
599
+ } else {
600
+ console.error('No sheets in workbook.');
601
+ console.error('Hint: this can happen when a non-Excel tool wrote the file with backslashes in zip entry paths (e.g. xl\\worksheets\\sheet1.xml). ExcelJS only recognizes forward-slash entries.');
602
+ }
557
603
  process.exit(1);
558
604
  }
559
605
 
@@ -601,6 +647,10 @@ async function main() {
601
647
  }
602
648
 
603
649
  main().catch((err) => {
604
- console.error(err.message);
650
+ const msg = err && err.message ? err.message : String(err);
651
+ console.error(msg);
652
+ if (/Invalid string length/i.test(msg)) {
653
+ console.error('Hint: this sheet renders to a text dump larger than V8\'s 512MB string limit. Try --max-rows N or --max-cols N to bound the output, or --json which streams per cell.');
654
+ }
605
655
  process.exit(1);
606
656
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xlsx-for-ai",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "CLI that converts .xlsx files into rich text or JSON dumps that AI coding agents (Claude, Cursor, Copilot, ChatGPT, etc.) can read — preserving values, formulas, formatting, colors, column widths, frozen panes, named ranges, tables, and more.",
5
5
  "main": "index.js",
6
6
  "bin": {