-
Notifications
You must be signed in to change notification settings - Fork 34
/
component.ts
65 lines (54 loc) · 1.96 KB
/
component.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import path from 'path';
import babel from '@babel/core';
import {NodePath, Visitor} from '@babel/traverse';
type FunctionDeclaration = babel.types.FunctionDeclaration;
const KEY_REACT_FUNCTIONS = new Set(['createElement', 'cloneElement']);
const resolveCalleeName = (path: NodePath<babel.types.CallExpression>) => {
const callee = path.get('callee');
if (callee.isIdentifier()) {
return callee.node.name;
}
if (callee.isMemberExpression()) {
const property = callee.get('property');
return property.isIdentifier() ? property.node.name : '';
}
return '';
};
// 函数体有以下东西就认定它是个组件:
//
// 1. 有`cloneElement`或`createElement`的调用
// 2. 有任何的JSX语法
const isFunctionBodyComponentLike = (path: NodePath<FunctionDeclaration>): boolean => {
let matched = false;
const visitor: Visitor = {
JSXElement(path) {
matched = true;
path.stop();
},
CallExpression(path) {
const calleeName = resolveCalleeName(path);
if (KEY_REACT_FUNCTIONS.has(calleeName)) {
matched = true;
path.stop();
}
},
};
path.traverse(visitor);
return matched;
};
export const isComponentDeclaration = (path: NodePath<FunctionDeclaration>, strict?: boolean) => {
const functionName = path.node.id?.name ?? '';
const args = path.node.params;
return args.length <= 1 && /^[A-Z]/.test(functionName) && (!strict || isFunctionBodyComponentLike(path));
};
export const resolveComponentName = (declaration: NodePath<FunctionDeclaration>, filename: string | undefined) => {
const functionName = declaration.node.id?.name;
if (functionName) {
return functionName;
}
if (!filename) {
return 'Unknown';
}
const file = path.basename(filename, path.extname(filename));
return file === 'index' ? path.dirname(filename).split(path.sep).pop() : file;
};