Compare commits

..

3 Commits

Author SHA1 Message Date
bd8a3b7aa8 Updata Ghidra docs
added java line that we discussed earlier
2025-10-19 12:43:23 +00:00
0812116df1 Merge pull request 'added java installation step' (#1) from rasmus-patch-1 into master
Reviewed-on: Rasmus/docs.sheetjs.com#1
2025-10-19 12:38:38 +00:00
1c50200605 added java installation step 2025-10-19 12:37:53 +00:00
222 changed files with 2149 additions and 8263 deletions

@ -1,417 +1,417 @@
<?xml version="1.0"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40">
<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
<WindowHeight>10620</WindowHeight>
<WindowWidth>11020</WindowWidth>
<WindowTopX>2260</WindowTopX>
<WindowTopY>19600</WindowTopY>
<ActiveSheet>1</ActiveSheet>
<ProtectStructure>False</ProtectStructure>
<ProtectWindows>False</ProtectWindows>
</ExcelWorkbook>
<Styles>
<Style ss:ID="Default" ss:Name="Normal">
<Alignment ss:Vertical="Bottom"/>
<Borders/>
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000"/>
<Interior/>
<NumberFormat/>
<Protection/>
</Style>
<Style ss:ID="s16">
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#1C1E21"/>
</Style>
<Style ss:ID="s17">
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000" ss:Bold="1"/>
</Style>
<Style ss:ID="s19">
<Alignment ss:Horizontal="Center" ss:Vertical="Bottom"/>
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000" ss:Bold="1"/>
</Style>
<Style ss:ID="s20">
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#467886" ss:Underline="Single"/>
</Style>
</Styles>
<Worksheet ss:Name="Engines">
<Table ss:ExpandedColumnCount="8" ss:ExpandedRowCount="18" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="16">
<Column ss:Index="3" ss:Width="24"/>
<Column ss:Width="31"/>
<Column ss:Width="24"/>
<Column ss:Width="31"/>
<Column ss:Width="24"/>
<Column ss:Width="31"/>
<Row>
<Cell ss:MergeAcross="1" ss:StyleID="s17"><Data ss:Type="String"></Data></Cell>
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">MacOS</Data></Cell>
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">Windows</Data></Cell>
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">Linux</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s17"><Data ss:Type="String">Engine</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">Lang</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">x64</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">x64</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">x64</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/duktape#complete-example"><Data ss:Type="String">Duktape</Data></Cell>
<Cell><Data ss:Type="String">C</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/v8#complete-example"><Data ss:Type="String">V8</Data></Cell>
<Cell><Data ss:Type="String">C++</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/rhino#complete-example"><Data ss:Type="String">Rhino</Data></Cell>
<Cell><Data ss:Type="String">Java</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/jsc#complete-example"><Data ss:Type="String">JSC</Data></Cell>
<Cell><Data ss:Type="String">C++</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/jint#integration-example"><Data ss:Type="String">Jint</Data></Cell>
<Cell><Data ss:Type="String">C#</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/goja#complete-example"><Data ss:Type="String">Goja</Data></Cell>
<Cell><Data ss:Type="String">Go</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/nashorn#complete-example"><Data ss:Type="String">Nashorn</Data></Cell>
<Cell><Data ss:Type="String">Java</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/quickjs#integration-example"><Data ss:Type="String">QuickJS</Data></Cell>
<Cell><Data ss:Type="String">C</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/hermes#integration-example"><Data ss:Type="String">Hermes</Data></Cell>
<Cell><Data ss:Type="String">C++</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/chakra#integration-example"><Data ss:Type="String">ChakraCore</Data></Cell>
<Cell><Data ss:Type="String">C++</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/boa#complete-example"><Data ss:Type="String">Boa</Data></Cell>
<Cell><Data ss:Type="String">Rust</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/perl#complete-example"><Data ss:Type="String">JE</Data></Cell>
<Cell><Data ss:Type="String">Perl</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/jerryscript#integration-example"><Data ss:Type="String">JerryScript</Data></Cell>
<Cell><Data ss:Type="String">C</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/graaljs#complete-example"><Data ss:Type="String">GraalJS</Data></Cell>
<Cell><Data ss:Type="String">Java</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/mujs#integration-example"><Data ss:Type="String">MuJS</Data></Cell>
<Cell><Data ss:Type="String">C</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/jurassic#integration-example"><Data ss:Type="String">Jurassic.NET</Data></Cell>
<Cell><Data ss:Type="String">C#</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
</Table>
<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
<PageSetup>
<Header x:Margin="0.3"/>
<Footer x:Margin="0.3"/>
<PageMargins x:Bottom="0.75" x:Left="0.7" x:Right="0.7" x:Top="0.75"/>
</PageSetup>
<FreezePanes/>
<FrozenNoSplit/>
<SplitHorizontal>2</SplitHorizontal>
<TopRowBottomPane>2</TopRowBottomPane>
<ActivePane>2</ActivePane>
<Panes>
<Pane>
<Number>3</Number>
</Pane>
<Pane>
<Number>2</Number>
<ActiveRow>12</ActiveRow>
<ActiveCol>5</ActiveCol>
</Pane>
</Panes>
<ProtectObjects>False</ProtectObjects>
<ProtectScenarios>False</ProtectScenarios>
</WorksheetOptions>
</Worksheet>
<Worksheet ss:Name="Bindings">
<Table ss:ExpandedColumnCount="8" ss:ExpandedRowCount="20" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="16">
<Column ss:Index="3" ss:Width="24"/>
<Column ss:Width="31"/>
<Column ss:Width="24"/>
<Column ss:Width="31"/>
<Column ss:Width="24"/>
<Column ss:Width="31"/>
<Row>
<Cell ss:MergeAcross="1" ss:StyleID="s17"><Data ss:Type="String"></Data></Cell>
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">MacOS</Data></Cell>
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">Windows</Data></Cell>
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">Linux</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s17"><Data ss:Type="String">Engine</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">Lang</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">x64</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">x64</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">x64</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/duktape#perl"><Data ss:Type="String">Duktape</Data></Cell>
<Cell><Data ss:Type="String">Perl</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/duktape#php"><Data ss:Type="String">Duktape</Data></Cell>
<Cell><Data ss:Type="String">PHP</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/duktape#python"><Data ss:Type="String">Duktape</Data></Cell>
<Cell><Data ss:Type="String">Python</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/duktape#rust"><Data ss:Type="String">Duktape</Data></Cell>
<Cell><Data ss:Type="String">Rust</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/duktape#zig"><Data ss:Type="String">Duktape</Data></Cell>
<Cell><Data ss:Type="String">Zig</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/v8#rust"><Data ss:Type="String">V8</Data></Cell>
<Cell><Data ss:Type="String">Rust</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/v8#java"><Data ss:Type="String">V8</Data></Cell>
<Cell><Data ss:Type="String">Java</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✘</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/v8#c"><Data ss:Type="String">V8</Data></Cell>
<Cell><Data ss:Type="String">C#</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/v8#python"><Data ss:Type="String">V8</Data></Cell>
<Cell><Data ss:Type="String">Python</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/jsc#swift"><Data ss:Type="String">JSC</Data></Cell>
<Cell><Data ss:Type="String">Swift</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/jsc#rust"><Data ss:Type="String">JSC</Data></Cell>
<Cell><Data ss:Type="String">Rust</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/rb#complete-example"><Data ss:Type="String">ExecJS</Data></Cell>
<Cell><Data ss:Type="String">Ruby</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
</Table>
<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
<PageSetup>
<Header x:Margin="0.3"/>
<Footer x:Margin="0.3"/>
<PageMargins x:Bottom="0.75" x:Left="0.7" x:Right="0.7" x:Top="0.75"/>
</PageSetup>
<Selected/>
<FreezePanes/>
<FrozenNoSplit/>
<SplitHorizontal>2</SplitHorizontal>
<TopRowBottomPane>2</TopRowBottomPane>
<ActivePane>2</ActivePane>
<Panes>
<Pane>
<Number>3</Number>
</Pane>
<Pane>
<Number>2</Number>
<ActiveRow>3</ActiveRow>
<ActiveCol>1</ActiveCol>
</Pane>
</Panes>
<ProtectObjects>False</ProtectObjects>
<ProtectScenarios>False</ProtectScenarios>
</WorksheetOptions>
</Worksheet>
</Workbook>
<?xml version="1.0"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40">
<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
<WindowHeight>10620</WindowHeight>
<WindowWidth>11020</WindowWidth>
<WindowTopX>2260</WindowTopX>
<WindowTopY>19600</WindowTopY>
<ActiveSheet>1</ActiveSheet>
<ProtectStructure>False</ProtectStructure>
<ProtectWindows>False</ProtectWindows>
</ExcelWorkbook>
<Styles>
<Style ss:ID="Default" ss:Name="Normal">
<Alignment ss:Vertical="Bottom"/>
<Borders/>
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000"/>
<Interior/>
<NumberFormat/>
<Protection/>
</Style>
<Style ss:ID="s16">
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#1C1E21"/>
</Style>
<Style ss:ID="s17">
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000" ss:Bold="1"/>
</Style>
<Style ss:ID="s19">
<Alignment ss:Horizontal="Center" ss:Vertical="Bottom"/>
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000" ss:Bold="1"/>
</Style>
<Style ss:ID="s20">
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#467886" ss:Underline="Single"/>
</Style>
</Styles>
<Worksheet ss:Name="Engines">
<Table ss:ExpandedColumnCount="8" ss:ExpandedRowCount="18" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="16">
<Column ss:Index="3" ss:Width="24"/>
<Column ss:Width="31"/>
<Column ss:Width="24"/>
<Column ss:Width="31"/>
<Column ss:Width="24"/>
<Column ss:Width="31"/>
<Row>
<Cell ss:MergeAcross="1" ss:StyleID="s17"><Data ss:Type="String"></Data></Cell>
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">MacOS</Data></Cell>
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">Windows</Data></Cell>
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">Linux</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s17"><Data ss:Type="String">Engine</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">Lang</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">x64</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">x64</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">x64</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/duktape#complete-example"><Data ss:Type="String">Duktape</Data></Cell>
<Cell><Data ss:Type="String">C</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/v8#complete-example"><Data ss:Type="String">V8</Data></Cell>
<Cell><Data ss:Type="String">C++</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/rhino#complete-example"><Data ss:Type="String">Rhino</Data></Cell>
<Cell><Data ss:Type="String">Java</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/jsc#complete-example"><Data ss:Type="String">JSC</Data></Cell>
<Cell><Data ss:Type="String">C++</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/jint#integration-example"><Data ss:Type="String">Jint</Data></Cell>
<Cell><Data ss:Type="String">C#</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/goja#complete-example"><Data ss:Type="String">Goja</Data></Cell>
<Cell><Data ss:Type="String">Go</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/nashorn#complete-example"><Data ss:Type="String">Nashorn</Data></Cell>
<Cell><Data ss:Type="String">Java</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/quickjs#integration-example"><Data ss:Type="String">QuickJS</Data></Cell>
<Cell><Data ss:Type="String">C</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/hermes#integration-example"><Data ss:Type="String">Hermes</Data></Cell>
<Cell><Data ss:Type="String">C++</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/chakra#integration-example"><Data ss:Type="String">ChakraCore</Data></Cell>
<Cell><Data ss:Type="String">C++</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/boa#complete-example"><Data ss:Type="String">Boa</Data></Cell>
<Cell><Data ss:Type="String">Rust</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/perl#complete-example"><Data ss:Type="String">JE</Data></Cell>
<Cell><Data ss:Type="String">Perl</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/jerryscript#integration-example"><Data ss:Type="String">JerryScript</Data></Cell>
<Cell><Data ss:Type="String">C</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/graaljs#complete-example"><Data ss:Type="String">GraalJS</Data></Cell>
<Cell><Data ss:Type="String">Java</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/mujs#integration-example"><Data ss:Type="String">MuJS</Data></Cell>
<Cell><Data ss:Type="String">C</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/jurassic#integration-example"><Data ss:Type="String">Jurassic.NET</Data></Cell>
<Cell><Data ss:Type="String">C#</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
</Table>
<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
<PageSetup>
<Header x:Margin="0.3"/>
<Footer x:Margin="0.3"/>
<PageMargins x:Bottom="0.75" x:Left="0.7" x:Right="0.7" x:Top="0.75"/>
</PageSetup>
<FreezePanes/>
<FrozenNoSplit/>
<SplitHorizontal>2</SplitHorizontal>
<TopRowBottomPane>2</TopRowBottomPane>
<ActivePane>2</ActivePane>
<Panes>
<Pane>
<Number>3</Number>
</Pane>
<Pane>
<Number>2</Number>
<ActiveRow>12</ActiveRow>
<ActiveCol>5</ActiveCol>
</Pane>
</Panes>
<ProtectObjects>False</ProtectObjects>
<ProtectScenarios>False</ProtectScenarios>
</WorksheetOptions>
</Worksheet>
<Worksheet ss:Name="Bindings">
<Table ss:ExpandedColumnCount="8" ss:ExpandedRowCount="20" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="16">
<Column ss:Index="3" ss:Width="24"/>
<Column ss:Width="31"/>
<Column ss:Width="24"/>
<Column ss:Width="31"/>
<Column ss:Width="24"/>
<Column ss:Width="31"/>
<Row>
<Cell ss:MergeAcross="1" ss:StyleID="s17"><Data ss:Type="String"></Data></Cell>
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">MacOS</Data></Cell>
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">Windows</Data></Cell>
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">Linux</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s17"><Data ss:Type="String">Engine</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">Lang</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">x64</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">x64</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">x64</Data></Cell>
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/duktape#perl"><Data ss:Type="String">Duktape</Data></Cell>
<Cell><Data ss:Type="String">Perl</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/duktape#php"><Data ss:Type="String">Duktape</Data></Cell>
<Cell><Data ss:Type="String">PHP</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/duktape#python"><Data ss:Type="String">Duktape</Data></Cell>
<Cell><Data ss:Type="String">Python</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/duktape#rust"><Data ss:Type="String">Duktape</Data></Cell>
<Cell><Data ss:Type="String">Rust</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/duktape#zig"><Data ss:Type="String">Duktape</Data></Cell>
<Cell><Data ss:Type="String">Zig</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/v8#rust"><Data ss:Type="String">V8</Data></Cell>
<Cell><Data ss:Type="String">Rust</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/v8#java"><Data ss:Type="String">V8</Data></Cell>
<Cell><Data ss:Type="String">Java</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/v8#c"><Data ss:Type="String">V8</Data></Cell>
<Cell><Data ss:Type="String">C#</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/v8#python"><Data ss:Type="String">V8</Data></Cell>
<Cell><Data ss:Type="String">Python</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/jsc#swift"><Data ss:Type="String">JSC</Data></Cell>
<Cell><Data ss:Type="String">Swift</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/jsc#rust"><Data ss:Type="String">JSC</Data></Cell>
<Cell><Data ss:Type="String">Rust</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s16"/>
</Row>
<Row>
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/rb#complete-example"><Data ss:Type="String">ExecJS</Data></Cell>
<Cell><Data ss:Type="String">Ruby</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
</Row>
</Table>
<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
<PageSetup>
<Header x:Margin="0.3"/>
<Footer x:Margin="0.3"/>
<PageMargins x:Bottom="0.75" x:Left="0.7" x:Right="0.7" x:Top="0.75"/>
</PageSetup>
<Selected/>
<FreezePanes/>
<FrozenNoSplit/>
<SplitHorizontal>2</SplitHorizontal>
<TopRowBottomPane>2</TopRowBottomPane>
<ActivePane>2</ActivePane>
<Panes>
<Pane>
<Number>3</Number>
</Pane>
<Pane>
<Number>2</Number>
<ActiveRow>3</ActiveRow>
<ActiveCol>1</ActiveCol>
</Pane>
</Panes>
<ProtectObjects>False</ProtectObjects>
<ProtectScenarios>False</ProtectScenarios>
</WorksheetOptions>
</Worksheet>
</Workbook>

@ -121,12 +121,12 @@ This demo was last tested in the following deployments:
| Architecture | BunJS | Date |
|:-------------|:---------|:-----------|
| `darwin-x64` | `1.3.6` | 2026-01-20 |
| `darwin-arm` | `1.3.6` | 2026-01-19 |
| `win11-x64` | `1.3.6` | 2026-01-28 |
| `win11-arm` | `1.3.10` | 2026-03-07 |
| `linux-x64` | `1.3.6` | 2026-01-18 |
| `linux-arm` | `1.3.10` | 2026-03-07 |
| `darwin-x64` | `1.2.8` | 2025-03-31 |
| `darwin-arm` | `1.2.7` | 2025-03-30 |
| `win11-x64` | `1.2.8` | 2025-04-17 |
| `win11-arm` | `1.2.3` | 2025-02-23 |
| `linux-x64` | `1.2.10` | 2025-04-21 |
| `linux-arm` | `1.2.2` | 2025-02-16 |
BunJS on Windows on ARM uses the X64 compatibility layer.
@ -137,9 +137,42 @@ BunJS on Windows on ARM uses the X64 compatibility layer.
```bash
mkdir sheetjs-bun-dle
cd sheetjs-bun-dle
bun init -y
echo "{}" > package.json
```
:::caution PowerShell Encoding Errors
The PowerShell file redirect will use the `UTF-16 LE` encoding. Bun does not
support the encoding and will fail to install the package:
```
bun add v1.1.42 (50eec002)
1 | <20><>
^
error: Unexpected <20><>
at <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>:1:1
```
The file must be resaved in UTF8 (without BOM) or ASCII.
0) Open `package.json` in VSCodium.
The current encoding is displayed in the lower-right corner:
![VSCodium status bar](pathname:///files/encodium.png)
1) Click the displayed encoding.
2) In the "Select Action" popup, select "Save with Encoding"
3) In the new list, select `UTF-8 utf8`:
![VSCodium encoding](pathname:///files/vscutf8.png)
VSCodium will automatically re-save the file.
:::
1) Install the SheetJS package tarball:
<CodeBlock language="bash">{`\

@ -469,16 +469,14 @@ The result is an array of "simple" objects with no nesting:
## Create a Workbook
The [`json_to_sheet`](/docs/api/utilities/array#array-of-objects-input) method
can generate a SheetJS worksheet from the cleaned dataset:
With the cleaned dataset, `XLSX.utils.json_to_sheet`[^3] generates a worksheet:
```js
const worksheet = XLSX.utils.json_to_sheet(rows);
```
[`XLSX.utils.book_new`](/docs/api/utilities/wb) creates a new workbook and
[`XLSX.utils.book_append_sheet`](/docs/api/utilities/wb) appends a worksheet to
the workbook. The new worksheet will be called "Dates":
`XLSX.utils.book_new`[^4] creates a new workbook and `XLSX.utils.book_append_sheet`[^5]
appends a worksheet to the workbook. The new worksheet will be called "Dates":
```js
const workbook = XLSX.utils.book_new();
@ -505,12 +503,10 @@ cell styling and frozen rows.
<summary><b>Changing Header Names</b> (click to show)</summary>
By default, `json_to_sheet` creates a worksheet with a header row. In this case,
the headers come from the JS object keys: "name" and "birthday". The headers are
written to the first row.
the headers come from the JS object keys: "name" and "birthday".
[`XLSX.utils.sheet_add_aoa`](/docs/api/utilities/array#array-of-arrays-input)
can overwrite data in the worksheet. The following line will set `A1` to "Name"
and set `B1` to "Birthday":
The headers are in cells `A1` and `B1`. `XLSX.utils.sheet_add_aoa`[^6] can write
text values to the existing worksheet starting at cell `A1`:
```js
XLSX.utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
@ -522,7 +518,7 @@ XLSX.utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
<summary><b>Changing Column Widths</b> (click to show)</summary>
Some of the names are longer than the default column width. Column widths are
stored in the [`"!cols"` worksheet property](/docs/csf/features/colprops).
set by setting the `"!cols"` worksheet property.[^7]
The following line sets the width of column A to approximately 10 characters:
@ -545,9 +541,9 @@ After cleanup, the generated workbook looks like the screenshot below:
## Export a File
[`XLSX.writeFile`](/docs/api/write-options) creates a spreadsheet file and tries
to write it to the system. In the browser, it will try to prompt the user to
download the file. In NodeJS, it will write to the local directory.
`XLSX.writeFile`[^8] creates a spreadsheet file and tries to write it to the
system. In the browser, it will try to prompt the user to download the file. In
NodeJS, it will write to the local directory.
```js
XLSX.writeFile(workbook, "Presidents.xlsx", { compression: true });
@ -1169,8 +1165,14 @@ see a preview of the data. The Numbers app can open the file.
</TabItem>
</Tabs>
[^1]: `https://theunitedstates.io/congress-legislators/executive.json` is the
[^1]: https://theunitedstates.io/congress-legislators/executive.json is the
original location of the example dataset. The contributors to the dataset
dedicated the content to the public domain. When this demo was last tested,
dedicated the content to the public domain.
[^2]: See ["The Executive Branch"](https://github.com/unitedstates/congress-legislators#the-executive-branch)
in the dataset documentation.
[^3]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
[^4]: See [`book_new` in "Utilities"](/docs/api/utilities/wb)
[^5]: See [`book_append_sheet` in "Utilities"](/docs/api/utilities/wb)
[^6]: See [`sheet_add_aoa` in "Utilities"](/docs/api/utilities/array#array-of-arrays-input)
[^7]: See ["Column Properties"](/docs/csf/features/colprops)
[^8]: See [`writeFile` in "Writing Files"](/docs/api/write-options)

@ -148,15 +148,14 @@ The file data is stored in an `ArrayBuffer`.
## Parse File
With the file data in hand, [`XLSX.read`](/docs/api/parse-options) parses the
file and generates a SheetJS workbook object:
With the file data in hand, `XLSX.read`[^2] parses the workbook:
```js
const workbook = XLSX.read(file);
```
The `workbook` object follows the ["Common Spreadsheet Format"](/docs/csf/), an
in-memory schema for workbooks, worksheets, cells, and spreadsheet features.
The `workbook` object follows the "Common Spreadsheet Format"[^3], an in-memory
format for representing workbooks, worksheets, cells, and spreadsheet features.
## Explore Dataset
@ -171,8 +170,8 @@ To determine how to process the data, it is best to inspect the file first.
### List Sheet Names
As explained in the ["Workbook Object"](/docs/csf/book) page, the `SheetNames`
property is a list of the sheet names in the workbook.
As explained in the "Workbook Object"[^4] section, the `SheetNames` property is
a ordered list of the sheet names in the workbook.
The following live code block displays an ordered list of the sheet names:
@ -196,25 +195,21 @@ function SheetJSheetNames() {
### Inspect Worksheet Data
The [`Sheets` property of the workbook object](/docs/csf/book) is an object
whose keys are sheet names and whose values are sheet objects.
For example, the first worksheet can be pulled by indexing `SheetNames` and
using the name to index `Sheets`:
The `Sheets` property of the workbook object[^5] is an object whose keys are
sheet names and whose values are sheet objects. For example, the first worksheet
is pulled by indexing `SheetNames` and using the name to index `Sheets`:
```js
var first_sheet = workbook.Sheets[workbook.SheetNames[0]];
```
The [worksheet object](/docs/csf/sheet) can be inspected directly, but it is
strongly recommended to use utility functions to extract relevant data.
The actual worksheet object can be inspected directly[^6], but it is strongly
recommended to use utility functions to present JS-friendly data structures.
### Preview HTML
The [`sheet_to_html` function](/docs/api/utilities/html#html-table-output)
generates an HTML table from worksheet objects.
The following live example shows the first 20 rows of data in a table:
The `sheet_to_html` utility function[^7] generates an HTML table from worksheet
objects. The following live example shows the first 20 rows of data in a table:
<details>
<summary><b>Live example</b> (click to show)</summary>
@ -259,8 +254,7 @@ The key points from looking at the table are:
### Extract Raw Data
The [`sheet_to_json` function](/docs/api/utilities/array#array-output) generates
arrays of data from worksheet objects.
`XLSX.utils.sheet_to_json`[^8] generates arrays of data from worksheet objects.
For a complex layout like this, it is easiest to generate an "array of arrays"
where each row is an array of cell values. The screenshot shows rows 5-8:
@ -283,7 +277,7 @@ Row 7 includes the data for FY2007:
```
`XLSX.utils.sheet_to_json` will generate an array of arrays if the option
[`header: 1`](/docs/api/utilities/array#array-output) is specified:
`header: 1` is specified[^9]:
```js
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
@ -338,9 +332,7 @@ function SheetJSAoAHoles() {
</details>
The [worksheet `!merges` property](/docs/csf/features/merges) includes every
merge range in the sheet.
The worksheet `!merges` property[^10] includes every merge range in the sheet.
It is possible to loop through every merge block and fill cells, but in this
case it is easier to post-process the raw data:
@ -547,7 +539,7 @@ Looking at the headers:
![Rows 5-8](pathname:///sl.png)
The desired data is in column `I`. The column index can be calculated using
the [`decode_col` utility function](/docs/csf/general#column-names).
`XLSX.utils.decode_col`[^11].
<details>
<summary><b>Column Index calculation</b> (click to show)</summary>
@ -625,7 +617,7 @@ At this point, `objects` is an array of objects.
### ReactJS
The live demos in this example use ReactJS. In ReactJS, arrays of objects are
best presented in [simple tables](/docs/demos/frontend/react#array-of-objects):
best presented in simple HTML tables[^12]:
```jsx
<table>
@ -1061,7 +1053,7 @@ Press `a` to run on Android.
:::info Device Testing
The demo also runs on real Android devices! After enabling USB debugging[^2],
The demo also runs on real Android devices! After enabling USB debugging[^13],
the Android device can be connected to the computer with a USB cable.
:::
@ -1096,4 +1088,15 @@ When the app is loaded, the data will be displayed in rows.
</Tabs>
[^1]: The dataset URL has changed many times over the years. The current location for the CC0-licensed dataset can be found by [searching for "National Student Loan Data System" on `data.gov`](https://catalog.data.gov/dataset/?q=national+student+loan+data+system&publisher=Office+of+Federal+Student+Aid+%28FSA%29&organization=ed-gov). `PortfolioSummary.xls` is the file name within the dataset.
[^2]: See ["Running on Device"](https://reactnative.dev/docs/running-on-device) in the React Native documentation for more details.
[^2]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^3]: See ["SheetJS Data Model"](/docs/csf/)
[^4]: See ["Workbook Object"](/docs/csf/book)
[^5]: See ["Workbook Object"](/docs/csf/book)
[^6]: See ["Sheet Objects"](/docs/csf/sheet)
[^7]: See [`sheet_to_html` in "Utilities"](/docs/api/utilities/html#html-table-output)
[^8]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
[^9]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
[^10]: See ["Merged Cells" in "SheetJS Data Model"](/docs/csf/features/merges)
[^11]: See ["Column Names" in "Addresses and Ranges"](/docs/csf/general#column-names)
[^12]: See ["Array of Objects" in "ReactJS"](/docs/demos/frontend/react#array-of-objects)
[^13]: See ["Running on Device"](https://reactnative.dev/docs/running-on-device) in the React Native documentation for more details.

@ -35,21 +35,17 @@ This demo was tested in the following configurations:
| Platform | Architecture | Date |
|:------------------------------------------------------------------|:-------------|:-----------|
| NVIDIA RTX PRO 6000 (96 GB VRAM) + Ryzen Z2 Go (32 GB RAM) | `win11-x64` | 2025-11-15 |
| NVIDIA RTX PRO 6000 (96 GB VRAM) + Ryzen Z2 Go (32 GB RAM) | `linux-x64` | 2025-11-15 |
| NVIDIA RTX 5090 (32 GB VRAM) + Ryzen Z2 (32 GB RAM) | `win11-x64` | 2026-01-25 |
| NVIDIA RTX 5090 (32 GB VRAM) + Ryzen Z2 Go (32 GB RAM) | `linux-x64` | 2025-11-15 |
| AMD AI PRO R9700 (32 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `win11-x64` | 2026-01-17 |
| AMD AI PRO R9700 (32 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `linux-x64` | 2026-01-17 |
| AMD RX 9070 XT (16 GB VRAM) + Ryzen Z2 Go (32 GB RAM) | `win11-x64` | 2026-01-17 |
| AMD RX 9070 XT (16 GB VRAM) + Ryzen Z2 Go (32 GB RAM) | `linux-x64` | 2026-01-17 |
| AMD RX 7900 XTX (24 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `win11-x64` | 2025-11-15 |
| AMD RX 7900 XTX (24 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `linux-x64` | 2025-11-15 |
| Intel Arc B580 (12 GB VRAM) + Ryzen Z1 Extreme (24 GB RAM) | `win11-x64` | 2025-11-15 |
| Intel Arc B580 (12 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `linux-x64` | 2025-11-15 |
| AMD RYZEN AI MAX+ 395 + Radeon 8060S (128 GB unified memory) | `linux-x64` | 2025-11-15 |
| AMD RYZEN AI MAX+ 395 + Radeon 8060S (128 GB unified memory) | `win11-x64` | 2025-11-15 |
| Apple M4 Max 16-Core CPU + 40-Core GPU (48 GB unified memory) | `darwin-arm` | 2025-11-15 |
| NVIDIA RTX 5090 (32 GB VRAM) + Ryzen Z1 Extreme (24 GB RAM) | `win11-x64` | 2025-06-17 |
| NVIDIA RTX 5090 (32 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `linux-x64` | 2025-06-20 |
| NVIDIA RTX 4090 (24 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `win11-x64` | 2025-04-17 |
| NVIDIA RTX 4090 (24 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `linux-x64` | 2025-06-20 |
| AMD RX 7900 XTX (24 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `win11-x64` | 2025-06-20 |
| AMD RX 7900 XTX (24 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `linux-x64` | 2025-06-21 |
| Intel Arc B580 (12 GB VRAM) + Ryzen Z1 Extreme (24 GB RAM) | `win11-x64` | 2025-06-20 |
| Intel Arc B580 (12 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `linux-x64` | 2025-06-21 |
| Apple M4 Max 16-Core CPU + 40-Core GPU (48 GB unified memory) | `darwin-arm` | 2025-03-06 |
| Apple M3 Ultra 28-Core CPU + 60-Core GPU (96 GB unified memory) | `darwin-arm` | 2025-06-24 |
| Apple M2 Max 12-Core CPU + 30-Core GPU (32 GB unified memory) | `darwin-arm` | 2025-03-25 |
SheetJS users have verified this demo in other configurations:
@ -59,8 +55,6 @@ SheetJS users have verified this demo in other configurations:
| Platform | Architecture | Demo |
|:---------------------------------------------------------------------|:-------------|:------------|
| NVIDIA L40 (48 GB VRAM) + i9-13900K (32 GB RAM) | `linux-x64` | LangChainJS |
| NVIDIA RTX 4090 (24 GB VRAM) + Ryzen Z2 Go (32 GB RAM) | `win11-x64` | LangChainJS |
| NVIDIA RTX 4090 (24 GB VRAM) + Ryzen Z2 Go (32 GB RAM) | `linux-x64` | LangChainJS |
| NVIDIA RTX 4080 SUPER (16 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `win11-x64` | LangChainJS |
| NVIDIA RTX 4080 SUPER (16 GB VRAM) + Ryzen Z2 Go (32 GB RAM) | `linux-x64` | LangChainJS |
| NVIDIA RTX 4070 Ti SUPER (16 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `win11-x64` | LangChainJS |
@ -77,8 +71,6 @@ SheetJS users have verified this demo in other configurations:
| NVIDIA GTX 1070 (8 GB VRAM) + Ryzen 7 7700x (32 GB RAM) | `win11-x64` | LangChainJS |
| AMD RX 6800 XT (16 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `win11-x64` | LangChainJS |
| Apple M4 10-Core CPU + 10-Core GPU (24 GB unified memory) | `darwin-arm` | LangChainJS |
| Apple M3 Ultra 28-Core CPU + 60-Core GPU (96 GB unified memory) | `darwin-arm` | LangChainJS |
| Apple M2 Max 12-Core CPU + 30-Core GPU (32 GB unified memory) | `darwin-arm` | LangChainJS |
</details>
@ -390,7 +382,7 @@ includes one header row and a number of data rows.
```js title="loadofsheet.mjs"
import { Document } from "@langchain/core/documents";
import { BufferLoader } from "@langchain/classic/document_loaders/fs/buffer";
import { BufferLoader } from "langchain/document_loaders/fs/buffer";
import { read, utils } from "xlsx";
/**
@ -529,7 +521,7 @@ export class CSVLoader extends TextLoader {
The SheetJS `read` method supports NodeJS Buffer objects directly[^6]:
```js title="Parsing a workbook in a BufferLoader"
import { BufferLoader } from "@langchain/classic/document_loaders/fs/buffer";
import { BufferLoader } from "langchain/document_loaders/fs/buffer";
import { read, utils } from "xlsx";
export default class LoadOfSheet extends BufferLoader {
@ -840,62 +832,6 @@ D) Run the `start-ollama.sh` script from the extracted folder.
:::
:::caution pass
AMD GPUs may require special ROCm / HIP libraries.
<details>
<summary><b>AMD Instructions on Windows</b> (click to show)</summary>
[`ollama-for-amd`](https://github.com/likelovewant/ollama-for-amd) is a
community fork that provides support for many AMD GPUs.
[Installers](https://github.com/ByronLeeeee/Ollama-For-AMD-Installer/releases)
are also provided by the community.
There are separate drivers for each GPU family. The following GPUs were tested:
| Name | GPU | Identifier |
|:-----------------|:--------|:-----------|
| AMD AI PRO R9700 | Navi 48 | `gfx1200` |
| AMD RX 9070 XT | Navi 48 | `gfx1201` |
| AND RX 7900 XTX | Navi 31 | `gfx1100` |
When this demo was last tested, the installer claimed `gfx1200` corresponded to
Navi 48. `gfx1200` should be used for the PRO R9700, while `gfx1201` should be
used for the 9070 XT.
</details>
<details>
<summary><b>AMD Instructions on Linux</b> (click to show)</summary>
The official Ollama release is compatible with AMD cards. However, in some test
runs, Ollama will try to use the CPU. Some environment variables were required:
```bash
export OLLAMA_USE_ROCM=1
export OLLAMA_GPU_LAYERS=999
export OLLAMA_VULKAN=1
```
In addition, `HSA_OVERRIDE_GFX_VERSION` must be set to a specific version based
on the card, which may not match the official version:
| Name | Value | Command |
|:-----------------|:---------|:-------------------------------------------|
| AMD AI PRO R9700 | `12.0.0` | `export HSA_OVERRIDE_GFX_VERSION="12.0.0"` |
| AMD RX 9070 XT | `12.0.0` | `export HSA_OVERRIDE_GFX_VERSION="12.0.0"` |
| AND RX 7900 XTX | `11.0.0` | `export HSA_OVERRIDE_GFX_VERSION="11.0.0"` |
These environment variables may not be used by the Ollama service, so it is
strongly recommended to stop the default service and run `ollama serve` in a
separate terminal window.
</details>
:::
After installing dependencies, start a new terminal session.
1) Create a new project:
@ -942,7 +878,7 @@ npm i --save https://sheet.lol/balls/xlsx-${current}.tgz`}
4) Install dependencies:
```bash
npm i --save @langchain/core@1.1.15 langchain@1.2.10 @langchain/classic@1.0.9 @langchain/ollama@1.2.0 peggy@5.0.6
npm i --save @langchain/core@0.3.59 langchain@0.3.28 @langchain/ollama@0.2.2 peggy@3.0.2
```
:::note pass
@ -951,7 +887,7 @@ In some test runs, there were error messages relating to dependency and peer
dependency versions. The `--force` flag will suppress version mismatch errors:
```bash
npm i --save @langchain/core@1.1.15 langchain@1.2.10 @langchain/classic@1.0.9 @langchain/ollama@1.2.0 peggy@5.0.6 --force
npm i --save @langchain/core@0.3.59 langchain@0.3.28 @langchain/ollama@0.2.2 peggy@3.0.2 --force
```
:::
@ -985,7 +921,7 @@ ollama pull phi4:14b
```
<details>
<summary><b>Additional steps for Intel GPUs and AMD Strix Halo</b> (click to show)</summary>
<summary><b>Additional steps for Intel GPUs</b> (click to show)</summary>
A different embedding model must be used on Intel GPUs:

@ -44,15 +44,14 @@ The NodeJS demo was tested in the following environments:
| NodeJS | TF.js | Date |
|:------------|:----------|:-----------|
| `24.13.0` | `4.22.0` | 2026-01-28 |
| `22.22.0` | `4.22.0` | 2026-01-28 |
| `20.20.0` | `4.22.0` | 2026-01-28 |
| `22.14.0` | `4.22.0` | 2025-04-21 |
| `20.18.0` | `4.22.0` | 2025-04-21 |
The Kaioken demo was tested in the following environments:
| Kaioken | TF.js | Date |
|:------------|:----------|:-----------|
| `0.44.3` | `4.22.0` | 2025-11-15 |
| `0.37.0` | `4.22.0` | 2025-04-21 |
:::
@ -418,7 +417,7 @@ The SheetJS team strongly recommends using Kaioken in projects using TF.js.
1) Create a new site.
```bash
npm create vite sheetjs-tfjs-kaioken -- --template vanilla-ts --no-rolldown --no-interactive
npm create vite sheetjs-tfjs-kaioken -- --template vanilla-ts
cd sheetjs-tfjs-kaioken
npm add --save kaioken
npm add --save vite-plugin-kaioken -D
@ -444,14 +443,7 @@ export default defineConfig({
"jsx": "preserve",
```
4) Edit `tsconfig.json` and remove any lines referencing `verbatimModuleSyntax`.
When the demo was last tested, the file had the following line:
```js title="tsconfig.json (remove this line if present)"
"verbatimModuleSyntax": true,
```
5) Replace `src/main.ts` with the following codeblock:
4) Replace `src/main.ts` with the following codeblock:
```js title="src/main.ts"
import { mount } from "kaioken";
@ -461,19 +453,19 @@ const root = document.getElementById("app");
mount(App, root!);
```
6) Download [`SheetJSTF.tsx`](pathname:///tfjs/SheetJSTF.tsx) to the `src` directory:
5) Download [`SheetJSTF.tsx`](pathname:///tfjs/SheetJSTF.tsx) to the `src` directory:
```bash
curl -L -o src/SheetJSTF.tsx https://docs.sheetjs.com/tfjs/SheetJSTF.tsx
```
7) Install SheetJS and TF.js dependencies:
6) Install SheetJS and TF.js dependencies:
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs`}
</CodeBlock>
8) Start the development server:
7) Start the development server:
```bash
npm run dev

@ -40,12 +40,12 @@ This demo was tested in the following deployments:
| Architecture | JS Engine | Pandas | Python | Date |
|:-------------|:----------------|:-------|:-------|:-----------|
| `darwin-x64` | Duktape `2.7.0` | 3.0.1 | 3.13.7 | 2026-03-07 |
| `darwin-arm` | Duktape `2.7.0` | 2.3.3 | 3.9.6 | 2026-01-25 |
| `win11-x64` | Duktape `2.7.0` | 3.0.1 | 3.11.9 | 2026-03-08 |
| `win11-arm` | Duktape `2.7.0` | 2.2.3 | 3.11.5 | 2026-03-07 |
| `linux-x64` | Duktape `2.7.0` | 3.0.1 | 3.13.9 | 2026-03-07 |
| `linux-arm` | Duktape `2.7.0` | 2.2.3 | 3.13.5 | 2026-03-07 |
| `darwin-x64` | Duktape `2.7.0` | 2.2.3 | 3.13.1 | 2025-03-31 |
| `darwin-arm` | Duktape `2.7.0` | 2.2.3 | 3.13.2 | 2025-03-30 |
| `win11-x64` | Duktape `2.7.0` | 2.2.3 | 3.11.9 | 2025-04-28 |
| `win11-arm` | Duktape `2.7.0` | 2.2.3 | 3.13.2 | 2025-02-23 |
| `linux-x64` | Duktape `2.7.0` | 2.1.4 | 3.12.3 | 2025-06-16 |
| `linux-arm` | Duktape `2.7.0` | 1.5.3 | 3.11.2 | 2025-02-16 |
:::
@ -542,14 +542,14 @@ The Pandas example requires a few slight changes to work with Polars:
This demo was tested in the following deployments:
| Architecture | JS Engine | Polars | Python | Date |
|:-------------|:----------------|:-------|:-------|:-----------|
| `darwin-x64` | Duktape `2.7.0` | 1.38.1 | 3.13.7 | 2026-03-07 |
| `darwin-arm` | Duktape `2.7.0` | 1.36.1 | 3.9.6 | 2026-01-25 |
| `win11-x64` | Duktape `2.7.0` | 1.38.1 | 3.11.9 | 2026-03-08 |
| `win11-arm` | Duktape `2.7.0` | 1.23.0 | 3.11.5 | 2026-03-07 |
| `linux-x64` | Duktape `2.7.0` | 1.38.1 | 3.13.9 | 2026-03-07 |
| `linux-arm` | Duktape `2.7.0` | 1.38.1 | 3.13.5 | 2026-03-07 |
| Architecture | JS Engine | Polars | Python | Date |
|:-------------|:----------------|:--------|:-------|:-----------|
| `darwin-x64` | Duktape `2.7.0` | 1.26.0 | 3.13.1 | 2025-03-31 |
| `darwin-arm` | Duktape `2.7.0` | 1.26.0 | 3.13.2 | 2025-03-30 |
| `win11-x64` | Duktape `2.7.0` | 1.28.1 | 3.11.9 | 2025-04-28 |
| `win11-arm` | Duktape `2.7.0` | 1.23.0 | 3.13.2 | 2025-02-23 |
| `linux-x64` | Duktape `2.7.0` | 1.30.0 | 3.12.3 | 2025-06-16 |
| `linux-arm` | Duktape `2.7.0` | 1.22.0 | 3.11.2 | 2025-02-16 |
:::

@ -360,9 +360,9 @@ export default function SheetJSKaiokenAoO() {
{ /* error message */
!pres && !loading && ( <tr><td colSpan="2">{error.message}</td></tr> )
}
</tbody><tfoot><tr><td colSpan={2}>
</tbody><tfoot><td colSpan={2}>
<button onclick={exportFile}>Export XLSX</button>
</td></tr></tfoot></table>);
</td></tfoot></table>);
}
```
@ -429,9 +429,9 @@ export default function SheetJSKaiokenAoO() {
<td>{pres.Index}</td>
</tr>))
}
</tbody><tfoot><tr><td colSpan={2}>
</tbody><tfoot><td colSpan={2}>
<button onclick={exportFile}>Export XLSX</button>
</td></tr></tfoot></table>);
</td></tfoot></table>);
}
```
@ -450,14 +450,14 @@ This demo was tested in the following environments:
| Kaioken | ViteJS | Date |
|:----------|:--------|:-----------|
| `0.44.3` | `6.4.1` | 2026-03-14 |
| `0.35.10` | `6.1.0` | 2025-02-11 |
:::
1) Create a new site.
```bash
npm create vite@latest sheetjs-kaioken -- --template vanilla-ts --no-rolldown --no-interactive
npm create vite@latest sheetjs-kaioken -- --template vanilla-ts
cd sheetjs-kaioken
npm add --save kaioken
npm add --save vite-plugin-kaioken -D
@ -597,14 +597,14 @@ This demo was tested in the following environments:
| Kaioken | ViteJS | Date |
|:----------|:--------|:-----------|
| `0.44.3` | `6.4.1` | 2026-03-14 |
| `0.35.10` | `6.1.0` | 2025-02-11 |
:::
1) Create a new site.
```bash
npm create vite@latest sheetjs-kaioken -- --template vanilla-ts --no-rolldown --no-interactive
npm create vite@latest sheetjs-kaioken -- --template vanilla-ts
cd sheetjs-kaioken
npm add --save kaioken
npm add --save vite-plugin-kaioken -D

@ -341,7 +341,7 @@ This demo was tested in the following environments:
1) Create a new site:
```bash
npm create vite@latest sheetjs-react -- --template react --no-rolldown --no-interactive
npm create vite@latest sheetjs-react -- --template react
```
2) Install the SheetJS dependency and start the dev server:
@ -845,7 +845,7 @@ This demo was tested in the following environments:
1) Create a new site:
```bash
npm create vite@latest sheetjs-react -- --template react --no-rolldown --no-interactive
npm create vite@latest sheetjs-react -- --template react
```
2) Install the SheetJS dependency and start the dev server:

@ -425,11 +425,11 @@ This demo was tested in the following environments:
| Angular | Date |
|:----------|:-----------|
| `20.3.1` | 2026-03-12 |
| `19.2.17` | 2026-03-12 |
| `18.2.14` | 2026-03-12 |
| `17.3.12` | 2026-03-12 |
| `16.2.16` | 2026-03-12 |
| `20.2.3` | 2025-09-03 |
| `19.0.5` | 2025-01-03 |
| `18.2.13` | 2025-01-03 |
| `17.3.12` | 2025-01-03 |
| `16.2.12` | 2025-01-03 |
:::
@ -634,11 +634,10 @@ This demo was tested in the following environments:
| Angular | Date |
|:----------|:-----------|
| `20.3.1` | 2026-03-12 |
| `19.2.17` | 2026-03-12 |
| `18.2.14` | 2026-03-12 |
| `17.3.12` | 2026-03-12 |
| `16.2.16` | 2026-03-12 |
| `19.0.5` | 2025-01-03 |
| `18.2.13` | 2025-01-03 |
| `17.3.12` | 2025-01-03 |
| `16.2.12` | 2025-01-03 |
:::
@ -651,7 +650,7 @@ npx @angular/cli analytics disable -g
1) Create a new project:
```bash
npx @angular/cli@20 new --minimal --defaults --no-interactive sheetjs-angular
npx @angular/cli@19 new --minimal --defaults --no-interactive sheetjs-angular
```
:::note pass

@ -59,15 +59,14 @@ depends on the application.
### Array of Objects
Typically, users will create a spreadsheet with data that should be imported to
the site. The data sheets will typically store headers in the first row. In some
applications, the data will follow a standardized template.
Typically, some users will create a spreadsheet with source data that should be
loaded into the site. This sheet will have known columns.
#### State
The example [presidents sheet](https://docs.sheetjs.com/pres.xlsx) has one
header row with "Name" and "Index" columns. The natural JS representation is an
object for each data row, where the values in the first row are used as keys:
object for each row, where the keys are specified in the first row:
<table>
<thead><tr><th>Spreadsheet</th><th>State</th></tr></thead>
@ -115,8 +114,7 @@ const pres = ref<any[]>([]);
</script>
```
`ref` is a generic method in TypeScript. If the spreadsheet header row is known
ahead of time, each row object can be typed:
When the spreadsheet header row is known ahead of time, row typing is possible:
```html
<script setup lang="ts">
@ -463,19 +461,18 @@ and test the page.
### HTML
The main disadvantage of the "Array of Objects" approach is the specific nature
The main disadvantage of the Array of Objects approach is the specific nature
of the columns. For more general use, passing around an Array of Arrays works.
However, this does not handle merged cells[^5] well!
However, this does not handle merge cells[^5] well!
The [`sheet_to_html`](/docs/api/utilities/html#html-table-output) function
generates HTML that is aware of merges and other worksheet features. VueJS
`v-html`[^6] attribute allows code to set the `innerHTML` attribute, effectively
inserting the code into the page.
In this example, the component attaches a `ref` to the `DIV` container. When the
"Export XLSX" button is clicked, the first `TABLE` child element will be parsed
with the [`table_to_book`](/docs/api/utilities/html#html-table-input) method.
The generated workbook object will be exported to XLSX using `writeFile`.
In this example, the component attaches a `ref` to the `DIV` container. During
export, the first `TABLE` child element can be parsed with [`table_to_book`](/docs/api/utilities/html#html-table-input) to
generate a workbook object.
```html title="src/SheetJSVueHTML.vue"
<script setup>

@ -143,14 +143,14 @@ This demo was tested in the following environments:
| SvelteJS | ViteJS | Date |
|:---------|:---------|:-----------|
| `5.43.7` | `7.2.2` | 2025-11-15 |
| `5.38.6` | `7.1.1` | 2025-09-03 |
:::
1) Create a new project:
```bash
npm create vite@latest sheetjs-svelte -- --template svelte-ts --no-rolldown --no-interactive
npm create vite@latest sheetjs-svelte -- --template svelte-ts
```
2) Install the SheetJS dependency and start the dev server:
@ -245,14 +245,14 @@ This demo was tested in the following environments:
| SvelteJS | ViteJS | Date |
|:---------|:---------|:-----------|
| `5.43.7` | `7.2.2` | 2025-11-15 |
| `5.25.3` | `6.2.3` | 2025-03-30 |
:::
1) Create a new project:
```bash
npm create vite@latest sheetjs-svelte -- --template svelte-ts --no-rolldown --no-interactive
npm create vite@latest sheetjs-svelte -- --template svelte-ts
```
2) Install the SheetJS dependency and start the dev server:

@ -295,8 +295,8 @@ This demo was tested in the following deployments:
| Architecture | Date |
|:-------------|:-----------|
| `darwin-x64` | 2026-03-07 |
| `darwin-arm` | 2026-03-07 |
| `darwin-x64` | 2025-04-17 |
| `darwin-arm` | 2025-04-24 |
| `win11-x64` | 2025-04-17 |
| `win11-arm` | 2025-04-24 |

@ -365,7 +365,7 @@ This demo was tested in the following environments:
| OpenUI5 | Date |
|:----------|------------|
| `1.145.0` | 2026-03-13 |
| `1.132.1` | 2025-01-24 |
:::
@ -535,7 +535,7 @@ This demo was tested in the following environments:
| OpenUI5 | Date |
|:----------|------------|
| `1.145.0` | 2026-03-13 |
| `1.132.1` | 2025-01-24 |
:::

@ -211,8 +211,8 @@ This demo was tested in the following environments:
| KnockoutJS | Date | Live Demo |
|:-----------|:-----------|:-----------------------------------------------|
| `3.5.0` | 2026-03-06 | [**KO3**](pathname:///knockout/knockout3.html) |
| `2.3.0` | 2026-03-06 | [**KO2**](pathname:///knockout/knockout2.html) |
| `3.5.0` | 2025-01-08 | [**KO3**](pathname:///knockout/knockout3.html) |
| `2.3.0` | 2025-01-08 | [**KO2**](pathname:///knockout/knockout2.html) |
:::

@ -41,11 +41,10 @@ This demo was tested in the following environments:
| ViteJS | Date |
|:---------|:-----------|
| `7.3.1` | 2026-01-28 |
| `6.4.1` | 2026-01-28 |
| `5.4.21` | 2026-01-28 |
| `4.5.14` | 2026-01-28 |
| `3.2.11` | 2026-01-28 |
| `6.2.3` | 2025-03-30 |
| `5.4.15` | 2025-03-30 |
| `4.5.10` | 2025-03-30 |
| `3.2.11` | 2025-03-30 |
:::
@ -75,7 +74,7 @@ It is strongly recommended to [upgrade to the latest version](/docs/getting-star
1) Create a new ViteJS project:
```bash
npm create vite@latest sheetjs-vite -- --template vue-ts --no-rolldown --no-interactive
npm create vite@latest sheetjs-vite -- --template vue-ts
cd sheetjs-vite
npm i
```

@ -47,25 +47,23 @@ This demo was tested in the following environments:
| ESBuild | Date |
|:----------|:-----------|
| `0.27.2` | 2026-01-28 |
| `0.26.0` | 2026-01-28 |
| `0.25.12` | 2026-01-28 |
| `0.24.2` | 2026-01-28 |
| `0.23.1` | 2026-01-28 |
| `0.22.0` | 2026-01-28 |
| `0.21.5` | 2026-01-28 |
| `0.20.2` | 2026-01-28 |
| `0.19.12` | 2026-01-28 |
| `0.18.20` | 2026-01-28 |
| `0.17.19` | 2026-01-28 |
| `0.16.17` | 2026-01-28 |
| `0.15.18` | 2026-01-28 |
| `0.14.54` | 2026-01-28 |
| `0.13.15` | 2026-01-28 |
| `0.12.29` | 2026-01-28 |
| `0.11.23` | 2026-01-28 |
| `0.10.2` | 2026-01-28 |
| `0.9.7` | 2026-01-28 |
| `0.25.5` | 2025-06-02 |
| `0.24.2` | 2025-06-02 |
| `0.23.1` | 2025-06-02 |
| `0.22.0` | 2025-06-02 |
| `0.21.5` | 2025-06-02 |
| `0.20.2` | 2025-06-02 |
| `0.19.12` | 2025-06-02 |
| `0.18.20` | 2025-06-02 |
| `0.17.19` | 2025-06-02 |
| `0.16.17` | 2025-06-02 |
| `0.15.18` | 2025-06-02 |
| `0.14.54` | 2025-06-02 |
| `0.13.15` | 2025-06-02 |
| `0.12.29` | 2025-06-02 |
| `0.11.23` | 2025-06-02 |
| `0.10.2` | 2025-06-02 |
| `0.9.7` | 2025-06-02 |
:::
@ -93,7 +91,7 @@ Assuming the primary source file is `in.js`, the following command will bundle
the script and generate `out.js`:
```bash
npx -y esbuild@0.25.9 in.js --bundle --outfile=out.js
npx -y esbuild@0.25.5 in.js --bundle --outfile=out.js
```
### Browser Demo
@ -142,7 +140,7 @@ curl -LO https://docs.sheetjs.com/esbuild/esbrowser.js
4) Create bundle:
```bash
npx -y esbuild@0.25.9 esbrowser.js --bundle --outfile=esb.browser.js
npx -y esbuild@0.25.5 esbrowser.js --bundle --outfile=esb.browser.js
```
5) Start a local HTTP server:
@ -182,7 +180,7 @@ Assuming the primary source file is `in.js`, the following command will bundle
the script for NodeJS and generate `out.js`:
```bash
npx -y esbuild@0.25.9 in.js --bundle --platform=node --outfile=out.js
npx -y esbuild@0.25.5 in.js --bundle --platform=node --outfile=out.js
```
### NodeJS Demo
@ -231,7 +229,7 @@ curl -LO https://docs.sheetjs.com/esbuild/esbnode.js
3) Create bundle:
```bash
npx -y esbuild@0.25.9 esbnode.js --bundle --platform=node --outfile=esb.node.js
npx -y esbuild@0.25.5 esbnode.js --bundle --platform=node --outfile=esb.node.js
```
4) Run the bundle:

@ -39,12 +39,12 @@ which covers SheetJS library usage in more detail.
This demo was tested in the following environments:
| Version | Date | Required Workarounds |
|:----------|:-----------|:------------------------------------|
| `5.104.1` | 2026-01-28 | |
| `4.47.0` | 2026-01-28 | |
| `3.12.0` | 2026-01-28 | Import `xlsx/dist/xlsx.full.min.js` |
| `2.7.0` | 2026-01-28 | Import `xlsx/dist/xlsx.full.min.js` |
| Version | Date | Required Workarounds |
|:---------|:-----------|:------------------------------------|
| `5.97.1` | 2025-01-03 | |
| `4.47.0` | 2025-01-03 | |
| `3.12.0` | 2025-01-03 | Import `xlsx/dist/xlsx.full.min.js` |
| `2.7.0` | 2025-01-03 | Import `xlsx/dist/xlsx.full.min.js` |
:::
@ -329,6 +329,8 @@ npx -y http-server .
Click on "Click here to export" to generate a file.
## Miscellany
[^1]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^2]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
[^3]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)

@ -34,21 +34,21 @@ This demo was tested in the following environments:
| Browserify | Date |
|:-----------|:-----------|
| `17.0.1` | 2026-01-28 |
| `16.5.2` | 2026-01-28 |
| `15.2.0` | 2026-01-28 |
| `14.5.0` | 2026-01-28 |
| `13.3.0` | 2026-01-28 |
| `12.0.2` | 2026-01-28 |
| `11.2.0` | 2026-01-28 |
| `10.2.6` | 2026-01-28 |
| `9.0.8` | 2026-01-28 |
| `8.1.3` | 2026-01-28 |
| `7.1.0` | 2026-01-28 |
| `6.3.4` | 2026-01-28 |
| `5.13.1` | 2026-01-28 |
| `4.2.3` | 2026-01-28 |
| `3.46.1` | 2026-01-28 |
| `17.0.1` | 2025-06-18 |
| `16.5.2` | 2025-06-18 |
| `15.2.0` | 2025-06-18 |
| `14.5.0` | 2025-06-18 |
| `13.3.0` | 2025-06-18 |
| `12.0.2` | 2025-06-18 |
| `11.2.0` | 2025-06-18 |
| `10.2.6` | 2025-06-18 |
| `9.0.8` | 2025-06-18 |
| `8.1.3` | 2025-06-18 |
| `7.1.0` | 2025-06-18 |
| `6.3.4` | 2025-06-18 |
| `5.13.1` | 2025-06-18 |
| `4.2.3` | 2025-06-18 |
| `3.46.1` | 2025-06-18 |
:::

@ -40,8 +40,8 @@ This demo was tested in the following environments:
| RequireJS | Date |
|:----------|:-----------|
| `2.3.7` | 2026-01-25 |
| `2.1.22` | 2026-01-25 |
| `2.3.7` | 2025-01-07 |
| `2.1.22` | 2025-01-07 |
:::

@ -46,11 +46,11 @@ This demo was tested in the following environments:
| Version | Platform | Date |
|:----------|:---------|:-----------|
| `6.15.1` | NodeJS | 2026-01-28 |
| `0.21.6` | NodeJS | 2026-01-28 |
| `0.20.19` | NodeJS | 2026-01-28 |
| `0.19.47` | NodeJS | 2026-01-28 |
| `0.20.16` | Browser | 2026-01-28 |
| `0.19.47` | NodeJS | 2025-01-03 |
| `0.20.16` | Browser | 2025-01-03 |
| `0.20.19` | NodeJS | 2025-03-03 |
| `0.21.6` | NodeJS | 2025-03-03 |
| `6.15.1` | NodeJS | 2025-01-03 |
:::

@ -34,10 +34,10 @@ This demo was tested in the following environments:
| Version | Date |
|:---------|:-----------|
| `4.57.0` | 2026-01-28 |
| `3.29.5` | 2026-01-28 |
| `2.79.2` | 2026-01-28 |
| `1.32.1` | 2026-01-28 |
| `4.29.1` | 2025-01-03 |
| `3.29.5` | 2025-01-03 |
| `2.79.1` | 2025-01-03 |
| `1.32.1` | 2025-01-03 |
:::

@ -1,195 +0,0 @@
---
title: Packing Sheets with Rspack
sidebar_label: Rspack
pagination_prev: demos/index
pagination_next: demos/grid/index
sidebar_position: 18
---
import current from '/version.js';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
[Rspack](https://rspack.rs/) is a fast module bundler for JavaScript.
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
This demo uses Rspack and SheetJS to export data. We'll explore how to add
SheetJS to a site using Rspack and how to export data to spreadsheets.
:::note pass
This demo focuses on integration details with the Rspack bundler.
The demos follow the ["Export Tutorial"](/docs/getting-started/examples/export),
which covers SheetJS library usage in more detail.
:::
:::note Tested Deployments
This demo was tested in the following environments:
| Rspack | Date |
|:--------|:-----------|
| `1.7.4` | 2026-01-28 |
:::
## Integration Details
[The "Frameworks" section](/docs/getting-started/installation/frameworks) covers
installation with Yarn and other package managers.
After installing the SheetJS module in a Rspack project, `import` statements can
load relevant parts of the library.
Projects that import data will use methods such as `read`[^1] to parse workbooks
and `sheet_to_json`[^2] to generate usable data from files. As `sheet_to_json`
is part of the `utils` object, the required import is:
```js
import { read, utils } from 'xlsx';
```
Projects that export data will use methods such as `json_to_sheet`[^3] to
generate worksheets and `writeFile`[^4] to export files. As `json_to_sheet` is
part of the `utils` object, the required import is:
```js
import { utils, writeFile } from 'xlsx';
```
## Complete Example
This demo follows the [Export Example](/docs/getting-started/examples/export).
0) Initialize a new project:
```bash
mkdir sheetjs-rspack
cd sheetjs-rspack
npm init -y
```
1) Install the dependencies using a package manager:
<Tabs groupId="pm">
<TabItem value="npm" label="npm">
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @rspack/core @rspack/cli`}
</CodeBlock>
</TabItem>
<TabItem value="pnpm" label="pnpm">
<CodeBlock language="bash">{`\
pnpm install --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @rspack/core @rspack/cli`}
</CodeBlock>
</TabItem>
<TabItem value="yarn" label="Yarn" default>
<CodeBlock language="bash">{`\
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @rspack/core @rspack/cli`}
</CodeBlock>
</TabItem>
</Tabs>
2) Save the following to `src/index.js`:
```js title="src/index.js"
import { utils, writeFileXLSX } from 'xlsx';
document.getElementById("xport").addEventListener("click", async() => {
/* fetch JSON data and parse */
const url = "https://docs.sheetjs.com/executive.json";
const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */
const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));
/* sort by first presidential term */
prez.forEach(row => row.start = row.terms.find(term => term.type === "prez").start);
prez.sort((l,r) => l.start.localeCompare(r.start));
/* flatten objects */
const rows = prez.map(row => ({
name: row.name.first + " " + row.name.last,
birthday: row.bio.birthday
}));
/* generate worksheet and workbook */
const worksheet = utils.json_to_sheet(rows);
const workbook = utils.book_new();
utils.book_append_sheet(workbook, worksheet, "Dates");
/* fix headers */
utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
/* calculate column width */
const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
worksheet["!cols"] = [ { wch: max_width } ];
/* create an XLSX file and try to save to Presidents.xlsx */
writeFileXLSX(workbook, "Presidents.xlsx");
});
```
3) Create bundle:
```bash
npx -p @rspack/cli rspack build
```
:::caution pass
This build may fail with a module error:
```
ERROR in ./src/index.js
× Module parse failed:
╰─▶ × JavaScript parse error: 'import', and 'export' cannot be used outside of module code
```
Some versions of the `npm` tool create `package.json` files with the option
`"type": "commonjs"`. Rspack will fail if that option is specified. Edit
`package.json` and remove the property:
```js title="package.json (remove highlighted line)"
"license": "ISC",
// highlight-next-line
"type": "commonjs",
"dependencies": {
```
:::
This command will create the script `dist/main.js`
4) Create a small HTML page that loads the generated script:
```html title="index.html"
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
<h1>SheetJS Presidents Demo</h1>
<button id="xport">Click here to export</button>
<script src="dist/main.js"></script>
</body>
</html>
```
5) Start a local HTTP server:
```bash
npx -y http-server .
```
Access the displayed URL (typically `http://localhost:8080`) with a web browser.
Click the "Click here to export" button to generate `Presidents.xlsx`
[^1]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^2]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
[^3]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
[^4]: See [`writeFile` in "Writing Files"](/docs/api/write-options)

