Skip to content

Commit 8918da1

Browse files
Volunebestander
authored andcommitted
Allow git+***:// dependencies (#3735)
* Move getExoticResolver function to resolvers/index.js * Add tests for getExoticResolver * Use Git Resolver for any `git+***://` dependencies Fix #3677
1 parent df26e88 commit 8918da1

File tree

10 files changed

+135
-42
lines changed

10 files changed

+135
-42
lines changed

__tests__/resolvers/index.js

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/* @flow */
2+
3+
import {getExoticResolver} from '../../src/resolvers/index.js';
4+
// exotic resolvers
5+
import GitResolver from '../../src/resolvers/exotics/git-resolver.js';
6+
import GistResolver from '../../src/resolvers/exotics/gist-resolver.js';
7+
import RegistryResolver from '../../src/resolvers/exotics/registry-resolver.js';
8+
import TarballResolver from '../../src/resolvers/exotics/tarball-resolver.js';
9+
// registry resolvers
10+
import NpmRegistryResolver from '../../src/resolvers/registries/npm-resolver.js';
11+
import YarnRegistryResolver from '../../src/resolvers/registries/yarn-resolver.js';
12+
13+
test('getExoticResolver does not return a Resolver for 0.0.0 pattern', () => {
14+
const pattern = '0.0.0';
15+
expect(getExoticResolver(pattern)).toEqual(null);
16+
});
17+
18+
test('getExoticResolver returns NPM Registry Resolver for npm:0.0.0 pattern', () => {
19+
const pattern = 'npm:0.0.0';
20+
const resolver = getExoticResolver(pattern);
21+
expect(resolver).toBeDefined();
22+
expect(resolver && resolver.prototype).toBeInstanceOf(RegistryResolver);
23+
expect(resolver && resolver.factory).toEqual(NpmRegistryResolver);
24+
});
25+
26+
test('getExoticResolver returns Yarn Registry Resolver for yarn:0.0.0 pattern', () => {
27+
const pattern = 'yarn:0.0.0';
28+
const resolver = getExoticResolver(pattern);
29+
expect(resolver).toBeDefined();
30+
expect(resolver && resolver.prototype).toBeInstanceOf(RegistryResolver);
31+
expect(resolver && resolver.factory).toEqual(YarnRegistryResolver);
32+
});
33+
34+
test('getExoticResolver returns Git Resolver for git+ssh://... pattern', () => {
35+
const pattern = 'git+ssh://user@hostname/path/repo#master';
36+
expect(getExoticResolver(pattern)).toEqual(GitResolver);
37+
});
38+
39+
test('getExoticResolver returns Git Resolver for git+https://... pattern', () => {
40+
const pattern = 'git+https://hostname/path/repo#master';
41+
expect(getExoticResolver(pattern)).toEqual(GitResolver);
42+
});
43+
44+
test('getExoticResolver returns Git Resolver for git+file:///... pattern', () => {
45+
const pattern = 'git+file:///path/repo#master';
46+
expect(getExoticResolver(pattern)).toEqual(GitResolver);
47+
});
48+
49+
test('getExoticResolver returns Git Resolver for git://... pattern', () => {
50+
const pattern = 'git://user@hostname/path/repo#master';
51+
expect(getExoticResolver(pattern)).toEqual(GitResolver);
52+
});
53+
54+
test('getExoticResolver returns Git Resolver for ssh://... pattern', () => {
55+
const pattern = 'ssh://user@hostname/path/repo#master';
56+
expect(getExoticResolver(pattern)).toEqual(GitResolver);
57+
});
58+
59+
test('getExoticResolver returns Git Resolver for http://... pattern with .git extension', () => {
60+
const pattern = 'http://hostname/path/repo.git#master';
61+
expect(getExoticResolver(pattern)).toEqual(GitResolver);
62+
});
63+
64+
test('getExoticResolver returns Git Resolver for https://... pattern with .git extension', () => {
65+
const pattern = 'https://hostname/path/repo.git#master';
66+
expect(getExoticResolver(pattern)).toEqual(GitResolver);
67+
});
68+
69+
test('getExoticResolver returns Tarball Resolver for http://... pattern', () => {
70+
const pattern = 'http://hostname/path/file.tgz';
71+
expect(getExoticResolver(pattern)).toEqual(TarballResolver);
72+
});
73+
74+
test('getExoticResolver returns Tarball Resolver for https://... pattern', () => {
75+
const pattern = 'https://hostname/path/file.tgz';
76+
expect(getExoticResolver(pattern)).toEqual(TarballResolver);
77+
});
78+
79+
test('getExoticResolver returns Gist Resolver for gist://... pattern', () => {
80+
const pattern = 'gist:id#hash';
81+
expect(getExoticResolver(pattern)).toEqual(GistResolver);
82+
});

