From 52ba904b98ba2b3d2e132542bd31806337834011 Mon Sep 17 00:00:00 2001 From: South Drifted Date: Sun, 3 Mar 2024 17:15:09 +0000 Subject: [PATCH] [add] Model, API & Page of Web polyfill CDN --- README.md | 8 +++ components/MainNavigator.tsx | 2 +- components/data.ts | 4 +- models/Polyfill.ts | 88 +++++++++++++++++++++++++++ package.json | 1 + pages/api/polyfill.ts | 27 +++++++++ pages/polyfill.tsx | 114 +++++++++++++++++++++++++++++++++++ pnpm-lock.yaml | 21 +++++++ 8 files changed, 262 insertions(+), 3 deletions(-) create mode 100644 models/Polyfill.ts create mode 100644 pages/api/polyfill.ts create mode 100644 pages/polyfill.tsx diff --git a/README.md b/README.md index e100a53..e9e1463 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,14 @@ - CI / CD: GitHub [Actions][16] + [Vercel][17] - Monitor service: [Sentry][18] +## Major features + +### Open Source license filter + +- [introduction](https://kaiyuanshe.feishu.cn/wiki/wikcnRn5pkE3BSvqFUMkJPymaG3) +- [home page](https://oss-toolbox.vercel.app/license-filter/) +- [source code](pages/license-filter.tsx) + ## Major examples 1. [Markdown articles](pages/article/) diff --git a/components/MainNavigator.tsx b/components/MainNavigator.tsx index 6dd5bcd..f1bb8f7 100644 --- a/components/MainNavigator.tsx +++ b/components/MainNavigator.tsx @@ -11,7 +11,7 @@ const { t } = i18n, LanguageMenu = dynamic(import('./LanguageMenu'), { ssr: false }); export const MainNavigator: FC = observer(() => ( - + {t('open_source_treasure_box')} diff --git a/components/data.ts b/components/data.ts index edc6434..dc519b1 100644 --- a/components/data.ts +++ b/components/data.ts @@ -13,14 +13,14 @@ export const MainRoutes: () => Link[] = () => [ title: 'GitHub issues', path: '/issue', }, - { title: t('license_tool'), path: '/tool/license-filter' }, + { title: t('license_tool'), path: '/license-filter' }, { title: `${t('Web_polyfill_CDN')} v1`, path: 'https://polyfill.kaiyuanshe.cn/', }, { title: `${t('Web_polyfill_CDN')} v2`, - path: 'https://polyfiller.kaiyuanshe.cn/', + path: '/polyfill', }, { title: t('open_source_mirror'), path: 'http://mirror.kaiyuanshe.cn/' }, { title: 'Git Pager', path: 'https://git-pager.vercel.app/' }, diff --git a/models/Polyfill.ts b/models/Polyfill.ts new file mode 100644 index 0000000..7afbf17 --- /dev/null +++ b/models/Polyfill.ts @@ -0,0 +1,88 @@ +import { observable } from 'mobx'; +import { BaseModel, toggle } from 'mobx-restful'; +import { buildURLData } from 'web-utility'; + +import { PolyfillHost } from '../pages/api/polyfill'; +import { ownClient } from './Base'; + +export type JSEnvironment = 'window' | 'worker' | 'node'; + +export type LibraryAlias = Record; + +export type LibraryPath = Record; + +export interface Library { + features: string[]; + dependencies: string[]; + contexts: {}; + meta?: object; + mustComeAfter?: string | string[]; +} + +export interface RemoteLibrary extends Library { + library: string | LibraryAlias; + relativePaths: string[] | LibraryPath; +} + +export interface LocalLibrary extends Library { + version: string; + localPaths: string[]; +} + +export interface Alias { + polyfills: string[]; +} + +export type PolyfillIndex = Record< + string, + RemoteLibrary | LocalLibrary | Alias +>; + +export const UserAgent: Record = { + 'IE 11': + 'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko', + 'Edge 18': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19042', + 'Safari 13.2': + 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1', + 'Opera 9.8': 'Opera/9.80 (Windows NT 6.1) Presto/2.12.388 Version/12.16', + 'UC 12': + 'Mozilla/5.0 (Linux; U; Android 8.1.0; en-US; Nexus 6P Build/OPM7.181205.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.11.1.1197 Mobile Safari/537.36', + 'Firefox 70': + 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:70.0) Gecko/20100101 Firefox/70.0', + 'Android 4': + 'Mozilla/5.0 (Linux; U; Android 4.0.2; en-us; Galaxy Nexus Build/ICL53F) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30', +}; + +export class PolyfillModel extends BaseModel { + @observable + accessor index: PolyfillIndex = {}; + + @observable + accessor currentUA = ''; + + @observable + accessor sourceCode = ''; + + @toggle('downloading') + async getIndex() { + const { body } = await ownClient.get( + `${PolyfillHost}/index.json`, + ); + return (this.index = body!); + } + + @toggle('downloading') + async getSourceCode(UA: string, features: string[]) { + const response = await fetch( + `/api/polyfill?${buildURLData({ features })}`, + { + headers: { 'User-Agent': UserAgent[UA] }, + }, + ); + this.currentUA = UA; + return (this.sourceCode = await response.text()); + } +} + +export default new PolyfillModel(); diff --git a/package.json b/package.json index 826a347..ebcc793 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "next-pwa": "~5.6.0", "next-ssr-middleware": "^0.7.0", "next-with-less": "^3.0.1", + "primereact": "^10.5.1", "prismjs": "^1.29.0", "react": "^18.2.0", "react-bootstrap": "^2.10.1", diff --git a/pages/api/polyfill.ts b/pages/api/polyfill.ts new file mode 100644 index 0000000..851f1a7 --- /dev/null +++ b/pages/api/polyfill.ts @@ -0,0 +1,27 @@ +import { HTTPClient } from 'koajax'; + +import { safeAPI } from './core'; + +export const PolyfillHost = 'https://polyfiller.kaiyuanshe.cn'; + +export const polyfillClient = new HTTPClient({ + baseURI: PolyfillHost, + responseType: 'text', +}); + +export default safeAPI(async ({ method, url, headers, body }, response) => { + delete headers.host; + + const { status, body: data } = await polyfillClient.request({ + // @ts-ignore + method, + path: url!, + // @ts-ignore + headers, + body: body || undefined, + }); + + response.status(status); + response.setHeader('Access-Control-Allow-Headers', '*'); + response.send(data); +}); diff --git a/pages/polyfill.tsx b/pages/polyfill.tsx new file mode 100644 index 0000000..7d1a7e2 --- /dev/null +++ b/pages/polyfill.tsx @@ -0,0 +1,114 @@ +import { Loading } from 'idea-react'; +import { computed, observable } from 'mobx'; +import { textJoin } from 'mobx-i18n'; +import { observer } from 'mobx-react'; +import { compose, translator } from 'next-ssr-middleware'; +import { TreeNode } from 'primereact/treenode'; +import { TreeSelect, TreeSelectSelectionKeysType } from 'primereact/treeselect'; +import { Component } from 'react'; +import { Card, Container, Dropdown, DropdownButton } from 'react-bootstrap'; + +import { PageHead } from '../components/PageHead'; +import polyfillStore, { UserAgent } from '../models/Polyfill'; +import { i18n } from '../models/Translation'; +import { PolyfillHost } from './api/polyfill'; + +export const getServerSideProps = compose(translator(i18n)); + +const { t } = i18n; + +@observer +export default class PolyfillPage extends Component { + @computed + get options() { + return Object.entries(polyfillStore.index) + .map(([key, data]) => !('polyfills' in data) && { key, label: key, data }) + .filter(Boolean) as TreeNode[]; + } + + @observable + accessor selectOptions: TreeSelectSelectionKeysType = {}; + + @computed + get features() { + return Object.keys(this.selectOptions); + } + + @computed + get polyfillURL() { + return `${PolyfillHost}/api/polyfill?features=${this.features}`; + } + + componentDidMount() { + polyfillStore.getIndex(); + } + + renderContent() { + const { options, selectOptions, features, polyfillURL } = this, + { currentUA, sourceCode } = polyfillStore; + + return ( +
+ + (this.selectOptions = value as TreeSelectSelectionKeysType) + } + /> +
+ + {Object.entries(UserAgent).map(([name, value]) => ( + polyfillStore.getSourceCode(name, features)} + > + {name} + + ))} + + +

{t('Web_polyfill_CDN')}

+
+ + + + {polyfillURL} + +
+
+            {sourceCode}
+          
+
+
+ ); + } + + render() { + const { downloading } = polyfillStore; + + return ( + + + + + + + {downloading > 0 && } + + {this.renderContent()} + + ); + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4e2336a..ff651c9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -95,6 +95,9 @@ dependencies: next-with-less: specifier: ^3.0.1 version: 3.0.1(less-loader@12.2.0)(less@4.2.0)(next@14.1.1) + primereact: + specifier: ^10.5.1 + version: 10.5.1(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0) prismjs: specifier: ^1.29.0 version: 1.29.0 @@ -6138,6 +6141,24 @@ packages: engines: {node: '>=6'} dev: false + /primereact@10.5.1(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-v+JpIisXmqUHWwYDojWibB4Kx8n6KOliBYhmRUQ2nzQAz14i53paU6YfSXe8zOSX3uNnbJAZF76hCKlXsUDlbQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.2.61 + '@types/react-transition-group': 4.4.10 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-transition-group: 4.4.5(react-dom@18.2.0)(react@18.2.0) + dev: false + /prismjs@1.29.0: resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} engines: {node: '>=6'}