@@ -60,6 +60,9 @@ var execSync = require('child_process').execSync;
60
60
var spawn = require ( 'cross-spawn' ) ;
61
61
var semver = require ( 'semver' ) ;
62
62
var dns = require ( 'dns' ) ;
63
+ var tmp = require ( 'tmp' ) ;
64
+ var unpack = require ( 'tar-pack' ) . unpack ;
65
+ var hyperquest = require ( 'hyperquest' ) ;
63
66
64
67
var projectName ;
65
68
@@ -201,23 +204,34 @@ function install(useYarn, dependencies, verbose, isOnline) {
201
204
202
205
function run ( root , appName , version , verbose , originalDirectory , template ) {
203
206
var packageToInstall = getInstallPackage ( version ) ;
204
- var packageName = getPackageName ( packageToInstall ) ;
205
-
206
207
var allDependencies = [ 'react' , 'react-dom' , packageToInstall ] ;
207
208
208
209
console . log ( 'Installing packages. This might take a couple minutes.' ) ;
209
- console . log (
210
- 'Installing ' + chalk . cyan ( 'react' ) + ', ' + chalk . cyan ( 'react-dom' ) +
211
- ', and ' + chalk . cyan ( packageName ) + '...'
212
- ) ;
213
- console . log ( ) ;
214
-
210
+
215
211
var useYarn = shouldUseYarn ( ) ;
216
- checkIfOnline ( useYarn )
217
- . then ( function ( isOnline ) {
218
- return install ( useYarn , allDependencies , verbose , isOnline ) ;
212
+ getPackageName ( packageToInstall )
213
+ . then ( function ( packageName ) {
214
+ return checkIfOnline ( useYarn ) . then ( function ( isOnline ) {
215
+ return {
216
+ isOnline : isOnline ,
217
+ packageName : packageName ,
218
+ } ;
219
+ } ) ;
220
+ } )
221
+ . then ( function ( info ) {
222
+ var isOnline = info . isOnline ;
223
+ var packageName = info . packageName ;
224
+ console . log (
225
+ 'Installing ' + chalk . cyan ( 'react' ) + ', ' + chalk . cyan ( 'react-dom' ) +
226
+ ', and ' + chalk . cyan ( packageName ) + '...'
227
+ ) ;
228
+ console . log ( ) ;
229
+
230
+ return install ( useYarn , allDependencies , verbose , isOnline ) . then ( function ( ) {
231
+ return packageName ;
232
+ } ) ;
219
233
} )
220
- . then ( function ( ) {
234
+ . then ( function ( packageName ) {
221
235
checkNodeVersion ( packageName ) ;
222
236
223
237
// Since react-scripts has been installed with --save
@@ -285,22 +299,77 @@ function getInstallPackage(version) {
285
299
return packageToInstall ;
286
300
}
287
301
302
+ function getTemporaryDirectory ( ) {
303
+ return new Promise ( function ( resolve , reject ) {
304
+ // Unsafe cleanup lets us recursively delete the directory if it contains
305
+ // contents; by default it only allows removal if it's empty
306
+ tmp . dir ( { unsafeCleanup : true } , function ( err , tmpdir , callback ) {
307
+ if ( err ) {
308
+ reject ( err ) ;
309
+ } else {
310
+ resolve ( {
311
+ tmpdir : tmpdir ,
312
+ cleanup : function ( ) {
313
+ try {
314
+ callback ( ) ;
315
+ } catch ( ignored ) {
316
+ // Callback might throw and fail, since it's a temp directory the
317
+ // OS will clean it up eventually...
318
+ }
319
+ }
320
+ } ) ;
321
+ }
322
+ } ) ;
323
+ } ) ;
324
+ }
325
+
326
+ function extractStream ( stream , dest ) {
327
+ return new Promise ( function ( resolve , reject ) {
328
+ stream . pipe ( unpack ( dest , function ( err ) {
329
+ if ( err ) {
330
+ reject ( err ) ;
331
+ } else {
332
+ resolve ( dest ) ;
333
+ }
334
+ } ) ) ;
335
+ } ) ;
336
+ }
337
+
288
338
// Extract package name from tarball url or path.
289
339
function getPackageName ( installPackage ) {
290
340
if ( installPackage . indexOf ( '.tgz' ) > - 1 ) {
291
- // The package name could be with or without semver version, e.g. react-cy-scripts-0.2.0-alpha.1.tgz
292
- // However, this function returns package name only without semver version.
293
- return installPackage . match ( / ^ .+ \/ ( .+ ?) (?: - \d + .+ ) ? \. t g z $ / ) [ 1 ] ;
341
+ return getTemporaryDirectory ( ) . then ( function ( obj ) {
342
+ var stream ;
343
+ if ( / ^ h t t p / . test ( installPackage ) ) {
344
+ stream = hyperquest ( installPackage ) ;
345
+ } else {
346
+ stream = fs . createReadStream ( installPackage ) ;
347
+ }
348
+ return extractStream ( stream , obj . tmpdir ) . then ( function ( ) {
349
+ return obj ;
350
+ } ) ;
351
+ } ) . then ( function ( obj ) {
352
+ var packageName = require ( path . join ( obj . tmpdir , 'package.json' ) ) . name ;
353
+ obj . cleanup ( ) ;
354
+ return packageName ;
355
+ } ) . catch ( function ( err ) {
356
+ // The package name could be with or without semver version, e.g. react-scripts-0.2.0-alpha.1.tgz
357
+ // However, this function returns package name only without semver version.
358
+ console . log ( 'Could not extract the package name from the archive: ' + err . message ) ;
359
+ var assumedProjectName = installPackage . match ( / ^ .+ \/ ( .+ ?) (?: - \d + .+ ) ? \. t g z $ / ) [ 1 ] ;
360
+ console . log ( 'Based on the filename, assuming it is "' + chalk . cyan ( assumedProjectName ) + '"' ) ;
361
+ return Promise . resolve ( assumedProjectName ) ;
362
+ } ) ;
294
363
} else if ( installPackage . indexOf ( 'git+' ) === 0 ) {
295
364
// Pull package name out of git urls e.g:
296
365
// git+https://github.com/mycompany/react-scripts.git
297
366
// git+ssh://github.com/mycompany/react-scripts.git#v1.2.3
298
- return installPackage . match ( / ( [ ^ \/ ] + ) \. g i t ( # .* ) ? $ / ) [ 1 ] ;
367
+ return Promise . resolve ( installPackage . match ( / ( [ ^ \/ ] + ) \. g i t ( # .* ) ? $ / ) [ 1 ] ) ;
299
368
} else if ( installPackage . indexOf ( '@' ) > 0 ) {
300
369
// Do not match @scope / when stripping off @version or @tag
301
- return installPackage . charAt ( 0 ) + installPackage . substr ( 1 ) . split ( '@' ) [ 0 ] ;
370
+ return Promise . resolve ( installPackage . charAt ( 0 ) + installPackage . substr ( 1 ) . split ( '@' ) [ 0 ] ) ;
302
371
}
303
- return installPackage ;
372
+ return Promise . resolve ( installPackage ) ;
304
373
}
305
374
306
375
function checkNpmVersion ( ) {
@@ -356,7 +425,7 @@ function checkAppName(appName) {
356
425
printValidationResults ( validationResult . warnings ) ;
357
426
process . exit ( 1 ) ;
358
427
}
359
-
428
+
360
429
// TODO: there should be a single place that holds the dependencies
361
430
// Currently we have to manually include eslint as a dep because otherwise text editors don't pick it up.
362
431
var dependencies = [ 'react' , 'react-dom' , 'eslint' , 'eslint-config-codeyellow' , 'mobx' ] ;
@@ -450,7 +519,7 @@ function checkIfOnline(useYarn) {
450
519
// We'll just assume the best case.
451
520
return Promise . resolve ( true ) ;
452
521
}
453
-
522
+
454
523
return new Promise ( function ( resolve ) {
455
524
dns . resolve ( 'registry.yarnpkg.com' , function ( err ) {
456
525
resolve ( err === null ) ;
0 commit comments