]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(compiler): port parser implementation based on work by @znck and @mysticatea
authorEvan You <yyx990803@gmail.com>
Mon, 16 Sep 2019 18:43:29 +0000 (14:43 -0400)
committerEvan You <yyx990803@gmail.com>
Mon, 16 Sep 2019 18:43:29 +0000 (14:43 -0400)
packages/compiler-core/__tests__/__snapshots__/parse.spec.ts.snap [new file with mode: 0644]
packages/compiler-core/__tests__/parse.spec.ts [new file with mode: 0644]
packages/compiler-core/src/ast.ts [new file with mode: 0644]
packages/compiler-core/src/errorTypes.ts [new file with mode: 0644]
packages/compiler-core/src/index.ts
packages/compiler-core/src/parser.ts [new file with mode: 0644]
packages/compiler-core/src/parserOptionsMinimal.ts [new file with mode: 0644]
packages/compiler-core/src/parserOptionsStandard.ts [new file with mode: 0644]

diff --git a/packages/compiler-core/__tests__/__snapshots__/parse.spec.ts.snap b/packages/compiler-core/__tests__/__snapshots__/parse.spec.ts.snap
new file mode 100644 (file)
index 0000000..ce41e30
--- /dev/null
@@ -0,0 +1,7392 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`parser/parse invalid html 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 1,
+              "line": 3,
+              "offset": 13,
+            },
+            "source": "<span>
+",
+            "start": Object {
+              "column": 1,
+              "line": 2,
+              "offset": 6,
+            },
+          },
+          "ns": 0,
+          "props": Array [],
+          "tag": "span",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 7,
+          "line": 3,
+          "offset": 19,
+        },
+        "source": "<div>
+<span>
+</div>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "div",
+      "tagType": 0,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 8,
+      "line": 4,
+      "offset": 27,
+    },
+    "source": "<div>
+<span>
+</div>
+</span>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option ABRUPT_CLOSING_OF_EMPTY_COMMENT <template><!--></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "",
+          "loc": Object {
+            "end": Object {
+              "column": 16,
+              "line": 1,
+              "offset": 15,
+            },
+            "source": "<!-->",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 1,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 27,
+          "line": 1,
+          "offset": 26,
+        },
+        "source": "<template><!--></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 27,
+      "line": 1,
+      "offset": 26,
+    },
+    "source": "<template><!--></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option ABRUPT_CLOSING_OF_EMPTY_COMMENT <template><!---></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "",
+          "loc": Object {
+            "end": Object {
+              "column": 17,
+              "line": 1,
+              "offset": 16,
+            },
+            "source": "<!--->",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 1,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 28,
+          "line": 1,
+          "offset": 27,
+        },
+        "source": "<template><!---></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 28,
+      "line": 1,
+      "offset": 27,
+    },
+    "source": "<template><!---></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option ABRUPT_CLOSING_OF_EMPTY_COMMENT <template><!----></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "",
+          "loc": Object {
+            "end": Object {
+              "column": 18,
+              "line": 1,
+              "offset": 17,
+            },
+            "source": "<!---->",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 1,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 29,
+          "line": 1,
+          "offset": 28,
+        },
+        "source": "<template><!----></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 29,
+      "line": 1,
+      "offset": 28,
+    },
+    "source": "<template><!----></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE <template attr="&#99;"></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 35,
+          "line": 1,
+          "offset": 34,
+        },
+        "source": "<template attr=\\"&#99;\\"></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [
+        Object {
+          "loc": Object {
+            "end": Object {
+              "column": 23,
+              "line": 1,
+              "offset": 22,
+            },
+            "source": "attr=\\"&#99;\\"",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "name": "attr",
+          "type": 3,
+          "value": Object {
+            "content": "c",
+            "isEmpty": false,
+            "loc": Object {
+              "end": Object {
+                "column": 23,
+                "line": 1,
+                "offset": 22,
+              },
+              "source": "\\"&#99;\\"",
+              "start": Object {
+                "column": 16,
+                "line": 1,
+                "offset": 15,
+              },
+            },
+            "type": 0,
+          },
+        },
+      ],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 35,
+      "line": 1,
+      "offset": 34,
+    },
+    "source": "<template attr=\\"&#99;\\"></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE <template attr="&#a;"></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 34,
+          "line": 1,
+          "offset": 33,
+        },
+        "source": "<template attr=\\"&#a;\\"></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [
+        Object {
+          "loc": Object {
+            "end": Object {
+              "column": 22,
+              "line": 1,
+              "offset": 21,
+            },
+            "source": "attr=\\"&#a;\\"",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "name": "attr",
+          "type": 3,
+          "value": Object {
+            "content": "&#a;",
+            "isEmpty": false,
+            "loc": Object {
+              "end": Object {
+                "column": 22,
+                "line": 1,
+                "offset": 21,
+              },
+              "source": "\\"&#a;\\"",
+              "start": Object {
+                "column": 16,
+                "line": 1,
+                "offset": 15,
+              },
+            },
+            "type": 0,
+          },
+        },
+      ],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 34,
+      "line": 1,
+      "offset": 33,
+    },
+    "source": "<template attr=\\"&#a;\\"></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE <template attr="&#xff;"></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 36,
+          "line": 1,
+          "offset": 35,
+        },
+        "source": "<template attr=\\"&#xff;\\"></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [
+        Object {
+          "loc": Object {
+            "end": Object {
+              "column": 24,
+              "line": 1,
+              "offset": 23,
+            },
+            "source": "attr=\\"&#xff;\\"",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "name": "attr",
+          "type": 3,
+          "value": Object {
+            "content": "ÿ",
+            "isEmpty": false,
+            "loc": Object {
+              "end": Object {
+                "column": 24,
+                "line": 1,
+                "offset": 23,
+              },
+              "source": "\\"&#xff;\\"",
+              "start": Object {
+                "column": 16,
+                "line": 1,
+                "offset": 15,
+              },
+            },
+            "type": 0,
+          },
+        },
+      ],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 36,
+      "line": 1,
+      "offset": 35,
+    },
+    "source": "<template attr=\\"&#xff;\\"></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE <template attr="&#xg;"></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 35,
+          "line": 1,
+          "offset": 34,
+        },
+        "source": "<template attr=\\"&#xg;\\"></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [
+        Object {
+          "loc": Object {
+            "end": Object {
+              "column": 23,
+              "line": 1,
+              "offset": 22,
+            },
+            "source": "attr=\\"&#xg;\\"",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "name": "attr",
+          "type": 3,
+          "value": Object {
+            "content": "&#xg;",
+            "isEmpty": false,
+            "loc": Object {
+              "end": Object {
+                "column": 23,
+                "line": 1,
+                "offset": 22,
+              },
+              "source": "\\"&#xg;\\"",
+              "start": Object {
+                "column": 16,
+                "line": 1,
+                "offset": 15,
+              },
+            },
+            "type": 0,
+          },
+        },
+      ],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 35,
+      "line": 1,
+      "offset": 34,
+    },
+    "source": "<template attr=\\"&#xg;\\"></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE <template>&#99;</template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "c",
+          "isEmpty": false,
+          "loc": Object {
+            "end": Object {
+              "column": 16,
+              "line": 1,
+              "offset": 15,
+            },
+            "source": "&#99;",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 0,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 27,
+          "line": 1,
+          "offset": 26,
+        },
+        "source": "<template>&#99;</template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 27,
+      "line": 1,
+      "offset": 26,
+    },
+    "source": "<template>&#99;</template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE <template>&#a;</template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "&#a;",
+          "isEmpty": false,
+          "loc": Object {
+            "end": Object {
+              "column": 15,
+              "line": 1,
+              "offset": 14,
+            },
+            "source": "&#a;",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 0,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 26,
+          "line": 1,
+          "offset": 25,
+        },
+        "source": "<template>&#a;</template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 26,
+      "line": 1,
+      "offset": 25,
+    },
+    "source": "<template>&#a;</template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE <template>&#xff;</template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "ÿ",
+          "isEmpty": false,
+          "loc": Object {
+            "end": Object {
+              "column": 17,
+              "line": 1,
+              "offset": 16,
+            },
+            "source": "&#xff;",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 0,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 28,
+          "line": 1,
+          "offset": 27,
+        },
+        "source": "<template>&#xff;</template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 28,
+      "line": 1,
+      "offset": 27,
+    },
+    "source": "<template>&#xff;</template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE <template>&#xg;</template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "&#xg;",
+          "isEmpty": false,
+          "loc": Object {
+            "end": Object {
+              "column": 16,
+              "line": 1,
+              "offset": 15,
+            },
+            "source": "&#xg;",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 0,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 27,
+          "line": 1,
+          "offset": 26,
+        },
+        "source": "<template>&#xg;</template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 27,
+      "line": 1,
+      "offset": 26,
+    },
+    "source": "<template>&#xg;</template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option CDATA_IN_HTML_CONTENT <template><![CDATA[cdata]]></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "[CDATA[cdata]]",
+          "loc": Object {
+            "end": Object {
+              "column": 28,
+              "line": 1,
+              "offset": 27,
+            },
+            "source": "<![CDATA[cdata]]>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 1,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 39,
+          "line": 1,
+          "offset": 38,
+        },
+        "source": "<template><![CDATA[cdata]]></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 39,
+      "line": 1,
+      "offset": 38,
+    },
+    "source": "<template><![CDATA[cdata]]></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option CDATA_IN_HTML_CONTENT <template><svg><![CDATA[cdata]]></svg></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [
+            Object {
+              "content": "cdata",
+              "isEmpty": false,
+              "loc": Object {
+                "end": Object {
+                  "column": 30,
+                  "line": 1,
+                  "offset": 29,
+                },
+                "source": "cdata",
+                "start": Object {
+                  "column": 25,
+                  "line": 1,
+                  "offset": 24,
+                },
+              },
+              "type": 0,
+            },
+          ],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 39,
+              "line": 1,
+              "offset": 38,
+            },
+            "source": "<svg><![CDATA[cdata]]></svg>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 1,
+          "props": Array [],
+          "tag": "svg",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 50,
+          "line": 1,
+          "offset": 49,
+        },
+        "source": "<template><svg><![CDATA[cdata]]></svg></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 50,
+      "line": 1,
+      "offset": 49,
+    },
+    "source": "<template><svg><![CDATA[cdata]]></svg></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option CHARACTER_REFERENCE_OUTSIDE_UNICODE_RANGE <template>&#1234567;</template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "�",
+          "isEmpty": false,
+          "loc": Object {
+            "end": Object {
+              "column": 21,
+              "line": 1,
+              "offset": 20,
+            },
+            "source": "&#1234567;",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 0,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 32,
+          "line": 1,
+          "offset": 31,
+        },
+        "source": "<template>&#1234567;</template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 32,
+      "line": 1,
+      "offset": 31,
+    },
+    "source": "<template>&#1234567;</template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option CONTROL_CHARACTER_REFERENCE <template>&#0003;</template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "\ 3",
+          "isEmpty": false,
+          "loc": Object {
+            "end": Object {
+              "column": 18,
+              "line": 1,
+              "offset": 17,
+            },
+            "source": "&#0003;",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 0,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 29,
+          "line": 1,
+          "offset": 28,
+        },
+        "source": "<template>&#0003;</template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 29,
+      "line": 1,
+      "offset": 28,
+    },
+    "source": "<template>&#0003;</template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option CONTROL_CHARACTER_REFERENCE <template>&#x7F;</template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "\7f",
+          "isEmpty": false,
+          "loc": Object {
+            "end": Object {
+              "column": 17,
+              "line": 1,
+              "offset": 16,
+            },
+            "source": "&#x7F;",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 0,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 28,
+          "line": 1,
+          "offset": 27,
+        },
+        "source": "<template>&#x7F;</template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 28,
+      "line": 1,
+      "offset": 27,
+    },
+    "source": "<template>&#x7F;</template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option DUPLICATE_ATTRIBUTE <template><div id="" id=""></div></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 34,
+              "line": 1,
+              "offset": 33,
+            },
+            "source": "<div id=\\"\\" id=\\"\\"></div>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 21,
+                  "line": 1,
+                  "offset": 20,
+                },
+                "source": "id=\\"\\"",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "id",
+              "type": 3,
+              "value": Object {
+                "content": "",
+                "isEmpty": true,
+                "loc": Object {
+                  "end": Object {
+                    "column": 21,
+                    "line": 1,
+                    "offset": 20,
+                  },
+                  "source": "\\"\\"",
+                  "start": Object {
+                    "column": 19,
+                    "line": 1,
+                    "offset": 18,
+                  },
+                },
+                "type": 0,
+              },
+            },
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 27,
+                  "line": 1,
+                  "offset": 26,
+                },
+                "source": "id=\\"\\"",
+                "start": Object {
+                  "column": 22,
+                  "line": 1,
+                  "offset": 21,
+                },
+              },
+              "name": "id",
+              "type": 3,
+              "value": Object {
+                "content": "",
+                "isEmpty": true,
+                "loc": Object {
+                  "end": Object {
+                    "column": 27,
+                    "line": 1,
+                    "offset": 26,
+                  },
+                  "source": "\\"\\"",
+                  "start": Object {
+                    "column": 25,
+                    "line": 1,
+                    "offset": 24,
+                  },
+                },
+                "type": 0,
+              },
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 45,
+          "line": 1,
+          "offset": 44,
+        },
+        "source": "<template><div id=\\"\\" id=\\"\\"></div></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 45,
+      "line": 1,
+      "offset": 44,
+    },
+    "source": "<template><div id=\\"\\" id=\\"\\"></div></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option END_TAG_WITH_ATTRIBUTES <template><div></div id=""></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 28,
+              "line": 1,
+              "offset": 27,
+            },
+            "source": "<div></div id=\\"\\">",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 39,
+          "line": 1,
+          "offset": 38,
+        },
+        "source": "<template><div></div id=\\"\\"></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 39,
+      "line": 1,
+      "offset": 38,
+    },
+    "source": "<template><div></div id=\\"\\"></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option END_TAG_WITH_TRAILING_SOLIDUS <template><div></div/></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 23,
+              "line": 1,
+              "offset": 22,
+            },
+            "source": "<div></div/>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 34,
+          "line": 1,
+          "offset": 33,
+        },
+        "source": "<template><div></div/></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 34,
+      "line": 1,
+      "offset": 33,
+    },
+    "source": "<template><div></div/></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option EOF_BEFORE_TAG_NAME <template>< 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "<",
+          "isEmpty": false,
+          "loc": Object {
+            "end": Object {
+              "column": 12,
+              "line": 1,
+              "offset": 11,
+            },
+            "source": "<",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 0,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 12,
+          "line": 1,
+          "offset": 11,
+        },
+        "source": "<template><",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 12,
+      "line": 1,
+      "offset": 11,
+    },
+    "source": "<template><",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option EOF_BEFORE_TAG_NAME <template></ 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "</",
+          "isEmpty": false,
+          "loc": Object {
+            "end": Object {
+              "column": 13,
+              "line": 1,
+              "offset": 12,
+            },
+            "source": "</",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 0,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 13,
+          "line": 1,
+          "offset": 12,
+        },
+        "source": "<template></",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 13,
+      "line": 1,
+      "offset": 12,
+    },
+    "source": "<template></",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option EOF_IN_CDATA <template><svg><![CDATA[ 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 25,
+              "line": 1,
+              "offset": 24,
+            },
+            "source": "<svg><![CDATA[",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 1,
+          "props": Array [],
+          "tag": "svg",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 25,
+          "line": 1,
+          "offset": 24,
+        },
+        "source": "<template><svg><![CDATA[",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 25,
+      "line": 1,
+      "offset": 24,
+    },
+    "source": "<template><svg><![CDATA[",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option EOF_IN_CDATA <template><svg><![CDATA[cdata 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [
+            Object {
+              "content": "cdata",
+              "isEmpty": false,
+              "loc": Object {
+                "end": Object {
+                  "column": 30,
+                  "line": 1,
+                  "offset": 29,
+                },
+                "source": "cdata",
+                "start": Object {
+                  "column": 25,
+                  "line": 1,
+                  "offset": 24,
+                },
+              },
+              "type": 0,
+            },
+          ],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 30,
+              "line": 1,
+              "offset": 29,
+            },
+            "source": "<svg><![CDATA[cdata",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 1,
+          "props": Array [],
+          "tag": "svg",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 30,
+          "line": 1,
+          "offset": 29,
+        },
+        "source": "<template><svg><![CDATA[cdata",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 30,
+      "line": 1,
+      "offset": 29,
+    },
+    "source": "<template><svg><![CDATA[cdata",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option EOF_IN_COMMENT <template><! 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "",
+          "loc": Object {
+            "end": Object {
+              "column": 13,
+              "line": 1,
+              "offset": 12,
+            },
+            "source": "<!",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 1,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 13,
+          "line": 1,
+          "offset": 12,
+        },
+        "source": "<template><!",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 13,
+      "line": 1,
+      "offset": 12,
+    },
+    "source": "<template><!",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option EOF_IN_COMMENT <template><!- 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "-",
+          "loc": Object {
+            "end": Object {
+              "column": 14,
+              "line": 1,
+              "offset": 13,
+            },
+            "source": "<!-",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 1,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 14,
+          "line": 1,
+          "offset": 13,
+        },
+        "source": "<template><!-",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 14,
+      "line": 1,
+      "offset": 13,
+    },
+    "source": "<template><!-",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option EOF_IN_COMMENT <template><!-- 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "",
+          "loc": Object {
+            "end": Object {
+              "column": 15,
+              "line": 1,
+              "offset": 14,
+            },
+            "source": "<!--",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 1,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 15,
+          "line": 1,
+          "offset": 14,
+        },
+        "source": "<template><!--",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 15,
+      "line": 1,
+      "offset": 14,
+    },
+    "source": "<template><!--",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option EOF_IN_COMMENT <template><!--comment 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "comment",
+          "loc": Object {
+            "end": Object {
+              "column": 22,
+              "line": 1,
+              "offset": 21,
+            },
+            "source": "<!--comment",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 1,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 22,
+          "line": 1,
+          "offset": 21,
+        },
+        "source": "<template><!--comment",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 22,
+      "line": 1,
+      "offset": 21,
+    },
+    "source": "<template><!--comment",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option EOF_IN_COMMENT <template><!abc 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "abc",
+          "loc": Object {
+            "end": Object {
+              "column": 16,
+              "line": 1,
+              "offset": 15,
+            },
+            "source": "<!abc",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 1,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 16,
+          "line": 1,
+          "offset": 15,
+        },
+        "source": "<template><!abc",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 16,
+      "line": 1,
+      "offset": 15,
+    },
+    "source": "<template><!abc",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option EOF_IN_SCRIPT_HTML_COMMENT_LIKE_TEXT <script><!--console.log('hello') 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "<!--console.log('hello')",
+          "isEmpty": false,
+          "loc": Object {
+            "end": Object {
+              "column": 33,
+              "line": 1,
+              "offset": 32,
+            },
+            "source": "<!--console.log('hello')",
+            "start": Object {
+              "column": 9,
+              "line": 1,
+              "offset": 8,
+            },
+          },
+          "type": 0,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 33,
+          "line": 1,
+          "offset": 32,
+        },
+        "source": "<script><!--console.log('hello')",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "script",
+      "tagType": 0,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 33,
+      "line": 1,
+      "offset": 32,
+    },
+    "source": "<script><!--console.log('hello')",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option EOF_IN_SCRIPT_HTML_COMMENT_LIKE_TEXT <script>console.log('hello') 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "console.log('hello')",
+          "isEmpty": false,
+          "loc": Object {
+            "end": Object {
+              "column": 29,
+              "line": 1,
+              "offset": 28,
+            },
+            "source": "console.log('hello')",
+            "start": Object {
+              "column": 9,
+              "line": 1,
+              "offset": 8,
+            },
+          },
+          "type": 0,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 29,
+          "line": 1,
+          "offset": 28,
+        },
+        "source": "<script>console.log('hello')",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "script",
+      "tagType": 0,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 29,
+      "line": 1,
+      "offset": 28,
+    },
+    "source": "<script>console.log('hello')",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option EOF_IN_TAG <template><div  1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 16,
+              "line": 1,
+              "offset": 15,
+            },
+            "source": "<div ",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 16,
+          "line": 1,
+          "offset": 15,
+        },
+        "source": "<template><div ",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 16,
+      "line": 1,
+      "offset": 15,
+    },
+    "source": "<template><div ",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option EOF_IN_TAG <template><div 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 15,
+              "line": 1,
+              "offset": 14,
+            },
+            "source": "<div",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 15,
+          "line": 1,
+          "offset": 14,
+        },
+        "source": "<template><div",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 15,
+      "line": 1,
+      "offset": 14,
+    },
+    "source": "<template><div",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option EOF_IN_TAG <template><div id  1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 19,
+              "line": 1,
+              "offset": 18,
+            },
+            "source": "<div id ",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 18,
+                  "line": 1,
+                  "offset": 17,
+                },
+                "source": "id",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "id",
+              "type": 3,
+              "value": undefined,
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 19,
+          "line": 1,
+          "offset": 18,
+        },
+        "source": "<template><div id ",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 19,
+      "line": 1,
+      "offset": 18,
+    },
+    "source": "<template><div id ",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option EOF_IN_TAG <template><div id = 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 20,
+              "line": 1,
+              "offset": 19,
+            },
+            "source": "<div id =",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 20,
+                  "line": 1,
+                  "offset": 19,
+                },
+                "source": "id =",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "id",
+              "type": 3,
+              "value": undefined,
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 20,
+          "line": 1,
+          "offset": 19,
+        },
+        "source": "<template><div id =",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 20,
+      "line": 1,
+      "offset": 19,
+    },
+    "source": "<template><div id =",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option EOF_IN_TAG <template><div id 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 18,
+              "line": 1,
+              "offset": 17,
+            },
+            "source": "<div id",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 18,
+                  "line": 1,
+                  "offset": 17,
+                },
+                "source": "id",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "id",
+              "type": 3,
+              "value": undefined,
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 18,
+          "line": 1,
+          "offset": 17,
+        },
+        "source": "<template><div id",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 18,
+      "line": 1,
+      "offset": 17,
+    },
+    "source": "<template><div id",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option EOF_IN_TAG <template><div id="abc 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 23,
+              "line": 1,
+              "offset": 22,
+            },
+            "source": "<div id=\\"abc",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 23,
+                  "line": 1,
+                  "offset": 22,
+                },
+                "source": "id=\\"abc",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "id",
+              "type": 3,
+              "value": Object {
+                "content": "abc",
+                "isEmpty": false,
+                "loc": Object {
+                  "end": Object {
+                    "column": 23,
+                    "line": 1,
+                    "offset": 22,
+                  },
+                  "source": "\\"abc",
+                  "start": Object {
+                    "column": 19,
+                    "line": 1,
+                    "offset": 18,
+                  },
+                },
+                "type": 0,
+              },
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 23,
+          "line": 1,
+          "offset": 22,
+        },
+        "source": "<template><div id=\\"abc",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 23,
+      "line": 1,
+      "offset": 22,
+    },
+    "source": "<template><div id=\\"abc",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option EOF_IN_TAG <template><div id="abc" 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 24,
+              "line": 1,
+              "offset": 23,
+            },
+            "source": "<div id=\\"abc\\"",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 24,
+                  "line": 1,
+                  "offset": 23,
+                },
+                "source": "id=\\"abc\\"",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "id",
+              "type": 3,
+              "value": Object {
+                "content": "abc",
+                "isEmpty": false,
+                "loc": Object {
+                  "end": Object {
+                    "column": 24,
+                    "line": 1,
+                    "offset": 23,
+                  },
+                  "source": "\\"abc\\"",
+                  "start": Object {
+                    "column": 19,
+                    "line": 1,
+                    "offset": 18,
+                  },
+                },
+                "type": 0,
+              },
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 24,
+          "line": 1,
+          "offset": 23,
+        },
+        "source": "<template><div id=\\"abc\\"",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 24,
+      "line": 1,
+      "offset": 23,
+    },
+    "source": "<template><div id=\\"abc\\"",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option EOF_IN_TAG <template><div id="abc"/ 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 25,
+              "line": 1,
+              "offset": 24,
+            },
+            "source": "<div id=\\"abc\\"/",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 24,
+                  "line": 1,
+                  "offset": 23,
+                },
+                "source": "id=\\"abc\\"",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "id",
+              "type": 3,
+              "value": Object {
+                "content": "abc",
+                "isEmpty": false,
+                "loc": Object {
+                  "end": Object {
+                    "column": 24,
+                    "line": 1,
+                    "offset": 23,
+                  },
+                  "source": "\\"abc\\"",
+                  "start": Object {
+                    "column": 19,
+                    "line": 1,
+                    "offset": 18,
+                  },
+                },
+                "type": 0,
+              },
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 25,
+          "line": 1,
+          "offset": 24,
+        },
+        "source": "<template><div id=\\"abc\\"/",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 25,
+      "line": 1,
+      "offset": 24,
+    },
+    "source": "<template><div id=\\"abc\\"/",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option EOF_IN_TAG <template><div id='abc 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 23,
+              "line": 1,
+              "offset": 22,
+            },
+            "source": "<div id='abc",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 23,
+                  "line": 1,
+                  "offset": 22,
+                },
+                "source": "id='abc",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "id",
+              "type": 3,
+              "value": Object {
+                "content": "abc",
+                "isEmpty": false,
+                "loc": Object {
+                  "end": Object {
+                    "column": 23,
+                    "line": 1,
+                    "offset": 22,
+                  },
+                  "source": "'abc",
+                  "start": Object {
+                    "column": 19,
+                    "line": 1,
+                    "offset": 18,
+                  },
+                },
+                "type": 0,
+              },
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 23,
+          "line": 1,
+          "offset": 22,
+        },
+        "source": "<template><div id='abc",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 23,
+      "line": 1,
+      "offset": 22,
+    },
+    "source": "<template><div id='abc",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option EOF_IN_TAG <template><div id='abc' 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 24,
+              "line": 1,
+              "offset": 23,
+            },
+            "source": "<div id='abc'",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 24,
+                  "line": 1,
+                  "offset": 23,
+                },
+                "source": "id='abc'",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "id",
+              "type": 3,
+              "value": Object {
+                "content": "abc",
+                "isEmpty": false,
+                "loc": Object {
+                  "end": Object {
+                    "column": 24,
+                    "line": 1,
+                    "offset": 23,
+                  },
+                  "source": "'abc'",
+                  "start": Object {
+                    "column": 19,
+                    "line": 1,
+                    "offset": 18,
+                  },
+                },
+                "type": 0,
+              },
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 24,
+          "line": 1,
+          "offset": 23,
+        },
+        "source": "<template><div id='abc'",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 24,
+      "line": 1,
+      "offset": 23,
+    },
+    "source": "<template><div id='abc'",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option EOF_IN_TAG <template><div id='abc'/ 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 25,
+              "line": 1,
+              "offset": 24,
+            },
+            "source": "<div id='abc'/",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 24,
+                  "line": 1,
+                  "offset": 23,
+                },
+                "source": "id='abc'",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "id",
+              "type": 3,
+              "value": Object {
+                "content": "abc",
+                "isEmpty": false,
+                "loc": Object {
+                  "end": Object {
+                    "column": 24,
+                    "line": 1,
+                    "offset": 23,
+                  },
+                  "source": "'abc'",
+                  "start": Object {
+                    "column": 19,
+                    "line": 1,
+                    "offset": 18,
+                  },
+                },
+                "type": 0,
+              },
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 25,
+          "line": 1,
+          "offset": 24,
+        },
+        "source": "<template><div id='abc'/",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 25,
+      "line": 1,
+      "offset": 24,
+    },
+    "source": "<template><div id='abc'/",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option EOF_IN_TAG <template><div id=abc / 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 24,
+              "line": 1,
+              "offset": 23,
+            },
+            "source": "<div id=abc /",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 22,
+                  "line": 1,
+                  "offset": 21,
+                },
+                "source": "id=abc",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "id",
+              "type": 3,
+              "value": Object {
+                "content": "abc",
+                "isEmpty": false,
+                "loc": Object {
+                  "end": Object {
+                    "column": 22,
+                    "line": 1,
+                    "offset": 21,
+                  },
+                  "source": "abc",
+                  "start": Object {
+                    "column": 19,
+                    "line": 1,
+                    "offset": 18,
+                  },
+                },
+                "type": 0,
+              },
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 24,
+          "line": 1,
+          "offset": 23,
+        },
+        "source": "<template><div id=abc /",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 24,
+      "line": 1,
+      "offset": 23,
+    },
+    "source": "<template><div id=abc /",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option EOF_IN_TAG <template><div id=abc 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 22,
+              "line": 1,
+              "offset": 21,
+            },
+            "source": "<div id=abc",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 22,
+                  "line": 1,
+                  "offset": 21,
+                },
+                "source": "id=abc",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "id",
+              "type": 3,
+              "value": Object {
+                "content": "abc",
+                "isEmpty": false,
+                "loc": Object {
+                  "end": Object {
+                    "column": 22,
+                    "line": 1,
+                    "offset": 21,
+                  },
+                  "source": "abc",
+                  "start": Object {
+                    "column": 19,
+                    "line": 1,
+                    "offset": 18,
+                  },
+                },
+                "type": 0,
+              },
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 22,
+          "line": 1,
+          "offset": 21,
+        },
+        "source": "<template><div id=abc",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 22,
+      "line": 1,
+      "offset": 21,
+    },
+    "source": "<template><div id=abc",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option INCORRECTLY_CLOSED_COMMENT <template><!--comment--!></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "comment",
+          "loc": Object {
+            "end": Object {
+              "column": 26,
+              "line": 1,
+              "offset": 25,
+            },
+            "source": "<!--comment--!>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 1,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 37,
+          "line": 1,
+          "offset": 36,
+        },
+        "source": "<template><!--comment--!></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 37,
+      "line": 1,
+      "offset": 36,
+    },
+    "source": "<template><!--comment--!></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option INCORRECTLY_OPENED_COMMENT <!DOCTYPE html> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "content": "DOCTYPE html",
+      "loc": Object {
+        "end": Object {
+          "column": 16,
+          "line": 1,
+          "offset": 15,
+        },
+        "source": "<!DOCTYPE html>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "type": 1,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 16,
+      "line": 1,
+      "offset": 15,
+    },
+    "source": "<!DOCTYPE html>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option INCORRECTLY_OPENED_COMMENT <template><!></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "",
+          "loc": Object {
+            "end": Object {
+              "column": 14,
+              "line": 1,
+              "offset": 13,
+            },
+            "source": "<!>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 1,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 25,
+          "line": 1,
+          "offset": 24,
+        },
+        "source": "<template><!></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 25,
+      "line": 1,
+      "offset": 24,
+    },
+    "source": "<template><!></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option INCORRECTLY_OPENED_COMMENT <template><!-></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "-",
+          "loc": Object {
+            "end": Object {
+              "column": 15,
+              "line": 1,
+              "offset": 14,
+            },
+            "source": "<!->",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 1,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 26,
+          "line": 1,
+          "offset": 25,
+        },
+        "source": "<template><!-></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 26,
+      "line": 1,
+      "offset": 25,
+    },
+    "source": "<template><!-></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option INCORRECTLY_OPENED_COMMENT <template><!ELEMENT br EMPTY></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "ELEMENT br EMPTY",
+          "loc": Object {
+            "end": Object {
+              "column": 30,
+              "line": 1,
+              "offset": 29,
+            },
+            "source": "<!ELEMENT br EMPTY>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 1,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 41,
+          "line": 1,
+          "offset": 40,
+        },
+        "source": "<template><!ELEMENT br EMPTY></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 41,
+      "line": 1,
+      "offset": 40,
+    },
+    "source": "<template><!ELEMENT br EMPTY></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option INVALID_FIRST_CHARACTER_OF_TAG_NAME <template></�></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "�",
+          "loc": Object {
+            "end": Object {
+              "column": 15,
+              "line": 1,
+              "offset": 14,
+            },
+            "source": "</�>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 1,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 26,
+          "line": 1,
+          "offset": 25,
+        },
+        "source": "<template></�></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 26,
+      "line": 1,
+      "offset": 25,
+    },
+    "source": "<template></�></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option INVALID_FIRST_CHARACTER_OF_TAG_NAME <template><�></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "<�>",
+          "isEmpty": false,
+          "loc": Object {
+            "end": Object {
+              "column": 14,
+              "line": 1,
+              "offset": 13,
+            },
+            "source": "<�>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 0,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 25,
+          "line": 1,
+          "offset": 24,
+        },
+        "source": "<template><�></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 25,
+      "line": 1,
+      "offset": 24,
+    },
+    "source": "<template><�></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option INVALID_FIRST_CHARACTER_OF_TAG_NAME <template>{{a < b}}</template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "a < b",
+          "isStatic": false,
+          "loc": Object {
+            "end": Object {
+              "column": 20,
+              "line": 1,
+              "offset": 19,
+            },
+            "source": "{{a < b}}",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 4,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 31,
+          "line": 1,
+          "offset": 30,
+        },
+        "source": "<template>{{a < b}}</template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 31,
+      "line": 1,
+      "offset": 30,
+    },
+    "source": "<template>{{a < b}}</template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option INVALID_FIRST_CHARACTER_OF_TAG_NAME <template>a < b</template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "a < b",
+          "isEmpty": false,
+          "loc": Object {
+            "end": Object {
+              "column": 16,
+              "line": 1,
+              "offset": 15,
+            },
+            "source": "a < b",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 0,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 27,
+          "line": 1,
+          "offset": 26,
+        },
+        "source": "<template>a < b</template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 27,
+      "line": 1,
+      "offset": 26,
+    },
+    "source": "<template>a < b</template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option INVALID_FIRST_CHARACTER_OF_TAG_NAME <template>a </ b</template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "a ",
+          "isEmpty": false,
+          "loc": Object {
+            "end": Object {
+              "column": 13,
+              "line": 1,
+              "offset": 12,
+            },
+            "source": "a ",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 0,
+        },
+        Object {
+          "content": " b</template",
+          "loc": Object {
+            "end": Object {
+              "column": 28,
+              "line": 1,
+              "offset": 27,
+            },
+            "source": "</ b</template>",
+            "start": Object {
+              "column": 13,
+              "line": 1,
+              "offset": 12,
+            },
+          },
+          "type": 1,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 28,
+          "line": 1,
+          "offset": 27,
+        },
+        "source": "<template>a </ b</template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 28,
+      "line": 1,
+      "offset": 27,
+    },
+    "source": "<template>a </ b</template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option MISSING_ATTRIBUTE_VALUE <template><div id= /></div></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 28,
+              "line": 1,
+              "offset": 27,
+            },
+            "source": "<div id= /></div>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 21,
+                  "line": 1,
+                  "offset": 20,
+                },
+                "source": "id= /",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "id",
+              "type": 3,
+              "value": Object {
+                "content": "/",
+                "isEmpty": false,
+                "loc": Object {
+                  "end": Object {
+                    "column": 21,
+                    "line": 1,
+                    "offset": 20,
+                  },
+                  "source": "/",
+                  "start": Object {
+                    "column": 20,
+                    "line": 1,
+                    "offset": 19,
+                  },
+                },
+                "type": 0,
+              },
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 39,
+          "line": 1,
+          "offset": 38,
+        },
+        "source": "<template><div id= /></div></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 39,
+      "line": 1,
+      "offset": 38,
+    },
+    "source": "<template><div id= /></div></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option MISSING_ATTRIBUTE_VALUE <template><div id= ></div></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 27,
+              "line": 1,
+              "offset": 26,
+            },
+            "source": "<div id= ></div>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 20,
+                  "line": 1,
+                  "offset": 19,
+                },
+                "source": "id= ",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "id",
+              "type": 3,
+              "value": undefined,
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 38,
+          "line": 1,
+          "offset": 37,
+        },
+        "source": "<template><div id= ></div></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 38,
+      "line": 1,
+      "offset": 37,
+    },
+    "source": "<template><div id= ></div></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option MISSING_ATTRIBUTE_VALUE <template><div id=></div></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 26,
+              "line": 1,
+              "offset": 25,
+            },
+            "source": "<div id=></div>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 19,
+                  "line": 1,
+                  "offset": 18,
+                },
+                "source": "id=",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "id",
+              "type": 3,
+              "value": undefined,
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 37,
+          "line": 1,
+          "offset": 36,
+        },
+        "source": "<template><div id=></div></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 37,
+      "line": 1,
+      "offset": 36,
+    },
+    "source": "<template><div id=></div></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option MISSING_END_TAG_NAME <template></></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 25,
+          "line": 1,
+          "offset": 24,
+        },
+        "source": "<template></></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 25,
+      "line": 1,
+      "offset": 24,
+    },
+    "source": "<template></></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE <template>&#40</template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "(",
+          "isEmpty": false,
+          "loc": Object {
+            "end": Object {
+              "column": 15,
+              "line": 1,
+              "offset": 14,
+            },
+            "source": "&#40",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 0,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 26,
+          "line": 1,
+          "offset": 25,
+        },
+        "source": "<template>&#40</template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 26,
+      "line": 1,
+      "offset": 25,
+    },
+    "source": "<template>&#40</template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE <template>&#x40</template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "@",
+          "isEmpty": false,
+          "loc": Object {
+            "end": Object {
+              "column": 16,
+              "line": 1,
+              "offset": 15,
+            },
+            "source": "&#x40",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 0,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 27,
+          "line": 1,
+          "offset": 26,
+        },
+        "source": "<template>&#x40</template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 27,
+      "line": 1,
+      "offset": 26,
+    },
+    "source": "<template>&#x40</template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE <template>&amp</template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "&",
+          "isEmpty": false,
+          "loc": Object {
+            "end": Object {
+              "column": 15,
+              "line": 1,
+              "offset": 14,
+            },
+            "source": "&amp",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 0,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 26,
+          "line": 1,
+          "offset": 25,
+        },
+        "source": "<template>&amp</template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 26,
+      "line": 1,
+      "offset": 25,
+    },
+    "source": "<template>&amp</template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option MISSING_WHITESPACE_BETWEEN_ATTRIBUTES <template><div id="foo"\\x0d;\\x0a;class="bar"></div></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 19,
+              "line": 2,
+              "offset": 43,
+            },
+            "source": "<div id=\\"foo\\"
+class=\\"bar\\"></div>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 24,
+                  "line": 1,
+                  "offset": 23,
+                },
+                "source": "id=\\"foo\\"",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "id",
+              "type": 3,
+              "value": Object {
+                "content": "foo",
+                "isEmpty": false,
+                "loc": Object {
+                  "end": Object {
+                    "column": 24,
+                    "line": 1,
+                    "offset": 23,
+                  },
+                  "source": "\\"foo\\"",
+                  "start": Object {
+                    "column": 19,
+                    "line": 1,
+                    "offset": 18,
+                  },
+                },
+                "type": 0,
+              },
+            },
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 12,
+                  "line": 2,
+                  "offset": 36,
+                },
+                "source": "class=\\"bar\\"",
+                "start": Object {
+                  "column": 1,
+                  "line": 2,
+                  "offset": 25,
+                },
+              },
+              "name": "class",
+              "type": 3,
+              "value": Object {
+                "content": "bar",
+                "isEmpty": false,
+                "loc": Object {
+                  "end": Object {
+                    "column": 12,
+                    "line": 2,
+                    "offset": 36,
+                  },
+                  "source": "\\"bar\\"",
+                  "start": Object {
+                    "column": 7,
+                    "line": 2,
+                    "offset": 31,
+                  },
+                },
+                "type": 0,
+              },
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 30,
+          "line": 2,
+          "offset": 54,
+        },
+        "source": "<template><div id=\\"foo\\"
+class=\\"bar\\"></div></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 30,
+      "line": 2,
+      "offset": 54,
+    },
+    "source": "<template><div id=\\"foo\\"
+class=\\"bar\\"></div></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option MISSING_WHITESPACE_BETWEEN_ATTRIBUTES <template><div id="foo"class="bar"></div></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 42,
+              "line": 1,
+              "offset": 41,
+            },
+            "source": "<div id=\\"foo\\"class=\\"bar\\"></div>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 24,
+                  "line": 1,
+                  "offset": 23,
+                },
+                "source": "id=\\"foo\\"",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "id",
+              "type": 3,
+              "value": Object {
+                "content": "foo",
+                "isEmpty": false,
+                "loc": Object {
+                  "end": Object {
+                    "column": 24,
+                    "line": 1,
+                    "offset": 23,
+                  },
+                  "source": "\\"foo\\"",
+                  "start": Object {
+                    "column": 19,
+                    "line": 1,
+                    "offset": 18,
+                  },
+                },
+                "type": 0,
+              },
+            },
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 35,
+                  "line": 1,
+                  "offset": 34,
+                },
+                "source": "class=\\"bar\\"",
+                "start": Object {
+                  "column": 24,
+                  "line": 1,
+                  "offset": 23,
+                },
+              },
+              "name": "class",
+              "type": 3,
+              "value": Object {
+                "content": "bar",
+                "isEmpty": false,
+                "loc": Object {
+                  "end": Object {
+                    "column": 35,
+                    "line": 1,
+                    "offset": 34,
+                  },
+                  "source": "\\"bar\\"",
+                  "start": Object {
+                    "column": 30,
+                    "line": 1,
+                    "offset": 29,
+                  },
+                },
+                "type": 0,
+              },
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 53,
+          "line": 1,
+          "offset": 52,
+        },
+        "source": "<template><div id=\\"foo\\"class=\\"bar\\"></div></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 53,
+      "line": 1,
+      "offset": 52,
+    },
+    "source": "<template><div id=\\"foo\\"class=\\"bar\\"></div></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option NESTED_COMMENT <template><!--a<!-- 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "a<!--",
+          "loc": Object {
+            "end": Object {
+              "column": 20,
+              "line": 1,
+              "offset": 19,
+            },
+            "source": "<!--a<!--",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 1,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 20,
+          "line": 1,
+          "offset": 19,
+        },
+        "source": "<template><!--a<!--",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 20,
+      "line": 1,
+      "offset": 19,
+    },
+    "source": "<template><!--a<!--",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option NESTED_COMMENT <template><!--a<!--></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "a<!",
+          "loc": Object {
+            "end": Object {
+              "column": 21,
+              "line": 1,
+              "offset": 20,
+            },
+            "source": "<!--a<!-->",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 1,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 32,
+          "line": 1,
+          "offset": 31,
+        },
+        "source": "<template><!--a<!--></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 32,
+      "line": 1,
+      "offset": 31,
+    },
+    "source": "<template><!--a<!--></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option NESTED_COMMENT <template><!--a<!--b<!--c--></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "a<!--b<!--c",
+          "loc": Object {
+            "end": Object {
+              "column": 29,
+              "line": 1,
+              "offset": 28,
+            },
+            "source": "<!--a<!--b<!--c-->",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 1,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 40,
+          "line": 1,
+          "offset": 39,
+        },
+        "source": "<template><!--a<!--b<!--c--></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 40,
+      "line": 1,
+      "offset": 39,
+    },
+    "source": "<template><!--a<!--b<!--c--></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option NESTED_COMMENT <template><!--a<!--b--></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "a<!--b",
+          "loc": Object {
+            "end": Object {
+              "column": 24,
+              "line": 1,
+              "offset": 23,
+            },
+            "source": "<!--a<!--b-->",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 1,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 35,
+          "line": 1,
+          "offset": 34,
+        },
+        "source": "<template><!--a<!--b--></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 35,
+      "line": 1,
+      "offset": 34,
+    },
+    "source": "<template><!--a<!--b--></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option NONCHARACTER_CHARACTER_REFERENCE <template>&#x1FFFF;</template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "🿿",
+          "isEmpty": false,
+          "loc": Object {
+            "end": Object {
+              "column": 20,
+              "line": 1,
+              "offset": 19,
+            },
+            "source": "&#x1FFFF;",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 0,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 31,
+          "line": 1,
+          "offset": 30,
+        },
+        "source": "<template>&#x1FFFF;</template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 31,
+      "line": 1,
+      "offset": 30,
+    },
+    "source": "<template>&#x1FFFF;</template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option NONCHARACTER_CHARACTER_REFERENCE <template>&#xFFFE;</template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "￾",
+          "isEmpty": false,
+          "loc": Object {
+            "end": Object {
+              "column": 19,
+              "line": 1,
+              "offset": 18,
+            },
+            "source": "&#xFFFE;",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 0,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 30,
+          "line": 1,
+          "offset": 29,
+        },
+        "source": "<template>&#xFFFE;</template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 30,
+      "line": 1,
+      "offset": 29,
+    },
+    "source": "<template>&#xFFFE;</template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option NULL_CHARACTER_REFERENCE <template>&#0000;</template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "�",
+          "isEmpty": false,
+          "loc": Object {
+            "end": Object {
+              "column": 18,
+              "line": 1,
+              "offset": 17,
+            },
+            "source": "&#0000;",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 0,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 29,
+          "line": 1,
+          "offset": 28,
+        },
+        "source": "<template>&#0000;</template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 29,
+      "line": 1,
+      "offset": 28,
+    },
+    "source": "<template>&#0000;</template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option SURROGATE_CHARACTER_REFERENCE <template>&#xD800;</template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "�",
+          "isEmpty": false,
+          "loc": Object {
+            "end": Object {
+              "column": 19,
+              "line": 1,
+              "offset": 18,
+            },
+            "source": "&#xD800;",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 0,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 30,
+          "line": 1,
+          "offset": 29,
+        },
+        "source": "<template>&#xD800;</template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 30,
+      "line": 1,
+      "offset": 29,
+    },
+    "source": "<template>&#xD800;</template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME <template><div a"bc=''></div></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 30,
+              "line": 1,
+              "offset": 29,
+            },
+            "source": "<div a\\"bc=''></div>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 23,
+                  "line": 1,
+                  "offset": 22,
+                },
+                "source": "a\\"bc=''",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "a\\"bc",
+              "type": 3,
+              "value": Object {
+                "content": "",
+                "isEmpty": true,
+                "loc": Object {
+                  "end": Object {
+                    "column": 23,
+                    "line": 1,
+                    "offset": 22,
+                  },
+                  "source": "''",
+                  "start": Object {
+                    "column": 21,
+                    "line": 1,
+                    "offset": 20,
+                  },
+                },
+                "type": 0,
+              },
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 41,
+          "line": 1,
+          "offset": 40,
+        },
+        "source": "<template><div a\\"bc=''></div></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 41,
+      "line": 1,
+      "offset": 40,
+    },
+    "source": "<template><div a\\"bc=''></div></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME <template><div a'bc=''></div></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 30,
+              "line": 1,
+              "offset": 29,
+            },
+            "source": "<div a'bc=''></div>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 23,
+                  "line": 1,
+                  "offset": 22,
+                },
+                "source": "a'bc=''",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "a'bc",
+              "type": 3,
+              "value": Object {
+                "content": "",
+                "isEmpty": true,
+                "loc": Object {
+                  "end": Object {
+                    "column": 23,
+                    "line": 1,
+                    "offset": 22,
+                  },
+                  "source": "''",
+                  "start": Object {
+                    "column": 21,
+                    "line": 1,
+                    "offset": 20,
+                  },
+                },
+                "type": 0,
+              },
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 41,
+          "line": 1,
+          "offset": 40,
+        },
+        "source": "<template><div a'bc=''></div></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 41,
+      "line": 1,
+      "offset": 40,
+    },
+    "source": "<template><div a'bc=''></div></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME <template><div a<bc=''></div></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 30,
+              "line": 1,
+              "offset": 29,
+            },
+            "source": "<div a<bc=''></div>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 23,
+                  "line": 1,
+                  "offset": 22,
+                },
+                "source": "a<bc=''",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "a<bc",
+              "type": 3,
+              "value": Object {
+                "content": "",
+                "isEmpty": true,
+                "loc": Object {
+                  "end": Object {
+                    "column": 23,
+                    "line": 1,
+                    "offset": 22,
+                  },
+                  "source": "''",
+                  "start": Object {
+                    "column": 21,
+                    "line": 1,
+                    "offset": 20,
+                  },
+                },
+                "type": 0,
+              },
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 41,
+          "line": 1,
+          "offset": 40,
+        },
+        "source": "<template><div a<bc=''></div></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 41,
+      "line": 1,
+      "offset": 40,
+    },
+    "source": "<template><div a<bc=''></div></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE <template><div foo=bar"></div></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 31,
+              "line": 1,
+              "offset": 30,
+            },
+            "source": "<div foo=bar\\"></div>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 24,
+                  "line": 1,
+                  "offset": 23,
+                },
+                "source": "foo=bar\\"",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "foo",
+              "type": 3,
+              "value": Object {
+                "content": "bar\\"",
+                "isEmpty": false,
+                "loc": Object {
+                  "end": Object {
+                    "column": 24,
+                    "line": 1,
+                    "offset": 23,
+                  },
+                  "source": "bar\\"",
+                  "start": Object {
+                    "column": 20,
+                    "line": 1,
+                    "offset": 19,
+                  },
+                },
+                "type": 0,
+              },
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 42,
+          "line": 1,
+          "offset": 41,
+        },
+        "source": "<template><div foo=bar\\"></div></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 42,
+      "line": 1,
+      "offset": 41,
+    },
+    "source": "<template><div foo=bar\\"></div></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE <template><div foo=bar'></div></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 31,
+              "line": 1,
+              "offset": 30,
+            },
+            "source": "<div foo=bar'></div>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 24,
+                  "line": 1,
+                  "offset": 23,
+                },
+                "source": "foo=bar'",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "foo",
+              "type": 3,
+              "value": Object {
+                "content": "bar'",
+                "isEmpty": false,
+                "loc": Object {
+                  "end": Object {
+                    "column": 24,
+                    "line": 1,
+                    "offset": 23,
+                  },
+                  "source": "bar'",
+                  "start": Object {
+                    "column": 20,
+                    "line": 1,
+                    "offset": 19,
+                  },
+                },
+                "type": 0,
+              },
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 42,
+          "line": 1,
+          "offset": 41,
+        },
+        "source": "<template><div foo=bar'></div></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 42,
+      "line": 1,
+      "offset": 41,
+    },
+    "source": "<template><div foo=bar'></div></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE <template><div foo=bar<div></div></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 34,
+              "line": 1,
+              "offset": 33,
+            },
+            "source": "<div foo=bar<div></div>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 27,
+                  "line": 1,
+                  "offset": 26,
+                },
+                "source": "foo=bar<div",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "foo",
+              "type": 3,
+              "value": Object {
+                "content": "bar<div",
+                "isEmpty": false,
+                "loc": Object {
+                  "end": Object {
+                    "column": 27,
+                    "line": 1,
+                    "offset": 26,
+                  },
+                  "source": "bar<div",
+                  "start": Object {
+                    "column": 20,
+                    "line": 1,
+                    "offset": 19,
+                  },
+                },
+                "type": 0,
+              },
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 45,
+          "line": 1,
+          "offset": 44,
+        },
+        "source": "<template><div foo=bar<div></div></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 45,
+      "line": 1,
+      "offset": 44,
+    },
+    "source": "<template><div foo=bar<div></div></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE <template><div foo=bar=baz></div></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 34,
+              "line": 1,
+              "offset": 33,
+            },
+            "source": "<div foo=bar=baz></div>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 27,
+                  "line": 1,
+                  "offset": 26,
+                },
+                "source": "foo=bar=baz",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "foo",
+              "type": 3,
+              "value": Object {
+                "content": "bar=baz",
+                "isEmpty": false,
+                "loc": Object {
+                  "end": Object {
+                    "column": 27,
+                    "line": 1,
+                    "offset": 26,
+                  },
+                  "source": "bar=baz",
+                  "start": Object {
+                    "column": 20,
+                    "line": 1,
+                    "offset": 19,
+                  },
+                },
+                "type": 0,
+              },
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 45,
+          "line": 1,
+          "offset": 44,
+        },
+        "source": "<template><div foo=bar=baz></div></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 45,
+      "line": 1,
+      "offset": 44,
+    },
+    "source": "<template><div foo=bar=baz></div></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE <template><div foo=bar\`></div></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 31,
+              "line": 1,
+              "offset": 30,
+            },
+            "source": "<div foo=bar\`></div>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 24,
+                  "line": 1,
+                  "offset": 23,
+                },
+                "source": "foo=bar\`",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "foo",
+              "type": 3,
+              "value": Object {
+                "content": "bar\`",
+                "isEmpty": false,
+                "loc": Object {
+                  "end": Object {
+                    "column": 24,
+                    "line": 1,
+                    "offset": 23,
+                  },
+                  "source": "bar\`",
+                  "start": Object {
+                    "column": 20,
+                    "line": 1,
+                    "offset": 19,
+                  },
+                },
+                "type": 0,
+              },
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 42,
+          "line": 1,
+          "offset": 41,
+        },
+        "source": "<template><div foo=bar\`></div></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 42,
+      "line": 1,
+      "offset": 41,
+    },
+    "source": "<template><div foo=bar\`></div></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option UNEXPECTED_EQUALS_SIGN_BEFORE_ATTRIBUTE_NAME <template><div =></div></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 24,
+              "line": 1,
+              "offset": 23,
+            },
+            "source": "<div =></div>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 17,
+                  "line": 1,
+                  "offset": 16,
+                },
+                "source": "=",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "=",
+              "type": 3,
+              "value": undefined,
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 35,
+          "line": 1,
+          "offset": 34,
+        },
+        "source": "<template><div =></div></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 35,
+      "line": 1,
+      "offset": 34,
+    },
+    "source": "<template><div =></div></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option UNEXPECTED_EQUALS_SIGN_BEFORE_ATTRIBUTE_NAME <template><div =foo=bar></div></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 31,
+              "line": 1,
+              "offset": 30,
+            },
+            "source": "<div =foo=bar></div>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 24,
+                  "line": 1,
+                  "offset": 23,
+                },
+                "source": "=foo=bar",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "=foo",
+              "type": 3,
+              "value": Object {
+                "content": "bar",
+                "isEmpty": false,
+                "loc": Object {
+                  "end": Object {
+                    "column": 24,
+                    "line": 1,
+                    "offset": 23,
+                  },
+                  "source": "bar",
+                  "start": Object {
+                    "column": 21,
+                    "line": 1,
+                    "offset": 20,
+                  },
+                },
+                "type": 0,
+              },
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 42,
+          "line": 1,
+          "offset": 41,
+        },
+        "source": "<template><div =foo=bar></div></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 42,
+      "line": 1,
+      "offset": 41,
+    },
+    "source": "<template><div =foo=bar></div></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option UNEXPECTED_QUESTION_MARK_INSTEAD_OF_TAG_NAME <template><?xml?></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "?xml?",
+          "loc": Object {
+            "end": Object {
+              "column": 18,
+              "line": 1,
+              "offset": 17,
+            },
+            "source": "<?xml?>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 1,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 29,
+          "line": 1,
+          "offset": 28,
+        },
+        "source": "<template><?xml?></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 29,
+      "line": 1,
+      "offset": 28,
+    },
+    "source": "<template><?xml?></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option UNEXPECTED_SOLIDUS_IN_TAG <template><div a/b></div></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 26,
+              "line": 1,
+              "offset": 25,
+            },
+            "source": "<div a/b></div>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 17,
+                  "line": 1,
+                  "offset": 16,
+                },
+                "source": "a",
+                "start": Object {
+                  "column": 16,
+                  "line": 1,
+                  "offset": 15,
+                },
+              },
+              "name": "a",
+              "type": 3,
+              "value": undefined,
+            },
+            Object {
+              "loc": Object {
+                "end": Object {
+                  "column": 19,
+                  "line": 1,
+                  "offset": 18,
+                },
+                "source": "b",
+                "start": Object {
+                  "column": 18,
+                  "line": 1,
+                  "offset": 17,
+                },
+              },
+              "name": "b",
+              "type": 3,
+              "value": undefined,
+            },
+          ],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 37,
+          "line": 1,
+          "offset": 36,
+        },
+        "source": "<template><div a/b></div></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 37,
+      "line": 1,
+      "offset": 36,
+    },
+    "source": "<template><div a/b></div></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option UNKNOWN_NAMED_CHARACTER_REFERENCE <template>&unknown;</template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "&unknown;",
+          "isEmpty": false,
+          "loc": Object {
+            "end": Object {
+              "column": 20,
+              "line": 1,
+              "offset": 19,
+            },
+            "source": "&unknown;",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 0,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 31,
+          "line": 1,
+          "offset": 30,
+        },
+        "source": "<template>&unknown;</template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 31,
+      "line": 1,
+      "offset": 30,
+    },
+    "source": "<template>&unknown;</template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option X_INVALID_END_TAG <svg><![CDATA[</div>]]></svg> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "</div>",
+          "isEmpty": false,
+          "loc": Object {
+            "end": Object {
+              "column": 21,
+              "line": 1,
+              "offset": 20,
+            },
+            "source": "</div>",
+            "start": Object {
+              "column": 15,
+              "line": 1,
+              "offset": 14,
+            },
+          },
+          "type": 0,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 30,
+          "line": 1,
+          "offset": 29,
+        },
+        "source": "<svg><![CDATA[</div>]]></svg>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 1,
+      "props": Array [],
+      "tag": "svg",
+      "tagType": 0,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 30,
+      "line": 1,
+      "offset": 29,
+    },
+    "source": "<svg><![CDATA[</div>]]></svg>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option X_INVALID_END_TAG <svg><!--</div>--></svg> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "</div>",
+          "loc": Object {
+            "end": Object {
+              "column": 19,
+              "line": 1,
+              "offset": 18,
+            },
+            "source": "<!--</div>-->",
+            "start": Object {
+              "column": 6,
+              "line": 1,
+              "offset": 5,
+            },
+          },
+          "type": 1,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 25,
+          "line": 1,
+          "offset": 24,
+        },
+        "source": "<svg><!--</div>--></svg>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 1,
+      "props": Array [],
+      "tag": "svg",
+      "tagType": 0,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 25,
+      "line": 1,
+      "offset": 24,
+    },
+    "source": "<svg><!--</div>--></svg>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option X_INVALID_END_TAG <template></div></div></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 34,
+          "line": 1,
+          "offset": 33,
+        },
+        "source": "<template></div></div></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 34,
+      "line": 1,
+      "offset": 33,
+    },
+    "source": "<template></div></div></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option X_INVALID_END_TAG <template></div></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 28,
+          "line": 1,
+          "offset": 27,
+        },
+        "source": "<template></div></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 28,
+      "line": 1,
+      "offset": 27,
+    },
+    "source": "<template></div></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option X_INVALID_END_TAG <template>{{'</div>'}}</template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "'</div>'",
+          "isStatic": false,
+          "loc": Object {
+            "end": Object {
+              "column": 23,
+              "line": 1,
+              "offset": 22,
+            },
+            "source": "{{'</div>'}}",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 4,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 34,
+          "line": 1,
+          "offset": 33,
+        },
+        "source": "<template>{{'</div>'}}</template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 34,
+      "line": 1,
+      "offset": 33,
+    },
+    "source": "<template>{{'</div>'}}</template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option X_INVALID_END_TAG <textarea></div></textarea> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "content": "</div>",
+          "isEmpty": false,
+          "loc": Object {
+            "end": Object {
+              "column": 17,
+              "line": 1,
+              "offset": 16,
+            },
+            "source": "</div>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "type": 0,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 28,
+          "line": 1,
+          "offset": 27,
+        },
+        "source": "<textarea></div></textarea>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "textarea",
+      "tagType": 0,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 28,
+      "line": 1,
+      "offset": 27,
+    },
+    "source": "<textarea></div></textarea>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option X_MISSING_END_TAG <template><div> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 16,
+              "line": 1,
+              "offset": 15,
+            },
+            "source": "<div>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 16,
+          "line": 1,
+          "offset": 15,
+        },
+        "source": "<template><div>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 16,
+      "line": 1,
+      "offset": 15,
+    },
+    "source": "<template><div>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option X_MISSING_END_TAG <template><div></template> 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": false,
+          "loc": Object {
+            "end": Object {
+              "column": 16,
+              "line": 1,
+              "offset": 15,
+            },
+            "source": "<div>",
+            "start": Object {
+              "column": 11,
+              "line": 1,
+              "offset": 10,
+            },
+          },
+          "ns": 0,
+          "props": Array [],
+          "tag": "div",
+          "tagType": 0,
+          "type": 2,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 27,
+          "line": 1,
+          "offset": 26,
+        },
+        "source": "<template><div></template>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [],
+      "tag": "template",
+      "tagType": 3,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 27,
+      "line": 1,
+      "offset": 26,
+    },
+    "source": "<template><div></template>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option X_MISSING_INTERPOLATION_END {{ 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "content": "{{",
+      "isEmpty": false,
+      "loc": Object {
+        "end": Object {
+          "column": 3,
+          "line": 1,
+          "offset": 2,
+        },
+        "source": "{{",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "type": 0,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 3,
+      "line": 1,
+      "offset": 2,
+    },
+    "source": "{{",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option X_MISSING_INTERPOLATION_END {{ foo 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "content": "{{ foo",
+      "isEmpty": false,
+      "loc": Object {
+        "end": Object {
+          "column": 7,
+          "line": 1,
+          "offset": 6,
+        },
+        "source": "{{ foo",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "type": 0,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 7,
+      "line": 1,
+      "offset": 6,
+    },
+    "source": "{{ foo",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse onError option X_MISSING_INTERPOLATION_END {{}} 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "content": "",
+      "isStatic": true,
+      "loc": Object {
+        "end": Object {
+          "column": 5,
+          "line": 1,
+          "offset": 4,
+        },
+        "source": "{{}}",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "type": 4,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 5,
+      "line": 1,
+      "offset": 4,
+    },
+    "source": "{{}}",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse self closing multiple tag 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [],
+      "isSelfClosing": true,
+      "loc": Object {
+        "end": Object {
+          "column": 37,
+          "line": 1,
+          "offset": 36,
+        },
+        "source": "<div :class=\\"{ some: condition }\\" />",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [
+        Object {
+          "arg": Object {
+            "content": "class",
+            "isStatic": true,
+            "loc": Object {
+              "end": Object {
+                "column": 12,
+                "line": 1,
+                "offset": 11,
+              },
+              "source": "class",
+              "start": Object {
+                "column": 7,
+                "line": 1,
+                "offset": 6,
+              },
+            },
+            "type": 4,
+          },
+          "exp": Object {
+            "content": "{ some: condition }",
+            "isStatic": false,
+            "loc": Object {
+              "end": Object {
+                "column": 34,
+                "line": 1,
+                "offset": 33,
+              },
+              "source": "\\"{ some: condition }\\"",
+              "start": Object {
+                "column": 13,
+                "line": 1,
+                "offset": 12,
+              },
+            },
+            "type": 4,
+          },
+          "loc": Object {
+            "end": Object {
+              "column": 34,
+              "line": 1,
+              "offset": 33,
+            },
+            "source": ":class=\\"{ some: condition }\\"",
+            "start": Object {
+              "column": 6,
+              "line": 1,
+              "offset": 5,
+            },
+          },
+          "modifiers": Array [],
+          "name": "bind",
+          "type": 5,
+        },
+      ],
+      "tag": "div",
+      "tagType": 0,
+      "type": 2,
+    },
+    Object {
+      "children": Array [],
+      "isSelfClosing": true,
+      "loc": Object {
+        "end": Object {
+          "column": 37,
+          "line": 2,
+          "offset": 73,
+        },
+        "source": "<p v-bind:style=\\"{ color: 'red' }\\"/>",
+        "start": Object {
+          "column": 1,
+          "line": 2,
+          "offset": 37,
+        },
+      },
+      "ns": 0,
+      "props": Array [
+        Object {
+          "arg": Object {
+            "content": "style",
+            "isStatic": true,
+            "loc": Object {
+              "end": Object {
+                "column": 16,
+                "line": 2,
+                "offset": 52,
+              },
+              "source": "style",
+              "start": Object {
+                "column": 11,
+                "line": 2,
+                "offset": 47,
+              },
+            },
+            "type": 4,
+          },
+          "exp": Object {
+            "content": "{ color: 'red' }",
+            "isStatic": false,
+            "loc": Object {
+              "end": Object {
+                "column": 35,
+                "line": 2,
+                "offset": 71,
+              },
+              "source": "\\"{ color: 'red' }\\"",
+              "start": Object {
+                "column": 17,
+                "line": 2,
+                "offset": 53,
+              },
+            },
+            "type": 4,
+          },
+          "loc": Object {
+            "end": Object {
+              "column": 35,
+              "line": 2,
+              "offset": 71,
+            },
+            "source": "v-bind:style=\\"{ color: 'red' }\\"",
+            "start": Object {
+              "column": 4,
+              "line": 2,
+              "offset": 40,
+            },
+          },
+          "modifiers": Array [],
+          "name": "bind",
+          "type": 5,
+        },
+      ],
+      "tag": "p",
+      "tagType": 0,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 37,
+      "line": 2,
+      "offset": 73,
+    },
+    "source": "<div :class=\\"{ some: condition }\\" />
+<p v-bind:style=\\"{ color: 'red' }\\"/>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
+
+exports[`parser/parse valid html 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "children": Array [
+        Object {
+          "children": Array [],
+          "isSelfClosing": true,
+          "loc": Object {
+            "end": Object {
+              "column": 38,
+              "line": 2,
+              "offset": 73,
+            },
+            "source": "<p v-bind:style=\\"{ color: 'red' }\\"/>",
+            "start": Object {
+              "column": 2,
+              "line": 2,
+              "offset": 37,
+            },
+          },
+          "ns": 0,
+          "props": Array [
+            Object {
+              "arg": Object {
+                "content": "style",
+                "isStatic": true,
+                "loc": Object {
+                  "end": Object {
+                    "column": 17,
+                    "line": 2,
+                    "offset": 52,
+                  },
+                  "source": "style",
+                  "start": Object {
+                    "column": 12,
+                    "line": 2,
+                    "offset": 47,
+                  },
+                },
+                "type": 4,
+              },
+              "exp": Object {
+                "content": "{ color: 'red' }",
+                "isStatic": false,
+                "loc": Object {
+                  "end": Object {
+                    "column": 36,
+                    "line": 2,
+                    "offset": 71,
+                  },
+                  "source": "\\"{ color: 'red' }\\"",
+                  "start": Object {
+                    "column": 18,
+                    "line": 2,
+                    "offset": 53,
+                  },
+                },
+                "type": 4,
+              },
+              "loc": Object {
+                "end": Object {
+                  "column": 36,
+                  "line": 2,
+                  "offset": 71,
+                },
+                "source": "v-bind:style=\\"{ color: 'red' }\\"",
+                "start": Object {
+                  "column": 5,
+                  "line": 2,
+                  "offset": 40,
+                },
+              },
+              "modifiers": Array [],
+              "name": "bind",
+              "type": 5,
+            },
+          ],
+          "tag": "p",
+          "tagType": 0,
+          "type": 2,
+        },
+        Object {
+          "content": " a comment with <html> inside it ",
+          "loc": Object {
+            "end": Object {
+              "column": 42,
+              "line": 3,
+              "offset": 116,
+            },
+            "source": "<!-- a comment with <html> inside it -->",
+            "start": Object {
+              "column": 2,
+              "line": 3,
+              "offset": 76,
+            },
+          },
+          "type": 1,
+        },
+      ],
+      "isSelfClosing": false,
+      "loc": Object {
+        "end": Object {
+          "column": 7,
+          "line": 4,
+          "offset": 123,
+        },
+        "source": "<div :class=\\"{ some: condition }\\">
+  <p v-bind:style=\\"{ color: 'red' }\\"/>
+  <!-- a comment with <html> inside it -->
+</div>",
+        "start": Object {
+          "column": 1,
+          "line": 1,
+          "offset": 0,
+        },
+      },
+      "ns": 0,
+      "props": Array [
+        Object {
+          "arg": Object {
+            "content": "class",
+            "isStatic": true,
+            "loc": Object {
+              "end": Object {
+                "column": 12,
+                "line": 1,
+                "offset": 11,
+              },
+              "source": "class",
+              "start": Object {
+                "column": 7,
+                "line": 1,
+                "offset": 6,
+              },
+            },
+            "type": 4,
+          },
+          "exp": Object {
+            "content": "{ some: condition }",
+            "isStatic": false,
+            "loc": Object {
+              "end": Object {
+                "column": 34,
+                "line": 1,
+                "offset": 33,
+              },
+              "source": "\\"{ some: condition }\\"",
+              "start": Object {
+                "column": 13,
+                "line": 1,
+                "offset": 12,
+              },
+            },
+            "type": 4,
+          },
+          "loc": Object {
+            "end": Object {
+              "column": 34,
+              "line": 1,
+              "offset": 33,
+            },
+            "source": ":class=\\"{ some: condition }\\"",
+            "start": Object {
+              "column": 6,
+              "line": 1,
+              "offset": 5,
+            },
+          },
+          "modifiers": Array [],
+          "name": "bind",
+          "type": 5,
+        },
+      ],
+      "tag": "div",
+      "tagType": 0,
+      "type": 2,
+    },
+  ],
+  "loc": Object {
+    "end": Object {
+      "column": 7,
+      "line": 4,
+      "offset": 123,
+    },
+    "source": "<div :class=\\"{ some: condition }\\">
+  <p v-bind:style=\\"{ color: 'red' }\\"/>
+  <!-- a comment with <html> inside it -->
+</div>",
+    "start": Object {
+      "column": 1,
+      "line": 1,
+      "offset": 0,
+    },
+  },
+  "type": 6,
+}
+`;
diff --git a/packages/compiler-core/__tests__/parse.spec.ts b/packages/compiler-core/__tests__/parse.spec.ts
new file mode 100644 (file)
index 0000000..8879c45
--- /dev/null
@@ -0,0 +1,2517 @@
+import { parse, ParserOptions } from '../src/parser'
+import {
+  AttributeNode,
+  CommentNode,
+  DirectiveNode,
+  ElementNode,
+  ElementTypes,
+  ExpressionNode,
+  Namespaces,
+  NodeTypes,
+  Position,
+  TextNode
+} from '../src/ast'
+import { ParserErrorTypes } from '../src/errorTypes'
+import { parserOptionsMinimal as parserOptions } from '../src/parserOptionsMinimal'
+
+describe('parser/parse', () => {
+  describe('Text', () => {
+    test('simple text', () => {
+      const ast = parse('some text', parserOptions)
+      const text = ast.children[0] as TextNode
+
+      expect(text).toStrictEqual({
+        type: NodeTypes.TEXT,
+        content: 'some text',
+        isEmpty: false,
+        loc: {
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 9, line: 1, column: 10 },
+          source: 'some text'
+        }
+      })
+    })
+
+    test('simple text with invalid end tag', () => {
+      const ast = parse('some text</div>', {
+        ...parserOptions,
+        onError: () => {}
+      })
+      const text = ast.children[0] as TextNode
+
+      expect(text).toStrictEqual({
+        type: NodeTypes.TEXT,
+        content: 'some text',
+        isEmpty: false,
+        loc: {
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 9, line: 1, column: 10 },
+          source: 'some text'
+        }
+      })
+    })
+
+    test('text with interpolation', () => {
+      const ast = parse('some {{ foo + bar }} text', parserOptions)
+      const text1 = ast.children[0] as TextNode
+      const text2 = ast.children[2] as TextNode
+
+      expect(text1).toStrictEqual({
+        type: NodeTypes.TEXT,
+        content: 'some ',
+        isEmpty: false,
+        loc: {
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 5, line: 1, column: 6 },
+          source: 'some '
+        }
+      })
+      expect(text2).toStrictEqual({
+        type: NodeTypes.TEXT,
+        content: ' text',
+        isEmpty: false,
+        loc: {
+          start: { offset: 20, line: 1, column: 21 },
+          end: { offset: 25, line: 1, column: 26 },
+          source: ' text'
+        }
+      })
+    })
+
+    test('text with interpolation which has `<`', () => {
+      const ast = parse('some {{ a<b && c>d }} text', parserOptions)
+      const text1 = ast.children[0] as TextNode
+      const text2 = ast.children[2] as TextNode
+
+      expect(text1).toStrictEqual({
+        type: NodeTypes.TEXT,
+        content: 'some ',
+        isEmpty: false,
+        loc: {
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 5, line: 1, column: 6 },
+          source: 'some '
+        }
+      })
+      expect(text2).toStrictEqual({
+        type: NodeTypes.TEXT,
+        content: ' text',
+        isEmpty: false,
+        loc: {
+          start: { offset: 21, line: 1, column: 22 },
+          end: { offset: 26, line: 1, column: 27 },
+          source: ' text'
+        }
+      })
+    })
+
+    test('text with mix of tags and interpolations', () => {
+      const ast = parse(
+        'some <span>{{ foo < bar + foo }} text</span>',
+        parserOptions
+      )
+      const text1 = ast.children[0] as TextNode
+      const text2 = (ast.children[1] as ElementNode).children![1] as TextNode
+
+      expect(text1).toStrictEqual({
+        type: NodeTypes.TEXT,
+        content: 'some ',
+        isEmpty: false,
+        loc: {
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 5, line: 1, column: 6 },
+          source: 'some '
+        }
+      })
+      expect(text2).toStrictEqual({
+        type: NodeTypes.TEXT,
+        content: ' text',
+        isEmpty: false,
+        loc: {
+          start: { offset: 32, line: 1, column: 33 },
+          end: { offset: 37, line: 1, column: 38 },
+          source: ' text'
+        }
+      })
+    })
+
+    test('CDATA', () => {
+      const ast = parse('<svg><![CDATA[some text]]></svg>', parserOptions)
+      const text = (ast.children[0] as ElementNode).children![0] as TextNode
+
+      expect(text).toStrictEqual({
+        type: NodeTypes.TEXT,
+        content: 'some text',
+        isEmpty: false,
+        loc: {
+          start: { offset: 14, line: 1, column: 15 },
+          end: { offset: 23, line: 1, column: 24 },
+          source: 'some text'
+        }
+      })
+    })
+
+    test('lonly "<" don\'t separate nodes', () => {
+      const ast = parse('a < b', {
+        ...parserOptions,
+        onError: errorCode => {
+          if (
+            errorCode !== ParserErrorTypes.INVALID_FIRST_CHARACTER_OF_TAG_NAME
+          ) {
+            throw new Error(`${errorCode}`)
+          }
+        }
+      })
+      const text = ast.children[0] as TextNode
+
+      expect(text).toStrictEqual({
+        type: NodeTypes.TEXT,
+        content: 'a < b',
+        isEmpty: false,
+        loc: {
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 5, line: 1, column: 6 },
+          source: 'a < b'
+        }
+      })
+    })
+
+    test('lonly "{{" don\'t separate nodes', () => {
+      const ast = parse('a {{ b', {
+        ...parserOptions,
+        onError: errorCode => {
+          if (errorCode !== ParserErrorTypes.X_MISSING_INTERPOLATION_END) {
+            throw new Error(`${errorCode}`)
+          }
+        }
+      })
+      const text = ast.children[0] as TextNode
+
+      expect(text).toStrictEqual({
+        type: NodeTypes.TEXT,
+        content: 'a {{ b',
+        isEmpty: false,
+        loc: {
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 6, line: 1, column: 7 },
+          source: 'a {{ b'
+        }
+      })
+    })
+
+    test('textarea handles comments/elements as just a text', () => {
+      const ast = parse(
+        '<textarea>some<div>text</div>and<!--comment--></textarea>',
+        parserOptions
+      )
+      const element = ast.children[0] as ElementNode
+      const text = element.children[0] as TextNode
+
+      expect(text).toStrictEqual({
+        type: NodeTypes.TEXT,
+        content: 'some<div>text</div>and<!--comment-->',
+        isEmpty: false,
+        loc: {
+          start: { offset: 10, line: 1, column: 11 },
+          end: { offset: 46, line: 1, column: 47 },
+          source: 'some<div>text</div>and<!--comment-->'
+        }
+      })
+    })
+
+    test('textarea handles character references', () => {
+      const ast = parse('<textarea>&amp;</textarea>', parserOptions)
+      const element = ast.children[0] as ElementNode
+      const text = element.children[0] as TextNode
+
+      expect(text).toStrictEqual({
+        type: NodeTypes.TEXT,
+        content: '&',
+        isEmpty: false,
+        loc: {
+          start: { offset: 10, line: 1, column: 11 },
+          end: { offset: 15, line: 1, column: 16 },
+          source: '&amp;'
+        }
+      })
+    })
+
+    test('style handles comments/elements as just a text', () => {
+      const ast = parse(
+        '<style>some<div>text</div>and<!--comment--></style>',
+        parserOptions
+      )
+      const element = ast.children[0] as ElementNode
+      const text = element.children[0] as TextNode
+
+      expect(text).toStrictEqual({
+        type: NodeTypes.TEXT,
+        content: 'some<div>text</div>and<!--comment-->',
+        isEmpty: false,
+        loc: {
+          start: { offset: 7, line: 1, column: 8 },
+          end: { offset: 43, line: 1, column: 44 },
+          source: 'some<div>text</div>and<!--comment-->'
+        }
+      })
+    })
+
+    test("style doesn't handle character references", () => {
+      const ast = parse('<style>&amp;</style>', parserOptions)
+      const element = ast.children[0] as ElementNode
+      const text = element.children[0] as TextNode
+
+      expect(text).toStrictEqual({
+        type: NodeTypes.TEXT,
+        content: '&amp;',
+        isEmpty: false,
+        loc: {
+          start: { offset: 7, line: 1, column: 8 },
+          end: { offset: 12, line: 1, column: 13 },
+          source: '&amp;'
+        }
+      })
+    })
+
+    test('HTML entities compatibility in text (https://html.spec.whatwg.org/multipage/parsing.html#named-character-reference-state).', () => {
+      const spy = jest.fn()
+      const ast = parse('&ampersand;', {
+        ...parserOptions,
+        namedCharacterReferences: { amp: '&' },
+        onError: spy
+      })
+      const text = ast.children[0] as TextNode
+
+      expect(text).toStrictEqual({
+        type: NodeTypes.TEXT,
+        content: '&ersand;',
+        isEmpty: false,
+        loc: {
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 11, line: 1, column: 12 },
+          source: '&ampersand;'
+        }
+      })
+      expect(spy.mock.calls).toEqual([
+        [
+          ParserErrorTypes.MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE,
+          { offset: 4, line: 1, column: 5 }
+        ]
+      ])
+    })
+
+    test('HTML entities compatibility in attribute (https://html.spec.whatwg.org/multipage/parsing.html#named-character-reference-state).', () => {
+      const spy = jest.fn()
+      const ast = parse(
+        '<div a="&ampersand;" b="&amp;ersand;" c="&amp!"></div>',
+        {
+          ...parserOptions,
+          namedCharacterReferences: { amp: '&', 'amp;': '&' },
+          onError: spy
+        }
+      )
+      const element = ast.children[0] as ElementNode
+      const text1 = (element.props[0] as AttributeNode).value
+      const text2 = (element.props[1] as AttributeNode).value
+      const text3 = (element.props[2] as AttributeNode).value
+
+      expect(text1).toStrictEqual({
+        type: NodeTypes.TEXT,
+        content: '&ampersand;',
+        isEmpty: false,
+        loc: {
+          start: { offset: 7, line: 1, column: 8 },
+          end: { offset: 20, line: 1, column: 21 },
+          source: '"&ampersand;"'
+        }
+      })
+      expect(text2).toStrictEqual({
+        type: NodeTypes.TEXT,
+        content: '&ersand;',
+        isEmpty: false,
+        loc: {
+          start: { offset: 23, line: 1, column: 24 },
+          end: { offset: 37, line: 1, column: 38 },
+          source: '"&amp;ersand;"'
+        }
+      })
+      expect(text3).toStrictEqual({
+        type: NodeTypes.TEXT,
+        content: '&!',
+        isEmpty: false,
+        loc: {
+          start: { offset: 40, line: 1, column: 41 },
+          end: { offset: 47, line: 1, column: 48 },
+          source: '"&amp!"'
+        }
+      })
+      expect(spy.mock.calls).toEqual([
+        [
+          ParserErrorTypes.MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE,
+          { offset: 45, line: 1, column: 46 }
+        ]
+      ])
+    })
+
+    test('Some control character reference should be replaced.', () => {
+      const spy = jest.fn()
+      const ast = parse('&#x86;', { ...parserOptions, onError: spy })
+      const text = ast.children[0] as TextNode
+
+      expect(text).toStrictEqual({
+        type: NodeTypes.TEXT,
+        content: '†',
+        isEmpty: false,
+        loc: {
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 6, line: 1, column: 7 },
+          source: '&#x86;'
+        }
+      })
+      expect(spy.mock.calls).toEqual([
+        [
+          ParserErrorTypes.CONTROL_CHARACTER_REFERENCE,
+          { offset: 0, line: 1, column: 1 }
+        ]
+      ])
+    })
+  })
+
+  describe('Interpolation', () => {
+    test('simple interpolation', () => {
+      const ast = parse('{{message}}', parserOptions)
+      const interpolation = ast.children[0] as ExpressionNode
+
+      expect(interpolation).toStrictEqual({
+        type: NodeTypes.EXPRESSION,
+        content: 'message',
+        isStatic: false,
+        loc: {
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 11, line: 1, column: 12 },
+          source: '{{message}}'
+        }
+      })
+    })
+
+    test('it can have tag-like notation', () => {
+      const ast = parse('{{ a<b }}', parserOptions)
+      const interpolation = ast.children[0] as ExpressionNode
+
+      expect(interpolation).toStrictEqual({
+        type: NodeTypes.EXPRESSION,
+        content: 'a<b',
+        isStatic: false,
+        loc: {
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 9, line: 1, column: 10 },
+          source: '{{ a<b }}'
+        }
+      })
+    })
+
+    test('it can have tag-like notation (2)', () => {
+      const ast = parse('{{ a<b }}{{ c>d }}', parserOptions)
+      const interpolation1 = ast.children[0] as ExpressionNode
+      const interpolation2 = ast.children[1] as ExpressionNode
+
+      expect(interpolation1).toStrictEqual({
+        type: NodeTypes.EXPRESSION,
+        content: 'a<b',
+        isStatic: false,
+        loc: {
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 9, line: 1, column: 10 },
+          source: '{{ a<b }}'
+        }
+      })
+      expect(interpolation2).toStrictEqual({
+        type: NodeTypes.EXPRESSION,
+        content: 'c>d',
+        isStatic: false,
+        loc: {
+          start: { offset: 9, line: 1, column: 10 },
+          end: { offset: 18, line: 1, column: 19 },
+          source: '{{ c>d }}'
+        }
+      })
+    })
+
+    test('it can have tag-like notation (3)', () => {
+      const ast = parse('<div>{{ "</div>" }}</div>', parserOptions)
+      const element = ast.children[0] as ElementNode
+      const interpolation = element.children[0] as ExpressionNode
+
+      expect(interpolation).toStrictEqual({
+        type: NodeTypes.EXPRESSION,
+        content: '"</div>"',
+        isStatic: false,
+        loc: {
+          start: { offset: 5, line: 1, column: 6 },
+          end: { offset: 19, line: 1, column: 20 },
+          source: '{{ "</div>" }}'
+        }
+      })
+    })
+
+    test('HTML entities in interpolation should be translated for backward compatibility.', () => {
+      const ast = parse('<div>{{ a &lt; b }}</div>', parserOptions)
+      const element = ast.children[0] as ElementNode
+      const interpolation = element.children[0] as ExpressionNode
+
+      expect(interpolation).toStrictEqual({
+        type: NodeTypes.EXPRESSION,
+        content: 'a < b',
+        isStatic: false,
+        loc: {
+          start: { offset: 5, line: 1, column: 6 },
+          end: { offset: 19, line: 1, column: 20 },
+          source: '{{ a &lt; b }}'
+        }
+      })
+    })
+  })
+
+  describe('Comment', () => {
+    test('empty comment', () => {
+      const ast = parse('<!---->', parserOptions)
+      const comment = ast.children[0] as CommentNode
+
+      expect(comment).toStrictEqual({
+        type: NodeTypes.COMMENT,
+        content: '',
+        loc: {
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 7, line: 1, column: 8 },
+          source: '<!---->'
+        }
+      })
+    })
+
+    test('simple comment', () => {
+      const ast = parse('<!--abc-->', parserOptions)
+      const comment = ast.children[0] as CommentNode
+
+      expect(comment).toStrictEqual({
+        type: NodeTypes.COMMENT,
+        content: 'abc',
+        loc: {
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 10, line: 1, column: 11 },
+          source: '<!--abc-->'
+        }
+      })
+    })
+
+    test('two comments', () => {
+      const ast = parse('<!--abc--><!--def-->', parserOptions)
+      const comment1 = ast.children[0] as CommentNode
+      const comment2 = ast.children[1] as CommentNode
+
+      expect(comment1).toStrictEqual({
+        type: NodeTypes.COMMENT,
+        content: 'abc',
+        loc: {
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 10, line: 1, column: 11 },
+          source: '<!--abc-->'
+        }
+      })
+      expect(comment2).toStrictEqual({
+        type: NodeTypes.COMMENT,
+        content: 'def',
+        loc: {
+          start: { offset: 10, line: 1, column: 11 },
+          end: { offset: 20, line: 1, column: 21 },
+          source: '<!--def-->'
+        }
+      })
+    })
+  })
+
+  describe('Element', () => {
+    test('simple div', () => {
+      const ast = parse('<div>hello</div>', parserOptions)
+      const element = ast.children[0] as ElementNode
+
+      expect(element).toStrictEqual({
+        type: NodeTypes.ELEMENT,
+        ns: Namespaces.HTML,
+        tag: 'div',
+        tagType: ElementTypes.ELEMENT,
+        props: [],
+        isSelfClosing: false,
+        children: [
+          {
+            type: NodeTypes.TEXT,
+            content: 'hello',
+            isEmpty: false,
+            loc: {
+              start: { offset: 5, line: 1, column: 6 },
+              end: { offset: 10, line: 1, column: 11 },
+              source: 'hello'
+            }
+          }
+        ],
+        loc: {
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 16, line: 1, column: 17 },
+          source: '<div>hello</div>'
+        }
+      })
+    })
+
+    test('empty', () => {
+      const ast = parse('<div></div>', parserOptions)
+      const element = ast.children[0] as ElementNode
+
+      expect(element).toStrictEqual({
+        type: NodeTypes.ELEMENT,
+        ns: Namespaces.HTML,
+        tag: 'div',
+        tagType: ElementTypes.ELEMENT,
+        props: [],
+        isSelfClosing: false,
+        children: [],
+        loc: {
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 11, line: 1, column: 12 },
+          source: '<div></div>'
+        }
+      })
+    })
+
+    test('self closing', () => {
+      const ast = parse('<div/>after', parserOptions)
+      const element = ast.children[0] as ElementNode
+
+      expect(element).toStrictEqual({
+        type: NodeTypes.ELEMENT,
+        ns: Namespaces.HTML,
+        tag: 'div',
+        tagType: ElementTypes.ELEMENT,
+        props: [],
+        isSelfClosing: true,
+        children: [],
+        loc: {
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 6, line: 1, column: 7 },
+          source: '<div/>'
+        }
+      })
+    })
+
+    test('void element', () => {
+      const ast = parse('<img>after', parserOptions)
+      const element = ast.children[0] as ElementNode
+
+      expect(element).toStrictEqual({
+        type: NodeTypes.ELEMENT,
+        ns: Namespaces.HTML,
+        tag: 'img',
+        tagType: ElementTypes.ELEMENT,
+        props: [],
+        isSelfClosing: false,
+        children: [],
+        loc: {
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 5, line: 1, column: 6 },
+          source: '<img>'
+        }
+      })
+    })
+
+    test('attribute with no value', () => {
+      const ast = parse('<div id></div>', parserOptions)
+      const element = ast.children[0] as ElementNode
+
+      expect(element).toStrictEqual({
+        type: NodeTypes.ELEMENT,
+        ns: Namespaces.HTML,
+        tag: 'div',
+        tagType: ElementTypes.ELEMENT,
+        props: [
+          {
+            type: NodeTypes.ATTRIBUTE,
+            name: 'id',
+            value: undefined,
+            loc: {
+              start: { offset: 5, line: 1, column: 6 },
+              end: { offset: 7, line: 1, column: 8 },
+              source: 'id'
+            }
+          }
+        ],
+        isSelfClosing: false,
+        children: [],
+        loc: {
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 14, line: 1, column: 15 },
+          source: '<div id></div>'
+        }
+      })
+    })
+
+    test('attribute with empty value, double quote', () => {
+      const ast = parse('<div id=""></div>', parserOptions)
+      const element = ast.children[0] as ElementNode
+
+      expect(element).toStrictEqual({
+        type: NodeTypes.ELEMENT,
+        ns: Namespaces.HTML,
+        tag: 'div',
+        tagType: ElementTypes.ELEMENT,
+        props: [
+          {
+            type: NodeTypes.ATTRIBUTE,
+            name: 'id',
+            value: {
+              type: NodeTypes.TEXT,
+              content: '',
+              isEmpty: true,
+              loc: {
+                start: { offset: 8, line: 1, column: 9 },
+                end: { offset: 10, line: 1, column: 11 },
+                source: '""'
+              }
+            },
+            loc: {
+              start: { offset: 5, line: 1, column: 6 },
+              end: { offset: 10, line: 1, column: 11 },
+              source: 'id=""'
+            }
+          }
+        ],
+        isSelfClosing: false,
+        children: [],
+        loc: {
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 17, line: 1, column: 18 },
+          source: '<div id=""></div>'
+        }
+      })
+    })
+
+    test('attribute with empty value, single quote', () => {
+      const ast = parse("<div id=''></div>", parserOptions)
+      const element = ast.children[0] as ElementNode
+
+      expect(element).toStrictEqual({
+        type: NodeTypes.ELEMENT,
+        ns: Namespaces.HTML,
+        tag: 'div',
+        tagType: ElementTypes.ELEMENT,
+        props: [
+          {
+            type: NodeTypes.ATTRIBUTE,
+            name: 'id',
+            value: {
+              type: NodeTypes.TEXT,
+              content: '',
+              isEmpty: true,
+              loc: {
+                start: { offset: 8, line: 1, column: 9 },
+                end: { offset: 10, line: 1, column: 11 },
+                source: "''"
+              }
+            },
+            loc: {
+              start: { offset: 5, line: 1, column: 6 },
+              end: { offset: 10, line: 1, column: 11 },
+              source: "id=''"
+            }
+          }
+        ],
+        isSelfClosing: false,
+        children: [],
+        loc: {
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 17, line: 1, column: 18 },
+          source: "<div id=''></div>"
+        }
+      })
+    })
+
+    test('attribute with value, double quote', () => {
+      const ast = parse('<div id=">\'"></div>', parserOptions)
+      const element = ast.children[0] as ElementNode
+
+      expect(element).toStrictEqual({
+        type: NodeTypes.ELEMENT,
+        ns: Namespaces.HTML,
+        tag: 'div',
+        tagType: ElementTypes.ELEMENT,
+        props: [
+          {
+            type: NodeTypes.ATTRIBUTE,
+            name: 'id',
+            value: {
+              type: NodeTypes.TEXT,
+              content: ">'",
+              isEmpty: false,
+              loc: {
+                start: { offset: 8, line: 1, column: 9 },
+                end: { offset: 12, line: 1, column: 13 },
+                source: '">\'"'
+              }
+            },
+            loc: {
+              start: { offset: 5, line: 1, column: 6 },
+              end: { offset: 12, line: 1, column: 13 },
+              source: 'id=">\'"'
+            }
+          }
+        ],
+        isSelfClosing: false,
+        children: [],
+        loc: {
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 19, line: 1, column: 20 },
+          source: '<div id=">\'"></div>'
+        }
+      })
+    })
+
+    test('attribute with value, single quote', () => {
+      const ast = parse("<div id='>\"'></div>", parserOptions)
+      const element = ast.children[0] as ElementNode
+
+      expect(element).toStrictEqual({
+        type: NodeTypes.ELEMENT,
+        ns: Namespaces.HTML,
+        tag: 'div',
+        tagType: ElementTypes.ELEMENT,
+        props: [
+          {
+            type: NodeTypes.ATTRIBUTE,
+            name: 'id',
+            value: {
+              type: NodeTypes.TEXT,
+              content: '>"',
+              isEmpty: false,
+              loc: {
+                start: { offset: 8, line: 1, column: 9 },
+                end: { offset: 12, line: 1, column: 13 },
+                source: "'>\"'"
+              }
+            },
+            loc: {
+              start: { offset: 5, line: 1, column: 6 },
+              end: { offset: 12, line: 1, column: 13 },
+              source: "id='>\"'"
+            }
+          }
+        ],
+        isSelfClosing: false,
+        children: [],
+        loc: {
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 19, line: 1, column: 20 },
+          source: "<div id='>\"'></div>"
+        }
+      })
+    })
+
+    test('attribute with value, unquoted', () => {
+      const ast = parse('<div id=a/></div>', parserOptions)
+      const element = ast.children[0] as ElementNode
+
+      expect(element).toStrictEqual({
+        type: NodeTypes.ELEMENT,
+        ns: Namespaces.HTML,
+        tag: 'div',
+        tagType: ElementTypes.ELEMENT,
+        props: [
+          {
+            type: NodeTypes.ATTRIBUTE,
+            name: 'id',
+            value: {
+              type: NodeTypes.TEXT,
+              content: 'a/',
+              isEmpty: false,
+              loc: {
+                start: { offset: 8, line: 1, column: 9 },
+                end: { offset: 10, line: 1, column: 11 },
+                source: 'a/'
+              }
+            },
+            loc: {
+              start: { offset: 5, line: 1, column: 6 },
+              end: { offset: 10, line: 1, column: 11 },
+              source: 'id=a/'
+            }
+          }
+        ],
+        isSelfClosing: false,
+        children: [],
+        loc: {
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 17, line: 1, column: 18 },
+          source: '<div id=a/></div>'
+        }
+      })
+    })
+
+    test('multiple attributes', () => {
+      const ast = parse(
+        '<div id=a class="c" inert style=\'\'></div>',
+        parserOptions
+      )
+      const element = ast.children[0] as ElementNode
+
+      expect(element).toStrictEqual({
+        type: NodeTypes.ELEMENT,
+        ns: Namespaces.HTML,
+        tag: 'div',
+        tagType: ElementTypes.ELEMENT,
+        props: [
+          {
+            type: NodeTypes.ATTRIBUTE,
+            name: 'id',
+            value: {
+              type: NodeTypes.TEXT,
+              content: 'a',
+              isEmpty: false,
+              loc: {
+                start: { offset: 8, line: 1, column: 9 },
+                end: { offset: 9, line: 1, column: 10 },
+                source: 'a'
+              }
+            },
+            loc: {
+              start: { offset: 5, line: 1, column: 6 },
+              end: { offset: 9, line: 1, column: 10 },
+              source: 'id=a'
+            }
+          },
+          {
+            type: NodeTypes.ATTRIBUTE,
+            name: 'class',
+            value: {
+              type: NodeTypes.TEXT,
+              content: 'c',
+              isEmpty: false,
+              loc: {
+                start: { offset: 16, line: 1, column: 17 },
+                end: { offset: 19, line: 1, column: 20 },
+                source: '"c"'
+              }
+            },
+            loc: {
+              start: { offset: 10, line: 1, column: 11 },
+              end: { offset: 19, line: 1, column: 20 },
+              source: 'class="c"'
+            }
+          },
+          {
+            type: NodeTypes.ATTRIBUTE,
+            name: 'inert',
+            value: undefined,
+            loc: {
+              start: { offset: 20, line: 1, column: 21 },
+              end: { offset: 25, line: 1, column: 26 },
+              source: 'inert'
+            }
+          },
+          {
+            type: NodeTypes.ATTRIBUTE,
+            name: 'style',
+            value: {
+              type: NodeTypes.TEXT,
+              content: '',
+              isEmpty: true,
+              loc: {
+                start: { offset: 32, line: 1, column: 33 },
+                end: { offset: 34, line: 1, column: 35 },
+                source: "''"
+              }
+            },
+            loc: {
+              start: { offset: 26, line: 1, column: 27 },
+              end: { offset: 34, line: 1, column: 35 },
+              source: "style=''"
+            }
+          }
+        ],
+        isSelfClosing: false,
+        children: [],
+        loc: {
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 41, line: 1, column: 42 },
+          source: '<div id=a class="c" inert style=\'\'></div>'
+        }
+      })
+    })
+
+    test('directive with no value', () => {
+      const ast = parse('<div v-if/>', parserOptions)
+      const directive = (ast.children[0] as ElementNode)
+        .props[0] as DirectiveNode
+
+      expect(directive).toStrictEqual({
+        type: NodeTypes.DIRECTIVE,
+        name: 'if',
+        arg: undefined,
+        modifiers: [],
+        exp: undefined,
+        loc: {
+          start: { offset: 5, line: 1, column: 6 },
+          end: { offset: 9, line: 1, column: 10 },
+          source: 'v-if'
+        }
+      })
+    })
+
+    test('directive with value', () => {
+      const ast = parse('<div v-if="a"/>', parserOptions)
+      const directive = (ast.children[0] as ElementNode)
+        .props[0] as DirectiveNode
+
+      expect(directive).toStrictEqual({
+        type: NodeTypes.DIRECTIVE,
+        name: 'if',
+        arg: undefined,
+        modifiers: [],
+        exp: {
+          type: NodeTypes.EXPRESSION,
+          content: 'a',
+          isStatic: false,
+          loc: {
+            start: { offset: 10, line: 1, column: 11 },
+            end: { offset: 13, line: 1, column: 14 },
+            source: '"a"'
+          }
+        },
+        loc: {
+          start: { offset: 5, line: 1, column: 6 },
+          end: { offset: 13, line: 1, column: 14 },
+          source: 'v-if="a"'
+        }
+      })
+    })
+
+    test('directive with argument', () => {
+      const ast = parse('<div v-on:click/>', parserOptions)
+      const directive = (ast.children[0] as ElementNode)
+        .props[0] as DirectiveNode
+
+      expect(directive).toStrictEqual({
+        type: NodeTypes.DIRECTIVE,
+        name: 'on',
+        arg: {
+          type: 4,
+          content: 'click',
+          isStatic: true,
+          loc: {
+            source: 'click',
+            start: {
+              column: 11,
+              line: 1,
+              offset: 10
+            },
+            end: {
+              column: 16,
+              line: 1,
+              offset: 15
+            }
+          }
+        },
+        modifiers: [],
+        exp: undefined,
+        loc: {
+          start: { offset: 5, line: 1, column: 6 },
+          end: { offset: 15, line: 1, column: 16 },
+          source: 'v-on:click'
+        }
+      })
+    })
+
+    test('directive with a modifier', () => {
+      const ast = parse('<div v-on.enter/>', parserOptions)
+      const directive = (ast.children[0] as ElementNode)
+        .props[0] as DirectiveNode
+
+      expect(directive).toStrictEqual({
+        type: NodeTypes.DIRECTIVE,
+        name: 'on',
+        arg: undefined,
+        modifiers: ['enter'],
+        exp: undefined,
+        loc: {
+          start: { offset: 5, line: 1, column: 6 },
+          end: { offset: 15, line: 1, column: 16 },
+          source: 'v-on.enter'
+        }
+      })
+    })
+
+    test('directive with two modifiers', () => {
+      const ast = parse('<div v-on.enter.exact/>', parserOptions)
+      const directive = (ast.children[0] as ElementNode)
+        .props[0] as DirectiveNode
+
+      expect(directive).toStrictEqual({
+        type: NodeTypes.DIRECTIVE,
+        name: 'on',
+        arg: undefined,
+        modifiers: ['enter', 'exact'],
+        exp: undefined,
+        loc: {
+          start: { offset: 5, line: 1, column: 6 },
+          end: { offset: 21, line: 1, column: 22 },
+          source: 'v-on.enter.exact'
+        }
+      })
+    })
+
+    test('directive with argument and modifiers', () => {
+      const ast = parse('<div v-on:click.enter.exact/>', parserOptions)
+      const directive = (ast.children[0] as ElementNode)
+        .props[0] as DirectiveNode
+
+      expect(directive).toStrictEqual({
+        type: NodeTypes.DIRECTIVE,
+        name: 'on',
+        arg: {
+          type: 4,
+          content: 'click',
+          isStatic: true,
+          loc: {
+            source: 'click',
+            start: {
+              column: 11,
+              line: 1,
+              offset: 10
+            },
+            end: {
+              column: 16,
+              line: 1,
+              offset: 15
+            }
+          }
+        },
+        modifiers: ['enter', 'exact'],
+        exp: undefined,
+        loc: {
+          start: { offset: 5, line: 1, column: 6 },
+          end: { offset: 27, line: 1, column: 28 },
+          source: 'v-on:click.enter.exact'
+        }
+      })
+    })
+
+    test('v-bind shorthand', () => {
+      const ast = parse('<div :a=b />', parserOptions)
+      const directive = (ast.children[0] as ElementNode)
+        .props[0] as DirectiveNode
+
+      expect(directive).toStrictEqual({
+        type: NodeTypes.DIRECTIVE,
+        name: 'bind',
+        arg: {
+          type: 4,
+          content: 'a',
+          isStatic: true,
+          loc: {
+            source: 'a',
+            start: {
+              column: 7,
+              line: 1,
+              offset: 6
+            },
+            end: {
+              column: 8,
+              line: 1,
+              offset: 7
+            }
+          }
+        },
+        modifiers: [],
+        exp: {
+          type: NodeTypes.EXPRESSION,
+          content: 'b',
+          isStatic: false,
+          loc: {
+            start: { offset: 8, line: 1, column: 9 },
+            end: { offset: 9, line: 1, column: 10 },
+            source: 'b'
+          }
+        },
+        loc: {
+          start: { offset: 5, line: 1, column: 6 },
+          end: { offset: 9, line: 1, column: 10 },
+          source: ':a=b'
+        }
+      })
+    })
+
+    test('v-bind shorthand with modifier', () => {
+      const ast = parse('<div :a.sync=b />', parserOptions)
+      const directive = (ast.children[0] as ElementNode)
+        .props[0] as DirectiveNode
+
+      expect(directive).toStrictEqual({
+        type: NodeTypes.DIRECTIVE,
+        name: 'bind',
+        arg: {
+          type: 4,
+          content: 'a',
+          isStatic: true,
+          loc: {
+            source: 'a',
+            start: {
+              column: 7,
+              line: 1,
+              offset: 6
+            },
+            end: {
+              column: 8,
+              line: 1,
+              offset: 7
+            }
+          }
+        },
+        modifiers: ['sync'],
+        exp: {
+          type: NodeTypes.EXPRESSION,
+          content: 'b',
+          isStatic: false,
+          loc: {
+            start: { offset: 13, line: 1, column: 14 },
+            end: { offset: 14, line: 1, column: 15 },
+            source: 'b'
+          }
+        },
+        loc: {
+          start: { offset: 5, line: 1, column: 6 },
+          end: { offset: 14, line: 1, column: 15 },
+          source: ':a.sync=b'
+        }
+      })
+    })
+
+    test('v-on shorthand', () => {
+      const ast = parse('<div @a=b />', parserOptions)
+      const directive = (ast.children[0] as ElementNode)
+        .props[0] as DirectiveNode
+
+      expect(directive).toStrictEqual({
+        type: NodeTypes.DIRECTIVE,
+        name: 'on',
+        arg: {
+          type: 4,
+          content: 'a',
+          isStatic: true,
+          loc: {
+            source: 'a',
+            start: {
+              column: 7,
+              line: 1,
+              offset: 6
+            },
+            end: {
+              column: 8,
+              line: 1,
+              offset: 7
+            }
+          }
+        },
+        modifiers: [],
+        exp: {
+          type: NodeTypes.EXPRESSION,
+          content: 'b',
+          isStatic: false,
+          loc: {
+            start: { offset: 8, line: 1, column: 9 },
+            end: { offset: 9, line: 1, column: 10 },
+            source: 'b'
+          }
+        },
+        loc: {
+          start: { offset: 5, line: 1, column: 6 },
+          end: { offset: 9, line: 1, column: 10 },
+          source: '@a=b'
+        }
+      })
+    })
+
+    test('v-on shorthand with modifier', () => {
+      const ast = parse('<div @a.enter=b />', parserOptions)
+      const directive = (ast.children[0] as ElementNode)
+        .props[0] as DirectiveNode
+
+      expect(directive).toStrictEqual({
+        type: NodeTypes.DIRECTIVE,
+        name: 'on',
+        arg: {
+          type: 4,
+          content: 'a',
+          isStatic: true,
+          loc: {
+            source: 'a',
+            start: {
+              column: 7,
+              line: 1,
+              offset: 6
+            },
+            end: {
+              column: 8,
+              line: 1,
+              offset: 7
+            }
+          }
+        },
+        modifiers: ['enter'],
+        exp: {
+          type: NodeTypes.EXPRESSION,
+          content: 'b',
+          isStatic: false,
+          loc: {
+            start: { offset: 14, line: 1, column: 15 },
+            end: { offset: 15, line: 1, column: 16 },
+            source: 'b'
+          }
+        },
+        loc: {
+          start: { offset: 5, line: 1, column: 6 },
+          end: { offset: 15, line: 1, column: 16 },
+          source: '@a.enter=b'
+        }
+      })
+    })
+
+    test('end tags are case-insensitive.', () => {
+      const ast = parse('<div>hello</DIV>after', parserOptions)
+      const element = ast.children[0] as ElementNode
+      const text = element.children[0] as TextNode
+
+      expect(text).toStrictEqual({
+        type: NodeTypes.TEXT,
+        content: 'hello',
+        isEmpty: false,
+        loc: {
+          start: { offset: 5, line: 1, column: 6 },
+          end: { offset: 10, line: 1, column: 11 },
+          source: 'hello'
+        }
+      })
+    })
+
+    test('Strict end tag detection.', () => {
+      const ast = parse(
+        '<textarea>hello</textarea</textarea0></texTArea a="<>">',
+        {
+          ...parserOptions,
+          onError: type => {
+            if (type !== ParserErrorTypes.END_TAG_WITH_ATTRIBUTES) {
+              throw new Error(String(type))
+            }
+          }
+        }
+      )
+      const element = ast.children[0] as ElementNode
+      const text = element.children[0] as TextNode
+
+      expect(ast.children.length).toBe(1)
+      expect(text).toStrictEqual({
+        type: NodeTypes.TEXT,
+        content: 'hello</textarea</textarea0>',
+        isEmpty: false,
+        loc: {
+          start: { offset: 10, line: 1, column: 11 },
+          end: { offset: 37, line: 1, column: 38 },
+          source: 'hello</textarea</textarea0>'
+        }
+      })
+    })
+  })
+
+  test('self closing single tag', () => {
+    const ast = parse('<div :class="{ some: condition }" />', parserOptions)
+
+    expect(ast.children).toHaveLength(1)
+    expect(ast.children[0]).toMatchObject({ tag: 'div' })
+  })
+
+  test('self closing multiple tag', () => {
+    const ast = parse(
+      `<div :class="{ some: condition }" />\n` +
+        `<p v-bind:style="{ color: 'red' }"/>`,
+      parserOptions
+    )
+
+    expect(ast).toMatchSnapshot()
+
+    expect(ast.children).toHaveLength(2)
+    expect(ast.children[0]).toMatchObject({ tag: 'div' })
+    expect(ast.children[1]).toMatchObject({ tag: 'p' })
+  })
+
+  test('valid html', () => {
+    const ast = parse(
+      `<div :class="{ some: condition }">\n` +
+        `  <p v-bind:style="{ color: 'red' }"/>\n` +
+        `  <!-- a comment with <html> inside it -->\n` +
+        `</div>`,
+      parserOptions
+    )
+
+    expect(ast).toMatchSnapshot()
+
+    expect(ast.children).toHaveLength(1)
+    const el = ast.children[0] as any
+    expect(el).toMatchObject({
+      tag: 'div'
+    })
+    expect(el.children).toHaveLength(2)
+    expect(el.children[0]).toMatchObject({
+      tag: 'p'
+    })
+    expect(el.children[1]).toMatchObject({
+      type: NodeTypes.COMMENT
+    })
+  })
+
+  test('invalid html', () => {
+    expect(() => {
+      parse(`<div>\n<span>\n</div>\n</span>`, parserOptions)
+    }).toThrow('End tag was not found. (3:1)')
+
+    const spy = jest.fn()
+    const ast = parse(`<div>\n<span>\n</div>\n</span>`, {
+      ...parserOptions,
+      onError: spy
+    })
+
+    expect(spy).toBeCalledWith(ParserErrorTypes.X_MISSING_END_TAG, {
+      offset: 13,
+      line: 3,
+      column: 1
+    })
+    expect(spy).toBeCalledWith(ParserErrorTypes.X_INVALID_END_TAG, {
+      offset: 20,
+      line: 4,
+      column: 1
+    })
+    expect(ast).toMatchSnapshot()
+  })
+
+  test('parse with correct location info', () => {
+    const [foo, bar, but, baz] = parse(
+      'foo \n is {{ bar }} but {{ baz }}',
+      parserOptions
+    ).children
+
+    let offset = 0
+    expect(foo.loc.start).toEqual({ line: 1, column: 1, offset })
+    offset += foo.loc.source.length
+    expect(foo.loc.end).toEqual({ line: 2, column: 4, offset })
+
+    expect(bar.loc.start).toEqual({ line: 2, column: 4, offset })
+    offset += bar.loc.source.length
+    expect(bar.loc.end).toEqual({ line: 2, column: 13, offset })
+
+    expect(but.loc.start).toEqual({ line: 2, column: 13, offset })
+    offset += but.loc.source.length
+    expect(but.loc.end).toEqual({ line: 2, column: 18, offset })
+
+    expect(baz.loc.start).toEqual({ line: 2, column: 18, offset })
+    offset += baz.loc.source.length
+    expect(baz.loc.end).toEqual({ line: 2, column: 27, offset })
+  })
+
+  describe('namedCharacterReferences option', () => {
+    test('use the given map', () => {
+      const ast: any = parse('&amp;&cups;', {
+        ...parserOptions,
+        namedCharacterReferences: {
+          'cups;': '\u222A\uFE00' // UNION with serifs
+        },
+        onError: () => {} // Ignore errors
+      })
+
+      expect(ast.children.length).toBe(1)
+      expect(ast.children[0].type).toBe(NodeTypes.TEXT)
+      expect(ast.children[0].content).toBe('&amp;\u222A\uFE00')
+    })
+  })
+
+  describe('onError option', () => {
+    const patterns: {
+      [key: string]: Array<{
+        code: string
+        errors: Array<{ type: ParserErrorTypes; loc: Position }>
+        options?: Partial<ParserOptions>
+      }>
+    } = {
+      ABRUPT_CLOSING_OF_EMPTY_COMMENT: [
+        {
+          code: '<template><!--></template>',
+          errors: [
+            {
+              type: ParserErrorTypes.ABRUPT_CLOSING_OF_EMPTY_COMMENT,
+              loc: { offset: 10, line: 1, column: 11 }
+            }
+          ]
+        },
+        {
+          code: '<template><!---></template>',
+          errors: [
+            {
+              type: ParserErrorTypes.ABRUPT_CLOSING_OF_EMPTY_COMMENT,
+              loc: { offset: 10, line: 1, column: 11 }
+            }
+          ]
+        },
+        {
+          code: '<template><!----></template>',
+          errors: []
+        }
+      ],
+      ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE: [
+        {
+          code: '<template>&#a;</template>',
+          errors: [
+            {
+              type:
+                ParserErrorTypes.ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE,
+              loc: { offset: 10, line: 1, column: 11 }
+            }
+          ]
+        },
+        {
+          code: '<template>&#xg;</template>',
+          errors: [
+            {
+              type:
+                ParserErrorTypes.ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE,
+              loc: { offset: 10, line: 1, column: 11 }
+            }
+          ]
+        },
+        {
+          code: '<template>&#99;</template>',
+          errors: []
+        },
+        {
+          code: '<template>&#xff;</template>',
+          errors: []
+        },
+        {
+          code: '<template attr="&#a;"></template>',
+          errors: [
+            {
+              type:
+                ParserErrorTypes.ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE,
+              loc: { offset: 16, line: 1, column: 17 }
+            }
+          ]
+        },
+        {
+          code: '<template attr="&#xg;"></template>',
+          errors: [
+            {
+              type:
+                ParserErrorTypes.ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE,
+              loc: { offset: 16, line: 1, column: 17 }
+            }
+          ]
+        },
+        {
+          code: '<template attr="&#99;"></template>',
+          errors: []
+        },
+        {
+          code: '<template attr="&#xff;"></template>',
+          errors: []
+        }
+      ],
+      CDATA_IN_HTML_CONTENT: [
+        {
+          code: '<template><![CDATA[cdata]]></template>',
+          errors: [
+            {
+              type: ParserErrorTypes.CDATA_IN_HTML_CONTENT,
+              loc: { offset: 10, line: 1, column: 11 }
+            }
+          ]
+        },
+        {
+          code: '<template><svg><![CDATA[cdata]]></svg></template>',
+          errors: []
+        }
+      ],
+      CHARACTER_REFERENCE_OUTSIDE_UNICODE_RANGE: [
+        {
+          code: '<template>&#1234567;</template>',
+          errors: [
+            {
+              type: ParserErrorTypes.CHARACTER_REFERENCE_OUTSIDE_UNICODE_RANGE,
+              loc: { offset: 10, line: 1, column: 11 }
+            }
+          ]
+        }
+      ],
+      CONTROL_CHARACTER_REFERENCE: [
+        {
+          code: '<template>&#0003;</template>',
+          errors: [
+            {
+              type: ParserErrorTypes.CONTROL_CHARACTER_REFERENCE,
+              loc: { offset: 10, line: 1, column: 11 }
+            }
+          ]
+        },
+        {
+          code: '<template>&#x7F;</template>',
+          errors: [
+            {
+              type: ParserErrorTypes.CONTROL_CHARACTER_REFERENCE,
+              loc: { offset: 10, line: 1, column: 11 }
+            }
+          ]
+        }
+      ],
+      DUPLICATE_ATTRIBUTE: [
+        {
+          code: '<template><div id="" id=""></div></template>',
+          errors: [
+            {
+              type: ParserErrorTypes.DUPLICATE_ATTRIBUTE,
+              loc: { offset: 21, line: 1, column: 22 }
+            }
+          ]
+        }
+      ],
+      END_TAG_WITH_ATTRIBUTES: [
+        {
+          code: '<template><div></div id=""></template>',
+          errors: [
+            {
+              type: ParserErrorTypes.END_TAG_WITH_ATTRIBUTES,
+              loc: { offset: 21, line: 1, column: 22 }
+            }
+          ]
+        }
+      ],
+      END_TAG_WITH_TRAILING_SOLIDUS: [
+        {
+          code: '<template><div></div/></template>',
+          errors: [
+            {
+              type: ParserErrorTypes.END_TAG_WITH_TRAILING_SOLIDUS,
+              loc: { offset: 20, line: 1, column: 21 }
+            }
+          ]
+        }
+      ],
+      EOF_BEFORE_TAG_NAME: [
+        {
+          code: '<template><',
+          errors: [
+            {
+              type: ParserErrorTypes.EOF_BEFORE_TAG_NAME,
+              loc: { offset: 11, line: 1, column: 12 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 11, line: 1, column: 12 }
+            }
+          ]
+        },
+        {
+          code: '<template></',
+          errors: [
+            {
+              type: ParserErrorTypes.EOF_BEFORE_TAG_NAME,
+              loc: { offset: 12, line: 1, column: 13 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 12, line: 1, column: 13 }
+            }
+          ]
+        }
+      ],
+      EOF_IN_CDATA: [
+        {
+          code: '<template><svg><![CDATA[cdata',
+          errors: [
+            {
+              type: ParserErrorTypes.EOF_IN_CDATA,
+              loc: { offset: 29, line: 1, column: 30 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 29, line: 1, column: 30 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 29, line: 1, column: 30 }
+            }
+          ]
+        },
+        {
+          code: '<template><svg><![CDATA[',
+          errors: [
+            {
+              type: ParserErrorTypes.EOF_IN_CDATA,
+              loc: { offset: 24, line: 1, column: 25 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 24, line: 1, column: 25 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 24, line: 1, column: 25 }
+            }
+          ]
+        }
+      ],
+      EOF_IN_COMMENT: [
+        {
+          code: '<template><!--comment',
+          errors: [
+            {
+              type: ParserErrorTypes.EOF_IN_COMMENT,
+              loc: { offset: 21, line: 1, column: 22 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 21, line: 1, column: 22 }
+            }
+          ]
+        },
+        {
+          code: '<template><!--',
+          errors: [
+            {
+              type: ParserErrorTypes.EOF_IN_COMMENT,
+              loc: { offset: 14, line: 1, column: 15 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 14, line: 1, column: 15 }
+            }
+          ]
+        },
+        // Bogus comments don't throw eof-in-comment error.
+        // https://html.spec.whatwg.org/multipage/parsing.html#bogus-comment-state
+        {
+          code: '<template><!',
+          errors: [
+            {
+              type: ParserErrorTypes.INCORRECTLY_OPENED_COMMENT,
+              loc: { offset: 10, line: 1, column: 11 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 12, line: 1, column: 13 }
+            }
+          ]
+        },
+        {
+          code: '<template><!-',
+          errors: [
+            {
+              type: ParserErrorTypes.INCORRECTLY_OPENED_COMMENT,
+              loc: { offset: 10, line: 1, column: 11 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 13, line: 1, column: 14 }
+            }
+          ]
+        },
+        {
+          code: '<template><!abc',
+          errors: [
+            {
+              type: ParserErrorTypes.INCORRECTLY_OPENED_COMMENT,
+              loc: { offset: 10, line: 1, column: 11 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 15, line: 1, column: 16 }
+            }
+          ]
+        }
+      ],
+      EOF_IN_SCRIPT_HTML_COMMENT_LIKE_TEXT: [
+        {
+          code: "<script><!--console.log('hello')",
+          errors: [
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 32, line: 1, column: 33 }
+            },
+            {
+              type: ParserErrorTypes.EOF_IN_SCRIPT_HTML_COMMENT_LIKE_TEXT,
+              loc: { offset: 32, line: 1, column: 33 }
+            }
+          ]
+        },
+        {
+          code: "<script>console.log('hello')",
+          errors: [
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 28, line: 1, column: 29 }
+            }
+          ]
+        }
+      ],
+      EOF_IN_TAG: [
+        {
+          code: '<template><div',
+          errors: [
+            {
+              type: ParserErrorTypes.EOF_IN_TAG,
+              loc: { offset: 14, line: 1, column: 15 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 14, line: 1, column: 15 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 14, line: 1, column: 15 }
+            }
+          ]
+        },
+        {
+          code: '<template><div ',
+          errors: [
+            {
+              type: ParserErrorTypes.EOF_IN_TAG,
+              loc: { offset: 15, line: 1, column: 16 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 15, line: 1, column: 16 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 15, line: 1, column: 16 }
+            }
+          ]
+        },
+        {
+          code: '<template><div id',
+          errors: [
+            {
+              type: ParserErrorTypes.EOF_IN_TAG,
+              loc: { offset: 17, line: 1, column: 18 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 17, line: 1, column: 18 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 17, line: 1, column: 18 }
+            }
+          ]
+        },
+        {
+          code: '<template><div id ',
+          errors: [
+            {
+              type: ParserErrorTypes.EOF_IN_TAG,
+              loc: { offset: 18, line: 1, column: 19 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 18, line: 1, column: 19 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 18, line: 1, column: 19 }
+            }
+          ]
+        },
+        {
+          code: '<template><div id =',
+          errors: [
+            {
+              type: ParserErrorTypes.MISSING_ATTRIBUTE_VALUE,
+              loc: { offset: 19, line: 1, column: 20 }
+            },
+            {
+              type: ParserErrorTypes.EOF_IN_TAG,
+              loc: { offset: 19, line: 1, column: 20 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 19, line: 1, column: 20 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 19, line: 1, column: 20 }
+            }
+          ]
+        },
+        {
+          code: "<template><div id='abc",
+          errors: [
+            {
+              type: ParserErrorTypes.EOF_IN_TAG,
+              loc: { offset: 22, line: 1, column: 23 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 22, line: 1, column: 23 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 22, line: 1, column: 23 }
+            }
+          ]
+        },
+        {
+          code: '<template><div id="abc',
+          errors: [
+            {
+              type: ParserErrorTypes.EOF_IN_TAG,
+              loc: { offset: 22, line: 1, column: 23 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 22, line: 1, column: 23 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 22, line: 1, column: 23 }
+            }
+          ]
+        },
+        {
+          code: "<template><div id='abc'",
+          errors: [
+            {
+              type: ParserErrorTypes.EOF_IN_TAG,
+              loc: { offset: 23, line: 1, column: 24 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 23, line: 1, column: 24 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 23, line: 1, column: 24 }
+            }
+          ]
+        },
+        {
+          code: '<template><div id="abc"',
+          errors: [
+            {
+              type: ParserErrorTypes.EOF_IN_TAG,
+              loc: { offset: 23, line: 1, column: 24 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 23, line: 1, column: 24 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 23, line: 1, column: 24 }
+            }
+          ]
+        },
+        {
+          code: '<template><div id=abc',
+          errors: [
+            {
+              type: ParserErrorTypes.EOF_IN_TAG,
+              loc: { offset: 21, line: 1, column: 22 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 21, line: 1, column: 22 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 21, line: 1, column: 22 }
+            }
+          ]
+        },
+        {
+          code: "<template><div id='abc'/",
+          errors: [
+            {
+              type: ParserErrorTypes.UNEXPECTED_SOLIDUS_IN_TAG,
+              loc: { offset: 23, line: 1, column: 24 }
+            },
+            {
+              type: ParserErrorTypes.EOF_IN_TAG,
+              loc: { offset: 24, line: 1, column: 25 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 24, line: 1, column: 25 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 24, line: 1, column: 25 }
+            }
+          ]
+        },
+        {
+          code: '<template><div id="abc"/',
+          errors: [
+            {
+              type: ParserErrorTypes.UNEXPECTED_SOLIDUS_IN_TAG,
+              loc: { offset: 23, line: 1, column: 24 }
+            },
+            {
+              type: ParserErrorTypes.EOF_IN_TAG,
+              loc: { offset: 24, line: 1, column: 25 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 24, line: 1, column: 25 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 24, line: 1, column: 25 }
+            }
+          ]
+        },
+        {
+          code: '<template><div id=abc /',
+          errors: [
+            {
+              type: ParserErrorTypes.UNEXPECTED_SOLIDUS_IN_TAG,
+              loc: { offset: 22, line: 1, column: 23 }
+            },
+            {
+              type: ParserErrorTypes.EOF_IN_TAG,
+              loc: { offset: 23, line: 1, column: 24 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 23, line: 1, column: 24 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 23, line: 1, column: 24 }
+            }
+          ]
+        }
+      ],
+      INCORRECTLY_CLOSED_COMMENT: [
+        {
+          code: '<template><!--comment--!></template>',
+          errors: [
+            {
+              type: ParserErrorTypes.INCORRECTLY_CLOSED_COMMENT,
+              loc: { offset: 10, line: 1, column: 11 }
+            }
+          ]
+        }
+      ],
+      INCORRECTLY_OPENED_COMMENT: [
+        {
+          code: '<template><!></template>',
+          errors: [
+            {
+              type: ParserErrorTypes.INCORRECTLY_OPENED_COMMENT,
+              loc: { offset: 10, line: 1, column: 11 }
+            }
+          ]
+        },
+        {
+          code: '<template><!-></template>',
+          errors: [
+            {
+              type: ParserErrorTypes.INCORRECTLY_OPENED_COMMENT,
+              loc: { offset: 10, line: 1, column: 11 }
+            }
+          ]
+        },
+        {
+          code: '<template><!ELEMENT br EMPTY></template>',
+          errors: [
+            {
+              type: ParserErrorTypes.INCORRECTLY_OPENED_COMMENT,
+              loc: { offset: 10, line: 1, column: 11 }
+            }
+          ]
+        },
+        // Just ignore doctype.
+        {
+          code: '<!DOCTYPE html>',
+          errors: []
+        }
+      ],
+      INVALID_FIRST_CHARACTER_OF_TAG_NAME: [
+        {
+          code: '<template>a < b</template>',
+          errors: [
+            {
+              type: ParserErrorTypes.INVALID_FIRST_CHARACTER_OF_TAG_NAME,
+              loc: { offset: 13, line: 1, column: 14 }
+            }
+          ]
+        },
+        {
+          code: '<template><�></template>',
+          errors: [
+            {
+              type: ParserErrorTypes.INVALID_FIRST_CHARACTER_OF_TAG_NAME,
+              loc: { offset: 11, line: 1, column: 12 }
+            }
+          ]
+        },
+        {
+          code: '<template>a </ b</template>',
+          errors: [
+            {
+              type: ParserErrorTypes.INVALID_FIRST_CHARACTER_OF_TAG_NAME,
+              loc: { offset: 14, line: 1, column: 15 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 27, line: 1, column: 28 }
+            }
+          ]
+        },
+        {
+          code: '<template></�></template>',
+          errors: [
+            {
+              type: ParserErrorTypes.INVALID_FIRST_CHARACTER_OF_TAG_NAME,
+              loc: { offset: 12, line: 1, column: 13 }
+            }
+          ]
+        },
+        // Don't throw invalid-first-character-of-tag-name in interpolation
+        {
+          code: '<template>{{a < b}}</template>',
+          errors: []
+        }
+      ],
+      MISSING_ATTRIBUTE_VALUE: [
+        {
+          code: '<template><div id=></div></template>',
+          errors: [
+            {
+              type: ParserErrorTypes.MISSING_ATTRIBUTE_VALUE,
+              loc: { offset: 18, line: 1, column: 19 }
+            }
+          ]
+        },
+        {
+          code: '<template><div id= ></div></template>',
+          errors: [
+            {
+              type: ParserErrorTypes.MISSING_ATTRIBUTE_VALUE,
+              loc: { offset: 19, line: 1, column: 20 }
+            }
+          ]
+        },
+        {
+          code: '<template><div id= /></div></template>',
+          errors: []
+        }
+      ],
+      MISSING_END_TAG_NAME: [
+        {
+          code: '<template></></template>',
+          errors: [
+            {
+              type: ParserErrorTypes.MISSING_END_TAG_NAME,
+              loc: { offset: 12, line: 1, column: 13 }
+            }
+          ]
+        }
+      ],
+      MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE: [
+        {
+          code: '<template>&amp</template>',
+          options: { namedCharacterReferences: { amp: '&' } },
+          errors: [
+            {
+              type:
+                ParserErrorTypes.MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE,
+              loc: { offset: 14, line: 1, column: 15 }
+            }
+          ]
+        },
+        {
+          code: '<template>&#40</template>',
+          errors: [
+            {
+              type:
+                ParserErrorTypes.MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE,
+              loc: { offset: 14, line: 1, column: 15 }
+            }
+          ]
+        },
+        {
+          code: '<template>&#x40</template>',
+          errors: [
+            {
+              type:
+                ParserErrorTypes.MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE,
+              loc: { offset: 15, line: 1, column: 16 }
+            }
+          ]
+        }
+      ],
+      MISSING_WHITESPACE_BETWEEN_ATTRIBUTES: [
+        {
+          code: '<template><div id="foo"class="bar"></div></template>',
+          errors: [
+            {
+              type: ParserErrorTypes.MISSING_WHITESPACE_BETWEEN_ATTRIBUTES,
+              loc: { offset: 23, line: 1, column: 24 }
+            }
+          ]
+        },
+        // CR doesn't appear in tokenization phase, but all CR are removed in preprocessing.
+        // https://html.spec.whatwg.org/multipage/parsing.html#preprocessing-the-input-stream
+        {
+          code: '<template><div id="foo"\r\nclass="bar"></div></template>',
+          errors: []
+        }
+      ],
+      NESTED_COMMENT: [
+        {
+          code: '<template><!--a<!--b--></template>',
+          errors: [
+            {
+              type: ParserErrorTypes.NESTED_COMMENT,
+              loc: { offset: 15, line: 1, column: 16 }
+            }
+          ]
+        },
+        {
+          code: '<template><!--a<!--b<!--c--></template>',
+          errors: [
+            {
+              type: ParserErrorTypes.NESTED_COMMENT,
+              loc: { offset: 15, line: 1, column: 16 }
+            },
+            {
+              type: ParserErrorTypes.NESTED_COMMENT,
+              loc: { offset: 20, line: 1, column: 21 }
+            }
+          ]
+        },
+        {
+          code: '<template><!--a<!--></template>',
+          errors: []
+        },
+        {
+          code: '<template><!--a<!--',
+          errors: [
+            {
+              type: ParserErrorTypes.EOF_IN_COMMENT,
+              loc: { offset: 19, line: 1, column: 20 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 19, line: 1, column: 20 }
+            }
+          ]
+        }
+      ],
+      NONCHARACTER_CHARACTER_REFERENCE: [
+        {
+          code: '<template>&#xFFFE;</template>',
+          errors: [
+            {
+              type: ParserErrorTypes.NONCHARACTER_CHARACTER_REFERENCE,
+              loc: { offset: 10, line: 1, column: 11 }
+            }
+          ]
+        },
+        {
+          code: '<template>&#x1FFFF;</template>',
+          errors: [
+            {
+              type: ParserErrorTypes.NONCHARACTER_CHARACTER_REFERENCE,
+              loc: { offset: 10, line: 1, column: 11 }
+            }
+          ]
+        }
+      ],
+      NULL_CHARACTER_REFERENCE: [
+        {
+          code: '<template>&#0000;</template>',
+          errors: [
+            {
+              type: ParserErrorTypes.NULL_CHARACTER_REFERENCE,
+              loc: { offset: 10, line: 1, column: 11 }
+            }
+          ]
+        }
+      ],
+      SURROGATE_CHARACTER_REFERENCE: [
+        {
+          code: '<template>&#xD800;</template>',
+          errors: [
+            {
+              type: ParserErrorTypes.SURROGATE_CHARACTER_REFERENCE,
+              loc: { offset: 10, line: 1, column: 11 }
+            }
+          ]
+        }
+      ],
+      UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME: [
+        {
+          code: "<template><div a\"bc=''></div></template>",
+          errors: [
+            {
+              type: ParserErrorTypes.UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME,
+              loc: { offset: 16, line: 1, column: 17 }
+            }
+          ]
+        },
+        {
+          code: "<template><div a'bc=''></div></template>",
+          errors: [
+            {
+              type: ParserErrorTypes.UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME,
+              loc: { offset: 16, line: 1, column: 17 }
+            }
+          ]
+        },
+        {
+          code: "<template><div a<bc=''></div></template>",
+          errors: [
+            {
+              type: ParserErrorTypes.UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME,
+              loc: { offset: 16, line: 1, column: 17 }
+            }
+          ]
+        }
+      ],
+      UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE: [
+        {
+          code: '<template><div foo=bar"></div></template>',
+          errors: [
+            {
+              type:
+                ParserErrorTypes.UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE,
+              loc: { offset: 22, line: 1, column: 23 }
+            }
+          ]
+        },
+        {
+          code: "<template><div foo=bar'></div></template>",
+          errors: [
+            {
+              type:
+                ParserErrorTypes.UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE,
+              loc: { offset: 22, line: 1, column: 23 }
+            }
+          ]
+        },
+        {
+          code: '<template><div foo=bar<div></div></template>',
+          errors: [
+            {
+              type:
+                ParserErrorTypes.UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE,
+              loc: { offset: 22, line: 1, column: 23 }
+            }
+          ]
+        },
+        {
+          code: '<template><div foo=bar=baz></div></template>',
+          errors: [
+            {
+              type:
+                ParserErrorTypes.UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE,
+              loc: { offset: 22, line: 1, column: 23 }
+            }
+          ]
+        },
+        {
+          code: '<template><div foo=bar`></div></template>',
+          errors: [
+            {
+              type:
+                ParserErrorTypes.UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE,
+              loc: { offset: 22, line: 1, column: 23 }
+            }
+          ]
+        }
+      ],
+      UNEXPECTED_EQUALS_SIGN_BEFORE_ATTRIBUTE_NAME: [
+        {
+          code: '<template><div =foo=bar></div></template>',
+          errors: [
+            {
+              type:
+                ParserErrorTypes.UNEXPECTED_EQUALS_SIGN_BEFORE_ATTRIBUTE_NAME,
+              loc: { offset: 15, line: 1, column: 16 }
+            }
+          ]
+        },
+        {
+          code: '<template><div =></div></template>',
+          errors: [
+            {
+              type:
+                ParserErrorTypes.UNEXPECTED_EQUALS_SIGN_BEFORE_ATTRIBUTE_NAME,
+              loc: { offset: 15, line: 1, column: 16 }
+            }
+          ]
+        }
+      ],
+      UNEXPECTED_QUESTION_MARK_INSTEAD_OF_TAG_NAME: [
+        {
+          code: '<template><?xml?></template>',
+          errors: [
+            {
+              type:
+                ParserErrorTypes.UNEXPECTED_QUESTION_MARK_INSTEAD_OF_TAG_NAME,
+              loc: { offset: 11, line: 1, column: 12 }
+            }
+          ]
+        }
+      ],
+      UNEXPECTED_SOLIDUS_IN_TAG: [
+        {
+          code: '<template><div a/b></div></template>',
+          errors: [
+            {
+              type: ParserErrorTypes.UNEXPECTED_SOLIDUS_IN_TAG,
+              loc: { offset: 16, line: 1, column: 17 }
+            }
+          ]
+        }
+      ],
+      UNKNOWN_NAMED_CHARACTER_REFERENCE: [
+        {
+          code: '<template>&unknown;</template>',
+          errors: [
+            {
+              type: ParserErrorTypes.UNKNOWN_NAMED_CHARACTER_REFERENCE,
+              loc: { offset: 10, line: 1, column: 11 }
+            }
+          ]
+        }
+      ],
+      X_INVALID_END_TAG: [
+        {
+          code: '<template></div></template>',
+          errors: [
+            {
+              type: ParserErrorTypes.X_INVALID_END_TAG,
+              loc: { offset: 10, line: 1, column: 11 }
+            }
+          ]
+        },
+        {
+          code: '<template></div></div></template>',
+          errors: [
+            {
+              type: ParserErrorTypes.X_INVALID_END_TAG,
+              loc: { offset: 10, line: 1, column: 11 }
+            },
+            {
+              type: ParserErrorTypes.X_INVALID_END_TAG,
+              loc: { offset: 16, line: 1, column: 17 }
+            }
+          ]
+        },
+        {
+          code: "<template>{{'</div>'}}</template>",
+          errors: []
+        },
+        {
+          code: '<textarea></div></textarea>',
+          errors: []
+        },
+        {
+          code: '<svg><![CDATA[</div>]]></svg>',
+          errors: []
+        },
+        {
+          code: '<svg><!--</div>--></svg>',
+          errors: []
+        }
+      ],
+      X_MISSING_END_TAG: [
+        {
+          code: '<template><div></template>',
+          errors: [
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 15, line: 1, column: 16 }
+            }
+          ]
+        },
+        {
+          code: '<template><div>',
+          errors: [
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 15, line: 1, column: 16 }
+            },
+            {
+              type: ParserErrorTypes.X_MISSING_END_TAG,
+              loc: { offset: 15, line: 1, column: 16 }
+            }
+          ]
+        }
+      ],
+      X_MISSING_INTERPOLATION_END: [
+        {
+          code: '{{ foo',
+          errors: [
+            {
+              type: ParserErrorTypes.X_MISSING_INTERPOLATION_END,
+              loc: { offset: 0, line: 1, column: 1 }
+            }
+          ]
+        },
+        {
+          code: '{{',
+          errors: [
+            {
+              type: ParserErrorTypes.X_MISSING_INTERPOLATION_END,
+              loc: { offset: 0, line: 1, column: 1 }
+            }
+          ]
+        },
+        {
+          code: '{{}}',
+          errors: []
+        }
+      ]
+    }
+
+    for (const key of Object.keys(patterns) as (keyof (typeof patterns))[]) {
+      describe(key, () => {
+        for (const { code, errors, options } of patterns[key]) {
+          test(
+            code.replace(
+              /[\r\n]/g,
+              c => `\\x0${c.codePointAt(0)!.toString(16)};`
+            ),
+            () => {
+              const spy = jest.fn()
+              const ast = parse(code, {
+                ...parserOptions,
+                ...options,
+                onError: spy
+              })
+
+              expect(spy.mock.calls).toEqual(
+                errors.map(({ type, loc }) => [type, loc])
+              )
+              expect(ast).toMatchSnapshot()
+            }
+          )
+        }
+      })
+    }
+  })
+})
diff --git a/packages/compiler-core/src/ast.ts b/packages/compiler-core/src/ast.ts
new file mode 100644 (file)
index 0000000..8941e2b
--- /dev/null
@@ -0,0 +1,87 @@
+export const enum NodeTypes {
+  TEXT,
+  COMMENT,
+  ELEMENT,
+  ATTRIBUTE,
+  EXPRESSION,
+  DIRECTIVE,
+  ROOT
+}
+
+export const enum ElementTypes {
+  ELEMENT,
+  COMPONENT,
+  SLOT, // slot
+  TEMPLATE // template, component
+}
+
+export const enum Namespaces {
+  HTML,
+  SVG, // allows CDATA section and forbids end tag omission.
+  MATH_ML // allows CDATA section and forbids end tag omission.
+}
+
+export interface Node {
+  type: NodeTypes
+  loc: SourceLocation
+}
+
+export interface RootNode extends Node {
+  type: NodeTypes.ROOT
+  children: Array<ElementNode | ExpressionNode | TextNode | CommentNode>
+}
+
+export interface ElementNode extends Node {
+  type: NodeTypes.ELEMENT
+  ns: Namespaces
+  tag: string
+  tagType: ElementTypes
+  isSelfClosing: boolean
+  props: Array<AttributeNode | DirectiveNode>
+  children: Array<ElementNode | ExpressionNode | TextNode | CommentNode>
+}
+
+export interface TextNode extends Node {
+  type: NodeTypes.TEXT
+  content: string
+  isEmpty: boolean
+}
+
+export interface CommentNode extends Node {
+  type: NodeTypes.COMMENT
+  content: string
+}
+
+export interface AttributeNode extends Node {
+  type: NodeTypes.ATTRIBUTE
+  name: string
+  value: TextNode | undefined
+}
+
+export interface DirectiveNode extends Node {
+  type: NodeTypes.DIRECTIVE
+  name: string
+  exp: ExpressionNode | undefined
+  arg: ExpressionNode | undefined
+  modifiers: string[]
+}
+
+export interface ExpressionNode extends Node {
+  type: NodeTypes.EXPRESSION
+  content: string
+  isStatic: boolean
+}
+
+export interface Position {
+  offset: number // from start of file
+  line: number
+  column: number
+}
+
+// The node's range. The `start` is inclusive and `end` is exclusive.
+// [start, end)
+export interface SourceLocation {
+  start: Position
+  end: Position
+  source: string
+}
diff --git a/packages/compiler-core/src/errorTypes.ts b/packages/compiler-core/src/errorTypes.ts
new file mode 100644 (file)
index 0000000..aad1047
--- /dev/null
@@ -0,0 +1,37 @@
+export const enum ParserErrorTypes {
+  ABRUPT_CLOSING_OF_EMPTY_COMMENT,
+  ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE,
+  CDATA_IN_HTML_CONTENT,
+  CHARACTER_REFERENCE_OUTSIDE_UNICODE_RANGE,
+  CONTROL_CHARACTER_REFERENCE,
+  DUPLICATE_ATTRIBUTE,
+  END_TAG_WITH_ATTRIBUTES,
+  END_TAG_WITH_TRAILING_SOLIDUS,
+  EOF_BEFORE_TAG_NAME,
+  EOF_IN_CDATA,
+  EOF_IN_COMMENT,
+  EOF_IN_SCRIPT_HTML_COMMENT_LIKE_TEXT,
+  EOF_IN_TAG,
+  INCORRECTLY_CLOSED_COMMENT,
+  INCORRECTLY_OPENED_COMMENT,
+  INVALID_FIRST_CHARACTER_OF_TAG_NAME,
+  MISSING_ATTRIBUTE_VALUE,
+  MISSING_END_TAG_NAME,
+  MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE,
+  MISSING_WHITESPACE_BETWEEN_ATTRIBUTES,
+  NESTED_COMMENT,
+  NONCHARACTER_CHARACTER_REFERENCE,
+  NULL_CHARACTER_REFERENCE,
+  SURROGATE_CHARACTER_REFERENCE,
+  UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME,
+  UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE,
+  UNEXPECTED_EQUALS_SIGN_BEFORE_ATTRIBUTE_NAME,
+  UNEXPECTED_NULL_CHARACTER,
+  UNEXPECTED_QUESTION_MARK_INSTEAD_OF_TAG_NAME,
+  UNEXPECTED_SOLIDUS_IN_TAG,
+  UNKNOWN_NAMED_CHARACTER_REFERENCE,
+  X_INVALID_END_TAG,
+  X_MISSING_END_TAG,
+  X_MISSING_INTERPOLATION_END,
+  X_MISSING_DYNAMIC_DIRECTIVE_ARGUMENT_END
+}
index 70b786d12ed055a08b57f5cf47f717bf6a266301..2e25f608b27eecff9698344563985c7acbf3a581 100644 (file)
@@ -1 +1,3 @@
-// TODO
+export { parse } from './parser'
+export * from './ast'
+export * from './errorTypes'
diff --git a/packages/compiler-core/src/parser.ts b/packages/compiler-core/src/parser.ts
new file mode 100644 (file)
index 0000000..abb7fa8
--- /dev/null
@@ -0,0 +1,918 @@
+import assert from 'assert'
+import { ParserErrorTypes } from './errorTypes'
+import {
+  Node,
+  AttributeNode,
+  CommentNode,
+  DirectiveNode,
+  ElementNode,
+  ElementTypes,
+  ExpressionNode,
+  Namespaces,
+  NodeTypes,
+  Position,
+  RootNode,
+  SourceLocation,
+  TextNode
+} from './ast'
+
+export interface ParserOptions {
+  isVoidTag: (tag: string) => boolean // e.g. img, br, hr
+  getNamespace: (tag: string, parent: ElementNode | undefined) => Namespaces
+  getTextMode: (tag: string, ns: Namespaces) => TextModes
+  delimiters: [string, string] // ['{{', '}}']
+  transform: (node: Node) => Node // --
+  ignoreSpaces: boolean
+
+  // Map to HTML entities. E.g., `{ "amp;": "&" }`
+  // The full set is https://html.spec.whatwg.org/multipage/named-characters.html#named-character-references
+  namedCharacterReferences: { [name: string]: string | undefined }
+
+  onError: (type: ParserErrorTypes, loc: Position) => void
+}
+
+export const enum TextModes {
+  // | Elements | Entities | End sign              | Inside of
+  DATA, // | ✔       | ✔       | End tags of ancestors |
+  RCDATA, // | ✘       | ✔       | End tag of the parent | <textarea>
+  RAWTEXT, // | ✘       | ✘       | End tag of the parent | <style>,<script>
+  CDATA,
+  ATTRIBUTE_VALUE
+}
+
+interface ParserContext extends ParserOptions {
+  readonly originalSource: string
+  source: string
+  offset: number
+  line: number
+  column: number
+  maxCRNameLength: number
+}
+
+export function parse(content: string, options: ParserOptions): RootNode {
+  const context = createParserContext(content, options)
+  const start = getCursor(context)
+
+  return {
+    type: NodeTypes.ROOT,
+    children: parseChildren(context, TextModes.DATA, []),
+    loc: getSelection(context, start)
+  }
+}
+
+function last<T>(xs: T[]): T | undefined {
+  return xs[xs.length - 1]
+}
+
+function startsWith(source: string, searchString: string): boolean {
+  return source.startsWith(searchString)
+}
+
+function advanceBy(context: ParserContext, numberOfCharacters: number): void {
+  assert(numberOfCharacters <= context.source.length)
+
+  const { column, source } = context
+  const str = source.slice(0, numberOfCharacters)
+  const lines = str.split(/\r?\n/)
+
+  context.source = source.slice(numberOfCharacters)
+  context.offset += numberOfCharacters
+  context.line += lines.length - 1
+  context.column =
+    lines.length === 1
+      ? column + numberOfCharacters
+      : Math.max(1, lines.pop()!.length)
+}
+
+function advanceSpaces(context: ParserContext): void {
+  const match = /^[\t\r\n\f ]+/.exec(context.source)
+  if (match) {
+    advanceBy(context, match[0].length)
+  }
+}
+
+function getCursor(context: ParserContext): Position {
+  const { column, line, offset } = context
+  return { column, line, offset }
+}
+
+function getNewPosition(
+  context: ParserContext,
+  start: Position,
+  numberOfCharacters: number
+): Position {
+  const { originalSource } = context
+  const str = originalSource.slice(start.offset, numberOfCharacters)
+  const lines = str.split(/\r?\n/)
+
+  const newPosition = {
+    column: start.column,
+    line: start.line,
+    offset: start.offset
+  }
+
+  newPosition.offset += numberOfCharacters
+  newPosition.line += lines.length - 1
+  newPosition.column =
+    lines.length === 1
+      ? start.column + numberOfCharacters
+      : Math.max(1, lines.pop()!.length)
+
+  return newPosition
+}
+
+function getSelection(
+  context: ParserContext,
+  start: Position,
+  end?: Position
+): SourceLocation {
+  end = end || getCursor(context)
+  return {
+    start,
+    end,
+    source: context.originalSource.slice(start.offset, end.offset)
+  }
+}
+
+function emitError(
+  context: ParserContext,
+  type: ParserErrorTypes,
+  offset?: number
+): void {
+  const loc = getCursor(context)
+  if (offset) {
+    loc.offset += offset
+    loc.column += offset
+  }
+  context.onError(type, loc)
+}
+
+function createParserContext(
+  content: string,
+  options: ParserOptions
+): ParserContext {
+  return {
+    ...options,
+    column: 1,
+    line: 1,
+    offset: 0,
+    originalSource: content,
+    source: content,
+    maxCRNameLength: Object.keys(options.namedCharacterReferences).reduce(
+      (max, name) => Math.max(max, name.length),
+      0
+    )
+  }
+}
+
+function parseChildren(
+  context: ParserContext,
+  mode: TextModes,
+  ancestors: ElementNode[]
+): RootNode['children'] {
+  const parent = last(ancestors)
+  const ns = parent ? parent.ns : Namespaces.HTML
+  const nodes: RootNode['children'] = []
+
+  while (!isEnd(context, mode, ancestors)) {
+    assert(context.source.length > 0)
+    const s = context.source
+    let node: any = null
+
+    if (startsWith(s, context.delimiters[0])) {
+      // '{{'
+      node = parseInterpolation(context, mode)
+    } else if (mode === TextModes.DATA && s[0] === '<') {
+      // https://html.spec.whatwg.org/multipage/parsing.html#tag-open-state
+      if (s.length === 1) {
+        emitError(context, ParserErrorTypes.EOF_BEFORE_TAG_NAME, 1)
+      } else if (s[1] === '!') {
+        // https://html.spec.whatwg.org/multipage/parsing.html#markup-declaration-open-state
+        if (startsWith(s, '<!--')) {
+          node = parseComment(context)
+        } else if (startsWith(s, '<!DOCTYPE')) {
+          // Ignore DOCTYPE by a limitation.
+          node = parseBogusComment(context)
+        } else if (startsWith(s, '<![CDATA[')) {
+          if (ns !== Namespaces.HTML) {
+            node = parseCDATA(context, ancestors)
+          } else {
+            emitError(context, ParserErrorTypes.CDATA_IN_HTML_CONTENT)
+            node = parseBogusComment(context)
+          }
+        } else {
+          emitError(context, ParserErrorTypes.INCORRECTLY_OPENED_COMMENT)
+          node = parseBogusComment(context)
+        }
+      } else if (s[1] === '/') {
+        // https://html.spec.whatwg.org/multipage/parsing.html#end-tag-open-state
+        if (s.length === 2) {
+          emitError(context, ParserErrorTypes.EOF_BEFORE_TAG_NAME, 2)
+        } else if (s[2] === '>') {
+          emitError(context, ParserErrorTypes.MISSING_END_TAG_NAME, 2)
+          advanceBy(context, 3)
+          continue
+        } else if (/[a-z]/i.test(s[2])) {
+          emitError(context, ParserErrorTypes.X_INVALID_END_TAG)
+          parseTag(context, TagType.End, parent)
+          continue
+        } else {
+          emitError(
+            context,
+            ParserErrorTypes.INVALID_FIRST_CHARACTER_OF_TAG_NAME,
+            2
+          )
+          node = parseBogusComment(context)
+        }
+      } else if (/[a-z]/i.test(s[1])) {
+        node = parseElement(context, ancestors)
+      } else if (s[1] === '?') {
+        emitError(
+          context,
+          ParserErrorTypes.UNEXPECTED_QUESTION_MARK_INSTEAD_OF_TAG_NAME,
+          1
+        )
+        node = parseBogusComment(context)
+      } else {
+        emitError(
+          context,
+          ParserErrorTypes.INVALID_FIRST_CHARACTER_OF_TAG_NAME,
+          1
+        )
+      }
+    }
+    if (!node) {
+      node = parseText(context, mode)
+    }
+
+    if (Array.isArray(node)) {
+      node.forEach(pushNode.bind(null, context, nodes))
+    } else {
+      pushNode(context, nodes, node)
+    }
+  }
+
+  return nodes
+}
+
+function isEnd(
+  context: ParserContext,
+  mode: TextModes,
+  ancestors: ElementNode[]
+): boolean {
+  const s = context.source
+
+  switch (mode) {
+    case TextModes.DATA:
+      if (startsWith(s, '</')) {
+        //TODO: probably bad performance
+        for (let i = ancestors.length - 1; i >= 0; --i) {
+          if (startsWithEndTagOpen(s, ancestors[i].tag)) {
+            return true
+          }
+        }
+      }
+      break
+
+    case TextModes.RCDATA:
+    case TextModes.RAWTEXT: {
+      const parent = last(ancestors)
+      if (parent && startsWithEndTagOpen(s, parent.tag)) {
+        return true
+      }
+      break
+    }
+
+    case TextModes.CDATA:
+      if (startsWith(s, ']]>')) {
+        return true
+      }
+      break
+  }
+
+  return !s
+}
+
+function startsWithEndTagOpen(source: string, tag: string): boolean {
+  return (
+    startsWith(source, '</') &&
+    source.substr(2, tag.length).toLowerCase() === tag.toLowerCase() &&
+    /[\t\n\f />]/.test(source[2 + tag.length] || '>')
+  )
+}
+
+function pushNode(
+  context: ParserContext,
+  nodes: RootNode['children'],
+  node: RootNode['children'][0]
+): void {
+  if (context.ignoreSpaces && node.type === NodeTypes.TEXT && node.isEmpty) {
+    return
+  }
+
+  // Merge if both this and the previous node are text and those are consecutive.
+  // This happens on "a < b" or something like.
+  const prev = last(nodes)
+  if (
+    prev &&
+    prev.type === NodeTypes.TEXT &&
+    node.type === NodeTypes.TEXT &&
+    prev.loc.end.offset === node.loc.start.offset
+  ) {
+    prev.content += node.content
+    prev.isEmpty = prev.content.trim().length === 0
+    prev.loc.end = node.loc.end
+    prev.loc.source += node.loc.source
+  } else {
+    nodes.push(node)
+  }
+}
+
+function parseCDATA(
+  context: ParserContext,
+  ancestors: ElementNode[]
+): RootNode['children'] {
+  assert(last(ancestors) == null || last(ancestors)!.ns !== Namespaces.HTML)
+  assert(startsWith(context.source, '<![CDATA['))
+
+  advanceBy(context, 9)
+  const nodes = parseChildren(context, TextModes.CDATA, ancestors)
+  if (context.source.length === 0) {
+    emitError(context, ParserErrorTypes.EOF_IN_CDATA)
+  } else {
+    assert(startsWith(context.source, ']]>'))
+    advanceBy(context, 3)
+  }
+
+  return nodes
+}
+
+function parseComment(context: ParserContext): CommentNode {
+  assert(startsWith(context.source, '<!--'))
+
+  const start = getCursor(context)
+  let content: string
+
+  // Regular comment.
+  const match = /--(\!)?>/.exec(context.source)
+  if (!match) {
+    content = context.source.slice(4)
+    advanceBy(context, context.source.length)
+    emitError(context, ParserErrorTypes.EOF_IN_COMMENT)
+  } else {
+    if (match.index <= 3) {
+      emitError(context, ParserErrorTypes.ABRUPT_CLOSING_OF_EMPTY_COMMENT)
+    }
+    if (match[1]) {
+      emitError(context, ParserErrorTypes.INCORRECTLY_CLOSED_COMMENT)
+    }
+    content = context.source.slice(4, match.index)
+
+    // Advancing with reporting nested comments.
+    const s = context.source.slice(0, match.index)
+    let prevIndex = 1,
+      nestedIndex = 0
+    while ((nestedIndex = s.indexOf('<!--', prevIndex)) !== -1) {
+      advanceBy(context, nestedIndex - prevIndex + 1)
+      if (nestedIndex + 4 < s.length) {
+        emitError(context, ParserErrorTypes.NESTED_COMMENT)
+      }
+      prevIndex = nestedIndex + 1
+    }
+    advanceBy(context, match.index + match[0].length - prevIndex + 1)
+  }
+
+  return {
+    type: NodeTypes.COMMENT,
+    content,
+    loc: getSelection(context, start)
+  }
+}
+
+function parseBogusComment(context: ParserContext): CommentNode | undefined {
+  assert(/^<(?:[\!\?]|\/[^a-z>])/i.test(context.source))
+
+  const start = getCursor(context)
+  const contentStart = context.source[1] === '?' ? 1 : 2
+  let content: string
+
+  const closeIndex = context.source.indexOf('>')
+  if (closeIndex === -1) {
+    content = context.source.slice(contentStart)
+    advanceBy(context, context.source.length)
+  } else {
+    content = context.source.slice(contentStart, closeIndex)
+    advanceBy(context, closeIndex + 1)
+  }
+
+  return {
+    type: NodeTypes.COMMENT,
+    content,
+    loc: getSelection(context, start)
+  }
+}
+
+function parseElement(
+  context: ParserContext,
+  ancestors: ElementNode[]
+): ElementNode | undefined {
+  assert(/^<[a-z]/i.test(context.source))
+
+  // Start tag.
+  const parent = last(ancestors)
+  const element = parseTag(context, TagType.Start, parent)
+
+  if (element.isSelfClosing || context.isVoidTag(element.tag)) {
+    return element
+  }
+
+  // Children.
+  ancestors.push(element)
+  const mode = (context.getTextMode(
+    element.tag,
+    element.ns
+  ) as unknown) as TextModes
+  const children = parseChildren(context, mode, ancestors)
+  ancestors.pop()
+
+  element.children = children
+
+  // End tag.
+  if (startsWithEndTagOpen(context.source, element.tag)) {
+    parseTag(context, TagType.End, parent)
+  } else {
+    emitError(context, ParserErrorTypes.X_MISSING_END_TAG)
+    if (context.source.length === 0 && element.tag.toLowerCase() === 'script') {
+      const first = children[0]
+      if (first && startsWith(first.loc.source, '<!--')) {
+        emitError(
+          context,
+          ParserErrorTypes.EOF_IN_SCRIPT_HTML_COMMENT_LIKE_TEXT
+        )
+      }
+    }
+  }
+
+  element.loc = getSelection(context, element.loc.start)
+  return element
+}
+
+const enum TagType {
+  Start,
+  End
+}
+
+/**
+ * Parse a tag (E.g. `<div id=a>`) with that type (start tag or end tag).
+ */
+function parseTag(
+  context: ParserContext,
+  type: TagType,
+  parent: ElementNode | undefined
+): ElementNode {
+  assert(/^<\/?[a-z]/i.test(context.source))
+  assert(
+    type === (startsWith(context.source, '</') ? TagType.End : TagType.Start)
+  )
+
+  // Tag open.
+  const start = getCursor(context)
+  const match = /^<\/?([a-z][^\t\r\n\f />]*)/i.exec(context.source)!
+  const tag = match[1]
+  const props = []
+  const ns = context.getNamespace(tag, parent)
+
+  advanceBy(context, match[0].length)
+  advanceSpaces(context)
+
+  // Attributes.
+  const attributeNames = new Set<string>()
+  while (
+    context.source.length > 0 &&
+    !startsWith(context.source, '>') &&
+    !startsWith(context.source, '/>')
+  ) {
+    if (startsWith(context.source, '/')) {
+      emitError(context, ParserErrorTypes.UNEXPECTED_SOLIDUS_IN_TAG)
+      advanceBy(context, 1)
+      advanceSpaces(context)
+      continue
+    }
+    if (type === TagType.End) {
+      emitError(context, ParserErrorTypes.END_TAG_WITH_ATTRIBUTES)
+    }
+
+    const attr = parseAttribute(context, attributeNames)
+    if (type === TagType.Start) {
+      props.push(attr)
+    }
+
+    if (/^[^\t\r\n\f />]/.test(context.source)) {
+      emitError(context, ParserErrorTypes.MISSING_WHITESPACE_BETWEEN_ATTRIBUTES)
+    }
+    advanceSpaces(context)
+  }
+
+  // Tag close.
+  let isSelfClosing = false
+  if (context.source.length === 0) {
+    emitError(context, ParserErrorTypes.EOF_IN_TAG)
+  } else {
+    isSelfClosing = startsWith(context.source, '/>')
+    if (type === TagType.End && isSelfClosing) {
+      emitError(context, ParserErrorTypes.END_TAG_WITH_TRAILING_SOLIDUS)
+    }
+    advanceBy(context, isSelfClosing ? 2 : 1)
+  }
+
+  let tagType = ElementTypes.ELEMENT
+
+  if (tag === 'slot') tagType = ElementTypes.SLOT
+  else if (tag === 'template') tagType = ElementTypes.TEMPLATE
+  else if (/[A-Z-]/.test(tag)) tagType = ElementTypes.COMPONENT
+
+  return {
+    type: NodeTypes.ELEMENT,
+    ns,
+    tag,
+    tagType,
+    props,
+    isSelfClosing,
+    children: [],
+    loc: getSelection(context, start)
+  }
+}
+
+function parseAttribute(
+  context: ParserContext,
+  nameSet: Set<string>
+): AttributeNode | DirectiveNode {
+  assert(/^[^\t\r\n\f />]/.test(context.source))
+
+  // Name.
+  const start = getCursor(context)
+  const match = /^[^\t\r\n\f />][^\t\r\n\f />=]*/.exec(context.source)!
+  const name = match[0]
+
+  if (nameSet.has(name)) {
+    emitError(context, ParserErrorTypes.DUPLICATE_ATTRIBUTE)
+  }
+  nameSet.add(name)
+
+  if (name[0] === '=') {
+    emitError(
+      context,
+      ParserErrorTypes.UNEXPECTED_EQUALS_SIGN_BEFORE_ATTRIBUTE_NAME
+    )
+  }
+  {
+    const pattern = /["'<]/g
+    let m: RegExpExecArray | null
+    while ((m = pattern.exec(name)) !== null) {
+      emitError(
+        context,
+        ParserErrorTypes.UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME,
+        m.index
+      )
+    }
+  }
+
+  advanceBy(context, name.length)
+
+  // Value
+  let value: { content: string; loc: SourceLocation } | undefined = undefined
+  if (/^[\t\r\n\f ]*=/.test(context.source)) {
+    advanceSpaces(context)
+    advanceBy(context, 1)
+    advanceSpaces(context)
+    value = parseAttributeValue(context)
+    if (!value) {
+      emitError(context, ParserErrorTypes.MISSING_ATTRIBUTE_VALUE)
+    }
+  }
+  const loc = getSelection(context, start)
+
+  if (/^(v-|:|@|#)/.test(name)) {
+    const match = /(?:^v-([a-z0-9-]+))?(?:(?::|^@|^#)([^\.]+))?(.+)?$/i.exec(
+      name
+    )!
+
+    let arg: ExpressionNode | undefined
+
+    if (match[2]) {
+      const startOffset = name.split(match[2], 2)!.shift()!.length
+      const loc = getSelection(
+        context,
+        getNewPosition(context, start, startOffset),
+        getNewPosition(context, start, startOffset + match[2].length)
+      )
+      let content = match[2]
+      let isStatic = true
+
+      if (content.startsWith('[')) {
+        isStatic = false
+
+        if (!content.endsWith(']')) {
+          emitError(
+            context,
+            ParserErrorTypes.X_MISSING_DYNAMIC_DIRECTIVE_ARGUMENT_END
+          )
+        }
+
+        content = content.substr(1, content.length - 2)
+      }
+
+      arg = {
+        type: NodeTypes.EXPRESSION,
+        content,
+        isStatic,
+        loc
+      }
+    }
+
+    return {
+      type: NodeTypes.DIRECTIVE,
+      name:
+        match[1] ||
+        (startsWith(name, ':')
+          ? 'bind'
+          : startsWith(name, '@')
+            ? 'on'
+            : 'slot'),
+      exp: value && {
+        type: NodeTypes.EXPRESSION,
+        content: value.content,
+        isStatic: false,
+        loc: value.loc
+      },
+      arg,
+      modifiers: match[3] ? match[3].substr(1).split('.') : [],
+      loc
+    }
+  }
+
+  return {
+    type: NodeTypes.ATTRIBUTE,
+    name,
+    value: value && {
+      type: NodeTypes.TEXT,
+      content: value.content,
+      isEmpty: value.content.trim().length === 0,
+      loc: value.loc
+    },
+    loc
+  }
+}
+
+function parseAttributeValue(
+  context: ParserContext
+): { content: string; loc: SourceLocation } | undefined {
+  const start = getCursor(context)
+  let content: string
+
+  if (/^["']/.test(context.source)) {
+    // Quoted value.
+    const quote = context.source[0]
+    advanceBy(context, 1)
+
+    const endIndex = context.source.indexOf(quote)
+    if (endIndex === -1) {
+      content = parseTextData(
+        context,
+        context.source.length,
+        TextModes.ATTRIBUTE_VALUE
+      )
+    } else {
+      content = parseTextData(context, endIndex, TextModes.ATTRIBUTE_VALUE)
+      advanceBy(context, 1)
+    }
+  } else {
+    // Unquoted
+    const match = /^[^\t\r\n\f >]+/.exec(context.source)
+    if (!match) {
+      return undefined
+    }
+    let unexpectedChars = /["'<=`]/g
+    let m: RegExpExecArray | null
+    while ((m = unexpectedChars.exec(match[0])) !== null) {
+      emitError(
+        context,
+        ParserErrorTypes.UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE,
+        m.index
+      )
+    }
+    content = parseTextData(context, match[0].length, TextModes.ATTRIBUTE_VALUE)
+  }
+
+  return { content, loc: getSelection(context, start) }
+}
+
+function parseInterpolation(
+  context: ParserContext,
+  mode: TextModes
+): ExpressionNode | undefined {
+  const [open, close] = context.delimiters
+  assert(startsWith(context.source, open))
+
+  const closeIndex = context.source.indexOf(close, open.length)
+  if (closeIndex === -1) {
+    emitError(context, ParserErrorTypes.X_MISSING_INTERPOLATION_END)
+    return undefined
+  }
+
+  const start = getCursor(context)
+  advanceBy(context, open.length)
+  const content = parseTextData(context, closeIndex - open.length, mode).trim()
+  advanceBy(context, close.length)
+
+  return {
+    type: NodeTypes.EXPRESSION,
+    content,
+    loc: getSelection(context, start),
+    isStatic: content === ''
+  }
+}
+
+function parseText(context: ParserContext, mode: TextModes): TextNode {
+  assert(context.source.length > 0)
+
+  const [open] = context.delimiters
+  const endIndex = Math.min(
+    ...[
+      context.source.indexOf('<', 1),
+      context.source.indexOf(open, 1),
+      mode === TextModes.CDATA ? context.source.indexOf(']]>') : -1,
+      context.source.length
+    ].filter(n => n !== -1)
+  )
+  assert(endIndex > 0)
+
+  const start = getCursor(context)
+  const content = parseTextData(context, endIndex, mode)
+
+  return {
+    type: NodeTypes.TEXT,
+    content,
+    loc: getSelection(context, start),
+    isEmpty: !content.trim()
+  }
+}
+
+/**
+ * Get text data with a given length from the current location.
+ * This translates HTML entities in the text data.
+ */
+function parseTextData(
+  context: ParserContext,
+  length: number,
+  mode: TextModes
+): string {
+  if (mode === TextModes.RAWTEXT || mode === TextModes.CDATA) {
+    const text = context.source.slice(0, length)
+    advanceBy(context, length)
+    return text
+  }
+
+  // DATA or RCDATA.
+  const end = context.offset + length
+  let text: string = ''
+
+  while (context.offset < end) {
+    const head = /&(?:#x?)?/i.exec(context.source)
+    if (!head || context.offset + head.index >= end) {
+      const remaining = end - context.offset
+      text += context.source.slice(0, remaining)
+      advanceBy(context, remaining)
+      break
+    }
+
+    // Advance to the "&".
+    text += context.source.slice(0, head.index)
+    advanceBy(context, head.index)
+
+    if (head[0] === '&') {
+      // Named character reference.
+      let name = '',
+        value: string | undefined = undefined
+      if (/[0-9a-z]/i.test(context.source[1])) {
+        for (
+          let length = context.maxCRNameLength;
+          !value && length > 0;
+          --length
+        ) {
+          name = context.source.substr(1, length)
+          value = context.namedCharacterReferences[name]
+        }
+        if (value) {
+          const semi = name.endsWith(';')
+          if (
+            mode === TextModes.ATTRIBUTE_VALUE &&
+            !semi &&
+            /[=a-z0-9]/i.test(context.source[1 + name.length] || '')
+          ) {
+            text += '&'
+            text += name
+            advanceBy(context, 1 + name.length)
+          } else {
+            text += value
+            advanceBy(context, 1 + name.length)
+            if (!semi) {
+              emitError(
+                context,
+                ParserErrorTypes.MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE
+              )
+            }
+          }
+        } else {
+          emitError(context, ParserErrorTypes.UNKNOWN_NAMED_CHARACTER_REFERENCE)
+          text += '&'
+          text += name
+          advanceBy(context, 1 + name.length)
+        }
+      } else {
+        text += '&'
+        advanceBy(context, 1)
+      }
+    } else {
+      // Numeric character reference.
+      const hex = head[0] === '&#x'
+      const pattern = hex ? /^&#x([0-9a-f]+);?/i : /^&#([0-9]+);?/
+      const body = pattern.exec(context.source)
+      if (!body) {
+        text += head[0]
+        emitError(
+          context,
+          ParserErrorTypes.ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE
+        )
+        advanceBy(context, head[0].length)
+      } else {
+        // https://html.spec.whatwg.org/multipage/parsing.html#numeric-character-reference-end-state
+        let cp = Number.parseInt(body[1], hex ? 16 : 10)
+        if (cp === 0) {
+          emitError(context, ParserErrorTypes.NULL_CHARACTER_REFERENCE)
+          cp = 0xfffd
+        } else if (cp > 0x10ffff) {
+          emitError(
+            context,
+            ParserErrorTypes.CHARACTER_REFERENCE_OUTSIDE_UNICODE_RANGE
+          )
+          cp = 0xfffd
+        } else if (cp >= 0xd800 && cp <= 0xdfff) {
+          emitError(context, ParserErrorTypes.SURROGATE_CHARACTER_REFERENCE)
+          cp = 0xfffd
+        } else if ((cp >= 0xfdd0 && cp <= 0xfdef) || (cp & 0xfffe) === 0xfffe) {
+          emitError(context, ParserErrorTypes.NONCHARACTER_CHARACTER_REFERENCE)
+        } else if (
+          (cp >= 0x01 && cp <= 0x08) ||
+          cp === 0x0b ||
+          (cp >= 0x0d && cp <= 0x1f) ||
+          (cp >= 0x7f && cp <= 0x9f)
+        ) {
+          emitError(context, ParserErrorTypes.CONTROL_CHARACTER_REFERENCE)
+          cp = CCR_REPLACEMENTS[cp] || cp
+        }
+        text += String.fromCodePoint(cp)
+        advanceBy(context, body[0].length)
+        if (!body![0].endsWith(';')) {
+          emitError(
+            context,
+            ParserErrorTypes.MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE
+          )
+        }
+      }
+    }
+  }
+
+  return text
+}
+
+// https://html.spec.whatwg.org/multipage/parsing.html#numeric-character-reference-end-state
+const CCR_REPLACEMENTS: { [key: number]: number | undefined } = {
+  0x80: 0x20ac,
+  0x82: 0x201a,
+  0x83: 0x0192,
+  0x84: 0x201e,
+  0x85: 0x2026,
+  0x86: 0x2020,
+  0x87: 0x2021,
+  0x88: 0x02c6,
+  0x89: 0x2030,
+  0x8a: 0x0160,
+  0x8b: 0x2039,
+  0x8c: 0x0152,
+  0x8e: 0x017d,
+  0x91: 0x2018,
+  0x92: 0x2019,
+  0x93: 0x201c,
+  0x94: 0x201d,
+  0x95: 0x2022,
+  0x96: 0x2013,
+  0x97: 0x2014,
+  0x98: 0x02dc,
+  0x99: 0x2122,
+  0x9a: 0x0161,
+  0x9b: 0x203a,
+  0x9c: 0x0153,
+  0x9e: 0x017e,
+  0x9f: 0x0178
+}
diff --git a/packages/compiler-core/src/parserOptionsMinimal.ts b/packages/compiler-core/src/parserOptionsMinimal.ts
new file mode 100644 (file)
index 0000000..e6f0576
--- /dev/null
@@ -0,0 +1,112 @@
+import { TextModes, ParserOptions } from './parser'
+import { ElementNode, Namespaces, Position, Node } from './ast'
+import { ParserErrorTypes } from './errorTypes'
+
+export const parserOptionsMinimal: ParserOptions = {
+  delimiters: [`{{`, `}}`],
+  ignoreSpaces: true,
+
+  getNamespace(tag: string, parent: ElementNode | undefined): Namespaces {
+    const ns = parent ? parent.ns : Namespaces.HTML
+    if (ns === Namespaces.HTML) {
+      if (tag === 'svg') {
+        return Namespaces.SVG
+      }
+      if (tag === 'math') {
+        return Namespaces.MATH_ML
+      }
+    }
+    return ns
+  },
+
+  getTextMode(tag: string, ns: Namespaces): TextModes {
+    if (ns === Namespaces.HTML) {
+      if (/^textarea$/i.test(tag)) {
+        return TextModes.RCDATA
+      }
+      if (/^(?:style|script)$/i.test(tag)) {
+        return TextModes.RAWTEXT
+      }
+    }
+    return TextModes.DATA
+  },
+
+  isVoidTag(tag: string): boolean {
+    return /^(?:area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/i.test(
+      tag
+    )
+  },
+
+  namedCharacterReferences: {
+    'gt;': '>',
+    'lt;': '<',
+    'amp;': '&',
+    'apos;': "'",
+    'quot;': '"'
+  },
+
+  onError(code: ParserErrorTypes, loc: Position): void {
+    const error: any = new SyntaxError(
+      `${messages[code]} (${loc.line}:${loc.column})`
+    )
+    error.code = code
+    error.loc = loc
+    throw error
+  },
+
+  transform(node: Node): Node {
+    return node
+  }
+}
+
+const messages: { [code: number]: string } = {
+  [ParserErrorTypes.ABRUPT_CLOSING_OF_EMPTY_COMMENT]: 'Illegal comment.',
+  [ParserErrorTypes.ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE]:
+    'Illegal numeric character reference: invalid character.',
+  [ParserErrorTypes.CDATA_IN_HTML_CONTENT]:
+    'CDATA section is allowed only in XML context.',
+  [ParserErrorTypes.CHARACTER_REFERENCE_OUTSIDE_UNICODE_RANGE]:
+    'Illegal numeric character reference: too big.',
+  [ParserErrorTypes.CONTROL_CHARACTER_REFERENCE]:
+    'Illegal numeric character reference: control character.',
+  [ParserErrorTypes.DUPLICATE_ATTRIBUTE]: 'Duplicate attribute.',
+  [ParserErrorTypes.END_TAG_WITH_ATTRIBUTES]: 'End tag cannot have attributes.',
+  [ParserErrorTypes.END_TAG_WITH_TRAILING_SOLIDUS]: "Illegal '/' in tags.",
+  [ParserErrorTypes.EOF_BEFORE_TAG_NAME]: 'Unexpected EOF in tag.',
+  [ParserErrorTypes.EOF_IN_CDATA]: 'Unexpected EOF in CDATA section.',
+  [ParserErrorTypes.EOF_IN_COMMENT]: 'Unexpected EOF in comment.',
+  [ParserErrorTypes.EOF_IN_SCRIPT_HTML_COMMENT_LIKE_TEXT]:
+    'Unexpected EOF in script.',
+  [ParserErrorTypes.EOF_IN_TAG]: 'Unexpected EOF in tag.',
+  [ParserErrorTypes.INCORRECTLY_CLOSED_COMMENT]: 'Incorrectly closed comment.',
+  [ParserErrorTypes.INCORRECTLY_OPENED_COMMENT]: 'Incorrectly opened comment.',
+  [ParserErrorTypes.INVALID_FIRST_CHARACTER_OF_TAG_NAME]:
+    "Illegal tag name. Use '&lt;' to print '<'.",
+  [ParserErrorTypes.MISSING_ATTRIBUTE_VALUE]: 'Attribute value was expected.',
+  [ParserErrorTypes.MISSING_END_TAG_NAME]: 'End tag name was expected.',
+  [ParserErrorTypes.MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE]:
+    'Semicolon was expected.',
+  [ParserErrorTypes.MISSING_WHITESPACE_BETWEEN_ATTRIBUTES]:
+    'Whitespace was expected.',
+  [ParserErrorTypes.NESTED_COMMENT]: "Unexpected '<!--' in comment.",
+  [ParserErrorTypes.NONCHARACTER_CHARACTER_REFERENCE]:
+    'Illegal numeric character reference: non character.',
+  [ParserErrorTypes.NULL_CHARACTER_REFERENCE]:
+    'Illegal numeric character reference: null character.',
+  [ParserErrorTypes.SURROGATE_CHARACTER_REFERENCE]:
+    'Illegal numeric character reference: non-pair surrogate.',
+  [ParserErrorTypes.UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME]:
+    'Attribute name cannot contain U+0022 ("), U+0027 (\'), and U+003C (<).',
+  [ParserErrorTypes.UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE]:
+    'Unquoted attribute value cannot contain U+0022 ("), U+0027 (\'), U+003C (<), U+003D (=), and U+0060 (`).',
+  [ParserErrorTypes.UNEXPECTED_EQUALS_SIGN_BEFORE_ATTRIBUTE_NAME]:
+    "Attribute name cannot start with '='.",
+  [ParserErrorTypes.UNEXPECTED_QUESTION_MARK_INSTEAD_OF_TAG_NAME]:
+    "'<?' is allowed only in XML context.",
+  [ParserErrorTypes.UNEXPECTED_SOLIDUS_IN_TAG]: "Illegal '/' in tags.",
+  [ParserErrorTypes.UNKNOWN_NAMED_CHARACTER_REFERENCE]: 'Unknown entity name.',
+  [ParserErrorTypes.X_INVALID_END_TAG]: 'Invalid end tag.',
+  [ParserErrorTypes.X_MISSING_END_TAG]: 'End tag was not found.',
+  [ParserErrorTypes.X_MISSING_INTERPOLATION_END]:
+    'Interpolation end sign was not found.'
+}
diff --git a/packages/compiler-core/src/parserOptionsStandard.ts b/packages/compiler-core/src/parserOptionsStandard.ts
new file mode 100644 (file)
index 0000000..c0547cd
--- /dev/null
@@ -0,0 +1,2307 @@
+import { TextModes, ParserOptions } from './parser'
+import { ElementNode, Namespaces, NodeTypes } from './ast'
+import { parserOptionsMinimal } from './parserOptionsMinimal'
+
+export const parserOptionsStandard: ParserOptions = {
+  // extends the minimal options with more spec-compliant overrides
+  ...parserOptionsMinimal,
+
+  // https://html.spec.whatwg.org/multipage/parsing.html#tree-construction-dispatcher
+  getNamespace(tag: string, parent: ElementNode | undefined): Namespaces {
+    let ns = parent ? parent.ns : Namespaces.HTML
+
+    if (parent && ns === Namespaces.MATH_ML) {
+      if (parent.tag === 'annotation-xml') {
+        if (tag === 'svg') {
+          return Namespaces.SVG
+        }
+        if (
+          parent.props.some(
+            a =>
+              a.type === NodeTypes.ATTRIBUTE &&
+              a.name === 'encoding' &&
+              a.value != null &&
+              (a.value.content === 'text/html' ||
+                a.value.content === 'application/xhtml+xml')
+          )
+        ) {
+          ns = Namespaces.HTML
+        }
+      } else if (
+        /^m(?:[ions]|text)$/.test(parent.tag) &&
+        tag !== 'mglyph' &&
+        tag !== 'malignmark'
+      ) {
+        ns = Namespaces.HTML
+      }
+    } else if (parent && ns === Namespaces.SVG) {
+      if (
+        parent.tag === 'foreignObject' ||
+        parent.tag === 'desc' ||
+        parent.tag === 'title'
+      ) {
+        ns = Namespaces.HTML
+      }
+    }
+
+    if (ns === Namespaces.HTML) {
+      if (tag === 'svg') {
+        return Namespaces.SVG
+      }
+      if (tag === 'math') {
+        return Namespaces.MATH_ML
+      }
+    }
+    return ns
+  },
+
+  // https://html.spec.whatwg.org/multipage/parsing.html#parsing-html-fragments
+  getTextMode(tag: string, ns: Namespaces): TextModes {
+    if (ns === Namespaces.HTML) {
+      if (/^(?:title|textarea)$/i.test(tag)) {
+        return TextModes.RCDATA
+      }
+      if (
+        /^(?:style|xmp|iframe|noembed|noframes|script|noscript)$/i.test(tag)
+      ) {
+        return TextModes.RAWTEXT
+      }
+    }
+    return TextModes.DATA
+  },
+
+  // https://html.spec.whatwg.org/multipage/named-characters.html#named-character-references
+  namedCharacterReferences: {
+    GT: '>',
+    gt: '>',
+    LT: '<',
+    lt: '<',
+    'ac;': '∾',
+    'af;': '⁡',
+    AMP: '&',
+    amp: '&',
+    'ap;': '≈',
+    'DD;': 'ⅅ',
+    'dd;': 'ⅆ',
+    deg: '°',
+    'ee;': 'ⅇ',
+    'eg;': '⪚',
+    'el;': '⪙',
+    ETH: 'Ð',
+    eth: 'ð',
+    'gE;': '≧',
+    'ge;': '≥',
+    'Gg;': '⋙',
+    'gg;': '≫',
+    'gl;': '≷',
+    'GT;': '>',
+    'Gt;': '≫',
+    'gt;': '>',
+    'ic;': '⁣',
+    'ii;': 'ⅈ',
+    'Im;': 'ℑ',
+    'in;': '∈',
+    'it;': '⁢',
+    'lE;': '≦',
+    'le;': '≤',
+    'lg;': '≶',
+    'Ll;': '⋘',
+    'll;': '≪',
+    'LT;': '<',
+    'Lt;': '≪',
+    'lt;': '<',
+    'mp;': '∓',
+    'Mu;': 'Μ',
+    'mu;': 'μ',
+    'ne;': '≠',
+    'ni;': '∋',
+    not: '¬',
+    'Nu;': 'Ν',
+    'nu;': 'ν',
+    'Or;': '⩔',
+    'or;': '∨',
+    'oS;': 'Ⓢ',
+    'Pi;': 'Π',
+    'pi;': 'π',
+    'pm;': '±',
+    'Pr;': '⪻',
+    'pr;': '≺',
+    'Re;': 'ℜ',
+    REG: '®',
+    reg: '®',
+    'rx;': '℞',
+    'Sc;': '⪼',
+    'sc;': '≻',
+    shy: '­',
+    uml: '¨',
+    'wp;': '℘',
+    'wr;': '≀',
+    'Xi;': 'Ξ',
+    'xi;': 'ξ',
+    yen: '¥',
+    'acd;': '∿',
+    'acE;': '∾̳',
+    'Acy;': 'А',
+    'acy;': 'а',
+    'Afr;': '𝔄',
+    'afr;': '𝔞',
+    'AMP;': '&',
+    'amp;': '&',
+    'And;': '⩓',
+    'and;': '∧',
+    'ang;': '∠',
+    'apE;': '⩰',
+    'ape;': '≊',
+    'ast;': '*',
+    Auml: 'Ä',
+    auml: 'ä',
+    'Bcy;': 'Б',
+    'bcy;': 'б',
+    'Bfr;': '𝔅',
+    'bfr;': '𝔟',
+    'bne;': '=⃥',
+    'bot;': '⊥',
+    'Cap;': '⋒',
+    'cap;': '∩',
+    cent: '¢',
+    'Cfr;': 'ℭ',
+    'cfr;': '𝔠',
+    'Chi;': 'Χ',
+    'chi;': 'χ',
+    'cir;': '○',
+    COPY: '©',
+    copy: '©',
+    'Cup;': '⋓',
+    'cup;': '∪',
+    'Dcy;': 'Д',
+    'dcy;': 'д',
+    'deg;': '°',
+    'Del;': '∇',
+    'Dfr;': '𝔇',
+    'dfr;': '𝔡',
+    'die;': '¨',
+    'div;': '÷',
+    'Dot;': '¨',
+    'dot;': '˙',
+    'Ecy;': 'Э',
+    'ecy;': 'э',
+    'Efr;': '𝔈',
+    'efr;': '𝔢',
+    'egs;': '⪖',
+    'ell;': 'ℓ',
+    'els;': '⪕',
+    'ENG;': 'Ŋ',
+    'eng;': 'ŋ',
+    'Eta;': 'Η',
+    'eta;': 'η',
+    'ETH;': 'Ð',
+    'eth;': 'ð',
+    Euml: 'Ë',
+    euml: 'ë',
+    'Fcy;': 'Ф',
+    'fcy;': 'ф',
+    'Ffr;': '𝔉',
+    'ffr;': '𝔣',
+    'gap;': '⪆',
+    'Gcy;': 'Г',
+    'gcy;': 'г',
+    'gEl;': '⪌',
+    'gel;': '⋛',
+    'geq;': '≥',
+    'ges;': '⩾',
+    'Gfr;': '𝔊',
+    'gfr;': '𝔤',
+    'ggg;': '⋙',
+    'gla;': '⪥',
+    'glE;': '⪒',
+    'glj;': '⪤',
+    'gnE;': '≩',
+    'gne;': '⪈',
+    'Hat;': '^',
+    'Hfr;': 'ℌ',
+    'hfr;': '𝔥',
+    'Icy;': 'И',
+    'icy;': 'и',
+    'iff;': '⇔',
+    'Ifr;': 'ℑ',
+    'ifr;': '𝔦',
+    'Int;': '∬',
+    'int;': '∫',
+    Iuml: 'Ï',
+    iuml: 'ï',
+    'Jcy;': 'Й',
+    'jcy;': 'й',
+    'Jfr;': '𝔍',
+    'jfr;': '𝔧',
+    'Kcy;': 'К',
+    'kcy;': 'к',
+    'Kfr;': '𝔎',
+    'kfr;': '𝔨',
+    'lap;': '⪅',
+    'lat;': '⪫',
+    'Lcy;': 'Л',
+    'lcy;': 'л',
+    'lEg;': '⪋',
+    'leg;': '⋚',
+    'leq;': '≤',
+    'les;': '⩽',
+    'Lfr;': '𝔏',
+    'lfr;': '𝔩',
+    'lgE;': '⪑',
+    'lnE;': '≨',
+    'lne;': '⪇',
+    'loz;': '◊',
+    'lrm;': '‎',
+    'Lsh;': '↰',
+    'lsh;': '↰',
+    macr: '¯',
+    'Map;': '⤅',
+    'map;': '↦',
+    'Mcy;': 'М',
+    'mcy;': 'м',
+    'Mfr;': '𝔐',
+    'mfr;': '𝔪',
+    'mho;': '℧',
+    'mid;': '∣',
+    'nap;': '≉',
+    nbsp: ' ',
+    'Ncy;': 'Н',
+    'ncy;': 'н',
+    'Nfr;': '𝔑',
+    'nfr;': '𝔫',
+    'ngE;': '≧̸',
+    'nge;': '≱',
+    'nGg;': '⋙̸',
+    'nGt;': '≫⃒',
+    'ngt;': '≯',
+    'nis;': '⋼',
+    'niv;': '∋',
+    'nlE;': '≦̸',
+    'nle;': '≰',
+    'nLl;': '⋘̸',
+    'nLt;': '≪⃒',
+    'nlt;': '≮',
+    'Not;': '⫬',
+    'not;': '¬',
+    'npr;': '⊀',
+    'nsc;': '⊁',
+    'num;': '#',
+    'Ocy;': 'О',
+    'ocy;': 'о',
+    'Ofr;': '𝔒',
+    'ofr;': '𝔬',
+    'ogt;': '⧁',
+    'ohm;': 'Ω',
+    'olt;': '⧀',
+    'ord;': '⩝',
+    ordf: 'ª',
+    ordm: 'º',
+    'orv;': '⩛',
+    Ouml: 'Ö',
+    ouml: 'ö',
+    'par;': '∥',
+    para: '¶',
+    'Pcy;': 'П',
+    'pcy;': 'п',
+    'Pfr;': '𝔓',
+    'pfr;': '𝔭',
+    'Phi;': 'Φ',
+    'phi;': 'φ',
+    'piv;': 'ϖ',
+    'prE;': '⪳',
+    'pre;': '⪯',
+    'Psi;': 'Ψ',
+    'psi;': 'ψ',
+    'Qfr;': '𝔔',
+    'qfr;': '𝔮',
+    QUOT: '"',
+    quot: '"',
+    'Rcy;': 'Р',
+    'rcy;': 'р',
+    'REG;': '®',
+    'reg;': '®',
+    'Rfr;': 'ℜ',
+    'rfr;': '𝔯',
+    'Rho;': 'Ρ',
+    'rho;': 'ρ',
+    'rlm;': '‏',
+    'Rsh;': '↱',
+    'rsh;': '↱',
+    'scE;': '⪴',
+    'sce;': '⪰',
+    'Scy;': 'С',
+    'scy;': 'с',
+    sect: '§',
+    'Sfr;': '𝔖',
+    'sfr;': '𝔰',
+    'shy;': '­',
+    'sim;': '∼',
+    'smt;': '⪪',
+    'sol;': '/',
+    'squ;': '□',
+    'Sub;': '⋐',
+    'sub;': '⊂',
+    'Sum;': '∑',
+    'sum;': '∑',
+    'Sup;': '⋑',
+    'sup;': '⊃',
+    sup1: '¹',
+    sup2: '²',
+    sup3: '³',
+    'Tab;': '\t',
+    'Tau;': 'Τ',
+    'tau;': 'τ',
+    'Tcy;': 'Т',
+    'tcy;': 'т',
+    'Tfr;': '𝔗',
+    'tfr;': '𝔱',
+    'top;': '⊤',
+    'Ucy;': 'У',
+    'ucy;': 'у',
+    'Ufr;': '𝔘',
+    'ufr;': '𝔲',
+    'uml;': '¨',
+    Uuml: 'Ü',
+    uuml: 'ü',
+    'Vcy;': 'В',
+    'vcy;': 'в',
+    'Vee;': '⋁',
+    'vee;': '∨',
+    'Vfr;': '𝔙',
+    'vfr;': '𝔳',
+    'Wfr;': '𝔚',
+    'wfr;': '𝔴',
+    'Xfr;': '𝔛',
+    'xfr;': '𝔵',
+    'Ycy;': 'Ы',
+    'ycy;': 'ы',
+    'yen;': '¥',
+    'Yfr;': '𝔜',
+    'yfr;': '𝔶',
+    yuml: 'ÿ',
+    'Zcy;': 'З',
+    'zcy;': 'з',
+    'Zfr;': 'ℨ',
+    'zfr;': '𝔷',
+    'zwj;': '‍',
+    Acirc: 'Â',
+    acirc: 'â',
+    acute: '´',
+    AElig: 'Æ',
+    aelig: 'æ',
+    'andd;': '⩜',
+    'andv;': '⩚',
+    'ange;': '⦤',
+    'Aopf;': '𝔸',
+    'aopf;': '𝕒',
+    'apid;': '≋',
+    'apos;': "'",
+    Aring: 'Å',
+    aring: 'å',
+    'Ascr;': '𝒜',
+    'ascr;': '𝒶',
+    'Auml;': 'Ä',
+    'auml;': 'ä',
+    'Barv;': '⫧',
+    'bbrk;': '⎵',
+    'Beta;': 'Β',
+    'beta;': 'β',
+    'beth;': 'ℶ',
+    'bNot;': '⫭',
+    'bnot;': '⌐',
+    'Bopf;': '𝔹',
+    'bopf;': '𝕓',
+    'boxH;': '═',
+    'boxh;': '─',
+    'boxV;': '║',
+    'boxv;': '│',
+    'Bscr;': 'ℬ',
+    'bscr;': '𝒷',
+    'bsim;': '∽',
+    'bsol;': '\\',
+    'bull;': '•',
+    'bump;': '≎',
+    'caps;': '∩︀',
+    'Cdot;': 'Ċ',
+    'cdot;': 'ċ',
+    cedil: '¸',
+    'cent;': '¢',
+    'CHcy;': 'Ч',
+    'chcy;': 'ч',
+    'circ;': 'ˆ',
+    'cirE;': '⧃',
+    'cire;': '≗',
+    'comp;': '∁',
+    'cong;': '≅',
+    'Copf;': 'ℂ',
+    'copf;': '𝕔',
+    'COPY;': '©',
+    'copy;': '©',
+    'Cscr;': '𝒞',
+    'cscr;': '𝒸',
+    'csub;': '⫏',
+    'csup;': '⫐',
+    'cups;': '∪︀',
+    'Darr;': '↡',
+    'dArr;': '⇓',
+    'darr;': '↓',
+    'dash;': '‐',
+    'dHar;': '⥥',
+    'diam;': '⋄',
+    'DJcy;': 'Ђ',
+    'djcy;': 'ђ',
+    'Dopf;': '𝔻',
+    'dopf;': '𝕕',
+    'Dscr;': '𝒟',
+    'dscr;': '𝒹',
+    'DScy;': 'Ѕ',
+    'dscy;': 'ѕ',
+    'dsol;': '⧶',
+    'dtri;': '▿',
+    'DZcy;': 'Џ',
+    'dzcy;': 'џ',
+    'ecir;': '≖',
+    Ecirc: 'Ê',
+    ecirc: 'ê',
+    'Edot;': 'Ė',
+    'eDot;': '≑',
+    'edot;': 'ė',
+    'emsp;': ' ',
+    'ensp;': ' ',
+    'Eopf;': '𝔼',
+    'eopf;': '𝕖',
+    'epar;': '⋕',
+    'epsi;': 'ε',
+    'Escr;': 'ℰ',
+    'escr;': 'ℯ',
+    'Esim;': '⩳',
+    'esim;': '≂',
+    'Euml;': 'Ë',
+    'euml;': 'ë',
+    'euro;': '€',
+    'excl;': '!',
+    'flat;': '♭',
+    'fnof;': 'ƒ',
+    'Fopf;': '𝔽',
+    'fopf;': '𝕗',
+    'fork;': '⋔',
+    'Fscr;': 'ℱ',
+    'fscr;': '𝒻',
+    'Gdot;': 'Ġ',
+    'gdot;': 'ġ',
+    'geqq;': '≧',
+    'gesl;': '⋛︀',
+    'GJcy;': 'Ѓ',
+    'gjcy;': 'ѓ',
+    'gnap;': '⪊',
+    'gneq;': '⪈',
+    'Gopf;': '𝔾',
+    'gopf;': '𝕘',
+    'Gscr;': '𝒢',
+    'gscr;': 'ℊ',
+    'gsim;': '≳',
+    'gtcc;': '⪧',
+    'gvnE;': '≩︀',
+    'half;': '½',
+    'hArr;': '⇔',
+    'harr;': '↔',
+    'hbar;': 'ℏ',
+    'Hopf;': 'ℍ',
+    'hopf;': '𝕙',
+    'Hscr;': 'ℋ',
+    'hscr;': '𝒽',
+    Icirc: 'Î',
+    icirc: 'î',
+    'Idot;': 'İ',
+    'IEcy;': 'Е',
+    'iecy;': 'е',
+    iexcl: '¡',
+    'imof;': '⊷',
+    'IOcy;': 'Ё',
+    'iocy;': 'ё',
+    'Iopf;': '𝕀',
+    'iopf;': '𝕚',
+    'Iota;': 'Ι',
+    'iota;': 'ι',
+    'Iscr;': 'ℐ',
+    'iscr;': '𝒾',
+    'isin;': '∈',
+    'Iuml;': 'Ï',
+    'iuml;': 'ï',
+    'Jopf;': '𝕁',
+    'jopf;': '𝕛',
+    'Jscr;': '𝒥',
+    'jscr;': '𝒿',
+    'KHcy;': 'Х',
+    'khcy;': 'х',
+    'KJcy;': 'Ќ',
+    'kjcy;': 'ќ',
+    'Kopf;': '𝕂',
+    'kopf;': '𝕜',
+    'Kscr;': '𝒦',
+    'kscr;': '𝓀',
+    'Lang;': '⟪',
+    'lang;': '⟨',
+    laquo: '«',
+    'Larr;': '↞',
+    'lArr;': '⇐',
+    'larr;': '←',
+    'late;': '⪭',
+    'lcub;': '{',
+    'ldca;': '⤶',
+    'ldsh;': '↲',
+    'leqq;': '≦',
+    'lesg;': '⋚︀',
+    'lHar;': '⥢',
+    'LJcy;': 'Љ',
+    'ljcy;': 'љ',
+    'lnap;': '⪉',
+    'lneq;': '⪇',
+    'Lopf;': '𝕃',
+    'lopf;': '𝕝',
+    'lozf;': '⧫',
+    'lpar;': '(',
+    'Lscr;': 'ℒ',
+    'lscr;': '𝓁',
+    'lsim;': '≲',
+    'lsqb;': '[',
+    'ltcc;': '⪦',
+    'ltri;': '◃',
+    'lvnE;': '≨︀',
+    'macr;': '¯',
+    'male;': '♂',
+    'malt;': '✠',
+    micro: 'µ',
+    'mlcp;': '⫛',
+    'mldr;': '…',
+    'Mopf;': '𝕄',
+    'mopf;': '𝕞',
+    'Mscr;': 'ℳ',
+    'mscr;': '𝓂',
+    'nang;': '∠⃒',
+    'napE;': '⩰̸',
+    'nbsp;': ' ',
+    'ncap;': '⩃',
+    'ncup;': '⩂',
+    'ngeq;': '≱',
+    'nges;': '⩾̸',
+    'ngtr;': '≯',
+    'nGtv;': '≫̸',
+    'nisd;': '⋺',
+    'NJcy;': 'Њ',
+    'njcy;': 'њ',
+    'nldr;': '‥',
+    'nleq;': '≰',
+    'nles;': '⩽̸',
+    'nLtv;': '≪̸',
+    'nmid;': '∤',
+    'Nopf;': 'ℕ',
+    'nopf;': '𝕟',
+    'npar;': '∦',
+    'npre;': '⪯̸',
+    'nsce;': '⪰̸',
+    'Nscr;': '𝒩',
+    'nscr;': '𝓃',
+    'nsim;': '≁',
+    'nsub;': '⊄',
+    'nsup;': '⊅',
+    'ntgl;': '≹',
+    'ntlg;': '≸',
+    'nvap;': '≍⃒',
+    'nvge;': '≥⃒',
+    'nvgt;': '>⃒',
+    'nvle;': '≤⃒',
+    'nvlt;': '<⃒',
+    'oast;': '⊛',
+    'ocir;': '⊚',
+    Ocirc: 'Ô',
+    ocirc: 'ô',
+    'odiv;': '⨸',
+    'odot;': '⊙',
+    'ogon;': '˛',
+    'oint;': '∮',
+    'omid;': '⦶',
+    'Oopf;': '𝕆',
+    'oopf;': '𝕠',
+    'opar;': '⦷',
+    'ordf;': 'ª',
+    'ordm;': 'º',
+    'oror;': '⩖',
+    'Oscr;': '𝒪',
+    'oscr;': 'ℴ',
+    'osol;': '⊘',
+    'Ouml;': 'Ö',
+    'ouml;': 'ö',
+    'para;': '¶',
+    'part;': '∂',
+    'perp;': '⊥',
+    'phiv;': 'ϕ',
+    'plus;': '+',
+    'Popf;': 'ℙ',
+    'popf;': '𝕡',
+    pound: '£',
+    'prap;': '⪷',
+    'prec;': '≺',
+    'prnE;': '⪵',
+    'prod;': '∏',
+    'prop;': '∝',
+    'Pscr;': '𝒫',
+    'pscr;': '𝓅',
+    'qint;': '⨌',
+    'Qopf;': 'ℚ',
+    'qopf;': '𝕢',
+    'Qscr;': '𝒬',
+    'qscr;': '𝓆',
+    'QUOT;': '"',
+    'quot;': '"',
+    'race;': '∽̱',
+    'Rang;': '⟫',
+    'rang;': '⟩',
+    raquo: '»',
+    'Rarr;': '↠',
+    'rArr;': '⇒',
+    'rarr;': '→',
+    'rcub;': '}',
+    'rdca;': '⤷',
+    'rdsh;': '↳',
+    'real;': 'ℜ',
+    'rect;': '▭',
+    'rHar;': '⥤',
+    'rhov;': 'ϱ',
+    'ring;': '˚',
+    'Ropf;': 'ℝ',
+    'ropf;': '𝕣',
+    'rpar;': ')',
+    'Rscr;': 'ℛ',
+    'rscr;': '𝓇',
+    'rsqb;': ']',
+    'rtri;': '▹',
+    'scap;': '⪸',
+    'scnE;': '⪶',
+    'sdot;': '⋅',
+    'sect;': '§',
+    'semi;': ';',
+    'sext;': '✶',
+    'SHcy;': 'Ш',
+    'shcy;': 'ш',
+    'sime;': '≃',
+    'simg;': '⪞',
+    'siml;': '⪝',
+    'smid;': '∣',
+    'smte;': '⪬',
+    'solb;': '⧄',
+    'Sopf;': '𝕊',
+    'sopf;': '𝕤',
+    'spar;': '∥',
+    'Sqrt;': '√',
+    'squf;': '▪',
+    'Sscr;': '𝒮',
+    'sscr;': '𝓈',
+    'Star;': '⋆',
+    'star;': '☆',
+    'subE;': '⫅',
+    'sube;': '⊆',
+    'succ;': '≻',
+    'sung;': '♪',
+    'sup1;': '¹',
+    'sup2;': '²',
+    'sup3;': '³',
+    'supE;': '⫆',
+    'supe;': '⊇',
+    szlig: 'ß',
+    'tbrk;': '⎴',
+    'tdot;': '⃛',
+    THORN: 'Þ',
+    thorn: 'þ',
+    times: '×',
+    'tint;': '∭',
+    'toea;': '⤨',
+    'Topf;': '𝕋',
+    'topf;': '𝕥',
+    'tosa;': '⤩',
+    'trie;': '≜',
+    'Tscr;': '𝒯',
+    'tscr;': '𝓉',
+    'TScy;': 'Ц',
+    'tscy;': 'ц',
+    'Uarr;': '↟',
+    'uArr;': '⇑',
+    'uarr;': '↑',
+    Ucirc: 'Û',
+    ucirc: 'û',
+    'uHar;': '⥣',
+    'Uopf;': '𝕌',
+    'uopf;': '𝕦',
+    'Upsi;': 'ϒ',
+    'upsi;': 'υ',
+    'Uscr;': '𝒰',
+    'uscr;': '𝓊',
+    'utri;': '▵',
+    'Uuml;': 'Ü',
+    'uuml;': 'ü',
+    'vArr;': '⇕',
+    'varr;': '↕',
+    'Vbar;': '⫫',
+    'vBar;': '⫨',
+    'Vert;': '‖',
+    'vert;': '|',
+    'Vopf;': '𝕍',
+    'vopf;': '𝕧',
+    'Vscr;': '𝒱',
+    'vscr;': '𝓋',
+    'Wopf;': '𝕎',
+    'wopf;': '𝕨',
+    'Wscr;': '𝒲',
+    'wscr;': '𝓌',
+    'xcap;': '⋂',
+    'xcup;': '⋃',
+    'xmap;': '⟼',
+    'xnis;': '⋻',
+    'Xopf;': '𝕏',
+    'xopf;': '𝕩',
+    'Xscr;': '𝒳',
+    'xscr;': '𝓍',
+    'xvee;': '⋁',
+    'YAcy;': 'Я',
+    'yacy;': 'я',
+    'YIcy;': 'Ї',
+    'yicy;': 'ї',
+    'Yopf;': '𝕐',
+    'yopf;': '𝕪',
+    'Yscr;': '𝒴',
+    'yscr;': '𝓎',
+    'YUcy;': 'Ю',
+    'yucy;': 'ю',
+    'Yuml;': 'Ÿ',
+    'yuml;': 'ÿ',
+    'Zdot;': 'Ż',
+    'zdot;': 'ż',
+    'Zeta;': 'Ζ',
+    'zeta;': 'ζ',
+    'ZHcy;': 'Ж',
+    'zhcy;': 'ж',
+    'Zopf;': 'ℤ',
+    'zopf;': '𝕫',
+    'Zscr;': '𝒵',
+    'zscr;': '𝓏',
+    'zwnj;': '‌',
+    Aacute: 'Á',
+    aacute: 'á',
+    'Acirc;': 'Â',
+    'acirc;': 'â',
+    'acute;': '´',
+    'AElig;': 'Æ',
+    'aelig;': 'æ',
+    Agrave: 'À',
+    agrave: 'à',
+    'aleph;': 'ℵ',
+    'Alpha;': 'Α',
+    'alpha;': 'α',
+    'Amacr;': 'Ā',
+    'amacr;': 'ā',
+    'amalg;': '⨿',
+    'angle;': '∠',
+    'angrt;': '∟',
+    'angst;': 'Å',
+    'Aogon;': 'Ą',
+    'aogon;': 'ą',
+    'Aring;': 'Å',
+    'aring;': 'å',
+    'asymp;': '≈',
+    Atilde: 'Ã',
+    atilde: 'ã',
+    'awint;': '⨑',
+    'bcong;': '≌',
+    'bdquo;': '„',
+    'bepsi;': '϶',
+    'blank;': '␣',
+    'blk12;': '▒',
+    'blk14;': '░',
+    'blk34;': '▓',
+    'block;': '█',
+    'boxDL;': '╗',
+    'boxDl;': '╖',
+    'boxdL;': '╕',
+    'boxdl;': '┐',
+    'boxDR;': '╔',
+    'boxDr;': '╓',
+    'boxdR;': '╒',
+    'boxdr;': '┌',
+    'boxHD;': '╦',
+    'boxHd;': '╤',
+    'boxhD;': '╥',
+    'boxhd;': '┬',
+    'boxHU;': '╩',
+    'boxHu;': '╧',
+    'boxhU;': '╨',
+    'boxhu;': '┴',
+    'boxUL;': '╝',
+    'boxUl;': '╜',
+    'boxuL;': '╛',
+    'boxul;': '┘',
+    'boxUR;': '╚',
+    'boxUr;': '╙',
+    'boxuR;': '╘',
+    'boxur;': '└',
+    'boxVH;': '╬',
+    'boxVh;': '╫',
+    'boxvH;': '╪',
+    'boxvh;': '┼',
+    'boxVL;': '╣',
+    'boxVl;': '╢',
+    'boxvL;': '╡',
+    'boxvl;': '┤',
+    'boxVR;': '╠',
+    'boxVr;': '╟',
+    'boxvR;': '╞',
+    'boxvr;': '├',
+    'Breve;': '˘',
+    'breve;': '˘',
+    brvbar: '¦',
+    'bsemi;': '⁏',
+    'bsime;': '⋍',
+    'bsolb;': '⧅',
+    'bumpE;': '⪮',
+    'bumpe;': '≏',
+    'caret;': '⁁',
+    'caron;': 'ˇ',
+    'ccaps;': '⩍',
+    Ccedil: 'Ç',
+    ccedil: 'ç',
+    'Ccirc;': 'Ĉ',
+    'ccirc;': 'ĉ',
+    'ccups;': '⩌',
+    'cedil;': '¸',
+    'check;': '✓',
+    'clubs;': '♣',
+    'Colon;': '∷',
+    'colon;': ':',
+    'comma;': ',',
+    'crarr;': '↵',
+    'Cross;': '⨯',
+    'cross;': '✗',
+    'csube;': '⫑',
+    'csupe;': '⫒',
+    'ctdot;': '⋯',
+    'cuepr;': '⋞',
+    'cuesc;': '⋟',
+    'cupor;': '⩅',
+    curren: '¤',
+    'cuvee;': '⋎',
+    'cuwed;': '⋏',
+    'cwint;': '∱',
+    'Dashv;': '⫤',
+    'dashv;': '⊣',
+    'dblac;': '˝',
+    'ddarr;': '⇊',
+    'Delta;': 'Δ',
+    'delta;': 'δ',
+    'dharl;': '⇃',
+    'dharr;': '⇂',
+    'diams;': '♦',
+    'disin;': '⋲',
+    divide: '÷',
+    'doteq;': '≐',
+    'dtdot;': '⋱',
+    'dtrif;': '▾',
+    'duarr;': '⇵',
+    'duhar;': '⥯',
+    Eacute: 'É',
+    eacute: 'é',
+    'Ecirc;': 'Ê',
+    'ecirc;': 'ê',
+    'eDDot;': '⩷',
+    'efDot;': '≒',
+    Egrave: 'È',
+    egrave: 'è',
+    'Emacr;': 'Ē',
+    'emacr;': 'ē',
+    'empty;': '∅',
+    'Eogon;': 'Ę',
+    'eogon;': 'ę',
+    'eplus;': '⩱',
+    'epsiv;': 'ϵ',
+    'eqsim;': '≂',
+    'Equal;': '⩵',
+    'equiv;': '≡',
+    'erarr;': '⥱',
+    'erDot;': '≓',
+    'esdot;': '≐',
+    'exist;': '∃',
+    'fflig;': 'ff',
+    'filig;': 'fi',
+    'fjlig;': 'fj',
+    'fllig;': 'fl',
+    'fltns;': '▱',
+    'forkv;': '⫙',
+    frac12: '½',
+    frac14: '¼',
+    frac34: '¾',
+    'frasl;': '⁄',
+    'frown;': '⌢',
+    'Gamma;': 'Γ',
+    'gamma;': 'γ',
+    'Gcirc;': 'Ĝ',
+    'gcirc;': 'ĝ',
+    'gescc;': '⪩',
+    'gimel;': 'ℷ',
+    'gneqq;': '≩',
+    'gnsim;': '⋧',
+    'grave;': '`',
+    'gsime;': '⪎',
+    'gsiml;': '⪐',
+    'gtcir;': '⩺',
+    'gtdot;': '⋗',
+    'Hacek;': 'ˇ',
+    'harrw;': '↭',
+    'Hcirc;': 'Ĥ',
+    'hcirc;': 'ĥ',
+    'hoarr;': '⇿',
+    Iacute: 'Í',
+    iacute: 'í',
+    'Icirc;': 'Î',
+    'icirc;': 'î',
+    'iexcl;': '¡',
+    Igrave: 'Ì',
+    igrave: 'ì',
+    'iiint;': '∭',
+    'iiota;': '℩',
+    'IJlig;': 'IJ',
+    'ijlig;': 'ij',
+    'Imacr;': 'Ī',
+    'imacr;': 'ī',
+    'image;': 'ℑ',
+    'imath;': 'ı',
+    'imped;': 'Ƶ',
+    'infin;': '∞',
+    'Iogon;': 'Į',
+    'iogon;': 'į',
+    'iprod;': '⨼',
+    iquest: '¿',
+    'isinE;': '⋹',
+    'isins;': '⋴',
+    'isinv;': '∈',
+    'Iukcy;': 'І',
+    'iukcy;': 'і',
+    'Jcirc;': 'Ĵ',
+    'jcirc;': 'ĵ',
+    'jmath;': 'ȷ',
+    'Jukcy;': 'Є',
+    'jukcy;': 'є',
+    'Kappa;': 'Κ',
+    'kappa;': 'κ',
+    'lAarr;': '⇚',
+    'langd;': '⦑',
+    'laquo;': '«',
+    'larrb;': '⇤',
+    'lates;': '⪭︀',
+    'lBarr;': '⤎',
+    'lbarr;': '⤌',
+    'lbbrk;': '❲',
+    'lbrke;': '⦋',
+    'lceil;': '⌈',
+    'ldquo;': '“',
+    'lescc;': '⪨',
+    'lhard;': '↽',
+    'lharu;': '↼',
+    'lhblk;': '▄',
+    'llarr;': '⇇',
+    'lltri;': '◺',
+    'lneqq;': '≨',
+    'lnsim;': '⋦',
+    'loang;': '⟬',
+    'loarr;': '⇽',
+    'lobrk;': '⟦',
+    'lopar;': '⦅',
+    'lrarr;': '⇆',
+    'lrhar;': '⇋',
+    'lrtri;': '⊿',
+    'lsime;': '⪍',
+    'lsimg;': '⪏',
+    'lsquo;': '‘',
+    'ltcir;': '⩹',
+    'ltdot;': '⋖',
+    'ltrie;': '⊴',
+    'ltrif;': '◂',
+    'mdash;': '—',
+    'mDDot;': '∺',
+    'micro;': 'µ',
+    middot: '·',
+    'minus;': '−',
+    'mumap;': '⊸',
+    'nabla;': '∇',
+    'napid;': '≋̸',
+    'napos;': 'ʼn',
+    'natur;': '♮',
+    'nbump;': '≎̸',
+    'ncong;': '≇',
+    'ndash;': '–',
+    'neArr;': '⇗',
+    'nearr;': '↗',
+    'nedot;': '≐̸',
+    'nesim;': '≂̸',
+    'ngeqq;': '≧̸',
+    'ngsim;': '≵',
+    'nhArr;': '⇎',
+    'nharr;': '↮',
+    'nhpar;': '⫲',
+    'nlArr;': '⇍',
+    'nlarr;': '↚',
+    'nleqq;': '≦̸',
+    'nless;': '≮',
+    'nlsim;': '≴',
+    'nltri;': '⋪',
+    'notin;': '∉',
+    'notni;': '∌',
+    'npart;': '∂̸',
+    'nprec;': '⊀',
+    'nrArr;': '⇏',
+    'nrarr;': '↛',
+    'nrtri;': '⋫',
+    'nsime;': '≄',
+    'nsmid;': '∤',
+    'nspar;': '∦',
+    'nsubE;': '⫅̸',
+    'nsube;': '⊈',
+    'nsucc;': '⊁',
+    'nsupE;': '⫆̸',
+    'nsupe;': '⊉',
+    Ntilde: 'Ñ',
+    ntilde: 'ñ',
+    'numsp;': ' ',
+    'nvsim;': '∼⃒',
+    'nwArr;': '⇖',
+    'nwarr;': '↖',
+    Oacute: 'Ó',
+    oacute: 'ó',
+    'Ocirc;': 'Ô',
+    'ocirc;': 'ô',
+    'odash;': '⊝',
+    'OElig;': 'Œ',
+    'oelig;': 'œ',
+    'ofcir;': '⦿',
+    Ograve: 'Ò',
+    ograve: 'ò',
+    'ohbar;': '⦵',
+    'olarr;': '↺',
+    'olcir;': '⦾',
+    'oline;': '‾',
+    'Omacr;': 'Ō',
+    'omacr;': 'ō',
+    'Omega;': 'Ω',
+    'omega;': 'ω',
+    'operp;': '⦹',
+    'oplus;': '⊕',
+    'orarr;': '↻',
+    'order;': 'ℴ',
+    Oslash: 'Ø',
+    oslash: 'ø',
+    Otilde: 'Õ',
+    otilde: 'õ',
+    'ovbar;': '⌽',
+    'parsl;': '⫽',
+    'phone;': '☎',
+    'plusb;': '⊞',
+    'pluse;': '⩲',
+    plusmn: '±',
+    'pound;': '£',
+    'prcue;': '≼',
+    'Prime;': '″',
+    'prime;': '′',
+    'prnap;': '⪹',
+    'prsim;': '≾',
+    'quest;': '?',
+    'rAarr;': '⇛',
+    'radic;': '√',
+    'rangd;': '⦒',
+    'range;': '⦥',
+    'raquo;': '»',
+    'rarrb;': '⇥',
+    'rarrc;': '⤳',
+    'rarrw;': '↝',
+    'ratio;': '∶',
+    'RBarr;': '⤐',
+    'rBarr;': '⤏',
+    'rbarr;': '⤍',
+    'rbbrk;': '❳',
+    'rbrke;': '⦌',
+    'rceil;': '⌉',
+    'rdquo;': '”',
+    'reals;': 'ℝ',
+    'rhard;': '⇁',
+    'rharu;': '⇀',
+    'rlarr;': '⇄',
+    'rlhar;': '⇌',
+    'rnmid;': '⫮',
+    'roang;': '⟭',
+    'roarr;': '⇾',
+    'robrk;': '⟧',
+    'ropar;': '⦆',
+    'rrarr;': '⇉',
+    'rsquo;': '’',
+    'rtrie;': '⊵',
+    'rtrif;': '▸',
+    'sbquo;': '‚',
+    'sccue;': '≽',
+    'Scirc;': 'Ŝ',
+    'scirc;': 'ŝ',
+    'scnap;': '⪺',
+    'scsim;': '≿',
+    'sdotb;': '⊡',
+    'sdote;': '⩦',
+    'seArr;': '⇘',
+    'searr;': '↘',
+    'setmn;': '∖',
+    'sharp;': '♯',
+    'Sigma;': 'Σ',
+    'sigma;': 'σ',
+    'simeq;': '≃',
+    'simgE;': '⪠',
+    'simlE;': '⪟',
+    'simne;': '≆',
+    'slarr;': '←',
+    'smile;': '⌣',
+    'smtes;': '⪬︀',
+    'sqcap;': '⊓',
+    'sqcup;': '⊔',
+    'sqsub;': '⊏',
+    'sqsup;': '⊐',
+    'srarr;': '→',
+    'starf;': '★',
+    'strns;': '¯',
+    'subnE;': '⫋',
+    'subne;': '⊊',
+    'supnE;': '⫌',
+    'supne;': '⊋',
+    'swArr;': '⇙',
+    'swarr;': '↙',
+    'szlig;': 'ß',
+    'Theta;': 'Θ',
+    'theta;': 'θ',
+    'thkap;': '≈',
+    'THORN;': 'Þ',
+    'thorn;': 'þ',
+    'Tilde;': '∼',
+    'tilde;': '˜',
+    'times;': '×',
+    'TRADE;': '™',
+    'trade;': '™',
+    'trisb;': '⧍',
+    'TSHcy;': 'Ћ',
+    'tshcy;': 'ћ',
+    'twixt;': '≬',
+    Uacute: 'Ú',
+    uacute: 'ú',
+    'Ubrcy;': 'Ў',
+    'ubrcy;': 'ў',
+    'Ucirc;': 'Û',
+    'ucirc;': 'û',
+    'udarr;': '⇅',
+    'udhar;': '⥮',
+    Ugrave: 'Ù',
+    ugrave: 'ù',
+    'uharl;': '↿',
+    'uharr;': '↾',
+    'uhblk;': '▀',
+    'ultri;': '◸',
+    'Umacr;': 'Ū',
+    'umacr;': 'ū',
+    'Union;': '⋃',
+    'Uogon;': 'Ų',
+    'uogon;': 'ų',
+    'uplus;': '⊎',
+    'upsih;': 'ϒ',
+    'UpTee;': '⊥',
+    'Uring;': 'Ů',
+    'uring;': 'ů',
+    'urtri;': '◹',
+    'utdot;': '⋰',
+    'utrif;': '▴',
+    'uuarr;': '⇈',
+    'varpi;': 'ϖ',
+    'vBarv;': '⫩',
+    'VDash;': '⊫',
+    'Vdash;': '⊩',
+    'vDash;': '⊨',
+    'vdash;': '⊢',
+    'veeeq;': '≚',
+    'vltri;': '⊲',
+    'vnsub;': '⊂⃒',
+    'vnsup;': '⊃⃒',
+    'vprop;': '∝',
+    'vrtri;': '⊳',
+    'Wcirc;': 'Ŵ',
+    'wcirc;': 'ŵ',
+    'Wedge;': '⋀',
+    'wedge;': '∧',
+    'xcirc;': '◯',
+    'xdtri;': '▽',
+    'xhArr;': '⟺',
+    'xharr;': '⟷',
+    'xlArr;': '⟸',
+    'xlarr;': '⟵',
+    'xodot;': '⨀',
+    'xrArr;': '⟹',
+    'xrarr;': '⟶',
+    'xutri;': '△',
+    Yacute: 'Ý',
+    yacute: 'ý',
+    'Ycirc;': 'Ŷ',
+    'ycirc;': 'ŷ',
+    'Aacute;': 'Á',
+    'aacute;': 'á',
+    'Abreve;': 'Ă',
+    'abreve;': 'ă',
+    'Agrave;': 'À',
+    'agrave;': 'à',
+    'andand;': '⩕',
+    'angmsd;': '∡',
+    'angsph;': '∢',
+    'apacir;': '⩯',
+    'approx;': '≈',
+    'Assign;': '≔',
+    'Atilde;': 'Ã',
+    'atilde;': 'ã',
+    'barvee;': '⊽',
+    'Barwed;': '⌆',
+    'barwed;': '⌅',
+    'becaus;': '∵',
+    'bernou;': 'ℬ',
+    'bigcap;': '⋂',
+    'bigcup;': '⋃',
+    'bigvee;': '⋁',
+    'bkarow;': '⤍',
+    'bottom;': '⊥',
+    'bowtie;': '⋈',
+    'boxbox;': '⧉',
+    'bprime;': '‵',
+    'brvbar;': '¦',
+    'bullet;': '•',
+    'Bumpeq;': '≎',
+    'bumpeq;': '≏',
+    'Cacute;': 'Ć',
+    'cacute;': 'ć',
+    'capand;': '⩄',
+    'capcap;': '⩋',
+    'capcup;': '⩇',
+    'capdot;': '⩀',
+    'Ccaron;': 'Č',
+    'ccaron;': 'č',
+    'Ccedil;': 'Ç',
+    'ccedil;': 'ç',
+    'circeq;': '≗',
+    'cirmid;': '⫯',
+    'Colone;': '⩴',
+    'colone;': '≔',
+    'commat;': '@',
+    'compfn;': '∘',
+    'Conint;': '∯',
+    'conint;': '∮',
+    'coprod;': '∐',
+    'copysr;': '℗',
+    'cularr;': '↶',
+    'CupCap;': '≍',
+    'cupcap;': '⩆',
+    'cupcup;': '⩊',
+    'cupdot;': '⊍',
+    'curarr;': '↷',
+    'curren;': '¤',
+    'cylcty;': '⌭',
+    'Dagger;': '‡',
+    'dagger;': '†',
+    'daleth;': 'ℸ',
+    'Dcaron;': 'Ď',
+    'dcaron;': 'ď',
+    'dfisht;': '⥿',
+    'divide;': '÷',
+    'divonx;': '⋇',
+    'dlcorn;': '⌞',
+    'dlcrop;': '⌍',
+    'dollar;': '$',
+    'DotDot;': '⃜',
+    'drcorn;': '⌟',
+    'drcrop;': '⌌',
+    'Dstrok;': 'Đ',
+    'dstrok;': 'đ',
+    'Eacute;': 'É',
+    'eacute;': 'é',
+    'easter;': '⩮',
+    'Ecaron;': 'Ě',
+    'ecaron;': 'ě',
+    'ecolon;': '≕',
+    'Egrave;': 'È',
+    'egrave;': 'è',
+    'egsdot;': '⪘',
+    'elsdot;': '⪗',
+    'emptyv;': '∅',
+    'emsp13;': ' ',
+    'emsp14;': ' ',
+    'eparsl;': '⧣',
+    'eqcirc;': '≖',
+    'equals;': '=',
+    'equest;': '≟',
+    'Exists;': '∃',
+    'female;': '♀',
+    'ffilig;': 'ffi',
+    'ffllig;': 'ffl',
+    'ForAll;': '∀',
+    'forall;': '∀',
+    'frac12;': '½',
+    'frac13;': '⅓',
+    'frac14;': '¼',
+    'frac15;': '⅕',
+    'frac16;': '⅙',
+    'frac18;': '⅛',
+    'frac23;': '⅔',
+    'frac25;': '⅖',
+    'frac34;': '¾',
+    'frac35;': '⅗',
+    'frac38;': '⅜',
+    'frac45;': '⅘',
+    'frac56;': '⅚',
+    'frac58;': '⅝',
+    'frac78;': '⅞',
+    'gacute;': 'ǵ',
+    'Gammad;': 'Ϝ',
+    'gammad;': 'ϝ',
+    'Gbreve;': 'Ğ',
+    'gbreve;': 'ğ',
+    'Gcedil;': 'Ģ',
+    'gesdot;': '⪀',
+    'gesles;': '⪔',
+    'gtlPar;': '⦕',
+    'gtrarr;': '⥸',
+    'gtrdot;': '⋗',
+    'gtrsim;': '≳',
+    'hairsp;': ' ',
+    'hamilt;': 'ℋ',
+    'HARDcy;': 'Ъ',
+    'hardcy;': 'ъ',
+    'hearts;': '♥',
+    'hellip;': '…',
+    'hercon;': '⊹',
+    'homtht;': '∻',
+    'horbar;': '―',
+    'hslash;': 'ℏ',
+    'Hstrok;': 'Ħ',
+    'hstrok;': 'ħ',
+    'hybull;': '⁃',
+    'hyphen;': '‐',
+    'Iacute;': 'Í',
+    'iacute;': 'í',
+    'Igrave;': 'Ì',
+    'igrave;': 'ì',
+    'iiiint;': '⨌',
+    'iinfin;': '⧜',
+    'incare;': '℅',
+    'inodot;': 'ı',
+    'intcal;': '⊺',
+    'iquest;': '¿',
+    'isinsv;': '⋳',
+    'Itilde;': 'Ĩ',
+    'itilde;': 'ĩ',
+    'Jsercy;': 'Ј',
+    'jsercy;': 'ј',
+    'kappav;': 'ϰ',
+    'Kcedil;': 'Ķ',
+    'kcedil;': 'ķ',
+    'kgreen;': 'ĸ',
+    'Lacute;': 'Ĺ',
+    'lacute;': 'ĺ',
+    'lagran;': 'ℒ',
+    'Lambda;': 'Λ',
+    'lambda;': 'λ',
+    'langle;': '⟨',
+    'larrfs;': '⤝',
+    'larrhk;': '↩',
+    'larrlp;': '↫',
+    'larrpl;': '⤹',
+    'larrtl;': '↢',
+    'lAtail;': '⤛',
+    'latail;': '⤙',
+    'lbrace;': '{',
+    'lbrack;': '[',
+    'Lcaron;': 'Ľ',
+    'lcaron;': 'ľ',
+    'Lcedil;': 'Ļ',
+    'lcedil;': 'ļ',
+    'ldquor;': '„',
+    'lesdot;': '⩿',
+    'lesges;': '⪓',
+    'lfisht;': '⥼',
+    'lfloor;': '⌊',
+    'lharul;': '⥪',
+    'llhard;': '⥫',
+    'Lmidot;': 'Ŀ',
+    'lmidot;': 'ŀ',
+    'lmoust;': '⎰',
+    'loplus;': '⨭',
+    'lowast;': '∗',
+    'lowbar;': '_',
+    'lparlt;': '⦓',
+    'lrhard;': '⥭',
+    'lsaquo;': '‹',
+    'lsquor;': '‚',
+    'Lstrok;': 'Ł',
+    'lstrok;': 'ł',
+    'lthree;': '⋋',
+    'ltimes;': '⋉',
+    'ltlarr;': '⥶',
+    'ltrPar;': '⦖',
+    'mapsto;': '↦',
+    'marker;': '▮',
+    'mcomma;': '⨩',
+    'midast;': '*',
+    'midcir;': '⫰',
+    'middot;': '·',
+    'minusb;': '⊟',
+    'minusd;': '∸',
+    'mnplus;': '∓',
+    'models;': '⊧',
+    'mstpos;': '∾',
+    'Nacute;': 'Ń',
+    'nacute;': 'ń',
+    'nbumpe;': '≏̸',
+    'Ncaron;': 'Ň',
+    'ncaron;': 'ň',
+    'Ncedil;': 'Ņ',
+    'ncedil;': 'ņ',
+    'nearhk;': '⤤',
+    'nequiv;': '≢',
+    'nesear;': '⤨',
+    'nexist;': '∄',
+    'nltrie;': '⋬',
+    'notinE;': '⋹̸',
+    'nparsl;': '⫽⃥',
+    'nprcue;': '⋠',
+    'nrarrc;': '⤳̸',
+    'nrarrw;': '↝̸',
+    'nrtrie;': '⋭',
+    'nsccue;': '⋡',
+    'nsimeq;': '≄',
+    'Ntilde;': 'Ñ',
+    'ntilde;': 'ñ',
+    'numero;': '№',
+    'nVDash;': '⊯',
+    'nVdash;': '⊮',
+    'nvDash;': '⊭',
+    'nvdash;': '⊬',
+    'nvHarr;': '⤄',
+    'nvlArr;': '⤂',
+    'nvrArr;': '⤃',
+    'nwarhk;': '⤣',
+    'nwnear;': '⤧',
+    'Oacute;': 'Ó',
+    'oacute;': 'ó',
+    'Odblac;': 'Ő',
+    'odblac;': 'ő',
+    'odsold;': '⦼',
+    'Ograve;': 'Ò',
+    'ograve;': 'ò',
+    'ominus;': '⊖',
+    'origof;': '⊶',
+    'Oslash;': 'Ø',
+    'oslash;': 'ø',
+    'Otilde;': 'Õ',
+    'otilde;': 'õ',
+    'Otimes;': '⨷',
+    'otimes;': '⊗',
+    'parsim;': '⫳',
+    'percnt;': '%',
+    'period;': '.',
+    'permil;': '‰',
+    'phmmat;': 'ℳ',
+    'planck;': 'ℏ',
+    'plankv;': 'ℏ',
+    'plusdo;': '∔',
+    'plusdu;': '⨥',
+    'plusmn;': '±',
+    'preceq;': '⪯',
+    'primes;': 'ℙ',
+    'prnsim;': '⋨',
+    'propto;': '∝',
+    'prurel;': '⊰',
+    'puncsp;': ' ',
+    'qprime;': '⁗',
+    'Racute;': 'Ŕ',
+    'racute;': 'ŕ',
+    'rangle;': '⟩',
+    'rarrap;': '⥵',
+    'rarrfs;': '⤞',
+    'rarrhk;': '↪',
+    'rarrlp;': '↬',
+    'rarrpl;': '⥅',
+    'Rarrtl;': '⤖',
+    'rarrtl;': '↣',
+    'rAtail;': '⤜',
+    'ratail;': '⤚',
+    'rbrace;': '}',
+    'rbrack;': ']',
+    'Rcaron;': 'Ř',
+    'rcaron;': 'ř',
+    'Rcedil;': 'Ŗ',
+    'rcedil;': 'ŗ',
+    'rdquor;': '”',
+    'rfisht;': '⥽',
+    'rfloor;': '⌋',
+    'rharul;': '⥬',
+    'rmoust;': '⎱',
+    'roplus;': '⨮',
+    'rpargt;': '⦔',
+    'rsaquo;': '›',
+    'rsquor;': '’',
+    'rthree;': '⋌',
+    'rtimes;': '⋊',
+    'Sacute;': 'Ś',
+    'sacute;': 'ś',
+    'Scaron;': 'Š',
+    'scaron;': 'š',
+    'Scedil;': 'Ş',
+    'scedil;': 'ş',
+    'scnsim;': '⋩',
+    'searhk;': '⤥',
+    'seswar;': '⤩',
+    'sfrown;': '⌢',
+    'SHCHcy;': 'Щ',
+    'shchcy;': 'щ',
+    'sigmaf;': 'ς',
+    'sigmav;': 'ς',
+    'simdot;': '⩪',
+    'smashp;': '⨳',
+    'SOFTcy;': 'Ь',
+    'softcy;': 'ь',
+    'solbar;': '⌿',
+    'spades;': '♠',
+    'sqcaps;': '⊓︀',
+    'sqcups;': '⊔︀',
+    'sqsube;': '⊑',
+    'sqsupe;': '⊒',
+    'Square;': '□',
+    'square;': '□',
+    'squarf;': '▪',
+    'ssetmn;': '∖',
+    'ssmile;': '⌣',
+    'sstarf;': '⋆',
+    'subdot;': '⪽',
+    'Subset;': '⋐',
+    'subset;': '⊂',
+    'subsim;': '⫇',
+    'subsub;': '⫕',
+    'subsup;': '⫓',
+    'succeq;': '⪰',
+    'supdot;': '⪾',
+    'Supset;': '⋑',
+    'supset;': '⊃',
+    'supsim;': '⫈',
+    'supsub;': '⫔',
+    'supsup;': '⫖',
+    'swarhk;': '⤦',
+    'swnwar;': '⤪',
+    'target;': '⌖',
+    'Tcaron;': 'Ť',
+    'tcaron;': 'ť',
+    'Tcedil;': 'Ţ',
+    'tcedil;': 'ţ',
+    'telrec;': '⌕',
+    'there4;': '∴',
+    'thetav;': 'ϑ',
+    'thinsp;': ' ',
+    'thksim;': '∼',
+    'timesb;': '⊠',
+    'timesd;': '⨰',
+    'topbot;': '⌶',
+    'topcir;': '⫱',
+    'tprime;': '‴',
+    'tridot;': '◬',
+    'Tstrok;': 'Ŧ',
+    'tstrok;': 'ŧ',
+    'Uacute;': 'Ú',
+    'uacute;': 'ú',
+    'Ubreve;': 'Ŭ',
+    'ubreve;': 'ŭ',
+    'Udblac;': 'Ű',
+    'udblac;': 'ű',
+    'ufisht;': '⥾',
+    'Ugrave;': 'Ù',
+    'ugrave;': 'ù',
+    'ulcorn;': '⌜',
+    'ulcrop;': '⌏',
+    'urcorn;': '⌝',
+    'urcrop;': '⌎',
+    'Utilde;': 'Ũ',
+    'utilde;': 'ũ',
+    'vangrt;': '⦜',
+    'varphi;': 'ϕ',
+    'varrho;': 'ϱ',
+    'Vdashl;': '⫦',
+    'veebar;': '⊻',
+    'vellip;': '⋮',
+    'Verbar;': '‖',
+    'verbar;': '|',
+    'vsubnE;': '⫋︀',
+    'vsubne;': '⊊︀',
+    'vsupnE;': '⫌︀',
+    'vsupne;': '⊋︀',
+    'Vvdash;': '⊪',
+    'wedbar;': '⩟',
+    'wedgeq;': '≙',
+    'weierp;': '℘',
+    'wreath;': '≀',
+    'xoplus;': '⨁',
+    'xotime;': '⨂',
+    'xsqcup;': '⨆',
+    'xuplus;': '⨄',
+    'xwedge;': '⋀',
+    'Yacute;': 'Ý',
+    'yacute;': 'ý',
+    'Zacute;': 'Ź',
+    'zacute;': 'ź',
+    'Zcaron;': 'Ž',
+    'zcaron;': 'ž',
+    'zeetrf;': 'ℨ',
+    'alefsym;': 'ℵ',
+    'angrtvb;': '⊾',
+    'angzarr;': '⍼',
+    'asympeq;': '≍',
+    'backsim;': '∽',
+    'Because;': '∵',
+    'because;': '∵',
+    'bemptyv;': '⦰',
+    'between;': '≬',
+    'bigcirc;': '◯',
+    'bigodot;': '⨀',
+    'bigstar;': '★',
+    'bnequiv;': '≡⃥',
+    'boxplus;': '⊞',
+    'Cayleys;': 'ℭ',
+    'Cconint;': '∰',
+    'ccupssm;': '⩐',
+    'Cedilla;': '¸',
+    'cemptyv;': '⦲',
+    'cirscir;': '⧂',
+    'coloneq;': '≔',
+    'congdot;': '⩭',
+    'cudarrl;': '⤸',
+    'cudarrr;': '⤵',
+    'cularrp;': '⤽',
+    'curarrm;': '⤼',
+    'dbkarow;': '⤏',
+    'ddagger;': '‡',
+    'ddotseq;': '⩷',
+    'demptyv;': '⦱',
+    'Diamond;': '⋄',
+    'diamond;': '⋄',
+    'digamma;': 'ϝ',
+    'dotplus;': '∔',
+    'DownTee;': '⊤',
+    'dwangle;': '⦦',
+    'Element;': '∈',
+    'Epsilon;': 'Ε',
+    'epsilon;': 'ε',
+    'eqcolon;': '≕',
+    'equivDD;': '⩸',
+    'gesdoto;': '⪂',
+    'gtquest;': '⩼',
+    'gtrless;': '≷',
+    'harrcir;': '⥈',
+    'Implies;': '⇒',
+    'intprod;': '⨼',
+    'isindot;': '⋵',
+    'larrbfs;': '⤟',
+    'larrsim;': '⥳',
+    'lbrksld;': '⦏',
+    'lbrkslu;': '⦍',
+    'ldrdhar;': '⥧',
+    'LeftTee;': '⊣',
+    'lesdoto;': '⪁',
+    'lessdot;': '⋖',
+    'lessgtr;': '≶',
+    'lesssim;': '≲',
+    'lotimes;': '⨴',
+    'lozenge;': '◊',
+    'ltquest;': '⩻',
+    'luruhar;': '⥦',
+    'maltese;': '✠',
+    'minusdu;': '⨪',
+    'napprox;': '≉',
+    'natural;': '♮',
+    'nearrow;': '↗',
+    'NewLine;': '\n',
+    'nexists;': '∄',
+    'NoBreak;': '⁠',
+    'notinva;': '∉',
+    'notinvb;': '⋷',
+    'notinvc;': '⋶',
+    'NotLess;': '≮',
+    'notniva;': '∌',
+    'notnivb;': '⋾',
+    'notnivc;': '⋽',
+    'npolint;': '⨔',
+    'npreceq;': '⪯̸',
+    'nsqsube;': '⋢',
+    'nsqsupe;': '⋣',
+    'nsubset;': '⊂⃒',
+    'nsucceq;': '⪰̸',
+    'nsupset;': '⊃⃒',
+    'nvinfin;': '⧞',
+    'nvltrie;': '⊴⃒',
+    'nvrtrie;': '⊵⃒',
+    'nwarrow;': '↖',
+    'olcross;': '⦻',
+    'Omicron;': 'Ο',
+    'omicron;': 'ο',
+    'orderof;': 'ℴ',
+    'orslope;': '⩗',
+    'OverBar;': '‾',
+    'pertenk;': '‱',
+    'planckh;': 'ℎ',
+    'pluscir;': '⨢',
+    'plussim;': '⨦',
+    'plustwo;': '⨧',
+    'precsim;': '≾',
+    'Product;': '∏',
+    'quatint;': '⨖',
+    'questeq;': '≟',
+    'rarrbfs;': '⤠',
+    'rarrsim;': '⥴',
+    'rbrksld;': '⦎',
+    'rbrkslu;': '⦐',
+    'rdldhar;': '⥩',
+    'realine;': 'ℛ',
+    'rotimes;': '⨵',
+    'ruluhar;': '⥨',
+    'searrow;': '↘',
+    'simplus;': '⨤',
+    'simrarr;': '⥲',
+    'subedot;': '⫃',
+    'submult;': '⫁',
+    'subplus;': '⪿',
+    'subrarr;': '⥹',
+    'succsim;': '≿',
+    'supdsub;': '⫘',
+    'supedot;': '⫄',
+    'suphsol;': '⟉',
+    'suphsub;': '⫗',
+    'suplarr;': '⥻',
+    'supmult;': '⫂',
+    'supplus;': '⫀',
+    'swarrow;': '↙',
+    'topfork;': '⫚',
+    'triplus;': '⨹',
+    'tritime;': '⨻',
+    'UpArrow;': '↑',
+    'Uparrow;': '⇑',
+    'uparrow;': '↑',
+    'Upsilon;': 'Υ',
+    'upsilon;': 'υ',
+    'uwangle;': '⦧',
+    'vzigzag;': '⦚',
+    'zigrarr;': '⇝',
+    'andslope;': '⩘',
+    'angmsdaa;': '⦨',
+    'angmsdab;': '⦩',
+    'angmsdac;': '⦪',
+    'angmsdad;': '⦫',
+    'angmsdae;': '⦬',
+    'angmsdaf;': '⦭',
+    'angmsdag;': '⦮',
+    'angmsdah;': '⦯',
+    'angrtvbd;': '⦝',
+    'approxeq;': '≊',
+    'awconint;': '∳',
+    'backcong;': '≌',
+    'barwedge;': '⌅',
+    'bbrktbrk;': '⎶',
+    'bigoplus;': '⨁',
+    'bigsqcup;': '⨆',
+    'biguplus;': '⨄',
+    'bigwedge;': '⋀',
+    'boxminus;': '⊟',
+    'boxtimes;': '⊠',
+    'bsolhsub;': '⟈',
+    'capbrcup;': '⩉',
+    'circledR;': '®',
+    'circledS;': 'Ⓢ',
+    'cirfnint;': '⨐',
+    'clubsuit;': '♣',
+    'cupbrcap;': '⩈',
+    'curlyvee;': '⋎',
+    'cwconint;': '∲',
+    'DDotrahd;': '⤑',
+    'doteqdot;': '≑',
+    'DotEqual;': '≐',
+    'dotminus;': '∸',
+    'drbkarow;': '⤐',
+    'dzigrarr;': '⟿',
+    'elinters;': '⏧',
+    'emptyset;': '∅',
+    'eqvparsl;': '⧥',
+    'fpartint;': '⨍',
+    'geqslant;': '⩾',
+    'gesdotol;': '⪄',
+    'gnapprox;': '⪊',
+    'hksearow;': '⤥',
+    'hkswarow;': '⤦',
+    'imagline;': 'ℐ',
+    'imagpart;': 'ℑ',
+    'infintie;': '⧝',
+    'integers;': 'ℤ',
+    'Integral;': '∫',
+    'intercal;': '⊺',
+    'intlarhk;': '⨗',
+    'laemptyv;': '⦴',
+    'ldrushar;': '⥋',
+    'leqslant;': '⩽',
+    'lesdotor;': '⪃',
+    'LessLess;': '⪡',
+    'llcorner;': '⌞',
+    'lnapprox;': '⪉',
+    'lrcorner;': '⌟',
+    'lurdshar;': '⥊',
+    'mapstoup;': '↥',
+    'multimap;': '⊸',
+    'naturals;': 'ℕ',
+    'ncongdot;': '⩭̸',
+    'NotEqual;': '≠',
+    'notindot;': '⋵̸',
+    'NotTilde;': '≁',
+    'otimesas;': '⨶',
+    'parallel;': '∥',
+    'PartialD;': '∂',
+    'plusacir;': '⨣',
+    'pointint;': '⨕',
+    'Precedes;': '≺',
+    'precneqq;': '⪵',
+    'precnsim;': '⋨',
+    'profalar;': '⌮',
+    'profline;': '⌒',
+    'profsurf;': '⌓',
+    'raemptyv;': '⦳',
+    'realpart;': 'ℜ',
+    'RightTee;': '⊢',
+    'rppolint;': '⨒',
+    'rtriltri;': '⧎',
+    'scpolint;': '⨓',
+    'setminus;': '∖',
+    'shortmid;': '∣',
+    'smeparsl;': '⧤',
+    'sqsubset;': '⊏',
+    'sqsupset;': '⊐',
+    'subseteq;': '⊆',
+    'Succeeds;': '≻',
+    'succneqq;': '⪶',
+    'succnsim;': '⋩',
+    'SuchThat;': '∋',
+    'Superset;': '⊃',
+    'supseteq;': '⊇',
+    'thetasym;': 'ϑ',
+    'thicksim;': '∼',
+    'timesbar;': '⨱',
+    'triangle;': '▵',
+    'triminus;': '⨺',
+    'trpezium;': '⏢',
+    'Uarrocir;': '⥉',
+    'ulcorner;': '⌜',
+    'UnderBar;': '_',
+    'urcorner;': '⌝',
+    'varkappa;': 'ϰ',
+    'varsigma;': 'ς',
+    'vartheta;': 'ϑ',
+    'backprime;': '‵',
+    'backsimeq;': '⋍',
+    'Backslash;': '∖',
+    'bigotimes;': '⨂',
+    'CenterDot;': '·',
+    'centerdot;': '·',
+    'checkmark;': '✓',
+    'CircleDot;': '⊙',
+    'complexes;': 'ℂ',
+    'Congruent;': '≡',
+    'Coproduct;': '∐',
+    'dotsquare;': '⊡',
+    'DoubleDot;': '¨',
+    'DownArrow;': '↓',
+    'Downarrow;': '⇓',
+    'downarrow;': '↓',
+    'DownBreve;': '̑',
+    'gtrapprox;': '⪆',
+    'gtreqless;': '⋛',
+    'gvertneqq;': '≩︀',
+    'heartsuit;': '♥',
+    'HumpEqual;': '≏',
+    'LeftArrow;': '←',
+    'Leftarrow;': '⇐',
+    'leftarrow;': '←',
+    'LeftFloor;': '⌊',
+    'lesseqgtr;': '⋚',
+    'LessTilde;': '≲',
+    'lvertneqq;': '≨︀',
+    'Mellintrf;': 'ℳ',
+    'MinusPlus;': '∓',
+    'ngeqslant;': '⩾̸',
+    'nleqslant;': '⩽̸',
+    'NotCupCap;': '≭',
+    'NotExists;': '∄',
+    'NotSubset;': '⊂⃒',
+    'nparallel;': '∦',
+    'nshortmid;': '∤',
+    'nsubseteq;': '⊈',
+    'nsupseteq;': '⊉',
+    'OverBrace;': '⏞',
+    'pitchfork;': '⋔',
+    'PlusMinus;': '±',
+    'rationals;': 'ℚ',
+    'spadesuit;': '♠',
+    'subseteqq;': '⫅',
+    'subsetneq;': '⊊',
+    'supseteqq;': '⫆',
+    'supsetneq;': '⊋',
+    'Therefore;': '∴',
+    'therefore;': '∴',
+    'ThinSpace;': ' ',
+    'triangleq;': '≜',
+    'TripleDot;': '⃛',
+    'UnionPlus;': '⊎',
+    'varpropto;': '∝',
+    'Bernoullis;': 'ℬ',
+    'circledast;': '⊛',
+    'CirclePlus;': '⊕',
+    'complement;': '∁',
+    'curlywedge;': '⋏',
+    'eqslantgtr;': '⪖',
+    'EqualTilde;': '≂',
+    'Fouriertrf;': 'ℱ',
+    'gtreqqless;': '⪌',
+    'ImaginaryI;': 'ⅈ',
+    'Laplacetrf;': 'ℒ',
+    'LeftVector;': '↼',
+    'lessapprox;': '⪅',
+    'lesseqqgtr;': '⪋',
+    'Lleftarrow;': '⇚',
+    'lmoustache;': '⎰',
+    'longmapsto;': '⟼',
+    'mapstodown;': '↧',
+    'mapstoleft;': '↤',
+    'nLeftarrow;': '⇍',
+    'nleftarrow;': '↚',
+    'NotElement;': '∉',
+    'NotGreater;': '≯',
+    'nsubseteqq;': '⫅̸',
+    'nsupseteqq;': '⫆̸',
+    'precapprox;': '⪷',
+    'Proportion;': '∷',
+    'RightArrow;': '→',
+    'Rightarrow;': '⇒',
+    'rightarrow;': '→',
+    'RightFloor;': '⌋',
+    'rmoustache;': '⎱',
+    'sqsubseteq;': '⊑',
+    'sqsupseteq;': '⊒',
+    'subsetneqq;': '⫋',
+    'succapprox;': '⪸',
+    'supsetneqq;': '⫌',
+    'ThickSpace;': '  ',
+    'TildeEqual;': '≃',
+    'TildeTilde;': '≈',
+    'UnderBrace;': '⏟',
+    'UpArrowBar;': '⤒',
+    'UpTeeArrow;': '↥',
+    'upuparrows;': '⇈',
+    'varepsilon;': 'ϵ',
+    'varnothing;': '∅',
+    'backepsilon;': '϶',
+    'blacksquare;': '▪',
+    'circledcirc;': '⊚',
+    'circleddash;': '⊝',
+    'CircleMinus;': '⊖',
+    'CircleTimes;': '⊗',
+    'curlyeqprec;': '⋞',
+    'curlyeqsucc;': '⋟',
+    'diamondsuit;': '♦',
+    'eqslantless;': '⪕',
+    'Equilibrium;': '⇌',
+    'expectation;': 'ℰ',
+    'GreaterLess;': '≷',
+    'LeftCeiling;': '⌈',
+    'LessGreater;': '≶',
+    'MediumSpace;': ' ',
+    'NotLessLess;': '≪̸',
+    'NotPrecedes;': '⊀',
+    'NotSucceeds;': '⊁',
+    'NotSuperset;': '⊃⃒',
+    'nRightarrow;': '⇏',
+    'nrightarrow;': '↛',
+    'OverBracket;': '⎴',
+    'preccurlyeq;': '≼',
+    'precnapprox;': '⪹',
+    'quaternions;': 'ℍ',
+    'RightVector;': '⇀',
+    'Rrightarrow;': '⇛',
+    'RuleDelayed;': '⧴',
+    'SmallCircle;': '∘',
+    'SquareUnion;': '⊔',
+    'straightphi;': 'ϕ',
+    'SubsetEqual;': '⊆',
+    'succcurlyeq;': '≽',
+    'succnapprox;': '⪺',
+    'thickapprox;': '≈',
+    'UpDownArrow;': '↕',
+    'Updownarrow;': '⇕',
+    'updownarrow;': '↕',
+    'VerticalBar;': '∣',
+    'blacklozenge;': '⧫',
+    'DownArrowBar;': '⤓',
+    'DownTeeArrow;': '↧',
+    'ExponentialE;': 'ⅇ',
+    'exponentiale;': 'ⅇ',
+    'GreaterEqual;': '≥',
+    'GreaterTilde;': '≳',
+    'HilbertSpace;': 'ℋ',
+    'HumpDownHump;': '≎',
+    'Intersection;': '⋂',
+    'LeftArrowBar;': '⇤',
+    'LeftTeeArrow;': '↤',
+    'LeftTriangle;': '⊲',
+    'LeftUpVector;': '↿',
+    'NotCongruent;': '≢',
+    'NotHumpEqual;': '≏̸',
+    'NotLessEqual;': '≰',
+    'NotLessTilde;': '≴',
+    'Proportional;': '∝',
+    'RightCeiling;': '⌉',
+    'risingdotseq;': '≓',
+    'RoundImplies;': '⥰',
+    'ShortUpArrow;': '↑',
+    'SquareSubset;': '⊏',
+    'triangledown;': '▿',
+    'triangleleft;': '◃',
+    'UnderBracket;': '⎵',
+    'varsubsetneq;': '⊊︀',
+    'varsupsetneq;': '⊋︀',
+    'VerticalLine;': '|',
+    'ApplyFunction;': '⁡',
+    'bigtriangleup;': '△',
+    'blacktriangle;': '▴',
+    'DifferentialD;': 'ⅆ',
+    'divideontimes;': '⋇',
+    'DoubleLeftTee;': '⫤',
+    'DoubleUpArrow;': '⇑',
+    'fallingdotseq;': '≒',
+    'hookleftarrow;': '↩',
+    'leftarrowtail;': '↢',
+    'leftharpoonup;': '↼',
+    'LeftTeeVector;': '⥚',
+    'LeftVectorBar;': '⥒',
+    'LessFullEqual;': '≦',
+    'LongLeftArrow;': '⟵',
+    'Longleftarrow;': '⟸',
+    'longleftarrow;': '⟵',
+    'looparrowleft;': '↫',
+    'measuredangle;': '∡',
+    'NotEqualTilde;': '≂̸',
+    'NotTildeEqual;': '≄',
+    'NotTildeTilde;': '≉',
+    'ntriangleleft;': '⋪',
+    'Poincareplane;': 'ℌ',
+    'PrecedesEqual;': '⪯',
+    'PrecedesTilde;': '≾',
+    'RightArrowBar;': '⇥',
+    'RightTeeArrow;': '↦',
+    'RightTriangle;': '⊳',
+    'RightUpVector;': '↾',
+    'shortparallel;': '∥',
+    'smallsetminus;': '∖',
+    'SucceedsEqual;': '⪰',
+    'SucceedsTilde;': '≿',
+    'SupersetEqual;': '⊇',
+    'triangleright;': '▹',
+    'UpEquilibrium;': '⥮',
+    'upharpoonleft;': '↿',
+    'varsubsetneqq;': '⫋︀',
+    'varsupsetneqq;': '⫌︀',
+    'VerticalTilde;': '≀',
+    'VeryThinSpace;': ' ',
+    'curvearrowleft;': '↶',
+    'DiacriticalDot;': '˙',
+    'doublebarwedge;': '⌆',
+    'DoubleRightTee;': '⊨',
+    'downdownarrows;': '⇊',
+    'DownLeftVector;': '↽',
+    'GreaterGreater;': '⪢',
+    'hookrightarrow;': '↪',
+    'HorizontalLine;': '─',
+    'InvisibleComma;': '⁣',
+    'InvisibleTimes;': '⁢',
+    'LeftDownVector;': '⇃',
+    'leftleftarrows;': '⇇',
+    'LeftRightArrow;': '↔',
+    'Leftrightarrow;': '⇔',
+    'leftrightarrow;': '↔',
+    'leftthreetimes;': '⋋',
+    'LessSlantEqual;': '⩽',
+    'LongRightArrow;': '⟶',
+    'Longrightarrow;': '⟹',
+    'longrightarrow;': '⟶',
+    'looparrowright;': '↬',
+    'LowerLeftArrow;': '↙',
+    'NestedLessLess;': '≪',
+    'NotGreaterLess;': '≹',
+    'NotLessGreater;': '≸',
+    'NotSubsetEqual;': '⊈',
+    'NotVerticalBar;': '∤',
+    'nshortparallel;': '∦',
+    'ntriangleright;': '⋫',
+    'OpenCurlyQuote;': '‘',
+    'ReverseElement;': '∋',
+    'rightarrowtail;': '↣',
+    'rightharpoonup;': '⇀',
+    'RightTeeVector;': '⥛',
+    'RightVectorBar;': '⥓',
+    'ShortDownArrow;': '↓',
+    'ShortLeftArrow;': '←',
+    'SquareSuperset;': '⊐',
+    'TildeFullEqual;': '≅',
+    'trianglelefteq;': '⊴',
+    'upharpoonright;': '↾',
+    'UpperLeftArrow;': '↖',
+    'ZeroWidthSpace;': '​',
+    'bigtriangledown;': '▽',
+    'circlearrowleft;': '↺',
+    'CloseCurlyQuote;': '’',
+    'ContourIntegral;': '∮',
+    'curvearrowright;': '↷',
+    'DoubleDownArrow;': '⇓',
+    'DoubleLeftArrow;': '⇐',
+    'downharpoonleft;': '⇃',
+    'DownRightVector;': '⇁',
+    'leftharpoondown;': '↽',
+    'leftrightarrows;': '⇆',
+    'LeftRightVector;': '⥎',
+    'LeftTriangleBar;': '⧏',
+    'LeftUpTeeVector;': '⥠',
+    'LeftUpVectorBar;': '⥘',
+    'LowerRightArrow;': '↘',
+    'nLeftrightarrow;': '⇎',
+    'nleftrightarrow;': '↮',
+    'NotGreaterEqual;': '≱',
+    'NotGreaterTilde;': '≵',
+    'NotHumpDownHump;': '≎̸',
+    'NotLeftTriangle;': '⋪',
+    'NotSquareSubset;': '⊏̸',
+    'ntrianglelefteq;': '⋬',
+    'OverParenthesis;': '⏜',
+    'RightDownVector;': '⇂',
+    'rightleftarrows;': '⇄',
+    'rightsquigarrow;': '↝',
+    'rightthreetimes;': '⋌',
+    'ShortRightArrow;': '→',
+    'straightepsilon;': 'ϵ',
+    'trianglerighteq;': '⊵',
+    'UpperRightArrow;': '↗',
+    'vartriangleleft;': '⊲',
+    'circlearrowright;': '↻',
+    'DiacriticalAcute;': '´',
+    'DiacriticalGrave;': '`',
+    'DiacriticalTilde;': '˜',
+    'DoubleRightArrow;': '⇒',
+    'DownArrowUpArrow;': '⇵',
+    'downharpoonright;': '⇂',
+    'EmptySmallSquare;': '◻',
+    'GreaterEqualLess;': '⋛',
+    'GreaterFullEqual;': '≧',
+    'LeftAngleBracket;': '⟨',
+    'LeftUpDownVector;': '⥑',
+    'LessEqualGreater;': '⋚',
+    'NonBreakingSpace;': ' ',
+    'NotPrecedesEqual;': '⪯̸',
+    'NotRightTriangle;': '⋫',
+    'NotSucceedsEqual;': '⪰̸',
+    'NotSucceedsTilde;': '≿̸',
+    'NotSupersetEqual;': '⊉',
+    'ntrianglerighteq;': '⋭',
+    'rightharpoondown;': '⇁',
+    'rightrightarrows;': '⇉',
+    'RightTriangleBar;': '⧐',
+    'RightUpTeeVector;': '⥜',
+    'RightUpVectorBar;': '⥔',
+    'twoheadleftarrow;': '↞',
+    'UnderParenthesis;': '⏝',
+    'UpArrowDownArrow;': '⇅',
+    'vartriangleright;': '⊳',
+    'blacktriangledown;': '▾',
+    'blacktriangleleft;': '◂',
+    'DoubleUpDownArrow;': '⇕',
+    'DoubleVerticalBar;': '∥',
+    'DownLeftTeeVector;': '⥞',
+    'DownLeftVectorBar;': '⥖',
+    'FilledSmallSquare;': '◼',
+    'GreaterSlantEqual;': '⩾',
+    'LeftDoubleBracket;': '⟦',
+    'LeftDownTeeVector;': '⥡',
+    'LeftDownVectorBar;': '⥙',
+    'leftrightharpoons;': '⇋',
+    'LeftTriangleEqual;': '⊴',
+    'NegativeThinSpace;': '​',
+    'NotGreaterGreater;': '≫̸',
+    'NotLessSlantEqual;': '⩽̸',
+    'NotNestedLessLess;': '⪡̸',
+    'NotReverseElement;': '∌',
+    'NotSquareSuperset;': '⊐̸',
+    'NotTildeFullEqual;': '≇',
+    'RightAngleBracket;': '⟩',
+    'rightleftharpoons;': '⇌',
+    'RightUpDownVector;': '⥏',
+    'SquareSubsetEqual;': '⊑',
+    'twoheadrightarrow;': '↠',
+    'VerticalSeparator;': '❘',
+    'blacktriangleright;': '▸',
+    'DownRightTeeVector;': '⥟',
+    'DownRightVectorBar;': '⥗',
+    'LongLeftRightArrow;': '⟷',
+    'Longleftrightarrow;': '⟺',
+    'longleftrightarrow;': '⟷',
+    'NegativeThickSpace;': '​',
+    'NotLeftTriangleBar;': '⧏̸',
+    'PrecedesSlantEqual;': '≼',
+    'ReverseEquilibrium;': '⇋',
+    'RightDoubleBracket;': '⟧',
+    'RightDownTeeVector;': '⥝',
+    'RightDownVectorBar;': '⥕',
+    'RightTriangleEqual;': '⊵',
+    'SquareIntersection;': '⊓',
+    'SucceedsSlantEqual;': '≽',
+    'DoubleLongLeftArrow;': '⟸',
+    'DownLeftRightVector;': '⥐',
+    'LeftArrowRightArrow;': '⇆',
+    'leftrightsquigarrow;': '↭',
+    'NegativeMediumSpace;': '​',
+    'NotGreaterFullEqual;': '≧̸',
+    'NotRightTriangleBar;': '⧐̸',
+    'RightArrowLeftArrow;': '⇄',
+    'SquareSupersetEqual;': '⊒',
+    'CapitalDifferentialD;': 'ⅅ',
+    'DoubleLeftRightArrow;': '⇔',
+    'DoubleLongRightArrow;': '⟹',
+    'EmptyVerySmallSquare;': '▫',
+    'NestedGreaterGreater;': '≫',
+    'NotDoubleVerticalBar;': '∦',
+    'NotGreaterSlantEqual;': '⩾̸',
+    'NotLeftTriangleEqual;': '⋬',
+    'NotSquareSubsetEqual;': '⋢',
+    'OpenCurlyDoubleQuote;': '“',
+    'ReverseUpEquilibrium;': '⥯',
+    'CloseCurlyDoubleQuote;': '”',
+    'DoubleContourIntegral;': '∯',
+    'FilledVerySmallSquare;': '▪',
+    'NegativeVeryThinSpace;': '​',
+    'NotPrecedesSlantEqual;': '⋠',
+    'NotRightTriangleEqual;': '⋭',
+    'NotSucceedsSlantEqual;': '⋡',
+    'DiacriticalDoubleAcute;': '˝',
+    'NotSquareSupersetEqual;': '⋣',
+    'NotNestedGreaterGreater;': '⪢̸',
+    'ClockwiseContourIntegral;': '∲',
+    'DoubleLongLeftRightArrow;': '⟺',
+    'CounterClockwiseContourIntegral;': '∳'
+  }
+}