src/cli/commands/add.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type Config from '../../config.js';
77
import type {ListOptions} from './list.js';
88
import Lockfile from '../../lockfile/wrapper.js';
99
import PackageRequest from '../../package-request.js';
10+
import {getExoticResolver} from '../../resolvers/index.js';
1011
import {buildTree} from './list.js';
1112
import {wrapLifecycle, Install} from './install.js';
1213
import {MessageError} from '../../errors.js';
@@ -56,7 +57,7 @@ export class Add extends Install {
5657
const {exact, tilde} = this.flags;
5758
const parts = PackageRequest.normalizePattern(pattern);
5859
let version;
59-
if (PackageRequest.getExoticResolver(pattern)) {
60+
if (getExoticResolver(pattern)) {
6061
// wasn't a name/range tuple so this is just a raw exotic pattern
6162
version = pattern;
6263
} else if (parts.hasVersion && parts.range) {

src/cli/commands/import.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type Config from '../../config.js';
77
import {Install} from './install.js';
88
import {verifyTreeCheck} from './check.js';
99
import {MessageError} from '../../errors.js';
10+
import {getExoticResolver} from '../../resolvers/index.js';
1011
import BaseResolver from '../../resolvers/base-resolver.js';
1112
import HostedGitResolver, {explodeHostedGitFragment} from '../../resolvers/exotics/hosted-git-resolver.js';
1213
import GistResolver, {explodeGistFragment} from '../../resolvers/exotics/gist-resolver.js';
@@ -40,7 +41,7 @@ class ImportResolver extends BaseResolver {
4041
return this.config.cwd;
4142
}
4243

43-
resolveHostedGit(info: Manifest, Resolver: typeof HostedGitResolver): Manifest {
44+
resolveHostedGit(info: Manifest, Resolver: Class<HostedGitResolver>): Manifest {
4445
const {range} = PackageRequest.normalizePattern(this.pattern);
4546
const exploded = explodeHostedGitFragment(range, this.reporter);
4647
const hash = (info: any).gitHead;
@@ -127,7 +128,7 @@ class ImportResolver extends BaseResolver {
127128

128129
resolveImport(info: Manifest): Manifest {
129130
const {range} = PackageRequest.normalizePattern(this.pattern);
130-
const Resolver = PackageRequest.getExoticResolver(range);
131+
const Resolver = getExoticResolver(range);
131132
if (Resolver && Resolver.prototype instanceof HostedGitResolver) {
132133
return this.resolveHostedGit(info, Resolver);
133134
} else if (Resolver && Resolver === GistResolver) {

src/cli/commands/install.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import PackageResolver from '../../package-resolver.js';
1818
import PackageLinker from '../../package-linker.js';
1919
import PackageRequest from '../../package-request.js';
2020
import {registries} from '../../registries/index.js';
21+
import {getExoticResolver} from '../../resolvers/index.js';
2122
import {clean} from './clean.js';
2223
import * as constants from '../../constants.js';
2324
import * as fs from '../../util/fs.js';
@@ -209,7 +210,7 @@ export class Install {
209210
const excludeNames = [];
210211
for (const pattern of excludePatterns) {
211212
// can't extract a package name from this
212-
if (PackageRequest.getExoticResolver(pattern)) {
213+
if (getExoticResolver(pattern)) {
213214
continue;
214215
}
215216

src/cli/commands/upgrade.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type {Reporter} from '../../reporters/index.js';
44
import type Config from '../../config.js';
55
import {Add} from './add.js';
66
import Lockfile from '../../lockfile/wrapper.js';
7-
import PackageRequest from '../../package-request.js';
7+
import {getExoticResolver} from '../../resolvers/index.js';
88
import {MessageError} from '../../errors.js';
99

1010
export function setFlags(commander: Object) {
@@ -69,7 +69,7 @@ export async function run(config: Config, reporter: Reporter, flags: Object, arg
6969
function getDependency(allDependencies, dependency): string {
7070
const remoteSource = allDependencies[dependency];
7171

72-
if (remoteSource && PackageRequest.getExoticResolver(remoteSource)) {
72+
if (remoteSource && getExoticResolver(remoteSource)) {
7373
return remoteSource;
7474
}
7575

src/package-request.js

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,10 @@ import Lockfile from './lockfile/wrapper.js';
1010
import PackageReference from './package-reference.js';
1111
import {registries as registryResolvers} from './resolvers/index.js';
1212
import {MessageError} from './errors.js';
13-
import {entries} from './util/misc.js';
1413
import * as constants from './constants.js';
1514
import * as versionUtil from './util/version.js';
1615
import WorkspaceResolver from './resolvers/contextual/workspace-resolver.js';
17-
import * as resolvers from './resolvers/index.js';
16+
import {getExoticResolver} from './resolvers/index.js';
1817
import * as fs from './util/fs.js';
1918

2019
const path = require('path');
@@ -38,16 +37,6 @@ export default class PackageRequest {
3837
resolver.usedRegistries.add(req.registry);
3938
}
4039

41-
static getExoticResolver(pattern: string): ?Function {
42-
// TODO make this type more refined
43-
for (const [, Resolver] of entries(resolvers.exotics)) {
44-
if (Resolver.isVersion(pattern)) {
45-
return Resolver;
46-
}
47-
}
48-
return null;
49-
}
50-
5140
parentRequest: ?PackageRequest;
5241
lockfile: Lockfile;
5342
reporter: Reporter;
@@ -108,8 +97,7 @@ export default class PackageRequest {
10897
async findVersionOnRegistry(pattern: string): Promise<Manifest> {
10998
const {range, name} = await this.normalize(pattern);
11099

111-
const exoticResolver = PackageRequest.getExoticResolver(range);
112-
100+
const exoticResolver = getExoticResolver(range);
113101
if (exoticResolver) {
114102
let data = await this.findExoticVersionInfo(exoticResolver, range);
115103

@@ -145,7 +133,7 @@ export default class PackageRequest {
145133
}
146134

147135
async normalizeRange(pattern: string): Promise<string> {
148-
if (pattern.includes(':') || pattern.includes('@') || PackageRequest.getExoticResolver(pattern)) {
136+
if (pattern.includes(':') || pattern.includes('@') || getExoticResolver(pattern)) {
149137
return Promise.resolve(pattern);
150138
}
151139

@@ -220,8 +208,7 @@ export default class PackageRequest {
220208
*/
221209

222210
findVersionInfo(): Promise<Manifest> {
223-
const exoticResolver = PackageRequest.getExoticResolver(this.pattern);
224-
211+
const exoticResolver = getExoticResolver(this.pattern);
225212
if (exoticResolver) {
226213
return this.findExoticVersionInfo(exoticResolver, this.pattern);
227214
} else if (WorkspaceResolver.isWorkspace(this.pattern, this.resolver.workspaceLayout)) {
@@ -409,7 +396,7 @@ export default class PackageRequest {
409396
410397
const normalized = PackageRequest.normalizePattern(pattern);
411398
412-
if (PackageRequest.getExoticResolver(pattern) || PackageRequest.getExoticResolver(normalized.range)) {
399+
if (getExoticResolver(pattern) || getExoticResolver(normalized.range)) {
413400
latest = wanted = 'exotic';
414401
url = normalized.range;
415402
} else {

src/package-resolver.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type {Manifest, DependencyRequestPatterns, DependencyRequestPattern} from
44
import type {RegistryNames} from './registries/index.js';
55
import type PackageReference from './package-reference.js';
66
import type {Reporter} from './reporters/index.js';
7+
import {getExoticResolver} from './resolvers/index.js';
78
import type Config from './config.js';
89
import PackageRequest from './package-request.js';
910
import RequestManager from './util/request-manager.js';
@@ -443,7 +444,7 @@ export default class PackageResolver {
443444
semver.validRange(range) &&
444445
semver.valid(lockfileEntry.version) &&
445446
!semver.satisfies(lockfileEntry.version, range) &&
446-
!PackageRequest.getExoticResolver(range) &&
447+
!getExoticResolver(range) &&
447448
hasVersion
448449
) {
449450
this.reporter.warn(this.reporter.lang('incorrectLockfileEntry', req.pattern));

src/resolvers/base-resolver.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export default class BaseResolver {
1818
this.config = request.config;
1919
}
2020

21+
static +isVersion: string => boolean;
2122
resolver: PackageResolver;
2223
reporter: Reporter;
2324
fragment: string;
@@ -26,13 +27,13 @@ export default class BaseResolver {
2627
config: Config;
2728
registry: RegistryNames;
2829

29-
fork(Resolver: Function, resolveArg: any, ...args: Array<string>): Promise<Manifest> {
30+
fork(Resolver: Class<BaseResolver>, resolveArg: any, ...args: Array<string>): Promise<Manifest> {
3031
const resolver = new Resolver(this.request, ...args);
3132
resolver.registry = this.registry;
3233
return resolver.resolve(resolveArg);
3334
}
3435

35-
resolve(): Promise<Manifest> {
36+
resolve(resolveArg?: any): Promise<Manifest> {
3637
throw new Error('Not implemented');
3738
}
3839
}

src/resolvers/exotics/git-resolver.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ import Git from '../../util/git.js';
1212

1313
const urlParse = require('url').parse;
1414

15+
const GIT_PROTOCOL_PATTERN = /git\+.+:/;
16+
1517
// we purposefully omit https and http as those are only valid if they end in the .git extension
16-
const GIT_PROTOCOLS = ['git:', 'git+ssh:', 'git+https:', 'ssh:'];
18+
const GIT_PROTOCOLS = ['git:', 'ssh:'];
1719

1820
const GIT_HOSTS = ['github.com', 'gitlab.com', 'bitbucket.com', 'bitbucket.org'];
1921

@@ -44,6 +46,10 @@ export default class GitResolver extends ExoticResolver {
4446
return true;
4547
}
4648

49+
if (GIT_PROTOCOL_PATTERN.test(parts.protocol)) {
50+
return true;
51+
}
52+
4753
if (GIT_PROTOCOLS.indexOf(parts.protocol) >= 0) {
4854
return true;
4955
}

src/resolvers/index.js

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
/* @flow */
22

3+
import BaseResolver from './base-resolver.js';
4+
35
import RegistryNpm from './registries/npm-resolver.js';
46
import RegistryYarn from './registries/yarn-resolver.js';
57

@@ -19,16 +21,25 @@ import ExoticGitLab from './exotics/gitlab-resolver.js';
1921
import ExoticGist from './exotics/gist-resolver.js';
2022
import ExoticBitbucket from './exotics/bitbucket-resolver.js';
2123

22-
export const exotics = {
23-
git: ExoticGit,
24-
tarball: ExoticTarball,
25-
github: ExoticGitHub,
26-
file: ExoticFile,
27-
link: ExoticLink,
28-
gitlab: ExoticGitLab,
29-
gist: ExoticGist,
30-
bitbucket: ExoticBitbucket,
31-
};
24+
const exotics: Set<Class<$Subtype<BaseResolver>>> = new Set([
25+
ExoticGit,
26+
ExoticTarball,
27+
ExoticGitHub,
28+
ExoticFile,
29+
ExoticLink,
30+
ExoticGitLab,
31+
ExoticGist,
32+
ExoticBitbucket,
33+
]);
34+
35+
export function getExoticResolver(pattern: string): ?Class<$Subtype<BaseResolver>> {
36+
for (const Resolver of exotics) {
37+
if (Resolver.isVersion(pattern)) {
38+
return Resolver;
39+
}
40+
}
41+
return null;
42+
}
3243

3344
//
3445

@@ -59,8 +70,10 @@ import ExoticRegistryResolver from './exotics/registry-resolver.js';
5970
for (const key in registries) {
6071
const RegistryResolver = registries[key];
6172

62-
exotics[key] = class extends ExoticRegistryResolver {
63-
static protocol = key;
64-
static factory = RegistryResolver;
65-
};
73+
exotics.add(
74+
class extends ExoticRegistryResolver {
75+
static protocol = key;
76+
static factory = RegistryResolver;
77+
},
78+
);
6679
}

0 commit comments

Comments
 (0)