UPDATE: Add node bindings

This commit is contained in:
Nicolás Hatcher
2025-01-23 22:43:45 +01:00
committed by Nicolás Hatcher Andrés
parent ec4e7b1ca3
commit 5d429b1660
48 changed files with 7362 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
[target.aarch64-unknown-linux-musl]
linker = "aarch64-linux-musl-gcc"
rustflags = ["-C", "target-feature=-crt-static"]
[target.x86_64-pc-windows-msvc]
rustflags = ["-C", "target-feature=+crt-static"]

197
bindings/nodejs/.gitignore vendored Normal file
View File

@@ -0,0 +1,197 @@
# Created by https://www.toptal.com/developers/gitignore/api/node
# Edit at https://www.toptal.com/developers/gitignore?templates=node
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# End of https://www.toptal.com/developers/gitignore/api/node
# Created by https://www.toptal.com/developers/gitignore/api/macos
# Edit at https://www.toptal.com/developers/gitignore?templates=macos
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### macOS Patch ###
# iCloud generated files
*.icloud
# End of https://www.toptal.com/developers/gitignore/api/macos
# Created by https://www.toptal.com/developers/gitignore/api/windows
# Edit at https://www.toptal.com/developers/gitignore?templates=windows
### Windows ###
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# End of https://www.toptal.com/developers/gitignore/api/windows
#Added by cargo
/target
Cargo.lock
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
*.node

View File

@@ -0,0 +1,13 @@
target
Cargo.lock
.cargo
.github
npm
.eslintrc
.prettierignore
rustfmt.toml
yarn.lock
*.node
.yarn
__test__
renovate.json

View File

@@ -0,0 +1 @@
nodeLinker: node-modules

View File

@@ -0,0 +1,18 @@
[package]
edition = "2021"
name = "ironcalc_nodejs"
version = "0.3.0"
[lib]
crate-type = ["cdylib"]
[dependencies]
# Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
napi = { version = "2.12.2", default-features = false, features = ["napi4", "serde-json"] }
napi-derive = "2.12.2"
ironcalc = { path = "../../xlsx", version = "0.3.0" }
serde = { version = "1.0", features = ["derive"] }
[build-dependencies]
napi-build = "2.0.1"

18
bindings/nodejs/README.md Normal file
View File

@@ -0,0 +1,18 @@
# IronCalc nodejs bindingds
Example usage:
```javascript
import { Model } from '@ironcalc/wasm';
const model = new Model("Workbook1", "en", "UTC");
model.setUserInput(0, 1, 1, "=1+1");
const result1 = model.getFormattedCellValue(0, 1, 1);
console.log('Cell value', result1);
let result2 = model.getCellStyle(0, 1, 1);
console.log('Cell style', result2);
```

View File

@@ -0,0 +1,11 @@
import test from 'ava'
import { Model } from '../index.js';
test('sum from native', (t) => {
const model = new Model("Workbook1", "en", "UTC");
model.setUserInput(0, 1, 1, "=1+1");
t.is(model.getFormattedCellValue(0, 1, 1), '2');
});

5
bindings/nodejs/build.rs Normal file
View File

@@ -0,0 +1,5 @@
extern crate napi_build;
fn main() {
napi_build::setup();
}

77
bindings/nodejs/index.d.ts vendored Normal file
View File

@@ -0,0 +1,77 @@
/* tslint:disable */
/* eslint-disable */
/* auto-generated by NAPI-RS */
export declare class Model {
constructor(name: string, locale: string, timezone: string)
static fromBytes(bytes: Uint8Array): Model
canUndo(): boolean
canRedo(): boolean
pauseEvaluation(): void
resumeEvaluation(): void
flushSendQueue(): Array<number>
applyExternalDiffs(diffs: Uint8Array): void
getCellContent(sheet: number, row: number, column: number): string
newSheet(): void
deleteSheet(sheet: number): void
hideSheet(sheet: number): void
unhideSheet(sheet: number): void
renameSheet(sheet: number, name: string): void
setSheetColor(sheet: number, color: string): void
rangeClearAll(sheet: number, startRow: number, startColumn: number, endRow: number, endColumn: number): void
rangeClearContents(sheet: number, startRow: number, startColumn: number, endRow: number, endColumn: number): void
insertRow(sheet: number, row: number): void
insertColumn(sheet: number, column: number): void
deleteRow(sheet: number, row: number): void
deleteColumn(sheet: number, column: number): void
setRowHeight(sheet: number, row: number, height: number): void
setColumnWidth(sheet: number, column: number, width: number): void
getRowHeight(sheet: number, row: number): number
getColumnWidth(sheet: number, column: number): number
setUserInput(sheet: number, row: number, column: number, input: string): void
getFormattedCellValue(sheet: number, row: number, column: number): string
getFrozenRowsCount(sheet: number): number
getFrozenColumnsCount(sheet: number): number
setFrozenRowsCount(sheet: number, count: number): void
setFrozenColumnsCount(sheet: number, count: number): void
updateRangeStyle(range: unknown, stylePath: string, value: string): void
getCellStyle(sheet: number, row: number, column: number): unknown
onPasteStyles(styles: unknown): void
getCellType(sheet: number, row: number, column: number): number
getWorksheetsProperties(): unknown
getSelectedSheet(): number
getSelectedCell(): Array<number>
getSelectedView(): unknown
setSelectedSheet(sheet: number): void
setSelectedCell(row: number, column: number): void
setSelectedRange(startRow: number, startColumn: number, endRow: number, endColumn: number): void
setTopLeftVisibleCell(topRow: number, topColumn: number): void
setShowGridLines(sheet: number, showGridLines: boolean): void
getShowGridLines(sheet: number): boolean
autoFillRows(sourceArea: unknown, toRow: number): void
autoFillColumns(sourceArea: unknown, toColumn: number): void
onArrowRight(): void
onArrowLeft(): void
onArrowUp(): void
onArrowDown(): void
onPageDown(): void
onPageUp(): void
setWindowWidth(windowWidth: number): void
setWindowHeight(windowHeight: number): void
getScrollX(): number
getScrollY(): number
onExpandSelectedRange(key: string): void
onAreaSelecting(targetRow: number, targetColumn: number): void
setAreaWithBorder(area: unknown, borderArea: unknown): void
toBytes(): Array<number>
getName(): string
setName(name: string): void
copyToClipboard(): unknown
pasteFromClipboard(sourceSheet: number, sourceRange: unknown, clipboard: unknown, isCut: boolean): void
pasteCsvText(area: unknown, csv: string): void
getDefinedNameList(): unknown
newDefinedName(name: string, scope: number | undefined | null, formula: string): void
updateDefinedName(name: string, scope: number | undefined | null, newName: string, newScope: number | undefined | null, newFormula: string): void
deleteDefinedName(name: string, scope?: number | undefined | null): void
}

