-
Notifications
You must be signed in to change notification settings - Fork 6
/
index.js
130 lines (107 loc) · 2.91 KB
/
index.js
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*::
import type {BabelPath as Path} from 'babel-flow-types';
type Binding = {
kind: 'import' | 'declaration' | 'expression' | 'param',
path: Path,
id: Path,
};
type Bindings = {
[name: string]: Binding,
};
type Visitor = {
[method: string]: (path: Path, state: { bindings: Bindings }) => void;
};
*/
let getId = (kind, path, bindings) => {
if (path.node.id) {
let id = path.get('id');
bindings[id.node.name] = {kind, path: id};
}
};
let visitor /*: Visitor */ = {
Scope(path) {
path.skip();
},
Declaration(path, state) {
if (
isTypeDeclaration(path)
) {
getId('declaration', path, state.bindings);
}
if (!path.isImportDeclaration() && !path.isExportDeclaration()) {
path.skip();
}
},
TypeParameter(path, state) {
state.bindings[path.node.name] = {kind: 'param', path};
},
'ImportSpecifier|ImportDefaultSpecifier'(path, state) {
let importKind = path.node.importKind || path.parent.importKind;
if (importKind !== 'type' && importKind !== 'typeof') return;
let local = path.get('local');
state.bindings[local.node.name] = {kind: 'import', path: local};
},
};
function isTypeImport(path) {
if (!path.isImportSpecifier() && !path.isImportDefaultSpecifier()) {
return false;
}
let importKind = path.node.importKind || path.parent.importKind;
return importKind === 'type' || importKind === 'typeof';
}
function isTypeDeclaration(path) {
return (
path.isTypeAlias() ||
path.isClassDeclaration() ||
path.isInterfaceDeclaration() ||
path.type === 'TSTypeAliasDeclaration' ||
path.type === 'TSInterfaceDeclaration' ||
path.type === 'TSEnumDeclaration' ||
path.type === 'TSModuleDeclaration'
);
}
function isTypeExpression(path) {
return path.isClassExpression();
}
function isTypeParam(path) {
return path.isTypeParameter();
}
function isTypeScope(path /*: Path */) {
return (
path.isScope() ||
path.isFunctionTypeAnnotation() ||
path.isTypeAlias() ||
path.isInterfaceDeclaration() ||
isTypeDeclaration(path)
);
}
function getOwnTypeBindings(path /*: Path */) {
if (!isTypeScope(path)) {
throw new Error('Must pass valid type scope path using getClosestTypeScope()');
}
let bindings = {};
if (isTypeExpression(path) && path.node.id) {
getId('expression', path, bindings);
} else {
path.traverse(visitor, { bindings });
}
return bindings;
}
function getTypeBinding(path /*: Path */, name /*: string */) /*: Binding */ {
let searching = path;
do {
searching = getClosestTypeScope(searching);
let bindings = getOwnTypeBindings(searching);
if (bindings[name]) return bindings[name];
} while (searching = searching.parentPath);
return null;
}
function getClosestTypeScope(path /*: Path */) /*: Path */ {
return path.find(p => isTypeScope(p));
}
module.exports = {
isTypeScope,
getClosestTypeScope,
getOwnTypeBindings,
getTypeBinding,
};