UNPKG

@pnpm/git-resolver

Version:
195 lines 7.25 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.parseBareSpecifier = parseBareSpecifier; // cspell:ignore sshurl const url_1 = __importStar(require("url")); const fetch_1 = require("@pnpm/fetch"); const graceful_git_1 = __importDefault(require("graceful-git")); const hosted_git_info_1 = __importDefault(require("hosted-git-info")); const gitProtocols = new Set([ 'git', 'git+http', 'git+https', 'git+rsync', 'git+ftp', 'git+file', 'git+ssh', 'ssh', ]); async function parseBareSpecifier(bareSpecifier, opts) { const hosted = hosted_git_info_1.default.fromUrl(bareSpecifier); if (hosted != null) { return fromHostedGit(hosted, opts); } const colonsPos = bareSpecifier.indexOf(':'); if (colonsPos === -1) return null; const protocol = bareSpecifier.slice(0, colonsPos); if (protocol && gitProtocols.has(protocol.toLocaleLowerCase())) { const correctBareSpecifier = correctUrl(bareSpecifier); const url = new url_1.URL(correctBareSpecifier); if (!url?.protocol) return null; const hash = (url.hash?.length > 1) ? decodeURIComponent(url.hash.slice(1)) : null; return { fetchSpec: urlToFetchSpec(url), normalizedBareSpecifier: bareSpecifier, ...parseGitParams(hash), }; } return null; } function urlToFetchSpec(url) { url.hash = ''; const fetchSpec = url_1.default.format(url); if (fetchSpec.startsWith('git+')) { return fetchSpec.slice(4); } return fetchSpec; } async function fromHostedGit(hosted, agentOptions) { let fetchSpec = null; // try git/https url before fallback to ssh url const gitHttpsUrl = hosted.https({ noCommittish: true, noGitPlus: true }); if (gitHttpsUrl && await isRepoPublic(gitHttpsUrl, agentOptions) && await accessRepository(gitHttpsUrl)) { fetchSpec = gitHttpsUrl; } else { const gitSshUrl = hosted.ssh({ noCommittish: true }); if (gitSshUrl && await accessRepository(gitSshUrl)) { fetchSpec = gitSshUrl; } } if (!fetchSpec) { const httpsUrl = hosted.https({ noGitPlus: true, noCommittish: true }); if (httpsUrl) { if ((hosted.auth || !await isRepoPublic(httpsUrl, agentOptions)) && await accessRepository(httpsUrl)) { return { fetchSpec: httpsUrl, hosted: { ...hosted, _fill: hosted._fill, tarball: undefined, }, normalizedBareSpecifier: `git+${httpsUrl}`, ...parseGitParams(hosted.committish), }; } else { try { // when git ls-remote private repo, it asks for login credentials. // use HTTP HEAD request to test whether this is a private repo, to avoid login prompt. // this is very similar to yarn classic's behavior. // npm instead tries git ls-remote directly which prompts user for login credentials. // HTTP HEAD on https://domain/user/repo, strip out ".git" const response = await (0, fetch_1.fetchWithAgent)(httpsUrl.replace(/\.git$/, ''), { method: 'HEAD', follow: 0, retry: { retries: 0 }, agentOptions }); if (response.ok) { fetchSpec = httpsUrl; } } catch { // ignore } } } } if (!fetchSpec) { // use ssh url for likely private repo fetchSpec = hosted.sshurl({ noCommittish: true }); } return { fetchSpec: fetchSpec, hosted: { ...hosted, _fill: hosted._fill, tarball: hosted.tarball, }, normalizedBareSpecifier: hosted.shortcut(), ...parseGitParams(hosted.committish), }; } async function isRepoPublic(httpsUrl, agentOptions) { try { const response = await (0, fetch_1.fetchWithAgent)(httpsUrl.replace(/\.git$/, ''), { method: 'HEAD', follow: 0, retry: { retries: 0 }, agentOptions }); return response.ok; } catch { return false; } } async function accessRepository(repository) { try { await (0, graceful_git_1.default)(['ls-remote', '--exit-code', repository, 'HEAD'], { retries: 0 }); return true; } catch { return false; } } function parseGitParams(committish) { const result = { gitCommittish: null }; if (!committish) { return result; } const params = committish.split('&'); for (const param of params) { if (param.length >= 7 && param.slice(0, 7) === 'semver:') { result.gitRange = param.slice(7); } else if (param.slice(0, 5) === 'path:') { result.path = param.slice(5); } else { result.gitCommittish = param; } } return result; } // handle SCP-like URLs // see https://github.com/yarnpkg/yarn/blob/5682d55/src/util/git.js#L103 function correctUrl(gitUrl) { let _gitUrl = gitUrl.replace(/^git\+/, ''); if (_gitUrl.startsWith('ssh://')) { const hashIndex = _gitUrl.indexOf('#'); let hash = ''; if (hashIndex !== -1) { hash = _gitUrl.slice(hashIndex); _gitUrl = _gitUrl.slice(0, hashIndex); } const [auth, ...pathname] = _gitUrl.slice(6).split('/'); const [, host] = auth.split('@'); if (host.includes(':') && !/:\d+$/.test(host)) { const authArr = auth.split(':'); const protocol = gitUrl.split('://')[0]; gitUrl = `${protocol}://${authArr.slice(0, -1).join(':') + '/' + authArr[authArr.length - 1]}${pathname.length ? '/' + pathname.join('/') : ''}${hash}`; } } return gitUrl; } //# sourceMappingURL=parseBareSpecifier.js.map