From e57a2f5a3af8f1e3ebd5bac93b70d986fe43a5b3 Mon Sep 17 00:00:00 2001 From: Philipp Spiess Date: Wed, 14 May 2025 14:51:51 +0200 Subject: [PATCH] =?UTF-8?q?Change=20casing=20of=20utilities=20with=20named?= =?UTF-8?q?=20values=20to=20kebab-case=20to=20match=20u=E2=80=A6=20(#18017?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #16156 ## Summary This PR adds a new 3 -> 4 template migration that changes the casing of in both utility values and modifier values from camelCase to kebab-case to match the updated CSS variable names. ## Test plan - Added integration test, see the diff in the PR. --- CHANGELOG.md | 1 + integrations/upgrade/js-config.test.ts | 16 ++++-- .../migrate-camelcase-in-named-value.test.ts | 24 +++++++++ .../migrate-camelcase-in-named-value.ts | 53 +++++++++++++++++++ .../src/codemods/template/migrate.ts | 2 + 5 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 packages/@tailwindcss-upgrade/src/codemods/template/migrate-camelcase-in-named-value.test.ts create mode 100644 packages/@tailwindcss-upgrade/src/codemods/template/migrate-camelcase-in-named-value.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index cdb0386c7..a6aada039 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `lightningcss` now statically links Visual Studio redistributables ([#17979](https://github.com/tailwindlabs/tailwindcss/pull/17979)) - Ensure that running the Standalone build does not leave temporary files behind ([#17981](https://github.com/tailwindlabs/tailwindcss/pull/17981)) - Fix `-rotate-*` utilities with arbitrary values ([#18014](https://github.com/tailwindlabs/tailwindcss/pull/18014)) +- Upgrade: Change casing of utilities with named values to kebab-case to match updated theme variables ([#18017](https://github.com/tailwindlabs/tailwindcss/pull/18017)) ### Added diff --git a/integrations/upgrade/js-config.test.ts b/integrations/upgrade/js-config.test.ts index d2240fbf0..93d239af0 100644 --- a/integrations/upgrade/js-config.test.ts +++ b/integrations/upgrade/js-config.test.ts @@ -30,9 +30,13 @@ test( 400: '#f87171', 500: 'red', }, + superRed: '#ff0000', steel: 'rgb(70 130 180 / )', smoke: 'rgba(245, 245, 245, var(--smoke-alpha, ))', }, + opacity: { + superOpaque: '0.95', + }, fontSize: { xs: ['0.75rem', { lineHeight: '1rem' }], sm: ['0.875rem', { lineHeight: '1.5rem' }], @@ -144,9 +148,10 @@ test( } `, 'src/index.html': html` -
+
`, 'node_modules/my-external-lib/src/template.html': html`
@@ -162,8 +167,9 @@ test( " --- src/index.html ---
+ class="[letter-spacing:var(--tracking-super-wide)] [line-height:var(--leading-super-loose)]" + >
+
--- src/input.css --- @import 'tailwindcss'; @@ -181,9 +187,13 @@ test( --color-red-500: #ef4444; --color-red-600: #dc2626; + --color-super-red: #ff0000; --color-steel: rgb(70 130 180); --color-smoke: rgba(245, 245, 245, var(--smoke-alpha, 1)); + --opacity-*: initial; + --opacity-super-opaque: 95%; + --text-*: initial; --text-xs: 0.75rem; --text-xs--line-height: 1rem; diff --git a/packages/@tailwindcss-upgrade/src/codemods/template/migrate-camelcase-in-named-value.test.ts b/packages/@tailwindcss-upgrade/src/codemods/template/migrate-camelcase-in-named-value.test.ts new file mode 100644 index 000000000..ed5be23c0 --- /dev/null +++ b/packages/@tailwindcss-upgrade/src/codemods/template/migrate-camelcase-in-named-value.test.ts @@ -0,0 +1,24 @@ +import { __unstable__loadDesignSystem } from '@tailwindcss/node' +import { expect, test, vi } from 'vitest' +import * as versions from '../../utils/version' +import { migrateCamelcaseInNamedValue } from './migrate-camelcase-in-named-value' +vi.spyOn(versions, 'isMajor').mockReturnValue(true) + +test.each([ + ['text-superRed', 'text-super-red'], + ['text-red/superOpaque', 'text-red/super-opaque'], + ['text-superRed/superOpaque', 'text-super-red/super-opaque'], + + // Should not migrate named values in modifiers + ['group-hover/superGroup:underline', 'group-hover/superGroup:underline'], + + ['hover:text-superRed', 'hover:text-super-red'], + ['hover:text-red/superOpaque', 'hover:text-red/super-opaque'], + ['hover:text-superRed/superOpaque', 'hover:text-super-red/super-opaque'], +])('%s => %s', async (candidate, result) => { + let designSystem = await __unstable__loadDesignSystem('@import "tailwindcss";', { + base: __dirname, + }) + + expect(migrateCamelcaseInNamedValue(designSystem, {}, candidate)).toEqual(result) +}) diff --git a/packages/@tailwindcss-upgrade/src/codemods/template/migrate-camelcase-in-named-value.ts b/packages/@tailwindcss-upgrade/src/codemods/template/migrate-camelcase-in-named-value.ts new file mode 100644 index 000000000..2214a8366 --- /dev/null +++ b/packages/@tailwindcss-upgrade/src/codemods/template/migrate-camelcase-in-named-value.ts @@ -0,0 +1,53 @@ +import type { Config } from '../../../../tailwindcss/src/compat/plugin-api' +import type { DesignSystem } from '../../../../tailwindcss/src/design-system' +import * as version from '../../utils/version' + +// Converts named values to use kebab-case. This is necessary because the +// upgrade tool also renames the theme values to kebab-case, so `text-superRed` +// will have its theme value renamed to `--color-super-red` and thus the utility +// will be renamed to `text-super-red`. +export function migrateCamelcaseInNamedValue( + designSystem: DesignSystem, + _userConfig: Config | null, + rawCandidate: string, +): string { + if (!version.isMajor(3)) return rawCandidate + + for (let candidate of designSystem.parseCandidate(rawCandidate)) { + if (candidate.kind !== 'functional') continue + let clone = structuredClone(candidate) + let didChange = false + + if ( + candidate.value && + clone.value && + candidate.value.kind === 'named' && + clone.value.kind === 'named' && + candidate.value.value.match(/[A-Z]/) + ) { + clone.value.value = camelToKebab(candidate.value.value) + didChange = true + } + + if ( + candidate.modifier && + clone.modifier && + candidate.modifier.kind === 'named' && + clone.modifier.kind === 'named' && + candidate.modifier.value.match(/[A-Z]/) + ) { + clone.modifier.value = camelToKebab(candidate.modifier.value) + didChange = true + } + + if (didChange) { + return designSystem.printCandidate(clone) + } + } + + return rawCandidate +} + +function camelToKebab(str: string): string { + return str.replace(/([A-Z])/g, '-$1').toLowerCase() +} diff --git a/packages/@tailwindcss-upgrade/src/codemods/template/migrate.ts b/packages/@tailwindcss-upgrade/src/codemods/template/migrate.ts index 594342a3f..fff859c0a 100644 --- a/packages/@tailwindcss-upgrade/src/codemods/template/migrate.ts +++ b/packages/@tailwindcss-upgrade/src/codemods/template/migrate.ts @@ -11,6 +11,7 @@ import { migrateArbitraryVariants } from './migrate-arbitrary-variants' import { migrateAutomaticVarInjection } from './migrate-automatic-var-injection' import { migrateBareValueUtilities } from './migrate-bare-utilities' import { migrateBgGradient } from './migrate-bg-gradient' +import { migrateCamelcaseInNamedValue } from './migrate-camelcase-in-named-value' import { migrateDropUnnecessaryDataTypes } from './migrate-drop-unnecessary-data-types' import { migrateEmptyArbitraryValues } from './migrate-handle-empty-arbitrary-values' import { migrateImportant } from './migrate-important' @@ -41,6 +42,7 @@ export const DEFAULT_MIGRATIONS: Migration[] = [ migrateImportant, migrateBgGradient, migrateSimpleLegacyClasses, + migrateCamelcaseInNamedValue, migrateLegacyClasses, migrateMaxWidthScreen, migrateThemeToVar,