Skip to content

Commit

Permalink
Fix template migration issues
Browse files Browse the repository at this point in the history
  • Loading branch information
philipp-spiess committed Oct 4, 2024
1 parent c01b825 commit a79a96e
Show file tree
Hide file tree
Showing 11 changed files with 151 additions and 20 deletions.
94 changes: 94 additions & 0 deletions integrations/upgrade/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,3 +261,97 @@ test(
)
},
)

test(
`migrates prefixes even if other files have unprefixed versions of the candidate`,
{
fs: {
'package.json': json`
{
"dependencies": {
"@tailwindcss/upgrade": "workspace:^"
}
}
`,
'tailwind.config.js': js`
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./src/**/*.{html,js}'],
prefix: 'tw__',
}
`,
'src/index.html': html`
<div class="tw__flex flex tw__flex"></div>
`,
'src/other.html': html`
<div class="flex tw__flex flex"></div>
`,
'src/input.css': css`
@tailwind base;
@tailwind components;
@tailwind utilities;
`,
},
},
async ({ exec, fs }) => {
console.log(await exec('npx @tailwindcss/upgrade -c tailwind.config.js'))

await fs.expectFileToContain(
'src/index.html',
html`
<div class="tw:flex flex tw:flex"></div>
`,
)
await fs.expectFileToContain(
'src/other.html',
html`
<div class="flex tw:flex flex"></div>
`,
)
},
)

