diff --git a/src/components/TaskRunnerPane/TaskRunnerPane.js b/src/components/TaskRunnerPane/TaskRunnerPane.js index 59b4b7d4..80d13268 100644 --- a/src/components/TaskRunnerPane/TaskRunnerPane.js +++ b/src/components/TaskRunnerPane/TaskRunnerPane.js @@ -27,6 +27,21 @@ class TaskRunnerPane extends Component { selectedTaskId: null, }; + static getDerivedStateFromProps(props, state) { + // It's possible that this task is deleted while the modal is open; + // For example, This can happen when ejecting the project, since the + // create-react-app "eject" task removes itself upon completion. + const selectedTaskExists = props.tasks.some( + task => task.id === state.selectedTaskId + ); + + if (!selectedTaskExists) { + return { selectedTaskId: null }; + } + + return null; + } + handleToggleTask = taskId => { const { tasks, runTask, abortTask } = this.props; diff --git a/src/middlewares/task.middleware.js b/src/middlewares/task.middleware.js index a997197b..fbfc729d 100644 --- a/src/middlewares/task.middleware.js +++ b/src/middlewares/task.middleware.js @@ -132,16 +132,8 @@ export default (store: any) => (next: any) => (action: any) => { additionalArgs.push('--', '--coverage'); } - /* Bypasses 'Are you sure?' check when ejecting CRA - * - * TODO: add windows support - */ - const command = - project.type === 'create-react-app' && name === 'eject' - ? 'echo yes | npm' - : 'npm'; const child = childProcess.spawn( - command, + 'npm', ['run', name, ...additionalArgs], { cwd: projectPath, @@ -158,6 +150,20 @@ export default (store: any) => (next: any) => (action: any) => { next(attachTaskMetadata(task, child.pid)); child.stdout.on('data', data => { + // The 'eject' task prompts the user, to ask if they're sure. + // We can bypass this prompt, as our UI already has an alert that + // confirms this action. + // TODO: Eject deserves its own Redux action, to avoid cluttering up + // this generic "RUN_TASK" action. + // TODO: Is there a way to "future-proof" this, in case the CRA + // confirmation copy changes? + const isEjectPrompt = data + .toString() + .includes('Are you sure you want to eject? This action is permanent'); + + if (isEjectPrompt) { + sendCommandToProcess(child, 'y'); + } next(receiveDataFromTaskExecution(task, data.toString())); }); @@ -270,3 +276,9 @@ const getDevServerCommand = ( throw new Error('Unrecognized project type: ' + projectType); } }; + +const sendCommandToProcess = (child: any, command: string) => { + // Commands have to be suffixed with '\n' to signal that the command is + // ready to be sent. Same as a regular command + hitting the enter key. + child.stdin.write(`${command}\n`); +};