diff --git a/README.md b/README.md index 1d86131..0588f32 100644 --- a/README.md +++ b/README.md @@ -212,20 +212,23 @@ to the base branch resolve against the PR branch instead. This ensures that links like `/blob/main/README.md` don't break when the file was added or moved in the PR. -For `/blob/` URLs, three ordered remap rules are applied +For `/blob/` URLs, four ordered remap rules are applied (lychee uses first-match-wins): -1. **Line-number anchors** (`#L123`): GitHub renders these with - JavaScript, so lychee can never verify the fragment. The anchor +1. **Line-number anchors** (`#L123`, `#L10-L20`): GitHub renders + these with JavaScript, so lychee can never verify the fragment. + The anchor is stripped and the file is checked on the PR branch. +2. **[Scroll to Text Fragment][stf] anchors** (`#:~:text=...`): + Browser-only feature, not present in static HTML. The anchor is stripped and the file is checked on the PR branch. -2. **Other fragment URLs** (`#section`): Remapped to +3. **Other fragment URLs** (`#section`): Remapped to `raw.githubusercontent.com` where lychee can verify the fragment in the raw file content (workaround for [lychee#1729](https://github.com/lycheeverse/lychee/issues/1729)). -3. **Non-fragment URLs**: Remapped from the base branch to the PR +4. **Non-fragment URLs**: Remapped from the base branch to the PR branch (the original behavior). -For `/tree/` URLs, rules 1 and 3 apply (no raw remap needed). +For `/tree/` URLs, rules 1 and 4 apply (no raw remap needed). **Global GitHub URL handling:** @@ -237,6 +240,9 @@ two patterns that affect ALL GitHub URLs (any repository): JS-rendered line-number fragment is skipped. This means consuming repos don't need to exclude these in their `lychee.toml`. +- **Scroll to Text Fragment anchors** (`#:~:text=...`): Stripped + from any GitHub `/blob/` URL. These are a browser-only feature + not present in static HTML. - **Issue comment anchors** (`#issuecomment-*`): The fragment is stripped so the issue/PR page is still checked, but the JS-rendered comment anchor is skipped. @@ -254,11 +260,14 @@ via `--remap` arguments: [lychee#1729](https://github.com/lycheeverse/lychee/issues/1729)** — flint remaps fragment URLs to `raw.githubusercontent.com` for the current PR's head branch, and strips line-number - anchors globally. + and Scroll to Text Fragment anchors globally. - **`#issuecomment-*` excludes** — flint strips the fragment via remap so the issue/PR page is still checked. -- **`#L\d+` line-number excludes** — flint strips the fragment - via remap so the file is still checked. +- **`#L\d+` / `#L\d+-L\d+` line-number excludes** — flint strips + the fragment via remap so the file is still checked. +- **`#:~:text=...` [Scroll to Text Fragment][stf] excludes** — + flint strips the fragment via remap so the file is still + checked. Note: flint uses `--remap` (not `--exclude`) for these because lychee's CLI `--exclude` flags override config file excludes @@ -444,3 +453,5 @@ When conventional commits land on `main`, Release Please opens > **Note:** CI checks don't trigger automatically on release-please > PRs because they are created with `GITHUB_TOKEN`. To run CI, > either click **Update branch** or **close and reopen** the PR. + +[stf]: https://developer.mozilla.org/en-US/docs/Web/URI/Fragment/Text_fragments diff --git a/tasks/lint/links.sh b/tasks/lint/links.sh index a6102de..c562cc2 100755 --- a/tasks/lint/links.sh +++ b/tasks/lint/links.sh @@ -25,9 +25,10 @@ eval "lychee_args=(${usage_lychee_args:-})" # https://github.com/lycheeverse/lychee/issues/1729). # # Lychee uses first-match-wins for remaps, so order matters: -# 1. Line-number anchors → strip fragment, remap to head branch -# 2. Other fragments → remap to raw.githubusercontent.com -# 3. No fragment → remap to head branch (existing behavior) +# 1. Line-number anchors → strip fragment, remap to head branch +# 2. Scroll to Text Fragments → strip fragment, remap to head branch +# 3. Other fragments → remap to raw.githubusercontent.com +# 4. No fragment → remap to head branch (existing behavior) # # Set LYCHEE_SKIP_GITHUB_REMAPS=true to skip the GitHub-specific remaps # emitted by this function (escape hatch if they cause unexpected behavior; @@ -72,26 +73,31 @@ build_remap_args() { local base_url="https://github.com/${repo}" local head_url="https://github.com/${head_repo}" - # /blob/ URLs — three rules, order matters (first-match-wins): + # /blob/ URLs — four rules, order matters (first-match-wins): - # 1. Line-number anchors (#L123): strip fragment, remap to head branch + # 1. Line-number anchors (#L123, #L10-L20): strip fragment, remap to head branch echo "--remap" - echo "^${base_url}/blob/${base_ref}/(.*?)#L[0-9]+\$ ${head_url}/blob/${head_ref}/\$1" + echo "^${base_url}/blob/${base_ref}/(.*?)#L[0-9]+.*\$ ${head_url}/blob/${head_ref}/\$1" - # 2. Other fragment URLs (#section): remap to raw.githubusercontent.com + # 2. Scroll to Text Fragment anchors (#:~:text=...): browser-only, + # strip fragment, remap to head branch + echo "--remap" + echo "^${base_url}/blob/${base_ref}/(.*?)#:~:text=.*\$ ${head_url}/blob/${head_ref}/\$1" + + # 3. Other fragment URLs (#section): remap to raw.githubusercontent.com # so lychee can verify the fragment in raw content echo "--remap" echo "^${base_url}/blob/${base_ref}/(.*#.*)\$ https://raw.githubusercontent.com/${head_repo}/${head_ref}/\$1" - # 3. Non-fragment URLs: branch-remap only (existing behavior) + # 4. Non-fragment URLs: branch-remap only (existing behavior) echo "--remap" echo "^${base_url}/blob/${base_ref}/(.*)\$ ${head_url}/blob/${head_ref}/\$1" # /tree/ URLs — two rules: - # 1. Line-number anchors: strip fragment, remap to head branch + # 1. Line-number anchors (#L123, #L10-L20): strip fragment, remap to head branch echo "--remap" - echo "^${base_url}/tree/${base_ref}/(.*?)#L[0-9]+\$ ${head_url}/tree/${head_ref}/\$1" + echo "^${base_url}/tree/${base_ref}/(.*?)#L[0-9]+.*\$ ${head_url}/tree/${head_ref}/\$1" # 2. Non-fragment URLs: branch-remap only echo "--remap" @@ -105,6 +111,9 @@ build_remap_args() { # - Line-number anchors (#L123, #L10-L20): rendered by JavaScript, # lychee cannot verify them. We strip the fragment so the file # itself is still checked. +# - Scroll to Text Fragment anchors (#:~:text=...): browser-only, +# lychee cannot verify them. We strip the fragment so the file +# itself is still checked. # - Issue comment anchors (#issuecomment-*): rendered by JavaScript, # lychee cannot verify them. The fragment is stripped so the # issue/PR page itself is still checked. @@ -122,6 +131,11 @@ build_global_github_args() { # shellcheck disable=SC2016 # single quotes are intentional: these are regex capture groups, not shell vars echo '^https://github.com/([^/]+/[^/]+)/blob/([^/]+)/(.*?)#L[0-9]+.*$ https://github.com/$1/blob/$2/$3' + # Strip Scroll to Text Fragment anchors from /blob/ URLs (browser-only, not in static HTML) + echo "--remap" + # shellcheck disable=SC2016 # single quotes are intentional: these are regex capture groups, not shell vars + echo '^https://github.com/([^/]+/[^/]+)/blob/([^/]+)/(.*?)#:~:text=.*$ https://github.com/$1/blob/$2/$3' + # Strip issue comment anchors (JS-rendered, not in static HTML). # The issue page is still checked, just not the fragment. # We use --remap instead of --exclude because CLI --exclude diff --git a/tests/test-links.md b/tests/test-links.md index 788fd93..fe08163 100644 --- a/tests/test-links.md +++ b/tests/test-links.md @@ -4,10 +4,22 @@ These links exercise the GitHub URL remap rules in `tasks/lint/links.sh`. On PR branches, lychee rewrites `blob/main/` URLs to the PR branch — these links verify that each remap rule works correctly during CI. -## Line-number anchors (`#L123`) — fragment stripped, file checked on PR branch +## Line-number anchors (`#L123`, `#L10-L20`) — fragment stripped, file checked on PR branch - [README.md#L1](https://github.com/grafana/flint/blob/main/README.md#L1) - [links.sh#L6](https://github.com/grafana/flint/blob/main/tasks/lint/links.sh#L6) +- [links.sh#L6-L10](https://github.com/grafana/flint/blob/main/tasks/lint/links.sh#L6-L10) + +## Scroll to Text Fragment anchors (`#:~:text=...`) — fragment stripped, file checked on PR branch + + + +- [links.sh text fragment](https://github.com/grafana/flint/blob/main/tasks/lint/links.sh#:~:text=build_remap_args) + + +## External Scroll to Text Fragment anchors — fragment stripped globally + +- [okhttp text fragment](https://github.com/square/okhttp/blob/master/README.md#:~:text=OkHttp) ## Section fragments (`#section`) — remapped to raw.githubusercontent.com