Skip to content

Commit 6ddd2c1

Browse files
authored
Merge pull request #3 from mailcarrierapp/feat/latte-to-twig
2 parents 9b9731f + 4eb0fee commit 6ddd2c1

File tree

14 files changed

+113
-214
lines changed

14 files changed

+113
-214
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Design global layouts, compose your template, preview your emails and send them
1212

1313
### Features
1414

15-
- 🎨 **Beautiful syntax**: Explore a beautiful, expressive template syntax similar to the most popular JS frameworks, brought you by [Latte](https://latte.nette.org/).
15+
- 🎨 **Beautiful syntax**: Explore a beautiful, expressive template syntax similar to JS thanks to [Twig by Symfony](https://twig.symfony.com).
1616
- 🧩 **Provider aware**: Bring your desired provider to send email, such as Amazon SES, MailGun, SendGrid etc.
1717
-**Friendly APIs**: Use a friendly and well documented API endpoint to send your emails.
1818
- 🔐 **Secure by default**: Both authentication and API endpoint are always secure: use one of the pre-built auth system or bring your own.

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@
2323
"jessarcher/laravel-castable-data-transfer-object": "^2.2.1",
2424
"laravel/sanctum": "^3.0",
2525
"laravel/socialite": "^5.5",
26-
"latte/latte": "^3.0",
2726
"livewire/livewire": "^2.10",
2827
"nunomaduro/termwind": "^1.13",
2928
"ralphjsmit/laravel-filament-components": "^1.1",
3029
"socialiteproviders/manager": "^4.1",
31-
"spatie/data-transfer-object": "^3.8"
30+
"spatie/data-transfer-object": "^3.8",
31+
"twig/twig": "^3.0"
3232
},
3333
"require-dev": {
3434
"laravel/pint": "^1.0",

composer.lock

Lines changed: 77 additions & 85 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/js/424.js

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/js/424.js.LICENSE.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/*!-----------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Version: 0.33.0(4b1abad427e58dbedc1215d99a0902ffc885fcd4)
4+
* Released under the MIT license
5+
* https://github.com/microsoft/monaco-editor/blob/main/LICENSE.txt
6+
*-----------------------------------------------------------------------------*/

dist/js/monaco.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

resources/views/forms/components/monaco-editor.blade.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class="w-full rounded-lg shadow-sm focus:border-primary-600 focus:ring-1 focus:r
1717
document.addEventListener('DOMContentLoaded', () => {
1818
const editor = monaco.editor.create($el, {
1919
value: state,
20-
language: 'html',
20+
language: 'twig',
2121
automaticLayout: true,
2222
scrollBeyondLastLine: false,
2323
theme: 'vs-dark',

src/Actions/SendMail.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,10 @@ protected function send(RecipientDto $recipient): void
9898

9999
try {
100100
$templateRender = (new Templates\Render)->run($this->template, $recipient->variables);
101-
} catch (Exception $e) {
102-
$exception = new TemplateRenderException($e->getMessage());
101+
} catch (\Twig\Error\RuntimeError $e) {
102+
$exception = new TemplateRenderException($e->getRawMessage());
103103

104-
if (str_contains($e->getMessage(), 'Undefined variable')) {
105-
$missingVariableName = Str::match('/Undefined variable \$([\w\d_]+)/i', $e->getMessage());
104+
if ($missingVariableName = Str::match('/Variable "(.*)" does not exist/i', $e->getRawMessage())) {
106105
$exception = new MissingVariableException(
107106
sprintf(
108107
'Missing variable "%s" for template "%s"',

src/Actions/Templates/Render.php

Lines changed: 10 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22

33
namespace MailCarrier\Actions\Templates;
44

5-
use Illuminate\Support\Str;
6-
use Illuminate\Support\Stringable;
7-
use Latte;
85
use MailCarrier\Actions\Action;
96
use MailCarrier\Models\Template;
107

@@ -15,71 +12,22 @@ class Render extends Action
1512
*/
1613
public function run(Template $template, array $variables = []): string
1714
{
18-
$mainFileName = sprintf('main-%d.latte', $template->id);
19-
$layoutFileName = !$template->layout_id ? null : sprintf('layout-%d.latte', $template->layout_id);
15+
$mainFileName = sprintf('main-%d.html', $template->id);
16+
$layoutFileName = !$template->layout_id ? null : sprintf('layout-%d.html', $template->layout_id);
2017
$mainFileContent = !$template->layout_id ? $template->content : sprintf(
21-
"{layout '%s'}{block content}%s{/block}",
18+
'{%% extends "%s" %%}{%% block content %%}%s{%% endblock %%}',
2219
$layoutFileName,
2320
$template->content,
2421
);
2522

26-
$latte = new Latte\Engine();
27-
$latte->setLoader(new Latte\Loaders\StringLoader([
28-
$mainFileName => $this->parse($mainFileContent),
29-
$layoutFileName => $this->parse($template->layout?->content ?: ''),
30-
]));
23+
$loader = new \Twig\Loader\ArrayLoader([
24+
$mainFileName => $mainFileContent,
25+
$layoutFileName => $template->layout?->content,
26+
]);
3127

32-
return $latte->renderToString($mainFileName, $variables);
33-
}
34-
35-
/**
36-
* Parse the template syntax.
37-
*/
38-
protected function parse(string $template): string
39-
{
40-
preg_match_all('/{{(.*?)}}/', $template, $variables);
41-
42-
foreach ($variables[1] ?? [] as $variable) {
43-
$realVariable = Str::of($variable)
44-
->before('|')
45-
->trim()
46-
->toString();
47-
48-
// Extract filters to reapply them when replacing the variable
49-
$variableFilters = '';
50-
51-
if (str_contains($variable, '|')) {
52-
$variableFilters = Str::of($variable)
53-
->after('|')
54-
->trim()
55-
->whenNotEmpty(fn (Stringable $str) => $str->prepend('|'))
56-
->toString();
57-
}
58-
59-
// Transform variable if is an array/object
60-
if (str_contains($realVariable, '.')) {
61-
// Transform everything after the actual variable name into an array-based index
62-
$indexBasedVariable = Str::of($realVariable)
63-
->after('.')
64-
->explode('.')
65-
->map(fn (string $value) => is_numeric($value) ? "[$value]" : "['$value']")
66-
->join('');
67-
68-
// Prepend the actual variable name
69-
$realVariable = Str::before($realVariable, '.') . $indexBasedVariable;
70-
}
71-
72-
if (!str_starts_with($realVariable, '$')) {
73-
$realVariable = '$' . $realVariable;
74-
}
75-
76-
$template = str_replace(
77-
'{{' . $variable . '}}',
78-
'{' . $realVariable . $variableFilters . '}',
79-
$template
80-
);
81-
}
28+
$twig = new \Twig\Environment($loader);
29+
$twig->enableStrictVariables();
8230

83-
return $template;
31+
return $twig->render($mainFileName, $variables);
8432
}
8533
}

src/Resources/LayoutResource.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class LayoutResource extends Resource
3232
<meta name="viewport" content="width=device-width, initial-scale=1.0">
3333
</head>
3434
<body>
35-
{include content}
35+
{% block content %}{% endblock %}
3636
</body>
3737
</html>
3838
HTML;
@@ -102,7 +102,7 @@ protected static function getFormContent(): array
102102

103103
MonacoEditor::make('content')
104104
->required()
105-
->hint('<a href="https://latte.nette.org/en/syntax" class="underline text-primary-500 cursor-help" target="_blank" tabindex="-1">Help with template Syntax</a>')
105+
->hint(new HtmlString('<a href="https://twig.symfony.com/doc/3.x/templates.html" class="underline text-primary-500 cursor-help" target="_blank" tabindex="-1">Help with syntax</a>'))
106106
->hintIcon('heroicon-o-code')
107107
// Full width
108108
->columnSpan(2)

0 commit comments

Comments
 (0)