@maizzle/framework 5.0.0-beta.24 → 5.0.0-beta.26

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maizzle/framework",
3
- "version": "5.0.0-beta.24",
3
+ "version": "5.0.0-beta.26",
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",
@@ -56,10 +56,10 @@
56
56
  "color-shorthand-hex-to-six-digit": "^5.0.16",
57
57
  "defu": "^6.1.4",
58
58
  "email-comb": "^7.0.21",
59
- "express": "^4.19.2",
59
+ "express": "^4.21.0",
60
60
  "fast-glob": "^3.3.2",
61
61
  "gray-matter": "^4.0.3",
62
- "html-crush": "^6.0.18",
62
+ "html-crush": "^6.0.19",
63
63
  "is-url-superb": "^6.1.0",
64
64
  "istextorbinary": "^9.5.0",
65
65
  "juice": "^11.0.0",
@@ -67,7 +67,7 @@
67
67
  "morphdom": "^2.7.4",
68
68
  "ora": "^8.1.0",
69
69
  "pathe": "^1.1.2",
70
- "postcss": "^8.4.39",
70
+ "postcss": "^8.4.47",
71
71
  "postcss-custom-properties": "^14.0.1",
72
72
  "postcss-import": "^16.1.0",
73
73
  "postcss-safe-parser": "^7.0.0",
@@ -81,7 +81,7 @@
81
81
  "posthtml-fetch": "^4.0.0",
82
82
  "posthtml-markdownit": "^3.1.0",
83
83
  "posthtml-mso": "^3.1.0",
84
- "posthtml-parser": "^0.12.0",
84
+ "posthtml-parser": "^0.12.1",
85
85
  "posthtml-postcss": "^1.0.2",
86
86
  "posthtml-postcss-merge-longhand": "^3.1.2",
87
87
  "posthtml-render": "^3.0.0",
@@ -90,16 +90,16 @@
90
90
  "pretty": "^2.0.0",
91
91
  "string-remove-widows": "^4.0.22",
92
92
  "string-strip-html": "^13.4.8",
93
- "tailwindcss": "^3.4.10",
94
- "ws": "^8.17.0"
93
+ "tailwindcss": "^3.4.13",
94
+ "ws": "^8.18.0"
95
95
  },
96
96
  "devDependencies": {
97
- "@biomejs/biome": "1.8.3",
97
+ "@biomejs/biome": "1.9.2",
98
98
  "@types/js-beautify": "^1.14.3",
99
- "@types/markdown-it": "^14.1.1",
100
- "@vitest/coverage-v8": "^2.0.1",
99
+ "@types/markdown-it": "^14.1.2",
100
+ "@vitest/coverage-v8": "^2.1.1",
101
101
  "supertest": "^7.0.0",
102
- "vitest": "^2.0.1"
102
+ "vitest": "^2.1.1"
103
103
  },
