templates: Mark all void tags as self-closing.

This reverses the policy that was set, but incompletely enforced, by
commit 951514dd7d.  The self-closing tag
syntax is clearer, more consistent, simpler to parse, compatible with
XML, preferred by Prettier, and (most importantly now) required by
FormatJS.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg
2021-04-20 15:46:14 -07:00
committed by Tim Abbott
parent 7177529107
commit dd3fa4ac52
84 changed files with 345 additions and 346 deletions

View File

@@ -120,9 +120,9 @@ def tokenize(text: str) -> List[Token]:
tag = tag_parts[0]
if is_special_html_tag(s, tag):
kind = "html_special"
elif is_self_closing_html_tag(s, tag):
if tag == "!DOCTYPE":
kind = "html_doctype"
elif s.endswith("/>"):
kind = "html_singleton"
else:
kind = "html_start"
@@ -203,6 +203,26 @@ def tokenize(text: str) -> List[Token]:
return tokens
HTML_VOID_TAGS = {
"area",
"base",
"br",
"col",
"command",
"embed",
"hr",
"img",
"input",
"keygen",
"link",
"meta",
"param",
"source",
"track",
"wbr",
}
def validate(
fn: Optional[str] = None, text: Optional[str] = None, check_indent: bool = True
) -> None:
@@ -227,6 +247,7 @@ def validate(
class State:
def __init__(self, func: Callable[[Token], None]) -> None:
self.depth = 0
self.foreign = False
self.matcher = func
def no_start_tag(token: Token) -> None:
@@ -249,6 +270,10 @@ def validate(
start_col = start_token.col
old_matcher = state.matcher
old_foreign = state.foreign
if start_tag in ["math", "svg"]:
state.foreign = True
def f(end_token: Token) -> None:
@@ -283,6 +308,7 @@ def validate(
"""
)
state.matcher = old_matcher
state.foreign = old_foreign
state.depth -= 1
state.matcher = f
@@ -292,7 +318,16 @@ def validate(
tag = token.tag
if kind == "html_start":
if not state.foreign and tag in HTML_VOID_TAGS:
raise TemplateParserException(
f"Tag must be self-closing: {tag} at {fn} line {token.line}, col {token.col}"
)
start_tag_matcher(token)
elif kind == "html_singleton":
if not state.foreign and tag not in HTML_VOID_TAGS:
raise TemplateParserException(
f"Tag must not be self-closing: {tag} at {fn} line {token.line}, col {token.col}"
)
elif kind == "html_end":
state.matcher(token)
@@ -315,45 +350,6 @@ def validate(
raise TemplateParserException("Missing end tag")
def is_special_html_tag(s: str, tag: str) -> bool:
return tag in ["link", "meta", "!DOCTYPE"]
OPTIONAL_CLOSING_TAGS = [
"br",
"circle",
"img",
"input",
"path",
"polygon",
"stop",
]
def is_self_closing_html_tag(s: str, tag: str) -> bool:
if s.endswith("/>"):
if tag in OPTIONAL_CLOSING_TAGS:
return True
raise TokenizationException("Singleton tag not allowed", tag)
self_closing_tag = tag in [
"area",
"base",
"br",
"col",
"embed",
"hr",
"img",
"input",
"param",
"source",
"track",
"wbr",
]
if self_closing_tag:
return True
return False
def is_django_block_tag(tag: str) -> bool:
return tag in [
"autoescape",