sndyuk/mangle-css-class-webpack-plugin

how can I make this plugin support Tailwind screen prefixes ?

vesper8 opened this issue Β· 11 comments

I'm wondering if you could explain how, or if it's even possible, to support Tailwind CSS screen prefixes?

Let me explain, Tailwind lets you set a prefix which is very useful for using with this package. The default prefix is tw-

This is how I configured your plugin right now:

  const cssObfuscatorPlugin = new MangleCssClassPlugin({
    classNameRegExp: '(tw)-[a-zA-Z0-9_-]*',
    log: true,
  });

This works great, it converts all the tailwind classes to 2 or 3 character long classes.. perfect

Tailwind CSS also supports prefixes for responsive websites. It has 5 default prefixes: xs,sm,md,lg,xl

So you can write something like this

<div class="tw-p-2 md:tw-p-4">test</div>

Which translates to

on all devices default to padding: 2rem; but on "medium" devices, aka tablet+desktop, use padding: 4rem;

Doing this

  const cssObfuscatorPlugin = new MangleCssClassPlugin({
    classNameRegExp: '(tw-|xs:tw-|md:tw-|sm:tw-|lg:tw-|xl:tw-|answer-|quiz-)[a-zA-Z0-9_-]*',
    log: true,
  });

Changes

<div class="tw-p-2 md:tw-p-4">test</div>

to

<div class="aa ab">test</div>

But that breaks the prefix support

What I need it to do is change

<div class="tw-p-2 md:tw-p-4">test</div>

to

<div class="aa md:ab">test</div>

So, converting all classes that begin with tw-, but leaving the prefixes in place

According go https://regex101.com/r/herNMh/2

(tw)-[a-zA-Z0-9_-]* should already be matching both tw-p-2 and tw-p-4 in <div class="tw-p-2 md:tw-p-4">test</div>, but it actually ends up ignoring all of md:tw-p-4 when run through your plugin

It's interesting class name form. Let me try it later.

@vesper8 Supported the option(v4.0.7). Please check if it works.

The option will be:

classNameRegExp: '(xs:|md:|sm:|lg:|xl:)?tw-[a-zA-Z0-9_-]*',
ignorePrefix: ['xs:', 'md:', 'sm:', 'lg:', 'xl:'],

@sndyuk thanks a lot for taking this on! It's very close to working now but there's still a hiccup

With your new change, it did indeed make the correct change in the html, changing

<div class="tw-p-2 md:tw-p-4">test</div>

to

<div class="aa md:ab">test</div>

What is still failing however is that in the generated css bundle, tw-p-2 was indeed changed to aa

But md:tw-p-4 was not changed to md:ab

Instead it remained unchanged and looks like this in the minified app.css bundle: .md\:tw-p-4{padding:1rem}

That's probably my bad for not mentioning the syntax inside the bundle until now. I guess the colon is escaped, or maybe your script isn't taking into account the unique class name composition

Now that I have more clarity into what gets added to the bundle css, I can see that there's two ways to handle this, the prefix doesn't really need to survive into the production html/bundle, it's just that because of the uniqueness of the class composition, it wasn't being caught on both ends

I hope I'm making sense! Would really appreciate it if you could make another modification that will

To make everything clearer I created this demo repository https://github.com/vesper8/vue-cli-tailwind-mangle-css-class-webpack-plugin

If you run yarn install and yarn serve you'll see the responsiveness in action, then if you run yarn build and serve the files inside the dist folder you'll see your plugin in action

@vesper8 Thanks! The demo repository deepened my understanding.
To escape the back slash, use both of bellow:

  • For JS: \\\\\\\\\\\\\\\\ . It's required by development mode.
  • For CSS: \\\\ . It's required by production mode.

Also I added the ignorePrefixRegExp option since the ignorePrefix option becomes too long to list up the all prefixes.
I guess the option can be used for tailwind. At least the demo works well:

classNameRegExp: '((hover|focus|xs|md|sm|lg|xl)(\\\\\\\\\\\\\\\\|\\\\)?:)*tw-[a-z_-][a-zA-Z0-9_-]*',
ignorePrefixRegExp: '((hover|focus|xs|md|sm|lg|xl)(\\\\\\\\\\\\\\\\|\\\\)?:)*',

Let me know if it works or not.

@sndyuk beautiful!!! once again thanks for your quick work. I have no doubt this will be used by lots of tailwind CSS users now.. might have to promote it a bit as being tailwind-compatible and including these instructions in the guide. I'll pass the word about too.

It works perfectly now for my current use.

And thanks for catching the hover and focus prefixes I had forgotten about those

However, now that I've visited https://tailwindcss.com/docs/pseudo-class-variants/ I see there are multiple other possible prefixes, all of these:

hover:
focus:
active:
disabled:
visited:
first:
last:
odd:
even:
group-hover:
focus-within:

AND, it's possible to combine any of those above with the 5 (xs, sm, md, lg, xl) responsive prefixes, like so:

sm:hover:tw-text-blue-500
md:visited:tw-text-purple-400

Do you suspect that the script needs further modification to be able to handle this last case?

Lastly, I noticed that class names that include a slash, and there are many such class names (examples https://tailwindcss.com/docs/width), such as .w-2/5, are being mangled to .hd/5, which still correctly matches up with .hd\/5{width:40%} in the generated app.css

So it does work with, but it doesn't shorten/obfuscate the class names quite as much as it could so there's room for more optimization there

@vesper8 This will work for the cases:

a. tw-text
b. tw-text1/5
c. even:tw-text
d. xs:group-hover:even:tw-text-1/5
e. group-hover:xs:even:tw-text-1/5

  classNameRegExp: '((hover|focus|active|disabled|visited|first|last|odd|even|group-hover|focus-within|xs|sm|md|lg|xl)(\\\\\\\\\\\\\\\\|\\\\)?:)*tw-[a-zA-Z0-9_-]*(\/[0-9])?',
  ignorePrefixRegExp: '((hover|focus|active|disabled|visited|first|last|odd|even|group-hover|focus-within|xs|sm|md||lg|xl)(\\\\\\\\\\\\\\\\|\\\\)?:)*',

It'll be great if you could update README for the tailwind CSS users :)

@sndyuk thanks for such fantastic plugin. Since this is a follow-up question after applying the solution you've provided, I'll comment here instead.

I've noticed Tailwind class names with slash is obfuscated to a different name in HTML & CSS (example from https://tailwindcss.com/docs/translate/).

For example, this:

<div class="tw--translate-y-3/4 md:tw--translate-y-3/4">

will transform to this in HTML:

<div class="o md:o">

but this in CSS:

.sfb\/4 {..}
.md\:sfb\/4 {..}

It will be great if the bug is fixed! Looking forward to hear from you soon.

@lowjidian Thank you for the feedback!

The new version(4.0.10) will handle the case with the configuration:

const cssObfuscatorPlugin = new MangleCssClassPlugin({
  classNameRegExp: '((hover|focus|active|disabled|visited|first|last|odd|even|group-hover|focus-within|xs|sm|md|lg|xl)[\\\\]*:)*tw-[a-zA-Z0-9_-]*([\\\\]*\/[0-9]*)?',
  ignorePrefixRegExp: '((hover|focus|active|disabled|visited|first|last|odd|even|group-hover|focus-within|xs|sm|md||lg|xl)[\\\\]*:)*',
  log: true,
});

@sndyuk the new version works perfectly! Thank you so much for your effort! ☺️