11import type { Nuxt } from '@nuxt/schema'
22import type { ConsolaInstance } from 'consola'
3+ import { exec } from 'node:child_process'
34import { existsSync , readFileSync , writeFileSync } from 'node:fs'
45import { homedir } from 'node:os'
56import { join , resolve } from 'node:path'
67import process from 'node:process'
8+ import { promisify } from 'node:util'
79import { consola } from 'consola'
810import { builders , loadFile , writeFile } from 'magicast'
911import { getDefaultExportOptions } from 'magicast/helpers'
1012import { $fetch } from 'ofetch'
1113import { getPrefix , toCamelCase } from '../utils/transform'
1214
15+ const execAsync = promisify ( exec )
16+
1317const SHELVE_RC_PATH = join ( homedir ( ) , '.shelve' )
1418const DEFAULT_URL = 'https://app.shelve.cloud'
1519
@@ -86,6 +90,34 @@ function detectValidationLibrary(rootDir: string): ValidationLibrary | null {
8690 catch { return null }
8791}
8892
93+ function detectPackageManager ( rootDir : string ) : 'pnpm' | 'yarn' | 'npm' {
94+ if ( existsSync ( join ( rootDir , 'pnpm-lock.yaml' ) ) )
95+ return 'pnpm'
96+ if ( existsSync ( join ( rootDir , 'yarn.lock' ) ) )
97+ return 'yarn'
98+ return 'npm'
99+ }
100+
101+ async function installValidationLibrary ( rootDir : string , library : ValidationLibrary , logger : ConsolaInstance ) : Promise < void > {
102+ const pm = detectPackageManager ( rootDir )
103+ const packages = library === 'valibot'
104+ ? [ 'valibot' , '@valibot/to-json-schema' ]
105+ : [ 'zod' ]
106+
107+ const cmd = pm === 'npm'
108+ ? `npm install ${ packages . join ( ' ' ) } `
109+ : `${ pm } add ${ packages . join ( ' ' ) } `
110+
111+ logger . info ( `Installing ${ packages . join ( ', ' ) } ...` )
112+ try {
113+ await execAsync ( cmd , { cwd : rootDir } )
114+ logger . success ( `Installed ${ library } dependencies` )
115+ }
116+ catch ( error ) {
117+ logger . error ( `Failed to install dependencies: ${ error } ` )
118+ }
119+ }
120+
89121function countPrefixes ( keys : string [ ] ) : Map < string , number > {
90122 const counts = new Map < string , number > ( )
91123 for ( const key of keys ) {
@@ -377,6 +409,7 @@ export async function runShelveWizard(nuxt: Nuxt): Promise<void> {
377409
378410 // 1. Detect or ask for validation library
379411 let library : ValidationLibrary | null = detectValidationLibrary ( nuxt . options . rootDir )
412+ let needsInstall = false
380413 if ( library ) {
381414 logger . info ( `Detected ${ library } for schema validation` )
382415 }
@@ -394,6 +427,7 @@ export async function runShelveWizard(nuxt: Nuxt): Promise<void> {
394427 }
395428 else {
396429 library = choice as ValidationLibrary
430+ needsInstall = true
397431 }
398432 }
399433
@@ -431,14 +465,19 @@ export async function runShelveWizard(nuxt: Nuxt): Promise<void> {
431465 }
432466 }
433467
434- // 3. Generate schema (only if library selected)
435- let schemaCode : { imports : string , schema : string } | null = null
468+ // 3. Install validation library if needed
469+ if ( library && needsInstall ) {
470+ await installValidationLibrary ( nuxt . options . rootDir , library , logger )
471+ }
472+
473+ // 4. Generate schema (only if library selected)
474+ let schemaCode : { imports : string , schemaExpr : string } | null = null
436475 if ( library ) {
437476 const keys = variables . length > 0 ? variables . map ( v => v . key ) : null
438477 schemaCode = generateSchemaCode ( keys , library )
439478 }
440479
441- // 4 . Only update config if we have something to add
480+ // 5 . Only update config if we have something to add
442481 if ( ! schemaCode && ! shelveConfig ) {
443482 logger . info ( 'No configuration changes needed' )
444483 return
0 commit comments