Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DO NOT MERGE YET] add native Typescript support #9412

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
234 changes: 208 additions & 26 deletions src/google/protobuf/compiler/js/js_generator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1950,19 +1950,189 @@ void Generator::GenerateTestOnly(const GeneratorOptions& options,
printer->Print("\n");
}

void Generator::GenerateClassesAndEnums(const GeneratorOptions& options,
io::Printer* printer,
const FileDescriptor* file) const {
for (int i = 0; i < file->message_type_count(); i++) {
GenerateClassConstructorAndDeclareExtensionFieldInfo(options, printer,
file->message_type(i));
}
for (int i = 0; i < file->message_type_count(); i++) {
GenerateClass(options, printer, file->message_type(i));
}
for (int i = 0; i < file->enum_type_count(); i++) {
GenerateEnum(options, printer, file->enum_type(i));
}
void Generator::GenerateClassesAndEnums(const GeneratorOptions& options, io::Printer* printer, const FileDescriptor* file) const
{
if (options.import_style == = GeneratorOptions::ImportStyle::kImportTypescript)
{
for (int i = 0; i < file->message_type_count(); i++)
{
GenerateTypescriptClass(options, printer, file->message_type(i));
}
}
else
{
for (int i = 0; i < file->message_type_count(); i++) {
GenerateClassConstructorAndDeclareExtensionFieldInfo(options, printer, file->message_type(i));
}

for (int i = 0; i < file->message_type_count(); i++)
{
GenerateClass(options, printer, file->message_type(i));
}

for (int i = 0; i < file->enum_type_count(); i++)
{
GenerateEnum(options, printer, file->enum_type(i));
}
}

}

void Generator::GenerateTypescriptClass(const GeneratorOptions& options, io::Printer* printer, const Descriptor* desc) const
{
if (IgnoreMessage(desc))
{
return;
}

// Only proto3 is supported for now
if (desc->file()->syntax() != FileDescriptor::SYNTAX_PROTO3)
{
return;
}


printer->Print(
"/**\n"
" * Generated by JsPbCodeGenerator.\n"
" */\n"
"export class $classname$ extends jspb.Message {\n",

"classprefix", GetMessagePathPrefix(options, desc),
"classname", desc->name(),
);
printer->Indent();

// TODO - Print fields and accessors
printer->Print(
"public static readonly displayname: string = '$displayName$';\n",

"displayName", GetMessagePath(options, desc),
);

for (int i = 0; i < desc->field_count(); i++) {
if (IgnoreField(desc->field(i))) {
continue;
}

FieldDescriptor* field = desc->field(i);
std::string identifier = JSIdent(options, field, false, false, false);
std::string index = JSFieldIndex(field);
std::string type = JSFieldTypeAnnotation(options, field,
/* is_setter_argument = */ false,
/* force_present = */ false,
/* singular_if_not_packed = */ false);

GenerateClassField(options, printer, desc->field(i));

// Getter
{
printer->Print(
"get $name$(): $type$ {\n"
" return ",

"name", identifier,
"type", type,
);
printer->Annotate("gettername", field);

GenerateFieldValueExpression(printer, "this", field, /* use_default = */ false);

printer->Print("};\n\n");
}

// Setter
{
printer->Print(
"set $name$(value: $type$) {\n"
" jspb.Message.setField(this, $index$, value);"
"};\n\n",

"name", identifier,
"index", index,
"type", type,
);
printer->Annotate("settername", field);

GenerateFieldValueExpression(printer, "this", field, /* use_default = */ false);

printer->Print("};\n\n");


printer->Print(
"$class$.prototype.$settername$ = function(value) {\n"
" return jspb.Message.setProto3$typetag$Field(this, $index$, "
"value);"
"\n"
"};\n"
"\n"
"\n",
"class", GetMessagePath(options, field->containing_type()),
"settername", "set" + JSGetterName(options, field), "typetag",
JSTypeTag(field), "index", JSFieldIndex(field));
}
}

// Print constructor
printer->Print("constructor(optionalData: Array<any>) {\n");
printer->Indent();
printer->Outdent();
printer->Print("}\n");

// Print Methods
// TODO - Print methods

//Close class
printer->Outdent();
printer->Print("}\n");














if (!NamespaceOnly(desc))
{
printer->Print("\n");
GenerateClassFieldInfo(options, printer, desc);

GenerateClassToObject(options, printer, desc);
// These must come *before* the extension-field info generation in
// GenerateClassRegistration so that references to the binary
// serialization/deserialization functions may be placed in the extension
// objects.
GenerateClassDeserializeBinary(options, printer, desc);
GenerateClassSerializeBinary(options, printer, desc);
}

// Recurse on nested types. These must come *before* the extension-field
// info generation in GenerateClassRegistration so that extensions that
// reference nested types proceed the definitions of the nested types.
for (int i = 0; i < desc->enum_type_count(); i++) {
GenerateEnum(options, printer, desc->enum_type(i));
}
for (int i = 0; i < desc->nested_type_count(); i++) {
GenerateClass(options, printer, desc->nested_type(i));
}

if (!NamespaceOnly(desc)) {
GenerateClassRegistration(options, printer, desc);
GenerateClassFields(options, printer, desc);

if (options.import_style != GeneratorOptions::kImportClosure) {
for (int i = 0; i < desc->extension_count(); i++) {
GenerateExtension(options, printer, desc->extension(i));
}
}
}
}

void Generator::GenerateClass(const GeneratorOptions& options,
Expand Down Expand Up @@ -2038,7 +2208,8 @@ void Generator::GenerateClassConstructor(const GeneratorOptions& options,
: (IsResponse(desc) ? "''" : "0"),
"pivot", GetPivot(desc), "rptfields",
RepeatedFieldsArrayName(options, desc), "oneoffields",
OneofFieldsArrayName(options, desc));
OneofFieldsArrayName(options, desc)
);
printer->Print(
"};\n"
"goog.inherits($classname$, jspb.Message);\n"
Expand All @@ -2051,7 +2222,8 @@ void Generator::GenerateClassConstructor(const GeneratorOptions& options,
" */\n"
" $classname$.displayName = '$classname$';\n"
"}\n",
"classname", GetMessagePath(options, desc));
"classname", GetMessagePath(options, desc)
);
}

