Skip to content

Commit 3fae6b0

Browse files
authored
Merge pull request #115 from arethetypeswrong/error-exit-code
Use exit code 3 for errors, 1 for failures
2 parents 3b76fc7 + 5dba9a9 commit 3fae6b0

File tree

10 files changed

+246
-21
lines changed

10 files changed

+246
-21
lines changed

.changeset/angry-timers-shave.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@arethetypeswrong/cli": patch
3+
---
4+
5+
Use exit code 3 for errors, 1 for failures

packages/cli/src/index.ts

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,13 @@ program
4646
.name("attw")
4747
.description(
4848
`${chalk.bold.blue(
49-
"Are the Types Wrong?"
49+
"Are the Types Wrong?",
5050
)} attempts to analyze npm package contents for issues with their TypeScript types,
51-
particularly ESM-related module resolution issues.`
51+
particularly ESM-related module resolution issues.`,
5252
)
5353
.argument(
5454
"[file-directory-or-package-spec]",
55-
"the packed .tgz, or directory containing package.json with --pack, or package spec with --from-npm"
55+
"the packed .tgz, or directory containing package.json with --pack, or package spec with --from-npm",
5656
)
5757
.option("-P, --pack", "Run `npm pack` in the specified directory and delete the resulting .tgz file afterwards")
5858
.option("-p, --from-npm", "Read from the npm registry instead of a local file")
@@ -64,15 +64,15 @@ particularly ESM-related module resolution issues.`
6464
"--entrypoints <entrypoints...>",
6565
"Specify an exhaustive list of entrypoints to check. " +
6666
'The package root is `"." Specifying this option disables automatic entrypoint discovery, ' +
67-
"and overrides the `--include-entrypoints` and `--exclude-entrypoints` options."
67+
"and overrides the `--include-entrypoints` and `--exclude-entrypoints` options.",
6868
)
6969
.option(
7070
"--include-entrypoints <entrypoints...>",
71-
"Specify entrypoints to check in addition to automatically discovered ones."
71+
"Specify entrypoints to check in addition to automatically discovered ones.",
7272
)
7373
.option("--exclude-entrypoints <entrypoints...>", "Specify entrypoints to exclude from checking.")
7474
.addOption(
75-
new Option("--ignore-rules <rules...>", "Specify rules to ignore").choices(Object.values(problemFlags)).default([])
75+
new Option("--ignore-rules <rules...>", "Specify rules to ignore").choices(Object.values(problemFlags)).default([]),
7676
)
7777
.option("--summary, --no-summary", "Whether to print summary information about the different errors")
7878
.option("--emoji, --no-emoji", "Whether to use any emojis")
@@ -82,7 +82,7 @@ particularly ESM-related module resolution issues.`
8282
const opts = program.opts<Opts>();
8383
await readConfig(program, opts.configPath);
8484
opts.ignoreRules = opts.ignoreRules?.map(
85-
(value) => Object.keys(problemFlags).find((key) => problemFlags[key as core.ProblemKind] === value) as string
85+
(value) => Object.keys(problemFlags).find((key) => problemFlags[key as core.ProblemKind] === value) as string,
8686
);
8787

8888
if (opts.quiet) {
@@ -114,7 +114,7 @@ particularly ESM-related module resolution issues.`
114114
let pkg;
115115
if (dtIsPath) {
116116
const dtPackage = core.createPackageFromTarballData(
117-
new Uint8Array(await readFile(opts.definitelyTyped as string))
117+
new Uint8Array(await readFile(opts.definitelyTyped as string)),
118118
);
119119
const pkgVersion =
120120
result.data.versionKind === "none"
@@ -150,15 +150,15 @@ particularly ESM-related module resolution issues.`
150150
if (!(await stat(path.join(fileOrDirectory, "package.json")).catch(() => false))) {
151151
program.error(
152152
`Specified directory must contain a package.json. No package.json found in ${path.resolve(
153-
fileOrDirectory
154-
)}.`
153+
fileOrDirectory,
154+
)}.`,
155155
);
156156
}
157157

