Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(rosetta): add skeleton of Java snippet translator #985

Merged
merged 98 commits into from
Dec 24, 2019
Merged
Show file tree
Hide file tree
Changes from 91 commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
abb0299
feat(rosetta): add skeleton of Java snippet translator.
skinny85 Nov 13, 2019
41091c6
Refactor tests a bit
Nov 14, 2019
3f0cd2c
Show AST for all failing tests
Nov 14, 2019
fd0565e
Token start of C#, add instructions on how to selectively run tests
Nov 14, 2019
a900474
Some renames and token translations for C#
Nov 14, 2019
2933be3
Working on the .NET translator
Nov 14, 2019
f89c30c
Indentation is still a little weird but at least everything passes
Nov 14, 2019
853b165
Adding a whole bunch of other tests for C#
Nov 14, 2019
203c86c
Got comments tests working
Nov 14, 2019
eb3c892
Hope I didn't brake things
Nov 14, 2019
93fec4b
Actually, you did break things Rico
skinny85 Nov 14, 2019
2247d95
Small refactorings of the Java translation skeleton.
skinny85 Nov 14, 2019
459b01f
Implemented simple inheritance for Java classes.
skinny85 Nov 15, 2019
05916b7
Handle the 'implements' keyword in the Java translator.
skinny85 Nov 15, 2019
e92cb22
Implement method declarations (with only print statements).
skinny85 Nov 15, 2019
cccc981
Correctly derive map type from object literal.
Nov 15, 2019
0e38f57
C# imports
Nov 15, 2019
580cff9
Classes
Nov 15, 2019
c827a26
All C# tests pass
Nov 15, 2019
27234c0
All C# tests pass
Nov 15, 2019
30a7f21
Integrate in .NET generator
Nov 15, 2019
4536658
Merge branch 'feat/java-snippets-translation' of github.com:awslabs/j…
Nov 15, 2019
1ea305e
Integrate example converter for Java
Nov 15, 2019
40f4b7e
Update failing jsii-packmak tests.
skinny85 Nov 15, 2019
334dffe
Implement Java constructor.
skinny85 Nov 16, 2019
9bafb7e
Update Java documentation.
skinny85 Nov 16, 2019
e059d77
Implement public properties as fields in Java.
skinny85 Nov 16, 2019
a4d219a
Private properties as fields work as well.
skinny85 Nov 16, 2019
156a4e6
Calls with boolean parameters work for free.
skinny85 Nov 16, 2019
122d1e3
Implement 'if' (without 'else').
skinny85 Nov 16, 2019
fbbe77b
Implement 'else' part of 'if'.
skinny85 Nov 16, 2019
f19b469
Fix incorrect struct type derivation inside assignment
Nov 18, 2019
3912893
Fix some style for comment rendering
Nov 18, 2019
cb308ed
Fix summary extraction for Vpc construct
Nov 18, 2019
46d1b40
Fix struct type derivation if props are optional
Nov 18, 2019
fe330ad
Impove rendering of HTML-like docstrings.
Nov 18, 2019
ccbe377
Emit [EditorBrowseableAttribute()] for internal API members
Nov 18, 2019
64dbb7f
Multi-line 'if' statement works already, as it turns out.
skinny85 Nov 27, 2019
2bc84c7
As does an empty control block.
skinny85 Nov 27, 2019
c6cc854
As does a block without braces.
skinny85 Nov 27, 2019
f8f50f8
As do the whitespace inside statement blocks.
skinny85 Nov 27, 2019
43e45a6
Update broken jsii-calc-lib tests.
skinny85 Nov 27, 2019
a75bf21
Update broken jsii-calc tests.
skinny85 Nov 27, 2019
dbec937
Update broken jsii-pacmak tests.
skinny85 Nov 28, 2019
87a464a
Handle for-of loops in Java.
skinny85 Nov 27, 2019
cc53ec8
Handle full imports in Java.
skinny85 Nov 28, 2019
0ecf3c5
We get 'import * as' in Java for free.
skinny85 Nov 28, 2019
05966dc
Handle selective imports in Java (but without renaming).
skinny85 Nov 28, 2019
8ad2568
WIPWIP
Nov 29, 2019
a70ed47
Update expectations.
rix0rrr Nov 29, 2019
19c4b3d
Update expectations.
Nov 29, 2019
fa5b47f
Merge commit 'a70ed47c' into feat/java-snippets-translation
rix0rrr Nov 29, 2019
31e913c
Commit with fake merge from 'master'
rix0rrr Nov 29, 2019
a7b6a11
Merge remote-tracking branch 'origin/master' into huijbers/experiment
rix0rrr Nov 29, 2019
fad508c
Remove exposed dependency on commonmark.
rix0rrr Nov 29, 2019
dc453b7
Handle string interpolation in the Java translator.
skinny85 Nov 29, 2019
21dec30
Update broken jsii-pacmak tests.
skinny85 Nov 29, 2019
0917dd7
We get prefix unary expressions for free in Java.
skinny85 Nov 29, 2019
0512e17
Handle non-null expressions in Java.
skinny85 Nov 29, 2019
7b5cf3d
Fix eslint indentation error.
skinny85 Nov 30, 2019
acda185
Ellipsis handling works out of the box for Java.
skinny85 Nov 30, 2019
18aae78
Fix calling Java methods.
skinny85 Nov 30, 2019
4b92e48
Backtick strings without substitutions work already.
skinny85 Nov 30, 2019
9ac849d
Handles 'as' expressions in Java translation.
skinny85 Nov 30, 2019
cc363b4
Mirror closing braces for C# structs
rix0rrr Dec 2, 2019
38de02b
Merge branch 'feat/java-snippets-translation' of github.com:awslabs/j…
rix0rrr Dec 2, 2019
beb2565
Implement map literals in Java.
skinny85 Dec 2, 2019
76a09a8
Function and method calls already work in Java.
skinny85 Dec 2, 2019
27dc4b3
Handle stand-alone functions and Maps passed in call arguments in Java.
skinny85 Dec 2, 2019
ae09207
Fix linter errors.
skinny85 Dec 2, 2019
7aef5ce
Update broken jsii-pacmak tests.
skinny85 Dec 2, 2019
5acb2da
This test is the same as the previous one.
skinny85 Dec 2, 2019
2f520e7
Nested map literals work as well.
skinny85 Dec 2, 2019
334033e
Implement array expressions in Java.
skinny85 Dec 3, 2019
09a43b1
Correctly handle multi-argument `console.log` calls in Java.
skinny85 Dec 3, 2019
6b59ad4
Fix linter error.
skinny85 Dec 3, 2019
3fbcaa7
Implement optional method parameters in Java translation by generatin…
skinny85 Dec 17, 2019
5f7f989
Handle overloaded constructors in Java translations.
skinny85 Dec 17, 2019
c957460
Handle struct declaration and instantiation in Java translation.
skinny85 Dec 18, 2019
8ef027d
Update jsii-pacmak tests.
skinny85 Dec 18, 2019
2a91be6
Correctly break lines in builder setter expressions in Java.
skinny85 Dec 18, 2019
b302b54
Hiding tests work already in Java.
skinny85 Dec 19, 2019
f0656af
Comment tests work already in Java.
skinny85 Dec 19, 2019
0a2688d
Creating a class with a struct as the last argument in Java translation.
skinny85 Dec 19, 2019
6c635cc
Struct tests also pass now in Java.
skinny85 Dec 19, 2019
9d3d583
Correctly render user-defined types for parameters.
skinny85 Dec 19, 2019
242c0c1
Change rendering of Map in Java: change the indent to 8, and don't ha…
skinny85 Dec 19, 2019
d9344b7
Report a warning if a union type is used in a snippet that's translat…
skinny85 Dec 19, 2019
c867e4d
Simplify the print statement in Java translation to omit the leading …
skinny85 Dec 19, 2019
818f3f3
Add comments to the JavaContext properties.
skinny85 Dec 19, 2019
e17cf32
Render all `new` expressions that have an object literal as its last …
skinny85 Dec 21, 2019
ae1b3ce
Render 'var' instead of 'Object' for variable declarations with unkno…
skinny85 Dec 23, 2019
e68da6f
Render structs as classes with fluent setters in Java.
skinny85 Dec 23, 2019
8557e1e
Remove the 'set' prefix from setters generated from structs in Java.
skinny85 Dec 23, 2019
db15d86
Merge remote-tracking branch 'origin/master' into feat/java-snippets-…
rix0rrr Dec 24, 2019
850009b
Update expectations
rix0rrr Dec 24, 2019
970afad
Fix escaping for Java and C#
rix0rrr Dec 24, 2019
8465500
Update expectations
rix0rrr Dec 24, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 3 additions & 2 deletions packages/@scope/jsii-calc-lib/test/assembly.jsii
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,9 @@
"@scope/jsii-calc-lib.EnumFromScopedModule": {
"assembly": "@scope/jsii-calc-lib",
"docs": {
"remarks": "See awslabs/jsii#138",
"stability": "deprecated",
"summary": "Check that enums from \\@scoped packages can be references. See awslabs/jsii#138."
"summary": "Check that enums from \\@scoped packages can be references."
},
"fqn": "@scope/jsii-calc-lib.EnumFromScopedModule",
"kind": "enum",
Expand Down Expand Up @@ -539,5 +540,5 @@
}
},
"version": "0.20.8",
"fingerprint": "FFFRcCrdPYvpu+IupXHGE0msGdLt4fpbgDMBKjGdCOs="
"fingerprint": "dP9qhYQ8sdUC+F7c427cCKa0pFPvot6EAZn2BNeUlNM="
}
18 changes: 11 additions & 7 deletions packages/jsii-calc/test/assembly.jsii
Original file line number Diff line number Diff line change
Expand Up @@ -7194,9 +7194,9 @@
"abstract": true,
"docs": {
"default": "256",
"remarks": "This default is set in the underlying FargateTaskDefinition construct.",
"remarks": "Valid values, which determines your range of valid values for the memory parameter:\n256 (.25 vCPU) - Available memory values: 0.5GB, 1GB, 2GB\n512 (.5 vCPU) - Available memory values: 1GB, 2GB, 3GB, 4GB\n1024 (1 vCPU) - Available memory values: 2GB, 3GB, 4GB, 5GB, 6GB, 7GB, 8GB\n2048 (2 vCPU) - Available memory values: Between 4GB and 16GB in 1GB increments\n4096 (4 vCPU) - Available memory values: Between 8GB and 30GB in 1GB increments\n\nThis default is set in the underlying FargateTaskDefinition construct.",
"stability": "experimental",
"summary": "The number of cpu units used by the task. Valid values, which determines your range of valid values for the memory parameter: 256 (.25 vCPU) - Available memory values: 0.5GB, 1GB, 2GB 512 (.5 vCPU) - Available memory values: 1GB, 2GB, 3GB, 4GB 1024 (1 vCPU) - Available memory values: 2GB, 3GB, 4GB, 5GB, 6GB, 7GB, 8GB 2048 (2 vCPU) - Available memory values: Between 4GB and 16GB in 1GB increments 4096 (4 vCPU) - Available memory values: Between 8GB and 30GB in 1GB increments."
"summary": "The number of cpu units used by the task."
},
"immutable": true,
"locationInModule": {
Expand Down Expand Up @@ -8536,8 +8536,9 @@
},
{
"docs": {
"remarks": "Must be implemented by derived classes.",
"stability": "experimental",
"summary": "The expression that this operation consists of. Must be implemented by derived classes."
"summary": "The expression that this operation consists of."
},
"immutable": true,
"locationInModule": {
Expand Down Expand Up @@ -9811,8 +9812,9 @@
},
{
"docs": {
"remarks": "Jsdocs for static setter.",
"stability": "experimental",
"summary": "Jsdocs for static getter. Jsdocs for static setter."
"summary": "Jsdocs for static getter."
},
"locationInModule": {
"filename": "lib/compliance.ts",
Expand Down Expand Up @@ -10305,8 +10307,9 @@
"properties": [
{
"docs": {
"remarks": "Must be implemented by derived classes.",
"stability": "experimental",
"summary": "The expression that this operation consists of. Must be implemented by derived classes."
"summary": "The expression that this operation consists of."
},
"immutable": true,
"locationInModule": {
Expand Down Expand Up @@ -11627,8 +11630,9 @@
{
"abstract": true,
"docs": {
"remarks": "Must be implemented by derived classes.",
"stability": "experimental",
"summary": "The expression that this operation consists of. Must be implemented by derived classes."
"summary": "The expression that this operation consists of."
},
"immutable": true,
"locationInModule": {
Expand Down Expand Up @@ -11743,5 +11747,5 @@
}
},
"version": "0.20.8",
"fingerprint": "w4S74J2aEQiII5p1/vLG5rXFrtvbxUP1mra7ZGBd4so="
"fingerprint": "RXyKxe7cZhjQraeBtWqnjdNPkV1KRAT6oHLGnU2sla4="
}
2 changes: 1 addition & 1 deletion packages/jsii-pacmak/lib/targets/dotnet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ export default class Dotnet extends Target {
public constructor(options: TargetOptions, assembliesCurrentlyBeingCompiled: string[]) {
super(options);

this.generator = new DotNetGenerator(assembliesCurrentlyBeingCompiled);
this.generator = new DotNetGenerator(assembliesCurrentlyBeingCompiled, options.rosetta);
}

/* eslint-disable @typescript-eslint/require-await */
Expand Down
132 changes: 85 additions & 47 deletions packages/jsii-pacmak/lib/targets/dotnet/dotnetdocgenerator.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { CodeMaker } from 'codemaker';
import * as spec from '@jsii/spec';
import { DotNetNameUtils } from './nameutils';
import { prefixMarkdownTsCodeBlocks } from '../../util';
import { Rosetta, Translation, typeScriptSnippetFromSource, markDownToXmlDoc } from 'jsii-rosetta';
import { INCOMPLETE_DISCLAIMER_COMPILING, INCOMPLETE_DISCLAIMER_NONCOMPILING } from '..';


const SAMPLES_DISCLAIMER = '// This example is in TypeScript, examples in C# are coming soon.';

/**
* Generates the Jsii attributes and calls for the .NET runtime
*
Expand All @@ -15,7 +14,7 @@ export class DotNetDocGenerator {
private readonly code: CodeMaker;
private readonly nameutils: DotNetNameUtils = new DotNetNameUtils();

public constructor(code: CodeMaker) {
public constructor(code: CodeMaker, private readonly rosetta: Rosetta) {
this.code = code;
}

Expand All @@ -29,7 +28,7 @@ export class DotNetDocGenerator {
* Returns
* Remarks (includes examples, links, deprecated)
*/
public emitDocs(obj: spec.Method | spec.InterfaceType | spec.ClassType | spec.Property | spec.EnumType | spec.Initializer): void {
public emitDocs(obj: spec.Documentable): void {
const docs = obj.docs;

// The docs may be undefined at the method level but not the parameters level
Expand All @@ -52,7 +51,7 @@ export class DotNetDocGenerator {
});
}

// At this point we only need a valid instance of docs
// At this pdocfx namespacedocd a valid instance of docs
if (!docs) {
return;
}
Expand All @@ -64,62 +63,101 @@ export class DotNetDocGenerator {
this.code.line('/// </returns>');
}

const remarks: string[] = [];
let remarksOpen = false;
if (docs.remarks) {
const remarks = this.renderRemarks(docs);
if (remarks.length > 0) {
this.code.line('/// <remarks>');
remarksOpen = true;
const remarkLines = prefixMarkdownTsCodeBlocks(docs.remarks, SAMPLES_DISCLAIMER).split('\n');
remarkLines.forEach( line => this.code.line(`/// ${line}`));
remarks.forEach(r => this.code.line(`/// ${r}`));
this.code.line('/// </remarks>');
}

if (docs.default) {
const defaultLines = docs.default.split('\n');
remarks.push('default:');
defaultLines.forEach( line => remarks.push(`${line}`));
if (docs.example) {
const exampleLines = this.convertExample(docs.example).split('\n');
this.code.line('/// <example>');
this.code.line('/// <code>');
exampleLines.forEach( line => this.code.line(`/// ${line}`));
this.code.line('/// </code>');
this.code.line('/// </example>');
}
}

if (docs.stability) {
remarks.push(`stability: ${this.nameutils.capitalizeWord(docs.stability)}`);
}
public emitMarkdownAsRemarks(markdown: string | undefined) {
if (!markdown) { return; }

if (docs.example) {
const remarkLines = docs.example.split('\n');
remarks.push('example:');
remarks.push('<code>');
remarks.push('// Examples in C# are coming soon.');
remarkLines.forEach( line => remarks.push(`${line}`));
remarks.push('</code>');
}
const translated = markDownToXmlDoc(this.convertSamplesInMarkdown(markdown));
const lines = translated.split('\n');

if (docs.see) {
const seeLines = docs.see.split('\n');
remarks.push('see:');
seeLines.forEach( line => remarks.push(`${line}`));
this.code.line('/// <remarks>');
for (const line of lines) {
this.code.line(`/// ${line}`);
}
this.code.line('/// </remarks>');
}

/**
* Returns the lines that should go into the <remarks> section
*/
private renderRemarks(docs: spec.Docs): string[] {
const ret: string[] = [];

if (docs.subclassable) {
remarks.push('subclassable');
if (docs.remarks) {
const translated = markDownToXmlDoc(this.convertSamplesInMarkdown(docs.remarks));
ret.push(...translated.split('\n'));
ret.push('');
}

if (docs.custom) {
for (const [k, v] of Object.entries(docs.custom ?? {})) {
const custom = k === 'link' ? `${k}: ${v} ` : `${k}: ${v}`; // Extra space for '@link' to keep unit tests happy
const customLines = custom.split('\n');
customLines.forEach( line => remarks.push(`${line}`));
}
// All the "tags" need to be rendered with empyt lines between them or they'll be word wrapped.

if (docs.default) { emitDocAttribute('default', docs.default); }
if (docs.stability) { emitDocAttribute('stability', this.nameutils.capitalizeWord(docs.stability)); }
if (docs.see) { emitDocAttribute('see', docs.see); }
if (docs.subclassable) { emitDocAttribute('subclassable', ''); }
for (const [k, v] of Object.entries(docs.custom || {})) {
const extraSpace = k === 'link' ? ' ' : ''; // Extra space for '@link' to keep unit tests happy
emitDocAttribute(k, v + extraSpace);
}

if (remarks.length > 0) {
if (!remarksOpen) {
this.code.line('/// <remarks>');
remarksOpen = true;
}
remarks.forEach( line => this.code.line(`/// ${line}`));
// Remove leading and trailing empty lines
while (ret.length > 0 && ret[0] === '') { ret.shift(); }
while (ret.length > 0 && ret[ret.length - 1] === '') { ret.pop(); }

return ret;

function emitDocAttribute(name: string, contents: string) {
const ls = contents.split('\n');
ret.push(`<strong>${ucFirst(name)}</strong>: ${ls[0]}`);
ret.push(...ls.slice(1));
ret.push('');
}
}

if (remarksOpen) {
this.code.line('/// </remarks>');
private convertExample(example: string): string {
const snippet = typeScriptSnippetFromSource(example, 'example');
const translated = this.rosetta.translateSnippet(snippet, 'csharp');
if (!translated) { return example; }
return this.prefixDisclaimer(translated);
}

private convertSamplesInMarkdown(markdown: string): string {
return this.rosetta.translateSnippetsInMarkdown(markdown, 'csharp', trans => ({
language: trans.language,
source: this.prefixDisclaimer(trans)
}));
}

private prefixDisclaimer(translated: Translation) {
if (translated.didCompile && INCOMPLETE_DISCLAIMER_COMPILING) {
return `// ${INCOMPLETE_DISCLAIMER_COMPILING}\n${translated.source}`;
}
if (!translated.didCompile && INCOMPLETE_DISCLAIMER_NONCOMPILING) {
return `// ${INCOMPLETE_DISCLAIMER_NONCOMPILING}\n${translated.source}`;
}
return translated.source;
}
}

/**
* Uppercase the first letter
*/
function ucFirst(x: string) {
return x.substr(0, 1).toUpperCase() + x.substr(1);
}
45 changes: 43 additions & 2 deletions packages/jsii-pacmak/lib/targets/dotnet/dotnetgenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { DotNetRuntimeGenerator } from './dotnetruntimegenerator';
import { DotNetTypeResolver } from './dotnettyperesolver';
import { DotNetDependency, FileGenerator } from './filegenerator';
import { DotNetNameUtils } from './nameutils';
import { Rosetta } from 'jsii-rosetta';

/**
* CODE GENERATOR V2
Expand All @@ -28,7 +29,7 @@ export class DotNetGenerator extends Generator {

private dotnetDocGenerator!: DotNetDocGenerator;

public constructor(private readonly assembliesCurrentlyBeingCompiled: string[]) {
public constructor(private readonly assembliesCurrentlyBeingCompiled: string[], private readonly rosetta: Rosetta) {
super();

// Override the openBlock to get a correct C# looking code block with the curly brace after the line
Expand All @@ -54,7 +55,9 @@ export class DotNetGenerator extends Generator {
);

this.dotnetRuntimeGenerator = new DotNetRuntimeGenerator(this.code, this.typeresolver);
this.dotnetDocGenerator = new DotNetDocGenerator(this.code);
this.dotnetDocGenerator = new DotNetDocGenerator(this.code, this.rosetta);

this.emitNamespaceDocs();

// We need to resolve the dependency tree
this.typeresolver.resolveNamespacesDependencies();
Expand Down Expand Up @@ -270,11 +273,13 @@ export class DotNetGenerator extends Generator {
}

this.dotnetRuntimeGenerator.emitDeprecatedAttributeIfNecessary(initializer);
this.emitHideAttribute();
this.code.openBlock(`protected ${className}(ByRefValue reference): base(reference)`);
this.code.closeBlock();
this.code.line();

this.dotnetRuntimeGenerator.emitDeprecatedAttributeIfNecessary(initializer);
this.emitHideAttribute();
this.code.openBlock(`protected ${className}(DeputyProps props): base(props)`);
this.code.closeBlock();

Expand Down Expand Up @@ -795,4 +800,40 @@ export class DotNetGenerator extends Generator {
this.firstMemberWritten = false;
}
}

/**
* Emit an unused, empty class called `NamespaceDoc` to attach the module README to
*
* There is no way to attach doc comments to a namespace in C#, and this trick has been
* semi-standardized by NDoc and Sandcastle Help File Builder.
*
* DocFX doesn't support it out of the box, but we should be able to get there with a
* bit of hackery.
*
* In any case, we need a place to attach the docs where they can be transported around,
* might as well be this method.
*/
private emitNamespaceDocs() {
if (!this.assembly.readme) { return; }

const namespace = this.assembly.targets!.dotnet!.namespace;
const className = 'NamespaceDoc';
this.openFileIfNeeded(className, namespace, false, false);

this.dotnetDocGenerator.emitMarkdownAsRemarks(this.assembly.readme.markdown);
this.emitHideAttribute();
// Traditionally this class is made 'internal', but that interacts poorly with DocFX's default filters
// which aren't overridable. So we make it public, but use attributes to hide it from users' IntelliSense,
// so that we can access the class in DocFX.
this.code.openBlock(`public class ${className}`);
this.code.closeBlock();
this.closeFileIfNeeded(className, namespace, false);
}

/**
* Emit an attribute that will hide the subsequent API element from users
*/
private emitHideAttribute() {
this.code.line('[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]');
}
}
3 changes: 2 additions & 1 deletion packages/jsii-pacmak/lib/targets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ export const ALL_BUILDERS: {[key in TargetName]: BuilderFactory} = {
ruby: (ms, o) => new OneByOneBuilder('ruby', Ruby, ms, o),
};


export const INCOMPLETE_DISCLAIMER_COMPILING = 'Example automatically generated. See https://github.com/aws/jsii/issues/826';
export const INCOMPLETE_DISCLAIMER_NONCOMPILING = 'Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826';
Loading