This commit is contained in:
SheetJS 2023-09-17 00:57:06 -04:00
parent 90300cd6b7
commit 4438bd86cd
9 changed files with 504 additions and 27 deletions

@ -64,7 +64,8 @@ but the registry is out of date. The latest version on that registry is 0.18.5
This is a known registry bug
<https://cdn.sheetjs.com/> is the authoritative source for SheetJS scripts.
**The SheetJS CDN** <https://cdn.sheetjs.com/> **is the authoritative source**
**for SheetJS modules.**
For existing projects, the easiest approach is to uninstall and reinstall:

@ -56,7 +56,8 @@ but the registry is out of date. The latest version on that registry is 0.18.5
This is a known registry bug
<https://cdn.sheetjs.com/> is the authoritative source for SheetJS scripts.
**The SheetJS CDN** <https://cdn.sheetjs.com/> **is the authoritative source**
**for SheetJS modules.**
For existing projects, the easiest approach is to uninstall and reinstall:

@ -72,7 +72,8 @@ and the types URLs should be updated at the same time:
The official Deno registry is out of date. This is a registry bug.
<https://cdn.sheetjs.com/> is the authoritative source for SheetJS scripts.
**The SheetJS CDN** <https://cdn.sheetjs.com/> **is the authoritative source**
**for SheetJS modules.**
:::

