├── .editorconfig ├── .gitattributes ├── .github ├── funding.yml └── workflows │ ├── main.yml │ └── npm-publish.yml ├── .gitignore ├── .npmrc ├── __snapshots__ └── index.test.js.snap ├── index.d.ts ├── index.js ├── index.test-d.ts ├── index.test.js ├── license ├── package.json ├── readme.md └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [{package.json,*.yml}] 11 | indent_style = space 12 | indent_size = 2 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.github/funding.yml: -------------------------------------------------------------------------------- 1 | github: fregante 2 | custom: [paypal.me/fregante, www.buymeacoffee.com/fregante] 3 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | - pull_request 5 | - push 6 | 7 | jobs: 8 | Lint: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: actions/setup-node@v4 13 | with: 14 | node-version-file: package.json 15 | - run: npm install 16 | - run: npx xo 17 | 18 | Types: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v4 22 | - uses: actions/setup-node@v4 23 | with: 24 | node-version-file: package.json 25 | - run: npm install 26 | - run: npx tsd 27 | 28 | Test: 29 | runs-on: ubuntu-latest 30 | steps: 31 | - uses: actions/checkout@v4 32 | - uses: actions/setup-node@v4 33 | with: 34 | node-version-file: package.json 35 | - run: npm install 36 | - run: npx vitest 37 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | env: {} 2 | 3 | # FILE GENERATED WITH: npx ghat fregante/ghatemplates/npm-publish 4 | # SOURCE: https://github.com/fregante/ghatemplates 5 | 6 | # Collaborators can publish a new version of what's on master via "workflow_dispatch" 7 | # https://github.blog/changelog/2020-07-06-github-actions-manual-triggers-with-workflow_dispatch/ 8 | 9 | name: Publish 10 | 11 | on: 12 | workflow_dispatch: 13 | inputs: 14 | Version: 15 | description: 'Version accepted by `npm version *`' 16 | required: true 17 | 18 | jobs: 19 | NPM: 20 | runs-on: ubuntu-latest 21 | 22 | # https://docs.npmjs.com/generating-provenance-statements#example-github-actions-workflow 23 | permissions: 24 | contents: write 25 | id-token: write 26 | 27 | steps: 28 | - uses: actions/checkout@v4 29 | - uses: actions/setup-node@v4 30 | with: 31 | node-version-file: package.json 32 | registry-url: https://registry.npmjs.org 33 | - run: npm ci || npm install 34 | - uses: fregante/setup-git-user@v2 35 | - name: Create version 36 | # Get the generated version, this enables support for keywords: `npm version patch` 37 | run: | 38 | VERSION="$(npm version "${{ github.event.inputs.Version }}")" 39 | echo "VERSION=$VERSION" >> $GITHUB_ENV 40 | - run: npm publish --provenance 41 | env: 42 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 43 | - run: git push --follow-tags 44 | - run: gh release create "$VERSION" --generate-notes 45 | env: 46 | GH_TOKEN: ${{ github.token }} 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | Desktop.ini 4 | ._* 5 | Thumbs.db 6 | *.tmp 7 | *.bak 8 | *.log 9 | *.lock 10 | logs 11 | package-lock.json 12 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /__snapshots__/index.test.js.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`http://www.google.com/ 1`] = `http://www.google.com`; 4 | 5 | exports[`https://cdn.rawgit.com/fregante/shorten-repo-url/d71718db/.gitignore 1`] = `d71718db/.gitignore (raw)`; 6 | 7 | exports[`https://cdn.rawgit.com/fregante/shorten-repo-url/v0.12/.gitignore 1`] = `v0.12/.gitignore (raw)`; 8 | 9 | exports[`https://cdn.rawgit.com/nodejs/node/d71718db/.gitignore 1`] = `nodejs/node@d71718db/.gitignore (raw)`; 10 | 11 | exports[`https://cdn.rawgit.com/nodejs/node/v0.12/.gitignore 1`] = `nodejs/node@v0.12/.gitignore (raw)`; 12 | 13 | exports[`https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement#parameters 1`] = `developer.mozilla.org/en-US/docs/Web/API/Document/createElement#parameters`; 14 | 15 | exports[`https://example.com/nodejs/node/blob/cc8fc46/.gitignore 1`] = `example.com/nodejs/node/blob/cc8fc46/.gitignore`; 16 | 17 | exports[`https://example.site/한글로-된-URL 1`] = `example.site/한글로-된-URL`; 18 | 19 | exports[`https://github.com 1`] = `github.com`; 20 | 21 | exports[`https://github.com/ 1`] = `github.com`; 22 | 23 | exports[`https://github.com/bfred-it/shorten-repo-url/network/dependencies 1`] = `bfred-it/shorten-repo-url (dependencies)`; 24 | 25 | exports[`https://github.com/bfred-it/shorten-repo-url/network/dependents 1`] = `bfred-it/shorten-repo-url (dependents)`; 26 | 27 | exports[`https://github.com/bfred-it/shorten-repo-url/wiki 1`] = `bfred-it/shorten-repo-url/wiki`; 28 | 29 | exports[`https://github.com/features 1`] = `github.com/features`; 30 | 31 | exports[`https://github.com/fregante/shorten-repo-url/ 1`] = `fregante/shorten-repo-url`; 32 | 33 | exports[`https://github.com/fregante/shorten-repo-url/?tab=readme-ov-file 1`] = `fregante/shorten-repo-url`; 34 | 35 | exports[`https://github.com/fregante/shorten-repo-url/archive/6.4.1.zip 1`] = `6.4.1.zip`; 36 | 37 | exports[`https://github.com/fregante/shorten-repo-url/blame/cc8fc46/.gitignore 1`] = `cc8fc46/.gitignore (blame)`; 38 | 39 | exports[`https://github.com/fregante/shorten-repo-url/blame/master/.gitignore 1`] = `master/.gitignore (blame)`; 40 | 41 | exports[`https://github.com/fregante/shorten-repo-url/blame/v0.12/.gitignore 1`] = `v0.12/.gitignore (blame)`; 42 | 43 | exports[`https://github.com/fregante/shorten-repo-url/blob/cc8fc46/.gitignore 1`] = `cc8fc46/.gitignore`; 44 | 45 | exports[`https://github.com/fregante/shorten-repo-url/blob/main/한글.txt 1`] = `main/한글.txt`; 46 | 47 | exports[`https://github.com/fregante/shorten-repo-url/blob/master/.gitignore 1`] = `master/.gitignore`; 48 | 49 | exports[`https://github.com/fregante/shorten-repo-url/blob/v0.12/.gitignore 1`] = `v0.12/.gitignore`; 50 | 51 | exports[`https://github.com/fregante/shorten-repo-url/commit/cc8fc46.diff 1`] = `cc8fc46.diff`; 52 | 53 | exports[`https://github.com/fregante/shorten-repo-url/commit/cc8fc46.patch 1`] = `cc8fc46.patch`; 54 | 55 | exports[`https://github.com/fregante/shorten-repo-url/commits/cc8fc46/.gitignore 1`] = `cc8fc46/.gitignore (commits)`; 56 | 57 | exports[`https://github.com/fregante/shorten-repo-url/commits/master/.gitignore 1`] = `master/.gitignore (commits)`; 58 | 59 | exports[`https://github.com/fregante/shorten-repo-url/commits/v0.12/.gitignore 1`] = `v0.12/.gitignore (commits)`; 60 | 61 | exports[`https://github.com/fregante/shorten-repo-url/compare 1`] = `fregante/shorten-repo-url/compare`; 62 | 63 | exports[`https://github.com/fregante/shorten-repo-url/compare/d71718db6aa4feb8dc10edbad1134472468e971a 1`] = `d71718d (compare)`; 64 | 65 | exports[`https://github.com/fregante/shorten-repo-url/compare/master 1`] = `master (compare)`; 66 | 67 | exports[`https://github.com/fregante/shorten-repo-url/compare/master...master 1`] = `master...master (compare)`; 68 | 69 | exports[`https://github.com/fregante/shorten-repo-url/contributors 1`] = `fregante/shorten-repo-url/contributors`; 70 | 71 | exports[`https://github.com/fregante/shorten-repo-url/issues 1`] = `fregante/shorten-repo-url/issues`; 72 | 73 | exports[`https://github.com/fregante/shorten-repo-url/issues?q=is%3Aissue++is%3Aopen+sort%3Aupdated-desc+&unrelated=true 1`] = `fregante/shorten-repo-url/issues?unrelated=true (is:open sort:updated-desc)`; 74 | 75 | exports[`https://github.com/fregante/shorten-repo-url/issues?q=wow 1`] = `fregante/shorten-repo-url/issues (wow)`; 76 | 77 | exports[`https://github.com/fregante/shorten-repo-url/labels 1`] = `fregante/shorten-repo-url/labels`; 78 | 79 | exports[`https://github.com/fregante/shorten-repo-url/labels/npm 1`] = `npm (label)`; 80 | 81 | exports[`https://github.com/fregante/shorten-repo-url/milestone/25 1`] = `fregante/shorten-repo-url/milestone/25`; 82 | 83 | exports[`https://github.com/fregante/shorten-repo-url/milestones 1`] = `fregante/shorten-repo-url/milestones`; 84 | 85 | exports[`https://github.com/fregante/shorten-repo-url/network 1`] = `fregante/shorten-repo-url/network`; 86 | 87 | exports[`https://github.com/fregante/shorten-repo-url/projects 1`] = `fregante/shorten-repo-url/projects`; 88 | 89 | exports[`https://github.com/fregante/shorten-repo-url/pull/123/checks 1`] = `#123 (checks)`; 90 | 91 | exports[`https://github.com/fregante/shorten-repo-url/pull/123/commits 1`] = `#123 (commits)`; 92 | 93 | exports[`https://github.com/fregante/shorten-repo-url/pull/123/files 1`] = `#123 (files)`; 94 | 95 | exports[`https://github.com/fregante/shorten-repo-url/pulse 1`] = `fregante/shorten-repo-url/pulse`; 96 | 97 | exports[`https://github.com/fregante/shorten-repo-url/releases 1`] = `fregante/shorten-repo-url/releases`; 98 | 99 | exports[`https://github.com/fregante/shorten-repo-url/releases/download/6.4.1/now-macos 1`] = `6.4.1 now-macos (download)`; 100 | 101 | exports[`https://github.com/fregante/shorten-repo-url/releases/tag/v0.12.0 1`] = `v0.12.0 (release)`; 102 | 103 | exports[`https://github.com/fregante/shorten-repo-url/tree/d71718db6aa4feb8dc10edbad1134472468e971a 1`] = `d71718d`; 104 | 105 | exports[`https://github.com/fregante/shorten-repo-url/tree/d71718db6aa4feb8dc10edbad1134472468e971a/doc 1`] = `d71718d/doc`; 106 | 107 | exports[`https://github.com/fregante/shorten-repo-url/tree/master/doc 1`] = `master/doc`; 108 | 109 | exports[`https://github.com/fregante/shorten-repo-url/tree/v0.12 1`] = `v0.12`; 110 | 111 | exports[`https://github.com/fregante/shorten-repo-url/tree/v0.12/doc 1`] = `v0.12/doc`; 112 | 113 | exports[`https://github.com/fregante/shorten-repo-url/wiki/%22Can-you-add-this-feature%3F%22 1`] = `Wiki: "Can you add this feature?"`; 114 | 115 | exports[`https://github.com/issues 1`] = `github.com/issues`; 116 | 117 | exports[`https://github.com/issues?q=is%3Aissue++is%3Aopen+sort%3Aupdated-desc+&unrelated=true 1`] = `github.com/issues?unrelated=true (is:open sort:updated-desc)`; 118 | 119 | exports[`https://github.com/marketplace 1`] = `github.com/marketplace`; 120 | 121 | exports[`https://github.com/network/dependencies 1`] = `github.com/network/dependencies`; 122 | 123 | exports[`https://github.com/nodejs 1`] = `@nodejs`; 124 | 125 | exports[`https://github.com/nodejs/node/ 1`] = `nodejs/node`; 126 | 127 | exports[`https://github.com/nodejs/node/blame/cc8fc46/.gitignore 1`] = `nodejs/node@cc8fc46/.gitignore (blame)`; 128 | 129 | exports[`https://github.com/nodejs/node/blame/master/.gitignore 1`] = `nodejs/node@master/.gitignore (blame)`; 130 | 131 | exports[`https://github.com/nodejs/node/blame/v0.12/.gitignore 1`] = `nodejs/node@v0.12/.gitignore (blame)`; 132 | 133 | exports[`https://github.com/nodejs/node/blob/cc8fc46/.gitignore 1`] = `nodejs/node@cc8fc46/.gitignore`; 134 | 135 | exports[`https://github.com/nodejs/node/blob/master/.gitignore 1`] = `nodejs/node@master/.gitignore`; 136 | 137 | exports[`https://github.com/nodejs/node/blob/v0.12/.gitignore 1`] = `nodejs/node@v0.12/.gitignore`; 138 | 139 | exports[`https://github.com/nodejs/node/commit/cc8fc46.diff 1`] = `nodejs/node@cc8fc46.diff`; 140 | 141 | exports[`https://github.com/nodejs/node/commit/cc8fc46.patch 1`] = `nodejs/node@cc8fc46.patch`; 142 | 143 | exports[`https://github.com/nodejs/node/commits/cc8fc46/.gitignore 1`] = `nodejs/node@cc8fc46/.gitignore (commits)`; 144 | 145 | exports[`https://github.com/nodejs/node/commits/master/.gitignore 1`] = `nodejs/node@master/.gitignore (commits)`; 146 | 147 | exports[`https://github.com/nodejs/node/commits/v0.12/.gitignore 1`] = `nodejs/node@v0.12/.gitignore (commits)`; 148 | 149 | exports[`https://github.com/nodejs/node/compare 1`] = `nodejs/node/compare`; 150 | 151 | exports[`https://github.com/nodejs/node/compare/d71718db6aa4feb8dc10edbad1134472468e971a 1`] = `nodejs/node@d71718d (compare)`; 152 | 153 | exports[`https://github.com/nodejs/node/compare/master 1`] = `nodejs/node@master (compare)`; 154 | 155 | exports[`https://github.com/nodejs/node/compare/master...master 1`] = `nodejs/node@master...master (compare)`; 156 | 157 | exports[`https://github.com/nodejs/node/contributors 1`] = `nodejs/node/contributors`; 158 | 159 | exports[`https://github.com/nodejs/node/graphs/commit-activity 1`] = `nodejs/node/graphs/commit-activity`; 160 | 161 | exports[`https://github.com/nodejs/node/labels 1`] = `nodejs/node/labels`; 162 | 163 | exports[`https://github.com/nodejs/node/labels/Please%21%20♥ 1`] = `nodejs/node/Please! ♥ (label)`; 164 | 165 | exports[`https://github.com/nodejs/node/labels/npm 1`] = `nodejs/node/npm (label)`; 166 | 167 | exports[`https://github.com/nodejs/node/milestone/25 1`] = `nodejs/node/milestone/25`; 168 | 169 | exports[`https://github.com/nodejs/node/milestones 1`] = `nodejs/node/milestones`; 170 | 171 | exports[`https://github.com/nodejs/node/network 1`] = `nodejs/node/network`; 172 | 173 | exports[`https://github.com/nodejs/node/projects 1`] = `nodejs/node/projects`; 174 | 175 | exports[`https://github.com/nodejs/node/pull/123/files 1`] = `nodejs/node#123 (files)`; 176 | 177 | exports[`https://github.com/nodejs/node/pulse 1`] = `nodejs/node/pulse`; 178 | 179 | exports[`https://github.com/nodejs/node/releases 1`] = `nodejs/node/releases`; 180 | 181 | exports[`https://github.com/nodejs/node/releases/tag/v0.12.0 1`] = `nodejs/node@v0.12.0 (release)`; 182 | 183 | exports[`https://github.com/nodejs/node/tree/d71718db6aa4feb8dc10edbad1134472468e971a 1`] = `nodejs/node@d71718d`; 184 | 185 | exports[`https://github.com/nodejs/node/tree/d71718db6aa4feb8dc10edbad1134472468e971a/doc 1`] = `nodejs/node@d71718d/doc`; 186 | 187 | exports[`https://github.com/nodejs/node/tree/master/doc 1`] = `nodejs/node@master/doc`; 188 | 189 | exports[`https://github.com/nodejs/node/tree/v0.12 1`] = `nodejs/node@v0.12`; 190 | 191 | exports[`https://github.com/nodejs/node/tree/v0.12/doc 1`] = `nodejs/node@v0.12/doc`; 192 | 193 | exports[`https://github.com/nodejs/node/wiki 1`] = `nodejs/node/wiki`; 194 | 195 | exports[`https://github.com/nodejs/shorten-repo-url/ 1`] = `nodejs/shorten-repo-url`; 196 | 197 | exports[`https://github.com/pulls 1`] = `github.com/pulls`; 198 | 199 | exports[`https://github.com/pulls?q=is%3Apr++is%3Aopen+sort%3Aupdated-desc+&unrelated=true 1`] = `github.com/pulls?unrelated=true (is:open sort:updated-desc)`; 200 | 201 | exports[`https://github.com/refined-github/refined-github/labels/Please%21%20♥%EF%B8%8E 1`] = `refined-github/refined-github/Please! ♥︎ (label)`; 202 | 203 | exports[`https://github.com/refined-github/refined-github/wiki/%22Can-you-add-this-feature%3F%22 1`] = `Wiki: "Can you add this feature?" (refined-github/refined-github)`; 204 | 205 | exports[`https://github.com/refined-github/refined-github/wiki/%22Can-you-add-this-feature%3F%22# 1`] = `Wiki: "Can you add this feature?" (refined-github/refined-github)`; 206 | 207 | exports[`https://github.com/refined-github/refined-github/wiki/%22Can-you-add-this-feature%3F%22#3-it-doesnt-require-options 1`] = `Wiki: "Can you add this feature?" (3 it doesnt require options) (refined-github/refined-github)`; 208 | 209 | exports[`https://github.com/scarf005/hangul-test/wiki/한글-위키-페이지 1`] = `Wiki: 한글 위키 페이지 (scarf005/hangul-test)`; 210 | 211 | exports[`https://github.com/scarf005/hangul-test/wiki/한글-위키-페이지#한글-헤딩 1`] = `Wiki: 한글 위키 페이지 (한글 헤딩) (scarf005/hangul-test)`; 212 | 213 | exports[`https://github.com/settings/profile 1`] = `github.com/settings/profile`; 214 | 215 | exports[`https://github.com/sindresorhus 1`] = `@sindresorhus`; 216 | 217 | exports[`https://github.com/sindresorhus/notifier-for-github/pull/253/files/6b4489d417c9425dc27c5fb8d6b4a8518debd035..60cdcf3c3646164441bf8f037cef620479cdec59 1`] = `6b4489d4..60cdcf3c (#253)`; 218 | 219 | exports[`https://github.com/trending 1`] = `github.com/trending`; 220 | 221 | exports[`https://github.com/trending/developers 1`] = `github.com/trending/developers`; 222 | 223 | exports[`https://github.com/zeit/now-cli/archive/6.4.1.zip 1`] = `zeit/now-cli@6.4.1.zip`; 224 | 225 | exports[`https://github.com/zeit/now-cli/releases/download/6.4.1/now-macos 1`] = `zeit/now-cli@6.4.1 now-macos (download)`; 226 | 227 | exports[`https://raw.githubusercontent.com/fregante/shorten-repo-url/d71718db/.gitignore 1`] = `d71718db/.gitignore (raw)`; 228 | 229 | exports[`https://raw.githubusercontent.com/fregante/shorten-repo-url/master/.gitignore 1`] = `master/.gitignore (raw)`; 230 | 231 | exports[`https://raw.githubusercontent.com/fregante/shorten-repo-url/v0.12/.gitignore 1`] = `v0.12/.gitignore (raw)`; 232 | 233 | exports[`https://raw.githubusercontent.com/nodejs/node/d71718db/.gitignore 1`] = `nodejs/node@d71718db/.gitignore (raw)`; 234 | 235 | exports[`https://raw.githubusercontent.com/nodejs/node/master/.gitignore 1`] = `nodejs/node@master/.gitignore (raw)`; 236 | 237 | exports[`https://raw.githubusercontent.com/nodejs/node/v0.12/.gitignore 1`] = `nodejs/node@v0.12/.gitignore (raw)`; 238 | 239 | exports[`https://rawgit.com/fregante/shorten-repo-url/master/.gitignore 1`] = `master/.gitignore (raw)`; 240 | 241 | exports[`https://rawgit.com/nodejs/node/master/.gitignore 1`] = `nodejs/node@master/.gitignore (raw)`; 242 | 243 | exports[`https://togithub.com/fregante/shorten-repo-url/commit/98c6175b0cbd4caca71d24e68e57b942b0dfb549 1`] = `98c6175`; 244 | 245 | exports[`https://togithub.com/fregante/shorten-repo-url/issues/25 1`] = `#25`; 246 | 247 | exports[`https://togithub.com/fregante/shorten-repo-url/issues/28#issue-850900171 1`] = `#28 (comment)`; 248 | 249 | exports[`https://togithub.com/fregante/shorten-repo-url/pull/32 1`] = `#32`; 250 | 251 | exports[`https://togithub.com/fregante/shorten-repo-url/pull/32/files 1`] = `#32 (files)`; 252 | 253 | exports[`https://togithub.com/fregante/shorten-repo-url/pull/33#discussion_r750069394 1`] = `#33 (review)`; 254 | 255 | exports[`https://togithub.com/fregante/shorten-repo-url/pull/33#pullrequestreview-801229042 1`] = `#33 (review)`; 256 | 257 | exports[`https://togithub.com/nodejs/node/pull/123 1`] = `nodejs/node#123`; 258 | 259 | exports[`https://togithub.com/nodejs/node/pull/123/files 1`] = `nodejs/node#123 (files)`; 260 | 261 | exports[`https://togithub.com/refined-github/refined-github/commit/4f270c4f50e0a2a20085a6e92095117f10340322 1`] = `refined-github/refined-github@4f270c4`; 262 | 263 | exports[`https://togithub.com/refined-github/refined-github/commit/e81a9646b448d90c7e02ab41332cab0507dccbbd#commitcomment-60089354 1`] = `refined-github/refined-github@e81a964 (comment)`; 264 | 265 | exports[`https://www.google.com/ 1`] = `google.com`; 266 | 267 | exports[`https://www.npmjs.com/ 1`] = `npmjs.com`; 268 | 269 | exports[`https://www.npmjs.com/packaasdge/node 1`] = `npmjs.com/packaasdge/node`; 270 | 271 | exports[`https://wwww.google.com/ 1`] = `wwww.google.com`; 272 | 273 | exports[`https://한글로-된-경로.com/하위경로#한글-해시 1`] = `한글로-된-경로.com/하위경로#한글-해시`; 274 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Shortens a GitHub URL string. 3 | 4 | @param anchor An HTMLAnchorElement 5 | @param url The GitHub URL to shorten. 6 | @returns The shortened URL in HTML as a string 7 | @example https://github.com/nodejs/node/tree/v0.12/doc becomes nodejs/node@v0.12 8 | 9 | */ 10 | export default function shortenRepoUrl(url: string, currentUrl?: string): string; 11 | 12 | /** 13 | Shortens a GitHub URL in a DOM anchor if the link label is not customized (i.e. if the `href` matches the `textContent`) 14 | 15 | @param anchor An HTMLAnchorElement 16 | @param url The GitHub URL to shorten. 17 | @examplehttps://github.com becomes github.com 18 | 19 | */ 20 | export function applyToLink(anchor: HTMLAnchorElement, url?: string): void; 21 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import reservedNames from 'github-reserved-names/reserved-names.json' with { type: 'json' }; 2 | 3 | const patchDiffRegex = /[.](patch|diff)$/; 4 | const releaseRegex = /^releases[/]tag[/]([^/]+)/; 5 | const labelRegex = /^labels[/]([^/]+)/; 6 | const compareRegex = /^compare[/]([^/]+)/; 7 | const pullRegex = /^pull[/](?\d+)(?:[/](?[^/]+))?(?:[/](?[\da-f]{40})[.][.](?[\da-f]{40}))?$/; 8 | const issueRegex = /^issues[/](\d+)$/; 9 | const commitRegex = /^commit[/]([\da-f]{40})$/; 10 | const releaseArchiveRegex = /^archive[/](.+)([.]zip|[.]tar[.]gz)/; 11 | const releaseDownloadRegex = /^releases[/]download[/]([^/]+)[/](.+)/; 12 | const dependentsRegex = /^network[/]dependents[/]?$/; 13 | const dependenciesRegex = /^network[/]dependencies[/]?$/; 14 | const wikiRegex = /^wiki[/](.+)$/; 15 | 16 | /** @type {(searchParameters: URLSearchParams, pathname: string) => string} */ 17 | function pullQueryOut(searchParameters, pathname) { 18 | let query = searchParameters.get('q'); 19 | 20 | if (!query) { 21 | return ''; 22 | } 23 | 24 | searchParameters.delete('q'); 25 | if (pathname.endsWith('/issues')) { 26 | query = query.replace('is:issue', ''); 27 | } 28 | 29 | if (pathname.endsWith('/pulls')) { 30 | query = query.replace('is:pr', ''); 31 | } 32 | 33 | return ` (${query.replaceAll(/\s+/g, ' ').trim()})`; 34 | } 35 | 36 | /** @param revision {string} */ 37 | function styleRevision(revision) { 38 | if (!revision) { 39 | return; 40 | } 41 | 42 | revision = revision.replace(patchDiffRegex, ''); 43 | if (/^[0-9a-f]{40}$/.test(revision)) { 44 | revision = revision.slice(0, 7); 45 | } 46 | 47 | return `${revision}`; 48 | } 49 | 50 | /** @param hash {string} */ 51 | function commentIndicator(hash) { 52 | if (hash.startsWith('#issue-') || hash.startsWith('#commitcomment-')) { 53 | return ' (comment)'; 54 | } 55 | 56 | if (hash.startsWith('#pullrequestreview-') || hash.startsWith('#discussion_r')) { 57 | return ' (review)'; 58 | } 59 | 60 | return ''; 61 | } 62 | 63 | /** 64 | * Filter out null values 65 | * @type {(array: string[], delimiter?: string) => string} 66 | */ 67 | function joinValues(array, delimiter = '/') { 68 | return array.filter(Boolean).join(delimiter); 69 | } 70 | 71 | /** 72 | * @param href {string} 73 | * @param currentUrl {string} 74 | */ 75 | function shortenRepoUrl(href, currentUrl = 'https://github.com') { 76 | if (!href) { 77 | return; 78 | } 79 | 80 | currentUrl = new URL(currentUrl); 81 | const currentRepo = currentUrl.pathname.slice(1).split('/', 2).join('/'); 82 | 83 | /** 84 | * Parse URL manually to avoid URL encoding and punycode 85 | */ 86 | const origin = href.split('/', 3).join('/'); 87 | const pathname = href.slice(origin.length).replace(/[?#].*/, '') || '/'; 88 | const hash = /#.+$/.exec(href)?.[0] ?? ''; 89 | 90 | // Use URL exclusively for search parameters because they're too hard to parse 91 | const url = new URL(href); 92 | const {search, searchParams} = url; 93 | 94 | const pathnameParts = pathname.slice(1).split('/'); // ['user', 'repo', 'pull', '342'] 95 | const repoPath = pathnameParts.slice(2).join('/'); // 'pull/342' 96 | 97 | const isRaw = [ 98 | 'https://raw.githubusercontent.com', 99 | 'https://cdn.rawgit.com', 100 | 'https://rawgit.com', 101 | ].includes(origin); 102 | 103 | const isRedirection = [ 104 | 'https://togithub.com', // Renovate 105 | 'https://github-redirect.dependabot.com', // Dependabot 106 | 'https://redirect.github.com', // Dependabot 107 | ].includes(origin); 108 | 109 | let [ 110 | user, 111 | repo, 112 | type, 113 | revision, 114 | ...filePath 115 | ] = pathnameParts; 116 | 117 | if (isRaw) { 118 | [ 119 | user, 120 | repo, 121 | // Raw URLs don't have `blob` here 122 | revision, 123 | ...filePath 124 | ] = pathnameParts; 125 | type = 'raw'; 126 | } 127 | 128 | revision = styleRevision(revision); 129 | filePath = filePath.join('/'); 130 | 131 | const isLocal = origin === currentUrl.origin; 132 | const isThisRepo = (isLocal || isRaw || isRedirection) && currentRepo === `${user}/${repo}`; 133 | const isReserved = reservedNames.includes(user); 134 | const isDependents = dependentsRegex.test(repoPath); 135 | const isDependencies = dependenciesRegex.test(repoPath); 136 | const [, diffOrPatch] = repoPath.match(patchDiffRegex) || []; 137 | const [, release] = repoPath.match(releaseRegex) || []; 138 | const [, releaseTag, releaseTagExtension] = repoPath.match(releaseArchiveRegex) || []; 139 | const [, downloadTag, downloadFilename] = repoPath.match(releaseDownloadRegex) || []; 140 | const [, label] = repoPath.match(labelRegex) || []; 141 | const [, compare] = repoPath.match(compareRegex) || []; 142 | const {pull, pullPage, pullPartialStart, pullPartialEnd} = repoPath.match(pullRegex)?.groups ?? {}; 143 | const [, issue] = isRedirection ? repoPath.match(issueRegex) || [] : []; 144 | const [, commit] = isRedirection ? repoPath.match(commitRegex) || [] : []; 145 | const [, wiki] = repoPath.match(wikiRegex) || []; 146 | const isFileOrDirectory = revision && [ 147 | 'raw', 148 | 'tree', 149 | 'blob', 150 | 'blame', 151 | 'commits', 152 | ].includes(type); 153 | 154 | const repoUrl = isThisRepo ? '' : `${user}/${repo}`; 155 | 156 | /** 157 | * Shorten URL 158 | */ 159 | if (isReserved || pathname === '/' || (!isLocal && !isRaw && !isRedirection)) { 160 | const cleanHref = [ 161 | origin 162 | .replace(/^https:[/][/]/, '') 163 | .replace(/^www[.]/, ''), 164 | pathname 165 | .replace(/[/]$/, ''), 166 | ]; 167 | 168 | if (['issues', 'pulls'].includes(user) && !repo) { 169 | const query = pullQueryOut(url.searchParams, url.pathname); 170 | cleanHref.push(url.search, query); 171 | } else { 172 | cleanHref.push(url.search); 173 | } 174 | 175 | cleanHref.push(decodeURI(url.hash)); 176 | 177 | return cleanHref.join(''); 178 | } 179 | 180 | if (user && !repo) { 181 | return `@${user}${search}${hash}`; 182 | } 183 | 184 | if (isFileOrDirectory) { 185 | const revisioned = joinValues( 186 | [joinValues([repoUrl, revision], '@'), filePath], 187 | '/', 188 | ); 189 | const partial = `${revisioned}${search}${hash}`; 190 | if (type !== 'blob' && type !== 'tree') { 191 | return `${partial} (${type})`; 192 | } 193 | 194 | return partial; 195 | } 196 | 197 | if (diffOrPatch) { 198 | const partial = joinValues([repoUrl, revision], '@'); 199 | return `${partial}.${diffOrPatch}${search}${hash}`; 200 | } 201 | 202 | if (release) { 203 | const partial = joinValues([repoUrl, `${release}`], '@'); 204 | return `${partial}${search}${hash} (release)`; 205 | } 206 | 207 | if (releaseTagExtension) { 208 | const partial = joinValues([repoUrl, `${releaseTag}`], '@'); 209 | return `${partial}${releaseTagExtension}${search}${hash}`; 210 | } 211 | 212 | if (downloadFilename) { 213 | const partial = joinValues([repoUrl, `${downloadTag}`], '@'); 214 | return `${partial} ${downloadFilename}${search}${hash} (download)`; 215 | } 216 | 217 | if (label) { 218 | return ( 219 | joinValues([repoUrl, decodeURIComponent(label)]) 220 | + `${search}${hash} (label)` 221 | ); 222 | } 223 | 224 | if (isDependents) { 225 | return `${user}/${repo} (dependents)`; 226 | } 227 | 228 | if (isDependencies) { 229 | return `${user}/${repo} (dependencies)`; 230 | } 231 | 232 | if (pull) { 233 | if (pullPage === 'files' && pullPartialStart && pullPartialEnd) { 234 | return `${pullPartialStart.slice(0, 8)}..${pullPartialEnd.slice(0, 8)} (#${pull})`; 235 | } 236 | 237 | if (pullPage) { 238 | return `${repoUrl}#${pull} (${pullPage})`; 239 | } 240 | } 241 | 242 | if (compare) { 243 | const partial = joinValues([repoUrl, revision], '@'); 244 | return `${partial}${search}${hash} (compare)`; 245 | } 246 | 247 | if (wiki) { 248 | const hashPart = (hash ? ' (' + hash.slice(1) + ')' : ''); 249 | const cleanLabel = (wiki + hashPart).replaceAll('-', ' '); 250 | const repoPart = repoUrl ? ` (${repoUrl})` : ''; 251 | return `Wiki: ${decodeURIComponent(cleanLabel)}${repoPart}`; 252 | } 253 | 254 | // Shorten URLs that would otherwise be natively shortened 255 | if (isRedirection) { 256 | if (issue) { 257 | return `${repoUrl}#${issue}${commentIndicator(hash)}`; 258 | } 259 | 260 | if (pull) { 261 | return `${repoUrl}#${pull}${commentIndicator(hash)}`; 262 | } 263 | 264 | if (commit) { 265 | return joinValues([repoUrl, `${commit.slice(0, 7)}`], '@') + commentIndicator(hash); 266 | } 267 | } 268 | 269 | const query = pullQueryOut(searchParams, pathname); 270 | 271 | if (searchParams.get('tab') === 'readme-ov-file') { 272 | searchParams.delete('tab'); 273 | } 274 | 275 | // Drop leading and trailing slash of relative path 276 | return pathname.replaceAll(/^[/]|[/]$/g, '') + url.search + hash + query; 277 | } 278 | 279 | /** 280 | * Without this, %% would throw an error 281 | * @type {(url: string) => string} 282 | */ 283 | function safeDecode(url) { 284 | try { 285 | return new URL(url).href; 286 | } catch { 287 | return url; 288 | } 289 | } 290 | 291 | /** @param a {HTMLAnchorElement} */ 292 | function getLinkHref(a) { 293 | return a.dataset.originalHref ?? a.href; 294 | } 295 | 296 | /** @param a {HTMLAnchorElement} */ 297 | function isCustomLink(a) { 298 | const url = safeDecode(getLinkHref(a)); 299 | const label = safeDecode(a.textContent); 300 | return ( 301 | // `trim` makes it compatible with this feature: https://github.com/sindresorhus/refined-github/pull/3085 302 | url !== label.trim() 303 | // .href automatically adds a / to naked origins so that needs to be tested too 304 | && url !== `${label}/` 305 | ); 306 | } 307 | 308 | /** @type {(a: HTMLAnchorElement, currentUrl: string) => boolean} */ 309 | export function applyToLink(a, currentUrl) { 310 | // `safeDecode` is needed because some URLs are encoded in different ways in the DOM and in the `href` property: https://github.com/refined-github/shorten-repo-url/issues/19 311 | 312 | if ( 313 | // Shorten only if the link name hasn't been customized 314 | !isCustomLink(a) 315 | // And if there are no additional images in the link 316 | && !a.firstElementChild 317 | ) { 318 | const url = a.textContent; 319 | const shortened = shortenRepoUrl(url, currentUrl); 320 | a.replaceChildren( 321 | ...shortened.split( 322 | /([^<]+)<\/code>/g, 323 | ).map((part, i) => { 324 | if (i % 2 === 0) { 325 | return part; 326 | } 327 | 328 | const codeElement = document.createElement('code'); 329 | codeElement.textContent = part; 330 | return codeElement; 331 | }), 332 | ); 333 | return true; 334 | } 335 | 336 | return false; 337 | } 338 | 339 | export default shortenRepoUrl; 340 | -------------------------------------------------------------------------------- /index.test-d.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import {applyToLink} from './index.js'; 3 | 4 | expectType( 5 | applyToLink(document.createElement('a'), 'https://bettersite.com'), 6 | ); 7 | -------------------------------------------------------------------------------- /index.test.js: -------------------------------------------------------------------------------- 1 | import {test, expect} from 'vitest'; 2 | import {Window} from 'happy-dom'; 3 | import shortenUrl, {applyToLink} from './index.js'; 4 | 5 | const currentLocation = 'https://github.com/fregante/shorten-repo-url/issue/1'; 6 | globalThis.document = new Window({url: currentLocation}).document; 7 | 8 | expect.addSnapshotSerializer({ 9 | serialize(value) { 10 | return value; 11 | }, 12 | test(value) { 13 | return value; 14 | }, 15 | }); 16 | 17 | const urls = [ 18 | 'https://github.com/fregante/shorten-repo-url/', 19 | 'https://github.com/fregante/shorten-repo-url/?tab=readme-ov-file', 20 | 'https://github.com/fregante/shorten-repo-url/tree/v0.12', 21 | 'https://github.com/fregante/shorten-repo-url/tree/d71718db6aa4feb8dc10edbad1134472468e971a', 22 | 'https://github.com/nodejs/node/', 23 | 'https://github.com/nodejs/shorten-repo-url/', 24 | 'https://github.com/nodejs/node/tree/v0.12', 25 | 'https://github.com/nodejs/node/tree/d71718db6aa4feb8dc10edbad1134472468e971a', 26 | 'https://github.com/fregante/shorten-repo-url/tree/master/doc', 27 | 'https://github.com/fregante/shorten-repo-url/tree/v0.12/doc', 28 | 'https://github.com/fregante/shorten-repo-url/tree/d71718db6aa4feb8dc10edbad1134472468e971a/doc', 29 | 'https://github.com/nodejs/node/tree/master/doc', 30 | 'https://github.com/nodejs/node/tree/v0.12/doc', 31 | 'https://github.com/nodejs/node/tree/d71718db6aa4feb8dc10edbad1134472468e971a/doc', 32 | 'https://github.com/fregante/shorten-repo-url/blob/master/.gitignore', 33 | 'https://github.com/fregante/shorten-repo-url/blob/v0.12/.gitignore', 34 | 'https://github.com/fregante/shorten-repo-url/blob/cc8fc46/.gitignore', 35 | 'https://github.com/fregante/shorten-repo-url/blob/main/한글.txt', 36 | 'https://github.com/nodejs/node/blob/master/.gitignore', 37 | 'https://github.com/nodejs/node/blob/v0.12/.gitignore', 38 | 'https://github.com/nodejs/node/blob/cc8fc46/.gitignore', 39 | 'https://github.com/fregante/shorten-repo-url/blame/master/.gitignore', 40 | 'https://github.com/fregante/shorten-repo-url/blame/v0.12/.gitignore', 41 | 'https://github.com/fregante/shorten-repo-url/blame/cc8fc46/.gitignore', 42 | 'https://github.com/nodejs/node/blame/master/.gitignore', 43 | 'https://github.com/nodejs/node/blame/v0.12/.gitignore', 44 | 'https://github.com/nodejs/node/blame/cc8fc46/.gitignore', 45 | 'https://github.com/fregante/shorten-repo-url/commits/master/.gitignore', 46 | 'https://github.com/fregante/shorten-repo-url/commits/v0.12/.gitignore', 47 | 'https://github.com/fregante/shorten-repo-url/commits/cc8fc46/.gitignore', 48 | 'https://github.com/nodejs/node/commits/master/.gitignore', 49 | 'https://github.com/nodejs/node/commits/v0.12/.gitignore', 50 | 'https://github.com/nodejs/node/commits/cc8fc46/.gitignore', 51 | 'https://github.com/fregante/shorten-repo-url/commit/cc8fc46.diff', 52 | 'https://github.com/fregante/shorten-repo-url/commit/cc8fc46.patch', 53 | 'https://github.com/nodejs/node/commit/cc8fc46.diff', 54 | 'https://github.com/nodejs/node/commit/cc8fc46.patch', 55 | 'https://github.com/fregante/shorten-repo-url/releases/tag/v0.12.0', 56 | 'https://github.com/nodejs/node/releases/tag/v0.12.0', 57 | 'https://github.com/fregante/shorten-repo-url/milestone/25', 58 | 'https://github.com/fregante/shorten-repo-url/compare/d71718db6aa4feb8dc10edbad1134472468e971a', 59 | 'https://github.com/fregante/shorten-repo-url/compare/master', 60 | 'https://github.com/fregante/shorten-repo-url/compare/master...master', 61 | 'https://github.com/nodejs/node/compare/d71718db6aa4feb8dc10edbad1134472468e971a', 62 | 'https://github.com/nodejs/node/compare/master', 63 | 'https://github.com/nodejs/node/compare/master...master', 64 | 'https://github.com/nodejs/node/milestone/25', 65 | 'https://github.com/fregante/shorten-repo-url/labels/npm', 66 | 'https://github.com/nodejs/node/labels/npm', 67 | 'https://github.com/nodejs/node/labels/Please%21%20♥', 68 | 'https://github.com/refined-github/refined-github/labels/Please%21%20♥%EF%B8%8E', 69 | 'https://github.com/fregante/shorten-repo-url/archive/6.4.1.zip', 70 | 'https://github.com/fregante/shorten-repo-url/releases/download/6.4.1/now-macos', 71 | 'https://github.com/zeit/now-cli/archive/6.4.1.zip', 72 | 'https://github.com/zeit/now-cli/releases/download/6.4.1/now-macos', 73 | 'https://github.com/bfred-it/shorten-repo-url/network/dependents', 74 | 'https://github.com/bfred-it/shorten-repo-url/network/dependencies', 75 | 'https://github.com/network/dependencies', 76 | 'https://github.com/bfred-it/shorten-repo-url/wiki', 77 | 'https://github.com/fregante/shorten-repo-url/pulse', 78 | 'https://github.com/fregante/shorten-repo-url/labels', 79 | 'https://github.com/fregante/shorten-repo-url/compare', 80 | 'https://github.com/fregante/shorten-repo-url/network', 81 | 'https://github.com/fregante/shorten-repo-url/projects', 82 | 'https://github.com/fregante/shorten-repo-url/releases', 83 | 'https://github.com/fregante/shorten-repo-url/milestones', 84 | 'https://github.com/fregante/shorten-repo-url/contributors', 85 | 'https://github.com/fregante/shorten-repo-url/pull/123/files', 86 | 'https://github.com/nodejs/node/pull/123/files', 87 | 'https://github.com/fregante/shorten-repo-url/pull/123/commits', 88 | 'https://github.com/fregante/shorten-repo-url/pull/123/checks', 89 | 'https://github.com/nodejs/node/wiki', 90 | 'https://github.com/nodejs/node/pulse', 91 | 'https://github.com/nodejs/node/labels', 92 | 'https://github.com/nodejs/node/compare', 93 | 'https://github.com/nodejs/node/network', 94 | 'https://github.com/nodejs/node/projects', 95 | 'https://github.com/nodejs/node/releases', 96 | 'https://github.com/nodejs/node/milestones', 97 | 'https://github.com/nodejs/node/contributors', 98 | 'https://github.com/nodejs/node/graphs/commit-activity', 99 | 'https://rawgit.com/fregante/shorten-repo-url/master/.gitignore', 100 | 'https://cdn.rawgit.com/fregante/shorten-repo-url/v0.12/.gitignore', 101 | 'https://cdn.rawgit.com/fregante/shorten-repo-url/d71718db/.gitignore', 102 | 'https://raw.githubusercontent.com/fregante/shorten-repo-url/master/.gitignore', 103 | 'https://raw.githubusercontent.com/fregante/shorten-repo-url/v0.12/.gitignore', 104 | 'https://raw.githubusercontent.com/fregante/shorten-repo-url/d71718db/.gitignore', 105 | 'https://rawgit.com/nodejs/node/master/.gitignore', 106 | 'https://cdn.rawgit.com/nodejs/node/v0.12/.gitignore', 107 | 'https://cdn.rawgit.com/nodejs/node/d71718db/.gitignore', 108 | 'https://raw.githubusercontent.com/nodejs/node/master/.gitignore', 109 | 'https://raw.githubusercontent.com/nodejs/node/v0.12/.gitignore', 110 | 'https://raw.githubusercontent.com/nodejs/node/d71718db/.gitignore', 111 | 'https://github.com/sindresorhus', 112 | 'https://github.com/nodejs', 113 | 'https://github.com/pulls', 114 | 'https://github.com/issues', 115 | 'https://github.com/trending', 116 | 'https://github.com/features', 117 | 'https://github.com/marketplace', 118 | 'https://github.com/trending/developers', 119 | 'https://github.com/settings/profile', 120 | 'https://github.com/', 121 | 'https://github.com', 122 | 'https://github.com/fregante/shorten-repo-url/issues', 123 | 'https://github.com/fregante/shorten-repo-url/issues?q=wow', 124 | 'https://github.com/fregante/shorten-repo-url/issues?q=is%3Aissue++is%3Aopen+sort%3Aupdated-desc+&unrelated=true', 125 | 'https://github.com/issues?q=is%3Aissue++is%3Aopen+sort%3Aupdated-desc+&unrelated=true', 126 | 'https://github.com/pulls?q=is%3Apr++is%3Aopen+sort%3Aupdated-desc+&unrelated=true', 127 | 'https://github.com/sindresorhus/notifier-for-github/pull/253/files/6b4489d417c9425dc27c5fb8d6b4a8518debd035..60cdcf3c3646164441bf8f037cef620479cdec59', 128 | 'https://togithub.com/fregante/shorten-repo-url/issues/25', 129 | 'https://togithub.com/fregante/shorten-repo-url/issues/28#issue-850900171', 130 | 'https://togithub.com/fregante/shorten-repo-url/pull/32', 131 | 'https://togithub.com/fregante/shorten-repo-url/pull/32/files', 132 | 'https://togithub.com/fregante/shorten-repo-url/pull/33#pullrequestreview-801229042', 133 | 'https://togithub.com/fregante/shorten-repo-url/pull/33#discussion_r750069394', 134 | 'https://togithub.com/nodejs/node/pull/123', 135 | 'https://togithub.com/nodejs/node/pull/123/files', 136 | 'https://togithub.com/fregante/shorten-repo-url/commit/98c6175b0cbd4caca71d24e68e57b942b0dfb549', 137 | 'https://togithub.com/refined-github/refined-github/commit/4f270c4f50e0a2a20085a6e92095117f10340322', 138 | 'https://togithub.com/refined-github/refined-github/commit/e81a9646b448d90c7e02ab41332cab0507dccbbd#commitcomment-60089354', 139 | 'https://github.com/refined-github/refined-github/wiki/%22Can-you-add-this-feature%3F%22#3-it-doesnt-require-options', 140 | 'https://github.com/refined-github/refined-github/wiki/%22Can-you-add-this-feature%3F%22#', 141 | 'https://github.com/refined-github/refined-github/wiki/%22Can-you-add-this-feature%3F%22', 142 | 'https://github.com/fregante/shorten-repo-url/wiki/%22Can-you-add-this-feature%3F%22', 143 | 'https://github.com/scarf005/hangul-test/wiki/한글-위키-페이지', 144 | 'https://github.com/scarf005/hangul-test/wiki/한글-위키-페이지#한글-헤딩', 145 | 'https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement#parameters', 146 | 'https://www.google.com/', 147 | 'https://wwww.google.com/', 148 | 'http://www.google.com/', 149 | 'https://www.npmjs.com/', 150 | 'https://www.npmjs.com/packaasdge/node', 151 | 'https://example.com/nodejs/node/blob/cc8fc46/.gitignore', 152 | 'https://example.site/한글로-된-URL', 153 | 'https://한글로-된-경로.com/하위경로#한글-해시', 154 | ]; 155 | 156 | test.each(urls)('%s', url => { 157 | const shortened = shortenUrl(url, currentLocation); 158 | expect(shortened).toMatchSnapshot(); 159 | 160 | const a = document.createElement('a'); 161 | a.href = url; 162 | 163 | // The URL might contain CJK characters, but after it's passed to a.href it's encoded 164 | const correctlyEncodedUrl = a.href; 165 | 166 | a.textContent = url; 167 | applyToLink(a, currentLocation); 168 | expect(a.href, 'applyToLink should not alter the href').toBe(correctlyEncodedUrl); 169 | expect(a.innerHTML).toBe(shortened); 170 | }); 171 | 172 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Federico Brigante (https://fregante.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shorten-repo-url", 3 | "version": "5.2.1", 4 | "description": "Shorten GitHub URLs like GitHub shortens Issues and Commit URLs", 5 | "keywords": [ 6 | "commit", 7 | "file", 8 | "github", 9 | "gitlab", 10 | "issue", 11 | "link", 12 | "links", 13 | "ref", 14 | "reference", 15 | "shorter", 16 | "url" 17 | ], 18 | "repository": "refined-github/shorten-repo-url", 19 | "funding": "https://github.com/sponsors/fregante", 20 | "license": "MIT", 21 | "author": "Federico Brigante (https://fregante.com)", 22 | "type": "module", 23 | "exports": "./index.js", 24 | "main": "./index.js", 25 | "types": "./index.d.ts", 26 | "files": [ 27 | "index.js", 28 | "index.d.ts" 29 | ], 30 | "scripts": { 31 | "test": "xo && tsd && vitest run" 32 | }, 33 | "xo": { 34 | "parser": "@typescript-eslint/parser", 35 | "globals": [ 36 | "document" 37 | ], 38 | "rules": { 39 | "unicorn/better-regex": "off", 40 | "unicorn/prefer-module": "off", 41 | "complexity": "off" 42 | } 43 | }, 44 | "dependencies": { 45 | "github-reserved-names": "^2.0.5" 46 | }, 47 | "devDependencies": { 48 | "@sindresorhus/tsconfig": "^5.0.0", 49 | "happy-dom": "^15.0.0", 50 | "tsd": "^0.31.1", 51 | "vitest": "^2.0.5", 52 | "xo": "^0.58.0" 53 | }, 54 | "engines": { 55 | "node": ">=20.10" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # shorten-repo-url 2 | 3 | > Shorten GitHub links like GitHub shortens Issues and Commit links. Used on [refined-github](https://github.com/refined-github/refined-github) 4 | 5 | ![Demo](https://user-images.githubusercontent.com/1402241/27252232-8fdf8ed0-538b-11e7-8f19-12d317c9cd32.png) 6 | 7 | Look at [the tests](https://github.com/refined-github/shorten-repo-url/blob/main/index.test.js) to see what each URL is shortened to. GitLab URLs are mostly compatible but they're not officially supported. 8 | 9 | It works on any domain, so GitHub Enterprise is also supported. 10 | 11 | ## Install 12 | 13 | ``` 14 | $ npm install shorten-repo-url 15 | ``` 16 | 17 | ## Usage 18 | 19 | ```js 20 | const shortenRepoUrl = require('shorten-repo-url'); 21 | 22 | const HTML = shortenRepoUrl( 23 | 'https://github.com/nodejs/node/tree/v0.12/doc', 24 | 'https://github.com/nodejs/node' // same repo 25 | ); 26 | //=> 'v0.12' // repo-less URL 27 | 28 | const HTML = shortenRepoUrl( 29 | 'https://github.com/nodejs/node/tree/v0.12/doc', 30 | 'https://github.com' // not the same repo 31 | ); 32 | //=> 'nodejs/node@v0.12' // URL with repo 33 | ``` 34 | 35 | ## API 36 | 37 | ### shortenRepoUrl(url, currentUrl) 38 | 39 | Returns the shortened URL in HTML as a `string` like `nodejs/node@v0.12`. 40 | 41 | #### url 42 | 43 | Type: `string` 44 | 45 | The GitHub URL to shorten. 46 | 47 | #### currentUrl 48 | 49 | Type: `string`, like `location.href` 50 | 51 | The URL of the current page, to build relative URLs like `v0.12` instead of the longer `nodejs/node@v0.12` 52 | 53 | ### shortenRepoUrl.applyToLink(link, currentUrl) 54 | 55 | Automatically shorten the link's text if the text matches the URL, i.e. `https://github.com`. If a `data-original-href` attribute is present, it will be used when comparing the link’s text and when generating the shortened URL. 56 | 57 | Note: this function will never change the `href` of the link, it only changes the text. 58 | 59 | It will return `true` or `false` depending on whether the link was shortened. 60 | 61 | #### link 62 | 63 | Type: `Element` 64 | 65 | Example: `shortenRepoUrl.applyToLink(document.querySelector(a))` 66 | 67 | #### currentUrl 68 | 69 | Type: `string`, like `location.href` 70 | 71 | Same as before. 72 | 73 | ## License 74 | 75 | MIT © [Federico Brigante](https://fregante.com) 76 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@sindresorhus/tsconfig", 3 | "compilerOptions": { 4 | "target": "ES2022", 5 | } 6 | } 7 | --------------------------------------------------------------------------------