Skip to content

Commit

Permalink
feat(status): support production build
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Mar 19, 2021
1 parent 06fb3ae commit 7c4ba3b
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 63 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
lib
dist
temp
.koishi
Expand Down
4 changes: 3 additions & 1 deletion build/compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const KOISHI_VERSION = JSON.stringify(version)
await Promise.all(workspaces.map(async (name) => {
if (name.startsWith('.')) return

let outdir = 'dist'
const base = `${root}/${name}`
const entryPoints = [base + '/src/index.ts']

Expand All @@ -60,6 +61,7 @@ const KOISHI_VERSION = JSON.stringify(version)
} else if (name === 'koishi-test-utils') {
await tasks[chai]
} else if (name === 'plugin-status') {
outdir = 'lib'
entryPoints.splice(0, 1, base + '/server/index.ts')
}

Expand All @@ -70,7 +72,7 @@ const KOISHI_VERSION = JSON.stringify(version)
platform: 'node',
target: 'node12.19',
charset: 'utf8',
outdir: `${root}/${name}/dist`,
outdir: `${root}/${name}/${outdir}`,
logLevel: 'silent',
sourcemap: true,
define: {
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
"addons": "yarn workspace addons",
"cli": "yarn workspace test koishi",
"build": "yarn compile && yarn dtsc",
"build:ci": "yarn build:reg && yarn build --listEmittedFiles",
"build:ci": "yarn build:reg && yarn build --listEmittedFiles && yarn build:web",
"build:reg": "esbuild build/register.ts --outdir=build --format=cjs --log-level=error",
"build:web": "node -r ./build/register packages/plugin-status/build",
"dtsc": "node -r ./build/register build/dtsc",
"bump": "node -r ./build/register build/bump",
"compile": "node -r ./build/register build/compile",
Expand All @@ -28,7 +29,7 @@
"test:text": "c8 -r text yarn test",
"test:reg": "yarn build:reg && yarn test:html",
"lint": "eslint packages/*/src/**/*.ts --fix --cache",
"pub": "yarn build && node -r ./build/register build/publish",
"pub": "yarn build && yarn build:web && node -r ./build/register build/publish",
"webui": "yarn workspace koishi-plugin-webui",
"shiki": "yarn workspace bot-shiki",
"utsuho": "yarn workspace bot-utsuho"
Expand Down
22 changes: 22 additions & 0 deletions packages/plugin-status/build/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { build } from 'vite'
import { resolve } from 'path'
import vuePlugin from '@vitejs/plugin-vue'

const root = resolve(__dirname, '../client')

build({
root,
base: './',
build: {
outDir: '../dist',
minify: 'esbuild',
emptyOutDir: true,
},
plugins: [vuePlugin()],
resolve: {
alias: {
'~/client': root,
'~/variables': root + '/index.scss',
},
},
})
6 changes: 3 additions & 3 deletions packages/plugin-status/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
"name": "koishi-plugin-status",
"description": "Show Status of Koishi",
"version": "4.0.0-alpha.1",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
"files": [
"dist"
"dist", "lib"
],
"author": "Shigma <1700011071@pku.edu.cn>",
"license": "MIT",
Expand Down
6 changes: 6 additions & 0 deletions packages/plugin-status/server/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,12 @@ export class WebAdapter extends Adapter<'sandbox'> {
})
}

broadcast(type: string, body: any) {
if (!this?.server.clients.size) return
const data = JSON.stringify({ type, body })
this.server.clients.forEach((socket) => socket.send(data))
}

stop() {
this.server.close()
}
Expand Down
116 changes: 60 additions & 56 deletions packages/plugin-status/server/webui.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Context, Plugin } from 'koishi-core'
import { assertProperty, noop } from 'koishi-utils'
import { resolve } from 'path'
import { promises as fs, Stats } from 'fs'
import { resolve, extname } from 'path'
import { promises as fs, Stats, createReadStream } from 'fs'
import { WebAdapter } from './adapter'
import { createServer, ViteDevServer } from 'vite'
import { createServer } from 'vite'
import vuePlugin from '@vitejs/plugin-vue'
import Profile from './profile'
import Statistics from './stats'
Expand All @@ -13,6 +13,7 @@ export { BotData, LoadRate } from './profile'
export interface Config extends WebAdapter.Config, Profile.Config {
selfUrl?: string
uiPath?: string
mode?: 'development' | 'production'
}

