zshy 0.0.6 → 0.0.7
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/README.md +134 -85
- package/{out → dist}/index.cjs +119 -74
- package/{out → dist}/index.js +120 -75
- package/{out → dist}/utils.cjs +12 -18
- package/{out → dist}/utils.d.cts +1 -0
- package/{out → dist}/utils.d.ts +1 -0
- package/{out → dist}/utils.js +11 -18
- package/package.json +10 -9
- /package/{out → dist}/index.d.cts +0 -0
- /package/{out → dist}/index.d.ts +0 -0
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<p align="center">
|
|
2
2
|
|
|
3
|
-
<h1 align="center"
|
|
4
|
-
<p align="center">The
|
|
3
|
+
<h1 align="center">🐒<br/><code>zshy</code></h1>
|
|
4
|
+
<p align="center">The no-bundler build tool for TypeScript libraries. Powered by <code>tsc</code>.
|
|
5
5
|
<br/>
|
|
6
6
|
by <a href="https://x.com/colinhacks">@colinhacks</a>
|
|
7
7
|
</p>
|
|
@@ -15,14 +15,14 @@
|
|
|
15
15
|
<a href="https://github.com/colinhacks/zshy" rel="nofollow"><img src="https://img.shields.io/github/stars/colinhacks/zshy" alt="stars"></a>
|
|
16
16
|
</p>
|
|
17
17
|
|
|
18
|
-
<div align="center">
|
|
18
|
+
<!-- <div align="center">
|
|
19
19
|
<a href="https://github.com/colinhacks/zshy">GitHub</a>
|
|
20
20
|
<span> • </span>
|
|
21
21
|
<a href="https://twitter.com/colinhacks">𝕏</a>
|
|
22
22
|
<span> • </span>
|
|
23
23
|
<a href="https://bsky.app/profile/colinhacks.com">Bluesky</a>
|
|
24
24
|
<br />
|
|
25
|
-
</div>
|
|
25
|
+
</div> -->
|
|
26
26
|
|
|
27
27
|
<br/>
|
|
28
28
|
<br/>
|
|
@@ -34,6 +34,8 @@
|
|
|
34
34
|
|
|
35
35
|
`zshy` is a simple, zero-config build tool for transpiling TypeScript libraries. It was originally created as internal build tool for [Zod](https://github.com/colinhacks/zod) but is now available as a general-purpose tool for TypeScript libraries.
|
|
36
36
|
|
|
37
|
+
<br/>
|
|
38
|
+
|
|
37
39
|
### Features
|
|
38
40
|
|
|
39
41
|
- 🧱 **Dual-module builds** — Builds ESM and CJS outputs from a single TypeScript source file
|
|
@@ -49,8 +51,47 @@
|
|
|
49
51
|
- 🐌 **Blazing fast** — Just kidding, it's slow. But [it's worth it](#is-it-fast).
|
|
50
52
|
|
|
51
53
|
<br/>
|
|
54
|
+
|
|
52
55
|
<br/>
|
|
53
|
-
<
|
|
56
|
+
<br/>
|
|
57
|
+
|
|
58
|
+
<h2 align="center">How does it work?</h2>
|
|
59
|
+
|
|
60
|
+
Each `.ts` file is transpiled to `.js/.d.ts` (ESM) and `.cjs/.d.cts` (CommonJS).
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
$ tree .
|
|
64
|
+
├── package.json # if type == "module"
|
|
65
|
+
├── src
|
|
66
|
+
│ └── index.ts
|
|
67
|
+
└── dist # generated
|
|
68
|
+
├── index.js
|
|
69
|
+
├── index.cjs
|
|
70
|
+
├── index.d.ts
|
|
71
|
+
└── index.d.cts
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
All relative `import`/`export` statements are rewritten to the appropriate extension during the build:
|
|
75
|
+
|
|
76
|
+
| Original path | Result (ESM) | Result (CJS) |
|
|
77
|
+
| ------------------ | ------------------ | ------------------- |
|
|
78
|
+
| `from "./util"` | `from "./util.js"` | `from "./util.cjs"` |
|
|
79
|
+
| `from "./util.ts"` | `from "./util.js"` | `from "./util.cjs"` |
|
|
80
|
+
| `from "./util.js"` | `from "./util.js"` | `from "./util.cjs"` |
|
|
81
|
+
|
|
82
|
+
Existing build tools (tsup, tsdown, etc) perform a similar transform during their bundling step. Unfortunately vanilla `tsc` [does not support extension rewriting](https://github.com/microsoft/TypeScript/issues/16577#issuecomment-754941937), leaving library authors with no choice but to use a bundler...
|
|
83
|
+
|
|
84
|
+
...until now. `zshy` implements extension rewriting during the `tsc` build step via the official [TypeScript Compiler API](https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API) — specifically, the `ts.TransformerFactory` API for defining AST-level code transforms. This obviates the need for a bundler. The result is a tool that I consider to be the "holy grail" of TypeScript library build tools:
|
|
85
|
+
|
|
86
|
+
- performs dual-module (ESM + CJS) builds
|
|
87
|
+
- type checks your code
|
|
88
|
+
- leverages `tsc` for gold-standard transpilation
|
|
89
|
+
- doesn't require a bundler
|
|
90
|
+
- doesn't require another config file (just `package.json` and `tsconfig.json`)
|
|
91
|
+
|
|
92
|
+
<br/>
|
|
93
|
+
<br/>
|
|
94
|
+
<h2 align="center">Quickstart</h2>
|
|
54
95
|
|
|
55
96
|
<br/>
|
|
56
97
|
|
|
@@ -83,30 +124,30 @@ Specify your package entrypoint with the `"zshy"` key in `package.json`.
|
|
|
83
124
|
```bash
|
|
84
125
|
$ npx zshy
|
|
85
126
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
127
|
+
→ Starting zshy build 🐒
|
|
128
|
+
→ Detected project root: /Users/colinmcd94/Documents/projects/zshy
|
|
129
|
+
→ Reading package.json from ./package.json
|
|
130
|
+
→ Reading tsconfig from ./tsconfig.json
|
|
131
|
+
→ Cleaning up outDir...
|
|
132
|
+
→ Determining entrypoints...
|
|
92
133
|
╔════════════╤════════════════╗
|
|
93
134
|
║ Subpath │ Entrypoint ║
|
|
94
135
|
╟────────────┼────────────────╢
|
|
95
136
|
║ "my-pkg" │ ./src/index.ts ║
|
|
96
137
|
╚════════════╧════════════════╝
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
║ Location │ Resolved
|
|
100
|
-
|
|
101
|
-
║ rootDir │ ./src
|
|
102
|
-
║ outDir │ ./
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
138
|
+
→ Resolved build paths:
|
|
139
|
+
╔══════════╤════════════════╗
|
|
140
|
+
║ Location │ Resolved pathh ║
|
|
141
|
+
╟──────────┼────────────────╢
|
|
142
|
+
║ rootDir │ ./src ║
|
|
143
|
+
║ outDir │ ./dist ║
|
|
144
|
+
╚══════════╧════════════════╝
|
|
145
|
+
→ Package is an ES module (package.json#/type is "module")
|
|
146
|
+
→ Building CJS... (rewriting .ts -> .cjs/.d.cts)
|
|
147
|
+
→ Building ESM...
|
|
148
|
+
→ Updating package.json#/exports...
|
|
149
|
+
→ Updating package.json#/bin...
|
|
150
|
+
→ Build complete! ✅
|
|
110
151
|
```
|
|
111
152
|
|
|
112
153
|
Alernatively, add a `"build"` script to your `package.json`:
|
|
@@ -126,6 +167,26 @@ Then, to run a build:
|
|
|
126
167
|
$ npm run build
|
|
127
168
|
```
|
|
128
169
|
|
|
170
|
+
<br/>
|
|
171
|
+
<br/>
|
|
172
|
+
|
|
173
|
+
<h2 align="center">Usage</h2>
|
|
174
|
+
|
|
175
|
+
<br/>
|
|
176
|
+
|
|
177
|
+
### Flags
|
|
178
|
+
|
|
179
|
+
```sh
|
|
180
|
+
$ npm zshy --help
|
|
181
|
+
Usage: zshy [options]
|
|
182
|
+
|
|
183
|
+
Options:
|
|
184
|
+
-h, --help Show this help message
|
|
185
|
+
-p, --project <path> Path to tsconfig (default: ./tsconfig.json)
|
|
186
|
+
--verbose Enable verbose output
|
|
187
|
+
--dry-run Don't write any files or update package.json
|
|
188
|
+
```
|
|
189
|
+
|
|
129
190
|
<br/>
|
|
130
191
|
|
|
131
192
|
### Subpaths and wildcards
|
|
@@ -150,16 +211,16 @@ Multi-entrypoint packages can specify subpaths or wildcard exports with `package
|
|
|
150
211
|
<details>
|
|
151
212
|
<summary>View typical build output</summary>
|
|
152
213
|
|
|
153
|
-
When you run a build, you'll see something like this:
|
|
214
|
+
When you run a build, you'''ll see something like this:
|
|
154
215
|
|
|
155
216
|
```bash
|
|
156
217
|
$ npx zshy
|
|
157
218
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
219
|
+
→ Starting zshy build... 🐒
|
|
220
|
+
→ Detected project root: /path/to/my-pkg
|
|
221
|
+
→ Reading package.json from ./package.json
|
|
222
|
+
→ Reading tsconfig from ./tsconfig.json
|
|
223
|
+
→ Determining entrypoints...
|
|
163
224
|
╔════════════════════╤═════════════════════════════╗
|
|
164
225
|
║ Subpath │ Entrypoint ║
|
|
165
226
|
╟────────────────────┼─────────────────────────────╢
|
|
@@ -167,18 +228,18 @@ $ npx zshy
|
|
|
167
228
|
║ "my-pkg/utils" │ ./src/utils.ts ║
|
|
168
229
|
║ "my-pkg/plugins/*" │ ./src/plugins/* (5 matches) ║
|
|
169
230
|
╚════════════════════╧═════════════════════════════╝
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
║ Location │ Resolved
|
|
173
|
-
|
|
174
|
-
║ rootDir │ ./src
|
|
175
|
-
║ outDir │ ./
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
231
|
+
→ Resolved build paths:
|
|
232
|
+
╔══════════╤════════════════╗
|
|
233
|
+
║ Location │ Resolved pathh ║
|
|
234
|
+
╟──────────┼────────────────╢
|
|
235
|
+
║ rootDir │ ./src ║
|
|
236
|
+
║ outDir │ ./dist ║
|
|
237
|
+
╚══════════╧════════════════╝
|
|
238
|
+
→ Package is ES module (package.json#/type is "module")
|
|
239
|
+
→ Building CJS... (rewriting .ts -> .cjs/.d.cts)
|
|
240
|
+
→ Building ESM...
|
|
241
|
+
→ Updating package.json exports...
|
|
242
|
+
→ Build complete! ✅
|
|
182
243
|
```
|
|
183
244
|
|
|
184
245
|
And the generated `"exports"` map will look like this:
|
|
@@ -189,19 +250,19 @@ And the generated `"exports"` map will look like this:
|
|
|
189
250
|
// ...
|
|
190
251
|
+ "exports": {
|
|
191
252
|
+ ".": {
|
|
192
|
-
+ "types": "./
|
|
193
|
-
+ "import": "./
|
|
194
|
-
+ "require": "./
|
|
253
|
+
+ "types": "./dist/index.d.cts",
|
|
254
|
+
+ "import": "./dist/index.js",
|
|
255
|
+
+ "require": "./dist/index.cjs"
|
|
195
256
|
+ },
|
|
196
257
|
+ "./utils": {
|
|
197
|
-
+ "types": "./
|
|
198
|
-
+ "import": "./
|
|
199
|
-
+ "require": "./
|
|
258
|
+
+ "types": "./dist/utils.d.cts",
|
|
259
|
+
+ "import": "./dist/utils.js",
|
|
260
|
+
+ "require": "./dist/utils.cjs"
|
|
200
261
|
+ },
|
|
201
262
|
+ "./plugins/*": {
|
|
202
|
-
+ "types": "./
|
|
203
|
-
+ "import": "./
|
|
204
|
-
+ "require": "./
|
|
263
|
+
+ "types": "./dist/src/plugins/*",
|
|
264
|
+
+ "import": "./dist/src/plugins/*",
|
|
265
|
+
+ "require": "./dist/src/plugins/*"
|
|
205
266
|
+ }
|
|
206
267
|
+ }
|
|
207
268
|
}
|
|
@@ -211,15 +272,16 @@ And the generated `"exports"` map will look like this:
|
|
|
211
272
|
|
|
212
273
|
<br/>
|
|
213
274
|
|
|
214
|
-
###
|
|
275
|
+
### Building CLIs (`"bin"` support)
|
|
215
276
|
|
|
216
277
|
If your package is a CLI, specify your CLI entrypoint in `package.json#/zshy/bin`. `zshy` will include this entrypoint in your builds and automatically set `"bin"` in your package.json.
|
|
217
278
|
|
|
218
|
-
```
|
|
279
|
+
```jsonc
|
|
219
280
|
{
|
|
220
281
|
// package.json
|
|
221
282
|
"name": "my-cli",
|
|
222
283
|
"version": "1.0.0",
|
|
284
|
+
"type": "module",
|
|
223
285
|
"zshy": {
|
|
224
286
|
"bin": "./src/cli.ts" // 👈 specify CLI entrypoint
|
|
225
287
|
}
|
|
@@ -238,30 +300,17 @@ When you run `zshy`, it will automatically add the appropriate `"bin"` field to
|
|
|
238
300
|
"bin": "./src/cli.ts"
|
|
239
301
|
},
|
|
240
302
|
+ "bin": {
|
|
241
|
-
+ "my-cli": "./
|
|
303
|
+
+ "my-cli": "./dist/cli.cjs" // CLI entrypoint
|
|
242
304
|
+ }
|
|
243
305
|
}
|
|
244
306
|
```
|
|
245
307
|
|
|
246
308
|
<br/>
|
|
247
309
|
|
|
248
|
-
### Flags
|
|
249
|
-
|
|
250
|
-
```sh
|
|
251
|
-
$ npm zshy --help
|
|
252
|
-
Usage: zshy [options]
|
|
253
|
-
|
|
254
|
-
Options:
|
|
255
|
-
-h, --help Show this help message
|
|
256
|
-
-p, --project <path> Path to tsconfig.json file
|
|
257
|
-
--verbose Enable verbose output
|
|
258
|
-
--dry-run Don't write any files or update package.json
|
|
259
|
-
```
|
|
260
|
-
|
|
261
310
|
<br/>
|
|
262
311
|
<br/>
|
|
263
312
|
|
|
264
|
-
<h2 align="center">FAQ</h2>
|
|
313
|
+
<h2 align="center">FAQ for nerds</h2>
|
|
265
314
|
|
|
266
315
|
<br/>
|
|
267
316
|
|
|
@@ -392,7 +441,7 @@ Use whatever you like; `zshy` will rewrite all imports/exports properly during t
|
|
|
392
441
|
|
|
393
442
|
<br/>
|
|
394
443
|
|
|
395
|
-
###
|
|
444
|
+
### What about `package.json#/exports`?
|
|
396
445
|
|
|
397
446
|
Your exports map is automatically written into your `package.json` when you run `zshy`. The generated exports map looks like this:
|
|
398
447
|
|
|
@@ -407,18 +456,18 @@ Your exports map is automatically written into your `package.json` when you run
|
|
|
407
456
|
},
|
|
408
457
|
+ "exports": { // auto-generated by zshy
|
|
409
458
|
+ ".": {
|
|
410
|
-
+ "types": "./
|
|
411
|
-
+ "import": "./
|
|
412
|
-
+ "require": "./
|
|
459
|
+
+ "types": "./dist/index.d.cts",
|
|
460
|
+
+ "import": "./dist/index.js",
|
|
461
|
+
+ "require": "./dist/index.cjs"
|
|
413
462
|
+ },
|
|
414
463
|
+ "./utils": {
|
|
415
|
-
+ "types": "./
|
|
416
|
-
+ "import": "./
|
|
417
|
-
+ "require": "./
|
|
464
|
+
+ "types": "./dist/utils.d.cts",
|
|
465
|
+
+ "import": "./dist/utils.js",
|
|
466
|
+
+ "require": "./dist/utils.cjs"
|
|
418
467
|
+ },
|
|
419
468
|
+ "./plugins/*": {
|
|
420
|
-
+ "import": "./
|
|
421
|
-
+ "require": "./
|
|
469
|
+
+ "import": "./dist/src/plugins/*",
|
|
470
|
+
+ "require": "./dist/src/plugins/*"
|
|
422
471
|
+ }
|
|
423
472
|
+ }
|
|
424
473
|
}
|
|
@@ -449,7 +498,7 @@ Yes! This is one of the key reasons `zshy` was originally developed. Many enviro
|
|
|
449
498
|
|
|
450
499
|
- Node.js v12.7 or earlier
|
|
451
500
|
- React Native - The Metro bundler does not support `"exports"` by default
|
|
452
|
-
- TypeScript projects with legacy configs — e
|
|
501
|
+
- TypeScript projects with legacy configs — e.g. `"module": "commonjs"`
|
|
453
502
|
|
|
454
503
|
This causes issues for packages that want to use subpath imports to structure their package. Fortunately `zshy` unlocks a workaround I call a _flat build_:
|
|
455
504
|
|
|
@@ -458,12 +507,12 @@ This causes issues for packages that want to use subpath imports to structure th
|
|
|
458
507
|
3. Set `outDir: "."` in your `tsconfig.json`
|
|
459
508
|
4. Configure `"exclude"` in `package.json` to exclude all source files:
|
|
460
509
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
510
|
+
```jsonc
|
|
511
|
+
{
|
|
512
|
+
// ...
|
|
513
|
+
"exclude": ["**/*.ts", "**/*.tsx", "**/*.cts", "**/*.mts", "node_modules"]
|
|
514
|
+
}
|
|
515
|
+
```
|
|
467
516
|
|
|
468
517
|
With this setup, your build outputs (`index.js`, etc) will be written to disk alongside to their corresponding source files. Older environments will resolve imports like `"your-library/utils"` to `"your-library/utils/index.js"`, effectively simulating subpath imports in environments that don't support them.
|
|
469
518
|
|
|
@@ -473,5 +522,5 @@ With this setup, your build outputs (`index.js`, etc) will be written to disk al
|
|
|
473
522
|
|
|
474
523
|
Not really. It uses `tsc` to typecheck your codebase, which is a lot slower than using a bundler that strips types. That said:
|
|
475
524
|
|
|
476
|
-
1.
|
|
525
|
+
1. You _should_ be type checking your code during builds
|
|
477
526
|
2. TypeScript is [about to get 10x faster](https://devblogs.microsoft.com/typescript/typescript-native-port/)
|