diff --git a/plugins/hmr/package.json b/plugins/hmr/package.json index 299ed10115..c39d2f3ac3 100644 --- a/plugins/hmr/package.json +++ b/plugins/hmr/package.json @@ -44,10 +44,13 @@ "koishi": "^4.12.7" }, "devDependencies": { + "@types/babel__code-frame": "^7.0.3", "@types/throttle-debounce": "^2.1.0", + "esbuild": "^0.17.18", "koishi": "^4.12.7" }, "dependencies": { + "@babel/code-frame": "^7.21.4", "chokidar": "^3.5.3", "throttle-debounce": "^3.0.1" } diff --git a/plugins/hmr/src/error.ts b/plugins/hmr/src/error.ts new file mode 100644 index 0000000000..00ac11aed0 --- /dev/null +++ b/plugins/hmr/src/error.ts @@ -0,0 +1,37 @@ +import { Logger } from 'koishi' +import { BuildFailure } from 'esbuild' +import { codeFrameColumns } from '@babel/code-frame' +import { readFileSync } from 'fs' + +const logger = new Logger('watch') + +function isBuildFailure(e: any): e is BuildFailure { + return Array.isArray(e?.errors) && e.errors.every((error: any) => error.text) +} + +export function handleError(e: any) { + if (!isBuildFailure(e)) { + logger.warn(e) + return + } + + for (const error of e.errors) { + if (!error.location) { + logger.warn(error.text) + continue + } + try { + const { file, line, column } = error.location + const source = readFileSync(file, 'utf8') + const formatted = codeFrameColumns(source, { + start: { line, column }, + }, { + highlightCode: true, + message: error.text, + }) + logger.warn(`File: ${file}:${line}:${column}\n` + formatted) + } catch (e) { + logger.warn(e) + } + } +} diff --git a/plugins/hmr/src/index.ts b/plugins/hmr/src/index.ts index 4a25d40b75..f25d827222 100644 --- a/plugins/hmr/src/index.ts +++ b/plugins/hmr/src/index.ts @@ -3,6 +3,7 @@ import { FSWatcher, watch, WatchOptions } from 'chokidar' import { relative, resolve } from 'path' import { debounce } from 'throttle-debounce' import { Loader, unwrapExports } from '@koishijs/loader' +import { handleError } from './error' declare module 'koishi' { interface Context { @@ -259,8 +260,8 @@ class Watcher { for (const [, { filename }] of reloads) { attempts[filename] = unwrapExports(require(filename)) } - } catch (err) { - logger.warn(err) + } catch (e) { + handleError(e) return rollback() }