@@ -32,8 +32,10 @@ const CASE_INSENSITIVE_HOSTS = new Set(['github.com', 'gitlab.com'])
3232 * Shared across the registry sub-workers (npm, Maven, …) and the GitHub
3333 * enricher so `repos.url` keys never diverge per ADR 0001. Handles npm
3434 * shorthand (`github:owner/repo`, bare `owner/repo`), SSH scp form, `ssh://`,
35- * `git+`, `git://`, `www.`, and monorepo `/tree/<branch>/<path>` deep-links
36- * (only the first two path segments are kept). Returns null when the input
35+ * `git+`, `git://`, `www.`, and monorepo deep-links: GitHub/Bitbucket's bare
36+ * `/tree/<branch>/<path>` (only the first two path segments are kept) and
37+ * GitLab's `/-/tree/<branch>/<path>` (kept segments run up to the `/-/`,
38+ * preserving arbitrarily nested subgroups). Returns null when the input
3739 * cannot be reduced to an owner/name pair.
3840 */
3941export function canonicalizeRepoUrl ( raw : string ) : CanonicalRepo | null {
@@ -80,8 +82,22 @@ export function canonicalizeRepoUrl(raw: string): CanonicalRepo | null {
8082 if ( segments . length < 2 ) return null
8183
8284 const isKnownHost = hostname in HOST_ENUM
83- let ownerPath = isKnownHost ? [ segments [ 0 ] ] : segments . slice ( 0 , - 1 )
84- let name = ( isKnownHost ? segments [ 1 ] : segments [ segments . length - 1 ] ) . replace ( / \. g i t $ / , '' )
85+ // GitLab uniquely supports arbitrarily nested subgroups (group/subgroup/.../project),
86+ // unlike GitHub/Bitbucket's flat owner/repo. Its deep-link suffixes (tree, blob, issues,
87+ // merge_requests, ...) are marked off by a `/-/` path segment rather than appended
88+ // directly after the repo path, so split there instead of truncating to 2 segments.
89+ let pathSegments : string [ ]
90+ if ( hostname === 'gitlab.com' ) {
91+ const dashIdx = segments . indexOf ( '-' )
92+ pathSegments = dashIdx === - 1 ? segments : segments . slice ( 0 , dashIdx )
93+ } else if ( isKnownHost ) {
94+ pathSegments = segments . slice ( 0 , 2 )
95+ } else {
96+ pathSegments = segments
97+ }
98+
99+ let ownerPath = pathSegments . slice ( 0 , - 1 )
100+ let name = ( pathSegments [ pathSegments . length - 1 ] ?? '' ) . replace ( / \. g i t $ / , '' )
85101 if ( ! name || ownerPath . length === 0 || ownerPath . some ( ( seg ) => ! seg ) ) return null
86102
87103 if ( CASE_INSENSITIVE_HOSTS . has ( hostname ) ) {
0 commit comments