@zeroheight/adoption-cli 2.4.2 → 3.0.0
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 +14 -1
- package/README.md +26 -4
- package/dist/ast/analyze.d.ts +11 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +18 -5
- package/dist/commands/analyze.d.ts +5 -3
- package/dist/commands/analyze.js +9 -4
- package/dist/commands/analyze.utils.d.ts +10 -11
- package/dist/commands/analyze.utils.js +87 -0
- package/dist/commands/track-package.d.ts +1 -0
- package/dist/commands/track-package.js +3 -1
- package/dist/commands/track-package.utils.d.ts +12 -1
- package/dist/commands/track-package.utils.js +33 -4
- package/dist/common/api.d.ts +20 -2
- package/dist/common/api.js +27 -1
- package/dist/components/analyze/analyze.d.ts +7 -4
- package/dist/components/analyze/analyze.js +330 -112
- package/dist/components/analyze/non-interactive-analyze.d.ts +9 -3
- package/dist/components/analyze/non-interactive-analyze.js +57 -19
- package/dist/components/auth/no-credentials-onboarding.js +3 -1
- package/dist/components/color-usage-table.d.ts +7 -0
- package/dist/components/color-usage-table.js +38 -0
- package/dist/components/latest-version-info.d.ts +9 -0
- package/dist/components/latest-version-info.js +27 -0
- package/dist/components/track-package/non-interactive-track-package.d.ts +4 -1
- package/dist/components/track-package/non-interactive-track-package.js +3 -3
- package/dist/components/ui/continue-prompt.js +4 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,12 +1,25 @@
|
|
|
1
1
|
# Release notes
|
|
2
2
|
|
|
3
|
+
## [3.0.0](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/3.0.0) - 15th March 2025
|
|
4
|
+
|
|
5
|
+
- `analyze` command updated to collect date about color usage
|
|
6
|
+
- For non-interactive command new flags introduced: `--color-usage` and `--component-usage`
|
|
7
|
+
- Improvement to usability of interactive `analyze` command
|
|
8
|
+
- Remove `--dry-run` from interactive `analyze`
|
|
9
|
+
- Additional prompt added if using an outdated version of the CLI tool
|
|
10
|
+
- Allow `track-package` command to use package where version is unspecified
|
|
11
|
+
|
|
12
|
+
## [2.4.3](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/2.4.3) - 2nd January 2025
|
|
13
|
+
|
|
14
|
+
- `track-package --packages` option can be used to allow tracking specific packages within monorepos in non-interactive mode
|
|
15
|
+
|
|
3
16
|
## [2.4.2](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/2.4.2) - 17th December 2024
|
|
4
17
|
|
|
5
18
|
- Fix issue with parsing pnpm lockfiles using version `6.0`
|
|
6
19
|
|
|
7
20
|
## [2.4.1](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/2.4.1) - 11th December 2024
|
|
8
21
|
|
|
9
|
-
- The monitor-repo command now supports custom package naming for projects.
|
|
22
|
+
- The `monitor-repo` command now supports custom package naming for projects.
|
|
10
23
|
|
|
11
24
|
## [2.4.0](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/2.4.0) - 10th December 2024
|
|
12
25
|
|
package/README.md
CHANGED
|
@@ -50,17 +50,27 @@ There are 4 levels of increasing severity:
|
|
|
50
50
|
|
|
51
51
|
---
|
|
52
52
|
|
|
53
|
+
### Color usage analysis
|
|
54
|
+
|
|
55
|
+
[Help center article](https://zeroheight.com/help/article/color-usage/)
|
|
56
|
+
|
|
57
|
+
In the repository in which you wish to get usage metrics around how raw color values are being used, run the following command:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
zh-adoption analyze --color-usage
|
|
61
|
+
```
|
|
62
|
+
|
|
53
63
|
### Component usage analysis
|
|
54
64
|
|
|
55
65
|
[Help center article](https://zeroheight.com/help/article/component-usage/)
|
|
56
66
|
|
|
57
|
-
In the React repository in which you wish to get usage metrics around how components from your design system packages are being used, run the following command:
|
|
67
|
+
In the **React** repository in which you wish to get usage metrics around how components from your design system packages are being used, run the following command:
|
|
58
68
|
|
|
59
69
|
```bash
|
|
60
|
-
zh-adoption analyze
|
|
70
|
+
zh-adoption analyze --component-usage
|
|
61
71
|
```
|
|
62
72
|
|
|
63
|
-
|
|
73
|
+
### Analyze command options
|
|
64
74
|
|
|
65
75
|
`-e` / `--extensions`
|
|
66
76
|
|
|
@@ -72,7 +82,7 @@ zh-adoption analyze -e "**/*.{js,jsx,ts,tsx}"
|
|
|
72
82
|
|
|
73
83
|
`-i` / `--ignore`
|
|
74
84
|
|
|
75
|
-
Provide a glob pattern to ignore files, directories or file extensions when
|
|
85
|
+
Provide a glob pattern to ignore files, directories or file extensions when analyzing.
|
|
76
86
|
|
|
77
87
|
```bash
|
|
78
88
|
zh-adoption analyze -i "**/*.{test,spec}.*"
|
|
@@ -186,6 +196,18 @@ Pass in `false` to disable the interactive mode e.g. when running in a CI enviro
|
|
|
186
196
|
zh-adoption track-package --interactive false
|
|
187
197
|
```
|
|
188
198
|
|
|
199
|
+
`-p` / `--packages`
|
|
200
|
+
|
|
201
|
+
Provide one or multiple package names to match when searching for design system packages. This must be passed when `-in` / `--interactive` is set to `false`.
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
zh-adoption track-package -in false -p package1 -p package2 -p package3
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
zh-adoption track-package -in false --packages package1,package2,package3
|
|
209
|
+
```
|
|
210
|
+
|
|
189
211
|
`--log-level`
|
|
190
212
|
|
|
191
213
|
Provide a severity to output diagnostic messages.
|
package/dist/ast/analyze.d.ts
CHANGED
|
@@ -31,6 +31,17 @@ export type RawUsage = {
|
|
|
31
31
|
package: string;
|
|
32
32
|
props: ComponentProps;
|
|
33
33
|
};
|
|
34
|
+
export type RawColorUsage = {
|
|
35
|
+
hex: string[];
|
|
36
|
+
rgb: string[];
|
|
37
|
+
hsla: string[];
|
|
38
|
+
oklab: string[];
|
|
39
|
+
hwb: string[];
|
|
40
|
+
lab: string[];
|
|
41
|
+
lch: string[];
|
|
42
|
+
colorSpace: string[];
|
|
43
|
+
totalCount: number;
|
|
44
|
+
};
|
|
34
45
|
declare const RUNTIME_VALUE = "zh-runtime-value";
|
|
35
46
|
export declare const ARRAY_VALUE = "zh-array-value";
|
|
36
47
|
export declare const BINARY_VALUE = "zh-binary-value";
|
package/dist/cli.d.ts
CHANGED
package/dist/cli.js
CHANGED
|
@@ -3,18 +3,26 @@ import * as React from "react";
|
|
|
3
3
|
import { Command, Option } from "commander";
|
|
4
4
|
import { render } from "ink-render-string";
|
|
5
5
|
import HelpInfo from "./components/help-info.js";
|
|
6
|
+
import LatestVersionInfo from "./components/latest-version-info.js";
|
|
6
7
|
import { analyzeCommand } from "./commands/analyze.js";
|
|
7
8
|
import { authCommand } from "./commands/auth.js";
|
|
8
9
|
import { monitorRepoCommand } from "./commands/monitor-repo.js";
|
|
9
10
|
import { trackPackageCommand } from "./commands/track-package.js";
|
|
10
11
|
import logger, { setFileStream } from "./common/logging.js";
|
|
11
12
|
const program = new Command();
|
|
12
|
-
const
|
|
13
|
+
export const CURRENT_VERSION = "3.0.0";
|
|
14
|
+
async function getLatestPackageVersion() {
|
|
15
|
+
const response = await fetch("https://registry.npmjs.org/@zeroheight/adoption-cli/latest");
|
|
16
|
+
const json = await response.json();
|
|
17
|
+
return json.version;
|
|
18
|
+
}
|
|
19
|
+
const { output: helpOutput, cleanup: helpCleanup } = render(React.createElement(HelpInfo, null));
|
|
20
|
+
const { output: latestVersionOutput, cleanup: latestVersionCleanup } = render(React.createElement(LatestVersionInfo, { latestVersion: await getLatestPackageVersion() }));
|
|
13
21
|
program
|
|
14
22
|
.name("zh-adoption")
|
|
15
23
|
.description("CLI for measuring design system usage usage in your products")
|
|
16
|
-
.version(
|
|
17
|
-
.addHelpText("before",
|
|
24
|
+
.version(CURRENT_VERSION)
|
|
25
|
+
.addHelpText("before", helpOutput)
|
|
18
26
|
.option("--log-file <path>", "Path to write logs to, if not provided logs only error logs will be written to stderr")
|
|
19
27
|
.addOption(new Option("--log-level <level>", "The lowest level of logs to display")
|
|
20
28
|
.default("error")
|
|
@@ -23,7 +31,7 @@ program
|
|
|
23
31
|
.addCommand(authCommand())
|
|
24
32
|
.addCommand(monitorRepoCommand())
|
|
25
33
|
.addCommand(trackPackageCommand())
|
|
26
|
-
.hook("preAction", (thisCommand, actionCommand) => {
|
|
34
|
+
.hook("preAction", async (thisCommand, actionCommand) => {
|
|
27
35
|
logger.level = thisCommand.opts()["logLevel"];
|
|
28
36
|
if (thisCommand.opts()["logFile"]) {
|
|
29
37
|
setFileStream(thisCommand.opts()["logFile"]);
|
|
@@ -35,8 +43,13 @@ program
|
|
|
35
43
|
globalOpts: thisCommand.opts(),
|
|
36
44
|
},
|
|
37
45
|
}, "Running command");
|
|
46
|
+
const latestVersion = await getLatestPackageVersion();
|
|
47
|
+
if (latestVersion !== CURRENT_VERSION) {
|
|
48
|
+
console.log(latestVersionOutput);
|
|
49
|
+
}
|
|
38
50
|
});
|
|
39
|
-
|
|
51
|
+
helpCleanup();
|
|
52
|
+
latestVersionCleanup();
|
|
40
53
|
// Only start parsing if run as CLI, don't start parsing during testing
|
|
41
54
|
if (process.env["NODE_ENV"] !== "test") {
|
|
42
55
|
program.parse();
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { RenderOptions } from "ink";
|
|
2
2
|
import { Command } from "commander";
|
|
3
|
-
import { RawUsage } from "../ast/analyze.js";
|
|
3
|
+
import { RawColorUsage, RawUsage } from "../ast/analyze.js";
|
|
4
4
|
interface AnalyzeOptions {
|
|
5
5
|
extensions: string;
|
|
6
6
|
ignore: string[];
|
|
7
|
-
dryRun: boolean;
|
|
8
7
|
repoName?: string;
|
|
9
8
|
interactive: boolean;
|
|
9
|
+
componentUsage: boolean;
|
|
10
|
+
tokenUsage: boolean;
|
|
10
11
|
}
|
|
11
|
-
export type
|
|
12
|
+
export type RawComponentUsageMap = Map<string, RawUsage[]>;
|
|
13
|
+
export type RawColorUsageMap = Map<string, RawColorUsage>;
|
|
12
14
|
export declare function analyzeAction(options: AnalyzeOptions, renderOptions?: RenderOptions): Promise<void>;
|
|
13
15
|
export declare function analyzeCommand(): Command;
|
|
14
16
|
export {};
|
package/dist/commands/analyze.js
CHANGED
|
@@ -2,16 +2,16 @@ import * as React from "react";
|
|
|
2
2
|
import { render } from "ink";
|
|
3
3
|
import { Command, Option } from "commander";
|
|
4
4
|
import yn from "yn";
|
|
5
|
-
import { analyzeFiles, parseGlobList } from "./analyze.utils.js";
|
|
5
|
+
import { analyzeFiles, analyzeRawColorUsage, parseGlobList, } 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
8
|
import logger, { setStdErrStream } from "../common/logging.js";
|
|
9
9
|
export async function analyzeAction(options, renderOptions) {
|
|
10
10
|
if (options.interactive) {
|
|
11
|
-
render(React.createElement(Analyze, {
|
|
11
|
+
render(React.createElement(Analyze, { onAnalyzeFiles: () => analyzeFiles(options.extensions, options.ignore), onAnalyzeColorUsage: () => analyzeRawColorUsage(options.extensions, options.ignore), repoName: options.repoName }), renderOptions);
|
|
12
12
|
}
|
|
13
13
|
else {
|
|
14
|
-
render(React.createElement(NonInteractiveAnalyze, { repoName: options.repoName, onAnalyzeFiles: () => analyzeFiles(options.extensions, options.ignore) }));
|
|
14
|
+
render(React.createElement(NonInteractiveAnalyze, { repoName: options.repoName, onAnalyzeFiles: () => analyzeFiles(options.extensions, options.ignore), onAnalyzeColorUsage: () => analyzeRawColorUsage(options.extensions, options.ignore), shouldAnalyzeComponentUsage: options.componentUsage, shouldAnalyzeTokenUsage: options.tokenUsage }));
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
export function analyzeCommand() {
|
|
@@ -26,11 +26,16 @@ export function analyzeCommand() {
|
|
|
26
26
|
.addOption(new Option("-i, --ignore [patterns]", "files to ignore when searching for components, use multiple times to add more than one glob pattern")
|
|
27
27
|
.default(["**/*.{test,spec}.*", "**/*.d.ts"], "*.test.*, *.spec.* and *.d.ts files")
|
|
28
28
|
.argParser(parseGlobList))
|
|
29
|
-
.addOption(new Option("-d, --dry-run", "don't push results to zeroheight").default(false))
|
|
30
29
|
.addOption(new Option("-r, --repo-name <string>", "name of the repository"))
|
|
31
30
|
.addOption(new Option("-in, --interactive [boolean]", "disable to skip input (useful when running in CI)")
|
|
32
31
|
.default(true)
|
|
33
32
|
.argParser((value) => yn(value)))
|
|
33
|
+
.addOption(new Option("-cu, --component-usage [boolean]", "gather data about component and prop usage")
|
|
34
|
+
.default(true)
|
|
35
|
+
.argParser((value) => yn(value)))
|
|
36
|
+
.addOption(new Option("-cou, --color-usage [boolean]", "gather data about color usage")
|
|
37
|
+
.default(true)
|
|
38
|
+
.argParser((value) => yn(value)))
|
|
34
39
|
.action(async (options) => {
|
|
35
40
|
if (!options.interactive) {
|
|
36
41
|
setStdErrStream();
|
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
import type { Option } from "commander";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
export interface ComponentUsageRecord {
|
|
5
|
-
name: string;
|
|
6
|
-
files: string[];
|
|
7
|
-
count: number;
|
|
8
|
-
package: string;
|
|
9
|
-
props: ComponentProps;
|
|
10
|
-
}
|
|
2
|
+
import { RawColorUsageMap, RawComponentUsageMap } from "./analyze.js";
|
|
3
|
+
import { RawColorUsage } from "../ast/analyze.js";
|
|
11
4
|
/**
|
|
12
5
|
* Get a list of files matching extensions, skips hidden files and node_modules
|
|
13
6
|
* @param base starting directory
|
|
@@ -19,9 +12,15 @@ export interface ComponentUsageRecord {
|
|
|
19
12
|
export declare function findFiles(base: string, extensions: string, ignorePattern: string[]): Promise<string[]>;
|
|
20
13
|
export declare function analyzeFiles(extensions: string, ignorePattern: string[]): Promise<{
|
|
21
14
|
errorFile: string | null;
|
|
22
|
-
usage:
|
|
15
|
+
usage: RawComponentUsageMap;
|
|
16
|
+
}>;
|
|
17
|
+
export declare function calculateNumberOfComponents(usageResult: RawComponentUsageMap): number;
|
|
18
|
+
export declare function calculateNumberOfColors(usageResult: RawColorUsageMap): number;
|
|
19
|
+
export declare function countColorOccurrences(fileContent: string): RawColorUsage;
|
|
20
|
+
export declare function analyzeRawColorUsage(extensions: string, ignorePattern: string[]): Promise<{
|
|
21
|
+
errorFile: string | null;
|
|
22
|
+
usage: RawColorUsageMap;
|
|
23
23
|
}>;
|
|
24
|
-
export declare function calculateNumberOfComponents(usageResult: RawUsageMap): number;
|
|
25
24
|
/**
|
|
26
25
|
* Parse the ignore array and format correctly
|
|
27
26
|
*/
|
|
@@ -101,6 +101,93 @@ export function calculateNumberOfComponents(usageResult) {
|
|
|
101
101
|
}
|
|
102
102
|
return 0;
|
|
103
103
|
}
|
|
104
|
+
export function calculateNumberOfColors(usageResult) {
|
|
105
|
+
if (!usageResult)
|
|
106
|
+
return 0;
|
|
107
|
+
const colors = Array.from(usageResult.entries()).flatMap((cols) => cols[1]);
|
|
108
|
+
const colorCount = colors.reduce((prev, next) => {
|
|
109
|
+
return prev + next.totalCount;
|
|
110
|
+
}, 0);
|
|
111
|
+
return colorCount;
|
|
112
|
+
}
|
|
113
|
+
export function countColorOccurrences(fileContent) {
|
|
114
|
+
// Matches #FFF, #FFFA, #FFFFFF or #FFFFFFA
|
|
115
|
+
const hexColorRegex = /#([a-fA-F0-9]{6}|[a-fA-F0-9]{8})\b|#([a-fA-F0-9]{3})\b/gi;
|
|
116
|
+
const hexMatches = Array.from(fileContent.match(hexColorRegex) ?? []);
|
|
117
|
+
// Matches rgb(255, 255, 255) or rgba(255, 255, 255) (with or without commas)
|
|
118
|
+
const rgbColorRegex = /rgba?\(\s*(\d{1,3})\s*[,\s]\s*(\d{1,3})\s*[,\s]\s*(\d{1,3})(?:\s*[,\s]\s*(\d*\.?\d+))?\s*\)/g;
|
|
119
|
+
const rgbMatches = Array.from(fileContent.match(rgbColorRegex) ?? []);
|
|
120
|
+
// Matches hsl and hsla values
|
|
121
|
+
const hslaColorRegex = /hsl(?:a)?\(\s*(?:(?:none|from\s+[a-zA-Z#0-9()]+)?\s*(?:[a-z\d+\-*/%()\s]+)\s*|(\d+(?:deg|grad|rad|turn)?)\s*(\d+%?)\s*(\d+%?)\s*(?:\/\s*(\d+%?|0?\.\d+))?|(\d+(?:deg|grad|rad|turn)?)\s*,\s*(\d+%)\s*,\s*(\d+%)\s*(?:,\s*(\d+(\.\d+)?%?))?)\)/g;
|
|
122
|
+
const hslaMatches = Array.from(fileContent.match(hslaColorRegex) ?? []);
|
|
123
|
+
// Matches oklab and oklch values (with or without commas and opacity)
|
|
124
|
+
const oklabColorRegex = /okl(?:ab|ch)\(\s*(-?\d*\.?\d+)\s*[,\s]\s*(-?\d*\.?\d+)\s*[,\s]\s*(-?\d*\.?\d+)(?:\s*[,\s]\s*(\d*\.?\d+))?\s*\)/g;
|
|
125
|
+
const oklabMatches = Array.from(fileContent.match(oklabColorRegex) ?? []);
|
|
126
|
+
// Matches hwb values (with or without commas and opacity)
|
|
127
|
+
const hwbColorRegex = /hwb\(\s*(\d*\.?\d+)(?:deg|turn|rad|grad)?\s*[,\s]\s*(\d*\.?\d+%)\s*[,\s]\s*(\d*\.?\d+%)(?:\s*[,\s]\s*(\d*\.?\d+))?\s*\)/g;
|
|
128
|
+
const hwbMatches = Array.from(fileContent.match(hwbColorRegex) ?? []);
|
|
129
|
+
// Matches lab values (with or without commas and opacity)
|
|
130
|
+
const labColorRegex = /(?<!ok)lab\(\s*(-?\d*\.?\d+%)\s*[,\s]\s*(-?\d*\.?\d+)\s*[,\s]\s*(-?\d*\.?\d+)(?:\s*[,\s]\s*(\d*\.?\d+))?\s*\)/g;
|
|
131
|
+
const labMatches = Array.from(fileContent.match(labColorRegex) ?? []);
|
|
132
|
+
// Matches lch values (with or without commas and opacity)
|
|
133
|
+
const lchColorRegex = /(?<!ok)lch\(\s*(-?\d*\.?\d+%)\s*[,\s]\s*(-?\d*\.?\d+)\s*[,\s]\s*(-?\d*\.?\d+)(?:\s*[,\s]\s*(\d*\.?\d+))?\s*\)/g;
|
|
134
|
+
const lchMatches = Array.from(fileContent.match(lchColorRegex) ?? []);
|
|
135
|
+
// Matches color space values
|
|
136
|
+
const colorSpaceColorRegex = /color\(\s*([\w-]+)\s+(?:[-+]?\d*\.?\d+(?:e[-+]?\d+)?%?\s*){3,4}\)/g;
|
|
137
|
+
const colorSpaceMatches = Array.from(fileContent.match(colorSpaceColorRegex) ?? []);
|
|
138
|
+
return {
|
|
139
|
+
hex: hexMatches,
|
|
140
|
+
rgb: rgbMatches,
|
|
141
|
+
hsla: hslaMatches,
|
|
142
|
+
oklab: oklabMatches,
|
|
143
|
+
hwb: hwbMatches,
|
|
144
|
+
lab: labMatches,
|
|
145
|
+
lch: lchMatches,
|
|
146
|
+
colorSpace: colorSpaceMatches,
|
|
147
|
+
totalCount: hexMatches.length +
|
|
148
|
+
rgbMatches.length +
|
|
149
|
+
hslaMatches.length +
|
|
150
|
+
hwbMatches.length +
|
|
151
|
+
labMatches.length +
|
|
152
|
+
lchMatches.length +
|
|
153
|
+
colorSpaceMatches.length +
|
|
154
|
+
oklabMatches.length,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
export async function analyzeRawColorUsage(extensions, ignorePattern) {
|
|
158
|
+
const files = await findFiles(process.cwd(), extensions, ignorePattern);
|
|
159
|
+
if (files.length === 0) {
|
|
160
|
+
throw new Error("Can't find any relevant files");
|
|
161
|
+
}
|
|
162
|
+
const parseErrors = [];
|
|
163
|
+
const usageMap = new Map();
|
|
164
|
+
for (const file of files) {
|
|
165
|
+
try {
|
|
166
|
+
const fileContents = await readFile(file, "utf-8");
|
|
167
|
+
const colorUsage = countColorOccurrences(fileContents);
|
|
168
|
+
if (colorUsage.totalCount > 0) {
|
|
169
|
+
const relativePath = file.slice(process.cwd().length);
|
|
170
|
+
usageMap.set(relativePath, colorUsage);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
catch (e) {
|
|
174
|
+
logger.error({ file, error: e }, "Error parsing file");
|
|
175
|
+
parseErrors.push(`Can't parse file ${file}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const errorFile = `/tmp/zh-adoption-analyze-errors-${Date.now()}`;
|
|
179
|
+
if (parseErrors.length > 0) {
|
|
180
|
+
const file = fs.createWriteStream(errorFile);
|
|
181
|
+
parseErrors.forEach((err) => {
|
|
182
|
+
file.write(err + "\n");
|
|
183
|
+
});
|
|
184
|
+
file.end();
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
errorFile: parseErrors.length > 0 ? errorFile : null,
|
|
188
|
+
usage: usageMap,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
104
191
|
/**
|
|
105
192
|
* Parse the ignore array and format correctly
|
|
106
193
|
*/
|
|
@@ -2,6 +2,7 @@ import { Command } from "commander";
|
|
|
2
2
|
import { RenderOptions } from "ink";
|
|
3
3
|
interface TrackPackageOptions {
|
|
4
4
|
interactive: boolean;
|
|
5
|
+
packages?: string[];
|
|
5
6
|
}
|
|
6
7
|
export declare function trackPackageAction(options: TrackPackageOptions, renderOptions?: RenderOptions): Promise<void>;
|
|
7
8
|
export declare function trackPackageCommand(): Command;
|
|
@@ -5,12 +5,13 @@ 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
7
|
import { setStdErrStream } from "../common/logging.js";
|
|
8
|
+
import { parsePackageList } from "./track-package.utils.js";
|
|
8
9
|
export async function trackPackageAction(options, renderOptions) {
|
|
9
10
|
if (options.interactive) {
|
|
10
11
|
render(React.createElement(TrackPackage, null), renderOptions);
|
|
11
12
|
}
|
|
12
13
|
else {
|
|
13
|
-
render(React.createElement(NonInteractiveTrackPackage,
|
|
14
|
+
render(React.createElement(NonInteractiveTrackPackage, { allowedPackages: options.packages }));
|
|
14
15
|
}
|
|
15
16
|
}
|
|
16
17
|
export function trackPackageCommand() {
|
|
@@ -24,6 +25,7 @@ export function trackPackageCommand() {
|
|
|
24
25
|
.addOption(new Option("-in, --interactive [boolean]", "disable to skip manual input (useful when running in CI)")
|
|
25
26
|
.default(true)
|
|
26
27
|
.argParser((value) => yn(value)))
|
|
28
|
+
.addOption(new Option("-p, --packages [packageNames]", "specify the packages to search for, use multiple times to match more than one package").argParser(parsePackageList))
|
|
27
29
|
.action(async (options) => {
|
|
28
30
|
if (!options.interactive) {
|
|
29
31
|
setStdErrStream();
|
|
@@ -4,7 +4,14 @@ import { PackageFile } from "../common/types/package-file.js";
|
|
|
4
4
|
* @param subpath - provide to search inside subpath instead of working directory
|
|
5
5
|
*/
|
|
6
6
|
export declare function findPackageFiles(subpath?: string): Promise<string[]>;
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Get allowed package files from the directory and return
|
|
9
|
+
* their name/package/version/exports
|
|
10
|
+
*
|
|
11
|
+
* @param allowedPackages a list of package names to match on
|
|
12
|
+
* @returns a list of matched packages files and errors
|
|
13
|
+
*/
|
|
14
|
+
export declare function getPackageInfo(allowedPackages?: string[]): Promise<{
|
|
8
15
|
files: PackageFile[];
|
|
9
16
|
error: string | null;
|
|
10
17
|
}>;
|
|
@@ -12,3 +19,7 @@ export declare function getPackageInfo(): Promise<{
|
|
|
12
19
|
* Transform exports object into fully qualified aliases
|
|
13
20
|
*/
|
|
14
21
|
export declare function getAliasesFromExports(packageName: string, exports?: PackageFile["exports"]): string[];
|
|
22
|
+
/**
|
|
23
|
+
* Parse package list and format correctly
|
|
24
|
+
*/
|
|
25
|
+
export declare function parsePackageList(value: string, previous?: string[]): string[];
|
|
@@ -9,7 +9,14 @@ export async function findPackageFiles(subpath) {
|
|
|
9
9
|
const workingDir = joinPath(process.cwd(), subpath ?? "");
|
|
10
10
|
return findFiles(workingDir, "**/**/package.json", []);
|
|
11
11
|
}
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Get allowed package files from the directory and return
|
|
14
|
+
* their name/package/version/exports
|
|
15
|
+
*
|
|
16
|
+
* @param allowedPackages a list of package names to match on
|
|
17
|
+
* @returns a list of matched packages files and errors
|
|
18
|
+
*/
|
|
19
|
+
export async function getPackageInfo(allowedPackages) {
|
|
13
20
|
const base = process.cwd();
|
|
14
21
|
const files = await findPackageFiles();
|
|
15
22
|
if (files.length === 0) {
|
|
@@ -19,16 +26,26 @@ export async function getPackageInfo() {
|
|
|
19
26
|
};
|
|
20
27
|
}
|
|
21
28
|
try {
|
|
22
|
-
|
|
29
|
+
let packageFiles = await Promise.all(files.map(async (file) => {
|
|
23
30
|
const fileContents = await readFile(file, "utf-8");
|
|
24
31
|
const parsedPackage = JSON.parse(fileContents);
|
|
25
32
|
return {
|
|
26
33
|
name: parsedPackage.name,
|
|
27
|
-
path: `.${
|
|
28
|
-
|
|
34
|
+
path: `.${file.split(base).pop()}`,
|
|
35
|
+
// Default to 0.0.0 as we don't have a version so it might be a workspaces project/monorepo
|
|
36
|
+
version: parsedPackage.version ?? "0.0.0",
|
|
29
37
|
exports: parsedPackage.exports,
|
|
30
38
|
};
|
|
31
39
|
}));
|
|
40
|
+
if (allowedPackages?.length) {
|
|
41
|
+
packageFiles = packageFiles.filter((pack) => allowedPackages.includes(pack.name));
|
|
42
|
+
if (!packageFiles.length) {
|
|
43
|
+
return {
|
|
44
|
+
files: [],
|
|
45
|
+
error: "Can't find any matching packages",
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
32
49
|
return {
|
|
33
50
|
files: packageFiles,
|
|
34
51
|
error: null,
|
|
@@ -51,3 +68,15 @@ export function getAliasesFromExports(packageName, exports) {
|
|
|
51
68
|
.map((path) => joinPath(packageName, path))
|
|
52
69
|
.filter((name) => name !== packageName);
|
|
53
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Parse package list and format correctly
|
|
73
|
+
*/
|
|
74
|
+
export function parsePackageList(value, previous = []) {
|
|
75
|
+
return [
|
|
76
|
+
...previous,
|
|
77
|
+
...value
|
|
78
|
+
.split(",")
|
|
79
|
+
.map((entry) => entry.trim())
|
|
80
|
+
.filter(Boolean),
|
|
81
|
+
];
|
|
82
|
+
}
|
package/dist/common/api.d.ts
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import { ComponentProps } from "../ast/analyze.js";
|
|
2
|
-
import {
|
|
2
|
+
import { RawColorUsageMap, RawComponentUsageMap } from "../commands/analyze.js";
|
|
3
3
|
import { Credentials } from "./config.js";
|
|
4
|
+
export interface ComponentUsageRecord {
|
|
5
|
+
name: string;
|
|
6
|
+
files: string[];
|
|
7
|
+
count: number;
|
|
8
|
+
package: string;
|
|
9
|
+
props: ComponentProps;
|
|
10
|
+
}
|
|
11
|
+
export interface TokenLiteralUsageRecord {
|
|
12
|
+
value: string;
|
|
13
|
+
files: string[];
|
|
14
|
+
/** Only support color values right now */
|
|
15
|
+
type: "color";
|
|
16
|
+
count: number;
|
|
17
|
+
}
|
|
4
18
|
export declare enum ResponseStatus {
|
|
5
19
|
Success = "success",
|
|
6
20
|
Error = "error",
|
|
@@ -73,7 +87,11 @@ interface ComponentUsageDetailsSuccess {
|
|
|
73
87
|
repo_name: string;
|
|
74
88
|
};
|
|
75
89
|
}
|
|
76
|
-
export declare function
|
|
90
|
+
export declare function submitComponentUsageData(usage: RawComponentUsageMap, repoName: string, credentials: Credentials): Promise<APIResponse<ComponentUsageDetailsSuccess>>;
|
|
91
|
+
interface TokenLiteralUsageDetailsSuccess {
|
|
92
|
+
token_literal_usage: {};
|
|
93
|
+
}
|
|
94
|
+
export declare function submitTokenLiteralUsageData(usage: RawColorUsageMap, repoName: string, credentials: Credentials): Promise<APIResponse<TokenLiteralUsageDetailsSuccess>>;
|
|
77
95
|
interface RepoNamesSuccess {
|
|
78
96
|
repo_names: string[];
|
|
79
97
|
}
|
package/dist/common/api.js
CHANGED
|
@@ -31,12 +31,20 @@ export async function submitMonitoredRepoDetails(name, version, lockfilePath, pa
|
|
|
31
31
|
version,
|
|
32
32
|
}, credentials);
|
|
33
33
|
}
|
|
34
|
-
export async function
|
|
34
|
+
export async function submitComponentUsageData(usage, repoName, credentials) {
|
|
35
35
|
return post("/component_usages", {
|
|
36
36
|
repo_name: repoName,
|
|
37
37
|
usage: transformUsageByName(usage),
|
|
38
38
|
}, credentials);
|
|
39
39
|
}
|
|
40
|
+
export async function submitTokenLiteralUsageData(usage, repoName, credentials) {
|
|
41
|
+
return post("/token_literal_usages", {
|
|
42
|
+
token_literal_usage: {
|
|
43
|
+
repo_name: repoName,
|
|
44
|
+
usage: transformTokenLiteralUsageByName(usage),
|
|
45
|
+
},
|
|
46
|
+
}, credentials);
|
|
47
|
+
}
|
|
40
48
|
export async function getExistingRepoNames(credentials) {
|
|
41
49
|
return get("/component_usages/repo_names", credentials);
|
|
42
50
|
}
|
|
@@ -141,3 +149,21 @@ function transformUsageByName(usage) {
|
|
|
141
149
|
}
|
|
142
150
|
return Array.from(transformedUsageMap.values());
|
|
143
151
|
}
|
|
152
|
+
function transformTokenLiteralUsageByName(usage) {
|
|
153
|
+
const transformedUsageMap = new Map();
|
|
154
|
+
for (const [file, { hex, rgb, hsla }] of usage) {
|
|
155
|
+
// These values shouldn't collide as they're all different notations and types
|
|
156
|
+
const values = [...hex, ...rgb, ...hsla];
|
|
157
|
+
for (const colorValue of values) {
|
|
158
|
+
const currentValue = transformedUsageMap.get(colorValue);
|
|
159
|
+
const newFileList = [...(currentValue?.files ?? []), file];
|
|
160
|
+
transformedUsageMap.set(colorValue, {
|
|
161
|
+
value: colorValue,
|
|
162
|
+
type: "color",
|
|
163
|
+
files: newFileList,
|
|
164
|
+
count: (currentValue?.count ?? 0) + 1,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return Array.from(transformedUsageMap.values());
|
|
169
|
+
}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { RawColorUsageMap, RawComponentUsageMap } from "../../commands/analyze.js";
|
|
3
3
|
export interface AnalyzeProps {
|
|
4
4
|
onAnalyzeFiles: () => Promise<{
|
|
5
5
|
errorFile: string | null;
|
|
6
|
-
usage:
|
|
6
|
+
usage: RawComponentUsageMap;
|
|
7
|
+
}>;
|
|
8
|
+
onAnalyzeColorUsage: () => Promise<{
|
|
9
|
+
errorFile: string | null;
|
|
10
|
+
usage: RawColorUsageMap;
|
|
7
11
|
}>;
|
|
8
|
-
dryRun: boolean;
|
|
9
12
|
repoName?: string;
|
|
10
13
|
}
|
|
11
|
-
export default function Analyze({ onAnalyzeFiles,
|
|
14
|
+
export default function Analyze({ onAnalyzeFiles, onAnalyzeColorUsage, repoName, }: AnalyzeProps): React.JSX.Element;
|