diff --git a/docs/source/Compiler.md b/docs/source/Compiler.md index b71b8c46d..b83aace12 100755 --- a/docs/source/Compiler.md +++ b/docs/source/Compiler.md @@ -93,6 +93,12 @@ Additional options: output (by default the case for C++ and JS), all code will end up in this one file. +- `--no-js-exports` : Removes Node.js style export lines (useful for JS) + +- `--goog-js-export` : Uses goog.exportsSymbol and goog.exportsProperty + instead of Node.js style exporting. Needed for compatibility with the + Google closure compiler (useful for JS). + - `--raw-binary` : Allow binaries without a file_indentifier to be read. This may crash flatc given a mismatched schema. diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 0956d1872..186cae0c2 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -337,6 +337,7 @@ struct ServiceDef : public Definition { struct IDLOptions { bool strict_json; bool skip_js_exports; + bool use_goog_js_export_format; bool output_default_scalars_in_json; int indent_step; bool output_enum_identifiers; @@ -378,6 +379,7 @@ struct IDLOptions { IDLOptions() : strict_json(false), skip_js_exports(false), + use_goog_js_export_format(false), output_default_scalars_in_json(false), indent_step(2), output_enum_identifiers(true), prefixed_enums(true), scoped_enums(false), diff --git a/js/flatbuffers.js b/js/flatbuffers.js index c1296d557..3ea1adfa1 100644 --- a/js/flatbuffers.js +++ b/js/flatbuffers.js @@ -2,6 +2,15 @@ /// @addtogroup flatbuffers_javascript_api /// @{ /// @cond FLATBUFFERS_INTERNAL + +/** + * @fileoverview + * + * Need to suppress 'global this' error so the Node.js export line doesn't cause + * closure compile to error out. + * @suppress {globalThis} + */ + /** * @const * @namespace @@ -530,6 +539,10 @@ flatbuffers.Builder.prototype.offset = function() { * @param {flatbuffers.ByteBuffer} bb The current buffer with the existing data * @returns {flatbuffers.ByteBuffer} A new byte buffer with the old data copied * to it. The data is located at the end of the buffer. + * + * uint8Array.set() formally takes {Array|ArrayBufferView}, so to pass + * it a uint8Array we need to suppress the type check: + * @suppress {checkTypes} */ flatbuffers.Builder.growByteBuffer = function(bb) { var old_buf_size = bb.capacity(); diff --git a/src/flatc.cpp b/src/flatc.cpp index 5d426cc72..5fbb8dbab 100644 --- a/src/flatc.cpp +++ b/src/flatc.cpp @@ -86,6 +86,8 @@ std::string FlatCompiler::GetUsageString(const char* program_name) const { " --escape-proto-ids Disable appending '_' in namespaces names.\n" " --gen-object-api Generate an additional object-based API.\n" " --cpp-ptr-type T Set object API pointer type (default std::unique_ptr)\n" + " --no-js-exports Removes Node.js style export lines in JS.\n" + " --goog-js-export Uses goog.exports* for closure compiler exporting in JS.\n" " --raw-binary Allow binaries without file_indentifier to be read.\n" " This may crash flatc given a mismatched schema.\n" " --proto Input is a .proto, translate to .fbs.\n" @@ -146,6 +148,8 @@ int FlatCompiler::Compile(int argc, const char** argv) { opts.allow_non_utf8 = true; } else if(arg == "--no-js-exports") { opts.skip_js_exports = true; + } else if(arg == "--goog-js-export") { + opts.use_goog_js_export_format = true; } else if(arg == "--defaults-json") { opts.output_default_scalars_in_json = true; } else if (arg == "--unknown-json") { diff --git a/src/idl_gen_js.cpp b/src/idl_gen_js.cpp index dd04bec45..e1daf0af5 100644 --- a/src/idl_gen_js.cpp +++ b/src/idl_gen_js.cpp @@ -113,7 +113,11 @@ class JsGenerator : public BaseGenerator { code += "/**\n * @const\n * @namespace\n */\n"; if (it->find('.') == std::string::npos) { code += "var "; - exports += "this." + *it + " = " + *it + ";\n"; + if(parser_.opts.use_goog_js_export_format) { + exports += "goog.exportSymbol('" + *it + "', " + *it + ");\n"; + } else { + exports += "this." + *it + " = " + *it + ";\n"; + } } code += *it + " = " + *it + " || {};\n\n"; } @@ -172,7 +176,12 @@ void GenEnum(EnumDef &enum_def, std::string *code_ptr, GenDocComment(enum_def.doc_comment, code_ptr, "@enum"); if (enum_def.defined_namespace->components.empty()) { code += "var "; - exports += "this." + enum_def.name + " = " + enum_def.name + ";\n"; + if(parser_.opts.use_goog_js_export_format) { + exports += "goog.exportSymbol('" + enum_def.name + "', " + enum_def.name + + ");\n"; + } else { + exports += "this." + enum_def.name + " = " + enum_def.name + ";\n"; + } } code += WrapInNameSpace(enum_def) + " = {\n"; for (auto it = enum_def.vals.vec.begin(); @@ -236,6 +245,9 @@ std::string GenDefaultValue(const Value &value, const std::string &context) { if (auto val = value.type.enum_def->ReverseLookup( atoi(value.constant.c_str()), false)) { return WrapInNameSpace(*value.type.enum_def) + "." + val->name; + } else { + return "/** @type {" + WrapInNameSpace(*value.type.enum_def) + "} */ (" + + value.constant + ")"; } } @@ -371,7 +383,12 @@ void GenStruct(const Parser &parser, StructDef &struct_def, std::string *code_pt std::string object_name = WrapInNameSpace(struct_def); GenDocComment(struct_def.doc_comment, code_ptr, "@constructor"); if (isStatement) { - exports += "this." + struct_def.name + " = " + struct_def.name + ";\n"; + if(parser_.opts.use_goog_js_export_format) { + exports += "goog.exportSymbol('" + struct_def.name + "', " + + struct_def.name + ");\n"; + } else { + exports += "this." + struct_def.name + " = " + struct_def.name + ";\n"; + } code += "function " + object_name; } else { code += object_name + " = function"; @@ -526,7 +543,13 @@ void GenStruct(const Parser &parser, StructDef &struct_def, std::string *code_pt field.value.type.element == BASE_TYPE_ULONG) { code += "this.bb.createLong(0, 0)"; } else if (IsScalar(field.value.type.element)) { - code += "0"; + if (field.value.type.enum_def) { + code += "/** @type {" + + WrapInNameSpace(*field.value.type.enum_def) + "} */ (" + + field.value.constant + ")"; + } else { + code += "0"; + } } else { code += "null"; } @@ -550,6 +573,12 @@ void GenStruct(const Parser &parser, StructDef &struct_def, std::string *code_pt } code += "};\n\n"; + if(parser_.opts.use_goog_js_export_format) { + exports += "goog.exportProperty(" + object_name + ".prototype, '" + + MakeCamel(field.name, false) + "', " + object_name + ".prototype." + + MakeCamel(field.name, false) + ");\n"; + } + // Adds the mutable scalar value to the output if (IsScalar(field.value.type.base_type) && parser.opts.mutable_buffer) { std::string annotations = "@param {" + GenTypeName(field.value.type, true) + "} value\n"; @@ -564,6 +593,12 @@ void GenStruct(const Parser &parser, StructDef &struct_def, std::string *code_pt code += " this.bb.write" + MakeCamel(GenType(field.value.type)) + "(this.bb_pos + offset, value);\n"; code += " return true;\n"; code += "};\n\n"; + + if(parser_.opts.use_goog_js_export_format) { + exports += "goog.exportProperty(" + object_name + + ".prototype, 'mutate_" + field.name + "', " + object_name + + ".prototype.mutate_" + field.name + ");\n"; + } } // Emit vector helpers @@ -574,6 +609,12 @@ void GenStruct(const Parser &parser, StructDef &struct_def, std::string *code_pt code += "Length = function() {\n" + offset_prefix; code += "this.bb.__vector_len(this.bb_pos + offset) : 0;\n};\n\n"; + if(parser_.opts.use_goog_js_export_format) { + exports += "goog.exportProperty(" + object_name + ".prototype, '" + + MakeCamel(field.name, false) + "Length', " + object_name + + ".prototype." + MakeCamel(field.name, false) + "Length);\n"; + } + // For scalar types, emit a typed array helper auto vectorType = field.value.type.VectorType(); if (IsScalar(vectorType.base_type) && !IsLong(vectorType.base_type)) { @@ -583,6 +624,12 @@ void GenStruct(const Parser &parser, StructDef &struct_def, std::string *code_pt code += "new " + GenType(vectorType) + "Array(this.bb.bytes().buffer, " "this.bb.bytes().byteOffset + this.bb.__vector(this.bb_pos + offset), " "this.bb.__vector_len(this.bb_pos + offset)) : null;\n};\n\n"; + + if(parser_.opts.use_goog_js_export_format) { + exports += "goog.exportProperty(" + object_name + ".prototype, '" + + MakeCamel(field.name, false) + "Array', " + object_name + + ".prototype." + MakeCamel(field.name, false) + "Array);\n"; + } } } }