@thi.ng/wasm-api 0.5.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/api.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- import type { Fn, Fn2 } from "@thi.ng/api";
1
+ import type { BigType, FloatType, Fn, Fn2 } from "@thi.ng/api";
2
2
  import type { WasmBridge } from "./bridge.js";
3
+ export declare const PKG_NAME = "@thi.ng/wasm-api";
3
4
  export declare type BigIntArray = bigint[] | BigInt64Array | BigUint64Array;
4
5
  /**
5
6
  * Common interface for WASM/JS child APIs which will be used in combination
@@ -60,12 +61,47 @@ export interface WasmExports {
60
61
  * Implementation specific function to free a previously allocated chunk of
61
62
  * of WASM memory (allocated via {@link WasmExports._wasm_allocate}).
62
63
  *
64
+ * @param addr
65
+ * @param numBytes
66
+ */
67
+ _wasm_free(addr: number, numBytes: number): void;
68
+ }
69
+ export interface IWasmMemoryAccess {
70
+ i8: Int8Array;
71
+ u8: Uint8Array;
72
+ i16: Int16Array;
73
+ u16: Uint16Array;
74
+ i32: Int32Array;
75
+ u32: Uint32Array;
76
+ i64: BigInt64Array;
77
+ u64: BigUint64Array;
78
+ f32: Float32Array;
79
+ f64: Float64Array;
80
+ /**
81
+ * Reads UTF-8 encoded string from given address and optional byte length.
82
+ * The default length is 0, which will be interpreted as a zero-terminated
83
+ * string. Returns string.
84
+ *
85
+ * @param addr
86
+ * @param len
87
+ */
88
+ getString(addr: number, len?: number): string;
89
+ /**
90
+ * Encodes given string as UTF-8 and writes it to WASM memory starting at
91
+ * `addr`. By default the string will be zero-terminated and only `maxBytes`
92
+ * will be written. Returns the number of bytes written.
93
+ *
63
94
  * @remarks
64
- * In the supplied Zig bindings (/zig/core.zig) this is a no-op (currently).
95
+ * An error will be thrown if the encoded string doesn't fully fit into the
96
+ * designated memory region (also note that there might need to be space for
97
+ * the additional sentinel/termination byte).
65
98
  *
99
+ * @param str
66
100
  * @param addr
101
+ * @param maxBytes
102
+ * @param terminate
67
103
  */
68
- _wasm_free(addr: number): void;
104
+ setString(str: string, addr: number, maxBytes: number, terminate?: boolean): number;
69
105
  }
