@@ -13,7 +13,7 @@ import { dirname, join } from 'node:path';
1313import { promisify } from 'node:util' ;
1414
1515const execFileAsync = promisify ( execFile ) ;
16- let prettierCliPath : string | null | undefined ;
16+ let formatter : { name : string ; cliPath : string } | null | undefined ;
1717
1818/**
1919 * Conservative upper bound for the length of a single spawned command line.
@@ -60,7 +60,7 @@ export function batchFilesByArgumentLength(
6060}
6161
6262/**
63- * Formats files using Prettier.
63+ * Formats files using the workspace's Prettier installation, falling back to Oxfmt .
6464 * @param cwd The current working directory.
6565 * @param files The files to format.
6666 */
@@ -69,40 +69,46 @@ export async function formatFiles(cwd: string, files: Set<string>): Promise<void
6969 return ;
7070 }
7171
72- if ( prettierCliPath === undefined ) {
73- try {
74- const prettierPath = createRequire ( cwd + '/' ) . resolve ( 'prettier/package.json' ) ;
75- const prettierPackageJson = JSON . parse ( await readFile ( prettierPath , 'utf-8' ) ) as {
76- bin : string ;
77- } ;
78- prettierCliPath = join ( dirname ( prettierPath ) , prettierPackageJson . bin ) ;
79- } catch {
80- // Prettier is not installed.
81- prettierCliPath = null ;
72+ if ( formatter === undefined ) {
73+ const workspaceRequire = createRequire ( cwd + '/' ) ;
74+ formatter = null ;
75+ for ( const name of [ 'prettier' , 'oxfmt' ] ) {
76+ try {
77+ const packageJsonPath = workspaceRequire . resolve ( `${ name } /package.json` ) ;
78+ const { bin } = JSON . parse ( await readFile ( packageJsonPath , 'utf-8' ) ) as {
79+ bin : string | Record < string , string > ;
80+ } ;
81+ formatter = {
82+ name,
83+ cliPath : join ( dirname ( packageJsonPath ) , typeof bin === 'string' ? bin : bin [ name ] ) ,
84+ } ;
85+ break ;
86+ } catch {
87+ continue ;
88+ }
8289 }
8390 }
8491
85- if ( ! prettierCliPath ) {
92+ if ( ! formatter ) {
8693 return ;
8794 }
8895
89- const baseArgs = [
90- prettierCliPath ,
91- '--write' ,
92- '--no-error-on-unmatched-pattern' ,
93- '--ignore-unknown' ,
94- ] ;
96+ const { cliPath : formatterCliPath } = formatter ;
97+ const baseArgs = [ formatterCliPath , '--write' , '--no-error-on-unmatched-pattern' ] ;
98+ if ( formatter . name === 'prettier' ) {
99+ baseArgs . push ( '--ignore-unknown' ) ;
100+ }
95101 // `process.execPath` is spawned as the first argument and also counts toward the
96- // command-line limit; it and `prettierCliPath ` are absolute paths that may contain
102+ // command-line limit; it and `formatterCliPath ` are absolute paths that may contain
97103 // spaces and therefore be quoted.
98104 const baseLength = [ process . execPath , ...baseArgs ] . reduce (
99105 ( total , arg ) => total + arg . length + ( arg . includes ( ' ' ) ? 3 : 1 ) ,
100106 0 ,
101107 ) ;
102108
103- // Spawn Prettier once per batch so repositories with many changed files do not
109+ // Spawn the formatter once per batch so repositories with many changed files do not
104110 // overflow the OS command-line length limit. A failure in one batch (e.g. a file
105- // Prettier cannot parse) must not stop the remaining batches, matching the previous
111+ // the formatter cannot parse) must not stop the remaining batches, matching the previous
106112 // single-invocation behavior, so errors are collected and reported together.
107113 const errors : string [ ] = [ ] ;
108114 for ( const batch of batchFilesByArgumentLength ( files , baseLength , MAX_COMMAND_LINE_LENGTH ) ) {
0 commit comments