From 20e5b9426f9eca0c93c6054ef03267cea56193d3 Mon Sep 17 00:00:00 2001 From: Sander Verweij Date: Mon, 18 Dec 2023 11:45:39 +0100 Subject: [PATCH] feat(config-utl): makes possibly old format known violations forward compatible (#890) ## Description, Motivation and Context - makes possibly old format known violations forward compatible directly after reading ... to prevent users from bumping into inexplicable errors. See the comment in extract-known-violations.mjs for details. ## How Has This Been Tested? - [x] green ci - [x] additional non-regression tests ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] Documentation only change - [ ] Refactor (non-breaking change which fixes an issue without changing functionality) - [x] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) --- src/config-utl/extract-known-violations.mjs | 46 ++++++- ...-violations-with-cycles-in-new-format.json | 47 ++++++++ ...-violations-with-cycles-in-old-format.json | 35 ++++++ .../extract-known-violations.spec.mjs | 112 ++++++++++++++++++ 4 files changed, 237 insertions(+), 3 deletions(-) create mode 100644 test/config-utl/__mocks__/known-violations/known-violations-with-cycles-in-new-format.json create mode 100644 test/config-utl/__mocks__/known-violations/known-violations-with-cycles-in-old-format.json diff --git a/src/config-utl/extract-known-violations.mjs b/src/config-utl/extract-known-violations.mjs index ac58ee610..b741b9733 100644 --- a/src/config-utl/extract-known-violations.mjs +++ b/src/config-utl/extract-known-violations.mjs @@ -2,15 +2,55 @@ import { readFile } from "node:fs/promises"; import json5 from "json5"; import makeAbsolute from "./make-absolute.mjs"; +/** + * @typedef {import("../../types/violations.d.mjs").IViolation} IViolation + * @typedef {import("../../types/shared-types.d.mts").DependencyType} DependencyType + * + * @typedef {IViolation & {cycle: Array<{name: string, dependencyTypes: Array}>} | {cycle: Array}} IMaybeOldFormatViolation + */ + +/** + * cycles went from Array to Array<{name: string, dependencyTypes: Array}> + * in version 16.0.0. Known violations would typically be still in the old + * format. To prevent customers from bumping into error messages and having + * to update or regenerate their known violations, we'll make the old format + * forward compatible with this. + * + * @param {IMaybeOldFormatViolation} pKnownViolation + * @returns {IViolation} + */ +function makeForwardCompatible(pKnownViolation) { + if (Boolean(pKnownViolation.cycle)) { + return { + ...pKnownViolation, + cycle: pKnownViolation.cycle.map((pModule) => { + if (Boolean(pModule.name)) { + return pModule; + } + return { + name: pModule, + dependencyTypes: [], + }; + }), + }; + } + return pKnownViolation; +} + export default async function extractKnownViolations(pKnownViolationsFileName) { try { - return json5.parse( - await readFile(makeAbsolute(pKnownViolationsFileName), "utf8") + const lFileContents = await readFile( + makeAbsolute(pKnownViolationsFileName), + "utf8", ); + const lKnownViolations = json5.parse(lFileContents); + const lForwardCompatible = lKnownViolations.map(makeForwardCompatible); + + return lForwardCompatible; } catch (pError) { if (pError instanceof SyntaxError) { throw new SyntaxError( - `'${pKnownViolationsFileName}' should be valid json\n ${pError}` + `'${pKnownViolationsFileName}' should be valid json\n ${pError}`, ); } throw pError; diff --git a/test/config-utl/__mocks__/known-violations/known-violations-with-cycles-in-new-format.json b/test/config-utl/__mocks__/known-violations/known-violations-with-cycles-in-new-format.json new file mode 100644 index 000000000..130033b47 --- /dev/null +++ b/test/config-utl/__mocks__/known-violations/known-violations-with-cycles-in-new-format.json @@ -0,0 +1,47 @@ +[ + { + "type": "cycle", + "from": "tmp-errors/cycle-1.js", + "to": "tmp-errors/cycle-2.js", + "rule": { + "severity": "error", + "name": "no-circular" + }, + "cycle": [ + { + "name": "tmp-errors/cycle-2.js", + "dependencyTypes": ["local", "require"] + }, + { + "name": "tmp-errors/cycle-3.js", + "dependencyTypes": ["local", "require"] + }, + { + "name": "tmp-errors/cycle-4.js", + "dependencyTypes": ["local", "require"] + }, + { + "name": "tmp-errors/cycle-1.js", + "dependencyTypes": ["local", "require"] + } + ] + }, + { + "type": "module", + "from": "tmp-errors/mod.mts", + "to": "tmp-errors/mod.mts", + "rule": { + "severity": "error", + "name": "no-orphans" + } + }, + { + "type": "dependency", + "from": "tmp-errors/call-mod.mts", + "to": "./mod.mjs", + "rule": { + "severity": "error", + "name": "not-to-unresolvable" + } + } +] diff --git a/test/config-utl/__mocks__/known-violations/known-violations-with-cycles-in-old-format.json b/test/config-utl/__mocks__/known-violations/known-violations-with-cycles-in-old-format.json new file mode 100644 index 000000000..a3d38a729 --- /dev/null +++ b/test/config-utl/__mocks__/known-violations/known-violations-with-cycles-in-old-format.json @@ -0,0 +1,35 @@ +[ + { + "type": "cycle", + "from": "tmp-errors/cycle-1.js", + "to": "tmp-errors/cycle-2.js", + "rule": { + "severity": "error", + "name": "no-circular" + }, + "cycle": [ + "tmp-errors/cycle-2.js", + "tmp-errors/cycle-3.js", + "tmp-errors/cycle-4.js", + "tmp-errors/cycle-1.js" + ] + }, + { + "type": "module", + "from": "tmp-errors/mod.mts", + "to": "tmp-errors/mod.mts", + "rule": { + "severity": "error", + "name": "no-orphans" + } + }, + { + "type": "dependency", + "from": "tmp-errors/call-mod.mts", + "to": "./mod.mjs", + "rule": { + "severity": "error", + "name": "not-to-unresolvable" + } + } +] diff --git a/test/config-utl/extract-known-violations.spec.mjs b/test/config-utl/extract-known-violations.spec.mjs index 748e5e392..53f6f11a4 100644 --- a/test/config-utl/extract-known-violations.spec.mjs +++ b/test/config-utl/extract-known-violations.spec.mjs @@ -55,4 +55,116 @@ describe("[I] config-utl/extractKnownViolations", () => { }, ]); }); + + it("Makes a forward compatible version of the known violations", async () => { + process.chdir("./test/config-utl/__mocks__/known-violations"); + deepEqual( + await extractKnownViolations( + "known-violations-with-cycles-in-old-format.json", + ), + [ + { + type: "cycle", + from: "tmp-errors/cycle-1.js", + to: "tmp-errors/cycle-2.js", + rule: { + severity: "error", + name: "no-circular", + }, + cycle: [ + { + name: "tmp-errors/cycle-2.js", + dependencyTypes: [], + }, + { + name: "tmp-errors/cycle-3.js", + dependencyTypes: [], + }, + { + name: "tmp-errors/cycle-4.js", + dependencyTypes: [], + }, + { + name: "tmp-errors/cycle-1.js", + dependencyTypes: [], + }, + ], + }, + { + type: "module", + from: "tmp-errors/mod.mts", + to: "tmp-errors/mod.mts", + rule: { + severity: "error", + name: "no-orphans", + }, + }, + { + type: "dependency", + from: "tmp-errors/call-mod.mts", + to: "./mod.mjs", + rule: { + severity: "error", + name: "not-to-unresolvable", + }, + }, + ], + ); + }); + + it("Leaves new format cycles alone", async () => { + process.chdir("./test/config-utl/__mocks__/known-violations"); + deepEqual( + await extractKnownViolations( + "known-violations-with-cycles-in-new-format.json", + ), + [ + { + type: "cycle", + from: "tmp-errors/cycle-1.js", + to: "tmp-errors/cycle-2.js", + rule: { + severity: "error", + name: "no-circular", + }, + cycle: [ + { + name: "tmp-errors/cycle-2.js", + dependencyTypes: ["local", "require"], + }, + { + name: "tmp-errors/cycle-3.js", + dependencyTypes: ["local", "require"], + }, + { + name: "tmp-errors/cycle-4.js", + dependencyTypes: ["local", "require"], + }, + { + name: "tmp-errors/cycle-1.js", + dependencyTypes: ["local", "require"], + }, + ], + }, + { + type: "module", + from: "tmp-errors/mod.mts", + to: "tmp-errors/mod.mts", + rule: { + severity: "error", + name: "no-orphans", + }, + }, + { + type: "dependency", + from: "tmp-errors/call-mod.mts", + to: "./mod.mjs", + rule: { + severity: "error", + name: "not-to-unresolvable", + }, + }, + ], + ); + }); });