@@ -11,7 +11,9 @@ import { execa } from 'execa'
1111import { globby } from 'globby'
1212import ansiEscapes from 'ansi-escapes'
1313
14- import type { CommandFixOptions , Project } from '#types'
14+ import type { CommandFixOptions , Config , Issue , Project } from '#types'
15+ import type { PackageJson } from 'type-fest'
16+ import _ from 'lodash'
1517
1618export async function run ( options : CommandFixOptions , positionals : string [ ] ) {
1719 if ( positionals . length > 0 ) {
@@ -20,34 +22,37 @@ export async function run(options: CommandFixOptions, positionals: string[]) {
2022
2123 const project = await getProject ( )
2224
23- let config = { }
25+ const globalConfig = {
26+ skippedOrganizations : [ 'bash-bastion' ] ,
27+ skippedRepositories : [ 'SchemaStore/schemastore' ] ,
28+ }
29+
30+ let config : Config = {
31+ rules : {
32+ ...( project . type === 'with-remote-url' &&
33+ `${ project . owner } /${ project . name } ` === 'awesome-lists/awesome-bash'
34+ ? {
35+ 'remote-url/remote-metadata/disable-projects-tab' : 'off' ,
36+ 'remote-url/remote-metadata/default-branch-main' : 'off' ,
37+ }
38+ : { } ) ,
39+ } ,
40+ }
2441 try {
25- config = toml . parse (
26- await fs . readFile ( path . join ( project . rootDir , 'project.toml' ) , 'utf-8' ) ,
27- )
42+ config = _ . merge (
43+ config ,
44+ toml . parse ( await fs . readFile ( path . join ( project . rootDir , 'dev.toml' ) , 'utf-8' ) ) ,
45+ ) as Config
2846 } catch ( err ) {
2947 if ( ( err as NodeJS . ErrnoException ) ?. code !== 'ENOENT' ) {
3048 throw err
3149 }
3250 }
3351
34- // Print metadata.
35- {
36- let str = ''
37- str += `${ styleText ( [ 'blue' , 'bold' ] , 'Directory:' ) } ${ project . rootDir } \n`
38- if ( project . type === 'with-remote-url' ) {
39- str += `${ styleText ( [ 'blue' , 'bold' ] , 'Project:' ) } ${ ansiEscapes . link ( `${ project . owner } /${ project . name } ` , `https://github.com/${ project . owner } /${ project . name } ` ) } \n`
40- }
41-
42- process . stdout . write ( str )
43- }
44-
45- // TODO
46- const skippedOrganizations = [ 'bash-bastion' ]
47- const skippedRepositories = [ 'SchemaStore/schemastore' ]
4852 if (
49- skippedRepositories . includes ( `${ project . owner } /${ project . name } ` ) ||
50- skippedOrganizations . includes ( project . owner )
53+ project . type === 'with-remote-url' &&
54+ ( globalConfig . skippedRepositories . includes ( `${ project . owner } /${ project . name } ` ) ||
55+ globalConfig . skippedOrganizations . includes ( project . owner ) )
5156 ) {
5257 console . info ( `[${ styleText ( 'yellow' , 'SKIP' ) } ] ${ project . owner } /${ project . name } ` )
5358 return
@@ -80,13 +85,53 @@ export async function run(options: CommandFixOptions, positionals: string[]) {
8085 }
8186
8287 // Collect rule files that match the ecosystem.
88+ const ecosystems : string [ ] = [ ]
8389 {
8490 if ( await fileExists ( 'package.json' ) ) {
8591 await collect ( `400-ecosystem/nodejs/*` )
92+ ecosystems . push ( 'nodejs' )
93+
94+ const content : PackageJson = JSON . parse ( await fs . readFile ( 'package.json' , 'utf-8' ) )
95+ if ( content . displayName ) {
96+ await collect ( `400-ecosystem/vscode-extension/*` )
97+ ecosystems . push ( 'vscode-extension' )
98+ }
8699 }
87100
88101 if ( ( await fileExists ( 'deno.jsonc' ) ) || ( await fileExists ( 'deno.json' ) ) ) {
89102 await collect ( `400-ecosystem/deno/*` )
103+ ecosystems . push ( 'deno' )
104+ }
105+
106+ if ( ( await globby ( '*.c' ) ) . length > 0 ) {
107+ await collect ( `400-ecosystem/c/*` )
108+ ecosystems . push ( 'c' )
109+ }
110+
111+ // https://cmake.org/cmake/help/latest/command/project.html
112+ if ( await fileExists ( 'CMakeLists.txt' ) ) {
113+ const content = await fs . readFile ( 'CMakeLists.txt' , 'utf-8' )
114+ const language : { groups ?: { lang ?: string } } = content . match (
115+ / p r o j e c t \( (?: .* ? (?< lang > [ a - z A - Z ] + ) \) | .* ?L A N G U A G E S [ \t ] + (?< lang > [ a - z A - Z ] + ) ) / ,
116+ ) as any
117+ if ( language . groups ?. lang === 'C' ) {
118+ await collect ( `400-ecosystem/c/*` )
119+ ecosystems . push ( 'c' )
120+ } else if ( language . groups ?. lang === 'CXX' ) {
121+ await collect ( `400-ecosystem/cpp/*` )
122+ ecosystems . push ( 'cpp' )
123+ }
124+ }
125+
126+ if ( await fileExists ( 'basalt.toml' ) ) {
127+ await collect ( `400-ecosystem/bash/*` )
128+ ecosystems . push ( 'bash' )
129+ }
130+
131+ // https://zed.dev/docs/extensions/developing-extensions
132+ if ( await fileExists ( 'extension.toml' ) ) {
133+ await collect ( `400-ecosystem/zed-extension/*` )
134+ ecosystems . push ( 'zed-extension' )
90135 }
91136
92137 await collect ( `400-ecosystem/_/*` )
@@ -116,35 +161,44 @@ export async function run(options: CommandFixOptions, positionals: string[]) {
116161 }
117162 }
118163
119- for ( const fixFile of ruleFiles ) {
120- const fixId = path . basename ( path . dirname ( fixFile ) ) + '/' + path . parse ( fixFile ) . name
121- if ( ` ${ project . owner } / ${ project . name } ` === 'awesome-lists/awesome-bash' ) {
122- if (
123- fixFile . includes ( '300-remote-url/repo-metadata' ) ||
124- fixFile . includes ( '_/editorconfig' )
125- ) {
126- console . info ( `[ ${ styleText ( 'yellow' , 'SKIP' ) } ] ${ fixId } ` )
127- continue
128- }
164+ // Print metadata.
165+ {
166+ let str = ''
167+ str += ` ${ styleText ( [ 'blue' , 'bold' ] , 'Directory:' ) } ${ project . rootDir } \n`
168+ str += ` ${ styleText ( [ 'blue' , 'bold' ] , 'Ecosystems:' ) } ${ new Intl . ListFormat ( ) . format ( ecosystems ) } \n`
169+ if ( project . type === 'with-remote-url' ) {
170+ str += ` ${ styleText ( [ 'blue' , 'bold' ] , 'Project:' ) } ${ ansiEscapes . link (
171+ ` ${ project . owner } / ${ project . name } ` ,
172+ `https://github.com/ ${ project . owner } / ${ project . name } ` ,
173+ ) } \n`
129174 }
130175
176+ process . stdout . write ( str )
177+ }
178+
179+ for ( const fixFile of ruleFiles ) {
131180 await fixFromFile (
132181 path . join ( pkgRoot ( ) , 'config/lint-rules' , fixFile ) ,
133182 project ,
134183 options ,
184+ config ,
135185 )
136186 }
137187
138188 console . info ( 'Done.' )
139189}
140190
141- /**
142- * @param {string } fixFile
143- ] * @param {Project } project
144- * @param {CommandFixOptions } options
145- */
146- async function fixFromFile ( fixFile , project , options ) {
147- const fixId = path . basename ( path . dirname ( fixFile ) ) + '/' + path . parse ( fixFile ) . name
191+ async function fixFromFile (
192+ fixFile : string ,
193+ project : Project ,
194+ options : CommandFixOptions ,
195+ config : Config ,
196+ ) {
197+ const fixId = (
198+ path . basename ( path . dirname ( fixFile ) ) +
199+ '/' +
200+ path . parse ( fixFile ) . name
201+ ) . replaceAll ( / [ 0 - 9 ] + - / gu, '' )
148202
149203 const module = await import ( fixFile )
150204 if ( ! module . issues ) {
@@ -160,8 +214,18 @@ async function fixFromFile(fixFile, project, options) {
160214
161215 try {
162216 let failed = false
163- const issues = module . issues ( { project } )
217+ const issues : AsyncGenerator < Issue > = module . issues ( { project } )
164218 for await ( const issue of issues ) {
219+ if ( issue . id ) {
220+ if ( `${ fixId } /${ issue . id } ` in ( config . rules ?? { } ) ) {
221+ const rule = ( config . rules ?? { } ) [ `${ fixId } /${ issue . id } ` ]
222+ if ( rule === 'off' ) {
223+ console . info ( `[${ styleText ( 'yellow' , 'SKIP' ) } ] ${ fixId } /${ issue . id } ` )
224+ continue
225+ }
226+ }
227+ }
228+
165229 console . info ( `[EVAL] ${ fixId } : Found issue` )
166230 if ( Array . isArray ( issue . message ) ) {
167231 for ( const message of issue . message ) {
@@ -196,7 +260,7 @@ async function fixFromFile(fixFile, project, options) {
196260 await issue . fix ( )
197261 } else {
198262 printWithTips ( `[${ styleText ( 'red' , 'FAIL' ) } ] ${ fixId } ` , [
199- 'Failed because running fix function was declined ' ,
263+ 'Failed because the fix function was not executed ' ,
200264 ] )
201265 failed = true
202266 break
@@ -217,10 +281,7 @@ async function fixFromFile(fixFile, project, options) {
217281 }
218282}
219283
220- /**
221- * @returns {Promise<Project> }
222- */
223- async function getProject ( ) {
284+ async function getProject ( ) : Promise < Project > {
224285 if ( ! ( await fileExists ( '.git' ) ) ) {
225286 return {
226287 type : 'only-directory' ,
0 commit comments