315
bindings/nodejs/index.js Normal file
View File

@@ -0,0 +1,315 @@
/* tslint:disable */
/* eslint-disable */
/* prettier-ignore */
/* auto-generated by NAPI-RS */
const { existsSync, readFileSync } = require('fs')
const { join } = require('path')
const { platform, arch } = process
let nativeBinding = null
let localFileExisted = false
let loadError = null
function isMusl() {
// For Node 10
if (!process.report || typeof process.report.getReport !== 'function') {
try {
const lddPath = require('child_process').execSync('which ldd').toString().trim()
return readFileSync(lddPath, 'utf8').includes('musl')
} catch (e) {
return true
}
} else {
const { glibcVersionRuntime } = process.report.getReport().header
return !glibcVersionRuntime
}
}
switch (platform) {
case 'android':
switch (arch) {
case 'arm64':
localFileExisted = existsSync(join(__dirname, 'nodejs.android-arm64.node'))
try {
if (localFileExisted) {
nativeBinding = require('./nodejs.android-arm64.node')
} else {
nativeBinding = require('@ironcalc/nodejs-android-arm64')
}
} catch (e) {
loadError = e
}
break
case 'arm':
localFileExisted = existsSync(join(__dirname, 'nodejs.android-arm-eabi.node'))
try {
if (localFileExisted) {
nativeBinding = require('./nodejs.android-arm-eabi.node')
} else {
nativeBinding = require('@ironcalc/nodejs-android-arm-eabi')
}
} catch (e) {
loadError = e
}
break
default:
throw new Error(`Unsupported architecture on Android ${arch}`)
}
break
case 'win32':
switch (arch) {
case 'x64':
localFileExisted = existsSync(
join(__dirname, 'nodejs.win32-x64-msvc.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./nodejs.win32-x64-msvc.node')
} else {
nativeBinding = require('@ironcalc/nodejs-win32-x64-msvc')
}
} catch (e) {
loadError = e
}
break
case 'ia32':
localFileExisted = existsSync(
join(__dirname, 'nodejs.win32-ia32-msvc.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./nodejs.win32-ia32-msvc.node')
} else {
nativeBinding = require('@ironcalc/nodejs-win32-ia32-msvc')
}
} catch (e) {
loadError = e
}
break
case 'arm64':
localFileExisted = existsSync(
join(__dirname, 'nodejs.win32-arm64-msvc.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./nodejs.win32-arm64-msvc.node')
} else {
nativeBinding = require('@ironcalc/nodejs-win32-arm64-msvc')
}
} catch (e) {
loadError = e
}
break
default:
throw new Error(`Unsupported architecture on Windows: ${arch}`)
}
break
case 'darwin':
localFileExisted = existsSync(join(__dirname, 'nodejs.darwin-universal.node'))
try {
if (localFileExisted) {
nativeBinding = require('./nodejs.darwin-universal.node')
} else {
nativeBinding = require('@ironcalc/nodejs-darwin-universal')
}
break
} catch {}
switch (arch) {
case 'x64':
localFileExisted = existsSync(join(__dirname, 'nodejs.darwin-x64.node'))
try {
if (localFileExisted) {
nativeBinding = require('./nodejs.darwin-x64.node')
} else {
nativeBinding = require('@ironcalc/nodejs-darwin-x64')
}
} catch (e) {
loadError = e
}
break
case 'arm64':
localFileExisted = existsSync(
join(__dirname, 'nodejs.darwin-arm64.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./nodejs.darwin-arm64.node')
} else {
nativeBinding = require('@ironcalc/nodejs-darwin-arm64')
}
} catch (e) {
loadError = e
}
break
default:
throw new Error(`Unsupported architecture on macOS: ${arch}`)
}
break
case 'freebsd':
if (arch !== 'x64') {
throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
}
localFileExisted = existsSync(join(__dirname, 'nodejs.freebsd-x64.node'))
try {
if (localFileExisted) {
nativeBinding = require('./nodejs.freebsd-x64.node')
} else {
nativeBinding = require('@ironcalc/nodejs-freebsd-x64')
}
} catch (e) {
loadError = e
}
break
case 'linux':
switch (arch) {
case 'x64':
if (isMusl()) {
localFileExisted = existsSync(
join(__dirname, 'nodejs.linux-x64-musl.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./nodejs.linux-x64-musl.node')
} else {
nativeBinding = require('@ironcalc/nodejs-linux-x64-musl')
}
} catch (e) {
loadError = e
}
} else {
localFileExisted = existsSync(
join(__dirname, 'nodejs.linux-x64-gnu.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./nodejs.linux-x64-gnu.node')
} else {
nativeBinding = require('@ironcalc/nodejs-linux-x64-gnu')
}
} catch (e) {
loadError = e
}
}
break
case 'arm64':
if (isMusl()) {
localFileExisted = existsSync(
join(__dirname, 'nodejs.linux-arm64-musl.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./nodejs.linux-arm64-musl.node')
} else {
nativeBinding = require('@ironcalc/nodejs-linux-arm64-musl')
}
} catch (e) {
loadError = e
}
} else {
localFileExisted = existsSync(
join(__dirname, 'nodejs.linux-arm64-gnu.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./nodejs.linux-arm64-gnu.node')
} else {
nativeBinding = require('@ironcalc/nodejs-linux-arm64-gnu')
}
} catch (e) {
loadError = e
}
}
break
case 'arm':
if (isMusl()) {
localFileExisted = existsSync(
join(__dirname, 'nodejs.linux-arm-musleabihf.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./nodejs.linux-arm-musleabihf.node')
} else {
nativeBinding = require('@ironcalc/nodejs-linux-arm-musleabihf')
}
} catch (e) {
loadError = e
}
} else {
localFileExisted = existsSync(
join(__dirname, 'nodejs.linux-arm-gnueabihf.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./nodejs.linux-arm-gnueabihf.node')
} else {
nativeBinding = require('@ironcalc/nodejs-linux-arm-gnueabihf')
}
} catch (e) {
loadError = e
}
}
break
case 'riscv64':
if (isMusl()) {
localFileExisted = existsSync(
join(__dirname, 'nodejs.linux-riscv64-musl.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./nodejs.linux-riscv64-musl.node')
} else {
nativeBinding = require('@ironcalc/nodejs-linux-riscv64-musl')
}
} catch (e) {
loadError = e
}
} else {
localFileExisted = existsSync(
join(__dirname, 'nodejs.linux-riscv64-gnu.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./nodejs.linux-riscv64-gnu.node')
} else {
nativeBinding = require('@ironcalc/nodejs-linux-riscv64-gnu')
}
} catch (e) {
loadError = e
}
}
break
case 's390x':
localFileExisted = existsSync(
join(__dirname, 'nodejs.linux-s390x-gnu.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./nodejs.linux-s390x-gnu.node')
} else {
nativeBinding = require('@ironcalc/nodejs-linux-s390x-gnu')
}
} catch (e) {
loadError = e
}
break
default:
throw new Error(`Unsupported architecture on Linux: ${arch}`)
}
break
default:
throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
}
if (!nativeBinding) {
if (loadError) {
throw loadError
}
throw new Error(`Failed to load native binding`)
}
const { Model } = nativeBinding
module.exports.Model = Model

