From 8b5596e2adec3d5953086cecebb85a10f85655fa Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Thu, 10 Jul 2025 17:34:17 +0300 Subject: [PATCH 001/100] feat: add tailwindcss v4 support --- package-lock.json | 1208 ++++++++++--------- package.json | 7 +- src/posthtml/index.js | 18 +- src/posthtml/plugins/combineMediaQueries.js | 42 + test/postcss.test.js | 17 +- 5 files changed, 710 insertions(+), 582 deletions(-) create mode 100644 src/posthtml/plugins/combineMediaQueries.js diff --git a/package-lock.json b/package-lock.json index b6e36967..42d2c2c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "MIT", "dependencies": { "@maizzle/cli": "^2.0.0", + "@tailwindcss/postcss": "^4.1.11", "cheerio": "^1.0.0", "chokidar": "^3.6.0", "cli-table3": "^0.6.5", @@ -29,10 +30,10 @@ "pathe": "^2.0.0", "postcss": "^8.4.49", "postcss-calc": "^10.0.2", - "postcss-color-functional-notation": "^7.0.10", "postcss-css-variables": "^0.19.0", - "postcss-import": "^16.1.0", + "postcss-lightningcss": "^1.0.1", "postcss-safe-parser": "^7.0.0", + "postcss-sort-media-queries": "^5.2.0", "posthtml": "^0.16.6", "posthtml-attrs-parser": "^1.1.1", "posthtml-base-url": "^3.1.8", @@ -52,7 +53,7 @@ "posthtml-widows": "^1.0.0", "pretty": "^2.0.0", "string-strip-html": "^13.4.8", - "tailwindcss": "^3.4.16", + "tailwindcss": "^4.1.11", "ws": "^8.18.0" }, "bin": { @@ -86,7 +87,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -356,163 +356,6 @@ "node": ">=0.1.90" } }, - "node_modules/@csstools/color-helpers": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", - "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - } - }, - "node_modules/@csstools/css-calc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", - "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-color-parser": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.10.tgz", - "integrity": "sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "dependencies": { - "@csstools/color-helpers": "^5.0.2", - "@csstools/css-calc": "^2.1.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", - "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-tokenizer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", - "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@csstools/postcss-progressive-custom-properties": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-4.1.0.tgz", - "integrity": "sha512-YrkI9dx8U4R8Sz2EJaoeD9fI7s7kmeEBfmO+UURNeL6lQI7VxF6sBE+rSqdCBn4onwqmxFdBU3lTwyYb/lCmxA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/utilities": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@csstools/utilities/-/utilities-2.0.0.tgz", - "integrity": "sha512-5VdOr0Z71u+Yp3ozOx8T11N703wIFGVRgOWbOZMKgglPJsWA54MRIoMNVMa7shUToIhx5J8vX4sOZgD2XiihiQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", @@ -978,6 +821,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -1412,6 +1267,276 @@ "win32" ] }, + "node_modules/@tailwindcss/node": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.11.tgz", + "integrity": "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==", + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "enhanced-resolve": "^5.18.1", + "jiti": "^2.4.2", + "lightningcss": "1.30.1", + "magic-string": "^0.30.17", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.11" + } + }, + "node_modules/@tailwindcss/node/node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.11.tgz", + "integrity": "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.4", + "tar": "^7.4.3" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.11", + "@tailwindcss/oxide-darwin-arm64": "4.1.11", + "@tailwindcss/oxide-darwin-x64": "4.1.11", + "@tailwindcss/oxide-freebsd-x64": "4.1.11", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", + "@tailwindcss/oxide-linux-x64-musl": "4.1.11", + "@tailwindcss/oxide-wasm32-wasi": "4.1.11", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.11.tgz", + "integrity": "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.11.tgz", + "integrity": "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.11.tgz", + "integrity": "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.11.tgz", + "integrity": "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.11.tgz", + "integrity": "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.11.tgz", + "integrity": "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.11.tgz", + "integrity": "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.11.tgz", + "integrity": "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.11.tgz", + "integrity": "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.11.tgz", + "integrity": "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@emnapi/wasi-threads": "^1.0.2", + "@napi-rs/wasm-runtime": "^0.2.11", + "@tybys/wasm-util": "^0.9.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz", + "integrity": "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.11.tgz", + "integrity": "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.11.tgz", + "integrity": "sha512-q/EAIIpF6WpLhKEuQSEVMZNMIY8KhWoAemZ9eylNAih9jxMGAYPPWBn3I9QL/2jZ+e7OEz/tZkX5HwbBR4HohA==", + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.1.11", + "@tailwindcss/oxide": "4.1.11", + "postcss": "^8.4.41", + "tailwindcss": "4.1.11" + } + }, "node_modules/@types/chai": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz", @@ -1699,12 +1824,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "license": "MIT" - }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -1718,12 +1837,6 @@ "node": ">= 8" } }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "license": "MIT" - }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -1985,15 +2098,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/caniuse-lite": { "version": "1.0.30001702", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001702.tgz", @@ -2150,6 +2254,15 @@ "fsevents": "~2.3.2" } }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, "node_modules/citty": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", @@ -2542,6 +2655,15 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/dezalgo": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", @@ -2552,18 +2674,6 @@ "wrappy": "1" } }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "license": "Apache-2.0" - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "license": "MIT" - }, "node_modules/dom-serializer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", @@ -2744,6 +2854,19 @@ "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" } }, + "node_modules/enhanced-resolve": { + "version": "5.18.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz", + "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -3301,6 +3424,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, "node_modules/gray-matter": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", @@ -3527,21 +3656,6 @@ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "license": "MIT" }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", @@ -3744,6 +3858,8 @@ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "license": "MIT", + "optional": true, + "peer": true, "bin": { "jiti": "bin/jiti.js" } @@ -3827,6 +3943,234 @@ "node": ">=0.10.0" } }, + "node_modules/lightningcss": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/lilconfig": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", @@ -3839,12 +4183,6 @@ "url": "https://github.com/sponsors/antonk52" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "license": "MIT" - }, "node_modules/linkify-it": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", @@ -3911,7 +4249,6 @@ "version": "0.30.17", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" @@ -4131,6 +4468,33 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/mlly": { "version": "1.7.4", "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz", @@ -4155,17 +4519,6 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -4267,15 +4620,6 @@ "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", "license": "MIT" }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/object-boolean-combinations": { "version": "6.1.8", "resolved": "https://registry.npmjs.org/object-boolean-combinations/-/object-boolean-combinations-6.1.8.tgz", @@ -4289,15 +4633,6 @@ "node": ">=14.18.0" } }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -4476,12 +4811,6 @@ "node": ">=8" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT" - }, "node_modules/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", @@ -4538,24 +4867,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/pkg-types": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", @@ -4611,35 +4922,6 @@ "postcss": "^8.4.38" } }, - "node_modules/postcss-color-functional-notation": { - "version": "7.0.10", - "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-7.0.10.tgz", - "integrity": "sha512-k9qX+aXHBiLTRrWoCJuUFI6F1iF6QJQUXNVWJVSbqZgj57jDhBlOvD8gNUGl35tgqDivbGLhZeW3Ongz4feuKA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-color-parser": "^3.0.10", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, "node_modules/postcss-css-variables": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/postcss-css-variables/-/postcss-css-variables-0.19.0.tgz", @@ -4663,42 +4945,22 @@ "node": ">=0.8.0" } }, - "node_modules/postcss-import": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-16.1.1.tgz", - "integrity": "sha512-2xVS1NCZAfjtVdvXiyegxzJ447GyqCeEI5V7ApgQVOWnros1p5lGNovJNapwPpMombyFBfqDwt7AD3n2l0KOfQ==", + "node_modules/postcss-lightningcss": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postcss-lightningcss/-/postcss-lightningcss-1.0.1.tgz", + "integrity": "sha512-9IrtZVt2HQ92iZJTkO43Qipx7E3PM+lLzZM8aGwMmMjNQHcir5jNC42U33p3Gh2lj1nES/ireYWEbMrJNiRBoQ==", "license": "MIT", "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" + "browserslist": "^4.19.1", + "lightningcss": "^1.22.0" }, "engines": { - "node": ">=18.0.0" + "node": "^18.0.0 || ^20.0.0 || >= 21" }, "peerDependencies": { "postcss": "^8.0.0" } }, - "node_modules/postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", - "license": "MIT", - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, "node_modules/postcss-load-config": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", @@ -4757,44 +5019,6 @@ "postcss": "^8.4.31" } }, - "node_modules/postcss-nested": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", - "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.1.1" - }, - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-nested/node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/postcss-safe-parser": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz", @@ -4834,6 +5058,21 @@ "node": ">=4" } }, + "node_modules/postcss-sort-media-queries": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-5.2.0.tgz", + "integrity": "sha512-AZ5fDMLD8SldlAYlvi8NIqo0+Z8xnXU2ia0jxmuhxAU+Lqt9K+AlmLNJ/zWEnE9x+Zx3qL3+1K20ATgNOr3fAA==", + "license": "MIT", + "dependencies": { + "sort-css-media-queries": "2.2.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.4.23" + } + }, "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", @@ -5477,15 +5716,6 @@ "node": ">=0.10.0" } }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "license": "MIT", - "dependencies": { - "pify": "^2.3.0" - } - }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -5507,26 +5737,6 @@ "node": ">=14.18.0" } }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/restore-cursor": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", @@ -5869,6 +6079,15 @@ "node": "*" } }, + "node_modules/sort-css-media-queries": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.2.0.tgz", + "integrity": "sha512-0xtkGhWCC9MGt/EzgnvbbbKhqWjl1+/rncmhTh5qCpbYguXh6S/qwePfv/JQ8jePXXmqingylxoC49pCkSPIbA==", + "license": "MIT", + "engines": { + "node": ">= 6.3.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -6216,37 +6435,6 @@ "node": ">=4" } }, - "node_modules/sucrase": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", - "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "^10.3.10", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/sucrase/node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/superagent": { "version": "10.2.2", "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.2.tgz", @@ -6305,130 +6493,36 @@ "node": ">=8" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/tailwindcss": { - "version": "3.4.17", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", - "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", - "license": "MIT", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.6.0", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.2", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.21.6", - "lilconfig": "^3.1.3", - "micromatch": "^4.0.8", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.1.1", - "postcss": "^8.4.47", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.2", - "postcss-nested": "^6.2.0", - "postcss-selector-parser": "^6.1.2", - "resolve": "^1.22.8", - "sucrase": "^3.35.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tailwindcss/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/tailwindcss/node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz", + "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==", + "license": "MIT" }, - "node_modules/tailwindcss/node_modules/postcss-load-config": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", - "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], + "node_modules/tapable": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", "license": "MIT", - "dependencies": { - "lilconfig": "^3.0.0", - "yaml": "^2.3.4" - }, "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } + "node": ">=6" } }, - "node_modules/tailwindcss/node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "license": "MIT", + "node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "license": "ISC", "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" }, "engines": { - "node": ">=4" + "node": ">=18" } }, "node_modules/test-exclude": { @@ -6490,27 +6584,6 @@ "url": "https://bevry.me/fund" } }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/tiny-invariant": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", @@ -6626,12 +6699,6 @@ "node": ">=0.6" } }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "license": "Apache-2.0" - }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -7260,11 +7327,22 @@ } } }, + "node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, "node_modules/yaml": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", "license": "ISC", + "optional": true, + "peer": true, "bin": { "yaml": "bin.mjs" }, diff --git a/package.json b/package.json index e277cf3b..9efa7c68 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ ], "dependencies": { "@maizzle/cli": "^2.0.0", + "@tailwindcss/postcss": "^4.1.11", "cheerio": "^1.0.0", "chokidar": "^3.6.0", "cli-table3": "^0.6.5", @@ -70,10 +71,10 @@ "pathe": "^2.0.0", "postcss": "^8.4.49", "postcss-calc": "^10.0.2", - "postcss-color-functional-notation": "^7.0.10", "postcss-css-variables": "^0.19.0", - "postcss-import": "^16.1.0", + "postcss-lightningcss": "^1.0.1", "postcss-safe-parser": "^7.0.0", + "postcss-sort-media-queries": "^5.2.0", "posthtml": "^0.16.6", "posthtml-attrs-parser": "^1.1.1", "posthtml-base-url": "^3.1.8", @@ -93,7 +94,7 @@ "posthtml-widows": "^1.0.0", "pretty": "^2.0.0", "string-strip-html": "^13.4.8", - "tailwindcss": "^3.4.16", + "tailwindcss": "^4.1.11", "ws": "^8.18.0" }, "devDependencies": { diff --git a/src/posthtml/index.js b/src/posthtml/index.js index b9a4a0f3..92f77938 100644 --- a/src/posthtml/index.js +++ b/src/posthtml/index.js @@ -10,14 +10,14 @@ import posthtmlPostcss from 'posthtml-postcss' import expandLinkTag from './plugins/expandLinkTag.js' import envAttributes from './plugins/envAttributes.js' import { getPosthtmlOptions } from './defaultConfig.js' +import combineMediaQueries from './plugins/combineMediaQueries.js' // PostCSS -import tailwindcss from 'tailwindcss' +import tailwindcss from '@tailwindcss/postcss' import postcssCalc from 'postcss-calc' -import postcssImport from 'postcss-import' import cssVariables from 'postcss-css-variables' import postcssSafeParser from 'postcss-safe-parser' -import postcssColorFunctionalNotation from 'postcss-color-functional-notation' +import postcssLightningCss from 'postcss-lightningcss' import defaultComponentsConfig from './defaultComponentsConfig.js' @@ -33,11 +33,18 @@ export async function process(html = '', config = {}) { const postcssPlugin = posthtmlPostcss( [ - postcssImport(), tailwindcss(get(config, 'css.tailwind', {})), resolveCSSProps !== false && cssVariables(resolveCSSProps), resolveCalc !== false && postcssCalc(resolveCalc), - postcssColorFunctionalNotation(), + postcssLightningCss({ + lightningcssOptions: { + errorRecovery: true, + minify: false, + targets: { + ie: 1, + } + } + }), ...get(config, 'postcss.plugins', []), ], merge( @@ -114,6 +121,7 @@ export async function process(html = '', config = {}) { postcssPlugin, envTags(config.env), envAttributes(config.env), + combineMediaQueries(get(config, 'css.combineMediaQueries', { sort: 'mobile-first' })), ...get( config, 'posthtml.plugins.after', diff --git a/src/posthtml/plugins/combineMediaQueries.js b/src/posthtml/plugins/combineMediaQueries.js new file mode 100644 index 00000000..cdb4ede1 --- /dev/null +++ b/src/posthtml/plugins/combineMediaQueries.js @@ -0,0 +1,42 @@ +import postcss from 'postcss' +import sortMediaQueries from 'postcss-sort-media-queries' + +const plugin = (options = {}) => tree => { + const process = node => { + // Check if this is a style tag with content + if (node.tag === 'style' && node.content && Array.isArray(node.content)) { + // Get the CSS content from the style tag + const cssContent = node.content.join('') + + if (cssContent.trim()) { + try { + // Create PostCSS processor for combining and sorting media queries + const processor = postcss([ + sortMediaQueries({ + sort: options.sort || 'mobile-first', + ...options + }) + ]) + + // Process CSS synchronously + const result = processor.process(cssContent, { + from: undefined, + to: undefined + }) + + // Replace the content with processed CSS + node.content = [result.css] + } catch (error) { + // If processing fails, leave the content unchanged + console.warn('Failed to process media queries:', error.message) + } + } + } + + return node + } + + return tree.walk(process) +} + +export default plugin diff --git a/test/postcss.test.js b/test/postcss.test.js index fe57ccb7..22efe691 100644 --- a/test/postcss.test.js +++ b/test/postcss.test.js @@ -17,7 +17,7 @@ describe.concurrent('PostCSS', () => {