158158
if (!opts.pack) {
159159
if (!process.stdout.isTTY) {
160160
program.error(
161-
"Specifying a directory requires the --pack option to confirm that running `npm pack` is ok."
161+
"Specifying a directory requires the --pack option to confirm that running `npm pack` is ok.",
162162
);
163163
}
164164
const rl = readline.createInterface(process.stdin, process.stdout);
@@ -173,7 +173,7 @@ particularly ESM-related module resolution issues.`
173173

174174
fileName = deleteTgz = path.resolve(
175175
fileOrDirectory,
176-
execSync("npm pack", { cwd: fileOrDirectory, encoding: "utf8", stdio: "pipe" }).trim()
176+
execSync("npm pack", { cwd: fileOrDirectory, encoding: "utf8", stdio: "pipe" }).trim(),
177177
);
178178
}
179179
const file = await readFile(fileName);
@@ -182,7 +182,7 @@ particularly ESM-related module resolution issues.`
182182
? core
183183
.createPackageFromTarballData(data)
184184
.mergedWithTypes(
185-
core.createPackageFromTarballData(new Uint8Array(await readFile(opts.definitelyTyped as string)))
185+
core.createPackageFromTarballData(new Uint8Array(await readFile(opts.definitelyTyped as string))),
186186
)
187187
: core.createPackageFromTarballData(data);
188188
analysis = await core.checkPackage(pkg, {
@@ -234,9 +234,14 @@ program.parse(process.argv);
234234
function handleError(error: unknown, title: string): never {
235235
if (error && typeof error === "object" && "message" in error) {
236236
program.error(`error while ${title}:\n${error.message}`, {
237+
exitCode: 3,
237238
code: "code" in error && typeof error.code === "string" ? error.code : "UNKNOWN",
238239
});
239240
}
240241

241-
program.error(`unknown error while ${title}`, { code: "UNKNOWN" });
242+
program.error(`unknown error while ${title}`, { code: "UNKNOWN", exitCode: 3 });
242243
}
244+
245+
process.on("unhandledRejection", (error) => {
246+
handleError(error, "checking package");
247+
});

packages/cli/test/snapshots.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ const tests = [
2323
2424
2525
26+
27+
2628

2729
["[email protected]", "--entrypoints vue"],
2830
["[email protected]", "--entrypoints . jsx-runtime"],
@@ -61,6 +63,7 @@ describe("snapshots", async () => {
6163
test(fixture, async () => {
6264
const tarballPath = new URL(`../../../core/test/fixtures/${tarball}`, import.meta.url).pathname;
6365
let stdout;
66+
let stderr = "";
6467
let exitCode = 0;
6568
try {
6669
stdout = execSync(`${attw} ${tarballPath} ${options ?? defaultOpts}`, {
@@ -69,6 +72,7 @@ describe("snapshots", async () => {
6972
});
7073
} catch (error) {
7174
stdout = (error as SpawnSyncReturns<string>).stdout;
75+
stderr = (error as SpawnSyncReturns<string>).stderr;
7276
exitCode = (error as SpawnSyncReturns<string>).status ?? 1;
7377
}
7478
const snapshotURL = new URL(`../snapshots/${fixture.replace(/\//g, "")}.md`, import.meta.url);
@@ -79,7 +83,7 @@ describe("snapshots", async () => {
7983
"```",
8084
`$ attw ${tarball} ${options ?? defaultOpts}`,
8185
"",
82-
stdout,
86+
[stdout, stderr].filter(Boolean).join("\n"),
8387
"",
8488
"```",
8589
"",
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
3+
```
4+
$ attw [email protected] -f table-flipped
5+
6+
error while checking file:
7+
Expected double-quoted property name in JSON at position 450
8+
9+
10+
```
11+
12+
Exit code: 3
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
3+
```
4+
$ attw [email protected] -f table-flipped
5+
6+
7+
moment v2.29.1
8+
9+
Build tools:
10+
- typescript@^1.8.10
11+
12+
13+
No problems found 🌟
14+
15+
16+
┌──────────┬────────┬───────────────────┬───────────────────┬─────────┐
17+
│ │ node10 │ node16 (from CJS) │ node16 (from ESM) │ bundler │
18+
├──────────┼────────┼───────────────────┼───────────────────┼─────────┤
19+
│ "moment" │ 🟢 │ 🟢 (CJS) │ 🟢 (CJS) │ 🟢 │
20+
└──────────┴────────┴───────────────────┴───────────────────┴─────────┘
21+
22+
23+
```
24+
25+
Exit code: 0
14.8 KB
Binary file not shown.
697 KB
Binary file not shown.

packages/core/test/snapshots.test.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ describe("snapshots", async () => {
2626
2727
};
2828

29+
const errorPackages = ["[email protected]"];
30+
2931
for (const fixture of fs.readdirSync(new URL("../fixtures", import.meta.url))) {
3032
if (fixture === ".DS_Store" || fixture.startsWith("@types__")) {
3133
continue;
@@ -35,10 +37,19 @@ describe("snapshots", async () => {
3537
const typesTarball = typesPackages[fixture]
3638
? await readFile(new URL(`../fixtures/${typesPackages[fixture]}`, import.meta.url))
3739
: undefined;
38-
const pkg = createPackageFromTarballData(tarball);
39-
const analysis = await checkPackage(
40-
typesTarball ? pkg.mergedWithTypes(createPackageFromTarballData(typesTarball)) : pkg
41-
);
40+
let analysis;
41+
try {
42+
const pkg = createPackageFromTarballData(tarball);
43+
analysis = await checkPackage(
44+
typesTarball ? pkg.mergedWithTypes(createPackageFromTarballData(typesTarball)) : pkg,
45+
);
46+
} catch (error) {
47+
if (errorPackages.includes(fixture)) {
48+
return;
49+
}
50+
throw error;
51+
}
52+
4253
const snapshotURL = new URL(`../snapshots/${fixture}.json`, import.meta.url);
4354
const expectedSnapshot = JSON.stringify(analysis, null, 2) + "\n";
4455

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
{
2+
"packageName": "moment",
3+
"packageVersion": "2.29.1",
4+
"types": {
5+
"kind": "included"
6+
},
7+
"buildTools": {
8+
"typescript": "^1.8.10",
9+
"rollup": "2.17.1"
10+
},
11+
"entrypoints": {
12+
".": {
13+
"subpath": ".",
14+
"resolutions": {
15+
"node10": {
16+
"name": ".",
17+
"resolutionKind": "node10",
18+
"resolution": {
19+
"fileName": "/node_modules/moment/ts3.1-typings/moment.d.ts",
20+
"isJson": false,
21+
"isTypeScript": true,
22+
"trace": [
23+
"======== Resolving module 'moment' from '/index.ts'. ========",
24+
"Explicitly specified module resolution kind: 'Node10'.",
25+
"Loading module 'moment' from 'node_modules' folder, target file types: TypeScript, Declaration.",
26+
"Searching all ancestor node_modules directories for preferred extensions: TypeScript, Declaration.",
27+
"Found 'package.json' at '/node_modules/moment/package.json'.",
28+
"File '/node_modules/moment.ts' does not exist.",
29+
"File '/node_modules/moment.tsx' does not exist.",
30+
"File '/node_modules/moment.d.ts' does not exist.",
31+
"'package.json' has a 'typesVersions' field with version-specific path mappings.",
32+
"'package.json' has 'typings' field './moment.d.ts' that references '/node_modules/moment/moment.d.ts'.",
33+
"'package.json' has a 'typesVersions' entry '>=3.1' that matches compiler version '5.2.2', looking for a pattern to match module name 'moment.d.ts'.",
34+
"Module name 'moment.d.ts', matched pattern '*'.",
35+
"Trying substitution 'ts3.1-typings/*', candidate module location: 'ts3.1-typings/moment.d.ts'.",
36+
"File '/node_modules/moment/ts3.1-typings/moment.d.ts' exists - use it as a name resolution result.",
37+
"======== Module name 'moment' was successfully resolved to '/node_modules/moment/ts3.1-typings/moment.d.ts' with Package ID 'moment/ts3.1-typings/[email protected]'. ========"
38+
]
39+
},
40+
"files": [
41+
"/node_modules/typescript/lib/lib.d.ts",
42+
"/node_modules/moment/ts3.1-typings/moment.d.ts"
43+
],
44+
"visibleProblems": []
45+
},
46+
"node16-cjs": {
47+
"name": ".",
48+
"resolutionKind": "node16-cjs",
49+
"resolution": {
50+
"fileName": "/node_modules/moment/ts3.1-typings/moment.d.ts",
51+
"isJson": false,
52+
"isTypeScript": true,
53+
"trace": [
54+
"======== Resolving module 'moment' from '/index.ts'. ========",
55+
"Explicitly specified module resolution kind: 'Node16'.",
56+
"Resolving in CJS mode with conditions 'require', 'types', 'node'.",
57+
"File '/package.json' does not exist.",
58+
"Loading module 'moment' from 'node_modules' folder, target file types: TypeScript, JavaScript, Declaration, JSON.",
59+
"Searching all ancestor node_modules directories for preferred extensions: TypeScript, Declaration.",
60+
"Found 'package.json' at '/node_modules/moment/package.json'.",
61+
"File '/node_modules/moment.ts' does not exist.",
62+
"File '/node_modules/moment.tsx' does not exist.",
63+
"File '/node_modules/moment.d.ts' does not exist.",
64+
"'package.json' has a 'typesVersions' field with version-specific path mappings.",
65+
"'package.json' has 'typings' field './moment.d.ts' that references '/node_modules/moment/moment.d.ts'.",
66+
"'package.json' has a 'typesVersions' entry '>=3.1' that matches compiler version '5.2.2', looking for a pattern to match module name 'moment.d.ts'.",
67+
"Module name 'moment.d.ts', matched pattern '*'.",
68+
"Trying substitution 'ts3.1-typings/*', candidate module location: 'ts3.1-typings/moment.d.ts'.",
69+
"File '/node_modules/moment/ts3.1-typings/moment.d.ts' exists - use it as a name resolution result.",
70+
"======== Module name 'moment' was successfully resolved to '/node_modules/moment/ts3.1-typings/moment.d.ts' with Package ID 'moment/ts3.1-typings/[email protected]'. ========"
71+
]
72+
},
73+
"files": [
74+
"/node_modules/typescript/lib/lib.d.ts",
75+
"/node_modules/moment/ts3.1-typings/moment.d.ts"
76+
],
77+
"visibleProblems": []
78+
},
79+
"node16-esm": {
80+
"name": ".",
81+
"resolutionKind": "node16-esm",
82+
"resolution": {
83+
"fileName": "/node_modules/moment/ts3.1-typings/moment.d.ts",
84+
"isJson": false,
85+
"isTypeScript": true,
86+
"trace": [
87+
"======== Resolving module 'moment' from '/index.mts'. ========",
88+
"Explicitly specified module resolution kind: 'Node16'.",
89+
"Resolving in ESM mode with conditions 'import', 'types', 'node'.",
90+
"File '/package.json' does not exist according to earlier cached lookups.",
91+
"Loading module 'moment' from 'node_modules' folder, target file types: TypeScript, JavaScript, Declaration, JSON.",
92+
"Searching all ancestor node_modules directories for preferred extensions: TypeScript, Declaration.",
93+
"File '/node_modules/moment/package.json' exists according to earlier cached lookups.",
94+
"'package.json' has 'typings' field './moment.d.ts' that references '/node_modules/moment/moment.d.ts'.",
95+
"'package.json' has a 'typesVersions' entry '>=3.1' that matches compiler version '5.2.2', looking for a pattern to match module name 'moment.d.ts'.",
96+
"Module name 'moment.d.ts', matched pattern '*'.",
97+
"Trying substitution 'ts3.1-typings/*', candidate module location: 'ts3.1-typings/moment.d.ts'.",
98+
"File '/node_modules/moment/ts3.1-typings/moment.d.ts' exists - use it as a name resolution result.",
99+
"======== Module name 'moment' was successfully resolved to '/node_modules/moment/ts3.1-typings/moment.d.ts' with Package ID 'moment/ts3.1-typings/[email protected]'. ========"
100+
]
101+
},
102+
"files": [
103+
"/node_modules/typescript/lib/lib.d.ts",
104+
"/node_modules/moment/ts3.1-typings/moment.d.ts"
105+
],
106+
"visibleProblems": []
107+
},
108+
"bundler": {
109+
"name": ".",
110+
"resolutionKind": "bundler",
111+
"resolution": {
112+
"fileName": "/node_modules/moment/ts3.1-typings/moment.d.ts",
113+
"isJson": false,
114+
"isTypeScript": true,
115+
"trace": [
116+
"======== Resolving module 'moment' from '/index.ts'. ========",
117+
"Explicitly specified module resolution kind: 'Bundler'.",
118+
"Resolving in CJS mode with conditions 'import', 'types'.",
119+
"File '/package.json' does not exist.",
120+
"Loading module 'moment' from 'node_modules' folder, target file types: TypeScript, JavaScript, Declaration, JSON.",
121+
"Searching all ancestor node_modules directories for preferred extensions: TypeScript, Declaration.",
122+
"Found 'package.json' at '/node_modules/moment/package.json'.",
123+
"File '/node_modules/moment.ts' does not exist.",
124+
"File '/node_modules/moment.tsx' does not exist.",
125+
"File '/node_modules/moment.d.ts' does not exist.",
126+
"'package.json' has a 'typesVersions' field with version-specific path mappings.",
127+
"'package.json' has 'typings' field './moment.d.ts' that references '/node_modules/moment/moment.d.ts'.",
128+
"'package.json' has a 'typesVersions' entry '>=3.1' that matches compiler version '5.2.2', looking for a pattern to match module name 'moment.d.ts'.",
129+
"Module name 'moment.d.ts', matched pattern '*'.",
130+
"Trying substitution 'ts3.1-typings/*', candidate module location: 'ts3.1-typings/moment.d.ts'.",
131+
"File '/node_modules/moment/ts3.1-typings/moment.d.ts' exists - use it as a name resolution result.",
132+
"======== Module name 'moment' was successfully resolved to '/node_modules/moment/ts3.1-typings/moment.d.ts' with Package ID 'moment/ts3.1-typings/[email protected]'. ========"
133+
]
134+
},
135+
"files": [
136+
"/node_modules/typescript/lib/lib.d.ts",
137+
"/node_modules/moment/ts3.1-typings/moment.d.ts"
138+
],
139+
"visibleProblems": []
140+
}
141+
},
142+
"hasTypes": true,
143+
"isWildcard": false
144+
}
145+
},
146+
"programInfo": {
147+
"node10": {},
148+
"node16": {
149+
"moduleKinds": {
150+
"/node_modules/typescript/lib/lib.d.ts": {
151+
"detectedKind": 1,
152+
"detectedReason": "no:type",
153+
"reasonFileName": "/node_modules/typescript/lib/lib.d.ts"
154+
},
155+
"/node_modules/moment/ts3.1-typings/moment.d.ts": {
156+
"detectedKind": 1,
157+
"detectedReason": "no:type",
158+
"reasonFileName": "/node_modules/moment/package.json"
159+
}
160+
}
161+
},
162+
"bundler": {}
163+
},
164+
"problems": []
165+
}

packages/history/scripts/generateFull.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ const excludedSpecs = [
2929
"[email protected]", // File not found: /node_modules/next/types/misc.d.ts
3030
"[email protected]", // File not found: /node_modules/next/types/misc.d.ts
3131
"[email protected]", // File not found: /node_modules/next/types/misc.d.ts
32-
"[email protected]", // Invalid gzip data
33-
"[email protected]", // Invalid gzip data
3432
];
3533

3634
// Array of month starts from 2022-01-01 until the first of this month

0 commit comments

Comments
 (0)