Skip to content

Commit a0dc5ea

Browse files
authored
feat: .NET Support (#73)
Adds support for generating a .NET (C#) project. I choose `namespace` rather than `package` since it seemed more appropriate when generating source code. I went with `dotnet` since it matches the jsii naming, but `csharp` may make more sense for configuration options / cli parameters. I don't believe there are any plans to generate F# code in jsii, but if there were, I'd lean towards using `csharp` naming.
1 parent 491ae93 commit a0dc5ea

10 files changed

Lines changed: 799 additions & 1 deletion

File tree

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,35 @@ original module. This code depends on the following maven package (should be def
9797

9898
The output directory will also include a tarball `generated@0.0.0.jsii.tgz` that must be bundled in your project.
9999

100+
### C# Output
101+
102+
To produce a C# module from your source, use the `csharp` option:
103+
104+
```ts
105+
await srcmak('srcdir', {
106+
csharp: {
107+
outdir: '/path/to/project/root',
108+
namespace: 'HelloWorld'
109+
}
110+
});
111+
```
112+
113+
Or the `--csharp-*` switches in the CLI:
114+
115+
```bash
116+
$ jsii-srcmak /src/dir --csharp-outdir=dir --csharp-namespace=HelloWorld
117+
```
118+
119+
* The `outdir`/`--csharp-outdir` option points to the root directory of your C# project.
120+
* The `package`/`--csharp-namespace` option is the C# root namespace.
121+
122+
The output directory will include a C# project that corresponds to the
123+
original module. This code depends on the following NuGet package (It is already defined as a dependency in the generated project):
124+
125+
- [jsii](https://www.nuget.org/packages/Amazon.JSII.Runtime/)
126+
127+
The output directory will also include a tarball `generated@0.0.0.jsii.tgz` that must be bundled in your project (It is already included as an embedded resource in the generated project).
128+
100129
### Entrypoint
101130

102131
The `entrypoint` option can be used to customize the name of the typescript entrypoint (default is `index.ts`).

src/cli.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ async function main() {
1111
.option('python-module-name', { desc: 'python module name', type: 'string' })
1212
.option('java-outdir', { desc: 'java output directory (requires --java-package)', type: 'string' })
1313
.option('java-package', { desc: 'the java package (namespace) to use for all generated types', type: 'string' })
14+
.option('csharp-outdir', { desc: 'C# output directory (requires --csharp-namespace)', type: 'string' })
15+
.option('csharp-namespace', { desc: 'the C# namespace to use for all generated types', type: 'string' })
1416
.showHelpOnFail(true)
1517
.help();
1618

@@ -30,6 +32,7 @@ async function main() {
3032
...parseJsiiOptions(),
3133
...parsePythonOptions(),
3234
...parseJavaOptions(),
35+
...parseCSharpOptions(),
3336
});
3437

3538
function parseJsiiOptions() {
@@ -70,6 +73,20 @@ async function main() {
7073
}
7174
}
7275

76+
function parseCSharpOptions() {
77+
const outdir = argv['csharp-outdir'];
78+
const namespace = argv['csharp-namespace'];
79+
if (!outdir && !namespace) { return undefined; }
80+
if (!outdir) { throw new Error('--csharp-outdir is required'); }
81+
if (!namespace) { throw new Error('--csharp-namespace is required'); }
82+
return {
83+
csharp: {
84+
outdir: outdir,
85+
namespace: namespace,
86+
},
87+
}
88+
}
89+
7390
function parseDepOption() {
7491
if (argv.dep?.length === 0) { return undefined; }
7592
return {

src/compile.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,13 @@ export async function compile(workdir: string, options: Options) {
8484
};
8585
}
8686

87+
if (options.csharp) {
88+
targets.dotnet = {
89+
namespace: options.csharp.namespace,
90+
packageId: options.csharp.namespace,
91+
};
92+
}
93+
8794
await fs.writeFile(path.join(workdir, 'package.json'), JSON.stringify(pkg, undefined, 2));
8895

8996
await exec(compilerModule, args, {

src/options.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ export interface Options {
3939
* @default - java is not generated
4040
*/
4141
java?: JavaOutputOptions;
42+
43+
/**
44+
* Produces C# code.
45+
*
46+
* @default - C# is not generated
47+
*/
48+
csharp?: CSharpOutputOptions;
4249
}
4350

4451
export interface JsiiOutputOptions {
@@ -77,3 +84,18 @@ export interface JavaOutputOptions {
7784
*/
7885
package: string;
7986
}
87+
88+
export interface CSharpOutputOptions {
89+
/**
90+
* Base root directory.
91+
*/
92+
outdir: string;
93+
94+
/**
95+
* The root namespace to generate types in
96+
*
97+
* This must follow standard C# namespace conventions.
98+
* For example, it cannot include a hyphen ('-')
99+
*/
100+
namespace: string;
101+
}

src/srcmak.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,12 @@ export async function srcmak(srcdir: string, options: Options = { }) {
4444
await fs.mkdirp(target); // make sure target directory exists
4545
await ncp(source, target, { clobber: false });
4646
}
47+
48+
if (options.csharp) {
49+
const reldir = options.csharp.namespace;
50+
const source = path.resolve(path.join(workdir, 'dist/dotnet/', reldir));
51+
const target = path.join(options.csharp.outdir, reldir);
52+
await fs.move(source, target, { overwrite: true });
53+
}
4754
});
4855
}

src/util.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export async function exec(moduleName: string, args: string[] = [], options: Spa
5858
}
5959

6060
/**
61-
* This validates that the Python module name and Java package name
61+
* This validates that the Python module name, Java package name, and C# namespace
6262
* conform to language-specific constraints.
6363
*
6464
* @param options Options set by the consumer
@@ -72,4 +72,8 @@ export function validateOptions(options: Options) {
7272
if (options.java?.package.includes('-')) {
7373
throw new Error(`Java package [${options.java.package}] may not contain "-"`);
7474
}
75+
76+
if (options.csharp?.namespace.includes('-')) {
77+
throw new Error(`C# namespace [${options.csharp.namespace}] may not contain "-"`);
78+
}
7579
}

0 commit comments

Comments
 (0)