// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`parser/parse invalid html 1`] = `
+exports[`base parser invalid html 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option ABRUPT_CLOSING_OF_EMPTY_COMMENT <template><!--></template> 1`] = `
+exports[`base parser onError option ABRUPT_CLOSING_OF_EMPTY_COMMENT <template><!--></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option ABRUPT_CLOSING_OF_EMPTY_COMMENT <template><!---></template> 1`] = `
+exports[`base parser onError option ABRUPT_CLOSING_OF_EMPTY_COMMENT <template><!---></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option ABRUPT_CLOSING_OF_EMPTY_COMMENT <template><!----></template> 1`] = `
+exports[`base parser onError option ABRUPT_CLOSING_OF_EMPTY_COMMENT <template><!----></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE <template attr="c"></template> 1`] = `
+exports[`base parser onError option ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE <template attr="c"></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE <template attr="&#a;"></template> 1`] = `
+exports[`base parser onError option ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE <template attr="&#a;"></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE <template attr="ÿ"></template> 1`] = `
+exports[`base parser onError option ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE <template attr="ÿ"></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE <template attr="&#xg;"></template> 1`] = `
+exports[`base parser onError option ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE <template attr="&#xg;"></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE <template>c</template> 1`] = `
+exports[`base parser onError option ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE <template>c</template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE <template>&#a;</template> 1`] = `
+exports[`base parser onError option ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE <template>&#a;</template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE <template>ÿ</template> 1`] = `
+exports[`base parser onError option ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE <template>ÿ</template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE <template>&#xg;</template> 1`] = `
+exports[`base parser onError option ABSENCE_OF_DIGITS_IN_NUMERIC_CHARACTER_REFERENCE <template>&#xg;</template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option CDATA_IN_HTML_CONTENT <template><![CDATA[cdata]]></template> 1`] = `
+exports[`base parser onError option CDATA_IN_HTML_CONTENT <template><![CDATA[cdata]]></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option CDATA_IN_HTML_CONTENT <template><svg><![CDATA[cdata]]></svg></template> 1`] = `
+exports[`base parser onError option CDATA_IN_HTML_CONTENT <template><svg><![CDATA[cdata]]></svg></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option CHARACTER_REFERENCE_OUTSIDE_UNICODE_RANGE <template>�</template> 1`] = `
+exports[`base parser onError option CHARACTER_REFERENCE_OUTSIDE_UNICODE_RANGE <template>�</template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option CONTROL_CHARACTER_REFERENCE <template></template> 1`] = `
+exports[`base parser onError option CONTROL_CHARACTER_REFERENCE <template></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option CONTROL_CHARACTER_REFERENCE <template></template> 1`] = `
+exports[`base parser onError option CONTROL_CHARACTER_REFERENCE <template></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option DUPLICATE_ATTRIBUTE <template><div id="" id=""></div></template> 1`] = `
+exports[`base parser onError option DUPLICATE_ATTRIBUTE <template><div id="" id=""></div></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option END_TAG_WITH_ATTRIBUTES <template><div></div id=""></template> 1`] = `
+exports[`base parser onError option END_TAG_WITH_ATTRIBUTES <template><div></div id=""></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option END_TAG_WITH_TRAILING_SOLIDUS <template><div></div/></template> 1`] = `
+exports[`base parser onError option END_TAG_WITH_TRAILING_SOLIDUS <template><div></div/></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option EOF_BEFORE_TAG_NAME <template>< 1`] = `
+exports[`base parser onError option EOF_BEFORE_TAG_NAME <template>< 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option EOF_BEFORE_TAG_NAME <template></ 1`] = `
+exports[`base parser onError option EOF_BEFORE_TAG_NAME <template></ 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option EOF_IN_CDATA <template><svg><![CDATA[ 1`] = `
+exports[`base parser onError option EOF_IN_CDATA <template><svg><![CDATA[ 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option EOF_IN_CDATA <template><svg><![CDATA[cdata 1`] = `
+exports[`base parser onError option EOF_IN_CDATA <template><svg><![CDATA[cdata 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option EOF_IN_COMMENT <template><! 1`] = `
+exports[`base parser onError option EOF_IN_COMMENT <template><! 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option EOF_IN_COMMENT <template><!- 1`] = `
+exports[`base parser onError option EOF_IN_COMMENT <template><!- 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option EOF_IN_COMMENT <template><!-- 1`] = `
+exports[`base parser onError option EOF_IN_COMMENT <template><!-- 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option EOF_IN_COMMENT <template><!--comment 1`] = `
+exports[`base parser onError option EOF_IN_COMMENT <template><!--comment 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option EOF_IN_COMMENT <template><!abc 1`] = `
+exports[`base parser onError option EOF_IN_COMMENT <template><!abc 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option EOF_IN_SCRIPT_HTML_COMMENT_LIKE_TEXT <script><!--console.log('hello') 1`] = `
+exports[`base parser onError option EOF_IN_SCRIPT_HTML_COMMENT_LIKE_TEXT <script><!--console.log('hello') 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option EOF_IN_SCRIPT_HTML_COMMENT_LIKE_TEXT <script>console.log('hello') 1`] = `
+exports[`base parser onError option EOF_IN_SCRIPT_HTML_COMMENT_LIKE_TEXT <script>console.log('hello') 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option EOF_IN_TAG <template><div 1`] = `
+exports[`base parser onError option EOF_IN_TAG <template><div 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option EOF_IN_TAG <template><div 1`] = `
+exports[`base parser onError option EOF_IN_TAG <template><div 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option EOF_IN_TAG <template><div id 1`] = `
+exports[`base parser onError option EOF_IN_TAG <template><div id 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option EOF_IN_TAG <template><div id = 1`] = `
+exports[`base parser onError option EOF_IN_TAG <template><div id = 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option EOF_IN_TAG <template><div id 1`] = `
+exports[`base parser onError option EOF_IN_TAG <template><div id 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option EOF_IN_TAG <template><div id="abc 1`] = `
+exports[`base parser onError option EOF_IN_TAG <template><div id="abc 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option EOF_IN_TAG <template><div id="abc" 1`] = `
+exports[`base parser onError option EOF_IN_TAG <template><div id="abc" 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option EOF_IN_TAG <template><div id="abc"/ 1`] = `
+exports[`base parser onError option EOF_IN_TAG <template><div id="abc"/ 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option EOF_IN_TAG <template><div id='abc 1`] = `
+exports[`base parser onError option EOF_IN_TAG <template><div id='abc 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option EOF_IN_TAG <template><div id='abc' 1`] = `
+exports[`base parser onError option EOF_IN_TAG <template><div id='abc' 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option EOF_IN_TAG <template><div id='abc'/ 1`] = `
+exports[`base parser onError option EOF_IN_TAG <template><div id='abc'/ 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option EOF_IN_TAG <template><div id=abc / 1`] = `
+exports[`base parser onError option EOF_IN_TAG <template><div id=abc / 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option EOF_IN_TAG <template><div id=abc 1`] = `
+exports[`base parser onError option EOF_IN_TAG <template><div id=abc 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option INCORRECTLY_CLOSED_COMMENT <template><!--comment--!></template> 1`] = `
+exports[`base parser onError option INCORRECTLY_CLOSED_COMMENT <template><!--comment--!></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option INCORRECTLY_OPENED_COMMENT <!DOCTYPE html> 1`] = `
+exports[`base parser onError option INCORRECTLY_OPENED_COMMENT <!DOCTYPE html> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option INCORRECTLY_OPENED_COMMENT <template><!></template> 1`] = `
+exports[`base parser onError option INCORRECTLY_OPENED_COMMENT <template><!></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option INCORRECTLY_OPENED_COMMENT <template><!-></template> 1`] = `
+exports[`base parser onError option INCORRECTLY_OPENED_COMMENT <template><!-></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option INCORRECTLY_OPENED_COMMENT <template><!ELEMENT br EMPTY></template> 1`] = `
+exports[`base parser onError option INCORRECTLY_OPENED_COMMENT <template><!ELEMENT br EMPTY></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option INVALID_FIRST_CHARACTER_OF_TAG_NAME <template></�></template> 1`] = `
+exports[`base parser onError option INVALID_FIRST_CHARACTER_OF_TAG_NAME <template></�></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option INVALID_FIRST_CHARACTER_OF_TAG_NAME <template><�></template> 1`] = `
+exports[`base parser onError option INVALID_FIRST_CHARACTER_OF_TAG_NAME <template><�></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option INVALID_FIRST_CHARACTER_OF_TAG_NAME <template>{{a < b}}</template> 1`] = `
+exports[`base parser onError option INVALID_FIRST_CHARACTER_OF_TAG_NAME <template>{{a < b}}</template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option INVALID_FIRST_CHARACTER_OF_TAG_NAME <template>a < b</template> 1`] = `
+exports[`base parser onError option INVALID_FIRST_CHARACTER_OF_TAG_NAME <template>a < b</template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option INVALID_FIRST_CHARACTER_OF_TAG_NAME <template>a </ b</template> 1`] = `
+exports[`base parser onError option INVALID_FIRST_CHARACTER_OF_TAG_NAME <template>a </ b</template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option MISSING_ATTRIBUTE_VALUE <template><div id= /></div></template> 1`] = `
+exports[`base parser onError option MISSING_ATTRIBUTE_VALUE <template><div id= /></div></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option MISSING_ATTRIBUTE_VALUE <template><div id= ></div></template> 1`] = `
+exports[`base parser onError option MISSING_ATTRIBUTE_VALUE <template><div id= ></div></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option MISSING_ATTRIBUTE_VALUE <template><div id=></div></template> 1`] = `
+exports[`base parser onError option MISSING_ATTRIBUTE_VALUE <template><div id=></div></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option MISSING_END_TAG_NAME <template></></template> 1`] = `
+exports[`base parser onError option MISSING_END_TAG_NAME <template></></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE <template>(</template> 1`] = `
+exports[`base parser onError option MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE <template>(</template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE <template>@</template> 1`] = `
+exports[`base parser onError option MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE <template>@</template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE <template>&</template> 1`] = `
+exports[`base parser onError option MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE <template>&</template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option MISSING_WHITESPACE_BETWEEN_ATTRIBUTES <template><div id="foo"\\x0d;\\x0a;class="bar"></div></template> 1`] = `
+exports[`base parser onError option MISSING_WHITESPACE_BETWEEN_ATTRIBUTES <template><div id="foo"\\x0d;\\x0a;class="bar"></div></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option MISSING_WHITESPACE_BETWEEN_ATTRIBUTES <template><div id="foo"class="bar"></div></template> 1`] = `
+exports[`base parser onError option MISSING_WHITESPACE_BETWEEN_ATTRIBUTES <template><div id="foo"class="bar"></div></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option NESTED_COMMENT <template><!--a<!-- 1`] = `
+exports[`base parser onError option NESTED_COMMENT <template><!--a<!-- 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option NESTED_COMMENT <template><!--a<!--></template> 1`] = `
+exports[`base parser onError option NESTED_COMMENT <template><!--a<!--></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option NESTED_COMMENT <template><!--a<!--b<!--c--></template> 1`] = `
+exports[`base parser onError option NESTED_COMMENT <template><!--a<!--b<!--c--></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option NESTED_COMMENT <template><!--a<!--b--></template> 1`] = `
+exports[`base parser onError option NESTED_COMMENT <template><!--a<!--b--></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option NONCHARACTER_CHARACTER_REFERENCE <template></template> 1`] = `
+exports[`base parser onError option NONCHARACTER_CHARACTER_REFERENCE <template></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option NONCHARACTER_CHARACTER_REFERENCE <template></template> 1`] = `
+exports[`base parser onError option NONCHARACTER_CHARACTER_REFERENCE <template></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option NULL_CHARACTER_REFERENCE <template>�</template> 1`] = `
+exports[`base parser onError option NULL_CHARACTER_REFERENCE <template>�</template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option SURROGATE_CHARACTER_REFERENCE <template>�</template> 1`] = `
+exports[`base parser onError option SURROGATE_CHARACTER_REFERENCE <template>�</template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME <template><div a"bc=''></div></template> 1`] = `
+exports[`base parser onError option UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME <template><div a"bc=''></div></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME <template><div a'bc=''></div></template> 1`] = `
+exports[`base parser onError option UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME <template><div a'bc=''></div></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME <template><div a<bc=''></div></template> 1`] = `
+exports[`base parser onError option UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME <template><div a<bc=''></div></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE <template><div foo=bar"></div></template> 1`] = `
+exports[`base parser onError option UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE <template><div foo=bar"></div></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE <template><div foo=bar'></div></template> 1`] = `
+exports[`base parser onError option UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE <template><div foo=bar'></div></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE <template><div foo=bar<div></div></template> 1`] = `
+exports[`base parser onError option UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE <template><div foo=bar<div></div></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE <template><div foo=bar=baz></div></template> 1`] = `
+exports[`base parser onError option UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE <template><div foo=bar=baz></div></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE <template><div foo=bar\`></div></template> 1`] = `
+exports[`base parser onError option UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE <template><div foo=bar\`></div></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option UNEXPECTED_EQUALS_SIGN_BEFORE_ATTRIBUTE_NAME <template><div =></div></template> 1`] = `
+exports[`base parser onError option UNEXPECTED_EQUALS_SIGN_BEFORE_ATTRIBUTE_NAME <template><div =></div></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option UNEXPECTED_EQUALS_SIGN_BEFORE_ATTRIBUTE_NAME <template><div =foo=bar></div></template> 1`] = `
+exports[`base parser onError option UNEXPECTED_EQUALS_SIGN_BEFORE_ATTRIBUTE_NAME <template><div =foo=bar></div></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option UNEXPECTED_QUESTION_MARK_INSTEAD_OF_TAG_NAME <template><?xml?></template> 1`] = `
+exports[`base parser onError option UNEXPECTED_QUESTION_MARK_INSTEAD_OF_TAG_NAME <template><?xml?></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option UNEXPECTED_SOLIDUS_IN_TAG <template><div a/b></div></template> 1`] = `
+exports[`base parser onError option UNEXPECTED_SOLIDUS_IN_TAG <template><div a/b></div></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option UNKNOWN_NAMED_CHARACTER_REFERENCE <template>&unknown;</template> 1`] = `
+exports[`base parser onError option UNKNOWN_NAMED_CHARACTER_REFERENCE <template>&unknown;</template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option X_INVALID_END_TAG <svg><![CDATA[</div>]]></svg> 1`] = `
+exports[`base parser onError option X_INVALID_END_TAG <svg><![CDATA[</div>]]></svg> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option X_INVALID_END_TAG <svg><!--</div>--></svg> 1`] = `
+exports[`base parser onError option X_INVALID_END_TAG <svg><!--</div>--></svg> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option X_INVALID_END_TAG <template></div></div></template> 1`] = `
+exports[`base parser onError option X_INVALID_END_TAG <template></div></div></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option X_INVALID_END_TAG <template></div></template> 1`] = `
+exports[`base parser onError option X_INVALID_END_TAG <template></div></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option X_INVALID_END_TAG <template>{{'</div>'}}</template> 1`] = `
+exports[`base parser onError option X_INVALID_END_TAG <template>{{'</div>'}}</template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option X_INVALID_END_TAG <textarea></div></textarea> 1`] = `
+exports[`base parser onError option X_INVALID_END_TAG <textarea></div></textarea> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option X_MISSING_END_TAG <template><div> 1`] = `
+exports[`base parser onError option X_MISSING_END_TAG <template><div> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option X_MISSING_END_TAG <template><div></template> 1`] = `
+exports[`base parser onError option X_MISSING_END_TAG <template><div></template> 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option X_MISSING_INTERPOLATION_END {{ 1`] = `
+exports[`base parser onError option X_MISSING_INTERPOLATION_END {{ 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option X_MISSING_INTERPOLATION_END {{ foo 1`] = `
+exports[`base parser onError option X_MISSING_INTERPOLATION_END {{ foo 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse onError option X_MISSING_INTERPOLATION_END {{}} 1`] = `
+exports[`base parser onError option X_MISSING_INTERPOLATION_END {{}} 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse self closing multiple tag 1`] = `
+exports[`base parser self closing multiple tag 1`] = `
Object {
"children": Array [
Object {
}
`;
-exports[`parser/parse valid html 1`] = `
+exports[`base parser valid html 1`] = `
Object {
"children": Array [
Object {
-import { parse, ParserOptions } from '../src/parser'
+import { parse, ParserOptions, TextModes } from '../src/parser'
+import { ParserErrorTypes } from '../src/errorTypes'
import {
- AttributeNode,
CommentNode,
DirectiveNode,
ElementNode,
Position,
TextNode
} from '../src/ast'
-import { ParserErrorTypes } from '../src/errorTypes'
-import { parserOptionsMinimal as parserOptions } from '../src/parserOptionsMinimal'
-describe('parser/parse', () => {
+describe('base parser', () => {
describe('Text', () => {
test('simple text', () => {
- const ast = parse('some text', parserOptions)
+ const ast = parse('some text')
const text = ast.children[0] as TextNode
expect(text).toStrictEqual({
test('simple text with invalid end tag', () => {
const ast = parse('some text</div>', {
- ...parserOptions,
onError: () => {}
})
const text = ast.children[0] as TextNode
})
test('text with interpolation', () => {
- const ast = parse('some {{ foo + bar }} text', parserOptions)
+ const ast = parse('some {{ foo + bar }} text')
const text1 = ast.children[0] as TextNode
const text2 = ast.children[2] as TextNode
})
test('text with interpolation which has `<`', () => {
- const ast = parse('some {{ a<b && c>d }} text', parserOptions)
+ const ast = parse('some {{ a<b && c>d }} text')
const text1 = ast.children[0] as TextNode
const text2 = ast.children[2] as TextNode
})
test('text with mix of tags and interpolations', () => {
- const ast = parse(
- 'some <span>{{ foo < bar + foo }} text</span>',
- parserOptions
- )
+ const ast = parse('some <span>{{ foo < bar + foo }} text</span>')
const text1 = ast.children[0] as TextNode
const text2 = (ast.children[1] as ElementNode).children![1] as TextNode
})
})
- 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
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}`)
})
})
- 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>&</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: '&'
- }
- })
- })
-
- 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>&</style>', 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: 7, line: 1, column: 8 },
- end: { offset: 12, line: 1, column: 13 },
- source: '&'
- }
- })
- })
-
- 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('&ersand;', {
- ...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: '&ersand;'
- }
- })
- 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="&ersand;" b="&ersand;" c="&!"></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: '&ersand;',
- isEmpty: false,
- loc: {
- start: { offset: 7, line: 1, column: 8 },
- end: { offset: 20, line: 1, column: 21 },
- source: '"&ersand;"'
- }
- })
- 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: '"&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: '"&!"'
- }
- })
- 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('†', { ...parserOptions, onError: spy })
+ const ast = parse('†', { onError: spy })
const text = ast.children[0] as TextNode
expect(text).toStrictEqual({
describe('Interpolation', () => {
test('simple interpolation', () => {
- const ast = parse('{{message}}', parserOptions)
+ const ast = parse('{{message}}')
const interpolation = ast.children[0] as ExpressionNode
expect(interpolation).toStrictEqual({
})
test('it can have tag-like notation', () => {
- const ast = parse('{{ a<b }}', parserOptions)
+ const ast = parse('{{ a<b }}')
const interpolation = ast.children[0] as ExpressionNode
expect(interpolation).toStrictEqual({
})
test('it can have tag-like notation (2)', () => {
- const ast = parse('{{ a<b }}{{ c>d }}', parserOptions)
+ const ast = parse('{{ a<b }}{{ c>d }}')
const interpolation1 = ast.children[0] as ExpressionNode
const interpolation2 = ast.children[1] as ExpressionNode
})
test('it can have tag-like notation (3)', () => {
- const ast = parse('<div>{{ "</div>" }}</div>', parserOptions)
+ const ast = parse('<div>{{ "</div>" }}</div>')
const element = ast.children[0] as ElementNode
const interpolation = element.children[0] as ExpressionNode
}
})
})
-
- test('HTML entities in interpolation should be translated for backward compatibility.', () => {
- const ast = parse('<div>{{ a < 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 < b }}'
- }
- })
- })
})
describe('Comment', () => {
test('empty comment', () => {
- const ast = parse('<!---->', parserOptions)
+ const ast = parse('<!---->')
const comment = ast.children[0] as CommentNode
expect(comment).toStrictEqual({
})
test('simple comment', () => {
- const ast = parse('<!--abc-->', parserOptions)
+ const ast = parse('<!--abc-->')
const comment = ast.children[0] as CommentNode
expect(comment).toStrictEqual({
})
test('two comments', () => {
- const ast = parse('<!--abc--><!--def-->', parserOptions)
+ const ast = parse('<!--abc--><!--def-->')
const comment1 = ast.children[0] as CommentNode
const comment2 = ast.children[1] as CommentNode
describe('Element', () => {
test('simple div', () => {
- const ast = parse('<div>hello</div>', parserOptions)
+ const ast = parse('<div>hello</div>')
const element = ast.children[0] as ElementNode
expect(element).toStrictEqual({
})
test('empty', () => {
- const ast = parse('<div></div>', parserOptions)
+ const ast = parse('<div></div>')
const element = ast.children[0] as ElementNode
expect(element).toStrictEqual({
})
test('self closing', () => {
- const ast = parse('<div/>after', parserOptions)
+ const ast = parse('<div/>after')
const element = ast.children[0] as ElementNode
expect(element).toStrictEqual({
})
test('void element', () => {
- const ast = parse('<img>after', parserOptions)
+ const ast = parse('<img>after', {
+ isVoidTag: tag => tag === 'img'
+ })
const element = ast.children[0] as ElementNode
expect(element).toStrictEqual({
})
test('attribute with no value', () => {
- const ast = parse('<div id></div>', parserOptions)
+ const ast = parse('<div id></div>')
const element = ast.children[0] as ElementNode
expect(element).toStrictEqual({
})
test('attribute with empty value, double quote', () => {
- const ast = parse('<div id=""></div>', parserOptions)
+ const ast = parse('<div id=""></div>')
const element = ast.children[0] as ElementNode
expect(element).toStrictEqual({
})
test('attribute with empty value, single quote', () => {
- const ast = parse("<div id=''></div>", parserOptions)
+ const ast = parse("<div id=''></div>")
const element = ast.children[0] as ElementNode
expect(element).toStrictEqual({
})
test('attribute with value, double quote', () => {
- const ast = parse('<div id=">\'"></div>', parserOptions)
+ const ast = parse('<div id=">\'"></div>')
const element = ast.children[0] as ElementNode
expect(element).toStrictEqual({
})
test('attribute with value, single quote', () => {
- const ast = parse("<div id='>\"'></div>", parserOptions)
+ const ast = parse("<div id='>\"'></div>")
const element = ast.children[0] as ElementNode
expect(element).toStrictEqual({
})
test('attribute with value, unquoted', () => {
- const ast = parse('<div id=a/></div>', parserOptions)
+ const ast = parse('<div id=a/></div>')
const element = ast.children[0] as ElementNode
expect(element).toStrictEqual({
})
test('multiple attributes', () => {
- const ast = parse(
- '<div id=a class="c" inert style=\'\'></div>',
- parserOptions
- )
+ const ast = parse('<div id=a class="c" inert style=\'\'></div>')
const element = ast.children[0] as ElementNode
expect(element).toStrictEqual({
})
test('directive with no value', () => {
- const ast = parse('<div v-if/>', parserOptions)
+ const ast = parse('<div v-if/>')
const directive = (ast.children[0] as ElementNode)
.props[0] as DirectiveNode
})
test('directive with value', () => {
- const ast = parse('<div v-if="a"/>', parserOptions)
+ const ast = parse('<div v-if="a"/>')
const directive = (ast.children[0] as ElementNode)
.props[0] as DirectiveNode
})
test('directive with argument', () => {
- const ast = parse('<div v-on:click/>', parserOptions)
+ const ast = parse('<div v-on:click/>')
const directive = (ast.children[0] as ElementNode)
.props[0] as DirectiveNode
})
test('directive with a modifier', () => {
- const ast = parse('<div v-on.enter/>', parserOptions)
+ const ast = parse('<div v-on.enter/>')
const directive = (ast.children[0] as ElementNode)
.props[0] as DirectiveNode
})
test('directive with two modifiers', () => {
- const ast = parse('<div v-on.enter.exact/>', parserOptions)
+ const ast = parse('<div v-on.enter.exact/>')
const directive = (ast.children[0] as ElementNode)
.props[0] as DirectiveNode
})
test('directive with argument and modifiers', () => {
- const ast = parse('<div v-on:click.enter.exact/>', parserOptions)
+ const ast = parse('<div v-on:click.enter.exact/>')
const directive = (ast.children[0] as ElementNode)
.props[0] as DirectiveNode
})
test('v-bind shorthand', () => {
- const ast = parse('<div :a=b />', parserOptions)
+ const ast = parse('<div :a=b />')
const directive = (ast.children[0] as ElementNode)
.props[0] as DirectiveNode
})
test('v-bind shorthand with modifier', () => {
- const ast = parse('<div :a.sync=b />', parserOptions)
+ const ast = parse('<div :a.sync=b />')
const directive = (ast.children[0] as ElementNode)
.props[0] as DirectiveNode
})
test('v-on shorthand', () => {
- const ast = parse('<div @a=b />', parserOptions)
+ const ast = parse('<div @a=b />')
const directive = (ast.children[0] as ElementNode)
.props[0] as DirectiveNode
})
test('v-on shorthand with modifier', () => {
- const ast = parse('<div @a.enter=b />', parserOptions)
+ const ast = parse('<div @a.enter=b />')
const directive = (ast.children[0] as ElementNode)
.props[0] as DirectiveNode
})
test('end tags are case-insensitive.', () => {
- const ast = parse('<div>hello</DIV>after', parserOptions)
+ const ast = parse('<div>hello</DIV>after')
const element = ast.children[0] as ElementNode
const text = element.children[0] as TextNode
}
})
})
-
- 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)
+ const ast = parse('<div :class="{ some: condition }" />')
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
+ `<p v-bind:style="{ color: 'red' }"/>`
)
expect(ast).toMatchSnapshot()
`<div :class="{ some: condition }">\n` +
` <p v-bind:style="{ color: 'red' }"/>\n` +
` <!-- a comment with <html> inside it -->\n` +
- `</div>`,
- parserOptions
+ `</div>`
)
expect(ast).toMatchSnapshot()
test('invalid html', () => {
expect(() => {
- parse(`<div>\n<span>\n</div>\n</span>`, parserOptions)
+ parse(`<div>\n<span>\n</div>\n</span>`)
}).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
})
test('parse with correct location info', () => {
const [foo, bar, but, baz] = parse(
- 'foo \n is {{ bar }} but {{ baz }}',
- parserOptions
+ 'foo \n is {{ bar }} but {{ baz }}'
).children
let offset = 0
describe('namedCharacterReferences option', () => {
test('use the given map', () => {
const ast: any = parse('&∪︀', {
- ...parserOptions,
namedCharacterReferences: {
'cups;': '\u222A\uFE00' // UNION with serifs
},
})
})
- describe('onError option', () => {
+ describe('Errors', () => {
const patterns: {
[key: string]: Array<{
code: string
() => {
const spy = jest.fn()
const ast = parse(code, {
- ...parserOptions,
+ getNamespace: (tag, parent) => {
+ const ns = parent ? parent.ns : Namespaces.HTML
+ if (ns === Namespaces.HTML) {
+ if (tag === 'svg') {
+ return (Namespaces.HTML + 1) as any
+ }
+ }
+ return ns
+ },
+ getTextMode: tag => {
+ if (tag === 'textarea') {
+ return TextModes.RCDATA
+ }
+ if (tag === 'script') {
+ return TextModes.RAWTEXT
+ }
+ return TextModes.DATA
+ },
...options,
onError: spy
})
+// Vue template is a platform-agnostic superset of HTML (syntax only).
+// More namespaces like SVG and MathML are declared by platform specific
+// compilers.
+export type Namespace = number
+
+export const enum Namespaces {
+ HTML
+}
+
export const enum NodeTypes {
TEXT,
COMMENT,
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 ElementNode extends Node {
type: NodeTypes.ELEMENT
- ns: Namespaces
+ ns: Namespace
tag: string
tagType: ElementTypes
isSelfClosing: boolean
-export { parse } from './parser'
+export { parse, ParserOptions, TextModes } from './parser'
export * from './ast'
export * from './errorTypes'
-import { ParserErrorTypes } from './errorTypes'
+import { ParserErrorTypes, errorMessages } from './errorTypes'
import {
- Node,
+ Namespace,
+ Namespaces,
AttributeNode,
CommentNode,
DirectiveNode,
ElementNode,
ElementTypes,
ExpressionNode,
- Namespaces,
NodeTypes,
Position,
RootNode,
} 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
+ isVoidTag?: (tag: string) => boolean // e.g. img, br, hr
+ getNamespace?: (tag: string, parent: ElementNode | undefined) => Namespace
+ getTextMode?: (tag: string, ns: Namespace) => TextModes
+ delimiters?: [string, string] // ['{{', '}}']
+ 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 }
+ namedCharacterReferences?: { [name: string]: string | undefined }
- onError: (type: ParserErrorTypes, loc: Position) => void
+ onError?: (type: ParserErrorTypes, loc: Position) => void
+}
+
+export const defaultParserOptions: Required<ParserOptions> = {
+ delimiters: [`{{`, `}}`],
+ ignoreSpaces: true,
+ getNamespace: () => Namespaces.HTML,
+ getTextMode: () => TextModes.DATA,
+ isVoidTag: () => false,
+ namedCharacterReferences: {},
+ onError(code: ParserErrorTypes, loc: Position): void {
+ const error: any = new SyntaxError(
+ `${errorMessages[code]} (${loc.line}:${loc.column})`
+ )
+ error.code = code
+ error.loc = loc
+ throw error
+ }
}
export const enum TextModes {
- // | Elements | Entities | End sign | Inside of
- DATA, // | ✔ | ✔ | End tags of ancestors |
- RCDATA, // | ✘ | ✔ | End tag of the parent | <textarea>
+ // | 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 {
+interface ParserContext extends Required<ParserOptions> {
readonly originalSource: string
source: string
offset: number
maxCRNameLength: number
}
-export function parse(content: string, options: ParserOptions): RootNode {
+export function parse(content: string, options: ParserOptions = {}): RootNode {
const context = createParserContext(content, options)
const start = getCursor(context)
options: ParserOptions
): ParserContext {
return {
+ ...defaultParserOptions,
...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
- )
+ maxCRNameLength: Object.keys(
+ options.namedCharacterReferences ||
+ defaultParserOptions.namedCharacterReferences
+ ).reduce((max, name) => Math.max(max, name.length), 0)
}
}
+++ /dev/null
-import { TextModes, ParserOptions } from './parser'
-import { ElementNode, Namespaces, Position, Node } from './ast'
-import { ParserErrorTypes, errorMessages } 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(
- `${errorMessages[code]} (${loc.line}:${loc.column})`
- )
- error.code = code
- error.loc = loc
- throw error
- },
-
- transform(node: Node): Node {
- return node
- }
-}
--- /dev/null
+import {
+ parse,
+ NodeTypes,
+ ElementNode,
+ TextNode,
+ AttributeNode,
+ ParserErrorTypes,
+ ExpressionNode,
+ ElementTypes
+} from '@vue/compiler-core'
+import {
+ parserOptionsMinimal as parserOptions,
+ DOMNamespaces
+} from '../src/parserOptionsMinimal'
+
+describe('DOM parser', () => {
+ describe('Text', () => {
+ test('textarea handles comments/elements as just 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>&</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: '&'
+ }
+ })
+ })
+
+ 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>&</style>', 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: 7, line: 1, column: 8 },
+ end: { offset: 12, line: 1, column: 13 },
+ source: '&'
+ }
+ })
+ })
+
+ 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('HTML entities compatibility in text (https://html.spec.whatwg.org/multipage/parsing.html#named-character-reference-state).', () => {
+ const spy = jest.fn()
+ const ast = parse('&ersand;', {
+ ...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: '&ersand;'
+ }
+ })
+ 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="&ersand;" b="&ersand;" c="&!"></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: '&ersand;',
+ isEmpty: false,
+ loc: {
+ start: { offset: 7, line: 1, column: 8 },
+ end: { offset: 20, line: 1, column: 21 },
+ source: '"&ersand;"'
+ }
+ })
+ 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: '"&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: '"&!"'
+ }
+ })
+ expect(spy.mock.calls).toEqual([
+ [
+ ParserErrorTypes.MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE,
+ { offset: 45, line: 1, column: 46 }
+ ]
+ ])
+ })
+ })
+
+ describe('Interpolation', () => {
+ test('HTML entities in interpolation should be translated for backward compatibility.', () => {
+ const ast = parse('<div>{{ a < 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 < b }}'
+ }
+ })
+ })
+ })
+
+ describe('Element', () => {
+ test('void element', () => {
+ const ast = parse('<img>after', parserOptions)
+ const element = ast.children[0] as ElementNode
+
+ expect(element).toStrictEqual({
+ type: NodeTypes.ELEMENT,
+ ns: DOMNamespaces.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('Strict end tag detection for textarea.', () => {
+ 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>'
+ }
+ })
+ })
+ })
+})
// TODO
export * from '@vue/compiler-core'
+export { parserOptionsMinimal } from './parserOptionsMinimal'
+export { parserOptionsStandard } from './parserOptionsStandard'
--- /dev/null
+import {
+ NodeTypes,
+ TextModes,
+ ParserOptions,
+ ElementNode,
+ Namespaces
+} from '@vue/compiler-core'
+
+export const enum DOMNamespaces {
+ HTML = Namespaces.HTML,
+ SVG,
+ MATH_ML
+}
+
+const MATH_ML_TEXT_INTEGRATION_POINT_RE = /^m(?:[ions]|text)$/
+const RAW_TEXT_CONTAINER_RE = /^(?:style|xmp|iframe|noembed|noframes|script|noscript)$/i
+const VOID_TAG_RE = /^(?:area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/i
+
+export const parserOptionsMinimal: ParserOptions = {
+ // https://html.spec.whatwg.org/multipage/parsing.html#tree-construction-dispatcher
+ getNamespace(tag: string, parent: ElementNode | undefined): DOMNamespaces {
+ let ns = parent ? parent.ns : DOMNamespaces.HTML
+
+ if (parent && ns === DOMNamespaces.MATH_ML) {
+ if (parent.tag === 'annotation-xml') {
+ if (tag === 'svg') {
+ return DOMNamespaces.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 = DOMNamespaces.HTML
+ }
+ } else if (
+ MATH_ML_TEXT_INTEGRATION_POINT_RE.test(parent.tag) &&
+ tag !== 'mglyph' &&
+ tag !== 'malignmark'
+ ) {
+ ns = DOMNamespaces.HTML
+ }
+ } else if (parent && ns === DOMNamespaces.SVG) {
+ if (
+ parent.tag === 'foreignObject' ||
+ parent.tag === 'desc' ||
+ parent.tag === 'title'
+ ) {
+ ns = DOMNamespaces.HTML
+ }
+ }
+
+ if (ns === DOMNamespaces.HTML) {
+ if (tag === 'svg') {
+ return DOMNamespaces.SVG
+ }
+ if (tag === 'math') {
+ return DOMNamespaces.MATH_ML
+ }
+ }
+ return ns
+ },
+
+ // https://html.spec.whatwg.org/multipage/parsing.html#parsing-html-fragments
+ getTextMode(tag: string, ns: DOMNamespaces): TextModes {
+ if (ns === DOMNamespaces.HTML) {
+ if (tag === 'textarea' || tag === 'title') {
+ return TextModes.RCDATA
+ }
+ if (RAW_TEXT_CONTAINER_RE.test(tag)) {
+ return TextModes.RAWTEXT
+ }
+ }
+ return TextModes.DATA
+ },
+
+ isVoidTag(tag: string): boolean {
+ return VOID_TAG_RE.test(tag)
+ },
+
+ namedCharacterReferences: {
+ 'gt;': '>',
+ 'lt;': '<',
+ 'amp;': '&',
+ 'apos;': "'",
+ 'quot;': '"'
+ }
+}
-import { TextModes, ParserOptions } from './parser'
-import { ElementNode, Namespaces, NodeTypes } from './ast'
+import { ParserOptions } from '@vue/compiler-core'
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: '>',