@@ -80,6 +80,100 @@ function normalizePath(filePath: string): string {
8080 return path . join ( ...filePath . split ( '/' ) ) ;
8181}
8282
83+ function getAllEnums ( dts : string ) : Set < string > {
84+ const result = new Set < string > ( ) ;
85+ const lines = dts . split ( '\n' ) ;
86+ const nestedIds = [ ] ;
87+ let currentEnum = undefined ;
88+ for ( const line of lines ) {
89+ const match = line . match (
90+ / ^ \s * (?: e x p o r t ) ? ( n a m e s p a c e | c l a s s | i n t e r f a c e | e n u m ) ( \w + ) .* { /
91+ ) ;
92+ if ( match ) {
93+ const [ , keyword , id ] = match ;
94+ nestedIds . push ( id ) ;
95+ if ( keyword === 'enum' ) {
96+ currentEnum = nestedIds . join ( '.' ) ;
97+ result . add ( currentEnum ) ;
98+ }
99+ continue ;
100+ }
101+ if ( line . match ( / ^ \s * } / ) ) {
102+ nestedIds . pop ( ) ;
103+ currentEnum = undefined ;
104+ continue ;
105+ }
106+ }
107+
108+ return result ;
109+ }
110+
111+ function updateDtsTypes ( dts : string , enums : Set < string > ) : string {
112+ const lines = dts . split ( '\n' ) ;
113+ const result : string [ ] = [ ] ;
114+
115+ for ( const line of lines ) {
116+ let typeName : string | undefined = undefined ;
117+ // Enums can be used in interfaces and in classes.
118+ // For simplicity, we'll check these two cases independently.
119+ // encoding?: (google.cloud.speech.v1p1beta1.RecognitionConfig.AudioEncoding|null);
120+ const interfaceMatch = line . match ( / \w + \? : \( ( [ \w . ] + ) \| n u l l \) ; / ) ;
121+ if ( interfaceMatch ) {
122+ typeName = interfaceMatch [ 1 ] ;
123+ }
124+ // public encoding: google.cloud.speech.v1p1beta1.RecognitionConfig.AudioEncoding;
125+ const classMatch = line . match ( / p u b l i c \w + : ( [ \w . ] + ) ; / ) ;
126+ if ( classMatch ) {
127+ typeName = classMatch [ 1 ] ;
128+ }
129+
130+ if ( line . match ( / \( n u m b e r \| L o n g (?: \| n u l l ) ? \) / ) ) {
131+ typeName = 'Long' ;
132+ }
133+
134+ let replaced = line ;
135+ if ( typeName && enums . has ( typeName ) ) {
136+ // enum: E => E|keyof typeof E to allow all string values
137+ replaced = replaced . replace (
138+ typeName ,
139+ `${ typeName } |keyof typeof ${ typeName } `
140+ ) ;
141+ } else if ( typeName === 'Uint8Array' ) {
142+ // bytes: Uint8Array => Uint8Array|string to allow base64-encoded strings
143+ replaced = replaced . replace ( typeName , `${ typeName } |string` ) ;
144+ } else if ( typeName === 'Long' ) {
145+ // Longs can be passed as strings :(
146+ // number|Long => number|Long|string
147+ replaced = replaced . replace ( 'number|Long' , 'number|Long|string' ) ;
148+ }
149+
150+ // add brackets if we have added a |
151+ replaced = replaced . replace ( / : ( [ \w . ] + \| [ \w . | ] + ) ; / , ': ($1);' ) ;
152+
153+ result . push ( replaced ) ;
154+ }
155+
156+ return result . join ( '\n' ) ;
157+ }
158+
159+ function fixDtsFile ( dts : string ) : string {
160+ // 1. fix for pbts output: the corresponding protobufjs PR
161+ // https://github.com/protobufjs/protobuf.js/pull/1166
162+ // is merged but not yet released.
163+ if ( ! dts . match ( / i m p o r t \* a s L o n g / ) ) {
164+ dts = 'import * as Long from "long "; \n' + dts ;
165+ }
166+
167+ // 2. add Apache license to the generated .d.ts file
168+ dts = apacheLicense + dts ;
169+
170+ // 3. major hack: update types to allow passing strings
171+ // where enums, longs, or bytes are expected
172+ const enums = getAllEnums ( dts ) ;
173+ dts = updateDtsTypes ( dts , enums ) ;
174+ return dts ;
175+ }
176+
83177/**
84178 * Returns a combined list of proto files listed in all JSON files given.
85179 *
@@ -153,14 +247,7 @@ async function compileProtos(protos: string[]): Promise<void> {
153247 await pbtsMain ( pbjsArgs4ts ) ;
154248
155249 let tsResult = ( await readFile ( tsOutput ) ) . toString ( ) ;
156- // fix for pbts output: the corresponding protobufjs PR
157- // https://github.com/protobufjs/protobuf.js/pull/1166
158- // is merged but not yet released.
159- if ( ! tsResult . match ( / i m p o r t \* a s L o n g / ) ) {
160- tsResult = 'import * as Long from "long";\n' + tsResult ;
161- }
162- // add Apache license to the generated .d.ts file
163- tsResult = apacheLicense + tsResult ;
250+ tsResult = fixDtsFile ( tsResult ) ;
164251 await writeFile ( tsOutput , tsResult ) ;
165252}
166253
0 commit comments