diff --git a/packages/ui-default/build/config/webpack.js b/packages/ui-default/build/config/webpack.js index 8d588967b..33ebab2f9 100644 --- a/packages/ui-default/build/config/webpack.js +++ b/packages/ui-default/build/config/webpack.js @@ -1,10 +1,12 @@ +/* eslint-disable global-require */ /* eslint-disable import/no-extraneous-dependencies */ import { dirname } from 'path'; import webpack from 'webpack'; +import { CleanWebpackPlugin } from 'clean-webpack-plugin'; +import { ESBuildMinifyPlugin } from 'esbuild-loader'; import ExtractCssPlugin from 'mini-css-extract-plugin'; import MonacoWebpackPlugin from 'monaco-editor-webpack-plugin'; import FriendlyErrorsPlugin from 'friendly-errors-webpack-plugin'; -import OptimizeCssAssetsPlugin from 'optimize-css-assets-webpack-plugin'; import CopyWebpackPlugin from 'copy-webpack-plugin'; import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; import SpeedMeasurePlugin from 'speed-measure-webpack-plugin'; @@ -20,22 +22,13 @@ const beautifyOutputUrl = mapWebpackUrlPrefix([ const smp = new SpeedMeasurePlugin(); export default function (env = {}) { - function babelLoader() { - return { - loader: 'babel-loader', - options: { - ...require(root('babel.config.js')), // eslint-disable-line - cacheDirectory: true, - }, - }; - } - function esbuildLoader() { return { loader: 'esbuild-loader', options: { loader: 'tsx', target: 'es2015', + sourcemap: true, }, }; } @@ -113,13 +106,7 @@ export default function (env = {}) { use: [fileLoader()], }, { - test: /\.jsx?$/, - exclude: /node_modules[/\\]/, - use: [babelLoader()], - }, - { - test: /\.tsx?$/, - exclude: /node_modules[/\\]/, + test: /\.[jt]sx?$/, use: [esbuildLoader()], }, { @@ -139,14 +126,55 @@ export default function (env = {}) { }, optimization: { splitChunks: { - minSize: 80000, - maxAsyncRequests: 15, - maxInitialRequests: 10, + minSize: 256000, + maxAsyncRequests: 5, + maxInitialRequests: 3, + automaticNameDelimiter: '-', + cacheGroups: { + blockly: { + test: /[\\/]@?blockly[\\/]/, + priority: 10, + name: 'blockly', + }, + monaco: { + test: /[\\/]monaco-editor[\\/]/, + priority: 9, + name: `monaco-${require('monaco-editor/package.json').version}`, + }, + vditor: { + test: /[\\/]vditor[\\/]/, + priority: 8, + name: `vditor-${require('vditor/package.json').version}`, + }, + vendors: { + test: /[\\/]node_modules[\\/]/, + priority: -10, + name: 'vendors', + reuseExistingChunk: true, + }, + default: { + priority: -20, + reuseExistingChunk: true, + }, + }, }, + minimizer: [new ESBuildMinifyPlugin({ + css: true, + minify: true, + minifySyntax: true, + minifyWhitespace: true, + minifyIdentifiers: true, + treeShaking: true, + target: [ + 'chrome60', + ], + exclude: [/mathmaps/, /\.min\.js$/], + })], moduleIds: env.production ? 'size' : 'named', chunkIds: env.production ? 'size' : 'named', }, plugins: [ + new CleanWebpackPlugin(), new WebpackBar(), new webpack.ProvidePlugin({ $: 'jquery', @@ -183,12 +211,6 @@ export default function (env = {}) { NODE_ENV: env.production ? '"production"' : '"debug"', }, }), - ...(env.production || env.measure) - ? [ - new OptimizeCssAssetsPlugin(), - new webpack.LoaderOptionsPlugin({ minimize: true }), - ] - : [], new webpack.LoaderOptionsPlugin({ options: { context: root(), diff --git a/packages/ui-default/build/utils/mapWebpackUrlPrefix.js b/packages/ui-default/build/utils/mapWebpackUrlPrefix.js index 9b8343b65..53caf8edb 100644 --- a/packages/ui-default/build/utils/mapWebpackUrlPrefix.js +++ b/packages/ui-default/build/utils/mapWebpackUrlPrefix.js @@ -1,9 +1,9 @@ /* eslint-disable import/no-extraneous-dependencies */ -import _ from 'lodash'; +import { escapeRegExp } from 'lodash'; export default function mapWebpackUrlPrefix(mapList) { const rules = mapList.map((mappingRule) => { - const regex = mappingRule.prefix.split('/').map((s) => _.escapeRegExp(s)).join('[\\/\\\\]'); + const regex = mappingRule.prefix.split('/').map((s) => escapeRegExp(s)).join('[\\/\\\\]'); return { from: new RegExp(`^${regex}`, mappingRule.flag), to: mappingRule.replace, diff --git a/packages/ui-default/components/autocomplete/DomainSelectAutoComplete.js b/packages/ui-default/components/autocomplete/DomainSelectAutoComplete.js index a09a9520b..3bc39f6bf 100644 --- a/packages/ui-default/components/autocomplete/DomainSelectAutoComplete.js +++ b/packages/ui-default/components/autocomplete/DomainSelectAutoComplete.js @@ -1,4 +1,4 @@ -import _ from 'lodash'; +import { assign } from 'lodash'; import tpl from 'vj/utils/tpl'; import request from 'vj/utils/request'; import DOMAttachedObject from 'vj/components/DOMAttachedObject'; @@ -40,5 +40,5 @@ export default class DomainSelectAutoComplete extends AutoComplete { } } -_.assign(DomainSelectAutoComplete, DOMAttachedObject); +assign(DomainSelectAutoComplete, DOMAttachedObject); window.Hydro.components.DomainSelectAutoComplete = DomainSelectAutoComplete; diff --git a/packages/ui-default/components/autocomplete/ProblemSelectAutoComplete.js b/packages/ui-default/components/autocomplete/ProblemSelectAutoComplete.js index 2dff234e4..7d755c8fe 100644 --- a/packages/ui-default/components/autocomplete/ProblemSelectAutoComplete.js +++ b/packages/ui-default/components/autocomplete/ProblemSelectAutoComplete.js @@ -1,4 +1,4 @@ -import _ from 'lodash'; +import { assign } from 'lodash'; import tpl from 'vj/utils/tpl'; import request from 'vj/utils/request'; import DOMAttachedObject from 'vj/components/DOMAttachedObject'; @@ -37,5 +37,5 @@ export default class ProblemSelectAutoComplete extends AutoComplete { } } -_.assign(ProblemSelectAutoComplete, DOMAttachedObject); +assign(ProblemSelectAutoComplete, DOMAttachedObject); window.Hydro.components.ProblemSelectAutoComplete = ProblemSelectAutoComplete; diff --git a/packages/ui-default/components/autocomplete/index.js b/packages/ui-default/components/autocomplete/index.js index d676d108d..8f3a648c8 100644 --- a/packages/ui-default/components/autocomplete/index.js +++ b/packages/ui-default/components/autocomplete/index.js @@ -1,5 +1,5 @@ import Drop from 'tether-drop'; -import _ from 'lodash'; +import { assign } from 'lodash'; import 'jquery-scroll-lock'; import DOMAttachedObject from 'vj/components/DOMAttachedObject'; @@ -194,5 +194,5 @@ export default class AutoComplete extends DOMAttachedObject { } } -_.assign(AutoComplete, DOMAttachedObject); +assign(AutoComplete, DOMAttachedObject); window.Hydro.components.autocomplete = AutoComplete; diff --git a/packages/ui-default/components/highlighter/prismjs.js b/packages/ui-default/components/highlighter/prismjs.js index 4f76c0f83..b123d689e 100644 --- a/packages/ui-default/components/highlighter/prismjs.js +++ b/packages/ui-default/components/highlighter/prismjs.js @@ -1,23 +1,28 @@ -/* - -To add a new language to highlight: -1. Add language in babel in package.json -2. Add new import statement in `components/cmeditor/vjcmeditor.js` -3. Add new import statement in `components/scratchpad/ScratchpadEditorContainer.js` -4. Add new meta data in `components/highlighter/meta.js` - - */ - import Prism from 'prismjs'; - +import components from 'prismjs/components'; +import getLoader from 'prismjs/dependencies'; +import 'prismjs/plugins/toolbar/prism-toolbar'; +import 'prismjs/plugins/toolbar/prism-toolbar.css'; +import 'prismjs/plugins/line-numbers/prism-line-numbers'; +import 'prismjs/plugins/line-numbers/prism-line-numbers.css'; import Clipboard from 'clipboard'; import Notification from 'vj/components/notification/index'; import i18n from 'vj/utils/i18n'; - import languageMeta from './meta'; -const languageExtMap = {}; +const files = require.context('prismjs/components/', true, /prism-[a-z0-9-]+\.js/); +const loadedLanguages = new Set(); +function loadLanguages() { + const languages = Object.keys(components.languages).filter((l) => l !== 'meta'); + const loaded = [...loadedLanguages, ...Object.keys(Prism.languages)]; + getLoader(components, languages, loaded).load((lang) => { + files(`./prism-${lang}.js`); + loadedLanguages.add(lang); + }); +} +const languageExtMap = {}; +loadLanguages(); // Map possible language names to Prism language name languageMeta.forEach((meta) => { for (let i = 0; i < meta.ext.length; ++i) { diff --git a/packages/ui-default/components/messagepad/MessagePadDialogueContentContainer.jsx b/packages/ui-default/components/messagepad/MessagePadDialogueContentContainer.jsx index 996a6ec08..b795ab2f9 100644 --- a/packages/ui-default/components/messagepad/MessagePadDialogueContentContainer.jsx +++ b/packages/ui-default/components/messagepad/MessagePadDialogueContentContainer.jsx @@ -1,5 +1,4 @@ import React from 'react'; -import _ from 'lodash'; import { connect } from 'react-redux'; import TimeAgo from 'timeago-react'; import moment from 'dayjs'; @@ -55,7 +54,7 @@ export default connect(mapStateToProps)(class MessagePadDialogueContentContainer renderInner() { if (this.props.activeId === null) return []; - return _.map(this.props.item.messages, (msg) => ( + return this.props.item.messages.map((msg) => ( ({ value: state.editor.code, @@ -29,7 +31,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(class MonacoEditor e const { language, theme } = this.props; if (this.props.language === 'blockly') { if (!this.blockly) { - import('./blockly.jsx').then(({ default: Blockly }) => { + import('./blockly').then(({ default: Blockly }) => { if (!this.blockly) return; Blockly.inject(this.containerElement, { }); @@ -51,6 +53,29 @@ export default connect(mapStateToProps, mapDispatchToProps)(class MonacoEditor e const fontSize = localStorage.getItem('scratchpad.editor.fontSize'); if (fontSize && !Number.isNaN(+fontSize)) config.fontSize = +fontSize; this.editor = monaco.editor.create(this.containerElement, config); + this.editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KEY_P, () => { + this.editor.getAction('editor.action.quickCommand').run(); + }); + load().then(([loaded, format]) => { + if (!loaded) return; + const formatAction = { + id: 'hydro.format', + label: 'Format Code', + contextMenuOrder: 0, + contextMenuGroupId: 'operation', + keybindings: [monaco.KeyMod.Alt | monaco.KeyMod.Shift | monaco.KeyCode.KEY_F], + run: () => { + if (!['cpp', 'c'].includes(this.model._languageIdentifier.language)) return; + const [success, result] = format(this.editor.getValue(), `${UserContext.astyleOptions.trim()} mode=c`); + if (success) this.editor.setValue(result); + else Notification.warn(result); + }, + }; + this.editor.addAction(formatAction); + this.editor.addCommand(monaco.KeyMod.Alt | monaco.KeyMod.Shift | monaco.KeyCode.KEY_F, () => { + this.editor.getAction('hydro.format').run(); + }); + }); this.disposable.push( this.editor.onDidChangeModelContent((event) => { if (!this.__prevent_trigger_change_event) { @@ -69,7 +94,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(class MonacoEditor e if (this.props.language === 'blockly') { if (!this.blockly) { this.editor.dispose(); - import('./blockly.jsx').then(({ default: Blockly, toolbox }) => { + import('./blockly').then(({ default: Blockly, toolbox }) => { if (!this.blockly) return; Blockly.inject(this.containerElement, { toolbox, diff --git a/packages/ui-default/package.json b/packages/ui-default/package.json index 1af7caae6..00acc9c29 100644 --- a/packages/ui-default/package.json +++ b/packages/ui-default/package.json @@ -1,6 +1,6 @@ { "name": "@hydrooj/ui-default", - "version": "4.15.2", + "version": "4.16.0", "author": "undefined ", "license": "AGPL-3.0", "main": "hydro.js", @@ -75,7 +75,6 @@ "nanoid": "^3.1.23", "normalize.css": "^8.0.1", "nprogress": "^0.2.0", - "optimize-css-assets-webpack-plugin": "^5.0.8", "pickadate": "^3.6.4", "plugin-error": "^1.0.1", "postcss-loader": "^3.0.0",