2023-08-03 18:33:58 -03:00
import { Command } from 'commander' ;
import ConfigsProcessor from './configsProcessor.js' ;
import configs from './configs.js' ;
2023-08-01 16:55:49 -03:00
import Workspace from './workspace.js' ;
2023-08-04 11:12:32 -03:00
import c from 'picocolors' ;
2023-08-03 18:33:58 -03:00
import path from 'node:path' ;
2023-08-04 11:12:32 -03:00
import { createSpinner } from 'nanospinner' ;
import count from './lib/count.js' ;
2023-08-04 16:10:24 -03:00
import prompts from 'prompts' ;
2023-08-14 14:33:44 -03:00
import ConfigsFile from './configsFile.js' ;
import * as cardinal from 'cardinal' ;
import ansi from 'sisteransi' ;
2023-08-23 10:54:15 -03:00
import PackageInstaller from './packageInstaller.js' ;
import notNull from './lib/notNull.js' ;
2023-08-14 14:33:44 -03:00
const stdout = process . stdout ;
2023-08-01 16:20:17 -03:00
export default class Cli {
2023-08-03 18:33:58 -03:00
# program = new Command ( ) ;
2023-08-01 16:20:17 -03:00
2023-08-03 18:33:58 -03:00
/** @type {import('./types').CliArgs} */
args = {
dir : process . cwd ( ) ,
} ;
2023-08-01 16:20:17 -03:00
2023-08-04 10:07:59 -03:00
/ * *
* @ param { import ( './types' ) . CliArgs } [ args ] Cli arguments object
* /
constructor ( args ) {
2023-08-03 18:33:58 -03:00
this . # program
. option ( '--packages <string...>' )
2023-08-04 10:07:59 -03:00
. option ( '--dir <path>' , undefined )
2023-08-23 10:54:15 -03:00
. option ( '--merge-to-root' )
. option ( '--install-pkgs' )
2023-08-04 10:07:59 -03:00
. parse ( ) ;
2023-08-03 16:53:26 -03:00
2023-08-03 18:33:58 -03:00
this . args = {
... this . args ,
... this . # program . opts ( ) ,
2023-08-04 10:07:59 -03:00
... args ,
2023-08-03 18:33:58 -03:00
} ;
2023-08-03 16:53:26 -03:00
2023-08-03 18:33:58 -03:00
this . args . dir = ! this . args . dir . startsWith ( '/' )
? path . join ( process . cwd ( ) , this . args . dir )
: this . args . dir ;
2023-08-03 16:53:26 -03:00
2023-08-03 18:33:58 -03:00
}
2023-08-03 16:53:26 -03:00
2023-08-03 18:33:58 -03:00
async run ( ) {
2023-08-03 16:53:26 -03:00
2023-08-23 10:54:15 -03:00
process . chdir ( this . args . dir ) ;
2023-08-04 11:12:32 -03:00
const spinner = createSpinner ( 'Detecting workspace configuration' ) ;
2023-08-03 18:33:58 -03:00
const processor = new ConfigsProcessor ( { configs } ) ;
2023-08-04 10:07:59 -03:00
const workspace = new Workspace ( this . args . dir , this . args ? . packages ) ;
2023-08-04 11:12:32 -03:00
2023-08-04 10:07:59 -03:00
let packages = ( await workspace . getPackages ( ) )
2023-08-03 18:33:58 -03:00
. map ( pkg => {
2023-08-04 11:12:32 -03:00
spinner . update ( { text : ` Detecting configuration for package ${ c . bold ( c . blue ( pkg . name ) ) } ` } ) ;
2023-08-03 18:33:58 -03:00
pkg . config = processor . detectConfig ( pkg ) ;
2023-08-04 11:12:32 -03:00
2023-08-03 18:33:58 -03:00
return pkg ;
2023-08-03 16:53:26 -03:00
} ) ;
2023-08-04 11:12:32 -03:00
2023-08-04 16:10:24 -03:00
spinner . success ( {
text :
'Detecting workspace configuration ' +
c . dim ( ` ${ count . packagesWithConfigs ( packages ) } configs founded \n ` ) ,
2023-08-04 11:12:32 -03:00
} ) ;
2023-08-23 11:40:16 -03:00
const merge = this . args . mergeToRoot !== undefined ? this . args . mergeToRoot : packages . length > 1 ?
2023-08-14 14:33:44 -03:00
/** @type {{merge: boolean}} */
2023-08-04 16:10:24 -03:00
( await prompts ( {
name : 'merge' ,
message :
2023-08-14 14:33:44 -03:00
` Would you like to merge all configuration files into one root ${ c . blue ( 'eslint.config.js?' ) } ` +
c . italic ( c . dim ( '\nAll configurations will be applied to the entire workspace and packages' ) ) ,
2023-08-04 16:10:24 -03:00
initial : true ,
type : 'confirm' ,
} ) ) . merge : true ;
console . log ( c . dim ( '\nPlease select which options you prefer\n' ) ) ;
packages = await processor . questionConfig (
merge ? workspace . mergePackages ( packages ) : packages ,
configs . filter ( c => c . manual ) ,
) ;
2023-08-01 16:20:17 -03:00
2023-08-14 14:33:44 -03:00
const fileHandler = new ConfigsFile ( configs , packages . find ( c => c . root ) ? . path ) ;
2023-08-01 16:20:17 -03:00
2023-08-14 14:33:44 -03:00
for ( const pkg of packages ) {
2023-08-07 16:25:04 -03:00
2023-08-14 14:33:44 -03:00
pkg . configFile = fileHandler . generateObj ( pkg ) ;
2023-08-14 14:58:41 -03:00
pkg . configFile . content = await fileHandler . generate ( pkg . configFile ) ;
/** @type {boolean} */
const shouldWrite =
/** @type {{write: boolean}} */
( await prompts ( {
type : 'confirm' ,
name : 'write' ,
message : ` Do you want to write this config file for ${ pkg . root
? c . blue ( 'the root directory' )
: c . blue ( pkg . name )
2023-08-14 14:33:44 -03:00
} ? \ n \ n$ { cardinal . highlight ( pkg . configFile . content ) } ` ,
2023-08-14 14:58:41 -03:00
initial : true ,
} ) ) . write ;
2023-08-14 14:33:44 -03:00
stdout . write ( ansi . erase . lines ( pkg . configFile . content . split ( '\n' ) . length + 2 ) ) ;
2023-08-14 14:58:41 -03:00
if ( shouldWrite ) await fileHandler . write ( pkg . configFile . path , pkg . configFile . content ) ;
2023-08-14 14:33:44 -03:00
}
2023-08-01 16:20:17 -03:00
2023-08-23 10:54:15 -03:00
const packagesMap = new Map ( packages . map ( p => [ p . path , [ ... notNull ( p . configFile ) . imports . keys ( ) ] ] ) ) ;
const installer = new PackageInstaller ( packagesMap , packages . find ( p => p . root === true ) ? . path ? ? this . args . dir ) ;
/** @type {boolean | 'changePackage'} */
let installPkgs = this . args . installPkgs !== undefined ? true :
/** @type {{install: boolean | 'changePackage'}} */
( await prompts ( {
name : 'install' ,
message :
2023-08-23 11:40:16 -03:00
` Would you like to ESLit to install the npm packages with ${ c . green ( installer . packageManager . name ) } ? \n ${ c . reset ( c . dim ( ` Packages to install: ${ [ ... new Set ( [ ... packagesMap . values ( ) ] ) ] . join ( ' ' ) } \n ` ) ) } ` ,
2023-08-23 10:54:15 -03:00
choices : [
{ title : 'Yes, install all packages' , value : true , description : installer . packageManager . description } ,
{ title : 'No, I will install them manually' , value : false } ,
{ title : 'Change package manager' , value : 'changePackage' } ,
] ,
type : 'select' ,
} ) ) . install ;
if ( installPkgs === 'changePackage' ) {
/** @type {{manager: import('./types').PackageManagerName}} */
const prompt = await prompts ( {
name : 'manager' ,
message : 'What package manager do you want ESLit to use?' ,
choices : Object . values ( installer . packageManagers ) . map ( m => {
return { title : m . name , description : m . description , value : m . name } ;
} ) ,
type : 'select' ,
} ) ;
installer . packageManager = installer . packageManagers [ prompt . manager ] ;
2023-08-23 11:40:16 -03:00
if ( ! installer . packageManager ) throw console . log ( c . red ( 'You must select a package manager' ) ) ;
2023-08-23 10:54:15 -03:00
installPkgs = true ;
}
if ( installPkgs ) await installer . install ( ) ;
2023-08-01 16:20:17 -03:00
}
2023-08-03 18:33:58 -03:00
2023-08-01 16:20:17 -03:00
}