@contractkit/plugin-csharp 0.1.1 → 0.1.3
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/.turbo/turbo-build$colon$ci.log +4 -4
- package/.turbo/turbo-test$colon$ci.log +13 -13
- package/CHANGELOG.md +23 -0
- package/dist/codegen-models.d.ts.map +1 -1
- package/dist/index.js +223 -485
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/codegen-models.ts +34 -8
- package/tests/codegen-models.test.ts +15 -0
package/dist/index.js
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
-
|
|
4
1
|
// src/index.ts
|
|
5
2
|
import { dirname, join, resolve } from "path";
|
|
6
3
|
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, rmdirSync, writeFileSync } from "fs";
|
|
7
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
buildModelIndex as buildModelIndex2,
|
|
6
|
+
collectTransitiveModelRefs,
|
|
7
|
+
collectTypeRefs as collectTypeRefs2,
|
|
8
|
+
emptyIncrementalManifest,
|
|
9
|
+
hashFingerprint,
|
|
10
|
+
parseIncrementalManifest,
|
|
11
|
+
runIncrementalCodegen,
|
|
12
|
+
serializeIncrementalManifest
|
|
13
|
+
} from "@contractkit/core";
|
|
8
14
|
|
|
9
15
|
// src/codegen-models.ts
|
|
10
16
|
import { buildModelIndex, computeModelsWithInput, resolveEffectiveFields, topoSortModels } from "@contractkit/core";
|
|
@@ -89,18 +95,10 @@ var CSHARP_KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
89
95
|
"volatile",
|
|
90
96
|
"while"
|
|
91
97
|
]);
|
|
92
|
-
var RESERVED_MEMBER_NAMES = /* @__PURE__ */ new Set([
|
|
93
|
-
"Equals",
|
|
94
|
-
"GetHashCode",
|
|
95
|
-
"GetType",
|
|
96
|
-
"ToString",
|
|
97
|
-
"EqualityContract",
|
|
98
|
-
"PrintMembers"
|
|
99
|
-
]);
|
|
98
|
+
var RESERVED_MEMBER_NAMES = /* @__PURE__ */ new Set(["Equals", "GetHashCode", "GetType", "ToString", "EqualityContract", "PrintMembers"]);
|
|
100
99
|
function escapeCSharpIdentifier(name) {
|
|
101
100
|
return CSHARP_KEYWORDS.has(name) ? `@${name}` : name;
|
|
102
101
|
}
|
|
103
|
-
__name(escapeCSharpIdentifier, "escapeCSharpIdentifier");
|
|
104
102
|
function toCSharpPropertyName(name) {
|
|
105
103
|
const words = splitWords(name);
|
|
106
104
|
if (words.length === 0) return "_";
|
|
@@ -108,7 +106,6 @@ function toCSharpPropertyName(name) {
|
|
|
108
106
|
if (/^\d/.test(result)) result = `_${result}`;
|
|
109
107
|
return result;
|
|
110
108
|
}
|
|
111
|
-
__name(toCSharpPropertyName, "toCSharpPropertyName");
|
|
112
109
|
function toCSharpParameterName(name) {
|
|
113
110
|
const words = splitWords(name);
|
|
114
111
|
if (words.length === 0) return "_";
|
|
@@ -118,12 +115,10 @@ function toCSharpParameterName(name) {
|
|
|
118
115
|
if (/^\d/.test(result)) result = `_${result}`;
|
|
119
116
|
return escapeCSharpIdentifier(result);
|
|
120
117
|
}
|
|
121
|
-
__name(toCSharpParameterName, "toCSharpParameterName");
|
|
122
118
|
function safeMemberName(propertyName, ownerTypeName) {
|
|
123
119
|
if (propertyName === ownerTypeName || RESERVED_MEMBER_NAMES.has(propertyName)) return `${propertyName}Value`;
|
|
124
120
|
return propertyName;
|
|
125
121
|
}
|
|
126
|
-
__name(safeMemberName, "safeMemberName");
|
|
127
122
|
function toCSharpTypeName(name) {
|
|
128
123
|
const words = splitWords(name);
|
|
129
124
|
if (words.length === 0) return "_";
|
|
@@ -131,7 +126,6 @@ function toCSharpTypeName(name) {
|
|
|
131
126
|
if (/^\d/.test(result)) result = `_${result}`;
|
|
132
127
|
return result;
|
|
133
128
|
}
|
|
134
|
-
__name(toCSharpTypeName, "toCSharpTypeName");
|
|
135
129
|
function sanitizeCSharpTypeName(name) {
|
|
136
130
|
let result = name.replace(/[^a-zA-Z0-9]/g, "");
|
|
137
131
|
if (result.length === 0) return "_";
|
|
@@ -139,7 +133,6 @@ function sanitizeCSharpTypeName(name) {
|
|
|
139
133
|
if (/^\d/.test(result)) result = `_${result}`;
|
|
140
134
|
return result;
|
|
141
135
|
}
|
|
142
|
-
__name(sanitizeCSharpTypeName, "sanitizeCSharpTypeName");
|
|
143
136
|
function toCSharpEnumMemberName(value) {
|
|
144
137
|
const words = splitWords(value);
|
|
145
138
|
if (words.length === 0) return "_";
|
|
@@ -147,43 +140,30 @@ function toCSharpEnumMemberName(value) {
|
|
|
147
140
|
if (/^\d/.test(result)) result = `_${result}`;
|
|
148
141
|
return result;
|
|
149
142
|
}
|
|
150
|
-
__name(toCSharpEnumMemberName, "toCSharpEnumMemberName");
|
|
151
143
|
function deriveCSharpFileBase(file) {
|
|
152
144
|
const base = file.split("/").pop()?.replace(/\.(op\.)?ck$/, "") ?? "models";
|
|
153
145
|
return toCSharpTypeName(base);
|
|
154
146
|
}
|
|
155
|
-
__name(deriveCSharpFileBase, "deriveCSharpFileBase");
|
|
156
147
|
function xmlDocLines(text, indent, tag = "summary") {
|
|
157
148
|
if (text.length === 0) return [];
|
|
158
149
|
const safe = escapeXml(text);
|
|
159
150
|
const sourceLines = safe.split("\n");
|
|
160
|
-
if (sourceLines.length === 1) return [
|
|
161
|
-
|
|
162
|
-
];
|
|
163
|
-
return [
|
|
164
|
-
`${indent}/// <${tag}>`,
|
|
165
|
-
...sourceLines.map((line) => `${indent}/// ${line}`.trimEnd()),
|
|
166
|
-
`${indent}/// </${tag}>`
|
|
167
|
-
];
|
|
151
|
+
if (sourceLines.length === 1) return [`${indent}/// <${tag}>${sourceLines[0]}</${tag}>`];
|
|
152
|
+
return [`${indent}/// <${tag}>`, ...sourceLines.map((line) => `${indent}/// ${line}`.trimEnd()), `${indent}/// </${tag}>`];
|
|
168
153
|
}
|
|
169
|
-
__name(xmlDocLines, "xmlDocLines");
|
|
170
154
|
function escapeXml(text) {
|
|
171
155
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
172
156
|
}
|
|
173
|
-
__name(escapeXml, "escapeXml");
|
|
174
157
|
function quoteCSharpString(value) {
|
|
175
158
|
const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t").replace(/\0/g, "\\0");
|
|
176
159
|
return `"${escaped}"`;
|
|
177
160
|
}
|
|
178
|
-
__name(quoteCSharpString, "quoteCSharpString");
|
|
179
161
|
function splitWords(name) {
|
|
180
162
|
return name.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").split(/[^a-zA-Z0-9]+/).filter(Boolean);
|
|
181
163
|
}
|
|
182
|
-
__name(splitWords, "splitWords");
|
|
183
164
|
function capitalize(word) {
|
|
184
165
|
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
|
185
166
|
}
|
|
186
|
-
__name(capitalize, "capitalize");
|
|
187
167
|
|
|
188
168
|
// src/codegen-models.ts
|
|
189
169
|
var MODEL_USINGS = [
|
|
@@ -205,27 +185,18 @@ function generateCSharpModels(root, opts) {
|
|
|
205
185
|
warn: opts.warn
|
|
206
186
|
};
|
|
207
187
|
const bodies = [];
|
|
208
|
-
const append =
|
|
188
|
+
const append = (lines) => {
|
|
209
189
|
if (lines.length === 0) return;
|
|
210
190
|
bodies.push("", ...lines);
|
|
211
|
-
}
|
|
191
|
+
};
|
|
212
192
|
for (const model of topoSortModels(root.models)) append(generateModel(model, ctx));
|
|
213
193
|
for (const decl of opts.hoisted?.byFile.get(root.file) ?? []) append(generateHoisted(decl, ctx));
|
|
214
|
-
return renderFile(`${opts.namespace}.Models`, ctx.globalAliases, [
|
|
215
|
-
...MODEL_USINGS
|
|
216
|
-
], bodies);
|
|
194
|
+
return renderFile(`${opts.namespace}.Models`, ctx.globalAliases, [...MODEL_USINGS], bodies);
|
|
217
195
|
}
|
|
218
|
-
__name(generateCSharpModels, "generateCSharpModels");
|
|
219
196
|
function resolveModelsWithInput(models, external = /* @__PURE__ */ new Set()) {
|
|
220
197
|
const seed = new Set(external);
|
|
221
|
-
return /* @__PURE__ */ new Set([
|
|
222
|
-
|
|
223
|
-
...computeModelsWithInput([
|
|
224
|
-
...models
|
|
225
|
-
], seed)
|
|
226
|
-
]);
|
|
227
|
-
}
|
|
228
|
-
__name(resolveModelsWithInput, "resolveModelsWithInput");
|
|
198
|
+
return /* @__PURE__ */ new Set([...seed, ...computeModelsWithInput([...models], seed)]);
|
|
199
|
+
}
|
|
229
200
|
function createRenderContext(opts) {
|
|
230
201
|
return {
|
|
231
202
|
namespace: opts.namespace,
|
|
@@ -236,18 +207,10 @@ function createRenderContext(opts) {
|
|
|
236
207
|
warn: opts.warn
|
|
237
208
|
};
|
|
238
209
|
}
|
|
239
|
-
__name(createRenderContext, "createRenderContext");
|
|
240
210
|
function renderFile(namespaceName, globalAliases, usings, bodies) {
|
|
241
|
-
const lines = [
|
|
242
|
-
"// <auto-generated/>",
|
|
243
|
-
"// Generated by @contractkit/plugin-csharp. Do not edit manually.",
|
|
244
|
-
"#nullable enable",
|
|
245
|
-
""
|
|
246
|
-
];
|
|
211
|
+
const lines = ["// <auto-generated/>", "// Generated by @contractkit/plugin-csharp. Do not edit manually.", "#nullable enable", ""];
|
|
247
212
|
if (globalAliases.length > 0) {
|
|
248
|
-
lines.push(...[
|
|
249
|
-
...globalAliases
|
|
250
|
-
].sort());
|
|
213
|
+
lines.push(...[...globalAliases].sort());
|
|
251
214
|
lines.push("");
|
|
252
215
|
}
|
|
253
216
|
lines.push(...usings);
|
|
@@ -257,7 +220,6 @@ function renderFile(namespaceName, globalAliases, usings, bodies) {
|
|
|
257
220
|
lines.push("");
|
|
258
221
|
return lines.join("\n");
|
|
259
222
|
}
|
|
260
|
-
__name(renderFile, "renderFile");
|
|
261
223
|
function renderCSharpType(type, ctx, forInput = false) {
|
|
262
224
|
const decl = ctx.hoisted?.byNode.get(type);
|
|
263
225
|
if (decl) return hoistedTypeName(decl, ctx, forInput);
|
|
@@ -273,7 +235,9 @@ function renderCSharpType(type, ctx, forInput = false) {
|
|
|
273
235
|
const value = renderCSharpType(type.value, ctx, forInput);
|
|
274
236
|
const stringType = qualify("string", "System.String", ctx);
|
|
275
237
|
if (key !== stringType) {
|
|
276
|
-
ctx.warn?.(
|
|
238
|
+
ctx.warn?.(
|
|
239
|
+
`A record key of type '${key}' is not representable as a JSON object key; emitting Dictionary<string, ${value}>. Parse the key yourself, or declare the key as a string.`
|
|
240
|
+
);
|
|
277
241
|
}
|
|
278
242
|
return `${qualify("Dictionary", "System.Collections.Generic.Dictionary", ctx)}<${stringType}, ${value}>`;
|
|
279
243
|
}
|
|
@@ -302,25 +266,20 @@ function renderCSharpType(type, ctx, forInput = false) {
|
|
|
302
266
|
return jsonElement(ctx);
|
|
303
267
|
}
|
|
304
268
|
}
|
|
305
|
-
__name(renderCSharpType, "renderCSharpType");
|
|
306
269
|
function hoistedTypeName(decl, ctx, forInput) {
|
|
307
270
|
const bare = forInput && decl.needsInput ? `${decl.name}Input` : decl.name;
|
|
308
271
|
const name = ctx.qualify ? `${ctx.namespace}.Models.${bare}` : bare;
|
|
309
272
|
return decl.nullable ? `${name}?` : name;
|
|
310
273
|
}
|
|
311
|
-
__name(hoistedTypeName, "hoistedTypeName");
|
|
312
274
|
function qualify(short, full, ctx) {
|
|
313
275
|
return ctx.qualify ? full : short;
|
|
314
276
|
}
|
|
315
|
-
__name(qualify, "qualify");
|
|
316
277
|
function jsonElement(ctx) {
|
|
317
278
|
return qualify("JsonElement", "System.Text.Json.JsonElement", ctx);
|
|
318
279
|
}
|
|
319
|
-
__name(jsonElement, "jsonElement");
|
|
320
280
|
function isNullScalar(type) {
|
|
321
281
|
return type.kind === "scalar" && type.name === "null";
|
|
322
282
|
}
|
|
323
|
-
__name(isNullScalar, "isNullScalar");
|
|
324
283
|
function renderScalar(name, ctx) {
|
|
325
284
|
switch (name) {
|
|
326
285
|
case "string":
|
|
@@ -365,14 +324,13 @@ function renderScalar(name, ctx) {
|
|
|
365
324
|
}
|
|
366
325
|
}
|
|
367
326
|
}
|
|
368
|
-
__name(renderScalar, "renderScalar");
|
|
369
327
|
function literalCSharpType(value, ctx) {
|
|
370
328
|
if (typeof value === "string") return qualify("string", "System.String", ctx);
|
|
371
329
|
if (typeof value === "boolean") return qualify("bool", "System.Boolean", ctx);
|
|
372
330
|
return Number.isInteger(value) ? qualify("long", "System.Int64", ctx) : qualify("double", "System.Double", ctx);
|
|
373
331
|
}
|
|
374
|
-
|
|
375
|
-
|
|
332
|
+
function renderDefault(value, type, ctx, memberNames = /* @__PURE__ */ new Set()) {
|
|
333
|
+
const enumType = (name) => memberNames.has(name) ? `global::${ctx.namespace}.Models.${name}` : name;
|
|
376
334
|
const inner = type.kind === "lazy" ? type.inner : type;
|
|
377
335
|
if (typeof value === "boolean") return String(value);
|
|
378
336
|
if (typeof value === "number") {
|
|
@@ -393,13 +351,13 @@ function renderDefault(value, type, ctx) {
|
|
|
393
351
|
if (inner.kind === "enum") {
|
|
394
352
|
const decl = ctx.hoisted?.byNode.get(inner);
|
|
395
353
|
if (!decl || !inner.values.includes(value)) return void 0;
|
|
396
|
-
return `${decl.name}.${enumMemberNames(inner.values).get(value)}`;
|
|
354
|
+
return `${enumType(decl.name)}.${enumMemberNames(inner.values).get(value)}`;
|
|
397
355
|
}
|
|
398
356
|
if (inner.kind === "ref") {
|
|
399
357
|
const target = ctx.modelIndex.get(inner.name);
|
|
400
358
|
const targetType = target?.type?.kind === "lazy" ? target.type.inner : target?.type;
|
|
401
359
|
if (targetType?.kind !== "enum" || !targetType.values.includes(value)) return void 0;
|
|
402
|
-
return `${inner.name}.${enumMemberNames(targetType.values).get(value)}`;
|
|
360
|
+
return `${enumType(inner.name)}.${enumMemberNames(targetType.values).get(value)}`;
|
|
403
361
|
}
|
|
404
362
|
if (inner.kind === "scalar") {
|
|
405
363
|
switch (inner.name) {
|
|
@@ -418,28 +376,26 @@ function renderDefault(value, type, ctx) {
|
|
|
418
376
|
}
|
|
419
377
|
return quoteCSharpString(value);
|
|
420
378
|
}
|
|
421
|
-
__name(renderDefault, "renderDefault");
|
|
422
379
|
function applyWireCase(name, wireCase) {
|
|
423
380
|
if (!wireCase || wireCase === "camel") return name;
|
|
424
381
|
if (wireCase === "snake") return name.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);
|
|
425
382
|
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
426
383
|
}
|
|
427
|
-
__name(applyWireCase, "applyWireCase");
|
|
428
384
|
function renamingCase(wireCase) {
|
|
429
385
|
return wireCase && wireCase !== "camel" ? wireCase : void 0;
|
|
430
386
|
}
|
|
431
|
-
__name(renamingCase, "renamingCase");
|
|
432
387
|
function wireCaseFor(model, forInput, split, ctx) {
|
|
433
388
|
const input = renamingCase(model.inputCase);
|
|
434
389
|
const output = renamingCase(model.outputCase);
|
|
435
390
|
if (split) return forInput ? input : output;
|
|
436
391
|
if (input && output && input !== output) {
|
|
437
|
-
ctx.warn?.(
|
|
392
|
+
ctx.warn?.(
|
|
393
|
+
`Contract '${model.name}' sets format(input=${input}) and format(output=${output}), but nothing about it splits into an Input variant, so one C# record carries both directions and can only spell one set of keys. The generated keys follow the output casing; a request built from this record will send the wrong ones.`
|
|
394
|
+
);
|
|
438
395
|
return output;
|
|
439
396
|
}
|
|
440
397
|
return output ?? input;
|
|
441
398
|
}
|
|
442
|
-
__name(wireCaseFor, "wireCaseFor");
|
|
443
399
|
function containsInlineObject(type) {
|
|
444
400
|
if (!type) return false;
|
|
445
401
|
switch (type.kind) {
|
|
@@ -461,13 +417,13 @@ function containsInlineObject(type) {
|
|
|
461
417
|
return false;
|
|
462
418
|
}
|
|
463
419
|
}
|
|
464
|
-
__name(containsInlineObject, "containsInlineObject");
|
|
465
420
|
function warnUncasedNesting(model, fields, wireCase, ctx) {
|
|
466
421
|
if (!wireCase) return;
|
|
467
422
|
if (!fields.some((f) => containsInlineObject(f.type)) && !containsInlineObject(model.type)) return;
|
|
468
|
-
ctx.warn?.(
|
|
423
|
+
ctx.warn?.(
|
|
424
|
+
`Contract '${model.name}' is declared format(${model.outputCase ? "output" : "input"}=${wireCase}) and holds an anonymous object. The record hoisted out of that object keeps its declared key names, so its keys will not be ${wireCase}-cased. Name the shape as its own contract to fix it.`
|
|
425
|
+
);
|
|
469
426
|
}
|
|
470
|
-
__name(warnUncasedNesting, "warnUncasedNesting");
|
|
471
427
|
function generateModel(model, ctx) {
|
|
472
428
|
if (model.type) return generateAliasModel(model, ctx);
|
|
473
429
|
const effective = effectiveFieldsFor(model, ctx);
|
|
@@ -481,7 +437,6 @@ function generateModel(model, ctx) {
|
|
|
481
437
|
...generateRecordForModel(`${model.name}Input`, inputFields, ctx, true, model, true)
|
|
482
438
|
];
|
|
483
439
|
}
|
|
484
|
-
__name(generateModel, "generateModel");
|
|
485
440
|
function effectiveFieldsFor(model, ctx) {
|
|
486
441
|
if (!model.bases || model.bases.length === 0) return model.fields;
|
|
487
442
|
const { fields, unresolved } = resolveEffectiveFields(model.name, ctx.modelIndex);
|
|
@@ -490,7 +445,6 @@ function effectiveFieldsFor(model, ctx) {
|
|
|
490
445
|
}
|
|
491
446
|
return fields;
|
|
492
447
|
}
|
|
493
|
-
__name(effectiveFieldsFor, "effectiveFieldsFor");
|
|
494
448
|
function generateAliasModel(model, ctx) {
|
|
495
449
|
const type = model.type;
|
|
496
450
|
const inner = type.kind === "lazy" ? type.inner : type;
|
|
@@ -504,40 +458,47 @@ function generateAliasModel(model, ctx) {
|
|
|
504
458
|
const needsSplit = ctx.modelsWithInput.has(model.name) || fields.some((f) => f.visibility !== "normal");
|
|
505
459
|
if (!needsSplit) return generateRecordForModel(model.name, fields, ctx, false, model, false);
|
|
506
460
|
return [
|
|
507
|
-
...generateRecordForModel(
|
|
461
|
+
...generateRecordForModel(
|
|
462
|
+
model.name,
|
|
463
|
+
fields.filter((f) => f.visibility !== "writeonly"),
|
|
464
|
+
ctx,
|
|
465
|
+
false,
|
|
466
|
+
model,
|
|
467
|
+
true
|
|
468
|
+
),
|
|
508
469
|
"",
|
|
509
|
-
...generateRecordForModel(
|
|
470
|
+
...generateRecordForModel(
|
|
471
|
+
`${model.name}Input`,
|
|
472
|
+
fields.filter((f) => f.visibility !== "readonly"),
|
|
473
|
+
ctx,
|
|
474
|
+
true,
|
|
475
|
+
model,
|
|
476
|
+
true
|
|
477
|
+
)
|
|
510
478
|
];
|
|
511
479
|
}
|
|
512
480
|
addAlias(model.name, type, ctx, false);
|
|
513
481
|
if (ctx.modelsWithInput.has(model.name)) addAlias(`${model.name}Input`, type, ctx, true);
|
|
514
482
|
return [];
|
|
515
483
|
}
|
|
516
|
-
__name(generateAliasModel, "generateAliasModel");
|
|
517
484
|
function addAlias(name, type, ctx, forInput) {
|
|
518
|
-
const target = renderCSharpType(type, {
|
|
519
|
-
...ctx,
|
|
520
|
-
qualify: true
|
|
521
|
-
}, forInput);
|
|
485
|
+
const target = renderCSharpType(type, { ...ctx, qualify: true }, forInput);
|
|
522
486
|
let aliased = target;
|
|
523
487
|
if (aliased.endsWith("?") && !isNullableValueType(type, ctx)) {
|
|
524
488
|
aliased = aliased.slice(0, -1);
|
|
525
|
-
ctx.warn?.(
|
|
489
|
+
ctx.warn?.(
|
|
490
|
+
`Contract '${name}' aliases a nullable type, which C# cannot express as a using alias; '${name}' is generated as '${aliased}'. Declare the nullability at each use site instead.`
|
|
491
|
+
);
|
|
526
492
|
}
|
|
527
493
|
ctx.globalAliases.push(`global using ${name} = ${aliased};`);
|
|
528
494
|
}
|
|
529
|
-
__name(addAlias, "addAlias");
|
|
530
495
|
function isNullableValueType(type, ctx) {
|
|
531
496
|
const inner = type.kind === "lazy" ? type.inner : type;
|
|
532
497
|
if (inner.kind !== "union") return false;
|
|
533
498
|
const nonNull = inner.members.filter((m) => !isNullScalar(m));
|
|
534
499
|
if (nonNull.length !== 1) return false;
|
|
535
|
-
return VALUE_TYPES.has(renderCSharpType(nonNull[0], {
|
|
536
|
-
...ctx,
|
|
537
|
-
qualify: false
|
|
538
|
-
}, false));
|
|
500
|
+
return VALUE_TYPES.has(renderCSharpType(nonNull[0], { ...ctx, qualify: false }, false));
|
|
539
501
|
}
|
|
540
|
-
__name(isNullableValueType, "isNullableValueType");
|
|
541
502
|
var VALUE_TYPES = /* @__PURE__ */ new Set([
|
|
542
503
|
"bool",
|
|
543
504
|
"byte",
|
|
@@ -558,7 +519,6 @@ function enumMemberNames(values) {
|
|
|
558
519
|
for (const value of values) out.set(value, uniqueName(toCSharpEnumMemberName(value), used));
|
|
559
520
|
return out;
|
|
560
521
|
}
|
|
561
|
-
__name(enumMemberNames, "enumMemberNames");
|
|
562
522
|
function generateEnum(name, values, ctx, description, deprecated) {
|
|
563
523
|
const entries = enumMemberNames(values);
|
|
564
524
|
const lines = [];
|
|
@@ -575,7 +535,6 @@ function generateEnum(name, values, ctx, description, deprecated) {
|
|
|
575
535
|
if (ctx.modelsWithInput.has(name)) ctx.globalAliases.push(`global using ${name}Input = ${ctx.namespace}.Models.${name};`);
|
|
576
536
|
return lines;
|
|
577
537
|
}
|
|
578
|
-
__name(generateEnum, "generateEnum");
|
|
579
538
|
function supertypesFor(readName, ctx, forInput) {
|
|
580
539
|
const unions = ctx.hoisted?.memberships.get(readName) ?? [];
|
|
581
540
|
return unions.map((union) => {
|
|
@@ -583,14 +542,12 @@ function supertypesFor(readName, ctx, forInput) {
|
|
|
583
542
|
return forInput && decl?.needsInput ? `${union}Input` : union;
|
|
584
543
|
});
|
|
585
544
|
}
|
|
586
|
-
__name(supertypesFor, "supertypesFor");
|
|
587
545
|
function generateRecordForModel(name, fields, ctx, forInput, model, split) {
|
|
588
546
|
const readName = forInput && name.endsWith("Input") ? name.slice(0, -"Input".length) : name;
|
|
589
547
|
const wireCase = wireCaseFor(model, forInput, split, ctx);
|
|
590
548
|
if (!forInput) warnUncasedNesting(model, fields, wireCase, ctx);
|
|
591
549
|
return renderRecord(name, fields, ctx, forInput, supertypesFor(readName, ctx, forInput), model.description, model.deprecated, wireCase);
|
|
592
550
|
}
|
|
593
|
-
__name(generateRecordForModel, "generateRecordForModel");
|
|
594
551
|
function renderRecord(name, fields, ctx, forInput, supertypes, description, deprecated, wireCase) {
|
|
595
552
|
const lines = [];
|
|
596
553
|
lines.push(...docLines(description, deprecated, ""));
|
|
@@ -599,25 +556,28 @@ function renderRecord(name, fields, ctx, forInput, supertypes, description, depr
|
|
|
599
556
|
lines.push(`public sealed record ${name}${implementsClause};`);
|
|
600
557
|
return lines;
|
|
601
558
|
}
|
|
559
|
+
const memberNames = new Set(fields.map((field) => memberName(field, name)));
|
|
602
560
|
lines.push(`public sealed record ${name}${implementsClause}`);
|
|
603
561
|
lines.push("{");
|
|
604
562
|
fields.forEach((field, index) => {
|
|
605
563
|
if (index > 0) lines.push("");
|
|
606
|
-
lines.push(...renderField(field, ctx, forInput, name, wireCase));
|
|
564
|
+
lines.push(...renderField(field, ctx, forInput, name, memberNames, wireCase));
|
|
607
565
|
});
|
|
608
566
|
lines.push("}");
|
|
609
567
|
return lines;
|
|
610
568
|
}
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
569
|
+
function memberName(field, ownerTypeName) {
|
|
570
|
+
return safeMemberName(toCSharpPropertyName(field.name), ownerTypeName);
|
|
571
|
+
}
|
|
572
|
+
function renderField(field, ctx, forInput, ownerTypeName, memberNames, wireCase) {
|
|
573
|
+
const propName = memberName(field, ownerTypeName);
|
|
614
574
|
const wireName = applyWireCase(field.name, wireCase);
|
|
615
575
|
let typeStr = renderCSharpType(field.type, ctx, forInput);
|
|
616
576
|
if ((field.optional || field.nullable) && !typeStr.endsWith("?")) typeStr += "?";
|
|
617
|
-
let initializer = field.default !== void 0 ? renderDefault(field.default, field.type, ctx) : void 0;
|
|
577
|
+
let initializer = field.default !== void 0 ? renderDefault(field.default, field.type, ctx, memberNames) : void 0;
|
|
618
578
|
if (initializer === void 0 && !field.optional && !field.nullable) {
|
|
619
579
|
const inner = field.type.kind === "lazy" ? field.type.inner : field.type;
|
|
620
|
-
if (inner.kind === "literal") initializer = renderDefault(inner.value, inner, ctx);
|
|
580
|
+
if (inner.kind === "literal") initializer = renderDefault(inner.value, inner, ctx, memberNames);
|
|
621
581
|
}
|
|
622
582
|
const isRequired = !field.optional && initializer === void 0;
|
|
623
583
|
const lines = [];
|
|
@@ -628,24 +588,25 @@ function renderField(field, ctx, forInput, ownerTypeName, wireCase) {
|
|
|
628
588
|
lines.push(` public ${isRequired ? "required " : ""}${typeStr} ${propName} { get; init; }${suffix}`);
|
|
629
589
|
return lines;
|
|
630
590
|
}
|
|
631
|
-
__name(renderField, "renderField");
|
|
632
591
|
function generateHoisted(decl, ctx) {
|
|
633
592
|
const read = generateHoistedVariant(decl, ctx, false);
|
|
634
593
|
if (!decl.needsInput) return read;
|
|
635
|
-
return [
|
|
636
|
-
...read,
|
|
637
|
-
"",
|
|
638
|
-
...generateHoistedVariant(decl, ctx, true)
|
|
639
|
-
];
|
|
594
|
+
return [...read, "", ...generateHoistedVariant(decl, ctx, true)];
|
|
640
595
|
}
|
|
641
|
-
__name(generateHoisted, "generateHoisted");
|
|
642
596
|
function generateHoistedVariant(decl, ctx, forInput) {
|
|
643
597
|
const name = forInput ? `${decl.name}Input` : decl.name;
|
|
644
598
|
switch (decl.kind) {
|
|
645
599
|
case "enum":
|
|
646
600
|
return generateEnum(name, decl.values ?? [], ctx, decl.description);
|
|
647
601
|
case "record":
|
|
648
|
-
return renderRecord(
|
|
602
|
+
return renderRecord(
|
|
603
|
+
name,
|
|
604
|
+
(decl.fields ?? []).filter((f) => forInput ? f.visibility !== "readonly" : f.visibility !== "writeonly"),
|
|
605
|
+
ctx,
|
|
606
|
+
forInput,
|
|
607
|
+
supertypesFor(decl.name, ctx, forInput),
|
|
608
|
+
decl.description
|
|
609
|
+
);
|
|
649
610
|
case "tuple":
|
|
650
611
|
return generateTupleRecord(decl, name, ctx, forInput);
|
|
651
612
|
case "plainUnion":
|
|
@@ -654,11 +615,9 @@ function generateHoistedVariant(decl, ctx, forInput) {
|
|
|
654
615
|
return generateDiscriminatedUnion(decl, name, ctx, forInput);
|
|
655
616
|
}
|
|
656
617
|
}
|
|
657
|
-
__name(generateHoistedVariant, "generateHoistedVariant");
|
|
658
618
|
function deserializeExpr(type, ctx, forInput) {
|
|
659
619
|
return `element.Deserialize<${renderCSharpType(type, ctx, forInput)}>(options)!`;
|
|
660
620
|
}
|
|
661
|
-
__name(deserializeExpr, "deserializeExpr");
|
|
662
621
|
function generateTupleRecord(decl, name, ctx, forInput) {
|
|
663
622
|
const items = decl.items ?? [];
|
|
664
623
|
const converterName = `${name}Converter`;
|
|
@@ -697,7 +656,6 @@ function generateTupleRecord(decl, name, ctx, forInput) {
|
|
|
697
656
|
lines.push("}");
|
|
698
657
|
return lines;
|
|
699
658
|
}
|
|
700
|
-
__name(generateTupleRecord, "generateTupleRecord");
|
|
701
659
|
function generatePlainUnion(decl, name, ctx, forInput) {
|
|
702
660
|
const converterName = `${name}Converter`;
|
|
703
661
|
const members = decl.members ?? [];
|
|
@@ -751,13 +709,9 @@ function generatePlainUnion(decl, name, ctx, forInput) {
|
|
|
751
709
|
lines.push("}");
|
|
752
710
|
return lines;
|
|
753
711
|
}
|
|
754
|
-
__name(generatePlainUnion, "generatePlainUnion");
|
|
755
712
|
function generateDiscriminatedUnion(decl, name, ctx, forInput) {
|
|
756
713
|
const converterName = `${name}Converter`;
|
|
757
|
-
const members = (decl.members ?? []).map((member) => ({
|
|
758
|
-
...member,
|
|
759
|
-
recordName: memberRecordName(member.typeName, ctx, forInput)
|
|
760
|
-
}));
|
|
714
|
+
const members = (decl.members ?? []).map((member) => ({ ...member, recordName: memberRecordName(member.typeName, ctx, forInput) }));
|
|
761
715
|
const discriminator = decl.discriminator ?? "";
|
|
762
716
|
const lines = [];
|
|
763
717
|
lines.push(...docLines(decl.description, void 0, ""));
|
|
@@ -773,7 +727,9 @@ function generateDiscriminatedUnion(decl, name, ctx, forInput) {
|
|
|
773
727
|
lines.push(" {");
|
|
774
728
|
lines.push(" using var document = JsonDocument.ParseValue(ref reader);");
|
|
775
729
|
lines.push(" var element = document.RootElement;");
|
|
776
|
-
lines.push(
|
|
730
|
+
lines.push(
|
|
731
|
+
` var tag = element.TryGetProperty(${quoteCSharpString(discriminator)}, out var tagElement) && tagElement.ValueKind == JsonValueKind.String`
|
|
732
|
+
);
|
|
777
733
|
lines.push(" ? tagElement.GetString()");
|
|
778
734
|
lines.push(" : null;");
|
|
779
735
|
lines.push("");
|
|
@@ -802,21 +758,18 @@ function generateDiscriminatedUnion(decl, name, ctx, forInput) {
|
|
|
802
758
|
lines.push("}");
|
|
803
759
|
return lines;
|
|
804
760
|
}
|
|
805
|
-
__name(generateDiscriminatedUnion, "generateDiscriminatedUnion");
|
|
806
761
|
function memberRecordName(typeName, ctx, forInput) {
|
|
807
762
|
if (!forInput) return typeName;
|
|
808
763
|
const decl = ctx.hoisted?.byName.get(typeName);
|
|
809
764
|
if (decl) return decl.needsInput ? `${typeName}Input` : typeName;
|
|
810
765
|
return ctx.modelsWithInput.has(typeName) ? `${typeName}Input` : typeName;
|
|
811
766
|
}
|
|
812
|
-
__name(memberRecordName, "memberRecordName");
|
|
813
767
|
function docLines(description, deprecated, indent) {
|
|
814
768
|
const lines = [];
|
|
815
769
|
if (description) lines.push(...xmlDocLines(description, indent));
|
|
816
770
|
if (deprecated) lines.push(...xmlDocLines("Deprecated in the contract.", indent, "remarks"));
|
|
817
771
|
return lines;
|
|
818
772
|
}
|
|
819
|
-
__name(docLines, "docLines");
|
|
820
773
|
function uniqueName(name, used) {
|
|
821
774
|
if (!used.has(name)) {
|
|
822
775
|
used.add(name);
|
|
@@ -827,7 +780,6 @@ function uniqueName(name, used) {
|
|
|
827
780
|
used.add(`${name}${n}`);
|
|
828
781
|
return `${name}${n}`;
|
|
829
782
|
}
|
|
830
|
-
__name(uniqueName, "uniqueName");
|
|
831
783
|
|
|
832
784
|
// src/codegen-client.ts
|
|
833
785
|
import { classifyContentType, observableResponses, resolveModifiers } from "@contractkit/core";
|
|
@@ -847,7 +799,6 @@ function clientUsings(namespaceName) {
|
|
|
847
799
|
`using ${namespaceName}.Runtime;`
|
|
848
800
|
];
|
|
849
801
|
}
|
|
850
|
-
__name(clientUsings, "clientUsings");
|
|
851
802
|
function hasPublicOperations(root, includeInternal = false) {
|
|
852
803
|
for (const route of root.routes) {
|
|
853
804
|
for (const op of route.operations) {
|
|
@@ -856,15 +807,12 @@ function hasPublicOperations(root, includeInternal = false) {
|
|
|
856
807
|
}
|
|
857
808
|
return false;
|
|
858
809
|
}
|
|
859
|
-
__name(hasPublicOperations, "hasPublicOperations");
|
|
860
810
|
function deriveClientClassName(file) {
|
|
861
811
|
return `${deriveCSharpFileBase(file)}Client`;
|
|
862
812
|
}
|
|
863
|
-
__name(deriveClientClassName, "deriveClientClassName");
|
|
864
813
|
function deriveClientPropertyName(file) {
|
|
865
814
|
return deriveCSharpFileBase(file);
|
|
866
815
|
}
|
|
867
|
-
__name(deriveClientPropertyName, "deriveClientPropertyName");
|
|
868
816
|
function generateCSharpClient(root, opts) {
|
|
869
817
|
const className = deriveClientClassName(root.file);
|
|
870
818
|
const includeInternal = opts.includeInternal ?? false;
|
|
@@ -873,29 +821,22 @@ function generateCSharpClient(root, opts) {
|
|
|
873
821
|
for (const route of root.routes) {
|
|
874
822
|
for (const op of route.operations) {
|
|
875
823
|
if (!includeInternal && resolveModifiers(route, op).includes("internal")) continue;
|
|
876
|
-
publicOps.push({
|
|
877
|
-
route,
|
|
878
|
-
op
|
|
879
|
-
});
|
|
824
|
+
publicOps.push({ route, op });
|
|
880
825
|
}
|
|
881
826
|
}
|
|
882
827
|
const shapeLines = [];
|
|
883
828
|
for (const { route, op } of publicOps) {
|
|
884
829
|
const base = methodBase(deriveMethodName(op, route));
|
|
885
830
|
for (const { source, suffix } of [
|
|
886
|
-
{
|
|
887
|
-
|
|
888
|
-
suffix: "Query"
|
|
889
|
-
},
|
|
890
|
-
{
|
|
891
|
-
source: op.headers,
|
|
892
|
-
suffix: "Headers"
|
|
893
|
-
}
|
|
831
|
+
{ source: op.query, suffix: "Query" },
|
|
832
|
+
{ source: op.headers, suffix: "Headers" }
|
|
894
833
|
]) {
|
|
895
834
|
if (source?.kind !== "params" || source.nodes.length === 0) continue;
|
|
896
835
|
const shapeName = `${base}${suffix}`;
|
|
897
836
|
shapeLines.push("");
|
|
898
|
-
shapeLines.push(
|
|
837
|
+
shapeLines.push(
|
|
838
|
+
...xmlDocLines(`The ${suffix === "Query" ? "query parameters" : "request headers"} declared on ${where(route, op)}.`, "")
|
|
839
|
+
);
|
|
899
840
|
shapeLines.push(`public sealed record ${shapeName}`);
|
|
900
841
|
shapeLines.push("{");
|
|
901
842
|
source.nodes.forEach((node, index) => {
|
|
@@ -918,7 +859,9 @@ function generateCSharpClient(root, opts) {
|
|
|
918
859
|
const methodName = deriveMethodName(op, route);
|
|
919
860
|
const clash = seen.get(methodName);
|
|
920
861
|
if (clash) {
|
|
921
|
-
throw new Error(
|
|
862
|
+
throw new Error(
|
|
863
|
+
`plugin-csharp: ${where(route, op)} and ${clash} both generate the client method '${methodName}' on ${className}. Give one of them a distinct 'sdk:' name.`
|
|
864
|
+
);
|
|
922
865
|
}
|
|
923
866
|
seen.set(methodName, where(route, op));
|
|
924
867
|
methodLines.push("");
|
|
@@ -934,39 +877,23 @@ function generateCSharpClient(root, opts) {
|
|
|
934
877
|
body.push(...shapeLines);
|
|
935
878
|
return renderFile(`${opts.namespace}.Clients`, ctx.globalAliases, clientUsings(opts.namespace), body);
|
|
936
879
|
}
|
|
937
|
-
__name(generateCSharpClient, "generateCSharpClient");
|
|
938
880
|
function where(route, op) {
|
|
939
881
|
return `${op.method.toUpperCase()} ${route.path}`;
|
|
940
882
|
}
|
|
941
|
-
__name(where, "where");
|
|
942
883
|
function methodBase(methodName) {
|
|
943
884
|
return methodName.endsWith("Async") ? methodName.slice(0, -"Async".length) : methodName;
|
|
944
885
|
}
|
|
945
|
-
__name(methodBase, "methodBase");
|
|
946
886
|
function responseShape(op) {
|
|
947
887
|
const observable = observableResponses(op);
|
|
948
|
-
if (observable.length > 1) return {
|
|
949
|
-
kind: "multiStatus",
|
|
950
|
-
responses: observable
|
|
951
|
-
};
|
|
888
|
+
if (observable.length > 1) return { kind: "multiStatus", responses: observable };
|
|
952
889
|
const response = observable[0];
|
|
953
|
-
if (response && response.bodies.length > 1) return {
|
|
954
|
-
|
|
955
|
-
response
|
|
956
|
-
};
|
|
957
|
-
return {
|
|
958
|
-
kind: "simple",
|
|
959
|
-
response
|
|
960
|
-
};
|
|
890
|
+
if (response && response.bodies.length > 1) return { kind: "multiMime", response };
|
|
891
|
+
return { kind: "simple", response };
|
|
961
892
|
}
|
|
962
|
-
__name(responseShape, "responseShape");
|
|
963
893
|
function observableOf(shape) {
|
|
964
894
|
if (shape.kind === "multiStatus") return shape.responses;
|
|
965
|
-
return shape.response ? [
|
|
966
|
-
shape.response
|
|
967
|
-
] : [];
|
|
895
|
+
return shape.response ? [shape.response] : [];
|
|
968
896
|
}
|
|
969
|
-
__name(observableOf, "observableOf");
|
|
970
897
|
function generateMethod(route, op, ctx, methodName) {
|
|
971
898
|
const base = methodBase(methodName);
|
|
972
899
|
const shape = responseShape(op);
|
|
@@ -974,19 +901,15 @@ function generateMethod(route, op, ctx, methodName) {
|
|
|
974
901
|
const observable = observableOf(shape);
|
|
975
902
|
const expectStatuses = observable.filter((r) => r.statusCode < 200 || r.statusCode >= 300).map((r) => r.statusCode);
|
|
976
903
|
const params = buildMethodParams(route, op, ctx);
|
|
977
|
-
const signature = [
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
].join(", ");
|
|
904
|
+
const signature = [...params.map((p) => `${p.type} ${p.name}${p.optional ? " = null" : ""}`), "CancellationToken cancellationToken = default"].join(
|
|
905
|
+
", "
|
|
906
|
+
);
|
|
981
907
|
const lines = [];
|
|
982
908
|
lines.push(...methodDoc(route, op, observable));
|
|
983
909
|
if (resolveModifiers(route, op).includes("deprecated")) lines.push('[Obsolete("Deprecated in the contract")]');
|
|
984
910
|
lines.push(`public async ${returnType === "void" ? "Task" : `Task<${returnType}>`} ${methodName}(${signature})`);
|
|
985
911
|
lines.push("{");
|
|
986
|
-
const callArgs = [
|
|
987
|
-
`HttpMethod.${httpMethodConstant(op.method)}`,
|
|
988
|
-
buildPathExpression(route.path, route.params)
|
|
989
|
-
];
|
|
912
|
+
const callArgs = [`HttpMethod.${httpMethodConstant(op.method)}`, buildPathExpression(route.path, route.params)];
|
|
990
913
|
if (op.query) callArgs.push("query: http.Params(query)");
|
|
991
914
|
if (op.headers) callArgs.push("headers: http.Params(customHeaders)");
|
|
992
915
|
const content = bodyArgument(op);
|
|
@@ -1002,7 +925,6 @@ function generateMethod(route, op, ctx, methodName) {
|
|
|
1002
925
|
lines.push("}");
|
|
1003
926
|
return lines;
|
|
1004
927
|
}
|
|
1005
|
-
__name(generateMethod, "generateMethod");
|
|
1006
928
|
function returnTypeFor(shape, base, ctx) {
|
|
1007
929
|
if (shape.kind !== "simple") return `${base}Response`;
|
|
1008
930
|
const response = shape.response;
|
|
@@ -1012,7 +934,6 @@ function returnTypeFor(shape, base, ctx) {
|
|
|
1012
934
|
const dataType = bodyCSharpType(body, ctx);
|
|
1013
935
|
return headers.length > 0 ? `${base}Result` : dataType;
|
|
1014
936
|
}
|
|
1015
|
-
__name(returnTypeFor, "returnTypeFor");
|
|
1016
937
|
function bodyCSharpType(body, ctx) {
|
|
1017
938
|
switch (classifyContentType(body.contentType)) {
|
|
1018
939
|
case "text":
|
|
@@ -1023,7 +944,6 @@ function bodyCSharpType(body, ctx) {
|
|
|
1023
944
|
return renderCSharpType(body.bodyType, ctx, false);
|
|
1024
945
|
}
|
|
1025
946
|
}
|
|
1026
|
-
__name(bodyCSharpType, "bodyCSharpType");
|
|
1027
947
|
function bodyReadExpr(body, ctx) {
|
|
1028
948
|
switch (classifyContentType(body.contentType)) {
|
|
1029
949
|
case "text":
|
|
@@ -1034,23 +954,14 @@ function bodyReadExpr(body, ctx) {
|
|
|
1034
954
|
return `http.ReadJson<${renderCSharpType(body.bodyType, ctx, false)}>(response)`;
|
|
1035
955
|
}
|
|
1036
956
|
}
|
|
1037
|
-
__name(bodyReadExpr, "bodyReadExpr");
|
|
1038
957
|
function returnStatements(shape, base, ctx, place) {
|
|
1039
958
|
if (shape.kind === "simple") {
|
|
1040
959
|
const response = shape.response;
|
|
1041
960
|
const body = response?.bodies[0];
|
|
1042
961
|
const headers = response?.headers ?? [];
|
|
1043
|
-
if (headers.length === 0) return body ? [
|
|
1044
|
-
` return ${bodyReadExpr(body, ctx)};`
|
|
1045
|
-
] : [];
|
|
962
|
+
if (headers.length === 0) return body ? [` return ${bodyReadExpr(body, ctx)};`] : [];
|
|
1046
963
|
const lines2 = readHeaderLines(headers, `${base}Headers`, ctx, place, " ");
|
|
1047
|
-
return body ? [
|
|
1048
|
-
...lines2,
|
|
1049
|
-
` return new ${base}Result(${bodyReadExpr(body, ctx)}, headers);`
|
|
1050
|
-
] : [
|
|
1051
|
-
...lines2,
|
|
1052
|
-
" return headers;"
|
|
1053
|
-
];
|
|
964
|
+
return body ? [...lines2, ` return new ${base}Result(${bodyReadExpr(body, ctx)}, headers);`] : [...lines2, " return headers;"];
|
|
1054
965
|
}
|
|
1055
966
|
if (shape.kind === "multiMime") {
|
|
1056
967
|
const headers = shape.response.headers ?? [];
|
|
@@ -1059,10 +970,7 @@ function returnStatements(shape, base, ctx, place) {
|
|
|
1059
970
|
return lines2;
|
|
1060
971
|
}
|
|
1061
972
|
const [fallback, ...rest] = shape.responses;
|
|
1062
|
-
const lines = [
|
|
1063
|
-
" switch (response.Status)",
|
|
1064
|
-
" {"
|
|
1065
|
-
];
|
|
973
|
+
const lines = [" switch (response.Status)", " {"];
|
|
1066
974
|
for (const response of rest) {
|
|
1067
975
|
lines.push(` case ${response.statusCode}:`);
|
|
1068
976
|
lines.push(" {");
|
|
@@ -1077,7 +985,6 @@ function returnStatements(shape, base, ctx, place) {
|
|
|
1077
985
|
lines.push(" }");
|
|
1078
986
|
return lines;
|
|
1079
987
|
}
|
|
1080
|
-
__name(returnStatements, "returnStatements");
|
|
1081
988
|
function statusBranch(response, base, statusCode, ctx, place, indent) {
|
|
1082
989
|
const lines = [];
|
|
1083
990
|
const headers = response.headers ?? [];
|
|
@@ -1085,23 +992,17 @@ function statusBranch(response, base, statusCode, ctx, place, indent) {
|
|
|
1085
992
|
lines.push(...mimeSwitch(response, base, statusCode, ctx, indent, headers.length > 0));
|
|
1086
993
|
return lines;
|
|
1087
994
|
}
|
|
1088
|
-
__name(statusBranch, "statusBranch");
|
|
1089
995
|
function mimeSwitch(response, base, statusCode, ctx, indent, hasHeaders) {
|
|
1090
996
|
const bodies = response.bodies;
|
|
1091
|
-
const construct =
|
|
997
|
+
const construct = (body) => {
|
|
1092
998
|
const args = [];
|
|
1093
999
|
if (body) args.push(bodyReadExpr(body, ctx));
|
|
1094
1000
|
if (hasHeaders) args.push("headers");
|
|
1095
1001
|
return `new ${base}Response.${leafRecordName(response, body, statusCode)}(${args.join(", ")})`;
|
|
1096
|
-
}
|
|
1097
|
-
if (bodies.length <= 1) return [
|
|
1098
|
-
`${indent}return ${construct(bodies[0])};`
|
|
1099
|
-
];
|
|
1002
|
+
};
|
|
1003
|
+
if (bodies.length <= 1) return [`${indent}return ${construct(bodies[0])};`];
|
|
1100
1004
|
const [fallback, ...rest] = bodies;
|
|
1101
|
-
const lines = [
|
|
1102
|
-
`${indent}switch (response.ContentType)`,
|
|
1103
|
-
`${indent}{`
|
|
1104
|
-
];
|
|
1005
|
+
const lines = [`${indent}switch (response.ContentType)`, `${indent}{`];
|
|
1105
1006
|
for (const body of rest) {
|
|
1106
1007
|
lines.push(`${indent} case ${quoteCSharpString(body.contentType)}:`);
|
|
1107
1008
|
lines.push(`${indent} return ${construct(body)};`);
|
|
@@ -1111,7 +1012,6 @@ function mimeSwitch(response, base, statusCode, ctx, indent, hasHeaders) {
|
|
|
1111
1012
|
lines.push(`${indent}}`);
|
|
1112
1013
|
return lines;
|
|
1113
1014
|
}
|
|
1114
|
-
__name(mimeSwitch, "mimeSwitch");
|
|
1115
1015
|
function bodyArgument(op) {
|
|
1116
1016
|
const body = op.request?.bodies[0];
|
|
1117
1017
|
if (!body) return void 0;
|
|
@@ -1129,23 +1029,20 @@ function bodyArgument(op) {
|
|
|
1129
1029
|
return `content: http.JsonContent(body, ${mime})`;
|
|
1130
1030
|
}
|
|
1131
1031
|
}
|
|
1132
|
-
__name(bodyArgument, "bodyArgument");
|
|
1133
1032
|
function headersRecordName(base, statusCode) {
|
|
1134
1033
|
return statusCode === void 0 ? `${base}Headers` : `${base}${statusCode}Headers`;
|
|
1135
1034
|
}
|
|
1136
|
-
__name(headersRecordName, "headersRecordName");
|
|
1137
1035
|
function leafRecordName(response, body, statusCode) {
|
|
1138
1036
|
const statusPart = statusCode === void 0 ? "" : `Status${statusCode}`;
|
|
1139
1037
|
if (response.bodies.length <= 1 || !body) return statusPart || "Body";
|
|
1140
1038
|
return `${statusPart}${toCSharpTypeName(body.contentType.replace(/[+/.]/g, " "))}`;
|
|
1141
1039
|
}
|
|
1142
|
-
__name(leafRecordName, "leafRecordName");
|
|
1143
1040
|
function responseDeclarations(route, op, ctx) {
|
|
1144
1041
|
const shape = responseShape(op);
|
|
1145
1042
|
const base = methodBase(deriveMethodName(op, route));
|
|
1146
1043
|
const place = where(route, op);
|
|
1147
1044
|
const lines = [];
|
|
1148
|
-
const headerRecord =
|
|
1045
|
+
const headerRecord = (headers, name) => {
|
|
1149
1046
|
const parameters = headers.map((header) => {
|
|
1150
1047
|
const reader = headerReader(header, place);
|
|
1151
1048
|
const type = header.optional ? `${reader.type}?` : reader.type;
|
|
@@ -1154,7 +1051,7 @@ function responseDeclarations(route, op, ctx) {
|
|
|
1154
1051
|
lines.push("");
|
|
1155
1052
|
lines.push(...xmlDocLines(`Response headers declared on ${place}.`, ""));
|
|
1156
1053
|
lines.push(`public sealed record ${name}(${parameters});`);
|
|
1157
|
-
}
|
|
1054
|
+
};
|
|
1158
1055
|
if (shape.kind === "simple") {
|
|
1159
1056
|
const response = shape.response;
|
|
1160
1057
|
const headers = response?.headers ?? [];
|
|
@@ -1175,18 +1072,21 @@ function responseDeclarations(route, op, ctx) {
|
|
|
1175
1072
|
if (headers.length > 0) headerRecord(headers, headersRecordName(base, withStatus ? response.statusCode : void 0));
|
|
1176
1073
|
}
|
|
1177
1074
|
lines.push("");
|
|
1178
|
-
lines.push(
|
|
1179
|
-
|
|
1180
|
-
`
|
|
1075
|
+
lines.push(
|
|
1076
|
+
...xmlDocLines(
|
|
1077
|
+
`What ${place} returned.
|
|
1078
|
+
|
|
1079
|
+
` + (withStatus ? "The operation declares several statuses the service produces, so the status is part of the value." : "The status declares several content types, so which one arrived is part of the value."),
|
|
1080
|
+
""
|
|
1081
|
+
)
|
|
1082
|
+
);
|
|
1181
1083
|
lines.push(`public abstract record ${base}Response`);
|
|
1182
1084
|
lines.push("{");
|
|
1183
1085
|
lines.push(` private ${base}Response() { }`);
|
|
1184
1086
|
for (const response of responses) {
|
|
1185
1087
|
const statusCode = withStatus ? response.statusCode : void 0;
|
|
1186
1088
|
const headers = response.headers ?? [];
|
|
1187
|
-
const bodies = response.bodies.length > 0 ? response.bodies : [
|
|
1188
|
-
void 0
|
|
1189
|
-
];
|
|
1089
|
+
const bodies = response.bodies.length > 0 ? response.bodies : [void 0];
|
|
1190
1090
|
for (const body of bodies) {
|
|
1191
1091
|
const name = leafRecordName(response, body, statusCode);
|
|
1192
1092
|
const parameters = [];
|
|
@@ -1199,7 +1099,6 @@ function responseDeclarations(route, op, ctx) {
|
|
|
1199
1099
|
lines.push("}");
|
|
1200
1100
|
return lines;
|
|
1201
1101
|
}
|
|
1202
|
-
__name(responseDeclarations, "responseDeclarations");
|
|
1203
1102
|
function headerReader(header, place) {
|
|
1204
1103
|
const scalar = header.type.kind === "scalar" ? header.type.name : void 0;
|
|
1205
1104
|
switch (scalar) {
|
|
@@ -1208,66 +1107,36 @@ function headerReader(header, place) {
|
|
|
1208
1107
|
case "url":
|
|
1209
1108
|
case "interval":
|
|
1210
1109
|
case "unknown":
|
|
1211
|
-
return {
|
|
1212
|
-
type: "string",
|
|
1213
|
-
read: /* @__PURE__ */ __name((raw) => raw, "read")
|
|
1214
|
-
};
|
|
1110
|
+
return { type: "string", read: (raw) => raw };
|
|
1215
1111
|
case "number":
|
|
1216
|
-
return {
|
|
1217
|
-
type: "double",
|
|
1218
|
-
read: /* @__PURE__ */ __name((raw) => `double.Parse(${raw}, CultureInfo.InvariantCulture)`, "read")
|
|
1219
|
-
};
|
|
1112
|
+
return { type: "double", read: (raw) => `double.Parse(${raw}, CultureInfo.InvariantCulture)` };
|
|
1220
1113
|
case "int":
|
|
1221
|
-
return {
|
|
1222
|
-
type: "long",
|
|
1223
|
-
read: /* @__PURE__ */ __name((raw) => `long.Parse(${raw}, CultureInfo.InvariantCulture)`, "read")
|
|
1224
|
-
};
|
|
1114
|
+
return { type: "long", read: (raw) => `long.Parse(${raw}, CultureInfo.InvariantCulture)` };
|
|
1225
1115
|
case "bigint":
|
|
1226
|
-
return {
|
|
1227
|
-
type: "BigInteger",
|
|
1228
|
-
read: /* @__PURE__ */ __name((raw) => `BigInteger.Parse(${raw}, CultureInfo.InvariantCulture)`, "read")
|
|
1229
|
-
};
|
|
1116
|
+
return { type: "BigInteger", read: (raw) => `BigInteger.Parse(${raw}, CultureInfo.InvariantCulture)` };
|
|
1230
1117
|
case "boolean":
|
|
1231
|
-
return {
|
|
1232
|
-
type: "bool",
|
|
1233
|
-
read: /* @__PURE__ */ __name((raw) => `${raw} == "true"`, "read")
|
|
1234
|
-
};
|
|
1118
|
+
return { type: "bool", read: (raw) => `${raw} == "true"` };
|
|
1235
1119
|
case "uuid":
|
|
1236
|
-
return {
|
|
1237
|
-
type: "Guid",
|
|
1238
|
-
read: /* @__PURE__ */ __name((raw) => `Guid.Parse(${raw})`, "read")
|
|
1239
|
-
};
|
|
1120
|
+
return { type: "Guid", read: (raw) => `Guid.Parse(${raw})` };
|
|
1240
1121
|
case "date":
|
|
1241
|
-
return {
|
|
1242
|
-
type: "DateOnly",
|
|
1243
|
-
read: /* @__PURE__ */ __name((raw) => `DateOnly.Parse(${raw}, CultureInfo.InvariantCulture)`, "read")
|
|
1244
|
-
};
|
|
1122
|
+
return { type: "DateOnly", read: (raw) => `DateOnly.Parse(${raw}, CultureInfo.InvariantCulture)` };
|
|
1245
1123
|
case "time":
|
|
1246
|
-
return {
|
|
1247
|
-
type: "TimeOnly",
|
|
1248
|
-
read: /* @__PURE__ */ __name((raw) => `TimeOnly.Parse(${raw}, CultureInfo.InvariantCulture)`, "read")
|
|
1249
|
-
};
|
|
1124
|
+
return { type: "TimeOnly", read: (raw) => `TimeOnly.Parse(${raw}, CultureInfo.InvariantCulture)` };
|
|
1250
1125
|
case "datetime":
|
|
1251
|
-
return {
|
|
1252
|
-
type: "DateTimeOffset",
|
|
1253
|
-
read: /* @__PURE__ */ __name((raw) => `DateTimeOffset.Parse(${raw}, CultureInfo.InvariantCulture)`, "read")
|
|
1254
|
-
};
|
|
1126
|
+
return { type: "DateTimeOffset", read: (raw) => `DateTimeOffset.Parse(${raw}, CultureInfo.InvariantCulture)` };
|
|
1255
1127
|
case "duration":
|
|
1256
|
-
return {
|
|
1257
|
-
type: "TimeSpan",
|
|
1258
|
-
read: /* @__PURE__ */ __name((raw) => `XmlConvert.ToTimeSpan(${raw})`, "read")
|
|
1259
|
-
};
|
|
1128
|
+
return { type: "TimeSpan", read: (raw) => `XmlConvert.ToTimeSpan(${raw})` };
|
|
1260
1129
|
default:
|
|
1261
|
-
throw new Error(
|
|
1130
|
+
throw new Error(
|
|
1131
|
+
`plugin-csharp: response header '${header.name}' on ${place} is declared as ${describeHeaderType(header.type)}, which cannot be read from an HTTP header. Header values arrive as strings \u2014 declare it as string, email, url, uuid, date, time, datetime, duration, interval, int, number, boolean or bigint.`
|
|
1132
|
+
);
|
|
1262
1133
|
}
|
|
1263
1134
|
}
|
|
1264
|
-
__name(headerReader, "headerReader");
|
|
1265
1135
|
function describeHeaderType(type) {
|
|
1266
1136
|
if (type.kind === "scalar") return `the '${type.name}' scalar`;
|
|
1267
1137
|
if (type.kind === "ref") return `the contract '${type.name}'`;
|
|
1268
1138
|
return `${type.kind === "array" || type.kind === "inlineObject" ? "an" : "a"} ${type.kind}`;
|
|
1269
1139
|
}
|
|
1270
|
-
__name(describeHeaderType, "describeHeaderType");
|
|
1271
1140
|
function readHeaderLines(headers, typeName, ctx, place, indent) {
|
|
1272
1141
|
const args = headers.map((header) => {
|
|
1273
1142
|
const reader = headerReader(header, place);
|
|
@@ -1276,13 +1145,10 @@ function readHeaderLines(headers, typeName, ctx, place, indent) {
|
|
|
1276
1145
|
const local = toCSharpParameterName(header.name);
|
|
1277
1146
|
return `response.Header(${name}) is { } ${local} ? ${reader.read(local)} : null`;
|
|
1278
1147
|
});
|
|
1279
|
-
const lines = [
|
|
1280
|
-
`${indent}var headers = new ${typeName}(`
|
|
1281
|
-
];
|
|
1148
|
+
const lines = [`${indent}var headers = new ${typeName}(`];
|
|
1282
1149
|
args.forEach((arg, index) => lines.push(`${indent} ${arg}${index === args.length - 1 ? ");" : ","}`));
|
|
1283
1150
|
return lines;
|
|
1284
1151
|
}
|
|
1285
|
-
__name(readHeaderLines, "readHeaderLines");
|
|
1286
1152
|
function methodDoc(route, op, observable) {
|
|
1287
1153
|
const lines = [];
|
|
1288
1154
|
const parts = [];
|
|
@@ -1294,12 +1160,10 @@ function methodDoc(route, op, observable) {
|
|
|
1294
1160
|
if (thrown.length > 0) lines.push(`/// <exception cref="SdkException">On ${thrown.join(", ")}.</exception>`);
|
|
1295
1161
|
return lines;
|
|
1296
1162
|
}
|
|
1297
|
-
__name(methodDoc, "methodDoc");
|
|
1298
1163
|
function httpMethodConstant(method) {
|
|
1299
1164
|
const lower = method.toLowerCase();
|
|
1300
1165
|
return lower.charAt(0).toUpperCase() + lower.slice(1);
|
|
1301
1166
|
}
|
|
1302
|
-
__name(httpMethodConstant, "httpMethodConstant");
|
|
1303
1167
|
var PATH_PLACEHOLDER = /\{([a-zA-Z_$][a-zA-Z0-9_$.-]*)\}/g;
|
|
1304
1168
|
function buildPathExpression(path, params) {
|
|
1305
1169
|
const args = path.split("/").filter(Boolean).map((raw) => {
|
|
@@ -1311,65 +1175,36 @@ function buildPathExpression(path, params) {
|
|
|
1311
1175
|
});
|
|
1312
1176
|
return `http.Path(${args.join(", ")})`;
|
|
1313
1177
|
}
|
|
1314
|
-
__name(buildPathExpression, "buildPathExpression");
|
|
1315
1178
|
function buildMethodParams(route, op, ctx) {
|
|
1316
1179
|
const params = [];
|
|
1317
1180
|
if (route.params) {
|
|
1318
1181
|
if (route.params.kind === "params") {
|
|
1319
1182
|
for (const node of route.params.nodes) {
|
|
1320
|
-
params.push({
|
|
1321
|
-
name: toCSharpParameterName(node.name),
|
|
1322
|
-
type: renderCSharpType(node.type, ctx, true),
|
|
1323
|
-
optional: false
|
|
1324
|
-
});
|
|
1183
|
+
params.push({ name: toCSharpParameterName(node.name), type: renderCSharpType(node.type, ctx, true), optional: false });
|
|
1325
1184
|
}
|
|
1326
1185
|
} else {
|
|
1327
|
-
params.push({
|
|
1328
|
-
name: "pathParams",
|
|
1329
|
-
type: renderParamSourceType(route.params, ctx, ""),
|
|
1330
|
-
optional: false
|
|
1331
|
-
});
|
|
1186
|
+
params.push({ name: "pathParams", type: renderParamSourceType(route.params, ctx, ""), optional: false });
|
|
1332
1187
|
}
|
|
1333
1188
|
}
|
|
1334
1189
|
const body = op.request?.bodies[0];
|
|
1335
1190
|
if (body) {
|
|
1336
1191
|
switch (classifyContentType(body.contentType)) {
|
|
1337
1192
|
case "multipart":
|
|
1338
|
-
params.push({
|
|
1339
|
-
name: "body",
|
|
1340
|
-
type: "IEnumerable<SdkPart>",
|
|
1341
|
-
optional: false
|
|
1342
|
-
});
|
|
1193
|
+
params.push({ name: "body", type: "IEnumerable<SdkPart>", optional: false });
|
|
1343
1194
|
break;
|
|
1344
1195
|
case "binary":
|
|
1345
|
-
params.push({
|
|
1346
|
-
name: "body",
|
|
1347
|
-
type: "byte[]",
|
|
1348
|
-
optional: false
|
|
1349
|
-
});
|
|
1196
|
+
params.push({ name: "body", type: "byte[]", optional: false });
|
|
1350
1197
|
break;
|
|
1351
1198
|
case "text":
|
|
1352
|
-
params.push({
|
|
1353
|
-
name: "body",
|
|
1354
|
-
type: "string",
|
|
1355
|
-
optional: false
|
|
1356
|
-
});
|
|
1199
|
+
params.push({ name: "body", type: "string", optional: false });
|
|
1357
1200
|
break;
|
|
1358
1201
|
default:
|
|
1359
|
-
params.push({
|
|
1360
|
-
name: "body",
|
|
1361
|
-
type: renderCSharpType(body.bodyType, ctx, true),
|
|
1362
|
-
optional: false
|
|
1363
|
-
});
|
|
1202
|
+
params.push({ name: "body", type: renderCSharpType(body.bodyType, ctx, true), optional: false });
|
|
1364
1203
|
}
|
|
1365
1204
|
}
|
|
1366
1205
|
const base = methodBase(deriveMethodName(op, route));
|
|
1367
1206
|
if (op.query) {
|
|
1368
|
-
params.push({
|
|
1369
|
-
name: "query",
|
|
1370
|
-
type: renderParamSourceType(op.query, ctx, `${base}Query`),
|
|
1371
|
-
optional: allFieldsOptional(op.query)
|
|
1372
|
-
});
|
|
1207
|
+
params.push({ name: "query", type: renderParamSourceType(op.query, ctx, `${base}Query`), optional: allFieldsOptional(op.query) });
|
|
1373
1208
|
}
|
|
1374
1209
|
if (op.headers) {
|
|
1375
1210
|
params.push({
|
|
@@ -1378,63 +1213,47 @@ function buildMethodParams(route, op, ctx) {
|
|
|
1378
1213
|
optional: allFieldsOptional(op.headers)
|
|
1379
1214
|
});
|
|
1380
1215
|
}
|
|
1381
|
-
const widened = params.map((p) => p.optional && !p.type.endsWith("?") ? {
|
|
1382
|
-
|
|
1383
|
-
type: `${p.type}?`
|
|
1384
|
-
} : p);
|
|
1385
|
-
return [
|
|
1386
|
-
...widened.filter((p) => !p.optional),
|
|
1387
|
-
...widened.filter((p) => p.optional)
|
|
1388
|
-
];
|
|
1216
|
+
const widened = params.map((p) => p.optional && !p.type.endsWith("?") ? { ...p, type: `${p.type}?` } : p);
|
|
1217
|
+
return [...widened.filter((p) => !p.optional), ...widened.filter((p) => p.optional)];
|
|
1389
1218
|
}
|
|
1390
|
-
__name(buildMethodParams, "buildMethodParams");
|
|
1391
1219
|
function allFieldsOptional(source) {
|
|
1392
1220
|
if (source.kind !== "params") return true;
|
|
1393
1221
|
return source.nodes.every((node) => Boolean(node.optional) || node.default !== void 0);
|
|
1394
1222
|
}
|
|
1395
|
-
__name(allFieldsOptional, "allFieldsOptional");
|
|
1396
1223
|
function renderParamSourceType(source, ctx, generatedName) {
|
|
1397
|
-
if (source.kind === "ref") return renderCSharpType({
|
|
1398
|
-
kind: "ref",
|
|
1399
|
-
name: source.name
|
|
1400
|
-
}, ctx, true);
|
|
1224
|
+
if (source.kind === "ref") return renderCSharpType({ kind: "ref", name: source.name }, ctx, true);
|
|
1401
1225
|
if (source.kind === "type") return renderCSharpType(source.node, ctx, true);
|
|
1402
1226
|
return source.nodes.length > 0 ? generatedName : "IReadOnlyDictionary<string, string>";
|
|
1403
1227
|
}
|
|
1404
|
-
__name(renderParamSourceType, "renderParamSourceType");
|
|
1405
1228
|
function deriveMethodName(op, route) {
|
|
1406
1229
|
if (op.sdk) return `${toCSharpTypeName(op.sdk)}Async`;
|
|
1407
1230
|
if (op.name) return `${toCSharpTypeName(op.name)}Async`;
|
|
1408
1231
|
return `${inferMethodName(op.method, route.path)}Async`;
|
|
1409
1232
|
}
|
|
1410
|
-
__name(deriveMethodName, "deriveMethodName");
|
|
1411
1233
|
function inferMethodName(method, path) {
|
|
1412
|
-
const parts = [
|
|
1413
|
-
toCSharpTypeName(method)
|
|
1414
|
-
];
|
|
1234
|
+
const parts = [toCSharpTypeName(method)];
|
|
1415
1235
|
for (const segment of path.split("/").filter(Boolean)) {
|
|
1416
1236
|
if (segment.startsWith("{")) parts.push(`By${toCSharpTypeName(segment.slice(1, -1))}`);
|
|
1417
1237
|
else parts.push(toCSharpTypeName(segment));
|
|
1418
1238
|
}
|
|
1419
1239
|
return parts.join("");
|
|
1420
1240
|
}
|
|
1421
|
-
__name(inferMethodName, "inferMethodName");
|
|
1422
1241
|
|
|
1423
1242
|
// src/codegen-sdk.ts
|
|
1424
1243
|
function generateSdkCs(namespaceName, sdkName, clients) {
|
|
1425
|
-
const lines = [
|
|
1426
|
-
"// <auto-generated/>",
|
|
1427
|
-
"// Generated by @contractkit/plugin-csharp. Do not edit manually.",
|
|
1428
|
-
"#nullable enable",
|
|
1429
|
-
""
|
|
1430
|
-
];
|
|
1244
|
+
const lines = ["// <auto-generated/>", "// Generated by @contractkit/plugin-csharp. Do not edit manually.", "#nullable enable", ""];
|
|
1431
1245
|
lines.push("using System;");
|
|
1432
1246
|
if (clients.length > 0) lines.push(`using ${namespaceName}.Clients;`);
|
|
1433
1247
|
lines.push(`using ${namespaceName}.Runtime;`);
|
|
1434
1248
|
lines.push("");
|
|
1435
1249
|
lines.push(`namespace ${namespaceName};`);
|
|
1436
1250
|
lines.push("");
|
|
1437
|
-
lines.push(
|
|
1251
|
+
lines.push(
|
|
1252
|
+
...xmlDocLines(
|
|
1253
|
+
"Entry point to the generated SDK.\n\nHolds one SdkHttp, shared by every client, so the SDK keeps a single connection pool.\nDisposing it disposes the underlying HttpClient, unless you supplied your own.",
|
|
1254
|
+
""
|
|
1255
|
+
)
|
|
1256
|
+
);
|
|
1438
1257
|
lines.push(`public sealed class ${sdkName} : IDisposable`);
|
|
1439
1258
|
lines.push("{");
|
|
1440
1259
|
lines.push(` public ${sdkName}(SdkOptions options)`);
|
|
@@ -1457,7 +1276,6 @@ function generateSdkCs(namespaceName, sdkName, clients) {
|
|
|
1457
1276
|
lines.push("");
|
|
1458
1277
|
return lines.join("\n");
|
|
1459
1278
|
}
|
|
1460
|
-
__name(generateSdkCs, "generateSdkCs");
|
|
1461
1279
|
|
|
1462
1280
|
// src/hoist.ts
|
|
1463
1281
|
import { collectTypeRefs, resolveEffectiveFields as resolveEffectiveFields2 } from "@contractkit/core";
|
|
@@ -1480,14 +1298,8 @@ function collectHoistedTypes(roots, opts) {
|
|
|
1480
1298
|
}
|
|
1481
1299
|
}
|
|
1482
1300
|
}
|
|
1483
|
-
return {
|
|
1484
|
-
byNode: state.byNode,
|
|
1485
|
-
byName: state.byName,
|
|
1486
|
-
byFile: state.byFile,
|
|
1487
|
-
memberships: state.memberships
|
|
1488
|
-
};
|
|
1301
|
+
return { byNode: state.byNode, byName: state.byName, byFile: state.byFile, memberships: state.memberships };
|
|
1489
1302
|
}
|
|
1490
|
-
__name(collectHoistedTypes, "collectHoistedTypes");
|
|
1491
1303
|
function walkType(type, path, ownerFile, state, atAliasRoot, description) {
|
|
1492
1304
|
switch (type.kind) {
|
|
1493
1305
|
case "union":
|
|
@@ -1498,14 +1310,11 @@ function walkType(type, path, ownerFile, state, atAliasRoot, description) {
|
|
|
1498
1310
|
return;
|
|
1499
1311
|
case "enum":
|
|
1500
1312
|
if (!atAliasRoot) {
|
|
1501
|
-
hoist(
|
|
1502
|
-
|
|
1503
|
-
name: claimFor(path, state, false),
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
values: type.values,
|
|
1507
|
-
description
|
|
1508
|
-
}, state);
|
|
1313
|
+
hoist(
|
|
1314
|
+
type,
|
|
1315
|
+
{ kind: "enum", name: claimFor(path, state, false), ownerFile, needsInput: false, values: type.values, description },
|
|
1316
|
+
state
|
|
1317
|
+
);
|
|
1509
1318
|
}
|
|
1510
1319
|
return;
|
|
1511
1320
|
case "inlineObject":
|
|
@@ -1523,14 +1332,18 @@ function walkType(type, path, ownerFile, state, atAliasRoot, description) {
|
|
|
1523
1332
|
}
|
|
1524
1333
|
case "tuple":
|
|
1525
1334
|
type.items.forEach((item, i) => walkType(item, `${path}Item${i}`, ownerFile, state, false));
|
|
1526
|
-
hoist(
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1335
|
+
hoist(
|
|
1336
|
+
type,
|
|
1337
|
+
{
|
|
1338
|
+
kind: "tuple",
|
|
1339
|
+
name: claimFor(path, state, false),
|
|
1340
|
+
ownerFile,
|
|
1341
|
+
needsInput: type.items.some((t) => typeNeedsInput(t, state)),
|
|
1342
|
+
items: type.items,
|
|
1343
|
+
description
|
|
1344
|
+
},
|
|
1345
|
+
state
|
|
1346
|
+
);
|
|
1534
1347
|
return;
|
|
1535
1348
|
case "array":
|
|
1536
1349
|
walkType(type.item, path, ownerFile, state, false);
|
|
@@ -1545,20 +1358,22 @@ function walkType(type, path, ownerFile, state, atAliasRoot, description) {
|
|
|
1545
1358
|
return;
|
|
1546
1359
|
}
|
|
1547
1360
|
}
|
|
1548
|
-
__name(walkType, "walkType");
|
|
1549
1361
|
function hoistRecord(node, fields, path, ownerFile, state, description) {
|
|
1550
1362
|
const name = claimFor(path, state, false);
|
|
1551
1363
|
for (const f of fields) walkType(f.type, `${name}${toCSharpTypeName(f.name)}`, ownerFile, state, false, f.description);
|
|
1552
|
-
hoist(
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1364
|
+
hoist(
|
|
1365
|
+
node,
|
|
1366
|
+
{
|
|
1367
|
+
kind: "record",
|
|
1368
|
+
name,
|
|
1369
|
+
ownerFile,
|
|
1370
|
+
needsInput: fields.some((f) => f.visibility !== "normal" || typeNeedsInput(f.type, state)),
|
|
1371
|
+
fields,
|
|
1372
|
+
description
|
|
1373
|
+
},
|
|
1374
|
+
state
|
|
1375
|
+
);
|
|
1560
1376
|
}
|
|
1561
|
-
__name(hoistRecord, "hoistRecord");
|
|
1562
1377
|
function hoistPlainUnion(type, path, ownerFile, state, atAliasRoot, description) {
|
|
1563
1378
|
const nullable = type.members.some((m) => m.kind === "scalar" && m.name === "null");
|
|
1564
1379
|
const members = type.members.filter((m) => !(m.kind === "scalar" && m.name === "null"));
|
|
@@ -1568,15 +1383,7 @@ function hoistPlainUnion(type, path, ownerFile, state, atAliasRoot, description)
|
|
|
1568
1383
|
}
|
|
1569
1384
|
if (members.every((m) => m.kind === "literal" && typeof m.value === "string")) {
|
|
1570
1385
|
const values = members.map((m) => String(m.value));
|
|
1571
|
-
hoist(type, {
|
|
1572
|
-
kind: "enum",
|
|
1573
|
-
name: claimFor(path, state, atAliasRoot),
|
|
1574
|
-
ownerFile,
|
|
1575
|
-
needsInput: false,
|
|
1576
|
-
nullable,
|
|
1577
|
-
values,
|
|
1578
|
-
description
|
|
1579
|
-
}, state);
|
|
1386
|
+
hoist(type, { kind: "enum", name: claimFor(path, state, atAliasRoot), ownerFile, needsInput: false, nullable, values, description }, state);
|
|
1580
1387
|
return;
|
|
1581
1388
|
}
|
|
1582
1389
|
const name = claimFor(path, state, atAliasRoot);
|
|
@@ -1585,23 +1392,22 @@ function hoistPlainUnion(type, path, ownerFile, state, atAliasRoot, description)
|
|
|
1585
1392
|
for (const member of members) {
|
|
1586
1393
|
walkType(member, `${name}${toCSharpTypeName(memberLabel(member, state))}`, ownerFile, state, false);
|
|
1587
1394
|
const typeName = memberTypeName(member, state);
|
|
1588
|
-
hoisted.push({
|
|
1589
|
-
typeName,
|
|
1590
|
-
wrapperName: uniqueIn(`Of${toCSharpTypeName(memberLabel(member, state))}`, used),
|
|
1591
|
-
type: member
|
|
1592
|
-
});
|
|
1395
|
+
hoisted.push({ typeName, wrapperName: uniqueIn(`Of${toCSharpTypeName(memberLabel(member, state))}`, used), type: member });
|
|
1593
1396
|
}
|
|
1594
|
-
hoist(
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1397
|
+
hoist(
|
|
1398
|
+
type,
|
|
1399
|
+
{
|
|
1400
|
+
kind: "plainUnion",
|
|
1401
|
+
name,
|
|
1402
|
+
ownerFile,
|
|
1403
|
+
needsInput: members.some((m) => typeNeedsInput(m, state)),
|
|
1404
|
+
nullable,
|
|
1405
|
+
members: hoisted,
|
|
1406
|
+
description
|
|
1407
|
+
},
|
|
1408
|
+
state
|
|
1409
|
+
);
|
|
1603
1410
|
}
|
|
1604
|
-
__name(hoistPlainUnion, "hoistPlainUnion");
|
|
1605
1411
|
function hoistDiscriminatedUnion(type, path, ownerFile, state, atAliasRoot, description) {
|
|
1606
1412
|
const name = claimFor(path, state, atAliasRoot);
|
|
1607
1413
|
const members = [];
|
|
@@ -1610,17 +1416,16 @@ function hoistDiscriminatedUnion(type, path, ownerFile, state, atAliasRoot, desc
|
|
|
1610
1416
|
const discriminatorField = fields.find((f) => f.name === type.discriminator);
|
|
1611
1417
|
const tagType = discriminatorField?.type.kind === "lazy" ? discriminatorField.type.inner : discriminatorField?.type;
|
|
1612
1418
|
if (!tagType || tagType.kind !== "literal") {
|
|
1613
|
-
state.warn?.(
|
|
1419
|
+
state.warn?.(
|
|
1420
|
+
`Discriminated union '${name}' has a member whose '${type.discriminator}' is not a literal, so its tag is not known at build time; emitting a raw JSON value instead of an interface.`,
|
|
1421
|
+
ownerFile
|
|
1422
|
+
);
|
|
1614
1423
|
release(name, state, atAliasRoot);
|
|
1615
1424
|
return;
|
|
1616
1425
|
}
|
|
1617
1426
|
const tag = String(tagType.value);
|
|
1618
1427
|
if (member.kind === "ref") {
|
|
1619
|
-
members.push({
|
|
1620
|
-
typeName: member.name,
|
|
1621
|
-
tag,
|
|
1622
|
-
type: member
|
|
1623
|
-
});
|
|
1428
|
+
members.push({ typeName: member.name, tag, type: member });
|
|
1624
1429
|
} else {
|
|
1625
1430
|
const memberPath = `${name}${toCSharpTypeName(tag)}`;
|
|
1626
1431
|
hoistRecord(member, fields, memberPath, ownerFile, state, void 0);
|
|
@@ -1629,11 +1434,7 @@ function hoistDiscriminatedUnion(type, path, ownerFile, state, atAliasRoot, desc
|
|
|
1629
1434
|
release(name, state, atAliasRoot);
|
|
1630
1435
|
return;
|
|
1631
1436
|
}
|
|
1632
|
-
members.push({
|
|
1633
|
-
typeName: decl2.name,
|
|
1634
|
-
tag,
|
|
1635
|
-
type: member
|
|
1636
|
-
});
|
|
1437
|
+
members.push({ typeName: decl2.name, tag, type: member });
|
|
1637
1438
|
}
|
|
1638
1439
|
}
|
|
1639
1440
|
if (members.length === 0) {
|
|
@@ -1656,7 +1457,6 @@ function hoistDiscriminatedUnion(type, path, ownerFile, state, atAliasRoot, desc
|
|
|
1656
1457
|
state.memberships.set(member.typeName, list);
|
|
1657
1458
|
}
|
|
1658
1459
|
}
|
|
1659
|
-
__name(hoistDiscriminatedUnion, "hoistDiscriminatedUnion");
|
|
1660
1460
|
function memberLabel(type, state) {
|
|
1661
1461
|
switch (type.kind) {
|
|
1662
1462
|
case "ref":
|
|
@@ -1677,23 +1477,18 @@ function memberLabel(type, state) {
|
|
|
1677
1477
|
}
|
|
1678
1478
|
}
|
|
1679
1479
|
}
|
|
1680
|
-
__name(memberLabel, "memberLabel");
|
|
1681
1480
|
function memberTypeName(type, state) {
|
|
1682
1481
|
const decl = state.byNode.get(type);
|
|
1683
1482
|
if (decl) return decl.name;
|
|
1684
1483
|
if (type.kind === "ref") return type.name;
|
|
1685
1484
|
return "";
|
|
1686
1485
|
}
|
|
1687
|
-
__name(memberTypeName, "memberTypeName");
|
|
1688
1486
|
function typeNeedsInput(type, state) {
|
|
1689
1487
|
const refs = /* @__PURE__ */ new Set();
|
|
1690
1488
|
collectTypeRefs(type, refs);
|
|
1691
|
-
if ([
|
|
1692
|
-
...refs
|
|
1693
|
-
].some((r) => state.modelsWithInput.has(r))) return true;
|
|
1489
|
+
if ([...refs].some((r) => state.modelsWithInput.has(r))) return true;
|
|
1694
1490
|
return hasVisibilityField(type);
|
|
1695
1491
|
}
|
|
1696
|
-
__name(typeNeedsInput, "typeNeedsInput");
|
|
1697
1492
|
function hasVisibilityField(type) {
|
|
1698
1493
|
switch (type.kind) {
|
|
1699
1494
|
case "inlineObject":
|
|
@@ -1714,7 +1509,6 @@ function hasVisibilityField(type) {
|
|
|
1714
1509
|
return false;
|
|
1715
1510
|
}
|
|
1716
1511
|
}
|
|
1717
|
-
__name(hasVisibilityField, "hasVisibilityField");
|
|
1718
1512
|
function hoist(node, decl, state) {
|
|
1719
1513
|
state.byNode.set(node, decl);
|
|
1720
1514
|
state.byName.set(decl.name, decl);
|
|
@@ -1722,16 +1516,13 @@ function hoist(node, decl, state) {
|
|
|
1722
1516
|
list.push(decl);
|
|
1723
1517
|
state.byFile.set(decl.ownerFile, list);
|
|
1724
1518
|
}
|
|
1725
|
-
__name(hoist, "hoist");
|
|
1726
1519
|
function claimFor(path, state, atAliasRoot) {
|
|
1727
1520
|
if (atAliasRoot) return path;
|
|
1728
1521
|
return uniqueIn(sanitizeCSharpTypeName(path), state.taken);
|
|
1729
1522
|
}
|
|
1730
|
-
__name(claimFor, "claimFor");
|
|
1731
1523
|
function release(name, state, atAliasRoot) {
|
|
1732
1524
|
if (!atAliasRoot) state.taken.delete(name);
|
|
1733
1525
|
}
|
|
1734
|
-
__name(release, "release");
|
|
1735
1526
|
function uniqueIn(base, taken) {
|
|
1736
1527
|
if (!taken.has(base)) {
|
|
1737
1528
|
taken.add(base);
|
|
@@ -1742,7 +1533,6 @@ function uniqueIn(base, taken) {
|
|
|
1742
1533
|
taken.add(`${base}${n}`);
|
|
1743
1534
|
return `${base}${n}`;
|
|
1744
1535
|
}
|
|
1745
|
-
__name(uniqueIn, "uniqueIn");
|
|
1746
1536
|
|
|
1747
1537
|
// src/runtime.ts
|
|
1748
1538
|
function generateRuntimeCs(namespaceName) {
|
|
@@ -2117,7 +1907,6 @@ public sealed class SdkHttp : IDisposable
|
|
|
2117
1907
|
}
|
|
2118
1908
|
`;
|
|
2119
1909
|
}
|
|
2120
|
-
__name(generateRuntimeCs, "generateRuntimeCs");
|
|
2121
1910
|
|
|
2122
1911
|
// src/runtime-converters.ts
|
|
2123
1912
|
function generateConvertersCs(namespaceName) {
|
|
@@ -2253,7 +2042,6 @@ public sealed class IsoTimeSpanConverter : JsonConverter<TimeSpan>
|
|
|
2253
2042
|
}
|
|
2254
2043
|
`;
|
|
2255
2044
|
}
|
|
2256
|
-
__name(generateConvertersCs, "generateConvertersCs");
|
|
2257
2045
|
|
|
2258
2046
|
// src/scaffold.ts
|
|
2259
2047
|
var SCAFFOLD_VERSIONS = {
|
|
@@ -2274,7 +2062,6 @@ function generateCsproj(namespaceName, sdkName) {
|
|
|
2274
2062
|
</Project>
|
|
2275
2063
|
`;
|
|
2276
2064
|
}
|
|
2277
|
-
__name(generateCsproj, "generateCsproj");
|
|
2278
2065
|
|
|
2279
2066
|
// src/index.ts
|
|
2280
2067
|
var CSHARP_CODEGEN_VERSION = "1";
|
|
@@ -2298,14 +2085,15 @@ function createCSharpSdkPlugin(config, rootDir) {
|
|
|
2298
2085
|
}
|
|
2299
2086
|
};
|
|
2300
2087
|
}
|
|
2301
|
-
__name(createCSharpSdkPlugin, "createCSharpSdkPlugin");
|
|
2302
2088
|
var NAMESPACE_RE = /^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$/;
|
|
2303
2089
|
var SDK_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
2304
2090
|
function assertValidConfig(config) {
|
|
2305
2091
|
const { namespace, sdkName } = config;
|
|
2306
2092
|
if (namespace !== void 0) {
|
|
2307
2093
|
if (typeof namespace !== "string" || !NAMESPACE_RE.test(namespace)) {
|
|
2308
|
-
throw new Error(
|
|
2094
|
+
throw new Error(
|
|
2095
|
+
`plugin-csharp: namespace '${String(namespace)}' is not a valid C# namespace \u2014 expected dot-separated identifiers, e.g. 'Acme.Sdk'.`
|
|
2096
|
+
);
|
|
2309
2097
|
}
|
|
2310
2098
|
const keyword = namespace.split(".").find((segment) => CSHARP_KEYWORDS.has(segment));
|
|
2311
2099
|
if (keyword) {
|
|
@@ -2320,17 +2108,13 @@ function assertValidConfig(config) {
|
|
|
2320
2108
|
throw new Error(`plugin-csharp: sdkName '${sdkName}' is a C# keyword.`);
|
|
2321
2109
|
}
|
|
2322
2110
|
}
|
|
2323
|
-
for (const key of [
|
|
2324
|
-
"includeInternal",
|
|
2325
|
-
"scaffold"
|
|
2326
|
-
]) {
|
|
2111
|
+
for (const key of ["includeInternal", "scaffold"]) {
|
|
2327
2112
|
const value = config[key];
|
|
2328
2113
|
if (value !== void 0 && typeof value !== "boolean") {
|
|
2329
2114
|
throw new Error(`plugin-csharp: ${key} must be a boolean \u2014 got ${JSON.stringify(value)}.`);
|
|
2330
2115
|
}
|
|
2331
2116
|
}
|
|
2332
2117
|
}
|
|
2333
|
-
__name(assertValidConfig, "assertValidConfig");
|
|
2334
2118
|
async function runCSharpCodegen(inputs, ctx, config, rootDir) {
|
|
2335
2119
|
assertValidConfig(config);
|
|
2336
2120
|
const { contractRoots } = inputs;
|
|
@@ -2341,13 +2125,11 @@ async function runCSharpCodegen(inputs, ctx, config, rootDir) {
|
|
|
2341
2125
|
const allModels = contractRoots.flatMap((root) => root.models);
|
|
2342
2126
|
const modelIndex = buildModelIndex2(allModels);
|
|
2343
2127
|
const modelsWithInput = resolveModelsWithInput(allModels, inputs.modelsWithInput);
|
|
2344
|
-
const modelsWithInputArray = [
|
|
2345
|
-
...modelsWithInput
|
|
2346
|
-
].sort();
|
|
2128
|
+
const modelsWithInputArray = [...modelsWithInput].sort();
|
|
2347
2129
|
const hoisted = collectHoistedTypes(contractRoots, {
|
|
2348
2130
|
modelIndex,
|
|
2349
2131
|
modelsWithInput,
|
|
2350
|
-
warn:
|
|
2132
|
+
warn: (message, file) => ctx.warn?.(message, file)
|
|
2351
2133
|
});
|
|
2352
2134
|
const prevManifest = ctx.cacheEnabled ? readManifest(manifestPath) : emptyIncrementalManifest(CSHARP_CODEGEN_VERSION);
|
|
2353
2135
|
const units = [];
|
|
@@ -2357,20 +2139,9 @@ async function runCSharpCodegen(inputs, ctx, config, rootDir) {
|
|
|
2357
2139
|
const ownNames = new Set(root.models.map((m) => m.name));
|
|
2358
2140
|
const referenced = referencedModelNames(root);
|
|
2359
2141
|
const relevantInputModels = modelsWithInputArray.filter((name) => ownNames.has(name) || referenced.has(name));
|
|
2360
|
-
const externalBases = [
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
const ownedDeclarations = (hoisted.byFile.get(root.file) ?? []).map((d) => ({
|
|
2364
|
-
kind: d.kind,
|
|
2365
|
-
name: d.name,
|
|
2366
|
-
needsInput: d.needsInput
|
|
2367
|
-
}));
|
|
2368
|
-
const declaredMemberships = [
|
|
2369
|
-
...ownNames
|
|
2370
|
-
].sort().map((name) => [
|
|
2371
|
-
name,
|
|
2372
|
-
hoisted.memberships.get(name) ?? []
|
|
2373
|
-
]).filter(([, unions]) => unions.length > 0);
|
|
2142
|
+
const externalBases = [...referenced].filter((name) => !ownNames.has(name)).sort().map((name) => modelIndex.get(name)).filter((m) => m !== void 0);
|
|
2143
|
+
const ownedDeclarations = (hoisted.byFile.get(root.file) ?? []).map((d) => ({ kind: d.kind, name: d.name, needsInput: d.needsInput }));
|
|
2144
|
+
const declaredMemberships = [...ownNames].sort().map((name) => [name, hoisted.memberships.get(name) ?? []]).filter(([, unions]) => unions.length > 0);
|
|
2374
2145
|
const fingerprint = hashFingerprint({
|
|
2375
2146
|
kind: "models",
|
|
2376
2147
|
v: CSHARP_CODEGEN_VERSION,
|
|
@@ -2385,7 +2156,7 @@ async function runCSharpCodegen(inputs, ctx, config, rootDir) {
|
|
|
2385
2156
|
units.push({
|
|
2386
2157
|
key: `models::${relPath}`,
|
|
2387
2158
|
fingerprint,
|
|
2388
|
-
render:
|
|
2159
|
+
render: () => [
|
|
2389
2160
|
{
|
|
2390
2161
|
relativePath: relPath,
|
|
2391
2162
|
content: generateCSharpModels(root, {
|
|
@@ -2393,24 +2164,19 @@ async function runCSharpCodegen(inputs, ctx, config, rootDir) {
|
|
|
2393
2164
|
modelsWithInput,
|
|
2394
2165
|
modelIndex,
|
|
2395
2166
|
hoisted,
|
|
2396
|
-
warn:
|
|
2167
|
+
warn: (message) => ctx.warn?.(message, root.file)
|
|
2397
2168
|
})
|
|
2398
2169
|
}
|
|
2399
|
-
]
|
|
2170
|
+
]
|
|
2400
2171
|
});
|
|
2401
2172
|
}
|
|
2402
2173
|
for (const root of inputs.opRoots) {
|
|
2403
2174
|
if (!hasPublicOperations(root, config.includeInternal)) continue;
|
|
2404
2175
|
const relPath = `Clients/${deriveClientClassName(root.file)}.cs`;
|
|
2405
|
-
clients.push({
|
|
2406
|
-
className: deriveClientClassName(root.file),
|
|
2407
|
-
propertyName: deriveClientPropertyName(root.file)
|
|
2408
|
-
});
|
|
2176
|
+
clients.push({ className: deriveClientClassName(root.file), propertyName: deriveClientPropertyName(root.file) });
|
|
2409
2177
|
const referenced = referencedOpModels(root, modelIndex);
|
|
2410
2178
|
const relevantInputModels = modelsWithInputArray.filter((name) => referenced.has(name));
|
|
2411
|
-
const referencedModels = [
|
|
2412
|
-
...referenced
|
|
2413
|
-
].sort().map((name) => modelIndex.get(name)).filter((m) => m !== void 0);
|
|
2179
|
+
const referencedModels = [...referenced].sort().map((name) => modelIndex.get(name)).filter((m) => m !== void 0);
|
|
2414
2180
|
const fingerprint = hashFingerprint({
|
|
2415
2181
|
kind: "client",
|
|
2416
2182
|
v: CSHARP_CODEGEN_VERSION,
|
|
@@ -2424,7 +2190,7 @@ async function runCSharpCodegen(inputs, ctx, config, rootDir) {
|
|
|
2424
2190
|
units.push({
|
|
2425
2191
|
key: `client::${relPath}`,
|
|
2426
2192
|
fingerprint,
|
|
2427
|
-
render:
|
|
2193
|
+
render: () => [
|
|
2428
2194
|
{
|
|
2429
2195
|
relativePath: relPath,
|
|
2430
2196
|
content: generateCSharpClient(root, {
|
|
@@ -2433,60 +2199,41 @@ async function runCSharpCodegen(inputs, ctx, config, rootDir) {
|
|
|
2433
2199
|
modelIndex,
|
|
2434
2200
|
hoisted,
|
|
2435
2201
|
includeInternal: config.includeInternal,
|
|
2436
|
-
warn:
|
|
2202
|
+
warn: (message) => ctx.warn?.(message, root.file)
|
|
2437
2203
|
})
|
|
2438
2204
|
}
|
|
2439
|
-
]
|
|
2205
|
+
]
|
|
2440
2206
|
});
|
|
2441
2207
|
}
|
|
2442
2208
|
const globalFiles = [
|
|
2443
|
-
{
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
},
|
|
2447
|
-
{
|
|
2448
|
-
relativePath: "Runtime/SdkRuntime.cs",
|
|
2449
|
-
content: generateRuntimeCs(namespaceName)
|
|
2450
|
-
},
|
|
2451
|
-
{
|
|
2452
|
-
relativePath: `${sdkName}.cs`,
|
|
2453
|
-
content: generateSdkCs(namespaceName, sdkName, clients)
|
|
2454
|
-
}
|
|
2209
|
+
{ relativePath: "Runtime/Converters.cs", content: generateConvertersCs(namespaceName) },
|
|
2210
|
+
{ relativePath: "Runtime/SdkRuntime.cs", content: generateRuntimeCs(namespaceName) },
|
|
2211
|
+
{ relativePath: `${sdkName}.cs`, content: generateSdkCs(namespaceName, sdkName, clients) }
|
|
2455
2212
|
];
|
|
2456
2213
|
if (config.scaffold) {
|
|
2457
|
-
globalFiles.push({
|
|
2458
|
-
relativePath: `${sdkName}.csproj`,
|
|
2459
|
-
content: generateCsproj(namespaceName, sdkName),
|
|
2460
|
-
ifAbsent: true
|
|
2461
|
-
});
|
|
2214
|
+
globalFiles.push({ relativePath: `${sdkName}.csproj`, content: generateCsproj(namespaceName, sdkName), ifAbsent: true });
|
|
2462
2215
|
}
|
|
2463
2216
|
const result = runIncrementalCodegen({
|
|
2464
2217
|
codegenVersion: CSHARP_CODEGEN_VERSION,
|
|
2465
2218
|
prevManifest,
|
|
2466
2219
|
globalFiles,
|
|
2467
2220
|
units,
|
|
2468
|
-
fileExists:
|
|
2221
|
+
fileExists: (relPath) => existsSync(resolve(outDir, relPath))
|
|
2469
2222
|
});
|
|
2470
2223
|
deleteStalePaths(outDir, result.deletedPaths);
|
|
2471
2224
|
for (const { relativePath, content, ifAbsent } of result.filesToWrite) {
|
|
2472
|
-
ctx.emitFile(resolve(outDir, relativePath), content, ifAbsent ? {
|
|
2473
|
-
ifAbsent: true
|
|
2474
|
-
} : void 0);
|
|
2225
|
+
ctx.emitFile(resolve(outDir, relativePath), content, ifAbsent ? { ifAbsent: true } : void 0);
|
|
2475
2226
|
}
|
|
2476
2227
|
writeManifest(manifestPath, result.manifest);
|
|
2477
2228
|
}
|
|
2478
|
-
__name(runCSharpCodegen, "runCSharpCodegen");
|
|
2479
2229
|
function referencedOpModels(root, modelIndex) {
|
|
2480
2230
|
const seeds = [];
|
|
2481
|
-
const addParamSource =
|
|
2231
|
+
const addParamSource = (source) => {
|
|
2482
2232
|
if (!source) return;
|
|
2483
2233
|
if (source.kind === "params") seeds.push(...source.nodes.map((n) => n.type));
|
|
2484
|
-
else if (source.kind === "ref") seeds.push({
|
|
2485
|
-
kind: "ref",
|
|
2486
|
-
name: source.name
|
|
2487
|
-
});
|
|
2234
|
+
else if (source.kind === "ref") seeds.push({ kind: "ref", name: source.name });
|
|
2488
2235
|
else seeds.push(source.node);
|
|
2489
|
-
}
|
|
2236
|
+
};
|
|
2490
2237
|
for (const route of root.routes) {
|
|
2491
2238
|
addParamSource(route.params);
|
|
2492
2239
|
for (const op of route.operations) {
|
|
@@ -2501,7 +2248,6 @@ function referencedOpModels(root, modelIndex) {
|
|
|
2501
2248
|
}
|
|
2502
2249
|
return collectTransitiveModelRefs(seeds, modelIndex);
|
|
2503
2250
|
}
|
|
2504
|
-
__name(referencedOpModels, "referencedOpModels");
|
|
2505
2251
|
function referencedModelNames(root) {
|
|
2506
2252
|
const refs = /* @__PURE__ */ new Set();
|
|
2507
2253
|
for (const model of root.models) {
|
|
@@ -2511,7 +2257,6 @@ function referencedModelNames(root) {
|
|
|
2511
2257
|
}
|
|
2512
2258
|
return refs;
|
|
2513
2259
|
}
|
|
2514
|
-
__name(referencedModelNames, "referencedModelNames");
|
|
2515
2260
|
function readManifest(manifestPath) {
|
|
2516
2261
|
if (!existsSync(manifestPath)) return emptyIncrementalManifest(CSHARP_CODEGEN_VERSION);
|
|
2517
2262
|
try {
|
|
@@ -2520,26 +2265,20 @@ function readManifest(manifestPath) {
|
|
|
2520
2265
|
return emptyIncrementalManifest(CSHARP_CODEGEN_VERSION);
|
|
2521
2266
|
}
|
|
2522
2267
|
}
|
|
2523
|
-
__name(readManifest, "readManifest");
|
|
2524
2268
|
function writeManifest(manifestPath, manifest) {
|
|
2525
2269
|
try {
|
|
2526
|
-
mkdirSync(dirname(manifestPath), {
|
|
2527
|
-
recursive: true
|
|
2528
|
-
});
|
|
2270
|
+
mkdirSync(dirname(manifestPath), { recursive: true });
|
|
2529
2271
|
writeFileSync(manifestPath, serializeIncrementalManifest(manifest), "utf-8");
|
|
2530
2272
|
} catch {
|
|
2531
2273
|
}
|
|
2532
2274
|
}
|
|
2533
|
-
__name(writeManifest, "writeManifest");
|
|
2534
2275
|
function deleteStalePaths(outDir, relPaths) {
|
|
2535
2276
|
if (relPaths.length === 0) return;
|
|
2536
2277
|
const removedDirs = /* @__PURE__ */ new Set();
|
|
2537
2278
|
for (const rel of relPaths) {
|
|
2538
2279
|
const abs = resolve(outDir, rel);
|
|
2539
2280
|
if (existsSync(abs)) {
|
|
2540
|
-
rmSync(abs, {
|
|
2541
|
-
force: true
|
|
2542
|
-
});
|
|
2281
|
+
rmSync(abs, { force: true });
|
|
2543
2282
|
removedDirs.add(join(abs, ".."));
|
|
2544
2283
|
}
|
|
2545
2284
|
}
|
|
@@ -2559,7 +2298,6 @@ function deleteStalePaths(outDir, relPaths) {
|
|
|
2559
2298
|
}
|
|
2560
2299
|
}
|
|
2561
2300
|
}
|
|
2562
|
-
__name(deleteStalePaths, "deleteStalePaths");
|
|
2563
2301
|
export {
|
|
2564
2302
|
CSHARP_CODEGEN_VERSION,
|
|
2565
2303
|
assertValidConfig,
|