Skip to content

Commit 955b0cd

Browse files
committed
fix: issue of group-modifier converting to sass
Sass does not support the custom tailwind group-modifier approach. Now converting as a sub query.
1 parent f3bfa36 commit 955b0cd

File tree

6 files changed

+143
-8
lines changed

6 files changed

+143
-8
lines changed

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@egoistdeveloper/twcss-to-sass",
3-
"version": "2.1.6",
3+
"version": "2.1.7",
44
"description": "HTML template to SASS converter for TailwindCSS",
55
"main": "dist/cjs/index.js",
66
"module": "dist/esm/index.js",

readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ npm i @egoistdeveloper/twcss-to-sass
3131
- [x] ~~Fix self-closing tags like `link`, `base`, `area`, `br` etc~~
3232
- [x] ~~Fix url conflict with class name in comment line~~
3333
- [ ] Add option for duplicated classes
34-
- [ ] Fix `group` and `peer` utility classes issue on SASS
34+
- [ ] ~~Fix `group`~~ and `peer` utility classes issue on SASS
3535
- [ ] Filter non tailwind classes
3636
- [ ] Order classes
3737
- [ ] Group classes
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export interface IGroupModifierPair {
2+
modifier: string
3+
utility: string
4+
className: string
5+
}
6+
7+
export default IGroupModifierPair

src/twcss-to-sass.ts

Lines changed: 132 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
IHtmlNodeAttribute,
1313
} from './interfaces/html-node'
1414
import { IConverterResult } from './interfaces/converter-result'
15+
import { IGroupModifierPair } from './interfaces/group-modifier-pair'
1516

1617
/**
1718
* Default js-beautify css formatter options
@@ -214,15 +215,117 @@ function getCssTree(cssTree: IHtmlNode[]): string {
214215

215216
if (styles.length > 0) {
216217
cssTree.forEach((style: IHtmlNode, index) => {
217-
css += `// #region Style Group ${index + 1}\n\n`
218+
css += `/* #region Style Group ${index + 1} */\n\n`
218219
css += `${style.content}\n`
219-
css += `// #endregion\n\n`
220+
css += `/* #endregion */\n\n`
220221
})
221222
}
222223

223224
return css
224225
}
225226

