win11-x64 tests

This commit is contained in:
SheetJS 2026-01-28 23:58:19 -05:00
parent 6532f9fea8
commit ef89abda1f
70 changed files with 2463 additions and 149 deletions

@ -123,7 +123,7 @@ This demo was last tested in the following deployments:
|:-------------|:---------|:-----------|
| `darwin-x64` | `1.3.6` | 2026-01-20 |
| `darwin-arm` | `1.3.6` | 2026-01-19 |
| `win11-x64` | `1.2.8` | 2025-04-17 |
| `win11-x64` | `1.3.6` | 2026-01-28 |
| `win11-arm` | `1.2.3` | 2025-02-23 |
| `linux-x64` | `1.3.6` | 2026-01-18 |
| `linux-arm` | `1.2.2` | 2025-02-16 |
@ -137,42 +137,9 @@ BunJS on Windows on ARM uses the X64 compatibility layer.
```bash
mkdir sheetjs-bun-dle
cd sheetjs-bun-dle
echo "{}" > package.json
bun init -y
```
:::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">{`\

@ -44,9 +44,9 @@ The NodeJS demo was tested in the following environments:
| NodeJS | TF.js | Date |
|:------------|:----------|:-----------|
| `24.11.1` | `4.22.0` | 2025-11-15 |
| `22.21.1` | `4.22.0` | 2025-11-15 |
| `20.18.0` | `4.22.0` | 2025-11-15 |
| `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 |
The Kaioken demo was tested in the following environments:

@ -41,11 +41,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 |
| `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 |
:::

@ -47,25 +47,25 @@ This demo was tested in the following environments:
| ESBuild | Date |
|:----------|:-----------|
| `0.27.0` | 2025-11-15 |
| `0.26.0` | 2025-11-15 |
| `0.25.12` | 2025-11-15 |
| `0.24.2` | 2025-11-15 |
| `0.23.1` | 2025-11-15 |
| `0.22.0` | 2025-11-15 |
| `0.21.5` | 2025-11-15 |
| `0.20.2` | 2025-11-15 |
| `0.19.12` | 2025-11-15 |
| `0.18.20` | 2025-11-15 |
| `0.17.19` | 2025-11-15 |
| `0.16.17` | 2025-11-15 |
| `0.15.18` | 2025-11-15 |
| `0.14.54` | 2025-11-15 |
| `0.13.15` | 2025-11-15 |
| `0.12.29` | 2025-11-15 |
| `0.11.23` | 2025-11-15 |
| `0.10.2` | 2025-11-15 |
| `0.9.7` | 2025-11-15 |
| `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 |
:::

@ -41,10 +41,10 @@ This demo was tested in the following environments:
| Version | Date | Required Workarounds |
|:----------|:-----------|:------------------------------------|
| `5.102.1` | 2025-11-15 | |
| `4.47.0` | 2025-11-15 | |
| `3.12.0` | 2025-11-15 | Import `xlsx/dist/xlsx.full.min.js` |
| `2.7.0` | 2025-11-15 | Import `xlsx/dist/xlsx.full.min.js` |
| `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` |
:::

@ -34,21 +34,21 @@ This demo was tested in the following environments:
| Browserify | Date |
|:-----------|:-----------|
| `17.0.1` | 2025-11-15 |
| `16.5.2` | 2025-11-15 |
| `15.2.0` | 2025-11-15 |
| `14.5.0` | 2025-11-15 |
| `13.3.0` | 2025-11-15 |
| `12.0.2` | 2025-11-15 |
| `11.2.0` | 2025-11-15 |
| `10.2.6` | 2025-11-15 |
| `9.0.8` | 2025-11-15 |
| `8.1.3` | 2025-11-15 |
| `7.1.0` | 2025-11-15 |
| `6.3.4` | 2025-11-15 |
| `5.13.1` | 2025-11-15 |
| `4.2.3` | 2025-11-15 |
| `3.46.1` | 2025-11-15 |
| `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 |
:::

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

@ -46,11 +46,11 @@ This demo was tested in the following environments:
| Version | Platform | Date |
|:----------|:---------|:-----------|
| `0.19.47` | NodeJS | 2025-11-15 |
| `0.20.16` | Browser | 2025-11-15 |
| `0.20.19` | NodeJS | 2025-11-15 |
| `0.21.6` | NodeJS | 2025-11-15 |
| `6.15.1` | NodeJS | 2025-11-15 |
| `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 |
:::

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

@ -34,7 +34,7 @@ This demo was tested in the following environments:
| Rspack | Date |
|:--------|:-----------|
| `1.6.3` | 2025-11-15 |
| `1.7.4` | 2026-01-28 |
:::

@ -34,8 +34,8 @@ This demo was tested in the following environments:
| Version | Date |
|:---------|:-----------|
| `2.16.1` | 2025-11-15 |
| `1.12.3` | 2025-11-15 |
| `2.16.1` | 2026-01-28 |
| `1.12.3` | 2026-01-28 |
:::

@ -35,7 +35,7 @@ This demo was tested in the following environments:
| Version | Date |
|:----------|:-----------|
| `1.15.2` | 2025-11-15 |
| `1.15.11` | 2026-01-28 |
:::

@ -62,7 +62,7 @@ This demo was tested in the following environments:
| Version | Date |
|:--------|:-----------|
| `3.8.8` | 2025-11-15 |
| `3.8.8` | 2026-01-28 |
:::
@ -194,7 +194,7 @@ This demo was tested in the following environments:
| Version | Date |
|:--------|:-----------|
| `3.8.0` | 2025-11-15 |
| `3.8.0` | 2026-01-28 |
:::

@ -296,9 +296,9 @@ features to ensure that SheetJS DOM methods can process TABLE elements.
This demo was tested in the following deployments:
| CheerioJS | Date |
|:--------------|:-----------|
| `1.1.2` | 2026-01-12 |
| CheerioJS | Date |
|:----------|:-----------|
| `1.2.0` | 2026-01-28 |
:::

@ -204,24 +204,26 @@ This demo was tested in the following environments:
| `esbuild` | Date |
|:----------|:-----------|
| `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 |
| `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 |
:::

@ -115,10 +115,11 @@ accessed using the variable `pres` in a template:
This demo was tested in the following environments:
| Eleventy | Date |
|:---------------|:-----------|
| `2.0.1` | 2026-01-12 |
| `3.1.2` | 2026-01-12 |
| Eleventy | Date |
|:----------------|:-----------|
| `4.0.0-alpha.6` | 2026-01-28 |
| `3.1.2` | 2026-01-28 |
| `2.0.1` | 2026-01-28 |
:::

@ -145,7 +145,8 @@ 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.**
:::
@ -549,6 +550,7 @@ This demo was tested in the following deployments:
|:-------------|:--------|:---------|:-----------|
| `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-01-28 |
| `linux-x64` | `2.7.0` | `3.12.3` | 2025-04-21 |
| `linux-arm` | `2.7.0` | `3.11.2` | 2025-02-15 |

@ -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 */

@ -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");

