Skip to content

Commit

Permalink
fix(cli): do not full reload when manager changes config file
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Jan 22, 2022
1 parent 07a0779 commit 5261305
Show file tree
Hide file tree
Showing 10 changed files with 159 additions and 153 deletions.
6 changes: 4 additions & 2 deletions packages/cli/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { App } from 'koishi'

export * from 'koishi'
export * from './lib/addons'
export * from './lib/worker'

export type * from './lib/addons'
export type * from './lib/loader'
export type * from './lib/worker'

export function defineConfig(config: App.Config): App.Config
19 changes: 6 additions & 13 deletions packages/cli/src/addons/index.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,19 @@
import { App, Context } from 'koishi'
import * as daemon from './daemon'
import * as logger from './logger'
import FileWatcher, { WatchConfig } from './watcher'
import ConfigWriter from './writer'
import Watcher from './watcher'

declare module 'koishi' {
namespace Context {
interface Services {
configWriter: ConfigWriter
fileWatcher: FileWatcher
}
}

interface App {
_prolog: string[]
watcher: Watcher
}

namespace App {
interface Config extends daemon.Config {
allowWrite?: boolean
logger?: logger.Config
watch?: WatchConfig
watch?: Watcher.Config
}
}
}
Expand All @@ -31,13 +25,12 @@ export function prepare(config: App.Config) {
}

export function apply(ctx: Context, config: App.Config) {
ctx.plugin(logger)
logger.apply(ctx.app)
ctx.plugin(daemon, config)

if (process.env.KOISHI_WATCH_ROOT !== undefined) {
(config.watch ??= {}).root = process.env.KOISHI_WATCH_ROOT
}

if (ctx.app.loader.enableWriter) ctx.plugin(ConfigWriter)
if (config.watch) ctx.plugin(FileWatcher, config.watch)
if (config.watch) ctx.plugin(Watcher, config.watch)
}
10 changes: 4 additions & 6 deletions packages/cli/src/addons/logger.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { App, Context, defineProperty, Logger, remove, Schema, version } from 'koishi'
import { App, defineProperty, Logger, remove, Schema, version } from 'koishi'

