diff --git a/ts/util/lint/linter.ts b/ts/util/lint/linter.ts index 55384d42c..a6fded2b2 100644 --- a/ts/util/lint/linter.ts +++ b/ts/util/lint/linter.ts @@ -10,7 +10,7 @@ import FastGlob from 'fast-glob'; import type { ExceptionType, RuleType } from './types'; import { REASONS } from './types'; -import { ENCODING, loadJSON, sortExceptions } from './util'; +import { ENCODING, loadJSON, sortExceptions, writeExceptions } from './util'; const ALL_REASONS = REASONS.join('|'); @@ -321,7 +321,11 @@ function setupRules(allRules: Array) { }); } -async function main(): Promise { +async function main(argv: ReadonlyArray): Promise { + const shouldRemoveUnusedExceptions = argv.includes( + '--remove-unused-exceptions' + ); + const now = new Date(); const rules: Array = loadJSON(rulesPath); @@ -393,10 +397,26 @@ async function main(): Promise { { concurrency: 100 } ); + let unusedExceptionsLogMessage: string; + + if (shouldRemoveUnusedExceptions && unusedExceptions.length) { + unusedExceptionsLogMessage = `${unusedExceptions.length} unused exceptions (automatically removed),`; + + const unusedExceptionsSet = new Set(unusedExceptions); + const newExceptions = exceptions.filter( + exception => !unusedExceptionsSet.has(exception) + ); + writeExceptions(exceptionsPath, newExceptions); + + unusedExceptions = []; + } else { + unusedExceptionsLogMessage = `${unusedExceptions.length} unused exceptions,`; + } + console.log( `${scannedCount} files scanned.`, `${results.length} questionable lines,`, - `${unusedExceptions.length} unused exceptions,`, + unusedExceptionsLogMessage, `${exceptions.length} total exceptions.` ); @@ -410,14 +430,16 @@ async function main(): Promise { if (unusedExceptions.length) { console.log(); - console.log('Unused exceptions!'); + console.log( + 'Unused exceptions! Run with --remove-unused-exceptions to automatically remove them.' + ); console.log(JSON.stringify(sortExceptions(unusedExceptions), null, ' ')); } process.exit(1); } -main().catch(err => { +main(process.argv).catch(err => { console.error(err); process.exit(1); }); diff --git a/ts/util/lint/sort_exceptions.ts b/ts/util/lint/sort_exceptions.ts index 9c2786273..7b7306b8b 100644 --- a/ts/util/lint/sort_exceptions.ts +++ b/ts/util/lint/sort_exceptions.ts @@ -1,15 +1,12 @@ -// Copyright 2018-2020 Signal Messenger, LLC +// Copyright 2018-2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import { join } from 'path'; -import { writeFileSync } from 'fs'; import type { ExceptionType } from './types'; -import { loadJSON, sortExceptions } from './util'; +import { loadJSON, writeExceptions } from './util'; const exceptionsPath = join(__dirname, 'exceptions.json'); const exceptions: Array = loadJSON(exceptionsPath); -const sorted = sortExceptions(exceptions); - -writeFileSync(exceptionsPath, JSON.stringify(sorted, null, ' ')); +writeExceptions(exceptionsPath, exceptions); diff --git a/ts/util/lint/util.ts b/ts/util/lint/util.ts index 71b719e38..d8c6edff4 100644 --- a/ts/util/lint/util.ts +++ b/ts/util/lint/util.ts @@ -1,43 +1,28 @@ -// Copyright 2018-2021 Signal Messenger, LLC +// Copyright 2018-2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -/* eslint-disable no-console */ +import { readJsonSync, writeJsonSync } from 'fs-extra'; -import { readFileSync } from 'fs'; - -import { omit, orderBy } from 'lodash'; +import { orderBy } from 'lodash'; import type { ExceptionType } from './types'; export const ENCODING = 'utf8'; -export function loadJSON(target: string): T { - try { - const contents = readFileSync(target, ENCODING); +export const loadJSON = (path: string): T => readJsonSync(path); - return JSON.parse(contents); - } catch (error) { - console.log(`Error loading JSON from ${target}: ${error.stack}`); - throw error; - } -} +export const writeExceptions = ( + path: string, + exceptions: ReadonlyArray +): void => writeJsonSync(path, sortExceptions(exceptions), { spaces: 2 }); -export function sortExceptions( - exceptions: Array -): Array { - return orderBy(exceptions, [ +export const sortExceptions = ( + exceptions: ReadonlyArray +): Array => + orderBy(exceptions, [ 'path', 'rule', 'reasonCategory', 'updated', 'reasonDetail', - ]).map(removeLegacyAttributes); -} - -// This is here in case any open changesets still touch `lineNumber`. We should remove -// this after 2021-06-01 to be conservative. -function removeLegacyAttributes( - exception: Readonly -): ExceptionType { - return omit(exception, ['lineNumber']); -} + ]);