From 6c75e22a128a68abca0b1e905c59a5b830bb2da1 Mon Sep 17 00:00:00 2001 From: Doug Parker Date: Wed, 13 Nov 2019 12:36:32 -0800 Subject: [PATCH] fix(@angular-devkit/schematics): filters out non-NPM dependencies Fixes #13059. Non-NPM dependencies in package.json are now filtered out of `ng update`. Dependencies which are linked as tarball or Git references will now be skipped during the update process. --- packages/schematics/update/update/index.ts | 40 ++++++++++++++++++---- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/packages/schematics/update/update/index.ts b/packages/schematics/update/update/index.ts index c177de50cc92..02001fb035ce 100644 --- a/packages/schematics/update/update/index.ts +++ b/packages/schematics/update/update/index.ts @@ -826,6 +826,29 @@ function _formatVersion(version: string | undefined) { return version; } +/** + * @param depString Dependency from `package.json`. Could be a semver, local tarball reference, + * link to a GitHub repo, etc. + * @return Whether or not the given string is a simple semantic version, and thus represents a + * package hosted on NPM, either as a public or private package. + */ +function isSimpleSemVer(depString: string): boolean { + // Any tarball is clearly not a simple semantic version. We need to manually exclude this + // case because semver.coerce() has special logic to extract the version number from the name + // of a tarball generated with `npm pack`. By NPM specification, the name *must* end in ".tar", + // ".tar.gz", or ".tgz", so we can simply exclude these extensions. + // See: https://docs.npmjs.com/cli/install. + if (depString.endsWith('.tar') || depString.endsWith('.tar.gz') || depString.endsWith('.tgz')) { + return false; + } + + const version = semver.coerce(depString); + if (!version) { + return false; + } + + return !!semver.valid(version); +} export default function(options: UpdateSchema): Rule { if (!options.packages) { @@ -850,11 +873,16 @@ export default function(options: UpdateSchema): Rule { return (tree: Tree, context: SchematicContext) => { const logger = context.logger; - const allDependencies = _getAllDependencies(tree); - const packages = _buildPackageList(options, allDependencies, logger); + const allDeps = _getAllDependencies(tree); + + // Filter out non-NPM dependencies by checking if they link to a semver. + const npmDeps = new Map(Array.from(allDeps.entries()) + .filter(([_, source]) => isSimpleSemVer(source))); + + const packages = _buildPackageList(options, npmDeps, logger); const usingYarn = options.packageManager === 'yarn'; - return observableFrom([...allDependencies.keys()]).pipe( + return observableFrom([...npmDeps.keys()]).pipe( // Grab all package.json from the npm repository. This requires a lot of HTTP calls so we // try to parallelize as many as possible. mergeMap(depName => getNpmPackageJson( @@ -899,8 +927,8 @@ export default function(options: UpdateSchema): Rule { do { lastPackagesSize = packages.size; npmPackageJsonMap.forEach((npmPackageJson) => { - _addPackageGroup(tree, packages, allDependencies, npmPackageJson, logger); - _addPeerDependencies(tree, packages, allDependencies, npmPackageJson, npmPackageJsonMap, logger); + _addPackageGroup(tree, packages, npmDeps, npmPackageJson, logger); + _addPeerDependencies(tree, packages, npmDeps, npmPackageJson, npmPackageJsonMap, logger); }); } while (packages.size > lastPackagesSize); @@ -909,7 +937,7 @@ export default function(options: UpdateSchema): Rule { npmPackageJsonMap.forEach((npmPackageJson) => { packageInfoMap.set( npmPackageJson.name, - _buildPackageInfo(tree, packages, allDependencies, npmPackageJson, logger), + _buildPackageInfo(tree, packages, npmDeps, npmPackageJson, logger), ); });