Skip to content

Commit 8bc2e5c

Browse files
authored
feat: support enforcing truncate shorthand (#255)
1 parent ae7000a commit 8bc2e5c

File tree

2 files changed

+132
-1
lines changed

2 files changed

+132
-1
lines changed

lib/rules/enforces-shorthand.js

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ module.exports = {
7373
// Helpers
7474
//----------------------------------------------------------------------
7575

76+
// These are shorthand candidates that do not share the same parent type
77+
const complexEquivalences = [
78+
[["overflow-hidden", "text-ellipsis", "whitespace-nowrap"], "truncate"]
79+
]
80+
7681
// Init assets
7782
const targetProperties = {
7883
Layout: ['Overflow', 'Overscroll Behavior', 'Top / Right / Bottom / Left'],
@@ -81,7 +86,9 @@ module.exports = {
8186
Borders: ['Border Radius', 'Border Width', 'Border Color'],
8287
Tables: ['Border Spacing'],
8388
Transforms: ['Scale'],
89+
Typography: ['Text Overflow', 'Whitespace']
8490
};
91+
8592
// We don't want to affect other rules by object reference
8693
const cloned = JSON.parse(JSON.stringify(defaultGroups));
8794
const targetGroups = cloned.filter((g) => Object.keys(targetProperties).includes(g.type));
@@ -210,9 +217,49 @@ module.exports = {
210217
});
211218

212219
const validated = [];
220+
221+
// Handle sets of classnames with different parent types
222+
let remaining = parsed
223+
for (const [inputSet, outputClassname] of complexEquivalences) {
224+
if (remaining.length < inputSet.length) {
225+
continue
226+
}
227+
228+
const parsedElementsInInputSet = remaining.filter(remainingClass => inputSet.some(inputClass => remainingClass.name.includes(inputClass)))
229+
230+
// Make sure all required classes for the shorthand are present
231+
if (parsedElementsInInputSet.length !== inputSet.length) {
232+
continue
233+
}
234+
235+
// Make sure the classes share all the same variants
236+
if (new Set(parsedElementsInInputSet.map(p => p.variants)).size !== 1) {
237+
continue
238+
}
239+
240+
// Make sure the classes share all the same importance
241+
if (new Set(parsedElementsInInputSet.map(p => p.important)).size !== 1) {
242+
continue
243+
}
244+
245+
const index = parsedElementsInInputSet[0].index
246+
const variants = parsedElementsInInputSet[0].variants
247+
const important = parsedElementsInInputSet[0].important ? "!" : ""
248+
249+
const patchedClassname = `${variants}${important}${mergedConfig.prefix}${outputClassname}`
250+
troubles.push([parsedElementsInInputSet.map((c) => `${c.name}`), patchedClassname]);
251+
252+
const validatedClassname = groupUtil.parseClassname(patchedClassname, targetGroups, mergedConfig, index)
253+
validated.push(validatedClassname);
254+
255+
remaining = remaining.filter(p => !parsedElementsInInputSet.includes(p))
256+
}
257+
258+
// Handle sets of classnames with the same parent type
259+
213260
// Each group parentType
214261
const checkedGroups = [];
215-
parsed.forEach((classname) => {
262+
remaining.forEach((classname) => {
216263
// Valid candidate
217264
if (classname.parentType === '') {
218265
validated.push(classname);

tests/lib/rules/enforces-shorthand.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,20 @@ ruleTester.run("shorthands", rule, {
112112
</div>
113113
`,
114114
},
115+
{
116+
code: `
117+
<div class="overflow-hidden text-ellipsis hover:whitespace-nowrap">
118+
Possible shorthand available for truncate, but some of the classes have modifiers
119+
</div>
120+
`,
121+
},
122+
{
123+
code: `
124+
<div class="overflow-hidden text-ellipsis !whitespace-nowrap">
125+
Possible shorthand available for truncate, but some of the classes have important
126+
</div>
127+
`,
128+
},
115129
],
116130

117131
invalid: [
@@ -601,5 +615,75 @@ ruleTester.run("shorthands", rule, {
601615
`,
602616
errors: [generateError(["group/name:rounded-r-full", "group/name:rounded-l-full"], "group/name:rounded-full")],
603617
},
618+
{
619+
code: `
620+
<div class="overflow-hidden text-ellipsis whitespace-nowrap">
621+
Possible shorthand when using truncate
622+
</div>
623+
`,
624+
output: `
625+
<div class="truncate">
626+
Possible shorthand when using truncate
627+
</div>
628+
`,
629+
errors: [generateError(["overflow-hidden", "text-ellipsis", "whitespace-nowrap"], "truncate")],
630+
},
631+
{
632+
code: `
633+
<div class="md:overflow-hidden md:text-ellipsis md:whitespace-nowrap">
634+
Possible shorthand when using truncate with breakpoint
635+
</div>
636+
`,
637+
output: `
638+
<div class="md:truncate">
639+
Possible shorthand when using truncate with breakpoint
640+
</div>
641+
`,
642+
errors: [generateError(["md:overflow-hidden", "md:text-ellipsis", "md:whitespace-nowrap"], "md:truncate")],
643+
},
644+
{
645+
code: `
646+
<div class="hover:overflow-hidden hover:text-ellipsis hover:whitespace-nowrap">
647+
Possible shorthand when using truncate with hover
648+
</div>
649+
`,
650+
output: `
651+
<div class="hover:truncate">
652+
Possible shorthand when using truncate with hover
653+
</div>
654+
`,
655+
errors: [generateError(["hover:overflow-hidden", "hover:text-ellipsis", "hover:whitespace-nowrap"], "hover:truncate")],
656+
},
657+
{
658+
code: `
659+
<div class="hover:sm:!tw-overflow-hidden hover:sm:!tw-text-ellipsis hover:sm:!tw-whitespace-nowrap">
660+
Possible shorthand when using truncate with hover, breakpoint, important and prefix
661+
</div>
662+
`,
663+
output: `
664+
<div class="hover:sm:!tw-truncate">
665+
Possible shorthand when using truncate with hover, breakpoint, important and prefix
666+
</div>
667+
`,
668+
errors: [generateError(["hover:sm:!tw-overflow-hidden", "hover:sm:!tw-text-ellipsis", "hover:sm:!tw-whitespace-nowrap"], "hover:sm:!tw-truncate")],
669+
options: [
670+
{
671+
config: { prefix: "tw-" },
672+
},
673+
],
674+
},
675+
{
676+
code: `
677+
<div class="overflow-hidden text-ellipsis whitespace-nowrap text-white text-xl">
678+
Possible shorthand when using truncate, tested with additional classnames
679+
</div>
680+
`,
681+
output: `
682+
<div class="truncate text-white text-xl">
683+
Possible shorthand when using truncate, tested with additional classnames
684+
</div>
685+
`,
686+
errors: [generateError(["overflow-hidden", "text-ellipsis", "whitespace-nowrap"], "truncate")],
687+
},
604688
],
605689
});

0 commit comments

Comments
 (0)