Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
While debugging issue #5765 we noticed that there are bigger underlying fundamental problems with the fact that
prefix
can be a function a JIT world. Instead we only allow a string prefix, so you either prefix everything or nothing.
Let's dive in a little deeper why this is a problem.
For starters, the
prefix
function gets a selector, like.text-center
, then in the function you can return the prefixbased on this information. In AOT mode (the default mode in a pre-JIT world), we generated all the utilities first, this
allowed us to call this function while generating the utilities.
However in JIT mode, we start from the content files. This means that we don't have anything to run through that prefix
function (yet).
So, what do we do currently with the
prefix
, because it seems to work without a function? What we do is when we calladdUtilities
(think of it as "static"/"hardcoded" utilities) then we already know the selector and the prefix, so wecan properly prefix this identifier and store it in the context. Perfect.
However, when we call
matchUtilities
(think of it as plugins that read from your config and allow for arbitraryvalues), then we only know a "base" part, for example
bg
. This means that we have a backwards compatibility problemfor people that had something like this (hopefully nobody did this, but I wouldn't be surprised with our user base):
What will happen in this case is that we will call
prefix()
with.bg
, because this is what we defined inmatchUtilities
and this is the only thing we know upfront in JIT mode land.This also means that we can't really differentiate between arbitrary values for example
PREFIXA-bg-[#0088cc]
vsPREFIXB-bg-[#eee]
. We probably don't need to be able to differentiate between those, but if you do want thisbehaviour, then we can't really support this.
Until this point, we only registered the plugins with a potential prefix or not. But there is an even more complex part.
The complex part is in
resolveMatchedPlugins
(./src/lib/generateRules.js). This is the place where we need to find thecorrect plugins for the current candidate.
Imagine your candidate looks like this
tw-text-center
, we then can do a direct lookup becausetext-center
is anaddUtilities
plugin, and thereforetw-text-center
will exist because we already prefixed it upfront. The annoyingpart is if you have something like
tw-bg-red-500
, that won't exist in the cache,tw-bg
will exist because this is amatchUtilities
plugin. At this point, the only way to look this value up is by parsing the candidate piece by piecelike
tw-bg-red-500
, thentw-bg-red
, thentw-bg
to find the corresponding plugin. This already works, and this iswhat we do currently.
However, we recently wanted to support proper negative values, e.g.:
-tw-ml-3
andtw--ml-3
at the same time. Thestored value in the lookup table is
tw-ml
. But we need to find a way to determine the actual prefix (but we don't knowthe selector) so that we can remove it from the selector in order to detect the
negative
value as well.-tw-ml-3
andtw--ml-3
both should look for thetw-ml
plugin, and know that it is negative. In case of-tw-ml-3
, it isrelatively straight forward, it starts with a
-
so therefore we know that we only have to processtw-ml-3
. Now wecan start looking up
tw-ml-3
, thentw-ml
not too bad.In case of
tw--ml-3
, it doesn't start with-
, so we have to do a bit more parsing. If we use the same algorithm asabove, then we would look for
tw--ml-3
, thentw--ml
, thentw-
maybe?We won't find a match in this case. Remember we don't know what the prefix is because we can only know it if we know the
base class
ml-3
. We can assume that--
is invalid and that one of those dashes is part of thenegative
and theother is part of the
prefix
.Now imagine that your prefix was
tw--
, ...Alright, so there is a lot of complex computation going on which is probably not worth it at all. The biggest use case
we saw for a
prefix
function, is so that you can only prefix some classes. For example, to prevent conflicting classeswith existing frameworks you might be using. This can be solved by applying the prefix to every utility, this does solve
the conflicts, but it might be a bit annoying in the beginning to convert existing classes.
Long story short, we are going to remove the prefix as a function. Then, if people have actual issues, then we will
try and solve those issues in a JIT world where we can drastically simplify this.
Closes: #5765