70
106
  /**
71
107
  * Core API of WASM imports defined by the {@link WasmBridge}. The same
@@ -100,4 +136,182 @@ export interface CoreAPI extends WebAssembly.ModuleImports {
100
136
  _printStr0: (addr: number) => void;
101
137
  _printStr: (addr: number, len: number) => void;
102
138
  }
139
+ export interface WasmTypeBase {
140
+ /**
141
+ * Base address in linear WASM memory.
142
+ */
143
+ readonly __base: number;
144
+ /**
145
+ * Obtain as byte buffer
146
+ */
147
+ readonly __bytes: Uint8Array;
148
+ }
149
+ export interface WasmType<T> {
150
+ readonly align: number;
151
+ readonly size: number;
152
+ instance: Fn<number, T>;
153
+ }
154
+ export declare type WasmTypeConstructor<T> = Fn<IWasmMemoryAccess, WasmType<T>>;
155
+ export declare type WasmInt = "i8" | "i16" | "i32" | "i64";
156
+ export declare type WasmUint = "u8" | "u16" | "u32" | "u64";
157
+ export declare type WasmFloat = FloatType;
158
+ export declare type WasmPrim = WasmInt | WasmUint | WasmFloat;
159
+ export declare type WasmPrim32 = Exclude<WasmPrim, BigType>;
160
+ export declare type TypeColl = Record<string, TopLevelType>;
161
+ export interface TypeInfo {
162
+ /**
163
+ * Auto-computed size (in bytes)
164
+ *
165
+ * @internal
166
+ */
167
+ __size?: number;
168
+ /**
169
+ * Auto-computed offset (in bytes) in parent struct
170
+ *
171
+ * @internal
172
+ */
173
+ __offset?: number;
174
+ /**
175
+ * Auto-computed alignment (in bytes)
176
+ *
177
+ * @internal
178
+ */
179
+ __align?: number;
180
+ }
181
+ export interface TopLevelType extends TypeInfo {
182
+ /**
183
+ * Type name
184
+ */
185
+ name: string;
186
+ /**
187
+ * Optional (multi-line) docstring for this type
188
+ */
189
+ doc?: string;
190
+ /**
191
+ * Type / kind
192
+ */
193
+ type: "struct" | "enum";
194
+ }
195
+ export interface Struct extends TopLevelType {
196
+ type: "struct";
197
+ /**
198
+ * List of struct fields (might be re-ordered if {@link Struct.auto} is
199
+ * enabled).
200
+ */
201
+ fields: StructField[];
202
+ /**
203
+ * If true, struct fields will be re-ordered in descending order based on
204
+ * their {@link TypeInfo.__align} size. This might result in overall smaller
205
+ * structs due to minimizing inter-field padding.
206
+ *
207
+ * @defaultValue false
208
+ */
209
+ auto?: boolean;
210
+ }
211
+ export interface StructField extends TypeInfo {
212
+ /**
213
+ * Field name (prefix: "__" is reserved)
214
+ */
215
+ name: string;
216
+ /**
217
+ * Field docstring (can be multiline, will be formatted)
218
+ */
219
+ doc?: string;
220
+ /**
221
+ * Field type tag/qualifier (note: `slice` & `vec` are only supported by Zig
222
+ * & TS).
223
+ *
224
+ * @remarks
225
+ * - Array & vector fields are statically sized (using
226
+ * {@link StructField.len})
227
+ * - Pointers are emitted as single-value pointers (where this distinction
228
+ * exist), i.e. even if they're pointing to multiple values, there's no
229
+ * explicit length encoded/available
230
+ * - Zig slices are essentially a pointer w/ associated length
231
+ * - Zig vectors will be processed using SIMD (if enabled in WASM target)
232
+ * and therefore will have stricter (larger) alignment requirements.
233
+ *
234
+ * @defaultValue "scalar"
235
+ */
236
+ tag?: "scalar" | "array" | "ptr" | "slice" | "vec";
237
+ /**
238
+ * Field base type. If not a {@link WasmPrim}, `string` or `opaque`, the
239
+ * value is interpreted as another type name in the {@link TypeColl}.
240
+ *
241
+ * @remarks
242
+ * Please see {@link CodeGenOpts.stringType} and consult package readme for
243
+ * further details re: string handling.
244
+ *
245
+ * TODO `opaque` currently unsupported.
246
+ */
247
+ type: WasmPrim | "string" | "opaque" | string;
248
+ /**
249
+ * TODO currently unsupported & ignored!
250
+ */
251
+ sentinel?: number;
252
+ /**
253
+ * Array or vector length (see {@link StructField.tag})
254
+ */
255
+ len?: number;
256
+ /**
257
+ * TODO currently unsupported & ignored!
258
+ */
259
+ default?: any;
260
+ }
261
+ export interface Enum extends TopLevelType {
262
+ type: "enum";
263
+ /**
264
+ * No i64/u64 support, due to Typescript not supporting bigint enum values
265
+ */
266
+ tag: Exclude<WasmPrim32, FloatType>;
267
+ /**
268
+ * List of possible values/IDs. Use {@link EnumValue}s for more detailed
269
+ * config.
270
+ */
271
+ values: (string | EnumValue)[];
272
+ }
273
+ export interface EnumValue {
274
+ /**
275
+ * Enum value name/ID
276
+ */
277
+ name: string;
278
+ /**
279
+ * Optional associated numeric value
280
+ */
281
+ value?: number;
282
+ /**
283
+ * Optional docstring for this value
284
+ */
285
+ doc?: string;
286
+ }
287
+ export interface ICodeGen {
288
+ /**
289
+ * Optional prelude source, to be prepended before any generated type defs.
290
+ */
291
+ pre?: string;
292
+ /**
293
+ * Optional source code to be appended after any generated type defs.
294
+ */
295
+ post?: string;
296
+ /**
297
+ * Docstring codegen
298
+ */
299
+ doc: (doc: string, indent: string, acc: string[], topLevel?: boolean) => void;
300
+ /**
301
+ * Codegen for enum types.
302
+ */
303
+ enum: (type: Enum, types: TypeColl, acc: string[]) => void;
304
+ /**
305
+ * Codegen for struct types.
306
+ */
307
+ struct: (type: Struct, types: TypeColl, acc: string[]) => void;
308
+ }
309
+ /**
310
+ * WASM usize type. Assuming wasm32 until wasm64 surfaces, then need an option.
311
+ */
312
+ export declare const USIZE = "u32";
313
+ /**
314
+ * Byte size of {@link USIZE}.
315
+ */
316
+ export declare const USIZE_SIZE = 4;
103
317
  //# sourceMappingURL=api.d.ts.map
