@systemfsoftware/arethetypeswrong-cli 0.18.5
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 +7 -0
- package/README.md +265 -0
- package/dist/getExitCode.d.ts +6 -0
- package/dist/getExitCode.mjs +14 -0
- package/dist/index-DM-Vgonq.d.ts +36 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.mjs +199 -0
- package/dist/problemUtils-DrDcGcsf.mjs +29 -0
- package/dist/render/index.d.ts +2 -0
- package/dist/render/index.mjs +2 -0
- package/dist/render-CTRBsIjQ.mjs +118 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2023 Andrew Branch
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the βSoftwareβ), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED βAS ISβ, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# arethetypeswrong/cli
|
|
2
|
+
|
|
3
|
+
A CLI for [arethetypeswrong.github.io](https://arethetypeswrong.github.io/).
|
|
4
|
+
|
|
5
|
+
This project attempts to analyze npm package contents for issues with their TypeScript types, particularly ESM-related module resolution issues. The following kinds of problems can be detected in the `node10`, `node16`, and `bundler` module resolution modes:
|
|
6
|
+
|
|
7
|
+
- [π Resolution failed](https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/NoResolution.md)
|
|
8
|
+
- [β No types](https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/UntypedResolution.md)
|
|
9
|
+
- [π Masquerading as CJS](https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseCJS.md)
|
|
10
|
+
- [πΊ Masquerading as ESM](https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseESM.md)
|
|
11
|
+
- [β οΈ ESM (dynamic import only)](https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/CJSResolvesToESM.md)
|
|
12
|
+
- [π Used fallback condition](https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FallbackCondition.md)
|
|
13
|
+
- [π€¨ CJS default export](https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/CJSOnlyExportsDefault.md)
|
|
14
|
+
- [βοΈ Incorrect default export](https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseExportDefault.md)
|
|
15
|
+
- [β Missing `export =`](https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/MissingExportEquals.md)
|
|
16
|
+
- [π Unexpected module syntax](https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/UnexpectedModuleSyntax.md)
|
|
17
|
+
- [π₯΄ Internal resolution error](https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/InternalResolutionError.md)
|
|
18
|
+
- [π΅οΈββοΈ Named exports](https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/NamedExports.md)
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```shell
|
|
23
|
+
npm i -g @systemfsoftware/arethetypeswrong-cli
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
<!-- Or, using `npx`: -->
|
|
27
|
+
<!---->
|
|
28
|
+
<!-- ```shell -->
|
|
29
|
+
<!-- npx attw -->
|
|
30
|
+
<!-- ``` -->
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
The `attw` command acts very similarly to [arethetypeswrong.github.io](https://arethetypeswrong.github.io/), with some additional features that are useful for command line usage.
|
|
35
|
+
|
|
36
|
+
The CLI can check an `npm pack`ed tarball:
|
|
37
|
+
|
|
38
|
+
```shell
|
|
39
|
+
npm pack
|
|
40
|
+
attw cool-package-1.0.0.tgz
|
|
41
|
+
# or
|
|
42
|
+
attw $(npm pack)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
or pack one in-place by specifying `--pack` and a directory:
|
|
46
|
+
|
|
47
|
+
```shell
|
|
48
|
+
attw --pack .
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
or check a package from npm:
|
|
52
|
+
|
|
53
|
+
```shell
|
|
54
|
+
attw --from-npm @systemfsoftware/arethetypeswrong-cli
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
You can also use `attw` without installing globally by using `npx`. Pack one in-place by specifying `--pack` and a directory:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
npx --yes @systemfsoftware/arethetypeswrong-cli --pack .
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
or check a package from npm:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
npx --yes @systemfsoftware/arethetypeswrong-cli --from-npm @systemfsoftware/arethetypeswrong-cli
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Configuration
|
|
70
|
+
|
|
71
|
+
`attw` supports a JSON config file (by default named `.attw.json`) which allows you to pre-set the command line arguments. The options are a one-to-one mapping of the command line flags, changed to camelCase, and are all documented in their relevant `Options` section below.
|
|
72
|
+
|
|
73
|
+
### Options
|
|
74
|
+
|
|
75
|
+
#### Help
|
|
76
|
+
|
|
77
|
+
Show help information and exit.
|
|
78
|
+
|
|
79
|
+
In the CLI: `--help`, `-h`
|
|
80
|
+
|
|
81
|
+
```shell
|
|
82
|
+
attw --help
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
#### Version
|
|
86
|
+
|
|
87
|
+
Print the current version of `attw` and exit.
|
|
88
|
+
|
|
89
|
+
In the CLI: `--version`, `-v`
|
|
90
|
+
|
|
91
|
+
```shell
|
|
92
|
+
attw --version
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Pack
|
|
96
|
+
|
|
97
|
+
Specify a directory to run `npm pack` in (instead of specifying a tarball filename), analyze the resulting tarball, and delete it afterwards.
|
|
98
|
+
|
|
99
|
+
```shell
|
|
100
|
+
attw --pack .
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Please note that the `--pack` option does not support package managers other than npm at this time.
|
|
104
|
+
Therefore, if you use pnpm or yarn, you should generate the tarball yourself first (using `pnpm pack`/`yarn pack`) and run then run `attw <packed-tarball-name>`, rather than using the `--pack` option.
|
|
105
|
+
|
|
106
|
+
#### From NPM
|
|
107
|
+
|
|
108
|
+
Specify the name (and, optionally, version or SemVer range) of a package from the NPM registry instead of a local tarball filename.
|
|
109
|
+
|
|
110
|
+
In the CLI: `--from-npm`, `-p`
|
|
111
|
+
|
|
112
|
+
```shell
|
|
113
|
+
attw --from-npm <package-name>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
In the config file, `fromNpm` can be a boolean value.
|
|
117
|
+
|
|
118
|
+
#### DefinitelyTyped
|
|
119
|
+
|
|
120
|
+
When a package does not contain types, specifies the version or SemVer range of the DefinitelyTyped `@types` package to use. Defaults to inferring the best version match from the implementation package version.
|
|
121
|
+
|
|
122
|
+
In the CLI: `--definitely-typed`, `--no-definitely-typed`
|
|
123
|
+
|
|
124
|
+
```shell
|
|
125
|
+
attw -p <package-name> --definitely-typed <version>
|
|
126
|
+
attw -p <package-name> --no-definitely-typed
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
#### Format
|
|
130
|
+
|
|
131
|
+
The format to print the output in. Defaults to `auto`.
|
|
132
|
+
|
|
133
|
+
The available values are:
|
|
134
|
+
|
|
135
|
+
- `table`, where columns are entrypoints and rows are resolution kinds
|
|
136
|
+
- `table-flipped`, where columns are resolution kinds and rows are entrypoints
|
|
137
|
+
- `ascii`, for large tables where the output is clunky
|
|
138
|
+
- `auto`, which picks whichever of the above best fits the terminal width
|
|
139
|
+
- `json` outputs the raw JSON data (overriding all other rendering options)
|
|
140
|
+
|
|
141
|
+
In the CLI: `--format`, `-f`
|
|
142
|
+
|
|
143
|
+
```shell
|
|
144
|
+
attw --format <format> <file-name>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
In the config file, `format` can be a string value.
|
|
148
|
+
|
|
149
|
+
#### Entrypoints
|
|
150
|
+
|
|
151
|
+
`attw` automatically discovers package entrypoints by looking at package.json `exports` and subdirectories with additional package.json files. In a package lacking `exports`, providing the `--entrypoints-legacy` option will include all published code files. This automatic discovery process can be overridden with the `--entrypoints` option, or altered with the `--include-entrypoints` and `--exclude-entrypoints` options:
|
|
152
|
+
|
|
153
|
+
```shell
|
|
154
|
+
attw --pack . --entrypoints . one two three # Just ".", "./one", "./two", "./three"
|
|
155
|
+
attw --pack . --include-entrypoints added # Auto-discovered entrypoints plus "./added"
|
|
156
|
+
attw --pack . --exclude-entrypoints styles.css # Auto-discovered entrypoints except "./styles.css"
|
|
157
|
+
attw --pack . --entrypoints-legacy # All published code files
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
#### Profiles
|
|
161
|
+
|
|
162
|
+
Profiles select a set of resolution modes to require/ignore. All are evaluated but failures outside of those required are ignored.
|
|
163
|
+
|
|
164
|
+
The available profiles are:
|
|
165
|
+
|
|
166
|
+
- `strict` - requires all resolutions
|
|
167
|
+
- `node16` - ignores node10 resolution failures
|
|
168
|
+
- `esm-only` - ignores CJS resolution failures
|
|
169
|
+
|
|
170
|
+
In the CLI: `--profile`
|
|
171
|
+
|
|
172
|
+
```shell
|
|
173
|
+
attw <file-name> --profile <profile>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
In the config file, `profile` can be a string value.
|
|
177
|
+
|
|
178
|
+
#### Ignore Rules
|
|
179
|
+
|
|
180
|
+
Specifies rules/problems to ignore (i.e. not raise an error for).
|
|
181
|
+
|
|
182
|
+
The available values are:
|
|
183
|
+
|
|
184
|
+
- `no-resolution`
|
|
185
|
+
- `untyped-resolution`
|
|
186
|
+
- `false-cjs`
|
|
187
|
+
- `false-esm`
|
|
188
|
+
- `cjs-resolves-to-esm`
|
|
189
|
+
- `fallback-condition`
|
|
190
|
+
- `cjs-only-exports-default`
|
|
191
|
+
- `false-export-default`
|
|
192
|
+
- `unexpected-module-syntax`
|
|
193
|
+
- `missing-export-equals`
|
|
194
|
+
- `internal-resolution-error`
|
|
195
|
+
- `named-exports`
|
|
196
|
+
|
|
197
|
+
In the CLI: `--ignore-rules`
|
|
198
|
+
|
|
199
|
+
```shell
|
|
200
|
+
attw <file-name> --ignore-rules <rules...>
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
In the config file, `ignoreRules` can be an array of strings.
|
|
204
|
+
|
|
205
|
+
#### Summary/No Summary
|
|
206
|
+
|
|
207
|
+
Whether to display a summary of what the different errors/problems mean. Defaults to showing the summary (`--summary`).
|
|
208
|
+
|
|
209
|
+
In the CLI: `--summary`/`--no-summary`
|
|
210
|
+
|
|
211
|
+
```shell
|
|
212
|
+
attw --summary/--no-summary <file-name>
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
In the config file, `summary` can be a boolean value.
|
|
216
|
+
|
|
217
|
+
#### Emoji/No Emoji
|
|
218
|
+
|
|
219
|
+
Whether to print the information with emojis. Defaults to printing with emojis (`--emoji`).
|
|
220
|
+
|
|
221
|
+
In the CLI: `--emoji`/`--no-emoji`
|
|
222
|
+
|
|
223
|
+
```shell
|
|
224
|
+
attw --emoji/--no-emoji <file-name>
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
In the config file, `emoji` can be a boolean value.
|
|
228
|
+
|
|
229
|
+
#### Color/No Color
|
|
230
|
+
|
|
231
|
+
Whether to print with colors. Defaults to printing with colors (`--color`).
|
|
232
|
+
|
|
233
|
+
The `FORCE_COLOR` env variable is also available for use (set is to `0` or `1`).
|
|
234
|
+
|
|
235
|
+
In the CLI: `--color`/`--no-color`
|
|
236
|
+
|
|
237
|
+
```shell
|
|
238
|
+
attw --color/--no-color <file-name>
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
In the config file, `color` can be a boolean value.
|
|
242
|
+
|
|
243
|
+
#### Quiet
|
|
244
|
+
|
|
245
|
+
When set, nothing will be printed to STDOUT.
|
|
246
|
+
|
|
247
|
+
In the CLI: `--quiet`, `-q`
|
|
248
|
+
|
|
249
|
+
```shell
|
|
250
|
+
attw --quiet <file-name>
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
In the config file, `quiet` can be a boolean value.
|
|
254
|
+
|
|
255
|
+
#### Config Path
|
|
256
|
+
|
|
257
|
+
The path to the config file. Defaults to `./.attw.json`.
|
|
258
|
+
|
|
259
|
+
In the CLI: `--config-path <path>`
|
|
260
|
+
|
|
261
|
+
```shell
|
|
262
|
+
attw --config-path <path> <file-name>
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Cannot be set from within the config file itself.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { n as RenderOptions } from "./index-DM-Vgonq.js";
|
|
2
|
+
import { CheckResult } from "@systemfsoftware/arethetypeswrong-core";
|
|
3
|
+
//#region src/getExitCode.d.ts
|
|
4
|
+
declare function getExitCode(analysis: CheckResult, opts?: RenderOptions): number;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { getExitCode };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { n as problemFlags } from "./problemUtils-DrDcGcsf.mjs";
|
|
2
|
+
//#region src/getExitCode.ts
|
|
3
|
+
function getExitCode(analysis, opts) {
|
|
4
|
+
if (!analysis.types) return 0;
|
|
5
|
+
const ignoreRules = opts?.ignoreRules ?? [];
|
|
6
|
+
const ignoreResolutions = opts?.ignoreResolutions ?? [];
|
|
7
|
+
return analysis.problems.some((problem) => {
|
|
8
|
+
const notRuleIgnored = !ignoreRules.includes(problemFlags[problem.kind]);
|
|
9
|
+
const notResolutionIgnored = "resolutionKind" in problem ? !ignoreResolutions.includes(problem.resolutionKind) : true;
|
|
10
|
+
return notRuleIgnored && notResolutionIgnored;
|
|
11
|
+
}) ? 1 : 0;
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { getExitCode };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as core from "@systemfsoftware/arethetypeswrong-core";
|
|
2
|
+
//#region src/problemUtils.d.ts
|
|
3
|
+
declare const problemFlags: {
|
|
4
|
+
readonly NoResolution: "no-resolution";
|
|
5
|
+
readonly UntypedResolution: "untyped-resolution";
|
|
6
|
+
readonly FalseCJS: "false-cjs";
|
|
7
|
+
readonly FalseESM: "false-esm";
|
|
8
|
+
readonly CJSResolvesToESM: "cjs-resolves-to-esm";
|
|
9
|
+
readonly FallbackCondition: "fallback-condition";
|
|
10
|
+
readonly CJSOnlyExportsDefault: "cjs-only-exports-default";
|
|
11
|
+
readonly NamedExports: "named-exports";
|
|
12
|
+
readonly FalseExportDefault: "false-export-default";
|
|
13
|
+
readonly MissingExportEquals: "missing-export-equals";
|
|
14
|
+
readonly UnexpectedModuleSyntax: "unexpected-module-syntax";
|
|
15
|
+
readonly InternalResolutionError: "internal-resolution-error";
|
|
16
|
+
};
|
|
17
|
+
declare const resolutionKinds: Record<core.ResolutionKind, string>;
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/render/typed.d.ts
|
|
20
|
+
declare function typed(analysis: core.Analysis, { emoji, summary, format, ignoreRules, ignoreResolutions }: RenderOptions): Promise<string>;
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/render/untyped.d.ts
|
|
23
|
+
declare function untyped(analysis: core.UntypedResult): string;
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/render/index.d.ts
|
|
26
|
+
type Format = 'auto' | 'table' | 'table-flipped' | 'ascii' | 'json';
|
|
27
|
+
interface RenderOptions {
|
|
28
|
+
ignoreRules?: (typeof problemFlags)[keyof typeof problemFlags][];
|
|
29
|
+
ignoreResolutions?: (keyof typeof resolutionKinds)[];
|
|
30
|
+
format?: Format;
|
|
31
|
+
color?: boolean;
|
|
32
|
+
summary?: boolean;
|
|
33
|
+
emoji?: boolean;
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
export { typed as i, RenderOptions as n, untyped as r, Format as t };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { n as problemFlags } from "./problemUtils-DrDcGcsf.mjs";
|
|
3
|
+
import { getExitCode } from "./getExitCode.mjs";
|
|
4
|
+
import { n as typed, t as untyped } from "./render-CTRBsIjQ.mjs";
|
|
5
|
+
import * as core from "@systemfsoftware/arethetypeswrong-core";
|
|
6
|
+
import { groupProblemsByKind, parsePackageSpec } from "@systemfsoftware/arethetypeswrong-core/utils";
|
|
7
|
+
import { versions } from "@systemfsoftware/arethetypeswrong-core/versions";
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
import { execSync } from "child_process";
|
|
10
|
+
import { Option, program } from "commander";
|
|
11
|
+
import { readFile, stat, unlink } from "fs/promises";
|
|
12
|
+
import { createRequire } from "module";
|
|
13
|
+
import path from "path";
|
|
14
|
+
import readline from "readline";
|
|
15
|
+
import { major, minor } from "semver";
|
|
16
|
+
import { Writable } from "stream";
|
|
17
|
+
import { Readable } from "node:stream";
|
|
18
|
+
//#region src/profiles.ts
|
|
19
|
+
const profiles = {
|
|
20
|
+
strict: { ignoreResolutions: [] },
|
|
21
|
+
node16: { ignoreResolutions: ["node10"] },
|
|
22
|
+
"esm-only": { ignoreResolutions: ["node10", "node16-cjs"] }
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Merges the profile with the provided options
|
|
26
|
+
*
|
|
27
|
+
* @param profileKey - name of the profile to apply
|
|
28
|
+
* @param opts - options to apply the profile to
|
|
29
|
+
*/
|
|
30
|
+
function applyProfile(profileKey, opts) {
|
|
31
|
+
const profile = profiles[profileKey];
|
|
32
|
+
opts.ignoreResolutions = (opts.ignoreResolutions ?? []).concat(profile.ignoreResolutions);
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region src/readConfig.ts
|
|
36
|
+
async function readConfig(program, alternate = ".attw.json") {
|
|
37
|
+
try {
|
|
38
|
+
const results = await readFile(alternate, "utf8");
|
|
39
|
+
if (!results) return;
|
|
40
|
+
const opts = JSON.parse(results);
|
|
41
|
+
for (let key in opts) {
|
|
42
|
+
if (key === "configPath") program.error(`cannot set "configPath" within ${alternate}`, { code: "INVALID_OPTION" });
|
|
43
|
+
const value = opts[key];
|
|
44
|
+
if (key === "ignoreRules") {
|
|
45
|
+
if (!Array.isArray(value)) program.error(`error: config option 'ignoreRules' should be an array.`);
|
|
46
|
+
const invalid = value.find((rule) => !Object.values(problemFlags).includes(rule));
|
|
47
|
+
if (invalid) program.error(`error: config option 'ignoreRules' argument '${invalid}' is invalid. Allowed choices are ${Object.values(problemFlags).join(", ")}.`);
|
|
48
|
+
}
|
|
49
|
+
if (key === "profile") {
|
|
50
|
+
if (typeof value !== "string") program.error(`error: config option 'profile' should be a string.`);
|
|
51
|
+
if (!(value in profiles)) program.error(`error: config option 'profile' argument '${value}' is invalid. Allowed choices are ${Object.keys(profiles).join(", ")}.`);
|
|
52
|
+
}
|
|
53
|
+
if (Array.isArray(value)) {
|
|
54
|
+
const opt = program.getOptionValue(key);
|
|
55
|
+
if (Array.isArray(opt)) {
|
|
56
|
+
program.setOptionValueWithSource(key, [...opt, ...value], "config");
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (key !== "help" && key !== "version") program.setOptionValueWithSource(key, opts[key], "config");
|
|
61
|
+
}
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (!error || typeof error !== "object" || !("code" in error) || !("message" in error)) program.error("unknown error while reading config file", { code: "UNKNOWN" });
|
|
64
|
+
else if (error.code !== "ENOENT") program.error(`error while reading config file:\n${error.message}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//#endregion
|
|
68
|
+
//#region src/write.ts
|
|
69
|
+
async function write(data, out) {
|
|
70
|
+
return new Promise((resolve, reject) => {
|
|
71
|
+
const stream = new Readable({ read() {
|
|
72
|
+
this.push(data);
|
|
73
|
+
this.push("\n");
|
|
74
|
+
this.push(null);
|
|
75
|
+
} });
|
|
76
|
+
stream.on("data", (chunk) => {
|
|
77
|
+
out.write(chunk);
|
|
78
|
+
});
|
|
79
|
+
stream.on("end", () => {
|
|
80
|
+
resolve();
|
|
81
|
+
});
|
|
82
|
+
out.on("error", (err) => {
|
|
83
|
+
reject(err);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
//#endregion
|
|
88
|
+
//#region src/index.ts
|
|
89
|
+
const version = createRequire(import.meta.url)("../package.json").version;
|
|
90
|
+
const formats = Object.keys({
|
|
91
|
+
auto: true,
|
|
92
|
+
json: true,
|
|
93
|
+
ascii: true,
|
|
94
|
+
table: true,
|
|
95
|
+
"table-flipped": true
|
|
96
|
+
});
|
|
97
|
+
program.addHelpText("before", `ATTW CLI (v${version})\n`).addHelpText("after", `\ncore: v${versions.core}, typescript: v${versions.typescript}`).version(`cli: v${version}\ncore: v${versions.core}\ntypescript: v${versions.typescript}`).name("attw").description(`${chalk.bold.blue("Are the Types Wrong?")} attempts to analyze npm package contents for issues with their TypeScript types,
|
|
98
|
+
particularly ESM-related module resolution issues.`).argument("[file-directory-or-package-spec]", "the packed .tgz, or directory containing package.json with --pack, or package spec with --from-npm").option("-P, --pack", "Run `npm pack` in the specified directory and delete the resulting .tgz file afterwards").option("-p, --from-npm", "Read from the npm registry instead of a local file").addOption(new Option("--definitely-typed [version]", "Specify the version range of @types to use").default(true)).option("--no-definitely-typed", "Don't include @types").addOption(new Option("-f, --format <format>", "Specify the print format").choices(formats).default("auto")).option("-q, --quiet", "Don't print anything to STDOUT (overrides all other options)").option("--entrypoints <entrypoints...>", "Specify an exhaustive list of entrypoints to check. The package root is `\".\" Specifying this option disables automatic entrypoint discovery, and overrides the `--include-entrypoints` and `--exclude-entrypoints` options.").option("--include-entrypoints <entrypoints...>", "Specify entrypoints to check in addition to automatically discovered ones.").option("--exclude-entrypoints <entrypoints...>", "Specify entrypoints to exclude from checking.").option("--entrypoints-legacy", "In packages without the `exports` field, every file is an entry point. Specifying this option only takes effect when no entrypoints are automatically detected, or explicitly provided with other options.").addOption(new Option("--ignore-rules <rules...>", "Specify rules to ignore").choices(Object.values(problemFlags)).default([])).addOption(new Option("--profile <profile>", "Specify analysis profile").choices(Object.keys(profiles)).default("strict")).option("--summary, --no-summary", "Whether to print summary information about the different errors").option("--emoji, --no-emoji", "Whether to use any emojis").option("--color, --no-color", "Whether to use any colors (the FORCE_COLOR env variable is also available)").option("--config-path <path>", "Path to config file (default: ./.attw.json)").action(async (fileOrDirectory = ".") => {
|
|
99
|
+
const opts = program.opts();
|
|
100
|
+
await readConfig(program, opts.configPath);
|
|
101
|
+
if (opts.profile) applyProfile(opts.profile, opts);
|
|
102
|
+
let out = process.stdout;
|
|
103
|
+
if (opts.quiet) out = new class extends Writable {
|
|
104
|
+
_write(_chunk, _encoding, callback) {
|
|
105
|
+
callback();
|
|
106
|
+
}
|
|
107
|
+
}();
|
|
108
|
+
if (!opts.color) process.env.FORCE_COLOR = "0";
|
|
109
|
+
let analysis;
|
|
110
|
+
let deleteTgz;
|
|
111
|
+
const dtIsPath = typeof opts.definitelyTyped === "string" && (opts.definitelyTyped.includes("/") || opts.definitelyTyped.includes("\\") || opts.definitelyTyped.endsWith(".tgz") || opts.definitelyTyped.endsWith(".tar.gz"));
|
|
112
|
+
if (opts.fromNpm) {
|
|
113
|
+
if (opts.pack) program.error("--pack and --from-npm cannot be used together");
|
|
114
|
+
try {
|
|
115
|
+
const result = parsePackageSpec(fileOrDirectory);
|
|
116
|
+
if (result.status === "error") program.error(result.error);
|
|
117
|
+
else {
|
|
118
|
+
let pkg;
|
|
119
|
+
if (dtIsPath) {
|
|
120
|
+
const dtPackage = core.createPackageFromTarballData(new Uint8Array(await readFile(opts.definitelyTyped)));
|
|
121
|
+
const pkgVersion = result.data.versionKind === "none" ? `${major(dtPackage.packageVersion)}.${minor(dtPackage.packageVersion)}` : result.data.version;
|
|
122
|
+
pkg = (await core.createPackageFromNpm(`${result.data.name}@${pkgVersion}`)).mergedWithTypes(dtPackage);
|
|
123
|
+
} else pkg = await core.createPackageFromNpm(`${result.data.name}@${result.data.version}`, { definitelyTyped: opts.definitelyTyped });
|
|
124
|
+
analysis = await core.checkPackage(pkg, {
|
|
125
|
+
entrypoints: opts.entrypoints,
|
|
126
|
+
includeEntrypoints: opts.includeEntrypoints,
|
|
127
|
+
excludeEntrypoints: opts.excludeEntrypoints,
|
|
128
|
+
entrypointsLegacy: opts.entrypointsLegacy
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
} catch (error) {
|
|
132
|
+
if (error instanceof Error && "code" in error) program.error(`error while fetching package:\n${error.message}`, { code: "" + error.code });
|
|
133
|
+
handleError(error, "checking package");
|
|
134
|
+
}
|
|
135
|
+
} else try {
|
|
136
|
+
let fileName = fileOrDirectory;
|
|
137
|
+
if (await stat(fileOrDirectory).then((stat) => !stat.isFile()).catch(() => false)) {
|
|
138
|
+
if (!await stat(path.join(fileOrDirectory, "package.json")).catch(() => false)) program.error(`Specified directory must contain a package.json. No package.json found in ${path.resolve(fileOrDirectory)}.`);
|
|
139
|
+
if (!opts.pack) {
|
|
140
|
+
if (!process.stdout.isTTY) program.error("Specifying a directory requires the --pack option to confirm that running `npm pack` is ok.");
|
|
141
|
+
const rl = readline.createInterface(process.stdin, process.stdout);
|
|
142
|
+
const answer = await new Promise((resolve) => {
|
|
143
|
+
rl.question(`Run \`npm pack\`? (Pass -P/--pack to skip) (Y/n) `, resolve);
|
|
144
|
+
});
|
|
145
|
+
rl.close();
|
|
146
|
+
if (answer.trim() && !answer.trim().toLowerCase().startsWith("y")) process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
const manifest = JSON.parse(await readFile(path.join(fileOrDirectory, "package.json"), { encoding: "utf8" }));
|
|
149
|
+
fileName = deleteTgz = path.join(fileOrDirectory, `${manifest.name.replace("@", "").replace("/", "-")}-${manifest.version}.tgz`);
|
|
150
|
+
execSync("npm pack", {
|
|
151
|
+
cwd: fileOrDirectory,
|
|
152
|
+
encoding: "utf8",
|
|
153
|
+
stdio: "ignore"
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
const file = await readFile(fileName);
|
|
157
|
+
const data = new Uint8Array(file);
|
|
158
|
+
const pkg = dtIsPath ? core.createPackageFromTarballData(data).mergedWithTypes(core.createPackageFromTarballData(new Uint8Array(await readFile(opts.definitelyTyped)))) : core.createPackageFromTarballData(data);
|
|
159
|
+
analysis = await core.checkPackage(pkg, {
|
|
160
|
+
entrypoints: opts.entrypoints,
|
|
161
|
+
includeEntrypoints: opts.includeEntrypoints,
|
|
162
|
+
excludeEntrypoints: opts.excludeEntrypoints,
|
|
163
|
+
entrypointsLegacy: opts.entrypointsLegacy
|
|
164
|
+
});
|
|
165
|
+
} catch (error) {
|
|
166
|
+
handleError(error, "checking file");
|
|
167
|
+
}
|
|
168
|
+
if (opts.format === "json") {
|
|
169
|
+
const result = { analysis };
|
|
170
|
+
if (analysis.types) result.problems = groupProblemsByKind(analysis.problems);
|
|
171
|
+
await write(JSON.stringify(result, void 0, 2), out);
|
|
172
|
+
if (deleteTgz) await unlink(deleteTgz);
|
|
173
|
+
const exitCode = getExitCode(analysis, opts);
|
|
174
|
+
if (exitCode) process.exit(exitCode);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
await write("", out);
|
|
178
|
+
if (analysis.types) {
|
|
179
|
+
await write(await typed(analysis, opts), out);
|
|
180
|
+
process.exitCode = getExitCode(analysis, opts);
|
|
181
|
+
} else await write(untyped(analysis), out);
|
|
182
|
+
if (deleteTgz) await unlink(deleteTgz);
|
|
183
|
+
});
|
|
184
|
+
program.parse(process.argv);
|
|
185
|
+
function handleError(error, title) {
|
|
186
|
+
if (error && typeof error === "object" && "message" in error) program.error(`error while ${title}:\n${error.message}`, {
|
|
187
|
+
exitCode: 3,
|
|
188
|
+
code: "code" in error && typeof error.code === "string" ? error.code : "UNKNOWN"
|
|
189
|
+
});
|
|
190
|
+
program.error(`unknown error while ${title}`, {
|
|
191
|
+
code: "UNKNOWN",
|
|
192
|
+
exitCode: 3
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
process.on("unhandledRejection", (error) => {
|
|
196
|
+
handleError(error, "checking package");
|
|
197
|
+
});
|
|
198
|
+
//#endregion
|
|
199
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import "@systemfsoftware/arethetypeswrong-core";
|
|
2
|
+
//#region src/problemUtils.ts
|
|
3
|
+
const problemFlags = {
|
|
4
|
+
NoResolution: "no-resolution",
|
|
5
|
+
UntypedResolution: "untyped-resolution",
|
|
6
|
+
FalseCJS: "false-cjs",
|
|
7
|
+
FalseESM: "false-esm",
|
|
8
|
+
CJSResolvesToESM: "cjs-resolves-to-esm",
|
|
9
|
+
FallbackCondition: "fallback-condition",
|
|
10
|
+
CJSOnlyExportsDefault: "cjs-only-exports-default",
|
|
11
|
+
NamedExports: "named-exports",
|
|
12
|
+
FalseExportDefault: "false-export-default",
|
|
13
|
+
MissingExportEquals: "missing-export-equals",
|
|
14
|
+
UnexpectedModuleSyntax: "unexpected-module-syntax",
|
|
15
|
+
InternalResolutionError: "internal-resolution-error"
|
|
16
|
+
};
|
|
17
|
+
const resolutionKinds = {
|
|
18
|
+
node10: "node10",
|
|
19
|
+
"node16-cjs": "node16 (from CJS)",
|
|
20
|
+
"node16-esm": "node16 (from ESM)",
|
|
21
|
+
bundler: "bundler"
|
|
22
|
+
};
|
|
23
|
+
const moduleKinds = {
|
|
24
|
+
1: "(CJS)",
|
|
25
|
+
99: "(ESM)",
|
|
26
|
+
"": ""
|
|
27
|
+
};
|
|
28
|
+
//#endregion
|
|
29
|
+
export { problemFlags as n, resolutionKinds as r, moduleKinds as t };
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { n as problemFlags, r as resolutionKinds, t as moduleKinds } from "./problemUtils-DrDcGcsf.mjs";
|
|
2
|
+
import "@systemfsoftware/arethetypeswrong-core";
|
|
3
|
+
import { allResolutionKinds, getResolutionOption, groupProblemsByKind } from "@systemfsoftware/arethetypeswrong-core/utils";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import { filterProblems, problemAffectsEntrypoint, problemAffectsResolutionKind, problemKindInfo } from "@systemfsoftware/arethetypeswrong-core/problems";
|
|
6
|
+
import Table from "cli-table3";
|
|
7
|
+
import { marked } from "marked";
|
|
8
|
+
import TerminalRenderer from "marked-terminal";
|
|
9
|
+
//#region src/render/asciiTable.ts
|
|
10
|
+
function asciiTable(table) {
|
|
11
|
+
return table.options.head.slice(1).map((entryPoint, i) => {
|
|
12
|
+
const keyValuePairs = table.reduce((acc, cur) => {
|
|
13
|
+
return acc + `${cur[0]?.toString()}: ${cur[i + 1]?.toString()}\n`;
|
|
14
|
+
}, "");
|
|
15
|
+
return `${chalk.bold.blue(entryPoint)}
|
|
16
|
+
|
|
17
|
+
${keyValuePairs}
|
|
18
|
+
***********************************`;
|
|
19
|
+
}).join("\n\n");
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/render/typed.ts
|
|
23
|
+
async function typed(analysis, { emoji = true, summary = true, format = "auto", ignoreRules = [], ignoreResolutions = [] }) {
|
|
24
|
+
let output = "";
|
|
25
|
+
const problems = analysis.problems.filter((problem) => !ignoreRules || !ignoreRules.includes(problemFlags[problem.kind]));
|
|
26
|
+
const requiredResolutions = allResolutionKinds.filter((kind) => !ignoreResolutions.includes(kind));
|
|
27
|
+
const ignoredResolutions = allResolutionKinds.filter((kind) => ignoreResolutions.includes(kind));
|
|
28
|
+
const resolutions = requiredResolutions.concat(ignoredResolutions);
|
|
29
|
+
const entrypoints = Object.keys(analysis.entrypoints);
|
|
30
|
+
marked.setOptions({ renderer: new TerminalRenderer() });
|
|
31
|
+
out(`${analysis.packageName} v${analysis.packageVersion}`);
|
|
32
|
+
if (analysis.types.kind === "@types") out(`${analysis.types.packageName} v${analysis.types.packageVersion}`);
|
|
33
|
+
out();
|
|
34
|
+
if (Object.keys(analysis.buildTools).length) {
|
|
35
|
+
out("Build tools:");
|
|
36
|
+
out(Object.entries(analysis.buildTools).map(([tool, version]) => {
|
|
37
|
+
return `- ${tool}@${version}`;
|
|
38
|
+
}).join("\n"));
|
|
39
|
+
out();
|
|
40
|
+
}
|
|
41
|
+
if (ignoreRules && ignoreRules.length) out(chalk.gray(` (ignoring rules: ${ignoreRules.map((rule) => `'${rule}'`).join(", ")})\n`));
|
|
42
|
+
if (ignoreResolutions && ignoreResolutions.length) out(chalk.gray(` (ignoring resolutions: ${ignoreResolutions.map((resolution) => `'${resolution}'`).join(", ")})\n`));
|
|
43
|
+
if (summary) {
|
|
44
|
+
const defaultSummary = marked(!emoji ? " No problems found" : " No problems found π");
|
|
45
|
+
const grouped = groupProblemsByKind(problems);
|
|
46
|
+
out(Object.entries(grouped).map(([kind, kindProblems]) => {
|
|
47
|
+
const info = problemKindInfo[kind];
|
|
48
|
+
const affectsRequiredResolution = kindProblems.some((p) => requiredResolutions.some((r) => problemAffectsResolutionKind(p, r, analysis)));
|
|
49
|
+
const description = marked(`${info.description}${info.details ? ` Use \`-f json\` to see ${info.details}.` : ""} ${info.docsUrl}`);
|
|
50
|
+
return `${affectsRequiredResolution ? "" : "(ignored per resolution) "}${emoji ? `${info.emoji} ` : ""}${description}`;
|
|
51
|
+
}).join("") || defaultSummary);
|
|
52
|
+
}
|
|
53
|
+
const entrypointNames = entrypoints.map((s) => `"${s === "." ? analysis.packageName : `${analysis.packageName}/${s.substring(2)}`}"`);
|
|
54
|
+
const entrypointHeaders = entrypoints.map((s, i) => {
|
|
55
|
+
const color = problems.some((p) => problemAffectsEntrypoint(p, s, analysis)) ? "redBright" : "greenBright";
|
|
56
|
+
return chalk.bold[color](entrypointNames[i]);
|
|
57
|
+
});
|
|
58
|
+
const getCellContents = memo((subpath, resolutionKind) => {
|
|
59
|
+
const ignoredPrefix = ignoreResolutions.includes(resolutionKind) ? "(ignored) " : "";
|
|
60
|
+
const problemsForCell = groupProblemsByKind(filterProblems(problems, analysis, {
|
|
61
|
+
entrypoint: subpath,
|
|
62
|
+
resolutionKind
|
|
63
|
+
}));
|
|
64
|
+
const entrypoint = analysis.entrypoints[subpath].resolutions[resolutionKind];
|
|
65
|
+
const resolution = entrypoint.resolution;
|
|
66
|
+
const kinds = Object.keys(problemsForCell);
|
|
67
|
+
if (kinds.length) return kinds.map((kind) => ignoredPrefix + (emoji ? `${problemKindInfo[kind].emoji} ` : "") + problemKindInfo[kind].shortDescription).join("\n");
|
|
68
|
+
const jsonResult = !emoji ? "OK (JSON)" : "π’ (JSON)";
|
|
69
|
+
const moduleResult = entrypoint.isWildcard ? "(wildcard)" : (!emoji ? "OK " : "π’ ") + moduleKinds[analysis.programInfo[getResolutionOption(resolutionKind)].moduleKinds?.[resolution?.fileName ?? ""]?.detectedKind || ""];
|
|
70
|
+
return ignoredPrefix + (resolution?.isJson ? jsonResult : moduleResult);
|
|
71
|
+
});
|
|
72
|
+
const flippedTable = format === "auto" || format === "table-flipped" ? new Table({ head: ["", ...resolutions.map((kind) => chalk.reset(resolutionKinds[kind] + (ignoreResolutions.includes(kind) ? " (ignored)" : "")))] }) : void 0;
|
|
73
|
+
if (flippedTable) entrypoints.forEach((subpath, i) => {
|
|
74
|
+
flippedTable.push([entrypointHeaders[i], ...resolutions.map((resolutionKind) => getCellContents(subpath, resolutionKind))]);
|
|
75
|
+
});
|
|
76
|
+
const table = format === "auto" || !flippedTable ? new Table({ head: ["", ...entrypointHeaders] }) : void 0;
|
|
77
|
+
if (table) resolutions.forEach((kind) => {
|
|
78
|
+
table.push([resolutionKinds[kind], ...entrypoints.map((entrypoint) => getCellContents(entrypoint, kind))]);
|
|
79
|
+
});
|
|
80
|
+
switch (format) {
|
|
81
|
+
case "table":
|
|
82
|
+
out(table.toString());
|
|
83
|
+
break;
|
|
84
|
+
case "table-flipped":
|
|
85
|
+
out(flippedTable.toString());
|
|
86
|
+
break;
|
|
87
|
+
case "ascii":
|
|
88
|
+
out(asciiTable(table));
|
|
89
|
+
break;
|
|
90
|
+
case "auto":
|
|
91
|
+
const terminalWidth = process.stdout.columns || 133;
|
|
92
|
+
if (table.width <= terminalWidth) out(table.toString());
|
|
93
|
+
else if (flippedTable.width <= terminalWidth) out(flippedTable.toString());
|
|
94
|
+
else out(asciiTable(table));
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
return output.trimEnd();
|
|
98
|
+
function out(s = "") {
|
|
99
|
+
output += s + "\n";
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function memo(fn) {
|
|
103
|
+
const cache = /* @__PURE__ */ new Map();
|
|
104
|
+
return (...args) => {
|
|
105
|
+
const key = "" + args;
|
|
106
|
+
if (cache.has(key)) return cache.get(key);
|
|
107
|
+
const result = fn(...args);
|
|
108
|
+
cache.set(key, result);
|
|
109
|
+
return result;
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
//#endregion
|
|
113
|
+
//#region src/render/untyped.ts
|
|
114
|
+
function untyped(analysis) {
|
|
115
|
+
return "This package does not contain types.\nDetails: " + JSON.stringify(analysis, null, 2);
|
|
116
|
+
}
|
|
117
|
+
//#endregion
|
|
118
|
+
export { typed as n, untyped as t };
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@systemfsoftware/arethetypeswrong-cli",
|
|
3
|
+
"version": "0.18.5",
|
|
4
|
+
"description": "A CLI tool for arethetypeswrong.github.io β forked under systemfsoftware",
|
|
5
|
+
"author": "Andrew Branch & ej-shafran (forked by systemfsoftware)",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/systemfsoftware/systemfsoftware.git",
|
|
10
|
+
"directory": "packages/arethetypeswrong/cli"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"bin": {
|
|
17
|
+
"attw": "./dist/index.mjs"
|
|
18
|
+
},
|
|
19
|
+
"exports": {
|
|
20
|
+
"./package.json": "./package.json",
|
|
21
|
+
"./internal/getExitCode": "./dist/getExitCode.mjs",
|
|
22
|
+
"./internal/render": "./dist/render/index.mjs"
|
|
23
|
+
},
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"type": "module",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"chalk": "^4.1.2",
|
|
30
|
+
"cli-table3": "^0.6.3",
|
|
31
|
+
"commander": "^10.0.1",
|
|
32
|
+
"marked": "^9.1.2",
|
|
33
|
+
"marked-terminal": "^7.1.0",
|
|
34
|
+
"semver": "^7.5.4",
|
|
35
|
+
"@systemfsoftware/arethetypeswrong-core": "0.18.5"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/marked": "^5.0.0",
|
|
39
|
+
"@types/marked-terminal": "^3.1.3",
|
|
40
|
+
"@types/node": "^24",
|
|
41
|
+
"@types/semver": "^7.5.3",
|
|
42
|
+
"rimraf": "^6.1.3",
|
|
43
|
+
"ts-expose-internals": "^5.6.0",
|
|
44
|
+
"tsdown": "^0.22.9",
|
|
45
|
+
"vite-tsconfig-paths": "^6.1.1",
|
|
46
|
+
"vitest": "^4",
|
|
47
|
+
"@systemfsoftware/tsconfig": "^1.0.0",
|
|
48
|
+
"@systemfsoftware/vitest-config": "^0.1.0"
|
|
49
|
+
},
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=22"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "tsdown",
|
|
55
|
+
"clean": "rimraf dist",
|
|
56
|
+
"typecheck": "tsc --noEmit --incremental",
|
|
57
|
+
"format": "dprint fmt",
|
|
58
|
+
"test": "vitest run",
|
|
59
|
+
"test:run": "vitest run --coverage"
|
|
60
|
+
}
|
|
61
|
+
}
|