@lint-todo/utils
Version:
 [](https://badge.fury.io/js/%40lint-todo%2Futils) [](h
308 lines • 13.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.compactTodoStorageFile = exports.applyTodoChanges = exports.getTodoBatches = exports.generateTodoBatches = exports.readTodoDataForFilePath = exports.readTodoData = exports.readTodosForFilePath = exports.readTodos = exports.writeTodos = exports.appendTodoStorageFile = exports.writeTodoStorageFile = exports.readTodoStorageFile = exports.resolveConflicts = exports.hasConflicts = exports.getTodoStorageFilePath = exports.ensureTodoStorageFile = exports.todoStorageFileExists = void 0;
const tslib_1 = require("tslib");
const node_path_1 = require("node:path");
const upath_1 = require("upath");
const node_os_1 = require("node:os");
const proper_lockfile_1 = require("proper-lockfile");
const fs_extra_1 = require("fs-extra");
const todo_matcher_1 = tslib_1.__importDefault(require("./todo-matcher"));
const todo_batch_generator_1 = tslib_1.__importDefault(require("./todo-batch-generator"));
const builders_1 = require("./builders");
const CONFLICT_PATTERN = /^\|{7,}|<{7,}|={7,}|>{7,}|!.*/;
/**
* Determines if the .lint-todo storage file exists.
*
* @param baseDir - The base directory that contains the .lint-todo storage file.
* @returns - true if the todo storage file exists, otherwise false.
*/
function todoStorageFileExists(baseDir) {
try {
return !(0, fs_extra_1.lstatSync)(getTodoStorageFilePath(baseDir)).isDirectory();
}
catch (error) {
if (error.code === 'ENOENT') {
return false;
}
throw error;
}
}
exports.todoStorageFileExists = todoStorageFileExists;
/**
* Creates, or ensures the creation of, the .lint-todo file.
*
* @param baseDir - The base directory that contains the .lint-todo storage file.
* @returns - The todo storage file path.
*/
function ensureTodoStorageFile(baseDir) {
const path = getTodoStorageFilePath(baseDir);
(0, fs_extra_1.ensureFileSync)(path);
return path;
}
exports.ensureTodoStorageFile = ensureTodoStorageFile;
/**
* @param baseDir - The base directory that contains the .lint-todo storage file.
* @returns - The todo storage file path.
*/
function getTodoStorageFilePath(baseDir) {
return (0, node_path_1.join)(baseDir, '.lint-todo');
}
exports.getTodoStorageFilePath = getTodoStorageFilePath;
/**
* Determines if the .lint-todo storage file has conflicts.
*
* @param todoContents - The unparsed contents of the .lint-todo file.
* @returns true if the file has conflicts, otherwise false.
*/
function hasConflicts(todoContents) {
return CONFLICT_PATTERN.test(todoContents);
}
exports.hasConflicts = hasConflicts;
/**
* Resolves git conflicts in todo operations by removing any lines that match conflict markers.
*
* @param operations - An array of string operations that are used to recreate todos.
* @returns An array of string operations excluding any operations that were identified as git conflict lines.
*/
function resolveConflicts(operations) {
return operations.filter((operation) => !CONFLICT_PATTERN.test(operation));
}
exports.resolveConflicts = resolveConflicts;
/**
* Reads the .lint-todo storage file.
*
* @param todoStorageFilePath - The .lint-todo storage file path.
* @returns A array of todo operations.
*/
function readTodoStorageFile(todoStorageFilePath) {
const todoContents = (0, fs_extra_1.readFileSync)(todoStorageFilePath, {
encoding: 'utf8',
});
// when splitting by EOL, make sure to filter off the '' caused by the final EOL
let operations = todoContents.split(node_os_1.EOL).filter((op) => op !== '');
if (hasConflicts(todoContents)) {
operations = resolveConflicts(operations);
writeTodoStorageFile(todoStorageFilePath, operations);
}
return operations;
}
exports.readTodoStorageFile = readTodoStorageFile;
/**
* Writes the operations to the .lint-todo storage file to the path provided by todoStorageFilePath.
*
* @param todoStorageFilePath - The .lint-todo storage file path.
* @param operations - An array of string operations that are used to recreate todos.
*/
function writeTodoStorageFile(todoStorageFilePath, operations) {
(0, fs_extra_1.writeFileSync)(todoStorageFilePath, operations.join(node_os_1.EOL) + node_os_1.EOL);
}
exports.writeTodoStorageFile = writeTodoStorageFile;
/**
* Appends the operations to the .lint-todo storage file to the path provided by the todoStorageFilePath
*
* @param todoStorageFilePath - The .lint-todo storage file path.
* @param operations - An array of string operations that are used to recreate todos.
*/
function appendTodoStorageFile(todoStorageFilePath, operations) {
(0, fs_extra_1.appendFileSync)(todoStorageFilePath, operations.join(node_os_1.EOL) + node_os_1.EOL);
}
exports.appendTodoStorageFile = appendTodoStorageFile;
/**
* Writes files for todo lint violations. One file is generated for each violation, using a generated
* hash to identify each.
*
* Given a list of todo lint violations, this function will also delete existing files that no longer
* have a todo lint violation.
*
* @param baseDir - The base directory that contains the .lint-todo storage file.
* @param maybeTodos - The linting data, converted to TodoData format.
* @param options - An object containing write options.
* @returns - The counts of added and removed todos.
*/
function writeTodos(baseDir, maybeTodos, options) {
options = Object.assign({ shouldRemove: () => true, overwrite: false }, options);
let batches;
ensureTodoStorageFile(baseDir);
const release = tryLockStorageFile(baseDir);
try {
const existing = options.filePath
? readTodosForFilePath(baseDir, options, false)
: readTodos(baseDir, options, false);
batches = getTodoBatches(maybeTodos, existing, options);
applyTodoChanges(baseDir, batches.add, batches.remove, false);
}
finally {
release();
}
return {
addedCount: batches.add.size,
removedCount: batches.remove.size,
stableCount: batches.stable.size,
expiredCount: batches.expired.size,
};
}
exports.writeTodos = writeTodos;
/**
* Reads all todo files in the .lint-todo file.
*
* @param baseDir - The base directory that contains the .lint-todo storage file.
* @param options - An object containing read options.
* @param shouldLock - True if the .lint-todo storage file should be locked, otherwise false. Default: true.
* @returns - A {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map|Map} of {@link https://github.com/lint-todo/utils/blob/master/src/types/todo.ts#L25|FilePath}/{@link https://github.com/lint-todo/utils/blob/master/src/todo-matcher.ts#L4|TodoMatcher}.
*/
function readTodos(baseDir, options, shouldLock = true) {
const release = shouldLock && todoStorageFileExists(baseDir)
? tryLockStorageFile(baseDir)
: // eslint-disable-next-line @typescript-eslint/no-empty-function
() => { };
try {
const todoOperations = readTodoStorageFile(getTodoStorageFilePath(baseDir));
return (0, builders_1.buildFromTodoOperations)(todoOperations, options.engine);
}
finally {
release();
}
}
exports.readTodos = readTodos;
/**
* Reads todo files in the .lint-todo file for a specific filePath.
*
* @param baseDir - The base directory that contains the .lint-todo storage file.
* @param options - An object containing read options.
* @param shouldLock - True if the .lint-todo storage file should be locked, otherwise false. Default: true.
* @returns - A {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map|Map} of {@link https://github.com/lint-todo/utils/blob/master/src/types/todo.ts#L25|FilePath}/{@link https://github.com/lint-todo/utils/blob/master/src/todo-matcher.ts#L4|TodoMatcher}.
*/
function readTodosForFilePath(baseDir, options, shouldLock = true) {
const existingTodos = readTodos(baseDir, options, shouldLock);
const normalizedFilePath = (0, upath_1.normalize)(options.filePath);
const matcher = existingTodos.get(normalizedFilePath) || new todo_matcher_1.default();
return new Map([[normalizedFilePath, matcher]]);
}
exports.readTodosForFilePath = readTodosForFilePath;
/**
* Reads todos in the .lint-todo file and returns Todo data in an array.
*
* @param baseDir - The base directory that contains the .lint-todo storage file.
* @param options - An object containing read options.
* @returns An array of {@link https://github.com/lint-todo/utils/blob/master/src/types/todo.ts#L61|TodoData}
*/
function readTodoData(baseDir, options) {
return new Set([...readTodos(baseDir, options).values()].reduce((matcherResults, matcher) => {
return [...matcherResults, ...matcher.unprocessed];
}, []));
}
exports.readTodoData = readTodoData;
/**
* Reads todos for a single filePath in the .lint-todo file and returns Todo data in an array.
*
* @param baseDir - The base directory that contains the .lint-todo storage file.
* @param options - An object containing read options.
* @returns An array of {@link https://github.com/lint-todo/utils/blob/master/src/types/todo.ts#L61|TodoData}
*/
function readTodoDataForFilePath(baseDir, options) {
return new Set([...readTodosForFilePath(baseDir, options).values()].reduce((matcherResults, matcher) => {
return [...matcherResults, ...matcher.unprocessed];
}, []));
}
exports.readTodoDataForFilePath = readTodoDataForFilePath;
/**
* Gets 4 data structures containing todo items to add, remove, those that are expired, and those that are stable (not to be modified).
*
* @param baseDir - The base directory that contains the .lint-todo storage file.
* @param maybeTodos - The linting data for violations.
* @param options - An object containing write options.
* @returns - An object of {@link https://github.com/lint-todo/utils/blob/master/src/types/todo.ts#L36|TodoBatches}.
*/
function generateTodoBatches(baseDir, maybeTodos, options) {
const existingTodos = readTodosForFilePath(baseDir, options);
return getTodoBatches(maybeTodos, existingTodos, options);
}
exports.generateTodoBatches = generateTodoBatches;
/**
* Gets 4 data structures containing todo items to add, remove, those that are expired, and those that are stable (not to be modified).
*
* @param maybeTodos - The linting data for violations.
* @param existing - Existing todo lint data.
* @param options - An object containing write options.
* @returns - An object of {@link https://github.com/lint-todo/utils/blob/master/src/types/todo.ts#L36|TodoBatches}.
*/
function getTodoBatches(maybeTodos, existing, options) {
const todoBatchGenerator = new todo_batch_generator_1.default(options);
return todoBatchGenerator.generate(maybeTodos, existing);
}
exports.getTodoBatches = getTodoBatches;
/**
* Applies todo changes, either adding or removing, based on batches from `getTodoBatches`.
*
* @param baseDir - The base directory that contains the .lint-todo storage file.
* @param add - Batch of todos to add.
* @param remove - Batch of todos to remove.
* @param shouldLock - True if the .lint-todo storage file should be locked, otherwise false. Default: true.
*/
function applyTodoChanges(baseDir, add, remove, shouldLock = true) {
const todoStorageFilePath = getTodoStorageFilePath(baseDir);
const ops = (0, builders_1.buildTodoOperations)(add, remove);
const release = shouldLock && todoStorageFileExists(baseDir)
? tryLockStorageFile(baseDir)
: // eslint-disable-next-line @typescript-eslint/no-empty-function
() => { };
if (ops.length === 0) {
return;
}
try {
appendTodoStorageFile(todoStorageFilePath, ops);
}
finally {
release();
}
}
exports.applyTodoChanges = applyTodoChanges;
/**
* Compacts the .lint-todo storage file.
*
* @param baseDir - The base directory that contains the .lint-todo storage file.
* @returns The count of compacted todos.
*/
function compactTodoStorageFile(baseDir) {
const todoStorageFilePath = getTodoStorageFilePath(baseDir);
const todos = readTodoData(baseDir, {
engine: 'all',
filePath: '',
});
const release = tryLockStorageFile(baseDir);
try {
const originalOperations = readTodoStorageFile(todoStorageFilePath);
const compactedOperations = (0, builders_1.buildTodoOperations)(todos, new Set());
writeTodoStorageFile(todoStorageFilePath, compactedOperations);
return {
originalOperations,
compactedOperations,
compacted: originalOperations.length - compactedOperations.length,
};
}
finally {
release();
}
}
exports.compactTodoStorageFile = compactTodoStorageFile;
function tryLockStorageFile(baseDir, attempts = 0) {
const todoStorageFilePath = getTodoStorageFilePath(baseDir);
try {
return (0, proper_lockfile_1.lockSync)(todoStorageFilePath);
}
catch (error) {
if (attempts > 5) {
throw error;
}
if (error.code === 'ELOCKED') {
const start = Date.now();
while (Date.now() - start < 500) {
// artifical wait for other process to unlock file
}
return tryLockStorageFile(baseDir, attempts + 1);
}
throw error;
}
}
//# sourceMappingURL=io.js.map