@maizzle/framework 5.0.1 → 5.0.3

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.1",
3
+ "version": "5.0.3",
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",
@@ -107,7 +107,7 @@ export default async (config = {}) => {
107
107
  /**
108
108
  * Check that templates to be built, actually exist
109
109
  */
110
- const contentPaths = get(config, 'build.content', ['src/templates/**/*.html'])
110
+ const contentPaths = get(config, 'build.content', ['emails/**/*.html'])
111
111
 
112
112
  const templateFolders = Array.isArray(contentPaths) ? contentPaths : [contentPaths]
113
113
  const templatePaths = await fg.glob([...new Set(templateFolders)])
@@ -122,7 +122,7 @@ export default async (config = {}) => {
122
122
  *
123
123
  * Copies each `build.content` path to the `build.output.path` directory.
124
124
  */
125
- let from = get(config, 'build.output.from', ['src/templates', 'src'])
125
+ let from = get(config, 'build.output.from', ['emails'])
126
126
 
127
127
  const globPathsToCopy = contentPaths.map(glob => {
128
128
  // Keep negated paths as they are
@@ -150,7 +150,7 @@ export async function writePlaintextFile(plaintext = '', config = {}) {
150
150
  * `config.build.output.path`
151
151
  */
152
152
  const plaintextConfig = get(config, 'plaintext')
153
- let plaintextOutputPath = get(plaintextConfig, 'output.path', get(config, 'build.output.path'))
153
+ let plaintextOutputPath = get(plaintextConfig, 'output.path', '')
154
154
  const plaintextExtension = get(plaintextConfig, 'output.extension', 'txt')
155
155
 
156
156
  /**
@@ -158,7 +158,7 @@ export async function writePlaintextFile(plaintext = '', config = {}) {
158
158
  * output plaintext file in the same location as the HTML file.
159
159
  */
160
160
  if (plaintextConfig === true) {
161
- plaintextOutputPath = get(config, 'build.output.path')
161
+ plaintextOutputPath = ''
162
162
  }
163
163
 
164
164
  /**
@@ -5,7 +5,7 @@ const plugin = (env => tree => {
5
5
  return node
6
6
  }
7
7
 
8
- if (node.attrs) {
8
+ if (node?.attrs) {
9
9
  for (const attr in node.attrs) {
10
10
  const suffix = `-${env}`
11
11
 
@@ -3,7 +3,7 @@ const plugin = (env => tree => {
3
3
  env = env || 'local'
4
4
 
5
5
  // Return the original node if it doesn't have a tag
6
- if (!node.tag) {
6
+ if (!node?.tag) {
7
7
  return node
8
8
  }
9
9
 
@@ -8,7 +8,7 @@ const plugin = (() => tree => {
8
8
  /**
9
9
  * Don't expand link tags that are not explicitly marked as such
10
10
  */
11
- if (node.attrs && ![...targets].some(attr => attr in node.attrs)) {
11
+ if (node?.attrs && ![...targets].some(attr => attr in node.attrs)) {
12
12
  for (const attr of targets) {
13
13
  node.attrs[attr] = false
14
14
  }
@@ -17,7 +17,8 @@ const plugin = (() => tree => {
17
17
  }
18
18
 
19
19
  if (
20
- node.tag === 'link'
20
+ node
21
+ && node.tag === 'link'
21
22
  && node.attrs
22
23
  && node.attrs.href
23
24
  && node.attrs.rel === 'stylesheet'
@@ -1,12 +1,12 @@
1
1
  import posthtml from 'posthtml'
2
2
  import isUrl from 'is-url-superb'
3
3
  import get from 'lodash-es/get.js'
4
- import baseUrl from 'posthtml-base-url'
5
4
  import { render } from 'posthtml-render'
6
5
  import isEmpty from 'lodash-es/isEmpty.js'
7
6
  import isObject from 'lodash-es/isObject.js'
8
7
  import { parser as parse } from 'posthtml-parser'
9
8
  import { getPosthtmlOptions } from '../posthtml/defaultConfig.js'
9
+ import baseUrl, { parseSrcset, stringifySrcset, defaultTags } from 'posthtml-base-url'
10
10
 
11
11
  const posthtmlOptions = getPosthtmlOptions()
12
12
 
@@ -48,11 +48,11 @@ const posthtmlPlugin = url => tree => {
48
48
 
49
49
  export default posthtmlPlugin
50
50
 
51
- export async function addBaseUrl(html = '', options = {}, posthtmlOptions = {}) {
51
+ export async function addBaseUrl(html = '', options = {}, posthtmlOpts = {}) {
52
52
  return posthtml([
53
53
  posthtmlPlugin(options)
54
54
  ])
55
- .process(html, getPosthtmlOptions())
55
+ .process(html, getPosthtmlOptions(posthtmlOpts))
56
56
  .then(result => result.html)
57
57
  }
58
58
 
@@ -99,5 +99,52 @@ const rewriteVMLs = (html, url) => {
99
99
  })
100
100
  }
101
101
 
102
+ /**
103
+ * Handle other sources inside MSO comments
104
+ */
105
+
106
+ // Make a | pipe-separated list of all the default tags and use it to create a regex
107
+ const uniqueSourceAttributes = [
108
+ ...new Set(Object.values(defaultTags).flatMap(Object.keys))
109
+ ].join('|')
110
+
111
+ const sourceAttrRegex = new RegExp(`\\b(${uniqueSourceAttributes})="([^"]+)"`, 'g')
112
+
113
+ // Replace all the source attributes inside MSO comments
114
+ html = html.replace(/<!--\[if [^\]]+\]>[\s\S]*?<!\[endif\]-->/g, (msoBlock) => {
115
+ return msoBlock.replace(sourceAttrRegex, (match, attr, value) => {
116
+ if (isUrl(value)) {
117
+ return match
118
+ }
119
+
120
+ const updatedValue = attr === 'srcset'
121
+ ? processSrcset(value, url)
122
+ : url + value
123
+
124
+ return `${attr}="${updatedValue}"`
125
+ })
126
+ })
127
+
102
128
  return html
103
129
  }
130
+
131
+ /**
132
+ * Add the base URL to the srcset URLs
133
+ *
134
+ * @param {*} srcsetValue The value of the srcset attribute
135
+ * @param {*} url The base URL
136
+ * @returns {string} The updated srcset attribute value
137
+ */
138
+ function processSrcset(srcsetValue, url) {
139
+ const parsed = parseSrcset(srcsetValue)
140
+
141
+ parsed.map(p => {
142
+ if (!isUrl(p.url)) {
143
+ p.url = url + p.url
144
+ }
145
+
146
+ return p
147
+ })
148
+
149
+ return stringifySrcset(parsed)
150
+ }
@@ -9,7 +9,6 @@ import isEmpty from 'lodash-es/isEmpty.js'
9
9
  import safeParser from 'postcss-safe-parser'
10
10
  import isObject from 'lodash-es/isObject.js'
11
11
  import { parser as parse } from 'posthtml-parser'
12
- import { parseCSSRule } from '../utils/string.js'
13
12
  import { useAttributeSizes } from './useAttributeSizes.js'
14
13
  import { getPosthtmlOptions } from '../posthtml/defaultConfig.js'
15
14
 
@@ -166,6 +165,13 @@ export async function inline(html = '', options = {}) {
166
165
  }
167
166
  })
168
167
 
168
+ /**
169
+ * CSS optimizations
170
+ *
171
+ * 1. `preferUnitlessValues` - Replace unit values with `0` where possible
172
+ * 2. `removeInlinedSelectors` - Remove inlined selectors from the HTML
173
+ */
174
+
169
175
  // Loop over selectors that we found in the <style> tags
170
176
  selectors.forEach(({ name, prop }) => {
171
177
  const elements = $(name).get()
@@ -176,11 +182,16 @@ export async function inline(html = '', options = {}) {
176
182
  elements.forEach((el) => {
177
183
  // Get a `property|value` list from the inline style attribute
178
184
  const styleAttr = $(el).attr('style')
179
- let inlineStyles = {}
185
+ const inlineStyles = {}
180
186
 
187
+ // 1. `preferUnitlessValues`
181
188
  if (styleAttr) {
182
- inlineStyles = styleAttr.split(';').reduce((acc, i) => {
183
- let { property, value } = parseCSSRule(i)
189
+ // Parse the inline styles using postcss
190
+ const root = postcss.parse(`* { ${styleAttr} }`)
191
+
192
+ root.first.each((decl) => {
193
+ const property = decl.prop
194
+ let value = decl.value
184
195
 
185
196
  if (value && options.preferUnitlessValues) {
186
197
  value = value.replace(
@@ -190,11 +201,9 @@ export async function inline(html = '', options = {}) {
190
201
  }
191
202
 
192
203
  if (property) {
193
- acc[property] = value
204
+ inlineStyles[property] = value
194
205
  }
195
-
196
- return acc
197
- }, {})
206
+ })
198
207
 
199
208
  // Update the element's style attribute with the new value
200
209
  $(el).attr(
@@ -206,6 +215,7 @@ export async function inline(html = '', options = {}) {
206
215
  // Get the classes from the element's class attribute
207
216
  const classes = $(el).attr('class')
208
217
 
218
+ // 2. `removeInlinedSelectors`
209
219
  if (options.removeInlinedSelectors && classes) {
210
220
  const classList = classes.split(' ')
211
221
 
@@ -174,27 +174,6 @@ export function getFileExtensionsFromPattern(pattern) {
174
174
  return ['html'] // No recognizable extension pattern, default to 'html'
175
175
  }
176
176
 
177
- export function parseCSSRule(rule) {
178
- // Step 1: Trim the input string
179
- rule = rule.trim()
180
-
181
- // Step 2: Find the index of the first colon
182
- const colonIndex = rule.indexOf(':')
183
-
184
- // Step 3: Extract property and value parts
185
- if (colonIndex === -1) {
186
- return {
187
- property: '',
188
- value: ''
189
- }
190
- }
191
-
192
- const property = rule.slice(0, colonIndex).trim()
193
- const value = rule.slice(colonIndex + 1).trim()
194
-
195
- return { property, value }
196
- }
197
-
198
177
  /**
199
178
  * Normalize a string by removing extra whitespace.
200
179
  *
package/types/index.d.ts CHANGED
@@ -9,6 +9,7 @@ import type CSSInlineConfig from './css/inline';
9
9
  import type WidowWordsConfig from './widowWords';
10
10
  import type { BaseURLConfig } from 'posthtml-base-url';
11
11
  import type { HTMLBeautifyOptions } from 'js-beautify'
12
+ import type { Opts as PlaintextOptions } from 'string-strip-html';
12
13
  import type { URLParametersConfig } from 'posthtml-url-parameters';
13
14
  import type { AttributeToStyleSupportedAttributes } from './css/inline';
14
15
 
@@ -188,11 +189,22 @@ declare namespace MaizzleFramework {
188
189
  /**
189
190
  * Generate a plaintext version of an HTML string.
190
191
  * @param {string} html - The HTML string to convert to plaintext.
191
- * @param {PlaintextConfig} [options] - A configuration object for the plaintext generator.
192
- * @returns {Promise<string>} The plaintext version of the HTML string.
192
+ * @param {Object} [config={}] - Configuration object.
193
+ * @param {PostHTMLConfig} [config.posthtml] - PostHTML options.
194
+ * @param {PlaintextOptions} [config.strip] - Options for `string-strip-html`.
195
+ * @returns {Promise<string>} A string representing the HTML converted to plaintext.
193
196
  * @see https://maizzle.com/docs/plaintext
194
197
  */
195
- function plaintext(html: string, options?: PlaintextConfig): Promise<string>;
198
+ function generatePlaintext(
199
+ html: string,
200
+ config?: {
201
+ /**
202
+ * Configure PostHTML options.
203
+ */
204
+ posthtml?: PostHTMLConfig
205
+ }
206
+ & PlaintextOptions,
207
+ ): Promise<string>;
196
208
 
197
209
  export {
198
210
  Config,
@@ -213,7 +225,7 @@ declare namespace MaizzleFramework {
213
225
  sixHEX,
214
226
  minify,
215
227
  replaceStrings,
216
- plaintext,
228
+ generatePlaintext,
217
229
  }
218
230
  }
219
231