@thi.ng/wasm-api-bindgen
Advanced tools
Comparing version 0.5.13 to 0.6.0
20
api.d.ts
@@ -202,2 +202,20 @@ import type { BigType, FloatType, Fn, Fn2, IObjectOf, NumOrString } from "@thi.ng/api"; | ||
skip?: boolean; | ||
/** | ||
* If false, omits getter for languages which would usually define one (e.g. | ||
* for TypeScript). Useful to avoid obsolete code and reduce file size. | ||
* | ||
* @defaultValue true | ||
*/ | ||
getter?: boolean; | ||
/** | ||
* If false, omits setter for languages which would usually define one (e.g. | ||
* for TypeScript). Useful to avoid obsolete code and reduce file size. | ||
* | ||
* @remarks | ||
* If a field has its setter disabled, the generated TypeScript interface | ||
* will declare this field as `readonly`. | ||
* | ||
* @defaultValue true | ||
*/ | ||
setter?: boolean; | ||
} | ||
@@ -358,3 +376,5 @@ export interface Enum extends TopLevelType { | ||
usize: "u32" | "u64"; | ||
bits: number; | ||
sizeBytes: number; | ||
shift: number; | ||
} | ||
@@ -361,0 +381,0 @@ /** |
@@ -5,3 +5,5 @@ const PKG_NAME = "@thi.ng/wasm-api-bindgen"; | ||
usize: "u32", | ||
sizeBytes: 4 | ||
bits: 32, | ||
sizeBytes: 4, | ||
shift: 2 | ||
}; | ||
@@ -11,3 +13,5 @@ const WASM64 = { | ||
usize: "u64", | ||
sizeBytes: 8 | ||
bits: 64, | ||
sizeBytes: 8, | ||
shift: 3 | ||
}; | ||
@@ -14,0 +18,0 @@ export { |
# Change Log | ||
- **Last updated**: 2024-08-10T15:03:07Z | ||
- **Last updated**: 2024-08-18T14:11:34Z | ||
- **Generator**: [thi.ng/monopub](https://thi.ng/monopub) | ||
@@ -12,2 +12,21 @@ | ||
## [0.6.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/wasm-api-bindgen@0.6.0) (2024-08-18) | ||
#### 🚀 Features | ||
- update TS codegen to produce more compact results ([ee5e283](https://github.com/thi-ng/umbrella/commit/ee5e283)) | ||
- update array & slice handling to re-use new helpers in [@thi.ng/wasm-api](https://github.com/thi-ng/umbrella/tree/main/packages/wasm-api) | ||
- update test snapshots | ||
- add getter/setter field flags ([6e87558](https://github.com/thi-ng/umbrella/commit/6e87558)) | ||
- update JSON schema | ||
- update TS codegen to handle optional getters/setters | ||
- add doc strings | ||
#### 🩹 Bug fixes | ||
- fix TS codegen address divisor for prim slices ([dfd66ba](https://github.com/thi-ng/umbrella/commit/dfd66ba)) | ||
- update __primSlice() | ||
- address needs to be divided based on type size | ||
- update test snapshots | ||
### [0.5.5](https://github.com/thi-ng/umbrella/tree/@thi.ng/wasm-api-bindgen@0.5.5) (2024-06-21) | ||
@@ -14,0 +33,0 @@ |
{ | ||
"name": "@thi.ng/wasm-api-bindgen", | ||
"version": "0.5.13", | ||
"version": "0.6.0", | ||
"description": "Polyglot bindings code generators (TS/JS, Zig, C11) for hybrid WebAssembly projects", | ||
@@ -41,15 +41,15 @@ "type": "module", | ||
"dependencies": { | ||
"@thi.ng/api": "^8.11.8", | ||
"@thi.ng/args": "^2.3.41", | ||
"@thi.ng/arrays": "^2.9.14", | ||
"@thi.ng/binary": "^3.4.31", | ||
"@thi.ng/checks": "^3.6.10", | ||
"@thi.ng/compare": "^2.4.0", | ||
"@thi.ng/defmulti": "^3.0.46", | ||
"@thi.ng/errors": "^2.5.14", | ||
"@thi.ng/file-io": "^2.1.10", | ||
"@thi.ng/logger": "^3.0.18", | ||
"@thi.ng/paths": "^5.1.88", | ||
"@thi.ng/strings": "^3.8.3", | ||
"@thi.ng/wasm-api": "^1.6.7" | ||
"@thi.ng/api": "^8.11.9", | ||
"@thi.ng/args": "^2.3.42", | ||
"@thi.ng/arrays": "^2.10.0", | ||
"@thi.ng/binary": "^3.4.32", | ||
"@thi.ng/checks": "^3.6.11", | ||
"@thi.ng/compare": "^2.4.1", | ||
"@thi.ng/defmulti": "^3.0.47", | ||
"@thi.ng/errors": "^2.5.15", | ||
"@thi.ng/file-io": "^2.1.11", | ||
"@thi.ng/logger": "^3.0.19", | ||
"@thi.ng/paths": "^5.1.89", | ||
"@thi.ng/strings": "^3.8.4", | ||
"@thi.ng/wasm-api": "^2.0.0" | ||
}, | ||
@@ -121,6 +121,11 @@ "devDependencies": { | ||
"parent": "@thi.ng/wasm-api", | ||
"status": "alpha", | ||
"related": [ | ||
"wasm-api-canvas", | ||
"wasm-api-dom", | ||
"wasm-api-schedule", | ||
"wasm-api-webgl" | ||
], | ||
"year": 2022 | ||
}, | ||
"gitHead": "e914ebbd81c56783c39cf746548c547cbacadc96\n" | ||
"gitHead": "f6e26ea1142525171de5d36b9c3119f2782bb437\n" | ||
} |
193
README.md
@@ -10,3 +10,3 @@ <!-- This file is generated - DO NOT EDIT! --> | ||
> [!NOTE] | ||
> This is one of 198 standalone projects, maintained as part | ||
> This is one of 199 standalone projects, maintained as part | ||
> of the [@thi.ng/umbrella](https://github.com/thi-ng/umbrella/) monorepo | ||
@@ -32,2 +32,3 @@ > and anti-framework. | ||
- [Padding](#padding) | ||
- [Omitting getters & setters](#omitting-getters--setters) | ||
- [JSON schema for type definitions](#json-schema-for-type-definitions) | ||
@@ -42,4 +43,6 @@ - [CLI generator](#cli-generator) | ||
- [Status](#status) | ||
- [Related packages](#related-packages) | ||
- [Installation](#installation) | ||
- [Dependencies](#dependencies) | ||
- [Usage examples](#usage-examples) | ||
- [API](#api) | ||
@@ -303,2 +306,143 @@ - [Authors](#authors) | ||
### Omitting getters & setters | ||
Depending on the amount and size of the data structures defined, generated code | ||
for JS/TS can grow quite quickly (even though since v0.6.0 the TS codegen has | ||
been much more optimized and the resulting file sizes gone down noticeably). | ||
One of the largest contributing factors to code size is the amount of field | ||
getters and setters. However, in many use cases only getters, setters or neither | ||
of them are required on the TS/JS side and omitting them where possible can lead | ||
to drastic file size savings. | ||
Use the `getter`, `setter` field options to control if relevant code should be | ||
generated for a single field. E.g. If you only intend to read a field on the JS | ||
side, we can omit generating its setter. In general, if a field has no setter | ||
defined, the generated TypeScript interface will mark this field as `readonly`. | ||
Furthermore, the `skip` option can be used to omit code generation of an entire | ||
struct, union or enum for specific languages. | ||
The following example illustrates these concepts: | ||
```json tangle:export/readme-omit.json | ||
[ | ||
{ | ||
"name": "TestOpts", | ||
"type": "struct", | ||
"doc": [ | ||
"Apart from the first field (`a`), all others in this struct ", | ||
"will only be partially generated in TypeScript..." | ||
], | ||
"fields": [ | ||
{ "name": "a", "type": "i32" }, | ||
{ "name": "b", "type": "i32", "getter": false }, | ||
{ "name": "c", "type": "i32", "setter": false }, | ||
{ "name": "d", "type": "TestType", "getter": false, "setter": false } | ||
] | ||
}, | ||
{ | ||
"name": "TestType", | ||
"type": "enum", | ||
"doc": "This enum will not be generated at all for TypeScript", | ||
"values": ["a", "b", "c"], | ||
"skip": ["ts"] | ||
} | ||
] | ||
``` | ||
<details><summary>Generated TypeScript source code</summary> | ||
```ts | ||
/** | ||
* Generated by @thi.ng/wasm-api-bindgen at 2024-08-18T11:18:16.006Z | ||
* DO NOT EDIT! | ||
*/ | ||
// @ts-ignore possibly includes unused imports | ||
import { Pointer, WasmStringPtr, type IWasmMemoryAccess, type MemorySlice, type MemoryView, type WasmType, type WasmTypeBase, type WasmTypeConstructor } from "@thi.ng/wasm-api"; | ||
// @ts-ignore | ||
import { __array, __instanceArray, __slice32, __primslice32 } from "@thi.ng/wasm-api/memory"; | ||
/** | ||
* Apart from the first field (`a`), all others in this struct | ||
* will only be partially generated in TypeScript... | ||
*/ | ||
export interface TestOpts extends WasmTypeBase { | ||
/** | ||
* Zig type: `i32` | ||
*/ | ||
a: number; | ||
/** | ||
* Zig type: `i32` | ||
*/ | ||
b: number; | ||
/** | ||
* Zig type: `i32` | ||
*/ | ||
readonly c: number; | ||
} | ||
export const $TestOpts: WasmTypeConstructor<TestOpts> = (mem) => ({ | ||
get align() { | ||
return 4; | ||
}, | ||
get size() { | ||
return 16; | ||
}, | ||
instanceArray(base, num) { | ||
return __instanceArray<TestOpts>(this, base, num); | ||
}, | ||
instance: (base) => { | ||
return { | ||
get __base() { | ||
return base; | ||
}, | ||
get __bytes() { | ||
return mem.u8.subarray(base, base + 16); | ||
}, | ||
get a(): number { | ||
return mem.i32[base >>> 2]; | ||
}, | ||
set a(x: number) { | ||
mem.i32[base >>> 2] = x; | ||
}, | ||
set b(x: number) { | ||
mem.i32[(base + 4) >>> 2] = x; | ||
}, | ||
get c(): number { | ||
return mem.i32[(base + 8) >>> 2]; | ||
}, | ||
}; | ||
} | ||
}); | ||
``` | ||
</details> | ||
<details><summary>Generated Zig source code</summary> | ||
```zig | ||
//! Generated by @thi.ng/wasm-api-bindgen at 2024-08-18T11:18:12.197Z | ||
//! DO NOT EDIT! | ||
const std = @import("std"); | ||
const bindgen = @import("wasm-api-bindgen"); | ||
/// Apart from the first field (`a`), all others in this struct | ||
/// will only be partially generated in TypeScript... | ||
pub const TestOpts = extern struct { | ||
a: i32, | ||
b: i32, | ||
c: i32, | ||
d: TestType, | ||
}; | ||
/// This enum will not be generated at all for TypeScript | ||
pub const TestType = enum(i32) { | ||
a, | ||
b, | ||
c, | ||
}; | ||
``` | ||
</details> | ||
## JSON schema for type definitions | ||
@@ -336,3 +480,3 @@ | ||
██ █ │ | ||
█ █ █ █ █ █ █ █ │ @thi.ng/wasm-api-bindgen 0.1.0 | ||
█ █ █ █ █ █ █ █ │ @thi.ng/wasm-api-bindgen 0.6.0 | ||
█ █ █ █ █ █ █ █ █ │ Multi-language data bindings code generator | ||
@@ -466,3 +610,3 @@ █ │ | ||
/** | ||
* Generated by @thi.ng/wasm-api-bindgen at 2022-11-24T10:54:36.733Z | ||
* Generated by @thi.ng/wasm-api-bindgen at 2024-08-18T11:20:15.332Z | ||
* DO NOT EDIT! | ||
@@ -472,3 +616,5 @@ */ | ||
// @ts-ignore possibly includes unused imports | ||
import { MemorySlice, Pointer, WasmStringPtr, WasmTypeBase, WasmTypeConstructor } from "@thi.ng/wasm-api"; | ||
import { Pointer, WasmStringPtr, type IWasmMemoryAccess, type MemorySlice, type MemoryView, type WasmType, type WasmTypeBase, type WasmTypeConstructor } from "@thi.ng/wasm-api"; | ||
// @ts-ignore | ||
import { __array, __instanceArray, __slice32, __primslice32 } from "@thi.ng/wasm-api/memory"; | ||
@@ -498,3 +644,3 @@ /** | ||
*/ | ||
pos: Uint16Array; | ||
readonly pos: Uint16Array; | ||
} | ||
@@ -509,2 +655,5 @@ | ||
}, | ||
instanceArray(base, num) { | ||
return __instanceArray<MouseEvent>(this, base, num); | ||
}, | ||
instance: (base) => { | ||
@@ -540,3 +689,3 @@ return { | ||
*/ | ||
key: WasmStringPtr; | ||
readonly key: WasmStringPtr; | ||
/** | ||
@@ -558,2 +707,5 @@ * Bitmask of active modifier keys | ||
}, | ||
instanceArray(base, num) { | ||
return __instanceArray<KeyEvent>(this, base, num); | ||
}, | ||
instance: (base) => { | ||
@@ -599,2 +751,5 @@ let $key: WasmStringPtr | null = null; | ||
}, | ||
instanceArray(base, num) { | ||
return __instanceArray<Event>(this, base, num); | ||
}, | ||
instance: (base) => { | ||
@@ -631,3 +786,3 @@ return { | ||
```zig | ||
//! Generated by @thi.ng/wasm-api-bindgen at 2022-11-24T10:54:36.735Z | ||
//! Generated by @thi.ng/wasm-api-bindgen at 2024-08-18T11:20:28.120Z | ||
//! DO NOT EDIT! | ||
@@ -727,10 +882,18 @@ | ||
value (mem copy). Currently, array, multi-pointers and slices do not provide | ||
write access (from the JS side)... | ||
write access (from the JS side). Also see [omitting | ||
getters/setters](#omitting-getters--setters)... | ||
## Status | ||
**ALPHA** - bleeding edge / work-in-progress | ||
**STABLE** - used in production | ||
[Search or submit any issues for this package](https://github.com/thi-ng/umbrella/issues?q=%5Bwasm-api-bindgen%5D+in%3Atitle) | ||
## Related packages | ||
- [@thi.ng/wasm-api-canvas](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api-canvas) - HTML Canvas2D bridge API for hybrid TypeScript & WASM (Zig) applications | ||
- [@thi.ng/wasm-api-dom](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api-dom) - Browser DOM bridge API for hybrid TypeScript & WASM (Zig) applications | ||
- [@thi.ng/wasm-api-schedule](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api-schedule) - Delayed & scheduled function execution (via setTimeout() etc.) for hybrid WASM apps | ||
- [@thi.ng/wasm-api-webgl](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api-webgl) - WebGL bridge API for hybrid TypeScript & WASM (Zig) applications | ||
## Installation | ||
@@ -762,3 +925,3 @@ | ||
Package sizes (brotli'd, pre-treeshake): ESM: 6.01 KB | ||
Package sizes (brotli'd, pre-treeshake): ESM: 6.07 KB | ||
@@ -783,2 +946,12 @@ ## Dependencies | ||
## Usage examples | ||
One project in this repo's | ||
[/examples](https://github.com/thi-ng/umbrella/tree/develop/examples) | ||
directory is using this package: | ||
| Screenshot | Description | Live demo | Source | | ||
|:---------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------|:----------------------------------------------------|:---------------------------------------------------------------------------------| | ||
| <img src="https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/examples/zig-todo-list.png" width="240"/> | Zig-based To-Do list, DOM creation, local storage task persistence | [Demo](https://demo.thi.ng/umbrella/zig-todo-list/) | [Source](https://github.com/thi-ng/umbrella/tree/develop/examples/zig-todo-list) | | ||
## API | ||
@@ -785,0 +958,0 @@ |
@@ -204,2 +204,12 @@ { | ||
"description": "If true, code generation of this field will be skipped for WASM host environment languages (e.g. TypeScript)" | ||
}, | ||
"getter": { | ||
"type": "boolean", | ||
"default": true, | ||
"description": "If false, omits getter for languages which would usually define one (e.g. TypeScript)" | ||
}, | ||
"setter": { | ||
"type": "boolean", | ||
"default": true, | ||
"description": "If false, omits setter for languages which would usually define one (e.g. TypeScript)" | ||
} | ||
@@ -206,0 +216,0 @@ }, |
@@ -31,3 +31,3 @@ import { | ||
id: "ts", | ||
pre: (_, globalOpts) => { | ||
pre: (coll, globalOpts) => { | ||
const res = [ | ||
@@ -37,4 +37,13 @@ "// @ts-ignore possibly includes unused imports", | ||
globalOpts | ||
)}, type MemorySlice, type WasmTypeBase, type WasmTypeConstructor } from "@thi.ng/wasm-api";` | ||
)}, type IWasmMemoryAccess, type MemorySlice, type MemoryView, type WasmType, type WasmTypeBase, type WasmTypeConstructor } from "@thi.ng/wasm-api";` | ||
]; | ||
if (Object.values(coll).some( | ||
(t) => t.type === "struct" || t.type === "union" | ||
)) { | ||
const bits = globalOpts.target.bits; | ||
res.push( | ||
"// @ts-ignore", | ||
`import { __array, __instanceArray, __slice${bits}, __primslice${bits} } from "@thi.ng/wasm-api/memory";` | ||
); | ||
} | ||
if (opts.pre) res.push("", ...ensureStringArray(opts.pre)); | ||
@@ -66,3 +75,3 @@ return res.join("\n"); | ||
const strType = __stringImpl(opts2); | ||
const fields = struct.fields.map((f) => __generateField(f, coll, opts2)).filter((f) => !!f); | ||
const fields = struct.fields.map((f) => __generateField(f, coll, opts2)).filter((f) => !!f && (f.getter || f.setter)); | ||
const lines = []; | ||
@@ -75,3 +84,4 @@ lines.push( | ||
doc && gen.doc(doc, lines, opts2); | ||
lines.push(`${f.field.name}: ${f.type};`); | ||
const decl = `${f.field.name}: ${f.type};`; | ||
lines.push((!f.setter ? `readonly ` : "") + decl); | ||
} | ||
@@ -95,5 +105,3 @@ injectBody(lines, struct.body?.ts, "decl"); | ||
`instanceArray(base, num) {`, | ||
`const items: ${struct.name}[] = [];`, | ||
`for (; num --> 0; base += ${struct.__size}) items.push(this.instance(base));`, | ||
`return items;`, | ||
`return __instanceArray<${struct.name}>(this, base, num);`, | ||
`},`, | ||
@@ -113,7 +121,9 @@ `instance: (base) => {`, | ||
if (!f) continue; | ||
lines.push( | ||
`get ${f.field.name}(): ${f.type} {`, | ||
...f.getter, | ||
"}," | ||
); | ||
if (f.getter) { | ||
lines.push( | ||
`get ${f.field.name}(): ${f.type} {`, | ||
...f.getter, | ||
"}," | ||
); | ||
} | ||
if (f.setter) { | ||
@@ -154,8 +164,2 @@ lines.push( | ||
const __mem = (type, offset) => `mem.${type}[${__addrShift(offset, type)}]`; | ||
const __mapArray = (f, coll, len = "len") => [ | ||
`const inst = $${f.type}(mem);`, | ||
`const buf: ${f.type}[] = [];`, | ||
`for(let i = 0; i < ${len}; i++) buf.push(inst.instance(addr + i * ${coll[f.type].__size}));`, | ||
`return buf;` | ||
]; | ||
const __mapStringArray = (target, name, type, len, isConst, isLocal = false) => [ | ||
@@ -166,6 +170,2 @@ isLocal ? `const $${name}: ${type}[] = [];` : `$${name} = [];`, | ||
]; | ||
const __primSlice = (type, _, __) => [ | ||
// `const addr = ${__ptrShift(opts.target, offset, type)};`, | ||
`return mem.${type}.subarray(addr, addr + len);` | ||
]; | ||
const __primArray = (type, len, offset) => [ | ||
@@ -205,3 +205,3 @@ `const addr = ${__addrShift(offset, type)};`, | ||
let decl; | ||
let getter = []; | ||
let getter; | ||
let setter; | ||
@@ -304,5 +304,3 @@ let ptrType; | ||
getter = __ptrBody(ptrType, name, offset, [ | ||
`(addr) => {`, | ||
...__mapArray(field, coll, field.len), | ||
"}" | ||
`(addr) => __array(mem, $${field.type}, addr, ${field.len})` | ||
]); | ||
@@ -327,4 +325,3 @@ } | ||
getter = [ | ||
`const addr = ${__addr(offset)};`, | ||
...__mapArray(field, coll, field.len) | ||
`return __array(mem, $${field.type}, ${__addr(offset)}, ${field.len});` | ||
]; | ||
@@ -340,19 +337,18 @@ } | ||
case "enumSlice": | ||
getter = [ | ||
`const addr = ${__ptr(opts.target, offset)};`, | ||
`const len = ${__ptr( | ||
opts.target, | ||
offset + opts.target.sizeBytes | ||
)};` | ||
]; | ||
if (isPrim) { | ||
getter.push(...__primSlice(type, offset, opts)); | ||
getter = [ | ||
`return __primslice${opts.target.bits}(mem, mem.${type}, ${__addr(offset)}, ${__shift(type)});` | ||
]; | ||
type = __arrayType(type); | ||
} else if ($isEnum) { | ||
tag = coll[type].tag; | ||
getter.push(...__primSlice(tag, offset, opts)); | ||
getter = [ | ||
`return __primslice${opts.target.bits}(mem, mem.${tag}, ${__addr(offset)}, ${__shift(tag)});` | ||
]; | ||
type = __arrayType(tag); | ||
} else { | ||
type += "[]"; | ||
getter.push(...__mapArray(field, coll)); | ||
getter = [ | ||
`return __slice${opts.target.bits}(mem, $${field.type}, ${__addr(offset)});` | ||
]; | ||
} | ||
@@ -387,3 +383,9 @@ break; | ||
} | ||
return { field, type, decl, getter, setter }; | ||
return { | ||
field, | ||
type, | ||
decl, | ||
getter: field.getter !== false ? getter : void 0, | ||
setter: field.setter !== false ? setter : void 0 | ||
}; | ||
}; | ||
@@ -390,0 +392,0 @@ export { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
136322
2437
969
+ Added@thi.ng/wasm-api@2.2.5(transitive)
- Removed@thi.ng/wasm-api@1.6.7(transitive)
Updated@thi.ng/api@^8.11.9
Updated@thi.ng/args@^2.3.42
Updated@thi.ng/arrays@^2.10.0
Updated@thi.ng/binary@^3.4.32
Updated@thi.ng/checks@^3.6.11
Updated@thi.ng/compare@^2.4.1
Updated@thi.ng/defmulti@^3.0.47
Updated@thi.ng/errors@^2.5.15
Updated@thi.ng/file-io@^2.1.11
Updated@thi.ng/logger@^3.0.19
Updated@thi.ng/paths@^5.1.89
Updated@thi.ng/strings@^3.8.4
Updated@thi.ng/wasm-api@^2.0.0