Skip to content

Fix isRepoRoot #204

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/demo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- run: npm ci
- run: npm run build:demo
- run: npm run test:demo
- run: grep -rq "/fregante/" ./demo/dist
# https://github.com/refined-github/github-url-detection/pull/161
name: Ensure that the demo is built correctly
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
uses: actions/upload-pages-artifact@v3
with:
path: demo/dist/

Expand All @@ -42,4 +42,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1
uses: actions/deploy-pages@v4
45 changes: 45 additions & 0 deletions index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,3 +197,48 @@ test('getRepositoryInfo', () => {
});
}
});

test('parseRepoExplorerTitle', () => {
const parse = pageDetect.utils.parseRepoExplorerTitle;

assert.deepEqual(
parse('/eslint/js/tree/2.x', 'eslint/js at 2.x'),
{
nameWithOwner: 'eslint/js',
branch: '2.x',
filePath: '',
},
);
assert.deepEqual(
parse('/eslint/js/tree/2.x', 'js/ at 2.x · eslint/js'),
{
nameWithOwner: 'eslint/js',
branch: '2.x',
filePath: '',
},
);
assert.deepEqual(
parse('/eslint/js/tree/2.x/tools', 'js/tools at 2.x · eslint/js'),
{
nameWithOwner: 'eslint/js',
branch: '2.x',
filePath: 'tools',
},
);
assert.deepEqual(
parse('/eslint/js/tree/2.x/docs/ast', 'js/docs/ast at 2.x · eslint/js'),
{
nameWithOwner: 'eslint/js',
branch: '2.x',
filePath: 'docs/ast',
},
);
assert.deepEqual(
parse('https://github.com/eslint/js', 'only /tree/ URLs are supported'),
undefined,
);
assert.deepEqual(
parse('https://github.com/eslint/js/issues', 'irrelephant'),
undefined,
);
});
67 changes: 57 additions & 10 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -449,25 +449,71 @@ TEST: addTests('isRepoHome', [
'https://github.com/sindresorhus/refined-github?files=1',
]);

export type RepoExplorerInfo = {
nameWithOwner: string;
branch: string;
filePath: string;
};

// https://github.com/eslint/js/tree/2.x ->'eslint/js at 2.x'
// https://github.com/eslint/js/tree/2.x ->'js/ at 2.x · eslint/js'
// https://github.com/eslint/js/tree/2.x/tools -> 'js/tools at 2.x · eslint/js'
const titleParseRegex = /^(?:(?<nameWithOwner>[^ ]+) at (?<branch>[^ ]+)|[^/ ]+(?:\/(?<filePath>[^ ]*))? at (?<branch2>[^ ]+)(?: · (?<nameWithOwner2>[^ ]+))?)$/;
// TODO: Reuse regex group names on the next MAJOR version https://github.com/tc39/proposal-duplicate-named-capturing-groups/issues/4

const parseRepoExplorerTitle = (pathname: string, title: string): RepoExplorerInfo | undefined => {
const match = titleParseRegex.exec(title);
if (!match?.groups) {
return;
}

let {nameWithOwner, branch, filePath, nameWithOwner2, branch2} = match.groups;

nameWithOwner ??= nameWithOwner2;
branch ??= branch2;
filePath ??= '';

if (!nameWithOwner || !branch || !pathname.startsWith(`/${nameWithOwner}/tree/`)) {
return;
}

return {nameWithOwner, branch, filePath};
};

const _isRepoRoot = (url?: URL | HTMLAnchorElement | Location): boolean => {
const repository = getRepo(url ?? location);

if (!repository) {
// Not a repo
return false;
}

if (!repository.path) {
// Absolute repo root: `isRepoHome`
return true;
}
const path = repository.path ? repository.path.split('/') : [];

if (url) {
// Root of a branch/commit/tag
return /^tree\/[^/]+$/.test(repository.path);
}
switch (path.length) {
case 0: {
// Absolute repo root: `isRepoHome`
return true;
}

case 2: {
// 100% certainty that it's a root if it's `tree`
return path[0] === 'tree';
}

// If we're checking the current page, add support for branches with slashes // #15 #24
return repository.path.startsWith('tree/') && document.title.startsWith(repository.nameWithOwner) && !document.title.endsWith(repository.nameWithOwner);
default: {
if (url) {
// From the URL we can safely only know it's a root if it's `user/repo/tree/something`
// With `user/repo/tree/something/else` we can't be sure whether `else` is a folder or still the branch name ("something/else")
return false;
}

// If we're checking the current page, add support for branches with slashes
const titleInfo = parseRepoExplorerTitle(location.pathname, document.title);

return titleInfo?.filePath === '';
}
}
};

// `_isRepoRoot` logic depends on whether a URL was passed, so don't use a `url` default parameter
Expand Down Expand Up @@ -857,4 +903,5 @@ export const utils = {
getCleanPathname,
getCleanGistPathname,
getRepositoryInfo: getRepo,
parseRepoExplorerTitle,
};
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"build:esbuild": "esbuild index.ts --bundle --external:github-reserved-names --outdir=distribution --format=esm --drop-labels=TEST",
"build:typescript": "tsc --declaration --emitDeclarationOnly",
"build:demo": "vite build demo",
"try": "esbuild index.ts --bundle --global-name=x --format=iife | pbcopy && echo 'Copied to clipboard'",
"fix": "xo --fix",
"prepack": "npm run build",
"test": "run-p build test:* xo",
Expand Down