Merge branch 'fix/load-meta-data' into develop

This commit is contained in:
EnixCoda 2023-12-15 23:11:44 +08:00
commit 5664ea1de8
6 changed files with 139 additions and 17 deletions

View file

@ -45,6 +45,7 @@
"react-use": "^17.3.2",
"react-window": "^1.8.7",
"styled-components": "^5.3.5",
"superstruct": "^1.0.3",
"webext-domain-permission-toggle": "^3.0.0",
"webext-dynamic-content-scripts": "^8.1.1",
"webextension-polyfill": "^0.10.0"

View file

@ -1,13 +1,16 @@
import { raiseError } from 'analytics'
import { Clippy, ClippyClassName } from 'components/Clippy'
import * as React from 'react'
import * as s from 'superstruct'
import { $ } from 'utils/$'
import { formatClass, parseIntFromElement } from 'utils/DOMHelper'
import { renderReact } from 'utils/general'
import { embeddedDataStruct } from './embeddedDataStructures'
const selectors = {
normal: {
reactApp: `react-app[app-name="react-code-view"] [data-target="react-app.reactRoot"]`,
codeTab: '#code-tab',
branchSwitcher: [`summary[title="Switch branches or tags"]`, `#branch-select-menu`].join(),
fileNavigation: `.file-navigation`,
breadcrumbs: `[data-testid="breadcrumbs"]`,
@ -27,28 +30,28 @@ const selectors = {
pathContext: '[data-testid="breadcrumbs"]',
pathContextFileName: '[data-testid="breadcrumbs-filename"]',
pathContextScreenReaderHeading: '[data-testid="screen-reader-heading"]',
embeddedData: {
app: 'script[type="application/json"][data-target="react-app.embeddedData"]',
reposOverview:
'[partial-name="repos-overview"] script[type="application/json"][data-target="react-partial.embeddedData"]',
},
},
}
export function resolveMetaFromDOMJSON(): { defaultBranch: string; metaData: MetaData } | void {
// in code page, there is a JSON script tag in DOM with meta data
const json = $('script[type="application/json"][data-target="react-app.embeddedData"]', e => {
const getDOMJSON = (selector: string) =>
$(selector, e => {
try {
return JSON.parse(e.textContent || '')
} catch (error) {
return null
}
})
if (!json) return
const { payload } = json
if (!payload) return
function getMetaFromPayload(payload: s.Infer<typeof embeddedDataStruct.repoPayload>) {
const { repo, refInfo } = payload
if (!repo || !refInfo) return
const { defaultBranch, name: repoName, ownerLogin: userName } = repo
const { name: branchName } = refInfo
return {
defaultBranch,
metaData: {
@ -59,8 +62,27 @@ export function resolveMetaFromDOMJSON(): { defaultBranch: string; metaData: Met
}
}
// in code page, there is a JSON script tag in DOM with meta data
function resolveEmbeddedAppData() {
const data = getDOMJSON(selectors.globalNavigation.embeddedData.app)
if (s.is(data, embeddedDataStruct.app)) return getMetaFromPayload(data.payload)
}
function resolveEmbeddedReposOverviewData() {
const data = getDOMJSON(selectors.globalNavigation.embeddedData.reposOverview)
if (s.is(data, embeddedDataStruct.reposOverview))
return getMetaFromPayload(data.props.initialPayload)
}
export function resolveEmbeddedData(): {
defaultBranch: string
metaData: MetaData
} | void {
return resolveEmbeddedAppData() || resolveEmbeddedReposOverviewData()
}
export function resolveMeta(): Partial<MetaData> {
const dataFromJSON = resolveMetaFromDOMJSON()
const dataFromJSON = resolveEmbeddedData()
if (dataFromJSON) return dataFromJSON.metaData
const metaData = {
@ -129,7 +151,9 @@ export function getCurrentBranch(passive = false) {
].join()
const branchButtonElement = $(selectedBranchButtonSelector)
if (branchButtonElement) {
const branchNameSpanElement = branchButtonElement.querySelector('span')
const branchNameSpanElement = branchButtonElement.querySelector(
['.ref-selector-button-text-container', 'span'].join(),
)
if (branchNameSpanElement) {
const partialBranchNameFromInnerText = branchNameSpanElement.textContent?.trim() || ''
if (partialBranchNameFromInnerText && !partialBranchNameFromInnerText.includes('…'))
@ -137,7 +161,7 @@ export function getCurrentBranch(passive = false) {
}
const defaultTitle = 'Switch branches or tags'
const title = branchButtonElement.title.trim()
if (title !== defaultTitle && !title.includes(' ')) return title
if (title && title !== defaultTitle && !title.includes(' ')) return title
}
const findFileButtonSelector = 'main .file-navigation a[data-hotkey="t"]'
@ -154,6 +178,17 @@ export function getCurrentBranch(passive = false) {
}
}
const branchNameFromCodeTab = $(selectors.normal.codeTab, e => {
if (e instanceof HTMLAnchorElement) {
const chunks = e.href.split('/')
const indexOfTree = chunks.indexOf('tree')
if (indexOfTree === -1) return
const branchName = chunks.slice(indexOfTree + 1).join('/')
return branchName
}
})
if (branchNameFromCodeTab) return branchNameFromCodeTab
if (!passive) raiseError(new Error('cannot get current branch'))
}

View file

@ -0,0 +1,81 @@
import * as s from 'superstruct'
const repo = s.object({
id: s.number(),
defaultBranch: s.string(),
name: s.string(),
ownerLogin: s.string(),
currentUserCanPush: s.boolean(),
isFork: s.boolean(),
isEmpty: s.boolean(),
createdAt: s.string(),
ownerAvatar: s.string(),
public: s.boolean(),
private: s.boolean(),
isOrgOwned: s.boolean(),
})
const user = s.object({
id: s.number(),
login: s.string(),
userEmail: s.string(),
})
const rel = s.object({
name: s.string(),
listCacheKey: s.string(),
canEdit: s.boolean(),
refType: s.string(),
currentOid: s.string(),
})
const treeItem = s.object({
name: s.string(),
path: s.string(),
contentType: s.string(),
})
const tree = s.object({
items: s.array(treeItem),
templateDirectorySuggestionUrl: s.nullable(s.never()),
readme: s.nullable(s.never()),
totalCount: s.number(),
showBranchInfobar: s.boolean(),
})
const repoPayload = s.object({
allShortcutsEnabled: s.boolean(),
path: s.string(),
repo: repo,
currentUser: user,
refInfo: rel,
tree: tree,
fileTree: s.nullable(s.never()),
fileTreeProcessingTime: s.nullable(s.never()),
foldersToFetch: s.array(s.unknown()),
treeExpanded: s.boolean(),
symbolsExpanded: s.boolean(),
isOverview: s.boolean(),
overview: s.unknown(),
})
const reposOverview = s.object({
props: s.object({
initialPayload: repoPayload,
appPayload: s.unknown(),
}),
})
const app = s.object({
payload: repoPayload,
})
export const embeddedDataStruct = {
repo,
user,
rel,
treeItem,
tree,
repoPayload,
reposOverview,
app,
}

View file

@ -7,12 +7,12 @@ import { resolveGitModules } from 'utils/gitSubmodule'
import { sortFoldersToFront } from 'utils/treeParser'
import * as API from './API'
import * as DOMHelper from './DOMHelper'
import * as URLHelper from './URLHelper'
import { getCommitTreeData } from './getCommitTreeData'
import { getPullRequestTreeData } from './getPullRequestTreeData'
import { useEnterpriseStatBarStyleFix } from './hooks/useEnterpriseStatBarStyleFix'
import { useGitHubAttachCopySnippetButton } from './hooks/useGitHubAttachCopySnippetButton'
import { useGitHubCodeFold } from './hooks/useGitHubCodeFold'
import * as URLHelper from './URLHelper'
export function processTree(tree: TreeNode[]): TreeNode {
// nodes are created from items and put onto tree
@ -109,7 +109,7 @@ export const GitHub: Platform = {
}
const { type } = metaFromURL
let branchName
let branchName = metaFromDOM.branchName
if (URLHelper.isInPullPage()) {
branchName = DOMHelper.getIssueTitle()
} else if (URLHelper.isInCommitPage()) {
@ -130,7 +130,7 @@ export const GitHub: Platform = {
return metaData
},
async getDefaultBranchName({ userName, repoName }, accessToken) {
const dataFromJSON = DOMHelper.resolveMetaFromDOMJSON()
const dataFromJSON = DOMHelper.resolveEmbeddedData()
if (dataFromJSON?.defaultBranch) return dataFromJSON.defaultBranch
return (await API.getRepoMeta(userName, repoName, accessToken)).default_branch

View file

@ -113,10 +113,10 @@ module.exports = {
sideEffects: false,
},
{
test: /\.js$/,
test: /\.m?js$/,
loader: 'babel-loader',
// Transpile as least files under node_modules
include: /node_modules\/(webext-content-scripts|webext-detect-page)\/.*\.js$/,
include: /node_modules\/(webext-content-scripts|webext-detect-page|superstruct)\/.*\.m?js$/,
options: {
cacheDirectory: true,
},

View file

@ -11325,6 +11325,11 @@ stylis@^4.0.6:
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.1.1.tgz#e46c6a9bbf7c58db1e65bb730be157311ae1fe12"
integrity sha512-lVrM/bNdhVX2OgBFNa2YJ9Lxj7kPzylieHd3TNjuGE0Re9JB7joL5VUKOVH1kdNNJTgGPpT8hmwIAPLaSyEVFQ==
superstruct@^1.0.3:
version "1.0.3"
resolved "https://registry.npmmirror.com/superstruct/-/superstruct-1.0.3.tgz#de626a5b49c6641ff4d37da3c7598e7a87697046"
integrity sha512-8iTn3oSS8nRGn+C2pgXSKPI3jmpm6FExNazNpjvqS6ZUJQCej3PUXEKM8NjHBOs54ExM+LPW/FBRhymrdcCiSg==
supports-color@6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3"