isSafeMigration() — tailwindcss Function Reference
Architecture documentation for the isSafeMigration() function in is-safe-migration.ts from the tailwindcss codebase.
Entity Profile
Dependency Diagram
graph TD aca11807_c543_c2c2_e4e5_c0f3adcb5924["isSafeMigration()"] a8567d3a_b9d2_a553_2828_a5a6b62364da["migrateCandidate()"] a8567d3a_b9d2_a553_2828_a5a6b62364da -->|calls| aca11807_c543_c2c2_e4e5_c0f3adcb5924 0aa64a1c_efd8_a69d_48ed_649b7a86c854["get()"] aca11807_c543_c2c2_e4e5_c0f3adcb5924 -->|calls| 0aa64a1c_efd8_a69d_48ed_649b7a86c854 0bec5ca9_74c8_dcc7_ec12_6404fb6493bd["parseCandidate()"] aca11807_c543_c2c2_e4e5_c0f3adcb5924 -->|calls| 0bec5ca9_74c8_dcc7_ec12_6404fb6493bd 78d56153_7669_7f22_26c3_390a27b34545["isGreaterThan()"] aca11807_c543_c2c2_e4e5_c0f3adcb5924 -->|calls| 78d56153_7669_7f22_26c3_390a27b34545 93f7fa1a_a769_e640_871b_757e4dca03f9["isMiddleOfString()"] aca11807_c543_c2c2_e4e5_c0f3adcb5924 -->|calls| 93f7fa1a_a769_e640_871b_757e4dca03f9 style aca11807_c543_c2c2_e4e5_c0f3adcb5924 fill:#6366f1,stroke:#818cf8,color:#fff
Relationship Graph
Source Code
packages/@tailwindcss-upgrade/src/codemods/template/is-safe-migration.ts lines 26–188
export function isSafeMigration(
rawCandidate: string,
location: { contents: string; start: number; end: number },
designSystem: DesignSystem,
): boolean {
// Ensure we are not migrating a candidate in a `<style>` block. The heuristic
// would be if the candidate is preceded by a whitespace and followed by a
// colon and whitespace.
//
// E.g.:
// ```vue
// <template>
// <div class="foo"></div>
// </template>
//
//
// <style>
// .foo {
// flex-shrink: 0;
// ^ ^^
// }
// </style>
// ```
if (
// 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/)
) {
// 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] = 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
// land, which also wouldn't parse.
//
// So let's only skip if we couldn't parse and we are not in Tailwind CSS v3.
//
if (!candidate && version.isGreaterThan(3)) {
return false
}
// 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.
//
// E.g.: `hover:focus:flex`
if (candidate.variants.length > 0) {
return true
}
// When we have an arbitrary property, the candidate has such a particular
// structure it's very likely to be safe.
//
// E.g.: `[color:red]`
if (candidate.kind === 'arbitrary') {
return true
}
// A static candidate is very likely safe if it contains a dash.
//
// E.g.: `items-center`
if (candidate.kind === 'static' && candidate.root.includes('-')) {
return true
}
// A functional candidate is very likely safe if it contains a value (which
// implies a `-`). Or if the root contains a dash.
//
// E.g.: `bg-red-500`, `bg-position-20`
if (
(candidate.kind === 'functional' && candidate.value !== null) ||
(candidate.kind === 'functional' && candidate.root.includes('-'))
) {
return true
}
// If the candidate contains a modifier, it's very likely to be safe because
// it implies that it contains a `/`.
//
// E.g.: `text-sm/7`
if (candidate.kind === 'functional' && candidate.modifier) {
return true
}
}
let currentLineBeforeCandidate = ''
for (let i = location.start - 1; i >= 0; i--) {
let char = location.contents.at(i)!
if (char === '\n') {
break
}
currentLineBeforeCandidate = char + currentLineBeforeCandidate
}
let currentLineAfterCandidate = ''
for (let i = location.end; i < location.contents.length; i++) {
let char = location.contents.at(i)!
if (char === '\n') {
break
}
currentLineAfterCandidate += char
}
// Heuristic: Require the candidate to be inside quotes
let isQuoteBeforeCandidate = isMiddleOfString(currentLineBeforeCandidate)
let isQuoteAfterCandidate = isMiddleOfString(currentLineAfterCandidate)
if (!isQuoteBeforeCandidate || !isQuoteAfterCandidate) {
return false
}
// Heuristic: Disallow object access immediately following the candidate
if (currentLineAfterCandidate[0] === '.') {
return false
}
// Heuristic: Disallow function call expressions immediately following the candidate
if (currentLineAfterCandidate.trim().startsWith('(')) {
return false
}
// Heuristic: Disallow logical operators preceding or following the candidate
for (let operator of LOGICAL_OPERATORS) {
if (
currentLineAfterCandidate.trim().startsWith(operator) ||
currentLineBeforeCandidate.trim().endsWith(operator)
) {
return false
}
}
// Heuristic: Disallow conditional template syntax
for (let rule of CONDITIONAL_TEMPLATE_SYNTAX) {
if (rule.test(currentLineBeforeCandidate)) {
return false
}
}
// Heuristic: Disallow Next.js Image `placeholder` prop
if (NEXT_PLACEHOLDER_PROP.test(currentLineBeforeCandidate)) {
return false
}
// Heuristic: Disallow replacements inside `emit('…', …)`
if (VUE_3_EMIT.test(currentLineBeforeCandidate)) {
return false
}
return true
}
Domain
Subdomains
Called By
Source
Frequently Asked Questions
What does isSafeMigration() do?
isSafeMigration() is a function in the tailwindcss codebase.
What does isSafeMigration() call?
isSafeMigration() calls 4 function(s): get, isGreaterThan, isMiddleOfString, parseCandidate.
What calls isSafeMigration()?
isSafeMigration() is called by 1 function(s): migrateCandidate.
Analyze Your Own Codebase
Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.
Try Supermodel Free