@maizzle/framework 5.0.0-beta.29 → 5.0.0-beta.30
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/package.json +3 -3
- package/src/transformers/comb.js +1 -1
- package/src/transformers/inline.js +26 -23
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maizzle/framework",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.30",
|
|
4
4
|
"description": "Maizzle is a framework that helps you quickly build HTML emails with Tailwind CSS.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -97,9 +97,9 @@
|
|
|
97
97
|
"@biomejs/biome": "1.9.2",
|
|
98
98
|
"@types/js-beautify": "^1.14.3",
|
|
99
99
|
"@types/markdown-it": "^14.1.2",
|
|
100
|
-
"@vitest/coverage-v8": "^2.1.
|
|
100
|
+
"@vitest/coverage-v8": "^2.1.5",
|
|
101
101
|
"supertest": "^7.0.0",
|
|
102
|
-
"vitest": "^2.1.
|
|
102
|
+
"vitest": "^2.1.5"
|
|
103
103
|
},
|
|
104
104
|
"engines": {
|
|
105
105
|
"node": ">=18.20"
|
package/src/transformers/comb.js
CHANGED
|
@@ -30,7 +30,7 @@ const posthtmlPlugin = options => tree => {
|
|
|
30
30
|
{ heads: '{{', tails: '}}' },
|
|
31
31
|
{ heads: '{%', tails: '%}' },
|
|
32
32
|
],
|
|
33
|
-
whitelist: [...defaultSafelist, ...get(options, '
|
|
33
|
+
whitelist: [...defaultSafelist, ...get(options, 'safelist', [])]
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
options = merge(options, defaultOptions)
|
|
@@ -2,6 +2,7 @@ import juice from 'juice'
|
|
|
2
2
|
import postcss from 'postcss'
|
|
3
3
|
import get from 'lodash-es/get.js'
|
|
4
4
|
import has from 'lodash-es/has.js'
|
|
5
|
+
import { defu as merge } from 'defu'
|
|
5
6
|
import * as cheerio from 'cheerio/slim'
|
|
6
7
|
import remove from 'lodash-es/remove.js'
|
|
7
8
|
import { render } from 'posthtml-render'
|
|
@@ -33,6 +34,26 @@ export async function inline(html = '', options = {}) {
|
|
|
33
34
|
options.removeInlinedSelectors = get(options, 'removeInlinedSelectors', true)
|
|
34
35
|
options.resolveCalc = get(options, 'resolveCalc', true)
|
|
35
36
|
options.preferUnitlessValues = get(options, 'preferUnitlessValues', true)
|
|
37
|
+
options.safelist = new Set([
|
|
38
|
+
...get(options, 'safelist', []),
|
|
39
|
+
...[
|
|
40
|
+
'.body', // Gmail
|
|
41
|
+
'.gmail', // Gmail
|
|
42
|
+
'.apple', // Apple Mail
|
|
43
|
+
'.ios', // Mail on iOS
|
|
44
|
+
'.ox-', // Open-Xchange
|
|
45
|
+
'.outlook', // Outlook.com
|
|
46
|
+
'[data-ogs', // Outlook.com
|
|
47
|
+
'.bloop_container', // Airmail
|
|
48
|
+
'.Singleton', // Apple Mail 10
|
|
49
|
+
'.unused', // Notes 8
|
|
50
|
+
'.moz-text-html', // Thunderbird
|
|
51
|
+
'.mail-detail-content', // Comcast, Libero webmail
|
|
52
|
+
'edo', // Edison (all)
|
|
53
|
+
'#msgBody', // Freenet uses #msgBody
|
|
54
|
+
'.lang' // Fenced code blocks
|
|
55
|
+
],
|
|
56
|
+
])
|
|
36
57
|
|
|
37
58
|
juice.styleToAttribute = get(options, 'styleToAttribute', {})
|
|
38
59
|
juice.applyWidthAttributes = get(options, 'applyWidthAttributes', true)
|
|
@@ -106,26 +127,8 @@ export async function inline(html = '', options = {}) {
|
|
|
106
127
|
}
|
|
107
128
|
)
|
|
108
129
|
|
|
109
|
-
const preservedClasses = new Set([
|
|
110
|
-
'.body', // Gmail
|
|
111
|
-
'.gmail', // Gmail
|
|
112
|
-
'.apple', // Apple Mail
|
|
113
|
-
'.ios', // Mail on iOS
|
|
114
|
-
'.ox-', // Open-Xchange
|
|
115
|
-
'.outlook', // Outlook.com
|
|
116
|
-
'[data-ogs', // Outlook.com
|
|
117
|
-
'.bloop_container', // Airmail
|
|
118
|
-
'.Singleton', // Apple Mail 10
|
|
119
|
-
'.unused', // Notes 8
|
|
120
|
-
'.moz-text-html', // Thunderbird
|
|
121
|
-
'.mail-detail-content', // Comcast, Libero webmail
|
|
122
|
-
'edo', // Edison (all)
|
|
123
|
-
'#msgBody', // Freenet uses #msgBody
|
|
124
|
-
'.lang' // Fenced code blocks
|
|
125
|
-
])
|
|
126
|
-
|
|
127
130
|
// Precompile a single regex to match any substring from the preservedClasses set
|
|
128
|
-
const combinedPattern = Array.from(
|
|
131
|
+
const combinedPattern = Array.from(options.safelist)
|
|
129
132
|
.map(pattern => pattern.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')) // Escape special regex chars
|
|
130
133
|
.join('|') // Combine all patterns into a single regex pattern with 'OR' (|)
|
|
131
134
|
|
|
@@ -137,7 +140,7 @@ export async function inline(html = '', options = {}) {
|
|
|
137
140
|
root.walkAtRules(rule => {
|
|
138
141
|
if (['media', 'supports'].includes(rule.name)) {
|
|
139
142
|
rule.walkRules(rule => {
|
|
140
|
-
|
|
143
|
+
options.safelist.add(rule.selector)
|
|
141
144
|
})
|
|
142
145
|
}
|
|
143
146
|
})
|
|
@@ -185,12 +188,12 @@ export async function inline(html = '', options = {}) {
|
|
|
185
188
|
// Preserve pseudo selectors
|
|
186
189
|
// TODO: revisit pseudos list
|
|
187
190
|
if ([':hover', ':active', ':focus', ':visited', ':link', ':before', ':after'].some(i => selector.includes(i))) {
|
|
188
|
-
|
|
191
|
+
options.safelist.add(selector)
|
|
189
192
|
}
|
|
190
193
|
|
|
191
194
|
if (options.removeInlinedSelectors) {
|
|
192
195
|
// Remove the rule in the <style> tag as long as it's not a preserved class
|
|
193
|
-
if (!
|
|
196
|
+
if (!options.safelist.has(selector) && !combinedRegex.test(selector)) {
|
|
194
197
|
rule.remove()
|
|
195
198
|
}
|
|
196
199
|
|
|
@@ -249,7 +252,7 @@ export async function inline(html = '', options = {}) {
|
|
|
249
252
|
// If the class has been inlined in the style attribute...
|
|
250
253
|
if (has(inlineStyles, prop)) {
|
|
251
254
|
// Try to remove the classes that have been inlined
|
|
252
|
-
if (![...
|
|
255
|
+
if (![...options.safelist].some(item => item.includes(name))) {
|
|
253
256
|
remove(classList, classToRemove => name.includes(classToRemove))
|
|
254
257
|
}
|
|
255
258
|
|