-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathextract-types.cjs
132 lines (110 loc) · 3.72 KB
/
extract-types.cjs
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
131
132
const ts = require('typescript');
const fs = require('fs');
const path = require('path');
function extractTypesFromModule(filePath) {
const program = ts.createProgram([filePath], {});
const checker = program.getTypeChecker();
const sourceFile = program.getSourceFile(filePath);
const seenTypes = new Set();
const typeInfo = [];
function visit(node) {
if (ts.isModuleDeclaration(node) && node.name.text === 'vue-data-ui') {
ts.forEachChild(node.body, visit);
}
if (ts.isInterfaceDeclaration(node)) {
const interfaceName = node.name.text;
const members = extractInterfaceMembers(node);
typeInfo.push({
kind: 'interface',
name: interfaceName,
members: members,
});
} else if (ts.isTypeAliasDeclaration(node)) {
const typeName = node.name.text;
const type = checker.getTypeAtLocation(node);
const typeString = checker.typeToString(type);
typeInfo.push({
kind: 'type',
name: typeName,
type: typeString,
details: extractTypeDetails(type),
});
} else if (ts.isEnumDeclaration(node)) {
const enumName = node.name.text;
const members = node.members.map(member => member.name.getText());
typeInfo.push({
kind: 'enum',
name: enumName,
members: members,
});
}
}
function extractInterfaceMembers(node) {
const members = [];
node.members.forEach(member => {
const memberName = member.name.getText();
const memberType = checker.getTypeAtLocation(member);
const memberTypeString = checker.typeToString(memberType);
const details = extractTypeDetails(memberType);
members.push({
name: memberName,
type: memberTypeString,
optional: member.questionToken ? true : false,
isReadonly: member.modifiers?.some(mod => mod.kind === ts.SyntaxKind.ReadonlyKeyword) || false,
details: details,
});
});
return members;
}
function extractTypeDetails(type) {
const typeId = checker.typeToString(type);
if (seenTypes.has(typeId)) {
return { name: typeId, circular: true };
}
seenTypes.add(typeId);
if (type.flags & ts.TypeFlags.Union) {
return {
kind: 'union',
types: type.types.map(t => checker.typeToString(t)),
};
} else if (type.flags & ts.TypeFlags.Intersection) {
return {
kind: 'intersection',
types: type.types.map(t => checker.typeToString(t)),
};
} else if (type.flags & ts.TypeFlags.Object) {
return extractObjectTypeDetails(type);
}
return { name: checker.typeToString(type) };
}
function extractObjectTypeDetails(type) {
const properties = type.getProperties();
const details = [];
properties.forEach(prop => {
const propType = checker.getTypeOfSymbolAtLocation(prop, prop.valueDeclaration);
const propTypeString = checker.typeToString(propType);
const optional = prop.flags & ts.SymbolFlags.Optional;
details.push({
name: prop.getName(),
type: propTypeString,
optional: optional,
details: extractTypeDetails(propType),
});
});
return { kind: 'object', properties: details };
}
ts.forEachChild(sourceFile, visit);
return typeInfo;
}
const filePath = path.resolve(__dirname, 'node_modules/vue-data-ui/dist/types/vue-data-ui.d.ts');
if (!fs.existsSync(filePath)) {
console.error('File does not exist:', filePath);
process.exit(1);
}
const types = extractTypesFromModule(filePath);
if (types.length === 0) {
console.warn('No types found in the .d.ts file');
} else {
fs.writeFileSync('types.json', JSON.stringify(types, null, 2));
console.log('Type information extracted and written to types.json');
}