@zeroheight/adoption-cli 2.3.0 → 2.4.2

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # Release notes
2
2
 
3
+ ## [2.4.2](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/2.4.2) - 17th December 2024
4
+
5
+ - Fix issue with parsing pnpm lockfiles using version `6.0`
6
+
7
+ ## [2.4.1](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/2.4.1) - 11th December 2024
8
+
9
+ - The monitor-repo command now supports custom package naming for projects.
10
+
11
+ ## [2.4.0](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/2.4.0) - 10th December 2024
12
+
13
+ - `analyze --ignore` option can be used more than once to allow for more glob patterns, this fixes an issue in **2.3.0** where glob patterns containing commas would not work as expected
14
+
3
15
  ## [2.3.0](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/2.3.0) - 5th December 2024
4
16
 
5
17
  - The `analyze` command now ignores `*.d.ts` files by default, alongside `*.test.*` and `*.spec.*` files.
package/README.md CHANGED
@@ -78,6 +78,18 @@ Provide a glob pattern to ignore files, directories or file extensions when sear
78
78
  zh-adoption analyze -i "**/*.{test,spec}.*"
79
79
  ```
80
80
 
81
+ To pass multiple patterns, use the option multiple times e.g.
82
+
83
+ ```bash
84
+ zh-adoption analyze -i "**/*.{test,spec}.*" -i "**/*.d.ts"
85
+ ```
86
+
87
+ To ignore nothing, even the default patterns (`['**/*.{test,spec}.*', '**/*.d.ts']`), pass an empty value
88
+
89
+ ```bash
90
+ zh-adoption analyze -i
91
+ ```
92
+
81
93
  `-r` / `--repo-name`
82
94
 
83
95
  Provide a name for the current repository. This must be passed when `-in` / `--interactive` is set to `false`.
package/dist/cli.js CHANGED
@@ -13,7 +13,7 @@ const { output, cleanup } = render(React.createElement(HelpInfo, null));
13
13
  program
14
14
  .name("zh-adoption")
15
15
  .description("CLI for measuring design system usage usage in your products")
16
- .version("2.3.0")
16
+ .version("2.4.2")
17
17
  .addHelpText("before", output)
18
18
  .option("--log-file <path>", "Path to write logs to, if not provided logs only error logs will be written to stderr")
19
19
  .addOption(new Option("--log-level <level>", "The lowest level of logs to display")
@@ -3,7 +3,7 @@ import { Command } from "commander";
3
3
  import { RawUsage } from "../ast/analyze.js";
4
4
  interface AnalyzeOptions {
5
5
  extensions: string;
6
- ignore: string;
6
+ ignore: string[];
7
7
  dryRun: boolean;
8
8
  repoName?: string;
9
9
  interactive: boolean;
@@ -1,8 +1,8 @@
1
- import React from "react";
1
+ 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 } from "./analyze.utils.js";
5
+ import { analyzeFiles, 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";
@@ -23,7 +23,9 @@ export function analyzeCommand() {
23
23
  showGlobalOptions: true,
24
24
  })
25
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"))
26
- .addOption(new Option("-i, --ignore [ext]", "files to ignore when searching for components").default("**/*.{test,spec}.*,**/*.d.ts", "glob pattern to determine ignored files"))
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
+ .default(["**/*.{test,spec}.*", "**/*.d.ts"], "*.test.*, *.spec.* and *.d.ts files")
28
+ .argParser(parseGlobList))
27
29
  .addOption(new Option("-d, --dry-run", "don't push results to zeroheight").default(false))
28
30
  .addOption(new Option("-r, --repo-name <string>", "name of the repository"))
29
31
  .addOption(new Option("-in, --interactive [boolean]", "disable to skip input (useful when running in CI)")
@@ -33,8 +35,10 @@ export function analyzeCommand() {
33
35
  if (!options.interactive) {
34
36
  setStdErrStream();
35
37
  }
38
+ // If no option is passed in aka `true`, use empty list of ignore patterns
39
+ const ignorePattern = typeof options.ignore === "boolean" ? [] : options.ignore;
36
40
  try {
37
- await analyzeAction(options);
41
+ await analyzeAction({ ...options, ignore: ignorePattern });
38
42
  }
39
43
  catch (e) {
40
44
  logger.error({ error: e }, "Unhandled exception running analyze command");
@@ -1,3 +1,4 @@
1
+ import type { Option } from "commander";
1
2
  import { RawUsageMap } from "./analyze.js";
2
3
  import { ComponentProps } from "../ast/analyze.js";
3
4
  export interface ComponentUsageRecord {
@@ -15,9 +16,13 @@ export interface ComponentUsageRecord {
15
16
  * @param gitIgnore contents of .gitignore file
16
17
  * @returns list of file paths
17
18
  */
18
- export declare function findFiles(base: string, extensions: string, ignorePattern: string | boolean): Promise<string[]>;
19
- export declare function analyzeFiles(extensions: string, ignorePattern: string): Promise<{
19
+ export declare function findFiles(base: string, extensions: string, ignorePattern: string[]): Promise<string[]>;
20
+ export declare function analyzeFiles(extensions: string, ignorePattern: string[]): Promise<{
20
21
  errorFile: string | null;
21
22
  usage: RawUsageMap;
22
23
  }>;
23
24
  export declare function calculateNumberOfComponents(usageResult: RawUsageMap): number;
25
+ /**
26
+ * Parse the ignore array and format correctly
27
+ */
28
+ export declare function parseGlobList(this: Option, value: string, previous: unknown): any[];
@@ -16,8 +16,6 @@ import logger from "../common/logging.js";
16
16
  */
17
17
  export async function findFiles(base, extensions, ignorePattern) {
18
18
  const gitIgnore = await getGitIgnore(base);
19
- // Split by comma if string exists otherwise it could be a boolean flag
20
- const ignorePatternArray = typeof ignorePattern === "string" ? ignorePattern.split(",") : [];
21
19
  // typescript complains about the ignore() function having
22
20
  // no call signature. Could not find a way to fix this.
23
21
  // @ts-ignore
@@ -32,7 +30,7 @@ export async function findFiles(base, extensions, ignorePattern) {
32
30
  const matchingFiles = [];
33
31
  const g = new Glob(extensions, {
34
32
  cwd: base,
35
- ignore: ignorePatternArray,
33
+ ignore: ignorePattern,
36
34
  fs,
37
35
  });
38
36
  for await (const file of g) {
@@ -103,3 +101,17 @@ export function calculateNumberOfComponents(usageResult) {
103
101
  }
104
102
  return 0;
105
103
  }
104
+ /**
105
+ * Parse the ignore array and format correctly
106
+ */
107
+ export function parseGlobList(value, previous) {
108
+ // Ignore default values if option given instead
109
+ if (previous == this.defaultValue) {
110
+ return [value];
111
+ }
112
+ // Concat to array if array already exists
113
+ if (Array.isArray(previous)) {
114
+ return previous.concat(value);
115
+ }
116
+ return [value];
117
+ }
@@ -3,6 +3,7 @@ import { RenderOptions } from "ink";
3
3
  interface MonitorRepoOptions {
4
4
  interactive: boolean;
5
5
  dir?: string[];
6
+ packageName?: string;
6
7
  }
7
8
  export declare function monitorRepoAction(options: MonitorRepoOptions, _renderOptions?: RenderOptions): Promise<void>;
8
9
  export declare function monitorRepoCommand(): Command;
@@ -8,7 +8,7 @@ export async function monitorRepoAction(options, _renderOptions) {
8
8
  // if (options.interactive) {
9
9
  // render(<MonitorRepo />, renderOptions);
10
10
  // } else {
11
- render(React.createElement(NonInteractiveMonitorRepo, { packageDirs: options.dir }));
11
+ render(React.createElement(NonInteractiveMonitorRepo, { packageDirs: options.dir, packageName: options.packageName }));
12
12
  // }
13
13
  }
14
14
  export function monitorRepoCommand() {
@@ -28,6 +28,7 @@ export function monitorRepoCommand() {
28
28
  // .argParser((value) => yn(value))
29
29
  // )
30
30
  .addOption(new Option("-d, --dir <path...>", "use package directory to find package.json and lockfile"))
31
+ .addOption(new Option("-p, --package-name <name>", "specify the name used to identify the package in zeroheight"))
31
32
  .action(async (options) => {
32
33
  setStdErrStream();
33
34
  try {
@@ -40,7 +40,7 @@ export async function parseLockfile(lockfilePath) {
40
40
  */
41
41
  export async function findLockfiles(subpath) {
42
42
  const workingDir = joinPath(process.cwd(), subpath ?? "");
43
- return findFiles(workingDir, "**/**/{package-lock.json,shrinkwrap.yaml,pnpm-lock.yaml,yarn.lock}", "");
43
+ return findFiles(workingDir, "**/**/{package-lock.json,shrinkwrap.yaml,pnpm-lock.yaml,yarn.lock}", []);
44
44
  }
45
45
  /**
46
46
  * Get the lockfile parser so name, version and packages can be extracted from the lockfile
@@ -7,7 +7,7 @@ import { findFiles } from "./analyze.utils.js";
7
7
  */
8
8
  export async function findPackageFiles(subpath) {
9
9
  const workingDir = joinPath(process.cwd(), subpath ?? "");
10
- return findFiles(workingDir, "**/**/package.json", "");
10
+ return findFiles(workingDir, "**/**/package.json", []);
11
11
  }
12
12
  export async function getPackageInfo() {
13
13
  const base = process.cwd();
@@ -1,6 +1,7 @@
1
1
  import React from "react";
2
2
  interface NonInteractiveMonitorRepoProps {
3
3
  packageDirs?: string[];
4
+ packageName?: string;
4
5
  }
5
- export default function NonInteractiveMonitorRepo({ packageDirs, }: NonInteractiveMonitorRepoProps): React.JSX.Element;
6
+ export default function NonInteractiveMonitorRepo({ packageDirs, packageName, }: NonInteractiveMonitorRepoProps): React.JSX.Element;
6
7
  export {};
@@ -1,7 +1,7 @@
1
1
  import React from "react";
2
2
  import { Box, Text } from "ink";
3
3
  import Spinner from "ink-spinner";
4
- import { join as joinPath } from "path";
4
+ import { basename, join as joinPath } from "path";
5
5
  import { findLockfiles, getPackageMeta, groupByBasename, parseLockfile, } from "../../commands/monitor-repo.utils.js";
6
6
  import { findPackageFiles } from "../../commands/track-package.utils.js";
7
7
  import { ResponseStatus, submitMonitoredRepoDetails, } from "../../common/api.js";
@@ -15,7 +15,7 @@ var Step;
15
15
  Step[Step["FINDING_DETAILS"] = 2] = "FINDING_DETAILS";
16
16
  Step[Step["SENDING_DATA"] = 3] = "SENDING_DATA";
17
17
  })(Step || (Step = {}));
18
- export default function NonInteractiveMonitorRepo({ packageDirs, }) {
18
+ export default function NonInteractiveMonitorRepo({ packageDirs, packageName, }) {
19
19
  const [errorMessage, setErrorMessage] = React.useState(); // Global error message
20
20
  const [repoStatus, setRepoStatus] = React.useState({});
21
21
  function updateRepoStatus(name, step, error) {
@@ -51,6 +51,13 @@ export default function NonInteractiveMonitorRepo({ packageDirs, }) {
51
51
  let packageMeta;
52
52
  try {
53
53
  packageMeta = await getPackageMeta(joinPath(directory, "package.json"));
54
+ if (packageName) {
55
+ packageMeta.name = packageName;
56
+ }
57
+ if (!packageMeta.name) {
58
+ setErrorMessage(`Name field is missing in your ${basename(directory)}/package.json file. Re-run the command with --package-name <name> to continue.`);
59
+ continue;
60
+ }
54
61
  }
55
62
  catch (e) {
56
63
  // package.json doesn't exist or is invalid, skip pair
@@ -37,7 +37,13 @@ _PNPMLockParser_instances = new WeakSet(), _PNPMLockParser_parseV5 = function _P
37
37
  }, _PNPMLockParser_parseV6 = function _PNPMLockParser_parseV6(data) {
38
38
  return Object.keys(data.packages)
39
39
  .map((packagePair) => {
40
- const [name, version] = packagePair.split("@");
40
+ const lastIndex = packagePair.lastIndexOf("@");
41
+ let name = packagePair.slice(0, lastIndex);
42
+ const version = packagePair.slice(lastIndex + 1);
43
+ // v6 lockfiles have a leading slash
44
+ if (name.startsWith("/")) {
45
+ name = name.slice(1);
46
+ }
41
47
  return { name, version };
42
48
  })
43
49
  .filter((p) => p.name && p.version);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeroheight/adoption-cli",
3
- "version": "2.3.0",
3
+ "version": "2.4.2",
4
4
  "license": "ISC",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {