Repository: michaelbukachi/flask-vuesfc

Issue:
Add support for escaping variable placeholders
Currently, the extension automatically converts `{{ }}` to `[[ ]]` so that it doesn't get parsed by flask's template engine. This is convenient most of the time, however, there are times when we the placeholder to parsed by flask instead of Vue, This issue proposes the introduction of triple parenthesis `{{{ }}}` for such cases. When the converter meets triple parenthesis it converts them to double parenthesis where it will be parsed by the flask parser.

Comments:

Patch:
diff --git a/README.rst b/README.rst
index 9f1ab59..5cd44a6 100644
--- a/README.rst
+++ b/README.rst
@@ -122,6 +122,13 @@ escape it. So be sure to always use the ``safe`` filter when rendering the compo
 
 Feel free to checkout the examples folder for other examples.
 
+Escaping
+--------
+Occasionally, you might need a variable to be parsed by ``jinja`` instead of ``vue``. To achieve this, use triple
+parenthesis i.e ``{{{ variable }}}``.
+
+Checkout **examples/app_with_escaping.py**.
+
 --------------
 Configuration
 --------------
diff --git a/example/app_with_escaping.py b/example/app_with_escaping.py
new file mode 100644
index 0000000..9be1b30
--- /dev/null
+++ b/example/app_with_escaping.py
@@ -0,0 +1,27 @@
+from flask import Flask, render_template
+
+from flask_vue_sfc import VueSFC
+from flask_vue_sfc.helpers import render_vue_component
+
+
+class Config:
+    SECRET_KEY = 'some-very-long-secret'
+
+
+def create_app():
+    app = Flask(__name__)
+    app.config.from_object(Config)
+
+    VueSFC(app)
+
+    @app.route('/')
+    def example1():
+        component = render_vue_component('index4.vue', message='This is rendered by flask')
+        return render_template('example.html', component=component)
+
+    return app
+
+
+if __name__ == '__main__':
+    application = create_app()
+    application.run(debug=True)
diff --git a/example/templates/index4.vue b/example/templates/index4.vue
new file mode 100644
index 0000000..5702999
--- /dev/null
+++ b/example/templates/index4.vue
@@ -0,0 +1,15 @@
+<template>
+  <div>{{ message }}</div>
+  <div>{{{ message }}}</div>
+</template>
+<script>
+
+export default  {
+  name: 'App',
+  data() {
+    return {
+      message: 'This is rendered by Vue',
+    }
+  }
+}
+</script>
\ No newline at end of file
diff --git a/flask_vue_sfc/helpers.py b/flask_vue_sfc/helpers.py
index b7b6bb0..ecc10de 100644
--- a/flask_vue_sfc/helpers.py
+++ b/flask_vue_sfc/helpers.py
@@ -10,10 +10,11 @@ def _create_random_id():
     return 'vue-sfc-' + secrets.token_hex(6)
 
 
-def _load_template(template_name):
+def _load_template(template_name, **context):
     ctx = _app_ctx_stack.top
+    ctx.app.update_template_context(context)
     t = ctx.app.jinja_env.get_or_select_template(template_name)
-    vue = t.render()
+    vue = t.render(context)
     parsed = ctx.g.v8.call('VueTemplateCompiler.parseComponent', vue)
 
     component = {
@@ -25,7 +26,7 @@ def _load_template(template_name):
     return component
 
 
-def _render_component(template_name):
+def _render_component(template_name, **context):
     ctx = _app_ctx_stack.top
 
     if 'sfc_cache' in ctx.g:
@@ -33,7 +34,7 @@ def _render_component(template_name):
         if sfc:
             return sfc
 
-    src = _load_template(template_name)
+    src = _load_template(template_name, **context)
     component = VueComponent(src, _create_random_id, _load_template)
     sfc = component.render(ctx.g.v8)
     sfc = str(sfc)
@@ -46,11 +47,12 @@ def _render_component(template_name):
 
 def render_vue_component(template_name, **context):
     is_page = context.get('is_page', False)
-    component = _render_component(template_name)
+    component = _render_component(template_name, **context)
     if is_page:
         return render_template('page.html', component=component)
     return component
 
 
-def render_vue_page(template_name):
-    return render_vue_component(template_name, is_page=True)
+def render_vue_page(template_name, **context):
+    context['is_page'] = True
+    return render_vue_component(template_name, **context)
diff --git a/flask_vue_sfc/utils.py b/flask_vue_sfc/utils.py
index 81871d9..f4c6bbc 100644
--- a/flask_vue_sfc/utils.py
+++ b/flask_vue_sfc/utils.py
@@ -17,7 +17,9 @@ class VueLoader(FileSystemLoader):
         if template and template.lower().endswith('.vue'):
             # We don't want jinja to touch  {{ }}
             contents, filename, uptodate = super(VueLoader, self).get_source(environment, template)
-            contents = '{% raw %}\n' + contents.replace('</template>', '</template>\n{% endraw %}')
+            contents = _change_delimiters(contents)
+            # contents = '{% raw %}\n' + contents.replace('</template>', '</template>\n{% endraw %}')
+            # print(contents)
             return contents, filename, uptodate
         return super(VueLoader, self).get_source(environment, template)
 
@@ -234,10 +236,8 @@ class HtmlTemplate:
         )
         html = html_minify(html)
         # Handler delimiters replacement to prevent conflicts with jinja
-        if '{{' in html:
-            html = html.replace('{{', '[[')
-            html = html.replace('}}', ']]')
         return html
+        # return _change_delimiters(html)
 
 
 class ChildHtmlTemplate(HtmlTemplate):
@@ -255,10 +255,8 @@ class ChildHtmlTemplate(HtmlTemplate):
         )
         html = html_minify(html)
         # Handler delimiters replacement to prevent conflicts with jinja
-        if '{{' in html:
-            html = html.replace('{{', '[[')
-            html = html.replace('}}', ']]')
         return html
+        # return _change_delimiters(html)
 
 
 class CssStyling:
@@ -330,3 +328,17 @@ class VueChildComponent(ChildVueScript, ChildHtmlTemplate, CssStyling):
 def _get_file_contents(path):
     with open(path, 'r') as fp:
         return fp.read()
+
+
+def _change_delimiters(html):
+    if '{{' in html:
+        html = html.replace('{{{', 'op_par')
+        html = html.replace('}}}', 'cl_par')
+
+        html = html.replace('{{', '[[')
+        html = html.replace('}}', ']]')
+
+        html = html.replace('op_par', '{{')
+        html = html.replace('cl_par', '}}')
+
+    return html


Test patch:
diff --git a/tests/test_utils.py b/tests/test_utils.py
index c4ba0d6..7044407 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -1,4 +1,4 @@
-from flask_vue_sfc.utils import VueComponent, VueChildComponent, SFC
+from flask_vue_sfc.utils import VueComponent, VueChildComponent, SFC, _change_delimiters
 
 
 def test_render_html():
@@ -185,3 +185,9 @@ def test_render_sfc__with_child():
         "</style>\n"
     )
     assert str(sfc) == expected
+
+
+def test_change_delimiters__escape_syntax():
+    expected = '{{ test }}'
+    html = _change_delimiters('{{{ test }}}')
+    assert expected == html