@zeroheight/adoption-cli 2.2.1 → 2.2.3
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/CHANGELOG.md +8 -0
- package/README.md +61 -0
- package/dist/ast/analyze.js +15 -7
- package/dist/ast/parser.d.ts +4 -1
- package/dist/ast/parser.js +1 -3
- package/dist/cli.js +21 -3
- package/dist/commands/analyze.js +8 -1
- package/dist/commands/analyze.utils.js +9 -2
- package/dist/commands/auth.js +3 -0
- package/dist/commands/monitor-repo.js +5 -0
- package/dist/commands/track-package.js +7 -0
- package/dist/common/api.js +14 -3
- package/dist/common/errors.d.ts +12 -0
- package/dist/common/errors.js +21 -0
- package/dist/common/logging.d.ts +4 -0
- package/dist/common/logging.js +26 -0
- package/dist/components/analyze/analyze.js +5 -3
- package/dist/components/analyze/non-interactive-analyze.js +5 -3
- package/dist/components/monitor-repo/non-interactive-monitor-repo.js +6 -1
- package/dist/components/track-package/non-interactive-track-package.js +5 -3
- package/dist/components/track-package/track-package.js +5 -3
- package/package.json +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Release notes
|
|
2
2
|
|
|
3
|
+
## [2.2.3](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/2.2.3) - 28th November 2024
|
|
4
|
+
|
|
5
|
+
- Update README to explain new CLI options
|
|
6
|
+
|
|
7
|
+
## [2.2.2](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/2.2.2) - 26th November 2024
|
|
8
|
+
|
|
9
|
+
- Add global `--log-level` and `--log-file` options
|
|
10
|
+
|
|
3
11
|
## [2.2.1](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/2.2.1) - 13th November 2024
|
|
4
12
|
|
|
5
13
|
- Handle API throttling
|
package/README.md
CHANGED
|
@@ -35,6 +35,19 @@ export ZEROHEIGHT_CLIENT_ID="your-client-id"
|
|
|
35
35
|
export ZEROHEIGHT_ACCESS_TOKEN="your-access-token"
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
+
#### Error logging
|
|
39
|
+
|
|
40
|
+
When running a command with the `--log-level <level> --log-file ./my-log-file.log` flags, diagnostic information will be written to a file. This can help diagnose issues and can be optionally shared with zeroheight directly to help resolve. If you don't specify `--log-file`, logs will be output to `stderr`.
|
|
41
|
+
|
|
42
|
+
There are 4 levels of increasing severity:
|
|
43
|
+
|
|
44
|
+
1. `info`
|
|
45
|
+
2. `warn`
|
|
46
|
+
3. `error`
|
|
47
|
+
4. `fatal`
|
|
48
|
+
|
|
49
|
+
**Note**: You will see logs for the specified level and those from the higher severity groups. For example, if you you choose `--log-level error` then the `error` and `fatal` level logs will be surfaced.
|
|
50
|
+
|
|
38
51
|
---
|
|
39
52
|
|
|
40
53
|
### Component usage analysis
|
|
@@ -81,6 +94,22 @@ Pass in `false` to disable the interactive mode e.g. when running in a CI enviro
|
|
|
81
94
|
zh-adoption analyze --interactive false -r "My Repo"
|
|
82
95
|
```
|
|
83
96
|
|
|
97
|
+
`--log-level`
|
|
98
|
+
|
|
99
|
+
Provide a severity to output diagnostic messages.
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
zh-adoption analyze --log-level info
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Write logs to a file so they can read and/or shared later.
|
|
106
|
+
|
|
107
|
+
`--log-file`
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
zh-adoption analyze --log-file ./dev.log
|
|
111
|
+
```
|
|
112
|
+
|
|
84
113
|
---
|
|
85
114
|
|
|
86
115
|
### Monitor repo
|
|
@@ -105,6 +134,22 @@ Provide a path to the specific folder to find package.json and lock file. This o
|
|
|
105
134
|
zh-adoption monitor-repo -d ./webApp
|
|
106
135
|
```
|
|
107
136
|
|
|
137
|
+
`--log-level`
|
|
138
|
+
|
|
139
|
+
Provide a severity to output diagnostic messages.
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
zh-adoption analyze --log-level info
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Write logs to a file so they can read and/or shared later.
|
|
146
|
+
|
|
147
|
+
`--log-file`
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
zh-adoption analyze --log-file ./dev.log
|
|
151
|
+
```
|
|
152
|
+
|
|
108
153
|
---
|
|
109
154
|
|
|
110
155
|
### Track package
|
|
@@ -129,6 +174,22 @@ Pass in `false` to disable the interactive mode e.g. when running in a CI enviro
|
|
|
129
174
|
zh-adoption track-package --interactive false
|
|
130
175
|
```
|
|
131
176
|
|
|
177
|
+
`--log-level`
|
|
178
|
+
|
|
179
|
+
Provide a severity to output diagnostic messages.
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
zh-adoption analyze --log-level info
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Write logs to a file so they can read and/or shared later.
|
|
186
|
+
|
|
187
|
+
`--log-file`
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
zh-adoption analyze --log-file ./dev.log
|
|
191
|
+
```
|
|
192
|
+
|
|
132
193
|
---
|
|
133
194
|
|
|
134
195
|
More info on the commands can be seen by running
|
package/dist/ast/analyze.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as walk from "acorn-walk";
|
|
2
|
+
import logger from "../common/logging.js";
|
|
2
3
|
const DYNAMIC_EXPRESSIONS = [
|
|
3
4
|
"Identifier",
|
|
4
5
|
"TemplateLiteral",
|
|
@@ -39,7 +40,10 @@ function transformVisitorStateToRawUsage(visitorState) {
|
|
|
39
40
|
const componentProps = currentValue.props;
|
|
40
41
|
props.forEach((prop) => {
|
|
41
42
|
componentProps[prop.name] = {
|
|
42
|
-
type: Array.from(new Set([
|
|
43
|
+
type: Array.from(new Set([
|
|
44
|
+
...(componentProps[prop.name]?.type ?? []),
|
|
45
|
+
...(prop.type ?? []),
|
|
46
|
+
])),
|
|
43
47
|
values: [...(componentProps[prop.name]?.values ?? []), ...prop.values],
|
|
44
48
|
};
|
|
45
49
|
});
|
|
@@ -133,21 +137,24 @@ export function analyze(ast) {
|
|
|
133
137
|
values: [RUNTIME_VALUE],
|
|
134
138
|
});
|
|
135
139
|
}
|
|
136
|
-
else if (expressionType &&
|
|
140
|
+
else if (expressionType &&
|
|
141
|
+
expressionType === "ObjectExpression") {
|
|
137
142
|
attrProps.push({
|
|
138
143
|
name: attr.name.name,
|
|
139
144
|
type: [expressionType],
|
|
140
145
|
values: [OBJECT_VALUE],
|
|
141
146
|
});
|
|
142
147
|
}
|
|
143
|
-
else if (expressionType &&
|
|
148
|
+
else if (expressionType &&
|
|
149
|
+
expressionType === "ArrayExpression") {
|
|
144
150
|
attrProps.push({
|
|
145
151
|
name: attr.name.name,
|
|
146
152
|
type: [expressionType],
|
|
147
153
|
values: [ARRAY_VALUE],
|
|
148
154
|
});
|
|
149
155
|
}
|
|
150
|
-
else if (expressionType &&
|
|
156
|
+
else if (expressionType &&
|
|
157
|
+
expressionType === "BinaryExpression") {
|
|
151
158
|
attrProps.push({
|
|
152
159
|
name: attr.name.name,
|
|
153
160
|
type: [expressionType],
|
|
@@ -158,7 +165,9 @@ export function analyze(ast) {
|
|
|
158
165
|
attrProps.push({
|
|
159
166
|
name: attr.name.name,
|
|
160
167
|
type: [expressionType],
|
|
161
|
-
values: [
|
|
168
|
+
values: [
|
|
169
|
+
attr.value.expression.value || attr.value.expression.name,
|
|
170
|
+
],
|
|
162
171
|
});
|
|
163
172
|
}
|
|
164
173
|
}
|
|
@@ -230,8 +239,7 @@ export function analyze(ast) {
|
|
|
230
239
|
walk.recursive(ast, visitorState, visitorFunctions, walk.base);
|
|
231
240
|
}
|
|
232
241
|
catch (e) {
|
|
233
|
-
|
|
234
|
-
// console.log("error", e);
|
|
242
|
+
logger.error({ error: e, ast }, "Error analyzing file");
|
|
235
243
|
}
|
|
236
244
|
finally {
|
|
237
245
|
return transformVisitorStateToRawUsage(visitorState);
|
package/dist/ast/parser.d.ts
CHANGED
|
@@ -7,4 +7,7 @@
|
|
|
7
7
|
* @param sourceFilename a filename to help with inference
|
|
8
8
|
* @returns Parsed AST
|
|
9
9
|
*/
|
|
10
|
-
export declare function parse(code: string, sourceFilename?: string):
|
|
10
|
+
export declare function parse(code: string, sourceFilename?: string): {
|
|
11
|
+
ast: any;
|
|
12
|
+
errors: string[];
|
|
13
|
+
};
|
package/dist/ast/parser.js
CHANGED
|
@@ -10,7 +10,5 @@ import oxc from "oxc-parser";
|
|
|
10
10
|
*/
|
|
11
11
|
export function parse(code, sourceFilename) {
|
|
12
12
|
const result = oxc.parseSync(code, { sourceFilename, sourceType: "module" });
|
|
13
|
-
|
|
14
|
-
// console.error(result.errors)
|
|
15
|
-
return JSON.parse(result.program);
|
|
13
|
+
return { ast: JSON.parse(result.program), errors: result.errors };
|
|
16
14
|
}
|
package/dist/cli.js
CHANGED
|
@@ -1,23 +1,41 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import * as React from "react";
|
|
3
|
-
import { Command } from "commander";
|
|
3
|
+
import { Command, Option } from "commander";
|
|
4
4
|
import { render } from "ink-render-string";
|
|
5
5
|
import HelpInfo from "./components/help-info.js";
|
|
6
6
|
import { analyzeCommand } from "./commands/analyze.js";
|
|
7
7
|
import { authCommand } from "./commands/auth.js";
|
|
8
8
|
import { monitorRepoCommand } from "./commands/monitor-repo.js";
|
|
9
9
|
import { trackPackageCommand } from "./commands/track-package.js";
|
|
10
|
+
import logger, { setFileStream } from "./common/logging.js";
|
|
10
11
|
const program = new Command();
|
|
11
12
|
const { output, cleanup } = render(React.createElement(HelpInfo, null));
|
|
12
13
|
program
|
|
13
14
|
.name("zh-adoption")
|
|
14
15
|
.description("CLI for measuring design system usage usage in your products")
|
|
15
|
-
.version("2.2.
|
|
16
|
+
.version("2.2.3")
|
|
16
17
|
.addHelpText("before", output)
|
|
18
|
+
.option("--log-file <path>", "Path to write logs to, if not provided logs only error logs will be written to stderr")
|
|
19
|
+
.addOption(new Option("--log-level <level>", "The lowest level of logs to display")
|
|
20
|
+
.default("error")
|
|
21
|
+
.choices(["fatal", "error", "warn", "info"]))
|
|
17
22
|
.addCommand(analyzeCommand())
|
|
18
23
|
.addCommand(authCommand())
|
|
19
24
|
.addCommand(monitorRepoCommand())
|
|
20
|
-
.addCommand(trackPackageCommand())
|
|
25
|
+
.addCommand(trackPackageCommand())
|
|
26
|
+
.hook("preAction", (thisCommand, actionCommand) => {
|
|
27
|
+
logger.level = thisCommand.opts()["logLevel"];
|
|
28
|
+
if (thisCommand.opts()["logFile"]) {
|
|
29
|
+
setFileStream(thisCommand.opts()["logFile"]);
|
|
30
|
+
}
|
|
31
|
+
logger.info({
|
|
32
|
+
command: {
|
|
33
|
+
name: actionCommand.name(),
|
|
34
|
+
opts: actionCommand.opts(),
|
|
35
|
+
globalOpts: thisCommand.opts(),
|
|
36
|
+
},
|
|
37
|
+
}, "Running command");
|
|
38
|
+
});
|
|
21
39
|
cleanup();
|
|
22
40
|
// Only start parsing if run as CLI, don't start parsing during testing
|
|
23
41
|
if (process.env["NODE_ENV"] !== "test") {
|
package/dist/commands/analyze.js
CHANGED
|
@@ -5,6 +5,7 @@ import yn from "yn";
|
|
|
5
5
|
import { analyzeFiles } from "./analyze.utils.js";
|
|
6
6
|
import Analyze from "../components/analyze/analyze.js";
|
|
7
7
|
import NonInteractiveAnalyze from "../components/analyze/non-interactive-analyze.js";
|
|
8
|
+
import logger, { setStdErrStream } from "../common/logging.js";
|
|
8
9
|
export async function analyzeAction(options, renderOptions) {
|
|
9
10
|
if (options.interactive) {
|
|
10
11
|
render(React.createElement(Analyze, { dryRun: options.dryRun, onAnalyzeFiles: () => analyzeFiles(options.extensions, options.ignore), repoName: options.repoName }), renderOptions);
|
|
@@ -18,6 +19,9 @@ export function analyzeCommand() {
|
|
|
18
19
|
return command
|
|
19
20
|
.command("analyze")
|
|
20
21
|
.description("Analyze your codebase to determine component usage metrics")
|
|
22
|
+
.configureHelp({
|
|
23
|
+
showGlobalOptions: true,
|
|
24
|
+
})
|
|
21
25
|
.addOption(new Option("-e, --extensions [ext]", "file extensions to include when searching for components").default("**/*.{js,jsx,ts,tsx}", "glob pattern to determine file extensions"))
|
|
22
26
|
.addOption(new Option("-i, --ignore [ext]", "files to ignore when searching for components").default("**/*.{test,spec}.*", "glob pattern to determine ignored files"))
|
|
23
27
|
.addOption(new Option("-d, --dry-run", "don't push results to zeroheight").default(false))
|
|
@@ -26,11 +30,14 @@ export function analyzeCommand() {
|
|
|
26
30
|
.default(true)
|
|
27
31
|
.argParser((value) => yn(value)))
|
|
28
32
|
.action(async (options) => {
|
|
33
|
+
if (!options.interactive) {
|
|
34
|
+
setStdErrStream();
|
|
35
|
+
}
|
|
29
36
|
try {
|
|
30
37
|
await analyzeAction(options);
|
|
31
38
|
}
|
|
32
39
|
catch (e) {
|
|
33
|
-
|
|
40
|
+
logger.error({ error: e }, "Unhandled exception running analyze command");
|
|
34
41
|
process.exitCode = 1;
|
|
35
42
|
}
|
|
36
43
|
});
|
|
@@ -5,6 +5,7 @@ import { Glob } from "glob";
|
|
|
5
5
|
import ignore from "ignore";
|
|
6
6
|
import { parse } from "../ast/parser.js";
|
|
7
7
|
import { analyze } from "../ast/analyze.js";
|
|
8
|
+
import logger from "../common/logging.js";
|
|
8
9
|
/**
|
|
9
10
|
* Get a list of files matching extensions, skips hidden files and node_modules
|
|
10
11
|
* @param base starting directory
|
|
@@ -56,14 +57,20 @@ export async function analyzeFiles(extensions, ignorePattern) {
|
|
|
56
57
|
for (const file of files) {
|
|
57
58
|
try {
|
|
58
59
|
const fileContents = await readFile(file, "utf-8");
|
|
59
|
-
const ast = parse(fileContents, file);
|
|
60
|
+
const { ast, errors } = parse(fileContents, file);
|
|
61
|
+
if (errors.length > 0) {
|
|
62
|
+
logger.error({ file, errors }, "Error parsing file");
|
|
63
|
+
parseErrors.push(`Can't parse file ${file}`);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
60
66
|
const usage = analyze(ast);
|
|
61
67
|
if (usage.length > 0) {
|
|
62
68
|
const relativePath = file.slice(process.cwd().length);
|
|
63
69
|
usageMap.set(relativePath, usage);
|
|
64
70
|
}
|
|
65
71
|
}
|
|
66
|
-
catch {
|
|
72
|
+
catch (e) {
|
|
73
|
+
logger.error({ file, error: e }, "Error parsing file");
|
|
67
74
|
parseErrors.push(`Can't parse file ${file}`);
|
|
68
75
|
}
|
|
69
76
|
}
|
package/dist/commands/auth.js
CHANGED
|
@@ -10,6 +10,9 @@ export function authCommand() {
|
|
|
10
10
|
return command
|
|
11
11
|
.command("auth")
|
|
12
12
|
.description("Authenticate with zeroheight")
|
|
13
|
+
.configureHelp({
|
|
14
|
+
showGlobalOptions: true,
|
|
15
|
+
})
|
|
13
16
|
.addHelpText("before", "Set credentials for performing actions with zeroheight. Credentials will default to ZEROHEIGHT_CLIENT_ID and ZEROHEIGHT_ACCESS_TOKEN environment variables if not supplied")
|
|
14
17
|
.option("-c, --client <client_id>", "zeroheight Client ID", process.env["ZEROHEIGHT_CLIENT_ID"])
|
|
15
18
|
.option("-t, --token <access_token>", "zeroheight Access Token", process.env["ZEROHEIGHT_ACCESS_TOKEN"])
|
|
@@ -3,6 +3,7 @@ import { Command, Option } from "commander";
|
|
|
3
3
|
import { render } from "ink";
|
|
4
4
|
// import yn from "yn";
|
|
5
5
|
import NonInteractiveMonitorRepo from "../components/monitor-repo/non-interactive-monitor-repo.js";
|
|
6
|
+
import { setStdErrStream } from "../common/logging.js";
|
|
6
7
|
export async function monitorRepoAction(options, _renderOptions) {
|
|
7
8
|
// if (options.interactive) {
|
|
8
9
|
// render(<MonitorRepo />, renderOptions);
|
|
@@ -15,6 +16,9 @@ export function monitorRepoCommand() {
|
|
|
15
16
|
return (command
|
|
16
17
|
.command("monitor-repo")
|
|
17
18
|
.description("Monitor package usage in a repository")
|
|
19
|
+
.configureHelp({
|
|
20
|
+
showGlobalOptions: true,
|
|
21
|
+
})
|
|
18
22
|
// .addOption(
|
|
19
23
|
// new Option(
|
|
20
24
|
// "-in, --interactive [boolean]",
|
|
@@ -25,6 +29,7 @@ export function monitorRepoCommand() {
|
|
|
25
29
|
// )
|
|
26
30
|
.addOption(new Option("-d, --dir <path...>", "use package directory to find package.json and lockfile"))
|
|
27
31
|
.action(async (options) => {
|
|
32
|
+
setStdErrStream();
|
|
28
33
|
try {
|
|
29
34
|
await monitorRepoAction(options);
|
|
30
35
|
}
|
|
@@ -4,6 +4,7 @@ import { render } from "ink";
|
|
|
4
4
|
import yn from "yn";
|
|
5
5
|
import NonInteractiveTrackPackage from "../components/track-package/non-interactive-track-package.js";
|
|
6
6
|
import TrackPackage from "../components/track-package/track-package.js";
|
|
7
|
+
import { setStdErrStream } from "../common/logging.js";
|
|
7
8
|
export async function trackPackageAction(options, renderOptions) {
|
|
8
9
|
if (options.interactive) {
|
|
9
10
|
render(React.createElement(TrackPackage, null), renderOptions);
|
|
@@ -17,10 +18,16 @@ export function trackPackageCommand() {
|
|
|
17
18
|
return command
|
|
18
19
|
.command("track-package")
|
|
19
20
|
.description("Track the latest version of your package in zeroheight")
|
|
21
|
+
.configureHelp({
|
|
22
|
+
showGlobalOptions: true,
|
|
23
|
+
})
|
|
20
24
|
.addOption(new Option("-in, --interactive [boolean]", "disable to skip manual input (useful when running in CI)")
|
|
21
25
|
.default(true)
|
|
22
26
|
.argParser((value) => yn(value)))
|
|
23
27
|
.action(async (options) => {
|
|
28
|
+
if (!options.interactive) {
|
|
29
|
+
setStdErrStream();
|
|
30
|
+
}
|
|
24
31
|
try {
|
|
25
32
|
await trackPackageAction(options);
|
|
26
33
|
}
|
package/dist/common/api.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { MaxRetriesError, UnauthorizedError, UnknownError } from "./errors.js";
|
|
2
|
+
import logger from "./logging.js";
|
|
1
3
|
const API_PATH = "/open_api/v2";
|
|
2
4
|
export var ResponseStatus;
|
|
3
5
|
(function (ResponseStatus) {
|
|
@@ -64,6 +66,7 @@ async function request(path, credentials, init) {
|
|
|
64
66
|
const maxRetries = 3;
|
|
65
67
|
let retries = 0;
|
|
66
68
|
while (retries < maxRetries) {
|
|
69
|
+
logger.info({ request: { url: url.toString(), init, retries } }, "Making API request");
|
|
67
70
|
const response = await fetch(url, {
|
|
68
71
|
...init,
|
|
69
72
|
headers: {
|
|
@@ -74,18 +77,26 @@ async function request(path, credentials, init) {
|
|
|
74
77
|
},
|
|
75
78
|
});
|
|
76
79
|
if (response.status === 401) {
|
|
77
|
-
|
|
80
|
+
logger.error({ response: { status: response.status, body: await response.text() } }, "Unauthorized response from API");
|
|
81
|
+
throw new UnauthorizedError();
|
|
78
82
|
}
|
|
79
83
|
else if (response.status === 429) {
|
|
80
84
|
retries++;
|
|
81
85
|
const responseData = await response.json();
|
|
82
86
|
const waitTime = responseData.data.reset_time * 1000 - Date.now();
|
|
87
|
+
logger.info(`Rate limited, waiting for ${waitTime}ms`);
|
|
83
88
|
await sleep(waitTime);
|
|
84
89
|
continue;
|
|
85
90
|
}
|
|
86
|
-
|
|
91
|
+
else if (response.status < 200 || response.status >= 300) {
|
|
92
|
+
logger.error({ response: { status: response.status, body: await response.text() } }, "API request failed");
|
|
93
|
+
throw new UnknownError();
|
|
94
|
+
}
|
|
95
|
+
const responseJson = await response.json();
|
|
96
|
+
logger.info({ response: { status: response.status, body: responseJson } }, "API response");
|
|
97
|
+
return responseJson;
|
|
87
98
|
}
|
|
88
|
-
throw new
|
|
99
|
+
throw new MaxRetriesError();
|
|
89
100
|
}
|
|
90
101
|
export function mergeUsageProps(newProps, currentProps) {
|
|
91
102
|
if (!currentProps)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare class ApiError extends Error {
|
|
2
|
+
constructor(message: string);
|
|
3
|
+
}
|
|
4
|
+
export declare class UnauthorizedError extends ApiError {
|
|
5
|
+
constructor();
|
|
6
|
+
}
|
|
7
|
+
export declare class UnknownError extends ApiError {
|
|
8
|
+
constructor();
|
|
9
|
+
}
|
|
10
|
+
export declare class MaxRetriesError extends ApiError {
|
|
11
|
+
constructor();
|
|
12
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export class ApiError extends Error {
|
|
2
|
+
constructor(message) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = "ApiError";
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export class UnauthorizedError extends ApiError {
|
|
8
|
+
constructor() {
|
|
9
|
+
super("Unauthorized. Please reset your authentication by running: npx @zeroheight/adoption-cli auth");
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export class UnknownError extends ApiError {
|
|
13
|
+
constructor() {
|
|
14
|
+
super("An unknown error occurred while calling the zeroheight API");
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export class MaxRetriesError extends ApiError {
|
|
18
|
+
constructor() {
|
|
19
|
+
super("Max retries exceeded while calling the zeroheight API");
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { pino } from "pino";
|
|
2
|
+
import { createWriteStream } from "fs";
|
|
3
|
+
import pretty from "pino-pretty";
|
|
4
|
+
const streams = [];
|
|
5
|
+
const loggerOptions = {
|
|
6
|
+
base: null,
|
|
7
|
+
level: "warn",
|
|
8
|
+
timestamp: pino.stdTimeFunctions.isoTime,
|
|
9
|
+
formatters: {
|
|
10
|
+
level: (label) => {
|
|
11
|
+
return { level: label.toUpperCase() };
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
const multistream = pino.multistream(streams);
|
|
16
|
+
const logger = pino(loggerOptions, multistream);
|
|
17
|
+
export function setFileStream(path) {
|
|
18
|
+
multistream.add({ stream: createWriteStream(path, { flags: "a" }) });
|
|
19
|
+
}
|
|
20
|
+
export function setStdErrStream() {
|
|
21
|
+
multistream.add({
|
|
22
|
+
stream: pretty({ destination: process.stderr, ignore: "pid,hostname" }),
|
|
23
|
+
level: "error",
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
export default logger;
|
|
@@ -10,6 +10,7 @@ import UsageTable from "../usage-table.js";
|
|
|
10
10
|
import { configPath, writeConfig, readConfig, } from "../../common/config.js";
|
|
11
11
|
import { ResponseStatus, getAuthDetails, getZeroheightURL, submitUsageData, } from "../../common/api.js";
|
|
12
12
|
import { calculateNumberOfComponents } from "../../commands/analyze.utils.js";
|
|
13
|
+
import { ApiError } from "../../common/errors.js";
|
|
13
14
|
export default function Analyze({ onAnalyzeFiles, dryRun, repoName, }) {
|
|
14
15
|
const { exit } = useApp();
|
|
15
16
|
const [usageResult, setUsageResult] = React.useState(null);
|
|
@@ -61,9 +62,10 @@ export default function Analyze({ onAnalyzeFiles, dryRun, repoName, }) {
|
|
|
61
62
|
setResourceURL(resourceURL);
|
|
62
63
|
}
|
|
63
64
|
catch (e) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
let errorMessage = "Failed to send data to zeroheight";
|
|
66
|
+
if (e instanceof ApiError) {
|
|
67
|
+
errorMessage = e.message;
|
|
68
|
+
}
|
|
67
69
|
setErrorList((s) => [...s, errorMessage]);
|
|
68
70
|
}
|
|
69
71
|
finally {
|
|
@@ -5,6 +5,7 @@ import Link from "ink-link";
|
|
|
5
5
|
import { calculateNumberOfComponents } from "../../commands/analyze.utils.js";
|
|
6
6
|
import { readConfig } from "../../common/config.js";
|
|
7
7
|
import { submitUsageData } from "../../common/api.js";
|
|
8
|
+
import { ApiError } from "../../common/errors.js";
|
|
8
9
|
export default function NonInteractiveAnalyze({ repoName, onAnalyzeFiles, }) {
|
|
9
10
|
const [isSendingData, setIsSendingData] = React.useState(false);
|
|
10
11
|
const [isAnalyzingFiles, setIsAnalyzingFiles] = React.useState(false);
|
|
@@ -36,9 +37,10 @@ export default function NonInteractiveAnalyze({ repoName, onAnalyzeFiles, }) {
|
|
|
36
37
|
await submitUsageData(result, repoName, credentials);
|
|
37
38
|
}
|
|
38
39
|
catch (e) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
let errorMessage = "Failed to send data to zeroheight";
|
|
41
|
+
if (e instanceof ApiError) {
|
|
42
|
+
errorMessage = e.message;
|
|
43
|
+
}
|
|
42
44
|
setErrorList((s) => [...s, errorMessage]);
|
|
43
45
|
}
|
|
44
46
|
finally {
|
|
@@ -7,6 +7,7 @@ import { findPackageFiles } from "../../commands/track-package.utils.js";
|
|
|
7
7
|
import { ResponseStatus, submitMonitoredRepoDetails, } from "../../common/api.js";
|
|
8
8
|
import { readConfig } from "../../common/config.js";
|
|
9
9
|
import YarnLockParser from "../../lockfile-parsers/yarn-lock-parser.js";
|
|
10
|
+
import { ApiError } from "../../common/errors.js";
|
|
10
11
|
var Step;
|
|
11
12
|
(function (Step) {
|
|
12
13
|
Step[Step["COMPLETE"] = 0] = "COMPLETE";
|
|
@@ -82,7 +83,11 @@ export default function NonInteractiveMonitorRepo({ packageDirs, }) {
|
|
|
82
83
|
}
|
|
83
84
|
}
|
|
84
85
|
catch (e) {
|
|
85
|
-
|
|
86
|
+
let errorMessage = "Failed to send data to zeroheight";
|
|
87
|
+
if (e instanceof ApiError) {
|
|
88
|
+
errorMessage = e.message;
|
|
89
|
+
}
|
|
90
|
+
updateRepoStatus(packageMeta.name, Step.ERRORED, errorMessage);
|
|
86
91
|
}
|
|
87
92
|
}
|
|
88
93
|
}
|
|
@@ -4,6 +4,7 @@ import Spinner from "ink-spinner";
|
|
|
4
4
|
import { readConfig } from "../../common/config.js";
|
|
5
5
|
import { getAliasesFromExports, getPackageInfo, } from "../../commands/track-package.utils.js";
|
|
6
6
|
import { ResponseStatus, submitPackageDetails } from "../../common/api.js";
|
|
7
|
+
import { ApiError } from "../../common/errors.js";
|
|
7
8
|
var Step;
|
|
8
9
|
(function (Step) {
|
|
9
10
|
Step[Step["COMPLETE"] = 0] = "COMPLETE";
|
|
@@ -63,9 +64,10 @@ export default function NonInteractiveTrackPackage() {
|
|
|
63
64
|
exit();
|
|
64
65
|
}
|
|
65
66
|
catch (e) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
let errorMessage = "Failed to send data to zeroheight";
|
|
68
|
+
if (e instanceof ApiError) {
|
|
69
|
+
errorMessage = e.message;
|
|
70
|
+
}
|
|
69
71
|
setErrorMessage(errorMessage);
|
|
70
72
|
setCurrentStep(Step.ERRORED);
|
|
71
73
|
exit();
|
|
@@ -7,6 +7,7 @@ import ConfirmInput from "../ui/confirm-input.js";
|
|
|
7
7
|
import { readConfig } from "../../common/config.js";
|
|
8
8
|
import { getAliasesFromExports, getPackageInfo, } from "../../commands/track-package.utils.js";
|
|
9
9
|
import { submitPackageDetails } from "../../common/api.js";
|
|
10
|
+
import { ApiError } from "../../common/errors.js";
|
|
10
11
|
var Step;
|
|
11
12
|
(function (Step) {
|
|
12
13
|
Step[Step["COMPLETE"] = 0] = "COMPLETE";
|
|
@@ -61,9 +62,10 @@ export default function TrackPackage() {
|
|
|
61
62
|
}
|
|
62
63
|
}
|
|
63
64
|
catch (e) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
let errorMessage = "Failed to send data to zeroheight";
|
|
66
|
+
if (e instanceof ApiError) {
|
|
67
|
+
errorMessage = e.message;
|
|
68
|
+
}
|
|
67
69
|
setErrorMessage(errorMessage);
|
|
68
70
|
setCurrentStep(Step.ERRORED);
|
|
69
71
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeroheight/adoption-cli",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.3",
|
|
4
4
|
"license": "ISC",
|
|
5
5
|
"main": "dist/cli.js",
|
|
6
6
|
"bin": {
|
|
@@ -39,6 +39,8 @@
|
|
|
39
39
|
"ink-spinner": "^5.0.0",
|
|
40
40
|
"ink-text-input": "^6.0.0",
|
|
41
41
|
"oxc-parser": "^0.22.0",
|
|
42
|
+
"pino": "^9.5.0",
|
|
43
|
+
"pino-pretty": "^13.0.0",
|
|
42
44
|
"react": "^18.2.0",
|
|
43
45
|
"yaml": "^2.5.1",
|
|
44
46
|
"yn": "^5.0.0"
|