From dbce817326ead7bd8f56523695eb3aafb063fff9 Mon Sep 17 00:00:00 2001 From: Juan-Pablo Scaletti Date: Wed, 10 Jul 2024 13:34:08 -0500 Subject: [PATCH] Add support to double-curly-braces syntax in addition of the vue-like syntax for passing arguments to components --- pyproject.toml | 2 +- src/jinjax/jinjax.py | 21 +++++++++++++++++---- tests/test_render.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 545281d..a2c4e59 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "jinjax" -version = "0.41" +version = "0.42" description = "Replace your HTML templates with Python server-Side components" authors = ["Juan-Pablo Scaletti "] license = "MIT" diff --git a/src/jinjax/jinjax.py b/src/jinjax/jinjax.py index d537b64..d0a3b05 100644 --- a/src/jinjax/jinjax.py +++ b/src/jinjax/jinjax.py @@ -29,7 +29,7 @@ (?P[a-zA-Z@:$_][a-zA-Z@:$_0-9-]*) (?: \s*=\s* - (?P".*?"|'.*?') + (?P".*?"|'.*?'|\{\{.*?\}\}) )? (?:\s+|/|"|$) """ @@ -86,6 +86,8 @@ def _process_tag(self, source: str, match: re.Match) -> str: tag = match.group("tag") attrs = (match.group("attrs") or "").strip() inline = match.group(0).endswith("/>") + lineno = source[:start].count("\n") + 1 + logger.debug(f"{tag} {attrs} {'inline' if not inline else ''}") if inline: content = "" @@ -95,7 +97,7 @@ def _process_tag(self, source: str, match: re.Match) -> str: if index == -1: raise TemplateSyntaxError( message=f"Unclosed component {match.group(0)}", - lineno=source[:start].count("\n") + 1, + lineno=lineno, name=self._name, filename=self._filename ) @@ -104,6 +106,7 @@ def _process_tag(self, source: str, match: re.Match) -> str: attrs_list = self._parse_attrs(attrs) repl = self._build_call(tag, attrs_list, content) + return f"{source[:start]}{repl}{source[end:]}" def _parse_attrs(self, attrs: str) -> list[tuple[str, str]]: @@ -128,9 +131,19 @@ def _build_call( name = name.lstrip(":") attrs.append(f'"{name}"=True') else: - if name.startswith(":"): - name = name.lstrip(":") + # vue-like syntax + if ( + name[0] == ":" + and value[0] in ("\"'") + and value[-1] in ("\"'") + ): value = value[1:-1].strip() + + # double curly braces syntax + if value[:2] == "{{" and value[-2:] == "}}": + value = value[2:-2].strip() + + name = name.lstrip(":") attrs.append(f'"{name}"={value}') str_attrs = "**{" + ", ".join([a.replace("=", ":", 1) for a in attrs]) + "}" diff --git a/tests/test_render.py b/tests/test_render.py index 6776530..0327521 100644 --- a/tests/test_render.py +++ b/tests/test_render.py @@ -857,3 +857,45 @@ def test_auto_load_assets_with_same_name(catalog, folder, autoescape): """.strip() assert html == Markup(expected) + + +def test_vue_like_syntax(catalog, folder): + (folder / "Test.jinja").write_text(""" + {#def a, b, c, d #} + {{ a }} {{ b }} {{ c }} {{ d }} + """) + (folder / "Caller.jinja").write_text( + """""" + ) + html = catalog.render("Caller") + print(html) + expected = """4 2+2 {'lorem': 'ipsum'} False""".strip() + assert html == Markup(expected) + + +def test_jinja_like_syntax(catalog, folder): + (folder / "Test.jinja").write_text(""" + {#def a, b, c, d #} + {{ a }} {{ b }} {{ c }} {{ d }} + """) + (folder / "Caller.jinja").write_text( + """""" + ) + html = catalog.render("Caller") + print(html) + expected = """4 2+2 {'lorem': 'ipsum'} False""".strip() + assert html == Markup(expected) + + +def test_mixed_syntax(catalog, folder): + (folder / "Test.jinja").write_text(""" + {#def a, b, c, d #} + {{ a }} {{ b }} {{ c }} {{ d }} + """) + (folder / "Caller.jinja").write_text( + """""" + ) + html = catalog.render("Caller") + print(html) + expected = """4 {{2+2}} {'lorem': 'ipsum'} False""".strip() + assert html == Markup(expected)