@ -61,7 +61,7 @@ const kJSTypedArrayTypeUint8Array: u32 = 3;
type JSPropertyAttributes = u32;
unsafe extern "C" {
pub unsafe fn JSEvaluateScript(ctx: JSContextRef, script: JSStringRef, thisObject: JSObjectRef, sourceURL: JSStringRef, startingLineNumber: i32, exception: JSValueRefRef) -> JSValueRef;
pub unsafe fn JSEvaluateScript(ctx: JSContextRef, script: JSStringRef, thisObject: JSObjectRef, sourceURL: JSStringRef, startingLineNumber: i32, exception: JSValueRefRef) -> JSValueRef;
pub unsafe fn JSGlobalContextCreate(string: JSClassRef) -> JSGlobalContextRef;
pub unsafe fn JSGlobalContextRelease(ctx: JSGlobalContextRef);
@ -89,7 +89,7 @@ pub struct JSC;
impl JSC {
pub fn JSEval(ctx: JSContextRef, script: &str) -> JSValueRef { unsafe {
let script: JSStringRef = JSStringCreateWithUTF8CString(std::ffi::CString::new(script.as_bytes()).unwrap().as_ptr() as *const u8);
let result: JSValueRef = JSEvaluateScript(ctx, script, NULL!(), NULL!(), 0, NULL!());
let result: JSValueRef = JSEvaluateScript(ctx, script, NULL!(), NULL!(), 0, NULL!());
JSStringRelease(script);
result
} }
@ -131,7 +131,7 @@ impl JSC {
}
} }
pub fn JSObjectSetProperty(ctx: JSContextRef, object: JSObjectRef, propertyName: JSStringRef, value: JSValueRef, attributes: JSPropertyAttributes, exception: JSValueRefRef) { unsafe {
pub fn JSObjectSetProperty(ctx: JSContextRef, object: JSObjectRef, propertyName: JSStringRef, value: JSValueRef, attributes: JSPropertyAttributes, exception: JSValueRefRef) { unsafe {
JSObjectSetProperty(ctx, object, propertyName, value, attributes, exception)
} }
pub fn JSObjectMakeUint8Array(ctx: JSContextRef, data: Vec<u8>) -> JSObjectRef { unsafe {
@ -173,10 +173,10 @@ fn main() {
let ctx: JSGlobalContextRef = JSC::JSGlobalContextCreate(NULL!());
/* JSC does not expose a standard "global" by default */
JSC::JSEval(ctx, "var global = (function(){ return this; }).call(null);");
JSC::JSEval(ctx, "var global = (function(){ return this; }).call(null);");
/* load library */
JSC::JSEval(ctx, include_str!("xlsx.full.min.js"));
JSC::JSEval(ctx, include_str!("xlsx.full.min.js"));
/* get version string */
match JSC::JSEvalStr(ctx, "XLSX.version") {
@ -194,7 +194,7 @@ 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 JSC */
let u8: JSObjectRef = JSC::JSObjectMakeUint8Array(ctx, file);

@ -1,13 +1,13 @@
/* based on the `coarse` project README */
const fs = require('fs');
const coarse = require('coarse');
const svg = fs.readFileSync(process.argv[2], "utf8");
let roughened = coarse(svg);
const viewbox = roughened.match(/viewBox="(.*?)"/)[1].split(/\s+/);
const v = viewbox.map(x => parseFloat(x));
v[0] -= 40; v[1] += 40; v[2] += 80; v[3] += 80;
roughened = roughened.replace(/<title>G<\/title>/, `$&<polygon fill="white" stroke="" points="${v[0]},${v[1]} ${v[0]},${v[1]-v[3]} ${v[0]+v[2]},${v[1]-v[3]} ${v[0]+v[2]},${v[1]} ${v[0]},${v[1]}"/>`);
fs.writeFileSync(process.argv[3], roughened);

@ -1,7 +1,8 @@
{
"private": true,
"scripts": {
"dev": "cd docz && npm run start -- --host=0.0.0.0 --no-open"
"init": "cd docz && npm i --legacy-peer-deps",
"dev": "cd docz && npm run start -- --host=0.0.0.0 --port 6996 --no-open"
},
"alex": {
"allow": [

@ -0,0 +1,20 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/bigdata/stream#nodejs
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-stream"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
bun init -y
bun i xlsx@https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
Invoke-WebRequest -Uri "https://docs.sheetjs.com/stream/SheetJSNodeJStream.js" -OutFile "SheetJSNodeJStream.js"
Invoke-WebRequest -Uri "https://docs.sheetjs.com/pres.xlsx" -OutFile "pres.xlsx"
bun --version
bun SheetJSNodeJStream.js pres.xlsx
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

@ -7,10 +7,10 @@ rm -rf sheetjs-stream
mkdir sheetjs-stream
cd sheetjs-stream
bun init -y
bun i xlsx@https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
curl -LO https://docs.sheetjs.com/stream/SheetJSNodeJStream.js
curl -LO https://docs.sheetjs.com/pres.xlsx
bun --version

@ -0,0 +1,20 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/bigdata/stream#nodejs
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-stream"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
Invoke-WebRequest -Uri "https://docs.sheetjs.com/stream/SheetJSNodeJStream.js" -OutFile "SheetJSNodeJStream.js"
Invoke-WebRequest -Uri "https://docs.sheetjs.com/pres.xlsx" -OutFile "pres.xlsx"
# Test with current Node.js version
node --version
node SheetJSNodeJStream.js pres.xlsx
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

@ -11,7 +11,6 @@ cd sheetjs-stream
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
curl -LO https://docs.sheetjs.com/stream/SheetJSNodeJStream.js
curl -LO https://docs.sheetjs.com/pres.xlsx
# this version uses `nvm` to cycle through node versions

@ -0,0 +1,108 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/frontend/bundler/browserify
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-browserify-tests"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
for ($n = 3; $n -le 17; $n++) {
$subDir = Join-Path -Path $tempDir -ChildPath "sheetjs-browserify-$n"
New-Item -ItemType Directory -Path $subDir | Out-Null
Set-Location -Path $subDir
npm init -y
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz puppeteer express@4
@'
const { utils, version, writeFileXLSX } = require('xlsx');
document.getElementById("xport").addEventListener("click", function() {
/* fetch JSON data and parse */
var url = "https://sheetjs.com/data/executive.json";
fetch(url).then(function(res) { return res.json(); }).then(function(raw_data) {
/* filter for the Presidents */
var prez = raw_data.filter(function(row) { return row.terms.some(function(term) { return term.type === "prez"; }); });
/* sort by first presidential term */
prez.forEach(function(row) {
row.start = row.terms.find(function(term) {
return term.type === "prez";
}).start;
});
prez.sort(function(l,r) { return l.start.localeCompare(r.start); });
/* flatten objects */
var rows = prez.map(function(row) { return {
name: row.name.first + " " + row.name.last,
birthday: row.bio.birthday
}; });
/* generate worksheet and workbook */
var worksheet = utils.json_to_sheet(rows);
var 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 */
var max_width = rows.reduce(function(w, r) { return 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");
});
});
'@ | Out-File -FilePath "index.js" -Encoding utf8
npm install --save browserify@$n
npm ls | Select-String "browserify"
npx browserify@$n index.js > index.min.js
@'
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
<h1>SheetJS Presidents Demo</h1>
<button id="xport">Click here to export</button>
<script src="./index.min.js"></script>
</body>
</html>
'@ | Out-File -FilePath "index.html" -Encoding utf8
@'
const puppeteer = require('puppeteer');
const express = require('express');
const app = express();
app.use(express.static('./'));
app.listen(7262, async() => {
await new Promise((res,rej) => setTimeout(res, 1000));
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.on("console", msg => console.log("PAGE LOG:", msg.text()));
await page.setViewport({width: 1920, height: 1080});
const client = await page.target().createCDPSession();
await client.send('Browser.setDownloadBehavior', {
behavior: 'allow',
downloadPath: require("path").resolve('./')
});
await page.goto('http://localhost:7262/');
await page.click("#xport");
await new Promise((res,rej) => setTimeout(res, 1000));
await browser.close();
process.exit();
});
'@ | Out-File -FilePath "test.js" -Encoding utf8
node test.js
npx -y xlsx-cli Presidents.xlsx | Select-Object -First 3
Set-Location -Path $tempDir
}
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

65
tests/bundler/esbuild.ps1 Normal file

@ -0,0 +1,65 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/frontend/bundler/esbuild
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-esbrowser"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
npm init -y
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
npm i --save puppeteer express
Invoke-WebRequest -Uri "https://docs.sheetjs.com/esbuild/esbrowser.js" -OutFile "esbrowser.js"
Invoke-WebRequest -Uri "https://docs.sheetjs.com/esbuild/esbnode.js" -OutFile "esbnode.js"
@'
<body><script src="esb.browser.js"></script></body>
'@ | Out-File -FilePath "index.html" -Encoding utf8
@'
const puppeteer = require('puppeteer');
const express = require('express');
const app = express();
app.use(express.static('./'));
app.listen(7262, async() => {
await new Promise((res,rej) => setTimeout(res, 1000));
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.on("console", msg => console.log("PAGE LOG:", msg.text()));
await page.setViewport({width: 1920, height: 1080});
const client = await page.target().createCDPSession();
await client.send('Browser.setDownloadBehavior', {
behavior: 'allow',
downloadPath: require("path").resolve('./')
});
page.on('request', req => console.log(req.url()));
await page.goto('http://localhost:7262/');
await new Promise((res,rej) => setTimeout(res, 1000));
await browser.close();
process.exit();
});
'@ | Out-File -FilePath "test.js" -Encoding utf8
for ($i = 9; $i -le 27; $i++) {
$n = "0.$i"
npx -y esbuild@$n --version
## Browser Test
npx -y esbuild@$n esbrowser.js --bundle --outfile=esb.browser.js
node test.js
npx -y xlsx-cli Presidents.xlsx | Select-Object -First 3
if (Test-Path -Path "esb.browser.js") { Remove-Item -Path "esb.browser.js" -Force }
if (Test-Path -Path "Presidents.xlsx") { Remove-Item -Path "Presidents.xlsx" -Force }
## Node test
npx -y esbuild@$n esbnode.js --bundle --platform=node --outfile=esb.node.js
node esb.node.js
npx -y xlsx-cli Presidents.xlsx | Select-Object -First 3
if (Test-Path -Path "esb.node.js") { Remove-Item -Path "esb.node.js" -Force }
if (Test-Path -Path "Presidents.xlsx") { Remove-Item -Path "Presidents.xlsx" -Force }
}
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

@ -49,6 +49,7 @@ for n in 0.{9..27}; do
npx -y xlsx-cli Presidents.xlsx | head -n 3
rm -f esb.browser.js Presidents.xlsx
## Node test
npx -y esbuild@$n esbnode.js --bundle --platform=node --outfile=esb.node.js
node esb.node.js
npx -y xlsx-cli Presidents.xlsx | head -n 3

102
tests/bundler/parcel.ps1 Normal file

@ -0,0 +1,102 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/frontend/bundler/parcel
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-parceljs"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
npm init -y
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
@'
<body>
<h3>SheetJS <span id="vers"></span> export demo</h3>
<button id="xport">Click to Export!</button>
<script src="index.js" type="module"></script>
<body>
'@ | Out-File -FilePath "index.html" -Encoding utf8
@'
// ESM-style import from "xlsx"
import { utils, version, writeFileXLSX } from 'xlsx';
document.getElementById("vers").innerText = version;
document.getElementById("xport").onclick = 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");
};
'@ | Out-File -FilePath "index.js" -Encoding utf8
@'
const puppeteer = require('puppeteer');
const express = require('express');
const app = express();
app.use(express.static('./dist'));
app.listen(7262, async() => {
await new Promise((res,rej) => setTimeout(res, 1000));
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.on("console", msg => console.log("PAGE LOG:", msg.text()));
await page.setViewport({width: 1920, height: 1080});
const client = await page.target().createCDPSession();
await client.send('Browser.setDownloadBehavior', {
behavior: 'allow',
downloadPath: require("path").resolve('./')
});
page.on('request', req => console.log(req.url()));
await page.goto('http://localhost:7262/');
await new Promise((res,rej) => setTimeout(res, 1000));
await page.click("#xport");
await new Promise((res,rej) => setTimeout(res, 1000));
await browser.close();
process.exit();
});
'@ | Out-File -FilePath "test.js" -Encoding utf8
npm i --save puppeteer express@4
node -e "var pjson = JSON.parse(fs.readFileSync('./package.json')); console.log(pjson); delete pjson.main; fs.writeFileSync('package.json', JSON.stringify(pjson))"
$versions = @("2.16.1", "1.12.3")
foreach ($n in $versions) {
npm i --save parcel@$n
npx -y parcel@$n build index.html
node test.js
npx -y xlsx-cli Presidents.xlsx | Select-Object -First 3
Remove-Item -Path "Presidents.xlsx" -Force -ErrorAction SilentlyContinue
}
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

170
tests/bundler/requirejs.ps1 Normal file

@ -0,0 +1,170 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/frontend/bundler/requirejs
# requires global puppeteer and express
# npm i -g puppeteer express@4
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-requirejs"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
Invoke-WebRequest -Uri "https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js" -OutFile "xlsx.full.min.js"
npm init -y
npm i puppeteer express
@'
require(["xlsx"], function(XLSX) {
document.getElementById("xport").addEventListener("click", function() {
/* fetch JSON data and parse */
var url = "https://docs.sheetjs.com/executive.json";
fetch(url).then(function(res) { return res.json(); }).then(function(raw_data) {
/* filter for the Presidents */
var prez = raw_data.filter(function(row) { return row.terms.some(function(term) { return term.type === "prez"; }); });
/* sort by first presidential term */
prez.forEach(function(row) {
row.start = row.terms.find(function(term) {
return term.type === "prez";
}).start;
});
prez.sort(function(l,r) { return l.start.localeCompare(r.start); });
/* flatten objects */
var rows = prez.map(function(row) { return {
name: row.name.first + " " + row.name.last,
birthday: row.bio.birthday
}; });
/* generate worksheet and workbook */
var worksheet = XLSX.utils.json_to_sheet(rows);
var workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "Dates");
/* fix headers */
XLSX.utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
/* calculate column width */
var max_width = rows.reduce(function(w, r) { return Math.max(w, r.name.length); }, 10);
worksheet["!cols"] = [ { wch: max_width } ];
/* create an XLSX file and try to save to Presidents.xlsx */
XLSX.writeFileXLSX(workbook, "Presidents.xlsx");
console.log(XLSX.utils.sheet_to_csv(worksheet));
});
});
});
'@ | Out-File -FilePath "SheetJSRequire.js" -Encoding utf8
@'
({
baseUrl: ".",
name: "SheetJSRequire",
paths: {
xlsx: "./xlsx.full.min"
},
out: "SheetJSRequire.min.js"
});
'@ | Out-File -FilePath "build.js" -Encoding utf8
@'
const puppeteer = require('puppeteer');
const express = require('express');
const app = express();
app.use(express.static('./'));
app.listen(7262, async() => {
await new Promise((res,rej) => setTimeout(res, 1000));
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.on("console", msg => console.log("PAGE LOG:", msg.text()));
await page.setViewport({width: 1920, height: 1080});
await page.goto('http://localhost:7262/', {waitUntil: 'domcontentloaded'});
/* wait for requirejs to request xlsx.full.min.js */
await page.waitForRequest(request => request.url().indexOf('xlsx.full.min.js') !== -1);
await new Promise((res,rej) => setTimeout(res, 1000));
await page.click("#xport");
await new Promise((res,rej) => setTimeout(res, 2000));
await browser.close();
process.exit();
});
'@ | Out-File -FilePath "test1.js" -Encoding utf8
@'
const puppeteer = require('puppeteer');
const express = require('express');
const app = express();
app.use(express.static('./'));
app.listen(7262, async() => {
await new Promise((res,rej) => setTimeout(res, 1000));
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.on("console", msg => console.log("PAGE LOG:", msg.text()));
await page.setViewport({width: 1920, height: 1080});
await page.goto('http://localhost:7262/optimized.html');
await page.click("#xport");
await new Promise((res,rej) => setTimeout(res, 1000));
await browser.close();
process.exit();
});
'@ | Out-File -FilePath "test2.js" -Encoding utf8
$versions = @("2.3.7", "2.1.22")
foreach ($n in $versions) {
Write-Host "$n Standalone"
@"
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
<h1>SheetJS Presidents Demo</h1>
<button id="xport">Click here to export</button>
<script src="http://requirejs.org/docs/release/$n/comments/require.js"></script>
<script>
/* Wire up RequireJS */
require.config({
baseUrl: ".",
name: "SheetJSRequire",
paths: {
xlsx: "xlsx.full.min"
}
});
</script>
<script src="SheetJSRequire.js"></script>
</body>
</html>
"@ | Out-File -FilePath "index.html" -Encoding utf8
node test1.js
Write-Host "$n Optimizer"
Remove-Item -Path "SheetJSRequire.min.js" -Force -ErrorAction Ignore
#npx -p requirejs@$n r.js -o build.js
# NOTE: `npx` issues in Windows necessitates this workaround
npm install --save requirejs@$n
node .\node_modules\requirejs\bin\r.js -o build.js
@"
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
<h1>SheetJS Presidents Demo</h1>
<button id="xport">Click here to export</button>
<script src="http://requirejs.org/docs/release/$n/comments/require.js"></script>
<script src="SheetJSRequire.min.js"></script>
</body>
</html>
"@ | Out-File -FilePath "optimized.html" -Encoding utf8
node test2.js
}
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

127
tests/bundler/rollup.ps1 Normal file

@ -0,0 +1,127 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/frontend/bundler/rollup
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-rollup"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
npm init -y
npm i --save https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz
@'
import { utils, version, 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");
});
'@ | Out-File -FilePath "index.js" -Encoding utf8
@'
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
<h1>SheetJS Presidents Demo</h1>
<button id="xport">Click here to export</button>
<script type="module" src="./bundle.js"></script>
</body>
</html>
'@ | Out-File -FilePath "index.html" -Encoding utf8
npm i --save puppeteer express@4
@'
const puppeteer = require('puppeteer');
const express = require('express');
const app = express();
app.use(express.static('./'));
app.listen(7262, async() => {
await new Promise((res,rej) => setTimeout(res, 1000));
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.on("console", msg => console.log("PAGE LOG:", msg.text()));
await page.setViewport({width: 1920, height: 1080});
const client = await page.target().createCDPSession();
await client.send('Browser.setDownloadBehavior', {
behavior: 'allow',
downloadPath: require("path").resolve('./')
});
await page.goto('http://localhost:7262/');
await page.click("#xport");
await new Promise((res,rej) => setTimeout(res, 1000));
await browser.close();
process.exit();
});
'@ | Out-File -FilePath "test.js" -Encoding utf8
## RollupJS 4.x
npm i --save rollup@4.x @rollup/plugin-node-resolve
npx -y rollup@4.x index.js --plugin @rollup/plugin-node-resolve --file bundle.js --format iife
node test.js
npx -y xlsx-cli Presidents.xlsx | Select-Object -First 3
Remove-Item -Path "bundle.js" -Force
Remove-Item -Path "Presidents.xlsx" -Force
## RollupJS 3.x
npm i --save rollup@3.x @rollup/plugin-node-resolve
npx -y rollup@3.x index.js --plugin @rollup/plugin-node-resolve --file bundle.js --format iife
node test.js
npx -y xlsx-cli Presidents.xlsx | Select-Object -First 3
Remove-Item -Path "bundle.js" -Force
Remove-Item -Path "Presidents.xlsx" -Force
## RollupJS 2.x
npm i --save rollup@2.x @rollup/plugin-node-resolve
npx -y rollup@2.x index.js --plugin @rollup/plugin-node-resolve --file bundle.js --format iife
node test.js
npx -y xlsx-cli Presidents.xlsx | Select-Object -First 3
Remove-Item -Path "bundle.js" -Force
Remove-Item -Path "Presidents.xlsx" -Force
## RollupJS 1.x
npm i --save rollup@1.x rollup-plugin-node-resolve
npx -y rollup@1.x index.js --plugin rollup-plugin-node-resolve --file bundle.js --format iife
node test.js
npx -y xlsx-cli Presidents.xlsx | Select-Object -First 3
Remove-Item -Path "bundle.js" -Force
Remove-Item -Path "Presidents.xlsx" -Force
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

103
tests/bundler/rspack.ps1 Normal file

@ -0,0 +1,103 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/frontend/bundler/rspack
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-rspack"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
npm init -y
$packageJson = Get-Content package.json -Raw -Encoding UTF8 | ConvertFrom-Json
$packageJson.PSObject.Properties.Remove("type")
$packageJson | ConvertTo-Json -Depth 20 | Set-Content package.json -Encoding ASCII
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz @rspack/core @rspack/cli
New-Item -ItemType Directory -Path "src" | Out-Null
@'
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");
});
'@ | Out-File -FilePath "src\index.js" -Encoding utf8
npx -p @rspack/cli rspack build
@'
<!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>
'@ | Out-File -FilePath "index.html" -Encoding utf8
npm i --save puppeteer express
@'
const puppeteer = require('puppeteer');
const express = require('express');
const app = express();
app.use(express.static('./'));
app.listen(7262, async() => {
await new Promise((res,rej) => setTimeout(res, 1000));
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.on("console", msg => console.log("PAGE LOG:", msg.text()));
await page.setViewport({width: 1920, height: 1080});
const client = await page.target().createCDPSession();
await client.send('Browser.setDownloadBehavior', {
behavior: 'allow',
downloadPath: require("path").resolve('./')
});
page.on('request', req => console.log(req.url()));
await page.goto('http://localhost:7262/');
await page.click("#xport");
await new Promise((res,rej) => setTimeout(res, 1000));
await browser.close();
process.exit();
});
'@ | Out-File -FilePath "test.js" -Encoding utf8
node test.js
npx -y xlsx-cli Presidents.xlsx | Select-Object -First 3
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

@ -92,4 +92,3 @@ EOF
node test.js
npx -y xlsx-cli Presidents.xlsx | head -n 3

@ -0,0 +1,5 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/frontend/bundler/
Write-Error "Snowpack does not work in Windows!"
Exit 1

109
tests/bundler/swcpack.ps1 Normal file

@ -0,0 +1,109 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/frontend/bundler/swcpack
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-spack"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
npm init -y
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz regenerator-runtime @swc/cli @swc/core
npm ls | Select-String "core"
@'
import { utils, version, 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");
});
'@ | Out-File -FilePath "index.js" -Encoding utf8
@'
module.exports = ({
entry: {
'web': __dirname + '/index.js',
},
output: {
path: __dirname + '/lib'
},
module: {},
});
'@ | Out-File -FilePath "spack.config.js" -Encoding utf8
npx spack
@'
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
<h1>SheetJS Presidents Demo</h1>
<button id="xport">Click here to export</button>
<script src="./lib/web.js"></script>
</body>
</html>
'@ | Out-File -FilePath "index.html" -Encoding utf8
npm i --save puppeteer express@4
@'
const puppeteer = require('puppeteer');
const express = require('express');
const app = express();
app.use(express.static('./'));
app.listen(7262, async() => {
await new Promise((res,rej) => setTimeout(res, 1000));
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.on("console", msg => console.log("PAGE LOG:", msg.text()));
await page.setViewport({width: 1920, height: 1080});
const client = await page.target().createCDPSession();
await client.send('Browser.setDownloadBehavior', {
behavior: 'allow',
downloadPath: require("path").resolve('./')
});
await page.goto('http://localhost:7262/');
await page.click("#xport");
await new Promise((res,rej) => setTimeout(res, 1000));
await browser.close();
process.exit();
});
'@ | Out-File -FilePath "test.js" -Encoding utf8
node test.js
npx -y xlsx-cli Presidents.xlsx | Select-Object -First 3
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

@ -0,0 +1,69 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/frontend/bundler/systemjs
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-systemjs"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
npm init -y
## NodeJS Demo
npm i --save https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz
Invoke-WebRequest -Uri "https://docs.sheetjs.com/systemjs/SheetJSystem.js" -OutFile "SheetJSystem.js"
$versions = @("6.x", "0.21.6", "0.20.19", "0.19.47")
foreach ($v in $versions) {
Write-Host "SystemJS NodeJS version $v"
npm i --save "systemjs@$v"
node SheetJSystem.js
npx -y xlsx-cli Presidents.xlsx | Select-Object -First 3
Remove-Item -Path "Presidents.xlsx" -Force
}
## Browser Demo
Invoke-WebRequest -Uri "https://docs.sheetjs.com/pres.xlsx" -OutFile "pres.xlsx"
npm i --save puppeteer
@'
const path = require('path');
const puppeteer = require('puppeteer');
(async() => {
await new Promise((res,rej) => setTimeout(res, 1000));
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.on("console", msg => console.log("PAGE LOG:", msg.text()));
await page.setViewport({width: 1920, height: 1080});
const client = await page.target().createCDPSession();
await client.send('Browser.setDownloadBehavior', {
behavior: 'allow',
downloadPath: require("path").resolve('./')
});
await page.goto('https://docs.sheetjs.com/systemjs/systemjs');
await new Promise((res,rej) => setTimeout(res, 3000)); // required in windows tests
const xlf = await page.$('#xlf');
await xlf.uploadFile(path.resolve('./pres.xlsx'));
await page.evaluate(() => {
const xlf = document.querySelector('#xlf');
const evt = new Event('change', { bubbles: true });
xlf.dispatchEvent(evt);
});
await new Promise((res,rej) => setTimeout(res, 1000));
const text = await page.$eval('#out', el => el.innerText);
console.log(text);
await browser.close();
process.exit();
})();
'@ | Out-File -FilePath "test.js" -Encoding utf8
Write-Host "SystemJS Browser Demo"
node test.js
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

@ -59,5 +59,6 @@ const puppeteer = require('puppeteer');
})();
EOF
echo "SystemJS Browser Demo"
node test.js

115
tests/bundler/vite.ps1 Normal file

@ -0,0 +1,115 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/frontend/bundler/vitejs
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-vite-tests"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
for ($n = 3; $n -le 7; $n++) {
Write-Host "Processing Vite version $n"
$projectDir = Join-Path -Path $tempDir -ChildPath "sheetjs-vite$n"
npm create -y "vite@$n" sheetjs-vite$n -- --template vue-ts --no-rolldown --no-interactive
Set-Location -Path $projectDir
npm i
npm i --save puppeteer express@4
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
npm ls | Select-String "vite"
## See https://github.com/vuejs/language-tools/issues/4484#issuecomment-2182469459
if ($n -eq 4) { npm i "vue-tsc@2" }
New-Item -ItemType Directory -Path "data" -Force | Out-Null
Invoke-WebRequest -Uri "https://docs.sheetjs.com/pres.xlsx" -OutFile "data\pres.xlsx"
@'
<script setup lang="ts">
import { version, utils, writeFileXLSX } from 'xlsx';
interface President {
terms: { "type": "prez" | "viceprez"; }[];
name: { first: string; last: string; }
bio: { birthday: string; }
}
async function xport() {
/* fetch JSON data and parse */
const url = "https://docs.sheetjs.com/executive.json";
const raw_data: President[] = 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 */
console.log(utils.sheet_to_csv(worksheet));
writeFileXLSX(workbook, "Presidents.xlsx");
}
</script>
<template>
<button type="button" @click="xport">Export with SheetJS version {{ version }}</button>
</template>
'@ | Out-File -FilePath "src\components\HelloWorld.vue" -Encoding utf8
npx vite build
@'
const puppeteer = require('puppeteer');
const express = require('express');
const app = express();
app.use(express.static('./dist/'));
app.listen(7262, async() => {
await new Promise((res,rej) => setTimeout(res, 1000));
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.on("console", msg => console.log("PAGE LOG:", msg.text()));
await page.setViewport({width: 1920, height: 1080});
const client = await page.target().createCDPSession();
await client.send('Browser.setDownloadBehavior', {
behavior: 'allow',
downloadPath: require("path").resolve('./')
});
page.on('request', req => console.log(req.url()));
await page.goto('http://localhost:7262/');
await page.click("button");
await new Promise((res,rej) => setTimeout(res, 2000));
await browser.close();
process.exit();
});
'@ | Out-File -FilePath "test.cjs" -Encoding utf8
node test.cjs
npx -y xlsx-cli Presidents.xlsx | Select-Object -First 3
Remove-Item -Path "Presidents.xlsx" -Force
Set-Location -Path $tempDir
}
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

150
tests/bundler/webpack.ps1 Normal file

@ -0,0 +1,150 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/frontend/bundler/webpack
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-webpack"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
npm init -y
npm i --save https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz
@'
import { utils, version, writeFileXLSX } from 'xlsx/dist/xlsx.full.min.js';
document.getElementById("xport").addEventListener("click", function() {
/* fetch JSON data and parse */
var url = "https://docs.sheetjs.com/executive.json";
fetch(url).then(function(res) { return res.json(); }).then(function(raw_data) {
/* filter for the Presidents */
var prez = raw_data.filter(function(row) { return row.terms.some(function(term) { return term.type === "prez"; }); });
/* sort by first presidential term */
prez.forEach(function(row) {
row.start = row.terms.find(function(term) {
return term.type === "prez";
}).start;
});
prez.sort(function(l,r) { return l.start.localeCompare(r.start); });
/* flatten objects */
var rows = prez.map(function(row) { return {
name: row.name.first + " " + row.name.last,
birthday: row.bio.birthday
}; });
/* generate worksheet and workbook */
var worksheet = utils.json_to_sheet(rows);
var 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 */
var max_width = rows.reduce(function(w, r) { return 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");
});
});
'@ | Out-File -FilePath "index.js" -Encoding utf8
@'
module.exports = {
/* entry point index.js */
entry: './index.js',
/* write to index.min.js */
output: { path:__dirname, filename: './index.min.js' }
}
'@ | Out-File -FilePath "webpack.config.js" -Encoding utf8
@'
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
<h1>SheetJS Presidents Demo</h1>
<button id="xport">Click here to export</button>
<script src="./index.min.js"></script>
</body>
</html>
'@ | Out-File -FilePath "index.html" -Encoding utf8
npm i --save puppeteer express@4
@'
const puppeteer = require('puppeteer');
const express = require('express');
const app = express();
app.use(express.static('./'));
app.listen(7262, async() => {
await new Promise((res,rej) => setTimeout(res, 1000));
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.on("console", msg => console.log("PAGE LOG:", msg.text()));
await page.setViewport({width: 1920, height: 1080});
const client = await page.target().createCDPSession();
await client.send('Browser.setDownloadBehavior', {
behavior: 'allow',
downloadPath: require("path").resolve('./')
});
await page.goto('http://localhost:7262/');
await page.click("#xport");
await new Promise((res,rej) => setTimeout(res, 1000));
await browser.close();
process.exit();
});
'@ | Out-File -FilePath "test.js" -Encoding utf8
$packageJson = Get-Content package.json -Raw -Encoding UTF8 | ConvertFrom-Json
$packageJson.PSObject.Properties.Remove("type")
$packageJson | ConvertTo-Json -Depth 20 | Set-Content package.json -Encoding ASCII
## Webpack 2.x
npx -y webpack@2.x -p
node test.js
npx -y xlsx-cli Presidents.xlsx | Select-Object -First 3
Remove-Item -Path "index.min.js" -Force
Remove-Item -Path "Presidents.xlsx" -Force
## Webpack 3.x
npx -y webpack@3.x -p
node test.js
npx -y xlsx-cli Presidents.xlsx | Select-Object -First 3
Remove-Item -Path "index.min.js" -Force
Remove-Item -Path "Presidents.xlsx" -Force
## Webpack 4.x and 5.x do not need to import the standalone build
$indexContent = Get-Content "index.js" -Raw
$indexContent = $indexContent -replace '/dist/xlsx.full.min.js', ''
$indexContent | Out-File -FilePath "index.js" -Encoding utf8
## Webpack 4.x
npm i --save webpack@4.x webpack-cli@4.x
npx -y webpack --mode=production
node test.js
npx -y xlsx-cli Presidents.xlsx | Select-Object -First 3
Remove-Item -Path "index.min.js" -Force
Remove-Item -Path "Presidents.xlsx" -Force
## Webpack 5.x
npm i --save webpack@5.x webpack-cli@5.x
npx -y webpack --mode=production
node test.js
npx -y xlsx-cli Presidents.xlsx | Select-Object -First 3
Remove-Item -Path "index.min.js" -Force
Remove-Item -Path "Presidents.xlsx" -Force
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

96
tests/bundler/wmr.ps1 Normal file

@ -0,0 +1,96 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/frontend/bundler/
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-wmr"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
npm init -y
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
@'
import { utils, version, 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");
});
'@ | Out-File -FilePath "index.js" -Encoding utf8
@'
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
<h1>SheetJS Presidents Demo</h1>
<button id="xport">Click here to export</button>
<script type="module" src="./index.js"></script>
</body>
</html>
'@ | Out-File -FilePath "index.html" -Encoding utf8
npx -y wmr@3.8.0 build
npm i --save puppeteer express@4
@'
const puppeteer = require('puppeteer');
const express = require('express');
const app = express();
app.use(express.static('./dist'));
app.listen(7262, async() => {
await new Promise((res,rej) => setTimeout(res, 1000));
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.on("console", msg => console.log("PAGE LOG:", msg.text()));
await page.setViewport({width: 1920, height: 1080});
const client = await page.target().createCDPSession();
await client.send('Browser.setDownloadBehavior', {
behavior: 'allow',
downloadPath: require("path").resolve('./')
});
await page.goto('http://localhost:7262/');
await page.click("#xport");
await new Promise((res,rej) => setTimeout(res, 1000));
await browser.close();
process.exit();
});
'@ | Out-File -FilePath "test.js" -Encoding utf8
node test.js
npx -y xlsx-cli Presidents.xlsx | Select-Object -First 3
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

39
tests/cli/bunsea.ps1 Normal file

@ -0,0 +1,39 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/cli/bunsea
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-bunsea"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
bun --version
curl -o pres.numbers https://docs.sheetjs.com/pres.numbers
@'
const XLSX = require("xlsx");
/* process.argv[2] is the first argument to the script */
const filename = process.argv[2];
/* read file */
const wb = XLSX.readFile(filename);
/* generate CSV of first sheet */
const ws = wb.Sheets[wb.SheetNames[0]];
const csv = XLSX.utils.sheet_to_csv(ws);
/* print to terminal */
console.log(csv);
'@ | Out-File -FilePath "sheet2csv.ts" -Encoding utf8
bun install https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
bun run sheet2csv.ts pres.numbers
bun build ./sheet2csv.ts --compile --outfile sheet2csv
./sheet2csv pres.numbers
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

21
tests/cli/denosea.ps1 Normal file

@ -0,0 +1,21 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/cli/denosea
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-cli-deno"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
deno --version
curl -o pres.numbers https://docs.sheetjs.com/pres.numbers
deno run -r --allow-read --allow-import https://docs.sheetjs.com/cli/sheet2csv.ts pres.numbers
deno compile -r --allow-read --allow-import https://docs.sheetjs.com/cli/sheet2csv.ts
./sheet2csv pres.numbers
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

26
tests/cli/nexe.ps1 Normal file

@ -0,0 +1,26 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/cli/nexe
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-nexe"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
Invoke-WebRequest -Uri "https://docs.sheetjs.com/pres.numbers" -OutFile "pres.numbers"
Invoke-WebRequest -Uri "https://docs.sheetjs.com/cli/xlsx-cli.js" -OutFile "xlsx-cli.js"
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz exit-on-epipe commander@2
$ARCH = $env:PROCESSOR_ARCHITECTURE
switch ($ARCH) {
"AMD64" { npx -y nexe -t 14.15.3 xlsx-cli.js }
"ARM64" { npx -y nexe xlsx-cli.js --build --python=$(Get-Command python3).Source }
default { Write-Error "unsupported architecture: $ARCH"; exit 1 }
}
.\xlsx-cli pres.numbers
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

@ -6,7 +6,6 @@ mkdir sheetjs-nexe
cd sheetjs-nexe
curl -o pres.numbers https://docs.sheetjs.com/pres.numbers
curl -o xlsx-cli.js https://docs.sheetjs.com/cli/xlsx-cli.js
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz exit-on-epipe commander@2
@ -26,5 +25,4 @@ case "$ARCH" in
*) echo "unsupported architecture: $ARCH"; exit 1 ;;
esac
# Run the generated binary
./xlsx-cli pres.numbers

115
tests/cli/nodesea.ps1 Normal file

@ -0,0 +1,115 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/cli/nodesea
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-sea"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
# NOTE: This effectuates "Native Tools Command Prompt"
$vsVersions = @("2022\Community", "2022\Professional", "2022\Enterprise", "2019\Community")
foreach ($vsVersion in $vsVersions) {
$vcbasePath = "${env:ProgramFiles}\Microsoft Visual Studio\$vsVersion"
$vcvarsPath = "${env:ProgramFiles}\Microsoft Visual Studio\$vsVersion\VC\Auxiliary\Build\vcvars64.bat"
if (Test-Path -Path $vcvarsPath) { break }
}
$tempEnvFile = [System.IO.Path]::GetTempFileName()
Push-Location "$vcbasePath"
cmd.exe /c "`"$vcvarsPath`" && set > `"$tempEnvFile`""
Pop-Location
Get-Content "$tempEnvFile" | ForEach-Object { if ($_ -match '^([^=]+)=(.*)$') {
$name = $matches[1]
$value = $matches[2]
if ($name -ne 'PATH') {
Set-Item -Path "Env:$name" -Value $value
} else {
$env:PATH = "$value;$env:PATH"
}
} }
Remove-Item "$tempEnvFile" -Force -ErrorAction SilentlyContinue
node --version
npm init -y
@'
// For NodeJS SEA, the CommonJS `require` must be used
const { createRequire } = require('node:module');
require = createRequire(__filename);
const { readFile, utils } = require("xlsx");
// argv[2] is the first argument to the script
const filename = process.argv[2];
// read file
const wb = readFile(filename);
// generate CSV of first sheet
const ws = wb.Sheets[wb.SheetNames[0]];
const csv = utils.sheet_to_csv(ws);
// print to terminal
console.log(csv);
'@ | Out-File -FilePath "sheet2csv.js" -Encoding utf8
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
### Script Test
Invoke-WebRequest -Uri "https://docs.sheetjs.com/pres.numbers" -OutFile "pres.numbers"
node sheet2csv.js pres.numbers
### SEA Bundle
@'
{
"main": "sheet2csv.js",
"output": "sheet2csv.blob"
}
'@ | Out-File -FilePath "sheet2csv.json" -Encoding utf8
node --experimental-sea-config sheet2csv.json
# Local Copy
$nodePath = (Get-Command node).Source
Copy-Item $nodePath "sheet2csv.exe"
# Remove the code signature
# (windows)
signtool remove /s .\sheet2csv.exe
# Inject the SEA bundle
## NOTE: npx -y postject seemed to fail in windows :(
npm i -g postject
postject --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 sheet2csv.exe NODE_SEA_BLOB sheet2csv.blob
# Resign the binary
$cert = New-SelfSignedCertificate -Type CodeSigning -DnsName "www.onlyspans.net" -CertStoreLocation Cert:\CurrentUser\My
$pass = ConvertTo-SecureString -String "hunter2" -Force -AsPlainText
Export-PfxCertificate -Cert "cert:\CurrentUser\My\$($cert.Thumbprint)" -FilePath "mycert.pfx" -Password $pass
## NOTE: if self-signed cert is not trusted, verify will show an error
$cert | Export-Certificate -FilePath "mycert.cer"
Import-Certificate -FilePath "mycert.cer" -CertStoreLocation Cert:\CurrentUser\Root | Out-Null
signtool sign /v /f mycert.pfx /p hunter2 /fd SHA256 sheet2csv.exe
### Standalone Test
.\sheet2csv.exe pres.numbers
# Validate the signature
signtool verify /v /pa sheet2csv.exe
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

@ -48,18 +48,26 @@ EOF
node --experimental-sea-config sheet2csv.json
# Local Copy
cp `which node` sheet2csv
## NOTE: codesign required for macOS
# Remove the code signature
# (macos)
command -v codesign
has_cs=$?
if [[ "$has_cs" == "0" ]]; then codesign --remove-signature ./sheet2csv; fi
# Inject the SEA bundle
npx -y postject --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 --macho-segment-name NODE_SEA sheet2csv NODE_SEA_BLOB sheet2csv.blob
# Resign the binary
if [[ "$has_cs" == "0" ]]; then codesign -s - ./sheet2csv; fi
### Standalone Test
./sheet2csv pres.numbers
# Validate the signature
if [[ "$has_cs" == "0" ]]; then codesign -dv ./sheet2csv; fi

23
tests/cli/pkg.ps1 Normal file

@ -0,0 +1,23 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/cli/pkg
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-pkg"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
Invoke-WebRequest -Uri "https://docs.sheetjs.com/pres.numbers" -OutFile "pres.numbers"
Invoke-WebRequest -Uri "https://docs.sheetjs.com/cli/xlsx-cli.js" -OutFile "xlsx-cli.js"
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz exit-on-epipe commander@2
$OS = "win"
$ARCH = "x64"
npx -y pkg -t "node18-win-$ARCH,node18-linux-$ARCH,node18-macos-$ARCH" xlsx-cli.js
.\xlsx-cli-win.exe pres.numbers
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

@ -6,7 +6,6 @@ mkdir sheetjs-pkg
cd sheetjs-pkg
curl -o pres.numbers https://docs.sheetjs.com/pres.numbers
curl -o xlsx-cli.js https://docs.sheetjs.com/cli/xlsx-cli.js
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz exit-on-epipe commander@2
@ -26,8 +25,6 @@ case "$ARCH" in
*) echo "unsupported arch: $ARCH"; exit 1 ;;
esac
# Build for the current platform
npx -y pkg -t "node18-win-${ARCH},node18-linux-${ARCH},node18-macos-${ARCH}" xlsx-cli.js
# Run the appropriate binary based on OS
./xlsx-cli-${OS} pres.numbers

56
tests/cli/txiki.ps1 Normal file

@ -0,0 +1,56 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/cli/txiki
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-txiki"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
Invoke-WebRequest -Uri "https://docs.sheetjs.com/pres.numbers" -OutFile "pres.numbers"
@'
const XLSX = require("./xlsx.full.min");
/* tjs.args[1] is the first argument to the script */
const filename = tjs.args[1];
/* read and parse file */
const data = await tjs.readFile(filename);
const wb = XLSX.read(data);
/* generate CSV of first sheet */
const ws = wb.Sheets[wb.SheetNames[0]];
const csv = XLSX.utils.sheet_to_csv(ws);
/* print to terminal */
console.log(csv);
'@ | Out-File -FilePath "sheet2csv.js" -Encoding utf8
Invoke-WebRequest -Uri "https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js" -OutFile "xlsx.full.min.js"
$txikiUrl = "https://github.com/saghul/txiki.js/releases/download/v24.12.0/txiki-windows-x86_64.zip"
$txikiZip = "txiki-windows-x86_64.zip"
Invoke-WebRequest -Uri $txikiUrl -OutFile $txikiZip
# Extract the zip file
Expand-Archive -Path $txikiZip -DestinationPath "."
# Move contents from subdirectory to current directory
$txikiDir = Get-ChildItem -Directory -Filter "txiki-windows-x86_64*" | Select-Object -First 1
if ($txikiDir) {
Get-ChildItem -Path $txikiDir.FullName -File | Move-Item -Destination "."
Remove-Item -Path $txikiDir.FullName -Recurse -Force
}
# Bundle the script
npx -y esbuild sheet2csv.js --bundle --outfile=bundle.js --platform=neutral
# Compile and run
.\tjs compile bundle.js sheet2csv
.\sheet2csv pres.numbers
# Cleanup
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

45
tests/data/alasql-bun.ps1 Normal file

@ -0,0 +1,45 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/data/alasql/#nodejs-example
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-alasql"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
@'
{
"overrides": {
"xlsx": "https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz"
}
}
'@ | Out-File -FilePath "package.json" -Encoding utf8
bun i --save alasql@3.1.0 https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
Invoke-WebRequest -Uri "https://docs.sheetjs.com/pres.numbers" -OutFile "pres.numbers"
@'
const { promise: alasql } = require("alasql");
(async() => {
/* read data from spreadsheet to JS */
const data = await alasql(`
SELECT \`Name\`, \`Index\`
FROM XLSX("pres.numbers", {autoExt:false})
WHERE \`Index\` < 45
`);
console.log(data);
/* write data from JS to spreadsheet */
data.push({ Name: "SheetJS Dev", Index: 47 });
await alasql(`SELECT * INTO XLSX("SheetJSAlaSQL1.xlsx") FROM ?`, [data]);
})();
'@ | Out-File -FilePath "SheetJSAlaSQL.js" -Encoding utf8
bun run SheetJSAlaSQL.js
bunx xlsx-cli SheetJSAlaSQL1.xlsx
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

@ -0,0 +1,45 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/data/alasql/#nodejs-example
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-alasql"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
@'
{
"overrides": {
"xlsx": "https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz"
}
}
'@ | Out-File -FilePath "package.json" -Encoding utf8
npm i --save alasql@3.1.0 https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
Invoke-WebRequest -Uri "https://docs.sheetjs.com/pres.numbers" -OutFile "pres.numbers"
@'
const { promise: alasql } = require("alasql");
(async() => {
/* read data from spreadsheet to JS */
const data = await alasql(`
SELECT \`Name\`, \`Index\`
FROM XLSX("pres.numbers", {autoExt:false})
WHERE \`Index\` < 45
`);
console.log(data);
/* write data from JS to spreadsheet */
data.push({ Name: "SheetJS Dev", Index: 47 });
await alasql(`SELECT * INTO XLSX("SheetJSAlaSQL1.xlsx") FROM ?`, [data]);
})();
'@ | Out-File -FilePath "SheetJSAlaSQL.js" -Encoding utf8
node SheetJSAlaSQL.js
npx xlsx-cli SheetJSAlaSQL1.xlsx
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

52
tests/data/knexjs.ps1 Normal file

@ -0,0 +1,52 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/data/knex
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-knexjs"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
# Download sqlite CLI
Invoke-WebRequest -Uri "https://www.sqlite.org/2026/sqlite-tools-win-x64-3510200.zip" -OutFile "sqlite.zip"
Expand-Archive sqlite.zip
Move-Item */sqlite3.exe .
npm init -y
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
Invoke-WebRequest -Uri "https://docs.sheetjs.com/knex/SheetJSKnexTest.js" -OutFile "SheetJSKnexTest.js"
Invoke-WebRequest -Uri "https://docs.sheetjs.com/pres.numbers" -OutFile "pres.numbers"
Copy-Item -Path "SheetJSKnexTest.js" -Destination "SheetJSKnexTestOrig.js"
# Workaround for KnexJS 0.21.20
Get-Content -Path "SheetJSKnexTestOrig.js" | ForEach-Object { $_ -replace 'better-sqlite3', 'sqlite' } | Out-File -FilePath "SheetJSKnexTest.js" -Encoding utf8
$oldVersions = @("0.21.20")
foreach ($n in $oldVersions) {
npm i --save knex@$n sqlite3
npm ls | Select-String "knex"
node SheetJSKnexTest.js
npx xlsx-cli SheetJSKnex.xlsx
.\sqlite3.exe SheetJSKnex.db 'select * from Test_Table'
}
# Newer KnexJS versions
Move-Item -Path "SheetJSKnexTestOrig.js" -Destination "SheetJSKnexTest.js" -Force
$newVersions = @("2.4", "2.5", "3.1")
foreach ($n in $newVersions) {
npm i --save knex@$n better-sqlite3
npm ls | Select-String "knex"
node SheetJSKnexTest.js
npx xlsx-cli SheetJSKnex.xlsx
.\sqlite3.exe SheetJSKnex.db 'select * from Test_Table'
}
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

110
tests/data/pouchdb.ps1 Normal file

@ -0,0 +1,110 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/data/pouchdb
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-pouch"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
Invoke-WebRequest -Uri "https://docs.sheetjs.com/pouchdb/master.zip" -OutFile "master.zip"
Expand-Archive -Path "master.zip" -DestinationPath "."
Set-Location -Path "getting-started-todo-master"
# awk 'NR>1{print p} {p = $0}' js/app.js > export_code.js
$prev = $null
Get-Content js/app.js | ForEach-Object {
if ($prev -ne $null) { $_out += "`n" + $prev }
$prev = $_
}
$_out | Set-Content export_code.js
@'
document.getElementById("xport").addEventListener("click", function() {
console.log("clicked");
db.allDocs({include_docs: true, descending: true}, function(err, doc) {
const aoo = doc.rows.map(r => {
const { _id, _rev, ...rest } = r.doc;
return rest;
});
const ws = XLSX.utils.json_to_sheet(aoo);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
XLSX.writeFile(wb, "SheetJSPouch.xlsx");
});
});
})();
'@ | Out-File -FilePath "export_code.js" -Append -Encoding utf8
Copy-Item -Path "export_code.js" -Destination "js\app.js" -Force
$indexContent = Get-Content -Path "index.html" -Raw
$modifiedIndex = $indexContent -replace '<body>', '<body><script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js"></script><button id="xport">Export!</button>'
$modifiedIndex | Out-File -FilePath "index.html" -Encoding utf8
npm init -y
npm i --save puppeteer express@4
@'
const puppeteer = require('puppeteer');
const express = require('express');
const app = express();
app.use(express.static('./'));
app.listen(7262, async() => {
await new Promise((res,rej) => setTimeout(res, 1000));
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.on("console", msg => console.log("PAGE LOG:", msg.text()));
await page.setViewport({width: 1920, height: 1080});
const client = await page.target().createCDPSession();
await client.send('Browser.setDownloadBehavior', {
behavior: 'allow',
downloadPath: require("path").resolve('./')
});
page.on('request', req => console.log(req.url()));
await page.goto('http://localhost:7262/');
await new Promise((res,rej) => setTimeout(res, 3000));
await page.focus('#new-todo');
await page.keyboard.type('JS');
await page.keyboard.press('Enter');
await new Promise((res,rej) => setTimeout(res, 1000));
await page.focus('#new-todo');
await page.keyboard.type('Sheet');
await page.keyboard.press('Enter');
await new Promise((res,rej) => setTimeout(res, 1000));
const toggles = await page.$$('.toggle');
await toggles[0].click();
await new Promise((res,rej) => setTimeout(res, 1000));
// Click export button
await page.click('#xport');
await new Promise((res,rej) => setTimeout(res, 3000));
await browser.close();
process.exit();
});
'@ | Out-File -FilePath "test.js" -Encoding utf8
$versions = @("9.0.0", "8.0.1", "7.3.1", "6.4.3", "5.4.5", "4.0.3", "3.6.0")
foreach ($version in $versions) {
Write-Host "PouchDB $version"
Copy-Item -Path "index.html" -Destination "index.html.bak" -Force
$currentIndex = Get-Content -Path "index.html" -Raw
$modifiedIndex = $currentIndex -replace 'pouchdb/3.2.0/pouchdb.min.js', "npm/pouchdb@${version}/dist/pouchdb.min.js"
$modifiedIndex | Out-File -FilePath "index.html" -Encoding utf8
node test.js
npx xlsx-cli SheetJSPouch.xlsx | Select-Object -First 3
Remove-Item -Path "SheetJSPouch.xlsx" -Force
Copy-Item -Path "index.html.bak" -Destination "index.html" -Force
}
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

