UNPKG

@lint-todo/utils

Version:

![CI Build](https://github.com/lint-todo/utils/workflows/CI%20Build/badge.svg) [![npm version](https://badge.fury.io/js/%40lint-todo%2Futils.svg)](https://badge.fury.io/js/%40lint-todo%2Futils) [![License](https://img.shields.io/npm/l/@checkup/cli.svg)](h

308 lines 13.5 kB
"use strict"; 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