diff --git a/src/platforms/GitHub/API.ts b/src/platforms/GitHub/API.ts index 3bc7ac0..6048cb5 100644 --- a/src/platforms/GitHub/API.ts +++ b/src/platforms/GitHub/API.ts @@ -2,7 +2,11 @@ import { errors } from 'platforms' import { isEnterprise } from '.' import { is } from '../../utils/is' import { gitakoServiceHost } from '../../utils/networkService' -import { continuousLoadFragmentedPages, getDOM, resolveHeaderLink } from './utils' +import { + continuousLoadFragmentedPages, + continuousLoadFragmentedPagesFromUrl, + resolveHeaderLink, +} from './utils' function isAPIRateLimitExceeded(content: JSONValue) { return ( @@ -145,15 +149,15 @@ export async function getPullPageDocuments( repoName: string, pullId: string, document?: Document, -): Promise { - // Response of this API contains view of few files but is not complete. - return continuousLoadFragmentedPages( - document || - (await getDOM(`${window.location.origin}/${userName}/${repoName}/pull/${pullId}/files`)), - ) +) { + if (document) { + return continuousLoadFragmentedPages(document) + } + // Response of this contains view of few files but is not complete. + return continuousLoadFragmentedPagesFromUrl(`/${userName}/${repoName}/pull/${pullId}/files`) } -export async function getCommitPageDocuments(): Promise { +export async function getCommitPageDocuments() { /* userName: string, repoName: string, commitId: string, */ diff --git a/src/platforms/GitHub/embeddedDataStructures.ts b/src/platforms/GitHub/embeddedDataStructures.ts index 2425b7a..3cdfb9a 100644 --- a/src/platforms/GitHub/embeddedDataStructures.ts +++ b/src/platforms/GitHub/embeddedDataStructures.ts @@ -1,6 +1,6 @@ import * as s from 'superstruct' -const repo = s.object({ +const repo = s.type({ id: s.number(), defaultBranch: s.string(), name: s.string(), @@ -15,13 +15,13 @@ const repo = s.object({ isOrgOwned: s.boolean(), }) -const user = s.object({ +const user = s.type({ id: s.number(), login: s.string(), userEmail: s.string(), }) -const rel = s.object({ +const rel = s.type({ name: s.string(), listCacheKey: s.string(), canEdit: s.boolean(), @@ -29,13 +29,13 @@ const rel = s.object({ currentOid: s.string(), }) -const treeItem = s.object({ +const treeItem = s.type({ name: s.string(), path: s.string(), contentType: s.string(), }) -const tree = s.object({ +const tree = s.type({ items: s.array(treeItem), templateDirectorySuggestionUrl: s.nullable(s.never()), readme: s.nullable(s.never()), @@ -43,7 +43,7 @@ const tree = s.object({ showBranchInfobar: s.boolean(), }) -const repoPayload = s.object({ +const repoPayload = s.type({ allShortcutsEnabled: s.boolean(), path: s.string(), repo: repo, @@ -59,18 +59,18 @@ const repoPayload = s.object({ overview: s.unknown(), }) -const reposOverview = s.object({ - props: s.object({ +const reposOverview = s.type({ + props: s.type({ initialPayload: repoPayload, appPayload: s.unknown(), }), }) -const app = s.object({ +const app = s.type({ payload: repoPayload, }) -const diffSummary = s.object({ +const diffSummary = s.type({ changeType: s.string(), highestAnnotationLevel: s.nullable(s.string()), isCodeowner: s.nullable(s.boolean()), @@ -87,11 +87,18 @@ const diffSummary = s.object({ export type DiffSummary = s.Infer -const pullRequest = s.object({ - payload: s.object({ - pullRequestsFilesRoute: s.object({ - diffSummaries: s.array(diffSummary), - }), +const pullRequest = s.type({ + payload: s.type({ + pullRequestsFilesRoute: s.optional( + s.type({ + diffSummaries: s.array(diffSummary), + }), + ), + pullRequestsChangesRoute: s.optional( + s.type({ + diffSummaries: s.array(diffSummary), + }), + ), }), }) diff --git a/src/platforms/GitHub/getCommitTreeData.ts b/src/platforms/GitHub/getCommitTreeData.ts index d2f5121..3ac5292 100644 --- a/src/platforms/GitHub/getCommitTreeData.ts +++ b/src/platforms/GitHub/getCommitTreeData.ts @@ -15,7 +15,7 @@ export async function getCommitTreeData( .map(({ files }) => files) .flat() - const documents = await API.getCommitPageDocuments(/* userName, repoName, commitSHA */) + const [, documents] = await API.getCommitPageDocuments(/* userName, repoName, commitSHA */) const getItemURL = (path: string) => { for (const doc of documents) { diff --git a/src/platforms/GitHub/getPullRequestTreeData.ts b/src/platforms/GitHub/getPullRequestTreeData.ts index 1eadf5f..51ec3ea 100644 --- a/src/platforms/GitHub/getPullRequestTreeData.ts +++ b/src/platforms/GitHub/getPullRequestTreeData.ts @@ -38,7 +38,7 @@ export async function getPullRequestTreeData( API.getPullComments(userName, repoName, pullId, accessToken), ]) - const docs = await API.getPullPageDocuments( + const [fileChangesPagePath, docs] = await API.getPullPageDocuments( userName, repoName, pullId, @@ -53,7 +53,7 @@ export async function getPullRequestTreeData( : resolveFileHashMap(docs) const url = new URL(sanitizedLocation.href) - url.pathname = `/${userName}/${repoName}/pull/${pullId}/files` + url.pathname = fileChangesPagePath const commentsMap = getCommentsMap(commentData) const nodes: TreeNode[] = treeData.map( ({ @@ -117,12 +117,15 @@ function resolveDiffSummaryMap(docs: Document[]) { return docs .map(resolveEmbeddedPullRequestData) .map(json => { - try { - return json?.payload.pullRequestsFilesRoute.diffSummaries - } catch (error) { - return null + const payload = json?.payload + if (!payload) return null + if ('pullRequestsFilesRoute' in payload) { + return payload.pullRequestsFilesRoute + } else if ('pullRequestsChangesRoute' in payload) { + return payload.pullRequestsChangesRoute } }) + .map(pullRequests => pullRequests?.diffSummaries) .reduce((map, curr) => { curr?.forEach(record => { map.set(record.path, record) diff --git a/src/platforms/GitHub/utils.ts b/src/platforms/GitHub/utils.ts index 1852ed6..f83ff58 100644 --- a/src/platforms/GitHub/utils.ts +++ b/src/platforms/GitHub/utils.ts @@ -1,3 +1,5 @@ +import { findMapFirst } from '../../utils/findMapFirst' + /** * Resolved from response header `link` * @@ -75,10 +77,19 @@ export function resolveHeaderLink(raw: string) { } export async function getDOM(url: string) { - return new DOMParser().parseFromString(await (await fetch(url)).text(), 'text/html') + const res = await fetch(url) + const content = await res.text() + return new DOMParser().parseFromString(content, 'text/html') } -export async function continuousLoadFragmentedPages(doc: Document) { +export async function continuousLoadFragmentedPages( + doc: Document, + docs: Document[] = [], +): Promise<[string, Document[]]> { + docs.push(doc) + + // const data = resolveEmbeddedPullRequestData(doc) + /** * doc.querySelector(selector)) - if (selector) { - // eslint-disable-next-line no-constant-condition - while (true) { - const fragment = doc.querySelector(selector) - if (!(fragment instanceof HTMLElement)) break - const src = fragment.getAttribute('src') - if (!src) break + const fragment = findMapFirst(fragmentSelectors, selector => doc.querySelector(selector)) + if (fragment instanceof HTMLElement) { + const src = fragment.getAttribute('src') + if (src) { // Using `src` without origin below would fail in Firefox if the src is an absolute path - doc = await getDOM(new URL(src, window.location.origin).href) - documents.push(doc) + await continuousLoadFragmentedPagesFromUrl(src, docs) } } - return documents + return [new URL(doc.baseURI).pathname, docs] +} + +export async function continuousLoadFragmentedPagesFromUrl(url: string, docs: Document[] = []) { + return continuousLoadFragmentedPages( + await getDOM(new URL(url, window.location.origin).href), + docs, + ) } export function getCommentsMap(commentData: GitHubAPI.PullComments) { diff --git a/src/utils/findMapFirst.ts b/src/utils/findMapFirst.ts new file mode 100644 index 0000000..7a798e3 --- /dev/null +++ b/src/utils/findMapFirst.ts @@ -0,0 +1,7 @@ +export const findMapFirst = (array: T[], map: (item: T) => R): R | null => { + for (const item of array) { + const result = map(item) + if (result) return result + } + return null +}