mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
Improve performance of upgrade tool (#18068)
This PR improves the performance of the upgrade tool due to a regression introduced by https://github.com/tailwindlabs/tailwindcss/pull/18057 Essentially, we had to make sure that we are not in `<style>…</style>` tags because we don't want to migrate declarations in there such as `flex-shrink: 0;` The issue with this approach is that we checked _before_ the candidate if a `<style` cold be found and if we found an `</style>` tag after the candidate. We would basically do this check for every candidate that matches. Running this on our Tailwind UI codebase, this resulted in a bit of a slowdown: ```diff - Before: ~13s + After: ~5m 39s ``` ... quite the difference. This is because we have a snapshot file that contains ~650k lines of code. Looking for `<style>` and `</style>` tags in a file that large is expensive, especially if we do it a lot. I ran some numbers and that file contains ~1.8 million candidates. Anyway, this PR fixes that by doing a few things: 1. We will compute the `<style>` and `</style>` tag positions only once per file and cache it. This allows us to re-use this work for every candidate that needs it. 2. We track the positions, which means that we can simply check if a candidate's location is within any of 2 start and end tags. If so, we skip it. Running the numbers now gets us to: ```diff - Before: ~5m 39s + After: ~9s ``` Much better! --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me>
This commit is contained in:
parent
71fb9cdf59
commit
a42251cc29
@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- Upgrade: Do not migrate declarations that look like candidates in `<style>` blocks ([#18057](https://github.com/tailwindlabs/tailwindcss/pull/18057))
|
||||
- Upgrade: Do not migrate declarations that look like candidates in `<style>` blocks ([#18057](https://github.com/tailwindlabs/tailwindcss/pull/18057), [18068](https://github.com/tailwindlabs/tailwindcss/pull/18068))
|
||||
- Upgrade: Improve `pnpm` workspaces support ([#18065](https://github.com/tailwindlabs/tailwindcss/pull/18065))
|
||||
|
||||
## [4.1.7] - 2025-05-15
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { parseCandidate } from '../../../../tailwindcss/src/candidate'
|
||||
import type { DesignSystem } from '../../../../tailwindcss/src/design-system'
|
||||
import { DefaultMap } from '../../../../tailwindcss/src/utils/default-map'
|
||||
import * as version from '../../utils/version'
|
||||
|
||||
const QUOTES = ['"', "'", '`']
|
||||
@ -44,16 +45,23 @@ export function isSafeMigration(
|
||||
// Whitespace before the candidate
|
||||
location.contents[location.start - 1]?.match(/\s/) &&
|
||||
// A colon followed by whitespace after the candidate
|
||||
location.contents.slice(location.end, location.end + 2)?.match(/^:\s/) &&
|
||||
// A `<style` block is present before the candidate
|
||||
location.contents.slice(0, location.start).includes('<style') &&
|
||||
// `</style>` is present after the candidate
|
||||
location.contents.slice(location.end).includes('</style>')
|
||||
location.contents.slice(location.end, location.end + 2)?.match(/^:\s/)
|
||||
) {
|
||||
return false
|
||||
// Compute all `<style>` ranges once and cache it for the current files
|
||||
let ranges = styleBlockRanges.get(location.contents)
|
||||
|
||||
for (let i = 0; i < ranges.length; i += 2) {
|
||||
let start = ranges[i]
|
||||
let end = ranges[i + 1]
|
||||
|
||||
// Check if the candidate is inside a `<style>` block
|
||||
if (location.start >= start && location.end <= end) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let [candidate] = Array.from(parseCandidate(rawCandidate, designSystem))
|
||||
let [candidate] = parseCandidate(rawCandidate, designSystem)
|
||||
|
||||
// If we can't parse the candidate, then it's not a candidate at all. However,
|
||||
// we could be dealing with legacy classes like `tw__flex` in Tailwind CSS v3
|
||||
@ -62,10 +70,10 @@ export function isSafeMigration(
|
||||
// So let's only skip if we couldn't parse and we are not in Tailwind CSS v3.
|
||||
//
|
||||
if (!candidate && version.isGreaterThan(3)) {
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
// Parsed a candidate succesfully, verify if it's a valid candidate
|
||||
// Parsed a candidate successfully, verify if it's a valid candidate
|
||||
else if (candidate) {
|
||||
// When we have variants, we can assume that the candidate is safe to migrate
|
||||
// because that requires colons.
|
||||
@ -168,3 +176,30 @@ export function isSafeMigration(
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Assumptions:
|
||||
// - All `<style` tags appear before the next `</style>` tag
|
||||
// - All `<style` tags are closed with `</style>`
|
||||
// - No nested `<style>` tags
|
||||
const styleBlockRanges = new DefaultMap((source: string) => {
|
||||
let ranges: number[] = []
|
||||
let offset = 0
|
||||
|
||||
while (true) {
|
||||
let startTag = source.indexOf('<style', offset)
|
||||
if (startTag === -1) return ranges
|
||||
|
||||
offset = startTag + 1
|
||||
|
||||
// Ensure the style looks like:
|
||||
// - `<style>` (closed)
|
||||
// - `<style …>` (with attributes)
|
||||
if (!source[startTag + 6].match(/[>\s]/)) continue
|
||||
|
||||
let endTag = source.indexOf('</style>', offset)
|
||||
if (endTag === -1) return ranges
|
||||
offset = endTag + 1
|
||||
|
||||
ranges.push(startTag, endTag)
|
||||
}
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user