Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
feat: Support enforcing truncate shorthand
  • Loading branch information
bezbac committed Jun 15, 2023
commit c56ec3b88baa54d820db1842fbd68e863b6a597c
49 changes: 48 additions & 1 deletion lib/rules/enforces-shorthand.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ module.exports = {
// Helpers
//----------------------------------------------------------------------

// These are shorthand candidates that do not share the same parent type
const complexEquivalences = [
[["overflow-hidden", "text-ellipsis", "whitespace-nowrap"], "truncate"]
]

// Init assets
const targetProperties = {
Layout: ['Overflow', 'Overscroll Behavior', 'Top / Right / Bottom / Left'],
Expand All @@ -81,7 +86,9 @@ module.exports = {
Borders: ['Border Radius', 'Border Width', 'Border Color'],
Tables: ['Border Spacing'],
Transforms: ['Scale'],
Typography: ['Text Overflow', 'Whitespace']
};

// We don't want to affect other rules by object reference
const cloned = JSON.parse(JSON.stringify(defaultGroups));
const targetGroups = cloned.filter((g) => Object.keys(targetProperties).includes(g.type));
Expand Down Expand Up @@ -210,9 +217,49 @@ module.exports = {
});

const validated = [];

// Handle sets of classnames with different parent types
let remaining = parsed
for (const [inputSet, outputClassname] of complexEquivalences) {
if (remaining.length < inputSet.length) {
continue
}

const parsedElementsInInputSet = remaining.filter(remainingClass => inputSet.some(inputClass => remainingClass.name.includes(inputClass)))

// Make sure all required classes for the shorthand are present
if (parsedElementsInInputSet.length !== inputSet.length) {
continue
}

// Make sure the classes share all the same variants
if (new Set(parsedElementsInInputSet.map(p => p.variants)).size !== 1) {
continue
}

// Make sure the classes share all the same importance
if (new Set(parsedElementsInInputSet.map(p => p.important)).size !== 1) {
continue
}

const index = parsedElementsInInputSet[0].index
const variants = parsedElementsInInputSet[0].variants
const important = parsedElementsInInputSet[0].important ? "!" : ""

const patchedClassname = `${variants}${important}${mergedConfig.prefix}${outputClassname}`
troubles.push([parsedElementsInInputSet.map((c) => `${c.name}`), patchedClassname]);

const validatedClassname = groupUtil.parseClassname(patchedClassname, targetGroups, mergedConfig, index)
validated.push(validatedClassname);

remaining = remaining.filter(p => !parsedElementsInInputSet.includes(p))
}

// Handle sets of classnames with the same parent type

// Each group parentType
const checkedGroups = [];
parsed.forEach((classname) => {
remaining.forEach((classname) => {
// Valid candidate
if (classname.parentType === '') {
validated.push(classname);
Expand Down
84 changes: 84 additions & 0 deletions tests/lib/rules/enforces-shorthand.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,20 @@ ruleTester.run("shorthands", rule, {
</div>
`,
},
{
code: `
<div class="overflow-hidden text-ellipsis hover:whitespace-nowrap">
Possible shorthand available for truncate, but some of the classes have modifiers
</div>
`,
},
{
code: `
<div class="overflow-hidden text-ellipsis !whitespace-nowrap">
Possible shorthand available for truncate, but some of the classes have important
</div>
`,
},
],

invalid: [
Expand Down Expand Up @@ -601,5 +615,75 @@ ruleTester.run("shorthands", rule, {
`,
errors: [generateError(["group/name:rounded-r-full", "group/name:rounded-l-full"], "group/name:rounded-full")],
},
{
code: `
<div class="overflow-hidden text-ellipsis whitespace-nowrap">
Possible shorthand when using truncate
</div>
`,
output: `
<div class="truncate">
Possible shorthand when using truncate
</div>
`,
errors: [generateError(["overflow-hidden", "text-ellipsis", "whitespace-nowrap"], "truncate")],
},
{
code: `
<div class="md:overflow-hidden md:text-ellipsis md:whitespace-nowrap">
Possible shorthand when using truncate with breakpoint
</div>
`,
output: `
<div class="md:truncate">
Possible shorthand when using truncate with breakpoint
</div>
`,
errors: [generateError(["md:overflow-hidden", "md:text-ellipsis", "md:whitespace-nowrap"], "md:truncate")],
},
{
code: `
<div class="hover:overflow-hidden hover:text-ellipsis hover:whitespace-nowrap">
Possible shorthand when using truncate with hover
</div>
`,
output: `
<div class="hover:truncate">
Possible shorthand when using truncate with hover
</div>
`,
errors: [generateError(["hover:overflow-hidden", "hover:text-ellipsis", "hover:whitespace-nowrap"], "hover:truncate")],
},
{
code: `
<div class="hover:sm:!tw-overflow-hidden hover:sm:!tw-text-ellipsis hover:sm:!tw-whitespace-nowrap">
Possible shorthand when using truncate with hover, breakpoint, important and prefix
</div>
`,
output: `
<div class="hover:sm:!tw-truncate">
Possible shorthand when using truncate with hover, breakpoint, important and prefix
</div>
`,
errors: [generateError(["hover:sm:!tw-overflow-hidden", "hover:sm:!tw-text-ellipsis", "hover:sm:!tw-whitespace-nowrap"], "hover:sm:!tw-truncate")],
options: [
{
config: { prefix: "tw-" },
},
],
},
{
code: `
<div class="overflow-hidden text-ellipsis whitespace-nowrap text-white text-xl">
Possible shorthand when using truncate, tested with additional classnames
</div>
`,
output: `
<div class="truncate text-white text-xl">
Possible shorthand when using truncate, tested with additional classnames
</div>
`,
errors: [generateError(["overflow-hidden", "text-ellipsis", "whitespace-nowrap"], "truncate")],
},
],
});