Skip to content

Commit 74dd741

Browse files
committed
feat: add zig-dtsx
In order to get better binary results chore: wip chore: wip chore: improve docs chore: minor binary perf improvement
1 parent 07e668b commit 74dd741

37 files changed

Lines changed: 9206 additions & 396 deletions

.github/workflows/ci.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,41 @@ jobs:
8383
- name: Unit Test
8484
run: bun test
8585

86+
zig-test:
87+
runs-on: ubuntu-latest
88+
89+
steps:
90+
- uses: actions/checkout@v6.0.2
91+
92+
- name: Setup Zig
93+
uses: mlugg/setup-zig@v2
94+
95+
- name: Build Zig
96+
working-directory: packages/zig-dtsx
97+
run: zig build
98+
99+
- name: Run Zig Unit Tests
100+
working-directory: packages/zig-dtsx
101+
run: zig build test
102+
103+
- name: Install Bun
104+
uses: oven-sh/setup-bun@v2.1.2
105+
106+
- name: Use cached node_modules
107+
uses: actions/cache@v4.3.0
108+
with:
109+
path: node_modules
110+
key: node-modules-${{ hashFiles('**/bun.lock') }}
111+
restore-keys: |
112+
node-modules-
113+
114+
- name: Install Dependencies
115+
run: bun install
116+
117+
- name: Run Zig Fixture Tests
118+
working-directory: packages/zig-dtsx
119+
run: bun test test/zig-dtsx.test.ts
120+
86121
publish-commit:
87122
runs-on: ubuntu-latest
88123

.github/workflows/release.yml

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,44 @@ jobs:
2222
- name: Setup Bun
2323
uses: oven-sh/setup-bun@v2
2424

25+
- name: Setup Zig
26+
uses: mlugg/setup-zig@v2
27+
2528
- name: Setup Pantry
2629
uses: home-lang/pantry-setup@v1
2730

2831
- name: Install Dependencies
2932
run: bun install
3033

34+
- name: Build Zig CLI Binaries
35+
working-directory: packages/zig-dtsx
36+
run: |
37+
mkdir -p ../dtsx/bin
38+
39+
# Linux x64
40+
zig build cli -Doptimize=ReleaseFast -Dtarget=x86_64-linux
41+
zip -j ../dtsx/bin/dtsx-linux-x64.zip zig-out/bin/zig-dtsx
42+
43+
# Linux arm64
44+
zig build cli -Doptimize=ReleaseFast -Dtarget=aarch64-linux
45+
zip -j ../dtsx/bin/dtsx-linux-arm64.zip zig-out/bin/zig-dtsx
46+
47+
# macOS x64
48+
zig build cli -Doptimize=ReleaseFast -Dtarget=x86_64-macos
49+
zip -j ../dtsx/bin/dtsx-darwin-x64.zip zig-out/bin/zig-dtsx
50+
51+
# macOS arm64
52+
zig build cli -Doptimize=ReleaseFast -Dtarget=aarch64-macos
53+
zip -j ../dtsx/bin/dtsx-darwin-arm64.zip zig-out/bin/zig-dtsx
54+
55+
# Windows x64
56+
zig build cli -Doptimize=ReleaseFast -Dtarget=x86_64-windows
57+
zip -j ../dtsx/bin/dtsx-windows-x64.zip zig-out/bin/zig-dtsx.exe
58+
59+
# FreeBSD x64
60+
zig build cli -Doptimize=ReleaseFast -Dtarget=x86_64-freebsd
61+
zip -j ../dtsx/bin/dtsx-freebsd-x64.zip zig-out/bin/zig-dtsx
62+
3163
- name: Publish
3264
run: pantry npm:publish --access public
3365
env:
@@ -39,8 +71,9 @@ jobs:
3971
files: |
4072
packages/dtsx/bin/dtsx-linux-x64.zip
4173
packages/dtsx/bin/dtsx-linux-arm64.zip
42-
packages/dtsx/bin/dtsx-windows-x64.zip
4374
packages/dtsx/bin/dtsx-darwin-x64.zip
4475
packages/dtsx/bin/dtsx-darwin-arm64.zip
76+
packages/dtsx/bin/dtsx-windows-x64.zip
77+
packages/dtsx/bin/dtsx-freebsd-x64.zip
4578
env:
4679
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,6 @@ test/debug
2020
/pantry
2121
benchmark/oxc-emit
2222
/packages/dtsx/test/.checker-test-fixtures
23+
/packages/zig-dtsx/.zig-cache
24+
/packages/zig-dtsx/zig-out
25+
*.node