11
bindings/nodejs/main.mjs Normal file
View File

@@ -0,0 +1,11 @@
import { Model } from './index.js'
const model = new Model("Workbook1", "en", "UTC");
model.setUserInput(0, 1, 1, "=1+1");
let t = model.getFormattedCellValue(0, 1, 1);
console.log('From native', t);
let t2 = model.getCellStyle(0, 1, 1);
console.log('From native', t2);

View File

@@ -0,0 +1,3 @@
# `@ironcalc/nodejs-android-arm-eabi`
This is the **armv7-linux-androideabi** binary for `@ironcalc/nodejs`

View File

@@ -0,0 +1,18 @@
{
"name": "@ironcalc/nodejs-android-arm-eabi",
"version": "0.2.0",
"os": [
"android"
],
"cpu": [
"arm"
],
"main": "nodejs.android-arm-eabi.node",
"files": [
"nodejs.android-arm-eabi.node"
],
"license": "MIT",
"engines": {
"node": ">= 10"
}
}

View File

@@ -0,0 +1,3 @@
# `@ironcalc/nodejs-android-arm64`
This is the **aarch64-linux-android** binary for `@ironcalc/nodejs`

View File

@@ -0,0 +1,18 @@
{
"name": "@ironcalc/nodejs-android-arm64",
"version": "0.2.0",
"os": [
"android"
],
"cpu": [
"arm64"
],
"main": "nodejs.android-arm64.node",
"files": [
"nodejs.android-arm64.node"
],
"license": "MIT",
"engines": {
"node": ">= 10"
}
}

View File

@@ -0,0 +1,3 @@
# `@ironcalc/nodejs-darwin-arm64`
This is the **aarch64-apple-darwin** binary for `@ironcalc/nodejs`

View File

@@ -0,0 +1,18 @@
{
"name": "@ironcalc/nodejs-darwin-arm64",
"version": "0.2.0",
"os": [
"darwin"
],
"cpu": [
"arm64"
],
"main": "nodejs.darwin-arm64.node",
"files": [
"nodejs.darwin-arm64.node"
],
"license": "MIT",
"engines": {
"node": ">= 10"
}
}

