@maizzle/framework 5.0.9 → 5.2.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/CHANGELOG.md +6 -0
- package/package.json +3 -2
- package/src/commands/build.js +11 -6
- package/src/posthtml/index.js +4 -2
- package/src/posthtml/plugins/expandLinkTag.js +50 -28
- package/src/server/client.js +2 -2
- package/src/server/index.js +12 -4
- package/src/server/routes/hmr.js +1 -1
- package/src/server/routes/index.js +1 -0
- package/src/transformers/inline.js +4 -4
- package/src/utils/getConfigByFilePath.js +3 -3
- package/src/utils/string.js +1 -0
- package/types/build.d.ts +4 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [5.1.0] - 2025-06-13
|
|
8
|
+
|
|
9
|
+
This release adds support for using Tailwind CSS color opacity modifiers in Gmail by automatically converting modern rgb/a syntax to the legacy, comma-separated syntax.
|
|
10
|
+
|
|
11
|
+
- feat: use commas instead of slash for css color opacity modifiers f26774b
|
|
12
|
+
|
|
7
13
|
## [5.0.9] - 2025-06-12
|
|
8
14
|
|
|
9
15
|
This release fixes an issue where builds failed because `preferUnitlessValues` (which is on by default) was throwing an error when encountering invalid inline CSS.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maizzle/framework",
|
|
3
|
-
"version": "5.0
|
|
3
|
+
"version": "5.2.0",
|
|
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",
|
|
@@ -70,6 +70,7 @@
|
|
|
70
70
|
"pathe": "^2.0.0",
|
|
71
71
|
"postcss": "^8.4.49",
|
|
72
72
|
"postcss-calc": "^10.0.2",
|
|
73
|
+
"postcss-color-functional-notation": "^7.0.10",
|
|
73
74
|
"postcss-css-variables": "^0.19.0",
|
|
74
75
|
"postcss-import": "^16.1.0",
|
|
75
76
|
"postcss-safe-parser": "^7.0.0",
|
|
@@ -96,7 +97,7 @@
|
|
|
96
97
|
"ws": "^8.18.0"
|
|
97
98
|
},
|
|
98
99
|
"devDependencies": {
|
|
99
|
-
"@biomejs/biome": "
|
|
100
|
+
"@biomejs/biome": "2.0.5",
|
|
100
101
|
"@types/js-beautify": "^1.14.3",
|
|
101
102
|
"@types/markdown-it": "^14.1.2",
|
|
102
103
|
"@vitest/coverage-v8": "^3.0.4",
|
package/src/commands/build.js
CHANGED
|
@@ -298,14 +298,19 @@ export default async (config = {}) => {
|
|
|
298
298
|
|
|
299
299
|
/**
|
|
300
300
|
* Copy static files
|
|
301
|
-
*
|
|
302
|
-
* TODO: support an array of objects with source and destination,
|
|
303
|
-
* i.e. static: [{ source: 'src/assets', destination: 'assets' }, ...]
|
|
304
301
|
*/
|
|
305
|
-
|
|
302
|
+
let staticFiles = get(config, 'build.static', [])
|
|
303
|
+
|
|
304
|
+
if (!Array.isArray(staticFiles)) {
|
|
305
|
+
staticFiles = [staticFiles]
|
|
306
|
+
}
|
|
306
307
|
|
|
307
|
-
for
|
|
308
|
-
|
|
308
|
+
for (const definition of staticFiles) {
|
|
309
|
+
const staticSourcePaths = getRootDirectories([...new Set(definition.source)])
|
|
310
|
+
|
|
311
|
+
for await (const rootDir of staticSourcePaths) {
|
|
312
|
+
await cp(rootDir, path.join(buildOutputPath, definition.destination), { recursive: true })
|
|
313
|
+
}
|
|
309
314
|
}
|
|
310
315
|
|
|
311
316
|
const allOutputFiles = await fg.glob(path.join(buildOutputPath, '**/*'))
|
package/src/posthtml/index.js
CHANGED
|
@@ -17,6 +17,7 @@ import postcssCalc from 'postcss-calc'
|
|
|
17
17
|
import postcssImport from 'postcss-import'
|
|
18
18
|
import cssVariables from 'postcss-css-variables'
|
|
19
19
|
import postcssSafeParser from 'postcss-safe-parser'
|
|
20
|
+
import postcssColorFunctionalNotation from 'postcss-color-functional-notation'
|
|
20
21
|
|
|
21
22
|
import defaultComponentsConfig from './defaultComponentsConfig.js'
|
|
22
23
|
|
|
@@ -36,6 +37,7 @@ export async function process(html = '', config = {}) {
|
|
|
36
37
|
tailwindcss(get(config, 'css.tailwind', {})),
|
|
37
38
|
resolveCSSProps !== false && cssVariables(resolveCSSProps),
|
|
38
39
|
resolveCalc !== false && postcssCalc(resolveCalc),
|
|
40
|
+
postcssColorFunctionalNotation(),
|
|
39
41
|
...get(config, 'postcss.plugins', []),
|
|
40
42
|
],
|
|
41
43
|
merge(
|
|
@@ -104,11 +106,11 @@ export async function process(html = '', config = {}) {
|
|
|
104
106
|
...beforePlugins,
|
|
105
107
|
envTags(config.env),
|
|
106
108
|
envAttributes(config.env),
|
|
107
|
-
expandLinkTag,
|
|
109
|
+
expandLinkTag(),
|
|
108
110
|
postcssPlugin,
|
|
109
111
|
fetchPlugin,
|
|
110
112
|
components(componentsConfig),
|
|
111
|
-
expandLinkTag,
|
|
113
|
+
expandLinkTag(),
|
|
112
114
|
postcssPlugin,
|
|
113
115
|
envTags(config.env),
|
|
114
116
|
envAttributes(config.env),
|
|
@@ -1,37 +1,59 @@
|
|
|
1
|
-
import fs from 'node:fs'
|
|
2
|
-
|
|
3
1
|
const targets = new Set(['expand', 'inline'])
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
node.attrs[attr] = false
|
|
3
|
+
const expandLinkPlugin = () => tree => {
|
|
4
|
+
return new Promise((resolve, reject) => {
|
|
5
|
+
const isNode = typeof process !== 'undefined' && process.versions?.node
|
|
6
|
+
|
|
7
|
+
const loadFile = async href => {
|
|
8
|
+
if (isNode) {
|
|
9
|
+
const { readFile } = await import('node:fs/promises')
|
|
10
|
+
return await readFile(href, 'utf8')
|
|
14
11
|
}
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
const res = await fetch(href)
|
|
14
|
+
|
|
15
|
+
if (!res.ok) {
|
|
16
|
+
throw new Error(`Failed to fetch ${href}: ${res.statusText}`)
|
|
17
|
+
}
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
node
|
|
21
|
-
&& node.tag === 'link'
|
|
22
|
-
&& node.attrs
|
|
23
|
-
&& node.attrs.href
|
|
24
|
-
&& node.attrs.rel === 'stylesheet'
|
|
25
|
-
) {
|
|
26
|
-
node.content = [fs.readFileSync(node.attrs.href, 'utf8')]
|
|
27
|
-
node.tag = 'style'
|
|
28
|
-
node.attrs = {}
|
|
19
|
+
return await res.text()
|
|
29
20
|
}
|
|
30
21
|
|
|
31
|
-
|
|
32
|
-
}
|
|
22
|
+
const promises = []
|
|
33
23
|
|
|
34
|
-
|
|
35
|
-
|
|
24
|
+
try {
|
|
25
|
+
tree.walk(node => {
|
|
26
|
+
if (node?.attrs && ![...targets].some(attr => attr in node.attrs)) {
|
|
27
|
+
for (const attr of targets) {
|
|
28
|
+
node.attrs[attr] = false
|
|
29
|
+
}
|
|
30
|
+
return node
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (
|
|
34
|
+
node?.tag === 'link' &&
|
|
35
|
+
node.attrs?.href &&
|
|
36
|
+
node.attrs.rel === 'stylesheet'
|
|
37
|
+
) {
|
|
38
|
+
const promise = loadFile(node.attrs.href).then(content => {
|
|
39
|
+
node.tag = 'style'
|
|
40
|
+
node.attrs = {}
|
|
41
|
+
node.content = [content]
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
promises.push(promise)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return node
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
Promise.all(promises)
|
|
51
|
+
.then(() => resolve(tree))
|
|
52
|
+
.catch(reject)
|
|
53
|
+
} catch (err) {
|
|
54
|
+
reject(err)
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
}
|
|
36
58
|
|
|
37
|
-
export default
|
|
59
|
+
export default expandLinkPlugin
|
package/src/server/client.js
CHANGED
|
@@ -138,7 +138,7 @@ function connectWebSocket() {
|
|
|
138
138
|
if (styleAttribute) {
|
|
139
139
|
const urlPattern = /(url\(["']?)(.*?)(["']?\))/g
|
|
140
140
|
// Replace URLs in style attribute with cache-busting parameter
|
|
141
|
-
const updatedStyleAttribute = styleAttribute.replace(urlPattern, (
|
|
141
|
+
const updatedStyleAttribute = styleAttribute.replace(urlPattern, (_match, p1, p2, p3) => {
|
|
142
142
|
// Update the value of 'v' parameter if it already exists
|
|
143
143
|
if (p2.includes('?')) {
|
|
144
144
|
return `${p1}${p2.replace(/([?&])v=[^&]*/, `$1v=${randomNumber}`)}${p3}`
|
|
@@ -172,7 +172,7 @@ function connectWebSocket() {
|
|
|
172
172
|
})
|
|
173
173
|
|
|
174
174
|
// Handle connection opened
|
|
175
|
-
socket.addEventListener('open',
|
|
175
|
+
socket.addEventListener('open', _event => {
|
|
176
176
|
console.log('WebSocket connection opened')
|
|
177
177
|
})
|
|
178
178
|
|
package/src/server/index.js
CHANGED
|
@@ -53,7 +53,7 @@ async function getTemplatePaths(templateFolders) {
|
|
|
53
53
|
return await fg.glob([...new Set(templateFolders)])
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
async function getUpdatedRoutes(
|
|
56
|
+
async function getUpdatedRoutes(_app, config) {
|
|
57
57
|
return getTemplatePaths(getTemplateFolders(config))
|
|
58
58
|
}
|
|
59
59
|
|
|
@@ -252,6 +252,14 @@ export default async (config = {}) => {
|
|
|
252
252
|
}
|
|
253
253
|
})
|
|
254
254
|
|
|
255
|
+
let staticFiles = get(config, 'build.static', [])
|
|
256
|
+
|
|
257
|
+
if (!Array.isArray(staticFiles)) {
|
|
258
|
+
staticFiles = [staticFiles]
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const staticFilesSourcePaths = staticFiles.flatMap((definition) => definition.source)
|
|
262
|
+
|
|
255
263
|
/**
|
|
256
264
|
* Global watcher
|
|
257
265
|
*
|
|
@@ -263,7 +271,7 @@ export default async (config = {}) => {
|
|
|
263
271
|
'maizzle.config*.{js,cjs,ts}',
|
|
264
272
|
'tailwind*.config.{js,ts}',
|
|
265
273
|
'**/*.css',
|
|
266
|
-
...
|
|
274
|
+
...staticFilesSourcePaths,
|
|
267
275
|
...get(config, 'server.watch', []),
|
|
268
276
|
])
|
|
269
277
|
|
|
@@ -365,12 +373,12 @@ export default async (config = {}) => {
|
|
|
365
373
|
const srcFoldersList = await fg.glob(
|
|
366
374
|
[
|
|
367
375
|
'**/*/',
|
|
368
|
-
...
|
|
376
|
+
...staticFilesSourcePaths,
|
|
369
377
|
], {
|
|
370
378
|
onlyFiles: false,
|
|
371
379
|
ignore: [
|
|
372
380
|
'node_modules',
|
|
373
|
-
get(config, 'build.output.path', 'build_*')
|
|
381
|
+
`${get(config, 'build.output.path', 'build_*')}/**`,
|
|
374
382
|
]
|
|
375
383
|
})
|
|
376
384
|
|
package/src/server/routes/hmr.js
CHANGED
|
@@ -8,7 +8,7 @@ const router = express.Router()
|
|
|
8
8
|
const require = createRequire(import.meta.url)
|
|
9
9
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
10
10
|
|
|
11
|
-
router.get('/hmr.js', async (
|
|
11
|
+
router.get('/hmr.js', async (_req, res) => {
|
|
12
12
|
try {
|
|
13
13
|
const morphdomPath = require.resolve('morphdom/dist/morphdom-umd.js')
|
|
14
14
|
const morphdomScript = await fs.readFile(morphdomPath, 'utf8')
|
|
@@ -71,10 +71,10 @@ export async function inline(html = '', options = {}) {
|
|
|
71
71
|
})
|
|
72
72
|
|
|
73
73
|
// Add a `data-embed` attribute to style tags that have the embed attribute
|
|
74
|
-
$('style[embed]:not([data-embed])').each((
|
|
74
|
+
$('style[embed]:not([data-embed])').each((_i, el) => {
|
|
75
75
|
$(el).attr('data-embed', '')
|
|
76
76
|
})
|
|
77
|
-
$('style[data-embed]:not([embed])').each((
|
|
77
|
+
$('style[data-embed]:not([embed])').each((_i, el) => {
|
|
78
78
|
$(el).attr('embed', '')
|
|
79
79
|
})
|
|
80
80
|
|
|
@@ -111,7 +111,7 @@ export async function inline(html = '', options = {}) {
|
|
|
111
111
|
* Remove inlined selectors from the HTML
|
|
112
112
|
*/
|
|
113
113
|
// For each style tag
|
|
114
|
-
$('style:not([embed])').each((
|
|
114
|
+
$('style:not([embed])').each((_i, el) => {
|
|
115
115
|
// Parse the CSS
|
|
116
116
|
const { root } = postcss()
|
|
117
117
|
.process(
|
|
@@ -243,7 +243,7 @@ export async function inline(html = '', options = {}) {
|
|
|
243
243
|
})
|
|
244
244
|
})
|
|
245
245
|
|
|
246
|
-
$('style[embed]').each((
|
|
246
|
+
$('style[embed]').each((_i, el) => {
|
|
247
247
|
$(el).removeAttr('embed')
|
|
248
248
|
})
|
|
249
249
|
|
|
@@ -73,7 +73,7 @@ export async function readFileConfig(config) {
|
|
|
73
73
|
baseConfig = merge(baseConfigFile, baseConfig)
|
|
74
74
|
break
|
|
75
75
|
}
|
|
76
|
-
} catch (
|
|
76
|
+
} catch (_error) {
|
|
77
77
|
break
|
|
78
78
|
}
|
|
79
79
|
|
|
@@ -107,7 +107,7 @@ export async function readFileConfig(config) {
|
|
|
107
107
|
loaded = true
|
|
108
108
|
break
|
|
109
109
|
}
|
|
110
|
-
} catch (
|
|
110
|
+
} catch (_error) {
|
|
111
111
|
break
|
|
112
112
|
}
|
|
113
113
|
}
|
|
@@ -128,7 +128,7 @@ export async function readFileConfig(config) {
|
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
return merge(envConfig, baseConfig)
|
|
131
|
-
} catch (
|
|
131
|
+
} catch (_error) {
|
|
132
132
|
throw new Error('Could not compute config')
|
|
133
133
|
}
|
|
134
134
|
}
|
package/src/utils/string.js
CHANGED
|
@@ -153,6 +153,7 @@ export function getRootDirectories(patterns = []) {
|
|
|
153
153
|
* @returns
|
|
154
154
|
*/
|
|
155
155
|
export function getFileExtensionsFromPattern(pattern) {
|
|
156
|
+
// biome-ignore lint: needs to be escaped
|
|
156
157
|
const starExtPattern = /\.([^\*\{\}]+)$/ // Matches .ext but not .* or .{ext}
|
|
157
158
|
const bracePattern = /\.{([^}]+)}$/ // Matches .{ext} or .{ext,ext}
|
|
158
159
|
const wildcardPattern = /\.\*$/ // Matches .*
|
package/types/build.d.ts
CHANGED