@game-infra/valibot-to-csharp 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +136 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +115 -0
- package/dist/cli.js.map +1 -0
- package/dist/emitter.d.ts +68 -0
- package/dist/emitter.d.ts.map +1 -0
- package/dist/emitter.js +573 -0
- package/dist/emitter.js.map +1 -0
- package/dist/generate.d.ts +101 -0
- package/dist/generate.d.ts.map +1 -0
- package/dist/generate.js +99 -0
- package/dist/generate.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/ir.d.ts +292 -0
- package/dist/ir.d.ts.map +1 -0
- package/dist/ir.js +12 -0
- package/dist/ir.js.map +1 -0
- package/dist/naming.d.ts +53 -0
- package/dist/naming.d.ts.map +1 -0
- package/dist/naming.js +91 -0
- package/dist/naming.js.map +1 -0
- package/dist/parser.d.ts +22 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +321 -0
- package/dist/parser.js.map +1 -0
- package/package.json +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# @game-infra/valibot-to-csharp
|
|
2
|
+
|
|
3
|
+
Generate idiomatic C# record types from TypeScript [valibot](https://valibot.dev) schemas.
|
|
4
|
+
|
|
5
|
+
## Why
|
|
6
|
+
|
|
7
|
+
We author JSON schemas in TypeScript (in editors and web tooling) but consume that JSON from a .NET game using `System.Text.Json`. Keeping a hand-maintained C# mirror of every `object({...})` is tedious and drifts the moment someone renames a field. This package walks the valibot source with the TypeScript compiler API and emits a `.cs` file whose records deserialise the same JSON, so the mirror is never hand-maintained.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
This package is private and consumed as source from a sibling `game-infra` checkout (see `docs/CONSUMING.md` in the repo root). Add it to your game repo as a linked dependency:
|
|
12
|
+
|
|
13
|
+
```jsonc
|
|
14
|
+
// pnpm
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@game-infra/valibot-to-csharp": "link:../../../game-infra/packages/valibot-to-csharp"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// npm
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@game-infra/valibot-to-csharp": "file:../../../game-infra/packages/valibot-to-csharp"
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## CLI
|
|
26
|
+
|
|
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
|
+
```sh
|
|
30
|
+
npx tsx ../game-infra/packages/valibot-to-csharp/src/cli.ts \
|
|
31
|
+
--input ./schemas/events.ts \
|
|
32
|
+
--input ./schemas/common.ts \
|
|
33
|
+
--output ./Generated \
|
|
34
|
+
--namespace MyGame.Events
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Alternatively, build once inside `game-infra` (`pnpm --filter @game-infra/valibot-to-csharp build`) and run the compiled entry point, which keeps its shebang:
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
node ../game-infra/packages/valibot-to-csharp/dist/cli.js --input ./schemas/events.ts --output ./Generated --namespace MyGame.Events
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Flags:
|
|
44
|
+
|
|
45
|
+
| Flag | Meaning |
|
|
46
|
+
| ---------------- | ------------------------------------------------------------- |
|
|
47
|
+
| `--input`, `-i` | Repeatable. Path to a `.ts` file containing valibot schemas. |
|
|
48
|
+
| `--output`, `-o` | Output directory for generated `.cs` files. |
|
|
49
|
+
| `--namespace` | C# namespace for emitted types. Defaults to `Generated`. |
|
|
50
|
+
| `--bundle` | Write a single bundle `.cs` file with the given name. |
|
|
51
|
+
| `--union-name` | Override a union base name. Format: `SchemaConst:CSharpName`. |
|
|
52
|
+
| `--help`, `-h` | Show usage. |
|
|
53
|
+
|
|
54
|
+
## Programmatic
|
|
55
|
+
|
|
56
|
+
Generate and write files in one call:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import { generateToFile } from "@game-infra/valibot-to-csharp";
|
|
60
|
+
|
|
61
|
+
await generateToFile({
|
|
62
|
+
inputs: ["./schemas/events.ts"],
|
|
63
|
+
outputDir: "./Generated",
|
|
64
|
+
namespace: "MyGame.Events",
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Inspect the output without touching disk (handy for embedding into other build pipelines):
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
import { generate } from "@game-infra/valibot-to-csharp";
|
|
72
|
+
|
|
73
|
+
const result = await generate({
|
|
74
|
+
inputs: ["./schemas/events.ts"],
|
|
75
|
+
outputDir: "./Generated",
|
|
76
|
+
namespace: "MyGame.Events",
|
|
77
|
+
bundle: true,
|
|
78
|
+
bundleName: "Events.cs",
|
|
79
|
+
});
|
|
80
|
+
for (const file of result.files) {
|
|
81
|
+
console.log(file.path, file.content.length);
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Go lower-level with the parser and emitter directly:
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
import { emitFileHeader, emitModule, parseFiles } from "@game-infra/valibot-to-csharp";
|
|
89
|
+
|
|
90
|
+
const module = parseFiles(["./schemas/events.ts"]);
|
|
91
|
+
const emit = emitModule(module, { namespace: "MyGame.Events" });
|
|
92
|
+
const csSource = emitFileHeader(module.notes) + emit.source;
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Supported valibot surface
|
|
96
|
+
|
|
97
|
+
| valibot | C# |
|
|
98
|
+
| ------------------------------------ | -------------------------------------------------------- |
|
|
99
|
+
| `string()` | `string` |
|
|
100
|
+
| `number()` | `double` |
|
|
101
|
+
| `boolean()` | `bool` |
|
|
102
|
+
| `unknown()` | `JsonElement` |
|
|
103
|
+
| `array(X)` | `IReadOnlyList<X>` |
|
|
104
|
+
| `record(string(), V)` | `IReadOnlyDictionary<string, V>` |
|
|
105
|
+
| `object({...})` | `public sealed record` |
|
|
106
|
+
| `optional(X)` / `nullable(X)` | Nullable field (`X?`) |
|
|
107
|
+
| `literal('a')` | String literal (merged into enums/variant tags) |
|
|
108
|
+
| `picklist(['a','b'])` | `[JsonConverter(typeof(JsonStringEnumConverter))] enum` |
|
|
109
|
+
| `union([literal(...), ...])` | Enum |
|
|
110
|
+
| `union([string(), array(string())])` | `StringOrStringList` helper (auto-emitted) |
|
|
111
|
+
| `union([{type:'A',...}, ...])` | `[JsonPolymorphic] abstract record` with derived records |
|
|
112
|
+
| `variant('type', [...])` | Same as discriminated union |
|
|
113
|
+
| `pipe(inner, ...validators)` | `inner` (validators are ignored) |
|
|
114
|
+
|
|
115
|
+
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
|
+
|
|
117
|
+
## Caveats
|
|
118
|
+
|
|
119
|
+
- 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
|
+
- 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`.
|
|
122
|
+
- Nested anonymous objects are named `<ParentType><FieldName>` to avoid collisions with sibling inline types.
|
|
123
|
+
- `DTO` becomes `Dto` in emitted type names (e.g. `EventDefinitionDTOSchema` becomes `EventDefinitionDto`).
|
|
124
|
+
|
|
125
|
+
## Extension points
|
|
126
|
+
|
|
127
|
+
- 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.
|
|
128
|
+
- Naming: `--union-name` (CLI) / `unionRenames` (API) override the C# base-type name of a union or variant schema without touching the TS source.
|
|
129
|
+
|
|
130
|
+
## Dependencies
|
|
131
|
+
|
|
132
|
+
- `@typescript/typescript6`: the classic in-process TypeScript compiler API (`ts.createSourceFile` and friends). TypeScript 7 no longer exposes that API from the `typescript` package root, so the parser uses this compatibility package while the repo compiles with TS 7.
|
|
133
|
+
|
|
134
|
+
## Consumers
|
|
135
|
+
|
|
136
|
+
The `*-schemas` contract packages in this repo, and game repos that need C# mirrors of their JSON contracts for .NET clients.
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { generateToFile } from "./generate.js";
|
|
5
|
+
const USAGE = `valibot-to-csharp: generate C# record types from valibot schemas
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
valibot-to-csharp --input <file.ts> [--input <file.ts>...] --output <dir> \\
|
|
9
|
+
--namespace <Ns> [--bundle <BundleName.cs>] \\
|
|
10
|
+
[--union-name <SchemaConst>:<CSharpName>]
|
|
11
|
+
|
|
12
|
+
Options:
|
|
13
|
+
--input, -i TS file containing valibot schemas (repeatable)
|
|
14
|
+
--output, -o Output directory for generated .cs files
|
|
15
|
+
--namespace, -n C# namespace for emitted types (default: Generated)
|
|
16
|
+
--bundle Write a single bundle .cs file with the given name
|
|
17
|
+
--union-name Override the C# name of a union/variant schema
|
|
18
|
+
(repeatable, format: SchemaConst:CSharpName)
|
|
19
|
+
--help, -h Show this help
|
|
20
|
+
`;
|
|
21
|
+
function parseArgs(argv) {
|
|
22
|
+
const out = { inputs: [], unionRenames: {}, help: false };
|
|
23
|
+
let i = 0;
|
|
24
|
+
while (i < argv.length) {
|
|
25
|
+
const a = argv[i];
|
|
26
|
+
const next = () => {
|
|
27
|
+
const v = argv[i + 1];
|
|
28
|
+
if (v === undefined)
|
|
29
|
+
throw new Error(`Missing value for ${a}`);
|
|
30
|
+
i += 2;
|
|
31
|
+
return v;
|
|
32
|
+
};
|
|
33
|
+
switch (a) {
|
|
34
|
+
case "--help":
|
|
35
|
+
case "-h":
|
|
36
|
+
out.help = true;
|
|
37
|
+
i++;
|
|
38
|
+
break;
|
|
39
|
+
case "--input":
|
|
40
|
+
case "-i":
|
|
41
|
+
out.inputs.push(next());
|
|
42
|
+
break;
|
|
43
|
+
case "--output":
|
|
44
|
+
case "-o":
|
|
45
|
+
out.output = next();
|
|
46
|
+
break;
|
|
47
|
+
case "--namespace":
|
|
48
|
+
case "-n":
|
|
49
|
+
out.namespace = next();
|
|
50
|
+
break;
|
|
51
|
+
case "--bundle":
|
|
52
|
+
out.bundle = next();
|
|
53
|
+
break;
|
|
54
|
+
case "--union-name": {
|
|
55
|
+
const v = next();
|
|
56
|
+
const sep = v.indexOf(":");
|
|
57
|
+
if (sep <= 0)
|
|
58
|
+
throw new Error(`--union-name expects SchemaConst:CSharpName, got '${v}'`);
|
|
59
|
+
const key = v.slice(0, sep);
|
|
60
|
+
const value = v.slice(sep + 1);
|
|
61
|
+
out.unionRenames[key] = value;
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
default:
|
|
65
|
+
// Positional arguments are treated as inputs for convenience.
|
|
66
|
+
if (a.startsWith("-")) {
|
|
67
|
+
throw new Error(`Unknown option: ${a}`);
|
|
68
|
+
}
|
|
69
|
+
out.inputs.push(a);
|
|
70
|
+
i++;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
async function main() {
|
|
77
|
+
let args;
|
|
78
|
+
try {
|
|
79
|
+
args = parseArgs(process.argv.slice(2));
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
process.stderr.write(`${e.message}\n\n${USAGE}`);
|
|
83
|
+
process.exit(2);
|
|
84
|
+
}
|
|
85
|
+
if (args.help) {
|
|
86
|
+
process.stdout.write(USAGE);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (args.inputs.length === 0 || !args.output) {
|
|
90
|
+
process.stderr.write(`Missing required --input and/or --output.\n\n${USAGE}`);
|
|
91
|
+
process.exit(2);
|
|
92
|
+
}
|
|
93
|
+
const absInputs = args.inputs.map((f) => path.resolve(f));
|
|
94
|
+
for (const f of absInputs) {
|
|
95
|
+
if (!fs.existsSync(f)) {
|
|
96
|
+
process.stderr.write(`Input file not found: ${f}\n`);
|
|
97
|
+
process.exit(2);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const result = await generateToFile({
|
|
101
|
+
inputs: absInputs,
|
|
102
|
+
outputDir: path.resolve(args.output),
|
|
103
|
+
namespace: args.namespace ?? "Generated",
|
|
104
|
+
bundle: Boolean(args.bundle),
|
|
105
|
+
bundleName: args.bundle,
|
|
106
|
+
unionRenames: args.unionRenames,
|
|
107
|
+
});
|
|
108
|
+
const names = result.files.map((f) => path.basename(f.path)).join(", ");
|
|
109
|
+
process.stdout.write(`Generated ${result.files.length} file(s) in ${path.resolve(args.output)}: ${names}\n`);
|
|
110
|
+
}
|
|
111
|
+
main().catch((e) => {
|
|
112
|
+
process.stderr.write(`Error: ${e.stack ?? e}\n`);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
});
|
|
115
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { SchemaModule } from "./ir.js";
|
|
2
|
+
/**
|
|
3
|
+
* Options accepted by {@link emitModule}.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* import type { EmitOptions } from "@game-infra/valibot-to-csharp";
|
|
8
|
+
*
|
|
9
|
+
* const opts: EmitOptions = {
|
|
10
|
+
* namespace: "MyGame.Events",
|
|
11
|
+
* unionRenames: new Map([["ActionSchema", "GameAction"]]),
|
|
12
|
+
* };
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export interface EmitOptions {
|
|
16
|
+
namespace: string;
|
|
17
|
+
/** Optional renames for union/variant base types. Keyed by source const name. */
|
|
18
|
+
unionRenames?: Map<string, string>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Result of {@link emitModule}.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* import { emitModule, parseFiles } from "@game-infra/valibot-to-csharp";
|
|
26
|
+
* import type { EmitResult } from "@game-infra/valibot-to-csharp";
|
|
27
|
+
*
|
|
28
|
+
* const result: EmitResult = emitModule(parseFiles(["./schemas/events.ts"]), {
|
|
29
|
+
* namespace: "MyGame.Events",
|
|
30
|
+
* });
|
|
31
|
+
* console.log(result.source);
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export interface EmitResult {
|
|
35
|
+
/** Full C# source body (namespace + types). */
|
|
36
|
+
source: string;
|
|
37
|
+
/** True if any StringOrStringList helper was referenced. */
|
|
38
|
+
usesStringOrStringList: boolean;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Turn a {@link SchemaModule} into a C# source file body. The caller wraps
|
|
42
|
+
* this with the using-block header (see {@link emitFileHeader}) and writes it
|
|
43
|
+
* to disk.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* import { emitFileHeader, emitModule, parseFiles } from "@game-infra/valibot-to-csharp";
|
|
48
|
+
*
|
|
49
|
+
* const module = parseFiles(["./schemas/events.ts"]);
|
|
50
|
+
* const emit = emitModule(module, { namespace: "MyGame.Events" });
|
|
51
|
+
* const csFile = emitFileHeader(module.notes) + emit.source;
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
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
|
+
//# sourceMappingURL=emitter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emitter.d.ts","sourceRoot":"","sources":["../src/emitter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAIV,YAAY,EAGb,MAAM,SAAS,CAAC;AAGjB;;;;;;;;;;;;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;CACjC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,WAAW,GAAG,UAAU,CA0B9E;AAgBD;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAgBtD"}
|