@siyavuyachagi/typesharp 0.1.3 → 0.1.5-beta.0
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/README.md +138 -118
- package/dist/cli/index.js +2 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +114 -38
- package/dist/core/index.js.map +1 -1
- package/dist/generator/index.d.ts +5 -0
- package/dist/generator/index.d.ts.map +1 -1
- package/dist/generator/index.js +21 -18
- package/dist/generator/index.js.map +1 -1
- package/dist/helpers/change-tracker.d.ts +14 -0
- package/dist/helpers/change-tracker.d.ts.map +1 -0
- package/dist/helpers/change-tracker.js +95 -0
- package/dist/helpers/change-tracker.js.map +1 -0
- package/dist/parser/index.d.ts.map +1 -1
- package/dist/parser/index.js +2 -108
- package/dist/parser/index.js.map +1 -1
- package/dist/parser/parse-classes-from-file.d.ts +6 -0
- package/dist/parser/parse-classes-from-file.d.ts.map +1 -0
- package/dist/parser/parse-classes-from-file.js +279 -0
- package/dist/parser/parse-classes-from-file.js.map +1 -0
- package/dist/parser/parse-properties.d.ts +22 -0
- package/dist/parser/parse-properties.d.ts.map +1 -1
- package/dist/parser/parse-properties.js +177 -34
- package/dist/parser/parse-properties.js.map +1 -1
- package/dist/parser/resolve-project-files-from-source.d.ts.map +1 -1
- package/dist/parser/resolve-project-files-from-source.js.map +1 -1
- package/dist/types/index.d.ts +3 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseClassesFromFile = parseClassesFromFile;
|
|
4
|
+
const parse_properties_1 = require("./parse-properties");
|
|
5
|
+
const parse_properties_2 = require("./parse-properties");
|
|
6
|
+
/**
|
|
7
|
+
* Parse classes and records from a C# file content
|
|
8
|
+
*/
|
|
9
|
+
function parseClassesFromFile(content, targetAnnotation) {
|
|
10
|
+
const classes = [];
|
|
11
|
+
const cleanContent = removeComments(content);
|
|
12
|
+
const annotationRegex = new RegExp(`\\[${targetAnnotation}(?:Attribute)?(?:\\(\\s*"([^"]*)"\\s*\\))?\\]`, 'g');
|
|
13
|
+
const matches = [...cleanContent.matchAll(annotationRegex)];
|
|
14
|
+
for (const match of matches) {
|
|
15
|
+
const startIndex = match.index;
|
|
16
|
+
const afterAnnotation = cleanContent.substring(startIndex);
|
|
17
|
+
// Check if it's an enum
|
|
18
|
+
const enumMatch = afterAnnotation.match(/(?:\[[\w]+(?:\([^)]*\))?\]\s*)*public\s+enum\s+(\w+)/);
|
|
19
|
+
if (enumMatch) {
|
|
20
|
+
const typeNameOverride = match[1] ?? undefined;
|
|
21
|
+
const enumClass = parseEnum(afterAnnotation, typeNameOverride ?? enumMatch[1]);
|
|
22
|
+
if (enumClass)
|
|
23
|
+
classes.push(enumClass);
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
// Check if it's a record (positional or with body)
|
|
27
|
+
// NOTE: we do NOT capture ctor params in the regex — attributes inside params
|
|
28
|
+
// contain `()` which would break a [^)]* capture group.
|
|
29
|
+
// Use paren-balanced extraction instead.
|
|
30
|
+
const recordMatch = afterAnnotation.match(/(?:\[[\w]+(?:\([^)]*\))?\]\s*)*public\s+(?:sealed\s+|abstract\s+)?record\s+(?:class\s+|struct\s+)?(\w+)(?:<([^>]+)>)?/);
|
|
31
|
+
if (recordMatch) {
|
|
32
|
+
const className = recordMatch[1];
|
|
33
|
+
const genericParams = recordMatch[2];
|
|
34
|
+
// Extract primary ctor params with paren-depth balancing so that
|
|
35
|
+
// attributes like [property: TypeAs("Date")] don't truncate the capture
|
|
36
|
+
const primaryCtorParams = extractPrimaryCtorParams(afterAnnotation);
|
|
37
|
+
// After the ctor (if any), look for `: BaseType<Generics>`
|
|
38
|
+
const ctorOpenIdx = afterAnnotation.indexOf('(');
|
|
39
|
+
const afterCtor = (primaryCtorParams !== undefined && ctorOpenIdx !== -1)
|
|
40
|
+
? afterAnnotation.slice(ctorOpenIdx + primaryCtorParams.length + 2)
|
|
41
|
+
: afterAnnotation.slice(recordMatch[0].length);
|
|
42
|
+
const inheritMatch = afterCtor.match(/^\s*:\s*(\w+)(?:<([^>]+)>)?/);
|
|
43
|
+
const inheritsFrom = inheritMatch?.[1];
|
|
44
|
+
const baseGenerics = inheritMatch?.[2];
|
|
45
|
+
const resolvedInheritsFrom = inheritsFrom && /^I[A-Z]/.test(inheritsFrom)
|
|
46
|
+
? undefined
|
|
47
|
+
: inheritsFrom;
|
|
48
|
+
const genericParameters = genericParams
|
|
49
|
+
? genericParams.split(',').map(p => p.trim())
|
|
50
|
+
: undefined;
|
|
51
|
+
const baseClassGenerics = baseGenerics
|
|
52
|
+
? baseGenerics.split(',').map(p => p.trim())
|
|
53
|
+
: undefined;
|
|
54
|
+
const typeNameOverride = match[1] ?? undefined;
|
|
55
|
+
// Parse positional primary constructor parameters
|
|
56
|
+
const positionalProperties = primaryCtorParams
|
|
57
|
+
? (0, parse_properties_2.parseRecordParameters)(primaryCtorParams)
|
|
58
|
+
: [];
|
|
59
|
+
// Also parse any body properties (records can have both)
|
|
60
|
+
const classBody = extractClassBody(afterAnnotation);
|
|
61
|
+
const bodyProperties = classBody
|
|
62
|
+
? (0, parse_properties_1.parseProperties)(classBody)
|
|
63
|
+
: [];
|
|
64
|
+
// Deduplicate: body props take priority if name clashes
|
|
65
|
+
const bodyPropNames = new Set(bodyProperties.map(p => p.name));
|
|
66
|
+
const mergedProperties = [
|
|
67
|
+
...positionalProperties.filter(p => !bodyPropNames.has(p.name)),
|
|
68
|
+
...bodyProperties,
|
|
69
|
+
];
|
|
70
|
+
classes.push({
|
|
71
|
+
name: typeNameOverride ?? className,
|
|
72
|
+
properties: mergedProperties,
|
|
73
|
+
inheritsFrom: resolvedInheritsFrom,
|
|
74
|
+
isEnum: false,
|
|
75
|
+
isRecord: true,
|
|
76
|
+
genericParameters,
|
|
77
|
+
baseClassGenerics: resolvedInheritsFrom ? baseClassGenerics : undefined,
|
|
78
|
+
});
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
// Regular class
|
|
82
|
+
const classMatch = afterAnnotation.match(/(?:\[[\w]+(?:\([^)]*\))?\]\s*)*public\s+class\s+(\w+)(?:<([^>]+)>)?(?:\s*:\s*(\w+)(?:<([^>]+)>)?)?/);
|
|
83
|
+
if (classMatch) {
|
|
84
|
+
const className = classMatch[1];
|
|
85
|
+
const genericParams = classMatch[2];
|
|
86
|
+
const inheritsFrom = classMatch[3];
|
|
87
|
+
const baseGenerics = classMatch[4];
|
|
88
|
+
const resolvedInheritsFrom = inheritsFrom && /^I[A-Z]/.test(inheritsFrom)
|
|
89
|
+
? undefined
|
|
90
|
+
: inheritsFrom;
|
|
91
|
+
const classBody = extractClassBody(afterAnnotation);
|
|
92
|
+
if (classBody) {
|
|
93
|
+
const { cleanedBody, injectedProperties } = stripNestedAnnotatedClasses(classBody, targetAnnotation);
|
|
94
|
+
const properties = [
|
|
95
|
+
...(0, parse_properties_1.parseProperties)(cleanedBody),
|
|
96
|
+
...injectedProperties
|
|
97
|
+
];
|
|
98
|
+
const genericParameters = genericParams
|
|
99
|
+
? genericParams.split(',').map(p => p.trim())
|
|
100
|
+
: undefined;
|
|
101
|
+
const baseClassGenerics = baseGenerics
|
|
102
|
+
? baseGenerics.split(',').map(p => p.trim())
|
|
103
|
+
: undefined;
|
|
104
|
+
const typeNameOverride = match[1] ?? undefined;
|
|
105
|
+
classes.push({
|
|
106
|
+
name: typeNameOverride ?? className,
|
|
107
|
+
properties,
|
|
108
|
+
inheritsFrom: resolvedInheritsFrom,
|
|
109
|
+
isEnum: false,
|
|
110
|
+
isRecord: false,
|
|
111
|
+
genericParameters,
|
|
112
|
+
baseClassGenerics: resolvedInheritsFrom ? baseClassGenerics : undefined
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return classes;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Parse enum from C# content
|
|
121
|
+
*/
|
|
122
|
+
function parseEnum(content, enumName) {
|
|
123
|
+
const enumBodyMatch = content.match(/enum\s+\w+\s*\{([^}]+)\}/);
|
|
124
|
+
if (!enumBodyMatch)
|
|
125
|
+
return null;
|
|
126
|
+
const enumBody = enumBodyMatch[1];
|
|
127
|
+
const enumValues = enumBody
|
|
128
|
+
.split(',')
|
|
129
|
+
.map(v => v.trim())
|
|
130
|
+
.filter(v => v.length > 0)
|
|
131
|
+
.map(v => v.split('=')[0].trim());
|
|
132
|
+
return {
|
|
133
|
+
name: enumName,
|
|
134
|
+
properties: [],
|
|
135
|
+
isEnum: true,
|
|
136
|
+
isRecord: false,
|
|
137
|
+
enumValues
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Extract class body between curly braces
|
|
142
|
+
*/
|
|
143
|
+
function extractClassBody(content) {
|
|
144
|
+
let braceCount = 0;
|
|
145
|
+
let startIndex = -1;
|
|
146
|
+
for (let i = 0; i < content.length; i++) {
|
|
147
|
+
if (content[i] === '{') {
|
|
148
|
+
if (braceCount === 0)
|
|
149
|
+
startIndex = i;
|
|
150
|
+
braceCount++;
|
|
151
|
+
}
|
|
152
|
+
else if (content[i] === '}') {
|
|
153
|
+
braceCount--;
|
|
154
|
+
if (braceCount === 0 && startIndex !== -1) {
|
|
155
|
+
return content.substring(startIndex + 1, i);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Extract the primary constructor parameter string from a record declaration,
|
|
163
|
+
* using paren-depth balancing so attribute parens like `[property: TypeAs("Date")]`
|
|
164
|
+
* don't cause early termination.
|
|
165
|
+
*
|
|
166
|
+
* Returns the raw content between the outermost `(` and its matching `)`,
|
|
167
|
+
* or undefined if no primary constructor is present (body-only record).
|
|
168
|
+
*
|
|
169
|
+
* The search starts after the class/record name so that attribute parens on
|
|
170
|
+
* the annotation itself are skipped.
|
|
171
|
+
*/
|
|
172
|
+
function extractPrimaryCtorParams(content) {
|
|
173
|
+
// Find the record name first so we skip annotation parens
|
|
174
|
+
const nameIdx = content.search(/\brecord\b/);
|
|
175
|
+
if (nameIdx === -1)
|
|
176
|
+
return undefined;
|
|
177
|
+
// Scan forward from the record keyword for the first `(` that isn't preceded
|
|
178
|
+
// by a `[` sequence (i.e. belongs to the ctor, not an attribute)
|
|
179
|
+
let i = nameIdx;
|
|
180
|
+
// Skip past the record keyword and name (and optional generics)
|
|
181
|
+
// by looking for the first `(` or `{` at the top paren-depth
|
|
182
|
+
let bracketDepth = 0; // tracks [] depth to ignore attr parens
|
|
183
|
+
while (i < content.length) {
|
|
184
|
+
const ch = content[i];
|
|
185
|
+
if (ch === '[') {
|
|
186
|
+
bracketDepth++;
|
|
187
|
+
i++;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (ch === ']') {
|
|
191
|
+
bracketDepth--;
|
|
192
|
+
i++;
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
// Only treat `(` as ctor open when we're not inside a `[...]`
|
|
196
|
+
if (ch === '(' && bracketDepth === 0) {
|
|
197
|
+
// Found the ctor opening paren — walk to the matching `)`
|
|
198
|
+
let depth = 0;
|
|
199
|
+
let start = i;
|
|
200
|
+
for (let j = i; j < content.length; j++) {
|
|
201
|
+
if (content[j] === '(')
|
|
202
|
+
depth++;
|
|
203
|
+
else if (content[j] === ')') {
|
|
204
|
+
depth--;
|
|
205
|
+
if (depth === 0) {
|
|
206
|
+
return content.slice(start + 1, j);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return undefined; // unmatched paren
|
|
211
|
+
}
|
|
212
|
+
// A `{` at bracket-depth 0 means we hit the body before a ctor — body-only record
|
|
213
|
+
if (ch === '{' && bracketDepth === 0)
|
|
214
|
+
return undefined;
|
|
215
|
+
i++;
|
|
216
|
+
}
|
|
217
|
+
return undefined;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Remove single-line and multi-line comments
|
|
221
|
+
*/
|
|
222
|
+
function removeComments(content) {
|
|
223
|
+
let result = content.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
224
|
+
result = result.replace(/\/\/.*/g, '');
|
|
225
|
+
return result;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Strip nested annotated classes from a class body and inject reference properties.
|
|
229
|
+
*/
|
|
230
|
+
function stripNestedAnnotatedClasses(classBody, targetAnnotation) {
|
|
231
|
+
const injectedProperties = [];
|
|
232
|
+
const regionsToRemove = [];
|
|
233
|
+
const annotationRegex = new RegExp(`\\[${targetAnnotation}(?:Attribute)?(?:\\(\\s*"([^"]*)"\\s*\\))?\\]\\s*public\\s+class\\s+(\\w+)`, 'g');
|
|
234
|
+
const matches = [...classBody.matchAll(annotationRegex)];
|
|
235
|
+
for (const match of matches) {
|
|
236
|
+
const typeNameOverride = match[1] ?? undefined;
|
|
237
|
+
const nestedClassName = match[2];
|
|
238
|
+
const resolvedName = typeNameOverride ?? nestedClassName;
|
|
239
|
+
const fromIndex = match.index;
|
|
240
|
+
const afterMatch = classBody.substring(fromIndex);
|
|
241
|
+
const braceStart = afterMatch.indexOf('{');
|
|
242
|
+
if (braceStart === -1)
|
|
243
|
+
continue;
|
|
244
|
+
let depth = 0;
|
|
245
|
+
let end = -1;
|
|
246
|
+
for (let i = braceStart; i < afterMatch.length; i++) {
|
|
247
|
+
if (afterMatch[i] === '{')
|
|
248
|
+
depth++;
|
|
249
|
+
else if (afterMatch[i] === '}') {
|
|
250
|
+
depth--;
|
|
251
|
+
if (depth === 0) {
|
|
252
|
+
end = i;
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (end === -1)
|
|
258
|
+
continue;
|
|
259
|
+
regionsToRemove.push({
|
|
260
|
+
start: fromIndex,
|
|
261
|
+
end: fromIndex + braceStart + end + 1
|
|
262
|
+
});
|
|
263
|
+
const propName = nestedClassName.charAt(0).toLowerCase() + nestedClassName.slice(1);
|
|
264
|
+
injectedProperties.push({
|
|
265
|
+
name: propName,
|
|
266
|
+
type: resolvedName,
|
|
267
|
+
isNullable: false,
|
|
268
|
+
isArray: false,
|
|
269
|
+
isGeneric: false,
|
|
270
|
+
isDeprecated: false,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
let cleanedBody = classBody;
|
|
274
|
+
for (const region of regionsToRemove.reverse()) {
|
|
275
|
+
cleanedBody = cleanedBody.substring(0, region.start) + cleanedBody.substring(region.end);
|
|
276
|
+
}
|
|
277
|
+
return { cleanedBody, injectedProperties };
|
|
278
|
+
}
|
|
279
|
+
//# sourceMappingURL=parse-classes-from-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-classes-from-file.js","sourceRoot":"","sources":["../../src/parser/parse-classes-from-file.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,4 +3,26 @@ import { CSharpProperty } from "../types";
|
|
|
3
3
|
* Parse properties from class body
|
|
4
4
|
*/
|
|
5
5
|
export declare function parseProperties(classBody: string): CSharpProperty[];
|
|
6
|
+
/**
|
|
7
|
+
* Parse positional primary constructor parameters from a C# record declaration.
|
|
8
|
+
*
|
|
9
|
+
* Handles:
|
|
10
|
+
* - Simple types: int Id
|
|
11
|
+
* - Nullable types: string? Name
|
|
12
|
+
* - Generic types: List<string> Tags
|
|
13
|
+
* - Nested generics: Dictionary<string, List<int>> Map
|
|
14
|
+
* - Per-parameter attributes with the `property:` target (required by C# for ctor params):
|
|
15
|
+
* [property: TypeIgnore]
|
|
16
|
+
* [property: TypeName("x")]
|
|
17
|
+
* [property: TypeAs("y")]
|
|
18
|
+
* [property: Obsolete("msg")]
|
|
19
|
+
* - [Obsolete] / [Obsolete("msg")]
|
|
20
|
+
*
|
|
21
|
+
* Note: [TypeAs], [TypeName], [TypeIgnore] on record primary constructor parameters
|
|
22
|
+
* MUST use the `property:` attribute target in C#:
|
|
23
|
+
* public record Foo([property: TypeAs("Date")] DateTime CreatedAt);
|
|
24
|
+
*
|
|
25
|
+
* @param raw - The raw text between the record's primary constructor parentheses
|
|
26
|
+
*/
|
|
27
|
+
export declare function parseRecordParameters(raw: string): CSharpProperty[];
|
|
6
28
|
//# sourceMappingURL=parse-properties.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-properties.d.ts","sourceRoot":"","sources":["../../src/parser/parse-properties.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAG1C;;GAEG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,EAAE,CAsDnE"}
|
|
1
|
+
{"version":3,"file":"parse-properties.d.ts","sourceRoot":"","sources":["../../src/parser/parse-properties.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAG1C;;GAEG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,EAAE,CAsDnE;AAGD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,EAAE,CA8DnE"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.parseProperties = parseProperties;
|
|
4
|
+
exports.parseRecordParameters = parseRecordParameters;
|
|
4
5
|
/**
|
|
5
6
|
* Parse properties from class body
|
|
6
7
|
*/
|
|
@@ -48,16 +49,189 @@ function parseProperties(classBody) {
|
|
|
48
49
|
}
|
|
49
50
|
return properties;
|
|
50
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Parse positional primary constructor parameters from a C# record declaration.
|
|
54
|
+
*
|
|
55
|
+
* Handles:
|
|
56
|
+
* - Simple types: int Id
|
|
57
|
+
* - Nullable types: string? Name
|
|
58
|
+
* - Generic types: List<string> Tags
|
|
59
|
+
* - Nested generics: Dictionary<string, List<int>> Map
|
|
60
|
+
* - Per-parameter attributes with the `property:` target (required by C# for ctor params):
|
|
61
|
+
* [property: TypeIgnore]
|
|
62
|
+
* [property: TypeName("x")]
|
|
63
|
+
* [property: TypeAs("y")]
|
|
64
|
+
* [property: Obsolete("msg")]
|
|
65
|
+
* - [Obsolete] / [Obsolete("msg")]
|
|
66
|
+
*
|
|
67
|
+
* Note: [TypeAs], [TypeName], [TypeIgnore] on record primary constructor parameters
|
|
68
|
+
* MUST use the `property:` attribute target in C#:
|
|
69
|
+
* public record Foo([property: TypeAs("Date")] DateTime CreatedAt);
|
|
70
|
+
*
|
|
71
|
+
* @param raw - The raw text between the record's primary constructor parentheses
|
|
72
|
+
*/
|
|
73
|
+
function parseRecordParameters(raw) {
|
|
74
|
+
const properties = [];
|
|
75
|
+
// Split on top-level commas (skip commas inside angle brackets or square brackets)
|
|
76
|
+
const params = splitTopLevelParams(raw);
|
|
77
|
+
for (const param of params) {
|
|
78
|
+
const trimmed = param.trim();
|
|
79
|
+
if (!trimmed)
|
|
80
|
+
continue;
|
|
81
|
+
// Extract all [Attr] tokens from the front of the param string,
|
|
82
|
+
// then treat the remainder as "type name".
|
|
83
|
+
// This handles both inline attrs ([TypeAs("x")] DateTime CreatedAt)
|
|
84
|
+
// and multiline attrs (separate lines with \n between them).
|
|
85
|
+
const { attrTokens, remainder } = extractLeadingAttributes(trimmed);
|
|
86
|
+
const typeAndName = remainder.trim();
|
|
87
|
+
if (!typeAndName)
|
|
88
|
+
continue;
|
|
89
|
+
// Check [TypeIgnore] — supports [property: TypeIgnore]
|
|
90
|
+
const ignore = attrTokens.some(l => /\[\s*(?:property\s*:\s*)?TypeIgnore\]/.test(l));
|
|
91
|
+
if (ignore)
|
|
92
|
+
continue;
|
|
93
|
+
// Check [TypeName("x")] — supports [property: TypeName("x")]
|
|
94
|
+
let overrideName;
|
|
95
|
+
for (const l of attrTokens) {
|
|
96
|
+
const m = l.match(/\[\s*(?:property\s*:\s*)?TypeName\s*\(\s*"([^"]+)"\s*\)\]/);
|
|
97
|
+
if (m) {
|
|
98
|
+
overrideName = m[1];
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Check [TypeAs("y")] — supports [property: TypeAs("y")]
|
|
103
|
+
let overrideType;
|
|
104
|
+
for (const l of attrTokens) {
|
|
105
|
+
const m = l.match(/\[\s*(?:property\s*:\s*)?TypeAs\s*\(\s*"([^"]+)"\s*\)\]/);
|
|
106
|
+
if (m) {
|
|
107
|
+
overrideType = m[1];
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Check [Obsolete] / [Obsolete("message")] — supports [property: Obsolete("msg")]
|
|
112
|
+
let isDeprecated = false;
|
|
113
|
+
let deprecationMessage;
|
|
114
|
+
for (const l of attrTokens) {
|
|
115
|
+
const m = l.match(/\[\s*(?:property\s*:\s*)?Obsolete(?:Attribute)?\s*(?:\(\s*"([^"]*)"\s*(?:,\s*(?:true|false))?\s*\))?\]/i);
|
|
116
|
+
if (m) {
|
|
117
|
+
isDeprecated = true;
|
|
118
|
+
deprecationMessage = m[1] ?? undefined;
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// Parse "TypeToken NameToken" — handle nested generics via angle-bracket depth
|
|
123
|
+
const parsed = parseTypeAndName(typeAndName);
|
|
124
|
+
if (!parsed)
|
|
125
|
+
continue;
|
|
126
|
+
const { csType, name } = parsed;
|
|
127
|
+
const resolvedType = overrideType ?? csType;
|
|
128
|
+
const resolvedName = overrideName ?? name;
|
|
129
|
+
const prop = parsePropertyType(resolvedName, resolvedType);
|
|
130
|
+
properties.push({ ...prop, isDeprecated, deprecationMessage });
|
|
131
|
+
}
|
|
132
|
+
return properties;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Walk a parameter string and peel off every leading [Attr] / [Attr("...")] token,
|
|
136
|
+
* returning them as an array plus the remaining "type name" tail.
|
|
137
|
+
*
|
|
138
|
+
* Works for both inline attrs → "[TypeAs("Date")] DateTime CreatedAt"
|
|
139
|
+
* and multiline attrs → "[Obsolete]\n string OldId"
|
|
140
|
+
*/
|
|
141
|
+
function extractLeadingAttributes(s) {
|
|
142
|
+
const attrTokens = [];
|
|
143
|
+
let i = 0;
|
|
144
|
+
// Skip leading whitespace / newlines
|
|
145
|
+
while (i < s.length && /\s/.test(s[i]))
|
|
146
|
+
i++;
|
|
147
|
+
while (i < s.length && s[i] === '[') {
|
|
148
|
+
// Find the matching ']', respecting nested brackets (e.g. [Obsolete("msg")])
|
|
149
|
+
let depth = 0;
|
|
150
|
+
let end = i;
|
|
151
|
+
for (let j = i; j < s.length; j++) {
|
|
152
|
+
if (s[j] === '[')
|
|
153
|
+
depth++;
|
|
154
|
+
else if (s[j] === ']') {
|
|
155
|
+
depth--;
|
|
156
|
+
if (depth === 0) {
|
|
157
|
+
end = j;
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
attrTokens.push(s.slice(i, end + 1).trim());
|
|
163
|
+
i = end + 1;
|
|
164
|
+
// Skip whitespace/newlines between attributes or before the type token
|
|
165
|
+
while (i < s.length && /\s/.test(s[i]))
|
|
166
|
+
i++;
|
|
167
|
+
}
|
|
168
|
+
return { attrTokens, remainder: s.slice(i) };
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Split a raw parameter string on top-level commas
|
|
172
|
+
* (i.e. commas not inside <> or [])
|
|
173
|
+
*/
|
|
174
|
+
function splitTopLevelParams(raw) {
|
|
175
|
+
const parts = [];
|
|
176
|
+
let depth = 0;
|
|
177
|
+
let buf = '';
|
|
178
|
+
for (let i = 0; i < raw.length; i++) {
|
|
179
|
+
const ch = raw[i];
|
|
180
|
+
if (ch === '<' || ch === '[') {
|
|
181
|
+
depth++;
|
|
182
|
+
buf += ch;
|
|
183
|
+
}
|
|
184
|
+
else if (ch === '>' || ch === ']') {
|
|
185
|
+
depth--;
|
|
186
|
+
buf += ch;
|
|
187
|
+
}
|
|
188
|
+
else if (ch === ',' && depth === 0) {
|
|
189
|
+
parts.push(buf);
|
|
190
|
+
buf = '';
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
buf += ch;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (buf.trim())
|
|
197
|
+
parts.push(buf);
|
|
198
|
+
return parts;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Parse a "TypeToken NameToken" string where TypeToken may contain generics.
|
|
202
|
+
* Returns null if the string doesn't look like a valid type+name pair.
|
|
203
|
+
*/
|
|
204
|
+
function parseTypeAndName(s) {
|
|
205
|
+
s = s.trim();
|
|
206
|
+
// Walk the string tracking angle-bracket depth to find the boundary
|
|
207
|
+
// between the type token and the name token
|
|
208
|
+
let depth = 0;
|
|
209
|
+
let splitAt = -1;
|
|
210
|
+
// Find the last space at depth 0 — that separates type from name
|
|
211
|
+
for (let i = 0; i < s.length; i++) {
|
|
212
|
+
if (s[i] === '<')
|
|
213
|
+
depth++;
|
|
214
|
+
else if (s[i] === '>')
|
|
215
|
+
depth--;
|
|
216
|
+
else if (s[i] === ' ' && depth === 0) {
|
|
217
|
+
splitAt = i;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (splitAt === -1)
|
|
221
|
+
return null;
|
|
222
|
+
const csType = s.slice(0, splitAt).trim();
|
|
223
|
+
const name = s.slice(splitAt + 1).trim();
|
|
224
|
+
if (!csType || !name)
|
|
225
|
+
return null;
|
|
226
|
+
return { csType, name };
|
|
227
|
+
}
|
|
51
228
|
function extractObsoleteInfo(classBody, matchIndex) {
|
|
52
229
|
const before = classBody.substring(0, matchIndex);
|
|
53
230
|
const lines = before.split('\n');
|
|
54
|
-
// Walk backwards, only through attribute lines and whitespace
|
|
55
|
-
// Stop as soon as we hit a line that isn't an attribute or blank
|
|
56
231
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
57
232
|
const line = lines[i].trim();
|
|
58
233
|
if (line === '')
|
|
59
234
|
continue;
|
|
60
|
-
// If it's an attribute line, check for Obsolete
|
|
61
235
|
if (line.startsWith('[')) {
|
|
62
236
|
const obsoleteMatch = line.match(/\[Obsolete(?:Attribute)?\s*(?:\(\s*"([^"]*)"\s*(?:,\s*(?:true|false))?\s*\))?\]/i);
|
|
63
237
|
if (obsoleteMatch) {
|
|
@@ -66,10 +240,8 @@ function extractObsoleteInfo(classBody, matchIndex) {
|
|
|
66
240
|
deprecationMessage: obsoleteMatch[1] ?? undefined
|
|
67
241
|
};
|
|
68
242
|
}
|
|
69
|
-
// It's a different attribute — keep walking back (stacked attributes)
|
|
70
243
|
continue;
|
|
71
244
|
}
|
|
72
|
-
// Hit a non-attribute, non-blank line — stop looking
|
|
73
245
|
break;
|
|
74
246
|
}
|
|
75
247
|
return { isDeprecated: false };
|
|
@@ -100,25 +272,18 @@ function extractTypeSharpAttributeInfo(classBody, matchIndex) {
|
|
|
100
272
|
function parsePropertyType(name, csType) {
|
|
101
273
|
let raw = csType.trim();
|
|
102
274
|
let isNullable = false;
|
|
103
|
-
// Extract nullable marker (T?)
|
|
104
275
|
if (raw.endsWith('?')) {
|
|
105
276
|
isNullable = true;
|
|
106
277
|
raw = raw.slice(0, -1).trim();
|
|
107
278
|
}
|
|
108
|
-
// Recursive resolver: returns { tsType, isArray }
|
|
109
279
|
function resolveType(typeText) {
|
|
110
280
|
const t = typeText.trim();
|
|
111
|
-
// 1) Array syntax: T[]
|
|
112
281
|
if (t.endsWith('[]')) {
|
|
113
282
|
const inner = t.slice(0, -2).trim();
|
|
114
283
|
const resolved = resolveType(inner);
|
|
115
|
-
// If inner is already an array, keep it as array-of-array semantics;
|
|
116
|
-
// mark as array so generator will append [] (or use resolved.tsType which may include [] already)
|
|
117
284
|
return { tsType: resolved.tsType, isArray: true };
|
|
118
285
|
}
|
|
119
|
-
// 2) Dictionary-like types (handle Dictionary, IDictionary, IReadOnlyDictionary)
|
|
120
286
|
if (/^(?:[\w\.]+\.)?(?:Dictionary|IDictionary|IReadOnlyDictionary)\s*</.test(t)) {
|
|
121
|
-
// locate first '<' and its matching '>'
|
|
122
287
|
const firstAngle = t.indexOf('<');
|
|
123
288
|
const lastAngle = findMatchingAngleBracket(t, firstAngle);
|
|
124
289
|
if (firstAngle !== -1 && lastAngle !== -1) {
|
|
@@ -127,36 +292,26 @@ function parsePropertyType(name, csType) {
|
|
|
127
292
|
if (args.length === 2) {
|
|
128
293
|
const keyCs = args[0];
|
|
129
294
|
const valueCs = args[1];
|
|
130
|
-
// Guard against undefined/null/empty parts before resolving
|
|
131
295
|
if (typeof keyCs !== 'string' || typeof valueCs !== 'string' || keyCs.trim() === '' || valueCs.trim() === '') {
|
|
132
|
-
// Fallback: avoid throwing — emit a generic record
|
|
133
296
|
return { tsType: 'Record<string, any>', isArray: false };
|
|
134
297
|
}
|
|
135
298
|
const resolvedKey = resolveType(keyCs);
|
|
136
299
|
const resolvedValue = resolveType(valueCs);
|
|
137
|
-
// Normalize key to a safe TS key type: Record<K extends keyof any, V>
|
|
138
|
-
// If key is not primitive 'string' or 'number' or 'symbol', coerce to 'string'
|
|
139
300
|
const keyTsRaw = resolvedKey.tsType;
|
|
140
301
|
const safeKey = keyTsRaw === 'string' || keyTsRaw === 'number' || keyTsRaw === 'symbol'
|
|
141
302
|
? keyTsRaw
|
|
142
303
|
: 'string';
|
|
143
|
-
// Build value type (respect nested arrays)
|
|
144
304
|
const valueType = resolvedValue.isArray ? `${resolvedValue.tsType}[]` : resolvedValue.tsType;
|
|
145
305
|
return { tsType: `Record<${safeKey}, ${valueType}>`, isArray: false };
|
|
146
306
|
}
|
|
147
|
-
// else: fallthrough to other checks (malformed or >2 args) — do not falsely match
|
|
148
307
|
}
|
|
149
308
|
}
|
|
150
|
-
// 3) Collections: List<T>, IEnumerable<T>, ICollection<T>, IList<T>
|
|
151
309
|
const collectionMatch = t.match(/^(?:List|IEnumerable|ICollection|IList)\s*<\s*(.+)\s*>$/);
|
|
152
310
|
if (collectionMatch) {
|
|
153
311
|
const inner = collectionMatch[1].trim();
|
|
154
312
|
const resolvedInner = resolveType(inner);
|
|
155
|
-
// Collections become inner[] in TS; mark isArray true and tsType = resolvedInner.tsType
|
|
156
|
-
// The generator will append [].
|
|
157
313
|
return { tsType: resolvedInner.tsType, isArray: true };
|
|
158
314
|
}
|
|
159
|
-
// 4) Fallback: map primitive / known types, else return as-is (class name)
|
|
160
315
|
return { tsType: mapCSharpTypeToTypeScript(t), isArray: false };
|
|
161
316
|
}
|
|
162
317
|
const resolved = resolveType(raw);
|
|
@@ -171,10 +326,6 @@ function parsePropertyType(name, csType) {
|
|
|
171
326
|
deprecationMessage: undefined,
|
|
172
327
|
};
|
|
173
328
|
}
|
|
174
|
-
/**
|
|
175
|
-
* Find the index of the matching '>' for the first '<' at startIdx.
|
|
176
|
-
* Returns -1 if not found.
|
|
177
|
-
*/
|
|
178
329
|
function findMatchingAngleBracket(s, startIdx) {
|
|
179
330
|
let depth = 0;
|
|
180
331
|
for (let i = startIdx; i < s.length; i++) {
|
|
@@ -188,11 +339,6 @@ function findMatchingAngleBracket(s, startIdx) {
|
|
|
188
339
|
}
|
|
189
340
|
return -1;
|
|
190
341
|
}
|
|
191
|
-
/**
|
|
192
|
-
* Split a comma-separated generic-argument string into top-level args,
|
|
193
|
-
* i.e. respects nested <...> and does NOT split commas inside nested generics.
|
|
194
|
-
* Example: "string, List<Dictionary<string, Foo>>, int" -> ["string", "List<Dictionary<string, Foo>>", "int"]
|
|
195
|
-
*/
|
|
196
342
|
function splitTopLevelGenericArgs(s) {
|
|
197
343
|
const parts = [];
|
|
198
344
|
let depth = 0;
|
|
@@ -219,9 +365,6 @@ function splitTopLevelGenericArgs(s) {
|
|
|
219
365
|
parts.push(buf.trim());
|
|
220
366
|
return parts;
|
|
221
367
|
}
|
|
222
|
-
/**
|
|
223
|
-
* Map C# primitive types to TypeScript types
|
|
224
|
-
*/
|
|
225
368
|
function mapCSharpTypeToTypeScript(csType) {
|
|
226
369
|
const typeMap = {
|
|
227
370
|
'bool': 'boolean',
|