@tsonic/tsbindgen 0.1.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/LICENSE +21 -0
- package/README.md +448 -0
- package/index.js +75 -0
- package/package.json +43 -0
- package/scripts/postinstall.js +80 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 tsoniclang
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
# tsbindgen
|
|
2
|
+
|
|
3
|
+
A .NET tool that generates TypeScript declaration files (`.d.ts`) from .NET assemblies for use with the Tsonic compiler.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`tsbindgen` uses reflection to analyze .NET assemblies and produces TypeScript declarations that follow Tsonic's interop rules. This allows TypeScript code compiled with Tsonic to properly type-check when using .NET libraries.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
Build the tool from source:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
dotnet build
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
### Basic Usage
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
tsbindgen <assembly-path>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Example:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
tsbindgen /usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/8.0.0/ref/net8.0/System.Text.Json.dll
|
|
29
|
+
# Creates:
|
|
30
|
+
# ./System.Text.Json.d.ts
|
|
31
|
+
# ./System.Text.Json.metadata.json
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Command-Line Options
|
|
35
|
+
|
|
36
|
+
| Option | Short | Description | Default |
|
|
37
|
+
|--------|-------|-------------|---------|
|
|
38
|
+
| `--namespaces` | `-n` | Comma-separated list of namespaces to include | All namespaces |
|
|
39
|
+
| `--out-dir` | `-o` | Output directory for generated file | `.` (current directory) |
|
|
40
|
+
| `--log` | `-l` | Path to write JSON log file | None |
|
|
41
|
+
| `--config` | `-c` | Path to configuration JSON file | None |
|
|
42
|
+
|
|
43
|
+
### Examples
|
|
44
|
+
|
|
45
|
+
**Filter specific namespaces:**
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
tsbindgen System.Text.Json.dll --namespaces System.Text.Json.Serialization
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Specify output directory:**
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
tsbindgen System.Net.Http.dll --out-dir ./declarations
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Generate with logging:**
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
tsbindgen System.IO.dll --log build.log.json
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Generate user library excluding BCL types:**
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Step 1: Generate BCL package (once)
|
|
67
|
+
tsbindgen generate -d ~/dotnet/shared/Microsoft.NETCore.App/10.0.0 -o ./bcl-package
|
|
68
|
+
|
|
69
|
+
# Step 2: Generate user library, BCL types excluded
|
|
70
|
+
tsbindgen generate -a MyLib.dll -d ~/dotnet/.../10.0.0 \
|
|
71
|
+
--lib ./bcl-package -o ./my-lib-package
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Library Mode (`--lib`)
|
|
75
|
+
|
|
76
|
+
When generating declarations for a user assembly that references a base library (e.g., .NET BCL), use `--lib` to exclude base library types from output. This produces a clean package containing only your library's types.
|
|
77
|
+
|
|
78
|
+
**How it works:**
|
|
79
|
+
1. Generate base library package first (contains `metadata.json` + `bindings.json`)
|
|
80
|
+
2. Use `--lib <base-package-path>` when generating user library
|
|
81
|
+
3. tsbindgen filters: keeps types NOT in base, removes types IN base
|
|
82
|
+
4. Validates no dangling references (LIB002 strict check)
|
|
83
|
+
|
|
84
|
+
**Use case:** Publishing TypeScript declarations for a .NET library without duplicating BCL type definitions.
|
|
85
|
+
|
|
86
|
+
See [CLI documentation](spec/cli.md#library-mode) for complete details.
|
|
87
|
+
|
|
88
|
+
## Generated Output
|
|
89
|
+
|
|
90
|
+
The tool generates two files for each assembly:
|
|
91
|
+
|
|
92
|
+
1. **TypeScript declarations** (`.d.ts`) - TypeScript type definitions
|
|
93
|
+
2. **Metadata sidecar** (`.metadata.json`) - C# semantic information
|
|
94
|
+
|
|
95
|
+
### TypeScript Declarations
|
|
96
|
+
|
|
97
|
+
The `.d.ts` file contains TypeScript declarations with:
|
|
98
|
+
|
|
99
|
+
1. **Branded type aliases** for C# numeric types:
|
|
100
|
+
```typescript
|
|
101
|
+
type int = number & { __brand: "int" };
|
|
102
|
+
type decimal = number & { __brand: "decimal" };
|
|
103
|
+
// ... etc
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
2. **Namespace declarations** matching .NET namespaces:
|
|
107
|
+
```typescript
|
|
108
|
+
declare namespace System.Text.Json {
|
|
109
|
+
class JsonSerializer {
|
|
110
|
+
static Serialize<T>(value: T): string;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
3. **Proper type mappings**:
|
|
116
|
+
- `System.String` → `string`
|
|
117
|
+
- `System.Int32` → `int`
|
|
118
|
+
- `System.Boolean` → `boolean`
|
|
119
|
+
- `Task<T>` → `Promise<T>`
|
|
120
|
+
- `T[]` → `ReadonlyArray<T>`
|
|
121
|
+
- `List<T>` → `List<T>`
|
|
122
|
+
- `Nullable<T>` → `T | null`
|
|
123
|
+
|
|
124
|
+
### Metadata Sidecar Files
|
|
125
|
+
|
|
126
|
+
The `.metadata.json` file contains C# semantic information that TypeScript cannot express. This enables the Tsonic compiler to generate correct C# code, particularly for:
|
|
127
|
+
|
|
128
|
+
- **Virtual/override methods** - Required to correctly override base class methods
|
|
129
|
+
- **Abstract classes/methods** - Required to properly extend abstract types
|
|
130
|
+
- **Sealed classes/methods** - Prevents invalid inheritance
|
|
131
|
+
- **Static classes** - Type-level restrictions
|
|
132
|
+
- **Struct vs Class** - Value vs reference type semantics
|
|
133
|
+
- **Method accessibility** - Public, protected, private, internal modifiers
|
|
134
|
+
|
|
135
|
+
#### Example Structure
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"assemblyName": "System.Text.Json",
|
|
140
|
+
"assemblyVersion": "10.0.0.0",
|
|
141
|
+
"types": {
|
|
142
|
+
"System.Text.Json.JsonSerializer": {
|
|
143
|
+
"kind": "class",
|
|
144
|
+
"isAbstract": true,
|
|
145
|
+
"isSealed": false,
|
|
146
|
+
"isStatic": false,
|
|
147
|
+
"baseType": null,
|
|
148
|
+
"interfaces": [],
|
|
149
|
+
"members": {
|
|
150
|
+
"Serialize<T>(T)": {
|
|
151
|
+
"kind": "method",
|
|
152
|
+
"isVirtual": false,
|
|
153
|
+
"isAbstract": false,
|
|
154
|
+
"isSealed": false,
|
|
155
|
+
"isOverride": false,
|
|
156
|
+
"isStatic": true,
|
|
157
|
+
"accessibility": "public"
|
|
158
|
+
},
|
|
159
|
+
"Deserialize<T>(string)": {
|
|
160
|
+
"kind": "method",
|
|
161
|
+
"isVirtual": false,
|
|
162
|
+
"isAbstract": false,
|
|
163
|
+
"isSealed": false,
|
|
164
|
+
"isOverride": false,
|
|
165
|
+
"isStatic": true,
|
|
166
|
+
"accessibility": "public"
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
#### Metadata Fields
|
|
175
|
+
|
|
176
|
+
**Type-level fields:**
|
|
177
|
+
- `kind`: `"class"`, `"struct"`, `"interface"`, or `"enum"`
|
|
178
|
+
- `isAbstract`: True for abstract classes (excluding interfaces)
|
|
179
|
+
- `isSealed`: True for sealed classes (excluding value types and enums)
|
|
180
|
+
- `isStatic`: True for static classes
|
|
181
|
+
- `baseType`: Full name of base class (if any)
|
|
182
|
+
- `interfaces`: Array of implemented interface names
|
|
183
|
+
- `members`: Dictionary of member metadata keyed by signature
|
|
184
|
+
|
|
185
|
+
**Member-level fields:**
|
|
186
|
+
- `kind`: `"method"`, `"property"`, or `"constructor"`
|
|
187
|
+
- `isVirtual`: True if method can be overridden
|
|
188
|
+
- `isAbstract`: True for abstract methods
|
|
189
|
+
- `isSealed`: True if method prevents further overriding
|
|
190
|
+
- `isOverride`: True if method overrides a base method
|
|
191
|
+
- `isStatic`: True for static members
|
|
192
|
+
- `accessibility`: `"public"`, `"protected"`, `"private"`, `"internal"`, etc.
|
|
193
|
+
|
|
194
|
+
**Signature format:**
|
|
195
|
+
- Methods: `MethodName(Type1,Type2,...)` using C# type names
|
|
196
|
+
- Properties: `PropertyName`
|
|
197
|
+
- Constructors: `ctor(Type1,Type2,...)`
|
|
198
|
+
|
|
199
|
+
## Configuration File
|
|
200
|
+
|
|
201
|
+
You can provide a JSON configuration file to customize behavior:
|
|
202
|
+
|
|
203
|
+
```json
|
|
204
|
+
{
|
|
205
|
+
"skipNamespaces": ["System.Internal"],
|
|
206
|
+
"typeRenames": {
|
|
207
|
+
"System.OldType": "NewType"
|
|
208
|
+
},
|
|
209
|
+
"skipMembers": [
|
|
210
|
+
"System.String::InternalMethod"
|
|
211
|
+
]
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Usage:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
tsbindgen Assembly.dll --config config.json
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Log Output
|
|
222
|
+
|
|
223
|
+
When using `--log`, a JSON file is generated with:
|
|
224
|
+
|
|
225
|
+
```json
|
|
226
|
+
{
|
|
227
|
+
"timestamp": "2025-11-01T13:03:38Z",
|
|
228
|
+
"namespaces": ["System.Text.Json"],
|
|
229
|
+
"typeCounts": {
|
|
230
|
+
"classes": 40,
|
|
231
|
+
"interfaces": 5,
|
|
232
|
+
"enums": 10,
|
|
233
|
+
"total": 55
|
|
234
|
+
},
|
|
235
|
+
"warnings": []
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Type Mapping Rules
|
|
240
|
+
|
|
241
|
+
The tool follows Tsonic's type mapping specification:
|
|
242
|
+
|
|
243
|
+
- **Classes** → TypeScript classes
|
|
244
|
+
- **Interfaces** → TypeScript interfaces
|
|
245
|
+
- **Enums** → TypeScript enums
|
|
246
|
+
- **Structs** → TypeScript classes
|
|
247
|
+
- **Static methods** → `static` methods
|
|
248
|
+
- **Properties** → TypeScript properties (with `readonly` when appropriate)
|
|
249
|
+
- **Generic types** → TypeScript generics `<T>`
|
|
250
|
+
- **Optional parameters** → `param?: Type`
|
|
251
|
+
- **Params arrays** → `...values: ReadonlyArray<T>`
|
|
252
|
+
|
|
253
|
+
## Excluded Members
|
|
254
|
+
|
|
255
|
+
The tool automatically skips:
|
|
256
|
+
|
|
257
|
+
- Private and internal members
|
|
258
|
+
- Compiler-generated types
|
|
259
|
+
- Common Object methods (`Equals`, `GetHashCode`, `ToString`, `GetType`, `ReferenceEquals`)
|
|
260
|
+
- Special-name members (property accessors, backing fields)
|
|
261
|
+
|
|
262
|
+
## Reserved Keyword Escaping
|
|
263
|
+
|
|
264
|
+
Parameter names that conflict with TypeScript/JavaScript reserved keywords are automatically escaped by prefixing them with an underscore. This prevents syntax errors in the generated declarations.
|
|
265
|
+
|
|
266
|
+
**Escaped keywords include:**
|
|
267
|
+
- Control flow: `break`, `case`, `catch`, `continue`, `default`, `do`, `else`, `finally`, `for`, `if`, `return`, `switch`, `throw`, `try`, `while`
|
|
268
|
+
- Declarations: `class`, `const`, `enum`, `export`, `extends`, `function`, `import`, `let`, `var`, `void`
|
|
269
|
+
- Modifiers: `async`, `await`, `implements`, `interface`, `package`, `private`, `protected`, `public`, `static`, `yield`
|
|
270
|
+
- Special identifiers: `arguments`, `eval`, `this`, `super`, `new`, `typeof`, `instanceof`, `delete`, `debugger`, `with`, `in`
|
|
271
|
+
|
|
272
|
+
**Example:**
|
|
273
|
+
```csharp
|
|
274
|
+
// C# method signature
|
|
275
|
+
public static LoopExpression Loop(Expression body, LabelTarget break, LabelTarget continue)
|
|
276
|
+
|
|
277
|
+
// Generated TypeScript
|
|
278
|
+
static Loop(body: Expression, _break: LabelTarget, _continue: LabelTarget): LoopExpression;
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
This ensures that all generated `.d.ts` files are valid TypeScript and can be parsed by the TypeScript compiler without syntax errors.
|
|
282
|
+
|
|
283
|
+
## Testing & Validation
|
|
284
|
+
|
|
285
|
+
The project includes comprehensive test scripts to ensure correctness and prevent regressions.
|
|
286
|
+
|
|
287
|
+
### Running All Tests
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
# TypeScript syntax validation
|
|
291
|
+
npm install # First time only
|
|
292
|
+
npm run validate
|
|
293
|
+
|
|
294
|
+
# Regression guards (run all)
|
|
295
|
+
./scripts/test-determinism.sh # Deterministic output
|
|
296
|
+
./scripts/test-strict-mode.sh # Strict mode compliance
|
|
297
|
+
./scripts/test-surface-manifest.sh # Surface baseline guard
|
|
298
|
+
./scripts/test-lib.sh # Library mode (--lib)
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### TypeScript Validation
|
|
302
|
+
|
|
303
|
+
Ensures all generated `.d.ts` files are syntactically valid TypeScript:
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
npm run validate
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
This script:
|
|
310
|
+
1. Regenerates all 38 BCL assemblies to a temporary directory
|
|
311
|
+
2. Creates an `index.d.ts` with triple-slash references
|
|
312
|
+
3. Runs the TypeScript compiler to validate all declarations
|
|
313
|
+
4. Reports syntax errors (TS1xxx), duplicate type errors (TS6200), and semantic errors (TS2xxx)
|
|
314
|
+
|
|
315
|
+
**Success criteria:**
|
|
316
|
+
- ✅ **Zero syntax errors (TS1xxx)** - All output is valid TypeScript
|
|
317
|
+
- ⚠️ **Semantic errors acceptable** - TS2xxx errors are expected (known limitations)
|
|
318
|
+
|
|
319
|
+
**Example output:**
|
|
320
|
+
```
|
|
321
|
+
✓ VALIDATION PASSED
|
|
322
|
+
|
|
323
|
+
All 38 assemblies generated successfully
|
|
324
|
+
All metadata files present
|
|
325
|
+
✓ No TypeScript syntax errors (TS1xxx)
|
|
326
|
+
|
|
327
|
+
Error breakdown:
|
|
328
|
+
- Syntax errors (TS1xxx): 0 ✓
|
|
329
|
+
- Duplicate types (TS6200): 0 (expected)
|
|
330
|
+
- Semantic errors (TS2xxx): 1 (expected - missing cross-assembly refs)
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Regression Guards
|
|
334
|
+
|
|
335
|
+
#### Determinism Test (`test-determinism.sh`)
|
|
336
|
+
|
|
337
|
+
Ensures tsbindgen produces identical output across runs:
|
|
338
|
+
|
|
339
|
+
```bash
|
|
340
|
+
./scripts/test-determinism.sh
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
**What it tests:**
|
|
344
|
+
- Same input → same output (bit-for-bit identical)
|
|
345
|
+
- No nondeterministic ordering or formatting
|
|
346
|
+
- Critical for reproducible builds and diffing
|
|
347
|
+
|
|
348
|
+
#### Strict Mode Test (`test-strict-mode.sh`)
|
|
349
|
+
|
|
350
|
+
Verifies strict mode compliance and performance baseline:
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
./scripts/test-strict-mode.sh
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
**What it tests:**
|
|
357
|
+
- No diagnostics with `--strict` flag
|
|
358
|
+
- Performance doesn't regress beyond baseline
|
|
359
|
+
|
|
360
|
+
#### Surface Baseline Test (`test-surface-manifest.sh`)
|
|
361
|
+
|
|
362
|
+
Guards against accidental removal of public API surface:
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
./scripts/test-surface-manifest.sh
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
**What it tests:**
|
|
369
|
+
- Type count matches baseline (prevents deletions)
|
|
370
|
+
- Member count matches baseline (prevents deletions)
|
|
371
|
+
- Intentional changes require baseline update
|
|
372
|
+
|
|
373
|
+
#### Library Mode Test (`test-lib.sh`)
|
|
374
|
+
|
|
375
|
+
Validates `--lib` mode filters base library types correctly:
|
|
376
|
+
|
|
377
|
+
```bash
|
|
378
|
+
./scripts/test-lib.sh
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
**What it tests:**
|
|
382
|
+
- BCL package generation succeeds
|
|
383
|
+
- User library builds successfully
|
|
384
|
+
- Full generation includes both user + BCL types
|
|
385
|
+
- `--lib` filtered generation includes ONLY user types
|
|
386
|
+
- No LIB001-003 validation errors
|
|
387
|
+
- BCL namespaces correctly excluded from filtered output
|
|
388
|
+
- User namespaces correctly included in filtered output
|
|
389
|
+
|
|
390
|
+
**Expected result:**
|
|
391
|
+
```
|
|
392
|
+
✓ LIBRARY MODE FULLY VERIFIED
|
|
393
|
+
|
|
394
|
+
Summary:
|
|
395
|
+
✓ BCL generation succeeded (130 namespaces)
|
|
396
|
+
✓ User library build succeeded
|
|
397
|
+
✓ Full generation: 131 namespaces (user + BCL)
|
|
398
|
+
✓ Filtered generation: 1 namespaces (user only)
|
|
399
|
+
✓ BCL types correctly excluded via --lib
|
|
400
|
+
✓ User types (MyCompany.Utils) correctly included
|
|
401
|
+
✓ No LIB001-003 validation errors
|
|
402
|
+
✓ Strict mode passes
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
## Development
|
|
406
|
+
|
|
407
|
+
### Project Structure
|
|
408
|
+
|
|
409
|
+
```
|
|
410
|
+
tsbindgen/
|
|
411
|
+
├── Src/
|
|
412
|
+
│ ├── Program.cs # CLI entry point
|
|
413
|
+
│ ├── AssemblyProcessor.cs # Reflection and type/metadata extraction
|
|
414
|
+
│ ├── TypeMapper.cs # C# to TypeScript type mapping
|
|
415
|
+
│ ├── DeclarationRenderer.cs # TypeScript output generation
|
|
416
|
+
│ ├── TypeInfo.cs # Data structures for declarations
|
|
417
|
+
│ ├── MetadataModel.cs # Data structures for metadata
|
|
418
|
+
│ ├── SignatureFormatter.cs # Method/property signature formatting
|
|
419
|
+
│ ├── MetadataWriter.cs # JSON metadata serialization
|
|
420
|
+
│ ├── GeneratorConfig.cs # Configuration support
|
|
421
|
+
│ └── GenerationLogger.cs # Logging functionality
|
|
422
|
+
├── Scripts/
|
|
423
|
+
│ └── validate.js # Full validation script
|
|
424
|
+
├── package.json # npm scripts (validate)
|
|
425
|
+
└── README.md
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
### Building
|
|
429
|
+
|
|
430
|
+
```bash
|
|
431
|
+
dotnet build
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### Running
|
|
435
|
+
|
|
436
|
+
```bash
|
|
437
|
+
dotnet run --project Src -- <assembly-path> [options]
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
## Related Documentation
|
|
441
|
+
|
|
442
|
+
- [Tsonic Type Mappings](../tsonic/spec/04-type-mappings.md)
|
|
443
|
+
- [.NET Interop](../tsonic/spec/08-dotnet-interop.md)
|
|
444
|
+
- [.NET Declarations](../tsonic/spec/14-dotnet-declarations.md)
|
|
445
|
+
|
|
446
|
+
## License
|
|
447
|
+
|
|
448
|
+
See LICENSE file for details.
|
package/index.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @tsonic/tsbindgen - Programmatic API
|
|
3
|
+
*
|
|
4
|
+
* Provides a way to invoke tsbindgen from JavaScript code.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { spawn } from "node:child_process";
|
|
8
|
+
import { existsSync } from "node:fs";
|
|
9
|
+
import { dirname, join } from "node:path";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
|
|
12
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Get the path to the tsbindgen binary (symlinked by postinstall)
|
|
16
|
+
*/
|
|
17
|
+
const getBinaryPath = () => {
|
|
18
|
+
const binaryPath = join(__dirname, "tsbindgen");
|
|
19
|
+
|
|
20
|
+
if (!existsSync(binaryPath)) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
"tsbindgen binary not found. " +
|
|
23
|
+
"This may mean the package was not installed correctly or " +
|
|
24
|
+
"your platform is not supported (darwin-arm64, darwin-x64, linux-arm64, linux-x64)."
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return binaryPath;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Run tsbindgen with the given arguments
|
|
33
|
+
* @param {string[]} args - Command line arguments
|
|
34
|
+
* @returns {Promise<{ code: number; stdout: string; stderr: string }>}
|
|
35
|
+
*/
|
|
36
|
+
export const run = (args) => {
|
|
37
|
+
return new Promise((resolve, reject) => {
|
|
38
|
+
const binaryPath = getBinaryPath();
|
|
39
|
+
const proc = spawn(binaryPath, args);
|
|
40
|
+
|
|
41
|
+
let stdout = "";
|
|
42
|
+
let stderr = "";
|
|
43
|
+
|
|
44
|
+
proc.stdout.on("data", (data) => {
|
|
45
|
+
stdout += data.toString();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
proc.stderr.on("data", (data) => {
|
|
49
|
+
stderr += data.toString();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
proc.on("error", (err) => {
|
|
53
|
+
reject(err);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
proc.on("close", (code) => {
|
|
57
|
+
resolve({ code: code ?? 0, stdout, stderr });
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Check if tsbindgen is available
|
|
64
|
+
* @returns {boolean}
|
|
65
|
+
*/
|
|
66
|
+
export const isAvailable = () => {
|
|
67
|
+
try {
|
|
68
|
+
getBinaryPath();
|
|
69
|
+
return true;
|
|
70
|
+
} catch {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export { getBinaryPath };
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tsonic/tsbindgen",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Generate TypeScript declarations from .NET assemblies",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"tsbindgen": "./tsbindgen"
|
|
8
|
+
},
|
|
9
|
+
"main": "./index.js",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./index.js"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"tsbindgen",
|
|
15
|
+
"index.js",
|
|
16
|
+
"scripts/postinstall.js",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"postinstall": "node scripts/postinstall.js",
|
|
21
|
+
"build": "./scripts/build-native.sh"
|
|
22
|
+
},
|
|
23
|
+
"optionalDependencies": {
|
|
24
|
+
"@tsonic/tsbindgen-darwin-arm64": "0.1.0",
|
|
25
|
+
"@tsonic/tsbindgen-darwin-x64": "0.1.0",
|
|
26
|
+
"@tsonic/tsbindgen-linux-arm64": "0.1.0",
|
|
27
|
+
"@tsonic/tsbindgen-linux-x64": "0.1.0"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"tsonic",
|
|
31
|
+
"typescript",
|
|
32
|
+
"dotnet",
|
|
33
|
+
"csharp",
|
|
34
|
+
"codegen",
|
|
35
|
+
"bindings"
|
|
36
|
+
],
|
|
37
|
+
"author": "Tsonic Team",
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://github.com/tsoniclang/tsbindgen.git"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Postinstall script for @tsonic/tsbindgen
|
|
5
|
+
* Links the correct platform-specific binary after npm install
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { existsSync, symlinkSync, unlinkSync, chmodSync } from "node:fs";
|
|
9
|
+
import { dirname, join } from "node:path";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
|
|
12
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const ROOT = join(__dirname, "..");
|
|
14
|
+
|
|
15
|
+
const PLATFORMS = {
|
|
16
|
+
"darwin-arm64": "@tsonic/tsbindgen-darwin-arm64",
|
|
17
|
+
"darwin-x64": "@tsonic/tsbindgen-darwin-x64",
|
|
18
|
+
"linux-arm64": "@tsonic/tsbindgen-linux-arm64",
|
|
19
|
+
"linux-x64": "@tsonic/tsbindgen-linux-x64",
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const getPlatformKey = () => `${process.platform}-${process.arch}`;
|
|
23
|
+
|
|
24
|
+
const findBinary = () => {
|
|
25
|
+
const key = getPlatformKey();
|
|
26
|
+
const packageName = PLATFORMS[key];
|
|
27
|
+
const platformDir = key; // e.g., "linux-x64"
|
|
28
|
+
|
|
29
|
+
if (!packageName) {
|
|
30
|
+
console.error(`Unsupported platform: ${key}`);
|
|
31
|
+
console.error("tsbindgen supports: darwin-arm64, darwin-x64, linux-arm64, linux-x64");
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const binaryName = "tsbindgen";
|
|
36
|
+
|
|
37
|
+
// Search paths where the platform package might be installed
|
|
38
|
+
const searchPaths = [
|
|
39
|
+
// Development: local npm/ directory
|
|
40
|
+
join(ROOT, "npm", platformDir, binaryName),
|
|
41
|
+
// Installed as dependency
|
|
42
|
+
join(ROOT, "node_modules", packageName, binaryName),
|
|
43
|
+
// Hoisted installations
|
|
44
|
+
join(ROOT, "..", packageName, binaryName),
|
|
45
|
+
join(ROOT, "..", "..", packageName, binaryName),
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
for (const p of searchPaths) {
|
|
49
|
+
if (existsSync(p)) {
|
|
50
|
+
return p;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Not found - this is OK during development or if optional dep failed
|
|
55
|
+
console.warn(`Warning: Could not find ${packageName} binary`);
|
|
56
|
+
console.warn("tsbindgen will not be available on this platform");
|
|
57
|
+
return null;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const main = () => {
|
|
61
|
+
const binaryPath = findBinary();
|
|
62
|
+
if (!binaryPath) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const linkPath = join(ROOT, "tsbindgen");
|
|
67
|
+
|
|
68
|
+
// Remove existing link if present
|
|
69
|
+
if (existsSync(linkPath)) {
|
|
70
|
+
unlinkSync(linkPath);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Create symlink to platform binary
|
|
74
|
+
symlinkSync(binaryPath, linkPath);
|
|
75
|
+
chmodSync(linkPath, 0o755);
|
|
76
|
+
|
|
77
|
+
console.log(`Linked tsbindgen -> ${binaryPath}`);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
main();
|