UNPKG

eslint-seatbelt

Version:

Gradually tighten ESLint rules in your codebase

1 lines 19.2 kB
{"version":3,"sources":["../src/SeatbeltFile.ts","../src/errorHanding.ts"],"sourcesContent":["import * as os from \"node:os\"\nimport * as fs from \"node:fs\"\nimport path, * as nodePath from \"node:path\"\nimport {\n formatFilename,\n formatRuleId,\n SEATBELT_FROZEN,\n SEATBELT_KEEP,\n SeatbeltArgs,\n} from \"./SeatbeltConfig\"\nimport { name } from \"../package.json\"\nimport { appendErrorContext, isErrno } from \"./errorHanding\"\n\nexport type SourceFileName = string\nexport type RuleId = string\n\ninterface SeatbeltFileLine {\n encoded?: string\n filename: SourceFileName\n ruleId: RuleId\n maxErrors: number\n}\n\nexport type SeatbeltFileJson = {\n filename: SourceFileName\n data: Record<SourceFileName, Record<RuleId, number>>\n}\n\nfunction encodeLine(line: SeatbeltFileLine): string {\n const { filename, ruleId, maxErrors } = line\n return `${JSON.stringify(filename)}\\t${JSON.stringify(ruleId)}\\t${maxErrors}\\n`\n}\n\nfunction decodeLine(line: string, index: number): SeatbeltFileLine {\n try {\n const lineParts = line.split(\"\\t\")\n if (lineParts.length !== 3) {\n throw new Error(\n `Expected 3 tab-separated JSON strings, instead have ${lineParts.length}`,\n )\n }\n let filename: string\n try {\n filename = JSON.parse(lineParts[0])\n } catch (e) {\n appendErrorContext(e, \"at tab-separated column 1 (filename)\")\n throw e\n }\n\n let ruleId: RuleId\n try {\n ruleId = JSON.parse(lineParts[1])\n } catch (e) {\n appendErrorContext(e, \"at tab-separated column 2 (RuleId)\")\n throw e\n }\n\n let maxErrors: number\n try {\n maxErrors = JSON.parse(lineParts[2])\n } catch (e) {\n appendErrorContext(e, \"at tab-separated column 3 (maxErrors)\")\n throw e\n }\n\n return {\n encoded: line,\n filename,\n ruleId,\n maxErrors,\n }\n } catch (e) {\n appendErrorContext(e, `at line ${index + 1}: \\`${line.trim()}\\``)\n throw e\n }\n}\n\ninterface SeatbeltStateFileData {\n maxErrors?: Map<RuleId, number>\n lines: SeatbeltFileLine[]\n}\n\nconst COMMENT_LINE_REGEX = /^\\s*#/\n\nconst DEFAULT_FILE_HEADER = `\n# ${name} temporarily allowed errors\n# docs: https://github.com/justjake/${name}#readme\n`.trim()\n\n/**\n * The state file is a Map<filename, Map<ruleId, allowedErrors>>.\n * It is stored in \"tab separated json\" format. This format is chosen over JSON\n * or YAML because each line is independent, which makes resolving merge\n * conflicts much easier than in a syntactically hierarchical format.\n */\nexport class SeatbeltFile {\n static readSync(filename: string): SeatbeltFile {\n const text = fs.readFileSync(filename, \"utf8\")\n try {\n return SeatbeltFile.parse(filename, text)\n } catch (e) {\n appendErrorContext(e, `in seatbelt file \\`${filename}\\``)\n throw e\n }\n }\n\n /**\n * Read `filename` if it exists, otherwise create a new empty seatbelt file object\n * that will write to that filename.\n */\n static openSync(filename: string): SeatbeltFile {\n try {\n return SeatbeltFile.readSync(filename)\n } catch (e) {\n if (isErrno(e, \"ENOENT\")) {\n return new SeatbeltFile(filename, new Map(), DEFAULT_FILE_HEADER)\n }\n throw e\n }\n }\n\n static parse(filename: string, text: string): SeatbeltFile {\n const data = new Map<SourceFileName, SeatbeltStateFileData>()\n const split = text.split(/(?<=\\n)/)\n const lines = split\n .filter(\n (line) =>\n line !== \"\" && line !== \"\\n\" && !COMMENT_LINE_REGEX.test(line),\n )\n .map(decodeLine)\n const comments = split\n .filter((line) => COMMENT_LINE_REGEX.test(line))\n .join(\"\")\n lines.forEach((line) => {\n let fileState = data.get(line.filename)\n if (!fileState) {\n fileState = { maxErrors: undefined, lines: [] }\n data.set(line.filename, fileState)\n }\n fileState.lines.push(line)\n })\n return new SeatbeltFile(filename, data, comments.trim())\n }\n\n static fromJSON(json: SeatbeltFileJson): SeatbeltFile {\n const data = new Map(\n Object.entries(json.data).map(([filename, maxErrors]) => [\n filename,\n { maxErrors: new Map(Object.entries(maxErrors)), lines: [] },\n ]),\n )\n return new SeatbeltFile(json.filename, data)\n }\n\n public changed = false\n private readonly dirname: string\n private useTempDirForWrites = true\n\n constructor(\n public readonly filename: string,\n protected data: Map<SourceFileName, SeatbeltStateFileData>,\n public readonly comments: string = \"\",\n ) {\n this.filename = path.resolve(this.filename)\n this.dirname = path.dirname(this.filename)\n }\n\n *filenames(): IterableIterator<SourceFileName> {\n for (const filename of this.data.keys()) {\n yield this.toAbsolutePath(filename)\n }\n }\n\n getMaxErrors(\n filename: SourceFileName,\n ): ReadonlyMap<RuleId, number> | undefined {\n const fileState = this.data.get(this.toRelativePath(filename))\n if (!fileState) {\n return undefined\n }\n fileState.maxErrors ??= parseMaxErrors(fileState.lines)\n return fileState.maxErrors\n }\n\n removeFile(filename: SourceFileName, args: SeatbeltArgs) {\n const relativeFilename = this.toRelativePath(filename)\n if (!this.data.has(relativeFilename)) {\n return false\n }\n\n SeatbeltArgs.verboseLog(args, () =>\n args.frozen\n ? `${formatFilename(filename)}: ${SEATBELT_FROZEN}: didn't remove max errors`\n : `${formatFilename(filename)}: remove max errors`,\n )\n\n if (args.frozen) {\n return false\n }\n\n this.data.delete(relativeFilename)\n this.changed = true\n return true\n }\n\n updateMaxErrors(\n filename: SourceFileName,\n args: SeatbeltArgs,\n ruleToErrorCount: ReadonlyMap<RuleId, number>,\n ) {\n const removedRules = new Set<RuleId>()\n let increasedRulesCount = 0\n let decreasedRulesCount = 0\n this.getMaxErrors(filename)\n const relativeFilename = this.toRelativePath(filename)\n const maxErrors =\n this.data.get(relativeFilename)?.maxErrors ?? new Map<RuleId, number>()\n\n ruleToErrorCount.forEach((errorCount, ruleId) => {\n const maxErrorCount = maxErrors.get(ruleId) ?? 0\n if (errorCount === maxErrorCount) {\n return\n }\n\n if (\n errorCount < maxErrorCount ||\n SeatbeltArgs.ruleSetHas(args.allowIncreaseRules, ruleId)\n ) {\n SeatbeltArgs.verboseLog(args, () =>\n args.frozen\n ? `${formatFilename(filename)}: ${formatRuleId(ruleId)}: ${SEATBELT_FROZEN}: didn't update max errors ${maxErrorCount} -> ${errorCount}`\n : `${formatFilename(filename)}: ${formatRuleId(ruleId)}: update max errors ${maxErrorCount} -> ${errorCount}`,\n )\n maxErrors.set(ruleId, errorCount)\n if (errorCount > maxErrorCount) {\n increasedRulesCount++\n } else {\n decreasedRulesCount++\n }\n }\n })\n\n if (args.verbose || args.keepRules !== \"all\") {\n maxErrors.forEach((maxErrorCount, ruleId) => {\n const shouldRemove =\n maxErrorCount === 0 || !ruleToErrorCount.has(ruleId)\n\n if (!shouldRemove) {\n return\n }\n\n if (SeatbeltArgs.ruleSetHas(args.keepRules, ruleId)) {\n SeatbeltArgs.verboseLog(\n args,\n () =>\n `${formatFilename(filename)}: ${formatRuleId(ruleId)}: ${SEATBELT_KEEP}: didn't update max errors ${maxErrorCount} -> ${0}`,\n )\n return\n }\n\n SeatbeltArgs.verboseLog(args, () =>\n args.frozen\n ? `${formatFilename(filename)}: ${formatRuleId(ruleId)}: ${SEATBELT_FROZEN}: didn't update max errors ${maxErrorCount} -> ${0}`\n : `${formatFilename(filename)}: ${formatRuleId(ruleId)}: update max errors ${maxErrorCount} -> ${0}`,\n )\n\n maxErrors.delete(ruleId)\n removedRules.add(ruleId)\n })\n }\n\n const changed =\n increasedRulesCount > 0 ||\n decreasedRulesCount > 0 ||\n removedRules.size > 0\n if (changed && !args.frozen) {\n const file = this.data.get(relativeFilename)\n if (file) {\n file.maxErrors = maxErrors\n } else {\n this.data.set(relativeFilename, {\n maxErrors,\n lines: [],\n })\n }\n this.changed = true\n }\n\n return { removedRules, increasedRulesCount, decreasedRulesCount }\n }\n\n toDataString(): string {\n const lines: string[] = []\n this.data.forEach((fileState, filename) => {\n if (fileState.maxErrors) {\n // Serialize maxErrors map structure if it exists, since it may have changes.\n fileState.lines = []\n fileState.maxErrors.forEach((maxErrorCount, ruleId) => {\n fileState.lines.push({ filename, ruleId, maxErrors: maxErrorCount })\n })\n fileState.lines.sort((a, b) =>\n a.ruleId === b.ruleId ? 0 : a.ruleId < b.ruleId ? -1 : 1,\n )\n }\n fileState.lines.forEach((line) => {\n const encoded = (line.encoded ??= encodeLine(line))\n lines.push(encoded)\n })\n })\n lines.sort()\n if (this.comments) {\n return this.comments + \"\\n\\n\" + lines.join(\"\")\n } else {\n return lines.join(\"\")\n }\n }\n\n readSync() {\n const nextStateFile = SeatbeltFile.openSync(this.filename)\n if (nextStateFile) {\n this.data = nextStateFile.data\n this.changed = false\n return true\n }\n return false\n }\n\n flushChanges() {\n if (this.changed) {\n this.writeSync()\n this.changed = false\n return { updated: true }\n }\n return { updated: false }\n }\n\n writeSync() {\n const dataString = this.toDataString()\n const dir = nodePath.dirname(this.filename)\n const base = nodePath.basename(this.filename)\n const tempFile = nodePath.join(\n this.useTempDirForWrites ? os.tmpdir() : dir,\n `.${base}.wip${process.pid}.${Date.now()}.tmp`,\n )\n fs.mkdirSync(dir, { recursive: true })\n fs.writeFileSync(tempFile, dataString, \"utf8\")\n try {\n fs.renameSync(tempFile, this.filename)\n } catch (error) {\n // If $TMPDIR is on a different filesystem from the git repo, we won't be\n // able to move the tempfile over the existing file.\n //\n // In such cases copy & remove the tempfile instead, and then prefer to create\n // tempfiles in the desination dir.\n //\n // https://github.com/justjake/eslint-seatbelt/issues/7\n if (isErrno(error, 'EXDEV')) {\n this.useTempDirForWrites = false\n fs.copyFileSync(tempFile, this.filename)\n fs.rmSync(tempFile)\n return\n }\n throw error\n }\n }\n\n toJSON(): SeatbeltFileJson {\n const data = Object.fromEntries(\n Array.from(this.data.keys()).map((filename) => {\n const maxErrors = this.getMaxErrors(filename)\n if (!maxErrors) {\n throw new Error(`${name} bug: expected errors for existing key`)\n }\n return [filename, Object.fromEntries(maxErrors)]\n }),\n )\n return { filename: this.filename, data }\n }\n\n toRelativePath(filename: string) {\n if (!nodePath.isAbsolute(filename)) {\n return filename\n }\n return nodePath.relative(this.dirname, filename)\n }\n\n toAbsolutePath(filename: string) {\n if (nodePath.isAbsolute(filename)) {\n return filename\n }\n return nodePath.resolve(this.dirname, filename)\n }\n}\n\nfunction parseMaxErrors(lines: SeatbeltFileLine[]): Map<RuleId, number> {\n const maxErrors = new Map<RuleId, number>()\n lines.forEach((line) => {\n maxErrors.set(line.ruleId, line.maxErrors)\n })\n return maxErrors\n}\n","export function appendErrorContext(error: unknown, context: string) {\n if (error instanceof Error) {\n error.message += `\\n ${context}`\n }\n}\n\nexport function isErrno(\n error: unknown,\n code: string,\n): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error && error.code === code\n}\n"],"mappings":"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","names":[]}