1- import type { NuxtConfig , NuxtModule } from '@nuxt/schema'
1+ import type { NuxtModule } from '@nuxt/schema'
22import type { PackageJson } from 'pkg-types'
33
44import os from 'node:os'
@@ -36,66 +36,52 @@ export default defineCommand({
3636 ...rootDirArgs ,
3737 } ,
3838 async run ( ctx ) {
39- // Resolve rootDir
4039 const cwd = resolveRootDir ( ctx . args )
41-
42- // Load Nuxt config
43- const nuxtConfig = await getNuxtConfig ( cwd )
44-
45- // Find nearest package.json
46- const projectPkg = await readPackageJSON ( cwd ) . catch ( ( ) => ( { } as PackageJson ) )
40+ const [ nuxtConfig , projectPkg , detectedPackageManager ] = await Promise . all ( [
41+ getNuxtConfig ( cwd ) ,
42+ readPackageJSON ( cwd ) . catch ( ( ) => ( { } as PackageJson ) ) ,
43+ detectPackageManager ( cwd ) ,
44+ ] )
4745 const { dependencies = { } , devDependencies = { } } = projectPkg
48-
49- // Utils to query a dependency version
5046 const nuxtPath = tryResolveNuxt ( cwd )
51- async function getDepVersion ( name : string ) {
52- for ( const url of [ cwd , nuxtPath ] ) {
53- if ( ! url ) {
54- continue
55- }
56- const pkg = await readDependencyPackageJson ( name , url )
57- if ( pkg ) {
58- return pkg . version !
59- }
47+ const versions = new Map < string , Promise < string | undefined > > ( )
48+ const getDepVersion = ( name : string ) => {
49+ let version = versions . get ( name )
50+ if ( ! version ) {
51+ version = resolveDependencyVersion ( name , [ cwd , nuxtPath ] , cwd , projectPkg , dependencies , devDependencies )
52+ versions . set ( name , version )
6053 }
61- return resolveCatalogEntry ( cwd , projectPkg , name ) ?. specifier
62- ?? ( dependencies [ name ] || devDependencies [ name ] )
54+ return version
6355 }
6456
65- async function listModules ( arr : NonNullable < NuxtConfig [ 'modules' ] > = [ ] ) {
66- const info : string [ ] = [ ]
67- for ( let m of arr ) {
68- if ( Array . isArray ( m ) ) {
69- m = m [ 0 ]
70- }
71- const name = normalizeConfigModule ( m , cwd )
72- if ( name ) {
73- const npmName = name ! . split ( '/' ) . splice ( 0 , 2 ) . join ( '/' ) // @foo /bar/baz => @foo/bar
74- const v = await getDepVersion ( npmName )
75- info . push ( `\`${ v ? `${ name } @${ v } ` : name } \`` )
76- }
57+ const modulesPromise = Promise . all ( ( nuxtConfig . modules || [ ] ) . map ( async ( module ) => {
58+ const name = normalizeConfigModule ( module , cwd )
59+ if ( ! name ) {
60+ return null
7761 }
78- return info . join ( ', ' )
79- }
80-
81- // Check Nuxt version
82- const nuxtVersion = await getDepVersion ( 'nuxt' ) || await getDepVersion ( 'nuxt-nightly' ) || '-'
62+ const specifier = Array . isArray ( module ) ? module [ 0 ] : module
63+ const packageName = typeof specifier === 'string' && getPackageName ( specifier )
64+ const version = packageName && await getDepVersion ( packageName )
65+ return `\`${ version ? `${ name } @${ version } ` : name } \``
66+ } ) )
67+ const [ modules , nuxtVersion = '-' , nitroVersion ] = await Promise . all ( [
68+ modulesPromise ,
69+ getDepVersion ( 'nuxt' ) . then ( version => version || getDepVersion ( 'nuxt-nightly' ) ) ,
70+ getDepVersion ( 'nitropack' ) . then ( version => version || getDepVersion ( 'nitro' ) ) ,
71+ ] )
8372 const builder = nuxtConfig . builder || 'vite'
84-
85- let packageManager = ( await detectPackageManager ( cwd ) ) ?. name
86-
87- if ( packageManager ) {
88- packageManager += `@${ getPackageManagerVersion ( packageManager ) } `
89- }
90-
73+ const packageManager = detectedPackageManager
74+ ? `${ detectedPackageManager . name } @${ getPackageManagerVersion ( detectedPackageManager . command ) } `
75+ : 'unknown'
9176 const osType = os . type ( )
92- const builderInfo = typeof builder === 'string'
77+ const cpus = os . cpus ( )
78+ const builderInfo = typeof builder === 'string' && [ 'vite' , '@nuxt/vite-builder' , 'webpack' , '@nuxt/webpack-builder' , 'rspack' , '@nuxt/rspack-builder' ] . includes ( builder )
9379 ? getBuilder ( cwd , builder )
9480 : { name : 'custom' , version : '0.0.0' }
9581
9682 const infoObj = {
9783 'Operating system' : osType === 'Darwin' ? `macOS ${ os . release ( ) } ` : osType === 'Windows_NT' ? `Windows ${ os . release ( ) } ` : `${ osType } ${ os . release ( ) } ` ,
98- 'CPU' : `${ os . cpus ( ) [ 0 ] ?. model || 'unknown' } (${ os . cpus ( ) . length } cores)` ,
84+ 'CPU' : `${ cpus [ 0 ] ?. model || 'unknown' } (${ cpus . length } cores)` ,
9985 ...isBun
10086 // @ts -expect-error Bun global
10187 ? { 'Bun version' : Bun ?. version as string }
@@ -104,41 +90,22 @@ export default defineCommand({
10490 ? { 'Deno version' : Deno ?. version . deno as string }
10591 : { 'Node.js version' : process . version as string } ,
10692 'nuxt/cli version' : nuxiVersion ,
107- 'Package manager' : packageManager ?? 'unknown' ,
93+ 'Package manager' : packageManager ,
10894 'Nuxt version' : nuxtVersion ,
109- 'Nitro version' : await getDepVersion ( 'nitropack' ) || await getDepVersion ( 'nitro' ) ,
95+ 'Nitro version' : nitroVersion ,
11096 'Builder' : builderInfo . name === 'custom' ? 'custom' : `${ builderInfo . name . toLowerCase ( ) } @${ builderInfo . version } ` ,
11197 'Config' : Object . keys ( nuxtConfig )
11298 . map ( key => `\`${ key } \`` )
11399 . sort ( )
114100 . join ( ', ' ) ,
115- 'Modules' : await listModules ( nuxtConfig . modules ) ,
101+ 'Modules' : modules . filter ( module => module !== null ) . join ( ', ' ) ,
116102 }
117103
118104 logger . info ( `Nuxt root directory: ${ styleText ( 'cyan' , nuxtConfig . rootDir || cwd ) } \n` )
119105
120106 const boxStr = formatInfoBox ( infoObj )
121107
122- let firstColumnLength = 0
123- let secondColumnLength = 0
124- const entries = Object . entries ( infoObj ) . map ( ( [ label , val ] ) => {
125- if ( label . length > firstColumnLength ) {
126- firstColumnLength = label . length + 4
127- }
128- if ( ( val || '' ) . length > secondColumnLength ) {
129- secondColumnLength = ( val || '' ) . length + 2
130- }
131- return [ label , val || '-' ] as const
132- } )
133-
134- // formatted for copy-pasting into an issue
135- let copyStr = `| ${ ' ' . repeat ( firstColumnLength ) } | ${ ' ' . repeat ( secondColumnLength ) } |\n| ${ '-' . repeat ( firstColumnLength ) } | ${ '-' . repeat ( secondColumnLength ) } |\n`
136- for ( const [ label , value ] of entries ) {
137- if ( ! isMinimal ) {
138- copyStr += `| ${ `**${ label } **` . padEnd ( firstColumnLength ) } | ${ ( value . includes ( '`' ) ? value : `\`${ value } \`` ) . padEnd ( secondColumnLength ) } |\n`
139- }
140- }
141-
108+ const copyStr = formatMarkdownTable ( infoObj )
142109 const copied = ! isMinimal && await writeText ( copyStr ) . then ( ( ) => true ) . catch ( ( ) => false )
143110
144111 if ( copied ) {
@@ -169,20 +136,69 @@ export default defineCommand({
169136 } ,
170137} )
171138
172- function normalizeConfigModule (
173- module : NuxtModule < any , any > | string | false | null | undefined ,
139+ async function resolveDependencyVersion (
140+ name : string ,
141+ roots : Array < string | null > ,
142+ cwd : string ,
143+ projectPkg : PackageJson ,
144+ dependencies : Record < string , string > ,
145+ devDependencies : Record < string , string > ,
146+ ) : Promise < string | undefined > {
147+ for ( const root of roots ) {
148+ if ( ! root ) {
149+ continue
150+ }
151+ const pkg = await readDependencyPackageJson ( name , root )
152+ if ( pkg ?. version ) {
153+ return pkg . version
154+ }
155+ }
156+ return resolveCatalogEntry ( cwd , projectPkg , name ) ?. specifier
157+ ?? dependencies [ name ]
158+ ?? devDependencies [ name ]
159+ }
160+
161+ export function formatMarkdownTable ( info : Record < string , string | undefined > ) : string {
162+ const entries = Object . entries ( info ) . map ( ( [ label , value ] ) => [ label , value || '-' ] as const )
163+ const labelWidth = Math . max ( ...entries . map ( ( [ label ] ) => label . length + 4 ) )
164+ const valueWidth = Math . max ( ...entries . map ( ( [ , value ] ) => value . length + ( value . includes ( '`' ) ? 0 : 2 ) ) )
165+ const rows = entries . map ( ( [ label , value ] ) => {
166+ const formattedValue = value . includes ( '`' ) ? value : `\`${ value } \``
167+ return `| ${ `**${ label } **` . padEnd ( labelWidth ) } | ${ formattedValue . padEnd ( valueWidth ) } |`
168+ } )
169+ return [
170+ `| ${ ' ' . repeat ( labelWidth ) } | ${ ' ' . repeat ( valueWidth ) } |` ,
171+ `| ${ '-' . repeat ( labelWidth ) } | ${ '-' . repeat ( valueWidth ) } |` ,
172+ ...rows ,
173+ '' ,
174+ ] . join ( '\n' )
175+ }
176+
177+ export function getPackageName ( name : string ) : string | undefined {
178+ if ( name . startsWith ( '.' ) || name . startsWith ( '/' ) || / ^ [ a - z ] : [ \\ / ] / i. test ( name ) || name . endsWith ( '()' ) ) {
179+ return undefined
180+ }
181+ const parts = name . split ( '/' )
182+ return name . startsWith ( '@' ) ? parts . slice ( 0 , 2 ) . join ( '/' ) : parts [ 0 ]
183+ }
184+
185+ export function normalizeConfigModule (
186+ module : NuxtModule < any , any > | string | false | null | undefined | readonly [ ( NuxtModule < any , any > | string | undefined ) ?, unknown ?] ,
174187 rootDir : string ,
175188) : string | null {
176189 if ( ! module ) {
177190 return null
178191 }
179192 if ( typeof module === 'string' ) {
180- return module
181- . split ( rootDir )
182- . pop ( ) ! // Strip rootDir
183- . split ( 'node_modules' )
184- . pop ( ) ! // Strip node_modules
185- . replace ( LEADING_SLASH_RE , '' )
193+ const normalized = module . replaceAll ( '\\' , '/' )
194+ const normalizedRoot = rootDir . replaceAll ( '\\' , '/' ) . replace ( / \/ $ / , '' )
195+ const nodeModulesIndex = normalized . lastIndexOf ( '/node_modules/' )
196+ if ( nodeModulesIndex !== - 1 ) {
197+ return normalized . slice ( nodeModulesIndex + '/node_modules/' . length )
198+ }
199+ return normalized . startsWith ( `${ normalizedRoot } /` )
200+ ? normalized . slice ( normalizedRoot . length + 1 )
201+ : normalized . replace ( LEADING_SLASH_RE , '' )
186202 }
187203 if ( typeof module === 'function' ) {
188204 return `${ module . name } ()`
0 commit comments