Skip to content

Commit bbd7fbc

Browse files
authored
ui-next: page registration (#1159)
1 parent 66a8a0a commit bbd7fbc

28 files changed

Lines changed: 237 additions & 97 deletions

File tree

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ packages/ui-default/public
4949
packages/ui-default/misc/.iconfont
5050
packages/ui-default/static/locale
5151
packages/ui-next/public
52-
plugins/
53-
modules/
52+
/plugins/
53+
/modules/
5454

5555
# Data files
5656
*.mmdb

build/prepare.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,6 @@ const UINextConfig = {
159159
'@/*': [
160160
'./packages/ui-next/src/*',
161161
],
162-
'vj/*': [
163-
'./packages/ui-default/*',
164-
],
165162
},
166163
},
167164
};

examples/plugins/.gitkeep

Whitespace-only changes.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "@hydrooj/ui-next-plugin",
3+
"version": "0.0.0",
4+
"author": "Baoshuo <i@baoshuo.ren>",
5+
"license": "AGPL-3.0",
6+
"hydro": {
7+
"cli": false
8+
},
9+
"dependencies": {
10+
"@hydrooj/ui-next": "workspace:*"
11+
}
12+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import type { PluginAPI } from '@hydrooj/ui-next';
2+
3+
export function setup(api: PluginAPI) {
4+
api.before('page:app', () => {
5+
console.log('before app');
6+
// throw new Error('test error boundary in before interceptor');
7+
return <div>before app via @hydrooj/ui-next-plugin-sample</div>;
8+
});
9+
}

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"packages/*",
66
"framework/*",
77
"plugins/*",
8-
"modules/*"
8+
"modules/*",
9+
"examples/plugins/*"
910
],
1011
"main": "package.json",
1112
"scripts": {

packages/ui-next-plugin-sample/package.json

Lines changed: 0 additions & 6 deletions
This file was deleted.

packages/ui-next-plugin-sample/ui/before.tsx

Lines changed: 0 additions & 5 deletions
This file was deleted.

packages/ui-next-plugin-sample/ui/index.ts

Lines changed: 0 additions & 6 deletions
This file was deleted.

packages/ui-next/index.ts

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import crypto from 'crypto';
22
import fs from 'fs';
33
import path from 'path';
4-
import react from '@vitejs/plugin-react';
54
import esbuild from 'esbuild';
65
import c2k from 'koa2-connect/ts';
76
import { createServer, type Plugin } from 'vite';
@@ -28,6 +27,20 @@ const PENDING_HTML = `<html>
2827
const INJECT_MARKER = '<!-- __HYDRO_INJECTION__DO_NOT_REMOVE_THIS__ -->';
2928
const buildInject = (data: string) => `<script id="__HYDRO_INJECTION__" type="application/json">${data}</script>`;
3029

30+
function getAddonEntries(): Record<string, string> {
31+
const entries: Record<string, string> = {};
32+
for (const [name, addon] of Object.entries(global.addons)) {
33+
const uiEntry = ['ui/index.ts', 'ui/index.tsx', 'ui/index.js', 'ui/index.jsx']
34+
.map((f) => path.resolve(addon as string, f))
35+
.find((f) => fs.existsSync(f));
36+
if (uiEntry) {
37+
logger.info('UI entry for addon %s: %s', name, uiEntry);
38+
entries[name] = uiEntry;
39+
}
40+
}
41+
return entries;
42+
}
43+
3144
function hydroPlugins(): Plugin {
3245
const virtualModuleId = 'virtual:hydro-plugins';
3346
const resolvedVirtualModuleId = `\0${virtualModuleId}`;
@@ -42,14 +55,12 @@ function hydroPlugins(): Plugin {
4255
},
4356
load(id) {
4457
if (id === resolvedVirtualModuleId) {
45-
const entries: string[] = [];
46-
for (const addon of Object.values(global.addons)) {
47-
const uiEntry = path.resolve(addon, 'ui', 'index.ts');
48-
if (fs.existsSync(uiEntry)) entries.push(uiEntry);
49-
}
50-
if (!entries.length) return 'export default [];';
51-
const imports = entries.map((e, i) => `import * as plugin${i} from '${e}';`).join('\n');
52-
const exports = `export default [${entries.map((_, i) => `plugin${i}`).join(', ')}];`;
58+
const entries = getAddonEntries();
59+
if (!Object.keys(entries).length) return 'export default [];';
60+
const imports = Object.entries(entries).map(([_, e], i) => `import * as plugin${i} from '${e}';`).join('\n');
61+
const exports = `export default [${Object.entries(entries).map(([addon, _], i) => {
62+
return `{ name: '${addon}', ...plugin${i} }`;
63+
}).join(', ')}];`;
5364
return `${imports}\n${exports}`;
5465
}
5566
return undefined;
@@ -105,12 +116,9 @@ class UiNextConstantHandler extends Handler {
105116
export async function buildPlugins() {
106117
const start = Date.now();
107118
let totalSize = 0;
108-
const entries: string[] = [];
109-
for (const addon of Object.values(global.addons)) {
110-
const uiEntry = path.resolve(addon as string, 'ui', 'index.ts');
111-
if (fs.existsSync(uiEntry)) entries.push(uiEntry);
112-
}
113-
if (!entries.length) {
119+
const entries = getAddonEntries();
120+
121+
if (!Object.keys(entries).length) {
114122
vfs['plugins.js'] = 'window.__hydroPlugins = [];';
115123
hashes['plugins.js'] = '00000000';
116124
logger.info('No plugins to build');
@@ -121,11 +129,8 @@ export async function buildPlugins() {
121129
const result = await esbuild.build({
122130
stdin: {
123131
contents: [
124-
...entries.map((e, i) => `import * as plugin${i} from '${e}';`),
125-
`window.__hydroPlugins = [${entries.map((e, i) => {
126-
const addonName = path.basename(path.resolve(e, '..', '..'));
127-
return `{ name: '${addonName}', ...plugin${i} }`;
128-
}).join(', ')}];`,
132+
...Object.entries(entries).map(([_, e], i) => `import * as plugin${i} from '${e}';`),
133+
`window.__hydroPlugins = [${Object.entries(entries).map(([n], i) => `{ name: '${n}', ...plugin${i} }`).join(', ')}];`,
129134
].join('\n'),
130135
resolveDir: process.cwd(),
131136
loader: 'ts',
@@ -155,7 +160,7 @@ export async function apply(ctx: Context) {
155160

156161
if (process.env.DEV) {
157162
const vite = await createServer({
158-
configFile: false,
163+
root: __dirname,
159164
clearScreen: false,
160165
server: {
161166
middlewareMode: true,
@@ -168,12 +173,7 @@ export async function apply(ctx: Context) {
168173
},
169174
},
170175
appType: 'custom',
171-
root: __dirname,
172-
base: '/',
173-
plugins: [react(), hydroPlugins()],
174-
worker: {
175-
format: 'es',
176-
},
176+
plugins: [hydroPlugins()],
177177
});
178178
const middleware = c2k(vite.middlewares);
179179
const capture = ['/@vite/', '/src/', '/node_modules/', '/@react-refresh', '/@fs', '/@id/'];
@@ -191,7 +191,11 @@ export async function apply(ctx: Context) {
191191
const serialized = JSON.stringify({
192192
HYDRO_INJECTED: true,
193193
name: context.handler.context._matchedRouteName,
194-
args,
194+
args: {
195+
UserContext: context.UserContext,
196+
UiContext: context.handler.UiContext,
197+
...args,
198+
},
195199
url: context.handler.context.req.url!,
196200
route_map: ctx.server.routeMap,
197201
endpoint: ctx.setting.get('server.url') || undefined,
@@ -220,7 +224,11 @@ export async function apply(ctx: Context) {
220224
const serialized = JSON.stringify({
221225
HYDRO_INJECTED: true,
222226
name: context.handler.context._matchedRouteName,
223-
args,
227+
args: {
228+
UserContext: context.UserContext,
229+
UiContext: context.handler.UiContext,
230+
...args,
231+
},
224232
url: context.handler.context.req.url!,
225233
route_map: ctx.server.routeMap,
226234
endpoint: ctx.setting.get('server.url') || undefined,

0 commit comments

Comments
 (0)