xref: /aosp_15_r20/external/flatbuffers/src/idl_gen_ts.cpp (revision 890232f25432b36107d06881e0a25aaa6b473652)
1 /*
2  * Copyright 2014 Google Inc. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <algorithm>
18 #include <cassert>
19 #include <unordered_map>
20 #include <unordered_set>
21 
22 #include "flatbuffers/code_generators.h"
23 #include "flatbuffers/flatbuffers.h"
24 #include "flatbuffers/idl.h"
25 #include "flatbuffers/util.h"
26 
27 namespace flatbuffers {
28 namespace {
29 struct ImportDefinition {
30   std::string name;
31   std::string import_statement;
32   std::string export_statement;
33   std::string bare_file_path;
34   std::string rel_file_path;
35   std::string object_name;
36   const Definition *dependent = nullptr;
37   const Definition *dependency = nullptr;
38 };
39 
40 enum AnnotationType { kParam = 0, kType = 1, kReturns = 2 };
41 }  // namespace
42 
43 namespace ts {
44 // Iterate through all definitions we haven't generate code for (enums, structs,
45 // and tables) and output them to a single file.
46 class TsGenerator : public BaseGenerator {
47  public:
48   typedef std::map<std::string, ImportDefinition> import_set;
49 
TsGenerator(const Parser & parser,const std::string & path,const std::string & file_name)50   TsGenerator(const Parser &parser, const std::string &path,
51               const std::string &file_name)
52       : BaseGenerator(parser, path, file_name, "", "_", "ts") {
53     // clang-format off
54 
55     // List of keywords retrieved from here:
56     // https://github.com/microsoft/TypeScript/issues/2536
57     // One per line to ease comparisons to that list are easier
58     static const char *const keywords[] = {
59       "arguments",
60       "break",
61       "case",
62       "catch",
63       "class",
64       "const",
65       "continue",
66       "debugger",
67       "default",
68       "delete",
69       "do",
70       "else",
71       "enum",
72       "export",
73       "extends",
74       "false",
75       "finally",
76       "for",
77       "function",
78       "if",
79       "import",
80       "in",
81       "instanceof",
82       "new",
83       "null",
84       "Object",
85       "return",
86       "super",
87       "switch",
88       "this",
89       "throw",
90       "true",
91       "try",
92       "typeof",
93       "var",
94       "void",
95       "while",
96       "with",
97       "as",
98       "implements",
99       "interface",
100       "let",
101       "package",
102       "private",
103       "protected",
104       "public",
105       "static",
106       "yield",
107       nullptr,
108       // clang-format on
109     };
110 
111     for (auto kw = keywords; *kw; kw++) keywords_.insert(*kw);
112   }
generate()113   bool generate() {
114     generateEnums();
115     generateStructs();
116     generateEntry();
117     return true;
118   }
119 
IncludeNamespace() const120   bool IncludeNamespace() const {
121     // When generating a single flat file and all its includes, namespaces are
122     // important to avoid type name clashes.
123     return parser_.opts.ts_flat_file && parser_.opts.generate_all;
124   }
125 
GetTypeName(const EnumDef & def,const bool=false,const bool force_ns_wrap=false)126   std::string GetTypeName(const EnumDef &def, const bool = false,
127                           const bool force_ns_wrap = false) {
128     std::string base_name = def.name;
129 
130     if (IncludeNamespace() || force_ns_wrap) {
131       base_name = WrapInNameSpace(def.defined_namespace, base_name);
132     }
133 
134     return EscapeKeyword(base_name);
135   }
136 
GetTypeName(const StructDef & def,const bool object_api=false,const bool force_ns_wrap=false)137   std::string GetTypeName(const StructDef &def, const bool object_api = false,
138                           const bool force_ns_wrap = false) {
139     std::string base_name = def.name;
140 
141     if (object_api && parser_.opts.generate_object_based_api) {
142       base_name =
143           parser_.opts.object_prefix + base_name + parser_.opts.object_suffix;
144     }
145 
146     if (IncludeNamespace() || force_ns_wrap) {
147       base_name = WrapInNameSpace(def.defined_namespace, base_name);
148     }
149 
150     return EscapeKeyword(base_name);
151   }
152 
153   // Save out the generated code for a single class while adding
154   // declaration boilerplate.
SaveType(const Definition & definition,const std::string & class_code,import_set & imports,import_set & bare_imports)155   bool SaveType(const Definition &definition, const std::string &class_code,
156                 import_set &imports, import_set &bare_imports) {
157     if (!class_code.length()) return true;
158 
159     std::string code;
160 
161     if (!parser_.opts.ts_flat_file) {
162       code += "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
163 
164       for (auto it = bare_imports.begin(); it != bare_imports.end(); it++) {
165         code += it->second.import_statement + "\n";
166       }
167       if (!bare_imports.empty()) code += "\n";
168 
169       for (auto it = imports.begin(); it != imports.end(); it++) {
170         if (it->second.dependency != &definition) {
171           code += it->second.import_statement + "\n";
172         }
173       }
174       if (!imports.empty()) code += "\n\n";
175     }
176 
177     code += class_code;
178 
179     if (parser_.opts.ts_flat_file) {
180       flat_file_ += code;
181       flat_file_ += "\n";
182       flat_file_definitions_.insert(&definition);
183       return true;
184     } else {
185       auto basename =
186           NamespaceDir(*definition.defined_namespace, true) +
187           ConvertCase(definition.name, Case::kDasher, Case::kUpperCamel);
188 
189       return SaveFile((basename + ".ts").c_str(), code, false);
190     }
191   }
192 
193  private:
194   std::unordered_set<std::string> keywords_;
195 
EscapeKeyword(const std::string & name) const196   std::string EscapeKeyword(const std::string &name) const {
197     return keywords_.find(name) == keywords_.end() ? name : name + "_";
198   }
199 
200   import_set imports_all_;
201 
202   // The following three members are used when generating typescript code into a
203   // single file rather than creating separate files for each type.
204 
205   // flat_file_ contains the aggregated contents of the file prior to being
206   // written to disk.
207   std::string flat_file_;
208   // flat_file_definitions_ tracks which types have been written to flat_file_.
209   std::unordered_set<const Definition *> flat_file_definitions_;
210   // This maps from import names to types to import.
211   std::map<std::string, std::map<std::string, std::string>>
212       flat_file_import_declarations_;
213   // For flat file codegen, tracks whether we need to import the flatbuffers
214   // library itself (not necessary for files that solely consist of enum
215   // definitions).
216   bool import_flatbuffers_lib_ = false;
217 
218   // Generate code for all enums.
generateEnums()219   void generateEnums() {
220     for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
221          ++it) {
222       import_set bare_imports;
223       import_set imports;
224       std::string enumcode;
225       auto &enum_def = **it;
226       GenEnum(enum_def, &enumcode, imports, false);
227       GenEnum(enum_def, &enumcode, imports, true);
228       SaveType(enum_def, enumcode, imports, bare_imports);
229       imports_all_.insert(imports.begin(), imports.end());
230     }
231   }
232 
233   // Generate code for all structs.
generateStructs()234   void generateStructs() {
235     for (auto it = parser_.structs_.vec.begin();
236          it != parser_.structs_.vec.end(); ++it) {
237       import_set bare_imports;
238       import_set imports;
239       AddImport(bare_imports, "* as flatbuffers", "flatbuffers");
240       import_flatbuffers_lib_ = true;
241       auto &struct_def = **it;
242       std::string declcode;
243       GenStruct(parser_, struct_def, &declcode, imports);
244       SaveType(struct_def, declcode, imports, bare_imports);
245       imports_all_.insert(imports.begin(), imports.end());
246     }
247   }
248 
249   // Generate code for a single entry point module.
generateEntry()250   void generateEntry() {
251     std::string code =
252         "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
253     if (parser_.opts.ts_flat_file) {
254       if (import_flatbuffers_lib_) {
255         code += "import * as flatbuffers from 'flatbuffers';\n";
256         code += "\n";
257       }
258       // Only include import statements when not generating all.
259       if (!parser_.opts.generate_all) {
260         for (const auto &it : flat_file_import_declarations_) {
261           // Note that we do end up generating an import for ourselves, which
262           // should generally be harmless.
263           // TODO: Make it so we don't generate a self-import; this will also
264           // require modifying AddImport to ensure that we don't use
265           // namespace-prefixed names anywhere...
266           std::string file = it.first;
267           if (file.empty()) { continue; }
268           std::string noext = flatbuffers::StripExtension(file);
269           std::string basename = flatbuffers::StripPath(noext);
270           std::string include_file = GeneratedFileName(
271               parser_.opts.include_prefix,
272               parser_.opts.keep_prefix ? noext : basename, parser_.opts);
273           // TODO: what is the right behavior when different include flags are
274           // specified here? Should we always be adding the "./" for a relative
275           // path or turn it off if --include-prefix is specified, or something
276           // else?
277           std::string include_name =
278               "./" + flatbuffers::StripExtension(include_file);
279           code += "import {";
280           for (const auto &pair : it.second) {
281             code += EscapeKeyword(pair.first) + " as " +
282                     EscapeKeyword(pair.second) + ", ";
283           }
284           code.resize(code.size() - 2);
285           code += "} from '" + include_name + "';\n";
286         }
287         code += "\n";
288       }
289 
290       code += flat_file_;
291       const std::string filename =
292           GeneratedFileName(path_, file_name_, parser_.opts);
293       SaveFile(filename.c_str(), code, false);
294     } else {
295       for (auto it = imports_all_.begin(); it != imports_all_.end(); it++) {
296         code += it->second.export_statement + "\n";
297       }
298       const std::string path =
299           GeneratedFileName(path_, file_name_, parser_.opts);
300       SaveFile(path.c_str(), code, false);
301     }
302   }
303 
304   // Generate a documentation comment, if available.
GenDocComment(const std::vector<std::string> & dc,std::string * code_ptr,const char * indent=nullptr)305   static void GenDocComment(const std::vector<std::string> &dc,
306                             std::string *code_ptr,
307                             const char *indent = nullptr) {
308     if (dc.empty()) {
309       // Don't output empty comment blocks with 0 lines of comment content.
310       return;
311     }
312 
313     std::string &code = *code_ptr;
314     if (indent) code += indent;
315     code += "/**\n";
316     for (auto it = dc.begin(); it != dc.end(); ++it) {
317       if (indent) code += indent;
318       code += " *" + *it + "\n";
319     }
320     if (indent) code += indent;
321     code += " */\n";
322   }
323 
GenDocComment(std::string * code_ptr)324   static void GenDocComment(std::string *code_ptr) {
325     GenDocComment(std::vector<std::string>(), code_ptr);
326   }
327 
328   // Generate an enum declaration and an enum string lookup table.
GenEnum(EnumDef & enum_def,std::string * code_ptr,import_set & imports,bool reverse)329   void GenEnum(EnumDef &enum_def, std::string *code_ptr, import_set &imports,
330                bool reverse) {
331     if (enum_def.generated) return;
332     if (reverse) return;  // FIXME.
333     std::string &code = *code_ptr;
334     GenDocComment(enum_def.doc_comment, code_ptr);
335     code += "export enum ";
336     code += GetTypeName(enum_def);
337     code += " {\n";
338     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
339       auto &ev = **it;
340       if (!ev.doc_comment.empty()) {
341         if (it != enum_def.Vals().begin()) { code += '\n'; }
342         GenDocComment(ev.doc_comment, code_ptr, "  ");
343       }
344 
345       const std::string escaped_name = EscapeKeyword(ev.name);
346 
347       // Generate mapping between EnumName: EnumValue(int)
348       if (reverse) {
349         code += "  '" + enum_def.ToString(ev) + "'";
350         code += " = ";
351         code += "'" + escaped_name + "'";
352       } else {
353         code += "  " + escaped_name;
354         code += " = ";
355         // Unfortunately, because typescript does not support bigint enums,
356         // for 64-bit enums, we instead map the enum names to strings.
357         switch (enum_def.underlying_type.base_type) {
358           case BASE_TYPE_LONG:
359           case BASE_TYPE_ULONG: {
360             code += "'" + enum_def.ToString(ev) + "'";
361             break;
362           }
363           default: code += enum_def.ToString(ev);
364         }
365       }
366 
367       code += (it + 1) != enum_def.Vals().end() ? ",\n" : "\n";
368     }
369     code += "}";
370 
371     if (enum_def.is_union) {
372       code += GenUnionConvFunc(enum_def.underlying_type, imports);
373     }
374 
375     code += "\n";
376   }
377 
GenType(const Type & type)378   static std::string GenType(const Type &type) {
379     switch (type.base_type) {
380       case BASE_TYPE_BOOL:
381       case BASE_TYPE_CHAR: return "Int8";
382       case BASE_TYPE_UTYPE:
383       case BASE_TYPE_UCHAR: return "Uint8";
384       case BASE_TYPE_SHORT: return "Int16";
385       case BASE_TYPE_USHORT: return "Uint16";
386       case BASE_TYPE_INT: return "Int32";
387       case BASE_TYPE_UINT: return "Uint32";
388       case BASE_TYPE_LONG: return "Int64";
389       case BASE_TYPE_ULONG: return "Uint64";
390       case BASE_TYPE_FLOAT: return "Float32";
391       case BASE_TYPE_DOUBLE: return "Float64";
392       case BASE_TYPE_STRING: return "String";
393       case BASE_TYPE_VECTOR: return GenType(type.VectorType());
394       case BASE_TYPE_STRUCT: return type.struct_def->name;
395       default: return "flatbuffers.Table";
396     }
397   }
398 
GenGetter(const Type & type,const std::string & arguments)399   std::string GenGetter(const Type &type, const std::string &arguments) {
400     switch (type.base_type) {
401       case BASE_TYPE_STRING: return GenBBAccess() + ".__string" + arguments;
402       case BASE_TYPE_STRUCT: return GenBBAccess() + ".__struct" + arguments;
403       case BASE_TYPE_UNION:
404         if (!UnionHasStringType(*type.enum_def)) {
405           return GenBBAccess() + ".__union" + arguments;
406         }
407         return GenBBAccess() + ".__union_with_string" + arguments;
408       case BASE_TYPE_VECTOR: return GenGetter(type.VectorType(), arguments);
409       default: {
410         auto getter = GenBBAccess() + ".read" +
411                       ConvertCase(GenType(type), Case::kUpperCamel) + arguments;
412         if (type.base_type == BASE_TYPE_BOOL) { getter = "!!" + getter; }
413         return getter;
414       }
415     }
416   }
417 
GenBBAccess() const418   std::string GenBBAccess() const { return "this.bb!"; }
419 
GenDefaultValue(const FieldDef & field,import_set & imports)420   std::string GenDefaultValue(const FieldDef &field, import_set &imports) {
421     if (field.IsScalarOptional()) { return "null"; }
422 
423     const auto &value = field.value;
424     if (value.type.enum_def && value.type.base_type != BASE_TYPE_UNION &&
425         value.type.base_type != BASE_TYPE_VECTOR) {
426       // If the value is an enum with a 64-bit base type, we have to just
427       // return the bigint value directly since typescript does not support
428       // enums with bigint backing types.
429       switch (value.type.base_type) {
430         case BASE_TYPE_LONG:
431         case BASE_TYPE_ULONG: {
432           return "BigInt('" + value.constant + "')";
433         }
434         default: {
435           if (auto val = value.type.enum_def->FindByValue(value.constant)) {
436             return AddImport(imports, *value.type.enum_def,
437                              *value.type.enum_def)
438                        .name +
439                    "." + EscapeKeyword(val->name);
440           } else {
441             return value.constant;
442           }
443         }
444       }
445     }
446 
447     switch (value.type.base_type) {
448       case BASE_TYPE_BOOL: return value.constant == "0" ? "false" : "true";
449 
450       case BASE_TYPE_STRING:
451       case BASE_TYPE_UNION:
452       case BASE_TYPE_STRUCT: {
453         return "null";
454       }
455 
456       case BASE_TYPE_VECTOR: return "[]";
457 
458       case BASE_TYPE_LONG:
459       case BASE_TYPE_ULONG: {
460         return "BigInt('" + value.constant + "')";
461       }
462 
463       default: return value.constant;
464     }
465   }
466 
GenTypeName(import_set & imports,const Definition & owner,const Type & type,bool input,bool allowNull=false)467   std::string GenTypeName(import_set &imports, const Definition &owner,
468                           const Type &type, bool input,
469                           bool allowNull = false) {
470     if (!input) {
471       if (IsString(type) || type.base_type == BASE_TYPE_STRUCT) {
472         std::string name;
473         if (IsString(type)) {
474           name = "string|Uint8Array";
475         } else {
476           name = AddImport(imports, owner, *type.struct_def).name;
477         }
478         return allowNull ? (name + "|null") : name;
479       }
480     }
481 
482     switch (type.base_type) {
483       case BASE_TYPE_BOOL: return allowNull ? "boolean|null" : "boolean";
484       case BASE_TYPE_LONG:
485       case BASE_TYPE_ULONG: return allowNull ? "bigint|null" : "bigint";
486       default:
487         if (IsScalar(type.base_type)) {
488           if (type.enum_def) {
489             const auto enum_name =
490                 AddImport(imports, owner, *type.enum_def).name;
491             return allowNull ? (enum_name + "|null") : enum_name;
492           }
493           return allowNull ? "number|null" : "number";
494         }
495         return "flatbuffers.Offset";
496     }
497   }
498 
499   // Returns the method name for use with add/put calls.
GenWriteMethod(const Type & type)500   static std::string GenWriteMethod(const Type &type) {
501     // Forward to signed versions since unsigned versions don't exist
502     switch (type.base_type) {
503       case BASE_TYPE_UTYPE:
504       case BASE_TYPE_UCHAR: return GenWriteMethod(Type(BASE_TYPE_CHAR));
505       case BASE_TYPE_USHORT: return GenWriteMethod(Type(BASE_TYPE_SHORT));
506       case BASE_TYPE_UINT: return GenWriteMethod(Type(BASE_TYPE_INT));
507       case BASE_TYPE_ULONG: return GenWriteMethod(Type(BASE_TYPE_LONG));
508       default: break;
509     }
510 
511     return IsScalar(type.base_type)
512                ? ConvertCase(GenType(type), Case::kUpperCamel)
513                : (IsStruct(type) ? "Struct" : "Offset");
514   }
515 
MaybeAdd(T value)516   template<typename T> static std::string MaybeAdd(T value) {
517     return value != 0 ? " + " + NumToString(value) : "";
518   }
519 
MaybeScale(T value)520   template<typename T> static std::string MaybeScale(T value) {
521     return value != 1 ? " * " + NumToString(value) : "";
522   }
523 
GenStructArgs(import_set & imports,const StructDef & struct_def,std::string * arguments,const std::string & nameprefix)524   void GenStructArgs(import_set &imports, const StructDef &struct_def,
525                      std::string *arguments, const std::string &nameprefix) {
526     for (auto it = struct_def.fields.vec.begin();
527          it != struct_def.fields.vec.end(); ++it) {
528       auto &field = **it;
529       if (IsStruct(field.value.type)) {
530         // Generate arguments for a struct inside a struct. To ensure names
531         // don't clash, and to make it obvious these arguments are constructing
532         // a nested struct, prefix the name with the field name.
533         GenStructArgs(imports, *field.value.type.struct_def, arguments,
534                       nameprefix + field.name + "_");
535       } else {
536         *arguments += ", " + nameprefix + field.name + ": " +
537                       GenTypeName(imports, field, field.value.type, true,
538                                   field.IsOptional());
539       }
540     }
541   }
542 
GenStructBody(const StructDef & struct_def,std::string * body,const std::string & nameprefix)543   static void GenStructBody(const StructDef &struct_def, std::string *body,
544                             const std::string &nameprefix) {
545     *body += "  builder.prep(";
546     *body += NumToString(struct_def.minalign) + ", ";
547     *body += NumToString(struct_def.bytesize) + ");\n";
548 
549     for (auto it = struct_def.fields.vec.rbegin();
550          it != struct_def.fields.vec.rend(); ++it) {
551       auto &field = **it;
552       if (field.padding) {
553         *body += "  builder.pad(" + NumToString(field.padding) + ");\n";
554       }
555       if (IsStruct(field.value.type)) {
556         // Generate arguments for a struct inside a struct. To ensure names
557         // don't clash, and to make it obvious these arguments are constructing
558         // a nested struct, prefix the name with the field name.
559         GenStructBody(*field.value.type.struct_def, body,
560                       nameprefix + field.name + "_");
561       } else {
562         *body += "  builder.write" + GenWriteMethod(field.value.type) + "(";
563         if (field.value.type.base_type == BASE_TYPE_BOOL) { *body += "+"; }
564         *body += nameprefix + field.name + ");\n";
565       }
566     }
567   }
568 
GenerateNewExpression(const std::string & object_name)569   std::string GenerateNewExpression(const std::string &object_name) {
570     return "new " + EscapeKeyword(object_name) + "()";
571   }
572 
GenerateRootAccessor(StructDef & struct_def,std::string * code_ptr,std::string & code,const std::string & object_name,bool size_prefixed)573   void GenerateRootAccessor(StructDef &struct_def, std::string *code_ptr,
574                             std::string &code, const std::string &object_name,
575                             bool size_prefixed) {
576     if (!struct_def.fixed) {
577       GenDocComment(code_ptr);
578       std::string sizePrefixed("SizePrefixed");
579       code += "static get" + (size_prefixed ? sizePrefixed : "") + "Root" +
580               GetPrefixedName(struct_def, "As");
581       code += "(bb:flatbuffers.ByteBuffer, obj?:" + object_name +
582               "):" + object_name + " {\n";
583       if (size_prefixed) {
584         code +=
585             "  bb.setPosition(bb.position() + "
586             "flatbuffers.SIZE_PREFIX_LENGTH);\n";
587       }
588       code += "  return (obj || " + GenerateNewExpression(object_name);
589       code += ").__init(bb.readInt32(bb.position()) + bb.position(), bb);\n";
590       code += "}\n\n";
591     }
592   }
593 
GenerateFinisher(StructDef & struct_def,std::string * code_ptr,std::string & code,bool size_prefixed)594   void GenerateFinisher(StructDef &struct_def, std::string *code_ptr,
595                         std::string &code, bool size_prefixed) {
596     if (parser_.root_struct_def_ == &struct_def) {
597       std::string sizePrefixed("SizePrefixed");
598       GenDocComment(code_ptr);
599 
600       code += "static finish" + (size_prefixed ? sizePrefixed : "") +
601               GetPrefixedName(struct_def) + "Buffer";
602       code += "(builder:flatbuffers.Builder, offset:flatbuffers.Offset) {\n";
603       code += "  builder.finish(offset";
604       if (!parser_.file_identifier_.empty()) {
605         code += ", '" + parser_.file_identifier_ + "'";
606       }
607       if (size_prefixed) {
608         if (parser_.file_identifier_.empty()) { code += ", undefined"; }
609         code += ", true";
610       }
611       code += ");\n";
612       code += "}\n\n";
613     }
614   }
615 
UnionHasStringType(const EnumDef & union_enum)616   bool UnionHasStringType(const EnumDef &union_enum) {
617     return std::any_of(union_enum.Vals().begin(), union_enum.Vals().end(),
618                        [](const EnumVal *ev) {
619                          return !ev->IsZero() && IsString(ev->union_type);
620                        });
621   }
622 
GenUnionGenericTypeTS(const EnumDef & union_enum)623   std::string GenUnionGenericTypeTS(const EnumDef &union_enum) {
624     // TODO: make it work without any
625     // return std::string("T") + (UnionHasStringType(union_enum) ? "|string" :
626     // "");
627     return std::string("any") +
628            (UnionHasStringType(union_enum) ? "|string" : "");
629   }
630 
GenUnionTypeTS(const EnumDef & union_enum,import_set & imports)631   std::string GenUnionTypeTS(const EnumDef &union_enum, import_set &imports) {
632     std::string ret;
633     std::set<std::string> type_list;
634 
635     for (auto it = union_enum.Vals().begin(); it != union_enum.Vals().end();
636          ++it) {
637       const auto &ev = **it;
638       if (ev.IsZero()) { continue; }
639 
640       std::string type = "";
641       if (IsString(ev.union_type)) {
642         type = "string";  // no need to wrap string type in namespace
643       } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
644         type = AddImport(imports, union_enum, *ev.union_type.struct_def).name;
645       } else {
646         FLATBUFFERS_ASSERT(false);
647       }
648       type_list.insert(type);
649     }
650 
651     for (auto it = type_list.begin(); it != type_list.end(); ++it) {
652       ret += *it + ((std::next(it) == type_list.end()) ? "" : "|");
653     }
654 
655     return ret;
656   }
657 
CheckIfNameClashes(const import_set & imports,const std::string & name)658   static bool CheckIfNameClashes(const import_set &imports,
659                                  const std::string &name) {
660     // TODO: this would be better as a hashset.
661     for (auto it = imports.begin(); it != imports.end(); it++) {
662       if (it->second.name == name) { return true; }
663     }
664     return false;
665   }
666 
GenSymbolExpression(const StructDef & struct_def,const bool has_name_clash,const std::string & import_name,const std::string & name,const std::string & object_name)667   std::string GenSymbolExpression(const StructDef &struct_def,
668                                   const bool has_name_clash,
669                                   const std::string &import_name,
670                                   const std::string &name,
671                                   const std::string &object_name) {
672     std::string symbols_expression;
673 
674     if (has_name_clash) {
675       // We have a name clash
676       symbols_expression += import_name + " as " + name;
677 
678       if (parser_.opts.generate_object_based_api) {
679         symbols_expression += ", " +
680                               GetTypeName(struct_def, /*object_api =*/true) +
681                               " as " + object_name;
682       }
683     } else {
684       // No name clash, use the provided name
685       symbols_expression += name;
686 
687       if (parser_.opts.generate_object_based_api) {
688         symbols_expression += ", " + object_name;
689       }
690     }
691 
692     return symbols_expression;
693   }
694 
GenSymbolExpression(const EnumDef & enum_def,const bool has_name_clash,const std::string & import_name,const std::string & name,const std::string &)695   std::string GenSymbolExpression(const EnumDef &enum_def,
696                                   const bool has_name_clash,
697                                   const std::string &import_name,
698                                   const std::string &name,
699                                   const std::string &) {
700     std::string symbols_expression;
701     if (has_name_clash) {
702       symbols_expression += import_name + " as " + name;
703     } else {
704       symbols_expression += name;
705     }
706 
707     if (enum_def.is_union) {
708       symbols_expression += ", unionTo" + name;
709       symbols_expression += ", unionListTo" + name;
710     }
711 
712     return symbols_expression;
713   }
714 
715   template<typename DefintionT>
AddImport(import_set & imports,const Definition & dependent,const DefintionT & dependency)716   ImportDefinition AddImport(import_set &imports, const Definition &dependent,
717                              const DefintionT &dependency) {
718     // The unique name of the dependency, fully qualified in its namespace.
719     const std::string unique_name = GetTypeName(
720         dependency, /*object_api = */ false, /*force_ns_wrap=*/true);
721 
722     // Look if we have already added this import and return its name if found.
723     const auto import_pair = imports.find(unique_name);
724     if (import_pair != imports.end()) { return import_pair->second; }
725 
726     // Check if this name would have a name clash with another type. Just use
727     // the "base" name (properly escaped) without any namespacing applied.
728     const std::string import_name = GetTypeName(dependency);
729     const bool has_name_clash = CheckIfNameClashes(imports, import_name);
730 
731     // If we have a name clash, use the unique name, otherwise use simple name.
732     std::string name = has_name_clash ? unique_name : import_name;
733 
734     const std::string object_name =
735         GetTypeName(dependency, /*object_api=*/true, has_name_clash);
736 
737     if (parser_.opts.ts_flat_file) {
738       // In flat-file generation, do not attempt to import things from ourselves
739       // *and* do not wrap namespaces (note that this does override the logic
740       // above, but since we force all non-self-imports to use namespace-based
741       // names in flat file generation, it's fine).
742       if (dependent.file == dependency.file) {
743         name = import_name;
744       } else {
745         const std::string file =
746             RelativeToRootPath(StripFileName(AbsolutePath(dependent.file)),
747                                dependency.file)
748                 // Strip the leading //
749                 .substr(2);
750         flat_file_import_declarations_[file][import_name] = name;
751         if (parser_.opts.generate_object_based_api &&
752             typeid(dependency) == typeid(StructDef)) {
753           flat_file_import_declarations_[file][import_name + "T"] = object_name;
754         }
755       }
756     }
757 
758     const std::string symbols_expression = GenSymbolExpression(
759         dependency, has_name_clash, import_name, name, object_name);
760 
761     std::string bare_file_path;
762     std::string rel_file_path;
763     const auto &dep_comps = dependent.defined_namespace->components;
764     for (size_t i = 0; i < dep_comps.size(); i++) {
765       rel_file_path += i == 0 ? ".." : (kPathSeparator + std::string(".."));
766     }
767     if (dep_comps.size() == 0) { rel_file_path += "."; }
768 
769     const auto &depc_comps = dependency.defined_namespace->components;
770     for (auto it = depc_comps.begin(); it != depc_comps.end(); it++) {
771       bare_file_path +=
772           kPathSeparator + ConvertCase(*it, Case::kDasher, Case::kUpperCamel);
773     }
774     bare_file_path +=
775         kPathSeparator +
776         ConvertCase(dependency.name, Case::kDasher, Case::kUpperCamel);
777     rel_file_path += bare_file_path;
778 
779     ImportDefinition import;
780     import.name = name;
781     import.object_name = object_name;
782     import.bare_file_path = bare_file_path;
783     import.rel_file_path = rel_file_path;
784     import.import_statement =
785         "import { " + symbols_expression + " } from '" + rel_file_path + "';";
786     import.export_statement =
787         "export { " + symbols_expression + " } from '." + bare_file_path + "';";
788     import.dependency = &dependency;
789     import.dependent = &dependent;
790 
791     imports.insert(std::make_pair(unique_name, import));
792 
793     return import;
794   }
795 
AddImport(import_set & imports,std::string import_name,std::string fileName)796   void AddImport(import_set &imports, std::string import_name,
797                  std::string fileName) {
798     ImportDefinition import;
799     import.name = import_name;
800     import.import_statement =
801         "import " + import_name + " from '" + fileName + "';";
802     imports.insert(std::make_pair(import_name, import));
803   }
804 
805   // Generate a TS union type based on a union's enum
GenObjApiUnionTypeTS(import_set & imports,const StructDef & dependent,const IDLOptions &,const EnumDef & union_enum)806   std::string GenObjApiUnionTypeTS(import_set &imports,
807                                    const StructDef &dependent,
808                                    const IDLOptions &,
809                                    const EnumDef &union_enum) {
810     std::string ret = "";
811     std::set<std::string> type_list;
812 
813     for (auto it = union_enum.Vals().begin(); it != union_enum.Vals().end();
814          ++it) {
815       const auto &ev = **it;
816       if (ev.IsZero()) { continue; }
817 
818       std::string type = "";
819       if (IsString(ev.union_type)) {
820         type = "string";  // no need to wrap string type in namespace
821       } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
822         type = AddImport(imports, dependent, *ev.union_type.struct_def)
823                    .object_name;
824       } else {
825         FLATBUFFERS_ASSERT(false);
826       }
827       type_list.insert(type);
828     }
829 
830     size_t totalPrinted = 0;
831     for (auto it = type_list.begin(); it != type_list.end(); ++it) {
832       ++totalPrinted;
833       ret += *it + ((totalPrinted == type_list.size()) ? "" : "|");
834     }
835 
836     return ret;
837   }
838 
GenUnionConvFuncName(const EnumDef & enum_def)839   std::string GenUnionConvFuncName(const EnumDef &enum_def) {
840     return "unionTo" + enum_def.name;
841   }
842 
GenUnionListConvFuncName(const EnumDef & enum_def)843   std::string GenUnionListConvFuncName(const EnumDef &enum_def) {
844     return "unionListTo" + enum_def.name;
845   }
846 
GenUnionConvFunc(const Type & union_type,import_set & imports)847   std::string GenUnionConvFunc(const Type &union_type, import_set &imports) {
848     if (union_type.enum_def) {
849       const auto &enum_def = *union_type.enum_def;
850 
851       const auto valid_union_type = GenUnionTypeTS(enum_def, imports);
852       const auto valid_union_type_with_null = valid_union_type + "|null";
853 
854       auto ret = "\n\nexport function " + GenUnionConvFuncName(enum_def) +
855                  "(\n  type: " + GetTypeName(enum_def) +
856                  ",\n  accessor: (obj:" + valid_union_type + ") => " +
857                  valid_union_type_with_null +
858                  "\n): " + valid_union_type_with_null + " {\n";
859 
860       const auto enum_type = AddImport(imports, enum_def, enum_def).name;
861 
862       const auto union_enum_loop = [&](const std::string &accessor_str) {
863         ret += "  switch(" + enum_type + "[type]) {\n";
864         ret += "    case 'NONE': return null; \n";
865 
866         for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
867              ++it) {
868           const auto &ev = **it;
869           if (ev.IsZero()) { continue; }
870 
871           ret += "    case '" + ev.name + "': ";
872 
873           if (IsString(ev.union_type)) {
874             ret += "return " + accessor_str + "'') as string;";
875           } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
876             const auto type =
877                 AddImport(imports, enum_def, *ev.union_type.struct_def).name;
878             ret += "return " + accessor_str + "new " + type + "())! as " +
879                    type + ";";
880           } else {
881             FLATBUFFERS_ASSERT(false);
882           }
883           ret += "\n";
884         }
885 
886         ret += "    default: return null;\n";
887         ret += "  }\n";
888       };
889 
890       union_enum_loop("accessor(");
891       ret += "}";
892 
893       ret += "\n\nexport function " + GenUnionListConvFuncName(enum_def) +
894              "(\n  type: " + GetTypeName(enum_def) +
895              ", \n  accessor: (index: number, obj:" + valid_union_type +
896              ") => " + valid_union_type_with_null +
897              ", \n  index: number\n): " + valid_union_type_with_null + " {\n";
898       union_enum_loop("accessor(index, ");
899       ret += "}";
900 
901       return ret;
902     }
903     FLATBUFFERS_ASSERT(0);
904     return "";
905   }
906 
907   // Used for generating a short function that returns the correct class
908   // based on union enum type. Assume the context is inside the non object api
909   // type
GenUnionValTS(import_set & imports,const StructDef & dependent,const std::string & field_name,const Type & union_type,const bool is_array=false)910   std::string GenUnionValTS(import_set &imports, const StructDef &dependent,
911                             const std::string &field_name,
912                             const Type &union_type,
913                             const bool is_array = false) {
914     if (union_type.enum_def) {
915       const auto &enum_def = *union_type.enum_def;
916       const auto enum_type = AddImport(imports, dependent, enum_def).name;
917       const std::string union_accessor = "this." + field_name;
918 
919       const auto union_has_string = UnionHasStringType(enum_def);
920       const auto field_binded_method = "this." + field_name + ".bind(this)";
921 
922       std::string ret;
923 
924       if (!is_array) {
925         const auto conversion_function = GenUnionConvFuncName(enum_def);
926         const auto target_enum = "this." + field_name + "Type()";
927 
928         ret = "(() => {\n";
929         ret += "      let temp = " + conversion_function + "(" + target_enum +
930                ", " + field_binded_method + ");\n";
931         ret += "      if(temp === null) { return null; }\n";
932         ret += union_has_string
933                    ? "      if(typeof temp === 'string') { return temp; }\n"
934                    : "";
935         ret += "      return temp.unpack()\n";
936         ret += "  })()";
937       } else {
938         const auto conversion_function = GenUnionListConvFuncName(enum_def);
939         const auto target_enum_accesor = "this." + field_name + "Type";
940         const auto target_enum_length = target_enum_accesor + "Length()";
941 
942         ret = "(() => {\n";
943         ret += "    let ret = [];\n";
944         ret += "    for(let targetEnumIndex = 0; targetEnumIndex < " +
945                target_enum_length +
946                "; "
947                "++targetEnumIndex) {\n";
948         ret += "      let targetEnum = " + target_enum_accesor +
949                "(targetEnumIndex);\n";
950         ret += "      if(targetEnum === null || " + enum_type +
951                "[targetEnum!] === 'NONE') { "
952                "continue; }\n\n";
953         ret += "      let temp = " + conversion_function + "(targetEnum, " +
954                field_binded_method + ", targetEnumIndex);\n";
955         ret += "      if(temp === null) { continue; }\n";
956         ret += union_has_string ? "      if(typeof temp === 'string') { "
957                                   "ret.push(temp); continue; }\n"
958                                 : "";
959         ret += "      ret.push(temp.unpack());\n";
960         ret += "    }\n";
961         ret += "    return ret;\n";
962         ret += "  })()";
963       }
964 
965       return ret;
966     }
967 
968     FLATBUFFERS_ASSERT(0);
969     return "";
970   }
971 
GenNullCheckConditional(const std::string & nullCheckVar,const std::string & trueVal,const std::string & falseVal="null")972   static std::string GenNullCheckConditional(
973       const std::string &nullCheckVar, const std::string &trueVal,
974       const std::string &falseVal = "null") {
975     return "(" + nullCheckVar + " !== null ? " + trueVal + " : " + falseVal +
976            ")";
977   }
978 
GenStructMemberValueTS(const StructDef & struct_def,const std::string & prefix,const std::string & delimiter,const bool nullCheck=true)979   std::string GenStructMemberValueTS(const StructDef &struct_def,
980                                      const std::string &prefix,
981                                      const std::string &delimiter,
982                                      const bool nullCheck = true) {
983     std::string ret;
984     for (auto it = struct_def.fields.vec.begin();
985          it != struct_def.fields.vec.end(); ++it) {
986       auto &field = **it;
987 
988       auto curr_member_accessor =
989           prefix + "." + ConvertCase(field.name, Case::kLowerCamel);
990       if (prefix != "this") {
991         curr_member_accessor =
992             prefix + "?." + ConvertCase(field.name, Case::kLowerCamel);
993       }
994       if (IsStruct(field.value.type)) {
995         ret += GenStructMemberValueTS(*field.value.type.struct_def,
996                                       curr_member_accessor, delimiter);
997       } else {
998         if (nullCheck) {
999           std::string nullValue = "0";
1000           if (field.value.type.base_type == BASE_TYPE_BOOL) {
1001             nullValue = "false";
1002           }
1003           ret += "(" + curr_member_accessor + " ?? " + nullValue + ")";
1004         } else {
1005           ret += curr_member_accessor;
1006         }
1007       }
1008 
1009       if (std::next(it) != struct_def.fields.vec.end()) { ret += delimiter; }
1010     }
1011 
1012     return ret;
1013   }
1014 
GenObjApi(const Parser & parser,StructDef & struct_def,std::string & obj_api_unpack_func,std::string & obj_api_class,import_set & imports)1015   void GenObjApi(const Parser &parser, StructDef &struct_def,
1016                  std::string &obj_api_unpack_func, std::string &obj_api_class,
1017                  import_set &imports) {
1018     const auto class_name = GetTypeName(struct_def, /*object_api=*/true);
1019 
1020     std::string unpack_func = "\nunpack(): " + class_name +
1021                               " {\n  return new " + class_name + "(" +
1022                               (struct_def.fields.vec.empty() ? "" : "\n");
1023     std::string unpack_to_func = "\nunpackTo(_o: " + class_name + "): void {" +
1024                                  +(struct_def.fields.vec.empty() ? "" : "\n");
1025 
1026     std::string constructor_func = "constructor(";
1027     constructor_func += (struct_def.fields.vec.empty() ? "" : "\n");
1028 
1029     const auto has_create =
1030         struct_def.fixed || CanCreateFactoryMethod(struct_def);
1031 
1032     std::string pack_func_prototype =
1033         "\npack(builder:flatbuffers.Builder): flatbuffers.Offset {\n";
1034 
1035     std::string pack_func_offset_decl;
1036     std::string pack_func_create_call;
1037 
1038     const auto struct_name = AddImport(imports, struct_def, struct_def).name;
1039 
1040     if (has_create) {
1041       pack_func_create_call = "  return " + struct_name + ".create" +
1042                               GetPrefixedName(struct_def) + "(builder" +
1043                               (struct_def.fields.vec.empty() ? "" : ",\n    ");
1044     } else {
1045       pack_func_create_call = "  " + struct_name + ".start" +
1046                               GetPrefixedName(struct_def) + "(builder);\n";
1047     }
1048 
1049     if (struct_def.fixed) {
1050       // when packing struct, nested struct's members instead of the struct's
1051       // offset are used
1052       pack_func_create_call +=
1053           GenStructMemberValueTS(struct_def, "this", ",\n    ", false) + "\n  ";
1054     }
1055 
1056     for (auto it = struct_def.fields.vec.begin();
1057          it != struct_def.fields.vec.end(); ++it) {
1058       auto &field = **it;
1059       if (field.deprecated) continue;
1060 
1061       const auto field_name = ConvertCase(field.name, Case::kLowerCamel);
1062       const auto field_name_escaped = EscapeKeyword(field_name);
1063       const std::string field_binded_method =
1064           "this." + field_name + ".bind(this)";
1065 
1066       std::string field_val;
1067       std::string field_type;
1068       // a string that declares a variable containing the
1069       // offset for things that can't be generated inline
1070       // empty otw
1071       std::string field_offset_decl;
1072       // a string that contains values for things that can be created inline or
1073       // the variable name from field_offset_decl
1074       std::string field_offset_val;
1075       const auto field_default_val = GenDefaultValue(field, imports);
1076 
1077       // Emit a scalar field
1078       const auto is_string = IsString(field.value.type);
1079       if (IsScalar(field.value.type.base_type) || is_string) {
1080         const auto has_null_default = is_string || HasNullDefault(field);
1081 
1082         field_type += GenTypeName(imports, field, field.value.type, false,
1083                                   has_null_default);
1084         field_val = "this." + field_name + "()";
1085 
1086         if (field.value.type.base_type != BASE_TYPE_STRING) {
1087           field_offset_val = "this." + field_name_escaped;
1088         } else {
1089           field_offset_decl = GenNullCheckConditional(
1090               "this." + field_name_escaped,
1091               "builder.createString(this." + field_name_escaped + "!)", "0");
1092         }
1093       }
1094 
1095       // Emit an object field
1096       else {
1097         auto is_vector = false;
1098         switch (field.value.type.base_type) {
1099           case BASE_TYPE_STRUCT: {
1100             const auto &sd = *field.value.type.struct_def;
1101             field_type += AddImport(imports, struct_def, sd).object_name;
1102 
1103             const std::string field_accessor = "this." + field_name + "()";
1104             field_val = GenNullCheckConditional(field_accessor,
1105                                                 field_accessor + "!.unpack()");
1106             auto packing = GenNullCheckConditional(
1107                 "this." + field_name_escaped,
1108                 "this." + field_name_escaped + "!.pack(builder)", "0");
1109 
1110             if (sd.fixed) {
1111               field_offset_val = std::move(packing);
1112             } else {
1113               field_offset_decl = std::move(packing);
1114             }
1115 
1116             break;
1117           }
1118 
1119           case BASE_TYPE_VECTOR: {
1120             auto vectortype = field.value.type.VectorType();
1121             auto vectortypename =
1122                 GenTypeName(imports, struct_def, vectortype, false);
1123             is_vector = true;
1124 
1125             field_type = "(";
1126 
1127             switch (vectortype.base_type) {
1128               case BASE_TYPE_STRUCT: {
1129                 const auto &sd = *field.value.type.struct_def;
1130                 field_type += GetTypeName(sd, /*object_api=*/true);
1131                 ;
1132                 field_type += ")[]";
1133 
1134                 field_val = GenBBAccess() + ".createObjList(" +
1135                             field_binded_method + ", this." + field_name +
1136                             "Length())";
1137 
1138                 if (sd.fixed) {
1139                   field_offset_decl =
1140                       "builder.createStructOffsetList(this." +
1141                       field_name_escaped + ", " +
1142                       AddImport(imports, struct_def, struct_def).name +
1143                       ".start" + ConvertCase(field_name, Case::kUpperCamel) +
1144                       "Vector)";
1145                 } else {
1146                   field_offset_decl =
1147                       AddImport(imports, struct_def, struct_def).name +
1148                       ".create" + ConvertCase(field_name, Case::kUpperCamel) +
1149                       "Vector(builder, builder.createObjectOffsetList(" +
1150                       "this." + field_name_escaped + "))";
1151                 }
1152 
1153                 break;
1154               }
1155 
1156               case BASE_TYPE_STRING: {
1157                 field_type += "string)[]";
1158                 field_val = GenBBAccess() + ".createScalarList(" +
1159                             field_binded_method + ", this." + field_name +
1160                             "Length())";
1161                 field_offset_decl =
1162                     AddImport(imports, struct_def, struct_def).name +
1163                     ".create" + ConvertCase(field_name, Case::kUpperCamel) +
1164                     "Vector(builder, builder.createObjectOffsetList(" +
1165                     "this." + field_name_escaped + "))";
1166                 break;
1167               }
1168 
1169               case BASE_TYPE_UNION: {
1170                 field_type += GenObjApiUnionTypeTS(
1171                     imports, struct_def, parser.opts, *(vectortype.enum_def));
1172                 field_type += ")[]";
1173                 field_val = GenUnionValTS(imports, struct_def, field_name,
1174                                           vectortype, true);
1175 
1176                 field_offset_decl =
1177                     AddImport(imports, struct_def, struct_def).name +
1178                     ".create" + ConvertCase(field_name, Case::kUpperCamel) +
1179                     "Vector(builder, builder.createObjectOffsetList(" +
1180                     "this." + field_name_escaped + "))";
1181 
1182                 break;
1183               }
1184               default: {
1185                 if (vectortype.enum_def) {
1186                   field_type += GenTypeName(imports, struct_def, vectortype,
1187                                             false, HasNullDefault(field));
1188                 } else {
1189                   field_type += vectortypename;
1190                 }
1191                 field_type += ")[]";
1192                 field_val = GenBBAccess() + ".createScalarList(" +
1193                             field_binded_method + ", this." + field_name +
1194                             "Length())";
1195 
1196                 field_offset_decl =
1197                     AddImport(imports, struct_def, struct_def).name +
1198                     ".create" + ConvertCase(field_name, Case::kUpperCamel) +
1199                     "Vector(builder, this." + field_name_escaped + ")";
1200 
1201                 break;
1202               }
1203             }
1204 
1205             break;
1206           }
1207 
1208           case BASE_TYPE_UNION: {
1209             field_type += GenObjApiUnionTypeTS(imports, struct_def, parser.opts,
1210                                                *(field.value.type.enum_def));
1211 
1212             field_val = GenUnionValTS(imports, struct_def, field_name,
1213                                       field.value.type);
1214             field_offset_decl =
1215                 "builder.createObjectOffset(this." + field_name_escaped + ")";
1216             break;
1217           }
1218 
1219           default: FLATBUFFERS_ASSERT(0); break;
1220         }
1221 
1222         // length 0 vector is simply empty instead of null
1223         field_type += is_vector ? "" : "|null";
1224       }
1225 
1226       if (!field_offset_decl.empty()) {
1227         field_offset_decl =
1228             "  const " + field_name_escaped + " = " + field_offset_decl + ";";
1229       }
1230       if (field_offset_val.empty()) { field_offset_val = field_name_escaped; }
1231 
1232       unpack_func += "    " + field_val;
1233       unpack_to_func += "  _o." + field_name_escaped + " = " + field_val + ";";
1234 
1235       // FIXME: if field_type and field_name_escaped are identical, then
1236       // this generates invalid typescript.
1237       constructor_func += "  public " + field_name_escaped + ": " + field_type +
1238                           " = " + field_default_val;
1239 
1240       if (!struct_def.fixed) {
1241         if (!field_offset_decl.empty()) {
1242           pack_func_offset_decl += field_offset_decl + "\n";
1243         }
1244 
1245         if (has_create) {
1246           pack_func_create_call += field_offset_val;
1247         } else {
1248           if (field.IsScalarOptional()) {
1249             pack_func_create_call +=
1250                 "  if (" + field_offset_val + " !== null)\n  ";
1251           }
1252           pack_func_create_call += "  " + struct_name + ".add" +
1253                                    ConvertCase(field.name, Case::kUpperCamel) +
1254                                    "(builder, " + field_offset_val + ");\n";
1255         }
1256       }
1257 
1258       if (std::next(it) != struct_def.fields.vec.end()) {
1259         constructor_func += ",\n";
1260 
1261         if (!struct_def.fixed && has_create) {
1262           pack_func_create_call += ",\n    ";
1263         }
1264 
1265         unpack_func += ",\n";
1266         unpack_to_func += "\n";
1267       } else {
1268         constructor_func += "\n";
1269         if (!struct_def.fixed) {
1270           pack_func_offset_decl += (pack_func_offset_decl.empty() ? "" : "\n");
1271           pack_func_create_call += "\n  ";
1272         }
1273 
1274         unpack_func += "\n  ";
1275         unpack_to_func += "\n";
1276       }
1277     }
1278 
1279     constructor_func += "){}\n\n";
1280 
1281     if (has_create) {
1282       pack_func_create_call += ");";
1283     } else {
1284       pack_func_create_call += "return " + struct_name + ".end" +
1285                                GetPrefixedName(struct_def) + "(builder);";
1286     }
1287     obj_api_class = "\n";
1288     obj_api_class += "export class ";
1289     obj_api_class += GetTypeName(struct_def, /*object_api=*/true);
1290     obj_api_class += " {\n";
1291     obj_api_class += constructor_func;
1292     obj_api_class += pack_func_prototype + pack_func_offset_decl +
1293                      pack_func_create_call + "\n}";
1294 
1295     obj_api_class += "\n}\n";
1296 
1297     unpack_func += ");\n}";
1298     unpack_to_func += "}\n";
1299 
1300     obj_api_unpack_func = unpack_func + "\n\n" + unpack_to_func;
1301   }
1302 
CanCreateFactoryMethod(const StructDef & struct_def)1303   static bool CanCreateFactoryMethod(const StructDef &struct_def) {
1304     // to preserve backwards compatibility, we allow the first field to be a
1305     // struct
1306     return struct_def.fields.vec.size() < 2 ||
1307            std::all_of(std::begin(struct_def.fields.vec) + 1,
1308                        std::end(struct_def.fields.vec),
1309                        [](const FieldDef *f) -> bool {
1310                          FLATBUFFERS_ASSERT(f != nullptr);
1311                          return f->value.type.base_type != BASE_TYPE_STRUCT;
1312                        });
1313   }
1314 
1315   // Generate an accessor struct with constructor for a flatbuffers struct.
GenStruct(const Parser & parser,StructDef & struct_def,std::string * code_ptr,import_set & imports)1316   void GenStruct(const Parser &parser, StructDef &struct_def,
1317                  std::string *code_ptr, import_set &imports) {
1318     if (struct_def.generated) return;
1319     std::string &code = *code_ptr;
1320 
1321     // Special case for the root struct, since no one will necessarily reference
1322     // it, we have to explicitly add it to the import list.
1323     if (&struct_def == parser_.root_struct_def_) {
1324       AddImport(imports, struct_def, struct_def);
1325     }
1326 
1327     const std::string object_name = GetTypeName(struct_def);
1328 
1329     // Emit constructor
1330     GenDocComment(struct_def.doc_comment, code_ptr);
1331     code += "export class ";
1332     code += object_name;
1333     code += " {\n";
1334     code += "  bb: flatbuffers.ByteBuffer|null = null;\n";
1335     code += "  bb_pos = 0;\n";
1336 
1337     // Generate the __init method that sets the field in a pre-existing
1338     // accessor object. This is to allow object reuse.
1339     code +=
1340         "  __init(i:number, bb:flatbuffers.ByteBuffer):" + object_name + " {\n";
1341     code += "  this.bb_pos = i;\n";
1342     code += "  this.bb = bb;\n";
1343     code += "  return this;\n";
1344     code += "}\n\n";
1345 
1346     // Generate special accessors for the table that when used as the root of a
1347     // FlatBuffer
1348     GenerateRootAccessor(struct_def, code_ptr, code, object_name, false);
1349     GenerateRootAccessor(struct_def, code_ptr, code, object_name, true);
1350 
1351     // Generate the identifier check method
1352     if (!struct_def.fixed && parser_.root_struct_def_ == &struct_def &&
1353         !parser_.file_identifier_.empty()) {
1354       GenDocComment(code_ptr);
1355       code +=
1356           "static bufferHasIdentifier(bb:flatbuffers.ByteBuffer):boolean "
1357           "{\n";
1358       code += "  return bb.__has_identifier('" + parser_.file_identifier_;
1359       code += "');\n}\n\n";
1360     }
1361 
1362     // Emit field accessors
1363     for (auto it = struct_def.fields.vec.begin();
1364          it != struct_def.fields.vec.end(); ++it) {
1365       auto &field = **it;
1366       if (field.deprecated) continue;
1367       auto offset_prefix =
1368           "  const offset = " + GenBBAccess() + ".__offset(this.bb_pos, " +
1369           NumToString(field.value.offset) + ");\n  return offset ? ";
1370 
1371       // Emit a scalar field
1372       const auto is_string = IsString(field.value.type);
1373       if (IsScalar(field.value.type.base_type) || is_string) {
1374         const auto has_null_default = is_string || HasNullDefault(field);
1375 
1376         GenDocComment(field.doc_comment, code_ptr);
1377         std::string prefix = ConvertCase(field.name, Case::kLowerCamel) + "(";
1378         if (is_string) {
1379           code += prefix + "):string|null\n";
1380           code +=
1381               prefix + "optionalEncoding:flatbuffers.Encoding" + "):" +
1382               GenTypeName(imports, struct_def, field.value.type, false, true) +
1383               "\n";
1384           code += prefix + "optionalEncoding?:any";
1385         } else {
1386           code += prefix;
1387         }
1388         if (field.value.type.enum_def) {
1389           code += "):" +
1390                   GenTypeName(imports, struct_def, field.value.type, false,
1391                               field.IsOptional()) +
1392                   " {\n";
1393         } else {
1394           code += "):" +
1395                   GenTypeName(imports, struct_def, field.value.type, false,
1396                               has_null_default) +
1397                   " {\n";
1398         }
1399 
1400         if (struct_def.fixed) {
1401           code +=
1402               "  return " +
1403               GenGetter(field.value.type,
1404                         "(this.bb_pos" + MaybeAdd(field.value.offset) + ")") +
1405               ";\n";
1406         } else {
1407           std::string index = "this.bb_pos + offset";
1408           if (is_string) { index += ", optionalEncoding"; }
1409           code += offset_prefix +
1410                   GenGetter(field.value.type, "(" + index + ")") + " : " +
1411                   GenDefaultValue(field, imports);
1412           code += ";\n";
1413         }
1414       }
1415 
1416       // Emit an object field
1417       else {
1418         switch (field.value.type.base_type) {
1419           case BASE_TYPE_STRUCT: {
1420             const auto type =
1421                 AddImport(imports, struct_def, *field.value.type.struct_def)
1422                     .name;
1423             GenDocComment(field.doc_comment, code_ptr);
1424             code += ConvertCase(field.name, Case::kLowerCamel);
1425             code += "(obj?:" + type + "):" + type + "|null {\n";
1426 
1427             if (struct_def.fixed) {
1428               code += "  return (obj || " + GenerateNewExpression(type);
1429               code += ").__init(this.bb_pos";
1430               code +=
1431                   MaybeAdd(field.value.offset) + ", " + GenBBAccess() + ");\n";
1432             } else {
1433               code += offset_prefix + "(obj || " + GenerateNewExpression(type) +
1434                       ").__init(";
1435               code += field.value.type.struct_def->fixed
1436                           ? "this.bb_pos + offset"
1437                           : GenBBAccess() + ".__indirect(this.bb_pos + offset)";
1438               code += ", " + GenBBAccess() + ") : null;\n";
1439             }
1440 
1441             break;
1442           }
1443 
1444           case BASE_TYPE_VECTOR: {
1445             auto vectortype = field.value.type.VectorType();
1446             auto vectortypename =
1447                 GenTypeName(imports, struct_def, vectortype, false);
1448             auto inline_size = InlineSize(vectortype);
1449             auto index = GenBBAccess() +
1450                          ".__vector(this.bb_pos + offset) + index" +
1451                          MaybeScale(inline_size);
1452             std::string ret_type;
1453             bool is_union = false;
1454             switch (vectortype.base_type) {
1455               case BASE_TYPE_STRUCT: ret_type = vectortypename; break;
1456               case BASE_TYPE_STRING: ret_type = vectortypename; break;
1457               case BASE_TYPE_UNION:
1458                 ret_type = "?flatbuffers.Table";
1459                 is_union = true;
1460                 break;
1461               default: ret_type = vectortypename;
1462             }
1463             GenDocComment(field.doc_comment, code_ptr);
1464             std::string prefix = ConvertCase(field.name, Case::kLowerCamel);
1465             // TODO: make it work without any
1466             // if (is_union) { prefix += "<T extends flatbuffers.Table>"; }
1467             if (is_union) { prefix += ""; }
1468             prefix += "(index: number";
1469             if (is_union) {
1470               const auto union_type =
1471                   GenUnionGenericTypeTS(*(field.value.type.enum_def));
1472 
1473               vectortypename = union_type;
1474               code += prefix + ", obj:" + union_type;
1475             } else if (vectortype.base_type == BASE_TYPE_STRUCT) {
1476               code += prefix + ", obj?:" + vectortypename;
1477             } else if (IsString(vectortype)) {
1478               code += prefix + "):string\n";
1479               code += prefix + ",optionalEncoding:flatbuffers.Encoding" +
1480                       "):" + vectortypename + "\n";
1481               code += prefix + ",optionalEncoding?:any";
1482             } else {
1483               code += prefix;
1484             }
1485             code += "):" + vectortypename + "|null {\n";
1486 
1487             if (vectortype.base_type == BASE_TYPE_STRUCT) {
1488               code += offset_prefix + "(obj || " +
1489                       GenerateNewExpression(vectortypename);
1490               code += ").__init(";
1491               code += vectortype.struct_def->fixed
1492                           ? index
1493                           : GenBBAccess() + ".__indirect(" + index + ")";
1494               code += ", " + GenBBAccess() + ")";
1495             } else {
1496               if (is_union) {
1497                 index = "obj, " + index;
1498               } else if (IsString(vectortype)) {
1499                 index += ", optionalEncoding";
1500               }
1501               code += offset_prefix + GenGetter(vectortype, "(" + index + ")");
1502             }
1503             code += " : ";
1504             if (field.value.type.element == BASE_TYPE_BOOL) {
1505               code += "false";
1506             } else if (field.value.type.element == BASE_TYPE_LONG ||
1507                        field.value.type.element == BASE_TYPE_ULONG) {
1508               code += "BigInt(0)";
1509             } else if (IsScalar(field.value.type.element)) {
1510               if (field.value.type.enum_def) {
1511                 code += field.value.constant;
1512               } else {
1513                 code += "0";
1514               }
1515             } else {
1516               code += "null";
1517             }
1518             code += ";\n";
1519             break;
1520           }
1521 
1522           case BASE_TYPE_UNION: {
1523             GenDocComment(field.doc_comment, code_ptr);
1524             code += ConvertCase(field.name, Case::kLowerCamel);
1525 
1526             const auto &union_enum = *(field.value.type.enum_def);
1527             const auto union_type = GenUnionGenericTypeTS(union_enum);
1528             code += "<T extends flatbuffers.Table>(obj:" + union_type +
1529                     "):" + union_type +
1530                     "|null "
1531                     "{\n";
1532 
1533             code += offset_prefix +
1534                     GenGetter(field.value.type, "(obj, this.bb_pos + offset)") +
1535                     " : null;\n";
1536             break;
1537           }
1538           default: FLATBUFFERS_ASSERT(0);
1539         }
1540       }
1541       code += "}\n\n";
1542 
1543       // Adds the mutable scalar value to the output
1544       if (IsScalar(field.value.type.base_type) && parser.opts.mutable_buffer &&
1545           !IsUnion(field.value.type)) {
1546         std::string type =
1547             GenTypeName(imports, struct_def, field.value.type, true);
1548 
1549         code += "mutate_" + field.name + "(value:" + type + "):boolean {\n";
1550 
1551         if (struct_def.fixed) {
1552           code += "  " + GenBBAccess() + ".write" +
1553                   ConvertCase(GenType(field.value.type), Case::kUpperCamel) +
1554                   "(this.bb_pos + " + NumToString(field.value.offset) + ", ";
1555         } else {
1556           code += "  const offset = " + GenBBAccess() +
1557                   ".__offset(this.bb_pos, " + NumToString(field.value.offset) +
1558                   ");\n\n";
1559           code += "  if (offset === 0) {\n";
1560           code += "    return false;\n";
1561           code += "  }\n\n";
1562 
1563           // special case for bools, which are treated as uint8
1564           code += "  " + GenBBAccess() + ".write" +
1565                   ConvertCase(GenType(field.value.type), Case::kUpperCamel) +
1566                   "(this.bb_pos + offset, ";
1567           if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1568         }
1569 
1570         code += "value);\n";
1571         code += "  return true;\n";
1572         code += "}\n\n";
1573       }
1574 
1575       // Emit vector helpers
1576       if (IsVector(field.value.type)) {
1577         // Emit a length helper
1578         GenDocComment(code_ptr);
1579         code += ConvertCase(field.name, Case::kLowerCamel);
1580         code += "Length():number {\n" + offset_prefix;
1581 
1582         code +=
1583             GenBBAccess() + ".__vector_len(this.bb_pos + offset) : 0;\n}\n\n";
1584 
1585         // For scalar types, emit a typed array helper
1586         auto vectorType = field.value.type.VectorType();
1587         if (IsScalar(vectorType.base_type) && !IsLong(vectorType.base_type)) {
1588           GenDocComment(code_ptr);
1589 
1590           code += ConvertCase(field.name, Case::kLowerCamel);
1591           code += "Array():" + GenType(vectorType) + "Array|null {\n" +
1592                   offset_prefix;
1593 
1594           code += "new " + GenType(vectorType) + "Array(" + GenBBAccess() +
1595                   ".bytes().buffer, " + GenBBAccess() +
1596                   ".bytes().byteOffset + " + GenBBAccess() +
1597                   ".__vector(this.bb_pos + offset), " + GenBBAccess() +
1598                   ".__vector_len(this.bb_pos + offset)) : null;\n}\n\n";
1599         }
1600       }
1601     }
1602 
1603     // Emit the fully qualified name
1604     if (parser_.opts.generate_name_strings) {
1605       GenDocComment(code_ptr);
1606       code += "static getFullyQualifiedName():string {\n";
1607       code += "  return '" + WrapInNameSpace(struct_def) + "';\n";
1608       code += "}\n\n";
1609     }
1610 
1611     // Emit the size of the struct.
1612     if (struct_def.fixed) {
1613       GenDocComment(code_ptr);
1614       code += "static sizeOf():number {\n";
1615       code += "  return " + NumToString(struct_def.bytesize) + ";\n";
1616       code += "}\n\n";
1617     }
1618 
1619     // Emit a factory constructor
1620     if (struct_def.fixed) {
1621       std::string arguments;
1622       GenStructArgs(imports, struct_def, &arguments, "");
1623       GenDocComment(code_ptr);
1624 
1625       code += "static create" + GetPrefixedName(struct_def) +
1626               "(builder:flatbuffers.Builder";
1627       code += arguments + "):flatbuffers.Offset {\n";
1628 
1629       GenStructBody(struct_def, &code, "");
1630       code += "  return builder.offset();\n}\n\n";
1631     } else {
1632       // Generate a method to start building a new object
1633       GenDocComment(code_ptr);
1634 
1635       code += "static start" + GetPrefixedName(struct_def) +
1636               "(builder:flatbuffers.Builder) {\n";
1637 
1638       code += "  builder.startObject(" +
1639               NumToString(struct_def.fields.vec.size()) + ");\n";
1640       code += "}\n\n";
1641 
1642       // Generate a set of static methods that allow table construction
1643       for (auto it = struct_def.fields.vec.begin();
1644            it != struct_def.fields.vec.end(); ++it) {
1645         auto &field = **it;
1646         if (field.deprecated) continue;
1647         const auto argname = GetArgName(field);
1648 
1649         // Generate the field insertion method
1650         GenDocComment(code_ptr);
1651         code += "static add" + ConvertCase(field.name, Case::kUpperCamel);
1652         code += "(builder:flatbuffers.Builder, " + argname + ":" +
1653                 GetArgType(imports, struct_def, field, false) + ") {\n";
1654         code += "  builder.addField" + GenWriteMethod(field.value.type) + "(";
1655         code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
1656         if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1657         code += argname + ", ";
1658         if (!IsScalar(field.value.type.base_type)) {
1659           code += "0";
1660         } else if (HasNullDefault(field)) {
1661           if (IsLong(field.value.type.base_type)) {
1662             code += "BigInt(0)";
1663           } else {
1664             code += "0";
1665           }
1666         } else {
1667           if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1668           code += GenDefaultValue(field, imports);
1669         }
1670         code += ");\n}\n\n";
1671 
1672         if (IsVector(field.value.type)) {
1673           auto vector_type = field.value.type.VectorType();
1674           auto alignment = InlineAlignment(vector_type);
1675           auto elem_size = InlineSize(vector_type);
1676 
1677           // Generate a method to create a vector from a JavaScript array
1678           if (!IsStruct(vector_type)) {
1679             GenDocComment(code_ptr);
1680 
1681             const std::string sig_begin =
1682                 "static create" + ConvertCase(field.name, Case::kUpperCamel) +
1683                 "Vector(builder:flatbuffers.Builder, data:";
1684             const std::string sig_end = "):flatbuffers.Offset";
1685             std::string type =
1686                 GenTypeName(imports, struct_def, vector_type, true) + "[]";
1687             if (type == "number[]") {
1688               const auto &array_type = GenType(vector_type);
1689               // the old type should be deprecated in the future
1690               std::string type_old = "number[]|Uint8Array";
1691               std::string type_new = "number[]|" + array_type + "Array";
1692               if (type_old == type_new) {
1693                 type = type_new;
1694               } else {
1695                 // add function overloads
1696                 code += sig_begin + type_new + sig_end + ";\n";
1697                 code +=
1698                     "/**\n * @deprecated This Uint8Array overload will "
1699                     "be removed in the future.\n */\n";
1700                 code += sig_begin + type_old + sig_end + ";\n";
1701                 type = type_new + "|Uint8Array";
1702               }
1703             }
1704             code += sig_begin + type + sig_end + " {\n";
1705             code += "  builder.startVector(" + NumToString(elem_size);
1706             code += ", data.length, " + NumToString(alignment) + ");\n";
1707             code += "  for (let i = data.length - 1; i >= 0; i--) {\n";
1708             code += "    builder.add" + GenWriteMethod(vector_type) + "(";
1709             if (vector_type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1710             code += "data[i]!);\n";
1711             code += "  }\n";
1712             code += "  return builder.endVector();\n";
1713             code += "}\n\n";
1714           }
1715 
1716           // Generate a method to start a vector, data to be added manually
1717           // after
1718           GenDocComment(code_ptr);
1719 
1720           code += "static start" + ConvertCase(field.name, Case::kUpperCamel);
1721           code += "Vector(builder:flatbuffers.Builder, numElems:number) {\n";
1722           code += "  builder.startVector(" + NumToString(elem_size);
1723           code += ", numElems, " + NumToString(alignment) + ");\n";
1724           code += "}\n\n";
1725         }
1726       }
1727 
1728       // Generate a method to stop building a new object
1729       GenDocComment(code_ptr);
1730 
1731       code += "static end" + GetPrefixedName(struct_def);
1732       code += "(builder:flatbuffers.Builder):flatbuffers.Offset {\n";
1733 
1734       code += "  const offset = builder.endObject();\n";
1735       for (auto it = struct_def.fields.vec.begin();
1736            it != struct_def.fields.vec.end(); ++it) {
1737         auto &field = **it;
1738         if (!field.deprecated && field.IsRequired()) {
1739           code += "  builder.requiredField(offset, ";
1740           code += NumToString(field.value.offset);
1741           code += ") // " + field.name + "\n";
1742         }
1743       }
1744       code += "  return offset;\n";
1745       code += "}\n\n";
1746 
1747       // Generate the methods to complete buffer construction
1748       GenerateFinisher(struct_def, code_ptr, code, false);
1749       GenerateFinisher(struct_def, code_ptr, code, true);
1750 
1751       // Generate a convenient CreateX function
1752       if (CanCreateFactoryMethod(struct_def)) {
1753         code += "static create" + GetPrefixedName(struct_def);
1754         code += "(builder:flatbuffers.Builder";
1755         for (auto it = struct_def.fields.vec.begin();
1756              it != struct_def.fields.vec.end(); ++it) {
1757           const auto &field = **it;
1758           if (field.deprecated) continue;
1759           code += ", " + GetArgName(field) + ":" +
1760                   GetArgType(imports, struct_def, field, true);
1761         }
1762 
1763         code += "):flatbuffers.Offset {\n";
1764         code += "  " + object_name + ".start" + GetPrefixedName(struct_def) +
1765                 "(builder);\n";
1766 
1767         std::string methodPrefix = object_name;
1768         for (auto it = struct_def.fields.vec.begin();
1769              it != struct_def.fields.vec.end(); ++it) {
1770           const auto &field = **it;
1771           if (field.deprecated) continue;
1772 
1773           const auto arg_name = GetArgName(field);
1774 
1775           if (field.IsScalarOptional()) {
1776             code += "  if (" + arg_name + " !== null)\n  ";
1777           }
1778 
1779           code += "  " + methodPrefix + ".add" +
1780                   ConvertCase(field.name, Case::kUpperCamel) + "(";
1781           code += "builder, " + arg_name + ");\n";
1782         }
1783 
1784         code += "  return " + methodPrefix + ".end" +
1785                 GetPrefixedName(struct_def) + "(builder);\n";
1786         code += "}\n";
1787       }
1788     }
1789 
1790     if (!struct_def.fixed && parser_.services_.vec.size() != 0) {
1791       auto name = GetPrefixedName(struct_def, "");
1792       code += "\n";
1793       code += "serialize():Uint8Array {\n";
1794       code += "  return this.bb!.bytes();\n";
1795       code += "}\n";
1796 
1797       code += "\n";
1798       code += "static deserialize(buffer: Uint8Array):" + EscapeKeyword(name) +
1799               " {\n";
1800       code += "  return " + AddImport(imports, struct_def, struct_def).name +
1801               ".getRootAs" + name + "(new flatbuffers.ByteBuffer(buffer))\n";
1802       code += "}\n";
1803     }
1804 
1805     if (parser_.opts.generate_object_based_api) {
1806       std::string obj_api_class;
1807       std::string obj_api_unpack_func;
1808       GenObjApi(parser_, struct_def, obj_api_unpack_func, obj_api_class,
1809                 imports);
1810 
1811       code += obj_api_unpack_func + "}\n" + obj_api_class;
1812     } else {
1813       code += "}\n";
1814     }
1815   }
1816 
HasNullDefault(const FieldDef & field)1817   static bool HasNullDefault(const FieldDef &field) {
1818     return field.IsOptional() && field.value.constant == "null";
1819   }
1820 
GetArgType(import_set & imports,const Definition & owner,const FieldDef & field,bool allowNull)1821   std::string GetArgType(import_set &imports, const Definition &owner,
1822                          const FieldDef &field, bool allowNull) {
1823     return GenTypeName(imports, owner, field.value.type, true,
1824                        allowNull && field.IsOptional());
1825   }
1826 
GetArgName(const FieldDef & field)1827   std::string GetArgName(const FieldDef &field) {
1828     auto argname = ConvertCase(field.name, Case::kLowerCamel);
1829     if (!IsScalar(field.value.type.base_type)) {
1830       argname += "Offset";
1831     } else {
1832       argname = EscapeKeyword(argname);
1833     }
1834     return argname;
1835   }
1836 
GetPrefixedName(const StructDef & struct_def,const char * prefix="")1837   std::string GetPrefixedName(const StructDef &struct_def,
1838                               const char *prefix = "") {
1839     return prefix + struct_def.name;
1840   }
1841 };  // namespace ts
1842 }  // namespace ts
1843 
GenerateTS(const Parser & parser,const std::string & path,const std::string & file_name)1844 bool GenerateTS(const Parser &parser, const std::string &path,
1845                 const std::string &file_name) {
1846   ts::TsGenerator generator(parser, path, file_name);
1847   return generator.generate();
1848 }
1849 
TSMakeRule(const Parser & parser,const std::string & path,const std::string & file_name)1850 std::string TSMakeRule(const Parser &parser, const std::string &path,
1851                        const std::string &file_name) {
1852   std::string filebase =
1853       flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
1854   ts::TsGenerator generator(parser, path, file_name);
1855   std::string make_rule =
1856       generator.GeneratedFileName(path, filebase, parser.opts) + ": ";
1857 
1858   auto included_files = parser.GetIncludedFilesRecursive(file_name);
1859   for (auto it = included_files.begin(); it != included_files.end(); ++it) {
1860     make_rule += " " + *it;
1861   }
1862   return make_rule;
1863 }
1864 
1865 }  // namespace flatbuffers
1866