---
package.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/package.json b/package.json
index a97c687..d070d9a 100644
--- a/package.json
+++ b/package.json
@@ -35,7 +35,7 @@
},
"devDependencies": {
"@npmcli/eslint-config": "^5.0.0",
- "@npmcli/template-oss": "4.23.5",
+ "@npmcli/template-oss": "4.23.6",
"tap": "^16.0.1"
},
"files": [
@@ -55,7 +55,7 @@
},
"templateOSS": {
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
- "version": "4.23.5",
+ "version": "4.23.6",
"publish": "true"
}
}
From ac08fe89153d19d1fecbd1e5ce5014fad833134c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 10 Apr 2025 06:54:54 -0700
Subject: [PATCH 3/5] chore: bump @npmcli/template-oss from 4.23.6 to 4.24.3
(#296)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [@npmcli/template-oss](https://github.com/npm/template-oss) from
4.23.6 to 4.24.3.
Release notes
Sourced from @npmcli/template-oss
's
releases.
v4.24.3
4.24.3
(2025-03-31)
Bug Fixes
v4.24.2
4.24.2
(2025-03-05)
Bug Fixes
v4.24.1
4.24.1
(2025-02-04)
Bug Fixes
v4.24.0
4.24.0
(2025-01-30)
Features
Dependencies
Changelog
Sourced from @npmcli/template-oss
's
changelog.
4.24.3
(2025-03-31)
Bug Fixes
4.24.2
(2025-03-05)
Bug Fixes
4.24.1
(2025-02-04)
Bug Fixes
4.24.0
(2025-01-30)
Features
Dependencies
Commits
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
---------
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: npm CLI robot
---
.gitignore | 1 +
package.json | 4 ++--
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/.gitignore b/.gitignore
index 2bab6d1..dedbc77 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@
!**/.gitignore
!/.commitlintrc.js
+!/.eslint.config.js
!/.eslintrc.js
!/.eslintrc.local.*
!/.git-blame-ignore-revs
diff --git a/package.json b/package.json
index d070d9a..ad9357f 100644
--- a/package.json
+++ b/package.json
@@ -35,7 +35,7 @@
},
"devDependencies": {
"@npmcli/eslint-config": "^5.0.0",
- "@npmcli/template-oss": "4.23.6",
+ "@npmcli/template-oss": "4.24.3",
"tap": "^16.0.1"
},
"files": [
@@ -55,7 +55,7 @@
},
"templateOSS": {
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
- "version": "4.23.6",
+ "version": "4.24.3",
"publish": "true"
}
}
From ef0865cc5c28700f990bf25d919e2520c944cf55 Mon Sep 17 00:00:00 2001
From: Jordan Harband
Date: Mon, 14 Apr 2025 10:35:15 -0700
Subject: [PATCH 4/5] feat: add `HostedGitInfo.fromManifest` (#288)
This encapsulates the logic used in `npm repo`
Per Slack discussion. Once this is published and updated in the cli,
I'll happily adapt the `npm repo` code to use this method.
The method will either return a HGI instance, a string URL, or `null`.
---
README.md | 7 ++++-
lib/index.js | 48 +++++++++++++++++++++++++++++++
test/file.js | 14 +++++++++
test/github.js | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++
test/gitlab.js | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 222 insertions(+), 1 deletion(-)
create mode 100644 test/file.js
diff --git a/README.md b/README.md
index 498e3d2..c5a537e 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@ const info = hostedGitInfo.fromUrl("git@github.com:npm/hosted-git-info.git", opt
*/
```
-If the URL can't be matched with a git host, `null` will be returned. We
+If the URL can't be matched with a git host, `null` will be returned. We
can match git, ssh and https urls. Additionally, we can match ssh connect
strings (`git@github.com:npm/hosted-git-info`) and shortcuts (eg,
`github:npm/hosted-git-info`). GitHub specifically, is detected in the case
@@ -59,6 +59,11 @@ Implications:
* *noCommittish* — If true then committishes won't be included in generated URLs.
* *noGitPlus* — If true then `git+` won't be prefixed on URLs.
+### const infoOrURL = hostedGitInfo.fromManifest(manifest[, options])
+
+* *manifest* is a package manifest, such as that returned by [`pacote.manifest()`](https://npmjs.com/pacote)
+* *options* is an optional object. It can have the same properties as `fromUrl` above.
+
## Methods
All of the methods take the same options as the `fromUrl` factory. Options
diff --git a/lib/index.js b/lib/index.js
index 0c9d0b0..2a7100d 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -7,6 +7,26 @@ const parseUrl = require('./parse-url.js')
const cache = new LRUCache({ max: 1000 })
+function unknownHostedUrl (url) {
+ try {
+ const {
+ protocol,
+ hostname,
+ pathname,
+ } = new URL(url)
+
+ if (!hostname) {
+ return null
+ }
+
+ const proto = /(?:git\+)http:$/.test(protocol) ? 'http:' : 'https:'
+ const path = pathname.replace(/\.git$/, '')
+ return `${proto}//${hostname}${path}`
+ } catch {
+ return null
+ }
+}
+
class GitHost {
constructor (type, user, auth, project, committish, defaultRepresentation, opts = {}) {
Object.assign(this, GitHost.#gitHosts[type], {
@@ -56,6 +76,34 @@ class GitHost {
return cache.get(key)
}
+ static fromManifest (manifest, opts = {}) {
+ if (!manifest || typeof manifest !== 'object') {
+ return
+ }
+
+ const r = manifest.repository
+ // TODO: look into also checking the `bugs`/`homepage` URLs
+
+ const rurl = r && (
+ typeof r === 'string'
+ ? r
+ : typeof r === 'object' && typeof r.url === 'string'
+ ? r.url
+ : null
+ )
+
+ if (!rurl) {
+ throw new Error('no repository')
+ }
+
+ const info = (rurl && GitHost.fromUrl(rurl.replace(/^git\+/, ''), opts)) || null
+ if (info) {
+ return info
+ }
+ const unk = unknownHostedUrl(rurl)
+ return GitHost.fromUrl(unk, opts) || unk
+ }
+
static parseUrl (url) {
return parseUrl(url)
}
diff --git a/test/file.js b/test/file.js
new file mode 100644
index 0000000..2cd2de0
--- /dev/null
+++ b/test/file.js
@@ -0,0 +1,14 @@
+const HostedGit = require('..')
+const t = require('tap')
+
+t.test('file:// URLs', t => {
+ const fileRepo = {
+ name: 'foo',
+ repository: {
+ url: 'file:///path/dot.git',
+ },
+ }
+ t.equal(HostedGit.fromManifest(fileRepo), null)
+
+ t.end()
+})
diff --git a/test/github.js b/test/github.js
index 6c01068..7d7739b 100644
--- a/test/github.js
+++ b/test/github.js
@@ -270,3 +270,80 @@ t.test('string methods populate correctly', t => {
t.end()
})
+
+t.test('from manifest', t => {
+ t.equal(HostedGit.fromManifest(), undefined, 'no manifest returns undefined')
+ t.equal(HostedGit.fromManifest(), undefined, 'no manifest returns undefined')
+ t.equal(HostedGit.fromManifest(false), undefined, 'false manifest returns undefined')
+ t.equal(HostedGit.fromManifest(() => {}), undefined, 'function manifest returns undefined')
+
+ const unknownHostRepo = {
+ name: 'foo',
+ repository: {
+ url: 'https://nope.com',
+ },
+ }
+ t.same(HostedGit.fromManifest(unknownHostRepo), 'https://nope.com/')
+
+ const insecureUnknownHostRepo = {
+ name: 'foo',
+ repository: {
+ url: 'http://nope.com',
+ },
+ }
+ t.same(HostedGit.fromManifest(insecureUnknownHostRepo), 'https://nope.com/')
+
+ const insecureGitUnknownHostRepo = {
+ name: 'foo',
+ repository: {
+ url: 'git+http://nope.com',
+ },
+ }
+ t.same(HostedGit.fromManifest(insecureGitUnknownHostRepo), 'http://nope.com')
+
+ const badRepo = {
+ name: 'foo',
+ repository: {
+ url: '#',
+ },
+ }
+ t.equal(HostedGit.fromManifest(badRepo), null)
+
+ const manifest = {
+ name: 'foo',
+ repository: {
+ type: 'git',
+ url: 'git+ssh://github.com/foo/bar.git',
+ },
+ }
+
+ const parsed = HostedGit.fromManifest(manifest)
+ t.same(parsed.browse(), 'https://github.com/foo/bar')
+
+ const monorepo = {
+ name: 'clowncar',
+ repository: {
+ type: 'git',
+ url: 'git+ssh://github.com/foo/bar.git',
+ directory: 'packages/foo',
+ },
+ }
+
+ const honk = HostedGit.fromManifest(monorepo)
+ t.same(honk.browse(monorepo.repository.directory), 'https://github.com/foo/bar/tree/HEAD/packages/foo')
+
+ const stringRepo = {
+ name: 'foo',
+ repository: 'git+ssh://github.com/foo/bar.git',
+ }
+ const stringRepoParsed = HostedGit.fromManifest(stringRepo)
+ t.same(stringRepoParsed.browse(), 'https://github.com/foo/bar')
+
+ const nonStringRepo = {
+ name: 'foo',
+ repository: 42,
+ }
+ t.throws(() => HostedGit.fromManifest(nonStringRepo))
+
+ t.end()
+})
diff --git a/test/gitlab.js b/test/gitlab.js
index 685bd06..ffa080c 100644
--- a/test/gitlab.js
+++ b/test/gitlab.js
@@ -321,3 +321,80 @@ t.test('string methods populate correctly', t => {
t.end()
})
+
+t.test('from manifest', t => {
+ t.equal(HostedGit.fromManifest(), undefined, 'no manifest returns undefined')
+ t.equal(HostedGit.fromManifest(), undefined, 'no manifest returns undefined')
+ t.equal(HostedGit.fromManifest(false), undefined, 'false manifest returns undefined')
+ t.equal(HostedGit.fromManifest(() => {}), undefined, 'function manifest returns undefined')
+
+ const unknownHostRepo = {
+ name: 'foo',
+ repository: {
+ url: 'https://nope.com',
+ },
+ }
+ t.same(HostedGit.fromManifest(unknownHostRepo), 'https://nope.com/')
+
+ const insecureUnknownHostRepo = {
+ name: 'foo',
+ repository: {
+ url: 'http://nope.com',
+ },
+ }
+ t.same(HostedGit.fromManifest(insecureUnknownHostRepo), 'https://nope.com/')
+
+ const insecureGitUnknownHostRepo = {
+ name: 'foo',
+ repository: {
+ url: 'git+http://nope.com',
+ },
+ }
+ t.same(HostedGit.fromManifest(insecureGitUnknownHostRepo), 'http://nope.com')
+
+ const badRepo = {
+ name: 'foo',
+ repository: {
+ url: '#',
+ },
+ }
+ t.equal(HostedGit.fromManifest(badRepo), null)
+
+ const manifest = {
+ name: 'foo',
+ repository: {
+ type: 'git',
+ url: 'git+ssh://gitlab.com/foo/bar.git',
+ },
+ }
+
+ const parsed = HostedGit.fromManifest(manifest)
+ t.same(parsed.browse(), 'https://gitlab.com/foo/bar')
+
+ const monorepo = {
+ name: 'clowncar',
+ repository: {
+ type: 'git',
+ url: 'git+ssh://gitlab.com/foo/bar.git',
+ directory: 'packages/foo',
+ },
+ }
+
+ const honk = HostedGit.fromManifest(monorepo)
+ t.same(honk.browse(monorepo.repository.directory), 'https://gitlab.com/foo/bar/tree/HEAD/packages/foo')
+
+ const stringRepo = {
+ name: 'foo',
+ repository: 'git+ssh://gitlab.com/foo/bar.git',
+ }
+ const stringRepoParsed = HostedGit.fromManifest(stringRepo)
+ t.same(stringRepoParsed.browse(), 'https://gitlab.com/foo/bar')
+
+ const nonStringRepo = {
+ name: 'foo',
+ repository: 42,
+ }
+ t.throws(() => HostedGit.fromManifest(nonStringRepo))
+
+ t.end()
+})
From 3dfd77128d9ecabf9ef692ca5af17c9349c56a4e Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Mon, 14 Apr 2025 11:29:05 -0700
Subject: [PATCH 5/5] chore: release 8.1.0 (#297)
:robot: I have created a release *beep* *boop*
---
##
[8.1.0](https://github.com/npm/hosted-git-info/compare/v8.0.2...v8.1.0)
(2025-04-14)
### Features
*
[`ef0865c`](https://github.com/npm/hosted-git-info/commit/ef0865cc5c28700f990bf25d919e2520c944cf55)
[#288](https://github.com/npm/hosted-git-info/pull/288) add
`HostedGitInfo.fromManifest` (#288) (@ljharb)
### Chores
*
[`ac08fe8`](https://github.com/npm/hosted-git-info/commit/ac08fe89153d19d1fecbd1e5ce5014fad833134c)
[#296](https://github.com/npm/hosted-git-info/pull/296) bump
@npmcli/template-oss from 4.23.6 to 4.24.3 (#296) (@dependabot[bot],
@npm-cli-bot)
---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 6 ++++++
package.json | 2 +-
3 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 6331e90..529b133 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "8.0.2"
+ ".": "8.1.0"
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ea84dac..f2a1030 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# Changelog
+## [8.1.0](https://github.com/npm/hosted-git-info/compare/v8.0.2...v8.1.0) (2025-04-14)
+### Features
+* [`ef0865c`](https://github.com/npm/hosted-git-info/commit/ef0865cc5c28700f990bf25d919e2520c944cf55) [#288](https://github.com/npm/hosted-git-info/pull/288) add `HostedGitInfo.fromManifest` (#288) (@ljharb)
+### Chores
+* [`ac08fe8`](https://github.com/npm/hosted-git-info/commit/ac08fe89153d19d1fecbd1e5ce5014fad833134c) [#296](https://github.com/npm/hosted-git-info/pull/296) bump @npmcli/template-oss from 4.23.6 to 4.24.3 (#296) (@dependabot[bot], @npm-cli-bot)
+
## [8.0.2](https://github.com/npm/hosted-git-info/compare/v8.0.1...v8.0.2) (2024-11-21)
### Bug Fixes
* [`cc004ba`](https://github.com/npm/hosted-git-info/commit/cc004bae62d17b90c2fc889fcde5afbcac2fc508) [#280](https://github.com/npm/hosted-git-info/pull/280) even better regex for host fragment (#280) (@wraithgar)
diff --git a/package.json b/package.json
index ad9357f..a9bb26b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "hosted-git-info",
- "version": "8.0.2",
+ "version": "8.1.0",
"description": "Provides metadata and conversions from repository urls for GitHub, Bitbucket and GitLab",
"main": "./lib/index.js",
"repository": {