Wrap types in namespace for --ts-flat-files and --gen-all (#7451)

* Wrap types in namespace for --ts-flat-files and --gen-all

* Fixes for escaping object types

* Added to generate_code
This commit is contained in:
Derek Bailey 2022-08-16 12:52:26 -07:00 committed by GitHub
parent f7c511957f
commit 82b75407a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 2152 additions and 256 deletions

View File

@ -138,7 +138,8 @@ class BaseGenerator {
std::string WrapInNameSpace(const Namespace *ns,
const std::string &name) const;
std::string WrapInNameSpace(const Definition &def) const;
std::string WrapInNameSpace(const Definition &def,
const std::string &suffix = "") const;
std::string GetNameSpace(const Definition &def) const;

View File

@ -245,6 +245,14 @@ flatc(
schema="monster_test.fbs",
)
# Generate the complete flat file TS of monster.
flatc(
["--ts", "--gen-all", "--ts-flat-files"],
include="include_test",
schema="monster_test.fbs",
prefix="ts/ts-flat-files"
)
flatc(
BASE_OPTS + TS_OPTS + ["-b"],
include="include_test",

View File

@ -131,8 +131,9 @@ std::string BaseGenerator::WrapInNameSpace(const Namespace *ns,
return qualified_name + name;
}
std::string BaseGenerator::WrapInNameSpace(const Definition &def) const {
return WrapInNameSpace(def.defined_namespace, def.name);
std::string BaseGenerator::WrapInNameSpace(const Definition &def,
const std::string &suffix) const {
return WrapInNameSpace(def.defined_namespace, def.name + suffix);
}
std::string BaseGenerator::GetNameSpace(const Definition &def) const {

View File

@ -14,7 +14,6 @@
* limitations under the License.
*/
// independent from idl_parser, since this code is not needed for most clients
#include <algorithm>
#include <cassert>
#include <unordered_map>
@ -33,6 +32,7 @@ struct ImportDefinition {
std::string export_statement;
std::string bare_file_path;
std::string rel_file_path;
std::string object_name;
const Definition *dependent = nullptr;
const Definition *dependency = nullptr;
};
@ -49,7 +49,7 @@ class TsGenerator : public BaseGenerator {
TsGenerator(const Parser &parser, const std::string &path,
const std::string &file_name)
: BaseGenerator(parser, path, file_name, "", ".", "ts") {
: BaseGenerator(parser, path, file_name, "", "_", "ts") {
// clang-format off
// List of keywords retrieved from here:
@ -117,6 +117,47 @@ class TsGenerator : public BaseGenerator {
return true;
}
bool IncludeNamespace() const {
// When generating a single flat file and all its includes, namespaces are
// important to avoid type name clashes.
return parser_.opts.ts_flat_file && parser_.opts.generate_all;
}
// Make the provided def wrapped in namespaced if configured to do so,
// otherwise just return the name.
std::string MakeNamespaced(const Definition &def,
const std::string &suffix = "") {
if (IncludeNamespace()) { return WrapInNameSpace(def, suffix); }
return def.name + suffix;
}
std::string GetTypeName(const EnumDef &def, const bool = false,
const bool force_ns_wrap = false) {
std::string base_name = def.name;
if (IncludeNamespace() || force_ns_wrap) {
base_name = WrapInNameSpace(def.defined_namespace, base_name);
}
return EscapeKeyword(base_name);
}
std::string GetTypeName(const StructDef &def, const bool object_api = false,
const bool force_ns_wrap = false) {
std::string base_name = def.name;
if (object_api && parser_.opts.generate_object_based_api) {
base_name =
parser_.opts.object_prefix + base_name + parser_.opts.object_suffix;
}
if (IncludeNamespace() || force_ns_wrap) {
base_name = WrapInNameSpace(def.defined_namespace, base_name);
}
return EscapeKeyword(base_name);
}
// Save out the generated code for a single class while adding
// declaration boilerplate.
bool SaveType(const Definition &definition, const std::string &class_code,
@ -145,6 +186,7 @@ class TsGenerator : public BaseGenerator {
if (parser_.opts.ts_flat_file) {
flat_file_ += code;
flat_file_ += "\n";
flat_file_definitions_.insert(&definition);
return true;
} else {
@ -152,15 +194,6 @@ class TsGenerator : public BaseGenerator {
NamespaceDir(*definition.defined_namespace, true) +
ConvertCase(definition.name, Case::kDasher, Case::kUpperCamel);
// Special case for the root table, generate an export statement
if (&definition == parser_.root_struct_def_) {
ImportDefinition import;
import.name = definition.name;
import.export_statement =
"export { " + import.name + " } from './" + basename + "';";
imports.insert(std::make_pair(import.name, import));
}
return SaveFile((basename + ".ts").c_str(), code, false);
}
}
@ -308,8 +341,8 @@ class TsGenerator : public BaseGenerator {
std::string &code = *code_ptr;
GenDocComment(enum_def.doc_comment, code_ptr);
code += "export enum ";
// TODO(7445): figure out if the export needs a namespace for ts-flat-files
code += EscapeKeyword(enum_def.name) + " {\n";
code += GetTypeName(enum_def);
code += " {\n";
for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
auto &ev = **it;
if (!ev.doc_comment.empty()) {
@ -347,7 +380,7 @@ class TsGenerator : public BaseGenerator {
code += GenUnionConvFunc(enum_def.underlying_type, imports);
}
code += "\n\n";
code += "\n";
}
static std::string GenType(const Type &type) {
@ -408,8 +441,9 @@ class TsGenerator : public BaseGenerator {
}
default: {
if (auto val = value.type.enum_def->FindByValue(value.constant)) {
return EscapeKeyword(AddImport(imports, *value.type.enum_def,
*value.type.enum_def)) +
return AddImport(imports, *value.type.enum_def,
*value.type.enum_def)
.name +
"." + EscapeKeyword(val->name);
} else {
return value.constant;
@ -447,7 +481,7 @@ class TsGenerator : public BaseGenerator {
if (IsString(type)) {
name = "string|Uint8Array";
} else {
name = EscapeKeyword(AddImport(imports, owner, *type.struct_def));
name = AddImport(imports, owner, *type.struct_def).name;
}
return allowNull ? (name + "|null") : name;
}
@ -460,7 +494,8 @@ class TsGenerator : public BaseGenerator {
default:
if (IsScalar(type.base_type)) {
if (type.enum_def) {
const auto enum_name = AddImport(imports, owner, *type.enum_def);
const auto enum_name =
AddImport(imports, owner, *type.enum_def).name;
return allowNull ? (enum_name + "|null") : enum_name;
}
return allowNull ? "number|null" : "number";
@ -586,16 +621,6 @@ class TsGenerator : public BaseGenerator {
}
}
static std::string GetObjApiClassName(const StructDef &sd,
const IDLOptions &opts) {
return GetObjApiClassName(sd.name, opts);
}
static std::string GetObjApiClassName(const std::string &name,
const IDLOptions &opts) {
return opts.object_prefix + name + opts.object_suffix;
}
bool UnionHasStringType(const EnumDef &union_enum) {
return std::any_of(union_enum.Vals().begin(), union_enum.Vals().end(),
[](const EnumVal *ev) {
@ -624,7 +649,7 @@ class TsGenerator : public BaseGenerator {
if (IsString(ev.union_type)) {
type = "string"; // no need to wrap string type in namespace
} else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
type = AddImport(imports, union_enum, *ev.union_type.struct_def);
type = AddImport(imports, union_enum, *ev.union_type.struct_def).name;
} else {
FLATBUFFERS_ASSERT(false);
}
@ -638,173 +663,142 @@ class TsGenerator : public BaseGenerator {
return ret;
}
std::string AddImport(import_set &imports, const Definition &dependent,
const StructDef &dependency) {
std::string ns;
const auto &depc_comps = dependency.defined_namespace->components;
for (auto it = depc_comps.begin(); it != depc_comps.end(); it++) {
ns += *it;
}
std::string unique_name = ns + dependency.name;
std::string import_name = dependency.name;
std::string long_import_name;
if (imports.find(unique_name) != imports.end())
return imports.find(unique_name)->second.name;
static bool CheckIfNameClashes(const import_set &imports,
const std::string &name) {
// TODO: this would be better as a hashset.
for (auto it = imports.begin(); it != imports.end(); it++) {
if (it->second.name == import_name) {
long_import_name = ns + import_name;
break;
if (it->second.name == name) { return true; }
}
return false;
}
std::string GenSymbolExpression(const StructDef &struct_def,
const bool has_name_clash,
const std::string &import_name,
const std::string &name,
const std::string &object_name) {
std::string symbols_expression;
if (has_name_clash) {
// We have a name clash
symbols_expression += import_name + " as " + name;
if (parser_.opts.generate_object_based_api) {
symbols_expression += ", " +
GetTypeName(struct_def, /*object_api =*/true) +
" as " + object_name;
}
} else {
// No name clash, use the provided name
symbols_expression += name;
if (parser_.opts.generate_object_based_api) {
symbols_expression += ", " + object_name;
}
}
return symbols_expression;
}
std::string GenSymbolExpression(const EnumDef &enum_def,
const bool has_name_clash,
const std::string &import_name,
const std::string &name,
const std::string &) {
std::string symbols_expression;
if (has_name_clash) {
symbols_expression += import_name + " as " + name;
} else {
symbols_expression += name;
}
if (enum_def.is_union) {
symbols_expression += ", unionTo" + name;
symbols_expression += ", unionListTo" + name;
}
return symbols_expression;
}
template<typename DefintionT>
ImportDefinition AddImport(import_set &imports, const Definition &dependent,
const DefintionT &dependency) {
// The unique name of the dependency, fully qualified in its namespace.
const std::string unique_name = GetTypeName(
dependency, /*object_api = */ false, /*force_ns_wrap=*/true);
// Look if we have already added this import and return its name if found.
const auto import_pair = imports.find(unique_name);
if (import_pair != imports.end()) { return import_pair->second; }
// Check if this name would have a name clash with another type. Just use
// the "base" name (properlly escaped) without any namespacing applied.
const std::string import_name = EscapeKeyword(dependency.name);
const bool has_name_clash = CheckIfNameClashes(imports, import_name);
// If we have a name clash, use the unique name, otherwise use simple name.
std::string name = has_name_clash ? unique_name : import_name;
const std::string object_name =
GetTypeName(dependency, /*object_api=*/true, has_name_clash);
if (parser_.opts.ts_flat_file) {
// In flat-file generation, do not attempt to import things from ourselves
// *and* do not wrap namespaces (note that this does override the logic
// above, but since we force all non-self-imports to use namespace-based
// names in flat file generation, it's fine).
if (dependent.file == dependency.file) {
long_import_name = import_name;
name = import_name;
} else {
long_import_name = ns + import_name;
std::string file =
const std::string file =
RelativeToRootPath(StripFileName(AbsolutePath(dependent.file)),
dependency.file)
// Strip the leading //
.substr(2);
flat_file_import_declarations_[file][import_name] = long_import_name;
if (parser_.opts.generate_object_based_api) {
flat_file_import_declarations_[file][import_name + "T"] =
long_import_name + "T";
flat_file_import_declarations_[file][import_name] = name;
if (parser_.opts.generate_object_based_api &&
typeid(dependency) == typeid(StructDef)) {
flat_file_import_declarations_[file][import_name + "T"] = object_name;
}
}
}
std::string import_statement;
std::string export_statement;
import_statement += "import { ";
export_statement += "export { ";
std::string symbols_expression;
if (long_import_name.empty()) {
symbols_expression += EscapeKeyword(import_name);
if (parser_.opts.generate_object_based_api)
symbols_expression += ", " + import_name + "T";
} else {
symbols_expression += EscapeKeyword(dependency.name) + " as " +
EscapeKeyword(long_import_name);
if (parser_.opts.generate_object_based_api)
symbols_expression +=
", " + dependency.name + "T as " + long_import_name + "T";
}
import_statement += symbols_expression + " } from '";
export_statement += symbols_expression + " } from '";
const std::string symbols_expression = GenSymbolExpression(
dependency, has_name_clash, import_name, name, object_name);
std::string bare_file_path;
std::string rel_file_path;
const auto &dep_comps = dependent.defined_namespace->components;
for (size_t i = 0; i < dep_comps.size(); i++)
for (size_t i = 0; i < dep_comps.size(); i++) {
rel_file_path += i == 0 ? ".." : (kPathSeparator + std::string(".."));
if (dep_comps.size() == 0) rel_file_path += ".";
for (auto it = depc_comps.begin(); it != depc_comps.end(); it++)
bare_file_path +=
kPathSeparator + ConvertCase(*it, Case::kDasher, Case::kUpperCamel);
bare_file_path +=
kPathSeparator +
ConvertCase(dependency.name, Case::kDasher, Case::kUpperCamel);
rel_file_path += bare_file_path;
import_statement += rel_file_path + "';";
export_statement += "." + bare_file_path + "';";
ImportDefinition import;
import.name = long_import_name.empty() ? import_name : long_import_name;
import.bare_file_path = bare_file_path;
import.rel_file_path = rel_file_path;
import.import_statement = import_statement;
import.export_statement = export_statement;
import.dependency = &dependency;
import.dependent = &dependent;
imports.insert(std::make_pair(unique_name, import));
return import.name;
}
}
if (dep_comps.size() == 0) { rel_file_path += "."; }
// TODO: largely (but not identical) duplicated code from above couln't find a
// good way to refactor
std::string AddImport(import_set &imports, const Definition &dependent,
const EnumDef &dependency) {
std::string ns;
const auto &depc_comps = dependency.defined_namespace->components;
for (auto it = depc_comps.begin(); it != depc_comps.end(); it++) {
ns += *it;
}
std::string unique_name = ns + dependency.name;
std::string import_name = EscapeKeyword(dependency.name);
std::string long_import_name;
if (imports.find(unique_name) != imports.end()) {
return imports.find(unique_name)->second.name;
}
for (auto it = imports.begin(); it != imports.end(); it++) {
if (it->second.name == import_name) {
long_import_name = ns + import_name;
break;
}
}
if (parser_.opts.ts_flat_file) {
// In flat-file generation, do not attempt to import things from ourselves
// *and* do not wrap namespaces (note that this does override the logic
// above, but since we force all non-self-imports to use namespace-based
// names in flat file generation, it's fine).
if (dependent.file == dependency.file) {
long_import_name = import_name;
} else {
long_import_name = ns + import_name;
std::string file =
RelativeToRootPath(StripFileName(AbsolutePath(dependent.file)),
dependency.file)
// Strip the leading //
.substr(2);
flat_file_import_declarations_[file][import_name] = long_import_name;
}
}
std::string import_statement;
std::string export_statement;
import_statement += "import { ";
export_statement += "export { ";
std::string symbols_expression;
if (long_import_name.empty())
symbols_expression += import_name;
else
symbols_expression += EscapeKeyword(dependency.name) + " as " +
EscapeKeyword(long_import_name);
if (dependency.is_union) {
symbols_expression += ", unionTo" + import_name;
symbols_expression += ", unionListTo" + import_name;
}
import_statement += symbols_expression + " } from '";
export_statement += symbols_expression + " } from '";
std::string bare_file_path;
std::string rel_file_path;
const auto &dep_comps = dependent.defined_namespace->components;
for (size_t i = 0; i < dep_comps.size(); i++)
rel_file_path += i == 0 ? ".." : (kPathSeparator + std::string(".."));
if (dep_comps.size() == 0) rel_file_path += ".";
for (auto it = depc_comps.begin(); it != depc_comps.end(); it++)
bare_file_path +=
kPathSeparator + ConvertCase(*it, Case::kDasher, Case::kUpperCamel);
}
bare_file_path +=
kPathSeparator +
ConvertCase(dependency.name, Case::kDasher, Case::kUpperCamel);
rel_file_path += bare_file_path;
import_statement += rel_file_path + "';";
export_statement += "." + bare_file_path + "';";
ImportDefinition import;
import.name = long_import_name.empty() ? import_name : long_import_name;
import.name = name;
import.object_name = object_name;
import.bare_file_path = bare_file_path;
import.rel_file_path = rel_file_path;
import.import_statement = import_statement;
import.export_statement = export_statement;
import.import_statement =
"import { " + symbols_expression + " } from '" + rel_file_path + "';";
import.export_statement =
"export { " + symbols_expression + " } from '." + bare_file_path + "';";
import.dependency = &dependency;
import.dependent = &dependent;
imports.insert(std::make_pair(unique_name, import));
return import.name;
return import;
}
void AddImport(import_set &imports, std::string import_name,
@ -819,7 +813,7 @@ class TsGenerator : public BaseGenerator {
// Generate a TS union type based on a union's enum
std::string GenObjApiUnionTypeTS(import_set &imports,
const StructDef &dependent,
const IDLOptions &opts,
const IDLOptions &,
const EnumDef &union_enum) {
std::string ret = "";
std::set<std::string> type_list;
@ -833,8 +827,8 @@ class TsGenerator : public BaseGenerator {
if (IsString(ev.union_type)) {
type = "string"; // no need to wrap string type in namespace
} else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
type = GetObjApiClassName(
AddImport(imports, dependent, *ev.union_type.struct_def), opts);
type = AddImport(imports, dependent, *ev.union_type.struct_def)
.object_name;
} else {
FLATBUFFERS_ASSERT(false);
}
@ -866,12 +860,12 @@ class TsGenerator : public BaseGenerator {
const auto valid_union_type_with_null = valid_union_type + "|null";
auto ret = "\n\nexport function " + GenUnionConvFuncName(enum_def) +
"(\n type: " + enum_def.name +
"(\n type: " + GetTypeName(enum_def) +
",\n accessor: (obj:" + valid_union_type + ") => " +
valid_union_type_with_null +
"\n): " + valid_union_type_with_null + " {\n";
const auto enum_type = AddImport(imports, enum_def, enum_def);
const auto enum_type = AddImport(imports, enum_def, enum_def).name;
const auto union_enum_loop = [&](const std::string &accessor_str) {
ret += " switch(" + enum_type + "[type]) {\n";
@ -888,7 +882,7 @@ class TsGenerator : public BaseGenerator {
ret += "return " + accessor_str + "'') as string;";
} else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
const auto type =
AddImport(imports, enum_def, *ev.union_type.struct_def);
AddImport(imports, enum_def, *ev.union_type.struct_def).name;
ret += "return " + accessor_str + "new " + type + "())! as " +
type + ";";
} else {
@ -905,7 +899,7 @@ class TsGenerator : public BaseGenerator {
ret += "}";
ret += "\n\nexport function " + GenUnionListConvFuncName(enum_def) +
"(\n type: " + enum_def.name +
"(\n type: " + GetTypeName(enum_def) +
", \n accessor: (index: number, obj:" + valid_union_type +
") => " + valid_union_type_with_null +
", \n index: number\n): " + valid_union_type_with_null + " {\n";
@ -927,7 +921,7 @@ class TsGenerator : public BaseGenerator {
const bool is_array = false) {
if (union_type.enum_def) {
const auto &enum_def = *union_type.enum_def;
const auto enum_type = AddImport(imports, dependent, enum_def);
const auto enum_type = AddImport(imports, dependent, enum_def).name;
const std::string union_accessor = "this." + field_name;
const auto union_has_string = UnionHasStringType(enum_def);
@ -1029,7 +1023,7 @@ class TsGenerator : public BaseGenerator {
void GenObjApi(const Parser &parser, StructDef &struct_def,
std::string &obj_api_unpack_func, std::string &obj_api_class,
import_set &imports) {
const auto class_name = GetObjApiClassName(struct_def, parser.opts);
const auto class_name = GetTypeName(struct_def, /*object_api=*/true);
std::string unpack_func = "\nunpack(): " + class_name +
" {\n return new " + class_name + "(" +
@ -1049,8 +1043,7 @@ class TsGenerator : public BaseGenerator {
std::string pack_func_offset_decl;
std::string pack_func_create_call;
const auto struct_name =
EscapeKeyword(AddImport(imports, struct_def, struct_def));
const auto struct_name = AddImport(imports, struct_def, struct_def).name;
if (has_create) {
pack_func_create_call = " return " + struct_name + ".create" +
@ -1113,8 +1106,7 @@ class TsGenerator : public BaseGenerator {
switch (field.value.type.base_type) {
case BASE_TYPE_STRUCT: {
const auto &sd = *field.value.type.struct_def;
field_type += GetObjApiClassName(AddImport(imports, struct_def, sd),
parser.opts);
field_type += AddImport(imports, struct_def, sd).object_name;
const std::string field_accessor = "this." + field_name + "()";
field_val = GenNullCheckConditional(field_accessor,
@ -1143,7 +1135,8 @@ class TsGenerator : public BaseGenerator {
switch (vectortype.base_type) {
case BASE_TYPE_STRUCT: {
const auto &sd = *field.value.type.struct_def;
field_type += GetObjApiClassName(sd, parser.opts);
field_type += GetTypeName(sd, /*object_api=*/true);
;
field_type += ")[]";
field_val = GenBBAccess() + ".createObjList(" +
@ -1154,14 +1147,12 @@ class TsGenerator : public BaseGenerator {
field_offset_decl =
"builder.createStructOffsetList(this." +
field_name_escaped + ", " +
EscapeKeyword(
AddImport(imports, struct_def, struct_def)) +
AddImport(imports, struct_def, struct_def).name +
".start" + ConvertCase(field_name, Case::kUpperCamel) +
"Vector)";
} else {
field_offset_decl =
EscapeKeyword(
AddImport(imports, struct_def, struct_def)) +
AddImport(imports, struct_def, struct_def).name +
".create" + ConvertCase(field_name, Case::kUpperCamel) +
"Vector(builder, builder.createObjectOffsetList(" +
"this." + field_name_escaped + "))";
@ -1176,7 +1167,7 @@ class TsGenerator : public BaseGenerator {
field_binded_method + ", this." + field_name +
"Length())";
field_offset_decl =
EscapeKeyword(AddImport(imports, struct_def, struct_def)) +
AddImport(imports, struct_def, struct_def).name +
".create" + ConvertCase(field_name, Case::kUpperCamel) +
"Vector(builder, builder.createObjectOffsetList(" +
"this." + field_name_escaped + "))";
@ -1191,7 +1182,7 @@ class TsGenerator : public BaseGenerator {
vectortype, true);
field_offset_decl =
EscapeKeyword(AddImport(imports, struct_def, struct_def)) +
AddImport(imports, struct_def, struct_def).name +
".create" + ConvertCase(field_name, Case::kUpperCamel) +
"Vector(builder, builder.createObjectOffsetList(" +
"this." + field_name_escaped + "))";
@ -1211,7 +1202,7 @@ class TsGenerator : public BaseGenerator {
"Length())";
field_offset_decl =
EscapeKeyword(AddImport(imports, struct_def, struct_def)) +
AddImport(imports, struct_def, struct_def).name +
".create" + ConvertCase(field_name, Case::kUpperCamel) +
"Vector(builder, this." + field_name_escaped + ")";
@ -1301,10 +1292,10 @@ class TsGenerator : public BaseGenerator {
pack_func_create_call += "return " + struct_name + ".end" +
GetPrefixedName(struct_def) + "(builder);";
}
obj_api_class = "\nexport class " +
GetObjApiClassName(struct_def, parser.opts) + " {\n";
obj_api_class = "\n";
obj_api_class += "export class ";
obj_api_class += GetTypeName(struct_def, /*object_api=*/true);
obj_api_class += " {\n";
obj_api_class += constructor_func;
obj_api_class += pack_func_prototype + pack_func_offset_decl +
pack_func_create_call + "\n}";
@ -1335,14 +1326,17 @@ class TsGenerator : public BaseGenerator {
if (struct_def.generated) return;
std::string &code = *code_ptr;
std::string object_name;
std::string object_namespace = GetNameSpace(struct_def);
// Special case for the root struct, since no one will necessarily reference
// it, we have to explicitly add it to the import list.
if (&struct_def == parser_.root_struct_def_) {
AddImport(imports, struct_def, struct_def);
}
const std::string object_name = GetTypeName(struct_def);
// Emit constructor
object_name = EscapeKeyword(struct_def.name);
GenDocComment(struct_def.doc_comment, code_ptr);
code += "export class ";
// TODO(7445): figure out if the export needs a namespace for ts-flat-files
code += object_name;
code += " {\n";
code += " bb: flatbuffers.ByteBuffer|null = null;\n";
@ -1431,8 +1425,9 @@ class TsGenerator : public BaseGenerator {
else {
switch (field.value.type.base_type) {
case BASE_TYPE_STRUCT: {
const auto type = EscapeKeyword(
AddImport(imports, struct_def, *field.value.type.struct_def));
const auto type =
AddImport(imports, struct_def, *field.value.type.struct_def)
.name;
GenDocComment(field.doc_comment, code_ptr);
code += ConvertCase(field.name, Case::kLowerCamel);
code += "(obj?:" + type + "):" + type + "|null {\n";
@ -1810,8 +1805,7 @@ class TsGenerator : public BaseGenerator {
code += "\n";
code += "static deserialize(buffer: Uint8Array):" + EscapeKeyword(name) +
" {\n";
code += " return " +
EscapeKeyword(AddImport(imports, struct_def, struct_def)) +
code += " return " + AddImport(imports, struct_def, struct_def).name +
".getRootAs" + name + "(new flatbuffers.ByteBuffer(buffer))\n";
code += "}\n";
}

0
tests/TypeScriptTest.py Normal file → Executable file
View File

View File

@ -0,0 +1,14 @@
include "baz/baz_with_ns.fbs";
include "baz/baz.fbs";
namespace bar;
table Bar {
baz:baz.Baz;
baz2:Baz;
foo:Foo;
}
table Foo {
a:int;
}

View File

@ -0,0 +1,8 @@
namespace baz;
enum Baz : short {
None = 0,
Red,
Green,
Blue,
}

View File

@ -66,7 +66,7 @@ class TsTests():
assert_file_and_contents(
"foo_with_ns_generated.ts",
[
"export { Bar } from './bar';",
"export { Bar } from './bar/bar';",
"export { Foo } from './something/foo';",
],
)
@ -77,7 +77,7 @@ class TsTests():
"something/foo.ts",
[
"export class Foo {",
"import { Bar } from '../bar';",
"import { Bar } from '../bar/bar';",
],
)
@ -142,6 +142,23 @@ class TsTests():
# The root type Foo should not be generated in its own file.
assert_file_doesnt_exists("foo.ts")
def FlatFilesWithNamespace(self):
# Generate just foo with the flat files option
flatc(["--ts", "--ts-flat-files", "foo_with_ns.fbs"])
# Should generate a single file that imports bar as a single file, and
# exports the Foo table.
assert_file_and_contents(
"foo_with_ns_generated.ts",
[
"import {Bar as Bar} from './bar_with_ns_generated';",
"export class Foo {",
],
)
# The root type Foo should not be generated in its own file.
assert_file_doesnt_exists("foo.ts")
def FlatFilesMultipleFiles(self):
# Generate both foo and bar with the flat files option
flatc(["--ts", "--ts-flat-files", "foo.fbs", "bar/bar.fbs"])
@ -194,3 +211,32 @@ class TsTests():
assert_file_doesnt_exists("foo.ts")
assert_file_doesnt_exists("bar.ts")
assert_file_doesnt_exists("baz.ts")
def ZFlatFilesGenAllWithNamespacing(self):
# Generate foo with all of its dependents with the flat files option
flatc(["--ts", "--ts-flat-files", "--gen-all", "foo_with_ns.fbs"])
# Should generate a single foo file
assert_file_and_contents(
"foo_with_ns_generated.ts",
# Should export each of the types within the single file
[
"export class bar_Bar {",
"export class bar_Foo {",
"export enum Baz {",
"export enum baz_Baz {",
"export class something_Foo {"
],
# No includes for the dependent types should be present.
doesnt_contain=[
"import {Bar as Bar}",
"import {Baz as Baz}",
],
)
# The types Foo, Bar and Baz should not be generated in their own files.
assert_file_doesnt_exists("foo.ts")
assert_file_doesnt_exists("bar.ts")
assert_file_doesnt_exists("baz.ts")

View File

@ -1,7 +1,9 @@
include "bar/bar.fbs";
include "bar/bar_with_ns.fbs";
table Foo {
bar:Bar;
bar2:bar.Bar;
}
root_type Foo;

View File

@ -1,9 +1,9 @@
include "bar/bar.fbs";
include "bar/bar_with_ns.fbs";
namespace something;
table Foo {
bar:Bar;
bar:bar.Bar;
}
root_type Foo;

View File

@ -27,3 +27,5 @@ print("{0} of {1} tests passed".format(passing, passing + failing))
if failing > 0:
sys.exit(1)
# TsTests().Base()

View File

@ -1,7 +1,6 @@
// automatically generated by the FlatBuffers compiler, do not modify
export { Monster } from './my-game/example/monster';
export { Monster as MyGameExample2Monster, MonsterT as MyGameExample2MonsterT } from './my-game/example2/monster';
export { Monster as MyGame_Example2_Monster, MonsterT as MyGame_Example2_MonsterT } from './my-game/example2/monster';
export { Ability, AbilityT } from './my-game/example/ability';
export { Any, unionToAny, unionListToAny } from './my-game/example/any';
export { AnyAmbiguousAliases, unionToAnyAmbiguousAliases, unionListToAnyAmbiguousAliases } from './my-game/example/any-ambiguous-aliases';

View File

@ -24,7 +24,7 @@ export class Ability {
return true;
}
static getFullyQualifiedName() {
return 'MyGame.Example.Ability';
return 'MyGame_Example_Ability';
}
static sizeOf() {
return 8;

View File

@ -32,7 +32,7 @@ mutate_distance(value:number):boolean {
}
static getFullyQualifiedName():string {
return 'MyGame.Example.Ability';
return 'MyGame_Example_Ability';
}
static sizeOf():number {

View File

@ -36,4 +36,3 @@ export function unionListToAnyAmbiguousAliases(
default: return null;
}
}

View File

@ -1,5 +1,5 @@
// automatically generated by the FlatBuffers compiler, do not modify
import { Monster as MyGameExample2Monster } from '../../my-game/example2/monster';
import { Monster as MyGame_Example2_Monster } from '../../my-game/example2/monster';
import { Monster } from '../../my-game/example/monster';
import { TestSimpleTableWithEnum } from '../../my-game/example/test-simple-table-with-enum';
export var AnyUniqueAliases;
@ -14,7 +14,7 @@ export function unionToAnyUniqueAliases(type, accessor) {
case 'NONE': return null;
case 'M': return accessor(new Monster());
case 'TS': return accessor(new TestSimpleTableWithEnum());
case 'M2': return accessor(new MyGameExample2Monster());
case 'M2': return accessor(new MyGame_Example2_Monster());
default: return null;
}
}
@ -23,7 +23,7 @@ export function unionListToAnyUniqueAliases(type, accessor, index) {
case 'NONE': return null;
case 'M': return accessor(index, new Monster());
case 'TS': return accessor(index, new TestSimpleTableWithEnum());
case 'M2': return accessor(index, new MyGameExample2Monster());
case 'M2': return accessor(index, new MyGame_Example2_Monster());
default: return null;
}
}

View File

@ -1,6 +1,6 @@
// automatically generated by the FlatBuffers compiler, do not modify
import { Monster as MyGameExample2Monster, MonsterT as MyGameExample2MonsterT } from '../../my-game/example2/monster';
import { Monster as MyGame_Example2_Monster, MonsterT as MyGame_Example2_MonsterT } from '../../my-game/example2/monster';
import { Monster, MonsterT } from '../../my-game/example/monster';
import { TestSimpleTableWithEnum, TestSimpleTableWithEnumT } from '../../my-game/example/test-simple-table-with-enum';
@ -14,28 +14,27 @@ export enum AnyUniqueAliases {
export function unionToAnyUniqueAliases(
type: AnyUniqueAliases,
accessor: (obj:Monster|MyGameExample2Monster|TestSimpleTableWithEnum) => Monster|MyGameExample2Monster|TestSimpleTableWithEnum|null
): Monster|MyGameExample2Monster|TestSimpleTableWithEnum|null {
accessor: (obj:Monster|MyGame_Example2_Monster|TestSimpleTableWithEnum) => Monster|MyGame_Example2_Monster|TestSimpleTableWithEnum|null
): Monster|MyGame_Example2_Monster|TestSimpleTableWithEnum|null {
switch(AnyUniqueAliases[type]) {
case 'NONE': return null;
case 'M': return accessor(new Monster())! as Monster;
case 'TS': return accessor(new TestSimpleTableWithEnum())! as TestSimpleTableWithEnum;
case 'M2': return accessor(new MyGameExample2Monster())! as MyGameExample2Monster;
case 'M2': return accessor(new MyGame_Example2_Monster())! as MyGame_Example2_Monster;
default: return null;
}
}
export function unionListToAnyUniqueAliases(
type: AnyUniqueAliases,
accessor: (index: number, obj:Monster|MyGameExample2Monster|TestSimpleTableWithEnum) => Monster|MyGameExample2Monster|TestSimpleTableWithEnum|null,
accessor: (index: number, obj:Monster|MyGame_Example2_Monster|TestSimpleTableWithEnum) => Monster|MyGame_Example2_Monster|TestSimpleTableWithEnum|null,
index: number
): Monster|MyGameExample2Monster|TestSimpleTableWithEnum|null {
): Monster|MyGame_Example2_Monster|TestSimpleTableWithEnum|null {
switch(AnyUniqueAliases[type]) {
case 'NONE': return null;
case 'M': return accessor(index, new Monster())! as Monster;
case 'TS': return accessor(index, new TestSimpleTableWithEnum())! as TestSimpleTableWithEnum;
case 'M2': return accessor(index, new MyGameExample2Monster())! as MyGameExample2Monster;
case 'M2': return accessor(index, new MyGame_Example2_Monster())! as MyGame_Example2_Monster;
default: return null;
}
}

View File

@ -1,5 +1,5 @@
// automatically generated by the FlatBuffers compiler, do not modify
import { Monster as MyGameExample2Monster } from '../../my-game/example2/monster';
import { Monster as MyGame_Example2_Monster } from '../../my-game/example2/monster';
import { Monster } from '../../my-game/example/monster';
import { TestSimpleTableWithEnum } from '../../my-game/example/test-simple-table-with-enum';
export var Any;
@ -14,7 +14,7 @@ export function unionToAny(type, accessor) {
case 'NONE': return null;
case 'Monster': return accessor(new Monster());
case 'TestSimpleTableWithEnum': return accessor(new TestSimpleTableWithEnum());
case 'MyGame_Example2_Monster': return accessor(new MyGameExample2Monster());
case 'MyGame_Example2_Monster': return accessor(new MyGame_Example2_Monster());
default: return null;
}
}
@ -23,7 +23,7 @@ export function unionListToAny(type, accessor, index) {
case 'NONE': return null;
case 'Monster': return accessor(index, new Monster());
case 'TestSimpleTableWithEnum': return accessor(index, new TestSimpleTableWithEnum());
case 'MyGame_Example2_Monster': return accessor(index, new MyGameExample2Monster());
case 'MyGame_Example2_Monster': return accessor(index, new MyGame_Example2_Monster());
default: return null;
}
}

View File

@ -1,6 +1,6 @@
// automatically generated by the FlatBuffers compiler, do not modify
import { Monster as MyGameExample2Monster, MonsterT as MyGameExample2MonsterT } from '../../my-game/example2/monster';
import { Monster as MyGame_Example2_Monster, MonsterT as MyGame_Example2_MonsterT } from '../../my-game/example2/monster';
import { Monster, MonsterT } from '../../my-game/example/monster';
import { TestSimpleTableWithEnum, TestSimpleTableWithEnumT } from '../../my-game/example/test-simple-table-with-enum';
@ -14,28 +14,27 @@ export enum Any {
export function unionToAny(
type: Any,
accessor: (obj:Monster|MyGameExample2Monster|TestSimpleTableWithEnum) => Monster|MyGameExample2Monster|TestSimpleTableWithEnum|null
): Monster|MyGameExample2Monster|TestSimpleTableWithEnum|null {
accessor: (obj:Monster|MyGame_Example2_Monster|TestSimpleTableWithEnum) => Monster|MyGame_Example2_Monster|TestSimpleTableWithEnum|null
): Monster|MyGame_Example2_Monster|TestSimpleTableWithEnum|null {
switch(Any[type]) {
case 'NONE': return null;
case 'Monster': return accessor(new Monster())! as Monster;
case 'TestSimpleTableWithEnum': return accessor(new TestSimpleTableWithEnum())! as TestSimpleTableWithEnum;
case 'MyGame_Example2_Monster': return accessor(new MyGameExample2Monster())! as MyGameExample2Monster;
case 'MyGame_Example2_Monster': return accessor(new MyGame_Example2_Monster())! as MyGame_Example2_Monster;
default: return null;
}
}
export function unionListToAny(
type: Any,
accessor: (index: number, obj:Monster|MyGameExample2Monster|TestSimpleTableWithEnum) => Monster|MyGameExample2Monster|TestSimpleTableWithEnum|null,
accessor: (index: number, obj:Monster|MyGame_Example2_Monster|TestSimpleTableWithEnum) => Monster|MyGame_Example2_Monster|TestSimpleTableWithEnum|null,
index: number
): Monster|MyGameExample2Monster|TestSimpleTableWithEnum|null {
): Monster|MyGame_Example2_Monster|TestSimpleTableWithEnum|null {
switch(Any[type]) {
case 'NONE': return null;
case 'Monster': return accessor(index, new Monster())! as Monster;
case 'TestSimpleTableWithEnum': return accessor(index, new TestSimpleTableWithEnum())! as TestSimpleTableWithEnum;
case 'MyGame_Example2_Monster': return accessor(index, new MyGameExample2Monster())! as MyGameExample2Monster;
case 'MyGame_Example2_Monster': return accessor(index, new MyGame_Example2_Monster())! as MyGame_Example2_Monster;
default: return null;
}
}

View File

@ -17,4 +17,3 @@ export enum Color {
*/
Blue = 8
}

View File

@ -5,4 +5,3 @@ export enum LongEnum {
LongTwo = '4',
LongBig = '1099511627776'
}

View File

@ -527,7 +527,7 @@ export class Monster {
return true;
}
static getFullyQualifiedName() {
return 'MyGame.Example.Monster';
return 'MyGame_Example_Monster';
}
static startMonster(builder) {
builder.startObject(54);

View File

@ -2,7 +2,7 @@
import * as flatbuffers from 'flatbuffers';
import { Monster as MyGameExample2Monster, MonsterT as MyGameExample2MonsterT } from '../../my-game/example2/monster';
import { Monster as MyGame_Example2_Monster, MonsterT as MyGame_Example2_MonsterT } from '../../my-game/example2/monster';
import { Ability, AbilityT } from '../../my-game/example/ability';
import { Any, unionToAny, unionListToAny } from '../../my-game/example/any';
import { AnyAmbiguousAliases, unionToAnyAmbiguousAliases, unionListToAnyAmbiguousAliases } from '../../my-game/example/any-ambiguous-aliases';
@ -684,7 +684,7 @@ mutate_long_enum_normal_default(value:bigint):boolean {
}
static getFullyQualifiedName():string {
return 'MyGame.Example.Monster';
return 'MyGame_Example_Monster';
}
static startMonster(builder:flatbuffers.Builder) {
@ -1296,7 +1296,7 @@ constructor(
public inventory: (number)[] = [],
public color: Color = Color.Blue,
public testType: Any = Any.NONE,
public test: MonsterT|MyGameExample2MonsterT|TestSimpleTableWithEnumT|null = null,
public test: MonsterT|MyGame_Example2_MonsterT|TestSimpleTableWithEnumT|null = null,
public test4: (TestT)[] = [],
public testarrayofstring: (string)[] = [],
public testarrayoftables: (MonsterT)[] = [],
@ -1332,7 +1332,7 @@ constructor(
public nonOwningReference: bigint = BigInt('0'),
public vectorOfNonOwningReferences: (bigint)[] = [],
public anyUniqueType: AnyUniqueAliases = AnyUniqueAliases.NONE,
public anyUnique: MonsterT|MyGameExample2MonsterT|TestSimpleTableWithEnumT|null = null,
public anyUnique: MonsterT|MyGame_Example2_MonsterT|TestSimpleTableWithEnumT|null = null,
public anyAmbiguousType: AnyAmbiguousAliases = AnyAmbiguousAliases.NONE,
public anyAmbiguous: MonsterT|null = null,
public vectorOfEnums: (Color)[] = [],

View File

@ -6,4 +6,3 @@ export enum Race {
Dwarf = 1,
Elf = 2
}

View File

@ -30,7 +30,7 @@ export class Referrable {
return true;
}
static getFullyQualifiedName() {
return 'MyGame.Example.Referrable';
return 'MyGame_Example_Referrable';
}
static startReferrable(builder) {
builder.startObject(1);

View File

@ -39,7 +39,7 @@ mutate_id(value:bigint):boolean {
}
static getFullyQualifiedName():string {
return 'MyGame.Example.Referrable';
return 'MyGame_Example_Referrable';
}
static startReferrable(builder:flatbuffers.Builder) {

View File

@ -46,7 +46,7 @@ export class Stat {
return true;
}
static getFullyQualifiedName() {
return 'MyGame.Example.Stat';
return 'MyGame_Example_Stat';
}
static startStat(builder) {
builder.startObject(3);

View File

@ -62,7 +62,7 @@ mutate_count(value:number):boolean {
}
static getFullyQualifiedName():string {
return 'MyGame.Example.Stat';
return 'MyGame_Example_Stat';
}
static startStat(builder:flatbuffers.Builder) {

View File

@ -19,7 +19,7 @@ a(obj?:StructOfStructs):StructOfStructs|null {
}
static getFullyQualifiedName():string {
return 'MyGame.Example.StructOfStructsOfStructs';
return 'MyGame_Example_StructOfStructsOfStructs';
}
static sizeOf():number {

View File

@ -21,7 +21,7 @@ export class StructOfStructs {
return (obj || new Ability()).__init(this.bb_pos + 12, this.bb);
}
static getFullyQualifiedName() {
return 'MyGame.Example.StructOfStructs';
return 'MyGame_Example_StructOfStructs';
}
static sizeOf() {
return 20;

View File

@ -28,7 +28,7 @@ c(obj?:Ability):Ability|null {
}
static getFullyQualifiedName():string {
return 'MyGame.Example.StructOfStructs';
return 'MyGame_Example_StructOfStructs';
}
static sizeOf():number {

View File

@ -31,7 +31,7 @@ export class TestSimpleTableWithEnum {
return true;
}
static getFullyQualifiedName() {
return 'MyGame.Example.TestSimpleTableWithEnum';
return 'MyGame_Example_TestSimpleTableWithEnum';
}
static startTestSimpleTableWithEnum(builder) {
builder.startObject(1);

View File

@ -40,7 +40,7 @@ mutate_color(value:Color):boolean {
}
static getFullyQualifiedName():string {
return 'MyGame.Example.TestSimpleTableWithEnum';
return 'MyGame_Example_TestSimpleTableWithEnum';
}
static startTestSimpleTableWithEnum(builder:flatbuffers.Builder) {

View File

@ -24,7 +24,7 @@ export class Test {
return true;
}
static getFullyQualifiedName() {
return 'MyGame.Example.Test';
return 'MyGame_Example_Test';
}
static sizeOf() {
return 4;

View File

@ -32,7 +32,7 @@ mutate_b(value:number):boolean {
}
static getFullyQualifiedName():string {
return 'MyGame.Example.Test';
return 'MyGame_Example_Test';
}
static sizeOf():number {

View File

@ -162,7 +162,7 @@ export class TypeAliases {
return offset ? new Float64Array(this.bb.bytes().buffer, this.bb.bytes().byteOffset + this.bb.__vector(this.bb_pos + offset), this.bb.__vector_len(this.bb_pos + offset)) : null;
}
static getFullyQualifiedName() {
return 'MyGame.Example.TypeAliases';
return 'MyGame_Example_TypeAliases';
}
static startTypeAliases(builder) {
builder.startObject(12);

View File

@ -213,7 +213,7 @@ vf64Array():Float64Array|null {
}
static getFullyQualifiedName():string {
return 'MyGame.Example.TypeAliases';
return 'MyGame_Example_TypeAliases';
}
static startTypeAliases(builder:flatbuffers.Builder) {

View File

@ -49,7 +49,7 @@ export class Vec3 {
return (obj || new Test()).__init(this.bb_pos + 26, this.bb);
}
static getFullyQualifiedName() {
return 'MyGame.Example.Vec3';
return 'MyGame_Example_Vec3';
}
static sizeOf() {
return 32;

View File

@ -65,7 +65,7 @@ test3(obj?:Test):Test|null {
}
static getFullyQualifiedName():string {
return 'MyGame.Example.Vec3';
return 'MyGame_Example_Vec3';
}
static sizeOf():number {

View File

@ -18,7 +18,7 @@ export class Monster {
return (obj || new Monster()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
static getFullyQualifiedName() {
return 'MyGame.Example2.Monster';
return 'MyGame_Example2_Monster';
}
static startMonster(builder) {
builder.startObject(0);

View File

@ -23,7 +23,7 @@ static getSizePrefixedRootAsMonster(bb:flatbuffers.ByteBuffer, obj?:Monster):Mon
}
static getFullyQualifiedName():string {
return 'MyGame.Example2.Monster';
return 'MyGame_Example2_Monster';
}
static startMonster(builder:flatbuffers.Builder) {

View File

@ -18,7 +18,7 @@ export class InParentNamespace {
return (obj || new InParentNamespace()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
static getFullyQualifiedName() {
return 'MyGame.InParentNamespace';
return 'MyGame_InParentNamespace';
}
static startInParentNamespace(builder) {
builder.startObject(0);

View File

@ -23,7 +23,7 @@ static getSizePrefixedRootAsInParentNamespace(bb:flatbuffers.ByteBuffer, obj?:In
}
static getFullyQualifiedName():string {
return 'MyGame.InParentNamespace';
return 'MyGame_InParentNamespace';
}
static startInParentNamespace(builder:flatbuffers.Builder) {

View File

@ -5,4 +5,3 @@ export enum OptionalByte {
One = 1,
Two = 2
}

View File

@ -1,4 +1,4 @@
// automatically generated by the FlatBuffers compiler, do not modify
export { ScalarStuff } from './optional-scalars/scalar-stuff';
export { OptionalByte } from './optional-scalars/optional-byte';
export { ScalarStuff } from './optional-scalars/scalar-stuff';

File diff suppressed because it is too large Load Diff

View File

@ -47,4 +47,3 @@ export function unionListToCharacter(
default: return null;
}
}

View File

@ -34,4 +34,3 @@ export function unionListToGadget(
default: return null;
}
}