1import { SpawnResult } from '@expo/spawn-async'; 2import swiftlint from '@expo/swiftlint'; 3import { ChildProcess } from 'child_process'; 4 5import { EXPO_DIR } from '../Constants'; 6 7/** 8 * Represents a single linter violation. 9 */ 10export type LintViolation = { 11 /** 12 * Number of the line at which the violation starts. 13 */ 14 line: number; 15 16 /** 17 * Column at which the violation starts or `null` when the entire line is violated. 18 */ 19 column: number | null; 20 21 /** 22 * An ID of the violated rule. 23 */ 24 ruleId: string; 25 26 /** 27 * Short description of the rule. 28 */ 29 type: string; 30 31 /** 32 * Full explanation of the rule. 33 */ 34 reason: string; 35 36 /** 37 * Level of the violation. Errors are serious violations and must be fixed. 38 */ 39 severity: 'warning' | 'error'; 40}; 41 42/** 43 * Spawns swiftlint process. 44 */ 45function runAsync(args: string[]): Promise<SpawnResult> { 46 return swiftlint(args, { 47 cwd: EXPO_DIR, 48 }); 49} 50 51/** 52 * Parses JSON reported by `swiftlint` to the array of violations. 53 */ 54function parseLintResultsFromJSONString(jsonString: string): LintViolation[] { 55 try { 56 const json = JSON.parse(jsonString); 57 58 return json.map(({ file, line, character, reason, severity, type, rule_id }) => ({ 59 file, 60 line, 61 column: character ?? 0, 62 reason, 63 severity: severity.toLowerCase(), 64 type, 65 ruleId: rule_id, 66 })); 67 } catch (e) { 68 console.error('Unable to parse as JSON:', jsonString); 69 throw e; 70 } 71} 72 73/** 74 * Returns the version of `swiftlint` binary. 75 */ 76export async function getVersionAsync(): Promise<string | null> { 77 try { 78 const { stdout } = await runAsync(['version']); 79 return stdout.trim(); 80 } catch { 81 return null; 82 } 83} 84 85/** 86 * Lints Swift source code passed as string. 87 */ 88export async function lintStringAsync(str: string): Promise<LintViolation[]> { 89 const promise = runAsync(['lint', '--reporter', 'json', '--quiet', '--use-stdin']); 90 91 // @ts-ignore 92 const child = promise.child as ChildProcess; 93 child.stdin?.write(str); 94 child.stdin?.end(); 95 96 let stdout: string; 97 try { 98 stdout = (await promise).stdout; 99 } catch (error) { 100 stdout = error.stdout; 101 } 102 103 return parseLintResultsFromJSONString(stdout); 104} 105