Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support enforcing truncate shorthand #255

Merged
merged 1 commit into from
Jun 24, 2023
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
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")],
},
],
});