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
DropCSS as an alternative to PurgeCSS #161
Comments
i ran a quick test against PurgeCSS using the html source of https://next.tailwindcss.com/ and
i've attached the pretty-printed results for diffing: output.zip the big caveat here is that due to Tailwind's "interesting" - but technically valid - conventions, DropCSS removes some stuff it shouldn't:
i'll test what perf impact handling these super-odd cases has and get back to you. out of curiosity, what's the history behind these choices rather than using selectors that don't require escaping? |
i got a much easier/faster pre/post-processing solution working. i temporarily re-map the escaped sequences in both the html and css to strings that match // remap
let css2 = css
.replace(/\\\:/gm, '__0')
.replace(/\\\//gm, '__1')
.replace(/\\\?/gm, '__2')
.replace(/\\\(/gm, '__3')
.replace(/\\\)/gm, '__4');
let html2 = html.replace(/class=["'][^"']*["']/gm, m =>
m
.replace(/\:/gm, '__0')
.replace(/\//gm, '__1')
.replace(/\?/gm, '__2')
.replace(/\(/gm, '__3')
.replace(/\)/gm, '__4')
);
res = dropcss({
css: css2,
html: html2,
});
// unmap
res.css = res.css
.replace(/__0/gm, '\\:')
.replace(/__1/gm, '\\/')
.replace(/__2/gm, '\\?')
.replace(/__3/gm, '\\(')
.replace(/__4/gm, '\\)'); new timings:
if you look at the diff between them now (and manually verify against the html), it's pretty scary: the only drawback to this is that the |
Honestly just because it's nicer to read You mention in your Reddit write-up that this project parses HTML — that's honestly the biggest worry for me because the whole reason Purgecss is great is that it doesn't parse HTML, it just extracts a list of tokens from your content file using a simple regex. That makes the mental model for Purgecss really simple — if you want to make sure a class doesn't get accidentally purged, just make sure it appears as a complete string anywhere in your template. All of the purging can be done statically without trying to evaluate every branch of your JavaScript or anything like that. When you're parsing HTML to find classes, how do you handle properly removing unused classes from Vue or React components that do stuff like this? <template>
<div :class="classes"></div>
</template>
<script>
export default {
props: ['active'],
computed: {
classes() {
return this.active ? 'bg-blue-100' : 'bg-gray-200'
}
},
}
</script> |
Good questions. TL;DR: It's quite easy to attain PurgeCSS's functionality using a small DropCSS wrapper, but impossible to do the inverse. The philosophies of PurgeCSS and DropCSS are somewhat different. PurgeCSS is designed to operate [imperfectly] on all sorts of sources via extractors, while DropCSS is designed to operate perfectly on the uniform HTML result. You would not be able to run random input like JSX or Vue files through DropCSS, however this is an easily solved situation. The whole assignment is to build a whitelist to make sure dynamic things missing from the HTML don't get removed. DropCSS provides a If you wanted to take your Vue file and parse all strings out of it into a whitelist, it's rather trivial: https://jsfiddle.net/nt6ge0r9/ let tpl = `
<template>
<div :class="classes"></div>
</template>
<script>
export default {
props: ['active'],
computed: {
classes() {
return this.active ? 'bg-blue-100' : 'bg-gray-200'
}
},
}
<\/script>
`;
let whitelist = [], m;
const STRINGS = /["']([^'"]*)["']/g;
while (m = STRINGS.exec(tpl))
whitelist.push(m[1]);
let html = '<div class="bg-blue-100">Hello World!</div>';
let css = `
.bg-blue-100 {color: blue;}
.bg-gray-200 {color: red;}
`;
let out = dropcss({
html,
css,
shouldDrop: sel =>
!whitelist.some(keepStr =>
sel.indexOf(keepStr != -1)
)
});
console.log(out.css); With this you've essentially put together your own "extractor". |
Yeah cool, so I'd be curious to see what the performance comparison is like if you have to do that anyways. I'd have to do it for every single project I work on because I don't work on vanilla HTML in general. I think the API would have to be a lot more ergonomic for that sort of situation for me to recommend it instead of Purgecss as well. Performance isn't a huge issue because Purgecss is only used in production builds anyways, so if it takes 3s that's really not a problem at all. |
Of course it depends on your inputs, so i have no way of telling you. all i can say is that the numbers i posted above are just for processing the CSS and HTML on https://next.tailwindcss.com/, so you will save at minimum that much, and very likely a lot more once more stuff is thrown in.
I mean, reusable functions are a thing. If you take a look at the difference in the outputs i attached, you'll see that PurgeCSS not only fails to purge a ton of stuff, but also removes things that are very obviously present in the HTML. They end up with the wrong 12.11 KB, while DropCSS ends up with the correct 10.57 KB at 4x the performance. I suspect they'll be chasing obscure extractor bugs for the foreseeable future with their 'simple' regex architecture. If you're unconvinced, feel free to close this issue. At this point I have no arguments (or time) left :). Thanks for the discussion, btw! |
Purgecss isn’t removing anything that it shouldn’t, otherwise we’d have bugs on the site in production that aren’t present in development.
Having to write code that scans my entire code directory, reads all of the files, and extracts all of the tokens based on a regex means I have to rewrite a big chunk of what Purgecss does for me by myself if I wanted to use DropCSS.
It sounds like an awesome tool if your only input is fully rendered HTML but honestly the thing I love most about Purgecss is how “dumb” it is, it makes it very easy to predict the output because the mental model is so simple.
Anyways don’t mean to discourage, awesome job on the project and if you ever decide to support a simple string matching strategy as an option instead of full HTML parsing I’d be happy to try it out. For me the performance benefits are just not important enough to trade off on the developer experience.
…On Apr 12, 2019, 12:30 PM -0400, Leon Sorokin ***@***.***>, wrote:
> Yeah cool, so I'd be curious to see what the performance comparison is like if you have to do that anyways.
Of course it depends on your inputs, so i have no way of telling you. all i can say is that the numbers i posted above are just for processing the CSS and HTML on https://next.tailwindcss.com/, so you will save at minimum that much, and very likely a lot more once more stuff is thrown in.
> I'd have to do it for every single project I work on because I don't work on vanilla HTML in general.
I mean, reusable functions are a thing.
If you take a look at the difference in the outputs i attached, you'll see that PurgeCSS not only fails to purge a ton of stuff, but also removes things that are very obviously present in the HTML. They end up with the wrong 12.11 KB, while DropCSS ends up with the correct 10.57 KB at 4x the performance. I suspect they'll be chasing obscure extractor bugs for the foreseeable future with their 'simple' regex architecture.
If you're unconvinced, feel free to close this issue. At this point I have no arguments (or time) left :).
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
|
no problem :) |
Hey @adamwathan,
I wanted to do a shameless plug for my new CSS purging lib. I wrote it after encountering numerous unresolved/unresolvable issues with PurgeCSS.
https://github.com/leeoniya/dropcss
Recent discussions:
Initial alpha:
v1.0.0 milestone (with more prose):
Feel free to close this issue without comment, too - no feelings will be hurt.
cheers and thanks!
Leon
The text was updated successfully, but these errors were encountered: