mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
This PR bumps all Tailwind CSS related dependencies when running the upgrade tool _if_ the dependency exists in your package.json file. E.g.: if you have `tailwindcss` and `@tailwindcss/vite` in your package.json, then both will be updated to the latest version. This PR is not trying to be smart and skip updating if you are already on the latest version. It will just try and update the dependencies and your package manager will do nothing in case it was already the latest version. ## Test plan Ran this on one of my personal projects and this was the output: <img width="1023" alt="image" src="https://github.com/user-attachments/assets/a189fe7a-a58a-44aa-9246-b720e7a2a892" /> Notice that I don't have `@tailwindcss/vite` logs because I am using `@tailwindcss/postcss`.
120 lines
3.5 KiB
TypeScript
120 lines
3.5 KiB
TypeScript
import { exec as execCb } from 'node:child_process'
|
|
import fs from 'node:fs/promises'
|
|
import { dirname, resolve } from 'node:path'
|
|
import { promisify } from 'node:util'
|
|
import { DefaultMap } from '../../../tailwindcss/src/utils/default-map'
|
|
import { error, warn } from './renderer'
|
|
|
|
const exec = promisify(execCb)
|
|
|
|
const SAVE_DEV: Record<string, string> = {
|
|
default: '-D',
|
|
bun: '-d',
|
|
}
|
|
|
|
export function pkg(base: string) {
|
|
return {
|
|
async add(packages: string[], location: 'dependencies' | 'devDependencies' = 'dependencies') {
|
|
let packageManager = await packageManagerForBase.get(base)
|
|
let args = packages.slice()
|
|
if (location === 'devDependencies') {
|
|
args.push(SAVE_DEV[packageManager] || SAVE_DEV.default)
|
|
}
|
|
|
|
let command = `${packageManager} add ${args.join(' ')}`
|
|
try {
|
|
return await exec(command, { cwd: base })
|
|
} catch (e: any) {
|
|
error(`An error occurred while running \`${command}\`\n\n${e.stdout}\n${e.stderr}`, {
|
|
prefix: '↳ ',
|
|
})
|
|
throw e
|
|
}
|
|
},
|
|
async has(name: string) {
|
|
try {
|
|
let packageJsonPath = resolve(base, 'package.json')
|
|
let packageJsonContent = await fs.readFile(packageJsonPath, 'utf-8')
|
|
return packageJsonContent.includes(`"${name}":`)
|
|
} catch {}
|
|
return false
|
|
},
|
|
async remove(packages: string[]) {
|
|
let packageManager = await packageManagerForBase.get(base)
|
|
let command = `${packageManager} remove ${packages.join(' ')}`
|
|
try {
|
|
return await exec(command, { cwd: base })
|
|
} catch (e: any) {
|
|
error(`An error occurred while running \`${command}\`\n\n${e.stdout}\n${e.stderr}`, {
|
|
prefix: '↳ ',
|
|
})
|
|
throw e
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
let didWarnAboutPackageManager = false
|
|
let packageManagerForBase = new DefaultMap(async (base) => {
|
|
do {
|
|
// 1. Check package.json for a `packageManager` field
|
|
let packageJsonPath = resolve(base, 'package.json')
|
|
try {
|
|
let packageJsonContent = await fs.readFile(packageJsonPath, 'utf-8')
|
|
let packageJson = JSON.parse(packageJsonContent)
|
|
if (packageJson.packageManager) {
|
|
if (packageJson.packageManager.includes('bun')) {
|
|
return 'bun'
|
|
}
|
|
if (packageJson.packageManager.includes('yarn')) {
|
|
return 'yarn'
|
|
}
|
|
if (packageJson.packageManager.includes('pnpm')) {
|
|
return 'pnpm'
|
|
}
|
|
if (packageJson.packageManager.includes('npm')) {
|
|
return 'npm'
|
|
}
|
|
}
|
|
} catch {}
|
|
|
|
// 2. Check for common lockfiles
|
|
try {
|
|
await fs.access(resolve(base, 'bun.lockb'))
|
|
return 'bun'
|
|
} catch {}
|
|
try {
|
|
await fs.access(resolve(base, 'bun.lock'))
|
|
return 'bun'
|
|
} catch {}
|
|
try {
|
|
await fs.access(resolve(base, 'pnpm-lock.yaml'))
|
|
return 'pnpm'
|
|
} catch {}
|
|
|
|
try {
|
|
await fs.access(resolve(base, 'yarn.lock'))
|
|
return 'yarn'
|
|
} catch {}
|
|
|
|
try {
|
|
await fs.access(resolve(base, 'package-lock.json'))
|
|
return 'npm'
|
|
} catch {}
|
|
|
|
// 3. If no lockfile is found, we might be in a monorepo
|
|
let previousBase = base
|
|
base = dirname(base)
|
|
|
|
// Already at the root
|
|
if (previousBase === base) {
|
|
if (!didWarnAboutPackageManager) {
|
|
didWarnAboutPackageManager = true
|
|
warn('Could not detect a package manager. Please manually update `tailwindcss` to v4.')
|
|
}
|
|
|
|
return Promise.reject('No package manager detected')
|
|
}
|
|
} while (true)
|
|
})
|