123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340 |
- // @remove-file-on-eject
- /**
- * 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';
- // Makes the script crash on unhandled rejections instead of silently
- // ignoring them. In the future, promise rejections that are not handled will
- // terminate the Node.js process with a non-zero exit code.
- process.on('unhandledRejection', err => {
- throw 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 spawnSync = require('react-dev-utils/crossSpawn').sync;
- const os = require('os');
- const green = chalk.green;
- const cyan = chalk.cyan;
- function getGitStatus() {
- try {
- let stdout = execSync(`git status --porcelain`, {
- stdio: ['pipe', 'pipe', 'ignore'],
- }).toString();
- return stdout.trim();
- } catch (e) {
- return '';
- }
- }
- function tryGitAdd(appPath) {
- try {
- spawnSync(
- 'git',
- ['add', path.join(appPath, 'config'), path.join(appPath, 'scripts')],
- {
- stdio: 'inherit',
- }
- );
- return true;
- } catch (e) {
- return false;
- }
- }
- console.log(
- chalk.cyan.bold(
- 'NOTE: Create React App 2+ supports TypeScript, Sass, CSS Modules and more without ejecting: ' +
- 'https://reactjs.org/blog/2018/10/01/create-react-app-v2.html'
- )
- );
- console.log();
- 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(
- 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);
- }
- console.log('Ejecting...');
- 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);
- }
- }
- 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);
- // Prepare Jest config early in case it throws
- const jestConfig = createJestConfig(
- filePath => path.posix.join('<rootDir>', filePath),
- null,
- true
- );
- 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/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 react-scripts types
- .replace(
- /^\s*\/\/\/\s*<reference\s+types.+?"react-scripts".*\/>.*(?:\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.
- }
- }
- // "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)) {
- 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!'));
- 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();
- });
|