@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 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();