View File

@@ -0,0 +1,3 @@
# `@ironcalc/nodejs-darwin-universal`
This is the **universal-apple-darwin** binary for `@ironcalc/nodejs`

View File

@@ -0,0 +1,15 @@
{
"name": "@ironcalc/nodejs-darwin-universal",
"version": "0.2.0",
"os": [
"darwin"
],
"main": "nodejs.darwin-universal.node",
"files": [
"nodejs.darwin-universal.node"
],
"license": "MIT",
"engines": {
"node": ">= 10"
}
}

View File

@@ -0,0 +1,3 @@
# `@ironcalc/nodejs-darwin-x64`
This is the **x86_64-apple-darwin** binary for `@ironcalc/nodejs`

View File

@@ -0,0 +1,18 @@
{
"name": "@ironcalc/nodejs-darwin-x64",
"version": "0.2.0",
"os": [
"darwin"
],
"cpu": [
"x64"
],
"main": "nodejs.darwin-x64.node",
"files": [
"nodejs.darwin-x64.node"
],
"license": "MIT",
"engines": {
"node": ">= 10"
}
}

View File

@@ -0,0 +1,3 @@
# `@ironcalc/nodejs-linux-arm-gnueabihf`
This is the **armv7-unknown-linux-gnueabihf** binary for `@ironcalc/nodejs`

View File

@@ -0,0 +1,18 @@
{
"name": "@ironcalc/nodejs-linux-arm-gnueabihf",
"version": "0.2.0",
"os": [
"linux"
],
"cpu": [
"arm"
],
"main": "nodejs.linux-arm-gnueabihf.node",
"files": [
"nodejs.linux-arm-gnueabihf.node"
],
"license": "MIT",
"engines": {
"node": ">= 10"
}
}

View File

@@ -0,0 +1,3 @@
# `@ironcalc/nodejs-linux-arm-musleabihf`
This is the **armv7-unknown-linux-musleabihf** binary for `@ironcalc/nodejs`

View File

@@ -0,0 +1,18 @@
{
"name": "@ironcalc/nodejs-linux-arm-musleabihf",
"version": "0.2.0",
"os": [
"linux"
],
"cpu": [
"arm"
],
"main": "nodejs.linux-arm-musleabihf.node",
"files": [
"nodejs.linux-arm-musleabihf.node"
],
"license": "MIT",
"engines": {
"node": ">= 10"
}
}

View File

@@ -0,0 +1,3 @@
# `@ironcalc/nodejs-linux-arm64-gnu`
This is the **aarch64-unknown-linux-gnu** binary for `@ironcalc/nodejs`

View File

@@ -0,0 +1,21 @@
{
"name": "@ironcalc/nodejs-linux-arm64-gnu",
"version": "0.2.0",
"os": [
"linux"
],
"cpu": [
"arm64"
],
"main": "nodejs.linux-arm64-gnu.node",
"files": [
"nodejs.linux-arm64-gnu.node"
],
"license": "MIT",
"engines": {
"node": ">= 10"
},
"libc": [
"glibc"
]
}

View File

@@ -0,0 +1,3 @@
# `@ironcalc/nodejs-linux-arm64-musl`
This is the **aarch64-unknown-linux-musl** binary for `@ironcalc/nodejs`

View File

@@ -0,0 +1,21 @@
{
"name": "@ironcalc/nodejs-linux-arm64-musl",
"version": "0.2.0",
"os": [
"linux"
],
"cpu": [
"arm64"
],
"main": "nodejs.linux-arm64-musl.node",
"files": [
"nodejs.linux-arm64-musl.node"
],
"license": "MIT",
"engines": {
"node": ">= 10"
},
"libc": [
"musl"
]
}

View File

@@ -0,0 +1,3 @@
# `@ironcalc/nodejs-linux-riscv64-gnu`
This is the **riscv64gc-unknown-linux-gnu** binary for `@ironcalc/nodejs`

View File

@@ -0,0 +1,21 @@
{
"name": "@ironcalc/nodejs-linux-riscv64-gnu",
"version": "0.2.0",
"os": [
"linux"
],
"cpu": [
"riscv64"
],
"main": "nodejs.linux-riscv64-gnu.node",
"files": [
"nodejs.linux-riscv64-gnu.node"
],
"license": "MIT",
"engines": {
"node": ">= 10"
},
"libc": [
"glibc"
]
}

View File

@@ -0,0 +1,3 @@
# `@ironcalc/nodejs-linux-x64-gnu`
This is the **x86_64-unknown-linux-gnu** binary for `@ironcalc/nodejs`

View File

@@ -0,0 +1,21 @@
{
"name": "@ironcalc/nodejs-linux-x64-gnu",
"version": "0.2.0",
"os": [
"linux"
],
"cpu": [
"x64"
],
"main": "nodejs.linux-x64-gnu.node",
"files": [
"nodejs.linux-x64-gnu.node"
],
"license": "MIT",
"engines": {
"node": ">= 10"
},
"libc": [
"glibc"
]
}

View File

@@ -0,0 +1,3 @@
# `@ironcalc/nodejs-linux-x64-musl`
This is the **x86_64-unknown-linux-musl** binary for `@ironcalc/nodejs`

View File

