Skip to content

Commit 9ebd678

Browse files
authored
Fix isRepoRoot (#204)
1 parent 278a6d2 commit 9ebd678

File tree

4 files changed

+106
-13
lines changed

4 files changed

+106
-13
lines changed

.github/workflows/demo.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ jobs:
1515
build:
1616
runs-on: ubuntu-latest
1717
steps:
18-
- uses: actions/checkout@v3
18+
- uses: actions/checkout@v4
1919
- run: npm ci
2020
- run: npm run build:demo
2121
- run: npm run test:demo
2222
- run: grep -rq "/fregante/" ./demo/dist
2323
# https://github.com/refined-github/github-url-detection/pull/161
2424
name: Ensure that the demo is built correctly
2525
- name: Upload artifact
26-
uses: actions/upload-pages-artifact@v1
26+
uses: actions/upload-pages-artifact@v3
2727
with:
2828
path: demo/dist/
2929

@@ -42,4 +42,4 @@ jobs:
4242
steps:
4343
- name: Deploy to GitHub Pages
4444
id: deployment
45-
uses: actions/deploy-pages@v1
45+
uses: actions/deploy-pages@v4

index.test.ts

+45
Original file line numberDiff line numberDiff line change
@@ -197,3 +197,48 @@ test('getRepositoryInfo', () => {
197197
});
198198
}
199199
});
200+
201+
test('parseRepoExplorerTitle', () => {
202+
const parse = pageDetect.utils.parseRepoExplorerTitle;
203+
204+
assert.deepEqual(
205+
parse('/eslint/js/tree/2.x', 'eslint/js at 2.x'),
206+
{
207+
nameWithOwner: 'eslint/js',
208+
branch: '2.x',
209+
filePath: '',
210+
},
211+
);
212+
assert.deepEqual(
213+
parse('/eslint/js/tree/2.x', 'js/ at 2.x · eslint/js'),
214+
{
215+
nameWithOwner: 'eslint/js',
216+
branch: '2.x',
217+
filePath: '',
218+
},
219+
);
220+
assert.deepEqual(
221+
parse('/eslint/js/tree/2.x/tools', 'js/tools at 2.x · eslint/js'),
222+
{
223+
nameWithOwner: 'eslint/js',
224+
branch: '2.x',
225+
filePath: 'tools',
226+
},
227+
);
228+
assert.deepEqual(
229+
parse('/eslint/js/tree/2.x/docs/ast', 'js/docs/ast at 2.x · eslint/js'),
230+
{
231+
nameWithOwner: 'eslint/js',
232+
branch: '2.x',
233+
filePath: 'docs/ast',
234+
},
235+
);
236+
assert.deepEqual(
237+
parse('https://github.com/eslint/js', 'only /tree/ URLs are supported'),
238+
undefined,
239+
);
240+
assert.deepEqual(
241+
parse('https://github.com/eslint/js/issues', 'irrelephant'),
242+
undefined,
243+
);
244+
});

index.ts

+57-10
Original file line numberDiff line numberDiff line change
@@ -449,25 +449,71 @@ TEST: addTests('isRepoHome', [
449449
'https://github.com/sindresorhus/refined-github?files=1',
450450
]);
451451

452+
export type RepoExplorerInfo = {
453+
nameWithOwner: string;
454+
branch: string;
455+
filePath: string;
456+
};
457+
458+
// https://github.com/eslint/js/tree/2.x ->'eslint/js at 2.x'
459+
// https://github.com/eslint/js/tree/2.x ->'js/ at 2.x · eslint/js'
460+
// https://github.com/eslint/js/tree/2.x/tools -> 'js/tools at 2.x · eslint/js'
461+
const titleParseRegex = /^(?:(?<nameWithOwner>[^ ]+) at (?<branch>[^ ]+)|[^/ ]+(?:\/(?<filePath>[^ ]*))? at (?<branch2>[^ ]+)(?: · (?<nameWithOwner2>[^ ]+))?)$/;
462+
// TODO: Reuse regex group names on the next MAJOR version https://github.com/tc39/proposal-duplicate-named-capturing-groups/issues/4
463+
464+
const parseRepoExplorerTitle = (pathname: string, title: string): RepoExplorerInfo | undefined => {
465+
const match = titleParseRegex.exec(title);
466+
if (!match?.groups) {
467+
return;
468+
}
469+
470+
let {nameWithOwner, branch, filePath, nameWithOwner2, branch2} = match.groups;
471+
472+
nameWithOwner ??= nameWithOwner2;
473+
branch ??= branch2;
474+
filePath ??= '';
475+
476+
if (!nameWithOwner || !branch || !pathname.startsWith(`/${nameWithOwner}/tree/`)) {
477+
return;
478+
}
479+
480+
return {nameWithOwner, branch, filePath};
481+
};
482+
452483
const _isRepoRoot = (url?: URL | HTMLAnchorElement | Location): boolean => {
453484
const repository = getRepo(url ?? location);
454485

455486
if (!repository) {
487+
// Not a repo
456488
return false;
457489
}
458490

459-
if (!repository.path) {
460-
// Absolute repo root: `isRepoHome`
461-
return true;
462-
}
491+
const path = repository.path ? repository.path.split('/') : [];
463492

464-
if (url) {
465-
// Root of a branch/commit/tag
466-
return /^tree\/[^/]+$/.test(repository.path);
467-
}
493+
switch (path.length) {
494+
case 0: {
495+
// Absolute repo root: `isRepoHome`
496+
return true;
497+
}
498+
499+
case 2: {
500+
// 100% certainty that it's a root if it's `tree`
501+
return path[0] === 'tree';
502+
}
468503

469-
// If we're checking the current page, add support for branches with slashes // #15 #24
470-
return repository.path.startsWith('tree/') && document.title.startsWith(repository.nameWithOwner) && !document.title.endsWith(repository.nameWithOwner);
504+
default: {
505+
if (url) {
506+
// From the URL we can safely only know it's a root if it's `user/repo/tree/something`
507+
// With `user/repo/tree/something/else` we can't be sure whether `else` is a folder or still the branch name ("something/else")
508+
return false;
509+
}
510+
511+
// If we're checking the current page, add support for branches with slashes
512+
const titleInfo = parseRepoExplorerTitle(location.pathname, document.title);
513+
514+
return titleInfo?.filePath === '';
515+
}
516+
}
471517
};
472518

473519
// `_isRepoRoot` logic depends on whether a URL was passed, so don't use a `url` default parameter
@@ -857,4 +903,5 @@ export const utils = {
857903
getCleanPathname,
858904
getCleanGistPathname,
859905
getRepositoryInfo: getRepo,
906+
parseRepoExplorerTitle,
860907
};

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"build:esbuild": "esbuild index.ts --bundle --external:github-reserved-names --outdir=distribution --format=esm --drop-labels=TEST",
2929
"build:typescript": "tsc --declaration --emitDeclarationOnly",
3030
"build:demo": "vite build demo",
31+
"try": "esbuild index.ts --bundle --global-name=x --format=iife | pbcopy && echo 'Copied to clipboard'",
3132
"fix": "xo --fix",
3233
"prepack": "npm run build",
3334
"test": "run-p build test:* xo",

0 commit comments

Comments
 (0)