eject.js 5.91 KB
Newer Older
Daniel Grant's avatar
Daniel Grant committed
1
// @remove-file-on-eject
eanplatter's avatar
eanplatter committed
2
3
4
5
6
7
8
9
/**
 * 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.
 */
10
'use strict';
eanplatter's avatar
eanplatter committed
11

12
13
14
15
16
17
18
const fs = require('fs-extra');
const path = require('path');
const spawnSync = require('cross-spawn').sync;
const chalk = require('chalk');
const prompt = require('react-dev-utils/prompt');
const paths = require('../config/paths');
const createJestConfig = require('./utils/createJestConfig');
Daniel Grant's avatar
Daniel Grant committed
19

20
21
const green = chalk.green;
const cyan = chalk.cyan;
eanplatter's avatar
eanplatter committed
22

23
24
25
26
prompt(
  'Are you sure you want to eject? This action is permanent.',
  false
).then(shouldEject => {
Dan Abramov's avatar
Dan Abramov committed
27
  if (!shouldEject) {
28
    console.log(cyan('Close one! Eject aborted.'));
eanplatter's avatar
eanplatter committed
29
30
31
32
    process.exit(1);
  }

  console.log('Ejecting...');
Dan Abramov's avatar
Dan Abramov committed
33

34
35
  const ownPath = paths.ownPath;
  const appPath = paths.appPath;
36
37
38
39

  function verifyAbsent(file) {
    if (fs.existsSync(path.join(appPath, file))) {
      console.error(
40
41
42
43
        `\`${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.'
44
45
46
47
48
      );
      process.exit(1);
    }
  }

49
  const folders = ['config', 'config/jest', 'scripts', 'scripts/utils'];
50

Daniel Grant's avatar
Daniel Grant committed
51
  // Make shallow array of files paths
52
53
54
55
56
57
58
59
60
61
62
63
64
  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())
      );
    },
    []
  );
eanplatter's avatar
eanplatter committed
65

66
  // Ensure that the app folder is clean and we won't override any files
67
68
  folders.forEach(verifyAbsent);
  files.forEach(verifyAbsent);
eanplatter's avatar
eanplatter committed
69

Daniel Grant's avatar
Daniel Grant committed
70
  console.log();
71
  console.log(cyan(`Copying files into ${appPath}`));
Daniel Grant's avatar
Daniel Grant committed
72

73
74
  folders.forEach(folder => {
    fs.mkdirSync(path.join(appPath, folder));
75
  });
76

77
78
  files.forEach(file => {
    let content = fs.readFileSync(file, 'utf8');
Daniel Grant's avatar
Daniel Grant committed
79
80
81
82
83
84

    // Skip flagged files
    if (content.match(/\/\/ @remove-file-on-eject/)) {
      return;
    }
    content = content
85
      // Remove dead code from .js files on eject
86
87
88
89
      .replace(
        /\/\/ @remove-on-eject-begin([\s\S]*?)\/\/ @remove-on-eject-end/mg,
        ''
      )
90
      // Remove dead code from .applescript files on eject
91
92
93
94
      .replace(
        /-- @remove-on-eject-begin([\s\S]*?)-- @remove-on-eject-end/mg,
        ''
      )
95
      .trim() + '\n';
96
    console.log(`  Adding ${cyan(file.replace(ownPath, ''))} to the project`);
Daniel Grant's avatar
Daniel Grant committed
97
    fs.writeFileSync(file.replace(ownPath, appPath), content);
eanplatter's avatar
eanplatter committed
98
99
100
  });
  console.log();

101
102
103
104
105
106
107
108
  const ownPackage = require(path.join(ownPath, 'package.json'));
  const appPackage = require(path.join(appPath, 'package.json'));
  const babelConfig = JSON.parse(
    fs.readFileSync(path.join(ownPath, '.babelrc'), 'utf8')
  );
  const eslintConfig = JSON.parse(
    fs.readFileSync(path.join(ownPath, '.eslintrc'), 'utf8')
  );
eanplatter's avatar
eanplatter committed
109

Dan Abramov's avatar
Dan Abramov committed
110
  console.log(cyan('Updating the dependencies'));
111
  const ownPackageName = ownPackage.name;
112
  if (appPackage.devDependencies[ownPackageName]) {
113
    console.log(`  Removing ${cyan(ownPackageName)} from devDependencies`);
114
115
116
    delete appPackage.devDependencies[ownPackageName];
  }
  if (appPackage.dependencies[ownPackageName]) {
117
    console.log(`  Removing ${cyan(ownPackageName)} from dependencies`);
118
119
    delete appPackage.dependencies[ownPackageName];
  }
eanplatter's avatar
eanplatter committed
120

121
  Object.keys(ownPackage.dependencies).forEach(key => {
Dan Abramov's avatar
Dan Abramov committed
122
    // For some reason optionalDependencies end up in dependencies after install
123
    if (ownPackage.optionalDependencies[key]) {
Dan Abramov's avatar
Dan Abramov committed
124
125
      return;
    }
126
    console.log(`  Adding ${cyan(key)} to devDependencies`);
127
    appPackage.devDependencies[key] = ownPackage.dependencies[key];
eanplatter's avatar
eanplatter committed
128
  });
129
  console.log();
Dan Abramov's avatar
Dan Abramov committed
130
  console.log(cyan('Updating the scripts'));
Dan Abramov's avatar
Dan Abramov committed
131
  delete appPackage.scripts['eject'];
132
133
134
135
136
137
138
  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'
      );
139
      console.log(
140
        `  Replacing ${cyan(`"${binKey} ${key}"`)} with ${cyan(`"node scripts/${key}.js"`)}`
141
142
      );
    });
eanplatter's avatar
eanplatter committed
143
144
  });

145
  console.log();
Dan Abramov's avatar
Dan Abramov committed
146
  console.log(cyan('Configuring package.json'));
Dan Abramov's avatar
Dan Abramov committed
147
  // Add Jest config
148
  console.log(`  Adding ${cyan('Jest')} configuration`);
Christoph Pojer's avatar
Christoph Pojer committed
149
  appPackage.jest = createJestConfig(
150
    filePath => path.posix.join('<rootDir>', filePath),
151
152
    null,
    true
Christoph Pojer's avatar
Christoph Pojer committed
153
154
  );

155
  // Add Babel config
156
  console.log(`  Adding ${cyan('Babel')} preset`);
157
158
159
  appPackage.babel = babelConfig;

  // Add ESlint config
160
  console.log(`  Adding ${cyan('ESLint')} configuration`);
161
162
  appPackage.eslintConfig = eslintConfig;

eanplatter's avatar
eanplatter committed
163
  fs.writeFileSync(
164
    path.join(appPath, 'package.json'),
165
    JSON.stringify(appPackage, null, 2) + '\n'
eanplatter's avatar
eanplatter committed
166
167
168
  );
  console.log();

169
170
171
172
  // "Don't destroy what isn't ours"
  if (ownPath.indexOf(appPath) === 0) {
    try {
      // remove react-scripts and react-scripts binaries from app node_modules
173
      Object.keys(ownPackage.bin).forEach(binKey => {
174
175
176
        fs.removeSync(path.join(appPath, 'node_modules', '.bin', binKey));
      });
      fs.removeSync(ownPath);
177
    } catch (e) {
178
179
      // It's not essential that this succeeds
    }
180
  }
181

182
  if (fs.existsSync(paths.yarnLockFile)) {
Ville Immonen's avatar
Ville Immonen committed
183
    console.log(cyan('Running yarn...'));
184
    spawnSync('yarnpkg', [], { stdio: 'inherit' });
Ville Immonen's avatar
Ville Immonen committed
185
186
  } else {
    console.log(cyan('Running npm install...'));
187
    spawnSync('npm', ['install'], { stdio: 'inherit' });
Ville Immonen's avatar
Ville Immonen committed
188
  }
189
  console.log(green('Ejected successfully!'));
eanplatter's avatar
eanplatter committed
190
191
  console.log();

192
193
  console.log(green('Please consider sharing why you ejected in this survey:'));
  console.log(green('  http://goo.gl/forms/Bi6CZjk1EqsdelXk1'));
194
195
  console.log();
});