From 67279c3745c64e85b30d448d1b0ef2fb3b5cc5a4 Mon Sep 17 00:00:00 2001 From: undefined Date: Mon, 13 Mar 2023 04:25:44 +0800 Subject: [PATCH] ui: speed up page load --- package.json | 12 +++--- packages/hydrojudge/package.json | 4 +- packages/hydrooj/package.json | 18 ++++----- .../office.page.ts} | 16 ++------ packages/prom-client/package.json | 2 +- .../recaptcha/frontend/captcha-main.page.js | 16 ++++++++ .../recaptcha/public/captcha-main.page.js | 19 ---------- packages/ui-default/api.ts | 1 + packages/ui-default/components/monaco/nls.js | 28 ++------------ packages/ui-default/entry.js | 38 +++++++++---------- packages/ui-default/hydro.ts | 5 +-- packages/ui-default/package.json | 16 ++++---- .../ui-default/templates/layout/html5.html | 6 ++- packages/ui-default/utils/base.ts | 3 ++ packages/vjudge/package.json | 2 +- 15 files changed, 80 insertions(+), 106 deletions(-) rename packages/onlyoffice/{public/office.page.js => frontend/office.page.ts} (86%) create mode 100644 packages/recaptcha/frontend/captcha-main.page.js delete mode 100644 packages/recaptcha/public/captcha-main.page.js diff --git a/package.json b/package.json index 1af02dd7..7ad7d4e2 100644 --- a/package.json +++ b/package.json @@ -36,8 +36,8 @@ "@types/node": "^18.14.1", "@types/semver": "^7.3.13", "@types/supertest": "^2.0.12", - "@typescript-eslint/eslint-plugin": "^5.54.0", - "@typescript-eslint/parser": "^5.54.0", + "@typescript-eslint/eslint-plugin": "^5.54.1", + "@typescript-eslint/parser": "^5.54.1", "autocannon": "^7.10.0", "cac": "^6.7.14", "chokidar": "^3.5.3", @@ -48,17 +48,17 @@ "css-loader": "^6.7.3", "esbuild": "0.17.10", "esbuild-loader": "^2.21.0", - "eslint": "^8.35.0", + "eslint": "^8.36.0", "eslint-import-resolver-typescript": "^3.5.3", "eslint-import-resolver-webpack": "^0.13.2", "fs-extra": "^11.1.0", "globby": "13.1.3", "inspectpack": "^4.7.1", "latest-version": "7.0.0", - "mini-css-extract-plugin": "^2.7.2", + "mini-css-extract-plugin": "^2.7.3", "monaco-editor-webpack-plugin": "^7.0.1", "mongodb": "^5.1.0", - "mongodb-memory-server": "^8.11.5", + "mongodb-memory-server": "^8.12.0", "nyc": "^15.1.0", "ora": "^6.1.2", "postcss-loader": "^7.0.2", @@ -69,7 +69,7 @@ "supertest": "^6.3.3", "ts-loader": "^9.4.2", "typescript": "4.9.5", - "webpack": "^5.75.0", + "webpack": "^5.76.1", "webpack-bundle-analyzer": "^4.8.0", "webpack-dev-server": "^4.11.1", "webpack-manifest-plugin": "^5.0.0", diff --git a/packages/hydrojudge/package.json b/packages/hydrojudge/package.json index 04241d95..282768ad 100644 --- a/packages/hydrojudge/package.json +++ b/packages/hydrojudge/package.json @@ -10,10 +10,10 @@ "cac": "^6.7.14", "mongodb": "^5.1.0", "p-queue": "^7.3.4", - "schemastery": "^3.7.1", + "schemastery": "^3.7.2", "shell-quote": "^1.8.0", "superagent": "^8.0.9", - "ws": "^8.12.1" + "ws": "^8.13.0" }, "preferUnplugged": true, "license": "AGPL-3.0-or-later", diff --git a/packages/hydrooj/package.json b/packages/hydrooj/package.json index de171414..1f852039 100644 --- a/packages/hydrooj/package.json +++ b/packages/hydrooj/package.json @@ -12,17 +12,17 @@ }, "preferUnplugged": true, "dependencies": { - "@aws-sdk/client-s3": "^3.282.0", - "@aws-sdk/lib-storage": "^3.282.0", - "@aws-sdk/middleware-endpoint": "^3.282.0", - "@aws-sdk/s3-presigned-post": "^3.282.0", - "@aws-sdk/s3-request-presigner": "^3.282.0", - "@graphql-tools/schema": "^9.0.16", + "@aws-sdk/client-s3": "^3.289.0", + "@aws-sdk/lib-storage": "^3.289.0", + "@aws-sdk/middleware-endpoint": "^3.289.0", + "@aws-sdk/s3-presigned-post": "^3.289.0", + "@aws-sdk/s3-request-presigner": "^3.289.0", + "@graphql-tools/schema": "^9.0.17", "@hydrooj/utils": "workspace:*", "@simplewebauthn/server": "^7.0.1", "adm-zip": "0.5.5", "cac": "^6.7.14", - "cordis": "^2.7.3", + "cordis": "^2.7.4", "detect-browser": "^5.3.0", "emoji-regex": "^10.2.1", "emojis-list": "2.1.0", @@ -48,13 +48,13 @@ "path-to-regexp": "^6.2.1", "require-resolve-hook": "^1.1.0", "saslprep": "^1.0.3", - "schemastery": "^3.7.1", + "schemastery": "^3.7.2", "semver": "^7.3.8", "serialize-javascript": "^6.0.1", "superagent": "^8.0.9", "tar": "^6.1.13", "thirty-two": "^1.0.2", - "ws": "^8.12.1" + "ws": "^8.13.0" }, "devDependencies": { "@types/adm-zip": "^0.4.34", diff --git a/packages/onlyoffice/public/office.page.js b/packages/onlyoffice/frontend/office.page.ts similarity index 86% rename from packages/onlyoffice/public/office.page.js rename to packages/onlyoffice/frontend/office.page.ts index 50a0bda2..602c812c 100644 --- a/packages/onlyoffice/public/office.page.js +++ b/packages/onlyoffice/frontend/office.page.ts @@ -1,6 +1,4 @@ -import { $ } from '@hydrooj/ui-default'; - -const { AutoloadPage } = window.Hydro; +import { $, addPage, AutoloadPage } from '@hydrooj/ui-default'; let loaded = false; async function load() { @@ -64,18 +62,12 @@ const loader = (mode) => async (element) => { }); }; -const getEles = (types) => { - const eles = []; - for (const type of types) eles.push(...$(`div[data-${type}]`).get()); - return eles; -}; +const getEles = (types: string[]) => types.flatMap((type) => $(`div[data-${type}]`).get()); -const page = new AutoloadPage('onlyoffice', async () => { +addPage(new AutoloadPage('onlyoffice', async () => { const all = getEles(['doc', 'docx', 'cell', 'xls', 'xlsx', 'slide', 'ppt', 'pptx']); if (all.length) await load(); getEles(['doc', 'docx']).forEach(loader('word')); getEles(['cell', 'xls', 'xlsx']).forEach(loader('cell')); getEles(['slide', 'ppt', 'pptx']).forEach(loader('slide')); -}); - -window.Hydro.extraPages.push(page); +})); diff --git a/packages/prom-client/package.json b/packages/prom-client/package.json index 94b78e1f..8b14f5da 100644 --- a/packages/prom-client/package.json +++ b/packages/prom-client/package.json @@ -3,6 +3,6 @@ "version": "0.1.5", "main": "index.ts", "dependencies": { - "prom-client": "^14.1.1" + "prom-client": "^14.2.0" } } diff --git a/packages/recaptcha/frontend/captcha-main.page.js b/packages/recaptcha/frontend/captcha-main.page.js new file mode 100644 index 00000000..8f016d9c --- /dev/null +++ b/packages/recaptcha/frontend/captcha-main.page.js @@ -0,0 +1,16 @@ +import { addPage, NamedPage } from '@hydrooj/ui-default'; +/* global grecaptcha */ + +addPage(new NamedPage('user_register', () => { + function captcha(event) { + event.preventDefault(); + grecaptcha.ready(() => { + grecaptcha.execute(UiContext.recaptchaKey, { action: 'submit' }).then((token) => { + document.getElementById('_captcha').value = token; + document.getElementById('_submit').click(); + }); + }); + } + const element = document.getElementById('submit'); + if (element) element.onclick = captcha; +})); diff --git a/packages/recaptcha/public/captcha-main.page.js b/packages/recaptcha/public/captcha-main.page.js deleted file mode 100644 index 383cce2e..00000000 --- a/packages/recaptcha/public/captcha-main.page.js +++ /dev/null @@ -1,19 +0,0 @@ -(() => { - const { NamedPage } = window.Hydro; - - const page = new NamedPage('user_register', () => { - function captcha(event) { - event.preventDefault(); - grecaptcha.ready(function () { - grecaptcha.execute(UiContext.recaptchaKey, { action: 'submit' }).then(function (token) { - document.getElementById('_captcha').value = token; - document.getElementById('_submit').click(); - }); - }); - } - const element = document.getElementById('submit'); - if (element) element.onclick = captcha; - }); - - window.Hydro.extraPages.push(page); -})(); \ No newline at end of file diff --git a/packages/ui-default/api.ts b/packages/ui-default/api.ts index e18b4d24..24b8e7eb 100644 --- a/packages/ui-default/api.ts +++ b/packages/ui-default/api.ts @@ -14,6 +14,7 @@ export { default as _ } from 'lodash'; export { default as React } from 'react'; export { default as ReactDOM } from 'react-dom/client'; export * from './misc/Page'; +export { initPageLoader } from './hydro'; const lazyModules = {}; export default async function load(name: string) { diff --git a/packages/ui-default/components/monaco/nls.js b/packages/ui-default/components/monaco/nls.js index 4cd5d63e..765778a0 100644 --- a/packages/ui-default/components/monaco/nls.js +++ b/packages/ui-default/components/monaco/nls.js @@ -1,11 +1,7 @@ -/* eslint-disable camelcase */ -import en_GB from 'monaco-editor-nls/locale/en-gb.json'; - function format(message, args) { let result; - if (args.length === 0) { - result = message; - } else { + if (!args.length) result = message; + else { result = String(message).replace(/\{(\d+)\}/g, (match, rest) => { const index = rest[0]; return typeof args[index] !== 'undefined' ? args[index] : match; @@ -18,28 +14,12 @@ export const getConfiguredDefaultLocale = () => 'zh'; let CURRENT_LOCALE_DATA = {}; // eslint-disable-line @typescript-eslint/naming-convention -function find(path, message) { - for (const key of Object.keys(CURRENT_LOCALE_DATA)) { - if (!CURRENT_LOCALE_DATA[key] || !en_GB[key]) continue; - if (CURRENT_LOCALE_DATA[key][path] && en_GB[key][path] === message) { - return CURRENT_LOCALE_DATA[key][path]; - } - } - for (const key of Object.keys(CURRENT_LOCALE_DATA)) { - if (!CURRENT_LOCALE_DATA[key]) continue; - if (CURRENT_LOCALE_DATA[key][path]) { - return CURRENT_LOCALE_DATA[key][path]; - } - } - return message; -} - export function localize(path, message, ...args) { - return format(find(path.key || path, message), args); + return format(CURRENT_LOCALE_DATA[path.key || path] || message, args); } export function setLocaleData(data) { - CURRENT_LOCALE_DATA = data; + CURRENT_LOCALE_DATA = Object.assign(...Object.values(data)); } export function loadMessageBundle() { diff --git a/packages/ui-default/entry.js b/packages/ui-default/entry.js index c96d9233..ad19e1b4 100644 --- a/packages/ui-default/entry.js +++ b/packages/ui-default/entry.js @@ -26,30 +26,30 @@ console.log( ); window.UiContext = JSON.parse(window.UiContext); +window.UserContext = JSON.parse(window.UserContext); +try { __webpack_public_path__ = UiContext.cdn_prefix; } catch (e) { } if ('serviceWorker' in navigator) { - window.addEventListener('load', () => { - const encodedConfig = encodeURIComponent(JSON.stringify(UiContext.SWConfig)); - navigator.serviceWorker.register(`/service-worker.js?config=${encodedConfig}`).then((registration) => { - console.log('SW registered: ', registration); - }).catch((registrationError) => { - console.log('SW registration failed: ', registrationError); - }); + const encodedConfig = encodeURIComponent(JSON.stringify(UiContext.SWConfig)); + navigator.serviceWorker.register(`/service-worker.js?config=${encodedConfig}`).then((registration) => { + console.log('SW registered: ', registration); + }).catch((registrationError) => { + console.log('SW registration failed: ', registrationError); }); } -document.addEventListener('DOMContentLoaded', async () => { - const PageLoader = ''; - $('body').prepend(PageLoader); - $('.page-loader').fadeIn(500); - // eslint-disable-next-line camelcase - try { __webpack_public_path__ = UiContext.cdn_prefix; } catch (e) { } +const PageLoader = ''; +$('body').prepend(PageLoader); +$('.page-loader').fadeIn(500); + +const prefetch = Promise.all([ + fetch(`/constant/${UiContext.constantVersion}.js`).then((r) => r.text()), + import('./api'), +]); - const [data, HydroExports] = await Promise.all([ - fetch(`/constant/${UiContext.constantVersion}.js`).then((r) => r.text()), - import('./api'), - ]); +document.addEventListener('DOMContentLoaded', async () => { + Object.assign(window.UiContext, JSON.parse(window.UiContextNew)) + const [data, HydroExports] = await prefetch; Object.assign(window, { HydroExports }); eval(data); // eslint-disable-line no-eval - - import('./hydro'); + await HydroExports.initPageLoader(); }, false); diff --git a/packages/ui-default/hydro.ts b/packages/ui-default/hydro.ts index 870de14d..3bfcfcd4 100644 --- a/packages/ui-default/hydro.ts +++ b/packages/ui-default/hydro.ts @@ -17,7 +17,6 @@ declare global { } const start = new Date(); -window.UserContext = JSON.parse(window.UserContext); function buildSequence(pages, type) { if (process.env.NODE_ENV !== 'production') { @@ -52,7 +51,7 @@ async function animate() { } } -async function load() { +export async function initPageLoader() { const pageLoader = new PageLoader(); const currentPageName = document.documentElement.getAttribute('data-page'); @@ -99,5 +98,3 @@ async function load() { $('.section').trigger('vjLayout'); $(document).trigger('vjPageFullyInitialized'); } - -load(); diff --git a/packages/ui-default/package.json b/packages/ui-default/package.json index b42508a6..ab085941 100644 --- a/packages/ui-default/package.json +++ b/packages/ui-default/package.json @@ -36,17 +36,17 @@ "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", "@types/redux-logger": "^3.0.9", - "@types/serviceworker": "^0.0.62", - "@types/sharedworker": "^0.0.91", + "@types/serviceworker": "^0.0.64", + "@types/sharedworker": "^0.0.93", "@types/webpack-env": "^1.18.0", "@vscode/codicons": "^0.0.32", "allotment": "^1.18.1", - "autoprefixer": "^10.4.13", + "autoprefixer": "^10.4.14", "browser-update": "^3.3.44", "chalk": "^5.2.0", "classnames": "^2.3.2", "clipboard": "^2.0.11", - "cordis": "^2.7.3", + "cordis": "^2.7.4", "diff": "^5.1.0", "diff-dom": "^5.0.4", "echarts": "^5.4.1", @@ -60,14 +60,14 @@ "gulp-if": "^3.0.0", "gulp-plumber": "^1.2.1", "gulp-svgmin": "^4.1.0", - "jquery": "^3.6.3", + "jquery": "^3.6.4", "jquery-scroll-lock": "^3.1.3", "jquery.easing": "^1.4.1", "jquery.transit": "^0.9.12", "matchmedia-polyfill": "^0.3.2", "moment": "^2.29.4", "monaco-editor": "0.36.1", - "monaco-editor-nls": "^2.0.0", + "monaco-editor-nls": "^3.0.0", "monaco-themes": "^0.4.3", "monaco-yaml": "^4.0.4", "nanoid": "^4.0.1", @@ -94,13 +94,13 @@ "redux-promise-middleware": "^6.1.3", "redux-thunk": "^2.4.2", "rupture": "^0.7.1", - "schemastery": "^3.7.1", + "schemastery": "^3.7.2", "slideout": "^1.0.1", "sticky-kit": "^1.1.3", "tether": "1.4.7", "tether-drop": "^1.4.2", "through2": "^4.0.2", - "timeago-react": "^3.0.5", + "timeago-react": "^3.0.6", "timeago.js": "^4.0.2", "vditor": "^3.9.0", "vinyl-buffer": "^1.0.1", diff --git a/packages/ui-default/templates/layout/html5.html b/packages/ui-default/templates/layout/html5.html index df0678c3..eba4eb1c 100644 --- a/packages/ui-default/templates/layout/html5.html +++ b/packages/ui-default/templates/layout/html5.html @@ -58,12 +58,12 @@ {% endif %} - {% block body %}{% endblock %} {% if not isIE(handler.request.headers['user-agent']) and not handler.session.legacy %} + {% set UiContext = Object.create(UiContext) %} {% if process.env.DEV %} @@ -72,6 +72,10 @@ {% endif %} {% endif %} + {% block body %}{% endblock %} {% block script %}{% endblock %} + {% if not isIE(handler.request.headers['user-agent']) and not handler.session.legacy %} + + {% endif %} diff --git a/packages/ui-default/utils/base.ts b/packages/ui-default/utils/base.ts index 1e72cbbf..82831f43 100644 --- a/packages/ui-default/utils/base.ts +++ b/packages/ui-default/utils/base.ts @@ -1,5 +1,7 @@ import $ from 'jquery'; import _ from 'lodash'; +import React from 'react'; +import DOMServer from 'react-dom/server.browser'; export function substitute(str: string, obj: any) { return str.replace(/\{([^{}]+)\}/g, (match, key) => { @@ -32,6 +34,7 @@ export function secureRandomString(digit = 32, dict = defaultDict) { type Substitution = string | number | { templateRaw: true, html: string }; export function tpl(pieces: TemplateStringsArray, ...substitutions: Substitution[]) { + if (React.isValidElement(pieces)) return DOMServer.renderToStaticMarkup(pieces); let result = pieces[0]; for (let i = 0; i < substitutions.length; ++i) { const subst = substitutions[i]; diff --git a/packages/vjudge/package.json b/packages/vjudge/package.json index dfe6d9cd..06c1ad0d 100644 --- a/packages/vjudge/package.json +++ b/packages/vjudge/package.json @@ -9,7 +9,7 @@ "preferUnplugged": true, "dependencies": { "@hydrooj/utils": "workspace:*", - "jsdom": "^21.1.0", + "jsdom": "^21.1.1", "superagent-proxy": "^3.0.0" }, "devDependencies": {