26
tests/dom/cheerio.ps1 Normal file

@ -0,0 +1,26 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/net/dom#cheeriojs
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-cheeriojs"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
Invoke-WebRequest -Uri "https://docs.sheetjs.com/dom/SheetJSCheerio.js" -OutFile "SheetJSCheerio.js" -MaximumRedirection 20
# NOTE: IWR does not correctly handle the redirect from SheetJSTable.html
Invoke-WebRequest -Uri "https://docs.sheetjs.com/dom/SheetJSTable" -OutFile "SheetJSTable.html" -MaximumRedirection 20
$versions = @("1.2.0")
foreach ($version in $versions) {
if (Test-Path -Path "SheetJSCheerio.xlsx") { Remove-Item -Path "SheetJSCheerio.xlsx" -Force }
npm i --save cheerio@$version
npm ls cheerio
node SheetJSCheerio.js
npx -y xlsx-cli SheetJSCheerio.xlsx | Select-Object -First 3
}
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

@ -11,7 +11,7 @@ npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
curl -LO https://docs.sheetjs.com/dom/SheetJSCheerio.js
curl -LO https://docs.sheetjs.com/dom/SheetJSTable.html
for n in 1.1.2; do
for n in 1.2.0; do
rm -f SheetJSCheerio.xlsx
npm i --save cheerio@$n
npm ls | grep cheerio

