Skip to content

Add prune orphaned todos command #577

@tylerbecks

Description

@tylerbecks

Currently, there is no command to remove orphaned todos. There are 2 ways that todos can become orphaned (that I can think of):

  1. the file for the todo no longer exists (it was moved or deleted)
  2. the rule the todo was created for was removed/disabled

After realizing the performance impact of the lint-todo system with many todos, we are prioritizing reducing the line count in the .lint-todo file as much as possible.

Here is an example of a command I wrote to clean up todo for files that no longer exist (case 1)

const fs = require('fs');

/**
 * This script parses a .lint-todo file and removes lines for non-existent files in the current working directory.
 *
 * Usage:
 *     node scripts/lint-todo-dead-file-cleaner.js /path/to/your/.lint-todo
 *
 * Replace '/path/to/your/.lint-todo' with the actual path to your .lint-todo file.
 *
 */

/**
 * Parses the contents of a .lint-todo file and returns a set of unique file paths and a list of lines from the file.
 * @param {string} filePath - The path to the .lint-todo file.
 * @returns {[Set<string>, string[]]} - A tuple containing a set of unique file paths and a list of lines from the file.
 */
function parseLintTodoFile(filePath) {
  const lines = fs.readFileSync(filePath, { encoding: 'utf8' }).split('\n');
  const filePaths = lines.map((line) => line.split('|').pop().trim()).filter(Boolean);
  return {
    filePaths: new Set(filePaths),
    lines
  };
}

/**
 * Checks if a file exists.
 * @param {string} filePath - The path to the file.
 * @returns {boolean} - `true` if the file exists, `false` otherwise.
 */
function checkFileExists(filePath) {
  return fs.existsSync(filePath);
}

/**
 * Removes lines for non-existent files from a .lint-todo file.
 * @param {string} filePath - The path to the .lint-todo file.
 * @param {string[]} lines - A list of lines from the .lint-todo file.
 * @param {Set<string>} nonexistentFiles - A set of non-existent file paths.
 */
function removeNonexistentFiles(filePath, lines, nonexistentFiles) {
  fs.writeFileSync(
    filePath,
    lines
      .filter((line) => {
        const filePath = line.split('|').pop().trim();
        return !nonexistentFiles.has(filePath);
      })
      .join('\n')
  );
}

/**
 * Entry point for the script. Parses command line arguments, checks for non-existent files, and removes their lines from the .lint-todo file.
 */
function main() {
  const filePath = process.argv[2];

  const { filePaths, lines } = parseLintTodoFile(filePath);
  const nonexistentFiles = new Set(Array.from(filePaths).filter((filePath) => !checkFileExists(filePath)));

  if (nonexistentFiles.size > 0) {
    removeNonexistentFiles(filePath, lines, nonexistentFiles);
    console.log(`${nonexistentFiles.size} dead files detected in .lint-todo:`);
    for (const filePath of nonexistentFiles) {
      console.log(`  - ${filePath}`);
    }

    const { lines: newLines } = parseLintTodoFile(filePath);
    console.log(`Removed ${lines.length - newLines.length} lines from the .lint-todo file`);
  } else {
    console.log('All files listed in the .lint-todo file exist.');
  }
}

main();

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions