1import fs from 'fs/promises'; 2import path from 'path'; 3 4import type { HashSource } from '../Fingerprint.types'; 5 6export async function getFileBasedHashSourceAsync( 7 projectRoot: string, 8 filePath: string, 9 reason: string 10): Promise<HashSource | null> { 11 let result: HashSource | null = null; 12 try { 13 const stat = await fs.stat(path.join(projectRoot, filePath)); 14 result = { 15 type: stat.isDirectory() ? 'dir' : 'file', 16 filePath, 17 reasons: [reason], 18 }; 19 } catch { 20 result = null; 21 } 22 return result; 23} 24 25/** 26 * A version of `JSON.stringify` that keeps the keys sorted 27 */ 28export function stringifyJsonSorted(target: any, space?: string | number | undefined): string { 29 return JSON.stringify(target, (_, value) => sortJson(value), space); 30} 31 32function sortJson(json: any): any { 33 if (Array.isArray(json)) { 34 return json.sort((a, b) => { 35 // Sort array items by their stringified value. 36 // We don't need the array to be sorted in meaningful way, just to be sorted in deterministic. 37 // E.g. `[{ b: '2' }, {}, { a: '3' }, null]` -> `[null, { a : '3' }, { b: '2' }, {}]` 38 // This result is not a perfect solution, but it's good enough for our use case. 39 const stringifiedA = stringifyJsonSorted(a); 40 const stringifiedB = stringifyJsonSorted(b); 41 if (stringifiedA < stringifiedB) { 42 return -1; 43 } else if (stringifiedA > stringifiedB) { 44 return 1; 45 } 46 return 0; 47 }); 48 } 49 50 if (json != null && typeof json === 'object') { 51 // Sort object items by keys 52 return Object.keys(json) 53 .sort() 54 .reduce((acc: any, key: string) => { 55 acc[key] = json[key]; 56 return acc; 57 }, {}); 58 } 59 60 // Return primitives 61 return json; 62} 63