22
tests/email/pst.ps1 Normal file

@ -0,0 +1,22 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/net/email/pst
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-pst"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
npm init -y
Invoke-WebRequest -Uri "https://docs.sheetjs.com/pst/SheetJSPST.js" -OutFile "SheetJSPST.js"
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz pst-extractor@1.11.0
node --version
node SheetJSPST.js
bun --version
bun SheetJSPST.js
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

26
tests/engines/v8-rust.ps1 Normal file

@ -0,0 +1,26 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/engines/v8
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-rustyv8"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
cargo new sheetjs-rustyv8
Set-Location -Path "sheetjs-rustyv8"
cargo run
cargo add v8
cargo run
Invoke-WebRequest -Uri "https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js" -OutFile "xlsx.full.min.js"
Invoke-WebRequest -Uri "https://docs.sheetjs.com/pres.numbers" -OutFile "pres.numbers"
Invoke-WebRequest -Uri "https://docs.sheetjs.com/v8/main.rs" -OutFile "src\main.rs"
cargo run --release pres.numbers
npx -y xlsx-cli sheetjsw.xlsb
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

@ -18,4 +18,3 @@ curl -L -o src/main.rs https://docs.sheetjs.com/v8/main.rs
cargo run --release pres.numbers; echo $?
npx -y xlsx-cli sheetjsw.xlsb

