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
7 changes: 5 additions & 2 deletions lib/rules/no-custom-classname.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const parserUtil = require('../util/parser');
const getClassnamesFromCSS = require('../util/cssFiles');
const createContextFallback = require('tailwindcss/lib/lib/setupContextUtils').createContext;
const generated = require('../util/generated');
const escapeRegex = require('../util/regex').escapeRegex;

//------------------------------------------------------------------------------
// Rule Definition
Expand All @@ -26,7 +27,8 @@ const CUSTOM_CLASSNAME_DETECTED_MSG = `Classname '{{classname}}' is not a Tailwi
// Group/peer names can be arbitrarily named and are not
// generated by generateRules. Using a custom regexp to
// validate these avoids false reports.
const GROUP_NAME_REGEXP = /^(group|peer)\/[\w\$\#\@\%\^\&\*\_\-]+$/i;
const getGroupNameRegex = (prefix = '') =>
new RegExp(`^${escapeRegex(prefix)}(group|peer)\/[\\w\\$\\#\\@\\%\\^\\&\\*\\_\\-]+$`, 'i');

const contextFallbackCache = new WeakMap();

Expand Down Expand Up @@ -110,6 +112,7 @@ module.exports = {
// Init assets before sorting
const groups = groupUtil.getGroups(defaultGroups, mergedConfig);
const classnamesFromFiles = getClassnamesFromCSS(cssFiles, cssFilesRefreshRate);
const groupNameRegex = getGroupNameRegex(mergedConfig.prefix);

/**
* Parse the classnames and report found conflicts
Expand All @@ -134,7 +137,7 @@ module.exports = {
if (fromFilesIdx >= 0) {
return; // Lazier is faster... processing next className!
}
if (GROUP_NAME_REGEXP.test(className)) {
if (groupNameRegex.test(className)) {
return; // Lazier is faster... processing next className!
}

Expand Down
13 changes: 13 additions & 0 deletions lib/util/regex.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* Escapes a string to be used in a regular expression
* Copied from https://stackoverflow.com/a/3561711.
* @param {string} input
* @returns {string}
*/
function escapeRegex(input) {
return input.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&');
}

module.exports = {
escapeRegex,
};
44 changes: 44 additions & 0 deletions tests/lib/rules/no-custom-classname.js
Original file line number Diff line number Diff line change
Expand Up @@ -877,14 +877,42 @@ ruleTester.run("no-custom-classname", rule, {
],
},
{ code: `<div className="group/edit">Custom group name</div>` },
{
code: `<div className="tw-group/edit">Prefix and custom group name</div>`,
options: [
{
config: { prefix: "tw-" },
},
],
},
{ code: `<div className="group-hover/edit:hidden">Custom group name variant</div>` },
{
code: `<div className="group-hover/edit:tw-hidden">Prefix and custom group name variant</div>`,
options: [
{
config: { prefix: "tw-" },
},
],
},
{
code: `
<div class="group/nav123$#@%^&*_-">
<div class="group-hover/nav123$#@%^&*_-:h-0">group/nav123$#@%^&*_-</div>
</div>
`,
},
{
code: `
<div class="tw-group/nav123$#@%^&*_-">
<div class="group-hover/nav123$#@%^&*_-:tw-h-0">group/nav123$#@%^&*_-</div>
</div>
`,
options: [
{
config: { prefix: "tw-" },
},
],
},
{
code: `<div className="group-hover/edit:custom-class">Custom group name variant</div>`,
options: [
Expand All @@ -894,7 +922,23 @@ ruleTester.run("no-custom-classname", rule, {
],
},
{ code: `<div className="peer/draft">Custom peer name</div>` },
{
code: `<div className="tw-peer/draft">Prefix and custom peer name</div>`,
options: [
{
config: { prefix: "tw-" },
},
],
},
{ code: `<div className="peer-checked/draft:hidden">Custom peer name variant</div>` },
{
code: `<div className="peer-checked/draft:tw-hidden">Prefix and custom peer name variant</div>`,
options: [
{
config: { prefix: "tw-" },
},
],
},
{
code: `<div className="peer-checked/draft:custom-class">Custom peer name variant</div>`,
options: [
Expand Down