zshy 0.0.9 β 0.0.11
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 +98 -67
- package/dist/index.cjs +3 -688
- package/dist/index.js +3 -652
- package/dist/main.cjs +679 -0
- package/dist/main.d.cts +1 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +640 -0
- package/dist/utils.cjs +7 -16
- package/dist/utils.d.cts +2 -1
- package/dist/utils.d.ts +2 -1
- package/dist/utils.js +5 -15
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -39,13 +39,13 @@
|
|
|
39
39
|
### Features
|
|
40
40
|
|
|
41
41
|
- π§± **Dual-module builds** β Builds ESM and CJS outputs from a single TypeScript source file
|
|
42
|
-
- π **Powered by `tsc`** β
|
|
43
|
-
-
|
|
42
|
+
- π **Powered by `tsc`** β The gold standard for TypeScript transpilation
|
|
43
|
+
- π¦ **Bundler-free** β No bundler or bundler configs involved
|
|
44
|
+
- π¦ **No config file** β Reads from your `package.json` and `tsconfig.json`
|
|
44
45
|
- π **Declarative entrypoint map** β Specify your TypeScript entrypoints in `package.json#/zshy`
|
|
45
46
|
- π€ **Auto-generated `"exports"`** β Writes `"exports"` map directly into your `package.json`
|
|
46
47
|
- π **CLI-friendly** β First-class `"bin"` support
|
|
47
|
-
- π **
|
|
48
|
-
- π **Supports extensionless imports** β Use any import syntax TypeScript supports: extensionless, `.js`, `.ts`
|
|
48
|
+
- π **Unopinionated** β Use any file structure or import extension syntax you like
|
|
49
49
|
- βοΈ **Supports `.tsx`** β Rewrites to `.js/.cjs/.mjs` per your `tsconfig.json#/jsx*` settings
|
|
50
50
|
- π± **Supports React Native** β Supports a [flat build mode](#can-it-support-react-native-legacy-or-non-nodejs-environments) designed for bundlers that don't support `package.json#/exports`
|
|
51
51
|
- π **Blazing fast** β Just kidding, it's slow. But [it's worth it](#is-it-fast).
|
|
@@ -55,42 +55,6 @@
|
|
|
55
55
|
<br/>
|
|
56
56
|
<br/>
|
|
57
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
58
|
<h2 align="center">Quickstart</h2>
|
|
95
59
|
|
|
96
60
|
<br/>
|
|
@@ -105,15 +69,15 @@ pnpm add --save-dev zshy
|
|
|
105
69
|
|
|
106
70
|
<br/>
|
|
107
71
|
|
|
108
|
-
### 2.
|
|
109
|
-
|
|
110
|
-
Specify your package entrypoint with the `"zshy"` key in `package.json`.
|
|
72
|
+
### 2. Specify your entrypoint(s) in `package.json#zshy`:
|
|
111
73
|
|
|
112
|
-
```
|
|
74
|
+
```diff
|
|
113
75
|
{
|
|
114
76
|
"name": "my-pkg",
|
|
115
77
|
"version": "1.0.0",
|
|
116
|
-
|
|
78
|
+
+ "zshy": {
|
|
79
|
+
+ ".": "./src/index.ts"
|
|
80
|
+
+ }
|
|
117
81
|
}
|
|
118
82
|
```
|
|
119
83
|
|
|
@@ -121,8 +85,10 @@ Specify your package entrypoint with the `"zshy"` key in `package.json`.
|
|
|
121
85
|
|
|
122
86
|
### 3. Run a build
|
|
123
87
|
|
|
88
|
+
Run a build with `npx zshy`:
|
|
89
|
+
|
|
124
90
|
```bash
|
|
125
|
-
$ npx zshy
|
|
91
|
+
$ npx zshy # use --dry-run to try it out without writing/updating files
|
|
126
92
|
|
|
127
93
|
β Starting zshy build π
|
|
128
94
|
β Detected project root: /Users/colinmcd94/Documents/projects/zshy
|
|
@@ -137,7 +103,7 @@ $ npx zshy
|
|
|
137
103
|
ββββββββββββββ§βββββββββββββββββ
|
|
138
104
|
β Resolved build paths:
|
|
139
105
|
ββββββββββββ€βββββββββββββββββ
|
|
140
|
-
β Location β Resolved
|
|
106
|
+
β Location β Resolved path β
|
|
141
107
|
ββββββββββββΌβββββββββββββββββ’
|
|
142
108
|
β rootDir β ./src β
|
|
143
109
|
β outDir β ./dist β
|
|
@@ -150,22 +116,80 @@ $ npx zshy
|
|
|
150
116
|
β Build complete! β
|
|
151
117
|
```
|
|
152
118
|
|
|
153
|
-
|
|
119
|
+
> **Add a `"build"` script to your `package.json`**
|
|
120
|
+
>
|
|
121
|
+
> ```diff
|
|
122
|
+
> {
|
|
123
|
+
> // ...
|
|
124
|
+
> "scripts": {
|
|
125
|
+
> + "build": "zshy"
|
|
126
|
+
> }
|
|
127
|
+
> }
|
|
128
|
+
> ```
|
|
129
|
+
>
|
|
130
|
+
> Then, to run a build:
|
|
131
|
+
>
|
|
132
|
+
> ```bash
|
|
133
|
+
> $ npm run build
|
|
134
|
+
> ```
|
|
135
|
+
|
|
136
|
+
<br/>
|
|
137
|
+
|
|
138
|
+
<br/>
|
|
139
|
+
|
|
140
|
+
<h2 align="center">How it works</h2>
|
|
141
|
+
|
|
142
|
+
Vanilla `tsc` does not perform _extension rewriting_; it will only ever transpile a `.ts` file to a `.js` file (never `.cjs` or `.mjs`). This is the fundamental limitation that forces library authors to use bundlers or bundler-powered tools like `tsup`, `tsdown`, or `unbuild`...
|
|
143
|
+
|
|
144
|
+
...until now! `zshy` works around this limitation using the official [TypeScript Compiler API](https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API), which provides some powerful (and criminally under-utilized) hooks for customizing file extensions during the `tsc` build process.
|
|
145
|
+
|
|
146
|
+
Using these hooks, `zshy` transpiles each `.ts` file to `.js/.d.ts` (ESM) and `.cjs/.d.cts` (CommonJS):
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
$ tree .
|
|
150
|
+
βββ package.json
|
|
151
|
+
βββ src
|
|
152
|
+
β βββ index.ts
|
|
153
|
+
βββ dist # generated
|
|
154
|
+
βββ index.js
|
|
155
|
+
βββ index.cjs
|
|
156
|
+
βββ index.d.ts
|
|
157
|
+
βββ index.d.cts
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Similarly, all relative `import`/`export` statements are rewritten to include the appropriate file extension. (Other tools like `tsup` or `tsdown` do the same, but they require a bundler to do so.)
|
|
161
|
+
|
|
162
|
+
| Original path | Result (ESM) | Result (CJS) |
|
|
163
|
+
| ------------------ | ------------------ | ------------------- |
|
|
164
|
+
| `from "./util"` | `from "./util.js"` | `from "./util.cjs"` |
|
|
165
|
+
| `from "./util.ts"` | `from "./util.js"` | `from "./util.cjs"` |
|
|
166
|
+
| `from "./util.js"` | `from "./util.js"` | `from "./util.cjs"` |
|
|
167
|
+
|
|
168
|
+
Finally, `zshy` automatically writes `"exports"` into your `package.json`:
|
|
154
169
|
|
|
155
170
|
```diff
|
|
156
171
|
{
|
|
157
172
|
// ...
|
|
158
|
-
"
|
|
159
|
-
|
|
160
|
-
}
|
|
173
|
+
"zshy": {
|
|
174
|
+
"exports": "./src/index.ts"
|
|
175
|
+
},
|
|
176
|
+
+ "exports": { // auto-generated by zshy
|
|
177
|
+
+ ".": {
|
|
178
|
+
+ "types": "./dist/index.d.cts",
|
|
179
|
+
+ "import": "./dist/index.js",
|
|
180
|
+
+ "require": "./dist/index.cjs"
|
|
181
|
+
+ }
|
|
182
|
+
+ }
|
|
161
183
|
}
|
|
162
184
|
```
|
|
163
185
|
|
|
164
|
-
|
|
186
|
+
The result is a tool that I consider to be the "holy grail" of TypeScript library build tools:
|
|
165
187
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
188
|
+
- performs dual-module (ESM + CJS) builds
|
|
189
|
+
- type checks your code
|
|
190
|
+
- leverages `tsc` for gold-standard transpilation
|
|
191
|
+
- doesn't require a bundler
|
|
192
|
+
- doesn't require another config file (just `package.json` and `tsconfig.json`)
|
|
169
193
|
|
|
170
194
|
<br/>
|
|
171
195
|
<br/>
|
|
@@ -288,7 +312,7 @@ If your package is a CLI, specify your CLI entrypoint in `package.json#/zshy/bin
|
|
|
288
312
|
}
|
|
289
313
|
```
|
|
290
314
|
|
|
291
|
-
|
|
315
|
+
The `"bin"` field is automatically written into your `package.json`:
|
|
292
316
|
|
|
293
317
|
```diff
|
|
294
318
|
{
|
|
@@ -300,13 +324,12 @@ When you run `zshy`, it will automatically add the appropriate `"bin"` field to
|
|
|
300
324
|
"bin": "./src/cli.ts"
|
|
301
325
|
},
|
|
302
326
|
+ "bin": {
|
|
303
|
-
+ "my-cli": "./dist/cli.cjs" //
|
|
327
|
+
+ "my-cli": "./dist/cli.cjs" // CommonJS entrypoint
|
|
304
328
|
+ }
|
|
305
329
|
}
|
|
306
330
|
```
|
|
307
331
|
|
|
308
332
|
<br/>
|
|
309
|
-
|
|
310
333
|
<br/>
|
|
311
334
|
<br/>
|
|
312
335
|
|
|
@@ -409,16 +432,22 @@ $ tree dist
|
|
|
409
432
|
|
|
410
433
|
### How does extension rewriting work?
|
|
411
434
|
|
|
412
|
-
`zshy` uses the [TypeScript Compiler API](https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API) to rewrite file extensions during the `tsc`
|
|
435
|
+
`zshy` uses the [TypeScript Compiler API](https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API) to rewrite file extensions during the `tsc` emit step.
|
|
413
436
|
|
|
414
|
-
|
|
437
|
+
- If `"type": "module"`
|
|
438
|
+
- `.ts` becomes `.js`/`.d.ts` (ESM) and `.cjs`/`.d.cts` (CJS)
|
|
439
|
+
- Otherwise:
|
|
440
|
+
- `.ts` becomes `.mjs`/`.d.mts` (ESM) and `.js`/`.d.ts` (CJS)
|
|
415
441
|
|
|
416
|
-
|
|
417
|
-
- Rewrite extensionless imports/exports to `.js`/`.cjs`/`.mjs`
|
|
418
|
-
- Rewrite `.js` imports/exports to `.cjs`/`.mjs`
|
|
419
|
-
- Rename build output files to `.cjs`/`.mjs`/`.d.cts`/`.d.mts`
|
|
442
|
+
Similarly, all relative `import`/`export` statements are rewritten to account for the new file extensions.
|
|
420
443
|
|
|
421
|
-
|
|
444
|
+
| Original path | Result (ESM) | Result (CJS) |
|
|
445
|
+
| ------------------ | ------------------ | ------------------- |
|
|
446
|
+
| `from "./util"` | `from "./util.js"` | `from "./util.cjs"` |
|
|
447
|
+
| `from "./util.ts"` | `from "./util.js"` | `from "./util.cjs"` |
|
|
448
|
+
| `from "./util.js"` | `from "./util.js"` | `from "./util.cjs"` |
|
|
449
|
+
|
|
450
|
+
TypeScript's Compiler API provides dedicated hooks for performing such transforms (though they are criminally under-utilized).
|
|
422
451
|
|
|
423
452
|
- **`ts.TransformerFactory`**: Provides AST transformations to rewrite import/export extensions before module conversion
|
|
424
453
|
- **`ts.CompilerHost#writeFile`**: Handles output file extension changes (`.js` β `.cjs`/`.mjs`)
|
|
@@ -479,14 +508,16 @@ Your exports map is automatically written into your `package.json` when you run
|
|
|
479
508
|
|
|
480
509
|
The `"types"` field always points to the CJS declaration file (`.d.cts`). This is an intentional design choice.
|
|
481
510
|
|
|
482
|
-
**It solves "Masquerading as ESM" issue**.
|
|
511
|
+
**It solves the "Masquerading as ESM" issue**. You've likely seen this dreaded error before:
|
|
483
512
|
|
|
484
513
|
```ts
|
|
485
514
|
import mod from "pkg"; ^^^^^
|
|
486
515
|
// ^ The current file is a CommonJS module whose imports will produce 'require' calls; however, the referenced file is an ECMAScript module and cannot be imported with 'require'. Consider writing a dynamic 'import("pkg")' call instead.
|
|
487
516
|
```
|
|
488
517
|
|
|
489
|
-
By having `"types"` point to the `.d.cts` declarations, this error will never happen. Technically, we're
|
|
518
|
+
Simply put: ESM files can `require` CommonJS, but CommonJS files can't `import` ESM. By having `"types"` point to the `.d.cts` declarations, this error will never happen. Technically, we're tricking TypeScript into thinking our code is CommonJS; in practice, this has no real consequences and maximizes compatibility.
|
|
519
|
+
|
|
520
|
+
> To learn more, read the ["Masquerading as ESM"](https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseESM.md) and ["Masquerading as CJS"](https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseCJS.md) writeups from Are The Types Wrong.
|
|
490
521
|
|
|
491
522
|
> **Comparison** β `tshy` generates independent (but identical) `.d.ts` files in `dist/esm` and `dist/cjs`. This can cause [Excessively Deep](https://github.com/colinhacks/zod/issues/4422) errors if users of the library use declaration merging (`declare module {}`) for plugins/extensions. [Zod](https://github.com/colinhacks/zod), [day.js](https://day.js.org/), and others rely on this pattern for plugins.
|
|
492
523
|
|