UNPKG

7.99 kBJavaScriptView Raw
1// @remove-file-on-eject
2/**
3 * Copyright (c) 2015-present, Facebook, Inc.
4 *
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the root directory of this source tree.
7 */
8'use strict';
9
10// Makes the script crash on unhandled rejections instead of silently
11// ignoring them. In the future, promise rejections that are not handled will
12// terminate the Node.js process with a non-zero exit code.
13process.on('unhandledRejection', err => {
14 throw err;
15});
16
17const fs = require('fs-extra');
18const path = require('path');
19const chalk = require('chalk');
20const execSync = require('child_process').execSync;
21const spawn = require('react-dev-utils/crossSpawn');
22const { defaultBrowsers } = require('react-dev-utils/browsersHelper');
23const os = require('os');
24
25function isInGitRepository() {
26 try {
27 execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' });
28 return true;
29 } catch (e) {
30 return false;
31 }
32}
33
34function isInMercurialRepository() {
35 try {
36 execSync('hg --cwd . root', { stdio: 'ignore' });
37 return true;
38 } catch (e) {
39 return false;
40 }
41}
42
43function tryGitInit(appPath) {
44 let didInit = false;
45 try {
46 execSync('git --version', { stdio: 'ignore' });
47 if (isInGitRepository() || isInMercurialRepository()) {
48 return false;
49 }
50
51 execSync('git init', { stdio: 'ignore' });
52 didInit = true;
53
54 execSync('git add -A', { stdio: 'ignore' });
55 execSync('git commit -m "Initial commit from Create React App"', {
56 stdio: 'ignore',
57 });
58 return true;
59 } catch (e) {
60 if (didInit) {
61 // If we successfully initialized but couldn't commit,
62 // maybe the commit author config is not set.
63 // In the future, we might supply our own committer
64 // like Ember CLI does, but for now, let's just
65 // remove the Git files to avoid a half-done state.
66 try {
67 // unlinkSync() doesn't work on directories.
68 fs.removeSync(path.join(appPath, '.git'));
69 } catch (removeErr) {
70 // Ignore.
71 }
72 }
73 return false;
74 }
75}
76
77module.exports = function(
78 appPath,
79 appName,
80 verbose,
81 originalDirectory,
82 template
83) {
84 const ownPackageName = require(path.join(__dirname, '..', 'package.json'))
85 .name;
86 const ownPath = path.join(appPath, 'node_modules', ownPackageName);
87 const appPackage = require(path.join(appPath, 'package.json'));
88 const useYarn = fs.existsSync(path.join(appPath, 'yarn.lock'));
89
90 // Copy over some of the devDependencies
91 appPackage.dependencies = appPackage.dependencies || {};
92
93 // Setup the script rules
94 appPackage.scripts = {
95 start: 'react-scripts-ts start',
96 build: 'react-scripts-ts build',
97 test: 'react-scripts-ts test',
98 eject: 'react-scripts-ts eject',
99 };
100
101 // Setup the eslint config
102 appPackage.eslintConfig = {
103 extends: 'react-app',
104 };
105
106 // Setup the browsers list
107 appPackage.browserslist = defaultBrowsers;
108
109 fs.writeFileSync(
110 path.join(appPath, 'package.json'),
111 JSON.stringify(appPackage, null, 2) + os.EOL
112 );
113
114 const readmeExists = fs.existsSync(path.join(appPath, 'README.md'));
115 if (readmeExists) {
116 fs.renameSync(
117 path.join(appPath, 'README.md'),
118 path.join(appPath, 'README.old.md')
119 );
120 }
121
122 // Copy the files for the user
123 const templatePath = template
124 ? path.resolve(originalDirectory, template)
125 : path.join(ownPath, 'template');
126 if (fs.existsSync(templatePath)) {
127 fs.copySync(templatePath, appPath);
128 } else {
129 console.error(
130 `Could not locate supplied template: ${chalk.green(templatePath)}`
131 );
132 return;
133 }
134
135 // Rename gitignore after the fact to prevent npm from renaming it to .npmignore
136 // See: https://github.com/npm/npm/issues/1862
137 try {
138 fs.moveSync(
139 path.join(appPath, 'gitignore'),
140 path.join(appPath, '.gitignore'),
141 []
142 );
143 } catch (err) {
144 // Append if there's already a `.gitignore` file there
145 if (err.code === 'EEXIST') {
146 const data = fs.readFileSync(path.join(appPath, 'gitignore'));
147 fs.appendFileSync(path.join(appPath, '.gitignore'), data);
148 fs.unlinkSync(path.join(appPath, 'gitignore'));
149 } else {
150 throw err;
151 }
152 }
153
154 let command;
155 let args;
156
157 if (useYarn) {
158 command = 'yarnpkg';
159 args = ['add'];
160 } else {
161 command = 'npm';
162 args = ['install', '--save', verbose && '--verbose'].filter(e => e);
163 }
164
165 // Install dev dependencies
166 const types = [
167 '@types/node',
168 '@types/react',
169 '@types/react-dom',
170 '@types/jest',
171 'typescript',
172 ];
173
174 console.log(
175 `Installing ${types.join(', ')} as dev dependencies with ${command}...`
176 );
177 console.log();
178
179 const proc = spawn.sync(command, args.concat('-D').concat(types), { stdio: 'inherit' });
180 if (proc.status !== 0) {
181 console.error(`\`${command} ${args.join(' ')}\` failed`);
182 return;
183 }
184
185 args.push('react', 'react-dom');
186
187 // Install additional template dependencies, if present
188 const templateDependenciesPath = path.join(
189 appPath,
190 '.template.dependencies.json'
191 );
192 if (fs.existsSync(templateDependenciesPath)) {
193 const templateDependencies = require(templateDependenciesPath).dependencies;
194 args = args.concat(
195 Object.keys(templateDependencies).map(key => {
196 return `${key}@${templateDependencies[key]}`;
197 })
198 );
199 fs.unlinkSync(templateDependenciesPath);
200 }
201
202 // Install react and react-dom for backward compatibility with old CRA cli
203 // which doesn't install react and react-dom along with react-scripts
204 // or template is presetend (via --internal-testing-template)
205 if (!isReactInstalled(appPackage) || template) {
206 console.log(`Installing react and react-dom using ${command}...`);
207 console.log();
208
209 const proc = spawn.sync(command, args.concat('-D').concat(types), { stdio: 'inherit' });
210 if (proc.status !== 0) {
211 console.error(`\`${command} ${args.join(' ')}\` failed`);
212 return;
213 }
214 }
215
216 if (tryGitInit(appPath)) {
217 console.log();
218 console.log('Initialized a git repository.');
219 }
220
221 // Display the most elegant way to cd.
222 // This needs to handle an undefined originalDirectory for
223 // backward compatibility with old global-cli's.
224 let cdpath;
225 if (originalDirectory && path.join(originalDirectory, appName) === appPath) {
226 cdpath = appName;
227 } else {
228 cdpath = appPath;
229 }
230
231 // Change displayed command to yarn instead of yarnpkg
232 const displayedCommand = useYarn ? 'yarn' : 'npm';
233
234 console.log();
235 console.log(`Success! Created ${appName} at ${appPath}`);
236 console.log('Inside that directory, you can run several commands:');
237 console.log();
238 console.log(chalk.cyan(` ${displayedCommand} start`));
239 console.log(' Starts the development server.');
240 console.log();
241 console.log(
242 chalk.cyan(` ${displayedCommand} ${useYarn ? '' : 'run '}build`)
243 );
244 console.log(' Bundles the app into static files for production.');
245 console.log();
246 console.log(chalk.cyan(` ${displayedCommand} test`));
247 console.log(' Starts the test runner.');
248 console.log();
249 console.log(
250 chalk.cyan(` ${displayedCommand} ${useYarn ? '' : 'run '}eject`)
251 );
252 console.log(
253 ' Removes this tool and copies build dependencies, configuration files'
254 );
255 console.log(
256 ' and scripts into the app directory. If you do this, you can’t go back!'
257 );
258 console.log();
259 console.log('We suggest that you begin by typing:');
260 console.log();
261 console.log(chalk.cyan(' cd'), cdpath);
262 console.log(` ${chalk.cyan(`${displayedCommand} start`)}`);
263 if (readmeExists) {
264 console.log();
265 console.log(
266 chalk.yellow(
267 'You had a `README.md` file, we renamed it to `README.old.md`'
268 )
269 );
270 }
271 console.log();
272 console.log('Happy hacking!');
273};
274
275function isReactInstalled(appPackage) {
276 const dependencies = appPackage.dependencies || {};
277
278 return (
279 typeof dependencies.react !== 'undefined' &&
280 typeof dependencies['react-dom'] !== 'undefined'
281 );
282}