From 7328ec3415990edbcad90c4814df0adf02808109 Mon Sep 17 00:00:00 2001 From: Harry Chen Date: Mon, 15 Apr 2024 21:45:09 +0800 Subject: [PATCH] Switch some js files to ts for better typing Signed-off-by: Harry Chen --- .gitignore | 2 +- _src/entrypoints-njs/{all.js => all.ts} | 0 .../{fancy_index.js => fancy_index.ts} | 6 +- .../{legacy_index.js => legacy_index.ts} | 4 +- _src/entrypoints/default.js | 3 +- _src/entrypoints/fancyIndex.js | 4 +- _src/entrypoints/help.js | 9 ++- _src/entrypoints/helpz.js | 2 +- _src/entrypoints/notfound.js | 9 +-- _src/lib/{mirrorList.js => mirrorList.ts} | 8 ++- ...cessing.js => mirrorListDataProcessing.ts} | 33 +++++------ _src/lib/types.ts | 26 +++++++++ _src/tsconfig.json | 13 +++++ _vite.config.mjs | 6 +- package-lock.json | 57 ++++++++++++++++++- package.json | 8 ++- 16 files changed, 150 insertions(+), 40 deletions(-) rename _src/entrypoints-njs/{all.js => all.ts} (100%) rename _src/entrypoints-njs/{fancy_index.js => fancy_index.ts} (77%) rename _src/entrypoints-njs/{legacy_index.js => legacy_index.ts} (95%) rename _src/lib/{mirrorList.js => mirrorList.ts} (82%) rename _src/lib/{mirrorListDataProcessing.js => mirrorListDataProcessing.ts} (81%) create mode 100644 _src/lib/types.ts create mode 100644 _src/tsconfig.json diff --git a/.gitignore b/.gitignore index 73602e2..94dcbbb 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,4 @@ /node_modules/ /.jekyll-cache/ _stats.html - +/_src/components.d.ts diff --git a/_src/entrypoints-njs/all.js b/_src/entrypoints-njs/all.ts similarity index 100% rename from _src/entrypoints-njs/all.js rename to _src/entrypoints-njs/all.ts diff --git a/_src/entrypoints-njs/fancy_index.js b/_src/entrypoints-njs/fancy_index.ts similarity index 77% rename from _src/entrypoints-njs/fancy_index.js rename to _src/entrypoints-njs/fancy_index.ts index e6c6025..9eb4b68 100644 --- a/_src/entrypoints-njs/fancy_index.js +++ b/_src/entrypoints-njs/fancy_index.ts @@ -1,6 +1,6 @@ import Mark from "markup-js"; -function fancyIndexRender(r, templateUrl) { +function fancyIndexRender(r: NginxHTTPRequest, templateUrl: string) { r.subrequest( templateUrl, { @@ -25,10 +25,10 @@ function fancyIndexRender(r, templateUrl) { ); } -export function fancyIndexBeforeRender(r) { +export function fancyIndexBeforeRender(r: NginxHTTPRequest) { return fancyIndexRender(r, "/fancy-index/before.html"); } -export function fancyIndexAfterRender(r) { +export function fancyIndexAfterRender(r: NginxHTTPRequest) { return fancyIndexRender(r, "/fancy-index/after.html"); } diff --git a/_src/entrypoints-njs/legacy_index.js b/_src/entrypoints-njs/legacy_index.ts similarity index 95% rename from _src/entrypoints-njs/legacy_index.js rename to _src/entrypoints-njs/legacy_index.ts index 50311f7..afcc5f6 100644 --- a/_src/entrypoints-njs/legacy_index.js +++ b/_src/entrypoints-njs/legacy_index.ts @@ -1,9 +1,9 @@ -import "../lib/njs-polyfill.js"; +import "../lib/njs-polyfill"; import Mark from "markup-js"; import processingHandlers from "../lib/mirrorListDataProcessing"; import { TUNASYNC_JSON_PATH } from "../lib/consts"; -export function legacyIndexRender(r) { +export function legacyIndexRender(r: NginxHTTPRequest) { r.subrequest( "/legacy_index.html", { diff --git a/_src/entrypoints/default.js b/_src/entrypoints/default.js index e19bf12..3632858 100644 --- a/_src/entrypoints/default.js +++ b/_src/entrypoints/default.js @@ -1,4 +1,5 @@ import "../styles/global.scss"; +// @ts-ignore import { suffix as siteSuffix } from "virtual:jekyll-config"; import { load as loadWebFont } from "webfontloader"; @@ -17,7 +18,7 @@ const lei3Po8h = ["support", ["tuna", "tsinghua", "edu", "cn"].join(".")].join( ); Array.from(document.querySelectorAll("a.eib1gieB")).forEach((el) => { el.textContent = lei3Po8h; - el.href = ["ma", "ilto:"].join("i") + lei3Po8h; + el["href"] = ["ma", "ilto:"].join("i") + lei3Po8h; }); loadWebFont({ diff --git a/_src/entrypoints/fancyIndex.js b/_src/entrypoints/fancyIndex.js index 4938755..6797241 100644 --- a/_src/entrypoints/fancyIndex.js +++ b/_src/entrypoints/fancyIndex.js @@ -7,7 +7,7 @@ import { createApp } from "vue"; document.getElementById("list").setAttribute("class", "table"); Array.from(document.querySelectorAll("#list tbody tr td:nth-child(3)")).forEach( (el) => { - const d = new Date(el.innerText); + const d = new Date(el["innerText"]); if (!isNaN(d.getTime())) { const date_str = ("000" + d.getFullYear()).substr(-4) + @@ -19,7 +19,7 @@ Array.from(document.querySelectorAll("#list tbody tr td:nth-child(3)")).forEach( ("0" + d.getHours()).substr(-2) + ":" + ("0" + d.getMinutes()).substr(-2)); - el.innerText = date_str; + el["innerText"] = date_str; } }, ); diff --git a/_src/entrypoints/help.js b/_src/entrypoints/help.js index 535eae7..5b1471c 100644 --- a/_src/entrypoints/help.js +++ b/_src/entrypoints/help.js @@ -1,8 +1,9 @@ import { hide_mirrorz as HideMirrorZ, hostname as SiteHostname, - mirrorz_help_link as MirrorzHelpLink, + mirrorz_help_link as MirrorzHelpLink, // @ts-ignore } from "virtual:jekyll-config"; +// @ts-ignore import { options as globalOptions } from "virtual:jekyll-data"; import hljs from "../lib/hljs"; import Mark from "markup-js"; @@ -41,7 +42,7 @@ const update_target = (ev) => { } // special hack for case-insensitive if ("sudoe" in template_data) { - template_data.sudoE = template_data.sudoe; + template_data["sudoE"] = template_data.sudoe; } const template = document .querySelector(template_selector) @@ -60,7 +61,9 @@ Array.from(document.querySelectorAll("select.content-select")).map((el) => { document.getElementById("help-select").addEventListener("change", (ev) => { let help_url = ev.target.querySelector("option:checked").attributes["data-help-url"].value; - window.location = `${window.location.protocol}//${window.location.host}${help_url}`; + window.location.assign( + `${window.location.protocol}//${window.location.host}${help_url}`, + ); }); fetch(TUNASYNC_JSON_PATH) diff --git a/_src/entrypoints/helpz.js b/_src/entrypoints/helpz.js index b086e05..f75b76d 100644 --- a/_src/entrypoints/helpz.js +++ b/_src/entrypoints/helpz.js @@ -10,7 +10,7 @@ function generateFormConfig(form) { // FormData ignores unchecked checkboxes, workaround form.querySelectorAll("input[type=checkbox]:not(:checked)"), ).forEach((elm) => { - formData[elm.name] = ""; + formData[elm["name"]] = ""; }); let conf = {}; for (const x in formData) { diff --git a/_src/entrypoints/notfound.js b/_src/entrypoints/notfound.js index 89e151f..aca3b5d 100644 --- a/_src/entrypoints/notfound.js +++ b/_src/entrypoints/notfound.js @@ -1,17 +1,18 @@ import "./default"; import "../styles/notfound.scss"; +// @ts-ignore import { issue_tag as IssueTag } from "virtual:jekyll-config"; const tag = `[${IssueTag}]`; const bugLink = document.getElementById("new_issue_bug"); -const bugURL = new URL(bugLink.href); +const bugURL = new URL(bugLink["href"]); bugURL.searchParams.append("title", tag + "404 at " + location.pathname); -bugLink.href = bugURL.href; +bugLink["href"] = bugURL.href; const mrLink = document.getElementById("new_issue_mr"); -const mrURL = new URL(mrLink.href); +const mrURL = new URL(mrLink["href"]); mrURL.searchParams.append( "title", tag + "Mirror Request for new mirror " + location.pathname.split("/")[1], ); -mrLink.href = mrURL.href; +mrLink["href"] = mrURL.href; diff --git a/_src/lib/mirrorList.js b/_src/lib/mirrorList.ts similarity index 82% rename from _src/lib/mirrorList.js rename to _src/lib/mirrorList.ts index 4529091..f141136 100644 --- a/_src/lib/mirrorList.js +++ b/_src/lib/mirrorList.ts @@ -1,12 +1,14 @@ import { TUNASYNC_JSON_PATH } from "../lib/consts"; +// @ts-ignore import { options as globalOptions } from "virtual:jekyll-data"; import { ref, onMounted, nextTick } from "vue"; import processingHandlers from "../lib/mirrorListDataProcessing"; +import { MirrorInfo } from "./types"; const { postProcessStatusData } = processingHandlers(globalOptions); -export const useMirrorList = (additional = []) => { - const mirrorList = ref([]); +export const useMirrorList = (additional: MirrorInfo[] = []) => { + const mirrorList = ref([] as MirrorInfo[]); let refreshTimer = null; const refreshMirrorList = async () => { @@ -15,7 +17,7 @@ export const useMirrorList = (additional = []) => { } try { const res = await fetch(TUNASYNC_JSON_PATH); - const status_data = await res.json(); + const status_data = (await res.json()) as MirrorInfo[]; mirrorList.value = postProcessStatusData(status_data, additional); } catch (e) { throw e; diff --git a/_src/lib/mirrorListDataProcessing.js b/_src/lib/mirrorListDataProcessing.ts similarity index 81% rename from _src/lib/mirrorListDataProcessing.js rename to _src/lib/mirrorListDataProcessing.ts index 6f96b8b..2cacdd3 100644 --- a/_src/lib/mirrorListDataProcessing.js +++ b/_src/lib/mirrorListDataProcessing.ts @@ -1,4 +1,5 @@ import { format as TimeAgoFormat } from "timeago.js"; +import { MirrorInfo } from "./types"; export default function (globalOptions) { const label_map = globalOptions.label_map; @@ -13,8 +14,8 @@ export default function (globalOptions) { globalOptions.mirror_desc.map((m) => [m.name, m.desc]), ); - const processLinkItem = (mirrors) => { - var processed = []; + const processLinkItem = (mirrors: MirrorInfo[]) => { + var processed: MirrorInfo[] = []; for (let d of mirrors) { if (d.link_to === undefined) { processed.push(d); @@ -30,8 +31,6 @@ export default function (globalOptions) { d.last_update_ago = target.last_update_ago; d.last_ended = target.last_ended; d.last_ended_ago = target.last_ended_ago; - d.last_schedule = target.last_schedule; - d.last_schedule_ago = target.last_schedule_ago; processed.push(d); break; } @@ -40,7 +39,7 @@ export default function (globalOptions) { return processed; }; - const stringifyTime = (ts) => { + const stringifyTime = (ts: number): [string, string] => { const date = new Date(ts * 1000); let str = ""; let ago = ""; @@ -56,7 +55,7 @@ export default function (globalOptions) { return [str, ago]; }; - const processMirrorItem = (d) => { + const processMirrorItem = (d: MirrorInfo): MirrorInfo => { if (d.is_master === undefined) { d.is_master = true; } @@ -64,7 +63,7 @@ export default function (globalOptions) { return d; } d.label = label_map[d.status]; - d.show_status = d.status != "success"; + d.show_status = d.status !== "success"; // Strip the second component of last_update [d.last_update, d.last_update_ago] = stringifyTime(d.last_update_ts); [d.last_ended, d.last_ended_ago] = stringifyTime(d.last_ended_ts); @@ -73,11 +72,11 @@ export default function (globalOptions) { return d; }; - const sortAndUniqMirrors = (mirs) => { + const sortAndUniqMirrors = (mirs: MirrorInfo[]): MirrorInfo[] => { mirs.sort((a, b) => { return a.name < b.name ? -1 : 1; }); - return mirs.reduce((acc, cur) => { + return mirs.reduce((acc: MirrorInfo[], cur: MirrorInfo) => { if (acc.length > 1 && acc[acc.length - 1].name == cur.name) { if (acc[acc.length - 1].last_update_ts && cur.last_update_ts) { if (acc[acc.length - 1].last_update_ts < cur.last_update_ts) { @@ -93,23 +92,25 @@ export default function (globalOptions) { }, []); }; - const postProcessStatusData = (status_data, additional) => { + const postProcessStatusData = ( + status_data: MirrorInfo[], + additional: MirrorInfo[], + ) => { const processed = status_data .concat(additional) .map((d) => processMirrorItem(d)); return sortAndUniqMirrors(processLinkItem(processed)); }; - const genMainMirrorList = (status_data, helpPages) => { + const genMainMirrorList = ( + status_data: MirrorInfo[], + helpPages: { [k: string]: string }, + ) => { return status_data .filter((d) => !(d.status == "disabled")) .map((d) => ({ ...d, - url: forceHelp[d.name] - ? helpPages[d.name] - : d.url - ? d.url - : `/${d.name}/`, + url: forceHelp[d.name] ? helpPages[d.name] : d.url ?? `/${d.name}/`, help_url: helpPages[d.name], is_new: Boolean(new_mirrors[d.name]), description: descriptions[d.name], diff --git a/_src/lib/types.ts b/_src/lib/types.ts new file mode 100644 index 0000000..363ccd1 --- /dev/null +++ b/_src/lib/types.ts @@ -0,0 +1,26 @@ +export interface FullMirrorInfo { + is_master: boolean; + last_ended: string; + last_ended_ts: number; + last_started: string; + last_started_ts: number; + last_update: string; + last_update_ts: number; + name: string; + next_schedule: string; + next_schedule_ts: number; + size: string; + status: string; + upstream: string; + link_to: string; + last_update_ago: string; + last_ended_ago: string; + last_started_ago: string; + next_schedule_ago: string; + label: string; + show_status: boolean; + /// The page of help page + url: string; +} + +export interface MirrorInfo extends Partial {} diff --git a/_src/tsconfig.json b/_src/tsconfig.json new file mode 100644 index 0000000..d54148e --- /dev/null +++ b/_src/tsconfig.json @@ -0,0 +1,13 @@ +{ + "include": ["**/*.ts", "**/*.js", "**/*.vue"], + "files": [ + "../node_modules/njs-types/ngx_http_js_module.d.ts", + "components.d.ts" + ], + "compilerOptions": { + "isolatedModules": true, + "target": "ESNext", + "moduleResolution": "NodeNext", + "module": "NodeNext" + } +} diff --git a/_vite.config.mjs b/_vite.config.mjs index ec337f0..5af72b9 100644 --- a/_vite.config.mjs +++ b/_vite.config.mjs @@ -12,6 +12,7 @@ import fs from "node:fs"; import { build as viteBuild, normalizePath } from "vite"; import glob from "fast-glob"; import { getBabelOutputPlugin } from "@rollup/plugin-babel"; +import typescript from "@rollup/plugin-typescript"; const visualizer = await (async () => { if (process.env.VISUALIZER) { @@ -148,7 +149,7 @@ export default defineConfig(({ mode }) => ({ return { name: "add-njs", config(config) { - savedConfig.minify = config.build?.minify ? 'terser' : false; + savedConfig.minify = config.build?.minify ? "terser" : false; savedConfig.root = config.root; savedConfig.mode = config.mode; savedConfig.njsFiles = glob.sync("entrypoints-njs/**", { @@ -177,6 +178,9 @@ export default defineConfig(({ mode }) => ({ configFile: false, logLevel, plugins: [ + typescript({ + tsconfig: path.join(root, "tsconfig.json"), + }), getBabelOutputPlugin({ presets: [ "babel-preset-njs", diff --git a/package-lock.json b/package-lock.json index 65415d3..1c2a155 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,8 +40,12 @@ "whatwg-fetch": "^3.6.20" }, "devDependencies": { + "@rollup/plugin-typescript": "^11.1.6", + "njs-types": "^0.8.2", "prettier": "^3.2.5", - "rollup-plugin-visualizer": "^5.12.0" + "rollup-plugin-visualizer": "^5.12.0", + "tslib": "^2.6.2", + "typescript": "^5.4.5" } }, "node_modules/@ampproject/remapping": { @@ -2191,6 +2195,32 @@ } } }, + "node_modules/@rollup/plugin-typescript": { + "version": "11.1.6", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.1.6.tgz", + "integrity": "sha512-R92yOmIACgYdJ7dJ97p4K69I8gg6IEHt8M7dUBxN3W6nrO8uUxX5ixl0yU/N3aZTi8WhPuICvOHXQvF6FaykAA==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.1.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.14.0||^3.0.0||^4.0.0", + "tslib": "*", + "typescript": ">=3.7.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + }, + "tslib": { + "optional": true + } + } + }, "node_modules/@rollup/pluginutils": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", @@ -3721,6 +3751,12 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/njs-types": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/njs-types/-/njs-types-0.8.2.tgz", + "integrity": "sha512-6uv1Tcb4khW45LHZqj5vu1uo7WbvB6nINZjDVmryOxiO3K7rDMqRVNJbYtLHOKNbGDqLygIip9iYZbFViIVGqA==", + "dev": true + }, "node_modules/node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", @@ -4455,6 +4491,25 @@ "node": ">=8.0" } }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "devOptional": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", diff --git a/package.json b/package.json index 7b515dd..6e8ea3b 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "private": true, "homepage": "https://github.com/tuna/mirror-web", "scripts": { - "format": "prettier --write \"_src/**/*.vue\" \"_src/**/*.html\" \"_src/**/*.js\" \"_src/**/*.scss\" *.mjs", + "format": "prettier --write \"_src/**/*.vue\" \"_src/**/*.html\" \"_src/**/*.js\" \"_src/**/*.scss\" \"_src/**/*.ts\" *.mjs", "postinstall": "patch-package --patch-dir _node_module_patch" }, "dependencies": { @@ -40,7 +40,11 @@ "whatwg-fetch": "^3.6.20" }, "devDependencies": { + "@rollup/plugin-typescript": "^11.1.6", + "njs-types": "^0.8.2", "prettier": "^3.2.5", - "rollup-plugin-visualizer": "^5.12.0" + "rollup-plugin-visualizer": "^5.12.0", + "tslib": "^2.6.2", + "typescript": "^5.4.5" } }