@@ -22,126 +22,209 @@ interface PluginToml {
2222}
2323
2424interface ValidationError {
25- plugin : string
2625 field : string
2726 message : string
2827 invalidValues ?: string [ ]
2928}
3029
31- function validatePluginToml ( pluginName : string , pluginPath : string ) : ValidationError [ ] {
30+ interface ValidationResult {
31+ plugin : string
32+ passed : boolean
33+ config : PluginTomlPlugin | null
34+ errors : ValidationError [ ]
35+ }
36+
37+ const REQUIRED_FIELDS : ( keyof Pick < PluginTomlPlugin , 'summary' | 'version' | 'description' | 'author' > ) [ ] = [
38+ 'summary' ,
39+ 'version' ,
40+ 'description' ,
41+ 'author' ,
42+ ]
43+
44+ const ALL_FIELDS : ( keyof PluginTomlPlugin ) [ ] = [ ...REQUIRED_FIELDS , 'icon' , 'tags' , 'database' ]
45+
46+ function validatePluginToml ( pluginName : string , pluginPath : string ) : ValidationResult | null {
3247 const errors : ValidationError [ ] = [ ]
3348 const tomlPath = path . join ( pluginPath , 'plugin.toml' )
3449
3550 if ( ! fs . existsSync ( tomlPath ) ) {
36- return errors
51+ return null
3752 }
3853
3954 let config : PluginToml
4055 try {
4156 config = toml . parse ( fs . readFileSync ( tomlPath , 'utf-8' ) ) as PluginToml
4257 } catch ( e ) {
4358 errors . push ( {
44- plugin : pluginName ,
4559 field : 'plugin.toml' ,
46- message : `解析失败 : ${ e instanceof Error ? e . message : String ( e ) } ` ,
60+ message : `Parse failed : ${ e instanceof Error ? e . message : String ( e ) } ` ,
4761 } )
48- return errors
62+ return { plugin : pluginName , passed : false , config : null , errors }
4963 }
5064
5165 if ( ! config . plugin ) {
5266 errors . push ( {
53- plugin : pluginName ,
5467 field : 'plugin' ,
55- message : '缺少 [plugin] 配置块 ' ,
68+ message : 'Missing [plugin] section ' ,
5669 } )
57- return errors
70+ return { plugin : pluginName , passed : false , config : null , errors }
5871 }
5972
60- // 验证 tags
73+ // Validate required fields
74+ for ( const field of REQUIRED_FIELDS ) {
75+ const value = config . plugin [ field ]
76+ if ( ! value || ( typeof value === 'string' && value . trim ( ) === '' ) ) {
77+ errors . push ( {
78+ field,
79+ message : 'Required field missing' ,
80+ } )
81+ }
82+ }
83+
84+ // Validate tags
6185 if ( config . plugin . tags && Array . isArray ( config . plugin . tags ) ) {
6286 const invalidTags = config . plugin . tags . filter (
6387 tag => ! VALID_TAGS . includes ( tag . toLowerCase ( ) as ValidTag )
6488 )
6589 if ( invalidTags . length > 0 ) {
6690 errors . push ( {
67- plugin : pluginName ,
6891 field : 'tags' ,
69- message : `包含无效的 tags 值` ,
92+ message : 'Invalid values' ,
7093 invalidValues : invalidTags ,
7194 } )
7295 }
7396 }
7497
75- // 验证 database
98+ // Validate database
7699 if ( config . plugin . database && Array . isArray ( config . plugin . database ) ) {
77100 const invalidDatabases = config . plugin . database . filter (
78101 db => ! VALID_DATABASES . includes ( db . toLowerCase ( ) as ValidDatabase )
79102 )
80103 if ( invalidDatabases . length > 0 ) {
81104 errors . push ( {
82- plugin : pluginName ,
83105 field : 'database' ,
84- message : `包含无效的 database 值` ,
106+ message : 'Invalid values' ,
85107 invalidValues : invalidDatabases ,
86108 } )
87109 }
88110 }
89111
90- return errors
112+ return {
113+ plugin : pluginName ,
114+ passed : errors . length === 0 ,
115+ config : config . plugin ,
116+ errors,
117+ }
118+ }
119+
120+ function formatFieldRow ( name : string , value : string | string [ ] | undefined , error ?: ValidationError ) : string {
121+ if ( Array . isArray ( value ) && value . length > 0 ) {
122+ const display = value . map ( v => `\`${ v } \`` ) . join ( ', ' )
123+ const status = error
124+ ? `❌ ${ error . message } : ${ error . invalidValues ! . map ( v => `\`${ v } \`` ) . join ( ', ' ) } `
125+ : '✅'
126+ return `| \`${ name } \` | ${ display } | ${ status } |\n`
127+ }
128+ if ( typeof value === 'string' && value . trim ( ) ) {
129+ return `| \`${ name } \` | ${ value } | ${ error ? `❌ ${ error . message } ` : '✅' } |\n`
130+ }
131+ return `| \`${ name } \` | - | ${ error ? `❌ ${ error . message } ` : '➖' } |\n`
132+ }
133+
134+ function generateReport ( results : ValidationResult [ ] ) : string {
135+ const allPassed = results . every ( r => r . passed )
136+
137+ let report = `## Plugin TOML Validation Report\n\n`
138+ report += `**Status**: ${ allPassed ? '✅ All Passed' : '❌ Validation Failed' } \n\n`
139+
140+ for ( const result of results ) {
141+ report += `### ${ result . passed ? '✅' : '❌' } \`${ result . plugin } \`\n\n`
142+
143+ if ( ! result . config ) {
144+ result . errors . forEach ( e => report += `> ⚠️ \`${ e . field } \`: ${ e . message } \n` )
145+ report += '\n'
146+ continue
147+ }
148+
149+ const errorMap = new Map ( result . errors . map ( e => [ e . field , e ] ) )
150+ report += `| Field | Value | Status |\n`
151+ report += `|-------|-------|--------|\n`
152+ for ( const field of ALL_FIELDS ) {
153+ report += formatFieldRow ( field , result . config [ field ] , errorMap . get ( field ) )
154+ }
155+ report += '\n'
156+ }
157+
158+ if ( ! allPassed ) {
159+ report += `### Reference\n\n`
160+ report += `| Type | Allowed Values |\n`
161+ report += `|------|----------------|\n`
162+ report += `| Tags | ${ VALID_TAGS . map ( t => `\`${ t } \`` ) . join ( ', ' ) } |\n`
163+ report += `| Database | ${ VALID_DATABASES . map ( d => `\`${ d } \`` ) . join ( ', ' ) } |\n`
164+ }
165+
166+ return report
91167}
92168
93169function main ( ) {
94170 const pluginsDir = path . join ( __dirname , 'plugins' )
95- const allErrors : ValidationError [ ] = [ ]
171+ const results : ValidationResult [ ] = [ ]
96172
97173 if ( ! fs . existsSync ( pluginsDir ) ) {
98- console . log ( 'plugins 目录不存在 ' )
174+ console . log ( 'plugins directory not found ' )
99175 process . exit ( 0 )
100176 }
101177
102- // 支持命令行参数指定要验证的插件
103178 const args = process . argv . slice ( 2 )
104179 let pluginDirs : string [ ]
105180
106181 if ( args . length > 0 ) {
107- // 验证指定的插件
108182 pluginDirs = args . filter ( name => {
109183 const pluginPath = path . join ( pluginsDir , name )
110184 return fs . existsSync ( pluginPath )
111185 } )
112186 } else {
113- // 验证所有插件
114187 pluginDirs = fs . readdirSync ( pluginsDir , { withFileTypes : true } )
115- . filter ( entry => entry . isDirectory ( ) && ! entry . name . startsWith ( '.' ) )
116- . map ( entry => entry . name )
188+ . filter ( entry => entry . isDirectory ( ) && ! entry . name . startsWith ( '.' ) )
189+ . map ( entry => entry . name )
117190 }
118191
119- console . log ( `验证 ${ pluginDirs . length } 个插件...\n` )
120-
121192 for ( const pluginName of pluginDirs ) {
122193 const pluginPath = path . join ( pluginsDir , pluginName )
123- const errors = validatePluginToml ( pluginName , pluginPath )
124- allErrors . push ( ...errors )
194+ const result = validatePluginToml ( pluginName , pluginPath )
195+ if ( result ) {
196+ results . push ( result )
197+ }
125198 }
126199
127- if ( allErrors . length === 0 ) {
128- console . log ( '所有插件验证通过' )
129- console . log ( `\n允许的 tags: ${ VALID_TAGS . join ( ', ' ) } ` )
130- console . log ( `允许的 database: ${ VALID_DATABASES . join ( ', ' ) } ` )
131- process . exit ( 0 )
132- }
200+ const passed = results . filter ( r => r . passed ) . length
201+ const failed = results . filter ( r => ! r . passed ) . length
133202
134- console . error ( '验证失败:\n' )
135- for ( const error of allErrors ) {
136- console . error ( `[${ error . plugin } ] ${ error . field } : ${ error . message } ` )
137- if ( error . invalidValues ) {
138- console . error ( ` 无效值: ${ error . invalidValues . join ( ', ' ) } ` )
203+ // Write report file (for CI)
204+ const reportPath = process . env . VALIDATION_REPORT_PATH
205+ if ( reportPath ) {
206+ fs . writeFileSync ( reportPath , generateReport ( results ) , 'utf-8' )
207+ console . log ( `Done: ${ passed } passed, ${ failed } failed` )
208+ } else {
209+ console . log ( `Validating ${ pluginDirs . length } plugins...\n` )
210+ for ( const { plugin, passed : ok , errors } of results ) {
211+ if ( ok ) {
212+ console . log ( ` ✅ ${ plugin } ` )
213+ continue
214+ }
215+ console . error ( ` ❌ ${ plugin } ` )
216+ errors . forEach ( ( { field, message, invalidValues } ) =>
217+ console . error ( ` - ${ field } : ${ invalidValues ? `${ message } : ${ invalidValues . join ( ', ' ) } ` : message } ` )
218+ )
219+ }
220+ console . log ( `\nResult: ${ passed } passed, ${ failed } failed` )
221+ if ( failed > 0 ) {
222+ console . error ( `\nAllowed tags: ${ VALID_TAGS . join ( ', ' ) } ` )
223+ console . error ( `Allowed database: ${ VALID_DATABASES . join ( ', ' ) } ` )
139224 }
140225 }
141226
142- console . error ( `\n允许的 tags: ${ VALID_TAGS . join ( ', ' ) } ` )
143- console . error ( `允许的 database: ${ VALID_DATABASES . join ( ', ' ) } ` )
144- process . exit ( 1 )
227+ process . exit ( failed > 0 ? 1 : 0 )
145228}
146229
147230main ( )
0 commit comments