@@ -60,6 +60,9 @@ var execSync = require('child_process').execSync;
6060var spawn = require ( 'cross-spawn' ) ;
6161var semver = require ( 'semver' ) ;
6262var dns = require ( 'dns' ) ;
63+ var tmp = require ( 'tmp' ) ;
64+ var unpack = require ( 'tar-pack' ) . unpack ;
65+ var hyperquest = require ( 'hyperquest' ) ;
6366
6467var projectName ;
6568
@@ -201,23 +204,34 @@ function install(useYarn, dependencies, verbose, isOnline) {
201204
202205function run ( root , appName , version , verbose , originalDirectory , template ) {
203206 var packageToInstall = getInstallPackage ( version ) ;
204- var packageName = getPackageName ( packageToInstall ) ;
205-
206207 var allDependencies = [ 'react' , 'react-dom' , packageToInstall ] ;
207208
208209 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+
215211 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+ } ) ;
219233 } )
220- . then ( function ( ) {
234+ . then ( function ( packageName ) {
221235 checkNodeVersion ( packageName ) ;
222236
223237 // Since react-scripts has been installed with --save
@@ -285,22 +299,77 @@ function getInstallPackage(version) {
285299 return packageToInstall ;
286300}
287301
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+
288338// Extract package name from tarball url or path.
289339function getPackageName ( installPackage ) {
290340 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+ } ) ;
294363 } else if ( installPackage . indexOf ( 'git+' ) === 0 ) {
295364 // Pull package name out of git urls e.g:
296365 // git+https://github.com/mycompany/react-scripts.git
297366 // 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 ] ) ;
299368 } else if ( installPackage . indexOf ( '@' ) > 0 ) {
300369 // 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 ] ) ;
302371 }
303- return installPackage ;
372+ return Promise . resolve ( installPackage ) ;
304373}
305374
306375function checkNpmVersion ( ) {
@@ -356,7 +425,7 @@ function checkAppName(appName) {
356425 printValidationResults ( validationResult . warnings ) ;
357426 process . exit ( 1 ) ;
358427 }
359-
428+
360429 // TODO: there should be a single place that holds the dependencies
361430 // Currently we have to manually include eslint as a dep because otherwise text editors don't pick it up.
362431 var dependencies = [ 'react' , 'react-dom' , 'eslint' , 'eslint-config-codeyellow' , 'mobx' ] ;
@@ -450,7 +519,7 @@ function checkIfOnline(useYarn) {
450519 // We'll just assume the best case.
451520 return Promise . resolve ( true ) ;
452521 }
453-
522+
454523 return new Promise ( function ( resolve ) {
455524 dns . resolve ( 'registry.yarnpkg.com' , function ( err ) {
456525 resolve ( err === null ) ;
0 commit comments