diff --git a/packages/hydrooj/src/init.ts b/packages/hydrooj/src/init.ts index e7eb590b8..ea4dbb989 100644 --- a/packages/hydrooj/src/init.ts +++ b/packages/hydrooj/src/init.ts @@ -21,7 +21,6 @@ if (!global.Hydro) { lib: {}, // @ts-ignore ui: { - manifest: {}, template: {}, }, // @ts-ignore diff --git a/packages/hydrooj/src/interface.ts b/packages/hydrooj/src/interface.ts index d0bd297df..65f44f65f 100644 --- a/packages/hydrooj/src/interface.ts +++ b/packages/hydrooj/src/interface.ts @@ -705,7 +705,6 @@ export interface Lib extends Record { export type UIInjectableFields = 'ProblemAdd' | 'Nav' | 'UserDropdown'; export interface UI { - manifest: Dictionary, template: Dictionary, nodes: Record, getNodes: typeof import('./lib/ui').getNodes, diff --git a/packages/hydrooj/src/loader.ts b/packages/hydrooj/src/loader.ts index 87c9ad358..cbc9aa9db 100644 --- a/packages/hydrooj/src/loader.ts +++ b/packages/hydrooj/src/loader.ts @@ -32,18 +32,6 @@ export function addon(addonPath: string, prepend = false) { const name = payload.name.startsWith('@hydrooj/') ? payload.name.split('@hydrooj/')[1] : payload.name; global.Hydro.version[name] = payload.version; const modulePath = path.dirname(packagejson); - const publicPath = path.resolve(modulePath, 'public'); - if (fs.existsSync(publicPath)) { - global.publicDirs[prepend ? 'push' : 'unshift'](publicPath); - const targets = fs.readdirSync(publicPath); - for (const target of targets) { - if (global.Hydro.ui.manifest[target] && !prepend) { - global.Hydro.ui.manifest[target] = publicPath; - } else if (!global.Hydro.ui.manifest[target]) { - global.Hydro.ui.manifest[target] = publicPath; - } - } - } global.addons[prepend ? 'unshift' : 'push'](modulePath); } catch (e) { logger.error(`Addon not found: ${addonPath}`); diff --git a/packages/hydrooj/src/service/watcher.ts b/packages/hydrooj/src/service/watcher.ts index 423b428a7..23d60cd5f 100644 --- a/packages/hydrooj/src/service/watcher.ts +++ b/packages/hydrooj/src/service/watcher.ts @@ -59,14 +59,21 @@ class Watcher { this.watcher.on('change', (path) => { logger.debug('change detected:', relative(this.root, path)); + bus.emit('app/watch/change', path); if (this.externals.has(path)) { logger.warn('Require full reload'); } else if (require.cache[path]) { this.stashed.add(path); + bus.emit('app/before-reload', this.stashed); triggerLocalReload(); + bus.emit('app/reload', this.stashed); } }); + this.watcher.on('unlink', (path) => { + logger.debug('change detected: ', `-${relative(this.root, path)}`); + bus.emit('app/watch/unlink', path); + }); } stop() { @@ -133,7 +140,6 @@ class Watcher { private triggerLocalReload() { const start = Date.now(); this.analyzeChanges(); - console.log(this.accepted); /** plugins pending classification */ const pending = new Map(); diff --git a/packages/ui-default/handler.ts b/packages/ui-default/handler.ts index 9cd9ca530..93ee85f22 100644 --- a/packages/ui-default/handler.ts +++ b/packages/ui-default/handler.ts @@ -2,19 +2,20 @@ /* eslint-disable camelcase */ import crypto from 'crypto'; import esbuild from 'esbuild'; +import fs from 'fs-extra'; +import { Logger } from 'hydrooj/src/logger'; import { PERM, PRIV } from 'hydrooj/src/model/builtin'; import * as contest from 'hydrooj/src/model/contest'; import problem from 'hydrooj/src/model/problem'; import * as setting from 'hydrooj/src/model/setting'; import * as system from 'hydrooj/src/model/system'; import user from 'hydrooj/src/model/user'; -import * as bus from 'hydrooj/src/service/bus'; import { UiContextBase } from 'hydrooj/src/service/layers/base'; -import { Handler, Route } from 'hydrooj/src/service/server'; +import { Handler } from 'hydrooj/src/service/server'; import { SystemSettings } from 'hydrooj/src/settings'; import { ObjectID } from 'mongodb'; import { tmpdir } from 'os'; -import { join } from 'path'; +import { join, resolve } from 'path'; import Schema from 'schemastery'; import convert from 'schemastery-jsonschema'; import markdown from './backendlib/markdown'; @@ -38,12 +39,23 @@ declare module 'hydrooj/src/service/layers/base' { let constant = ''; let hash = ''; +const logger = new Logger('ui'); -async function buildUI() { - const pageFiles = Object.keys(global.Hydro.ui.manifest).filter((i) => /\.page\.[jt]sx?$/.test(i)); +export async function buildUI() { + const start = Date.now(); + const entryPoints: string[] = []; + for (const addon of global.addons) { + const publicPath = resolve(addon, 'public'); + if (fs.existsSync(publicPath)) { + const targets = fs.readdirSync(publicPath); + for (const target of targets) { + if (/\.page\.[jt]sx?$/.test(target)) entryPoints.push(join(publicPath, target)); + } + } + } const build = await esbuild.build({ format: 'iife', - entryPoints: pageFiles.map((i) => join(global.Hydro.ui.manifest[i], i)), + entryPoints, bundle: true, outdir: tmpdir(), splitting: false, @@ -64,15 +76,13 @@ async function buildUI() { const version = c.digest('hex'); constant = JSON.stringify(payload); UiContextBase.constantVersion = hash = version; + logger.success('UI addons built in %d ms', Date.now() - start); } function updateLogo() { [UiContextBase.nav_logo_dark, UiContextBase.nav_logo_dark_2x] = system.getMany([ 'ui-default.nav_logo_dark', 'ui-default.nav_logo_dark_2x', ]); } -bus.on('app/started', buildUI); -bus.on('app/started', updateLogo); -bus.on('system/setting', updateLogo); class WikiHelpHandler extends Handler { noCheckPermView = true; @@ -217,13 +227,20 @@ class RichMediaHandler extends Handler { } } -global.Hydro.handler.ui = async () => { - Route('wiki_help', '/wiki/help', WikiHelpHandler); - Route('wiki_about', '/wiki/about', WikiAboutHandler); - Route('set_theme', '/set_theme/:theme', SetThemeHandler); - Route('constant', '/constant/:version', UiConstantsHandler); - Route('markdown', '/markdown', MarkdownHandler); - Route('config_schema', '/manage/config/schema.json', SystemConfigSchemaHandler, PRIV.PRIV_EDIT_SYSTEM); - Route('lang', '/l/:lang', LanguageHandler); - Route('media', '/media', RichMediaHandler); -}; +export function apply(ctx) { + ctx.Route('wiki_help', '/wiki/help', WikiHelpHandler); + ctx.Route('wiki_about', '/wiki/about', WikiAboutHandler); + ctx.Route('set_theme', '/set_theme/:theme', SetThemeHandler); + ctx.Route('constant', '/constant/:version', UiConstantsHandler); + ctx.Route('markdown', '/markdown', MarkdownHandler); + ctx.Route('config_schema', '/manage/config/schema.json', SystemConfigSchemaHandler, PRIV.PRIV_EDIT_SYSTEM); + ctx.Route('lang', '/l/:lang', LanguageHandler); + ctx.Route('media', '/media', RichMediaHandler); + ctx.on('app/started', buildUI); + ctx.on('app/watch/change', buildUI); + ctx.on('app/watch/unlink', buildUI); + ctx.on('app/started', updateLogo); + ctx.on('system/setting', updateLogo); + buildUI(); + updateLogo(); +}