Skip to content

Commit 13e4cdb

Browse files
committed
prepare and cleanup raw candidates
This is used in 2 spots: 1. When handling prefixes 2. When modernizing values The issues this tries to solve is to ensure that valid v3 candidates but invalid v4 candidates are migrated correctly. E.g.: `group-[]:flex` is valid in v3, but not in v4 (it won't parse). With this workaround, it is parsing correctly.
1 parent e25d2dc commit 13e4cdb

File tree

3 files changed

+32
-29
lines changed

3 files changed

+32
-29
lines changed

packages/@tailwindcss-upgrade/src/template/candidates.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,28 @@ import type { Candidate, Variant } from '../../../tailwindcss/src/candidate'
33
import type { DesignSystem } from '../../../tailwindcss/src/design-system'
44
import * as ValueParser from '../../../tailwindcss/src/value-parser'
55

6+
// Some valid candidates in v3 won't parse in v4. E.g.: `group-[]:flex`
7+
// Some of these candidates can be migrated, but that means that they should be
8+
// able to be parsed in v4. This function prepares a raw candidate for parsing
9+
// by replacing invalid parts with a placeholder.
10+
export function prepareRawCandidate(input: string) {
11+
// Empty arbitrary values don't parse anymore. This is a little bit of a hack
12+
// to work around that behavior so we can still perform the migration:
13+
input = input.replaceAll('-[]:', '-[--tw-custom-placeholder]:') // End of variant
14+
input = input.replaceAll('-[]/', '-[--tw-custom-placeholder]/') // With modifier
15+
input = input.replaceAll('/[]:', '/[--tw-custom-placeholder]:') // Empty modifier
16+
return input
17+
}
18+
19+
// This does the cleanup from `prepareRawCandidate` in case we couldn't migrate
20+
// the candidate.
21+
export function cleanupCandidate(input: string) {
22+
input = input.replaceAll('-[--tw-custom-placeholder]:', '-[]:') // End of variant
23+
input = input.replaceAll('-[--tw-custom-placeholder]/', '-[]/') // With modifier
24+
input = input.replaceAll('/[--tw-custom-placeholder]:', '/[]:') // Empty modifier
25+
return input
26+
}
27+
628
export async function extractRawCandidates(
729
content: string,
830
extension: string = 'html',

packages/@tailwindcss-upgrade/src/template/codemods/modernize-arbitrary-values.ts

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { parseCandidate, type Candidate, type Variant } from '../../../../tailwi
33
import type { Config } from '../../../../tailwindcss/src/compat/plugin-api'
44
import type { DesignSystem } from '../../../../tailwindcss/src/design-system'
55
import { isPositiveInteger } from '../../../../tailwindcss/src/utils/infer-data-type'
6-
import { printCandidate } from '../candidates'
6+
import { cleanupCandidate, prepareRawCandidate, printCandidate } from '../candidates'
77

88
function memcpy<T extends object, U extends object | null>(target: T, source: U): U {
99
// Clear out the target object, otherwise inspecting the final object will
@@ -18,13 +18,7 @@ export function modernizeArbitraryValues(
1818
_userConfig: Config,
1919
rawCandidate: string,
2020
): string {
21-
// Empty arbitrary values don't parse anymore. This is a little bit of a hack
22-
// to work around that behavior so we can still perform the migration:
23-
if (rawCandidate.includes('group-')) {
24-
rawCandidate = rawCandidate.replaceAll('-[]:', '-[--tw-custom-placeholder]:') // End of variant
25-
rawCandidate = rawCandidate.replaceAll('-[]/', '-[--tw-custom-placeholder]/') // With modifier
26-
rawCandidate = rawCandidate.replaceAll('/[]:', '/[--tw-custom-placeholder]:') // Empty modifier
27-
}
21+
rawCandidate = prepareRawCandidate(rawCandidate)
2822

2923
for (let candidate of parseCandidate(rawCandidate, designSystem)) {
3024
let clone = structuredClone(candidate)
@@ -386,16 +380,7 @@ export function modernizeArbitraryValues(
386380
}
387381
}
388382

389-
let newCandidate = changed ? printCandidate(designSystem, clone) : rawCandidate
390-
391-
// Empty arbitrary values don't parse anymore. This is a little bit of a hack
392-
// to work around that behavior so we can still perform the migration:
393-
if (newCandidate.includes('group-')) {
394-
newCandidate = newCandidate.replaceAll('-[--tw-custom-placeholder]:', '-[]:') // End of variant
395-
newCandidate = newCandidate.replaceAll('-[--tw-custom-placeholder]/', '-[]/') // With modifier
396-
newCandidate = newCandidate.replaceAll('/[--tw-custom-placeholder]:', '/[]:') // Empty modifier
397-
}
398-
return newCandidate
383+
return cleanupCandidate(changed ? printCandidate(designSystem, clone) : rawCandidate)
399384
}
400385

401386
return rawCandidate

packages/@tailwindcss-upgrade/src/template/codemods/prefix.ts

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { parseCandidate, type Candidate } from '../../../../tailwindcss/src/cand
22
import type { Config } from '../../../../tailwindcss/src/compat/plugin-api'
33
import type { DesignSystem } from '../../../../tailwindcss/src/design-system'
44
import { segment } from '../../../../tailwindcss/src/utils/segment'
5-
import { printCandidate } from '../candidates'
5+
import { cleanupCandidate, prepareRawCandidate, printCandidate } from '../candidates'
66

77
export function prefix(
88
designSystem: DesignSystem,
@@ -11,15 +11,9 @@ export function prefix(
1111
): string {
1212
if (!designSystem.theme.prefix) return rawCandidate
1313

14-
// Empty arbitrary values don't parse anymore. This is a little bit of a hack
15-
// to work around that behavior so we can still perform the migration:
16-
if (rawCandidate.includes('group-')) {
17-
rawCandidate = rawCandidate.replaceAll('-[]:', '-[--tw-custom-placeholder]:') // End of variant
18-
rawCandidate = rawCandidate.replaceAll('-[]/', '-[--tw-custom-placeholder]/') // With modifier
19-
rawCandidate = rawCandidate.replaceAll('/[]:', '/[--tw-custom-placeholder]:') // Empty modifier
20-
}
14+
let cleanedRawCandidate = prepareRawCandidate(rawCandidate)
2115

22-
let v3Base = extractV3Base(designSystem, userConfig, rawCandidate)
16+
let v3Base = extractV3Base(designSystem, userConfig, cleanedRawCandidate)
2317

2418
if (!v3Base) return rawCandidate
2519

@@ -30,7 +24,9 @@ export function prefix(
3024
designSystem.theme.prefix = null
3125

3226
let unprefixedCandidate =
33-
rawCandidate.slice(0, v3Base.start) + v3Base.base + rawCandidate.slice(v3Base.end)
27+
cleanedRawCandidate.slice(0, v3Base.start) +
28+
v3Base.base +
29+
cleanedRawCandidate.slice(v3Base.end)
3430

3531
// Note: This is not a valid candidate in the original DesignSystem, so we
3632
// can not use the `DesignSystem#parseCandidate` API here or otherwise this
@@ -45,7 +41,7 @@ export function prefix(
4541

4642
if (!candidate) return rawCandidate
4743

48-
return printCandidate(designSystem, candidate)
44+
return cleanupCandidate(printCandidate(designSystem, candidate))
4945
}
5046

5147
// Parses a raw candidate with v3 compatible prefix syntax. This won't match if

0 commit comments

Comments
 (0)