@ -0,0 +1,66 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/getting-started/installation/bun
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-bun-dle"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
bun init -y
bun install https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
if ($LASTEXITCODE -ne 0) { bun install xlsx@https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz }
@'
import * as XLSX from 'xlsx';
import * as fs from 'fs';
XLSX.set_fs(fs);
/* 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 = XLSX.utils.json_to_sheet(rows);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "Dates");
/* fix headers */
XLSX.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 */
XLSX.writeFile(workbook, "Presidents.xlsx");
'@ | Out-File -FilePath "SheetJSBun.js" -Encoding utf8
bun build --target=bun SheetJSBun.js --outfile=app.js
Remove-Item -Path "package.json" -Force
Remove-Item -Path "bun.lockb" -Force -ErrorAction SilentlyContinue
Remove-Item -Path "bun.lock" -Force -ErrorAction SilentlyContinue
Remove-Item -Path "SheetJSBun.js" -Force
Remove-Item -Path "node_modules" -Recurse -Force -ErrorAction SilentlyContinue
bun app.js
bunx xlsx-cli Presidents.xlsx | Select-Object -First 10
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

@ -5,7 +5,8 @@ cd /tmp
rm -rf sheetjs-bun-dle
mkdir sheetjs-bun-dle
cd sheetjs-bun-dle
echo "{}" > package.json
bun init -y
bun install https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz || bun install xlsx@https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz

