@marlinjai/email-editor-core 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/MjmlBuilder-DsiOyhiL.d.mts +667 -0
- package/dist/MjmlBuilder-DsiOyhiL.d.ts +667 -0
- package/dist/{TemplateModel-v__r5Nvi.d.mts → TemplateModel-CmpvrapI.d.ts} +2 -433
- package/dist/{TemplateModel-v__r5Nvi.d.ts → TemplateModel-PcCoyBdo.d.mts} +2 -433
- package/dist/browser.d.mts +13 -0
- package/dist/browser.d.ts +13 -0
- package/dist/browser.js +959 -0
- package/dist/browser.mjs +30 -0
- package/dist/chunk-NQPMHQ5G.mjs +490 -0
- package/dist/chunk-OOCYGZMB.mjs +1406 -0
- package/dist/index.d.mts +7 -30
- package/dist/index.d.ts +7 -30
- package/dist/index.js +881 -33
- package/dist/index.mjs +46 -51
- package/dist/server.d.mts +9 -167
- package/dist/server.d.ts +9 -167
- package/dist/server.js +172 -79
- package/dist/server.mjs +55 -817
- package/package.json +13 -7
- package/dist/TemplateModel-Dq4GoWpl.d.mts +0 -14743
- package/dist/TemplateModel-Dq4GoWpl.d.ts +0 -14743
- package/dist/chunk-2ZL7A2I6.mjs +0 -1029
- package/dist/chunk-MBTNU2JZ.mjs +0 -395
package/dist/server.js
CHANGED
|
@@ -32,18 +32,25 @@ var server_exports = {};
|
|
|
32
32
|
__export(server_exports, {
|
|
33
33
|
DEFAULT_MJML_ATTRIBUTES: () => DEFAULT_MJML_ATTRIBUTES,
|
|
34
34
|
EDITOR_DATA_ATTRIBUTE: () => EDITOR_DATA_ATTRIBUTE,
|
|
35
|
+
EDITOR_MARKER: () => EDITOR_MARKER,
|
|
35
36
|
MAX_MJML_BYTES: () => MAX_MJML_BYTES,
|
|
36
37
|
MAX_MJML_DEPTH: () => MAX_MJML_DEPTH,
|
|
37
38
|
MAX_MJML_ELEMENTS: () => MAX_MJML_ELEMENTS,
|
|
38
39
|
MJMLCompiler: () => MJMLCompiler,
|
|
39
40
|
MJMLExporter: () => MJMLExporter,
|
|
41
|
+
MjmlBuilder: () => MjmlBuilder,
|
|
40
42
|
MjmlImportError: () => MjmlImportError,
|
|
43
|
+
addPx: () => addPx,
|
|
44
|
+
base64Utf8: () => base64Utf8,
|
|
41
45
|
createMJMLCompiler: () => createMJMLCompiler,
|
|
42
46
|
createMJMLExporter: () => createMJMLExporter,
|
|
43
47
|
encodeEditorData: () => encodeEditorData,
|
|
44
48
|
exportTemplate: () => exportTemplate,
|
|
45
49
|
importMjml: () => importMjml,
|
|
46
|
-
isMjmlImportError: () => isMjmlImportError
|
|
50
|
+
isMjmlImportError: () => isMjmlImportError,
|
|
51
|
+
mjmlOptions: () => mjmlOptions,
|
|
52
|
+
paddingWithGap: () => paddingWithGap,
|
|
53
|
+
stripEditorMarkup: () => stripEditorMarkup
|
|
47
54
|
});
|
|
48
55
|
module.exports = __toCommonJS(server_exports);
|
|
49
56
|
|
|
@@ -82,6 +89,9 @@ function spacingToCss(p) {
|
|
|
82
89
|
const t = p.top ?? "0", r = p.right ?? "0", b = p.bottom ?? "0", l = p.left ?? "0";
|
|
83
90
|
return `padding:${t} ${r} ${b} ${l};`;
|
|
84
91
|
}
|
|
92
|
+
function identity(b) {
|
|
93
|
+
return `class="el-${b.type} el-${b.id}"`;
|
|
94
|
+
}
|
|
85
95
|
function textToHtml(b) {
|
|
86
96
|
const styles = [];
|
|
87
97
|
if (b.color) styles.push(`color:${b.color}`);
|
|
@@ -91,11 +101,11 @@ function textToHtml(b) {
|
|
|
91
101
|
if (b.align) styles.push(`text-align:${b.align}`);
|
|
92
102
|
styles.push("margin:0");
|
|
93
103
|
const padding = spacingToCss(b.padding);
|
|
94
|
-
return `<div style="${styles.join(";")};${padding}">${b.content}</div>`;
|
|
104
|
+
return `<div ${identity(b)} style="${styles.join(";")};${padding}">${b.content}</div>`;
|
|
95
105
|
}
|
|
96
106
|
function imageToHtml(b) {
|
|
97
107
|
const align = b.align ?? "center";
|
|
98
|
-
const wrap =
|
|
108
|
+
const wrap = `${identity(b)} style="text-align:${align};${spacingToCss(b.padding)}"`;
|
|
99
109
|
const widthAttr = b.width ? ` width="${escapeAttr(b.width)}"` : "";
|
|
100
110
|
const heightAttr = b.height ? ` height="${escapeAttr(b.height)}"` : "";
|
|
101
111
|
const radius = b.borderRadius ? `border-radius:${b.borderRadius};` : "";
|
|
@@ -110,7 +120,7 @@ function buttonToHtml(b) {
|
|
|
110
120
|
const radius = b.borderRadius ?? "3px";
|
|
111
121
|
const innerPadding = b.innerPadding ?? "10px 25px";
|
|
112
122
|
const border = b.border ? `border:${b.border};` : "";
|
|
113
|
-
return `<div style="text-align:${align};${spacingToCss(b.padding)}">
|
|
123
|
+
return `<div ${identity(b)} style="text-align:${align};${spacingToCss(b.padding)}">
|
|
114
124
|
<table role="presentation" cellpadding="0" cellspacing="0" border="0" style="border-collapse:separate;line-height:100%;display:inline-block;">
|
|
115
125
|
<tr>
|
|
116
126
|
<td align="center" valign="middle" role="presentation" style="background-color:${bg};border-radius:${radius};${border}padding:${innerPadding};">
|
|
@@ -125,12 +135,12 @@ function dividerToHtml(b) {
|
|
|
125
135
|
const w = b.borderWidth ?? "1px";
|
|
126
136
|
const style = b.borderStyle ?? "solid";
|
|
127
137
|
const width = b.width ?? "100%";
|
|
128
|
-
return `<div style="${spacingToCss(b.padding)}">
|
|
138
|
+
return `<div ${identity(b)} style="${spacingToCss(b.padding)}">
|
|
129
139
|
<div style="border-top:${w} ${style} ${color};width:${width};font-size:1px;line-height:1px;"> </div>
|
|
130
140
|
</div>`;
|
|
131
141
|
}
|
|
132
142
|
function spacerToHtml(b) {
|
|
133
|
-
return `<div style="height:${b.height};line-height:${b.height};font-size:1px;"> </div>`;
|
|
143
|
+
return `<div ${identity(b)} style="height:${b.height};line-height:${b.height};font-size:1px;"> </div>`;
|
|
134
144
|
}
|
|
135
145
|
function socialToHtml(b) {
|
|
136
146
|
const items = (b.links ?? []).map((link) => {
|
|
@@ -139,7 +149,7 @@ function socialToHtml(b) {
|
|
|
139
149
|
<img src="${escapeAttr(icon)}" alt="${escapeAttr(link.platform)}" width="24" height="24" style="display:inline-block;border:0;" />
|
|
140
150
|
</a>`;
|
|
141
151
|
}).join("");
|
|
142
|
-
return `<div style="text-align:center;">${items}</div>`;
|
|
152
|
+
return `<div ${identity(b)} style="text-align:center;">${items}</div>`;
|
|
143
153
|
}
|
|
144
154
|
function blockToRawHtml(block) {
|
|
145
155
|
if (!isLeafBlockType(block.type)) {
|
|
@@ -181,7 +191,7 @@ function buildGradientCSS(gradient) {
|
|
|
181
191
|
return `linear-gradient(${gradient.angle}deg, ${stopList})`;
|
|
182
192
|
}
|
|
183
193
|
|
|
184
|
-
// src/compiler/
|
|
194
|
+
// src/compiler/MjmlBuilder.ts
|
|
185
195
|
var DEFAULT_MJML_ATTRIBUTES = `
|
|
186
196
|
<mj-all font-family="Georgia, serif" />
|
|
187
197
|
<mj-text font-size="14px" line-height="1.6" />
|
|
@@ -190,8 +200,30 @@ var EDITOR_DATA_ATTRIBUTE = {
|
|
|
190
200
|
social: "data-ee-social",
|
|
191
201
|
subColumns: "data-ee-subcols"
|
|
192
202
|
};
|
|
203
|
+
function base64Utf8(text) {
|
|
204
|
+
const bytes = new TextEncoder().encode(text);
|
|
205
|
+
let binary = "";
|
|
206
|
+
for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
|
|
207
|
+
return btoa(binary);
|
|
208
|
+
}
|
|
209
|
+
var EDITOR_MARKER = { begin: "<!--ee:begin-->", end: "<!--ee:end-->" };
|
|
210
|
+
function stripEditorMarkup(markup) {
|
|
211
|
+
return markup.replace(/<!--ee:begin-->[\s\S]*?<!--ee:end-->/g, "").replace(/<!--\/?ee:raw:[^>]*-->/g, "");
|
|
212
|
+
}
|
|
213
|
+
function rawMarkers(id, html, editor) {
|
|
214
|
+
return editor ? `<!--ee:raw:${id}-->${html}<!--/ee:raw:${id}-->` : html;
|
|
215
|
+
}
|
|
216
|
+
function textInlineStyles(block) {
|
|
217
|
+
const styles = [];
|
|
218
|
+
if (block.align) styles.push(`text-align:${block.align}`);
|
|
219
|
+
if (block.color) styles.push(`color:${block.color}`);
|
|
220
|
+
if (block.fontSize) styles.push(`font-size:${block.fontSize}`);
|
|
221
|
+
if (block.fontFamily) styles.push(`font-family:${block.fontFamily}`);
|
|
222
|
+
if (block.lineHeight) styles.push(`line-height:${block.lineHeight}`);
|
|
223
|
+
return styles;
|
|
224
|
+
}
|
|
193
225
|
function encodeEditorData(value) {
|
|
194
|
-
return
|
|
226
|
+
return base64Utf8(JSON.stringify(value));
|
|
195
227
|
}
|
|
196
228
|
function finishAttributes(attrs, classes, extra) {
|
|
197
229
|
const out = [...attrs];
|
|
@@ -219,13 +251,37 @@ function spacingToString(spacing) {
|
|
|
219
251
|
if (!top && !right && !bottom && !left) return "";
|
|
220
252
|
return [top || "0", right || "0", bottom || "0", left || "0"].join(" ");
|
|
221
253
|
}
|
|
222
|
-
var
|
|
254
|
+
var MJML_SECTION_PADDING = { top: "20px", right: "0", bottom: "20px", left: "0" };
|
|
255
|
+
function addPx(a, b) {
|
|
256
|
+
const px = (v) => v === "0" ? 0 : /^-?\d+(\.\d+)?px$/.test(v) ? Number.parseFloat(v) : NaN;
|
|
257
|
+
const sum = px(a) + px(b);
|
|
258
|
+
if (Number.isNaN(sum)) return void 0;
|
|
259
|
+
return `${Number.isInteger(sum) ? sum : Number(sum.toFixed(2))}px`;
|
|
260
|
+
}
|
|
261
|
+
function paddingWithGap(spacing, gap) {
|
|
262
|
+
const base = spacing ?? MJML_SECTION_PADDING;
|
|
263
|
+
const top = addPx(base.top || "0", gap);
|
|
264
|
+
return top === void 0 ? base : { ...base, top };
|
|
265
|
+
}
|
|
266
|
+
function mjmlOptions(options = {}) {
|
|
267
|
+
return {
|
|
268
|
+
// Never resolve <mj-include>: its path is read from the server's disk,
|
|
269
|
+
// and a stored document (a Raw block's html) must not reach local files.
|
|
270
|
+
ignoreIncludes: true,
|
|
271
|
+
validationLevel: "soft",
|
|
272
|
+
minify: false,
|
|
273
|
+
...options.webFonts === false ? { fonts: {} } : {}
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
var MjmlBuilder = class {
|
|
223
277
|
constructor() {
|
|
224
278
|
/**
|
|
225
279
|
* Set true while emitting a column with sub-columns. Causes generateHead
|
|
226
280
|
* to inject the responsive media query exactly once per template.
|
|
227
281
|
*/
|
|
228
282
|
this.needsSubColumnStyles = false;
|
|
283
|
+
/** Set for the duration of one build in editor mode; see {@link BuildOptions}. */
|
|
284
|
+
this.editor = false;
|
|
229
285
|
/**
|
|
230
286
|
* Platform brand colors for social icons
|
|
231
287
|
*/
|
|
@@ -239,40 +295,25 @@ var MJMLCompiler = class {
|
|
|
239
295
|
github: "#181717"
|
|
240
296
|
};
|
|
241
297
|
}
|
|
242
|
-
/**
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
const result = (0, import_mjml.default)(mjml, {
|
|
250
|
-
// Never resolve <mj-include>: its path is read from the server's disk,
|
|
251
|
-
// and a stored document (a Raw block's html) must not reach local files.
|
|
252
|
-
ignoreIncludes: true,
|
|
253
|
-
validationLevel: "soft",
|
|
254
|
-
minify: false,
|
|
255
|
-
...options.webFonts === false ? { fonts: {} } : {}
|
|
256
|
-
});
|
|
257
|
-
return {
|
|
258
|
-
mjml,
|
|
259
|
-
html: result.html,
|
|
260
|
-
errors: result.errors.length > 0 ? result.errors.map((e) => e.formattedMessage) : void 0
|
|
261
|
-
};
|
|
262
|
-
} catch (error) {
|
|
263
|
-
return {
|
|
264
|
-
mjml: "",
|
|
265
|
-
html: "",
|
|
266
|
-
errors: [error instanceof Error ? error.message : "Unknown compilation error"]
|
|
267
|
-
};
|
|
268
|
-
}
|
|
298
|
+
/** `fragment` fenced as an editor addition, or nothing outside editor mode. */
|
|
299
|
+
ee(fragment) {
|
|
300
|
+
return this.editor ? `${EDITOR_MARKER.begin}${fragment}${EDITOR_MARKER.end}` : "";
|
|
301
|
+
}
|
|
302
|
+
/** The label of a ghost: what the node is, so the person can find and unhide it. */
|
|
303
|
+
ghostText(cssClasses, label) {
|
|
304
|
+
return `<mj-text css-class="ee-ghost ${cssClasses.join(" ")}" padding="8px 25px" font-size="12px" line-height="1.4" color="#6b7280" container-background-color="#f3f4f6"><span style="display:block;border:1px dashed #9ca3af;padding:6px 10px;">${label} (hidden)</span></mj-text>`;
|
|
269
305
|
}
|
|
270
306
|
/**
|
|
271
307
|
* The MJML markup for a document, without compiling it to HTML.
|
|
272
308
|
*/
|
|
273
|
-
toMJML(template) {
|
|
309
|
+
toMJML(template, options = {}) {
|
|
274
310
|
this.needsSubColumnStyles = false;
|
|
275
|
-
|
|
311
|
+
this.editor = options.editor === true;
|
|
312
|
+
try {
|
|
313
|
+
return this.templateToMJML(template);
|
|
314
|
+
} finally {
|
|
315
|
+
this.editor = false;
|
|
316
|
+
}
|
|
276
317
|
}
|
|
277
318
|
/**
|
|
278
319
|
* Convert template to MJML markup
|
|
@@ -379,7 +420,9 @@ var MJMLCompiler = class {
|
|
|
379
420
|
* renders at standard width, which the inspector explains).
|
|
380
421
|
*/
|
|
381
422
|
wrapperToMJML(wrapper) {
|
|
382
|
-
if (wrapper.hidden)
|
|
423
|
+
if (wrapper.hidden) {
|
|
424
|
+
return this.ee(`<mj-wrapper css-class="ee-ghost el-wrapper el-${wrapper.id}" padding="0"><mj-section padding="0"><mj-column>${this.ghostText([], "Container")}</mj-column></mj-section></mj-wrapper>`);
|
|
425
|
+
}
|
|
383
426
|
const attrs = [];
|
|
384
427
|
const cssClasses = ["el-wrapper", `el-${wrapper.id}`];
|
|
385
428
|
this.pushBackground(attrs, cssClasses, wrapper);
|
|
@@ -395,7 +438,13 @@ var MJMLCompiler = class {
|
|
|
395
438
|
if (wrapper.gap) attrs.push(`gap="${quoted(wrapper.gap)}"`);
|
|
396
439
|
if (wrapper.textAlign) attrs.push(`text-align="${quoted(wrapper.textAlign)}"`);
|
|
397
440
|
for (const c of (wrapper.cssClass ?? "").split(/\s+/)) if (c && !cssClasses.includes(quoted(c))) cssClasses.push(quoted(c));
|
|
398
|
-
|
|
441
|
+
let first = true;
|
|
442
|
+
const sections = wrapper.sections.map((section) => {
|
|
443
|
+
if (section.hidden) return this.sectionToMJML(section);
|
|
444
|
+
const out = this.sectionToMJML(section, first ? void 0 : wrapper.gap);
|
|
445
|
+
first = false;
|
|
446
|
+
return out;
|
|
447
|
+
}).filter(Boolean).join("\n");
|
|
399
448
|
return `
|
|
400
449
|
<mj-wrapper ${finishAttributes(attrs, cssClasses, wrapper.extraAttributes)}>
|
|
401
450
|
${sections}
|
|
@@ -420,12 +469,14 @@ var MJMLCompiler = class {
|
|
|
420
469
|
* Convert section to MJML
|
|
421
470
|
* Supports background images, full-width, and mj-group for non-stacking columns
|
|
422
471
|
*/
|
|
423
|
-
sectionToMJML(section) {
|
|
424
|
-
if (section.hidden)
|
|
472
|
+
sectionToMJML(section, gapAbove) {
|
|
473
|
+
if (section.hidden) {
|
|
474
|
+
return this.ee(`<mj-section css-class="ee-ghost el-section el-${section.id}" padding="0"><mj-column>${this.ghostText([], "Section")}</mj-column></mj-section>`);
|
|
475
|
+
}
|
|
425
476
|
if (section.bodyRaw) {
|
|
426
477
|
const blocks = section.columns.flatMap((c) => c.blocks).filter((b) => !b.hidden);
|
|
427
478
|
if (blocks.every((b) => b.type === "raw") && section.columns.every((c) => !c.subColumns?.length)) {
|
|
428
|
-
return blocks.map((b) => `<mj-raw>${b.html}</mj-raw>`).join("\n");
|
|
479
|
+
return blocks.map((b) => `<mj-raw>${rawMarkers(b.id, b.html, this.editor)}</mj-raw>`).join("\n");
|
|
429
480
|
}
|
|
430
481
|
}
|
|
431
482
|
const attrs = [];
|
|
@@ -434,8 +485,9 @@ var MJMLCompiler = class {
|
|
|
434
485
|
if (section.fullWidth) {
|
|
435
486
|
attrs.push('full-width="full-width"');
|
|
436
487
|
}
|
|
437
|
-
|
|
438
|
-
|
|
488
|
+
const sectionPadding = gapAbove ? paddingWithGap(section.padding, gapAbove) : section.padding;
|
|
489
|
+
if (sectionPadding) {
|
|
490
|
+
const padding = spacingToString(sectionPadding);
|
|
439
491
|
if (padding) attrs.push(`padding="${padding}"`);
|
|
440
492
|
}
|
|
441
493
|
const columns = section.columns.map((col) => this.columnToMJML(col)).join("\n");
|
|
@@ -485,9 +537,10 @@ var MJMLCompiler = class {
|
|
|
485
537
|
if (padding) attrs.push(`padding="${padding}"`);
|
|
486
538
|
}
|
|
487
539
|
const blocks = column.blocks.map((block) => this.blockToMJML(block)).join("\n");
|
|
540
|
+
const floor = column.blocks.length === 0 ? this.ee('<mj-spacer height="48px" css-class="ee-ghost ee-empty" />') : "";
|
|
488
541
|
return `
|
|
489
542
|
<mj-column ${finishAttributes(attrs, cssClasses, column.extraAttributes)}>
|
|
490
|
-
${blocks}
|
|
543
|
+
${blocks}${floor}
|
|
491
544
|
</mj-column>
|
|
492
545
|
`.trim();
|
|
493
546
|
}
|
|
@@ -511,7 +564,7 @@ var MJMLCompiler = class {
|
|
|
511
564
|
const hasPadding = sc.paddingTop || sc.paddingRight || sc.paddingBottom || sc.paddingLeft;
|
|
512
565
|
const padding = hasPadding ? `padding:${sc.paddingTop || "0"} ${sc.paddingRight || "0"} ${sc.paddingBottom || "0"} ${sc.paddingLeft || "0"};` : "padding:10px;";
|
|
513
566
|
const bg = sc.backgroundColor ? `background-color:${sc.backgroundColor};` : "";
|
|
514
|
-
return `<td class="ee-sub-col" width="${widths[i]}%" valign="${valign}" style="${padding}${bg}">${inner}</td>`;
|
|
567
|
+
return `<td class="ee-sub-col el-subcolumn el-${sc.id}" width="${widths[i]}%" valign="${valign}" style="${padding}${bg}">${inner}</td>`;
|
|
515
568
|
}).join("");
|
|
516
569
|
const colAttrs = [];
|
|
517
570
|
if (column.width) colAttrs.push(`width="${column.width}%"`);
|
|
@@ -535,7 +588,7 @@ var MJMLCompiler = class {
|
|
|
535
588
|
* Convert block to MJML based on type
|
|
536
589
|
*/
|
|
537
590
|
blockToMJML(block) {
|
|
538
|
-
if (block.hidden) return
|
|
591
|
+
if (block.hidden) return this.ee(this.ghostText([`el-${block.type}`, `el-${block.id}`], block.type.charAt(0).toUpperCase() + block.type.slice(1)));
|
|
539
592
|
switch (block.type) {
|
|
540
593
|
case "text":
|
|
541
594
|
return this.textBlockToMJML(block);
|
|
@@ -579,27 +632,12 @@ var MJMLCompiler = class {
|
|
|
579
632
|
*/
|
|
580
633
|
textBlockToMJML(block) {
|
|
581
634
|
const attrs = [];
|
|
582
|
-
const inlineStyles =
|
|
583
|
-
if (block.align) {
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
}
|
|
587
|
-
if (block.
|
|
588
|
-
attrs.push(`color="${block.color}"`);
|
|
589
|
-
inlineStyles.push(`color:${block.color}`);
|
|
590
|
-
}
|
|
591
|
-
if (block.fontSize) {
|
|
592
|
-
attrs.push(`font-size="${block.fontSize}"`);
|
|
593
|
-
inlineStyles.push(`font-size:${block.fontSize}`);
|
|
594
|
-
}
|
|
595
|
-
if (block.fontFamily) {
|
|
596
|
-
attrs.push(`font-family="${block.fontFamily}"`);
|
|
597
|
-
inlineStyles.push(`font-family:${block.fontFamily}`);
|
|
598
|
-
}
|
|
599
|
-
if (block.lineHeight) {
|
|
600
|
-
attrs.push(`line-height="${block.lineHeight}"`);
|
|
601
|
-
inlineStyles.push(`line-height:${block.lineHeight}`);
|
|
602
|
-
}
|
|
635
|
+
const inlineStyles = textInlineStyles(block);
|
|
636
|
+
if (block.align) attrs.push(`align="${block.align}"`);
|
|
637
|
+
if (block.color) attrs.push(`color="${block.color}"`);
|
|
638
|
+
if (block.fontSize) attrs.push(`font-size="${block.fontSize}"`);
|
|
639
|
+
if (block.fontFamily) attrs.push(`font-family="${block.fontFamily}"`);
|
|
640
|
+
if (block.lineHeight) attrs.push(`line-height="${block.lineHeight}"`);
|
|
603
641
|
if (block.padding) {
|
|
604
642
|
const padding = spacingToString(block.padding);
|
|
605
643
|
if (padding) attrs.push(`padding="${padding}"`);
|
|
@@ -677,7 +715,7 @@ var MJMLCompiler = class {
|
|
|
677
715
|
github: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>'
|
|
678
716
|
};
|
|
679
717
|
const svg = svgIcons[platform] || svgIcons.facebook;
|
|
680
|
-
return `data:image/svg+xml;base64,${
|
|
718
|
+
return `data:image/svg+xml;base64,${base64Utf8(svg)}`;
|
|
681
719
|
}
|
|
682
720
|
/**
|
|
683
721
|
* Convert social block to MJML using raw HTML table for reliable horizontal layout
|
|
@@ -785,7 +823,7 @@ ${items}
|
|
|
785
823
|
* Convert raw HTML block to MJML
|
|
786
824
|
*/
|
|
787
825
|
rawBlockToMJML(block) {
|
|
788
|
-
return `<mj-raw css-class="el-raw el-${block.id}">${block.html}</mj-raw>`;
|
|
826
|
+
return `<mj-raw css-class="el-raw el-${block.id}">${rawMarkers(block.id, block.html, this.editor)}</mj-raw>`;
|
|
789
827
|
}
|
|
790
828
|
/**
|
|
791
829
|
* Convert navbar block to MJML
|
|
@@ -913,6 +951,30 @@ ${dataRows}
|
|
|
913
951
|
`.trim();
|
|
914
952
|
}
|
|
915
953
|
};
|
|
954
|
+
|
|
955
|
+
// src/compiler/MJMLCompiler.ts
|
|
956
|
+
var MJMLCompiler = class extends MjmlBuilder {
|
|
957
|
+
/**
|
|
958
|
+
* Compile email template to MJML and HTML
|
|
959
|
+
*/
|
|
960
|
+
async compile(template, options = {}) {
|
|
961
|
+
try {
|
|
962
|
+
const mjml = this.toMJML(template, { editor: options.editor });
|
|
963
|
+
const result = await (0, import_mjml.default)(mjml, mjmlOptions(options));
|
|
964
|
+
return {
|
|
965
|
+
mjml,
|
|
966
|
+
html: result.html,
|
|
967
|
+
errors: result.errors.length > 0 ? result.errors.map((e) => e.formattedMessage) : void 0
|
|
968
|
+
};
|
|
969
|
+
} catch (error) {
|
|
970
|
+
return {
|
|
971
|
+
mjml: "",
|
|
972
|
+
html: "",
|
|
973
|
+
errors: [error instanceof Error ? error.message : "Unknown compilation error"]
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
};
|
|
916
978
|
function createMJMLCompiler() {
|
|
917
979
|
return new MJMLCompiler();
|
|
918
980
|
}
|
|
@@ -932,10 +994,10 @@ var MJMLExporter = class {
|
|
|
932
994
|
/**
|
|
933
995
|
* Export template to MJML + HTML
|
|
934
996
|
*/
|
|
935
|
-
export(template) {
|
|
997
|
+
async export(template) {
|
|
936
998
|
const mjml = this.exportMJML(template);
|
|
937
999
|
try {
|
|
938
|
-
const result = (0, import_mjml2.default)(mjml, {
|
|
1000
|
+
const result = await (0, import_mjml2.default)(mjml, {
|
|
939
1001
|
// Never resolve <mj-include>: see MJMLCompiler.compile.
|
|
940
1002
|
ignoreIncludes: true,
|
|
941
1003
|
validationLevel: this.options.validationLevel,
|
|
@@ -1666,6 +1728,12 @@ function decodeEditorData(value) {
|
|
|
1666
1728
|
return null;
|
|
1667
1729
|
}
|
|
1668
1730
|
}
|
|
1731
|
+
function subtractPx(a, b) {
|
|
1732
|
+
const px = (v) => v === "0" ? 0 : /^-?\d+(\.\d+)?px$/.test(v) ? Number.parseFloat(v) : NaN;
|
|
1733
|
+
const diff = px(a) - px(b);
|
|
1734
|
+
if (Number.isNaN(diff) || diff < 0) return void 0;
|
|
1735
|
+
return diff === 0 ? "0" : `${Number.isInteger(diff) ? diff : Number(diff.toFixed(2))}px`;
|
|
1736
|
+
}
|
|
1669
1737
|
function parsePadding(value) {
|
|
1670
1738
|
const parts = value.trim().split(/\s+/);
|
|
1671
1739
|
if (parts.length < 1 || parts.length > 4 || parts.some((p) => !/^-?[0-9.]+(px|%|em|rem)?$/.test(p))) return null;
|
|
@@ -1972,6 +2040,24 @@ var Importer = class {
|
|
|
1972
2040
|
if (typeof node.content === "string" && node.content.trim()) {
|
|
1973
2041
|
this.warn("stray_text", "info", path, node, "Text directly in mj-wrapper is ignored by MJML, and by the import.");
|
|
1974
2042
|
}
|
|
2043
|
+
if (wrapper.gap) {
|
|
2044
|
+
let first = true;
|
|
2045
|
+
for (const section of wrapper.sections) {
|
|
2046
|
+
if (section.bodyRaw) continue;
|
|
2047
|
+
if (first) {
|
|
2048
|
+
first = false;
|
|
2049
|
+
continue;
|
|
2050
|
+
}
|
|
2051
|
+
const top = section.padding?.top;
|
|
2052
|
+
if (!top) continue;
|
|
2053
|
+
const without = subtractPx(top, wrapper.gap);
|
|
2054
|
+
if (without === void 0) continue;
|
|
2055
|
+
const padding = { ...section.padding, top: without };
|
|
2056
|
+
const isDefault = padding.top === "20px" && (padding.right ?? "0") === "0" && padding.bottom === "20px" && (padding.left ?? "0") === "0";
|
|
2057
|
+
if (isDefault) delete section.padding;
|
|
2058
|
+
else section.padding = padding;
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
1975
2061
|
return wrapper;
|
|
1976
2062
|
}
|
|
1977
2063
|
section(node, path) {
|
|
@@ -2368,7 +2454,7 @@ var Importer = class {
|
|
|
2368
2454
|
return block;
|
|
2369
2455
|
}
|
|
2370
2456
|
// Fallbacks: compile the whole source once, with each fallback marked, and cut them out.
|
|
2371
|
-
resolveFallbacks(metadata) {
|
|
2457
|
+
async resolveFallbacks(metadata) {
|
|
2372
2458
|
if (this.fallbacks.length === 0) return;
|
|
2373
2459
|
const marker = (i, edge) => ({
|
|
2374
2460
|
tagName: "mj-raw",
|
|
@@ -2382,7 +2468,7 @@ var Importer = class {
|
|
|
2382
2468
|
}
|
|
2383
2469
|
let html = "";
|
|
2384
2470
|
try {
|
|
2385
|
-
html = (0, import_mjml3.default)(serialize(this.root), { ignoreIncludes: true, validationLevel: "skip", minify: false, keepComments: true }).html;
|
|
2471
|
+
html = (await (0, import_mjml3.default)(serialize(this.root), { ignoreIncludes: true, validationLevel: "skip", minify: false, keepComments: true })).html;
|
|
2386
2472
|
} catch {
|
|
2387
2473
|
html = "";
|
|
2388
2474
|
}
|
|
@@ -2446,7 +2532,7 @@ function supportingCss(fullHtml, fragments, breakpoint = "480px") {
|
|
|
2446
2532
|
}
|
|
2447
2533
|
return out.join("\n");
|
|
2448
2534
|
}
|
|
2449
|
-
function importMjml(source) {
|
|
2535
|
+
async function importMjml(source) {
|
|
2450
2536
|
if (typeof source !== "string") throw new MjmlImportError("not_mjml", "The MJML must be a string.");
|
|
2451
2537
|
scanMjml(source, ENDING_TAGS);
|
|
2452
2538
|
const root = parseMjml(source);
|
|
@@ -2462,7 +2548,7 @@ function importMjml(source) {
|
|
|
2462
2548
|
}
|
|
2463
2549
|
const metadata = importer.head(head, body);
|
|
2464
2550
|
const sections = importer.body(body);
|
|
2465
|
-
importer.resolveFallbacks(metadata);
|
|
2551
|
+
await importer.resolveFallbacks(metadata);
|
|
2466
2552
|
importer.summarize();
|
|
2467
2553
|
const document = { version: CURRENT_TEMPLATE_VERSION, metadata, sections };
|
|
2468
2554
|
try {
|
|
@@ -2477,16 +2563,23 @@ function importMjml(source) {
|
|
|
2477
2563
|
0 && (module.exports = {
|
|
2478
2564
|
DEFAULT_MJML_ATTRIBUTES,
|
|
2479
2565
|
EDITOR_DATA_ATTRIBUTE,
|
|
2566
|
+
EDITOR_MARKER,
|
|
2480
2567
|
MAX_MJML_BYTES,
|
|
2481
2568
|
MAX_MJML_DEPTH,
|
|
2482
2569
|
MAX_MJML_ELEMENTS,
|
|
2483
2570
|
MJMLCompiler,
|
|
2484
2571
|
MJMLExporter,
|
|
2572
|
+
MjmlBuilder,
|
|
2485
2573
|
MjmlImportError,
|
|
2574
|
+
addPx,
|
|
2575
|
+
base64Utf8,
|
|
2486
2576
|
createMJMLCompiler,
|
|
2487
2577
|
createMJMLExporter,
|
|
2488
2578
|
encodeEditorData,
|
|
2489
2579
|
exportTemplate,
|
|
2490
2580
|
importMjml,
|
|
2491
|
-
isMjmlImportError
|
|
2581
|
+
isMjmlImportError,
|
|
2582
|
+
mjmlOptions,
|
|
2583
|
+
paddingWithGap,
|
|
2584
|
+
stripEditorMarkup
|
|
2492
2585
|
});
|