@maizzle/framework 6.0.0-3 → 6.0.0-4

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 CHANGED
@@ -4,6 +4,20 @@ 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
+ ## [6.0.0-3] - 2025-07-14
8
+
9
+ ### Added
10
+
11
+ - added support for skipping CSS compilation on individual `<style>` tags by adding any of the following attributes: `raw`, `plain`, `as-is`, `uncompiled`, `unprocessed`
12
+
13
+ ### Changed
14
+
15
+ - refactored CSS compilation into a custom PostHTML plugin
16
+
17
+ ### Removed
18
+
19
+ - removed `posthtml-postcss` dependency
20
+
7
21
  ## [6.0.0-2] - 2025-07-11
8
22
 
9
23
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maizzle/framework",
3
- "version": "6.0.0-3",
3
+ "version": "6.0.0-4",
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",
@@ -86,7 +86,6 @@
86
86
  "posthtml-markdownit": "^3.1.0",
87
87
  "posthtml-mso": "^3.1.0",
88
88
  "posthtml-parser": "^0.12.1",
89
- "posthtml-postcss": "^1.0.2",
90
89
  "posthtml-postcss-merge-longhand": "^3.1.2",
91
90
  "posthtml-render": "^3.0.0",
92
91
  "posthtml-safe-class-names": "^4.1.0",
@@ -6,51 +6,16 @@ import posthtml from 'posthtml'
6
6
  import posthtmlFetch from 'posthtml-fetch'
7
7
  import envTags from './plugins/envTags.js'
8
8
  import components from 'posthtml-component'
9
- import posthtmlPostcss from 'posthtml-postcss'
10
9
  import expandLinkTag from './plugins/expandLinkTag.js'
11
10
  import envAttributes from './plugins/envAttributes.js'
12
11
  import { getPosthtmlOptions } from './defaultConfig.js'
13
- import lowerCssSyntax from './plugins/lowerCssSyntax.js'
14
12
  import combineMediaQueries from './plugins/combineMediaQueries.js'
13
+ import defaultComponentsConfig from './defaultComponentsConfig.js'
15
14
 
16
15
  // PostCSS
17
- import tailwindcss from '@tailwindcss/postcss'
18
- import postcssCalc from 'postcss-calc'
19
- import cssVariables from 'postcss-css-variables'
20
- import postcssSafeParser from 'postcss-safe-parser'
21
- import removeDuplicateSelectors from './plugins/postcss/removeDuplicateSelectors.js'
22
- import cleanupTailwindArtifacts from './plugins/postcss/cleanupTailwindArtifacts.js'
23
-
24
- import defaultComponentsConfig from './defaultComponentsConfig.js'
16
+ import compileCss from './plugins/postcss/compileCss.js'
25
17
 
