UNPKG

eslint-remote-tester

Version:
114 lines (113 loc) 4.26 kB
import fs from 'node:fs'; import * as JSONStream from 'JSONStream'; import { RESULTS_COMPARISON_CACHE_LOCATION, RESULTS_COMPARE_LOCATION, } from './file-constants.js'; import { RESULT_PARSER_TO_COMPARE_TEMPLATE, RESULT_PARSER_TO_EXTENSION, ComparisonTypes, } from './result-templates.js'; import config from '../config/index.js'; export const RESULT_COMPARISON_TEMPLATE = RESULT_PARSER_TO_COMPARE_TEMPLATE[config.resultParser]; const EXTENSION = RESULT_PARSER_TO_EXTENSION[config.resultParser]; /** * Compare two sets of `Result`s and get diff of changes * - `added`: results which were present in previous scan but not in the current * - `removed`: results which are present in current scan but not in the previous */ export async function compareResults(current) { const previous = await readCache(); if (previous.length === 0) { // All results are new return { added: current, removed: [] }; } // Using Map instead of arrays for comparison is faster: with ~500Mb JSON 20min vs 2s const mapCurrent = initializeMap(current); const mapPrevious = initializeMap(previous); function generatePredicate(resultsMap) { return function predicate(result) { return !resultsMap.has(result.__internalHash); }; } return { added: current.filter(generatePredicate(mapPrevious)), removed: previous.filter(generatePredicate(mapCurrent)), }; } /** * Write comparison results to file system and update cache with current results */ export async function writeComparisonResults(comparisonResults, currentScanResults) { await writeComparisons(comparisonResults); if (config.updateComparisonReference) { await updateCache(currentScanResults); } } async function readCache() { if (!fs.existsSync(RESULTS_COMPARISON_CACHE_LOCATION)) { return []; } const results = []; await new Promise((resolve, reject) => { fs.createReadStream(RESULTS_COMPARISON_CACHE_LOCATION, { encoding: 'utf8', }) .pipe(JSONStream.parse('*')) .on('data', result => results.push(result)) .on('end', resolve) .on('error', reject); }); return results; } async function updateCache(currentScanResults) { if (fs.existsSync(RESULTS_COMPARISON_CACHE_LOCATION)) { fs.unlinkSync(RESULTS_COMPARISON_CACHE_LOCATION); } return new Promise((resolve, reject) => { const stream = fs.createWriteStream(RESULTS_COMPARISON_CACHE_LOCATION); const jstream = JSONStream.stringify() .pipe(stream) .on('finish', resolve) .on('error', reject); const copy = [...currentScanResults]; while (copy.length > 0) { const chunk = copy.splice(0, 200); jstream.write(JSON.stringify(chunk)); } jstream.end(); stream.end(); }); } async function writeComparisons(comparisonResults) { // Directory should always be available but let's handle condition where // user intentionally removes it during scan. if (!fs.existsSync(RESULTS_COMPARE_LOCATION)) { fs.mkdirSync(RESULTS_COMPARE_LOCATION, { recursive: true }); } for (const type of ComparisonTypes) { const results = [...comparisonResults[type]]; if (!results.length) continue; await new Promise((resolve, reject) => { const stream = fs .createWriteStream(`${RESULTS_COMPARE_LOCATION}/${type}${EXTENSION}`, { encoding: 'utf8' }) .on('finish', resolve) .on('error', reject); stream.write(RESULT_COMPARISON_TEMPLATE.header(type)); stream.write('\n'); while (results.length > 0) { const chunk = results .splice(0, 200) .map(RESULT_COMPARISON_TEMPLATE.results) .join('\n'); stream.write(chunk); } stream.end(); }); } } /** * Create map of `result._internalHash`'s */ function initializeMap(results) { const map = new Map(); for (const key of results.map(c => c.__internalHash)) { map.set(key, true); } return map; }