@game-infra/valibot-to-csharp 0.0.0 → 0.2.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/LICENSE +21 -0
- package/README.md +159 -31
- package/dist/cli.js.map +1 -1
- package/dist/csharp.d.ts +65 -0
- package/dist/csharp.d.ts.map +1 -0
- package/dist/csharp.js +147 -0
- package/dist/csharp.js.map +1 -0
- package/dist/emitter.d.ts +8 -13
- package/dist/emitter.d.ts.map +1 -1
- package/dist/emitter.js +298 -183
- package/dist/emitter.js.map +1 -1
- package/dist/generate.d.ts.map +1 -1
- package/dist/generate.js +2 -2
- package/dist/generate.js.map +1 -1
- package/dist/ir.d.ts +39 -6
- package/dist/ir.d.ts.map +1 -1
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +193 -44
- package/dist/parser.js.map +1 -1
- package/dist/variants.d.ts +63 -0
- package/dist/variants.d.ts.map +1 -0
- package/dist/variants.js +63 -0
- package/dist/variants.js.map +1 -0
- package/package.json +17 -7
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Igor Savin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -8,36 +8,35 @@ We author JSON schemas in TypeScript (in editors and web tooling) but consume th
|
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
11
|
-
This
|
|
11
|
+
This one is published, unlike the rest of the repo's tooling: a game repo that generates C# from its own schemas needs the generator without checking out `game-infra`.
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
pnpm add -D @game-infra/valibot-to-csharp
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
A sibling checkout still works and is what you want while changing the generator itself (see `docs/CONSUMING.md` in the repo root):
|
|
12
18
|
|
|
13
19
|
```jsonc
|
|
14
20
|
// pnpm
|
|
15
21
|
"devDependencies": {
|
|
16
22
|
"@game-infra/valibot-to-csharp": "link:../../../game-infra/packages/valibot-to-csharp"
|
|
17
23
|
}
|
|
18
|
-
|
|
19
|
-
// npm
|
|
20
|
-
"devDependencies": {
|
|
21
|
-
"@game-infra/valibot-to-csharp": "file:../../../game-infra/packages/valibot-to-csharp"
|
|
22
|
-
}
|
|
23
24
|
```
|
|
24
25
|
|
|
25
26
|
## CLI
|
|
26
27
|
|
|
27
|
-
There is no published dist, so run the CLI straight from the sibling checkout's source with a TS runner such as tsx. Plain `node src/cli.ts` does not work: Node's type stripping refuses to map the `.js` relative import specifiers this repo uses onto their `.ts` sources.
|
|
28
|
-
|
|
29
28
|
```sh
|
|
30
|
-
npx
|
|
29
|
+
npx valibot-to-csharp \
|
|
31
30
|
--input ./schemas/events.ts \
|
|
32
31
|
--input ./schemas/common.ts \
|
|
33
32
|
--output ./Generated \
|
|
34
33
|
--namespace MyGame.Events
|
|
35
34
|
```
|
|
36
35
|
|
|
37
|
-
|
|
36
|
+
From a sibling checkout there is no `dist`, so run the source with a TS runner such as tsx. Plain `node src/cli.ts` does not work: Node's type stripping refuses to map the `.js` relative import specifiers this repo uses onto their `.ts` sources.
|
|
38
37
|
|
|
39
38
|
```sh
|
|
40
|
-
|
|
39
|
+
npx tsx ../game-infra/packages/valibot-to-csharp/src/cli.ts --input ./schemas/events.ts --output ./Generated --namespace MyGame.Events
|
|
41
40
|
```
|
|
42
41
|
|
|
43
42
|
Flags:
|
|
@@ -89,28 +88,152 @@ import { emitFileHeader, emitModule, parseFiles } from "@game-infra/valibot-to-c
|
|
|
89
88
|
|
|
90
89
|
const module = parseFiles(["./schemas/events.ts"]);
|
|
91
90
|
const emit = emitModule(module, { namespace: "MyGame.Events" });
|
|
92
|
-
|
|
91
|
+
// Both note channels: what the parser could not read, and what the emitter could not
|
|
92
|
+
// render faithfully. Each one is something the generated file no longer carries.
|
|
93
|
+
const csSource = emitFileHeader([...module.notes, ...emit.notes]) + emit.source;
|
|
93
94
|
```
|
|
94
95
|
|
|
95
96
|
## Supported valibot surface
|
|
96
97
|
|
|
97
|
-
| valibot
|
|
98
|
-
|
|
|
99
|
-
| `string()`
|
|
100
|
-
| `number()`
|
|
101
|
-
| `
|
|
102
|
-
| `
|
|
103
|
-
| `
|
|
104
|
-
| `
|
|
105
|
-
| `
|
|
106
|
-
| `
|
|
107
|
-
| `
|
|
108
|
-
| `
|
|
109
|
-
| `
|
|
110
|
-
| `
|
|
111
|
-
| `
|
|
112
|
-
| `
|
|
113
|
-
| `
|
|
98
|
+
| valibot | C# |
|
|
99
|
+
| ----------------------------------------------- | -------------------------------------------------------- |
|
|
100
|
+
| `string()` | `string` |
|
|
101
|
+
| `number()` | `double` |
|
|
102
|
+
| `pipe(number(), integer(), minValue, maxValue)` | `int` when both bounds fit `Int32`, otherwise `long` |
|
|
103
|
+
| `boolean()` | `bool` |
|
|
104
|
+
| `unknown()` | `JsonElement` |
|
|
105
|
+
| `array(X)` | `IReadOnlyList<X>` |
|
|
106
|
+
| `record(string(), V)` | `IReadOnlyDictionary<string, V>` |
|
|
107
|
+
| `object`, `strictObject` | `public sealed record` (closed) |
|
|
108
|
+
| `looseObject` | `public sealed record` + `[JsonExtensionData]` bag |
|
|
109
|
+
| `nullable(X)` | Nullable field (`X?`) |
|
|
110
|
+
| `optional(X)` | Nullable field, omitted when null |
|
|
111
|
+
| `literal('a')` | String literal (merged into enums/variant tags) |
|
|
112
|
+
| `literal(1)` / `literal(true)` | Constant `int`/`long`/`double` / `bool` field |
|
|
113
|
+
| `picklist(['a','b'])` | `[JsonConverter(typeof(JsonStringEnumConverter))] enum` |
|
|
114
|
+
| `union([literal(...), ...])` | Enum (string literals only) |
|
|
115
|
+
| `union([string(), array(string())])` | `StringOrStringList` helper (auto-emitted) |
|
|
116
|
+
| `union([{type:'A',...}, ...])` | `[JsonPolymorphic] abstract record` with derived records |
|
|
117
|
+
| `variant('type', [...])` | Same as discriminated union |
|
|
118
|
+
| `pipe(inner, ...validators)` | `inner`, plus the numeric refinements above |
|
|
119
|
+
|
|
120
|
+
### Numbers
|
|
121
|
+
|
|
122
|
+
A bare `number()` is a `double`, which is the honest mapping and the wrong one
|
|
123
|
+
for anything a digest is taken over: `double` round-trips `1e+21`, and .NET and
|
|
124
|
+
JavaScript disagree on how to spell that. Pipe it through `integer()` and the
|
|
125
|
+
emitted type is integral. It narrows to `int` only when `minValue` and `maxValue`
|
|
126
|
+
both bound it inside `System.Int32` — JavaScript integers run to 2^53, so an
|
|
127
|
+
unbounded one held in an `int` would refuse every value past two billion, which
|
|
128
|
+
is a whole class of documents rejected rather than a rounding error.
|
|
129
|
+
|
|
130
|
+
A numeric `literal(n)` is bounded by definition — the value is both bounds — so it
|
|
131
|
+
takes its type by the same rule: `literal(1)` is an `int`, `literal(3000000000)` a
|
|
132
|
+
`long`, and `literal(1.5)` a `double`.
|
|
133
|
+
|
|
134
|
+
### Absent versus null
|
|
135
|
+
|
|
136
|
+
`optional(X)` and `nullable(X)` are both `X?` in C#, and they are not the same
|
|
137
|
+
fact on the wire: the first says the key may be absent, the second says the value
|
|
138
|
+
may be `null`. Optional fields therefore carry
|
|
139
|
+
`[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]`, so serialising
|
|
140
|
+
one with no value omits the key instead of writing a `null` that a schema
|
|
141
|
+
accepting only absence would refuse. Nullable fields keep writing `null`.
|
|
142
|
+
|
|
143
|
+
### Unknown keys
|
|
144
|
+
|
|
145
|
+
`object()` ignores entries its shape does not declare and `strictObject()` rejects
|
|
146
|
+
them, so a closed C# record loses nothing. `looseObject()` keeps them, so its record
|
|
147
|
+
carries a `[JsonExtensionData]` bag:
|
|
148
|
+
|
|
149
|
+
```csharp
|
|
150
|
+
public sealed record Envelope(
|
|
151
|
+
[property: JsonPropertyName("id")] string Id
|
|
152
|
+
)
|
|
153
|
+
{
|
|
154
|
+
[JsonExtensionData]
|
|
155
|
+
public Dictionary<string, JsonElement>? AdditionalProperties { get; set; }
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
The bag is declared in the record body rather than as a positional parameter:
|
|
160
|
+
`System.Text.Json` refuses an extension-data property bound to a constructor
|
|
161
|
+
parameter, and every positional record parameter is one.
|
|
162
|
+
|
|
163
|
+
### Which field is the discriminator
|
|
164
|
+
|
|
165
|
+
A `variant('op', [...])` states its discriminator, and the emitter takes it at its
|
|
166
|
+
word: a member is tagged by its `op` literal and by nothing else. Only a bare
|
|
167
|
+
`union([...])`, which declares nothing, infers one — a string literal field,
|
|
168
|
+
preferring one named `type`.
|
|
169
|
+
|
|
170
|
+
The difference shows up the moment members share an envelope that carries a string
|
|
171
|
+
literal of its own:
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
const envelope = { type: literal("op"), at: string() };
|
|
175
|
+
export const MoveOpSchema = object({ ...envelope, op: literal("move") });
|
|
176
|
+
export const HaltOpSchema = object({ ...envelope, op: literal("halt") });
|
|
177
|
+
export const OpSchema = variant("op", [MoveOpSchema, HaltOpSchema]);
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Tagging by `type` would give both members the `[JsonDerivedType]` value `"op"`, which
|
|
181
|
+
`System.Text.Json` refuses when it builds the converter, and would leave the real `op`
|
|
182
|
+
key off the wire entirely. Two members that do end up claiming the same tag — an
|
|
183
|
+
ambiguity in the schema itself — are reported in the generated header, and only the
|
|
184
|
+
first is emitted.
|
|
185
|
+
|
|
186
|
+
### Referring to one member of a variant
|
|
187
|
+
|
|
188
|
+
A field pointing at a single member (`last: optional(MoveOpSchema)`) is typed as the
|
|
189
|
+
polymorphic **base**, not as the member:
|
|
190
|
+
|
|
191
|
+
```csharp
|
|
192
|
+
[property: JsonPropertyName("last")] Op? Last
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
The discriminator is written by `JsonPolymorphic` on the base and by nothing else, so
|
|
196
|
+
a field declared as the derived record would serialise with no `op` key at all. The
|
|
197
|
+
base deserialises straight back into the member; the widening is noted in the
|
|
198
|
+
generated file's header.
|
|
199
|
+
|
|
200
|
+
### Shared fields on a union's base
|
|
201
|
+
|
|
202
|
+
Fields that every member of a `variant`/discriminated union declares identically — same name, same
|
|
203
|
+
nullability, same shape — are hoisted onto the abstract base record, and each member passes them
|
|
204
|
+
through as a plain constructor parameter. A shared envelope is then readable off the base type
|
|
205
|
+
instead of through a switch over every member:
|
|
206
|
+
|
|
207
|
+
```csharp
|
|
208
|
+
foreach (var e in events) Console.WriteLine(e.Seq); // declared once, on MatchEvent
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
A field only some members have, or one whose shape differs between them, stays on the members that
|
|
212
|
+
have it.
|
|
213
|
+
|
|
214
|
+
### Shared field groups
|
|
215
|
+
|
|
216
|
+
Fields common to several schemas can live in a plain object literal and be
|
|
217
|
+
spread into each one; the parser resolves the spread against module-level
|
|
218
|
+
literals in the same file, and a later field of the same name wins as it does in
|
|
219
|
+
JavaScript.
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
const eventEnvelope = { seq: seqSchema, createdAt: string() };
|
|
223
|
+
|
|
224
|
+
export const JoinedSchema = strictObject({
|
|
225
|
+
...eventEnvelope,
|
|
226
|
+
type: literal("joined"),
|
|
227
|
+
});
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
A spread it cannot resolve — a value from another module, a call, a `.entries`
|
|
231
|
+
access — contributes nothing rather than failing, so keep groups local and literal.
|
|
232
|
+
Every unresolved spread is reported in the generated file's header, so a missing
|
|
233
|
+
envelope is visible in the artifact rather than only in the diff of the C# it
|
|
234
|
+
produced. A field an unreadable expression overrides is dropped and reported the same
|
|
235
|
+
way: keeping the group's version would type the property as the shared shape while
|
|
236
|
+
the schema says something else.
|
|
114
237
|
|
|
115
238
|
Cross-file imports (relative paths only) are followed automatically so imported schemas land in the same output file. Imports from `node_modules` are not traversed.
|
|
116
239
|
|
|
@@ -118,13 +241,18 @@ Cross-file imports (relative paths only) are followed automatically so imported
|
|
|
118
241
|
|
|
119
242
|
- Not evaluated: the generator parses the source and never executes it. Schemas built at runtime (e.g. produced from a function) are invisible to it.
|
|
120
243
|
- String literal kludge: if you write `string('foo')` inside a `union(...)` (a shorthand some schemas use as a literal placeholder), the parser treats it as `literal('foo')` and notes the substitution in the generated file.
|
|
121
|
-
- Unsupported features: `lazy`, `tuple`, `intersect`, `custom` transforms, and non-string record keys. Heterogeneous unions with no common discriminator fall back to `JsonElement
|
|
244
|
+
- Unsupported features: `lazy`, `tuple`, `intersect`, `custom` transforms, and non-string record keys. Heterogeneous unions with no common discriminator fall back to `JsonElement`; a union mixing a numeric literal with a numeric schema (`union([literal(0), pipe(number(), integer())])`) is one of those, so express such a range as a single piped number with a `check` instead.
|
|
245
|
+
- A discriminated union's base record keeps its tag as a plain `[JsonIgnore]` property: `System.Text.Json` writes the discriminator from `JsonPolymorphic`, and a second property claiming that JSON name is a hard failure on both serialize and deserialize.
|
|
246
|
+
- A property whose C# name would equal its enclosing type's gets a `Value` suffix (`variant('op', ...)` named `Op` emits `OpValue`), because C# refuses the collision.
|
|
247
|
+
- A named alias that declares no C# type of its own — `array(...)`, `record(...)`, a bare primitive, an `optional`/`nullable` wrapper, or another such alias — is inlined wherever it is referenced, wrappers and all. `export const NoteSchema = nullable(string())` used as a field is a `string?`, not a reference to a `Note` type nothing declares.
|
|
248
|
+
- Anything dropped on the way is reported in the generated file's header, from both note channels. A generated file that lost a field says so at the top of itself.
|
|
122
249
|
- Nested anonymous objects are named `<ParentType><FieldName>` to avoid collisions with sibling inline types.
|
|
123
250
|
- `DTO` becomes `Dto` in emitted type names (e.g. `EventDefinitionDTOSchema` becomes `EventDefinitionDto`).
|
|
124
251
|
|
|
125
252
|
## Extension points
|
|
126
253
|
|
|
127
254
|
- New valibot features: add a node kind to `src/ir.ts`, parse it in `src/parser.ts`, and teach `src/emitter.ts` how to render it. The IR is a closed union, so the compiler points at every switch that needs a new case.
|
|
255
|
+
- The emitter's supporting rules live beside it: `src/csharp.ts` holds everything about how a value spells itself in C# (numeric widths, property names, the file header, the hand-written `StringOrStringList` source), and `src/variants.ts` holds union classification (which field tags a member, what every member has in common). Neither needs a module to answer, so both are plain functions.
|
|
128
256
|
- Naming: `--union-name` (CLI) / `unionRenames` (API) override the C# base-type name of a union or variant schema without touching the TS source.
|
|
129
257
|
|
|
130
258
|
## Dependencies
|
|
@@ -133,4 +261,4 @@ Cross-file imports (relative paths only) are followed automatically so imported
|
|
|
133
261
|
|
|
134
262
|
## Consumers
|
|
135
263
|
|
|
136
|
-
The `*-schemas` contract packages in this repo, and game repos that need C# mirrors of their JSON contracts for .NET clients.
|
|
264
|
+
The `*-schemas` contract packages in this repo, and game repos that need C# mirrors of their JSON contracts for .NET clients — among them _Chaos Overlords: New Chrome_, whose MonoGame client mirrors the multiplayer server's valibot wire contracts.
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/csharp.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a schema value spells itself in C#: numeric widths, property names, the file header,
|
|
3
|
+
* and the source of the one hand-written helper type the emitter can reach for.
|
|
4
|
+
*
|
|
5
|
+
* Kept apart from the emitter because none of it needs a module to answer — given a node or
|
|
6
|
+
* a name, the answer is the same every time.
|
|
7
|
+
*/
|
|
8
|
+
import type { PrimitiveNode } from "./ir.js";
|
|
9
|
+
/** Property name for the `[JsonExtensionData]` bag a `looseObject` record carries. */
|
|
10
|
+
export declare const EXTENSION_DATA_PROPERTY = "AdditionalProperties";
|
|
11
|
+
/**
|
|
12
|
+
* The C# type for a `number()`.
|
|
13
|
+
*
|
|
14
|
+
* A plain number is `double`. An `integer()` is integral, and narrows to `int`
|
|
15
|
+
* only when both declared bounds fit `System.Int32`: JavaScript integers run to
|
|
16
|
+
* 2^53, so an unbounded one deserialised into an `int` would reject every value
|
|
17
|
+
* above two billion — the failure mode is a whole class of documents refused,
|
|
18
|
+
* not a rounding error, so the wider type is the safe default.
|
|
19
|
+
*/
|
|
20
|
+
export declare function numericCsType(node: PrimitiveNode): string;
|
|
21
|
+
/**
|
|
22
|
+
* The C# type of a `literal(...)` value, by the JSON kind it stands for.
|
|
23
|
+
*
|
|
24
|
+
* A numeric literal is a `number()` whose bounds are the value itself, so it goes through
|
|
25
|
+
* {@link numericCsType} rather than repeating the Int32 fit test: `literal(3000000000)` is
|
|
26
|
+
* a `long` for the same reason `pipe(number(), integer(), minValue(3000000000))` is, and
|
|
27
|
+
* spelling it `double` would put a `3E+09` where the digest expects `3000000000`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function literalCsType(value: string | number | boolean): string;
|
|
30
|
+
/**
|
|
31
|
+
* The C# property name for a field of a given type.
|
|
32
|
+
*
|
|
33
|
+
* C# refuses a member whose name is its enclosing type's (CS0542), which a
|
|
34
|
+
* `variant("op", ...)` named `Op` walks straight into. The JSON name is carried
|
|
35
|
+
* by `JsonPropertyName` either way, so the C# name is free to move.
|
|
36
|
+
*/
|
|
37
|
+
export declare function propertyNameIn(typeName: string, fieldName: string): string;
|
|
38
|
+
/**
|
|
39
|
+
* Render the file-level header: auto-generated banner, whatever the parse and the emit could
|
|
40
|
+
* not carry across, and the using-block. Split out so callers that want to embed the emitter
|
|
41
|
+
* output in larger files can skip it.
|
|
42
|
+
*
|
|
43
|
+
* Pass both {@link SchemaModule.notes} and {@link EmitResult.notes}: a field the parser could
|
|
44
|
+
* not read and a member the emitter had to drop are the same kind of fact to whoever opens
|
|
45
|
+
* the generated file, and the header is the only place either one surfaces.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* import { emitFileHeader, emitModule, parseFiles } from "@game-infra/valibot-to-csharp";
|
|
50
|
+
*
|
|
51
|
+
* const module = parseFiles(["./schemas/events.ts"]);
|
|
52
|
+
* const emit = emitModule(module, { namespace: "MyGame.Events" });
|
|
53
|
+
* const header = emitFileHeader([...module.notes, ...emit.notes]);
|
|
54
|
+
* // "// <auto-generated>..." plus the System.Text.Json using-block
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare function emitFileHeader(notes: string[]): string;
|
|
58
|
+
/**
|
|
59
|
+
* The C# source of the `StringOrStringList` helper: the one type the emitter writes by hand
|
|
60
|
+
* rather than deriving from a schema. `union([string(), array(string())])` has no C# shape of
|
|
61
|
+
* its own, so it gets a record with implicit conversions and a converter that reads either
|
|
62
|
+
* form and writes back the one it was given.
|
|
63
|
+
*/
|
|
64
|
+
export declare const STRING_OR_STRING_LIST_SOURCE = "[JsonConverter(typeof(StringOrStringListConverter))]\npublic sealed record StringOrStringList(IReadOnlyList<string> Values)\n{\n public static implicit operator StringOrStringList(string value) => new(new[] { value });\n public static implicit operator StringOrStringList(string[] values) => new(values);\n}\n\npublic sealed class StringOrStringListConverter : JsonConverter<StringOrStringList>\n{\n public override StringOrStringList Read(ref Utf8JsonReader reader, System.Type typeToConvert, JsonSerializerOptions options)\n {\n if (reader.TokenType == JsonTokenType.String)\n {\n var single = reader.GetString() ?? string.Empty;\n return new StringOrStringList(new[] { single });\n }\n if (reader.TokenType == JsonTokenType.StartArray)\n {\n var list = new List<string>();\n while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)\n {\n list.Add(reader.GetString() ?? string.Empty);\n }\n return new StringOrStringList(list);\n }\n throw new JsonException(\"Expected string or array of strings for StringOrStringList.\");\n }\n\n public override void Write(Utf8JsonWriter writer, StringOrStringList value, JsonSerializerOptions options)\n {\n if (value.Values.Count == 1)\n {\n writer.WriteStringValue(value.Values[0]);\n return;\n }\n writer.WriteStartArray();\n foreach (var v in value.Values) writer.WriteStringValue(v);\n writer.WriteEndArray();\n }\n}";
|
|
65
|
+
//# sourceMappingURL=csharp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csharp.d.ts","sourceRoot":"","sources":["../src/csharp.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAG7C,sFAAsF;AACtF,eAAO,MAAM,uBAAuB,yBAAyB,CAAC;AAK9D;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAKzD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAUtE;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAG1E;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAoBtD;AAED;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,qkDAuCvC,CAAC"}
|
package/dist/csharp.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a schema value spells itself in C#: numeric widths, property names, the file header,
|
|
3
|
+
* and the source of the one hand-written helper type the emitter can reach for.
|
|
4
|
+
*
|
|
5
|
+
* Kept apart from the emitter because none of it needs a module to answer — given a node or
|
|
6
|
+
* a name, the answer is the same every time.
|
|
7
|
+
*/
|
|
8
|
+
import { toPascalCase } from "./naming.js";
|
|
9
|
+
/** Property name for the `[JsonExtensionData]` bag a `looseObject` record carries. */
|
|
10
|
+
export const EXTENSION_DATA_PROPERTY = "AdditionalProperties";
|
|
11
|
+
const INT32_MIN = -2_147_483_648;
|
|
12
|
+
const INT32_MAX = 2_147_483_647;
|
|
13
|
+
/**
|
|
14
|
+
* The C# type for a `number()`.
|
|
15
|
+
*
|
|
16
|
+
* A plain number is `double`. An `integer()` is integral, and narrows to `int`
|
|
17
|
+
* only when both declared bounds fit `System.Int32`: JavaScript integers run to
|
|
18
|
+
* 2^53, so an unbounded one deserialised into an `int` would reject every value
|
|
19
|
+
* above two billion — the failure mode is a whole class of documents refused,
|
|
20
|
+
* not a rounding error, so the wider type is the safe default.
|
|
21
|
+
*/
|
|
22
|
+
export function numericCsType(node) {
|
|
23
|
+
if (!node.integer)
|
|
24
|
+
return "double";
|
|
25
|
+
const { min, max } = node;
|
|
26
|
+
const fitsInt32 = min !== undefined && max !== undefined && min >= INT32_MIN && max <= INT32_MAX;
|
|
27
|
+
return fitsInt32 ? "int" : "long";
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The C# type of a `literal(...)` value, by the JSON kind it stands for.
|
|
31
|
+
*
|
|
32
|
+
* A numeric literal is a `number()` whose bounds are the value itself, so it goes through
|
|
33
|
+
* {@link numericCsType} rather than repeating the Int32 fit test: `literal(3000000000)` is
|
|
34
|
+
* a `long` for the same reason `pipe(number(), integer(), minValue(3000000000))` is, and
|
|
35
|
+
* spelling it `double` would put a `3E+09` where the digest expects `3000000000`.
|
|
36
|
+
*/
|
|
37
|
+
export function literalCsType(value) {
|
|
38
|
+
if (typeof value === "string")
|
|
39
|
+
return "string";
|
|
40
|
+
if (typeof value === "boolean")
|
|
41
|
+
return "bool";
|
|
42
|
+
return numericCsType({
|
|
43
|
+
kind: "primitive",
|
|
44
|
+
type: "number",
|
|
45
|
+
integer: Number.isSafeInteger(value),
|
|
46
|
+
min: value,
|
|
47
|
+
max: value,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The C# property name for a field of a given type.
|
|
52
|
+
*
|
|
53
|
+
* C# refuses a member whose name is its enclosing type's (CS0542), which a
|
|
54
|
+
* `variant("op", ...)` named `Op` walks straight into. The JSON name is carried
|
|
55
|
+
* by `JsonPropertyName` either way, so the C# name is free to move.
|
|
56
|
+
*/
|
|
57
|
+
export function propertyNameIn(typeName, fieldName) {
|
|
58
|
+
const propName = toPascalCase(fieldName);
|
|
59
|
+
return propName === typeName ? `${propName}Value` : propName;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Render the file-level header: auto-generated banner, whatever the parse and the emit could
|
|
63
|
+
* not carry across, and the using-block. Split out so callers that want to embed the emitter
|
|
64
|
+
* output in larger files can skip it.
|
|
65
|
+
*
|
|
66
|
+
* Pass both {@link SchemaModule.notes} and {@link EmitResult.notes}: a field the parser could
|
|
67
|
+
* not read and a member the emitter had to drop are the same kind of fact to whoever opens
|
|
68
|
+
* the generated file, and the header is the only place either one surfaces.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```ts
|
|
72
|
+
* import { emitFileHeader, emitModule, parseFiles } from "@game-infra/valibot-to-csharp";
|
|
73
|
+
*
|
|
74
|
+
* const module = parseFiles(["./schemas/events.ts"]);
|
|
75
|
+
* const emit = emitModule(module, { namespace: "MyGame.Events" });
|
|
76
|
+
* const header = emitFileHeader([...module.notes, ...emit.notes]);
|
|
77
|
+
* // "// <auto-generated>..." plus the System.Text.Json using-block
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
export function emitFileHeader(notes) {
|
|
81
|
+
const lines = [
|
|
82
|
+
"// <auto-generated>",
|
|
83
|
+
"// This file was generated by @game-infra/valibot-to-csharp.",
|
|
84
|
+
"// Do not edit by hand; regenerate from the valibot source instead.",
|
|
85
|
+
"// </auto-generated>",
|
|
86
|
+
];
|
|
87
|
+
for (const note of notes) {
|
|
88
|
+
lines.push(`// ${note}`);
|
|
89
|
+
}
|
|
90
|
+
lines.push("");
|
|
91
|
+
// A file the compiler recognises as auto-generated is outside the project's nullable context, so
|
|
92
|
+
// every `T?` in it is an error (CS8669) unless the file turns the context on itself.
|
|
93
|
+
lines.push("#nullable enable");
|
|
94
|
+
lines.push("");
|
|
95
|
+
lines.push("using System.Collections.Generic;");
|
|
96
|
+
lines.push("using System.Text.Json;");
|
|
97
|
+
lines.push("using System.Text.Json.Serialization;");
|
|
98
|
+
lines.push("");
|
|
99
|
+
return lines.join("\n");
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The C# source of the `StringOrStringList` helper: the one type the emitter writes by hand
|
|
103
|
+
* rather than deriving from a schema. `union([string(), array(string())])` has no C# shape of
|
|
104
|
+
* its own, so it gets a record with implicit conversions and a converter that reads either
|
|
105
|
+
* form and writes back the one it was given.
|
|
106
|
+
*/
|
|
107
|
+
export const STRING_OR_STRING_LIST_SOURCE = `[JsonConverter(typeof(StringOrStringListConverter))]
|
|
108
|
+
public sealed record StringOrStringList(IReadOnlyList<string> Values)
|
|
109
|
+
{
|
|
110
|
+
public static implicit operator StringOrStringList(string value) => new(new[] { value });
|
|
111
|
+
public static implicit operator StringOrStringList(string[] values) => new(values);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
public sealed class StringOrStringListConverter : JsonConverter<StringOrStringList>
|
|
115
|
+
{
|
|
116
|
+
public override StringOrStringList Read(ref Utf8JsonReader reader, System.Type typeToConvert, JsonSerializerOptions options)
|
|
117
|
+
{
|
|
118
|
+
if (reader.TokenType == JsonTokenType.String)
|
|
119
|
+
{
|
|
120
|
+
var single = reader.GetString() ?? string.Empty;
|
|
121
|
+
return new StringOrStringList(new[] { single });
|
|
122
|
+
}
|
|
123
|
+
if (reader.TokenType == JsonTokenType.StartArray)
|
|
124
|
+
{
|
|
125
|
+
var list = new List<string>();
|
|
126
|
+
while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
|
|
127
|
+
{
|
|
128
|
+
list.Add(reader.GetString() ?? string.Empty);
|
|
129
|
+
}
|
|
130
|
+
return new StringOrStringList(list);
|
|
131
|
+
}
|
|
132
|
+
throw new JsonException("Expected string or array of strings for StringOrStringList.");
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
public override void Write(Utf8JsonWriter writer, StringOrStringList value, JsonSerializerOptions options)
|
|
136
|
+
{
|
|
137
|
+
if (value.Values.Count == 1)
|
|
138
|
+
{
|
|
139
|
+
writer.WriteStringValue(value.Values[0]);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
writer.WriteStartArray();
|
|
143
|
+
foreach (var v in value.Values) writer.WriteStringValue(v);
|
|
144
|
+
writer.WriteEndArray();
|
|
145
|
+
}
|
|
146
|
+
}`;
|
|
147
|
+
//# sourceMappingURL=csharp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csharp.js","sourceRoot":"","sources":["../src/csharp.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,sFAAsF;AACtF,MAAM,CAAC,MAAM,uBAAuB,GAAG,sBAAsB,CAAC;AAE9D,MAAM,SAAS,GAAG,CAAC,aAAa,CAAC;AACjC,MAAM,SAAS,GAAG,aAAa,CAAC;AAEhC;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,IAAmB;IAC/C,IAAI,CAAC,IAAI,CAAC,OAAO;QAAE,OAAO,QAAQ,CAAC;IACnC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAC1B,MAAM,SAAS,GAAG,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,IAAI,SAAS,IAAI,GAAG,IAAI,SAAS,CAAC;IACjG,OAAO,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;AACpC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,KAAgC;IAC5D,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC/C,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IAC9C,OAAO,aAAa,CAAC;QACnB,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC;QACpC,GAAG,EAAE,KAAK;QACV,GAAG,EAAE,KAAK;KACX,CAAC,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,SAAiB;IAChE,MAAM,QAAQ,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;IACzC,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC/D,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,cAAc,CAAC,KAAe;IAC5C,MAAM,KAAK,GAAG;QACZ,qBAAqB;QACrB,gEAAgE;QAChE,uEAAuE;QACvE,sBAAsB;KACvB,CAAC;IACF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IAC3B,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,iGAAiG;IACjG,qFAAqF;IACrF,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAC/B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;IAChD,KAAK,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IACtC,KAAK,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;IACpD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuC1C,CAAC"}
|
package/dist/emitter.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { SchemaModule } from "./ir.js";
|
|
2
|
+
export { emitFileHeader } from "./csharp.js";
|
|
2
3
|
/**
|
|
3
4
|
* Options accepted by {@link emitModule}.
|
|
4
5
|
*
|
|
@@ -36,6 +37,13 @@ export interface EmitResult {
|
|
|
36
37
|
source: string;
|
|
37
38
|
/** True if any StringOrStringList helper was referenced. */
|
|
38
39
|
usesStringOrStringList: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Shapes the emitter could not render faithfully — a dropped variant member, a widened
|
|
42
|
+
* field type, a reference it could not close. The caller renders these into the generated
|
|
43
|
+
* file's header alongside {@link SchemaModule.notes}, so a lossy translation says so in
|
|
44
|
+
* the artifact rather than only in whoever ran it.
|
|
45
|
+
*/
|
|
46
|
+
notes: string[];
|
|
39
47
|
}
|
|
40
48
|
/**
|
|
41
49
|
* Turn a {@link SchemaModule} into a C# source file body. The caller wraps
|
|
@@ -52,17 +60,4 @@ export interface EmitResult {
|
|
|
52
60
|
* ```
|
|
53
61
|
*/
|
|
54
62
|
export declare function emitModule(module: SchemaModule, opts: EmitOptions): EmitResult;
|
|
55
|
-
/**
|
|
56
|
-
* Render the file-level header: auto-generated banner + using-block. Split out
|
|
57
|
-
* so callers that want to embed the emitter output in larger files can skip it.
|
|
58
|
-
*
|
|
59
|
-
* @example
|
|
60
|
-
* ```ts
|
|
61
|
-
* import { emitFileHeader } from "@game-infra/valibot-to-csharp";
|
|
62
|
-
*
|
|
63
|
-
* const header = emitFileHeader([]);
|
|
64
|
-
* // "// <auto-generated>..." plus the System.Text.Json using-block
|
|
65
|
-
* ```
|
|
66
|
-
*/
|
|
67
|
-
export declare function emitFileHeader(notes: string[]): string;
|
|
68
63
|
//# sourceMappingURL=emitter.d.ts.map
|
package/dist/emitter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emitter.d.ts","sourceRoot":"","sources":["../src/emitter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"emitter.d.ts","sourceRoot":"","sources":["../src/emitter.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAIV,YAAY,EAGb,MAAM,SAAS,CAAC;AAajB,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,UAAU;IACzB,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,sBAAsB,EAAE,OAAO,CAAC;IAChC;;;;;OAKG;IACH,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,WAAW,GAAG,UAAU,CA8B9E"}
|