Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 5 additions & 11 deletions src/commands/info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,8 @@ import { splitByCase } from 'scule'
import clipboardy from 'clipboardy'
import type { NuxtConfig, NuxtModule } from '@nuxt/schema'
import { defineCommand } from 'citty'
import type { packageManagerLocks } from '../utils/packageManagers'
import {
getPackageManager,
getPackageManagerVersion,
} from '../utils/packageManagers'
import { detectPackageManager } from 'nypm'
import { getPackageManagerVersion } from '../utils/packageManagers'
import { findup } from '../utils/fs'

import nuxiPkg from '../../package.json'
Expand Down Expand Up @@ -69,22 +66,19 @@ export default defineCommand({
? 'vite' /* nuxt-vite */
: 'webpack'

let packageManager: keyof typeof packageManagerLocks | 'unknown' | null
= getPackageManager(cwd)?.name ?? null
let packageManager = (await detectPackageManager(cwd))?.name

if (packageManager) {
packageManager += '@' + getPackageManagerVersion(packageManager)
}
else {
packageManager = 'unknown'
}

const infoObj = {
OperatingSystem: os.type(),
NodeVersion: process.version,
NuxtVersion: nuxtVersion,
CLIVersion: nuxiPkg.version,
NitroVersion: getDepVersion('nitropack'),
PackageManager: packageManager,
PackageManager: packageManager ?? 'unknown',
Builder: typeof builder === 'string' ? builder : 'custom',
UserConfig: Object.keys(nuxtConfig)
.map(key => '`' + key + '`')
Expand Down
76 changes: 44 additions & 32 deletions src/commands/upgrade.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { execSync } from 'node:child_process'
import { existsSync } from 'node:fs'
import { consola } from 'consola'
import { colors } from 'consola/utils'
import { relative, resolve } from 'pathe'
import { resolve } from 'pathe'
import type { PackageJson } from 'pkg-types'
import { readPackageJSON } from 'pkg-types'
import { defineCommand } from 'citty'
import {
getPackageManager,
getPackageManagerVersion,
} from '../utils/packageManagers'
import { addDependency, detectPackageManager } from 'nypm'
import { getPackageManagerVersion } from '../utils/packageManagers'
import { rmRecursive, touchFile } from '../utils/fs'
import { cleanupNuxtDirs, nuxtVersionToGitIdentifier } from '../utils/nuxt'

Expand All @@ -29,21 +26,16 @@ async function getNuxtVersion(path: string): Promise<string | null> {
}
}

async function checkNuxtDependencyType(pkg: PackageJson): Promise<'dependencies' | 'devDependencies' | null> {
if (pkg.dependencies && pkg.dependencies['nuxt']) {
function checkNuxtDependencyType(pkg: PackageJson): 'dependencies' | 'devDependencies' {
if (pkg.dependencies?.['nuxt']) {
return 'dependencies'
}
if (pkg.devDependencies && pkg.devDependencies['nuxt']) {
if (pkg.devDependencies?.['nuxt']) {
return 'devDependencies'
}
return 'dependencies'
}

function hasPnpmWorkspaceFile(cwd: string): boolean {
const pnpmWorkspaceFilePath = resolve(cwd, 'pnpm-workspace.yaml')
return existsSync(pnpmWorkspaceFilePath)
}

const nuxtVersionTags = {
'3.x': '3x',
'4.x': 'latest',
Expand Down Expand Up @@ -99,16 +91,16 @@ export default defineCommand({
const cwd = resolve(ctx.args.cwd || ctx.args.rootDir)

// Check package manager
const packageManagerAndLockFile = getPackageManager(cwd)
if (!packageManagerAndLockFile) {
const packageManager = await detectPackageManager(cwd)
if (!packageManager) {
consola.error(
`Unable to determine the package manager used by this project.\n\nNo lock files found in \`${cwd}\`, and no \`packageManager\` field specified in \`package.json\`.\n\nPlease either add the \`packageManager\` field to \`package.json\` or execute the installation command for your package manager. For example, you can use \`pnpm i\`, \`npm i\`, \`bun i\`, or \`yarn i\`, and then try again.`,
)
process.exit(1)
}
const { name: packageManager, lockFilePath } = packageManagerAndLockFile
const packageManagerVersion = getPackageManagerVersion(packageManager)
consola.info('Package manager:', packageManager, packageManagerVersion)
const { name: packageManagerName, lockFile: lockFileCandidates } = packageManager
const packageManagerVersion = getPackageManagerVersion(packageManagerName)
consola.info('Package manager:', packageManagerName, packageManagerVersion)

// Check currently installed Nuxt version
const currentVersion = (await getNuxtVersion(cwd)) || '[unknown]'
Expand All @@ -117,7 +109,7 @@ export default defineCommand({
const pkg = await readPackageJSON(cwd).catch(() => null)

// Check if Nuxt is a dependency or devDependency
const nuxtDependencyType = pkg ? await checkNuxtDependencyType(pkg) : 'dependencies'
const nuxtDependencyType = pkg ? checkNuxtDependencyType(pkg) : 'dependencies'
const corePackages = ['@nuxt/kit', '@nuxt/schema', '@nuxt/vite-builder', '@nuxt/webpack-builder', '@nuxt/rspack-builder']

const packagesToUpdate = pkg ? corePackages.filter(p => pkg.dependencies?.[p] || pkg.devDependencies?.[p]) : []
Expand All @@ -126,8 +118,14 @@ export default defineCommand({
const { npmPackages, nuxtVersion } = await getRequiredNewVersion(['nuxt', ...packagesToUpdate], ctx.args.channel)

// Force install
const pmLockFile = resolve(cwd, lockFilePath)
const forceRemovals = ['node_modules', relative(process.cwd(), pmLockFile)]
const toRemove = ['node_modules']

const lockFile = normaliseLockFile(cwd, lockFileCandidates)
if (lockFile) {
toRemove.push(lockFile)
}

const forceRemovals = toRemove
.map(p => colors.cyan(p))
.join(' and ')
if (ctx.args.force === undefined) {
Expand All @@ -143,22 +141,20 @@ export default defineCommand({
consola.info(
`Recreating ${forceRemovals}. If you encounter any issues, revert the changes and try with \`--no-force\``,
)
await rmRecursive([pmLockFile, resolve(cwd, 'node_modules')])
await touchFile(pmLockFile)
await rmRecursive(toRemove.map(file => resolve(cwd, file)))
if (lockFile) {
await touchFile(resolve(cwd, lockFile))
}
}

const versionType = ctx.args.channel === 'nightly' ? 'nightly' : 'latest stable'
consola.info(`Installing ${versionType} Nuxt ${nuxtVersion} release...`)

const command = [
await addDependency(npmPackages, {
cwd,
packageManager,
packageManager === 'yarn' ? 'add' : 'install',
nuxtDependencyType === 'devDependencies' ? '-D' : '',
packageManager === 'pnpm' && hasPnpmWorkspaceFile(cwd) ? '-w' : '',
...npmPackages,
].filter(Boolean).join(' ')

execSync(command, { stdio: 'inherit', cwd })
dev: nuxtDependencyType === 'devDependencies' ? true : false,
})

// Clean up after upgrade
let buildDir: string = '.nuxt'
Expand Down Expand Up @@ -204,3 +200,19 @@ export default defineCommand({
}
},
})

// Find which lock file is in use since `nypm.detectPackageManager` doesn't return this
function normaliseLockFile(cwd: string, lockFiles: string | Array<string> | undefined) {
if (typeof lockFiles === 'string') {
lockFiles = [lockFiles]
}

const lockFile = lockFiles?.find(file => existsSync(resolve(cwd, file)))

if (lockFile === undefined) {
consola.error(`Unable to find any lock files in ${cwd}`)
return undefined
Copy link

Copilot AI Dec 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning 'undefined' here can lead to unintended behavior when 'undefined' is added to the 'toRemove' array. Consider handling this case more gracefully.

Suggested change
return undefined
return ''

Copilot uses AI. Check for mistakes.
}

return lockFile
}
8 changes: 6 additions & 2 deletions src/utils/fs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,12 @@ export async function rmRecursive(paths: string[]) {
}

export async function touchFile(path: string) {
const time = new Date()
await fsp.utimes(path, time, time).catch(() => {})
if (await exists(path)) {
return
}
await fsp.writeFile(path, '').catch(() => {
consola.error(`Failed to create file: ${path}`)
})
}

export function findup<T>(
Expand Down
27 changes: 0 additions & 27 deletions src/utils/packageManagers.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,4 @@
import { execSync } from 'node:child_process'
import { existsSync } from 'node:fs'
import { resolve } from 'pathe'
import { findup } from './fs'

export const packageManagerLocks = {
yarn: ['yarn.lock'],
npm: ['package-lock.json'],
pnpm: ['pnpm-lock.yaml'],
bun: ['bun.lockb', 'bun.lock'],
}

export function getPackageManager(rootDir: string) {
return findup(rootDir, (dir) => {
let name: keyof typeof packageManagerLocks
for (name in packageManagerLocks) {
const paths = packageManagerLocks[name]
for (const lockFilePath of paths) {
if (lockFilePath && existsSync(resolve(dir, lockFilePath))) {
return {
name,
lockFilePath,
}
}
}
}
})
}

export function getPackageManagerVersion(name: string) {
return execSync(`${name} --version`).toString('utf8').trim()
Expand Down
Loading