README.md

Lines changed: 178 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
1313
## Features
1414

15+
- 🎯 Narrowest possible type inference — no `isolatedDeclarations` needed
1516
- ⚡ Extremely fast .d.ts generation
1617
- 🔄 Parallel processing support
1718
- 📥 Stdin/stdout support for piping
@@ -21,6 +22,122 @@
2122
- 👀 Watch mode for development
2223
- ✅ Built-in validation
2324

25+
## Type Inference — dtsx vs oxc vs tsc
26+
27+
dtsx generates the **narrowest possible types** from your source values — no `isolatedDeclarations` flag required, no explicit type annotations needed. Where other tools emit broad types like `string`, `number`, or `number[]`, dtsx preserves the exact literal types from your code.
28+
29+
All output below is real — same source file, three tools, nothing hand-edited.
30+
31+
### Literal Types
32+
33+
```ts
34+
// Source
35+
export const port = 3000
36+
export const debug = true
37+
export const items = [1, 2, 3]
38+
```
39+
40+
| | `port` | `debug` | `items` |
41+
|---|---|---|---|
42+
| **dtsx** | **`3000`** | **`true`** | **`readonly [1, 2, 3]`** |
43+
| oxc | `3e3` _(mangled!)_ | `boolean` | `unknown` _(error)_ |
44+
| tsc | `3000` | `true` | `number[]` |
45+
46+
> oxc and tsc only narrow arrays when you add explicit `as const`. dtsx always narrows.
47+
48+
### Object Properties
49+
50+
```ts
51+
// Source
52+
export const config = {
53+
apiUrl: 'https://api.stacksjs.org',
54+
timeout: 5000,
55+
features: { darkMode: true, notifications: false },
56+
routes: ['/', '/about', '/contact'],
57+
}
58+
```
59+
60+
| Property | dtsx | oxc | tsc |
61+
|---|---|---|---|
62+
| `apiUrl` | **`'https://api.stacksjs.org'`** | `string` | `string` |
63+
| `timeout` | **`5000`** | `number` | `number` |
64+
| `darkMode` | **`true`** | `boolean` | `boolean` |
65+
| `routes` | **`readonly ['/', '/about', '/contact']`** | `unknown` _(error)_ | `string[]` |
66+
67+
### Generic Type Replacement
68+
69+
dtsx replaces broad generic annotations with narrow types inferred from the actual value:
70+
71+
```ts
72+
// Source — generic index signature
73+
export const conf: { [key: string]: string } = {
74+
apiUrl: 'https://api.stacksjs.org',
75+
timeout: '5000',
76+
}
77+
```
78+
79+
| Tool | Output |
80+
|---|---|
81+
| **dtsx** | `{ apiUrl: 'https://api.stacksjs.org'; timeout: '5000' }` |
82+
| oxc | `{ [key: string]: string }` — kept broad, lost all property info |
83+
| tsc | `{ [key: string]: string }` — kept broad, lost all property info |
84+
85+
### Deep as const
86+
87+
```ts
88+
// Source
89+
export const CONFIG = {
90+
api: { baseUrl: 'https://api.example.com', timeout: 5000, retries: 3 },
91+
features: { darkMode: true, notifications: false },
92+
routes: ['/', '/about', '/contact'],
93+
} as const
94+
```
95+
96+
dtsx output — every value preserved as a literal, arrays become readonly tuples, full depth:
97+
98+
```ts
99+
export declare const CONFIG: {
100+
api: {
101+
baseUrl: 'https://api.example.com';
102+
timeout: 5000;
103+
retries: 3
104+
};
105+
features: {
106+
darkMode: true;
107+
notifications: false
108+
};
109+
routes: readonly ['/', '/about', '/contact']
110+
};
111+
```
112+
113+
### Promise & Complex Types
114+
115+
```ts
116+
export const promiseVal = Promise.resolve(42)
117+
```
118+
119+
| Tool | Output |
120+
|---|---|
121+
| **dtsx** | `Promise<42>` |
122+
| oxc | `unknown` _(error — requires explicit annotation)_ |
123+
| tsc | `Promise<number>` |
124+
125+
### Full Comparison
126+
127+
| Declaration | dtsx | oxc | tsc |
128+
|---|---|---|---|
129+
| `const port = 3000` | `3000` | `3e3` | `3000` |
130+
| `const debug = true` | `true` | `boolean` | `true` |
131+
| `const items = [1,2,3]` | `readonly [1,2,3]` | `unknown` (error) | `number[]` |
132+
| `config.apiUrl` | `'https://...'` | `string` | `string` |
133+
| `config.timeout` | `5000` | `number` | `number` |
134+
| `config.routes` | readonly tuple | `unknown` (error) | `string[]` |
135+
| `conf` _(generic annotation)_ | exact properties | `{ [key]: string }` | `{ [key]: string }` |
136+
| `Promise.resolve(42)` | `Promise<42>` | `unknown` (error) | `Promise<number>` |
137+
| **Errors** | **0** | **3** | **0** |
138+
139+
dtsx infers the narrowest possible type from every value — no `as const`, no explicit annotations, no `isolatedDeclarations` flag required. Just write normal TypeScript.
140+
24141
## Install
25142

