(allFiles: string[], action: FormatterAction)
| 42 | * The promise resolves with a list of failures, or `false` if no formatters have matched. |
| 43 | */ |
| 44 | export function runFormatterInParallel(allFiles: string[], action: FormatterAction) { |
| 45 | return new Promise<false | FormatFailure[]>(async (resolve) => { |
| 46 | const formatters = await getActiveFormatters(); |
| 47 | const failures: FormatFailure[] = []; |
| 48 | const pendingCommands: {formatter: Formatter; file: string}[] = []; |
| 49 | |
| 50 | for (const formatter of formatters) { |
| 51 | pendingCommands.push( |
| 52 | ...multimatch |
| 53 | .call(undefined, allFiles, formatter.getFileMatcher(), {dot: true}) |
| 54 | .map((file) => ({formatter, file})), |
| 55 | ); |
| 56 | } |
| 57 | |
| 58 | // If no commands are generated, resolve the promise as `false` as no files |
| 59 | // were run against the any formatters. |
| 60 | if (pendingCommands.length === 0) { |
| 61 | return resolve(false); |
| 62 | } |
| 63 | |
| 64 | switch (action) { |
| 65 | case 'format': |
| 66 | Log.info(`Formatting ${pendingCommands.length} file(s)`); |
| 67 | break; |
| 68 | case 'check': |
| 69 | Log.info(`Checking format of ${pendingCommands.length} file(s)`); |
| 70 | break; |
| 71 | default: |
| 72 | throw Error(`Invalid format action "${action}": allowed actions are "format" and "check"`); |
| 73 | } |
| 74 | |
| 75 | // The progress bar instance to use for progress tracking. |
| 76 | const progressBar = new Bar({ |
| 77 | format: `[{bar}] ETA: {eta}s | {value}/{total} files`, |
| 78 | clearOnComplete: true, |
| 79 | }); |
| 80 | // A local copy of the files to run the command on. |
| 81 | // An array to represent the current usage state of each of the threads for parallelization. |
| 82 | const threads = new Array<boolean>(AVAILABLE_THREADS).fill(false); |
| 83 | |
| 84 | // Recursively run the command on the next available file from the list using the provided |
| 85 | // thread. |
| 86 | function runCommandInThread(thread: number) { |
| 87 | const nextCommand = pendingCommands.pop(); |
| 88 | // If no file was pulled from the array, return as there are no more files to run against. |
| 89 | if (nextCommand === undefined) { |
| 90 | threads[thread] = false; |
| 91 | return; |
| 92 | } |
| 93 | |
| 94 | // Get the file and formatter for the next command. |
| 95 | const {file, formatter} = nextCommand; |
| 96 | |
| 97 | if (lstatSync(file).isSymbolicLink()) { |
| 98 | throw new Error(`Security violation: symlink detected for file ${file}`); |
| 99 | } |
| 100 | |
| 101 | const [spawnCmd, ...spawnArgs] = [...formatter.commandFor(action), '--', file]; |
no test coverage detected