@@ -5,6 +5,13 @@ import { existsSync } from 'node:fs'
55import { loadFile , writeFile , parseModule , ProxifiedModule } from 'magicast'
66import consola from 'consola'
77import { addDependency } from 'nypm'
8+ import {
9+ NuxtModule ,
10+ checkNuxtCompatibility ,
11+ fetchModules ,
12+ getNuxtVersion ,
13+ } from './_utils'
14+ import { satisfies } from 'semver'
815
916export default defineCommand ( {
1017 meta : {
@@ -29,16 +36,18 @@ export default defineCommand({
2936 async setup ( ctx ) {
3037 const cwd = resolve ( ctx . args . cwd || '.' )
3138
32- // TODO: Resolve and validate npm package name first
33- const npmPackage = ctx . args . moduleName
39+ const r = await resolveModule ( ctx . args . moduleName , cwd )
40+ if ( r === false ) {
41+ return
42+ }
3443
3544 // Add npm dependency
3645 if ( ! ctx . args . skipInstall ) {
37- consola . info ( `Installing dev dependency \`${ npmPackage } \`` )
38- await addDependency ( npmPackage , { cwd, dev : true } ) . catch ( ( err ) => {
46+ consola . info ( `Installing dev dependency \`${ r . pkg } \`` )
47+ await addDependency ( r . pkg , { cwd, dev : true } ) . catch ( ( err ) => {
3948 consola . error ( err )
4049 consola . error (
41- `Please manually install \`${ npmPackage } \` as a dev dependency` ,
50+ `Please manually install \`${ r . pkg } \` as a dev dependency` ,
4251 )
4352 } )
4453 }
@@ -50,17 +59,17 @@ export default defineCommand({
5059 config . modules = [ ]
5160 }
5261 for ( let i = 0 ; i < config . modules . length ; i ++ ) {
53- if ( config . modules [ i ] === npmPackage ) {
54- consola . info ( `\`${ npmPackage } \` is already in the \`modules\`` )
62+ if ( config . modules [ i ] === r . pkgName ) {
63+ consola . info ( `\`${ r . pkgName } \` is already in the \`modules\`` )
5564 return
5665 }
5766 }
58- consola . info ( `Adding \`${ npmPackage } \` to the \`modules\`` )
59- config . modules . push ( npmPackage )
67+ consola . info ( `Adding \`${ r . pkgName } \` to the \`modules\`` )
68+ config . modules . push ( r . pkgName )
6069 } ) . catch ( ( err ) => {
6170 consola . error ( err )
6271 consola . error (
63- `Please manually add \`${ npmPackage } \` to the \`modules\` in \`nuxt.config.ts\`` ,
72+ `Please manually add \`${ r . pkgName } \` to the \`modules\` in \`nuxt.config.ts\`` ,
6473 )
6574 } )
6675 }
@@ -102,3 +111,97 @@ export default defineNuxtConfig({
102111 modules: []
103112})`
104113}
114+
115+ // Based on https://github.com/dword-design/package-name-regex
116+ const packageRegex =
117+ / ^ ( @ [ a - z 0 - 9 - ~ ] [ a - z 0 - 9 - ._ ~ ] * \/ ) ? ( [ a - z 0 - 9 - ~ ] [ a - z 0 - 9 - ._ ~ ] * ) ( @ [ ^ @ ] + ) ? $ /
118+
119+ async function resolveModule (
120+ moduleName : string ,
121+ cwd : string ,
122+ ) : Promise <
123+ | false
124+ | {
125+ nuxtModule ?: NuxtModule
126+ pkg : string
127+ pkgName : string
128+ pkgVersion : string
129+ }
130+ > {
131+ let pkgName = moduleName
132+ let pkgVersion : string | undefined
133+
134+ const reMatch = moduleName . match ( packageRegex )
135+ if ( reMatch ) {
136+ if ( reMatch [ 3 ] ) {
137+ pkgName = `${ reMatch [ 1 ] || '' } ${ reMatch [ 2 ] || '' } `
138+ pkgVersion = reMatch [ 3 ] . slice ( 1 )
139+ }
140+ } else {
141+ consola . error ( `Invalid package name \`${ pkgName } \`.` )
142+ return false
143+ }
144+
145+ const modulesDB = await fetchModules ( ) . catch ( ( err ) => {
146+ consola . warn ( 'Cannot search in the Nuxt Modules database: ' + err )
147+ return [ ]
148+ } )
149+
150+ const matchedModule = modulesDB . find (
151+ ( module ) => module . name === moduleName || module . npm === pkgName ,
152+ )
153+
154+ if ( matchedModule ?. npm ) {
155+ pkgName = matchedModule . npm
156+ }
157+
158+ if ( matchedModule && matchedModule . compatibility . nuxt ) {
159+ // Get local Nuxt version
160+ const nuxtVersion = await getNuxtVersion ( cwd )
161+
162+ // Check for Module Compatibility
163+ if ( ! checkNuxtCompatibility ( matchedModule , nuxtVersion ) ) {
164+ consola . warn (
165+ `The module \`${ pkgName } \` is not compatible with Nuxt \`${ nuxtVersion } \` (requires \`${ matchedModule . compatibility . nuxt } \`)` ,
166+ )
167+ const shouldContinue = await consola . prompt (
168+ 'Do you want to continue installing incompatible version?' ,
169+ {
170+ type : 'confirm' ,
171+ initial : false ,
172+ } ,
173+ )
174+ if ( shouldContinue !== true ) {
175+ return false
176+ }
177+ }
178+
179+ // Match corresponding version of module for local Nuxt version
180+ const versionMap = matchedModule . compatibility . versionMap
181+ if ( versionMap ) {
182+ for ( const [ _nuxtVersion , _moduleVersion ] of Object . entries ( versionMap ) ) {
183+ if ( satisfies ( nuxtVersion , _nuxtVersion ) ) {
184+ if ( ! pkgVersion ) {
185+ pkgVersion = _moduleVersion
186+ } else {
187+ consola . warn (
188+ `Recommended version of \`${ pkgName } \` for Nuxt \`${ nuxtVersion } \` is \`${ _moduleVersion } \` but you have requested \`${ pkgVersion } \`` ,
189+ )
190+ pkgVersion = await consola . prompt ( 'Choose a version:' , {
191+ type : 'select' ,
192+ options : [ _moduleVersion , pkgVersion ] ,
193+ } )
194+ }
195+ break
196+ }
197+ }
198+ }
199+ }
200+
201+ return {
202+ nuxtModule : matchedModule ,
203+ pkg : `${ pkgName } @${ pkgVersion || 'latest' } ` ,
204+ pkgName,
205+ pkgVersion : pkgVersion || 'latest' ,
206+ }
207+ }
0 commit comments