import { __unstable__loadDesignSystem } from '@tailwindcss/node'
import path from 'node:path'
import url from 'node:url'
import type { Candidate } from '../../../../tailwindcss/src/candidate'
import type { Config } from '../../../../tailwindcss/src/compat/plugin-api'
import type { DesignSystem } from '../../../../tailwindcss/src/design-system'
import { DefaultMap } from '../../../../tailwindcss/src/utils/default-map'
import { printCandidate } from '../candidates'
import { isSafeMigration } from '../is-safe-migration'
const __filename = url.fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const LEGACY_CLASS_MAP = new Map([
['shadow', 'shadow-sm'],
['shadow-sm', 'shadow-xs'],
['shadow-xs', 'shadow-2xs'],
['inset-shadow', 'inset-shadow-sm'],
['inset-shadow-sm', 'inset-shadow-xs'],
['inset-shadow-xs', 'inset-shadow-2xs'],
['drop-shadow', 'drop-shadow-sm'],
['drop-shadow-sm', 'drop-shadow-xs'],
['rounded', 'rounded-sm'],
['rounded-sm', 'rounded-xs'],
['blur', 'blur-sm'],
['blur-sm', 'blur-xs'],
['backdrop-blur', 'backdrop-blur-sm'],
['backdrop-blur-sm', 'backdrop-blur-xs'],
['ring', 'ring-3'],
])
const THEME_KEYS = new Map([
['shadow', '--shadow'],
['shadow-sm', '--shadow-sm'],
['shadow-xs', '--shadow-xs'],
['shadow-2xs', '--shadow-2xs'],
['drop-shadow', '--drop-shadow'],
['drop-shadow-sm', '--drop-shadow-sm'],
['drop-shadow-xs', '--drop-shadow-xs'],
['rounded', '--radius'],
['rounded-sm', '--radius-sm'],
['rounded-xs', '--radius-xs'],
['blur', '--blur'],
['blur-sm', '--blur-sm'],
['blur-xs', '--blur-xs'],
['backdrop-blur', '--backdrop-blur'],
['backdrop-blur-sm', '--backdrop-blur-sm'],
['backdrop-blur-xs', '--backdrop-blur-xs'],
['ring', '--ring-width'],
['ring-3', '--ring-width-3'],
])
const DESIGN_SYSTEMS = new DefaultMap((base) => {
return __unstable__loadDesignSystem('@import "tailwindcss";', { base })
})
export async function legacyClasses(
designSystem: DesignSystem,
_userConfig: Config,
rawCandidate: string,
location?: {
contents: string
start: number
end: number
},
): Promise<string> {
let defaultDesignSystem = await DESIGN_SYSTEMS.get(__dirname)
function* migrate(rawCandidate: string) {
for (let candidate of designSystem.parseCandidate(rawCandidate)) {
let baseCandidate = structuredClone(candidate) as Candidate
baseCandidate.variants = []
baseCandidate.important = false
let baseCandidateString = printCandidate(designSystem, baseCandidate)
let newBaseCandidateString = LEGACY_CLASS_MAP.get(baseCandidateString)
if (!newBaseCandidateString) continue
let [newBaseCandidate] = designSystem.parseCandidate(newBaseCandidateString)
if (!newBaseCandidate) continue
let newCandidate = structuredClone(newBaseCandidate) as Candidate
newCandidate.variants = candidate.variants
newCandidate.important = candidate.important
yield [
candidate,
newCandidate,
THEME_KEYS.get(baseCandidateString),
THEME_KEYS.get(newBaseCandidateString),
] as const
}
}
for (let [fromCandidate, toCandidate, fromThemeKey, toThemeKey] of migrate(rawCandidate)) {
let isPotentialProblematicClass = (() => {
if (fromCandidate.variants.length > 0) {
return false
}
if (fromCandidate.kind === 'arbitrary') {
return false
}
if (fromCandidate.kind === 'static') {
return !fromCandidate.root.includes('-')
}
if (fromCandidate.kind === 'functional') {
return fromCandidate.value === null || !fromCandidate.root.includes('-')
}
return false
})()
if (location && isPotentialProblematicClass && !isSafeMigration(location)) {
continue
}
if (fromThemeKey && toThemeKey) {
let customFrom = designSystem.resolveThemeValue(fromThemeKey)
let defaultFrom = defaultDesignSystem.resolveThemeValue(fromThemeKey)
let customTo = designSystem.resolveThemeValue(toThemeKey)
let defaultTo = defaultDesignSystem.resolveThemeValue(toThemeKey)
if (customTo === undefined && defaultTo !== undefined) {
continue
}
if (customFrom !== defaultFrom) {
continue
}
if (customTo !== defaultTo) {
continue
}
}
return printCandidate(designSystem, toCandidate)
}
return rawCandidate
}