diff --git a/package.json b/package.json index d05ed01..4736291 100644 --- a/package.json +++ b/package.json @@ -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" diff --git a/src/platforms/GitHub/DOMHelper.ts b/src/platforms/GitHub/DOMHelper.ts index 1de1bae..68bb777 100644 --- a/src/platforms/GitHub/DOMHelper.ts +++ b/src/platforms/GitHub/DOMHelper.ts @@ -1,9 +1,11 @@ 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: { @@ -44,16 +46,11 @@ const getDOMJSON = (selector: string) => } }) -function getMetaFromPayload(payload: any) { +function getMetaFromPayload(payload: s.Infer) { const { repo, refInfo } = payload - if (!repo || !refInfo) return - const { defaultBranch, name: repoName, ownerLogin: userName } = repo const { name: branchName } = refInfo - // TODO: use runtime type check - if (!defaultBranch || !repoName || !userName || !branchName) return - return { defaultBranch, metaData: { @@ -66,15 +63,14 @@ function getMetaFromPayload(payload: any) { // in code page, there is a JSON script tag in DOM with meta data function resolveEmbeddedAppData() { - const appData = getDOMJSON(selectors.globalNavigation.embeddedData.app) - const payload = appData?.payload - if (payload) return getMetaFromPayload(payload) + const data = getDOMJSON(selectors.globalNavigation.embeddedData.app) + if (s.is(data, embeddedDataStruct.app)) return getMetaFromPayload(data.payload) } function resolveEmbeddedReposOverviewData() { - const reposOverviewData = getDOMJSON(selectors.globalNavigation.embeddedData.reposOverview) - const payload = reposOverviewData?.props?.initialPayload - if (payload) return getMetaFromPayload(payload) + const data = getDOMJSON(selectors.globalNavigation.embeddedData.reposOverview) + if (s.is(data, embeddedDataStruct.reposOverview)) + return getMetaFromPayload(data.props.initialPayload) } export function resolveEmbeddedData(): { diff --git a/src/platforms/GitHub/embeddedDataStructures.ts b/src/platforms/GitHub/embeddedDataStructures.ts new file mode 100644 index 0000000..676b93e --- /dev/null +++ b/src/platforms/GitHub/embeddedDataStructures.ts @@ -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, +} diff --git a/webpack.config.js b/webpack.config.js index 30839d9..8a1f1af 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -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, }, diff --git a/yarn.lock b/yarn.lock index bc5a776..3585039 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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"