Commit 82687dd2 authored by Ian Sutherland's avatar Ian Sutherland Committed by Dan Abramov
Browse files

Replace prompt function from react-dev-utils with Inquirer.js. (#1772)

parent be432924
3 merge requests!12191Lim.Pisey.168:/Identified - We are currently investigating reports of missing build logs. The issue has been identified and a resolution is in progress. We will provide a further update when available.Mar 21, 09:02 UTC,!12853brikk,!5717Automatically extract project file structure from build bundle file
Showing with 182 additions and 240 deletions
+182 -240
...@@ -198,29 +198,6 @@ if (openBrowser('http://localhost:3000')) { ...@@ -198,29 +198,6 @@ if (openBrowser('http://localhost:3000')) {
} }
``` ```
#### `prompt(message: string, isYesDefault: boolean): Promise<boolean>`
This function displays a console prompt to the user.
By convention, "no" should be the conservative choice.<br>
If you mistype the answer, we'll always take it as a "no".<br>
You can control the behavior on `<Enter>` with `isYesDefault`.
```js
var prompt = require('react-dev-utils/prompt');
prompt(
'Are you sure you want to eat all the candy?',
/* isYesDefault */ false
).then(shouldEat => {
if (shouldEat) {
console.log('You have successfully consumed all the candy.');
} else {
console.log('Phew, candy is still available!');
}
});
```
#### `webpackHotDevClient.js` #### `webpackHotDevClient.js`
This is an alternative client for [WebpackDevServer](https://github.com/webpack/webpack-dev-server) that shows a syntax error overlay. This is an alternative client for [WebpackDevServer](https://github.com/webpack/webpack-dev-server) that shows a syntax error overlay.
......
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
"launchEditor.js", "launchEditor.js",
"openBrowser.js", "openBrowser.js",
"openChrome.applescript", "openChrome.applescript",
"prompt.js",
"WatchMissingNodeModulesPlugin.js", "WatchMissingNodeModulesPlugin.js",
"webpackHotDevClient.js" "webpackHotDevClient.js"
], ],
......
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';
var rl = require('readline');
// Convention: "no" should be the conservative choice.
// If you mistype the answer, we'll always take it as a "no".
// You can control the behavior on <Enter> with `isYesDefault`.
function prompt(question, isYesDefault) {
if (typeof isYesDefault !== 'boolean') {
throw new Error(
'Provide explicit boolean isYesDefault as second argument.'
);
}
return new Promise(resolve => {
var rlInterface = rl.createInterface({
input: process.stdin,
output: process.stdout,
});
var hint = isYesDefault === true ? '[Y/n]' : '[y/N]';
var message = question + ' ' + hint + '\n';
rlInterface.question(message, function(answer) {
rlInterface.close();
var useDefault = answer.trim().length === 0;
if (useDefault) {
return resolve(isYesDefault);
}
var isYes = answer.match(/^(yes|y)$/i);
return resolve(isYes);
});
});
}
module.exports = prompt;
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
"fs-extra": "0.30.0", "fs-extra": "0.30.0",
"html-webpack-plugin": "2.28.0", "html-webpack-plugin": "2.28.0",
"http-proxy-middleware": "0.17.3", "http-proxy-middleware": "0.17.3",
"inquirer": "3.0.6",
"jest": "18.1.0", "jest": "18.1.0",
"object-assign": "4.1.1", "object-assign": "4.1.1",
"postcss-loader": "1.3.3", "postcss-loader": "1.3.3",
......
...@@ -20,181 +20,187 @@ const fs = require('fs-extra'); ...@@ -20,181 +20,187 @@ const fs = require('fs-extra');
const path = require('path'); const path = require('path');
const spawnSync = require('cross-spawn').sync; const spawnSync = require('cross-spawn').sync;
const chalk = require('chalk'); const chalk = require('chalk');
const prompt = require('react-dev-utils/prompt'); const inquirer = require('inquirer');
const paths = require('../config/paths'); const paths = require('../config/paths');
const createJestConfig = require('./utils/createJestConfig'); const createJestConfig = require('./utils/createJestConfig');
const green = chalk.green; const green = chalk.green;
const cyan = chalk.cyan; const cyan = chalk.cyan;
prompt( inquirer
'Are you sure you want to eject? This action is permanent.', .prompt({
false type: 'confirm',
).then(shouldEject => { name: 'shouldEject',
if (!shouldEject) { message: 'Are you sure you want to eject? This action is permanent.',
console.log(cyan('Close one! Eject aborted.')); default: false,
process.exit(1); })
} .then(answer => {
if (!answer.shouldEject) {
console.log('Ejecting...'); console.log(cyan('Close one! Eject aborted.'));
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); process.exit(1);
} }
}
const folders = ['config', 'config/jest', 'scripts', 'scripts/utils'];
// 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);
console.log();
console.log(cyan(`Copying files into ${appPath}`));
folders.forEach(folder => {
fs.mkdirSync(path.join(appPath, folder));
});
files.forEach(file => { console.log('Ejecting...');
let content = fs.readFileSync(file, 'utf8');
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);
}
}
// Skip flagged files const folders = ['config', 'config/jest', 'scripts', 'scripts/utils'];
if (content.match(/\/\/ @remove-file-on-eject/)) {
return; // 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);
console.log();
console.log(cyan(`Copying files into ${appPath}`));
folders.forEach(folder => {
fs.mkdirSync(path.join(appPath, folder));
});
files.forEach(file => {
let content = fs.readFileSync(file, 'utf8');
// 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/mg,
''
)
// Remove dead code from .applescript files on eject
.replace(
/-- @remove-on-eject-begin([\s\S]*?)-- @remove-on-eject-end/mg,
''
)
.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[ownPackageName]) {
console.log(` Removing ${cyan(ownPackageName)} from devDependencies`);
delete appPackage.devDependencies[ownPackageName];
} }
content = content if (appPackage.dependencies[ownPackageName]) {
// Remove dead code from .js files on eject console.log(` Removing ${cyan(ownPackageName)} from dependencies`);
.replace( delete appPackage.dependencies[ownPackageName];
/\/\/ @remove-on-eject-begin([\s\S]*?)\/\/ @remove-on-eject-end/mg,
''
)
// Remove dead code from .applescript files on eject
.replace(
/-- @remove-on-eject-begin([\s\S]*?)-- @remove-on-eject-end/mg,
''
)
.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[ownPackageName]) {
console.log(` Removing ${cyan(ownPackageName)} from devDependencies`);
delete appPackage.devDependencies[ownPackageName];
}
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[key]) {
return;
} }
console.log(` Adding ${cyan(key)} to devDependencies`);
appPackage.devDependencies[key] = ownPackage.dependencies[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');
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(); Object.keys(ownPackage.dependencies).forEach(key => {
console.log(cyan('Configuring package.json')); // For some reason optionalDependencies end up in dependencies after install
// Add Jest config if (ownPackage.optionalDependencies[key]) {
console.log(` Adding ${cyan('Jest')} configuration`); return;
appPackage.jest = createJestConfig( }
filePath => path.posix.join('<rootDir>', filePath), console.log(` Adding ${cyan(key)} to devDependencies`);
null, appPackage.devDependencies[key] = ownPackage.dependencies[key];
true });
); console.log();
console.log(cyan('Updating the scripts'));
// Add Babel config delete appPackage.scripts['eject'];
console.log(` Adding ${cyan('Babel')} preset`); Object.keys(appPackage.scripts).forEach(key => {
appPackage.babel = {
presets: ['react-app'],
};
// Add ESlint config
console.log(` Adding ${cyan('ESLint')} configuration`);
appPackage.eslintConfig = {
extends: 'react-app',
};
fs.writeFileSync(
path.join(appPath, 'package.json'),
JSON.stringify(appPackage, null, 2) + '\n'
);
console.log();
// "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 => { Object.keys(ownPackage.bin).forEach(binKey => {
fs.removeSync(path.join(appPath, 'node_modules', '.bin', binKey)); const regex = new RegExp(binKey + ' (\\w+)', 'g');
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(ownPath); });
} catch (e) {
// It's not essential that this succeeds console.log();
console.log(cyan('Configuring package.json'));
// Add Jest config
console.log(` Adding ${cyan('Jest')} configuration`);
appPackage.jest = createJestConfig(
filePath => path.posix.join('<rootDir>', filePath),
null,
true
);
// Add Babel config
console.log(` Adding ${cyan('Babel')} preset`);
appPackage.babel = {
presets: ['react-app'],
};
// Add ESlint config
console.log(` Adding ${cyan('ESLint')} configuration`);
appPackage.eslintConfig = {
extends: 'react-app',
};
fs.writeFileSync(
path.join(appPath, 'package.json'),
JSON.stringify(appPackage, null, 2) + '\n'
);
console.log();
// "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 => {
fs.removeSync(path.join(appPath, 'node_modules', '.bin', binKey));
});
fs.removeSync(ownPath);
} catch (e) {
// It's not essential that this succeeds
}
} }
}
if (fs.existsSync(paths.yarnLockFile)) {
if (fs.existsSync(paths.yarnLockFile)) { console.log(cyan('Running yarn...'));
console.log(cyan('Running yarn...')); spawnSync('yarnpkg', [], { stdio: 'inherit' });
spawnSync('yarnpkg', [], { stdio: 'inherit' }); } else {
} else { console.log(cyan('Running npm install...'));
console.log(cyan('Running npm install...')); spawnSync('npm', ['install'], { stdio: 'inherit' });
spawnSync('npm', ['install'], { stdio: 'inherit' }); }
} console.log(green('Ejected successfully!'));
console.log(green('Ejected successfully!')); console.log();
console.log();
console.log(
console.log(green('Please consider sharing why you ejected in this survey:')); green('Please consider sharing why you ejected in this survey:')
console.log(green(' http://goo.gl/forms/Bi6CZjk1EqsdelXk1')); );
console.log(); console.log(green(' http://goo.gl/forms/Bi6CZjk1EqsdelXk1'));
}); console.log();
});
...@@ -30,7 +30,7 @@ const clearConsole = require('react-dev-utils/clearConsole'); ...@@ -30,7 +30,7 @@ const clearConsole = require('react-dev-utils/clearConsole');
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
const getProcessForPort = require('react-dev-utils/getProcessForPort'); const getProcessForPort = require('react-dev-utils/getProcessForPort');
const openBrowser = require('react-dev-utils/openBrowser'); const openBrowser = require('react-dev-utils/openBrowser');
const prompt = require('react-dev-utils/prompt'); const inquirer = require('inquirer');
const paths = require('../config/paths'); const paths = require('../config/paths');
const config = require('../config/webpack.config.dev'); const config = require('../config/webpack.config.dev');
const devServerConfig = require('../config/webpackDevServer.config'); const devServerConfig = require('../config/webpackDevServer.config');
...@@ -114,13 +114,18 @@ detect(DEFAULT_PORT, HOST).then(port => { ...@@ -114,13 +114,18 @@ detect(DEFAULT_PORT, HOST).then(port => {
if (isInteractive) { if (isInteractive) {
clearConsole(); clearConsole();
const existingProcess = getProcessForPort(DEFAULT_PORT); const existingProcess = getProcessForPort(DEFAULT_PORT);
const question = chalk.yellow( const question = {
`Something is already running on port ${DEFAULT_PORT}.` + type: 'confirm',
`${existingProcess ? ` Probably:\n ${existingProcess}` : ''}` name: 'shouldChangePort',
) + '\n\nWould you like to run the app on another port instead?'; message: chalk.yellow(
`Something is already running on port ${DEFAULT_PORT}.` +
prompt(question, true).then(shouldChangePort => { `${existingProcess ? ` Probably:\n ${existingProcess}` : ''}`
if (shouldChangePort) { ) + '\n\nWould you like to run the app on another port instead?',
default: true,
};
inquirer.prompt(question).then(answer => {
if (answer.shouldChangePort) {
run(port); run(port);
} }
}); });
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment