ui: optimize bundle

pull/420/head
undefined 2 years ago
parent 9c02ed4dd8
commit 5ae8258b8a

@ -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';

@ -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',
}),

@ -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<Stats>((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<string, number>;
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() {

@ -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) {

@ -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) {

@ -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

@ -1,4 +1,5 @@
export default `#include <world.h>
export default `\
#include <world.h>
int main() {
// ~ Switch on the power line / Remember to put on PROTECTION ~

@ -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; }
}

@ -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;

@ -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;

@ -1,3 +0,0 @@
import NProgress from 'nprogress';
export default NProgress;

@ -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);

@ -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);

@ -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);

@ -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);

@ -1,8 +0,0 @@
export default function attachObjectMeta(obj, key, value) {
Object.defineProperty(obj, `__${key}`, {
value,
enumerable: false,
configurable: false,
writable: false,
});
}

@ -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';

@ -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 {

@ -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

@ -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 {

@ -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);

@ -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<string, import('@hydrooj/utils/lib/lang').LangConfig>;
declare let LANGS: Record<string, any>;

@ -1,6 +0,0 @@
export default function cloneAttributes(from, to) {
const attributes = from.prop('attributes');
$.each(attributes, function () {
to.attr(this.name, this.value);
});
}

@ -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;

@ -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 = {};

@ -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];

Loading…
Cancel
Save