Repository: w3c/webidl2.js (js)

Issue:
Some leading underscores are no longer removed
Web IDL spec says:

> For all of these constructs [[named definitions](https://heycam.github.io/webidl/#dfn-named-definition), attributes, typedefs, dictionary members, constants, and operations], the [identifier](https://heycam.github.io/webidl/#dfn-identifier) is the value of the [**identifier**](https://heycam.github.io/webidl/#prod-identifier) token with any leading U+005F LOW LINE ("\_") character (underscore) removed.

However, webidl2.js no longer seems to remove some underscores, from identifiers that did not need them to start with:

```js
> require('webidl2').parse('interface _Int {};')
[ { type: 'interface',
    name: '_Int',
    partial: null,
    members: [],
    trivia:
     { base: '', name: ' ', open: ' ', close: '', termination: '' },
    inheritance: null,
    extAttrs: null },
  { type: 'eof', trivia: '' } ]
```

Pull request description:
fix: escape non-member top identifiers
Fixes #228 

This adds `escapedName` field for top level definitions, and also correctly checks duplication:

```webidl
interface Hi {};
interface _Hi {}; // this is a duplicate!
```

Patch:
diff --git a/lib/webidl2.js b/lib/webidl2.js
index 46043d8..fb12cef 100644
--- a/lib/webidl2.js
+++ b/lib/webidl2.js
@@ -235,11 +235,12 @@
     }
 
     function sanitize_name(name, type) {
-      if (names.has(name)) {
-        error(`The name "${name}" of type "${names.get(name)}" is already seen`);
+      const unescaped = unescape(name);
+      if (names.has(unescaped)) {
+        error(`The name "${unescaped}" of type "${names.get(unescaped)}" was already seen`);
       }
-      names.set(name, type);
-      return name;
+      names.set(unescaped, type);
+      return unescaped;
     }
 
     let consume_position = 0;
@@ -876,6 +877,7 @@
       const ret = current = {
         type: typeName,
         name: partialModifier ? name.value : sanitize_name(name.value, "interface"),
+        escapedName: name.value,
         partial: partialModifier,
         members: mems,
         trivia
@@ -917,6 +919,7 @@
       const ret = current = {
         type: "interface mixin",
         name: partialModifier ? name.value : sanitize_name(name.value, "interface mixin"),
+        escapedName: name.value,
         partial: partialModifier,
         members: mems,
         trivia
@@ -962,6 +965,7 @@
       const ret = current = {
         type: "namespace",
         name: partialModifier ? name.value : sanitize_name(name.value, "namespace"),
+        escapedName: name.value,
         partial: partialModifier,
         members: mems,
         trivia
@@ -1005,6 +1009,7 @@
       const ret = current = {
         type: "dictionary",
         name: partialModifier ? name.value : sanitize_name(name.value, "dictionary"),
+        escapedName: name.value,
         partial: partialModifier,
         members: mems,
         trivia
@@ -1054,6 +1059,7 @@
       const ret = current = {
         type: "enum",
         name: sanitize_name(name.value, "enum"),
+        escapedName: name.value,
         values: vals,
         trivia
       };
@@ -1091,6 +1097,7 @@
       ret.idlType = type_with_extended_attributes("typedef-type") || error("No type in typedef");
       const name = consume(ID) || error("No name in typedef");
       ret.name = sanitize_name(name.value, "typedef");
+      ret.escapedName = name.value,
       trivia.name = name.trivia;
       current = ret;
       const termination = consume(";") || error("Unterminated typedef");
diff --git a/lib/writer.js b/lib/writer.js
index a1a63ac..e6ca51e 100644
--- a/lib/writer.js
+++ b/lib/writer.js
@@ -104,7 +104,7 @@
     function interface_(it) {
       let ret = extended_attributes(it.extAttrs);
       if (it.partial) ret += `${it.partial.trivia}partial`;
-      ret += `${it.trivia.base}interface${it.trivia.name}${it.name}`;
+      ret += `${it.trivia.base}interface${it.trivia.name}${it.escapedName}`;
       if (it.inheritance) ret += inheritance(it.inheritance);
       ret += `${it.trivia.open}{${iterate(it.members)}${it.trivia.close}}${it.trivia.termination};`;
       return ret;
@@ -113,7 +113,7 @@
     function interface_mixin(it) {
       let ret = extended_attributes(it.extAttrs);
       if (it.partial) ret += `${it.partial.trivia}partial`;
-      ret += `${it.trivia.base}interface${it.trivia.mixin}mixin${it.trivia.name}${it.name}`;
+      ret += `${it.trivia.base}interface${it.trivia.mixin}mixin${it.trivia.name}${it.escapedName}`;
       ret += `${it.trivia.open}{${iterate(it.members)}${it.trivia.close}}${it.trivia.termination};`;
       return ret;
     }
@@ -121,7 +121,7 @@
     function namespace(it) {
       let ret = extended_attributes(it.extAttrs);
       if (it.partial) ret += `${it.partial.trivia}partial`;
-      ret += `${it.trivia.base}namespace${it.trivia.name}${it.name}`;
+      ret += `${it.trivia.base}namespace${it.trivia.name}${it.escapedName}`;
       ret += `${it.trivia.open}{${iterate(it.members)}${it.trivia.close}}${it.trivia.termination};`;
       return ret;
     }
@@ -129,7 +129,7 @@
     function dictionary(it) {
       let ret = extended_attributes(it.extAttrs);
       if (it.partial) ret += `${it.partial.trivia}partial`;
-      ret += `${it.trivia.base}dictionary${it.trivia.name}${it.name}`;
+      ret += `${it.trivia.base}dictionary${it.trivia.name}${it.escapedName}`;
       if (it.inheritance) ret += inheritance(it.inheritance);
       ret += `${it.trivia.open}{${iterate(it.members)}${it.trivia.close}}${it.trivia.termination};`;
       return ret;
@@ -148,7 +148,7 @@
     }
     function typedef(it) {
       const ret = extended_attributes(it.extAttrs);
-      return `${ret}${it.trivia.base}typedef${type(it.idlType)}${it.trivia.name}${it.name}${it.trivia.termination};`;
+      return `${ret}${it.trivia.base}typedef${type(it.idlType)}${it.trivia.name}${it.escapedName}${it.trivia.termination};`;
     }
     function includes(it) {
       const ret = extended_attributes(it.extAttrs);
@@ -167,7 +167,7 @@
         const body = `${v.trivia}"${v.value}"`;
         return v.separator ? `${body}${v.separator.trivia}${v.separator.value}` : body;
       }).join("");
-      return `${ext}${it.trivia.base}enum${it.trivia.name}${it.name}${it.trivia.open}{${values}${it.trivia.close}}${it.trivia.termination};`;
+      return `${ext}${it.trivia.base}enum${it.trivia.name}${it.escapedName}${it.trivia.open}{${values}${it.trivia.close}}${it.trivia.termination};`;
     }
     function iterable_like(it) {
       const readonly = it.readonly ? `${it.readonly.trivia}readonly` : "";


Test patch:
diff --git a/test/invalid/idl/duplicate-escaped.widl b/test/invalid/idl/duplicate-escaped.widl
new file mode 100644
index 0000000..7b0ce0d
--- /dev/null
+++ b/test/invalid/idl/duplicate-escaped.widl
@@ -0,0 +1,2 @@
+interface Iroha {};
+interface _Iroha {};
diff --git a/test/invalid/json/duplicate-escaped.json b/test/invalid/json/duplicate-escaped.json
new file mode 100644
index 0000000..3b01e7d
--- /dev/null
+++ b/test/invalid/json/duplicate-escaped.json
@@ -0,0 +1,4 @@
+{
+    "message": "Got an error during or right after parsing `interface Iroha`: The name \"Iroha\" of type \"interface\" was already seen",
+    "line": 2
+}
diff --git a/test/invalid/json/duplicate.json b/test/invalid/json/duplicate.json
index e88a715..052beb0 100644
--- a/test/invalid/json/duplicate.json
+++ b/test/invalid/json/duplicate.json
@@ -1,4 +1,4 @@
 {
-    "message": "Got an error during or right after parsing `typedef Test`: The name \"Test\" of type \"typedef\" is already seen",
+    "message": "Got an error during or right after parsing `typedef Test`: The name \"Test\" of type \"typedef\" was already seen",
     "line": 3
 }
diff --git a/test/syntax/idl/dictionary.widl b/test/syntax/idl/dictionary.widl
index c64a14c..2641eb0 100644
--- a/test/syntax/idl/dictionary.widl
+++ b/test/syntax/idl/dictionary.widl
@@ -9,7 +9,7 @@ dictionary PaintOptions {
   required long reqSeq;
 };
 
-partial dictionary A {
+partial dictionary _A {
   long h;
   long d;
-};
\ No newline at end of file
+};
diff --git a/test/syntax/idl/enum.widl b/test/syntax/idl/enum.widl
index 37c4ffd..9ea3d74 100644
--- a/test/syntax/idl/enum.widl
+++ b/test/syntax/idl/enum.widl
@@ -7,4 +7,4 @@ interface Meal {
   void initialize(MealType type, float size);
 };
 
-enum AltMealType { "rice", "noodles", "other", };
\ No newline at end of file
+enum _AltMealType { "rice", "noodles", "other", };
diff --git a/test/syntax/idl/escaped-name.widl b/test/syntax/idl/escaped-name.widl
new file mode 100644
index 0000000..dd9ad32
--- /dev/null
+++ b/test/syntax/idl/escaped-name.widl
@@ -0,0 +1,1 @@
+interface _Iroha {};
diff --git a/test/syntax/idl/mixin.widl b/test/syntax/idl/mixin.widl
index 7c37a6e..5af0436 100644
--- a/test/syntax/idl/mixin.widl
+++ b/test/syntax/idl/mixin.widl
@@ -10,3 +10,5 @@ WorkerGlobalScope includes GlobalCrypto;
 partial interface mixin WindowOrWorkerGlobalScope {
   readonly attribute Crypto crypto;
 };
+
+interface mixin _LocalCrypto {};
diff --git a/test/syntax/idl/namespace.widl b/test/syntax/idl/namespace.widl
index d961055..93930c3 100644
--- a/test/syntax/idl/namespace.widl
+++ b/test/syntax/idl/namespace.widl
@@ -7,4 +7,6 @@ namespace VectorUtils {
 
 partial namespace SomeNamespace {
   /* namespace_members... */
-};
\ No newline at end of file
+};
+
+namespace _ScalarUtils {};
diff --git a/test/syntax/idl/typedef.widl b/test/syntax/idl/typedef.widl
index b4c17d8..a1fc9e7 100644
--- a/test/syntax/idl/typedef.widl
+++ b/test/syntax/idl/typedef.widl
@@ -19,4 +19,4 @@
     boolean allPointsWithinBounds(PointSequence ps);
   };
 
-  typedef [Clamp] octet value;
\ No newline at end of file
+  typedef [Clamp] octet _value;
diff --git a/test/syntax/json/allowany.json b/test/syntax/json/allowany.json
index 8a1aff2..a44a389 100644
--- a/test/syntax/json/allowany.json
+++ b/test/syntax/json/allowany.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "B",
+        "escapedName": "B",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/attributes.json b/test/syntax/json/attributes.json
index a3729f0..328d810 100644
--- a/test/syntax/json/attributes.json
+++ b/test/syntax/json/attributes.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Person",
+        "escapedName": "Person",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/callback.json b/test/syntax/json/callback.json
index ba85fc0..18bda33 100644
--- a/test/syntax/json/callback.json
+++ b/test/syntax/json/callback.json
@@ -59,6 +59,7 @@
     {
         "type": "callback interface",
         "name": "EventHandler",
+        "escapedName": "EventHandler",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/constants.json b/test/syntax/json/constants.json
index 7dd99cc..0962b96 100644
--- a/test/syntax/json/constants.json
+++ b/test/syntax/json/constants.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Util",
+        "escapedName": "Util",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/constructor.json b/test/syntax/json/constructor.json
index e6f141f..db1f515 100644
--- a/test/syntax/json/constructor.json
+++ b/test/syntax/json/constructor.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Circle",
+        "escapedName": "Circle",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/dictionary-inherits.json b/test/syntax/json/dictionary-inherits.json
index 62d46f5..923c374 100644
--- a/test/syntax/json/dictionary-inherits.json
+++ b/test/syntax/json/dictionary-inherits.json
@@ -2,6 +2,7 @@
     {
         "type": "dictionary",
         "name": "PaintOptions",
+        "escapedName": "PaintOptions",
         "partial": null,
         "members": [
             {
@@ -116,6 +117,7 @@
     {
         "type": "dictionary",
         "name": "WetPaintOptions",
+        "escapedName": "WetPaintOptions",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/dictionary.json b/test/syntax/json/dictionary.json
index f245fa6..36c74ad 100644
--- a/test/syntax/json/dictionary.json
+++ b/test/syntax/json/dictionary.json
@@ -2,6 +2,7 @@
     {
         "type": "dictionary",
         "name": "PaintOptions",
+        "escapedName": "PaintOptions",
         "partial": null,
         "members": [
             {
@@ -201,7 +202,8 @@
     },
     {
         "type": "dictionary",
-        "name": "A",
+        "name": "_A",
+        "escapedName": "_A",
         "partial": {
             "trivia": "\n\n"
         },
@@ -272,6 +274,6 @@
     },
     {
         "type": "eof",
-        "trivia": ""
+        "trivia": "\n"
     }
 ]
diff --git a/test/syntax/json/documentation-dos.json b/test/syntax/json/documentation-dos.json
index 975f0a5..b6e8ce1 100644
--- a/test/syntax/json/documentation-dos.json
+++ b/test/syntax/json/documentation-dos.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Documentation",
+        "escapedName": "Documentation",
         "partial": null,
         "members": [],
         "trivia": {
diff --git a/test/syntax/json/documentation.json b/test/syntax/json/documentation.json
index 8b0e24f..46f68e2 100644
--- a/test/syntax/json/documentation.json
+++ b/test/syntax/json/documentation.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Documentation",
+        "escapedName": "Documentation",
         "partial": null,
         "members": [],
         "trivia": {
diff --git a/test/syntax/json/enum.json b/test/syntax/json/enum.json
index 8d57aab..bf5b23d 100644
--- a/test/syntax/json/enum.json
+++ b/test/syntax/json/enum.json
@@ -2,6 +2,7 @@
     {
         "type": "enum",
         "name": "MealType",
+        "escapedName": "MealType",
         "values": [
             {
                 "type": "string",
@@ -40,6 +41,7 @@
     {
         "type": "interface",
         "name": "Meal",
+        "escapedName": "Meal",
         "partial": null,
         "members": [
             {
@@ -213,6 +215,7 @@
     {
         "type": "enum",
         "name": "AltMealType",
+        "escapedName": "_AltMealType",
         "values": [
             {
                 "type": "string",
@@ -253,6 +256,6 @@
     },
     {
         "type": "eof",
-        "trivia": ""
+        "trivia": "\n"
     }
 ]
diff --git a/test/syntax/json/equivalent-decl.json b/test/syntax/json/equivalent-decl.json
index 237a072..afea142 100644
--- a/test/syntax/json/equivalent-decl.json
+++ b/test/syntax/json/equivalent-decl.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Dictionary",
+        "escapedName": "Dictionary",
         "partial": null,
         "members": [
             {
@@ -221,6 +222,7 @@
     {
         "type": "interface",
         "name": "Dictionary2",
+        "escapedName": "Dictionary2",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/escaped-name.json b/test/syntax/json/escaped-name.json
new file mode 100644
index 0000000..0bd7d02
--- /dev/null
+++ b/test/syntax/json/escaped-name.json
@@ -0,0 +1,22 @@
+[
+    {
+        "type": "interface",
+        "name": "Iroha",
+        "escapedName": "_Iroha",
+        "partial": null,
+        "members": [],
+        "trivia": {
+            "base": "",
+            "name": " ",
+            "open": " ",
+            "close": "",
+            "termination": ""
+        },
+        "inheritance": null,
+        "extAttrs": null
+    },
+    {
+        "type": "eof",
+        "trivia": "\n"
+    }
+]
diff --git a/test/syntax/json/extended-attributes.json b/test/syntax/json/extended-attributes.json
index fa57cd8..bf61db7 100644
--- a/test/syntax/json/extended-attributes.json
+++ b/test/syntax/json/extended-attributes.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "ServiceWorkerGlobalScope",
+        "escapedName": "ServiceWorkerGlobalScope",
         "partial": null,
         "members": [],
         "trivia": {
@@ -82,6 +83,7 @@
     {
         "type": "interface",
         "name": "IdInterface",
+        "escapedName": "IdInterface",
         "partial": null,
         "members": [],
         "trivia": {
@@ -161,6 +163,7 @@
     {
         "type": "interface",
         "name": "Circle",
+        "escapedName": "Circle",
         "partial": null,
         "members": [
             {
@@ -363,6 +366,7 @@
     {
         "type": "interface",
         "name": "I",
+        "escapedName": "I",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/generic.json b/test/syntax/json/generic.json
index 96d98ab..fc2c268 100644
--- a/test/syntax/json/generic.json
+++ b/test/syntax/json/generic.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Foo",
+        "escapedName": "Foo",
         "partial": null,
         "members": [
             {
@@ -179,6 +180,7 @@
     {
         "type": "interface",
         "name": "ServiceWorkerClients",
+        "escapedName": "ServiceWorkerClients",
         "partial": null,
         "members": [
             {
@@ -319,6 +321,7 @@
     {
         "type": "interface",
         "name": "FetchEvent",
+        "escapedName": "FetchEvent",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/getter-setter.json b/test/syntax/json/getter-setter.json
index 5dd0e0f..74f2c60 100644
--- a/test/syntax/json/getter-setter.json
+++ b/test/syntax/json/getter-setter.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Dictionary",
+        "escapedName": "Dictionary",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/identifier-qualified-names.json b/test/syntax/json/identifier-qualified-names.json
index 0a5ab36..badb142 100644
--- a/test/syntax/json/identifier-qualified-names.json
+++ b/test/syntax/json/identifier-qualified-names.json
@@ -17,6 +17,7 @@
             }
         },
         "name": "number",
+        "escapedName": "number",
         "trivia": {
             "base": "// Extracted from http://dev.w3.org/2006/webapi/WebIDL/ on 2011-05-06\n  // Typedef identifier: \"number\"\n  // Qualified name:    \"::framework::number\"\n  ",
             "name": " ",
@@ -27,6 +28,7 @@
     {
         "type": "interface",
         "name": "System",
+        "escapedName": "System",
         "partial": null,
         "members": [
             {
@@ -175,6 +177,7 @@
     {
         "type": "interface",
         "name": "TextField",
+        "escapedName": "TextField",
         "partial": null,
         "members": [
             {
@@ -253,6 +256,7 @@
     {
         "type": "interface",
         "name": "FooEventTarget",
+        "escapedName": "FooEventTarget",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/indexed-properties.json b/test/syntax/json/indexed-properties.json
index a3badfd..75ca730 100644
--- a/test/syntax/json/indexed-properties.json
+++ b/test/syntax/json/indexed-properties.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "OrderedMap",
+        "escapedName": "OrderedMap",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/inherits-getter.json b/test/syntax/json/inherits-getter.json
index 0745ce2..c32a500 100644
--- a/test/syntax/json/inherits-getter.json
+++ b/test/syntax/json/inherits-getter.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Animal",
+        "escapedName": "Animal",
         "partial": null,
         "members": [
             {
@@ -50,6 +51,7 @@
     {
         "type": "interface",
         "name": "Person",
+        "escapedName": "Person",
         "partial": null,
         "members": [
             {
@@ -139,6 +141,7 @@
     {
         "type": "interface",
         "name": "Ghost",
+        "escapedName": "Ghost",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/interface-inherits.json b/test/syntax/json/interface-inherits.json
index 1a403a5..5aa9420 100644
--- a/test/syntax/json/interface-inherits.json
+++ b/test/syntax/json/interface-inherits.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Animal",
+        "escapedName": "Animal",
         "partial": null,
         "members": [
             {
@@ -48,6 +49,7 @@
     {
         "type": "interface",
         "name": "Human",
+        "escapedName": "Human",
         "partial": null,
         "members": [
             {
@@ -100,6 +102,7 @@
     {
         "type": "interface",
         "name": "Dog",
+        "escapedName": "Dog",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/iterable.json b/test/syntax/json/iterable.json
index c8f4929..7f8eaf9 100644
--- a/test/syntax/json/iterable.json
+++ b/test/syntax/json/iterable.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "IterableOne",
+        "escapedName": "IterableOne",
         "partial": null,
         "members": [
             {
@@ -45,6 +46,7 @@
     {
         "type": "interface",
         "name": "IterableTwo",
+        "escapedName": "IterableTwo",
         "partial": null,
         "members": [
             {
@@ -108,6 +110,7 @@
     {
         "type": "interface",
         "name": "IterableThree",
+        "escapedName": "IterableThree",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/maplike.json b/test/syntax/json/maplike.json
index 15fa234..a890806 100644
--- a/test/syntax/json/maplike.json
+++ b/test/syntax/json/maplike.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "MapLike",
+        "escapedName": "MapLike",
         "partial": null,
         "members": [
             {
@@ -64,6 +65,7 @@
     {
         "type": "interface",
         "name": "ReadOnlyMapLike",
+        "escapedName": "ReadOnlyMapLike",
         "partial": null,
         "members": [
             {
@@ -128,6 +130,7 @@
     {
         "type": "interface",
         "name": "I",
+        "escapedName": "I",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/mixin.json b/test/syntax/json/mixin.json
index de0e5aa..3987405 100644
--- a/test/syntax/json/mixin.json
+++ b/test/syntax/json/mixin.json
@@ -2,6 +2,7 @@
     {
         "type": "interface mixin",
         "name": "GlobalCrypto",
+        "escapedName": "GlobalCrypto",
         "partial": null,
         "members": [
             {
@@ -74,6 +75,7 @@
     {
         "type": "interface mixin",
         "name": "WindowOrWorkerGlobalScope",
+        "escapedName": "WindowOrWorkerGlobalScope",
         "partial": {
             "trivia": "\n\n"
         },
@@ -121,6 +123,22 @@
         },
         "extAttrs": null
     },
+    {
+        "type": "interface mixin",
+        "name": "LocalCrypto",
+        "escapedName": "_LocalCrypto",
+        "partial": null,
+        "members": [],
+        "trivia": {
+            "base": "\n\n",
+            "mixin": " ",
+            "name": " ",
+            "open": " ",
+            "close": "",
+            "termination": ""
+        },
+        "extAttrs": null
+    },
     {
         "type": "eof",
         "trivia": "\n"
diff --git a/test/syntax/json/namedconstructor.json b/test/syntax/json/namedconstructor.json
index 377edd5..95881ab 100644
--- a/test/syntax/json/namedconstructor.json
+++ b/test/syntax/json/namedconstructor.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "HTMLAudioElement",
+        "escapedName": "HTMLAudioElement",
         "partial": null,
         "members": [],
         "trivia": {
diff --git a/test/syntax/json/namespace.json b/test/syntax/json/namespace.json
index 79bd21a..de45de4 100644
--- a/test/syntax/json/namespace.json
+++ b/test/syntax/json/namespace.json
@@ -2,6 +2,7 @@
     {
         "type": "namespace",
         "name": "VectorUtils",
+        "escapedName": "VectorUtils",
         "partial": null,
         "members": [
             {
@@ -243,6 +244,7 @@
     {
         "type": "namespace",
         "name": "SomeNamespace",
+        "escapedName": "SomeNamespace",
         "partial": {
             "trivia": "\n\n"
         },
@@ -256,8 +258,23 @@
         },
         "extAttrs": null
     },
+    {
+        "type": "namespace",
+        "name": "ScalarUtils",
+        "escapedName": "_ScalarUtils",
+        "partial": null,
+        "members": [],
+        "trivia": {
+            "base": "\n\n",
+            "name": " ",
+            "open": " ",
+            "close": "",
+            "termination": ""
+        },
+        "extAttrs": null
+    },
     {
         "type": "eof",
-        "trivia": ""
+        "trivia": "\n"
     }
 ]
diff --git a/test/syntax/json/nointerfaceobject.json b/test/syntax/json/nointerfaceobject.json
index 1765100..833eec8 100644
--- a/test/syntax/json/nointerfaceobject.json
+++ b/test/syntax/json/nointerfaceobject.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Query",
+        "escapedName": "Query",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/nullable.json b/test/syntax/json/nullable.json
index fb1bb4d..186266d 100644
--- a/test/syntax/json/nullable.json
+++ b/test/syntax/json/nullable.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "MyConstants",
+        "escapedName": "MyConstants",
         "partial": null,
         "members": [
             {
@@ -51,6 +52,7 @@
     {
         "type": "interface",
         "name": "Node",
+        "escapedName": "Node",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/nullableobjects.json b/test/syntax/json/nullableobjects.json
index 6c8e718..b4c15d1 100644
--- a/test/syntax/json/nullableobjects.json
+++ b/test/syntax/json/nullableobjects.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "A",
+        "escapedName": "A",
         "partial": null,
         "members": [],
         "trivia": {
@@ -17,6 +18,7 @@
     {
         "type": "interface",
         "name": "B",
+        "escapedName": "B",
         "partial": null,
         "members": [],
         "trivia": {
@@ -32,6 +34,7 @@
     {
         "type": "interface",
         "name": "C",
+        "escapedName": "C",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/operation-optional-arg.json b/test/syntax/json/operation-optional-arg.json
index abf8dc0..fb9507c 100644
--- a/test/syntax/json/operation-optional-arg.json
+++ b/test/syntax/json/operation-optional-arg.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "ColorCreator",
+        "escapedName": "ColorCreator",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/overloading.json b/test/syntax/json/overloading.json
index 0ee805b..f8656d9 100644
--- a/test/syntax/json/overloading.json
+++ b/test/syntax/json/overloading.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "A",
+        "escapedName": "A",
         "partial": null,
         "members": [],
         "trivia": {
@@ -17,6 +18,7 @@
     {
         "type": "interface",
         "name": "B",
+        "escapedName": "B",
         "partial": null,
         "members": [],
         "trivia": {
@@ -32,6 +34,7 @@
     {
         "type": "interface",
         "name": "C",
+        "escapedName": "C",
         "partial": null,
         "members": [
             {
@@ -182,6 +185,7 @@
     {
         "type": "interface",
         "name": "D",
+        "escapedName": "D",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/overridebuiltins.json b/test/syntax/json/overridebuiltins.json
index 9171d34..2a7c48f 100644
--- a/test/syntax/json/overridebuiltins.json
+++ b/test/syntax/json/overridebuiltins.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "StringMap2",
+        "escapedName": "StringMap2",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/partial-interface.json b/test/syntax/json/partial-interface.json
index 2d9c2d7..8331fd7 100644
--- a/test/syntax/json/partial-interface.json
+++ b/test/syntax/json/partial-interface.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Foo",
+        "escapedName": "Foo",
         "partial": null,
         "members": [
             {
@@ -48,6 +49,7 @@
     {
         "type": "interface",
         "name": "Foo",
+        "escapedName": "Foo",
         "partial": {
             "trivia": "\n\n"
         },
diff --git a/test/syntax/json/primitives.json b/test/syntax/json/primitives.json
index 6e7c093..12d1e4d 100644
--- a/test/syntax/json/primitives.json
+++ b/test/syntax/json/primitives.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Primitives",
+        "escapedName": "Primitives",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/promise-void.json b/test/syntax/json/promise-void.json
index c5f670f..4f99e49 100644
--- a/test/syntax/json/promise-void.json
+++ b/test/syntax/json/promise-void.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Cat",
+        "escapedName": "Cat",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/prototyperoot.json b/test/syntax/json/prototyperoot.json
index 5b526e7..bc1e72c 100644
--- a/test/syntax/json/prototyperoot.json
+++ b/test/syntax/json/prototyperoot.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Node",
+        "escapedName": "Node",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/putforwards.json b/test/syntax/json/putforwards.json
index e9e79a8..47f0127 100644
--- a/test/syntax/json/putforwards.json
+++ b/test/syntax/json/putforwards.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Person",
+        "escapedName": "Person",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/record.json b/test/syntax/json/record.json
index d05908f..a6aa894 100644
--- a/test/syntax/json/record.json
+++ b/test/syntax/json/record.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Foo",
+        "escapedName": "Foo",
         "partial": null,
         "members": [
             {
@@ -354,6 +355,7 @@
     {
         "type": "interface",
         "name": "Bar",
+        "escapedName": "Bar",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/reg-operations.json b/test/syntax/json/reg-operations.json
index d356e6e..9b70769 100644
--- a/test/syntax/json/reg-operations.json
+++ b/test/syntax/json/reg-operations.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Dimensions",
+        "escapedName": "Dimensions",
         "partial": null,
         "members": [
             {
@@ -84,6 +85,7 @@
     {
         "type": "interface",
         "name": "Button",
+        "escapedName": "Button",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/replaceable.json b/test/syntax/json/replaceable.json
index 0bcac8e..783903d 100644
--- a/test/syntax/json/replaceable.json
+++ b/test/syntax/json/replaceable.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Counter",
+        "escapedName": "Counter",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/sequence.json b/test/syntax/json/sequence.json
index 1bac1a1..6b85be7 100644
--- a/test/syntax/json/sequence.json
+++ b/test/syntax/json/sequence.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Canvas",
+        "escapedName": "Canvas",
         "partial": null,
         "members": [
             {
@@ -168,6 +169,7 @@
     {
         "type": "interface",
         "name": "I",
+        "escapedName": "I",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/setlike.json b/test/syntax/json/setlike.json
index 05d12e9..2955ed3 100644
--- a/test/syntax/json/setlike.json
+++ b/test/syntax/json/setlike.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "SetLike",
+        "escapedName": "SetLike",
         "partial": null,
         "members": [
             {
@@ -46,6 +47,7 @@
     {
         "type": "interface",
         "name": "ReadOnlySetLike",
+        "escapedName": "ReadOnlySetLike",
         "partial": null,
         "members": [
             {
@@ -92,6 +94,7 @@
     {
         "type": "interface",
         "name": "SetLikeExt",
+        "escapedName": "SetLikeExt",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/static.json b/test/syntax/json/static.json
index c3a7bba..c72a3ff 100644
--- a/test/syntax/json/static.json
+++ b/test/syntax/json/static.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Point",
+        "escapedName": "Point",
         "partial": null,
         "members": [],
         "trivia": {
@@ -17,6 +18,7 @@
     {
         "type": "interface",
         "name": "Circle",
+        "escapedName": "Circle",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/stringifier-attribute.json b/test/syntax/json/stringifier-attribute.json
index fd6f129..8ed5da3 100644
--- a/test/syntax/json/stringifier-attribute.json
+++ b/test/syntax/json/stringifier-attribute.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Student",
+        "escapedName": "Student",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/stringifier-custom.json b/test/syntax/json/stringifier-custom.json
index 3edf1f0..d050428 100644
--- a/test/syntax/json/stringifier-custom.json
+++ b/test/syntax/json/stringifier-custom.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Student",
+        "escapedName": "Student",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/stringifier.json b/test/syntax/json/stringifier.json
index e149983..7373482 100644
--- a/test/syntax/json/stringifier.json
+++ b/test/syntax/json/stringifier.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "A",
+        "escapedName": "A",
         "partial": null,
         "members": [
             {
@@ -55,6 +56,7 @@
     {
         "type": "interface",
         "name": "B",
+        "escapedName": "B",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/treatasnull.json b/test/syntax/json/treatasnull.json
index 69bce25..c587ed5 100644
--- a/test/syntax/json/treatasnull.json
+++ b/test/syntax/json/treatasnull.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Dog",
+        "escapedName": "Dog",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/treatasundefined.json b/test/syntax/json/treatasundefined.json
index 3f4cb83..e3baad3 100644
--- a/test/syntax/json/treatasundefined.json
+++ b/test/syntax/json/treatasundefined.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Cat",
+        "escapedName": "Cat",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/typedef-union.json b/test/syntax/json/typedef-union.json
index da3ba7e..1d3c483 100644
--- a/test/syntax/json/typedef-union.json
+++ b/test/syntax/json/typedef-union.json
@@ -88,6 +88,7 @@
             }
         },
         "name": "TexImageSource",
+        "escapedName": "TexImageSource",
         "trivia": {
             "base": "  ",
             "name": " ",
diff --git a/test/syntax/json/typedef.json b/test/syntax/json/typedef.json
index 6537966..516cdb0 100644
--- a/test/syntax/json/typedef.json
+++ b/test/syntax/json/typedef.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Point",
+        "escapedName": "Point",
         "partial": null,
         "members": [
             {
@@ -115,6 +116,7 @@
             }
         },
         "name": "PointSequence",
+        "escapedName": "PointSequence",
         "trivia": {
             "base": "\n\n      ",
             "name": " ",
@@ -125,6 +127,7 @@
     {
         "type": "interface",
         "name": "Rect",
+        "escapedName": "Rect",
         "partial": null,
         "members": [
             {
@@ -201,6 +204,7 @@
     {
         "type": "interface",
         "name": "Widget",
+        "escapedName": "Widget",
         "partial": null,
         "members": [
             {
@@ -415,6 +419,7 @@
             }
         },
         "name": "value",
+        "escapedName": "_value",
         "trivia": {
             "base": "\n\n  ",
             "name": " ",
@@ -424,6 +429,6 @@
     },
     {
         "type": "eof",
-        "trivia": ""
+        "trivia": "\n"
     }
 ]
diff --git a/test/syntax/json/typesuffixes.json b/test/syntax/json/typesuffixes.json
index b04ecec..c4c5d60 100644
--- a/test/syntax/json/typesuffixes.json
+++ b/test/syntax/json/typesuffixes.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Suffixes",
+        "escapedName": "Suffixes",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/uniontype.json b/test/syntax/json/uniontype.json
index cecc82c..9e0fc15 100644
--- a/test/syntax/json/uniontype.json
+++ b/test/syntax/json/uniontype.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "Union",
+        "escapedName": "Union",
         "partial": null,
         "members": [
             {
diff --git a/test/syntax/json/variadic-operations.json b/test/syntax/json/variadic-operations.json
index 91c8533..88be02f 100644
--- a/test/syntax/json/variadic-operations.json
+++ b/test/syntax/json/variadic-operations.json
@@ -2,6 +2,7 @@
     {
         "type": "interface",
         "name": "IntegerSet",
+        "escapedName": "IntegerSet",
         "partial": null,
         "members": [
             {