bilig

xlsx-calc alternative for Node workbook recalculation

You probably got here because you have an .xlsx file, you changed an input cell in Node, and now the cached formula result is wrong.

xlsx-calc can be the right answer. If your workbook is already a SheetJS object and the formulas you use are in its supported set, the API is simple: edit cells, call XLSX_CALC(workbook), read the values.

Use @bilig/workpaper when the spreadsheet is not just a file. The usual case is a backend decision path: quote approval, payout checks, import validation, or an agent tool that needs to write inputs, recalculate, read outputs, and save a state it can test again later.

Quick choice

You need Start with
Recalculate a supported formula set on a SheetJS workbook object xlsx-calc
Read and write lots of spreadsheet file formats SheetJS
Build styled .xlsx files ExcelJS or SheetJS
Keep a formula workbook as service state @bilig/workpaper
Read recalculated outputs before accepting a request @bilig/workpaper
Persist JSON state and still import or export XLSX at the edge @bilig/workpaper

If you only need to refresh an existing XLSX file before your service returns, try the file-level package before migrating workbook state:

npx --package @bilig/xlsx-formula-recalc xlsx-recalc pricing.xlsx \
  --set Inputs!B2=48 \
  --read Summary!B7 \
  --out pricing.recalculated.xlsx \
  --json

That is the whole distinction. xlsx-calc is a calculator over a workbook object. Bilig is a workbook runtime with import/export at the edges.

Node service recalculation path

import { readFile, writeFile } from 'node:fs/promises'
import { WorkPaper } from '@bilig/workpaper'
import { exportXlsx, importXlsx } from '@bilig/workpaper/xlsx'

const source = await readFile('pricing-model.xlsx')
const imported = importXlsx(source, 'pricing-model.xlsx')
const workbook = WorkPaper.buildFromSnapshot(imported.snapshot)

const inputs = workbook.getSheetId('Inputs')
const summary = workbook.getSheetId('Summary')
if (inputs === undefined || summary === undefined) {
  throw new Error('Expected Inputs and Summary sheets')
}

workbook.setCellContents({ sheet: inputs, row: 1, col: 1 }, 48)
workbook.setCellContents({ sheet: inputs, row: 2, col: 1 }, 1250)

const decision = workbook.getCellValue({ sheet: summary, row: 6, col: 1 })
if (decision !== 'approved') {
  throw new Error(`Expected approved decision, got ${String(decision)}`)
}

const edited = exportXlsx(workbook.exportSnapshot())
await writeFile('pricing-model-edited.xlsx', edited)

The maintained example is small enough to inspect:

git clone https://github.com/proompteng/bilig.git
cd bilig/examples/xlsx-recalculation-node
npm install
npm start

It should end with checks like these:

{
  "decisionChanged": true,
  "exportedReimportMatchesAfter": true,
  "formulasSurvivedXlsxRoundTrip": true,
  "verified": true
}

Measured Lane

Historical xlsx-calc timing comparisons are intentionally excluded from the default quality story. The product gate here is deterministic workbook recalculation behavior, not a checked-in competitor contract report or .cache research artifact.

What I Would Test

For a production service, I would not ship this based on one happy-path example. I would add tests for:

If the service mostly formats files, keep using a file library. If the service acts on the calculated result, put the cells behind a small adapter and test that adapter like business logic.

Repository and release notes: https://github.com/proompteng/bilig.