Skip to content

Commit 08bd7c5

Browse files
authored
fix: url encoding of url-unsafe characters (#40)
1 parent 3ff8e33 commit 08bd7c5

File tree

7 files changed

+74
-18
lines changed

7 files changed

+74
-18
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## 1.0.1 - 2021-04-17
11+
12+
### Fixed
13+
14+
- Fixed URL escaping of branch and file names.
15+
1016
## 1.0.0 - 2021-04-17
1117

1218
### Added

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ The "main" branch is configured via `githubinator.mainBranches` (see "Extension
6969

7070
## Release Notes
7171

72+
## 1.0.1
73+
74+
- Fixed URL escaping of branch and file names.
75+
7276
## 1.0.0
7377

7478
- support multiple default branches. vscode-githubinator now attempts to open `main`, then `master`, `trunk`, `develop`, and `dev`. Configure these branches with the `githubinator.mainBranches` option.

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "githubinator",
33
"displayName": "Githubinator",
44
"description": "Quickly open files on Github and other providers. View blame information, copy permalinks and more. See the \"commands\" section of the README for more details.",
5-
"version": "1.0.0",
5+
"version": "1.0.1",
66
"publisher": "chdsbd",
77
"license": "SEE LICENSE IN LICENSE",
88
"icon": "images/logo256.png",
@@ -301,6 +301,7 @@
301301
},
302302
"devDependencies": {
303303
"@types/ini": "^1.3.30",
304+
"@types/lodash": "^4.14.168",
304305
"@types/mocha": "^2.2.42",
305306
"@types/mz": "^0.0.32",
306307
"@types/node": "^10.12.21",
@@ -312,6 +313,7 @@
312313
"dependencies": {
313314
"gitconfiglocal": "^2.1.0",
314315
"ini": "^1.3.5",
316+
"lodash": "^4.17.21",
315317
"mz": "^2.7.0"
316318
}
317319
}

src/extension.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,10 +242,11 @@ async function githubinator({
242242
return err("could not find url")
243243
}
244244
if (openUrl) {
245-
vscode.env.openExternal(vscode.Uri.parse(url))
245+
// @ts-ignore This works. Using vscode.Uri.parse double encodes characters which breaks URLs.
246+
await vscode.env.openExternal(url)
246247
}
247248
if (copyToClipboard) {
248-
vscode.env.clipboard.writeText(url)
249-
vscode.window.showInformationMessage("URL copied to clipboard.")
249+
await vscode.env.clipboard.writeText(url)
250+
await vscode.window.showInformationMessage("URL copied to clipboard.")
250251
}
251252
}

src/providers.ts

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as path from "path"
22
import * as url from "url"
33
import { IProviderConfig } from "./extension"
44
import { cleanHostname } from "./utils"
5+
import { flatten } from "lodash"
56

67
interface IBaseGetUrls {
78
readonly selection: [number | null, number | null]
@@ -102,6 +103,12 @@ abstract class BaseProvider {
102103
abstract getUrls(params: IBaseGetUrls): Promise<IUrlInfo | null>
103104
}
104105

106+
export function pathJoin(...args: string[]): string {
107+
return path.join(
108+
...flatten(args.map(x => x.split("/"))).map(encodeURIComponent),
109+
)
110+
}
111+
105112
export class Github extends BaseProvider {
106113
DEFAULT_HOSTNAMES = ["github.com"]
107114
PROVIDER_NAME = "github"
@@ -132,7 +139,7 @@ export class Github extends BaseProvider {
132139
return null
133140
}
134141
const u = new url.URL(
135-
path.join(
142+
pathJoin(
136143
repoInfo.org,
137144
repoInfo.repo,
138145
mode,
@@ -150,11 +157,11 @@ export class Github extends BaseProvider {
150157
const blameUrl = createUrl("blame")
151158
const historyUrl = createUrl("commits", false)
152159
const compareUrl = new url.URL(
153-
path.join(repoInfo.org, repoInfo.repo, "compare", head.value),
160+
pathJoin(repoInfo.org, repoInfo.repo, "compare", head.value),
154161
rootUrl,
155162
).toString()
156163
const prUrl = new url.URL(
157-
path.join(repoInfo.org, repoInfo.repo, "pull", "new", head.value),
164+
pathJoin(repoInfo.org, repoInfo.repo, "pull", "new", head.value),
158165
rootUrl,
159166
).toString()
160167
return {
@@ -190,15 +197,15 @@ export class Gitlab extends BaseProvider {
190197
const lines =
191198
start != null && end != null ? `L${start + 1}-${end + 1}` : null
192199
const repoUrl = new url.URL(
193-
path.join(repoInfo.org, repoInfo.repo),
200+
pathJoin(repoInfo.org, repoInfo.repo),
194201
rootUrl,
195202
).toString()
196203
const createUrl = (mode: string, hash = true) => {
197204
if (relativeFilePath == null) {
198205
return null
199206
}
200207
const u = new url.URL(
201-
path.join(
208+
pathJoin(
202209
repoInfo.org,
203210
repoInfo.repo,
204211
mode,
@@ -216,12 +223,12 @@ export class Gitlab extends BaseProvider {
216223
const blameUrl = createUrl("blame")
217224
const historyUrl = createUrl("commits", false)
218225
const compareUrl = new url.URL(
219-
path.join(repoInfo.org, repoInfo.repo, "compare", head.value),
226+
pathJoin(repoInfo.org, repoInfo.repo, "compare", head.value),
220227
rootUrl,
221228
).toString()
222229
// https://gitlab.com/recipeyak/recipeyak/merge_requests/new?merge_request%5Bsource_branch%5D=master
223230
const prUrl = new url.URL(
224-
path.join(repoInfo.org, repoInfo.repo, "merge_requests", "new"),
231+
pathJoin(repoInfo.org, repoInfo.repo, "merge_requests", "new"),
225232
rootUrl,
226233
)
227234
prUrl.search = `merge_request%5Bsource_branch%5D=${head.value}`
@@ -258,15 +265,15 @@ export class Bitbucket extends BaseProvider {
258265
const lines =
259266
start != null && end != null ? `lines-${start + 1}:${end + 1}` : null
260267
const repoUrl = new url.URL(
261-
path.join(repoInfo.org, repoInfo.repo),
268+
pathJoin(repoInfo.org, repoInfo.repo),
262269
rootUrl,
263270
).toString()
264271
const createUrl = (mode: string, hash = true) => {
265272
if (relativeFilePath == null) {
266273
return null
267274
}
268275
const u = new url.URL(
269-
path.join(
276+
pathJoin(
270277
repoInfo.org,
271278
repoInfo.repo,
272279
mode,
@@ -283,7 +290,7 @@ export class Bitbucket extends BaseProvider {
283290
const blobUrl = createUrl("blob")
284291
const blameUrl = createUrl("annotate")
285292
const compareUrl = new url.URL(
286-
path.join(
293+
pathJoin(
287294
repoInfo.org,
288295
repoInfo.repo,
289296
"branches",
@@ -295,7 +302,7 @@ export class Bitbucket extends BaseProvider {
295302
const historyUrl = createUrl("history-node", false)
296303
// "https://bitbucket.org/recipeyak/recipeyak/pull-requests/new?source=db99a912f5c4bffe11d91e163cd78ed96589611b"
297304
const prUrl = new url.URL(
298-
path.join(repoInfo.org, repoInfo.repo, "pull-requests", "new"),
305+
pathJoin(repoInfo.org, repoInfo.repo, "pull-requests", "new"),
299306
rootUrl,
300307
)
301308
prUrl.search = `source=${head.value}`
@@ -337,7 +344,7 @@ export class VisualStudio extends BaseProvider {
337344
? `&line=${start + 1}&lineEnd=${end + 1}`
338345
: null
339346
const repoUrl = new url.URL(
340-
path.join(repoInfo.org, "_git", repoInfo.repo),
347+
pathJoin(repoInfo.org, "_git", repoInfo.repo),
341348
rootUrl,
342349
)
343350
let filePath = relativeFilePath
@@ -359,12 +366,12 @@ export class VisualStudio extends BaseProvider {
359366
historyUrl.search = baseSearch + "&_a=history"
360367
}
361368
const compareUrl = new url.URL(
362-
path.join(repoInfo.org, "_git", repoInfo.repo, "branches"),
369+
pathJoin(repoInfo.org, "_git", repoInfo.repo, "branches"),
363370
rootUrl,
364371
)
365372
compareUrl.search = `targetVersion=${version}&_a=commits`
366373
const prUrl = new url.URL(
367-
path.join(repoInfo.org, "_git", repoInfo.repo, "pullrequestcreate"),
374+
pathJoin(repoInfo.org, "_git", repoInfo.repo, "pullrequestcreate"),
368375
rootUrl,
369376
)
370377
prUrl.search = `sourceRef=${head.value}`

src/test/providers.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,35 @@ import {
55
createSha,
66
createBranch,
77
Github,
8+
pathJoin,
89
} from "../providers"
910
import * as assert from "assert"
1011

12+
suite("utils", async () => {
13+
test("pathJoin", () => {
14+
assert.strictEqual(
15+
pathJoin(
16+
"ghost",
17+
"ghost.github.io",
18+
"blob",
19+
"fixit/-#123✅",
20+
"C#/C#.Package",
21+
),
22+
"ghost/ghost.github.io/blob/fixit/-%23123%E2%9C%85/C%23/C%23.Package",
23+
)
24+
assert.strictEqual(
25+
pathJoin(
26+
"ghost",
27+
"ghost.github.io",
28+
"blob",
29+
"chris/fix#123-✅",
30+
"blah-#🤷🏻‍♂️.txt",
31+
),
32+
"ghost/ghost.github.io/blob/chris/fix%23123-%E2%9C%85/blah-%23%F0%9F%A4%B7%F0%9F%8F%BB%E2%80%8D%E2%99%82%EF%B8%8F.txt",
33+
)
34+
})
35+
})
36+
1137
suite("Github", async () => {
1238
test("ssh", async () => {
1339
for (let url of [

yarn.lock

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
resolved "https://registry.yarnpkg.com/@types/ini/-/ini-1.3.30.tgz#d1485459c9fad84e937414b832a2adb649eab379"
1313
integrity sha512-2+iF8zPSbpU83UKE+PNd4r/MhwNAdyGpk3H+VMgEH3EhjFZq1kouLgRoZrmIcmoGX97xFvqdS44DkICR5Nz3tQ==
1414

15+
"@types/lodash@^4.14.168":
16+
version "4.14.168"
17+
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.168.tgz#fe24632e79b7ade3f132891afff86caa5e5ce008"
18+
integrity sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==
19+
1520
"@types/mocha@^2.2.42":
1621
version "2.2.48"
1722
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.48.tgz#3523b126a0b049482e1c3c11877460f76622ffab"
@@ -336,6 +341,11 @@ js-yaml@^3.7.0:
336341
argparse "^1.0.7"
337342
esprima "^4.0.0"
338343

344+
lodash@^4.17.21:
345+
version "4.17.21"
346+
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
347+
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
348+
339349
minimatch@3.0.4, minimatch@^3.0.4:
340350
version "3.0.4"
341351
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"

0 commit comments

Comments
 (0)