-
Notifications
You must be signed in to change notification settings - Fork 2.7k
/
git-resolver.js
135 lines (111 loc) · 3.92 KB
/
git-resolver.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/* @flow */
import type {Manifest} from '../../types.js';
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';
const urlParse = require('url').parse;
const GIT_HOSTS = ['github.com', 'gitlab.com', 'bitbucket.com', 'bitbucket.org'];
const GIT_PATTERN_MATCHERS = [/^git:/, /^git\+.+:/, /^ssh:/, /^https?:.+\.git$/, /^https?:.+\.git#.+/];
export default class GitResolver extends ExoticResolver {
constructor(request: PackageRequest, fragment: string) {
super(request, fragment);
const {url, hash} = versionUtil.explodeHashedUrl(fragment);
this.url = url;
this.hash = hash;
}
url: string;
hash: string;
static isVersion(pattern: string): boolean {
for (const matcher of GIT_PATTERN_MATCHERS) {
if (matcher.test(pattern)) {
return true;
}
}
const {hostname, path} = urlParse(pattern);
if (hostname && path && GIT_HOSTS.indexOf(hostname) >= 0) {
// only if dependency is pointing to a git repo,
// e.g. facebook/flow and not file in a git repo facebook/flow/archive/v1.0.0.tar.gz
return path.split('/').filter((p): boolean => !!p).length === 2;
}
return false;
}
async resolve(forked?: true): Promise<Manifest> {
const {url} = this;
// shortcut for hosted git. we will fallback to a GitResolver if the hosted git
// optimisations fail which the `forked` flag indicates so we don't get into an
// infinite loop
const parts = urlParse(url);
if (false && !forked && !parts.auth && parts.pathname) {
// check if this git url uses any of the hostnames defined in our hosted git resolvers
for (const name in hostedGitResolvers) {
const Resolver = hostedGitResolvers[name];
if (Resolver.hostname !== parts.hostname) {
continue;
}
// we have a match! clean up the pathname of url artifacts
let pathname = parts.pathname;
pathname = util.removePrefix(pathname, '/'); // remove prefixed slash
pathname = util.removeSuffix(pathname, '.git'); // remove .git suffix if present
const url = `${pathname}${this.hash ? '#' + decodeURIComponent(this.hash) : ''}`;
return this.fork(Resolver, false, url);
}
}
// get from lockfile
const shrunk = this.request.getLocked('git');
if (shrunk) {
return shrunk;
}
const {config} = this;
const gitUrl = Git.npmUrlToGitUrl(url);
const client = new Git(config, gitUrl, this.hash);
const commit = await client.init();
async function tryRegistry(registry): Promise<?Manifest> {
const {filename} = registries[registry];
const file = await client.getFile(filename);
if (!file) {
return null;
}
const json = await config.readJson(`${url}/${filename}`, () => JSON.parse(file));
json._uid = commit;
json._remote = {
resolved: `${url}#${commit}`,
type: 'git',
reference: url,
hash: commit,
registry,
};
return json;
}
const file = await tryRegistry(this.registry);
if (file) {
return file;
}
for (const registry in registries) {
if (registry === this.registry) {
continue;
}
const file = await tryRegistry(registry);
if (file) {
return file;
}
}
return {
// This is just the default, it can be overridden with key of dependencies
name: guessName(url),
version: '0.0.0',
_uid: commit,
_remote: {
resolved: `${url}#${commit}`,
type: 'git',
reference: url,
hash: commit,
registry: 'npm',
},
};
}
}