diff --git a/__tests__/package-resolver.js b/__tests__/package-resolver.js index 601f491592..b6c50f8cd5 100644 --- a/__tests__/package-resolver.js +++ b/__tests__/package-resolver.js @@ -62,6 +62,8 @@ addTest('https://github.com/yarnpkg/yarn/releases/download/v0.18.1/yarn-v0.18.1. addTest('https://github.com/yarnpkg/e2e-test-repo.git#greenkeeper/cross-env-3.1.4'); // hash with slashes addTest('https://github.com/bestander/chrome-app-livereload.git'); // no package.json addTest('livereload@https://github.com/bestander/chrome-app-livereload.git'); // no package.json, named +addTest('bestander/chrome-app-livereload'); // no package.json, github +addTest('livereload@https://github.com/bestander/chrome-app-livereload/archive/0.0.5.tar.gz'); // no package.json, targz addTest('gitlab:leanlabsio/kanban'); // gitlab addTest('gist:d59975ac23e26ad4e25b'); // gist url addTest('bitbucket:hgarcia/node-bitbucket-api'); // bitbucket url diff --git a/__tests__/util/guess-name.js b/__tests__/util/guess-name.js new file mode 100644 index 0000000000..a9115480a1 --- /dev/null +++ b/__tests__/util/guess-name.js @@ -0,0 +1,33 @@ +/* @flow */ + +import guessName from '../../src/util/guess-name'; + +const examples = [ + 'http://github.com/foo-bar/awesome-name', + 'http://github.com/foo-bar/awesome-name.git', + 'http://awesome.com/awesome-name.git', + 'http://awesome.com/awesome-name.git?foo/bar#fiz/fuz', + 'https://github.com/hashicorp/awesome-name/archive/v0.5.5.tar.gz', + 'https://gitlab.com/foo/awesome-name/repository/archive.tar.gz?ref=3.11.0', + 'https://gitlab.com/foo/awesome-name/repository/archive.tar.bz2?ref=3.11.0', + 'https://gitlab.com/foo/awesome-name/repository/archive.tar?ref=3.11.0', + 'https://gitlab.com/foo/awesome-name/repository/archive.zip?ref=3.11.0', + 'git@gitlab.com:yolo/awesome-name.git', + 'https://gitlab.com/yolo/awesome-name.git', + 'https://asesome.com/yolo/awesome-name-0.2.3.tar.gz', + '/foo/bar/awesome-name', + './foo/bar/awesome-name', + '../foo/bar/awesome-name', + 'file:../foo/bar/awesome-name', + 'file:../foo/bar/awesome-name.tar.gz', + 'awesome-name', + 'awesome-name.tar.gz', +]; + +describe('guessName', () => { + for (const source of examples) { + it(`guess name of ${source}`, () => { + expect(guessName(source)).toBe('awesome-name'); + }); + } +}); diff --git a/src/resolvers/exotics/git-resolver.js b/src/resolvers/exotics/git-resolver.js index 8eb5ebebb7..4ad0dfeb2b 100644 --- a/src/resolvers/exotics/git-resolver.js +++ b/src/resolvers/exotics/git-resolver.js @@ -5,6 +5,7 @@ import type PackageRequest from '../../package-request.js'; import {hostedGit as hostedGitResolvers} from '../index.js'; import * as util from '../../util/misc.js'; import * as versionUtil from '../../util/version.js'; +import guessName from '../../util/guess-name.js'; import {registries} from '../../registries/index.js'; import ExoticResolver from './exotic-resolver.js'; import Git from '../../util/git.js'; @@ -132,16 +133,9 @@ export default class GitResolver extends ExoticResolver { } } - let name = ''; - - if (parts.path) { - const names = parts.path.split('/'); - name = names[names.length - 1].split('.')[0]; - } - return { // This is just the default, it can be overridden with key of dependencies - name, + name: guessName(url), version: '0.0.0', _uid: commit, _remote: { diff --git a/src/resolvers/exotics/hosted-git-resolver.js b/src/resolvers/exotics/hosted-git-resolver.js index d159e0fe48..59ebf7d1b1 100644 --- a/src/resolvers/exotics/hosted-git-resolver.js +++ b/src/resolvers/exotics/hosted-git-resolver.js @@ -8,6 +8,7 @@ import {registries} from '../../registries/index.js'; import GitResolver from './git-resolver.js'; import ExoticResolver from './exotic-resolver.js'; import Git from '../../util/git.js'; +import guessName from '../../util/guess-name.js'; export type ExplodedFragment = { user: string, @@ -183,7 +184,18 @@ export default class HostedGitResolver extends ExoticResolver { } } - throw new MessageError(this.reporter.lang('couldntFindManifestIn', url)); + return { + name: guessName(url), + version: '0.0.0', + _uid: commit, + _remote: { + hash: commit, + resolved: url, + type: 'tarball', + reference: url, + registry: 'npm', + }, + }; } async hasHTTPCapability(url: string): Promise { diff --git a/src/resolvers/exotics/tarball-resolver.js b/src/resolvers/exotics/tarball-resolver.js index 61754bf465..b3eb45adeb 100644 --- a/src/resolvers/exotics/tarball-resolver.js +++ b/src/resolvers/exotics/tarball-resolver.js @@ -6,6 +6,7 @@ import TarballFetcher from '../../fetchers/tarball-fetcher.js'; import ExoticResolver from './exotic-resolver.js'; import Git from './git-resolver.js'; import {removePrefix} from '../../util/misc.js'; +import guessName from '../../util/guess-name.js'; import * as versionUtil from '../../util/version.js'; import * as crypto from '../../util/crypto.js'; import * as fs from '../../util/fs.js'; @@ -77,7 +78,11 @@ export default class TarballResolver extends ExoticResolver { ); // fetch file and get it's hash - const fetched: FetchedMetadata = await fetcher.fetch(); + const fetched: FetchedMetadata = await fetcher.fetch({ + name: guessName(url), + version: '0.0.0', + _registry: 'npm', + }); pkgJson = fetched.package; hash = fetched.hash; diff --git a/src/util/guess-name.js b/src/util/guess-name.js new file mode 100644 index 0000000000..a922718412 --- /dev/null +++ b/src/util/guess-name.js @@ -0,0 +1,52 @@ +/* @flow */ + +import url from 'url'; + +function cleanup(name: string): string { + name = name.replace(/-\d+\.\d+\.\d+/, ''); + return name.split('.')[0]; +} + +function guessNameFallback(source: string): string { + // If cannot parse as url, just return cleaned up last part + const parts = source.split('/'); + return cleanup(parts[parts.length - 1]); +} + +export default function guessName(source: string): string { + try { + const parsed = url.parse(source); + + if (!parsed.pathname) { + return guessNameFallback(source); + } + + const parts = parsed.pathname.split('/'); + + // Priority goes to part that ends with .git + for (const part of parts) { + if (part.match(/\.git$/)) { + return cleanup(part); + } + } + + // Most likely a directory + if (parsed.host == null) { + return cleanup(parts[parts.length - 1]); + } + + // A site like github or gitlab + if (parts.length > 2) { + return cleanup(parts[2]); + } + + // Privately hosted package? + if (parts.length > 1) { + return cleanup(parts[1]); + } + + return guessNameFallback(source); + } catch (e) { + return guessNameFallback(source); + } +}