Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
6f54b38efe | |||
c9128d6202 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,4 +4,3 @@ package-lock.json
|
||||
pnpm-lock.yaml
|
||||
/docs
|
||||
node_modules
|
||||
.idea
|
83
.spelling
83
.spelling
@ -20,10 +20,7 @@ sql
|
||||
# Excel-related terms
|
||||
A1
|
||||
A1-Style
|
||||
A2
|
||||
A7
|
||||
AutoFilter
|
||||
B7
|
||||
BIFF12
|
||||
BIFF2
|
||||
BIFF3
|
||||
@ -44,17 +41,14 @@ FM3
|
||||
FMT
|
||||
FODS
|
||||
FoxPro
|
||||
Gmail
|
||||
IEEE754
|
||||
JSON
|
||||
Macrosheet
|
||||
Macrosheets
|
||||
Multiplan
|
||||
NodeMailer
|
||||
ODF
|
||||
ODS
|
||||
OData
|
||||
ORM
|
||||
OpenDocument
|
||||
OpenFormula
|
||||
PRN
|
||||
@ -115,7 +109,6 @@ tooltips
|
||||
# Other terms
|
||||
1.x
|
||||
2.x
|
||||
2FA
|
||||
3.x
|
||||
4.x
|
||||
5.x
|
||||
@ -123,27 +116,21 @@ tooltips
|
||||
7.x
|
||||
8.x
|
||||
9.x
|
||||
AArch64
|
||||
APIs
|
||||
APK
|
||||
ARM64
|
||||
ActiveX
|
||||
Airtable
|
||||
AlaSQL
|
||||
AngularJS
|
||||
AppleScript
|
||||
ArrayBuffer
|
||||
AstroJS
|
||||
Auth
|
||||
BOM
|
||||
Base64
|
||||
Base64-encoded
|
||||
Big5
|
||||
BitBucket
|
||||
Blazor
|
||||
Booleans
|
||||
Browserify
|
||||
BunJS
|
||||
Bundlers
|
||||
CDN
|
||||
CEP
|
||||
@ -151,105 +138,72 @@ CLI
|
||||
CMS
|
||||
CORS
|
||||
CPAN
|
||||
CRA
|
||||
CRX
|
||||
CS6
|
||||
CTRL
|
||||
CapacitorJS
|
||||
Chakra
|
||||
ChakraCore
|
||||
CheerioJS
|
||||
ClearScript
|
||||
CocoaPods
|
||||
CommonJS
|
||||
Cordova
|
||||
DOM
|
||||
DPI
|
||||
DanfoJS
|
||||
DataFrame
|
||||
DataGrid
|
||||
Deno
|
||||
DenoDOM
|
||||
DexieJS
|
||||
Dojo
|
||||
Downloadify
|
||||
Drash
|
||||
Duktape
|
||||
ERP
|
||||
ES3
|
||||
ES5
|
||||
ES6
|
||||
ESBuild
|
||||
ESM
|
||||
ETH
|
||||
Eleventy
|
||||
ElysiaJS
|
||||
Endian
|
||||
Ethercalc
|
||||
ExecJS
|
||||
ExpressJS
|
||||
ExtendScript
|
||||
FastifyJS
|
||||
FerretDB
|
||||
Fastify
|
||||
FileReader
|
||||
FileReaderSync
|
||||
FileSaver
|
||||
GBK
|
||||
GTX
|
||||
GatsbyJS
|
||||
Ghidra
|
||||
GitHub
|
||||
GitLab
|
||||
Goja
|
||||
GraalJS
|
||||
Gradle
|
||||
GraphQL
|
||||
GraphiQL
|
||||
HTML
|
||||
HTML5
|
||||
HTTP
|
||||
HTTPS
|
||||
HappyDOM
|
||||
Homebrew
|
||||
HonoJS
|
||||
IANA
|
||||
IE
|
||||
IE10
|
||||
IE11
|
||||
IE6
|
||||
IE8
|
||||
IE9
|
||||
IMAP
|
||||
InDesign
|
||||
IndexedDB
|
||||
Integrations
|
||||
JDK
|
||||
JE
|
||||
JS
|
||||
JSC
|
||||
JSDOM
|
||||
JSX
|
||||
JWT
|
||||
JavaScriptCore
|
||||
Javet
|
||||
JerryScript
|
||||
Jint
|
||||
Kaioken
|
||||
Kaioponent
|
||||
Kaioponents
|
||||
KeyDB
|
||||
KnexJS
|
||||
Knex
|
||||
KnockoutJS
|
||||
LLC
|
||||
LTS
|
||||
LWC
|
||||
LangChain
|
||||
LangChainJS
|
||||
Lifecycle
|
||||
LocalStorage
|
||||
LowDB
|
||||
Lume
|
||||
MUI
|
||||
MVC
|
||||
MVVM
|
||||
MacOS
|
||||
@ -257,9 +211,7 @@ MariaDB
|
||||
Mathematica
|
||||
Meridiem
|
||||
MongoDB
|
||||
MuJS
|
||||
MySQL
|
||||
NASM
|
||||
NPM
|
||||
NW.js
|
||||
Nashorn
|
||||
@ -267,7 +219,6 @@ NativeScript
|
||||
NestJS
|
||||
NetSuite
|
||||
NeutralinoJS
|
||||
Nexe
|
||||
NextJS
|
||||
NoSQL
|
||||
NodeJS
|
||||
@ -276,19 +227,14 @@ Nunjucks
|
||||
Nuxt
|
||||
NuxtJS
|
||||
OSA
|
||||
OpenSSL
|
||||
OpenJDK
|
||||
PPI
|
||||
ParcelJS
|
||||
PhantomJS
|
||||
PhoneGap
|
||||
Photoshop
|
||||
Polars
|
||||
PostgreSQL
|
||||
PouchDB
|
||||
PowerShell
|
||||
Preact
|
||||
PreactJS
|
||||
QuickJS
|
||||
R1
|
||||
R2
|
||||
@ -298,23 +244,15 @@ RDBMS
|
||||
README
|
||||
RESTlets
|
||||
RSS
|
||||
RTX
|
||||
ReactJS
|
||||
Redis
|
||||
RequireJS
|
||||
RhinoJS
|
||||
Roadmap
|
||||
Rollup
|
||||
RollupJS
|
||||
Ryzen
|
||||
S3
|
||||
SDK
|
||||
SMS
|
||||
SMTP
|
||||
SQLite
|
||||
SSG
|
||||
SSL
|
||||
SSR
|
||||
SWC
|
||||
SWF
|
||||
Schemas
|
||||
@ -323,38 +261,28 @@ SessionStorage
|
||||
Shift-JIS
|
||||
SlimerJS
|
||||
Snowpack
|
||||
Stata
|
||||
SuiteScript
|
||||
SuiteScripts
|
||||
Suitelets
|
||||
SvelteJS
|
||||
SvelteKit
|
||||
SystemJS
|
||||
Tauri
|
||||
Temurin
|
||||
TensorFlow
|
||||
UI
|
||||
UI5
|
||||
UNPKG
|
||||
URI
|
||||
UTF-16
|
||||
UTF-8
|
||||
UUID
|
||||
UXP
|
||||
Uint8Array
|
||||
V2
|
||||
V8
|
||||
VBScript
|
||||
VRAM
|
||||
VSCodium
|
||||
Valkey
|
||||
Vendoring
|
||||
Vercel
|
||||
Vite
|
||||
ViteJS
|
||||
VueJS
|
||||
VueJS-friendly
|
||||
WASM
|
||||
WMR
|
||||
WSL
|
||||
WebAssembly
|
||||
@ -363,9 +291,7 @@ WebKit
|
||||
WebSQL
|
||||
Webpack
|
||||
Win10
|
||||
Win11
|
||||
XHR
|
||||
XMLDOM
|
||||
XMLHttpRequest
|
||||
XP
|
||||
Xcode
|
||||
@ -394,9 +320,7 @@ frontmatter
|
||||
globals
|
||||
iOS
|
||||
iWork
|
||||
jQuery
|
||||
javascript
|
||||
jujutsu
|
||||
lifecycle
|
||||
localForage
|
||||
macOS
|
||||
@ -405,13 +329,11 @@ microcontrollers
|
||||
middleware
|
||||
minified
|
||||
minifier
|
||||
mitigations
|
||||
namespace
|
||||
natively
|
||||
nodejs
|
||||
npm
|
||||
parsers
|
||||
polychotomous
|
||||
pre-built
|
||||
pre-generated
|
||||
prepend
|
||||
@ -432,7 +354,6 @@ transpile
|
||||
transpiled
|
||||
transpiling
|
||||
uncheck
|
||||
uncomment
|
||||
unidimensional
|
||||
unminified
|
||||
unpkg
|
||||
|
@ -139,10 +139,6 @@ function SheetJSTestDropbox() {
|
||||
|
||||
## Other Notes
|
||||
|
||||
`static/shim.js` shims the following functions:
|
||||
|
||||
- `Object.hasOwn`
|
||||
|
||||
`src/theme/Admonition` was swizzled from 3.2.1 to enable `pass` for hiding
|
||||
header text. See Docusaurus issue 8568 for more details.
|
||||
|
||||
|
@ -1,13 +0,0 @@
|
||||
import url from './engines.xls';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
const BindingData = () => {
|
||||
const [binding, setBinding] = useState("");
|
||||
|
||||
useEffect(() => { (async() => {
|
||||
const html = await (await fetch(url)).json();
|
||||
setBinding(html["Bindings"]);
|
||||
})(); }, []);
|
||||
return ( <p dangerouslySetInnerHTML={{__html: binding}}/> );
|
||||
};
|
||||
export default BindingData;
|
@ -1,3 +1,4 @@
|
||||
import { read, utils } from 'xlsx';
|
||||
import url from './cli.xls';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
@ -5,9 +6,9 @@ const FrameworkData = () => {
|
||||
const [fw, setFW] = useState("");
|
||||
|
||||
useEffect(() => { (async() => {
|
||||
const html = await (await fetch(url)).json();
|
||||
setFW(html["Frameworks"]);
|
||||
const wb = read(await (await fetch(url)).arrayBuffer(), { dense: true });
|
||||
setFW(utils.sheet_to_html(wb.Sheets["Frameworks"]));
|
||||
})(); }, []);
|
||||
return ( <p dangerouslySetInnerHTML={{__html: fw}}/> );
|
||||
return ( <div dangerouslySetInnerHTML={{__html: fw}}/> );
|
||||
};
|
||||
export default FrameworkData;
|
||||
|
@ -34,7 +34,7 @@
|
||||
</Style>
|
||||
</Styles>
|
||||
<Worksheet ss:Name="Frameworks">
|
||||
<Table ss:ExpandedColumnCount="7" ss:ExpandedRowCount="11" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="16">
|
||||
<Table ss:ExpandedColumnCount="7" ss:ExpandedRowCount="10" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="16">
|
||||
<Column ss:Index="2" ss:Width="24"/>
|
||||
<Column ss:Width="31"/>
|
||||
<Column ss:Width="24"/>
|
||||
@ -79,7 +79,7 @@
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
@ -97,7 +97,7 @@
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
@ -106,17 +106,8 @@
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/cli/txiki"><Data ss:Type="String">txiki.js</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { read, utils } from 'xlsx';
|
||||
import url from './desktop.xls';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
@ -5,9 +6,9 @@ const FrameworkData = () => {
|
||||
const [fw, setFW] = useState("");
|
||||
|
||||
useEffect(() => { (async() => {
|
||||
const html = await (await fetch(url)).json();
|
||||
setFW(html["Frameworks"]);
|
||||
const wb = read(await (await fetch(url)).arrayBuffer(), { dense: true });
|
||||
setFW(utils.sheet_to_html(wb.Sheets["Frameworks"]));
|
||||
})(); }, []);
|
||||
return ( <p dangerouslySetInnerHTML={{__html: fw}}/> );
|
||||
return ( <div dangerouslySetInnerHTML={{__html: fw}}/> );
|
||||
};
|
||||
export default FrameworkData;
|
@ -1,13 +1,22 @@
|
||||
import { read, utils } from 'xlsx';
|
||||
import url from './engines.xls';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
const EngineData = () => {
|
||||
const [engines, setEngines] = useState("");
|
||||
const [binding, setBinding] = useState("");
|
||||
|
||||
useEffect(() => { (async() => {
|
||||
const html = await (await fetch(url)).json();
|
||||
setEngines(html["Engines"]);
|
||||
const wb = read(await (await fetch(url)).arrayBuffer(), { dense: true });
|
||||
setEngines(utils.sheet_to_html(wb.Sheets["Engines"]));
|
||||
setBinding(utils.sheet_to_html(wb.Sheets["Bindings"]));
|
||||
})(); }, []);
|
||||
return ( <p dangerouslySetInnerHTML={{__html: engines}}/> );
|
||||
return ( <>
|
||||
<p>The following engines have been tested in their native languages:</p>
|
||||
<div dangerouslySetInnerHTML={{__html: engines}}/>
|
||||
<p>The following bindings have been tested:</p>
|
||||
<div dangerouslySetInnerHTML={{__html: binding}}/>
|
||||
<p>Asterisks (✱) in the Windows columns mark tests that were run in Windows Subsystem for Linux (WSL)</p>
|
||||
</> );
|
||||
};
|
||||
export default EngineData;
|
@ -132,8 +132,8 @@
|
||||
<Cell><Data ss:Type="String">C</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
@ -244,7 +244,7 @@
|
||||
</WorksheetOptions>
|
||||
</Worksheet>
|
||||
<Worksheet ss:Name="Bindings">
|
||||
<Table ss:ExpandedColumnCount="8" ss:ExpandedRowCount="20" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="16">
|
||||
<Table ss:ExpandedColumnCount="8" ss:ExpandedRowCount="15" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="16">
|
||||
<Column ss:Index="3" ss:Width="24"/>
|
||||
<Column ss:Width="31"/>
|
||||
<Column ss:Width="24"/>
|
||||
@ -292,18 +292,8 @@
|
||||
<Cell><Data ss:Type="String">Python</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/duktape#rust"><Data ss:Type="String">Duktape</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Rust</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
@ -337,26 +327,6 @@
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/v8#c"><Data ss:Type="String">V8</Data></Cell>
|
||||
<Cell><Data ss:Type="String">C#</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/v8#python"><Data ss:Type="String">V8</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Python</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/jsc#swift"><Data ss:Type="String">JSC</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Swift</Data></Cell>
|
||||
@ -367,16 +337,6 @@
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/jsc#rust"><Data ss:Type="String">JSC</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Rust</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/rb#complete-example"><Data ss:Type="String">ExecJS</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Ruby</Data></Cell>
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { read, utils } from 'xlsx';
|
||||
import url from './mobile.xls';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
@ -5,9 +6,9 @@ const FrameworkData = () => {
|
||||
const [fw, setFW] = useState("");
|
||||
|
||||
useEffect(() => { (async() => {
|
||||
const html = await (await fetch(url)).json();
|
||||
setFW(html["Frameworks"]);
|
||||
const wb = read(await (await fetch(url)).arrayBuffer(), { dense: true });
|
||||
setFW(utils.sheet_to_html(wb.Sheets["Frameworks"]));
|
||||
})(); }, []);
|
||||
return ( <p dangerouslySetInnerHTML={{__html: fw}}/> );
|
||||
return ( <div dangerouslySetInnerHTML={{__html: fw}}/> );
|
||||
};
|
||||
export default FrameworkData;
|
||||
|
@ -34,7 +34,7 @@
|
||||
</Style>
|
||||
</Styles>
|
||||
<Worksheet ss:Name="Frameworks">
|
||||
<Table ss:ExpandedColumnCount="8" ss:ExpandedRowCount="18" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="16">
|
||||
<Table ss:ExpandedColumnCount="8" ss:ExpandedRowCount="17" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="16">
|
||||
<Column ss:Index="3" ss:Width="24"/>
|
||||
<Column ss:Width="31"/>
|
||||
<Column ss:Width="24"/>
|
||||
@ -79,7 +79,7 @@
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✘</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✘</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"> </Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/mobile/capacitor"><Data ss:Type="String">CapacitorJS</Data></Cell>
|
||||
@ -90,7 +90,7 @@
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✘</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✘</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"> </Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/mobile/ionic"><Data ss:Type="String">Ionic</Data></Cell>
|
||||
@ -99,7 +99,7 @@
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✘</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"> </Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✘</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"> </Data></Cell>
|
||||
</Row>
|
||||
@ -110,7 +110,7 @@
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✘</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"> </Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✘</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"> </Data></Cell>
|
||||
</Row>
|
||||
@ -121,18 +121,7 @@
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✘</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✘</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"> </Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/mobile/lynx"><Data ss:Type="String">Lynx</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✘</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✘</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"> </Data></Cell>
|
||||
</Row>
|
||||
|
@ -50,13 +50,14 @@ reading and writing many spreadsheet formats.
|
||||
`xlsx.mini.min.js` is a slimmer build that omits the following features:
|
||||
|
||||
- CSV and SYLK encodings (directly affecting users outside of the United States)
|
||||
- XLSB / XLS / Lotus 1-2-3 / SpreadsheetML 2003 / Apple Numbers file formats
|
||||
- [Stream utility functions](/docs/api/stream)
|
||||
- XLSB / XLS / Lotus 1-2-3 / SpreadsheetML 2003 / Numbers file formats
|
||||
- Stream utility functions
|
||||
|
||||
<details>
|
||||
<summary><b>How to integrate the mini build</b> (click to show)</summary>
|
||||
|
||||
A single script tag should be added at the top of the HTML page:
|
||||
Replace references to `xlsx.full.min.js` with `xlsx.mini.min.js`. Starting from
|
||||
scratch, a single script tag should be added at the top of the HTML page:
|
||||
|
||||
<CodeBlock language="html">{`\
|
||||
<!-- use xlsx.mini.min.js from version ${current} -->
|
||||
@ -75,7 +76,7 @@ strongly recommended. Vendoring decouples websites from SheetJS infrastructure.
|
||||
|
||||
2) Move the script to a `public` folder with other scripts.
|
||||
|
||||
3) Reference the vendored script from HTML pages:
|
||||
3) Reference the local script from HTML pages:
|
||||
|
||||
```html
|
||||
<script src="/public/xlsx.full.min.js"></script>
|
||||
@ -89,8 +90,9 @@ For broad compatibility with JavaScript engines, the library is written using
|
||||
ECMAScript 3 language dialect. A "shim" script provides implementations of
|
||||
functions for older browsers and environments.
|
||||
|
||||
Due to SSL compatibility issues, older versions of IE will not be able to use
|
||||
the CDN scripts directly. They should be downloaded and saved to a public path:
|
||||
Due to SSL compatibility issues, older versions of IE will not be able to
|
||||
use the CDN scripts directly. They should be downloaded and saved to a public
|
||||
directory in the site:
|
||||
|
||||
<ul>
|
||||
<li>Standalone: <a href={"https://cdn.sheetjs.com/xlsx-" + current + "/package/dist/xlsx.mini.min.js"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/package/dist/xlsx.mini.min.js"}</a></li>
|
||||
@ -116,75 +118,12 @@ importScripts("https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js"
|
||||
importScripts("https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js");`}
|
||||
</CodeBlock>
|
||||
|
||||
### Type Checker
|
||||
|
||||
:::danger VSCode Telemetry and Data Exfiltration
|
||||
|
||||
The official Microsoft builds of Visual Studio Code ("VSCode") embed telemetry
|
||||
and send information to external servers.
|
||||
|
||||
**[VSCodium](https://vscodium.com/) is a telemetry-free fork of VSCode.**
|
||||
|
||||
When writing code that may process personally identifiable information (PII),
|
||||
the SheetJS team strongly encourages building VSCode from source or using IDEs
|
||||
that do not exfiltrate data.
|
||||
|
||||
:::
|
||||
|
||||
The type checker integrated in VSCodium and VSCode does not currently provide
|
||||
type hints when using the standalone build. Using the JSDoc `@type` directive
|
||||
coupled with type imports, VSCodium will recognize the types:
|
||||
|
||||

|
||||
|
||||
<ol start="1">
|
||||
<li><p>Download the types (<code parentName="pre">index.d.ts</code>) for
|
||||
the desired version. The current version is available at <a href={"https://cdn.sheetjs.com/xlsx-" + current + "/package/types/index.d.ts"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/package/types/index.d.ts"}</a></p></li>
|
||||
</ol>
|
||||
|
||||
2) Rename the types file to `xlsx.d.ts`. It does not need to reside in the same
|
||||
folder as the standalone script.
|
||||
|
||||
3) In the browser script referencing the global, prepend the following lines:
|
||||
|
||||
```js title="Prepend this fragment in each source file referencing the XLSX global"
|
||||
/** @type {import("./xlsx")} */
|
||||
const XLSX = globalThis.XLSX;
|
||||
```
|
||||
|
||||
4) If the `xlsx.d.ts` file is in a different folder, change the argument to the
|
||||
`import` method to reflect the relative path. For example, given the structure:
|
||||
|
||||
```text title="Folder Structure"
|
||||
- /vendor
|
||||
- /vendor/xlsx.ts
|
||||
- /src
|
||||
- /src/app.js
|
||||
```
|
||||
|
||||
`/src/app.js` must refer to the types as `../vendor/xlsx`:
|
||||
|
||||
```js title="Preamble for /src/app.js when types are at /vendor/xlsx.d.ts"
|
||||
// highlight-next-line
|
||||
/** @type {import("../vendor/xlsx")} */
|
||||
const XLSX = globalThis.XLSX;
|
||||
```
|
||||
|
||||
The `.d.ts` file extension must be omitted.
|
||||
|
||||
:::warning pass
|
||||
|
||||
JSDoc types using the `@import` directive are not supported in `<script>` tags.
|
||||
|
||||
**This is a known bug with VSCode!**
|
||||
|
||||
:::
|
||||
|
||||
## ECMAScript Module Imports
|
||||
|
||||
:::info pass
|
||||
:::caution pass
|
||||
|
||||
This section refers to imports in HTML pages using `<script type="module">`.
|
||||
This section refers to imports in HTML pages using `script type="module"`.
|
||||
|
||||
The ["Frameworks and Bundlers"](/docs/getting-started/installation/frameworks)
|
||||
section covers imports in projects using bundlers (ViteJS) or frameworks
|
@ -174,30 +174,10 @@ yarn remove xlsx`}
|
||||
<ol start="1"><li><p>Download the tarball (<code parentName="pre">xlsx-{current}.tgz</code>) for the desired version. The current
|
||||
version is available at <a href={"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}</a></p></li></ol>
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
curl -O https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
2) Create a `vendor` subfolder at the root of your project and move the tarball
|
||||
to that folder. Add it to your project repository.
|
||||
|
||||
2) Create a `vendor` subfolder at the root of your project:
|
||||
|
||||
```bash
|
||||
mkdir -p vendor
|
||||
```
|
||||
|
||||
3) Move the tarball from step (1) to the `vendor` folder:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
mv xlsx-${current}.tgz vendor`}
|
||||
</CodeBlock>
|
||||
|
||||
4) If the project is managed with a version control system, add the tarball to
|
||||
the source repository. The Git VCS supports the `add` subcommand:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
git add vendor/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
5) Install the tarball using a package manager:
|
||||
3) Install the tarball using a package manager:
|
||||
|
||||
<Tabs groupId="pm">
|
||||
<TabItem value="npm" label="npm">
|
||||
@ -281,8 +261,7 @@ import { utils, writeFileXLSX } from "xlsx";
|
||||
export { utils, writeFileXLSX };
|
||||
```
|
||||
|
||||
Bundlers will typically optimize the script and only add the requested features.
|
||||
A dynamic import of the wrapper will load the optimized wrapper script:
|
||||
A dynamic import of the wrapper script will only load the requested features:
|
||||
|
||||
```js
|
||||
async function export_data() {
|
||||
|
@ -161,33 +161,12 @@ yarn remove xlsx`}
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<ol start="1"><li><p>Download the tarball (<code parentName="pre">xlsx-{current}.tgz</code>) for the desired version. The current
|
||||
version is available at <a href={"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}</a></p></li></ol>
|
||||
<ol start="1"><li><p>Download the tarball (<code parentName="pre">xlsx-{current}.tgz</code>) for the desired version. The current version is available at <a href={"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}</a></p></li></ol>
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
curl -O https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
2) Create a `vendor` subfolder at the root of your project and move the tarball
|
||||
to that folder. Add it to your project repository.
|
||||
|
||||
2) Create a `vendor` subfolder at the root of your project:
|
||||
|
||||
```bash
|
||||
mkdir -p vendor
|
||||
```
|
||||
|
||||
3) Move the tarball from step (1) to the `vendor` folder:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
mv xlsx-${current}.tgz vendor`}
|
||||
</CodeBlock>
|
||||
|
||||
4) If the project is managed with a version control system, add the tarball to
|
||||
the source repository. The Git VCS supports the `add` subcommand:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
git add vendor/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
5) Install the tarball using a package manager:
|
||||
3) Install the tarball using a package manager:
|
||||
|
||||
<Tabs groupId="pm">
|
||||
<TabItem value="npm" label="npm">
|
||||
@ -239,7 +218,7 @@ The package supports CommonJS `require` and ESM `import` module systems.
|
||||
### CommonJS `require`
|
||||
|
||||
By default, the module supports `require` and it will automatically add support
|
||||
for encodings, streams and file system access:
|
||||
for streams and file system access:
|
||||
|
||||
```js
|
||||
var XLSX = require("xlsx");
|
||||
@ -251,66 +230,6 @@ The package also ships with `xlsx.mjs`, a script compatible with the ECMAScript
|
||||
module system. When using the ESM build in NodeJS, some dependencies must be
|
||||
loaded manually.
|
||||
|
||||
:::danger ECMAScript Module Limitations
|
||||
|
||||
The original ECMAScript module specification only supported top-level imports:
|
||||
|
||||
```js
|
||||
import { Readable } from 'stream';
|
||||
```
|
||||
|
||||
If a module is unavailable, there is no way for scripts to gracefully fail or
|
||||
ignore the error. This presents an insurmountable challenge for libraries.
|
||||
|
||||
To contrast, the SheetJS CommonJS modules gracefully handle missing dependencies
|
||||
since `require` failures are errors that the library can catch and handle.
|
||||
|
||||
---
|
||||
|
||||
Patches to the specification added two different solutions to the problem:
|
||||
|
||||
- "dynamic imports" will throw errors that can be handled by libraries. Dynamic
|
||||
imports will taint APIs that do not use Promise-based methods.
|
||||
|
||||
```js
|
||||
/* Readable will be undefined if stream cannot be imported */
|
||||
const Readable = await (async() => {
|
||||
try {
|
||||
return (await import("stream"))?.Readable;
|
||||
} catch(e) { /* silently ignore error */ }
|
||||
})();
|
||||
```
|
||||
|
||||
- "import maps" control module resolution, allowing library users to manually
|
||||
shunt unsupported modules.
|
||||
|
||||
**These patches were released after browsers adopted ESM!** A number of browsers
|
||||
and other platforms support top-level imports but do not support the patches.
|
||||
|
||||
---
|
||||
|
||||
For the ESM build, there were four unpalatable options:
|
||||
|
||||
A) Generate a module script for browsers, a module script for ViteJS, a module
|
||||
script for [Deno](/docs/getting-started/installation/deno), and a module script
|
||||
for NodeJS and [BunJS](/docs/getting-started/installation/bun).
|
||||
|
||||
B) Remove all optional features, including support for non-English legacy files.
|
||||
|
||||
C) Add all optional features, effectively making the features mandatory.
|
||||
|
||||
D) Introduce special methods for optional dependency injection.
|
||||
|
||||
The SheetJS team chose option (D). NodeJS native modules are still automatically
|
||||
loaded in the CommonJS build, but NodeJS ESM scripts must now load and pass the
|
||||
dependencies to the library using special methods.
|
||||
|
||||
---
|
||||
|
||||
**It is strongly recommended to use CommonJS in NodeJS scripts!**
|
||||
|
||||
:::
|
||||
|
||||
#### Filesystem Operations
|
||||
|
||||
The `set_fs` method accepts a `fs` instance for reading and writing files using
|
||||
@ -327,7 +246,7 @@ XLSX.set_fs(fs);
|
||||
#### Stream Operations
|
||||
|
||||
The `set_readable` method accepts a `stream.Readable` instance for use in stream
|
||||
methods including [`XLSX.stream.to_csv`](/docs/api/stream):
|
||||
methods such as `XLSX.stream.to_csv`:
|
||||
|
||||
```js
|
||||
import * as XLSX from 'xlsx';
|
||||
@ -366,8 +285,6 @@ import * as fs from 'fs'; // this import will fail
|
||||
set_fs(fs);
|
||||
```
|
||||
|
||||
**This is a design flaw in NextJS!**
|
||||
|
||||
:::
|
||||
|
||||
For server-side file processing, `fs` should be loaded with a dynamic import
|
||||
|
@ -125,13 +125,25 @@ define(['N/file', 'sheetjs'], function(file, XLSX) {
|
||||
|
||||
## SAP UI5
|
||||
|
||||
OpenUI5 and SAPUI5 installation instructions are covered in the dedicated
|
||||
["OpenUI5 / SAPUI5" demo](/docs/demos/frontend/openui5#installation).
|
||||
After downloading the script, it can be uploaded to the UI5 project and loaded
|
||||
in the `sap.ui.define` call:
|
||||
|
||||
SheetJS standalone scripts can be loaded in two ways:
|
||||
```js
|
||||
sap.ui.define([
|
||||
/* ... other libraries ... */
|
||||
"path/to/xlsx.full.min"
|
||||
], function(/* ... variables for the other libraries ... */, XLSX) {
|
||||
// use XLSX here
|
||||
})
|
||||
```
|
||||
|
||||
- [`sap.ui.define`](/docs/demos/frontend/openui5#installation-define)
|
||||
- [HTML SCRIPT tag](/docs/demos/frontend/openui5#installation-html)
|
||||
:::danger pass
|
||||
|
||||
**Copy and pasting code does not work** for SheetJS scripts as they contain
|
||||
Unicode characters that may be mangled. The standalone script should be
|
||||
downloaded and manually uploaded to the project.
|
||||
|
||||
:::
|
||||
|
||||
## RequireJS
|
||||
|
||||
@ -207,8 +219,8 @@ require([
|
||||
|
||||
#### Asynchronous Loading
|
||||
|
||||
When `async` is enabled, Dojo will only understand the name `xlsx`. `dojoConfig`
|
||||
can map package names to scripts:
|
||||
When `async` is enabled, Dojo will only understand the name `xlsx`. The config
|
||||
object can map package names to scripts:
|
||||
|
||||
<CodeBlock language="html">{`\
|
||||
<script>
|
||||
|
@ -4,7 +4,7 @@ pagination_prev: getting-started/index
|
||||
pagination_next: getting-started/examples/index
|
||||
sidebar_position: 7
|
||||
sidebar_custom_props:
|
||||
summary: Load NodeJS modules using CommonJS or ESM
|
||||
summary: Load NodeJS-style modules using CommonJS or ESM
|
||||
---
|
||||
|
||||
import current from '/version.js';
|
||||
@ -76,7 +76,7 @@ The package supports CommonJS `require` and ESM `import` module systems.
|
||||
### CommonJS `require`
|
||||
|
||||
By default, the module supports `require` and it will automatically add support
|
||||
for encodings, streams and file system access:
|
||||
for streams and file system access:
|
||||
|
||||
```js
|
||||
const { readFile } = require("xlsx");
|
||||
@ -119,16 +119,12 @@ builder requires a proper `package.json` that includes the SheetJS dependency.
|
||||
|
||||
This demo was last tested in the following deployments:
|
||||
|
||||
| Architecture | BunJS | Date |
|
||||
|:-------------|:---------|:-----------|
|
||||
| `darwin-x64` | `1.2.8` | 2025-03-31 |
|
||||
| `darwin-arm` | `1.2.7` | 2025-03-30 |
|
||||
| `win11-x64` | `1.2.8` | 2025-04-17 |
|
||||
| `win11-arm` | `1.2.3` | 2025-02-23 |
|
||||
| `linux-x64` | `1.2.10` | 2025-04-21 |
|
||||
| `linux-arm` | `1.2.2` | 2025-02-16 |
|
||||
|
||||
BunJS on Windows on ARM uses the X64 compatibility layer.
|
||||
| Architecture | BunJS | Date |
|
||||
|:-------------|:--------|:-----------|
|
||||
| `darwin-x64` | `1.1.4` | 2024-04-19 |
|
||||
| `win10-x64` | `1.1.4` | 2024-04-19 |
|
||||
| `win11-x64` | `1.1.8` | 2024-05-17 |
|
||||
| `linux-x64` | `1.1.4` | 2024-04-25 |
|
||||
|
||||
:::
|
||||
|
||||
@ -140,36 +136,10 @@ cd sheetjs-bun-dle
|
||||
echo "{}" > package.json
|
||||
```
|
||||
|
||||
:::caution PowerShell Encoding Errors
|
||||
:::caution pass
|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
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 will automatically re-save the file.
|
||||
In PowerShell, the redirect will add the UTF16LE BOM. Bun does not currently
|
||||
support the encoding. The file must be resaved in UTF8 (without BOM) or ASCII.
|
||||
|
||||
:::
|
||||
|
||||
@ -179,22 +149,6 @@ VSCodium will automatically re-save the file.
|
||||
bun install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
:::caution pass
|
||||
|
||||
In some test runs, the command failed with a module resolution error:
|
||||
|
||||
<CodeBlock>{`\
|
||||
error: https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz failed to resolve`}
|
||||
</CodeBlock>
|
||||
|
||||
The workaround is to prepend `xlsx@` to the URL:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
bun install xlsx@https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
2) Save the following script to `SheetJSBun.js`:
|
||||
|
||||
```js title="SheetJSBun.js"
|
||||
@ -249,7 +203,7 @@ This procedure will generate `app.js`.
|
||||
4) Remove the module artifacts and original script:
|
||||
|
||||
```bash
|
||||
rm package.json bun.lock bun.lockb SheetJSBun.js
|
||||
rm package.json bun.lockb SheetJSBun.js
|
||||
rm -rf ./node_modules
|
||||
```
|
||||
|
||||
@ -259,7 +213,6 @@ PowerShell does not support `rm -rf`. Instead, each file must be removed:
|
||||
|
||||
```powershell title="Windows Powershell commands"
|
||||
rm package.json
|
||||
rm bun.lock
|
||||
rm bun.lockb
|
||||
rm SheetJSBun.js
|
||||
rm .\\node_modules -r -fo
|
||||
@ -276,9 +229,4 @@ bun app.js
|
||||
```
|
||||
|
||||
If the script succeeded, the file `Presidents.xlsx` will be created. That file
|
||||
can be opened in a spreadsheet editor. If a spreadsheet editor is unavailable,
|
||||
the contents can be displayed using the `xlsx-cli` tool:
|
||||
|
||||
```bash
|
||||
bunx xlsx-cli Presidents.xlsx
|
||||
```
|
||||
can be opened in a spreadsheet editor.
|
||||
|
@ -942,6 +942,13 @@ of the React Native documentation before testing the demo.
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
For Android testing, React Native requires Java 11. It will not work with
|
||||
current Java releases.
|
||||
|
||||
:::
|
||||
|
||||
:::danger pass
|
||||
|
||||
There are a number of potential pitfalls.
|
||||
@ -957,10 +964,10 @@ any issues not mentioned in the demo page.**
|
||||
Create a new project by running the following commands in the Terminal:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npx -y @react-native-community/cli@15 init SheetJSPres --version="0.76.5"
|
||||
npx -y react-native@0.73.6 init SheetJSPres --version="0.73.6"
|
||||
cd SheetJSPres
|
||||
\n\
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-native-blob-util@0.21.2`}
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-native-blob-util@0.19.8`}
|
||||
</CodeBlock>
|
||||
|
||||
Save the following to `App.tsx` in the project:
|
||||
@ -1053,10 +1060,10 @@ npx react-native start
|
||||
Once Metro is ready, it will display the commands:
|
||||
|
||||
```
|
||||
r - reload the app
|
||||
d - open developer menu
|
||||
i - run on iOS
|
||||
a - run on Android
|
||||
r - reload app
|
||||
d - open Dev Menu
|
||||
j - open DevTools
|
||||
```
|
||||
|
||||
Press `a` to run on Android. The app will launch in the emulator.
|
@ -489,9 +489,8 @@ function SheetJSAoAFilled() {
|
||||
### Select Data Rows
|
||||
|
||||
At this point, each data row will have the year in column `A` and dollar value
|
||||
in column `C`. The year (first value in the row) will be between 2007 and 2024.
|
||||
The value (third value) will be positive. The following function tests a row
|
||||
against the requirements:
|
||||
in column `C`. The year will be between 2007 and 2024 and the value will be
|
||||
positive. The following function tests a data row:
|
||||
|
||||
```js
|
||||
const is_valid_row = r =>
|
||||
@ -967,7 +966,7 @@ uses the native `FlatList` component.
|
||||
Create a new project by running the following commands in the Terminal:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npx -y @react-native-community/cli@15 init SheetJSSL --version="0.76.5"
|
||||
npx react-native@0.72.4 init SheetJSSL --version="0.72.4"
|
||||
cd SheetJSSL
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
@ -1030,7 +1029,7 @@ export default App;
|
||||
|
||||
:::note pass
|
||||
|
||||
The Android demo has been tested in Windows and macOS.
|
||||
The Android demo has been tested in Windows 10 and in macOS.
|
||||
|
||||
:::
|
||||
|
||||
@ -1043,10 +1042,10 @@ npx react-native start
|
||||
Once Metro is ready, it will display the commands:
|
||||
|
||||
```
|
||||
r - reload the app
|
||||
d - open developer menu
|
||||
i - run on iOS
|
||||
a - run on Android
|
||||
r - reload app
|
||||
d - open Dev Menu
|
||||
j - open DevTools
|
||||
```
|
||||
|
||||
Press `a` to run on Android.
|
||||
@ -1067,12 +1066,6 @@ This demo runs in iOS and requires a Macintosh computer with Xcode installed.
|
||||
|
||||
:::
|
||||
|
||||
The native component must be linked:
|
||||
|
||||
```bash
|
||||
cd ios; pod install; cd ..
|
||||
```
|
||||
|
||||
Test the app in the iOS simulator:
|
||||
|
||||
```bash
|
||||
|
@ -20,8 +20,8 @@ a large language model to generate queries based on English language input. The
|
||||
existing tooling supports CSV but does not support real spreadsheets.
|
||||
|
||||
In ["SheetJS Conversion"](#sheetjs-conversion), we will use SheetJS libraries to
|
||||
generate CSV files for the LangChainJS CSV loader. These conversions can be run
|
||||
in a preprocessing step without disrupting existing CSV workflows.
|
||||
generate CSV files for the LangChain CSV loader. These conversions can be run in
|
||||
a preprocessing step without disrupting existing CSV workflows.
|
||||
|
||||
In ["SheetJS Loader"](#sheetjs-loader), we will use SheetJS libraries in a
|
||||
custom `LoadOfSheet` data loader to directly generate documents and metadata.
|
||||
@ -33,47 +33,40 @@ SheetJS Loader to answer questions based on data from a XLS workbook.
|
||||
|
||||
This demo was tested in the following configurations:
|
||||
|
||||
| Platform | Architecture | Date |
|
||||
|:------------------------------------------------------------------|:-------------|:-----------|
|
||||
| NVIDIA RTX 4090 (24 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `win11-x64` | 2025-04-17 |
|
||||
| NVIDIA RTX 4090 (24 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `linux-x64` | 2025-01-28 |
|
||||
| AMD RX 7900 XTX (24 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `win11-x64` | 2025-01-12 |
|
||||
| AMD RX 7900 XTX (24 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `linux-x64` | 2025-01-29 |
|
||||
| Intel Arc B580 (12 GB VRAM) + Ryzen Z1 Extreme (24 GB RAM) | `win11-x64` | 2025-01-24 |
|
||||
| Intel Arc B580 (12 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `linux-x64` | 2025-02-08 |
|
||||
| Apple M4 Max 16-Core CPU + 40-Core GPU (48 GB unified memory) | `darwin-arm` | 2025-03-06 |
|
||||
| Apple M2 Max 12-Core CPU + 30-Core GPU (32 GB unified memory) | `darwin-arm` | 2025-03-25 |
|
||||
| Date | Platform |
|
||||
|:-----------|:--------------------------------------------------------------|
|
||||
| 2024-07-15 | Apple M2 Max 12-Core CPU + 30-Core GPU (32 GB unified memory) |
|
||||
| 2024-07-14 | NVIDIA RTX 4090 (24 GB VRAM) + i9-10910 (128 GB RAM) |
|
||||
| 2024-07-14 | NVIDIA RTX 4080 SUPER (16 GB VRAM) + i9-10910 (128 GB RAM) |
|
||||
|
||||
SheetJS users have verified this demo in other configurations:
|
||||
|
||||
<details>
|
||||
<summary><b>Other tested configurations</b> (click to show)</summary>
|
||||
|
||||
| Platform | Architecture | Demo |
|
||||
|:---------------------------------------------------------------------|:-------------|:------------|
|
||||
| NVIDIA L40 (48 GB VRAM) + i9-13900K (32 GB RAM) | `linux-x64` | LangChainJS |
|
||||
| NVIDIA RTX 4080 SUPER (16 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `win11-x64` | LangChainJS |
|
||||
| NVIDIA RTX 4070 Ti SUPER (16 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `win11-x64` | LangChainJS |
|
||||
| NVIDIA RTX 4070 Ti (12 GB VRAM) + Ryzen 7 5800x (64 GB RAM) | `win11-x64` | LangChainJS |
|
||||
| NVIDIA RTX 4060 (8 GB VRAM) + Ryzen 7 5700g (32 GB RAM) | `win11-x64` | LangChainJS |
|
||||
| NVIDIA RTX 3090 (24 GB VRAM) + Ryzen 9 3900XT (128 GB RAM) | `win11-x64` | LangChainJS |
|
||||
| NVIDIA RTX 3080 (12 GB VRAM) + Ryzen 7 5800X (32 GB RAM) | `win11-x64` | LangChainJS |
|
||||
| NVIDIA RTX 3070 (8 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `win11-x64` | LangChainJS |
|
||||
| NVIDIA RTX 3060 (12 GB VRAM) + i5-11400 (32 GB RAM) | `win10-x64` | LangChainJS |
|
||||
| NVIDIA RTX 2080 (12 GB VRAM) + i7-9700K (16 GB RAM) | `win10-x64` | LangChainJS |
|
||||
| NVIDIA RTX 2070 (8 GB VRAM) + Ryzen 7 3700x (80 GB RAM) | `linux-x64` | LangChainJS |
|
||||
| NVIDIA RTX 2060 (6 GB VRAM) + Ryzen 5 3600 (32 GB RAM) | `win10-x64` | LangChainJS |
|
||||
| NVIDIA GTX 1080 (8 GB VRAM) + Ryzen 7 5800x (64 GB RAM) | `win10-x64` | LangChainJS |
|
||||
| NVIDIA GTX 1070 (8 GB VRAM) + Ryzen 7 7700x (32 GB RAM) | `win11-x64` | LangChainJS |
|
||||
| AMD RX 6800 XT (16 GB VRAM) + Ryzen Z1 Extreme (16 GB RAM) | `win11-x64` | LangChainJS |
|
||||
| Apple M4 10-Core CPU + 10-Core GPU (24 GB unified memory) | `darwin-arm` | LangChainJS |
|
||||
| Demo | Platform |
|
||||
|:------------|:-------------------------------------------------------------|
|
||||
| LangChainJS | NVIDIA RTX 4070 Ti (12 GB VRAM) + Ryzen 7 5800x (64 GB RAM) |
|
||||
| LangChainJS | NVIDIA RTX 4060 (8 GB VRAM) + Ryzen 7 5700g (32 GB RAM) |
|
||||
| LangChainJS | NVIDIA RTX 3090 (24 GB VRAM) + Ryzen 9 3900XT (128 GB RAM) |
|
||||
| LangChainJS | NVIDIA RTX 3080 (12 GB VRAM) + Ryzen 7 5800X (32 GB RAM) |
|
||||
| LangChainJS | NVIDIA RTX 3060 (12 GB VRAM) + i5-11400 (32 GB RAM) |
|
||||
| LangChainJS | NVIDIA RTX 2080 (12 GB VRAM) + i7-9700K (16 GB RAM) |
|
||||
| LangChainJS | NVIDIA RTX 2060 (6 GB VRAM) + Ryzen 5 3600 (32 GB RAM) |
|
||||
| LangChainJS | NVIDIA GTX 1080 (8 GB VRAM) + Ryzen 7 5800x (64 GB RAM) |
|
||||
| LangChainJS | NVIDIA GTX 1070 (8 GB VRAM) + Ryzen 7 7700x (32 GB RAM) |
|
||||
|
||||
</details>
|
||||
|
||||
Special thanks to the following users for testing with multiple configurations:
|
||||
Special thanks to:
|
||||
|
||||
- [Asadbek Karimov](https://asadk.dev/)
|
||||
- [Rasmus Tengstedt](https://tengstedt.dev/)
|
||||
- [`@Rasmus`](https://tengstedt.dev/)
|
||||
- [Triston Armstrong](https://tristonarmstrong.com/)
|
||||
- [Ben Halverson](https://benhalverson.dev/)
|
||||
- [Navid Nami](https://github.com/CaseoJKL)
|
||||
- [`@Smor`](https://smor.dev/)
|
||||
- [`@timbr`](https://timbr.dev/)
|
||||
- [`n3bs`](https://github.com/0xn3bs)
|
||||
|
||||
:::
|
||||
|
||||
@ -81,14 +74,14 @@ Special thanks to the following users for testing with multiple configurations:
|
||||
|
||||
:::note pass
|
||||
|
||||
This explanation was verified against LangChainJS 0.2.
|
||||
This explanation was verified against LangChain 0.2.
|
||||
|
||||
:::
|
||||
|
||||
Document loaders generate data objects ("documents") and associated metadata
|
||||
from data sources.
|
||||
|
||||
LangChainJS offers a `CSVLoader`[^1] component for loading CSV data from a file:
|
||||
LangChain offers a `CSVLoader`[^1] component for loading CSV data from a file:
|
||||
|
||||
```js title="Generating Documents from a CSV file"
|
||||
import { CSVLoader } from "@langchain/community/document_loaders/fs/csv";
|
||||
@ -133,7 +126,7 @@ Index: 44
|
||||
</td></tr></tbody>
|
||||
</table>
|
||||
|
||||
The LangChainJS CSV loader will include source metadata in the document:
|
||||
The LangChain CSV loader will include source metadata in the document:
|
||||
|
||||
```js title="Document generated by the CSV loader"
|
||||
Document {
|
||||
@ -145,7 +138,7 @@ Document {
|
||||
## SheetJS Conversion
|
||||
|
||||
The [SheetJS NodeJS module](/docs/getting-started/installation/nodejs) can be
|
||||
imported in NodeJS scripts that use LangChainJS and other JavaScript libraries.
|
||||
imported in NodeJS scripts that use LangChain and other JavaScript libraries.
|
||||
|
||||
A simple pre-processing step can convert workbooks to CSV files that can be
|
||||
processed by the existing CSV tooling:
|
||||
@ -166,51 +159,14 @@ flowchart LR
|
||||
linkStyle 0,1,2 color:blue,stroke:blue;
|
||||
```
|
||||
|
||||
**Parsing files from the filesystem**
|
||||
|
||||
The SheetJS `readFile` method[^2] can read workbook files. The method accepts a
|
||||
path and returns a workbook object that conforms to the SheetJS data model[^3].
|
||||
|
||||
```js
|
||||
/* Load SheetJS Libraries */
|
||||
import { readFile, set_fs } from 'xlsx';
|
||||
|
||||
/* Load 'fs' for readFile support */
|
||||
import * as fs from 'fs';
|
||||
set_fs(fs);
|
||||
|
||||
/* Parse `pres.xlsx` */
|
||||
// highlight-next-line
|
||||
const wb = readFile("pres.xlsx");
|
||||
```
|
||||
|
||||
**Inspecting SheetJS workbook and worksheet objects**
|
||||
The SheetJS `readFile` method[^2] can read general workbooks. The method returns
|
||||
a workbook object that conforms to the SheetJS data model[^3].
|
||||
|
||||
Workbook objects represent multi-sheet workbook files. They store individual
|
||||
worksheet objects and other metadata.
|
||||
|
||||
Relevant to this discussion, the workbook object uses the following keys[^7]:
|
||||
- `SheetNames` is an array of worksheet names
|
||||
- `Sheets` is an object whose keys are sheet names and whose values are sheet objects.
|
||||
|
||||
`SheetNames[0]` is the first worksheet name, so the following snippet will pull
|
||||
the first worksheet from the workbook:
|
||||
|
||||
```js
|
||||
const first_ws = wb.Sheets[wb.SheetNames[0]];
|
||||
```
|
||||
|
||||
**Exporting SheetJS worksheets to CSV**
|
||||
|
||||
Each worksheet in the workbook can be written to CSV text using the SheetJS
|
||||
`sheet_to_csv`[^4] method. The method accepts a SheetJS worksheet object and
|
||||
returns a string.
|
||||
|
||||
```js
|
||||
const csv = utils.sheet_to_csv(first_ws);
|
||||
```
|
||||
|
||||
**Complete Script**
|
||||
`sheet_to_csv`[^4] method.
|
||||
|
||||
For example, the following NodeJS script reads `pres.xlsx` and displays CSV rows
|
||||
from the first worksheet:
|
||||
@ -232,8 +188,6 @@ const csv = utils.sheet_to_csv(first_ws);
|
||||
console.log(csv);
|
||||
```
|
||||
|
||||
### Similar Workflows {#similar-workflows}
|
||||
|
||||
:::note pass
|
||||
|
||||
A number of demos cover spiritually similar workflows:
|
||||
@ -243,8 +197,8 @@ and [Maple](/docs/demos/extensions/maple/) support XLSX data import. The SheetJS
|
||||
integrations generate clean XLSX workbooks from user-supplied spreadsheets.
|
||||
|
||||
- [TensorFlow.js](/docs/demos/math/tensorflow), [Pandas](/docs/demos/math/pandas)
|
||||
and [Mathematica](/docs/demos/extensions/mathematica) support CSV data import.
|
||||
The SheetJS integrations generate clean CSVs and use built-in CSV processors.
|
||||
and [Mathematica](/docs/demos/extensions/mathematica) support CSV. The SheetJS
|
||||
integrations generate clean CSVs and use built-in CSV processors.
|
||||
|
||||
- The ["Command-Line Tools"](/docs/demos/cli/) demo covers techniques for making
|
||||
standalone command-line tools for file conversion.
|
||||
@ -334,9 +288,9 @@ console.log(docs);
|
||||
|
||||
## SheetJS Loader
|
||||
|
||||
The LangChainJS `CSVLoader` does not add any Document metadata and does not
|
||||
generate any attributes. A custom loader can work around limitations in the CSV
|
||||
tooling and potentially include metadata that has no CSV equivalent.
|
||||
The `CSVLoader` that ships with LangChain does not add any Document metadata and
|
||||
does not generate any attributes. A custom loader can work around limitations in
|
||||
the CSV tooling and potentially include metadata that has no CSV equivalent.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
@ -453,7 +407,7 @@ export default class LoadOfSheet extends BufferLoader {
|
||||
### From Text to Binary
|
||||
|
||||
Many libraries and platforms offer generic "text" loaders that process files
|
||||
assuming the UTF-8 encoding. This corrupts many spreadsheet formats including
|
||||
assuming the UTF8 encoding. This corrupts many spreadsheet formats including
|
||||
XLSX, XLSB, XLSM and XLS.
|
||||
|
||||
:::note pass
|
||||
@ -463,15 +417,12 @@ This issue affects many JavaScript tools. Various demos cover workarounds:
|
||||
- [ViteJS plugins](/docs/demos/static/vitejs#plugins) receive the relative path
|
||||
to the workbook file and can read the file directly.
|
||||
|
||||
- [Webpack plugins](/docs/demos/static/webpack#sheetjs-loader) support a special
|
||||
`raw` option that instructs the bundler to pass raw binary data.
|
||||
|
||||
- [NuxtJS parsers and transformers](/docs/demos/static/nuxtjs) can deduce the
|
||||
path to the workbook file from internal identifiers.
|
||||
- [Webpack Plugins](/docs/demos/static/webpack#sheetjs-loader) have a special
|
||||
option to instruct the library to pass raw binary data rather than text.
|
||||
|
||||
:::
|
||||
|
||||
The `CSVLoader` extends a special `TextLoader` that forces UTF-8 text parsing.
|
||||
The `CSVLoader` extends a special `TextLoader` that forces UTF8 text parsing.
|
||||
|
||||
There is a separate `BufferLoader` class, used by the PDF loader, that passes
|
||||
the raw data using NodeJS `Buffer` objects.
|
||||
@ -720,96 +671,26 @@ export default class LoadOfSheet extends BufferLoader {
|
||||
The demo performs the query "Which rows have over 40 miles per gallon?" against
|
||||
a [sample cars dataset](pathname:///cd.xls) and displays the results.
|
||||
|
||||
:::caution pass
|
||||
|
||||
This demo was tested using the ChatQA-1.5 model[^9] in Ollama.
|
||||
|
||||
The tested model used up to 10GB VRAM. It is strongly recommended to run the
|
||||
demo on a GPU with at least 12GB VRAM or a newer Apple Silicon Mac with at least
|
||||
32GB unified memory.
|
||||
|
||||
:::
|
||||
|
||||
0) Install pre-requisites:
|
||||
|
||||
- [NodeJS LTS (version 20+)](https://nodejs.org/)
|
||||
- [Ollama](https://ollama.com/download)
|
||||
|
||||
:::note pass
|
||||
|
||||
Ollama should be installed on the same platform as NodeJS. If NodeJS is run
|
||||
within WSL, Ollama should also be installed within WSL.
|
||||
SheetJS team members have tested this demo on Windows 10 and Windows 11 using
|
||||
PowerShell and Ollama for Windows.
|
||||
|
||||
SheetJS users have also tested this demo within Windows Subsystem for Linux.
|
||||
|
||||
:::
|
||||
|
||||
:::danger pass
|
||||
:::caution pass
|
||||
|
||||
Intel ARC GPUs require the Intel Extension for PyTorch (IPEX) and a special
|
||||
version of Ollama that ships with the associated LLM Library (IPEX-LLM).
|
||||
This demo was tested using the ChatQA-1.5 model[^9] in Ollama[^10].
|
||||
|
||||
<details>
|
||||
<summary><b>ARC Instructions</b> (click to show)</summary>
|
||||
|
||||
These instructions are based on the official Intel recommendations.
|
||||
|
||||
A) If Ollama for Windows was installed, close the program by right-clicking on
|
||||
the tray icon and selecting "Quit Ollama".
|
||||
|
||||
B) Install Miniforge3[^10], selecting "Just Me" when prompted.
|
||||
|
||||
C) Launch a normal Command Prompt and create a Conda environment:
|
||||
|
||||
```bash
|
||||
cd %USERPROFILE%\Documents
|
||||
mkdir ollama-intel
|
||||
cd ollama-intel
|
||||
set PATH=%PATH%;%USERPROFILE%\miniforge3\condabin
|
||||
conda create -n llm-cpp python=3.11
|
||||
```
|
||||
|
||||
D) Activate the environment in the session and install dependencies:
|
||||
|
||||
```bash
|
||||
conda activate llm-cpp
|
||||
pip install --pre --upgrade ipex-llm[cpp]
|
||||
```
|
||||
|
||||
Close the window after the installation.
|
||||
|
||||
E) Launch a new Administrator Command Prompt and set up Ollama:
|
||||
|
||||
```bash
|
||||
cd %USERPROFILE%\Documents\ollama-intel
|
||||
set PATH=%PATH%;%USERPROFILE%\miniforge3\condabin
|
||||
conda activate llm-cpp
|
||||
init-ollama.bat
|
||||
```
|
||||
|
||||
Close the window.
|
||||
|
||||
F) Launch a normal Command Prompt window and run Ollama:
|
||||
|
||||
```bash
|
||||
cd %USERPROFILE%\Documents\ollama-intel
|
||||
set PATH=%PATH%;%USERPROFILE%\miniforge3\condabin
|
||||
conda activate llm-cpp
|
||||
set OLLAMA_NUM_GPU=999
|
||||
set no_proxy=localhost,127.0.0.1
|
||||
set ZES_ENABLE_SYSMAN=1
|
||||
set SYCL_CACHE_PERSISTENT=1
|
||||
set SYCL_PI_LEVEL_ZERO_USE_IMMEDIATE_COMMANDLISTS=1
|
||||
ollama serve
|
||||
```
|
||||
|
||||
This window should be kept open throughout the demo.
|
||||
|
||||
</details>
|
||||
The tested model used up to 9.2GB VRAM. It is strongly recommended to run the
|
||||
demo on a newer Apple Silicon Mac or a PC with an Nvidia GPU with at least 12GB
|
||||
VRAM. SheetJS users have tested the demo on systems with as little as 6GB VRAM.
|
||||
|
||||
:::
|
||||
|
||||
After installing dependencies, start a new terminal session.
|
||||
|
||||
1) Create a new project:
|
||||
0) Create a new project:
|
||||
|
||||
```bash
|
||||
mkdir sheetjs-loader
|
||||
@ -817,7 +698,7 @@ cd sheetjs-loader
|
||||
npm init -y
|
||||
```
|
||||
|
||||
2) Download the demo scripts:
|
||||
1) Download the demo scripts:
|
||||
|
||||
- [`loadofsheet.mjs`](pathname:///loadofsheet/loadofsheet.mjs)
|
||||
- [`query.mjs`](pathname:///loadofsheet/query.mjs)
|
||||
@ -844,30 +725,26 @@ curl.exe -LO https://docs.sheetjs.com/loadofsheet/loadofsheet.mjs
|
||||
|
||||
:::
|
||||
|
||||
3) Install the SheetJS NodeJS module:
|
||||
2) Install the SheetJS NodeJS module:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://sheet.lol/balls/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
4) Install dependencies:
|
||||
3) Install dependencies:
|
||||
|
||||
```bash
|
||||
npm i --save @langchain/core@0.3.44 langchain@0.3.21 @langchain/ollama@0.2.0 peggy@3.0.2
|
||||
npm i --save @langchain/community@0.2.18 @langchain/core@0.2.15 langchain@0.2.9 peggy@4.0.3 --force
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
In some test runs, there were error messages relating to dependency and peer
|
||||
dependency versions. The `--force` flag will suppress version mismatch errors:
|
||||
|
||||
```bash
|
||||
npm i --save @langchain/core@0.3.44 langchain@0.3.21 @langchain/ollama@0.2.0 peggy@3.0.2 --force
|
||||
```
|
||||
When this demo was last tested, there were error messages relating to dependency
|
||||
and peer dependency versions. The `--force` flag was required.
|
||||
|
||||
:::
|
||||
|
||||
5) Download the [cars dataset](pathname:///cd.xls):
|
||||
4) Download the [cars dataset](pathname:///cd.xls):
|
||||
|
||||
```bash
|
||||
curl -LO https://docs.sheetjs.com/cd.xls
|
||||
@ -889,34 +766,20 @@ curl.exe -LO https://docs.sheetjs.com/cd.xls
|
||||
|
||||
:::
|
||||
|
||||
6) Install the `llama3-chatqa:8b-v1.5-q8_0` model using Ollama:
|
||||
5) Install the `llama3-chatqa:8b-v1.5-q8_0` model using Ollama:
|
||||
|
||||
```bash
|
||||
ollama pull llama3-chatqa:8b-v1.5-q8_0
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>Additional steps for Intel GPUs</b> (click to show)</summary>
|
||||
:::note pass
|
||||
|
||||
A different embedding model must be used on Intel GPUs:
|
||||
If the command cannot be found, install Ollama[^10] and run the command in a new
|
||||
terminal window.
|
||||
|
||||
A) Install the `nomic-embed-text:latest` model through Ollama:
|
||||
:::
|
||||
|
||||
```bash
|
||||
ollama pull nomic-embed-text:latest
|
||||
```
|
||||
|
||||
B) Edit `query.mjs` to use the embedding model:
|
||||
|
||||
```js title="query.mjs (edit highlighted line)"
|
||||
const llm = new ChatOllama({ baseUrl: "http://127.0.0.1:11434", model });
|
||||
// highlight-next-line
|
||||
const embeddings = new OllamaEmbeddings({ baseUrl: "http://127.0.0.1:11434", model: "nomic-embed-text:latest"});
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
7) Run the demo script
|
||||
6) Run the demo script
|
||||
|
||||
```bash
|
||||
node query.mjs
|
||||
@ -975,4 +838,4 @@ charts, tables, and other features.
|
||||
[^7]: See ["Workbook Object"](/docs/csf/book)
|
||||
[^8]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
[^9]: See [the official ChatQA website](https://chatqa-project.github.io/) for the ChatQA paper and other model details.
|
||||
[^10]: Select ["Windows" `x86_64`](https://conda-forge.org/download/) in the Installation page.
|
||||
[^10]: See [the official Ollama website](https://ollama.com/download) for installation instructions.
|
||||
|
@ -38,11 +38,10 @@ tutorial first.
|
||||
|
||||
This browser demo was tested in the following environments:
|
||||
|
||||
| Browser | Date |
|
||||
|:-------------|:-----------|
|
||||
| Chromium 133 | 2025-03-30 |
|
||||
| Safari 18.3 | 2025-03-30 |
|
||||
| Konqueror 22 | 2025-04-23 |
|
||||
| Browser | Date |
|
||||
|:------------|:-----------|
|
||||
| Chrome 126 | 2024-06-21 |
|
||||
| Safari 17.4 | 2024-06-20 |
|
||||
|
||||
:::
|
||||
|
||||
@ -515,11 +514,10 @@ The van Reeken array mean can be implemented in one line of JavaScript code:
|
||||
for(var n = 1, mean = 0; n <= x.length; ++n) mean += (x[n-1] - mean)/n;
|
||||
```
|
||||
|
||||
<details open>
|
||||
<summary><b>Math details</b> (click to hide)</summary>
|
||||
<details>
|
||||
<summary><b>Math details</b> (click to show)</summary>
|
||||
|
||||
Let $M[x;m] = \frac{1}{m}\sum_{i=1}^{m}x_i$ be the mean of the first $m$ elements
|
||||
(where $m > 0$). The mean of the first $m+1$ elements follows a simple relation:
|
||||
Let $M[x;m] = \frac{1}{m}\sum_{i=1}^{m}x_m$ be the mean of the first $m$ elements. Then:
|
||||
|
||||
<table style={bs}>
|
||||
<tbody style={bs}>
|
||||
@ -539,14 +537,6 @@ $= \frac{1}{m+1}\sum_{i=1}^{m+1} x_i$
|
||||
<td style={bs}> </td>
|
||||
<td style={bs}>
|
||||
|
||||
$= \frac{1}{m+1}((\sum_{i=1}^{m} x_i) + x_{m+1})$
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr style={bs}>
|
||||
<td style={bs}> </td>
|
||||
<td style={bs}>
|
||||
|
||||
$= \frac{1}{m+1}\sum_{i=1}^{m} x_i + \frac{x_{m+1}}{m+1}$
|
||||
|
||||
</td>
|
||||
@ -584,61 +574,22 @@ $= M[x;m] + \frac{x_{m+1}}{m+1} - \frac{1}{m+1}M[x;m]$
|
||||
</td>
|
||||
</tr>
|
||||
<tr style={bs}>
|
||||
<td style={bs}>
|
||||
|
||||
$M[x;m+1]$
|
||||
|
||||
</td>
|
||||
<td style={bs}> </td>
|
||||
<td style={bs}>
|
||||
|
||||
$= M[x;m] + \frac{1}{m+1}(x_{m+1}-M[x;m])$
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
---
|
||||
|
||||
The mean of the first element is $M[x;1] = \frac{1}{1}\sum_{i=1}^{1}x_i = x_1$.
|
||||
By defining $M[x;0]=0$, the recurrence relation also holds for $m = 0$:
|
||||
|
||||
<table style={bs}>
|
||||
<tbody style={bs}>
|
||||
<tr style={bs}>
|
||||
<td style={bs}>
|
||||
|
||||
$x_1 = 0 + (x_1 - 0)$
|
||||
$new\_mean$
|
||||
|
||||
</td>
|
||||
<td style={bs}>
|
||||
|
||||
$= 0 + \frac{1}{1}(x_1 - 0)$
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr style={bs}>
|
||||
<td style={bs}>
|
||||
|
||||
$M[x;1]$
|
||||
|
||||
</td>
|
||||
<td style={bs}>
|
||||
|
||||
$= M[x;0] + \frac{1}{1}(x_{1} - M[x;0])$
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr style={bs}>
|
||||
<td style={bs}>
|
||||
|
||||
$M[x;m+1]$
|
||||
|
||||
</td>
|
||||
<td style={bs}>
|
||||
|
||||
$= M[x;m] + \frac{1}{m+1}(x_{m+1} - M[x;m])$
|
||||
$= old\_mean + (x_{m+1}-old\_mean) / (m+1)$
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
@ -646,52 +597,16 @@ $= M[x;m] + \frac{1}{m+1}(x_{m+1} - M[x;m])$
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
$m = 0, M[x;0] = 0$ will serve as the initial condition for the recurrence.
|
||||
|
||||
---
|
||||
|
||||
JavaScript is zero-indexed: $x_1$ is `x[0]` and generally $x_{m}$ is `x[m-1]`.
|
||||
|
||||
Adjusting for zero-based indexing, the following code calculates the recurrence:
|
||||
Switching to zero-based indexing, the relation matches the following expression:
|
||||
|
||||
```js
|
||||
/* data array */
|
||||
var x = [ /* ... data ... */ ];
|
||||
|
||||
/* initial condition M[x;0] = 0 */
|
||||
var mean = 0;
|
||||
|
||||
/* loop through each value in the array */
|
||||
for(var m = 0; m < x.length; ++m) {
|
||||
/* store the old mean M[x;m] */
|
||||
var mean_m = mean;
|
||||
/* get the next data point x_{m+1} */
|
||||
var x_m1 = x[m];
|
||||
/* calculate the new mean M[x;m+1] */
|
||||
var mean_m1 = mean_m + (x_m1 - mean_m) / (m + 1);
|
||||
/* update the mean */
|
||||
mean = mean_m1;
|
||||
}
|
||||
new_mean = old_mean + (x[m] - old_mean) / (m + 1);
|
||||
```
|
||||
|
||||
This can be succinctly implemented by assigning to `mean` directly:
|
||||
This update can be succinctly implemented in JavaScript:
|
||||
|
||||
```js
|
||||
for(var m = 0, mean = 0; m < x.length; ++m) {
|
||||
mean = mean + (x[m] - mean) / (m + 1);
|
||||
}
|
||||
```
|
||||
|
||||
This can be condensed further by using the addition assignment (`+=`) operator:
|
||||
|
||||
```js
|
||||
for(var m = 0, mean = 0; m < x.length; ++m) mean += (x[m] - mean) / (m + 1);
|
||||
```
|
||||
|
||||
Replacing `n = m+1` yields the final code expression:
|
||||
|
||||
```js
|
||||
for(var n = 1, mean = 0; n <= x.length; ++n) mean += (x[n-1] - mean) / n;
|
||||
mean += (x[m] - mean) / (m + 1);
|
||||
```
|
||||
|
||||
</details>
|
||||
@ -837,10 +752,6 @@ van Reeken[^13] reported success with the algorithm presented in this section.
|
||||
|
||||
Knuth[^14] erroneously attributed this implementation of the mean to Welford.
|
||||
|
||||
**The error in "Seminumerical Algorithms" (TAOCP Volume 2) was addressed!**[^15]
|
||||
|
||||
A SheetJS teammate has received a Knuth Reward Check for the contribution.
|
||||
|
||||
:::
|
||||
|
||||
[^1]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
@ -857,4 +768,3 @@ A SheetJS teammate has received a Knuth Reward Check for the contribution.
|
||||
[^12]: See "Comparison of Several Algorithms for Computation of Means, Standard Deviations and Correlation Coefficients" in CACM Vol 9 No 7 (1966 July).
|
||||
[^13]: See "Dealing with Neely's Algorithms" in CACM Vol 11 No 3 (1968 March).
|
||||
[^14]: See "The Art of Computer Programming: Seminumerical Algorithms" Third Edition page 232.
|
||||
[^15]: See the ["Errata for Volume 2 (after 2021)" in the TAOCP site](https://www-cs-faculty.stanford.edu/~knuth/taocp.html)
|
||||
|
@ -19,13 +19,7 @@ This demo covers details elided in the official DanfoJS documentation.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Platform | Version | Date |
|
||||
|:-------------|:--------|:-----------|
|
||||
| Chromium 131 | `1.1.2` | 2025-01-01 |
|
||||
| Safari 18.2 | `1.1.2` | 2025-01-01 |
|
||||
| Konqueror 22 | `1.1.2` | 2025-04-23 |
|
||||
This example was last tested on 2024 April 25 against DanfoJS 1.1.2.
|
||||
|
||||
:::
|
||||
|
||||
@ -213,23 +207,15 @@ The following example exports a sample dataframe to a XLSX spreadsheet.
|
||||
|
||||
```jsx live
|
||||
function DanfoToExcel() {
|
||||
if(typeof dfd === "undefined") return (<b>RELOAD THIS PAGE</b>);
|
||||
/* sample dataframe */
|
||||
const [df, setDF] = React.useState({});
|
||||
React.useEffect(() => {
|
||||
/* sample dataframe */
|
||||
setDF(new dfd.DataFrame([{Sheet:1,JS:2},{Sheet:3,JS:4}]));
|
||||
}, []);
|
||||
|
||||
if(!df.head) return ;
|
||||
return !df.head ? ( <b>RELOAD THIS PAGE</b>) : ( <>
|
||||
<button onClick={async() => {
|
||||
/* dfd.toExcel calls the SheetJS `writeFile` method */
|
||||
dfd.toExcel(df, {fileName: "SheetJSDanfoJS.xlsx", writingOptions: {
|
||||
compression: true
|
||||
}});
|
||||
}}>Click to Export</button>
|
||||
<pre>{"Data:\n"+df.head()}</pre>
|
||||
</> );
|
||||
const df = new dfd.DataFrame([{Sheet:1,JS:2},{Sheet:3,JS:4}]);
|
||||
return ( <><button onClick={async() => {
|
||||
/* dfd.toExcel calls the SheetJS `writeFile` method */
|
||||
dfd.toExcel(df, {fileName: "SheetJSDanfoJS.xlsx", writingOptions: {
|
||||
compression: true
|
||||
}});
|
||||
}}>Click to Export</button><pre>{"Data:\n"+df.head()}</pre></> );
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -5,11 +5,6 @@ pagination_prev: demos/index
|
||||
pagination_next: demos/frontend/index
|
||||
---
|
||||
|
||||
import current from '/version.js';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
<head>
|
||||
<script src="https://docs.sheetjs.com/tfjs/tf.min.js"></script>
|
||||
</head>
|
||||
@ -30,153 +25,27 @@ results back to spreadsheets.
|
||||
- ["JS Array Interchange"](#js-array-interchange) uses SheetJS to process sheets
|
||||
and generate rows of objects that can be post-processed.
|
||||
|
||||
:::info pass
|
||||
|
||||
Live code blocks in this page use the TF.js `4.14.0` standalone build.
|
||||
|
||||
For use in web frameworks, the `@tensorflow/tfjs` module should be used.
|
||||
|
||||
For use in NodeJS, the native bindings module is `@tensorflow/tfjs-node`.
|
||||
|
||||
:::
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
Each browser demo was tested in the following environments:
|
||||
|
||||
| Browser | TF.js | Date |
|
||||
|:-------------|:----------|:-----------|
|
||||
| Chromium 133 | `4.22.0` | 2025-04-21 |
|
||||
| Safari 18.3 | `4.22.0` | 2025-04-21 |
|
||||
| Konqueror 22 | `4.22.0` | 2025-04-23 |
|
||||
|
||||
The NodeJS demo was tested in the following environments:
|
||||
|
||||
| NodeJS | TF.js | Date |
|
||||
|:------------|:----------|:-----------|
|
||||
| `22.14.0` | `4.22.0` | 2025-04-21 |
|
||||
| `20.18.0` | `4.22.0` | 2025-04-21 |
|
||||
|
||||
The Kaioken demo was tested in the following environments:
|
||||
|
||||
| Kaioken | TF.js | Date |
|
||||
|:------------|:----------|:-----------|
|
||||
| `0.37.0` | `4.22.0` | 2025-04-21 |
|
||||
| Browser | TF.js version | Date |
|
||||
|:------------|:--------------|:-----------|
|
||||
| Chrome 122 | `4.14.0` | 2024-04-07 |
|
||||
| Safari 17.4 | `4.14.0` | 2024-03-23 |
|
||||
|
||||
:::
|
||||
|
||||
## Installation
|
||||
|
||||
#### Standalone Browser Scripts
|
||||
|
||||
Live code blocks in this page use the TF.js `4.22.0` standalone build.
|
||||
|
||||
Standalone scripts are available on various CDNs including UNPKG. The latest
|
||||
version can be loaded with the following `SCRIPT` tag.
|
||||
|
||||
The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone)
|
||||
can be loaded after the TF.js standalone script.
|
||||
|
||||
<CodeBlock language="html">{`\
|
||||
<!-- latest version of TF.js -->
|
||||
<script src="https://unpkg.com/@tensorflow/tfjs@latest/dist/tf.min.js"></script>
|
||||
<!-- use version ${current} -->
|
||||
<script lang="javascript" src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>`}
|
||||
</CodeBlock>
|
||||
|
||||
#### Frameworks and Bundlers
|
||||
|
||||
[The "Frameworks" section](/docs/getting-started/installation/frameworks) covers
|
||||
installation with Yarn and other package managers.
|
||||
|
||||
`@tensorflow/tfjs` and SheetJS modules should be installed using a package manager:
|
||||
|
||||
<Tabs groupId="pm">
|
||||
<TabItem value="npm" label="npm">
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs`}
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="pnpm" label="pnpm">
|
||||
<CodeBlock language="bash">{`\
|
||||
pnpm install --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs`}
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn" default>
|
||||
<CodeBlock language="bash">{`\
|
||||
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs`}
|
||||
</CodeBlock>
|
||||
|
||||
:::caution pass
|
||||
|
||||
Newer releases of Yarn may throw an error:
|
||||
|
||||
```
|
||||
Usage Error: It seems you are trying to add a package using a https:... url; we now require package names to be explicitly specified.
|
||||
Try running the command again with the package name prefixed: yarn add my-package@https:...
|
||||
```
|
||||
|
||||
The workaround is to prepend the URL with `xlsx@`:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
yarn add xlsx@https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
#### NodeJS
|
||||
|
||||
The [SheetJS NodeJS module](/docs/getting-started/installation/nodejs) can be
|
||||
imported in NodeJS scripts that use TF.js.
|
||||
|
||||
There are two options for NodeJS:
|
||||
|
||||
- the pure JavaScript bindings module is `@tensorflow/tfjs`
|
||||
- the native bindings module is `@tensorflow/tfjs-node`
|
||||
|
||||
:::danger pass
|
||||
|
||||
When this demo was last tested, there were issues with the native binding:
|
||||
|
||||
```
|
||||
Error: The specified module could not be found.
|
||||
\\?\C:\Users\SheetJS\node_modules\@tensorflow\tfjs-node\lib\napi-v8\tfjs_binding.node
|
||||
```
|
||||
|
||||
For general compatibility, the demos use the pure `@tensorflow/tfjs` binding.
|
||||
|
||||
:::
|
||||
|
||||
|
||||
<Tabs groupId="pm">
|
||||
<TabItem value="npm" label="npm">
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs @tensorflow/tfjs-node`}
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="pnpm" label="pnpm">
|
||||
<CodeBlock language="bash">{`\
|
||||
pnpm install --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs @tensorflow/tfjs-node`}
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn" default>
|
||||
<CodeBlock language="bash">{`\
|
||||
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs @tensorflow/tfjs-node`}
|
||||
</CodeBlock>
|
||||
|
||||
:::caution pass
|
||||
|
||||
Newer releases of Yarn may throw an error:
|
||||
|
||||
```
|
||||
Usage Error: It seems you are trying to add a package using a https:... url; we now require package names to be explicitly specified.
|
||||
Try running the command again with the package name prefixed: yarn add my-package@https:...
|
||||
```
|
||||
|
||||
The workaround is to prepend the URL with `xlsx@`:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
yarn add xlsx@https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs @tensorflow/tfjs-node`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## CSV Data Interchange
|
||||
|
||||
`tf.data.csv`[^1] generates a Dataset from CSV data. The function expects a URL.
|
||||
@ -257,8 +126,6 @@ The demo builds a model for predicting MPG from Horsepower data. It:
|
||||
- builds a model and trains with `fitDataset`[^8]
|
||||
- predicts MPG from a set of sample inputs and displays results in a table
|
||||
|
||||
#### Live Demo
|
||||
|
||||
<details>
|
||||
<summary><b>Live Demo</b> (click to show)</summary>
|
||||
|
||||
@ -319,8 +186,8 @@ function SheetJSToTFJSCSV() {
|
||||
hasHeader: true,
|
||||
configuredColumnsOnly: true,
|
||||
columnConfigs:{
|
||||
"Horsepower": { required: false, default: 0},
|
||||
"Miles_per_Gallon": { required: false, default: 0, isLabel: true }
|
||||
"Horsepower": {required: false, default: 0},
|
||||
"Miles_per_Gallon":{required: false, default: 0, isLabel:true}
|
||||
}
|
||||
});
|
||||
|
||||
@ -367,121 +234,6 @@ function SheetJSToTFJSCSV() {
|
||||
|
||||
</details>
|
||||
|
||||
#### NodeJS Demo
|
||||
|
||||
<details>
|
||||
<summary><b>Demo Steps</b> (click to show)</summary>
|
||||
|
||||
0) Create a new project:
|
||||
|
||||
```bash
|
||||
mkdir sheetjs-tfjs-csv
|
||||
cd sheetjs-tfjs-csv
|
||||
npm init -y
|
||||
```
|
||||
|
||||
1) Download [`SheetJSTF.js`](pathname:///tfjs/SheetJSTF.js):
|
||||
|
||||
```bash
|
||||
curl -LO https://docs.sheetjs.com/tfjs/SheetJSTF.js
|
||||
```
|
||||
|
||||
2) Install SheetJS and TF.js dependencies:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs @tensorflow/tfjs-node`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Run the script:
|
||||
|
||||
```bash
|
||||
node SheetJSTF.js
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
#### Kaioken Demo
|
||||
|
||||
:::tip pass
|
||||
|
||||
[Kaioken](/docs/demos/frontend/kaioken) is a popular front-end framework that
|
||||
uses patterns that will be familiar to ReactJS developers.
|
||||
|
||||
The SheetJS team strongly recommends using Kaioken in projects using TF.js.
|
||||
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary><b>Demo Steps</b> (click to show)</summary>
|
||||
|
||||
1) Create a new site.
|
||||
|
||||
```bash
|
||||
npm create vite sheetjs-tfjs-kaioken -- --template vanilla-ts
|
||||
cd sheetjs-tfjs-kaioken
|
||||
npm add --save kaioken
|
||||
npm add --save vite-plugin-kaioken -D
|
||||
```
|
||||
|
||||
2) Create a new file `vite.config.ts` with the following content:
|
||||
|
||||
```ts title="vite.config.ts (create new file)"
|
||||
import { defineConfig } from "vite"
|
||||
import kaioken from "vite-plugin-kaioken"
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [kaioken()],
|
||||
})
|
||||
```
|
||||
|
||||
3) Edit `tsconfig.json` and add `"jsx": "preserve"` within `compilerOptions`:
|
||||
|
||||
```js title="tsconfig.json (add highlighted line)"
|
||||
{
|
||||
"compilerOptions": {
|
||||
// highlight-next-line
|
||||
"jsx": "preserve",
|
||||
```
|
||||
|
||||
4) Replace `src/main.ts` with the following codeblock:
|
||||
|
||||
```js title="src/main.ts"
|
||||
import { mount } from "kaioken";
|
||||
import App from "./SheetJSTF";
|
||||
|
||||
const root = document.getElementById("app");
|
||||
mount(App, root!);
|
||||
```
|
||||
|
||||
5) Download [`SheetJSTF.tsx`](pathname:///tfjs/SheetJSTF.tsx) to the `src` directory:
|
||||
|
||||
```bash
|
||||
curl -L -o src/SheetJSTF.tsx https://docs.sheetjs.com/tfjs/SheetJSTF.tsx
|
||||
```
|
||||
|
||||
6) Install SheetJS and TF.js dependencies:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs`}
|
||||
</CodeBlock>
|
||||
|
||||
7) Start the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
The process will display a URL:
|
||||
|
||||
```
|
||||
➜ Local: http://localhost:5173/
|
||||
```
|
||||
|
||||
Open the displayed URL (`http://localhost:5173/` in this example) with a web
|
||||
browser. Click the "Click to Run" button to see the results.
|
||||
|
||||
</details>
|
||||
|
||||
## JS Array Interchange
|
||||
|
||||
[The official Linear Regression tutorial](https://www.tensorflow.org/js/tutorials/training/linear_regression)
|
||||
@ -520,19 +272,17 @@ Differences from the official example are highlighted below:
|
||||
*/
|
||||
async function getData() {
|
||||
// highlight-start
|
||||
/* fetch file and pull data into an ArrayBuffer */
|
||||
/* fetch file */
|
||||
const carsDataResponse = await fetch('https://docs.sheetjs.com/cd.xls');
|
||||
/* get file data (ArrayBuffer) */
|
||||
const carsDataAB = await carsDataResponse.arrayBuffer();
|
||||
|
||||
/* parse */
|
||||
const carsDataWB = XLSX.read(carsDataAB);
|
||||
|
||||
/* get first worksheet */
|
||||
const carsDataWS = carsDataWB.Sheets[carsDataWB.SheetNames[0]];
|
||||
/* generate array of JS objects */
|
||||
const carsData = XLSX.utils.sheet_to_json(carsDataWS);
|
||||
// highlight-end
|
||||
|
||||
const cleaned = carsData.map(car => ({
|
||||
mpg: car.Miles_per_Gallon,
|
||||
horsepower: car.Horsepower,
|
||||
@ -556,7 +306,7 @@ The SheetJS `sheet_to_json` method[^10] will translate worksheet objects into an
|
||||
array of row objects:
|
||||
|
||||
```js
|
||||
const aoo = [
|
||||
var aoo = [
|
||||
{"sepal length": 5.1, "sepal width": 3.5, ...},
|
||||
{"sepal length": 4.9, "sepal width": 3, ...},
|
||||
...
|
||||
@ -566,18 +316,18 @@ const aoo = [
|
||||
TF.js and other libraries tend to operate on individual columns, equivalent to:
|
||||
|
||||
```js
|
||||
const sepal_lengths = [5.1, 4.9, ...];
|
||||
const sepal_widths = [3.5, 3, ...];
|
||||
var sepal_lengths = [5.1, 4.9, ...];
|
||||
var sepal_widths = [3.5, 3, ...];
|
||||
```
|
||||
|
||||
When a `tensor2d` can be exported, it will look different from the spreadsheet:
|
||||
|
||||
```js
|
||||
const data_set_2d = [
|
||||
[5.1, 4.9, /*...*/],
|
||||
[3.5, 3, /*...*/],
|
||||
// ...
|
||||
];
|
||||
var data_set_2d = [
|
||||
[5.1, 4.9, ...],
|
||||
[3.5, 3, ...],
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
This is the transpose of how people use spreadsheets!
|
||||
@ -586,85 +336,49 @@ This is the transpose of how people use spreadsheets!
|
||||
|
||||
The `aoa_to_sheet` method[^11] can generate a worksheet from an array of arrays.
|
||||
ML libraries typically provide APIs to pull an array of arrays, but it will be
|
||||
transposed. The following function transposes arrays of normal and typed arrays:
|
||||
|
||||
```js title="Transpose array of arrays"
|
||||
/* `data` is an array of (typed or normal) arrays */
|
||||
function transpose_array_of_arrays(data) {
|
||||
const aoa = [];
|
||||
for(let i = 0; i < data.length; ++i) {
|
||||
for(let j = 0; j < data[i].length; ++j) {
|
||||
if(!aoa[j]) aoa[j] = [];
|
||||
aoa[j][i] = data[i][j];
|
||||
}
|
||||
}
|
||||
return aoa;
|
||||
}
|
||||
```
|
||||
|
||||
It is recommended to create a new worksheet from the header row and add the
|
||||
transposed data using the `sheet_add_aoa` method. The option `origin: -1`[^12]
|
||||
ensures that the data is written after the headers:
|
||||
transposed. To export multiple data sets, the data should be transposed:
|
||||
|
||||
```js
|
||||
const headers = [ "sepal length", "sepal width"];
|
||||
const data_set_2d = [
|
||||
[5.1, 4.9, /*...*/],
|
||||
[3.5, 3, /*...*/],
|
||||
// ...
|
||||
];
|
||||
|
||||
// highlight-start
|
||||
/* transpose data */
|
||||
const transposed_data = transpose_array_of_arrays(data_set_2d);
|
||||
// highlight-end
|
||||
|
||||
/* create worksheet from headers */
|
||||
const ws = XLSX.utils.aoa_to_sheet([ headers ])
|
||||
|
||||
/* add the transposed data starting on row 2 */
|
||||
XLSX.utils.sheet_add_aoa(ws, transposed_data, { origin: 1 });
|
||||
/* assuming data is an array of typed arrays */
|
||||
var aoa = [];
|
||||
for(var i = 0; i < data.length; ++i) {
|
||||
for(var j = 0; j < data[i].length; ++j) {
|
||||
if(!aoa[j]) aoa[j] = [];
|
||||
aoa[j][i] = data[i][j];
|
||||
}
|
||||
}
|
||||
/* aoa can be directly converted to a worksheet object */
|
||||
var ws = XLSX.utils.aoa_to_sheet(aoa);
|
||||
```
|
||||
|
||||
### Importing Data from a Spreadsheet
|
||||
|
||||
`sheet_to_json` with the option `header: 1`[^13] will generate a row-major array
|
||||
`sheet_to_json` with the option `header:1`[^12] will generate a row-major array
|
||||
of arrays that can be transposed. However, it is more efficient to walk the
|
||||
sheet manually. The following function accepts a number of header rows to skip:
|
||||
sheet manually:
|
||||
|
||||
```js title="Worksheet to transposed array of typed arrays"
|
||||
function sheet_to_array_of_f32(ws, header_row_count) {
|
||||
const out = [];
|
||||
|
||||
/* find worksheet range */
|
||||
const range = XLSX.utils.decode_range(ws['!ref']);
|
||||
|
||||
/* skip specified number of headers */
|
||||
range.s.r += (header_row_count | 0);
|
||||
|
||||
/* walk the columns */
|
||||
for(let C = range.s.c; C <= range.e.c; ++C) {
|
||||
/* create the typed array */
|
||||
const ta = new Float32Array(range.e.r - range.s.r + 1);
|
||||
|
||||
/* walk the rows */
|
||||
for(let R = range.s.r; R <= range.e.r; ++R) {
|
||||
/* find the cell, skip it if the cell isn't numeric or boolean */
|
||||
const cell = ws["!data"] ? (ws["!data"][R]||[])[C] : ws[XLSX.utils.encode_cell({r:R, c:C})];
|
||||
if(!cell || cell.t != 'n' && cell.t != 'b') continue;
|
||||
|
||||
/* assign to the typed array */
|
||||
ta[R - range.s.r] = cell.v;
|
||||
}
|
||||
|
||||
/* add typed array to output */
|
||||
out.push(ta);
|
||||
```js
|
||||
/* find worksheet range */
|
||||
var range = XLSX.utils.decode_range(ws['!ref']);
|
||||
var out = []
|
||||
/* walk the columns */
|
||||
for(var C = range.s.c; C <= range.e.c; ++C) {
|
||||
/* create the typed array */
|
||||
var ta = new Float32Array(range.e.r - range.s.r + 1);
|
||||
/* walk the rows */
|
||||
for(var R = range.s.r; R <= range.e.r; ++R) {
|
||||
/* find the cell, skip it if the cell isn't numeric or boolean */
|
||||
var cell = ws["!data"] ? (ws["!data"][R]||[])[C] : ws[XLSX.utils.encode_cell({r:R, c:C})];
|
||||
if(!cell || cell.t != 'n' && cell.t != 'b') continue;
|
||||
/* assign to the typed array */
|
||||
ta[R - range.s.r] = cell.v;
|
||||
}
|
||||
|
||||
return out;
|
||||
out.push(ta);
|
||||
}
|
||||
```
|
||||
|
||||
If the data set has a header row, the loop can be adjusted to skip those rows.
|
||||
|
||||
### TF.js Tensors
|
||||
|
||||
A single `Array#map` can pull individual named fields from the result, which
|
||||
@ -679,38 +393,38 @@ const tensor = tf.tensor1d(lengths);
|
||||
`tf.Tensor` objects can be directly transposed using `transpose`:
|
||||
|
||||
```js
|
||||
const aoo = XLSX.utils.sheet_to_json(worksheet);
|
||||
var aoo = XLSX.utils.sheet_to_json(worksheet);
|
||||
// "x" and "y" are the fields we want to pull from the data
|
||||
con st data = aoo.map(row => ([row["x"], row["y"]]));
|
||||
var data = aoo.map(row => ([row["x"], row["y"]]));
|
||||
|
||||
// create a tensor representing two column datasets
|
||||
const tensor = tf.tensor2d(data).transpose();
|
||||
var tensor = tf.tensor2d(data).transpose();
|
||||
|
||||
// individual columns can be accessed
|
||||
const col1 = tensor.slice([0,0], [1,tensor.shape[1]]).flatten();
|
||||
const col2 = tensor.slice([1,0], [1,tensor.shape[1]]).flatten();
|
||||
var col1 = tensor.slice([0,0], [1,tensor.shape[1]]).flatten();
|
||||
var col2 = tensor.slice([1,0], [1,tensor.shape[1]]).flatten();
|
||||
```
|
||||
|
||||
For exporting, `stack` can be used to collapse the columns into a linear array:
|
||||
|
||||
```js
|
||||
/* pull data into a Float32Array */
|
||||
const result = tf.stack([col1, col2]).transpose();
|
||||
const shape = tensor.shape;
|
||||
const f32 = tensor.dataSync();
|
||||
var result = tf.stack([col1, col2]).transpose();
|
||||
var shape = tensor.shape;
|
||||
var f32 = tensor.dataSync();
|
||||
|
||||
/* construct an array of arrays of the data in spreadsheet order */
|
||||
const aoa = [];
|
||||
for(let j = 0; j < shape[0]; ++j) {
|
||||
var aoa = [];
|
||||
for(var j = 0; j < shape[0]; ++j) {
|
||||
aoa[j] = [];
|
||||
for(let i = 0; i < shape[1]; ++i) aoa[j][i] = f32[j * shape[1] + i];
|
||||
for(var i = 0; i < shape[1]; ++i) aoa[j][i] = f32[j * shape[1] + i];
|
||||
}
|
||||
|
||||
/* add headers to the top */
|
||||
aoa.unshift(["x", "y"]);
|
||||
|
||||
/* generate worksheet */
|
||||
const worksheet = XLSX.utils.aoa_to_sheet(aoa);
|
||||
var worksheet = XLSX.utils.aoa_to_sheet(aoa);
|
||||
```
|
||||
|
||||
[^1]: See [`tf.data.csv`](https://js.tensorflow.org/api/latest/#data.csv) in the TensorFlow.js documentation
|
||||
@ -724,5 +438,4 @@ const worksheet = XLSX.utils.aoa_to_sheet(aoa);
|
||||
[^9]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
[^10]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
[^11]: See [`aoa_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-arrays-input)
|
||||
[^12]: See [the `origin` option of `sheet_add_aoa` in "Utilities"](/docs/api/utilities/array#array-of-arrays-input)
|
||||
[^13]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
[^12]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
|
@ -40,12 +40,12 @@ This demo was tested in the following deployments:
|
||||
|
||||
| Architecture | JS Engine | Pandas | Python | Date |
|
||||
|:-------------|:----------------|:-------|:-------|:-----------|
|
||||
| `darwin-x64` | Duktape `2.7.0` | 2.2.3 | 3.13.1 | 2025-03-31 |
|
||||
| `darwin-arm` | Duktape `2.7.0` | 2.2.3 | 3.13.2 | 2025-03-30 |
|
||||
| `win11-x64` | Duktape `2.7.0` | 2.2.3 | 3.11.8 | 2024-12-21 |
|
||||
| `win11-arm` | Duktape `2.7.0` | 2.2.3 | 3.13.2 | 2025-02-23 |
|
||||
| `linux-x64` | Duktape `2.7.0` | 1.5.3 | 3.11.7 | 2025-01-01 |
|
||||
| `linux-arm` | Duktape `2.7.0` | 1.5.3 | 3.11.2 | 2025-02-16 |
|
||||
| `darwin-x64` | Duktape `2.7.0` | 2.2.1 | 3.12.2 | 2024-03-15 |
|
||||
| `darwin-arm` | Duktape `2.7.0` | 2.2.2 | 3.12.3 | 2024-06-30 |
|
||||
| `win10-x64` | Duktape `2.7.0` | 2.2.1 | 3.12.2 | 2024-03-25 |
|
||||
| `win11-arm` | Duktape `2.7.0` | 2.2.2 | 3.11.5 | 2024-06-20 |
|
||||
| `linux-x64` | Duktape `2.7.0` | 1.5.3 | 3.11.3 | 2024-03-21 |
|
||||
| `linux-arm` | Duktape `2.7.0` | 1.5.3 | 3.11.2 | 2024-06-20 |
|
||||
|
||||
:::
|
||||
|
||||
@ -231,16 +231,8 @@ When `pip` is not installed, the command will fail:
|
||||
/usr/bin/python3: No module named pip
|
||||
```
|
||||
|
||||
`pip` must be installed.
|
||||
|
||||
In Debian Linux, `pip` can be installed through the package manager:
|
||||
|
||||
```bash
|
||||
sudo apt-get install pip
|
||||
```
|
||||
|
||||
On Arch Linux-based platforms including the Steam Deck, `python-pip` can be
|
||||
installed through the package manager:
|
||||
`pip` must be installed. On Arch Linux-based platforms including the Steam Deck,
|
||||
`python-pip` can be installed through the package manager:
|
||||
|
||||
```bash
|
||||
sudo pacman -Syu python-pip
|
||||
@ -303,7 +295,7 @@ cd ..
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="win11-x64" label="Windows">
|
||||
<TabItem value="win10-x64" label="Windows">
|
||||
|
||||
- Download and extract the source tarball. Commands must be run in WSL `bash`:
|
||||
|
||||
@ -322,7 +314,7 @@ cd duktape-2.7.0
|
||||
|
||||
- Edit `src\duk_config.h` and add the highlighted lines to the end of the file:
|
||||
|
||||
```c title="src\duk_config.h (add highlighted lines to end of file)"
|
||||
```c title="src\duk_config.h (add highlighted lines)"
|
||||
#endif /* DUK_CONFIG_H_INCLUDED */
|
||||
|
||||
// highlight-start
|
||||
@ -370,7 +362,7 @@ cp duktape-*/libduktape.* .
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="win11-x64" label="Windows">
|
||||
<TabItem value="win10-x64" label="Windows">
|
||||
|
||||
```cmd
|
||||
copy duktape-2.7.0\duktape.dll .
|
||||
@ -434,7 +426,7 @@ lib = "./libduktape.so.207.20700"
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="win11-x64" label="Windows">
|
||||
<TabItem value="win10-x64" label="Windows">
|
||||
|
||||
The name of the library is `duktape.dll`:
|
||||
|
||||
@ -527,12 +519,12 @@ This demo was tested in the following deployments:
|
||||
|
||||
| Architecture | JS Engine | Polars | Python | Date |
|
||||
|:-------------|:----------------|:--------|:-------|:-----------|
|
||||
| `darwin-x64` | Duktape `2.7.0` | 1.26.0 | 3.13.1 | 2025-03-31 |
|
||||
| `darwin-arm` | Duktape `2.7.0` | 1.26.0 | 3.13.2 | 2025-03-30 |
|
||||
| `win11-x64` | Duktape `2.7.0` | 1.17.1 | 3.11.8 | 2024-12-21 |
|
||||
| `win11-arm` | Duktape `2.7.0` | 1.23.0 | 3.13.2 | 2025-02-23 |
|
||||
| `linux-x64` | Duktape `2.7.0` | 1.18.0 | 3.11.7 | 2025-01-01 |
|
||||
| `linux-arm` | Duktape `2.7.0` | 1.22.0 | 3.11.2 | 2025-02-16 |
|
||||
| `darwin-x64` | Duktape `2.7.0` | 0.20.15 | 3.12.2 | 2024-03-15 |
|
||||
| `darwin-arm` | Duktape `2.7.0` | 0.20.31 | 3.12.3 | 2024-06-30 |
|
||||
| `win10-x64` | Duktape `2.7.0` | 0.20.16 | 3.12.2 | 2024-03-25 |
|
||||
| `win10-arm` | Duktape `2.7.0` | 0.20.31 | 3.11.5 | 2024-06-20 |
|
||||
| `linux-x64` | Duktape `2.7.0` | 0.20.16 | 3.11.3 | 2024-03-21 |
|
||||
| `linux-arm` | Duktape `2.7.0` | 0.20.31 | 3.11.2 | 2024-06-20 |
|
||||
|
||||
:::
|
||||
|
||||
@ -553,25 +545,11 @@ duk = CDLL(lib)
|
||||
- Within the `export_df_to_wb` function, change the `df.to_json` line:
|
||||
|
||||
```python title="sheetjs.py (edit highlighted line)"
|
||||
def export_df_to_wb(ctx, df, path, sheet_name="Sheet1", book_type=None):
|
||||
# highlight-next-line
|
||||
json = df.write_json()
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
**Polars made a breaking change in the `1.0` release.**
|
||||
|
||||
For `0.20` and earlier, the `row_oriented` option must be passed:
|
||||
|
||||
```python title="sheetjs.py (changes for Polars 0.20)"
|
||||
def export_df_to_wb(ctx, df, path, sheet_name="Sheet1", book_type=None):
|
||||
# highlight-next-line
|
||||
json = df.write_json(row_oriented=True)
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
2) Edit `SheetJSPandas.py`.
|
||||
|
||||
- In the `process` function, change `df.info()` to `df`:
|
||||
@ -616,15 +594,13 @@ through the Visual Studio installer.
|
||||
|
||||
:::caution pass
|
||||
|
||||
In some local tests, the install failed with the following error:
|
||||
On Arch Linux-based platforms including the Steam Deck, the install may fail:
|
||||
|
||||
```
|
||||
error: externally-managed-environment
|
||||
```
|
||||
|
||||
It is recommended to use a virtual environment.
|
||||
|
||||
---
|
||||
It is recommended to use a virtual environment for Polars.
|
||||
|
||||
`venv` must be installed through the system package manager:
|
||||
|
||||
@ -642,8 +618,6 @@ sudo apt-get install python3.11-venv
|
||||
brew install pyenv-virtualenv
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
After installing `venv`, the following commands set up the virtual environment:
|
||||
|
||||
```bash
|
||||
|
@ -63,7 +63,7 @@ loaded into the site. This sheet will have known columns.
|
||||
|
||||
The example [presidents sheet](https://docs.sheetjs.com/pres.xlsx) has one
|
||||
header row with "Name" and "Index" columns. The natural JS representation is an
|
||||
object for each row, where the keys are specified in the first row:
|
||||
object for each row, using the values in the first rows as keys:
|
||||
|
||||
<table>
|
||||
<thead><tr><th>Spreadsheet</th><th>State</th></tr></thead>
|
||||
@ -448,9 +448,9 @@ export default function SheetJSKaiokenAoO() {
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Kaioken | ViteJS | Date |
|
||||
|:----------|:--------|:-----------|
|
||||
| `0.35.10` | `6.1.0` | 2025-02-11 |
|
||||
| Kaioken | ViteJS | Date |
|
||||
|:---------|:---------|:-----------|
|
||||
| `0.17.0` | `5.2.11` | 2024-05-21 |
|
||||
|
||||
:::
|
||||
|
||||
@ -554,7 +554,7 @@ import { read, utils, writeFileXLSX } from 'xlsx';
|
||||
|
||||
export default function SheetJSKaiokenHTML() {
|
||||
/* the ref is used in export */
|
||||
const tbl = useRef<HTMLDivElement>(null);
|
||||
const tbl = useRef<Element>(null);
|
||||
|
||||
/* Fetch and update the state once */
|
||||
useEffect(() => { (async() => {
|
||||
@ -595,9 +595,9 @@ export default function SheetJSKaiokenHTML() {
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Kaioken | ViteJS | Date |
|
||||
|:----------|:--------|:-----------|
|
||||
| `0.35.10` | `6.1.0` | 2025-02-11 |
|
||||
| Kaioken | ViteJS | Date |
|
||||
|:---------|:---------|:-----------|
|
||||
| `0.17.0` | `5.2.11` | 2024-05-21 |
|
||||
|
||||
:::
|
||||
|
||||
|
@ -59,7 +59,7 @@ loaded into the site. This sheet will have known columns.
|
||||
|
||||
The example [presidents sheet](https://docs.sheetjs.com/pres.xlsx) has one
|
||||
header row with "Name" and "Index" columns. The natural JS representation is an
|
||||
object for each row, where the keys are specified in the first row:
|
||||
object for each row, using the values in the first rows as keys:
|
||||
|
||||
<table>
|
||||
<thead><tr><th>Spreadsheet</th><th>State</th></tr></thead>
|
||||
@ -274,16 +274,15 @@ const exportFile = useCallback(() => {
|
||||
#### Complete Component
|
||||
|
||||
This complete component example fetches a test file and displays the contents in
|
||||
an HTML table. When the export button is clicked, a callback will export a file:
|
||||
a HTML table. When the export button is clicked, a callback will export a file:
|
||||
|
||||
```jsx title="src/SheetJSReactAoO.js"
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
|
||||
import { read, utils, writeFileXLSX } from 'xlsx';
|
||||
|
||||
export default function SheetJSReactAoO() {
|
||||
/* the component state is an array of presidents */
|
||||
const [pres, setPres] = useState([] /* as any[] */);
|
||||
const [pres, setPres] = useState([]);
|
||||
|
||||
/* Fetch and update the state once */
|
||||
useEffect(() => { (async() => {
|
||||
@ -307,16 +306,16 @@ export default function SheetJSReactAoO() {
|
||||
|
||||
return (<table><thead><tr><th>Name</th><th>Index</th></tr></thead><tbody>
|
||||
{ /* generate row for each president */
|
||||
// highlight-start
|
||||
pres.map((pres, index) => (<tr key={index}>
|
||||
// highlight-start
|
||||
pres.map(pres => (<tr>
|
||||
<td>{pres.Name}</td>
|
||||
<td>{pres.Index}</td>
|
||||
</tr>))
|
||||
// highlight-end
|
||||
// highlight-end
|
||||
}
|
||||
</tbody><tfoot><tr><td colSpan={2}>
|
||||
</tbody><tfoot><td colSpan={2}>
|
||||
<button onClick={exportFile}>Export XLSX</button>
|
||||
</td></tr></tfoot></table>);
|
||||
</td></tfoot></table>);
|
||||
}
|
||||
```
|
||||
|
||||
@ -332,7 +331,7 @@ This demo was tested in the following environments:
|
||||
|
||||
| ReactJS | ViteJS | Date |
|
||||
|:---------|:--------|:-----------|
|
||||
| `18.3.1` | `6.0.1` | 2024-12-12 |
|
||||
| `18.2.0` | `5.1.6` | 2024-03-13 |
|
||||
|
||||
:::
|
||||
|
||||
@ -384,19 +383,10 @@ This demo was tested in the following environments:
|
||||
|
||||
| ReactJS | CRA | Date |
|
||||
|:---------|:--------|:-----------|
|
||||
| `18.2.0` | `5.0.1` | 2024-12-12 |
|
||||
| `18.2.0` | `5.0.1` | 2024-03-13 |
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
CRA has known compatibility issues with React 19[^5]. CRA no longer receives
|
||||
updates and the ReactJS docs no longer recommend using CRA. For new projects, it
|
||||
is strongly recommended to use ViteJS with the `react` or `react-ts` templates.
|
||||
|
||||
:::
|
||||
|
||||
|
||||
1) Create a new site:
|
||||
|
||||
```bash
|
||||
@ -408,7 +398,6 @@ npx -y create-react-app@5.0.1 --scripts-version=5.0.1 sheetjs-react
|
||||
<CodeBlock language="bash">{`\
|
||||
cd sheetjs-react
|
||||
npm i
|
||||
npm i react@18.2.0 react-dom@18.2.0 web-vitals --save --save-exact
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npm start`}
|
||||
</CodeBlock>
|
||||
@ -446,7 +435,7 @@ This demo was tested in the following environments:
|
||||
|
||||
| ReactJS | NextJS | Date |
|
||||
|:---------|:---------|:-----------|
|
||||
| `19.0.0` | `15.1.0` | 2024-12-13 |
|
||||
| `18.2.0` | `14.1.3` | 2024-03-13 |
|
||||
|
||||
:::
|
||||
|
||||
@ -491,34 +480,26 @@ npx next telemetry disable
|
||||
1) Create a new site:
|
||||
|
||||
```bash
|
||||
npx create-next-app@latest sheetjs-nextjs --ts --no-eslint --no-tailwind --no-src-dir --no-app --import-alias "@/*" --no-turbopack
|
||||
npx create-next-app@latest sheetjs-react --ts --no-eslint --no-tailwind --no-src-dir --no-app --import-alias "@/*"
|
||||
```
|
||||
|
||||
2) Install the SheetJS dependency and start the dev server:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
cd sheetjs-nextjs
|
||||
cd sheetjs-react
|
||||
npm i
|
||||
npx next telemetry disable
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npm run dev`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Open a web browser and access the displayed URL (`http://localhost:3000`)
|
||||
3) Open a web browser and access the displayed URL (`http://localhost:5173`)
|
||||
|
||||
4) Replace `pages/index.tsx` with the `src/SheetJSReactAoO.js` example.
|
||||
4) Replace `src/App.jsx` with the `src/SheetJSReactAoO.js` example.
|
||||
|
||||
After replacing the code:
|
||||
After replacing the code, add the following to the top of `src/App.jsx`:
|
||||
|
||||
- uncomment the type hint in the `useState` function call:
|
||||
|
||||
```js title="pages/index.tsx (uncomment the inline comment)"
|
||||
const [pres, setPres] = useState([] as any[]);
|
||||
```
|
||||
|
||||
- add the following to the top of `pages/index.tsx`:
|
||||
|
||||
```js title="pages/index.tsx (add to top)"
|
||||
```js title="src/App.jsx (add to top)"
|
||||
"use client";
|
||||
```
|
||||
|
||||
@ -528,7 +509,7 @@ The goal is to run SheetJS code in the browser. NextJS will attempt to render
|
||||
pages on the server by default. `"use client";` instructs NextJS to treat the
|
||||
exported component as a "Client Component" that will be rendered in the browser.
|
||||
|
||||
If the directive is not added, NextJS will report errors related to hydration:
|
||||
If the pragma is not added, NextJS will report errors related to hydration:
|
||||
|
||||
```
|
||||
Error: Hydration failed because the initial UI does not match what was rendered on the server.
|
||||
@ -549,75 +530,9 @@ and the page will attempt to download `SheetJSReactAoO.xlsx`.
|
||||
npm run build
|
||||
```
|
||||
|
||||
6) Start a local web server:
|
||||
|
||||
```bash
|
||||
npm run start
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:3000`) with a web browser
|
||||
and test the page.
|
||||
|
||||
</TabItem>
|
||||
<TabItem name="preact" value="Preact">
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Preact | ViteJS | Date |
|
||||
|:----------|:----------|:-----------|
|
||||
| `10.22.1` | `5.3.3` | 2024-12-17 |
|
||||
|
||||
:::
|
||||
|
||||
1) Create a new site:
|
||||
|
||||
```bash
|
||||
npm init preact sheetjs-preact
|
||||
```
|
||||
|
||||
This will initiate the project creation process. **Follow the on-screen prompts and
|
||||
press Enter to accept the default options.**
|
||||
|
||||
2) Install the SheetJS dependency and start the dev server:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
cd sheetjs-preact
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npm run dev`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Open a web browser and access the displayed URL (`http://localhost:5173`)
|
||||
|
||||
4) Copy `src/SheetJSReactAoO.js` demo code to `src/index.jsx` and update the imports:
|
||||
|
||||
```jsx title="src/index.jsx (replace React import with Preact)"
|
||||
// Remove React import and replace with:
|
||||
import { render } from 'preact';
|
||||
import { useCallback, useEffect, useState } from 'preact/hooks';
|
||||
```
|
||||
|
||||
5) Add the following to the bottom of the file:
|
||||
|
||||
```jsx title="src/index.jsx (append to file)"
|
||||
export function App() { return ( <SheetJSReactAoO/> );}
|
||||
|
||||
render(<App />, document.getElementById('app'));
|
||||
```
|
||||
|
||||
The page will refresh and show a table with an Export button. Click the button
|
||||
and the page will attempt to download `SheetJSReactAoO.xlsx`.
|
||||
|
||||
6) Build the site:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
The generated site will be placed in the `dist` folder.
|
||||
|
||||
7) Start a local web server:
|
||||
6) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx http-server dist
|
||||
@ -626,7 +541,7 @@ npx http-server dist
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
and test the page.
|
||||
|
||||
</TabItem>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
When the page loads, the app will fetch https://docs.sheetjs.com/pres.xlsx and
|
||||
@ -639,11 +554,11 @@ will generate a workbook that can be opened in a spreadsheet editor.
|
||||
|
||||
The main disadvantage of the Array of Objects approach is the specific nature
|
||||
of the columns. For more general use, passing around an Array of Arrays works.
|
||||
However, this does not handle merge cells[^6] well!
|
||||
However, this does not handle merge cells[^5] well!
|
||||
|
||||
The [`sheet_to_html`](/docs/api/utilities/html#html-table-output) function
|
||||
generates HTML that is aware of merges and other worksheet features. ReactJS
|
||||
`dangerouslySetInnerHTML`[^7] prop allows code to set the `innerHTML` attribute,
|
||||
`dangerouslySetInnerHTML`[^6] prop allows code to set the `innerHTML` attribute,
|
||||
effectively inserting the code into the page.
|
||||
|
||||
In this example, the component attaches a `ref` to the `DIV` container. During
|
||||
@ -652,7 +567,6 @@ generate a workbook object.
|
||||
|
||||
```jsx title="src/SheetJSReactHTML.js"
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
|
||||
import { read, utils, writeFileXLSX } from 'xlsx';
|
||||
|
||||
export default function SheetJSReactHTML() {
|
||||
@ -701,7 +615,7 @@ This demo was tested in the following environments:
|
||||
|
||||
| ReactJS | ViteJS | Date |
|
||||
|:---------|:--------|:-----------|
|
||||
| `18.3.1` | `6.0.1` | 2024-12-13 |
|
||||
| `18.2.0` | `5.1.6` | 2024-03-13 |
|
||||
|
||||
:::
|
||||
|
||||
@ -753,15 +667,7 @@ This demo was tested in the following environments:
|
||||
|
||||
| ReactJS | CRA | Date |
|
||||
|:---------|:--------|:-----------|
|
||||
| `18.2.0` | `5.0.1` | 2024-12-31 |
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
CRA has known compatibility issues with React 19[^5]. CRA no longer receives
|
||||
updates and the ReactJS docs no longer recommend using CRA. For new projects, it
|
||||
is strongly recommended to use ViteJS with the `react` or `react-ts` templates.
|
||||
| `18.2.0` | `5.0.1` | 2024-03-13 |
|
||||
|
||||
:::
|
||||
|
||||
@ -776,7 +682,6 @@ npx -y create-react-app@5.0.1 --scripts-version=5.0.1 sheetjs-react
|
||||
<CodeBlock language="bash">{`\
|
||||
cd sheetjs-react
|
||||
npm i
|
||||
npm i react@18.2.0 react-dom@18.2.0 web-vitals --save --save-exact
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npm start`}
|
||||
</CodeBlock>
|
||||
@ -802,73 +707,6 @@ The generated site will be placed in the `build` folder.
|
||||
npx http-server build
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
and test the page.
|
||||
|
||||
</TabItem>
|
||||
<TabItem name="preact" value="Preact">
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Preact | ViteJS | Date |
|
||||
|:----------|:--------|:-----------|
|
||||
| `10.22.1` | `5.3.3` | 2024-12-17 |
|
||||
|
||||
:::
|
||||
|
||||
```bash
|
||||
npm init preact sheetjs-preact
|
||||
```
|
||||
|
||||
This will initiate the project creation process. **Follow the on-screen prompts and
|
||||
press Enter to accept the default options.**
|
||||
|
||||
2) Install the SheetJS dependency and start the dev server:
|
||||
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
cd sheetjs-preact
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npm run dev`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Open a web browser and access the displayed URL (`http://localhost:5173`)
|
||||
|
||||
4) Copy `src/SheetJSReactHTML.js` demo code to `src/index.jsx` and update the imports:
|
||||
|
||||
```jsx title="src/index.jsx (replace React import with Preact)"
|
||||
// Remove React import and replace with:
|
||||
import { render } from 'preact';
|
||||
import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
|
||||
```
|
||||
|
||||
5) Add the following to the bottom of the file:
|
||||
|
||||
```jsx title="src/index.jsx (append to file)"
|
||||
export function App() { return ( <SheetJSReactHTML/> );}
|
||||
|
||||
render(<App />, document.getElementById('app'));
|
||||
```
|
||||
|
||||
The page will refresh and show a table with an Export button. Click the button
|
||||
and the page will attempt to download `SheetJSReactHTML.xlsx`.
|
||||
|
||||
6) Build the site:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
The generated site will be placed in the `dist` folder.
|
||||
|
||||
7) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
and test the page.
|
||||
|
||||
@ -923,6 +761,5 @@ transpiled in the browser using Babel standalone library.
|
||||
[^2]: See [`useEffect`](https://react.dev/reference/react/useEffect) in the ReactJS documentation.
|
||||
[^3]: See [`useCallback`](https://react.dev/reference/react/useCallback) in the ReactJS documentation.
|
||||
[^4]: See [`useCallback`](https://react.dev/reference/react/useCallback) in the ReactJS documentation.
|
||||
[^5]: See [Create React App Issue #13717](https://github.com/facebook/create-react-app/issues/13717) for details about React 19+ compatibility issues.
|
||||
[^6]: See ["Merged Cells" in "SheetJS Data Model"](/docs/csf/features/merges) for more details.
|
||||
[^7]: [`dangerouslySetInnerHTML`](https://react.dev/reference/react-dom/components/common#common-props) is a ReactJS prop supported for all built-in components.
|
||||
[^5]: See ["Merged Cells" in "SheetJS Data Model"](/docs/csf/features/merges) for more details.
|
||||
[^6]: [`dangerouslySetInnerHTML`](https://react.dev/reference/react-dom/components/common#common-props) is a ReactJS prop supported for all built-in components.
|
||||
|
@ -89,7 +89,7 @@ loaded into the site. This sheet will have known columns.
|
||||
|
||||
The example [presidents sheet](https://docs.sheetjs.com/pres.xlsx) has one
|
||||
header row with "Name" and "Index" columns. The natural JS representation is an
|
||||
object for each row, where the keys are specified in the first row:
|
||||
object for each row, using the values in the first rows as keys:
|
||||
|
||||
<table>
|
||||
<thead><tr><th>Spreadsheet</th><th>State</th></tr></thead>
|
||||
@ -298,7 +298,7 @@ export class AppComponent {
|
||||
#### Complete Component
|
||||
|
||||
This complete component example fetches a test file and displays the contents in
|
||||
an HTML table. When the export button is clicked, a callback will export a file:
|
||||
a HTML table. When the export button is clicked, a callback will export a file:
|
||||
|
||||
<Tabs groupId="ngVer">
|
||||
<TabItem value="2" label="Angular 2-16">
|
||||
@ -425,10 +425,8 @@ This demo was tested in the following environments:
|
||||
|
||||
| Angular | Date |
|
||||
|:----------|:-----------|
|
||||
| `19.0.5` | 2025-01-03 |
|
||||
| `18.2.13` | 2025-01-03 |
|
||||
| `17.3.12` | 2025-01-03 |
|
||||
| `16.2.12` | 2025-01-03 |
|
||||
| `17.3.0` | 2024-03-13 |
|
||||
| `16.2.12` | 2024-03-13 |
|
||||
|
||||
:::
|
||||
|
||||
@ -441,16 +439,16 @@ npx @angular/cli analytics disable -g
|
||||
1) Create a new project:
|
||||
|
||||
```bash
|
||||
npx @angular/cli@19 new --minimal --defaults --no-interactive sheetjs-angular
|
||||
npx @angular/cli@17.3.0 new --minimal --defaults --no-interactive sheetjs-angular
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
The `@angular/cli` version controls the project version of Angular. For example,
|
||||
the following command uses Angular 16:
|
||||
the following command uses Angular 16.2.12:
|
||||
|
||||
```bash
|
||||
npx @angular/cli@16 new --minimal --defaults --no-interactive sheetjs-angular
|
||||
npx @angular/cli@16.2.12 new --minimal --defaults --no-interactive sheetjs-angular
|
||||
```
|
||||
|
||||
:::
|
||||
@ -621,10 +619,8 @@ This demo was tested in the following environments:
|
||||
|
||||
| Angular | Date |
|
||||
|:----------|:-----------|
|
||||
| `19.0.5` | 2025-01-03 |
|
||||
| `18.2.13` | 2025-01-03 |
|
||||
| `17.3.12` | 2025-01-03 |
|
||||
| `16.2.12` | 2025-01-03 |
|
||||
| `17.3.0` | 2024-03-13 |
|
||||
| `16.2.12` | 2024-03-13 |
|
||||
|
||||
:::
|
||||
|
||||
@ -637,16 +633,16 @@ npx @angular/cli analytics disable -g
|
||||
1) Create a new project:
|
||||
|
||||
```bash
|
||||
npx @angular/cli@19 new --minimal --defaults --no-interactive sheetjs-angular
|
||||
npx @angular/cli@17.3.0 new --minimal --defaults --no-interactive sheetjs-angular
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
The `@angular/cli` version controls the project version of Angular. For example,
|
||||
the following command uses Angular 16:
|
||||
the following command uses Angular 16.2.12:
|
||||
|
||||
```bash
|
||||
npx @angular/cli@16 new --minimal --defaults --no-interactive sheetjs-angular
|
||||
npx @angular/cli@16.2.12 new --minimal --defaults --no-interactive sheetjs-angular
|
||||
```
|
||||
|
||||
:::
|
||||
|
@ -18,8 +18,8 @@ import CodeBlock from '@theme/CodeBlock';
|
||||
data from spreadsheets.
|
||||
|
||||
This demo uses VueJS and SheetJS to process and generate spreadsheets. We'll
|
||||
explore how to load SheetJS in a VueJS single-file component and compare common
|
||||
state models and data flow strategies.
|
||||
explore how to load SheetJS in a VueJS SFC (single-file component) and compare
|
||||
common state models and data flow strategies.
|
||||
|
||||
:::note pass
|
||||
|
||||
@ -58,7 +58,7 @@ loaded into the site. This sheet will have known columns.
|
||||
|
||||
The example [presidents sheet](https://docs.sheetjs.com/pres.xlsx) has one
|
||||
header row with "Name" and "Index" columns. The natural JS representation is an
|
||||
object for each row, where the keys are specified in the first row:
|
||||
object for each row, using the values in the first rows as keys:
|
||||
|
||||
<table>
|
||||
<thead><tr><th>Spreadsheet</th><th>State</th></tr></thead>
|
||||
@ -234,7 +234,7 @@ onMounted(async() => {
|
||||
A component will typically map over the data with `v-for`[^3]. The following
|
||||
example generates a TABLE with a row for each President:
|
||||
|
||||
```html title="Example single-file component for displaying arrays of objects"
|
||||
```html title="Example SFC for displaying arrays of objects"
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
const rows = ref([]);
|
||||
@ -305,7 +305,7 @@ function exportFile() {
|
||||
#### Complete Component
|
||||
|
||||
This complete component example fetches a test file and displays the contents in
|
||||
an HTML table. When the export button is clicked, a callback will export a file:
|
||||
a HTML table. When the export button is clicked, a callback will export a file:
|
||||
|
||||
```html title="src/SheetJSVueAoO.vue"
|
||||
<script setup>
|
||||
@ -359,7 +359,7 @@ This demo was tested in the following environments:
|
||||
|
||||
| VueJS | ViteJS | Date |
|
||||
|:---------|:---------|:-----------|
|
||||
| `3.5.13` | `6.1.0` | 2025-02-15 |
|
||||
| `3.4.27` | `5.2.11` | 2024-05-26 |
|
||||
|
||||
:::
|
||||
|
||||
@ -412,7 +412,7 @@ This demo was tested in the following environments:
|
||||
|
||||
| VueJS | NuxtJS | Date |
|
||||
|:---------|:---------|:-----------|
|
||||
| `3.5.13` | `3.15.0` | 2025-01-02 |
|
||||
| `3.4.21` | `3.11.1` | 2024-03-21 |
|
||||
|
||||
:::
|
||||
|
||||
@ -520,7 +520,7 @@ This demo was tested in the following environments:
|
||||
|
||||
| VueJS | ViteJS | Date |
|
||||
|:---------|:--------|:-----------|
|
||||
| `3.5.13` | `6.0.7` | 2025-01-02 |
|
||||
| `3.4.21` | `5.2.2` | 2024-03-21 |
|
||||
|
||||
:::
|
||||
|
||||
@ -573,7 +573,7 @@ This demo was tested in the following environments:
|
||||
|
||||
| VueJS | NuxtJS | Date |
|
||||
|:---------|:---------|:-----------|
|
||||
| `3.5.13` | `3.15.0` | 2025-01-02 |
|
||||
| `3.4.21` | `3.11.1` | 2024-03-21 |
|
||||
|
||||
:::
|
||||
|
||||
|
@ -128,9 +128,9 @@ function exportFile() {
|
||||
<td>{p.Index}</td>
|
||||
</tr>{/each}
|
||||
<!-- highlight-end -->
|
||||
</tbody><tfoot><tr><td colSpan={2}>
|
||||
</tbody><tfoot><td colSpan={2}>
|
||||
<button on:click={exportFile}>Export XLSX</button>
|
||||
</td></tfoot></tr></table>
|
||||
</td></tfoot></table>
|
||||
</main>
|
||||
```
|
||||
|
||||
@ -143,7 +143,7 @@ This demo was tested in the following environments:
|
||||
|
||||
| SvelteJS | ViteJS | Date |
|
||||
|:---------|:---------|:-----------|
|
||||
| `5.25.3` | `6.2.3` | 2025-03-30 |
|
||||
| `4.2.18` | `5.2.13` | 2024-06-07 |
|
||||
|
||||
:::
|
||||
|
||||
@ -170,9 +170,7 @@ The page will refresh and show a table with an Export button. Click the button
|
||||
and the page will attempt to download `SheetJSSvelteAoA.xlsx`. There may be a
|
||||
delay since Vite will try to optimize the SheetJS library on the fly.
|
||||
|
||||
5) Stop the server (press <kbd>CTRL</kbd>+<kbd>C</kbd> in the terminal window).
|
||||
|
||||
6) Build the site:
|
||||
5) Build the site:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
@ -180,10 +178,10 @@ npm run build
|
||||
|
||||
The generated site will be placed in the `dist` folder.
|
||||
|
||||
7) Start a local web server:
|
||||
6) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
@ -245,7 +243,7 @@ This demo was tested in the following environments:
|
||||
|
||||
| SvelteJS | ViteJS | Date |
|
||||
|:---------|:---------|:-----------|
|
||||
| `5.25.3` | `6.2.3` | 2025-03-30 |
|
||||
| `4.2.18` | `5.2.13` | 2024-06-07 |
|
||||
|
||||
:::
|
||||
|
||||
@ -272,9 +270,7 @@ The page will refresh and show a table with an Export button. Click the button
|
||||
and the page will attempt to download `SheetJSSvelteHTML.xlsx`. There may be a
|
||||
delay since Vite will try to optimize the SheetJS library on the fly.
|
||||
|
||||
5) Stop the server (press <kbd>CTRL</kbd>+<kbd>C</kbd> in the terminal window).
|
||||
|
||||
6) Build the site:
|
||||
5) Build the site:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
@ -282,10 +278,10 @@ npm run build
|
||||
|
||||
The generated site will be placed in the `dist` folder.
|
||||
|
||||
7) Start a local web server:
|
||||
6) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
|
@ -36,9 +36,8 @@ This demo was tested in the following environments:
|
||||
|
||||
| Browser | Version | Date |
|
||||
|:-------------|:------------------|:-----------|
|
||||
| Chromium 133 | `1.8.2` (latest) | 2025-03-30 |
|
||||
| Konqueror 22 | `1.8.2` (latest) | 2025-04-23 |
|
||||
| Chromium 133 | `1.2.32` (legacy) | 2025-03-30 |
|
||||
| Chromiun 125 | `1.8.2` (latest) | 2024-06-09 |
|
||||
| Chromium 125 | `1.2.32` (legacy) | 2024-06-09 |
|
||||
|
||||
:::
|
||||
|
||||
@ -77,7 +76,7 @@ and return a SheetJS workbook object[^2].
|
||||
This controller fetches [a sample file](https://docs.sheetjs.com/pres.xlsx),
|
||||
parses the result into a workbook, extracts the first worksheet, and uses the
|
||||
SheetJS [`sheet_to_html`](/docs/api/utilities/html#html-table-output) method to
|
||||
generate an HTML table:
|
||||
generate a HTML table:
|
||||
|
||||
```js title="Sample Controller"
|
||||
/* The controller function must accept a `$http` argument */
|
||||
@ -87,9 +86,9 @@ app.controller('sheetjs', function($scope, $http) {
|
||||
method:'GET', url:'https://docs.sheetjs.com/pres.xlsx',
|
||||
/* ensure the result is an ArrayBuffer object */
|
||||
responseType:'arraybuffer'
|
||||
}).then(function(response) {
|
||||
}).then(function(data) {
|
||||
// highlight-next-line
|
||||
var wb = XLSX.read(response.data);
|
||||
var wb = XLSX.read(data.data);
|
||||
/* generate HTML from first worksheet*/
|
||||
var ws = wb.Sheets[wb.SheetNames[0]];
|
||||
var html = XLSX.utils.sheet_to_html(ws);
|
||||
@ -170,7 +169,7 @@ depends on the application.
|
||||
|
||||
The example [presidents sheet](https://docs.sheetjs.com/pres.xlsx) has one
|
||||
header row with "Name" and "Index" columns. The natural JS representation is an
|
||||
object for each row, where the keys are specified in the first row:
|
||||
object for each row, using the values in the first rows as keys:
|
||||
|
||||
<table>
|
||||
<thead><tr><th>Spreadsheet</th><th>State</th></tr></thead>
|
||||
@ -201,8 +200,8 @@ app.controller('sheetjs', function($scope, $http) {
|
||||
$http({
|
||||
url:'https://docs.sheetjs.com/pres.xlsx',
|
||||
method:'GET', responseType:'arraybuffer'
|
||||
}).then(function(response) {
|
||||
var wb = XLSX.read(response.data);
|
||||
}).then(function(data) {
|
||||
var wb = XLSX.read(data.data);
|
||||
// highlight-next-line
|
||||
$scope.data = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
||||
}, function(err) { console.log(err); });
|
||||
@ -278,8 +277,8 @@ app.controller('sheetjs', function($scope, $http) {
|
||||
$http({
|
||||
url:'https://docs.sheetjs.com/pres.xlsx',
|
||||
method:'GET', responseType:'arraybuffer'
|
||||
}).then(function(response) {
|
||||
var wb = XLSX.read(response.data);
|
||||
}).then(function(data) {
|
||||
var wb = XLSX.read(data.data);
|
||||
var data = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
||||
$scope.data = data;
|
||||
}, function(err) { console.log(err); });
|
||||
@ -289,13 +288,8 @@ app.controller('sheetjs', function($scope, $http) {
|
||||
</html>`}
|
||||
</CodeBlock>
|
||||
|
||||
2) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server .
|
||||
```
|
||||
|
||||
Access the displayed URL with a web browser (typically `http://localhost:8080`)
|
||||
2) Start a local web server with `npx http-server .` and access the displayed
|
||||
URL with a web browser (typically `http://localhost:8080`)
|
||||
|
||||
When the page loads, the app will fetch https://docs.sheetjs.com/pres.xlsx and
|
||||
store an array of objects in state. When the "Export Table" button is clicked,
|
||||
@ -327,8 +321,8 @@ app.controller('sheetjs', function($scope, $http) {
|
||||
$http({
|
||||
url:'https://docs.sheetjs.com/pres.xlsx',
|
||||
method:'GET', responseType:'arraybuffer'
|
||||
}).then(function(response) {
|
||||
var wb = XLSX.read(response.data);
|
||||
}).then(function(data) {
|
||||
var wb = XLSX.read(data.data);
|
||||
// highlight-next-line
|
||||
$scope.data = XLSX.utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]);
|
||||
}, function(err) { console.log(err); });
|
||||
@ -383,8 +377,8 @@ app.controller('sheetjs', function($scope, $http) {
|
||||
$http({
|
||||
url:'https://docs.sheetjs.com/pres.xlsx',
|
||||
method:'GET', responseType:'arraybuffer'
|
||||
}).then(function(response) {
|
||||
var wb = XLSX.read(response.data);
|
||||
}).then(function(data) {
|
||||
var wb = XLSX.read(data.data);
|
||||
$scope.data = XLSX.utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]);
|
||||
}, function(err) { console.log(err); });
|
||||
});
|
||||
@ -393,13 +387,8 @@ app.controller('sheetjs', function($scope, $http) {
|
||||
</html>`}
|
||||
</CodeBlock>
|
||||
|
||||
2) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server .
|
||||
```
|
||||
|
||||
Access the displayed URL with a web browser (typically `http://localhost:8080`)
|
||||
2) Start a local web server with `npx http-server .` and access the displayed
|
||||
URL with a web browser (typically `http://localhost:8080`)
|
||||
|
||||
When the page loads, the app will fetch https://docs.sheetjs.com/pres.xlsx and
|
||||
store the HTML string in state. When the "Export Table" button is clicked, a
|
||||
|
@ -76,8 +76,7 @@ This demo was tested in the following environments:
|
||||
|
||||
| Platform | Date |
|
||||
|:-------------|:-----------|
|
||||
| Chromium 133 | 2025-03-30 |
|
||||
| Konqueror 22 | 2025-04-23 |
|
||||
| Chromium 125 | 2024-06-08 |
|
||||
|
||||
Demos exclusively using Dojo Core were tested using Dojo Toolkit `1.17.3`.
|
||||
|
||||
@ -98,7 +97,7 @@ was the latest version available on the Google CDN.
|
||||
When fetching spreadsheets with XHR, `handleAs: "arraybuffer"` yields an
|
||||
`ArrayBuffer` which can be passed to the SheetJS `read` method.
|
||||
|
||||
The following example generates an HTML table from the first worksheet:
|
||||
The following example generates a HTML table from the first worksheet:
|
||||
|
||||
```html
|
||||
<div id="tbl"></div>
|
||||
|
@ -1,506 +0,0 @@
|
||||
---
|
||||
title: Sheets in Blazor Sites
|
||||
sidebar_label: Blazor
|
||||
pagination_prev: demos/index
|
||||
pagination_next: demos/grid/index
|
||||
sidebar_position: 9
|
||||
---
|
||||
|
||||
import current from '/version.js';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
[Blazor](https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor) is a
|
||||
framework for building user interfaces using C#, HTML, JS and CSS.
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
This demo uses Blazor and SheetJS to process and generate spreadsheets. We'll
|
||||
explore how to load SheetJS in Razor components and compare common state models
|
||||
and data flow strategies.
|
||||
|
||||
:::caution Blazor support is considered experimental.
|
||||
|
||||
Great open source software grows with user tests and reports. Any issues should
|
||||
be reported to the Blazor project for further diagnosis.
|
||||
|
||||
:::
|
||||
|
||||
:::danger Telemetry
|
||||
|
||||
**The `dotnet` command embeds telemetry.**
|
||||
|
||||
The `DOTNET_CLI_TELEMETRY_OPTOUT` environment variable should be set to `1`.
|
||||
|
||||
["Platform Configuration"](#platform-configuration) includes instructions for
|
||||
setting the environment variable on supported platforms.
|
||||
|
||||
:::
|
||||
|
||||
## Integration Details
|
||||
|
||||
### Installation
|
||||
|
||||
The SheetJS library can be loaded when the page is loaded or imported whenever
|
||||
the library functionality is used.
|
||||
|
||||
#### Standalone Script
|
||||
|
||||
The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone)
|
||||
can be loaded in the root HTML page (typically `wwwroot/index.html`):
|
||||
|
||||
<CodeBlock language="html">{`\
|
||||
<!-- use version ${current} -->
|
||||
<script lang="javascript" src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>`}
|
||||
</CodeBlock>
|
||||
|
||||
#### ECMAScript Module
|
||||
|
||||
The SheetJS ECMAScript module script can be dynamically imported from functions.
|
||||
This ensures the library is only loaded when necessary. The following JS example
|
||||
loads the library and returns a Promise that resolves to the version string:
|
||||
|
||||
<CodeBlock language="js">{`\
|
||||
async function sheetjs_version(id) {
|
||||
/* dynamically import the script in the event listener */
|
||||
// highlight-next-line
|
||||
const XLSX = await import("https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs");
|
||||
\n\
|
||||
/* use the library */
|
||||
return XLSX.version;
|
||||
}`}
|
||||
</CodeBlock>
|
||||
|
||||
### Calling JS from C#
|
||||
|
||||
Callbacks for events in Razor elements invoke C# methods. The C# methods can use
|
||||
Blazor APIs to invoke JS methods that are visible in the browser global scope.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
actor U as User
|
||||
participant P as Browser
|
||||
participant A as Blazor
|
||||
U-->>P: click button
|
||||
P-->>A: click event
|
||||
Note over A: C#35; callback<br/><br/>InvokeVoidAsync
|
||||
A->>P: call JS function
|
||||
Note over P: global method<br/><br/>SheetJS logic
|
||||
P->>U: download workbook
|
||||
```
|
||||
|
||||
#### Setup
|
||||
|
||||
The primary mechanism for invoking JS functions from Blazor is `IJSRuntime`[^1].
|
||||
It should be injected at the top of relevant Razor component scripts:
|
||||
|
||||
```csharp title="Injecting IJSRuntime"
|
||||
@inject IJSRuntime JS
|
||||
```
|
||||
|
||||
#### Fire and Forget
|
||||
|
||||
When exporting a file with the SheetJS `writeFile` method[^2], browser APIs do
|
||||
not provide success or error feedback. As a result, this demo invokes functions
|
||||
using the `InvokeVoidAsync` static method[^3].
|
||||
|
||||
The following C# method will invoke the `export_method` method in the browser:
|
||||
|
||||
```csharp title="Invoking JS functions from C#"
|
||||
private async Task ExportDataset() {
|
||||
await JS.InvokeVoidAsync("export_method", data);
|
||||
}
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
**The JS methods must be defined in the global scope!**
|
||||
|
||||
In this demo, the script is added to the `HEAD` block of the root HTML file:
|
||||
|
||||
```html title="wwwroot/index.html"
|
||||
<head>
|
||||
<!-- ... meta / title / base / link tags -->
|
||||
<link href="SheetJSBlazorWasm.styles.css" rel="stylesheet" />
|
||||
|
||||
<!-- highlight-start -->
|
||||
<!-- script with `export_method` is in the HEAD block -->
|
||||
<script>
|
||||
/* In a normal script tag, Blazor JS can call this method */
|
||||
async function export_method(...rows) {
|
||||
/* display the array of objects */
|
||||
console.log(rows);
|
||||
}
|
||||
</script>
|
||||
<!-- highlight-end -->
|
||||
</head>
|
||||
```
|
||||
|
||||
When using `<script type="module">`, top-level function definitions are not
|
||||
visible to Blazor by default. They must be attached to `globalThis`:
|
||||
|
||||
```html title="Attaching methods to globalThis"
|
||||
<script type="module">
|
||||
/* Using `type="module"`, Blazor JS cannot see this function definition */
|
||||
async function export_method(...rows) {
|
||||
/* display the array of objects */
|
||||
console.log(rows);
|
||||
}
|
||||
|
||||
// highlight-start
|
||||
/* Once attached to `globalThis`, Blazor JS can call this method */
|
||||
globalThis.export_method = export_method;
|
||||
// highlight-end
|
||||
</script>
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
#### Blazor Callbacks
|
||||
|
||||
Methods are commonly bound to buttons in the Razor template using `@onclick`.
|
||||
When the following button is clicked, Blazor will invoke `ExportDataset`:
|
||||
|
||||
```html title="Binding callback to a HTML button"
|
||||
<button @onclick="ExportDataset">Export Dataset</button>
|
||||
```
|
||||
|
||||
### State in Blazor
|
||||
|
||||
The example [presidents sheet](https://docs.sheetjs.com/pres.xlsx) has one
|
||||
header row with "Name" and "Index" columns.
|
||||
|
||||

|
||||
|
||||
#### C# Representation
|
||||
|
||||
The natural C# representation of a single row is a class object:
|
||||
|
||||
```csharp title="President class"
|
||||
public class President {
|
||||
public string Name { get; set; }
|
||||
public int Index { get; set; }
|
||||
}
|
||||
|
||||
var PrezClinton = new President() { Name = "Bill Clinton", Index = 42 };
|
||||
```
|
||||
|
||||
The entire dataset is typically stored in an array of class objects:
|
||||
|
||||
```csharp title="President dataset"
|
||||
private President[] data;
|
||||
```
|
||||
|
||||
#### Data Interchange
|
||||
|
||||
`InvokeVoidAsync` can pass data from the C# state to a JS function:
|
||||
|
||||
```csharp
|
||||
await JS.InvokeVoidAsync("export_method", data);
|
||||
```
|
||||
|
||||
Each row in the dataset will be passed as a separate argument to the JavaScript
|
||||
method, so the JavaScript code should collect the arguments:
|
||||
|
||||
```js title="Collecting rows in a JS callback"
|
||||
/* NOTE: blazor spreads the C# array, so the ... spread syntax is required */
|
||||
async function export_method(...rows) {
|
||||
/* display the array of objects */
|
||||
console.log(rows);
|
||||
}
|
||||
```
|
||||
|
||||
Each row is a simple JavaScript object.
|
||||
|
||||
:::caution pass
|
||||
|
||||
Blazor automatically spreads arrays. Each row is passed as a separate argument
|
||||
to the JavaScript method.
|
||||
|
||||
The example method uses the JavaScript spread syntax to collect the arguments.
|
||||
|
||||
:::
|
||||
|
||||
#### Exporting Data
|
||||
|
||||
With the collected array of objects, the SheetJS `json_to_sheet` method[^4] will
|
||||
generate a SheetJS worksheet[^5] from the dataset. After creating a workbook[^6]
|
||||
object with the `book_new` method[^7], the file is written with `writeFile`[^2]:
|
||||
|
||||
<CodeBlock title="JS Callback for exporting datasets" language="javascript">{`\
|
||||
/* NOTE: blazor spreads the C# array, so the spread is required */
|
||||
async function export_method(...rows) {
|
||||
const XLSX = await import("https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs");
|
||||
const ws = XLSX.utils.json_to_sheet(rows);
|
||||
const wb = XLSX.utils.book_new(ws, "Data");
|
||||
XLSX.writeFile(wb, "SheetJSBlazor.xlsx");
|
||||
}`}
|
||||
</CodeBlock>
|
||||
|
||||
|
||||
### HTML Tables
|
||||
|
||||
When displaying datasets, Razor components typically generate HTML tables:
|
||||
|
||||
```html title="Razor template from official starter"
|
||||
<table class="table" id="weather-table">
|
||||
<thead>
|
||||
<tr><th>Date</th><th>Temp. (C)</th><th>Temp. (F)</th><th>Summary</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var forecast in forecasts)
|
||||
{
|
||||
<tr>
|
||||
<td>@forecast.Date.ToShortDateString()</td>
|
||||
<td>@forecast.TemperatureC</td>
|
||||
<td>@forecast.TemperatureF</td>
|
||||
<td>@forecast.Summary</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
```
|
||||
|
||||
If it has an `id`, JS code on the frontend can find the table element using the
|
||||
`document.getElementById` DOM method. A SheetJS workbook object can be generated
|
||||
using the `table_to_book` method[^8] and exported with `writeFile`[^2]:
|
||||
|
||||
<CodeBlock title="JS Callback for exporting HTML TABLE elements" language="javascript">{`\
|
||||
async function export_method() {
|
||||
const XLSX = await import("https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs");
|
||||
const wb = XLSX.utils.table_to_book(document.getElementById("weather-table"));
|
||||
XLSX.writeFile(wb, "SheetJSBlazor.xlsx");
|
||||
}`}
|
||||
</CodeBlock>
|
||||
|
||||
This approach uses data that already exists in the document, so no additional
|
||||
data is passed from C# to JavaScript.
|
||||
|
||||
## Complete Demo
|
||||
|
||||
The Blazor + WASM starter app includes a "Weather" component that displays data
|
||||
from a C#-managed dataset. This demo uses SheetJS to export data in two ways:
|
||||
|
||||
- "Export Dataset" will send row objects from the underlying C# data store to
|
||||
the frontend. The SheetJS `json_to_sheet` method[^4] builds the worksheet.
|
||||
|
||||
- "Export HTML Table" will scrape the table using the SheetJS `table_to_book`
|
||||
method[^8]. No extra data will be sent to the frontend.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Architecture | Date |
|
||||
|:-------------|:-----------|
|
||||
| `darwin-x64` | 2025-04-17 |
|
||||
| `darwin-arm` | 2025-04-24 |
|
||||
| `win11-x64` | 2025-04-17 |
|
||||
| `win11-arm` | 2025-04-24 |
|
||||
|
||||
:::
|
||||
|
||||
### Platform Configuration
|
||||
|
||||
0) Set the `DOTNET_CLI_TELEMETRY_OPTOUT` environment variable to `1`.
|
||||
|
||||
<details open>
|
||||
<summary><b>How to disable telemetry</b> (click to hide)</summary>
|
||||
|
||||
<Tabs groupId="os">
|
||||
<TabItem value="unix" label="Linux/MacOS">
|
||||
|
||||
Add the following line to `.profile`, `.bashrc` and `.zshrc`:
|
||||
|
||||
```bash title="(add to .profile , .bashrc , and .zshrc)"
|
||||
export DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||
```
|
||||
|
||||
Close and restart the Terminal to load the changes.
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="win" label="Windows">
|
||||
|
||||
Type `env` in the search bar and select "Edit the system environment variables".
|
||||
|
||||
In the new window, click the "Environment Variables..." button.
|
||||
|
||||
In the new window, look for the "System variables" section and click "New..."
|
||||
|
||||
Set the "Variable name" to `DOTNET_CLI_TELEMETRY_OPTOUT` and the value to `1`.
|
||||
|
||||
Click "OK" in each window (3 windows) and restart your computer.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
</details>
|
||||
|
||||
1) Install .NET
|
||||
|
||||
<details>
|
||||
<summary><b>Installation Notes</b> (click to show)</summary>
|
||||
|
||||
For macOS x64 and ARM64, install the `dotnet-sdk` Cask with Homebrew:
|
||||
|
||||
```bash
|
||||
brew install --cask dotnet-sdk
|
||||
```
|
||||
|
||||
For Steam Deck Holo and other Arch Linux x64 distributions, the `dotnet-sdk` and
|
||||
`dotnet-runtime` packages should be installed using `pacman`:
|
||||
|
||||
```bash
|
||||
sudo pacman -Syu dotnet-sdk dotnet-runtime
|
||||
```
|
||||
|
||||
https://dotnet.microsoft.com/en-us/download/dotnet/6.0 is the official source
|
||||
for Windows and ARM64 Linux versions.
|
||||
|
||||
</details>
|
||||
|
||||
2) Open a new Terminal window in macOS or PowerShell window in Windows.
|
||||
|
||||
### App Creation
|
||||
|
||||
3) Create a new `blazorwasm` app:
|
||||
|
||||
```bash
|
||||
dotnet new blazorwasm -o SheetJSBlazorWasm
|
||||
cd SheetJSBlazorWasm
|
||||
dotnet run
|
||||
```
|
||||
|
||||
When the Blazor service runs, the terminal will display a URL:
|
||||
|
||||
```text
|
||||
info: Microsoft.Hosting.Lifetime[14]
|
||||
// highlight-next-line
|
||||
Now listening on: http://localhost:6969
|
||||
```
|
||||
|
||||
4) In a new browser window, open the displayed URL from Step 3.
|
||||
|
||||
5) Click the "Weather" link and confirm the page includes a data table.
|
||||
|
||||
6) Stop the server (press <kbd>CTRL</kbd>+<kbd>C</kbd> in the terminal window).
|
||||
|
||||
### SheetJS Integration
|
||||
|
||||
7) Add the following script tag to `wwwroot/index.html` in the `HEAD` block:
|
||||
|
||||
<CodeBlock title="wwwroot/index.html (add within the HEAD block)" language="html">{`\
|
||||
<script>
|
||||
/* NOTE: blazor spreads the C# array, so the spread is required */
|
||||
async function export_dataset(...rows) {
|
||||
const XLSX = await import("https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs");
|
||||
const ws = XLSX.utils.json_to_sheet(rows);
|
||||
const wb = XLSX.utils.book_new(ws, "Data");
|
||||
XLSX.writeFile(wb, "SheetJSBlazorDataset.xlsx");
|
||||
}
|
||||
\n\
|
||||
async function export_html(id) {
|
||||
const XLSX = await import("https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs");
|
||||
const wb = XLSX.utils.table_to_book(document.getElementById(id));
|
||||
XLSX.writeFile(wb, "SheetJSBlazorHTML.xlsx");
|
||||
}
|
||||
</script>`}
|
||||
</CodeBlock>
|
||||
|
||||
8) Inject the `IJSRuntime` dependency near the top of `Pages/Weather.razor`:
|
||||
|
||||
```csharp title="Pages/Weather.razor (add highlighted lines)"
|
||||
@page "/weather"
|
||||
@inject HttpClient Http
|
||||
// highlight-next-line
|
||||
@inject IJSRuntime JS
|
||||
```
|
||||
|
||||
9) Add an ID to the `TABLE` element in `Pages/Weather.razor`:
|
||||
|
||||
```html title="Pages/Weather.razor (add id to TABLE element)"
|
||||
{
|
||||
<!-- highlight-next-line -->
|
||||
<table class="table" id="weather-table">
|
||||
<thead>
|
||||
<tr>
|
||||
```
|
||||
|
||||
10) Add callbacks to the `@code` section in `Pages/Weather.razor`:
|
||||
|
||||
```csharp title="Pages/Weather.razor (add within the @code section)"
|
||||
private async Task ExportDataset()
|
||||
{
|
||||
await JS.InvokeVoidAsync("export_dataset", forecasts);
|
||||
}
|
||||
|
||||
private async Task ExportHTML()
|
||||
{
|
||||
await JS.InvokeVoidAsync("export_html", "weather-table");
|
||||
}
|
||||
```
|
||||
|
||||
11) Add Export buttons to the template in `Pages/Weather.razor`:
|
||||
|
||||
```csharp title="Pages/Weather.razor (add highlighted lines)"
|
||||
<p>This component demonstrates fetching data from the server.</p>
|
||||
|
||||
<!-- highlight-start -->
|
||||
<button @onclick="ExportDataset">Export Dataset</button>
|
||||
<button @onclick="ExportHTML">Export HTML TABLE</button>
|
||||
<!-- highlight-end -->
|
||||
```
|
||||
|
||||
|
||||
### Testing
|
||||
|
||||
12) Launch the `dotnet` process again:
|
||||
|
||||
```bash
|
||||
dotnet run
|
||||
```
|
||||
|
||||
When the Blazor service runs, the terminal will display a URL:
|
||||
|
||||
```text
|
||||
info: Microsoft.Hosting.Lifetime[14]
|
||||
Now listening on: http://localhost:6969
|
||||
```
|
||||
|
||||
13) In a new browser window, open the displayed URL from Step 12.
|
||||
|
||||
14) Click the "Weather" link. The page should match the following screenshot:
|
||||
|
||||

|
||||
|
||||
15) Click the "Export Dataset" button and save the generated file to
|
||||
`SheetJSBlazorDataset.xlsx`. Open the file in a spreadsheet editor and confirm
|
||||
the data matches the table. The column labels will differ since the underlying
|
||||
dataset uses different labels.
|
||||
|
||||

|
||||
|
||||
16) Click the "Export HTML TABLE" button and save the generated file to
|
||||
`SheetJSBlazorHTML.xlsx`. Open the file in a spreadsheet editor and confirm the
|
||||
data matches the table. The column labels will match the HTML table.
|
||||
|
||||

|
||||
|
||||
:::note pass
|
||||
|
||||
It is somewhat curious that the official `dotnet` Blazor sample dataset marks
|
||||
`1 C` and `-13 C` as "freezing" but marks `-2 C` as "chilly". It stands to
|
||||
reason that `-2 C` should also be freezing.
|
||||
|
||||
:::
|
||||
|
||||
[^1]: See ["Microsoft.JSInterop.IJSRuntime"](https://learn.microsoft.com/en-us/dotnet/api/microsoft.jsinterop.ijsruntime) in the `dotnet` documentation.
|
||||
[^2]: See [`writeFile` in "Writing Files"](/docs/api/write-options)
|
||||
[^3]: See ["Microsoft.JSInterop.JSRuntimeExtensions.InvokeVoidAsync"](https://learn.microsoft.com/en-us/dotnet/api/microsoft.jsinterop.jsruntimeextensions.invokevoidasync) in the `dotnet` documentation.
|
||||
[^4]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
|
||||
[^5]: See ["Sheet Objects"](/docs/csf/sheet)
|
||||
[^6]: See ["Workbook Object"](/docs/csf/book)
|
||||
[^7]: See [`book_new` in "Utilities"](/docs/api/utilities/wb)
|
||||
[^8]: See [`table_to_book` in "HTML" Utilities](/docs/api/utilities/html#create-new-sheet)
|
@ -2,7 +2,7 @@
|
||||
title: Legacy Frameworks
|
||||
pagination_prev: demos/index
|
||||
pagination_next: demos/grid/index
|
||||
sidebar_position: 18
|
||||
sidebar_position: 9
|
||||
sidebar_custom_props:
|
||||
skip: 1
|
||||
---
|
||||
@ -211,8 +211,8 @@ This demo was tested in the following environments:
|
||||
|
||||
| KnockoutJS | Date | Live Demo |
|
||||
|:-----------|:-----------|:-----------------------------------------------|
|
||||
| `3.5.0` | 2025-01-08 | [**KO3**](pathname:///knockout/knockout3.html) |
|
||||
| `2.3.0` | 2025-01-08 | [**KO2**](pathname:///knockout/knockout2.html) |
|
||||
| `3.5.0` | 2024-04-07 | [**KO3**](pathname:///knockout/knockout3.html) |
|
||||
| `2.3.0` | 2024-04-07 | [**KO2**](pathname:///knockout/knockout2.html) |
|
||||
|
||||
:::
|
||||
|
@ -1,635 +0,0 @@
|
||||
---
|
||||
title: Sheets in UI5 Sites
|
||||
sidebar_label: OpenUI5 / SAPUI5
|
||||
description: Build enterprise-grade applications with OpenUI5. Seamlessly integrate spreadsheets into your app using SheetJS. Bring Excel-powered workflows and data to the modern web.
|
||||
pagination_prev: demos/index
|
||||
pagination_next: demos/grid/index
|
||||
sidebar_position: 10
|
||||
---
|
||||
|
||||
import current from '/version.js';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
[OpenUI5](https://openui5.org/) is a JavaScript framework for building
|
||||
enterprise-ready web applications. It is compatible with the SAPUI5 framework.
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
This demo shows how to handle spreadsheet data in OpenUI5 apps using SheetJS.
|
||||
You'll learn how to load spreadsheet files, process their data, and generate
|
||||
new spreadsheet exports.
|
||||
|
||||
:::info pass
|
||||
|
||||
[Docs Issue #20](https://git.sheetjs.com/sheetjs/docs.sheetjs.com/issues/20)
|
||||
includes a complete example starting from the OpenUI5 "Worklist App Tutorial".
|
||||
|
||||
:::
|
||||
|
||||
## Installation
|
||||
|
||||
SheetJS libraries conform to the UI5 ECMAScript requirements[^1]. SheetJS
|
||||
libraries can be loaded in a UI5 site at different points in the app lifecycle.
|
||||
|
||||
#### UI5 Module {#installation-define}
|
||||
|
||||
The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone)
|
||||
comply with AMD `define` semantics. They support `sap.ui.define` out of the box.
|
||||
|
||||
If the SheetJS Standalone script is saved to `webapp/xlsx.full.min.js`, the base
|
||||
script `webapp/index.js` can load the `./xlsx.full.min` dependency:
|
||||
|
||||
```js title="webapp/index.js (loading the SheetJS dependency)"
|
||||
sap.ui.define([
|
||||
// highlight-next-line
|
||||
"./xlsx.full.min", // relative path to script, without the file extension
|
||||
/* ... other libraries ... */
|
||||
], function (
|
||||
// highlight-next-line
|
||||
_XLSX // !! NOTE: this is not XLSX! A different variable name must be used
|
||||
/* ... variables for the other libraries ... */,
|
||||
) {
|
||||
// highlight-next-line
|
||||
alert(XLSX.version); // use XLSX in the callback
|
||||
});
|
||||
```
|
||||
|
||||
:::info pass
|
||||
|
||||
In some deployments, the function argument was `undefined`.
|
||||
|
||||
The standalone scripts add `window.XLSX`, so it is recommended to use `_XLSX`
|
||||
in the function arguments and access the library with `XLSX` in the callback.
|
||||
|
||||
:::
|
||||
|
||||
#### HTML {#installation-html}
|
||||
|
||||
UI5 is typically loaded in a `SCRIPT` tag in `webapp/index.html`. Similarly,
|
||||
[SheetJS Standalone scripts](/docs/getting-started/installation/standalone)
|
||||
can be loaded with a `SCRIPT` tag in the same HTML page:
|
||||
|
||||
<CodeBlock language="html" value="html" title="index.html (add in the HEAD block before UI5 scripts)">{`\
|
||||
<script src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>
|
||||
`}
|
||||
</CodeBlock>
|
||||
|
||||
This will expose the `XLSX` global object, which includes the functions listed
|
||||
in the ["API Reference"](/docs/api/) section of the documentation.
|
||||
|
||||
:::caution pass
|
||||
|
||||
**The SheetJS Standalone script must be loaded before the UI5 bootstrap script**:
|
||||
|
||||
<CodeBlock language="html" value="html" title="webapp/index.html (loading the SheetJS standalone script)">{`\
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>UI5 Walkthrough</title>
|
||||
|
||||
<!-- The SheetJS Standalone script must be loaded before the UI5 bootstrap -->
|
||||
<script src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>
|
||||
|
||||
<!-- UI5 bootstrap script -->
|
||||
<script
|
||||
id="sap-ui-bootstrap"
|
||||
src="resources/sap-ui-core.js"
|
||||
...(other attributes)...
|
||||
></script>
|
||||
</head>
|
||||
`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
## Internal State
|
||||
|
||||
The various SheetJS APIs work with various data shapes. The preferred state
|
||||
depends on the application.
|
||||
|
||||
### JSON Model
|
||||
|
||||
The UI5 `JSONModel`[^2] is a client-side model implementation for JavaScript
|
||||
object data. Think of it like a container that holds your spreadsheet data.
|
||||
|
||||
`JSONModel` provides powerful two-way data binding capabilities. UI5 will
|
||||
automatically updates your webpage whenever the data changes. It will also
|
||||
respond to changes when users interact with components in the webpage.
|
||||
|
||||
|
||||
#### State {#json-state}
|
||||
|
||||
The example [presidents sheet](https://docs.sheetjs.com/pres.xlsx) has one
|
||||
header row with "Name" and "Index" columns. The natural JS representation is an
|
||||
object for each row, where the keys are specified in the first row:
|
||||
|
||||
<table>
|
||||
<thead><tr><th>Spreadsheet</th><th>State</th></tr></thead>
|
||||
<tbody><tr><td>
|
||||
|
||||

|
||||
|
||||
</td><td>
|
||||
|
||||
```js
|
||||
[
|
||||
{ Name: "Bill Clinton", Index: 42 },
|
||||
{ Name: "GeorgeW Bush", Index: 43 },
|
||||
{ Name: "Barack Obama", Index: 44 },
|
||||
{ Name: "Donald Trump", Index: 45 },
|
||||
{ Name: "Joseph Biden", Index: 46 }
|
||||
]
|
||||
```
|
||||
|
||||
</td></tr></tbody></table>
|
||||
|
||||
Here is a basic example of initializing a model. A more complete implementation
|
||||
will be shown later.
|
||||
|
||||
```js title="UI5 JSONModel for rows of data"
|
||||
sap.ui.define(["sap/ui/model/json/JSONModel"], function (JSONModel) {
|
||||
// highlight-next-line
|
||||
const oModel = new JSONModel({ presidents: [] });
|
||||
});
|
||||
```
|
||||
|
||||
#### Updating State {#json-update}
|
||||
|
||||
Starting from a spreadsheet file, the SheetJS [`read`](/docs/api/parse-options)
|
||||
method parses the data into a SheetJS workbook object[^3]. After selecting a
|
||||
worksheet, the [`sheet_to_json`](/docs/api/utilities/array#array-output) method
|
||||
generates row objects that can be assigned to the model.
|
||||
|
||||
Here is a sample flow diagram and method for downloading a workbook, generating
|
||||
rows from the first worksheet, and updating a UI5 `JSONModel`:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
url[(Remote\nFile)]
|
||||
ab[(Data\nArrayBuffer)]
|
||||
wb(SheetJS\nWorkbook)
|
||||
ws(SheetJS\nWorksheet)
|
||||
aoo(array of\nobjects)
|
||||
model((JSON\nModel))
|
||||
url --> |fetch\n\n| ab
|
||||
ab --> |read\n\n| wb
|
||||
wb --> |wb.Sheets\nselect sheet| ws
|
||||
ws --> |sheet_to_json\n\n| aoo
|
||||
aoo --> |setProperty\nfrom model| model
|
||||
linkStyle 1,2,3 color:blue,stroke:blue;
|
||||
```
|
||||
|
||||
```js title="Download workbook, extract data from first worksheet, and update JSONModel"
|
||||
_loadExcelFile: async function () {
|
||||
/* Download from https://docs.sheetjs.com/pres.xlsx */
|
||||
const f = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
|
||||
// highlight-start
|
||||
/* parse */
|
||||
const wb = XLSX.read(f); // parse the array buffer
|
||||
/* generate array of objects from first worksheet */
|
||||
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
|
||||
const data = XLSX.utils.sheet_to_json(ws); // generate objects
|
||||
|
||||
/* update JSONModel */
|
||||
this.getView().getModel().setProperty("/presidents", data);
|
||||
// highlight-end
|
||||
}
|
||||
```
|
||||
|
||||
#### Rendering Data {#json-render}
|
||||
|
||||
In UI5, the "Model-View-Controller"[^4] pattern is used to organize code and
|
||||
separate concerns. The view defines the UI structure, the controller handles the
|
||||
logic, and the model manages the data.
|
||||
|
||||
The following example uses the `Table` component[^5] to display data.
|
||||
|
||||
```xml title="Example View XML for displaying an array of objects"
|
||||
<mvc:View>
|
||||
<Page>
|
||||
<!-- The Table component binds to the presidents array -->
|
||||
<!-- highlight-next-line -->
|
||||
<Table width="300px" items="{/presidents}">
|
||||
<!-- Column definitions specify the table structure -->
|
||||
<columns>
|
||||
<Column><header><Text text="Name" /></header></Column>
|
||||
<Column><header><Text text="Value" /></header></Column>
|
||||
</columns>
|
||||
<!-- ColumnListItem template defines how each row should be rendered -->
|
||||
<!-- highlight-start -->
|
||||
<items>
|
||||
<ColumnListItem>
|
||||
<cells>
|
||||
<Text text="{Name}" />
|
||||
<Text text="{Index}" />
|
||||
</cells>
|
||||
</ColumnListItem>
|
||||
</items>
|
||||
<!-- highlight-end -->
|
||||
</Table>
|
||||
</Page>
|
||||
</mvc:View>
|
||||
```
|
||||
|
||||
#### Exporting Data {#json-export}
|
||||
|
||||
The `getProperty` method[^6] of the `JSONModel` pulls data from the UI5 model.
|
||||
If an array of objects was pushed with `setProperty`, the `getProperty` method
|
||||
will return an array of objects.
|
||||
|
||||
The SheetJS [`json_to_sheet`](/docs/api/utilities/array#array-of-objects-input)
|
||||
function will create a SheetJS worksheet object[^7] from the data in the array.
|
||||
The [`book_new`](/docs/api/utilities/wb) method will create a SheetJS workbook
|
||||
object that includes the new worksheet. [`writeFile`](/docs/api/write-options)
|
||||
will attempt to generate a file and initiate a download.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
state((State\nJSONModel))
|
||||
aoo[(Array of\nObjects)]
|
||||
ws(SheetJS\nWorksheet)
|
||||
wb(((SheetJS\nWorkbook)))
|
||||
file[(XLSX\nexport)]
|
||||
state --> |getProperty\n\n| aoo
|
||||
aoo --> |json_to_sheet\n\n| ws
|
||||
ws --> |book_new\n\n| wb
|
||||
wb --> |writeFile\n\n| file
|
||||
linkStyle 1,2,3 color:blue,stroke:blue;
|
||||
```
|
||||
|
||||
Here is a sample method for exporting data from the UI5 `JSONModel` to XLSX:
|
||||
|
||||
```js title="Fetch data from JSONModel and export to XLSX"
|
||||
/* get model data and export to XLSX */
|
||||
onExport: function () {
|
||||
const data = this.getView().getModel().getProperty("/presidents");
|
||||
/* generate worksheet from model data */
|
||||
// highlight-next-line
|
||||
const ws = XLSX.utils.json_to_sheet(data);
|
||||
/* create workbook and append worksheet */
|
||||
const wb = XLSX.utils.book_new(ws, "Data");
|
||||
/* export to XLSX */
|
||||
XLSX.writeFileXLSX(wb, "SheetJSOpenUI5AoO.xlsx");
|
||||
}
|
||||
```
|
||||
|
||||
This method can be bound to the `press` event of a `sap.m.Button` control:
|
||||
|
||||
```xml title="Example View XML for exporting an array of objects to a workbook"
|
||||
<mvc:View>
|
||||
<Page>
|
||||
<!-- The `onExport` method is bound to the `press` event -->
|
||||
<Button text="Export Data" press=".onExport" />
|
||||
</Page>
|
||||
</mvc:View>
|
||||
```
|
||||
|
||||
#### Complete Component
|
||||
|
||||
This complete component example fetches a test file and displays the contents in a table.
|
||||
When the export button is clicked, an event handler will export a file:
|
||||
|
||||
##### View Implementation {#view-implementation}
|
||||
|
||||
```xml title="webapp/view/Main.view.xml"
|
||||
<mvc:View controllerName="sheetjs.openui5.controller.Main" displayBlock="true" xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc" xmlns:core="sap.ui.core" core:require="{formatter: 'sheetjs/openui5/model/formatter'}">
|
||||
<Page>
|
||||
<VBox width="auto" alignItems="Start">
|
||||
<Table width="300px" items="{/presidents}">
|
||||
<columns>
|
||||
<Column><header><Text text="Name" /></header></Column>
|
||||
<Column><header><Text text="Index" /></header></Column>
|
||||
</columns>
|
||||
<items><ColumnListItem><cells>
|
||||
<Text text="{Name}" />
|
||||
<Text text="{Index}" />
|
||||
</cells></ColumnListItem></items>
|
||||
</Table>
|
||||
<Button text="Export XLSX" press=".onExport" />
|
||||
</VBox>
|
||||
</Page>
|
||||
</mvc:View>
|
||||
```
|
||||
|
||||
##### Controller Implementation {#controller-implementation}
|
||||
|
||||
```js title="webapp/controller/Main.controller.js"
|
||||
sap.ui.define(
|
||||
["./BaseController", "sap/ui/model/json/JSONModel"],
|
||||
function (BaseController, JSONModel) {
|
||||
"use strict";
|
||||
|
||||
return BaseController.extend("com.demo.xlsx.controller.Main", {
|
||||
onInit: function () {
|
||||
/* initialize model */
|
||||
const oModel = new JSONModel({ presidents: [] });
|
||||
this.getView().setModel(oModel);
|
||||
/* load data */
|
||||
this._loadExcelFile();
|
||||
},
|
||||
|
||||
_loadExcelFile: async function () {
|
||||
/* fetch and parse file */
|
||||
const f = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
const wb = XLSX.read(f);
|
||||
/* extract data from first worksheet */
|
||||
const ws = wb.Sheets[wb.SheetNames[0]];
|
||||
const data = XLSX.utils.sheet_to_json(ws);
|
||||
/* update state model */
|
||||
this.getView().getModel().setProperty("/presidents", data);
|
||||
},
|
||||
|
||||
onExport: function () {
|
||||
/* fetch data from model */
|
||||
const data = this.getView().getModel().getProperty("/presidents");
|
||||
/* generate workbook */
|
||||
const ws = XLSX.utils.json_to_sheet(data);
|
||||
const wb = XLSX.utils.book_new(ws, "Data");
|
||||
/* export to XLSX */
|
||||
XLSX.writeFileXLSX(wb, "SheetJSOpenUI5AoO.xlsx");
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
<details open>
|
||||
<summary><b>How to run the example</b> (click to hide)</summary>
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| OpenUI5 | Date |
|
||||
|:----------|------------|
|
||||
| `1.132.1` | 2025-01-24 |
|
||||
|
||||
:::
|
||||
|
||||
1) Create a new site:
|
||||
|
||||
```bash
|
||||
npm i --global generator-easy-ui5
|
||||
npx yo easy-ui5 app
|
||||
```
|
||||
|
||||
When prompted, enter the following options:
|
||||
|
||||
- `Enter your application id (namespace)?`: Type `sheetjs.openui5` and press <kbd>Enter</kbd>
|
||||
- `Which framework do you want to use?`: Press <kbd>Enter</kbd> (`OpenUI5` should be the default)
|
||||
- `Which framework version do you want to use?`: Type `1.132.1` and press <kbd>Enter</kbd>
|
||||
- `Who is the author of the application?`: Press <kbd>Enter</kbd> (use the default author)
|
||||
- `Would you like to create a new directory for the application?`: Type `Y` and press <kbd>Enter</kbd>
|
||||
- `Would you like to initialize a local git repository for the application?`: Type `N` and press <kbd>Enter</kbd>
|
||||
|
||||

|
||||
|
||||
2) Install the dependencies and start server:
|
||||
|
||||
```bash
|
||||
cd sheetjs.openui5
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
|
||||
3) Open a web browser and access the displayed URL (`http://localhost:8080`).
|
||||
|
||||
In the file listing, click `index.html` to launch the app.
|
||||
|
||||
4) Add the SheetJS Standalone script to `webapp/index.html` after the `title` tag:
|
||||
|
||||
<CodeBlock language="html" value="html" title="webapp/index.html (add highlighted lines)">{`\
|
||||
<title>UI5 Application: sheetjs.openui5</title>
|
||||
<!-- highlight-next-line -->
|
||||
<script src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>`}
|
||||
</CodeBlock>
|
||||
|
||||
5) Replace `webapp/view/Main.view.xml` with the `Main.view.xml` snippet in the
|
||||
["View Implementation" section](#view-implementation).
|
||||
|
||||
6) Replace `webapp/controller/Main.controller.js` with the `Main.controller.js`
|
||||
example in the ["Controller Implementation" section](#controller-implementation).
|
||||
|
||||
7) Switch back to the browser window.
|
||||
|
||||
The page will refresh and show a table with an Export button.
|
||||
|
||||
Click the button and the page will attempt to download `SheetJSOpenUI5AoO.xlsx`.
|
||||
This file can be inspected with a spreadsheet editor.
|
||||
|
||||
8) Build the site:
|
||||
|
||||
```bash
|
||||
npm run build:opt
|
||||
```
|
||||
|
||||
The generated site will be placed in the `dist` folder.
|
||||
|
||||
:::caution pass
|
||||
|
||||
SAP recommends `npm run build`. This does not generate a proper standalone site!
|
||||
Sites built with `npm run build` must be served with `npm run start:dist`.
|
||||
|
||||
This demo uses the `build:opt` target to ensure that a proper static site is
|
||||
generated. The `dist` folder in this demo can be deployed on a static host.
|
||||
|
||||
:::
|
||||
|
||||
9) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
and test the page.
|
||||
|
||||
When the page loads, the app will fetch https://docs.sheetjs.com/pres.xlsx and
|
||||
display the data from the first worksheet in a TABLE. The "Export XLSX" button
|
||||
will generate a workbook that can be opened in a spreadsheet editor.
|
||||
|
||||
</details>
|
||||
|
||||
### HTML
|
||||
|
||||
The main disadvantage of the Array of Objects approach is the specific nature
|
||||
of the columns. For more general use, passing around an Array of Arrays works.
|
||||
However, this does not handle merge cells[^8] well!
|
||||
|
||||
The [`sheet_to_html`](/docs/api/utilities/html#html-table-output) function
|
||||
generates HTML that is aware of merges and other worksheet features.
|
||||
|
||||
To render the HTML string from the model, the property from the model should be
|
||||
bound to the `content` property of a UI5 `core:HTML`[^9] control.
|
||||
|
||||
On export, the [`table_to_book`](/docs/api/utilities/html#html-table-input)
|
||||
method creates a SheetJS workbook object from the rendered HTML table.
|
||||
|
||||
##### View Implementation {#view-implementation-html}
|
||||
|
||||
```xml title="webapp/view/Main.view.xml"
|
||||
<mvc:View controllerName="sheetjs.openui5.controller.Main" displayBlock="true" xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc" xmlns:core="sap.ui.core" xmlns:html="http://www.w3.org/1999/xhtml">
|
||||
<Page>
|
||||
<content>
|
||||
<core:HTML id="tbl" content="{/tableHTML}" />
|
||||
<Button text="Export XLSX" press=".onExport"/>
|
||||
</content>
|
||||
</Page>
|
||||
</mvc:View>
|
||||
```
|
||||
|
||||
##### Controller Implementation {#controller-implementation-html}
|
||||
|
||||
```js title="webapp/controller/Main.controller.js"
|
||||
sap.ui.define([
|
||||
"sap/ui/core/mvc/Controller",
|
||||
"sap/ui/model/json/JSONModel"
|
||||
], function (Controller, JSONModel) {
|
||||
"use strict";
|
||||
|
||||
return Controller.extend("sheetjs.openui5.controller.Main", {
|
||||
onInit: function () {
|
||||
/* the component state is an HTML string */
|
||||
const oModel = new JSONModel({ tableHTML: "", });
|
||||
this.getView().setModel(oModel);
|
||||
/* load data */
|
||||
this._loadExcelFile();
|
||||
},
|
||||
|
||||
_loadExcelFile: async function () {
|
||||
/* fetch and parse file */
|
||||
const f = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
const wb = XLSX.read(f);
|
||||
/* generate HTML table from first worksheet */
|
||||
const ws = wb.Sheets[wb.SheetNames[0]];
|
||||
const opts = { header: `<table>`, footer: `</table>` };
|
||||
const tableHTML = XLSX.utils.sheet_to_html(ws, opts);
|
||||
/* update state model */
|
||||
this.getView().getModel().setProperty("/tableHTML", tableHTML);
|
||||
},
|
||||
|
||||
onExport: function () {
|
||||
/* Get reference to the `TABLE` element in the model */
|
||||
const table = this.getView().byId("tbl").getDomRef();
|
||||
|
||||
/* Generate workbook */
|
||||
const wb = XLSX.utils.table_to_book(table);
|
||||
|
||||
/* Export to XLSX */
|
||||
XLSX.writeFileXLSX(wb, "SheetJSOpenUI5HTML.xlsx");
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
<details open>
|
||||
<summary><b>How to run the example</b> (click to hide)</summary>
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| OpenUI5 | Date |
|
||||
|:----------|------------|
|
||||
| `1.132.1` | 2025-01-24 |
|
||||
|
||||
:::
|
||||
|
||||
1) Create a new site:
|
||||
|
||||
```bash
|
||||
npm i --global generator-easy-ui5
|
||||
npx yo easy-ui5 app
|
||||
```
|
||||
|
||||
When prompted, enter the following options:
|
||||
|
||||
- `Enter your application id (namespace)?`: Type `sheetjs.openui5` and press <kbd>Enter</kbd>
|
||||
- `Which framework do you want to use?`: Press <kbd>Enter</kbd> (`OpenUI5` should be the default)
|
||||
- `Which framework version do you want to use?`: Type `1.132.1` and press <kbd>Enter</kbd>
|
||||
- `Who is the author of the application?`: Press <kbd>Enter</kbd> (use the default author)
|
||||
- `Would you like to create a new directory for the application?`: Type `Y` and press <kbd>Enter</kbd>
|
||||
- `Would you like to initialize a local git repository for the application?`: Type `N` and press <kbd>Enter</kbd>
|
||||
|
||||

|
||||
|
||||
2) Install the dependencies and start server:
|
||||
|
||||
```bash
|
||||
cd sheetjs.openui5
|
||||
npm install
|
||||
npm start
|
||||
````
|
||||
|
||||
3) Open a web browser and access the displayed URL (`http://localhost:8080`)
|
||||
|
||||
In the file listing, click `index.html` to launch the app.
|
||||
|
||||
4) Add the SheetJS Standalone script to `webapp/index.html` after the `title` tag:
|
||||
|
||||
<CodeBlock language="html" value="html" title="webapp/index.html (add highlighted lines)">{`\
|
||||
<title>UI5 Application: sheetjs.openui5</title>
|
||||
<!-- highlight-next-line -->
|
||||
<script src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>`}
|
||||
</CodeBlock>
|
||||
|
||||
5) Replace `webapp/view/Main.view.xml` with the `Main.view.xml` snippet in the
|
||||
["View Implementation" section](#view-implementation-html).
|
||||
|
||||
6) Replace `webapp/controller/Main.controller.js` with the `Main.controller.js`
|
||||
example in ["Controller Implementation"](#controller-implementation-html).
|
||||
|
||||
7) Switch back to the browser window.
|
||||
|
||||
The page will refresh and show a table with an Export button.
|
||||
|
||||
Click the button and the page will attempt to download `SheetJSOpenUI5HTML.xlsx`.
|
||||
This file can be inspected with a spreadsheet editor.
|
||||
|
||||
8) Build the site:
|
||||
|
||||
```bash
|
||||
npm run build:opt
|
||||
```
|
||||
|
||||
The generated site will be placed in the `dist` folder.
|
||||
|
||||
:::caution pass
|
||||
|
||||
SAP recommends `npm run build`. This does not generate a proper standalone site!
|
||||
Sites built with `npm run build` must be served with `npm run start:dist`.
|
||||
|
||||
This demo uses the `build:opt` target to ensure that a proper static site is
|
||||
generated. The `dist` folder in this demo can be deployed on a static host.
|
||||
|
||||
:::
|
||||
|
||||
9) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
and test the page.
|
||||
|
||||
When the page loads, the app will fetch https://docs.sheetjs.com/pres.xlsx and
|
||||
display the data from the first worksheet in a TABLE. The "Export XLSX" button
|
||||
will generate a workbook that can be opened in a spreadsheet editor.
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
[^1]: See ["ECMAScript Support"](https://sdk.openui5.org/topic/0cb44d7a147640a0890cefa5fd7c7f8e.html#loio0cb44d7a147640a0890cefa5fd7c7f8e/section_UI5Mod) for more details about OpenUI5 compatibility.
|
||||
[^2]: See [`JSONModel`](https://sdk.openui5.org/1.38.62/docs/api/symbols/sap.ui.model.json.JSONModel.html) in the OpenUI5 documentation.
|
||||
[^3]: See ["SheetJS Data Model"](/docs/csf/)
|
||||
[^4]: See [OpenUI5's MVC Documentation](https://sdk.openui5.org/topic/91f233476f4d1014b6dd926db0e91070) for detailed explanation of the pattern implementation.
|
||||
[^5]: See ["List, List Item, and Table"](https://sdk.openui5.org/topic/295e44b2d0144318bcb7bdd56bfa5189) in the OpenUI5 documentation.
|
||||
[^6]: See [`getProperty` of class `sap.ui.model.json.JSONModel`](https://sdk.openui5.org/api/sap.ui.model.json.JSONModel#methods/getProperty) in the OpenUI5 documentation.
|
||||
[^7]: See ["Worksheet Object" in "SheetJS Data Model"](/docs/csf/sheet) for more details.
|
||||
[^8]: See ["Merged Cells" in "SheetJS Data Model"](/docs/csf/features/merges) for more details.
|
||||
[^9]: See [`core:HTML`](https://sdk.openui5.org/1.38.62/docs/api/symbols/sap.ui.core.HTML.html) in the OpenUI5 documentation.
|
@ -41,10 +41,9 @@ This demo was tested in the following environments:
|
||||
|
||||
| ViteJS | Date |
|
||||
|:---------|:-----------|
|
||||
| `6.2.3` | 2025-03-30 |
|
||||
| `5.4.15` | 2025-03-30 |
|
||||
| `4.5.10` | 2025-03-30 |
|
||||
| `3.2.11` | 2025-03-30 |
|
||||
| `5.2.10` | 2024-04-27 |
|
||||
| `4.5.3` | 2024-04-27 |
|
||||
| `3.2.10` | 2024-04-27 |
|
||||
|
||||
:::
|
||||
|
||||
@ -144,24 +143,6 @@ writeFileXLSX(workbook, "Presidents.xlsx");
|
||||
npm run dev
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
When this was last tested in ViteJS version 4, the process crashed with the error
|
||||
|
||||
```
|
||||
Search string not found: "for (const existingRoot of buildInfoVersionMap.roots) {"
|
||||
```
|
||||
|
||||
**This is a known issue with ViteJS 4 and its dependency tree!**[^1]
|
||||
|
||||
The recommended workaround is to forcefully upgrade `vue-tsc`:
|
||||
|
||||
```bash
|
||||
npm i "vue-tsc@2"
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
5) Open a web browser to `http://localhost:5173/` and click the export button.
|
||||
|
||||
6) Build the production site:
|
||||
@ -178,5 +159,3 @@ npx http-server dist
|
||||
|
||||
8) Access the displayed URL (typically `http://localhost:8080`) in a web browser
|
||||
and click the export button.
|
||||
|
||||
[^1]: See [issue 4484 in the `vuejs/language-tools` repository on GitHub](https://github.com/vuejs/language-tools/issues/4484) for the issue triage and workaround. This issue does not affect other ViteJS major versions.
|
@ -47,22 +47,19 @@ This demo was tested in the following environments:
|
||||
|
||||
| ESBuild | Date |
|
||||
|:----------|:-----------|
|
||||
| `0.24.2` | 2025-01-07 |
|
||||
| `0.23.1` | 2025-01-07 |
|
||||
| `0.22.0` | 2025-01-07 |
|
||||
| `0.21.5` | 2025-01-07 |
|
||||
| `0.20.2` | 2025-01-07 |
|
||||
| `0.19.12` | 2025-01-07 |
|
||||
| `0.18.20` | 2025-01-07 |
|
||||
| `0.17.19` | 2025-01-07 |
|
||||
| `0.16.17` | 2025-01-07 |
|
||||
| `0.15.18` | 2025-01-07 |
|
||||
| `0.14.54` | 2025-01-07 |
|
||||
| `0.13.15` | 2025-01-07 |
|
||||
| `0.12.29` | 2025-01-07 |
|
||||
| `0.11.23` | 2025-01-07 |
|
||||
| `0.10.2` | 2025-01-07 |
|
||||
| `0.9.7` | 2025-01-07 |
|
||||
| `0.21.4` | 2024-06-07 |
|
||||
| `0.20.2` | 2024-06-07 |
|
||||
| `0.19.12` | 2024-06-07 |
|
||||
| `0.18.20` | 2024-06-07 |
|
||||
| `0.17.19` | 2024-06-07 |
|
||||
| `0.16.17` | 2024-06-07 |
|
||||
| `0.15.18` | 2024-06-07 |
|
||||
| `0.14.54` | 2024-06-07 |
|
||||
| `0.13.15` | 2024-06-07 |
|
||||
| `0.12.29` | 2024-06-07 |
|
||||
| `0.11.23` | 2024-06-07 |
|
||||
| `0.10.2` | 2024-06-07 |
|
||||
| `0.9.7` | 2024-06-07 |
|
||||
|
||||
:::
|
||||
|
||||
@ -90,7 +87,7 @@ Assuming the primary source file is `in.js`, the following command will bundle
|
||||
the script and generate `out.js`:
|
||||
|
||||
```bash
|
||||
npx -y esbuild@0.24.2 in.js --bundle --outfile=out.js
|
||||
npx -y esbuild@0.19.8 in.js --bundle --outfile=out.js
|
||||
```
|
||||
|
||||
### Browser Demo
|
||||
|
@ -41,10 +41,10 @@ This demo was tested in the following environments:
|
||||
|
||||
| Version | Date | Required Workarounds |
|
||||
|:---------|:-----------|:------------------------------------|
|
||||
| `5.97.1` | 2025-01-03 | |
|
||||
| `4.47.0` | 2025-01-03 | |
|
||||
| `3.12.0` | 2025-01-03 | Import `xlsx/dist/xlsx.full.min.js` |
|
||||
| `2.7.0` | 2025-01-03 | Import `xlsx/dist/xlsx.full.min.js` |
|
||||
| `2.7.0` | 2024-03-16 | Import `xlsx/dist/xlsx.full.min.js` |
|
||||
| `3.12.0` | 2024-03-16 | Import `xlsx/dist/xlsx.full.min.js` |
|
||||
| `4.47.0` | 2024-03-16 | Downgrade NodeJS (tested v16.20.2) |
|
||||
| `5.90.3` | 2024-03-16 | |
|
||||
|
||||
:::
|
||||
|
||||
@ -269,16 +269,15 @@ version above 4.0 can be pinned by locally installing webpack and the CLI tool.
|
||||
|
||||
**Webpack 4.x**
|
||||
|
||||
:::note pass
|
||||
:::info pass
|
||||
|
||||
Some Webpack 4 versions are incompatible with Node 18+. They will elicit the
|
||||
following error:
|
||||
Webpack 4 is incompatible with Node 18+. It will elicit the following error:
|
||||
|
||||
```
|
||||
Error: error:0308010C:digital envelope routines::unsupported
|
||||
```
|
||||
|
||||
In some demo tests, NodeJS was locally downgraded to 16.20.2
|
||||
When this demo was last tested, NodeJS was locally downgraded to 16.20.2
|
||||
|
||||
:::
|
||||
|
||||
|
@ -34,21 +34,21 @@ This demo was tested in the following environments:
|
||||
|
||||
| Browserify | Date |
|
||||
|:-----------|:-----------|
|
||||
| `17.0.1` | 2025-01-07 |
|
||||
| `16.5.2` | 2025-01-07 |
|
||||
| `15.2.0` | 2025-01-07 |
|
||||
| `14.5.0` | 2025-01-07 |
|
||||
| `13.3.0` | 2025-01-07 |
|
||||
| `12.0.2` | 2025-01-07 |
|
||||
| `11.2.0` | 2025-01-07 |
|
||||
| `10.2.6` | 2025-01-07 |
|
||||
| `9.0.8` | 2025-01-07 |
|
||||
| `8.1.3` | 2025-01-07 |
|
||||
| `7.1.0` | 2025-01-07 |
|
||||
| `6.3.4` | 2025-01-07 |
|
||||
| `5.13.1` | 2025-01-07 |
|
||||
| `4.2.3` | 2025-01-07 |
|
||||
| `3.46.1` | 2025-01-07 |
|
||||
| `17.0.0` | 2024-04-13 |
|
||||
| `16.5.2` | 2024-04-13 |
|
||||
| `15.2.0` | 2024-04-13 |
|
||||
| `14.5.0` | 2024-04-13 |
|
||||
| `13.3.0` | 2024-04-13 |
|
||||
| `12.0.2` | 2024-04-13 |
|
||||
| `11.2.0` | 2024-04-13 |
|
||||
| `10.2.6` | 2024-04-13 |
|
||||
| `9.0.8` | 2024-04-13 |
|
||||
| `8.1.3` | 2024-04-13 |
|
||||
| `7.1.0` | 2024-04-13 |
|
||||
| `6.3.4` | 2024-04-13 |
|
||||
| `5.13.1` | 2024-04-13 |
|
||||
| `4.2.3` | 2024-04-13 |
|
||||
| `3.46.1` | 2024-04-13 |
|
||||
|
||||
:::
|
||||
|
||||
|
@ -40,8 +40,8 @@ This demo was tested in the following environments:
|
||||
|
||||
| RequireJS | Date |
|
||||
|:----------|:-----------|
|
||||
| `2.3.7` | 2025-01-07 |
|
||||
| `2.1.22` | 2025-01-07 |
|
||||
| `2.3.6` | 2024-04-27 |
|
||||
| `2.1.22` | 2024-04-27 |
|
||||
|
||||
:::
|
||||
|
||||
@ -51,7 +51,7 @@ The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone)
|
||||
comply with AMD `define` semantics. They support RequireJS and the `r.js`
|
||||
optimizer out of the box.
|
||||
|
||||
### Configuration
|
||||
### Config
|
||||
|
||||
The RequireJS config should set the `xlsx` alias in the `paths` property.
|
||||
|
||||
|
@ -46,11 +46,11 @@ This demo was tested in the following environments:
|
||||
|
||||
| Version | Platform | Date |
|
||||
|:----------|:---------|:-----------|
|
||||
| `0.19.47` | NodeJS | 2025-01-03 |
|
||||
| `0.20.16` | Browser | 2025-01-03 |
|
||||
| `0.20.19` | NodeJS | 2025-03-03 |
|
||||
| `0.21.6` | NodeJS | 2025-03-03 |
|
||||
| `6.15.1` | NodeJS | 2025-01-03 |
|
||||
| `0.19.47` | NodeJS | 2024-03-31 |
|
||||
| `0.20.16` | Browser | 2024-03-31 |
|
||||
| `0.20.19` | NodeJS | 2024-03-31 |
|
||||
| `0.21.6` | NodeJS | 2024-03-31 |
|
||||
| `6.14.3` | NodeJS | 2024-03-31 |
|
||||
|
||||
:::
|
||||
|
||||
@ -203,7 +203,7 @@ npm init -y
|
||||
1) Install the dependencies:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz systemjs@6.15.1`}
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz systemjs@6.14.3`}
|
||||
</CodeBlock>
|
||||
|
||||
2) Download [`SheetJSystem.js`](pathname:///systemjs/SheetJSystem.js) and move
|
||||
|
@ -34,10 +34,10 @@ This demo was tested in the following environments:
|
||||
|
||||
| Version | Date |
|
||||
|:---------|:-----------|
|
||||
| `4.29.1` | 2025-01-03 |
|
||||
| `3.29.5` | 2025-01-03 |
|
||||
| `2.79.1` | 2025-01-03 |
|
||||
| `1.32.1` | 2025-01-03 |
|
||||
| `4.13.0` | 2024-03-25 |
|
||||
| `3.29.4` | 2024-03-25 |
|
||||
| `2.79.1` | 2024-03-25 |
|
||||
| `1.32.1` | 2024-03-25 |
|
||||
|
||||
:::
|
||||
|
||||
|
@ -34,8 +34,8 @@ This demo was tested in the following environments:
|
||||
|
||||
| Version | Date |
|
||||
|:---------|:-----------|
|
||||
| `2.13.3` | 2024-12-31 |
|
||||
| `1.12.3` | 2024-12-31 |
|
||||
| `2.12.0` | 2024-06-08 |
|
||||
| `1.12.4` | 2024-06-08 |
|
||||
|
||||
:::
|
||||
|
||||
|
@ -35,7 +35,7 @@ This demo was tested in the following environments:
|
||||
|
||||
| Version | Date |
|
||||
|:----------|:-----------|
|
||||
| `1.2.246` | 2025-01-07 |
|
||||
| `1.2.246` | 2024-04-27 |
|
||||
|
||||
:::
|
||||
|
||||
@ -73,7 +73,7 @@ thread '<unnamed>' panicked at 'cannot access a scoped thread local variable wit
|
||||
|
||||
**This is a bug in SWC**
|
||||
|
||||
This bug is known to affect versions `1.3.100`, `1.4.17`, and `1.10.6`.
|
||||
This bug is known to affect versions `1.3.100` and `1.4.17`.
|
||||
|
||||
Until the bug is fixed, it is strongly recommended to use `@swc/core@1.2.246`.
|
||||
|
||||
|
@ -62,7 +62,7 @@ This demo was tested in the following environments:
|
||||
|
||||
| Version | Date |
|
||||
|:--------|:-----------|
|
||||
| `3.8.8` | 2025-01-07 |
|
||||
| `3.8.8` | 2024-04-14 |
|
||||
|
||||
:::
|
||||
|
||||
@ -161,14 +161,12 @@ Unlike other bundlers, Snowpack requires a full page including `HEAD` element.
|
||||
npx snowpack@3.8.8 build
|
||||
```
|
||||
|
||||
5) Start a local HTTP server:
|
||||
5) Start a local HTTP server, then go to `http://localhost:8080/`
|
||||
|
||||
```bash
|
||||
npx http-server build/
|
||||
```
|
||||
|
||||
6) Open a web browser to the displayed URL (typically `http://localhost:8080/`).
|
||||
|
||||
Click on "Click here to export" to generate a file.
|
||||
|
||||
</details>
|
||||
@ -194,7 +192,7 @@ This demo was tested in the following environments:
|
||||
|
||||
| Version | Date |
|
||||
|:--------|:-----------|
|
||||
| `3.8.0` | 2025-01-07 |
|
||||
| `3.8.0` | 2024-04-14 |
|
||||
|
||||
:::
|
||||
|
||||
@ -287,14 +285,12 @@ writeFileXLSX(workbook, "Presidents.xlsx");
|
||||
npx wmr@3.8.0 build
|
||||
```
|
||||
|
||||
5) Start a local HTTP server:
|
||||
5) Start a local HTTP server in `dist` folder and go to `http://localhost:8080/`
|
||||
|
||||
```bash
|
||||
npx http-server dist/
|
||||
```
|
||||
|
||||
6) Open a web browser to the displayed URL (typically `http://localhost:8080/`).
|
||||
|
||||
Click on "Click here to export" to generate a file.
|
||||
|
||||
</details>
|
||||
|
@ -88,11 +88,10 @@ first worksheet. The SheetJS `sheet_to_html` method[^2] creates the HTML table.
|
||||
|
||||
Each browser demo was tested in the following environments:
|
||||
|
||||
| Browser | Date |
|
||||
|:-------------|:-----------|
|
||||
| Chromium 133 | 2025-03-30 |
|
||||
| Safari 18.3 | 2025-03-30 |
|
||||
| Konqueror 22 | 2025-04-23 |
|
||||
| Browser | Date |
|
||||
|:------------|:-----------|
|
||||
| Chrome 126 | 2024-06-19 |
|
||||
| Safari 17.3 | 2024-06-19 |
|
||||
|
||||
:::
|
||||
|
||||
@ -403,18 +402,18 @@ This demo was tested in the following environments:
|
||||
|
||||
| NodeJS | Date | Workarounds |
|
||||
|:-----------|:-----------|:-------------------------------|
|
||||
| `0.10.48` | 2025-03-31 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `0.12.18` | 2025-03-31 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `4.9.1` | 2025-03-31 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `6.17.1` | 2025-03-31 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `8.17.0` | 2025-03-31 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `10.24.1` | 2025-03-31 | |
|
||||
| `12.22.12` | 2025-03-31 | |
|
||||
| `14.21.3` | 2025-03-31 | |
|
||||
| `16.20.2` | 2025-03-31 | |
|
||||
| `18.20.8` | 2025-03-31 | |
|
||||
| `20.19.0` | 2025-03-31 | |
|
||||
| `22.14.0` | 2025-03-31 | |
|
||||
| `0.10.48` | 2024-06-21 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `0.12.18` | 2024-06-21 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `4.9.1` | 2024-06-21 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `6.17.1` | 2024-06-21 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `8.17.0` | 2024-06-21 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `10.24.1` | 2024-06-21 | |
|
||||
| `12.22.12` | 2024-06-21 | |
|
||||
| `14.21.3` | 2024-06-21 | |
|
||||
| `16.20.2` | 2024-06-21 | |
|
||||
| `18.20.3` | 2024-06-21 | |
|
||||
| `20.15.0` | 2024-06-21 | |
|
||||
| `22.3.0` | 2024-06-21 | |
|
||||
|
||||
The `NODE_TLS_REJECT_UNAUTHORIZED` workaround sets the value to `'0'`:
|
||||
|
||||
@ -445,7 +444,7 @@ If successful, the script will print CSV contents of the test file.
|
||||
|
||||
:::caution pass
|
||||
|
||||
For older versions of NodeJS, the script may fail due to a certificate error.
|
||||
For older versions of NodeJS, the script will fail due to a certificate error.
|
||||
The error can be suppressed by prepending the following line to the script:
|
||||
|
||||
```js title="SheetJSHTTPSGet.js (add to top)"
|
||||
@ -485,9 +484,9 @@ This demo was tested in the following environments:
|
||||
|
||||
| NodeJS | Date |
|
||||
|:-----------|:-----------|
|
||||
| `18.20.8` | 2025-03-30 |
|
||||
| `20.18.0` | 2025-03-30 |
|
||||
| `22.14.0` | 2025-03-30 |
|
||||
| `18.20.3` | 2024-06-21 |
|
||||
| `20.15.0` | 2024-06-21 |
|
||||
| `22.3.0` | 2024-06-21 |
|
||||
|
||||
:::
|
||||
|
||||
@ -569,20 +568,20 @@ request(url, {encoding: null}, function(err, res, data) {
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| NodeJS | `request` | Date | Workarounds |
|
||||
|:-----------|:----------|:-----------|:-------------------------------|
|
||||
| `0.10.48` | `2.22.0` | 2025-03-31 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `0.12.18` | `2.22.0` | 2025-03-31 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `4.9.1` | `2.22.0` | 2025-03-31 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `6.17.1` | `2.88.2` | 2025-03-31 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `8.17.0` | `2.88.2` | 2025-03-31 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `10.24.1` | `2.88.2` | 2025-03-31 | |
|
||||
| `12.22.12` | `2.88.2` | 2025-03-31 | |
|
||||
| `14.21.3` | `2.88.2` | 2025-03-31 | |
|
||||
| `16.20.2` | `2.88.2` | 2025-03-31 | |
|
||||
| `18.20.8` | `2.88.2` | 2025-03-31 | |
|
||||
| `20.19.0` | `2.88.2` | 2025-03-31 | |
|
||||
| `22.14.0` | `2.88.2` | 2025-03-31 | |
|
||||
| NodeJS | Date | Workarounds |
|
||||
|:-----------|:-----------|:-------------------------------|
|
||||
| `0.10.48` | 2024-06-21 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `0.12.18` | 2024-06-21 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `4.9.1` | 2024-06-21 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `6.17.1` | 2024-06-21 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `8.17.0` | 2024-06-21 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `10.24.1` | 2024-06-21 | |
|
||||
| `12.22.12` | 2024-06-21 | |
|
||||
| `14.21.3` | 2024-06-21 | |
|
||||
| `16.20.2` | 2024-06-21 | |
|
||||
| `18.20.3` | 2024-06-21 | |
|
||||
| `20.15.0` | 2024-06-21 | |
|
||||
| `22.3.0` | 2024-06-21 | |
|
||||
|
||||
The `NODE_TLS_REJECT_UNAUTHORIZED` workaround sets the value to `'0'`:
|
||||
|
||||
@ -613,7 +612,7 @@ If successful, the script will print CSV contents of the test file.
|
||||
|
||||
:::caution pass
|
||||
|
||||
For older versions of NodeJS, the script may fail due to a certificate error.
|
||||
For older versions of NodeJS, the script will fail due to a certificate error.
|
||||
The error can be suppressed by prepending the following line to the script:
|
||||
|
||||
```js title="SheetJSRequest.js (add to top)"
|
||||
@ -648,16 +647,13 @@ This demo was tested in the following environments:
|
||||
|
||||
| NodeJS | Axios | Date |
|
||||
|:-----------|:-------|:-----------|
|
||||
| `4.9.1` | 0.22.0 | 2025-03-31 |
|
||||
| `6.17.1` | 0.22.0 | 2025-03-31 |
|
||||
| `8.17.0` | 0.28.1 | 2025-03-31 |
|
||||
| `10.24.1` | 0.28.1 | 2025-03-31 |
|
||||
| `12.22.12` | 1.8.4 | 2025-03-31 |
|
||||
| `14.21.3` | 1.8.4 | 2025-03-31 |
|
||||
| `16.20.2` | 1.8.4 | 2025-03-31 |
|
||||
| `18.20.8` | 1.8.4 | 2025-03-31 |
|
||||
| `20.19.0` | 1.8.4 | 2025-03-31 |
|
||||
| `22.14.0` | 1.8.4 | 2025-03-31 |
|
||||
| `10.24.1` | 0.28.1 | 2024-06-21 |
|
||||
| `12.22.12` | 1.7.2 | 2024-06-21 |
|
||||
| `14.21.3` | 1.7.2 | 2024-06-21 |
|
||||
| `16.20.2` | 1.7.2 | 2024-06-21 |
|
||||
| `18.20.3` | 1.7.2 | 2024-06-21 |
|
||||
| `20.15.0` | 1.7.2 | 2024-06-21 |
|
||||
| `22.3.0` | 1.7.2 | 2024-06-21 |
|
||||
|
||||
:::
|
||||
|
||||
@ -667,7 +663,7 @@ This demo was tested in the following environments:
|
||||
1) Install the [NodeJS module](/docs/getting-started/installation/nodejs)
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz axios@1.8.4`}
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz axios@1.7.2`}
|
||||
</CodeBlock>
|
||||
|
||||
2) Save the following to `SheetJSAxios.js`:
|
||||
@ -698,27 +694,6 @@ node SheetJSAxios.js
|
||||
|
||||
If successful, the script will print CSV contents of the test file.
|
||||
|
||||
:::caution pass
|
||||
|
||||
Legacy NodeJS versions do not support `async` functions. The `async` function
|
||||
must be manually translated to a `then` chain:
|
||||
|
||||
```js title="SheetJSAxios.js (ES5)"
|
||||
const XLSX = require("xlsx"), axios = require("axios");
|
||||
|
||||
var url = 'https://docs.sheetjs.com/pres.numbers';
|
||||
|
||||
axios(url, {responseType:'arraybuffer'}).then(function(res) {
|
||||
/* at this point, res.data is a Buffer */
|
||||
var wb = XLSX.read(res.data, {type: "buffer"});
|
||||
/* print the first worksheet to console */
|
||||
var ws = wb.Sheets[wb.SheetNames[0]];
|
||||
console.log(XLSX.utils.sheet_to_csv(ws));
|
||||
});
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
</details>
|
||||
|
||||
## Other Platforms
|
@ -133,11 +133,10 @@ response. If the process was successful, a HTML table will be displayed
|
||||
|
||||
Each browser demo was tested in the following environments:
|
||||
|
||||
| Browser | Date |
|
||||
|:-------------|:-----------|
|
||||
| Chromium 133 | 2025-03-30 |
|
||||
| Safari 18.3 | 2025-03-30 |
|
||||
| Konqueror 22 | 2025-04-23 |
|
||||
| Browser | Date |
|
||||
|:------------|:-----------|
|
||||
| Chrome 126 | 2024-06-19 |
|
||||
| Safari 17.3 | 2024-06-19 |
|
||||
|
||||
:::
|
||||
|
||||
@ -579,7 +578,7 @@ function SheetJSSuperAgentUL() {
|
||||
|
||||
## NodeJS Demos
|
||||
|
||||
These examples show how to upload data in NodeJS scripts.
|
||||
These examples show how to upload data in NodeJS.
|
||||
|
||||
### fetch
|
||||
|
||||
@ -591,8 +590,8 @@ This demo was tested in the following environments:
|
||||
|
||||
| NodeJS | Date |
|
||||
|:-----------|:-----------|
|
||||
| `22.13.0` | 2025-01-08 |
|
||||
| `20.18.1` | 2025-01-08 |
|
||||
| `20.12.1` | 2024-04-07 |
|
||||
| `21.7.2` | 2024-04-07 |
|
||||
|
||||
:::
|
||||
|
||||
@ -705,18 +704,17 @@ This demo was tested in the following environments:
|
||||
|
||||
| NodeJS | `request` | Date |
|
||||
|:-----------|:----------|:-----------|
|
||||
| `22.13.0` | `2.88.2` | 2025-01-08 |
|
||||
| `20.18.1` | `2.88.2` | 2025-01-08 |
|
||||
| `18.20.5` | `2.88.2` | 2025-01-08 |
|
||||
| `16.20.2` | `2.88.2` | 2025-01-08 |
|
||||
| `14.21.3` | `2.88.2` | 2025-01-08 |
|
||||
| `12.22.12` | `2.88.2` | 2025-01-08 |
|
||||
| `10.24.1` | `2.88.2` | 2025-01-08 |
|
||||
| `8.17.0` | `2.88.2` | 2025-01-08 |
|
||||
| `6.17.1` | `2.88.2` | 2025-01-08 |
|
||||
| `4.9.1` | `2.81.0` | 2025-01-08 |
|
||||
| `0.12.18` | `2.81.0` | 2025-01-08 |
|
||||
| `0.10.48` | `2.81.0` | 2025-01-08 |
|
||||
| `0.10.48` | `2.88.2` | 2024-04-07 |
|
||||
| `0.12.18` | `2.88.2` | 2024-04-07 |
|
||||
| `4.9.1` | `2.88.2` | 2024-04-07 |
|
||||
| `6.17.1` | `2.88.2` | 2024-04-07 |
|
||||
| `8.17.0` | `2.88.2` | 2024-04-07 |
|
||||
| `10.24.1` | `2.88.2` | 2024-04-07 |
|
||||
| `12.22.12` | `2.88.2` | 2024-04-07 |
|
||||
| `14.21.3` | `2.88.2` | 2024-04-07 |
|
||||
| `16.20.2` | `2.88.2` | 2024-04-07 |
|
||||
| `18.20.1` | `2.88.2` | 2024-04-07 |
|
||||
| `20.12.1` | `2.88.2` | 2024-04-07 |
|
||||
|
||||
:::
|
||||
|
||||
@ -733,13 +731,6 @@ and `request` module:
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz request`}
|
||||
</CodeBlock>
|
||||
|
||||
:::note pass
|
||||
|
||||
The current version of `request` requires NodeJS 6 or later. For older versions
|
||||
of NodeJS, `request` version `2.81.0` should be installed.
|
||||
|
||||
:::
|
||||
|
||||
2) Save the following to `SheetJSRequest.js`:
|
||||
|
||||
```js title="SheetJSRequest.js"
|
@ -22,12 +22,8 @@ The ["Complete Example"](#complete-example) section includes a complete server.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Version | Date |
|
||||
|:---------|:-----------|
|
||||
| `4.21.2` | 2025-01-03 |
|
||||
| `5.0.1` | 2025-01-03 |
|
||||
This demo was tested on 2024 March 11 using `express-formidable@1.2.0` and
|
||||
ExpressJS `4.18.3`
|
||||
|
||||
:::
|
||||
|
||||
@ -140,23 +136,21 @@ app.get('/download', function(req, res) {
|
||||
res.status(200).end(buf);
|
||||
// highlight-end
|
||||
});
|
||||
app.listen(+process.env.PORT||3000, function() { console.log("Ready to go"); });
|
||||
app.listen(+process.env.PORT||3000);
|
||||
```
|
||||
|
||||
2) Install dependencies:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz express@4.21.2 express-formidable@1.2.0`}
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz express@4.18.3 express-formidable@1.2.0`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Start server
|
||||
3) Start server (note: it will not print anything to console when running)
|
||||
|
||||
```bash
|
||||
node SheetJSExpressCSV.js
|
||||
```
|
||||
|
||||
Once the server starts, the process will print `Ready to go`.
|
||||
|
||||
4) Test POST requests using https://docs.sheetjs.com/pres.numbers . The commands
|
||||
should be run in a new terminal window:
|
||||
|
||||
|
@ -22,12 +22,7 @@ The ["Complete Example"](#complete-example) section includes a complete server.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Drash | Deno | Date |
|
||||
|:--------|:-------|:-----------|
|
||||
| `2.8.1` | 1.44.1 | 2024-12-19 |
|
||||
| `2.8.1` | 2.1.4 | 2024-12-19 |
|
||||
This demo was last tested on 2024 March 11 against Drash 2.8.1 and Deno 1.41.2.
|
||||
|
||||
:::
|
||||
|
||||
@ -128,16 +123,6 @@ curl -LO https://docs.sheetjs.com/drash/SheetJSDrash.ts
|
||||
deno run --allow-net SheetJSDrash.ts
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
Deno 2 requires the `--allow-import` entitlement:
|
||||
|
||||
```bash
|
||||
deno run --allow-net --allow-write --allow-import SheetJSDenoDOM.ts
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
3) Download the test file https://docs.sheetjs.com/pres.numbers
|
||||
|
||||
4) Open `http://localhost:7262/` in your browser.
|
||||
|
@ -1,248 +0,0 @@
|
||||
---
|
||||
title: Sheets on Fire with HonoJS
|
||||
sidebar_label: HonoJS
|
||||
pagination_prev: demos/net/network/index
|
||||
pagination_next: demos/net/email/index
|
||||
---
|
||||
|
||||
import current from '/version.js';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
[HonoJS](https://hono.dev/) is a lightweight server-side framework.
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
This demo uses HonoJS and SheetJS to read and write data. We'll explore how to
|
||||
parse uploaded files in a POST request handler and respond to GET requests with
|
||||
downloadable spreadsheets.
|
||||
|
||||
The ["Complete Example"](#complete-example) section includes a complete server.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Platform | HonoJS | Date |
|
||||
|:---------------|:---------|:-----------|
|
||||
| BunJS `1.1.40` | `4.6.14` | 2024-12-19 |
|
||||
|
||||
:::
|
||||
|
||||
## Integration Details
|
||||
|
||||
The [SheetJS BunJS module](/docs/getting-started/installation/bun) can be
|
||||
imported from HonoJS server scripts.
|
||||
|
||||
### Reading Data
|
||||
|
||||
The HonoJS body parser[^1] processes files in POST requests. The body parser
|
||||
returns an object that can be indexed by field name:
|
||||
|
||||
```js
|
||||
/* /import route */
|
||||
app.post('/import', async(c) => {
|
||||
/* parse body */
|
||||
const body = await c.req.parseBody();
|
||||
/* get a file uploaded in the `upload` field */
|
||||
// highlight-next-line
|
||||
const file = body["upload"];
|
||||
|
||||
/* `file` is a `File` object */
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
By default, the HonoJS body parser will use the last value when the form body
|
||||
specifies multiple values for a given field. To force the body parser to process
|
||||
all files, the field name must end with `[]`:
|
||||
|
||||
```js
|
||||
/* parse body */
|
||||
const body = await c.req.parseBody();
|
||||
/* get all files uploaded in the `upload` field */
|
||||
// highlight-next-line
|
||||
const files = body["upload[]"];
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
HonoJS exposes each file as a `Blob` object. The `Blob#arrayBuffer` method
|
||||
returns a Promise that resolves to an `ArrayBuffer`. That `ArrayBuffer` can be
|
||||
parsed with the SheetJS `read` method[^2].
|
||||
|
||||
This example server responds to POST requests. The server will look for a file
|
||||
in the request body under the `"upload"` key. If a file is present, the server
|
||||
will parse the file and, generate CSV rows using the `sheet_to_csv` method[^3],
|
||||
and respond with text:
|
||||
|
||||
```js
|
||||
import { Hono } from 'hono';
|
||||
import { read, utils } from 'xlsx';
|
||||
|
||||
const app = new Hono();
|
||||
app.post('/import', async(c) => {
|
||||
/* get file data */
|
||||
const body = await c.req.parseBody();
|
||||
const file = body["upload"];
|
||||
const ab = await file.arrayBuffer();
|
||||
/* parse */
|
||||
const wb = read(ab);
|
||||
/* generate CSV */
|
||||
const csv = utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]);
|
||||
return c.text(csv);
|
||||
});
|
||||
export default app;
|
||||
```
|
||||
|
||||
### Writing Data
|
||||
|
||||
Given a SheetJS workbook object, the `write` method using `type: "buffer"`[^4]
|
||||
generates data objects which can be passed to the response `body` method.
|
||||
|
||||
This example server responds to GET requests. The server will generate a SheetJS
|
||||
worksheet object from an array of arrays[^5], build up a new workbook using the
|
||||
`book_new`[^6] utility method, generate a XLSX file using `write`, and send the
|
||||
file with appropriate headers to download `SheetJSHonoJS.xlsx`:
|
||||
|
||||
```js
|
||||
import { Hono } from 'hono';
|
||||
import { utils, write } from "xlsx";
|
||||
|
||||
const app = new Hono();
|
||||
app.get("/export", (c) => {
|
||||
/* generate SheetJS workbook object */
|
||||
var ws = utils.aoa_to_sheet(["SheetJS".split(""), [5,4,3,3,7,9,5]]);
|
||||
var wb = utils.book_new(ws, "Data");
|
||||
/* generate buffer */
|
||||
var buf = write(wb, {type: "buffer", bookType: "xlsx"});
|
||||
/* set headers */
|
||||
c.header('Content-Disposition', 'attachment; filename="SheetJSHonoJS.xlsx"');
|
||||
c.header('Content-Type', 'application/vnd.ms-excel');
|
||||
/* export buffer */
|
||||
return c.body(buf);
|
||||
});
|
||||
export default app;
|
||||
```
|
||||
|
||||
## Complete Example
|
||||
|
||||
This example creates a simple server that stores an array of arrays. There are
|
||||
three server endpoints:
|
||||
|
||||
- `/import` POST request expects a file in the `upload` field. It will parse the
|
||||
file, update the internal array of arrays, and responds with CSV data.
|
||||
|
||||
- `/export` GET request generates a workbook from the internal array of arrays.
|
||||
It will respond with XLSX data and initiate a download to `SheetJSHonoJS.xlsx` .
|
||||
|
||||
- `/json` GET request responds with the internal state.
|
||||
|
||||
1) Create a new BunJS + HonoJS project:
|
||||
|
||||
```bash
|
||||
bun create hono sheetjs-hono --template bun --install --pm bun
|
||||
cd sheetjs-hono
|
||||
```
|
||||
|
||||
2) Install the [SheetJS BunJS module](/docs/getting-started/installation/bun):
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
bun i xlsx@https://sheet.lol/balls/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Save the following script to `src/index.ts`:
|
||||
|
||||
```ts title="src/index.ts"
|
||||
import { Hono } from 'hono';
|
||||
import { read, write, utils } from 'xlsx';
|
||||
|
||||
const app = new Hono();
|
||||
let data = ["SheetJS".split(""), [5,4,3,3,7,9,5]];
|
||||
|
||||
app.get('/export', (c) => {
|
||||
const ws = utils.aoa_to_sheet(data);
|
||||
const wb = utils.book_new(ws, "SheetJSHono");
|
||||
const buf = write(wb, { type: "buffer", bookType: "xlsx" });
|
||||
c.header('Content-Disposition', 'attachment; filename="SheetJSHonoJS.xlsx"');
|
||||
c.header('Content-Type', 'application/vnd.ms-excel');
|
||||
return c.body(buf);
|
||||
});
|
||||
|
||||
app.post('/import', async(c) => {
|
||||
const body = await c.req.parseBody();
|
||||
const file = body["upload"];
|
||||
const ab = await file.arrayBuffer();
|
||||
const wb = read(ab);
|
||||
data = utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]], { header:1 });
|
||||
return c.text(utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]));
|
||||
});
|
||||
|
||||
app.get('/json', (c) => c.json(data));
|
||||
export default app;
|
||||
```
|
||||
|
||||
4) Run the server:
|
||||
|
||||
```bash
|
||||
bun run dev
|
||||
```
|
||||
|
||||
The process will display a URL (typically `http://localhost:3000`):
|
||||
|
||||
```text
|
||||
% bun run dev
|
||||
$ bun run --hot src/index.ts
|
||||
// highlight-next-line
|
||||
Started server http://localhost:3000
|
||||
```
|
||||
|
||||
5) Test exports by opening `http://localhost:3000/export` in your browser.
|
||||
|
||||
The page should attempt to download `SheetJSHonoJS.xlsx` . Save the download and
|
||||
open the new file. The contents should match the original data:
|
||||
|
||||
<table>
|
||||
<tr><td>S</td><td>h</td><td>e</td><td>e</td><td>t</td><td>J</td><td>S</td></tr>
|
||||
<tr><td>5</td><td>4</td><td>3</td><td>3</td><td>7</td><td>9</td><td>5</td></tr>
|
||||
</table>
|
||||
|
||||
6) Test imports using https://docs.sheetjs.com/pres.numbers . The commands
|
||||
should be run in a new terminal window:
|
||||
|
||||
```bash
|
||||
curl -LO https://docs.sheetjs.com/pres.numbers
|
||||
curl -X POST -F upload=@pres.numbers http://localhost:3000/import
|
||||
```
|
||||
|
||||
The terminal will display CSV rows generated from the first worksheet:
|
||||
|
||||
```text title="Expected output"
|
||||
Name,Index
|
||||
Bill Clinton,42
|
||||
GeorgeW Bush,43
|
||||
Barack Obama,44
|
||||
Donald Trump,45
|
||||
Joseph Biden,46
|
||||
```
|
||||
|
||||
7) Confirm the state was updated by loading `http://localhost:3000/json` :
|
||||
|
||||
```bash
|
||||
curl -LO http://localhost:3000/json
|
||||
```
|
||||
|
||||
The terminal will display the worksheet data in an array of arrays:
|
||||
|
||||
```json title="Expected output"
|
||||
[["Name","Index"],["Bill Clinton",42],["GeorgeW Bush",43],["Barack Obama",44],["Donald Trump",45],["Joseph Biden",46]]
|
||||
```
|
||||
|
||||
[^1]: See ["`parseBody()`"](https://hono.dev/docs/api/request#parsebody) in the HonoJS documentation.
|
||||
[^2]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^3]: See [`sheet_to_csv` in "Utilities"](/docs/api/utilities/csv#delimiter-separated-output)
|
||||
[^4]: See [`write` in "Writing Files"](/docs/api/write-options)
|
||||
[^5]: See [`aoa_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-arrays-input)
|
||||
[^6]: See [`book_new` in "Utilities"](/docs/api/utilities/wb)
|
@ -8,8 +8,7 @@ pagination_next: demos/net/email/index
|
||||
import current from '/version.js';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
[ElysiaJS](https://elysiajs.com/) is a server-side framework for
|
||||
[BunJS](/docs/getting-started/installation/bun).
|
||||
[Elysia](https://elysiajs.com/) is a BunJS server-side framework.
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
@ -22,12 +21,7 @@ The ["Complete Example"](#complete-example) section includes a complete server.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Platform | ElysiaJS | Date |
|
||||
|:---------------|:---------|:-----------|
|
||||
| BunJS `1.1.40` | `0.8.17` | 2024-12-19 |
|
||||
| BunJS `1.1.40` | `1.1.26` | 2024-12-19 |
|
||||
This demo was last tested on 2024 March 11 with ElysiaJS 0.8.17 and BunJS 1.0.30.
|
||||
|
||||
:::
|
||||
|
||||
@ -118,16 +112,6 @@ bun create elysia sheetjs-elysia
|
||||
cd sheetjs-elysia
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
The `bun install` command can install specific versions of ElysiaJS:
|
||||
|
||||
```bash
|
||||
bun install elysia@0.8.17
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
2) Install the [SheetJS BunJS module](/docs/getting-started/installation/bun):
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
@ -158,7 +142,7 @@ app.post("/", async({ body: { upload } }) => {
|
||||
upload: t.File()
|
||||
})
|
||||
});
|
||||
app.listen(3000, () => { console.log("SheetJS ElysiaJS server is up")});
|
||||
app.listen(3000);
|
||||
```
|
||||
|
||||
4) Run the server:
|
||||
@ -167,8 +151,6 @@ app.listen(3000, () => { console.log("SheetJS ElysiaJS server is up")});
|
||||
bun run src/SheetJSElysia.ts
|
||||
```
|
||||
|
||||
The script will print `SheetJS ElysiaJS server is up`.
|
||||
|
||||
5) Test POST requests using https://docs.sheetjs.com/pres.numbers . The commands
|
||||
should be run in a new terminal window:
|
||||
|
||||
|
@ -22,13 +22,7 @@ The ["Complete Example"](#complete-example) section includes a complete server.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| NestJS | Date |
|
||||
|:----------|:-------------|
|
||||
| `10.4.15` | `2024-12-22` |
|
||||
| `9.4.3` | `2024-12-22` |
|
||||
| `8.4.7` | `2024-12-22` |
|
||||
This demo was tested on 2024 March 11 using NestJS `10.3.3`.
|
||||
|
||||
:::
|
||||
|
||||
@ -130,23 +124,10 @@ npx @nestjs/cli@latest new -p npm sheetjs-nest
|
||||
cd sheetjs-nest
|
||||
```
|
||||
|
||||
:::info pass
|
||||
|
||||
The NestJS CLI generates a project using the latest version of NestJS. To force
|
||||
an older version, install older versions of dependencies in the `@nestjs` scope.
|
||||
|
||||
For example, the following command rolls back to NestJS major version 8:
|
||||
2) Install the `@types/multer` package as a development dependency:
|
||||
|
||||
```bash
|
||||
npm install --save @nestjs/cli@8.x @nestjs/common@8.x @nestjs/core@8.x @nestjs/platform-express@8.x @nestjs/schematics@8.x @nestjs/testing@8.x --force
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
2) Install the `@types/multer` package as a dependency:
|
||||
|
||||
```bash
|
||||
npm i --save @types/multer
|
||||
npm i --save-dev @types/multer
|
||||
```
|
||||
|
||||
3) Install the SheetJS library:
|
||||
@ -206,7 +187,7 @@ npx @nestjs/cli start
|
||||
|
||||
:::note pass
|
||||
|
||||
In some tests, the process failed with a message referencing `Multer`:
|
||||
In the most recent test, the process failed with a message referencing Multer:
|
||||
|
||||
```
|
||||
src/sheetjs/sheetjs.controller.ts:9:54 - error TS2694: Namespace 'global.Express' has no exported member 'Multer'.
|
||||
@ -222,7 +203,7 @@ This error indicates that `@types/multer` is not available.
|
||||
The recommended fix is to install `@types/multer` again:
|
||||
|
||||
```bash
|
||||
npm i --save @types/multer
|
||||
npm i --save-dev @types/multer
|
||||
npx @nestjs/cli start
|
||||
```
|
||||
|
||||
|
@ -21,12 +21,7 @@ The ["Complete Example"](#complete-example) section includes a complete server.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Version | Date |
|
||||
|:---------|:-----------|
|
||||
| `4.29.0` | 2025-01-02 |
|
||||
| `5.2.0` | 2025-01-02 |
|
||||
This demo was verified on 2024 March 11 using `fastify@4.26.2`
|
||||
|
||||
:::
|
||||
|
||||
@ -106,8 +101,8 @@ fastify.post('/', async(req, reply) => {
|
||||
|
||||
:::caution pass
|
||||
|
||||
Out of the box, FastifyJS will return an error `FST_ERR_CTP_BODY_TOO_LARGE` when
|
||||
processing large spreadsheets (`statusCode 413`). This is a FastifyJS issue.
|
||||
Out of the box, Fastify will return an error `FST_ERR_CTP_BODY_TOO_LARGE` when
|
||||
processing large spreadsheets (`statusCode 413`). This is a Fastify issue.
|
||||
|
||||
The default body size limit (including all uploaded files and fields) is 1 MB.
|
||||
It can be increased by setting the `bodyLimit` option during server creation:
|
||||
@ -162,7 +157,7 @@ fastify.listen({port: process.env.PORT || 3000}, (err, addr) => { if(err) throw
|
||||
1) Install dependencies:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz fastify@4.29.0 @fastify/multipart@8`}
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz fastify@4.26.2 @fastify/multipart@8.1.0`}
|
||||
</CodeBlock>
|
||||
|
||||
2) Start server
|
||||
|
@ -127,18 +127,15 @@ That approach is not explored in this demo.
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| NodeJS | Date | ExpressJS |
|
||||
|:----------|:-----------|:----------|
|
||||
| `18.20.8` | 2025-04-17 | `5.1.0` |
|
||||
| `20.18.0` | 2025-04-17 | `5.1.0` |
|
||||
| `22.14.0` | 2025-04-17 | `5.1.0` |
|
||||
| `18.20.8` | 2025-04-17 | `4.21.2` |
|
||||
| `20.18.0` | 2025-04-17 | `4.21.2` |
|
||||
| `22.14.0` | 2025-04-17 | `4.21.2` |
|
||||
| NodeJS | Date | Dependencies |
|
||||
|:----------|:-----------|:------------------------------------|
|
||||
| `18.20.3` | 2024-06-30 | ExpressJS 4.19.2 + Formidable 2.1.2 |
|
||||
| `20.15.0` | 2024-06-30 | ExpressJS 4.19.2 + Formidable 2.1.2 |
|
||||
| `22.3.0` | 2024-06-30 | ExpressJS 4.19.2 + Formidable 2.1.2 |
|
||||
|
||||
:::
|
||||
|
||||
0) Create a new project with a `package.json` that enables ESM:
|
||||
0) Create a new project with a ESM-enabled `package.json`:
|
||||
|
||||
```bash
|
||||
mkdir sheetjs-worker
|
||||
|
@ -34,14 +34,7 @@ the file can be downloaded or previewed in the browser.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Platform | Version | Date |
|
||||
|:-------------|:---------|:-----------|
|
||||
| Chromium 131 | `1.9.0` | 2024-12-22 |
|
||||
| Konqueror 22 | `1.9.0` | 2025-04-23 |
|
||||
| NodeJS 20 | `1.10.0` | 2024-12-22 |
|
||||
| BunJS 1.1 | `1.10.0` | 2024-12-22 |
|
||||
This demo was last tested on 2024 March 11 against `pst-extractor` 1.9.0
|
||||
|
||||
:::
|
||||
|
||||
@ -293,7 +286,7 @@ This demo reads PST mailboxes. Due to browser limitations, PST files larger than
|
||||
100 MB may crash the browser.
|
||||
|
||||
After parsing the PST file, the "Attachments" table will list attached XLSX and
|
||||
XLS spreadsheets in the file. The "preview" link will display an HTML table with
|
||||
XLS spreadsheets in the file. The "preview" link will display a HTML table with
|
||||
the data in the spreadsheet. The "download" link will download the attachment.
|
||||
|
||||
The [test file](pathname:///pst/enron.pst) was based on the EDRM clean extract
|
||||
|
@ -32,25 +32,53 @@ before integrating with important inboxes or accounts.
|
||||
:::danger pass
|
||||
|
||||
It is strongly advised to use a test email address before using an important
|
||||
address. One small mistake could erase decades of messages or result in a block
|
||||
address. One small mistake could erase decades of messages or result in a block
|
||||
or ban from Google services.
|
||||
|
||||
:::
|
||||
|
||||
### App Passwords
|
||||
|
||||
Many email providers (including GMail and Yahoo Mail) require "app passwords" or
|
||||
passwords for "less secure apps". Attempting to connect and send using the
|
||||
account password will throw errors.
|
||||
Many email providers (including Fastmail, GMail, and Yahoo Mail) require "app
|
||||
passwords" or passwords for "less secure apps". Attempting to connect and send
|
||||
using the account password will throw errors.
|
||||
|
||||
### Test Account
|
||||
|
||||
It is strongly recommended to first test with a separate account.
|
||||
It is strongly recommended to first test with an independent service provider.
|
||||
|
||||
#### Fastmail
|
||||
|
||||
This demo will start with a free 30-day trial of Fastmail. At the time the demo
|
||||
was last tested, no payment details were required.
|
||||
|
||||
:::caution pass
|
||||
|
||||
A valid phone number (for SMS verification) was required.
|
||||
|
||||
:::
|
||||
|
||||
0) Create a new Fastmail email account and verify with a mobile number.
|
||||
|
||||
|
||||
_Create App Password_
|
||||
|
||||
1) Open the settings screen (click on the icon in the top-left corner of the
|
||||
screen and select "Settings").
|
||||
|
||||
2) Select "Privacy & Security" in the left pane, then click "Integrations" near
|
||||
the top of the main page. Click "New app password".
|
||||
|
||||
3) Select any name in the top drop-down (the default "iPhone" can be used). In
|
||||
the second drop-down, select "Mail (IMAP/POP/SMTP)". Click "Generate password".
|
||||
|
||||
A new password will be displayed. This is the app password that will be used in
|
||||
the demo script. **Copy the displayed password or write it down.**
|
||||
|
||||
#### Gmail
|
||||
|
||||
This demo will start with a free Gmail account. When the demo was last tested,
|
||||
no payment details were required.
|
||||
This demo will start with a free Gmail account. At the time the demo was last
|
||||
tested, no payment details were required.
|
||||
|
||||
:::caution pass
|
||||
|
||||
@ -70,8 +98,7 @@ _Create App Password_
|
||||
|
||||
4) Click "2-Step Verification"
|
||||
|
||||
5) Click the right arrow (`>`) next to "App passwords". If that option is not
|
||||
available, open https://myaccount.google.com/apppasswords
|
||||
5) Click the right arrow (`>`) next to "App passwords".
|
||||
|
||||
6) Type a name ("SheetJS Test") and click "Create".
|
||||
|
||||
@ -125,7 +152,8 @@ This demo was tested in the following deployments:
|
||||
|
||||
| Email Provider | Date | Library | Version |
|
||||
|:---------------|:-----------|:-------------|:---------|
|
||||
| `gmail.com` | 2025-01-05 | `nodemailer` | `6.9.16` |
|
||||
| `gmail.com` | 2024-03-11 | `nodemailer` | `6.9.12` |
|
||||
| `fastmail.com` | 2024-03-11 | `nodemailer` | `6.9.12` |
|
||||
|
||||
:::
|
||||
|
||||
@ -147,7 +175,7 @@ const nodemailer = require('nodemailer');
|
||||
|
||||
const transporter = nodemailer.createTransport({
|
||||
// highlight-next-line
|
||||
service: 'gmail',
|
||||
service: 'fastmail',
|
||||
auth: {
|
||||
// highlight-start
|
||||
user: '**',
|
||||
@ -182,7 +210,7 @@ transporter.sendMail(mailOptions, function (err, info) {
|
||||
|
||||
3) Edit `SheetJSend.js` and replace the highlighted lines:
|
||||
|
||||
- `service: 'gmail',` the value should be one of the supported providers[^1]
|
||||
- `service: 'fastmail',` the value should be one of the supported providers[^1]
|
||||
- `user: "**",` the value should be the sender email address
|
||||
- `pass: "**"` the value should be the app password from earlier
|
||||
- `from: "**",` the value should be the sender email address
|
||||
@ -272,7 +300,8 @@ This demo was tested in the following deployments:
|
||||
|
||||
| Email Provider | Date | Library | Version |
|
||||
|:---------------|:-----------|:-----------|:----------|
|
||||
| `gmail.com` | 2025-01-05 | `imapflow` | `1.0.172` |
|
||||
| `gmail.com` | 2024-03-11 | `imapflow` | `1.0.156` |
|
||||
| `fastmail.com` | 2024-03-11 | `imapflow` | `1.0.156` |
|
||||
|
||||
:::
|
||||
|
||||
@ -283,7 +312,7 @@ This demo was tested in the following deployments:
|
||||
<CodeBlock language="bash">{`\
|
||||
mkdir sheetjs-recv
|
||||
cd sheetjs-recv
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz imapflow@1.0.172`}
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz imapflow@1.0.156`}
|
||||
</CodeBlock>
|
||||
|
||||
2) Save the following script to `SheetJSIMAP.js`:
|
||||
@ -294,7 +323,7 @@ const { ImapFlow } = require('imapflow');
|
||||
|
||||
const client = new ImapFlow({
|
||||
// highlight-next-line
|
||||
host: 'imap.gmail.com',
|
||||
host: 'imap.fastmail.com',
|
||||
port: 993, secure: true, logger: false,
|
||||
auth: {
|
||||
// highlight-start
|
||||
@ -342,19 +371,18 @@ const concat_RS = (stream) => new Promise((res, rej) => {
|
||||
|
||||
- `user: "**",` the value should be the account address
|
||||
- `pass: "**"` the value should be the app password from earlier
|
||||
- `host: 'imap.gmail.com',` the value should be the host name:
|
||||
- `host: 'imap.fastmail.com',` the value should be the host name:
|
||||
|
||||
| Service | `host` value |
|
||||
|:---------------|:--------------------|
|
||||
| `gmail.com` | `imap.gmail.com` |
|
||||
| `fastmail.com` | `imap.fastmail.com` |
|
||||
|
||||
4) Download https://docs.sheetjs.com/pres.numbers .
|
||||
4) Download https://docs.sheetjs.com/pres.numbers . Using a different account,
|
||||
send an email to the test account and attach the file. At the end of this step,
|
||||
the test account should have an email in the inbox that has an attachment.
|
||||
|
||||
5) Send an email to the test account and attach `pres.numbers` from step 4.
|
||||
|
||||
6) Wait until the test account receives the email.
|
||||
|
||||
7) Run the script:
|
||||
5) Run the script:
|
||||
|
||||
```bash
|
||||
node SheetJSIMAP.js
|
||||
|
@ -25,17 +25,6 @@ not generally support passing objects between the browser context and the
|
||||
automation script, so the file data must be generated in the browser context
|
||||
and sent back to the automation script for saving in the file system.
|
||||
|
||||
This demo exports data from https://sheetjs.com/demos/table.
|
||||
|
||||
:::note pass
|
||||
|
||||
It is also possible to parse files from the browser context, but parsing from
|
||||
the automation context is more efficient and strongly recommended.
|
||||
|
||||
:::
|
||||
|
||||
#### Key Steps
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber off
|
||||
@ -61,23 +50,39 @@ sequenceDiagram
|
||||
end
|
||||
```
|
||||
|
||||
<details open>
|
||||
<summary><b>Key Steps</b> (click to hide)</summary>
|
||||
|
||||
1) Launch the headless browser and load the target site.
|
||||
|
||||
2) Add the standalone SheetJS build to the page in a `SCRIPT` tag.
|
||||
|
||||
3) Add a script to the page (in the browser context) that will:
|
||||
|
||||
- Make a SheetJS workbook object[^1] from the first table using the SheetJS
|
||||
`table_to_book`[^2] method.
|
||||
- Generate the bytes for an XLSB file using the SheetJS `write`[^3] method.
|
||||
- Make a workbook object from the first table using `XLSX.utils.table_to_book`
|
||||
- Generate the bytes for an XLSB file using `XLSX.write`
|
||||
- Send the bytes back to the automation script
|
||||
|
||||
4) When the automation context receives data, save to a file
|
||||
|
||||
</details>
|
||||
|
||||
This demo exports data from https://sheetjs.com/demos/table.
|
||||
|
||||
:::note pass
|
||||
|
||||
It is also possible to parse files from the browser context, but parsing from
|
||||
the automation context is more efficient and strongly recommended.
|
||||
|
||||
:::
|
||||
|
||||
## Puppeteer
|
||||
|
||||
[Puppeteer](https://pptr.dev/) enables headless Chromium automation for NodeJS
|
||||
and BunJS. Releases ship with a script that installs a headless browser.
|
||||
[Puppeteer](https://pptr.dev/) enables headless Chromium automation for NodeJS.
|
||||
Releases ship with an installer script that installs a headless browser.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="nodejs" label="NodeJS">
|
||||
|
||||
Binary strings are the favored data type. They can be safely passed from the
|
||||
browser context to the automation script. NodeJS provides an API to write
|
||||
@ -126,61 +131,116 @@ const puppeteer = require('puppeteer');
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Puppeteer | Date |
|
||||
|:----------|:-----------|
|
||||
| `23.11.1` | 2024-12-31 |
|
||||
| `22.15.0` | 2024-12-31 |
|
||||
| `21.11.0` | 2024-12-31 |
|
||||
| `20.9.0` | 2024-12-31 |
|
||||
| `15.5.0` | 2024-12-31 |
|
||||
| `10.4.0` | 2024-12-31 |
|
||||
This demo was last tested on 2024 June 24 against Puppeteer 22.12.0.
|
||||
|
||||
:::
|
||||
|
||||
1) Install SheetJS and Puppeteer:
|
||||
|
||||
<Tabs groupId="ssplat">
|
||||
<TabItem value="nodejs" label="NodeJS">
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz puppeteer@23.11.1`}
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz puppeteer@22.12.0`}
|
||||
</CodeBlock>
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="bunjs" label="BunJS">
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
bun install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz puppeteer@23.11.1`}
|
||||
</CodeBlock>
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
2) Save the `SheetJSPuppeteer.js` code snippet to `SheetJSPuppeteer.js`.
|
||||
|
||||
3) Run the script:
|
||||
|
||||
<Tabs groupId="ssplat">
|
||||
<TabItem value="nodejs" label="NodeJS">
|
||||
|
||||
```bash
|
||||
node SheetJSPuppeteer.js
|
||||
```
|
||||
|
||||
When the script finishes, the file `SheetJSPuppeteer.xlsb` will be created.
|
||||
This file can be opened with Excel.
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="bunjs" label="BunJS">
|
||||
<TabItem value="deno" label="Deno">
|
||||
|
||||
:::caution pass
|
||||
|
||||
Deno Puppeteer is a fork. It is not officially supported by the Puppeteer team.
|
||||
|
||||
:::
|
||||
|
||||
Base64 strings are the favored data type. They can be safely passed from the
|
||||
browser context to the automation script. Deno can decode the Base64 strings
|
||||
and write the decoded `Uint8Array` data to file with `Deno.writeFileSync`
|
||||
|
||||
The key steps are commented below:
|
||||
|
||||
<CodeBlock language="ts" title="SheetJSPuppeteer.ts">{`\
|
||||
import puppeteer from "https://deno.land/x/puppeteer@16.2.0/mod.ts";
|
||||
import { decode } from "https://deno.land/std/encoding/base64.ts"
|
||||
\n\
|
||||
/* (1) Load the target page */
|
||||
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('https://sheetjs.com/demos/table');
|
||||
\n\
|
||||
/* (2) Load the standalone SheetJS build from the CDN */
|
||||
await page.addScriptTag({ url: 'https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js' });
|
||||
\n\
|
||||
/* (3) Run the snippet in browser and return data */
|
||||
const b64 = await page.evaluate(() => {
|
||||
/* NOTE: this function will be evaluated in the browser context.
|
||||
\`page\`, \`fs\` and \`puppeteer\` are not available.
|
||||
\`XLSX\` will be available thanks to step 2 */
|
||||
\n\
|
||||
/* find first table */
|
||||
var table = document.body.getElementsByTagName('table')[0];
|
||||
\n\
|
||||
/* call table_to_book on first table */
|
||||
var wb = XLSX.utils.table_to_book(table);
|
||||
\n\
|
||||
/* generate XLSB and return binary string */
|
||||
return XLSX.write(wb, {type: "base64", bookType: "xlsb"});
|
||||
});
|
||||
/* (4) write data to file */
|
||||
Deno.writeFileSync("SheetJSPuppeteer.xlsb", decode(b64));
|
||||
\n\
|
||||
await browser.close();`}
|
||||
</CodeBlock>
|
||||
|
||||
**Demo**
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was last tested on 2024 June 24 against deno-puppeteer 16.2.0.
|
||||
|
||||
:::
|
||||
|
||||
1) Install deno-puppeteer:
|
||||
|
||||
```bash
|
||||
bun SheetJSPuppeteer.js
|
||||
env PUPPETEER_PRODUCT=chrome deno run -A --unstable https://deno.land/x/puppeteer@16.2.0/install.ts
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
In PowerShell, the environment variable should be set separately:
|
||||
|
||||
```powershell
|
||||
[Environment]::SetEnvironmentVariable('PUPPETEER_PRODUCT', 'chrome')
|
||||
deno run -A --unstable https://deno.land/x/puppeteer@16.2.0/install.ts
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
2) Save the `SheetJSPuppeteer.ts` code snippet to `SheetJSPuppeteer.ts`.
|
||||
|
||||
3) Run the script:
|
||||
|
||||
```bash
|
||||
deno run -A --unstable SheetJSPuppeteer.ts
|
||||
```
|
||||
|
||||
When the script finishes, the file `SheetJSPuppeteer.xlsb` will be created.
|
||||
This file can be opened with Excel.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
When the script finishes, the file `SheetJSPuppeteer.xlsb` will be created.
|
||||
This file can be opened with a spreadsheet editor that supports XLSB workbooks.
|
||||
|
||||
## Playwright
|
||||
|
||||
@ -234,56 +294,26 @@ const { webkit } = require('playwright'); // import desired browser
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Playwright | Date |
|
||||
|:-----------|:-----------|
|
||||
| `1.49.1` | 2024-12-31 |
|
||||
This demo was last tested on 2024 June 24 against Playwright 1.45.0.
|
||||
|
||||
:::
|
||||
|
||||
1) Install SheetJS and Playwright:
|
||||
|
||||
<Tabs groupId="ssplat">
|
||||
<TabItem value="nodejs" label="NodeJS">
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz playwright@1.49.1`}
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz playwright@1.45.0`}
|
||||
</CodeBlock>
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="bunjs" label="BunJS">
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
bun install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz playwright@1.49.1`}
|
||||
</CodeBlock>
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
2) Save the `SheetJSPlaywright.js` code snippet to `SheetJSPlaywright.js`.
|
||||
|
||||
3) Run the script
|
||||
|
||||
<Tabs groupId="ssplat">
|
||||
<TabItem value="nodejs" label="NodeJS">
|
||||
|
||||
```bash
|
||||
node SheetJSPlaywright.js
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="bunjs" label="BunJS">
|
||||
|
||||
```bash
|
||||
bun SheetJSPlaywright.js
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
When the script finishes, the file `SheetJSPlaywright.xlsb` will be created.
|
||||
This file can be opened with a spreadsheet editor that supports XLSB workbooks.
|
||||
This file can be opened with Excel.
|
||||
|
||||
:::caution pass
|
||||
|
||||
@ -326,18 +356,6 @@ Binary strings are the favored data type. They can be safely passed from the
|
||||
browser context to the automation script. PhantomJS provides an API to write
|
||||
binary strings to file (`fs.write` using mode `wb`).
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Architecture | PhantomJS | Date |
|
||||
|:-------------|:----------|:-----------|
|
||||
| `darwin-x64` | `2.1.1` | 2025-03-31 |
|
||||
| `win11-x64` | `2.1.1` | 2025-01-19 |
|
||||
| `linux-x64` | `2.1.1` | 2025-01-07 |
|
||||
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary><b>Integration Details and Demo</b> (click to show)</summary>
|
||||
|
||||
@ -383,6 +401,19 @@ strongly recommended to add verbose logging and to lint scripts before use.
|
||||
|
||||
**Demo**
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Architecture | PhantomJS | Date |
|
||||
|:-------------|:----------|:-----------|
|
||||
| `darwin-x64` | `2.1.1` | 2024-03-15 |
|
||||
| `win10-x64` | `2.1.1` | 2024-03-24 |
|
||||
| `win11-x64` | `2.1.1` | 2024-05-22 |
|
||||
| `linux-x64` | `2.1.1` | 2024-04-25 |
|
||||
|
||||
:::
|
||||
|
||||
1) [Download and extract PhantomJS](https://phantomjs.org/download.html)
|
||||
|
||||
2) Save the `SheetJSPhantom.js` code snippet to `SheetJSPhantom.js`.
|
||||
@ -426,7 +457,3 @@ env OPENSSL_CONF=/dev/null QT_QPA_PLATFORM=phantom ./phantomjs-2.1.1-linux-x86_6
|
||||
:::
|
||||
|
||||
</details>
|
||||
|
||||
[^1]: See ["Workbook Object"](/docs/csf/book) for more details about the SheetJS workbook object.
|
||||
[^2]: See [`table_to_book` in "HTML" Utilities](/docs/api/utilities/html#create-new-sheet)
|
||||
[^3]: See [`write` in "Writing Files"](/docs/api/write-options)
|
@ -31,7 +31,7 @@ web browser. ["Browser Automation"](/docs/demos/net/headless) includes demos.
|
||||
|
||||
## Integration Details
|
||||
|
||||
Synthetic DOM implementations typically provide a function that accept an HTML
|
||||
Synthetic DOM implementations typically provide a function that accept a HTML
|
||||
string and return an object that represents `document`. An API method such as
|
||||
`getElementsByTagName` or `querySelector` can pull TABLE elements.
|
||||
|
||||
@ -111,25 +111,23 @@ XLSX.writeFile(workbook, "SheetJSDOM.xlsx");
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| JSDOM | Date |
|
||||
|:----------|:-----------|
|
||||
| `26.1.0` | 2025-04-24 |
|
||||
| `25.0.1` | 2025-04-24 |
|
||||
| `24.1.3` | 2025-04-24 |
|
||||
| `23.2.0` | 2025-04-24 |
|
||||
| `22.1.0` | 2025-04-24 |
|
||||
| `21.1.2` | 2025-04-24 |
|
||||
| `20.0.3` | 2025-04-24 |
|
||||
| `19.0.0` | 2025-04-24 |
|
||||
| `18.1.1` | 2025-04-24 |
|
||||
| `17.0.0` | 2025-04-24 |
|
||||
| `16.7.0` | 2025-04-24 |
|
||||
| `15.2.1` | 2025-04-24 |
|
||||
| `14.1.0` | 2025-04-24 |
|
||||
| `13.2.0` | 2025-04-24 |
|
||||
| `12.2.0` | 2025-04-24 |
|
||||
| `11.12.0` | 2025-04-24 |
|
||||
| `10.1.0` | 2025-04-24 |
|
||||
| JSDOM | Date |
|
||||
|:--------|:-----------|
|
||||
| 24.1.0 | 2024-06-24 |
|
||||
| 23.2.0 | 2024-06-24 |
|
||||
| 22.1.0 | 2024-06-24 |
|
||||
| 21.1.2 | 2024-06-24 |
|
||||
| 20.0.3 | 2024-06-24 |
|
||||
| 19.0.0 | 2024-06-24 |
|
||||
| 18.1.1 | 2024-06-24 |
|
||||
| 17.0.0 | 2024-06-24 |
|
||||
| 16.7.0 | 2024-06-24 |
|
||||
| 15.2.1 | 2024-06-24 |
|
||||
| 14.1.0 | 2024-06-24 |
|
||||
| 13.2.0 | 2024-06-24 |
|
||||
| 12.2.0 | 2024-06-24 |
|
||||
| 11.12.0 | 2024-06-24 |
|
||||
| 10.1.0 | 2024-06-24 |
|
||||
|
||||
:::
|
||||
|
||||
@ -162,36 +160,31 @@ The script will create a file `SheetJSDOM.xlsx` that can be opened.
|
||||
|
||||
### HappyDOM
|
||||
|
||||
HappyDOM provides a DOM framework for NodeJS. Older versions required the
|
||||
following patches:
|
||||
HappyDOM provides a DOM framework for NodeJS. For the tested version (`13.3.1`),
|
||||
the following patches were needed:
|
||||
|
||||
- TABLE `rows` property (explained above)
|
||||
- TR `cells` property (explained above)
|
||||
|
||||
HappyDOM `15.7.4` did not require any workarounds.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| HappyDOM | Date |
|
||||
|:---------|:-----------|
|
||||
| 17.4.4 | 2025-04-24 |
|
||||
| 16.8.1 | 2025-04-24 |
|
||||
| 15.11.7 | 2025-04-24 |
|
||||
| 14.12.3 | 2025-04-24 |
|
||||
| 13.10.1 | 2025-04-24 |
|
||||
| 12.10.3 | 2025-04-24 |
|
||||
| 11.2.0 | 2025-04-24 |
|
||||
| 10.11.2 | 2025-04-24 |
|
||||
| 9.20.3 | 2025-04-24 |
|
||||
| 8.9.0 | 2025-04-24 |
|
||||
| 7.8.1 | 2025-04-24 |
|
||||
| 6.0.4 | 2025-04-24 |
|
||||
| 5.4.0 | 2025-04-24 |
|
||||
| 4.1.0 | 2025-04-24 |
|
||||
| 3.2.2 | 2025-04-24 |
|
||||
| 2.55.0 | 2025-04-24 |
|
||||
| 14.12.3 | 2024-06-24 |
|
||||
| 13.10.1 | 2024-06-24 |
|
||||
| 12.10.3 | 2024-06-24 |
|
||||
| 11.2.0 | 2024-06-24 |
|
||||
| 10.11.2 | 2024-06-24 |
|
||||
| 9.20.3 | 2024-06-24 |
|
||||
| 8.9.0 | 2024-06-24 |
|
||||
| 7.8.1 | 2024-06-24 |
|
||||
| 6.0.4 | 2024-06-24 |
|
||||
| 5.4.0 | 2024-06-24 |
|
||||
| 4.1.0 | 2024-06-24 |
|
||||
| 3.2.2 | 2024-06-24 |
|
||||
| 2.55.0 | 2024-06-24 |
|
||||
|
||||
:::
|
||||
|
||||
@ -243,24 +236,19 @@ Object.defineProperty(tbl.__proto__, "innerHTML", { get: function() {
|
||||
}});
|
||||
```
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| XMLDOM | Date |
|
||||
|:---------|:-----------|
|
||||
| `0.9.8` | 2025-04-24 |
|
||||
| `0.8.10` | 2025-04-24 |
|
||||
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary><b>Complete Demo</b> (click to show)</summary>
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was last tested on 2024 March 12 against XMLDOM `0.8.10`
|
||||
|
||||
:::
|
||||
|
||||
1) Install SheetJS and XMLDOM libraries:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @xmldom/xmldom@0.9.5`}
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @xmldom/xmldom@0.8.10`}
|
||||
</CodeBlock>
|
||||
|
||||
2) Download [the sample script `SheetJSXMLDOM.js`](pathname:///dom/SheetJSXMLDOM.js):
|
||||
@ -292,24 +280,19 @@ can be shimmed, but it is strongly recommended to use a more compliant library.
|
||||
[`SheetJSCheerio.js`](pathname:///dom/SheetJSCheerio.js) implements the missing
|
||||
features to ensure that SheetJS DOM methods can process TABLE elements.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| CheerioJS | Date |
|
||||
|:--------------|:-----------|
|
||||
| `1.0.0` | 2025-04-24 |
|
||||
| `1.0.0-rc.12` | 2025-04-24 |
|
||||
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary><b>Complete Demo</b> (click to show)</summary>
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was last tested on 2024 March 12 against Cheerio `1.0.0-rc.12`
|
||||
|
||||
:::
|
||||
|
||||
1) Install SheetJS and CheerioJS libraries:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz cheerio@1.0.0`}
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz cheerio@1.0.0-rc.12`}
|
||||
</CodeBlock>
|
||||
|
||||
2) Download [the sample script `SheetJSCheerio.js`](pathname:///dom/SheetJSCheerio.js):
|
||||
@ -339,7 +322,7 @@ The script will create a file `SheetJSCheerio.xlsx` that can be opened.
|
||||
### DenoDOM
|
||||
|
||||
[DenoDOM](https://deno.land/x/deno_dom) provides a DOM framework for Deno. For
|
||||
the tested version (`0.1.48`), the following patches were needed:
|
||||
the tested version (`0.1.46`), the following patches were needed:
|
||||
|
||||
- TABLE `rows` property (explained above)
|
||||
- TR `cells` property (explained above)
|
||||
@ -350,7 +333,7 @@ This example fetches [a sample table](pathname:///dom/SheetJSTable.html):
|
||||
// @deno-types="https://cdn.sheetjs.com/xlsx-${current}/package/types/index.d.ts"
|
||||
import * as XLSX from 'https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs';
|
||||
\n\
|
||||
import { DOMParser } from 'https://deno.land/x/deno_dom@v0.1.48/deno-dom-wasm.ts';
|
||||
import { DOMParser } from 'https://deno.land/x/deno_dom@v0.1.46/deno-dom-wasm.ts';
|
||||
\n\
|
||||
const doc = new DOMParser().parseFromString(
|
||||
await (await fetch('https://docs.sheetjs.com/dom/SheetJSTable.html')).text(),
|
||||
@ -369,24 +352,20 @@ const workbook = XLSX.utils.table_to_book(tbl);
|
||||
XLSX.writeFile(workbook, "SheetJSDenoDOM.xlsx");`}
|
||||
</CodeBlock>
|
||||
|
||||
<details>
|
||||
<summary><b>Complete Demo</b> (click to show)</summary>
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Architecture | DenoDOM | Deno | Date |
|
||||
|:-------------|:--------|:-------|:-----------|
|
||||
| `darwin-x64` | 0.1.48 | 2.2.6 | 2025-03-31 |
|
||||
| `darwin-arm` | 0.1.48 | 2.2.12 | 2025-04-24 |
|
||||
| `win11-x64` | 0.1.48 | 2.0.4 | 2024-10-30 |
|
||||
| `win11-arm` | 0.1.48 | 2.2.1 | 2025-02-23 |
|
||||
| `linux-x64` | 0.1.48 | 2.0.5 | 2025-01-10 |
|
||||
| `linux-arm` | 0.1.48 | 2.1.10 | 2025-02-16 |
|
||||
| `darwin-x64` | 0.1.46 | 1.44.4 | 2024-06-19 |
|
||||
| `darwin-arm` | 0.1.46 | 1.44.4 | 2024-06-19 |
|
||||
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary><b>Complete Demo</b> (click to show)</summary>
|
||||
|
||||
1) Save the previous codeblock to `SheetJSDenoDOM.ts`.
|
||||
|
||||
2) Run the script with `--allow-net` and `--allow-write` entitlements:
|
||||
@ -397,16 +376,6 @@ deno run --allow-net --allow-write SheetJSDenoDOM.ts
|
||||
|
||||
The script will create a file `SheetJSDenoDOM.xlsx` that can be opened.
|
||||
|
||||
:::caution pass
|
||||
|
||||
Deno 2 additionally requires the `--allow-import` entitlement:
|
||||
|
||||
```bash
|
||||
deno run --allow-net --allow-write --allow-import SheetJSDenoDOM.ts
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
</details>
|
||||
|
||||
[^1]: See [`table_to_sheet` in "HTML" Utilities](/docs/api/utilities/html#create-new-sheet)
|
||||
|
@ -16,13 +16,7 @@ With a familiar UI, `x-spreadsheet` is an excellent choice for a modern editor.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Browser | Date |
|
||||
|:-------------|:-----------|
|
||||
| Chromium 131 | 2024-12-31 |
|
||||
| Safari 18.2 | 2024-12-31 |
|
||||
| Konqueror 22 | 2025-04-23 |
|
||||
This demo was last verified on 2024 April 25.
|
||||
|
||||
:::
|
||||
|
||||
|
@ -15,13 +15,7 @@ with a straightforward API.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Browser | Date |
|
||||
|:-------------|:-----------|
|
||||
| Chromium 131 | 2024-12-31 |
|
||||
| Safari 18.2 | 2024-12-31 |
|
||||
| Konqueror 22 | 2025-04-23 |
|
||||
This demo was last verified on 2024 April 25.
|
||||
|
||||
:::
|
||||
|
||||
|
@ -24,8 +24,7 @@ This demo was tested in the following deployments:
|
||||
|
||||
| Browser | Version | Date |
|
||||
|:-------------|:--------|:-----------|
|
||||
| Chromium 133 | `6.3.1` | 2025-03-31 |
|
||||
| Konqueror 22 | `6.3.1` | 2025-04-23 |
|
||||
| Chromium 125 | `6.2.1` | 2024-06-13 |
|
||||
|
||||
:::
|
||||
|
||||
@ -46,7 +45,7 @@ installation instructions for projects using a framework.
|
||||
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/shim.min.js"></script>
|
||||
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js"></script>
|
||||
<!-- Tabulator must be loaded after SheetJS scripts -->
|
||||
<script type="text/javascript" src="https://unpkg.com/tabulator-tables@6.3.1/dist/js/tabulator.min.js"></script>
|
||||
<script type="text/javascript" src="https://unpkg.com/tabulator-tables@6.2.1/dist/js/tabulator.min.js"></script>
|
||||
```
|
||||
|
||||
:::
|
||||
@ -140,7 +139,7 @@ function export_xlsx() {
|
||||
</script>
|
||||
```
|
||||
|
||||
[The official documentation](https://tabulator.info/docs/6.3/download#xlsx)
|
||||
[The official documentation](https://tabulator.info/docs/6.2/download#xlsx)
|
||||
covers supported options.
|
||||
|
||||
#### Post-processing
|
||||
|
@ -28,8 +28,7 @@ This demo was tested in the following deployments:
|
||||
|
||||
| Browser | Version | Date |
|
||||
|:-------------|:--------|:-----------|
|
||||
| Chromium 135 | `1.4.3` | 2025-04-23 |
|
||||
| Konqueror 22 | `1.4.3` | 2025-04-23 |
|
||||
| Chromium 125 | `1.4.0` | 2024-06-13 |
|
||||
|
||||
:::
|
||||
|
||||
@ -41,7 +40,7 @@ installation in ViteJS projects using Vue 3 Table Lite.
|
||||
Using the `npm` tool, this command installs SheetJS and Vue 3 Table Lite:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz vue3-table-lite@1.4.3`}
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz vue3-table-lite@1.4.0`}
|
||||
</CodeBlock>
|
||||
|
||||
#### Rows and Columns Bindings
|
||||
@ -137,7 +136,7 @@ cd sheetjs-vtl
|
||||
2) Install dependencies:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz vue3-table-lite@1.4.3`}
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz vue3-table-lite@1.4.0`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Download [`src/App.vue`](pathname:///vtl/App.vue) and replace the contents:
|
||||
|
@ -30,28 +30,7 @@ This demo was tested in the following environments:
|
||||
|
||||
| Browser | Version | Date |
|
||||
|:-------------|:--------|:-----------|
|
||||
| Chromium 135 | `5.3.2` | 2025-04-23 |
|
||||
| Konqueror 22 | `5.3.2` | 2025-04-23 |
|
||||
|
||||
:::
|
||||
|
||||
:::danger pass
|
||||
|
||||
**Glide Data Grid is not compatible with ReactJS 19!**
|
||||
|
||||
When trying to install in a new project, `npm install` will fail:
|
||||
|
||||
```
|
||||
npm error Found: react@19.1.0
|
||||
npm error node_modules/react
|
||||
npm error react@"^19.0.0" from the root project
|
||||
npm error
|
||||
npm error Could not resolve dependency:
|
||||
npm error peer react@"^16.12.0 || 17.x || 18.x" from @glideapps/glide-data-grid@5.3.2
|
||||
npm error node_modules/@glideapps/glide-data-grid
|
||||
```
|
||||
|
||||
This demo explicitly uses ReactJS 18.
|
||||
| Chromiun 125 | `5.3.2` | 2024-06-09 |
|
||||
|
||||
:::
|
||||
|
||||
@ -365,19 +344,13 @@ cd sheetjs-gdg
|
||||
npm i
|
||||
```
|
||||
|
||||
2) Explicitly downgrade ReactJS to version 18:
|
||||
|
||||
```bash
|
||||
npm i --save react@18 react-dom@18
|
||||
```
|
||||
|
||||
3) Install SheetJS and Glide Data Grid libraries:
|
||||
2) Install SheetJS and Glide Data Grid libraries:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @glideapps/glide-data-grid@5.3.2`}
|
||||
</CodeBlock>
|
||||
|
||||
4) Start the dev server:
|
||||
3) Start dev server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
@ -386,21 +359,21 @@ npm run dev
|
||||
The terminal window will display a URL (typically `http://localhost:5173`).
|
||||
Open the URL with a web browser and confirm that a page loads.
|
||||
|
||||
5) Download [`App.tsx`](pathname:///gdg/App.tsx) and replace `src/App.tsx`:
|
||||
4) Download [`App.tsx`](pathname:///gdg/App.tsx) and replace `src/App.tsx`:
|
||||
|
||||
```bash
|
||||
curl -L -o src/App.tsx https://docs.sheetjs.com/gdg/App.tsx
|
||||
```
|
||||
|
||||
#### Testing
|
||||
**Testing**
|
||||
|
||||
6) Refresh the browser window. A grid should be displayed:
|
||||
5) Refresh the browser window. A grid should be displayed:
|
||||
|
||||

|
||||
|
||||
The demo downloads and processes https://docs.sheetjs.com/pres.numbers .
|
||||
|
||||
7) Make some changes to the grid data.
|
||||
6) Make some changes to the grid data.
|
||||
|
||||
:::note pass
|
||||
|
||||
@ -415,14 +388,14 @@ values should be 41, 42, 43, 44, and 45, as shown in the screenshot below:
|
||||
|
||||

|
||||
|
||||
8) Click on the "Export" button. The browser should attempt to download a XLSX
|
||||
7) Click on the "Export" button. The browser should attempt to download a XLSX
|
||||
file (`sheetjs-gdg.xlsx`). Save the file.
|
||||
|
||||
Open the generated file and verify the contents match the grid.
|
||||
|
||||
9) Reload the page. The contents will revert back to the original table.
|
||||
8) Reload the page. The contents will revert back to the original table.
|
||||
|
||||
10) Click "Choose File" and select the new `sheetjs-gdg.xlsx` file. The table
|
||||
9) Click "Choose File" and select the new `sheetjs-gdg.xlsx` file. The table
|
||||
should update with the data in the file.
|
||||
|
||||
[^1]: See ["Array of Objects" in the ReactJS demo](/docs/demos/frontend/react#array-of-objects)
|
||||
|
@ -26,11 +26,10 @@ user-supplied sheets and exports data to XLSX workbooks:
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Browser | Version | Date | Notes |
|
||||
|:-------------|:----------------|:-----------|:----------------------------|
|
||||
| Chromium 135 | `7.0.0-beta.19` | 2025-04-23 | Requires ReactJS 18 |
|
||||
| Chromium 135 | `7.0.0-beta.52` | 2025-04-23 | No edit support |
|
||||
| Konqueror 22 | `7.0.0-beta.52` | 2025-04-23 | No edit support, CSS errors |
|
||||
| Version | Date | Notes |
|
||||
|:----------------|:-----------|:---------------------|
|
||||
| `7.0.0-beta.19` | 2024-06-09 | |
|
||||
| `7.0.0-beta.44` | 2024-06-09 | Editing did not work |
|
||||
|
||||
:::
|
||||
|
||||
@ -39,8 +38,7 @@ This demo was tested in the following environments:
|
||||
When this demo was last tested against the latest version, the grid correctly
|
||||
displayed data but data could not be edited by the user.
|
||||
|
||||
The current recommendation is to use version `7.0.0-beta.19` and to forcefully
|
||||
downgrade ReactJS to version 18.
|
||||
The current recommendation is to use version `7.0.0-beta.19`.
|
||||
|
||||
:::
|
||||
|
||||
@ -161,18 +159,6 @@ cd sheetjs-rdg
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-data-grid@7.0.0-beta.19`}
|
||||
</CodeBlock>
|
||||
|
||||
<details>
|
||||
<summary><b>Installing RDG version that supports editing</b> (click to show)</summary>
|
||||
|
||||
Editing support requires ReactJS 18 and React DataGrid version `7.0.0-beta.19`:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i -S react@18 react-dom@18
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-data-grid@7.0.0-beta.19`}
|
||||
</CodeBlock>
|
||||
|
||||
</details>
|
||||
|
||||
3) Download [`App.tsx`](pathname:///rdg/App.tsx) and replace `src/App.tsx`.
|
||||
|
||||
```bash
|
||||
@ -188,30 +174,6 @@ npm run dev
|
||||
The terminal window will display a URL (typically `http://localhost:5173`).
|
||||
Open the URL with a web browser and confirm that a page loads.
|
||||
|
||||
:::caution pass
|
||||
|
||||
**There were breaking changes in point releases of React DataGrid!**
|
||||
|
||||
The JavaScript console may show an error message referencing `default`:
|
||||
|
||||
```
|
||||
Uncaught SyntaxError: The requested module '/node_modules/.vite/deps/react-data-grid.js?v=f9b1b87a' does not provide an export named 'default' (at App.tsx:2:8)
|
||||
```
|
||||
|
||||
In a point release, `DataGrid` was moved from the default export to a named
|
||||
export. The `src/App.tsx` script must be edited to reflect the change:
|
||||
|
||||
```js title="src/App.tsx (edit highlighted lines)"
|
||||
import React, { useEffect, useState, ChangeEvent } from "react";
|
||||
// highlight-next-line
|
||||
import { textEditor, Column, DataGrid } from "react-data-grid";
|
||||
import { read, utils, WorkSheet, writeFile } from "xlsx";
|
||||
```
|
||||
|
||||
After updating the script, the webpage must be manually refreshed.
|
||||
|
||||
:::
|
||||
|
||||
#### Testing
|
||||
|
||||
5) Confirm the table shows a list of Presidents.
|
||||
|
@ -141,7 +141,7 @@ The following example JSX shows a table using HTML and using MUI components:
|
||||
### Exporting Data
|
||||
|
||||
The SheetJS `table_to_book` method[^3] can parse data from a DOM element.
|
||||
The MUI `Table` element is really an HTML TABLE element under the hood. A `ref`
|
||||
The MUI `Table` element is really a HTML TABLE element under the hood. A `ref`
|
||||
attached to the `Table` element can be processed by `table_to_book`.
|
||||
|
||||
The following snippet uses the `writeFileXLSX` method[^4] to generate and
|
||||
@ -184,10 +184,9 @@ export default function MUITableSheetJSExport() {
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Browser | Material UI | Emotion | Date |
|
||||
|:-------------|:------------|:----------|:-----------|
|
||||
| Chromium 135 | `7.0.2` | `11.11.4` | 2025-04-23 |
|
||||
| Konqueror 22 | `7.0.2` | `11.11.4` | 2025-04-23 |
|
||||
| Material UI | Emotion | Date |
|
||||
|:------------|:----------|:-----------|
|
||||
| `5.15.20` | `11.11.4` | 2024-06-12 |
|
||||
|
||||
:::
|
||||
|
||||
@ -201,7 +200,7 @@ cd sheetjs-mui
|
||||
2) Install dependencies:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @mui/material@7.0.2 @emotion/react@11.11.4 @emotion/styled@11.11.5`}
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @mui/material@5.15.20 @emotion/react@11.11.4 @emotion/styled@11.11.5`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Download [`App.tsx`](pathname:///mui/table/App.tsx) and replace `src/App.tsx`.
|
||||
@ -216,14 +215,8 @@ curl -L -o src/App.tsx https://docs.sheetjs.com/mui/table/App.tsx
|
||||
npm run dev
|
||||
```
|
||||
|
||||
The terminal window will display a URL (typically `http://localhost:5173`).
|
||||
Open the URL with a web browser and confirm that a page loads.
|
||||
|
||||
Confirm the table shows a list of Presidents. When the page loads, it will fetch
|
||||
https://docs.sheetjs.com/pres.numbers, parse with SheetJS, and load the data in
|
||||
the data grid.
|
||||
|
||||
Click the "Export" button and open the generated file in a spreadsheet editor.
|
||||
The script should open the live demo in a web browser. Click the "Export" button
|
||||
to save the file. Open the generated file in a spreadsheet editor.
|
||||
|
||||
## Material UI Data Grid
|
||||
|
||||
@ -337,10 +330,9 @@ export default function App() {
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Browser | Data Grid | Emotion | Date |
|
||||
|:-------------|:----------|:----------|:-----------|
|
||||
| Chromium 125 | `8.0.0` | `11.11.4` | 2025-04-23 |
|
||||
| Konqueror 22 | `8.0.0` | `11.11.4` | 2025-04-23 |
|
||||
| Data Grid | Emotion | Date |
|
||||
|:----------|:----------|:-----------|
|
||||
| `7.6.2` | `11.11.4` | 2024-06-12 |
|
||||
|
||||
:::
|
||||
|
||||
@ -354,7 +346,7 @@ cd sheetjs-muidg
|
||||
2) Install dependencies:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @mui/x-data-grid@8.0.0 @emotion/react@11.11.4 @emotion/styled@11.11.5`}
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @mui/x-data-grid@7.6.2 @emotion/react@11.11.4 @emotion/styled@11.11.5`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Download [`App.tsx`](pathname:///mui/dg/App.tsx) and replace `src/App.tsx`.
|
||||
@ -369,30 +361,8 @@ curl -L -o src/App.tsx https://docs.sheetjs.com/mui/dg/App.tsx
|
||||
npm run dev
|
||||
```
|
||||
|
||||
The terminal window will display a URL (typically `http://localhost:5173`).
|
||||
Open the URL with a web browser and confirm that a page loads.
|
||||
|
||||
When the page loads, it will process https://docs.sheetjs.com/pres.numbers
|
||||
|
||||
:::caution pass
|
||||
|
||||
The data grid uses nascent ECMAScript features that are not supported in older
|
||||
browsers. Shims can be added in the `<head>` section of `index.html`:
|
||||
|
||||
```html title="index.html (add highlighted lines in HEAD)"
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<!-- highlight-start -->
|
||||
<script>
|
||||
/* workarounds for legacy browsers */
|
||||
if(!Object.hasOwn) Object.hasOwn = function(o, v) { return o.hasOwnProperty(v); };
|
||||
if(!Array.prototype.at) Array.prototype.at = function(idx) { return this[idx < 0 ? idx + this.length : idx]; };
|
||||
</script>
|
||||
<!-- highlight-end -->
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
[^1]: See ["Sheet Objects"](/docs/csf/sheet)
|
||||
[^2]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
[^3]: See [`table_to_book` in "HTML" Utilities](/docs/api/utilities/html#create-new-sheet)
|
||||
|
@ -133,8 +133,8 @@ This demo was tested in the following environments:
|
||||
|
||||
| Lume | Date |
|
||||
|:---------|:-----------|
|
||||
| `1.19.4` | 2025-01-02 |
|
||||
| `2.4.3` | 2025-01-02 |
|
||||
| `1.19.4` | 2024-03-16 |
|
||||
| `2.1.2` | 2024-03-16 |
|
||||
|
||||
This example uses the Nunjucks template format. Lume plugins support additional
|
||||
template formats, including Markdown and JSX.
|
||||
@ -150,17 +150,15 @@ template formats, including Markdown and JSX.
|
||||
```bash
|
||||
mkdir -p sheetjs-lume
|
||||
cd sheetjs-lume
|
||||
deno run -Ar https://deno.land/x/lume@v2.4.3/init.ts
|
||||
deno run -Ar https://deno.land/x/lume@v2.1.2/init.ts
|
||||
```
|
||||
|
||||
When prompted, enter the following options. The initialization script has
|
||||
changed over time, so not all questions will be asked.
|
||||
When prompted, enter the following options:
|
||||
|
||||
- `What kind of setup do you want?`: select `Basic + plugins`
|
||||
- `Choose the configuration file format`: select `_config.ts`
|
||||
- `Do you want to install some plugins now?`: select `Yes`
|
||||
- `Select the plugins to install`: select `sheets` and `nunjucks`
|
||||
- `Do you want to setup a CMS?`: select `No` or `Maybe later`
|
||||
- `Do you want to setup a CMS?`: select `Maybe later`
|
||||
|
||||
The project will be configured and modules will be installed.
|
||||
|
||||
@ -228,7 +226,7 @@ deno task lume
|
||||
|
||||
This will create a static site in the `_site` folder
|
||||
|
||||
8) Test the generated site by starting a web server:
|
||||
7) Test the generated site by starting a web server:
|
||||
|
||||
```bash
|
||||
npx http-server _site
|
||||
|
@ -53,24 +53,12 @@ overridden through a `package.json` override in the latest versions of NodeJS:
|
||||
|
||||
:::danger Telemetry
|
||||
|
||||
Older versions of GatsbyJS collect telemetry by default. The `telemetry`
|
||||
subcommand can disable telemetry:
|
||||
GatsbyJS collects telemetry by default. The `telemetry` subcommand can disable it:
|
||||
|
||||
```js
|
||||
npx gatsby telemetry --disable
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
GatsbyJS `5.14.1` does not transmit telemetry:
|
||||
|
||||
```text title="Expected output in GatsbyJS 5.14.1"
|
||||
Telemetry is no longer gathered and is always disabled
|
||||
```
|
||||
|
||||
It is still strongly encouraged to disable telemetry since older projects may
|
||||
use a GatsbyJS version that embeds telemetry.
|
||||
|
||||
:::
|
||||
|
||||
## Integration Details
|
||||
@ -191,8 +179,8 @@ This demo was tested in the following environments:
|
||||
|
||||
| GatsbyJS | Date |
|
||||
|:---------|:-----------|
|
||||
| `5.14.1` | 2025-01-19 |
|
||||
| `4.25.8` | 2025-01-02 |
|
||||
| `5.13.4` | 2024-05-04 |
|
||||
| `4.25.8` | 2024-03-27 |
|
||||
|
||||
:::
|
||||
|
||||
@ -233,8 +221,8 @@ npx gatsby new sheetjs-gatsby
|
||||
|
||||
For older Gatsby versions, the project must be built from the starter project.
|
||||
|
||||
The starter commit for GatsbyJS 4 is `6bc4466090845f20650117b3d27e68e6e46dc8d5`.
|
||||
This version of the starter can be fetched with `git`:
|
||||
For GatsbyJS 4, the starter commit is `6bc4466090845f20650117b3d27e68e6e46dc8d5`
|
||||
and the steps are shown below:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/gatsbyjs/gatsby-starter-default sheetjs-gatsby
|
||||
@ -246,7 +234,7 @@ cd ..
|
||||
|
||||
:::
|
||||
|
||||
2) Start the local development server:
|
||||
2) Follow the on-screen instructions for starting the local development server:
|
||||
|
||||
```bash
|
||||
cd sheetjs-gatsby
|
||||
@ -269,7 +257,7 @@ Open a web browser to the displayed URL (typically `http://localhost:8000/`)
|
||||
`}
|
||||
</CodeBlock>
|
||||
|
||||
4) Install the SheetJS library and GatsbyJS plugins:
|
||||
4) Install the library and plugins:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
@ -341,18 +329,14 @@ If the `plugins` array exists, the two plugins should be added at the beginning:
|
||||
|
||||
:::
|
||||
|
||||
7) Stop and restart the development server process:
|
||||
|
||||
```bash
|
||||
npm run develop
|
||||
```
|
||||
Stop and restart the development server process (`npm run develop`).
|
||||
|
||||
### GraphiQL test
|
||||
|
||||
8) Open the GraphiQL editor in a web browser. The output of the previous step
|
||||
displayed the URL (typically `http://localhost:8000/___graphql` ).
|
||||
7) Open the GraphiQL editor. The output of the previous step displayed the URL
|
||||
(typically `http://localhost:8000/___graphql` )
|
||||
|
||||
9) Paste the following query into the code editor:
|
||||
There is an editor in the left pane. Paste the following query into the editor:
|
||||
|
||||
```graphql title="GraphQL Query (paste into editor)"
|
||||
{
|
||||
@ -374,7 +358,7 @@ Press the Execute Query button (`▶`) and data should show up in the right pane
|
||||
<details>
|
||||
<summary><b>Sample Output</b> (click to show)</summary>
|
||||
|
||||
In GatsbyJS versions `5.13.4` and `4.25.8`, the raw output was:
|
||||
In GatsbyJS version `5.13.4`, the raw output was:
|
||||
|
||||
```json title="GraphQL query result from GatsbyJS 5.13.4"
|
||||
{
|
||||
@ -422,7 +406,7 @@ In GatsbyJS versions `5.13.4` and `4.25.8`, the raw output was:
|
||||
|
||||
### React page
|
||||
|
||||
10) Create a new page `src/pages/pres.js` that displays the raw query result:
|
||||
8) Create a new file `src/pages/pres.js` that uses the query and displays the result:
|
||||
|
||||
```jsx title="src/pages/pres.js (create new file)"
|
||||
import { graphql } from "gatsby"
|
||||
@ -445,10 +429,8 @@ const PageComponent = ({data}) => {
|
||||
export default PageComponent;
|
||||
```
|
||||
|
||||
Save the file.
|
||||
|
||||
11) Access the `/pres` page (typically `http://localhost:8000/pres` ) in a web
|
||||
browser. The displayed JSON is the data that the component receives:
|
||||
After saving the file, access `http://localhost:8000/pres` in the browser. The
|
||||
displayed JSON is the data that the component receives:
|
||||
|
||||
```js title="Expected contents of /pres"
|
||||
{
|
||||
@ -463,7 +445,7 @@ browser. The displayed JSON is the data that the component receives:
|
||||
// ....
|
||||
```
|
||||
|
||||
12) Change `PageComponent` to display a table based on the data:
|
||||
9) Change `PageComponent` to display a table based on the data:
|
||||
|
||||
```jsx title="src/pages/pres.js (replace PageComponent)"
|
||||
import { graphql } from "gatsby"
|
||||
@ -502,7 +484,7 @@ Going back to the browser, `http://localhost:8000/pres` will show a table:
|
||||
|
||||
### Live refresh
|
||||
|
||||
13) Open the file `src/data/pres.xlsx` in Excel or another spreadsheet editor.
|
||||
10) Open the file `src/data/pres.xlsx` in Excel or another spreadsheet editor.
|
||||
Add a new row at the end of the file, setting cell `A7` to "SheetJS Dev" and
|
||||
cell `B7` to `47`. The sheet should look like the following screenshot:
|
||||
|
||||
@ -514,7 +496,7 @@ Save the file and observe that the table has refreshed with the new data:
|
||||
|
||||
### Static site
|
||||
|
||||
14) Stop the development server and build the site:
|
||||
11) Stop the development server and build the site:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
@ -523,6 +505,13 @@ npm run build
|
||||
The build output will confirm that the `/pres` route is static:
|
||||
|
||||
```text title="Output from GatsbyJS build process"
|
||||
Pages
|
||||
|
||||
┌ src/pages/404.js
|
||||
│ ├ /404/
|
||||
│ └ /404.html
|
||||
├ src/pages/index.js
|
||||
│ └ /
|
||||
└ src/pages/pres.js
|
||||
└ /pres/
|
||||
|
||||
@ -538,7 +527,7 @@ The build output will confirm that the `/pres` route is static:
|
||||
|
||||
The generated page will be placed in `public/pres/index.html`.
|
||||
|
||||
15) Open `public/pres/index.html` with a text editor and search for "SheetJS".
|
||||
12) Open `public/pres/index.html` with a text editor and search for "SheetJS".
|
||||
There will be a HTML row:
|
||||
|
||||
```html title="public/pres/index.html (Expected contents)"
|
||||
|
@ -69,9 +69,9 @@ flowchart LR
|
||||
aoo --> |app.js\nfrontend code| html
|
||||
```
|
||||
|
||||
### ESBuild Configuration
|
||||
### ESBuild Config
|
||||
|
||||
Plugins can be referenced in the `plugins` array of the build configuration:
|
||||
Plugins can be referenced in the `plugins` array of the build config object:
|
||||
|
||||
```js title="build.mjs (structure)"
|
||||
import * as esbuild from 'esbuild'
|
||||
@ -204,23 +204,19 @@ This demo was tested in the following environments:
|
||||
|
||||
| `esbuild` | Date |
|
||||
|:----------|:-----------|
|
||||
| `0.24.2` | 2025-01-07 |
|
||||
| `0.23.1` | 2025-01-07 |
|
||||
| `0.22.0` | 2025-01-07 |
|
||||
| `0.21.5` | 2025-01-07 |
|
||||
| `0.20.2` | 2025-01-07 |
|
||||
| `0.19.12` | 2025-01-07 |
|
||||
| `0.18.20` | 2025-01-07 |
|
||||
| `0.17.19` | 2025-01-07 |
|
||||
| `0.16.17` | 2025-01-07 |
|
||||
| `0.15.18` | 2025-01-07 |
|
||||
| `0.14.54` | 2025-01-07 |
|
||||
| `0.13.15` | 2025-01-07 |
|
||||
| `0.12.29` | 2025-01-07 |
|
||||
| `0.11.23` | 2025-01-07 |
|
||||
| `0.10.2` | 2025-01-07 |
|
||||
| `0.9.7` | 2025-01-07 |
|
||||
| `0.9.1` | 2025-01-07 |
|
||||
| `0.20.2` | 2024-04-07 |
|
||||
| `0.19.12` | 2024-04-07 |
|
||||
| `0.18.20` | 2024-04-07 |
|
||||
| `0.17.19` | 2024-04-07 |
|
||||
| `0.16.17` | 2024-04-07 |
|
||||
| `0.15.18` | 2024-04-07 |
|
||||
| `0.14.54` | 2024-04-07 |
|
||||
| `0.13.15` | 2024-04-07 |
|
||||
| `0.12.29` | 2024-04-07 |
|
||||
| `0.11.23` | 2024-04-07 |
|
||||
| `0.10.2` | 2024-04-07 |
|
||||
| `0.9.7` | 2024-04-07 |
|
||||
| `0.9.1` | 2024-04-07 |
|
||||
|
||||
:::
|
||||
|
||||
|
@ -169,7 +169,7 @@ export default defineConfig({
|
||||
plugins: [
|
||||
{ // this plugin handles ?sheetjs tags
|
||||
name: "vite-sheet",
|
||||
transform(_code, id) {
|
||||
transform(code, id) {
|
||||
if(!id.match(/\?sheetjs$/)) return;
|
||||
var wb = read(readFileSync(id.replace(/\?sheetjs$/, "")));
|
||||
var data = utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
||||
@ -243,7 +243,7 @@ export default defineConfig({
|
||||
plugins: [
|
||||
{ // this plugin handles ?html tags
|
||||
name: "vite-sheet-html",
|
||||
transform(_code, id) {
|
||||
transform(code, id) {
|
||||
if(!id.match(/\?html/)) return;
|
||||
var wb = read(readFileSync(id.replace(/\?html/, "")));
|
||||
var html = utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]);
|
||||
@ -309,7 +309,7 @@ export default defineConfig({
|
||||
plugins: [
|
||||
{ // this plugin handles ?b64 tags
|
||||
name: "vite-b64-plugin",
|
||||
transform(_code, id) {
|
||||
transform(code, id) {
|
||||
if(!id.match(/\?b64$/)) return;
|
||||
var path = id.replace(/\?b64/, "");
|
||||
var data = readFileSync(path, "base64");
|
||||
@ -356,11 +356,10 @@ This demo was tested in the following environments:
|
||||
|
||||
| ViteJS | Date |
|
||||
|:---------|:-----------|
|
||||
| `6.2.3` | 2025-03-30 |
|
||||
| `5.4.15` | 2025-03-30 |
|
||||
| `4.5.10` | 2025-03-30 |
|
||||
| `3.2.11` | 2025-03-30 |
|
||||
| `2.9.18` | 2025-03-30 |
|
||||
| `5.2.12` | 2024-06-02 |
|
||||
| `4.5.3` | 2024-06-02 |
|
||||
| `3.2.10` | 2024-06-02 |
|
||||
| `2.9.18` | 2024-06-02 |
|
||||
|
||||
:::
|
||||
|
||||
@ -642,5 +641,5 @@ embedded in the final site and the data is parsed when the page is loaded.
|
||||
[^6]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
[^7]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^8]: See [the "base64" type in "Reading Files"](/docs/api/parse-options#input-type)
|
||||
[^9]: See [`examples/sheetjs-vite`](https://git.sheetjs.com/examples/sheetjs-vite/) on the SheetJS Git server.
|
||||
[^9]: See [`examples/sheetjs-vite`](https://git.sheetjs.com/examples/sheetjs-vite/) on the SheetJS git server.
|
||||
[^10]: See ["Server-Side Rendering"](https://vitejs.dev/guide/ssr.html) in the ViteJS documentation.
|
@ -55,7 +55,7 @@ flowchart LR
|
||||
aoo --> |src/index.js\nfrontend code| html
|
||||
```
|
||||
|
||||
### Webpack Configuration
|
||||
### Webpack Config
|
||||
|
||||
The Webpack configuration is normally saved to `webpack.config.js`.
|
||||
|
||||
@ -184,11 +184,7 @@ document.body.appendChild(elt);
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Version | Date |
|
||||
|:---------|:-----------|
|
||||
| `5.97.1` | 2025-01-08 |
|
||||
This demo was last tested on 2024 April 06 against Webpack 5.91.0
|
||||
|
||||
:::
|
||||
|
||||
@ -200,7 +196,7 @@ This demo was tested in the following deployments:
|
||||
mkdir sheetjs-wp5
|
||||
cd sheetjs-wp5
|
||||
npm init -y
|
||||
npm install webpack@5.97.1 webpack-cli@6.0.1 webpack-dev-server@5.2.0 --save
|
||||
npm install webpack@5.91.0 webpack-cli@5.1.4 webpack-dev-server@5.0.4 --save
|
||||
mkdir -p dist
|
||||
mkdir -p src
|
||||
mkdir -p data
|
||||
|
@ -114,10 +114,10 @@ accessed using the variable `pres` in a template:
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Eleventy | Date |
|
||||
|:---------|:-----------|
|
||||
| `2.0.1` | 2024-12-23 |
|
||||
| `3.0.0` | 2024-12-23 |
|
||||
| Eleventy | Date |
|
||||
|:----------------|:-----------|
|
||||
| `2.0.1` | 2024-03-15 |
|
||||
| `3.0.0-alpha.5` | 2024-03-15 |
|
||||
|
||||
:::
|
||||
|
||||
@ -134,17 +134,17 @@ npm init -y
|
||||
2) Install Eleventy and SheetJS libraries:
|
||||
|
||||
<Tabs groupId="11ty">
|
||||
<TabItem value="2" label="2.x">
|
||||
<TabItem value="2" label="Stable">
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @11ty/eleventy@2.0.1`}
|
||||
</CodeBlock>
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3" label="3.x">
|
||||
<TabItem value="3" label="Alpha">
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @11ty/eleventy@3.0.0`}
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @11ty/eleventy@3.0.0-alpha.5`}
|
||||
</CodeBlock>
|
||||
|
||||
</TabItem>
|
||||
|
@ -75,13 +75,12 @@ This demo was tested in the following environments:
|
||||
|
||||
| NextJS | NodeJS | Date |
|
||||
|:----------|:----------|:-----------|
|
||||
| ` 9.5.5` | `16.20.2` | 2025-04-24 |
|
||||
| `10.2.3` | `16.20.2` | 2025-04-24 |
|
||||
| `11.1.4` | `16.20.2` | 2025-04-24 |
|
||||
| `12.3.7` | `20.14.0` | 2025-04-24 |
|
||||
| `13.5.11` | `20.14.0` | 2025-04-24 |
|
||||
| `14.2.28` | `20.14.0` | 2025-04-24 |
|
||||
| `15.3.1` | `20.18.0` | 2025-04-24 |
|
||||
| ` 9.5.5` | `16.20.2` | 2024-06-07 |
|
||||
| `10.2.3` | `16.20.2` | 2024-06-07 |
|
||||
| `11.1.4` | `16.20.2` | 2024-06-07 |
|
||||
| `12.3.4` | `20.14.0` | 2024-06-07 |
|
||||
| `13.5.6` | `20.14.0` | 2024-06-07 |
|
||||
| `14.2.3` | `20.14.0` | 2024-06-07 |
|
||||
|
||||
:::
|
||||
|
||||
@ -95,7 +94,7 @@ NextJS use legacy versions of ReactJS that do not support function components
|
||||
and other idioms.
|
||||
|
||||
[`examples/reactjs-legacy`](https://git.sheetjs.com/examples/reactjs-legacy) on
|
||||
the SheetJS Git server includes code samples for legacy NextJS versions.
|
||||
the SheetJS git server includes code samples for legacy NextJS versions.
|
||||
|
||||
:::
|
||||
|
||||
@ -114,7 +113,7 @@ but does not support live reloading.
|
||||
:::caution pass
|
||||
|
||||
When the demo was last tested, Turbopack did not support true raw loaders. For
|
||||
development use, the normal `npx -y next dev` should be used.
|
||||
development use, the normal `npx next dev` should be used.
|
||||
|
||||
:::
|
||||
|
||||
@ -584,7 +583,6 @@ npx -y next@13.5.6 telemetry status
|
||||
```bash
|
||||
mkdir sheetjs-next
|
||||
cd sheetjs-next
|
||||
npm init -y
|
||||
mkdir -p pages/sheets/
|
||||
```
|
||||
|
||||
@ -599,7 +597,7 @@ curl -LO https://docs.sheetjs.com/next/sheetjs.xlsx
|
||||
|
||||
:::note pass
|
||||
|
||||
The `next@13.5.6` dependency can be adjusted to pick a different version. For
|
||||
The `next@13.5.6` depefndency can be adjusted to pick a different version. For
|
||||
example, NextJS `12.3.4` is installed with
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
@ -667,7 +665,7 @@ cd ../..
|
||||
6) Test the deployment:
|
||||
|
||||
```bash
|
||||
npx -y next
|
||||
npx next
|
||||
```
|
||||
|
||||
Open a web browser and access:
|
||||
@ -693,7 +691,7 @@ After saving the file, the website should refresh with the new row.
|
||||
8) Stop the server and run a production build:
|
||||
|
||||
```bash
|
||||
npx -y next build
|
||||
npx next build
|
||||
```
|
||||
|
||||
The final output will show a list of the routes and types:
|
||||
@ -728,7 +726,7 @@ was changed to a stylized lowercase F (`ƒ`) in recent versions of NextJS.
|
||||
<TabItem value="13" label="NextJS 9 - 13">
|
||||
|
||||
```bash
|
||||
npx -y next export
|
||||
npx next export
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@ -753,7 +751,7 @@ module.exports = {
|
||||
After adding the line, run the `build` command:
|
||||
|
||||
```bash
|
||||
npx -y next build
|
||||
npx next build
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@ -793,7 +791,7 @@ After editing `next.config.js`, run the build command:
|
||||
</Tabs>
|
||||
|
||||
```bash
|
||||
npx -y next build
|
||||
npx next build
|
||||
```
|
||||
|
||||
Inspecting the output, there should be no lines with `λ` or `ƒ`:
|
||||
@ -816,7 +814,7 @@ Route (pages) Size First Load JS
|
||||
<TabItem value="13" label="NextJS 9 - 13">
|
||||
|
||||
```bash
|
||||
npx -y next export
|
||||
npx next export
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@ -834,7 +832,7 @@ module.exports = {
|
||||
After adding the line, run the `build` command:
|
||||
|
||||
```bash
|
||||
npx -y next build
|
||||
npx next build
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@ -845,7 +843,7 @@ The static site will be written to the `out` subfolder
|
||||
13) Serve the static site:
|
||||
|
||||
```bash
|
||||
npx -y http-server out
|
||||
npx http-server out
|
||||
```
|
||||
|
||||
The command will start a local HTTP server at `http://localhost:8080/` for
|
||||
@ -861,7 +859,7 @@ page was removed.
|
||||
[^7]: See [`getStaticProps`](https://nextjs.org/docs/pages/building-your-application/data-fetching/get-static-props) in the NextJS documentation.
|
||||
[^8]: See [`getStaticPaths`](https://nextjs.org/docs/pages/building-your-application/data-fetching/get-static-paths) in the NextJS documentation.
|
||||
[^9]: See [`getServerSideProps`](https://nextjs.org/docs/pages/building-your-application/data-fetching/get-server-side-props) in the NextJS documentation.
|
||||
[^10]: See [`fallback` in `getStaticPaths`](https://nextjs.org/docs/pages/api-reference/functions/get-static-paths#fallback-false) in the NextJS documentation.
|
||||
[^10]: See [`fallback` in getStaticPaths](https://nextjs.org/docs/pages/api-reference/functions/get-static-paths#fallback-false) in the NextJS documentation.
|
||||
[^11]: See [`sheet_to_html` in "Utilities"](/docs/api/utilities/html#html-table-output)
|
||||
[^12]: [`dangerouslySetInnerHTML`](https://react.dev/reference/react-dom/components/common#common-props) is a ReactJS prop supported for all built-in components.
|
||||
[^13]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output).
|
||||
|
@ -46,10 +46,10 @@ that process spreadsheets in the browser.
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Nuxt Content | Nuxt | Date |
|
||||
|:-------------|:-----------|:-----------|
|
||||
| `1.15.1` | `2.18.1` | 2025-04-23 |
|
||||
| `2.13.4` | `3.14.159` | 2024-11-14 |
|
||||
| Nuxt Content | Nuxt | Date |
|
||||
|:-------------|:---------|:-----------|
|
||||
| `1.15.1` | `2.17.3` | 2024-06-04 |
|
||||
| `2.12.1` | `3.11.2` | 2024-06-04 |
|
||||
|
||||
:::
|
||||
|
||||
@ -58,7 +58,7 @@ This demo was tested in the following environments:
|
||||
Nuxt embeds telemetry. According to the docs, it can be disabled with:
|
||||
|
||||
```bash
|
||||
npx -y nuxt telemetry disable
|
||||
npx nuxt telemetry disable
|
||||
```
|
||||
|
||||
**When the demo was last tested, this command did not work.**
|
||||
@ -268,7 +268,7 @@ The recommended solution is to switch to Node 18.
|
||||
1) Create a stock app:
|
||||
|
||||
```bash
|
||||
npx -y create-nuxt-app@4.0.0 sheetjs-nuxt
|
||||
npx create-nuxt-app@4.0.0 sheetjs-nuxt
|
||||
```
|
||||
|
||||
When prompted, enter the following options:
|
||||
@ -352,7 +352,7 @@ Replace the property with the following definition:
|
||||
|
||||
5) Replace `pages/index.vue` with the following:
|
||||
|
||||
```html title="pages/index.vue (replace contents)"
|
||||
```html title="pages/index.vue"
|
||||
<!-- sheetjs (C) 2013-present SheetJS -- https://sheetjs.com -->
|
||||
<template><div>
|
||||
<div v-for="item in data.data" v-bind:key="item.name">
|
||||
@ -412,7 +412,7 @@ This will create a static site in the `dist` folder.
|
||||
9) Serve the static site:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) in a web browser.
|
||||
@ -499,16 +499,6 @@ export default defineTransformer({
|
||||
The data object returned by the transformer must have the original `_id` key.
|
||||
The data is stored in the `body` property of the final object.
|
||||
|
||||
:::danger pass
|
||||
|
||||
When this demo was last tested, there were errors in the NuxtJS types.
|
||||
|
||||
**As this affects the official examples, this is a bug in NuxtJS!**
|
||||
|
||||
Until the bugs are fixed, type checking should be disabled.
|
||||
|
||||
:::
|
||||
|
||||
### Custom Modules
|
||||
|
||||
NuxtJS modules are the main mechanism for adding transformers to the pipeline.
|
||||
|
@ -49,11 +49,10 @@ flowchart LR
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| SvelteJS | Kit | Date |
|
||||
|:----------|:---------|:-----------|
|
||||
| `5.27.2` | `2.20.7` | 2025-04-17 |
|
||||
| `4.2.19` | `2.5.10` | 2025-04-17 |
|
||||
| `3.59.2` | `1.30.4` | 2025-04-17 |
|
||||
| SvelteJS | Kit | Date |
|
||||
|:-----------------|:---------|:-----------|
|
||||
| `4.2.17` | `2.5.10` | 2024-06-03 |
|
||||
| `5.0.0-next.149` | `2.5.10` | 2024-06-03 |
|
||||
|
||||
:::
|
||||
|
||||
@ -190,35 +189,7 @@ When this demo was last tested, SvelteKit required NodeJS major version 20.
|
||||
1) Create a new site:
|
||||
|
||||
```bash
|
||||
mkdir -p sheetjs-svelte
|
||||
cd sheetjs-svelte
|
||||
npx sv create
|
||||
```
|
||||
|
||||
When prompted:
|
||||
|
||||
- `Where would you like your project to be created?`: press <kbd>Enter</kbd>
|
||||
- `Which template would you like?` select `SvelteKit minimal`
|
||||
- `Add type checking with TypeScript?` select `Yes, using JavaScript with JSDoc`
|
||||
- `What would you like to add to your project?` press <kbd>Enter</kbd> (do not select options)
|
||||
- `Which package manager do you want to install dependencies with?`: select `npm` and press <kbd>Enter</kbd>
|
||||
|
||||
<details>
|
||||
<summary><b>Older SvelteJS versions</b> (click to show)</summary>
|
||||
|
||||
The following commands create SvelteJS projects for older versions:
|
||||
|
||||
**SvelteJS 3.x**
|
||||
|
||||
```bash
|
||||
npm create svelte@4 sheetjs-svelte
|
||||
```
|
||||
|
||||
|
||||
**SvelteJS 4.x**
|
||||
|
||||
```bash
|
||||
npm create svelte@6 sheetjs-svelte
|
||||
npm create svelte@latest sheetjs-svelte
|
||||
```
|
||||
|
||||
When prompted:
|
||||
@ -227,21 +198,19 @@ When prompted:
|
||||
- `Add type checking with TypeScript?` select `Yes, using JavaScript with JSDoc`
|
||||
- `Select additional options` press <kbd>Enter</kbd> (do not select options)
|
||||
|
||||
After creating the project, enter the project folder and install dependencies:
|
||||
:::note pass
|
||||
|
||||
To test the Svelte 5 beta, select `Try the Svelte 5 preview (unstable!)`
|
||||
|
||||
:::
|
||||
|
||||
2) Enter the project folder and install dependencies:
|
||||
|
||||
```bash
|
||||
cd sheetjs-svelte
|
||||
npm i
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
2) Install dependencies:
|
||||
|
||||
```bash
|
||||
npm i
|
||||
```
|
||||
|
||||
3) Fetch the example file [`pres.xlsx`](https://docs.sheetjs.com/pres.xlsx) and
|
||||
move to a `data` subdirectory in the root of the project:
|
||||
|
||||
@ -342,14 +311,6 @@ observe that the data from the spreadsheet is displayed in the page.
|
||||
11) In the spreadsheet, set cell A7 to `SheetJS Dev` and cell B7 to `47`. Save
|
||||
the file. After saving, the browser should automatically refresh with new data.
|
||||
|
||||
:::caution pass
|
||||
|
||||
**Live reloading may not work in newer projects!**
|
||||
|
||||
If the page does not reload, manually refresh the page.
|
||||
|
||||
:::
|
||||
|
||||
### Static Site
|
||||
|
||||
12) Stop the development server and install the static adapter:
|
||||
|
@ -52,14 +52,8 @@ This demo was tested in the following environments:
|
||||
|
||||
| AstroJS | Template | Date |
|
||||
|:--------|:-----------------|:-----------|
|
||||
| `4.6.1` | Starlight 0.22.4 | 2025-01-07 |
|
||||
| `5.1.3` | Starlight 0.30.5 | 2025-01-07 |
|
||||
|
||||
In previous test runs, this demo successfully ran using older AstroJS versions:
|
||||
|
||||
| AstroJS | Template |
|
||||
|:--------|:-----------------|
|
||||
| `3.6.5` | Starlight 0.14.0 |
|
||||
| `3.6.5` | Starlight 0.14.0 | 2024-04-14 |
|
||||
| `4.6.1` | Starlight 0.21.5 | 2024-04-14 |
|
||||
|
||||
:::
|
||||
|
||||
@ -116,7 +110,7 @@ export default defineConfig({
|
||||
|
||||
#### Types
|
||||
|
||||
For VSCodium integration, types can be specified in `src/env.d.ts`.
|
||||
For VSCode and VSCodium integration, types can be specified in `src/env.d.ts`.
|
||||
|
||||
This data loader returns Base64 strings:
|
||||
|
||||
@ -221,10 +215,10 @@ cd sheetjs-astro
|
||||
To test an older version of AstroJS, install the specific version of `astro` and
|
||||
a supported starter template after creating the project.
|
||||
|
||||
For major version 4, Starlight must be version `0.22.4`:
|
||||
For major version 3, Starlight must be version `0.14.0`:
|
||||
|
||||
```bash
|
||||
npm install --force astro@4 @astrojs/starlight@0.22.4
|
||||
npm install --force astro@3.6.5 @astrojs/starlight@0.14.0
|
||||
```
|
||||
|
||||
The version can be verified by running:
|
||||
@ -233,22 +227,6 @@ The version can be verified by running:
|
||||
npx astro --version
|
||||
```
|
||||
|
||||
**When using older versions of AstroJS, the sidebar must be removed!**
|
||||
|
||||
In `astro.config.mjs`, the `sidebar` property in the config must be removed:
|
||||
|
||||
```js title="astro.config.mjs (snippet)"
|
||||
export default defineConfig({
|
||||
integrations: [
|
||||
starlight({
|
||||
// ...
|
||||
// remove the `sidebar` array if it exists in the object
|
||||
sidebar: [
|
||||
// remove this entire array
|
||||
],
|
||||
}),
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
2) Fetch the example file [`pres.numbers`](https://docs.sheetjs.com/pres.numbers):
|
||||
@ -349,7 +327,8 @@ npx http-server dist
|
||||
```
|
||||
|
||||
Open a web browser and access the displayed URL ( `http://localhost:8080` ).
|
||||
View the page source and confirm that the raw data is stored in an HTML table.
|
||||
View the page source and confirm that no JS was added to the page. It only
|
||||
contains the content from the file in an HTML table.
|
||||
|
||||
:::caution pass
|
||||
|
||||
|
@ -97,7 +97,7 @@ _Complete State_
|
||||
|
||||
The complete state is initialized with the following snippet:
|
||||
|
||||
```js title="State variables"
|
||||
```js
|
||||
const [data, setData] = useState([
|
||||
"SheetJS".split(""),
|
||||
[5,4,3,3,7,9,5],
|
||||
@ -112,7 +112,7 @@ const [widths, setWidths] = useState(Array.from({length:7}, () => 20));
|
||||
Starting from a SheetJS worksheet object, `sheet_to_json`[^3] with the `header`
|
||||
option can generate an array of arrays:
|
||||
|
||||
```js title="Updating state from a workbook"
|
||||
```js
|
||||
/* assuming `wb` is a SheetJS workbook */
|
||||
function update_state(wb) {
|
||||
/* convert first worksheet to AOA */
|
||||
@ -133,7 +133,7 @@ _Calculating Column Widths_
|
||||
Column widths can be calculated by walking each column and calculating the max
|
||||
data width. Using the array of arrays:
|
||||
|
||||
```js title="Calculating column widths"
|
||||
```js
|
||||
/* this function takes an array of arrays and generates widths */
|
||||
function make_width(aoa) {
|
||||
/* walk each row */
|
||||
@ -154,7 +154,7 @@ function make_width(aoa) {
|
||||
|
||||
`aoa_to_sheet`[^4] builds a SheetJS worksheet object from the array of arrays:
|
||||
|
||||
```js title="Exporting state data to a workbook"
|
||||
```js
|
||||
/* generate a SheetJS workbook from the state */
|
||||
function export_state() {
|
||||
/* convert AOA back to worksheet */
|
||||
@ -170,19 +170,13 @@ function export_state() {
|
||||
|
||||
### Displaying Data
|
||||
|
||||
React Native does not ship with a component for displaying tabular data.
|
||||
|
||||
`react-native-table-component` is a simple UI component designed for legacy
|
||||
versions of React Native.
|
||||
|
||||
[`react-native-tabeller`](https://git.sheetjs.com/asadbek064/react-native-tabeller)
|
||||
uses a similar API and follows modern React Native design patterns.
|
||||
The demos uses `react-native-table-component` to display the first worksheet.
|
||||
|
||||
The demos use components similar to the example below:
|
||||
|
||||
```jsx title="Example JSX for displaying data"
|
||||
```jsx
|
||||
import { ScrollView } from 'react-native';
|
||||
import { Table, Row, Rows, TableWrapper } from 'react-native-tabeller';
|
||||
import { Table, Row, Rows, TableWrapper } from 'react-native-table-component';
|
||||
|
||||
(
|
||||
{/* Horizontal scroll */}
|
||||
@ -232,25 +226,19 @@ This demo was tested in the following environments:
|
||||
|
||||
| OS | Device | RN | Date |
|
||||
|:-----------|:------------------|:---------|:-----------|
|
||||
| iOS 15.6 | iPhone 13 Pro Max | `0.76.8` | 2025-03-26 |
|
||||
| Android 34 | NVIDIA Shield | `0.76.8` | 2025-03-26 |
|
||||
| iOS 15.1 | iPhone 12 Pro Max | `0.73.6` | 2024-03-13 |
|
||||
| Android 29 | NVIDIA Shield | `0.73.6` | 2024-03-13 |
|
||||
|
||||
**Simulators**
|
||||
|
||||
| OS | Device | RN | Dev Platform | Date |
|
||||
|:-----------|:--------------------|:---------|:-------------|:-----------|
|
||||
| Android 34 | Pixel 3a | `0.76.8` | `darwin-arm` | 2025-03-26 |
|
||||
| iOS 18.3 | iPhone 16 Pro | `0.76.8` | `darwin-arm` | 2025-03-26 |
|
||||
| Android 35 | Pixel 9 | `0.76.5` | `win11-x64` | 2024-12-22 |
|
||||
| Android 35 | Pixel 9 | `0.76.5` | `linux-x64` | 2025-01-02 |
|
||||
|
||||
:::
|
||||
|
||||
:::caution
|
||||
|
||||
**Before testing this demo, follow the official React Native CLI Guide!**[^1]
|
||||
|
||||
Make sure you can run a basic test app on your phone/simulator before continuing!
|
||||
| Android 34 | Pixel 3a | `0.73.6` | `darwin-x64` | 2024-03-13 |
|
||||
| iOS 17.4 | iPhone 15 Pro Max | `0.73.6` | `darwin-x64` | 2024-03-13 |
|
||||
| Android 34 | Pixel 3a | `0.74.2` | `darwin-arm` | 2024-06-20 |
|
||||
| iOS 17.5 | iPhone SE (3rd gen) | `0.74.2` | `darwin-arm` | 2024-06-20 |
|
||||
| Android 34 | Pixel 3a | `0.73.5` | `win10-x64` | 2024-03-05 |
|
||||
| Android 34 | Pixel 3a | `0.73.7` | `linux-x64` | 2024-04-29 |
|
||||
|
||||
:::
|
||||
|
||||
@ -289,48 +277,19 @@ export JAVA_HOME=/usr/lib/jvm/java-17-openjdk
|
||||
1) Create project:
|
||||
|
||||
```bash
|
||||
npx -y @react-native-community/cli@15 init SheetJSRNFetch --version="0.76.8"
|
||||
npx -y react-native@0.74.2 init SheetJSRNFetch --version="0.74.2"
|
||||
```
|
||||
|
||||
On macOS, if prompted to install `CocoaPods`, press <kbd>Y</kbd>
|
||||
|
||||
:::info pass
|
||||
|
||||
If you were prompted to install CocoaPods, verify it worked by opening a new
|
||||
terminal and running:
|
||||
|
||||
```bash
|
||||
pod --version
|
||||
```
|
||||
|
||||
If you see "command not found", install it via `brew`:
|
||||
|
||||
```bash
|
||||
brew install cocoapods
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::note pass
|
||||
|
||||
Older versions of this demo used the `react-native` package. The `init` command
|
||||
was officially deprecated.
|
||||
|
||||
React Native now recommends using `@react-native-community/cli`. The versioning
|
||||
scheme is fundamentally different from `react-native`.[^6]
|
||||
|
||||
:::
|
||||
|
||||
2) Install shared dependencies:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
cd SheetJSRNFetch
|
||||
curl -LO https://docs.sheetjs.com/logo.png
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npm i -S https://cdn.sheetjs.com/react-native-tabeller-0.1.0/react-native-tabeller-0.1.0.tgz`}
|
||||
npm i -S react-native-table-component@1.2.2 @types/react-native-table-component`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Download and replace [`App.tsx`](pathname:///reactnative/App.tsx):
|
||||
3) Download [`App.tsx`](pathname:///reactnative/App.tsx) and replace:
|
||||
|
||||
```bash
|
||||
curl -LO https://docs.sheetjs.com/reactnative/App.tsx
|
||||
@ -338,7 +297,7 @@ curl -LO https://docs.sheetjs.com/reactnative/App.tsx
|
||||
|
||||
**Android Testing**
|
||||
|
||||
4) Install or switch to Java 17[^7]
|
||||
4) Install or switch to Java 17[^6]
|
||||
|
||||
5) Start the Android emulator:
|
||||
|
||||
@ -378,25 +337,6 @@ This error can be resolved by installing and switching to the requested version.
|
||||
|
||||
:::caution pass
|
||||
|
||||
When this demo was last tested on Windows 11, there was a Gradle error
|
||||
mentioning a "temporary workspace":
|
||||
|
||||
```
|
||||
> java.io.UncheckedIOException: Could not move temporary workspace (C:\Users\sheetjs\Documents\SheetJSRNFetch\android\.gradle\8.10.2\dependencies-accessors\569c8b261a8a714d7731d5f568e0e5c05babae10-4756c477-c60a-4328-ba8c-e12cc53b4ed0) to immutable location (C:\Users\sheetjs\Documents\SheetJSRNFetch\android\.gradle\8.10.2\dependencies-accessors\569c8b261a8a714d7731d5f568e0e5c05babae10)
|
||||
```
|
||||
|
||||
**This is a known bug in the version of Gradle used by React Native 0.76.5!**
|
||||
|
||||
The Gradle version should be set to `8.11.1` in `gradle-wrapper.properties`:
|
||||
|
||||
```text title="android/gradle/wrapper/gradle-wrapper.properties (change line)"
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
When this demo was last tested on Linux, the process failed to launch the emulator:
|
||||
|
||||
<pre>
|
||||
@ -512,12 +452,6 @@ If the device asks to allow USB debugging, tap "Allow".
|
||||
npx react-native run-android
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
Depending on the device, it may take up to 10 seconds for app to update.
|
||||
|
||||
:::
|
||||
|
||||
**iOS Device Testing**
|
||||
|
||||
13) Connect an iOS device using a USB cable.
|
||||
@ -526,12 +460,12 @@ If the device asks to trust the computer, tap "Trust" and enter the passcode.
|
||||
|
||||
14) Close any Android / iOS emulators.
|
||||
|
||||
15) Enable developer code signing certificates[^8].
|
||||
15) Enable developer code signing certificates[^7].
|
||||
|
||||
<details open>
|
||||
<summary><b>Enabling Code Signing</b> (click to show)</summary>
|
||||
|
||||
These instructions were verified against Xcode 16.2.
|
||||
These instructions were verified against Xcode 15.3.
|
||||
|
||||
A) Open the included iOS workspace in Xcode:
|
||||
|
||||
@ -563,29 +497,6 @@ brew install ios-deploy
|
||||
npx react-native run-ios
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
If the device is not detected, the terminal will print
|
||||
|
||||
```
|
||||
info No booted devices or simulators found. Launching first available simulator...
|
||||
```
|
||||
|
||||
This may happen if the device goes to sleep while connected. Disconnect and
|
||||
reconnect the device before trying again.
|
||||
|
||||
:::
|
||||
|
||||
:::info pass
|
||||
|
||||
In some test runs, the app requested for local network access:
|
||||
|
||||
> "SheetJSRNFetch" would like to find and connect to devices on your local network.
|
||||
|
||||
Local network access is not required for the demo. Select "Don't Allow".
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
In some tests, the app failed to run on the device due to "Untrusted Developer":
|
||||
@ -594,7 +505,7 @@ In some tests, the app failed to run on the device due to "Untrusted Developer":
|
||||
Your device management settings do not allow apps from developer ... on this iPhone. You can allow using these apps in Settings.
|
||||
```
|
||||
|
||||
These instructions were verified against iOS 15.6.
|
||||
These instructions were verified against iOS 15.1.
|
||||
|
||||
A) Open the Settings app and select "General" > "VPN & Device Management".
|
||||
|
||||
@ -749,10 +660,10 @@ for accessing data and are subject to change in future platform versions.
|
||||
|
||||
iOS applications typically require two special settings in `Info.plist`:
|
||||
|
||||
- `UIFileSharingEnabled`[^9] allows users to use files written by the app. A
|
||||
- `UIFileSharingEnabled`[^8] allows users to use files written by the app. A
|
||||
special folder will appear in the "Files" app.
|
||||
|
||||
- `LSSupportsOpeningDocumentsInPlace`[^10] allows the app to open files without
|
||||
- `LSSupportsOpeningDocumentsInPlace`[^9] allows the app to open files without
|
||||
creating a local copy.
|
||||
|
||||
Both settings must be set to `true`:
|
||||
@ -1087,17 +998,17 @@ This demo was tested in the following environments:
|
||||
|
||||
| OS | Device | RN | Date |
|
||||
|:-----------|:------------------|:---------|:-----------|
|
||||
| iOS 15.6 | iPhone 13 Pro Max | `0.76.5` | 2025-01-05 |
|
||||
| Android 34 | NVIDIA Shield | `0.76.5` | 2025-01-05 |
|
||||
| iOS 15.5 | iPhone 13 Pro Max | `0.73.6` | 2024-03-31 |
|
||||
| Android 29 | NVIDIA Shield | `0.73.6` | 2024-03-31 |
|
||||
|
||||
**Simulators**
|
||||
|
||||
| OS | Device | RN | Dev Platform | Date |
|
||||
|:-----------|:------------------|:---------|:-------------|:-----------|
|
||||
| Android 34 | Pixel 3a | `0.76.5` | `darwin-arm` | 2025-01-05 |
|
||||
| iOS 18.2 | iPhone 16 Pro | `0.76.5` | `darwin-arm` | 2025-01-05 |
|
||||
| Android 35 | Pixel 9 | `0.76.5` | `win11-x64` | 2024-12-22 |
|
||||
| Android 35 | Pixel 9 | `0.76.5` | `linux-x64` | 2025-01-02 |
|
||||
| Android 34 | Pixel 3a | `0.73.6` | `darwin-x64` | 2024-03-31 |
|
||||
| iOS 17.4 | iPhone 15 Pro Max | `0.73.6` | `darwin-x64` | 2024-03-31 |
|
||||
| Android 34 | Pixel 3a | `0.73.6` | `win10-x64` | 2024-03-31 |
|
||||
| Android 34 | Pixel 3a | `0.73.6` | `linux-x64` | 2024-03-31 |
|
||||
|
||||
:::
|
||||
|
||||
@ -1105,7 +1016,7 @@ This demo was tested in the following environments:
|
||||
|
||||
There are many moving parts and pitfalls with React Native apps. It is strongly
|
||||
recommended to follow the official React Native tutorials for iOS and Android
|
||||
before approaching this demo.[^11] Details including Android Virtual Device
|
||||
before approaching this demo.[^10] Details including Android Virtual Device
|
||||
configuration are not covered here.
|
||||
|
||||
:::
|
||||
@ -1117,10 +1028,10 @@ This example tries to separate the library-specific functions.
|
||||
1) Create project:
|
||||
|
||||
```bash
|
||||
npx -y @react-native-community/cli@15 init SheetJSRN --version="0.76.5"
|
||||
npx react-native init SheetJSRN --version="0.73.6"
|
||||
```
|
||||
|
||||
On macOS, if prompted to install `CocoaPods`, press <kbd>Y</kbd>
|
||||
On macOS, if prompted to install `CocoaPods`, press `y`.
|
||||
|
||||
2) Install shared dependencies:
|
||||
|
||||
@ -1128,7 +1039,7 @@ On macOS, if prompted to install `CocoaPods`, press <kbd>Y</kbd>
|
||||
cd SheetJSRN
|
||||
curl -LO https://docs.sheetjs.com/logo.png
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npm i -S react-native-table-component@1.2.2 react-native-document-picker@9.3.1`}
|
||||
npm i -S react-native-table-component@1.2.2 react-native-document-picker@9.1.1`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Download [`index.js`](pathname:///mobile/index.js) and replace:
|
||||
@ -1149,37 +1060,18 @@ The app should look like the following screenshot:
|
||||
|
||||
:::caution pass
|
||||
|
||||
In some test runs on Windows, the build failed with an error:
|
||||
When this demo was last tested on Windows, the build failed with an error:
|
||||
|
||||
```
|
||||
> Failed to apply plugin 'com.android.internal.application'.
|
||||
> Android Gradle plugin requires Java 17 to run. You are currently using Java 11.
|
||||
```
|
||||
|
||||
Java 17 must be installed[^12] and the `JAVA_HOME` environment variable must
|
||||
Java 17 must be installed[^11] and the `JAVA_HOME` environment variable must
|
||||
point to the Java 17 location.
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
When this demo was last tested on Windows 11, there was a Gradle error
|
||||
mentioning a "temporary workspace":
|
||||
|
||||
```
|
||||
> java.io.UncheckedIOException: Could not move temporary workspace (C:\Users\sheetjs\Documents\SheetJSRNFetch\android\.gradle\8.10.2\dependencies-accessors\569c8b261a8a714d7731d5f568e0e5c05babae10-4756c477-c60a-4328-ba8c-e12cc53b4ed0) to immutable location (C:\Users\sheetjs\Documents\SheetJSRNFetch\android\.gradle\8.10.2\dependencies-accessors\569c8b261a8a714d7731d5f568e0e5c05babae10)
|
||||
```
|
||||
|
||||
**This is a known bug in the version of Gradle used by React Native 0.76.5!**
|
||||
|
||||
The Gradle version should be set to `8.11.1` in `gradle-wrapper.properties`:
|
||||
|
||||
```text title="android/gradle/wrapper/gradle-wrapper.properties (change line)"
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
Stop the dev server and close the React Native Metro NodeJS window.
|
||||
|
||||
**File Integration**
|
||||
@ -1192,7 +1084,7 @@ Stop the dev server and close the React Native Metro NodeJS window.
|
||||
Install `react-native-blob-util` dependency:
|
||||
|
||||
```bash
|
||||
npm i -S react-native-blob-util@0.21.2
|
||||
npm i -S react-native-blob-util@0.19.8
|
||||
```
|
||||
|
||||
Add the highlighted lines to `index.js`:
|
||||
@ -1240,7 +1132,7 @@ const make_width = ws => {
|
||||
Install `react-native-file-access` dependency:
|
||||
|
||||
```bash
|
||||
npm i -S react-native-file-access@3.1.1
|
||||
npm i -S react-native-file-access@3.0.7
|
||||
```
|
||||
|
||||
Add the highlighted lines to `index.js`:
|
||||
@ -1295,21 +1187,6 @@ If prompted to install Expo CLI integration, choose No.
|
||||
|
||||
:::
|
||||
|
||||
:::info pass
|
||||
|
||||
On Windows, `npx install-expo-modules` will throw errors related to CocoaPods:
|
||||
|
||||
```
|
||||
Uncaught Error CocoaPodsError: Command `pod install` failed.
|
||||
└─ Cause: spawn pod ENOENT
|
||||
'pod' is not recognized as an internal or external command,
|
||||
operable program or batch file.
|
||||
```
|
||||
|
||||
These errors can be ignored.
|
||||
|
||||
:::
|
||||
|
||||
Add the highlighted lines to `index.js`:
|
||||
|
||||
```js title="index.js"
|
||||
@ -1682,15 +1559,14 @@ Swipe left until the "Numbers" app icon appears and tap the app icon.
|
||||
|
||||
The Numbers app will load the spreadsheet, confirming that the file is valid.
|
||||
|
||||
[^1]: Follow the ["Set Up Your Environment" guide](https://reactnative.dev/docs/set-up-your-environment) and select the appropriate "Development OS".
|
||||
[^1]: Follow the ["React Native CLI Quickstart"](https://reactnative.dev/docs/environment-setup) and select the appropriate "Development OS".
|
||||
[^2]: See ["Array of Arrays" in the API reference](/docs/api/utilities/array#array-of-arrays)
|
||||
[^3]: See ["Array Output" in "Utility Functions"](/docs/api/utilities/array#array-output)
|
||||
[^4]: See ["Array of Arrays Input" in "Utility Functions"](/docs/api/utilities/array#array-of-arrays-input)
|
||||
[^5]: React-Native commit [`5b597b5`](https://github.com/facebook/react-native/commit/5b597b5ff94953accc635ed3090186baeecb3873) added the final piece required for `fetch` support. It is available in official releases starting from `0.72.0`.
|
||||
[^6]: See [the compatibility table](https://github.com/react-native-community/cli) in the CLI project repository to determine which version of `@react-native-community/cli` is required for a given `react-native` version.
|
||||
[^7]: When the demo was last tested, the Temurin distribution of Java 17 was installed through the macOS Brew package manager by running `brew install temurin17`. [Direct downloads are available at `adoptium.net`](https://adoptium.net/temurin/releases/?version=17)
|
||||
[^8]: See ["Running On Device"](https://reactnative.dev/docs/running-on-device) in the React Native documentation
|
||||
[^9]: See [`UIFileSharingEnabled`](https://developer.apple.com/documentation/bundleresources/information_property_list/uifilesharingenabled) in the Apple Developer Documentation.
|
||||
[^10]: See [`LSSupportsOpeningDocumentsInPlace`](https://developer.apple.com/documentation/bundleresources/information_property_list/lssupportsopeningdocumentsinplace) in the Apple Developer Documentation.
|
||||
[^11]: Follow the ["React Native CLI Quickstart"](https://reactnative.dev/docs/environment-setup) for Android (and iOS, if applicable)
|
||||
[^12]: See the [JDK Archive](https://jdk.java.net/archive/) for Java 17 JDK download links.
|
||||
[^6]: When the demo was last tested, the Temurin distribution of Java 17 was installed through the macOS Brew package manager by running `brew install temurin17`. [Direct downloads are available at `adoptium.net`](https://adoptium.net/temurin/releases/?version=17)
|
||||
[^7]: See ["Running On Device"](https://reactnative.dev/docs/running-on-device) in the React Native documentation
|
||||
[^8]: See [`UIFileSharingEnabled`](https://developer.apple.com/documentation/bundleresources/information_property_list/uifilesharingenabled) in the Apple Developer Documentation.
|
||||
[^9]: See [`LSSupportsOpeningDocumentsInPlace`](https://developer.apple.com/documentation/bundleresources/information_property_list/lssupportsopeningdocumentsinplace) in the Apple Developer Documentation.
|
||||
[^10]: Follow the ["React Native CLI Quickstart"](https://reactnative.dev/docs/environment-setup) for Android (and iOS, if applicable)
|
||||
[^11]: See the [JDK Archive](https://jdk.java.net/archive/) for Java 17 JDK download links.
|
@ -65,8 +65,7 @@ This demo was tested in the following environments:
|
||||
|:-----------|:--------------------|:---------|:-------------|:-----------|
|
||||
| Android 34 | Pixel 3a | `8.7.2` | `darwin-arm` | 2024-06-09 |
|
||||
| iOS 17.5 | iPhone SE (3rd gen) | `8.7.2` | `darwin-arm` | 2024-06-09 |
|
||||
| Android 35 | Pixel 9 | `8.8.3` | `win11-x64` | 2024-12-21 |
|
||||
| Android 35 | Pixel 9 | `8.8.3` | `linux-x64` | 2025-01-02 |
|
||||
| Android 34 | Pixel 3a | `8.6.5` | `win10-x64` | 2024-04-07 |
|
||||
|
||||
:::
|
||||
|
||||
@ -273,25 +272,21 @@ npx -p nativescript ns error-reporting disable
|
||||
|
||||
:::caution pass
|
||||
|
||||
In previous test runs, NativeScript did not support the latest Android API.
|
||||
The error message from `npx -p nativescript ns doctor android` clearly stated
|
||||
supported versions:
|
||||
When the demo was last tested, the latest version of the Android API was 34.
|
||||
NativeScript did not support that API level. The exact error message from
|
||||
`npx -p nativescript ns doctor ios` clearly stated supported versions:
|
||||
|
||||
<pre>
|
||||
<span {...r}>✖</span> No compatible version of the Android SDK Build-tools are installed on your system. You can install any version in the following range: '>=23 <=33'.
|
||||
</pre>
|
||||
|
||||
If NativeScript does not properly supports the latest API level, a previous API
|
||||
version should be installed using Android Studio.
|
||||
|
||||
In a previous test run, the following packages were required:
|
||||
The SDK Platform `Android 13.0 ("Tiramisu")` was compatible with NativeScript.
|
||||
Until NativeScript properly supports API level 34, "Tiramisu" must be used.
|
||||
This requires installing the following packages from Android Studio:
|
||||
|
||||
- `Android 13.0 ("Tiramisu")` API Level `33`
|
||||
- `Android SDK Build-Tools` Version `33.0.2`
|
||||
|
||||
It is recommended to install the SDK Platform and corresponding Android SDK
|
||||
Build-Tools for the latest supported API level.ß
|
||||
|
||||
:::
|
||||
|
||||
2) Test the local system configuration for Android development:
|
||||
@ -591,30 +586,9 @@ If the emulator cannot be rooted, the following command works in macOS:
|
||||
adb shell "run-as org.nativescript.SheetJSNS cat /data/user/0/org.nativescript.SheetJSNS/files/SheetJSNS.xls" > SheetJSNS.xls
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
In the most recent `win11-x64` test, the generated file was corrupt. This is a
|
||||
known issue with Windows redirects. The solution is to generate a Base64-encoded
|
||||
string and decode using PowerShell:
|
||||
|
||||
```bash
|
||||
adb shell "run-as org.nativescript.SheetJSNS base64 /data/user/0/org.nativescript.SheetJSNS/files/SheetJSNS.xls" > SheetJSNS.xls.b64
|
||||
$b64 = Get-Content -Path .\SheetJSNS.xls.b64 -Raw
|
||||
$bytes = [Convert]::FromBase64String($b64)
|
||||
[System.IO.File]::WriteAllBytes("SheetJSNS.xls", $bytes)
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
17) Open `SheetJSNS.xls` with a spreadsheet editor.
|
||||
|
||||
After the header row, insert a row and make the following assignments:
|
||||
|
||||
- Set cell `A2` to `0`
|
||||
- Set cell `B2` to `SheetJS` (type `'SheetJS` in the formula bar)
|
||||
- Set cell `C2` to `Library` (type `'Library` in the formula bar)
|
||||
|
||||
After making the changes, the worksheet should look like the following:
|
||||
After the header row, insert a row with cell A2 = 0, B2 = SheetJS, C2 = Library:
|
||||
|
||||
```text
|
||||
id | name | role
|
||||
@ -635,21 +609,7 @@ If the emulator cannot be rooted, the following command works in macOS:
|
||||
|
||||
```bash
|
||||
dd if=SheetJSNS.xls | adb shell "run-as org.nativescript.SheetJSNS dd of=/data/user/0/org.nativescript.SheetJSNS/files/SheetJSNS.xls"
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
In the most recent `win11-x64` test, neither workaround worked. The solution is
|
||||
to generate a Base64-encoded string and decode in `adb`. After closing Excel and
|
||||
saving the `SheetJSNS.xls` file, run the following commands:
|
||||
|
||||
```bash
|
||||
$bytes = [IO.File]::ReadAllBytes(".\SheetJSNS.xls")
|
||||
$b64 = [Convert]::ToBase64String($bytes)
|
||||
echo $b64 | adb shell "run-as org.nativescript.SheetJSNS base64 -d | run-as org.nativescript.SheetJSNS dd of=/data/user/0/org.nativescript.SheetJSNS/files/SheetJSNS.xls"
|
||||
```
|
||||
|
||||
:::
|
||||
```
|
||||
|
||||
19) Tap "Import File". A dialog will print the path of the file that was read.
|
||||
The first item in the list will change.
|
||||
@ -678,13 +638,7 @@ npx -p nativescript ns run ios
|
||||
|
||||
22) Open the file with a spreadsheet editor.
|
||||
|
||||
After the header row, insert a row and make the following assignments:
|
||||
|
||||
- Set cell `A2` to `0`
|
||||
- Set cell `B2` to `SheetJS` (type `'SheetJS` in the formula bar)
|
||||
- Set cell `C2` to `Library` (type `'Library` in the formula bar)
|
||||
|
||||
After making the changes, the worksheet should look like the following:
|
||||
After the header row, insert a row with cell A2 = 0, B2 = SheetJS, C2 = Library:
|
||||
|
||||
```text
|
||||
id | name | role
|
||||
@ -874,5 +828,4 @@ file named `SheetJSNS.xls`.
|
||||
[^4]: See [`write` in "Writing Files"](/docs/api/write-options)
|
||||
[^5]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
|
||||
[^6]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
|
||||
[^7]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^8]: See ["Local setup"](https://docs.nativescript.org/setup/#local-setup) in the NativeScript documentation. For Windows and Linux, follow the "Android" instructions. For macOS, follow both the iOS and Android instructions.
|
||||
[^7]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
@ -45,16 +45,15 @@ This demo was tested in the following environments:
|
||||
|
||||
| OS | Device | Quasar | Date |
|
||||
|:-----------|:--------------------|:---------|:-----------|
|
||||
| Android 34 | NVIDIA Shield | `2.18.1` | 2025-04-17 |
|
||||
| iOS 15.1 | iPad Pro | `2.18.1` | 2025-04-17 |
|
||||
| Android 30 | NVIDIA Shield | `2.16.4` | 2024-06-09 |
|
||||
| iOS 15.1 | iPad Pro | `2.16.4` | 2024-06-09 |
|
||||
|
||||
**Simulators**
|
||||
|
||||
| OS | Device | Quasar | Dev Platform | Date |
|
||||
|:-----------|:--------------------|:---------|:-------------|:-----------|
|
||||
| Android 35 | Pixel 9 Pro XL | `2.18.1` | `darwin-arm` | 2025-04-17 |
|
||||
| iOS 18.2 | iPhone 16 Pro Max | `2.18.1` | `darwin-arm` | 2025-04-17 |
|
||||
| Android 36 | Pixel 9 Pro XL | `2.18.1` | `win11-x64` | 2025-04-17 |
|
||||
| Android 34 | Pixel 3a | `2.16.4` | `darwin-arm` | 2024-06-09 |
|
||||
| iOS 17.5 | iPhone SE (3rd gen) | `2.16.4` | `darwin-arm` | 2024-06-09 |
|
||||
|
||||
:::
|
||||
|
||||
@ -77,7 +76,7 @@ cd ..
|
||||
|
||||
### Reading data
|
||||
|
||||
The `QFile`[^1] component presents an API reminiscent of File Input elements:
|
||||
The QFile[^1] component presents an API reminiscent of File Input elements:
|
||||
|
||||
```html
|
||||
<q-file label="Load File" filled label-color="orange" @input="updateFile"/>
|
||||
@ -167,23 +166,13 @@ window.requestFileSystem(window.PERSISTENT, 0, function(fs) {
|
||||
The demo draws from the ViteJS example. Familiarity with VueJS and TypeScript
|
||||
is assumed.
|
||||
|
||||
### Platform Setup
|
||||
|
||||
0) Ensure all of the dependencies are installed. Install the CLI globally:
|
||||
|
||||
```bash
|
||||
npm i -g @quasar/cli cordova
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
In some systems, the command must be run as the root user:
|
||||
|
||||
```bash
|
||||
sudo npm i -g @quasar/cli cordova
|
||||
```
|
||||
|
||||
:::
|
||||
(you may need to run `sudo npm i -g` if there are permission issues)
|
||||
|
||||
<details>
|
||||
<summary><b>Installation Notes</b> (click to show)</summary>
|
||||
@ -192,8 +181,6 @@ Quasar requires Java 17
|
||||
|
||||
</details>
|
||||
|
||||
### Base Project
|
||||
|
||||
1) Create a new app:
|
||||
|
||||
```bash
|
||||
@ -206,12 +193,14 @@ When prompted:
|
||||
|
||||
- "What would you like to build?": `App with Quasar CLI, let's go!`
|
||||
- "Project folder": `SheetJSQuasar`
|
||||
- "Pick Quasar version": `Quasar v2 (Vue 3 | latest and greatest)`
|
||||
- "Pick script type": `Typescript`
|
||||
- "Pick Quasar App CLI variant": `Quasar App CLI with Vite`
|
||||
- "Package name": (press <kbd>Enter</kbd>, it will use the default `sheetjsquasar`)
|
||||
- "Project product name": `SheetJSQuasar`
|
||||
- "Project description": `SheetJS + Quasar`
|
||||
- "Pick a Vue component style": `Composition API with <script setup>`
|
||||
- "Author": (press <kbd>Enter</kbd>, it will use your git config settings)
|
||||
- "Pick a Vue component style": `Composition API`
|
||||
- "Pick your CSS preprocessor": `None`
|
||||
- "Check the features needed for your project": Deselect everything (scroll down to each selected item and press <kbd>Space</kbd>)
|
||||
- "Install project dependencies": `Yes, use npm`
|
||||
@ -229,105 +218,18 @@ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
3) Set up Cordova:
|
||||
|
||||
```bash
|
||||
npx cordova telemetry off
|
||||
npx quasar mode add cordova
|
||||
npx quasar mode remove capacitor
|
||||
quasar mode add cordova
|
||||
```
|
||||
|
||||
When prompted, enter the app id `org.sheetjs.quasar`.
|
||||
|
||||
4) Add the Dialog plugin to the `plugins` array in `quasar.config.ts`:
|
||||
|
||||
```js title="quasar.config.ts (edit highlighted line)"
|
||||
// Quasar plugins
|
||||
// highlight-next-line
|
||||
plugins: ['Dialog']
|
||||
```
|
||||
|
||||
5) Download [`IndexPage.vue`](pathname:///quasar/IndexPage.vue) and replace the
|
||||
existing page script `src/pages/IndexPage.vue`:
|
||||
|
||||
```bash
|
||||
curl -L -o src/pages/IndexPage.vue https://docs.sheetjs.com/quasar/IndexPage.vue
|
||||
```
|
||||
|
||||
### Android
|
||||
|
||||
6) Create the Android project:
|
||||
It will create a new `src-cordova` folder. Continue in that folder:
|
||||
|
||||
```bash
|
||||
cd src-cordova
|
||||
npx cordova platform add android
|
||||
npx cordova plugin add cordova-plugin-wkwebview-engine
|
||||
npx cordova plugin add cordova-plugin-file
|
||||
cd ..
|
||||
```
|
||||
|
||||
7) Start the simulator:
|
||||
|
||||
```bash
|
||||
npx quasar dev -m cordova -T android
|
||||
```
|
||||
|
||||
If prompted to select an external IP, press <kbd>Enter</kbd>.
|
||||
|
||||
:::caution pass
|
||||
|
||||
If the app is blank or not refreshing, delete the app and close the simulator,
|
||||
then restart the development process.
|
||||
|
||||
:::
|
||||
|
||||
:::info pass
|
||||
|
||||
In some test runs, the command failed with a Gradle error
|
||||
|
||||
```
|
||||
Could not find an installed version of Gradle either in Android Studio,
|
||||
or on your system to install the gradle wrapper. Please include gradle
|
||||
in your path, or install Android Studio
|
||||
```
|
||||
|
||||
[Gradle](https://gradle.org/) (the complete version) must be extracted and the
|
||||
`bin` folder must be added to the user PATH variable. After adding to PATH,
|
||||
launch a new PowerShell or Command Prompt window and run the command.
|
||||
|
||||
:::
|
||||
|
||||
To test that reading works:
|
||||
|
||||
- Download https://docs.sheetjs.com/pres.numbers
|
||||
- Open the Downloads folder in Finder or Explorer
|
||||
- Click and drag `pres.numbers` from the window into the simulator.
|
||||
- Tap "Load File", tap the `≡` icon, tap "Downloads" and select `pres.numbers`.
|
||||
|
||||
To test that writing works:
|
||||
|
||||
- Tap "Save File". You will see a popup with a location.
|
||||
- Pull the file from the simulator and verify the contents:
|
||||
|
||||
```bash
|
||||
adb exec-out run-as org.sheetjs.quasar cat files/files/SheetJSQuasar.xlsx > /tmp/SheetJSQuasar.xlsx
|
||||
npx xlsx-cli /tmp/SheetJSQuasar.xlsx
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
PowerShell file redirects will corrupt binary data. In Windows, commands must be
|
||||
run from a Command Prompt session.
|
||||
|
||||
:::
|
||||
|
||||
### iOS
|
||||
|
||||
8) Create the iOS project:
|
||||
|
||||
```bash
|
||||
cd src-cordova
|
||||
npx cordova platform add ios
|
||||
npx cordova plugin add cordova-plugin-wkwebview-engine
|
||||
npx cordova plugin add cordova-plugin-file
|
||||
cd ..
|
||||
cordova platform add ios
|
||||
cordova plugin add cordova-plugin-wkwebview-engine
|
||||
cordova plugin add cordova-plugin-file
|
||||
```
|
||||
|
||||
:::note pass
|
||||
@ -335,14 +237,20 @@ cd ..
|
||||
If there is an error `Could not load API for iOS project`, it needs to be reset:
|
||||
|
||||
```bash
|
||||
npx cordova platform rm ios
|
||||
npx cordova platform add ios
|
||||
npx cordova plugin add cordova-plugin-file
|
||||
cordova platform rm ios
|
||||
cordova platform add ios
|
||||
cordova plugin add cordova-plugin-file
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
9) Enable file sharing and make the documents folder visible in the iOS app.
|
||||
Return to the project directory:
|
||||
|
||||
```bash
|
||||
cd ..
|
||||
```
|
||||
|
||||
4) Enable file sharing and make the documents folder visible in the iOS app.
|
||||
The following lines must be added to `src-cordova/platforms/ios/SheetJSQuasar/SheetJSQuasar-Info.plist`:
|
||||
|
||||
```xml title="src-cordova/platforms/ios/SheetJSQuasar/SheetJSQuasar-Info.plist (add to file)"
|
||||
@ -360,10 +268,10 @@ The following lines must be added to `src-cordova/platforms/ios/SheetJSQuasar/Sh
|
||||
(The root element of the document is `plist` and it contains one `dict` child)
|
||||
|
||||
|
||||
10) Start the development server:
|
||||
5) Start the development server:
|
||||
|
||||
```bash
|
||||
npx quasar dev -m cordova -T ios
|
||||
quasar dev -m ios
|
||||
```
|
||||
|
||||
If prompted to select an external IP, press <kbd>Enter</kbd>.
|
||||
@ -375,6 +283,52 @@ then restart the development process.
|
||||
|
||||
:::
|
||||
|
||||
6) Add the Dialog plugin to `quasar.config.js`:
|
||||
|
||||
```js title="quasar.config.js (add highlighted line)"
|
||||
framework: {
|
||||
config: {},
|
||||
// ...
|
||||
// Quasar plugins
|
||||
// highlight-next-line
|
||||
plugins: ['Dialog']
|
||||
},
|
||||
```
|
||||
|
||||
7) In the template section of `src/pages/IndexPage.vue`, replace the example
|
||||
with a Table, Save button and Load file picker component:
|
||||
|
||||
```html title="src/pages/IndexPage.vue (change highlighted lines)"
|
||||
<template>
|
||||
<q-page class="row items-center justify-evenly">
|
||||
<!-- highlight-start -->
|
||||
<q-table :rows="todos" />
|
||||
<q-btn-group>
|
||||
<q-file label="Load File" filled label-color="orange" @input="updateFile"/>
|
||||
<q-btn label="Save File" @click="saveFile" />
|
||||
</q-btn-group>
|
||||
<!-- highlight-end -->
|
||||
</q-page>
|
||||
</template>
|
||||
```
|
||||
|
||||
This uses two functions that should be added to the component script:
|
||||
|
||||
```ts title="src/pages/IndexPage.vue (add highlighted lines)"
|
||||
const meta = ref<Meta>({
|
||||
totalCount: 1200
|
||||
});
|
||||
// highlight-start
|
||||
function saveFile() {
|
||||
}
|
||||
async function updateFile(v: Event) {
|
||||
}
|
||||
return { todos, meta, saveFile, updateFile };
|
||||
// highlight-end
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
The app should now show two buttons at the bottom:
|
||||
|
||||

|
||||
@ -386,6 +340,38 @@ then restart the development process.
|
||||
|
||||
:::
|
||||
|
||||
8) Wire up the `updateFile` function:
|
||||
|
||||
```ts title="src/pages/IndexPage.vue (add highlighted lines)"
|
||||
import { defineComponent, ref } from 'vue';
|
||||
// highlight-start
|
||||
import { read, write, utils } from 'xlsx';
|
||||
import { useQuasar } from 'quasar';
|
||||
// highlight-end
|
||||
|
||||
export default defineComponent({
|
||||
// ...
|
||||
// highlight-start
|
||||
const $q = useQuasar();
|
||||
function dialogerr(e: Error) { $q.dialog({title: "Error!", message: e.message || String(e)}); }
|
||||
// highlight-end
|
||||
function saveFile() {
|
||||
}
|
||||
async function updateFile(v: Event) {
|
||||
// highlight-start
|
||||
try {
|
||||
const files = (v.target as HTMLInputElement).files;
|
||||
if(!files || files.length == 0) return;
|
||||
|
||||
const wb = read(await files[0].arrayBuffer());
|
||||
|
||||
const data = utils.sheet_to_json<any>(wb.Sheets[wb.SheetNames[0]]);
|
||||
todos.value = data.map(row => ({id: row.Index, content: row.Name}));
|
||||
} catch(e) { dialogerr(e); }
|
||||
// highlight-end
|
||||
}
|
||||
```
|
||||
|
||||
To test that reading works:
|
||||
|
||||
- Download https://docs.sheetjs.com/pres.numbers
|
||||
@ -403,11 +389,46 @@ To test that reading works:
|
||||
|
||||
Once selected, the screen should refresh with new contents.
|
||||
|
||||
9) Wire up the `saveFile` function:
|
||||
|
||||
```ts title="src/pages/IndexPage.vue (add highlighted lines)"
|
||||
function saveFile() {
|
||||
// highlight-start
|
||||
/* generate workbook from state */
|
||||
const ws = utils.json_to_sheet(todos.value);
|
||||
const wb = utils.book_new();
|
||||
utils.book_append_sheet(wb, ws, "SheetJSQuasar");
|
||||
const u8: Uint8Array = write(wb, {bookType: "xlsx", type: "buffer"});
|
||||
const dir: string = $q.cordova.file.documentsDirectory || $q.cordova.file.externalApplicationStorageDirectory;
|
||||
|
||||
/* save to file */
|
||||
window.requestFileSystem(window.PERSISTENT, 0, function(fs) {
|
||||
try {
|
||||
fs.root.getFile("SheetJSQuasar.xlsx", {create: true}, entry => {
|
||||
const msg = `File stored at ${dir} ${entry.fullPath}`;
|
||||
entry.createWriter(writer => {
|
||||
try {
|
||||
const data = new Blob([u8], {type: "application/vnd.ms-excel"});
|
||||
writer.onwriteend = () => {
|
||||
try {
|
||||
$q.dialog({title: "Success!", message: msg});
|
||||
} catch(e) { dialogerr(e); }
|
||||
};
|
||||
writer.onerror = dialogerr;
|
||||
writer.write(data);
|
||||
} catch(e) { dialogerr(e); }
|
||||
}, dialogerr);
|
||||
}, dialogerr);
|
||||
} catch(e) { dialogerr(e) }
|
||||
}, dialogerr);
|
||||
// highlight-end
|
||||
}
|
||||
```
|
||||
|
||||
The page should revert to the old contents.
|
||||
|
||||
To test that writing works:
|
||||
|
||||
- Close the app in the simulator and re-launch the app.
|
||||
|
||||
- Click "Save File". You will see a popup with a location:
|
||||
|
||||

|
||||
@ -452,23 +473,121 @@ id,content
|
||||
46,Joseph Biden
|
||||
```
|
||||
|
||||
### Android Device
|
||||
**Android**
|
||||
|
||||
11) Close all open emulators and simulators.
|
||||
|
||||
12) Disconnect any iOS or Android devices connected to the computer.
|
||||
|
||||
13) Connect the Android device to the computer.
|
||||
|
||||
14) Start the dev process:
|
||||
10) Create the Android project:
|
||||
|
||||
```bash
|
||||
npx quasar dev -m cordova -T android
|
||||
cd src-cordova
|
||||
cordova platform add android
|
||||
cd ..
|
||||
```
|
||||
|
||||
11) Start the simulator:
|
||||
|
||||
```bash
|
||||
quasar dev -m android
|
||||
```
|
||||
|
||||
If prompted to select an external IP, press <kbd>Enter</kbd>.
|
||||
|
||||
15) Test the application:
|
||||
:::caution pass
|
||||
|
||||
If the app is blank or not refreshing, delete the app and close the simulator,
|
||||
then restart the development process.
|
||||
|
||||
:::
|
||||
|
||||
:::warning pass
|
||||
|
||||
On Windows, the command failed with a Gradle error
|
||||
|
||||
```
|
||||
Could not find an installed version of Gradle either in Android Studio,
|
||||
or on your system to install the gradle wrapper. Please include gradle
|
||||
in your path, or install Android Studio
|
||||
```
|
||||
|
||||
[Gradle](https://gradle.org/) (the complete version) must be extracted and the
|
||||
`bin` folder must be added to the user PATH variable. After adding to PATH,
|
||||
launch a new PowerShell or CMD window and run the command.
|
||||
|
||||
:::
|
||||
|
||||
To test that reading works:
|
||||
|
||||
- Click and drag `pres.numbers` from a Finder window into the simulator.
|
||||
- Tap "Load", tap the `≡` icon, tap "Downloads" and select `pres.numbers`.
|
||||
|
||||
To test that writing works:
|
||||
|
||||
- Tap "Save File". You will see a popup with a location.
|
||||
|
||||
- Pull the file from the simulator and verify the contents:
|
||||
|
||||
```bash
|
||||
adb exec-out run-as org.sheetjs.quasar cat files/files/SheetJSQuasar.xlsx > /tmp/SheetJSQuasar.xlsx
|
||||
npx xlsx-cli /tmp/SheetJSQuasar.xlsx
|
||||
```
|
||||
|
||||
**iOS Device**
|
||||
|
||||
12) Close all open emulators and simulators.
|
||||
|
||||
13) Disconnect any iOS or Android devices connected to the computer.
|
||||
|
||||
14) Connect the iOS device to the computer.
|
||||
|
||||
15) Open the Xcode project:
|
||||
|
||||
```bash
|
||||
open src-cordova/platforms/ios/SheetJSQuasar.xcodeproj
|
||||
```
|
||||
|
||||
Select "SheetJSQuasar" in the Navigator. In the main pane, select "Signing &
|
||||
Capabilities" and ensure a Team is selected. Save and close the project.
|
||||
|
||||
16) Start the dev process:
|
||||
|
||||
```bash
|
||||
quasar dev -m ios
|
||||
```
|
||||
|
||||
If prompted to select an external IP, press <kbd>Enter</kbd>.
|
||||
|
||||
17) Test the application:
|
||||
|
||||
- Press the Home button (or swipe up with one finger) and switch to Safari.
|
||||
- Download https://docs.sheetjs.com/pres.numbers
|
||||
- Press the Home button (or swipe up with one finger) and select the `SheetJSQuasar` app
|
||||
- Tap the "Load" button, then select "Choose File" and select the downloaded `pres.numbers`
|
||||
|
||||
The table will update with new data.
|
||||
|
||||
- Tap "Save File"
|
||||
- Press the Home button (or swipe up with one finger) and switch to Files.
|
||||
- Tap `<` until the main "Browse" window is displayed, then select "On My iPhone"
|
||||
- Look for the "SheetJSQuasar" folder and tap `SheetJSQuasar.xlsx`.
|
||||
|
||||
If Numbers is installed on the device, it will display the contents of the new file.
|
||||
|
||||
**Android Device**
|
||||
|
||||
18) Close all open emulators and simulators.
|
||||
|
||||
19) Disconnect any iOS or Android devices connected to the computer.
|
||||
|
||||
20) Connect the Android device to the computer.
|
||||
|
||||
21) Start the dev process:
|
||||
|
||||
```bash
|
||||
quasar dev -m android
|
||||
```
|
||||
|
||||
If prompted to select an external IP, press <kbd>Enter</kbd>.
|
||||
|
||||
22) Test the application:
|
||||
|
||||
- Press the Home button (or swipe up with one finger) and switch to Browser.
|
||||
- Download https://docs.sheetjs.com/pres.numbers
|
||||
@ -485,48 +604,6 @@ methods ("Storage Access Framework") that are not implemented in Quasar.
|
||||
:::
|
||||
|
||||
|
||||
### iOS Device
|
||||
|
||||
16) Close all open emulators and simulators.
|
||||
|
||||
17) Disconnect any iOS or Android devices connected to the computer.
|
||||
|
||||
18) Connect the iOS device to the computer.
|
||||
|
||||
19) Open the Xcode project:
|
||||
|
||||
```bash
|
||||
open src-cordova/platforms/ios/SheetJSQuasar.xcodeproj
|
||||
```
|
||||
|
||||
Select "SheetJSQuasar" in the Navigator. In the main pane, select "Signing &
|
||||
Capabilities" and ensure a Team is selected. Save and close the project.
|
||||
|
||||
20) Start the dev process:
|
||||
|
||||
```bash
|
||||
npx quasar dev -m cordova -T ios
|
||||
```
|
||||
|
||||
If prompted to select an external IP, press <kbd>Enter</kbd>.
|
||||
|
||||
11) Test the application:
|
||||
|
||||
- Press the Home button (or swipe up with one finger) and switch to Safari.
|
||||
- Download https://docs.sheetjs.com/pres.numbers
|
||||
- Press the Home button (or swipe up with one finger) and select the `SheetJSQuasar` app
|
||||
- Tap the "Load" button, then select "Choose File" and select the downloaded `pres.numbers`
|
||||
|
||||
The table will update with new data.
|
||||
|
||||
- Tap "Save File"
|
||||
- Press the Home button (or swipe up with one finger) and switch to Files.
|
||||
- Tap `<` until the main "Browse" window is displayed, then select "On My iPhone"
|
||||
- Look for the "SheetJSQuasar" folder and tap `SheetJSQuasar.xlsx`.
|
||||
|
||||
If Numbers is installed on the device, it will display the contents of the new file.
|
||||
|
||||
|
||||
[^1]: See ["File Picker"](https://quasar.dev/vue-components/file-picker) in the Quasar documentation.
|
||||
[^2]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^3]: See ["SheetJS Data Model"](/docs/csf/) for more details on workbooks, worksheets, and other concepts.
|
||||
|
@ -53,31 +53,24 @@ This demo was tested in the following environments:
|
||||
|
||||
| OS | Device | Config | Date |
|
||||
|:-----------|:--------------------|:-------|:-----------|
|
||||
| Android 34 | NVIDIA Shield | B | 2025-03-30 |
|
||||
| iOS 15.6 | iPhone 13 Pro Max | B | 2025-03-30 |
|
||||
| Android 30 | NVIDIA Shield | A | 2024-05-30 |
|
||||
| iOS 15.1 | iPad Pro | A | 2024-05-30 |
|
||||
|
||||
**Simulators**
|
||||
|
||||
| OS | Device | Config | Dev Platform | Date |
|
||||
|:-----------|:--------------------|:-------|:-------------|:-----------|
|
||||
| Android 34 | Pixel 3a | B | `darwin-arm` | 2025-03-30 |
|
||||
| iOS 18.2 | iPhone SE (3rd gen) | B | `darwin-arm` | 2025-03-30 |
|
||||
| Android 36 | Pixel 9 Pro XL | A | `win11-x64` | 2025-04-17 |
|
||||
| Android 34 | Pixel 3a | A | `darwin-arm` | 2024-05-30 |
|
||||
| iOS 17.5 | iPhone SE (3rd gen) | A | `darwin-arm` | 2024-05-30 |
|
||||
|
||||
<details>
|
||||
<summary><b>Configurations</b> (click to show)</summary>
|
||||
|
||||
Configuration A:
|
||||
|
||||
- Ionic: `@ionic/angular 8.5.2`, `@ionic/angular-toolkit 12.1.1`
|
||||
- Cordova: `cordova-lib@12.0.2`, `android 14.0.0`
|
||||
- File Integration: `@awesome-cordova-plugins/file` version `6.16.0`
|
||||
|
||||
Configuration B:
|
||||
|
||||
- Ionic: `@ionic/angular 8.5.2`, `@ionic/angular-toolkit 12.1.1`
|
||||
- Cordova: `cordova-lib@12.0.2`, `android 14.0.0, ios 7.1.1`
|
||||
- File Integration: `@awesome-cordova-plugins/file` version `6.16.0`
|
||||
- Ionic: `@ionic/angular 8.2.0`, `@ionic/angular-toolkit 11.0.1`
|
||||
- Cordova: `cordova-lib@12.0.1`, `android 13.0.0, ios 7.1.0`
|
||||
- File Integration: `@awesome-cordova-plugins/file` version `6.7.0`
|
||||
|
||||
</details>
|
||||
|
||||
@ -85,18 +78,26 @@ Configuration B:
|
||||
|
||||
:::danger Telemetry
|
||||
|
||||
Before starting this demo, manually disable telemetry:
|
||||
Before starting this demo, manually disable telemetry. On Linux and MacOS:
|
||||
|
||||
```bash
|
||||
npx -y @ionic/cli config set -g telemetry false
|
||||
npx -y @capacitor/cli telemetry off
|
||||
rm -rf ~/.ionic/
|
||||
mkdir ~/.ionic
|
||||
cat <<EOF > ~/.ionic/config.json
|
||||
{
|
||||
"version": "6.20.1",
|
||||
"telemetry": false,
|
||||
"npmClient": "npm"
|
||||
}
|
||||
EOF
|
||||
npx @capacitor/cli telemetry off
|
||||
```
|
||||
|
||||
To verify telemetry was disabled:
|
||||
|
||||
```bash
|
||||
npx -y @ionic/cli config get -g telemetry
|
||||
npx -y @capacitor/cli telemetry
|
||||
npx @ionic/cli config get -g telemetry
|
||||
npx @capacitor/cli telemetry
|
||||
```
|
||||
|
||||
:::
|
||||
@ -173,7 +174,7 @@ These objects can be parsed with the SheetJS `read` method[^4]. The SheetJS
|
||||
`sheet_to_json` method[^5] with the option `header: 1` generates an array of
|
||||
arrays which can be assigned to the page state:
|
||||
|
||||
```ts title="Read file from device and update state"
|
||||
```ts
|
||||
/* read a workbook file */
|
||||
const ab: ArrayBuffer = await this.file.readAsArrayBuffer(url, filename);
|
||||
/* parse */
|
||||
@ -194,7 +195,7 @@ worksheet object. The `book_new` and `book_append_sheet` helpers[^7] generate a
|
||||
workbook object. The SheetJS `write` method[^8] with the option `type: "array"`
|
||||
will generate an `ArrayBuffer`, from which a `Blob` can be created:
|
||||
|
||||
```ts title="Export state data to XLSX workbook and write to device"
|
||||
```ts
|
||||
/* generate worksheet */
|
||||
const ws: XLSX.WorkSheet = XLSX.utils.aoa_to_sheet(this.data);
|
||||
|
||||
@ -230,12 +231,7 @@ known location. After writing, an alert will display the location of the file.
|
||||
|
||||
### Platform Setup
|
||||
|
||||
0) Disable telemetry:
|
||||
|
||||
```bash
|
||||
npx -y @ionic/cli config set -g telemetry false
|
||||
npx -y @capacitor/cli telemetry off
|
||||
```
|
||||
0) Disable telemetry as noted in the warning.
|
||||
|
||||
1) Follow the official instructions for iOS and Android development[^9].
|
||||
|
||||
@ -272,9 +268,9 @@ ionic start SheetJSIonic blank --type angular --cordova --quiet --no-git --no-li
|
||||
|
||||
When asked to select `NgModules` or `Standalone Components`, select `NgModules`
|
||||
|
||||
If a prompt asks to confirm Cordova use, enter <kbd>Y</kbd> to continue.
|
||||
If a prompt asks to confirm Cordova use, enter `Yes` to continue.
|
||||
|
||||
If a prompt asks to create an Ionic account, enter <kbd>N</kbd> to opt out.
|
||||
If a prompt asks about creating an Ionic account, enter `N` to opt out.
|
||||
|
||||
:::caution pass
|
||||
|
||||
@ -296,9 +292,28 @@ cd ..
|
||||
```bash
|
||||
cd SheetJSIonic
|
||||
ionic cordova plugin add cordova-plugin-file
|
||||
ionic cordova platform add ios --confirm
|
||||
ionic cordova platform add android --confirm
|
||||
npm i --save @awesome-cordova-plugins/core @awesome-cordova-plugins/file @ionic/cordova-builders
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
If `cordova-plugin-file` is added before the platforms, installation may fail:
|
||||
|
||||
```
|
||||
CordovaError: Could not load API for ios project
|
||||
```
|
||||
|
||||
This can be resolved by removing and reinstalling the `ios` platform:
|
||||
|
||||
```bash
|
||||
ionic cordova platform rm ios
|
||||
ionic cordova platform add ios --confirm
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
If the `npm i` step fails due to `rxjs` resolution, add the highlighted lines
|
||||
@ -326,9 +341,9 @@ After adding the lines, the `npm i` command will succeed.
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
6) Add the `@awesome-cordova-plugins/file` plugin to `src/app/app.module.ts`:
|
||||
6) Add `@awesome-cordova-plugins/file` to the module. Differences highlighted below:
|
||||
|
||||
```ts title="src/app/app.module.ts (add highlighted lines)"
|
||||
```ts title="src/app/app.module.ts"
|
||||
import { AppComponent } from './app.component';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
|
||||
@ -352,167 +367,9 @@ export class AppModule {}
|
||||
curl -o src/app/home/home.page.ts -L https://docs.sheetjs.com/ionic/home.page.ts
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
In PowerShell, the command may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'L'.
|
||||
```
|
||||
|
||||
`curl.exe` must be invoked directly:
|
||||
|
||||
```bash
|
||||
curl.exe -o src/app/home/home.page.ts -L https://docs.sheetjs.com/ionic/home.page.ts
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Android
|
||||
|
||||
8) Add the Android platform to the project:
|
||||
|
||||
```bash
|
||||
ionic cordova platform add android --confirm
|
||||
npm i --save cordova-android
|
||||
```
|
||||
|
||||
9) Enable file reading and writing in the Android app.
|
||||
|
||||
Edit `platforms/android/app/src/main/AndroidManifest.xml` and add the following
|
||||
two lines before the `application` tag:
|
||||
|
||||
```xml title="platforms/android/app/src/main/AndroidManifest.xml (add to file)"
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
```
|
||||
|
||||
In the `application` tag, add the attribute `android:requestLegacyExternalStorage="true"`.
|
||||
|
||||
10) Build the app and start the emulator:
|
||||
|
||||
```bash
|
||||
ionic cordova emulate android
|
||||
```
|
||||
|
||||
If prompted to share pseudonymous usage data with Google, type <kbd>N</kbd> and
|
||||
press <kbd>Enter</kbd> to opt out.
|
||||
|
||||
If prompted to share anonymous usage data with Cordova, type <kbd>N</kbd>.
|
||||
|
||||
When the app is loaded, a list of Presidents should be displayed. This list is
|
||||
dynamically generated by fetching and parsing a test file.
|
||||
|
||||
:::caution pass
|
||||
|
||||
In some test runs, `cordova build android --emulator` step failed with error:
|
||||
|
||||
```
|
||||
Could not find or parse valid build output file
|
||||
```
|
||||
|
||||
This was resolved by forcefully installing `cordova-android`:
|
||||
|
||||
```bash
|
||||
npm i --save cordova-android
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
In some test runs, Ionic could not find the emulator:
|
||||
|
||||
```
|
||||
ERR_NO_TARGET: No target devices/emulators available.
|
||||
```
|
||||
|
||||
The target emulator can be found by running
|
||||
|
||||
```bash
|
||||
avdmanager list avd
|
||||
```
|
||||
|
||||
In a test run, the output showed a Pixel 3a with the following details:
|
||||
|
||||
```text
|
||||
// highlight-next-line
|
||||
Name: Pixel_3a_API_34
|
||||
Device: pixel_3a (Google)
|
||||
Path: /Users/SheetJS/.android/avd/Pixel_3a_API_34.avd
|
||||
```
|
||||
|
||||
The Ionic command accepts a `--target` flag. Pass the emulator name:
|
||||
|
||||
```bash
|
||||
ionic cordova emulate android --target=Pixel_3a_API_34
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
In some tests, the build failed with a Gradle error:
|
||||
|
||||
```
|
||||
Could not find an installed version of Gradle either in Android Studio,
|
||||
or on your system to install the gradle wrapper. Please include gradle
|
||||
in your path or install Android Studio
|
||||
```
|
||||
|
||||
On macOS, this issue was resolved by installing Gradle with Homebrew manager:
|
||||
|
||||
```bash
|
||||
brew install gradle
|
||||
```
|
||||
|
||||
In Windows, Gradle must be installed manually[^11]
|
||||
|
||||
:::
|
||||
|
||||
:::danger pass
|
||||
|
||||
When the demo was last tested on Android, reading files worked as expected.
|
||||
However, the generated files were not externally visible from the Files app.
|
||||
|
||||
**This is a known bug with Android SDK 33 and the underlying file plugins!**
|
||||
|
||||
:::
|
||||
|
||||
### iOS
|
||||
|
||||
:::danger pass
|
||||
|
||||
**iOS testing can only be performed on Apple hardware running macOS!**
|
||||
|
||||
Xcode and iOS simulators are not available on Windows or Linux.
|
||||
|
||||
:::
|
||||
|
||||
11) Add the iOS platform to the project:
|
||||
|
||||
```bash
|
||||
ionic cordova platform add ios --confirm
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
If `cordova-plugin-file` is added before the platforms, installation may fail:
|
||||
|
||||
```
|
||||
CordovaError: Could not load API for ios project
|
||||
```
|
||||
|
||||
This can be resolved by removing and reinstalling the `ios` platform:
|
||||
|
||||
```bash
|
||||
ionic cordova platform rm ios
|
||||
ionic cordova platform add ios --confirm
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
12) Enable file sharing and make the documents folder visible in the iOS app.
|
||||
8) Enable file sharing and make the documents folder visible in the iOS app.
|
||||
Add the following lines to `platforms/ios/SheetJSIonic/SheetJSIonic-Info.plist`:
|
||||
|
||||
```xml title="platforms/ios/SheetJSIonic/SheetJSIonic-Info.plist (add to file)"
|
||||
@ -529,7 +386,7 @@ Add the following lines to `platforms/ios/SheetJSIonic/SheetJSIonic-Info.plist`:
|
||||
|
||||
(The root element of the document is `plist` and it contains one `dict` child)
|
||||
|
||||
13) Build the app and start the simulator
|
||||
9) Build the app and start the simulator
|
||||
|
||||
```bash
|
||||
ionic cordova emulate ios
|
||||
@ -598,86 +455,71 @@ ng add @ionic/cordova-builders
|
||||
|
||||
:::
|
||||
|
||||
### iOS Device
|
||||
### Android
|
||||
|
||||
14) Connect an iOS device to the computer and "Trust" the device if prompted.
|
||||
10) Enable file reading and writing in the Android app.
|
||||
|
||||
15) Enable code signing for the project:
|
||||
Edit `platforms/android/app/src/main/AndroidManifest.xml` and add the following
|
||||
two lines before the `application` tag:
|
||||
|
||||
Open the `SheetJSIonic.xcodeproj` project in Xcode:
|
||||
```xml title="platforms/android/app/src/main/AndroidManifest.xml (add to file)"
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
```
|
||||
|
||||
In the `application` tag, add the attribute `android:requestLegacyExternalStorage="true"`.
|
||||
|
||||
11) Build the app and start the emulator
|
||||
|
||||
```bash
|
||||
open platforms/ios/SheetJSIonic.xcodeproj
|
||||
ionic cordova emulate android
|
||||
```
|
||||
|
||||
Select the "SheetJSIonic" project in the "Project navigator" side panel.
|
||||
When the app is loaded, a list of Presidents should be displayed. This list is
|
||||
dynamically generated by fetching and parsing a test file.
|
||||
|
||||
In the main panel, select "Signing & Capabilities".
|
||||
:::caution pass
|
||||
|
||||
In the "Team" dropdown, select a certificate.
|
||||
In some test runs, `cordova build android --emulator` step failed with error:
|
||||
|
||||
In the "Bundle Identifier" text box, enter `com.sheetjs.SheetJSIonic`
|
||||
```
|
||||
Could not find or parse valid build output file
|
||||
```
|
||||
|
||||
16) Launch the app on the device:
|
||||
This was resolved by forcefully installing `cordova-android`:
|
||||
|
||||
```bash
|
||||
ionic cordova run ios --device --verbose
|
||||
```
|
||||
|
||||
:::info pass
|
||||
|
||||
In the most recent test, the `native-run ios` command failed with
|
||||
|
||||
```
|
||||
[native-run] ERR_UNKNOWN: Path 'platforms/ios/build/device/SheetJSIonic.ipa' not found
|
||||
```
|
||||
|
||||
Inspecting `platforms/ios/build/`, the actual folder name was:
|
||||
|
||||
```bash
|
||||
% ls platforms/ios/build
|
||||
#highlight-next-line
|
||||
Debug-iphoneos
|
||||
```
|
||||
|
||||
To force `native-run` to use the device, the name must be found by inspecting
|
||||
the output of `native-run ios --list`:
|
||||
|
||||
```bash
|
||||
% native-run ios --list
|
||||
|
||||
Connected Devices:
|
||||
|
||||
Name API Target ID
|
||||
---------------------------------------------
|
||||
SheetJS iOS 15.6 12345678-90ABCDEF12345678
|
||||
|
||||
```
|
||||
|
||||
`native-run` accepts a `--device` flag. Pass the device name:
|
||||
|
||||
|
||||
```bash
|
||||
native-run ios --app platforms/ios/build/Debug-iphoneos/SheetJSIonic.ipa --device SheetJS
|
||||
npm i --save cordova-android
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
17) Test the app.
|
||||
:::caution pass
|
||||
|
||||
The app will fetch a file and display the contents in a table.
|
||||
In some tests, the build failed with a Gradle error:
|
||||
|
||||
Tap "Export Data" to create a file. To find the file, switch to the "Files" app
|
||||
and browse "On My iPhone" > "SheetJSIonic". There should be a new spreadsheet
|
||||
named "SheetJSIonic".
|
||||
```
|
||||
Could not find an installed version of Gradle either in Android Studio,
|
||||
or on your system to install the gradle wrapper. Please include gradle
|
||||
in your path or install Android Studio
|
||||
```
|
||||
|
||||
Switch to the "Numbers" app and open that file. Tap "EDIT" to make changes.
|
||||
Change cell A7 to "SheetJS Dev" and cell B7 to 47. Tap "Done" and close the app.
|
||||
On macOS, this issue was resolved by installing gradle with Homebrew manager:
|
||||
|
||||
Switch back to "SheetJSIonic" and tap "Import Data". Tap "Choose Files" in the
|
||||
popup. Tap "Browse" in the bottom of the sheet. Navigate to "On My iPhone" >
|
||||
"SheetJSIonic" and tap the new "SheetJSIonic" spreadsheet. The screen will show
|
||||
the file with the new line.
|
||||
```bash
|
||||
brew install gradle
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::danger pass
|
||||
|
||||
When the demo was last tested on Android, reading files worked as expected.
|
||||
However, the generated files were not externally visible from the Files app.
|
||||
|
||||
**This is a known bug with Android SDK 33 and the underlying file plugins!**
|
||||
|
||||
:::
|
||||
|
||||
[^1]: See ["Array of Arrays" in the API reference](/docs/api/utilities/array#array-of-arrays)
|
||||
[^2]: See [`ion-grid`](https://ionicframework.com/docs/api/grid) in the Ionic documentation.
|
||||
@ -689,4 +531,3 @@ the file with the new line.
|
||||
[^8]: See [`write` in "Writing Files"](/docs/api/write-options)
|
||||
[^9]: See ["Developing for iOS"](https://ionic-docs-o31kiyk8l-ionic1.vercel.app/docs/v6/developing/ios) and ["Developing for Android"](https://ionic-docs-o31kiyk8l-ionic1.vercel.app/docs/v6/developing/android). The Ionic team removed these pages from the official docs site and recommend the `vercel.app` docs site.
|
||||
[^10]: See the [JDK Archive](https://jdk.java.net/archive/) for Java 17 JDK download links.
|
||||
[^11]: See ["Installing manually"](https://gradle.org/install/#manually) in the Gradle documentation.
|
@ -44,17 +44,18 @@ This demo was tested in the following environments:
|
||||
|
||||
| OS | Device | CapacitorJS + FS | Date |
|
||||
|:-----------|:--------------------|:------------------|:-----------|
|
||||
| Android 30 | NVIDIA Shield | `6.2.0` / `6.0.3` | 2025-01-19 |
|
||||
| iOS 15.1 | iPad Pro | `6.2.0` / `6.0.3` | 2025-01-19 |
|
||||
| Android 30 | NVIDIA Shield | `6.0.0` / `6.0.0` | 2024-06-02 |
|
||||
| iOS 15.1 | iPad Pro | `6.0.0` / `6.0.0` | 2024-06-02 |
|
||||
|
||||
**Simulators**
|
||||
|
||||
| OS | Device | CapacitorJS + FS | Dev Platform | Date |
|
||||
|:-----------|:--------------------|:------------------|:-------------|:-----------|
|
||||
| Android 34 | Pixel 3a | `7.1.0` / `7.0.0` | `darwin-arm` | 2025-03-30 |
|
||||
| iOS 18.2 | iPhone 16 Pro Max | `7.1.0` / `7.0.0` | `darwin-arm` | 2025-03-30 |
|
||||
| Android 35 | Pixel 9 | `6.2.0` / `6.0.2` | `win11-x64` | 2024-12-21 |
|
||||
| Android 35 | Pixel 9 | `6.2.0` / `6.0.2` | `linux-x64` | 2025-01-02 |
|
||||
| Android 34 | Pixel 3a | `6.0.0` / `6.0.0` | `darwin-x64` | 2024-06-02 |
|
||||
| iOS 17.5 | iPhone 15 Pro Max | `6.0.0` / `6.0.0` | `darwin-x64` | 2024-06-02 |
|
||||
| Android 34 | Pixel 3a | `6.0.0` / `6.0.0` | `darwin-arm` | 2024-06-02 |
|
||||
| iOS 17.5 | iPhone 15 Pro Max | `6.0.0` / `6.0.0` | `darwin-arm` | 2024-06-02 |
|
||||
| Android 34 | Pixel 3a | `6.0.0` / `6.0.0` | `win10-x64` | 2024-05-28 |
|
||||
|
||||
:::
|
||||
|
||||
@ -92,7 +93,7 @@ activated, CapacitorJS will show a file picker. After the user selects a file,
|
||||
the element will receive a `change` event.
|
||||
|
||||
The following example parses the selected file using the SheetJS `read`[^1]
|
||||
method, generates an HTML table from the first sheet using `sheet_to_html`[^2],
|
||||
method, generates a HTML table from the first sheet using `sheet_to_html`[^2],
|
||||
and displays the table by setting the `innerHTML` attribute of a `div` element:
|
||||
|
||||
```html title="Sample component for data import"
|
||||
@ -127,7 +128,7 @@ Starting from a SheetJS workbook object[^3], the `write` method with the option
|
||||
The `@capacitor/filesystem` plugin can write Base64 strings to the device.
|
||||
|
||||
The following example uses the SheetJS `table_to_book` method[^5] to create a
|
||||
workbook object from an HTML table. The workbook object is exported to the XLSX
|
||||
workbook object from a HTML table. The workbook object is exported to the XLSX
|
||||
format and written to the device.
|
||||
|
||||
```html title="Sample component for data export"
|
||||
@ -215,9 +216,7 @@ iOS development is only supported on macOS.
|
||||
<details>
|
||||
<summary><b>Installation Notes</b> (click to show)</summary>
|
||||
|
||||
For Android development, CapacitorJS requires a Java version compatible with the
|
||||
expected Gradle version. When this demo was tested against CapacitorJS `6.2.0`,
|
||||
Java 20 was required to support Gradle `8.2.1`.
|
||||
CapacitorJS requires Java 17.
|
||||
|
||||
</details>
|
||||
|
||||
@ -277,20 +276,6 @@ npm i --save @capacitor/android
|
||||
npx cap add android
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
If the wrong Java version is installed, the last command will fail with a
|
||||
message that references a "class file major version"
|
||||
|
||||
```
|
||||
> BUG! exception in phase 'semantic analysis' in source unit '_BuildScript_' Unsupported class file major version 67
|
||||
```
|
||||
|
||||
The correct Java version must be installed. When this demo was last tested, Java
|
||||
20 was compatible with CapacitorJS Android projects.
|
||||
|
||||
:::
|
||||
|
||||
7) Enable file reading and writing in the Android app.
|
||||
|
||||
Add the highlighted lines to `android/app/src/main/AndroidManifest.xml` after
|
||||
@ -307,9 +292,7 @@ the `Permissions` comment:
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
```
|
||||
|
||||
8) Start the Android simulator through Android Studio.
|
||||
|
||||
9) Run the app in the simulator:
|
||||
8) Run the app in the simulator:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
@ -319,12 +302,10 @@ npx cap run android
|
||||
|
||||
The app should look like the screenshot at the top of the page.
|
||||
|
||||
10) Test the export functionality.
|
||||
9) Test the export functionality.
|
||||
|
||||
Touch "Export XLSX". If the emulator asks for permission, tap "Allow". A popup
|
||||
will show the exported path.
|
||||
|
||||

|
||||
Touch "Export XLSX" and the emulator will ask for permission. Tap "Allow" and a
|
||||
popup will be displayed with a path.
|
||||
|
||||
Open the "Files" app in the simulator, tap the `≡` icon and tap "Documents". Tap
|
||||
the "Documents" folder to find `SheetJSCap.xlsx`.
|
||||
@ -332,51 +313,57 @@ the "Documents" folder to find `SheetJSCap.xlsx`.
|
||||
<details open>
|
||||
<summary><b>Downloading the generated file</b> (click to hide)</summary>
|
||||
|
||||
`adb` must be run from the root user:
|
||||
|
||||
```bash
|
||||
adb root
|
||||
```
|
||||
|
||||
The file location can be found by searching for `SheetJSCap.xlsx`:
|
||||
|
||||
```bash
|
||||
adb exec-out find / -name SheetJSCap.xlsx
|
||||
```
|
||||
|
||||
There may be a number of error messages that start with `find:`. There will be
|
||||
at least one line starting with `/`:
|
||||
The first line of the output starting with `/` is the desired path:
|
||||
|
||||
```text
|
||||
find: /proc/8533/task/8533/exe: No such file or directory
|
||||
find: /proc/8533/exe: No such file or directory
|
||||
// highlight-next-line
|
||||
/data/media/0/Documents/SheetJSCap.xlsx
|
||||
/storage/emulated/0/Documents/SheetJSCap.xlsx
|
||||
```
|
||||
|
||||
The `/storage` path can be pulled using `adb pull`:
|
||||
`adb pull` can download the file:
|
||||
|
||||
```bash
|
||||
adb pull "/storage/emulated/0/Documents/SheetJSCap.xlsx" SheetJSCap.xlsx
|
||||
adb pull "/data/media/0/Documents/SheetJSCap.xlsx" SheetJSCap.xlsx
|
||||
```
|
||||
|
||||
`SheetJSCap.xlsx` can be opened with a spreadsheet editor such as Excel.
|
||||
|
||||
</details>
|
||||
|
||||
11) Test the import functionality.
|
||||
10) Test the import functionality.
|
||||
|
||||
Edit `SheetJSCap.xlsx`, setting cell `A7` to `SheetJS Dev` and setting cell `B7`
|
||||
to `47`. Save the file.
|
||||
Create a spreadsheet or find an existing file. Click and drag the file into the
|
||||
Android emulator window. The file will be uploaded to a Downloads folder in the
|
||||
emulator.
|
||||
|
||||
Click and drag the file into the Android emulator window. The file will be
|
||||
uploaded to a Downloads folder in the emulator.
|
||||
|
||||
Switch back to the app and tap "Choose File". In the selector, tap `≡`, select
|
||||
"Downloads" and tap `SheetJSCap.xlsx`. The table will refresh with the new row.
|
||||
Tap on "Choose File" in the app. In the selector, tap `≡` and select "Downloads"
|
||||
to find the uploaded file. After selecting the file, the table will refresh.
|
||||
|
||||
### iOS
|
||||
|
||||
12) Create iOS app.
|
||||
11) Create iOS app
|
||||
|
||||
```bash
|
||||
npm i --save @capacitor/ios
|
||||
npx cap add ios
|
||||
```
|
||||
|
||||
13) Enable file sharing and make the documents folder visible in the iOS app.
|
||||
12) Enable file sharing and make the documents folder visible in the iOS app.
|
||||
The following lines must be added to `ios/App/App/Info.plist`:
|
||||
|
||||
```xml title="ios/App/App/Info.plist (add to file)"
|
||||
@ -393,7 +380,7 @@ The following lines must be added to `ios/App/App/Info.plist`:
|
||||
|
||||
(The root element of the document is `plist` and it contains one `dict` child)
|
||||
|
||||
14) Run the app in the simulator.
|
||||
13) Run the app in the simulator
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
@ -401,16 +388,14 @@ npx cap sync
|
||||
npx cap run ios
|
||||
```
|
||||
|
||||
If prompted to select a target device, select "iPhone 16 Pro Max (simulator)".
|
||||
If prompted to select a target device, select "iPhone 15 Pro Max (simulator)".
|
||||
|
||||
The app should look like the screenshot at the top of the page.
|
||||
|
||||
15) Test the export functionality.
|
||||
14) Test the export functionality.
|
||||
|
||||
Touch "Export XLSX" and a popup will be displayed.
|
||||
|
||||

|
||||
|
||||
To see the generated file, switch to the "Files" app in the simulator and look
|
||||
for `SheetJSCap.xlsx` in "On My iPhone" > "`sheetjs-cap`"
|
||||
|
||||
@ -426,41 +411,24 @@ find ~/Library/Developer -name SheetJSCap.xlsx
|
||||
|
||||
</details>
|
||||
|
||||
16) Test the import functionality.
|
||||
15) Test the import functionality.
|
||||
|
||||
Edit `SheetJSCap.xlsx`, setting cell `A7` to `SheetJS Dev` and setting cell `B7`
|
||||
to `47`. Save the file.
|
||||
Create a spreadsheet or find an existing file. Click and drag the file into the
|
||||
iOS simulator window. The simulator will show a picker for saving the file.
|
||||
Select the `sheetjs-cap` folder and tap "Save".
|
||||
|
||||
Click and drag the file into the iOS simulator window. The simulator will show a
|
||||
picker for saving the file. Select the `sheetjs-cap` folder and tap "Save". If
|
||||
prompted to "Replace Existing Items?", tap "Replace".
|
||||
|
||||
Switch back to the app and tap "Choose File". Tap "Choose File" in the popup.
|
||||
In the picker, tap "Recents" and select the newest `SheetJSCap` file. The table
|
||||
will refresh with the new data.
|
||||
Tap on "Choose File" in the app and "Choose File" in the popup. In the picker,
|
||||
tap "Recents" and select the new file. After selecting the file, the table will refresh.
|
||||
|
||||
### Android Device
|
||||
|
||||
17) Connect an Android device using a USB cable.
|
||||
16) Connect an Android device using a USB cable.
|
||||
|
||||
If the device asks to allow USB debugging, tap "Allow".
|
||||
|
||||
18) Confirm the device is detected by `adb`.
|
||||
17) Close any Android / iOS emulators.
|
||||
|
||||
```bash
|
||||
adb devices
|
||||
```
|
||||
|
||||
If the device is detected, the command will list the device:
|
||||
|
||||
```text title="Expected output"
|
||||
List of devices attached
|
||||
1234567890 device
|
||||
```
|
||||
|
||||
19) Close any Android / iOS emulators.
|
||||
|
||||
20) Build APK and run on device:
|
||||
18) Build APK and run on device:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
@ -499,19 +467,17 @@ to the `application` open tag in `android/app/src/main/AndroidManifest.xml`:
|
||||
|
||||
### iOS Device
|
||||
|
||||
21) Connect an iOS device using a USB cable
|
||||
19) Connect an iOS device using a USB cable
|
||||
|
||||
If prompted to "Trust This Computer", tap "Trust" and enter the device passcode.
|
||||
20) Close any Android / iOS emulators.
|
||||
|
||||
22) Close any Android / iOS emulators.
|
||||
|
||||
23) Enable developer code signing certificates.
|
||||
21) Enable developer code signing certificates.
|
||||
|
||||
Open `ios/App/App.xcworkspace` in Xcode. Select the "Project Navigator" and
|
||||
select the "App" project. In the main view, select "Signing & Capabilities".
|
||||
Under "Signing", select a team in the dropdown menu.
|
||||
|
||||
24) Run on device:
|
||||
22) Run on device:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
@ -521,35 +487,6 @@ npx cap run ios
|
||||
|
||||
When prompted to select a target device, select the real device in the list.
|
||||
|
||||
:::info pass
|
||||
|
||||
In some test runs, the build failed with a provisioning error:
|
||||
|
||||
```
|
||||
error: Provisioning profile "iOS Team Provisioning Profile: com.sheetjs.cap" doesn't include the currently selected device "SheetJS Test Device" (identifier 12345678-9ABCDEF012345678). (in target 'App' from project 'App')
|
||||
```
|
||||
|
||||
This error was resolved by manually selecting the device as the primary target
|
||||
in the Xcode workspace.
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
In some tests, the app failed to launch with a "Untrusted Developer" error.
|
||||
|
||||
Switch to the Settings app and select General > VPN & Device Management. There
|
||||
will be a new item in the "DEVELOPER APP" section. Tap the line and verify that
|
||||
`sheetjs-cap` is listed in the screen. Tap "Trust" and tap "Trust" in the popup.
|
||||
|
||||
After trusting the certificate, re-run the app:
|
||||
|
||||
```bash
|
||||
npx cap run ios
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
[^1]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^2]: See [`sheet_to_html` in "Utilities"](/docs/api/utilities/html#html-table-output)
|
||||
[^3]: See ["Workbook Object"](/docs/csf/book)
|
||||
|
@ -14,9 +14,6 @@ import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
export const r = {style: {color:"red"}};
|
||||
export const g = {style: {color:"green"}};
|
||||
|
||||
Dart[^1] + Flutter[^2] is a popular cross-platform app framework. JavaScript
|
||||
code can be run through [embedded engines](/docs/demos/engines).
|
||||
|
||||
@ -50,16 +47,15 @@ This demo was tested in the following environments:
|
||||
|
||||
| OS | Device | Dart | Flutter | Date |
|
||||
|:-----------|:------------------|:--------|:---------|:-----------|
|
||||
| Android 34 | NVIDIA Shield | `3.7.2` | `3.29.2` | 2025-03-31 |
|
||||
| iOS 15.6 | iPhone 13 Pro Max | `3.7.2` | `3.29.2` | 2025-03-31 |
|
||||
| Android 30 | NVIDIA Shield | `3.4.3` | `3.22.2` | 2024-06-09 |
|
||||
| iOS 15.1 | iPad Pro | `3.4.3` | `3.22.2` | 2024-06-09 |
|
||||
|
||||
**Simulators**
|
||||
|
||||
| OS | Device | Dart | Flutter | Dev Platform | Date |
|
||||
|:-----------|:------------------|:--------|:---------|:-------------|:-----------|
|
||||
| Android 35 | Pixel 9 Pro XL | `3.7.2` | `3.29.2` | `darwin-x64` | 2025-03-31 |
|
||||
| iOS 18.3 | iPhone 16 Pro Max | `3.7.2` | `3.29.2` | `darwin-x64` | 2025-03-31 |
|
||||
| Android 35 | Pixel 3a | `3.5.0` | `3.24.0` | `win11-x64` | 2024-08-10 |
|
||||
| Android 34 | Pixel 3a | `3.4.3` | `3.22.2` | `darwin-x64` | 2024-06-09 |
|
||||
| iOS 17.5 | iPhone 15 Pro Max | `3.4.3` | `3.22.2` | `darwin-x64` | 2024-06-09 |
|
||||
|
||||
:::
|
||||
|
||||
@ -228,24 +224,24 @@ Run `flutter doctor` and confirm the following items are checked:
|
||||
<Tabs groupId="os">
|
||||
<TabItem value="linux" label="Linux">
|
||||
|
||||
<pre>
|
||||
<span {...g}>[✓]</span> Android toolchain - develop for Android devices (Android SDK version 34.0.0)
|
||||
</pre>
|
||||
```
|
||||
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="macos" label="macOS" default>
|
||||
|
||||
<pre>
|
||||
<span {...g}>[✓]</span> Android toolchain - develop for Android devices (Android SDK version 36.0.0)
|
||||
<span {...g}>[✓]</span> Xcode - develop for iOS and macOS (Xcode 16.2)
|
||||
</pre>
|
||||
```
|
||||
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
|
||||
[✓] Xcode - develop for iOS and macOS (Xcode 15.4)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="win" label="Windows">
|
||||
|
||||
<pre>
|
||||
<span {...g}>[✓]</span> Android toolchain - develop for Android devices (Android SDK version 35.0.0)
|
||||
</pre>
|
||||
```
|
||||
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
@ -294,11 +290,6 @@ In local testing, there were issues with the Android toolchain:
|
||||
error: Android sdkmanager not found. Update to the latest Android SDK and ensure that the cmdline-tools are installed to resolve this.
|
||||
```
|
||||
|
||||
Android Studio does not install `Android SDK Command-Line Tools` by default. It
|
||||
must be installed manually.
|
||||
|
||||
Assuming the command-line tools are installed
|
||||
|
||||
This was fixed by switching to Java 20, installing `Android SDK 33`, and rolling
|
||||
back to `Android SDK Command-Line Tools (revision: 10.0)`
|
||||
|
||||
@ -316,12 +307,13 @@ If Google Chrome is not installed, `flutter doctor` will show an issue:
|
||||
|
||||
If Chromium is installed, the environment variable should be manually assigned:
|
||||
|
||||
```bash
|
||||
export CHROME_EXECUTABLE=/Applications/Chromium.app/Contents/MacOS/Chromium
|
||||
```
|
||||
|
||||
<Tabs groupId="os">
|
||||
<TabItem value="linux" label="Linux">
|
||||
|
||||
The `CHROME_EXECUTABLE` environment variable should be set to the path to the
|
||||
`chrome` binary. This path differs between distributions and package managers.
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="macos" label="macOS">
|
||||
|
||||
@ -332,18 +324,6 @@ export CHROME_EXECUTABLE=/Applications/Chromium.app/Contents/MacOS/Chromium
|
||||
</TabItem>
|
||||
<TabItem value="win" label="Windows">
|
||||
|
||||
Type `env` in the search bar and select "Edit the system environment variables".
|
||||
|
||||
In the new window, click the "Environment Variables..." button.
|
||||
|
||||
In the new window, look for the "System variables" section and click "New..."
|
||||
|
||||
Set the "Variable name" to `CHROME_EXECUTABLE` and the value to the path to the
|
||||
program. When this demo was last tested, Chromium was installed for the local
|
||||
user at `C:\Users\USERNAME\AppData\Local\Chromium\Application\chrome.exe` .
|
||||
|
||||
Click "OK" in each window (3 windows) and restart your computer.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
@ -351,49 +331,15 @@ Click "OK" in each window (3 windows) and restart your computer.
|
||||
|
||||
</details>
|
||||
|
||||
List all available emulators:
|
||||
|
||||
```bash
|
||||
flutter emulators
|
||||
```
|
||||
|
||||
<Tabs groupId="os">
|
||||
<TabItem value="linux" label="Linux">
|
||||
|
||||
There should be at least one `android` emulator:
|
||||
Run `flutter emulators` and check for both `ios` and `android` emulators:
|
||||
|
||||
```
|
||||
Id • Name • Manufacturer • Platform
|
||||
|
||||
Pixel_3a_API_35 • Pixel 3a API 35 • Google • android
|
||||
apple_ios_simulator • iOS Simulator • Apple • ios
|
||||
Pixel_3a_API_34 • Pixel 3a API 34 • Google • android
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="macos" label="macOS">
|
||||
|
||||
There should be at least one `android` emulator and one `ios` simulator:
|
||||
|
||||
```
|
||||
Id • Name • Manufacturer • Platform
|
||||
|
||||
apple_ios_simulator • iOS Simulator • Apple • ios
|
||||
Pixel_9_Pro_XL_API_35 • Pixel 9 Pro XL API 35 • Google • android
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="win" label="Windows">
|
||||
|
||||
There should be at least one `android` emulator:
|
||||
|
||||
```
|
||||
Id • Name • Manufacturer • Platform
|
||||
|
||||
Pixel_3a_API_35 • Pixel 3a API 35 • Google • android
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
1) Disable telemetry. The following commands were confirmed to work:
|
||||
|
||||
```bash
|
||||
@ -429,18 +375,16 @@ List the available emulators with `flutter emulators`:
|
||||
% flutter emulators
|
||||
2 available emulators:
|
||||
|
||||
Id • Name • Manufacturer • Platform
|
||||
|
||||
apple_ios_simulator • iOS Simulator • Apple • ios
|
||||
Pixel_9_Pro_XL_API_35 • Pixel 9 Pro XL API 35 • Google • android
|
||||
^^^^^^^^^^^^^^^^^^^^^--- the first column is the name for `emulator avd`
|
||||
apple_ios_simulator • iOS Simulator • Apple • ios
|
||||
Pixel_3a_API_34 • Pixel 3a API 34 • Google • android
|
||||
^^^^^^^^^^^^^^^--- the first column is the name
|
||||
```
|
||||
|
||||
The first column shows the name that should be passed to `emulator -avd`. In a
|
||||
previous test, the name was `Pixel_9_Pro_XL_API_35` and the launch command was:
|
||||
previous test, the name was `Pixel_3a_API_34` and the launch command was:
|
||||
|
||||
```bash
|
||||
emulator -avd Pixel_9_Pro_XL_API_35
|
||||
emulator -avd Pixel_3a_API_34
|
||||
```
|
||||
|
||||
:::note pass
|
||||
@ -450,7 +394,7 @@ On macOS, `~/Library/Android/sdk/emulator/` is the typical location for the
|
||||
|
||||
```bash
|
||||
export PATH="$PATH":~/Library/Android/sdk/emulator
|
||||
emulator -avd Pixel_9_Pro_XL_API_35
|
||||
emulator -avd Pixel_3a_API_34
|
||||
```
|
||||
|
||||
:::
|
||||
@ -505,32 +449,6 @@ Once the app loads, stop the terminal process and close the simulator.
|
||||
flutter pub add http csv flutter_js
|
||||
```
|
||||
|
||||
:::info pass
|
||||
|
||||
The command may fail in Windows with the following message:
|
||||
|
||||
<pre {...r}>
|
||||
Building with plugins requires symlink support.
|
||||
|
||||
Please enable Developer Mode in your system settings. Run
|
||||
{` `}start ms-settings:developers
|
||||
to open settings.
|
||||
</pre>
|
||||
|
||||
As stated, "Developer Mode" must be enabled:
|
||||
|
||||
1) Run `start ms-settings:developers`
|
||||
|
||||
2) In the panel, enable "Developer Mode" and click "Yes" in the popup.
|
||||
|
||||
3) Reinstall dependencies:
|
||||
|
||||
```bash
|
||||
flutter pub add http csv flutter_js
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
6) Open `pubspec.yaml` with a text editor. Search for the line that starts with
|
||||
`flutter:` (no whitespace) and add the highlighted lines:
|
||||
|
||||
@ -554,50 +472,12 @@ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js
|
||||
cd ..`}
|
||||
</CodeBlock>
|
||||
|
||||
:::caution pass
|
||||
|
||||
PowerShell `curl` is incompatible with the official `curl` program. The command
|
||||
may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'LO'.
|
||||
```
|
||||
|
||||
`curl.exe` must be used instead:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
mkdir -p scripts
|
||||
cd scripts
|
||||
curl.exe -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
|
||||
curl.exe -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js
|
||||
cd ..`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
8) Download [`main.dart`](pathname:///flutter/main.dart) to `lib/main.dart`:
|
||||
|
||||
```bash
|
||||
curl -L -o lib/main.dart https://docs.sheetjs.com/flutter/main.dart
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
PowerShell `curl` is incompatible with the official `curl` program. The command
|
||||
may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'L'.
|
||||
```
|
||||
|
||||
`curl.exe` must be used instead:
|
||||
|
||||
```bash
|
||||
curl.exe -L -o lib/main.dart https://docs.sheetjs.com/flutter/main.dart
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Android
|
||||
|
||||
9) Start the Android emulator using the same instructions as Step 3.
|
||||
@ -611,41 +491,7 @@ flutter run
|
||||
The app fetches https://docs.sheetjs.com/pres.numbers, parses, converts data to
|
||||
an array of arrays, and presents the data in a Flutter `Table` widget.
|
||||
|
||||
<details>
|
||||
<summary><b>If emulator is not detected</b> (click to show)</summary>
|
||||
|
||||
In some test runs, `flutter run` did not automatically detect the emulator.
|
||||
|
||||
Run `flutter -v -d sheetjs run` and the command will fail. Inspect the output:
|
||||
|
||||
```text title="Command output"
|
||||
// highlight-next-line
|
||||
[ +6 ms] No supported devices found with name or id matching 'sheetjs'.
|
||||
[ ] The following devices were found:
|
||||
...
|
||||
// highlight-next-line
|
||||
[ +26 ms] sdk gphone64 arm64 (mobile) • emulator-5554 • android-arm64 • Android 13 (API 33) (emulator)
|
||||
[ ] macOS (desktop) • macos • darwin-arm64 • macOS 13.5.1 22G90 darwin-arm64
|
||||
...
|
||||
```
|
||||
|
||||
Search the output for `sheetjs`. After that line, search for the emulator list.
|
||||
One of the lines will correspond to the running emulator:
|
||||
|
||||
```
|
||||
[ +26 ms] sdk gphone64 arm64 (mobile) • emulator-5554 • android-arm64 • Android 13 (API 33) (emulator)
|
||||
^^^^^^^^^^^^^--- the second column is the name
|
||||
```
|
||||
|
||||
The second column is the device name. Assuming the name is `emulator-5554`, run:
|
||||
|
||||
```bash
|
||||
flutter -v -d emulator-5554 run
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
:::info Troubleshooting
|
||||
:::info pass
|
||||
|
||||
In some demo runs, the build failed with an Android SDK error:
|
||||
|
||||
@ -673,57 +519,6 @@ Searching for `minSdkVersion` should reveal the following line:
|
||||
minSdkVersion 21
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
In some demo runs, the build failed with an Android NDK error:
|
||||
|
||||
```
|
||||
Your project is configured with Android NDK 26.3.11579264, but the following plugin(s) depend on a different Android NDK version:
|
||||
- flutter_js requires Android NDK 27.0.12077973
|
||||
Fix this issue by using the highest Android NDK version (they are backward compatible).
|
||||
Add the following to /.../android/app/build.gradle.kts:
|
||||
|
||||
android {
|
||||
ndkVersion = "27.0.12077973"
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
This was fixed by editing `android/app/build.gradle.kts`.
|
||||
|
||||
Searching for `ndkVersion` should reveal the following line:
|
||||
|
||||
```text title="android\app\build.gradle.kts"
|
||||
ndkVersion = flutter.ndkVersion
|
||||
```
|
||||
|
||||
`flutter.ndkVersion` should be replaced with `27.0.12077973`:
|
||||
|
||||
```text title="android\app\build.gradle.kts"
|
||||
ndkVersion = "27.0.12077973"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
In some demo runs, the build failed with an Android namespace error:
|
||||
|
||||
```
|
||||
A problem occurred configuring project ':flutter_js'.
|
||||
> Could not create an instance of type com.android.build.api.variant.impl.LibraryVariantBuilderImpl.
|
||||
> Namespace not specified. Specify a namespace in the module's build file: /Users/sheetjs/.pub-cache/hosted/pub.dev/flutter_js-0.8.2/android/build.gradle. See https://d.android.com/r/tools/upgrade-assistant/set-namespace for information about setting the namespace.
|
||||
```
|
||||
|
||||
This affects `flutter_js` version `0.8.2`.
|
||||
|
||||
The file (`flutter_js-0.8.2/android/build.gradle`) should be manually edited. In
|
||||
the `android` block, add a `namespace` field:
|
||||
|
||||
```text title="cached flutter_js android/build.gradle (add highlighted line)"
|
||||
android {
|
||||
// highlight-next-line
|
||||
namespace "io.abner.flutter_js"
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
11) Close the Android emulator.
|
||||
@ -766,28 +561,6 @@ The list should include the device:
|
||||
flutter build apk --release
|
||||
```
|
||||
|
||||
:::info Troubleshooting
|
||||
|
||||
In some demo runs, the build failed with an Android resource error:
|
||||
|
||||
```
|
||||
Execution failed for task ':flutter_js:verifyReleaseResources'.
|
||||
> A failure occurred while executing com.android.build.gradle.tasks.VerifyLibraryResourcesTask$Action
|
||||
> Android resource linking failed
|
||||
ERROR: /private/tmp/sheetjs_flutter/build/flutter_js/intermediates/merged_res/release/mergeReleaseResources/values/values.xml:194: AAPT: error: resource android:attr/lStar not found.
|
||||
```
|
||||
|
||||
The file (`flutter_js-0.8.2/android/build.gradle`) should be manually edited. In
|
||||
the `android` block, force the `compileSdkVersion` to be `31`:
|
||||
|
||||
```text title="cached flutter_js android/build.gradle (add highlighted line)"
|
||||
android {
|
||||
// highlight-next-line
|
||||
compileSdkVersion 31
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
17) Install on the Android device:
|
||||
|
||||
```bash
|
||||
@ -850,16 +623,6 @@ device will ask for permission:
|
||||
|
||||
Tap "OK" to continue.
|
||||
|
||||
:::info pass
|
||||
|
||||
In some test runs, the app requested for local network access:
|
||||
|
||||
> "Sheetjs Flutter" would like to find and connect to devices on your local network.
|
||||
|
||||
Local network access is not required for the demo. Select "Don't Allow".
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
When this demo was last tested, the build failed with an error:
|
||||
|
@ -1,402 +0,0 @@
|
||||
---
|
||||
title: Sheets at Native Speed with Lynx
|
||||
sidebar_label: Lynx
|
||||
description: Build data-intensive mobile apps with Lynx. Seamlessly integrate spreadsheets into your app using SheetJS. Securely process and generate Excel files in the field.
|
||||
pagination_prev: demos/static/index
|
||||
pagination_next: demos/desktop/index
|
||||
sidebar_position: 7
|
||||
sidebar_custom_props:
|
||||
summary: React + Native Rendering
|
||||
---
|
||||
|
||||
import current from '/version.js';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
export const r = {style: {color:"red"}};
|
||||
export const g = {style: {color:"green"}};
|
||||
export const y = {style: {color:"gold"}};
|
||||
export const gr = {style: {color:"gray"}};
|
||||
|
||||
[Lynx](https://lynxjs.org/) is a modern cross-platform framework. It builds iOS,
|
||||
Android and Web apps that use JavaScript for describing layouts and events.
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
:::caution Lynx support is considered experimental.
|
||||
|
||||
Lynx is relatively new and does not currently have a deep community.
|
||||
|
||||
Any issues should be reported to the Lynx project for further diagnosis.
|
||||
|
||||
:::
|
||||
|
||||
This demo uses React (using [ReactLynx](https://lynxjs.org/react)) and SheetJS
|
||||
to process and generate spreadsheets. We'll explore how to load SheetJS in Lynx
|
||||
apps in the following scenarios:
|
||||
|
||||
- ["Fetching Remote Data"](#fetching-remote-data) uses the built-in `fetch` to download
|
||||
and parse remote workbook files.
|
||||
|
||||
The "Fetching Remote Data" example creates an app that looks like the screenshots below:
|
||||
|
||||
<table><thead><tr>
|
||||
<th><a href="#demo">iOS</a></th>
|
||||
<th><a href="#demo">Android</a></th>
|
||||
</tr></thead><tbody><tr><td>
|
||||
|
||||

|
||||
|
||||
</td><td>
|
||||
|
||||

|
||||
|
||||
</td></tr></tbody></table>
|
||||
|
||||
:::caution pass
|
||||
|
||||
**Before testing this demo, follow the official React Lynx Guide!**[^1]
|
||||
|
||||
Follow the instructions for iOS (requires macOS) and for Android. They will
|
||||
cover installation and system configuration. You should be able to build and run
|
||||
a sample app in the Android and the iOS (if applicable) simulators.
|
||||
|
||||
:::
|
||||
|
||||
:::danger pass
|
||||
|
||||
**Lynx development requires an Apple Silicon-powered Macintosh!**
|
||||
|
||||
[X64 is currently unsupported.](https://github.com/lynx-family/lynx/issues/219)
|
||||
|
||||
:::
|
||||
|
||||
## Integration Details
|
||||
|
||||
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
|
||||
imported from any component or script in the app.
|
||||
|
||||
### Internal State
|
||||
|
||||
For simplicity, this demo uses an "Array of Arrays"[^2] as the internal state.
|
||||
|
||||
|
||||
<table>
|
||||
<thead><tr><th>Spreadsheet</th><th>Array of Arrays</th></tr></thead>
|
||||
<tbody><tr><td>
|
||||
|
||||

|
||||
|
||||
</td><td>
|
||||
|
||||
```js
|
||||
[
|
||||
["Name", "Index"],
|
||||
["Bill Clinton", 42],
|
||||
["GeorgeW Bush", 43],
|
||||
["Barack Obama", 44],
|
||||
["Donald Trump", 45],
|
||||
["Joseph Biden", 46]
|
||||
]
|
||||
```
|
||||
|
||||
</td></tr></tbody></table>
|
||||
|
||||
Each array represents a row in the table.
|
||||
|
||||
This demo also keeps track of the column widths as a single array of numbers.
|
||||
The widths are used by the display component.
|
||||
|
||||
```ts title="State variables"
|
||||
const [data, setData] = useState<any[]>([
|
||||
"SheetJS".split(""),
|
||||
[5, 4, 3, 3, 7, 9, 5],
|
||||
[8, 6, 7, 5, 3, 0, 9]
|
||||
]);
|
||||
const [widths, setWidths] = useState<number[]>(Array.from({ length: 7 }, () => 20));
|
||||
```
|
||||
|
||||
#### Updating State
|
||||
|
||||
Starting from a SheetJS worksheet object, `sheet_to_json`[^3] with the `header`
|
||||
option can generate an array of arrays:
|
||||
|
||||
```js title="Updating state from a workbook"
|
||||
/* assuming `wb` is a SheetJS workbook */
|
||||
function update_state(wb) {
|
||||
/* convert first worksheet to AOA */
|
||||
const wsname = wb.SheetNames[0];
|
||||
const ws = wb.Sheets[wsname];
|
||||
const data = utils.sheet_to_json(ws, {header:1});
|
||||
|
||||
/* update state */
|
||||
setData(data);
|
||||
|
||||
/* update column widths */
|
||||
setWidths(make_width(data));
|
||||
}
|
||||
```
|
||||
|
||||
_Calculating Column Widths_
|
||||
|
||||
Column widths can be calculated by walking each column and calculating the max
|
||||
data width. Using the array of arrays:
|
||||
|
||||
```js title="Calculating column widths"
|
||||
/* this function takes an array of arrays and generates widths */
|
||||
function make_width(aoa) {
|
||||
/* walk each row */
|
||||
aoa.forEach((r) => {
|
||||
/* walk each column */
|
||||
r.forEach((c, C) => {
|
||||
/* update column width based on the length of the cell contents */
|
||||
res[C] = Math.max(res[C]||60, String(c).length * 10);
|
||||
});
|
||||
});
|
||||
/* use a default value for columns with no data */
|
||||
for(let C = 0; C < res.length; ++C) if(!res[C]) res[C] = 60;
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
### Displaying Data
|
||||
|
||||
Lynx does not ship with a component for displaying tabular data.
|
||||
|
||||
The demo uses Lynx `<view/>` and `<text/>` elements to display tabular data:
|
||||
|
||||
```tsx title="Example JSX for displaying data"
|
||||
{/* Table container */}
|
||||
<view className='Table'>
|
||||
{/* Map through each row in the data array */}
|
||||
{data.map((row, rowIndex) => (
|
||||
<view key={`row-${rowIndex}`} className="Row">
|
||||
{/* Map through each cell in the current row */}
|
||||
{Array.isArray(row) && row.map((cell, cellIndex) => (
|
||||
{/* Cell with dynamic width based on content */}
|
||||
<view
|
||||
key={`cell-${rowIndex}-${cellIndex}`} className="Cell"
|
||||
style={{ width: `${widths[cellIndex]}px` }}>
|
||||
{/* Display cell content as text */}
|
||||
<text>{String(cell)}</text>
|
||||
</view>
|
||||
))}
|
||||
</view>
|
||||
))}
|
||||
</view>
|
||||
```
|
||||
|
||||
## Fetching Remote Data
|
||||
|
||||
This snippet downloads and parses https://docs.sheetjs.com/pres.xlsx:
|
||||
|
||||
```js
|
||||
/* fetch data into an ArrayBuffer */
|
||||
const ab = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
/* parse data */
|
||||
const wb = XLSX.read(ab);
|
||||
```
|
||||
|
||||
### Fetch Demo
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
**Simulators**
|
||||
|
||||
| OS | Device | Lynx | LynxExplorer | Dev Platform | Date |
|
||||
|:-----------|:--------------------|:---------|:-------------|:-------------|:-----------|
|
||||
| Android 35 | Pixel 3a | `0.8.6` | `3.2.0-rc.1` | `darwin-arm` | 2025-03-26 |
|
||||
| iOS 18.3 | iPhone 16 Pro | `0.8.6` | `3.2.0-rc.1` | `darwin-arm` | 2025-03-26 |
|
||||
|
||||
:::
|
||||
|
||||
:::danger Real Devices
|
||||
|
||||
When this demo was last tested, there was no simple standalone guide for running
|
||||
Lynx apps on real devices.
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
First install Lynx by following the Guide![^1].
|
||||
|
||||
Make sure you can run a basic test app on your simulator before continuing!
|
||||
|
||||
:::
|
||||
|
||||
0) Install Lynx dependencies
|
||||
|
||||
1) Create project:
|
||||
|
||||
```bash
|
||||
npm create rspeedy@0.8.6 -- -d SheetJSLynxFetch -t react-ts --tools biome
|
||||
```
|
||||
|
||||
2) Install shared dependencies:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
cd SheetJSLynxFetch
|
||||
curl -o ./src/assets/SheetJS-logo.png https://docs.sheetjs.com/logo.png
|
||||
npm i
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Download [`App.tsx`](pathname:///lynx/App.tsx) into the `src` folder:
|
||||
|
||||
```bash
|
||||
curl -o ./src/App.tsx https://docs.sheetjs.com/lynx/App.tsx
|
||||
```
|
||||
|
||||
4) Download [`App.css`](pathname:///lynx/App.css) into the `src` folder:
|
||||
|
||||
```bash
|
||||
curl -o ./src/App.css https://docs.sheetjs.com/lynx/App.css
|
||||
```
|
||||
<a name="step5"></a>
|
||||
|
||||
5) Start the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Keep the window open.
|
||||
|
||||
#### Android
|
||||
|
||||
6) Start the Android emulator:
|
||||
|
||||
<Tabs>
|
||||
<TabItem name="Android Studio" value="Android Studio">
|
||||
|
||||
In Android Studio, click "More actions" > "Virtual Device Manager". Look for the
|
||||
emulated device in the list and click the ▶ button to play.
|
||||
|
||||
</TabItem>
|
||||
<TabItem name="Android Studio" value="Command Line">
|
||||
|
||||
List the available emulators with `emulator -list-avds`:
|
||||
|
||||
```
|
||||
shjs@sheetjs SheetJSLynxFetch % emulator -list-avds
|
||||
Medium_Phone_API_35
|
||||
^^^^^^^^^^^^^^^^^^^--- emulator name
|
||||
```
|
||||
|
||||
The emulator name should be passed to `emulator -avd`. In a previous test, the
|
||||
name was `Medium_Phone_API_35` and the launch command was:
|
||||
|
||||
```bash
|
||||
emulator -avd Medium_Phone_API_35
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
On macOS, `~/Library/Android/sdk/emulator/` is the typical location
|
||||
for the `emulator` binary. If it cannot be found, add the folder to `PATH`:
|
||||
|
||||
```bash
|
||||
export PATH="$PATH":~/Library/Android/sdk/emulator
|
||||
emulator -avd Medium_Phone_API_35
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
7) Download the LynxExplorer[^4] APK.
|
||||
|
||||
The latest test used [`LynxExplorer-noasan-release.apk` for version `3.2.0-rc.1`](https://github.com/lynx-family/lynx/releases/download/3.2.0-rc.1/LynxExplorer-noasan-release.apk).
|
||||
|
||||
8) Drag and drop the APK into the Android emulator window.
|
||||
|
||||
The emulator will install LynxExplorer.
|
||||
|
||||
9) In the terminal window from [step 5](#step5), copy the HTTP link. It will be
|
||||
printed below the QR code, as shown in the following screenshot:
|
||||
|
||||

|
||||
|
||||
10) In the emulator, open the "LynxExplorer" app.
|
||||
|
||||
11) In the **Enter Card URL** input field, paste the link. Tap **Go**.
|
||||
|
||||
The view will refresh. The app should look like the "Before" screenshot:
|
||||
|
||||
<table><thead><tr>
|
||||
<th>Before</th>
|
||||
<th>After</th>
|
||||
</tr></thead><tbody><tr><td>
|
||||
|
||||

|
||||
|
||||
</td><td>
|
||||
|
||||

|
||||
|
||||
</td></tr></tbody></table>
|
||||
|
||||
12) Tap "Import data from a spreadsheet" and verify that the app shows new data.
|
||||
The app should look like the "After" screenshot.
|
||||
|
||||
|
||||
**iOS Testing**
|
||||
|
||||
:::danger pass
|
||||
|
||||
**iOS testing can only be performed on Apple hardware running macOS!**
|
||||
|
||||
Xcode and iOS simulators are not available on Windows or Linux.
|
||||
|
||||
:::
|
||||
|
||||
13) Download the LynxExplorer[^4] app tarball.
|
||||
|
||||
The latest test used [`LynxExplorer-arm64.app.tar.gz` for version `3.2.0-rc.1`](https://github.com/lynx-family/lynx/releases/download/3.2.0-rc.1/LynxExplorer-arm64.app.tar.gz).
|
||||
|
||||
14) Open `LynxExplorer-arm64.app.tar.gz` using Finder.
|
||||
|
||||
The tarball contains an app named `LynxExplorer-arm64` .
|
||||
|
||||
15) Launch the iOS Simulator.
|
||||
|
||||
16) Click and drag `LynxExplorer-arm64` into the Simulator window.
|
||||
|
||||
The simulator will install the "LynxExplorer" app.
|
||||
|
||||
17) Copy the HTTP link from the terminal window in [step 5](#step5).
|
||||
|
||||

|
||||
|
||||
18) Tap the "LynxExplorer" icon in the simulator to launch the app.
|
||||
|
||||
19) Tap the **Enter Card URL** input field and paste the link. Tap **Go**.
|
||||
|
||||
The view will refresh. The app should look like the "Before" screenshot:
|
||||
|
||||
<table><thead><tr>
|
||||
<th>Before</th>
|
||||
<th>After</th>
|
||||
</tr></thead><tbody><tr><td>
|
||||
|
||||

|
||||
|
||||
</td><td>
|
||||
|
||||

|
||||
|
||||
</td></tr></tbody></table>
|
||||
|
||||
20) Tap "Import data from a spreadsheet" and verify that the app shows new data. The app should look like the "After" screenshot.
|
||||
|
||||
[^1]: Follow ["Quick Start"](https://lynxjs.org/guide/start/quick-start.html) in
|
||||
the Lynx documentation and select the appropriate "Lynx Explorer sandbox"
|
||||
[^2]: See ["Array of Arrays" in the API reference](/docs/api/utilities/array#array-of-arrays)
|
||||
[^3]: See ["Array Output" in "Utility Functions"](/docs/api/utilities/array#array-output)
|
||||
[^4]: See ["LynxExplorer sandbox"](https://github.com/lynx-family/lynx/releases/latest/)
|
@ -1,6 +1,5 @@
|
||||
---
|
||||
title: Electrified Sheets with Electron
|
||||
sidebar_label: Electron
|
||||
title: Electron
|
||||
pagination_prev: demos/mobile/index
|
||||
pagination_next: demos/cli/index
|
||||
sidebar_position: 1
|
||||
@ -11,14 +10,10 @@ sidebar_custom_props:
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
[Electron](https://www.electronjs.org/) is a modern toolkit for building desktop
|
||||
apps. Electron apps use the same technologies powering Chromium and NodeJS.
|
||||
The [NodeJS Module](/docs/getting-started/installation/nodejs) can be imported
|
||||
from the main or the renderer thread.
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
The ["Complete Example"](#complete-example) section covers a complete desktop
|
||||
app to read and write workbooks. The app will look like the screenshots below:
|
||||
The "Complete Example" creates an app that looks like the screenshots below:
|
||||
|
||||
<table><thead><tr>
|
||||
<th><a href="#complete-example">Windows</a></th>
|
||||
@ -40,22 +35,8 @@ app to read and write workbooks. The app will look like the screenshots below:
|
||||
|
||||
## Integration Details
|
||||
|
||||
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
|
||||
imported from the main or the renderer thread.
|
||||
|
||||
The SheetJS `readFile` and `writeFile` methods will use the Electron `fs` module
|
||||
where available.
|
||||
|
||||
<details>
|
||||
<summary><b>Renderer Configuration</b> (click to show)</summary>
|
||||
|
||||
Electron 9 and later require the preference `nodeIntegration: true` in order to
|
||||
`require('xlsx')` in the renderer process.
|
||||
|
||||
Electron 12 and later also require `worldSafeExecuteJavascript: true` and
|
||||
`contextIsolation: true`.
|
||||
|
||||
</details>
|
||||
Electron presents a `fs` module. The `require('xlsx')` call loads the CommonJS
|
||||
module, so `XLSX.readFile` and `XLSX.writeFile` work in the renderer thread.
|
||||
|
||||
### Reading Files
|
||||
|
||||
@ -207,12 +188,12 @@ This demo was tested in the following environments:
|
||||
|
||||
| OS and Version | Architecture | Electron | Date |
|
||||
|:---------------|:-------------|:---------|:-----------|
|
||||
| macOS 15.3 | `darwin-x64` | `35.1.2` | 2025-03-31 |
|
||||
| macOS 14.5 | `darwin-arm` | `35.1.2` | 2025-03-30 |
|
||||
| Windows 11 | `win11-x64` | `33.2.1` | 2025-02-11 |
|
||||
| Windows 11 | `win11-arm` | `33.2.1` | 2025-02-23 |
|
||||
| Linux (HoloOS) | `linux-x64` | `33.2.1` | 2025-01-02 |
|
||||
| Linux (Debian) | `linux-arm` | `33.2.1` | 2025-02-16 |
|
||||
| macOS 14.4 | `darwin-x64` | `29.1.4` | 2024-03-15 |
|
||||
| macOS 14.5 | `darwin-arm` | `30.0.8` | 2024-05-28 |
|
||||
| Windows 10 | `win10-x64` | `31.2.0` | 2024-07-12 |
|
||||
| Windows 11 | `win11-arm` | `30.0.8` | 2024-05-28 |
|
||||
| Linux (HoloOS) | `linux-x64` | `29.1.4` | 2024-03-21 |
|
||||
| Linux (Debian) | `linux-arm` | `30.0.8` | 2024-05-28 |
|
||||
|
||||
:::
|
||||
|
||||
@ -250,26 +231,6 @@ curl -LO https://docs.sheetjs.com/electron/index.html
|
||||
curl -LO https://docs.sheetjs.com/electron/index.js
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
In PowerShell, the command may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'LO'.
|
||||
```
|
||||
|
||||
`curl.exe` must be invoked directly:
|
||||
|
||||
```bash
|
||||
curl.exe -LO https://docs.sheetjs.com/electron/package.json
|
||||
curl.exe -LO https://docs.sheetjs.com/electron/main.js
|
||||
curl.exe -LO https://docs.sheetjs.com/electron/index.html
|
||||
curl.exe -LO https://docs.sheetjs.com/electron/index.js
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
|
||||
2) Install dependencies:
|
||||
|
||||
```bash
|
||||
@ -315,7 +276,7 @@ The program will run on ARM64 Windows.
|
||||
|:-------------|:--------------------------------------------------------------|
|
||||
| `darwin-x64` |`open ./out/sheetjs-electron-darwin-x64/sheetjs-electron.app` |
|
||||
| `darwin-arm` |`open ./out/sheetjs-electron-darwin-arm64/sheetjs-electron.app`|
|
||||
| `win11-x64` |`.\out\sheetjs-electron-win32-x64\sheetjs-electron.exe` |
|
||||
| `win10-x64` |`.\out\sheetjs-electron-win32-x64\sheetjs-electron.exe` |
|
||||
| `win11-arm` |`.\out\sheetjs-electron-win32-x64\sheetjs-electron.exe` |
|
||||
| `linux-x64` |`./out/sheetjs-electron-linux-x64/sheetjs-electron` |
|
||||
| `linux-arm` |`./out/sheetjs-electron-linux-arm64/sheetjs-electron` |
|
||||
@ -360,8 +321,8 @@ and select `pres.numbers`.
|
||||
|
||||
## Electron Breaking Changes
|
||||
|
||||
The first version of this demo used Electron `1.7.5`. The current demo includes
|
||||
the required changes for Electron `35.1.2`.
|
||||
The first version of this demo used Electron 1.7.5. The current demo includes
|
||||
the required changes for Electron 30.0.8.
|
||||
|
||||
There are no Electron-specific workarounds in the library, but Electron broke
|
||||
backwards compatibility multiple times. A summary of changes is noted below.
|
||||
@ -375,7 +336,6 @@ methods have been renamed:
|
||||
|:-----------------|:---------------------|
|
||||
| `showOpenDialog` | `showOpenDialogSync` |
|
||||
| `showSaveDialog` | `showSaveDialogSync` |
|
||||
|
||||
**This change was not properly documented!**
|
||||
|
||||
Electron 9 and later require the preference `nodeIntegration: true` in order to
|
||||
@ -389,4 +349,4 @@ Electron 14 and later must use `@electron/remote` instead of `remote`. An
|
||||
|
||||
:::
|
||||
|
||||
[^1]: See ["Makers"](https://www.electronforge.io/config/makers) in the Electron Forge documentation. On Linux, the demo generates `rpm` and `deb` distributables. On Arch Linux and the Steam Deck, `sudo pacman -Syu rpm-tools dpkg fakeroot` installed required packages. On Debian and Ubuntu, `sudo apt-get install rpm` sufficed.
|
||||
[^1]: See ["Makers"](https://www.electronforge.io/config/makers) in the Electron Forge documentation. On Linux, the demo generates `rpm` and `deb` distributables. On Arch Linux and the Steam Deck, `sudo pacman -Syu rpm-tools dpkg fakeroot` installed required packages.
|
@ -1,6 +1,5 @@
|
||||
---
|
||||
title: Sheets in NW.js
|
||||
sidebar_label: NW.js
|
||||
title: NW.js
|
||||
pagination_prev: demos/mobile/index
|
||||
pagination_next: demos/cli/index
|
||||
sidebar_position: 2
|
||||
@ -13,14 +12,10 @@ import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
[NW.js](https://nwjs.io/), formerly `node-webkit`, is a modern toolkit for
|
||||
building desktop apps using web technologies.
|
||||
The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone)
|
||||
can be referenced in a `SCRIPT` tag from the entry point HTML page.
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
The ["Complete Example"](#complete-example) section covers a complete desktop
|
||||
app to read and write workbooks. The app will look like the screenshots below:
|
||||
The "Complete Example" creates an app that looks like the screenshots below:
|
||||
|
||||
<table><thead><tr>
|
||||
<th><a href="#complete-example">Windows</a></th>
|
||||
@ -42,9 +37,6 @@ app to read and write workbooks. The app will look like the screenshots below:
|
||||
|
||||
## Integration Details
|
||||
|
||||
The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone)
|
||||
can be referenced in a `SCRIPT` tag from the entry point HTML page.
|
||||
|
||||
NW.js provides solutions for reading and writing files.
|
||||
|
||||
### Reading Files
|
||||
@ -121,12 +113,12 @@ This demo was tested in the following environments:
|
||||
|
||||
| OS and Version | Architecture | NW.js | Date | Notes |
|
||||
|:---------------|:-------------|:---------|:-----------|:---------------------|
|
||||
| macOS 15.3.2 | `darwin-x64` | `0.94.0` | 2025-03-31 | |
|
||||
| macOS 14.5 | `darwin-arm` | `0.94.0` | 2025-03-30 | |
|
||||
| Windows 11 | `win11-x64` | `0.94.0` | 2024-12-19 | |
|
||||
| Windows 11 | `win11-arm` | `0.94.0` | 2025-02-23 | |
|
||||
| Linux (HoloOS) | `linux-x64` | `0.89.0` | 2025-01-10 | |
|
||||
| Linux (Debian) | `linux-arm` | `0.60.0` | 2025-02-16 | Unofficial build[^1] |
|
||||
| macOS 14.3.1 | `darwin-x64` | `0.85.0` | 2024-03-12 | |
|
||||
| macOS 14.5 | `darwin-arm` | `0.88.0` | 2024-05-28 | |
|
||||
| Windows 10 | `win10-x64` | `0.83.0` | 2024-03-04 | |
|
||||
| Windows 11 | `win11-arm` | `0.88.0` | 2024-05-28 | |
|
||||
| Linux (HoloOS) | `linux-x64` | `0.89.0` | 2024-07-07 | |
|
||||
| Linux (Debian) | `linux-arm` | `0.60.0` | 2024-05-23 | Unofficial build[^1] |
|
||||
|
||||
:::
|
||||
|
||||
@ -146,7 +138,7 @@ cd sheetjs-nwjs
|
||||
"version": "0.0.0",
|
||||
"main": "index.html",
|
||||
"dependencies": {
|
||||
"nw": "0.94.0",
|
||||
"nw": "0.89.0",
|
||||
"xlsx": "https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz"
|
||||
}
|
||||
}`}
|
||||
@ -187,47 +179,18 @@ table will refresh with the contents of the selected file.
|
||||
Click "Export Data!" and save the generated file to `SheetJSNWDemo.xlsx`. This
|
||||
file can be opened in Excel or another spreadsheet editor.
|
||||
|
||||
<details>
|
||||
<summary><b>Linux ARM64 support</b> (click to show)</summary>
|
||||
|
||||
NW.js does not officially support `linux-arm`. The official recommendation is to
|
||||
use a third-party pre-built version.
|
||||
|
||||
```bash
|
||||
curl -LO https://github.com/LeonardLaszlo/nw.js-armv7-binaries/releases/download/nw60-arm64_2022-01-08/nw60-arm64_2022-01-08.tar.gz
|
||||
tar -xzf nw60-arm64_2022-01-08.tar.gz
|
||||
cp usr/docker/dist/nwjs-chromium-ffmpeg-branding/nwjs-v0.60.1-linux-arm64.tar.gz
|
||||
tar -xzf nwjs-v0.60.1-linux-arm64.tar.gz
|
||||
./nwjs-v0.60.1-linux-arm64/nw .
|
||||
```
|
||||
|
||||
Unfortunately `nw-builder` will not be able to build a standalone program.
|
||||
|
||||
</details>
|
||||
|
||||
5) To build a standalone app, run the builder:
|
||||
|
||||
```bash
|
||||
npx -p nw-builder@4.11.6 nwbuild --mode=build --version=0.94.0 --glob=false --outDir=../out ./
|
||||
npx -p nw-builder nwbuild --mode=build --version=0.89.0 --glob=false --outDir=../out ./
|
||||
```
|
||||
|
||||
This will generate the standalone app in the `..\out\` folder.
|
||||
|
||||
:::caution pass
|
||||
|
||||
There is a regression in `nw-builder` version `4.12.0`. In local `win11-x64`
|
||||
testing, version `4.11.6` correctly generated the standalone application.
|
||||
|
||||
:::
|
||||
|
||||
6) Launch the generated application:
|
||||
|
||||
| Architecture | Command |
|
||||
|:-------------|:--------------------------------------------------------------|
|
||||
| `darwin-x64` | `open ../out/sheetjs-nwjs.app` |
|
||||
| `darwin-arm` | `open ../out/sheetjs-nwjs.app` |
|
||||
| `win11-x64` | `..\out\sheetjs-nwjs.exe` |
|
||||
| `win11-arm` | `..\out\sheetjs-nwjs.exe` |
|
||||
| `linux-x64` | `../out/sheetjs-nwjs` |
|
||||
|
||||
[^1]: The [`nw60-arm64_2022-01-08` release](https://github.com/LeonardLaszlo/nw.js-armv7-binaries/releases/tag/nw60-arm64_2022-01-08) included an ARM64 version of `nw`.
|
@ -33,7 +33,7 @@ app to read and write workbooks. The app will look like the screenshots below:
|
||||
<th><a href="#complete-example">Linux</a></th>
|
||||
</tr></thead><tbody><tr><td>
|
||||
|
||||

|
||||

|
||||
|
||||
</td><td>
|
||||
|
||||
@ -295,14 +295,14 @@ async function exportFile(table_element) {
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| OS and Version | Architecture | Wails | Date |
|
||||
|:---------------|:-------------|:----------|:-----------|
|
||||
| macOS 15.3.2 | `darwin-x64` | `v2.10.1` | 2025-03-31 |
|
||||
| macOS 14.5 | `darwin-arm` | `v2.10.1` | 2025-03-30 |
|
||||
| Windows 11 | `win11-x64` | `v2.9.2` | 2024-12-21 |
|
||||
| Windows 11 | `win11-arm` | `v2.10` | 2025-02-23 |
|
||||
| Linux (HoloOS) | `linux-x64` | `v2.9.2` | 2025-01-02 |
|
||||
| Linux (Debian) | `linux-arm` | `v2.10` | 2025-02-16 |
|
||||
| OS and Version | Architecture | Wails | Date |
|
||||
|:---------------|:-------------|:---------|:-----------|
|
||||
| macOS 14.4 | `darwin-x64` | `v2.8.0` | 2024-03-15 |
|
||||
| macOS 14.5 | `darwin-arm` | `v2.8.2` | 2024-05-28 |
|
||||
| Windows 10 | `win10-x64` | `v2.8.0` | 2024-03-24 |
|
||||
| Windows 11 | `win11-arm` | `v2.8.2` | 2024-05-28 |
|
||||
| Linux (HoloOS) | `linux-x64` | `v2.8.0` | 2024-03-21 |
|
||||
| Linux (Debian) | `linux-arm` | `v2.8.2` | 2024-05-28 |
|
||||
|
||||
:::
|
||||
|
||||
@ -358,7 +358,7 @@ None of the optional packages are required for building and running this demo.
|
||||
On the Steam Deck (HoloOS), some dependencies must be reinstalled:
|
||||
|
||||
```bash
|
||||
sudo pacman -Syu base-devel gtk3 glib2 pango harfbuzz cairo gdk-pixbuf2 atk libsoup webkit2gtk
|
||||
sudo pacman -Syu base-devel gtk3 glib2 pango harfbuzz cairo gdk-pixbuf2 atk libsoup
|
||||
```
|
||||
|
||||
:::
|
||||
|
@ -14,7 +14,6 @@ import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
export const c = {style: {color:"cyan"}};
|
||||
export const y = {style: {color:"gold"}};
|
||||
export const g = {style: {color:"green"}};
|
||||
export const B = {style: {fontWeight:"bold"}};
|
||||
@ -38,7 +37,7 @@ app to read and write workbooks. The app will look like the screenshots below:
|
||||
<th><a href="#complete-example">Linux</a></th>
|
||||
</tr></thead><tbody><tr><td>
|
||||
|
||||

|
||||

|
||||
|
||||
</td><td>
|
||||
|
||||
@ -55,24 +54,16 @@ app to read and write workbooks. The app will look like the screenshots below:
|
||||
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
|
||||
installed and imported from JavaScript code.
|
||||
|
||||
:::info pass
|
||||
|
||||
The following explanation applies to Tauri 1.
|
||||
|
||||
The `allowlist` security model was abandoned in Tauri 2.
|
||||
|
||||
:::
|
||||
|
||||
:::note pass
|
||||
|
||||
Tauri 1.x does not provide the equivalent of NodeJS `fs` module. The raw
|
||||
Tauri currently does not provide the equivalent of NodeJS `fs` module. The raw
|
||||
`@tauri-apps/api` methods used in the examples are not expected to change.
|
||||
|
||||
:::
|
||||
|
||||
For security reasons, Tauri apps must explicitly enable system features.[^1]
|
||||
They are enabled in `src-tauri/tauri.conf.json` in the `allowlist` subsection of
|
||||
the `tauri` section of the configuration file.
|
||||
the `tauri` section of the config.
|
||||
|
||||
- The `fs` entitlement[^2] enables reading and writing file data.
|
||||
|
||||
@ -353,12 +344,12 @@ This demo was tested in the following environments:
|
||||
|
||||
| OS and Version | Architecture | Tauri | Date |
|
||||
|:---------------|:-------------|:----------|:-----------|
|
||||
| macOS 15.3.2 | `darwin-x64` | `v1.6.0` | 2025-03-31 |
|
||||
| macOS 14.5 | `darwin-arm` | `v1.6.0` | 2025-03-30 |
|
||||
| Windows 11 | `win11-x64` | `v1.6.0` | 2024-12-21 |
|
||||
| Windows 11 | `win11-arm` | `v1.6.0` | 2025-02-23 |
|
||||
| Linux (HoloOS) | `linux-x64` | `v1.6.0` | 2025-01-02 |
|
||||
| Linux (Debian) | `linux-arm` | `v1.6.0` | 2025-02-16 |
|
||||
| macOS 14.4 | `darwin-x64` | `v1.5.11` | 2024-04-20 |
|
||||
| macOS 14.5 | `darwin-arm` | `v1.5.14` | 2024-05-26 |
|
||||
| Windows 10 | `win10-x64` | `v1.5.11` | 2024-03-24 |
|
||||
| Windows 11 | `win11-arm` | `v1.5.14` | 2024-05-28 |
|
||||
| Linux (HoloOS) | `linux-x64` | `v1.5.11` | 2024-03-21 |
|
||||
| Linux (Debian) | `linux-arm` | `v1.5.14` | 2024-05-28 |
|
||||
|
||||
:::
|
||||
|
||||
@ -370,7 +361,7 @@ This demo was tested in the following environments:
|
||||
At a high level, the following software is required for building Tauri apps:
|
||||
|
||||
- a native platform-specific C/C++ compiler (for example, macOS requires Xcode)
|
||||
- a browser engine integration (for example, Linux requires `webkit2gtk`)
|
||||
- a browser engine integration (for example, linux requires `webkit2gtk`)
|
||||
- [Rust](https://www.rust-lang.org/tools/install)
|
||||
|
||||
The platform configuration can be verified by running:
|
||||
@ -384,17 +375,15 @@ If required dependencies are installed, the output will show a checkmark next to
|
||||
|
||||
<pre>
|
||||
<span {...g}>[✔]</span> <span style={{...y.style,...B.style}}>Environment</span>
|
||||
{` `}<span {...c}>-</span> <span {...B}>OS</span>: Mac OS 14.5.0 arm64 (X64)
|
||||
{` `}<span {...g}>-</span> <span {...B}>OS</span>: Mac OS 14.4.1 X64
|
||||
{` `}<span {...g}>✔</span> <span {...B}>Xcode Command Line Tools</span>: installed
|
||||
{` `}<span {...g}>✔</span> <span {...B}>rustc</span>: 1.85.1 (4eb161250 2025-03-15)
|
||||
{` `}<span {...g}>✔</span> <span {...B}>cargo</span>: 1.85.1 (d73d2caf9 2024-12-31)
|
||||
{` `}<span {...g}>✔</span> <span {...B}>rustup</span>: 1.28.1 (f9edccde0 2025-03-05)
|
||||
{` `}<span {...g}>✔</span> <span {...B}>Rust toolchain</span>: stable-aarch64-apple-darwin (default)
|
||||
{` `}<span {...c}>-</span> <span {...B}>node</span>: 20.18.0
|
||||
{` `}<span {...c}>-</span> <span {...B}>pnpm</span>: 9.12.3
|
||||
{` `}<span {...c}>-</span> <span {...B}>npm</span>: 10.8.2
|
||||
{` `}<span {...c}>-</span> <span {...B}>bun</span>: 1.2.7
|
||||
{` `}<span {...c}>-</span> <span {...B}>deno</span>: deno 2.2.6
|
||||
{` `}<span {...g}>✔</span> <span {...B}>rustc</span>: 1.77.2 (25ef9e3d8 2024-04-09)
|
||||
{` `}<span {...g}>✔</span> <span {...B}>cargo</span>: 1.77.2 (e52e36006 2024-03-26)
|
||||
{` `}<span {...g}>✔</span> <span {...B}>rustup</span>: 1.27.0 (bbb9276d2 2024-03-08)
|
||||
{` `}<span {...g}>✔</span> <span {...B}>Rust toolchain</span>: stable-x86_64-apple-darwin (default)
|
||||
{` `}<span {...g}>-</span> <span {...B}>node</span>: 20.12.1
|
||||
{` `}<span {...g}>-</span> <span {...B}>npm</span>: 10.5.0
|
||||
{` `}<span {...g}>-</span> <span {...B}>bun</span>: 1.1.4
|
||||
</pre>
|
||||
|
||||
:::caution pass
|
||||
@ -412,7 +401,7 @@ build step will correctly detect the platform architecture.
|
||||
<TabItem value="vuejs" label="VueJS">
|
||||
|
||||
```bash
|
||||
npm create tauri-app@3.x -- -m npm -t vue-ts SheetJSTauri -y
|
||||
npm create tauri-app@latest -- -m npm -t vue-ts SheetJSTauri -y
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@ -426,7 +415,7 @@ TypeScript template and manually wires Kaioken
|
||||
:::
|
||||
|
||||
```bash
|
||||
npm create tauri-app@3.x -- -m npm -t vanilla-ts SheetJSTauri -y
|
||||
npm create tauri-app@latest -- -m npm -t vanilla-ts SheetJSTauri -y
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@ -481,7 +470,6 @@ npm add vite-plugin-kaioken -D --save
|
||||
In the same file, look for `"title"` and change the value to `SheetJS x Tauri`:
|
||||
|
||||
```json title="src-tauri/tauri.conf.json (edit highlighted line)"
|
||||
"windows": [
|
||||
{
|
||||
// highlight-next-line
|
||||
"title": "SheetJS x Tauri",
|
||||
@ -537,7 +525,7 @@ export default defineConfig(async () => ({
|
||||
|
||||
- Replace `index.html` with the following codeblock:
|
||||
|
||||
```html title="index.html (replace contents)"
|
||||
```html title="index.html"
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@ -574,7 +562,7 @@ table.center {
|
||||
|
||||
- Replace `src/main.ts` with the following codeblock:
|
||||
|
||||
```ts title="src/main.ts (replace contents)"
|
||||
```ts title="src/main.ts"
|
||||
import { mount } from "kaioken";
|
||||
import App from "./App";
|
||||
|
||||
@ -613,7 +601,7 @@ If the build fails, see ["Troubleshooting"](#troubleshooting) for more details.
|
||||
Depending on the version of Tauri, the command may be
|
||||
|
||||
```bash
|
||||
./src-tauri/target/release/sheetjstauri
|
||||
./src-tauri/target/release/SheetJSTauri
|
||||
```
|
||||
|
||||
or
|
||||
@ -625,7 +613,7 @@ or
|
||||
or
|
||||
|
||||
```bash
|
||||
./src-tauri/target/release/SheetJSTauri
|
||||
./src-tauri/target/release/sheetjstauri
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@ -651,22 +639,6 @@ The following features should be manually verified:
|
||||
|
||||
:::note pass
|
||||
|
||||
In some tests, the build failed with the error message:
|
||||
|
||||
```
|
||||
Search string not found: "/supportedTSExtensions = .*(?=;)/"
|
||||
```
|
||||
|
||||
This is a known issue with `vue-tsc`. The dependency must be upgraded:
|
||||
|
||||
```bash
|
||||
npm install vue-tsc@latest
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::note pass
|
||||
|
||||
During the last Linux ARM64 test, the build failed to create an AppImage:
|
||||
|
||||
```
|
||||
|
@ -33,7 +33,7 @@ app to read and write workbooks. The app will look like the screenshots below:
|
||||
<th><a href="#complete-example">Linux</a></th>
|
||||
</tr></thead><tbody><tr><td>
|
||||
|
||||

|
||||

|
||||
|
||||
</td><td>
|
||||
|
||||
@ -192,15 +192,15 @@ This demo was tested in the following environments:
|
||||
|
||||
| OS and Version | Architecture | Server | Client | Date |
|
||||
|:---------------|:-------------|:---------|:---------|:-----------|
|
||||
| macOS 15.3.2 | `darwin-x64` | `6.0.0` | `6.0.0` | 2025-03-31 |
|
||||
| macOS 14.5 | `darwin-arm` | `6.0.0` | `6.0.0` | 2025-03-30 |
|
||||
| Windows 11 | `win11-x64` | `5.5.0` | `5.5.0` | 2024-12-20 |
|
||||
| Windows 11 | `win11-arm` | `5.6.0` | `5.6.0` | 2025-02-23 |
|
||||
| Linux (HoloOS) | `linux-x64` | `5.5.0` | `5.5.0` | 2025-01-02 |
|
||||
| Linux (Debian) | `linux-arm` | `5.6.0` | `5.6.0` | 2025-02-16 |
|
||||
| macOS 14.4 | `darwin-x64` | `5.0.0` | `5.0.1` | 2024-03-15 |
|
||||
| macOS 14.5 | `darwin-arm` | `5.1.0` | `5.1.0` | 2024-05-25 |
|
||||
| Windows 10 | `win10-x64` | `5.1.0` | `5.1.0` | 2024-03-24 |
|
||||
| Windows 11 | `win11-arm` | `5.1.0` | `5.1.1` | 2024-05-28 |
|
||||
| Linux (HoloOS) | `linux-x64` | `5.0.0` | `5.0.1` | 2024-03-21 |
|
||||
| Linux (Debian) | `linux-arm` | `5.1.0` | `5.1.1` | 2024-05-28 |
|
||||
|
||||
NeutralinoJS on Windows on ARM generates X64 binaries that run using the X64
|
||||
compatibility layer. The binaries are not native ARM64 programs!
|
||||
On `win11-arm`, the Electron runner is a proper ARM64 binary but the binaries
|
||||
generated by Electron Forge are x64. The x64 binaries run in Windows on ARM.
|
||||
|
||||
:::
|
||||
|
||||
@ -211,7 +211,7 @@ the window. Writing files will parse the table into a spreadsheet.
|
||||
<summary><b>Installation Notes</b> (click to show)</summary>
|
||||
|
||||
NeutralinoJS uses `portable-file-dialogs`[^12] to show open and save dialogs. On
|
||||
Linux, a dialog box helper (Zenity or KDialog) must be installed.
|
||||
Linux, Zenity or KDialog are require.
|
||||
|
||||
The last Debian test was run on a system using LXDE. KDialog is supported but
|
||||
must be explicitly installed:
|
||||
@ -413,12 +413,9 @@ Platform-specific programs will be created in the `dist` folder:
|
||||
|
||||
| Platform | Path to binary |
|
||||
|:-------------|:---------------------------------------------|
|
||||
| `darwin-x64` | `./dist/sheetjs-neu/sheetjs-neu-mac_x64` |
|
||||
| `darwin-arm` | `./dist/sheetjs-neu/sheetjs-neu-mac_arm64` |
|
||||
| `win11-x64` | `.\dist\sheetjs-neu\sheetjs-neu-win_x64.exe` |
|
||||
| `win11-arm` | `.\dist\sheetjs-neu\sheetjs-neu-win_x64.exe` |
|
||||
| `linux-x64` | `./dist/sheetjs-neu/sheetjs-neu-linux_x64` |
|
||||
| `linux-arm` | `./dist/sheetjs-neu/sheetjs-neu-linux_arm64` |
|
||||
| `linux-arm` | `.\dist\sheetjs-neu\sheetjs-neu-linux_arm64` |
|
||||
|
||||
Run the generated app and confirm that Presidential data is displayed.
|
||||
|
||||
|
@ -46,12 +46,11 @@ This demo was tested in the following environments:
|
||||
|
||||
| OS and Version | Architecture | RN Platform | Date |
|
||||
|:---------------|:-------------|:------------|:-----------|
|
||||
| Windows 11 C++ | `win11-x64` | `v0.75.11` | 2024-12-22 |
|
||||
| Windows 11 C# | `win11-x64` | `v0.75.11` | 2024-12-22 |
|
||||
| Windows 11 C++ | `win11-arm` | `v0.77.2` | 2025-02-23 |
|
||||
| Windows 11 C# | `win11-arm` | `v0.77.2` | 2025-02-23 |
|
||||
| MacOS 15.3.2 | `darwin-x64` | `v0.76.7` | 2025-03-31 |
|
||||
| MacOS 14.5 | `darwin-arm` | `v0.75.16` | 2024-12-22 |
|
||||
| Windows 10 | `win10-x64` | `v0.74.6` | 2024-05-28 |
|
||||
| Windows 11 | `win11-x64` | `v0.74.6` | 2024-05-28 |
|
||||
| Windows 11 | `win11-arm` | `v0.74.5` | 2024-05-25 |
|
||||
| MacOS 14.4 | `darwin-x64` | `v0.73.22` | 2024-03-24 |
|
||||
| MacOS 14.5 | `darwin-arm` | `v0.73.30` | 2024-05-28 |
|
||||
|
||||
:::
|
||||
|
||||
@ -421,66 +420,36 @@ iex (New-Object System.Net.WebClient).DownloadString('https://aka.ms/rnw-vs2022-
|
||||
If any step fails to install, open the dependencies page and expand "Manual
|
||||
setup instructions" to find instructions for manual installation.
|
||||
|
||||
:::note pass
|
||||
|
||||
Even though React Native for Windows recommends enabling "Developer Mode", it is
|
||||
not a requirement for this demo.
|
||||
|
||||
:::
|
||||
|
||||
</details>
|
||||
|
||||
### Project Setup
|
||||
|
||||
1) Create a new project using React Native `0.77.1`:
|
||||
1) Create a new project using React Native `0.74.1`:
|
||||
|
||||
```bash
|
||||
npx -y @react-native-community/cli@16 init SheetJSWin --version="0.77.1"
|
||||
npx react-native init SheetJSWin --template react-native@0.74.1
|
||||
cd SheetJSWin
|
||||
npm install --save react-native-windows@0.77.2
|
||||
```
|
||||
|
||||
:::info pass
|
||||
|
||||
Older versions of this demo used the `react-native` package. The `init` command
|
||||
was officially deprecated.
|
||||
|
||||
React Native now recommends using `@react-native-community/cli`. The versioning
|
||||
scheme is fundamentally different from `react-native`.[^5]
|
||||
|
||||
:::
|
||||
|
||||
2) Create the Windows part of the application:
|
||||
|
||||
<Tabs groupId="rnwlang">
|
||||
<TabItem value="cs" label="C#">
|
||||
|
||||
```bash
|
||||
npx react-native init-windows --no-telemetry --overwrite --template old/uwp-cs-app
|
||||
npx react-native-windows-init --no-telemetry --overwrite --language=cs
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="cpp" label="C++">
|
||||
|
||||
```bash
|
||||
npx react-native init-windows --no-telemetry --overwrite --template old/uwp-cpp-app
|
||||
npx react-native-windows-init --no-telemetry --overwrite
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
:::note pass
|
||||
|
||||
Older versions of this demo used the `react-native-windows-init` package. This
|
||||
package does not support React Native `0.77`. The official instructions now
|
||||
recommend directly installing `react-native-windows`.
|
||||
|
||||
When this demo was last tested, there was no template for a C# application using
|
||||
the React Native "New Architecture". For consistency, both the C# and C++ demos
|
||||
are tested using the "Old Architecture".
|
||||
|
||||
:::
|
||||
|
||||
3) Install the SheetJS library:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
@ -629,11 +598,7 @@ curl -LO https://docs.sheetjs.com/reactnative/rnw/App.tsx
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
:::note pass
|
||||
|
||||
The `rm` command may fail if `App.js` does not exist. That error can be ignored.
|
||||
|
||||
:::
|
||||
|
||||
8) Launch the app again:
|
||||
|
||||
@ -675,35 +640,35 @@ select `pres.xlsx` . The app will refresh and display the data from the file.
|
||||
|
||||
## macOS Demo
|
||||
|
||||
0) Follow the "Setting up the development environment"[^6] guide in the React
|
||||
:::info pass
|
||||
|
||||
When the demo was last tested, the official website asserted that the React
|
||||
Native for macOS required React Native `0.71`.
|
||||
|
||||
**The official documentation is out of date.**
|
||||
|
||||
There exist official `react-native-macos` releases compatible with RN `0.73`
|
||||
|
||||
:::
|
||||
|
||||
0) Follow the "Setting up the development environment"[^5] guide in the React
|
||||
Native documentation for "React Native CLI Quickstart" + "macOS" + "iOS".
|
||||
|
||||
### Project Setup
|
||||
|
||||
1) Create a new React Native project using React Native `0.76.8`:
|
||||
1) Create a new React Native project using React Native `0.73`:
|
||||
|
||||
```bash
|
||||
npx -y @react-native-community/cli init SheetJSmacOS --version 0.76.8
|
||||
npx -y react-native init SheetJSmacOS --template react-native@^0.73.0
|
||||
cd SheetJSmacOS
|
||||
```
|
||||
|
||||
If prompted to install CocoaPods, type `Y`.
|
||||
|
||||
:::info pass
|
||||
|
||||
Older versions of this demo used the `react-native` package. The `init` command
|
||||
was officially deprecated.
|
||||
|
||||
React Native now recommends using `@react-native-community/cli`. The versioning
|
||||
scheme is fundamentally different from `react-native`.[^5]
|
||||
|
||||
:::
|
||||
|
||||
2) Create the MacOS part of the application:
|
||||
|
||||
```bash
|
||||
npx -y react-native-macos-init --no-telemetry
|
||||
cd macos; pod install; cd ..
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
@ -745,9 +710,9 @@ npx react-native run-macos
|
||||
|
||||
Close the running app from the dock and close the Metro terminal window.
|
||||
|
||||
:::caution pass
|
||||
:::danger pass
|
||||
|
||||
In some test runs, the app failed with a warning
|
||||
When the demo was last tested, the app failed with a warning
|
||||
|
||||
> No bundle URL present.
|
||||
|
||||
@ -817,6 +782,7 @@ A) Copy the highlighted line and paste under `/* Begin PBXBuildFile section */`:
|
||||
/* Begin PBXBuildFile section */
|
||||
// highlight-next-line
|
||||
4717DC6A28CC499A00A9BE56 /* RCTDocumentPicker.m in Sources */ = {isa = PBXBuildFile; fileRef = 4717DC6928CC499A00A9BE56 /* RCTDocumentPicker.m */; };
|
||||
5142014D2437B4B30078DB4F /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5142014C2437B4B30078DB4F /* AppDelegate.mm */; };
|
||||
```
|
||||
|
||||
:::
|
||||
@ -924,6 +890,7 @@ highlighted lines:
|
||||
4717DC6928CC499A00A9BE56 /* RCTDocumentPicker.m */,
|
||||
// highlight-end
|
||||
5142014A2437B4B30078DB4F /* SheetJSmacOS-macOS */,
|
||||
13B07FAE1A68108700A75B9A /* SheetJSmacOS-iOS */,
|
||||
```
|
||||
|
||||
:::
|
||||
@ -966,7 +933,7 @@ In some test runs, the development mode app failed with a "bundle URL" error:
|
||||
|
||||
> No bundle URL present.
|
||||
|
||||
The ["Production"](#production) section covers release builds and tests.
|
||||
The ["Production" section](#production) section covers release builds and tests.
|
||||
|
||||
:::
|
||||
|
||||
@ -1011,39 +978,6 @@ If there are no instances, the app path can be found in the `DerivedData` folder
|
||||
find ~/Library/Developer/Xcode/DerivedData -name SheetJSmacOS.app | grep Release
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
During the last test, `xcodebuild` failed. Scrolling through the log reveals:
|
||||
|
||||
```
|
||||
Welcome to Metro v0.80.12
|
||||
Fast - Scalable - Integrated
|
||||
|
||||
|
||||
node:events:497
|
||||
throw er; // Unhandled 'error' event
|
||||
^
|
||||
|
||||
Error: EMFILE: too many open files, watch
|
||||
at FSWatcher._handle.onchange (node:internal/fs/watchers:207:21)
|
||||
```
|
||||
|
||||
The file descriptor limits must be increased[^7].
|
||||
|
||||
```bash
|
||||
sudo sysctl -w kern.maxfiles=10485760
|
||||
sudo sysctl -w kern.maxfilesperproc=1048576
|
||||
```
|
||||
|
||||
After making this change, forcefully reset `watchman`:
|
||||
|
||||
```bash
|
||||
watchman shutdown-server
|
||||
watchman watch-del-all
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
15) Run the release app:
|
||||
|
||||
```bash
|
||||
@ -1059,6 +993,4 @@ The app will refresh and display the data from the file.
|
||||
[^2]: See ["Array of Arrays" in the API reference](/docs/api/utilities/array#array-of-arrays)
|
||||
[^3]: See ["Array Output" in "Utility Functions"](/docs/api/utilities/array#array-output)
|
||||
[^4]: See ["Turbo Native Modules"](https://reactnative.dev/docs/the-new-architecture/pillars-turbomodules) in the React Native documentation.
|
||||
[^5]: See [the compatibility table](https://github.com/react-native-community/cli) in the CLI project repository to determine which version of `@react-native-community/cli` is required for a given `react-native` version.
|
||||
[^6]: See ["Setting up the development environment"](https://reactnative.dev/docs/environment-setup) in the React Native documentation. Select the "React Native CLI Quickstart" tab and choose the Development OS "macOS" and the Target OS "iOS".
|
||||
[^7]: See ["macOS File Descriptor Limits"](https://facebook.github.io/watchman/docs/install.html#mac-os-file-descriptor-limits) in the `watchman` docs for more details
|
||||
[^5]: See ["Setting up the development environment"](https://reactnative.dev/docs/environment-setup) in the React Native documentation. Select the "React Native CLI Quickstart" tab and choose the Development OS "macOS" and the Target OS "iOS".
|
@ -35,14 +35,14 @@ The latest prebuilt package matches NodeJS version `14.15.3`.
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Architecture | Version | NodeJS | Source | Date |
|
||||
|:-------------|:---------------|:----------|:----------|:-----------|
|
||||
| `darwin-x64` | `5.0.0-beta.4` | `14.15.3` | Pre-built | 2025-04-21 |
|
||||
| `darwin-arm` | `4.0.0-rc.6` | `22.14.0` | Compiled | 2025-04-03 |
|
||||
| `win11-x64` | `4.0.0-rc.6` | `14.15.3` | Pre-built | 2024-12-19 |
|
||||
| `win11-arm` | `4.0.0-rc.6` | `22.14.0` | Compiled | 2025-02-23 |
|
||||
| `linux-x64` | `5.0.0-beta.4` | `14.15.3` | Pre-built | 2025-04-21 |
|
||||
| `linux-arm` | `4.0.0-rc.6` | `22.13.0` | Compiled | 2025-02-15 |
|
||||
| Architecture | Version | NodeJS | Source | Date |
|
||||
|:-------------|:-------------|:----------|:----------|:-----------|
|
||||
| `darwin-x64` | `4.0.0-rc.6` | `14.15.3` | Pre-built | 2024-05-28 |
|
||||
| `darwin-arm` | `4.0.0-rc.6` | `18.20.3` | Compiled | 2024-05-25 |
|
||||
| `win10-x64` | `4.0.0-rc.4` | `14.15.3` | Pre-built | 2024-04-18 |
|
||||
| `win11-arm` | `4.0.0-rc.6` | `20.10.0` | Compiled | 2024-05-28 |
|
||||
| `linux-x64` | `4.0.0-rc.4` | `14.15.3` | Pre-built | 2024-03-21 |
|
||||
| `linux-arm` | `4.0.0-rc.6` | `18.20.3` | Compiled | 2024-05-26 |
|
||||
|
||||
:::
|
||||
|
||||
@ -96,7 +96,7 @@ yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz exit-on-epi
|
||||
3) Create the standalone program:
|
||||
|
||||
```bash
|
||||
npx -y nexe -t 14.15.3 xlsx-cli.js
|
||||
npx nexe -t 14.15.3 xlsx-cli.js
|
||||
```
|
||||
|
||||
<details open>
|
||||
@ -109,7 +109,7 @@ missing. For unsupported NodeJS versions, packages must be built from source:
|
||||
<TabItem value="unix" label="Linux/MacOS">
|
||||
|
||||
```bash
|
||||
npx -y nexe xlsx-cli.js --build --python=$(which python3) --make="-j8"
|
||||
npx nexe xlsx-cli.js --build --python=$(which python3) --make="-j8"
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@ -118,14 +118,13 @@ npx -y nexe xlsx-cli.js --build --python=$(which python3) --make="-j8"
|
||||
On Windows x64, the `--build` flag suffices:
|
||||
|
||||
```bash
|
||||
npx -y nexe xlsx-cli.js --build --make="-j8"
|
||||
npx nexe xlsx-cli.js --build --make="-j8"
|
||||
```
|
||||
|
||||
On Windows ARM, the target `windows-arm64-22.14.0` must be specified and the
|
||||
command must be run in a "Native Tools Command Prompt":
|
||||
On Windows ARM, the target `windows-arm64-20.10.0` must be specified:
|
||||
|
||||
```bash
|
||||
npx -y nexe xlsx-cli.js --build --make="-j8" --target=windows-arm64-22.14.0
|
||||
npx nexe xlsx-cli.js --build --make="-j8" --target=windows-arm64-20.10.0
|
||||
```
|
||||
|
||||
**`vcbuild.bat` issues**
|
||||
@ -136,36 +135,8 @@ The Windows ARM build may fail with a `vcbuild.bat` error:
|
||||
Error: vcbuild.bat nosign release arm64 exited with code: 1
|
||||
```
|
||||
|
||||
Pass the `--verbose` flag for more details:
|
||||
|
||||
```bash
|
||||
npx -y nexe xlsx-cli.js --build --make="-j8" --target=windows-arm64-22.14.0 --verbose
|
||||
```
|
||||
|
||||
Common error messages:
|
||||
|
||||
_"Python was not found"_
|
||||
|
||||
```
|
||||
Python was not found; run without arguments to install from the Microsoft Store, or disable this shortcut from Settings > Manage App Execution Aliases.
|
||||
```
|
||||
|
||||
Out of the box, Windows 11 will alias `python`, redirecting unsuspecting users
|
||||
to the App Installer. This redirect must be disabled:
|
||||
|
||||
Type `alias` in the search bar and select "Manage app execution aliases", In the
|
||||
settings pane, scroll down and turn off the alias for `python.exe`.
|
||||
|
||||
_"Could not find Python"_
|
||||
|
||||
```
|
||||
Could not find Python.
|
||||
```
|
||||
|
||||
After installing Python using the official installer, the command should be run
|
||||
in a new terminal window.
|
||||
|
||||
_Python version mismatch ("Please use")_
|
||||
Pass the `-v` flag for more details. In the most recent test, the error stemmed
|
||||
from a Python version mismatch:
|
||||
|
||||
```
|
||||
Node.js configure: found Python 2.7.18
|
||||
@ -178,8 +149,8 @@ The resolved version of Python can be found with
|
||||
where python
|
||||
```
|
||||
|
||||
In some tests, a Python 2 version appeared first. This was fixed by finding the
|
||||
Python 3 location and prepending it to `PATH`:
|
||||
In the most recent test, a Python 2 version appeared first. This was fixed by
|
||||
finding the Python 3 location and prepending it to `PATH`:
|
||||
|
||||
```cmd
|
||||
set PATH="C:\correct\path\to\python\three";%PATH%
|
||||
@ -211,16 +182,6 @@ This generates `xlsx-cli` or `xlsx-cli.exe` depending on platform.
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
The program should display CSV contents from the first sheet:
|
||||
|
||||
```text title="Expected Output"
|
||||
Name,Index
|
||||
Bill Clinton,42
|
||||
GeorgeW Bush,43
|
||||
Barack Obama,44
|
||||
Donald Trump,45
|
||||
Joseph Biden,46
|
||||
```
|
||||
|
||||
[^1]: The project does not have a website. The [source repository](https://github.com/nexe/nexe) is publicly available.
|
||||
[^2]: The NodeJS website hosts [prebuilt installers](https://nodejs.org/en/download/prebuilt-installer).
|
@ -26,7 +26,7 @@ spreadsheets and converting to other formats.
|
||||
:::caution pass
|
||||
|
||||
With the official release of [NodeJS SEA](/docs/demos/cli/nodesea), Vercel opted
|
||||
to deprecate `pkg`. It is still useful for deploying apps with NodeJS version 18
|
||||
to deprecate `pkg`. It is still useful for deploying apps embedding NodeJS v18
|
||||
or earlier since those versions do not support NodeJS SEA.
|
||||
|
||||
:::
|
||||
@ -37,19 +37,12 @@ This demo was tested in the following deployments:
|
||||
|
||||
| Architecture | Version | NodeJS | Date |
|
||||
|:-------------|:--------|:---------|:-----------|
|
||||
| `darwin-x64` | `5.8.1` | `18.5.0` | 2025-04-21 |
|
||||
| `darwin-arm` | `5.8.1` | `18.5.0` | 2025-02-13 |
|
||||
| `win11-x64` | `5.8.1` | `18.5.0` | 2024-12-19 |
|
||||
| `win11-arm` | `5.8.1` | `18.5.0` | 2025-02-23 |
|
||||
| `linux-x64` | `5.8.1` | `18.5.0` | 2025-04-21 |
|
||||
| `linux-arm` | `5.8.1` | `18.5.0` | 2025-02-15 |
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
`pkg` on Windows on ARM uses the X64 compatibility layer. It does not generate
|
||||
a native ARM64 binary!
|
||||
| `darwin-x64` | `5.8.1` | `18.5.0` | 2024-05-28 |
|
||||
| `darwin-arm` | `5.8.1` | `18.5.0` | 2024-05-25 |
|
||||
| `win10-x64` | `5.8.1` | `18.5.0` | 2024-04-18 |
|
||||
| `win11-arm` | `5.8.1` | `18.5.0` | 2024-05-28 |
|
||||
| `linux-x64` | `5.8.1` | `18.5.0` | 2024-03-21 |
|
||||
| `linux-arm` | `5.8.1` | `18.5.0` | 2024-05-26 |
|
||||
|
||||
:::
|
||||
|
||||
@ -140,30 +133,11 @@ yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz exit-on-epi
|
||||
4) Create the standalone program:
|
||||
|
||||
```bash
|
||||
npx -y pkg xlsx-cli.js
|
||||
npx pkg xlsx-cli.js
|
||||
```
|
||||
|
||||
This generates `xlsx-cli-linux`, `xlsx-cli-macos`, and `xlsx-cli-win.exe` .
|
||||
|
||||
:::info pass
|
||||
|
||||
If it is not possible to switch NodeJS versions, the `-t` flag can be used to
|
||||
override the target detection:
|
||||
|
||||
**X64**
|
||||
|
||||
```bash
|
||||
npx -y pkg -t 'node18-win-x64,node18-linux-x64,node18-macos-x64' xlsx-cli.js
|
||||
```
|
||||
|
||||
**ARM64**
|
||||
|
||||
```bash
|
||||
npx -y pkg -t 'node18-win-arm64,node18-linux-arm64,node18-macos-arm64' xlsx-cli.js
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
5) Run the generated program, passing `pres.numbers` as the argument:
|
||||
|
||||
<Tabs groupId="os">
|
||||
|
@ -30,11 +30,11 @@ This demo was tested in the following deployments:
|
||||
|
||||
| Architecture | Version | NodeJS | Date |
|
||||
|:-------------|:--------|:----------|:-----------|
|
||||
| `darwin-x64` | `2.4.4` | `23.11.0` | 2025-04-21 |
|
||||
| `darwin-arm` | `2.4.4` | `22.14.0` | 2025-04-03 |
|
||||
| `win11-x64` | `2.4.4` | `16.20.2` | 2024-12-19 |
|
||||
| `linux-x64` | `2.4.4` | `23.11.0` | 2025-04-21 |
|
||||
| `linux-arm` | `2.4.4` | `23.8.0` | 2025-02-15 |
|
||||
| `darwin-x64` | `2.4.0` | `22.2.0` | 2024-05-28 |
|
||||
| `darwin-arm` | `2.4.3` | `22.2.0` | 2024-05-25 |
|
||||
| `win10-x64` | `2.4.2` | `16.20.2` | 2024-04-18 |
|
||||
| `linux-x64` | `2.4.0` | `21.7.1` | 2024-03-21 |
|
||||
| `linux-arm` | `2.4.3` | `20.13.1` | 2024-05-26 |
|
||||
|
||||
:::
|
||||
|
||||
@ -91,12 +91,12 @@ yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz exit-on-epi
|
||||
<TabItem value="unix" label="Linux/MacOS">
|
||||
|
||||
```bash
|
||||
npx -y boxednode@2.4.4 -s xlsx-cli.js -t xlsx-cli
|
||||
npx boxednode@2.4.3 -s xlsx-cli.js -t xlsx-cli
|
||||
```
|
||||
|
||||
:::note pass
|
||||
:::caution pass
|
||||
|
||||
In previous `linux-arm` test runs, the build failed with an error:
|
||||
When this demo was last tested in `linux-arm`, the build failed with an error:
|
||||
|
||||
<pre>
|
||||
../deps/v8/src/base/small-vector.h: In instantiation of <span {...B}>‘class v8::base::SmallVector<std::pair<const v8::internal::compiler::turboshaft::PhiOp*, const v8::internal::compiler::turboshaft::OpIndex>, 16>’</span>:
|
||||
@ -106,14 +106,14 @@ In previous `linux-arm` test runs, the build failed with an error:
|
||||
{" |"} ^~~~~
|
||||
</pre>
|
||||
|
||||
This affects NodeJS `22.2.0`, but does not affect `20.13.1` or `23.8.0`. It also
|
||||
affects the [V8 JavaScript Engine](/docs/demos/engines/v8#build-v8).
|
||||
This affects NodeJS `22.2.0`, but does not affect `20.13.1`. It affects the
|
||||
[V8 JavaScript Engine](/docs/demos/engines/v8#build-v8).
|
||||
|
||||
The `-n` flag controls the target NodeJS version. For this demo, the following
|
||||
command uses NodeJS `23.8.0`:
|
||||
command uses NodeJS `20.13.1`:
|
||||
|
||||
```bash
|
||||
npx -y boxednode@2.4.4 -s xlsx-cli.js -t xlsx-cli -n 23.8.0
|
||||
npx boxednode@2.4.3 -s xlsx-cli.js -t xlsx-cli -n 20.13.1
|
||||
```
|
||||
|
||||
:::
|
||||
@ -122,12 +122,12 @@ npx -y boxednode@2.4.4 -s xlsx-cli.js -t xlsx-cli -n 23.8.0
|
||||
<TabItem value="win" label="Windows">
|
||||
|
||||
```bash
|
||||
npx -y boxednode@2.4.4 -s xlsx-cli.js -t xlsx-cli.exe -n 16.20.2
|
||||
npx boxednode@2.4.3 -s xlsx-cli.js -t xlsx-cli.exe -n 16.20.2
|
||||
```
|
||||
|
||||
:::info pass
|
||||
|
||||
The Windows builds require Visual Studio with "Desktop development with C++"
|
||||
The Windows 10 build requires Visual Studio with "Desktop development with C++"
|
||||
workload, Python 3.11, and NASM[^2].
|
||||
|
||||
**The build command must be run in "x64 Native Tools Command Prompt"**
|
||||
|
@ -158,12 +158,13 @@ This demo was tested in the following deployments:
|
||||
|
||||
| Architecture | NodeJS | Date |
|
||||
|:-------------|:----------|:-----------|
|
||||
| `darwin-x64` | `22.14.0` | 2025-04-21 |
|
||||
| `darwin-arm` | `23.8.0` | 2025-02-16 |
|
||||
| `win11-x64` | `22.13.0` | 2025-01-19 |
|
||||
| `win11-arm` | `22.14.0` | 2025-02-23 |
|
||||
| `linux-x64` | `22.12.0` | 2025-01-02 |
|
||||
| `linux-arm` | `22.13.0` | 2025-02-16 |
|
||||
| `darwin-x64` | `22.2.0` | 2024-05-28 |
|
||||
| `darwin-arm` | `22.2.0` | 2024-05-29 |
|
||||
| `win10-x64` | `20.12.0` | 2024-03-26 |
|
||||
| `win11-x64` | `20.13.1` | 2024-05-22 |
|
||||
| `win11-arm` | `20.14.0` | 2024-06-11 |
|
||||
| `linux-x64` | `20.11.1` | 2024-03-18 |
|
||||
| `linux-arm` | `20.14.0` | 2024-06-10 |
|
||||
|
||||
:::
|
||||
|
||||
@ -284,7 +285,7 @@ codesign --remove-signature ./sheet2csv
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="win11-x64" label="Windows">
|
||||
<TabItem value="win10-x64" label="Windows">
|
||||
|
||||
In PowerShell, the `Get-Command` command displays the location to `node.exe`:
|
||||
|
||||
@ -313,9 +314,6 @@ signtool remove /s .\sheet2csv.exe
|
||||
|
||||
`signtool` is included in the Windows SDK[^4].
|
||||
|
||||
If the Windows SDK is installed but the command fails, run the comand in the
|
||||
"x64 Native Tools Command Prompt" that ships with Visual Studio.
|
||||
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
@ -346,7 +344,7 @@ codesign -s - ./sheet2csv
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="win11-x64" label="Windows">
|
||||
<TabItem value="win10-x64" label="Windows">
|
||||
|
||||
```bash
|
||||
npx -y postject --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 sheet2csv.exe NODE_SEA_BLOB sheet2csv.blob
|
||||
@ -354,7 +352,7 @@ npx -y postject --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 s
|
||||
|
||||
11) Resign the binary.
|
||||
|
||||
The following PowerShell commands generate a self-signed certificate:
|
||||
The following sequence generates a self-signed certificate:
|
||||
|
||||
```powershell
|
||||
$cert = New-SelfSignedCertificate -Type CodeSigning -DnsName www.onlyspans.net -CertStoreLocation Cert:\CurrentUser\My
|
||||
@ -368,15 +366,6 @@ After creating a cert, sign the binary:
|
||||
signtool sign /v /f mycert.pfx /p hunter2 /fd SHA256 sheet2csv.exe
|
||||
```
|
||||
|
||||
:::info pass
|
||||
|
||||
`signtool` is included in the Windows SDK[^4].
|
||||
|
||||
If the Windows SDK is installed but the command fails, run the comand in the
|
||||
"x64 Native Tools Command Prompt" that ships with Visual Studio.
|
||||
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="linux-x64" label="Linux">
|
||||
|
||||
@ -416,7 +405,7 @@ Signature=adhoc
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="win11-x64" label="Windows">
|
||||
<TabItem value="win10-x64" label="Windows">
|
||||
|
||||
13) Validate the binary signature:
|
||||
|
||||
@ -424,15 +413,6 @@ Signature=adhoc
|
||||
signtool verify sheet2csv.exe
|
||||
```
|
||||
|
||||
:::info pass
|
||||
|
||||
`signtool` is included in the Windows SDK[^4].
|
||||
|
||||
If the Windows SDK is installed but the command fails, run the comand in the
|
||||
"Native Tools Command Prompt" that ships with Visual Studio.
|
||||
|
||||
:::
|
||||
|
||||
If the certificate is self-signed, there may be an error:
|
||||
|
||||
```
|
||||
|
@ -54,22 +54,22 @@ For example, the following script accepts one command line argument, parses the
|
||||
specified file using the SheetJS `readFile` method[^1], generates CSV text from
|
||||
the first worksheet using `sheet_to_csv`[^2], and prints to terminal:
|
||||
|
||||
```ts title="sheet2csv.ts"
|
||||
<CodeBlock language="ts" title="sheet2csv.ts">{`\
|
||||
const XLSX = require("xlsx");
|
||||
|
||||
\n\
|
||||
/* process.argv[2] is the first argument to the script */
|
||||
const filename = process.argv[2];
|
||||
|
||||
\n\
|
||||
/* read file */
|
||||
const wb = XLSX.readFile(filename);
|
||||
|
||||
\n\
|
||||
/* generate CSV of first sheet */
|
||||
const ws = wb.Sheets[wb.SheetNames[0]];
|
||||
const csv = XLSX.utils.sheet_to_csv(ws);
|
||||
|
||||
\n\
|
||||
/* print to terminal */
|
||||
console.log(csv);
|
||||
```
|
||||
console.log(csv);`}
|
||||
</CodeBlock>
|
||||
|
||||
## Complete Example
|
||||
|
||||
@ -79,19 +79,11 @@ This demo was last tested in the following deployments:
|
||||
|
||||
| Architecture | BunJS | Date |
|
||||
|:-------------|:---------|:-----------|
|
||||
| `darwin-x64` | `1.2.10` | 2025-04-21 |
|
||||
| `darwin-arm` | `1.2.8` | 2025-04-03 |
|
||||
| `win11-x64` | `1.1.40` | 2024-12-19 |
|
||||
| `win11-arm` | `1.2.3` | 2025-02-23 |
|
||||
| `linux-x64` | `1.1.43` | 2025-01-10 |
|
||||
| `linux-arm` | `1.2.2` | 2025-02-16 |
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
BunJS on Windows on ARM uses the X64 compatibility layer. It does not generate
|
||||
a native ARM64 binary!
|
||||
| `darwin-x64` | `1.1.10` | 2024-05-28 |
|
||||
| `darwin-arm` | `1.1.10` | 2024-05-29 |
|
||||
| `win10-x64` | `1.1.12` | 2024-06-10 |
|
||||
| `linux-x64` | `1.1.12` | 2024-06-09 |
|
||||
| `linux-arm` | `1.1.12` | 2024-06-10 |
|
||||
|
||||
:::
|
||||
|
||||
@ -114,11 +106,13 @@ bun install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
|
||||
:::caution pass
|
||||
|
||||
In some test runs, the command failed with a module resolution error:
|
||||
On Windows, the command failed with a `ENOTEMPTY` error:
|
||||
|
||||
<CodeBlock>{`\
|
||||
error: https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz failed to resolve`}
|
||||
</CodeBlock>
|
||||
```
|
||||
error: InstallFailed extracting tarball for https://cdn.sheetjs.com/xlsx-0.20.1/xlsx-0.20.1.tgz
|
||||
error: moving "https://cdn.sheetjs.com/xlsx-0.20.1/xlsx-0.20.1.tgz" to cache dir failed
|
||||
ENOTEMPTY: Directory not empty (NtSetInformationFile())
|
||||
```
|
||||
|
||||
The workaround is to prepend `xlsx@` to the URL:
|
||||
|
||||
|
@ -88,9 +88,6 @@ The same permissions that apply to normal Deno scripts apply to the compiler:
|
||||
- The `--allow-net` option must be specified to allow the program to download
|
||||
and upload spreadsheets.
|
||||
|
||||
- The `--allow-import` option must be specified in Deno 2 to allow the program
|
||||
to import SheetJS scripts.
|
||||
|
||||
More flags can be found in the official permissions list[^5]
|
||||
|
||||
|
||||
@ -102,19 +99,13 @@ This demo was last tested in the following deployments:
|
||||
|
||||
| Architecture | Deno | Date |
|
||||
|:-------------|:---------|:-----------|
|
||||
| `darwin-x64` | `2.2.11` | 2025-04-21 |
|
||||
| `darwin-arm` | `2.1.10` | 2025-02-13 |
|
||||
| `win11-x64` | `2.1.6` | 2025-01-19 |
|
||||
| `win11-arm` | `2.2.1` | 2025-02-23 |
|
||||
| `linux-x64` | `2.1.4` | 2025-01-02 |
|
||||
| `linux-arm` | `2.1.10` | 2025-02-15 |
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
Deno on Windows on ARM uses the X64 compatibility layer. It does not generate
|
||||
a native ARM64 binary!
|
||||
| `darwin-x64` | `1.43.6` | 2024-05-28 |
|
||||
| `darwin-arm` | `1.43.6` | 2024-05-23 |
|
||||
| `win10-x64` | `1.41.3` | 2024-03-24 |
|
||||
| `win11-x64` | `1.43.6` | 2024-05-25 |
|
||||
| `win11-arm` | `1.43.6` | 2024-05-25 |
|
||||
| `linux-x64` | `1.41.3` | 2024-03-18 |
|
||||
| `linux-arm` | `1.43.6` | 2024-05-25 |
|
||||
|
||||
:::
|
||||
|
||||
@ -129,7 +120,7 @@ curl -o pres.numbers https://docs.sheetjs.com/pres.numbers
|
||||
2) Test the script with `deno run`:
|
||||
|
||||
```bash
|
||||
deno run -r --allow-read --allow-import https://docs.sheetjs.com/cli/sheet2csv.ts pres.numbers
|
||||
deno run -r --allow-read https://docs.sheetjs.com/cli/sheet2csv.ts pres.numbers
|
||||
```
|
||||
|
||||
The script should display CSV contents from the first sheet:
|
||||
@ -143,39 +134,14 @@ Donald Trump,45
|
||||
Joseph Biden,46
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
In older versions of Deno, the `--allow-import` flag must be omitted:
|
||||
|
||||
```bash
|
||||
deno run -r --allow-read https://docs.sheetjs.com/cli/sheet2csv.ts pres.numbers
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
3) Compile `sheet2csv`:
|
||||
|
||||
```bash
|
||||
deno compile -r --allow-read --allow-import https://docs.sheetjs.com/cli/sheet2csv.ts
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
In older versions of Deno, the `--allow-import` flag must be omitted:
|
||||
3) Compile and run `sheet2csv`:
|
||||
|
||||
```bash
|
||||
deno compile -r --allow-read https://docs.sheetjs.com/cli/sheet2csv.ts
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
4) Run the command:
|
||||
|
||||
```bash
|
||||
./sheet2csv pres.numbers
|
||||
```
|
||||
|
||||
The program should display the same CSV contents as the script (from step 2).
|
||||
The program should display the same CSV contents as the script (from step 2)
|
||||
|
||||
[^1]: See [`readFile` in "Reading Files"](/docs/api/parse-options)
|
||||
[^2]: See [`sheet_to_csv` in "CSV and Text"](/docs/api/utilities/csv#delimiter-separated-output)
|
||||
|
@ -1,247 +0,0 @@
|
||||
---
|
||||
title: TxikiJS Standalone Apps
|
||||
sidebar_label: txiki.js
|
||||
pagination_prev: demos/desktop/index
|
||||
pagination_next: demos/data/index
|
||||
sidebar_custom_props:
|
||||
summary: Compiled apps powered by QuickJS and txiki.js
|
||||
---
|
||||
|
||||
import current from '/version.js';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
`txiki.js` is a small runtime powered by [QuickJS](/docs/demos/engines/quickjs).
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
This demo uses `txiki.js` and SheetJS to create a standalone CLI tool for
|
||||
parsing spreadsheets and generating CSV rows.
|
||||
|
||||
:::caution TxikiJS support is considered experimental.
|
||||
|
||||
Great open source software grows with user tests and reports. Any issues should
|
||||
be reported to the `txiki.js` project for further diagnosis.
|
||||
|
||||
:::
|
||||
|
||||
## Integration Details
|
||||
|
||||
The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone)
|
||||
can be evaluated and consumed in TxikiJS.
|
||||
|
||||
The platform provides APIs for filesystem operations that differ from NodeJS:
|
||||
|
||||
- `tjs.readFile` reads raw data from a specified filename and returns a Promise
|
||||
that resolves to a `Uint8Array`
|
||||
- `tjs.args` is an array of arguments. In the compiled program, the first value
|
||||
will be the program name and the second value will be the first argument.
|
||||
|
||||
The SheetJS filesystem methods ([`readFile`](/docs/api/parse-options) and
|
||||
[`writeFile`](/docs/api/parse-options)) do not recognize the `txiki.js` APIs.
|
||||
Fortunately, `read` and `write` directly work with `Uint8Array` data.
|
||||
|
||||
The following example reads and parses `pres.xlsx` in the current directory:
|
||||
|
||||
```js title="Parse pres.xlsx in TxikiJS"
|
||||
/* read data from pres.xlsx into a Uint8Array */
|
||||
const data = await tjs.readFile("pres.xlsx");
|
||||
|
||||
/* parse data and generate a SheetJS workbook object */
|
||||
const wb = XLSX.read(data);
|
||||
```
|
||||
|
||||
### Script Requirements
|
||||
|
||||
The compiler does not bundle scripts automatically. Scripts that exclusively use
|
||||
web APIs, SheetJS API methods, and `tjs` API methods can be bundled and compiled.
|
||||
|
||||
[`esbuild`](/docs/demos/frontend/bundler/esbuild) is the recommended bundler.
|
||||
|
||||
For example, the following script accepts one command line argument, parses the
|
||||
specified file using `tjs.readFile` and the SheetJS `read` method[^1], generates
|
||||
CSV text from the first sheet using `sheet_to_csv`[^2], and prints the result:
|
||||
|
||||
```js title="sheet2csv.js (before bundling)"
|
||||
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);
|
||||
```
|
||||
|
||||
## Complete Example
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Architecture | TxikiJS | Date |
|
||||
|:-------------|:----------|:-----------|
|
||||
| `darwin-x64` | `24.12.0` | 2025-04-19 |
|
||||
| `darwin-arm` | `24.12.0` | 2025-04-19 |
|
||||
| `win11-x64` | `24.12.0` | 2025-04-19 |
|
||||
| `win11-arm` | `24.12.0` | 2025-04-19 |
|
||||
| `linux-x64` | `24.12.0` | 2025-04-19 |
|
||||
| `linux-arm` | `24.12.0` | 2025-04-19 |
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
TxikiJS on Windows on ARM uses the X64 compatibility layer. It does not generate
|
||||
a native ARM64 binary!
|
||||
|
||||
:::
|
||||
|
||||
0) Create a new project folder and download `txiki.js`[^3]:
|
||||
|
||||
<Tabs groupId="triple">
|
||||
<TabItem value="darwin-x64" label="MacOS">
|
||||
|
||||
```bash
|
||||
mkdir sheetjs-txiki
|
||||
cd sheetjs-txiki
|
||||
curl -LO https://github.com/saghul/txiki.js/releases/download/v24.12.0/txiki-macos.zip
|
||||
unzip txiki-macos.zip
|
||||
mv txiki-macos/tjs .
|
||||
chmod +x tjs
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="linux-x64" label="Linux">
|
||||
|
||||
```bash
|
||||
mkdir sheetjs-txiki
|
||||
cd sheetjs-txiki
|
||||
git clone --recursive https://github.com/saghul/txiki.js --shallow-submodules
|
||||
cd txiki.js
|
||||
make
|
||||
cp build/tjs ../
|
||||
cd ..
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="win11-x64" label="Windows">
|
||||
|
||||
```bash
|
||||
mkdir sheetjs-txiki
|
||||
cd sheetjs-txiki
|
||||
curl.exe -LO https://github.com/saghul/txiki.js/releases/download/v24.12.0/txiki-windows-x86_64.zip
|
||||
tar xf txiki-windows-x86_64.zip
|
||||
mv txiki-windows-x86_64\* .
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
1) Download the test file https://docs.sheetjs.com/pres.numbers:
|
||||
|
||||
```bash
|
||||
curl -LO https://docs.sheetjs.com/pres.numbers
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
In PowerShell, the command may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'LO'.
|
||||
```
|
||||
|
||||
`curl.exe` must be invoked directly:
|
||||
|
||||
```bash
|
||||
curl.exe -LO https://docs.sheetjs.com/pres.numbers
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
2) Save the [contents of the `sheet2csv.js` code block](#script-requirements)
|
||||
to `sheet2csv.js` in the project folder.
|
||||
|
||||
```bash
|
||||
curl -LO https://docs.sheetjs.com/txikijs/sheet2csv.js
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
In PowerShell, the command may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'LO'.
|
||||
```
|
||||
|
||||
`curl.exe` must be invoked directly:
|
||||
|
||||
```bash
|
||||
curl.exe -LO https://docs.sheetjs.com/txikijs/sheet2csv.js
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
3) Download the SheetJS Standalone script and move to the project directory:
|
||||
|
||||
<ul>
|
||||
<li><a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}>xlsx.full.min.js</a></li>
|
||||
</ul>
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}
|
||||
</CodeBlock>
|
||||
|
||||
:::note pass
|
||||
|
||||
In PowerShell, the command may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'LO'.
|
||||
```
|
||||
|
||||
`curl.exe` must be invoked directly:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
curl.exe -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
4) Bundle the script:
|
||||
|
||||
```js
|
||||
npx -y esbuild sheet2csv.js --bundle --outfile=bundle.js --platform=neutral
|
||||
```
|
||||
|
||||
5) Compile and run `sheet2csv`:
|
||||
|
||||
```bash
|
||||
./tjs compile bundle.js sheet2csv
|
||||
./sheet2csv pres.numbers
|
||||
```
|
||||
|
||||
The program should display the same CSV contents as the script:
|
||||
|
||||
```text title="Expected Output"
|
||||
Name,Index
|
||||
Bill Clinton,42
|
||||
GeorgeW Bush,43
|
||||
Barack Obama,44
|
||||
Donald Trump,45
|
||||
Joseph Biden,46
|
||||
```
|
||||
|
||||
[^1]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^2]: See [`sheet_to_csv` in "CSV and Text"](/docs/api/utilities/csv#delimiter-separated-output)
|
||||
[^3]: When this demo was last tested, the [ZIP archive for version `24.12.0`](https://github.com/saghul/txiki.js/releases/tag/v24.12.0) was downloaded and extracted.
|
@ -54,13 +54,6 @@ The following frameworks have been tested on the following platforms:
|
||||
|
||||
<FrameworkData/>
|
||||
|
||||
:::note pass
|
||||
|
||||
Asterisks (✱) in the Windows for ARM column mark tests that generated x64
|
||||
binaries. The CLI tools run under the x64 emulator integrated in Windows.
|
||||
|
||||
:::
|
||||
|
||||
:::tip pass
|
||||
|
||||
The [`xlsx-cli`](https://cdn.sheetjs.com/xlsx-cli/) NodeJS script is available
|
||||
|
@ -147,7 +147,7 @@ function SheetJSQLWriter() {
|
||||
Query builders are designed to simplify query generation and normalize field
|
||||
types and other database minutiae.
|
||||
|
||||
**KnexJS**
|
||||
**Knex**
|
||||
|
||||
**[The exposition has been moved to a separate page.](/docs/demos/data/knex)**
|
||||
|
||||
|
@ -37,13 +37,12 @@ SQLite-compatible database built into Chromium and Google Chrome.
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Platform | Connector Library | Date |
|
||||
|:-----------------|:----------------------------|:-----------|
|
||||
| Chromium 131 | `sql.js` (`1.8.0`) | 2025-01-08 |
|
||||
| Konqueror 22 | `sql.js` (`1.8.0`) | 2025-04-23 |
|
||||
| NodeJS `20.18.0` | `better-sqlite3` (`11.7.2`) | 2025-01-08 |
|
||||
| BunJS `1.1.43` | (built-in) | 2025-01-08 |
|
||||
| Deno `2.1.4` | `sqlite` (`3.9.1`) | 2025-01-09 |
|
||||
| Platform | Connector Library | Date |
|
||||
|:-----------------|:---------------------------|:-----------|
|
||||
| Chromium 122 | `sql.js` (`1.8.0`) | 2024-04-09 |
|
||||
| NodeJS `20.12.1` | `better-sqlite3` (`9.4.5`) | 2024-04-09 |
|
||||
| BunJS `1.1.3` | (built-in) | 2024-04-09 |
|
||||
| Deno `1.42.1` | `sqlite` (`3.8`) | 2024-04-09 |
|
||||
|
||||
:::
|
||||
|
||||
@ -316,16 +315,6 @@ curl -LO https://docs.sheetjs.com/sqlite/SheetJSQLiteDeno.ts
|
||||
deno run --allow-read --allow-write SheetJSQLiteDeno.ts
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
Deno 2 requires the `--allow-import` entitlement:
|
||||
|
||||
```bash
|
||||
deno run --allow-read --allow-write --allow-import SheetJSQLiteDeno.ts
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
Open `SheetJSQLiteDeno.xlsx` with a spreadsheet editor.
|
||||
|
||||
[^1]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
|
||||
|
@ -25,10 +25,10 @@ This demo was tested in the following environments:
|
||||
|
||||
| Version | Database | Connector Module | Date |
|
||||
|:----------|:---------|:-----------------|:-----------|
|
||||
| `0.21.20` | SQLite | `sqlite3` | 2025-01-09 |
|
||||
| `2.4.2` | SQLite | `better-sqlite3` | 2025-01-09 |
|
||||
| `2.5.1` | SQLite | `better-sqlite3` | 2025-01-09 |
|
||||
| `3.1.0` | SQLite | `better-sqlite3` | 2025-01-09 |
|
||||
| `0.21.20` | SQLite | `sqlite3` | 2024-04-09 |
|
||||
| `2.4.2` | SQLite | `better-sqlite3` | 2024-04-09 |
|
||||
| `2.5.1` | SQLite | `better-sqlite3` | 2024-04-09 |
|
||||
| `3.1.0` | SQLite | `better-sqlite3` | 2024-04-09 |
|
||||
|
||||
:::
|
||||
|
||||
@ -192,7 +192,7 @@ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz knex be
|
||||
For KnexJS version `0.21.20`, the `sqlite3` module must be installed:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz knex@0.21.20 sqlite3`}
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz knex sqlite3`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
@ -36,12 +36,9 @@ This demo was tested in the following environments:
|
||||
|
||||
| Postgres | Connector Library | Date |
|
||||
|:---------|:------------------|:-----------|
|
||||
| `17.2` | `pg` (`8.13.1`) | 2025-01-03 |
|
||||
| `16.6` | `pg` (`8.13.1`) | 2025-01-03 |
|
||||
| `15.10` | `pg` (`8.13.1`) | 2025-01-03 |
|
||||
| `14.15` | `pg` (`8.13.1`) | 2025-01-03 |
|
||||
| `13.18` | `pg` (`8.13.1`) | 2025-01-03 |
|
||||
| `12.22` | `pg` (`8.13.1`) | 2025-01-03 |
|
||||
| `16.2.1` | `pg` (`8.11.4`) | 2024-03-31 |
|
||||
| `15.6` | `pg` (`8.11.4`) | 2024-03-31 |
|
||||
| `14.11` | `pg` (`8.11.4`) | 2024-03-31 |
|
||||
|
||||
:::
|
||||
|
||||
@ -132,140 +129,80 @@ for(let row of aoo) {
|
||||
|
||||
### Creating a Table
|
||||
|
||||
The worksheet can be scanned to determine column names and types. With the names
|
||||
and types, a `CREATE TABLE` query can be written.
|
||||
The array of objects can be scanned to determine column names and types. With
|
||||
the names and types, a `CREATE TABLE` query can be written.
|
||||
|
||||
<details>
|
||||
<summary><b>Implementation Details</b> (click to show)</summary>
|
||||
|
||||
The `sheet_to_pg_table` function:
|
||||
The `aoo_to_pg_table` function:
|
||||
|
||||
- scans worksheet cells to determine column names and types
|
||||
- scans each row object to determine column names and types
|
||||
- drops and creates a new table with the determined column names and types
|
||||
- loads the entire dataset into the new table
|
||||
|
||||
```js
|
||||
/* create table and load data given a worksheet and a PostgreSQL client */
|
||||
async function sheet_to_pg_table(client, worksheet, tableName) {
|
||||
if (!worksheet['!ref']) return;
|
||||
/* create table and load data given an array of objects and a PostgreSQL client */
|
||||
async function aoo_to_pg_table(client, aoo, table_name) {
|
||||
/* define types that can be converted (e.g. boolean can be stored in float) */
|
||||
const T_FLOAT = ["float8", "boolean"];
|
||||
const T_BOOL = ["boolean"];
|
||||
|
||||
const range = XLSX.utils.decode_range(worksheet['!ref']);
|
||||
|
||||
/* Extract headers from first row, clean names for PostgreSQL */
|
||||
const headers = [];
|
||||
for (let col = range.s.c; col <= range.e.c; col++) {
|
||||
const cellAddress = XLSX.utils.encode_cell({ r: range.s.r, c: col });
|
||||
const cell = worksheet[cellAddress];
|
||||
const headerValue = cell ? String(cell.v).replace(/[^a-zA-Z0-9_]/g, '_') : `column_${col + 1}`;
|
||||
headers.push(headerValue.toLowerCase());
|
||||
}
|
||||
|
||||
/* Group cell values by column for type deduction */
|
||||
const columnValues = headers.map(() => []);
|
||||
for (let row = range.s.r + 1; row <= range.e.r; row++) {
|
||||
for (let col = range.s.c; col <= range.e.c; col++) {
|
||||
const cellAddress = XLSX.utils.encode_cell({ r: row, c: col });
|
||||
const cell = worksheet[cellAddress];
|
||||
columnValues[col].push(cell);
|
||||
}
|
||||
}
|
||||
|
||||
/* Deduce PostgreSQL type for each column */
|
||||
/* types is a map from column headers to Knex schema column type */
|
||||
const types = {};
|
||||
headers.forEach((header, idx) => {
|
||||
types[header] = deduceType(columnValues[idx]);
|
||||
});
|
||||
|
||||
/* names is an ordered list of the column header names */
|
||||
const names = [];
|
||||
|
||||
/* loop across each row object */
|
||||
aoo.forEach(row =>
|
||||
/* Object.entries returns a row of [key, value] pairs */
|
||||
Object.entries(row).forEach(([k,v]) => {
|
||||
|
||||
/* If this is first occurrence, mark unknown and append header to names */
|
||||
if(!types[k]) { types[k] = ""; names.push(k); }
|
||||
|
||||
/* skip null and undefined values */
|
||||
if(v == null) return;
|
||||
|
||||
/* check and resolve type */
|
||||
switch(typeof v) {
|
||||
/* change type if it is empty or can be stored in a float */
|
||||
case "number": if(!types[k] || T_FLOAT.includes(types[k])) types[k] = "float8"; break;
|
||||
/* change type if it is empty or can be stored in a boolean */
|
||||
case "boolean": if(!types[k] || T_BOOL.includes(types[k])) types[k] = "boolean"; break;
|
||||
/* no other type can hold strings */
|
||||
case "string": types[k] = "text"; break;
|
||||
default: types[k] = "text"; break;
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
/* Delete table if it exists in the DB */
|
||||
await client.query(format('DROP TABLE IF EXISTS %I', tableName));
|
||||
const query = format("DROP TABLE IF EXISTS %I;", table_name);
|
||||
await client.query(query);
|
||||
|
||||
/* Create table */
|
||||
const createTableSQL = format(
|
||||
'CREATE TABLE %I (%s)',
|
||||
tableName,
|
||||
headers.map(header => format('%I %s', header, types[header])).join(', ')
|
||||
);
|
||||
await client.query(createTableSQL);
|
||||
{
|
||||
const entries = Object.entries(types);
|
||||
const Istr = entries.map(e => format(`%I ${e[1]}`, e[0])).join(", ");
|
||||
let query = format.withArray(`CREATE TABLE %I (${Istr});`, [ table_name ]);
|
||||
await client.query(query);
|
||||
}
|
||||
|
||||
/* Insert data row by row */
|
||||
for (let row = range.s.r + 1; row <= range.e.r; row++) {
|
||||
const values = headers.map((header, col) => {
|
||||
const cellAddress = XLSX.utils.encode_cell({ r: row, c: col });
|
||||
const cell = worksheet[cellAddress];
|
||||
return parseValue(cell, types[header]);
|
||||
});
|
||||
|
||||
const insertSQL = format(
|
||||
'INSERT INTO %I (%s) VALUES (%s)',
|
||||
tableName,
|
||||
headers.map(h => format('%I', h)).join(', '),
|
||||
values.map(() => '%L').join(', ')
|
||||
/* Insert each row */
|
||||
for(let row of aoo) {
|
||||
const ent = Object.entries(row);
|
||||
const Istr = Array.from({length: ent.length}, ()=>"%I").join(", ");
|
||||
const Lstr = Array.from({length: ent.length}, ()=>"%L").join(", ");
|
||||
let query = format.withArray(
|
||||
`INSERT INTO %I (${Istr}) VALUES (${Lstr});`,
|
||||
[ table_name, ...ent.map(x => x[0]), ...ent.map(x => x[1]) ]
|
||||
);
|
||||
await client.query(format(insertSQL, ...values));
|
||||
await client.query(query);
|
||||
}
|
||||
}
|
||||
|
||||
function deduceType(cells) {
|
||||
if (!cells || cells.length === 0) return 'text';
|
||||
|
||||
const nonEmptyCells = cells.filter(cell => cell && cell.v != null);
|
||||
if (nonEmptyCells.length === 0) return 'text';
|
||||
|
||||
// Check for dates by looking at both cell type and formatted value
|
||||
const isDateCell = cell => cell?.t === 'd' || (cell?.t === 'n' && cell.w && /\d{4}-\d{2}-\d{2}|\d{1,2}\/\d{1,2}\/\d{4}|\d{2}-[A-Za-z]{3}-\d{4}|[A-Za-z]{3}-\d{2}|\d{1,2}-[A-Za-z]{3}/.test(cell.w));
|
||||
|
||||
if (nonEmptyCells.some(isDateCell)) { return 'date'; }
|
||||
|
||||
const allBooleans = nonEmptyCells.every(cell => cell.t === 'b');
|
||||
if (allBooleans) { return 'boolean'; }
|
||||
|
||||
const allNumbers = nonEmptyCells.every(cell => cell.t === 'n' || (cell.t === 's' && !isNaN(cell.v.replace(/[,$\s%()]/g, ''))));
|
||||
|
||||
if (allNumbers) {
|
||||
const numbers = nonEmptyCells.map(cell => {
|
||||
if (cell.t === 'n') return cell.v;
|
||||
return parseFloat(cell.v.replace(/[,$\s%()]/g, ''));
|
||||
});
|
||||
|
||||
const needsPrecision = numbers.some(num => {
|
||||
const str = num.toString();
|
||||
return str.includes('e')
|
||||
|| (str.includes('.') && str.split('.')[1].length > 6)
|
||||
|| Math.abs(num) > 1e15;
|
||||
});
|
||||
|
||||
return needsPrecision ? 'numeric' : 'double precision';
|
||||
}
|
||||
return 'text';
|
||||
}
|
||||
|
||||
function parseValue(cell, type) {
|
||||
if (!cell || cell.v == null) return null;
|
||||
|
||||
switch (type) {
|
||||
case 'date':
|
||||
if (cell.t === 'd') { return cell.v.toISOString().split('T')[0]; }
|
||||
if (cell.t === 'n') {
|
||||
const date = new Date((cell.v - 25569) * 86400 * 1000);
|
||||
return date.toISOString().split('T')[0];
|
||||
}
|
||||
return null;
|
||||
|
||||
case 'numeric':
|
||||
case 'double precision':
|
||||
if (cell.t === 'n') return cell.v;
|
||||
if (cell.t === 's') {
|
||||
const cleaned = cell.v.replace(/[,$\s%()]/g, '');
|
||||
if (!isNaN(cleaned)) return parseFloat(cleaned);
|
||||
}
|
||||
return null;
|
||||
|
||||
case 'boolean':
|
||||
return cell.t === 'b' ? cell.v : null;
|
||||
|
||||
default:
|
||||
return String(cell.v);
|
||||
}
|
||||
return client;
|
||||
}
|
||||
```
|
||||
|
||||
@ -274,7 +211,7 @@ function parseValue(cell, type) {
|
||||
|
||||
## Complete Example
|
||||
|
||||
0) Install and start the PostgreSQL serve r.
|
||||
0) Install and start the PostgreSQL server.
|
||||
|
||||
<details>
|
||||
<summary><b>Installation Notes</b> (click to show)</summary>
|
||||
@ -293,23 +230,6 @@ Or, if you don't want/need a background service you can just run:
|
||||
LC_ALL="C" /usr/local/opt/postgresql@16/bin/postgres -D /usr/local/var/postgresql@16
|
||||
```
|
||||
|
||||
On Linux, install `postgresql` by running the following script:
|
||||
|
||||
```bash
|
||||
echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" | sudo tee /etc/apt/sources.list.d/pgdg.list
|
||||
wget -qO - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
|
||||
|
||||
sudo apt update
|
||||
sudo apt install postgresql-16
|
||||
sudo systemctl start postgresql
|
||||
|
||||
# Optional: Create user with password
|
||||
sudo -u postgres createuser -P $USER
|
||||
sudo -u postgres psql -c "ALTER USER $USER WITH SUPERUSER;"
|
||||
```
|
||||
|
||||
If running the optional user creation steps above, a PostgreSQL password will be required. [^8]
|
||||
|
||||
Run the command to start a local database instance.
|
||||
|
||||
</details>
|
||||
@ -320,17 +240,6 @@ Run the command to start a local database instance.
|
||||
dropdb SheetJSPG
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
Some Linux installations do not create the `dropdb` command. The command can be
|
||||
run through the `postgres` command-line tool:
|
||||
|
||||
```bash title="dropdb in Ubuntu/Debian"
|
||||
sudo -i -u postgres dropdb SheetJSPG
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::info pass
|
||||
|
||||
If the server is running elsewhere, or if the username is different from the
|
||||
@ -344,35 +253,12 @@ current user, command-line flags can override the defaults.
|
||||
|
||||
:::
|
||||
|
||||
:::note pass
|
||||
|
||||
If the database does not exist, the command will fail with the following error:
|
||||
|
||||
```
|
||||
dropdb: error: database removal failed: ERROR: database "SheetJSPG" does not exist
|
||||
```
|
||||
|
||||
This error can be safely ignored.
|
||||
|
||||
:::
|
||||
|
||||
2) Create an empty `SheetJSPG` database using the `createdb` command:
|
||||
|
||||
```bash
|
||||
createdb SheetJSPG
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
Some Linux installations do not create the `createdb` command. The command can be
|
||||
run through the `postgres` command-line tool:
|
||||
|
||||
```bash title="createdb in Ubuntu/Debian"
|
||||
sudo -i -u postgres createdb SheetJSPG
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::note pass
|
||||
|
||||
`createdb` supports the same `-h`, `-p`, and `-U` flags as `dropdb`.
|
||||
@ -392,7 +278,7 @@ npm init -y
|
||||
4) Install the `pg` connector module:
|
||||
|
||||
```bash
|
||||
npm i --save pg@8.13.1
|
||||
npm i --save pg@8.11.4
|
||||
```
|
||||
|
||||
5) Save the following example codeblock to `PGTest.js`:
|
||||
@ -430,10 +316,6 @@ correct host name and port number.
|
||||
- If the server expects a different username and password, uncomment the `user`
|
||||
and `password` lines and replace the values with the username and password.
|
||||
|
||||
- For Ubuntu/Debian PostgreSQL installations, the default user is `postgres`.
|
||||
The password must be set during installation or using `sudo -u postgres psql`
|
||||
followed by `\password postgres` in the `psql` prompt.
|
||||
|
||||
7) Run the script:
|
||||
|
||||
```bash
|
||||
@ -497,11 +379,6 @@ correct host name and port number.
|
||||
- If the server expects a different username and password, uncomment the `user`
|
||||
and `password` lines and replace the values with the username and password.
|
||||
|
||||
- For Ubuntu/Debian PostgreSQL installations, the default user is postgres.
|
||||
The password must be set during installation or using `sudo -u postgres psql`
|
||||
followed by `\password postgres` in the `psql` prompt.
|
||||
|
||||
|
||||
11) Fetch the example file [`pres.numbers`](https://docs.sheetjs.com/pres.numbers):
|
||||
|
||||
```bash
|
||||
@ -543,5 +420,4 @@ psql SheetJSPG -c 'SELECT * FROM "Presidents";'
|
||||
[^4]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
|
||||
[^5]: See [`writeFile` in "Writing Files"](/docs/api/write-options)
|
||||
[^6]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
[^7]: The [`pg-format`](https://npm.im/pg-format) package is available on the public NPM registry. Even though the project is marked as deprecated, the official [`pg` website still recommends `pg-format`](https://node-postgres.com/features/queries#parameterized-query:~:text=use%20pg%2Dformat%20package%20for%20handling%20escaping)
|
||||
[^8]: PostgreSQL on Linux uses [SCRAM authentication by default, which requires a password](https://www.postgresql.org/docs/current/auth-password.html)
|
||||
[^7]: The [`pg-format`](https://npm.im/pg-format) package is available on the public NPM registry. Even though the project is marked as deprecated, the official [`pg` website still recommends `pg-format`](https://node-postgres.com/features/queries#parameterized-query:~:text=use%20pg%2Dformat%20package%20for%20handling%20escaping)
|
@ -34,9 +34,9 @@ against SQL injection and other vulnerabilities.
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| MariaDB | Connector Library | Date |
|
||||
|:---------|:--------------------|:-----------|
|
||||
| `11.6.2` | `mysql2` (`3.12.0`) | 2025-01-19 |
|
||||
| MariaDB | Connector Library | Date |
|
||||
|:---------|:-------------------|:-----------|
|
||||
| `11.3.2` | `mysql2` (`3.9.7`) | 2024-05-04 |
|
||||
|
||||
:::
|
||||
|
||||
|
@ -12,8 +12,6 @@ sidebar_custom_props:
|
||||
</head>
|
||||
|
||||
import current from '/version.js';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
[AlaSQL](https://alasql.org/) is a pure JavaScript in-memory SQL database. It
|
||||
@ -30,12 +28,10 @@ strategies for general data processing in AlaSQL expressions.
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Environment | AlaSQL | Date |
|
||||
|:-------------|:-------|:----------:|
|
||||
| Chromium 135 | 3.1.0 | 2025-04-23 |
|
||||
| Konqueror 22 | 3.1.0 | 2025-04-23 |
|
||||
| NodeJS | 3.1.0 | 2025-04-23 |
|
||||
| BunJS | 3.1.0 | 2025-04-23 |
|
||||
| Environment | AlaSQL | Date |
|
||||
|:--------------------|:-------|:----------:|
|
||||
| NodeJS | 3.1.0 | 2024-06-03 |
|
||||
| Standalone (Chrome) | 3.1.0 | 2024-06-03 |
|
||||
|
||||
:::
|
||||
|
||||
@ -267,8 +263,8 @@ const { promise: alasql } = require("alasql");
|
||||
1) Create an empty folder for the project:
|
||||
|
||||
```bash
|
||||
mkdir sheetjs-alasql
|
||||
cd sheetjs-alasql
|
||||
mkdir alasql
|
||||
cd alasql
|
||||
```
|
||||
|
||||
2) In the folder, create a stub `package.json` with the `xlsx` override:
|
||||
@ -283,23 +279,10 @@ cd sheetjs-alasql
|
||||
|
||||
3) Install SheetJS and AlaSQL:
|
||||
|
||||
<Tabs groupId="ssplat">
|
||||
<TabItem value="nodejs" label="NodeJS">
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save alasql@3.1.0 https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="bun" label="Bun">
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
bun i --save alasql@3.1.0 https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
4) Download the test file https://docs.sheetjs.com/pres.numbers :
|
||||
|
||||
```bash
|
||||
@ -328,24 +311,11 @@ const { promise: alasql } = require("alasql");
|
||||
|
||||
6) Run the test script
|
||||
|
||||
<Tabs groupId="ssplat">
|
||||
<TabItem value="nodejs" label="NodeJS">
|
||||
|
||||
```bash
|
||||
node SheetJSAlaSQL.js
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="bun" label="Bun">
|
||||
|
||||
```bash
|
||||
bun run SheetJSAlaSQL.js
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
The script will print the following array of objects:
|
||||
The output should display:
|
||||
|
||||
```js
|
||||
[
|
||||
@ -355,7 +325,7 @@ The script will print the following array of objects:
|
||||
]
|
||||
```
|
||||
|
||||
The script will generate `SheetJSAlaSQL1.xlsx` with an additional row:
|
||||
The script should generate `SheetJSAlaSQL1.xlsx` with the additional row:
|
||||
|
||||
```csv
|
||||
Name,Index
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user