package/api.js CHANGED
@@ -1 +1,9 @@
1
- export {};
1
+ export const PKG_NAME = "@thi.ng/wasm-api";
2
+ /**
3
+ * WASM usize type. Assuming wasm32 until wasm64 surfaces, then need an option.
4
+ */
5
+ export const USIZE = "u32";
6
+ /**
7
+ * Byte size of {@link USIZE}.
8
+ */
9
+ export const USIZE_SIZE = 4;
package/bin/wasm-api ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # https://stackoverflow.com/a/246128/294515
4
+ SOURCE="${BASH_SOURCE[0]}"
5
+ while [ -h "$SOURCE" ]; do
6
+ DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
7
+ SOURCE="$(readlink "$SOURCE")"
8
+ [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE"
9
+ done
10
+ DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
11
+
12
+ /usr/bin/env node "$DIR/../cli.js" "$DIR" "$@"
package/bridge.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /// <reference types="node" />
2
2
  import type { NumericArray } from "@thi.ng/api";
3
3
  import type { ILogger } from "@thi.ng/logger";
4
- import type { BigIntArray, CoreAPI, IWasmAPI, WasmExports } from "./api.js";
4
+ import type { BigIntArray, CoreAPI, IWasmAPI, WasmExports, IWasmMemoryAccess } from "./api.js";
5
5
  export declare const OutOfMemoryError: {
6
6
  new (msg?: string | undefined): {
7
7
  name: string;
@@ -28,7 +28,7 @@ export declare const OutOfMemoryError: {
28
28
  * 64bit integers are handled via JS `BigInt` and hence require the host env to
29
29
  * support it. No polyfill is provided.
30
30
  */
31
- export declare class WasmBridge<T extends WasmExports = WasmExports> {
31
+ export declare class WasmBridge<T extends WasmExports = WasmExports> implements IWasmMemoryAccess {
32
32
  modules: Record<string, IWasmAPI<T>>;
33
33
  logger: ILogger;
34
34
  i8: Int8Array;
@@ -45,7 +45,7 @@ export declare class WasmBridge<T extends WasmExports = WasmExports> {
45
45
  utf8Encoder: TextEncoder;
46
46
  imports: WebAssembly.Imports;
47
47
  exports: T;
48
- core: CoreAPI;
48
+ api: CoreAPI;
49
49
  constructor(modules?: Record<string, IWasmAPI<T>>, logger?: ILogger);
50
50
  /**
51
51
  * Instantiates WASM module from given `src` (and optional provided extra
@@ -95,7 +95,7 @@ export declare class WasmBridge<T extends WasmExports = WasmExports> {
95
95
  * bridge.getImports();
96
96
  * {
97
97
  * // imports defined by the core API of the bridge itself
98
- * core: { ... },
98
+ * wasmapi: { ... },
99
99
  * // imports defined by the CustomAPI module
100
100
  * custom: { ... }
101
101
  * }
@@ -121,15 +121,26 @@ export declare class WasmBridge<T extends WasmExports = WasmExports> {
121
121
  * Attempts to allocate `numBytes` using the exported WASM core API function
122
122
  * {@link WasmExports._wasm_allocate} (implementation specific) and returns
123
123
  * start address of the new memory block. If unsuccessful, throws an
124
- * {@link OutOfMemoryError}.
124
+ * {@link OutOfMemoryError}. If `clear` is true, the allocated region will
125
+ * be zero-filled.
125
126
  *
126
127
  * @remarks
127
128
  * See {@link WasmExports._wasm_allocate} docs for further details.
128
129
  *
129
130
  * @param numBytes
131
+ * @param clear
130
132
  */
131
- allocate(numBytes: number): number;
132
- free(addr: number): void;
133
+ allocate(numBytes: number, clear?: boolean): number;
134
+ /**
135
+ * Frees a previous allocated memory region using the exported WASM core API
136
+ * function {@link WasmExports._wasm_free} (implementation specific). The
137
+ * `numBytes` value must be the same as previously given to
138
+ * {@link WasmBridge.allocate}.
139
+ *
140
+ * @param addr
141
+ * @param numBytes
142
+ */
143
+ free(addr: number, numBytes: number): void;
133
144
  getI8(addr: number): number;
134
145
  getU8(addr: number): number;
135
146
  getI16(addr: number): number;
@@ -170,7 +181,30 @@ export declare class WasmBridge<T extends WasmExports = WasmExports> {
170
181
  setU64Array(addr: number, buf: BigIntArray): this;
171
182
  setF32Array(addr: number, buf: NumericArray): this;
172
183
  setF64Array(addr: number, buf: NumericArray): this;
184
+ /**
185
+ * Reads UTF-8 encoded string from given address and optional byte length.
186
+ * The default length is 0, which will be interpreted as a zero-terminated
187
+ * string. Returns string.
188
+ *
189
+ * @param addr
190
+ * @param len
191
+ */
173
192
  getString(addr: number, len?: number): string;
193
+ /**
194
+ * Encodes given string as UTF-8 and writes it to WASM memory starting at
195
+ * `addr`. By default the string will be zero-terminated and only `maxBytes`
196
+ * will be written. Returns the number of bytes written.
197
+ *
198
+ * @remarks
199
+ * An error will be thrown if the encoded string doesn't fully fit into the
200
+ * designated memory region (also note that there might need to be space for
201
+ * the additional sentinel/termination byte).
202
+ *
203
+ * @param str
204
+ * @param addr
205
+ * @param maxBytes
206
+ * @param terminate
207
+ */
174
208
  setString(str: string, addr: number, maxBytes: number, terminate?: boolean): number;
175
209
  getElementById(addr: number, len?: number): HTMLElement;
176
210
  }
package/bridge.js CHANGED
@@ -28,7 +28,7 @@ export class WasmBridge {
28
28
  this.utf8Encoder = new TextEncoder();
29
29
  const logN = (x) => this.logger.debug(x);
30
30
  const logA = (method) => (addr, len) => this.logger.debug(method(addr, len).join(", "));
31
- this.core = {
31
+ this.api = {
32
32
  printI8: logN,
33
33
  printU8: logN,
34
34
  printU8Hex: (x) => this.logger.debug(`0x${U8(x)}`),
@@ -136,7 +136,7 @@ export class WasmBridge {
136
136
  * bridge.getImports();
137
137
  * {
138
138
  * // imports defined by the core API of the bridge itself
139
- * core: { ... },
139
+ * wasmapi: { ... },
140
140
  * // imports defined by the CustomAPI module
141
141
  * custom: { ... }
142
142
  * }
@@ -151,7 +151,7 @@ export class WasmBridge {
151
151
  */
152
152
  getImports() {
153
153
  if (!this.imports) {
154
- this.imports = { core: this.core };
154
+ this.imports = { wasmapi: this.api };
155
155
  for (let id in this.modules) {
156
156
  if (this.imports[id] !== undefined) {
157
157
  illegalArgs(`attempt to redeclare API module ${id}`);
@@ -176,24 +176,36 @@ export class WasmBridge {
176
176
  * Attempts to allocate `numBytes` using the exported WASM core API function
177
177
  * {@link WasmExports._wasm_allocate} (implementation specific) and returns
178
178
  * start address of the new memory block. If unsuccessful, throws an
179
- * {@link OutOfMemoryError}.
179
+ * {@link OutOfMemoryError}. If `clear` is true, the allocated region will
180
+ * be zero-filled.
180
181
  *
181
182
  * @remarks
182
183
  * See {@link WasmExports._wasm_allocate} docs for further details.
183
184
  *
184
185
  * @param numBytes
186
+ * @param clear
185
187
  */
186
- allocate(numBytes) {
188
+ allocate(numBytes, clear = false) {
187
189
  const addr = this.exports._wasm_allocate(numBytes);
188
190
  if (!addr)
189
191
  throw new OutOfMemoryError(`unable to allocate: ${numBytes}`);
190
192
  this.logger.debug(`allocated ${numBytes} bytes @ 0x${U32(addr)}`);
191
193
  this.ensureMemory();
194
+ clear && this.u8.fill(0, addr, addr + numBytes);
192
195
  return addr;
193
196
  }
194
- free(addr) {
195
- this.logger.debug(`freeing memory @ 0x${U32(addr)}`);
196
- this.exports._wasm_free(addr);
197
+ /**
198
+ * Frees a previous allocated memory region using the exported WASM core API
199
+ * function {@link WasmExports._wasm_free} (implementation specific). The
200
+ * `numBytes` value must be the same as previously given to
201
+ * {@link WasmBridge.allocate}.
202
+ *
203
+ * @param addr
204
+ * @param numBytes
205
+ */
206
+ free(addr, numBytes) {
207
+ this.logger.debug(`freeing memory @ 0x${U32(addr)} .. 0x${U32(addr + numBytes - 1)}`);
208
+ this.exports._wasm_free(addr, numBytes);
197
209
  }
198
210
  getI8(addr) {
199
211
  return this.i8[addr];
@@ -343,14 +355,38 @@ export class WasmBridge {
343
355
  this.f64.set(buf, addr >> 3);
344
356
  return this;
345
357
  }
358
+ /**
359
+ * Reads UTF-8 encoded string from given address and optional byte length.
360
+ * The default length is 0, which will be interpreted as a zero-terminated
361
+ * string. Returns string.
362
+ *
363
+ * @param addr
364
+ * @param len
365
+ */
346
366
  getString(addr, len = 0) {
367
+ this.ensureMemory();
347
368
  return this.utf8Decoder.decode(this.u8.subarray(addr, len > 0 ? addr + len : this.u8.indexOf(0, addr)));
348
369
  }
370
+ /**
371
+ * Encodes given string as UTF-8 and writes it to WASM memory starting at
372
+ * `addr`. By default the string will be zero-terminated and only `maxBytes`
373
+ * will be written. Returns the number of bytes written.
374
+ *
375
+ * @remarks
376
+ * An error will be thrown if the encoded string doesn't fully fit into the
377
+ * designated memory region (also note that there might need to be space for
378
+ * the additional sentinel/termination byte).
379
+ *
380
+ * @param str
381
+ * @param addr
382
+ * @param maxBytes
383
+ * @param terminate
384
+ */
349
385
  setString(str, addr, maxBytes, terminate = true) {
350
386
  maxBytes = Math.min(maxBytes, this.u8.length - addr);
351
387
  const len = this.utf8Encoder.encodeInto(str, this.u8.subarray(addr, addr + maxBytes)).written;
352
388
  if (len == null || len >= maxBytes + (terminate ? 0 : 1)) {
353
- illegalArgs(`error writing string to 0x${U32(addr)}`);
389
+ illegalArgs(`error writing string to 0x${U32(addr)} (max. ${maxBytes} bytes, got at least ${str.length})`);
354
390
  }
355
391
  if (terminate) {
356
392
  this.u8[addr + len] = 0;
package/cli.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ export declare const INSTALL_DIR: string;
2
+ export declare const PKG: any;
3
+ export declare const APP_NAME: any;
4
+ export declare const HEADER: string;
5
+ //# sourceMappingURL=cli.d.ts.map
package/cli.js ADDED
@@ -0,0 +1,142 @@
1
+ import { flag, oneOfMulti, parse, ParseError, string, strings, usage, } from "@thi.ng/args";
2
+ import { isArray, isPlainObject } from "@thi.ng/checks";
3
+ import { illegalArgs } from "@thi.ng/errors";
4
+ import { readJSON, writeText } from "@thi.ng/file-io";
5
+ import { ConsoleLogger } from "@thi.ng/logger";
6
+ import { resolve } from "path";
7
+ import { generateTypes } from "./codegen.js";
8
+ import { TYPESCRIPT } from "./codegen/typescript.js";
9
+ import { isWasmPrim, isWasmString } from "./codegen/utils.js";
10
+ import { ZIG } from "./codegen/zig.js";
11
+ const GENERATORS = { ts: TYPESCRIPT, zig: ZIG };
12
+ const argOpts = {
13
+ config: string({
14
+ alias: "c",
15
+ hint: "FILE",
16
+ desc: "JSON config file with codegen options",
17
+ }),
18
+ debug: flag({ alias: "d", default: false, desc: "enable debug output" }),
19
+ dryRun: flag({
20
+ default: false,
21
+ desc: "enable dry run (don't overwrite files)",
22
+ }),
23
+ lang: oneOfMulti(Object.keys(GENERATORS), {
24
+ alias: "l",
25
+ desc: "target language",
26
+ default: ["ts", "zig"],
27
+ delim: ",",
28
+ }),
29
+ out: strings({ alias: "o", hint: "FILE", desc: "output file path" }),
30
+ };
31
+ export const INSTALL_DIR = resolve(`${process.argv[2]}/..`);
32
+ export const PKG = readJSON(`${INSTALL_DIR}/package.json`);
33
+ export const APP_NAME = PKG.name.split("/")[1];
34
+ export const HEADER = `
35
+ █ █ █ │
36
+ ██ █ │
37
+ █ █ █ █ █ █ █ █ │ ${PKG.name} ${PKG.version}
38
+ █ █ █ █ █ █ █ █ █ │ Multi-language data bindings code generator
39
+ █ │
40
+ █ █ │
41
+ `;
42
+ const usageOpts = {
43
+ lineWidth: process.stdout.columns,
44
+ prefix: `${HEADER}
45
+ usage: ${APP_NAME} [OPTS] JSON-INPUT-FILE(S) ...
46
+ ${APP_NAME} --help
47
+
48
+ `,
49
+ showGroupNames: true,
50
+ paramWidth: 32,
51
+ };
52
+ const showUsage = () => {
53
+ process.stderr.write(usage(argOpts, usageOpts));
54
+ process.exit(1);
55
+ };
56
+ const invalidSpec = (path, msg) => {
57
+ throw new Error(`invalid typedef: ${path}${msg ? ` (${msg})` : ""}`);
58
+ };
59
+ const addTypeSpec = (ctx, path, coll, spec) => {
60
+ if (!(spec.name && spec.type))
61
+ invalidSpec(path);
62
+ if (!(spec.type === "enum" || spec.type === "struct"))
63
+ invalidSpec(path, `${spec.name} type: ${spec.type}`);
64
+ if (coll[spec.name])
65
+ invalidSpec(path, `duplicate name: ${spec.name}`);
66
+ ctx.logger.debug(`registering ${spec.type}: ${spec.name}`);
67
+ coll[spec.name] = spec;
68
+ spec.__path = path;
69
+ };
70
+ const validateTypeRefs = (coll) => {
71
+ for (let spec of Object.values(coll)) {
72
+ if (spec.type !== "struct")
73
+ continue;
74
+ for (let f of spec.fields) {
75
+ if (!(isWasmPrim(f.type) || isWasmString(f.type) || coll[f.type])) {
76
+ invalidSpec(spec.__path, `structfield ${spec.name}.${f.name} of unknown type: ${f.type}`);
77
+ }
78
+ }
79
+ }
80
+ };
81
+ const parseTypeSpecs = (ctx, inputs) => {
82
+ const coll = {};
83
+ for (let path of inputs) {
84
+ try {
85
+ const spec = readJSON(resolve(path), ctx.logger);
86
+ if (isArray(spec)) {
87
+ for (let s of spec)
88
+ addTypeSpec(ctx, path, coll, s);
89
+ }
90
+ else if (isPlainObject(spec)) {
91
+ addTypeSpec(ctx, path, coll, spec);
92
+ }
93
+ else {
94
+ invalidSpec(path);
95
+ }
96
+ }
97
+ catch (e) {
98
+ process.stderr.write(e.message);
99
+ process.exit(1);
100
+ }
101
+ }
102
+ validateTypeRefs(coll);
103
+ return coll;
104
+ };
105
+ const generateOutputs = ({ config, logger, opts }, coll) => {
106
+ for (let i = 0; i < opts.lang.length; i++) {
107
+ const lang = opts.lang[i];
108
+ logger.debug(`generating ${lang.toUpperCase()} output...`);
109
+ const src = generateTypes(coll, GENERATORS[lang](config[lang]), config.global);
110
+ if (opts.out) {
111
+ writeText(resolve(opts.out[i]), src, logger, opts.dryRun);
112
+ }
113
+ else {
114
+ process.stdout.write(src + "\n");
115
+ }
116
+ }
117
+ };
118
+ try {
119
+ const result = parse(argOpts, process.argv, { start: 3, usageOpts });
120
+ if (!result)
121
+ process.exit(1);
122
+ const { result: opts, rest } = result;
123
+ if (!rest.length)
124
+ showUsage();
125
+ if (opts.out && opts.lang.length != opts.out.length) {
126
+ illegalArgs(`expected ${opts.lang.length} outputs, but got ${opts.out.length}`);
127
+ }
128
+ const ctx = {
129
+ logger: new ConsoleLogger("wasm-api", opts.debug ? "DEBUG" : "INFO"),
130
+ config: {},
131
+ opts,
132
+ };
133
+ if (opts.config) {
134
+ ctx.config = readJSON(resolve(opts.config), ctx.logger);
135
+ }
136
+ generateOutputs(ctx, parseTypeSpecs(ctx, rest));
137
+ }
138
+ catch (e) {
139
+ if (!(e instanceof ParseError))
140
+ process.stderr.write(e.message);
141
+ process.exit(1);
142
+ }
@@ -0,0 +1,35 @@
1
+ import { ICodeGen } from "../api.js";
2
+ /**
3
+ * TypeScript code generator options.
4
+ */
5
+ export interface TSOpts {
6
+ /**
7
+ * Indentation string
8
+ *
9
+ * @defaultValue "\t"
10
+ */
11
+ indent: string;
12
+ /**
13
+ * If true (default), forces uppercase enums
14
+ *
15
+ * @defaultValue true
16
+ */
17
+ uppercaseEnums: boolean;
18
+ /**
19
+ * Same as {@link CodeGenOpts.stringType}.
20
+ */
21
+ stringType: "slice" | "ptr";
22
+ }
23
+ /**
24
+ * TypeScript code generator. Call with options and then pass to
25
+ * {@link generateTypes} (see its docs for further usage).
26
+ *
27
+ * @remarks
28
+ * This codegen generates interface and enum definitions for a {@link TypeColl}
29
+ * given to {@link generateTypes}. For structs it will also generate memory
30
+ * mapped wrappers with fully typed accessors.
31
+ *
32
+ * @param opts
33
+ */
34
+ export declare const TYPESCRIPT: (opts?: Partial<TSOpts>) => ICodeGen;
35
+ //# sourceMappingURL=typescript.d.ts.map