diff --git a/README.md b/README.md index a89d6422..76911407 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,35 @@ original module. This code depends on the following maven package (should be def The output directory will also include a tarball `generated@0.0.0.jsii.tgz` that must be bundled in your project. +### C# Output + +To produce a C# module from your source, use the `csharp` option: + +```ts +await srcmak('srcdir', { + csharp: { + outdir: '/path/to/project/root', + namespace: 'HelloWorld' + } +}); +``` + +Or the `--csharp-*` switches in the CLI: + +```bash +$ jsii-srcmak /src/dir --csharp-outdir=dir --csharp-namespace=HelloWorld +``` + +* The `outdir`/`--csharp-outdir` option points to the root directory of your C# project. +* The `package`/`--csharp-namespace` option is the C# root namespace. + +The output directory will include a C# project that corresponds to the +original module. This code depends on the following NuGet package (It is already defined as a dependency in the generated project): + +- [jsii](https://www.nuget.org/packages/Amazon.JSII.Runtime/) + +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). + ### Entrypoint The `entrypoint` option can be used to customize the name of the typescript entrypoint (default is `index.ts`). diff --git a/src/cli.ts b/src/cli.ts index 77eb006a..9d0017db 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -11,6 +11,8 @@ async function main() { .option('python-module-name', { desc: 'python module name', type: 'string' }) .option('java-outdir', { desc: 'java output directory (requires --java-package)', type: 'string' }) .option('java-package', { desc: 'the java package (namespace) to use for all generated types', type: 'string' }) + .option('csharp-outdir', { desc: 'C# output directory (requires --csharp-namespace)', type: 'string' }) + .option('csharp-namespace', { desc: 'the C# namespace to use for all generated types', type: 'string' }) .showHelpOnFail(true) .help(); @@ -30,6 +32,7 @@ async function main() { ...parseJsiiOptions(), ...parsePythonOptions(), ...parseJavaOptions(), + ...parseCSharpOptions(), }); function parseJsiiOptions() { @@ -70,6 +73,20 @@ async function main() { } } + function parseCSharpOptions() { + const outdir = argv['csharp-outdir']; + const namespace = argv['csharp-namespace']; + if (!outdir && !namespace) { return undefined; } + if (!outdir) { throw new Error('--csharp-outdir is required'); } + if (!namespace) { throw new Error('--csharp-namespace is required'); } + return { + csharp: { + outdir: outdir, + namespace: namespace, + }, + } + } + function parseDepOption() { if (argv.dep?.length === 0) { return undefined; } return { diff --git a/src/compile.ts b/src/compile.ts index 3fce96df..b10f1f32 100644 --- a/src/compile.ts +++ b/src/compile.ts @@ -84,6 +84,13 @@ export async function compile(workdir: string, options: Options) { }; } + if (options.csharp) { + targets.dotnet = { + namespace: options.csharp.namespace, + packageId: options.csharp.namespace, + }; + } + await fs.writeFile(path.join(workdir, 'package.json'), JSON.stringify(pkg, undefined, 2)); await exec(compilerModule, args, { diff --git a/src/options.ts b/src/options.ts index 7642740a..4432beef 100644 --- a/src/options.ts +++ b/src/options.ts @@ -39,6 +39,13 @@ export interface Options { * @default - java is not generated */ java?: JavaOutputOptions; + + /** + * Produces C# code. + * + * @default - C# is not generated + */ + csharp?: CSharpOutputOptions; } export interface JsiiOutputOptions { @@ -77,3 +84,18 @@ export interface JavaOutputOptions { */ package: string; } + +export interface CSharpOutputOptions { + /** + * Base root directory. + */ + outdir: string; + + /** + * The root namespace to generate types in + * + * This must follow standard C# namespace conventions. + * For example, it cannot include a hyphen ('-') + */ + namespace: string; +} diff --git a/src/srcmak.ts b/src/srcmak.ts index d3eccee1..212769dd 100644 --- a/src/srcmak.ts +++ b/src/srcmak.ts @@ -44,5 +44,12 @@ export async function srcmak(srcdir: string, options: Options = { }) { await fs.mkdirp(target); // make sure target directory exists await ncp(source, target, { clobber: false }); } + + if (options.csharp) { + const reldir = options.csharp.namespace; + const source = path.resolve(path.join(workdir, 'dist/dotnet/', reldir)); + const target = path.join(options.csharp.outdir, reldir); + await fs.move(source, target, { overwrite: true }); + } }); } diff --git a/src/util.ts b/src/util.ts index 8eda36d6..507354a4 100644 --- a/src/util.ts +++ b/src/util.ts @@ -58,7 +58,7 @@ export async function exec(moduleName: string, args: string[] = [], options: Spa } /** - * This validates that the Python module name and Java package name + * This validates that the Python module name, Java package name, and C# namespace * conform to language-specific constraints. * * @param options Options set by the consumer @@ -72,4 +72,8 @@ export function validateOptions(options: Options) { if (options.java?.package.includes('-')) { throw new Error(`Java package [${options.java.package}] may not contain "-"`); } + + if (options.csharp?.namespace.includes('-')) { + throw new Error(`C# namespace [${options.csharp.namespace}] may not contain "-"`); + } } diff --git a/test/__snapshots__/cli.test.ts.snap b/test/__snapshots__/cli.test.ts.snap index 045f6409..af0c72c8 100644 --- a/test/__snapshots__/cli.test.ts.snap +++ b/test/__snapshots__/cli.test.ts.snap @@ -1,5 +1,372 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`csharp output 1`] = ` +Object { + "MyPackage/.jsii": "{ + \\"author\\": { + \\"name\\": \\"generated@generated.com\\", + \\"roles\\": [ + \\"author\\" + ] + }, + \\"description\\": \\"4c6b576fbe7e27053874813d9754cb2e46811d806c1e0aa9aae8add4c3763060\\", + \\"homepage\\": \\"http://generated\\", + \\"jsiiVersion\\": \\"1.12.0 (build 5ddc9f2)\\", + \\"license\\": \\"Apache-2.0\\", + \\"name\\": \\"4c6b576fbe7e27053874813d9754cb2e46811d806c1e0aa9aae8add4c3763060\\", + \\"repository\\": { + \\"type\\": \\"git\\", + \\"url\\": \\"http://generated\\" + }, + \\"schema\\": \\"jsii/0.10.0\\", + \\"targets\\": { + \\"dotnet\\": { + \\"namespace\\": \\"MyPackage\\", + \\"packageId\\": \\"MyPackage\\" + }, + \\"js\\": { + \\"npm\\": \\"4c6b576fbe7e27053874813d9754cb2e46811d806c1e0aa9aae8add4c3763060\\" + } + }, + \\"types\\": { + \\"4c6b576fbe7e27053874813d9754cb2e46811d806c1e0aa9aae8add4c3763060.Calculator\\": { + \\"assembly\\": \\"4c6b576fbe7e27053874813d9754cb2e46811d806c1e0aa9aae8add4c3763060\\", + \\"docs\\": { + \\"summary\\": \\"A sophisticaed multi-language calculator.\\" + }, + \\"fqn\\": \\"4c6b576fbe7e27053874813d9754cb2e46811d806c1e0aa9aae8add4c3763060.Calculator\\", + \\"initializer\\": {}, + \\"kind\\": \\"class\\", + \\"locationInModule\\": { + \\"filename\\": \\"lib/main.ts\\", + \\"line\\": 19 + }, + \\"methods\\": [ + { + \\"docs\\": { + \\"summary\\": \\"Adds the two operands.\\" + }, + \\"locationInModule\\": { + \\"filename\\": \\"lib/main.ts\\", + \\"line\\": 24 + }, + \\"name\\": \\"add\\", + \\"parameters\\": [ + { + \\"docs\\": { + \\"summary\\": \\"operands.\\" + }, + \\"name\\": \\"ops\\", + \\"type\\": { + \\"fqn\\": \\"4c6b576fbe7e27053874813d9754cb2e46811d806c1e0aa9aae8add4c3763060.Operands\\" + } + } + ], + \\"returns\\": { + \\"type\\": { + \\"primitive\\": \\"number\\" + } + } + }, + { + \\"docs\\": { + \\"summary\\": \\"Multiplies the two operands.\\" + }, + \\"locationInModule\\": { + \\"filename\\": \\"lib/main.ts\\", + \\"line\\": 40 + }, + \\"name\\": \\"mul\\", + \\"parameters\\": [ + { + \\"docs\\": { + \\"summary\\": \\"operands.\\" + }, + \\"name\\": \\"ops\\", + \\"type\\": { + \\"fqn\\": \\"4c6b576fbe7e27053874813d9754cb2e46811d806c1e0aa9aae8add4c3763060.Operands\\" + } + } + ], + \\"returns\\": { + \\"type\\": { + \\"primitive\\": \\"number\\" + } + } + }, + { + \\"docs\\": { + \\"summary\\": \\"Subtracts the two operands.\\" + }, + \\"locationInModule\\": { + \\"filename\\": \\"lib/main.ts\\", + \\"line\\": 32 + }, + \\"name\\": \\"sub\\", + \\"parameters\\": [ + { + \\"docs\\": { + \\"summary\\": \\"operands.\\" + }, + \\"name\\": \\"ops\\", + \\"type\\": { + \\"fqn\\": \\"4c6b576fbe7e27053874813d9754cb2e46811d806c1e0aa9aae8add4c3763060.Operands\\" + } + } + ], + \\"returns\\": { + \\"type\\": { + \\"primitive\\": \\"number\\" + } + } + } + ], + \\"name\\": \\"Calculator\\" + }, + \\"4c6b576fbe7e27053874813d9754cb2e46811d806c1e0aa9aae8add4c3763060.Operands\\": { + \\"assembly\\": \\"4c6b576fbe7e27053874813d9754cb2e46811d806c1e0aa9aae8add4c3763060\\", + \\"datatype\\": true, + \\"docs\\": { + \\"summary\\": \\"Math operands.\\" + }, + \\"fqn\\": \\"4c6b576fbe7e27053874813d9754cb2e46811d806c1e0aa9aae8add4c3763060.Operands\\", + \\"kind\\": \\"interface\\", + \\"locationInModule\\": { + \\"filename\\": \\"lib/main.ts\\", + \\"line\\": 4 + }, + \\"name\\": \\"Operands\\", + \\"properties\\": [ + { + \\"abstract\\": true, + \\"docs\\": { + \\"summary\\": \\"Left-hand side operand.\\" + }, + \\"immutable\\": true, + \\"locationInModule\\": { + \\"filename\\": \\"lib/main.ts\\", + \\"line\\": 8 + }, + \\"name\\": \\"lhs\\", + \\"type\\": { + \\"primitive\\": \\"number\\" + } + }, + { + \\"abstract\\": true, + \\"docs\\": { + \\"summary\\": \\"Right-hand side operand.\\" + }, + \\"immutable\\": true, + \\"locationInModule\\": { + \\"filename\\": \\"lib/main.ts\\", + \\"line\\": 13 + }, + \\"name\\": \\"rhs\\", + \\"type\\": { + \\"primitive\\": \\"number\\" + } + } + ] + } + }, + \\"version\\": \\"0.0.0\\", + \\"fingerprint\\": \\"F2Ja2YDGjKNYeGATUA/z5rZjbzgcQStHaXQMJ2VJRjg=\\" +} +", + "MyPackage/AssemblyInfo.cs": "using Amazon.JSII.Runtime.Deputy; + +[assembly: JsiiAssembly(\\"4c6b576fbe7e27053874813d9754cb2e46811d806c1e0aa9aae8add4c3763060\\", \\"0.0.0\\", \\"4c6b576fbe7e27053874813d9754cb2e46811d806c1e0aa9aae8add4c3763060-0.0.0.tgz\\")] +", + "MyPackage/MyPackage.csproj": " + + + 4c6b576fbe7e27053874813d9754cb2e46811d806c1e0aa9aae8add4c3763060 + MyPackage + Apache-2.0 + 0.0.0 + + generated@generated.com + en-US + http://generated + http://generated + git + + true + true + true + true + enable + snupkg + netcoreapp3.1 + + + + + + + + + 0612,0618 + + + +", + "MyPackage/MyPackage/Calculator.cs": "using Amazon.JSII.Runtime.Deputy; + +#pragma warning disable CS0672,CS0809,CS1591 + +namespace MyPackage +{ + /// A sophisticaed multi-language calculator. + [JsiiClass(nativeType: typeof(MyPackage.Calculator), fullyQualifiedName: \\"4c6b576fbe7e27053874813d9754cb2e46811d806c1e0aa9aae8add4c3763060.Calculator\\")] + public class Calculator : DeputyBase + { + public Calculator(): base(new DeputyProps(System.Array.Empty())) + { + } + + /// Used by jsii to construct an instance of this class from a Javascript-owned object reference + /// The Javascript-owned object reference + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + protected Calculator(ByRefValue reference): base(reference) + { + } + + /// Used by jsii to construct an instance of this class from DeputyProps + /// The deputy props + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + protected Calculator(DeputyProps props): base(props) + { + } + + /// Adds the two operands. + /// operands. + [JsiiMethod(name: \\"add\\", returnsJson: \\"{\\\\\\"type\\\\\\":{\\\\\\"primitive\\\\\\":\\\\\\"number\\\\\\"}}\\", parametersJson: \\"[{\\\\\\"docs\\\\\\":{\\\\\\"summary\\\\\\":\\\\\\"operands.\\\\\\"},\\\\\\"name\\\\\\":\\\\\\"ops\\\\\\",\\\\\\"type\\\\\\":{\\\\\\"fqn\\\\\\":\\\\\\"4c6b576fbe7e27053874813d9754cb2e46811d806c1e0aa9aae8add4c3763060.Operands\\\\\\"}}]\\")] + public virtual double Add(MyPackage.IOperands ops) + { + return InvokeInstanceMethod(new System.Type[]{typeof(MyPackage.IOperands)}, new object[]{ops}); + } + + /// Multiplies the two operands. + /// operands. + [JsiiMethod(name: \\"mul\\", returnsJson: \\"{\\\\\\"type\\\\\\":{\\\\\\"primitive\\\\\\":\\\\\\"number\\\\\\"}}\\", parametersJson: \\"[{\\\\\\"docs\\\\\\":{\\\\\\"summary\\\\\\":\\\\\\"operands.\\\\\\"},\\\\\\"name\\\\\\":\\\\\\"ops\\\\\\",\\\\\\"type\\\\\\":{\\\\\\"fqn\\\\\\":\\\\\\"4c6b576fbe7e27053874813d9754cb2e46811d806c1e0aa9aae8add4c3763060.Operands\\\\\\"}}]\\")] + public virtual double Mul(MyPackage.IOperands ops) + { + return InvokeInstanceMethod(new System.Type[]{typeof(MyPackage.IOperands)}, new object[]{ops}); + } + + /// Subtracts the two operands. + /// operands. + [JsiiMethod(name: \\"sub\\", returnsJson: \\"{\\\\\\"type\\\\\\":{\\\\\\"primitive\\\\\\":\\\\\\"number\\\\\\"}}\\", parametersJson: \\"[{\\\\\\"docs\\\\\\":{\\\\\\"summary\\\\\\":\\\\\\"operands.\\\\\\"},\\\\\\"name\\\\\\":\\\\\\"ops\\\\\\",\\\\\\"type\\\\\\":{\\\\\\"fqn\\\\\\":\\\\\\"4c6b576fbe7e27053874813d9754cb2e46811d806c1e0aa9aae8add4c3763060.Operands\\\\\\"}}]\\")] + public virtual double Sub(MyPackage.IOperands ops) + { + return InvokeInstanceMethod(new System.Type[]{typeof(MyPackage.IOperands)}, new object[]{ops}); + } + } +} +", + "MyPackage/MyPackage/IOperands.cs": "using Amazon.JSII.Runtime.Deputy; + +#pragma warning disable CS0672,CS0809,CS1591 + +namespace MyPackage +{ + /// Math operands. + [JsiiInterface(nativeType: typeof(IOperands), fullyQualifiedName: \\"4c6b576fbe7e27053874813d9754cb2e46811d806c1e0aa9aae8add4c3763060.Operands\\")] + public interface IOperands + { + /// Left-hand side operand. + [JsiiProperty(name: \\"lhs\\", typeJson: \\"{\\\\\\"primitive\\\\\\":\\\\\\"number\\\\\\"}\\")] + double Lhs + { + get; + } + + /// Right-hand side operand. + [JsiiProperty(name: \\"rhs\\", typeJson: \\"{\\\\\\"primitive\\\\\\":\\\\\\"number\\\\\\"}\\")] + double Rhs + { + get; + } + } +} +", + "MyPackage/MyPackage/Internal/DependencyResolution/Anchor.cs": "#pragma warning disable CS0672,CS0809,CS1591 + +namespace MyPackage.Internal.DependencyResolution +{ + public sealed class Anchor + { + public Anchor() + { + } + } +} +", + "MyPackage/MyPackage/Operands.cs": "using Amazon.JSII.Runtime.Deputy; + +#pragma warning disable CS0672,CS0809,CS1591 + +namespace MyPackage +{ + #pragma warning disable CS8618 + + /// Math operands. + [JsiiByValue(fqn: \\"4c6b576fbe7e27053874813d9754cb2e46811d806c1e0aa9aae8add4c3763060.Operands\\")] + public class Operands : MyPackage.IOperands + { + /// Left-hand side operand. + [JsiiProperty(name: \\"lhs\\", typeJson: \\"{\\\\\\"primitive\\\\\\":\\\\\\"number\\\\\\"}\\", isOverride: true)] + public double Lhs + { + get; + set; + } + + /// Right-hand side operand. + [JsiiProperty(name: \\"rhs\\", typeJson: \\"{\\\\\\"primitive\\\\\\":\\\\\\"number\\\\\\"}\\", isOverride: true)] + public double Rhs + { + get; + set; + } + } +} +", + "MyPackage/MyPackage/OperandsProxy.cs": "using Amazon.JSII.Runtime.Deputy; + +#pragma warning disable CS0672,CS0809,CS1591 + +namespace MyPackage +{ + /// Math operands. + [JsiiTypeProxy(nativeType: typeof(IOperands), fullyQualifiedName: \\"4c6b576fbe7e27053874813d9754cb2e46811d806c1e0aa9aae8add4c3763060.Operands\\")] + internal sealed class OperandsProxy : DeputyBase, MyPackage.IOperands + { + private OperandsProxy(ByRefValue reference): base(reference) + { + } + + /// Left-hand side operand. + [JsiiProperty(name: \\"lhs\\", typeJson: \\"{\\\\\\"primitive\\\\\\":\\\\\\"number\\\\\\"}\\")] + public double Lhs + { + get => GetInstanceProperty(); + } + + /// Right-hand side operand. + [JsiiProperty(name: \\"rhs\\", typeJson: \\"{\\\\\\"primitive\\\\\\":\\\\\\"number\\\\\\"}\\")] + public double Rhs + { + get => GetInstanceProperty(); + } + } +} +", +} +`; + exports[`java output 1`] = ` Object { "src/main/java/mypackage/$Module.java": "package mypackage; diff --git a/test/__snapshots__/srcmak.test.ts.snap b/test/__snapshots__/srcmak.test.ts.snap index 4e4ea797..28af7b7c 100644 --- a/test/__snapshots__/srcmak.test.ts.snap +++ b/test/__snapshots__/srcmak.test.ts.snap @@ -1,5 +1,274 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`csharp + different entrypoint 1`] = ` +Object { + "Hello.World/.jsii": "{ + \\"author\\": { + \\"name\\": \\"generated@generated.com\\", + \\"roles\\": [ + \\"author\\" + ] + }, + \\"description\\": \\"csharppackage\\", + \\"homepage\\": \\"http://generated\\", + \\"jsiiVersion\\": \\"1.12.0 (build 5ddc9f2)\\", + \\"license\\": \\"Apache-2.0\\", + \\"name\\": \\"csharppackage\\", + \\"repository\\": { + \\"type\\": \\"git\\", + \\"url\\": \\"http://generated\\" + }, + \\"schema\\": \\"jsii/0.10.0\\", + \\"targets\\": { + \\"dotnet\\": { + \\"namespace\\": \\"Hello.World\\", + \\"packageId\\": \\"Hello.World\\" + }, + \\"js\\": { + \\"npm\\": \\"csharppackage\\" + } + }, + \\"types\\": { + \\"csharppackage.Hello\\": { + \\"assembly\\": \\"csharppackage\\", + \\"fqn\\": \\"csharppackage.Hello\\", + \\"initializer\\": {}, + \\"kind\\": \\"class\\", + \\"locationInModule\\": { + \\"filename\\": \\"different/entry.ts\\", + \\"line\\": 7 + }, + \\"methods\\": [ + { + \\"locationInModule\\": { + \\"filename\\": \\"different/entry.ts\\", + \\"line\\": 8 + }, + \\"name\\": \\"add\\", + \\"parameters\\": [ + { + \\"name\\": \\"ops\\", + \\"type\\": { + \\"fqn\\": \\"csharppackage.Operands\\" + } + } + ], + \\"returns\\": { + \\"type\\": { + \\"primitive\\": \\"number\\" + } + } + } + ], + \\"name\\": \\"Hello\\" + }, + \\"csharppackage.Operands\\": { + \\"assembly\\": \\"csharppackage\\", + \\"datatype\\": true, + \\"fqn\\": \\"csharppackage.Operands\\", + \\"kind\\": \\"interface\\", + \\"locationInModule\\": { + \\"filename\\": \\"different/entry.ts\\", + \\"line\\": 2 + }, + \\"name\\": \\"Operands\\", + \\"properties\\": [ + { + \\"abstract\\": true, + \\"immutable\\": true, + \\"locationInModule\\": { + \\"filename\\": \\"different/entry.ts\\", + \\"line\\": 3 + }, + \\"name\\": \\"lhs\\", + \\"type\\": { + \\"primitive\\": \\"number\\" + } + }, + { + \\"abstract\\": true, + \\"immutable\\": true, + \\"locationInModule\\": { + \\"filename\\": \\"different/entry.ts\\", + \\"line\\": 4 + }, + \\"name\\": \\"rhs\\", + \\"type\\": { + \\"primitive\\": \\"number\\" + } + } + ] + } + }, + \\"version\\": \\"0.0.0\\", + \\"fingerprint\\": \\"mG6NnEA4XZ1m9xHiIMvxMdhoYlOfYkrtFxxKEuI8RdM=\\" +} +", + "Hello.World/AssemblyInfo.cs": "using Amazon.JSII.Runtime.Deputy; + +[assembly: JsiiAssembly(\\"csharppackage\\", \\"0.0.0\\", \\"csharppackage-0.0.0.tgz\\")] +", + "Hello.World/Hello.World.csproj": " + + + csharppackage + Hello.World + Apache-2.0 + 0.0.0 + + generated@generated.com + en-US + http://generated + http://generated + git + + true + true + true + true + enable + snupkg + netcoreapp3.1 + + + + + + + + + 0612,0618 + + + +", + "Hello.World/Hello/World/Hello.cs": "using Amazon.JSII.Runtime.Deputy; + +#pragma warning disable CS0672,CS0809,CS1591 + +namespace Hello.World +{ + [JsiiClass(nativeType: typeof(Hello.World.Hello), fullyQualifiedName: \\"csharppackage.Hello\\")] + public class Hello : DeputyBase + { + public Hello(): base(new DeputyProps(System.Array.Empty())) + { + } + + /// Used by jsii to construct an instance of this class from a Javascript-owned object reference + /// The Javascript-owned object reference + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + protected Hello(ByRefValue reference): base(reference) + { + } + + /// Used by jsii to construct an instance of this class from DeputyProps + /// The deputy props + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + protected Hello(DeputyProps props): base(props) + { + } + + [JsiiMethod(name: \\"add\\", returnsJson: \\"{\\\\\\"type\\\\\\":{\\\\\\"primitive\\\\\\":\\\\\\"number\\\\\\"}}\\", parametersJson: \\"[{\\\\\\"name\\\\\\":\\\\\\"ops\\\\\\",\\\\\\"type\\\\\\":{\\\\\\"fqn\\\\\\":\\\\\\"csharppackage.Operands\\\\\\"}}]\\")] + public virtual double Add(Hello.World.IOperands ops) + { + return InvokeInstanceMethod(new System.Type[]{typeof(Hello.World.IOperands)}, new object[]{ops}); + } + } +} +", + "Hello.World/Hello/World/IOperands.cs": "using Amazon.JSII.Runtime.Deputy; + +#pragma warning disable CS0672,CS0809,CS1591 + +namespace Hello.World +{ + [JsiiInterface(nativeType: typeof(IOperands), fullyQualifiedName: \\"csharppackage.Operands\\")] + public interface IOperands + { + [JsiiProperty(name: \\"lhs\\", typeJson: \\"{\\\\\\"primitive\\\\\\":\\\\\\"number\\\\\\"}\\")] + double Lhs + { + get; + } + + [JsiiProperty(name: \\"rhs\\", typeJson: \\"{\\\\\\"primitive\\\\\\":\\\\\\"number\\\\\\"}\\")] + double Rhs + { + get; + } + } +} +", + "Hello.World/Hello/World/Internal/DependencyResolution/Anchor.cs": "#pragma warning disable CS0672,CS0809,CS1591 + +namespace Hello.World.Internal.DependencyResolution +{ + public sealed class Anchor + { + public Anchor() + { + } + } +} +", + "Hello.World/Hello/World/Operands.cs": "using Amazon.JSII.Runtime.Deputy; + +#pragma warning disable CS0672,CS0809,CS1591 + +namespace Hello.World +{ + #pragma warning disable CS8618 + + [JsiiByValue(fqn: \\"csharppackage.Operands\\")] + public class Operands : Hello.World.IOperands + { + [JsiiProperty(name: \\"lhs\\", typeJson: \\"{\\\\\\"primitive\\\\\\":\\\\\\"number\\\\\\"}\\", isOverride: true)] + public double Lhs + { + get; + set; + } + + [JsiiProperty(name: \\"rhs\\", typeJson: \\"{\\\\\\"primitive\\\\\\":\\\\\\"number\\\\\\"}\\", isOverride: true)] + public double Rhs + { + get; + set; + } + } +} +", + "Hello.World/Hello/World/OperandsProxy.cs": "using Amazon.JSII.Runtime.Deputy; + +#pragma warning disable CS0672,CS0809,CS1591 + +namespace Hello.World +{ + [JsiiTypeProxy(nativeType: typeof(IOperands), fullyQualifiedName: \\"csharppackage.Operands\\")] + internal sealed class OperandsProxy : DeputyBase, Hello.World.IOperands + { + private OperandsProxy(ByRefValue reference): base(reference) + { + } + + [JsiiProperty(name: \\"lhs\\", typeJson: \\"{\\\\\\"primitive\\\\\\":\\\\\\"number\\\\\\"}\\")] + public double Lhs + { + get => GetInstanceProperty(); + } + + [JsiiProperty(name: \\"rhs\\", typeJson: \\"{\\\\\\"primitive\\\\\\":\\\\\\"number\\\\\\"}\\")] + public double Rhs + { + get => GetInstanceProperty(); + } + } +} +", +} +`; + exports[`java + different entrypoint 1`] = ` Object { "src/main/java/hello/world/$Module.java": "package hello.world; diff --git a/test/cli.test.ts b/test/cli.test.ts index 13ccc28b..9689ec4c 100644 --- a/test/cli.test.ts +++ b/test/cli.test.ts @@ -61,6 +61,18 @@ test('fails if only one java option is given', () => { )).toThrow(/--java-package is required/); }); +test('fails if only one csharp option is given', () => { + expect(() => srcmakcli(srcdir, + '--entrypoint lib/main.ts', + '--csharp-namespace mypackage', + )).toThrow(/--csharp-outdir is required/); + + expect(() => srcmakcli(srcdir, + '--entrypoint lib/main.ts', + '--csharp-outdir dir', + )).toThrow(/--csharp-namespace is required/); +}); + test('python output', async () => { await mkdtemp(async tmpdir => { @@ -97,6 +109,23 @@ test('java output', async () => { }); }); +test('csharp output', async () => { + await mkdtemp(async tmpdir => { + // put under a non-existent directory to verify it is created + const outdir = path.join(tmpdir, 'csharp'); + + srcmakcli(srcdir, + '--entrypoint lib/main.ts', + `--csharp-outdir ${outdir}`, + '--csharp-namespace MyPackage', + ); + + expect(await snapshotDirectory(outdir, { + excludeFiles: [ '0.0.0.tgz' ], + })).toMatchSnapshot(); + }); +}); + test('dependencies', async () => { await mkdtemp(async srcdir => { await fs.writeFile(path.join(srcdir, 'index.ts'), ` diff --git a/test/srcmak.test.ts b/test/srcmak.test.ts index ae30f1ba..0ae2d5c8 100644 --- a/test/srcmak.test.ts +++ b/test/srcmak.test.ts @@ -113,6 +113,42 @@ test('java + different entrypoint', async () => { }); }); +test('csharp + different entrypoint', async () => { + await mkdtemp(async source => { + const entry = 'different/entry.ts'; + const ep = path.join(source, entry); + await fs.mkdirp(path.dirname(ep)); + await fs.writeFile(ep, ` + export interface Operands { + readonly lhs: number; + readonly rhs: number; + } + + export class Hello { + public add(ops: Operands): number { + return ops.lhs + ops.rhs; + } + } + `); + + await mkdtemp(async target => { + await srcmak(source, { + entrypoint: 'different/entry.ts', + moduleKey: 'csharp.package', + csharp: { + outdir: target, + namespace: 'Hello.World', + }, + }); + + const dir = await snapshotDirectory(target, { + excludeFiles: [ '0.0.0.tgz' ], + }); + expect(dir).toMatchSnapshot(); + }); + }); +}); + test('deps: compile against a local jsii dependency', async () => { await mkdtemp(async source => { await fs.writeFile(path.join(source, 'index.ts'), ` @@ -169,4 +205,15 @@ test('python with invalid module name', async () => { moduleName: 'my-python.submodule', }, })).rejects.toEqual(new Error('Python moduleName [my-python.submodule] may not contain "-"')); +}); + +test('csharp with invalid namespace', async () => { + await expect(srcmak('.', { + entrypoint: 'different/entry.ts', + moduleKey: 'csharp.package', + csharp: { + outdir: '.', + namespace: 'hello-world', + }, + })).rejects.toEqual(new Error('C# namespace [hello-world] may not contain "-"')); }); \ No newline at end of file