From 4532ea491b76b8c64374e2982d101c0f5fa1e64e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arnaud=20Barr=C3=A9?= Date: Tue, 5 Dec 2023 01:04:36 +0100 Subject: [PATCH] Group selectors with same content [publish] --- CHANGELOG.md | 3 ++- package.json | 2 +- src/index.ts | 32 ++++++++++++++++++++++++-------- tests/generate.test.ts | 1 + tests/snapshots/generate.css | 11 +++++++++++ 5 files changed, 39 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c3ad16..78fb529 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,10 @@ # Changelog -## Unreleased +## 0.7.1 - Sort defaults to get a stable output - Internally consider media & supports variant as `atRule`s so that the nesting output is closer to Tailwind +- Group selectors with same content. Pre 0.7 this optimization was done by Lightning CSS and esbuild does not support this optimization when minifying ## 0.7.0 diff --git a/package.json b/package.json index 9021f5b..fe2bdcc 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description": "A bundler-first & PostCSS-independent implementation of Tailwind", "private": true, "type": "module", - "version": "0.7.0", + "version": "0.7.1", "author": "Arnaud Barré (https://github.com/ArnaudBarre)", "license": "MIT", "scripts": { diff --git a/src/index.ts b/src/index.ts index b5fd55f..ff0ea9b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -432,6 +432,11 @@ export const initDownwindWithConfig = ({ return { cssLines, invalidateUtils }; }; + const getLines = (match: Match) => + match.type === "Rule" + ? toCSS(match.ruleEntry, match.important) + : [arbitraryPropertyMatchToLine(match)]; + for (const token of config.safelist) { const match = parse(token); if (!match) { @@ -525,7 +530,8 @@ export const initDownwindWithConfig = ({ if (diff !== 0) return diff; return a.token.localeCompare(b.token); }); - for (const match of group.matches) { + let nextLines: string[] | undefined = undefined; + for (const [idx, match] of group.matches.entries()) { const meta = match.type === "Rule" ? getRuleMeta(match.ruleEntry.rule) @@ -542,13 +548,23 @@ export const initDownwindWithConfig = ({ match.variants, meta, ); - utilsOutput += printBlock( - selector, - match.type === "Rule" - ? toCSS(match.ruleEntry, match.important) - : [arbitraryPropertyMatchToLine(match)], - indentation, - ); + const lines: string[] = nextLines ?? getLines(match); + const nextMatch = group.matches.at(idx + 1); + nextLines = nextMatch ? getLines(nextMatch) : undefined; + if ( + lines.length === nextLines?.length && + nextLines.every((l, i) => l === lines[i]) + ) { + utilsOutput += `${indentation}${selector},\n`; + } else { + utilsOutput += printBlock( + selector, + match.type === "Rule" + ? toCSS(match.ruleEntry, match.important) + : [arbitraryPropertyMatchToLine(match)], + indentation, + ); + } } group.atRules.sort((a, b) => { diff --git a/tests/generate.test.ts b/tests/generate.test.ts index dfb7a75..62182c1 100644 --- a/tests/generate.test.ts +++ b/tests/generate.test.ts @@ -115,6 +115,7 @@ const cases: [name: string, content: string, config?: UserConfig][] = [ "text-slate-200 bg-orange-300 border-blue-100 border-black/20", { coreRules: { textOpacity: false, borderOpacity: false } }, ], + ["group-rules", "first:p-2 p-2 last:p-2 before:mt-[3px] after:mt-[3px]"], ["custom-config", "p-4 p-6 m-4", { theme: { padding: { 4: "4px" } } }], [ "extend-config", diff --git a/tests/snapshots/generate.css b/tests/snapshots/generate.css index 5a8e4e0..065793c 100644 --- a/tests/snapshots/generate.css +++ b/tests/snapshots/generate.css @@ -498,6 +498,17 @@ html:has(.\[html\:has\(\&\)\]\:bg-blue-500) { color: #e2e8f0; } +/* group-rules: first:p-2 p-2 last:p-2 before:mt-[3px] after:mt-[3px] */ +.after\:mt-\[3px\]::after, +.before\:mt-\[3px\]::before { + margin-top: 3px; +} +.first\:p-2:first-child, +.last\:p-2:last-child, +.p-2 { + padding: 0.5rem; +} + /* custom-config: p-4 p-6 m-4 */ .m-4 { margin: 1rem;