mirror of
https://github.com/zhigang1992/create-react-app.git
synced 2026-04-28 09:25:42 +08:00
Properly extract package name for installing tgz of scoped packages (#1706)
* Properly extract package name * Download package if need be ... * Oops * Add e2e test based on #1537, but without specific filename * Pass packageName through promises A little bit more verbose but explicit and doesn't rely on shared mutable state. * Fix up directory name in test * Tweak failure message * Fix lint
This commit is contained in:
@@ -60,6 +60,9 @@ var execSync = require('child_process').execSync;
|
||||
var spawn = require('cross-spawn');
|
||||
var semver = require('semver');
|
||||
var dns = require('dns');
|
||||
var tmp = require('tmp');
|
||||
var unpack = require('tar-pack').unpack;
|
||||
var hyperquest = require('hyperquest');
|
||||
|
||||
var projectName;
|
||||
|
||||
@@ -201,23 +204,34 @@ function install(useYarn, dependencies, verbose, isOnline) {
|
||||
|
||||
function run(root, appName, version, verbose, originalDirectory, template) {
|
||||
var packageToInstall = getInstallPackage(version);
|
||||
var packageName = getPackageName(packageToInstall);
|
||||
|
||||
var allDependencies = ['react', 'react-dom', packageToInstall];
|
||||
|
||||
console.log('Installing packages. This might take a couple minutes.');
|
||||
console.log(
|
||||
'Installing ' + chalk.cyan('react') + ', ' + chalk.cyan('react-dom') +
|
||||
', and ' + chalk.cyan(packageName) + '...'
|
||||
);
|
||||
console.log();
|
||||
|
||||
|
||||
var useYarn = shouldUseYarn();
|
||||
checkIfOnline(useYarn)
|
||||
.then(function(isOnline) {
|
||||
return install(useYarn, allDependencies, verbose, isOnline);
|
||||
getPackageName(packageToInstall)
|
||||
.then(function(packageName) {
|
||||
return checkIfOnline(useYarn).then(function(isOnline) {
|
||||
return {
|
||||
isOnline: isOnline,
|
||||
packageName: packageName,
|
||||
};
|
||||
});
|
||||
})
|
||||
.then(function() {
|
||||
.then(function(info) {
|
||||
var isOnline = info.isOnline;
|
||||
var packageName = info.packageName;
|
||||
console.log(
|
||||
'Installing ' + chalk.cyan('react') + ', ' + chalk.cyan('react-dom') +
|
||||
', and ' + chalk.cyan(packageName) + '...'
|
||||
);
|
||||
console.log();
|
||||
|
||||
return install(useYarn, allDependencies, verbose, isOnline).then(function() {
|
||||
return packageName;
|
||||
});
|
||||
})
|
||||
.then(function(packageName) {
|
||||
checkNodeVersion(packageName);
|
||||
|
||||
// Since react-scripts has been installed with --save
|
||||
@@ -285,22 +299,77 @@ function getInstallPackage(version) {
|
||||
return packageToInstall;
|
||||
}
|
||||
|
||||
function getTemporaryDirectory() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
// Unsafe cleanup lets us recursively delete the directory if it contains
|
||||
// contents; by default it only allows removal if it's empty
|
||||
tmp.dir({ unsafeCleanup: true }, function(err, tmpdir, callback) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve({
|
||||
tmpdir: tmpdir,
|
||||
cleanup: function() {
|
||||
try {
|
||||
callback();
|
||||
} catch (ignored) {
|
||||
// Callback might throw and fail, since it's a temp directory the
|
||||
// OS will clean it up eventually...
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function extractStream(stream, dest) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
stream.pipe(unpack(dest, function(err) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(dest);
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
// Extract package name from tarball url or path.
|
||||
function getPackageName(installPackage) {
|
||||
if (installPackage.indexOf('.tgz') > -1) {
|
||||
// The package name could be with or without semver version, e.g. react-scripts-0.2.0-alpha.1.tgz
|
||||
// However, this function returns package name only without semver version.
|
||||
return installPackage.match(/^.+\/(.+?)(?:-\d+.+)?\.tgz$/)[1];
|
||||
return getTemporaryDirectory().then(function(obj) {
|
||||
var stream;
|
||||
if (/^http/.test(installPackage)) {
|
||||
stream = hyperquest(installPackage);
|
||||
} else {
|
||||
stream = fs.createReadStream(installPackage);
|
||||
}
|
||||
return extractStream(stream, obj.tmpdir).then(function() {
|
||||
return obj;
|
||||
});
|
||||
}).then(function(obj) {
|
||||
var packageName = require(path.join(obj.tmpdir, 'package.json')).name;
|
||||
obj.cleanup();
|
||||
return packageName;
|
||||
}).catch(function(err) {
|
||||
// The package name could be with or without semver version, e.g. react-scripts-0.2.0-alpha.1.tgz
|
||||
// However, this function returns package name only without semver version.
|
||||
console.log('Could not extract the package name from the archive: ' + err.message);
|
||||
var assumedProjectName = installPackage.match(/^.+\/(.+?)(?:-\d+.+)?\.tgz$/)[1];
|
||||
console.log('Based on the filename, assuming it is "' + chalk.cyan(assumedProjectName) + '"');
|
||||
return Promise.resolve(assumedProjectName);
|
||||
});
|
||||
} else if (installPackage.indexOf('git+') === 0) {
|
||||
// Pull package name out of git urls e.g:
|
||||
// git+https://github.com/mycompany/react-scripts.git
|
||||
// git+ssh://github.com/mycompany/react-scripts.git#v1.2.3
|
||||
return installPackage.match(/([^\/]+)\.git(#.*)?$/)[1];
|
||||
return Promise.resolve(installPackage.match(/([^\/]+)\.git(#.*)?$/)[1]);
|
||||
} else if (installPackage.indexOf('@') > 0) {
|
||||
// Do not match @scope/ when stripping off @version or @tag
|
||||
return installPackage.charAt(0) + installPackage.substr(1).split('@')[0];
|
||||
return Promise.resolve(installPackage.charAt(0) + installPackage.substr(1).split('@')[0]);
|
||||
}
|
||||
return installPackage;
|
||||
return Promise.resolve(installPackage);
|
||||
}
|
||||
|
||||
function checkNpmVersion() {
|
||||
@@ -356,7 +425,7 @@ function checkAppName(appName) {
|
||||
printValidationResults(validationResult.warnings);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
||||
// TODO: there should be a single place that holds the dependencies
|
||||
var dependencies = ['react', 'react-dom'];
|
||||
var devDependencies = ['react-scripts'];
|
||||
@@ -449,7 +518,7 @@ function checkIfOnline(useYarn) {
|
||||
// We'll just assume the best case.
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
|
||||
return new Promise(function(resolve) {
|
||||
dns.resolve('registry.yarnpkg.com', function(err) {
|
||||
resolve(err === null);
|
||||
|
||||
@@ -24,7 +24,10 @@
|
||||
"commander": "^2.9.0",
|
||||
"cross-spawn": "^4.0.0",
|
||||
"fs-extra": "^1.0.0",
|
||||
"hyperquest": "^2.1.2",
|
||||
"semver": "^5.0.3",
|
||||
"tar-pack": "^3.4.0",
|
||||
"tmp": "0.0.31",
|
||||
"validate-npm-package-name": "^3.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,6 +145,18 @@ if [ "$(ls -1 ./test-app-should-remain | wc -l | tr -d '[:space:]')" != "1" ]; t
|
||||
false
|
||||
fi
|
||||
|
||||
# ******************************************************************************
|
||||
# Test --scripts-version with a scoped fork tgz of react-scripts
|
||||
# ******************************************************************************
|
||||
|
||||
cd $temp_app_path
|
||||
curl "https://registry.npmjs.org/@enoah_netzach/react-scripts/-/react-scripts-0.9.0.tgz" -o enoah-scripts-0.9.0.tgz
|
||||
create_react_app --scripts-version=$temp_app_path/enoah-scripts-0.9.0.tgz test-app-scoped-fork-tgz
|
||||
cd test-app-scoped-fork-tgz
|
||||
|
||||
# Check corresponding scripts version is installed.
|
||||
exists node_modules/@enoah_netzach/react-scripts
|
||||
|
||||
# ******************************************************************************
|
||||
# Test nested folder path as the project name
|
||||
# ******************************************************************************
|
||||
|
||||
Reference in New Issue
Block a user