Robin Malfait 02cfc45057
Ensure @utility is processed before using them (#15542)
This PR fixes an issue where using an `@utility` before it is defined,
and _if_ that `@utility` contains `@apply`, that it won't result in the
expected output. But results in an `@apply` rule that is not
substituted. Additionally, if you have multiple levels of `@apply`, we
have to make sure that everything is applied (no pun intended) in the
right order.

Right now, the following steps are taken:

1. Collect all the `@utility` at-rules (and register them in the system
as utilities).
2. Substitute `@apply` on the AST (including `@utility`'s ASTs) with the
content of the utility.
3. Delete the `@utility` at-rules such that they are removed from the
CSS output itself.

The reason we do it in this order is because handling `@apply` during
`@utility`
handling means that we could rely on another `@utility` that is defined
later
and therefore the order of the utilities starts to matter. This is not a
bad
thing, but the moment you import multiple CSS files or plugins, this
could
become hard to manage.

Another important step is that when using `@utility foo`, the
implementation creates a `structuredClone` from its AST when first using
the utility. The reason we do that is because `foo` and `foo!` generate
different output and we don't want to accidentally mutate the same AST.
This structured clone is the start of the problem in the linked issue
(#15501).

If we don't do the structured clone, then substituting the `@apply`
rules would work, but then `foo` and `foo!` will generate the same
output, which is bad.

The linked issue has this structure:
```css
.foo {
  @apply bar;
}

@utility bar {
  @apply flex;
}
```

If we follow the steps above, this would substitute `@apply bar` first,
which
results in:

```css
.foo {
  @apply flex;
}
```

But the `bar` utility, was already cloned (and cached) so now we end up
with an `@apply` rule that is not substituted.

To properly solve this problem, we have to make sure that we collect all
the `@apply` at-rules, and apply them in the correct order. To do this,
we run a topological sort on them which ensures that all the
dependencies are applied before substituting the current `@apply`.

This means that in the above example, in order to process `@apply bar`,
we have to process the `bar` utility first.

If we run into a circular dependency, then we will throw an error like
before. You'll notice that the error message in this PR is updated to a
different spot. This one is a bit easier to grasp because it shows the
error where the circular dependency _starts_ not where it _ends_ (and
completes the circle). The previous message was not wrong (since it's a
circle), but now it's a bit easier to reason about.

Fixes: #15501
2025-01-07 15:19:37 +00:00
2024-12-13 15:30:58 +01:00
2025-01-06 11:59:31 +01:00
2024-03-05 14:29:15 +01:00
2024-03-05 14:29:15 +01:00

Tailwind CSS

A utility-first CSS framework for rapidly building custom user interfaces.

Build Status Total Downloads Latest Release License


Documentation

For full documentation, visit tailwindcss.com.

Community

For help, discussion about best practices, or any other conversation that would benefit from being searchable:

Discuss Tailwind CSS on GitHub

For chatting with others using the framework:

Join the Tailwind CSS Discord Server

Contributing

If you're interested in contributing to Tailwind CSS, please read our contributing docs before submitting a pull request.

Description
A utility-first CSS framework for rapid UI development.
Readme 222 MiB
Languages
JavaScript 90.6%
CSS 7.6%
HTML 1.7%