Commit b77971f
authored
Introduce
This PR introduces a new `canonicalizeCandidates` function on the
internal Design System.
The big motivation to moving this to the core `tailwindcss` package is
that we can use this in various places:
- The Raycast extension
- The VS Code extension / language server
- 3rd party tools that use the Tailwind CSS design system APIs
> This PR looks very big, but **I think it's best to go over the changes
commit by commit**. Basically all of these steps already existed in the
upgrade tool, but are now moved to our core `tailwindcss` package.
Here is a list of all the changes:
- Added a new `canonicalizeCandidates` function to the design system
- Moved various migration steps to the core package. I inlined them in
the same file and because of that I noticed a specific pattern (more on
this later).
- Moved `printCandidate` tests to the `tailwindcss` package
- Setup tests for `canonicalizeCandidates` based on the existing tests
in the upgrade tool.
I noticed that all the migrations followed a specific pattern:
1. Parse the raw candidate into a `Candidate[]` AST
2. In a loop, try to migrate the `Candidate` to a new `Candidate` (this
often handled both the `Candidate` and its `Variant[]`)
3. If something changed, print the new `Candidate` back to a string, and
pass it to the next migration step.
While this makes sense in isolation, we are doing a lot of repeated work
by parsing, modifying, and printing the candidate multiple times. This
let me to introduce the `big refactor` commit. This changes the steps
to:
1. Up front, parse the raw candidate into a `Candidate[]` _once_.
2. Strip the variants and the important marker from the candidate. This
means that each migration step only has to deal with the base `utility`
and not care about the variants or the important marker. We can
re-attach these afterwards.
3. Instead of a `rawCandidate: string`, each migration step receives an
actual `Candidate` object (or a `Variant` object).
4. I also split up the migration steps for the `Candidate` and the
`Variant[]`.
All of this means that there is a lot less work that needs to be done.
We can also cache results between migrations. So `[@media_print]:flex`
and `[@media_print]:block` will result in `print:flex` and `print:block`
respectively, but the `[@media_print]` part is only migrated once across
both candidates.
One migration step relied on the `postcss-selector-parser` package to
parse selectors and attribute selectors. I didn't want to introduce a
package just for this, so instead used our own `SelectorParser` in the
migration and wrote a small `AttributeSelectorParser` that can parse the
attribute selector into a little data structure we can work with
instead.
If we want, we can split this PR up into smaller pieces, but since the
biggest chunk is moving existing code around, I think it's fairly doable
to review as long as you go commit by commit.
---
With this new API, we can turn:
```
[
'bg-red-500',
'hover:bg-red-500',
'[@media_print]:bg-red-500',
'hover:[@media_print]:bg-red-500',
'bg-red-500/100',
'hover:bg-red-500/100',
'[@media_print]:bg-red-500/100',
'hover:[@media_print]:bg-red-500/100',
'bg-[var(--color-red-500)]',
'hover:bg-[var(--color-red-500)]',
'[@media_print]:bg-[var(--color-red-500)]',
'hover:[@media_print]:bg-[var(--color-red-500)]',
'bg-[var(--color-red-500)]/100',
'hover:bg-[var(--color-red-500)]/100',
'[@media_print]:bg-[var(--color-red-500)]/100',
'hover:[@media_print]:bg-[var(--color-red-500)]/100',
'bg-(--color-red-500)',
'hover:bg-(--color-red-500)',
'[@media_print]:bg-(--color-red-500)',
'hover:[@media_print]:bg-(--color-red-500)',
'bg-(--color-red-500)/100',
'hover:bg-(--color-red-500)/100',
'[@media_print]:bg-(--color-red-500)/100',
'hover:[@media_print]:bg-(--color-red-500)/100',
'bg-[color:var(--color-red-500)]',
'hover:bg-[color:var(--color-red-500)]',
'[@media_print]:bg-[color:var(--color-red-500)]',
'hover:[@media_print]:bg-[color:var(--color-red-500)]',
'bg-[color:var(--color-red-500)]/100',
'hover:bg-[color:var(--color-red-500)]/100',
'[@media_print]:bg-[color:var(--color-red-500)]/100',
'hover:[@media_print]:bg-[color:var(--color-red-500)]/100',
'bg-(color:--color-red-500)',
'hover:bg-(color:--color-red-500)',
'[@media_print]:bg-(color:--color-red-500)',
'hover:[@media_print]:bg-(color:--color-red-500)',
'bg-(color:--color-red-500)/100',
'hover:bg-(color:--color-red-500)/100',
'[@media_print]:bg-(color:--color-red-500)/100',
'hover:[@media_print]:bg-(color:--color-red-500)/100',
'[background-color:var(--color-red-500)]',
'hover:[background-color:var(--color-red-500)]',
'[@media_print]:[background-color:var(--color-red-500)]',
'hover:[@media_print]:[background-color:var(--color-red-500)]',
'[background-color:var(--color-red-500)]/100',
'hover:[background-color:var(--color-red-500)]/100',
'[@media_print]:[background-color:var(--color-red-500)]/100',
'hover:[@media_print]:[background-color:var(--color-red-500)]/100'
]
```
Into their canonicalized form:
```
[
'bg-red-500',
'hover:bg-red-500',
'print:bg-red-500',
'hover:print:bg-red-500'
]
```
The list is also unique, so we won't end up with `bg-red-500 bg-red-500`
twice.
While the canonicalization itself is fairly fast, we still pay a **~1s**
startup cost for some migrations (once, and cached for the entire
lifetime of the design system). I would like to keep improving the
performance and the kinds of migrations we do, but I think this is a
good start.
The cost we pay is for:
1. Generating a full list of all possible utilities based on the
`getClassList` suggestions API.
2. Generating a full list of all possible variants.
The canonicalization step for this list takes **~2.9ms** on my machine.
Just for fun, if you use the `getClassList` API for intellisense that
generates all the suggestions and their modifiers, you get a list of
**263788** classes. If you canonicalize all of these, it takes
**~500ms** in total. So roughly **~1.9μs** per candidate.
This new API doesn't result in a performance difference for normal
Tailwind CSS builds.
The other potential concern is file size of the package. The generated
`tailwindcss.tgz` file changed like this:
```diff
- 684652 bytes (684.65 kB)
+ 749169 bytes (749.17 kB)
```
So the package increased by ~65 kB which I don't think is the end of the
world, but it is important for the `@tailwindcss/browser` build which we
don't want to grow unnecessarily. For this reason we remove some of the
code for the design system conditionally such that you don't pay this
cost in an environment where you will never need this API.
The `@tailwindcss/browser` build looks like this:
```shell
`dist/index.global.js 255.14 KB` (main)
`dist/index.global.js 272.61 KB` (before this change)
`dist/index.global.js 252.83 KB` (after this change, even smaller than on `main`)
```canonicalizeCandidates on the internal Design System (#19059)1 parent 73628f6 commit b77971f
File tree
37 files changed
+3019
-2417
lines changed- packages
- @tailwindcss-browser
- @tailwindcss-upgrade/src/codemods/template
- tailwindcss/src
- compat
- utils
37 files changed
+3019
-2417
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
16 | 32 | | |
Lines changed: 1 addition & 114 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
| 2 | + | |
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| |||
82 | 82 | | |
83 | 83 | | |
84 | 84 | | |
85 | | - | |
86 | | - | |
87 | | - | |
88 | | - | |
89 | | - | |
90 | | - | |
91 | | - | |
92 | | - | |
93 | | - | |
94 | | - | |
95 | | - | |
96 | | - | |
97 | | - | |
98 | | - | |
99 | | - | |
100 | | - | |
101 | | - | |
102 | | - | |
103 | | - | |
104 | | - | |
105 | | - | |
106 | | - | |
107 | | - | |
108 | | - | |
109 | | - | |
110 | | - | |
111 | | - | |
112 | | - | |
113 | | - | |
114 | | - | |
115 | | - | |
116 | | - | |
117 | | - | |
118 | | - | |
119 | | - | |
120 | | - | |
121 | | - | |
122 | | - | |
123 | | - | |
124 | | - | |
125 | | - | |
126 | | - | |
127 | | - | |
128 | | - | |
129 | | - | |
130 | | - | |
131 | | - | |
132 | | - | |
133 | | - | |
134 | | - | |
135 | | - | |
136 | | - | |
137 | | - | |
138 | | - | |
139 | | - | |
140 | | - | |
141 | | - | |
142 | | - | |
143 | | - | |
144 | | - | |
145 | | - | |
146 | | - | |
147 | | - | |
148 | | - | |
149 | | - | |
150 | | - | |
151 | | - | |
152 | | - | |
153 | | - | |
154 | | - | |
155 | | - | |
156 | | - | |
157 | | - | |
158 | | - | |
159 | | - | |
160 | | - | |
161 | | - | |
162 | | - | |
163 | | - | |
164 | | - | |
165 | | - | |
166 | | - | |
167 | | - | |
168 | | - | |
169 | | - | |
170 | | - | |
171 | | - | |
172 | | - | |
173 | | - | |
174 | | - | |
175 | | - | |
176 | | - | |
177 | | - | |
178 | | - | |
179 | | - | |
180 | | - | |
181 | | - | |
182 | | - | |
183 | | - | |
184 | | - | |
185 | | - | |
186 | | - | |
187 | | - | |
188 | | - | |
189 | | - | |
190 | | - | |
191 | | - | |
192 | | - | |
193 | | - | |
194 | | - | |
195 | | - | |
196 | | - | |
197 | | - | |
0 commit comments