@@ -0,0 +1,21 @@
{
"name": "@ironcalc/nodejs-linux-x64-musl",
"version": "0.2.0",
"os": [
"linux"
],
"cpu": [
"x64"
],
"main": "nodejs.linux-x64-musl.node",
"files": [
"nodejs.linux-x64-musl.node"
],
"license": "MIT",
"engines": {
"node": ">= 10"
},
"libc": [
"musl"
]
}

View File

@@ -0,0 +1,3 @@
# `@ironcalc/nodejs-win32-arm64-msvc`
This is the **aarch64-pc-windows-msvc** binary for `@ironcalc/nodejs`

View File

@@ -0,0 +1,18 @@
{
"name": "@ironcalc/nodejs-win32-arm64-msvc",
"version": "0.2.0",
"os": [
"win32"
],
"cpu": [
"arm64"
],
"main": "nodejs.win32-arm64-msvc.node",
"files": [
"nodejs.win32-arm64-msvc.node"
],
"license": "MIT",
"engines": {
"node": ">= 10"
}
}

View File

@@ -0,0 +1,3 @@
# `@ironcalc/nodejs-win32-x64-msvc`
This is the **x86_64-pc-windows-msvc** binary for `@ironcalc/nodejs`

View File

@@ -0,0 +1,18 @@
{
"name": "@ironcalc/nodejs-win32-x64-msvc",
"version": "0.2.0",
"os": [
"win32"
],
"cpu": [
"x64"
],
"main": "nodejs.win32-x64-msvc.node",
"files": [
"nodejs.win32-x64-msvc.node"
],
"license": "MIT",
"engines": {
"node": ">= 10"
}
}

2215
bindings/nodejs/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,44 @@
{
"name": "@ironcalc/nodejs",
"version": "0.3.0",
"main": "index.js",
"types": "index.d.ts",
"napi": {
"name": "nodejs",
"triples": {
"additional": [
"aarch64-apple-darwin",
"aarch64-linux-android",
"aarch64-unknown-linux-gnu",
"aarch64-unknown-linux-musl",
"aarch64-pc-windows-msvc",
"armv7-unknown-linux-gnueabihf",
"armv7-unknown-linux-musleabihf",
"x86_64-unknown-linux-musl",
"armv7-linux-androideabi",
"universal-apple-darwin",
"riscv64gc-unknown-linux-gnu"
]
}
},
"license": "MIT",
"devDependencies": {
"@napi-rs/cli": "^2.18.4",
"ava": "^6.0.1"
},
"ava": {
"timeout": "3m"
},
"engines": {
"node": ">= 10"
},
"scripts": {
"artifacts": "napi artifacts",
"build": "napi build --platform --release",
"build:debug": "napi build --platform",
"prepublishOnly": "napi prepublish -t npm",
"test": "ava",
"universal": "napi universal",
"version": "napi version"
}
}

1359
bindings/nodejs/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
tab_spaces = 2
edition = "2021"

623
bindings/nodejs/src/lib.rs Normal file
View File

