* Fix context reuse test
* Don't update files with at-apply when content changes
* Prevent at-apply directives from creating new contexts
* Rework apply to use local postcss root
We were storing user CSS in the context so we could use it with apply. The problem is that this CSS does not get updated on save unless it has a tailwind directive in it resulting in stale apply caches. This could result in either stale generation or errors about missing classes.
* Don’t build local cache unless `@apply` is used
* Update changelog
* Replace chalk with picocolors
Already used in latest postcss, autoprefixer and browserslist versions.
See https://github.com/alexeyraspopov/picocolors
* Update `dim` function
Co-authored-by: Brad Cornes <bradlc41@gmail.com>
* Preserve source maps for `@apply`
* Overwrite the source for all cloned descendants
* Preserve source maps when expanding defaults
* Verify that source maps are correctly generated
* Update changelog
* Prevent nesting plugin from breaking other plugins
This uses a private API but it’s the only solution we have right now. It’s guarded to hopefully be less breaking if the API disappears.
* Update changelog
This would be better as a symbol but the stringy-ness of class candidates is fairly well baked into assumptions across the codebase. Using `new String` with a well placed check seems to solve the problem.
* add prettier-plugin-tailwindcss
This will use the prettier plugin in our tests as well, yay consistency!
* ensure that both `group` and `peer` can't be used in `@apply`
This was only configured for `group`
* expose `sortClassList` on the context
This function will be used by the `prettier-plugin-tailwindcss` plugin,
this way the sorting happens within Tailwind CSS itself adn the
`prettier-plugin-tailwindcss` plugin doesn't have to use internal /
private APIs.
The signature looks like this:
```ts
function sortClassList(classes: string[]): string[]
```
E.g.:
```js
let sortedClasses = context.sortClassList(['p-1', 'm-1', 'container'])
```
* update changelog
* add sort test for utilities with the important modifier e.g.: `!p-4`
* Add failing tests for negative utility detection
We're not generating them properly in all cases, when using at-apply we sometimes crash, and safelisting doesn't currently work as expected.
* Refactor
* Generate utilities for negatives before and after the prefix
* Properly detect negative utilities with prefixes in the safelist
* Refactor test a bit
* Add class list tests
* Update changelog
* quick fix for incorrect arbitrary properties
Turns out that using links like [https://example.com] causes arbitrary
properties to generate invalid css.
This is a very dirty quick fix for this specific case, so we have to fix
this properly!
* update changelog
* ensure content files are available in config
If you use the cli with the `--content` option, then we first resolve
the config (empty), then add the `content` to the config. The issue is
that this means that the content will be empty when you resolve it
initially. This results in a warning in your terminal.
Now, we will make sure to merge 2 configs if you have the `--content`
data passed. We will also make sure to override the final
`config.content.files` to whatever you passed in to make sure that this
is the same behaviour as before.
* update changelog
* ensure that we compile the postcss nesting plugin
* re-add optional chaining
This will allow us to be 100% sure that we can safely call
hasOwnProperty in case we get some very strange objects.
This will now also be compiled/transpiled by esbuild.
* import the internal postcss nesting plugin
This allows us to work on it without re-compiling while running tests.
Just like we do with all other code.
* update changelog
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
* improve extractCandidates
When we have a css rule that is defined as `.foo, .bar {}`, then we will
crawl each selector and link it to the same node. This is useful because
now our Map looks something like this:
```js
Map(2) { 'foo' => Node {}, 'bar' => Node {} }
```
This allows us to later on `@apply foo` or `@apply bar` and we can do a
direct lookup for this "candidate".
When we have css defined as `span {}`, then we consider this
"non-ondemandable". This means that we will _always_ inject these rules
into the `*` section and call it a day.
However, it could happen that you have something like this: `span, .foo
{}` up until now this was totally fine. It contains a non-ondemandable
selector (`span`) and therefore we injected this into that `*` section.
However, the issue occurs if you now try to `@apply foo`. Since we had
an early return for this use case it didn't endup in our Map from above
and now you get an error like:
```
The `foo` class does not exist. If `foo` is a custom class, make sure it
is defined within a `@layer` directive."
```
So instead what we will do is keep track whether or not a css rule
contains any on-demandable classes. If this is the case then we still
generate it always by putting it in that `*` section. However, we will
still register all on-demandable classes in our Map (in this case `.foo`).
This allows us to `@apply foo` again!
* update changelog
* ignore content files that don't exist
PostCSS CLI will give you a fake file path that ends in /stdin if you
are reading from stdin.
* update changelog
* use outputFile instead of direct writeFile
This is an improvement we introduced earlier but forgot this part.
* allow to pipe in data to the CLI
* add integration tests to validate piping to the CLI
* update changelog
* fix(cli): avoid write same output when no changes
* generalize outputFile
This function will check a cache, it will only write the file if:
- The modified timestamps changed since last time we wrote something.
This is useful to know if something changed by another tool or
manually without diffing the full file.
- The contents changed.
* further simplify checks
Turns out that reading files and comparing them is fairly fast and there
is no huge benefit over only using the Stats of the file and keeping
track of that information.
Thanks @kentcdodds!
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
* Always include css with apply in context
* Use let
We use it more consistently
* Remove early return
To match the style of the surrounding code
* Don't return layer directives
They do not need to be returned here. If it's needed in the future its easy enough to add it back.
* Use let
* Update changelog
* fix typo
And re-format comments
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
* improve collapsing of duplicate properties
In theory, we don't have to do anything because the browser is smart
enough to figure everything out. However, leaving in duplicate
properties is not that ideal for file size.
Our previous method was pretty simple: if you see a declaration you
already saw in this rule, delete the previous one and keep the current
one.
This works pretty well, but this gets rid of **all** the declarations
with the same property. This is not great for overrides for older
browsers.
In a perfect world, we can handle this based on your target browser but
this is a lot of unnecessary complexity and will slow things down
performance wise.
Alternative, we improved the solution by being a bit smarter:
1. Delete duplicate declarations that have the same property and value
(this will get rid of **exact** duplications).
2. Delete declarations with the same property and the same **unit**.
This means that we will reduce this:
```css
.example {
height: 50%;
height: 100px;
height: 20vh;
height: 30%;
height: 50px;
height: 30vh;
transform: var(--value);
transform: var(--value);
}
```
To:
```diff-css
.example {
- height: 50%; /* Another height exists later with a `%` unit */
- height: 100px; /* Another height exists later with a `px` unit */
- height: 20vh; /* Another height exists later with a `vh` unit */
height: 30%;
height: 50px;
height: 30vh;
- transform: var(--value); /* Value is too complex, but is **exactly** the same as the one below */
transform: var(--value);
}
```
This will reduce the values that we are 100% sure that can be safely
removed. This will still result in some overrides but the browser can
handle those for you.
Fixes: #6844
* update changelog
* ensure apply of rule inside atrule works
If that atrule happens to contain another rule that is technically
unrelated.
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
* update changelog
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
* ensure that strangely used jsx with interpolation gets detected
Co-authored-by: Luke Warlow <projects@warlow.dev>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
* update changelog
Co-authored-by: Luke Warlow <projects@warlow.dev>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>