core: hot reload for ui modules

This commit is contained in:
undefined committed 2022-09-21 15:38:36 +08:00
1 parent 26364a717c
commit d9a28d9152
5 files changed
+43 -34

No files matched your search

-1
View File
@@ -21,7 +21,6 @@ if (!global.Hydro) {
lib: {},
// @ts-ignore
ui: {
manifest: {},
template: {},
},
// @ts-ignore
-1
View File
@@ -705,7 +705,6 @@ export interface Lib extends Record<string, any> {
export type UIInjectableFields = 'ProblemAdd' | 'Nav' | 'UserDropdown';
export interface UI {
manifest: Dictionary<string>,
template: Dictionary<string>,
nodes: Record<UIInjectableFields, any[]>,
getNodes: typeof import('./lib/ui').getNodes,
-12
View File
@@ -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}`);
+7 -1
View File
@@ -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<string, Runtime>();
+36 -19
View File
@@ -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();
}