From 37fe2175b6c0fdd1654b712cbcd480e9c59f3444 Mon Sep 17 00:00:00 2001 From: Evan Bacon Date: Mon, 16 Nov 2020 20:56:57 +0100 Subject: [PATCH] replace inquirer with prompts - remove `react-dev-utils/inquirer` public import --- packages/create-react-app/createReactApp.js | 26 +- packages/create-react-app/package.json | 2 +- .../react-dev-utils/WebpackDevServerUtils.js | 6 +- packages/react-dev-utils/browsersHelper.js | 6 +- packages/react-dev-utils/inquirer.js | 12 - packages/react-dev-utils/package.json | 3 +- packages/react-scripts/package.json | 1 + packages/react-scripts/scripts/eject.js | 482 +++++++++--------- 8 files changed, 260 insertions(+), 278 deletions(-) delete mode 100644 packages/react-dev-utils/inquirer.js diff --git a/packages/create-react-app/createReactApp.js b/packages/create-react-app/createReactApp.js index aac5b33da6a..f8b7443987d 100755 --- a/packages/create-react-app/createReactApp.js +++ b/packages/create-react-app/createReactApp.js @@ -37,7 +37,7 @@ const envinfo = require('envinfo'); const execSync = require('child_process').execSync; const fs = require('fs-extra'); const hyperquest = require('hyperquest'); -const inquirer = require('inquirer'); +const prompts = require('prompts'); const os = require('os'); const path = require('path'); const semver = require('semver'); @@ -604,20 +604,18 @@ function getInstallPackage(version, originalDirectory) { for (const script of scriptsToWarn) { if (packageToInstall.startsWith(script.name)) { - return inquirer - .prompt({ - type: 'confirm', - name: 'useScript', - message: script.message, - default: false, - }) - .then(answer => { - if (!answer.useScript) { - process.exit(0); - } + return prompts({ + type: 'confirm', + name: 'useScript', + message: script.message, + initial: false, + }).then(answer => { + if (!answer.useScript) { + process.exit(0); + } - return packageToInstall; - }); + return packageToInstall; + }); } } diff --git a/packages/create-react-app/package.json b/packages/create-react-app/package.json index 915592768a1..7e0645ab92f 100644 --- a/packages/create-react-app/package.json +++ b/packages/create-react-app/package.json @@ -35,7 +35,7 @@ "envinfo": "7.7.3", "fs-extra": "9.0.1", "hyperquest": "2.1.3", - "inquirer": "7.3.3", + "prompts": "2.4.0", "semver": "7.3.2", "tar-pack": "3.4.1", "tmp": "0.2.1", diff --git a/packages/react-dev-utils/WebpackDevServerUtils.js b/packages/react-dev-utils/WebpackDevServerUtils.js index a4aafedd0a1..6a56f666aa1 100644 --- a/packages/react-dev-utils/WebpackDevServerUtils.js +++ b/packages/react-dev-utils/WebpackDevServerUtils.js @@ -13,7 +13,7 @@ const url = require('url'); const chalk = require('chalk'); const detect = require('detect-port-alt'); const isRoot = require('is-root'); -const inquirer = require('inquirer'); +const prompts = require('prompts'); const clearConsole = require('./clearConsole'); const formatWebpackMessages = require('./formatWebpackMessages'); const getProcessForPort = require('./getProcessForPort'); @@ -467,9 +467,9 @@ function choosePort(host, defaultPort) { message + `${existingProcess ? ` Probably:\n ${existingProcess}` : ''}` ) + '\n\nWould you like to run the app on another port instead?', - default: true, + initial: true, }; - inquirer.prompt(question).then(answer => { + prompts(question).then(answer => { if (answer.shouldChangePort) { resolve(port); } else { diff --git a/packages/react-dev-utils/browsersHelper.js b/packages/react-dev-utils/browsersHelper.js index a3408a14a7f..b925235f8f2 100644 --- a/packages/react-dev-utils/browsersHelper.js +++ b/packages/react-dev-utils/browsersHelper.js @@ -9,7 +9,7 @@ const browserslist = require('browserslist'); const chalk = require('chalk'); const os = require('os'); -const inquirer = require('inquirer'); +const prompts = require('prompts'); const pkgUp = require('pkg-up'); const fs = require('fs'); @@ -35,10 +35,10 @@ function shouldSetBrowsers(isInteractive) { `\n\nWould you like to add the defaults to your ${chalk.bold( 'package.json' )}?`, - default: true, + initial: true, }; - return inquirer.prompt(question).then(answer => answer.shouldSetBrowsers); + return prompts(question).then(answer => answer.shouldSetBrowsers); } function checkBrowsers(dir, isInteractive, retry = true) { diff --git a/packages/react-dev-utils/inquirer.js b/packages/react-dev-utils/inquirer.js deleted file mode 100644 index 6b8eca9ea8f..00000000000 --- a/packages/react-dev-utils/inquirer.js +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -'use strict'; - -var inquirer = require('inquirer'); - -module.exports = inquirer; diff --git a/packages/react-dev-utils/package.json b/packages/react-dev-utils/package.json index 972539eb17b..cc12a63fa42 100644 --- a/packages/react-dev-utils/package.json +++ b/packages/react-dev-utils/package.json @@ -34,7 +34,6 @@ "ignoredFiles.js", "immer.js", "InlineChunkHtmlPlugin.js", - "inquirer.js", "InterpolateHtmlPlugin.js", "launchEditor.js", "launchEditorEndpoint.js", @@ -67,7 +66,7 @@ "globby": "11.0.1", "gzip-size": "5.1.1", "immer": "7.0.9", - "inquirer": "7.3.3", + "prompts": "2.4.0", "is-root": "2.1.0", "loader-utils": "2.0.0", "open": "^7.0.2", diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json index cfbc9a46189..9c2afc422a9 100644 --- a/packages/react-scripts/package.json +++ b/packages/react-scripts/package.json @@ -70,6 +70,7 @@ "postcss-normalize": "8.0.1", "postcss-preset-env": "6.7.0", "postcss-safe-parser": "5.0.2", + "prompts": "2.4.0", "react-app-polyfill": "^2.0.0", "react-dev-utils": "^11.0.0", "react-refresh": "^0.8.3", diff --git a/packages/react-scripts/scripts/eject.js b/packages/react-scripts/scripts/eject.js index 2a494ffe24f..0972d33384a 100644 --- a/packages/react-scripts/scripts/eject.js +++ b/packages/react-scripts/scripts/eject.js @@ -16,11 +16,11 @@ process.on('unhandledRejection', err => { const fs = require('fs-extra'); const path = require('path'); +const prompts = require('prompts'); const execSync = require('child_process').execSync; const chalk = require('react-dev-utils/chalk'); const paths = require('../config/paths'); const createJestConfig = require('./utils/createJestConfig'); -const inquirer = require('react-dev-utils/inquirer'); const spawnSync = require('react-dev-utils/crossSpawn').sync; const os = require('os'); @@ -62,283 +62,279 @@ console.log( ); console.log(); -inquirer - .prompt({ - type: 'confirm', - name: 'shouldEject', - message: 'Are you sure you want to eject? This action is permanent.', - default: false, - }) - .then(answer => { - if (!answer.shouldEject) { - console.log(cyan('Close one! Eject aborted.')); - return; - } +prompts({ + type: 'confirm', + name: 'shouldEject', + message: 'Are you sure you want to eject? This action is permanent.', + initial: false, +}).then(answer => { + if (!answer.shouldEject) { + console.log(cyan('Close one! Eject aborted.')); + return; + } - const gitStatus = getGitStatus(); - if (gitStatus) { - console.error( + const gitStatus = getGitStatus(); + if (gitStatus) { + console.error( + chalk.red( + 'This git repository has untracked files or uncommitted changes:' + ) + + '\n\n' + + gitStatus + .split('\n') + .map(line => line.match(/ .*/g)[0].trim()) + .join('\n') + + '\n\n' + chalk.red( - 'This git repository has untracked files or uncommitted changes:' - ) + - '\n\n' + - gitStatus - .split('\n') - .map(line => line.match(/ .*/g)[0].trim()) - .join('\n') + - '\n\n' + - chalk.red( - 'Remove untracked files, stash or commit any changes, and try again.' - ) - ); - process.exit(1); - } + 'Remove untracked files, stash or commit any changes, and try again.' + ) + ); + process.exit(1); + } - console.log('Ejecting...'); + console.log('Ejecting...'); - const ownPath = paths.ownPath; - const appPath = paths.appPath; + const ownPath = paths.ownPath; + const appPath = paths.appPath; - function verifyAbsent(file) { - if (fs.existsSync(path.join(appPath, file))) { - console.error( - `\`${file}\` already exists in your app folder. We cannot ` + - 'continue as you would lose all the changes in that file or directory. ' + - 'Please move or delete it (maybe make a copy for backup) and run this ' + - 'command again.' - ); - process.exit(1); - } + function verifyAbsent(file) { + if (fs.existsSync(path.join(appPath, file))) { + console.error( + `\`${file}\` already exists in your app folder. We cannot ` + + 'continue as you would lose all the changes in that file or directory. ' + + 'Please move or delete it (maybe make a copy for backup) and run this ' + + 'command again.' + ); + process.exit(1); } + } - const folders = ['config', 'config/jest', 'scripts']; - - // Make shallow array of files paths - const files = folders.reduce((files, folder) => { - return files.concat( - fs - .readdirSync(path.join(ownPath, folder)) - // set full path - .map(file => path.join(ownPath, folder, file)) - // omit dirs from file list - .filter(file => fs.lstatSync(file).isFile()) - ); - }, []); + const folders = ['config', 'config/jest', 'scripts']; + + // Make shallow array of files paths + const files = folders.reduce((files, folder) => { + return files.concat( + fs + .readdirSync(path.join(ownPath, folder)) + // set full path + .map(file => path.join(ownPath, folder, file)) + // omit dirs from file list + .filter(file => fs.lstatSync(file).isFile()) + ); + }, []); - // Ensure that the app folder is clean and we won't override any files - folders.forEach(verifyAbsent); - files.forEach(verifyAbsent); + // Ensure that the app folder is clean and we won't override any files + folders.forEach(verifyAbsent); + files.forEach(verifyAbsent); - // Prepare Jest config early in case it throws - const jestConfig = createJestConfig( - filePath => path.posix.join('', filePath), - null, - true - ); + // Prepare Jest config early in case it throws + const jestConfig = createJestConfig( + filePath => path.posix.join('', filePath), + null, + true + ); - console.log(); - console.log(cyan(`Copying files into ${appPath}`)); + console.log(); + console.log(cyan(`Copying files into ${appPath}`)); - folders.forEach(folder => { - fs.mkdirSync(path.join(appPath, folder)); - }); + folders.forEach(folder => { + fs.mkdirSync(path.join(appPath, folder)); + }); - files.forEach(file => { - let content = fs.readFileSync(file, 'utf8'); + files.forEach(file => { + let content = fs.readFileSync(file, 'utf8'); - // Skip flagged files - if (content.match(/\/\/ @remove-file-on-eject/)) { + // Skip flagged files + if (content.match(/\/\/ @remove-file-on-eject/)) { + return; + } + content = + content + // Remove dead code from .js files on eject + .replace( + /\/\/ @remove-on-eject-begin([\s\S]*?)\/\/ @remove-on-eject-end/gm, + '' + ) + // Remove dead code from .applescript files on eject + .replace( + /-- @remove-on-eject-begin([\s\S]*?)-- @remove-on-eject-end/gm, + '' + ) + .trim() + '\n'; + console.log(` Adding ${cyan(file.replace(ownPath, ''))} to the project`); + fs.writeFileSync(file.replace(ownPath, appPath), content); + }); + console.log(); + + const ownPackage = require(path.join(ownPath, 'package.json')); + const appPackage = require(path.join(appPath, 'package.json')); + + console.log(cyan('Updating the dependencies')); + const ownPackageName = ownPackage.name; + if (appPackage.devDependencies) { + // We used to put react-scripts in devDependencies + if (appPackage.devDependencies[ownPackageName]) { + console.log(` Removing ${cyan(ownPackageName)} from devDependencies`); + delete appPackage.devDependencies[ownPackageName]; + } + } + appPackage.dependencies = appPackage.dependencies || {}; + if (appPackage.dependencies[ownPackageName]) { + console.log(` Removing ${cyan(ownPackageName)} from dependencies`); + delete appPackage.dependencies[ownPackageName]; + } + Object.keys(ownPackage.dependencies).forEach(key => { + // For some reason optionalDependencies end up in dependencies after install + if ( + ownPackage.optionalDependencies && + ownPackage.optionalDependencies[key] + ) { + return; + } + console.log(` Adding ${cyan(key)} to dependencies`); + appPackage.dependencies[key] = ownPackage.dependencies[key]; + }); + // Sort the deps + const unsortedDependencies = appPackage.dependencies; + appPackage.dependencies = {}; + Object.keys(unsortedDependencies) + .sort() + .forEach(key => { + appPackage.dependencies[key] = unsortedDependencies[key]; + }); + console.log(); + + console.log(cyan('Updating the scripts')); + delete appPackage.scripts['eject']; + Object.keys(appPackage.scripts).forEach(key => { + Object.keys(ownPackage.bin).forEach(binKey => { + const regex = new RegExp(binKey + ' (\\w+)', 'g'); + if (!regex.test(appPackage.scripts[key])) { return; } + appPackage.scripts[key] = appPackage.scripts[key].replace( + regex, + 'node scripts/$1.js' + ); + console.log( + ` Replacing ${cyan(`"${binKey} ${key}"`)} with ${cyan( + `"node scripts/${key}.js"` + )}` + ); + }); + }); + + console.log(); + console.log(cyan('Configuring package.json')); + // Add Jest config + console.log(` Adding ${cyan('Jest')} configuration`); + appPackage.jest = jestConfig; + + // Add Babel config + console.log(` Adding ${cyan('Babel')} preset`); + appPackage.babel = { + presets: ['react-app'], + }; + + // Add ESlint config + if (!appPackage.eslintConfig) { + console.log(` Adding ${cyan('ESLint')} configuration`); + appPackage.eslintConfig = { + extends: 'react-app', + }; + } + + fs.writeFileSync( + path.join(appPath, 'package.json'), + JSON.stringify(appPackage, null, 2) + os.EOL + ); + console.log(); + + if (fs.existsSync(paths.appTypeDeclarations)) { + try { + // Read app declarations file + let content = fs.readFileSync(paths.appTypeDeclarations, 'utf8'); + const ownContent = + fs.readFileSync(paths.ownTypeDeclarations, 'utf8').trim() + os.EOL; + + // Remove react-scripts reference since they're getting a copy of the types in their project content = content - // Remove dead code from .js files on eject + // Remove react-scripts types .replace( - /\/\/ @remove-on-eject-begin([\s\S]*?)\/\/ @remove-on-eject-end/gm, + /^\s*\/\/\/\s*.*(?:\n|$)/gm, '' ) - // Remove dead code from .applescript files on eject - .replace( - /-- @remove-on-eject-begin([\s\S]*?)-- @remove-on-eject-end/gm, - '' - ) - .trim() + '\n'; - console.log(` Adding ${cyan(file.replace(ownPath, ''))} to the project`); - fs.writeFileSync(file.replace(ownPath, appPath), content); - }); - console.log(); - - const ownPackage = require(path.join(ownPath, 'package.json')); - const appPackage = require(path.join(appPath, 'package.json')); + .trim() + os.EOL; - console.log(cyan('Updating the dependencies')); - const ownPackageName = ownPackage.name; - if (appPackage.devDependencies) { - // We used to put react-scripts in devDependencies - if (appPackage.devDependencies[ownPackageName]) { - console.log(` Removing ${cyan(ownPackageName)} from devDependencies`); - delete appPackage.devDependencies[ownPackageName]; - } - } - appPackage.dependencies = appPackage.dependencies || {}; - if (appPackage.dependencies[ownPackageName]) { - console.log(` Removing ${cyan(ownPackageName)} from dependencies`); - delete appPackage.dependencies[ownPackageName]; + fs.writeFileSync( + paths.appTypeDeclarations, + (ownContent + os.EOL + content).trim() + os.EOL + ); + } catch (e) { + // It's not essential that this succeeds, the TypeScript user should + // be able to re-create these types with ease. } - Object.keys(ownPackage.dependencies).forEach(key => { - // For some reason optionalDependencies end up in dependencies after install - if ( - ownPackage.optionalDependencies && - ownPackage.optionalDependencies[key] - ) { - return; - } - console.log(` Adding ${cyan(key)} to dependencies`); - appPackage.dependencies[key] = ownPackage.dependencies[key]; - }); - // Sort the deps - const unsortedDependencies = appPackage.dependencies; - appPackage.dependencies = {}; - Object.keys(unsortedDependencies) - .sort() - .forEach(key => { - appPackage.dependencies[key] = unsortedDependencies[key]; - }); - console.log(); + } - console.log(cyan('Updating the scripts')); - delete appPackage.scripts['eject']; - Object.keys(appPackage.scripts).forEach(key => { + // "Don't destroy what isn't ours" + if (ownPath.indexOf(appPath) === 0) { + try { + // remove react-scripts and react-scripts binaries from app node_modules Object.keys(ownPackage.bin).forEach(binKey => { - const regex = new RegExp(binKey + ' (\\w+)', 'g'); - if (!regex.test(appPackage.scripts[key])) { - return; - } - appPackage.scripts[key] = appPackage.scripts[key].replace( - regex, - 'node scripts/$1.js' - ); - console.log( - ` Replacing ${cyan(`"${binKey} ${key}"`)} with ${cyan( - `"node scripts/${key}.js"` - )}` - ); + fs.removeSync(path.join(appPath, 'node_modules', '.bin', binKey)); }); - }); - - console.log(); - console.log(cyan('Configuring package.json')); - // Add Jest config - console.log(` Adding ${cyan('Jest')} configuration`); - appPackage.jest = jestConfig; - - // Add Babel config - console.log(` Adding ${cyan('Babel')} preset`); - appPackage.babel = { - presets: ['react-app'], - }; - - // Add ESlint config - if (!appPackage.eslintConfig) { - console.log(` Adding ${cyan('ESLint')} configuration`); - appPackage.eslintConfig = { - extends: 'react-app', - }; + fs.removeSync(ownPath); + } catch (e) { + // It's not essential that this succeeds } + } - fs.writeFileSync( - path.join(appPath, 'package.json'), - JSON.stringify(appPackage, null, 2) + os.EOL + if (fs.existsSync(paths.yarnLockFile)) { + const windowsCmdFilePath = path.join( + appPath, + 'node_modules', + '.bin', + 'react-scripts.cmd' ); - console.log(); - - if (fs.existsSync(paths.appTypeDeclarations)) { + let windowsCmdFileContent; + if (process.platform === 'win32') { + // https://github.com/facebook/create-react-app/pull/3806#issuecomment-357781035 + // Yarn is diligent about cleaning up after itself, but this causes the react-scripts.cmd file + // to be deleted while it is running. This trips Windows up after the eject completes. + // We'll read the batch file and later "write it back" to match npm behavior. try { - // Read app declarations file - let content = fs.readFileSync(paths.appTypeDeclarations, 'utf8'); - const ownContent = - fs.readFileSync(paths.ownTypeDeclarations, 'utf8').trim() + os.EOL; - - // Remove react-scripts reference since they're getting a copy of the types in their project - content = - content - // Remove react-scripts types - .replace( - /^\s*\/\/\/\s*.*(?:\n|$)/gm, - '' - ) - .trim() + os.EOL; - - fs.writeFileSync( - paths.appTypeDeclarations, - (ownContent + os.EOL + content).trim() + os.EOL - ); - } catch (e) { - // It's not essential that this succeeds, the TypeScript user should - // be able to re-create these types with ease. + windowsCmdFileContent = fs.readFileSync(windowsCmdFilePath); + } catch (err) { + // If this fails we're not worse off than if we didn't try to fix it. } } - // "Don't destroy what isn't ours" - if (ownPath.indexOf(appPath) === 0) { + console.log(cyan('Running yarn...')); + spawnSync('yarnpkg', ['--cwd', process.cwd()], { stdio: 'inherit' }); + + if (windowsCmdFileContent && !fs.existsSync(windowsCmdFilePath)) { try { - // remove react-scripts and react-scripts binaries from app node_modules - Object.keys(ownPackage.bin).forEach(binKey => { - fs.removeSync(path.join(appPath, 'node_modules', '.bin', binKey)); - }); - fs.removeSync(ownPath); - } catch (e) { - // It's not essential that this succeeds + fs.writeFileSync(windowsCmdFilePath, windowsCmdFileContent); + } catch (err) { + // If this fails we're not worse off than if we didn't try to fix it. } } + } else { + console.log(cyan('Running npm install...')); + spawnSync('npm', ['install', '--loglevel', 'error'], { + stdio: 'inherit', + }); + } + console.log(green('Ejected successfully!')); + console.log(); - if (fs.existsSync(paths.yarnLockFile)) { - const windowsCmdFilePath = path.join( - appPath, - 'node_modules', - '.bin', - 'react-scripts.cmd' - ); - let windowsCmdFileContent; - if (process.platform === 'win32') { - // https://github.com/facebook/create-react-app/pull/3806#issuecomment-357781035 - // Yarn is diligent about cleaning up after itself, but this causes the react-scripts.cmd file - // to be deleted while it is running. This trips Windows up after the eject completes. - // We'll read the batch file and later "write it back" to match npm behavior. - try { - windowsCmdFileContent = fs.readFileSync(windowsCmdFilePath); - } catch (err) { - // If this fails we're not worse off than if we didn't try to fix it. - } - } - - console.log(cyan('Running yarn...')); - spawnSync('yarnpkg', ['--cwd', process.cwd()], { stdio: 'inherit' }); - - if (windowsCmdFileContent && !fs.existsSync(windowsCmdFilePath)) { - try { - fs.writeFileSync(windowsCmdFilePath, windowsCmdFileContent); - } catch (err) { - // If this fails we're not worse off than if we didn't try to fix it. - } - } - } else { - console.log(cyan('Running npm install...')); - spawnSync('npm', ['install', '--loglevel', 'error'], { - stdio: 'inherit', - }); - } - console.log(green('Ejected successfully!')); + if (tryGitAdd(appPath)) { + console.log(cyan('Staged ejected files for commit.')); console.log(); + } - if (tryGitAdd(appPath)) { - console.log(cyan('Staged ejected files for commit.')); - console.log(); - } - - console.log( - green('Please consider sharing why you ejected in this survey:') - ); - console.log(green(' http://goo.gl/forms/Bi6CZjk1EqsdelXk1')); - console.log(); - }); + console.log(green('Please consider sharing why you ejected in this survey:')); + console.log(green(' http://goo.gl/forms/Bi6CZjk1EqsdelXk1')); + console.log(); +});