okai 0.0.11 → 0.0.12
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/cs-apis.js +5 -5
- package/dist/cs-ast.js +37 -0
- package/dist/cs-gen.js +6 -0
- package/dist/index.js +25 -6
- package/dist/ts-parser.js +191 -31
- package/dist/tsd-gen.js +30 -1
- package/package.json +1 -1
package/dist/cs-apis.js
CHANGED
@@ -29,18 +29,18 @@ export class CSharpApiGenerator extends CSharpGenerator {
|
|
29
29
|
}
|
30
30
|
if (op.requiredRoles?.length) {
|
31
31
|
const adminRole = op.requiredRoles.includes('Admin');
|
32
|
-
if (adminRole) {
|
32
|
+
if (adminRole && !this.typeHasAttr(op.request, 'ValidateIsAdmin')) {
|
33
33
|
sb.push(`[ValidateIsAdmin]`);
|
34
34
|
}
|
35
35
|
const roles = op.requiredRoles.filter(r => r !== 'Admin');
|
36
|
-
if (roles.length) {
|
36
|
+
if (roles.length && !this.typeHasAttr(op.request, 'ValidateHasRole')) {
|
37
37
|
sb.push(`[ValidateHasRole("${roles[0]}")]`);
|
38
38
|
}
|
39
39
|
}
|
40
|
-
else if (op.requiresAuth) {
|
40
|
+
else if (op.requiresAuth && !this.typeHasAttr(op.request, 'ValidateIsAuthenticated')) {
|
41
41
|
sb.push(`[ValidateIsAuthenticated]`);
|
42
42
|
}
|
43
|
-
if (cls.description) {
|
43
|
+
if (cls.description && !this.typeHasAttr(op.request, 'Api')) {
|
44
44
|
sb.push(`[Api("${cls.description}")]`);
|
45
45
|
}
|
46
46
|
for (const attr of cls.attributes ?? []) {
|
@@ -55,7 +55,7 @@ export class CSharpApiGenerator extends CSharpGenerator {
|
|
55
55
|
sb.push('{');
|
56
56
|
for (const prop of cls.properties) {
|
57
57
|
this.addNamespace(prop.namespace);
|
58
|
-
if (prop.description) {
|
58
|
+
if (prop.description && !this.propHasAttr(prop, 'ApiMember')) {
|
59
59
|
if (!prop.description.includes('\n')) {
|
60
60
|
sb.push(` [ApiMember(Description="${prop.description.replace(/"/g, '\\"')}")]`);
|
61
61
|
}
|
package/dist/cs-ast.js
CHANGED
@@ -75,6 +75,35 @@ export class CSharpAst {
|
|
75
75
|
}
|
76
76
|
return this.typeMap[type] ?? { name: type, namespace: "MyApp" };
|
77
77
|
}
|
78
|
+
csharpAttribute(attr) {
|
79
|
+
const to = { name: toPascalCase(attr.name) };
|
80
|
+
const attrType = (value) => typeof value == 'string'
|
81
|
+
? (`${value}`.startsWith('typeof') ? "Type" : "string")
|
82
|
+
: typeof value == "object"
|
83
|
+
? (value instanceof Date ? "string" : Array.isArray(value) ? "array" : "object")
|
84
|
+
: typeof value;
|
85
|
+
if (attr.constructorArgs?.length) {
|
86
|
+
to.constructorArgs = attr.constructorArgs.map(x => {
|
87
|
+
const type = attrType(x.value);
|
88
|
+
return {
|
89
|
+
name: 'String',
|
90
|
+
type,
|
91
|
+
value: `${x.value}`
|
92
|
+
};
|
93
|
+
});
|
94
|
+
}
|
95
|
+
if (attr.args && Object.keys(attr.args).length) {
|
96
|
+
to.args = Object.entries(attr.args).map(([name, value]) => {
|
97
|
+
const type = attrType(value);
|
98
|
+
return {
|
99
|
+
name: toPascalCase(name),
|
100
|
+
type,
|
101
|
+
value: `${value}`
|
102
|
+
};
|
103
|
+
});
|
104
|
+
}
|
105
|
+
return to;
|
106
|
+
}
|
78
107
|
addMetadataType(cls) {
|
79
108
|
const type = {
|
80
109
|
name: this.toCsName(cls.name),
|
@@ -110,9 +139,15 @@ export class CSharpAst {
|
|
110
139
|
args: [{ name: "Currency", type: "constant", value: "NumberCurrency.USD" }]
|
111
140
|
});
|
112
141
|
}
|
142
|
+
if (p.annotations?.length) {
|
143
|
+
prop.attributes = p.annotations.map(x => this.csharpAttribute(x));
|
144
|
+
}
|
113
145
|
return prop;
|
114
146
|
}),
|
115
147
|
};
|
148
|
+
if (cls.annotations?.length) {
|
149
|
+
type.attributes = cls.annotations.map(x => this.csharpAttribute(x));
|
150
|
+
}
|
116
151
|
// Add dependent types first
|
117
152
|
type.properties.filter(x => x.namespace === 'MyApp'
|
118
153
|
&& x.name !== cls.name
|
@@ -588,6 +623,8 @@ export class CSharpAst {
|
|
588
623
|
if (icon) {
|
589
624
|
if (!type.attributes)
|
590
625
|
type.attributes = [];
|
626
|
+
if (type.attributes.some(x => x.name === 'Icon'))
|
627
|
+
continue;
|
591
628
|
type.attributes.push({
|
592
629
|
name: "Icon",
|
593
630
|
args: [{ name: "Svg", type: "string", value: icon }]
|
package/dist/cs-gen.js
CHANGED
@@ -5,6 +5,12 @@ export class CSharpGenerator {
|
|
5
5
|
classes = [];
|
6
6
|
enums = [];
|
7
7
|
ast = { namespaces: [], operations: [], types: [] };
|
8
|
+
typeHasAttr(type, name) {
|
9
|
+
return type.attributes?.some(x => x.name === name);
|
10
|
+
}
|
11
|
+
propHasAttr(prop, name) {
|
12
|
+
return prop.attributes?.some(x => x.name === name);
|
13
|
+
}
|
8
14
|
addNamespace(ns) {
|
9
15
|
if (!ns || this.namespaces.includes(ns))
|
10
16
|
return;
|
package/dist/index.js
CHANGED
@@ -45,21 +45,39 @@ function parseArgs(...args) {
|
|
45
45
|
case "/license":
|
46
46
|
ret.license = args[++i];
|
47
47
|
break;
|
48
|
+
case "/url":
|
49
|
+
ret.baseUrl = args[++i];
|
50
|
+
break;
|
51
|
+
case "/cached":
|
52
|
+
ret.cached = true;
|
53
|
+
break;
|
48
54
|
default:
|
49
55
|
ret.unknown = ret.unknown || [];
|
50
56
|
ret.unknown.push(arg);
|
51
57
|
break;
|
52
58
|
}
|
53
59
|
}
|
54
|
-
else if (ret.type === "help" && ["help", "info", "init", "ls", "rm"].includes(arg)) {
|
60
|
+
else if (ret.type === "help" && ["help", "info", "init", "ls", "rm", "update"].includes(arg)) {
|
55
61
|
if (arg == "help")
|
56
62
|
ret.type = "help";
|
57
63
|
else if (arg == "info")
|
58
64
|
ret.type = "info";
|
59
65
|
else if (arg == "init")
|
60
66
|
ret.type = "init";
|
61
|
-
else if (arg == "
|
67
|
+
else if (arg == "update") {
|
68
|
+
ret.type = "update";
|
69
|
+
ret.tsdFile = args[++i];
|
70
|
+
if (ret.tsdFile && !ret.tsdFile.endsWith('.d.ts')) {
|
71
|
+
ret.tsdFile += '.d.ts';
|
72
|
+
}
|
73
|
+
}
|
74
|
+
else if (arg == "rm") {
|
62
75
|
ret.type = "remove";
|
76
|
+
ret.tsdFile = args[++i];
|
77
|
+
if (ret.tsdFile && !ret.tsdFile.endsWith('.d.ts')) {
|
78
|
+
ret.tsdFile += '.d.ts';
|
79
|
+
}
|
80
|
+
}
|
63
81
|
else if (arg == "ls") {
|
64
82
|
ret.type = "list";
|
65
83
|
ret.list = args[++i];
|
@@ -79,6 +97,9 @@ function parseArgs(...args) {
|
|
79
97
|
}
|
80
98
|
}
|
81
99
|
if (ret.type === "prompt") {
|
100
|
+
if (!ret.cached && process.env.OKAI_CACHED) {
|
101
|
+
ret.cached = true;
|
102
|
+
}
|
82
103
|
if (!ret.models && process.env.OKAI_MODELS) {
|
83
104
|
ret.models = process.env.OKAI_MODELS;
|
84
105
|
}
|
@@ -321,7 +342,7 @@ Options:
|
|
321
342
|
}
|
322
343
|
async function fetchGistFiles(command) {
|
323
344
|
const url = new URL('/models/gist', command.baseUrl);
|
324
|
-
if (
|
345
|
+
if (command.cached) {
|
325
346
|
url.searchParams.append('cached', `1`);
|
326
347
|
}
|
327
348
|
url.searchParams.append('prompt', command.prompt);
|
@@ -489,7 +510,7 @@ async function createGistPreview(title, gist) {
|
|
489
510
|
function chooseFile(ctx, info, gist) {
|
490
511
|
const { screen, titleBar, fileList, preview, statusBar, result } = ctx;
|
491
512
|
const file = gist.files[result.selectedFile];
|
492
|
-
|
513
|
+
console.clear();
|
493
514
|
const tsd = file.content;
|
494
515
|
const tsdAst = toAst(tsd);
|
495
516
|
const csAst = toMetadataTypes(tsdAst);
|
@@ -518,8 +539,6 @@ function chooseFile(ctx, info, gist) {
|
|
518
539
|
const fullTsdPath = path.join(info.slnDir, relativeServiceModelDir, tsdFileName);
|
519
540
|
const fullApiPath = path.join(info.slnDir, relativeServiceModelDir, apiFileName);
|
520
541
|
const fullMigrationPath = path.join(info.slnDir, relativeMigrationDir, migrationFileName);
|
521
|
-
const clearScreen = blessed.screen();
|
522
|
-
clearScreen.render();
|
523
542
|
if (!fs.existsSync(path.dirname(fullTsdPath))) {
|
524
543
|
console.log(`Directory does not exist: ${path.dirname(fullTsdPath)}`);
|
525
544
|
process.exit(0);
|
package/dist/ts-parser.js
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
export class TypeScriptParser {
|
2
|
-
static CLASS_PATTERN = /class\s+(\w+)(?:\s+extends\s+(\w+))?(?:\s+implements\s+([\w,\s]+))?\s*{
|
3
|
-
static INTERFACE_PATTERN = /interface\s+(\w+)(?:\s+extends\s+(\w+))?\s*{
|
2
|
+
static CLASS_PATTERN = /class\s+(\w+)(?:\s+extends\s+(\w+))?(?:\s+implements\s+([\w,\s]+))?\s*{/g;
|
3
|
+
static INTERFACE_PATTERN = /interface\s+(\w+)(?:\s+extends\s+(\w+))?\s*{/g;
|
4
4
|
static ENUM_PATTERN = /enum\s+(\w+)\s*{([^}]*)}/g;
|
5
5
|
static PROPERTY_PATTERN = /(?:(?<modifier>private|public|protected|readonly)\s+)*(?<name>\w+)(?<optional>\?)?\s*:\s*(?<type>[\w<>[\],\s]+)(?<union>\|\s*[\w<>[\],|,\s]+)?\s*;?/;
|
6
6
|
static ENUM_MEMBER_PATTERN = /(\w+)\s*(?:=\s*("[^"]*"|'[^']*'|\d+|[^,\n]+))?\s*/;
|
7
|
+
static ANNOTATION_PATTERN = /@\w+\(.*\)/;
|
7
8
|
classes = [];
|
8
9
|
interfaces = [];
|
9
10
|
enums = [];
|
@@ -23,26 +24,80 @@ export class TypeScriptParser {
|
|
23
24
|
getPreviousLine(content, position) {
|
24
25
|
const beforePosition = content.substring(0, position);
|
25
26
|
const lineNumber = beforePosition.split('\n').length;
|
26
|
-
if (lineNumber >
|
27
|
+
if (lineNumber > 0) {
|
27
28
|
const lines = content.split('\n');
|
28
29
|
return lines[lineNumber - 2]; // -2 because array is 0-based and we want previous line
|
29
30
|
}
|
30
31
|
return undefined;
|
31
32
|
}
|
33
|
+
parseMetadata(body, line) {
|
34
|
+
const annotations = [];
|
35
|
+
const commments = [];
|
36
|
+
const ANNOTATION = TypeScriptParser.ANNOTATION_PATTERN;
|
37
|
+
let previousLine = this.getPreviousLine(body, body.indexOf(line));
|
38
|
+
while (previousLine && (!previousLine.match(TypeScriptParser.PROPERTY_PATTERN) || previousLine.match(ANNOTATION))) {
|
39
|
+
const annotation = previousLine.match(ANNOTATION) ? parseAnnotation(previousLine) : undefined;
|
40
|
+
if (annotation) {
|
41
|
+
if (previousLine.trimStart().startsWith('//')) {
|
42
|
+
annotation.comment = true;
|
43
|
+
}
|
44
|
+
annotations.push(annotation);
|
45
|
+
}
|
46
|
+
else {
|
47
|
+
const comment = this.getLineComment(previousLine);
|
48
|
+
if (comment) {
|
49
|
+
const annotation = comment.match(ANNOTATION) ? parseAnnotation(comment) : undefined;
|
50
|
+
if (annotation) {
|
51
|
+
annotation.comment = true;
|
52
|
+
annotations.push(annotation);
|
53
|
+
}
|
54
|
+
else {
|
55
|
+
commments.unshift(comment);
|
56
|
+
}
|
57
|
+
}
|
58
|
+
}
|
59
|
+
previousLine = this.getPreviousLine(body, body.indexOf(previousLine));
|
60
|
+
}
|
61
|
+
const lineComment = this.getLineComment(line);
|
62
|
+
if (lineComment) {
|
63
|
+
const annotation = lineComment.match(ANNOTATION) ? parseAnnotation(lineComment) : undefined;
|
64
|
+
if (annotation) {
|
65
|
+
annotation.comment = true;
|
66
|
+
annotations.push(annotation);
|
67
|
+
}
|
68
|
+
else {
|
69
|
+
commments.push(lineComment);
|
70
|
+
}
|
71
|
+
}
|
72
|
+
else if (line.match(ANNOTATION)) {
|
73
|
+
const annotation = parseAnnotation(line);
|
74
|
+
if (annotation) {
|
75
|
+
annotations.push(annotation);
|
76
|
+
}
|
77
|
+
}
|
78
|
+
return {
|
79
|
+
comment: commments.length ? commments.join('\n') : undefined,
|
80
|
+
annotations: annotations.length ? annotations : undefined,
|
81
|
+
};
|
82
|
+
}
|
32
83
|
parseClassProperties(classBody) {
|
33
84
|
const props = [];
|
34
85
|
const lines = this.cleanBody(classBody).split('\n');
|
35
86
|
lines.forEach((line, index) => {
|
36
87
|
if (line.trim().startsWith('//'))
|
37
88
|
return;
|
89
|
+
if (line.match(TypeScriptParser.ANNOTATION_PATTERN))
|
90
|
+
return;
|
38
91
|
const match = line.match(TypeScriptParser.PROPERTY_PATTERN);
|
39
92
|
if (match?.groups) {
|
40
93
|
const member = {
|
41
|
-
modifier: match.groups.modifier,
|
42
94
|
name: match.groups.name,
|
43
95
|
type: match.groups.type.trim(),
|
44
|
-
optional: match.groups.optional === '?' || undefined,
|
45
96
|
};
|
97
|
+
if (match.groups.modifier)
|
98
|
+
member.modifier = match.groups.modifier;
|
99
|
+
if (match.groups.optional === '?')
|
100
|
+
member.optional = true;
|
46
101
|
// empty for properties with inline objects `salaryRange: {min:number, max:number};`
|
47
102
|
if (!member.type) {
|
48
103
|
return;
|
@@ -56,49 +111,82 @@ export class TypeScriptParser {
|
|
56
111
|
member.optional = true;
|
57
112
|
}
|
58
113
|
}
|
59
|
-
const
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
previousLine = this.getPreviousLine(classBody, classBody.indexOf(previousLine));
|
66
|
-
}
|
67
|
-
const lineComment = this.getLineComment(line);
|
68
|
-
if (lineComment) {
|
69
|
-
commments.push(lineComment);
|
70
|
-
}
|
71
|
-
if (commments.length) {
|
72
|
-
member.comment = commments.join('\n');
|
73
|
-
}
|
114
|
+
const { comment, annotations } = this.parseMetadata(classBody, line);
|
115
|
+
if (comment)
|
116
|
+
member.comment = comment;
|
117
|
+
if (annotations)
|
118
|
+
member.annotations = annotations;
|
119
|
+
// console.log('member', { comment, annotations, line })
|
74
120
|
props.push(member);
|
75
121
|
}
|
76
122
|
});
|
77
123
|
return props;
|
78
124
|
}
|
125
|
+
getBlockBody(content, startIndex) {
|
126
|
+
const bodyStartPos = content.indexOf('{', startIndex);
|
127
|
+
// console.log('bodyStartPos', `<|${content.substring(bodyStartPos, bodyStartPos + 20)}|>`)
|
128
|
+
// Find the end of the body
|
129
|
+
let depth = 0;
|
130
|
+
let bodyEndPos = bodyStartPos;
|
131
|
+
for (let i = bodyStartPos; i < content.length; i++) {
|
132
|
+
if (content[i] === '{')
|
133
|
+
depth++;
|
134
|
+
if (content[i] === '}')
|
135
|
+
depth--;
|
136
|
+
if (depth === 0) {
|
137
|
+
bodyEndPos = i + 1;
|
138
|
+
break;
|
139
|
+
}
|
140
|
+
}
|
141
|
+
let body = content.substring(bodyStartPos + 1, bodyEndPos - 1);
|
142
|
+
return body;
|
143
|
+
}
|
79
144
|
parseInterfaces(content) {
|
80
145
|
let match;
|
81
146
|
while ((match = TypeScriptParser.INTERFACE_PATTERN.exec(content))) {
|
82
147
|
const previousLine = this.getPreviousLine(content, match.index);
|
83
|
-
this.
|
148
|
+
const body = this.getBlockBody(content, match.index);
|
149
|
+
const cls = {
|
84
150
|
name: match[1],
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
151
|
+
properties: this.parseClassProperties(body),
|
152
|
+
};
|
153
|
+
if (match[2]) {
|
154
|
+
cls.extends = match[2];
|
155
|
+
}
|
156
|
+
if (previousLine) {
|
157
|
+
const { comment, annotations } = this.parseMetadata(content, previousLine);
|
158
|
+
if (comment)
|
159
|
+
cls.comment = comment;
|
160
|
+
if (annotations)
|
161
|
+
cls.annotations = annotations;
|
162
|
+
}
|
163
|
+
this.interfaces.push(cls);
|
89
164
|
}
|
90
165
|
}
|
91
166
|
parseClasses(content) {
|
92
167
|
let match;
|
93
168
|
while ((match = TypeScriptParser.CLASS_PATTERN.exec(content))) {
|
94
169
|
const previousLine = this.getPreviousLine(content, match.index);
|
95
|
-
this.
|
170
|
+
const body = this.getBlockBody(content, match.index);
|
171
|
+
const cls = {
|
96
172
|
name: match[1],
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
}
|
173
|
+
properties: this.parseClassProperties(body),
|
174
|
+
};
|
175
|
+
if (match[2]) {
|
176
|
+
cls.extends = match[2];
|
177
|
+
}
|
178
|
+
const impls = match[3]?.split(',').map(i => i.trim());
|
179
|
+
if (impls) {
|
180
|
+
cls.implements = impls;
|
181
|
+
}
|
182
|
+
if (previousLine) {
|
183
|
+
const { comment, annotations } = this.parseMetadata(content, previousLine);
|
184
|
+
if (comment)
|
185
|
+
cls.comment = comment;
|
186
|
+
if (annotations)
|
187
|
+
cls.annotations = annotations;
|
188
|
+
}
|
189
|
+
this.classes.push(cls);
|
102
190
|
}
|
103
191
|
}
|
104
192
|
parseEnumMembers(enumBody) {
|
@@ -170,3 +258,75 @@ export class TypeScriptParser {
|
|
170
258
|
};
|
171
259
|
}
|
172
260
|
}
|
261
|
+
export function parseAnnotation(annotation) {
|
262
|
+
// Match @name and everything inside ()
|
263
|
+
const regex = /@(\w+)\s*\((.*)\)/;
|
264
|
+
const match = annotation.match(regex);
|
265
|
+
if (!match)
|
266
|
+
return null;
|
267
|
+
const [, name, paramsStr] = match;
|
268
|
+
try {
|
269
|
+
// Handle multiple arguments by splitting on commas outside quotes/braces
|
270
|
+
const rawArgs = splitArgs(paramsStr);
|
271
|
+
// Parse each argument
|
272
|
+
const parsedArgs = rawArgs.map(arg => {
|
273
|
+
if (arg.startsWith('{')) {
|
274
|
+
// Parse object literals
|
275
|
+
return (new Function(`return ${arg}`))();
|
276
|
+
}
|
277
|
+
else if (arg.startsWith('"') || arg.startsWith("'") || arg.startsWith("`")) {
|
278
|
+
// Parse strings
|
279
|
+
return arg.slice(1, -1);
|
280
|
+
}
|
281
|
+
else if (!isNaN(parseInt(arg))) {
|
282
|
+
// Parse numbers
|
283
|
+
return Number(arg);
|
284
|
+
}
|
285
|
+
return arg;
|
286
|
+
});
|
287
|
+
const lastArg = parsedArgs[parsedArgs.length - 1];
|
288
|
+
const args = typeof lastArg === 'object'
|
289
|
+
? lastArg
|
290
|
+
: undefined;
|
291
|
+
const constructorArgs = args ? parsedArgs.slice(0, -1) : parsedArgs;
|
292
|
+
const to = { name };
|
293
|
+
if (constructorArgs.length)
|
294
|
+
to.constructorArgs = constructorArgs;
|
295
|
+
if (args)
|
296
|
+
to.args = args;
|
297
|
+
return to;
|
298
|
+
}
|
299
|
+
catch (e) {
|
300
|
+
return null;
|
301
|
+
}
|
302
|
+
}
|
303
|
+
// Helper to split args while respecting objects/strings
|
304
|
+
function splitArgs(str) {
|
305
|
+
const args = [];
|
306
|
+
let current = '';
|
307
|
+
let depth = 0;
|
308
|
+
let inQuotes = false;
|
309
|
+
let quoteChar = '';
|
310
|
+
for (let char of str) {
|
311
|
+
if ((char === '"' || char === "'") && !inQuotes) {
|
312
|
+
inQuotes = true;
|
313
|
+
quoteChar = char;
|
314
|
+
}
|
315
|
+
else if (char === quoteChar && inQuotes) {
|
316
|
+
inQuotes = false;
|
317
|
+
}
|
318
|
+
else if (char === '{')
|
319
|
+
depth++;
|
320
|
+
else if (char === '}')
|
321
|
+
depth--;
|
322
|
+
else if (char === ',' && depth === 0 && !inQuotes) {
|
323
|
+
args.push(current.trim());
|
324
|
+
current = '';
|
325
|
+
continue;
|
326
|
+
}
|
327
|
+
current += char;
|
328
|
+
}
|
329
|
+
if (current.trim())
|
330
|
+
args.push(current.trim());
|
331
|
+
return args;
|
332
|
+
}
|
package/dist/tsd-gen.js
CHANGED
@@ -8,21 +8,50 @@ export class TsdGenerator {
|
|
8
8
|
name: ast.name,
|
9
9
|
extends: ast.extends,
|
10
10
|
comment: ast.comment,
|
11
|
-
properties: ast.properties
|
11
|
+
properties: ast.properties,
|
12
|
+
annotations: ast.annotations,
|
12
13
|
};
|
13
14
|
return to;
|
14
15
|
}
|
16
|
+
attrValue(type, value) {
|
17
|
+
return type === 'string' ? `"${value}"` : value;
|
18
|
+
}
|
19
|
+
toAttr(attr) {
|
20
|
+
const sbArgs = [];
|
21
|
+
if (attr?.constructorArgs?.length) {
|
22
|
+
for (const arg of attr.constructorArgs) {
|
23
|
+
sbArgs.push(this.attrValue(typeof arg, arg));
|
24
|
+
}
|
25
|
+
}
|
26
|
+
if (attr.args) {
|
27
|
+
for (const [name, value] of Object.entries(attr.args)) {
|
28
|
+
sbArgs.push(`${name}=${this.attrValue(typeof value, value)}`);
|
29
|
+
}
|
30
|
+
}
|
31
|
+
const prefix = attr.comment ? '// ' : '';
|
32
|
+
return `${prefix}@${attr.name}(${sbArgs.join(',')})`;
|
33
|
+
}
|
15
34
|
toInterface(ast) {
|
16
35
|
const sb = [];
|
17
36
|
if (ast.comment) {
|
18
37
|
sb.push(ast.comment.split('\n').map(x => `// ${x}`).join('\n'));
|
19
38
|
}
|
39
|
+
if (ast.annotations?.length) {
|
40
|
+
for (const attr of ast.annotations) {
|
41
|
+
sb.push(this.toAttr(attr));
|
42
|
+
}
|
43
|
+
}
|
20
44
|
const extend = ast.extends ? ` extends ${ast.extends}` : '';
|
21
45
|
sb.push(`export interface ${ast.name}${extend} {`);
|
22
46
|
for (const prop of ast.properties) {
|
23
47
|
if (prop.comment) {
|
24
48
|
sb.push(prop.comment.split('\n').map(x => ` // ${x}`).join('\n'));
|
25
49
|
}
|
50
|
+
if (prop.annotations?.length) {
|
51
|
+
for (const attr of prop.annotations) {
|
52
|
+
sb.push(' ' + this.toAttr(attr));
|
53
|
+
}
|
54
|
+
}
|
26
55
|
sb.push(` ${prop.name}${prop.optional ? '?' : ''}: ${prop.type}`);
|
27
56
|
}
|
28
57
|
sb.push('}');
|