Skip to content

Commit

Permalink
feat: support [email protected] (#306)
Browse files Browse the repository at this point in the history
* test(tw3.4.0): Dynamic viewport units

* test(tw3.4.0): New :has() variant

* test(tw3.4.0): Style children with the * variant

* feat: autofix h-* w-* becomes size-* shorthand

* 3.14.0-beta.0

* fix: support for contradicting sizes

* feat: new text-wrap utilities

* feat: Subgrid support

* test: Extended min-width, max-width, and min-height scales

* test: Extended opacity scale

* test: Extended grid-rows-* scale

* test: New forced-colors variant

* test: Forced Color Adjust

* 3.14.0-beta.1
  • Loading branch information
francoismassart authored Jan 15, 2024
1 parent 3ee4698 commit 0486628
Show file tree
Hide file tree
Showing 10 changed files with 260 additions and 99 deletions.
12 changes: 12 additions & 0 deletions lib/config/groups.js
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,10 @@ module.exports.groups = [
type: 'Max-Height',
members: 'max\\-h\\-(?<value>${maxHeight})',
},
{
type: 'Size',
members: 'size\\-(?<value>${size})',
},
],
},
{
Expand Down Expand Up @@ -594,6 +598,10 @@ module.exports.groups = [
members: 'overflow\\-(ellipsis|clip)',
deprecated: true,
},
{
type: 'Text Wrap',
members: 'text\\-(wrap|nowrap|balance|pretty)',
},
{
type: 'Text Indent',
members: '(indent\\-(?<value>${textIndent})|\\-indent\\-(?<negativeValue>${-textIndent}))',
Expand Down Expand Up @@ -1341,6 +1349,10 @@ module.exports.groups = [
type: 'Screen Readers',
members: '(not\\-)?sr\\-only',
},
{
type: 'Forced Color Adjust',
members: 'forced\\-color\\-adjust\\-(auto|none)',
},
],
},
{
Expand Down
139 changes: 85 additions & 54 deletions lib/rules/enforces-shorthand.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,18 +75,28 @@ module.exports = {

// These are shorthand candidates that do not share the same parent type
const complexEquivalences = [
[["overflow-hidden", "text-ellipsis", "whitespace-nowrap"], "truncate"]
]
{
needles: ['overflow-hidden', 'text-ellipsis', 'whitespace-nowrap'],
shorthand: 'truncate',
mode: 'exact',
},
{
needles: ['w-', 'h-'],
shorthand: 'size-',
mode: 'value',
},
];

// Init assets
const targetProperties = {
Layout: ['Overflow', 'Overscroll Behavior', 'Top / Right / Bottom / Left'],
'Flexbox & Grid': ['Gap'],
Spacing: ['Padding', 'Margin'],
Sizing: ['Width', 'Height'],
Borders: ['Border Radius', 'Border Width', 'Border Color'],
Tables: ['Border Spacing'],
Transforms: ['Scale'],
Typography: ['Text Overflow', 'Whitespace']
Typography: ['Text Overflow', 'Whitespace'],
};

// We don't want to affect other rules by object reference
Expand Down Expand Up @@ -219,53 +229,79 @@ module.exports = {
const validated = [];

// Handle sets of classnames with different parent types
let remaining = parsed
for (const [inputSet, outputClassname] of complexEquivalences) {
let remaining = parsed;
for (const { needles: inputSet, shorthand: outputClassname, mode } 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
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
}
// Matching classes
const parsedElementsInInputSet = remaining.filter((remainingClass) => {
if (mode === 'exact') {
// Test if the name contains the target class, eg. 'text-ellipsis' inside 'md:text-ellipsis'...
return inputSet.some((inputClass) => remainingClass.name.includes(inputClass));
}
// Test if the body of the class matches, eg. 'h-' inside 'h-10'
if (mode === 'value') {
return inputSet.some((inputClassPattern) => inputClassPattern === remainingClass.body);
}
});

const index = parsedElementsInInputSet[0].index
const variants = parsedElementsInInputSet[0].variants
const important = parsedElementsInInputSet[0].important ? "!" : ""
const variantGroups = new Map();
parsedElementsInInputSet.forEach((o) => {
const val = mode === 'value' ? o.value : '';
const v = `${o.variants}${o.important ? '!' : ''}${val}`;
if (!variantGroups.has(v)) {
variantGroups.set(
v,
parsedElementsInInputSet.filter(
(c) => c.variants === o.variants && c.important === o.important && (val === '' || c.value === val)
)
);
}
});
const validKeys = new Set();
variantGroups.forEach((classes, key) => {
let skip = false;
// Make sure all required classes for the shorthand are present
if (classes.length < inputSet.length) {
skip = true;
}
// Make sure the classes share all the single/shared/same value
if (mode === 'value' && new Set(classes.map((p) => p.value)).size !== 1) {
skip = true;
}
if (!skip) {
validKeys.add(key);
}
});
validKeys.forEach((k) => {
const candidates = variantGroups.get(k);
const index = candidates[0].index;
const variants = candidates[0].variants;
const important = candidates[0].important ? '!' : '';
const classValue = mode === 'value' ? candidates[0].value : '';

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

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

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

// Handle sets of classnames with the same parent type

// Each group parentType
const checkedGroups = [];
remaining.forEach((classname) => {
remaining.forEach((classname, idx, arr) => {
// Valid candidate
if (classname.parentType === '') {
validated.push(classname);
} else if (!checkedGroups.includes(classname.parentType)) {
checkedGroups.push(classname.parentType);
const sameType = parsed.filter((cls) => cls.parentType === classname.parentType);
const sameType = remaining.filter((cls) => cls.parentType === classname.parentType);
// Comparing same parentType classnames
const checkedVariantsValue = [];
sameType.forEach((cls) => {
Expand Down Expand Up @@ -404,27 +440,22 @@ module.exports = {
}
}

troubles
.filter((trouble) => {
// Only valid issue if there are classes to replace
return trouble[0].length;
})
.forEach((issue) => {
if (originalClassNamesValue !== validatedClassNamesValue) {
validatedClassNamesValue = prefix + validatedClassNamesValue + suffix;
context.report({
node: node,
messageId: 'shorthandCandidateDetected',
data: {
classnames: issue[0].join(', '),
shorthand: issue[1],
},
fix: function (fixer) {
return fixer.replaceTextRange([start, end], validatedClassNamesValue);
},
});
}
});
troubles.forEach((issue) => {
if (originalClassNamesValue !== validatedClassNamesValue) {
validatedClassNamesValue = prefix + validatedClassNamesValue + suffix;
context.report({
node: node,
messageId: 'shorthandCandidateDetected',
data: {
classnames: issue[0].join(', '),
shorthand: issue[1],
},
fix: function (fixer) {
return fixer.replaceTextRange([start, end], validatedClassNamesValue);
},
});
}
});
};

//----------------------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions lib/util/groupMethods.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ function generateOptions(propName, keys, config, isNegative = false) {
case 'height':
case 'lineHeight':
case 'maxHeight':
case 'size':
case 'maxWidth':
case 'minHeight':
case 'minWidth':
Expand Down
54 changes: 26 additions & 28 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 0486628

Please sign in to comment.