@ -3,24 +3,17 @@ pagination_prev: getting-started/index
pagination_next: getting-started/examples/index
sidebar_position: 7
sidebar_custom_props:
summary: Download and Import ECMAScript Modules
summary: Load NodeJS-style modules using CommonJS or ESM
---
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
# Bun
Module scripts are available at <https://cdn.sheetjs.com/>.
Tarballs are available on <https://cdn.sheetjs.com>.
<p><a href={`https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs`}>https://cdn.sheetjs.com/xlsx-{current}/package/xlsx.mjs</a> is the URL for {current}</p>
After downloading the script, it can be directly imported:
```js
import * as XLSX from './xlsx.mjs';
import * as fs from 'fs';
XLSX.set_fs(fs);
```
<p><a href={`https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}>https://cdn.sheetjs.com/xlsx-{current}/xlsx-{current}.tgz</a> is the URL for version {current}</p>
:::caution Bun support is considered experimental.
@ -29,6 +22,14 @@ be reported to the Bun project for further diagnosis.
:::
## Installation
Tarballs can be directly installed with `bun install`[^1]:
<CodeBlock language="bash">{`\
bun install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
</CodeBlock>
:::tip pass
[Watch the repo](https://git.sheetjs.com/SheetJS/sheetjs) or subscribe to the
@ -37,15 +38,52 @@ new versions are released!
:::
## Encoding support
:::warning pass
If Encoding support is required, `cpexcel.full.mjs` must be manually imported.
At the time of writing `bun install` does not support vendored tarballs[^2].
<p><a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/cpexcel.full.mjs`}>https://cdn.sheetjs.com/xlsx-{current}/package/dist/cpexcel.full.mjs</a> is the URL for {current}</p>
:::
## Usage
```ts
/* load the codepage support library for extended support with older formats */
import * as cptable from './cpexcel.full.mjs';
XLSX.set_cptable(cptable);
Bun supports both "CommonJS" and "ESM" modules.
:::info pass
It is strongly recommended to use CommonJS in Bun.
:::
#### CommonJS `require`
By default, the module supports `require` and it will automatically add support
for streams and file system access:
```js
const { readFile } = require("xlsx");
const wb = readFile("pres.numbers"); // works!
```
#### ESM `import`
When importing the library using ESM `import` statements, the native NodeJS
modules are not loaded. They must be added manually:
```js
import * as XLSX from 'xlsx';
/* load 'fs' for readFile and writeFile support */
import * as fs from 'fs';
XLSX.set_fs(fs);
/* load 'stream' for stream support */
import { Readable } from 'stream';
XLSX.stream.set_readable(Readable);
/* load the codepage support library for extended support with older formats */
import * as cpexcel from 'xlsx/dist/cpexcel.full.mjs';
XLSX.set_cptable(cpexcel);
```
[^1]: Bun releases before the official 1.0.0 release did not support tarball dependencies. If a pre-1.0.0 release must be used, the module can be installed with a package manager like Yarn or the ESM script can be vendored.
[^2]: See [the relevant issue in the Bun issue tracker](https://github.com/oven-sh/bun/issues/101)

@ -394,7 +394,7 @@ Open the generated file and verify the contents match the grid.
should update with the data in the file.
[^1]: See ["Array of Objects" in the ReactJS demo](/docs/demos/frontend/react#array-of-objects)
[^2]: The "Story" section of the ["Getting Started" page in the Glide Data Grid Storybook](https://quicktype.github.io/glide-data-grid/?path=/story/glide-data-grid-docs--getting-started) stores an Array of Objects outside of the component.
[^2]: The "Story" section of the ["Getting Started" page in the Glide Data Grid Storybook](https://glideapps.github.io/glide-data-grid/?path=/story/glide-data-grid-docs--getting-started) stores an Array of Objects outside of the component.
[^3]: See [the "Data" section in `DataEditorProps`](https://grid.glideapps.com/docs/interfaces/DataEditorProps.html#columns:~:text=default-,Data,-Readonly) in the Glide Data Grid API documentation.
[^4]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^5]: See ["SheetJS Data Model"](/docs/csf)

@ -29,7 +29,7 @@ remote file, parses the contents, and writes data to the sheet:
:::note
This demo was last tested on 2023 April 17.
This demo was last tested on 2023 September 16.
:::
@ -152,12 +152,28 @@ npx @google/clasp push
:::caution
If the push command fails with an error message like
If the Google Apps Script API is not enabled, the command will display an object
with `code: 403` and an error message about the Apps Script API:
> User has not enabled the Apps Script API
```js
{ // ...
code: 403,
errors: [
{
message: 'User has not enabled the Apps Script API. Enable it by ...',
domain: 'global',
reason: 'forbidden'
}
]
}
```
The message includes a URL (`https://script.google.com/home/usersettings` when
the demo was last tested). Visit that URL and enable the Google Apps Script API.
the demo was last tested). Visit that URL.
If the Google Apps Script API is "Off", click on "Google Apps Script API" and
click on the slider to enable the API.
After enabling the API, run `npx @google/clasp push` again.
:::
@ -190,7 +206,7 @@ function SHEETJS(url) {
}
```
Click the "Save Project" icon to save the project.
Click the "Save Project" icon (💾) to save the project.
11) In the Google Sheets window, select cell A1 and enter the formula

@ -0,0 +1,414 @@
---
title: Sheets in .NET with Jint
sidebar_label: C# + Jint
description: Process structured data in C#. Seamlessly integrate spreadsheets into your program by pairing Jint and SheetJS. Handle the most complex Excel files without breaking a sweat.
pagination_prev: demos/bigdata/index
pagination_next: solutions/input
---
import current from '/version.js';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
Jint[^1] is a JavaScript interpreter for .NET Standard and .NET Core. It has
built-in support for binary data with .NET `byte[]` and ES6 `Uint8Array`.
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
This demo uses Jint and SheetJS to read and write spreadsheets. We'll explore
how to load SheetJS in the Jint engine, exchange binary data with a C# program,
and process spreadsheets and structured data.
The ["Integration Example"](#integration-example) section includes a complete
command-line tool for reading arbitrary workbooks and writing data to XLSB
(Excel 2007+ Binary Format) workbooks.
## Integration Details
:::note pass
Most of the integration functions are not documented. This explanation is based
on version `3.0.0-beta-2051`.
:::
The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone)
can be parsed and evaluated in a Jint engine instance.
### Initialize Jint
A `Jint.Engine` object can be created in one line:
```csharp
var engine = new Jint.Engine();
```
Jint does not expose the NodeJS `global` but does provide `globalThis`. Using
`Jint.Engine#Evaluate`, a `global` global variable can be created:
>The JS code is
```js
global = globalThis;
```
```csharp
engine.Evaluate("global = globalThis");
```
### Load SheetJS Scripts
The main library can be loaded by reading the scripts from the file system with
`System.IO.File.ReadAllText` and evaluating in the Jint engine instance:
```csharp
/* read and evaluate the shim script */
string src = System.IO.File.ReadAllText("shim.min.js");
engine.Evaluate(src);
/* read and evaluate the main library */
engine.Evaluate(System.IO.File.ReadAllText("xlsx.full.min.js"));
```
To confirm the library is loaded, `XLSX.version` can be inspected:
```csharp
Console.WriteLine("SheetJS version {0}", engine.Evaluate("XLSX.version"));
```
:::info pass
The Jint `Evaluate` method returns a generic `Jint.Native.JsValue` object.
When the JS expression returns a string, the `JsValue` object is an instance of
`Jint.Native.JsString`. C# `ToString` will return the underlying `string` value.
:::
### Reading Files
In C#, `System.IO.File.ReadAllBytes` reads file data into a `byte[]` byte array:
```csharp
string filename = "pres.xlsx";
byte[] buf = File.ReadAllBytes(filename);
```
Jint natively supports `Uint8Array` construction from the byte array:
```csharp
Jint.Native.JsValue u8 = engine.Realm.Intrinsics.Uint8Array.Construct(buf);
```
`Jint.Engine#SetValue` will assign the `Uint8Array` to a scope variable in JS:
```csharp
engine.SetValue("buf", u8);
```
The `buf` variable can be parsed from JS with the SheetJS `read` method[^2]:
>The JS code is
```js
var wb = XLSX.read(buf);
```
```csharp
engine.Evaluate("var wb = XLSX.read(buf);");
```
`wb` is a SheetJS workbook object. The ["SheetJS Data Model"](/docs/csf) section
describes the object structure and the ["API Reference"](/docs/api) section
describes various helper functions.
### Writing Files
The SheetJS `write` method[^3] can write workbooks. The option `type: "buffer"`
instructs the library to generate `Uint8Array` objects.
> The JS code for exporting to the XLSB format is:
```js
var u8 = XLSX.write(wb, {bookType: 'xlsb', type: 'buffer'});
```
The file format can be changed with the `bookType` option[^4]
```csharp
Jint.Native.JsValue xlsb = engine.Evaluate("XLSX.write(wb, {bookType: 'xlsb', type: 'buffer'})");
```
`xlsb` represents a `Uint8Array`. `xlsb.AsUint8Array()` returns the bytes as a
`byte[]` array which can be exported with `System.IO.File.WriteAllBytes`:
```csharp
byte[] outfile = xlsb.AsUint8Array();
System.IO.File.WriteAllBytes("SheetJSJint.xlsb", outfile);
```
## Integration Example
:::note
This demo was tested in the following deployments:
| Architecture | Jint Version | Date |
|:-------------|:------------------|:-----------|
| `darwin-x64` | `3.0.0-beta-2051` | 2023-09-16 |
| `win10-x64` | `3.0.0-beta-2051` | 2023-09-16 |
:::
0) Install .NET[^5]
### Platform Configuration
1) 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
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>
2) Open a new Terminal window in macOS or PowerShell window in Windows.
### Base Project
3) Create a new folder `SheetJSJint` and a new project using the `dotnet` tool:
```bash
rm -rf SheetJSJint
mkdir SheetJSJint
cd SheetJSJint
dotnet new console --framework net6.0
dotnet run
```
4) Add Jint using the NuGet tool:
```bash
dotnet nuget add source https://www.myget.org/F/jint/api/v3/index.json
dotnet add package Jint --version 3.0.0-beta-2051
```
To verify Jint is installed, replace `Program.cs` with the following:
```csharp title="Program.cs"
var engine = new Jint.Engine();
Console.WriteLine("Hello {0}", engine.Evaluate("'Sheet' + 'JS'"));
```
After saving, run the program:
```bash
dotnet run
```
The terminal should display `Hello SheetJS`
### Add SheetJS
5) Download the standalone script, shim and test file:
<ul>
<li><a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}>xlsx.full.min.js</a></li>
<li><a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js`}>shim.min.js</a></li>
<li><a href="https://sheetjs.com/pres.xlsx">pres.xlsx</a></li>
</ul>
<CodeBlock language="bash">{`\
curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js
curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
curl -LO https://sheetjs.com/pres.xlsx`}
</CodeBlock>
6) Replace `Program.cs` with the following:
```csharp title="Program.cs"
var engine = new Jint.Engine();
engine.Evaluate("global = globalThis;");
engine.Evaluate(File.ReadAllText("shim.min.js"));
engine.Evaluate(File.ReadAllText("xlsx.full.min.js"));
Console.WriteLine("SheetJS version {0}", engine.Evaluate("XLSX.version"));
```
After saving, run the program:
```bash
dotnet run
```
<p>The terminal should display <code>SheetJS version {current}</code></p>
### Read and Write Files
7) Replace `Program.cs` with the following:
```csharp title="Program.cs"
using Jint;
/* Initialize Jint */
var engine = new Jint.Engine();
engine.Evaluate("global = globalThis;");
/* Load SheetJS Scripts */
engine.Evaluate(File.ReadAllText("shim.min.js"));
engine.Evaluate(File.ReadAllText("xlsx.full.min.js"));
Console.WriteLine("SheetJS version {0}", engine.Evaluate("XLSX.version"));
/* Read and Parse File */
byte[] filedata = File.ReadAllBytes(args[0]);
Jint.Native.JsValue u8 = engine.Realm.Intrinsics.Uint8Array.Construct(filedata);
engine.SetValue("buf", u8);
engine.Evaluate("var wb = XLSX.read(buf);");
/* Print CSV of first worksheet*/
engine.Evaluate("var ws = wb.Sheets[wb.SheetNames[0]];");
Jint.Native.JsValue csv = engine.Evaluate("XLSX.utils.sheet_to_csv(ws)");
Console.Write(csv);
/* Generate XLSB file and save to SheetJSJint.xlsb */
Jint.Native.JsValue xlsb = engine.Evaluate("XLSX.write(wb, {bookType: 'xlsb', type: 'buffer'})");
File.WriteAllBytes("SheetJSJint.xlsb", xlsb.AsUint8Array());
```
After saving, run the program and pass the test file name as an argument:
```bash
dotnet run pres.xlsx
```
If successful, the program will print the contents of the first sheet as CSV
rows. It will also create `SheetJSJint.xlsb` which can be opened in Excel or
another spreadsheet editor.
:::caution pass
If the `using Jint;` directive is omitted, the build will fail:
```
'JsValue' does not contain a definition for 'AsUint8Array' and no accessible extension method 'AsUint8Array' accepting a first argument of type 'JsValue' could be found
```
:::
### Standalone Application
8) Find the runtime identifier (RID) for your platform[^6]. The RID values for
tested platforms are listed below:
| Platform | RID |
|:-----------------|:------------|
| Intel Mac | `osx-x64` |
| Windows 10 (x64) | `win10-x64` |
9) Build the standalone application. Replace `$RID` with the real value in:
```bash
dotnet publish -c Release -r $RID --self-contained true -p:PublishSingleFile=true -p:PublishTrimmed=true
```
<details open><summary><b>Tested platforms</b> (click to hide)</summary>
<Tabs groupId="triple">
<TabItem value="darwin-x64" label="Intel Mac">
For Intel Mac, the RID is `osx-x64` and the command is
```bash
dotnet publish -c Release -r osx-x64 --self-contained true -p:PublishSingleFile=true -p:PublishTrimmed=true
```
</TabItem>
<TabItem value="win10-x64" label="Windows x64">
For Windows 10 x64, the RID is `win10-x64` and the command is:
```powershell
dotnet publish -c Release -r win10-x64 --self-contained true -p:PublishSingleFile=true -p:PublishTrimmed=true
```
</TabItem>
</Tabs>
</details>
10) Copy the generated executable to the project directory.
The build from step 9 is placed in `bin\Release\net6.0\$RID\publish` and the
binary name will be `SheetJSJint` or `SheetJSJint.exe` depending on OS.
<details open><summary><b>Tested platforms</b> (click to hide)</summary>
<Tabs groupId="triple">
<TabItem value="darwin-x64" label="Intel Mac">
For Intel Mac, the RID is `osx-x64` and the command is:
```bash
cp bin/Release/net6.0/osx-x64/publish/SheetJSJint .
```
</TabItem>
<TabItem value="win10-x64" label="Windows x64">
For Windows 10 x64, the RID is `win10-x64` and the command is:
```powershell
copy .\bin\Release\net6.0\win10-x64\publish\SheetJSJint.exe .
```
</TabItem>
</Tabs>
</details>
11) Run the generated command.
<Tabs groupId="os">
<TabItem value="unix" label="Linux/MacOS">
```bash
./SheetJSJint pres.xlsx
```
</TabItem>
<TabItem value="win" label="Windows">
```powershell
.\SheetJSJint pres.xlsx
```
</TabItem>
</Tabs>
[^1]: The Jint project recommends the ["MyGet" service](https://www.myget.org/feed/jint/package/nuget/Jint). According to the developers, the ["NuGet" package](https://www.nuget.org/packages/jint) is "occasionally published".
[^2]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^3]: See [`write` in "Writing Files"](/docs/api/write-options)
[^4]: See ["Supported Output Formats" in "Writing Files"](/docs/api/write-options#supported-output-formats) for details on `bookType`
[^5]: At the time of writing, <https://dotnet.microsoft.com/en-us/download> is the official endpoint.
[^6]: See [".NET RID Catalog"](https://learn.microsoft.com/en-us/dotnet/core/rid-catalog) in the .NET documentation

@ -204,6 +204,12 @@ build/bin/jerry xlsx.jerry.js; echo $?
</details>
### Jint
Jint is an embeddable JS engine for .NET written in C#.
This demo has been moved [to a dedicated page](/docs/demos/engines/jint).
### Nashorn