@ -34,8 +34,8 @@ This demo was tested in the following environments:
| Version | Date |
|:---------|:-----------|
| `2.16.1` | 2026-01-28 |
| `1.12.3` | 2026-01-28 |
| `2.14.4` | 2025-05-07 |
| `1.12.3` | 2025-05-07 |
:::
@ -44,7 +44,7 @@ This demo was tested in the following environments:
[The "Frameworks" section](/docs/getting-started/installation/frameworks) covers
installation with Yarn and other package managers.
After installing the SheetJS module in a ParcelJS project, `import` statements
After installing the SheetJS module in a RollupJS project, `import` statements
can load relevant parts of the library:
```js

@ -35,7 +35,7 @@ This demo was tested in the following environments:
| Version | Date |
|:----------|:-----------|
| `1.15.11` | 2026-01-28 |
| `1.21.1` | 2025-06-18 |
:::
@ -75,8 +75,8 @@ thread '<unnamed>' panicked at 'cannot access a scoped thread local variable wit
This bug is known to affect versions `1.3.100`, `1.4.17`, and `1.10.6`.
This bug was fixed in version `1.12.1`. It is strongly recommended to upgrade
existing projects to use `1.12.1` or to downgrade to `1.2.246`.
This bug was fixed in version `1.21.1`. It is strongly recommended to upgrade
existing projects to use `1.21.1` or to downgrade to `1.2.246`.
:::
@ -95,17 +95,17 @@ npm init -y
<Tabs groupId="pm">
<TabItem value="npm" label="npm">
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz regenerator-runtime @swc/cli @swc/core@1.13.5`}
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz regenerator-runtime @swc/cli @swc/core@1.21.1`}
</CodeBlock>
</TabItem>
<TabItem value="pnpm" label="pnpm">
<CodeBlock language="bash">{`\
pnpm install --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz regenerator-runtime @swc/cli @swc/core@1.13.5`}
pnpm install --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz regenerator-runtime @swc/cli @swc/core@1.21.1`}
</CodeBlock>
</TabItem>
<TabItem value="yarn" label="Yarn" default>
<CodeBlock language="bash">{`\
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz regenerator-runtime @swc/cli @swc/core@1.13.5`}
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz regenerator-runtime @swc/cli @swc/core@1.21.1`}
</CodeBlock>
</TabItem>
</Tabs>

@ -62,7 +62,7 @@ This demo was tested in the following environments:
| Version | Date |
|:--------|:-----------|
| `3.8.8` | 2026-01-28 |
| `3.8.8` | 2025-01-07 |
:::
@ -149,7 +149,7 @@ writeFileXLSX(workbook, "Presidents.xlsx");
</html>
```
:::danger pass
:::note pass
Unlike other bundlers, Snowpack requires a full page including `HEAD` element.
@ -194,7 +194,7 @@ This demo was tested in the following environments:
| Version | Date |
|:--------|:-----------|
| `3.8.0` | 2026-01-28 |
| `3.8.0` | 2025-01-07 |
:::

@ -98,40 +98,11 @@ Each browser demo was tested in the following environments:
### XMLHttpRequest
`XMLHttpRequest` is a browser API for performing network requests. Files can be
downloaded from external sources in `GET` and `POST` requests.
The `responseType` property controls how `XMLHttpRequest` processes data. The
SheetJS [`read`](/docs/api/parse-options/) method accepts a `type` option that
specifies the data representation. SheetJS and `XMLHttpRequest` options must be
set in tandem.
In modern browsers, when the response type is `"arraybuffer"`, `XMLHttpRequest`
will generate an `ArrayBuffer` of raw data. The SheetJS `read` method directly
processes `ArrayBuffer` objects.
```mermaid
---
title: XMLHttpRequest + SheetJS in modern browsers
---
flowchart LR
file[(workbook\nfile)]
ab[(file bytes\nArrayBuffer)]
wb(((SheetJS\nWorkbook)))
file --> |XMLHttpRequest\nresponseType=&quot;arraybuffer&quot;| ab
ab --> |SheetJS read\n\n|wb
```
The following example fetches a file, generates an HTML `TABLE` from the first
sheet using the [`sheet_to_html`](/docs/api/utilities/html#html-table-output)
method, and inserts the `TABLE` in a `DIV` container:
```html title="Download and parse files with XMLHttpRequest and SheetJS"
<div id="sheetjs-tbl"></div>
<script>
/* This file will be downloaded and processed */
var url = "https://docs.sheetjs.com/pres.numbers";
For downloading data, the `arraybuffer` response type generates an `ArrayBuffer`
that can be viewed as an `Uint8Array` and fed to the SheetJS `read` method. For
legacy browsers, the option `type: "array"` should be specified:
```js
/* set up an async GET request */
var req = new XMLHttpRequest();
req.open("GET", url, true);
@ -139,19 +110,11 @@ req.responseType = "arraybuffer";
req.onload = function(e) {
/* parse the data when it is received */
var workbook = XLSX.read(req.response);
var data = new Uint8Array(req.response);
var workbook = XLSX.read(data, {type:"array"});
/* DO SOMETHING WITH workbook HERE */
/* generate HTML from the first worksheet */
var first_sheet = workbook.Sheets[workbook.SheetNames[0]];
var table_html = XLSX.utils.sheet_to_html(first_sheet);
/* add to page */
document.getElementById("sheetjs-tbl").innerHTML = table_html;
};
req.send();
</script>
```
<details>
@ -172,7 +135,7 @@ function SheetJSXHRDL() {
req.responseType = "arraybuffer";
req.onload = e => {
/* Parse file */
const wb = XLSX.read(req.response);
const wb = XLSX.read(new Uint8Array(req.response));
const ws = wb.Sheets[wb.SheetNames[0]];
/* Generate HTML */
@ -187,35 +150,6 @@ function SheetJSXHRDL() {
</details>
In browsers that do not support `ArrayBuffer`, including Internet Explorer and
older versions of Firefox, `XMLHttpRequest` will return an array of unsigned
bytes. In this case, the `read` method requires the option `type: "array"`:
```mermaid
---
title: XMLHttpRequest + SheetJS in browsers lacking ArrayBuffer support
---
flowchart LR
file[(workbook\nfile)]
ab[(file bytes\nArray)]
wb(((SheetJS\nWorkbook)))
file --> |XMLHttpRequest\nresponseType=&quot;arraybuffer&quot;| ab
ab --> |SheetJS read\ntype=&quot;array&quot;|wb
```
```js title="Download and parse files in legacy browsers (snippet)"
/* set up an async GET request */
var req = new XMLHttpRequest();
req.open("GET", url, true);
req.responseType = "arraybuffer";
req.onload = function(e) {
/* parse the data when it is received */
var workbook = XLSX.read(req.response, {type: "array"});
/* DO SOMETHING WITH workbook HERE */
};
req.send();
```
### fetch

@ -589,11 +589,10 @@ NodeJS `fetch`, available in version 20, mirrors the [browser `fetch`](#fetch).
This demo was tested in the following environments:
| NodeJS | Date |
|:-----------|:------------|
| `24.11.1` | 2026-03-22 |
| `22.22.1` | 2026-03-22 |
| `20.18.0` | 2026-03-22 |
| NodeJS | Date |
|:-----------|:-----------|
| `22.13.0` | 2025-01-08 |
| `20.18.1` | 2025-01-08 |
:::
@ -706,19 +705,18 @@ This demo was tested in the following environments:
| NodeJS | `request` | Date |
|:-----------|:----------|:-----------|
| `24.11.1` | `2.88.2` | 2026-03-22 |
| `22.22.1` | `2.88.2` | 2026-03-22 |
| `20.18.0` | `2.88.2` | 2026-03-22 |
| `18.20.8` | `2.88.2` | 2026-03-22 |
| `16.20.2` | `2.88.2` | 2026-03-22 |
| `14.21.3` | `2.88.2` | 2026-03-22 |
| `12.22.12` | `2.88.2` | 2026-03-22 |
| `10.24.1` | `2.88.2` | 2026-03-22 |
| `8.17.0` | `2.88.2` | 2026-03-22 |
| `6.17.1` | `2.88.2` | 2026-03-22 |
| `4.9.1` | `2.88.2` | 2026-03-22 |
| `0.12.18` | `2.81.0` | 2026-03-22 |
| `0.10.48` | `2.81.0` | 2026-03-22 |
| `22.13.0` | `2.88.2` | 2025-01-08 |
| `20.18.1` | `2.88.2` | 2025-01-08 |
| `18.20.5` | `2.88.2` | 2025-01-08 |
| `16.20.2` | `2.88.2` | 2025-01-08 |
| `14.21.3` | `2.88.2` | 2025-01-08 |
| `12.22.12` | `2.88.2` | 2025-01-08 |
| `10.24.1` | `2.88.2` | 2025-01-08 |
| `8.17.0` | `2.88.2` | 2025-01-08 |
| `6.17.1` | `2.88.2` | 2025-01-08 |
| `4.9.1` | `2.81.0` | 2025-01-08 |
| `0.12.18` | `2.81.0` | 2025-01-08 |
| `0.10.48` | `2.81.0` | 2025-01-08 |
:::
@ -737,13 +735,9 @@ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz request
:::note pass
The current version of `request` requires NodeJS 4 or later. For older versions
The current version of `request` requires NodeJS 6 or later. For older versions
of NodeJS, `request` version `2.81.0` should be installed.
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz request@2.81.0`}
</CodeBlock>
:::
2) Save the following to `SheetJSRequest.js`:

