2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
29 #include <unordered_map>
34 #include "thrift/platform.h"
35 #include "thrift/version.h"
39 using std::ostringstream
;
41 using std::stringstream
;
42 using std::unordered_map
;
45 static const string endl
= "\n"; // avoid ostream << std::endl flushes
46 static const string episode_file_name
= "thrift.js.episode";
47 // largest consecutive integer representable by a double (2 ^ 53 - 1)
48 static const int64_t max_safe_integer
= 0x1fffffffffffff;
49 // smallest consecutive number representable by a double (-2 ^ 53 + 1)
50 static const int64_t min_safe_integer
= -max_safe_integer
;
52 #include "thrift/generate/t_oop_generator.h"
58 class t_js_generator
: public t_oop_generator
{
60 t_js_generator(t_program
* program
,
61 const std::map
<std::string
, std::string
>& parsed_options
,
62 const std::string
& option_string
)
63 : t_oop_generator(program
) {
65 std::map
<std::string
, std::string
>::const_iterator iter
;
71 gen_episode_file_
= false;
73 bool with_ns_
= false;
75 for (iter
= parsed_options
.begin(); iter
!= parsed_options
.end(); ++iter
) {
76 if( iter
->first
.compare("node") == 0) {
78 } else if( iter
->first
.compare("jquery") == 0) {
80 } else if( iter
->first
.compare("ts") == 0) {
82 } else if( iter
->first
.compare("with_ns") == 0) {
84 } else if( iter
->first
.compare("es6") == 0) {
86 } else if( iter
->first
.compare("imports") == 0) {
87 parse_imports(program
, iter
->second
);
88 } else if (iter
->first
.compare("thrift_package_output_directory") == 0) {
89 parse_thrift_package_output_directory(iter
->second
);
91 throw std::invalid_argument("unknown option js:" + iter
->first
);
95 if (gen_es6_
&& gen_jquery_
) {
96 throw std::invalid_argument("invalid switch: [-gen js:es6,jquery] options not compatible");
99 if (gen_node_
&& gen_jquery_
) {
100 throw std::invalid_argument("invalid switch: [-gen js:node,jquery] options not compatible, try: [-gen js:node -gen "
104 if (!gen_node_
&& with_ns_
) {
105 throw std::invalid_argument("invalid switch: [-gen js:with_ns] is only valid when using node.js");
108 // Depending on the processing flags, we will update these to be ES6 compatible
109 js_const_type_
= "var ";
110 js_let_type_
= "var ";
111 js_var_type_
= "var ";
113 js_const_type_
= "const ";
114 js_let_type_
= "let ";
118 out_dir_base_
= "gen-nodejs";
121 out_dir_base_
= "gen-js";
125 escape_
['\''] = "\\'";
129 * Init and close methods
132 void init_generator() override
;
133 void close_generator() override
;
136 * Program-level generation functions
139 void generate_typedef(t_typedef
* ttypedef
) override
;
140 void generate_enum(t_enum
* tenum
) override
;
141 void generate_const(t_const
* tconst
) override
;
142 void generate_struct(t_struct
* tstruct
) override
;
143 void generate_xception(t_struct
* txception
) override
;
144 void generate_service(t_service
* tservice
) override
;
146 std::string
render_recv_throw(std::string var
);
147 std::string
render_recv_return(std::string var
);
149 std::string
render_const_value(t_type
* type
, t_const_value
* value
);
154 void generate_js_struct(t_struct
* tstruct
, bool is_exception
);
155 void generate_js_struct_definition(std::ostream
& out
,
157 bool is_xception
= false,
158 bool is_exported
= true);
159 void generate_js_struct_reader(std::ostream
& out
, t_struct
* tstruct
);
160 void generate_js_struct_writer(std::ostream
& out
, t_struct
* tstruct
);
161 void generate_js_function_helpers(t_function
* tfunction
);
164 * Service-level generation functions
166 void generate_service_helpers(t_service
* tservice
);
167 void generate_service_interface(t_service
* tservice
);
168 void generate_service_rest(t_service
* tservice
);
169 void generate_service_client(t_service
* tservice
);
170 void generate_service_processor(t_service
* tservice
);
171 void generate_process_function(t_service
* tservice
, t_function
* tfunction
);
174 * Serialization constructs
177 void generate_deserialize_field(std::ostream
& out
,
179 std::string prefix
= "",
180 bool inclass
= false);
182 void generate_deserialize_struct(std::ostream
& out
, t_struct
* tstruct
, std::string prefix
= "");
184 void generate_deserialize_container(std::ostream
& out
, t_type
* ttype
, std::string prefix
= "");
186 void generate_deserialize_set_element(std::ostream
& out
, t_set
* tset
, std::string prefix
= "");
188 void generate_deserialize_map_element(std::ostream
& out
, t_map
* tmap
, std::string prefix
= "");
190 void generate_deserialize_list_element(std::ostream
& out
,
192 std::string prefix
= "");
194 void generate_serialize_field(std::ostream
& out
, t_field
* tfield
, std::string prefix
= "");
196 void generate_serialize_struct(std::ostream
& out
, t_struct
* tstruct
, std::string prefix
= "");
198 void generate_serialize_container(std::ostream
& out
, t_type
* ttype
, std::string prefix
= "");
200 void generate_serialize_map_element(std::ostream
& out
,
205 void generate_serialize_set_element(std::ostream
& out
, t_set
* tmap
, std::string iter
);
207 void generate_serialize_list_element(std::ostream
& out
, t_list
* tlist
, std::string iter
);
210 * Helper rendering functions
213 std::string
js_includes();
214 std::string
ts_includes();
215 std::string
ts_service_includes();
216 std::string
render_includes();
217 std::string
render_ts_includes();
218 std::string
get_import_path(t_program
* program
);
219 std::string
declare_field(t_field
* tfield
, bool init
= false, bool obj
= false);
220 std::string
function_signature(t_function
* tfunction
,
221 std::string prefix
= "",
222 bool include_callback
= false);
223 std::string
argument_list(t_struct
* tstruct
, bool include_callback
= false);
224 std::string
type_to_enum(t_type
* ttype
);
225 std::string
make_valid_nodeJs_identifier(std::string
const& name
);
228 * Helper parser functions
231 void parse_imports(t_program
* program
, const std::string
& imports_string
);
232 void parse_thrift_package_output_directory(const std::string
& thrift_package_output_directory
);
234 std::string
autogen_comment() override
{
235 return std::string("//\n") + "// Autogenerated by Thrift Compiler (" + THRIFT_VERSION
+ ")\n"
236 + "//\n" + "// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n"
240 t_type
* get_contained_type(t_type
* t
);
242 std::vector
<std::string
> js_namespace_pieces(t_program
* p
) {
243 std::string ns
= p
->get_namespace("js");
245 std::string::size_type loc
;
246 std::vector
<std::string
> pieces
;
253 while ((loc
= ns
.find(".")) != std::string::npos
) {
254 pieces
.push_back(ns
.substr(0, loc
));
255 ns
= ns
.substr(loc
+ 1);
260 pieces
.push_back(ns
);
266 std::string
js_type_namespace(t_program
* p
) {
268 if (p
!= NULL
&& p
!= program_
) {
269 return make_valid_nodeJs_identifier(p
->get_name()) + "_ttypes.";
273 return js_namespace(p
);
276 std::string
js_export_namespace(t_program
* p
) {
280 return js_namespace(p
);
283 bool has_js_namespace(t_program
* p
) {
287 std::string ns
= p
->get_namespace("js");
288 return (ns
.size() > 0);
291 std::string
js_namespace(t_program
* p
) {
295 std::string ns
= p
->get_namespace("js");
304 * TypeScript Definition File helper functions
307 string
ts_function_signature(t_function
* tfunction
, bool include_callback
);
308 string
ts_get_type(t_type
* type
);
311 * Special indentation for TypeScript Definitions because of the module.
312 * Returns the normal indentation + " " if a module was defined.
315 string
ts_indent() { return indent() + (!ts_module_
.empty() ? " " : ""); }
318 * Returns "declare " if no module was defined.
321 string
ts_declare() { return (ts_module_
.empty() ? (gen_node_
? "declare " : "export declare ") : ""); }
324 * Returns "?" if the given field is optional or has a default value.
325 * @param t_field The field to check
328 string
ts_get_req(t_field
* field
) {return (field
->get_req() == t_field::T_OPTIONAL
|| field
->get_value() != NULL
? "?" : ""); }
331 * Returns the documentation, if the provided documentable object has one.
332 * @param t_doc The object to get the documentation from
333 * @return string The documentation
335 string
ts_print_doc(t_doc
* tdoc
) {
336 string result
= endl
;
338 if (tdoc
->has_doc()) {
339 std::stringstream
doc(tdoc
->get_doc());
342 result
+= ts_indent() + "/**" + endl
;
343 while (std::getline(doc
, item
)) {
344 result
+= ts_indent() + " * " + item
+ endl
;
346 result
+= ts_indent() + " */" + endl
;
353 * True if we should generate NodeJS-friendly RPC services.
358 * True if we should generate services that use jQuery ajax (async/sync).
363 * True if we should generate a TypeScript Definition File for each service.
368 * True if we should generate ES6 code, i.e. with Promises
373 * True if we will generate an episode file.
375 bool gen_episode_file_
;
378 * The name of the defined module(s), for TypeScript Definition Files.
383 * True if we should not generate namespace objects for node.
388 * The node modules to use when importing the previously generated files.
390 vector
<string
> imports
;
393 * Cache for imported modules.
395 unordered_map
<string
, string
> module_name_2_import_path
;
398 * Cache for TypeScript includes to generated import name.
400 unordered_map
<t_program
*, string
> include_2_import_name
;
403 * The prefix to use when generating the episode file.
405 string thrift_package_output_directory_
;
408 * The variable decorator for "const" variables. Will default to "var" if in an incompatible language.
410 string js_const_type_
;
413 * The variable decorator for "let" variables. Will default to "var" if in an incompatible language.
418 * The default variable decorator. Supports all javascript languages, but is not scoped to functions or closures.
425 ofstream_with_content_based_conditional_update f_episode_
;
426 ofstream_with_content_based_conditional_update f_types_
;
427 ofstream_with_content_based_conditional_update f_service_
;
428 ofstream_with_content_based_conditional_update f_types_ts_
;
429 ofstream_with_content_based_conditional_update f_service_ts_
;
433 * Prepares for file generation by opening up the necessary file output
436 * @param tprogram The program to generate
438 void t_js_generator::init_generator() {
439 // Make output directory
440 MKDIR(get_out_dir().c_str());
442 const auto outdir
= get_out_dir();
444 // Make output file(s)
445 if (gen_episode_file_
) {
446 const auto f_episode_file_path
= outdir
+ episode_file_name
;
447 f_episode_
.open(f_episode_file_path
);
450 const auto f_types_name
= outdir
+ program_
->get_name() + "_types.js";
451 f_types_
.open(f_types_name
.c_str());
452 if (gen_episode_file_
) {
453 const auto types_module
= program_
->get_name() + "_types";
454 f_episode_
<< types_module
<< ":" << thrift_package_output_directory_
<< "/" << types_module
<< endl
;
458 const auto f_types_ts_name
= outdir
+ program_
->get_name() + "_types.d.ts";
459 f_types_ts_
.open(f_types_ts_name
.c_str());
463 f_types_
<< autogen_comment();
465 if ((gen_node_
|| gen_es6_
) && no_ns_
) {
466 f_types_
<< "\"use strict\";" << endl
<< endl
;
469 f_types_
<< js_includes() << endl
<< render_includes() << endl
;
472 f_types_ts_
<< autogen_comment() << ts_includes() << endl
<< render_ts_includes() << endl
;
476 f_types_
<< js_const_type_
<< "ttypes = module.exports = {};" << endl
;
481 // setup the namespace
482 // TODO should the namespace just be in the directory structure for node?
483 vector
<string
> ns_pieces
= js_namespace_pieces(program_
);
484 if (ns_pieces
.size() > 0) {
485 for (size_t i
= 0; i
< ns_pieces
.size(); ++i
) {
486 pns
+= ((i
== 0) ? "" : ".") + ns_pieces
[i
];
487 f_types_
<< "if (typeof " << pns
<< " === 'undefined') {" << endl
;
488 f_types_
<< " " << pns
<< " = {};" << endl
;
489 f_types_
<< "}" << endl
;
490 f_types_
<< "" << "if (typeof module !== 'undefined' && module.exports) {" << endl
;
491 f_types_
<< " module.exports." << pns
<< " = " << pns
<< ";" << endl
<< "}" << endl
;
495 f_types_ts_
<< "declare module " << ts_module_
<< " {";
501 * Prints standard js imports
503 string
t_js_generator::js_includes() {
505 string result
= js_const_type_
+ "thrift = require('thrift');\n"
506 + js_const_type_
+ "Thrift = thrift.Thrift;\n";
508 result
+= js_const_type_
+ "Q = thrift.Q;\n";
510 result
+= js_const_type_
+ "Int64 = require('node-int64');\n";
513 string result
= "if (typeof Int64 === 'undefined' && typeof require === 'function') {\n " + js_const_type_
+ "Int64 = require('node-int64');\n}\n";
518 * Prints standard ts imports
520 string
t_js_generator::ts_includes() {
523 "import thrift = require('thrift');\n"
524 "import Thrift = thrift.Thrift;\n"
525 "import Q = thrift.Q;\n"
526 "import Int64 = require('node-int64');");
528 return string("import Int64 = require('node-int64');");
532 * Prints service ts imports
534 string
t_js_generator::ts_service_includes() {
537 "import thrift = require('thrift');\n"
538 "import Thrift = thrift.Thrift;\n"
539 "import Q = thrift.Q;\n"
540 "import Int64 = require('node-int64');");
542 return string("import Int64 = require('node-int64');");
546 * Renders all the imports necessary for including another Thrift program
548 string
t_js_generator::render_includes() {
552 const vector
<t_program
*>& includes
= program_
->get_includes();
553 for (auto include
: includes
) {
554 result
+= js_const_type_
+ make_valid_nodeJs_identifier(include
->get_name()) + "_ttypes = require('" + get_import_path(include
) + "');\n";
556 if (includes
.size() > 0) {
565 * Renders all the imports necessary for including another Thrift program
567 string
t_js_generator::render_ts_includes() {
573 const vector
<t_program
*>& includes
= program_
->get_includes();
574 for (auto include
: includes
) {
575 string include_name
= make_valid_nodeJs_identifier(include
->get_name()) + "_ttypes";
576 include_2_import_name
.insert({include
, include_name
});
577 result
+= "import " + include_name
+ " = require('" + get_import_path(include
) + "');\n";
579 if (includes
.size() > 0) {
586 string
t_js_generator::get_import_path(t_program
* program
) {
587 const string
import_file_name(program
->get_name() + "_types");
588 if (program
->get_recursive()) {
589 return "./" + import_file_name
;
592 const string import_file_name_with_extension
= import_file_name
+ ".js";
594 auto module_name_and_import_path_iterator
= module_name_2_import_path
.find(import_file_name
);
595 if (module_name_and_import_path_iterator
!= module_name_2_import_path
.end()) {
596 return module_name_and_import_path_iterator
->second
;
598 return "./" + import_file_name
;
602 * Close up (or down) some filez.
604 void t_js_generator::close_generator() {
605 // Close types file(s)
610 if (!ts_module_
.empty()) {
615 if (gen_episode_file_
){
621 * Generates a typedef. This is not done in JS, types are all implicit.
623 * @param ttypedef The type definition
625 void t_js_generator::generate_typedef(t_typedef
* ttypedef
) {
630 * Generates code for an enumerated type. Since define is expensive to lookup
631 * in JS, we use a global array for this.
633 * @param tenum The enumeration
635 void t_js_generator::generate_enum(t_enum
* tenum
) {
636 f_types_
<< js_type_namespace(tenum
->get_program()) << tenum
->get_name() << " = {" << endl
;
639 f_types_ts_
<< ts_print_doc(tenum
) << ts_indent() << ts_declare() << "enum "
640 << tenum
->get_name() << " {" << endl
;
645 vector
<t_enum_value
*> const& constants
= tenum
->get_constants();
646 vector
<t_enum_value
*>::const_iterator c_iter
;
647 for (c_iter
= constants
.begin(); c_iter
!= constants
.end(); ++c_iter
) {
648 int value
= (*c_iter
)->get_value();
650 f_types_ts_
<< ts_indent() << (*c_iter
)->get_name() << " = " << value
<< "," << endl
;
651 // add 'value: key' in addition to 'key: value' for TypeScript enums
652 f_types_
<< indent() << "'" << value
<< "' : '" << (*c_iter
)->get_name() << "'," << endl
;
654 f_types_
<< indent() << "'" << (*c_iter
)->get_name() << "' : " << value
;
655 if (c_iter
!= constants
.end() - 1) {
663 f_types_
<< "};" << endl
;
666 f_types_ts_
<< ts_indent() << "}" << endl
;
671 * Generate a constant value
673 void t_js_generator::generate_const(t_const
* tconst
) {
674 t_type
* type
= tconst
->get_type();
675 string name
= tconst
->get_name();
676 t_const_value
* value
= tconst
->get_value();
678 f_types_
<< js_type_namespace(program_
) << name
<< " = ";
679 f_types_
<< render_const_value(type
, value
) << ";" << endl
;
682 f_types_ts_
<< ts_print_doc(tconst
) << ts_indent() << ts_declare() << js_const_type_
<< name
<< ": "
683 << ts_get_type(type
) << ";" << endl
;
688 * Prints the value of a constant with the given type. Note that type checking
689 * is NOT performed in this function as it is always run beforehand using the
690 * validate_types method in main.cc
692 string
t_js_generator::render_const_value(t_type
* type
, t_const_value
* value
) {
693 std::ostringstream out
;
695 type
= get_true_type(type
);
697 if (type
->is_base_type()) {
698 t_base_type::t_base tbase
= ((t_base_type
*)type
)->get_base();
700 case t_base_type::TYPE_STRING
:
701 out
<< "'" << get_escaped_string(value
) << "'";
703 case t_base_type::TYPE_BOOL
:
704 out
<< (value
->get_integer() > 0 ? "true" : "false");
706 case t_base_type::TYPE_I8
:
707 case t_base_type::TYPE_I16
:
708 case t_base_type::TYPE_I32
:
709 out
<< value
->get_integer();
711 case t_base_type::TYPE_I64
:
713 int64_t const& integer_value
= value
->get_integer();
714 if (integer_value
<= max_safe_integer
&& integer_value
>= min_safe_integer
) {
715 out
<< "new Int64(" << integer_value
<< ")";
717 out
<< "new Int64('" << std::hex
<< integer_value
<< std::dec
<< "')";
721 case t_base_type::TYPE_DOUBLE
:
722 if (value
->get_type() == t_const_value::CV_INTEGER
) {
723 out
<< value
->get_integer();
725 out
<< emit_double_as_string(value
->get_double());
729 throw std::runtime_error("compiler error: no const of base type " + t_base_type::t_base_name(tbase
));
731 } else if (type
->is_enum()) {
732 out
<< value
->get_integer();
733 } else if (type
->is_struct() || type
->is_xception()) {
734 out
<< "new " << js_type_namespace(type
->get_program()) << type
->get_name() << "({";
736 const vector
<t_field
*>& fields
= ((t_struct
*)type
)->get_members();
737 vector
<t_field
*>::const_iterator f_iter
;
738 const map
<t_const_value
*, t_const_value
*, t_const_value::value_compare
>& val
= value
->get_map();
739 map
<t_const_value
*, t_const_value
*, t_const_value::value_compare
>::const_iterator v_iter
;
740 for (v_iter
= val
.begin(); v_iter
!= val
.end(); ++v_iter
) {
741 t_type
* field_type
= NULL
;
742 for (f_iter
= fields
.begin(); f_iter
!= fields
.end(); ++f_iter
) {
743 if ((*f_iter
)->get_name() == v_iter
->first
->get_string()) {
744 field_type
= (*f_iter
)->get_type();
747 if (field_type
== NULL
) {
748 throw std::runtime_error("type error: " + type
->get_name() + " has no field " + v_iter
->first
->get_string());
750 if (v_iter
!= val
.begin())
752 out
<< endl
<< indent() << render_const_value(g_type_string
, v_iter
->first
);
754 out
<< render_const_value(field_type
, v_iter
->second
);
757 out
<< endl
<< indent() << "})";
758 } else if (type
->is_map()) {
759 t_type
* ktype
= ((t_map
*)type
)->get_key_type();
761 t_type
* vtype
= ((t_map
*)type
)->get_val_type();
765 const map
<t_const_value
*, t_const_value
*, t_const_value::value_compare
>& val
= value
->get_map();
766 map
<t_const_value
*, t_const_value
*, t_const_value::value_compare
>::const_iterator v_iter
;
767 for (v_iter
= val
.begin(); v_iter
!= val
.end(); ++v_iter
) {
768 if (v_iter
!= val
.begin())
771 if (ktype
->is_base_type() && ((t_base_type
*)get_true_type(ktype
))->get_base() == t_base_type::TYPE_I64
){
772 out
<< indent() << "\"" << v_iter
->first
->get_integer() << "\"";
774 out
<< indent() << render_const_value(ktype
, v_iter
->first
);
778 out
<< render_const_value(vtype
, v_iter
->second
);
781 out
<< endl
<< indent() << "}";
782 } else if (type
->is_list() || type
->is_set()) {
784 if (type
->is_list()) {
785 etype
= ((t_list
*)type
)->get_elem_type();
787 etype
= ((t_set
*)type
)->get_elem_type();
790 const vector
<t_const_value
*>& val
= value
->get_list();
791 vector
<t_const_value
*>::const_iterator v_iter
;
792 for (v_iter
= val
.begin(); v_iter
!= val
.end(); ++v_iter
) {
793 if (v_iter
!= val
.begin())
795 out
<< render_const_value(etype
, *v_iter
);
805 void t_js_generator::generate_struct(t_struct
* tstruct
) {
806 generate_js_struct(tstruct
, false);
810 * Generates a struct definition for a thrift exception. Basically the same
811 * as a struct but extends the Exception class.
813 * @param txception The struct definition
815 void t_js_generator::generate_xception(t_struct
* txception
) {
816 generate_js_struct(txception
, true);
820 * Structs can be normal or exceptions.
822 void t_js_generator::generate_js_struct(t_struct
* tstruct
, bool is_exception
) {
823 generate_js_struct_definition(f_types_
, tstruct
, is_exception
);
827 * Return type of contained elements for a container type. For maps
828 * this is type of value (keys are always strings in js)
830 t_type
* t_js_generator::get_contained_type(t_type
* t
) {
833 etype
= ((t_list
*)t
)->get_elem_type();
834 } else if (t
->is_set()) {
835 etype
= ((t_set
*)t
)->get_elem_type();
837 etype
= ((t_map
*)t
)->get_val_type();
843 * Generates a struct definition for a thrift data type. This is nothing in JS
844 * where the objects are all just associative arrays (unless of course we
845 * decide to start using objects for them...)
847 * @param tstruct The struct definition
849 void t_js_generator::generate_js_struct_definition(ostream
& out
,
853 const vector
<t_field
*>& members
= tstruct
->get_members();
854 vector
<t_field
*>::const_iterator m_iter
;
857 string prefix
= has_js_namespace(tstruct
->get_program()) ? js_namespace(tstruct
->get_program()) : js_const_type_
;
858 out
<< prefix
<< tstruct
->get_name() <<
859 (is_exported
? " = module.exports." + tstruct
->get_name() : "");
861 f_types_ts_
<< ts_print_doc(tstruct
) << ts_indent() << ts_declare() << "class "
862 << tstruct
->get_name() << (is_exception
? " extends Thrift.TException" : "")
866 out
<< js_namespace(tstruct
->get_program()) << tstruct
->get_name();
868 f_types_ts_
<< ts_print_doc(tstruct
) << ts_indent() << ts_declare() << "class "
869 << tstruct
->get_name() << (is_exception
? " extends Thrift.TException" : "")
875 if (gen_node_
&& is_exception
) {
876 out
<< " = class extends Thrift.TException {" << endl
;
878 out
<< " = class {" << endl
;
881 indent(out
) << "constructor(args) {" << endl
;
883 out
<< " = function(args) {" << endl
;
888 // Call super() method on inherited Error class
889 if (gen_node_
&& is_exception
) {
891 indent(out
) << "super(args);" << endl
;
893 indent(out
) << "Thrift.TException.call(this, \"" << js_namespace(tstruct
->get_program())
894 << tstruct
->get_name() << "\");" << endl
;
896 out
<< indent() << "this.name = \"" << js_namespace(tstruct
->get_program())
897 << tstruct
->get_name() << "\";" << endl
;
900 // members with arguments
901 for (m_iter
= members
.begin(); m_iter
!= members
.end(); ++m_iter
) {
902 string dval
= declare_field(*m_iter
, false, true);
903 t_type
* t
= get_true_type((*m_iter
)->get_type());
904 if ((*m_iter
)->get_value() != NULL
&& !(t
->is_struct() || t
->is_xception())) {
905 dval
= render_const_value((*m_iter
)->get_type(), (*m_iter
)->get_value());
906 out
<< indent() << "this." << (*m_iter
)->get_name() << " = " << dval
<< ";" << endl
;
908 out
<< indent() << dval
<< ";" << endl
;
912 f_types_ts_
<< ts_indent() << "public " << (*m_iter
)->get_name() << ": "
913 << ts_get_type((*m_iter
)->get_type()) << ";" << endl
;
915 f_types_ts_
<< ts_indent() << (*m_iter
)->get_name() << ": "
916 << ts_get_type((*m_iter
)->get_type()) << ";" << endl
;
921 // Generate constructor from array
922 if (members
.size() > 0) {
924 for (m_iter
= members
.begin(); m_iter
!= members
.end(); ++m_iter
) {
925 t_type
* t
= get_true_type((*m_iter
)->get_type());
926 if ((*m_iter
)->get_value() != NULL
&& (t
->is_struct() || t
->is_xception())) {
927 indent(out
) << "this." << (*m_iter
)->get_name() << " = "
928 << render_const_value(t
, (*m_iter
)->get_value()) << ";" << endl
;
932 // Early returns for exceptions
933 for (m_iter
= members
.begin(); m_iter
!= members
.end(); ++m_iter
) {
934 t_type
* t
= get_true_type((*m_iter
)->get_type());
935 if (t
->is_xception()) {
936 out
<< indent() << "if (args instanceof " << js_type_namespace(t
->get_program())
937 << t
->get_name() << ") {" << endl
<< indent() << indent() << "this."
938 << (*m_iter
)->get_name() << " = args;" << endl
<< indent() << indent() << "return;"
939 << endl
<< indent() << "}" << endl
;
943 indent(out
) << "if (args) {" << endl
;
946 f_types_ts_
<< endl
<< ts_indent() << "constructor(args?: { ";
949 for (m_iter
= members
.begin(); m_iter
!= members
.end(); ++m_iter
) {
950 t_type
* t
= get_true_type((*m_iter
)->get_type());
951 indent(out
) << "if (args." << (*m_iter
)->get_name() << " !== undefined && args." << (*m_iter
)->get_name() << " !== null) {" << endl
;
953 indent(out
) << "this." << (*m_iter
)->get_name();
955 if (t
->is_struct()) {
956 out
<< (" = new " + js_type_namespace(t
->get_program()) + t
->get_name() +
957 "(args."+(*m_iter
)->get_name() +");");
959 } else if (t
->is_container()) {
960 t_type
* etype
= get_contained_type(t
);
961 string copyFunc
= t
->is_map() ? "Thrift.copyMap" : "Thrift.copyList";
962 string type_list
= "";
964 while (etype
->is_container()) {
965 if (type_list
.length() > 0) {
968 type_list
+= etype
->is_map() ? "Thrift.copyMap" : "Thrift.copyList";
969 etype
= get_contained_type(etype
);
972 if (etype
->is_struct()) {
973 if (type_list
.length() > 0) {
976 type_list
+= js_type_namespace(etype
->get_program()) + etype
->get_name();
979 if (type_list
.length() > 0) {
985 out
<< (" = " + copyFunc
+ "(args." + (*m_iter
)->get_name() +
986 ", [" + type_list
+ "]);");
989 out
<< " = args." << (*m_iter
)->get_name() << ";" << endl
;
993 if (!(*m_iter
)->get_req()) {
994 indent(out
) << "} else {" << endl
;
996 << " throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.UNKNOWN, "
997 "'Required field " << (*m_iter
)->get_name() << " is unset!');" << endl
;
999 indent(out
) << "}" << endl
;
1001 f_types_ts_
<< (*m_iter
)->get_name() << ts_get_req(*m_iter
) << ": "
1002 << ts_get_type((*m_iter
)->get_type()) << "; ";
1006 out
<< indent() << "}" << endl
;
1008 f_types_ts_
<< "});" << endl
;
1012 // Done with constructor
1015 indent(out
) << "}" << endl
<< endl
;
1017 indent(out
) << "};" << endl
;
1021 f_types_ts_
<< ts_indent() << "}" << endl
;
1026 out
<< "Thrift.inherits(" << js_namespace(tstruct
->get_program()) << tstruct
->get_name()
1027 << ", Thrift.TException);" << endl
;
1028 out
<< js_namespace(tstruct
->get_program()) << tstruct
->get_name() << ".prototype.name = '"
1029 << tstruct
->get_name() << "';" << endl
;
1031 // init prototype manually if we aren't using es6
1032 out
<< js_namespace(tstruct
->get_program()) << tstruct
->get_name() << ".prototype = {};"
1038 generate_js_struct_reader(out
, tstruct
);
1039 generate_js_struct_writer(out
, tstruct
);
1041 // Close out the class definition
1044 indent(out
) << "};" << endl
;
1049 * Generates the read() method for a struct
1051 void t_js_generator::generate_js_struct_reader(ostream
& out
, t_struct
* tstruct
) {
1052 const vector
<t_field
*>& fields
= tstruct
->get_members();
1053 vector
<t_field
*>::const_iterator f_iter
;
1056 indent(out
) << "read (input) {" << endl
;
1058 indent(out
) << js_namespace(tstruct
->get_program()) << tstruct
->get_name()
1059 << ".prototype.read = function(input) {" << endl
;
1064 indent(out
) << "input.readStructBegin();" << endl
;
1066 // Loop over reading in fields
1067 indent(out
) << "while (true) {" << endl
;
1071 indent(out
) << js_const_type_
<< "ret = input.readFieldBegin();" << endl
;
1072 indent(out
) << js_const_type_
<< "ftype = ret.ftype;" << endl
;
1073 if (!fields
.empty()) {
1074 indent(out
) << js_const_type_
<< "fid = ret.fid;" << endl
;
1077 // Check for field STOP marker and break
1078 indent(out
) << "if (ftype == Thrift.Type.STOP) {" << endl
;
1080 indent(out
) << "break;" << endl
;
1082 indent(out
) << "}" << endl
;
1083 if (!fields
.empty()) {
1084 // Switch statement on the field we are reading
1085 indent(out
) << "switch (fid) {" << endl
;
1089 // Generate deserialization code for known cases
1090 for (f_iter
= fields
.begin(); f_iter
!= fields
.end(); ++f_iter
) {
1092 indent(out
) << "case " << (*f_iter
)->get_key() << ":" << endl
;
1093 indent(out
) << "if (ftype == " << type_to_enum((*f_iter
)->get_type()) << ") {" << endl
;
1096 generate_deserialize_field(out
, *f_iter
, "this.");
1099 indent(out
) << "} else {" << endl
;
1101 indent(out
) << " input.skip(ftype);" << endl
;
1103 out
<< indent() << "}" << endl
<< indent() << "break;" << endl
;
1105 if (fields
.size() == 1) {
1106 // pseudo case to make jslint happy
1107 indent(out
) << "case 0:" << endl
;
1108 indent(out
) << " input.skip(ftype);" << endl
;
1109 indent(out
) << " break;" << endl
;
1111 // In the default case we skip the field
1112 indent(out
) << "default:" << endl
;
1113 indent(out
) << " input.skip(ftype);" << endl
;
1117 indent(out
) << "input.skip(ftype);" << endl
;
1120 indent(out
) << "input.readFieldEnd();" << endl
;
1124 indent(out
) << "input.readStructEnd();" << endl
;
1126 indent(out
) << "return;" << endl
;
1131 indent(out
) << "}" << endl
<< endl
;
1133 indent(out
) << "};" << endl
<< endl
;
1138 * Generates the write() method for a struct
1140 void t_js_generator::generate_js_struct_writer(ostream
& out
, t_struct
* tstruct
) {
1141 string name
= tstruct
->get_name();
1142 const vector
<t_field
*>& fields
= tstruct
->get_members();
1143 vector
<t_field
*>::const_iterator f_iter
;
1146 indent(out
) << "write (output) {" << endl
;
1148 indent(out
) << js_namespace(tstruct
->get_program()) << tstruct
->get_name()
1149 << ".prototype.write = function(output) {" << endl
;
1154 indent(out
) << "output.writeStructBegin('" << name
<< "');" << endl
;
1156 for (f_iter
= fields
.begin(); f_iter
!= fields
.end(); ++f_iter
) {
1157 out
<< indent() << "if (this." << (*f_iter
)->get_name() << " !== null && this."
1158 << (*f_iter
)->get_name() << " !== undefined) {" << endl
;
1161 indent(out
) << "output.writeFieldBegin("
1162 << "'" << (*f_iter
)->get_name() << "', " << type_to_enum((*f_iter
)->get_type())
1163 << ", " << (*f_iter
)->get_key() << ");" << endl
;
1165 // Write field contents
1166 generate_serialize_field(out
, *f_iter
, "this.");
1168 indent(out
) << "output.writeFieldEnd();" << endl
;
1171 indent(out
) << "}" << endl
;
1174 out
<< indent() << "output.writeFieldStop();" << endl
<< indent() << "output.writeStructEnd();"
1177 out
<< indent() << "return;" << endl
;
1181 out
<< indent() << "}" << endl
<< endl
;
1183 out
<< indent() << "};" << endl
<< endl
;
1188 * Generates a thrift service.
1190 * @param tservice The service definition
1192 void t_js_generator::generate_service(t_service
* tservice
) {
1193 string f_service_name
= get_out_dir() + service_name_
+ ".js";
1194 f_service_
.open(f_service_name
.c_str());
1195 if (gen_episode_file_
) {
1196 f_episode_
<< service_name_
<< ":" << thrift_package_output_directory_
<< "/" << service_name_
<< endl
;
1200 string f_service_ts_name
= get_out_dir() + service_name_
+ ".d.ts";
1201 f_service_ts_
.open(f_service_ts_name
.c_str());
1204 f_service_
<< autogen_comment();
1206 if ((gen_node_
|| gen_es6_
) && no_ns_
) {
1207 f_service_
<< "\"use strict\";" << endl
<< endl
;
1210 f_service_
<< js_includes() << endl
<< render_includes() << endl
;
1213 if (tservice
->get_extends() != NULL
) {
1214 f_service_ts_
<< "/// <reference path=\"" << tservice
->get_extends()->get_name()
1215 << ".d.ts\" />" << endl
;
1217 f_service_ts_
<< autogen_comment() << endl
<< ts_includes() << endl
<< render_ts_includes() << endl
;
1219 f_service_ts_
<< "import ttypes = require('./" + program_
->get_name() + "_types');" << endl
;
1220 // Generate type aliases
1222 vector
<t_enum
*> const& enums
= program_
->get_enums();
1223 vector
<t_enum
*>::const_iterator e_iter
;
1224 for (e_iter
= enums
.begin(); e_iter
!= enums
.end(); ++e_iter
) {
1225 f_service_ts_
<< "import " << (*e_iter
)->get_name() << " = ttypes."
1226 << js_namespace(program_
) << (*e_iter
)->get_name() << endl
;
1229 vector
<t_const
*> const& consts
= program_
->get_consts();
1230 vector
<t_const
*>::const_iterator c_iter
;
1231 for (c_iter
= consts
.begin(); c_iter
!= consts
.end(); ++c_iter
) {
1232 f_service_ts_
<< "import " << (*c_iter
)->get_name() << " = ttypes."
1233 << js_namespace(program_
) << (*c_iter
)->get_name() << endl
;
1236 vector
<t_struct
*> const& exceptions
= program_
->get_xceptions();
1237 vector
<t_struct
*>::const_iterator x_iter
;
1238 for (x_iter
= exceptions
.begin(); x_iter
!= exceptions
.end(); ++x_iter
) {
1239 f_service_ts_
<< "import " << (*x_iter
)->get_name() << " = ttypes."
1240 << js_namespace(program_
) << (*x_iter
)->get_name() << endl
;
1243 vector
<t_struct
*> const& structs
= program_
->get_structs();
1244 vector
<t_struct
*>::const_iterator s_iter
;
1245 for (s_iter
= structs
.begin(); s_iter
!= structs
.end(); ++s_iter
) {
1246 f_service_ts_
<< "import " << (*s_iter
)->get_name() << " = ttypes."
1247 << js_namespace(program_
) << (*s_iter
)->get_name() << endl
;
1250 f_service_ts_
<< "import { " << program_
->get_name() << " } from \"./" << program_
->get_name() << "_types\";" << endl
<< endl
;
1252 if (!ts_module_
.empty()) {
1254 f_service_ts_
<< "declare module " << ts_module_
<< " {";
1256 f_service_ts_
<< "declare module \"./" << program_
->get_name() << "_types\" {" << endl
;
1258 f_service_ts_
<< ts_indent() << "module " << program_
->get_name() << " {" << endl
;
1265 if (tservice
->get_extends() != NULL
) {
1266 f_service_
<< js_const_type_
<< tservice
->get_extends()->get_name() << " = require('./"
1267 << tservice
->get_extends()->get_name() << "');" << endl
<< js_const_type_
1268 << tservice
->get_extends()->get_name()
1269 << "Client = " << tservice
->get_extends()->get_name() << ".Client;" << endl
1270 << js_const_type_
<< tservice
->get_extends()->get_name()
1271 << "Processor = " << tservice
->get_extends()->get_name() << ".Processor;" << endl
;
1274 f_service_
<< js_const_type_
<< "ttypes = require('./" + program_
->get_name() + "_types');" << endl
;
1277 generate_service_helpers(tservice
);
1278 generate_service_interface(tservice
);
1279 generate_service_client(tservice
);
1282 generate_service_processor(tservice
);
1287 if (!ts_module_
.empty()) {
1289 f_service_ts_
<< "}" << endl
;
1292 f_service_ts_
<< ts_indent() << "}" << endl
;
1293 f_service_ts_
<< "}" << endl
;
1296 f_service_ts_
.close();
1301 * Generates a service server definition.
1303 * @param tservice The service to generate a server for.
1305 void t_js_generator::generate_service_processor(t_service
* tservice
) {
1306 vector
<t_function
*> functions
= tservice
->get_functions();
1307 vector
<t_function
*>::iterator f_iter
;
1310 string prefix
= has_js_namespace(tservice
->get_program()) ? js_namespace(tservice
->get_program()) : js_const_type_
;
1311 f_service_
<< prefix
<< service_name_
<< "Processor = " << "exports.Processor";
1313 f_service_ts_
<< endl
<< "declare class Processor ";
1314 if (tservice
->get_extends() != NULL
) {
1315 f_service_ts_
<< "extends " << tservice
->get_extends()->get_name() << "Processor ";
1317 f_service_ts_
<< "{" << endl
;
1319 f_service_ts_
<< ts_indent() << "private _handler: object;" << endl
<< endl
;
1320 f_service_ts_
<< ts_indent() << "constructor(handler: object);" << endl
;
1321 f_service_ts_
<< ts_indent() << "process(input: thrift.TProtocol, output: thrift.TProtocol): void;" << endl
;
1325 f_service_
<< js_namespace(tservice
->get_program()) << service_name_
<< "Processor = "
1326 << "exports.Processor";
1329 bool is_subclass_service
= tservice
->get_extends() != NULL
;
1333 if (is_subclass_service
) {
1334 f_service_
<< " = class extends " << tservice
->get_extends()->get_name() << "Processor {" << endl
;
1336 f_service_
<< " = class {" << endl
;
1339 indent(f_service_
) << "constructor(handler) {" << endl
;
1341 f_service_
<< " = function(handler) {" << endl
;
1345 if (gen_es6_
&& is_subclass_service
) {
1346 indent(f_service_
) << "super(handler);" << endl
;
1348 indent(f_service_
) << "this._handler = handler;" << endl
;
1351 // Done with constructor
1353 indent(f_service_
) << "}" << endl
;
1355 indent(f_service_
) << "};" << endl
;
1358 // ES5 service inheritance
1359 if (!gen_es6_
&& is_subclass_service
) {
1360 indent(f_service_
) << "Thrift.inherits(" << js_namespace(tservice
->get_program())
1361 << service_name_
<< "Processor, " << tservice
->get_extends()->get_name()
1362 << "Processor);" << endl
;
1365 // Generate the server implementation
1367 indent(f_service_
) << "process (input, output) {" << endl
;
1369 indent(f_service_
) << js_namespace(tservice
->get_program()) << service_name_
1370 << "Processor.prototype.process = function(input, output) {" << endl
;
1375 indent(f_service_
) << js_const_type_
<< "r = input.readMessageBegin();" << endl
<< indent()
1376 << "if (this['process_' + r.fname]) {" << endl
<< indent()
1377 << " return this['process_' + r.fname].call(this, r.rseqid, input, output);" << endl
1378 << indent() << "} else {" << endl
<< indent() << " input.skip(Thrift.Type.STRUCT);"
1379 << endl
<< indent() << " input.readMessageEnd();" << endl
<< indent()
1380 << " " << js_const_type_
<< "x = new "
1381 "Thrift.TApplicationException(Thrift.TApplicationExceptionType.UNKNOWN_METHOD, "
1382 "'Unknown function ' + r.fname);" << endl
<< indent()
1383 << " output.writeMessageBegin(r.fname, Thrift.MessageType.EXCEPTION, r.rseqid);"
1384 << endl
<< indent() << " x.write(output);" << endl
<< indent()
1385 << " output.writeMessageEnd();" << endl
<< indent() << " output.flush();" << endl
1386 << indent() << "}" << endl
;
1390 indent(f_service_
) << "}" << endl
;
1392 indent(f_service_
) << "};" << endl
;
1395 // Generate the process subfunctions
1396 for (f_iter
= functions
.begin(); f_iter
!= functions
.end(); ++f_iter
) {
1397 generate_process_function(tservice
, *f_iter
);
1400 // Close off the processor class definition
1403 indent(f_service_
) << "};" << endl
;
1405 if (gen_node_
&& gen_ts_
) {
1406 f_service_ts_
<< "}" << endl
;
1411 * Generates a process function definition.
1413 * @param tfunction The function to write a dispatcher for
1415 void t_js_generator::generate_process_function(t_service
* tservice
, t_function
* tfunction
) {
1417 indent(f_service_
) << "process_" + tfunction
->get_name() + " (seqid, input, output) {" << endl
;
1419 indent(f_service_
) << js_namespace(tservice
->get_program()) << service_name_
1420 << "Processor.prototype.process_" + tfunction
->get_name()
1421 + " = function(seqid, input, output) {" << endl
;
1425 f_service_ts_
<< ts_indent() << "process_" << tfunction
->get_name() << "(seqid: number, input: thrift.TProtocol, output: thrift.TProtocol): void;" << endl
;
1431 string argsname
= js_namespace(program_
) + service_name_
+ "_" + tfunction
->get_name() + "_args";
1432 string resultname
= js_namespace(program_
) + service_name_
+ "_" + tfunction
->get_name()
1435 indent(f_service_
) << js_const_type_
<< "args = new " << argsname
<< "();" << endl
<< indent()
1436 << "args.read(input);" << endl
<< indent() << "input.readMessageEnd();" << endl
;
1438 // Generate the function call
1439 t_struct
* arg_struct
= tfunction
->get_arglist();
1440 const std::vector
<t_field
*>& fields
= arg_struct
->get_members();
1441 vector
<t_field
*>::const_iterator f_iter
;
1443 // Shortcut out here for oneway functions
1444 if (tfunction
->is_oneway()) {
1445 indent(f_service_
) << "this._handler." << tfunction
->get_name() << "(";
1448 for (f_iter
= fields
.begin(); f_iter
!= fields
.end(); ++f_iter
) {
1454 f_service_
<< "args." << (*f_iter
)->get_name();
1457 f_service_
<< ");" << endl
;
1461 indent(f_service_
) << "}" << endl
;
1463 indent(f_service_
) << "};" << endl
;
1468 // Promise style invocation
1469 indent(f_service_
) << "if (this._handler." << tfunction
->get_name()
1470 << ".length === " << fields
.size() << ") {" << endl
;
1474 indent(f_service_
) << "Promise.resolve(this._handler." << tfunction
->get_name() << ".bind(this._handler)(" << endl
;
1476 string maybeComma
= (fields
.size() > 0 ? "," : "");
1477 indent(f_service_
) << "Q.fcall(this._handler." << tfunction
->get_name() << ".bind(this._handler)"
1478 << maybeComma
<< endl
;
1482 for (f_iter
= fields
.begin(); f_iter
!= fields
.end(); ++f_iter
) {
1483 string maybeComma
= (f_iter
!= fields
.end() - 1 ? "," : "");
1484 indent(f_service_
) << "args." << (*f_iter
)->get_name() << maybeComma
<< endl
;
1489 indent(f_service_
) << ")).then(result => {" << endl
;
1491 indent(f_service_
) << ").then(function(result) {" << endl
;
1495 f_service_
<< indent() << js_const_type_
<< "result_obj = new " << resultname
<< "({success: result});" << endl
1496 << indent() << "output.writeMessageBegin(\"" << tfunction
->get_name()
1497 << "\", Thrift.MessageType.REPLY, seqid);" << endl
<< indent()
1498 << "result_obj.write(output);" << endl
<< indent() << "output.writeMessageEnd();" << endl
1499 << indent() << "output.flush();" << endl
;
1503 indent(f_service_
) << "}).catch(err => {" << endl
;
1505 indent(f_service_
) << "}).catch(function (err) {" << endl
;
1508 indent(f_service_
) << js_let_type_
<< "result;" << endl
;
1510 bool has_exception
= false;
1511 t_struct
* exceptions
= tfunction
->get_xceptions();
1513 const vector
<t_field
*>& members
= exceptions
->get_members();
1514 for (auto member
: members
) {
1515 t_type
* t
= get_true_type(member
->get_type());
1516 if (t
->is_xception()) {
1517 if (!has_exception
) {
1518 has_exception
= true;
1519 indent(f_service_
) << "if (err instanceof " << js_type_namespace(t
->get_program())
1522 f_service_
<< " || err instanceof " << js_type_namespace(t
->get_program())
1529 if (has_exception
) {
1530 f_service_
<< ") {" << endl
;
1532 f_service_
<< indent() << "result = new " << resultname
<< "(err);" << endl
<< indent()
1533 << "output.writeMessageBegin(\"" << tfunction
->get_name()
1534 << "\", Thrift.MessageType.REPLY, seqid);" << endl
;
1537 indent(f_service_
) << "} else {" << endl
;
1541 f_service_
<< indent() << "result = new "
1542 "Thrift.TApplicationException(Thrift.TApplicationExceptionType.UNKNOWN,"
1543 " err.message);" << endl
<< indent() << "output.writeMessageBegin(\""
1544 << tfunction
->get_name() << "\", Thrift.MessageType.EXCEPTION, seqid);" << endl
;
1546 if (has_exception
) {
1548 indent(f_service_
) << "}" << endl
;
1551 f_service_
<< indent() << "result.write(output);" << endl
<< indent()
1552 << "output.writeMessageEnd();" << endl
<< indent() << "output.flush();" << endl
;
1554 indent(f_service_
) << "});" << endl
;
1556 // End promise style invocation
1558 // Callback style invocation
1559 indent(f_service_
) << "} else {" << endl
;
1561 indent(f_service_
) << "this._handler." << tfunction
->get_name() << "(";
1563 for (f_iter
= fields
.begin(); f_iter
!= fields
.end(); ++f_iter
) {
1564 f_service_
<< "args." << (*f_iter
)->get_name() << ", ";
1568 f_service_
<< "(err, result) => {" << endl
;
1570 f_service_
<< "function (err, result) {" << endl
;
1573 indent(f_service_
) << js_let_type_
<< "result_obj;" << endl
;
1575 indent(f_service_
) << "if ((err === null || typeof err === 'undefined')";
1576 if (has_exception
) {
1577 const vector
<t_field
*>& members
= exceptions
->get_members();
1578 for (auto member
: members
) {
1579 t_type
* t
= get_true_type(member
->get_type());
1580 if (t
->is_xception()) {
1581 f_service_
<< " || err instanceof " << js_type_namespace(t
->get_program()) << t
->get_name();
1585 f_service_
<< ") {" << endl
;
1587 f_service_
<< indent() << "result_obj = new " << resultname
1588 << "((err !== null || typeof err === 'undefined') ? err : {success: result});" << endl
<< indent()
1589 << "output.writeMessageBegin(\"" << tfunction
->get_name()
1590 << "\", Thrift.MessageType.REPLY, seqid);" << endl
;
1592 indent(f_service_
) << "} else {" << endl
;
1594 f_service_
<< indent() << "result_obj = new "
1595 "Thrift.TApplicationException(Thrift.TApplicationExceptionType.UNKNOWN,"
1596 " err.message);" << endl
<< indent() << "output.writeMessageBegin(\""
1597 << tfunction
->get_name() << "\", Thrift.MessageType.EXCEPTION, seqid);" << endl
;
1599 f_service_
<< indent() << "}" << endl
<< indent() << "result_obj.write(output);" << endl
<< indent()
1600 << "output.writeMessageEnd();" << endl
<< indent() << "output.flush();" << endl
;
1603 indent(f_service_
) << "});" << endl
;
1605 indent(f_service_
) << "}" << endl
;
1606 // End callback style invocation
1611 indent(f_service_
) << "}" << endl
;
1613 indent(f_service_
) << "};" << endl
;
1618 * Generates helper functions for a service.
1620 * @param tservice The service to generate a header definition for
1622 void t_js_generator::generate_service_helpers(t_service
* tservice
) {
1623 // Do not generate TS definitions for helper functions
1624 bool gen_ts_tmp
= gen_ts_
;
1627 vector
<t_function
*> functions
= tservice
->get_functions();
1628 vector
<t_function
*>::iterator f_iter
;
1630 f_service_
<< "//HELPER FUNCTIONS AND STRUCTURES" << endl
<< endl
;
1632 for (f_iter
= functions
.begin(); f_iter
!= functions
.end(); ++f_iter
) {
1633 t_struct
* ts
= (*f_iter
)->get_arglist();
1634 string name
= ts
->get_name();
1635 ts
->set_name(service_name_
+ "_" + name
);
1636 generate_js_struct_definition(f_service_
, ts
, false, false);
1637 generate_js_function_helpers(*f_iter
);
1641 gen_ts_
= gen_ts_tmp
;
1645 * Generates a struct and helpers for a function.
1647 * @param tfunction The function
1649 void t_js_generator::generate_js_function_helpers(t_function
* tfunction
) {
1650 t_struct
result(program_
, service_name_
+ "_" + tfunction
->get_name() + "_result");
1651 t_field
success(tfunction
->get_returntype(), "success", 0);
1652 if (!tfunction
->get_returntype()->is_void()) {
1653 result
.append(&success
);
1656 t_struct
* xs
= tfunction
->get_xceptions();
1657 const vector
<t_field
*>& fields
= xs
->get_members();
1658 vector
<t_field
*>::const_iterator f_iter
;
1659 for (f_iter
= fields
.begin(); f_iter
!= fields
.end(); ++f_iter
) {
1660 result
.append(*f_iter
);
1663 generate_js_struct_definition(f_service_
, &result
, false, false);
1667 * Generates a service interface definition.
1669 * @param tservice The service to generate a header definition for
1671 void t_js_generator::generate_service_interface(t_service
* tservice
) {
1676 * Generates a REST interface
1678 void t_js_generator::generate_service_rest(t_service
* tservice
) {
1683 * Generates a service client definition.
1685 * @param tservice The service to generate a server for.
1687 void t_js_generator::generate_service_client(t_service
* tservice
) {
1689 bool is_subclass_service
= tservice
->get_extends() != NULL
;
1692 string prefix
= has_js_namespace(tservice
->get_program()) ? js_namespace(tservice
->get_program()) : js_const_type_
;
1693 f_service_
<< prefix
<< service_name_
<< "Client = " << "exports.Client";
1695 f_service_ts_
<< ts_print_doc(tservice
) << ts_indent() << ts_declare() << "class "
1697 if (tservice
->get_extends() != NULL
) {
1698 f_service_ts_
<< "extends " << tservice
->get_extends()->get_name() << "Client ";
1700 f_service_ts_
<< "{" << endl
;
1703 f_service_
<< js_namespace(tservice
->get_program()) << service_name_
1706 f_service_ts_
<< ts_print_doc(tservice
) << ts_indent() << ts_declare() << "class "
1707 << service_name_
<< "Client ";
1708 if (is_subclass_service
) {
1709 f_service_ts_
<< "extends " << tservice
->get_extends()->get_name() << "Client ";
1711 f_service_ts_
<< "{" << endl
;
1717 if (is_subclass_service
) {
1718 f_service_
<< " = class extends " << js_namespace(tservice
->get_extends()->get_program())
1719 << tservice
->get_extends()->get_name() << "Client {" << endl
;
1721 f_service_
<< " = class {" << endl
;
1725 indent(f_service_
) << "constructor(output, pClass) {" << endl
;
1727 indent(f_service_
) << "constructor(input, output) {" << endl
;
1731 f_service_
<< " = function(output, pClass) {" << endl
;
1733 f_service_
<< " = function(input, output) {" << endl
;
1740 indent(f_service_
) << "this.output = output;" << endl
;
1741 indent(f_service_
) << "this.pClass = pClass;" << endl
;
1742 indent(f_service_
) << "this._seqid = 0;" << endl
;
1743 indent(f_service_
) << "this._reqs = {};" << endl
;
1745 f_service_ts_
<< ts_indent() << "private output: thrift.TTransport;" << endl
1746 << ts_indent() << "private pClass: thrift.TProtocol;" << endl
1747 << ts_indent() << "private _seqid: number;" << endl
1749 << ts_indent() << "constructor(output: thrift.TTransport, pClass: { new(trans: thrift.TTransport): thrift.TProtocol });"
1753 indent(f_service_
) << "this.input = input;" << endl
;
1754 indent(f_service_
) << "this.output = (!output) ? input : output;" << endl
;
1755 indent(f_service_
) << "this.seqid = 0;" << endl
;
1757 f_service_ts_
<< ts_indent() << "input: Thrift.TJSONProtocol;" << endl
<< ts_indent()
1758 << "output: Thrift.TJSONProtocol;" << endl
<< ts_indent() << "seqid: number;"
1759 << endl
<< endl
<< ts_indent()
1760 << "constructor(input: Thrift.TJSONProtocol, output?: Thrift.TJSONProtocol);"
1768 indent(f_service_
) << "}" << endl
;
1770 indent(f_service_
) << "};" << endl
;
1771 if (is_subclass_service
) {
1772 indent(f_service_
) << "Thrift.inherits(" << js_namespace(tservice
->get_program())
1773 << service_name_
<< "Client, "
1774 << js_namespace(tservice
->get_extends()->get_program())
1775 << tservice
->get_extends()->get_name() << "Client);" << endl
;
1778 indent(f_service_
) << js_namespace(tservice
->get_program()) << service_name_
1779 << "Client.prototype = {};" << endl
;
1783 // utils for multiplexed services
1786 indent(f_service_
) << "seqid () { return this._seqid; }" << endl
;
1787 indent(f_service_
) << "new_seqid () { return this._seqid += 1; }" << endl
;
1789 indent(f_service_
) << js_namespace(tservice
->get_program()) << service_name_
1790 << "Client.prototype.seqid = function() { return this._seqid; };" << endl
1791 << js_namespace(tservice
->get_program()) << service_name_
1792 << "Client.prototype.new_seqid = function() { return this._seqid += 1; };"
1797 // Generate client method implementations
1798 vector
<t_function
*> functions
= tservice
->get_functions();
1799 vector
<t_function
*>::const_iterator f_iter
;
1800 for (f_iter
= functions
.begin(); f_iter
!= functions
.end(); ++f_iter
) {
1801 t_struct
* arg_struct
= (*f_iter
)->get_arglist();
1802 const vector
<t_field
*>& fields
= arg_struct
->get_members();
1803 vector
<t_field
*>::const_iterator fld_iter
;
1804 string funname
= (*f_iter
)->get_name();
1805 string arglist
= argument_list(arg_struct
);
1810 indent(f_service_
) << funname
<< " (" << arglist
<< ") {" << endl
;
1812 indent(f_service_
) << js_namespace(tservice
->get_program()) << service_name_
<< "Client.prototype."
1813 << function_signature(*f_iter
, "", !gen_es6_
) << " {" << endl
;
1819 // function definition without callback
1820 f_service_ts_
<< ts_print_doc(*f_iter
) << ts_indent() << ts_function_signature(*f_iter
, false) << endl
;
1822 // overload with callback
1823 f_service_ts_
<< ts_print_doc(*f_iter
) << ts_indent() << ts_function_signature(*f_iter
, true) << endl
;
1825 // overload with callback
1826 f_service_ts_
<< ts_print_doc(*f_iter
) << ts_indent() << ts_function_signature(*f_iter
, true) << endl
;
1830 if (gen_es6_
&& gen_node_
) {
1831 indent(f_service_
) << "this._seqid = this.new_seqid();" << endl
;
1832 indent(f_service_
) << js_const_type_
<< "self = this;" << endl
<< indent()
1833 << "return new Promise((resolve, reject) => {" << endl
;
1835 indent(f_service_
) << "self._reqs[self.seqid()] = (error, result) => {" << endl
;
1837 indent(f_service_
) << "return error ? reject(error) : resolve(result);" << endl
;
1839 indent(f_service_
) << "};" << endl
;
1840 indent(f_service_
) << "self.send_" << funname
<< "(" << arglist
<< ");" << endl
;
1842 indent(f_service_
) << "});" << endl
;
1843 } else if (gen_node_
) { // Node.js output ./gen-nodejs
1844 f_service_
<< indent() << "this._seqid = this.new_seqid();" << endl
<< indent()
1845 << "if (callback === undefined) {" << endl
;
1847 f_service_
<< indent() << js_const_type_
<< "_defer = Q.defer();" << endl
<< indent()
1848 << "this._reqs[this.seqid()] = function(error, result) {" << endl
;
1850 indent(f_service_
) << "if (error) {" << endl
;
1852 indent(f_service_
) << "_defer.reject(error);" << endl
;
1854 indent(f_service_
) << "} else {" << endl
;
1856 indent(f_service_
) << "_defer.resolve(result);" << endl
;
1858 indent(f_service_
) << "}" << endl
;
1860 indent(f_service_
) << "};" << endl
;
1861 f_service_
<< indent() << "this.send_" << funname
<< "(" << arglist
<< ");" << endl
1862 << indent() << "return _defer.promise;" << endl
;
1864 indent(f_service_
) << "} else {" << endl
;
1866 f_service_
<< indent() << "this._reqs[this.seqid()] = callback;" << endl
<< indent()
1867 << "this.send_" << funname
<< "(" << arglist
<< ");" << endl
;
1869 indent(f_service_
) << "}" << endl
;
1870 } else if (gen_es6_
) {
1871 f_service_
<< indent() << js_const_type_
<< "self = this;" << endl
<< indent()
1872 << "return new Promise((resolve, reject) => {" << endl
;
1874 f_service_
<< indent() << "self.send_" << funname
<< "(" << arglist
1875 << (arglist
.empty() ? "" : ", ") << "(error, result) => {" << endl
;
1877 indent(f_service_
) << "return error ? reject(error) : resolve(result);" << endl
;
1879 f_service_
<< indent() << "});" << endl
;
1881 f_service_
<< indent() << "});" << endl
;
1883 } else if (gen_jquery_
) { // jQuery output ./gen-js
1884 f_service_
<< indent() << "if (callback === undefined) {" << endl
;
1886 f_service_
<< indent() << "this.send_" << funname
<< "(" << arglist
<< ");" << endl
;
1887 if (!(*f_iter
)->is_oneway()) {
1888 f_service_
<< indent();
1889 if (!(*f_iter
)->get_returntype()->is_void()) {
1890 f_service_
<< "return ";
1892 f_service_
<< "this.recv_" << funname
<< "();" << endl
;
1895 f_service_
<< indent() << "} else {" << endl
;
1897 f_service_
<< indent() << js_const_type_
<< "postData = this.send_" << funname
<< "(" << arglist
1898 << (arglist
.empty() ? "" : ", ") << "true);" << endl
;
1899 f_service_
<< indent() << "return this.output.getTransport()" << endl
;
1901 f_service_
<< indent() << ".jqRequest(this, postData, arguments, this.recv_" << funname
1905 f_service_
<< indent() << "}" << endl
;
1906 } else { // Standard JavaScript ./gen-js
1907 f_service_
<< indent() << "this.send_" << funname
<< "(" << arglist
1908 << (arglist
.empty() ? "" : ", ") << "callback); " << endl
;
1909 if (!(*f_iter
)->is_oneway()) {
1910 f_service_
<< indent() << "if (!callback) {" << endl
;
1911 f_service_
<< indent();
1912 if (!(*f_iter
)->get_returntype()->is_void()) {
1913 f_service_
<< " return ";
1915 f_service_
<< "this.recv_" << funname
<< "();" << endl
;
1916 f_service_
<< indent() << "}" << endl
;
1923 indent(f_service_
) << "}" << endl
<< endl
;
1925 indent(f_service_
) << "};" << endl
<< endl
;
1931 indent(f_service_
) << "send_" << funname
<< " (" << arglist
<< ") {" << endl
;
1933 // ES6 js still uses callbacks here. Should refactor this to promise style later..
1934 indent(f_service_
) << "send_" << funname
<< " (" << argument_list(arg_struct
, true) << ") {" << endl
;
1937 indent(f_service_
) << js_namespace(tservice
->get_program()) << service_name_
<< "Client.prototype.send_"
1938 << function_signature(*f_iter
, "", !gen_node_
) << " {" << endl
;
1943 std::string outputVar
;
1945 f_service_
<< indent() << js_const_type_
<< "output = new this.pClass(this.output);" << endl
;
1946 outputVar
= "output";
1948 outputVar
= "this.output";
1951 std::string argsname
= js_namespace(program_
) + service_name_
+ "_" + (*f_iter
)->get_name()
1954 std::string messageType
= (*f_iter
)->is_oneway() ? "Thrift.MessageType.ONEWAY"
1955 : "Thrift.MessageType.CALL";
1957 if (fields
.size() > 0){
1958 f_service_
<< indent() << js_const_type_
<< "params = {" << endl
;
1960 for (fld_iter
= fields
.begin(); fld_iter
!= fields
.end(); ++fld_iter
) {
1961 indent(f_service_
) << (*fld_iter
)->get_name() << ": " << (*fld_iter
)->get_name();
1962 if (fld_iter
!= fields
.end()-1) {
1963 f_service_
<< "," << endl
;
1969 indent(f_service_
) << "};" << endl
;
1970 indent(f_service_
) << js_const_type_
<< "args = new " << argsname
<< "(params);" << endl
;
1972 indent(f_service_
) << js_const_type_
<< "args = new " << argsname
<< "();" << endl
;
1976 // Serialize the request header within try/catch
1977 indent(f_service_
) << "try {" << endl
;
1981 f_service_
<< indent() << outputVar
<< ".writeMessageBegin('" << (*f_iter
)->get_name()
1982 << "', " << messageType
<< ", this.seqid());" << endl
;
1984 f_service_
<< indent() << outputVar
<< ".writeMessageBegin('" << (*f_iter
)->get_name()
1985 << "', " << messageType
<< ", this.seqid);" << endl
;
1989 // Write to the stream
1990 f_service_
<< indent() << "args.write(" << outputVar
<< ");" << endl
<< indent() << outputVar
1991 << ".writeMessageEnd();" << endl
;
1994 if((*f_iter
)->is_oneway()) {
1995 f_service_
<< indent() << "this.output.flush();" << endl
;
1996 f_service_
<< indent() << js_const_type_
<< "callback = this._reqs[this.seqid()] || function() {};" << endl
;
1997 f_service_
<< indent() << "delete this._reqs[this.seqid()];" << endl
;
1998 f_service_
<< indent() << "callback(null);" << endl
;
2000 f_service_
<< indent() << "return this.output.flush();" << endl
;
2004 f_service_
<< indent() << "return this.output.getTransport().flush(callback);" << endl
;
2005 } else if (gen_es6_
) {
2006 f_service_
<< indent() << js_const_type_
<< "self = this;" << endl
;
2007 if((*f_iter
)->is_oneway()) {
2008 f_service_
<< indent() << "this.output.getTransport().flush(true, null);" << endl
;
2009 f_service_
<< indent() << "callback();" << endl
;
2011 f_service_
<< indent() << "this.output.getTransport().flush(true, () => {" << endl
;
2013 f_service_
<< indent() << js_let_type_
<< "error = null, result = null;" << endl
;
2014 f_service_
<< indent() << "try {" << endl
;
2015 f_service_
<< indent() << " result = self.recv_" << funname
<< "();" << endl
;
2016 f_service_
<< indent() << "} catch (e) {" << endl
;
2017 f_service_
<< indent() << " error = e;" << endl
;
2018 f_service_
<< indent() << "}" << endl
;
2019 f_service_
<< indent() << "callback(error, result);" << endl
;
2021 f_service_
<< indent() << "});" << endl
;
2024 f_service_
<< indent() << "if (callback) {" << endl
;
2026 if((*f_iter
)->is_oneway()) {
2027 f_service_
<< indent() << "this.output.getTransport().flush(true, null);" << endl
;
2028 f_service_
<< indent() << "callback();" << endl
;
2030 f_service_
<< indent() << js_const_type_
<< "self = this;" << endl
;
2031 f_service_
<< indent() << "this.output.getTransport().flush(true, function() {" << endl
;
2033 f_service_
<< indent() << js_let_type_
<< "result = null;" << endl
;
2034 f_service_
<< indent() << "try {" << endl
;
2035 f_service_
<< indent() << " result = self.recv_" << funname
<< "();" << endl
;
2036 f_service_
<< indent() << "} catch (e) {" << endl
;
2037 f_service_
<< indent() << " result = e;" << endl
;
2038 f_service_
<< indent() << "}" << endl
;
2039 f_service_
<< indent() << "callback(result);" << endl
;
2041 f_service_
<< indent() << "});" << endl
;
2044 f_service_
<< indent() << "} else {" << endl
;
2045 f_service_
<< indent() << " return this.output.getTransport().flush();" << endl
;
2046 f_service_
<< indent() << "}" << endl
;
2051 f_service_
<< indent() << "}" << endl
;
2053 // Reset the transport and delete registered callback if there was a serialization error
2054 f_service_
<< indent() << "catch (e) {" << endl
;
2057 f_service_
<< indent() << "delete this._reqs[this.seqid()];" << endl
;
2058 f_service_
<< indent() << "if (typeof " << outputVar
<< ".reset === 'function') {" << endl
;
2059 f_service_
<< indent() << " " << outputVar
<< ".reset();" << endl
;
2060 f_service_
<< indent() << "}" << endl
;
2062 f_service_
<< indent() << "if (typeof " << outputVar
<< ".getTransport().reset === 'function') {" << endl
;
2063 f_service_
<< indent() << " " << outputVar
<< ".getTransport().reset();" << endl
;
2064 f_service_
<< indent() << "}" << endl
;
2066 f_service_
<< indent() << "throw e;" << endl
;
2068 f_service_
<< indent() << "}" << endl
;
2072 // Close send function
2074 indent(f_service_
) << "}" << endl
;
2076 indent(f_service_
) << "};" << endl
;
2080 if (!(*f_iter
)->is_oneway()) {
2081 std::string resultname
= js_namespace(tservice
->get_program()) + service_name_
+ "_"
2082 + (*f_iter
)->get_name() + "_result";
2085 // Open receive function
2088 indent(f_service_
) << "recv_" << (*f_iter
)->get_name() << " (input, mtype, rseqid) {" << endl
;
2090 indent(f_service_
) << js_namespace(tservice
->get_program()) << service_name_
2091 << "Client.prototype.recv_" << (*f_iter
)->get_name()
2092 << " = function(input,mtype,rseqid) {" << endl
;
2096 indent(f_service_
) << "recv_" << (*f_iter
)->get_name() << " () {" << endl
;
2098 t_struct
noargs(program_
);
2100 t_function
recv_function((*f_iter
)->get_returntype(),
2101 string("recv_") + (*f_iter
)->get_name(),
2103 indent(f_service_
) << js_namespace(tservice
->get_program()) << service_name_
2104 << "Client.prototype." << function_signature(&recv_function
) << " {" << endl
;
2110 std::string inputVar
;
2114 inputVar
= "this.input";
2118 f_service_
<< indent() << js_const_type_
<< "callback = this._reqs[rseqid] || function() {};" << endl
2119 << indent() << "delete this._reqs[rseqid];" << endl
;
2121 f_service_
<< indent() << js_const_type_
<< "ret = this.input.readMessageBegin();" << endl
2122 << indent() << js_const_type_
<< "mtype = ret.mtype;" << endl
;
2125 f_service_
<< indent() << "if (mtype == Thrift.MessageType.EXCEPTION) {" << endl
;
2128 f_service_
<< indent() << js_const_type_
<< "x = new Thrift.TApplicationException();" << endl
2129 << indent() << "x.read(" << inputVar
<< ");" << endl
2130 << indent() << inputVar
<< ".readMessageEnd();" << endl
2131 << indent() << render_recv_throw("x") << endl
;
2132 scope_down(f_service_
);
2134 f_service_
<< indent() << js_const_type_
<< "result = new " << resultname
<< "();" << endl
<< indent()
2135 << "result.read(" << inputVar
<< ");" << endl
;
2137 f_service_
<< indent() << inputVar
<< ".readMessageEnd();" << endl
<< endl
;
2139 t_struct
* xs
= (*f_iter
)->get_xceptions();
2140 const std::vector
<t_field
*>& xceptions
= xs
->get_members();
2141 vector
<t_field
*>::const_iterator x_iter
;
2142 for (x_iter
= xceptions
.begin(); x_iter
!= xceptions
.end(); ++x_iter
) {
2143 f_service_
<< indent() << "if (null !== result." << (*x_iter
)->get_name() << ") {" << endl
2144 << indent() << " " << render_recv_throw("result." + (*x_iter
)->get_name())
2145 << endl
<< indent() << "}" << endl
;
2148 // Careful, only return result if not a void function
2149 if (!(*f_iter
)->get_returntype()->is_void()) {
2150 f_service_
<< indent() << "if (null !== result.success) {" << endl
<< indent() << " "
2151 << render_recv_return("result.success") << endl
<< indent() << "}" << endl
;
2152 f_service_
<< indent()
2153 << render_recv_throw("'" + (*f_iter
)->get_name() + " failed: unknown result'")
2157 indent(f_service_
) << "callback(null);" << endl
;
2159 indent(f_service_
) << "return;" << endl
;
2163 // Close receive function
2166 indent(f_service_
) << "}" << endl
;
2168 indent(f_service_
) << "};" << endl
;
2173 // Finish class definitions
2175 f_service_ts_
<< ts_indent() << "}" << endl
;
2179 f_service_
<< "};" << endl
;
2183 std::string
t_js_generator::render_recv_throw(std::string var
) {
2185 return "return callback(" + var
+ ");";
2187 return "throw " + var
+ ";";
2191 std::string
t_js_generator::render_recv_return(std::string var
) {
2193 return "return callback(null, " + var
+ ");";
2195 return "return " + var
+ ";";
2200 * Deserializes a field of any type.
2202 void t_js_generator::generate_deserialize_field(ostream
& out
,
2207 t_type
* type
= get_true_type(tfield
->get_type());
2209 if (type
->is_void()) {
2210 throw std::runtime_error("CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix
+ tfield
->get_name());
2213 string name
= prefix
+ tfield
->get_name();
2215 if (type
->is_struct() || type
->is_xception()) {
2216 generate_deserialize_struct(out
, (t_struct
*)type
, name
);
2217 } else if (type
->is_container()) {
2218 generate_deserialize_container(out
, type
, name
);
2219 } else if (type
->is_base_type() || type
->is_enum()) {
2220 indent(out
) << name
<< " = input.";
2222 if (type
->is_base_type()) {
2223 t_base_type::t_base tbase
= ((t_base_type
*)type
)->get_base();
2225 case t_base_type::TYPE_VOID
:
2226 throw std::runtime_error("compiler error: cannot serialize void field in a struct: " + name
);
2228 case t_base_type::TYPE_STRING
:
2229 out
<< (type
->is_binary() ? "readBinary()" : "readString()");
2231 case t_base_type::TYPE_BOOL
:
2232 out
<< "readBool()";
2234 case t_base_type::TYPE_I8
:
2235 out
<< "readByte()";
2237 case t_base_type::TYPE_I16
:
2240 case t_base_type::TYPE_I32
:
2243 case t_base_type::TYPE_I64
:
2246 case t_base_type::TYPE_DOUBLE
:
2247 out
<< "readDouble()";
2250 throw std::runtime_error("compiler error: no JS name for base type " + t_base_type::t_base_name(tbase
));
2252 } else if (type
->is_enum()) {
2262 printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n",
2263 tfield
->get_name().c_str(),
2264 type
->get_name().c_str());
2269 * Generates an unserializer for a variable. This makes two key assumptions,
2270 * first that there is a const char* variable named data that points to the
2271 * buffer for deserialization, and that there is a variable protocol which
2272 * is a reference to a TProtocol serialization object.
2274 void t_js_generator::generate_deserialize_struct(ostream
& out
, t_struct
* tstruct
, string prefix
) {
2275 out
<< indent() << prefix
<< " = new " << js_type_namespace(tstruct
->get_program())
2276 << tstruct
->get_name() << "();" << endl
<< indent() << prefix
<< ".read(input);" << endl
;
2279 void t_js_generator::generate_deserialize_container(ostream
& out
, t_type
* ttype
, string prefix
) {
2280 string size
= tmp("_size");
2281 string rtmp3
= tmp("_rtmp3");
2283 t_field
fsize(g_type_i32
, size
);
2285 // Declare variables, read header
2286 if (ttype
->is_map()) {
2287 out
<< indent() << prefix
<< " = {};" << endl
;
2289 out
<< indent() << js_const_type_
<< rtmp3
<< " = input.readMapBegin();" << endl
;
2290 out
<< indent() << js_const_type_
<< size
<< " = " << rtmp3
<< ".size || 0;" << endl
;
2292 } else if (ttype
->is_set()) {
2294 out
<< indent() << prefix
<< " = [];" << endl
2295 << indent() << js_const_type_
<< rtmp3
<< " = input.readSetBegin();" << endl
2296 << indent() << js_const_type_
<< size
<< " = " << rtmp3
<< ".size || 0;" << endl
;
2298 } else if (ttype
->is_list()) {
2300 out
<< indent() << prefix
<< " = [];" << endl
2301 << indent() << js_const_type_
<< rtmp3
<< " = input.readListBegin();" << endl
2302 << indent() << js_const_type_
<< size
<< " = " << rtmp3
<< ".size || 0;" << endl
;
2305 // For loop iterates over elements
2306 string i
= tmp("_i");
2307 indent(out
) << "for (" << js_let_type_
<< i
<< " = 0; " << i
<< " < " << size
<< "; ++" << i
<< ") {" << endl
;
2311 if (ttype
->is_map()) {
2313 out
<< indent() << "if (" << i
<< " > 0 ) {" << endl
<< indent()
2314 << " if (input.rstack.length > input.rpos[input.rpos.length -1] + 1) {" << endl
2315 << indent() << " input.rstack.pop();" << endl
<< indent() << " }" << endl
<< indent()
2319 generate_deserialize_map_element(out
, (t_map
*)ttype
, prefix
);
2320 } else if (ttype
->is_set()) {
2321 generate_deserialize_set_element(out
, (t_set
*)ttype
, prefix
);
2322 } else if (ttype
->is_list()) {
2323 generate_deserialize_list_element(out
, (t_list
*)ttype
, prefix
);
2328 // Read container end
2329 if (ttype
->is_map()) {
2330 indent(out
) << "input.readMapEnd();" << endl
;
2331 } else if (ttype
->is_set()) {
2332 indent(out
) << "input.readSetEnd();" << endl
;
2333 } else if (ttype
->is_list()) {
2334 indent(out
) << "input.readListEnd();" << endl
;
2339 * Generates code to deserialize a map
2341 void t_js_generator::generate_deserialize_map_element(ostream
& out
, t_map
* tmap
, string prefix
) {
2342 string key
= tmp("key");
2343 string val
= tmp("val");
2344 t_field
fkey(tmap
->get_key_type(), key
);
2345 t_field
fval(tmap
->get_val_type(), val
);
2347 indent(out
) << declare_field(&fkey
, false, false) << ";" << endl
;
2348 indent(out
) << declare_field(&fval
, false, false) << ";" << endl
;
2350 generate_deserialize_field(out
, &fkey
);
2351 generate_deserialize_field(out
, &fval
);
2353 indent(out
) << prefix
<< "[" << key
<< "] = " << val
<< ";" << endl
;
2356 void t_js_generator::generate_deserialize_set_element(ostream
& out
, t_set
* tset
, string prefix
) {
2357 string elem
= tmp("elem");
2358 t_field
felem(tset
->get_elem_type(), elem
);
2360 indent(out
) << js_let_type_
<< elem
<< " = null;" << endl
;
2362 generate_deserialize_field(out
, &felem
);
2364 indent(out
) << prefix
<< ".push(" << elem
<< ");" << endl
;
2367 void t_js_generator::generate_deserialize_list_element(ostream
& out
,
2370 string elem
= tmp("elem");
2371 t_field
felem(tlist
->get_elem_type(), elem
);
2373 indent(out
) << js_let_type_
<< elem
<< " = null;" << endl
;
2375 generate_deserialize_field(out
, &felem
);
2377 indent(out
) << prefix
<< ".push(" << elem
<< ");" << endl
;
2381 * Serializes a field of any type.
2383 * @param tfield The field to serialize
2384 * @param prefix Name to prepend to field name
2386 void t_js_generator::generate_serialize_field(ostream
& out
, t_field
* tfield
, string prefix
) {
2387 t_type
* type
= get_true_type(tfield
->get_type());
2389 // Do nothing for void types
2390 if (type
->is_void()) {
2391 throw std::runtime_error("CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + prefix
+ tfield
->get_name());
2394 if (type
->is_struct() || type
->is_xception()) {
2395 generate_serialize_struct(out
, (t_struct
*)type
, prefix
+ tfield
->get_name());
2396 } else if (type
->is_container()) {
2397 generate_serialize_container(out
, type
, prefix
+ tfield
->get_name());
2398 } else if (type
->is_base_type() || type
->is_enum()) {
2400 string name
= tfield
->get_name();
2402 // Hack for when prefix is defined (always a hash ref)
2403 if (!prefix
.empty())
2404 name
= prefix
+ tfield
->get_name();
2406 indent(out
) << "output.";
2408 if (type
->is_base_type()) {
2409 t_base_type::t_base tbase
= ((t_base_type
*)type
)->get_base();
2411 case t_base_type::TYPE_VOID
:
2412 throw std::runtime_error("compiler error: cannot serialize void field in a struct: " + name
);
2414 case t_base_type::TYPE_STRING
:
2415 out
<< (type
->is_binary() ? "writeBinary(" : "writeString(") << name
<< ")";
2417 case t_base_type::TYPE_BOOL
:
2418 out
<< "writeBool(" << name
<< ")";
2420 case t_base_type::TYPE_I8
:
2421 out
<< "writeByte(" << name
<< ")";
2423 case t_base_type::TYPE_I16
:
2424 out
<< "writeI16(" << name
<< ")";
2426 case t_base_type::TYPE_I32
:
2427 out
<< "writeI32(" << name
<< ")";
2429 case t_base_type::TYPE_I64
:
2430 out
<< "writeI64(" << name
<< ")";
2432 case t_base_type::TYPE_DOUBLE
:
2433 out
<< "writeDouble(" << name
<< ")";
2436 throw std::runtime_error("compiler error: no JS name for base type " + t_base_type::t_base_name(tbase
));
2438 } else if (type
->is_enum()) {
2439 out
<< "writeI32(" << name
<< ")";
2444 printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n",
2446 tfield
->get_name().c_str(),
2447 type
->get_name().c_str());
2452 * Serializes all the members of a struct.
2454 * @param tstruct The struct to serialize
2455 * @param prefix String prefix to attach to all fields
2457 void t_js_generator::generate_serialize_struct(ostream
& out
, t_struct
* tstruct
, string prefix
) {
2459 indent(out
) << prefix
<< ".write(output);" << endl
;
2463 * Writes out a container
2465 void t_js_generator::generate_serialize_container(ostream
& out
, t_type
* ttype
, string prefix
) {
2466 if (ttype
->is_map()) {
2467 indent(out
) << "output.writeMapBegin(" << type_to_enum(((t_map
*)ttype
)->get_key_type()) << ", "
2468 << type_to_enum(((t_map
*)ttype
)->get_val_type()) << ", "
2469 << "Thrift.objectLength(" << prefix
<< "));" << endl
;
2470 } else if (ttype
->is_set()) {
2471 indent(out
) << "output.writeSetBegin(" << type_to_enum(((t_set
*)ttype
)->get_elem_type()) << ", "
2472 << prefix
<< ".length);" << endl
;
2474 } else if (ttype
->is_list()) {
2476 indent(out
) << "output.writeListBegin(" << type_to_enum(((t_list
*)ttype
)->get_elem_type())
2477 << ", " << prefix
<< ".length);" << endl
;
2480 if (ttype
->is_map()) {
2481 string kiter
= tmp("kiter");
2482 string viter
= tmp("viter");
2483 indent(out
) << "for (" << js_let_type_
<< kiter
<< " in " << prefix
<< ") {" << endl
;
2485 indent(out
) << "if (" << prefix
<< ".hasOwnProperty(" << kiter
<< ")) {" << endl
;
2487 indent(out
) << js_let_type_
<< viter
<< " = " << prefix
<< "[" << kiter
<< "];" << endl
;
2488 generate_serialize_map_element(out
, (t_map
*)ttype
, kiter
, viter
);
2492 } else if (ttype
->is_set()) {
2493 string iter
= tmp("iter");
2494 indent(out
) << "for (" << js_let_type_
<< iter
<< " in " << prefix
<< ") {" << endl
;
2496 indent(out
) << "if (" << prefix
<< ".hasOwnProperty(" << iter
<< ")) {" << endl
;
2498 indent(out
) << iter
<< " = " << prefix
<< "[" << iter
<< "];" << endl
;
2499 generate_serialize_set_element(out
, (t_set
*)ttype
, iter
);
2503 } else if (ttype
->is_list()) {
2504 string iter
= tmp("iter");
2505 indent(out
) << "for (" << js_let_type_
<< iter
<< " in " << prefix
<< ") {" << endl
;
2507 indent(out
) << "if (" << prefix
<< ".hasOwnProperty(" << iter
<< ")) {" << endl
;
2509 indent(out
) << iter
<< " = " << prefix
<< "[" << iter
<< "];" << endl
;
2510 generate_serialize_list_element(out
, (t_list
*)ttype
, iter
);
2515 if (ttype
->is_map()) {
2516 indent(out
) << "output.writeMapEnd();" << endl
;
2517 } else if (ttype
->is_set()) {
2518 indent(out
) << "output.writeSetEnd();" << endl
;
2519 } else if (ttype
->is_list()) {
2520 indent(out
) << "output.writeListEnd();" << endl
;
2525 * Serializes the members of a map.
2528 void t_js_generator::generate_serialize_map_element(ostream
& out
,
2532 t_field
kfield(tmap
->get_key_type(), kiter
);
2533 generate_serialize_field(out
, &kfield
);
2535 t_field
vfield(tmap
->get_val_type(), viter
);
2536 generate_serialize_field(out
, &vfield
);
2540 * Serializes the members of a set.
2542 void t_js_generator::generate_serialize_set_element(ostream
& out
, t_set
* tset
, string iter
) {
2543 t_field
efield(tset
->get_elem_type(), iter
);
2544 generate_serialize_field(out
, &efield
);
2548 * Serializes the members of a list.
2550 void t_js_generator::generate_serialize_list_element(ostream
& out
, t_list
* tlist
, string iter
) {
2551 t_field
efield(tlist
->get_elem_type(), iter
);
2552 generate_serialize_field(out
, &efield
);
2556 * Declares a field, which may include initialization as necessary.
2558 * @param ttype The type
2560 string
t_js_generator::declare_field(t_field
* tfield
, bool init
, bool obj
) {
2561 string result
= "this." + tfield
->get_name();
2564 result
= js_let_type_
+ tfield
->get_name();
2568 t_type
* type
= get_true_type(tfield
->get_type());
2569 if (type
->is_base_type()) {
2570 t_base_type::t_base tbase
= ((t_base_type
*)type
)->get_base();
2572 case t_base_type::TYPE_VOID
:
2574 case t_base_type::TYPE_STRING
:
2575 case t_base_type::TYPE_BOOL
:
2576 case t_base_type::TYPE_I8
:
2577 case t_base_type::TYPE_I16
:
2578 case t_base_type::TYPE_I32
:
2579 case t_base_type::TYPE_I64
:
2580 case t_base_type::TYPE_DOUBLE
:
2581 result
+= " = null";
2584 throw std::runtime_error("compiler error: no JS initializer for base type " + t_base_type::t_base_name(tbase
));
2586 } else if (type
->is_enum()) {
2587 result
+= " = null";
2588 } else if (type
->is_map()) {
2589 result
+= " = null";
2590 } else if (type
->is_container()) {
2591 result
+= " = null";
2592 } else if (type
->is_struct() || type
->is_xception()) {
2594 result
+= " = new " + js_type_namespace(type
->get_program()) + type
->get_name() + "()";
2596 result
+= " = null";
2600 result
+= " = null";
2606 * Renders a function signature of the form 'type name(args)'
2608 * @param tfunction Function definition
2609 * @return String of rendered function definition
2611 string
t_js_generator::function_signature(t_function
* tfunction
,
2613 bool include_callback
) {
2617 str
= prefix
+ tfunction
->get_name() + " = function(";
2619 str
+= argument_list(tfunction
->get_arglist(), include_callback
);
2626 * Renders a field list
2628 string
t_js_generator::argument_list(t_struct
* tstruct
, bool include_callback
) {
2631 const vector
<t_field
*>& fields
= tstruct
->get_members();
2632 vector
<t_field
*>::const_iterator f_iter
;
2634 for (f_iter
= fields
.begin(); f_iter
!= fields
.end(); ++f_iter
) {
2640 result
+= (*f_iter
)->get_name();
2643 if (include_callback
) {
2644 if (!fields
.empty()) {
2647 result
+= "callback";
2654 * Converts the parse type to a C++ enum string for the given type.
2656 string
t_js_generator::type_to_enum(t_type
* type
) {
2657 type
= get_true_type(type
);
2659 if (type
->is_base_type()) {
2660 t_base_type::t_base tbase
= ((t_base_type
*)type
)->get_base();
2662 case t_base_type::TYPE_VOID
:
2663 throw std::runtime_error("NO T_VOID CONSTRUCT");
2664 case t_base_type::TYPE_STRING
:
2665 return "Thrift.Type.STRING";
2666 case t_base_type::TYPE_BOOL
:
2667 return "Thrift.Type.BOOL";
2668 case t_base_type::TYPE_I8
:
2669 return "Thrift.Type.BYTE";
2670 case t_base_type::TYPE_I16
:
2671 return "Thrift.Type.I16";
2672 case t_base_type::TYPE_I32
:
2673 return "Thrift.Type.I32";
2674 case t_base_type::TYPE_I64
:
2675 return "Thrift.Type.I64";
2676 case t_base_type::TYPE_DOUBLE
:
2677 return "Thrift.Type.DOUBLE";
2679 } else if (type
->is_enum()) {
2680 return "Thrift.Type.I32";
2681 } else if (type
->is_struct() || type
->is_xception()) {
2682 return "Thrift.Type.STRUCT";
2683 } else if (type
->is_map()) {
2684 return "Thrift.Type.MAP";
2685 } else if (type
->is_set()) {
2686 return "Thrift.Type.SET";
2687 } else if (type
->is_list()) {
2688 return "Thrift.Type.LIST";
2691 throw std::runtime_error("INVALID TYPE IN type_to_enum: " + type
->get_name());
2695 * Converts a t_type to a TypeScript type (string).
2696 * @param t_type Type to convert to TypeScript
2697 * @return String TypeScript type
2699 string
t_js_generator::ts_get_type(t_type
* type
) {
2700 std::string ts_type
;
2702 type
= get_true_type(type
);
2704 if (type
->is_base_type()) {
2705 t_base_type::t_base tbase
= ((t_base_type
*)type
)->get_base();
2707 case t_base_type::TYPE_STRING
:
2710 case t_base_type::TYPE_BOOL
:
2711 ts_type
= "boolean";
2713 case t_base_type::TYPE_I8
:
2716 case t_base_type::TYPE_I16
:
2717 case t_base_type::TYPE_I32
:
2718 case t_base_type::TYPE_DOUBLE
:
2721 case t_base_type::TYPE_I64
:
2724 case t_base_type::TYPE_VOID
:
2727 } else if (type
->is_enum() || type
->is_struct() || type
->is_xception()) {
2728 std::string type_name
;
2730 if (type
->get_program()) {
2731 type_name
= js_namespace(type
->get_program());
2733 // If the type is not defined within the current program, we need to prefix it with the same name as
2734 // the generated "import" statement for the types containing program
2735 if(type
->get_program() != program_
) {
2736 auto prefix
= include_2_import_name
.find(type
->get_program());
2738 if(prefix
!= include_2_import_name
.end()) {
2739 type_name
.append(prefix
->second
);
2740 type_name
.append(".");
2745 type_name
.append(type
->get_name());
2746 ts_type
= type_name
;
2747 } else if (type
->is_list() || type
->is_set()) {
2750 if (type
->is_list()) {
2751 etype
= ((t_list
*)type
)->get_elem_type();
2753 etype
= ((t_set
*)type
)->get_elem_type();
2756 ts_type
= ts_get_type(etype
) + "[]";
2757 } else if (type
->is_map()) {
2758 string ktype
= ts_get_type(((t_map
*)type
)->get_key_type());
2759 string vtype
= ts_get_type(((t_map
*)type
)->get_val_type());
2762 if (ktype
== "number" || ktype
== "string" ) {
2763 ts_type
= "{ [k: " + ktype
+ "]: " + vtype
+ "; }";
2764 } else if ((((t_map
*)type
)->get_key_type())->is_enum()) {
2765 // Not yet supported (enum map): https://github.com/Microsoft/TypeScript/pull/2652
2766 //ts_type = "{ [k: " + ktype + "]: " + vtype + "; }";
2767 ts_type
= "{ [k: number /*" + ktype
+ "*/]: " + vtype
+ "; }";
2777 * Renders a TypeScript function signature of the form 'name(args: types): type;'
2779 * @param t_function Function definition
2780 * @param bool in-/exclude the callback argument
2781 * @return String of rendered function definition
2783 std::string
t_js_generator::ts_function_signature(t_function
* tfunction
, bool include_callback
) {
2785 const vector
<t_field
*>& fields
= tfunction
->get_arglist()->get_members();
2786 vector
<t_field
*>::const_iterator f_iter
;
2788 str
= tfunction
->get_name() + "(";
2790 for (f_iter
= fields
.begin(); f_iter
!= fields
.end(); ++f_iter
) {
2791 str
+= (*f_iter
)->get_name() + ts_get_req(*f_iter
) + ": " + ts_get_type((*f_iter
)->get_type());
2793 if (f_iter
+ 1 != fields
.end() || (include_callback
&& fields
.size() > 0)) {
2798 if (include_callback
) {
2800 t_struct
* exceptions
= tfunction
->get_xceptions();
2801 string exception_types
;
2803 const vector
<t_field
*>& members
= exceptions
->get_members();
2804 for (vector
<t_field
*>::const_iterator it
= members
.begin(); it
!= members
.end(); ++it
) {
2805 t_type
* t
= get_true_type((*it
)->get_type());
2806 if (it
== members
.begin()) {
2807 exception_types
= js_type_namespace(t
->get_program()) + t
->get_name();
2809 exception_types
+= " | " + js_type_namespace(t
->get_program()) + t
->get_name();
2813 if (exception_types
== "") {
2814 str
+= "callback?: (error: void, response: " + ts_get_type(tfunction
->get_returntype()) + ")=>void): ";
2816 str
+= "callback?: (error: " + exception_types
+ ", response: " + ts_get_type(tfunction
->get_returntype()) + ")=>void): ";
2819 str
+= "callback?: (data: " + ts_get_type(tfunction
->get_returntype()) + ")=>void): ";
2823 str
+= "JQueryPromise<" + ts_get_type(tfunction
->get_returntype()) +">;";
2829 str
+= "): Promise<" + ts_get_type(tfunction
->get_returntype()) + ">;";
2832 str
+= "): " + ts_get_type(tfunction
->get_returntype()) + ";";
2840 * Takes a name and produces a valid NodeJS identifier from it
2842 * @param name The name which shall become a valid NodeJS identifier
2843 * @return The modified name with the updated identifier
2845 std::string
t_js_generator::make_valid_nodeJs_identifier(std::string
const& name
) {
2846 std::string str
= name
;
2851 // tests rely on this
2852 assert(('A' < 'Z') && ('a' < 'z') && ('0' < '9'));
2854 // if the first letter is a number, we add an additional underscore in front of it
2856 if (('0' <= c
) && (c
<= '9')) {
2860 // following chars: letter, number or underscore
2861 for (size_t i
= 0; i
< str
.size(); ++i
) {
2863 if ((('A' > c
) || (c
> 'Z')) && (('a' > c
) || (c
> 'z')) && (('0' > c
) || (c
> '9'))
2864 && ('_' != c
) && ('$' != c
)) {
2865 str
.replace(i
, 1, "_");
2872 void t_js_generator::parse_imports(t_program
* program
, const std::string
& imports_string
) {
2873 if (program
->get_recursive()) {
2874 throw std::invalid_argument("[-gen js:imports=] option is not usable in recursive code generation mode");
2876 std::stringstream
sstream(imports_string
);
2878 while (std::getline(sstream
, import
, ':')) {
2879 imports
.emplace_back(import
);
2881 if (imports
.empty()) {
2882 throw std::invalid_argument("invalid usage: [-gen js:imports=] requires at least one path "
2883 "(multiple paths are separated by ':')");
2885 for (auto& import
: imports
) {
2886 // Strip trailing '/'
2887 if (!import
.empty() && import
[import
.size() - 1] == '/') {
2888 import
= import
.substr(0, import
.size() - 1);
2890 if (import
.empty()) {
2891 throw std::invalid_argument("empty paths are not allowed in imports");
2893 std::ifstream episode_file
;
2895 const auto episode_file_path
= import
+ "/" + episode_file_name
;
2896 episode_file
.open(episode_file_path
);
2897 if (!episode_file
) {
2898 throw std::runtime_error("failed to open the file '" + episode_file_path
+ "'");
2900 while (std::getline(episode_file
, line
)) {
2901 const auto separator_position
= line
.find(':');
2902 if (separator_position
== string::npos
) {
2903 // could not find the separator in the line
2904 throw std::runtime_error("the episode file '" + episode_file_path
+ "' is malformed, the line '" + line
2905 + "' does not have a key:value separator ':'");
2907 const auto module_name
= line
.substr(0, separator_position
);
2908 const auto import_path
= line
.substr(separator_position
+ 1);
2909 if (module_name
.empty()) {
2910 throw std::runtime_error("the episode file '" + episode_file_path
+ "' is malformed, the module name is empty");
2912 if (import_path
.empty()) {
2913 throw std::runtime_error("the episode file '" + episode_file_path
+ "' is malformed, the import path is empty");
2915 const auto module_import_path
= import
.substr(import
.find_last_of('/') + 1) + "/" + import_path
;
2916 const auto result
= module_name_2_import_path
.emplace(module_name
, module_import_path
);
2917 if (!result
.second
) {
2918 throw std::runtime_error("multiple providers of import path found for " + module_name
2919 + "\n\t" + module_import_path
+ "\n\t" + result
.first
->second
);
2924 void t_js_generator::parse_thrift_package_output_directory(const std::string
& thrift_package_output_directory
) {
2925 thrift_package_output_directory_
= thrift_package_output_directory
;
2926 // Strip trailing '/'
2927 if (!thrift_package_output_directory_
.empty() && thrift_package_output_directory_
[thrift_package_output_directory_
.size() - 1] == '/') {
2928 thrift_package_output_directory_
= thrift_package_output_directory_
.substr(0, thrift_package_output_directory_
.size() - 1);
2930 // Check that the thrift_package_output_directory is not empty after stripping
2931 if (thrift_package_output_directory_
.empty()) {
2932 throw std::invalid_argument("the thrift_package_output_directory argument must not be empty");
2934 gen_episode_file_
= true;
2938 THRIFT_REGISTER_GENERATOR(js
,
2940 " jquery: Generate jQuery compatible code.\n"
2941 " node: Generate node.js compatible code.\n"
2942 " ts: Generate TypeScript definition files.\n"
2943 " with_ns: Create global namespace objects when using node.js\n"
2944 " es6: Create ES6 code with Promises\n"
2945 " thrift_package_output_directory=<path>:\n"
2946 " Generate episode file and use the <path> as prefix\n"
2947 " imports=<paths_to_modules>:\n"
2948 " ':' separated list of paths of modules that has episode files in their root\n")