@@ -27,9 +27,12 @@ export class Plugins {
2727 private pluginsDir : string ;
2828 private globalEnabled = true ;
2929 private disabledPlugins = new Set < string > ( ) ;
30+ private hmr = false ;
31+ private watcher : fs . FSWatcher | null = null ;
3032
31- constructor ( pluginsDir : string = path . join ( process . cwd ( ) , 'plugins' ) ) {
32- this . pluginsDir = pluginsDir ;
33+ constructor ( pluginsDir : string = 'plugins' , hmr = false ) {
34+ this . pluginsDir = path . isAbsolute ( pluginsDir ) ? pluginsDir : path . join ( process . cwd ( ) , pluginsDir ) ;
35+ this . hmr = hmr ;
3336 }
3437
3538 private async getAllFiles ( dir : string , baseDir : string = dir ) : Promise < FileInfo [ ] > {
@@ -49,21 +52,17 @@ export class Plugins {
4952 return this . getAllFiles ( filePath , baseDir ) ;
5053 }
5154
52- const isValidFile =
53- ( entry . name . endsWith ( '.ts' ) || entry . name . endsWith ( '.js' ) ) &&
54- ! entry . name . endsWith ( '.d.ts' ) ;
55+ const isValidFile = ( entry . name . endsWith ( '.ts' ) || entry . name . endsWith ( '.js' ) ) && ! entry . name . endsWith ( '.d.ts' ) ;
5556
5657 if ( isValidFile ) {
5758 const relativePath = path . relative ( baseDir , dir ) ;
58- const parent = relativePath === ''
59- ? null
60- : relativePath . split ( path . sep ) [ 0 ] ;
61-
59+ const parent = relativePath === '' ? null : relativePath . split ( path . sep ) [ 0 ] ;
60+
6261 return [ { filePath, parent } ] ;
6362 }
6463
6564 return [ ] ;
66- } )
65+ } ) ,
6766 ) ;
6867
6968 return results . flat ( ) ;
@@ -81,7 +80,10 @@ export class Plugins {
8180 const loadResults = await Promise . all (
8281 files . map ( async ( { filePath, parent } ) : Promise < PluginDefinition | null > => {
8382 try {
84- const pluginModule = await import ( pathToFileURL ( filePath ) . href ) ;
83+ const fileUrl = pathToFileURL ( filePath ) . href ;
84+ const finalUrl = this . hmr ? `${ fileUrl } ?t=${ Date . now ( ) } ` : fileUrl ;
85+
86+ const pluginModule = await import ( finalUrl ) ;
8587 let plugin = pluginModule . default ;
8688
8789 if ( plugin ?. default ) {
@@ -90,28 +92,48 @@ export class Plugins {
9092
9193 if ( plugin ?. handler && plugin ?. config ) {
9294 const pluginId = this . getPluginId ( plugin . config . matcher ) ;
93- return {
94- ...plugin ,
95- parent,
96- enabled : ! this . disabledPlugins . has ( pluginId )
95+ return {
96+ ...plugin ,
97+ parent,
98+ enabled : ! this . disabledPlugins . has ( pluginId ) ,
9799 } ;
98100 }
99- } catch { }
101+ } catch ( error ) {
102+ console . error ( `[Plugins] Failed to load plugin ${ filePath } :` , error ) ;
103+ }
100104 return null ;
101- } )
105+ } ) ,
102106 ) ;
103107
104108 this . plugins = loadResults . filter ( ( p ) : p is PluginDefinition => p !== null ) ;
105109 }
106110
111+ setupHmr ( ) : void {
112+ if ( ! this . hmr || this . watcher ) return ;
113+
114+ try {
115+ if ( ! fs . existsSync ( this . pluginsDir ) ) {
116+ return ;
117+ }
118+
119+ this . watcher = fs . watch ( this . pluginsDir , { recursive : true } , async ( event , filename ) => {
120+ if ( filename && ( filename . endsWith ( '.ts' ) || filename . endsWith ( '.js' ) ) ) {
121+ await this . reload ( ) ;
122+ }
123+ } ) ;
124+ } catch ( error ) {
125+ console . error ( '[Plugins] Failed to setup HMR:' , error ) ;
126+ }
127+ }
128+
107129 private getPluginId ( matcher : ( string | RegExp ) [ ] ) : string {
108- return matcher . map ( m => m . toString ( ) ) . join ( '|' ) ;
130+ return matcher . map ( ( m ) => m . toString ( ) ) . join ( '|' ) ;
109131 }
110132
111133 async execute ( wa : Client , ctx : MiddlewareContextType ) : Promise < void > {
112134 if ( ! this . globalEnabled ) return ;
113135
114- const messageText = ctx . messages . text || '' ;
136+ const messageText = ctx . messages ? .text || '' ;
115137
116138 for ( const plugin of this . plugins ) {
117139 if ( ! plugin . enabled ) continue ;
@@ -122,7 +144,9 @@ export class Plugins {
122144 if ( isMatch ) {
123145 await plugin . handler ( wa , ctx ) ;
124146 }
125- } catch { }
147+ } catch ( error ) {
148+ console . error ( `[Plugins] Error executing plugin:` , error ) ;
149+ }
126150 }
127151 }
128152
@@ -137,7 +161,7 @@ export class Plugins {
137161
138162 enableAll ( ) : void {
139163 this . globalEnabled = true ;
140- this . plugins . forEach ( p => p . enabled = true ) ;
164+ this . plugins . forEach ( ( p ) => ( p . enabled = true ) ) ;
141165 this . disabledPlugins . clear ( ) ;
142166 }
143167
@@ -147,10 +171,8 @@ export class Plugins {
147171
148172 enable ( matcher : string | RegExp ) : boolean {
149173 const matcherStr = matcher . toString ( ) ;
150- const plugin = this . plugins . find ( p =>
151- p . config . matcher . some ( m => m . toString ( ) === matcherStr )
152- ) ;
153-
174+ const plugin = this . plugins . find ( ( p ) => p . config . matcher . some ( ( m ) => m . toString ( ) === matcherStr ) ) ;
175+
154176 if ( plugin ) {
155177 plugin . enabled = true ;
156178 this . disabledPlugins . delete ( this . getPluginId ( plugin . config . matcher ) ) ;
@@ -161,10 +183,8 @@ export class Plugins {
161183
162184 disable ( matcher : string | RegExp ) : boolean {
163185 const matcherStr = matcher . toString ( ) ;
164- const plugin = this . plugins . find ( p =>
165- p . config . matcher . some ( m => m . toString ( ) === matcherStr )
166- ) ;
167-
186+ const plugin = this . plugins . find ( ( p ) => p . config . matcher . some ( ( m ) => m . toString ( ) === matcherStr ) ) ;
187+
168188 if ( plugin ) {
169189 plugin . enabled = false ;
170190 this . disabledPlugins . add ( this . getPluginId ( plugin . config . matcher ) ) ;
@@ -175,7 +195,8 @@ export class Plugins {
175195
176196 enableByParent ( parent : string ) : number {
177197 let count = 0 ;
178- this . plugins . forEach ( p => {
198+
199+ this . plugins . forEach ( ( p ) => {
179200 if ( p . parent === parent ) {
180201 p . enabled = true ;
181202 this . disabledPlugins . delete ( this . getPluginId ( p . config . matcher ) ) ;
@@ -187,7 +208,8 @@ export class Plugins {
187208
188209 disableByParent ( parent : string ) : number {
189210 let count = 0 ;
190- this . plugins . forEach ( p => {
211+
212+ this . plugins . forEach ( ( p ) => {
191213 if ( p . parent === parent ) {
192214 p . enabled = false ;
193215 this . disabledPlugins . add ( this . getPluginId ( p . config . matcher ) ) ;
@@ -205,9 +227,9 @@ export class Plugins {
205227 return this . plugins ;
206228 }
207229
208- getPluginsInfo ( ) : {
209- matcher : ( string | RegExp ) [ ] ;
210- metadata ?: Record < string , any > ;
230+ getPluginsInfo ( ) : {
231+ matcher : ( string | RegExp ) [ ] ;
232+ metadata ?: Record < string , any > ;
211233 parent : string | null ;
212234 enabled : boolean ;
213235 } [ ] {
@@ -222,15 +244,20 @@ export class Plugins {
222244 async reload ( ) : Promise < void > {
223245 this . plugins = [ ] ;
224246 await this . load ( ) ;
247+ console . log ( `[Plugins] Successfully reloaded ${ this . plugins . length } plugins.` ) ;
248+ }
249+
250+ stopHmr ( ) : void {
251+ if ( this . watcher ) {
252+ this . watcher . close ( ) ;
253+ this . watcher = null ;
254+ }
225255 }
226256}
227257
228- export const definePlugins = (
229- handler : PluginsHandlerType ,
230- config : PluginsConfigType
231- ) : Omit < PluginDefinition , 'parent' | 'enabled' > => {
258+ export const definePlugins = ( handler : PluginsHandlerType , config : PluginsConfigType ) : Omit < PluginDefinition , 'parent' | 'enabled' > => {
232259 return {
233260 handler,
234261 config,
235262 } ;
236- } ;
263+ } ;
0 commit comments