test(
`prefixed variants do not cause their unprefixed counterparts to be valid`,
{
fs: {
'package.json': json`
{
"dependencies": {
"@tailwindcss/upgrade": "workspace:^"
}
}
`,
'tailwind.config.js': js`
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./src/**/*.{html,js}'],
prefix: 'tw__',
}
`,
'src/index.html': html`
<div class="tw__bg-gradient-to-t"></div>
`,
'src/other.html': html`
<div class="bg-gradient-to-t"></div>
`,
},
},
async ({ exec, fs }) => {
await exec('npx @tailwindcss/upgrade -c tailwind.config.js')

await fs.expectFileToContain(
'src/index.html',
html`
<div class="tw:bg-linear-to-t"></div>
`,
)

await fs.expectFileToContain(
'src/other.html',
html`
<div class="bg-gradient-to-t"></div>
`,
)
},
)
2 changes: 1 addition & 1 deletion integrations/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export function test(
) {
return (only || (!process.env.CI && debug) ? defaultTest.only : defaultTest)(
name,
{ timeout: TEST_TIMEOUT, retry: 3 },
{ timeout: TEST_TIMEOUT, retry: debug ? 0 : 3 },
async (options) => {
let rootDir = debug ? path.join(REPO_ROOT, '.debug') : TMP_ROOT
await fs.mkdir(rootDir, { recursive: true })
Expand Down
4 changes: 2 additions & 2 deletions packages/@tailwindcss-upgrade/src/template/candidates.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ test('extracts candidates with positions from a template', async () => {
base: __dirname,
})

let candidates = await extractRawCandidates(content)
let candidates = await extractRawCandidates(content, 'html')
let validCandidates = candidates.filter(
({ rawCandidate }) => designSystem.parseCandidate(rawCandidate).length > 0,
)
Expand Down Expand Up @@ -60,7 +60,7 @@ test('replaces the right positions for a candidate', async () => {
base: __dirname,
})

let candidates = await extractRawCandidates(content)
let candidates = await extractRawCandidates(content, 'html')

let candidate = candidates.find(
({ rawCandidate }) => designSystem.parseCandidate(rawCandidate).length > 0,
Expand Down
17 changes: 13 additions & 4 deletions packages/@tailwindcss-upgrade/src/template/candidates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,24 @@ import type { DesignSystem } from '../../../tailwindcss/src/design-system'

export async function extractRawCandidates(
content: string,
extension: string,
): Promise<{ rawCandidate: string; start: number; end: number }[]> {
let scanner = new Scanner({})
let result = scanner.getCandidatesWithPositions({ content, extension: 'html' })
let result = scanner.getCandidatesWithPositions({ content, extension })

// Create a map to remove duplicates
let candidatesMap = new Map<string, { rawCandidate: string; start: number; end: number }>()

let candidates: { rawCandidate: string; start: number; end: number }[] = []
for (let { candidate: rawCandidate, position: start } of result) {
candidates.push({ rawCandidate, start, end: start + rawCandidate.length })
let end = start + rawCandidate.length
candidatesMap.set(`${start}:${end}:${rawCandidate}`, {
rawCandidate,
start,
end: start + rawCandidate.length,
})
}
return candidates

return [...candidatesMap.values()]
}

export function printCandidate(designSystem: DesignSystem, candidate: Candidate) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Config } from 'tailwindcss'
import { walk, WalkAction } from '../../../../tailwindcss/src/ast'
import type { Candidate, Variant } from '../../../../tailwindcss/src/candidate'
import { type Candidate, type Variant } from '../../../../tailwindcss/src/candidate'
import type { DesignSystem } from '../../../../tailwindcss/src/design-system'
import { printCandidate } from '../candidates'

Expand All @@ -9,7 +9,11 @@ export function automaticVarInjection(
_userConfig: Config,
rawCandidate: string,
): string {
for (let candidate of designSystem.parseCandidate(rawCandidate)) {
for (let readonlyCandidate of designSystem.parseCandidate(rawCandidate)) {
// The below logic makes extended use of mutation. Since candidates in the
// DesignSystem are cached, we can't mutate them directly.
let candidate = structuredClone(readonlyCandidate) as Candidate

let didChange = false

// Add `var(…)` in modifier position, e.g.:
Expand Down
16 changes: 14 additions & 2 deletions packages/@tailwindcss-upgrade/src/template/codemods/bg-gradient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ export function bgGradient(
_userConfig: Config,
rawCandidate: string,
): string {
/**
* Iterates over the candidates parsed from the raw candidate string, and processes each candidate that represents a Tailwind CSS background gradient utility.
*
* For each candidate that starts with "bg-gradient-to-", this function checks if the direction is valid, and if so, updates the candidate's root to use the "bg-linear-to-" prefix instead.
* The updated candidate is then returned for further processing.
*
* @param designSystem - The design system configuration object.
* @param rawCandidate - The raw candidate string to be parsed and processed.
* @returns The updated candidate string, or the original rawCandidate if no processing was done.
*/
for (let candidate of designSystem.parseCandidate(rawCandidate)) {
if (candidate.kind === 'static' && candidate.root.startsWith('bg-gradient-to-')) {
let direction = candidate.root.slice(15)
Expand All @@ -17,8 +27,10 @@ export function bgGradient(
continue
}

candidate.root = `bg-linear-to-${direction}`
return printCandidate(designSystem, candidate)
return printCandidate(designSystem, {
...candidate,
root: `bg-linear-to-${direction}`,
})
}
}
return rawCandidate
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Config } from 'tailwindcss'
import { parseCandidate } from '../../../../tailwindcss/src/candidate'
import type { DesignSystem } from '../../../../tailwindcss/src/design-system'
import { printCandidate } from '../candidates'

Expand All @@ -19,7 +20,7 @@ export function important(
_userConfig: Config,
rawCandidate: string,
): string {
for (let candidate of designSystem.parseCandidate(rawCandidate)) {
for (let candidate of parseCandidate(rawCandidate, designSystem)) {
if (candidate.important && candidate.raw[candidate.raw.length - 1] !== '!') {
// The printCandidate function will already put the exclamation mark in
// the right place, so we just need to mark this candidate as requiring a
Expand Down
7 changes: 5 additions & 2 deletions packages/@tailwindcss-upgrade/src/template/codemods/prefix.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Config } from 'tailwindcss'
import type { Candidate } from '../../../../tailwindcss/src/candidate'
import { parseCandidate, type Candidate } from '../../../../tailwindcss/src/candidate'
import type { DesignSystem } from '../../../../tailwindcss/src/design-system'
import { segment } from '../../../../tailwindcss/src/utils/segment'
import { printCandidate } from '../candidates'
Expand All @@ -24,7 +24,10 @@ export function prefix(
let unprefixedCandidate =
rawCandidate.slice(0, v3Base.start) + v3Base.base + rawCandidate.slice(v3Base.end)

let candidates = designSystem.parseCandidate(unprefixedCandidate)
// Note: This is not a valid candidate in the original DesignSystem, so we
// can not use the `DesignSystem#parseCandidate` API here or otherwise this
// invalid candidate will be cached.
let candidates = [...parseCandidate(unprefixedCandidate, designSystem)]
if (candidates.length > 0) {
candidate = candidates[0]
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Config } from 'tailwindcss'
import { walk, type AstNode } from '../../../../tailwindcss/src/ast'
import type { Variant } from '../../../../tailwindcss/src/candidate'
import { type Variant } from '../../../../tailwindcss/src/candidate'
import type { DesignSystem } from '../../../../tailwindcss/src/design-system'
import { printCandidate } from '../candidates'

Expand Down
14 changes: 11 additions & 3 deletions packages/@tailwindcss-upgrade/src/template/migrate.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import fs from 'node:fs/promises'
import path from 'node:path'
import path, { extname } from 'node:path'
import type { Config } from 'tailwindcss'
import type { DesignSystem } from '../../../tailwindcss/src/design-system'
import { extractRawCandidates, replaceCandidateInContent } from './candidates'
Expand Down Expand Up @@ -38,8 +38,9 @@ export default async function migrateContents(
designSystem: DesignSystem,
userConfig: Config,
contents: string,
extension: string,
): Promise<string> {
let candidates = await extractRawCandidates(contents)
let candidates = await extractRawCandidates(contents, extension)

// Sort candidates by starting position desc
candidates.sort((a, z) => z.start - a.start)
Expand All @@ -49,6 +50,8 @@ export default async function migrateContents(
let migratedCandidate = migrateCandidate(designSystem, userConfig, rawCandidate)

if (migratedCandidate !== rawCandidate) {
console.error('DOING WORK!!!!!!!!!!!')
console.log('start', start, 'end', end, migratedCandidate, rawCandidate)
output = replaceCandidateInContent(output, migratedCandidate, start, end)
}
}
Expand All @@ -60,5 +63,10 @@ export async function migrate(designSystem: DesignSystem, userConfig: Config, fi
let fullPath = path.resolve(process.cwd(), file)
let contents = await fs.readFile(fullPath, 'utf-8')

await fs.writeFile(fullPath, await migrateContents(designSystem, userConfig, contents))
console.log('> ', fullPath)

await fs.writeFile(
fullPath,
await migrateContents(designSystem, userConfig, contents, extname(file)),
)
}
4 changes: 2 additions & 2 deletions packages/tailwindcss/src/design-system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ export type DesignSystem = {
getClassList(): ClassEntry[]
getVariants(): VariantEntry[]

parseCandidate(candidate: string): Candidate[]
parseVariant(variant: string): Variant | null
parseCandidate(candidate: string): Readonly<Candidate>[]
parseVariant(variant: string): Readonly<Variant> | null
compileAstNodes(candidate: Candidate): ReturnType<typeof compileAstNodes>

getVariantOrder(): Map<Variant, number>
Expand Down

0 comments on commit a79a96e

Please sign in to comment.