@ -26,8 +26,8 @@ This demo was tested in the following deployments:
| Version | Date |
|:---------|:-----------|
| `4.21.2` | 2026-03-12 |
| `5.2.1` | 2026-03-12 |
| `4.21.2` | 2025-01-03 |
| `5.0.1` | 2025-01-03 |
:::

@ -130,13 +130,13 @@ This demo was tested in the following deployments:
| Puppeteer | Date |
|:----------|:-----------|
| `24.35.0` | 2026-01-21 |
| `23.11.1` | 2026-01-21 |
| `22.15.0` | 2026-01-21 |
| `21.11.0` | 2026-01-21 |
| `20.9.0` | 2026-01-21 |
| `15.5.0` | 2026-01-21 |
| `10.4.0` | 2026-01-21 |
| `24.9.0` | 2025-05-21 |
| `23.11.1` | 2025-05-21 |
| `22.15.0` | 2025-05-21 |
| `21.11.0` | 2025-05-21 |
| `20.9.0` | 2025-05-21 |
| `15.5.0` | 2025-05-21 |
| `10.4.0` | 2025-05-21 |
:::
@ -333,9 +333,9 @@ This demo was tested in the following environments:
| Architecture | PhantomJS | Date |
|:-------------|:----------|:-----------|
| `darwin-x64` | `2.1.1` | 2026-03-07 |
| `win11-x64` | `2.1.1` | 2026-03-07 |
| `linux-x64` | `2.1.1` | 2026-03-07 |
| `darwin-x64` | `2.1.1` | 2025-03-31 |
| `win11-x64` | `2.1.1` | 2025-06-18 |
| `linux-x64` | `2.1.1` | 2025-06-16 |
:::
@ -430,4 +430,4 @@ env OPENSSL_CONF=/dev/null QT_QPA_PLATFORM=phantom ./phantomjs-2.1.1-linux-x86_6
[^1]: See ["Workbook Object"](/docs/csf/book) for more details about the SheetJS workbook object.
[^2]: See [`table_to_book` in "HTML" Utilities](/docs/api/utilities/html#create-new-sheet)
[^3]: See [`write` in "Writing Files"](/docs/api/write-options)
[^3]: See [`write` in "Writing Files"](/docs/api/write-options)

@ -296,9 +296,10 @@ features to ensure that SheetJS DOM methods can process TABLE elements.
This demo was tested in the following deployments:
| CheerioJS | Date |
|:----------|:-----------|
| `1.2.0` | 2026-01-28 |
| CheerioJS | Date |
|:--------------|:-----------|
| `1.0.0` | 2025-04-24 |
| `1.0.0-rc.12` | 2025-04-24 |
:::
@ -308,7 +309,7 @@ This demo was tested in the following deployments:
1) Install SheetJS and CheerioJS libraries:
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz cheerio@1.1.2`}
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz cheerio@1.0.0`}
</CodeBlock>
2) Download [the sample script `SheetJSCheerio.js`](pathname:///dom/SheetJSCheerio.js):
@ -374,12 +375,12 @@ This demo was tested in the following deployments:
| Architecture | DenoDOM | Deno | Date |
|:-------------|:--------|:-------|:-----------|
| `darwin-x64` | 0.1.48 | 2.7.4 | 2026-03-07 |
| `darwin-arm` | 0.1.48 | 2.7.4 | 2026-03-12 |
| `darwin-x64` | 0.1.48 | 2.2.6 | 2025-03-31 |
| `darwin-arm` | 0.1.48 | 2.2.12 | 2025-04-24 |
| `win11-x64` | 0.1.48 | 2.2.12 | 2025-04-28 |
| `win11-arm` | 0.1.48 | 2.7.7 | 2026-03-22 |
| `linux-x64` | 0.1.48 | 2.7.4 | 2026-03-07 |
| `linux-arm` | 0.1.48 | 2.7.4 | 2026-03-07 |
| `win11-arm` | 0.1.48 | 2.2.1 | 2025-02-23 |
| `linux-x64` | 0.1.48 | 2.3.6 | 2025-06-16 |
| `linux-arm` | 0.1.48 | 2.1.10 | 2025-02-16 |
:::

@ -204,26 +204,24 @@ This demo was tested in the following environments:
| `esbuild` | Date |
|:----------|:-----------|
| `0.27.2` | 2026-01-28 |
| `0.26.0` | 2026-01-28 |
| `0.25.5` | 2026-01-28 |
| `0.24.2` | 2026-01-28 |
| `0.23.1` | 2026-01-28 |
| `0.22.0` | 2026-01-28 |
| `0.21.5` | 2026-01-28 |
| `0.20.2` | 2026-01-28 |
| `0.19.12` | 2026-01-28 |
| `0.18.20` | 2026-01-28 |
| `0.17.19` | 2026-01-28 |
| `0.16.17` | 2026-01-28 |
| `0.15.18` | 2026-01-28 |
| `0.14.54` | 2026-01-28 |
| `0.13.15` | 2026-01-28 |
| `0.12.29` | 2026-01-28 |
| `0.11.23` | 2026-01-28 |
| `0.10.2` | 2026-01-28 |
| `0.9.7` | 2026-01-28 |
| `0.9.1` | 2026-01-28 |
| `0.25.5` | 2025-06-20 |
| `0.24.2` | 2025-06-20 |
| `0.23.1` | 2025-06-20 |
| `0.22.0` | 2025-06-20 |
| `0.21.5` | 2025-06-20 |
| `0.20.2` | 2025-06-20 |
| `0.19.12` | 2025-06-20 |
| `0.18.20` | 2025-06-20 |
| `0.17.19` | 2025-06-20 |
| `0.16.17` | 2025-06-20 |
| `0.15.18` | 2025-06-20 |
| `0.14.54` | 2025-06-20 |
| `0.13.15` | 2025-06-20 |
| `0.12.29` | 2025-06-20 |
| `0.11.23` | 2025-06-20 |
| `0.10.2` | 2025-06-20 |
| `0.9.7` | 2025-06-20 |
| `0.9.1` | 2025-06-20 |
:::

@ -356,12 +356,11 @@ This demo was tested in the following environments:
| ViteJS | Date |
|:---------|:-----------|
| `7.2.2` | 2025-11-15 |
| `6.4.1` | 2025-11-15 |
| `5.4.21` | 2025-11-15 |
| `4.5.14` | 2025-11-15 |
| `3.2.11` | 2025-11-15 |
| `2.9.18` | 2025-11-15 |
| `6.2.3` | 2025-03-30 |
| `5.4.15` | 2025-03-30 |
| `4.5.10` | 2025-03-30 |
| `3.2.11` | 2025-03-30 |
| `2.9.18` | 2025-03-30 |
:::
@ -377,7 +376,7 @@ major version. For example, `npm create vite@3` will use ViteJS major version 3.
:::
<CodeBlock language="bash">{`\
npm create vite@5 sheetjs-vite -- --template vue-ts --no-rolldown --no-interactive
npm create vite@5 sheetjs-vite -- --template vue-ts
cd sheetjs-vite
npm i
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}

@ -186,9 +186,9 @@ document.body.appendChild(elt);
This demo was tested in the following deployments:
| Version | Date |
|:----------|:-----------|
| `5.105.2` | 2026-03-12 |
| Version | Date |
|:---------|:-----------|
| `5.97.1` | 2025-01-08 |
:::

@ -115,11 +115,11 @@ accessed using the variable `pres` in a template:
This demo was tested in the following environments:
| Eleventy | Date |
|:----------------|:-----------|
| `4.0.0-alpha.6` | 2026-01-28 |
| `3.1.2` | 2026-01-28 |
| `2.0.1` | 2026-01-28 |
| Eleventy | Date |
|:---------------|:-----------|
| `2.0.1` | 2025-05-07 |
| `3.0.0` | 2025-05-07 |
| `3.1.0-beta.1` | 2025-05-07 |
:::

@ -52,14 +52,13 @@ This demo was tested in the following environments:
| AstroJS | Template | Date |
|:--------|:-----------------|:-----------|
| `6.0.6` | Starlight 0.38.1 | 2026-03-22 |
| `4.6.1` | Starlight 0.22.4 | 2025-01-07 |
| `5.1.3` | Starlight 0.30.5 | 2025-01-07 |
In previous test runs, this demo successfully ran using older AstroJS versions:
| AstroJS | Template |
|:--------|:-----------------|
| `5.1.3` | Starlight 0.30.5 |
| `4.6.1` | Starlight 0.22.4 |
| `3.6.5` | Starlight 0.14.0 |
:::

@ -242,7 +242,7 @@ npx -y @capacitor/cli telemetry
2) Create a new Svelte project:
```bash
npm create vite@latest sheetjs-cap -- --template svelte --no-rolldown --no-interactive
npm create vite@latest sheetjs-cap -- --template svelte
cd sheetjs-cap
```

@ -48,18 +48,18 @@ This demo was tested in the following environments:
**Real Devices**
| OS | Device | Dart | Flutter | Date |
|:-----------|:------------------|:---------|:---------|:-----------|
| Android 34 | NVIDIA Shield | `3.7.2` | `3.29.2` | 2025-03-31 |
| iOS 26.3 | iPhone 13 Pro Max | `3.11.1` | `3.41.4` | 2026-03-10 |
| OS | Device | Dart | Flutter | Date |
|:-----------|:------------------|:--------|:---------|:-----------|
| Android 34 | NVIDIA Shield | `3.7.2` | `3.29.2` | 2025-03-31 |
| iOS 15.6 | iPhone 13 Pro Max | `3.7.2` | `3.29.2` | 2025-03-31 |
**Simulators**
| OS | Device | Dart | Flutter | Dev Platform | Date |
|:-----------|:------------------|:---------|:---------|:-------------|:-----------|
| Android 36 | Pixel Tablet | `3.11.1` | `3.41.4` | `darwin-arm` | 2026-03-11 |
| iOS 18.6 | iPhone 16 Pro Max | `3.11.1` | `3.41.4` | `darwin-arm` | 2026-03-11 |
| Android 36 | Pixel 9 Pro XL | `3.7.2` | `3.29.3` | `win11-x64` | 2054-04-28 |
| OS | Device | Dart | Flutter | Dev Platform | Date |
|:-----------|:------------------|:--------|:---------|:-------------|:-----------|
| Android 35 | Pixel 9 Pro XL | `3.7.2` | `3.29.2` | `darwin-x64` | 2025-03-31 |
| iOS 18.3 | iPhone 16 Pro Max | `3.7.2` | `3.29.2` | `darwin-x64` | 2025-03-31 |
| Android 36 | Pixel 9 Pro XL | `3.7.2` | `3.29.3` | `win11-x64` | 2054-04-28 |
:::
@ -297,21 +297,6 @@ error: Android sdkmanager not found. Update to the latest Android SDK and ensure
Android Studio does not install `Android SDK Command-Line Tools` by default. It
must be installed manually.
---
To enable Android SDK Command-Line Tools via Android Studio follow this:
Find SDK manager by searching it in *Search Everywhere*
![open_sdk_manager](pathname:///flutter/open_sdk_manager.png)
Select **SDK Tools** from the top tab in the middle panel, then check **Android SDK Command-line Tools (latest)** and
click **OK**.
![enbable_and_install_cmd_tools](pathname:///flutter/enbable_and_install_cmd_tools.png)
A dialog will appear asking to download and install the tools. Click **OK** to continue.
---
Assuming the command-line tools are installed
This was fixed by switching to Java 20, installing `Android SDK 33`, and rolling
@ -546,7 +531,7 @@ Once the app loads, stop the terminal process and close the simulator.
5) Install Flutter / Dart dependencies:
```bash
flutter pub add http csv flutter_js collection
flutter pub add http csv flutter_js
```
:::info pass
@ -570,7 +555,7 @@ As stated, "Developer Mode" must be enabled:
3) Reinstall dependencies:
```bash
flutter pub add http csv flutter_js collection
flutter pub add http csv flutter_js
```
:::
@ -873,7 +858,7 @@ The app may take 30 seconds to load the content.
### iOS Device
19) **Follow the official ["Deploy to physical iOS devices"](https://docs.flutter.dev/platform-integration/ios/setup#set-up-devices) instructions**
19) Follow the official "Deploy to physical iOS devices" instructions[^10]
20) Connect the iOS device and verify that `flutter` can find the device:

@ -13,7 +13,6 @@ import current from '/version.js';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
import Link from '@docusaurus/Link';
export const r = {style: {color:"red"}};
export const g = {style: {color:"green"}};
@ -258,8 +257,7 @@ curl -o ./src/App.tsx https://docs.sheetjs.com/lynx/App.tsx
```bash
curl -o ./src/App.css https://docs.sheetjs.com/lynx/App.css
```
<Link id="step5"/>
<a name="step5"></a>
5) Start the development server:

@ -238,7 +238,7 @@ This demo was tested in the following environments:
| OS and Version | Architecture | Electron | Date |
|:---------------|:-------------|:---------|:-----------|
| macOS 15.7.4 | `darwin-x64` | `40.8.0` | 2026-03-08 |
| macOS 15.3 | `darwin-x64` | `35.1.2` | 2025-03-31 |
| macOS 14.5 | `darwin-arm` | `35.1.2` | 2025-08-30 |
| Windows 11 | `win11-x64` | `33.2.1` | 2025-02-11 |
| Windows 11 | `win11-arm` | `33.2.1` | 2025-02-23 |

@ -37,12 +37,12 @@ This demo was tested in the following deployments:
| Architecture | Version | NodeJS | Source | Date |
|:-------------|:---------------|:----------|:----------|:-----------|
| `darwin-x64` | `5.0.0-beta.4` | `14.15.3` | Pre-built | 2026-01-21 |
| `darwin-arm` | `5.0.0-beta.4` | `20.18.0` | Compiled | 2026-03-07 |
| `win11-x64` | `5.0.0-beta.4` | `14.15.3` | Pre-built | 2026-03-08 |
| `darwin-x64` | `5.0.0-beta.4` | `14.15.3` | Pre-built | 2025-04-21 |
| `darwin-arm` | `5.0.0-beta.4` | `22.14.0` | Compiled | 2025-06-18 |
| `win11-x64` | `5.0.0-beta.4` | `14.15.3` | Pre-built | 2025-05-07 |
| `win11-arm` | `4.0.0-rc.6` | `22.14.0` | Compiled | 2025-02-23 |
| `linux-x64` | `5.0.0-beta.4` | `14.15.3` | Pre-built | 2026-01-18 |
| `linux-arm` | `5.0.0-beta.4` | `24.14.0` | Compiled | 2026-03-07 |
| `linux-x64` | `5.0.0-beta.4` | `14.15.3` | Pre-built | 2025-04-21 |
| `linux-arm` | `4.0.0-rc.6` | `22.13.0` | Compiled | 2025-02-15 |
:::

@ -37,12 +37,12 @@ This demo was tested in the following deployments:
| Architecture | Version | NodeJS | Date |
|:-------------|:--------|:---------|:-----------|
| `darwin-x64` | `5.8.1` | `18.5.0` | 2026-01-21 |
| `darwin-arm` | `5.8.1` | `18.5.0` | 2026-03-07 |
| `win11-x64` | `5.8.1` | `18.5.0` | 2026-03-08 |
| `darwin-x64` | `5.8.1` | `18.5.0` | 2025-04-21 |
| `darwin-arm` | `5.8.1` | `18.5.0` | 2025-06-18 |
| `win11-x64` | `5.8.1` | `18.5.0` | 2025-05-07 |
| `win11-arm` | `5.8.1` | `18.5.0` | 2025-02-23 |
| `linux-x64` | `5.8.1` | `18.5.0` | 2026-01-18 |
| `linux-arm` | `5.8.1` | `18.5.0` | 2026-03-07 |
| `linux-x64` | `5.8.1` | `18.5.0` | 2025-04-21 |
| `linux-arm` | `5.8.1` | `18.5.0` | 2025-02-15 |
:::

@ -30,10 +30,10 @@ This demo was tested in the following deployments:
| Architecture | Version | NodeJS | Date |
|:-------------|:--------|:----------|:-----------|
| `darwin-x64` | `3.0.0` | `25.8.0` | 2026-03-08 |
| `darwin-arm` | `3.0.0` | `23.11.0` | 2026-03-13 |
| `darwin-x64` | `2.4.4` | `23.11.0` | 2025-04-21 |
| `darwin-arm` | `2.4.4` | `23.11.0` | 2025-06-18 |
| `win11-x64` | `2.4.4` | `16.20.2` | 2025-05-07 |
| `linux-x64` | `3.0.0` | `25.8.0` | 2026-03-08 |
| `linux-x64` | `2.4.4` | `23.11.0` | 2025-04-21 |
| `linux-arm` | `2.4.4` | `23.8.0` | 2025-02-15 |
:::
@ -91,7 +91,7 @@ yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz exit-on-epi
<TabItem value="unix" label="Linux/MacOS">
```bash
npx -y boxednode@3.0.0 -s xlsx-cli.js -t xlsx-cli
npx -y boxednode@2.4.4 -s xlsx-cli.js -t xlsx-cli
```
:::note pass

@ -158,12 +158,12 @@ This demo was tested in the following deployments:
| Architecture | NodeJS | Date |
|:-------------|:----------|:-----------|
| `darwin-x64` | `24.13.0` | 2026-01-21 |
| `darwin-arm` | `20.18.0` | 2026-03-13 |
| `win11-x64` | `24.13.0` | 2026-03-08 |
| `darwin-x64` | `22.14.0` | 2025-04-21 |
| `darwin-arm` | `24.2.0` | 2025-06-18 |
| `win11-x64` | `24.2.0` | 2025-06-17 |
| `win11-arm` | `22.14.0` | 2025-02-23 |
| `linux-x64` | `24.11.0` | 2026-03-08 |
| `linux-arm` | `24.14.0` | 2026-03-07 |
| `linux-x64` | `24.2.0` | 2025-06-16 |
| `linux-arm` | `22.13.0` | 2025-02-16 |
:::

@ -79,12 +79,12 @@ This demo was last tested in the following deployments:
| Architecture | BunJS | Date |
|:-------------|:---------|:-----------|
| `darwin-x64` | `1.3.6` | 2026-01-21 |
| `darwin-arm` | `1.3.10` | 2026-03-07 |
| `win11-x64` | `1.3.10` | 2026-03-08 |
| `darwin-x64` | `1.2.10` | 2025-04-21 |
| `darwin-arm` | `1.2.16` | 2025-06-18 |
| `win11-x64` | `1.2.13` | 2025-05-07 |
| `win11-arm` | `1.2.3` | 2025-02-23 |
| `linux-x64` | `1.3.10` | 2026-03-08 |
| `linux-arm` | `1.3.10` | 2026-03-07 |
| `linux-x64` | `1.2.16` | 2025-06-16 |
| `linux-arm` | `1.2.2` | 2025-02-16 |
:::

@ -102,12 +102,12 @@ This demo was last tested in the following deployments:
| Architecture | Deno | Date |
|:-------------|:---------|:-----------|
| `darwin-x64` | `2.6.5` | 2026-01-21 |
| `darwin-arm` | `2.7.4` | 2026-03-07 |
| `win11-x64` | `2.7.4` | 2026-03-08 |
| `darwin-x64` | `2.2.11` | 2025-04-21 |
| `darwin-arm` | `2.3.6` | 2025-06-18 |
| `win11-x64` | `2.3.6` | 2025-06-17 |
| `win11-arm` | `2.2.1` | 2025-02-23 |
| `linux-x64` | `2.7.4` | 2026-03-08 |
| `linux-arm` | `2.7.4` | 2026-03-07 |
| `linux-x64` | `2.3.6` | 2025-06-16 |
| `linux-arm` | `2.1.10` | 2025-02-15 |
:::

@ -88,14 +88,14 @@ console.log(csv);
This demo was tested in the following deployments:
| Architecture | Version | Commit | Date |
|:-------------|:----------|:----------|:-----------|
| `darwin-x64` | `24.12.0` | `793dd9d` | 2026-01-21 |
| `darwin-arm` | `24.12.0` | `793dd9d` | 2026-01-18 |
| `win11-x64` | `24.12.0` | | 2026-03-08 |
| `win11-arm` | `24.12.0` | | 2025-04-19 |
| `linux-x64` | `24.12.0` | `65e5595` | 2026-01-18 |
| `linux-arm` | `24.12.0` | `65e5595` | 2026-01-21 |
| Architecture | TxikiJS | Date |
|:-------------|:----------|:-----------|
| `darwin-x64` | `24.12.0` | 2025-04-19 |
| `darwin-arm` | `24.12.0` | 2025-04-19 |
| `win11-x64` | `24.12.0` | 2025-04-19 |
| `win11-arm` | `24.12.0` | 2025-04-19 |
| `linux-x64` | `24.12.0` | 2025-04-19 |
| `linux-arm` | `24.12.0` | 2025-04-19 |
:::
@ -133,40 +133,6 @@ cp build/tjs ../
cd ..
```
:::caution pass
In some `linux-x64` test runs, the `make` step failed:
```
make[5]: *** No rule to make target 'all-configured'. Stop.
```
After re-running `make`, the `[ 0%]` line identifies the root cause:
```text
// highlight-next-line
[ 0%] Performing build step for 'libffi'
[ 1%] Built target sqlite3
[ 2%] Built target ffi-test
...
[ 61%] Built target sqlite-test
MAKE x86_64-pc-linux-gnu : 0 * all-configured
// highlight-next-line
make[5]: *** No rule to make target 'all-configured'. Stop.
```
If the root cause is `libffi`, it is possible to use the system `libffi`. The
following commands should be run in the `txiki.js` folder:
```bash
rm -rf build
cmake -B build -DCMAKE_BUILD_TYPE=Release -DUSE_EXTERNAL_FFI=ON
cmake --build build -j 8
cp build/tjs ../
```
:::
</TabItem>
<TabItem value="win11-x64" label="Windows">

@ -8,7 +8,7 @@ sidebar_custom_props:
---
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.13.0/sql-wasm.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.8.0/sql-wasm.js"></script>
</head>
import current from '/version.js';
@ -39,11 +39,11 @@ This demo was tested in the following environments:
| Platform | Connector Library | Date |
|:-----------------|:----------------------------|:-----------|
| Chromium 143 | `sql.js` (`1.13.0`) | 2026-03-22 |
| Konqueror 25 | `sql.js` (`1.13.0`) | 2026-03-22 |
| NodeJS `20.18.0` | `better-sqlite3` (`12.6.2`) | 2026-03-22 |
| BunJS `1.3.11` | (built-in) | 2026-03-22 |
| Deno `2.7.4` | `sqlite` (`3.9.1`) | 2026-03-22 |
| Chromium 131 | `sql.js` (`1.8.0`) | 2025-01-08 |
| Konqueror 22 | `sql.js` (`1.8.0`) | 2025-04-23 |
| NodeJS `20.18.0` | `better-sqlite3` (`11.7.2`) | 2025-01-08 |
| BunJS `1.1.43` | (built-in) | 2025-01-08 |
| Deno `2.1.4` | `sqlite` (`3.9.1`) | 2025-01-09 |
:::
@ -143,7 +143,7 @@ exported to a XLSX file.
function SheetJSQLJS() { return (<button onClick={async() => {
/* Load sql.js library */
const config = {
locateFile: filename => `https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.13.0/${filename}`
locateFile: filename => `https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.8.0/${filename}`
}
const SQL = await initSqlJs(config);
@ -211,7 +211,7 @@ sqlite3 chinook.db ".read chinook.sql"
<CodeBlock language="bash">{`\
npm init -y
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz better-sqlite3@12.6.2`}
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz better-sqlite3@9.2.0`}
</CodeBlock>
2) Download [`SheetJSQLiteNode.mjs`](pathname:///sqlite/SheetJSQLiteNode.mjs):
@ -288,8 +288,7 @@ import * as XLSX from "https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs"
var db = new DB("chinook.db");
\n\
/* get data from the \`Invoice\` table */
var query = db.prepareQuery("SELECT * FROM 'Invoice' LIMIT 100000");
var aoa = query.all();
var aoa = db.prepareQuery("SELECT * FROM 'Invoice' LIMIT 100000").all();
\n\
/* create worksheet from the row objects */
var data = [query.columns().map(x => x.name)].concat(aoa);

@ -25,9 +25,10 @@ This demo was tested in the following environments:
| Version | Database | Connector Module | Date |
|:----------|:---------|:-----------------|:-----------|
| `0.21.20` | SQLite | `sqlite3` | 2026-03-22 |
| `2.5.1` | SQLite | `better-sqlite3` | 2026-03-22 |
| `3.1.0` | SQLite | `better-sqlite3` | 2026-03-22 |
| `0.21.20` | SQLite | `sqlite3` | 2025-01-09 |
| `2.4.2` | SQLite | `better-sqlite3` | 2025-01-09 |
| `2.5.1` | SQLite | `better-sqlite3` | 2025-01-09 |
| `3.1.0` | SQLite | `better-sqlite3` | 2025-01-09 |
:::
@ -41,8 +42,8 @@ loaded in NodeJS scripts that use KnexJS.
The KnexJS `select` method[^1] creates a `SELECT` query. The return value is a
Promise that resolves to an array of objects.
The SheetJS [`json_to_sheet`](/docs/api/utilities/array#array-of-objects-input)
method can generate a [worksheet object](/docs/csf/sheet) from the array:
The SheetJS `json_to_sheet` method[^2] can generate a worksheet object[^3] from
the array of objects:
```js
const table_name = "Tabeller1"; // name of table
@ -54,9 +55,8 @@ const aoo = await knex.select("*").from(table_name);
const worksheet = XLSX.utils.json_to_sheet(aoo);
```
Using [`book_new` and `book_append_sheet`](/docs/api/utilities/wb), a workbook
object can be created. This workbook is typically exported to the filesystem
with the [`writeFile`](/docs/api/write-options) method:
A workbook object can be built from the worksheet using utility functions[^4].
The workbook can be exported using the SheetJS `writeFile` method[^5]:
```js
/* create a new workbook and add the worksheet */
@ -69,10 +69,10 @@ XLSX.writeFile(wb, "SheetJSKnexJSExport.xlsx");
### Importing Data
The SheetJS [`sheet_to_json` function](/docs/api/utilities/array#array-output)
accepts a worksheet object and generates an array of objects.
The SheetJS `sheet_to_json` function[^6] takes a worksheet object and generates
an array of objects.
The KnexJS `insert` method[^2] creates `INSERT` queries. The return value is a
The KnexJS `insert` method[^7] creates `INSERT` queries. The return value is a
Promise that resolves when the query is executed:
```js
@ -87,8 +87,8 @@ await knex.insert(aoo).into(table_name);
### Creating a Table
The KnexJS Schema Builder supports creating tables with `createTable`[^3] and
dropping tables with `dropTableIfExists`[^4].
The KnexJS Schema Builder supports creating tables with `createTable`[^8] and
dropping tables with `dropTableIfExists`[^9].
The array of objects can be scanned to determine column names and types.
@ -272,6 +272,11 @@ Older versions of KnexJS do not support the `better-sqlite3` module. The
:::
[^1]: See [`select`](https://knexjs.org/guide/query-builder.html#select) in the KnexJS query builder documentation.
[^2]: See [`insert`](https://knexjs.org/guide/query-builder.html#insert) in the KnexJS query builder documentation.
[^3]: See [`createTable`](https://knexjs.org/guide/schema-builder.html#createtable) in the KnexJS Schema Builder documentation.
[^4]: See [`dropTableIfExists`](https://knexjs.org/guide/schema-builder.html#droptableifexists) in the KnexJS Schema Builder documentation.
[^2]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
[^3]: See ["Sheet Objects"](/docs/csf/sheet) in "SheetJS Data Model" for more details.
[^4]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
[^5]: See [`writeFile` in "Writing Files"](/docs/api/write-options)
[^6]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
[^7]: See [`insert`](https://knexjs.org/guide/query-builder.html#insert) in the KnexJS query builder documentation.
[^8]: See [`createTable`](https://knexjs.org/guide/schema-builder.html#createtable) in the KnexJS Schema Builder documentation.
[^9]: See [`dropTableIfExists`](https://knexjs.org/guide/schema-builder.html#droptableifexists) in the KnexJS Schema Builder documentation.

@ -36,13 +36,12 @@ This demo was tested in the following environments:
| Postgres | Connector Library | Date |
|:---------|:------------------|:-----------|
| `18.3` | `pg` (`8.20.0`) | 2026-03-22 |
| `17.2` | `pg` (`8.20.0`) | 2026-03-22 |
| `16.6` | `pg` (`8.20.0`) | 2026-03-22 |
| `15.10` | `pg` (`8.20.0`) | 2026-03-22 |
| `14.15` | `pg` (`8.20.0`) | 2026-03-22 |
| `13.18` | `pg` (`8.20.0`) | 2026-03-22 |
| `12.22` | `pg` (`8.20.0`) | 2026-03-22 |
| `17.2` | `pg` (`8.13.1`) | 2025-01-03 |
| `16.6` | `pg` (`8.13.1`) | 2025-01-03 |
| `15.10` | `pg` (`8.13.1`) | 2025-01-03 |
| `14.15` | `pg` (`8.13.1`) | 2025-01-03 |
| `13.18` | `pg` (`8.13.1`) | 2025-01-03 |
| `12.22` | `pg` (`8.13.1`) | 2025-01-03 |
:::
@ -59,8 +58,8 @@ other PostgreSQL libraries.
`Client#query` returns a Promise that resolves to a result set. The `rows`
property of the result is an array of objects.
The SheetJS [`json_to_sheet`](/docs/api/utilities/array#array-of-objects-input)
method can generate a [worksheet object](/docs/csf/sheet) from the array:
The SheetJS `json_to_sheet` method[^2] can generate a worksheet object[^3] from
the array of objects:
```js
const table_name = "Tabeller1"; // name of table
@ -72,8 +71,8 @@ const res = await client.query(`SELECT * FROM ${table_name}`);
const worksheet = XLSX.utils.json_to_sheet(res.rows);
```
[Utility functions](/docs/api/utilities/wb) can build a SheetJS workbook object.
The workbook can be exported using the [`writeFile`](/docs/api/write-options):
A workbook object can be built from the worksheet using utility functions[^4].
The workbook can be exported using the SheetJS `writeFile` method[^5]:
```js
/* create a new workbook and add the worksheet */
@ -86,8 +85,8 @@ XLSX.writeFile(wb, "SheetJSPGExport.xlsx");
### Importing Data
The SheetJS [`sheet_to_json` function](/docs/api/utilities/array#array-output)
accepts a worksheet object and generates an array of objects.
The SheetJS `sheet_to_json` function[^6] takes a worksheet object and generates
an array of objects.
Queries must be manually generated from the objects. Assuming the field names
in the object match the column headers, a loop can generate `INSERT` queries.
@ -102,7 +101,7 @@ INSERT INTO table_name (?) VALUES (?);
```
Queries are generated manually. To help prevent SQL injection vulnerabilities,
the `pg-format`[^2] module escapes identifiers and fields.
the `pg-format`[^7] module escapes identifiers and fields.
:::
@ -309,7 +308,7 @@ sudo -u postgres createuser -P $USER
sudo -u postgres psql -c "ALTER USER $USER WITH SUPERUSER;"
```
If running the optional user creation steps above, a PostgreSQL password will be required. [^3]
If running the optional user creation steps above, a PostgreSQL password will be required. [^8]
Run the command to start a local database instance.
@ -539,5 +538,10 @@ psql SheetJSPG -c 'SELECT * FROM "Presidents";'
[^1]: See [the official `pg` website](https://node-postgres.com/) for more info.
[^2]: The [`pg-format`](https://npm.im/pg-format) package is available on the public NPM registry. Even though the project is marked as deprecated, the official [`pg` website still recommends `pg-format`](https://node-postgres.com/features/queries#parameterized-query:~:text=use%20pg%2Dformat%20package%20for%20handling%20escaping)
[^3]: PostgreSQL on Linux uses [SCRAM authentication by default, which requires a password](https://www.postgresql.org/docs/current/auth-password.html)
[^2]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
[^3]: See ["Sheet Objects"](/docs/csf/sheet) in "SheetJS Data Model" for more details.
[^4]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
[^5]: See [`writeFile` in "Writing Files"](/docs/api/write-options)
[^6]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
[^7]: The [`pg-format`](https://npm.im/pg-format) package is available on the public NPM registry. Even though the project is marked as deprecated, the official [`pg` website still recommends `pg-format`](https://node-postgres.com/features/queries#parameterized-query:~:text=use%20pg%2Dformat%20package%20for%20handling%20escaping)
[^8]: PostgreSQL on Linux uses [SCRAM authentication by default, which requires a password](https://www.postgresql.org/docs/current/auth-password.html)

@ -36,8 +36,7 @@ This demo was tested in the following environments:
| MariaDB | Connector Library | Date |
|:---------|:--------------------|:-----------|
| `11.8` | `mysql2` (`3.20.2`) | 2026-03-22 |
| `10.6` | `mysql2` (`3.20.2`) | 2026-03-22 |
| `11.6.2` | `mysql2` (`3.12.0`) | 2025-01-19 |
:::
@ -54,8 +53,8 @@ to other MariaDB and MySQL libraries.
`Connection#execute` returns a Promise that resolves to a result array. The
first entry of the result is an array of objects.
The SheetJS [`json_to_sheet`](/docs/api/utilities/array#array-of-objects-input)
method can generate a [worksheet object](/docs/csf/sheet) from the array:
The SheetJS `json_to_sheet` method[^2] can generate a worksheet object[^3] from
the array of objects:
```js
const mysql = require("mysql2/promise"), XLSX = require("xlsx");
@ -73,9 +72,8 @@ const [rows, fields] = await conn.execute(`SELECT * FROM ${mysql.escapeId(table_
const worksheet = XLSX.utils.json_to_sheet(rows);
```
Using [`book_new` and `book_append_sheet`](/docs/api/utilities/wb), a workbook
object can be created. This workbook is typically exported to the filesystem
with the [`writeFile`](/docs/api/write-options) method:
A workbook object can be built from the worksheet using utility functions[^4].
The workbook can be exported using the SheetJS `writeFile` method[^5]:
```js
/* create a new workbook and add the worksheet */
@ -88,8 +86,8 @@ XLSX.writeFile(wb, "SheetJSMariaDBExport.xlsx");
### Importing Data
The SheetJS [`sheet_to_json` function](/docs/api/utilities/array#array-output)
accepts a worksheet object and generates an array of objects.
The SheetJS `sheet_to_json` function[^6] takes a worksheet object and generates
an array of objects.
Queries must be manually generated from the objects. Assuming the field names
in the object match the column headers, a loop can generate `INSERT` queries.
@ -104,7 +102,7 @@ INSERT INTO table_name (?) VALUES (?);
```
Queries are generated manually. To help prevent SQL injection vulnerabilities,
the undocumented `escapeId` method [^2] escapes identifiers and fields.
the undocumented `escapeId` method [^7] escapes identifiers and fields.
:::
@ -410,4 +408,9 @@ The output should be consistent with the following table:
```
[^1]: See [the official `mysql2` website](https://sidorares.github.io/node-mysql2/docs) for more info.
[^2]: The `mysql2` connector library `escapeId` method is not mentioned in the documentation but is present in the TypeScript definitions.
[^2]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
[^3]: See ["Sheet Objects"](/docs/csf/sheet) in "SheetJS Data Model" for more details.
[^4]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
[^5]: See [`writeFile` in "Writing Files"](/docs/api/write-options)
[^6]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
[^7]: The `mysql2` connector library `escapeId` method is not mentioned in the documentation but is present in the TypeScript definitions.

@ -10,35 +10,27 @@ sidebar_custom_props:
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
:::danger pass
**MongoDB is not open source!**
MongoDB uses the Server Side Public License (SSPL), which is not open source.
This demo has been tested with FerretDB and other servers that implement the
MongoDB wire protocol.
:::
[FerretDB](https://www.ferretdb.com/) is an Apache 2.0-licensed document database
that implements the MongoDB wire protocol using PostgreSQL or SQLite as the backend.
[MongoDB](https://mongodb.github.io/node-mongodb-native/) is a document-oriented
database engine. [FerretDB](https://www.ferretdb.com/) is a truly open source
implementation of the MongoDB wire protocol.
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
This demo uses SheetJS to exchange data between spreadsheets and MongoDB. We'll
explore how to save tables from a database collection to spreadsheets and how to
add data from spreadsheets into a collection.
explore how to use save tables from a MongoDB collection to spreadsheets and how
to add data from spreadsheets into a collection.
:::note Tested Deployments
This demo was tested in the following environments:
| Server | Connector Library | Date |
|:-------------------|:--------------------|:-----------|
| FerretDB `1.24.0` | `mongodb` (`7.1.0`) | 2026-03-22 |
| MongoDB CE `8.2.0` | `mongodb` (`7.1.0`) | 2026-03-22 |
| Server | Connector Library | Date |
|:--------------------|:---------------------|:-----------|
| FerretDB `1.24.0` | `mongodb` (`6.12.0`) | 2025-01-03 |
| MongoDB CE `8.0.4` | `mongodb` (`6.12.0`) | 2025-01-19 |
| MongoDB CE `7.0.16` | `mongodb` (`6.12.0`) | 2025-01-19 |
| MongoDB CE `6.0.20` | `mongodb` (`6.5.0`) | 2025-01-19 |
:::
@ -53,10 +45,8 @@ a row in the table.
#### Importing Data
Data stored in an array of objects can be added to MongoDB Collections using
`Collection#insertMany`[^1].
The SheetJS [`sheet_to_json` method](/docs/api/utilities/array#array-output) can
generate data from worksheets:
`Collection#insertMany`[^1]. The SheetJS `sheet_to_json` method[^2] can generate
data from worksheets:
```js
/* import data from a worksheet to a collection */
@ -64,15 +54,15 @@ const aoo = XLSX.utils.sheet_to_json(ws);
await collection.insertMany(aoo, {ordered: true});
```
Typically worksheets are extracted from [workbook objects](/docs/csf/book) that
are created by SheetJS [`read` or `readFile`](/docs/api/parse-options) methods.
Typically worksheet objects are extracted from workbook objects[^3] generated
from the SheetJS `read` or `readFile` methods[^4].
#### Exporting Data
`Collection#find`[^2] can pull an array of objects from a MongoDB Collection.
`Collection#find`[^5] can pull an array of objects from a MongoDB Collection.
The SheetJS [`json_to_sheet`](/docs/api/utilities/array#array-of-objects-input)
method can take the result and generate a worksheet object.
The SheetJS `json_to_sheet` method[^6] can take the result and generate a
worksheet object.
:::info pass
@ -89,18 +79,20 @@ const aoo = await collection.find({}, {projection:{_id:0}}).toArray();
const ws = utils.json_to_sheet(aoo);
```
Using [`book_new` and `book_append_sheet`](/docs/api/utilities/wb), a workbook
object can be created. This workbook is typically exported to the filesystem
with the [`writeFile`](/docs/api/write-options) method.
Using `book_new` and `book_append_sheet`[^7], a workbook object can be created.
This workbook is typically exported to the filesystem with `writeFile`[^8].
## Complete Example
1) Install and start a compatible database locally.
0) Install a MongoDB-compatible server. Options include MongoDB CE[^9] and
FerretDB[^10]
1) Start a server on `localhost` (follow official instructions).
<details>
<summary><b>MongoDB CE</b> (click to show)</summary>
<summary><b>MongoDB CE Setup</b> (click to show)</summary>
For MongoDB 8.2 Community Edition, the macOS steps required `brew`:
For MongoDB 8.0 Community Edition, the macOS steps required `brew`:
```bash
brew tap mongodb/brew
@ -124,7 +116,7 @@ $(brew --prefix)/opt/mongodb-community/bin/mongod --config $(brew --prefix)/etc/
</details>
<details>
<summary><b>FerretDB</b> (click to show)</summary>
<summary><b>FerretDB Setup</b> (click to show)</summary>
The official documentation recommends Docker, but it is strongly recommended to
use [`colima`](https://github.com/abiosoft/colima) on MacOS:
@ -196,7 +188,7 @@ docker run -d --rm --name ferretdb -p 27017:27017 -e FERRETDB_HANDLER=sqlite ghc
mkdir sheetjs-mongo
cd sheetjs-mongo
npm init -y
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz mongodb@7.1.0`}
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz mongodb@6.12.0`}
</CodeBlock>
3) Save the following to `SheetJSMongoCRUD.mjs` (the key step is highlighted):
@ -255,6 +247,12 @@ There should be no errors in the terminal. The script will generate the file
`SheetJSMongoCRUD.xlsx`. That file can be opened in a spreadsheet editor.
[^1]: See [`insertMany`](https://mongodb.github.io/node-mongodb-native/5.7/classes/Collection.html#insertMany) in the MongoDB documentation.
[^2]: See [`find`](https://mongodb.github.io/node-mongodb-native/5.7/classes/Collection.html#find) in the MongoDB documentation.
[^3]: See ["Install MongoDB Community Edition"](https://www.mongodb.com/docs/manual/administration/install-community/#std-label-install-mdb-community-edition) in the MongoDB documentation.
[^4]: See ["SQLite Setup with Docker Compose"](https://docs.ferretdb.io/quickstart-guide/docker/#sqlite-setup-with-docker-compose) in the FerretDB documentation.
[^2]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
[^3]: See ["Workbook Object"](/docs/csf/book)
[^4]: See [`read` and `readFile` in "Reading Files"](/docs/api/parse-options)
[^5]: See [`find`](https://mongodb.github.io/node-mongodb-native/5.7/classes/Collection.html#find) in the MongoDB documentation.
[^6]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
[^7]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
[^8]: See [`writeFile` in "Writing Files"](/docs/api/write-options)
[^9]: See ["Install MongoDB Community Edition"](https://www.mongodb.com/docs/manual/administration/install-community/#std-label-install-mdb-community-edition) in the MongoDB documentation.
[^10]: See ["SQLite Setup with Docker Compose"](https://docs.ferretdb.io/quickstart-guide/docker/#sqlite-setup-with-docker-compose) in the FerretDB documentation.

@ -24,9 +24,8 @@ serialization protocol" (RESP).
:::
[KeyDB](https://docs.keydb.dev/) is a Redis-compatible in-memory data store
licensed under BSD-3-Clause. It is capable of storing sets, lists and other
simple data structures.
[KeyDB](https://docs.keydb.dev/) is a Redis-compatible in-memory data store. It
is capable of storing sets, lists and other simple data structures.
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
@ -39,12 +38,12 @@ from XLSX files to a Redis database and to serialize a database to a workbook.
This demo was tested in the following environments:
| Server | Connector Module | Date |
|:---------------|:-------------------|:----------:|
| KeyDB `6.3.4` | `redis` (`5.11.0`) | 2026-03-22 |
| Redis `6.2.18` | `redis` (`5.11.0`) | 2026-03-22 |
| Valkey `9.0.3` | `redis` (`5.11.0`) | 2026-03-22 |
| Garnet `1.1.1` | `redis` (`5.11.0`) | 2026-03-22 |
| Server | Connector Module | Date |
|:----------------|:------------------|:----------:|
| KeyDB `6.3.4` | `redis` (`4.7.0`) | 2025-01-08 |
| Redis `6.2.17` | `redis` (`4.7.0`) | 2025-01-08 |
| Valkey `8.0.2` | `redis` (`4.7.0`) | 2025-01-08 |
| Garnet `1.0.49` | `redis` (`4.7.0`) | 2025-01-08 |
:::
@ -250,14 +249,12 @@ this demo also requires NodeJS version 18 or later.
This demo was last tested on macOS.
---
_KeyDB_
KeyDB was installed from Homebrew:
KeyDB was installed with:
```bash
brew install keydb
brew install keydb@6.3.4
```
The following command started the server process:
@ -266,8 +263,6 @@ The following command started the server process:
keydb-server --protected-mode no
```
---
_Valkey_
Valkey was installed with:
@ -283,7 +278,7 @@ This conflicts with the main `redis` package. `redis` must be unlinked:
```bash
brew unlink redis
brew link valkey || brew install valkey
brew link valkey
```
:::
@ -291,11 +286,9 @@ brew link valkey || brew install valkey
The following command started the server process:
```bash
redis-server $(brew --prefix)/etc/redis.conf
redis-server $(brew config | grep HOMEBREW_PREFIX | awk '{print $2}')/etc/redis.conf
```
---
_Redis 6_
Redis 6 was installed with:
@ -307,7 +300,7 @@ brew install redis@6.2
The following command started the server process:
```bash
redis-server $(brew --prefix)/etc/redis.conf
redis-server $(brew config | grep HOMEBREW_PREFIX | awk '{print $2}')/etc/redis.conf
```
:::caution pass
@ -317,8 +310,6 @@ version of Redis.
:::
---
_Garnet_
After installing `dotnet` and .NET runtime, install the `garnet-server` tool:
@ -344,7 +335,7 @@ curl -LO https://docs.sheetjs.com/nosql/SheetJSRedisTest.mjs
2) Install dependencies:
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz redis@5.11.0`}
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz redis@4.6.13`}
</CodeBlock>
3) Run the test script:

@ -29,13 +29,13 @@ This demo was tested in the following environments:
| PouchDB | Date |
|:--------|:----------:|
| `9.0.0` | 2026-01-08 |
| `8.0.1` | 2026-01-08 |
| `7.3.1` | 2026-01-08 |
| `6.4.3` | 2026-01-08 |
| `5.4.5` | 2026-01-08 |
| `4.0.3` | 2026-01-08 |
| `3.6.0` | 2026-01-08 |
| `9.0.0` | 2025-01-19 |
| `8.0.1` | 2025-01-19 |
| `7.3.1` | 2025-01-19 |
| `6.4.3` | 2025-01-19 |
| `5.4.5` | 2025-01-19 |
| `4.0.3` | 2025-01-19 |
| `3.6.0` | 2025-01-19 |
:::
@ -53,18 +53,20 @@ The `PouchDB` constructor returns a `Database` object.
#### Importing Data
`Database#bulkDocs`[^2] is the standard approach for bulk data import. The method
accepts "arrays of objects" that can be generated using the SheetJS
[`sheet_to_json`](/docs/api/utilities/array#array-output) method.
accepts "arrays of objects" that can be generated through the SheetJS
`sheet_to_json`[^3] method.
If rows do not include the `_id` parameter, the database will automatically
assign an ID per row. It is strongly recommended to generate the `_id` directly.
This method starts from a SheetJS [worksheet object](/docs/csf/sheet) and uses
data from the first sheet. [`read` and `readFile`](/docs/api/parse-options) can
generate workbook objects from files.
This method starts from a SheetJS workbook object[^4] and uses data from the
first sheet. `read` and `readFile`[^5] can generate workbook objects from files.
```js
async function push_first_sheet_to_pouchdb(db, wb, _id_) {
/* get first worksheet */
const ws = wb.Sheets[wb.SheetNames[0]];
```js title="Extract data from a SheetJS worksheet and push to PouchDB"
async function push_sheet_to_pouchdb(db, ws, _id_) {
/* generate array of objects */
const aoo = XLSX.utils.sheet_to_json(ws);
@ -84,15 +86,14 @@ Existing data can be erased with `Database#destroy`.
#### Exporting Data
`Database#allDocs`[^3] is the standard approach for bulk data export. Generated
`Database#allDocs`[^6] is the standard approach for bulk data export. Generated
row objects have additional `_id` and `_rev` keys that should be removed.
The SheetJS [`json_to_sheet`](/docs/api/utilities/array#array-of-objects-input)
method can generate a worksheet. [API functions](/docs/api/utilities/wb) can
generate a workbook object. The [`writeFile`](/docs/api/write-options) method
will attempt to download the generated workbook:
After removing the PouchDB internal fields, the SheetJS `json_to_sheet`[^7]
method can generate a worksheet. Other utility functions[^8] can construct a
workbook. The workbook can be exported with the SheetJS `writeFile`[^9] method:
```js title="Extract data from PouchDB and export to XLSX using SheetJS"
```js
function export_pouchdb_to_xlsx(db) {
/* fetch all rows, including the underlying data */
db.allDocs({include_docs: true}, function(err, doc) {
@ -170,8 +171,8 @@ cd getting-started-todo-master
<script src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>
<button id="xport">Export!</button>
<!-- highlight-end -->
<section id="todoapp">
`}</CodeBlock>
<section id="todoapp">`}
</CodeBlock>
3) Near the end of `index.html`, look for a script tag referencing a CDN:
@ -179,7 +180,7 @@ cd getting-started-todo-master
<script src="//cdn.jsdelivr.net/pouchdb/3.2.0/pouchdb.min.js"></script>
```
Upgrade PouchDB by changing the `src` attribute to the production build[^4]:
Upgrade PouchDB by changing the `src` attribute to the production build[^10]:
```html title="index.html (replace line)"
<script src="//cdn.jsdelivr.net/npm/pouchdb@8.0.1/dist/pouchdb.min.js"></script>
@ -254,5 +255,11 @@ export named "SheetJSPouch.xlsx"
[^1]: See ["Setting up PouchDB"](https://pouchdb.com/guides/setup-pouchdb.html) in the PouchDB documentation.
[^2]: See ["Create/update a batch of documents"](https://pouchdb.com/api.html#batch_create) in the PouchDB API documentation
[^3]: See ["Fetch a batch of documents"](https://pouchdb.com/api.html#batch_fetch) in the PouchDB API documentation
[^4]: The ["Quick Start" section of "Download"](https://pouchdb.com/download.html#file) in the PouchDB website describes the recommended CDN for PouchDB scripts.
[^3]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
[^4]: See ["SheetJS Data Model"](/docs/csf)
[^5]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^6]: See ["Fetch a batch of documents"](https://pouchdb.com/api.html#batch_fetch) in the PouchDB API documentation
[^7]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
[^8]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
[^9]: See [`writeFile` in "Writing Files"](/docs/api/write-options)
[^10]: The ["Quick Start" section of "Download"](https://pouchdb.com/download.html#file) in the PouchDB website describes the recommended CDN for PouchDB scripts.

@ -291,7 +291,7 @@ following screenshot was taken in Chrome 126.0.6478.127:
This is a browser limitation and no pure JavaScript library can work around the
issue. See [Issue #3145](https://git.sheetjs.com/sheetjs/sheetjs/issues/3145) in
the SheetJS CE bug tracker for more details.
the SheetJS bug tracker for more details.
:::
@ -466,13 +466,13 @@ This browser demo was tested in the following environments:
| Browser | Date |
|:-------------|:-----------|
| Chromium 142 | 2025-11-15 |
| Chromium 137 | 2025-06-20 |
Some lesser-used browsers do not support File System Access API:
| Browser | Date |
|:-------------|:-----------|
| Safari 26.1 | 2025-11-15 |
| Safari 18.5 | 2025-06-20 |
| Konqueror 22 | 2025-04-23 |
| Firefox 139 | 2025-06-20 |
@ -836,4 +836,4 @@ Desktop and mobile apps have their own specific APIs covered in separate demos:
[^1]: See ["Input Type" in "Reading Files"](/docs/api/parse-options#input-type)
[^2]: See ["Supported Output Formats" type in "Writing Files"](/docs/api/write-options#supported-output-formats)
[^3]: See ["Buffers and TypedArrays"](https://nodejs.org/api/buffer.html#buffers-and-typedarrays) in the NodeJS documentation.
[^4]: See [issue 3145 in the SheetJS CE bug tracker](https://git.sheetjs.com/sheetjs/sheetjs/issues/3145#issuecomment-11074) for more details. Special thanks to `@sjoenH`!
[^4]: See [issue 3145 in the SheetJS bug tracker](https://git.sheetjs.com/sheetjs/sheetjs/issues/3145#issuecomment-11074) for more details. Special thanks to `@sjoenH`!

@ -8,7 +8,7 @@ pagination_next: demos/extensions/index
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
[Deno Deploy](https://deno.com/deploy) offers distributed "Serverless Functions"
[Deno Deploy](https://dash.deno.com/) offers distributed "Serverless Functions"
powered by Deno.
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
@ -22,7 +22,7 @@ types of spreadsheets to HTML tables and CSV rows.
:::caution pass
When the demo was last tested, Deno Deploy required a GitHub or Google account.
When the demo was last tested, Deno Deploy required a GitHub account.
:::
@ -82,9 +82,9 @@ class SheetJSResource extends Drash.Resource {
## Demo
0) Create a new Github or Google account, or sign into an existing account.
0) Create a new GitHub account or sign into an existing account.
1) Open the [main Deno Deploy portal](https://console.deno.com/) in a browser.
1) Open the [main Deno Deploy portal](https://dash.deno.com/) in a browser.
2) If the account never signed into Deno Deploy, click "Continue with Github".

@ -207,7 +207,7 @@ const wb = XLSX.readFile("SheetJSAirtableTest.xlsb");
:::note Tested Deployments
This demo was last tested on 2026-03-15. In the most recent test, free accounts
This demo was last tested on 2025-04-21. In the most recent test, free accounts
included limited API access.
:::

@ -1,11 +1,11 @@
---
title: Visualizing Data in VS Code
sidebar_label: Visual Studio Code
description: View Excel files directly in VS Code. Seamlessly browse spreadsheet data using SheetJS. Navigate between worksheets and pages of data with a responsive interface.
description: View Excel files directly in VS Code. Seamlessly browse spreadsheet data without leaving your editor using SheetJS. Navigate between worksheets and pages of data with a responsive
pagination_prev: demos/cloud/index
pagination_next: demos/bigdata/index
sidebar_custom_props:
summary: View Excel spreadsheets directly within Visual Studio Code
summary: View Excel files directly within Visual Studio Code
---
import current from '/version.js';
@ -21,14 +21,13 @@ that supports JavaScript extensions for customizing and enhancing functionality.
The ["Complete Example"](#complete-example) uses SheetJS in a VS Code extension
to view Excel files directly within the editor. The extension leverages the VS
Code "WebView API"[^1] and "Custom Editor API"[^2] to display spreadsheet data
Code "Custom Editor API"[^2] and "WebView API"[^1] to display spreadsheet data
as HTML tables.
:::tip pass
"SheetJS Spreadsheet Viewer" is a sample extension based on this demo. It is
available on [Open VSX](https://open-vsx.org/extension/asadbek/sheetjs-demo) and
[VSCode Marketplace](https://marketplace.visualstudio.com/items?itemName=asadbek.sheetjs-demo)
["SheetJS Spreadsheet Viewer"](https://marketplace.visualstudio.com/items?itemName=asadbek.sheetjs-demo)
is a sample extension based on this demo.
[The source code](https://git.sheetjs.com/asadbek064/sheetjs-vscode-extension)
is available on the SheetJS Git server. Feedback and contributions are welcome!
@ -39,217 +38,146 @@ is available on the SheetJS Git server. Feedback and contributions are welcome!
:::note Tested Deployments
This demo was tested in the following deployments:
This demo was verified in the following deployments:
| Platform | Architecture | Date |
|:--------------------------------------------------|:-------------|:-----------|
| [VSCodium 1.109.51242](https://vscodium.com/) | `darwin-arm` | 2026-03-05 |
| [VS Code 1.110.0](https://code.visualstudio.com/) | `win11-arm` | 2026-03-05 |
| [Antigravity 1.19.6](https://antigravity.google/) | `linux-arm` | 2026-03-05 |
:::
:::danger Telemetry and Data Exfiltration
VSCode and many forks embed telemetry and send code to third-party servers.
For example, Antigravity includes AI features that are powered by Google Gemini
and other cloud AI services. These features necessitate code exfiltration.
**[VSCodium](https://vscodium.com/) does not include AI features or telemetry!**
| Platform | Architecture | Date |
|:-----------------|:-------------|:-----------|
| VS Code 1.100.0 | `darwin-arm` | 2025-05-15 | TODO
| VSCodium 1.100.0 | `darwin-arm` | 2025-05-15 | TODO
| Cursor | `win11-arm` | 2025-05-15 | TODO
| Windsurf | `win11-arm` | 2025-05-15 | TODO
| Void | `win11-arm` | 2025-05-15 | TODO
:::
## Integration Details
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
imported from any script in the extension.
imported from any component or script in the extension.
:::caution pass
The SheetJS NodeJS module must be installed as a development dependency. If the
module is installed as a normal dependency, the `vsce`[^3] command-line tool
will fail to package or publish the extension.
The module must be installed as a development dependency. If the module is
installed as a normal dependency, `vsce`[^5] (Visual Studio Code Extension
Manager) will fail to package or publish your extension correctly.
<Tabs groupId="pm">
<TabItem value="npm" label="npm">
<CodeBlock language="bash">{`\
<Tabs groupId="pm">
<TabItem value="npm" label="npm">
<CodeBlock language="bash">{`\
npm rm --save xlsx
npm i --save-dev https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
</CodeBlock>
</TabItem>
<TabItem value="pnpm" label="pnpm">
<CodeBlock language="bash">{`\
</CodeBlock>
</TabItem>
<TabItem value="pnpm" label="pnpm">
<CodeBlock language="bash">{`\
pnpm rm xlsx
pnpm install -D https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
</CodeBlock>
</TabItem>
<TabItem value="yarn" label="Yarn" default>
<CodeBlock language="bash">{`\
</CodeBlock>
</TabItem>
<TabItem value="yarn" label="Yarn" default>
<CodeBlock language="bash">{`\
yarn remove xlsx
yarn add -D https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
</CodeBlock>
</CodeBlock>
</TabItem>
</Tabs>
</Tabs>
:::
The core extension APIs are available in the `vscode` package. A TypeScript
extension typically uses "glob imports" to load SheetJS and VSCode features:
```ts title="Importing SheetJS and VSCode features"
import * as vscode from 'vscode';
import * as XLSX from 'xlsx';
```
## Extension Architecture
VSCode Extensions for processing custom file types use the Custom Editor API[^2]
for lifecycle events. This involves a number of moving parts:
The VS Code Spreadsheet viewer extension has three main components:
1) "Custom Document" for managing file metadata
- **Extension Entry Point:** Registers the extension with VS Code
- **Custom Editor Provider:** Handles Excel files and converts them to web content
- **WebView Content:** Displays Excel data as HTML tables
2) "Custom Editor Provider" for processing file data and generating previews
The extension uses VS Code's `Custom Editor API`[^2] to register as a handler for Excel files. When a file is opened,
SheetJS parses it and displays the data in a WebView component.
3) Registration during extension lifecycle events.
### Extension Entry Point
4) Advertisement of filetype support in extension metadata.
The main entry point registers the custom editor provider:
When a spreadsheet is opened, the extension will use SheetJS methods to parse
the raw file and display the data in a HTML table.
```ts title="src/extension.ts"
import * as vscode from 'vscode';
// highlight-start
import { ExcelEditorProvider } from './excelEditorProvider';
// highlight-end
### Custom Documents
Extensions must provide a class that implements `vscode.CustomDocument`[^4].
```ts title="Simple CustomDocument"
class ExcelDocument implements vscode.CustomDocument {
constructor(public readonly uri: vscode.Uri) { }
dispose() { }
export function activate(context: vscode.ExtensionContext) {
// SheetJS Spreadsheet Viewer extension activating...
// highlight-start
const provider = ExcelEditorProvider.register(context);
context.subscriptions.push(provider);
// highlight-end
}
export function deactivate() {}`}
```
### Editor Provider
The `custom editor`[^3] is configured to support specific file types, giving us complete control over how each file is
presented to the user. Additionally, `custom document`[^4] enables us to maintain and persist the state of each individual
file that's opened.
Extensions that read data should implement `vscode.CustomReadonlyEditorProvider`
with a generic parameter for the custom document type.
<CodeBlock language="typescript" value="typescript" title="src/excelEditorProvider.ts">
{`import * as vscode from 'vscode';
// highlight-start
import * as XLSX from 'xlsx';
import { ExcelDocument } from './excelDocument';
// highlight-end
The `openCustomDocument` method takes a `vscode.Uri` and is expected to return a
new document:
```ts title="src/extension.ts (snippet)"
class ExcelEditorProvider implements vscode.CustomReadonlyEditorProvider<ExcelDocument> {
async openCustomDocument(uri: vscode.Uri): Promise<ExcelDocument> {
return new ExcelDocument(uri);
// A simple class to store document state (one per opened file)
class ExcelDocument implements vscode.CustomDocument {
constructor(public readonly uri: vscode.Uri) {}
dispose() {}
}
}
```
export class ExcelEditorProvider implements vscode.CustomReadonlyEditorProvider<ExcelDocument> {
// ...
public static register(context: vscode.ExtensionContext): vscode.Disposable {
return vscode.window.registerCustomEditorProvider(
'excelViewer.spreadsheet',
new ExcelEditorProvider(),
{ webviewOptions: { retainContextWhenHidden: true } } // keep webview state when hidden
);
}
// ...
}`}
</CodeBlock>
### Reading Files
The `FileSystemProvider` API[^5], available at `vscode.workspace.fs`, exposes
common filesystem operations including reading raw data from files and watching
for changes.
The extension reads Excel files using the VS Code filesystem API and passes
the data to SheetJS for parsing:
The `resolveCustomEditor`[^6] method of the `CustomEditorProvider` will be
called when a file is opened. The first argument is a `CustomDocument` whose
`uri` property points to the location of the file.
`vscode.workspace.fs.readFile`[^5] returns a promise that resolves to a
`Uint8Array` containing the raw binary data. This `Uint8Array` can be passed to
the SheetJS [`read`](/docs/api/parse-options) method:
```ts title="src/extension.ts (snippet)"
export class ExcelEditorProvider implements vscode.CustomReadonlyEditorProvider<ExcelDocument> {
// ...
async resolveCustomEditor(document: ExcelDocument, webviewPanel: vscode.WebviewPanel): Promise<void> {
// read the raw bytes from the file
const data: Uint8Array = await vscode.workspace.fs.readFile(document.uri);
// parse the file data
const wb: XLSX.WorkBook = XLSX.read(data);
// At this point, `wb` is a SheetJS Workbook Object
<CodeBlock language="typescript" value="typescript" title="src/excelEditorProvider.ts">
{`export class ExcelEditorProvider implements vscode.CustomReadonlyEditorProvider<ExcelDocument> {
// ...
}
// ...
}
```
private async loadWorkbook(document: ExcelDocument, webviewPanel: vscode.WebviewPanel): Promise<XLSX.WorkBook> {
const data: Uint8Array = await vscode.workspace.fs.readFile(document.uri);
### Previewing Data
const options: XLSX.ParsingOptions = {
type: 'array',
cellStyles: true,
cellDates: true,
};
The `resolveCustomEditor`[^6] method of the `CustomEditorProvider` will be
called when a file is opened. The second argument is a `WebviewPanel`[^7].
return XLSX.read(new Uint8Array(data), options); // returns a XLSX.WorkBook
}
The `webview.html` nested property of the `WebviewPanel` controls the displayed
HTML. Extensions can use SheetJS [API methods](/docs/api/).
// This is called when the first time an editor for a given resource is opened
async openCustomDocument(uri: vscode.Uri): Promise<ExcelDocument> {
return new ExcelDocument(uri);
}
The SheetJS [`sheet_to_html`](/docs/api/utilities/html#html-table-output) method
generates a simple HTML table. The following snippet displays the data of the
first worksheet:
```ts title="src/extension.ts (snippet)"
export class ExcelEditorProvider implements vscode.CustomReadonlyEditorProvider<ExcelDocument> {
// ...
async resolveCustomEditor(document: ExcelDocument, webviewPanel: vscode.WebviewPanel): Promise<void> {
// ...
// continuing from "Reading Files", `wb` is a SheetJS Workbook Object
const first_sheet = wb.Sheets[wb.SheetNames[0]];
webviewPanel.webview.html = XLSX.utils.sheet_to_html(first_sheet);
}
// ...
}
```
### Registration
The exported `activate` method registers the editor provider. The first argument
to `registerCustomEditorProvider` is expected to be a unique name that will be
referenced later.
```ts title="src/extension.ts (snippet)"
export function activate(context: vscode.ExtensionContext) {
const provider = vscode.window.registerCustomEditorProvider(
'excelViewer.spreadsheet',
new ExcelEditorProvider(),
{ webviewOptions: { retainContextWhenHidden: true } } // keep webview state when hidden
);
context.subscriptions.push(provider);
}
```
### Filetype Support
Extensions must announce custom editor file type support in `package.json`.
The `selector` property[^8] is expected to include "Glob Pattterns"[^9].
The demo uses the following `package.json` snippet to announce support for `xls`
and `xlsx` spreadsheets:
```json title="package.json (snippet)"
"contributes": {
"customEditors": [
{
// ... other properties including displayName
// highlight-start
// The viewType must match the first argument of `registerCustomEditorProvider`
"viewType": "excelViewer.spreadsheet",
"selector": [
{ "filenamePattern": "*.xlsx" },
{ "filenamePattern": "*.xls" }
]
// highlight-end
}
],
// ...
},
```
// This is called whenever the user opens a new editor
async resolveCustomEditor(document: ExcelDocument, webviewPanel: vscode.WebviewPanel): Promise<void> {
const wb: XLSX.WorkBook = await this.loadWorkbook(document, webviewPanel);
const htmlTable = XLSX.utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]);
webviewPanel.webview.html = \`<!DOCTYPE html><html><body>\${htmlTable}</body></html>\`;
}
}`}
</CodeBlock>
### Usage Flow
@ -277,17 +205,7 @@ sequenceDiagram
## Complete Example
:::caution pass
To avoid conflicts with existing extensions, it is strongly recommended to test
with a different VSCode fork. For example, VSCode and Antigravity users should
install and use VSCodium for extension development.
:::
1) Download the [`pres.xlsx`](https://docs.sheetjs.com/pres.xlsx) sample file.
2) Create a new VS Code extension
1) Create a new VS Code extension
```bash
npx --package yo --package generator-code -- yo code
@ -297,15 +215,17 @@ When prompted, enter the following options:
- `What type of extension do you want to create?`: Select `New Extension (TypeScript)` and press <kbd>Enter</kbd>
- `What's the name of your extension?`: Type `sheetjs-demo` and press <kbd>Enter</kbd>
- `What's the identifier of your extension?`: Press <kbd>Enter</kbd> (use the default `sheetjs-demo`)
- `What's the description of your extension?`: Press <kbd>Enter</kbd> (leave blank)
- `What's the identifier of your extension?`: Press <kbd>Enter</kbd>
- `What's the description of your extension?`: Press <kbd>Enter</kbd>
- `Initialize a git repository?`: Type `n` and press <kbd>Enter</kbd>
- `Which bundler to use?`: Select `webpack` and press <kbd>Enter</kbd>
- `Which package manager to use?`: Select `pnpm` and press <kbd>Enter</kbd>
![Expected output](pathname:///vscode/yeo-code.png)
3) Install the SheetJS library and start the dev server:
- `Do you want to open the new folder with Visual Studio Code?`: Press <kbd>Enter</kbd>
2) [Install the dependencies](#integration-details) and start the dev server:
<CodeBlock language="bash">{`\
cd sheetjs-demo
@ -314,11 +234,9 @@ pnpm run watch
`}
</CodeBlock>
4) Launch a new window with the VSCode fork and open the `sheetjs-demo` folder.
3) Save the following code snippet to `src/excelEditorProvider.ts`:
5) Save the following codeblock to `src/extension.ts`:
```ts title="src/extension.ts (replace contents)"
<CodeBlock language="typescript" value="typescript" title="src/excelEditorProvider.ts">{`\
import * as vscode from 'vscode';
import * as XLSX from 'xlsx';
@ -327,7 +245,27 @@ class ExcelDocument implements vscode.CustomDocument {
dispose() { }
}
class ExcelEditorProvider implements vscode.CustomReadonlyEditorProvider<ExcelDocument> {
export class ExcelEditorProvider implements vscode.CustomReadonlyEditorProvider<ExcelDocument> {
public static register(context: vscode.ExtensionContext): vscode.Disposable {
return vscode.window.registerCustomEditorProvider(
'excelViewer.spreadsheet',
new ExcelEditorProvider(),
{ webviewOptions: { retainContextWhenHidden: true } } // keep webview state when hidden
);
}
private async loadWorkbook(document: ExcelDocument, webviewPanel: vscode.WebviewPanel): Promise<XLSX.WorkBook> {
const data: Uint8Array = await vscode.workspace.fs.readFile(document.uri);
const options: XLSX.ParsingOptions = {
type: 'array',
cellStyles: true,
cellDates: true,
};
return XLSX.read(new Uint8Array(data), options); // returns a XLSX.WorkBook
}
// This is called when the first time an editor for a given resource is opened
async openCustomDocument(uri: vscode.Uri): Promise<ExcelDocument> {
return new ExcelDocument(uri);
@ -335,30 +273,31 @@ class ExcelEditorProvider implements vscode.CustomReadonlyEditorProvider<ExcelDo
// This is called whenever the user opens a new editor
async resolveCustomEditor(document: ExcelDocument, webviewPanel: vscode.WebviewPanel): Promise<void> {
const data: Uint8Array = await vscode.workspace.fs.readFile(document.uri);
const options: XLSX.ParsingOptions = { cellStyles: true, cellDates: true };
const wb: XLSX.WorkBook = XLSX.read(data, options);
const first_sheet = wb.Sheets[wb.SheetNames[0]];
webviewPanel.webview.html = XLSX.utils.sheet_to_html(first_sheet);
const wb: XLSX.WorkBook = await this.loadWorkbook(document, webviewPanel);
const htmlTable = XLSX.utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]);
webviewPanel.webview.html = \`<!DOCTYPE html><html><body>\${htmlTable}</body></html>\`;
}
}
}`}
</CodeBlock>
4) Register the custom editor provider in `src/extension.ts`:
<CodeBlock language="typescript" value="typescript" title="src/extension.ts (replace contents)">{`\
import * as vscode from 'vscode';
import { ExcelEditorProvider } from './excelEditorProvider';
export function activate(context: vscode.ExtensionContext) {
const provider = vscode.window.registerCustomEditorProvider(
'excelViewer.spreadsheet',
new ExcelEditorProvider(),
{ webviewOptions: { retainContextWhenHidden: true } } // keep webview state when hidden
);
// SheetJS Spreadsheet Viewer extension activating...
const provider = ExcelEditorProvider.register(context);
context.subscriptions.push(provider);
}
export function deactivate() {}
```
export function deactivate() {}`}
</CodeBlock>
6) Add the highlighted lines to the `contributes` section of `package.json`:
5) Register the custom editor in the `contributes` section of `package.json`:
```json title="package.json (add highlighted lines)"
<CodeBlock language="json" value="json" title="package.json (add highlighted lines)">{`\
"main": "./dist/extension.js",
"contributes": {
// highlight-start
"customEditors": [
@ -379,42 +318,19 @@ export function deactivate() {}
}
]
},
```
`}
</CodeBlock>
7) In the editor, open the Command Palette (Help > "Show All Commands" from the
menu), type `Debug: Start` and select `Debug: Start Debugging`.
6. Inside the editor, open `src/extension.ts` and press <kbd>F5</kbd> or run the command **Debug: Start Debugging**
from the Command Palette (<kbd>⇧⌘P</kbd>). This will compile and run the extension in a new Extension Development Host window.
This will compile and run the extension in a new Extension Development Host window.
7. Select the new VSCode Window and open a `.xlsx` or `.xls` file.
8) Drag and drop the `pres.xlsx` test file into the new window. If drag and drop
is not available, click "Open..." in the Welcome screen and select the file.
A new `pres.xlsx` tab will show the contents of the file.
---
:::info pass
When this demo was last tested, the default project assumed VSCode version
1.109.0 or later. Antigravity 1.19.6 is aligned to VSCode 1.107.0. The default
extension will not run in Antigravity.
This can be fixed by changing the `vscode` field in `package.json`. When this
demo was last tested, it was safe to set a minimum version of `^1.100.0`:
```json title="package.json (change highlighted line)"
"engines": {
// highlight-next-line
"vscode": "^1.100.0"
}
```
:::
[^1]: See [`Webview API`](https://code.visualstudio.com/api/extension-guides/webview) in the VSCode documentation for more details.
[^2]: See [`Custom Editor API`](https://code.visualstudio.com/api/extension-guides/custom-editors) in the VSCode documentation for more details.
[^3]: See [`vsce`](https://code.visualstudio.com/api/working-with-extensions/publishing-extension#vsce) in the VSCode documentation for more details.
[^4]: See [`CustomDocument`](https://code.visualstudio.com/api/references/vscode-api#CustomDocument) in the VSCode API documentation for more details.
[^5]: See [`FileSystemProvider`](https://code.visualstudio.com/api/references/vscode-api#FileSystemProvider) in the VSCode API documentation for more details.
[^6]: See [`CustomEditorProvider<T>`](https://code.visualstudio.com/api/references/vscode-api#CustomEditorProvider&lt;T&gt;) in the VSCode API documentation for more details.
[^7]: See [`WebviewPanel`](https://code.visualstudio.com/api/references/vscode-api#WebviewPanel) in the VSCode API documentation for more details.
[^8]: See [`contributes.customEditors`](https://code.visualstudio.com/api/references/contribution-points#contributes.customEditors) in the VSCode API documentation for more details.
[^9]: See ["Glob Patterns Reference"](https://code.visualstudio.com/docs/editor/glob-patterns) in the VSCode documentation for more details.
[^1]: See [`Webview API`](https://code.visualstudio.com/api/extension-guides/webview) for more details.
[^2]: See [`Custom Editor API`](https://code.visualstudio.com/api/extension-guides/custom-editors) documentation for more details.
[^3]: See [`Custom Editor`](https://code.visualstudio.com/api/extension-guides/custom-editors#custom-editor) for more details.
[^4]: See [`CustomDocument`](https://code.visualstudio.com/api/extension-guides/custom-editors#customdocument) for more details.
[^5]: See [`Visual Studio Code Extension Manager`](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) for more details.

@ -54,7 +54,7 @@ This demo was tested in the following deployments:
| Architecture | Version | Date |
|:-------------|:------------------|:-----------|
| `darwin-x64` | `18.5` (StataNow) | 2025-06-20 |
| `darwin-arm` | `19.5` (StataNow) | 2026-03-13 |
| `darwin-arm` | `18.5` (StataNow) | 2025-04-24 |
| `win11-x64` | `18.5` (StataNow) | 2025-04-28 |
| `win11-arm` | `18.5` (StataNow) | 2025-02-23 |
| `linux-x64` | `19.5` (StataNow) | 2025-07-06 |
@ -299,7 +299,7 @@ curl -LO https://docs.sheetjs.com/stata/cleanfile.c
7) Build the plugin:
```bash title="Build plugin (run in terminal)"
gcc -shared -fPIC -DSYSTEM=APPLEMAC -fms-extensions stplugin.c duktape.c cleanfile.c -lm -std=c99 -Wall -o cleanfile.plugin
gcc -shared -fPIC -DSYSTEM=APPLEMAC stplugin.c duktape.c cleanfile.c -lm -std=c99 -Wall -ocleanfile.plugin
```
</TabItem>

@ -28,7 +28,7 @@ This demo was tested by SheetJS users in the following deployments:
| Architecture | Ghidra | Date |
|:-------------|:----------|:-----------|
| `darwin-x64` | `11.13.1` | 2025-04-17 |
| `darwin-arm` | `11.13.1` | 2025-10-19 |
| `darwin-arm` | `11.13.1` | 2025-03-17 |
:::
@ -255,18 +255,30 @@ Rows will be generated for each block and the final dataset will be exported.
### System Setup
0) Install Java, Ghidra, Xcode, and Apple Numbers.
0) Install Ghidra, Xcode, and Apple Numbers.
<details>
<summary><b>Installation Notes</b> (click to show)</summary>
When this demo was last tested, Java and Ghidra were installed using Homebrew:
On macOS, Ghidra was installed using Homebrew:
```bash
brew install zulu@21
brew install --cask ghidra
```
:::note pass
Ghidra requires Java to run. If Ghidra returns `JDK 21+ (64-bit) could not be found and must be manually chosen!`:
```bash
brew install zulu@21
```
:::
</details>
1) Add the base Ghidra folder to the PATH variable. The following shell command

@ -180,25 +180,25 @@ be reported to the Bun project for further diagnosis.
This demo was tested in the following deployments:
| Platform | Date | Status when tested |
|:------------------|:-----------|:-------------------|
| NodeJS `0.12.18` | 2025-11-15 | End-of-Life |
| NodeJS `4.9.1` | 2025-11-15 | End-of-Life |
| NodeJS `6.17.1` | 2025-11-15 | End-of-Life |
| NodeJS `8.17.0` | 2025-11-15 | End-of-Life |
| NodeJS `10.24.1` | 2025-11-15 | End-of-Life |
| NodeJS `12.22.12` | 2025-11-15 | End-of-Life |
| NodeJS `14.21.3` | 2025-11-15 | End-of-Life |
| NodeJS `16.20.2` | 2025-11-15 | End-of-Life |
| NodeJS `18.20.8` | 2025-11-15 | End-of-Life |
| NodeJS `20.18.0` | 2025-11-15 | Maintenance LTS |
| NodeJS `22.21.1` | 2025-11-15 | Active LTS |
| NodeJS `24.11.1` | 2025-11-15 | Current |
| BunJS `1.3.2` | 2025-11-15 | Current |
| Node Version | Date | Node Status when tested |
|:-------------|:-----------|:------------------------|
| `0.12.18` | 2025-04-24 | End-of-Life |
| `4.9.1` | 2025-04-24 | End-of-Life |
| `6.17.1` | 2025-04-24 | End-of-Life |
| `8.17.0` | 2025-04-24 | End-of-Life |
| `10.24.1` | 2025-04-24 | End-of-Life |
| `12.22.12` | 2025-04-24 | End-of-Life |
| `14.15.5` | 2025-04-24 | End-of-Life |
| `16.20.2` | 2025-04-24 | End-of-Life |
| `18.20.8` | 2025-04-24 | Maintenance LTS |
| `20.18.0` | 2025-04-24 | Active LTS |
| `22.14.0` | 2025-04-24 | Current |
While streaming methods work in End-of-Life versions of NodeJS, production
deployments should upgrade to a Current or LTS version of NodeJS.
This demo was also tested against BunJS `1.2.10` on 2025-04-24.
:::
1) Install the [NodeJS module](/docs/getting-started/installation/nodejs)
@ -399,11 +399,11 @@ includes a live example of CSV streaming write.
The demo has a URL input box. Feel free to change the URL. For example,
`https://test-files.sheetjs.com/large_strings.xls` is a large XLS file with many
large strings (approximately 56 MB).
`https://raw.githubusercontent.com/SheetJS/test_files/master/large_strings.xls`
is an XLS file over 50 MB
`https://test-files.sheetjs.com/8-by-300000-cells.xlsx` is an XLSX file with
300000 rows (approximately 20 MB).
`https://raw.githubusercontent.com/SheetJS/libreoffice_test-files/master/calc/xlsx-import/perf/8-by-300000-cells.xlsx`
is an XLSX file with 300000 rows (approximately 20 MB)
<CodeBlock language="jsx" live>{`\
function SheetJSFetchCSVStreamWorker() {

@ -576,10 +576,10 @@ will try to commit each row as it is generated.
The demo also has a URL input box. Feel free to change the URL. For example:
`https://test-files.sheetjs.com/large_strings.xls` is approximately 56 MB. The
equivalent CSV is about 55 MB.
`https://raw.githubusercontent.com/SheetJS/test_files/master/large_strings.xls`
is an XLS file over 50 MB. The generated CSV file is about 55 MB.
`https://test-files.sheetjs.com/8-by-300000-cells.xlsx`
`https://raw.githubusercontent.com/SheetJS/libreoffice_test-files/master/calc/xlsx-import/perf/8-by-300000-cells.xlsx`
is an XLSX file with 300000 rows (approximately 20 MB) yielding a CSV of 10 MB.
<CodeBlock language="jsx" live>{`\

@ -128,12 +128,12 @@ This demo was tested in the following deployments:
| Architecture | Version | Date |
|:-------------|:--------|:-----------|
| `darwin-x64` | `2.7.0` | 2026-01-21 |
| `darwin-arm` | `2.7.0` | 2026-03-07 |
| `darwin-x64` | `2.7.0` | 2025-03-31 |
| `darwin-arm` | `2.7.0` | 2025-09-03 |
| `win11-x64` | `2.7.0` | 2025-04-28 |
| `win11-arm` | `2.7.0` | 2025-02-23 |
| `linux-x64` | `2.7.0` | 2025-04-21 |
| `linux-arm` | `2.7.0` | 2026-03-07 |
| `linux-arm` | `2.7.0` | 2025-02-15 |
:::
@ -145,8 +145,7 @@ The [flow diagram is displayed after the example steps](#flow-diagram)
:::note pass
The Windows build requires Visual Studio with "Desktop development with C++".
**Commands must be run in a "Native Tools Command Prompt" session.**
Commands must be run in a "Native Tools Command Prompt" session.
:::
@ -226,7 +225,7 @@ curl -LO https://docs.sheetjs.com/duk/sheetjs.duk.c
<TabItem value="unix" label="Linux/MacOS">
```bash
gcc -std=c99 -Wall -o sheetjs.duk sheetjs.duk.c duktape.c -lm
gcc -std=c99 -Wall -osheetjs.duk sheetjs.duk.c duktape.c -lm
```
:::note pass
@ -406,10 +405,10 @@ This demo was tested in the following deployments:
| Architecture | Version | PHP | Date |
|:-------------|:--------|:---------|:-----------|
| `darwin-x64` | `2.7.0` | `8.4.11` | 2026-01-21 |
| `darwin-arm` | `2.7.0` | `8.4.8` | 2026-01-23 |
| `darwin-x64` | `2.7.0` | `8.4.2` | 2025-03-31 |
| `darwin-arm` | `2.7.0` | `8.4.4` | 2025-02-25 |
| `linux-x64` | `2.7.0` | `8.3.6` | 2025-04-21 |
| `linux-arm` | `2.7.0` | `8.4.16` | 2026-03-07 |
| `linux-arm` | `2.7.0` | `8.2.26` | 2025-02-15 |
:::
@ -548,12 +547,10 @@ This demo was tested in the following deployments:
| Architecture | Version | Python | Date |
|:-------------|:--------|:---------|:-----------|
| `darwin-x64` | `2.7.0` | `3.13.7` | 2026-01-21 |
| `darwin-arm` | `2.7.0` | `3.12.3` | 2026-01-23 |
| `win11-x64` | `2.7.0` | `3.11.9` | 2026-02-02 |
| `win11-arm` | `2.7.0` | `3.11.9` | 2026-02-02 |
| `darwin-x64` | `2.7.0` | `3.13.1` | 2025-03-31 |
| `darwin-arm` | `2.7.0` | `3.12.3` | 2025-03-30 |
| `linux-x64` | `2.7.0` | `3.12.3` | 2025-04-21 |
| `linux-arm` | `2.7.0` | `3.13.5` | 2026-03-07 |
| `linux-arm` | `2.7.0` | `3.11.2` | 2025-02-15 |
:::
@ -561,9 +558,6 @@ This demo was tested in the following deployments:
1) Build the Duktape shared library:
<Tabs groupId="triple">
<TabItem value="darwin-x64" label="MacOS">
```bash
curl -LO https://duktape.org/duktape-2.7.0.tar.xz
tar -xJf duktape-2.7.0.tar.xz
@ -572,92 +566,18 @@ make -f Makefile.sharedlibrary
cd ..
```
</TabItem>
<TabItem value="linux-x64" label="Linux">
```bash
curl -LO https://duktape.org/duktape-2.7.0.tar.xz
tar -xJf duktape-2.7.0.tar.xz
cd duktape-2.7.0
make -f Makefile.sharedlibrary
cd ..
```
</TabItem>
<TabItem value="win11-x64" label="Windows">
- Download and extract the source tarball:
```bash
curl -LO https://duktape.org/duktape-2.7.0.tar.xz
tar -xJf duktape-2.7.0.tar.xz
```
- Enter the source folder:
```bash
cd duktape-2.7.0
```
- Edit `src\duk_config.h` and add the highlighted lines to the end of the file:
```c title="src\duk_config.h (add highlighted lines to end of file)"
#endif /* DUK_CONFIG_H_INCLUDED */
// highlight-start
#define DUK_EXTERNAL_DECL extern __declspec(dllexport)
#define DUK_EXTERNAL __declspec(dllexport)
// highlight-end
```
- Build the Duktape DLL:
```cmd
cl /O2 /W3 /Isrc /LD /DDUK_SINGLE_FILE /DDUK_F_DLL_BUILD /DDUK_F_WINDOWS /DDUK_COMPILING_DUKTAPE src\\duktape.c
```
- Move up to the parent directory:
```bash
cd ..
```
</TabItem>
</Tabs>
2) Copy the shared library to the current folder. When the demo was last tested,
the shared library file name differed by platform:
| OS | name |
|:--------|:--------------------------|
| Darwin | `libduktape.207.20700.so` |
| Linux | `libduktape.so.207.20700` |
| Windows | `duktape.dll` |
<Tabs groupId="triple">
<TabItem value="darwin-x64" label="MacOS">
| OS | name |
|:-------|:--------------------------|
| Darwin | `libduktape.207.20700.so` |
| Linux | `libduktape.so.207.20700` |
```bash
cp duktape-*/libduktape.* .
```
</TabItem>
<TabItem value="linux-x64" label="Linux">
```bash
cp duktape-*/libduktape.* .
```
</TabItem>
<TabItem value="win11-x64" label="Windows">
```cmd
copy duktape-2.7.0\duktape.dll .
```
</TabItem>
</Tabs>
3) Download the SheetJS Standalone script, shim script and test file. Move all
three files to the project directory:
@ -710,38 +630,6 @@ The name of the library is `libduktape.so.207.20700`:
lib = "libduktape.so.207.20700"
```
</TabItem>
<TabItem value="win11-x64" label="Windows">
The name of the library is `duktape.dll`:
```python title="SheetJSDuk.py (change highlighted line)"
# highlight-next-line
lib = ".\\duktape.dll"
```
In addition, the following changes must be made:
- `str_to_c` must be defined as follows:
```python title="SheetJSDuk.py (replace str_to_c function)"
def str_to_c(s):
if type(s) == bytes:
b = s
else:
b = s.encode("utf8")
return [c_char_p(b), len(b)]
```
- `eval_file` must `open` with mode `rb`:
```python title="SheetJSDuk.py (edit highlighted line)"
def eval_file(ctx, path):
# highlight-next-line
with open(path, "rb") as f:
code = f.read()
```
</TabItem>
</Tabs>
@ -782,16 +670,6 @@ The name of the library is `libduktape.so.207.20700`:
```python title="SheetJSDuk.py (change highlighted line)"
# highlight-next-line
lib = "./libduktape.so.207.20700"
```
</TabItem>
<TabItem value="win11-x64" label="Windows">
The name of the library is `duktape.dll`:
```python title="SheetJSDuk.py (change highlighted line)"
# highlight-next-line
lib = ".\\duktape.dll"
```
</TabItem>
@ -906,12 +784,12 @@ This demo was tested in the following deployments:
| Architecture | Version | Zig | Date |
|:-------------|:--------|:---------|:-----------|
| `darwin-x64` | `2.7.0` | `0.15.2` | 2026-01-20 |
| `darwin-arm` | `2.7.0` | `0.15.2` | 2026-01-20 |
| `darwin-x64` | `2.7.0` | `0.14.0` | 2025-03-31 |
| `darwin-arm` | `2.7.0` | `0.13.0` | 2025-02-13 |
| `win11-x64` | `2.7.0` | `0.14.0` | 2025-04-28 |
| `win11-arm` | `2.7.0` | `0.13.0` | 2025-02-23 |
| `linux-x64` | `2.7.0` | `0.14.0` | 2025-04-21 |
| `linux-arm` | `2.7.0` | `0.15.2` | 2026-03-07 |
| `linux-arm` | `2.7.0` | `0.13.0` | 2025-02-15 |
On Windows, due to incompatibilities between WSL and PowerShell, some commands
must be run in WSL Bash.
@ -934,15 +812,15 @@ the project folder.
For X64 Mac:
```bash
curl -LO https://ziglang.org/download/0.15.2/zig-x86_64-macos-0.15.2.tar.xz
tar -xzf zig-*.tar.xz
curl -LO https://ziglang.org/download/0.14.0/zig-macos-x86_64-0.14.0.tar.xz
tar -xzf zig-macos-*.tar.xz
```
For ARM64 Mac:
```bash
curl -LO https://ziglang.org/download/0.15.2/zig-aarch64-macos-0.15.2.tar.xz
tar -xzf zig-*.tar.xz
curl -LO https://ziglang.org/download/0.13.0/zig-macos-aarch64-0.13.0.tar.xz
tar -xzf zig-macos-*.tar.xz
```
@ -960,9 +838,9 @@ tar -xf zig-linux-*.tar
For AArch64 Linux:
```bash
curl -LO https://ziglang.org/download/0.15.2/zig-aarch64-linux-0.15.2.tar.xz
xz -d zig-*.tar.xz
tar -xf zig-*.tar
curl -LO https://ziglang.org/download/0.13.0/zig-linux-aarch64-0.13.0.tar.xz
xz -d zig-linux-*.tar.xz
tar -xf zig-linux-*.tar
```
</TabItem>
@ -1139,10 +1017,10 @@ This demo was tested in the following deployments:
| Architecture | Version | Date |
|:-------------|:--------|:-----------|
| `darwin-x64` | `2.2.0` | 2026-01-21 |
| `darwin-arm` | `2.2.0` | 2026-03-07 |
| `darwin-x64` | `2.2.0` | 2025-03-31 |
| `darwin-arm` | `2.2.0` | 2025-03-30 |
| `linux-x64` | `2.2.0` | 2025-04-21 |
| `linux-arm` | `2.2.0` | 2026-03-07 |
| `linux-arm` | `2.2.0` | 2025-02-15 |
:::
@ -1234,39 +1112,11 @@ This demo was tested in the following deployments:
| Architecture | Version | Date |
|:-------------|:--------|:-----------|
| `darwin-x64` | `2.2.1` | 2026-01-21 |
| `darwin-arm` | `2.2.1` | 2026-03-07 |
| `win11-x64` | `2.2.1` | 2026-02-02 |
| `win11-arm` | `2.2.1` | 2026-02-02 |
| `linux-x64` | `2.2.1` | 2026-01-08 |
| `linux-arm` | `2.2.1` | 2026-03-07 |
:::
:::caution pass
The `ducc` crate cannot compile Duktape from source in Windows on ARM, so the
x64 Rust toolchain must be used through the X64 compatibility layer.
<details>
<summary><b>Windows on ARM steps</b> (click to show)</summary>
The following commands switch to the X64 toolchain:
```pwsh
rustup toolchain install stable-x86_64-pc-windows-msvc --force-non-host
rustup default stable-x86_64-pc-windows-msvc --force-non-host
```
---
The following command switches back to the native toolchain:
```pwsh
rustup default stable
```
</details>
| `darwin-x64` | `2.2.1` | 2025-03-31 |
| `darwin-arm` | `2.2.1` | 2025-03-31 |
| `win11-x64` | `2.2.1` | 2025-04-17 |
| `linux-x64` | `2.2.1` | 2025-04-18 |
| `linux-arm` | `2.2.1` | 2025-04-18 |
:::
@ -1331,7 +1181,7 @@ cargo add ducc base64
6) Build and run the app:
```bash
cargo run -- pres.numbers
cargo run pres.numbers
```
If the program succeeded, the CSV contents will be printed to console and the

@ -184,7 +184,7 @@ cd /usr/local/lib
:::note pass
If this step throws an error, run the following commands to fix permissions:
If this step throws a permission error, run the following commands:
```bash
sudo mkdir -p /usr/local/lib
@ -489,7 +489,7 @@ ninja -C out.gn/x64.release.sample v8_monolith
**This may not work in newer Python releases due to a breaking change!**
Python 3.13 removed the `pipes` module from the standard library[^7]. `v8gen.py`
Python 3.13 removed the `pipes` module from the standard library[^9]. `v8gen.py`
will fail on newer Python releases with the following traceback:
```
@ -1078,14 +1078,18 @@ may not work on every platform.
The `v8` crate[^6] provides binary builds and straightforward bindings. The Rust
code is similar to the C++ code.
Pulling data from an `ArrayBuffer` back into Rust involves an unsafe conversion
from a raw pointer and byte length to `Vec<u8>`:
Pulling data from an `ArrayBuffer` back into Rust involves an unsafe operation:
```rust title="Pull ArrayBuffer data from V8 to Rust vector of bytes"
fn pull_ab_to_vecu8(ab: v8::Local<v8::ArrayBuffer>) -> Vec<u8> {
```rust
/* assuming JS code returns an ArrayBuffer, copy result to a Vec<u8> */
fn eval_code_ab(scope: &mut v8::HandleScope, code: &str) -> Vec<u8> {
let source = v8::String::new(scope, code).unwrap();
let script = v8::Script::compile(scope, source, None).unwrap();
let result: v8::Local<v8::ArrayBuffer> = script.run(scope).unwrap().try_into().unwrap();
/* In C++, `Data` returns a pointer. Collecting data into Vec<u8> is unsafe */
unsafe { return std::slice::from_raw_parts_mut(
ab.data().unwrap().cast::<u8>().as_ptr(),
ab.byte_length()
result.data().unwrap().cast::<u8>().as_ptr(),
result.byte_length()
).to_vec(); }
}
```
@ -1096,12 +1100,11 @@ This demo was last tested in the following deployments:
| Architecture | V8 Crate | Date |
|:-------------|:----------|:-----------|
| `darwin-x64` | `146.3.0` | 2026-03-08 |
| `darwin-x64` | `136.0.0` | 2025-04-21 |
| `darwin-arm` | `134.3.0` | 2025-02-13 |
| `win11-x64` | `137.1.0` | 2025-05-11 |
| `win11-arm` | `145.0.0` | 2026-02-02 |
| `linux-x64` | `142.2.0` | 2026-01-08 |
| `linux-arm` | `146.3.0` | 2026-03-07 |
| `linux-x64` | `137.2.0` | 2025-06-16 |
| `linux-arm` | `134.4.0` | 2025-02-15 |
:::
@ -1174,12 +1177,13 @@ curl.exe -L -o src/main.rs https://docs.sheetjs.com/v8/main.rs
:::info pass
There were multiple breaking changes in the `v8` crate. This example was tested
against version `142.2.0`.
There was a breaking change in version `0.102.0` affecting `v8::Context::new`.
When targeting older versions of the crate, remove the second argument:
Older versions of this demo script were designed for older `v8` versions. They
are available in the source repo for the documentation. Please reach out to the
[SheetJS chat](https://sheetjs.com/chat) for more details.
```rust title="src/main.rs"
let context = v8::Context::new(handle_scope); // v8 <= 0.101.0
//let context = v8::Context::new(handle_scope, Default::default()); // v8 >= 0.102.0
```
:::
@ -1213,29 +1217,10 @@ This demo was last tested in the following deployments:
| Architecture | V8 Version | Javet | Java | Date |
|:-------------|:--------------|:--------|:----------|:-----------|
| `darwin-x64` | `13.2.152.16` | `4.1.1` | `24.0.1` | 2025-04-21 |
| `darwin-arm` | `13.2.152.16` | `4.1.1` | `17.0.14` | 2026-03-04 |
| `win11-x64` | `13.2.152.16` | `4.1.1` | `17.0.12` | 2026-03-04 |
| `win11-arm` | `13.2.152.16` | `4.1.1` | `25.0.2` | 2026-03-04 |
| `linux-x64` | `13.2.152.16` | `4.1.1` | `21.0.10` | 2026-02-04 |
| `linux-arm` | `13.2.152.16` | `4.1.1` | `17.0.18` | 2026-02-04 |
:::
:::caution pass
Javet does not provide a pre-built JAR for Windows on ARM. This demo was tested
using the X64 compatibility layer.
<details>
<summary><b>Windows on ARM steps</b> (click to show)</summary>
Windows on ARM defaults to ARM64 JRE/JDK. A proper x64 JDK must be installed and
`JAVA_HOME` must point to the x64 version.
[Direct downloads are available at `adoptium.net`](https://adoptium.net/temurin/releases/).
The Windows x64 JDK release should be downloaded and installed.
</details>
| `darwin-arm` | `13.2.152.16` | `4.1.1` | `17.0.14` | 2025-03-30 |
| `win11-x64` | `13.2.152.16` | `4.1.1` | `17.0.13` | 2025-05-11 |
| `linux-x64` | `13.2.152.16` | `4.1.1` | `21.0.6` | 2025-04-21 |
| `linux-arm` | `13.2.152.16` | `4.1.1` | `17.0.14` | 2025-02-16 |
:::
@ -1445,12 +1430,12 @@ This demo was last tested in the following deployments:
| Architecture | V8 Version | Date |
|:-------------|:--------------|:-----------|
| `darwin-x64` | `13.3.415.23` | 2026-03-06 |
| `darwin-arm` | `13.3.415.23` | 2026-03-06 |
| `darwin-x64` | `13.3.415.23` | 2025-03-31 |
| `darwin-arm` | `13.3.415.23` | 2025-03-31 |
| `win11-x64` | `13.3.415.23` | 2025-05-11 |
| `win11-arm` | `12.3.219.12` | 2025-02-23 |
| `linux-x64` | `12.3.219.12` | 2026-03-06 |
| `linux-arm` | `13.3.415.23` | 2026-03-07 |
| `linux-x64` | `12.3.219.12` | 2025-06-16 |
| `linux-arm` | `12.3.219.12` | 2025-02-16 |
:::
@ -1598,15 +1583,16 @@ opened in a spreadsheet editor.
### Python
The [`mini-racer`](https://bpcreech.com/PyMiniRacer/) V8 wrapper package has
precompiled wheels for all major platforms.
[`pyv8`](https://code.google.com/archive/p/pyv8/) is a Python wrapper for V8.
The `stpyv8` package[^7] is an actively-maintained fork with binary wheels.
:::caution pass
When this demo was last tested, there was no direct conversion between Python
`bytes` and JavaScript `ArrayBuffer` data.
This is a known issue. The current recommendation is Base64 strings.
This is a known issue[^8]. The current recommendation is Base64 strings.
:::
@ -1617,26 +1603,25 @@ the `base64` type[^5].
_Reading Files_
The file data can be read in Python and encoded as Base64. The `set_object_item`
method on the internal context can assign to properties of the global object.
`mini-racer` does not expose a dedicated API to reference the global object.
The standard approach is to evaluate `this` in the JavaScript engine:
It is recommended to create a global context with a special method that handles
file reading from Python. The `read_file` helper in the following snippet will
read bytes from `sheetjs.xlsx` and generate a Base64 string:
```py
from base64 import b64encode;
from py_mini_racer import MiniRacer;
from STPyV8 import JSContext, JSClass;
with open("sheetjs.xlsx", "rb") as f:
file_data = b64encode(f.read()).decode("ascii");
# Create context with methods for file i/o
class Base64Context(JSClass):
def read_file(self, path):
with open(path, "rb") as f:
data = f.read();
return b64encode(data).decode("ascii");
globals = Base64Context();
# Create context and obtain a handle to `this`
ctx = MiniRacer();
global_scope = ctx.eval("this");
# assign to the `fileData` global property and parse
ctx._ctx.set_object_item(global_scope, "fileData", file_data);
ctx.eval("var wb = XLSX.read(fileData, {type:'base64'});");
# The JSContext starts and cleans up the V8 engine
with JSContext(globals) as ctxt:
print(ctxt.eval("read_file('sheetjs.xlsx')")); # read base64 data and print
```
_Writing Files_
@ -1646,14 +1631,14 @@ decoded and written to file from Python:
```py
from base64 import b64decode;
from py_mini_racer import MiniRacer;
from STPyV8 import JSContext;
ctx = MiniRacer();
# ... initialization and workbook creation ...
xlsb = ctx.eval("XLSX.write(wb, {type: 'base64', bookType: 'xlsb'})");
with open("SheetJSMiniRacer.xlsb", "wb") as f:
f.write(b64decode(xlsb));
ctx.close();
# The JSContext starts and cleans up the V8 engine
with JSContext() as ctxt:
# ... initialization and workbook creation ...
xlsb = ctxt.eval("XLSX.write(wb, {type: 'base64', bookType: 'xlsb'})");
with open("SheetJSSTPyV8.xlsb", "wb") as f:
f.write(b64decode(xlsb));
```
#### Python Demo
@ -1664,28 +1649,46 @@ This demo was last tested in the following deployments:
| Architecture | V8 Version | Python | Date |
|:-------------|:--------------|:---------|:-----------|
| `darwin-x64` | `14.4` | `3.13.7` | 2026-03-04 |
| `darwin-arm` | `14.4` | `3.14.3` | 2026-03-04 |
| `win11-x64` | `14.4` | `3.11.9` | 2026-03-05 |
| `win11-arm` | `14.4` | `3.11.9` | 2026-03-05 |
| `linux-x64` | `14.4` | `3.13.9` | 2026-03-04 |
| `linux-arm` | `14.4` | `3.13.5` | 2026-03-04 |
| `darwin-x64` | `13.1.201.22` | `3.13.1` | 2025-03-31 |
| `darwin-arm` | `13.1.201.22` | `3.13.2` | 2025-04-24 |
| `win11-x64` | `13.1.201.22` | `3.11.9` | 2025-04-28 |
| `linux-x64` | `13.1.201.22` | `3.12.3` | 2025-06-16 |
:::
0) Make a new folder for the project:
```bash
mkdir sheetjs-miniracer
cd sheetjs-miniracer
mkdir sheetjs-stpyv8
cd sheetjs-stpyv8
```
1) Install `mini-racer`:
1) Install `stpyv8`:
```bash
pip install mini-racer
pip install stpyv8
```
:::caution pass
The install may fail with a `externally-managed-environment` error:
```
error: externally-managed-environment
× This environment is externally managed
```
The wheel can be downloaded and forcefully installed. The following commands
download and install version `13.0.245.16` for Python `3.13` on `darwin-arm`:
```bash
curl -LO https://github.com/cloudflare/stpyv8/releases/download/v13.1.201.22/stpyv8-13.1.201.22-cp313-cp313-macosx_14_0_arm64.whl
sudo python -m pip install --break-system-packages --upgrade stpyv8-13.1.201.22-cp313-cp313-macosx_14_0_arm64.whl
```
:::
2) Download the SheetJS standalone script and test file. Move both files to the
project directory:
@ -1699,20 +1702,40 @@ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
curl -LO https://docs.sheetjs.com/pres.xlsx`}
</CodeBlock>
3) Download [`sheetjs-mini-racer.py`](pathname:///v8/sheetjs-mini-racer.py):
3) Download [`sheetjs-stpyv8.py`](pathname:///v8/sheetjs-stpyv8.py):
```bash
curl -LO https://docs.sheetjs.com/v8/sheetjs-mini-racer.py
curl -LO https://docs.sheetjs.com/v8/sheetjs-stpyv8.py
```
4) Run the script and pass `pres.xlsx` as the first argument:
```bash
python sheetjs-mini-racer.py pres.xlsx
python sheetjs-stpyv8.py pres.xlsx
```
The script will display CSV rows from the first worksheet. It will also create
`SheetJSMiniRacer.xlsb`, a workbook that can be opened with a spreadsheet editor.
`SheetJSSTPyV8.xlsb`, a workbook that can be opened with a spreadsheet editor.
:::caution pass
On Windows, this may fail with a `charmap` error:
```
return codecs.charmap_decode(input,self.errors,decoding_table)[0]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'charmap' codec can't decode byte 0x90 in position 380: character maps to <undefined>
```
`sheetjs-stpyv8.py` must be altered to read `xlsx.full.min.js` with mode `rb`:
```python title="sheetjs-stpyv8.py (edit highlighted line)"
# Read xlsx.full.min.js
# highlight-next-line
with open("xlsx.full.min.js", "rb") as f:
```
:::
## Snapshots
@ -1738,11 +1761,11 @@ This demo was last tested in the following deployments:
| Architecture | V8 Version | Crate | Date |
|:-------------|:--------------|:----------|:-----------|
| `darwin-x64` | `13.5.212.10` | `136.0.0` | 2026-03-08 |
| `darwin-x64` | `13.5.212.10` | `136.0.0` | 2025-04-21 |
| `darwin-arm` | `13.5.212.10` | `136.0.0` | 2025-04-24 |
| `win11-x64` | `13.5.212.10` | `136.0.0` | 2025-05-11 |
| `linux-x64` | `13.5.212.10` | `136.0.0` | 2025-06-16 |
| `linux-arm` | `13.5.212.10` | `136.0.0` | 2026-03-07 |
| `linux-arm` | `13.4.114.9` | `134.4.0` | 2025-02-15 |
:::
@ -1841,4 +1864,6 @@ mv target/release/sheet2csv.exe .
[^4]: See [`write` in "Writing Files"](/docs/api/write-options)
[^5]: See ["Supported Output Formats" type in "Writing Files"](/docs/api/write-options#supported-output-formats)
[^6]: The project does not have an official website. The [official Rust crate](https://crates.io/crates/v8) is hosted on `crates.io`.
[^7]: `pipes` and other modules were removed from the standard library in Python 3.13 as part of ["PEP 594"](https://docs.python.org/3/whatsnew/3.13.html#whatsnew313-pep594).
[^7]: The project does not have a separate website. The source repository is hosted on [GitHub](https://github.com/cloudflare/stpyv8)
[^8]: According to a maintainer, [typed arrays were not supported in the original `pyv8` project](https://github.com/cloudflare/stpyv8/issues/104#issuecomment-2059125389)
[^9]: `pipes` and other modules were removed from the standard library in Python 3.13 as part of ["PEP 594"](https://docs.python.org/3/whatsnew/3.13.html#whatsnew313-pep594).

@ -32,28 +32,28 @@ Swift on MacOS supports JavaScriptCore without additional dependencies.
| Architecture | Swift | Date |
|:-------------|:--------|:-----------|
| `darwin-x64` | `6.1.2` | 2026-03-04 |
| `darwin-arm` | `6.0.3` | 2026-03-04 |
| `darwin-x64` | `6.1` | 2025-04-21 |
| `darwin-arm` | `6.1` | 2025-04-21 |
[**C / C++ Compiled from Source**](#c)
JavaScriptCore can be built from source and linked in C / C++ programs.
| Architecture | Version | Date |
|:-------------|:------------------|:-----------|
| `darwin-x64` | `7623.1.14.14.11` | 2026-03-04 |
| `darwin-arm` | `7623.1.14.14.11` | 2026-03-04 |
| `linux-x64` | `7623.1.14.14.11` | 2026-03-04 |
| `linux-arm` | `7623.1.14.14.11` | 2026-03-04 |
| Architecture | Version | Date |
|:-------------|:-----------------|:-----------|
| `darwin-x64` | `7620.2.4.111.7` | 2025-04-21 |
| `darwin-arm` | `7620.2.4.111.7` | 2025-04-21 |
| `linux-x64` | `7620.2.4.111.7` | 2025-04-21 |
| `linux-arm` | `7620.2.4.111.7` | 2025-04-21 |
[**Swift Compiled from Source**](#swift-c)
Swift compiler can link against libraries built from the JavaScriptCore source.
| Architecture | Version | Date |
|:-------------|:------------------|:-----------|
| `linux-x64` | `7623.1.14.14.11` | 2026-03-04 |
| `linux-arm` | `7623.1.14.14.11` | 2026-03-04 |
| Architecture | Version | Date |
|:-------------|:-----------------|:-----------|
| `linux-x64` | `7620.2.4.111.7` | 2025-04-21 |
| `linux-arm` | `7620.2.4.111.7` | 2025-04-21 |
:::
@ -445,7 +445,7 @@ sudo pacman -Syu base-devel cmake ruby icu glibc linux-api-headers
On Debian and Ubuntu, dependencies should be installed with `apt`:
```bash
sudo apt-get install build-essential cmake ruby libicu-dev
sudo apt-get install build-essential cmake ruby
```
</details>
@ -457,12 +457,12 @@ mkdir sheetjs-jsc
cd sheetjs-jsc
```
2) Clone the WebKit repository and switch to the `WebKit-7623.1.14.14.11` tag:
2) Clone the WebKit repository and switch to the `WebKit-7620.2.4.111.7` tag:
```bash
git clone https://github.com/WebKit/WebKit.git WebKit
cd WebKit
git checkout WebKit-7623.1.14.14.11
git checkout WebKit-7620.2.4.111.7
cd ..
```
@ -473,36 +473,19 @@ cd ..
```bash
cd WebKit
env CFLAGS="-Wno-error=all -Wno-deprecated-declarations" CXXFLAGS="-Wno-error=all -Wno-deprecated-declarations" LDFLAGS="-framework Foundation" Tools/Scripts/build-webkit --jsc-only --cmakeargs="-Wno-error=all -DENABLE_STATIC_JSC=ON -DCMAKE_C_FLAGS=\"-Wno-error=all -Wno-deprecated-declarations\" -DCMAKE_CXX_FLAGS=\"-Wno-error=all -Wno-deprecated-declarations\"" --make-args="-Wno-error=all -Wno-deprecated-declarations" --no-jit --no-webassembly
env CFLAGS="-Wno-error=all -Wno-deprecated-declarations" CXXFLAGS="-Wno-error=all -Wno-deprecated-declarations" LDFLAGS="-framework Foundation" Tools/Scripts/build-webkit --jsc-only --cmakeargs="-Wno-error=all -DENABLE_STATIC_JSC=ON -DCMAKE_C_FLAGS=\"-Wno-error=all -Wno-deprecated-declarations\" -DCMAKE_CXX_FLAGS=\"-Wno-error=all -Wno-deprecated-declarations\"" --make-args="-Wno-error=all -Wno-deprecated-declarations"
cd ..
```
:::caution pass
:::note pass
In some test runs, there were test compile errors:
```
WebKit/Source/JavaScriptCore/API/tests/ExecutionTimeLimitTest.cpp:42:10: fatal error: 'wtf/darwin/DispatchExtras.h' file not found
42 | #include <wtf/darwin/DispatchExtras.h>
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
```
The referenced file must be patched to assume `MACH_EXCEPTIONS` is not defined.
There are a number of pre-processor directives:
```c
#if HAVE(MACH_EXCEPTIONS)
#include <wtf/darwin/DispatchExtras.h>
#endif
```
Each pre-processor block must be removed.
This can be automated with a simple Perl command:
In some test runs on ARM64 macOS, JIT elicited runtime errors and WebAssembly
elicited compile-time errors. WebAssembly and JIT should be disabled:
```bash
perl -0777 -i -pe 's/#if HAVE\(MACH_EXCEPTIONS\).*?#endif\n*/\n/gs' "Source/JavaScriptCore/API/tests/ExecutionTimeLimitTest.cpp"
cd WebKit
env CFLAGS="-Wno-error=all -Wno-deprecated-declarations" CXXFLAGS="-Wno-error=all -Wno-deprecated-declarations" LDFLAGS="-framework Foundation" Tools/Scripts/build-webkit --jsc-only --cmakeargs="-Wno-error=all -DENABLE_STATIC_JSC=ON -DCMAKE_C_FLAGS=\"-Wno-error=all -Wno-deprecated-declarations\" -DCMAKE_CXX_FLAGS=\"-Wno-error=all -Wno-deprecated-declarations\"" --make-args="-Wno-error=all -Wno-deprecated-declarations" --no-jit --no-webassembly
cd ..
```
:::
@ -562,7 +545,7 @@ The `#include` should be changed to a relative directive:
```bash
cd WebKit
env CFLAGS="-Wno-error=all -Wno-error=volatile-register-var -Wno-dangling-reference" CXXFLAGS="-Wno-error=all -Wno-error=volatile-register-var -Wno-dangling-reference" Tools/Scripts/build-webkit --jsc-only --cmakeargs="-Wno-error=all -Wno-error=volatile-register-var -DENABLE_STATIC_JSC=ON -DUSE_THIN_ARCHIVES=OFF" --make-args="-j1 -Wno-error=all -Wno-error=volatile-register-var" -j1 --no-jit --no-webassembly
env CFLAGS="-Wno-error=all -Wno-error=volatile-register-var -Wno-dangling-reference" CXXFLAGS="-Wno-error=all -Wno-error=volatile-register-var -Wno-dangling-reference" Tools/Scripts/build-webkit --jsc-only --cmakeargs="-Wno-error=all -Wno-error=volatile-register-var -DENABLE_STATIC_JSC=ON -DUSE_THIN_ARCHIVES=OFF -DCMAKE_C_FLAGS=\"-Wno-error=all -Wno-error=volatile-register-var -Wno-dangling-reference\" -DCMAKE_CXX_FLAGS=\"-Wno-error=all -Wno-error=volatile-register-var \"" --make-args="-j1 -Wno-error=all -Wno-error=volatile-register-var " -j1
cd ..
```
@ -602,10 +585,17 @@ The error can be suppressed with preprocessor directives around the definition:
T* prev() const { return static_cast<T*>(PtrTraits::unwrap(m_prev)); }
```
After patching the header, JSC must be built without WebAssembly or JIT support:
```bash
cd WebKit
env CFLAGS="-Wno-error=all -Wno-error=volatile-register-var -Wno-dangling-reference" CXXFLAGS="-Wno-error=all -Wno-error=volatile-register-var -Wno-dangling-reference" Tools/Scripts/build-webkit --jsc-only --cmakeargs="-Wno-error=all -Wno-error=volatile-register-var -DENABLE_STATIC_JSC=ON -DUSE_THIN_ARCHIVES=OFF -DCMAKE_C_FLAGS=\"-Wno-error=all -Wno-error=volatile-register-var -Wno-dangling-reference\" -DCMAKE_CXX_FLAGS=\"-Wno-error=all -Wno-error=volatile-register-var \"" --make-args="-j1 -Wno-error=all -Wno-error=volatile-register-var " -j1 --no-jit --no-webassembly
cd ..
```
:::
:::note pass
:::caution pass
In some test runs, there was a register error:
@ -894,7 +884,7 @@ pub struct JSString {
_marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
}
// highlight-next-line
type JSStringRef = *mut JSString;
type JSStringRef = *mut JSContext;
```
**Function Declaration**
@ -908,10 +898,9 @@ JSStringRef JSStringCreateWithUTF8CString(const char * string);
The equivalent Rust declaration must be defined in an `extern "C"` block:
```rust title="JSStringCreateWithUTF8CString Rust declaration"
use libc::c_char;
unsafe extern "C" {
// JSStringRef JSStringCreateWithUTF8CString(const char * string);
pub unsafe fn JSStringCreateWithUTF8CString(string: *const c_char) -> JSStringRef;
pub unsafe fn JSStringCreateWithUTF8CString(string: *const u8) -> JSStringRef;
}
```
@ -951,10 +940,8 @@ The demo makes a safe wrapper to perform the unsafe waltz in one line:
pub struct JSC;
impl JSC {
pub fn JSStringCreateWithUTF8CString(str: &str) -> JSStringRef { unsafe {
// highlight-start
let cstr = std::ffi::CString::new(str).unwrap();
JSStringCreateWithUTF8CString(cstr.as_ptr())
// highlight-end
// highlight-next-line
JSStringCreateWithUTF8CString(std::ffi::CString::new(str.as_bytes()).unwrap().as_ptr() as *const u8)
} }
}
```
@ -967,24 +954,11 @@ This demo was last tested in the following deployments:
| Architecture | Date |
|:-------------|:-----------|
| `darwin-x64` | 2026-03-04 |
| `darwin-arm` | 2026-03-04 |
| `linux-x64` | 2026-03-04 |
| `linux-arm` | 2026-03-04 |
| `darwin-x64` | 2025-03-31 |
| `darwin-arm` | 2025-03-30 |
:::
<Tabs groupId="triple">
<TabItem value="darwin-x64" label="MacOS">
</TabItem>
<TabItem value="linux-x64" label="Linux">
0) Follow the entire ["C" demo](#c). The library will be used in Rust.
</TabItem>
</Tabs>
1) Create a new project:
```bash
@ -1025,13 +999,7 @@ curl -L -o src/main.rs https://docs.sheetjs.com/jsc/main.rs
curl -LO https://docs.sheetjs.com/jsc/build.rs
```
6) Install the `libc` crate:
```bash
cargo add libc
```
7) Build and run the app:
6) Build and run the app:
```bash
cargo run pres.numbers

@ -163,12 +163,12 @@ This demo was tested in the following deployments:
| Architecture | Jint | Date |
|:-------------|:--------|:-----------|
| `darwin-x64` | `4.5.0` | 2026-01-21 |
| `darwin-arm` | `4.6.3` | 2026-03-22 |
| `darwin-x64` | `4.2.1` | 2025-03-31 |
| `darwin-arm` | `4.2.0` | 2025-02-13 |
| `win11-x64` | `4.2.2` | 2026-04-28 |
| `win11-arm` | `4.6.3` | 2026-03-22 |
| `win11-arm` | `4.2.0` | 2025-02-23 |
| `linux-x64` | `4.2.2` | 2025-06-16 |
| `linux-arm` | `4.5.0` | 2026-03-07 |
| `linux-arm` | `4.2.0` | 2025-02-15 |
:::
@ -248,7 +248,7 @@ dotnet run
```bash
dotnet nuget add source https://www.myget.org/F/jint/api/v3/index.json
dotnet add package Jint --version 4.6.3
dotnet add package Jint --version 4.2.2
```
To verify Jint is installed, replace `Program.cs` with the following:

@ -105,12 +105,12 @@ This demo was tested in the following deployments:
| Architecture | Git Commit | Go version | Date |
|:-------------|:-----------|:-----------|:-----------|
| `darwin-x64` | `651366f` | `1.25.6` | 2026-01-21 |
| `darwin-arm` | `6a7976c` | `1.24.0` | 2026-03-07 |
| `win11-x64` | `6a7976c` | `1.24.2` | 2026-03-08 |
| `win11-arm` | `065cd970` | `1.26.1` | 2026-03-22 |
| `darwin-x64` | `bcd7cc6` | `1.24.1` | 2025-03-31 |
| `darwin-arm` | `cb187b0` | `1.24.0` | 2025-06-18 |
| `win11-x64` | `bcd7cc6` | `1.24.2` | 2025-04-28 |
| `win11-arm` | `5ef83b8` | `1.24.0` | 2025-02-23 |
| `linux-x64` | `cb187b0` | `1.22.2` | 2025-06-16 |
| `linux-arm` | `6a7976c` | `1.24.4` | 2026-03-07 |
| `linux-arm` | `5ef83b8` | `1.19.8` | 2025-02-15 |
At the time of writing, Goja did not have proper version numbers. Versions are
identified by Git commit hashes.

@ -27,7 +27,7 @@ command-line tool for reading data from files.
:::note pass
Many QuickJS functions are not documented. The explanation was verified against
commit `f113949`.
commit `0d7aaed`.
:::
@ -263,14 +263,14 @@ This demo was tested in the following deployments:
| Architecture | Library | Git Commit | Date |
|:-------------|:-----------|:-----------|:-----------|
| `darwin-x64` | QuickJS | `f113949` | 2026-01-21 |
| `darwin-arm` | QuickJS | `f113949` | 2026-03-07 |
| `win11-x64` | QuickJS-NG | `4951c83` | 2025-04-18 |
| `darwin-x64` | QuickJS | `0d7aaed` | 2025-03-31 |
| `darwin-arm` | QuickJS | `3306254` | 2025-09-03 |
| `win11-x64` | QuickJS-NG | `865ba1f` | 2025-04-18 |
| `win11-arm` | QuickJS-NG | `865ba1f` | 2025-04-18 |
| `linux-x64` | QuickJS | `f113949` | 2026-03-08 |
| `linux-arm` | QuickJS | `f113949` | 2026-03-07 |
| `linux-x64` | QuickJS | `3306254` | 2025-06-18 |
| `linux-arm` | QuickJS | `6e2e68f` | 2025-02-15 |
When the demo was tested, `f113949` was the HEAD commit on the `master` branch.
When the demo was tested, `0d7aaed` was the HEAD commit on the `master` branch.
:::
@ -281,7 +281,7 @@ When the demo was tested, `f113949` was the HEAD commit on the `master` branch.
```bash
git clone https://github.com/bellard/quickjs
cd quickjs
git checkout f113949
git checkout 3306254
make
cd ..
```
@ -369,7 +369,7 @@ cd sheetjs-quick
```bash
git clone https://github.com/quickjs-ng/quickjs
cd quickjs
git checkout 4951c83
git checkout 865ba1f1
cmake -B build -DQJS_BUILD_EXAMPLES=ON
cmake --build build --config Release
build\Release\qjs.exe amalgam.js
@ -386,7 +386,7 @@ copy quickjs\quickjs.h .
3) Download [`sheetjs.quick.c`](pathname:///quickjs/sheetjs.quick.c):
```bash
curl.exe -LO https://docs.sheetjs.com/quickjs/sheetjs.quick.c
curl -LO https://docs.sheetjs.com/quickjs/sheetjs.quick.c
```
4) Build the sample application:
@ -406,8 +406,8 @@ the project directory:
</ul>
<CodeBlock language="bash">{`\
curl.exe -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
curl.exe -LO https://docs.sheetjs.com/pres.numbers`}
curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
curl -LO https://docs.sheetjs.com/pres.numbers`}
</CodeBlock>
6) Run the test program:
@ -428,9 +428,9 @@ This demo was tested in the following environments:
| Git Commit | Date |
|:-----------|:-----------|
| `f113949` | 2026-01-21 |
| `0d7aaed` | 2025-03-31 |
When the demo was tested, `f113949` was the HEAD commit on the `master` branch.
When the demo was tested, `0d7aaed` was the HEAD commit on the `master` branch.
:::
@ -440,7 +440,7 @@ and build the `quickjs` project:
```bash
git clone https://github.com/bellard/quickjs
cd quickjs
git checkout f113949
git checkout 0d7aaed
make
cd ..
```

@ -364,10 +364,10 @@ This demo was tested in the following deployments:
| Architecture | Git Commit | Date |
|:-------------|:-----------|:-----------|
| `darwin-x64` | `f45c6bc` | 2026-03-06 |
| `darwin-arm` | `f45c6bc` | 2026-01-23 |
| `darwin-x64` | `8ef11b4` | 2025-03-31 |
| `darwin-arm` | `8ef11b4` | 2025-09-03 |
| `linux-x64` | `8ef11b4` | 2025-04-21 |
| `linux-arm` | `f45c6bc` | 2026-03-07 |
| `linux-arm` | `388376f` | 2025-02-15 |
The main Hermes source tree does not have Windows support. The `hermes-windows`
fork, which powers React Native for Windows, does have built-in support[^5]
@ -586,15 +586,9 @@ cp ./build_release/jsi/libjsi.so .
</TabItem>
<TabItem value="darwin" label="MacOS">
:::note pass
Some Hermes releases build static libraries, rendering this step unnecessary.
:::
```bash
if [ -e ./build_release/API/hermes/libhermes.dylib ]; then cp ./build_release/API/hermes/libhermes.dylib .; fi
if [ -e ./build_release/jsi/libjsi.dylib ]; then cp ./build_release/jsi/libjsi.dylib .; fi
cp ./build_release/API/hermes/libhermes.dylib .
cp ./build_release/jsi/libjsi.dylib .
```
</TabItem>
@ -853,16 +847,13 @@ This demo was tested in the following deployments:
| Architecture | Hermes | Date |
|:-------------|:---------|:-----------|
| `darwin-x64` | `0.13.0` | 2026-03-06 |
| `darwin-arm` | `0.13.0` | 2026-03-06 |
| `darwin-x64` | `0.13.0` | 2025-03-31 |
| `darwin-arm` | `0.13.0` | 2025-04-23 |
| `win11-x64` | `0.13.0` | 2025-04-28 |
| `win11-arm` | `0.13.0` | 2025-02-23 |
| `linux-x64` | `0.13.0` | 2025-04-21 |
| `linux-arm` | `0.13.0` | 2026-03-07 |
When this demo was last tested, `jsvu` did not install a native `linux-arm`
binary. Nevertheless, the process was tested using `qemu-user` to simulate the
`linux-x64` program.
When this demo was last tested, `jsvu` did not support `linux-arm`.
:::
@ -879,26 +870,13 @@ npx -y jsvu hermes@0.13.0
When prompted, select the appropriate operating system.
<details>
<summary><b>Installation Notes</b> (click to show)</summary>
:::info pass
`jsvu` on Windows on ARM uses the X64 compatibility layer. When the demo was
last tested, the engine binaries were not native ARM64 programs.
---
:::
`jsvu` will fail on Linux on ARM since the `linux64` binary is X64. This error
can be ignored.
On this platform, `qemu-user` must be installed by the root user:
```bash
sudo apt install qemu-user
```
In Debian, the package includes bindings to support the `binfmt_misc` system.
</details>
1) Inspect the output of the installer. Look for "Installing binary" lines:

@ -87,12 +87,12 @@ This demo was tested in the following deployments:
| Platform | Ruby | ExecJS | Date |
|:-------------|:---------|:---------|:-----------|
| `darwin-x64` | `2.6.10` | `2.10.0` | 2026-01-21 |
| `darwin-arm` | `2.6.10` | `2.10.0` | 2026-03-07 |
| `darwin-x64` | `2.6.10` | `2.10.0` | 2025-03-31 |
| `darwin-arm` | `2.6.10` | `2.10.0` | 2025-09-03 |
| `win11-x64` | `3.3.8` | `2.10.0` | 2025-04-28 |
| `win11-arm` | `3.2.3` | `2.10.0` | 2025-02-23 |
| `linux-x64` | `3.3.8` | `2.10.0` | 2026-03-08 |
| `linux-arm` | `3.3.8` | `2.10.0` | 2026-03-07 |
| `linux-x64` | `3.2.3` | `2.10.0` | 2025-04-21 |
| `linux-arm` | `3.1.2` | `2.10.0` | 2025-02-15 |
When the demo was last tested, there was no official Ruby release for Windows
on ARM. The `win11-arm` test was run in WSL.

@ -87,6 +87,7 @@ static char *read_file(const char *filename, size_t *sz) {
// ...
/* load library */
EVAL_FILE("shim.min.js")
EVAL_FILE("xlsx.full.min.js")
```
@ -131,12 +132,11 @@ This demo was tested in the following deployments:
| Architecture | Git Commit | Date |
|:-------------|:-----------|:-----------|
| `darwin-x64` | `792ee76` | 2026-01-20 |
| `darwin-arm` | `792ee76` | 2026-03-08 |
| `darwin-x64` | `36becec` | 2025-03-31 |
| `darwin-arm` | `36becec` | 2025-09-03 |
| `win11-x64` | `36becec` | 2025-04-28 |
| `win11-arm` | `e26c81f` | 2025-02-23 |
| `linux-x64` | `792ee76` | 2026-03-08 |
| `linux-arm` | `792ee76` | 2026-01-10 |
| `linux-x64` | `36becec` | 2025-06-18 |
:::
@ -204,7 +204,7 @@ The commands in this demo should be run in "ARM64 Native Tools Command Prompt".
```bash
git clone https://github.com/chakra-core/ChakraCore.git
cd ChakraCore
git checkout 792ee76
git checkout 36becec
cd ..
```
@ -215,7 +215,7 @@ cd ..
```bash
cd ChakraCore
./build.sh --static --icu=/usr/local/opt/icu4c/include --test-build -j=8 --no-jit
./build.sh --static --icu=/usr/local/opt/icu4c/include --test-build -j=8 --system-icu --no-jit
cd ..
```
@ -233,7 +233,6 @@ This was fixed with a local symlink to the `icu4c` folder before the build step:
cd ChakraCore
mkdir -p usr/local/opt
ln -s /opt/homebrew/opt/icu4c usr/local/opt/icu4c
./build.sh --static --icu=usr/local/opt/icu4c/include --test-build -j=8 --system-icu --no-jit
cd ..
```
@ -292,10 +291,12 @@ When the demo was last tested, ChakraCore JIT was not supported.
```bash
cd ChakraCore
./build.sh --static --icu=/usr/local/opt/icu4c/include --test-build -j=8 --no-jit
export PATH="$(brew --prefix cmake3)/bin:${PATH}"
./build.sh --static --icu=$(brew --prefix)/opt/icu4c/include --test-build -j=8 --no-jit
cd ..
```
:::
:::caution Troubleshooting
@ -567,16 +568,18 @@ cl sheetjs.ch.cpp ChakraCore.lib /I ChakraCore\lib\Jsrt /link /LIBPATH:ChakraCor
</TabItem>
</Tabs>
5) Download the SheetJS Standalone script and test file. Move all three files to
the project directory:
5) Download the SheetJS Standalone script, shim script and test file. Move all
three files to the project directory:
<ul>
<li><a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}>xlsx.full.min.js</a></li>
<li><a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js`}>shim.min.js</a></li>
<li><a href="https://docs.sheetjs.com/pres.numbers">pres.numbers</a></li>
</ul>
<CodeBlock language="bash">{`\
curl -L -O https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
curl -L -O https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js
curl -L -O https://docs.sheetjs.com/pres.numbers`}
</CodeBlock>
@ -606,7 +609,7 @@ If successful, the program will print the contents of the first sheet as CSV.
:::note Tested Deployments
This demo was last tested on 2026-01-10 against `ch` commit `792ee76`.
This demo was last tested on 2025-06-20 against `ch` commit `36becec`.
:::
@ -624,16 +627,18 @@ or `ChakraCore\Build\VcBuild\bin\x64_debug\` on x64 Windows.
:::
1) Download the SheetJS Standalone script and test file. Move all three files to
the project directory:
1) Download the SheetJS Standalone script, shim script and test file. Move all
three files to the project directory:
<ul>
<li><a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}>xlsx.full.min.js</a></li>
<li><a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js`}>shim.min.js</a></li>
<li><a href="https://docs.sheetjs.com/pres.numbers">pres.numbers</a></li>
</ul>
<CodeBlock language="bash">{`\
curl -L -O https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
curl -L -O https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js
curl -L -O https://docs.sheetjs.com/pres.numbers`}
</CodeBlock>
@ -682,16 +687,6 @@ On Windows, the command should be run in WSL.
./ch xlsx.chakra.js
```
:::note pass
The "Integration Example" stores `ch` in the `ChakraCore/out/Test/` folder:
```bash
./ChakraCore/out/Test/ch xlsx.chakra.js
```
:::
</TabItem>
<TabItem value="win" label="Windows">

@ -82,8 +82,7 @@ Boa supports `ArrayBuffer` natively. This snippet reads data from a file into
```rust
/* read file */
let data: Vec<u8> = std::fs::read("pres.xlsx").unwrap();
let aligned = boa_engine::object::builtins::AlignedVec::from_iter(0, file.into_iter());
let array: boa_engine::object::builtins::JsArrayBuffer = boa_engine::object::builtins::JsArrayBuffer::from_byte_block(aligned, context).unwrap();
let array: boa_engine::object::builtins::JsArrayBuffer = boa_engine::object::builtins::JsArrayBuffer::from_byte_block(file, context).unwrap();
let attrs = boa_engine::property::Attribute::WRITABLE | boa_engine::property::Attribute::ENUMERABLE | boa_engine::property::Attribute::CONFIGURABLE;
let _ = context.register_global_property(boa_engine::js_string!("buf"), array, attrs);
@ -105,12 +104,12 @@ This demo was tested in the following deployments:
| Architecture | Boa | Date |
|:-------------|:---------|:-----------|
| `darwin-x64` | `0.20.1` | 2026-01-21 |
| `darwin-arm` | `0.21.0` | 2026-03-07 |
| `darwin-x64` | `0.20.0` | 2025-03-31 |
| `darwin-arm` | `0.20.0` | 2025-09-03 |
| `win11-x64` | `0.20.0` | 2025-04-28 |
| `win11-arm` | `0.21.0` | 2026-03-22 |
| `linux-x64` | `0.21.0` | 2026-01-08 |
| `linux-arm` | `0.21.0` | 2026-03-07 |
| `win11-arm` | `0.20.0` | 2025-02-23 |
| `linux-x64` | `0.20.0` | 2025-04-21 |
| `linux-arm` | `0.20.0` | 2025-02-15 |
:::

@ -68,12 +68,10 @@ String.prototype.match = function(p) {
When loading the ExtendScript build, the BOM must be removed:
```perl title="Remove BOM from the SheetJS ExtendScript build and evaluate"
```perl
## Load SheetJS source
my $src = read_file('xlsx.extendscript.js', { binmode => ':raw' });
$src =~ s/^\xEF\xBB\xBF//; ## remove UTF8 BOM
## evaluate the updated source
my $XLSX = $je->eval($src);
```
@ -81,7 +79,7 @@ my $XLSX = $je->eval($src);
Data should be passed as Base64 strings:
```perl title="Read spreadsheet file in Perl and parse with SheetJS"
```perl
use File::Slurp;
use MIME::Base64 qw( encode_base64 );
@ -104,7 +102,7 @@ $return_val = $je->method(sheetjsparse => $raw_data);
Due to bugs in data interchange, it is strongly recommended to use a simple
format like `.fods`:
```perl title="Generate FODS with SheetJS and write to file in Perl"
```perl
use File::Slurp;
## Set up conversion method
@ -129,14 +127,10 @@ This demo was tested in the following deployments:
| Architecture | Version | Date |
|:-------------|:--------|:-----------|
| `darwin-x64` | `0.066` | 2026-01-21 |
| `darwin-arm` | `0.066` | 2026-03-07 |
| `win11-x64` | `0.066` | 2026-01-10 |
| `win11-arm` | `0.066` | 2026-01-10 |
| `linux-x64` | `0.066` | 2026-03-07 |
| `linux-arm` | `0.066` | 2026-03-07 |
The Windows tests were run in WSL.
| `darwin-x64` | `0.066` | 2025-03-31 |
| `darwin-arm` | `0.066` | 2025-09-03 |
| `linux-x64` | `0.066` | 2025-06-16 |
| `linux-arm` | `0.066` | 2025-02-15 |
:::

@ -36,12 +36,12 @@ This demo was tested in the following environments:
| Architecture | Commit | Date |
|:-------------|:----------|:-----------|
| `darwin-x64` | `b706935` | 2026-01-21 |
| `darwin-arm` | `b706935` | 2026-03-07 |
| `darwin-x64` | `5020015` | 2025-03-31 |
| `darwin-arm` | `355ab24` | 2025-09-03 |
| `win11-x64` | `5020015` | 2025-04-23 |
| `win11-arm` | `5020015` | 2025-02-23 |
| `linux-x64` | `b706935` | 2026-03-07 |
| `linux-arm` | `b706935` | 2026-03-07 |
| `linux-x64` | `5020015` | 2025-04-21 |
| `linux-arm` | `5020015` | 2025-02-15 |
The Windows tests were run in WSL.
@ -52,7 +52,7 @@ The Windows tests were run in WSL.
:::info pass
The official JerryScript documentation and examples are out of date. This
explanation was verified against the latest release (commit `b706935`).
explanation was verified against the latest release (commit `5020015`).
:::
@ -366,18 +366,6 @@ python3 tools/build.py --error-messages=ON --logging=ON --mem-heap=8192 --cpoint
cd ..
```
:::caution pass
In some tests, the build step failed with compiler warnings treated as errors.
The flags `-Wno-error` and `-Wno-unterminated-string-initialization` should be
added to the build:
```bash
python3 tools/build.py --error-messages=ON --logging=ON --mem-heap=8192 --cpointer-32bit=ON --compile-flag="-Wno-error" --compile-flag="-Wno-unterminated-string-initialization"
```
:::
3) Download the SheetJS Standalone script, shim script and test file. Move all
three files to the `SheetJSJerry` directory:

@ -36,7 +36,7 @@ as [Duktape](/docs/demos/engines/duktape).
:::info pass
Many MuJS functions are not documented. The explanation was verified against
version `1.3.8`.
version `1.3.6`.
:::
@ -322,12 +322,12 @@ This demo was tested in the following deployments:
| Architecture | Version | Date |
|:-------------|:--------|:-----------|
| `darwin-x64` | `1.3.8` | 2026-01-20 |
| `darwin-arm` | `1.3.8` | 2026-01-20 |
| `win11-x64` | `1.3.8` | 2026-03-08 |
| `darwin-x64` | `1.3.6` | 2025-03-31 |
| `darwin-arm` | `1.3.6` | 2025-09-03 |
| `win11-x64` | `1.3.6` | 2025-04-23 |
| `win11-arm` | `1.3.5` | 2025-02-23 |
| `linux-x64` | `1.3.8` | 2026-03-07 |
| `linux-arm` | `1.3.8` | 2026-01-10 |
| `linux-x64` | `1.3.6` | 2025-06-16 |
| `linux-arm` | `1.3.5` | 2025-02-15 |
:::
@ -355,9 +355,9 @@ cd sheetjs-mu
2) Build the MuJS shared library from source:
```bash
curl -LO https://mujs.com/downloads/mujs-1.3.8.zip
unzip mujs-1.3.8.zip
cd mujs-1.3.8
curl -LO https://mujs.com/downloads/mujs-1.3.6.zip
unzip mujs-1.3.6.zip
cd mujs-1.3.6
make release
cd ..
```
@ -365,7 +365,7 @@ cd ..
3) Copy the `mujs.h` header file and `libmujs.a` library to the project folder:
```bash
cp mujs-1.3.8/build/release/libmujs.a mujs-1.3.8/mujs.h .
cp mujs-1.3.6/build/release/libmujs.a mujs-1.3.6/mujs.h .
```
4) Download [`SheetJSMu.c`](pathname:///mujs/SheetJSMu.c):

@ -176,12 +176,12 @@ This demo was tested in the following deployments:
| Architecture | Jurassic | Date |
|:-------------|:---------|:-----------|
| `darwin-x64` | `3.2.9` | 2026-01-21 |
| `darwin-arm` | `3.2.9` | 2026-03-07 |
| `darwin-x64` | `3.2.9` | 2025-03-31 |
| `darwin-arm` | `3.2.9` | 2025-09-03 |
| `win11-x64` | `3.2.9` | 2025-04-23 |
| `win11-arm` | `3.2.9` | 2026-03-22 |
| `linux-x64` | `3.2.9` | 2026-03-07 |
| `linux-arm` | `3.2.9` | 2026-03-07 |
| `win11-arm` | `3.2.9` | 2025-02-23 |
| `linux-x64` | `3.2.9` | 2025-06-16 |
| `linux-arm` | `3.2.9` | 2025-02-15 |
:::

@ -100,9 +100,6 @@ Asterisks (✱) in the Windows columns mark tests that were run in Windows
Subsystem for Linux (WSL). In some cases, community efforts have produced forks
with native Windows support.
Crosses (✘) mark tests for Windows on ARM that use the X64 compatibility layer.
Proper Windows x64 binaries were cross-compiled and run in Windows on ARM.
Blank cells mark untested or unsupported configurations. With cross-compilation,
V8 can run natively in Windows on ARM. The `win11-arm` platform is not tested
since the official build infrastructure does not support Windows on ARM and the

@ -8,23 +8,6 @@ hide_table_of_contents: true
Demos include complete examples and short discussions. For features that can
run in the web browser, demos will include interactive examples.
:::info pass
**These demos were tested or verified by SheetJS teammates!**
Many demos were borne from requests by [SheetJS Pro](https://sheetjs.com/pro)
customers and written based on experience from interactive debugging sessions.
When possible, SheetJS teammates test demos on local infrastructure. Some cloud
service tests were verified in screen-share sessions.
The web ecosystem moves fast and break things. Demos that worked a week ago may
not work today if a dependency makes a breaking change. Please leave a note in
the [issue tracker](https://git.sheetjs.com/sheetjs/docs.sheetjs.com/issues) if
a demo does not currently work.
:::
:::tip pass
If a demo for a library or framework is not included here, please leave a note

@ -29,6 +29,7 @@ var workbook = XLSX.read(data, opts);
The `read` method can extract data from spreadsheet bytes stored in a JS string,
"binary string", NodeJS buffer or typed array (`Uint8Array` or `ArrayBuffer`).
_Read spreadsheet bytes from a local file and extract data_
```js
@ -37,8 +38,8 @@ var workbook = XLSX.readFile(filename, opts);
The `readFile` method attempts to read a spreadsheet file at the supplied path.
The ["Reading Files"](/docs/api/parse-options) section covers the supported
properties and behaviors.
The second `opts` argument is optional. ["Parsing Options"](/docs/api/parse-options)
covers the supported properties and behaviors.
:::danger pass
@ -47,6 +48,7 @@ security risk), and running `XLSX.readFile` in the browser will throw an error.
Deno scripts must be invoked with `--allow-read` to read from the filesystem.
:::
#### Examples
@ -163,6 +165,7 @@ const workbook = read(Buffer.from(readFileSync(path)));
</TabItem>
</Tabs>
### Example: User Submissions
This example focuses on user-submitted files through a drag-and-drop event, HTML
@ -185,6 +188,7 @@ Assume `drop_dom_element` is the DOM element that will listen for changes:
The event property is `e.dataTransfer`. The code snippet highlights the
difference between the drag-and-drop example and the file input example:
```js
// XLSX is a global from the standalone script
@ -234,6 +238,7 @@ input_dom_element.addEventListener("change", handleFileAsync, false);
https://oss.sheetjs.com/sheetjs/ demonstrates the FileReader technique.
**For maximal compatibility (IE10+)**, the `FileReader` approach is recommended:
<Tabs>
@ -382,9 +387,11 @@ curl -X POST -F "file=@test.xlsx" http://localhost:7262/
:::
</TabItem>
</Tabs>
### Example: Remote File
This example focuses on fetching files ("Ajax" in browser parlance) using APIs
@ -580,6 +587,7 @@ other tools.
:::
<Tabs>
<TabItem value="browser" label="Browser">
@ -723,6 +731,7 @@ var worksheet = XLSX.utils.aoa_to_sheet([
["Array of Arrays Input"](/docs/api/utilities/array#array-of-arrays-input) describes
the function and the optional `opts` argument in more detail.
_Create a worksheet from an array of JS objects_
```js
@ -756,6 +765,7 @@ databases and query results.
</details>
## Processing HTML Tables
#### API
@ -771,6 +781,8 @@ through the rows to generate a worksheet. The `opts` argument is optional.
["HTML Table Input"](/docs/api/utilities/html#html-table-input) describes the
function in more detail.
_Create a workbook by scraping an HTML TABLE in the page_
```js

@ -33,34 +33,110 @@ to mark a dynamic array formula in the XLS file format.
SheetJS supports reading and writing formulae for a number of file formats. When
supported, formulae will always be exported.
The ["A1-Style"](/docs/csf/general#a1-style) formula string is stored in the `f`
field of the cell object. The SheetJS formula string closely matches the text
representation in spreadsheet file formats.
By default, formulae are not always imported. To ensure formula parsing, the
option `cellFormula: true` should be passed to the parser.
:::caution pass
<Tabs>
<TabItem value="browser" label="Browser">
**SheetJS formula strings do not always match Excel or other spreadsheets!**
Typically file data will be available as an `ArrayBuffer`, either downloaded
with `fetch` / `XMLHttpRequest` or user-submitted with a File Input element.
`cellFormula: true` should be added to the second options argument:
- Spreadsheet software typically represent formulae with a leading `=` sign, but
SheetJS formulae omit the `=`.
```js
/* using read in the browser, `cellFormula` is in the second argument */
const ab = await (await fetch("test.xlsx")).arrayBuffer();
const workbook = XLSX.read(ab, { cellFormula: true });
// ------------------------------^^^^^^^^^^^^^^^^^
```
- Spreadsheet software [localize formulae](#localization), but SheetJS formulae
always use the `en-US` form.
</TabItem>
<TabItem value="nodejs" label="NodeJS">
- Spreadsheet software may display formulae in RC notation, but SheetJS formulae
always use A1-Style.
Typically file data will be available as a `Buffer` from a network request / API
or stored in the file system. `cellFormula: true` should be added to the second
options argument to `read` or `readFile`:
- Spreadsheet software will hide [prefixes](#prefixed-future-functions). SheetJS
parsers provide options to replicate that behavior.
**`XLSX.read`**
When building new exports, it is strongly recommended to create a sample file in
Excel, parse with the SheetJS library, and inspect the formula string.
```js
/* using read in NodeJS, `cellFormula` is in the second argument */
const ab = await (await fetch("test.xlsx")).arrayBuffer();
const workbook = XLSX.read(ab, { cellFormula: true });
// ------------------------------^^^^^^^^^^^^^^^^^
```
:::
**`XLSX.readFile`**
## Live Demo
```js
/* using readFile in NodeJS, add `cellFormula` to the second argument */
const workbook = XLSX.readFile("test.xlsx", { cellFormula: true });
// -------------------------------------------^^^^^^^^^^^^^^^^^
```
[This test file](pathname:///files/concat.xlsx) includes a formula in cell `D1`:
</TabItem>
<TabItem value="bun" label="Bun">
Typically file data will be available as a `Uint8Array` from a network request
or stored in the file system. `cellFormula: true` should be set in the options
argument to `read` or `readFile`:
**`XLSX.read`**
```js
/* using read in Bun, `cellFormula` is in the second argument */
const ab = await (await fetch("test.xlsx")).arrayBuffer();
const workbook = XLSX.read(ab, { cellFormula: true });
// ------------------------------^^^^^^^^^^^^^^^^^
```
**`XLSX.readFile`**
```js
/* using readFile in Bun, add `cellFormula` to the second argument */
const workbook = XLSX.readFile("test.xlsx", { cellFormula: true });
// -------------------------------------------^^^^^^^^^^^^^^^^^
```
</TabItem>
<TabItem value="deno" label="Deno">
Typically file data will be available as a `Uint8Array` or `ArrayBuffer` from
API or stored in the file system. `cellFormula: true` should be set in the
options argument to `read` or `readFile`:
**`XLSX.read`**
```js
/* using read in Deno, `cellFormula` is in the second argument */
const ab = await (await fetch("test.xlsx")).arrayBuffer();
const workbook = XLSX.read(ab, { cellFormula: true });
// ------------------------------^^^^^^^^^^^^^^^^^
```
**`XLSX.readFile`**
```js
/* using readFile in Deno, add `cellFormula` to the second argument */
const workbook = XLSX.readFile("test.xlsx", { cellFormula: true });
// -------------------------------------------^^^^^^^^^^^^^^^^^
```
</TabItem>
</Tabs>
## A1-Style Formulae
The A1-Style formula string is stored in the `f` field of the cell object.
Spreadsheet software typically represent formulae with a leading `=` sign, but
SheetJS formulae omit the `=`.
["A1-Style"](/docs/csf/general#a1-style) describes A1-Style in more detail.
<details open>
<summary><b>Live Example</b> (click to hide)</summary>
For example, consider [this test file](pathname:///files/concat.xlsx):
![Screenshot](pathname:///files/concat.png)
@ -68,7 +144,7 @@ The following code block fetches the file, parses and prints info on cell `D1`:
```jsx live
/* The live editor requires this function wrapper */
function InspectCellFormula(props) {
function ConcatFormula(props) {
const [ws, setWS] = React.useState({"!ref":"A1"});
const [addr, setAddr] = React.useState("D1");
const setaddr = React.useCallback((evt)=>{ setAddr(evt.target.value) });
@ -87,8 +163,7 @@ function InspectCellFormula(props) {
process_ab(await e.target.files[0].arrayBuffer());
};
return ( <>
<b>Select a file to inspect cell formulae</b><br/>
<input type="file" onChange={process_file}/><br/><br/>
<input type="file" onChange={process_file}/><br/>
<b>Cell: </b><input type="text" value={addr} onChange={setaddr} size="6"/>
{!ws[addr] ? ( <b>Cell {addr} not found</b> ) : ( <table>
<tr><td>Formula</td><td><code>{ws[addr].f}</code></td></tr>
@ -99,9 +174,9 @@ function InspectCellFormula(props) {
}
```
## Storage
</details>
### Single-Cell Formulae
## Single-Cell Formulae
For simple formulae, the `f` key of the desired cell can be set to the actual
formula text. This worksheet represents `A1=1`, `A2=2`, and `A3=A1+A2`:
@ -175,7 +250,7 @@ values and dependent cells, and refreshing entire workbooks.
:::
### Array Formulae
## Array Formulae
_Assign an array formula_
@ -318,129 +393,20 @@ function ExportDynamicArrayFormulae(props) {
</details>
## Functions
#### Reading Files
[`read` and `readFile`](/docs/api/parse-options) accept an options argument. The
`cellFormula` option should be set to `true` to expose cell formulae:
<Tabs>
<TabItem value="browser" label="Browser">
Typically file data will be available as an `ArrayBuffer`, either downloaded
with `fetch` / `XMLHttpRequest` or user-submitted with a File Input element.
`cellFormula: true` should be added to the second options argument:
```js
/* using read in the browser, `cellFormula` is in the second argument */
const ab = await (await fetch("test.xlsx")).arrayBuffer();
const workbook = XLSX.read(ab, { cellFormula: true });
// ------------------------------^^^^^^^^^^^^^^^^^
```
</TabItem>
<TabItem value="nodejs" label="NodeJS">
Typically file data will be available as a `Buffer` from a network request / API
or stored in the file system. `cellFormula: true` should be added to the second
options argument to `read` or `readFile`:
**`XLSX.read`**
```js
/* using read in NodeJS, `cellFormula` is in the second argument */
const ab = await (await fetch("test.xlsx")).arrayBuffer();
const workbook = XLSX.read(ab, { cellFormula: true });
// ------------------------------^^^^^^^^^^^^^^^^^
```
**`XLSX.readFile`**
```js
/* using readFile in NodeJS, add `cellFormula` to the second argument */
const workbook = XLSX.readFile("test.xlsx", { cellFormula: true });
// -------------------------------------------^^^^^^^^^^^^^^^^^
```
</TabItem>
<TabItem value="bun" label="Bun">
Typically file data will be available as a `Uint8Array` from a network request
or stored in the file system. `cellFormula: true` should be set in the options
argument to `read` or `readFile`:
**`XLSX.read`**
```js
/* using read in Bun, `cellFormula` is in the second argument */
const ab = await (await fetch("test.xlsx")).arrayBuffer();
const workbook = XLSX.read(ab, { cellFormula: true });
// ------------------------------^^^^^^^^^^^^^^^^^
```
**`XLSX.readFile`**
```js
/* using readFile in Bun, add `cellFormula` to the second argument */
const workbook = XLSX.readFile("test.xlsx", { cellFormula: true });
// -------------------------------------------^^^^^^^^^^^^^^^^^
```
</TabItem>
<TabItem value="deno" label="Deno">
Typically file data will be available as a `Uint8Array` or `ArrayBuffer` from
API or stored in the file system. `cellFormula: true` should be set in the
options argument to `read` or `readFile`:
**`XLSX.read`**
```js
/* using read in Deno, `cellFormula` is in the second argument */
const ab = await (await fetch("test.xlsx")).arrayBuffer();
const workbook = XLSX.read(ab, { cellFormula: true });
// ------------------------------^^^^^^^^^^^^^^^^^
```
**`XLSX.readFile`**
```js
/* using readFile in Deno, add `cellFormula` to the second argument */
const workbook = XLSX.readFile("test.xlsx", { cellFormula: true });
// -------------------------------------------^^^^^^^^^^^^^^^^^
```
</TabItem>
</Tabs>
## Localization
Excel and other spreadsheet software will display different formula strings in
different countries and locales.
SheetJS parsers and writers do not attempt locale-specific changes.
For example, the Spanish Excel formula `=CONTAR(A1:C3;B4:D6)` is equivalent to
the SheetJS formula string `COUNT(A1:A3,B4:D6)` .
### Grammar
SheetJS formula function arguments are always separated with commas (`,`).
| SheetJS | `en-US` Excel | `es-ES` Excel |
|:-------------|:--------------|:--------------|
| `ATAN2(1,1)` | `=ATAN2(1,1)` | `=ATAN2(1;1)` |
### Function Names
Excel stores formula expressions using the English (United States) function
names. For non-English users, Excel uses a localized set of function names.
SheetJS operates at the file level. Excel stores formula expressions using the
English (United States) function names. For non-English users, Excel uses a
localized set of function names.
For example, when the computer language and region is set to Spanish, Excel
interprets `=CONTAR(A1:C3)` as if `CONTAR` is the `COUNT` function. However,
in the actual file, Excel stores `COUNT(A1:C3)`.
Function arguments are separated with commas. For example, the Spanish Excel
formula `=CONTAR(A1:C3;B4:D6)` is equivalent to the SheetJS formula string
`COUNT(A1:A3,B4:D6)`
[JSON Translation table](https://docs.sheetjs.com/fmla/table.json).
<details open>

@ -4,9 +4,6 @@ sidebar_label: Cell Comments
sidebar_position: 4
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
<details>
<summary><b>File Format Support</b> (click to show)</summary>
@ -37,7 +34,7 @@ support Excel styled comments or Excel legacy notes.
The letter R (R) marks features parsed but not written in the format.
:::tip pass
:::note pass
[SheetJS Pro](https://sheetjs.com/pro) supports comment rich text and styling.
@ -46,66 +43,25 @@ The letter R (R) marks features parsed but not written in the format.
</details>
Comments and notes are cell annotations. Cells with comments or notes are marked
with `◥` or `¬` in the upper-right corner.
with a small triangle or `¬` in the upper-right corner.
Excel notes are standalone text boxes with adjustable background colors and
support for rich text. Historically people "replied" to comments by adding text
to the end of existing comments.
Excel comments are simple text boxes that allow users to enter plain text. Users
can reply to comments. The replies are typically shown in a vertical stack.
can reply to comments.
#### Live Example
The following live example shows a spreadsheet with comments and a note.
The following screenshot shows a spreadsheet with comments and a note.
- The note is associated with cell A1 (the cell with the red triangle). It has
a green gradient background fill.
- The comments are associated with cell A2 (the cell with the blue `¬`). There
are 2 comments from different authors. A "Reply" box appears below the thread.
:::caution pass
Excel for Mac and other software that lack full support for threaded comments
will show a fallback note that includes each reply on a separate line.
:::
![Excel comments and notes](pathname:///comments/types.png)
<details open>
<summary><b>Live Example</b> (click to hide)</summary>
```jsx live
function SheetJSThreadedComments() {
return ( <button onClick={() => {
var ws = XLSX.utils.aoa_to_sheet([["SheetJS"], [5433795]], {dense: true});
var cell_A1 = (ws["!data"][0]??=[])[0]??={t:"z"}; // Cell A1
var cell_A2 = (ws["!data"][1]??=[])[0]??={t:"z"}; // Cell A2
/* normal comment in cell A1 */
cell_A1.c ??= [];
cell_A1.c.push({a:"SheetJS", t:"This is not threaded"});
/* threaded comment in cell A2 */
cell_A2.c ??= [];
/* add parts */
cell_A2.c.push({a:"SheetJS", t:"This is threaded", T: true});
var part = {a:"JSSheet", t:"This is also threaded"};
cell_A2.c.push({...part, T: true});
/* create workbook and export */
var wb = XLSX.utils.book_new(ws, "Sheet1");
XLSX.writeFile(wb, "SheetJSThreadedComments.xlsx");
}}>Click me to generate a sample file</button> );
}
```
</details>
:::info Application Support
:::info pass
Google Sheets "notes" do not currently support rich text or background colors.
@ -115,18 +71,19 @@ Apple Numbers supports "comments" but does not support "notes".
## Basic Structure
Cell comments are stored in the `c` property of cell objects. They are expected
to be arrays of fragments objects that include text and metadata.
Cell comments are objects stored in the `c` array of cell objects.
The comment content is split into parts based on the comment author.
The `a` field of each comment part is the author of the comment and the `t`
field is the plain text representation.
For example, the following snippet appends a cell comment into cell `A1`:
<Tabs groupId="sheetrep">
<TabItem value="sparse" label="Sparse Sheets">
```js title="Add cell comment to cell A1 in a sparse worksheet"
```js
/* get cell A1, creating an empty cell if necessary */
var cell = ws["A1"];
if(!cell) cell = ws["A1"] = { t: "z" };
if(!ws["A1"]) ws["A1"] = { t: "z" };
/* create comment array if it does not exist */
if(!cell.c) cell.c = [];
@ -141,78 +98,6 @@ var comment_part = {
cell.c.push(comment_part);
```
</TabItem>
<TabItem value="dense" label="Dense Sheets">
:::info pass
[`read`](/docs/api/parse-options), [`aoa_to_sheet`](/docs/api/utilities/array),
and other SheetJS methods will generate sparse worksheets by default.
[Dense mode worksheets](/docs/csf/sheet#dense-mode) require explicit opt-in.
:::
```js title="Add cell comment to cell A1 in a dense worksheet"
/* get cell A1, creating an empty cell if necessary */
var row = ws["!data"][0];
if(!row) row = ws["!data"][0] = [];
var cell = row[0];
if(!row[0]) cell = row[0] = { t: "z" };
/* create comment array if it does not exist */
if(!cell.c) cell.c = [];
/* create a comment part */
var comment_part = {
a: "SheetJS",
t: "I'm a little comment, short and stout!"
};
/* Add comment part to the comment array */
cell.c.push(comment_part);
```
</TabItem>
</Tabs>
### Fragment Properties
Fragments support the following properties:
| Key | Description |
|:----|:-------------------------------------------------------------------|
| `t` | [Text](#fragment-text) |
| `a` | [Author](#fragment-author) |
| `T` | If true, fragment is a [threaded comment](#threaded-comments) part |
#### Fragment Text
Each fragment must include text.
Note and comment fragment texts are concatenated to form the full text.
[Threaded comment](#threaded-comments) fragments are visually distinct.
:::tip pass
SheetJS CE supports plaintext comments.
[SheetJS Pro](https://sheetjs.com/pro) supports comment styling and rich text.
Google Sheets and Excel threaded comments currently do not support styling.
:::
#### Fragment Author
The fragment author is optional.
Note and comment fragment authors are not displayed in Excel.
[Threaded comment](#threaded-comments) fragment authors are displayed in the
thread in Excel.
:::note XLSB Author limits
XLSB enforces a 54 character limit on the Author name. Names longer than 54
@ -220,46 +105,6 @@ characters may cause issues with other formats.
:::
### Comment Properties
#### Visibility
The `hidden` property of the comment block indicates comment visibility. If set
to `true`, the comment will not be visible until users hover over the comment.
```js title="Mark a cell comment as hidden"
if(!cell.c) cell.c = [];
// highlight-next-line
cell.c.hidden = true;
cell.c.push({a:"SheetJS", t:"This comment will be hidden"});
```
<details>
<summary><b>Live Example</b> (click to show)</summary>
The following demo creates a worksheet with two comments. The comment in cell A1
will be visibile and the comment in cell A2 will be hidden.
```jsx live
function SheetJSComments2() {
return (<button onClick={() => {
var ws = XLSX.utils.aoa_to_sheet([["SheetJS"], [5433795]]);
if(!ws.A1.c) ws.A1.c = [];
ws.A1.c.push({a:"SheetJS", t:"This comment is visible"});
if(!ws.A2.c) ws.A2.c = [];
ws.A2.c.hidden = true;
ws.A2.c.push({a:"SheetJS", t:"This comment will be hidden"});
var wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
XLSX.writeFile(wb, "SheetJSComments2.xlsx");
}}>Click me to generate a sample file</button>);
}
```
</details>
## Demos
#### Export
@ -327,6 +172,44 @@ function SheetJSParseComments(props) {
</details>
## Visibility
The `hidden` property of the comment block indicates comment visibility. If set
to `true`, the comment will not be visible until users hover over the comment.
```js
if(!cell.c) cell.c = [];
// highlight-next-line
cell.c.hidden = true;
cell.c.push({a:"SheetJS", t:"This comment will be hidden"});
```
<details>
<summary><b>Live Example</b> (click to show)</summary>
The following demo creates a worksheet with two comments. The comment in cell A1
will be visibile and the comment in cell A2 will be hidden.
```jsx live
function SheetJSComments2() {
return (<button onClick={() => {
var ws = XLSX.utils.aoa_to_sheet([["SheetJS"], [5433795]]);
if(!ws.A1.c) ws.A1.c = [];
ws.A1.c.push({a:"SheetJS", t:"This comment is visible"});
if(!ws.A2.c) ws.A2.c = [];
ws.A2.c.hidden = true;
ws.A2.c.push({a:"SheetJS", t:"This comment will be hidden"});
var wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
XLSX.writeFile(wb, "SheetJSComments2.xlsx");
}}>Click me to generate a sample file</button>);
}
```
</details>
## Threaded Comments
Threaded comments are plain text comment snippets with author metadata and
@ -334,7 +217,7 @@ parent references. They are supported in XLSX, XLSB, and NUMBERS files.
To mark a comment as threaded, each comment part must have a true `T` property:
```js title="Create a threaded comment with a reply"
```js
if(!cell.c) cell.c = [];
var part1 = {
@ -354,8 +237,34 @@ var part2 = {
cell.c.push({ ...part2, T: true});
```
:::caution pass
There is no Active Directory or Office 365 metadata associated with authors.
:::
<details open>
<summary><b>Live Example</b> (click to hide)</summary>
```jsx live
function SheetJSThreadedComments() {
return ( <button onClick={() => {
var ws = XLSX.utils.aoa_to_sheet([["SheetJS"], [5433795]]);
/* normal comment */
if(!ws.A1.c) ws.A1.c = [];
ws.A1.c.push({a:"SheetJS", t:"This is not threaded"});
/* threaded comment */
if(!ws.A2.c) ws.A2.c = [];
/* add parts */
ws.A2.c.push({a:"SheetJS", t:"This is threaded", T: true});
var part = {a:"JSSheet", t:"This is also threaded"};
ws.A2.c.push({...part, T: true});
/* create workbook and export */
var wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
XLSX.writeFile(wb, "SheetJSThreadedComments.xlsx");
}}>Click me to generate a sample file</button> );
}
```
</details>

@ -15,31 +15,31 @@ is stored as the formatted value. The formatted values can be generated from
many different values and number formats. SheetJS parsers expose options to
control value parsing and number format speculation.
| Formats | Basic | Storage Representation |
|:------------------|:-----:|:--------------------------|
| XLSX / XLSM | ✔ | Number Format Code |
| XLSB | ✔ | Number Format Code |
| XLS | ✔ | Number Format Code |
| XLML | ✔ | Number Format Code |
| SYLK | R | Number Format Code |
| ODS / FODS / UOS | ✔ | XML Tokens |
| NUMBERS | | Binary encoding |
| WK1 | + | Fixed set of formats |
| WK3 / WK4 | | Binary encoding |
| WKS Lotus | + | Fixed set of formats |
| WKS Works | + | Fixed set of formats |
| WQ1 | + | Fixed set of formats |
| WQ2 | | Binary encoding |
| WB1 / WB2 / WB3 | | Binary encoding |
| QPW | + | Binary encoding |
| DBF | | Inferred from field types |
| HTML | ! | Special override |
| CSV | * | N/A |
| PRN | * | N/A |
| DIF | * | N/A |
| RTF | * | N/A |
| Formats | Basic | Storage Representation |
|:------------------|:-----:|:-----------------------|
| XLSX / XLSM | ✔ | Number Format Code |
| XLSB | ✔ | Number Format Code |
| XLS | ✔ | Number Format Code |
| XLML | ✔ | Number Format Code |
| SYLK | R | Number Format Code |
| ODS / FODS / UOS | ✔ | XML Tokens |
| NUMBERS | | Binary encoding |
| WK1 | + | Fixed set of formats |
| WK3 / WK4 | | Binary encoding |
| WKS Lotus | + | Fixed set of formats |
| WKS Works | + | Fixed set of formats |
| WQ1 | + | Fixed set of formats |
| WQ2 | | Binary encoding |
| WB1 / WB2 / WB3 | | Binary encoding |
| QPW | + | Binary encoding |
| DBF | | Implied by field types |
| HTML | ! | Special override |
| CSV | * | N/A |
| PRN | * | N/A |
| DIF | * | N/A |
| RTF | * | N/A |
Plus symbols (+) mark formats with limited support. The QPW (Quattro Pro) parser
(+) mark formats with limited support. The QPW (Quattro Pro Workbooks) parser
supports the built-in date and built-in time formats but does not support
custom number formats. [Date and Time support](/docs/csf/features/dates) in
modern Excel formats requires limited number format support to distinguish date

@ -5,7 +5,7 @@ hide_table_of_contents: true
---
The main SheetJS method for reading files is `read`. It expects developers to
supply the actual data in a [supported representation](#input-type).
supply the actual data in a supported representation.
The `readFile` helper method accepts a filename and tries to read the specified
file using standard APIs. *It does not work in web browsers!*
@ -38,130 +38,64 @@ includes additional instructions for non-standard use cases.
:::
:::tip pass
The SheetJS file format import codecs focus on raw data. Not all codecs support
all features. Features not described in the documentation may not be extracted.
[SheetJS Pro](https://sheetjs.com/pro) offers support for additional features,
including styling, images, graphs, and PivotTables.
:::
## Parsing Options
The read functions accept an options argument:
| Option Name | Default | Description |
|:--------------|:--------|:---------------------------------------------------|
| `type` | | [Input data representation](#input-type) |
| `raw` | `false` | Disable [value parsing in plaintext formats](#raw) |
| `dense` | `false` | If true, [generate dense worksheets](#dense) |
| `codepage` | | Use specified [code page encoding](#codepage) |
| `cellFormula` | `true` | Save [formulae to the `f` field](#formulae) |
| `cellHTML` | `true` | Parse text and [save HTML to the `h` field](#html) |
| `cellNF` | `false` | Save [number format to the `z` field](#text) |
| `cellStyles` | `false` | Save [style/theme info to the `s` field](#style) |
| `cellText` | `true` | Save [formatted text to the `w` field](#text) |
| `cellDates` | `false` | [Generate proper date (type `d`) cells](#dates) |
| `dateNF` | | If specified, [override date code 14](#dates) |
| `sheetStubs` | `false` | [Create cells of type `z` for stub cells](#stubs) |
| `sheetRows` | `0` | If >0, read the [specified number of rows](#range) |
| `bookDeps` | `false` | If true, parse calculation chains |
| `bookFiles` | `false` | Add [raw files](#files) to book object |
| `bookProps` | `false` | If true, [only parse book metadata](#metadata) |
| `bookSheets` | `false` | If true, [only parse sheet names](#metadata) |
| `bookVBA` | `false` | If true, generate [VBA blob](#vba) |
| `password` | `""` | If specified, [decrypt workbook](#password) |
| `WTF` | `false` | [Do not suppress worksheet parsing errors](#wtf) |
| `sheets` | | Only parse [specified sheets](#sheets) |
| `nodim` | `false` | If true, calculate [worksheet ranges](#range) |
| `PRN` | `false` | If true, [allow parsing of PRN files](#prn) |
| `xlfn` | `false` | Use [raw formula function names](#formulae) |
| `FS` | | [DSV Field Separator override](#dsv) |
| `UTC` | `true` | Parse [text dates and times using UTC](#tz) |
| Option Name | Default | Description |
|:------------|:--------|:-----------------------------------------------------|
|`type` | | [Input data representation](#input-type) |
|`raw` | `false` | If true, plain text parsing will not parse values ** |
|`dense` | `false` | If true, use a [dense sheet representation](#dense) |
|`codepage` | | If specified, use code page when appropriate ** |
|`cellFormula`| `true` | Save [formulae to the `.f` field](#formulae) |
|`cellHTML` | `true` | Parse rich text and save HTML to the `.h` field |
|`cellNF` | `false` | Save number format string to the `.z` field |
|`cellStyles` | `false` | Save style/theme info to the `.s` field |
|`cellText` | `true` | Generated formatted text to the `.w` field |
|`cellDates` | `false` | Store dates as type `d` (default is `n`) |
|`dateNF` | | If specified, use the string for date code 14 ** |
|`sheetStubs` | `false` | Create cell objects of type `z` for stub cells |
|`sheetRows` | `0` | If >0, read the [specified number of rows](#range) |
|`bookDeps` | `false` | If true, parse calculation chains |
|`bookFiles` | `false` | If true, add raw files to book object ** |
|`bookProps` | `false` | If true, only parse enough to get book metadata ** |
|`bookSheets` | `false` | If true, only parse enough to get the sheet names |
|`bookVBA` | `false` | If true, generate [VBA blob](#vba) |
|`password` | `""` | If defined and file is encrypted, use password ** |
|`WTF` | `false` | If true, throw errors on unexpected file features ** |
|`sheets` | | If specified, only parse specified sheets ** |
|`nodim` | `false` | If true, calculate [worksheet ranges](#range) |
|`PRN` | `false` | If true, allow parsing of PRN files ** |
|`xlfn` | `false` | If true, [preserve prefixes](#formulae) in formulae |
|`FS` | | DSV Field Separator override |
|`UTC` | `true` | If explicitly false, parse text dates in local time |
### Cell-Level Options
#### Dates
By default, for consistency with spreadsheet applications, date cells are stored
as numeric cells (type `n`) with special number formats. If `cellDates` is
enabled, date codes are converted to proper Date objects.
Excel file formats (including XLSX, XLSB, and XLS) support a locale-specific
date format, typically stored as date code 14 or the string `m/d/yy`. The
formatted text for some cells will change based on the computer locale. SheetJS
parsers use the `en-US` form by default. If the `dateNF` option is set, that
number format string will be used.
["Dates and Times"](/docs/csf/features/dates) covers features in more detail.
#### Formulae
For some file formats, the `cellFormula` option must be explicitly enabled to
ensure that formulae are extracted.
Newer Excel functions are serialized with the `_xlfn.` prefix, hidden from the
user. By default, the file parsers will strip `_xlfn.` and similar prefixes.
If the `xlfn` option is enabled, the prefixes will be preserved.
[The "Formulae" docs](/docs/csf/features/formulae#prefixed-future-functions)
covers this in more detail.
#### Formatted Text {#text}
Many common spreadsheet formats (including XLSX, XLSB, and XLS) store numeric
values and number formats. Applications are expected to use the number formats
to display currency strings, dates, and other values.
Under the hood, parsers use the [SSF Number Formatter](/docs/constellation/ssf)
library to generated formatted text.
By default, formatted text is generated. If the `cellText` option is false,
formatted text will not be written.
By default, cell number formats are not preserved. If the `cellNF` option is
enabled, number format strings will be saved to the `z` field of cell objects.
["Number Formats"](/docs/csf/features/nf) covers the features in more detail.
:::note pass
Even if `cellNF` is false, formatted text will be generated and saved to `w`.
:::
#### Text and Cell Styling {#style}
By default, SheetJS CE parsers focus on data extraction.
If the `cellStyles` option is `true`, other styling metadata including
[row](/docs/csf/features/rowprops) and [column](/docs/csf/features/colprops)
properties will be parsed.
:::tip pass
[SheetJS Pro](https://sheetjs.com/pro) offers cell / text styling, conditional
formatting and additional styling options.
:::
#### HTML Formatted Text {#html}
Spreadsheet applications support a limited form of rich text styling.
If the `cellHTML` option is `true`, some file parsers will attempt to translate
the rich text to standard HTML with inner tags for bold text and other styles.
:::tip pass
[SheetJS Pro](https://sheetjs.com/pro) offers additional styling options,
conversions for all supported file formats, and whole-worsheet HTML generation.
:::
### Sheet-Level Options
- Even if `cellNF` is false, formatted text will be generated and saved to `.w`
- In some cases, sheets may be parsed even if `bookSheets` is false.
- Excel aggressively tries to interpret values from CSV and other plain text.
This leads to surprising behavior! The `raw` option suppresses value parsing.
- `bookSheets` and `bookProps` combine to give both sets of information
- `Deps` will be an empty object if `bookDeps` is false
- `bookFiles` behavior depends on file type:
* `keys` array (paths in the ZIP) for ZIP-based formats
* `files` hash (mapping paths to objects representing the files) for ZIP
* `cfb` object for formats using CFB containers
- By default all worksheets are parsed. `sheets` restricts based on input type:
* number: zero-based index of worksheet to parse (`0` is first worksheet)
* string: name of worksheet to parse (case insensitive)
* array of numbers and strings to select multiple worksheets.
- `codepage` is applied to BIFF2 - BIFF5 files without `CodePage` records and to
CSV files without BOM in `type:"binary"`. BIFF8 XLS always defaults to 1200.
- `PRN` affects parsing of text files without a common delimiter character.
- Currently only XOR encryption is supported. Unsupported error will be thrown
for files employing other encryption methods.
- `WTF` is mainly for development. By default, the parser will suppress read
errors on single worksheets, allowing you to read from the worksheets that do
parse properly. Setting `WTF:true` forces those errors to be thrown.
- `UTC` applies to CSV, Text and HTML formats. When explicitly set to `false`,
the parsers will assume the files are specified in local time. By default, as
is the case for other file formats, dates and times are interpreted in UTC.
#### Dense
@ -196,16 +130,17 @@ The `nodim` option instructs the parser to ignore self-reported ranges and use
the actual cells in the worksheet to determine the range. This addresses known
issues with non-compliant third-party exporters.
#### Stubs
#### Formulae
Some file formats, including XLSX and XLS, can specify cells without cell data.
For example, cells covered by a [merged cell block](/docs/csf/features/merges)
are technically invalid but files may include metadata.
For some file formats, the `cellFormula` option must be explicitly enabled to
ensure that formulae are extracted.
By default, the cells are skipped. If the `sheetStubs` option is `true`, these
cells will be parsed as [stub cells](/docs/csf/cell#cell-types)
Newer Excel functions are serialized with the `_xlfn.` prefix, hidden from the
user. SheetJS will strip `_xlfn.` normally. The `xlfn` option preserves them.
[The "Formulae" docs](/docs/csf/features/formulae#prefixed-future-functions)
covers this in more detail.
### Book-Level Options
["Formulae"](/docs/csf/features/formulae) covers the features in more detail.
#### VBA
@ -225,166 +160,18 @@ new blob from the XLS CFB container that works in XLSM and XLSB files.
</details>
#### Workbook Metadata {#metadata}
By default, the data from each worksheet is parsed.
If any of the following options are passed, parsers will not parse sheet data.
They will parse enough of the workbook to extract the requested information.
| Option | Extracted Data |
|:-------------|:--------------------|
| `bookProps` | Workbook properties |
| `bookSheets` | Worksheet names |
The options apply to XLSX, XLSB, XLS and XLML parsers.
#### Worksheets {#sheets}
By default, all worksheets are parsed. The `sheets` option limits which sheets
are parsed.
If the `sheets` option is a number, the number is interpreted as a zero-based
index. For example, `sheets: 2` instructs the parser to read the third sheet.
If the `sheets` option is text, the string is interpreted as a worksheet name.
The name is case-insensitive. `sheets: "Sheet1"` instructs the parser to read
the worksheet named "Sheet1".
If the `sheets` option is an array of numbers and text, each worksheets will
be parsed. `sheets: [2, "Sheet1"]` instructs the parser to read the third sheet
and the sheet named "Sheet1". If the third worksheet is coincidentally named
"Sheet1", only one worksheet will be parsed
### File-Level Options
#### Password Protection {#password}
SheetJS CE currently supports XOR encryption in XLS files. Errors will be thrown
when trying to parse files using unsupported encryption methods.
:::tip pass
[SheetJS Pro](https://sheetjs.com/pro) offers support for additional encryption
schemes, including the AES-CBC schemes used in XLSX / XLSM / XLSB files and the
RC4 schemes used in newer XLS files.
:::
#### Lotus Formatted Text (PRN) {#prn}
Lotus Formatted Text (`PRN`) worksheets are plain text files that do not include
delimiter characters. Each cell in a column has the same width.
If the `PRN` option is set, the plaintext parser will attempt to parse some
plaintext files as if they follow the `PRN` format.
:::note pass
If the `PRN` option is set, text files that do not include commas or semicolons
or other common delimiters may not be parsed as expected.
This option should not be enabled unless it is known that the file was exported
from Lotus 1-2-3 or from Excel using the "Lotus Formatted Text (`PRN`)" format.
:::
#### Value Parsing {#raw}
Spreadsheet software including Excel aggressively try to interpret values from
CSV and other plain text. This leads to surprising behavior[^1]!
If the `raw` option is true, value parsing will be suppressed. All cells values
are treated as strings.
The `raw` option affects the following formats: HTML, CSV, PRN, DIF, RTF.
The `raw` option does not affect XLSX, XLSB, XLS and other file formats that
support explicit value typing.
:::note pass
See [Issue #3331](https://git.sheetjs.com/sheetjs/sheetjs/issues/3145) in the
SheetJS CE bug tracker for more details.
:::
#### Code Page Encoding {#codepage}
Spreadsheet applications support a number of legacy encodings. Plaintext files
will appear different when opened in different computers in different regions.
By default, the parsers use the most common "English (United States)" encodings.
The `codepage` option controls the encoding in BIFF2 - BIFF5 XLS files without
`CodePage` records, some legacy formats including DBF, and in CSV files without
BOM in `type: "binary"`. BIFF8 XLS always defaults to 1200.
The `codepage` support library is not guaranteed to be loaded by default. The
["Installation"](/docs/getting-started/installation/) section describes how to
install and load the support library.
See ["Legacy Codepages"](/docs/constellation/codepage) for more details.
#### Date Processing {#tz}
Plaintext formats may include date and time values without timezone info. The
time `12:30 AM` is ambiguous.
In the wild, there are two popular approaches:
A) Spreadsheet software typically interpret time values using local timezones.
When opening a file in New York, `12:30 AM` will be parsed as `12:30 AM ET`.
When opening a file in Los Angeles, the time will be parsed as `12:30 AM PT`.
B) APIs use [UTC](https://en.wikipedia.org/wiki/Coordinated_Universal_Time), the
most popular global time standard. `12:30 AM` will be parsed as the absolute
moment in time corresponding to `8:30 PM EDT` or `7:30 PM EST`.
By default, the parsers assume files are specified in UTC. When the `UTC` option
is explicitly set to `false`, dates and times are interpreted in timezone of the
web browser or JavaScript engine.
#### Delimiter-Separated Values {#dsv}
The plaintext parser applies a number of heuristics to determine if files are
CSV (fields separated by commas), TSV (fields separated by tabs), PSV (fields
separated by `|`) or SSV (fields separated by `;`). The heuristics are based on
the presence of characters not in a double-quoted value.
The `FS` option instructs the parser to use the specified delimiter if multiple
delimiter characters are in the text. This bypasses the default heuristics.
#### Internal Files {#files}
Some file formats are structured as larger containers that include sub-files.
For example, XLSX files are ZIP files with XML sub-files.
If the `bookFiles` option is `true`, each sub-file will be preserved in the
workbook. The behavior depends on file type:
- `keys` array (paths in the ZIP) for ZIP-based formats
- `files` hash (mapping paths to objects representing the files) for ZIP
- `cfb` object for formats using CFB containers
#### Parsing Errors {#wtf}
By default, the workbook parser will suppress errors when parsing worksheets.
This ensures the valid worksheets from a multi-sheet workbook are parsed.
If the `WTF` option is enabled, the errors will not be suppressed.
### Input Type
The `type` parameter for `read` controls how data is interpreted:
| `type` | expected input |
|:---------|:----------------------------------------------------------------|
| `base64` | string: Base64 encoding of the file |
| `binary` | string: binary string (byte `n` is `data.charCodeAt(n)`) |
| `string` | string: JS string (only appropriate for UTF-8 text formats) |
| `buffer` | nodejs Buffer |
| `array` | array: array of 8-bit unsigned integers (byte `n` is `data[n]`) |
| `file` | string: path of file that will be read (nodejs only) |
| `type` | expected input |
|:-----------|:----------------------------------------------------------------|
| `"base64"` | string: Base64 encoding of the file |
| `"binary"` | string: binary string (byte `n` is `data.charCodeAt(n)`) |
| `"string"` | string: JS string (only appropriate for UTF-8 text formats) |
| `"buffer"` | nodejs Buffer |
| `"array"` | array: array of 8-bit unsigned integers (byte `n` is `data[n]`) |
| `"file"` | string: path of file that will be read (nodejs only) |
Some common types are automatically deduced from the data input type, including
NodeJS `Buffer` objects, `Uint8Array` and `ArrayBuffer` objects, and arrays of
@ -459,6 +246,3 @@ were CSV or TSV. SheetJS attempts to replicate that behavior.
</details>
[^1]: The gene [`SEPT1`](https://en.wikipedia.org/wiki/SEPTIN1) was renamed to
`SEPTIN1` to avoid Excel value interpretations: the string `SEPT1` is parsed as
the date "September 1".

@ -8,15 +8,13 @@ import current from '/version.js';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
import Link from '@docusaurus/Link';
The main SheetJS method for writing workbooks is `write`. It accepts a SheetJS
[workbook object](/docs/csf/book) and returns the file data stored in common
[JavaScript data representations](#output-type). Scripts calling `write` are
expected to write or share files using platform-specific APIs.
The main SheetJS method for writing workbooks is `write`. Scripts receive common
[JavaScript data representations](#output-type) and are expected to write or
share files using platform-specific APIs.
The `writeFile` helper method accepts a filename and tries to write to a local
file using [standard local file APIs](/docs/demos/local/file).
file using [standard APIs](/docs/demos/local/file).
**Export a SheetJS workbook object in a specified file format**
@ -26,9 +24,9 @@ var file_data = XLSX.write(wb, opts);
`write` attempts to write the workbook `wb` and return the file.
The `options` argument is required. It must specify
- [`bookType`](#bookType): file format of the exported file
- [`type`](#output-type): return value type
The `options` argument is required. It must specify
- [`bookType`](#supported-output-formats) (file format of the exported file)
- [`type`](#output-type) (return value type)
**Export a SheetJS workbook object and attempt to write a local file**
@ -41,18 +39,17 @@ XLSX.writeFile(wb, filename, options);
In browser-based environments, it will attempt to force a client-side download.
It also supports NodeJS, ExtendScript applications, and Chromium extensions.
The `options` argument is optional.
If `options` is omitted or if `bookType` is missing from the `options` object,
the output file format will be [inferred from the filename](#extension).
the output file format will be deduced from the filename extension.
**Special functions for exporting data in the XLSX format**
```js
/* `write` (XLSX only) */
// limited form of `write`
var file_data = XLSX.writeXLSX(wb, options);
/* `writeFile` (XLSX only) */
// limited form of `writeFile`
XLSX.writeFileXLSX(wb, filename, options);
```
@ -63,357 +60,111 @@ For websites that exclusively export to XLSX, these functions can reduce the
size of the production site. The general `write` and `writeFile` functions are
more appropriate when exporting to XLS or XLSB or other formats.
<details>
<summary><b>NodeJS-specific methods</b> (click to show)</summary>
**Export a workbook and attempt to write a local file using `fs.writeFile`**
```js
/* callback equivalent of `XLSX.writeFile(wb, filename)` */
// callback equivalent of `XLSX.writeFile`
XLSX.writeFileAsync(filename, wb, cb);
/* callback equivalent of `XLSX.writeFile(wb, filename, options)` */
// callback equivalent with options argument
XLSX.writeFileAsync(filename, wb, options, cb);
```
`writeFileAsync` attempts to write `wb` to `filename` and invoke the callback
`cb` on completion.
When an `options` object is specified, it is expected to be the third argument.
This method only works in NodeJS. It uses `fs.writeFile` under the hood.
This method only works in NodeJS and uses `fs.writeFile` under the hood.
</details>
:::note Recommendation
`writeFile` wraps a number of export techniques, making it suitable for web
browsers, [Photoshop and InDesign](/docs/demos/extensions/extendscript), and
server-side platforms including NodeJS. It does not work in other environments
with more advanced export methods.
`writeFile` wraps a number of export techniques, making it suitable for browser
downloads, NodeJS, ExtendScript apps, and Chromium extensions. It does not work
in other environments with more advanced export methods.
`write` returns raw bytes or strings that can be exported with platform-specific
APIs in [Desktop apps](/docs/demos/desktop), [Mobile apps](/docs/demos/mobile),
[Servers](/docs/demos/net/server), and [extensions](/docs/demos/extensions).
The `write` method returns raw bytes or strings that can be exported in
[Desktop apps](/docs/demos/desktop/) , [Mobile apps](/docs/demos/mobile) , and
[Servers](/docs/demos/net/server).
The [demos](/docs/demos) preferentially use `writeFile`. When `writeFile` is not
supported, the demos show file creation using `write` and platform APIs.
:::
:::tip pass
The SheetJS file format export codecs focus on raw data. Not all codecs support
all features. Features not described in the documentation may not be serialized.
[SheetJS Pro](https://sheetjs.com/pro) offers support for additional features,
including styling, images, graphs, and PivotTables.
:::
## Writing Options
The write functions accept an options argument:
| Option Name | Default | Description |
|:--------------|:---------|:---------------------------------------------------|
| `type` | | [Output data encoding](#output-type) |
| `cellDates` | `false` | Convert [numeric date codes to strings](#dates) |
| `cellStyles` | `false` | Export [style/theme info](#style) |
| `codepage` | | Use specified [code page encoding](#codepage) |
| `bookSST` | `false` | Generate [Shared String Table](#sst) |
| `bookType` | `"xlsx"` | [Type of Workbook](#supported-output-formats) |
| `bookVBA` | | [Add VBA blob from workbook object](#vba) |
| `WTF` | `false` | [Do not suppress warnings and errors](#wtf) |
| `sheet` | `""` | [Export the named sheet](#sheet) |
| `compression` | `false` | [Try to reduce file size](#compression) |
| `Props` | | Override [workbook properties](#props) |
| `themeXLSX` | | Override [theme XML for XLSX/XLSB/XLSM](#theme) |
| `ignoreEC` | `true` | [Suppress "number as text" errors](#ec) |
| `numbers` | | [Payload for NUMBERS export](#numbers) |
| `FS` | `","` | [Field Separator](#dsv) for CSV and Text exports |
| `RS` | `"\n"` | [Record Separator](#dsv) for CSV and Text exports |
## Supported Output Formats
<Link id="bookType"/>
For broad compatibility with third-party tools, SheetJS CE supports many output
formats. The writer will select the file type based on the `bookType` option:
| `bookType` | extension | sheets | Description |
|:-----------|:-----------|:-------|:--------------------------------|
| `xlsx` | `.xlsx` | multi | Excel 2007+ XML Format |
| `xlsm` | `.xlsm` | multi | Excel 2007+ Macro XML Format |
| `xlsb` | `.xlsb` | multi | Excel 2007+ Binary Format |
| `biff8` | `.xls` | multi | Excel 97-2004 Workbook Format |
| `biff5` | `.xls` | multi | Excel 5.0/95 Workbook Format |
| `biff4` | `.xls` | single | Excel 4.0 Worksheet Format |
| `biff3` | `.xls` | single | Excel 3.0 Worksheet Format |
| `biff2` | `.xls` | single | Excel 2.0 Worksheet Format |
| `xlml` | `.xls` | multi | Excel 2003-2004 (SpreadsheetML) |
| `numbers` | `.numbers` | multi | Numbers 3.0+ Spreadsheet |
| `ods` | `.ods` | multi | OpenDocument Spreadsheet |
| `fods` | `.fods` | multi | Flat OpenDocument Spreadsheet |
| `wk3` | `.wk3` | multi | Lotus Workbook (WK3) |
| `csv` | `.csv` | single | Comma Separated Values |
| `txt` | `.txt` | single | UTF-16 Unicode Text (TXT) |
| `sylk` | `.sylk` | single | Symbolic Link (SYLK) |
| `html` | `.html` | single | HTML Document |
| `dif` | `.dif` | single | Data Interchange Format (DIF) |
| `dbf` | `.dbf` | single | dBASE II + VFP Extensions (DBF) |
| `wk1` | `.wk1` | single | Lotus Worksheet (WK1) |
| `rtf` | `.rtf` | single | Rich Text Format (RTF) |
| `prn` | `.prn` | single | Lotus Formatted Text |
| `eth` | `.eth` | single | Ethercalc Record Format (ETH) |
If the output format supports multiple worksheets, the workbook writer will try
to export each worksheet. If the format only supports one worksheet, the writer
will export the first worksheet. If the [`sheet` option](#sheet) is a string,
the writer will use the named sheet.
#### Output Format inference from File Extension {#extension}
`writeFile` will automatically guess the output file format based on the file
extension if `bookType` is not specified.
| extension | Description |
|:-----------|:--------------------------------|
| `.csv` | Comma Separated Values |
| `.dbf` | dBASE II + VFP Extensions (DBF) |
| `.dif` | Data Interchange Format (DIF) |
| `.eth` | Ethercalc Record Format (ETH) |
| `.fods` | Flat OpenDocument Spreadsheet |
| `.html` | HTML Document |
| `.numbers` | Numbers 3.0+ Spreadsheet |
| `.ods` | OpenDocument Spreadsheet |
| `.prn` | Lotus Formatted Text |
| `.rtf` | Rich Text Format (RTF) |
| `.sylk` | Symbolic Link (SYLK) |
| `.txt` | UTF-16 Unicode Text (TXT) |
| `.wk1` | Lotus Worksheet (WK1) |
| `.wk3` | Lotus Workbook (WK3) |
| `.xls` | Excel 97-2004 Workbook Format |
| `.xlsb` | Excel 2007+ Binary Format |
| `.xlsm` | Excel 2007+ Macro XML Format |
| `.xlsx` | Excel 2007+ XML Format |
## Output Type
The `type` option specifies the JS form of the output:
| `type` | output |
|----------|-----------------------------------------------------------------|
| `base64` | string: Base64 encoding of the file |
| `binary` | string: binary string (byte `n` is `data.charCodeAt(n)`) |
| `string` | string: JS string (not compatible with binary formats) |
| `buffer` | NodeJS `Buffer` or `Uint8Array` |
| `array` | `ArrayBuffer` or array of 8-bit unsigned int |
| `file` | (attempt to download a file) |
:::note pass
For compatibility with Excel, `csv` output will always include the UTF-8 byte
order mark ("BOM").
The raw [`sheet_to_csv` method](/docs/api/utilities/csv#csv-output) will return
JavaScript strings without the UTF-8 BOM.
:::
## Other Options
### Cell-Level Options
#### Dates
Plaintext files store dates using formatted strings. XLS and most Excel file
formats store dates using numeric date codes.
XLSX and ODS/FODS support both numeric date codes and ISO 8601 date strings.
By default, when both numeric date codes and date strings are supported, the
writers will export date codes. Date cells (type `d`) will be converted to
numeric cells.
If the `cellDates` option is set, writers will export date strings. If number
formats include [date tokens](/docs/csf/features/nf#dates-and-times), numeric
cells (type `n`) will be converted to date cells.
:::caution pass
Generated files are not guaranteed to work with third-party spreadsheet readers!
Third-party tools may ignore cells or reject files that use proper date cells.
:::
["Dates and Times"](/docs/csf/features/dates) covers features in more detail.
#### Text and Cell Styling {#style}
By default, SheetJS CE writers focus on data preservation.
If the `cellStyles` option is `true`, other styling metadata including
[row](/docs/csf/features/rowprops) and [column](/docs/csf/features/colprops)
will be exported.
:::tip pass
[SheetJS Pro](https://sheetjs.com/pro) offers cell / text styling, conditional
formatting and additional styling options.
:::
### Sheet-Level Options
#### Error Checking {#ec}
By default, Excel warns users when creating text cells when the text could be
parsed as a number. People and software systems commonly use text to store US
ZIP Codes, as many ZIP Codes in New Jersey start with "0".
By default, SheetJS writers add special marks to instruct Excel not to elicit
warnings on cells containing text that look like numbers.
Due to a bug in Excel, "Text to Columns" and other features may lead to crashes
in files where error conditions are ignored. If the `ignoreEC` option is
explicitly set to `false`, SheetJS writers will not add the offending marks.
### Book-Level Options
#### VBA
When exporting to file formats that support VBA (XLSX, XLSB, XLS), macros are
not guaranteed to be exported by default. The `bookVBA` option should be set
to a truthy value (`true` or `1`).
The ["VBA"](/docs/csf/features/vba) section explains the feature in more detail.
#### Workbook Properties {#props}
When exporting to a file format that supports workbook properties, the SheetJS
export codecs will look in the workbook object for file properties.
If the `Props` option is specified, the export codecs will ignore properties
from the workbook object.
The ["File Properties"](/docs/csf/features/props) section lists the supported
file properties.
### File-Level Options
#### Compression
XLSX, XLSB, NUMBERS, and ODS files use ZIP containers. The ZIP container format
supports different levels of compression. Spreadsheet software typically use
compression when exporting files.
By default, SheetJS writers optimize for speed. Exports are faster but the
generated files are larger.
If the `compression` option is set, writers will optimize for file size. Exports
are smaller but will take more time.
#### Target Sheet {#sheet}
Some output formats support multiple worksheets. By default, the SheetJS export
codecs will export all worksheets.
Other output formats, including CSV and legacy Excel and Lotus worksheet files,
only support one worksheet. By default, the SheetJS export codecs will use the
first worksheet from the workbook.
If the `sheet` option is set to the name of a valid worksheet in the workbook,
the SheetJS writers will use the named sheet even if it is not the first sheet.
#### Shared String Table (SST) {#sst}
XLSX, XLS, and XLSB can store text cells in two ways.
By default, the writers use "inline" strings. The content of each text cell is
stored in the cell representation. This approach is conceptually simple, but it
uses features that may not be supported in iOS Numbers and other apps.
If the `bookSST` option is set, writers use the "shared string table" (SST)
feature. A separate lookup table houses each text string and cells store indices
into the SST. Multiple cells can point to the same SST entry.
Exporting with SST is typically slower and more memory intensive, but the files
may be smaller and have better compatibility with third-party software.
#### Delimiter-Separated Values {#dsv}
The SheetJS CSV writer uses commas to separate fields within a row and adds line
separators between rows.
The characters used to separate fields and rows can be controlled through the
`FS` ("field separator") and `RS` ("row separator") options.
["CSV and Text"](/docs/api/utilities/csv#delimiter-separated-output) discusses
the options in more detail.
#### Code Page Encoding {#codepage}
Spreadsheet applications support a number of legacy encodings. Plaintext files
will appear different when opened in different computers in different regions.
By default, the writers use the most common "English (United States)" encodings.
The `codepage` option controls the encoding in BIFF2 - BIFF5 XLS files without
`CodePage` records, some legacy formats including DBF, and in CSV files without
BOM in `type: "binary"`.
Characters missing from the specified encoding will be replaced with underscore
characters (`_`).
The `codepage` support library is not guaranteed to be loaded by default. The
["Installation"](/docs/getting-started/installation/) section describes how to
install and load the support library.
See ["Legacy Codepages"](/docs/constellation/codepage) for more details.
#### XLSX and XLSB Theme {#theme}
By default, the SheetJS XLSX and XLSB output codecs use a predetermined theme.
This simplifies [column widths](/docs/csf/features/colprops#column-widths) and
other features that depend on font metrics.
If the `themeXLSX` option is a string, the XLSB and XLSX codecs will suppress
the default theme. The default theme `xl/theme/theme1.xml` will be overridden.
#### Writing Errors {#wtf}
If the `WTF` option is enabled, workbook writers will show warnings and errors
when workbooks use features that may be considered unsafe. For example, some
file features are only supported in specific versions of Excel.
#### NUMBERS Exports {#numbers}
| Option Name | Default | Description |
| :---------- | -------: | :------------------------------------------------- |
|`type` | | Output data encoding (see Output Type below) |
|`cellDates` | `false` | Store dates as type `d` (default is `n`) |
|`cellStyles` | `false` | Save style/theme info to the `.s` field |
|`codepage` | | If specified, use code page when appropriate ** |
|`bookSST` | `false` | Generate Shared String Table ** |
|`bookType` | `"xlsx"` | Type of Workbook (see below for supported formats) |
|`bookVBA` | | Add VBA blob from workbook object to the file ** |
|`WTF` | `false` | If true, throw errors on unexpected features ** |
|`sheet` | `""` | Name of Worksheet for single-sheet formats ** |
|`compression`| `false` | Use ZIP compression for ZIP-based formats ** |
|`Props` | | Override workbook properties when writing ** |
|`themeXLSX` | | Override theme XML when writing XLSX/XLSB/XLSM ** |
|`ignoreEC` | `true` | Suppress "number as text" errors ** |
|`numbers` | | Payload for NUMBERS export ** |
|`FS` | `","` | "Field Separator" delimiter between fields ** |
|`RS` | `"\n"` | "Record Separator" delimiter between rows ** |
- `bookSST` is slower and more memory intensive, but has better compatibility
with older versions of iOS Numbers
- The raw data is the only thing guaranteed to be saved. Features not described
in this README may not be serialized.
- `cellDates` only applies to XLSX output and is not guaranteed to work with
third-party readers. Excel itself does not usually write cells with type `d`
so non-Excel tools may ignore the data or error in the presence of dates.
- `codepage` is applied to legacy formats including DBF. Characters missing
from the encoding will be replaced with underscore characters (`_`).
- `Props` is an object mirroring the workbook `Props` field. See the table from
the [Workbook File Properties](/docs/csf/book#file-properties) section.
- if specified, the string from `themeXLSX` will be saved as the primary theme
for XLSX/XLSB/XLSM files (to `xl/theme/theme1.xml` in the ZIP)
- Due to a bug in the program, some features like "Text to Columns" will crash
Excel on worksheets where error conditions are ignored. The writer will mark
files to ignore the error by default. Set `ignoreEC` to `false` to suppress.
- `FS` and `RS` apply to CSV and Text output formats. The options are discussed
in ["CSV and Text"](/docs/api/utilities/csv#delimiter-separated-output)
- `bookVBA` only applies to supported formats. ["VBA"](/docs/csf/features/vba)
section explains the feature in more detail.
- `WTF` is mainly for development.
<details open>
<summary><b>Rationale</b> (click to hide)</summary>
<summary><b>Exporting NUMBERS files</b> (click to show)</summary>
Apple Numbers has broken backwards compatibility many times over the years.
Files generated by older versions of Numbers are not guaranteed to work with
newer versions of Numbers or third-party tools.
The NUMBERS writer requires a fairly large base. The supplementary `xlsx.zahl`
scripts provide support. `xlsx.zahl.js` is designed for standalone and NodeJS
use, while `xlsx.zahl.mjs` is suitable for ESM.
The NUMBERS exporter starts from a working file and rewrites the data blocks.
This ensures exports still work
Adding NUMBERS export support involves two steps:
The current design allows for updating the base file without disrupting the rest
of the library.
1) Load the `xlsx.zahl` script
</details>
The `numbers` option is required for exporting NUMBERS files. It is expected to
be a Base64 string that encodes a valid Numbers file. The supplementary
`xlsx.zahl` scripts include the required string.
2) Pass the payload into the `numbers` option to `write` or `writeFile`.
<Tabs>
<TabItem value="standalone" label="Standalone">
<TabItem value="browser" label="Browser">
<p><a href={"https://cdn.sheetjs.com/xlsx-" + current + "/package/dist/xlsx.zahl.js"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/package/dist/xlsx.zahl.js"}</a> is the URL for {current}</p>
<CodeBlock language="html" title="Sample HTML">{`\
<html><head><meta charset="utf8"></head><body>\n\
<CodeBlock language="html">{`\
<meta charset="utf8">\n\
<body>\n\
<script src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>\n\
<script src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.zahl.js"></script>\n\
<script>\n\
@ -425,7 +176,7 @@ var wb = XLSX.utils.book_new(); var ws = XLSX.utils.aoa_to_sheet([\n\
]); XLSX.utils.book_append_sheet(wb, ws, "Sheet1");\n\
XLSX.writeFile(wb, "textport.numbers", {numbers: XLSX_ZAHL_PAYLOAD, compression: true});\n\
</script>\n\
</body></html>`}
</body>`}
</CodeBlock>
</TabItem>
@ -499,3 +250,67 @@ XLSX.writeFile(wb, "textport.numbers", {numbers: XLSX_ZAHL_PAYLOAD, compression:
</TabItem>
</Tabs>
</details>
## Supported Output Formats
For broad compatibility with third-party tools, this library supports many
output formats. The specific file type is controlled with `bookType` option:
| `bookType` | extension | sheets | Description |
|:-----------|:-----------|:-------|:--------------------------------|
| `xlsx` | `.xlsx` | multi | Excel 2007+ XML Format |
| `xlsm` | `.xlsm` | multi | Excel 2007+ Macro XML Format |
| `xlsb` | `.xlsb` | multi | Excel 2007+ Binary Format |
| `biff8` | `.xls` | multi | Excel 97-2004 Workbook Format |
| `biff5` | `.xls` | multi | Excel 5.0/95 Workbook Format |
| `biff4` | `.xls` | single | Excel 4.0 Worksheet Format |
| `biff3` | `.xls` | single | Excel 3.0 Worksheet Format |
| `biff2` | `.xls` | single | Excel 2.0 Worksheet Format |
| `xlml` | `.xls` | multi | Excel 2003-2004 (SpreadsheetML) |
| `numbers` | `.numbers` | multi | Numbers 3.0+ Spreadsheet |
| `ods` | `.ods` | multi | OpenDocument Spreadsheet |
| `fods` | `.fods` | multi | Flat OpenDocument Spreadsheet |
| `wk3` | `.wk3` | multi | Lotus Workbook (WK3) |
| `csv` | `.csv` | single | Comma Separated Values |
| `txt` | `.txt` | single | UTF-16 Unicode Text (TXT) |
| `sylk` | `.sylk` | single | Symbolic Link (SYLK) |
| `html` | `.html` | single | HTML Document |
| `dif` | `.dif` | single | Data Interchange Format (DIF) |
| `dbf` | `.dbf` | single | dBASE II + VFP Extensions (DBF) |
| `wk1` | `.wk1` | single | Lotus Worksheet (WK1) |
| `rtf` | `.rtf` | single | Rich Text Format (RTF) |
| `prn` | `.prn` | single | Lotus Formatted Text |
| `eth` | `.eth` | single | Ethercalc Record Format (ETH) |
- `compression` applies to ZIP-based formats (XLSX, XLSM, XLSB, NUMBERS, ODS)
- Formats that only support a single sheet require a `sheet` option specifying
the worksheet. If the string is empty, the first worksheet is used.
- `writeFile` will automatically guess the output file format based on the file
extension if `bookType` is not specified. It will choose the first format in
the aforementioned table that matches the extension.
## Output Type
The `type` option specifies the JS form of the output:
| `type` | output |
|------------|-----------------------------------------------------------------|
| `"base64"` | string: Base64 encoding of the file |
| `"binary"` | string: binary string (byte `n` is `data.charCodeAt(n)`) |
| `"string"` | string: JS string (not compatible with binary formats) |
| `"buffer"` | nodejs Buffer |
| `"array"` | ArrayBuffer, fallback array of 8-bit unsigned int |
| `"file"` | string: path of file that will be created (nodejs only) |
:::note pass
For compatibility with Excel, `csv` output will always include the UTF-8 byte
order mark ("BOM").
The raw [`sheet_to_csv` method](/docs/api/utilities/csv#csv-output) will return
JavaScript strings without the UTF-8 BOM.
:::

@ -26,7 +26,7 @@ HTML format and HTML table utilities.
var html = XLSX.utils.sheet_to_html(ws, opts);
```
The `sheet_to_html` method generates HTML strings from [SheetJS worksheet objects](/docs/csf/sheet).
The `sheet_to_html` method generates HTML strings from SheetJS worksheets[^1].
The following options are supported:
@ -513,3 +513,5 @@ browser. ["Browser Automation"](/docs/demos/net/headless) covers some browsers.
Some ecosystems provide DOM-like frameworks that are compatible with SheetJS.
Examples are included in the ["Synthetic DOM"](/docs/demos/net/dom) demo
[^1]: See ["Worksheet Object" in "SheetJS Data Model"](/docs/csf/sheet) for more details.

@ -41,12 +41,12 @@ These instructions were tested on the following platforms:
| Platform | Architecture | Test Date |
|:------------------------------|:-------------|:-----------|
| Linux (Ubuntu Linux x64) | `linux-x64` | 2026-02-02 |
| Linux (Debian Linux AArch64) | `linux-arm` | 2026-03-05 |
| MacOS 15.6 (x64) | `darwin-x64` | 2026-01-21 |
| MacOS 15.7 (ARM64) | `darwin-arm` | 2026-02-02 |
| Linux (Ubuntu Linux x64) | `linux-x64` | 2025-07-06 |
| Linux (Debian Linux AArch64) | `linux-arm` | 2025-01-14 |
| MacOS 15.3 (x64) | `darwin-x64` | 2025-03-31 |
| MacOS 15.2 (ARM64) | `darwin-arm` | 2025-03-07 |
| Windows 11 (x64) + WSL Ubuntu | `win11-x64` | 2025-06-20 |
| Windows 11 (ARM) + WSL Ubuntu | `win11-arm` | 2026-03-08 |
| Windows 11 (ARM) + WSL Ubuntu | `win11-arm` | 2025-02-23 |
With some additional dependencies, the unminified scripts are reproducible and
tests will pass in Windows XP with NodeJS 5.10.0.
@ -303,7 +303,7 @@ browser.
*In the `"Get Node.js®"` section:*
1. Select the LTS version (currently `"v24.13.0 (LTS)"`) in the first dropdown
1. Select the LTS version (currently `"v22.14.0 (LTS)"`) in the first dropdown
*In the `"Or get a prebuilt Node.js® for"` section:*
@ -734,20 +734,6 @@ Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/)
^^^^^^^^^^^^^^^^^^^^ URL
```
:::info pass
The local server is configured to start on port 8000. If another service is
listening on that port, the process will fail to start.
If the other process cannot be stopped, run the server manually. The following
snippet starts a server on port 9999:
```bash
npx -y http-server -p 9999 tests
```
:::
11) Open a browser window and access the displayed URL.
## Development

@ -25,15 +25,6 @@ Metadata standards. XLSX files must reference `http://purl.org/dc/elements/1.1/`
Any tool that generates XML files must generate URLs to domains outside of the
control of the vendor.
### JavaScript Links
Excel and other spreadsheet tools support hyperlinks using the `javascript:`
protocol. When exporting to HTML, they will not sanitize or strip these URLs.
**Microsoft does not currently believe this is a vulnerability!**
The SheetJS HTML exporter mirrors established behavior.
### Non-ASCII Characters
XLS, CSV and other legacy file formats use system-specific encodings. Excel and

@ -48,7 +48,7 @@ Alias variables (supported in DTA versions 120-121) are not processed.
This demo fetches a [sample DTA file](pathname:///dta/pres.dta), parses the data
using the SheetJS DTA Codec and displays the data in an HTML table using the
[`sheet_to_html` utility function](/docs/api/utilities/html#html-table-output):
`sheet_to_html` method[^1].
:::tip pass
@ -97,3 +97,5 @@ function SheetJSDTA() {
</> );
}
```
[^1]: See [`sheet_to_html` in "Utilities"](/docs/api/utilities/html#html-table-output)

@ -91,7 +91,7 @@ export default App;
If you are starting from scratch, create a new ViteJS + ReactJS project:
```bash
npm create vite@latest -- sheetjs-react --template react --no-rolldown --no-interactive
npm create vite@latest -- sheetjs-react --template react --default
cd sheetjs-react
npm install
npm run dev
@ -123,19 +123,19 @@ yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
2) Ensure that your component script imports `useRef` from the `react` library:
```js title="src/App.tsx (add useRef import if it does not exist)"
```js
import { useRef } from "react";
```
3) Add the following line at the top of your component script:
```js title="src/App.tsx (add import to the top of your component script)"
```js
import { utils, writeFileXLSX } from "xlsx";
```
4) Create a ref in the body of your function component:
```jsx title="src/App.tsx (add highlighted line)"
```jsx
function App() {
// highlight-next-line
const tbl = useRef(null);
@ -144,7 +144,7 @@ function App() {
5) Attach the ref to the table element:
```jsx title="src/App.tsx (add ref attribute to TABLE element)"
```jsx
function App() {
// ...
return (
@ -156,7 +156,7 @@ function App() {
6) Add a button with a click handler that will export table data to XLSX:
```jsx title="src/App.tsx (add button element to JSX)"
```jsx
function App() {
// ...
return (
@ -172,9 +172,6 @@ function App() {
{/*...*/}
```
7) Start the development server (typically `npm run dev`) and open the app.
Click the "Export XLSX" button and open the generated file.
</TabItem>
</Tabs>
@ -370,7 +367,7 @@ This,is,a,Test
The test suite is regularly run against a number of modern and legacy browsers
using [Sauce Labs](https://saucelabs.com/).
Browser tests for SheetJS version `0.20.3` were run on 2026-01-12:
The following chart shows test results on 2025-05-15 for version `0.20.3`:
[![Build Status](pathname:///test/sheetjs.svg)](https://saucelabs.com/u/sheetjs)

@ -31,10 +31,9 @@ fn main() {
let path: String = iter.nth(1).expect("must specify a file name");
let file: Vec<u8> = std::fs::read(path.clone()).unwrap();
/* push data to boa */
let aligned = boa_engine::object::builtins::AlignedVec::from_iter(0, file.into_iter());
let array: boa_engine::object::builtins::JsArrayBuffer = boa_engine::object::builtins::JsArrayBuffer::from_byte_block(aligned, context).unwrap();
let array: boa_engine::object::builtins::JsArrayBuffer = boa_engine::object::builtins::JsArrayBuffer::from_byte_block(file, context).unwrap();
let attrs = boa_engine::property::Attribute::WRITABLE | boa_engine::property::Attribute::ENUMERABLE | boa_engine::property::Attribute::CONFIGURABLE;
let _ = context.register_global_property(boa_engine::js_string!("buf"), array, attrs);
}

@ -50,6 +50,8 @@ int main(int argc, char *argv[]) {
JsValueRef global;
FAIL_CHECK(JsGetGlobalObject(&global));
EVAL_FILE("shim.min.js")
EVAL_FILE("xlsx.full.min.js")
JsValueRef buf_str;

Binary file not shown.

@ -45,7 +45,7 @@ fn main() {
let csv: ducc::Value = ctx.compile("XLSX.utils.sheet_to_csv(ws)", None).unwrap().call(()).unwrap();
println!("{}", get_string(csv));
}
/* write file */
{
/* due to issues with the duktape crate, it is easier to pass a base64-encoded string and decode in Rust */

@ -6,7 +6,7 @@
"version": "0.0.0",
"main": "main.js",
"dependencies": {
"@electron/remote": "2.1.3",
"@electron/remote": "2.1.2",
"xlsx": "https://sheet.lol/balls/xlsx-0.20.3.tgz"
},
"scripts": {
@ -15,12 +15,12 @@
"make": "electron-forge make"
},
"devDependencies": {
"@electron-forge/cli": "7.11.1",
"@electron-forge/maker-deb": "7.11.1",
"@electron-forge/maker-rpm": "7.11.1",
"@electron-forge/maker-squirrel": "7.11.1",
"@electron-forge/maker-zip": "7.11.1",
"electron": "40.8.0"
"@electron-forge/cli": "7.8.0",
"@electron-forge/maker-deb": "7.8.0",
"@electron-forge/maker-rpm": "7.8.0",
"@electron-forge/maker-squirrel": "7.8.0",
"@electron-forge/maker-zip": "7.8.0",
"electron": "35.1.2"
},
"config": {
"forge": {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 KiB

@ -77,7 +77,7 @@ class SheetJSFlutterState extends State<SheetJSFlutter> {
XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]);
""");
setState(() {
_data = const CsvDecoder().convert(func.stringResult);
_data = CsvToListConverter(eol: "\n").convert(func.stringResult);
});
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 467 KiB

@ -66,5 +66,5 @@ const aoo = offset.map((name, idx) => ({
const ws = json_to_sheet(aoo);
/* write workbook */
const wb = book_new(ws, "Offsets");
/* write to XLSX */
/* write to XLSX */
writeFileXLSX(wb, "SheetJSGhidraTSTCell.xlsx");

@ -1,5 +1,5 @@
# Note: The official Hermes documentation includes zero guidance on embedding.=
# Tested against commit f45c6bcb2d6e6516b25abbf6c5964f9dc95c5761 on darwin-x64
# Tested against commit 8ef11b45d7b078434605658421efb34cf436c005 on darwin-x64
# History https://git.sheetjs.com/sheetjs/docs.sheetjs.com/commits/branch/master/docz/static/hermes/Makefile
MYCC=llvm-g++
@ -33,8 +33,8 @@ clean-all: clean
sheetjs-hermes: sheetjs-hermes.cpp init
$(MYCC) $< -o $@ -std=gnu++17 \
-Ihermes/include/ -Ihermes/API/ -Ihermes/API/jsi -Ihermes/public \
-Lbuild_release/API/hermes/ -lsynthTrace -lsynthTraceParser \
-lhermesapi \
-Lbuild_release/API/hermes/ -lsynthTrace -lsynthTraceParser \
-lhermes \
-Lbuild_release/lib/VM/ -lhermesVMRuntime \
-Lbuild_release/lib/BCGen/HBC/ -lhermesHBCBackend \
-Lbuild_release/lib/BCGen/ -lhermesBackend \
@ -45,24 +45,24 @@ sheetjs-hermes: sheetjs-hermes.cpp init
-Lbuild_release/external/llvh/lib/Demangle/ -lLLVHDemangle \
-Lbuild_release/external/llvh/lib/Support/ -lLLVHSupport \
-Lbuild_release/jsi/ -ljsi \
-Lbuild_release/lib/ -lhermesOptimizer \
-Lbuild_release/lib/ -lhermesFrontend \
-Lbuild_release/lib/ -lhermesOptimizer \
-Lbuild_release/lib/ADT -lhermesADT \
-Lbuild_release/lib/AST/ -lhermesAST \
-Lbuild_release/lib/AST2JS/ -lhermesAST2JS \
-Lbuild_release/lib/CompilerDriver/ -lhermesCompilerDriver \
-Lbuild_release/lib/ConsoleHost/ -lhermesConsoleHost \
-Lbuild_release/lib/DependencyExtractor/ -lhermesDependencyExtractor \
-Lbuild_release/lib/FlowParser/ -lhermesFlowParser \
-Lbuild_release/lib/FrontEndDefs/ -lhermesFrontEndDefs \
-Lbuild_release/lib/Inst/ -lhermesInst \
-Lbuild_release/lib/InternalBytecode/ -lhermesInternalBytecode \
-Lbuild_release/lib/Platform/ -lhermesPlatform \
-Lbuild_release/lib/Platform/Intl/ -lhermesBCP47Parser \
-Lbuild_release/lib/Regex/ -lhermesRegex \
-Lbuild_release/lib/Support/ -lhermesSupport \
-Lbuild_release/lib/Sema/ -lhermesSema \
-Lbuild_release/lib/InternalJavaScript/ -lhermesInternalUnit -lhermesInternalBytecode \
-Lbuild_release/external/boost/boost_1_86_0/libs/context/ -lboost_context \
-Lbuild_release/public/hermes/Public -lhermesPublic \
-Lhermes/external/flowparser/ -lflowparser-mac \
-Lbuild_release/external/dtoa/ -ldtoa \
$(POSTAMBLE)
@ -71,5 +71,5 @@ sheetjs-hermes.cpp:
.PHONY: init
init:
if [ ! -e hermes ]; then git clone https://github.com/facebook/hermes.git; cd hermes; git checkout f45c6bcb2d6e6516b25abbf6c5964f9dc95c5761; cd ..; fi
if [ ! -e hermes ]; then git clone https://github.com/facebook/hermes.git; cd hermes; git checkout 8ef11b45d7b078434605658421efb34cf436c005; cd ..; fi
if [ ! -e build_release ]; then cmake -S hermes -B build_release -G Ninja -DCMAKE_BUILD_TYPE=Release -DHERMES_BUILD_APPLE_FRAMEWORK=OFF; cmake --build ./build_release; fi

@ -2,25 +2,3 @@
fn main() {
println!("cargo::rustc-link-lib=framework=JavaScriptCore");
}
#[cfg(target_os = "linux")]
fn main() {
// Link against JavaScriptCore libraries built from WebKit source
// Libraries are static archives (.a files)
println!("cargo::rustc-link-lib=static=JavaScriptCore");
println!("cargo::rustc-link-lib=static=WTF");
println!("cargo::rustc-link-lib=static=bmalloc");
println!("cargo::rustc-link-lib=icui18n");
println!("cargo::rustc-link-lib=icuuc");
println!("cargo::rustc-link-lib=atomic");
// Required system libraries for the C++ runtime dependencies
println!("cargo::rustc-link-lib=stdc++");
println!("cargo::rustc-link-lib=pthread");
println!("cargo::rustc-link-lib=m");
println!("cargo::rustc-link-lib=dl");
println!("cargo::rustc-link-lib=c");
// Search path to the pre-built JavaScriptCore libraries
println!("cargo::rustc-link-search=native=/tmp/sheetjs-jsc/Release/lib");
}

Some files were not shown because too many files have changed in this diff Show More