@@ -13,54 +13,97 @@ type ResolveFmtConfigOptions = {
1313 cwd : string ;
1414} ;
1515
16+ type PathMatcher = ( filePath : string ) => boolean ;
17+
18+ const neverMatches : PathMatcher = ( ) => false ;
19+
20+ const compileMatchers = (
21+ patterns : string [ ] ,
22+ excludedPatterns : string | string [ ] | undefined ,
23+ basename : boolean ,
24+ ) : PathMatcher | undefined => {
25+ if ( patterns . length === 0 ) {
26+ return ;
27+ }
28+
29+ const options = {
30+ ignore : excludedPatterns ,
31+ basename,
32+ dot : true ,
33+ } ;
34+
35+ if ( patterns . length === 1 ) {
36+ return micromatch . matcher ( patterns [ 0 ] , options ) ;
37+ }
38+
39+ const matchers = patterns . map ( ( pattern ) => micromatch . matcher ( pattern , options ) ) ;
40+
41+ return ( filePath ) => {
42+ for ( const matches of matchers ) {
43+ if ( matches ( filePath ) ) {
44+ return true ;
45+ }
46+ }
47+ return false ;
48+ } ;
49+ } ;
50+
51+ const createPathMatcher = (
52+ patterns : string | string [ ] ,
53+ excludedPatterns ?: string | string [ ] ,
54+ ) : PathMatcher => {
55+ const pathPatterns : string [ ] = [ ] ;
56+ const basenamePatterns : string [ ] = [ ] ;
57+
58+ for ( const pattern of Array . isArray ( patterns ) ? patterns : [ patterns ] ) {
59+ if ( pattern . includes ( '/' ) ) {
60+ pathPatterns . push ( pattern ) ;
61+ } else {
62+ basenamePatterns . push ( pattern ) ;
63+ }
64+ }
65+
66+ const basenameMatcher = compileMatchers ( basenamePatterns , excludedPatterns , true ) ;
67+ const pathMatcher = compileMatchers ( pathPatterns , excludedPatterns , false ) ;
68+
69+ if ( ! basenameMatcher || ! pathMatcher ) {
70+ return basenameMatcher ?? pathMatcher ?? neverMatches ;
71+ }
72+ return ( filePath ) => basenameMatcher ( filePath ) || pathMatcher ( filePath ) ;
73+ } ;
74+
1675/** Splits a flat config into project-level formatting options and rules. */
1776const normalizeFmtConfig = ( config : FmtConfig | undefined , rootPath : string ) : ResolvedFmtConfig => {
1877 const { ignorePatterns = [ ] , overrides = [ ] , ...baseOptions } = config ?? { } ;
1978
2079 return {
2180 rootPath,
2281 baseOptions,
23- overrides,
82+ overrides : overrides . map ( ( { files, excludeFiles, options } ) => ( {
83+ matches : createPathMatcher ( files , excludeFiles ) ,
84+ options,
85+ } ) ) ,
2486 ignorePatterns,
2587 } ;
2688} ;
2789
28- const pathMatchesGlobs = (
29- filePath : string ,
30- patterns : string | string [ ] ,
31- excludedPatterns ?: string | string [ ] ,
32- ) : boolean => {
33- const patternList = Array . isArray ( patterns ) ? patterns : [ patterns ] ;
34- const withSlashes = patternList . filter ( ( pattern ) => pattern . includes ( '/' ) ) ;
35- const withoutSlashes = patternList . filter ( ( pattern ) => ! pattern . includes ( '/' ) ) ;
36-
37- return (
38- micromatch . isMatch ( filePath , withoutSlashes , {
39- ignore : excludedPatterns ,
40- basename : true ,
41- dot : true ,
42- } ) ||
43- micromatch . isMatch ( filePath , withSlashes , {
44- ignore : excludedPatterns ,
45- basename : false ,
46- dot : true ,
47- } )
48- ) ;
49- } ;
50-
5190/** Applies matching overrides to the shared formatter options. */
5291const resolveFmtOptions = ( filePath : string , config : ResolvedFmtConfig ) : ResolvedFmtOptions => {
5392 if ( config . overrides . length === 0 ) {
5493 return config . baseOptions ;
5594 }
5695
57- const options = { ... config . baseOptions } ;
96+ let options = config . baseOptions ;
5897 const relativeFilePath = relative ( config . rootPath , filePath ) ;
5998
6099 for ( const override of config . overrides ) {
61- if ( pathMatchesGlobs ( relativeFilePath , override . files , override . excludeFiles ) ) {
62- Object . assign ( options , override . options ) ;
100+ if ( ! override . options || ! override . matches ( relativeFilePath ) ) {
101+ continue ;
102+ }
103+ if ( options === config . baseOptions ) {
104+ options = { ...options } ;
63105 }
106+ Object . assign ( options , override . options ) ;
64107 }
65108
66109 return options ;
0 commit comments