From 33bc80b073d006024ea45151dfb1b47378269df9 Mon Sep 17 00:00:00 2001 From: Colonel-Sandvich <21989418+Colonel-Sandvich@users.noreply.github.com> Date: Fri, 27 Dec 2024 01:27:42 +0000 Subject: [PATCH 1/2] fix(upgrade): use nypm in upgrade and info commands Unfortunately, nypm doesn't return which lock file is in use for bun so we still have to search for that ourselves. --- src/commands/info.ts | 16 +++----- src/commands/upgrade.ts | 76 +++++++++++++++++++++--------------- src/utils/packageManagers.ts | 27 ------------- 3 files changed, 49 insertions(+), 70 deletions(-) diff --git a/src/commands/info.ts b/src/commands/info.ts index 7a86be0f9..e128dd26f 100644 --- a/src/commands/info.ts +++ b/src/commands/info.ts @@ -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' @@ -69,14 +66,11 @@ 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(), @@ -84,7 +78,7 @@ export default defineCommand({ 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 + '`') diff --git a/src/commands/upgrade.ts b/src/commands/upgrade.ts index 6835a50c3..a5bb807a4 100644 --- a/src/commands/upgrade.ts +++ b/src/commands/upgrade.ts @@ -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' @@ -29,21 +26,16 @@ async function getNuxtVersion(path: string): Promise { } } -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', @@ -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]' @@ -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]) : [] @@ -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) { @@ -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' @@ -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 | 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 + } + + return lockFile +} diff --git a/src/utils/packageManagers.ts b/src/utils/packageManagers.ts index 9394ace3c..752eec969 100644 --- a/src/utils/packageManagers.ts +++ b/src/utils/packageManagers.ts @@ -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() From 488896f8f197e45a6293f4303084966a3efa1435 Mon Sep 17 00:00:00 2001 From: Colonel-Sandvich <21989418+Colonel-Sandvich@users.noreply.github.com> Date: Fri, 27 Dec 2024 18:43:19 +0000 Subject: [PATCH 2/2] fix: touch file not actually creating the file Further testing revealed that `utimes` throws an error if the file doesn't exist, this seemingly wasn't caught because package managers create the lockfile upon the install command --- src/utils/fs.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/utils/fs.ts b/src/utils/fs.ts index 4832d34fc..d0dcd558f 100644 --- a/src/utils/fs.ts +++ b/src/utils/fs.ts @@ -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(