@@ -9,7 +9,13 @@ export function compile(proto) {
99
1010export function compileRaw ( proto , options = { } ) {
1111 const context = buildDefaults ( buildContext ( proto , null ) , proto . syntax ) ;
12- return `${ options . dev ? '' : `// code generated by pbf v${ version } \n` } ${ writeContext ( context , options ) } ` ;
12+
13+ let output = options . dev ? '' : `// code generated by pbf v${ version } \n` ;
14+ if ( options . jsDoc ) {
15+ output += typeDef ( `import("${ options . dev ? '../../index.js' : 'pbf' } ").default` , 'Pbf' ) ;
16+ }
17+ output += writeContext ( context , options ) ;
18+ return output ;
1319}
1420
1521function writeContext ( ctx , options ) {
@@ -24,17 +30,35 @@ function writeContext(ctx, options) {
2430}
2531
2632function writeMessage ( ctx , options ) {
33+ const name = ctx . _name ;
2734 const fields = ctx . _proto . fields ;
2835
2936 let code = '\n' ;
3037
38+ if ( options . jsDoc ) {
39+ code += compileType ( ctx , name , fields ) ;
40+ }
41+
3142 if ( ! options . noRead ) {
32- const readName = `read${ ctx . _name } ` ;
43+ const readName = `read${ name } ` ;
44+ if ( options . jsDoc ) {
45+ code += [ '\n/**' , ' * @param {Pbf} pbf' , ' * @param {number} [end]' , ` * @returns {${ name } }` , ' */' ] . join ( '\n' ) . concat ( '\n' ) ;
46+ }
3347 code +=
3448`${ writeFunctionExport ( options , readName ) } function ${ readName } (pbf, end) {
3549 return pbf.readFields(${ readName } Field, ${ compileDest ( ctx ) } , end);
36- }
37- function ${ readName } Field(tag, obj, pbf) {
50+ }\n` ;
51+
52+ if ( options . jsDoc ) {
53+ let param = name ;
54+ if ( ctx . _proto . map ) {
55+ const { key, value} = getMapTsType ( fields ) ;
56+ param = `{key: ${ key } ; value: ${ value } }` ;
57+ }
58+ code += [ '\n/**' , ' * @param {number} tag' , ` * @param {${ param } } obj` , ' * @param {Pbf} pbf' , ' */' ] . join ( '\n' ) . concat ( '\n' ) ;
59+ }
60+ code +=
61+ `function ${ readName } Field(tag, obj, pbf) {
3862` ;
3963 for ( let i = 0 ; i < fields . length ; i ++ ) {
4064 const field = fields [ i ] ;
@@ -60,7 +84,18 @@ function ${readName}Field(tag, obj, pbf) {
6084 }
6185
6286 if ( ! options . noWrite ) {
63- const writeName = `write${ ctx . _name } ` ;
87+ const writeName = `write${ name } ` ;
88+
89+ if ( options . jsDoc ) {
90+ let param = name ;
91+ if ( ctx . _proto . map ) {
92+ const { key, value} = getMapTsType ( fields ) ;
93+ param = `{key: ${ key } ; value: ${ value } }` ;
94+ }
95+
96+ code += [ '\n/**' , ` * @param {${ param } } obj` , ' * @param {Pbf} pbf' , ' */' ] . join ( '\n' ) . concat ( '\n' ) ;
97+ }
98+
6499 code += `${ writeFunctionExport ( options , writeName ) } function ${ writeName } (obj, pbf) {\n` ;
65100 for ( const field of fields ) {
66101 const writeCode =
@@ -87,10 +122,17 @@ function getEnumValues(ctx) {
87122 return enums ;
88123}
89124
90- function writeEnum ( ctx , { legacy } ) {
125+ function writeEnum ( ctx , options ) {
91126 const enums = JSON . stringify ( getEnumValues ( ctx ) , null , 4 ) ;
92127 const name = ctx . _name ;
93- return `\n${ legacy ? `const ${ name } = exports.${ name } ` : `export const ${ name } ` } = ${ enums } ;\n` ;
128+
129+ let code = '\n' ;
130+ if ( options . jsDoc ) {
131+ code = '\n/** @enum {number} */\n' ;
132+ }
133+
134+ code += `${ options . legacy ? `const ${ name } = exports.${ name } ` : `export const ${ name } ` } = ${ enums } ;\n` ;
135+ return code ;
94136}
95137
96138function compileDest ( ctx ) {
@@ -114,23 +156,27 @@ function getType(ctx, field) {
114156 return path . reduce ( ( ctx , name ) => ctx && ctx [ name ] , ctx ) ;
115157}
116158
159+ function fieldTypeIsNumber ( field ) {
160+ switch ( field . type ) {
161+ case 'float' :
162+ case 'double' :
163+ case 'uint32' :
164+ case 'uint64' :
165+ case 'int32' :
166+ case 'int64' :
167+ case 'sint32' :
168+ case 'sint64' :
169+ case 'fixed32' :
170+ case 'fixed64' :
171+ case 'sfixed32' :
172+ case 'sfixed64' : return true ;
173+ default : return false ;
174+ }
175+ }
176+
117177function fieldShouldUseStringAsNumber ( field ) {
118178 if ( field . options . jstype === 'JS_STRING' ) {
119- switch ( field . type ) {
120- case 'float' :
121- case 'double' :
122- case 'uint32' :
123- case 'uint64' :
124- case 'int32' :
125- case 'int64' :
126- case 'sint32' :
127- case 'sint64' :
128- case 'fixed32' :
129- case 'fixed64' :
130- case 'sfixed32' :
131- case 'sfixed64' : return true ;
132- default : return false ;
133- }
179+ return fieldTypeIsNumber ( field ) ;
134180 }
135181 return false ;
136182}
@@ -256,6 +302,7 @@ function getMapMessage(field) {
256302 return {
257303 name : getMapMessageName ( field . tag ) ,
258304 enums : [ ] ,
305+ map : true ,
259306 messages : [ ] ,
260307 extensions : null ,
261308 fields : [
@@ -415,3 +462,72 @@ function getDefaultWriteTest(ctx, field) {
415462function isPacked ( field ) {
416463 return field . options . packed === 'true' ;
417464}
465+
466+ function getTsType ( field ) {
467+ let type = field . type ;
468+
469+ if ( fieldShouldUseStringAsNumber ( field ) ) type = 'string' ;
470+ else if ( fieldTypeIsNumber ( field ) ) type = 'number' ;
471+ else if ( field . type === 'bytes' ) type = 'Uint8Array' ;
472+ else if ( field . type === 'bool' ) type = 'boolean' ;
473+
474+ return field . repeated ? `Array<${ type } >` : type ;
475+ }
476+
477+ function getMapTsType ( fields ) {
478+ const key = getTsType ( fields [ 0 ] ) ;
479+ const value = getTsType ( fields [ 1 ] ) ;
480+ return { key, value} ;
481+ }
482+
483+ /**
484+ * @param {string } type
485+ * @param {string } name
486+ * @param {{name: string; type: string; required: boolean} } [fields]
487+ * @returns {string }
488+ */
489+ function typeDef ( type , name , fields = [ ] ) {
490+ const unionProperties = { } ;
491+ const properties = fields . map ( ( field ) => {
492+ if ( field . oneof ) {
493+ unionProperties [ field . oneof ] = unionProperties [ field . oneof ] || [ ] ;
494+ unionProperties [ field . oneof ] . push ( field . name ) ;
495+ }
496+
497+ const type = getTsType ( field ) ;
498+ const isRequired = field . required || field . repeated || field . map ;
499+
500+ const name = isRequired ? field . name : `[${ field . name } ]` ;
501+
502+ return ` * @property {${ type } } ${ name } ` ;
503+ } ) ;
504+
505+ for ( const prop in unionProperties ) {
506+ const union = unionProperties [ prop ] . map ( s => `"${ s } "` ) . join ( ' | ' ) ;
507+ properties . push ( ` * @property {${ union } } [${ prop } ]` ) ;
508+ }
509+
510+ return [ '/**' , ` * @typedef {${ type } } ${ name } ` , ...properties , ' */' ]
511+ . join ( '\n' )
512+ . concat ( '\n' ) ;
513+ }
514+
515+ /**
516+ * @param {object } ctx
517+ * @param {string } name
518+ * @param {{name: string; type: string; required: boolean} } [fields]
519+ * @returns {string }
520+ */
521+ function compileType ( ctx , name , fields = [ ] ) {
522+ if ( ctx . _proto . map ) {
523+ const { key, value} = getMapTsType ( fields ) ;
524+ return typeDef ( `Object<${ key } , ${ value } >` , name , [ ] ) ;
525+ }
526+
527+ const typedFields = fields . map ( ( field ) => {
528+ const type = getType ( ctx , field ) ;
529+ return { ...field , type : type ? type . _name : field . type } ;
530+ } ) ;
531+
532+ return typeDef ( 'object' , name , typedFields ) ;
533+ }
0 commit comments