blob: 9fd1203f588099adb305c57871e659638e183c41 [file] [log] [blame]
James Kuszmaul8e62b022022-03-22 09:33:25 -07001/*
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
James Kuszmaul8e62b022022-03-22 09:33:25 -070017#include <algorithm>
18#include <cassert>
James Kuszmaul3b15b0c2022-11-08 14:03:16 -080019#include <cmath>
James Kuszmaul8e62b022022-03-22 09:33:25 -070020#include <unordered_map>
21#include <unordered_set>
22
23#include "flatbuffers/code_generators.h"
24#include "flatbuffers/flatbuffers.h"
25#include "flatbuffers/idl.h"
26#include "flatbuffers/util.h"
Austin Schuh2dd86a92022-09-14 21:19:23 -070027#include "idl_namer.h"
James Kuszmaul8e62b022022-03-22 09:33:25 -070028
29namespace flatbuffers {
Austin Schuh2dd86a92022-09-14 21:19:23 -070030namespace {
James Kuszmaul8e62b022022-03-22 09:33:25 -070031struct ImportDefinition {
32 std::string name;
33 std::string import_statement;
34 std::string export_statement;
35 std::string bare_file_path;
36 std::string rel_file_path;
Austin Schuh2dd86a92022-09-14 21:19:23 -070037 std::string object_name;
James Kuszmaul8e62b022022-03-22 09:33:25 -070038 const Definition *dependent = nullptr;
39 const Definition *dependency = nullptr;
40};
41
Austin Schuh2dd86a92022-09-14 21:19:23 -070042Namer::Config TypeScriptDefaultConfig() {
43 return { /*types=*/Case::kKeep,
44 /*constants=*/Case::kUnknown,
45 /*methods=*/Case::kLowerCamel,
46 /*functions=*/Case::kLowerCamel,
47 /*fields=*/Case::kLowerCamel,
48 /*variables=*/Case::kLowerCamel,
49 /*variants=*/Case::kKeep,
50 /*enum_variant_seperator=*/"::",
51 /*escape_keywords=*/Namer::Config::Escape::AfterConvertingCase,
52 /*namespaces=*/Case::kKeep,
53 /*namespace_seperator=*/"_",
54 /*object_prefix=*/"",
55 /*object_suffix=*/"T",
56 /*keyword_prefix=*/"",
57 /*keyword_suffix=*/"_",
58 /*filenames=*/Case::kDasher,
59 /*directories=*/Case::kDasher,
60 /*output_path=*/"",
61 /*filename_suffix=*/"_generated",
62 /*filename_extension=*/".ts" };
63}
64
65std::set<std::string> TypescriptKeywords() {
66 // List of keywords retrieved from here:
67 // https://github.com/microsoft/TypeScript/issues/2536
68 return {
69 "arguments", "break", "case", "catch", "class", "const",
70 "continue", "debugger", "default", "delete", "do", "else",
71 "enum", "export", "extends", "false", "finally", "for",
72 "function", "if", "import", "in", "instanceof", "new",
73 "null", "Object", "return", "super", "switch", "this",
74 "throw", "true", "try", "typeof", "var", "void",
75 "while", "with", "as", "implements", "interface", "let",
76 "package", "private", "protected", "public", "static", "yield",
77 };
78}
79
James Kuszmaul8e62b022022-03-22 09:33:25 -070080enum AnnotationType { kParam = 0, kType = 1, kReturns = 2 };
81
Austin Schuh2dd86a92022-09-14 21:19:23 -070082template<typename T> struct SupportsObjectAPI : std::false_type {};
83
84template<> struct SupportsObjectAPI<StructDef> : std::true_type {};
85
86} // namespace
87
James Kuszmaul8e62b022022-03-22 09:33:25 -070088namespace ts {
89// Iterate through all definitions we haven't generate code for (enums, structs,
90// and tables) and output them to a single file.
91class TsGenerator : public BaseGenerator {
92 public:
93 typedef std::map<std::string, ImportDefinition> import_set;
94
95 TsGenerator(const Parser &parser, const std::string &path,
96 const std::string &file_name)
Austin Schuh2dd86a92022-09-14 21:19:23 -070097 : BaseGenerator(parser, path, file_name, "", "_", "ts"),
98 namer_(WithFlagOptions(TypeScriptDefaultConfig(), parser.opts, path),
99 TypescriptKeywords()) {}
James Kuszmaul8e62b022022-03-22 09:33:25 -0700100
James Kuszmaul8e62b022022-03-22 09:33:25 -0700101 bool generate() {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700102 generateEnums();
103 generateStructs();
104 generateEntry();
105 return true;
106 }
107
Austin Schuh2dd86a92022-09-14 21:19:23 -0700108 bool IncludeNamespace() const {
109 // When generating a single flat file and all its includes, namespaces are
110 // important to avoid type name clashes.
111 return parser_.opts.ts_flat_file && parser_.opts.generate_all;
112 }
113
114 std::string GetTypeName(const EnumDef &def, const bool = false,
115 const bool force_ns_wrap = false) {
116 if (IncludeNamespace() || force_ns_wrap) {
117 return namer_.NamespacedType(def);
118 }
119 return namer_.Type(def);
120 }
121
122 std::string GetTypeName(const StructDef &def, const bool object_api = false,
123 const bool force_ns_wrap = false) {
124 if (object_api && parser_.opts.generate_object_based_api) {
125 if (IncludeNamespace() || force_ns_wrap) {
126 return namer_.NamespacedObjectType(def);
127 } else {
128 return namer_.ObjectType(def);
129 }
130 } else {
131 if (IncludeNamespace() || force_ns_wrap) {
132 return namer_.NamespacedType(def);
133 } else {
134 return namer_.Type(def);
135 }
136 }
137 }
138
James Kuszmaul8e62b022022-03-22 09:33:25 -0700139 // Save out the generated code for a single class while adding
140 // declaration boilerplate.
Austin Schuh2dd86a92022-09-14 21:19:23 -0700141 bool SaveType(const Definition &definition, const std::string &class_code,
James Kuszmaul8e62b022022-03-22 09:33:25 -0700142 import_set &imports, import_set &bare_imports) {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700143 if (!class_code.length()) return true;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700144
145 std::string code;
146
147 if (!parser_.opts.ts_flat_file) {
148 code += "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
149
150 for (auto it = bare_imports.begin(); it != bare_imports.end(); it++) {
151 code += it->second.import_statement + "\n";
152 }
153 if (!bare_imports.empty()) code += "\n";
154
155 for (auto it = imports.begin(); it != imports.end(); it++) {
156 if (it->second.dependency != &definition) {
157 code += it->second.import_statement + "\n";
158 }
159 }
160 if (!imports.empty()) code += "\n\n";
161 }
162
Austin Schuh2dd86a92022-09-14 21:19:23 -0700163 code += class_code;
164
James Kuszmaul8e62b022022-03-22 09:33:25 -0700165 if (parser_.opts.ts_flat_file) {
166 flat_file_ += code;
Austin Schuh2dd86a92022-09-14 21:19:23 -0700167 flat_file_ += "\n";
James Kuszmaul8e62b022022-03-22 09:33:25 -0700168 flat_file_definitions_.insert(&definition);
169 return true;
170 } else {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700171 auto dirs = namer_.Directories(*definition.defined_namespace);
172 EnsureDirExists(dirs);
173 auto basename = dirs + namer_.File(definition, SkipFile::Suffix);
174
175 return SaveFile(basename.c_str(), code, false);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700176 }
177 }
178
179 private:
Austin Schuh2dd86a92022-09-14 21:19:23 -0700180 IdlNamer namer_;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700181
182 import_set imports_all_;
183
184 // The following three members are used when generating typescript code into a
185 // single file rather than creating separate files for each type.
186
187 // flat_file_ contains the aggregated contents of the file prior to being
188 // written to disk.
189 std::string flat_file_;
190 // flat_file_definitions_ tracks which types have been written to flat_file_.
191 std::unordered_set<const Definition *> flat_file_definitions_;
192 // This maps from import names to types to import.
193 std::map<std::string, std::map<std::string, std::string>>
194 flat_file_import_declarations_;
Austin Schuh2dd86a92022-09-14 21:19:23 -0700195 // For flat file codegen, tracks whether we need to import the flatbuffers
196 // library itself (not necessary for files that solely consist of enum
197 // definitions).
198 bool import_flatbuffers_lib_ = false;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700199
200 // Generate code for all enums.
201 void generateEnums() {
202 for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
203 ++it) {
204 import_set bare_imports;
205 import_set imports;
206 std::string enumcode;
207 auto &enum_def = **it;
208 GenEnum(enum_def, &enumcode, imports, false);
209 GenEnum(enum_def, &enumcode, imports, true);
210 SaveType(enum_def, enumcode, imports, bare_imports);
211 imports_all_.insert(imports.begin(), imports.end());
212 }
213 }
214
215 // Generate code for all structs.
216 void generateStructs() {
217 for (auto it = parser_.structs_.vec.begin();
218 it != parser_.structs_.vec.end(); ++it) {
219 import_set bare_imports;
220 import_set imports;
221 AddImport(bare_imports, "* as flatbuffers", "flatbuffers");
Austin Schuh2dd86a92022-09-14 21:19:23 -0700222 import_flatbuffers_lib_ = true;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700223 auto &struct_def = **it;
224 std::string declcode;
225 GenStruct(parser_, struct_def, &declcode, imports);
226 SaveType(struct_def, declcode, imports, bare_imports);
227 imports_all_.insert(imports.begin(), imports.end());
228 }
229 }
230
231 // Generate code for a single entry point module.
232 void generateEntry() {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700233 std::string code =
234 "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
James Kuszmaul8e62b022022-03-22 09:33:25 -0700235 if (parser_.opts.ts_flat_file) {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700236 if (import_flatbuffers_lib_) {
237 code += "import * as flatbuffers from 'flatbuffers';\n";
238 code += "\n";
James Kuszmaul8e62b022022-03-22 09:33:25 -0700239 }
Austin Schuh2dd86a92022-09-14 21:19:23 -0700240 // Only include import statements when not generating all.
241 if (!parser_.opts.generate_all) {
242 for (const auto &it : flat_file_import_declarations_) {
243 // Note that we do end up generating an import for ourselves, which
244 // should generally be harmless.
245 // TODO: Make it so we don't generate a self-import; this will also
246 // require modifying AddImport to ensure that we don't use
247 // namespace-prefixed names anywhere...
248 std::string file = it.first;
249 if (file.empty()) { continue; }
250 std::string noext = flatbuffers::StripExtension(file);
251 std::string basename = flatbuffers::StripPath(noext);
252 std::string include_file = GeneratedFileName(
253 parser_.opts.include_prefix,
254 parser_.opts.keep_prefix ? noext : basename, parser_.opts);
255 // TODO: what is the right behavior when different include flags are
256 // specified here? Should we always be adding the "./" for a relative
257 // path or turn it off if --include-prefix is specified, or something
258 // else?
259 std::string include_name =
260 "./" + flatbuffers::StripExtension(include_file);
261 code += "import {";
262 for (const auto &pair : it.second) {
263 code += namer_.EscapeKeyword(pair.first) + " as " +
264 namer_.EscapeKeyword(pair.second) + ", ";
265 }
266 code.resize(code.size() - 2);
267 code += "} from '" + include_name + ".js';\n";
268 }
269 code += "\n";
270 }
271
James Kuszmaul8e62b022022-03-22 09:33:25 -0700272 code += flat_file_;
273 const std::string filename =
274 GeneratedFileName(path_, file_name_, parser_.opts);
275 SaveFile(filename.c_str(), code, false);
276 } else {
277 for (auto it = imports_all_.begin(); it != imports_all_.end(); it++) {
278 code += it->second.export_statement + "\n";
279 }
Austin Schuh2dd86a92022-09-14 21:19:23 -0700280 const std::string path =
281 GeneratedFileName(path_, file_name_, parser_.opts);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700282 SaveFile(path.c_str(), code, false);
283 }
284 }
285
286 // Generate a documentation comment, if available.
287 static void GenDocComment(const std::vector<std::string> &dc,
288 std::string *code_ptr,
289 const char *indent = nullptr) {
290 if (dc.empty()) {
291 // Don't output empty comment blocks with 0 lines of comment content.
292 return;
293 }
294
295 std::string &code = *code_ptr;
296 if (indent) code += indent;
297 code += "/**\n";
298 for (auto it = dc.begin(); it != dc.end(); ++it) {
299 if (indent) code += indent;
300 code += " *" + *it + "\n";
301 }
302 if (indent) code += indent;
303 code += " */\n";
304 }
305
306 static void GenDocComment(std::string *code_ptr) {
307 GenDocComment(std::vector<std::string>(), code_ptr);
308 }
309
310 // Generate an enum declaration and an enum string lookup table.
311 void GenEnum(EnumDef &enum_def, std::string *code_ptr, import_set &imports,
312 bool reverse) {
313 if (enum_def.generated) return;
314 if (reverse) return; // FIXME.
315 std::string &code = *code_ptr;
316 GenDocComment(enum_def.doc_comment, code_ptr);
Austin Schuh2dd86a92022-09-14 21:19:23 -0700317 code += "export enum ";
318 code += GetTypeName(enum_def);
319 code += " {\n";
James Kuszmaul8e62b022022-03-22 09:33:25 -0700320 for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
321 auto &ev = **it;
322 if (!ev.doc_comment.empty()) {
323 if (it != enum_def.Vals().begin()) { code += '\n'; }
324 GenDocComment(ev.doc_comment, code_ptr, " ");
325 }
326
James Kuszmaul8e62b022022-03-22 09:33:25 -0700327 // Generate mapping between EnumName: EnumValue(int)
328 if (reverse) {
329 code += " '" + enum_def.ToString(ev) + "'";
330 code += " = ";
Austin Schuh2dd86a92022-09-14 21:19:23 -0700331 code += "'" + namer_.Variant(ev) + "'";
James Kuszmaul8e62b022022-03-22 09:33:25 -0700332 } else {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700333 code += " " + namer_.Variant(ev);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700334 code += " = ";
335 // Unfortunately, because typescript does not support bigint enums,
336 // for 64-bit enums, we instead map the enum names to strings.
337 switch (enum_def.underlying_type.base_type) {
338 case BASE_TYPE_LONG:
339 case BASE_TYPE_ULONG: {
340 code += "'" + enum_def.ToString(ev) + "'";
341 break;
342 }
343 default: code += enum_def.ToString(ev);
344 }
345 }
346
347 code += (it + 1) != enum_def.Vals().end() ? ",\n" : "\n";
348 }
349 code += "}";
350
351 if (enum_def.is_union) {
352 code += GenUnionConvFunc(enum_def.underlying_type, imports);
353 }
354
Austin Schuh2dd86a92022-09-14 21:19:23 -0700355 code += "\n";
James Kuszmaul8e62b022022-03-22 09:33:25 -0700356 }
357
358 static std::string GenType(const Type &type) {
359 switch (type.base_type) {
360 case BASE_TYPE_BOOL:
361 case BASE_TYPE_CHAR: return "Int8";
362 case BASE_TYPE_UTYPE:
363 case BASE_TYPE_UCHAR: return "Uint8";
364 case BASE_TYPE_SHORT: return "Int16";
365 case BASE_TYPE_USHORT: return "Uint16";
366 case BASE_TYPE_INT: return "Int32";
367 case BASE_TYPE_UINT: return "Uint32";
368 case BASE_TYPE_LONG: return "Int64";
369 case BASE_TYPE_ULONG: return "Uint64";
370 case BASE_TYPE_FLOAT: return "Float32";
371 case BASE_TYPE_DOUBLE: return "Float64";
372 case BASE_TYPE_STRING: return "String";
373 case BASE_TYPE_VECTOR: return GenType(type.VectorType());
374 case BASE_TYPE_STRUCT: return type.struct_def->name;
375 default: return "flatbuffers.Table";
376 }
377 }
378
379 std::string GenGetter(const Type &type, const std::string &arguments) {
380 switch (type.base_type) {
381 case BASE_TYPE_STRING: return GenBBAccess() + ".__string" + arguments;
382 case BASE_TYPE_STRUCT: return GenBBAccess() + ".__struct" + arguments;
383 case BASE_TYPE_UNION:
384 if (!UnionHasStringType(*type.enum_def)) {
385 return GenBBAccess() + ".__union" + arguments;
386 }
387 return GenBBAccess() + ".__union_with_string" + arguments;
388 case BASE_TYPE_VECTOR: return GenGetter(type.VectorType(), arguments);
389 default: {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700390 auto getter = GenBBAccess() + "." +
391 namer_.Method("read_" + GenType(type)) + arguments;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700392 if (type.base_type == BASE_TYPE_BOOL) { getter = "!!" + getter; }
393 return getter;
394 }
395 }
396 }
397
398 std::string GenBBAccess() const { return "this.bb!"; }
399
400 std::string GenDefaultValue(const FieldDef &field, import_set &imports) {
401 if (field.IsScalarOptional()) { return "null"; }
402
403 const auto &value = field.value;
404 if (value.type.enum_def && value.type.base_type != BASE_TYPE_UNION &&
405 value.type.base_type != BASE_TYPE_VECTOR) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700406 switch (value.type.base_type) {
James Kuszmaul3b15b0c2022-11-08 14:03:16 -0800407 case BASE_TYPE_ARRAY: {
408 std::string ret = "[";
409 for (auto i = 0; i < value.type.fixed_length; ++i) {
410 std::string enum_name =
411 AddImport(imports, *value.type.enum_def, *value.type.enum_def)
412 .name;
413 std::string enum_value = namer_.Variant(
414 *value.type.enum_def->FindByValue(value.constant));
415 ret += enum_name + "." + enum_value +
416 (i < value.type.fixed_length - 1 ? ", " : "");
417 }
418 ret += "]";
419 return ret;
420 }
James Kuszmaul8e62b022022-03-22 09:33:25 -0700421 case BASE_TYPE_LONG:
422 case BASE_TYPE_ULONG: {
James Kuszmaul3b15b0c2022-11-08 14:03:16 -0800423 // If the value is an enum with a 64-bit base type, we have to just
424 // return the bigint value directly since typescript does not support
425 // enums with bigint backing types.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700426 return "BigInt('" + value.constant + "')";
427 }
428 default: {
429 if (auto val = value.type.enum_def->FindByValue(value.constant)) {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700430 return AddImport(imports, *value.type.enum_def,
431 *value.type.enum_def)
432 .name +
433 "." + namer_.Variant(*val);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700434 } else {
435 return value.constant;
436 }
437 }
438 }
439 }
440
441 switch (value.type.base_type) {
442 case BASE_TYPE_BOOL: return value.constant == "0" ? "false" : "true";
443
444 case BASE_TYPE_STRING:
445 case BASE_TYPE_UNION:
446 case BASE_TYPE_STRUCT: {
447 return "null";
448 }
449
James Kuszmaul3b15b0c2022-11-08 14:03:16 -0800450 case BASE_TYPE_ARRAY:
James Kuszmaul8e62b022022-03-22 09:33:25 -0700451 case BASE_TYPE_VECTOR: return "[]";
452
453 case BASE_TYPE_LONG:
454 case BASE_TYPE_ULONG: {
455 return "BigInt('" + value.constant + "')";
456 }
457
James Kuszmaul3b15b0c2022-11-08 14:03:16 -0800458 default: {
459 if (StringIsFlatbufferNan(value.constant)) {
460 return "NaN";
461 } else if (StringIsFlatbufferPositiveInfinity(value.constant)) {
462 return "Infinity";
463 } else if (StringIsFlatbufferNegativeInfinity(value.constant)) {
464 return "-Infinity";
465 }
Austin Schuh2dd86a92022-09-14 21:19:23 -0700466 return value.constant;
James Kuszmaul3b15b0c2022-11-08 14:03:16 -0800467 }
James Kuszmaul8e62b022022-03-22 09:33:25 -0700468 }
469 }
470
471 std::string GenTypeName(import_set &imports, const Definition &owner,
472 const Type &type, bool input,
473 bool allowNull = false) {
474 if (!input) {
475 if (IsString(type) || type.base_type == BASE_TYPE_STRUCT) {
476 std::string name;
477 if (IsString(type)) {
478 name = "string|Uint8Array";
479 } else {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700480 name = AddImport(imports, owner, *type.struct_def).name;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700481 }
482 return allowNull ? (name + "|null") : name;
483 }
484 }
485
486 switch (type.base_type) {
487 case BASE_TYPE_BOOL: return allowNull ? "boolean|null" : "boolean";
488 case BASE_TYPE_LONG:
489 case BASE_TYPE_ULONG: return allowNull ? "bigint|null" : "bigint";
James Kuszmaul3b15b0c2022-11-08 14:03:16 -0800490 case BASE_TYPE_ARRAY: {
491 std::string name;
492 if (type.element == BASE_TYPE_LONG || type.element == BASE_TYPE_ULONG) {
493 name = "bigint[]";
494 } else if (type.element != BASE_TYPE_STRUCT) {
495 name = "number[]";
496 } else {
497 name = "any[]";
498 if (parser_.opts.generate_object_based_api) {
499 name = "(any|" +
500 GetTypeName(*type.struct_def, /*object_api =*/true) + ")[]";
501 }
502 }
503
504 return name + (allowNull ? "|null" : "");
505 }
James Kuszmaul8e62b022022-03-22 09:33:25 -0700506 default:
507 if (IsScalar(type.base_type)) {
508 if (type.enum_def) {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700509 const auto enum_name =
510 AddImport(imports, owner, *type.enum_def).name;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700511 return allowNull ? (enum_name + "|null") : enum_name;
512 }
513 return allowNull ? "number|null" : "number";
514 }
515 return "flatbuffers.Offset";
516 }
517 }
518
519 // Returns the method name for use with add/put calls.
Austin Schuh2dd86a92022-09-14 21:19:23 -0700520 std::string GenWriteMethod(const Type &type) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700521 // Forward to signed versions since unsigned versions don't exist
522 switch (type.base_type) {
523 case BASE_TYPE_UTYPE:
524 case BASE_TYPE_UCHAR: return GenWriteMethod(Type(BASE_TYPE_CHAR));
525 case BASE_TYPE_USHORT: return GenWriteMethod(Type(BASE_TYPE_SHORT));
526 case BASE_TYPE_UINT: return GenWriteMethod(Type(BASE_TYPE_INT));
527 case BASE_TYPE_ULONG: return GenWriteMethod(Type(BASE_TYPE_LONG));
528 default: break;
529 }
530
Austin Schuh2dd86a92022-09-14 21:19:23 -0700531 return IsScalar(type.base_type) ? namer_.Type(GenType(type))
532 : (IsStruct(type) ? "Struct" : "Offset");
James Kuszmaul8e62b022022-03-22 09:33:25 -0700533 }
534
535 template<typename T> static std::string MaybeAdd(T value) {
536 return value != 0 ? " + " + NumToString(value) : "";
537 }
538
539 template<typename T> static std::string MaybeScale(T value) {
540 return value != 1 ? " * " + NumToString(value) : "";
541 }
542
543 void GenStructArgs(import_set &imports, const StructDef &struct_def,
544 std::string *arguments, const std::string &nameprefix) {
545 for (auto it = struct_def.fields.vec.begin();
546 it != struct_def.fields.vec.end(); ++it) {
547 auto &field = **it;
548 if (IsStruct(field.value.type)) {
549 // Generate arguments for a struct inside a struct. To ensure names
550 // don't clash, and to make it obvious these arguments are constructing
551 // a nested struct, prefix the name with the field name.
552 GenStructArgs(imports, *field.value.type.struct_def, arguments,
553 nameprefix + field.name + "_");
554 } else {
555 *arguments += ", " + nameprefix + field.name + ": " +
556 GenTypeName(imports, field, field.value.type, true,
557 field.IsOptional());
558 }
559 }
560 }
561
Austin Schuh2dd86a92022-09-14 21:19:23 -0700562 void GenStructBody(const StructDef &struct_def, std::string *body,
563 const std::string &nameprefix) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700564 *body += " builder.prep(";
565 *body += NumToString(struct_def.minalign) + ", ";
566 *body += NumToString(struct_def.bytesize) + ");\n";
567
568 for (auto it = struct_def.fields.vec.rbegin();
569 it != struct_def.fields.vec.rend(); ++it) {
570 auto &field = **it;
571 if (field.padding) {
572 *body += " builder.pad(" + NumToString(field.padding) + ");\n";
573 }
574 if (IsStruct(field.value.type)) {
575 // Generate arguments for a struct inside a struct. To ensure names
576 // don't clash, and to make it obvious these arguments are constructing
577 // a nested struct, prefix the name with the field name.
James Kuszmaul3b15b0c2022-11-08 14:03:16 -0800578 GenStructBody(
579 *field.value.type.struct_def, body,
580 nameprefix.length() ? nameprefix + "_" + field.name : field.name);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700581 } else {
James Kuszmaul3b15b0c2022-11-08 14:03:16 -0800582 auto element_type = field.value.type.element;
583
584 if (field.value.type.base_type == BASE_TYPE_ARRAY) {
585 switch (field.value.type.element) {
586 case BASE_TYPE_STRUCT: {
587 std::string str_last_item_idx =
588 NumToString(field.value.type.fixed_length - 1);
589 *body += "\n for (let i = " + str_last_item_idx +
590 "; i >= 0; --i" + ") {\n";
591
592 std::string fname = nameprefix.length()
593 ? nameprefix + "_" + field.name
594 : field.name;
595
596 *body += " const item = " + fname + "?.[i];\n\n";
597
598 if (parser_.opts.generate_object_based_api) {
599 *body += " if (item instanceof " +
600 GetTypeName(*field.value.type.struct_def,
601 /*object_api =*/true) +
602 ") {\n";
603 *body += " item.pack(builder);\n";
604 *body += " continue;\n";
605 *body += " }\n\n";
606 }
607
608 std::string class_name =
609 GetPrefixedName(*field.value.type.struct_def);
610 std::string pack_func_create_call =
611 class_name + ".create" + class_name + "(builder,\n";
612 pack_func_create_call +=
613 " " +
614 GenStructMemberValueTS(*field.value.type.struct_def, "item",
615 ",\n ", false) +
616 "\n ";
617 *body += " " + pack_func_create_call;
618 *body += " );\n }\n\n";
619
620 break;
621 }
622 default: {
623 std::string str_last_item_idx =
624 NumToString(field.value.type.fixed_length - 1);
625 std::string fname = nameprefix.length()
626 ? nameprefix + "_" + field.name
627 : field.name;
628
629 *body += "\n for (let i = " + str_last_item_idx +
630 "; i >= 0; --i) {\n";
631 *body += " builder.write";
632 *body += GenWriteMethod(
633 static_cast<flatbuffers::Type>(field.value.type.element));
634 *body += "(";
635 *body += element_type == BASE_TYPE_BOOL ? "+" : "";
636
637 if (element_type == BASE_TYPE_LONG ||
638 element_type == BASE_TYPE_ULONG) {
639 *body += "BigInt(" + fname + "?.[i] ?? 0));\n";
640 } else {
641 *body += "(" + fname + "?.[i] ?? 0));\n\n";
642 }
643 *body += " }\n\n";
644 break;
645 }
646 }
647 } else {
648 std::string fname =
649 nameprefix.length() ? nameprefix + "_" + field.name : field.name;
650
651 *body += " builder.write" + GenWriteMethod(field.value.type) + "(";
652 if (field.value.type.base_type == BASE_TYPE_BOOL) {
653 *body += "Number(Boolean(" + fname + ")));\n";
654 continue;
655 } else if (field.value.type.base_type == BASE_TYPE_LONG ||
656 field.value.type.base_type == BASE_TYPE_ULONG) {
657 *body += "BigInt(" + fname + " ?? 0));\n";
658 continue;
659 }
660
661 *body += fname + ");\n";
662 }
James Kuszmaul8e62b022022-03-22 09:33:25 -0700663 }
664 }
665 }
666
667 std::string GenerateNewExpression(const std::string &object_name) {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700668 return "new " + namer_.Type(object_name) + "()";
James Kuszmaul8e62b022022-03-22 09:33:25 -0700669 }
670
671 void GenerateRootAccessor(StructDef &struct_def, std::string *code_ptr,
672 std::string &code, const std::string &object_name,
673 bool size_prefixed) {
674 if (!struct_def.fixed) {
675 GenDocComment(code_ptr);
676 std::string sizePrefixed("SizePrefixed");
677 code += "static get" + (size_prefixed ? sizePrefixed : "") + "Root" +
678 GetPrefixedName(struct_def, "As");
679 code += "(bb:flatbuffers.ByteBuffer, obj?:" + object_name +
680 "):" + object_name + " {\n";
681 if (size_prefixed) {
682 code +=
683 " bb.setPosition(bb.position() + "
684 "flatbuffers.SIZE_PREFIX_LENGTH);\n";
685 }
686 code += " return (obj || " + GenerateNewExpression(object_name);
687 code += ").__init(bb.readInt32(bb.position()) + bb.position(), bb);\n";
688 code += "}\n\n";
689 }
690 }
691
692 void GenerateFinisher(StructDef &struct_def, std::string *code_ptr,
693 std::string &code, bool size_prefixed) {
694 if (parser_.root_struct_def_ == &struct_def) {
695 std::string sizePrefixed("SizePrefixed");
696 GenDocComment(code_ptr);
697
698 code += "static finish" + (size_prefixed ? sizePrefixed : "") +
699 GetPrefixedName(struct_def) + "Buffer";
700 code += "(builder:flatbuffers.Builder, offset:flatbuffers.Offset) {\n";
701 code += " builder.finish(offset";
702 if (!parser_.file_identifier_.empty()) {
703 code += ", '" + parser_.file_identifier_ + "'";
704 }
705 if (size_prefixed) {
706 if (parser_.file_identifier_.empty()) { code += ", undefined"; }
707 code += ", true";
708 }
709 code += ");\n";
710 code += "}\n\n";
711 }
712 }
713
James Kuszmaul8e62b022022-03-22 09:33:25 -0700714 bool UnionHasStringType(const EnumDef &union_enum) {
715 return std::any_of(union_enum.Vals().begin(), union_enum.Vals().end(),
716 [](const EnumVal *ev) {
717 return !ev->IsZero() && IsString(ev->union_type);
718 });
719 }
720
721 std::string GenUnionGenericTypeTS(const EnumDef &union_enum) {
722 // TODO: make it work without any
723 // return std::string("T") + (UnionHasStringType(union_enum) ? "|string" :
724 // "");
725 return std::string("any") +
726 (UnionHasStringType(union_enum) ? "|string" : "");
727 }
728
729 std::string GenUnionTypeTS(const EnumDef &union_enum, import_set &imports) {
730 std::string ret;
731 std::set<std::string> type_list;
732
733 for (auto it = union_enum.Vals().begin(); it != union_enum.Vals().end();
734 ++it) {
735 const auto &ev = **it;
736 if (ev.IsZero()) { continue; }
737
738 std::string type = "";
739 if (IsString(ev.union_type)) {
740 type = "string"; // no need to wrap string type in namespace
741 } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700742 type = AddImport(imports, union_enum, *ev.union_type.struct_def).name;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700743 } else {
744 FLATBUFFERS_ASSERT(false);
745 }
746 type_list.insert(type);
747 }
748
749 for (auto it = type_list.begin(); it != type_list.end(); ++it) {
750 ret += *it + ((std::next(it) == type_list.end()) ? "" : "|");
751 }
752
753 return ret;
754 }
755
Austin Schuh2dd86a92022-09-14 21:19:23 -0700756 static bool CheckIfNameClashes(const import_set &imports,
757 const std::string &name) {
758 // TODO: this would be better as a hashset.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700759 for (auto it = imports.begin(); it != imports.end(); it++) {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700760 if (it->second.name == name) { return true; }
James Kuszmaul8e62b022022-03-22 09:33:25 -0700761 }
Austin Schuh2dd86a92022-09-14 21:19:23 -0700762 return false;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700763 }
764
Austin Schuh2dd86a92022-09-14 21:19:23 -0700765 std::string GenSymbolExpression(const StructDef &struct_def,
766 const bool has_name_clash,
767 const std::string &import_name,
768 const std::string &name,
769 const std::string &object_name) {
770 std::string symbols_expression;
771
772 if (has_name_clash) {
773 // We have a name clash
774 symbols_expression += import_name + " as " + name;
775
776 if (parser_.opts.generate_object_based_api) {
777 symbols_expression += ", " +
778 GetTypeName(struct_def, /*object_api =*/true) +
779 " as " + object_name;
780 }
781 } else {
782 // No name clash, use the provided name
783 symbols_expression += name;
784
785 if (parser_.opts.generate_object_based_api) {
786 symbols_expression += ", " + object_name;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700787 }
788 }
789
Austin Schuh2dd86a92022-09-14 21:19:23 -0700790 return symbols_expression;
791 }
792
793 std::string GenSymbolExpression(const EnumDef &enum_def,
794 const bool has_name_clash,
795 const std::string &import_name,
796 const std::string &name,
797 const std::string &) {
798 std::string symbols_expression;
799 if (has_name_clash) {
800 symbols_expression += import_name + " as " + name;
801 } else {
802 symbols_expression += name;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700803 }
804
Austin Schuh2dd86a92022-09-14 21:19:23 -0700805 if (enum_def.is_union) {
806 symbols_expression += ", unionTo" + name;
807 symbols_expression += ", unionListTo" + name;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700808 }
Austin Schuh2dd86a92022-09-14 21:19:23 -0700809
810 return symbols_expression;
811 }
812
813 template<typename DefinitionT>
814 ImportDefinition AddImport(import_set &imports, const Definition &dependent,
815 const DefinitionT &dependency) {
816 // The unique name of the dependency, fully qualified in its namespace.
817 const std::string unique_name = GetTypeName(
818 dependency, /*object_api = */ false, /*force_ns_wrap=*/true);
819
820 // Look if we have already added this import and return its name if found.
821 const auto import_pair = imports.find(unique_name);
822 if (import_pair != imports.end()) { return import_pair->second; }
823
824 // Check if this name would have a name clash with another type. Just use
825 // the "base" name (properly escaped) without any namespacing applied.
826 const std::string import_name = GetTypeName(dependency);
827 const bool has_name_clash = CheckIfNameClashes(imports, import_name);
828
829 // If we have a name clash, use the unique name, otherwise use simple name.
830 std::string name = has_name_clash ? unique_name : import_name;
831
832 const std::string object_name =
833 GetTypeName(dependency, /*object_api=*/true, has_name_clash);
834
835 if (parser_.opts.ts_flat_file) {
836 // In flat-file generation, do not attempt to import things from ourselves
837 // *and* do not wrap namespaces (note that this does override the logic
838 // above, but since we force all non-self-imports to use namespace-based
839 // names in flat file generation, it's fine).
840 if (dependent.file == dependency.file) {
841 name = import_name;
842 } else {
843 const std::string file =
844 RelativeToRootPath(StripFileName(AbsolutePath(dependent.file)),
845 dependency.file)
846 // Strip the leading //
847 .substr(2);
848 flat_file_import_declarations_[file][import_name] = name;
849
850 if (parser_.opts.generate_object_based_api &&
851 SupportsObjectAPI<DefinitionT>::value) {
852 flat_file_import_declarations_[file][import_name + "T"] = object_name;
853 }
854 }
855 }
856
857 const std::string symbols_expression = GenSymbolExpression(
858 dependency, has_name_clash, import_name, name, object_name);
859
James Kuszmaul8e62b022022-03-22 09:33:25 -0700860 std::string bare_file_path;
861 std::string rel_file_path;
862 const auto &dep_comps = dependent.defined_namespace->components;
Austin Schuh2dd86a92022-09-14 21:19:23 -0700863 for (size_t i = 0; i < dep_comps.size(); i++) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700864 rel_file_path += i == 0 ? ".." : (kPathSeparator + std::string(".."));
Austin Schuh2dd86a92022-09-14 21:19:23 -0700865 }
866 if (dep_comps.size() == 0) { rel_file_path += "."; }
867
James Kuszmaul8e62b022022-03-22 09:33:25 -0700868 bare_file_path +=
869 kPathSeparator +
Austin Schuh2dd86a92022-09-14 21:19:23 -0700870 namer_.Directories(dependency.defined_namespace->components,
871 SkipDir::OutputPath) +
872 namer_.File(dependency, SkipFile::SuffixAndExtension);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700873 rel_file_path += bare_file_path;
Austin Schuh2dd86a92022-09-14 21:19:23 -0700874
James Kuszmaul8e62b022022-03-22 09:33:25 -0700875 ImportDefinition import;
Austin Schuh2dd86a92022-09-14 21:19:23 -0700876 import.name = name;
877 import.object_name = object_name;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700878 import.bare_file_path = bare_file_path;
879 import.rel_file_path = rel_file_path;
James Kuszmaul3b15b0c2022-11-08 14:03:16 -0800880 import.import_statement = "import { " + symbols_expression + " } from '" +
881 rel_file_path + ".js';";
882 import.export_statement = "export { " + symbols_expression + " } from '." +
883 bare_file_path + ".js';";
James Kuszmaul8e62b022022-03-22 09:33:25 -0700884 import.dependency = &dependency;
885 import.dependent = &dependent;
Austin Schuh2dd86a92022-09-14 21:19:23 -0700886
James Kuszmaul8e62b022022-03-22 09:33:25 -0700887 imports.insert(std::make_pair(unique_name, import));
Austin Schuh2dd86a92022-09-14 21:19:23 -0700888
889 return import;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700890 }
891
892 void AddImport(import_set &imports, std::string import_name,
893 std::string fileName) {
894 ImportDefinition import;
895 import.name = import_name;
896 import.import_statement =
897 "import " + import_name + " from '" + fileName + "';";
898 imports.insert(std::make_pair(import_name, import));
899 }
900
901 // Generate a TS union type based on a union's enum
Austin Schuh2dd86a92022-09-14 21:19:23 -0700902 std::string GenObjApiUnionTypeTS(import_set &imports,
903 const StructDef &dependent,
904 const IDLOptions &,
James Kuszmaul8e62b022022-03-22 09:33:25 -0700905 const EnumDef &union_enum) {
906 std::string ret = "";
907 std::set<std::string> type_list;
908
909 for (auto it = union_enum.Vals().begin(); it != union_enum.Vals().end();
910 ++it) {
911 const auto &ev = **it;
912 if (ev.IsZero()) { continue; }
913
914 std::string type = "";
915 if (IsString(ev.union_type)) {
916 type = "string"; // no need to wrap string type in namespace
917 } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700918 type = AddImport(imports, dependent, *ev.union_type.struct_def)
919 .object_name;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700920 } else {
921 FLATBUFFERS_ASSERT(false);
922 }
923 type_list.insert(type);
924 }
925
926 size_t totalPrinted = 0;
927 for (auto it = type_list.begin(); it != type_list.end(); ++it) {
928 ++totalPrinted;
929 ret += *it + ((totalPrinted == type_list.size()) ? "" : "|");
930 }
931
932 return ret;
933 }
934
935 std::string GenUnionConvFuncName(const EnumDef &enum_def) {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700936 return namer_.Function("unionTo", enum_def);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700937 }
938
939 std::string GenUnionListConvFuncName(const EnumDef &enum_def) {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700940 return namer_.Function("unionListTo", enum_def);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700941 }
942
943 std::string GenUnionConvFunc(const Type &union_type, import_set &imports) {
944 if (union_type.enum_def) {
945 const auto &enum_def = *union_type.enum_def;
946
947 const auto valid_union_type = GenUnionTypeTS(enum_def, imports);
948 const auto valid_union_type_with_null = valid_union_type + "|null";
949
950 auto ret = "\n\nexport function " + GenUnionConvFuncName(enum_def) +
Austin Schuh2dd86a92022-09-14 21:19:23 -0700951 "(\n type: " + GetTypeName(enum_def) +
James Kuszmaul8e62b022022-03-22 09:33:25 -0700952 ",\n accessor: (obj:" + valid_union_type + ") => " +
953 valid_union_type_with_null +
954 "\n): " + valid_union_type_with_null + " {\n";
955
Austin Schuh2dd86a92022-09-14 21:19:23 -0700956 const auto enum_type = AddImport(imports, enum_def, enum_def).name;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700957
958 const auto union_enum_loop = [&](const std::string &accessor_str) {
959 ret += " switch(" + enum_type + "[type]) {\n";
960 ret += " case 'NONE': return null; \n";
961
962 for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
963 ++it) {
964 const auto &ev = **it;
965 if (ev.IsZero()) { continue; }
966
Austin Schuh2dd86a92022-09-14 21:19:23 -0700967 ret += " case '" + namer_.Variant(ev) + "': ";
James Kuszmaul8e62b022022-03-22 09:33:25 -0700968
969 if (IsString(ev.union_type)) {
970 ret += "return " + accessor_str + "'') as string;";
971 } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
972 const auto type =
Austin Schuh2dd86a92022-09-14 21:19:23 -0700973 AddImport(imports, enum_def, *ev.union_type.struct_def).name;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700974 ret += "return " + accessor_str + "new " + type + "())! as " +
975 type + ";";
976 } else {
977 FLATBUFFERS_ASSERT(false);
978 }
979 ret += "\n";
980 }
981
982 ret += " default: return null;\n";
983 ret += " }\n";
984 };
985
986 union_enum_loop("accessor(");
987 ret += "}";
988
989 ret += "\n\nexport function " + GenUnionListConvFuncName(enum_def) +
Austin Schuh2dd86a92022-09-14 21:19:23 -0700990 "(\n type: " + GetTypeName(enum_def) +
James Kuszmaul8e62b022022-03-22 09:33:25 -0700991 ", \n accessor: (index: number, obj:" + valid_union_type +
992 ") => " + valid_union_type_with_null +
993 ", \n index: number\n): " + valid_union_type_with_null + " {\n";
994 union_enum_loop("accessor(index, ");
995 ret += "}";
996
997 return ret;
998 }
999 FLATBUFFERS_ASSERT(0);
1000 return "";
1001 }
1002
1003 // Used for generating a short function that returns the correct class
1004 // based on union enum type. Assume the context is inside the non object api
1005 // type
Austin Schuh2dd86a92022-09-14 21:19:23 -07001006 std::string GenUnionValTS(import_set &imports, const StructDef &dependent,
1007 const std::string &field_name,
James Kuszmaul8e62b022022-03-22 09:33:25 -07001008 const Type &union_type,
1009 const bool is_array = false) {
1010 if (union_type.enum_def) {
1011 const auto &enum_def = *union_type.enum_def;
Austin Schuh2dd86a92022-09-14 21:19:23 -07001012 const auto enum_type = AddImport(imports, dependent, enum_def).name;
James Kuszmaul8e62b022022-03-22 09:33:25 -07001013 const std::string union_accessor = "this." + field_name;
1014
1015 const auto union_has_string = UnionHasStringType(enum_def);
1016 const auto field_binded_method = "this." + field_name + ".bind(this)";
1017
1018 std::string ret;
1019
1020 if (!is_array) {
1021 const auto conversion_function = GenUnionConvFuncName(enum_def);
James Kuszmaul8e62b022022-03-22 09:33:25 -07001022
1023 ret = "(() => {\n";
James Kuszmaul3b15b0c2022-11-08 14:03:16 -08001024 ret += " const temp = " + conversion_function + "(this." +
Austin Schuh2dd86a92022-09-14 21:19:23 -07001025 namer_.Method(field_name, "Type") + "(), " +
1026 field_binded_method + ");\n";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001027 ret += " if(temp === null) { return null; }\n";
1028 ret += union_has_string
1029 ? " if(typeof temp === 'string') { return temp; }\n"
1030 : "";
1031 ret += " return temp.unpack()\n";
1032 ret += " })()";
1033 } else {
1034 const auto conversion_function = GenUnionListConvFuncName(enum_def);
James Kuszmaul8e62b022022-03-22 09:33:25 -07001035
1036 ret = "(() => {\n";
James Kuszmaul3b15b0c2022-11-08 14:03:16 -08001037 ret += " const ret: (" +
1038 GenObjApiUnionTypeTS(imports, *union_type.struct_def,
1039 parser_.opts, *union_type.enum_def) +
1040 ")[] = [];\n";
Austin Schuh2dd86a92022-09-14 21:19:23 -07001041 ret += " for(let targetEnumIndex = 0; targetEnumIndex < this." +
1042 namer_.Method(field_name, "TypeLength") + "()" +
James Kuszmaul8e62b022022-03-22 09:33:25 -07001043 "; "
1044 "++targetEnumIndex) {\n";
James Kuszmaul3b15b0c2022-11-08 14:03:16 -08001045 ret += " const targetEnum = this." +
Austin Schuh2dd86a92022-09-14 21:19:23 -07001046 namer_.Method(field_name, "Type") + "(targetEnumIndex);\n";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001047 ret += " if(targetEnum === null || " + enum_type +
1048 "[targetEnum!] === 'NONE') { "
1049 "continue; }\n\n";
James Kuszmaul3b15b0c2022-11-08 14:03:16 -08001050 ret += " const temp = " + conversion_function + "(targetEnum, " +
James Kuszmaul8e62b022022-03-22 09:33:25 -07001051 field_binded_method + ", targetEnumIndex);\n";
1052 ret += " if(temp === null) { continue; }\n";
1053 ret += union_has_string ? " if(typeof temp === 'string') { "
1054 "ret.push(temp); continue; }\n"
1055 : "";
1056 ret += " ret.push(temp.unpack());\n";
1057 ret += " }\n";
1058 ret += " return ret;\n";
1059 ret += " })()";
1060 }
1061
1062 return ret;
1063 }
1064
1065 FLATBUFFERS_ASSERT(0);
1066 return "";
1067 }
1068
1069 static std::string GenNullCheckConditional(
1070 const std::string &nullCheckVar, const std::string &trueVal,
1071 const std::string &falseVal = "null") {
1072 return "(" + nullCheckVar + " !== null ? " + trueVal + " : " + falseVal +
1073 ")";
1074 }
1075
1076 std::string GenStructMemberValueTS(const StructDef &struct_def,
1077 const std::string &prefix,
1078 const std::string &delimiter,
1079 const bool nullCheck = true) {
1080 std::string ret;
1081 for (auto it = struct_def.fields.vec.begin();
1082 it != struct_def.fields.vec.end(); ++it) {
1083 auto &field = **it;
1084
Austin Schuh2dd86a92022-09-14 21:19:23 -07001085 auto curr_member_accessor = prefix + "." + namer_.Method(field);
1086 if (prefix != "this") {
1087 curr_member_accessor = prefix + "?." + namer_.Method(field);
1088 }
James Kuszmaul8e62b022022-03-22 09:33:25 -07001089 if (IsStruct(field.value.type)) {
1090 ret += GenStructMemberValueTS(*field.value.type.struct_def,
1091 curr_member_accessor, delimiter);
1092 } else {
1093 if (nullCheck) {
Austin Schuh2dd86a92022-09-14 21:19:23 -07001094 std::string nullValue = "0";
1095 if (field.value.type.base_type == BASE_TYPE_BOOL) {
1096 nullValue = "false";
James Kuszmaul3b15b0c2022-11-08 14:03:16 -08001097 } else if (field.value.type.base_type == BASE_TYPE_LONG ||
1098 field.value.type.base_type == BASE_TYPE_ULONG) {
1099 nullValue = "BigInt(0)";
1100 } else if (field.value.type.base_type == BASE_TYPE_ARRAY) {
1101 nullValue = "[]";
Austin Schuh2dd86a92022-09-14 21:19:23 -07001102 }
1103 ret += "(" + curr_member_accessor + " ?? " + nullValue + ")";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001104 } else {
1105 ret += curr_member_accessor;
1106 }
1107 }
1108
1109 if (std::next(it) != struct_def.fields.vec.end()) { ret += delimiter; }
1110 }
1111
1112 return ret;
1113 }
1114
1115 void GenObjApi(const Parser &parser, StructDef &struct_def,
1116 std::string &obj_api_unpack_func, std::string &obj_api_class,
1117 import_set &imports) {
Austin Schuh2dd86a92022-09-14 21:19:23 -07001118 const auto class_name = GetTypeName(struct_def, /*object_api=*/true);
James Kuszmaul8e62b022022-03-22 09:33:25 -07001119
1120 std::string unpack_func = "\nunpack(): " + class_name +
1121 " {\n return new " + class_name + "(" +
1122 (struct_def.fields.vec.empty() ? "" : "\n");
1123 std::string unpack_to_func = "\nunpackTo(_o: " + class_name + "): void {" +
1124 +(struct_def.fields.vec.empty() ? "" : "\n");
1125
1126 std::string constructor_func = "constructor(";
1127 constructor_func += (struct_def.fields.vec.empty() ? "" : "\n");
1128
1129 const auto has_create =
1130 struct_def.fixed || CanCreateFactoryMethod(struct_def);
1131
1132 std::string pack_func_prototype =
1133 "\npack(builder:flatbuffers.Builder): flatbuffers.Offset {\n";
1134
1135 std::string pack_func_offset_decl;
1136 std::string pack_func_create_call;
1137
Austin Schuh2dd86a92022-09-14 21:19:23 -07001138 const auto struct_name = AddImport(imports, struct_def, struct_def).name;
James Kuszmaul8e62b022022-03-22 09:33:25 -07001139
1140 if (has_create) {
1141 pack_func_create_call = " return " + struct_name + ".create" +
1142 GetPrefixedName(struct_def) + "(builder" +
1143 (struct_def.fields.vec.empty() ? "" : ",\n ");
1144 } else {
1145 pack_func_create_call = " " + struct_name + ".start" +
1146 GetPrefixedName(struct_def) + "(builder);\n";
1147 }
1148
1149 if (struct_def.fixed) {
1150 // when packing struct, nested struct's members instead of the struct's
1151 // offset are used
1152 pack_func_create_call +=
1153 GenStructMemberValueTS(struct_def, "this", ",\n ", false) + "\n ";
1154 }
1155
1156 for (auto it = struct_def.fields.vec.begin();
1157 it != struct_def.fields.vec.end(); ++it) {
1158 auto &field = **it;
1159 if (field.deprecated) continue;
1160
Austin Schuh2dd86a92022-09-14 21:19:23 -07001161 const auto field_method = namer_.Method(field);
1162 const auto field_field = namer_.Field(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -07001163 const std::string field_binded_method =
Austin Schuh2dd86a92022-09-14 21:19:23 -07001164 "this." + field_method + ".bind(this)";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001165
1166 std::string field_val;
1167 std::string field_type;
1168 // a string that declares a variable containing the
1169 // offset for things that can't be generated inline
1170 // empty otw
1171 std::string field_offset_decl;
1172 // a string that contains values for things that can be created inline or
1173 // the variable name from field_offset_decl
1174 std::string field_offset_val;
1175 const auto field_default_val = GenDefaultValue(field, imports);
1176
1177 // Emit a scalar field
1178 const auto is_string = IsString(field.value.type);
1179 if (IsScalar(field.value.type.base_type) || is_string) {
1180 const auto has_null_default = is_string || HasNullDefault(field);
1181
1182 field_type += GenTypeName(imports, field, field.value.type, false,
1183 has_null_default);
Austin Schuh2dd86a92022-09-14 21:19:23 -07001184 field_val = "this." + namer_.Method(field) + "()";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001185
1186 if (field.value.type.base_type != BASE_TYPE_STRING) {
Austin Schuh2dd86a92022-09-14 21:19:23 -07001187 field_offset_val = "this." + namer_.Field(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -07001188 } else {
1189 field_offset_decl = GenNullCheckConditional(
Austin Schuh2dd86a92022-09-14 21:19:23 -07001190 "this." + namer_.Field(field),
1191 "builder.createString(this." + field_field + "!)", "0");
James Kuszmaul8e62b022022-03-22 09:33:25 -07001192 }
1193 }
1194
1195 // Emit an object field
1196 else {
1197 auto is_vector = false;
1198 switch (field.value.type.base_type) {
1199 case BASE_TYPE_STRUCT: {
1200 const auto &sd = *field.value.type.struct_def;
Austin Schuh2dd86a92022-09-14 21:19:23 -07001201 field_type += AddImport(imports, struct_def, sd).object_name;
James Kuszmaul8e62b022022-03-22 09:33:25 -07001202
1203 const std::string field_accessor =
Austin Schuh2dd86a92022-09-14 21:19:23 -07001204 "this." + namer_.Method(field) + "()";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001205 field_val = GenNullCheckConditional(field_accessor,
1206 field_accessor + "!.unpack()");
1207 auto packing = GenNullCheckConditional(
Austin Schuh2dd86a92022-09-14 21:19:23 -07001208 "this." + field_field,
1209 "this." + field_field + "!.pack(builder)", "0");
James Kuszmaul8e62b022022-03-22 09:33:25 -07001210
1211 if (sd.fixed) {
1212 field_offset_val = std::move(packing);
1213 } else {
1214 field_offset_decl = std::move(packing);
1215 }
1216
1217 break;
1218 }
1219
James Kuszmaul3b15b0c2022-11-08 14:03:16 -08001220 case BASE_TYPE_ARRAY: {
James Kuszmaul8e62b022022-03-22 09:33:25 -07001221 auto vectortype = field.value.type.VectorType();
1222 auto vectortypename =
1223 GenTypeName(imports, struct_def, vectortype, false);
1224 is_vector = true;
1225
1226 field_type = "(";
1227
1228 switch (vectortype.base_type) {
1229 case BASE_TYPE_STRUCT: {
1230 const auto &sd = *field.value.type.struct_def;
James Kuszmaul3b15b0c2022-11-08 14:03:16 -08001231 const auto field_type_name =
1232 GetTypeName(sd, /*object_api=*/true);
1233 field_type += field_type_name;
James Kuszmaul8e62b022022-03-22 09:33:25 -07001234 field_type += ")[]";
1235
James Kuszmaul3b15b0c2022-11-08 14:03:16 -08001236 field_val = GenBBAccess() + ".createObjList<" + vectortypename +
1237 ", " + field_type_name + ">(" +
1238 field_binded_method + ", " +
1239 NumToString(field.value.type.fixed_length) + ")";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001240
1241 if (sd.fixed) {
1242 field_offset_decl =
Austin Schuh2dd86a92022-09-14 21:19:23 -07001243 "builder.createStructOffsetList(this." + field_field +
1244 ", " + AddImport(imports, struct_def, struct_def).name +
1245 "." + namer_.Method("start", field, "Vector") + ")";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001246 } else {
1247 field_offset_decl =
Austin Schuh2dd86a92022-09-14 21:19:23 -07001248 AddImport(imports, struct_def, struct_def).name + "." +
1249 namer_.Method("create", field, "Vector") +
1250 "(builder, builder.createObjectOffsetList(" + "this." +
1251 field_field + "))";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001252 }
1253
1254 break;
1255 }
1256
1257 case BASE_TYPE_STRING: {
1258 field_type += "string)[]";
James Kuszmaul3b15b0c2022-11-08 14:03:16 -08001259 field_val = GenBBAccess() + ".createScalarList<string>(" +
Austin Schuh2dd86a92022-09-14 21:19:23 -07001260 field_binded_method + ", this." +
1261 namer_.Field(field, "Length") + "())";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001262 field_offset_decl =
Austin Schuh2dd86a92022-09-14 21:19:23 -07001263 AddImport(imports, struct_def, struct_def).name + "." +
1264 namer_.Method("create", field, "Vector") +
1265 "(builder, builder.createObjectOffsetList(" + "this." +
1266 namer_.Field(field) + "))";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001267 break;
1268 }
1269
1270 case BASE_TYPE_UNION: {
Austin Schuh2dd86a92022-09-14 21:19:23 -07001271 field_type += GenObjApiUnionTypeTS(
1272 imports, struct_def, parser.opts, *(vectortype.enum_def));
James Kuszmaul8e62b022022-03-22 09:33:25 -07001273 field_type += ")[]";
Austin Schuh2dd86a92022-09-14 21:19:23 -07001274 field_val = GenUnionValTS(imports, struct_def, field_method,
1275 vectortype, true);
James Kuszmaul8e62b022022-03-22 09:33:25 -07001276
1277 field_offset_decl =
Austin Schuh2dd86a92022-09-14 21:19:23 -07001278 AddImport(imports, struct_def, struct_def).name + "." +
1279 namer_.Method("create", field, "Vector") +
1280 "(builder, builder.createObjectOffsetList(" + "this." +
1281 namer_.Field(field) + "))";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001282
1283 break;
1284 }
1285 default: {
1286 if (vectortype.enum_def) {
1287 field_type += GenTypeName(imports, struct_def, vectortype,
1288 false, HasNullDefault(field));
1289 } else {
1290 field_type += vectortypename;
1291 }
1292 field_type += ")[]";
James Kuszmaul3b15b0c2022-11-08 14:03:16 -08001293 field_val = GenBBAccess() + ".createScalarList<" +
1294 vectortypename + ">(" + field_binded_method + ", " +
1295 NumToString(field.value.type.fixed_length) + ")";
1296
1297 field_offset_decl =
1298 AddImport(imports, struct_def, struct_def).name + "." +
1299 namer_.Method("create", field, "Vector") +
1300 "(builder, this." + field_field + ")";
1301
1302 break;
1303 }
1304 }
1305
1306 break;
1307 }
1308
1309 case BASE_TYPE_VECTOR: {
1310 auto vectortype = field.value.type.VectorType();
1311 auto vectortypename =
1312 GenTypeName(imports, struct_def, vectortype, false);
1313 is_vector = true;
1314
1315 field_type = "(";
1316
1317 switch (vectortype.base_type) {
1318 case BASE_TYPE_STRUCT: {
1319 const auto &sd = *field.value.type.struct_def;
1320 const auto field_type_name =
1321 GetTypeName(sd, /*object_api=*/true);
1322 field_type += field_type_name;
1323 field_type += ")[]";
1324
1325 field_val = GenBBAccess() + ".createObjList<" + vectortypename +
1326 ", " + field_type_name + ">(" +
Austin Schuh2dd86a92022-09-14 21:19:23 -07001327 field_binded_method + ", this." +
1328 namer_.Method(field, "Length") + "())";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001329
James Kuszmaul3b15b0c2022-11-08 14:03:16 -08001330 if (sd.fixed) {
1331 field_offset_decl =
1332 "builder.createStructOffsetList(this." + field_field +
1333 ", " + AddImport(imports, struct_def, struct_def).name +
1334 "." + namer_.Method("start", field, "Vector") + ")";
1335 } else {
1336 field_offset_decl =
1337 AddImport(imports, struct_def, struct_def).name + "." +
1338 namer_.Method("create", field, "Vector") +
1339 "(builder, builder.createObjectOffsetList(" + "this." +
1340 field_field + "))";
1341 }
1342
1343 break;
1344 }
1345
1346 case BASE_TYPE_STRING: {
1347 field_type += "string)[]";
1348 field_val = GenBBAccess() + ".createScalarList<string>(" +
1349 field_binded_method + ", this." +
1350 namer_.Field(field, "Length") + "())";
1351 field_offset_decl =
1352 AddImport(imports, struct_def, struct_def).name + "." +
1353 namer_.Method("create", field, "Vector") +
1354 "(builder, builder.createObjectOffsetList(" + "this." +
1355 namer_.Field(field) + "))";
1356 break;
1357 }
1358
1359 case BASE_TYPE_UNION: {
1360 field_type += GenObjApiUnionTypeTS(
1361 imports, struct_def, parser.opts, *(vectortype.enum_def));
1362 field_type += ")[]";
1363 field_val = GenUnionValTS(imports, struct_def, field_method,
1364 vectortype, true);
1365
1366 field_offset_decl =
1367 AddImport(imports, struct_def, struct_def).name + "." +
1368 namer_.Method("create", field, "Vector") +
1369 "(builder, builder.createObjectOffsetList(" + "this." +
1370 namer_.Field(field) + "))";
1371
1372 break;
1373 }
1374 default: {
1375 if (vectortype.enum_def) {
1376 field_type += GenTypeName(imports, struct_def, vectortype,
1377 false, HasNullDefault(field));
1378 } else {
1379 field_type += vectortypename;
1380 }
1381 field_type += ")[]";
1382 field_val = GenBBAccess() + ".createScalarList<" +
1383 vectortypename + ">(" + field_binded_method +
1384 ", this." + namer_.Method(field, "Length") + "())";
1385
James Kuszmaul8e62b022022-03-22 09:33:25 -07001386 field_offset_decl =
Austin Schuh2dd86a92022-09-14 21:19:23 -07001387 AddImport(imports, struct_def, struct_def).name + "." +
1388 namer_.Method("create", field, "Vector") +
1389 "(builder, this." + field_field + ")";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001390
1391 break;
1392 }
1393 }
1394
1395 break;
1396 }
1397
1398 case BASE_TYPE_UNION: {
Austin Schuh2dd86a92022-09-14 21:19:23 -07001399 field_type += GenObjApiUnionTypeTS(imports, struct_def, parser.opts,
James Kuszmaul8e62b022022-03-22 09:33:25 -07001400 *(field.value.type.enum_def));
1401
Austin Schuh2dd86a92022-09-14 21:19:23 -07001402 field_val = GenUnionValTS(imports, struct_def, field_method,
1403 field.value.type);
James Kuszmaul8e62b022022-03-22 09:33:25 -07001404 field_offset_decl =
Austin Schuh2dd86a92022-09-14 21:19:23 -07001405 "builder.createObjectOffset(this." + field_field + ")";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001406 break;
1407 }
1408
1409 default: FLATBUFFERS_ASSERT(0); break;
1410 }
1411
1412 // length 0 vector is simply empty instead of null
1413 field_type += is_vector ? "" : "|null";
1414 }
1415
1416 if (!field_offset_decl.empty()) {
1417 field_offset_decl =
Austin Schuh2dd86a92022-09-14 21:19:23 -07001418 " const " + field_field + " = " + field_offset_decl + ";";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001419 }
Austin Schuh2dd86a92022-09-14 21:19:23 -07001420 if (field_offset_val.empty()) { field_offset_val = field_field; }
James Kuszmaul8e62b022022-03-22 09:33:25 -07001421
1422 unpack_func += " " + field_val;
Austin Schuh2dd86a92022-09-14 21:19:23 -07001423 unpack_to_func += " _o." + field_field + " = " + field_val + ";";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001424
Austin Schuh2dd86a92022-09-14 21:19:23 -07001425 // FIXME: if field_type and field_field are identical, then
James Kuszmaul8e62b022022-03-22 09:33:25 -07001426 // this generates invalid typescript.
Austin Schuh2dd86a92022-09-14 21:19:23 -07001427 constructor_func += " public " + field_field + ": " + field_type +
1428 " = " + field_default_val;
James Kuszmaul8e62b022022-03-22 09:33:25 -07001429
1430 if (!struct_def.fixed) {
1431 if (!field_offset_decl.empty()) {
1432 pack_func_offset_decl += field_offset_decl + "\n";
1433 }
1434
1435 if (has_create) {
1436 pack_func_create_call += field_offset_val;
1437 } else {
1438 if (field.IsScalarOptional()) {
Austin Schuh2dd86a92022-09-14 21:19:23 -07001439 pack_func_create_call +=
1440 " if (" + field_offset_val + " !== null)\n ";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001441 }
Austin Schuh2dd86a92022-09-14 21:19:23 -07001442 pack_func_create_call += " " + struct_name + "." +
1443 namer_.Method("add", field) + "(builder, " +
1444 field_offset_val + ");\n";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001445 }
1446 }
1447
1448 if (std::next(it) != struct_def.fields.vec.end()) {
1449 constructor_func += ",\n";
1450
1451 if (!struct_def.fixed && has_create) {
1452 pack_func_create_call += ",\n ";
1453 }
1454
1455 unpack_func += ",\n";
1456 unpack_to_func += "\n";
1457 } else {
1458 constructor_func += "\n";
1459 if (!struct_def.fixed) {
1460 pack_func_offset_decl += (pack_func_offset_decl.empty() ? "" : "\n");
1461 pack_func_create_call += "\n ";
1462 }
1463
1464 unpack_func += "\n ";
1465 unpack_to_func += "\n";
1466 }
1467 }
1468
1469 constructor_func += "){}\n\n";
1470
1471 if (has_create) {
1472 pack_func_create_call += ");";
1473 } else {
1474 pack_func_create_call += "return " + struct_name + ".end" +
1475 GetPrefixedName(struct_def) + "(builder);";
1476 }
Austin Schuh2dd86a92022-09-14 21:19:23 -07001477 obj_api_class = "\n";
1478 obj_api_class += "export class ";
1479 obj_api_class += GetTypeName(struct_def, /*object_api=*/true);
James Kuszmaul3b15b0c2022-11-08 14:03:16 -08001480 obj_api_class += " implements flatbuffers.IGeneratedObject {\n";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001481 obj_api_class += constructor_func;
1482 obj_api_class += pack_func_prototype + pack_func_offset_decl +
1483 pack_func_create_call + "\n}";
1484
1485 obj_api_class += "\n}\n";
1486
1487 unpack_func += ");\n}";
1488 unpack_to_func += "}\n";
1489
1490 obj_api_unpack_func = unpack_func + "\n\n" + unpack_to_func;
1491 }
1492
1493 static bool CanCreateFactoryMethod(const StructDef &struct_def) {
1494 // to preserve backwards compatibility, we allow the first field to be a
1495 // struct
1496 return struct_def.fields.vec.size() < 2 ||
1497 std::all_of(std::begin(struct_def.fields.vec) + 1,
1498 std::end(struct_def.fields.vec),
1499 [](const FieldDef *f) -> bool {
1500 FLATBUFFERS_ASSERT(f != nullptr);
1501 return f->value.type.base_type != BASE_TYPE_STRUCT;
1502 });
1503 }
1504
1505 // Generate an accessor struct with constructor for a flatbuffers struct.
1506 void GenStruct(const Parser &parser, StructDef &struct_def,
1507 std::string *code_ptr, import_set &imports) {
1508 if (struct_def.generated) return;
1509 std::string &code = *code_ptr;
1510
Austin Schuh2dd86a92022-09-14 21:19:23 -07001511 // Special case for the root struct, since no one will necessarily reference
1512 // it, we have to explicitly add it to the import list.
1513 if (&struct_def == parser_.root_struct_def_) {
1514 AddImport(imports, struct_def, struct_def);
1515 }
1516
1517 const std::string object_name = GetTypeName(struct_def);
James Kuszmaul3b15b0c2022-11-08 14:03:16 -08001518 const std::string object_api_name = GetTypeName(struct_def, true);
James Kuszmaul8e62b022022-03-22 09:33:25 -07001519
1520 // Emit constructor
James Kuszmaul8e62b022022-03-22 09:33:25 -07001521 GenDocComment(struct_def.doc_comment, code_ptr);
Austin Schuh2dd86a92022-09-14 21:19:23 -07001522 code += "export class ";
1523 code += object_name;
James Kuszmaul3b15b0c2022-11-08 14:03:16 -08001524 if (parser.opts.generate_object_based_api)
1525 code += " implements flatbuffers.IUnpackableObject<" + object_api_name +
1526 "> {\n";
1527 else
1528 code += " {\n";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001529 code += " bb: flatbuffers.ByteBuffer|null = null;\n";
1530 code += " bb_pos = 0;\n";
1531
1532 // Generate the __init method that sets the field in a pre-existing
1533 // accessor object. This is to allow object reuse.
1534 code +=
Austin Schuh2dd86a92022-09-14 21:19:23 -07001535 " __init(i:number, bb:flatbuffers.ByteBuffer):" + object_name + " {\n";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001536 code += " this.bb_pos = i;\n";
1537 code += " this.bb = bb;\n";
1538 code += " return this;\n";
1539 code += "}\n\n";
1540
1541 // Generate special accessors for the table that when used as the root of a
1542 // FlatBuffer
1543 GenerateRootAccessor(struct_def, code_ptr, code, object_name, false);
1544 GenerateRootAccessor(struct_def, code_ptr, code, object_name, true);
1545
1546 // Generate the identifier check method
1547 if (!struct_def.fixed && parser_.root_struct_def_ == &struct_def &&
1548 !parser_.file_identifier_.empty()) {
1549 GenDocComment(code_ptr);
1550 code +=
1551 "static bufferHasIdentifier(bb:flatbuffers.ByteBuffer):boolean "
1552 "{\n";
1553 code += " return bb.__has_identifier('" + parser_.file_identifier_;
1554 code += "');\n}\n\n";
1555 }
1556
1557 // Emit field accessors
1558 for (auto it = struct_def.fields.vec.begin();
1559 it != struct_def.fields.vec.end(); ++it) {
1560 auto &field = **it;
1561 if (field.deprecated) continue;
James Kuszmaul3b15b0c2022-11-08 14:03:16 -08001562 std::string offset_prefix = "";
1563
1564 if (field.value.type.base_type == BASE_TYPE_ARRAY) {
1565 offset_prefix = " return ";
1566 } else {
1567 offset_prefix = " const offset = " + GenBBAccess() +
1568 ".__offset(this.bb_pos, " +
1569 NumToString(field.value.offset) + ");\n";
1570 offset_prefix += " return offset ? ";
1571 }
James Kuszmaul8e62b022022-03-22 09:33:25 -07001572
1573 // Emit a scalar field
1574 const auto is_string = IsString(field.value.type);
1575 if (IsScalar(field.value.type.base_type) || is_string) {
1576 const auto has_null_default = is_string || HasNullDefault(field);
1577
1578 GenDocComment(field.doc_comment, code_ptr);
Austin Schuh2dd86a92022-09-14 21:19:23 -07001579 std::string prefix = namer_.Method(field) + "(";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001580 if (is_string) {
1581 code += prefix + "):string|null\n";
1582 code +=
1583 prefix + "optionalEncoding:flatbuffers.Encoding" + "):" +
1584 GenTypeName(imports, struct_def, field.value.type, false, true) +
1585 "\n";
1586 code += prefix + "optionalEncoding?:any";
1587 } else {
1588 code += prefix;
1589 }
1590 if (field.value.type.enum_def) {
1591 code += "):" +
1592 GenTypeName(imports, struct_def, field.value.type, false,
1593 field.IsOptional()) +
1594 " {\n";
1595 } else {
1596 code += "):" +
1597 GenTypeName(imports, struct_def, field.value.type, false,
1598 has_null_default) +
1599 " {\n";
1600 }
1601
1602 if (struct_def.fixed) {
1603 code +=
1604 " return " +
1605 GenGetter(field.value.type,
1606 "(this.bb_pos" + MaybeAdd(field.value.offset) + ")") +
1607 ";\n";
1608 } else {
1609 std::string index = "this.bb_pos + offset";
1610 if (is_string) { index += ", optionalEncoding"; }
James Kuszmaul3b15b0c2022-11-08 14:03:16 -08001611 code +=
1612 offset_prefix + GenGetter(field.value.type, "(" + index + ")");
1613 if (field.value.type.base_type != BASE_TYPE_ARRAY) {
1614 code += " : " + GenDefaultValue(field, imports);
1615 }
James Kuszmaul8e62b022022-03-22 09:33:25 -07001616 code += ";\n";
1617 }
1618 }
1619
1620 // Emit an object field
1621 else {
1622 switch (field.value.type.base_type) {
1623 case BASE_TYPE_STRUCT: {
Austin Schuh2dd86a92022-09-14 21:19:23 -07001624 const auto type =
1625 AddImport(imports, struct_def, *field.value.type.struct_def)
1626 .name;
James Kuszmaul8e62b022022-03-22 09:33:25 -07001627 GenDocComment(field.doc_comment, code_ptr);
Austin Schuh2dd86a92022-09-14 21:19:23 -07001628 code += namer_.Method(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -07001629 code += "(obj?:" + type + "):" + type + "|null {\n";
1630
1631 if (struct_def.fixed) {
1632 code += " return (obj || " + GenerateNewExpression(type);
1633 code += ").__init(this.bb_pos";
1634 code +=
1635 MaybeAdd(field.value.offset) + ", " + GenBBAccess() + ");\n";
1636 } else {
1637 code += offset_prefix + "(obj || " + GenerateNewExpression(type) +
1638 ").__init(";
1639 code += field.value.type.struct_def->fixed
1640 ? "this.bb_pos + offset"
1641 : GenBBAccess() + ".__indirect(this.bb_pos + offset)";
1642 code += ", " + GenBBAccess() + ") : null;\n";
1643 }
1644
1645 break;
1646 }
1647
James Kuszmaul3b15b0c2022-11-08 14:03:16 -08001648 case BASE_TYPE_ARRAY: {
1649 auto vectortype = field.value.type.VectorType();
1650 auto vectortypename =
1651 GenTypeName(imports, struct_def, vectortype, false);
1652 auto inline_size = InlineSize(vectortype);
1653 auto index = "this.bb_pos + " + NumToString(field.value.offset) +
1654 " + index" + MaybeScale(inline_size);
1655 std::string ret_type;
1656 bool is_union = false;
1657 switch (vectortype.base_type) {
1658 case BASE_TYPE_STRUCT: ret_type = vectortypename; break;
1659 case BASE_TYPE_STRING: ret_type = vectortypename; break;
1660 case BASE_TYPE_UNION:
1661 ret_type = "?flatbuffers.Table";
1662 is_union = true;
1663 break;
1664 default: ret_type = vectortypename;
1665 }
1666 GenDocComment(field.doc_comment, code_ptr);
1667 std::string prefix = namer_.Method(field);
1668 // TODO: make it work without any
1669 // if (is_union) { prefix += "<T extends flatbuffers.Table>"; }
1670 if (is_union) { prefix += ""; }
1671 prefix += "(index: number";
1672 if (is_union) {
1673 const auto union_type =
1674 GenUnionGenericTypeTS(*(field.value.type.enum_def));
1675
1676 vectortypename = union_type;
1677 code += prefix + ", obj:" + union_type;
1678 } else if (vectortype.base_type == BASE_TYPE_STRUCT) {
1679 code += prefix + ", obj?:" + vectortypename;
1680 } else if (IsString(vectortype)) {
1681 code += prefix + "):string\n";
1682 code += prefix + ",optionalEncoding:flatbuffers.Encoding" +
1683 "):" + vectortypename + "\n";
1684 code += prefix + ",optionalEncoding?:any";
1685 } else {
1686 code += prefix;
1687 }
1688 code += "):" + vectortypename + "|null {\n";
1689
1690 if (vectortype.base_type == BASE_TYPE_STRUCT) {
1691 code += offset_prefix + "(obj || " +
1692 GenerateNewExpression(vectortypename);
1693 code += ").__init(";
1694 code += vectortype.struct_def->fixed
1695 ? index
1696 : GenBBAccess() + ".__indirect(" + index + ")";
1697 code += ", " + GenBBAccess() + ")";
1698 } else {
1699 if (is_union) {
1700 index = "obj, " + index;
1701 } else if (IsString(vectortype)) {
1702 index += ", optionalEncoding";
1703 }
1704 code += offset_prefix + GenGetter(vectortype, "(" + index + ")");
1705 }
1706
1707 switch (field.value.type.base_type) {
1708 case BASE_TYPE_ARRAY: {
1709 break;
1710 }
1711 case BASE_TYPE_BOOL: {
1712 code += " : false";
1713 break;
1714 }
1715 case BASE_TYPE_LONG:
1716 case BASE_TYPE_ULONG: {
1717 code += " : BigInt(0)";
1718 break;
1719 }
1720 default: {
1721 if (IsScalar(field.value.type.element)) {
1722 if (field.value.type.enum_def) {
1723 code += field.value.constant;
1724 } else {
1725 code += " : 0";
1726 }
1727 } else {
1728 code += ": null";
1729 }
1730 break;
1731 }
1732 }
1733 code += ";\n";
1734 break;
1735 }
1736
James Kuszmaul8e62b022022-03-22 09:33:25 -07001737 case BASE_TYPE_VECTOR: {
1738 auto vectortype = field.value.type.VectorType();
1739 auto vectortypename =
1740 GenTypeName(imports, struct_def, vectortype, false);
1741 auto inline_size = InlineSize(vectortype);
1742 auto index = GenBBAccess() +
1743 ".__vector(this.bb_pos + offset) + index" +
1744 MaybeScale(inline_size);
1745 std::string ret_type;
1746 bool is_union = false;
1747 switch (vectortype.base_type) {
1748 case BASE_TYPE_STRUCT: ret_type = vectortypename; break;
1749 case BASE_TYPE_STRING: ret_type = vectortypename; break;
1750 case BASE_TYPE_UNION:
1751 ret_type = "?flatbuffers.Table";
1752 is_union = true;
1753 break;
1754 default: ret_type = vectortypename;
1755 }
1756 GenDocComment(field.doc_comment, code_ptr);
Austin Schuh2dd86a92022-09-14 21:19:23 -07001757 std::string prefix = namer_.Method(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -07001758 // TODO: make it work without any
1759 // if (is_union) { prefix += "<T extends flatbuffers.Table>"; }
1760 if (is_union) { prefix += ""; }
1761 prefix += "(index: number";
1762 if (is_union) {
1763 const auto union_type =
1764 GenUnionGenericTypeTS(*(field.value.type.enum_def));
1765
1766 vectortypename = union_type;
1767 code += prefix + ", obj:" + union_type;
1768 } else if (vectortype.base_type == BASE_TYPE_STRUCT) {
1769 code += prefix + ", obj?:" + vectortypename;
1770 } else if (IsString(vectortype)) {
1771 code += prefix + "):string\n";
1772 code += prefix + ",optionalEncoding:flatbuffers.Encoding" +
1773 "):" + vectortypename + "\n";
1774 code += prefix + ",optionalEncoding?:any";
1775 } else {
1776 code += prefix;
1777 }
1778 code += "):" + vectortypename + "|null {\n";
1779
1780 if (vectortype.base_type == BASE_TYPE_STRUCT) {
1781 code += offset_prefix + "(obj || " +
1782 GenerateNewExpression(vectortypename);
1783 code += ").__init(";
1784 code += vectortype.struct_def->fixed
1785 ? index
1786 : GenBBAccess() + ".__indirect(" + index + ")";
1787 code += ", " + GenBBAccess() + ")";
1788 } else {
1789 if (is_union) {
1790 index = "obj, " + index;
1791 } else if (IsString(vectortype)) {
1792 index += ", optionalEncoding";
1793 }
1794 code += offset_prefix + GenGetter(vectortype, "(" + index + ")");
1795 }
1796 code += " : ";
1797 if (field.value.type.element == BASE_TYPE_BOOL) {
1798 code += "false";
1799 } else if (field.value.type.element == BASE_TYPE_LONG ||
1800 field.value.type.element == BASE_TYPE_ULONG) {
1801 code += "BigInt(0)";
1802 } else if (IsScalar(field.value.type.element)) {
1803 if (field.value.type.enum_def) {
1804 code += field.value.constant;
1805 } else {
1806 code += "0";
1807 }
1808 } else {
1809 code += "null";
1810 }
1811 code += ";\n";
1812 break;
1813 }
1814
1815 case BASE_TYPE_UNION: {
1816 GenDocComment(field.doc_comment, code_ptr);
Austin Schuh2dd86a92022-09-14 21:19:23 -07001817 code += namer_.Method(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -07001818
1819 const auto &union_enum = *(field.value.type.enum_def);
1820 const auto union_type = GenUnionGenericTypeTS(union_enum);
1821 code += "<T extends flatbuffers.Table>(obj:" + union_type +
1822 "):" + union_type +
1823 "|null "
1824 "{\n";
1825
1826 code += offset_prefix +
1827 GenGetter(field.value.type, "(obj, this.bb_pos + offset)") +
1828 " : null;\n";
1829 break;
1830 }
1831 default: FLATBUFFERS_ASSERT(0);
1832 }
1833 }
1834 code += "}\n\n";
1835
1836 // Adds the mutable scalar value to the output
1837 if (IsScalar(field.value.type.base_type) && parser.opts.mutable_buffer &&
1838 !IsUnion(field.value.type)) {
1839 std::string type =
1840 GenTypeName(imports, struct_def, field.value.type, true);
1841
Austin Schuh2dd86a92022-09-14 21:19:23 -07001842 code += namer_.LegacyTsMutateMethod(field) + "(value:" + type +
1843 "):boolean {\n";
1844
1845 const std::string write_method =
1846 "." + namer_.Method("write", GenType(field.value.type));
James Kuszmaul8e62b022022-03-22 09:33:25 -07001847
1848 if (struct_def.fixed) {
Austin Schuh2dd86a92022-09-14 21:19:23 -07001849 code += " " + GenBBAccess() + write_method + "(this.bb_pos + " +
1850 NumToString(field.value.offset) + ", ";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001851 } else {
1852 code += " const offset = " + GenBBAccess() +
1853 ".__offset(this.bb_pos, " + NumToString(field.value.offset) +
1854 ");\n\n";
1855 code += " if (offset === 0) {\n";
1856 code += " return false;\n";
1857 code += " }\n\n";
1858
1859 // special case for bools, which are treated as uint8
Austin Schuh2dd86a92022-09-14 21:19:23 -07001860 code +=
1861 " " + GenBBAccess() + write_method + "(this.bb_pos + offset, ";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001862 if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1863 }
1864
1865 code += "value);\n";
1866 code += " return true;\n";
1867 code += "}\n\n";
1868 }
1869
1870 // Emit vector helpers
1871 if (IsVector(field.value.type)) {
1872 // Emit a length helper
1873 GenDocComment(code_ptr);
Austin Schuh2dd86a92022-09-14 21:19:23 -07001874 code += namer_.Method(field, "Length");
1875 code += "():number {\n" + offset_prefix;
James Kuszmaul8e62b022022-03-22 09:33:25 -07001876
1877 code +=
1878 GenBBAccess() + ".__vector_len(this.bb_pos + offset) : 0;\n}\n\n";
1879
1880 // For scalar types, emit a typed array helper
1881 auto vectorType = field.value.type.VectorType();
1882 if (IsScalar(vectorType.base_type) && !IsLong(vectorType.base_type)) {
1883 GenDocComment(code_ptr);
1884
Austin Schuh2dd86a92022-09-14 21:19:23 -07001885 code += namer_.Method(field, "Array");
1886 code +=
1887 "():" + GenType(vectorType) + "Array|null {\n" + offset_prefix;
James Kuszmaul8e62b022022-03-22 09:33:25 -07001888
1889 code += "new " + GenType(vectorType) + "Array(" + GenBBAccess() +
1890 ".bytes().buffer, " + GenBBAccess() +
1891 ".bytes().byteOffset + " + GenBBAccess() +
1892 ".__vector(this.bb_pos + offset), " + GenBBAccess() +
1893 ".__vector_len(this.bb_pos + offset)) : null;\n}\n\n";
1894 }
1895 }
1896 }
1897
1898 // Emit the fully qualified name
1899 if (parser_.opts.generate_name_strings) {
1900 GenDocComment(code_ptr);
1901 code += "static getFullyQualifiedName():string {\n";
1902 code += " return '" + WrapInNameSpace(struct_def) + "';\n";
1903 code += "}\n\n";
1904 }
1905
1906 // Emit the size of the struct.
1907 if (struct_def.fixed) {
1908 GenDocComment(code_ptr);
1909 code += "static sizeOf():number {\n";
1910 code += " return " + NumToString(struct_def.bytesize) + ";\n";
1911 code += "}\n\n";
1912 }
1913
1914 // Emit a factory constructor
1915 if (struct_def.fixed) {
1916 std::string arguments;
1917 GenStructArgs(imports, struct_def, &arguments, "");
1918 GenDocComment(code_ptr);
1919
1920 code += "static create" + GetPrefixedName(struct_def) +
1921 "(builder:flatbuffers.Builder";
1922 code += arguments + "):flatbuffers.Offset {\n";
1923
1924 GenStructBody(struct_def, &code, "");
1925 code += " return builder.offset();\n}\n\n";
1926 } else {
1927 // Generate a method to start building a new object
1928 GenDocComment(code_ptr);
1929
1930 code += "static start" + GetPrefixedName(struct_def) +
1931 "(builder:flatbuffers.Builder) {\n";
1932
1933 code += " builder.startObject(" +
1934 NumToString(struct_def.fields.vec.size()) + ");\n";
1935 code += "}\n\n";
1936
1937 // Generate a set of static methods that allow table construction
1938 for (auto it = struct_def.fields.vec.begin();
1939 it != struct_def.fields.vec.end(); ++it) {
1940 auto &field = **it;
1941 if (field.deprecated) continue;
1942 const auto argname = GetArgName(field);
1943
1944 // Generate the field insertion method
1945 GenDocComment(code_ptr);
Austin Schuh2dd86a92022-09-14 21:19:23 -07001946 code += "static " + namer_.Method("add", field);
James Kuszmaul8e62b022022-03-22 09:33:25 -07001947 code += "(builder:flatbuffers.Builder, " + argname + ":" +
1948 GetArgType(imports, struct_def, field, false) + ") {\n";
1949 code += " builder.addField" + GenWriteMethod(field.value.type) + "(";
1950 code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
1951 if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1952 code += argname + ", ";
1953 if (!IsScalar(field.value.type.base_type)) {
1954 code += "0";
1955 } else if (HasNullDefault(field)) {
1956 if (IsLong(field.value.type.base_type)) {
1957 code += "BigInt(0)";
1958 } else {
1959 code += "0";
1960 }
1961 } else {
1962 if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1963 code += GenDefaultValue(field, imports);
1964 }
1965 code += ");\n}\n\n";
1966
1967 if (IsVector(field.value.type)) {
1968 auto vector_type = field.value.type.VectorType();
1969 auto alignment = InlineAlignment(vector_type);
1970 auto elem_size = InlineSize(vector_type);
1971
1972 // Generate a method to create a vector from a JavaScript array
1973 if (!IsStruct(vector_type)) {
1974 GenDocComment(code_ptr);
1975
1976 const std::string sig_begin =
Austin Schuh2dd86a92022-09-14 21:19:23 -07001977 "static " + namer_.Method("create", field, "Vector") +
1978 "(builder:flatbuffers.Builder, data:";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001979 const std::string sig_end = "):flatbuffers.Offset";
1980 std::string type =
1981 GenTypeName(imports, struct_def, vector_type, true) + "[]";
1982 if (type == "number[]") {
1983 const auto &array_type = GenType(vector_type);
1984 // the old type should be deprecated in the future
1985 std::string type_old = "number[]|Uint8Array";
1986 std::string type_new = "number[]|" + array_type + "Array";
1987 if (type_old == type_new) {
1988 type = type_new;
1989 } else {
1990 // add function overloads
1991 code += sig_begin + type_new + sig_end + ";\n";
1992 code +=
1993 "/**\n * @deprecated This Uint8Array overload will "
1994 "be removed in the future.\n */\n";
1995 code += sig_begin + type_old + sig_end + ";\n";
1996 type = type_new + "|Uint8Array";
1997 }
1998 }
1999 code += sig_begin + type + sig_end + " {\n";
2000 code += " builder.startVector(" + NumToString(elem_size);
2001 code += ", data.length, " + NumToString(alignment) + ");\n";
2002 code += " for (let i = data.length - 1; i >= 0; i--) {\n";
2003 code += " builder.add" + GenWriteMethod(vector_type) + "(";
2004 if (vector_type.base_type == BASE_TYPE_BOOL) { code += "+"; }
2005 code += "data[i]!);\n";
2006 code += " }\n";
2007 code += " return builder.endVector();\n";
2008 code += "}\n\n";
2009 }
2010
2011 // Generate a method to start a vector, data to be added manually
2012 // after
2013 GenDocComment(code_ptr);
2014
Austin Schuh2dd86a92022-09-14 21:19:23 -07002015 code += "static ";
2016 code += namer_.Method("start", field, "Vector");
2017 code += "(builder:flatbuffers.Builder, numElems:number) {\n";
James Kuszmaul8e62b022022-03-22 09:33:25 -07002018 code += " builder.startVector(" + NumToString(elem_size);
2019 code += ", numElems, " + NumToString(alignment) + ");\n";
2020 code += "}\n\n";
2021 }
2022 }
2023
2024 // Generate a method to stop building a new object
2025 GenDocComment(code_ptr);
2026
2027 code += "static end" + GetPrefixedName(struct_def);
2028 code += "(builder:flatbuffers.Builder):flatbuffers.Offset {\n";
2029
2030 code += " const offset = builder.endObject();\n";
2031 for (auto it = struct_def.fields.vec.begin();
2032 it != struct_def.fields.vec.end(); ++it) {
2033 auto &field = **it;
2034 if (!field.deprecated && field.IsRequired()) {
2035 code += " builder.requiredField(offset, ";
2036 code += NumToString(field.value.offset);
2037 code += ") // " + field.name + "\n";
2038 }
2039 }
2040 code += " return offset;\n";
2041 code += "}\n\n";
2042
2043 // Generate the methods to complete buffer construction
2044 GenerateFinisher(struct_def, code_ptr, code, false);
2045 GenerateFinisher(struct_def, code_ptr, code, true);
2046
2047 // Generate a convenient CreateX function
2048 if (CanCreateFactoryMethod(struct_def)) {
2049 code += "static create" + GetPrefixedName(struct_def);
2050 code += "(builder:flatbuffers.Builder";
2051 for (auto it = struct_def.fields.vec.begin();
2052 it != struct_def.fields.vec.end(); ++it) {
2053 const auto &field = **it;
2054 if (field.deprecated) continue;
2055 code += ", " + GetArgName(field) + ":" +
2056 GetArgType(imports, struct_def, field, true);
2057 }
2058
2059 code += "):flatbuffers.Offset {\n";
Austin Schuh2dd86a92022-09-14 21:19:23 -07002060 code += " " + object_name + ".start" + GetPrefixedName(struct_def) +
2061 "(builder);\n";
James Kuszmaul8e62b022022-03-22 09:33:25 -07002062
2063 std::string methodPrefix = object_name;
2064 for (auto it = struct_def.fields.vec.begin();
2065 it != struct_def.fields.vec.end(); ++it) {
2066 const auto &field = **it;
2067 if (field.deprecated) continue;
2068
2069 const auto arg_name = GetArgName(field);
2070
2071 if (field.IsScalarOptional()) {
2072 code += " if (" + arg_name + " !== null)\n ";
2073 }
2074
Austin Schuh2dd86a92022-09-14 21:19:23 -07002075 code += " " + methodPrefix + "." + namer_.Method("add", field) + "(";
James Kuszmaul8e62b022022-03-22 09:33:25 -07002076 code += "builder, " + arg_name + ");\n";
2077 }
2078
2079 code += " return " + methodPrefix + ".end" +
2080 GetPrefixedName(struct_def) + "(builder);\n";
2081 code += "}\n";
2082 }
2083 }
2084
2085 if (!struct_def.fixed && parser_.services_.vec.size() != 0) {
2086 auto name = GetPrefixedName(struct_def, "");
2087 code += "\n";
2088 code += "serialize():Uint8Array {\n";
2089 code += " return this.bb!.bytes();\n";
2090 code += "}\n";
2091
2092 code += "\n";
Austin Schuh2dd86a92022-09-14 21:19:23 -07002093 code += "static deserialize(buffer: Uint8Array):" +
2094 namer_.EscapeKeyword(name) + " {\n";
2095 code += " return " + AddImport(imports, struct_def, struct_def).name +
James Kuszmaul8e62b022022-03-22 09:33:25 -07002096 ".getRootAs" + name + "(new flatbuffers.ByteBuffer(buffer))\n";
2097 code += "}\n";
2098 }
2099
2100 if (parser_.opts.generate_object_based_api) {
2101 std::string obj_api_class;
2102 std::string obj_api_unpack_func;
2103 GenObjApi(parser_, struct_def, obj_api_unpack_func, obj_api_class,
2104 imports);
2105
2106 code += obj_api_unpack_func + "}\n" + obj_api_class;
2107 } else {
2108 code += "}\n";
2109 }
2110 }
2111
2112 static bool HasNullDefault(const FieldDef &field) {
2113 return field.IsOptional() && field.value.constant == "null";
2114 }
2115
2116 std::string GetArgType(import_set &imports, const Definition &owner,
2117 const FieldDef &field, bool allowNull) {
2118 return GenTypeName(imports, owner, field.value.type, true,
2119 allowNull && field.IsOptional());
2120 }
2121
2122 std::string GetArgName(const FieldDef &field) {
Austin Schuh2dd86a92022-09-14 21:19:23 -07002123 auto argname = namer_.Variable(field);
2124 if (!IsScalar(field.value.type.base_type)) { argname += "Offset"; }
James Kuszmaul8e62b022022-03-22 09:33:25 -07002125 return argname;
2126 }
2127
2128 std::string GetPrefixedName(const StructDef &struct_def,
2129 const char *prefix = "") {
2130 return prefix + struct_def.name;
2131 }
2132}; // namespace ts
2133} // namespace ts
2134
2135bool GenerateTS(const Parser &parser, const std::string &path,
2136 const std::string &file_name) {
2137 ts::TsGenerator generator(parser, path, file_name);
2138 return generator.generate();
2139}
2140
2141std::string TSMakeRule(const Parser &parser, const std::string &path,
2142 const std::string &file_name) {
2143 std::string filebase =
2144 flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
2145 ts::TsGenerator generator(parser, path, file_name);
2146 std::string make_rule =
2147 generator.GeneratedFileName(path, filebase, parser.opts) + ": ";
2148
2149 auto included_files = parser.GetIncludedFilesRecursive(file_name);
2150 for (auto it = included_files.begin(); it != included_files.end(); ++it) {
2151 make_rule += " " + *it;
2152 }
2153 return make_rule;
2154}
2155
2156} // namespace flatbuffers