test

`).then(({ html }) => { - expect(cleanString(html)).toBe(`

test

`) + expect(cleanString(html)).toBe(`

test

`) }) // Passing options @@ -56,7 +56,7 @@ describe.concurrent('PostCSS', () => { resolveProps: false, } }).then(({ html }) => { - expect(cleanString(html)).toBe(`

test

`) + expect(cleanString(html)).toBe(`

test

`) }) }) @@ -71,7 +71,7 @@ describe.concurrent('PostCSS', () => { posthtml(html) .then(({ html }) => { - expect(cleanString(html)).toBe('') + expect(cleanString(html)).toBe('') }) posthtml(html, { @@ -81,17 +81,17 @@ describe.concurrent('PostCSS', () => { }, } }).then(({ html }) => { - expect(cleanString(html)).toBe('') + expect(cleanString(html)).toBe('') }) }) test('functional color notation', async () => { const html = ` @@ -102,9 +102,8 @@ describe.concurrent('PostCSS', () => { expect(cleanString(html)) .toBe( cleanString(` - ` ) ) From a352a21bf53e3d2c12164991d40a0e18e7ce82d0 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Thu, 10 Jul 2025 17:38:54 +0300 Subject: [PATCH 002/100] 6.0.0-0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 42d2c2c3..32dc3381 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@maizzle/framework", - "version": "5.2.1", + "version": "6.0.0-0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@maizzle/framework", - "version": "5.2.1", + "version": "6.0.0-0", "license": "MIT", "dependencies": { "@maizzle/cli": "^2.0.0", diff --git a/package.json b/package.json index 9efa7c68..af3cbc2d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@maizzle/framework", - "version": "5.2.1", + "version": "6.0.0-0", "description": "Maizzle is a framework that helps you quickly build HTML emails with Tailwind CSS.", "license": "MIT", "type": "module", From 7800ef45782595944deace528acd38c551744766 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Fri, 11 Jul 2025 16:34:55 +0300 Subject: [PATCH 003/100] fix: skip unsupported selectors in inliner optimizations --- src/transformers/inline.js | 118 +++++++++++++++++++------------------ 1 file changed, 60 insertions(+), 58 deletions(-) diff --git a/src/transformers/inline.js b/src/transformers/inline.js index 0a615a05..c854cb46 100644 --- a/src/transformers/inline.js +++ b/src/transformers/inline.js @@ -177,69 +177,71 @@ export async function inline(html = '', options = {}) { // Loop over selectors that we found in the + ``` + +There are still things missign and/or broken: + +- inline CSS like the `line-height` on spacers is broken/missing +- `tailwindcss-email-variants` and `tailwindcss-mso` are not ported yet +- some CSS duplication and artifacts still present in the final build + +--- + +- feat: add tailwindcss v4 support 8b5596e + +https://github.com/maizzle/framework/compare/v5.2.1...v6.0.0-0 + ## [5.2.1] - 2025-06-25 This is just a maintenance release to update dependencies. From 76d0e0653efe25ae5d0db95e8c1ef8191a63ba51 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Fri, 11 Jul 2025 18:12:40 +0300 Subject: [PATCH 007/100] 6.0.0-1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6ac0f238..6a95ce2b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@maizzle/framework", - "version": "6.0.0-0", + "version": "6.0.0-1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@maizzle/framework", - "version": "6.0.0-0", + "version": "6.0.0-1", "license": "MIT", "dependencies": { "@maizzle/cli": "^2.0.0", diff --git a/package.json b/package.json index 5a14d4f8..d953839b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@maizzle/framework", - "version": "6.0.0-0", + "version": "6.0.0-1", "description": "Maizzle is a framework that helps you quickly build HTML emails with Tailwind CSS.", "license": "MIT", "type": "module", From 788aaa706ab8fe43a9bdbf0d27a8bd51c5d6a106 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Fri, 11 Jul 2025 19:52:08 +0300 Subject: [PATCH 008/100] fix: remove previous duplicate selectors keep just the last in an identical set, as that is most likely us trying to override tailwindcss output --- src/posthtml/index.js | 2 ++ .../postcss/removeDuplicateSelectors.js | 35 +++++++++++++++++++ test/postcss.test.js | 16 +++++++++ 3 files changed, 53 insertions(+) create mode 100644 src/posthtml/plugins/postcss/removeDuplicateSelectors.js diff --git a/src/posthtml/index.js b/src/posthtml/index.js index 7c977dcd..f68be2fb 100644 --- a/src/posthtml/index.js +++ b/src/posthtml/index.js @@ -18,6 +18,7 @@ import tailwindcss from '@tailwindcss/postcss' import postcssCalc from 'postcss-calc' import cssVariables from 'postcss-css-variables' import postcssSafeParser from 'postcss-safe-parser' +import removeDuplicateSelectors from './plugins/postcss/removeDuplicateSelectors.js' import defaultComponentsConfig from './defaultComponentsConfig.js' @@ -36,6 +37,7 @@ export async function process(html = '', config = {}) { tailwindcss(get(config, 'css.tailwind', {})), resolveCSSProps !== false && cssVariables(resolveCSSProps), resolveCalc !== false && postcssCalc(resolveCalc), + removeDuplicateSelectors(), ...get(config, 'postcss.plugins', []), ], merge( diff --git a/src/posthtml/plugins/postcss/removeDuplicateSelectors.js b/src/posthtml/plugins/postcss/removeDuplicateSelectors.js new file mode 100644 index 00000000..700e7f34 --- /dev/null +++ b/src/posthtml/plugins/postcss/removeDuplicateSelectors.js @@ -0,0 +1,35 @@ +/** + * PostCSS plugin to remove duplicate selectors, keeping only the last occurrence. + * This is useful when CSS contains multiple rules with the same selector, + * and we want to keep only the most recent one. + */ +export default function removeDuplicateSelectors() { + return { + postcssPlugin: 'remove-duplicate-selectors', + OnceExit(root) { + const selectorMap = new Map() + const rulesToRemove = [] + + // First pass: collect all rules and their selectors + root.walkRules(rule => { + const selector = rule.selector + + // If we've seen this selector before, mark the previous one for removal + if (selectorMap.has(selector)) { + const previousRule = selectorMap.get(selector) + rulesToRemove.push(previousRule) + } + + // Update the map with the current rule (latest occurrence) + selectorMap.set(selector, rule) + }) + + // Second pass: remove all the duplicate rules + rulesToRemove.forEach(rule => { + rule.remove() + }) + } + } +} + +removeDuplicateSelectors.postcss = true diff --git a/test/postcss.test.js b/test/postcss.test.js index 22efe691..29017528 100644 --- a/test/postcss.test.js +++ b/test/postcss.test.js @@ -109,4 +109,20 @@ describe.concurrent('PostCSS', () => { ) }) }) + + test('removes duplicate selectors', async () => { + const html = ` + + ` + + posthtml(html) + .then(({ html }) => { + expect(cleanString(html)).toBe('') + }) + }) }) From e990f9fb1b69a0145b19af26dc17e2adbc3a8a72 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Fri, 11 Jul 2025 20:18:04 +0300 Subject: [PATCH 009/100] fix: clean up css artifacts conservatively remove unnecessary tailwindcss rules such as @layer or @property --- src/posthtml/index.js | 2 + .../postcss/cleanupTailwindArtifacts.js | 78 +++++++++++++++++++ test/postcss.test.js | 25 ++++++ 3 files changed, 105 insertions(+) create mode 100644 src/posthtml/plugins/postcss/cleanupTailwindArtifacts.js diff --git a/src/posthtml/index.js b/src/posthtml/index.js index f68be2fb..37129400 100644 --- a/src/posthtml/index.js +++ b/src/posthtml/index.js @@ -19,6 +19,7 @@ import postcssCalc from 'postcss-calc' import cssVariables from 'postcss-css-variables' import postcssSafeParser from 'postcss-safe-parser' import removeDuplicateSelectors from './plugins/postcss/removeDuplicateSelectors.js' +import cleanupTailwindArtifacts from './plugins/postcss/cleanupTailwindArtifacts.js' import defaultComponentsConfig from './defaultComponentsConfig.js' @@ -38,6 +39,7 @@ export async function process(html = '', config = {}) { resolveCSSProps !== false && cssVariables(resolveCSSProps), resolveCalc !== false && postcssCalc(resolveCalc), removeDuplicateSelectors(), + cleanupTailwindArtifacts(get(config, 'css.cleanup', {})), ...get(config, 'postcss.plugins', []), ], merge( diff --git a/src/posthtml/plugins/postcss/cleanupTailwindArtifacts.js b/src/posthtml/plugins/postcss/cleanupTailwindArtifacts.js new file mode 100644 index 00000000..87323dc3 --- /dev/null +++ b/src/posthtml/plugins/postcss/cleanupTailwindArtifacts.js @@ -0,0 +1,78 @@ +/** + * PostCSS plugin to clean up Tailwind CSS artifacts and leftovers. + * + * This plugin safely removes unused Tailwind-specific CSS that + * may be left behind after processing, while preserving + * any CSS that might be intentionally used. + */ +export default function cleanupTailwindArtifacts(options = {}) { + const opts = { + removeEmptyLayers: true, + removeUnusedTwProperties: true, + removeEmptyRules: false, + preserveCustomProperties: [], // Array of custom property names to preserve + ...options + } + + return { + postcssPlugin: 'cleanup-tailwind-artifacts', + OnceExit(root) { + const usedCustomProperties = new Set() + const rulesToRemove = [] + + // First pass: collect all custom properties usage + root.walkDecls(decl => { + // Check if any declaration uses custom properties + if (decl.value.includes('var(--')) { + const matches = decl.value.match(/var\(--[\w-]+\)/g) + if (matches) { + matches.forEach(match => { + const propName = match.replace(/var\(|-|\)/g, '') + usedCustomProperties.add(propName) + }) + } + } + }) + + // Second pass: find unused @property declarations and empty @layer rules + root.walkAtRules(rule => { + // Handle @property declarations + if (rule.name === 'property' && opts.removeUnusedTwProperties) { + const propertyName = rule.params.replace(/^--/, '') + + // Only remove Tailwind-specific custom properties that aren't used + if (propertyName.startsWith('tw-') && !usedCustomProperties.has(propertyName)) { + // Check if it's in the preserve list + if (!opts.preserveCustomProperties.includes(propertyName)) { + rulesToRemove.push(rule) + } + } + } + + // Handle @layer rules + if (rule.name === 'layer' && opts.removeEmptyLayers) { + // Only remove @layer rules that have no nodes + if (!rule.nodes || rule.nodes.length === 0) { + rulesToRemove.push(rule) + } + } + }) + + // Third pass: remove empty rules (optional, off by default) + if (opts.removeEmptyRules) { + root.walkRules(rule => { + if (!rule.nodes || rule.nodes.length === 0) { + rulesToRemove.push(rule) + } + }) + } + + // Remove all identified artifacts + rulesToRemove.forEach(rule => { + rule.remove() + }) + } + } +} + +cleanupTailwindArtifacts.postcss = true diff --git a/test/postcss.test.js b/test/postcss.test.js index 29017528..50a8949a 100644 --- a/test/postcss.test.js +++ b/test/postcss.test.js @@ -125,4 +125,29 @@ describe.concurrent('PostCSS', () => { expect(cleanString(html)).toBe('') }) }) + + test('cleans up tailwind artifacts', async () => { + const html = ` + + ` + + posthtml(html, { + css: { + cleanup: { + removeEmptyLayers: true, + removeUnusedTwProperties: true, + preserveCustomProperties: ['--tw-color'], + }, + } + }).then(({ html }) => { + expect(cleanString(html)).toBe('') + }) + }) }) From a15c855f6f9dfa6490afcce40af7b8a46f704113 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Fri, 11 Jul 2025 20:21:06 +0300 Subject: [PATCH 010/100] chore: update changelog.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 538d1b0c..6795b59c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. 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.0.0-2] - 2025-07-11 + +### Fixed + +- fixed an issue where some Tailwind directives like `@layer` or `@property` were still present in the final build, even though they were not used + ## [6.0.0-1] - 2025-07-11 ### Added From b45a0b3894c952fc9f00d19dcc99bde90472f7c7 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Fri, 11 Jul 2025 20:24:22 +0300 Subject: [PATCH 011/100] chore: update changelog.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6795b59c..019c341c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Fixed +- fixed an issue with duplicate CSS selectors for utilities that cannot be disabled in Tailwind CSS v4, like `text-decoration` - fixed an issue where some Tailwind directives like `@layer` or `@property` were still present in the final build, even though they were not used ## [6.0.0-1] - 2025-07-11 From 2cb6c8acd55178ea56e79d28ba94dc9a4b42b1a2 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Fri, 11 Jul 2025 20:25:07 +0300 Subject: [PATCH 012/100] chore: update package-lock.json --- package-lock.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package-lock.json b/package-lock.json index 3d2e544b..46033365 100644 --- a/package-lock.json +++ b/package-lock.json @@ -337,6 +337,7 @@ }, "node_modules/@clack/prompts/node_modules/is-unicode-supported": { "version": "1.3.0", + "extraneous": true, "inBundle": true, "license": "MIT", "engines": { From d513317b21d7a2bbcc557ac7dd4f833a69b3babb Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Fri, 11 Jul 2025 20:25:44 +0300 Subject: [PATCH 013/100] 6.0.0-2 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 46033365..e9fceb20 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@maizzle/framework", - "version": "6.0.0-1", + "version": "6.0.0-2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@maizzle/framework", - "version": "6.0.0-1", + "version": "6.0.0-2", "license": "MIT", "dependencies": { "@maizzle/cli": "^2.0.0", diff --git a/package.json b/package.json index d953839b..34e6a09b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@maizzle/framework", - "version": "6.0.0-1", + "version": "6.0.0-2", "description": "Maizzle is a framework that helps you quickly build HTML emails with Tailwind CSS.", "license": "MIT", "type": "module", From 2f3429f183b44964c9c7ba7e9bd7120adf552617 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Sat, 12 Jul 2025 13:16:31 +0300 Subject: [PATCH 014/100] fix: preserve yahoo mail targeting selectors --- src/transformers/inline.js | 3 ++- src/transformers/safeClassNames.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/transformers/inline.js b/src/transformers/inline.js index c854cb46..e33fe490 100644 --- a/src/transformers/inline.js +++ b/src/transformers/inline.js @@ -37,6 +37,7 @@ export async function inline(html = '', options = {}) { '.apple', // Apple Mail '.ios', // Mail on iOS '.ox-', // Open-Xchange + '.yahoo', // Yahoo! Mail '.outlook', // Outlook.com '[data-ogs', // Outlook.com '.bloop_container', // Airmail @@ -46,7 +47,7 @@ export async function inline(html = '', options = {}) { '.mail-detail-content', // Comcast, Libero webmail 'edo', // Edison (all) '#msgBody', // Freenet uses #msgBody - '.lang' // Fenced code blocks + '.lang', // Fenced code blocks ], ]) diff --git a/src/transformers/safeClassNames.js b/src/transformers/safeClassNames.js index 2d3bb605..ff5cf876 100644 --- a/src/transformers/safeClassNames.js +++ b/src/transformers/safeClassNames.js @@ -12,7 +12,8 @@ export default function posthtmlPlugin(options = {}) { options = merge({ replacements: { '{': '{', - '}': '}' + '}': '}', + '&': '&', } }, options) From 967742179a0659c69dd0b28e68caf4f193874199 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Sat, 12 Jul 2025 13:23:56 +0300 Subject: [PATCH 015/100] fix: safelisting comcast targeting selector purging was removing this because the pattern was wrong --- src/transformers/purge.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transformers/purge.js b/src/transformers/purge.js index d8c9a864..fa24fced 100644 --- a/src/transformers/purge.js +++ b/src/transformers/purge.js @@ -19,7 +19,7 @@ const posthtmlPlugin = options => tree => { '.Singleton', // Apple Mail 10 '.unused', // Notes 8 '.moz-text-html', // Thunderbird - '.mail-detail-content', // Comcast, Libero webmail + '*mail-detail-content*', // Comcast, Libero webmail '*edo*', // Edison (all) '#*', // Freenet uses #msgBody '.lang*' // Fenced code blocks From 5cfd68f5dc7fa6e4a1aa7321a4bfd0c119cfc6d3 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Sat, 12 Jul 2025 13:57:44 +0300 Subject: [PATCH 016/100] feat: safelist notion mail targeting --- src/transformers/inline.js | 1 + src/transformers/purge.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/transformers/inline.js b/src/transformers/inline.js index e33fe490..bf47436d 100644 --- a/src/transformers/inline.js +++ b/src/transformers/inline.js @@ -45,6 +45,7 @@ export async function inline(html = '', options = {}) { '.unused', // Notes 8 '.moz-text-html', // Thunderbird '.mail-detail-content', // Comcast, Libero webmail + 'mail-content', // Notion 'edo', // Edison (all) '#msgBody', // Freenet uses #msgBody '.lang', // Fenced code blocks diff --git a/src/transformers/purge.js b/src/transformers/purge.js index fa24fced..a8ca2f4f 100644 --- a/src/transformers/purge.js +++ b/src/transformers/purge.js @@ -20,6 +20,7 @@ const posthtmlPlugin = options => tree => { '.unused', // Notes 8 '.moz-text-html', // Thunderbird '*mail-detail-content*', // Comcast, Libero webmail + '*mail-content-*', // Notion '*edo*', // Edison (all) '#*', // Freenet uses #msgBody '.lang*' // Fenced code blocks From b8b21f3c78328ac1d133798cb3e7cc46f4dc1bb7 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Sat, 12 Jul 2025 13:59:26 +0300 Subject: [PATCH 017/100] refactor: safelisting outlook targeting applies to outlook on mac and android --- src/transformers/inline.js | 2 +- src/transformers/purge.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/transformers/inline.js b/src/transformers/inline.js index bf47436d..d56afde5 100644 --- a/src/transformers/inline.js +++ b/src/transformers/inline.js @@ -38,7 +38,7 @@ export async function inline(html = '', options = {}) { '.ios', // Mail on iOS '.ox-', // Open-Xchange '.yahoo', // Yahoo! Mail - '.outlook', // Outlook.com + 'outlook', // Outlook Mac and Android '[data-ogs', // Outlook.com '.bloop_container', // Airmail '.Singleton', // Apple Mail 10 diff --git a/src/transformers/purge.js b/src/transformers/purge.js index a8ca2f4f..4abb298b 100644 --- a/src/transformers/purge.js +++ b/src/transformers/purge.js @@ -13,7 +13,7 @@ const posthtmlPlugin = options => tree => { '.apple*', // Apple Mail '.ios*', // Mail on iOS '.ox-*', // Open-Xchange - '.outlook*', // Outlook.com + '*outlook*', // Outlook Mac and Android '[data-ogs*', // Outlook.com '.bloop_container', // Airmail '.Singleton', // Apple Mail 10 From acb7b80f5fa7a917671790fb51cc3c4b32d82174 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Sat, 12 Jul 2025 14:04:56 +0300 Subject: [PATCH 018/100] refactor: safelisting selectors --- src/transformers/inline.js | 24 ++++++++++++------------ src/transformers/purge.js | 6 +++--- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/transformers/inline.js b/src/transformers/inline.js index d56afde5..f131d505 100644 --- a/src/transformers/inline.js +++ b/src/transformers/inline.js @@ -32,23 +32,23 @@ export async function inline(html = '', options = {}) { options.safelist = new Set([ ...get(options, 'safelist', []), ...[ - '.body', // Gmail - '.gmail', // Gmail - '.apple', // Apple Mail - '.ios', // Mail on iOS - '.ox-', // Open-Xchange - '.yahoo', // Yahoo! Mail + 'body', // Gmail + 'gmail', // Gmail + 'apple', // Apple Mail + 'ios', // Mail on iOS + 'ox-', // Open-Xchange + 'yahoo', // Yahoo! Mail 'outlook', // Outlook Mac and Android '[data-ogs', // Outlook.com - '.bloop_container', // Airmail - '.Singleton', // Apple Mail 10 - '.unused', // Notes 8 - '.moz-text-html', // Thunderbird - '.mail-detail-content', // Comcast, Libero webmail + 'bloop_container', // Airmail + 'Singleton', // Apple Mail 10 + 'unused', // Notes 8 + 'moz-text-html', // Thunderbird + 'mail-detail-content', // Comcast, Libero webmail 'mail-content', // Notion 'edo', // Edison (all) '#msgBody', // Freenet uses #msgBody - '.lang', // Fenced code blocks + 'lang', // Fenced code blocks ], ]) diff --git a/src/transformers/purge.js b/src/transformers/purge.js index 4abb298b..f8fd4a13 100644 --- a/src/transformers/purge.js +++ b/src/transformers/purge.js @@ -13,17 +13,17 @@ const posthtmlPlugin = options => tree => { '.apple*', // Apple Mail '.ios*', // Mail on iOS '.ox-*', // Open-Xchange - '*outlook*', // Outlook Mac and Android + '*outlook*', // Outlook.com '[data-ogs*', // Outlook.com '.bloop_container', // Airmail '.Singleton', // Apple Mail 10 '.unused', // Notes 8 - '.moz-text-html', // Thunderbird + '*moz-text-html*', // Thunderbird '*mail-detail-content*', // Comcast, Libero webmail '*mail-content-*', // Notion '*edo*', // Edison (all) '#*', // Freenet uses #msgBody - '.lang*' // Fenced code blocks + '.lang*', // Fenced code blocks ] const defaultOptions = { From beb6b41d121b08b02ddf627c6d62bef3b68aa555 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Sat, 12 Jul 2025 14:11:36 +0300 Subject: [PATCH 019/100] feat: safelist targeting for superhuman --- src/transformers/inline.js | 1 + src/transformers/purge.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/transformers/inline.js b/src/transformers/inline.js index f131d505..09ec4a3e 100644 --- a/src/transformers/inline.js +++ b/src/transformers/inline.js @@ -49,6 +49,7 @@ export async function inline(html = '', options = {}) { 'edo', // Edison (all) '#msgBody', // Freenet uses #msgBody 'lang', // Fenced code blocks + 'ShadowHTML', // Superhuman ], ]) diff --git a/src/transformers/purge.js b/src/transformers/purge.js index f8fd4a13..f33b1055 100644 --- a/src/transformers/purge.js +++ b/src/transformers/purge.js @@ -24,6 +24,7 @@ const posthtmlPlugin = options => tree => { '*edo*', // Edison (all) '#*', // Freenet uses #msgBody '.lang*', // Fenced code blocks + '*ShadowHTML*', // Superhuman ] const defaultOptions = { From f29efb8ac8419706b49d70687052e632107d7240 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Sat, 12 Jul 2025 14:19:42 +0300 Subject: [PATCH 020/100] feat: safelist spark targeting selectors --- src/transformers/inline.js | 1 + src/transformers/purge.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/transformers/inline.js b/src/transformers/inline.js index 09ec4a3e..197897b2 100644 --- a/src/transformers/inline.js +++ b/src/transformers/inline.js @@ -50,6 +50,7 @@ export async function inline(html = '', options = {}) { '#msgBody', // Freenet uses #msgBody 'lang', // Fenced code blocks 'ShadowHTML', // Superhuman + 'spark', // Spark ], ]) diff --git a/src/transformers/purge.js b/src/transformers/purge.js index f33b1055..9154ecdc 100644 --- a/src/transformers/purge.js +++ b/src/transformers/purge.js @@ -25,6 +25,7 @@ const posthtmlPlugin = options => tree => { '#*', // Freenet uses #msgBody '.lang*', // Fenced code blocks '*ShadowHTML*', // Superhuman + '*spark*', // Spark ] const defaultOptions = { From d6b9c4845be3af961c739b038839c9f3a48bd47d Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Sat, 12 Jul 2025 14:27:34 +0300 Subject: [PATCH 021/100] fix: purge safelisting patterns using wildcards for selector matching ensures we don't accidentally purge some of them --- src/transformers/purge.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/transformers/purge.js b/src/transformers/purge.js index 9154ecdc..a16cd6fe 100644 --- a/src/transformers/purge.js +++ b/src/transformers/purge.js @@ -9,21 +9,21 @@ import { getPosthtmlOptions } from '../posthtml/defaultConfig.js' const posthtmlPlugin = options => tree => { const defaultSafelist = [ '*body*', // Gmail - '.gmail*', // Gmail - '.apple*', // Apple Mail - '.ios*', // Mail on iOS - '.ox-*', // Open-Xchange + '*gmail*', // Gmail + '*apple*', // Apple Mail + '*ios*', // Mail on iOS + '*ox-*', // Open-Xchange '*outlook*', // Outlook.com '[data-ogs*', // Outlook.com - '.bloop_container', // Airmail - '.Singleton', // Apple Mail 10 - '.unused', // Notes 8 + '*bloop_container*', // Airmail + '*Singleton*', // Apple Mail 10 + '*unused', // Notes 8 '*moz-text-html*', // Thunderbird '*mail-detail-content*', // Comcast, Libero webmail '*mail-content-*', // Notion '*edo*', // Edison (all) '#*', // Freenet uses #msgBody - '.lang*', // Fenced code blocks + '*lang*', // Fenced code blocks '*ShadowHTML*', // Superhuman '*spark*', // Spark ] From b828c44c57b938988574bbc1f86328f55dacfd44 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Sat, 12 Jul 2025 16:28:57 +0300 Subject: [PATCH 022/100] fix: safelist class names for container queries --- src/transformers/inline.js | 1 + src/transformers/purge.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/transformers/inline.js b/src/transformers/inline.js index 197897b2..f13efd4c 100644 --- a/src/transformers/inline.js +++ b/src/transformers/inline.js @@ -51,6 +51,7 @@ export async function inline(html = '', options = {}) { 'lang', // Fenced code blocks 'ShadowHTML', // Superhuman 'spark', // Spark + 'at-', // Safe class names for container queries ], ]) diff --git a/src/transformers/purge.js b/src/transformers/purge.js index a16cd6fe..7e832cf3 100644 --- a/src/transformers/purge.js +++ b/src/transformers/purge.js @@ -26,6 +26,7 @@ const posthtmlPlugin = options => tree => { '*lang*', // Fenced code blocks '*ShadowHTML*', // Superhuman '*spark*', // Spark + '*at-*', // Safe class names for container queries ] const defaultOptions = { From 7aef1b1212bc4f4def118ab68110a3cff3cbd17c Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Sat, 12 Jul 2025 20:01:42 +0300 Subject: [PATCH 023/100] 6.0.0-3 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e9fceb20..d78e52d5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@maizzle/framework", - "version": "6.0.0-2", + "version": "6.0.0-3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@maizzle/framework", - "version": "6.0.0-2", + "version": "6.0.0-3", "license": "MIT", "dependencies": { "@maizzle/cli": "^2.0.0", diff --git a/package.json b/package.json index 34e6a09b..9aa6ecb0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@maizzle/framework", - "version": "6.0.0-2", + "version": "6.0.0-3", "description": "Maizzle is a framework that helps you quickly build HTML emails with Tailwind CSS.", "license": "MIT", "type": "module", From 52f31f7a3b5369c35ea6bf7463a16675a28c56d2 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Mon, 14 Jul 2025 19:36:56 +0300 Subject: [PATCH 024/100] refactor: css compilation consolidates css compilation into a single, custom posthtml plugin, dropping the use of posthtml-postcss also introduces custom attributes for skipping the compilation of a style tag, such as `raw` or `plain` --- src/posthtml/index.js | 50 +------- src/posthtml/plugins/lowerCssSyntax.js | 43 ------- src/posthtml/plugins/postcss/compileCss.js | 137 +++++++++++++++++++++ test/postcss.test.js | 15 +++ 4 files changed, 158 insertions(+), 87 deletions(-) delete mode 100644 src/posthtml/plugins/lowerCssSyntax.js create mode 100644 src/posthtml/plugins/postcss/compileCss.js diff --git a/src/posthtml/index.js b/src/posthtml/index.js index 37129400..1c819a66 100644 --- a/src/posthtml/index.js +++ b/src/posthtml/index.js @@ -6,51 +6,16 @@ import posthtml from 'posthtml' import posthtmlFetch from 'posthtml-fetch' import envTags from './plugins/envTags.js' import components from 'posthtml-component' -import posthtmlPostcss from 'posthtml-postcss' import expandLinkTag from './plugins/expandLinkTag.js' import envAttributes from './plugins/envAttributes.js' import { getPosthtmlOptions } from './defaultConfig.js' -import lowerCssSyntax from './plugins/lowerCssSyntax.js' import combineMediaQueries from './plugins/combineMediaQueries.js' +import defaultComponentsConfig from './defaultComponentsConfig.js' // PostCSS -import tailwindcss from '@tailwindcss/postcss' -import postcssCalc from 'postcss-calc' -import cssVariables from 'postcss-css-variables' -import postcssSafeParser from 'postcss-safe-parser' -import removeDuplicateSelectors from './plugins/postcss/removeDuplicateSelectors.js' -import cleanupTailwindArtifacts from './plugins/postcss/cleanupTailwindArtifacts.js' - -import defaultComponentsConfig from './defaultComponentsConfig.js' +import compileCss from './plugins/postcss/compileCss.js' export async function process(html = '', config = {}) { - /** - * Configure PostCSS pipeline. Plugins defined and added here - * will apply to all `') }) }) + + test('skips processing marked style tags', async () => { + const html = ` + + ` + + posthtml(html) + .then(({ html }) => { + expect(cleanString(html)).toBe('') + }) + }) }) From 98c831e9e788fa537a3a14cfa7f604233a5252e1 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Mon, 14 Jul 2025 19:42:52 +0300 Subject: [PATCH 025/100] chore: remove posthtml-postcss --- package-lock.json | 70 ++--------------------------------------------- package.json | 1 - 2 files changed, 2 insertions(+), 69 deletions(-) diff --git a/package-lock.json b/package-lock.json index d78e52d5..5ea460a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -45,7 +45,6 @@ "posthtml-markdownit": "^3.1.0", "posthtml-mso": "^3.1.0", "posthtml-parser": "^0.12.1", - "posthtml-postcss": "^1.0.2", "posthtml-postcss-merge-longhand": "^3.1.2", "posthtml-render": "^3.0.0", "posthtml-safe-class-names": "^4.1.0", @@ -3858,6 +3857,7 @@ "version": "1.21.7", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, "license": "MIT", "optional": true, "peer": true, @@ -4172,18 +4172,6 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, "node_modules/linkify-it": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", @@ -4946,48 +4934,6 @@ "node": ">=0.8.0" } }, - "node_modules/postcss-load-config": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", - "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "lilconfig": "^3.1.1" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "jiti": ">=1.21.0", - "postcss": ">=8.0.9", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - }, - "postcss": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, "node_modules/postcss-merge-longhand": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-7.0.4.tgz", @@ -5334,19 +5280,6 @@ "node": ">=16" } }, - "node_modules/posthtml-postcss": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/posthtml-postcss/-/posthtml-postcss-1.0.4.tgz", - "integrity": "sha512-bG6pw+6WYxNhIkSqDGpJCZw+1OSqdPFFbW2aUtkB/pE4dPmTtlltzqPyEluj2gc3jgstW9dbxKj0rUq8WMPgtA==", - "license": "MIT", - "dependencies": { - "postcss": "^8.4.35", - "postcss-load-config": "^6.0.1" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/posthtml-postcss-merge-longhand": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/posthtml-postcss-merge-longhand/-/posthtml-postcss-merge-longhand-3.1.4.tgz", @@ -7325,6 +7258,7 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "dev": true, "license": "ISC", "optional": true, "peer": true, diff --git a/package.json b/package.json index 9aa6ecb0..607ca8b4 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,6 @@ "posthtml-markdownit": "^3.1.0", "posthtml-mso": "^3.1.0", "posthtml-parser": "^0.12.1", - "posthtml-postcss": "^1.0.2", "posthtml-postcss-merge-longhand": "^3.1.2", "posthtml-render": "^3.0.0", "posthtml-safe-class-names": "^4.1.0", From b5caec76c9bc62b3483417756907e870a3e2ba0f Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Mon, 14 Jul 2025 19:54:23 +0300 Subject: [PATCH 026/100] chore: update changelog.md --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 019c341c..3e529553 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file. 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.0.0-3] - 2025-07-14 + +### Added + +- added support for skipping CSS compilation on individual ` - ` - - posthtml(html, { - css: { - cleanup: { - removeEmptyLayers: true, - removeUnusedTwProperties: true, - preserveCustomProperties: ['--tw-color'], - }, - } - }).then(({ html }) => { - expect(cleanString(html)).toBe('') - }) - }) - test('skips processing marked style tags', async () => { const html = ` -

test

`, { - removeInlinedSelectors: true, - excludedProperties: ['margin'] - }) +

test

`, + { + removeInlinedSelectors: true, + excludedProperties: ['margin'] + } + ) ) ).toBe(cleanString(` @@ -167,11 +169,24 @@ describe.concurrent('Inline CSS', () => { }) test('Uses `applyWidthAttributes` and `applyHeightAttributes` by default', async () => { - expect( - await useTransformers('', { - css: { inline: { removeInlinedSelectors: true } }, - }).then(({ html }) => html) - ).toBe('') + await useTransformers( + ` + + + `, + { + css: { + inline: { + removeInlinedSelectors: true + } + }, + } + ).then(({ html }) => + expect(cleanString(html)).toBe(cleanString(` + + `) + ) + ) }) test('Does not inline

test

`, + { + removeInlinedSelectors: true, + } ) ) ).toBe(cleanString(` diff --git a/test/transformers/preferAttributeSizes.test.js b/test/transformers/preferAttributeSizes.test.js index 658fe142..b013ad31 100644 --- a/test/transformers/preferAttributeSizes.test.js +++ b/test/transformers/preferAttributeSizes.test.js @@ -35,6 +35,6 @@ describe.concurrent('Prefer attribute sizes', () => { } }) .then(({ html }) => html) - ).toBe('') + ).toBe('') }) }) From 4dcc4a682a8c0189cb7f7a0a3813c16c0b7a8603 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Wed, 16 Jul 2025 19:04:49 +0300 Subject: [PATCH 038/100] chore: clean up comments --- src/posthtml/plugins/postcss/compileCss.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/posthtml/plugins/postcss/compileCss.js b/src/posthtml/plugins/postcss/compileCss.js index 4fa92372..4585a90f 100644 --- a/src/posthtml/plugins/postcss/compileCss.js +++ b/src/posthtml/plugins/postcss/compileCss.js @@ -86,8 +86,6 @@ async function processCss(css, config) { tailwindcss(get(config, 'css.tailwind', {})), resolveCSSProps !== false && cssVariables(resolveCSSProps), resolveCalc !== false && postcssCalc(resolveCalc), - // removeDuplicateSelectors(), - // cleanupTailwindArtifacts(get(config, 'css.cleanup', {})), ...get(config, 'postcss.plugins', []), ].filter(Boolean)) From f142d504383e46d8b4ba03fd08ab2b6c2f32bb84 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Wed, 16 Jul 2025 19:48:08 +0300 Subject: [PATCH 039/100] chore: update changelog.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc3e6342..dd486f85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. 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.0.0-8] - 2025-07-16 + +### Changed + +- refactor: css inlining d443c31 + ## [6.0.0-7] - 2025-07-15 This release integrates the `v5.2.2` patch release, which fixes an issue where both local and production `build.content` paths were used when building for production. When specified in a `config.{env}.js` file, build.content must not be merged with the one in the base `config.js`. From ddebda00032eca6f01dd2e911d48d8b001e016f0 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Wed, 16 Jul 2025 19:49:30 +0300 Subject: [PATCH 040/100] chore: update package.lock --- package-lock.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2c32fcd3..00c1edb9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2779,9 +2779,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.183", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.183.tgz", - "integrity": "sha512-vCrDBYjQCAEefWGjlK3EpoSKfKbT10pR4XXPdn65q7snuNOZnthoVpBfZPykmDapOKfoD+MMIPG8ZjKyyc9oHA==", + "version": "1.5.185", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.185.tgz", + "integrity": "sha512-dYOZfUk57hSMPePoIQ1fZWl1Fkj+OshhEVuPacNKWzC1efe56OsHY3l/jCfiAgIICOU3VgOIdoq7ahg7r7n6MQ==", "license": "ISC" }, "node_modules/email-comb": { @@ -6560,9 +6560,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { @@ -6855,9 +6855,9 @@ } }, "node_modules/vite/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { @@ -6941,9 +6941,9 @@ } }, "node_modules/vitest/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { From 466d2e8d99bb6a04d74984528459c4272b8ac4b2 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Wed, 16 Jul 2025 19:49:55 +0300 Subject: [PATCH 041/100] 6.0.0-8 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 00c1edb9..f1d338b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@maizzle/framework", - "version": "6.0.0-7", + "version": "6.0.0-8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@maizzle/framework", - "version": "6.0.0-7", + "version": "6.0.0-8", "license": "MIT", "dependencies": { "@maizzle/cli": "^2.0.0", diff --git a/package.json b/package.json index e6350ffc..8f762953 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@maizzle/framework", - "version": "6.0.0-7", + "version": "6.0.0-8", "description": "Maizzle is a framework that helps you quickly build HTML emails with Tailwind CSS.", "license": "MIT", "type": "module", From c5a5c64bf2d69e47498d7131edf26e387acacc14 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Wed, 16 Jul 2025 22:22:08 +0300 Subject: [PATCH 042/100] refactor: resolving css props switch to postcss-custom-properties, as postcss-css-variables is tripping on some tailwind selectors; this also removes the need for postcss-calc --- package-lock.json | 145 +++++++++++++----- package.json | 3 +- src/posthtml/plugins/combineMediaQueries.js | 2 +- src/posthtml/plugins/postcss/compileCss.js | 42 +++-- .../plugins/removeRawStyleAttributes.js | 2 +- test/postcss.test.js | 54 ++----- 6 files changed, 139 insertions(+), 109 deletions(-) diff --git a/package-lock.json b/package-lock.json index f1d338b6..3a580c12 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,8 +30,7 @@ "ora": "^8.1.0", "pathe": "^2.0.0", "postcss": "^8.4.49", - "postcss-calc": "^10.0.2", - "postcss-css-variables": "^0.19.0", + "postcss-custom-properties": "^14.0.6", "postcss-safe-parser": "^7.0.0", "postcss-sort-media-queries": "^5.2.0", "posthtml": "^0.16.6", @@ -364,6 +363,92 @@ "node": ">=0.1.90" } }, + "node_modules/@csstools/cascade-layer-name-parser": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-2.0.5.tgz", + "integrity": "sha512-p1ko5eHgV+MgXFVa4STPKpvPxr6ReS8oS2jzTukjR74i5zJNyWO1ZM1m8YKBXnzDKWfBN1ztLYlHxbVemDD88A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/utilities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@csstools/utilities/-/utilities-2.0.0.tgz", + "integrity": "sha512-5VdOr0Z71u+Yp3ozOx8T11N703wIFGVRgOWbOZMKgglPJsWA54MRIoMNVMa7shUToIhx5J8vX4sOZgD2XiihiQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.6", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz", @@ -3097,12 +3182,6 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" - }, "node_modules/extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", @@ -4892,43 +4971,33 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/postcss-calc": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-10.1.1.tgz", - "integrity": "sha512-NYEsLHh8DgG/PRH2+G9BTuUdtf9ViS+vdoQ0YA5OQdGsfN4ztiwtDWNtBl9EKeqNMFnIu8IKZ0cLxEQ5r5KVMw==", + "node_modules/postcss-custom-properties": { + "version": "14.0.6", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-14.0.6.tgz", + "integrity": "sha512-fTYSp3xuk4BUeVhxCSJdIPhDLpJfNakZKoiTDx7yRGCdlZrSJR7mWKVOBS4sBF+5poPQFMj2YdXx1VHItBGihQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], "license": "MIT", "dependencies": { - "postcss-selector-parser": "^7.0.0", + "@csstools/cascade-layer-name-parser": "^2.0.5", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/utilities": "^2.0.0", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^18.12 || ^20.9 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.38" - } - }, - "node_modules/postcss-css-variables": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/postcss-css-variables/-/postcss-css-variables-0.19.0.tgz", - "integrity": "sha512-Hr0WEYKLK9VCrY15anHXOd4RCvJy/xRvCnWdplGBeLInwEj6Z14hgzTb2W/39dYTCnS8hnHUfU4/F1zxX0IZuQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "escape-string-regexp": "^1.0.3", - "extend": "^3.0.1" + "node": ">=18" }, "peerDependencies": { - "postcss": "^8.2.6" - } - }, - "node_modules/postcss-css-variables/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "license": "MIT", - "engines": { - "node": ">=0.8.0" + "postcss": "^8.4" } }, "node_modules/postcss-merge-longhand": { diff --git a/package.json b/package.json index 8f762953..bbc9236a 100644 --- a/package.json +++ b/package.json @@ -71,8 +71,7 @@ "ora": "^8.1.0", "pathe": "^2.0.0", "postcss": "^8.4.49", - "postcss-calc": "^10.0.2", - "postcss-css-variables": "^0.19.0", + "postcss-custom-properties": "^14.0.6", "postcss-safe-parser": "^7.0.0", "postcss-sort-media-queries": "^5.2.0", "posthtml": "^0.16.6", diff --git a/src/posthtml/plugins/combineMediaQueries.js b/src/posthtml/plugins/combineMediaQueries.js index cdb4ede1..e6904d98 100644 --- a/src/posthtml/plugins/combineMediaQueries.js +++ b/src/posthtml/plugins/combineMediaQueries.js @@ -4,7 +4,7 @@ import sortMediaQueries from 'postcss-sort-media-queries' const plugin = (options = {}) => tree => { const process = node => { // Check if this is a style tag with content - if (node.tag === 'style' && node.content && Array.isArray(node.content)) { + if (node && node.tag === 'style' && node.content && Array.isArray(node.content)) { // Get the CSS content from the style tag const cssContent = node.content.join('') diff --git a/src/posthtml/plugins/postcss/compileCss.js b/src/posthtml/plugins/postcss/compileCss.js index 4585a90f..098e1d17 100644 --- a/src/posthtml/plugins/postcss/compileCss.js +++ b/src/posthtml/plugins/postcss/compileCss.js @@ -1,11 +1,10 @@ import postcss from 'postcss' import get from 'lodash-es/get.js' import { defu as merge } from 'defu' -import postcssCalc from 'postcss-calc' import { transform } from 'lightningcss' import tailwindcss from '@tailwindcss/postcss' -import cssVariables from 'postcss-css-variables' import postcssSafeParser from 'postcss-safe-parser' +import customProperties from 'postcss-custom-properties' const attributes = new Set([ 'raw', @@ -31,7 +30,7 @@ export function compileCss(config = {}) { const stylePromises = [] tree.walk(node => { - if (node.tag === 'style' && node.content) { + if (node && node.tag === 'style' && node.content) { if (node.attrs && Object.keys(node.attrs).some(attr => attributes.has(attr))) { return node } @@ -67,29 +66,26 @@ async function processCss(css, config) { * will apply to all ` -

test

- `).then(({ html }) => { - expect(cleanString(html)).toBe(`

test

`) - }) + ` - // Passing options - posthtml(` - -

test

- `, { + // Default: resolves CSS variables + posthtml(input, { css: { - resolveProps: { - variables: { - '--font-weight': 'bold', - } - }, + lightning: false, } }).then(({ html }) => { - expect(cleanString(html)).toBe(`

test

`) + expect(cleanString(html)).toBe(``) }) // Disabling `resolveProps` - posthtml(` - -

test

- `, { + posthtml(input, { css: { resolveProps: false, } }).then(({ html }) => { - expect(cleanString(html)).toBe(`

test

`) + expect(cleanString(html)).toBe(``) }) }) @@ -71,18 +47,8 @@ describe.concurrent('PostCSS', () => { posthtml(html) .then(({ html }) => { - expect(cleanString(html)).toBe('') + expect(cleanString(html)).toBe('') }) - - posthtml(html, { - css: { - resolveCalc: { - precision: 1, - }, - } - }).then(({ html }) => { - expect(cleanString(html)).toBe('') - }) }) test('functional color notation', async () => { From bc48638aa36473d0db31f00a0c8989303b8e9718 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Wed, 16 Jul 2025 22:27:20 +0300 Subject: [PATCH 043/100] chore: update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd486f85..3be07ec4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. 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.0.0-9] - 2025-07-16 + +### Changed + +- refactor: resolving css props acacc14 + ## [6.0.0-8] - 2025-07-16 ### Changed From 62d7a5cbebe24cbbfaa2e55394059c175f64a3d5 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Wed, 16 Jul 2025 22:28:58 +0300 Subject: [PATCH 044/100] 6.0.0-9 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3a580c12..29ce2d2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@maizzle/framework", - "version": "6.0.0-8", + "version": "6.0.0-9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@maizzle/framework", - "version": "6.0.0-8", + "version": "6.0.0-9", "license": "MIT", "dependencies": { "@maizzle/cli": "^2.0.0", diff --git a/package.json b/package.json index bbc9236a..bb74b144 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@maizzle/framework", - "version": "6.0.0-8", + "version": "6.0.0-9", "description": "Maizzle is a framework that helps you quickly build HTML emails with Tailwind CSS.", "license": "MIT", "type": "module", From 8673a1ee9e6d10b2ca837292f1f2f99ceb636c97 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Wed, 16 Jul 2025 22:32:39 +0300 Subject: [PATCH 045/100] fix: resolve props default options typo --- CHANGELOG.md | 6 ++++++ src/posthtml/plugins/postcss/compileCss.js | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3be07ec4..a36eb593 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. 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.0.0-10] - 2025-07-16 + +### Fixed + + + ## [6.0.0-9] - 2025-07-16 ### Changed diff --git a/src/posthtml/plugins/postcss/compileCss.js b/src/posthtml/plugins/postcss/compileCss.js index 098e1d17..351f3f40 100644 --- a/src/posthtml/plugins/postcss/compileCss.js +++ b/src/posthtml/plugins/postcss/compileCss.js @@ -66,7 +66,7 @@ async function processCss(css, config) { * will apply to all ``) + expect(cleanString(html)).toBe(``) }) // Disabling `resolveProps` From aca21549486640e05031e89479b5e7ce57e54c98 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Wed, 16 Jul 2025 22:38:05 +0300 Subject: [PATCH 048/100] 6.0.0-10 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 29ce2d2d..894a3ed4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@maizzle/framework", - "version": "6.0.0-9", + "version": "6.0.0-10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@maizzle/framework", - "version": "6.0.0-9", + "version": "6.0.0-10", "license": "MIT", "dependencies": { "@maizzle/cli": "^2.0.0", diff --git a/package.json b/package.json index bb74b144..3ef63ebc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@maizzle/framework", - "version": "6.0.0-9", + "version": "6.0.0-10", "description": "Maizzle is a framework that helps you quickly build HTML emails with Tailwind CSS.", "license": "MIT", "type": "module", From f0a13508a446e6c97da341215e50b9ba95d78370 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Wed, 16 Jul 2025 23:08:01 +0300 Subject: [PATCH 049/100] fix: return posthtml tree from inliner was returning string, preventing subsequent transformers from working --- src/transformers/inline.js | 2 +- test/transformers/attributeToStyle.test.js | 4 ++-- test/transformers/inlineCSS.test.js | 2 +- test/transformers/preferAttributeSizes.test.js | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/transformers/inline.js b/src/transformers/inline.js index 78291bd1..2ff9309b 100644 --- a/src/transformers/inline.js +++ b/src/transformers/inline.js @@ -19,7 +19,7 @@ import { getPosthtmlOptions } from '../posthtml/defaultConfig.js' * @returns {Function} PostHTML tree */ export default (options = {}) => tree => { - return inline(render(tree), options) + return inline(render(tree), options).then(html => parse(html, getPosthtmlOptions())) } /** diff --git a/test/transformers/attributeToStyle.test.js b/test/transformers/attributeToStyle.test.js index 9b8f1d85..0f2c094c 100644 --- a/test/transformers/attributeToStyle.test.js +++ b/test/transformers/attributeToStyle.test.js @@ -23,9 +23,9 @@ describe.concurrent('Attribute to style', () => { attributes: { add: false }, css: { inline: { attributeToStyle: ['width', 'height'] } }, }).then(({ html }) => html) - ).toBe(` + ).toBe(`
- +
`) }) diff --git a/test/transformers/inlineCSS.test.js b/test/transformers/inlineCSS.test.js index b0c6a19f..74171cdd 100644 --- a/test/transformers/inlineCSS.test.js +++ b/test/transformers/inlineCSS.test.js @@ -184,7 +184,7 @@ describe.concurrent('Inline CSS', () => { ).then(({ html }) => expect(cleanString(html)).toBe(cleanString(` - `) + `) ) ) }) diff --git a/test/transformers/preferAttributeSizes.test.js b/test/transformers/preferAttributeSizes.test.js index b013ad31..658fe142 100644 --- a/test/transformers/preferAttributeSizes.test.js +++ b/test/transformers/preferAttributeSizes.test.js @@ -35,6 +35,6 @@ describe.concurrent('Prefer attribute sizes', () => { } }) .then(({ html }) => html) - ).toBe('') + ).toBe('') }) }) From 1fdc9150e9055d47ca942dba982a99c6df7f7604 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Wed, 16 Jul 2025 23:08:59 +0300 Subject: [PATCH 050/100] chore: update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c49ec9f7..c5f2823c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. 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.0.0-11] - 2025-07-16 + +### Fixed + +- fix: return posthtml tree from inliner 5d78370 + ## [6.0.0-10] - 2025-07-16 ### Fixed From 14cc1da186ac5bcc4f08fd18862575c393cf5878 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Wed, 16 Jul 2025 23:09:36 +0300 Subject: [PATCH 051/100] 6.0.0-11 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 894a3ed4..b1e6e5f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@maizzle/framework", - "version": "6.0.0-10", + "version": "6.0.0-11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@maizzle/framework", - "version": "6.0.0-10", + "version": "6.0.0-11", "license": "MIT", "dependencies": { "@maizzle/cli": "^2.0.0", diff --git a/package.json b/package.json index 3ef63ebc..cf695ab8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@maizzle/framework", - "version": "6.0.0-10", + "version": "6.0.0-11", "description": "Maizzle is a framework that helps you quickly build HTML emails with Tailwind CSS.", "license": "MIT", "type": "module", From b4d02f162d9cf6f8d9d6b46353b4f57014ef58e0 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Thu, 17 Jul 2025 00:20:50 +0300 Subject: [PATCH 052/100] fix: @container class safelisting --- CHANGELOG.md | 4 ++++ src/transformers/inline.js | 2 +- src/transformers/purge.js | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5f2823c..9c66ec8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. 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.0.0-12] - 2025-07-16 + +### Fixed + ## [6.0.0-11] - 2025-07-16 ### Fixed diff --git a/src/transformers/inline.js b/src/transformers/inline.js index 2ff9309b..8d4a4fd1 100644 --- a/src/transformers/inline.js +++ b/src/transformers/inline.js @@ -62,7 +62,7 @@ export async function inline(html = '', options = {}) { 'lang', // Fenced code blocks 'ShadowHTML', // Superhuman 'spark', // Spark - 'at-', // Safe class names for container queries + '.at-', // Safe class names for container queries ], ]) diff --git a/src/transformers/purge.js b/src/transformers/purge.js index 7e832cf3..b2820695 100644 --- a/src/transformers/purge.js +++ b/src/transformers/purge.js @@ -26,7 +26,7 @@ const posthtmlPlugin = options => tree => { '*lang*', // Fenced code blocks '*ShadowHTML*', // Superhuman '*spark*', // Spark - '*at-*', // Safe class names for container queries + '.at-*', // Safe class names for container queries ] const defaultOptions = { From 9e7e74b62c32cf8f89bd75a709759449934a3d05 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Thu, 17 Jul 2025 00:21:21 +0300 Subject: [PATCH 053/100] 6.0.0-12 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b1e6e5f5..fb07cd92 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@maizzle/framework", - "version": "6.0.0-11", + "version": "6.0.0-12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@maizzle/framework", - "version": "6.0.0-11", + "version": "6.0.0-12", "license": "MIT", "dependencies": { "@maizzle/cli": "^2.0.0", diff --git a/package.json b/package.json index cf695ab8..6f572a57 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@maizzle/framework", - "version": "6.0.0-11", + "version": "6.0.0-12", "description": "Maizzle is a framework that helps you quickly build HTML emails with Tailwind CSS.", "license": "MIT", "type": "module", From b9fb09d431e5121e01e2636e40016c7c77813e92 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Thu, 17 Jul 2025 00:22:24 +0300 Subject: [PATCH 054/100] chore: update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c66ec8f..e5b85203 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Fixed +- fix: @​container class safelisting b4d02f1 + ## [6.0.0-11] - 2025-07-16 ### Fixed From ae9ff309a53c3ab67e5f18a225810cf98a5916ec Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Thu, 17 Jul 2025 12:04:43 +0300 Subject: [PATCH 055/100] fix: default posthtml options for the prettify transformer --- src/transformers/prettify.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/transformers/prettify.js b/src/transformers/prettify.js index 3f5e0a51..7c794d19 100644 --- a/src/transformers/prettify.js +++ b/src/transformers/prettify.js @@ -21,6 +21,8 @@ const posthtmlPlugin = (options = {}) => tree => { export default posthtmlPlugin export async function prettify(html = '', options = {}, posthtmlOptions = {}) { + posthtmlOptions = merge(posthtmlOptions, getPosthtmlOptions()) + return posthtml([ posthtmlPlugin(options) ]) From 4f1680180cabedfbdee6624bcad6e4bfc173aa3f Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Thu, 17 Jul 2025 12:13:00 +0300 Subject: [PATCH 056/100] refactor: cache posthtml options in inliner --- src/transformers/inline.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/transformers/inline.js b/src/transformers/inline.js index 8d4a4fd1..2ff955e2 100644 --- a/src/transformers/inline.js +++ b/src/transformers/inline.js @@ -35,6 +35,8 @@ export async function inline(html = '', options = {}) { return html } + const posthtmlOptions = getPosthtmlOptions() + const removeStyleTags = get(options, 'removeStyleTags', false) const css = get(options, 'customCSS', false) @@ -85,7 +87,7 @@ export async function inline(html = '', options = {}) { * If customCSS is passed, inline that CSS specifically * Otherwise, use Juice's default inlining */ - const tree = parse(html, getPosthtmlOptions()) + const tree = parse(html, posthtmlOptions) tree.match = match /** @@ -127,7 +129,7 @@ export async function inline(html = '', options = {}) { * Remove inlined selectors from the HTML * */ - const inlined_tree = parse(inlined_html, getPosthtmlOptions()) + const inlined_tree = parse(inlined_html, posthtmlOptions) inlined_tree.match = match const preservedAtRules = get(options, 'preservedAtRules', ['media']) @@ -286,7 +288,7 @@ export async function inline(html = '', options = {}) { } catch { } }) - const optimized_tree = parse($.html(), getPosthtmlOptions()) + const optimized_tree = parse($.html(), posthtmlOptions) optimized_tree.match = match /** From e3e1b98d70f6b335e402a218497d469692fa7e1c Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Thu, 17 Jul 2025 14:42:44 +0300 Subject: [PATCH 057/100] fix: ensure user posthtml options are used by all transformers --- src/transformers/addAttributes.js | 3 ++- src/transformers/attributeToStyle.js | 3 ++- src/transformers/baseUrl.js | 12 +++++------ src/transformers/filters/index.js | 3 ++- src/transformers/index.js | 31 +++++++++++++++------------ src/transformers/inline.js | 4 ++-- src/transformers/markdown.js | 22 +++++++++++++------ src/transformers/minify.js | 9 ++++---- src/transformers/posthtmlMso.js | 3 ++- src/transformers/prettify.js | 8 +++---- src/transformers/preventWidows.js | 5 +++-- src/transformers/purge.js | 9 ++++---- src/transformers/removeAttributes.js | 6 ++++-- src/transformers/replaceStrings.js | 2 +- src/transformers/safeClassNames.js | 3 ++- src/transformers/shorthandCss.js | 3 ++- src/transformers/sixHex.js | 3 ++- src/transformers/urlParameters.js | 3 ++- src/transformers/useAttributeSizes.js | 3 ++- 19 files changed, 81 insertions(+), 54 deletions(-) diff --git a/src/transformers/addAttributes.js b/src/transformers/addAttributes.js index a2e52578..21dc6700 100644 --- a/src/transformers/addAttributes.js +++ b/src/transformers/addAttributes.js @@ -1,6 +1,7 @@ import posthtml from 'posthtml' import { defu as merge } from 'defu' import addAttributesPlugin from 'posthtml-extra-attributes' +import { getPosthtmlOptions } from '../posthtml/defaultConfig.js' export default function posthtmlPlugin(attributes = {}) { const defaultAttributes = { @@ -24,6 +25,6 @@ export async function addAttributes(html = '', attributes = {}, posthtmlOptions return posthtml([ posthtmlPlugin(attributes) ]) - .process(html, posthtmlOptions) + .process(html, getPosthtmlOptions(posthtmlOptions)) .then(result => result.html) } diff --git a/src/transformers/attributeToStyle.js b/src/transformers/attributeToStyle.js index e527ba47..8b778698 100644 --- a/src/transformers/attributeToStyle.js +++ b/src/transformers/attributeToStyle.js @@ -4,6 +4,7 @@ import keys from 'lodash-es/keys.js' import forEach from 'lodash-es/forEach.js' import parseAttrs from 'posthtml-attrs-parser' import intersection from 'lodash-es/intersection.js' +import { getPosthtmlOptions } from '../posthtml/defaultConfig.js' const posthtmlPlugin = (attributes = []) => tree => { if (!Array.isArray(attributes)) { @@ -85,6 +86,6 @@ export async function attributeToStyle(html = '', attributes = [], posthtmlOptio return posthtml([ posthtmlPlugin(attributes) ]) - .process(html, posthtmlOptions) + .process(html, getPosthtmlOptions(posthtmlOptions)) .then(result => result.html) } diff --git a/src/transformers/baseUrl.js b/src/transformers/baseUrl.js index eb4f4d0b..a4160769 100644 --- a/src/transformers/baseUrl.js +++ b/src/transformers/baseUrl.js @@ -8,9 +8,7 @@ import { parser as parse } from 'posthtml-parser' import { getPosthtmlOptions } from '../posthtml/defaultConfig.js' import baseUrl, { parseSrcset, stringifySrcset, defaultTags } from 'posthtml-base-url' -const posthtmlOptions = getPosthtmlOptions() - -const posthtmlPlugin = url => tree => { +const posthtmlPlugin = (url, posthtmlOptions = {}) => tree => { // Handle `baseURL` as a string if (typeof url === 'string' && url.length > 0) { const html = rewriteVMLs(render(tree), url) @@ -48,11 +46,13 @@ const posthtmlPlugin = url => tree => { export default posthtmlPlugin -export async function addBaseUrl(html = '', options = {}, posthtmlOpts = {}) { +export async function addBaseUrl(html = '', options = {}, posthtmlOptions = {}) { + posthtmlOptions = getPosthtmlOptions(posthtmlOptions) + return posthtml([ - posthtmlPlugin(options) + posthtmlPlugin(options, posthtmlOptions) ]) - .process(html, getPosthtmlOptions(posthtmlOpts)) + .process(html, posthtmlOptions) .then(result => result.html) } diff --git a/src/transformers/filters/index.js b/src/transformers/filters/index.js index 4ce1beb9..5036953c 100644 --- a/src/transformers/filters/index.js +++ b/src/transformers/filters/index.js @@ -2,6 +2,7 @@ import posthtml from 'posthtml' import { defu as merge } from 'defu' import posthtmlContent from 'posthtml-content' import { filters as defaultFilters } from './defaultFilters.js' +import { getPosthtmlOptions } from '../../posthtml/defaultConfig.js' export default function posthtmlPlugin(filters = {}) { filters = merge(defaultFilters, filters) @@ -13,6 +14,6 @@ export async function filters(html = '', filters = {}, posthtmlOptions = {}) { return posthtml([ posthtmlPlugin(filters) ]) - .process(html, posthtmlOptions) + .process(html, getPosthtmlOptions(posthtmlOptions)) .then(result => result.html) } diff --git a/src/transformers/index.js b/src/transformers/index.js index c293cc12..fa53fd71 100644 --- a/src/transformers/index.js +++ b/src/transformers/index.js @@ -37,7 +37,7 @@ import { getPosthtmlOptions } from '../posthtml/defaultConfig.js' export async function run(html = '', config = {}) { const posthtmlPlugins = [] - const posthtmlConfig = getPosthtmlOptions(get(config, 'posthtml.options', {})) + const posthtmlOptions = getPosthtmlOptions(get(config, 'posthtml.options', {})) /** * 1. Core transformers @@ -110,14 +110,15 @@ export async function run(html = '', config = {}) { * Inline CSS into HTML. */ if (get(config, 'css.inline')) { - posthtmlPlugins.push(inlineCSS( - merge( - get(config, 'css.inline', {}), - { - removeInlinedSelectors: true, - } + posthtmlPlugins.push( + inlineCSS( + merge( + get(config, 'css.inline', {}), + { removeInlinedSelectors: true }, + ), + posthtmlOptions ) - )) + ) } /** @@ -130,7 +131,7 @@ export async function run(html = '', config = {}) { posthtmlPlugins.push( removeAttributes( get(config, 'attributes.remove', []), - posthtmlConfig + posthtmlOptions ) ) } @@ -165,7 +166,7 @@ export async function run(html = '', config = {}) { const baseConfig = get(config, 'baseURL', get(config, 'baseUrl')) if (baseConfig) { posthtmlPlugins.push( - baseUrl(baseConfig) + baseUrl(baseConfig, posthtmlOptions) ) } @@ -208,7 +209,9 @@ export async function run(html = '', config = {}) { * Remove unused CSS, uglify classes etc. */ if (get(config, 'css.purge')) { - posthtmlPlugins.push(purge(config.css.purge)) + posthtmlPlugins.push( + purge(config.css.purge, posthtmlOptions) + ) } /** @@ -236,7 +239,7 @@ export async function run(html = '', config = {}) { */ if (get(config, 'prettify')) { posthtmlPlugins.push( - prettify(get(config, 'prettify', {})) + prettify(get(config, 'prettify', {}), posthtmlOptions) ) } @@ -247,12 +250,12 @@ export async function run(html = '', config = {}) { */ if (get(config, 'minify')) { posthtmlPlugins.push( - minify(get(config, 'minify', {})) + minify(get(config, 'minify', {}), posthtmlOptions) ) } return posthtml(posthtmlPlugins) - .process(html, posthtmlConfig) + .process(html, posthtmlOptions) .then(result => ({ html: result.html, })) diff --git a/src/transformers/inline.js b/src/transformers/inline.js index 2ff955e2..710b4d25 100644 --- a/src/transformers/inline.js +++ b/src/transformers/inline.js @@ -18,8 +18,8 @@ import { getPosthtmlOptions } from '../posthtml/defaultConfig.js' * @param {*} options `css.inline` object from config * @returns {Function} PostHTML tree */ -export default (options = {}) => tree => { - return inline(render(tree), options).then(html => parse(html, getPosthtmlOptions())) +export default (options = {}, posthtmlOptions = {}) => tree => { + return inline(render(tree), options).then(html => parse(html, posthtmlOptions)) } /** diff --git a/src/transformers/markdown.js b/src/transformers/markdown.js index 0ed0c093..ec29d6cf 100644 --- a/src/transformers/markdown.js +++ b/src/transformers/markdown.js @@ -1,6 +1,17 @@ import posthtml from 'posthtml' -import md from 'posthtml-markdownit' +import markdownIt from 'posthtml-markdownit' +import { getPosthtmlOptions } from '../posthtml/defaultConfig.js' +/** + * Markdown transformer for PostHTML. + * + * Exported only for the Maizzle API - internally we use `posthtml-markdownit` directly. + * + * @param {String} input Markdown input string + * @param {Object} options Options for `posthtml-markdownit` + * @param {Object} posthtmlOptions PostHTML options + * @returns {Promise} Processed HTML string + */ export async function markdown(input = '', options = {}, posthtmlOptions = {}) { /** * If no input is provided, return an empty string. @@ -10,17 +21,16 @@ export async function markdown(input = '', options = {}, posthtmlOptions = {}) { } /** - * Automatically wrap in tag, unless manual mode is enabled. - * - * With manual mode, user must wrap the input in a tag. + * Automatically wrap in tag, unless `manual` mode is enabled. + * In `manual` mode, user must wrap the input in a tag. * * https://github.com/posthtml/posthtml-markdownit#usage */ input = options.manual ? input : `${input}` return posthtml([ - md(options) + markdownIt(options) ]) - .process(input, posthtmlOptions) + .process(input, getPosthtmlOptions(posthtmlOptions)) .then(result => result.html) } diff --git a/src/transformers/minify.js b/src/transformers/minify.js index 3d1e369a..7c232f54 100644 --- a/src/transformers/minify.js +++ b/src/transformers/minify.js @@ -5,22 +5,23 @@ import { render } from 'posthtml-render' import { parser as parse } from 'posthtml-parser' import { getPosthtmlOptions } from '../posthtml/defaultConfig.js' -const posthtmlPlugin = (options = {}) => tree => { +const posthtmlPlugin = (options = {}, posthtmlOptions = {}) => tree => { options = merge(options, { removeLineBreaks: true, }) - const posthtmlConfig = getPosthtmlOptions() const { result: html } = crush(render(tree), options) - return parse(html, posthtmlConfig) + return parse(html, posthtmlOptions) } export default posthtmlPlugin export async function minify(html = '', options = {}, posthtmlOptions = {}) { + posthtmlOptions = getPosthtmlOptions(posthtmlOptions) + return posthtml([ - posthtmlPlugin(options) + posthtmlPlugin(options, posthtmlOptions), ]) .process(html, posthtmlOptions) .then(result => result.html) diff --git a/src/transformers/posthtmlMso.js b/src/transformers/posthtmlMso.js index bbc49829..18a18cb7 100644 --- a/src/transformers/posthtmlMso.js +++ b/src/transformers/posthtmlMso.js @@ -1,5 +1,6 @@ import posthtml from 'posthtml' import posthtmlMso from 'posthtml-mso' +import { getPosthtmlOptions } from '../posthtml/defaultConfig.js' export default function posthtmlPlugin(options = {}) { return posthtmlMso(options) @@ -9,6 +10,6 @@ export async function useMso(html = '', options = {}, posthtmlOptions = {}) { return posthtml([ posthtmlPlugin(options) ]) - .process(html, posthtmlOptions) + .process(html, getPosthtmlOptions(posthtmlOptions)) .then(result => result.html) } diff --git a/src/transformers/prettify.js b/src/transformers/prettify.js index 7c794d19..d97e0f8f 100644 --- a/src/transformers/prettify.js +++ b/src/transformers/prettify.js @@ -5,7 +5,7 @@ import { render } from 'posthtml-render' import { parser as parse } from 'posthtml-parser' import { getPosthtmlOptions } from '../posthtml/defaultConfig.js' -const posthtmlPlugin = (options = {}) => tree => { +const posthtmlPlugin = (options = {}, posthtmlOptions = {}) => tree => { const defaultConfig = { space_around_combinator: true, // Preserve space around CSS selector combinators newline_between_rules: false, // Remove empty lines between CSS rules @@ -15,16 +15,16 @@ const posthtmlPlugin = (options = {}) => tree => { const config = merge(options, defaultConfig) - return parse(pretty(render(tree), config), getPosthtmlOptions()) + return parse(pretty(render(tree), config), posthtmlOptions) } export default posthtmlPlugin export async function prettify(html = '', options = {}, posthtmlOptions = {}) { - posthtmlOptions = merge(posthtmlOptions, getPosthtmlOptions()) + posthtmlOptions = getPosthtmlOptions(posthtmlOptions) return posthtml([ - posthtmlPlugin(options) + posthtmlPlugin(options, posthtmlOptions) ]) .process(html, posthtmlOptions) .then(result => result.html) diff --git a/src/transformers/preventWidows.js b/src/transformers/preventWidows.js index 38d0a93b..4c51f8bf 100644 --- a/src/transformers/preventWidows.js +++ b/src/transformers/preventWidows.js @@ -1,6 +1,7 @@ import posthtml from 'posthtml' -import posthtmlWidows from 'posthtml-widows' import { defu as merge } from 'defu' +import posthtmlWidows from 'posthtml-widows' +import { getPosthtmlOptions } from '../posthtml/defaultConfig.js' export default function posthtmlPlugin(options = {}) { options = merge(options, { @@ -32,6 +33,6 @@ export async function preventWidows(html = '', options = {}, posthtmlOptions = { return posthtml([ posthtmlPlugin(options) ]) - .process(html, posthtmlOptions) + .process(html, getPosthtmlOptions(posthtmlOptions)) .then(result => result.html) } diff --git a/src/transformers/purge.js b/src/transformers/purge.js index b2820695..514dfe5e 100644 --- a/src/transformers/purge.js +++ b/src/transformers/purge.js @@ -6,7 +6,7 @@ import { render } from 'posthtml-render' import { parser as parse } from 'posthtml-parser' import { getPosthtmlOptions } from '../posthtml/defaultConfig.js' -const posthtmlPlugin = options => tree => { +const posthtmlPlugin = (options = {}, posthtmlOptions = {}) => tree => { const defaultSafelist = [ '*body*', // Gmail '*gmail*', // Gmail @@ -39,17 +39,18 @@ const posthtmlPlugin = options => tree => { options = merge(options, defaultOptions) - const posthtmlConfig = getPosthtmlOptions() const { result: html } = comb(render(tree), options) - return parse(html, posthtmlConfig) + return parse(html, posthtmlOptions) } export default posthtmlPlugin export async function purge(html = '', pluginOptions = {}, posthtmlOptions = {}) { + posthtmlOptions = getPosthtmlOptions(posthtmlOptions) + return posthtml([ - posthtmlPlugin(pluginOptions) + posthtmlPlugin(pluginOptions, posthtmlOptions) ]) .process(html, posthtmlOptions) .then(result => result.html) diff --git a/src/transformers/removeAttributes.js b/src/transformers/removeAttributes.js index e3e0585b..f77007fb 100644 --- a/src/transformers/removeAttributes.js +++ b/src/transformers/removeAttributes.js @@ -47,9 +47,11 @@ const posthtmlPlugin = (attributes = [], posthtmlOptions = {}) => tree => { export default posthtmlPlugin export async function removeAttributes(html = '', attributes = [], posthtmlOptions = {}) { + posthtmlOptions = getPosthtmlOptions(posthtmlOptions) + return posthtml([ - posthtmlPlugin(attributes, getPosthtmlOptions(posthtmlOptions)) + posthtmlPlugin(attributes, posthtmlOptions) ]) - .process(html, getPosthtmlOptions()) + .process(html, posthtmlOptions) .then(result => result.html) } diff --git a/src/transformers/replaceStrings.js b/src/transformers/replaceStrings.js index d79c62ac..e21d3ede 100644 --- a/src/transformers/replaceStrings.js +++ b/src/transformers/replaceStrings.js @@ -32,6 +32,6 @@ export async function replaceStrings(html = '', replacements = {}, posthtmlOptio return posthtml([ posthtmlPlugin(replacements) ]) - .process(html, posthtmlOptions) + .process(html, getPosthtmlOptions(posthtmlOptions)) .then(result => result.html) } diff --git a/src/transformers/safeClassNames.js b/src/transformers/safeClassNames.js index ff5cf876..9b8027fa 100644 --- a/src/transformers/safeClassNames.js +++ b/src/transformers/safeClassNames.js @@ -1,6 +1,7 @@ import posthtml from 'posthtml' import { defu as merge } from 'defu' import posthtmlSafeClassNames from 'posthtml-safe-class-names' +import { getPosthtmlOptions } from '../posthtml/defaultConfig.js' export default function posthtmlPlugin(options = {}) { // If options is boolean, convert to object @@ -24,6 +25,6 @@ export async function safeClassNames(html = '', options = {}, posthtmlOptions = return posthtml([ posthtmlPlugin(options) ]) - .process(html, posthtmlOptions) + .process(html, getPosthtmlOptions(posthtmlOptions)) .then(result => result.html) } diff --git a/src/transformers/shorthandCss.js b/src/transformers/shorthandCss.js index ca1dc95f..6d7e9ec0 100644 --- a/src/transformers/shorthandCss.js +++ b/src/transformers/shorthandCss.js @@ -1,4 +1,5 @@ import posthtml from 'posthtml' +import { getPosthtmlOptions } from '../posthtml/defaultConfig.js' import posthtmlMergeLonghand from 'posthtml-postcss-merge-longhand' export default function posthtmlPlugin(options = {}) { @@ -15,6 +16,6 @@ export async function shorthandCSS(html = '', options = {}, posthtmlOptions = {} return posthtml([ posthtmlPlugin(options) ]) - .process(html, posthtmlOptions) + .process(html, getPosthtmlOptions(posthtmlOptions)) .then(result => result.html) } diff --git a/src/transformers/sixHex.js b/src/transformers/sixHex.js index a64149d3..0e9a3949 100644 --- a/src/transformers/sixHex.js +++ b/src/transformers/sixHex.js @@ -1,5 +1,6 @@ import posthtml from 'posthtml' import { conv } from 'color-shorthand-hex-to-six-digit' +import { getPosthtmlOptions } from '../posthtml/defaultConfig.js' const posthtmlPlugin = () => tree => { const targets = new Set(['bgcolor', 'color']) @@ -25,6 +26,6 @@ export async function sixHEX(html = '', posthtmlOptions = {}) { return posthtml([ posthtmlPlugin() ]) - .process(html, posthtmlOptions) + .process(html, getPosthtmlOptions(posthtmlOptions)) .then(result => result.html) } diff --git a/src/transformers/urlParameters.js b/src/transformers/urlParameters.js index 3b0e8181..df410c63 100644 --- a/src/transformers/urlParameters.js +++ b/src/transformers/urlParameters.js @@ -1,6 +1,7 @@ import posthtml from 'posthtml' import get from 'lodash-es/get.js' import urlParameters from 'posthtml-url-parameters' +import { getPosthtmlOptions } from '../posthtml/defaultConfig.js' export default function posthtmlPlugin(options = {}) { const { _options, ...parameters } = options @@ -15,6 +16,6 @@ export async function addURLParams(html = '', options = {}, posthtmlOptions = {} return posthtml([ posthtmlPlugin(options) ]) - .process(html, posthtmlOptions) + .process(html, getPosthtmlOptions(posthtmlOptions)) .then(result => result.html) } diff --git a/src/transformers/useAttributeSizes.js b/src/transformers/useAttributeSizes.js index 779d4baf..e42e646b 100644 --- a/src/transformers/useAttributeSizes.js +++ b/src/transformers/useAttributeSizes.js @@ -1,6 +1,7 @@ import postcss from 'postcss' import posthtml from 'posthtml' import get from 'lodash-es/get.js' +import { getPosthtmlOptions } from '../posthtml/defaultConfig.js' const posthtmlPlugin = (mappings = {}) => tree => { if (!Object.keys(mappings).length) { @@ -58,6 +59,6 @@ export async function useAttributeSizes(html = '', mappings = {}, posthtmlOption return posthtml([ posthtmlPlugin(mappings) ]) - .process(html, posthtmlOptions) + .process(html, getPosthtmlOptions(posthtmlOptions)) .then(result => result.html) } From 08fd17542ede05811343d23ad449be9aa8e9b6af Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Thu, 17 Jul 2025 22:47:42 +0300 Subject: [PATCH 058/100] feat: replace css properties transformer --- src/transformers/index.js | 44 +++++++++------ src/transformers/replaceCssProperties.js | 55 +++++++++++++++++++ .../transformers/replaceCssProperties.test.js | 16 ++++++ 3 files changed, 98 insertions(+), 17 deletions(-) create mode 100644 src/transformers/replaceCssProperties.js create mode 100644 test/transformers/replaceCssProperties.test.js diff --git a/src/transformers/index.js b/src/transformers/index.js index fa53fd71..1d22e2b1 100644 --- a/src/transformers/index.js +++ b/src/transformers/index.js @@ -21,6 +21,7 @@ import safeClassNames from './safeClassNames.js' import replaceStrings from './replaceStrings.js' import attributeToStyle from './attributeToStyle.js' import removeAttributes from './removeAttributes.js' +import replaceCssProperties from './replaceCssProperties.js' import { getPosthtmlOptions } from '../posthtml/defaultConfig.js' @@ -60,7 +61,16 @@ export async function run(html = '', config = {}) { } /** - * 3. Filters + * 3. Replace CSS properties + * + * Replaces CSS properties based on a custom mapping. + */ + if (get(config, 'css.replaceProperties') !== false) { + posthtmlPlugins.push(replaceCssProperties(config)) + } + + /** + * 4. Filters * * Filters are always applied, unless explicitly disabled. */ @@ -71,7 +81,7 @@ export async function run(html = '', config = {}) { } /** - * 4. Markdown + * 5. Markdown * * Convert Markdown to HTML with markdown-it, unless explicitly disabled. */ @@ -82,7 +92,7 @@ export async function run(html = '', config = {}) { } /** - * 5. Prevent widow words + * 6. Prevent widow words * * Enabled by default, will prevent widow words in elements * wrapped with a `prevent-widows` attribute. @@ -94,7 +104,7 @@ export async function run(html = '', config = {}) { } /** - * 6. Attribute to `style` + * 7. Attribute to `style` * * Duplicate HTML attributes to inline CSS. */ @@ -105,7 +115,7 @@ export async function run(html = '', config = {}) { } /** - * 7. Inline CSS + * 8. Inline CSS * * Inline CSS into HTML. */ @@ -122,7 +132,7 @@ export async function run(html = '', config = {}) { } /** - * 8. Remove attributes + * 9. Remove attributes * * Remove attributes from HTML tags * If `undefined`, removes empty `style` and `class` attributes @@ -137,7 +147,7 @@ export async function run(html = '', config = {}) { } /** - * 9. Shorthand CSS + * 10. Shorthand CSS * * Convert longhand CSS properties to shorthand in `style` attributes. */ @@ -148,7 +158,7 @@ export async function run(html = '', config = {}) { } /** - * 10. Add attributes + * 11. Add attributes * * Add attributes to HTML tags. */ @@ -159,7 +169,7 @@ export async function run(html = '', config = {}) { } /** - * 11. Base URL + * 12. Base URL * * Add a base URL to relative paths. */ @@ -171,7 +181,7 @@ export async function run(html = '', config = {}) { } /** - * 12. URL parameters + * 13. URL parameters * * Add parameters to URLs. */ @@ -182,7 +192,7 @@ export async function run(html = '', config = {}) { } /** - * 13. Six-digit HEX + * 14. Six-digit HEX * * Enabled by default, converts three-digit HEX colors to six-digit. */ @@ -193,7 +203,7 @@ export async function run(html = '', config = {}) { } /** - * 14. PostHTML MSO + * 15. PostHTML MSO * * Enabled by default, simplifies writing MSO conditionals for Outlook. */ @@ -204,7 +214,7 @@ export async function run(html = '', config = {}) { } /** - * 15. Purge CSS + * 16. Purge CSS * * Remove unused CSS, uglify classes etc. */ @@ -215,14 +225,14 @@ export async function run(html = '', config = {}) { } /** - * 16.