-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathspecs.ts
More file actions
138 lines (130 loc) · 3.82 KB
/
specs.ts
File metadata and controls
138 lines (130 loc) · 3.82 KB
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
136
137
138
/**
* @fileoverview Package spec parsing and GitHub URL utilities.
*/
import npmPackageArg from '../external/npm-package-arg'
import { isObjectObject } from '../objects'
import { isNonEmptyString } from '../strings'
import { StringPrototypeEndsWith } from '../primordials'
/**
* Extract user and project from GitHub repository URL.
*
* @example
* ```typescript
* getRepoUrlDetails('https://github.com/lodash/lodash.git')
* // { user: 'lodash', project: 'lodash' }
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export function getRepoUrlDetails(repoUrl: string = ''): {
user: string
project: string
} {
// Anchor the host to exactly `github.com` (optionally preceded by
// userinfo like `user@`). Escaping the `.` blocks lookalikes like
// `githubXcom`; pinning the host to the full final label blocks
// `fake-github.com` or `github.com.attacker.tld` shenanigans. The
// scheme class allows `+` so npm's canonical `git+https://…` and
// `git+ssh://…` forms from package.json `repository.url` match.
// Callers passing scp-style git@github.com:… need to normalize
// upstream; we require `://` here so the host is unambiguous.
const match =
/^(?:[a-z][a-z+]*:\/\/)(?:[^/@]+@)?github\.com\/([^?#]+)(?:[?#]|$)/i.exec(
repoUrl,
)
if (!match || !match[1]) {
return { user: '', project: '' }
}
const userAndRepo = match[1].split('/')
const user = userAndRepo[0] || ''
const rawProject = userAndRepo[1] ?? ''
const project = StringPrototypeEndsWith(rawProject, '.git')
? rawProject.slice(0, -4)
: rawProject
return { user, project }
}
/**
* Generate GitHub API URL for a tag reference.
*
* @example
* ```typescript
* gitHubTagRefUrl('lodash', 'lodash', 'v4.17.21')
* // 'https://api.github.com/repos/lodash/lodash/git/ref/tags/v4.17.21'
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export function gitHubTagRefUrl(
user: string,
project: string,
tag: string,
): string {
return `https://api.github.com/repos/${user}/${project}/git/ref/tags/${tag}`
}
/**
* Generate GitHub tarball download URL for a commit SHA.
*
* @example
* ```typescript
* gitHubTgzUrl('lodash', 'lodash', 'abc123')
* // 'https://github.com/lodash/lodash/archive/abc123.tar.gz'
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export function gitHubTgzUrl(
user: string,
project: string,
sha: string,
): string {
return `https://github.com/${user}/${project}/archive/${sha}.tar.gz`
}
/**
* Check if a package specifier is a GitHub tarball URL.
*
* @example
* ```typescript
* isGitHubTgzSpec('https://github.com/user/repo/archive/abc123.tar.gz') // true
* isGitHubTgzSpec('lodash@4.17.21') // false
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export function isGitHubTgzSpec(spec: unknown, where?: string): boolean {
let parsedSpec: unknown
if (isObjectObject(spec)) {
parsedSpec = spec
} else {
// module is imported at the top
parsedSpec = npmPackageArg(spec as string, where)
}
const typedSpec = parsedSpec as { type?: string; saveSpec?: string }
return (
typedSpec.type === 'remote' && !!typedSpec.saveSpec?.endsWith('.tar.gz')
)
}
/**
* Check if a package specifier is a GitHub URL with committish.
*
* @example
* ```typescript
* isGitHubUrlSpec('github:user/repo#v1.0.0') // true
* isGitHubUrlSpec('lodash@4.17.21') // false
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export function isGitHubUrlSpec(spec: unknown, where?: string): boolean {
let parsedSpec: unknown
if (isObjectObject(spec)) {
parsedSpec = spec
} else {
// module is imported at the top
parsedSpec = npmPackageArg(spec as string, where)
}
const typedSpec = parsedSpec as {
gitCommittish?: string
hosted?: { domain?: string }
type?: string
}
return (
typedSpec.type === 'git' &&
typedSpec.hosted?.domain === 'github.com' &&
isNonEmptyString(typedSpec.gitCommittish)
)
}