THRIFT-3314 Dots in file names of includes causes dots in javascript variable names
Client: Javascript
Patch: Kapil Joshi (based on the equivalent C# version)
diff --git a/compiler/cpp/src/generate/t_js_generator.cc b/compiler/cpp/src/generate/t_js_generator.cc
index 7c73add..cb0e181 100644
--- a/compiler/cpp/src/generate/t_js_generator.cc
+++ b/compiler/cpp/src/generate/t_js_generator.cc
@@ -23,6 +23,7 @@
 #include <iostream>
 #include <vector>
 #include <list>
+#include <cassert>
 
 #include <stdlib.h>
 #include <sys/stat.h>
@@ -175,6 +176,7 @@
                                  bool include_callback = false);
   std::string argument_list(t_struct* tstruct, bool include_callback = false);
   std::string type_to_enum(t_type* ttype);
+  std::string make_valid_nodeJs_identifier(std::string const& name);
 
   std::string autogen_comment() {
     return std::string("//\n") + "// Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n"
@@ -207,7 +209,7 @@
   std::string js_type_namespace(t_program* p) {
     if (gen_node_) {
       if (p != NULL && p != program_) {
-        return p->get_name() + "_ttypes.";
+        return make_valid_nodeJs_identifier(p->get_name()) + "_ttypes.";
       }
       return "ttypes.";
     }
@@ -387,7 +389,7 @@
   if (gen_node_) {
     const vector<t_program*>& includes = program_->get_includes();
     for (size_t i = 0; i < includes.size(); ++i) {
-      result += "var " + includes[i]->get_name() + "_ttypes = require('./" + includes[i]->get_name()
+      result += "var " + make_valid_nodeJs_identifier(includes[i]->get_name()) + "_ttypes = require('./" + includes[i]->get_name()
                 + "_types');\n";
     }
     if (includes.size() > 0) {
@@ -2184,6 +2186,39 @@
   return str;
 }
 
+/**
+ * Takes a name and produces a valid NodeJS identifier from it
+ *
+ * @param name The name which shall become a valid NodeJS identifier
+ * @return The modified name with the updated identifier
+ */
+std::string t_js_generator::make_valid_nodeJs_identifier(std::string const& name) {
+  std::string str = name;
+  if (str.empty()) {
+    return str;
+  }
+
+  // tests rely on this
+  assert(('A' < 'Z') && ('a' < 'z') && ('0' < '9'));
+
+  // if the first letter is a number, we add an additional underscore in front of it
+  char c = str.at(0);
+  if (('0' <= c) && (c <= '9')) {
+    str = "_" + str;
+  }
+
+  // following chars: letter, number or underscore
+  for (size_t i = 0; i < str.size(); ++i) {
+    c = str.at(i);
+    if ((('A' > c) || (c > 'Z')) && (('a' > c) || (c > 'z')) && (('0' > c) || (c > '9'))
+        && ('_' != c) && ('$' != c)) {
+      str.replace(i, 1, "_");
+    }
+  }
+
+  return str;
+}
+
 THRIFT_REGISTER_GENERATOR(js,
                           "Javascript",
                           "    jquery:          Generate jQuery compatible code.\n"