Skip to content

Fix duplicated query string and hash in bare-domain links - #72

Open
hyldmo wants to merge 1 commit into
refined-github:mainfrom
hyldmo:fix/duplicate-query-bare-domain
Open

Fix duplicated query string and hash in bare-domain links#72
hyldmo wants to merge 1 commit into
refined-github:mainfrom
hyldmo:fix/duplicate-query-bare-domain

Conversation

@hyldmo

@hyldmo hyldmo commented Jun 4, 2026

Copy link
Copy Markdown

The bug

When a URL's host is followed directly by ? or # with no path, the shortened text repeats the query string / hash:

Input Shortened (before) Expected
https://example.com?foo=bar example.com?foo=bar?foo=bar example.com?foo=bar
https://example.com#frag example.com#frag#frag example.com#frag

This is visible in the wild via Refined GitHub's shorten-links feature: a comment containing a bare-domain link with a query string (e.g. a CI preview-environment link like https://sub.example.com?preview_id=123) renders its link text with the query string twice, while the href stays correct.

Root cause

origin is parsed manually to avoid encoding/punycode issues:

const origin = href.split('/', 3).join('/');

This assumes a scheme://host/… shape. When there is no / between the host and the ?/#, the query/hash is absorbed into origin. The bare-domain branch (pathname === '/') then appends url.search / decodeURI(url.hash) a second time, producing the duplication.

A trailing slash (example.com/?foo=bar) makes split('/', 3) stop at the host, so the bug only triggers on the slash-less form — which is why no existing fixture caught it.

The fix

Strip any ?query/#hash from origin so it stays scheme://host[:port]:

-const origin = href.split('/', 3).join('/');
+const origin = href.split('/', 3).join('/').replace(/[?#].*/, '');

The replace is a no-op for any URL that has a path (i.e. every previously-handled case), so all existing snapshots are unchanged — the snapshot diff is purely additive.

Tests

Added bare-domain regression fixtures covering query + hash, across https (scheme stripped), http (scheme retained), www, and the already-working trailing-slash variant. npm test (xo + tsd + vitest, 141 tests) passes.

When a URL's host is followed directly by `?` or `#` with no path
(e.g. `https://example.com?foo=bar`), `origin` is parsed via
`href.split('/', 3).join('/')`, which assumes a `scheme://host/...`
shape and absorbs the query/hash into `origin`. The bare-domain
branch then appends `url.search`/`url.hash` again, producing a
duplicated query/hash in the shortened text:

  https://example.com?foo=bar  ->  example.com?foo=bar?foo=bar
  https://example.com#frag     ->  example.com#frag#frag

A trailing slash (`example.com/?foo=bar`) hid the bug, so no existing
fixture exercised the slash-less case.

Strip any `?query`/`#hash` from `origin` so it stays `scheme://host`.
The replace is a no-op for URLs that have a path (every prior case),
so existing snapshots are unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant