11#!/usr/bin/env node
2- function runCommand ( command , options ) {
2+
3+ process . exitCode = 0 ;
4+
5+ /**
6+ * @param {string } command process to run
7+ * @param {string[] } args commandline arguments
8+ * @returns {Promise<void> } promise
9+ */
10+ const runCommand = ( command , args ) => {
311 const cp = require ( "child_process" ) ;
412 return new Promise ( ( resolve , reject ) => {
5- const executedCommand = cp . spawn ( command , options , {
13+ const executedCommand = cp . spawn ( command , args , {
614 stdio : "inherit" ,
715 shell : true
816 } ) ;
@@ -13,69 +21,146 @@ function runCommand(command, options) {
1321
1422 executedCommand . on ( "exit" , code => {
1523 if ( code === 0 ) {
16- resolve ( true ) ;
24+ resolve ( ) ;
1725 } else {
1826 reject ( ) ;
1927 }
2028 } ) ;
2129 } ) ;
22- }
30+ } ;
2331
24- let webpackCliInstalled = false ;
25- try {
26- require . resolve ( "webpack-cli" ) ;
27- webpackCliInstalled = true ;
28- } catch ( err ) {
29- webpackCliInstalled = false ;
30- }
32+ /**
33+ * @param { string } packageName name of the package
34+ * @returns { boolean } is the package installed?
35+ */
36+ const isInstalled = packageName => {
37+ try {
38+ require . resolve ( packageName ) ;
3139
32- if ( ! webpackCliInstalled ) {
40+ return true ;
41+ } catch ( err ) {
42+ return false ;
43+ }
44+ } ;
45+
46+ /**
47+ * @typedef {Object } CliOption
48+ * @property {string } name display name
49+ * @property {string } package npm package name
50+ * @property {string } alias shortcut for choice
51+ * @property {boolean } installed currently installed?
52+ * @property {string } url homepage
53+ * @property {string } description description
54+ */
55+
56+ /** @type {CliOption[] } */
57+ const CLIs = [
58+ {
59+ name : "webpack-cli" ,
60+ package : "webpack-cli" ,
61+ alias : "cli" ,
62+ installed : isInstalled ( "webpack-cli" ) ,
63+ url : "https://github.com/webpack/webpack-cli" ,
64+ description : "The original webpack full-featured CLI."
65+ } ,
66+ {
67+ name : "webpack-command" ,
68+ package : "webpack-command" ,
69+ alias : "command" ,
70+ installed : isInstalled ( "webpack-command" ) ,
71+ url : "https://github.com/webpack-contrib/webpack-command" ,
72+ description : "A lightweight, opinionated webpack CLI."
73+ }
74+ ] ;
75+
76+ const installedClis = CLIs . filter ( cli => cli . installed ) ;
77+
78+ if ( installedClis . length === 0 ) {
3379 const path = require ( "path" ) ;
3480 const fs = require ( "fs" ) ;
3581 const readLine = require ( "readline" ) ;
36- const isYarn = fs . existsSync ( path . resolve ( process . cwd ( ) , "yarn.lock" ) ) ;
3782
38- const packageManager = isYarn ? "yarn" : "npm" ;
39- const options = [ "install" , "-D" , "webpack-cli" ] ;
83+ let notify =
84+ "One CLI for webpack must be installed. These are recommended choices, delivered as separate packages:" ;
4085
41- if ( isYarn ) {
42- options [ 0 ] = "add" ;
86+ for ( const item of CLIs ) {
87+ notify += `\n - ${ item . name } ( ${ item . url } )\n ${ item . description } ` ;
4388 }
4489
45- const commandToBeRun = ` ${ packageManager } ${ options . join ( " " ) } ` ;
90+ console . error ( notify ) ;
4691
47- const question = `Would you like to install webpack-cli? (That will run ${ commandToBeRun } ) (yes/NO)` ;
92+ const isYarn = fs . existsSync ( path . resolve ( process . cwd ( ) , "yarn.lock" ) ) ;
93+
94+ const packageManager = isYarn ? "yarn" : "npm" ;
95+ const installOptions = [ isYarn ? "add" : "install" , "-D" ] ;
96+
97+ console . error (
98+ `We will use "${ packageManager } " to install the CLI via "${ packageManager } ${ installOptions . join (
99+ " "
100+ ) } ".`
101+ ) ;
102+
103+ let question = `Which one do you like to install (${ CLIs . map (
104+ item => item . name
105+ ) . join ( "/" ) } ):\n`;
48106
49- console . error ( "The CLI moved into a separate package: webpack-cli" ) ;
50107 const questionInterface = readLine . createInterface ( {
51108 input : process . stdin ,
52- output : process . stdout
109+ output : process . stderr
53110 } ) ;
54111 questionInterface . question ( question , answer => {
55112 questionInterface . close ( ) ;
56- switch ( answer . toLowerCase ( ) ) {
57- case "y" :
58- case "yes" :
59- case "1" : {
60- runCommand ( packageManager , options )
61- . then ( result => {
62- return require ( "webpack-cli" ) ; //eslint-disable-line
63- } )
64- . catch ( error => {
65- console . error ( error ) ;
66- process . exitCode = 1 ;
67- } ) ;
68- break ;
69- }
70- default : {
71- console . error (
72- "It needs to be installed alongside webpack to use the CLI"
73- ) ;
74- process . exitCode = 1 ;
75- break ;
76- }
113+
114+ const normalizedAnswer = answer . toLowerCase ( ) ;
115+ const selectedPackage = CLIs . find ( item => {
116+ return item . name === normalizedAnswer || item . alias === normalizedAnswer ;
117+ } ) ;
118+
119+ if ( ! normalizedAnswer ) {
120+ console . error (
121+ "One CLI needs to be installed alongside webpack to use the CLI."
122+ ) ;
123+ process . exitCode = 1 ;
124+
125+ return ;
126+ } else if ( ! selectedPackage ) {
127+ console . error (
128+ "No matching choice.\n" +
129+ "One CLI needs to be installed alongside webpack to use the CLI.\n" +
130+ "Try to installing your CLI of choice manually."
131+ ) ;
132+ process . exitCode = 1 ;
133+
134+ return ;
77135 }
136+
137+ const packageName = selectedPackage . package ;
138+
139+ console . log (
140+ `Installing '${
141+ selectedPackage . name
142+ } ' (running '${ packageManager } ${ installOptions . join (
143+ " "
144+ ) } ${ packageName } ')...`
145+ ) ;
146+
147+ runCommand ( packageManager , installOptions . concat ( packageName ) )
148+ . then ( ( ) => {
149+ require ( packageName ) ; //eslint-disable-line
150+ } )
151+ . catch ( error => {
152+ console . error ( error ) ;
153+ process . exitCode = 1 ;
154+ } ) ;
78155 } ) ;
156+ } else if ( installedClis . length === 1 ) {
157+ require ( installedClis [ 0 ] . package ) ; // eslint-disable-line
79158} else {
80- require ( "webpack-cli" ) ; // eslint-disable-line
159+ console . warn (
160+ `You have installed ${ installedClis
161+ . map ( item => item . name )
162+ . join (
163+ " and "
164+ ) } together. To work with the "webpack" command you need only one CLI package, please remove one of them or use them directly via their binary.`
165+ ) ;
81166}
0 commit comments