@composize/excel
is a DSL for creating Excel files through a composable, declarative API.
It provides an intuitive interface to create complex Excel workbooks without directly dealing with the complexities of the underlying ExcelJS library.
npm install @composize/excel
Here's a simple example of how to create an Excel file using the DSL:
import { cell, row, workbook } from '@composize/excel';
workbook(() => {
row(() => {
cell('title1');
cell('title2');
});
row(() => {
cell('value1');
cell('value2');
});
});
The @composize/excel
DSL follows a hierarchical structure that mirrors Excel's document model, with composable functions representing each level of the hierarchy.
graph LR
workbook --> worksheet
worksheet --> row
row --> cell
row --> borderedCell
The @composize/excel
DSL uses a functional composition pattern where parent elements accept callback functions that define their children. This creates a natural nesting structure that makes the code's visual layout reflect the structure of the resulting Excel document.
The @composize/excel
DSL includes the following key features:
Function | Description | Parameters | Returns |
---|---|---|---|
workbook() |
Creates a new workbook | composable: () => void |
Workbook |
worksheet() |
Adds a new worksheet to the current workbook | name: string, composable: () => void |
Worksheet |
row() |
Adds a new row to the current worksheet | composable: () => void |
Row |
cell() |
Adds a cell to the current row | value: string, options?: CellOptions |
Cell |
borderedCell() |
Adds a cell with thin borders to the current row | value: string, options?: CellOptions |
Cell |
The cell()
and borderedCell()
functions accept an optional options parameter that allows for cell styling and merging:
Option | Type | Description |
---|---|---|
colSpan |
number |
Number of columns to merge |
rowSpan |
number |
Number of rows to merge |
numFmt |
string |
Number format string |
font |
Font |
Font properties (size, name, color, etc.) |
alignment |
Alignment |
Text alignment properties |
border |
Borders |
Cell border properties |
fill |
Fill |
Cell background fill properties |
The simplest way to create an Excel file is by nesting function calls that mirror the structure of your desired spreadsheet:
import { workbook, row, cell } from '@composize/excel';
const book = workbook(() => {
row(() => {
cell('Title 1');
cell('Title 2');
});
row(() => {
cell('Value 1');
cell('Value 2');
});
});
// Save the workbook to a file
book.xlsx.writeFile('./example.xlsx');
This creates a simple 2x2 table with headers and values.
By default, if no worksheet is explicitly created, the DSL automatically creates a worksheet named "Sheet1":
const book = workbook(() => {
row(() => {
cell('Data without explicit worksheet');
});
});
You can explicitly create named worksheets using the worksheet()
function:
const book = workbook(() => {
worksheet('First Sheet', () => {
row(() => {
cell('Data in first sheet');
});
});
worksheet('Second Sheet', () => {
row(() => {
cell('Data in second sheet');
});
});
});
The Excel DSL supports merging cells both horizontally (using colSpan
) and vertically (using rowSpan
):
To merge cells horizontally, use the colSpan
option:
workbook(() => {
row(() => {
cell('Wide Header', { colSpan: 3 });
});
row(() => {
cell('Data 1');
cell('Data 2');
cell('Data 3');
});
});
This creates a header cell that spans across three columns.
To merge cells vertically, use the rowSpan
option:
workbook(() => {
row(() => {
cell('Tall Cell', { rowSpan: 2 });
cell('Top Cell');
});
row(() => {
// No need to set the first cell, it's already covered by the merged cell
cell('Bottom Cell');
});
});
This creates a cell that spans two rows in the first column.
You can combine both horizontal and vertical merging:
workbook(() => {
row(() => {
cell('Large Cell', { rowSpan: 2, colSpan: 2 });
cell('Top Right');
});
row(() => {
cell('Bottom Right');
});
});
This creates a cell that spans two rows and two columns in the top-left corner.
The cell()
function accepts styling options from ExcelJS's Cell object:
cell('Styled Text', {
font: { bold: true, size: 14 },
alignment: { vertical: 'middle', horizontal: 'center' },
fill: { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFFF0000' } }
});
For convenience, the DSL provides a borderedCell()
function that applies thin borders to all sides of a cell:
row(() => {
borderedCell('Bordered Content');
borderedCell('Custom Format', { numFmt: '0.00' });
});
The borderedCell()
function accepts the same options as cell()
, but automatically adds thin borders.
The Excel DSL automatically adjusts column widths based on the content of each cell. For merged cells, auto-fitting enables text wrapping instead of increasing width.
Here's a more complex example that demonstrates many features of the Excel DSL:
const book = workbook(() => {
worksheet('Report', () => {
// Header row with merged title
row(() => {
cell('Quarterly Report', {
colSpan: 4,
font: { bold: true, size: 16 },
alignment: { horizontal: 'center' }
});
});
// Empty row for spacing
row(() => {});
// Column headers
row(() => {
borderedCell('Category', { font: { bold: true } });
borderedCell('Q1', { font: { bold: true } });
borderedCell('Q2', { font: { bold: true } });
borderedCell('Q3', { font: { bold: true } });
});
// Data rows
row(() => {
borderedCell('Revenue');
borderedCell(10000, { numFmt: '$#,##0' });
borderedCell(12000, { numFmt: '$#,##0' });
borderedCell(15000, { numFmt: '$#,##0' });
});
row(() => {
borderedCell('Expenses');
borderedCell(8000, { numFmt: '$#,##0' });
borderedCell(8500, { numFmt: '$#,##0' });
borderedCell(9000, { numFmt: '$#,##0' });
});
row(() => {
borderedCell('Profit', { font: { bold: true } });
borderedCell(2000, { numFmt: '$#,##0', font: { bold: true } });
borderedCell(3500, { numFmt: '$#,##0', font: { bold: true } });
borderedCell(6000, { numFmt: '$#,##0', font: { bold: true } });
});
});
});
book.xlsx.writeFile('quarterly_report.xlsx');
Learn about the latest improvements.
Do you love ✨ composize ✨ ? Star for this project!
Thanks to JetBrains for supporting us free open source licenses.