@zeroheight/adoption-cli 0.2.12 → 0.3.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 +9 -0
- package/README.md +8 -0
- package/dist/ast/analyze.d.ts +6 -2
- package/dist/ast/analyze.js +84 -26
- package/dist/ast/parser.d.ts +3 -4
- package/dist/ast/parser.js +8 -15
- package/dist/cli.js +1 -1
- package/dist/commands/analyze.utils.d.ts +1 -1
- package/dist/commands/analyze.utils.js +3 -6
- package/package.json +2 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Release notes
|
|
2
2
|
|
|
3
|
+
## [0.3.0](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/0.3.0) - 29th July 2024
|
|
4
|
+
|
|
5
|
+
- Replace acorn parser with oxc-parser
|
|
6
|
+
|
|
7
|
+
## [0.2.13](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/0.2.13) - 25th July 2024
|
|
8
|
+
|
|
9
|
+
- Fix ws dependency security issue — https://github.com/advisories/GHSA-3h5v-q93c-6h6q
|
|
10
|
+
- Stop the flow breaking due to parsing errors
|
|
11
|
+
|
|
3
12
|
## [0.2.12](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/0.2.12) - 24th July 2024
|
|
4
13
|
- Update release notes
|
|
5
14
|
|
package/README.md
CHANGED
|
@@ -18,12 +18,20 @@ In the repository in which you wish to analyze the component usage, run the foll
|
|
|
18
18
|
zh-adoption analyze
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
You are able to limit the file extensions to include when searching for components by using `-e, --extensions [ext]` with a glob pattern to determine file extensions.
|
|
22
|
+
|
|
23
|
+
You are able to state files to ignore when searching for components by using `-i, --ignore [ext]` with a glob pattern to determine ignored files.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
21
27
|
To send adoption data to your [zeroheight](https://zeroheight.com/) account you will need to authenticate. This can be done as part of the `analyze` flow or separately by running:
|
|
22
28
|
|
|
23
29
|
```
|
|
24
30
|
zh-adoption auth
|
|
25
31
|
```
|
|
26
32
|
|
|
33
|
+
---
|
|
34
|
+
|
|
27
35
|
More info on the commands can be seen by running
|
|
28
36
|
|
|
29
37
|
```
|
package/dist/ast/analyze.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import type { Node } from "acorn";
|
|
1
|
+
import type { Node, Program, Identifier } from "acorn";
|
|
2
|
+
export interface PlainIdentifier extends Node {
|
|
3
|
+
type: "Identifier";
|
|
4
|
+
local: Identifier;
|
|
5
|
+
}
|
|
2
6
|
export type VisitorState = {
|
|
3
7
|
components: Map<string, number>;
|
|
4
8
|
imports: Map<string, {
|
|
@@ -11,4 +15,4 @@ export type RawUsage = {
|
|
|
11
15
|
count: number;
|
|
12
16
|
package: string;
|
|
13
17
|
};
|
|
14
|
-
export declare function analyze(ast:
|
|
18
|
+
export declare function analyze(ast: Program): RawUsage[];
|
package/dist/ast/analyze.js
CHANGED
|
@@ -23,27 +23,98 @@ export function analyze(ast) {
|
|
|
23
23
|
components: new Map(),
|
|
24
24
|
imports: new Map(),
|
|
25
25
|
};
|
|
26
|
-
const
|
|
27
|
-
|
|
26
|
+
const visitorFunctions = {
|
|
27
|
+
CatchClause() { },
|
|
28
|
+
CatchParameter() { },
|
|
29
|
+
ComputedMemberExpression() { },
|
|
30
|
+
JSXText() { },
|
|
31
|
+
NullLiteral() { },
|
|
32
|
+
NumericLiteral() { },
|
|
33
|
+
BooleanLiteral() { },
|
|
34
|
+
ObjectExpression() { },
|
|
35
|
+
ObjectProperty() { },
|
|
36
|
+
StringLiteral() { },
|
|
37
|
+
TSAsExpression() { },
|
|
38
|
+
TSEnumDeclaration() { },
|
|
39
|
+
TSInterfaceDeclaration() { },
|
|
40
|
+
TSTypeAliasDeclaration() { },
|
|
41
|
+
TSModuleDeclaration() { },
|
|
42
|
+
TSSatisfiesExpression() { },
|
|
43
|
+
StaticMemberExpression() { },
|
|
44
|
+
RegExpLiteral() { },
|
|
45
|
+
TSNonNullExpression() { },
|
|
46
|
+
JSXEmptyExpression() { },
|
|
47
|
+
JSXFragment(node, state) {
|
|
48
|
+
node.children.forEach((child) => walk.recursive(child, state, visitorFunctions, walk.base));
|
|
49
|
+
},
|
|
50
|
+
IfStatement(node, state) {
|
|
51
|
+
walk.recursive(node.consequent, state, visitorFunctions, walk.base);
|
|
52
|
+
if (node.alternate) {
|
|
53
|
+
walk.recursive(node.alternate, state, visitorFunctions, walk.base);
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
VariableDeclaration(node, state) {
|
|
57
|
+
node.declarations.forEach((child) => {
|
|
58
|
+
walk.recursive(child, state, visitorFunctions, walk.base);
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
ArrowFunctionExpression(node, state) {
|
|
62
|
+
if (node.body.type === "FunctionBody") {
|
|
63
|
+
walk.recursive(node.body, state, visitorFunctions, walk.base);
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
ParenthesizedExpression(node, state) {
|
|
67
|
+
walk.recursive(node.expression, state, visitorFunctions, walk.base);
|
|
68
|
+
},
|
|
69
|
+
ReturnStatement(node, state) {
|
|
70
|
+
if (node.argument) {
|
|
71
|
+
walk.recursive(node.argument, state, visitorFunctions, walk.base);
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
FunctionBody(node, state) {
|
|
75
|
+
node.statements.forEach((child) => walk.recursive(child, state, visitorFunctions, walk.base));
|
|
76
|
+
},
|
|
77
|
+
Program(node, state) {
|
|
78
|
+
node.body.forEach((child) => {
|
|
79
|
+
walk.recursive(child, state, visitorFunctions, walk.base);
|
|
80
|
+
});
|
|
81
|
+
},
|
|
82
|
+
JSXElement(node, state) {
|
|
28
83
|
const el = node.openingElement;
|
|
29
84
|
const elName = el.name.name;
|
|
30
|
-
if (!elName)
|
|
31
|
-
return;
|
|
32
85
|
// Ignore html tags e.g. div, h1, span
|
|
33
|
-
if (elName[0]
|
|
34
|
-
|
|
35
|
-
|
|
86
|
+
if (elName?.[0] !== elName?.[0]?.toLocaleLowerCase()) {
|
|
87
|
+
state.components.set(elName, (state.components.get(elName) ?? 0) + 1);
|
|
88
|
+
}
|
|
89
|
+
node.children.forEach((child) => {
|
|
90
|
+
walk.recursive(child, state, visitorFunctions, walk.base);
|
|
91
|
+
});
|
|
92
|
+
},
|
|
93
|
+
CallExpression(node, state) {
|
|
94
|
+
node.arguments.forEach((child) => walk.recursive(child, state, visitorFunctions, walk.base));
|
|
95
|
+
},
|
|
96
|
+
ExportDefaultDeclaration(node, state) {
|
|
97
|
+
if (node.declaration.type === "FunctionDeclaration") {
|
|
98
|
+
const { statements } = node.declaration.body;
|
|
99
|
+
statements.forEach((child) => {
|
|
100
|
+
walk.recursive(child, state, visitorFunctions, walk.base);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
JSXExpressionContainer(node, state) {
|
|
105
|
+
walk.recursive(node.expression, state, visitorFunctions, walk.base);
|
|
36
106
|
},
|
|
37
|
-
ImportDeclaration(node, state
|
|
38
|
-
const packageName = node.source.value;
|
|
39
|
-
node.specifiers
|
|
107
|
+
ImportDeclaration(node, state) {
|
|
108
|
+
const packageName = node.source.value?.toString() ?? "";
|
|
109
|
+
node.specifiers?.forEach((specifier) => {
|
|
40
110
|
// not handling namespace imports as we can't determine the actual name
|
|
41
111
|
// e.g. import * as MyIconLibrary from 'my-icon-library'
|
|
42
112
|
if (specifier.type === "ImportNamespaceSpecifier") {
|
|
43
113
|
return;
|
|
44
114
|
}
|
|
45
115
|
let name = specifier.local.name;
|
|
46
|
-
if (specifier.type === "ImportDefaultSpecifier"
|
|
116
|
+
if (specifier.type === "ImportDefaultSpecifier" ||
|
|
117
|
+
specifier.type === "Identifier") {
|
|
47
118
|
// handles the following cases:
|
|
48
119
|
// import Icon from 'my-icon-library'
|
|
49
120
|
name = specifier.local.name;
|
|
@@ -63,23 +134,10 @@ export function analyze(ast) {
|
|
|
63
134
|
});
|
|
64
135
|
});
|
|
65
136
|
},
|
|
66
|
-
|
|
67
|
-
const base = {
|
|
68
|
-
...walk.base,
|
|
69
|
-
TSInterfaceDeclaration() { },
|
|
70
|
-
TSModuleDeclaration() { },
|
|
71
|
-
TSAsExpression() { },
|
|
72
|
-
TSDeclareFunction() { },
|
|
73
|
-
TSTypeAliasDeclaration() { },
|
|
74
|
-
TSNonNullExpression() { },
|
|
75
|
-
TSEnumDeclaration() { },
|
|
76
|
-
TSParameterProperty() { },
|
|
77
|
-
TSImportEqualsDeclaration() { },
|
|
78
|
-
TSInstantiationExpression() { },
|
|
79
|
-
TSDeclareMethod() { },
|
|
137
|
+
// FIXME: Not the best typing but there are additional types not in acorn
|
|
80
138
|
};
|
|
81
139
|
try {
|
|
82
|
-
walk.
|
|
140
|
+
walk.recursive(ast, visitorState, visitorFunctions, walk.base);
|
|
83
141
|
}
|
|
84
142
|
catch (e) {
|
|
85
143
|
// We don't want to log the error to the users - maybe could add some actually logging in the future
|
package/dist/ast/parser.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import * as acorn from "acorn";
|
|
2
1
|
/**
|
|
3
2
|
* Parse code and return AST
|
|
4
3
|
*
|
|
5
4
|
* This extends the acorn parser with support for TypeScript and JSX
|
|
6
5
|
*
|
|
7
|
-
* @param code The string containing JS/TS
|
|
8
|
-
* @param
|
|
6
|
+
* @param code The string containing JS?X/TS?X
|
|
7
|
+
* @param sourceFilename a filename to help with inference
|
|
9
8
|
* @returns Parsed AST
|
|
10
9
|
*/
|
|
11
|
-
export declare function parse(code: string,
|
|
10
|
+
export declare function parse(code: string, sourceFilename?: string): any;
|
package/dist/ast/parser.js
CHANGED
|
@@ -1,23 +1,16 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { tsPlugin } from "acorn-typescript";
|
|
3
|
-
import * as walk from "acorn-walk";
|
|
4
|
-
import { extend } from "acorn-jsx-walk";
|
|
1
|
+
import oxc from "oxc-parser";
|
|
5
2
|
/**
|
|
6
3
|
* Parse code and return AST
|
|
7
4
|
*
|
|
8
5
|
* This extends the acorn parser with support for TypeScript and JSX
|
|
9
6
|
*
|
|
10
|
-
* @param code The string containing JS/TS
|
|
11
|
-
* @param
|
|
7
|
+
* @param code The string containing JS?X/TS?X
|
|
8
|
+
* @param sourceFilename a filename to help with inference
|
|
12
9
|
* @returns Parsed AST
|
|
13
10
|
*/
|
|
14
|
-
export function parse(code,
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return
|
|
19
|
-
ecmaVersion: version,
|
|
20
|
-
sourceType: "module",
|
|
21
|
-
locations: true,
|
|
22
|
-
});
|
|
11
|
+
export function parse(code, sourceFilename) {
|
|
12
|
+
const result = oxc.parseSync(code, { sourceFilename, sourceType: "module" });
|
|
13
|
+
// TODO: Surface these errors to help in debugging
|
|
14
|
+
// console.error(result.errors)
|
|
15
|
+
return JSON.parse(result.program);
|
|
23
16
|
}
|
package/dist/cli.js
CHANGED
|
@@ -10,7 +10,7 @@ const { output, cleanup } = render(React.createElement(HelpInfo, null));
|
|
|
10
10
|
program
|
|
11
11
|
.name("zh-adoption")
|
|
12
12
|
.description("CLI for measuring design system usage usage in your products")
|
|
13
|
-
.version("0.2.
|
|
13
|
+
.version("0.2.13")
|
|
14
14
|
.addHelpText("before", output)
|
|
15
15
|
.addCommand(analyzeCommand())
|
|
16
16
|
.addCommand(authCommand());
|
|
@@ -15,6 +15,6 @@ export interface ComponentUsageRecord {
|
|
|
15
15
|
*/
|
|
16
16
|
export declare function findFiles(base: string, extensions: string, ignorePattern: string): Promise<string[]>;
|
|
17
17
|
export declare function analyzeFiles(extensions: string, ignorePattern: string): Promise<{
|
|
18
|
-
errors:
|
|
18
|
+
errors: never[];
|
|
19
19
|
usage: RawUsageMap;
|
|
20
20
|
}>;
|
|
@@ -51,21 +51,18 @@ export async function analyzeFiles(extensions, ignorePattern) {
|
|
|
51
51
|
if (files.length === 0) {
|
|
52
52
|
throw new Error("Can't find any relevant files");
|
|
53
53
|
}
|
|
54
|
-
const parseErrors = [];
|
|
55
54
|
const usageMap = new Map();
|
|
56
55
|
for (const file of files) {
|
|
57
56
|
try {
|
|
58
57
|
const fileContents = await readFile(file, "utf-8");
|
|
59
|
-
const ast = parse(fileContents);
|
|
58
|
+
const ast = parse(fileContents, file);
|
|
60
59
|
const usage = analyze(ast);
|
|
61
60
|
if (usage.length > 0) {
|
|
62
61
|
const relativePath = file.slice(process.cwd().length);
|
|
63
62
|
usageMap.set(relativePath, usage);
|
|
64
63
|
}
|
|
65
64
|
}
|
|
66
|
-
catch
|
|
67
|
-
parseErrors.push(`Can't parse file ${file}`);
|
|
68
|
-
}
|
|
65
|
+
catch { }
|
|
69
66
|
}
|
|
70
|
-
return { errors:
|
|
67
|
+
return { errors: [], usage: usageMap };
|
|
71
68
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeroheight/adoption-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"license": "ISC",
|
|
5
5
|
"main": "dist/cli.js",
|
|
6
6
|
"bin": {
|
|
@@ -26,8 +26,6 @@
|
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"acorn": "^8.11.3",
|
|
29
|
-
"acorn-jsx-walk": "^2.0.0",
|
|
30
|
-
"acorn-typescript": "^1.4.13",
|
|
31
29
|
"acorn-walk": "^8.3.2",
|
|
32
30
|
"chalk": "^5.2.0",
|
|
33
31
|
"commander": "^12.0.0",
|
|
@@ -39,6 +37,7 @@
|
|
|
39
37
|
"ink-select-input": "^5.0.0",
|
|
40
38
|
"ink-spinner": "^5.0.0",
|
|
41
39
|
"ink-text-input": "^5.0.1",
|
|
40
|
+
"oxc-parser": "^0.22.0",
|
|
42
41
|
"react": "^18.2.0",
|
|
43
42
|
"yn": "^5.0.0"
|
|
44
43
|
},
|