tools: Enforce '_html' suffix for unescaped hbs vars.

This adds a check to enforce the new convention of
raw HTML variables having a `_html` suffix for
better clarity.

Discussion: https://chat.zulip.org/#narrow/channel/92-learning/topic/Marking.20commits.20to.20be.20squashed.20in.20PRs
Signed-off-by: apoorvapendse <apoorvavpendse@gmail.com>
This commit is contained in:
apoorvapendse
2025-08-10 12:38:18 +05:30
committed by Tim Abbott
parent a935866601
commit 3188d9db31
3 changed files with 79 additions and 2 deletions

View File

@@ -81,6 +81,9 @@ def tokenize(text: str, template_format: str | None = None) -> list[Token]:
def looking_at_handlebars_partial_block() -> bool:
return template_format == "handlebars" and looking_at("{{#>")
def looking_at_handlebars_triple_stache() -> bool:
return template_format == "handlebars" and (looking_at("{{{") or looking_at("{{~{"))
def looking_at_html_start() -> bool:
return looking_at("<") and not looking_at("</")
@@ -184,6 +187,19 @@ def tokenize(text: str, template_format: str | None = None) -> list[Token]:
s = get_handlebars_tag(text, state.i)
tag = "else"
kind = "handlebars_else"
elif looking_at_handlebars_triple_stache():
s = get_handlebars_triple_stache_tag(text, state.i)
start_offset = end_offset = 3
if s.startswith("{{~{"):
start_offset += 1
if s.endswith("}~}}"):
end_offset += 1
tag = s[start_offset:-end_offset].strip()
if not tag.endswith("_html"):
raise TemplateParserError(
"Unescaped variables in triple staches {{{ }}} must be suffixed with `_html`"
)
kind = "handlebars_triple_stache"
elif looking_at_handlebars_start():
s = get_handlebars_tag(text, state.i)
tag = s[3:-2].split()[0].strip("#").removeprefix("*")
@@ -321,6 +337,7 @@ def tag_flavor(token: Token) -> str | None:
"template_var",
"text",
"whitespace",
"handlebars_triple_stache",
):
return None
@@ -632,6 +649,18 @@ def get_handlebars_tag(text: str, i: int) -> str:
return s
def get_handlebars_triple_stache_tag(text: str, i: int) -> str:
end = i + 3
while end < len(text) - 3 and text[end] != "}":
end += 1
if text[end : end + 3] == "}}}":
return text[i : end + 3]
elif end + 4 <= len(text) and text[end : end + 4] == "}~}}":
return text[i : end + 4]
else:
raise TokenizationError('Tag missing "}}}"', text[i : end + 3])
def get_spaces(text: str, i: int) -> str:
s = ""
while i < len(text) and text[i] in " ":

View File

@@ -179,7 +179,7 @@ BAD_HTML8 = """
{{#each test}}
{{#with this}}
{{#if foobar}}
<div class="anything">{{{test}}}</div>
<div class="anything">{{{test_html}}}</div>
{{/if}}
{{#if foobar2}}
{{> teststuff}}
@@ -192,7 +192,7 @@ GOOD_HTML8 = """
{{#each test}}
{{#with this}}
{{#if foobar}}
<div class="anything">{{{test}}}</div>
<div class="anything">{{{test_html}}}</div>
{{/if}}
{{#if foobar2}}
{{> teststuff}}

View File

@@ -147,6 +147,54 @@ class ParserTest(unittest.TestCase):
template_format="handlebars",
)
def test_validate_triple_stache_var_1(self) -> None:
my_html = """
{{{ foo}}
"""
self._assert_validate_error(
'Tag missing "}}}" at line 2 col 13:"{{{ foo}}\n"',
text=my_html,
template_format="handlebars",
)
def test_validate_triple_stache_var_2(self) -> None:
my_html = """
{{{ foo}~}
"""
self._assert_validate_error(
'Tag missing "}}}" at line 2 col 13:"{{{ foo}~}"',
text=my_html,
template_format="handlebars",
)
def test_validate_triple_stache_var_3(self) -> None:
my_html = """
{{{ foo }}}
"""
self._assert_validate_error(
"Unescaped variables in triple staches {{{ }}} must be suffixed with `_html`",
text=my_html,
template_format="handlebars",
)
def test_validate_triple_stache_var_4(self) -> None:
my_html = """
{{{ foo_html }~}}
"""
validate(text=my_html, template_format="handlebars")
def test_validate_triple_stache_var_5(self) -> None:
my_html = "{{{ foo_html}}}"
validate(text=my_html, template_format="handlebars")
def test_validate_triple_stache_var_6(self) -> None:
my_html = "{{~{ bar_html}}}"
validate(text=my_html, template_format="handlebars")
def test_validate_triple_stache_var_7(self) -> None:
my_html = "{{~{ bar_html}~}}"
validate(text=my_html, template_format="handlebars")
def test_validate_incomplete_django_tag_1(self) -> None:
my_html = """
{% foo