26143
```bash
@@ -40,12 +157,12 @@ pkgx install dtsx # wip
40157

41158
There are two ways of using this ".d.ts generation" tool: _as a library or as a CLI._
42159

43-
_But before you get started, please ensure you enabled `isolatedDeclarations` in your `tsconfig.json` file._
160+
_dtsx works out of the box — no `isolatedDeclarations` required. It infers narrow types directly from your source values. If you do enable `isolatedDeclarations`, dtsx uses it as a fast path to skip initializer parsing when explicit type annotations are present._
44161

45162
```json
46163
{
47164
"compilerOptions": {
48-
"isolatedDeclarations": true
165+
"isolatedDeclarations": true // optional — dtsx works great without it
49166
}
50167
}
51168
```
@@ -229,6 +346,65 @@ cat src/utils.ts | dtsx stdin > dist/utils.d.ts
229346

230347
To learn more, head over to the [documentation](https://dtsx.stacksjs.org/).
231348

349+
## Benchmarks
350+
351+
Benchmarked on Apple M3 Pro, macOS _(bun 1.3.10, arm64-darwin)_. Run `bun benchmark/index.ts` to reproduce.
352+
353+
### In-Process API — Cached
354+
355+
_dtsx uses smart caching (hash check + cache hit) for watch mode, incremental builds, and CI pipelines._
356+
357+
| Tool | Small (~50 lines) | Medium (~100 lines) | Large (~330 lines) | XLarge (~1050 lines) |
358+
|------|-------------------|---------------------|--------------------|--------------------|
359+
| **dtsx (cached)** | **0.95 µs** | **2.16 µs** | **19.84 µs** | **105.83 µs** |
360+
| zig-dtsx | 4.60 µs _(4.8x)_ | 11.27 µs _(5.2x)_ | 26.75 µs _(1.3x)_ | 230.91 µs _(2.2x)_ |
361+
| oxc-transform | 6.76 µs _(7.1x)_ | 20.54 µs _(9.5x)_ | 79.54 µs _(4.0x)_ | 519.44 µs _(4.9x)_ |
362+
| tsc | 194.34 µs _(205x)_ | 438.12 µs _(203x)_ | 1.14 ms _(57x)_ | 4.20 ms _(40x)_ |
363+
364+
### In-Process API — No Cache
365+
366+
_Cache cleared every iteration for raw single-transform comparison._
367+
368+
| Tool | Small (~50 lines) | Medium (~100 lines) | Large (~330 lines) | XLarge (~1050 lines) |
369+
|------|-------------------|---------------------|--------------------|--------------------|
370+
| **zig-dtsx** | **4.68 µs** | **11.43 µs** | **27.89 µs** | **230.32 µs** |
371+
| oxc-transform | 6.95 µs _(1.5x)_ | 21.05 µs _(1.8x)_ | 81.46 µs _(2.9x)_ | 519.01 µs _(2.3x)_ |
372+
| dtsx (no-cache) | 10.42 µs _(2.2x)_ | 23.06 µs _(2.0x)_ | 67.79 µs _(2.4x)_ | 400.81 µs _(1.7x)_ |
373+
| tsc | 155.16 µs _(33x)_ | 389.90 µs _(34x)_ | 918.21 µs _(33x)_ | 3.82 ms _(17x)_ |
374+
375+
### CLI — Single File
376+
377+
_All tools run as compiled native binaries via subprocess._
378+
379+
| Tool | Small (~50 lines) | Medium (~100 lines) | Large (~330 lines) | XLarge (~1050 lines) |
380+
|------|-------------------|---------------------|--------------------|--------------------|
381+
| **zig-dtsx** | **2.32 ms** | **2.31 ms** | **2.42 ms** | **2.46 ms** |
382+
| oxc | 16.51 ms _(7.1x)_ | 15.71 ms _(6.8x)_ | 16.41 ms _(6.8x)_ | 16.14 ms _(6.6x)_ |
383+
| dtsx | 29.42 ms _(12.7x)_ | 29.36 ms _(12.7x)_ | 30.96 ms _(12.8x)_ | 32.30 ms _(13.1x)_ |
384+
| tsgo | 38.70 ms _(16.7x)_ | 41.97 ms _(18.2x)_ | 42.09 ms _(17.4x)_ | 52.83 ms _(21.5x)_ |
385+
| tsc | 347.31 ms _(150x)_ | 374.30 ms _(162x)_ | 376.76 ms _(156x)_ | 403.00 ms _(164x)_ |
386+
387+
### Multi-File Project
388+
389+
| Tool | 50 files | 100 files | 500 files |
390+
|------|----------|-----------|-----------|
391+
| **zig-dtsx** | **12.16 ms** | **23.23 ms** | **109.33 ms** |
392+
| oxc | 35.38 ms _(2.9x)_ | 58.62 ms _(2.5x)_ | 402.32 ms _(3.7x)_ |
393+
| dtsx | 55.21 ms _(4.5x)_ | 79.14 ms _(3.4x)_ | 281.40 ms _(2.6x)_ |
394+
| tsgo | 210.54 ms _(17.3x)_ | 413.69 ms _(17.8x)_ | 2.18 s _(20.0x)_ |
395+
| tsc | 774.44 ms _(63.7x)_ | 1.18 s _(50.6x)_ | 3.99 s _(36.5x)_ |
396+
397+
### Binary Size
398+
399+
| Platform | Zig Binary | Bun Binary | Reduction |
400+
|----------|-----------|------------|-----------|
401+
| macOS arm64 | 659 KB | 61 MB | **95x smaller** |
402+
| macOS x64 | 716 KB | 67 MB | **96x smaller** |
403+
| Linux x64 | 6.2 MB | 108 MB | **17x smaller** |
404+
| Linux arm64 | 6.3 MB | 103 MB | **16x smaller** |
405+
| Windows x64 | 1.0 MB | 101 MB | **101x smaller** |
406+
| FreeBSD x64 | 5.5 MB |||
407+
232408
## Testing
233409

234410
```bash

0 commit comments

Comments
 (0)