eject.js 5.8 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
var fs = require('fs-extra');
eanplatter's avatar
eanplatter committed
13
var path = require('path');
Dan Abramov's avatar
Dan Abramov committed
14
var spawnSync = require('cross-spawn').sync;
15
var chalk = require('chalk');
Daniel Grant's avatar
Daniel Grant committed
16
17
18
19
var prompt = require('react-dev-utils/prompt');
var paths = require('../config/paths');
var createJestConfig = require('./utils/createJestConfig');

20
21
var green = chalk.green;
var 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
  var ownPath = paths.ownPath;
  var appPath = paths.appPath;
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

  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);
    }
  }

  var folders = [
    'config',
Daniel Grant's avatar
Daniel Grant committed
51
52
53
    'config/jest',
    'scripts',
    'scripts/utils',
54
55
  ];

Daniel Grant's avatar
Daniel Grant committed
56
57
58
59
60
61
62
63
64
65
  // Make shallow array of files paths
  var files = folders.reduce(function (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
66

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

Daniel Grant's avatar
Daniel Grant committed
71
72
73
  console.log();
  console.log(cyan('Copying files into ' + appPath));

74
75
76
  folders.forEach(function(folder) {
    fs.mkdirSync(path.join(appPath, folder))
  });
77

eanplatter's avatar
eanplatter committed
78
  files.forEach(function(file) {
Daniel Grant's avatar
Daniel Grant committed
79
80
81
82
83
84
85
    var content = fs.readFileSync(file, 'utf8');

    // Skip flagged files
    if (content.match(/\/\/ @remove-file-on-eject/)) {
      return;
    }
    content = content
86
87
88
89
      // 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, '')
90
      .trim() + '\n';
Daniel Grant's avatar
Daniel Grant committed
91
92
    console.log('  Adding ' + cyan(file.replace(ownPath, '')) + ' to the project');
    fs.writeFileSync(file.replace(ownPath, appPath), content);
eanplatter's avatar
eanplatter committed
93
94
95
  });
  console.log();

96
97
  var ownPackage = require(path.join(ownPath, 'package.json'));
  var appPackage = require(path.join(appPath, 'package.json'));
98
99
  var babelConfig = JSON.parse(fs.readFileSync(path.join(ownPath, 'babelrc'), 'utf8'));
  var eslintConfig = JSON.parse(fs.readFileSync(path.join(ownPath, 'eslintrc'), 'utf8'));
eanplatter's avatar
eanplatter committed
100

Dan Abramov's avatar
Dan Abramov committed
101
  console.log(cyan('Updating the dependencies'));
102
  var ownPackageName = ownPackage.name;
103
104
105
106
107
108
109
110
  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];
  }
eanplatter's avatar
eanplatter committed
111

112
  Object.keys(ownPackage.dependencies).forEach(function (key) {
Dan Abramov's avatar
Dan Abramov committed
113
    // For some reason optionalDependencies end up in dependencies after install
114
    if (ownPackage.optionalDependencies[key]) {
Dan Abramov's avatar
Dan Abramov committed
115
116
      return;
    }
Dan Abramov's avatar
Dan Abramov committed
117
    console.log('  Adding ' + cyan(key) + ' to devDependencies');
118
    appPackage.devDependencies[key] = ownPackage.dependencies[key];
eanplatter's avatar
eanplatter committed
119
  });
120
  console.log();
Dan Abramov's avatar
Dan Abramov committed
121
  console.log(cyan('Updating the scripts'));
Dan Abramov's avatar
Dan Abramov committed
122
  delete appPackage.scripts['eject'];
123
  Object.keys(appPackage.scripts).forEach(function (key) {
124
125
126
127
128
129
130
131
132
133
134
    Object.keys(ownPackage.bin).forEach(function (binKey) {
      var 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"')
      );
    });
eanplatter's avatar
eanplatter committed
135
136
  });

137
  console.log();
Dan Abramov's avatar
Dan Abramov committed
138
  console.log(cyan('Configuring package.json'));
Dan Abramov's avatar
Dan Abramov committed
139
  // Add Jest config
140
  console.log('  Adding ' + cyan('Jest') + ' configuration');
Christoph Pojer's avatar
Christoph Pojer committed
141
  appPackage.jest = createJestConfig(
142
    filePath => path.posix.join('<rootDir>', filePath),
143
144
    null,
    true
Christoph Pojer's avatar
Christoph Pojer committed
145
146
  );

147
  // Add Babel config
148
  console.log('  Adding ' + cyan('Babel') + ' preset');
149
150
151
  appPackage.babel = babelConfig;

  // Add ESlint config
152
  console.log('  Adding ' + cyan('ESLint') +' configuration');
153
154
  appPackage.eslintConfig = eslintConfig;

eanplatter's avatar
eanplatter committed
155
  fs.writeFileSync(
156
    path.join(appPath, 'package.json'),
157
    JSON.stringify(appPackage, null, 2) + '\n'
eanplatter's avatar
eanplatter committed
158
159
160
  );
  console.log();

161
162
163
164
165
166
  try {
    // remove react-scripts and react-scripts binaries from app node_modules
    Object.keys(ownPackage.bin).forEach(function(binKey) {
      fs.removeSync(path.join(appPath, 'node_modules', '.bin', binKey));
    });
    fs.removeSync(ownPath);
167
168
169
  } catch(e) {
    // It's not essential that this succeeds
  }
170

171
  if (fs.existsSync(paths.yarnLockFile)) {
Ville Immonen's avatar
Ville Immonen committed
172
    console.log(cyan('Running yarn...'));
173
    spawnSync('yarnpkg', [], {stdio: 'inherit'});
Ville Immonen's avatar
Ville Immonen committed
174
175
176
177
  } else {
    console.log(cyan('Running npm install...'));
    spawnSync('npm', ['install'], {stdio: 'inherit'});
  }
178
  console.log(green('Ejected successfully!'));
eanplatter's avatar
eanplatter committed
179
180
  console.log();

181
182
183
184
  console.log(green('Please consider sharing why you ejected in this survey:'));
  console.log(green('  http://goo.gl/forms/Bi6CZjk1EqsdelXk1'));
  console.log()
})