Skip to content
Merged
13 changes: 7 additions & 6 deletions packages/nuxi/src/utils/banner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ export function getBuilder(cwd: string, builder: Exclude<NuxtOptions['builder']
switch (builder) {
case 'rspack':
case '@nuxt/rspack-builder':
return { name: 'Rspack', version: getPkgVersion(cwd, '@rspack/core') }
return { name: 'Rspack', version: getPkgVersion(cwd, '@rspack/core', { via: ['@nuxt/rspack-builder'] }) }
case 'webpack':
case '@nuxt/webpack-builder':
return { name: 'Webpack', version: getPkgVersion(cwd, 'webpack') }
return { name: 'Webpack', version: getPkgVersion(cwd, 'webpack', { via: ['@nuxt/webpack-builder'] }) }
case 'vite':
case '@nuxt/vite-builder':
default: {
const pkgJSON = getPkgJSON(cwd, 'vite')
const pkgJSON = getPkgJSON(cwd, 'vite', { via: ['nuxt', '@nuxt/vite-builder'] })
const isRolldown = pkgJSON.name.includes('rolldown')
return { name: isRolldown ? 'Rolldown-Vite' : 'Vite', version: pkgJSON.version }
return { name: isRolldown ? 'Rolldown-Vite' : 'Vite', version: pkgJSON.version || 'unknown' }
}
}
}
Expand All @@ -27,9 +27,10 @@ export function showVersionsFromConfig(cwd: string, config: NuxtOptions) {
const { bold, gray, green } = colors

const nuxtVersion = getPkgVersion(cwd, 'nuxt') || getPkgVersion(cwd, 'nuxt-nightly') || getPkgVersion(cwd, 'nuxt3') || getPkgVersion(cwd, 'nuxt-edge')
const nitroVersion = getPkgVersion(cwd, 'nitropack') || getPkgVersion(cwd, 'nitro') || getPkgVersion(cwd, 'nitropack-nightly') || getPkgVersion(cwd, 'nitropack-edge')
const nitroVia = { via: ['nuxt', '@nuxt/nitro-server'] }
const nitroVersion = getPkgVersion(cwd, 'nitropack', nitroVia) || getPkgVersion(cwd, 'nitro', nitroVia) || getPkgVersion(cwd, 'nitropack-nightly') || getPkgVersion(cwd, 'nitropack-edge')
const builder = getBuilder(cwd, config.builder)
const vueVersion = getPkgVersion(cwd, 'vue') || null
const vueVersion = getPkgVersion(cwd, 'vue', { via: ['nuxt'] }) || null

logger.info(
green(`Nuxt ${bold(nuxtVersion)}`)
Expand Down
51 changes: 44 additions & 7 deletions packages/nuxi/src/utils/versions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,57 @@ export async function getNuxtVersion(cwd: string, cache = true) {
return (pkgDep && coerce(pkgDep)?.version) || '3.0.0'
}

export function getPkgVersion(cwd: string, pkg: string) {
const pkgJSON = getPkgJSON(cwd, pkg)
export function getPkgVersion(cwd: string, pkg: string, options?: { via?: string[] }) {
const pkgJSON = getPkgJSON(cwd, pkg, options)
return pkgJSON?.version ?? ''
}

export function getPkgJSON(cwd: string, pkg: string) {
for (const url of [cwd, tryResolveNuxt(cwd)]) {
if (!url) {
continue
/**
* Resolve a package.json, optionally walking a dependency chain.
*
* `via` is an array of `[startingPoint, ...intermediates]` describing
* the dependency path to walk before resolving `pkg`. For example:
*
* // vite is a dep of @nuxt/vite-builder, which is a dep of nuxt
* getPkgJSON(cwd, 'vite', { via: ['nuxt', '@nuxt/vite-builder'] })
*
* // webpack is a dep of @nuxt/webpack-builder, which the user installs
* getPkgJSON(cwd, 'webpack', { via: ['@nuxt/webpack-builder'] })
*
* Each entry is resolved from the location of the previous one,
* starting from cwd. Falls back to direct resolution from cwd/nuxt.
*/
export function getPkgJSON(cwd: string, pkg: string, options?: { via?: string[] }) {
// Build list of locations to try resolving pkg from.
// When `via` is provided, walk the chain first; then fall back to cwd/nuxt.
const roots: string[] = []

if (options?.via && options.via.length > 0) {
let from: string | undefined = cwd
for (const step of options.via) {
from = resolveModulePath(step, { from, try: true }) ?? undefined
if (!from) {
break
}
}
if (from) {
roots.push(from)
}
const p = resolveModulePath(`${pkg}/package.json`, { from: url, try: true })
}

// Fallback: direct resolution from cwd or nuxt's location
roots.push(cwd)
const nuxtPath = tryResolveNuxt(cwd)
if (nuxtPath) {
roots.push(nuxtPath)
}

for (const root of roots) {
const p = resolveModulePath(`${pkg}/package.json`, { from: root, try: true })
if (p) {
return JSON.parse(readFileSync(p, 'utf-8'))
}
}

return null
}
35 changes: 35 additions & 0 deletions packages/nuxi/test/unit/utils/banner.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { describe, expect, it, vi } from 'vitest'

vi.mock('../../../src/utils/versions', () => ({
getPkgJSON: vi.fn((_cwd: string, pkg: string, options?: { via?: string[] }) => {
if (pkg === 'vite' && options?.via?.includes('@nuxt/vite-builder')) {
return { name: 'vite', version: '7.3.1' }
}
return null
}),
getPkgVersion: vi.fn((_cwd: string, pkg: string, options?: { via?: string[] }) => {
if (pkg === 'webpack' && options?.via?.includes('@nuxt/webpack-builder')) {
return '5.99.0'
}
if (pkg === '@rspack/core' && options?.via?.includes('@nuxt/rspack-builder')) {
return '1.3.0'
}
return ''
}),
}))

const { getBuilder } = await import('../../../src/utils/banner')

describe('getBuilder', () => {
it('resolves vite version via nuxt -> @nuxt/vite-builder', () => {
expect(getBuilder('/any', 'vite')).toEqual({ name: 'Vite', version: '7.3.1' })
})

it('resolves webpack version via @nuxt/webpack-builder', () => {
expect(getBuilder('/any', 'webpack')).toEqual({ name: 'Webpack', version: '5.99.0' })
})

it('resolves rspack version via @nuxt/rspack-builder', () => {
expect(getBuilder('/any', 'rspack')).toEqual({ name: 'Rspack', version: '1.3.0' })
})
})
Loading