33
tests/math/tfjs-node.ps1 Normal file

@ -0,0 +1,33 @@
#!/usr/bin/env powershell
# https://docs.sheetjs.com/docs/demos/math/tensorflow#nodejs-demo
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-tfjs-csv"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
npm init -y
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz @tensorflow/tfjs @tensorflow/tfjs-node
Invoke-WebRequest -Uri "https://docs.sheetjs.com/tfjs/SheetJSTF.js" -OutFile "SheetJSTF.js"
# this version uses `nvm` to cycle through node versions
$nvmCommandAvailable = Get-Command "nvm" -ErrorAction SilentlyContinue
if(! ($nvmCommandAvailable)) {
node --version
node SheetJSTF.js
} else {
$versions = @(20, 22, 24)
foreach ($n in $versions) {
nvm install $n
nvm use $n
node --version
node SheetJSTF.js
}
}
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

@ -0,0 +1,96 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/net/server#worker-threads
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-worker"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
'{ "type": "module" }' | Out-File -FilePath "package.json" -Encoding ASCII
@'
/* load the worker_threads module */
import { parentPort } from 'node:worker_threads';
/* load the SheetJS module and hook to FS */
import { set_fs, readFile, write } from 'xlsx';
import * as fs from 'fs';
set_fs(fs);
/* the server will send a message with the `path` field */
parentPort.on('message', (task) => {
// read file
const wb = readFile(task.path, { dense: true });
// send back XLSX
parentPort.postMessage(write(wb, { type: "buffer", bookType: "xlsx" }));
// remove file
fs.unlink(task.path, ()=>{});
});
'@ | Out-File -FilePath "worker.js" -Encoding ASCII
Invoke-WebRequest -Uri "https://docs.sheetjs.com/server/worker_pool.js" -OutFile "worker_pool.js"
Invoke-WebRequest -Uri "https://docs.sheetjs.com/pres.numbers" -OutFile "pres.numbers"
@'
/* load dependencies */
import os from 'node:os';
import process from 'node:process'
import express from 'express';
import formidable from 'formidable';
/* load worker pool */
import WorkerPool from './worker_pool.js';
const pool = new WorkerPool(os.cpus().length);
process.on("beforeExit", () => { pool.close(); })
/* create server */
const app = express();
app.post('/', (req, res, next) => {
// parse body
const form = formidable({});
form.parse(req, (err, fields, files) => {
// look for "upload" field
if(err) return next(err);
if(!files["upload"]) return next(new Error("missing `upload` file"));
// send a message to the worker with the path to the uploaded file
pool.runTask({ path: files["upload"].filepath }, (err, result) => {
if(err) return next(err);
// send the file back as an attachment
res.attachment("SheetJSPool.xlsx");
res.status(200).end(result);
});
});
});
// start server
app.listen(7262, () => { console.log(`Example app listening on port 7262`); });
'@ | Out-File -FilePath "main.mjs" -Encoding ASCII
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz formidable@2.1.2
$expressVersions = @("4.21.2", "5.1.0")
foreach ($express in $expressVersions) {
npm i --save express@$express
npm ls | Select-String "express"
# Test with different node versions (if available)
$nodeVersions = @(20, 22, 24)
foreach ($n in $nodeVersions) {
nvm use $n
node --version
$proc = Start-Process -NoNewWindow -FilePath "node" -ArgumentList "main.mjs" -PassThru
Start-Sleep -Seconds 2
pwsh -NonInteractive -Command 'Invoke-WebRequest -Uri "http://localhost:7262/" -Method Post -Form @{ upload = Get-Item "pres.numbers" } -OutFile "SheetJSPool.xlsx"'
npx -y xlsx-cli SheetJSPool.xlsx | Select-Object -First 10
Remove-Item -Path "SheetJSPool.xlsx" -Force
$proc | Stop-Process
}
}
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