227+
let groupModifierList: IGroupModifierPair[] = []
228+
229+
/**
230+
* Convert group-* modifiers to sub-selectors
231+
*
232+
* @param nodeTree IHtmlNode[]
233+
* @param deepth number
234+
* @param isChildNodes boolean
235+
*
236+
* @returns string
237+
*/
238+
function groupUtilityToSass(
239+
nodeTree: IHtmlNode[],
240+
deepth: number,
241+
isChildNodes = false
242+
): string {
243+
if (!isChildNodes) {
244+
groupModifierList = []
245+
}
246+
247+
let groupSass = ''
248+
249+
const groupPattern = / group-([a-z0-9]+):([a-z0-9-:\/]+)/gm
250+
251+
nodeTree.forEach((node: IHtmlNode) => {
252+
if (node.filterAttributes) {
253+
if (
254+
node.filterAttributes.class &&
255+
node.filterAttributes.class?.match(groupPattern)
256+
) {
257+
node.filterAttributes.class?.match(groupPattern)?.forEach((item) => {
258+
const matches = new RegExp(groupPattern).exec(item)
259+
260+
const groupModifierPair = <IGroupModifierPair>{
261+
modifier: matches?.[1],
262+
utility: matches?.[2],
263+
className: getClassName(node, deepth),
264+
}
265+
266+
groupModifierList.push(groupModifierPair)
267+
})
268+
269+
if (node.filterAttributes.class.match(/(group)(?!-)/gm)) {
270+
return groupSass
271+
} else if (node.children.length) {
272+
groupUtilityToSass(node.children, ++deepth, true)
273+
}
274+
}
275+
}
276+
})
277+
278+
if (!isChildNodes) {
279+
if (groupModifierList.length > 0) {
280+
const modifierGroups = groupModifierList.reduce((prev, next) => {
281+
prev[next.modifier] = prev[next.modifier] || []
282+
prev[next.modifier].push(next)
283+
284+
return prev
285+
}, Object.create(null))
286+
287+
Object.entries(modifierGroups)?.forEach(([modifier, utilityList]) => {
288+
const _utilityList = <IGroupModifierPair[]>utilityList
289+
290+
const classGroups = _utilityList.reduce((prev, next) => {
291+
prev[next.className] = prev[next.className] || []
292+
prev[next.className].push(next)
293+
294+
return prev
295+
}, Object.create(null))
296+
297+
groupSass += `&:${modifier} {\n`
298+
299+
Object.entries(classGroups)?.forEach(([className, utilityList]) => {
300+
const _utilityList = <IGroupModifierPair[]>utilityList
301+
302+
const classList = _utilityList
303+
.map((x: IGroupModifierPair) => x.utility)
304+
.join(' ')
305+
306+
groupSass += `\t${className} {\n`
307+
groupSass += `\t\t@apply ${classList};\n`
308+
groupSass += `\t}\n`
309+
})
310+
311+
groupSass += `}\n\n`
312+
313+
// const classList = _utilityList
314+
// .map((x: IGroupModifierPair) => x.utility)
315+
// .join(' ')
316+
317+
// groupSass += `&:${modifier} {\n`
318+
// groupSass += `\t@apply ${classList};\n`
319+
// groupSass += `}\n`
320+
})
321+
}
322+
323+
return groupSass
324+
}
325+
326+
return ''
327+
}
328+
226329
/**
227330
* Extract SASS tree from HTML JSON tree
228331
*
@@ -246,9 +349,14 @@ function getSassTree(nodeTree: IHtmlNode[], deepth = 0) {
246349
if (node.filterAttributes) {
247350
// print tailwind class names
248351
if (node.filterAttributes.class) {
249-
treeString += node.filterAttributes.class
352+
let tailwindClassList = node.filterAttributes.class
250353
? `@apply ${node.filterAttributes.class};`
251354
: ''
355+
356+
// remove group class
357+
tailwindClassList = tailwindClassList.replace(/(group)(?!-)/gm, ' ')
358+
359+
treeString += tailwindClassList
252360
}
253361

254362
// inline style printing
@@ -259,7 +367,7 @@ function getSassTree(nodeTree: IHtmlNode[], deepth = 0) {
259367
)
260368

261369
treeString += node.filterAttributes.style
262-
? `\n${node.filterAttributes.style}\n`
370+
? `\n\n${node.filterAttributes.style}`
263371
: ''
264372
}
265373
}
@@ -273,7 +381,26 @@ function getSassTree(nodeTree: IHtmlNode[], deepth = 0) {
273381

274382
const className = getClassName(node, deepth)
275383

276-
return `${classComment}${className}{${treeString}${subTreeString}}`
384+
let groupUtilityTree = ''
385+
386+
if (node.filterAttributes?.class?.match(/(group)(?!-)/gm)) {
387+
groupUtilityTree = groupUtilityToSass(node.children, deepth)
388+
389+
if (groupUtilityTree !== '') {
390+
treeString += groupUtilityTree
391+
392+
// clear group modifier classes from @apply
393+
subTreeString = subTreeString.replace(
394+
/ group-([a-z0-9]+):([a-z0-9-:\/]+)/gm,
395+
''
396+
)
397+
}
398+
}
399+
400+
return `${classComment}
401+
${className} {
402+
${treeString} ${subTreeString}
403+
}`
277404
}
278405

279406
return null

test/twcss-to-sass.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ test('convert to sass with inline css', () => {
2121
const sassOutput = `/* div -> 1 */
2222
.class-div-1 {
2323
@apply w-72 h-40 bg-green-400 transform transition-all;
24+
2425
border: 1px solid white;
2526
padding: 30px;
2627
font-weight: 50px;

0 commit comments

Comments
 (0)