void Generator::GenerateClassConstructorAndDeclareExtensionFieldInfo(
Expand Down Expand Up @@ -2600,6 +2772,8 @@ void Generator::GenerateClassField(const GeneratorOptions& options,
// Message field: special handling in order to wrap the underlying data
// array with a message object.


// Print getter
printer->Print(
"/**\n"
" * $fielddef$\n"
Expand All @@ -2621,16 +2795,19 @@ void Generator::GenerateClassField(const GeneratorOptions& options,
"\n"
"\n",
"class", GetMessagePath(options, field->containing_type()),
"gettername", "get" + JSGetterName(options, field), "type",
JSFieldTypeAnnotation(options, field,
"gettername", "get" + JSGetterName(options, field),
"type", JSFieldTypeAnnotation(options, field,
/* is_setter_argument = */ false,
/* force_present = */ false,
/* singular_if_not_packed = */ false),
"rpt", (field->is_repeated() ? "Repeated" : ""), "index",
JSFieldIndex(field), "wrapperclass", SubmessageTypeRef(options, field),
"required",
(field->label() == FieldDescriptor::LABEL_REQUIRED ? ", 1" : ""));
"rpt", (field->is_repeated() ? "Repeated" : ""),
"index", JSFieldIndex(field),
"wrapperclass", SubmessageTypeRef(options, field),
"required", (field->label() == FieldDescriptor::LABEL_REQUIRED ? ", 1" : "")
);
printer->Annotate("gettername", field);

// Print setter
printer->Print(
"/**\n"
" * @param {$optionaltype$} value\n"
Expand Down Expand Up @@ -2691,13 +2868,18 @@ void Generator::GenerateClassField(const GeneratorOptions& options,
"$comment$"
" * @return {$type$}\n"
" */\n",
"fielddef", FieldDefinition(options, field), "comment",
FieldComments(field, bytes_mode), "type", typed_annotation);
"fielddef", FieldDefinition(options, field),
"comment", FieldComments(field, bytes_mode),
"type", typed_annotation
);
}

printer->Print("$class$.prototype.$gettername$ = function() {\n", "class",
GetMessagePath(options, field->containing_type()),
"gettername", "get" + JSGetterName(options, field));
printer->Print(
"$class$.prototype.$gettername$ = function() {\n",

"class", GetMessagePath(options, field->containing_type()),
"gettername", "get" + JSGetterName(options, field)
);
printer->Annotate("gettername", field);

if (untyped) {
Expand Down
9 changes: 8 additions & 1 deletion src/google/protobuf/compiler/js/js_generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ struct GeneratorOptions {
kImportCommonJsStrict, // require() with no global export
kImportBrowser, // no import statements
kImportEs6, // import { member } from ''
kImportTypescript, // import { member } from ''
} import_style;

GeneratorOptions()
Expand All @@ -93,7 +94,11 @@ struct GeneratorOptions {

// Returns the file name extension to use for generated code.
std::string GetFileNameExtension() const {
return import_style == kImportClosure ? extension : "_pb.js";
return import_style == kImportClosure
? extension
: import_style == kImportTypescript
? "_pb.ts"
: "_pb.js";
}

enum OutputMode {
Expand Down Expand Up @@ -247,6 +252,8 @@ class PROTOC_EXPORT Generator : public CodeGenerator {
bool use_default) const;

// Generate definition for one class.
void GenerateTypescriptClass(const GeneratorOptions& options, io::Printer* printer,
const Descriptor* desc) const;
void GenerateClass(const GeneratorOptions& options, io::Printer* printer,
const Descriptor* desc) const;
void GenerateClassConstructor(const GeneratorOptions& options,
Expand Down