Skip to content

Commit

Permalink
rewrite the entire module (#80)
Browse files Browse the repository at this point in the history
* feat! rewrite the world

all internals have been rewritten to maintain a similar contract but to remove
excessive use of regular expressions, unnecessary loops, the custom string
templating engine, and various other bits of complexity

BREAKING CHANGE extending with custom providers has changed
  • Loading branch information
nlf committed Mar 9, 2021
1 parent deab507 commit c218b9e
Show file tree
Hide file tree
Showing 10 changed files with 5,434 additions and 4,112 deletions.
211 changes: 143 additions & 68 deletions git-host-info.js
Original file line number Diff line number Diff line change
@@ -1,79 +1,154 @@
'use strict'
const maybeJoin = (...args) => args.every(arg => arg) ? args.join('') : ''
const maybeEncode = (arg) => arg ? encodeURIComponent(arg) : ''

var gitHosts = module.exports = {
github: {
// First two are insecure and generally shouldn't be used any more, but
// they are still supported.
'protocols': [ 'git', 'http', 'git+ssh', 'git+https', 'ssh', 'https' ],
'domain': 'github.com',
'treepath': 'tree',
'filetemplate': 'https://{auth@}raw.githubusercontent.com/{user}/{project}/{committish}/{path}',
'bugstemplate': 'https://{domain}/{user}/{project}/issues',
'gittemplate': 'git://{auth@}{domain}/{user}/{project}.git{#committish}',
'tarballtemplate': 'https://codeload.{domain}/{user}/{project}/tar.gz/{committish}'
},
bitbucket: {
'protocols': [ 'git+ssh', 'git+https', 'ssh', 'https' ],
'domain': 'bitbucket.org',
'treepath': 'src',
'tarballtemplate': 'https://{domain}/{user}/{project}/get/{committish}.tar.gz'
},
gitlab: {
'protocols': [ 'git+ssh', 'git+https', 'ssh', 'https' ],
'domain': 'gitlab.com',
'treepath': 'tree',
'bugstemplate': 'https://{domain}/{user}/{project}/issues',
'httpstemplate': 'git+https://{auth@}{domain}/{user}/{projectPath}.git{#committish}',
'tarballtemplate': 'https://{domain}/{user}/{project}/repository/archive.tar.gz?ref={committish}',
'pathmatch': /^\/([^/]+)\/((?!.*(\/-\/|\/repository(\/[^/]+)?\/archive\.tar\.gz)).*?)(?:\.git|\/)?$/
},
gist: {
'protocols': [ 'git', 'git+ssh', 'git+https', 'ssh', 'https' ],
'domain': 'gist.github.com',
'pathmatch': /^[/](?:([^/]+)[/])?([a-z0-9]{7,})(?:[.]git)?$/,
'filetemplate': 'https://gist.githubusercontent.com/{user}/{project}/raw{/committish}/{path}',
'bugstemplate': 'https://{domain}/{project}',
'gittemplate': 'git://{domain}/{project}.git{#committish}',
'sshtemplate': 'git@{domain}:/{project}.git{#committish}',
'sshurltemplate': 'git+ssh://git@{domain}/{project}.git{#committish}',
'browsetemplate': 'https://{domain}/{project}{/committish}',
'browsefiletemplate': 'https://{domain}/{project}{/committish}{#path}',
'docstemplate': 'https://{domain}/{project}{/committish}',
'httpstemplate': 'git+https://{domain}/{project}.git{#committish}',
'shortcuttemplate': '{type}:{project}{#committish}',
'pathtemplate': '{project}{#committish}',
'tarballtemplate': 'https://codeload.github.com/gist/{project}/tar.gz/{committish}',
'hashformat': function (fragment) {
return 'file-' + formatHashFragment(fragment)
const defaults = {
sshtemplate: ({ domain, user, project, committish }) => `git@${domain}:${user}/${project}.git${maybeJoin('#', committish)}`,
sshurltemplate: ({ domain, user, project, committish }) => `git+ssh://git@${domain}/${user}/${project}.git${maybeJoin('#', committish)}`,
browsetemplate: ({ domain, user, project, committish, treepath }) => `https://${domain}/${user}/${project}${maybeJoin('/', treepath, '/', maybeEncode(committish))}`,
browsefiletemplate: ({ domain, user, project, committish, treepath, path, fragment, hashformat }) => `https://${domain}/${user}/${project}/${treepath}/${maybeEncode(committish || 'master')}/${path}${maybeJoin('#', hashformat(fragment || ''))}`,
docstemplate: ({ domain, user, project, treepath, committish }) => `https://${domain}/${user}/${project}${maybeJoin('/', treepath, '/', maybeEncode(committish))}#readme`,
httpstemplate: ({ auth, domain, user, project, committish }) => `git+https://${maybeJoin(auth, '@')}${domain}/${user}/${project}.git${maybeJoin('#', committish)}`,
filetemplate: ({ domain, user, project, committish, path }) => `https://${domain}/${user}/${project}/raw/${maybeEncode(committish) || 'master'}/${path}`,
shortcuttemplate: ({ type, user, project, committish }) => `${type}:${user}/${project}${maybeJoin('#', committish)}`,
pathtemplate: ({ user, project, committish }) => `${user}/${project}${maybeJoin('#', committish)}`,
bugstemplate: ({ domain, user, project }) => `https://${domain}/${user}/${project}/issues`,
hashformat: formatHashFragment
}

const gitHosts = {}
gitHosts.github = Object.assign({}, defaults, {
// First two are insecure and generally shouldn't be used any more, but
// they are still supported.
protocols: ['git:', 'http:', 'git+ssh:', 'git+https:', 'ssh:', 'https:'],
domain: 'github.com',
treepath: 'tree',
filetemplate: ({ auth, user, project, committish, path }) => `https://${maybeJoin(auth, '@')}raw.githubusercontent.com/${user}/${project}/${maybeEncode(committish) || 'master'}/${path}`,
gittemplate: ({ auth, domain, user, project, committish }) => `git://${maybeJoin(auth, '@')}${domain}/${user}/${project}.git${maybeJoin('#', committish)}`,
tarballtemplate: ({ domain, user, project, committish }) => `https://codeload.${domain}/${user}/${project}/tar.gz/${maybeEncode(committish) || 'master'}`,
extract: (url) => {
let [, user, project, type, committish] = url.pathname.split('/', 5)
if (type && type !== 'tree') {
return
}

if (!type) {
committish = url.hash.slice(1)
}

if (project && project.endsWith('.git')) {
project = project.slice(0, -4)
}

if (!user || !project) {
return
}

return { user, project, committish }
}
}
})

var gitHostDefaults = {
'sshtemplate': 'git@{domain}:{user}/{project}.git{#committish}',
'sshurltemplate': 'git+ssh://git@{domain}/{user}/{project}.git{#committish}',
'browsetemplate': 'https://{domain}/{user}/{project}{/tree/committish}',
'browsefiletemplate': 'https://{domain}/{user}/{project}/{treepath}/{committish}/{path}{#fragment}',
'docstemplate': 'https://{domain}/{user}/{project}{/tree/committish}#readme',
'httpstemplate': 'git+https://{auth@}{domain}/{user}/{project}.git{#committish}',
'filetemplate': 'https://{domain}/{user}/{project}/raw/{committish}/{path}',
'shortcuttemplate': '{type}:{user}/{project}{#committish}',
'pathtemplate': '{user}/{project}{#committish}',
'pathmatch': /^[/]([^/]+)[/]([^/]+?)(?:[.]git|[/])?$/,
'hashformat': formatHashFragment
}
gitHosts.bitbucket = Object.assign({}, defaults, {
protocols: ['git+ssh:', 'git+https:', 'ssh:', 'https:'],
domain: 'bitbucket.org',
treepath: 'src',
tarballtemplate: ({ domain, user, project, committish }) => `https://${domain}/${user}/${project}/get/${maybeEncode(committish) || 'master'}.tar.gz`,
extract: (url) => {
let [, user, project, aux] = url.pathname.split('/', 4)
if (['get'].includes(aux)) {
return
}

if (project && project.endsWith('.git')) {
project = project.slice(0, -4)
}

if (!user || !project) {
return
}

return { user, project, committish: url.hash.slice(1) }
}
})

gitHosts.gitlab = Object.assign({}, defaults, {
protocols: ['git+ssh:', 'git+https:', 'ssh:', 'https:'],
domain: 'gitlab.com',
treepath: 'tree',
httpstemplate: ({ auth, domain, user, project, committish }) => `git+https://${maybeJoin(auth, '@')}${domain}/${user}/${project}.git${maybeJoin('#', committish)}`,
tarballtemplate: ({ domain, user, project, committish }) => `https://${domain}/${user}/${project}/repository/archive.tar.gz?ref=${maybeEncode(committish) || 'master'}`,
extract: (url) => {
const path = url.pathname.slice(1)
if (path.includes('/-/')) {
return
}

const segments = path.split('/')
let project = segments.pop()
if (project.endsWith('.git')) {
project = project.slice(0, -4)
}

const user = segments.join('/')
if (!user || !project) {
return
}

return { user, project, committish: url.hash.slice(1) }
}
})

gitHosts.gist = Object.assign({}, defaults, {
protocols: ['git:', 'git+ssh:', 'git+https:', 'ssh:', 'https:'],
domain: 'gist.github.com',
sshtemplate: ({ domain, project, committish }) => `git@${domain}:${project}.git${maybeJoin('#', committish)}`,
sshurltemplate: ({ domain, project, committish }) => `git+ssh://git@${domain}/${project}.git${maybeJoin('#', committish)}`,
browsetemplate: ({ domain, project, committish }) => `https://${domain}/${project}${maybeJoin('/', maybeEncode(committish))}`,
browsefiletemplate: ({ domain, project, committish, path, hashformat }) => `https://${domain}/${project}${maybeJoin('/', maybeEncode(committish))}${maybeJoin('#', hashformat(path))}`,
docstemplate: ({ domain, project, committish }) => `https://${domain}/${project}${maybeJoin('/', maybeEncode(committish))}`,
httpstemplate: ({ domain, project, committish }) => `git+https://${domain}/${project}.git${maybeJoin('#', committish)}`,
filetemplate: ({ user, project, committish, path }) => `https://gist.githubusercontent.com/${user}/${project}/raw${maybeJoin('/', maybeEncode(committish))}/${path}`,
shortcuttemplate: ({ type, project, committish }) => `${type}:${project}${maybeJoin('#', committish)}`,
pathtemplate: ({ project, committish }) => `${project}${maybeJoin('#', committish)}`,
bugstemplate: ({ domain, project }) => `https://${domain}/${project}`,
gittemplate: ({ domain, project, committish }) => `git://${domain}/${project}.git${maybeJoin('#', committish)}`,
tarballtemplate: ({ project, committish }) => `https://codeload.github.com/gist/${project}/tar.gz/${maybeEncode(committish) || 'master'}`,
extract: (url) => {
let [, user, project, aux] = url.pathname.split('/', 4)
if (aux === 'raw') {
return
}

if (!project) {
if (!user) {
return
}

project = user
user = null
}

if (project.endsWith('.git')) {
project = project.slice(0, -4)
}

Object.keys(gitHosts).forEach(function (name) {
Object.keys(gitHostDefaults).forEach(function (key) {
if (gitHosts[name][key]) return
gitHosts[name][key] = gitHostDefaults[key]
})
gitHosts[name].protocols_re = RegExp('^(' +
gitHosts[name].protocols.map(function (protocol) {
return protocol.replace(/([\\+*{}()[\]$^|])/g, '\\$1')
}).join('|') + '):$')
return { user, project, committish: url.hash.slice(1) }
},
hashformat: function (fragment) {
return fragment && 'file-' + formatHashFragment(fragment)
}
})

const names = Object.keys(gitHosts)
gitHosts.byShortcut = {}
gitHosts.byDomain = {}
for (const name of names) {
gitHosts.byShortcut[`${name}:`] = name
gitHosts.byDomain[gitHosts[name].domain] = name
}

function formatHashFragment (fragment) {
return fragment.toLowerCase().replace(/^\W+|\/|\W+$/g, '').replace(/\W+/g, '-')
}

module.exports = gitHosts

0 comments on commit c218b9e

Please sign in to comment.