26
18
  export async function process(html = '', config = {}) {
27
- /**
28
- * Configure PostCSS pipeline. Plugins defined and added here
29
- * will apply to all `<style>` tags in the HTML.
30
- */
31
- const resolveCSSProps = get(config, 'css.resolveProps')
32
- const resolveCalc = get(config, 'css.resolveCalc') !== false
33
- ? get(config, 'css.resolveCalc', { precision: 2 }) // it's true by default, use default precision 2
34
- : false
35
-
36
- const postcssPlugin = posthtmlPostcss(
37
- [
38
- tailwindcss(get(config, 'css.tailwind', {})),
39
- resolveCSSProps !== false && cssVariables(resolveCSSProps),
40
- resolveCalc !== false && postcssCalc(resolveCalc),
41
- removeDuplicateSelectors(),
42
- cleanupTailwindArtifacts(get(config, 'css.cleanup', {})),
43
- ...get(config, 'postcss.plugins', []),
44
- ],
45
- merge(
46
- get(config, 'postcss.options', {}),
47
- {
48
- from: config.cwd || './',
49
- parser: postcssSafeParser
50
- }
51
- )
52
- )
53
-
54
19
  /**
55
20
  * Define PostHTML options by merging user-provided ones
56
21
  * on top of a default configuration.
@@ -106,18 +71,15 @@ export async function process(html = '', config = {}) {
106
71
 
107
72
  return posthtml([
108
73
  ...beforePlugins,
109
- envTags(config.env),
110
- envAttributes(config.env),
111
- expandLinkTag(),
112
- postcssPlugin,
113
74
  fetchPlugin,
114
75
  components(componentsConfig),
76
+ fetchPlugin,
115
77
  expandLinkTag(),
116
- postcssPlugin,
117
78
  envTags(config.env),
118
79
  envAttributes(config.env),
119
- lowerCssSyntax(get(config, 'css.lightningcss', {})),
120
- get(config, 'css.combineMediaQueries') !== false && combineMediaQueries(get(config, 'css.combineMediaQueries', { sort: 'mobile-first' })),
80
+ compileCss(config),
81
+ get(config, 'css.combineMediaQueries') !== false
82
+ && combineMediaQueries(get(config, 'css.combineMediaQueries', { sort: 'mobile-first' })),
121
83
  ...get(
122
84
  config,
123
85
  'posthtml.plugins.after',
@@ -0,0 +1,137 @@
1
+ import postcss from 'postcss'
2
+ import get from 'lodash-es/get.js'
3
+ import { defu as merge } from 'defu'
4
+ import postcssCalc from 'postcss-calc'
5
+ import { transform } from 'lightningcss'
6
+ import tailwindcss from '@tailwindcss/postcss'
7
+ import cssVariables from 'postcss-css-variables'
8
+ import postcssSafeParser from 'postcss-safe-parser'
9
+ import removeDuplicateSelectors from './removeDuplicateSelectors.js'
10
+ import cleanupTailwindArtifacts from './cleanupTailwindArtifacts.js'
11
+
12
+ const validAttributeNames = new Set([
13
+ 'raw',
14
+ 'plain',
15
+ 'as-is',
16
+ 'uncompiled',
17
+ 'unprocessed',
18
+ ])
19
+
20
+ /**
21
+ * PostHTML plugin to process Tailwind CSS within style tags.
22
+ *
23
+ * This plugin processes CSS content in `<style>` tags and
24
+ * compiles it with PostCSS. `<style>` tags marked as
25
+ * `no-process` will be skipped.
26
+ */
27
+ export default function compile(config = {}) {
28
+ return tree => {
29
+ return new Promise((resolve, reject) => {
30
+ const stylePromises = []
31
+
32
+ tree.walk(node => {
33
+ if (node.tag === 'style' && node.content) {
34
+ if (node.attrs && Object.keys(node.attrs).some(attr => validAttributeNames.has(attr))) {
35
+ // Remove the attribute
36
+ for (const attr of Object.keys(node.attrs)) {
37
+ if (validAttributeNames.has(attr)) {
38
+ delete node.attrs[attr]
39
+ }
40
+ }
41
+
42
+ return node
43
+ }
44
+
45
+ const css = Array.isArray(node.content)
46
+ ? node.content.join('')
47
+ : node.content
48
+
49
+ const promise = processCss(css, config)
50
+ .then(processedCss => {
51
+ node.content = [processedCss]
52
+ })
53
+ .catch(error => {
54
+ console.warn('Error processing CSS in style tag:', error.message)
55
+ })
56
+
57
+ stylePromises.push(promise)
58
+ }
59
+
60
+ return node
61
+ })
62
+
63
+ Promise.all(stylePromises)
64
+ .then(() => resolve(tree))
65
+ .catch(reject)
66
+ })
67
+ }
68
+ }
69
+
70
+ async function processCss(css, config) {
71
+ /**
72
+ * PostCSS pipeline. Plugins defined and added here
73
+ * will apply to all `<style>` tags in the HTML,
74
+ * unless marked to be excluded.
75
+ */
76
+ const resolveCSSProps = get(config, 'css.resolveProps')
77
+ const resolveCalc = get(config, 'css.resolveCalc') !== false
78
+ ? get(config, 'css.resolveCalc', { precision: 2 })
79
+ : false
80
+
81
+ const lightningCssOptions = merge(
82
+ get(config, 'css.lightning', {}),
83
+ {
84
+ targets: {
85
+ ie: 1,
86
+ },
87
+ }
88
+ )
89
+
90
+ try {
91
+ const processor = postcss([
92
+ tailwindcss(get(config, 'css.tailwind', {})),
93
+ resolveCSSProps !== false && cssVariables(resolveCSSProps),
94
+ resolveCalc !== false && postcssCalc(resolveCalc),
95
+ removeDuplicateSelectors(),
96
+ cleanupTailwindArtifacts(get(config, 'css.cleanup', {})),
97
+ ...get(config, 'postcss.plugins', []),
98
+ ].filter(Boolean))
99
+
100
+ const result = await processor.process(css, merge(
101
+ get(config, 'postcss.options', {}),
102
+ {
103
+ from: config.cwd || './',
104
+ parser: postcssSafeParser
105
+ }
106
+ ))
107
+
108
+ /**
109
+ * Lightning CSS processing
110
+ *
111
+ * We use this to lower the modern Tailwind CSS 4 syntax
112
+ * to be more email-friendly.
113
+ */
114
+
115
+ if (result.css?.trim()) {
116
+ try {
117
+ const { code } = transform(
118
+ merge(
119
+ lightningCssOptions,
120
+ {
121
+ code: Buffer.from(result.css)
122
+ }
123
+ )
124
+ )
125
+
126
+ return code.toString()
127
+ } catch (error) {
128
+ console.warn('Failed to lower syntax with Lightning CSS:', error.message)
129
+ }
130
+ }
131
+
132
+ return result.css
133
+ } catch (error) {
134
+ console.warn('Error compiling CSS:', error.message)
135
+ return css
136
+ }
137
+ }
@@ -1,43 +0,0 @@
1
- import { defu as merge } from 'defu'
2
- import { transform } from 'lightningcss'
3
-
4
- const plugin = (options = {}) => tree => {
5
- options = merge(options, {
6
- targets: options.targets ? {} : {
7
- ie: 1,
8
- },
9
- })
10
-
11
- const process = node => {
12
- // Check if this is a style tag with content
13
- if (node.tag === 'style' && node.content && Array.isArray(node.content)) {
14
- // Get the CSS content from the style tag
15
- const cssContent = node.content.join('')
16
-
17
- if (cssContent.trim()) {
18
- try {
19
- const { code } = transform(
20
- merge(
21
- options,
22
- {
23
- code: Buffer.from(cssContent)
24
- }
25
- )
26
- )
27
-
28
- // Replace the content with processed CSS
29
- node.content = [code.toString()]
30
- } catch (error) {
31
- // If processing fails, leave the content unchanged
32
- console.warn('Failed to process media queries:', error.message)
33
- }
34
- }
35
- }
36
-
37
- return node
38
- }
39
-
40
- return tree.walk(process)
41
- }
42
-
43
- export default plugin