@maizzle/framework 6.0.0-3 → 6.0.0-5

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