33
tests/static/eleventy.ps1 Normal file

@ -0,0 +1,33 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/static/eleventy
# This script builds the Static Site. It does not test HMR!
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-11ty"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
npm init -y
New-Item -ItemType Directory -Path "_data" | Out-Null
Invoke-WebRequest -Uri "https://docs.sheetjs.com/pres.xlsx" -OutFile "_data/pres.xlsx"
Invoke-WebRequest -Uri "https://docs.sheetjs.com/eleventy/_eleventy.js" -OutFile ".eleventy.js"
Invoke-WebRequest -Uri "https://docs.sheetjs.com/eleventy/index.njk" -OutFile "index.njk"
$versions = @("2.0.1", "3.1.2", "4.0.0-alpha.6")
foreach ($n in $versions) {
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz @11ty/eleventy@$n
npx @11ty/eleventy@$n
$htmlContent = Get-Content -Path "_site/index.html" -Raw
$clintonCount = ($htmlContent | Select-String -Pattern "Clinton" -AllMatches).Matches.Count
$besseljCount = ($htmlContent | Select-String -Pattern "BESSELJ" -AllMatches).Matches.Count
$jsCount = ($htmlContent | Select-String -Pattern '\.js' -AllMatches).Matches.Count
Write-Output "Clinton $clintonCount BESSELJ $besseljCount JS $jsCount"
# Expected output: Clinton 1 BESSELJ 0 JS 0
}
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

