okai 0.0.24 → 0.0.25
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/dist/api.d.ts +12 -1
- package/dist/cs-apis.js +5 -0
- package/dist/cs-ast.js +326 -283
- package/dist/cs-gen.js +15 -9
- package/dist/cs-migrations.js +14 -5
- package/dist/index.js +80 -38
- package/dist/info.js +76 -0
- package/dist/ts-ast.js +32 -1
- package/dist/ts-once.js +240 -0
- package/dist/ts-parser.js +36 -27
- package/dist/tsd-gen.js +18 -81
- package/dist/utils.js +26 -1
- package/package.json +1 -1
package/dist/ts-once.js
ADDED
@@ -0,0 +1,240 @@
|
|
1
|
+
import { pick, toCamelCase, toPascalCase } from "./utils.js";
|
2
|
+
// Tranforms that are only applied once on AI TypeScript AST
|
3
|
+
export function createTdAstFromAIAst(tsAst) {
|
4
|
+
mergeInterfacesAndClasses(tsAst);
|
5
|
+
rewriteToPascalCase(tsAst);
|
6
|
+
replaceReferences(tsAst);
|
7
|
+
replaceIds(tsAst);
|
8
|
+
tsAst.classes.forEach(cls => {
|
9
|
+
// replaceUserRefs(cls)
|
10
|
+
rewriteDuplicateTypePropNames(cls);
|
11
|
+
rewriteSelfReferencingIds(cls);
|
12
|
+
});
|
13
|
+
changeUserRefsToAuditBase(tsAst);
|
14
|
+
return tsAst;
|
15
|
+
}
|
16
|
+
/*
|
17
|
+
function replaceUserRefs(type:ParsedClass) {
|
18
|
+
type.name = toPascalCase(type.name)
|
19
|
+
type.properties?.forEach(prop => {
|
20
|
+
prop.name = toCamelCase(prop.name)
|
21
|
+
if (prop.type === 'User' || prop.name === 'userId') {
|
22
|
+
type.extends = 'AuditBase'
|
23
|
+
}
|
24
|
+
|
25
|
+
if (prop.type.startsWith('Array<')) {
|
26
|
+
const elType = prop.type.slice('Array<'.length, -1)
|
27
|
+
prop.type = elType + '[]'
|
28
|
+
}
|
29
|
+
if (prop.type === 'User') {
|
30
|
+
prop.name = 'userId'
|
31
|
+
prop.type = 'string'
|
32
|
+
}
|
33
|
+
if (prop.type === 'User[]') {
|
34
|
+
if (prop.name.endsWith('s')) {
|
35
|
+
prop.name = prop.name.slice(0, -1) + 'Ids'
|
36
|
+
}
|
37
|
+
prop.type = 'string[]'
|
38
|
+
}
|
39
|
+
})
|
40
|
+
}
|
41
|
+
*/
|
42
|
+
// Replace User Tables and FKs with AuditBase tables and
|
43
|
+
export function transformUserRefs(tsAst, info) {
|
44
|
+
const addClasses = [];
|
45
|
+
for (const cls of tsAst.classes) {
|
46
|
+
const removeProps = [];
|
47
|
+
for (const prop of cls.properties) {
|
48
|
+
const propLower = prop.name.toLowerCase();
|
49
|
+
if (propLower === 'userid') {
|
50
|
+
removeProps.push(prop.name);
|
51
|
+
}
|
52
|
+
if (prop.type === 'User') {
|
53
|
+
cls.extends = 'AuditBase';
|
54
|
+
// Replace with local User Type
|
55
|
+
/*
|
56
|
+
[Reference(SelfId = nameof(CreatedBy), RefId = nameof(User.UserName), RefLabel = nameof(User.DisplayName))]
|
57
|
+
public User UserRef { get; set; }
|
58
|
+
*/
|
59
|
+
if (info.userType)
|
60
|
+
prop.type = info.userType;
|
61
|
+
if (!prop.annotations)
|
62
|
+
prop.annotations = [];
|
63
|
+
const attr = {
|
64
|
+
name: 'reference',
|
65
|
+
args: {
|
66
|
+
selfId: 'createdBy',
|
67
|
+
refId: 'userName',
|
68
|
+
}
|
69
|
+
};
|
70
|
+
if (info.userLabel)
|
71
|
+
attr.args.refLabel = info.userLabel;
|
72
|
+
prop.annotations.push(attr);
|
73
|
+
}
|
74
|
+
if (prop.type === 'User[]') {
|
75
|
+
if (info.userType) {
|
76
|
+
prop.type = info.userType + '[]';
|
77
|
+
if (!prop.annotations)
|
78
|
+
prop.annotations = [];
|
79
|
+
prop.annotations.push({ name: 'reference' });
|
80
|
+
const idPropType = cls.properties.find(x => x.name.toLowerCase() === 'id')?.type ?? 'number';
|
81
|
+
// Add Many to Many User Table
|
82
|
+
addClasses.push({
|
83
|
+
name: cls.name + 'User',
|
84
|
+
properties: [
|
85
|
+
{ name: 'id', type: 'number' },
|
86
|
+
{ name: toCamelCase(cls.name) + 'Id', type: idPropType },
|
87
|
+
{ name: toCamelCase(info.userType) + 'Id', type: info.userIdType ?? 'string' },
|
88
|
+
]
|
89
|
+
});
|
90
|
+
}
|
91
|
+
}
|
92
|
+
}
|
93
|
+
if (removeProps.length) {
|
94
|
+
cls.extends = 'AuditBase';
|
95
|
+
cls.properties = cls.properties.filter(x => !removeProps.includes(x.name));
|
96
|
+
}
|
97
|
+
}
|
98
|
+
if (addClasses.length) {
|
99
|
+
tsAst.classes.push(...addClasses);
|
100
|
+
}
|
101
|
+
// Remove User Table if local User Table exists
|
102
|
+
if (info.userType) {
|
103
|
+
tsAst.classes = tsAst.classes.filter(x => x.name !== 'User');
|
104
|
+
}
|
105
|
+
}
|
106
|
+
function rewriteDuplicateTypePropNames(type) {
|
107
|
+
const duplicateTypePropMap = {
|
108
|
+
note: 'content',
|
109
|
+
};
|
110
|
+
const duplicateProp = type.properties?.find(x => toPascalCase(x.name) === type.name);
|
111
|
+
if (duplicateProp) {
|
112
|
+
const newName = duplicateTypePropMap[duplicateProp.name] ?? 'value';
|
113
|
+
duplicateProp.name = newName;
|
114
|
+
}
|
115
|
+
}
|
116
|
+
function rewriteSelfReferencingIds(type) {
|
117
|
+
const selfRefId = type.properties?.find(x => x.name.toLowerCase() === `${type.name}id`.toLowerCase());
|
118
|
+
if (selfRefId) {
|
119
|
+
selfRefId.name = `parentId`;
|
120
|
+
}
|
121
|
+
}
|
122
|
+
function rewriteToPascalCase(ast) {
|
123
|
+
ast?.classes.forEach(t => {
|
124
|
+
t.name = toPascalCase(t.name);
|
125
|
+
t.properties?.forEach(p => p.name = toCamelCase(p.name));
|
126
|
+
});
|
127
|
+
ast.enums?.forEach(e => {
|
128
|
+
e.name = toPascalCase(e.name);
|
129
|
+
e.members?.forEach(m => {
|
130
|
+
m.name = toPascalCase(m.name);
|
131
|
+
});
|
132
|
+
});
|
133
|
+
}
|
134
|
+
function changeUserRefsToAuditBase(ast) {
|
135
|
+
const user = ast.classes?.find(x => x.name === 'User') ?? ast.interfaces?.find(x => x.name === 'User');
|
136
|
+
if (user) {
|
137
|
+
user.properties?.forEach(prop => {
|
138
|
+
// If User Model references any other model, chnage it to extend AuditBase
|
139
|
+
const ref = ast.classes?.find(x => x.name === prop.type)
|
140
|
+
?? ast.interfaces?.find(x => x.name === prop.type);
|
141
|
+
if (ref) {
|
142
|
+
ref.extends = 'AuditBase';
|
143
|
+
}
|
144
|
+
// If User Model references any other model[], chnage it to extend AuditBase
|
145
|
+
const refs = ast.classes?.find(x => prop.type.endsWith('[]') && x.name === prop.type.substring(0, prop.type.length - 2))
|
146
|
+
?? ast.interfaces?.find(x => prop.type.endsWith('[]') && x.name === prop.type.substring(0, prop.type.length - 2));
|
147
|
+
if (refs) {
|
148
|
+
refs.extends = 'AuditBase';
|
149
|
+
}
|
150
|
+
});
|
151
|
+
}
|
152
|
+
}
|
153
|
+
function mergeInterfacesAndClasses(ast) {
|
154
|
+
const classes = [];
|
155
|
+
for (const iface of ast.interfaces) {
|
156
|
+
const cls = interfaceToClass(iface);
|
157
|
+
classes.push(cls);
|
158
|
+
}
|
159
|
+
for (const cls of ast.classes) {
|
160
|
+
classes.push(cls);
|
161
|
+
}
|
162
|
+
ast.classes = classes;
|
163
|
+
ast.interfaces = [];
|
164
|
+
}
|
165
|
+
function interfaceToClass(ast) {
|
166
|
+
return pick(ast, ['name', 'extends', 'comment', 'properties', 'annotations']);
|
167
|
+
}
|
168
|
+
export function replaceReferences(tsAst) {
|
169
|
+
const references = [
|
170
|
+
'Service',
|
171
|
+
];
|
172
|
+
// The most important types are the ones with the most references
|
173
|
+
const refCount = (t) => t.properties?.filter(p => tsAst.classes.find(x => x.name === p.type)).length || 0;
|
174
|
+
const importantTypes = tsAst.classes.sort((x, y) => refCount(y) - refCount(x));
|
175
|
+
for (const cls of tsAst.classes) {
|
176
|
+
if (references.includes(cls.name)) {
|
177
|
+
const importantType = importantTypes.find(x => x.properties?.some(p => p.type === cls.name));
|
178
|
+
if (importantType) {
|
179
|
+
const newName = `${importantType.name}${cls.name}`;
|
180
|
+
replaceReference(tsAst, cls.name, newName);
|
181
|
+
}
|
182
|
+
}
|
183
|
+
}
|
184
|
+
}
|
185
|
+
export function replaceReference(gen, fromType, toType) {
|
186
|
+
for (const cls of gen.classes) {
|
187
|
+
if (cls.name === fromType) {
|
188
|
+
cls.name = toType;
|
189
|
+
}
|
190
|
+
if (cls.properties) {
|
191
|
+
for (const prop of cls.properties) {
|
192
|
+
if (prop.type === fromType) {
|
193
|
+
prop.type = toType;
|
194
|
+
}
|
195
|
+
if (prop.type === `${fromType}[]`) {
|
196
|
+
prop.type = `${toType}[]`;
|
197
|
+
}
|
198
|
+
2;
|
199
|
+
if (prop.name === fromType) {
|
200
|
+
prop.name = toType;
|
201
|
+
}
|
202
|
+
if (prop.name === `${fromType}Id`) {
|
203
|
+
prop.name = `${toType}Id`;
|
204
|
+
}
|
205
|
+
}
|
206
|
+
}
|
207
|
+
}
|
208
|
+
}
|
209
|
+
export function replaceIds(gen) {
|
210
|
+
for (const type of gen.classes) {
|
211
|
+
const explicitIdProp = type.properties?.find(x => x.name.toLowerCase() === `${type.name}Id`.toLowerCase());
|
212
|
+
if (explicitIdProp) {
|
213
|
+
const hasId = type.properties.find(x => x.name === 'id');
|
214
|
+
if (hasId) {
|
215
|
+
explicitIdProp.name = 'parentId';
|
216
|
+
explicitIdProp.optional = true;
|
217
|
+
}
|
218
|
+
else {
|
219
|
+
explicitIdProp.name = 'id';
|
220
|
+
}
|
221
|
+
}
|
222
|
+
else {
|
223
|
+
// If using a shortened id for the type e.g. (PerformanceReview, ReviewId)
|
224
|
+
const firstProp = type.properties?.[0];
|
225
|
+
if (firstProp?.name.endsWith('Id') && type.name.toLowerCase().includes(firstProp.name.substring(0, firstProp.name.length - 2).toLowerCase())) {
|
226
|
+
firstProp.name = 'id';
|
227
|
+
}
|
228
|
+
}
|
229
|
+
}
|
230
|
+
// Replace all string Ids with int Ids
|
231
|
+
const anyIntPks = gen.classes.some(x => x.properties?.some(p => p.name === 'id' && p.type === 'number'));
|
232
|
+
if (!anyIntPks) {
|
233
|
+
for (const type of gen.classes) {
|
234
|
+
const idProp = type.properties?.find(x => x.name === 'id');
|
235
|
+
if (idProp) {
|
236
|
+
idProp.type = 'number';
|
237
|
+
}
|
238
|
+
}
|
239
|
+
}
|
240
|
+
}
|
package/dist/ts-parser.js
CHANGED
@@ -1,15 +1,20 @@
|
|
1
|
+
import { lastLeftPart, leftPart, rightPart } from "./utils.js";
|
1
2
|
export class TypeScriptParser {
|
2
3
|
static CLASS_PATTERN = /class\s+(\w+)(?:\s+extends\s+(\w+))?(?:\s+implements\s+([\w,\s]+))?\s*{/g;
|
3
4
|
static INTERFACE_PATTERN = /interface\s+(\w+)(?:\s+extends\s+(\w+))?\s*{/g;
|
4
5
|
static ENUM_PATTERN = /enum\s+(\w+)\s*{([^}]*)}/g;
|
5
6
|
static PROPERTY_PATTERN = /(?:(?<modifier>private|public|protected|readonly)\s+)*(?<name>\w+)(?<optional>\?)?\s*:\s*(?<type>[\w<>[\],\s]+)(?<union>\|\s*[\w<>[\],|,\s]+)?\s*;?/;
|
6
7
|
static ENUM_MEMBER_PATTERN = /(\w+)\s*(?:=\s*("[^"]*"|'[^']*'|\d+|[^,\n]+))?\s*/;
|
7
|
-
|
8
|
-
static
|
8
|
+
static ANNOTATION_PATTERN = /^\s*@([A-Za-z_][A-Za-z0-9_]*\.?[A-Za-z_]?[A-Za-z0-9_]*)/;
|
9
|
+
static REFERENCE_PATTERN = /\/\/\/\s*<reference\s+path="([^"]+)"\s*\/>/g;
|
9
10
|
classes = [];
|
10
11
|
interfaces = [];
|
11
12
|
enums = [];
|
13
|
+
references = [];
|
12
14
|
getLineComment(line) {
|
15
|
+
if (line.match(TypeScriptParser.REFERENCE_PATTERN)) {
|
16
|
+
return undefined;
|
17
|
+
}
|
13
18
|
// Check for single line comment at end of line
|
14
19
|
const singleLineMatch = line.match(/.*?\/\/\s*(.+)$/);
|
15
20
|
if (singleLineMatch) {
|
@@ -27,7 +32,8 @@ export class TypeScriptParser {
|
|
27
32
|
const lineNumber = beforePosition.split('\n').length;
|
28
33
|
if (lineNumber > 0) {
|
29
34
|
const lines = content.split('\n');
|
30
|
-
|
35
|
+
const ret = lines[lineNumber - 2]; // -2 because array is 0-based and we want previous line
|
36
|
+
return ret;
|
31
37
|
}
|
32
38
|
return undefined;
|
33
39
|
}
|
@@ -39,36 +45,19 @@ export class TypeScriptParser {
|
|
39
45
|
while (previousLine && (!previousLine.match(TypeScriptParser.PROPERTY_PATTERN) || previousLine.match(ANNOTATION))) {
|
40
46
|
const annotation = previousLine.match(ANNOTATION) ? parseAnnotation(previousLine) : undefined;
|
41
47
|
if (annotation) {
|
42
|
-
if (previousLine.trimStart().startsWith('//')) {
|
43
|
-
annotation.comment = true;
|
44
|
-
}
|
45
48
|
annotations.push(annotation);
|
46
49
|
}
|
47
50
|
else {
|
48
|
-
const comment = this.getLineComment(previousLine);
|
51
|
+
const comment = this.isComment(previousLine) ? this.getLineComment(previousLine) : null;
|
49
52
|
if (comment) {
|
50
|
-
|
51
|
-
if (annotation) {
|
52
|
-
annotation.comment = true;
|
53
|
-
annotations.push(annotation);
|
54
|
-
}
|
55
|
-
else {
|
56
|
-
commments.unshift(comment);
|
57
|
-
}
|
53
|
+
commments.unshift(comment);
|
58
54
|
}
|
59
55
|
}
|
60
56
|
previousLine = this.getPreviousLine(body, body.indexOf(previousLine));
|
61
57
|
}
|
62
58
|
const lineComment = this.getLineComment(line);
|
63
59
|
if (lineComment) {
|
64
|
-
|
65
|
-
if (annotation) {
|
66
|
-
annotation.comment = true;
|
67
|
-
annotations.push(annotation);
|
68
|
-
}
|
69
|
-
else {
|
70
|
-
commments.push(lineComment);
|
71
|
-
}
|
60
|
+
commments.push(lineComment);
|
72
61
|
}
|
73
62
|
else if (line.match(ANNOTATION)) {
|
74
63
|
const annotation = parseAnnotation(line);
|
@@ -190,6 +179,12 @@ export class TypeScriptParser {
|
|
190
179
|
this.classes.push(cls);
|
191
180
|
}
|
192
181
|
}
|
182
|
+
parseReferences(content) {
|
183
|
+
let match;
|
184
|
+
while ((match = TypeScriptParser.REFERENCE_PATTERN.exec(content))) {
|
185
|
+
this.references.push({ path: match[1] });
|
186
|
+
}
|
187
|
+
}
|
193
188
|
parseEnumMembers(enumBody) {
|
194
189
|
const members = [];
|
195
190
|
const lines = enumBody.split('\n')
|
@@ -215,7 +210,7 @@ export class TypeScriptParser {
|
|
215
210
|
prevIntValue = member.value;
|
216
211
|
}
|
217
212
|
const previousLine = this.getPreviousLine(enumBody, enumBody.indexOf(line));
|
218
|
-
if (previousLine) {
|
213
|
+
if (previousLine && this.isComment(previousLine)) {
|
219
214
|
member.comment = this.getLineComment(previousLine);
|
220
215
|
}
|
221
216
|
const lineComment = this.getLineComment(line);
|
@@ -227,6 +222,12 @@ export class TypeScriptParser {
|
|
227
222
|
});
|
228
223
|
return members;
|
229
224
|
}
|
225
|
+
isComment(s) {
|
226
|
+
if (!s)
|
227
|
+
return false;
|
228
|
+
s = s.trim();
|
229
|
+
return s.startsWith('//') || s.startsWith('/*');
|
230
|
+
}
|
230
231
|
parseEnums(content) {
|
231
232
|
let match;
|
232
233
|
while ((match = TypeScriptParser.ENUM_PATTERN.exec(content))) {
|
@@ -249,10 +250,12 @@ export class TypeScriptParser {
|
|
249
250
|
this.classes = [];
|
250
251
|
this.interfaces = [];
|
251
252
|
this.enums = [];
|
253
|
+
this.parseReferences(sourceCode);
|
252
254
|
this.parseInterfaces(sourceCode);
|
253
255
|
this.parseClasses(sourceCode);
|
254
256
|
this.parseEnums(sourceCode);
|
255
257
|
return {
|
258
|
+
references: this.references,
|
256
259
|
classes: this.classes,
|
257
260
|
interfaces: this.interfaces,
|
258
261
|
enums: this.enums
|
@@ -260,12 +263,18 @@ export class TypeScriptParser {
|
|
260
263
|
}
|
261
264
|
}
|
262
265
|
export function parseAnnotation(annotation) {
|
263
|
-
|
264
|
-
|
266
|
+
const regex = TypeScriptParser.ANNOTATION_PATTERN;
|
267
|
+
if (annotation.includes('//')) {
|
268
|
+
annotation = leftPart(annotation, '//');
|
269
|
+
}
|
265
270
|
const match = annotation.match(regex);
|
266
271
|
if (!match)
|
267
272
|
return null;
|
268
|
-
const [, name
|
273
|
+
const [, name] = match;
|
274
|
+
// Extract parameters if they exist
|
275
|
+
const paramsStr = annotation.includes('(') && annotation.includes(')')
|
276
|
+
? lastLeftPart(rightPart(annotation, '('), ')')
|
277
|
+
: '';
|
269
278
|
try {
|
270
279
|
// Handle multiple arguments by splitting on commas outside quotes/braces
|
271
280
|
const rawArgs = splitArgs(paramsStr);
|
package/dist/tsd-gen.js
CHANGED
@@ -1,35 +1,24 @@
|
|
1
|
-
import { toPascalCase, toCamelCase } from "./utils.js";
|
2
1
|
export class TsdGenerator {
|
3
2
|
interfaces = [];
|
4
3
|
enums = [];
|
5
|
-
ast = { classes: [], interfaces: [], enums: [] };
|
6
|
-
clsToInterface(ast) {
|
7
|
-
const to = {
|
8
|
-
name: ast.name,
|
9
|
-
extends: ast.extends,
|
10
|
-
comment: ast.comment,
|
11
|
-
properties: ast.properties,
|
12
|
-
annotations: ast.annotations,
|
13
|
-
};
|
14
|
-
return to;
|
15
|
-
}
|
4
|
+
ast = { references: [], classes: [], interfaces: [], enums: [] };
|
16
5
|
attrValue(type, value) {
|
17
6
|
return type === 'string' ? `"${value}"` : value;
|
18
7
|
}
|
19
8
|
toAttr(attr) {
|
20
|
-
const
|
9
|
+
const sbCtorArgs = [];
|
21
10
|
if (attr?.constructorArgs?.length) {
|
22
11
|
for (const arg of attr.constructorArgs) {
|
23
|
-
|
12
|
+
sbCtorArgs.push(this.attrValue(typeof arg, arg));
|
24
13
|
}
|
25
14
|
}
|
15
|
+
const sbArgs = [];
|
26
16
|
if (attr.args) {
|
27
17
|
for (const [name, value] of Object.entries(attr.args)) {
|
28
|
-
sbArgs.push(`${name}
|
18
|
+
sbArgs.push(`${name}:${this.attrValue(typeof value, value)}`);
|
29
19
|
}
|
30
20
|
}
|
31
|
-
|
32
|
-
return `${prefix}@${attr.name}(${sbArgs.join(',')})`;
|
21
|
+
return `@${attr.name}(${sbCtorArgs.join(',')}${sbCtorArgs.length && sbArgs.length ? ',' : ''}${sbArgs.length ? `{${sbArgs.join(',')}}` : ''})`;
|
33
22
|
}
|
34
23
|
toInterface(ast) {
|
35
24
|
return this.toType(ast, 'interface');
|
@@ -82,84 +71,32 @@ export class TsdGenerator {
|
|
82
71
|
sb.push('}');
|
83
72
|
return sb.join('\n');
|
84
73
|
}
|
85
|
-
generate(ast
|
74
|
+
generate(ast) {
|
86
75
|
this.ast = ast;
|
87
76
|
const sb = [];
|
77
|
+
if (ast.references?.length) {
|
78
|
+
for (const ref of ast.references) {
|
79
|
+
sb.push(`/// <reference path="${ref.path}" />`);
|
80
|
+
}
|
81
|
+
sb.push('');
|
82
|
+
}
|
88
83
|
for (const cls of ast.enums ?? []) {
|
89
84
|
sb.push(this.toEnum(cls));
|
90
85
|
sb.push('');
|
91
86
|
}
|
92
87
|
for (const cls of ast.interfaces ?? []) {
|
93
|
-
sb.push(this.toType(cls,
|
88
|
+
sb.push(this.toType(cls, "interface"));
|
94
89
|
sb.push('');
|
95
90
|
}
|
96
91
|
for (const cls of ast.classes ?? []) {
|
97
|
-
|
98
|
-
sb.push(this.toType(iface, type));
|
92
|
+
sb.push(this.toType(cls, "class"));
|
99
93
|
sb.push('');
|
100
94
|
}
|
101
95
|
const tsd = sb.join('\n');
|
102
96
|
return tsd;
|
103
97
|
}
|
104
98
|
}
|
105
|
-
export
|
106
|
-
|
107
|
-
|
108
|
-
};
|
109
|
-
convertType(type) {
|
110
|
-
type.name = toPascalCase(type.name);
|
111
|
-
type.properties?.forEach(prop => {
|
112
|
-
prop.name = toCamelCase(prop.name);
|
113
|
-
if (prop.type.startsWith('Array<')) {
|
114
|
-
const elType = prop.type.slice('Array<'.length, -1);
|
115
|
-
prop.type = elType + '[]';
|
116
|
-
}
|
117
|
-
if (prop.type === 'User') {
|
118
|
-
prop.name = 'userId';
|
119
|
-
prop.type = 'string';
|
120
|
-
}
|
121
|
-
if (prop.type === 'User[]') {
|
122
|
-
if (prop.name.endsWith('s')) {
|
123
|
-
prop.name = prop.name.slice(0, -1) + 'Ids';
|
124
|
-
}
|
125
|
-
prop.type = 'string[]';
|
126
|
-
}
|
127
|
-
});
|
128
|
-
this.rewriteDuplicateTypePropNames(type);
|
129
|
-
this.rewriteSelfReferencingIds(type);
|
130
|
-
}
|
131
|
-
rewriteDuplicateTypePropNames(type) {
|
132
|
-
const duplicateProp = type.properties?.find(x => toPascalCase(x.name) === type.name);
|
133
|
-
if (duplicateProp) {
|
134
|
-
const newName = this.duplicateTypePropMap[duplicateProp.name] ?? 'value';
|
135
|
-
duplicateProp.name = newName;
|
136
|
-
}
|
137
|
-
}
|
138
|
-
rewriteSelfReferencingIds(type) {
|
139
|
-
const selfRefId = type.properties?.find(x => x.name.toLowerCase() === `${type.name}id`.toLowerCase());
|
140
|
-
if (selfRefId) {
|
141
|
-
selfRefId.name = `parentId`;
|
142
|
-
}
|
143
|
-
}
|
144
|
-
generate(ast) {
|
145
|
-
ast.classes?.forEach(cls => {
|
146
|
-
this.convertType(cls);
|
147
|
-
});
|
148
|
-
ast.interfaces?.forEach(cls => {
|
149
|
-
this.convertType(cls);
|
150
|
-
});
|
151
|
-
ast.enums?.forEach(e => {
|
152
|
-
e.name = toPascalCase(e.name);
|
153
|
-
e.members?.forEach(m => {
|
154
|
-
m.name = toPascalCase(m.name);
|
155
|
-
});
|
156
|
-
});
|
157
|
-
if (ast.classes) {
|
158
|
-
ast.classes = ast.classes.filter(x => x.name !== 'User');
|
159
|
-
}
|
160
|
-
if (ast.interfaces) {
|
161
|
-
ast.interfaces = ast.interfaces.filter(x => x.name !== 'User');
|
162
|
-
}
|
163
|
-
return super.generate(ast, "class");
|
164
|
-
}
|
99
|
+
export function toTsd(tsAst) {
|
100
|
+
const gen = new TsdGenerator();
|
101
|
+
return gen.generate(tsAst);
|
165
102
|
}
|
package/dist/utils.js
CHANGED
@@ -121,9 +121,34 @@ export function rightPart(s, needle) {
|
|
121
121
|
? s
|
122
122
|
: s.substring(pos + needle.length);
|
123
123
|
}
|
124
|
+
export function lastLeftPart(s, needle) {
|
125
|
+
if (s == null)
|
126
|
+
return null;
|
127
|
+
let pos = s.lastIndexOf(needle);
|
128
|
+
return pos == -1
|
129
|
+
? s
|
130
|
+
: s.substring(0, pos);
|
131
|
+
}
|
132
|
+
export function lastRightPart(s, needle) {
|
133
|
+
if (s == null)
|
134
|
+
return null;
|
135
|
+
let pos = s.lastIndexOf(needle);
|
136
|
+
return pos == -1
|
137
|
+
? s
|
138
|
+
: s.substring(pos + needle.length);
|
139
|
+
}
|
124
140
|
export function splitCase(t) {
|
125
141
|
return typeof t != 'string' ? t : t.replace(/([A-Z]|[0-9]+)/g, ' $1').replace(/_/g, ' ').trim();
|
126
142
|
}
|
143
|
+
export function pick(o, keys) {
|
144
|
+
const to = {};
|
145
|
+
Object.keys(o).forEach(k => {
|
146
|
+
if (keys.indexOf(k) >= 0) {
|
147
|
+
to[k] = o[k];
|
148
|
+
}
|
149
|
+
});
|
150
|
+
return to;
|
151
|
+
}
|
127
152
|
export function refCount(t) {
|
128
153
|
return t.properties?.filter(x => x.attributes?.some(x => x.name === 'References')).length || 0;
|
129
154
|
}
|
@@ -140,7 +165,7 @@ export function tsdWithoutPrompt(tsd) {
|
|
140
165
|
}
|
141
166
|
export function parseTsdHeader(tsd) {
|
142
167
|
const header = tsd.includes('/*prompt:')
|
143
|
-
? leftPart(rightPart(tsd, '/*prompt:'), '*/').trim()
|
168
|
+
? leftPart(rightPart(tsd, '/*prompt:') ?? '', '*/').trim()
|
144
169
|
: null;
|
145
170
|
if (!header)
|
146
171
|
return null;
|