@@ -31,7 +31,7 @@ exports.main = function(args, callback) {
3131 lint : "l"
3232 } ,
3333 string : [ "target" , "out" , "path" , "wrap" , "root" , "lint" ] ,
34- boolean : [ "keep-case" , "create" , "encode" , "decode" , "verify" , "convert" , "delimited" , "beautify" , "comments" , "es6" ] ,
34+ boolean : [ "keep-case" , "create" , "encode" , "decode" , "verify" , "convert" , "delimited" , "beautify" , "comments" , "es6" , "sparse" ] ,
3535 default : {
3636 target : "json" ,
3737 create : true ,
@@ -51,6 +51,9 @@ exports.main = function(args, callback) {
5151 files = argv . _ ,
5252 paths = typeof argv . path === "string" ? [ argv . path ] : argv . path || [ ] ;
5353
54+ // protobuf.js package directory contains additional, otherwise non-bundled google types
55+ paths . push ( path . relative ( process . cwd ( ) , path . join ( __dirname , ".." ) ) || "." ) ;
56+
5457 if ( ! files . length ) {
5558 var descs = Object . keys ( targets ) . filter ( function ( key ) { return ! targets [ key ] . private ; } ) . map ( function ( key ) {
5659 return " " + util . pad ( key , 14 , true ) + targets [ key ] . description ;
@@ -71,6 +74,8 @@ exports.main = function(args, callback) {
7174 "" ,
7275 " -o, --out Saves to a file instead of writing to stdout." ,
7376 "" ,
77+ " --sparse Exports only those types referenced from a main file (experimental)." ,
78+ "" ,
7479 chalk . bold . gray ( " Module targets only:" ) ,
7580 "" ,
7681 " -w, --wrap Specifies the wrapper to use. Also accepts a path to require a custom wrapper." ,
@@ -124,17 +129,33 @@ exports.main = function(args, callback) {
124129
125130 var root = new protobuf . Root ( ) ;
126131
132+ var mainFiles = [ ] ;
133+
127134 // Search include paths when resolving imports
128135 root . resolvePath = function pbjsResolvePath ( origin , target ) {
129- var filepath = protobuf . util . path . resolve ( origin , target ) ;
130- if ( fs . existsSync ( filepath ) )
131- return filepath ;
136+ var normOrigin = protobuf . util . path . normalize ( origin ) ,
137+ normTarget = protobuf . util . path . normalize ( target ) ;
138+ if ( ! normOrigin )
139+ mainFiles . push ( normTarget ) ;
140+
141+ var resolved = protobuf . util . path . resolve ( normOrigin , normTarget , true ) ;
142+ var idx = resolved . lastIndexOf ( "google/protobuf/" ) ;
143+ if ( idx > - 1 ) {
144+ var altname = resolved . substring ( idx ) ;
145+ if ( altname in protobuf . common )
146+ resolved = altname ;
147+ }
148+
149+ if ( fs . existsSync ( resolved ) )
150+ return resolved ;
151+
132152 for ( var i = 0 ; i < paths . length ; ++ i ) {
133- var ifilepath = protobuf . util . path . resolve ( paths [ i ] + "/" , target ) ;
134- if ( fs . existsSync ( ifilepath ) )
135- return ifilepath ;
153+ var iresolved = protobuf . util . path . resolve ( paths [ i ] + "/" , target ) ;
154+ if ( fs . existsSync ( iresolved ) )
155+ return iresolved ;
136156 }
137- return filepath ;
157+
158+ return resolved ;
138159 } ;
139160
140161 // Use es6 syntax if not explicitly specified on the command line and the es6 wrapper is used
@@ -153,19 +174,28 @@ exports.main = function(args, callback) {
153174 } ) ;
154175 process . stdin . on ( "end" , function ( ) {
155176 var source = Buffer . concat ( data ) . toString ( "utf8" ) ;
156- if ( source . charAt ( 0 ) !== "{" ) {
157- protobuf . parse ( source , root , parseOptions ) ;
158- } else {
159- var json = JSON . parse ( source ) ;
160- root . setOptions ( json . options ) . addJSON ( json ) ;
177+ try {
178+ if ( source . charAt ( 0 ) !== "{" ) {
179+ protobuf . parse . filename = "-" ;
180+ protobuf . parse ( source , root , parseOptions ) ;
181+ } else {
182+ var json = JSON . parse ( source ) ;
183+ root . setOptions ( json . options ) . addJSON ( json ) ;
184+ }
185+ callTarget ( ) ;
186+ } catch ( err ) {
187+ if ( callback )
188+ return callback ( err ) ;
189+ throw err ;
161190 }
162- callTarget ( ) ;
163191 } ) ;
164192
165193 // Load from disk
166194 } else {
167195 try {
168- root . loadSync ( files , parseOptions ) ; // sync is deterministic while async is not
196+ root . loadSync ( files , parseOptions ) . resolveAll ( ) ; // sync is deterministic while async is not
197+ if ( argv . sparse )
198+ sparsify ( root ) ;
169199 callTarget ( ) ;
170200 } catch ( err ) {
171201 if ( callback ) {
@@ -176,6 +206,62 @@ exports.main = function(args, callback) {
176206 }
177207 }
178208
209+ function markReferenced ( tobj ) {
210+ tobj . referenced = true ;
211+ // also mark a type's fields and oneofs
212+ if ( tobj . fieldsArray )
213+ tobj . fieldsArray . forEach ( function ( fobj ) {
214+ fobj . referenced = true ;
215+ } ) ;
216+ if ( tobj . oneofsArray )
217+ tobj . oneofsArray . forEach ( function ( oobj ) {
218+ oobj . referenced = true ;
219+ } ) ;
220+ // also mark an extension field's extended type, but not its (other) fields
221+ if ( tobj . extensionField )
222+ tobj . extensionField . parent . referenced = true ;
223+ }
224+
225+ function sparsify ( root ) {
226+
227+ // 1. mark directly or indirectly referenced objects
228+ util . traverse ( root , function ( obj ) {
229+ if ( ! obj . filename )
230+ return ;
231+ if ( mainFiles . indexOf ( obj . filename ) > - 1 )
232+ util . traverseResolved ( obj , markReferenced ) ;
233+ } ) ;
234+
235+ // 2. empty unreferenced objects
236+ util . traverse ( root , function ( obj ) {
237+ var parent = obj . parent ;
238+ if ( ! parent || obj . referenced ) // root or referenced
239+ return ;
240+ // remove unreferenced namespaces
241+ if ( obj instanceof protobuf . Namespace ) {
242+ var hasReferenced = false ;
243+ util . traverse ( obj , function ( iobj ) {
244+ if ( iobj . referenced )
245+ hasReferenced = true ;
246+ } ) ;
247+ if ( hasReferenced ) { // replace with plain namespace if a namespace subclass
248+ if ( obj instanceof protobuf . Type || obj instanceof protobuf . Service ) {
249+ var robj = new protobuf . Namespace ( obj . name , obj . options ) ;
250+ robj . nested = obj . nested ;
251+ parent . add ( robj ) ;
252+ }
253+ } else // remove completely if nothing inside is referenced
254+ parent . remove ( obj ) ;
255+
256+ // remove everything else unreferenced
257+ } else if ( ! ( obj instanceof protobuf . Namespace ) )
258+ parent . remove ( obj ) ;
259+ } ) ;
260+
261+ // 3. validate that everything is fine
262+ root . resolveAll ( ) ;
263+ }
264+
179265 function callTarget ( ) {
180266 target ( root , argv , function targetCallback ( err , output ) {
181267 if ( err ) {
0 commit comments