Skip to content

Commit 511e8a7

Browse files
authored
chore: Add MJML templating example (#30)
* add mjml example * update example * _ * _ * _ * use libffi from system, do not build * restore macos aarch build * use local build for testing * _ * _ * _ * _ * _init_ function is optional * _ * _ * _
1 parent 90cde2f commit 511e8a7

29 files changed

+2135
-3037
lines changed

.github/workflows/runtime.yaml

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,54 +5,35 @@ on:
55
branches:
66
- main
77
tags:
8-
- '*'
8+
- "*"
99
pull_request:
1010

1111
jobs:
12-
Test:
13-
runs-on: ubuntu-latest
14-
steps:
15-
- uses: actions/checkout@v4
16-
- uses: dart-lang/setup-dart@v1
17-
18-
- name: Install Globe Runtime
19-
run: |
20-
sudo apt-get update && sudo apt-get install -y jq curl
21-
mkdir -p ~/.globe/runtime && cd ~/.globe/runtime
22-
curl -s https://api.github.com/repos/invertase/globe_runtime/releases/latest \
23-
| jq -r '.assets[] | select(.name == "libglobe_runtime-x86_64-unknown-linux-gnu.so") | .browser_download_url' \
24-
| xargs curl -L -OJ
25-
26-
- name: Run Dart Tests
27-
working-directory: packages/globe_runtime
28-
run: dart test --reporter expanded --concurrency=1
29-
3012
Build:
31-
needs: Test
3213
name: Build - ${{ matrix.platform.os-name }}
3314
strategy:
3415
matrix:
3516
platform:
3617
- os-name: MacOS-x86_64
37-
runs-on: macOS-latest
18+
runs-on: macos-15-intel
3819
target: x86_64-apple-darwin
39-
20+
4021
- os-name: MacOS-aarch64
41-
runs-on: macOS-latest
22+
runs-on: macos-15
4223
target: aarch64-apple-darwin
43-
24+
4425
- os-name: Linux-x86_64
4526
runs-on: ubuntu-22.04
4627
target: x86_64-unknown-linux-gnu
4728

4829
- os-name: Linux-aarch64
4930
runs-on: ubuntu-22.04-arm
50-
target: aarch64-unknown-linux-gnu
31+
target: aarch64-unknown-linux-gnu
5132

5233
- os-name: Windows-x86_64
53-
runs-on: windows-latest
34+
runs-on: windows-2025
5435
target: x86_64-pc-windows-msvc
55-
36+
5637
runs-on: ${{ matrix.platform.runs-on }}
5738
steps:
5839
- name: Checkout
@@ -99,13 +80,37 @@ jobs:
9980
mv "$ARTIFACT_FILE" "$NEW_NAME"
10081
10182
echo "artifact_path=$NEW_NAME" >> $GITHUB_ENV
102-
83+
10384
- name: Upload built library as artifact
10485
uses: actions/upload-artifact@v4
10586
with:
10687
name: ${{ env.artifact_path }}
10788
path: ${{ env.artifact_path }}
10889

90+
Test:
91+
needs: Build
92+
runs-on: ubuntu-latest
93+
steps:
94+
- uses: actions/checkout@v4
95+
96+
- name: Download Linux x86_64 artifact
97+
uses: actions/download-artifact@v4
98+
with:
99+
name: libglobe_runtime-x86_64-unknown-linux-gnu.so
100+
path: ./artifacts
101+
102+
- name: Add library path to environment
103+
run: |
104+
LIB_PATH="${GITHUB_WORKSPACE}/artifacts/libglobe_runtime-x86_64-unknown-linux-gnu.so"
105+
echo "GLOBE_RUNTIME_LIB_PATH=${LIB_PATH}" >> $GITHUB_ENV
106+
echo "Library path set to: ${LIB_PATH}"
107+
ls -la "${LIB_PATH}"
108+
109+
- uses: dart-lang/setup-dart@v1
110+
- name: Run Dart Tests
111+
working-directory: packages/globe_runtime
112+
run: dart test --reporter expanded --concurrency=1
113+
109114
Release:
110115
name: Publish Release
111116
if: github.ref_type == 'tag'
@@ -125,5 +130,3 @@ jobs:
125130
uses: softprops/action-gh-release@v2
126131
with:
127132
files: dist/**/*
128-
129-

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ javascript
33
node_modules
44
.dart_tool
55
.idea
6+
dist
67

78
**/pubspec_overrides.yaml
89
*.iml

Cargo.lock

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

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,8 @@ node-resolve = "2.2.0"
2121
[build-dependencies]
2222
bindgen = "0.71.1"
2323
cc = "1.0"
24+
25+
26+
[dependencies.libffi-sys]
27+
version = "2.3.0"
28+
features = ["system"]

examples/basic_example/pubspec.lock

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,10 @@ packages:
124124
globe_runtime:
125125
dependency: "direct main"
126126
description:
127-
name: globe_runtime
128-
sha256: c538800ceabfacc1e7ab51d733cb58a5f269649cbc5c022f78531d64867fef40
129-
url: "https://pub.dev"
130-
source: hosted
131-
version: "1.0.7"
127+
path: "../../packages/globe_runtime"
128+
relative: true
129+
source: path
130+
version: "1.0.8"
132131
http:
133132
dependency: transitive
134133
description:

examples/mjml/bin/main.dart

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import 'dart:io';
2+
import 'package:mjml_bridge/mjml_bridge.dart';
3+
4+
const mjmlSource = r'''
5+
<mjml>
6+
<mj-body background-color="#F4F4F4">
7+
<mj-section background-color="#ffffff" background-repeat="repeat" padding-bottom="0px" padding-top="30px" padding="20px 0" text-align="center">
8+
<mj-column>
9+
<mj-image align="center" padding="10px 25px" src="https://static.mailjet.com/mjml-website/templates/christmas-logo.png" target="_blank" width="214px"></mj-image>
10+
<mj-text align="left" color="#55575d" font-family="Arial, sans-serif" font-size="13px" line-height="22px" padding-bottom="15px" padding-top="0px" padding="10px 25px">
11+
<p style="text-align: center; margin: 10px 0;color:#151e23;font-size:14px;font-family:Georgia,Helvetica,Arial,sans-serif">Product | Concept | Contact</p>
12+
</mj-text>
13+
</mj-column>
14+
</mj-section>
15+
<mj-section background-repeat="repeat" padding-bottom="0px" padding-top="0px" padding="20px 0" text-align="center">
16+
<mj-column>
17+
<mj-image align="center" padding-bottom="0px" padding-left="0px" padding-right="0px" padding-top="0px" padding="10px 25px" src="https://static.mailjet.com/mjml-website/templates/christmas-hero.jpg" target="_blank" width="600px"></mj-image>
18+
</mj-column>
19+
</mj-section>
20+
<mj-section background-color="#ffffff" background-repeat="repeat" background-size="auto" padding-bottom="0px" padding-top="30px" padding="20px 0" text-align="center">
21+
<mj-column>
22+
<mj-text align="left" color="#55575d" font-family="Arial, sans-serif" font-size="30px" line-height="22px" padding-bottom="10px" padding-top="10px" padding="10px 25px">
23+
<p style="line-height: 30px; margin: 10px 0; text-align: center; color:#151e23; font-size:30p; font-family:Georgia,Helvetica,Arial,sans-serif">- Our Holiday Recipes -</p>
24+
</mj-text>
25+
</mj-column>
26+
</mj-section>
27+
<mj-section background-color="#ffffff" background-repeat="repeat" padding-bottom="0px" padding="20px 0" text-align="center">
28+
<mj-column>
29+
<mj-image align="center" padding-bottom="20px" padding-left="30px" padding-right="30px" padding-top="0px" padding="10px 25px" src="https://static.mailjet.com/mjml-website/templates/christmas-product-1.jpg" target="_blank" width="1200px"></mj-image>
30+
</mj-column>
31+
<mj-column>
32+
<mj-text align="left" color="#55575d" font-family="Arial, sans-serif" font-size="13px" line-height="22px" padding-bottom="0px" padding-left="40px" padding-right="40px" padding-top="0px" padding="10px 25px">
33+
<p style="margin: 10px 0; color:#151e23; font-size:16px; font-family:Georgia,Helvetica,Arial,sans-serif"><b>Cake Title</b></p>
34+
<p style="line-height: 16px; margin: 10px 0;font-size:14px; color:#151e23; font-family:Georgia,Helvetica,Arial,sans-serif; color:#354552">Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.</p>
35+
<p style="line-height: 16px; margin: 10px 0; color:#354552; font-size:14px; font-family:Georgia,Helvetica,Arial,sans-serif"><u>Choose me</u> &gt;</p>
36+
</mj-text>
37+
</mj-column>
38+
</mj-section>
39+
<mj-section background-color="#ffffff" background-repeat="repeat" direction="rtl" padding-bottom="0px" padding-top="0px" padding="20px 0" text-align="center">
40+
<mj-column>
41+
<mj-image align="center" padding-bottom="20px" padding-left="30px" padding-right="30px" padding-top="20px" padding="10px 25px" src="https://static.mailjet.com/mjml-website/templates/christmas-product-2.jpg" target="_blank" width="1200px"></mj-image>
42+
</mj-column>
43+
<mj-column>
44+
<mj-text align="left" color="#55575d" font-family="Arial, sans-serif" font-size="13px" line-height="22px" padding-bottom="0px" padding-left="40px" padding-right="40px" padding-top="0px" padding="10px 25px">
45+
<p style="margin: 10px 0; color:#151e23; font-size:16px; font-family:Georgia,Helvetica,Arial,sans-serif"><b>Cake Title</b></p>
46+
<p style="line-height: 16px; margin: 10px 0;font-size:14px; color:#151e23; font-family:Georgia,Helvetica,Arial,sans-serif; color:#354552">Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.</p>
47+
<p style="line-height: 16px; margin: 10px 0; color:#354552; font-size:14px; font-family:Georgia,Helvetica,Arial,sans-serif"><u>Choose me</u> &gt;</p>
48+
</mj-text>
49+
</mj-column>
50+
</mj-section>
51+
<mj-section background-color="#ffffff" background-repeat="repeat" padding-bottom="0px" padding-top="0px" padding="20px 0" text-align="center">
52+
<mj-column>
53+
<mj-image align="center" padding-bottom="20px" padding-left="30px" padding-right="30px" padding-top="20px" padding="10px 25px" src="https://static.mailjet.com/mjml-website/templates/christmas-product-3.jpg" target="_blank" width="1200px"></mj-image>
54+
</mj-column>
55+
<mj-column>
56+
<mj-text align="left" color="#55575d" font-family="Arial, sans-serif" font-size="13px" line-height="22px" padding-bottom="0px" padding-left="40px" padding-right="40px" padding-top="0px" padding="10px 25px">
57+
<p style="margin: 10px 0; color:#151e23; font-size:16px; font-family:Georgia,Helvetica,Arial,sans-serif"><b>Cake Title</b></p>
58+
<p style="line-height: 16px; margin: 10px 0;font-size:14px; color:#151e23; font-family:Georgia,Helvetica,Arial,sans-serif; color:#354552">Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.</p>
59+
<p style="line-height: 16px; margin: 10px 0; color:#354552; font-size:14px; font-family:Georgia,Helvetica,Arial,sans-serif"><u>Choose me</u> &gt;</p>
60+
</mj-text>
61+
</mj-column>
62+
</mj-section>
63+
<mj-section background-color="#ffffff" background-repeat="repeat" padding-top="0px" padding="20px 0" text-align="center">
64+
<mj-column>
65+
<mj-button align="center" background-color="#354552" border-radius="3px" color="#ffffff" font-family="Georgia, Helvetica, Arial, sans-serif" font-size="14px" font-weight="normal" inner-padding="10px 25px" padding="10px 25px" text-decoration="none" text-transform="none" vertical-align="middle">Discover all desserts</mj-button>
66+
</mj-column>
67+
</mj-section>
68+
<mj-section background-color="#ffffff" background-repeat="repeat" padding-bottom="0px" padding-top="0px" padding="20px 0" text-align="center">
69+
<mj-column>
70+
<mj-image align="center" padding-bottom="0px" padding-left="0px" padding-right="0px" padding-top="0px" padding="10px 25px" src="https://static.mailjet.com/mjml-website/templates/christmas-footer.jpg" target="_blank" width="600px"></mj-image>
71+
</mj-column>
72+
</mj-section>
73+
<mj-section background-color="#ffffff" background-repeat="repeat" padding="20px 0" text-align="center">
74+
<mj-column>
75+
<mj-image align="center" padding="10px 25px" src="https://static.mailjet.com/mjml-website/templates/christmas-logo.png" target="_blank" width="202px"></mj-image>
76+
<mj-social align="center">
77+
<mj-social-element name="facebook"></mj-social-element>
78+
<mj-social-element name="pinterest"></mj-social-element>
79+
<mj-social-element name="instagram"></mj-social-element>
80+
</mj-social>
81+
</mj-column>
82+
</mj-section>
83+
</mj-body>
84+
</mjml>
85+
''';
86+
87+
void main() async {
88+
final result = await mjmlSource.render();
89+
90+
final server = await HttpServer.bind(InternetAddress.loopbackIPv4, 8080);
91+
server.listen((request) {
92+
request.response.headers.contentType = ContentType.html;
93+
request.response
94+
..write(result.html)
95+
..close();
96+
});
97+
98+
if (Platform.isWindows) {
99+
await Process.run('cmd', ['/c', 'start', 'http://localhost:8080']);
100+
} else if (Platform.isMacOS) {
101+
await Process.run('open', ['http://localhost:8080']);
102+
} else if (Platform.isLinux) {
103+
await Process.run('xdg-open', ['http://localhost:8080']);
104+
}
105+
}

examples/mjml/lib/mjml_bridge.dart

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import 'dart:async';
2+
3+
import 'package:globe_runtime/globe_runtime.dart';
4+
import 'package:mjml_bridge/mjml_bridge_source.dart';
5+
6+
const _module = InlinedModule(name: 'mjml_bridge', sourceCode: packageSource);
7+
8+
extension MjMlExtension on String {
9+
Future<MJMLResult> render({MJMLOptions? options}) async {
10+
final completer = Completer<MJMLResult>();
11+
12+
await _module.register();
13+
14+
_module.callFunction(
15+
'render',
16+
args: [
17+
this.toFFIType,
18+
(options?.toJson() ?? {}).toFFIType,
19+
],
20+
onData: (data) {
21+
if (data.hasError()) {
22+
completer.completeError(data.error);
23+
} else {
24+
final json = data.data.unpack();
25+
completer.complete(MJMLResult.fromJson(json));
26+
}
27+
return true;
28+
},
29+
);
30+
return completer.future;
31+
}
32+
}
33+
34+
class MJMLOptions {
35+
final bool? keepComments;
36+
final bool? socialIconPath;
37+
final String? fonts;
38+
final String? validationLevel;
39+
40+
const MJMLOptions({
41+
this.keepComments,
42+
this.socialIconPath,
43+
this.fonts,
44+
this.validationLevel,
45+
});
46+
47+
Map<String, dynamic> toJson() {
48+
return {
49+
if (keepComments != null) 'keepComments': keepComments,
50+
if (socialIconPath != null) 'socialIconPath': socialIconPath,
51+
if (fonts != null) 'fonts': fonts,
52+
if (validationLevel != null) 'validationLevel': validationLevel,
53+
};
54+
}
55+
}
56+
57+
class MJMLResult {
58+
final String html;
59+
final List<dynamic> errors;
60+
61+
const MJMLResult({required this.html, required this.errors});
62+
63+
factory MJMLResult.fromJson(Map<dynamic, dynamic> json) {
64+
return MJMLResult(
65+
html: json['html'] ?? '',
66+
errors: json['errors'] ?? [],
67+
);
68+
}
69+
}

examples/mjml/lib/mjml_bridge.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import mjml2html from "mjml-browser";
2+
3+
function render(_: any, mjml: string, options: any, callbackId: number) {
4+
const result = mjml2html(mjml, options);
5+
const encoded = JsonPayload.encode(result);
6+
Dart.send_value(callbackId, encoded);
7+
}
8+
9+
export default {
10+
functions: {
11+
render,
12+
},
13+
};

0 commit comments

Comments
 (0)