@@ -6,7 +6,7 @@ export interface Info {
66 command : string [ ]
77 environment ?: Record < string , string >
88 extensions : string [ ]
9- enabled : ( projectDir : string ) => Promise < boolean >
9+ enabled : ( filePath : string , projectDir : string ) => Promise < boolean >
1010}
1111
1212/**
@@ -21,12 +21,17 @@ async function findUp(
2121 let currentDir = startDir
2222 const root = stopDir ?? path . parse ( startDir ) . root
2323
24- while ( currentDir !== root && currentDir !== path . dirname ( currentDir ) ) {
24+ while ( true ) {
2525 const filePath = path . join ( currentDir , filename )
2626 const file = Bun . file ( filePath )
2727 if ( await file . exists ( ) ) {
2828 results . push ( filePath )
2929 }
30+
31+ // Stop if we've reached the stopDir or filesystem root
32+ if ( currentDir === root || currentDir === path . dirname ( currentDir ) ) {
33+ break
34+ }
3035 currentDir = path . dirname ( currentDir )
3136 }
3237
@@ -41,7 +46,7 @@ export const gofmt: Info = {
4146 name : 'gofmt' ,
4247 command : [ 'gofmt' , '-w' , '$FILE' ] ,
4348 extensions : [ '.go' ] ,
44- async enabled ( ) {
49+ async enabled ( _filePath , _projectDir ) {
4550 return Bun . which ( 'gofmt' ) !== null
4651 } ,
4752}
@@ -50,7 +55,7 @@ export const mix: Info = {
5055 name : 'mix' ,
5156 command : [ 'mix' , 'format' , '$FILE' ] ,
5257 extensions : [ '.ex' , '.exs' , '.eex' , '.heex' , '.leex' , '.neex' , '.sface' ] ,
53- async enabled ( ) {
58+ async enabled ( _filePath , _projectDir ) {
5459 return Bun . which ( 'mix' ) !== null
5560 } ,
5661}
@@ -89,8 +94,9 @@ export const prettier: Info = {
8994 '.graphql' ,
9095 '.gql' ,
9196 ] ,
92- async enabled ( projectDir : string ) {
93- const items = await findUp ( 'package.json' , projectDir )
97+ async enabled ( filePath : string , projectDir : string ) {
98+ const startDir = path . dirname ( filePath )
99+ const items = await findUp ( 'package.json' , startDir , projectDir )
94100 for ( const item of items ) {
95101 const json = await Bun . file ( item ) . json ( )
96102 if ( json . dependencies ?. prettier )
@@ -136,10 +142,11 @@ export const biome: Info = {
136142 '.graphql' ,
137143 '.gql' ,
138144 ] ,
139- async enabled ( projectDir : string ) {
145+ async enabled ( filePath : string , projectDir : string ) {
146+ const startDir = path . dirname ( filePath )
140147 const configs = [ 'biome.json' , 'biome.jsonc' ]
141148 for ( const config of configs ) {
142- const found = await findUp ( config , projectDir )
149+ const found = await findUp ( config , startDir , projectDir )
143150 if ( found . length > 0 ) {
144151 return true
145152 }
@@ -152,7 +159,7 @@ export const zig: Info = {
152159 name : 'zig' ,
153160 command : [ 'zig' , 'fmt' , '$FILE' ] ,
154161 extensions : [ '.zig' , '.zon' ] ,
155- async enabled ( ) {
162+ async enabled ( _filePath , _projectDir ) {
156163 return Bun . which ( 'zig' ) !== null
157164 } ,
158165}
@@ -161,8 +168,9 @@ export const clang: Info = {
161168 name : 'clang-format' ,
162169 command : [ 'clang-format' , '-i' , '$FILE' ] ,
163170 extensions : [ '.c' , '.cc' , '.cpp' , '.cxx' , '.c++' , '.h' , '.hh' , '.hpp' , '.hxx' , '.h++' , '.ino' , '.C' , '.H' ] ,
164- async enabled ( projectDir : string ) {
165- const items = await findUp ( '.clang-format' , projectDir )
171+ async enabled ( filePath : string , projectDir : string ) {
172+ const startDir = path . dirname ( filePath )
173+ const items = await findUp ( '.clang-format' , startDir , projectDir )
166174 return items . length > 0
167175 } ,
168176}
@@ -171,7 +179,7 @@ export const ktlint: Info = {
171179 name : 'ktlint' ,
172180 command : [ 'ktlint' , '-F' , '$FILE' ] ,
173181 extensions : [ '.kt' , '.kts' ] ,
174- async enabled ( ) {
182+ async enabled ( _filePath , _projectDir ) {
175183 return Bun . which ( 'ktlint' ) !== null
176184 } ,
177185}
@@ -180,12 +188,13 @@ export const ruff: Info = {
180188 name : 'ruff' ,
181189 command : [ 'ruff' , 'format' , '$FILE' ] ,
182190 extensions : [ '.py' , '.pyi' ] ,
183- async enabled ( projectDir : string ) {
191+ async enabled ( filePath : string , projectDir : string ) {
184192 if ( ! Bun . which ( 'ruff' ) )
185193 return false
194+ const startDir = path . dirname ( filePath )
186195 const configs = [ 'pyproject.toml' , 'ruff.toml' , '.ruff.toml' ]
187196 for ( const config of configs ) {
188- const found = await findUp ( config , projectDir )
197+ const found = await findUp ( config , startDir , projectDir )
189198 const firstFound = found [ 0 ]
190199 if ( firstFound ) {
191200 if ( config === 'pyproject.toml' ) {
@@ -200,7 +209,7 @@ export const ruff: Info = {
200209 }
201210 const deps = [ 'requirements.txt' , 'pyproject.toml' , 'Pipfile' ]
202211 for ( const dep of deps ) {
203- const found = await findUp ( dep , projectDir )
212+ const found = await findUp ( dep , startDir , projectDir )
204213 const firstFound = found [ 0 ]
205214 if ( firstFound ) {
206215 const content = await Bun . file ( firstFound ) . text ( )
@@ -216,7 +225,7 @@ export const rlang: Info = {
216225 name : 'air' ,
217226 command : [ 'air' , 'format' , '$FILE' ] ,
218227 extensions : [ '.R' ] ,
219- async enabled ( ) {
228+ async enabled ( _filePath , _projectDir ) {
220229 const airPath = Bun . which ( 'air' )
221230 if ( airPath == null )
222231 return false
@@ -245,8 +254,8 @@ export const uvformat: Info = {
245254 name : 'uv format' ,
246255 command : [ 'uv' , 'format' , '--' , '$FILE' ] ,
247256 extensions : [ '.py' , '.pyi' ] ,
248- async enabled ( projectDir : string ) {
249- if ( await ruff . enabled ( projectDir ) )
257+ async enabled ( filePath : string , projectDir : string ) {
258+ if ( await ruff . enabled ( filePath , projectDir ) )
250259 return false
251260 if ( Bun . which ( 'uv' ) !== null ) {
252261 const proc = Bun . spawn ( [ 'uv' , 'format' , '--help' ] , { stderr : 'pipe' , stdout : 'pipe' } )
@@ -261,7 +270,7 @@ export const rubocop: Info = {
261270 name : 'rubocop' ,
262271 command : [ 'rubocop' , '--autocorrect' , '$FILE' ] ,
263272 extensions : [ '.rb' , '.rake' , '.gemspec' , '.ru' ] ,
264- async enabled ( ) {
273+ async enabled ( _filePath , _projectDir ) {
265274 return Bun . which ( 'rubocop' ) !== null
266275 } ,
267276}
@@ -270,7 +279,7 @@ export const standardrb: Info = {
270279 name : 'standardrb' ,
271280 command : [ 'standardrb' , '--fix' , '$FILE' ] ,
272281 extensions : [ '.rb' , '.rake' , '.gemspec' , '.ru' ] ,
273- async enabled ( ) {
282+ async enabled ( _filePath , _projectDir ) {
274283 return Bun . which ( 'standardrb' ) !== null
275284 } ,
276285}
@@ -279,7 +288,7 @@ export const htmlbeautifier: Info = {
279288 name : 'htmlbeautifier' ,
280289 command : [ 'htmlbeautifier' , '$FILE' ] ,
281290 extensions : [ '.erb' , '.html.erb' ] ,
282- async enabled ( ) {
291+ async enabled ( _filePath , _projectDir ) {
283292 return Bun . which ( 'htmlbeautifier' ) !== null
284293 } ,
285294}
@@ -288,7 +297,7 @@ export const dart: Info = {
288297 name : 'dart' ,
289298 command : [ 'dart' , 'format' , '$FILE' ] ,
290299 extensions : [ '.dart' ] ,
291- async enabled ( ) {
300+ async enabled ( _filePath , _projectDir ) {
292301 return Bun . which ( 'dart' ) !== null
293302 } ,
294303}
@@ -297,10 +306,11 @@ export const ocamlformat: Info = {
297306 name : 'ocamlformat' ,
298307 command : [ 'ocamlformat' , '-i' , '$FILE' ] ,
299308 extensions : [ '.ml' , '.mli' ] ,
300- async enabled ( projectDir : string ) {
309+ async enabled ( filePath : string , projectDir : string ) {
301310 if ( ! Bun . which ( 'ocamlformat' ) )
302311 return false
303- const items = await findUp ( '.ocamlformat' , projectDir )
312+ const startDir = path . dirname ( filePath )
313+ const items = await findUp ( '.ocamlformat' , startDir , projectDir )
304314 return items . length > 0
305315 } ,
306316}
@@ -309,7 +319,7 @@ export const terraform: Info = {
309319 name : 'terraform' ,
310320 command : [ 'terraform' , 'fmt' , '$FILE' ] ,
311321 extensions : [ '.tf' , '.tfvars' ] ,
312- async enabled ( ) {
322+ async enabled ( _filePath , _projectDir ) {
313323 return Bun . which ( 'terraform' ) !== null
314324 } ,
315325}
@@ -318,7 +328,7 @@ export const latexindent: Info = {
318328 name : 'latexindent' ,
319329 command : [ 'latexindent' , '-w' , '-s' , '$FILE' ] ,
320330 extensions : [ '.tex' ] ,
321- async enabled ( ) {
331+ async enabled ( _filePath , _projectDir ) {
322332 return Bun . which ( 'latexindent' ) !== null
323333 } ,
324334}
@@ -327,7 +337,7 @@ export const gleam: Info = {
327337 name : 'gleam' ,
328338 command : [ 'gleam' , 'format' , '$FILE' ] ,
329339 extensions : [ '.gleam' ] ,
330- async enabled ( ) {
340+ async enabled ( _filePath , _projectDir ) {
331341 return Bun . which ( 'gleam' ) !== null
332342 } ,
333343}
@@ -339,11 +349,12 @@ export const prisma: Info = {
339349 BUN_BE_BUN : '1' ,
340350 } ,
341351 extensions : [ '.prisma' ] ,
342- async enabled ( projectDir : string ) {
352+ async enabled ( filePath : string , projectDir : string ) {
343353 // Check for schema.prisma or prisma/schema.prisma
354+ const startDir = path . dirname ( filePath )
344355 const schemaFiles = [ 'schema.prisma' , 'prisma/schema.prisma' ]
345356 for ( const schema of schemaFiles ) {
346- const found = await findUp ( schema , projectDir )
357+ const found = await findUp ( schema , startDir , projectDir )
347358 if ( found . length > 0 ) {
348359 return true
349360 }
0 commit comments