export interface PluginData extends Plugin.Meta {
Expand All @@ -30,14 +31,20 @@ export interface Registry {
export const name = 'webui'

export function apply(ctx: Context, config: Config = {}) {
const root = resolve(__dirname, '../client')
const koishiPort = assertProperty(ctx.app.options, 'port')
const { apiPath, uiPath, selfUrl = `http://localhost:${koishiPort}` } = config
const { apiPath, uiPath, mode, selfUrl = `http://localhost:${koishiPort}` } = config

let vite: ViteDevServer
let adapter: WebAdapter
ctx.on('connect', async () => {
vite = await createServer({
const globalVariables = Object.entries({
KOISHI_UI_PATH: uiPath,
KOISHI_ENDPOINT: selfUrl + apiPath,
}).map(([key, value]) => `window.${key} = ${JSON.stringify(value)};`).join('\n')

const root = resolve(__dirname, '..', mode === 'development' ? 'client' : 'dist')

async function createVite() {
if (mode !== 'development') return

const vite = await createServer({
root,
base: '/vite/',
server: { middlewareMode: true },
Expand All @@ -48,29 +55,19 @@ export function apply(ctx: Context, config: Config = {}) {
'~/variables': root + '/index.scss',
},
},
define: {
KOISHI_UI_PATH: JSON.stringify(uiPath),
KOISHI_ENDPOINT: JSON.stringify(selfUrl + apiPath),
},
})

ctx.router.get(uiPath + '(/.+)*', async (koa) => {
const filename = root + koa.path.slice(uiPath.length)
const stats = await fs.stat(filename).catch<Stats>(noop)
if (stats?.isFile()) {
return koa.body = await fs.readFile(filename)
}
const raw = await fs.readFile(resolve(root, 'index.html'), 'utf8')
const template = await vite.transformIndexHtml(uiPath, raw)
koa.set('content-type', 'text/html')
koa.body = template
})

ctx.router.all('/vite(/.+)+', (koa) => new Promise((resolve) => {
vite.middlewares(koa.req, koa.res, resolve)
}))

adapter = ctx.app.adapters.sandbox = new WebAdapter(ctx, config)
ctx.before('disconnect', () => vite.close())

return vite
}

async function createAdapter() {
const adapter = ctx.app.adapters.sandbox = new WebAdapter(ctx, config)

adapter.server.on('connection', async (socket) => {
function send(type: string, body: any) {
Expand All @@ -83,6 +80,35 @@ export function apply(ctx: Context, config: Config = {}) {
})

await adapter.start()

ctx.before('disconnect', () => adapter.stop())

ctx.on('registry', () => {
adapter.broadcast('registry', getRegistry(true))
})

ctx.on('status/tick', async () => {
adapter.broadcast('profile', await getProfile(true))
})

return adapter
}

ctx.on('connect', async () => {
const [vite] = await Promise.all([createVite(), createAdapter()])

ctx.router.get(uiPath + '(/.+)*', async (koa) => {
const filename = root + koa.path.slice(uiPath.length)
const stats = await fs.stat(filename).catch<Stats>(noop)
if (stats?.isFile()) {
koa.type = extname(filename)
return koa.body = createReadStream(filename)
}
let template = await fs.readFile(resolve(root, 'index.html'), 'utf8')
if (vite) template = await vite.transformIndexHtml(uiPath, template)
koa.set('content-type', 'text/html')
koa.body = template.replace('</head>', '<script>' + globalVariables + '</script></head>')
})
})

function* getDeps(state: Plugin.State): Generator<string> {
Expand All @@ -100,44 +126,22 @@ export function apply(ctx: Context, config: Config = {}) {
const children = state.children.flatMap(traverse, 1)
const { name, sideEffect } = state
if (!name) return children
internal.pluginCount += 1
registry.pluginCount += 1
const dependencies = [...new Set(getDeps(state))]
return [{ name, sideEffect, children, dependencies }]
}

let profile: Promise<Profile>
let internal: Registry

async function broadcast(type: string, body: any) {
if (!adapter?.server.clients.size) return
const data = JSON.stringify({ type, body })
adapter.server.clients.forEach((socket) => socket.send(data))
}

function getRegistry(forced = false) {
if (internal && !forced) return internal
internal = { pluginCount: 0 } as Registry
internal.plugins = traverse(null)
return internal
}

function getProfile(forced = false) {
if (profile && !forced) return profile
return profile = Profile.get(ctx, config)
}

ctx.on('registry', () => {
broadcast('registry', getRegistry(true))
})

ctx.on('status/tick', async () => {
broadcast('profile', await getProfile(true))
})

ctx.before('disconnect', async () => {
await Promise.all([
vite?.close(),
adapter?.stop(),
])
})
let registry: Registry
function getRegistry(forced = false) {
if (registry && !forced) return registry
registry = { pluginCount: 0 } as Registry
registry.plugins = traverse(null)
return registry
}
}
2 changes: 1 addition & 1 deletion packages/plugin-status/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"extends": "../../tsconfig.base",
"compilerOptions": {
"outDir": "dist",
"outDir": "lib",
"rootDir": "server",
},
"include": [
Expand Down

0 comments on commit 7c4ba3b

Please sign in to comment.