hucre Documentation

repository·main·Indexed 23 days ago

https://github.com/productdevbook/hucre

A zero-dependency spreadsheet engine written in pure TypeScript for reading and writing XLSX, CSV, ODS, JSON, NDJSON, and XML. It features schema validation, streaming for large files via streamXlsxRows and XlsxStreamWriter, and support for advanced Excel features including password protection (ECMA-376 Agile), Pivot Tables, charts, data validation, and hyperlinks. The library is tree-shakeable and optimized for environments like the Edge runtime.

Tokens
39.8K
Snippets
70
Records
190
Agent score
79%

What's inside hucre

  1. Read and preserve Slicers and Timeline Filters

    main

    Hucre supports reading Excel Slicers (2010+) and Timeline Slicers (2013+). These are stored in workbook.slicerCaches / workbook.timelineCaches at the workbook level, and in sheet.slicers / sheet.timelines at the sheet level. When using saveXlsx, hucre ensures these parts are re-declared in the necessary XML files (Content_Types, workbook rels, extLst, and sheet rels) to prevent Excel from treating them as orphans and dropping them.

    import { readXlsx, parseSlicers, parseSlicerCache, parseTimelines, parseTimelineCache } from "hucre"
    
    const wb = await readXlsx(buf)
    
    // Workbook-level cache definitions.
    console.log(wb.slicerCaches) // SlicerCache[] (pivot-table or table source)
    console.log(wb.timelineCaches) // TimelineCache[]
    
    // Per-sheet slicer / timeline instances.
    for (const sheet of wb.sheets) {
      for (const s of sheet.slicers ?? []) console.log(s.name, s.cache, s.caption)
      for (const t of sheet.timelines ?? []) console.log(t.name, t.cache, t.level)
    }
  2. Understand the hucre Architecture

    main

    hucre is a modular library composed of several specialized engines. The core hucre module provides a unified read/write API with automatic format detection. Other key components include:

    • xlsx: Comprehensive Excel support including a reader, writer, roundtrip (open/modify/save), streaming (via AsyncGenerator), and auto-column width calculation.
    • ods: OpenDocument Spreadsheet support.
    • csv: RFC 4180 compliant parser/writer with streaming support.
    • builder: Fluent WorkbookBuilder and SheetBuilder APIs for constructing files.
    • sheet-ops: High-level operations like insert, delete, move, sort, find, replace, clone, and copy.
    • export: Support for HTML, Markdown, JSON, and TSV output, plus HTML import.
    • template: A {{placeholder}} template engine.
    • _schema: Handles schema validation, type coercion, and error collection.
    • cli: Command-line tools for converting, inspecting, and validating files.

    The library is zero-dependency and written in pure TypeScript. The ZIP engine utilizes the CompressionStream/DecompressionStream Web APIs, with a pure TypeScript fallback for environments where they are unavailable.

  3. Work with Excel Charts

    main

    Hucre provides a three-layer system for handling charts:

    1. Read: parseChart(xml) or getCharts(workbook) surfaces a Chart record containing titles, series, axes, legends, etc.
    2. Write: writeXlsx with charts: [SheetChart] emits chart parts and re-anchors them in the worksheet.
    3. Bridge: cloneChart(source, options) converts a parsed Chart into a writable SheetChart.

    Override Grammar for cloneChart:

    • undefined: Inherit from source.
    • null: Drop the property.
    • value: Replace with new value.

    Helper functions:

    • getCharts(workbook): Flattens all charts in the workbook into a single array.
    • addChart(sheet, chart): Appends a SheetChart to a WriteSheet.
    import { getCharts, openXlsx, parseChart, addChart, writeXlsx } from "hucre"
    
    // Reading charts
    const wb = await openXlsx(buf)
    for (const { sheetName, chart } of getCharts(wb)) {
      console.log(sheetName, chart.kinds, chart.title)
      console.log(chart.anchor) // { from: { row: 1, col: 3 }, to: { row: 16, col: 10 } }
      console.log(chart.axes?.x?.title, chart.axes?.y?.scale)
    
      for (const s of chart.series ?? []) {
        console.log(s.kind, s.name, s.valuesRef, s.color, s.dataLabels)
      }
    }
    
    // Standalone parser
    const chart = parseChart(xml)
  4. Stream XLSX rows for large files

    main

    To process large files without loading them entirely into memory, use streamXlsxRows from hucre/xlsx. This works with Uint8Array or ReadableStream (e.g., from a fetch response).

    Key features:

    • Async Generator: Yields rows one at a time.
    • maxRows: Cap the number of rows yielded to save resources.
    • range: Filter to a specific A1 range (e.g., "B2:D1000"). Cells outside the column span are masked to null.
  5. Preserve features during round-trip (Open/Modify/Save)

    main

    To modify an existing XLSX file without losing features that hucre doesn't natively manage (like charts, macros/VBA, or specific themes), use openXlsx and saveXlsx from hucre/xlsx. This ensures the underlying XML structure for unsupported features is preserved.

    import { openXlsx, saveXlsx } from "hucre/xlsx"
    
    const workbook = await openXlsx(buffer)
    workbook.sheets[0].rows[0][0] = "Updated!"
    const output = await saveXlsx(workbook) // Charts, VBA, themes preserved
  6. Password protect XLSX workbooks

    main

    hucre supports reading and writing password-protected XLSX files using the ECMA-376 Agile encryption scheme (Excel 2010+). This uses the platform's WebCrypto API.

    • Writing: Pass an encryption object with a password to writeXlsx.
    • Reading: Pass the password in the options for readXlsx, readObjects, or streamXlsxRows.
    • Errors:
      • EncryptedFileError: Thrown if the file is encrypted but no password was provided.
      • DecryptionError: Thrown if the provided password is incorrect.

    Note: You can adjust the security/speed trade-off using spinCount in the encryption options.

    import { writeXlsx, readXlsx, readObjects, EncryptedFileError, DecryptionError } from "hucre"
    
    // Write encrypted
    const encrypted = await writeXlsx({
      sheets: [{ name: "Secret", rows: [["pin", 1234]] }],
      encryption: { password: "hunter2" },
    })
    
    // Read encrypted
    try {
      const wb = await readXlsx(encrypted, { password: "hunter2" })
      const rows = await readObjects(encrypted, { password: "hunter2" })
    } catch (e) {
      if (e instanceof EncryptedFileError) console.log("needs a password")
      if (e instanceof DecryptionError) console.log("wrong password")
    }
  7. Quick Start: Read and Write XLSX files

    main

    You can use readXlsx to parse an XLSX buffer and writeXlsx to generate an XLSX file from a structured object. The writeXlsx function accepts an object containing an array of sheets. Each sheet defines columns (with header, key, width, and optional numFmt) and data (an array of objects matching the column keys).

    import { readXlsx, writeXlsx } from "hucre"
    
    // Read an XLSX file
    const workbook = await readXlsx(buffer)
    console.log(workbook.sheets[0].rows)
    
    // Write an XLSX file
    const xlsx = await writeXlsx({
      sheets: [
        {
          name: "Products",
          columns: [
            { header: "Name", key: "name", width: 25 },
            { header: "Price", key: "price", width: 12, numFmt: "$#,##0.00" },
            { header: "Stock", key: "stock", width: 10 },
          ],
          data: [
            { name: "Widget", price: 9.99, stock: 142 },
            { name: "Gadget", price: 24.5, stock: 87 },
          ],
        },
      ],
    })
  8. Optimize bundle size with Tree Shaking

    main

    To minimize your bundle size, avoid importing from the main hucre entry point. Instead, import specific format handlers from their respective sub-paths. This allows your bundler to tree-shake unused formats.

    import { readXlsx, writeXlsx } from "hucre/xlsx" // XLSX only
    import { parseCsv, writeCsv } from "hucre/csv" // CSV only (~2 KB gzipped)
    import { readOds, writeOds } from "hucre/ods" // ODS only
    import { parseJson, writeNdjson } from "hucre/json" // JSON / NDJSON
    import { readXml, writeXml } from "hucre/xml" // Tabular XML
  9. Add hyperlinks to XLSX sheets

    main

    You can add hyperlinks to cells in two ways:

    1. Using a cells map: For non-tabular layouts, map cell coordinates (e.g., "0,0") to objects containing a hyperlink property.
    2. Inline in data rows: For tabular reports, include a link object directly in the data row, keyed by the column's key. Use the link() helper or a plain object { text, hyperlink, tooltip? }.

    Internal references (to other sheets/cells) should be prefixed with # (e.g., #Sheet2!A1).

    import { writeXlsx, link } from "hucre/xlsx"
    
    // Inline in data rows (Recommended for tables)
    await writeXlsx({
      sheets: [
        {
          name: "Summary",
          columns: [
            { header: "Link", key: "link" },
            { header: "ID", key: "id" },
          ],
          data: [
            { link: link("Open", "https://example.com/items/abc-123"), id: "abc-123" },
            { link: { text: "Open", hyperlink: "https://example.com/items/def-456" }, id: "def-456" },
          ],
        },
      ],
    })
  10. Read and author Pivot Tables

    main

    Hucre can read existing Pivot Tables and their workbook-level caches into workbook.pivotCaches and sheet.pivotTables.

    Authoring from scratch: You can create new Pivot Tables using writeXlsx by defining them in the pivotTables array of a sheet. Hucre handles the creation of the pivot cache (definition + records) and all required relationships. Note that the numeric layout (totals, etc.) is computed by Excel on the first open via fullCalcOnLoad.

    import { readXlsx, writeXlsx, parsePivotTable, parsePivotCacheDefinition, attachPivotCacheFields } from "hucre"
    
    // Reading existing pivots
    const wb = await readXlsx(buf)
    for (const cache of wb.pivotCaches ?? []) {
      console.log(cache.cacheId, cache.sourceSheet, cache.sourceRef, cache.fieldNames)
    }
    for (const sheet of wb.sheets) {
      for (const pt of sheet.pivotTables ?? []) {
        console.log(pt.name, pt.location, pt.cacheId)
        for (const f of pt.fields) {
          console.log("  ", f.name, f.axis, f.function)
        }
      }
    }
    
    // Authoring new pivots
    const xlsx = await writeXlsx({
      sheets: [
        {
          name: "Data",
          rows: [
            ["Region", "Product", "Revenue"],
            ["EU", "A", 100],
            ["EU", "B", 50],
            ["US", "A", 200],
            ["US", "B", 75],
          ],
        },
        {
          name: "Pivot",
          pivotTables: [
            {
              name: "SalesPivot",
              sourceSheet: "Data",
              rows: ["Region"],
              columns: ["Product"],
              values: [{ field: "Revenue", function: "sum" }],
            },
          ],
        },
      ],
    })