@@ -2,9 +2,16 @@ import * as spec from '@jsii/spec';
22import { toSnakeCase , toPascalCase } from 'codemaker' ;
33import * as fs from 'fs-extra' ;
44import * as reflect from 'jsii-reflect' ;
5+ import {
6+ ApiLocation ,
7+ enforcesStrictMode ,
8+ RosettaTabletReader ,
9+ TargetLanguage ,
10+ } from 'jsii-rosetta' ;
511import * as path from 'path' ;
612
713import { Generator , Legalese } from '../generator' ;
14+ import { assertSpecIsRosettaCompatible } from '../rosetta-assembly' ;
815import { Target , TargetOptions } from '../target' ;
916import { subprocess } from '../util' ;
1017import { VERSION } from '../version' ;
@@ -17,7 +24,7 @@ export class RubyTarget extends Target {
1724
1825 public constructor ( options : TargetOptions ) {
1926 super ( options ) ;
20- this . generator = new RubyGenerator ( options ) ;
27+ this . generator = new RubyGenerator ( options . rosetta , options ) ;
2128 }
2229
2330 public async build ( sourceDir : string , outDir : string ) : Promise < void > {
@@ -211,12 +218,32 @@ const RUBY_RESERVED_NAMES = new Set([
211218] ) ;
212219
213220export class RubyGenerator extends Generator {
214- public constructor ( options : TargetOptions ) {
221+ public constructor (
222+ private readonly rosetta : RosettaTabletReader ,
223+ options : TargetOptions ,
224+ ) {
215225 super ( { runtimeTypeChecking : options . runtimeTypeChecking } ) ;
216226 // Ruby convention is 2-space indentation (CodeMaker defaults to 4).
217227 this . code . indentation = 2 ;
218228 }
219229
230+ /**
231+ * Translate a jsii `@example` (authored in TypeScript) into idiomatic Ruby
232+ * via Rosetta. Falls back to the original text if translation fails, so a
233+ * bad snippet degrades to a TypeScript example rather than breaking the
234+ * build.
235+ */
236+ private convertExample ( example : string , apiLocation : ApiLocation ) : string {
237+ assertSpecIsRosettaCompatible ( this . assembly ) ;
238+ const translated = this . rosetta . translateExample (
239+ apiLocation ,
240+ example ,
241+ TargetLanguage . RUBY ,
242+ enforcesStrictMode ( this . assembly ) ,
243+ ) ;
244+ return translated . source ;
245+ }
246+
220247 /**
221248 * Normalize a type reference to its raw `spec.TypeReference` shape.
222249 * Call sites hold two shapes: jsii-reflect `TypeReference` instances
@@ -389,6 +416,12 @@ export class RubyGenerator extends Generator {
389416 /** Property getter: emit an @return of the property's type. */
390417 propertyType ?: RubyTypeRef ;
391418 propertyOptional ?: boolean ;
419+ /**
420+ * The API location the docs belong to. When present, `@example`
421+ * snippets are translated to Ruby via Rosetta; without it they are
422+ * emitted verbatim (i.e. as the original TypeScript).
423+ */
424+ apiLocation ?: ApiLocation ;
392425 } = { } ,
393426 ) : void {
394427 const docs : spec . Docs = this . rawDocs ( docsSource ) ?? { } ;
@@ -430,12 +463,14 @@ export class RubyGenerator extends Generator {
430463 tags . push ( `# @see ${ this . inlineDoc ( docs . see ) } ` ) ;
431464 }
432465
433- const exampleLines = docs . example
466+ const exampleText =
467+ docs . example && opts . apiLocation
468+ ? this . convertExample ( docs . example , opts . apiLocation )
469+ : docs . example ;
470+ const exampleLines = exampleText
434471 ? [
435472 '# @example' ,
436- ...docs . example
437- . split ( '\n' )
438- . map ( ( l ) => `# ${ l . trimEnd ( ) } ` . trimEnd ( ) ) ,
473+ ...exampleText . split ( '\n' ) . map ( ( l ) => `# ${ l . trimEnd ( ) } ` . trimEnd ( ) ) ,
439474 ]
440475 : [ ] ;
441476
@@ -692,10 +727,14 @@ export class RubyGenerator extends Generator {
692727 ( m ) => this . rubyConstName ( m . name ) ,
693728 typeSpec . fqn ,
694729 ) ;
695- this . emitDocs ( typeSpec ) ;
730+ this . emitDocs ( typeSpec , {
731+ apiLocation : { api : 'type' , fqn : typeSpec . fqn } ,
732+ } ) ;
696733 this . code . open ( `module ${ prefix } ${ this . rubyModuleName ( typeSpec . name ) } ` ) ;
697734 for ( const member of resolvedMembers ) {
698- this . emitDocs ( member ) ;
735+ this . emitDocs ( member , {
736+ apiLocation : { api : 'member' , fqn : typeSpec . fqn , memberName : member . name } ,
737+ } ) ;
699738 this . code . line (
700739 `${ this . rubyConstName ( member . name ) } = Jsii::Enum.new("${ rubyDq ( typeSpec . fqn ) } ", "${ rubyDq ( member . name ) } ")` ,
701740 ) ;
@@ -744,7 +783,9 @@ export class RubyGenerator extends Generator {
744783 ? ' < Jsii::Struct'
745784 : '' ;
746785
747- this . emitDocs ( typeSpec ) ;
786+ this . emitDocs ( typeSpec , {
787+ apiLocation : { api : 'type' , fqn : typeSpec . fqn } ,
788+ } ) ;
748789 this . code . open ( `${ kind } ${ prefix } ${ rubyName } ${ baseString } ` ) ;
749790
750791 if ( ! typeSpec . datatype ) {
@@ -796,6 +837,11 @@ export class RubyGenerator extends Generator {
796837 this . emitDocs ( prop , {
797838 propertyType : prop . type ,
798839 propertyOptional : prop . optional ,
840+ apiLocation : {
841+ api : 'member' ,
842+ fqn : typeSpec . fqn ,
843+ memberName : prop . name ,
844+ } ,
799845 } ) ;
800846 this . code . line ( `attr_reader :${ this . rubyName ( prop . name ) } ` ) ;
801847 }
@@ -832,6 +878,11 @@ export class RubyGenerator extends Generator {
832878 this . emitDocs ( prop , {
833879 propertyType : prop . type ,
834880 propertyOptional : prop . optional ,
881+ apiLocation : {
882+ api : 'member' ,
883+ fqn : typeSpec . fqn ,
884+ memberName : prop . name ,
885+ } ,
835886 } ) ;
836887 this . code . open ( `def ${ propRubyName } ()` ) ;
837888 this . code . line ( `jsii_get_property("${ rubyDq ( prop . name ) } ")` ) ;
@@ -868,6 +919,11 @@ export class RubyGenerator extends Generator {
868919 params : method . parameters ,
869920 returns : method . spec ?. returns ,
870921 isMethod : true ,
922+ apiLocation : {
923+ api : 'member' ,
924+ fqn : typeSpec . fqn ,
925+ memberName : method . name ,
926+ } ,
871927 } ) ;
872928 this . code . open ( `def ${ this . rubyName ( method . name ) } (${ sigParams } )` ) ;
873929 for ( const p of method . parameters ) {
@@ -951,7 +1007,9 @@ export class RubyGenerator extends Generator {
9511007 ( i ) => `::${ this . rubyFullTypeName ( i ) } ` ,
9521008 ) ;
9531009
954- this . emitDocs ( typeSpec ) ;
1010+ this . emitDocs ( typeSpec , {
1011+ apiLocation : { api : 'type' , fqn : typeSpec . fqn } ,
1012+ } ) ;
9551013 this . code . open ( `class ${ prefix } ${ rubyName } < ${ baseClass } ` ) ;
9561014
9571015 for ( const mixin of interfaceMixins ) {
@@ -977,7 +1035,10 @@ export class RubyGenerator extends Generator {
9771035 } )
9781036 . join ( ', ' ) ;
9791037
980- this . emitDocs ( initializer , { params : initializer . parameters } ) ;
1038+ this . emitDocs ( initializer , {
1039+ params : initializer . parameters ,
1040+ apiLocation : { api : 'initializer' , fqn : typeSpec . fqn } ,
1041+ } ) ;
9811042 this . code . open ( `def initialize(${ initParams } )` ) ;
9821043 for ( const p of initializer . parameters ) {
9831044 const rubyParam = this . rubyName ( p . name ) ;
@@ -1084,6 +1145,11 @@ export class RubyGenerator extends Generator {
10841145 params : method . parameters ,
10851146 returns : method . spec ?. returns ,
10861147 isMethod : true ,
1148+ apiLocation : {
1149+ api : 'member' ,
1150+ fqn : typeSpec . fqn ,
1151+ memberName : method . name ,
1152+ } ,
10871153 } ) ;
10881154 this . code . open ( `def self.${ this . rubyMethodName ( method ) } (${ sigParams } )` ) ;
10891155 for ( const p of method . parameters ) {
@@ -1112,6 +1178,11 @@ export class RubyGenerator extends Generator {
11121178 this . emitDocs ( prop , {
11131179 propertyType : prop . type ,
11141180 propertyOptional : prop . optional ,
1181+ apiLocation : {
1182+ api : 'member' ,
1183+ fqn : typeSpec . fqn ,
1184+ memberName : prop . name ,
1185+ } ,
11151186 } ) ;
11161187 this . code . open ( `def self.${ rubyName } ()` ) ;
11171188 this . code . line (
@@ -1136,6 +1207,11 @@ export class RubyGenerator extends Generator {
11361207 this . emitDocs ( prop , {
11371208 propertyType : prop . type ,
11381209 propertyOptional : prop . optional ,
1210+ apiLocation : {
1211+ api : 'member' ,
1212+ fqn : typeSpec . fqn ,
1213+ memberName : prop . name ,
1214+ } ,
11391215 } ) ;
11401216 this . code . open ( `def ${ rubyName } ()` ) ;
11411217 this . code . line ( `jsii_get_property("${ rubyDq ( prop . name ) } ")` ) ;
@@ -1178,6 +1254,11 @@ export class RubyGenerator extends Generator {
11781254 params : method . parameters ,
11791255 returns : method . spec ?. returns ,
11801256 isMethod : true ,
1257+ apiLocation : {
1258+ api : 'member' ,
1259+ fqn : typeSpec . fqn ,
1260+ memberName : method . name ,
1261+ } ,
11811262 } ) ;
11821263 this . code . open ( `def ${ this . rubyMethodName ( method ) } (${ sigParams } )` ) ;
11831264 for ( const p of method . parameters ) {
0 commit comments