interface LogLevelConfig {
// a little different from koishi-utils
Expand Down Expand Up @@ -76,11 +76,9 @@ export function prepare(config: Config = {}) {
Logger.timestamp = Date.now()
}

export const name = 'logger'

export function apply(ctx: Context) {
ctx.app._prolog = prolog
ctx.on('ready', () => {
export function apply(app: App) {
app._prolog = prolog
app.once('ready', () => {
remove(Logger.targets, target)
})
}
54 changes: 31 additions & 23 deletions packages/cli/src/addons/watcher.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,8 @@
import { App, coerce, Context, Dict, Logger, Plugin, Schema, Service } from 'koishi'
import { App, coerce, Context, Dict, Logger, Plugin, Schema } from 'koishi'
import { FSWatcher, watch, WatchOptions } from 'chokidar'
import { relative, resolve } from 'path'
import { debounce } from 'throttle-debounce'

export interface WatchConfig extends WatchOptions {
root?: string
debounce?: number
}

export const WatchConfig = Schema.object({
root: Schema.string().description('要监听的根目录,相对于当前工作路径。'),
debounce: Schema.number().default(100).description('延迟触发更新的等待时间。'),
ignored: Schema.array(Schema.string()).description('要忽略的文件或目录。'),
}).default(null).description('热重载设置')

App.Config.list.push(Schema.object({
watch: WatchConfig,
}))

function loadDependencies(filename: string, ignored: Set<string>) {
const dependencies = new Set<string>()
function traverse({ filename, children }: NodeJS.Module) {
Expand All @@ -35,7 +20,7 @@ function unwrap(module: any) {

const logger = new Logger('watch')

export default class FileWatcher extends Service {
class Watcher {
public suspend = false

private root: string
Expand Down Expand Up @@ -68,8 +53,8 @@ export default class FileWatcher extends Service {
/** stashed changes */
private stashed = new Set<string>()

constructor(ctx: Context, private config: WatchConfig) {
super(ctx, 'fileWatcher')
constructor(private ctx: Context, private config: Watcher.Config) {
ctx.app.watcher = this
}

private triggerFullReload() {
Expand All @@ -79,20 +64,24 @@ export default class FileWatcher extends Service {

start() {
const { root = '', ignored = [] } = this.config
this.root = resolve(this.ctx.app.loader.dirname, root)
this.root = resolve(this.ctx.loader.dirname, root)
this.watcher = watch(this.root, {
...this.config,
ignored: ['**/node_modules/**', '**/.git/**', '**/logs/**', ...ignored],
})

this.externals = loadDependencies(__filename, new Set(Object.keys(this.ctx.app.loader.cache)))
this.externals = loadDependencies(__filename, new Set(Object.keys(this.ctx.loader.cache)))
const flushChanges = debounce(this.config.debounce || 100, () => this.flushChanges())

this.watcher.on('change', (path) => {
if (this.suspend) return
if (this.suspend) {
this.suspend = false
return
}

logger.debug('change detected:', path)

const isEntry = path === this.ctx.app.loader.filename
const isEntry = path === this.ctx.loader.filename
if (!require.cache[path] && !isEntry) return

// files independent from any plugins will trigger a full reload
Expand Down Expand Up @@ -268,3 +257,22 @@ export default class FileWatcher extends Service {
this.stashed = new Set()
}
}

namespace Watcher {
export interface Config extends WatchOptions {
root?: string
debounce?: number
}

export const Config = Schema.object({
root: Schema.string().description('要监听的根目录,相对于当前工作路径。'),
debounce: Schema.number().default(100).description('延迟触发更新的等待时间。'),
ignored: Schema.array(Schema.string()).description('要忽略的文件或目录。'),
}).default(null).description('热重载设置')

App.Config.list.push(Schema.object({
watch: Config,
}))
}

export default Watcher
78 changes: 0 additions & 78 deletions packages/cli/src/addons/writer.ts

This file was deleted.

22 changes: 16 additions & 6 deletions packages/cli/src/loader.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { resolve, extname, dirname, isAbsolute } from 'path'
import { yellow } from 'kleur'
import { readdirSync, readFileSync } from 'fs'
import { readdirSync, readFileSync, writeFileSync } from 'fs'
import { App, Dict, Logger, Modules, Plugin, Schema } from 'koishi'
import { load } from 'js-yaml'
import { dump, load } from 'js-yaml'

declare module 'koishi' {
interface App {
loader: Loader
namespace Context {
interface Services {
loader: Loader
}
}
}

Expand All @@ -20,6 +22,7 @@ Modules.internal.paths = function (name: string) {
}

App.Config.list.push(Schema.object({
allowWrite: Schema.boolean().description('允许在运行时修改配置文件。').default(true),
autoRestart: Schema.boolean().description('应用在运行时崩溃自动重启。').default(true),
plugins: Schema.any().hidden(),
}).description('CLI 设置'))
Expand All @@ -34,7 +37,6 @@ export class Loader {
app: App
config: App.Config
cache: Dict<Plugin> = {}
enableWriter = true

constructor() {
const basename = 'koishi.config'
Expand All @@ -51,7 +53,6 @@ export class Loader {
this.dirname = cwd
this.filename = cwd + '/' + basename + this.extname
}
this.enableWriter = ['.json', '.yaml', '.yml'].includes(this.extname)
}

loadConfig(): App.Config {
Expand All @@ -78,6 +79,12 @@ export class Loader {
return plugin
}

writeConfig() {
// prevent hot reload when it's being written
if (this.app.watcher) this.app.watcher.suspend = true
writeFileSync(this.filename, dump(this.config))
}

createApp() {
const app = this.app = new App(this.config)
app.loader = this
Expand All @@ -91,6 +98,9 @@ export class Loader {
this.loadPlugin(name, plugins[name] ?? {})
}
}
if (!['.json', '.yaml', '.yml'].includes(this.extname)) {
app.options.allowWrite = false
}
return app
}
}
1 change: 0 additions & 1 deletion packages/cli/src/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ declare module 'koishi' {

interface EventMap {
'exit'(signal: NodeJS.Signals): Promise<void>
'reload'(path: string): Promise<void>
}
}

Expand Down
4 changes: 3 additions & 1 deletion plugins/frontend/manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,12 @@
"koishi": "^4.0.1"
},
"devDependencies": {
"@types/cross-spawn": "^6.0.2"
"@types/cross-spawn": "^6.0.2",
"@types/js-yaml": "^4.0.5"
},
"dependencies": {
"cross-spawn": "^7.0.3",
"js-yaml": "^4.1.0",
"semver": "^7.3.5"
}
}
26 changes: 3 additions & 23 deletions plugins/frontend/manager/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import MarketProvider from './market'
import PackageProvider from './packages'
import AdapterProvider from './protocols'
import ServiceProvider from './services'
import ConfigWriter from './writer'

export * from './bots'
export * from './market'
Expand Down Expand Up @@ -40,7 +41,7 @@ declare module '@koishijs/plugin-console' {
}

export const name = 'manager'
export const using = ['console'] as const
export const using = ['console', 'loader'] as const

export interface Config extends MarketProvider.Config {}

Expand All @@ -54,29 +55,8 @@ export function apply(ctx: Context, config: Config = {}) {
ctx.plugin(AdapterProvider)
ctx.plugin(PackageProvider)
ctx.plugin(ServiceProvider)
ctx.plugin(ConfigWriter, ctx.app.options.allowWrite)

const filename = ctx.console.config.devMode ? '../client/index.ts' : '../dist/index.js'
ctx.console.addEntry(resolve(__dirname, filename))

ctx.using(['configWriter'], (ctx) => {
ctx.console.addListener('plugin/load', (name, config) => {
ctx.configWriter.loadPlugin(name, config)
})

ctx.console.addListener('plugin/unload', (name) => {
ctx.configWriter.unloadPlugin(name)
})

ctx.console.addListener('plugin/reload', (name, config) => {
ctx.configWriter.reloadPlugin(name, config)
})

ctx.console.addListener('plugin/save', (name, config) => {
ctx.configWriter.savePlugin(name, config)
})

ctx.console.addListener('bot/create', (platform, config) => {
ctx.configWriter.createBot(platform, config)
})
})
}
Loading

0 comments on commit 5261305

Please sign in to comment.