Skip to content

Commit cc3a4d7

Browse files
committed
feat: add option for duplicate classes same level
1 parent 23262ce commit cc3a4d7

File tree

4 files changed

+133
-28
lines changed

4 files changed

+133
-28
lines changed

package.json

Lines changed: 5 additions & 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.13",
3+
"version": "2.1.14",
44
"description": "HTML template to SASS converter for TailwindCSS",
55
"main": "dist/cjs/index.js",
66
"module": "dist/esm/index.js",
@@ -15,6 +15,10 @@
1515
"build:esm": "node tools/cleanup esm && tsc -p config/tsconfig.esm.json",
1616
"build:umd": "node tools/cleanup umd && webpack --config config/webpack.config.js",
1717
"build:types": "node tools/cleanup types && tsc -p config/tsconfig.types.json",
18+
"build:cjs:watch": "tsc --watch -p config/tsconfig.cjs.json",
19+
"build:esm:watch": "tsc --watch -p config/tsconfig.esm.json",
20+
"build:umd:watch": "webpack --watch --progress --config config/webpack.config.js",
21+
"build:types:watch": "tsc --watch -p config/tsconfig.types.json",
1822
"clean": "node tools/cleanup",
1923
"package": "npm run build && npm pack",
2024
"test": "jest --no-cache --runInBand",

src/interfaces/tw-to-sass-options.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ export interface ITwToSassOptions {
88
printSassComments: boolean
99
useCommentBlocksAsClassName: boolean
1010
maxClassNameLength: number
11-
classNameOptions: IClassNameOptions
11+
classNameOptions: IClassNameOptions,
12+
preventDuplicateClasses: boolean
1213
}

src/twcss-to-sass.ts