@ -13,7 +13,7 @@ curl -Lo _data/pres.xlsx https://docs.sheetjs.com/pres.xlsx
curl -L -o .eleventy.js https://docs.sheetjs.com/eleventy/_eleventy.js
curl -LO https://docs.sheetjs.com/eleventy/index.njk
for n in 2.0.1 3.1.2; do
for n in 2.0.1 3.1.2 4.0.0-alpha.6; do
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz @11ty/eleventy@$n
npx @11ty/eleventy@$n

84
tests/static/esbuild.ps1 Normal file

@ -0,0 +1,84 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/demos/static/esbuild
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-esb"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
npm init -y
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
npm i --save puppeteer express
@'
<!DOCTYPE html>
<html>
<head>
<title>SheetJS + ESBuild</title>
</head>
<body>
<script src="out.js"></script>
</body>
</html>
'@ | Out-File -FilePath "index.html" -Encoding utf8
@'
import data from './pres.numbers'
const elt = document.createElement('div');
elt.innerHTML = "<table><tr><th>Name</th><th>Index</th></tr>" +
data.map((row) => `<tr>
<td>${row.Name}</td>
<td>${row.Index}</td>
</tr>`).join("") +
"</table>";
document.body.appendChild(elt);
'@ | Out-File -FilePath "app.js" -Encoding utf8
Invoke-WebRequest -Uri "https://docs.sheetjs.com/esbuild/build.mjs" -OutFile "build.mjs"
Invoke-WebRequest -Uri "https://docs.sheetjs.com/pres.numbers" -OutFile "pres.numbers"
@'
const puppeteer = require('puppeteer');
const express = require('express');
const app = express();
app.use(express.static('./'));
app.listen(7262, async() => {
await new Promise((res,rej) => setTimeout(res, 1000));
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.on("console", msg => console.log("PAGE LOG:", msg.text()));
await page.setViewport({width: 1920, height: 1080});
const client = await page.target().createCDPSession();
await client.send('Browser.setDownloadBehavior', {
behavior: 'allow',
downloadPath: require("path").resolve('./')
});
page.on('request', req => console.log(req.url()));
await page.goto('http://localhost:7262/');
await new Promise((res,rej) => setTimeout(res, 1000));
const innerText = await page.evaluate(() => document.body.innerText);
console.log(innerText);
await browser.close();
process.exit();
});
'@ | Out-File -FilePath "test.js" -Encoding utf8
$versions = @("0.9.1", "0.9", "0.10", "0.11", "0.12", "0.13", "0.14", "0.15", "0.16", "0.17", "0.18", "0.19", "0.20", "0.21", "0.22", "0.23", "0.24", "0.25", "0.26", "0.27")
foreach ($n in $versions) {
npm rm --save esbuild
npm i --save esbuild@$n
npm ls | Select-String "esbuild"
if (Test-Path -Path "out.js") { Remove-Item -Path "out.js" -Force }
node build.mjs
$clintonCount = (Select-String -Path "out.js" -Pattern "Clinton" | Measure-Object).Count
$besseljCount = (Select-String -Path "out.js" -Pattern "BESSELJ" | Measure-Object).Count
Write-Host "Clinton $clintonCount BESSELJ $besseljCount"
node test.js
}
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

@ -63,7 +63,7 @@ app.listen(7262, async() => {
EOF
for n in 0.9.1 0.{9..25}; do
for n in 0.9.1 0.{9..27}; do
npm rm --save esbuild
npm i --save esbuild@$n
npm ls | grep esbuild

@ -0,0 +1,55 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/getting-started/installation/bun
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-bun"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
bun init -y
bun i --save https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz
@'
const XLSX = require("xlsx");
(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 = XLSX.utils.json_to_sheet(rows);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "Dates");
/* fix headers */
XLSX.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 */
XLSX.writeFile(workbook, "Presidents.xlsx", { compression: true });
})();
'@ | Out-File -FilePath "SheetJSNodeJS.js" -Encoding utf8
bun SheetJSNodeJS.js
npx xlsx-cli Presidents.xlsx | Select-Object -First 10
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

@ -0,0 +1,51 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/getting-started/installation/deno
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-deno"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
@'
// @deno-types="https://cdn.sheetjs.com/xlsx-0.20.3/package/types/index.d.ts"
import * as XLSX from 'https://cdn.sheetjs.com/xlsx-0.20.3/package/xlsx.mjs';
/* 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: any) => row.terms.some((term: any) => 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: any) => ({
name: row.name.first + " " + row.name.last,
birthday: row.bio.birthday
}));
/* generate worksheet and workbook */
const worksheet = XLSX.utils.json_to_sheet(rows);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "Dates");
/* fix headers */
XLSX.utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
/* calculate column width */
const max_width = rows.reduce((w: number, r: any) => Math.max(w, r.name.length), 10);
worksheet["!cols"] = [ { wch: max_width } ];
/* create an XLSX file and try to save to Presidents.xlsx */
XLSX.writeFile(workbook, "Presidents.xlsx", { compression: true });
'@ | Out-File -FilePath "SheetJSDeno.ts" -Encoding utf8
deno run -A SheetJSDeno.ts
npx xlsx-cli Presidents.xlsx | Select-Object -First 10
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force

@ -0,0 +1,55 @@
#!/usr/bin/env pwsh
# https://docs.sheetjs.com/docs/getting-started/installation/node
$oldDir = Get-Location
$tempDir = Join-Path -Path $env:TEMP -ChildPath "sheetjs-node"
if (Test-Path -Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force }
New-Item -ItemType Directory -Path $tempDir | Out-Null
Set-Location -Path $tempDir
npm init -y
npm i --save https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz
@'
const XLSX = require("xlsx");
(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 = XLSX.utils.json_to_sheet(rows);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "Dates");
/* fix headers */
XLSX.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 */
XLSX.writeFile(workbook, "Presidents.xlsx", { compression: true });
})();
'@ | Out-File -FilePath "SheetJSNodeJS.js" -Encoding utf8
node SheetJSNodeJS.js
npx xlsx-cli Presidents.xlsx | Select-Object -First 10
Set-Location $oldDir
Remove-Item -Path $tempDir -Recurse -Force