Built motion from commit 6a09e18b.|2.6.11
[motion2.git] / legacy-libs / protobufjs / cli / targets / static.js
1 "use strict";
2 module.exports = static_target;
3
4 var protobuf   = require("../.."),
5     UglifyJS   = require("uglify-js"),
6     espree     = require("espree"),
7     escodegen  = require("escodegen"),
8     estraverse = require("estraverse");
9
10 var Type      = protobuf.Type,
11     Service   = protobuf.Service,
12     Enum      = protobuf.Enum,
13     Namespace = protobuf.Namespace,
14     util      = protobuf.util;
15
16 var out = [];
17 var indent = 0;
18 var config = {};
19
20 static_target.description = "Static code without reflection (non-functional on its own)";
21
22 function static_target(root, options, callback) {
23     config = options;
24     try {
25         var aliases = [];
26         if (config.decode)
27             aliases.push("Reader");
28         if (config.encode)
29             aliases.push("Writer");
30         aliases.push("util");
31         if (aliases.length) {
32             if (config.comments)
33                 push("// Common aliases");
34             push((config.es6 ? "const " : "var ") + aliases.map(function(name) { return "$" + name + " = $protobuf." + name; }).join(", ") + ";");
35             push("");
36         }
37         if (config.comments) {
38             if (root.comment) {
39                 pushComment("@fileoverview " + root.comment);
40                 push("");
41             }
42             push("// Exported root namespace");
43         }
44         var rootProp = util.safeProp(config.root || "default");
45         push((config.es6 ? "const" : "var") + " $root = $protobuf.roots" + rootProp + " || ($protobuf.roots" + rootProp + " = {});");
46         buildNamespace(null, root);
47         return callback(null, out.join("\n"));
48     } catch (err) {
49         return callback(err);
50     } finally {
51         out = [];
52         indent = 0;
53         config = {};
54     }
55 }
56
57 function push(line) {
58     if (line === "")
59         return out.push("");
60     var ind = "";
61     for (var i = 0; i < indent; ++i)
62         ind += "    ";
63     return out.push(ind + line);
64 }
65
66 function pushComment(lines) {
67     if (!config.comments)
68         return;
69     var split = [];
70     for (var i = 0; i < lines.length; ++i)
71         if (lines[i] != null && lines[i].substring(0, 8) !== "@exclude")
72             Array.prototype.push.apply(split, lines[i].split(/\r?\n/g));
73     push("/**");
74     split.forEach(function(line) {
75         if (line === null)
76             return;
77         push(" * " + line.replace(/\*\//g, "* /"));
78     });
79     push(" */");
80 }
81
82 function exportName(object, asInterface) {
83     if (asInterface) {
84         if (object.__interfaceName)
85             return object.__interfaceName;
86     } else if (object.__exportName)
87         return object.__exportName;
88     var parts = object.fullName.substring(1).split("."),
89         i = 0;
90     while (i < parts.length)
91         parts[i] = escapeName(parts[i++]);
92     if (asInterface)
93         parts[i - 1] = "I" + parts[i - 1];
94     return object[asInterface ? "__interfaceName" : "__exportName"] = parts.join(".");
95 }
96
97 function escapeName(name) {
98     if (!name)
99         return "$root";
100     return util.isReserved(name) ? name + "_" : name;
101 }
102
103 function aOrAn(name) {
104     return ((/^[hH](?:ou|on|ei)/.test(name) || /^[aeiouAEIOU][a-z]/.test(name)) && !/^us/i.test(name)
105         ? "an "
106         : "a ") + name;
107 }
108
109 function buildNamespace(ref, ns) {
110     if (!ns)
111         return;
112     if (ns.name !== "") {
113         push("");
114         if (!ref && config.es6)
115             push("export const " + escapeName(ns.name) + " = " + escapeName(ref) + "." + escapeName(ns.name) + " = (() => {");
116         else
117             push(escapeName(ref) + "." + escapeName(ns.name) + " = (function() {");
118         ++indent;
119     }
120
121     if (ns instanceof Type) {
122         buildType(undefined, ns);
123     } else if (ns instanceof Service)
124         buildService(undefined, ns);
125     else if (ns.name !== "") {
126         push("");
127         pushComment([
128             ns.comment || "Namespace " + ns.name + ".",
129             ns.parent instanceof protobuf.Root ? "@exports " + escapeName(ns.name) : "@memberof " + exportName(ns.parent),
130             "@namespace"
131         ]);
132         push((config.es6 ? "const" : "var") + " " + escapeName(ns.name) + " = {};");
133     }
134
135     ns.nestedArray.forEach(function(nested) {
136         if (nested instanceof Enum)
137             buildEnum(ns.name, nested);
138         else if (nested instanceof Namespace)
139             buildNamespace(ns.name, nested);
140     });
141     if (ns.name !== "") {
142         push("");
143         push("return " + escapeName(ns.name) + ";");
144         --indent;
145         push("})();");
146     }
147 }
148
149 var reduceableBlockStatements = {
150     IfStatement: true,
151     ForStatement: true,
152     WhileStatement: true
153 };
154
155 var shortVars = {
156     "r": "reader",
157     "w": "writer",
158     "m": "message",
159     "t": "tag",
160     "l": "length",
161     "c": "end", "c2": "end2",
162     "k": "key",
163     "ks": "keys", "ks2": "keys2",
164     "e": "error",
165     "f": "impl",
166     "o": "options",
167     "d": "object",
168     "n": "long",
169     "p": "properties"
170 };
171
172 function beautifyCode(code) {
173     // Add semicolons
174     code = UglifyJS.minify(code, {
175         compress: false,
176         mangle: false,
177         output: { beautify: true }
178     }).code;
179     // Properly beautify
180     var ast = espree.parse(code);
181     estraverse.replace(ast, {
182         enter: function(node, parent) {
183             // rename short vars
184             if (node.type === "Identifier" && (parent.property !== node || parent.computed) && shortVars[node.name])
185                 return {
186                     "type": "Identifier",
187                     "name": shortVars[node.name]
188                 };
189             // replace var with let if es6
190             if (config.es6 && node.type === "VariableDeclaration" && node.kind === "var") {
191                 node.kind = "let";
192                 return undefined;
193             }
194             // remove braces around block statements with a single child
195             if (node.type === "BlockStatement" && reduceableBlockStatements[parent.type] && node.body.length === 1)
196                 return node.body[0];
197             return undefined;
198         }
199     });
200     code = escodegen.generate(ast, {
201         format: {
202             newline: "\n",
203             quotes: "double"
204         }
205     });
206     // Add id, wireType comments
207     if (config.comments)
208         code = code.replace(/\.uint32\((\d+)\)/g, function($0, $1) {
209             var id = $1 >>> 3,
210                 wireType = $1 & 7;
211             return ".uint32(/* id " + id + ", wireType " + wireType + " =*/" + $1 + ")";
212         });
213     return code;
214 }
215
216 var renameVars = {
217     "Writer": "$Writer",
218     "Reader": "$Reader",
219     "util": "$util"
220 };
221
222 function buildFunction(type, functionName, gen, scope) {
223     var code = gen.toString(functionName)
224         .replace(/((?!\.)types\[\d+])(\.values)/g, "$1"); // enums: use types[N] instead of reflected types[N].values
225
226     var ast = espree.parse(code);
227     /* eslint-disable no-extra-parens */
228     estraverse.replace(ast, {
229         enter: function(node, parent) {
230             // rename vars
231             if (
232                 node.type === "Identifier" && renameVars[node.name]
233                 && (
234                     (parent.type === "MemberExpression" && parent.object === node)
235                  || (parent.type === "BinaryExpression" && parent.right === node)
236                 )
237             )
238                 return {
239                     "type": "Identifier",
240                     "name": renameVars[node.name]
241                 };
242             // replace this.ctor with the actual ctor
243             if (
244                 node.type === "MemberExpression"
245              && node.object.type === "ThisExpression"
246              && node.property.type === "Identifier" && node.property.name === "ctor"
247             )
248                 return {
249                     "type": "Identifier",
250                     "name": "$root" + type.fullName
251                 };
252             // replace types[N] with the field's actual type
253             if (
254                 node.type === "MemberExpression"
255              && node.object.type === "Identifier" && node.object.name === "types"
256              && node.property.type === "Literal"
257             )
258                 return {
259                     "type": "Identifier",
260                     "name": "$root" + type.fieldsArray[node.property.value].resolvedType.fullName
261                 };
262             return undefined;
263         }
264     });
265     /* eslint-enable no-extra-parens */
266     code = escodegen.generate(ast, {
267         format: {
268             newline: "\n",
269             quotes: "double"
270         }
271     });
272
273     if (config.beautify)
274         code = beautifyCode(code);
275
276     code = code.replace(/ {4}/g, "\t");
277
278     var hasScope = scope && Object.keys(scope).length,
279         isCtor = functionName === type.name;
280
281     if (hasScope) // remove unused scope vars
282         Object.keys(scope).forEach(function(key) {
283             if (!new RegExp("\\b(" + key + ")\\b", "g").test(code))
284                 delete scope[key];
285         });
286
287     var lines = code.split(/\n/g);
288     if (isCtor) // constructor
289         push(lines[0]);
290     else if (hasScope) // enclose in an iife
291         push(escapeName(type.name) + "." + escapeName(functionName) + " = (function(" + Object.keys(scope).map(escapeName).join(", ") + ") { return " + lines[0]);
292     else
293         push(escapeName(type.name) + "." + escapeName(functionName) + " = " + lines[0]);
294     lines.slice(1, lines.length - 1).forEach(function(line) {
295         var prev = indent;
296         var i = 0;
297         while (line.charAt(i++) === "\t")
298             ++indent;
299         push(line.trim());
300         indent = prev;
301     });
302     if (isCtor)
303         push("}");
304     else if (hasScope)
305         push("};})(" + Object.keys(scope).map(function(key) { return scope[key]; }).join(", ") + ");");
306     else
307         push("};");
308 }
309
310 function toJsType(field) {
311     var type;
312
313     switch (field.type) {
314         case "double":
315         case "float":
316         case "int32":
317         case "uint32":
318         case "sint32":
319         case "fixed32":
320         case "sfixed32":
321             type = "number";
322             break;
323         case "int64":
324         case "uint64":
325         case "sint64":
326         case "fixed64":
327         case "sfixed64":
328             type = config.forceLong ? "Long" : config.forceNumber ? "number" : "number|Long";
329             break;
330         case "bool":
331             type = "boolean";
332             break;
333         case "string":
334             type = "string";
335             break;
336         case "bytes":
337             type = "Uint8Array";
338             break;
339         default:
340             if (field.resolve().resolvedType)
341                 type = exportName(field.resolvedType, !(field.resolvedType instanceof protobuf.Enum || config.forceMessage));
342             else
343                 type = "*"; // should not happen
344             break;
345     }
346     if (field.map)
347         return "Object.<string," + type + ">";
348     if (field.repeated)
349         return "Array.<" + type + ">";
350     return type;
351 }
352
353 function buildType(ref, type) {
354
355     if (config.comments) {
356         var typeDef = [
357             "Properties of " + aOrAn(type.name) + ".",
358             type.parent instanceof protobuf.Root ? "@exports " + escapeName("I" + type.name) : "@memberof " + exportName(type.parent),
359             "@interface " + escapeName("I" + type.name)
360         ];
361         type.fieldsArray.forEach(function(field) {
362             var prop = util.safeProp(field.name); // either .name or ["name"]
363             prop = prop.substring(1, prop.charAt(0) === "[" ? prop.length - 1 : prop.length);
364             var jsType = toJsType(field);
365             if (field.optional)
366                 jsType = jsType + "|null";
367             typeDef.push("@property {" + jsType + "} " + (field.optional ? "[" + prop + "]" : prop) + " " + (field.comment || type.name + " " + field.name));
368         });
369         push("");
370         pushComment(typeDef);
371     }
372
373     // constructor
374     push("");
375     pushComment([
376         "Constructs a new " + type.name + ".",
377         type.parent instanceof protobuf.Root ? "@exports " + escapeName(type.name) : "@memberof " + exportName(type.parent),
378         "@classdesc " + (type.comment || "Represents " + aOrAn(type.name) + "."),
379         config.comments ? "@implements " + escapeName("I" + type.name) : null,
380         "@constructor",
381         "@param {" + exportName(type, true) + "=} [" + (config.beautify ? "properties" : "p") + "] Properties to set"
382     ]);
383     buildFunction(type, type.name, Type.generateConstructor(type));
384
385     // default values
386     var firstField = true;
387     type.fieldsArray.forEach(function(field) {
388         field.resolve();
389         var prop = util.safeProp(field.name);
390         if (config.comments) {
391             push("");
392             var jsType = toJsType(field);
393             if (field.optional && !field.map && !field.repeated && field.resolvedType instanceof Type)
394                 jsType = jsType + "|null|undefined";
395             pushComment([
396                 field.comment || type.name + " " + field.name + ".",
397                 "@member {" + jsType + "} " + field.name,
398                 "@memberof " + exportName(type),
399                 "@instance"
400             ]);
401         } else if (firstField) {
402             push("");
403             firstField = false;
404         }
405         if (field.repeated)
406             push(escapeName(type.name) + ".prototype" + prop + " = $util.emptyArray;"); // overwritten in constructor
407         else if (field.map)
408             push(escapeName(type.name) + ".prototype" + prop + " = $util.emptyObject;"); // overwritten in constructor
409         else if (field.long)
410             push(escapeName(type.name) + ".prototype" + prop + " = $util.Long ? $util.Long.fromBits("
411                     + JSON.stringify(field.typeDefault.low) + ","
412                     + JSON.stringify(field.typeDefault.high) + ","
413                     + JSON.stringify(field.typeDefault.unsigned)
414                 + ") : " + field.typeDefault.toNumber(field.type.charAt(0) === "u") + ";");
415         else if (field.bytes) {
416             push(escapeName(type.name) + ".prototype" + prop + " = $util.newBuffer(" + JSON.stringify(Array.prototype.slice.call(field.typeDefault)) + ");");
417         } else
418             push(escapeName(type.name) + ".prototype" + prop + " = " + JSON.stringify(field.typeDefault) + ";");
419     });
420
421     // virtual oneof fields
422     var firstOneOf = true;
423     type.oneofsArray.forEach(function(oneof) {
424         if (firstOneOf) {
425             firstOneOf = false;
426             push("");
427             if (config.comments)
428                 push("// OneOf field names bound to virtual getters and setters");
429             push((config.es6 ? "let" : "var") + " $oneOfFields;");
430         }
431         oneof.resolve();
432         push("");
433         pushComment([
434             oneof.comment || type.name + " " + oneof.name + ".",
435             "@member {" + oneof.oneof.map(JSON.stringify).join("|") + "|undefined} " + escapeName(oneof.name),
436             "@memberof " + exportName(type),
437             "@instance"
438         ]);
439         push("Object.defineProperty(" + escapeName(type.name) + ".prototype, " + JSON.stringify(oneof.name) +", {");
440         ++indent;
441             push("get: $util.oneOfGetter($oneOfFields = [" + oneof.oneof.map(JSON.stringify).join(", ") + "]),");
442             push("set: $util.oneOfSetter($oneOfFields)");
443         --indent;
444         push("});");
445     });
446
447     if (config.create) {
448         push("");
449         pushComment([
450             "Creates a new " + type.name + " instance using the specified properties.",
451             "@function create",
452             "@memberof " + exportName(type),
453             "@static",
454             "@param {" + exportName(type, true) + "=} [properties] Properties to set",
455             "@returns {" + exportName(type) + "} " + type.name + " instance"
456         ]);
457         push(escapeName(type.name) + ".create = function create(properties) {");
458             ++indent;
459             push("return new " + escapeName(type.name) + "(properties);");
460             --indent;
461         push("};");
462     }
463
464     if (config.encode) {
465         push("");
466         pushComment([
467             "Encodes the specified " + type.name + " message. Does not implicitly {@link " + exportName(type) + ".verify|verify} messages.",
468             "@function encode",
469             "@memberof " + exportName(type),
470             "@static",
471             "@param {" + exportName(type, !config.forceMessage) + "} " + (config.beautify ? "message" : "m") + " " + type.name + " message or plain object to encode",
472             "@param {$protobuf.Writer} [" + (config.beautify ? "writer" : "w") + "] Writer to encode to",
473             "@returns {$protobuf.Writer} Writer"
474         ]);
475         buildFunction(type, "encode", protobuf.encoder(type));
476
477         if (config.delimited) {
478             push("");
479             pushComment([
480                 "Encodes the specified " + type.name + " message, length delimited. Does not implicitly {@link " + exportName(type) + ".verify|verify} messages.",
481                 "@function encodeDelimited",
482                 "@memberof " + exportName(type),
483                 "@static",
484                 "@param {" + exportName(type, !config.forceMessage) + "} message " + type.name + " message or plain object to encode",
485                 "@param {$protobuf.Writer} [writer] Writer to encode to",
486                 "@returns {$protobuf.Writer} Writer"
487             ]);
488             push(escapeName(type.name) + ".encodeDelimited = function encodeDelimited(message, writer) {");
489             ++indent;
490             push("return this.encode(message, writer).ldelim();");
491             --indent;
492             push("};");
493         }
494     }
495
496     if (config.decode) {
497         push("");
498         pushComment([
499             "Decodes " + aOrAn(type.name) + " message from the specified reader or buffer.",
500             "@function decode",
501             "@memberof " + exportName(type),
502             "@static",
503             "@param {$protobuf.Reader|Uint8Array} " + (config.beautify ? "reader" : "r") + " Reader or buffer to decode from",
504             "@param {number} [" + (config.beautify ? "length" : "l") + "] Message length if known beforehand",
505             "@returns {" + exportName(type) + "} " + type.name,
506             "@throws {Error} If the payload is not a reader or valid buffer",
507             "@throws {$protobuf.util.ProtocolError} If required fields are missing"
508         ]);
509         buildFunction(type, "decode", protobuf.decoder(type));
510
511         if (config.delimited) {
512             push("");
513             pushComment([
514                 "Decodes " + aOrAn(type.name) + " message from the specified reader or buffer, length delimited.",
515                 "@function decodeDelimited",
516                 "@memberof " + exportName(type),
517                 "@static",
518                 "@param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from",
519                 "@returns {" + exportName(type) + "} " + type.name,
520                 "@throws {Error} If the payload is not a reader or valid buffer",
521                 "@throws {$protobuf.util.ProtocolError} If required fields are missing"
522             ]);
523             push(escapeName(type.name) + ".decodeDelimited = function decodeDelimited(reader) {");
524             ++indent;
525                 push("if (!(reader instanceof $Reader))");
526                 ++indent;
527                     push("reader = new $Reader(reader);");
528                 --indent;
529                 push("return this.decode(reader, reader.uint32());");
530             --indent;
531             push("};");
532         }
533     }
534
535     if (config.verify) {
536         push("");
537         pushComment([
538             "Verifies " + aOrAn(type.name) + " message.",
539             "@function verify",
540             "@memberof " + exportName(type),
541             "@static",
542             "@param {Object.<string,*>} " + (config.beautify ? "message" : "m") + " Plain object to verify",
543             "@returns {string|null} `null` if valid, otherwise the reason why it is not"
544         ]);
545         buildFunction(type, "verify", protobuf.verifier(type));
546     }
547
548     if (config.convert) {
549         push("");
550         pushComment([
551             "Creates " + aOrAn(type.name) + " message from a plain object. Also converts values to their respective internal types.",
552             "@function fromObject",
553             "@memberof " + exportName(type),
554             "@static",
555             "@param {Object.<string,*>} " + (config.beautify ? "object" : "d") + " Plain object",
556             "@returns {" + exportName(type) + "} " + type.name
557         ]);
558         buildFunction(type, "fromObject", protobuf.converter.fromObject(type));
559
560         push("");
561         pushComment([
562             "Creates a plain object from " + aOrAn(type.name) + " message. Also converts values to other types if specified.",
563             "@function toObject",
564             "@memberof " + exportName(type),
565             "@static",
566             "@param {" + exportName(type) + "} " + (config.beautify ? "message" : "m") + " " + type.name,
567             "@param {$protobuf.IConversionOptions} [" + (config.beautify ? "options" : "o") + "] Conversion options",
568             "@returns {Object.<string,*>} Plain object"
569         ]);
570         buildFunction(type, "toObject", protobuf.converter.toObject(type));
571
572         push("");
573         pushComment([
574             "Converts this " + type.name + " to JSON.",
575             "@function toJSON",
576             "@memberof " + exportName(type),
577             "@instance",
578             "@returns {Object.<string,*>} JSON object"
579         ]);
580         push(escapeName(type.name) + ".prototype.toJSON = function toJSON() {");
581         ++indent;
582             push("return this.constructor.toObject(this, $protobuf.util.toJSONOptions);");
583         --indent;
584         push("};");
585     }
586 }
587
588 function buildService(ref, service) {
589
590     push("");
591     pushComment([
592         "Constructs a new " + service.name + " service.",
593         service.parent instanceof protobuf.Root ? "@exports " + escapeName(service.name) : "@memberof " + exportName(service.parent),
594         "@classdesc " + (service.comment || "Represents " + aOrAn(service.name)),
595         "@extends $protobuf.rpc.Service",
596         "@constructor",
597         "@param {$protobuf.RPCImpl} rpcImpl RPC implementation",
598         "@param {boolean} [requestDelimited=false] Whether requests are length-delimited",
599         "@param {boolean} [responseDelimited=false] Whether responses are length-delimited"
600     ]);
601     push("function " + escapeName(service.name) + "(rpcImpl, requestDelimited, responseDelimited) {");
602     ++indent;
603     push("$protobuf.rpc.Service.call(this, rpcImpl, requestDelimited, responseDelimited);");
604     --indent;
605     push("}");
606     push("");
607     push("(" + escapeName(service.name) + ".prototype = Object.create($protobuf.rpc.Service.prototype)).constructor = " + escapeName(service.name) + ";");
608
609     if (config.create) {
610         push("");
611         pushComment([
612             "Creates new " + service.name + " service using the specified rpc implementation.",
613             "@function create",
614             "@memberof " + exportName(service),
615             "@static",
616             "@param {$protobuf.RPCImpl} rpcImpl RPC implementation",
617             "@param {boolean} [requestDelimited=false] Whether requests are length-delimited",
618             "@param {boolean} [responseDelimited=false] Whether responses are length-delimited",
619             "@returns {" + escapeName(service.name) + "} RPC service. Useful where requests and/or responses are streamed."
620         ]);
621         push(escapeName(service.name) + ".create = function create(rpcImpl, requestDelimited, responseDelimited) {");
622             ++indent;
623             push("return new this(rpcImpl, requestDelimited, responseDelimited);");
624             --indent;
625         push("};");
626     }
627
628     service.methodsArray.forEach(function(method) {
629         method.resolve();
630         var lcName = protobuf.util.lcFirst(method.name),
631             cbName = escapeName(method.name + "Callback");
632         push("");
633         pushComment([
634             "Callback as used by {@link " + exportName(service) + "#" + escapeName(lcName) + "}.",
635             // This is a more specialized version of protobuf.rpc.ServiceCallback
636             "@memberof " + exportName(service),
637             "@typedef " + cbName,
638             "@type {function}",
639             "@param {Error|null} error Error, if any",
640             "@param {" + exportName(method.resolvedResponseType) + "} [response] " + method.resolvedResponseType.name
641         ]);
642         push("");
643         pushComment([
644             method.comment || "Calls " + method.name + ".",
645             "@function " + lcName,
646             "@memberof " + exportName(service),
647             "@instance",
648             "@param {" + exportName(method.resolvedRequestType, !config.forceMessage) + "} request " + method.resolvedRequestType.name + " message or plain object",
649             "@param {" + exportName(service) + "." + cbName + "} callback Node-style callback called with the error, if any, and " + method.resolvedResponseType.name,
650             "@returns {undefined}",
651             "@variation 1"
652         ]);
653         push("Object.defineProperty(" + escapeName(service.name) + ".prototype" + util.safeProp(lcName) + " = function " + escapeName(lcName) + "(request, callback) {");
654             ++indent;
655             push("return this.rpcCall(" + escapeName(lcName) + ", $root." + exportName(method.resolvedRequestType) + ", $root." + exportName(method.resolvedResponseType) + ", request, callback);");
656             --indent;
657         push("}, \"name\", { value: " + JSON.stringify(method.name) + " });");
658         if (config.comments)
659             push("");
660         pushComment([
661             method.comment || "Calls " + method.name + ".",
662             "@function " + lcName,
663             "@memberof " + exportName(service),
664             "@instance",
665             "@param {" + exportName(method.resolvedRequestType, !config.forceMessage) + "} request " + method.resolvedRequestType.name + " message or plain object",
666             "@returns {Promise<" + exportName(method.resolvedResponseType) + ">} Promise",
667             "@variation 2"
668         ]);
669     });
670 }
671
672 function buildEnum(ref, enm) {
673
674     push("");
675     var comment = [
676         enm.comment || enm.name + " enum.",
677         enm.parent instanceof protobuf.Root ? "@exports " + escapeName(enm.name) : "@name " + exportName(enm),
678         config.forceEnumString ? "@enum {string}" : "@enum {number}",
679     ];
680     Object.keys(enm.values).forEach(function(key) {
681         var val = config.forceEnumString ? key : enm.values[key];
682         comment.push((config.forceEnumString ? "@property {string} " : "@property {number} ") + key + "=" + val + " " + (enm.comments[key] || key + " value"));
683     });
684     pushComment(comment);
685     if (!ref && config.es6)
686         push("export const " + escapeName(enm.name) + " = " + escapeName(ref) + "." + escapeName(enm.name) + " = (() => {");
687     else
688         push(escapeName(ref) + "." + escapeName(enm.name) + " = (function() {");
689     ++indent;
690         push((config.es6 ? "const" : "var") + " valuesById = {}, values = Object.create(valuesById);");
691         var aliased = [];
692         Object.keys(enm.values).forEach(function(key) {
693             var valueId = enm.values[key];
694             var val = config.forceEnumString ? JSON.stringify(key) : valueId;
695             if (aliased.indexOf(valueId) > -1)
696                 push("values[" + JSON.stringify(key) + "] = " + val + ";");
697             else {
698                 push("values[valuesById[" + valueId + "] = " + JSON.stringify(key) + "] = " + val + ";");
699                 aliased.push(valueId);
700             }
701         });
702         push("return values;");
703     --indent;
704     push("})();");
705 }