Skip to content

Commit 9cb3899

Browse files
Upgrade deprecated order-none to order-0 (#18126)
This PR adds an initial version for deprecated utilities. Right now it's hardcoded to just the `order-none` utility. This means that `order-0` and `order-[0]` will not be migrated to `order-none` anymore. We did that automatically because we prefer named utilities over bare values and arbitrary values. With this PR, `order-none` is ignored. Similarly, `order-none` will be migrated to `order-0` instead (defined in a separate migration for deprecated values). Made it a new migration instead of using the legacy migration because there all utilities still exist, but are defined differently (e.g.: `shadow`, `shadow-sm`, `shadow-xs`). This PR is also an initial version, it doesn't add any form of `deprecated` flag or feature on a per-utility implementation basis. This therefor has the side effect that if you have a custom `order-none` defined, that it will also be ignored during migrations. ## Test plan 1. Added tests to ensure the `order-0` is not migrated to `order-none` 2. Added tests to ensure `order-none` is migrated to `order-0` (if it's safe to do so, the signature is still computed to ensure the output is the same). 3. Ran this on the Tailwind Plus codebase and ensured that `order-0` is not migrated to `order-none` and that `order-none` is migrated to `order-0`. --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me>
1 parent ed3cecd commit 9cb3899

File tree

8 files changed

+107
-2
lines changed

8 files changed

+107
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
- 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))
1313
- Upgrade: Improve `pnpm` workspaces support ([#18065](https://github.com/tailwindlabs/tailwindcss/pull/18065))
14+
- Upgrade: Migrate deprecated `order-none` to `order-0` ([#18126](https://github.com/tailwindlabs/tailwindcss/pull/18126))
1415
- Support Leptos `class:` attributes when extracting classes ([#18093](https://github.com/tailwindlabs/tailwindcss/pull/18093))
1516

1617
## [4.1.7] - 2025-05-15

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,11 @@ export function parseCandidate(designSystem: DesignSystem, input: string) {
3333
: input,
3434
)
3535
}
36+
37+
export function printUnprefixedCandidate(designSystem: DesignSystem, candidate: Candidate) {
38+
let candidateString = designSystem.printCandidate(candidate)
39+
40+
return designSystem.theme.prefix && candidateString.startsWith(`${designSystem.theme.prefix}:`)
41+
? candidateString.slice(designSystem.theme.prefix.length + 1)
42+
: candidateString
43+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import type { Config } from '../../../../tailwindcss/src/compat/plugin-api'
2+
import type { DesignSystem } from '../../../../tailwindcss/src/design-system'
3+
import type { Writable } from '../../utils/types'
4+
import { baseCandidate, parseCandidate, printUnprefixedCandidate } from './candidates'
5+
import { computeUtilitySignature } from './signatures'
6+
7+
const DEPRECATION_MAP = new Map([['order-none', 'order-0']])
8+
9+
export async function migrateDeprecatedUtilities(
10+
designSystem: DesignSystem,
11+
_userConfig: Config | null,
12+
rawCandidate: string,
13+
): Promise<string> {
14+
let signatures = computeUtilitySignature.get(designSystem)
15+
16+
for (let readonlyCandidate of designSystem.parseCandidate(rawCandidate)) {
17+
// The below logic makes use of mutation. Since candidates in the
18+
// DesignSystem are cached, we can't mutate them directly.
19+
let candidate = structuredClone(readonlyCandidate) as Writable<typeof readonlyCandidate>
20+
21+
// Create a basic stripped candidate without variants or important flag. We
22+
// will re-add those later but they are irrelevant for what we are trying to
23+
// do here (and will increase cache hits because we only have to deal with
24+
// the base utility, nothing more).
25+
let targetCandidate = baseCandidate(candidate)
26+
let targetCandidateString = printUnprefixedCandidate(designSystem, targetCandidate)
27+
28+
let replacementString = DEPRECATION_MAP.get(targetCandidateString) ?? null
29+
if (replacementString === null) return rawCandidate
30+
31+
let legacySignature = signatures.get(targetCandidateString)
32+
if (typeof legacySignature !== 'string') return rawCandidate
33+
34+
let replacementSignature = signatures.get(replacementString)
35+
if (typeof replacementSignature !== 'string') return rawCandidate
36+
37+
// Not the same signature, not safe to migrate
38+
if (legacySignature !== replacementSignature) return rawCandidate
39+
40+
let [replacement] = parseCandidate(designSystem, replacementString)
41+
42+
// Re-add the variants and important flag from the original candidate
43+
return designSystem.printCandidate(
44+
Object.assign(structuredClone(replacement), {
45+
variants: candidate.variants,
46+
important: candidate.important,
47+
}),
48+
)
49+
}
50+
51+
return rawCandidate
52+
}

packages/@tailwindcss-upgrade/src/codemods/template/migrate.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@ describe.each([['default'], ['with-variant'], ['important'], ['prefix']])('%s',
4545

4646
// Promote inferred data type to more specific utility if it exists
4747
['bg-[123px]', 'bg-position-[123px]'],
48+
49+
// Do not migrate bare values or arbitrary values to named values that are
50+
// deprecated
51+
['order-[0]', 'order-0'],
52+
['order-0', 'order-0'],
53+
54+
// Migrate deprecated named values to bare values
55+
['order-none', 'order-0'],
4856
])(testName, async (candidate, result) => {
4957
if (strategy === 'with-variant') {
5058
candidate = `focus:${candidate}`
@@ -63,4 +71,38 @@ describe.each([['default'], ['with-variant'], ['important'], ['prefix']])('%s',
6371
let migrated = await migrate(designSystem, {}, candidate)
6472
expect(migrated).toEqual(result)
6573
})
74+
75+
test.each([
76+
['order-[0]', 'order-0'],
77+
['order-0', 'order-0'],
78+
79+
// Do not migrate `order-none` if defined as a custom utility as it is then
80+
// not safe to migrate to `order-0`
81+
['order-none', 'order-none'],
82+
])(`${testName} with custom implementations`, async (candidate, result) => {
83+
if (strategy === 'with-variant') {
84+
candidate = `focus:${candidate}`
85+
result = `focus:${result}`
86+
} else if (strategy === 'important') {
87+
candidate = `${candidate}!`
88+
result = `${result}!`
89+
} else if (strategy === 'prefix') {
90+
// Not only do we need to prefix the candidate, we also have to make
91+
// sure that we prefix all CSS variables.
92+
candidate = `tw:${candidate.replaceAll('var(--', 'var(--tw-')}`
93+
result = `tw:${result.replaceAll('var(--', 'var(--tw-')}`
94+
}
95+
96+
let localInput = css`
97+
${input}
98+
99+
@utility order-none {
100+
order: none; /* imagine this exists */
101+
}
102+
`
103+
104+
let designSystem = await designSystems.get(__dirname).get(localInput)
105+
let migrated = await migrate(designSystem, {}, candidate)
106+
expect(migrated).toEqual(result)
107+
})
66108
})

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { migrateBareValueUtilities } from './migrate-bare-utilities'
1414
import { migrateBgGradient } from './migrate-bg-gradient'
1515
import { migrateCamelcaseInNamedValue } from './migrate-camelcase-in-named-value'
1616
import { migrateCanonicalizeCandidate } from './migrate-canonicalize-candidate'
17+
import { migrateDeprecatedUtilities } from './migrate-deprecated-utilities'
1718
import { migrateDropUnnecessaryDataTypes } from './migrate-drop-unnecessary-data-types'
1819
import { migrateEmptyArbitraryValues } from './migrate-handle-empty-arbitrary-values'
1920
import { migrateLegacyArbitraryValues } from './migrate-legacy-arbitrary-values'
@@ -48,6 +49,7 @@ export const DEFAULT_MIGRATIONS: Migration[] = [
4849
migrateLegacyArbitraryValues,
4950
migrateArbitraryUtilities,
5051
migrateBareValueUtilities,
52+
migrateDeprecatedUtilities,
5153
migrateModernizeArbitraryValues,
5254
migrateArbitraryVariants,
5355
migrateDropUnnecessaryDataTypes,

packages/tailwindcss/src/__snapshots__/intellisense.test.ts.snap

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8079,7 +8079,6 @@ exports[`getClassList 1`] = `
80798079
"order-12",
80808080
"order-first",
80818081
"order-last",
8082-
"order-none",
80838082
"ordinal",
80848083
"origin-bottom",
80858084
"origin-bottom-left",

packages/tailwindcss/src/compat/legacy-utilities.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,6 @@ export function registerLegacyUtilities(designSystem: DesignSystem) {
9090
return [decl('flex-grow', candidate.value.value)]
9191
}
9292
})
93+
94+
designSystem.utilities.static('order-none', () => [decl('order', '0')])
9395
}

packages/tailwindcss/src/utilities.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -649,7 +649,6 @@ export function createUtilities(theme: Theme) {
649649
*/
650650
staticUtility('order-first', [['order', '-9999']])
651651
staticUtility('order-last', [['order', '9999']])
652-
staticUtility('order-none', [['order', '0']])
653652
functionalUtility('order', {
654653
supportsNegative: true,
655654
handleBareValue: ({ value }) => {

0 commit comments

Comments
 (0)