@@ -0,0 +1,623 @@
#![deny(clippy::all)]
use serde::Serialize;
#[macro_use]
extern crate napi_derive;
use napi::{self, bindgen_prelude::*, JsUnknown, Result};
use ironcalc::base::{
expressions::types::Area,
types::{CellType, Style},
BorderArea, ClipboardData, UserModel as BaseModel,
};
#[derive(Serialize)]
struct DefinedName {
name: String,
scope: Option<u32>,
formula: String,
}
fn to_js_error(error: String) -> Error {
Error::new(Status::Unknown, error)
}
#[napi]
pub struct Model {
model: BaseModel,
}
#[napi]
impl Model {
#[napi(constructor)]
pub fn new(name: String, locale: String, timezone: String) -> Result<Self> {
let model = BaseModel::new_empty(&name, &locale, &timezone).map_err(to_js_error)?;
Ok(Self { model })
}
#[napi(factory)]
pub fn from_bytes(bytes: &[u8]) -> Result<Model> {
let model = BaseModel::from_bytes(bytes).map_err(to_js_error)?;
Ok(Model { model })
}
pub fn undo(&mut self) -> Result<()> {
self.model.undo().map_err(to_js_error)
}
pub fn redo(&mut self) -> Result<()> {
self.model.redo().map_err(to_js_error)
}
#[napi(js_name = "canUndo")]
pub fn can_undo(&self) -> bool {
self.model.can_undo()
}
#[napi(js_name = "canRedo")]
pub fn can_redo(&self) -> bool {
self.model.can_redo()
}
#[napi(js_name = "pauseEvaluation")]
pub fn pause_evaluation(&mut self) {
self.model.pause_evaluation()
}
#[napi(js_name = "resumeEvaluation")]
pub fn resume_evaluation(&mut self) {
self.model.resume_evaluation()
}
pub fn evaluate(&mut self) {
self.model.evaluate();
}
#[napi(js_name = "flushSendQueue")]
pub fn flush_send_queue(&mut self) -> Vec<u8> {
self.model.flush_send_queue()
}
#[napi(js_name = "applyExternalDiffs")]
pub fn apply_external_diffs(&mut self, diffs: &[u8]) -> Result<()> {
self.model.apply_external_diffs(diffs).map_err(to_js_error)
}
#[napi(js_name = "getCellContent")]
pub fn get_cell_content(&self, sheet: u32, row: i32, column: i32) -> Result<String> {
self
.model
.get_cell_content(sheet, row, column)
.map_err(to_js_error)
}
#[napi(js_name = "newSheet")]
pub fn new_sheet(&mut self) -> Result<()> {
self.model.new_sheet().map_err(to_js_error)
}
#[napi(js_name = "deleteSheet")]
pub fn delete_sheet(&mut self, sheet: u32) -> Result<()> {
self.model.delete_sheet(sheet).map_err(to_js_error)
}
#[napi(js_name = "hideSheet")]
pub fn hide_sheet(&mut self, sheet: u32) -> Result<()> {
self.model.hide_sheet(sheet).map_err(to_js_error)
}
#[napi(js_name = "unhideSheet")]
pub fn unhide_sheet(&mut self, sheet: u32) -> Result<()> {
self.model.unhide_sheet(sheet).map_err(to_js_error)
}
#[napi(js_name = "renameSheet")]
pub fn rename_sheet(&mut self, sheet: u32, name: String) -> Result<()> {
self.model.rename_sheet(sheet, &name).map_err(to_js_error)
}
#[napi(js_name = "setSheetColor")]
pub fn set_sheet_color(&mut self, sheet: u32, color: String) -> Result<()> {
self
.model
.set_sheet_color(sheet, &color)
.map_err(to_js_error)
}
#[napi(js_name = "rangeClearAll")]
pub fn range_clear_all(
&mut self,
sheet: u32,
start_row: i32,
start_column: i32,
end_row: i32,
end_column: i32,
) -> Result<()> {
let range = Area {
sheet,
row: start_row,
column: start_column,
width: end_column - start_column + 1,
height: end_row - start_row + 1,
};
self.model.range_clear_all(&range).map_err(to_js_error)
}
#[napi(js_name = "rangeClearContents")]
pub fn range_clear_contents(
&mut self,
sheet: u32,
start_row: i32,
start_column: i32,
end_row: i32,
end_column: i32,
) -> Result<()> {
let range = Area {
sheet,
row: start_row,
column: start_column,
width: end_column - start_column + 1,
height: end_row - start_row + 1,
};
self.model.range_clear_contents(&range).map_err(to_js_error)
}
#[napi(js_name = "insertRow")]
pub fn insert_row(&mut self, sheet: u32, row: i32) -> Result<()> {
self.model.insert_row(sheet, row).map_err(to_js_error)
}
#[napi(js_name = "insertColumn")]
pub fn insert_column(&mut self, sheet: u32, column: i32) -> Result<()> {
self.model.insert_column(sheet, column).map_err(to_js_error)
}
#[napi(js_name = "deleteRow")]
pub fn delete_row(&mut self, sheet: u32, row: i32) -> Result<()> {
self.model.delete_row(sheet, row).map_err(to_js_error)
}
#[napi(js_name = "deleteColumn")]
pub fn delete_column(&mut self, sheet: u32, column: i32) -> Result<()> {
self.model.delete_column(sheet, column).map_err(to_js_error)
}
#[napi(js_name = "setRowHeight")]
pub fn set_row_height(&mut self, sheet: u32, row: i32, height: f64) -> Result<()> {
self
.model
.set_row_height(sheet, row, height)
.map_err(to_js_error)
}
#[napi(js_name = "setColumnWidth")]
pub fn set_column_width(&mut self, sheet: u32, column: i32, width: f64) -> Result<()> {
self
.model
.set_column_width(sheet, column, width)
.map_err(to_js_error)
}
#[napi(js_name = "getRowHeight")]
pub fn get_row_height(&mut self, sheet: u32, row: i32) -> Result<f64> {
self.model.get_row_height(sheet, row).map_err(to_js_error)
}
#[napi(js_name = "getColumnWidth")]
pub fn get_column_width(&mut self, sheet: u32, column: i32) -> Result<f64> {
self
.model
.get_column_width(sheet, column)
.map_err(to_js_error)
}
#[napi(js_name = "setUserInput")]
pub fn set_user_input(&mut self, sheet: u32, row: i32, column: i32, input: String) -> Result<()> {
self
.model
.set_user_input(sheet, row, column, &input)
.map_err(to_js_error)
}
#[napi(js_name = "getFormattedCellValue")]
pub fn get_formatted_cell_value(&self, sheet: u32, row: i32, column: i32) -> Result<String> {
self
.model
.get_formatted_cell_value(sheet, row, column)
.map_err(to_js_error)
}
#[napi(js_name = "getFrozenRowsCount")]
pub fn get_frozen_rows_count(&self, sheet: u32) -> Result<i32> {
self.model.get_frozen_rows_count(sheet).map_err(to_js_error)
}
#[napi(js_name = "getFrozenColumnsCount")]
pub fn get_frozen_columns_count(&self, sheet: u32) -> Result<i32> {
self
.model
.get_frozen_columns_count(sheet)
.map_err(to_js_error)
}
#[napi(js_name = "setFrozenRowsCount")]
pub fn set_frozen_rows_count(&mut self, sheet: u32, count: i32) -> Result<()> {
self
.model
.set_frozen_rows_count(sheet, count)
.map_err(to_js_error)
}
#[napi(js_name = "setFrozenColumnsCount")]
pub fn set_frozen_columns_count(&mut self, sheet: u32, count: i32) -> Result<()> {
self
.model
.set_frozen_columns_count(sheet, count)
.map_err(to_js_error)
}
#[napi(js_name = "updateRangeStyle")]
pub fn update_range_style(
&mut self,
env: Env,
range: JsUnknown,
style_path: String,
value: String,
) -> Result<()> {
let range: Area = env
.from_js_value(range)
.map_err(|e| to_js_error(e.to_string()))?;
self
.model
.update_range_style(&range, &style_path, &value)
.map_err(to_js_error)
}
#[napi(js_name = "getCellStyle")]
pub fn get_cell_style(
&mut self,
env: Env,
sheet: u32,
row: i32,
column: i32,
) -> Result<JsUnknown> {
let style = self
.model
.get_cell_style(sheet, row, column)
.map_err(to_js_error)?;
env
.to_js_value(&style)
.map_err(|e| to_js_error(e.to_string()))
}
#[napi(js_name = "onPasteStyles")]
pub fn on_paste_styles(&mut self, env: Env, styles: JsUnknown) -> Result<()> {
let styles: &Vec<Vec<Style>> = &env
.from_js_value(styles)
.map_err(|e| to_js_error(e.to_string()))?;
self.model.on_paste_styles(styles).map_err(to_js_error)
}
#[napi(js_name = "getCellType")]
pub fn get_cell_type(&self, sheet: u32, row: i32, column: i32) -> Result<i32> {
Ok(
match self
.model
.get_cell_type(sheet, row, column)
.map_err(to_js_error)?
{
CellType::Number => 1,
CellType::Text => 2,
CellType::LogicalValue => 4,
CellType::ErrorValue => 16,
CellType::Array => 64,
CellType::CompoundData => 128,
},
)
}
// I don't _think_ serializing to JsUnknown can't fail
// FIXME: Remove this clippy directive
#[napi(js_name = "getWorksheetsProperties")]
#[allow(clippy::unwrap_used)]
pub fn get_worksheets_properties(&self, env: Env) -> JsUnknown {
env
.to_js_value(&self.model.get_worksheets_properties())
.unwrap()
}
#[napi(js_name = "getSelectedSheet")]
pub fn get_selected_sheet(&self) -> u32 {
self.model.get_selected_sheet()
}
#[napi(js_name = "getSelectedCell")]
pub fn get_selected_cell(&self) -> Vec<i32> {
let (sheet, row, column) = self.model.get_selected_cell();
vec![sheet as i32, row, column]
}
// I don't _think_ serializing to JsUnknown can't fail
// FIXME: Remove this clippy directive
#[napi(js_name = "getSelectedView")]
#[allow(clippy::unwrap_used)]
pub fn get_selected_view(&self, env: Env) -> JsUnknown {
env.to_js_value(&self.model.get_selected_view()).unwrap()
}
#[napi(js_name = "setSelectedSheet")]
pub fn set_selected_sheet(&mut self, sheet: u32) -> Result<()> {
self.model.set_selected_sheet(sheet).map_err(to_js_error)
}
#[napi(js_name = "setSelectedCell")]
pub fn set_selected_cell(&mut self, row: i32, column: i32) -> Result<()> {
self
.model
.set_selected_cell(row, column)
.map_err(to_js_error)
}
#[napi(js_name = "setSelectedRange")]
pub fn set_selected_range(
&mut self,
start_row: i32,
start_column: i32,
end_row: i32,
end_column: i32,
) -> Result<()> {
self
.model
.set_selected_range(start_row, start_column, end_row, end_column)
.map_err(to_js_error)
}
#[napi(js_name = "setTopLeftVisibleCell")]
pub fn set_top_left_visible_cell(&mut self, top_row: i32, top_column: i32) -> Result<()> {
self
.model
.set_top_left_visible_cell(top_row, top_column)
.map_err(to_js_error)
}
#[napi(js_name = "setShowGridLines")]
pub fn set_show_grid_lines(&mut self, sheet: u32, show_grid_lines: bool) -> Result<()> {
self
.model
.set_show_grid_lines(sheet, show_grid_lines)
.map_err(to_js_error)
}
#[napi(js_name = "getShowGridLines")]
pub fn get_show_grid_lines(&mut self, sheet: u32) -> Result<bool> {
self.model.get_show_grid_lines(sheet).map_err(to_js_error)
}
#[napi(js_name = "autoFillRows")]
pub fn auto_fill_rows(&mut self, env: Env, source_area: JsUnknown, to_row: i32) -> Result<()> {
let area: Area = env
.from_js_value(source_area)
.map_err(|e| to_js_error(e.to_string()))?;
self
.model
.auto_fill_rows(&area, to_row)
.map_err(to_js_error)
}
#[napi(js_name = "autoFillColumns")]
pub fn auto_fill_columns(
&mut self,
env: Env,
source_area: JsUnknown,
to_column: i32,
) -> Result<()> {
let area: Area = env
.from_js_value(source_area)
.map_err(|e| to_js_error(e.to_string()))?;
self
.model
.auto_fill_columns(&area, to_column)
.map_err(to_js_error)
}
#[napi(js_name = "onArrowRight")]
pub fn on_arrow_right(&mut self) -> Result<()> {
self.model.on_arrow_right().map_err(to_js_error)
}
#[napi(js_name = "onArrowLeft")]
pub fn on_arrow_left(&mut self) -> Result<()> {
self.model.on_arrow_left().map_err(to_js_error)
}
#[napi(js_name = "onArrowUp")]
pub fn on_arrow_up(&mut self) -> Result<()> {
self.model.on_arrow_up().map_err(to_js_error)
}
#[napi(js_name = "onArrowDown")]
pub fn on_arrow_down(&mut self) -> Result<()> {
self.model.on_arrow_down().map_err(to_js_error)
}
#[napi(js_name = "onPageDown")]
pub fn on_page_down(&mut self) -> Result<()> {
self.model.on_page_down().map_err(to_js_error)
}
#[napi(js_name = "onPageUp")]
pub fn on_page_up(&mut self) -> Result<()> {
self.model.on_page_up().map_err(to_js_error)
}
#[napi(js_name = "setWindowWidth")]
pub fn set_window_width(&mut self, window_width: f64) {
self.model.set_window_width(window_width);
}
#[napi(js_name = "setWindowHeight")]
pub fn set_window_height(&mut self, window_height: f64) {
self.model.set_window_height(window_height);
}
#[napi(js_name = "getScrollX")]
pub fn get_scroll_x(&self) -> Result<f64> {
self.model.get_scroll_x().map_err(to_js_error)
}
#[napi(js_name = "getScrollY")]
pub fn get_scroll_y(&self) -> Result<f64> {
self.model.get_scroll_y().map_err(to_js_error)
}
#[napi(js_name = "onExpandSelectedRange")]
pub fn on_expand_selected_range(&mut self, key: String) -> Result<()> {
self
.model
.on_expand_selected_range(&key)
.map_err(to_js_error)
}
#[napi(js_name = "onAreaSelecting")]
pub fn on_area_selecting(&mut self, target_row: i32, target_column: i32) -> Result<()> {
self
.model
.on_area_selecting(target_row, target_column)
.map_err(to_js_error)
}
#[napi(js_name = "setAreaWithBorder")]
pub fn set_area_with_border(
&mut self,
env: Env,
area: JsUnknown,
border_area: JsUnknown,
) -> Result<()> {
let range: Area = env
.from_js_value(area)
.map_err(|e| to_js_error(e.to_string()))?;
let border: BorderArea = env
.from_js_value(border_area)
.map_err(|e| to_js_error(e.to_string()))?;
self
.model
.set_area_with_border(&range, &border)
.map_err(|e| to_js_error(e.to_string()))?;
Ok(())
}
#[napi(js_name = "toBytes")]
pub fn to_bytes(&self) -> Vec<u8> {
self.model.to_bytes()
}
#[napi(js_name = "getName")]
pub fn get_name(&self) -> String {
self.model.get_name()
}
#[napi(js_name = "setName")]
pub fn set_name(&mut self, name: String) {
self.model.set_name(&name);
}
#[napi(js_name = "copyToClipboard")]
pub fn copy_to_clipboard(&self, env: Env) -> Result<JsUnknown> {
let data = self
.model
.copy_to_clipboard()
.map_err(|e| to_js_error(e.to_string()))?;
env
.to_js_value(&data)
.map_err(|e| to_js_error(e.to_string()))
}
#[napi(js_name = "pasteFromClipboard")]
pub fn paste_from_clipboard(
&mut self,
env: Env,
source_sheet: u32,
source_range: JsUnknown,
clipboard: JsUnknown,
is_cut: bool,
) -> Result<()> {
let source_range: (i32, i32, i32, i32) = env
.from_js_value(source_range)
.map_err(|e| to_js_error(e.to_string()))?;
let clipboard: ClipboardData = env
.from_js_value(clipboard)
.map_err(|e| to_js_error(e.to_string()))?;
self
.model
.paste_from_clipboard(source_sheet, source_range, &clipboard, is_cut)
.map_err(|e| to_js_error(e.to_string()))
}
#[napi(js_name = "pasteCsvText")]
pub fn paste_csv_string(&mut self, env: Env, area: JsUnknown, csv: String) -> Result<()> {
let range: Area = env
.from_js_value(area)
.map_err(|e| to_js_error(e.to_string()))?;
self
.model
.paste_csv_string(&range, &csv)
.map_err(|e| to_js_error(e.to_string()))
}
#[napi(js_name = "getDefinedNameList")]
pub fn get_defined_name_list(&self, env: Env) -> Result<JsUnknown> {
let data: Vec<DefinedName> = self
.model
.get_defined_name_list()
.iter()
.map(|s| DefinedName {
name: s.0.to_owned(),
scope: s.1,
formula: s.2.to_owned(),
})
.collect();
env
.to_js_value(&data)
.map_err(|e| to_js_error(e.to_string()))
}
#[napi(js_name = "newDefinedName")]
pub fn new_defined_name(
&mut self,
name: String,
scope: Option<u32>,
formula: String,
) -> Result<()> {
self
.model
.new_defined_name(&name, scope, &formula)
.map_err(|e| to_js_error(e.to_string()))
}
#[napi(js_name = "updateDefinedName")]
pub fn update_defined_name(
&mut self,
name: String,
scope: Option<u32>,
new_name: String,
new_scope: Option<u32>,
new_formula: String,
) -> Result<()> {
self
.model
.update_defined_name(&name, scope, &new_name, new_scope, &new_formula)
.map_err(|e| to_js_error(e.to_string()))
}
#[napi(js_name = "deleteDefinedName")]
pub fn delete_definedname(&mut self, name: String, scope: Option<u32>) -> Result<()> {
self
.model
.delete_defined_name(&name, scope)
.map_err(|e| to_js_error(e.to_string()))
}
}

1575
bindings/nodejs/yarn.lock Normal file

File diff suppressed because it is too large Load Diff