All files / src/rules/converters excel-converter.ts

55.88% Statements 38/68
92.85% Branches 13/14
75% Functions 3/4
55.88% Lines 38/68

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 692x 2x 2x 2x 2x 2x 2x 2x 8x 8x 8x 8x 8x 91x 91x 8x 8x 8x 2x 4x 4x 4x 7x 7x 7x 7x 2x 2x 5x 5x 4x 4x 2x 2x 2x 2x 2x                                                             2x  
import * as vscode from 'vscode';
import * as XLSX from 'xlsx';
import { RuleConverter } from './converter';
import { convertContentWithAI } from '../import-service';
import { buildSystemPrompt } from './prompt-builder';
import type { CustomRule } from '../../types';
import { t } from '../../i18n/messages';
 
export function buildMarkdownTable(rows: Record<string, unknown>[], sheetName: string): string {
  const keys = [...new Set(rows.flatMap(row => Object.keys(row)))];
  const header = `| ${keys.join(' | ')} |`;
  const separator = `| ${keys.map(() => '---').join(' | ')} |`;
  const dataLines = rows.map(row => {
    const cells = keys.map(k => String(row[k] ?? ''));
    return `| ${cells.join(' | ')} |`;
  });
  return [`## ${sheetName}`, header, separator, ...dataLines].join('\n');
}
 
export function renderWorkbookToMarkdown(workbook: XLSX.WorkBook): string | null {
  const parts: string[] = [];
  for (const sheetName of workbook.SheetNames) {
    const sheet = workbook.Sheets[sheetName];
    if (!sheet) { continue; }
    const rows = XLSX.utils.sheet_to_json<Record<string, string>>(sheet);
    if (rows.length === 0) {
      continue;
    }
    parts.push(buildMarkdownTable(rows, sheetName));
  }
  return parts.length === 0 ? null : parts.join('\n\n');
}
 
export class ExcelConverter implements RuleConverter {
  supportedExtensions = ['.xlsx', '.xls'];
 
  async convert(srcPath: string, context: vscode.ExtensionContext, existingRules?: CustomRule[]): Promise<string | null> {
    let workbook: XLSX.WorkBook;
    try {
      workbook = XLSX.readFile(srcPath);
    } catch (err) {
      const msg = err instanceof Error ? err.message : String(err);
      vscode.window.showErrorMessage(t('import.excelReadFail', { 0: msg }));
      return null;
    }

    if (workbook.SheetNames.length === 0) {
      vscode.window.showErrorMessage(t('import.excelEmpty'));
      return null;
    }

    let combined: string | null;
    try {
      combined = renderWorkbookToMarkdown(workbook);
    } catch (err) {
      const msg = err instanceof Error ? err.message : String(err);
      vscode.window.showErrorMessage(t('import.excelReadFail', { 0: msg }));
      return null;
    }

    if (combined === null) {
      vscode.window.showErrorMessage(t('import.excelNoData'));
      return null;
    }

    return convertContentWithAI(combined, context, buildSystemPrompt('spreadsheet', existingRules));
  }
}