diff --git a/packages/ui-default/build/config/gulp.js b/packages/ui-default/build/config/gulp.js index 81455936..7610719e 100644 --- a/packages/ui-default/build/config/gulp.js +++ b/packages/ui-default/build/config/gulp.js @@ -1,15 +1,15 @@ /* eslint-disable no-restricted-syntax */ /* eslint-disable guard-for-in */ /* eslint-disable import/no-extraneous-dependencies */ -import _ from 'lodash'; -import gulp from 'gulp'; -import log from 'fancy-log'; import chalk from 'chalk'; -import svgmin from 'gulp-svgmin'; -import vinylBuffer from 'vinyl-buffer'; +import log from 'fancy-log'; +import gulp from 'gulp'; import iconfont from 'gulp-iconfont'; -import plumber from 'gulp-plumber'; import gulpif from 'gulp-if'; +import plumber from 'gulp-plumber'; +import svgmin from 'gulp-svgmin'; +import _ from 'lodash'; +import vinylBuffer from 'vinyl-buffer'; import nunjucks from '../plugins/gulpNunjucks'; import vjTouch from '../plugins/gulpTouch'; diff --git a/packages/ui-default/build/config/webpack.ts b/packages/ui-default/build/config/webpack.ts index 0de553a6..f9901c36 100644 --- a/packages/ui-default/build/config/webpack.ts +++ b/packages/ui-default/build/config/webpack.ts @@ -217,7 +217,6 @@ export default function (env: { production?: boolean, measure?: boolean } = {}) $: 'jquery', jQuery: 'jquery', 'window.jQuery': 'jquery', - katex: 'katex/dist/katex.js', React: 'react', monaco: 'monaco-editor/esm/vs/editor/editor.api', }), diff --git a/packages/ui-default/build/main.ts b/packages/ui-default/build/main.ts index 14d740c6..47e9af63 100644 --- a/packages/ui-default/build/main.ts +++ b/packages/ui-default/build/main.ts @@ -1,11 +1,14 @@ /* eslint-disable import/no-import-module-exports */ -/* eslint-disable import/no-extraneous-dependencies */ +import { size } from '@hydrooj/utils/lib/utils'; import cac from 'cac'; import chalk from 'chalk'; +import { build } from 'esbuild'; import log from 'fancy-log'; import fs from 'fs-extra'; import gulp from 'gulp'; -import webpack from 'webpack'; +import { sum } from 'lodash'; +import path from 'path'; +import webpack, { Stats } from 'webpack'; import WebpackDevServer from 'webpack-dev-server'; import pkg from '../package.json'; import gulpConfig from './config/gulp'; @@ -24,14 +27,15 @@ async function runWebpack({ compress: true, hot: true, proxy: { - context: (path) => path !== '/ws', + context: (p) => p !== '/ws', target: 'http://localhost:2333', ws: true, }, }, compiler); - return server.start(); + server.start(); + return; } - return new Promise((resolve, reject) => { + const res = await new Promise((resolve, reject) => { function compilerCallback(err, stats) { if (err) { console.error(err.stack || err); @@ -42,11 +46,40 @@ async function runWebpack({ } if (argv.options.detail) console.log(stats.toString()); if (!watch && (!stats || stats.hasErrors())) process.exitCode = 1; - resolve(null); + resolve(stats); } if (watch) compiler.watch({}, compilerCallback); else compiler.run(compilerCallback); }); + if (production && res && !res.hasErrors()) { + const stats = {}; + const files = fs.readdirSync(root('public'), { withFileTypes: true }); + for (const file of files) { + if (!file.isFile() || file.name.endsWith('.map')) continue; + const data = await fs.stat(path.join(root('public'), file.name)); + stats[file.name.replace(/\.[a-f0-9]{10}\./, '.')] = data.size; + } + const statsPath = root('__bundleInfo'); + if (fs.existsSync(statsPath)) { + log('Compare to last production bundle:'); + const oldStats = JSON.parse(await fs.readFile(statsPath, 'utf-8')) as Record; + for (const key in stats) if (!oldStats[key]) oldStats[key] = 0; + const entries: [filename: string, orig: number, curr: number][] = []; + for (const [key, value] of Object.entries(oldStats)) { + if (Math.abs((stats[key] || 0) - value) > 25) entries.push([key, value, stats[key] || 0]); + } + const sorted = entries.sort((i) => i[1] - i[2]); + sorted.push(['Total', sum(sorted.map((i) => i[1])), sum(sorted.map((i) => i[2]))]); + for (const entry of sorted) { + const [name, orig, curr] = entry; + const diff = 100 * (curr - orig) / orig; + if (Math.abs(diff) < 0.01 && name !== 'Total') continue; + const color = orig > curr ? chalk.green : chalk.red; + log(color(`${name.padStart(35)} ${size(orig).padStart(10)} -> ${size(curr).padEnd(10)} (${diff.toPrecision(5)}%)`), chalk.reset()); + } + } + await fs.writeFile(statsPath, JSON.stringify(stats)); + } } async function runGulp() { diff --git a/packages/ui-default/build/plugins/gulpNunjucks.js b/packages/ui-default/build/plugins/gulpNunjucks.js index dffa94c0..e1556b2f 100644 --- a/packages/ui-default/build/plugins/gulpNunjucks.js +++ b/packages/ui-default/build/plugins/gulpNunjucks.js @@ -1,5 +1,5 @@ -import through from 'through2'; import nunjucks from 'nunjucks'; +import through from 'through2'; export default function compile(data, options = {}) { return through.obj(function (file, encoding, callback) { diff --git a/packages/ui-default/build/plugins/gulpTouch.js b/packages/ui-default/build/plugins/gulpTouch.js index c1ab2d1b..58fc26a8 100644 --- a/packages/ui-default/build/plugins/gulpTouch.js +++ b/packages/ui-default/build/plugins/gulpTouch.js @@ -1,7 +1,7 @@ /* eslint-disable import/no-extraneous-dependencies */ +import fs from 'fs-extra'; import PluginError from 'plugin-error'; import through from 'through2'; -import fs from 'fs-extra'; export default function touch(mtime) { async function touchFile(file) { diff --git a/packages/ui-default/components/form/button.page.98.styl b/packages/ui-default/components/form/button.page.98.styl deleted file mode 100644 index ac7e7d83..00000000 --- a/packages/ui-default/components/form/button.page.98.styl +++ /dev/null @@ -1,32 +0,0 @@ -@import './var.inc.styl' - -.button - box-sizing: border-box - border: none - background: #c0c0c0 - border-radius: 0 - min-width: 75px; - min-height: 23px; - padding: 0 12px; - - &:not(:disabled) - &:active - box-shadow: --border-sunken-outer, --border-sunken-inner - padding: 2px 11px 0 13px; - -.button.inverse - line-height: rem($form-control-height - 4) - border: 2px solid #FFF - background: none - - &, &:visited - &, .typo & - color: #FFF - - &:not(.disabled) - &:hover, &:focus - border-color: $immersive-primary-color - background: none - box-shadow: none - &, .typo & - color: $immersive-primary-color diff --git a/packages/ui-default/components/highlighter/code-example.js b/packages/ui-default/components/highlighter/code-example.js index f64616dc..e4b7017c 100644 --- a/packages/ui-default/components/highlighter/code-example.js +++ b/packages/ui-default/components/highlighter/code-example.js @@ -1,4 +1,5 @@ -export default `#include +export default `\ +#include int main() { // ~ Switch on the power line / Remember to put on PROTECTION ~ diff --git a/packages/ui-default/components/highlighter/codemirror.page.styl b/packages/ui-default/components/highlighter/codemirror.page.styl deleted file mode 100644 index 8daa5f35..00000000 --- a/packages/ui-default/components/highlighter/codemirror.page.styl +++ /dev/null @@ -1,10 +0,0 @@ -@css { -span.cm-comment { color: #008000; } -span.cm-keyword, span.cm-variable-3 { line-height: 1em; color: #00f; } -span.cm-string { color: #a31515; } -span.cm-builtin { line-height: 1em; font-weight: bold; color: #077; } -span.cm-special { line-height: 1em; font-weight: bold; color: #0aa; } -span.cm-variable { color: black; } -span.cm-meta { color: #2b91af; } -span.cm-link { color: #3a3; } -} diff --git a/packages/ui-default/components/hitokoto/index.page.js b/packages/ui-default/components/hitokoto/index.page.js index d70b0dcc..4ab68d78 100644 --- a/packages/ui-default/components/hitokoto/index.page.js +++ b/packages/ui-default/components/hitokoto/index.page.js @@ -1,10 +1,10 @@ import $ from 'jquery'; -import { AutoloadPage } from 'vj/misc/Page'; +import { NamedPage } from 'vj/misc/Page'; import i18n from 'vj/utils/i18n'; import request from 'vj/utils/request'; import tpl from 'vj/utils/tpl'; -const hitokotoPage = new AutoloadPage('hitokotoPage', () => { +export default new NamedPage('homepage', () => { function getHitokoto($containers) { $containers.get().forEach((container) => { request.get('https://v1.hitokoto.cn?c=a&c=b&c=c&c=d&c=e&c=f') @@ -20,10 +20,4 @@ const hitokotoPage = new AutoloadPage('hitokotoPage', () => { }); } if ($('[name="hitokoto"]')) getHitokoto($('[name="hitokoto"]')); - $(document).on('vjContentNew', (e) => { - const elem = $(e.target).find('[name="hitokoto"]'); - if (elem.get) getHitokoto(elem); - }); }); - -export default hitokotoPage; diff --git a/packages/ui-default/components/katex/katex.styl b/packages/ui-default/components/katex.page.styl similarity index 100% rename from packages/ui-default/components/katex/katex.styl rename to packages/ui-default/components/katex.page.styl diff --git a/packages/ui-default/components/katex/katex.page.js b/packages/ui-default/components/katex/katex.page.js deleted file mode 100644 index f3a4c970..00000000 --- a/packages/ui-default/components/katex/katex.page.js +++ /dev/null @@ -1,26 +0,0 @@ -import $ from 'jquery'; -import { AutoloadPage } from 'vj/misc/Page'; - -const katexPage = new AutoloadPage('katexPage', () => { - import('katex/contrib/auto-render/auto-render').then(({ default: katex }) => { - function runKatex($containers) { - $containers.get().forEach((container) => katex(container, { - delimiters: [ - { left: '$$', right: '$$', display: true }, - { left: '$', right: '$', display: false }, - { left: '\\(', right: '\\)', display: false }, - { left: '\\[', right: '\\]', display: true }, - { left: '\\begin{equation}', right: '\\end{equation}', display: true }, - { left: '\\begin{align}', right: '\\end{align}', display: true }, - { left: '\\begin{alignat}', right: '\\end{alignat}', display: true }, - { left: '\\begin{gather}', right: '\\end{gather}', display: true }, - { left: '\\begin{CD}', right: '\\end{CD}', display: true }, - ], - })); - } - runKatex($('.typo')); - $(document).on('vjContentNew', (e) => runKatex($(e.target).find('.typo'))); - }); -}); - -export default katexPage; diff --git a/packages/ui-default/components/nprogress/nprogress.page.styl b/packages/ui-default/components/nprogress.page.styl similarity index 100% rename from packages/ui-default/components/nprogress/nprogress.page.styl rename to packages/ui-default/components/nprogress.page.styl diff --git a/packages/ui-default/components/nprogress/index.js b/packages/ui-default/components/nprogress/index.js deleted file mode 100644 index e7edcffe..00000000 --- a/packages/ui-default/components/nprogress/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import NProgress from 'nprogress'; - -export default NProgress; diff --git a/packages/ui-default/constant/domain.js b/packages/ui-default/constant/domain.js index ba52b020..ae361878 100644 --- a/packages/ui-default/constant/domain.js +++ b/packages/ui-default/constant/domain.js @@ -1,5 +1,3 @@ -import attachObjectMeta from './util/objectMeta'; - export const JOIN_METHOD_NONE = 0; export const JOIN_METHOD_ALL = 1; export const JOIN_METHOD_CODE = 2; @@ -8,7 +6,6 @@ export const JOIN_METHOD_RANGE = { [JOIN_METHOD_ALL]: 'Any user is allowed to join this domain', [JOIN_METHOD_CODE]: 'Any user is allowed to join this domain with an invitation code', }; -attachObjectMeta(JOIN_METHOD_RANGE, 'intKey', true); export const JOIN_EXPIRATION_KEEP_CURRENT = 0; export const JOIN_EXPIRATION_UNLIMITED = -1; @@ -22,4 +19,3 @@ export const JOIN_EXPIRATION_RANGE = { [24 * 30]: 'In 1 month', [JOIN_EXPIRATION_UNLIMITED]: 'Never expire', }; -attachObjectMeta(JOIN_EXPIRATION_RANGE, 'intKey', true); diff --git a/packages/ui-default/constant/model.js b/packages/ui-default/constant/model.js deleted file mode 100644 index 634ef613..00000000 --- a/packages/ui-default/constant/model.js +++ /dev/null @@ -1,18 +0,0 @@ -import attachObjectMeta from './util/objectMeta'; - -export const USER_GENDER_MALE = 0; -export const USER_GENDER_FEMALE = 1; -export const USER_GENDER_OTHER = 2; -export const USER_GENDERS = [USER_GENDER_MALE, USER_GENDER_FEMALE, USER_GENDER_OTHER]; -export const USER_GENDER_RANGE = { - [USER_GENDER_MALE]: 'Boy ♂', - [USER_GENDER_FEMALE]: 'Girl ♀', - [USER_GENDER_OTHER]: 'Other', -}; -attachObjectMeta(USER_GENDER_RANGE, 'intKey', true); -export const USER_GENDER_ICONS = { - [USER_GENDER_MALE]: '♂', - [USER_GENDER_FEMALE]: '♀', - [USER_GENDER_OTHER]: '?', -}; -attachObjectMeta(USER_GENDER_ICONS, 'intKey', true); diff --git a/packages/ui-default/constant/record.js b/packages/ui-default/constant/record.js index 867b035f..bbd95b2b 100644 --- a/packages/ui-default/constant/record.js +++ b/packages/ui-default/constant/record.js @@ -1,5 +1,3 @@ -import attachObjectMeta from './util/objectMeta'; - // Please note that accepted < others, rp system uses this feature. export const STATUS_WAITING = 0; export const STATUS_ACCEPTED = 1; @@ -34,7 +32,6 @@ export const STATUS_TEXTS = { [STATUS_FETCHED]: 'Fetched', [STATUS_IGNORED]: 'Ignored', }; -attachObjectMeta(STATUS_TEXTS, 'intKey', true); export const STATUS_CODES = { [STATUS_WAITING]: 'pending', @@ -53,7 +50,6 @@ export const STATUS_CODES = { [STATUS_FETCHED]: 'progress', [STATUS_IGNORED]: 'ignored', }; -attachObjectMeta(STATUS_CODES, 'intKey', true); /** * Whether to show detail about each test case for a submission status @@ -74,7 +70,6 @@ export const STATUS_SCRATCHPAD_SHOW_DETAIL_FLAGS = { [STATUS_FETCHED]: false, [STATUS_IGNORED]: false, }; -attachObjectMeta(STATUS_SCRATCHPAD_SHOW_DETAIL_FLAGS, 'exportToPython', false); /** * Short text to show in Scratchpad mode @@ -86,4 +81,3 @@ export const STATUS_SCRATCHPAD_SHORT_TEXTS = { [STATUS_MEMORY_LIMIT_EXCEEDED]: 'MLE', [STATUS_RUNTIME_ERROR]: 'RTE', }; -attachObjectMeta(STATUS_SCRATCHPAD_SHORT_TEXTS, 'exportToPython', false); diff --git a/packages/ui-default/constant/setting.js b/packages/ui-default/constant/setting.js deleted file mode 100644 index a2a919d6..00000000 --- a/packages/ui-default/constant/setting.js +++ /dev/null @@ -1,17 +0,0 @@ -import attachObjectMeta from './util/objectMeta'; - -export const PRIVACY_PUBLIC = 0; -export const PRIVACY_REGISTERED_ONLY = 1; -export const PRIVACY_SECRET = 2; -export const PRIVACY_RANGE = { - [PRIVACY_PUBLIC]: 'Public', - [PRIVACY_REGISTERED_ONLY]: 'Visible to registered users', - [PRIVACY_SECRET]: 'Secret', -}; -attachObjectMeta(PRIVACY_RANGE, 'intKey', true); - -export const FUNCTION_RANGE = { - 0: 'Disabled', - 1: 'Enabled', -}; -attachObjectMeta(FUNCTION_RANGE, 'intKey', true); diff --git a/packages/ui-default/constant/util/objectMeta.js b/packages/ui-default/constant/util/objectMeta.js deleted file mode 100644 index f51df3a4..00000000 --- a/packages/ui-default/constant/util/objectMeta.js +++ /dev/null @@ -1,8 +0,0 @@ -export default function attachObjectMeta(obj, key, value) { - Object.defineProperty(obj, `__${key}`, { - value, - enumerable: false, - configurable: false, - writable: false, - }); -} diff --git a/packages/ui-default/modules.js b/packages/ui-default/modules.js index 633abe95..dc9e6000 100644 --- a/packages/ui-default/modules.js +++ b/packages/ui-default/modules.js @@ -4,7 +4,6 @@ import './utils/i18n'; import './utils/loadReactRedux'; import './utils/mediaQuery'; import './utils/mongoId'; -import './utils/parseQueryString'; import './utils/pipeStream'; import './utils/pjax'; import './utils/request'; @@ -16,7 +15,6 @@ import './utils/zip'; import './components/autocomplete'; import './components/dialog'; import './components/notification'; -import './components/nprogress'; import './components/monaco/loader'; import $ from 'jquery'; diff --git a/packages/ui-default/pages/files.page.js b/packages/ui-default/pages/files.page.js index 0688ebac..86bcc54a 100644 --- a/packages/ui-default/pages/files.page.js +++ b/packages/ui-default/pages/files.page.js @@ -88,7 +88,6 @@ async function handleClickUpload(files) { Notification.success(i18n('File uploaded successfully.')); await pjax.request({ push: false }); } catch (e) { - window.captureException?.(e); console.error(e); Notification.error(i18n('File upload failed: {0}', e.toString())); } finally { diff --git a/packages/ui-default/pages/home_messages.page.jsx b/packages/ui-default/pages/home_messages.page.jsx index fad56c91..15ef3ba6 100644 --- a/packages/ui-default/pages/home_messages.page.jsx +++ b/packages/ui-default/pages/home_messages.page.jsx @@ -7,7 +7,6 @@ import VjNotification from 'vj/components/notification'; import { NamedPage } from 'vj/misc/Page'; import api, { gql } from 'vj/utils/api'; import loadReactRedux from 'vj/utils/loadReactRedux'; -import parseQueryString from 'vj/utils/parseQueryString'; const page = new NamedPage('home_messages', () => { let reduxStore; @@ -92,10 +91,10 @@ const page = new NamedPage('home_messages', () => { * A target user id may be assigned in the query string. */ async function loadSendTarget() { - const queryString = parseQueryString(); - if (!queryString.target) return; + const target = new URL(window.location.href).searchParams.get('target'); + if (!target) return; const user = await api(gql` - users(search: ${queryString.target}, exact: true) { + users(search: ${target}, exact: true) { _id uname avatarUrl diff --git a/packages/ui-default/pages/problem_files.page.js b/packages/ui-default/pages/problem_files.page.js index 5f853885..95bd71eb 100644 --- a/packages/ui-default/pages/problem_files.page.js +++ b/packages/ui-default/pages/problem_files.page.js @@ -100,7 +100,6 @@ const page = new NamedPage('problem_files', () => { Notification.success(i18n('File uploaded successfully.')); await pjax.request({ push: false }); } catch (e) { - window.captureException?.(e); console.error(e); Notification.error(i18n('File upload failed: {0}', e.toString())); } finally { diff --git a/packages/ui-default/theme/default.js b/packages/ui-default/theme/default.js index c8d3dc55..92848f1d 100644 --- a/packages/ui-default/theme/default.js +++ b/packages/ui-default/theme/default.js @@ -25,7 +25,6 @@ import 'vj/misc/section.styl'; import 'vj/misc/nothing.styl'; import 'vj/components/editor/cmeditor.styl'; import 'vj/components/datepicker/datepicker.styl'; -import 'vj/components/katex/katex.styl'; // load all page stylesheets const pageStyleReq = require.context('../', true, /\.page\.styl$/i); diff --git a/packages/ui-default/types.ts b/packages/ui-default/types.ts index 83c51cf2..d414abba 100644 --- a/packages/ui-default/types.ts +++ b/packages/ui-default/types.ts @@ -3,4 +3,4 @@ declare let UserContext: any; declare let UiContext: any; // eslint-disable-next-line camelcase declare let node_modules: any; -declare let LANGS: Record; +declare let LANGS: Record; diff --git a/packages/ui-default/utils/cloneAttributes.js b/packages/ui-default/utils/cloneAttributes.js deleted file mode 100644 index e7760a8a..00000000 --- a/packages/ui-default/utils/cloneAttributes.js +++ /dev/null @@ -1,6 +0,0 @@ -export default function cloneAttributes(from, to) { - const attributes = from.prop('attributes'); - $.each(attributes, function () { - to.attr(this.name, this.value); - }); -} diff --git a/packages/ui-default/utils/parseQueryString.js b/packages/ui-default/utils/parseQueryString.js deleted file mode 100644 index 5b6859e1..00000000 --- a/packages/ui-default/utils/parseQueryString.js +++ /dev/null @@ -1,13 +0,0 @@ -export default function parseQueryString(str) { - const obj = {}; - (str || document.location.search) - .replace(/(^\?)/, '') - .split('&') - .forEach((n) => { - const [key, value] = n.split('=').map((v) => decodeURIComponent(v)); - obj[key] = value; - }); - return obj; -} - -window.Hydro.utils.parseQueryString = parseQueryString; diff --git a/packages/ui-default/utils/pjax.js b/packages/ui-default/utils/pjax.js index 7abe08d3..1e4ed20a 100644 --- a/packages/ui-default/utils/pjax.js +++ b/packages/ui-default/utils/pjax.js @@ -3,8 +3,8 @@ import $ from 'jquery'; import { nanoid } from 'nanoid'; +import NProgress from 'nprogress'; import Notification from 'vj/components/notification'; -import NProgress from 'vj/components/nprogress'; import request from 'vj/utils/request'; const pjax = {}; diff --git a/packages/ui-default/utils/tpl.ts b/packages/ui-default/utils/tpl.ts index 21d57487..c0d7d9c7 100644 --- a/packages/ui-default/utils/tpl.ts +++ b/packages/ui-default/utils/tpl.ts @@ -1,6 +1,6 @@ import _ from 'lodash'; -type Substitution = string | { templateRaw: true, html: string }; +type Substitution = string | number | { templateRaw: true, html: string }; export default function tpl(pieces: TemplateStringsArray, ...substitutions: Substitution[]) { let result = pieces[0];