@contractkit/plugin-csharp 0.1.4 → 0.1.5
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 +18 -0
- package/dist/codegen-models.d.ts.map +1 -1
- package/dist/index.js +7 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/codegen-models.ts +17 -4
- package/tests/codegen-models.test.ts +9 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contractkit/plugin-csharp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "ContractKit built-in plugin: C#/.NET SDK client generation (System.Text.Json + HttpClient, no NuGet dependencies)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
".": "./dist/index.js"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@contractkit/core": "0.
|
|
32
|
+
"@contractkit/core": "0.31.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"unplugin-swc": "^1.5.9",
|
package/src/codegen-models.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ContractRootNode, ContractTypeNode, FieldNode, ModelNode, ScalarTypeNode } from '@contractkit/core';
|
|
1
|
+
import type { ContractRootNode, ContractTypeNode, FieldDefault, FieldNode, ModelNode, ScalarTypeNode } from '@contractkit/core';
|
|
2
2
|
import { buildModelIndex, computeModelsWithInput, resolveEffectiveFields, topoSortModels } from '@contractkit/core';
|
|
3
3
|
import type { HoistedDecl, HoistResult } from './hoist.js';
|
|
4
4
|
import { quoteCSharpString, safeMemberName, toCSharpEnumMemberName, toCSharpPropertyName, xmlDocLines } from './naming.js';
|
|
@@ -272,6 +272,18 @@ function literalCSharpType(value: string | number | boolean, ctx: RenderContext)
|
|
|
272
272
|
|
|
273
273
|
// ─── Default values ────────────────────────────────────────────────────────
|
|
274
274
|
|
|
275
|
+
const MIN_SAFE_BIGINT = BigInt(Number.MIN_SAFE_INTEGER);
|
|
276
|
+
const MAX_SAFE_BIGINT = BigInt(Number.MAX_SAFE_INTEGER);
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Whether a `bigint` field's default is written as `new BigInteger(<literal>)` rather than parsed
|
|
280
|
+
* from a string. A number past 2**53 has already lost digits, so it cannot be written as a literal;
|
|
281
|
+
* a bigint keeps the same cutoff so a value renders one way whichever form it arrives in.
|
|
282
|
+
*/
|
|
283
|
+
function isSafeInteger(value: number | bigint): boolean {
|
|
284
|
+
return typeof value === 'bigint' ? value >= MIN_SAFE_BIGINT && value <= MAX_SAFE_BIGINT : Number.isSafeInteger(value);
|
|
285
|
+
}
|
|
286
|
+
|
|
275
287
|
/**
|
|
276
288
|
* Render a contract default as a C# expression of the field's own type. Returns `undefined` when the
|
|
277
289
|
* value cannot be expressed, so the field is emitted as `required` rather than with an initializer
|
|
@@ -284,7 +296,7 @@ function literalCSharpType(value: string | number | boolean, ctx: RenderContext)
|
|
|
284
296
|
* `Nullable<Rating>`. An enum a member would shadow is written from the global namespace instead.
|
|
285
297
|
*/
|
|
286
298
|
function renderDefault(
|
|
287
|
-
value:
|
|
299
|
+
value: FieldDefault,
|
|
288
300
|
type: ContractTypeNode,
|
|
289
301
|
ctx: RenderContext,
|
|
290
302
|
memberNames: ReadonlySet<string> = new Set(),
|
|
@@ -295,7 +307,8 @@ function renderDefault(
|
|
|
295
307
|
|
|
296
308
|
if (typeof value === 'boolean') return String(value);
|
|
297
309
|
|
|
298
|
-
|
|
310
|
+
// A `bigint` value comes from a `bigint` field, whose case below writes it from its exact digits.
|
|
311
|
+
if (typeof value === 'number' || typeof value === 'bigint') {
|
|
299
312
|
if (inner.kind === 'scalar') {
|
|
300
313
|
switch (inner.name) {
|
|
301
314
|
case 'int':
|
|
@@ -305,7 +318,7 @@ function renderDefault(
|
|
|
305
318
|
case 'decimal':
|
|
306
319
|
return `${value}m`;
|
|
307
320
|
case 'bigint':
|
|
308
|
-
return
|
|
321
|
+
return isSafeInteger(value) ? `new BigInteger(${value})` : `BigInteger.Parse("${value}")`;
|
|
309
322
|
}
|
|
310
323
|
}
|
|
311
324
|
return Number.isInteger(value) ? `${value}L` : `${value}d`;
|
|
@@ -133,6 +133,15 @@ describe('optional, nullable, default and literal', () => {
|
|
|
133
133
|
expect(render(one('M', field('f', scalarType('bigint'), { default: 7 })))).toContain('= new BigInteger(7);');
|
|
134
134
|
});
|
|
135
135
|
|
|
136
|
+
it('initializes a bigint default from its exact digits', () => {
|
|
137
|
+
// The AST carries a bigint default as a JS bigint: small ones keep the constructor a number
|
|
138
|
+
// default gets, and one past 2**53 is parsed from the digits a number could not hold.
|
|
139
|
+
expect(render(one('M', field('f', scalarType('bigint'), { default: 7n })))).toContain('= new BigInteger(7);');
|
|
140
|
+
expect(render(one('M', field('f', scalarType('bigint'), { default: 9007199254740993n })))).toContain(
|
|
141
|
+
'= BigInteger.Parse("9007199254740993");',
|
|
142
|
+
);
|
|
143
|
+
});
|
|
144
|
+
|
|
136
145
|
it('leaves a field required when its default has no C# literal form', () => {
|
|
137
146
|
const out = render(one('M', field('f', scalarType('uuid'), { default: 'not-a-literal' })));
|
|
138
147
|
expect(out).toContain('public required Guid F { get; init; }');
|