104
104
  "engines": {
105
105
  "node": ">=18.20"
@@ -63,7 +63,7 @@ async function renderUpdatedFile(file, config) {
63
63
 
64
64
  // beforeCreate event
65
65
  if (typeof config.beforeCreate === 'function') {
66
- await config.beforeCreate(config)
66
+ await config.beforeCreate({ config })
67
67
  }
68
68
 
69
69
  // Read the file
@@ -164,11 +164,11 @@ export default async (config = {}) => {
164
164
  app.get(routePattern, async (req, res, next) => {
165
165
  // Run beforeCreate event
166
166
  if (typeof config.beforeCreate === 'function') {
167
- config.beforeCreate(config)
167
+ await config.beforeCreate({ config })
168
168
  }
169
169
 
170
170
  try {
171
- const filePath = templatePaths.find(t => t.endsWith(req.url.slice(1)))
171
+ const filePath = templatePaths.find(t => t.endsWith(decodeURI(req.url.slice(1))))
172
172
 
173
173
  // Set the file being viewed
174
174
  viewing = filePath
@@ -288,7 +288,7 @@ export default async (config = {}) => {
288
288
 
289
289
  // Run beforeCreate event
290
290
  if (typeof config.beforeCreate === 'function') {
291
- await config.beforeCreate(config)
291
+ await config.beforeCreate({ config })
292
292
  }
293
293
 
294
294
  // Read the file
@@ -27,7 +27,7 @@ function groupFilesByDirectories(globs, files) {
27
27
  const fileName = parts[parts.length - 1]
28
28
  current[fileName] = {
29
29
  name: fileName,
30
- href: file,
30
+ href: encodeURI(file),
31
31
  }
32
32
  }
33
33
  })
@@ -79,6 +79,11 @@
79
79
  border-left: 1px solid #64748B;
80
80
  }
81
81
 
82
+ li.folder.nested > strong {
83
+ cursor: pointer;
84
+ display: block;
85
+ }
86
+
82
87
  li.folder.root {
83
88
  padding-top: 3rem;
84
89
  border: none;
@@ -92,6 +97,15 @@
92
97
  line-height: 2.25rem;
93
98
  }
94
99
 
100
+ li.file.nested.collapsed {
101
+ display: none;
102
+ }
103
+
104
+ li.file.nested.collapsed + .folder.nested {
105
+ padding-top: 0;
106
+ }
107
+
108
+
95
109
  li.file.nested a {
96
110
  padding-bottom: 0.75rem;
97
111
  margin-left: -1.5rem;
@@ -143,5 +157,16 @@
143
157
  </ul>
144
158
 
145
159
  <svg fill="none" stroke="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 993 483" class="insignia"><mask id="a" style="mask-type:alpha;" maskUnits="userSpaceOnUse" x="0" y="0" width="993" height="483"><path fill="#D9D9D9" d="M0 0h993v483H0z"></path></mask><g mask="url(#a)" stroke="#e2e8f0" stroke-miterlimit="10"><path d="M954.124 81.816c0-45.163-39.678-81.774-88.624-81.774-48.945 0-88.624 36.611-88.624 81.774v436.13c0 45.163 39.679 81.775 88.624 81.775 48.946 0 88.624-36.612 88.624-81.775V81.816ZM583.379 81.816c0-45.163-39.678-81.774-88.624-81.774-48.945 0-88.624 36.611-88.624 81.774v436.13c0 45.163 39.679 81.775 88.624 81.775 48.946 0 88.624-36.612 88.624-81.775V81.816Z"></path><path d="M935.39 132.185c30.161-35.57 23.362-86.965-15.187-114.795S825.954-4.165 795.794 31.404L425.713 467.848c-30.161 35.57-23.361 86.965 15.187 114.795 38.549 27.829 94.249 21.555 124.41-14.014l370.08-436.444ZM564.288 132.196c30.161-35.569 23.362-86.964-15.187-114.794S454.852-4.154 424.692 31.416L54.611 467.86c-30.16 35.569-23.361 86.965 15.187 114.794 38.549 27.83 94.249 21.556 124.41-14.013l370.08-436.445Z"></path></g></svg>
160
+ <script>
161
+ document.querySelectorAll('.folder.nested').forEach(folder => {
162
+ folder.addEventListener('click', function() {
163
+ let nextElement = this.nextElementSibling;
164
+ while (nextElement && nextElement.classList.contains('file') && nextElement.classList.contains('nested')) {
165
+ nextElement.classList.toggle('collapsed');
166
+ nextElement = nextElement.nextElementSibling;
167
+ }
168
+ });
169
+ });
170
+ </script>
146
171
  </body>
147
172
  </html>
@@ -124,16 +124,7 @@ export async function run(html = '', config = {}) {
124
124
  }
125
125
 
126
126
  /**
127
- * 8. Purge CSS
128
- *
129
- * Remove unused CSS, uglify classes etc.
130
- */
131
- if (get(config, 'css.purge')) {
132
- posthtmlPlugins.push(comb(config.css.purge))
133
- }
134
-
135
- /**
136
- * 9. Remove attributes
127
+ * 8. Remove attributes
137
128
  *
138
129
  * Remove attributes from HTML tags
139
130
  * If `undefined`, removes empty `style` and `class` attributes
@@ -148,7 +139,7 @@ export async function run(html = '', config = {}) {
148
139
  }
149
140
 
150
141
  /**
151
- * 10. Shorthand CSS
142
+ * 9. Shorthand CSS
152
143
  *
153
144
  * Convert longhand CSS properties to shorthand in `style` attributes.
154
145
  */
@@ -159,7 +150,7 @@ export async function run(html = '', config = {}) {
159
150
  }
160
151
 
161
152
  /**
162
- * 11. Add attributes
153
+ * 10. Add attributes
163
154
  *
164
155
  * Add attributes to HTML tags.
165
156
  */
@@ -170,7 +161,7 @@ export async function run(html = '', config = {}) {
170
161
  }
171
162
 
172
163
  /**
173
- * 12. Base URL
164
+ * 11. Base URL
174
165
  *
175
166
  * Add a base URL to relative paths.
176
167
  */
@@ -181,7 +172,7 @@ export async function run(html = '', config = {}) {
181
172
  }
182
173
 
183
174
  /**
184
- * 13. URL parameters
175
+ * 12. URL parameters
185
176
  *
186
177
  * Add parameters to URLs.
187
178
  */
@@ -192,7 +183,7 @@ export async function run(html = '', config = {}) {
192
183
  }
193
184
 
194
185
  /**
195
- * 14. Six-digit HEX
186
+ * 13. Six-digit HEX
196
187
  *
197
188
  * Enabled by default, converts three-digit HEX colors to six-digit.
198
189
  */
@@ -203,7 +194,7 @@ export async function run(html = '', config = {}) {
203
194
  }
204
195
 
205
196
  /**
206
- * 15. PostHTML MSO
197
+ * 14. PostHTML MSO
207
198
  *
208
199
  * Enabled by default, simplifies writing MSO conditionals for Outlook.
209
200
  */
@@ -214,7 +205,23 @@ export async function run(html = '', config = {}) {
214
205
  }
215
206
 
216
207
  /**
217
- * 16. Prettify
208
+ * 15. Purge CSS
209
+ *
210
+ * Remove unused CSS, uglify classes etc.
211
+ */
212
+ if (get(config, 'css.purge')) {
213
+ posthtmlPlugins.push(comb(config.css.purge))
214
+ }
215
+
216
+ /**
217
+ * 16. <template> tags
218
+ *
219
+ * Replace <template> tags with their content.
220
+ */
221
+ posthtmlPlugins.push(templateTag())
222
+
223
+ /**
224
+ * 17. Prettify
218
225
  *
219
226
  * Pretty-print HTML using js-beautify.
220
227
  */
@@ -225,7 +232,7 @@ export async function run(html = '', config = {}) {
225
232
  }
226
233
 
227
234
  /**
228
- * 17. Minify
235
+ * 18. Minify
229
236
  *
230
237
  * Minify HTML using html-crush.
231
238
  */
@@ -235,13 +242,6 @@ export async function run(html = '', config = {}) {
235
242
  )
236
243
  }
237
244
 
238
- /**
239
- * 18. <template> tags
240
- *
241
- * Replace <template> tags with their content.
242
- */
243
- posthtmlPlugins.push(templateTag())
244
-
245
245
  /**
246
246
  * 19. Replace strings
247
247
  *
@@ -55,9 +55,12 @@ export async function inline(html = '', options = {}) {
55
55
  })
56
56
 
57
57
  // Add a `data-embed` attribute to style tags that have the embed attribute
58
- $('style[embed]').each((i, el) => {
58
+ $('style[embed]:not([data-embed])').each((i, el) => {
59
59
  $(el).attr('data-embed', '')
60
60
  })
61
+ $('style[data-embed]:not([embed])').each((i, el) => {
62
+ $(el).attr('embed', '')
63
+ })
61
64
 
62
65
  /**
63
66
  * Inline the CSS
@@ -103,7 +106,31 @@ export async function inline(html = '', options = {}) {
103
106
  }
104
107
  )
105
108
 
106
- const preservedClasses = new Set()
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
+ // Precompile a single regex to match any substring from the preservedClasses set
128
+ const combinedPattern = Array.from(preservedClasses)
129
+ .map(pattern => pattern.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')) // Escape special regex chars
130
+ .join('|') // Combine all patterns into a single regex pattern with 'OR' (|)
131
+
132
+ const combinedRegex = new RegExp(combinedPattern)
133
+
107
134
  const selectors = new Set()
108
135
 
109
136
  // Preserve selectors in at rules
@@ -163,7 +190,7 @@ export async function inline(html = '', options = {}) {
163
190
 
164
191
  if (options.removeInlinedSelectors) {
165
192
  // Remove the rule in the <style> tag as long as it's not a preserved class
166
- if (!preservedClasses.has(selector)) {
193
+ if (!preservedClasses.has(selector) && !combinedRegex.test(selector)) {
167
194
  rule.remove()
168
195
  }
169
196
 
@@ -3,17 +3,26 @@ import { defu as merge } from 'defu'
3
3
  import md from 'posthtml-markdownit'
4
4
  import posthtmlConfig from '../posthtml/defaultConfig.js'
5
5
 
6
- export async function markdown(html = '', options = {}, posthtmlOptions = {}) {
6
+ export async function markdown(input = '', options = {}, posthtmlOptions = {}) {
7
7
  /**
8
- * Automatically wrap in <md> tag, unless manual mode is enabled
9
- * With manual mode, user must wrap the markdown content in a <md> tag
8
+ * If no input is provided, return an empty string.
9
+ */
10
+ if (!input) {
11
+ return ''
12
+ }
13
+
14
+ /**
15
+ * Automatically wrap in <md> tag, unless manual mode is enabled.
16
+ *
17
+ * With manual mode, user must wrap the input in a <md> tag.
18
+ *
10
19
  * https://github.com/posthtml/posthtml-markdownit#usage
11
20
  */
12
- html = options.manual ? html : `<md>${html}</md>`
21
+ input = options.manual ? input : `<md>${input}</md>`
13
22
 
14
23
  return posthtml([
15
24
  md(options)
16
25
  ])
17
- .process(html, merge(posthtmlOptions, posthtmlConfig))
26
+ .process(input, merge(posthtmlOptions, posthtmlConfig))
18
27
  .then(result => result.html)
19
28
  }
@@ -194,3 +194,13 @@ export function parseCSSRule(rule) {
194
194
 
195
195
  return { property, value }
196
196
  }
197
+
198
+ /**
199
+ * Normalize a string by removing extra whitespace.
200
+ *
201
+ * @param {String} str The string to clean
202
+ * @returns {String} The cleaned string
203
+ */
204
+ export function cleanString(str) {
205
+ return str.replace(/\s+/g, ' ').trim()
206
+ }
package/types/index.d.ts CHANGED
@@ -37,10 +37,11 @@ declare namespace MaizzleFramework {
37
37
  *
38
38
  * @param {string} input The Markdown string to compile.
39
39
  * @param {MarkdownConfig} [options] A configuration object for the Markdown compiler.
40
- * @returns {string} The compiled HTML string.
41
- * @see https://maizzle.com/docs/transformers/markdown
40
+ * @param {PostHTMLConfig} [posthtmlOptions] A configuration object for PostHTML.
41
+ * @returns {Promise<string>} The compiled HTML string.
42
+ * @see https://maizzle.com/docs/markdown#api
42
43
  */
43
- function markdown(input: string, options?: MarkdownConfig): string;
44
+ function markdown(input: string, options?: MarkdownConfig, posthtmlOptions?: PostHTMLConfig): Promise<string>;
44
45
 
45
46
  /**
46
47
  * Prevent widow words inside a tag by adding a `&nbsp;` between its last two words.