Lines changed: 74 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const defaultOptions: ITwToSassOptions = {
3737
printHtmlComments: true,
3838
printSassComments: true,
3939
formatterOptions: formatterOptions,
40+
preventDuplicateClasses: true,
4041
classNameOptions: {
4142
lowercase: true,
4243
replacement: '-',
@@ -351,7 +352,6 @@ function peerUtilityToSass(
351352

352353
nodeTree.forEach((node: IHtmlNode) => {
353354
if (node.filterAttributes) {
354-
console.log(node.filterAttributes.class?.match(groupPattern))
355355
if (
356356
node.filterAttributes.class &&
357357
node.filterAttributes.class?.match(groupPattern)
@@ -365,8 +365,6 @@ function peerUtilityToSass(
365365
className: getClassName(node, node.order),
366366
}
367367

368-
console.log(groupModifierPair)
369-
370368
peerModifierList.push(groupModifierPair)
371369
})
372370

@@ -424,6 +422,40 @@ function peerUtilityToSass(
424422
return ''
425423
}
426424

425+
/**
426+
* Get class name and styles of node as SASS format
427+
*
428+
* @param node IHtmlNode
429+
*
430+
* @returns string
431+
*/
432+
function getNodeClassAndStyles(node: IHtmlNode): string {
433+
let nodeClassAndStyles = ''
434+
435+
if (node.filterAttributes) {
436+
// print tailwind class names
437+
if (node.filterAttributes.class) {
438+
nodeClassAndStyles += node.filterAttributes.class
439+
? `@apply ${node.filterAttributes.class};`
440+
: ''
441+
}
442+
443+
// inline style printing
444+
if (node.filterAttributes.style) {
445+
node.filterAttributes.style = Utils.addMissingSuffix(
446+
node.filterAttributes.style,
447+
';'
448+
)
449+
450+
nodeClassAndStyles += node.filterAttributes.style
451+
? `\n\n${node.filterAttributes.style}`
452+
: ''
453+
}
454+
}
455+
456+
return nodeClassAndStyles
457+
}
458+
427459
/**
428460
* Extract SASS tree from HTML JSON tree
429461
*
@@ -432,9 +464,11 @@ function peerUtilityToSass(
432464
* @returns string
433465
*/
434466
function getSassTree(nodeTree: IHtmlNode[]): string {
435-
let sassTree = ''
467+
let sassTree = '',
468+
isLastLevel = false,
469+
duplicatedItems: string[] = []
436470

437-
nodeTree.forEach((node: IHtmlNode) => {
471+
nodeTree.forEach((node: IHtmlNode, index) => {
438472
let treeString = '',
439473
subTreeString = ''
440474

@@ -444,29 +478,46 @@ function getSassTree(nodeTree: IHtmlNode[]): string {
444478

445479
if (hasSubElement) {
446480
subTreeString = getSassTree(node.children)
447-
}
448481

449-
if (node.filterAttributes) {
450-
// print tailwind class names
451-
if (node.filterAttributes.class) {
452-
treeString += node.filterAttributes.class
453-
? `@apply ${node.filterAttributes.class};`
454-
: ''
455-
}
456-
457-
// inline style printing
458-
if (node.filterAttributes.style) {
459-
node.filterAttributes.style = Utils.addMissingSuffix(
460-
node.filterAttributes.style,
461-
';'
462-
)
482+
isLastLevel = false
483+
} else if (_defaultOptions.preventDuplicateClasses) {
484+
isLastLevel = true
485+
}
463486

464-
treeString += node.filterAttributes.style
465-
? `\n\n${node.filterAttributes.style}`
466-
: ''
487+
let nodeClassAndStyles = ''
488+
489+
nodeClassAndStyles += getNodeClassAndStyles(node)
490+
491+
if (
492+
_defaultOptions.preventDuplicateClasses &&
493+
isLastLevel &&
494+
nodeTree[index + 1]
495+
) {
496+
const _hasSubElement = nodeTree[index + 1].children?.filter(
497+
(child) => child.type === 'element'
498+
).length
499+
500+
if (!_hasSubElement) {
501+
const _nodeClassAndStyles = getNodeClassAndStyles(nodeTree[index + 1])
502+
503+
// compare node sass content with next node sass content or
504+
// in same level other nodes
505+
if (nodeClassAndStyles != '' && _nodeClassAndStyles != '') {
506+
if (
507+
nodeClassAndStyles == _nodeClassAndStyles ||
508+
!duplicatedItems.includes(nodeClassAndStyles)
509+
) {
510+
duplicatedItems.push(_nodeClassAndStyles)
511+
512+
// clear duplicated node sass content
513+
nodeClassAndStyles = ''
514+
}
515+
}
467516
}
468517
}
469518

519+
treeString += nodeClassAndStyles
520+
470521
if (treeString.length || subTreeString.length) {
471522
const classComment = _defaultOptions.printSassComments
472523
? `/* ${node.comment ? node.comment : node.tagName}${

test/twcss-to-sass.test.ts

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,17 @@ test('convert to sass with comments', () => {
4545
<!-- Container End-->`
4646

4747
const sassOutput = `/* Container Any -> 1 */
48-
.class-div-1 {
48+
.container-any {
4949
@apply bg-white;
5050
5151
/* Some Div -> 2 */
52-
.class-div-2 {
52+
.some-div {
5353
@apply flex justify-center items-center min-h-screen min-w-full;
5454
}
5555
}`
5656

5757
const converterConfigs = <ITwToSassOptions>{
58-
useCommentBlocksAsClassName: false,
58+
useCommentBlocksAsClassName: true,
5959
printSassComments: true,
6060
}
6161
const converterResult = convertToSass(htmlCotnent, converterConfigs)
@@ -131,3 +131,52 @@ test('convert to sass with group-modifier', () => {
131131

132132
expect(converterResult?.sass).toBe(sassOutput)
133133
})
134+
135+
test('convert to sass with non-duplicated classes', () => {
136+
const htmlCotnent = `<!-- Rating -->
137+
<div class="flex flex-row group">
138+
<i class="mdi mdi-star text-xs text-amber-400
139+
hover:text-amber-500 transition-all duration-200"
140+
title="Worst"></i>
141+
142+
<i class="mdi mdi-star text-xs text-amber-400
143+
hover:text-amber-500 transition-all duration-200"
144+
title="Bad"></i>
145+
146+
<i class="mdi mdi-star text-xs text-amber-400
147+
hover:text-amber-500 transition-all duration-200"
148+
title="Not Bad"></i>
149+
150+
<i class="mdi mdi-star text-xs text-amber-400
151+
hover:text-amber-500 transition-all duration-200"
152+
title="Good"></i>
153+
154+
<i class="mdi mdi-star text-xs text-amber-400
155+
hover:text-amber-500 transition-all duration-200"
156+
title="Awesome"></i>
157+
158+
<div class="text-xxs text-gray-400 ml-1 hover:underline">
159+
text
160+
</div>
161+
</div>`
162+
163+
const sassOutput = `/* Rating -> 1 */
164+
.rating {
165+
@apply flex flex-row group;
166+
167+
/* i */
168+
i {
169+
@apply mdi mdi-star text-xs text-amber-400 hover:text-amber-500 transition-all duration-200;
170+
}
171+
172+
/* div -> 2 */
173+
.class-div-2 {
174+
@apply text-xxs text-gray-400 ml-1 hover:underline;
175+
}
176+
}`
177+
178+
const converterResult = convertToSass(htmlCotnent)
179+
180+
expect(converterResult?.sass).toBe(sassOutput)
181+
})
182+

0 commit comments

Comments
 (0)