diff --git a/README.md b/README.md index 61e18ef955b..230b3ad625b 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Create React apps with no build configuration. * **Zero Configuration:** There are no configuration files or command line options. Configuring both development and production builds is handled for you so you can focus on writing code. -* **No Lock-In:** You can “graduate” to a custom setup at any time. Run a single command, and all the configuration and build dependencies will be moved directly into your project, so you can pick up where we left off. +* **No Lock-In:** You can “eject” to a custom setup at any time. Run a single command, and all the configuration and build dependencies will be moved directly into your project, so you can pick up where we left off. ## Installation @@ -47,15 +47,15 @@ It correctly bundles React in production mode and optimizes the build for the be The build is minified and the filenames include the hashes. Your app is ready to be deployed! -### `npm run graduate` +### `npm run eject` -**Note: this is a one-way operation. Once you “graduate”, you can’t go back!** +**Note: this is a one-way operation. Once you “eject”, you can’t go back!** -If you aren’t satisfied with the build tool and configuration choices, you can “graduate” at any time. This command will remove the single build dependency from your project. +If you aren’t satisfied with the build tool and configuration choices, you can “eject” at any time. This command will remove the single build dependency from your project. -Instead, it will copy all the configuration files and the transient dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `graduate` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. +Instead, it will copy all the configuration files and the transient dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. -You don’t have to ever graduate. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obliged to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. +You don’t have to ever eject. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obliged to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. ## What’s Inside? @@ -80,7 +80,7 @@ Our goal is to provide a tool that bootstraps a minimal production-ready React p This is why many features such as server rendering, experimental Babel plugins, or custom ESLint configuration, are not supported. It is hard to add features that would work for everyone without adding configuration. Having no configuration is an explicit design decision of this project. Currently, even running tests is not supported, although this limitation is temporary. -If you want an advanced feature, you can still use this tool, and later run `npm run graduate` (but then there’s no going back!) +If you want an advanced feature, you can still use this tool, and later run `npm run eject` (but then there’s no going back!) ## You Don’t Have to Use This diff --git a/bin/eject-react-app.js b/bin/eject-react-app.js new file mode 100644 index 00000000000..3a4141bb604 --- /dev/null +++ b/bin/eject-react-app.js @@ -0,0 +1,2 @@ +#!/usr/bin/env node +require('../scripts/eject'); diff --git a/bin/graduate-react-app.js b/bin/graduate-react-app.js deleted file mode 100644 index 6a95eb77c1f..00000000000 --- a/bin/graduate-react-app.js +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env node -require('../scripts/graduate'); diff --git a/package.json b/package.json index 1fe4552ec0a..751e37728af 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "bin": { "start-react-app": "./bin/start-react-app.js", "build-react-app": "./bin/build-react-app.js", - "graduate-react-app": "./bin/graduate-react-app.js" + "eject-react-app": "./bin/eject-react-app.js" }, "dependencies": { "autoprefixer": "^6.3.7", diff --git a/scripts/eject.js b/scripts/eject.js new file mode 100644 index 00000000000..709a4319c3d --- /dev/null +++ b/scripts/eject.js @@ -0,0 +1,101 @@ +/** + * 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. + */ + +var fs = require('fs'); +var path = require('path'); +var rl = require('readline'); +var rimrafSync = require('rimraf').sync; +var spawnSync = require('child_process').spawnSync; + +var prompt = function(question, cb) { + var rlInterface = rl.createInterface({ + input: process.stdin, + output: process.stdout, + }); + rlInterface.question(question + '\n', function(answer) { + rlInterface.close(); + cb(answer); + }) +} + +prompt('Are you sure you want to eject? This action is permanent. [Y/n]', function(answer) { + if (answer && answer !== 'Y' && answer !== 'yes') { + console.log('Close one! Eject aported.'); + process.exit(1); + } + + console.log('Ejecting...'); + console.log(); + var selfPath = path.join(__dirname, '..'); + var hostPath = path.join(selfPath, '..', '..'); + + var files = [ + 'scripts', + 'webpack.config.dev.js', + 'webpack.config.prod.js', + '.eslintrc' + ]; + + // Ensure that the host folder is clean and we won't override any files + files.forEach(function(file) { + if (fs.existsSync(path.join(hostPath, 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 delete it (maybe make a copy for backup) and run this ' + + 'command again.' + ); + process.exit(1); + } + }); + + // Move the files over + files.forEach(function(file) { + console.log('Moving ' + file + ' to ' + hostPath); + fs.renameSync(path.join(selfPath, file), path.join(hostPath, file)); + }); + + // These are unnecessary after graduation + fs.unlinkSync(path.join(hostPath, 'scripts', 'init.js')); + fs.unlinkSync(path.join(hostPath, 'scripts', 'eject.js')); + + console.log(); + + var selfPackage = require(path.join(selfPath, 'package.json')); + var hostPackage = require(path.join(hostPath, 'package.json')); + + console.log('Removing dependency: create-react-app-scripts'); + delete hostPackage.devDependencies['create-react-app-scripts']; + + Object.keys(selfPackage.dependencies).forEach(function (key) { + console.log('Adding dependency: ' + key); + hostPackage.devDependencies[key] = selfPackage.dependencies[key]; + }); + + console.log('Updating scripts'); + Object.keys(hostPackage.scripts).forEach(function (key) { + hostPackage.scripts[key] = 'node ./scripts/' + key + '.js' + }); + delete hostPackage.scripts['eject']; + + console.log('Writing package.json'); + fs.writeFileSync( + path.join(hostPath, 'package.json'), + JSON.stringify(hostPackage, null, 2) + ); + console.log(); + + console.log('Running npm install...'); + rimrafSync(selfPath); + spawnSync('npm', ['install'], {stdio: 'inherit'}); + console.log(); + + console.log('Done!'); + +}); diff --git a/scripts/graduate.js b/scripts/graduate.js deleted file mode 100644 index af0a348e424..00000000000 --- a/scripts/graduate.js +++ /dev/null @@ -1,82 +0,0 @@ -/** - * 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. - */ - -var fs = require('fs'); -var path = require('path'); -var rimrafSync = require('rimraf').sync; -var spawnSync = require('child_process').spawnSync; - -console.log('Graduating...'); -console.log(); - -var selfPath = path.join(__dirname, '..'); -var hostPath = path.join(selfPath, '..', '..'); - -var files = [ - 'scripts', - 'webpack.config.dev.js', - 'webpack.config.prod.js', - '.eslintrc' -]; - -// Ensure that the host folder is clean and we won't override any files -files.forEach(function(file) { - if (fs.existsSync(path.join(hostPath, 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 delete it (maybe make a copy for backup) and run this ' + - 'command again.' - ); - process.exit(1); - } -}); - -// Move the files over -files.forEach(function(file) { - console.log('Moving ' + file + ' to ' + hostPath); - fs.renameSync(path.join(selfPath, file), path.join(hostPath, file)); -}); - -// These are unnecessary after graduation -fs.unlinkSync(path.join(hostPath, 'scripts', 'init.js')); -fs.unlinkSync(path.join(hostPath, 'scripts', 'graduate.js')); - -console.log(); - -var selfPackage = require(path.join(selfPath, 'package.json')); -var hostPackage = require(path.join(hostPath, 'package.json')); - -console.log('Removing dependency: create-react-app-scripts'); -delete hostPackage.devDependencies['create-react-app-scripts']; - -Object.keys(selfPackage.dependencies).forEach(function (key) { - console.log('Adding dependency: ' + key); - hostPackage.devDependencies[key] = selfPackage.dependencies[key]; -}); - -console.log('Updating scripts'); -Object.keys(hostPackage.scripts).forEach(function (key) { - hostPackage.scripts[key] = 'node ./scripts/' + key + '.js' -}); -delete hostPackage.scripts['graduate']; - -console.log('Writing package.json'); -fs.writeFileSync( - path.join(hostPath, 'package.json'), - JSON.stringify(hostPackage, null, 2) -); -console.log(); - -console.log('Running npm install...'); -rimrafSync(selfPath); -spawnSync('npm', ['install'], {stdio: 'inherit'}); -console.log(); - -console.log('Done!'); diff --git a/scripts/init.js b/scripts/init.js index e9c12ecc6b0..c8bf6c72efe 100644 --- a/scripts/init.js +++ b/scripts/init.js @@ -25,7 +25,7 @@ module.exports = function(hostPath, appName, verbose) { // Setup the script rules hostPackage.scripts = {}; - ['start', 'build', 'graduate'].forEach(function(command) { + ['start', 'build', 'eject'].forEach(function(command) { hostPackage.scripts[command] = command + '-react-app'; }); @@ -58,7 +58,7 @@ module.exports = function(hostPath, appName, verbose) { console.log('Inside that directory, you can run several commands:'); console.log(' * npm start: Starts the development server.'); console.log(' * npm run build: Builds the app for production.'); - console.log(' * npm run graduate: Removes this tool. If you do this, you can’t go back!'); + console.log(' * npm run eject: Removes this tool. If you do this, you can’t go back!'); console.log(); console.log('We suggest that you begin by typing:'); console.log(' cd', appName);