From 05d97951451fa17fd27cdac08b70679409b62224 Mon Sep 17 00:00:00 2001 From: Sander Verweij Date: Fri, 22 Dec 2023 12:37:58 +0100 Subject: [PATCH] feat!: restructures the via* sub-restrictions to also accept & validate against dependency types (BREAKING for API users) (#894) ## Description - restructures the via* sub restrictions to also accept & validate against dependency types - deprecates the viaNot and viaSomeNot restrictions in favor of viaOnly.pathNot and via.pathNot restrictions respectively. You could still have them in dependency-cruiser configs, but internally they're rewritten into `viaOnly` and `via` rules when possible. This is a breaking change for the API only - in the input/ rules instead of a string | string[] the via and viaOnly restrictions can now also be an object with `path`, `pathNot`, `dependencyTypes` and `dependencyTypesNot` attributes - in the cruise result via and viaOnly are _only_ available as that object (in stead of string | string[]) - in the cruise result the `viaNot` and `viaSomeNot` have disappeared (rules in the input will have been rewritten as via or viaOnly rules). ## Motivation and Context There's a long outstanding and much upvoted request (#695) to be able to exclude e.g. type-only over a whole cycle instead to just the first dependency of the cycle. This PR enables that. ## How Has This Been Tested? - [x] green ci - [x] additional automated 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) - [x] Breaking change (fix or feature that would cause existing functionality to change) => for API only --- .c8rc.json | 2 +- doc/rules-reference.md | 120 +++-- src/main/helpers.mjs | 4 - src/main/rule-set/assert-validity.mjs | 7 +- src/main/rule-set/normalize.mjs | 77 +++- src/schema/baseline-violations.schema.json | 1 + src/schema/baseline-violations.schema.mjs | 2 +- src/schema/configuration.schema.json | 46 +- src/schema/configuration.schema.mjs | 2 +- src/schema/cruise-result.schema.json | 44 +- src/schema/cruise-result.schema.mjs | 2 +- src/validate/match-dependency-rule.mjs | 12 +- src/validate/matchers.mjs | 106 +++-- test/main/rule-set/assert-validity.spec.mjs | 7 + test/main/rule-set/normalize.spec.mjs | 52 +++ .../__mocks__/rules.scary-regex-in-via.json | 12 + test/validate/index.cycle-via-not.spec.mjs | 140 ------ test/validate/index.cycle-via-only.spec.mjs | 422 ++++++++++++++++++ .../index.cycle-via-some-not.spec.mjs | 134 ------ test/validate/index.cycle-via.spec.mjs | 330 ++++++++------ tools/schema/options.mjs | 1 + tools/schema/restrictions.mjs | 72 ++- types/restrictions.d.mts | 34 +- types/strict-restrictions.d.mts | 12 +- 24 files changed, 1077 insertions(+), 564 deletions(-) create mode 100644 test/validate/__mocks__/rules.scary-regex-in-via.json delete mode 100644 test/validate/index.cycle-via-not.spec.mjs create mode 100644 test/validate/index.cycle-via-only.spec.mjs delete mode 100644 test/validate/index.cycle-via-some-not.spec.mjs diff --git a/.c8rc.json b/.c8rc.json index 56d01de06..f5db3aae8 100644 --- a/.c8rc.json +++ b/.c8rc.json @@ -1,7 +1,7 @@ { "checkCoverage": true, "statements": 99.89, - "branches": 98.77, + "branches": 98.78, "functions": 100, "lines": 99.89, "exclude": [ diff --git a/doc/rules-reference.md b/doc/rules-reference.md index 1eb467073..c09fc5565 100644 --- a/doc/rules-reference.md +++ b/doc/rules-reference.md @@ -709,7 +709,69 @@ up at itself. } ``` -### `via` and `viaNot`, `viaOnly` and `viaSomeNot` - restricting what cycles to match +#### `via` and `viaOnly`- restricting what cycles to match + +There are two attributes to put restrictions on through which modules +cycles are allowed to pass: + +- `via`: matches against _some_ of the modules in the cycle +- `viaOnly`: matches against _all_ of the modules in the cycle + +Both take the `path`, `pathNot` and `dependencyTypes`, `dependencyTypesNot` +attributes that have the a meaning similar to the ones in the `from` and `to` +parts of a rule, with dependencyTypes expressing the type of relation a +module has with its predecessor in the cycle. + +With the cycle `a/aa.js` -> `a/ab.js` -> `b/bb.js` -> `a/aa.js` the restrictions +to this: + +| restriction | what it does | example input | match? | because... | +| -------------- | -------------------------------------------------------- | --------------- | ------- | ----------------------------- | +| `via` | **some** of the modules in the cycle match the condition | `path: "^a/.+"` | `true` | `a/aa.js` and `a/ab.js` match | +| `viaOnly` | **all** of the modules in the cycle match the condition | `path: "^a/.+"` | `false` | `b/bb.js` doesn't match | +| _`viaNot`_ | _deprecated - use `viaOnly.pathNot` in stead_ | `path: "^a/.+"` | `false` | `a/aa.js` and `a/ab.js` match | +| _`viaSomeNot`_ | _deprecated - use `via.pathNot` in stead_ | `path: "^a/.+"` | `true` | `b/bb.js` doesn't match | + +##### Example: allow cycles that have a type-only dependency in them + +If you're only interested in cycles at run time, you might allow dependencies +that are only used at compile time to be part of a cycle. `type-only` ones +are a good example of that. E.g. at run time this cycle, where `r.ts` imports +some things from `s.ts` as 'type-only': + +```mermaid +flowchart LR +p.ts --> q.ts --> r.ts --> |type-only| s.ts --> p.ts +``` + +... would look like this at run time (so: not a cycle): + +```mermaid +flowchart LR +p.ts --> q.ts --> r.ts +s.ts --> p.ts +``` + +To express this in a rule, you can use the `viaOnly` attribute: + +```javascript +// log an error for all circular dependencies which consist of only non-`type-only` +// dependencies +{ + name: 'no-circular-at-runtime', + severity: 'error', + from: { + }, + to: { + circular: true, + viaOnly: { + dependencyTypesNot: ['type-only'] + } + } +}, +``` + +##### Example: exclude cycles from erroring when they go to a known 'knot' Some codebases include a lot of circular dependencies, sometimes with a few 'knots' (typically barrel files) that partake in most of them. Fixing these cycles might @@ -717,30 +779,9 @@ take a spell, so you might want to (temporarily :-) ) exclude them from breaking the build. At the same time you might want to prevent any new violation going unnoticed because of this. -One solution to this is to use dependency-cruiser's -[`ignore-known`](cli.md#--ignore-known-ignore-known-violations) mechanism, Another -solution is to put restrictions on through which modules the cycles pass; the -"via"'s, in a similar fashion as possible with `path` and `pathNot`. There are -_four_ via-like restrictions in dependency-cruiser, as - different from the -`path`/`pathNot` restrictions the `via` (and `viaNot`) ones always almost have -to check against multiple paths; all the "via"'s in the cycle. The variants -exist to enable matching against only _some_ of the modules in the cycle or -against _all_ of them. - -All these restrictions take the whole cycle into account; _including_ the tested -'from'; if `a/aa.js` has a cycle via `a/ab.js` and `b/bb/js` back to `a/aa.js` -the via-like restrictions also take `a/aa.js` into account. - -The examples below refer to this cycle: `a/aa.js`, `a/ab.js`, `b/bb.js`, `a/aa.js` - -| restriction | what it does | example input | match? | because... | -| ------------ | ------------------------------------------------------------------- | ------------- | ------- | ----------------------------- | -| `via` | **some** of the modules in the cycle **do** match the expression | `^a/.+` | `true` | `a/aa.js` and `a/ab.js` match | -| `viaOnly` | **all** of the modules in the cycle **do** match the expression | `^a/.+` | `false` | `b/bb.js` doesn't match | -| `viaNot` | **all** of the modules in the cycle **don't** match the expression | `^a/.+` | `false` | `a/aa.js` and `a/ab.js` match | -| `viaSomeNot` | **some** of the modules in the cycle **don't** match the expression | `^a/.+` | `true` | `b/bb.js` doesn't match | - -#### Usage example: prevent code from going through a 'knot' +The recommended solution for this is dependency-cruiser's +[`ignore-known`](cli.md#--ignore-known-ignore-known-violations) mechanism. +However, you can also use the `via` attribute. In this example `app/javascript/tables/index.ts` and `app/javascript/tables/index.ts` are the known 'knots': @@ -755,10 +796,12 @@ are the known 'knots': }, to: { circular: true, - viaNot: [ - '^app/javascript/tables/index.ts', - '^app/javascript/ui/index.tsx', - ] + via: { + pathNot: [ + '^app/javascript/tables/index.ts', + '^app/javascript/ui/index.tsx', + ] + } } }, @@ -771,21 +814,22 @@ are the known 'knots': }, to: { circular: true, - via: [ + via: { + path: [ '^app/javascript/tables/index.ts', '^app/javascript/ui/index.tsx', - ] + ] + } } } ``` -#### Usage example: prevent cycles from going outside a folder +##### Example: prevent cycles from going outside a folder This example (adapted from a [question on GitHub](https://github.com/sverweij/dependency-cruiser/issues/585) -by [@PetFeld-ed](https://github.com/PetFeld-ed)) -not only makes use of the `viaSomeNot`, but also displays the use of -[group matching](#group-matching). +by [@PetFeld-ed](https://github.com/PetFeld-ed)) not only uses the `via`, but +also displays [group matching](#group-matching). ```javascript // in the `forbidden` section of a dependency-cruiser config: @@ -802,7 +846,11 @@ not only makes use of the `viaSomeNot`, but also displays the use of }, to: { circular: true, - viaSomeNot: '^src/business-components/$1/.+', + via: { + pathNot: '^src/business-components/$1/.+' + }, + // previously written with the now deprecated viaSomeNot attribute: + // viaSomeNot: '^src/business-components/$1/.+' }, } ``` diff --git a/src/main/helpers.mjs b/src/main/helpers.mjs index 163b4b552..de7f02232 100644 --- a/src/main/helpers.mjs +++ b/src/main/helpers.mjs @@ -9,10 +9,6 @@ const RE_PROPERTIES = [ "licenseNot", "exoticRequire", "exoticRequireNot", - "via", - "viaNot", - "viaOnly", - "viaSomeNot", ]; /** diff --git a/src/main/rule-set/assert-validity.mjs b/src/main/rule-set/assert-validity.mjs index 20197480c..eeab4604a 100644 --- a/src/main/rule-set/assert-validity.mjs +++ b/src/main/rule-set/assert-validity.mjs @@ -1,6 +1,7 @@ import Ajv from "ajv"; import safeRegex from "safe-regex"; import has from "lodash/has.js"; +import get from "lodash/get.js"; import { assertCruiseOptionsValid } from "../options/assert-validity.mjs"; import { normalizeToREAsString } from "../helpers.mjs"; import configurationSchema from "#configuration-schema"; @@ -34,7 +35,7 @@ function hasPath(pObject, pSection, pCondition) { function safeRule(pRule, pSection, pCondition) { return ( !hasPath(pRule, pSection, pCondition) || - safeRegex(normalizeToREAsString(pRule[pSection][pCondition]), { + safeRegex(normalizeToREAsString(get(pRule[pSection], pCondition)), { limit: MAX_SAFE_REGEX_STAR_REPEAT_LIMIT, }) ); @@ -49,9 +50,13 @@ function assertRuleSafety(pRule) { { section: "to", condition: "license" }, { section: "to", condition: "licenseNot" }, { section: "to", condition: "via" }, + { section: "to", condition: "via.path" }, { section: "to", condition: "viaNot" }, + { section: "to", condition: "viaNot.path" }, { section: "to", condition: "viaOnly" }, + { section: "to", condition: "viaOnly.path" }, { section: "to", condition: "viaSomeNot" }, + { section: "to", condition: "viaSomeNot.path" }, ]; if ( diff --git a/src/main/rule-set/normalize.mjs b/src/main/rule-set/normalize.mjs index d448ecd24..794103db9 100644 --- a/src/main/rule-set/normalize.mjs +++ b/src/main/rule-set/normalize.mjs @@ -1,5 +1,5 @@ import has from "lodash/has.js"; -import { normalizeREProperties } from "../helpers.mjs"; +import { normalizeREProperties, normalizeToREAsString } from "../helpers.mjs"; const VALID_SEVERITIES = /^(error|warn|info|ignore)$/; const DEFAULT_SEVERITY = "warn"; @@ -23,26 +23,74 @@ function normalizeName(pRuleName) { } /** - * - * @param {import("../../../types/shared-types.js").RuleScopeType} pScope? - * @returns {import("../../../types/shared-types.js").RuleScopeType} + * @param {import("../../../types/shared-types.mjs").RuleScopeType} pScope? + * @returns {import("../../../types/shared-types.mjs").RuleScopeType} */ function normalizeScope(pScope) { return pScope ? pScope : DEFAULT_SCOPE; } /** - * + * @param {import("../../../types/restrictions.mjs").MiniDependencyRestrictionType} pVia + * @returns {import("../../../types/strict-restrictions.mjs").IStrictMiniDependencyRestriction} + */ +function normalizeVia(pVia) { + let lReturnValue = {}; + + if (typeof pVia === "string" || Array.isArray(pVia)) { + lReturnValue.path = pVia; + } else { + lReturnValue = structuredClone(pVia); + } + if (has(lReturnValue, "path")) { + lReturnValue.path = normalizeToREAsString(lReturnValue.path); + } + return lReturnValue; +} + +// eslint-disable-next-line complexity +function normalizeVias(pRuleTo) { + const lRuleTo = structuredClone(pRuleTo); + + if (has(lRuleTo, "via")) { + lRuleTo.via = normalizeVia(lRuleTo.via); + } + if (has(lRuleTo, "viaOnly")) { + lRuleTo.viaOnly = normalizeVia(lRuleTo.viaOnly); + } + if (has(lRuleTo, "viaNot")) { + if (!has(lRuleTo, "viaOnly.pathNot")) { + lRuleTo.viaOnly = { + ...lRuleTo.viaOnly, + pathNot: normalizeToREAsString(lRuleTo.viaNot), + }; + } + delete lRuleTo.viaNot; + } + if (has(lRuleTo, "viaSomeNot")) { + if (!has(lRuleTo, "via.pathNot")) { + lRuleTo.via = { + ...lRuleTo.via, + pathNot: normalizeToREAsString(lRuleTo.viaSomeNot), + }; + } + delete lRuleTo.viaSomeNot; + } + return lRuleTo; +} + +/** * @param {import("../../../types/rule-set.mjs").IAnyRuleType} pRule - * @returns {import("../../../types/strict-rule-set.js").IStrictAnyRuleType} + * @returns {import("../../../types/strict-rule-set.mjs").IStrictAnyRuleType} */ function normalizeRule(pRule) { + const lRuleTo = normalizeVias(pRule.to); return { ...pRule, severity: normalizeSeverity(pRule.severity), name: normalizeName(pRule.name), from: normalizeREProperties(pRule.from), - to: normalizeREProperties(pRule.to), + to: normalizeREProperties(lRuleTo), scope: normalizeScope(pRule.scope), ...(pRule.module ? { module: normalizeREProperties(pRule.module) } : {}), }; @@ -66,12 +114,15 @@ export default function normalizeRuleSet(pRuleSet) { Reflect.deleteProperty(lRuleSet, "allowed"); Reflect.deleteProperty(lRuleSet, "allowedSeverity"); } else { - lRuleSet.allowed = lRuleSet.allowed.map((pRule) => ({ - ...pRule, - name: "not-in-allowed", - from: normalizeREProperties(pRule.from), - to: normalizeREProperties(pRule.to), - })); + lRuleSet.allowed = lRuleSet.allowed.map((pRule) => { + const lRuleTo = normalizeVias(pRule.to); + return { + ...pRule, + name: "not-in-allowed", + from: normalizeREProperties(pRule.from), + to: normalizeREProperties(lRuleTo), + }; + }); } } diff --git a/src/schema/baseline-violations.schema.json b/src/schema/baseline-violations.schema.json index edf93bbbd..9ffd32d91 100644 --- a/src/schema/baseline-violations.schema.json +++ b/src/schema/baseline-violations.schema.json @@ -125,6 +125,7 @@ "npm-peer", "npm-unknown", "npm", + "pre-compilation-only", "require", "triple-slash-amd-dependency", "triple-slash-directive", diff --git a/src/schema/baseline-violations.schema.mjs b/src/schema/baseline-violations.schema.mjs index 7d46d6f81..3de7b5782 100644 --- a/src/schema/baseline-violations.schema.mjs +++ b/src/schema/baseline-violations.schema.mjs @@ -1 +1 @@ -/* generated - don't edit */export default {"title":"dependency-cruiser baseline ('known errors') format","$schema":"http://json-schema.org/draft-07/schema#","$id":"https://dependency-cruiser.js.org/schema/baseline-violations.schema.json","$ref":"#/definitions/ViolationsType","definitions":{"ViolationsType":{"type":"array","items":{"$ref":"#/definitions/ViolationType"}},"ViolationType":{"type":"object","required":["from","to","rule"],"additionalProperties":false,"properties":{"from":{"type":"string"},"to":{"type":"string"},"type":{"$ref":"#/definitions/ViolationTypeType"},"rule":{"$ref":"#/definitions/RuleSummaryType"},"cycle":{"type":"array","items":{"$ref":"#/definitions/MiniDependency"}},"via":{"type":"array","items":{"type":"string"}},"metrics":{"type":"object","required":["from","to"],"additionalProperties":false,"properties":{"from":{"type":"object","required":["instability"],"additionalProperties":false,"properties":{"instability":{"type":"number"}}},"to":{"type":"object","required":["instability"],"additionalProperties":false,"properties":{"instability":{"type":"number"}}}}},"comment":{"type":"string"}}},"RuleSummaryType":{"type":"object","required":["name","severity"],"additionalProperties":false,"properties":{"name":{"type":"string"},"severity":{"$ref":"#/definitions/SeverityType"}}},"SeverityType":{"type":"string","enum":["error","warn","info","ignore"]},"ViolationTypeType":{"type":"string","enum":["dependency","module","reachability","cycle","instability","folder"]},"MiniDependency":{"type":"object","required":["name","dependencyTypes"],"additionalProperties":false,"properties":{"name":{"type":"string"},"dependencyTypes":{"type":"array","items":{"$ref":"#/definitions/DependencyTypeType"}}}},"DependencyTypeType":{"type":"string","enum":["aliased-subpath-import","aliased-tsconfig-base-url","aliased-tsconfig-paths","aliased-tsconfig","aliased-webpack","aliased-workspace","aliased","amd-define","amd-require","amd-exotic-require","core","deprecated","dynamic-import","exotic-require","export","import-equals","import","local","localmodule","npm-bundled","npm-dev","npm-no-pkg","npm-optional","npm-peer","npm-unknown","npm","require","triple-slash-amd-dependency","triple-slash-directive","triple-slash-file-reference","triple-slash-type-reference","type-import","type-only","undetermined","unknown"]}}} \ No newline at end of file +/* generated - don't edit */export default {"title":"dependency-cruiser baseline ('known errors') format","$schema":"http://json-schema.org/draft-07/schema#","$id":"https://dependency-cruiser.js.org/schema/baseline-violations.schema.json","$ref":"#/definitions/ViolationsType","definitions":{"ViolationsType":{"type":"array","items":{"$ref":"#/definitions/ViolationType"}},"ViolationType":{"type":"object","required":["from","to","rule"],"additionalProperties":false,"properties":{"from":{"type":"string"},"to":{"type":"string"},"type":{"$ref":"#/definitions/ViolationTypeType"},"rule":{"$ref":"#/definitions/RuleSummaryType"},"cycle":{"type":"array","items":{"$ref":"#/definitions/MiniDependency"}},"via":{"type":"array","items":{"type":"string"}},"metrics":{"type":"object","required":["from","to"],"additionalProperties":false,"properties":{"from":{"type":"object","required":["instability"],"additionalProperties":false,"properties":{"instability":{"type":"number"}}},"to":{"type":"object","required":["instability"],"additionalProperties":false,"properties":{"instability":{"type":"number"}}}}},"comment":{"type":"string"}}},"RuleSummaryType":{"type":"object","required":["name","severity"],"additionalProperties":false,"properties":{"name":{"type":"string"},"severity":{"$ref":"#/definitions/SeverityType"}}},"SeverityType":{"type":"string","enum":["error","warn","info","ignore"]},"ViolationTypeType":{"type":"string","enum":["dependency","module","reachability","cycle","instability","folder"]},"MiniDependency":{"type":"object","required":["name","dependencyTypes"],"additionalProperties":false,"properties":{"name":{"type":"string"},"dependencyTypes":{"type":"array","items":{"$ref":"#/definitions/DependencyTypeType"}}}},"DependencyTypeType":{"type":"string","enum":["aliased-subpath-import","aliased-tsconfig-base-url","aliased-tsconfig-paths","aliased-tsconfig","aliased-webpack","aliased-workspace","aliased","amd-define","amd-require","amd-exotic-require","core","deprecated","dynamic-import","exotic-require","export","import-equals","import","local","localmodule","npm-bundled","npm-dev","npm-no-pkg","npm-optional","npm-peer","npm-unknown","npm","pre-compilation-only","require","triple-slash-amd-dependency","triple-slash-directive","triple-slash-file-reference","triple-slash-type-reference","type-import","type-only","undetermined","unknown"]}}} \ No newline at end of file diff --git a/src/schema/configuration.schema.json b/src/schema/configuration.schema.json index c4930fadc..00b6aa710 100644 --- a/src/schema/configuration.schema.json +++ b/src/schema/configuration.schema.json @@ -172,6 +172,35 @@ "to": { "$ref": "#/definitions/RequiredToRestrictionType" } } }, + "MiniDependencyRestrictionType": { + "oneOf": [ + { "$ref": "#/definitions/REAsStringsType" }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "description": "A regular expression or an array of regular expressions the 'via' module should match to be caught by this rule.", + "$ref": "#/definitions/REAsStringsType" + }, + "pathNot": { + "description": "A regular expression or an array of regular expressions an the 'via' module should _not_ match to be caught by this rule.", + "$ref": "#/definitions/REAsStringsType" + }, + "dependencyTypes": { + "type": "array", + "description": "Which dependency types the dependency between this via and the previous one in the 'via chain' should have to be caught by this rule.", + "items": { "$ref": "#/definitions/DependencyTypeType" } + }, + "dependencyTypesNot": { + "type": "array", + "description": "Which dependency types the dependency between this via and the previous one in the 'via chain' should _not_ have to be caught by this rule.", + "items": { "$ref": "#/definitions/DependencyTypeType" } + } + } + } + ] + }, "FromRestrictionType": { "type": "object", "description": "Criteria an end of a dependency should match to be caught by this rule. Leave it empty if you want any module to be matched.", @@ -270,19 +299,21 @@ "$ref": "#/definitions/REAsStringsType" }, "via": { - "description": "For circular dependencies - whether or not to match cycles that include some modules with this regular expression. If you want to match cycles that _exclusively_ include modules satisfying the regular expression use the viaOnly restriction.E.g. to allow all cycles, except when they go through one specific module. Typically to temporarily disallow some cycles with a lower severity - setting up a rule with a via that ignores them in an 'allowed' section.", - "$ref": "#/definitions/REAsStringsType" + "description": "For circular dependencies - whether or not to match cycles that include some modules with these conditions. If you want to match cycles that _exclusively_ include modules satisfying them use the viaOnly restriction.E.g. to allow all cycles, except when they go through one specific module. Typically to temporarily disallow some cycles with a lower severity - setting up a rule with a via that ignores them in an 'allowed' section.", + "$ref": "#/definitions/MiniDependencyRestrictionType" }, "viaOnly": { - "description": "For circular dependencies - whether or not to match cycles that include exclusively modules with this regular expression. This is different from the regular via that already matches when only some of the modules in the cycle satisfy the regular expression", - "$ref": "#/definitions/REAsStringsType" + "description": "For circular dependencies - whether or not to match cycles that include exclusively modules with these conditions. This is different from the regular via that already matches when only _some_ of the modules in the cycle satisfy the condition.", + "$ref": "#/definitions/MiniDependencyRestrictionType" }, "viaNot": { - "description": "For circular dependencies - whether or not to match cycles that include _only_ modules that don't satisfy this regular expression. E.g. to disallow all cycles, except when they go through one specific module. Typically to temporarily allow some cycles until they're removed.", + "description": "This attribute is deprecated. Use 'viaOnly' with a 'pathNot' attribute in stead.", + "deprecated": true, "$ref": "#/definitions/REAsStringsType" }, "viaSomeNot": { - "description": "For circular dependencies - whether or not to match cycles that include _some_ modules that don't satisfy this regular expression. ", + "description": "This attribute is deprecated. Use 'via' with a 'pathNot' attribute in stead.", + "deprecated": true, "$ref": "#/definitions/REAsStringsType" }, "moreUnstable": { @@ -412,6 +443,7 @@ "npm-peer", "npm-unknown", "npm", + "pre-compilation-only", "require", "triple-slash-amd-dependency", "triple-slash-directive", @@ -674,7 +706,7 @@ "cache": { "oneOf": [ { "type": "boolean" }, - { "type": "string" }, + { "deprecated": true, "type": "string" }, { "$ref": "#/definitions/CacheOptionsType" } ], "description": "- false: don't use caching. \n- true or empty object: use caching with the default settings \n- a string (deprecated): cache in the folder denoted by the string & use the \n default caching strategy. This is deprecated - instead pass a cache object \n e.g. ```{ folder: 'your/cache/location' }```.\n\nDefaults to false (no caching).\nWhen caching is switched on the default cache folder is 'node_modules/.cache/dependency-cruiser/'" diff --git a/src/schema/configuration.schema.mjs b/src/schema/configuration.schema.mjs index 7accabe25..cd6515866 100644 --- a/src/schema/configuration.schema.mjs +++ b/src/schema/configuration.schema.mjs @@ -1 +1 @@ -/* generated - don't edit */export default {"title":"dependency-cruiser configuration","$schema":"http://json-schema.org/draft-07/schema#","$id":"https://dependency-cruiser.js.org/schema/configuration.schema.json","type":"object","additionalProperties":false,"properties":{"$schema":{"type":"string"},"forbidden":{"type":"array","items":{"$ref":"#/definitions/ForbiddenRuleType"}},"allowed":{"type":"array","items":{"$ref":"#/definitions/AllowedRuleType"}},"allowedSeverity":{"$ref":"#/definitions/SeverityType"},"required":{"type":"array","items":{"$ref":"#/definitions/RequiredRuleType"}},"options":{"$ref":"#/definitions/OptionsType"},"extends":{"$ref":"#/definitions/ExtendsType"}},"definitions":{"RuleSetType":{"type":"object","additionalProperties":false,"properties":{"forbidden":{"type":"array","items":{"$ref":"#/definitions/ForbiddenRuleType"}},"allowed":{"type":"array","items":{"$ref":"#/definitions/AllowedRuleType"}},"allowedSeverity":{"$ref":"#/definitions/SeverityType"},"required":{"type":"array","items":{"$ref":"#/definitions/RequiredRuleType"}}}},"AllowedRuleType":{"oneOf":[{"$ref":"#/definitions/RegularAllowedRuleType"},{"$ref":"#/definitions/ReachabilityAllowedRuleType"}]},"RegularAllowedRuleType":{"type":"object","required":["from","to"],"additionalProperties":false,"properties":{"comment":{"type":"string"},"scope":{"type":"string","enum":["module","folder"]},"from":{"$ref":"#/definitions/FromRestrictionType"},"to":{"$ref":"#/definitions/ToRestrictionType"}}},"ReachabilityAllowedRuleType":{"type":"object","required":["from","to"],"additionalProperties":false,"properties":{"comment":{"type":"string"},"scope":{"type":"string","enum":["module","folder"]},"from":{"$ref":"#/definitions/ReachabilityFromRestrictionType"},"to":{"$ref":"#/definitions/ReachabilityToRestrictionType"}}},"ForbiddenRuleType":{"oneOf":[{"$ref":"#/definitions/RegularForbiddenRuleType"},{"$ref":"#/definitions/ReachabilityForbiddenRuleType"},{"$ref":"#/definitions/DependentsForbiddenRuleType"}]},"RegularForbiddenRuleType":{"type":"object","required":["from","to"],"additionalProperties":false,"properties":{"name":{"type":"string"},"severity":{"$ref":"#/definitions/SeverityType"},"scope":{"type":"string","enum":["module","folder"]},"comment":{"type":"string"},"from":{"$ref":"#/definitions/FromRestrictionType"},"to":{"$ref":"#/definitions/ToRestrictionType"}}},"DependentsForbiddenRuleType":{"type":"object","required":["module","from"],"additionalProperties":false,"properties":{"name":{"type":"string"},"severity":{"$ref":"#/definitions/SeverityType"},"scope":{"type":"string","enum":["module","folder"]},"comment":{"type":"string"},"module":{"$ref":"#/definitions/DependentsModuleRestrictionType"},"from":{"$ref":"#/definitions/DependentsFromRestrictionType"}}},"ReachabilityForbiddenRuleType":{"type":"object","required":["from","to"],"additionalProperties":false,"properties":{"name":{"type":"string"},"severity":{"$ref":"#/definitions/SeverityType"},"scope":{"type":"string","enum":["module","folder"]},"comment":{"type":"string"},"from":{"$ref":"#/definitions/ReachabilityFromRestrictionType"},"to":{"$ref":"#/definitions/ReachabilityToRestrictionType"}}},"RequiredRuleType":{"type":"object","required":["module","to"],"additionalProperties":false,"properties":{"name":{"type":"string"},"severity":{"$ref":"#/definitions/SeverityType"},"scope":{"type":"string","enum":["module","folder"]},"comment":{"type":"string"},"module":{"$ref":"#/definitions/RequiredModuleRestrictionType"},"to":{"$ref":"#/definitions/RequiredToRestrictionType"}}},"FromRestrictionType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"},"orphan":{"type":"boolean"}}},"ReachabilityFromRestrictionType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"}}},"ToRestrictionType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"},"couldNotResolve":{"type":"boolean"},"circular":{"type":"boolean"},"dynamic":{"type":"boolean"},"exoticallyRequired":{"type":"boolean"},"exoticRequire":{"$ref":"#/definitions/REAsStringsType"},"exoticRequireNot":{"$ref":"#/definitions/REAsStringsType"},"preCompilationOnly":{"type":"boolean"},"dependencyTypes":{"type":"array","items":{"$ref":"#/definitions/DependencyTypeType"}},"dependencyTypesNot":{"type":"array","items":{"$ref":"#/definitions/DependencyTypeType"}},"moreThanOneDependencyType":{"type":"boolean"},"license":{"$ref":"#/definitions/REAsStringsType"},"licenseNot":{"$ref":"#/definitions/REAsStringsType"},"via":{"$ref":"#/definitions/REAsStringsType"},"viaOnly":{"$ref":"#/definitions/REAsStringsType"},"viaNot":{"$ref":"#/definitions/REAsStringsType"},"viaSomeNot":{"$ref":"#/definitions/REAsStringsType"},"moreUnstable":{"type":"boolean"}}},"DependentsModuleRestrictionType":{"required":[],"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"},"numberOfDependentsLessThan":{"type":"integer","minimum":0,"maximum":100},"numberOfDependentsMoreThan":{"type":"integer","minimum":0,"maximum":100}}},"DependentsFromRestrictionType":{"required":[],"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"}}},"ReachabilityToRestrictionType":{"required":["reachable"],"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"},"reachable":{"type":"boolean"}}},"RequiredModuleRestrictionType":{"required":[],"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"}}},"RequiredToRestrictionType":{"required":[],"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"}}},"DependencyTypeType":{"type":"string","enum":["aliased-subpath-import","aliased-tsconfig-base-url","aliased-tsconfig-paths","aliased-tsconfig","aliased-webpack","aliased-workspace","aliased","amd-define","amd-require","amd-exotic-require","core","deprecated","dynamic-import","exotic-require","export","import-equals","import","local","localmodule","npm-bundled","npm-dev","npm-no-pkg","npm-optional","npm-peer","npm-unknown","npm","require","triple-slash-amd-dependency","triple-slash-directive","triple-slash-file-reference","triple-slash-type-reference","type-import","type-only","undetermined","unknown"]},"REAsStringsType":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"SeverityType":{"type":"string","enum":["error","warn","info","ignore"]},"OptionsType":{"type":"object","additionalProperties":false,"properties":{"doNotFollow":{"oneOf":[{"$ref":"#/definitions/REAsStringsType"},{"$ref":"#/definitions/CompoundDoNotFollowType"}]},"exclude":{"oneOf":[{"$ref":"#/definitions/REAsStringsType"},{"$ref":"#/definitions/CompoundExcludeType"}]},"includeOnly":{"oneOf":[{"$ref":"#/definitions/REAsStringsType"},{"$ref":"#/definitions/CompoundIncludeOnlyType"}]},"focus":{"oneOf":[{"$ref":"#/definitions/REAsStringsType"},{"$ref":"#/definitions/CompoundFocusType"}]},"reaches":{"oneOf":[{"$ref":"#/definitions/REAsStringsType"},{"$ref":"#/definitions/CompoundReachesType"}]},"highlight":{"oneOf":[{"$ref":"#/definitions/REAsStringsType"},{"$ref":"#/definitions/CompoundHighlightType"}]},"knownViolations":{"$ref":"#/definitions/ViolationsType"},"collapse":{"oneOf":[{"type":"string"},{"type":"integer","minimum":1,"maximum":9}]},"maxDepth":{"type":"integer","minimum":0,"maximum":99},"moduleSystems":{"$ref":"#/definitions/ModuleSystemsType"},"prefix":{"type":"string"},"preserveSymlinks":{"type":"boolean"},"combinedDependencies":{"type":"boolean"},"tsConfig":{"type":"object","additionalProperties":false,"properties":{"fileName":{"type":"string"}}},"tsPreCompilationDeps":{"oneOf":[{"type":"boolean"},{"type":"string","enum":["specify"]}]},"extraExtensionsToScan":{"type":"array","items":{"type":"string"}},"externalModuleResolutionStrategy":{"type":"string","enum":["node_modules","yarn-pnp"]},"builtInModules":{"type":"object","additionalProperties":false,"properties":{"override":{"type":"array","items":{"type":"string"}},"add":{"type":"array","items":{"type":"string"}}}},"forceDeriveDependents":{"type":"boolean"},"webpackConfig":{"type":"object","additionalProperties":false,"properties":{"fileName":{"type":"string"},"env":{"oneOf":[{"type":"object"},{"type":"string"}]},"arguments":{"type":"object"}}},"enhancedResolveOptions":{"type":"object","additionalProperties":false,"properties":{"exportsFields":{"type":"array","items":{"type":"string"}},"conditionNames":{"type":"array","items":{"type":"string"}},"extensions":{"type":"array","items":{"type":"string"}},"mainFields":{"type":"array","items":{"type":"string"}},"mainFiles":{"type":"array"},"aliasFields":{"type":"array","items":{"type":"string"}},"cachedInputFileSystem":{"type":"object","additionalProperties":false,"properties":{"cacheDuration":{"type":"integer","minimum":0,"maximum":1800000}}}}},"babelConfig":{"type":"object","additionalProperties":false,"properties":{"fileName":{"type":"string"}}},"parser":{"type":"string","enum":["acorn","swc","tsc"]},"exoticRequireStrings":{"type":"array","items":{"type":"string"}},"reporterOptions":{"$ref":"#/definitions/ReporterOptionsType"},"progress":{"type":"object","additionalProperties":false,"properties":{"type":{"type":"string","enum":["cli-feedback","performance-log","ndjson","none"]},"maximumLevel":{"type":"number","enum":[-1,40,50,60,70,80,99]}}},"metrics":{"type":"boolean"},"baseDir":{"type":"string"},"cache":{"oneOf":[{"type":"boolean"},{"type":"string"},{"$ref":"#/definitions/CacheOptionsType"}]}}},"ModuleSystemType":{"type":"string","enum":["cjs","es6","amd","tsd"]},"ModuleSystemsType":{"type":"array","items":{"$ref":"#/definitions/ModuleSystemType"}},"CompoundExcludeType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"dynamic":{"type":"boolean"}}},"CompoundDoNotFollowType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"dependencyTypes":{"type":"array","items":{"$ref":"#/definitions/DependencyTypeType"}}}},"CompoundIncludeOnlyType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"}}},"CompoundFocusType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"depth":{"type":"number","minimum":1,"maximum":4}}},"CompoundReachesType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"}}},"CompoundHighlightType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"}}},"ReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"anon":{"$ref":"#/definitions/AnonReporterOptionsType"},"archi":{"$ref":"#/definitions/DotReporterOptionsType"},"dot":{"$ref":"#/definitions/DotReporterOptionsType"},"ddot":{"$ref":"#/definitions/DotReporterOptionsType"},"flat":{"$ref":"#/definitions/DotReporterOptionsType"},"markdown":{"$ref":"#/definitions/MarkdownReporterOptionsType"},"metrics":{"$ref":"#/definitions/MetricsReporterOptionsType"},"mermaid":{"$ref":"#/definitions/MermaidReporterOptionsType"},"text":{"$ref":"#/definitions/TextReporterOptionsType"}}},"AnonReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"wordlist":{"type":"array","items":{"type":"string"}}}},"MetricsReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"orderBy":{"type":"string","enum":["instability","moduleCount","afferentCouplings","efferentCouplings","name"]},"hideModules":{"type":"boolean"},"hideFolders":{"type":"boolean"}}},"MarkdownReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"showTitle":{"type":"boolean"},"title":{"type":"string"},"showSummary":{"type":"boolean"},"showSummaryHeader":{"type":"boolean"},"summaryHeader":{"type":"string"},"showStatsSummary":{"type":"boolean"},"showRulesSummary":{"type":"boolean"},"includeIgnoredInSummary":{"type":"boolean"},"showDetails":{"type":"boolean"},"includeIgnoredInDetails":{"type":"boolean"},"showDetailsHeader":{"type":"boolean"},"detailsHeader":{"type":"string"},"collapseDetails":{"type":"boolean"},"collapsedMessage":{"type":"string"},"noViolationsMessage":{"type":"string"},"showFooter":{"type":"boolean"}}},"MermaidReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"minify":{"type":"boolean"}}},"TextReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"highlightFocused":{"type":"boolean"}}},"DotReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"collapsePattern":{"$ref":"#/definitions/REAsStringsType"},"filters":{"$ref":"#/definitions/ReporterFiltersType"},"showMetrics":{"type":"boolean"},"theme":{"$ref":"#/definitions/DotThemeType"}}},"DotThemeType":{"type":"object","additionalProperties":false,"properties":{"replace":{"type":"boolean"},"graph":{"type":"object"},"node":{"type":"object"},"edge":{"type":"object"},"modules":{"$ref":"#/definitions/DotThemeArrayType"},"dependencies":{"$ref":"#/definitions/DotThemeArrayType"}}},"DotThemeArrayType":{"type":"array","items":{"$ref":"#/definitions/DotThemeEntryType"}},"DotThemeEntryType":{"type":"object","additionalProperties":false,"properties":{"criteria":{"type":"object"},"attributes":{"type":"object"}}},"ReporterFiltersType":{"type":"object","additionalProperties":false,"properties":{"exclude":{"$ref":"#/definitions/CompoundExcludeType"},"includeOnly":{"$ref":"#/definitions/CompoundIncludeOnlyType"},"focus":{"$ref":"#/definitions/CompoundFocusType"},"reaches":{"$ref":"#/definitions/CompoundReachesType"}}},"ViolationsType":{"type":"array","items":{"$ref":"#/definitions/ViolationType"}},"ViolationType":{"type":"object","required":["from","to","rule"],"additionalProperties":false,"properties":{"from":{"type":"string"},"to":{"type":"string"},"type":{"$ref":"#/definitions/ViolationTypeType"},"rule":{"$ref":"#/definitions/RuleSummaryType"},"cycle":{"type":"array","items":{"$ref":"#/definitions/MiniDependency"}},"via":{"type":"array","items":{"type":"string"}},"metrics":{"type":"object","required":["from","to"],"additionalProperties":false,"properties":{"from":{"type":"object","required":["instability"],"additionalProperties":false,"properties":{"instability":{"type":"number"}}},"to":{"type":"object","required":["instability"],"additionalProperties":false,"properties":{"instability":{"type":"number"}}}}},"comment":{"type":"string"}}},"RuleSummaryType":{"type":"object","required":["name","severity"],"additionalProperties":false,"properties":{"name":{"type":"string"},"severity":{"$ref":"#/definitions/SeverityType"}}},"ViolationTypeType":{"type":"string","enum":["dependency","module","reachability","cycle","instability","folder"]},"MiniDependency":{"type":"object","required":["name","dependencyTypes"],"additionalProperties":false,"properties":{"name":{"type":"string"},"dependencyTypes":{"type":"array","items":{"$ref":"#/definitions/DependencyTypeType"}}}},"CacheOptionsType":{"type":"object","additionalProperties":false,"properties":{"folder":{"type":"string"},"strategy":{"$ref":"#/definitions/CacheStrategyType"},"compress":{"type":"boolean","default":false}}},"CacheStrategyType":{"type":"string","enum":["metadata","content"]},"ExtendsType":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]}}} \ No newline at end of file +/* generated - don't edit */export default {"title":"dependency-cruiser configuration","$schema":"http://json-schema.org/draft-07/schema#","$id":"https://dependency-cruiser.js.org/schema/configuration.schema.json","type":"object","additionalProperties":false,"properties":{"$schema":{"type":"string"},"forbidden":{"type":"array","items":{"$ref":"#/definitions/ForbiddenRuleType"}},"allowed":{"type":"array","items":{"$ref":"#/definitions/AllowedRuleType"}},"allowedSeverity":{"$ref":"#/definitions/SeverityType"},"required":{"type":"array","items":{"$ref":"#/definitions/RequiredRuleType"}},"options":{"$ref":"#/definitions/OptionsType"},"extends":{"$ref":"#/definitions/ExtendsType"}},"definitions":{"RuleSetType":{"type":"object","additionalProperties":false,"properties":{"forbidden":{"type":"array","items":{"$ref":"#/definitions/ForbiddenRuleType"}},"allowed":{"type":"array","items":{"$ref":"#/definitions/AllowedRuleType"}},"allowedSeverity":{"$ref":"#/definitions/SeverityType"},"required":{"type":"array","items":{"$ref":"#/definitions/RequiredRuleType"}}}},"AllowedRuleType":{"oneOf":[{"$ref":"#/definitions/RegularAllowedRuleType"},{"$ref":"#/definitions/ReachabilityAllowedRuleType"}]},"RegularAllowedRuleType":{"type":"object","required":["from","to"],"additionalProperties":false,"properties":{"comment":{"type":"string"},"scope":{"type":"string","enum":["module","folder"]},"from":{"$ref":"#/definitions/FromRestrictionType"},"to":{"$ref":"#/definitions/ToRestrictionType"}}},"ReachabilityAllowedRuleType":{"type":"object","required":["from","to"],"additionalProperties":false,"properties":{"comment":{"type":"string"},"scope":{"type":"string","enum":["module","folder"]},"from":{"$ref":"#/definitions/ReachabilityFromRestrictionType"},"to":{"$ref":"#/definitions/ReachabilityToRestrictionType"}}},"ForbiddenRuleType":{"oneOf":[{"$ref":"#/definitions/RegularForbiddenRuleType"},{"$ref":"#/definitions/ReachabilityForbiddenRuleType"},{"$ref":"#/definitions/DependentsForbiddenRuleType"}]},"RegularForbiddenRuleType":{"type":"object","required":["from","to"],"additionalProperties":false,"properties":{"name":{"type":"string"},"severity":{"$ref":"#/definitions/SeverityType"},"scope":{"type":"string","enum":["module","folder"]},"comment":{"type":"string"},"from":{"$ref":"#/definitions/FromRestrictionType"},"to":{"$ref":"#/definitions/ToRestrictionType"}}},"DependentsForbiddenRuleType":{"type":"object","required":["module","from"],"additionalProperties":false,"properties":{"name":{"type":"string"},"severity":{"$ref":"#/definitions/SeverityType"},"scope":{"type":"string","enum":["module","folder"]},"comment":{"type":"string"},"module":{"$ref":"#/definitions/DependentsModuleRestrictionType"},"from":{"$ref":"#/definitions/DependentsFromRestrictionType"}}},"ReachabilityForbiddenRuleType":{"type":"object","required":["from","to"],"additionalProperties":false,"properties":{"name":{"type":"string"},"severity":{"$ref":"#/definitions/SeverityType"},"scope":{"type":"string","enum":["module","folder"]},"comment":{"type":"string"},"from":{"$ref":"#/definitions/ReachabilityFromRestrictionType"},"to":{"$ref":"#/definitions/ReachabilityToRestrictionType"}}},"RequiredRuleType":{"type":"object","required":["module","to"],"additionalProperties":false,"properties":{"name":{"type":"string"},"severity":{"$ref":"#/definitions/SeverityType"},"scope":{"type":"string","enum":["module","folder"]},"comment":{"type":"string"},"module":{"$ref":"#/definitions/RequiredModuleRestrictionType"},"to":{"$ref":"#/definitions/RequiredToRestrictionType"}}},"MiniDependencyRestrictionType":{"oneOf":[{"$ref":"#/definitions/REAsStringsType"},{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"},"dependencyTypes":{"type":"array","items":{"$ref":"#/definitions/DependencyTypeType"}},"dependencyTypesNot":{"type":"array","items":{"$ref":"#/definitions/DependencyTypeType"}}}}]},"FromRestrictionType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"},"orphan":{"type":"boolean"}}},"ReachabilityFromRestrictionType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"}}},"ToRestrictionType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"},"couldNotResolve":{"type":"boolean"},"circular":{"type":"boolean"},"dynamic":{"type":"boolean"},"exoticallyRequired":{"type":"boolean"},"exoticRequire":{"$ref":"#/definitions/REAsStringsType"},"exoticRequireNot":{"$ref":"#/definitions/REAsStringsType"},"preCompilationOnly":{"type":"boolean"},"dependencyTypes":{"type":"array","items":{"$ref":"#/definitions/DependencyTypeType"}},"dependencyTypesNot":{"type":"array","items":{"$ref":"#/definitions/DependencyTypeType"}},"moreThanOneDependencyType":{"type":"boolean"},"license":{"$ref":"#/definitions/REAsStringsType"},"licenseNot":{"$ref":"#/definitions/REAsStringsType"},"via":{"$ref":"#/definitions/MiniDependencyRestrictionType"},"viaOnly":{"$ref":"#/definitions/MiniDependencyRestrictionType"},"viaNot":{"deprecated":true,"$ref":"#/definitions/REAsStringsType"},"viaSomeNot":{"deprecated":true,"$ref":"#/definitions/REAsStringsType"},"moreUnstable":{"type":"boolean"}}},"DependentsModuleRestrictionType":{"required":[],"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"},"numberOfDependentsLessThan":{"type":"integer","minimum":0,"maximum":100},"numberOfDependentsMoreThan":{"type":"integer","minimum":0,"maximum":100}}},"DependentsFromRestrictionType":{"required":[],"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"}}},"ReachabilityToRestrictionType":{"required":["reachable"],"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"},"reachable":{"type":"boolean"}}},"RequiredModuleRestrictionType":{"required":[],"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"}}},"RequiredToRestrictionType":{"required":[],"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"}}},"DependencyTypeType":{"type":"string","enum":["aliased-subpath-import","aliased-tsconfig-base-url","aliased-tsconfig-paths","aliased-tsconfig","aliased-webpack","aliased-workspace","aliased","amd-define","amd-require","amd-exotic-require","core","deprecated","dynamic-import","exotic-require","export","import-equals","import","local","localmodule","npm-bundled","npm-dev","npm-no-pkg","npm-optional","npm-peer","npm-unknown","npm","pre-compilation-only","require","triple-slash-amd-dependency","triple-slash-directive","triple-slash-file-reference","triple-slash-type-reference","type-import","type-only","undetermined","unknown"]},"REAsStringsType":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"SeverityType":{"type":"string","enum":["error","warn","info","ignore"]},"OptionsType":{"type":"object","additionalProperties":false,"properties":{"doNotFollow":{"oneOf":[{"$ref":"#/definitions/REAsStringsType"},{"$ref":"#/definitions/CompoundDoNotFollowType"}]},"exclude":{"oneOf":[{"$ref":"#/definitions/REAsStringsType"},{"$ref":"#/definitions/CompoundExcludeType"}]},"includeOnly":{"oneOf":[{"$ref":"#/definitions/REAsStringsType"},{"$ref":"#/definitions/CompoundIncludeOnlyType"}]},"focus":{"oneOf":[{"$ref":"#/definitions/REAsStringsType"},{"$ref":"#/definitions/CompoundFocusType"}]},"reaches":{"oneOf":[{"$ref":"#/definitions/REAsStringsType"},{"$ref":"#/definitions/CompoundReachesType"}]},"highlight":{"oneOf":[{"$ref":"#/definitions/REAsStringsType"},{"$ref":"#/definitions/CompoundHighlightType"}]},"knownViolations":{"$ref":"#/definitions/ViolationsType"},"collapse":{"oneOf":[{"type":"string"},{"type":"integer","minimum":1,"maximum":9}]},"maxDepth":{"type":"integer","minimum":0,"maximum":99},"moduleSystems":{"$ref":"#/definitions/ModuleSystemsType"},"prefix":{"type":"string"},"preserveSymlinks":{"type":"boolean"},"combinedDependencies":{"type":"boolean"},"tsConfig":{"type":"object","additionalProperties":false,"properties":{"fileName":{"type":"string"}}},"tsPreCompilationDeps":{"oneOf":[{"type":"boolean"},{"type":"string","enum":["specify"]}]},"extraExtensionsToScan":{"type":"array","items":{"type":"string"}},"externalModuleResolutionStrategy":{"type":"string","enum":["node_modules","yarn-pnp"]},"builtInModules":{"type":"object","additionalProperties":false,"properties":{"override":{"type":"array","items":{"type":"string"}},"add":{"type":"array","items":{"type":"string"}}}},"forceDeriveDependents":{"type":"boolean"},"webpackConfig":{"type":"object","additionalProperties":false,"properties":{"fileName":{"type":"string"},"env":{"oneOf":[{"type":"object"},{"type":"string"}]},"arguments":{"type":"object"}}},"enhancedResolveOptions":{"type":"object","additionalProperties":false,"properties":{"exportsFields":{"type":"array","items":{"type":"string"}},"conditionNames":{"type":"array","items":{"type":"string"}},"extensions":{"type":"array","items":{"type":"string"}},"mainFields":{"type":"array","items":{"type":"string"}},"mainFiles":{"type":"array"},"aliasFields":{"type":"array","items":{"type":"string"}},"cachedInputFileSystem":{"type":"object","additionalProperties":false,"properties":{"cacheDuration":{"type":"integer","minimum":0,"maximum":1800000}}}}},"babelConfig":{"type":"object","additionalProperties":false,"properties":{"fileName":{"type":"string"}}},"parser":{"type":"string","enum":["acorn","swc","tsc"]},"exoticRequireStrings":{"type":"array","items":{"type":"string"}},"reporterOptions":{"$ref":"#/definitions/ReporterOptionsType"},"progress":{"type":"object","additionalProperties":false,"properties":{"type":{"type":"string","enum":["cli-feedback","performance-log","ndjson","none"]},"maximumLevel":{"type":"number","enum":[-1,40,50,60,70,80,99]}}},"metrics":{"type":"boolean"},"baseDir":{"type":"string"},"cache":{"oneOf":[{"type":"boolean"},{"deprecated":true,"type":"string"},{"$ref":"#/definitions/CacheOptionsType"}]}}},"ModuleSystemType":{"type":"string","enum":["cjs","es6","amd","tsd"]},"ModuleSystemsType":{"type":"array","items":{"$ref":"#/definitions/ModuleSystemType"}},"CompoundExcludeType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"dynamic":{"type":"boolean"}}},"CompoundDoNotFollowType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"dependencyTypes":{"type":"array","items":{"$ref":"#/definitions/DependencyTypeType"}}}},"CompoundIncludeOnlyType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"}}},"CompoundFocusType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"depth":{"type":"number","minimum":1,"maximum":4}}},"CompoundReachesType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"}}},"CompoundHighlightType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"}}},"ReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"anon":{"$ref":"#/definitions/AnonReporterOptionsType"},"archi":{"$ref":"#/definitions/DotReporterOptionsType"},"dot":{"$ref":"#/definitions/DotReporterOptionsType"},"ddot":{"$ref":"#/definitions/DotReporterOptionsType"},"flat":{"$ref":"#/definitions/DotReporterOptionsType"},"markdown":{"$ref":"#/definitions/MarkdownReporterOptionsType"},"metrics":{"$ref":"#/definitions/MetricsReporterOptionsType"},"mermaid":{"$ref":"#/definitions/MermaidReporterOptionsType"},"text":{"$ref":"#/definitions/TextReporterOptionsType"}}},"AnonReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"wordlist":{"type":"array","items":{"type":"string"}}}},"MetricsReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"orderBy":{"type":"string","enum":["instability","moduleCount","afferentCouplings","efferentCouplings","name"]},"hideModules":{"type":"boolean"},"hideFolders":{"type":"boolean"}}},"MarkdownReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"showTitle":{"type":"boolean"},"title":{"type":"string"},"showSummary":{"type":"boolean"},"showSummaryHeader":{"type":"boolean"},"summaryHeader":{"type":"string"},"showStatsSummary":{"type":"boolean"},"showRulesSummary":{"type":"boolean"},"includeIgnoredInSummary":{"type":"boolean"},"showDetails":{"type":"boolean"},"includeIgnoredInDetails":{"type":"boolean"},"showDetailsHeader":{"type":"boolean"},"detailsHeader":{"type":"string"},"collapseDetails":{"type":"boolean"},"collapsedMessage":{"type":"string"},"noViolationsMessage":{"type":"string"},"showFooter":{"type":"boolean"}}},"MermaidReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"minify":{"type":"boolean"}}},"TextReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"highlightFocused":{"type":"boolean"}}},"DotReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"collapsePattern":{"$ref":"#/definitions/REAsStringsType"},"filters":{"$ref":"#/definitions/ReporterFiltersType"},"showMetrics":{"type":"boolean"},"theme":{"$ref":"#/definitions/DotThemeType"}}},"DotThemeType":{"type":"object","additionalProperties":false,"properties":{"replace":{"type":"boolean"},"graph":{"type":"object"},"node":{"type":"object"},"edge":{"type":"object"},"modules":{"$ref":"#/definitions/DotThemeArrayType"},"dependencies":{"$ref":"#/definitions/DotThemeArrayType"}}},"DotThemeArrayType":{"type":"array","items":{"$ref":"#/definitions/DotThemeEntryType"}},"DotThemeEntryType":{"type":"object","additionalProperties":false,"properties":{"criteria":{"type":"object"},"attributes":{"type":"object"}}},"ReporterFiltersType":{"type":"object","additionalProperties":false,"properties":{"exclude":{"$ref":"#/definitions/CompoundExcludeType"},"includeOnly":{"$ref":"#/definitions/CompoundIncludeOnlyType"},"focus":{"$ref":"#/definitions/CompoundFocusType"},"reaches":{"$ref":"#/definitions/CompoundReachesType"}}},"ViolationsType":{"type":"array","items":{"$ref":"#/definitions/ViolationType"}},"ViolationType":{"type":"object","required":["from","to","rule"],"additionalProperties":false,"properties":{"from":{"type":"string"},"to":{"type":"string"},"type":{"$ref":"#/definitions/ViolationTypeType"},"rule":{"$ref":"#/definitions/RuleSummaryType"},"cycle":{"type":"array","items":{"$ref":"#/definitions/MiniDependency"}},"via":{"type":"array","items":{"type":"string"}},"metrics":{"type":"object","required":["from","to"],"additionalProperties":false,"properties":{"from":{"type":"object","required":["instability"],"additionalProperties":false,"properties":{"instability":{"type":"number"}}},"to":{"type":"object","required":["instability"],"additionalProperties":false,"properties":{"instability":{"type":"number"}}}}},"comment":{"type":"string"}}},"RuleSummaryType":{"type":"object","required":["name","severity"],"additionalProperties":false,"properties":{"name":{"type":"string"},"severity":{"$ref":"#/definitions/SeverityType"}}},"ViolationTypeType":{"type":"string","enum":["dependency","module","reachability","cycle","instability","folder"]},"MiniDependency":{"type":"object","required":["name","dependencyTypes"],"additionalProperties":false,"properties":{"name":{"type":"string"},"dependencyTypes":{"type":"array","items":{"$ref":"#/definitions/DependencyTypeType"}}}},"CacheOptionsType":{"type":"object","additionalProperties":false,"properties":{"folder":{"type":"string"},"strategy":{"$ref":"#/definitions/CacheStrategyType"},"compress":{"type":"boolean","default":false}}},"CacheStrategyType":{"type":"string","enum":["metadata","content"]},"ExtendsType":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]}}} \ No newline at end of file diff --git a/src/schema/cruise-result.schema.json b/src/schema/cruise-result.schema.json index 8684152fa..eba594917 100644 --- a/src/schema/cruise-result.schema.json +++ b/src/schema/cruise-result.schema.json @@ -293,6 +293,7 @@ "npm-peer", "npm-unknown", "npm", + "pre-compilation-only", "require", "triple-slash-amd-dependency", "triple-slash-directive", @@ -668,6 +669,35 @@ "to": { "$ref": "#/definitions/RequiredToRestrictionType" } } }, + "MiniDependencyRestrictionType": { + "oneOf": [ + { "$ref": "#/definitions/REAsStringsType" }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "description": "A regular expression or an array of regular expressions the 'via' module should match to be caught by this rule.", + "$ref": "#/definitions/REAsStringsType" + }, + "pathNot": { + "description": "A regular expression or an array of regular expressions an the 'via' module should _not_ match to be caught by this rule.", + "$ref": "#/definitions/REAsStringsType" + }, + "dependencyTypes": { + "type": "array", + "description": "Which dependency types the dependency between this via and the previous one in the 'via chain' should have to be caught by this rule.", + "items": { "$ref": "#/definitions/DependencyTypeType" } + }, + "dependencyTypesNot": { + "type": "array", + "description": "Which dependency types the dependency between this via and the previous one in the 'via chain' should _not_ have to be caught by this rule.", + "items": { "$ref": "#/definitions/DependencyTypeType" } + } + } + } + ] + }, "FromRestrictionType": { "type": "object", "description": "Criteria an end of a dependency should match to be caught by this rule. Leave it empty if you want any module to be matched.", @@ -766,19 +796,21 @@ "$ref": "#/definitions/REAsStringsType" }, "via": { - "description": "For circular dependencies - whether or not to match cycles that include some modules with this regular expression. If you want to match cycles that _exclusively_ include modules satisfying the regular expression use the viaOnly restriction.E.g. to allow all cycles, except when they go through one specific module. Typically to temporarily disallow some cycles with a lower severity - setting up a rule with a via that ignores them in an 'allowed' section.", - "$ref": "#/definitions/REAsStringsType" + "description": "For circular dependencies - whether or not to match cycles that include some modules with these conditions. If you want to match cycles that _exclusively_ include modules satisfying them use the viaOnly restriction.E.g. to allow all cycles, except when they go through one specific module. Typically to temporarily disallow some cycles with a lower severity - setting up a rule with a via that ignores them in an 'allowed' section.", + "$ref": "#/definitions/MiniDependencyRestrictionType" }, "viaOnly": { - "description": "For circular dependencies - whether or not to match cycles that include exclusively modules with this regular expression. This is different from the regular via that already matches when only some of the modules in the cycle satisfy the regular expression", - "$ref": "#/definitions/REAsStringsType" + "description": "For circular dependencies - whether or not to match cycles that include exclusively modules with these conditions. This is different from the regular via that already matches when only _some_ of the modules in the cycle satisfy the condition.", + "$ref": "#/definitions/MiniDependencyRestrictionType" }, "viaNot": { - "description": "For circular dependencies - whether or not to match cycles that include _only_ modules that don't satisfy this regular expression. E.g. to disallow all cycles, except when they go through one specific module. Typically to temporarily allow some cycles until they're removed.", + "description": "This attribute is deprecated. Use 'viaOnly' with a 'pathNot' attribute in stead.", + "deprecated": true, "$ref": "#/definitions/REAsStringsType" }, "viaSomeNot": { - "description": "For circular dependencies - whether or not to match cycles that include _some_ modules that don't satisfy this regular expression. ", + "description": "This attribute is deprecated. Use 'via' with a 'pathNot' attribute in stead.", + "deprecated": true, "$ref": "#/definitions/REAsStringsType" }, "moreUnstable": { diff --git a/src/schema/cruise-result.schema.mjs b/src/schema/cruise-result.schema.mjs index 64b35f46f..ea74b300f 100644 --- a/src/schema/cruise-result.schema.mjs +++ b/src/schema/cruise-result.schema.mjs @@ -1 +1 @@ -/* generated - don't edit */export default {"title":"dependency-cruiser output format","$schema":"http://json-schema.org/draft-07/schema#","$id":"https://dependency-cruiser.js.org/schema/cruise-result.schema.json","type":"object","required":["summary","modules"],"additionalProperties":false,"properties":{"modules":{"$ref":"#/definitions/ModulesType"},"folders":{"$ref":"#/definitions/FoldersType"},"summary":{"$ref":"#/definitions/SummaryType"},"revisionData":{"$ref":"#/definitions/RevisionDataType"}},"definitions":{"ModulesType":{"type":"array","items":{"$ref":"#/definitions/ModuleType"}},"ModuleType":{"type":"object","required":["source","dependencies","valid"],"additionalProperties":false,"properties":{"source":{"type":"string"},"valid":{"type":"boolean"},"dependencies":{"$ref":"#/definitions/DependenciesType"},"dependents":{"type":"array","items":{"type":"string"}},"followable":{"type":"boolean"},"matchesDoNotFollow":{"type":"boolean"},"matchesFocus":{"type":"boolean"},"matchesReaches":{"type":"boolean"},"matchesHighlight":{"type":"boolean"},"coreModule":{"type":"boolean"},"couldNotResolve":{"type":"boolean"},"dependencyTypes":{"type":"array","items":{"$ref":"#/definitions/DependencyTypeType"}},"license":{"type":"string"},"orphan":{"type":"boolean"},"reachable":{"type":"array","items":{"$ref":"#/definitions/ReachableType"}},"reaches":{"type":"array","items":{"$ref":"#/definitions/ReachesType"}},"rules":{"type":"array","items":{"$ref":"#/definitions/RuleSummaryType"}},"consolidated":{"type":"boolean"},"instability":{"type":"number"},"checksum":{"type":"string"}}},"ReachableType":{"type":"object","required":["value","asDefinedInRule","matchedFrom"],"additionalProperties":false,"properties":{"value":{"type":"boolean"},"asDefinedInRule":{"type":"string"},"matchedFrom":{"type":"string"}}},"ReachesType":{"type":"object","required":["modules","asDefinedInRule"],"additionalProperties":false,"properties":{"modules":{"type":"array","items":{"type":"object","required":["source","via"],"additionalProperties":false,"properties":{"source":{"type":"string"},"via":{"type":"array","items":{"type":"string"}}}}},"asDefinedInRule":{"type":"string"}}},"DependenciesType":{"type":"array","items":{"$ref":"#/definitions/DependencyType"}},"DependencyType":{"type":"object","required":["circular","coreModule","couldNotResolve","dependencyTypes","exoticallyRequired","dynamic","followable","module","moduleSystem","resolved","valid"],"additionalProperties":false,"properties":{"module":{"type":"string"},"protocol":{"type":"string","enum":["data:","file:","node:"]},"mimeType":{"type":"string"},"resolved":{"type":"string"},"coreModule":{"type":"boolean"},"dependencyTypes":{"type":"array","items":{"$ref":"#/definitions/DependencyTypeType"}},"license":{"type":"string"},"followable":{"type":"boolean"},"dynamic":{"type":"boolean"},"exoticallyRequired":{"type":"boolean"},"exoticRequire":{"type":"string"},"matchesDoNotFollow":{"type":"boolean"},"couldNotResolve":{"type":"boolean"},"preCompilationOnly":{"type":"boolean"},"typeOnly":{"type":"boolean"},"circular":{"type":"boolean"},"cycle":{"type":"array","items":{"$ref":"#/definitions/MiniDependency"}},"moduleSystem":{"$ref":"#/definitions/ModuleSystemType"},"valid":{"type":"boolean"},"rules":{"type":"array","items":{"$ref":"#/definitions/RuleSummaryType"}},"instability":{"type":"number"}}},"DependencyTypeType":{"type":"string","enum":["aliased-subpath-import","aliased-tsconfig-base-url","aliased-tsconfig-paths","aliased-tsconfig","aliased-webpack","aliased-workspace","aliased","amd-define","amd-require","amd-exotic-require","core","deprecated","dynamic-import","exotic-require","export","import-equals","import","local","localmodule","npm-bundled","npm-dev","npm-no-pkg","npm-optional","npm-peer","npm-unknown","npm","require","triple-slash-amd-dependency","triple-slash-directive","triple-slash-file-reference","triple-slash-type-reference","type-import","type-only","undetermined","unknown"]},"ModuleSystemType":{"type":"string","enum":["cjs","es6","amd","tsd"]},"RuleSummaryType":{"type":"object","required":["name","severity"],"additionalProperties":false,"properties":{"name":{"type":"string"},"severity":{"$ref":"#/definitions/SeverityType"}}},"SeverityType":{"type":"string","enum":["error","warn","info","ignore"]},"MiniDependency":{"type":"object","required":["name","dependencyTypes"],"additionalProperties":false,"properties":{"name":{"type":"string"},"dependencyTypes":{"type":"array","items":{"$ref":"#/definitions/DependencyTypeType"}}}},"FoldersType":{"type":"array","items":{"$ref":"#/definitions/FolderType"}},"FolderType":{"type":"object","required":["name","moduleCount"],"additionalProperties":false,"properties":{"name":{"type":"string"},"dependents":{"type":"array","items":{"type":"object","required":["name"],"additionalProperties":false,"properties":{"name":{"type":"string"}}}},"dependencies":{"type":"array","items":{"type":"object","required":["name","valid","circular"],"additionalProperties":false,"properties":{"name":{"type":"string"},"instability":{"type":"number"},"valid":{"type":"boolean"},"circular":{"type":"boolean"},"cycle":{"type":"array","items":{"$ref":"#/definitions/MiniDependency"}},"rules":{"type":"array","items":{"$ref":"#/definitions/RuleSummaryType"}}}}},"moduleCount":{"type":"number"},"afferentCouplings":{"type":"number"},"efferentCouplings":{"type":"number"},"instability":{"type":"number"}}},"SummaryType":{"type":"object","required":["violations","error","warn","info","totalCruised","optionsUsed"],"additionalProperties":false,"properties":{"violations":{"$ref":"#/definitions/ViolationsType"},"error":{"type":"number"},"warn":{"type":"number"},"info":{"type":"number"},"ignore":{"type":"number"},"totalCruised":{"type":"number"},"totalDependenciesCruised":{"type":"number"},"ruleSetUsed":{"$ref":"#/definitions/RuleSetType"},"optionsUsed":{"$ref":"#/definitions/OptionsUsedType"}}},"ViolationsType":{"type":"array","items":{"$ref":"#/definitions/ViolationType"}},"ViolationType":{"type":"object","required":["from","to","rule"],"additionalProperties":false,"properties":{"from":{"type":"string"},"to":{"type":"string"},"type":{"$ref":"#/definitions/ViolationTypeType"},"rule":{"$ref":"#/definitions/RuleSummaryType"},"cycle":{"type":"array","items":{"$ref":"#/definitions/MiniDependency"}},"via":{"type":"array","items":{"type":"string"}},"metrics":{"type":"object","required":["from","to"],"additionalProperties":false,"properties":{"from":{"type":"object","required":["instability"],"additionalProperties":false,"properties":{"instability":{"type":"number"}}},"to":{"type":"object","required":["instability"],"additionalProperties":false,"properties":{"instability":{"type":"number"}}}}},"comment":{"type":"string"}}},"ViolationTypeType":{"type":"string","enum":["dependency","module","reachability","cycle","instability","folder"]},"RuleSetType":{"type":"object","additionalProperties":false,"properties":{"forbidden":{"type":"array","items":{"$ref":"#/definitions/ForbiddenRuleType"}},"allowed":{"type":"array","items":{"$ref":"#/definitions/AllowedRuleType"}},"allowedSeverity":{"$ref":"#/definitions/SeverityType"},"required":{"type":"array","items":{"$ref":"#/definitions/RequiredRuleType"}}}},"AllowedRuleType":{"oneOf":[{"$ref":"#/definitions/RegularAllowedRuleType"},{"$ref":"#/definitions/ReachabilityAllowedRuleType"}]},"RegularAllowedRuleType":{"type":"object","required":["from","to"],"additionalProperties":false,"properties":{"comment":{"type":"string"},"scope":{"type":"string","enum":["module","folder"]},"from":{"$ref":"#/definitions/FromRestrictionType"},"to":{"$ref":"#/definitions/ToRestrictionType"}}},"ReachabilityAllowedRuleType":{"type":"object","required":["from","to"],"additionalProperties":false,"properties":{"comment":{"type":"string"},"scope":{"type":"string","enum":["module","folder"]},"from":{"$ref":"#/definitions/ReachabilityFromRestrictionType"},"to":{"$ref":"#/definitions/ReachabilityToRestrictionType"}}},"ForbiddenRuleType":{"oneOf":[{"$ref":"#/definitions/RegularForbiddenRuleType"},{"$ref":"#/definitions/ReachabilityForbiddenRuleType"},{"$ref":"#/definitions/DependentsForbiddenRuleType"}]},"RegularForbiddenRuleType":{"type":"object","required":["from","to"],"additionalProperties":false,"properties":{"name":{"type":"string"},"severity":{"$ref":"#/definitions/SeverityType"},"scope":{"type":"string","enum":["module","folder"]},"comment":{"type":"string"},"from":{"$ref":"#/definitions/FromRestrictionType"},"to":{"$ref":"#/definitions/ToRestrictionType"}}},"DependentsForbiddenRuleType":{"type":"object","required":["module","from"],"additionalProperties":false,"properties":{"name":{"type":"string"},"severity":{"$ref":"#/definitions/SeverityType"},"scope":{"type":"string","enum":["module","folder"]},"comment":{"type":"string"},"module":{"$ref":"#/definitions/DependentsModuleRestrictionType"},"from":{"$ref":"#/definitions/DependentsFromRestrictionType"}}},"ReachabilityForbiddenRuleType":{"type":"object","required":["from","to"],"additionalProperties":false,"properties":{"name":{"type":"string"},"severity":{"$ref":"#/definitions/SeverityType"},"scope":{"type":"string","enum":["module","folder"]},"comment":{"type":"string"},"from":{"$ref":"#/definitions/ReachabilityFromRestrictionType"},"to":{"$ref":"#/definitions/ReachabilityToRestrictionType"}}},"RequiredRuleType":{"type":"object","required":["module","to"],"additionalProperties":false,"properties":{"name":{"type":"string"},"severity":{"$ref":"#/definitions/SeverityType"},"scope":{"type":"string","enum":["module","folder"]},"comment":{"type":"string"},"module":{"$ref":"#/definitions/RequiredModuleRestrictionType"},"to":{"$ref":"#/definitions/RequiredToRestrictionType"}}},"FromRestrictionType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"},"orphan":{"type":"boolean"}}},"ReachabilityFromRestrictionType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"}}},"ToRestrictionType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"},"couldNotResolve":{"type":"boolean"},"circular":{"type":"boolean"},"dynamic":{"type":"boolean"},"exoticallyRequired":{"type":"boolean"},"exoticRequire":{"$ref":"#/definitions/REAsStringsType"},"exoticRequireNot":{"$ref":"#/definitions/REAsStringsType"},"preCompilationOnly":{"type":"boolean"},"dependencyTypes":{"type":"array","items":{"$ref":"#/definitions/DependencyTypeType"}},"dependencyTypesNot":{"type":"array","items":{"$ref":"#/definitions/DependencyTypeType"}},"moreThanOneDependencyType":{"type":"boolean"},"license":{"$ref":"#/definitions/REAsStringsType"},"licenseNot":{"$ref":"#/definitions/REAsStringsType"},"via":{"$ref":"#/definitions/REAsStringsType"},"viaOnly":{"$ref":"#/definitions/REAsStringsType"},"viaNot":{"$ref":"#/definitions/REAsStringsType"},"viaSomeNot":{"$ref":"#/definitions/REAsStringsType"},"moreUnstable":{"type":"boolean"}}},"DependentsModuleRestrictionType":{"required":[],"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"},"numberOfDependentsLessThan":{"type":"integer","minimum":0,"maximum":100},"numberOfDependentsMoreThan":{"type":"integer","minimum":0,"maximum":100}}},"DependentsFromRestrictionType":{"required":[],"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"}}},"ReachabilityToRestrictionType":{"required":["reachable"],"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"},"reachable":{"type":"boolean"}}},"RequiredModuleRestrictionType":{"required":[],"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"}}},"RequiredToRestrictionType":{"required":[],"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"}}},"REAsStringsType":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"OptionsUsedType":{"type":"object","additionalProperties":false,"properties":{"doNotFollow":{"$ref":"#/definitions/CompoundDoNotFollowType"},"exclude":{"$ref":"#/definitions/CompoundExcludeType"},"includeOnly":{"oneOf":[{"$ref":"#/definitions/REAsStringsType"},{"$ref":"#/definitions/CompoundIncludeOnlyType"}]},"focus":{"$ref":"#/definitions/CompoundFocusType"},"reaches":{"$ref":"#/definitions/CompoundReachesType"},"highlight":{"$ref":"#/definitions/CompoundHighlightType"},"knownViolations":{"$ref":"#/definitions/ViolationsType"},"collapse":{"type":"string"},"maxDepth":{"type":"integer","minimum":0,"maximum":99},"moduleSystems":{"$ref":"#/definitions/ModuleSystemsType"},"prefix":{"type":"string"},"preserveSymlinks":{"type":"boolean"},"combinedDependencies":{"type":"boolean"},"tsConfig":{"type":"object","additionalProperties":false,"properties":{"fileName":{"type":"string"}}},"tsPreCompilationDeps":{"oneOf":[{"type":"boolean"},{"type":"string","enum":["specify"]}]},"extraExtensionsToScan":{"type":"array","items":{"type":"string"}},"externalModuleResolutionStrategy":{"type":"string","enum":["node_modules","yarn-pnp"]},"builtInModules":{"type":"object","additionalProperties":false,"properties":{"override":{"type":"array","items":{"type":"string"}},"add":{"type":"array","items":{"type":"string"}}}},"forceDeriveDependents":{"type":"boolean"},"webpackConfig":{"type":"object","additionalProperties":false,"properties":{"fileName":{"type":"string"},"env":{"oneOf":[{"type":"object"},{"type":"string"}]},"arguments":{"type":"object"}}},"enhancedResolveOptions":{"type":"object","additionalProperties":false,"properties":{"exportsFields":{"type":"array","items":{"type":"string"}},"conditionNames":{"type":"array","items":{"type":"string"}},"extensions":{"type":"array","items":{"type":"string"}},"mainFields":{"type":"array","items":{"type":"string"}},"mainFiles":{"type":"array"},"aliasFields":{"type":"array","items":{"type":"string"}},"cachedInputFileSystem":{"type":"object","additionalProperties":false,"properties":{"cacheDuration":{"type":"integer","minimum":0,"maximum":1800000}}}}},"babelConfig":{"type":"object","additionalProperties":false,"properties":{"fileName":{"type":"string"}}},"parser":{"type":"string","enum":["acorn","swc","tsc"]},"exoticRequireStrings":{"type":"array","items":{"type":"string"}},"reporterOptions":{"$ref":"#/definitions/ReporterOptionsType"},"progress":{"type":"object","additionalProperties":false,"properties":{"type":{"type":"string","enum":["cli-feedback","performance-log","ndjson","none"]},"maximumLevel":{"type":"number","enum":[-1,40,50,60,70,80,99]}}},"metrics":{"type":"boolean"},"baseDir":{"type":"string"},"cache":{"oneOf":[{"type":"boolean","enum":[false]},{"$ref":"#/definitions/CacheOptionsType"}]},"args":{"type":"string"},"rulesFile":{"type":"string"},"outputTo":{"type":"string"},"outputType":{"$ref":"#/definitions/OutputType"}}},"ModuleSystemsType":{"type":"array","items":{"$ref":"#/definitions/ModuleSystemType"}},"OutputType":{"oneOf":[{"type":"string","enum":["json","html","dot","ddot","cdot","archi","fdot","flat","csv","err","err-long","err-html","teamcity","anon","text","metrics","markdown","mermaid","d2","null"]},{"type":"string","pattern":"^plugin:[^:]+$"}]},"CompoundExcludeType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"dynamic":{"type":"boolean"}}},"CompoundDoNotFollowType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"dependencyTypes":{"type":"array","items":{"$ref":"#/definitions/DependencyTypeType"}}}},"CompoundIncludeOnlyType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"}}},"CompoundFocusType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"depth":{"type":"number","minimum":1,"maximum":4}}},"CompoundReachesType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"}}},"CompoundHighlightType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"}}},"ReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"anon":{"$ref":"#/definitions/AnonReporterOptionsType"},"archi":{"$ref":"#/definitions/DotReporterOptionsType"},"dot":{"$ref":"#/definitions/DotReporterOptionsType"},"ddot":{"$ref":"#/definitions/DotReporterOptionsType"},"flat":{"$ref":"#/definitions/DotReporterOptionsType"},"markdown":{"$ref":"#/definitions/MarkdownReporterOptionsType"},"metrics":{"$ref":"#/definitions/MetricsReporterOptionsType"},"mermaid":{"$ref":"#/definitions/MermaidReporterOptionsType"},"text":{"$ref":"#/definitions/TextReporterOptionsType"}}},"AnonReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"wordlist":{"type":"array","items":{"type":"string"}}}},"MetricsReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"orderBy":{"type":"string","enum":["instability","moduleCount","afferentCouplings","efferentCouplings","name"]},"hideModules":{"type":"boolean"},"hideFolders":{"type":"boolean"}}},"MarkdownReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"showTitle":{"type":"boolean"},"title":{"type":"string"},"showSummary":{"type":"boolean"},"showSummaryHeader":{"type":"boolean"},"summaryHeader":{"type":"string"},"showStatsSummary":{"type":"boolean"},"showRulesSummary":{"type":"boolean"},"includeIgnoredInSummary":{"type":"boolean"},"showDetails":{"type":"boolean"},"includeIgnoredInDetails":{"type":"boolean"},"showDetailsHeader":{"type":"boolean"},"detailsHeader":{"type":"string"},"collapseDetails":{"type":"boolean"},"collapsedMessage":{"type":"string"},"noViolationsMessage":{"type":"string"},"showFooter":{"type":"boolean"}}},"MermaidReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"minify":{"type":"boolean"}}},"TextReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"highlightFocused":{"type":"boolean"}}},"DotReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"collapsePattern":{"$ref":"#/definitions/REAsStringsType"},"filters":{"$ref":"#/definitions/ReporterFiltersType"},"showMetrics":{"type":"boolean"},"theme":{"$ref":"#/definitions/DotThemeType"}}},"DotThemeType":{"type":"object","additionalProperties":false,"properties":{"replace":{"type":"boolean"},"graph":{"type":"object"},"node":{"type":"object"},"edge":{"type":"object"},"modules":{"$ref":"#/definitions/DotThemeArrayType"},"dependencies":{"$ref":"#/definitions/DotThemeArrayType"}}},"DotThemeArrayType":{"type":"array","items":{"$ref":"#/definitions/DotThemeEntryType"}},"DotThemeEntryType":{"type":"object","additionalProperties":false,"properties":{"criteria":{"type":"object"},"attributes":{"type":"object"}}},"ReporterFiltersType":{"type":"object","additionalProperties":false,"properties":{"exclude":{"$ref":"#/definitions/CompoundExcludeType"},"includeOnly":{"$ref":"#/definitions/CompoundIncludeOnlyType"},"focus":{"$ref":"#/definitions/CompoundFocusType"},"reaches":{"$ref":"#/definitions/CompoundReachesType"}}},"CacheOptionsType":{"type":"object","additionalProperties":false,"properties":{"folder":{"type":"string"},"strategy":{"$ref":"#/definitions/CacheStrategyType"},"compress":{"type":"boolean","default":false}}},"CacheStrategyType":{"type":"string","enum":["metadata","content"]},"RevisionDataType":{"type":"object","required":["SHA1","changes"],"properties":{"cacheFormatVersion":{"type":"number"},"SHA1":{"type":"string"},"changes":{"type":"array","items":{"type":"object","required":["name","changeType"],"properties":{"name":{"type":"string"},"changeType":{"type":"string","enum":["added","copied","deleted","modified","renamed","type changed","unmerged","pairing broken","unknown","unmodified","untracked","ignored"]},"oldName":{"type":"string"},"checksum":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"rulesFile":{"type":"string"}}}}}}}} \ No newline at end of file +/* generated - don't edit */export default {"title":"dependency-cruiser output format","$schema":"http://json-schema.org/draft-07/schema#","$id":"https://dependency-cruiser.js.org/schema/cruise-result.schema.json","type":"object","required":["summary","modules"],"additionalProperties":false,"properties":{"modules":{"$ref":"#/definitions/ModulesType"},"folders":{"$ref":"#/definitions/FoldersType"},"summary":{"$ref":"#/definitions/SummaryType"},"revisionData":{"$ref":"#/definitions/RevisionDataType"}},"definitions":{"ModulesType":{"type":"array","items":{"$ref":"#/definitions/ModuleType"}},"ModuleType":{"type":"object","required":["source","dependencies","valid"],"additionalProperties":false,"properties":{"source":{"type":"string"},"valid":{"type":"boolean"},"dependencies":{"$ref":"#/definitions/DependenciesType"},"dependents":{"type":"array","items":{"type":"string"}},"followable":{"type":"boolean"},"matchesDoNotFollow":{"type":"boolean"},"matchesFocus":{"type":"boolean"},"matchesReaches":{"type":"boolean"},"matchesHighlight":{"type":"boolean"},"coreModule":{"type":"boolean"},"couldNotResolve":{"type":"boolean"},"dependencyTypes":{"type":"array","items":{"$ref":"#/definitions/DependencyTypeType"}},"license":{"type":"string"},"orphan":{"type":"boolean"},"reachable":{"type":"array","items":{"$ref":"#/definitions/ReachableType"}},"reaches":{"type":"array","items":{"$ref":"#/definitions/ReachesType"}},"rules":{"type":"array","items":{"$ref":"#/definitions/RuleSummaryType"}},"consolidated":{"type":"boolean"},"instability":{"type":"number"},"checksum":{"type":"string"}}},"ReachableType":{"type":"object","required":["value","asDefinedInRule","matchedFrom"],"additionalProperties":false,"properties":{"value":{"type":"boolean"},"asDefinedInRule":{"type":"string"},"matchedFrom":{"type":"string"}}},"ReachesType":{"type":"object","required":["modules","asDefinedInRule"],"additionalProperties":false,"properties":{"modules":{"type":"array","items":{"type":"object","required":["source","via"],"additionalProperties":false,"properties":{"source":{"type":"string"},"via":{"type":"array","items":{"type":"string"}}}}},"asDefinedInRule":{"type":"string"}}},"DependenciesType":{"type":"array","items":{"$ref":"#/definitions/DependencyType"}},"DependencyType":{"type":"object","required":["circular","coreModule","couldNotResolve","dependencyTypes","exoticallyRequired","dynamic","followable","module","moduleSystem","resolved","valid"],"additionalProperties":false,"properties":{"module":{"type":"string"},"protocol":{"type":"string","enum":["data:","file:","node:"]},"mimeType":{"type":"string"},"resolved":{"type":"string"},"coreModule":{"type":"boolean"},"dependencyTypes":{"type":"array","items":{"$ref":"#/definitions/DependencyTypeType"}},"license":{"type":"string"},"followable":{"type":"boolean"},"dynamic":{"type":"boolean"},"exoticallyRequired":{"type":"boolean"},"exoticRequire":{"type":"string"},"matchesDoNotFollow":{"type":"boolean"},"couldNotResolve":{"type":"boolean"},"preCompilationOnly":{"type":"boolean"},"typeOnly":{"type":"boolean"},"circular":{"type":"boolean"},"cycle":{"type":"array","items":{"$ref":"#/definitions/MiniDependency"}},"moduleSystem":{"$ref":"#/definitions/ModuleSystemType"},"valid":{"type":"boolean"},"rules":{"type":"array","items":{"$ref":"#/definitions/RuleSummaryType"}},"instability":{"type":"number"}}},"DependencyTypeType":{"type":"string","enum":["aliased-subpath-import","aliased-tsconfig-base-url","aliased-tsconfig-paths","aliased-tsconfig","aliased-webpack","aliased-workspace","aliased","amd-define","amd-require","amd-exotic-require","core","deprecated","dynamic-import","exotic-require","export","import-equals","import","local","localmodule","npm-bundled","npm-dev","npm-no-pkg","npm-optional","npm-peer","npm-unknown","npm","pre-compilation-only","require","triple-slash-amd-dependency","triple-slash-directive","triple-slash-file-reference","triple-slash-type-reference","type-import","type-only","undetermined","unknown"]},"ModuleSystemType":{"type":"string","enum":["cjs","es6","amd","tsd"]},"RuleSummaryType":{"type":"object","required":["name","severity"],"additionalProperties":false,"properties":{"name":{"type":"string"},"severity":{"$ref":"#/definitions/SeverityType"}}},"SeverityType":{"type":"string","enum":["error","warn","info","ignore"]},"MiniDependency":{"type":"object","required":["name","dependencyTypes"],"additionalProperties":false,"properties":{"name":{"type":"string"},"dependencyTypes":{"type":"array","items":{"$ref":"#/definitions/DependencyTypeType"}}}},"FoldersType":{"type":"array","items":{"$ref":"#/definitions/FolderType"}},"FolderType":{"type":"object","required":["name","moduleCount"],"additionalProperties":false,"properties":{"name":{"type":"string"},"dependents":{"type":"array","items":{"type":"object","required":["name"],"additionalProperties":false,"properties":{"name":{"type":"string"}}}},"dependencies":{"type":"array","items":{"type":"object","required":["name","valid","circular"],"additionalProperties":false,"properties":{"name":{"type":"string"},"instability":{"type":"number"},"valid":{"type":"boolean"},"circular":{"type":"boolean"},"cycle":{"type":"array","items":{"$ref":"#/definitions/MiniDependency"}},"rules":{"type":"array","items":{"$ref":"#/definitions/RuleSummaryType"}}}}},"moduleCount":{"type":"number"},"afferentCouplings":{"type":"number"},"efferentCouplings":{"type":"number"},"instability":{"type":"number"}}},"SummaryType":{"type":"object","required":["violations","error","warn","info","totalCruised","optionsUsed"],"additionalProperties":false,"properties":{"violations":{"$ref":"#/definitions/ViolationsType"},"error":{"type":"number"},"warn":{"type":"number"},"info":{"type":"number"},"ignore":{"type":"number"},"totalCruised":{"type":"number"},"totalDependenciesCruised":{"type":"number"},"ruleSetUsed":{"$ref":"#/definitions/RuleSetType"},"optionsUsed":{"$ref":"#/definitions/OptionsUsedType"}}},"ViolationsType":{"type":"array","items":{"$ref":"#/definitions/ViolationType"}},"ViolationType":{"type":"object","required":["from","to","rule"],"additionalProperties":false,"properties":{"from":{"type":"string"},"to":{"type":"string"},"type":{"$ref":"#/definitions/ViolationTypeType"},"rule":{"$ref":"#/definitions/RuleSummaryType"},"cycle":{"type":"array","items":{"$ref":"#/definitions/MiniDependency"}},"via":{"type":"array","items":{"type":"string"}},"metrics":{"type":"object","required":["from","to"],"additionalProperties":false,"properties":{"from":{"type":"object","required":["instability"],"additionalProperties":false,"properties":{"instability":{"type":"number"}}},"to":{"type":"object","required":["instability"],"additionalProperties":false,"properties":{"instability":{"type":"number"}}}}},"comment":{"type":"string"}}},"ViolationTypeType":{"type":"string","enum":["dependency","module","reachability","cycle","instability","folder"]},"RuleSetType":{"type":"object","additionalProperties":false,"properties":{"forbidden":{"type":"array","items":{"$ref":"#/definitions/ForbiddenRuleType"}},"allowed":{"type":"array","items":{"$ref":"#/definitions/AllowedRuleType"}},"allowedSeverity":{"$ref":"#/definitions/SeverityType"},"required":{"type":"array","items":{"$ref":"#/definitions/RequiredRuleType"}}}},"AllowedRuleType":{"oneOf":[{"$ref":"#/definitions/RegularAllowedRuleType"},{"$ref":"#/definitions/ReachabilityAllowedRuleType"}]},"RegularAllowedRuleType":{"type":"object","required":["from","to"],"additionalProperties":false,"properties":{"comment":{"type":"string"},"scope":{"type":"string","enum":["module","folder"]},"from":{"$ref":"#/definitions/FromRestrictionType"},"to":{"$ref":"#/definitions/ToRestrictionType"}}},"ReachabilityAllowedRuleType":{"type":"object","required":["from","to"],"additionalProperties":false,"properties":{"comment":{"type":"string"},"scope":{"type":"string","enum":["module","folder"]},"from":{"$ref":"#/definitions/ReachabilityFromRestrictionType"},"to":{"$ref":"#/definitions/ReachabilityToRestrictionType"}}},"ForbiddenRuleType":{"oneOf":[{"$ref":"#/definitions/RegularForbiddenRuleType"},{"$ref":"#/definitions/ReachabilityForbiddenRuleType"},{"$ref":"#/definitions/DependentsForbiddenRuleType"}]},"RegularForbiddenRuleType":{"type":"object","required":["from","to"],"additionalProperties":false,"properties":{"name":{"type":"string"},"severity":{"$ref":"#/definitions/SeverityType"},"scope":{"type":"string","enum":["module","folder"]},"comment":{"type":"string"},"from":{"$ref":"#/definitions/FromRestrictionType"},"to":{"$ref":"#/definitions/ToRestrictionType"}}},"DependentsForbiddenRuleType":{"type":"object","required":["module","from"],"additionalProperties":false,"properties":{"name":{"type":"string"},"severity":{"$ref":"#/definitions/SeverityType"},"scope":{"type":"string","enum":["module","folder"]},"comment":{"type":"string"},"module":{"$ref":"#/definitions/DependentsModuleRestrictionType"},"from":{"$ref":"#/definitions/DependentsFromRestrictionType"}}},"ReachabilityForbiddenRuleType":{"type":"object","required":["from","to"],"additionalProperties":false,"properties":{"name":{"type":"string"},"severity":{"$ref":"#/definitions/SeverityType"},"scope":{"type":"string","enum":["module","folder"]},"comment":{"type":"string"},"from":{"$ref":"#/definitions/ReachabilityFromRestrictionType"},"to":{"$ref":"#/definitions/ReachabilityToRestrictionType"}}},"RequiredRuleType":{"type":"object","required":["module","to"],"additionalProperties":false,"properties":{"name":{"type":"string"},"severity":{"$ref":"#/definitions/SeverityType"},"scope":{"type":"string","enum":["module","folder"]},"comment":{"type":"string"},"module":{"$ref":"#/definitions/RequiredModuleRestrictionType"},"to":{"$ref":"#/definitions/RequiredToRestrictionType"}}},"MiniDependencyRestrictionType":{"oneOf":[{"$ref":"#/definitions/REAsStringsType"},{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"},"dependencyTypes":{"type":"array","items":{"$ref":"#/definitions/DependencyTypeType"}},"dependencyTypesNot":{"type":"array","items":{"$ref":"#/definitions/DependencyTypeType"}}}}]},"FromRestrictionType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"},"orphan":{"type":"boolean"}}},"ReachabilityFromRestrictionType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"}}},"ToRestrictionType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"},"couldNotResolve":{"type":"boolean"},"circular":{"type":"boolean"},"dynamic":{"type":"boolean"},"exoticallyRequired":{"type":"boolean"},"exoticRequire":{"$ref":"#/definitions/REAsStringsType"},"exoticRequireNot":{"$ref":"#/definitions/REAsStringsType"},"preCompilationOnly":{"type":"boolean"},"dependencyTypes":{"type":"array","items":{"$ref":"#/definitions/DependencyTypeType"}},"dependencyTypesNot":{"type":"array","items":{"$ref":"#/definitions/DependencyTypeType"}},"moreThanOneDependencyType":{"type":"boolean"},"license":{"$ref":"#/definitions/REAsStringsType"},"licenseNot":{"$ref":"#/definitions/REAsStringsType"},"via":{"$ref":"#/definitions/MiniDependencyRestrictionType"},"viaOnly":{"$ref":"#/definitions/MiniDependencyRestrictionType"},"viaNot":{"deprecated":true,"$ref":"#/definitions/REAsStringsType"},"viaSomeNot":{"deprecated":true,"$ref":"#/definitions/REAsStringsType"},"moreUnstable":{"type":"boolean"}}},"DependentsModuleRestrictionType":{"required":[],"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"},"numberOfDependentsLessThan":{"type":"integer","minimum":0,"maximum":100},"numberOfDependentsMoreThan":{"type":"integer","minimum":0,"maximum":100}}},"DependentsFromRestrictionType":{"required":[],"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"}}},"ReachabilityToRestrictionType":{"required":["reachable"],"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"},"reachable":{"type":"boolean"}}},"RequiredModuleRestrictionType":{"required":[],"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"pathNot":{"$ref":"#/definitions/REAsStringsType"}}},"RequiredToRestrictionType":{"required":[],"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"}}},"REAsStringsType":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"OptionsUsedType":{"type":"object","additionalProperties":false,"properties":{"doNotFollow":{"$ref":"#/definitions/CompoundDoNotFollowType"},"exclude":{"$ref":"#/definitions/CompoundExcludeType"},"includeOnly":{"oneOf":[{"$ref":"#/definitions/REAsStringsType"},{"$ref":"#/definitions/CompoundIncludeOnlyType"}]},"focus":{"$ref":"#/definitions/CompoundFocusType"},"reaches":{"$ref":"#/definitions/CompoundReachesType"},"highlight":{"$ref":"#/definitions/CompoundHighlightType"},"knownViolations":{"$ref":"#/definitions/ViolationsType"},"collapse":{"type":"string"},"maxDepth":{"type":"integer","minimum":0,"maximum":99},"moduleSystems":{"$ref":"#/definitions/ModuleSystemsType"},"prefix":{"type":"string"},"preserveSymlinks":{"type":"boolean"},"combinedDependencies":{"type":"boolean"},"tsConfig":{"type":"object","additionalProperties":false,"properties":{"fileName":{"type":"string"}}},"tsPreCompilationDeps":{"oneOf":[{"type":"boolean"},{"type":"string","enum":["specify"]}]},"extraExtensionsToScan":{"type":"array","items":{"type":"string"}},"externalModuleResolutionStrategy":{"type":"string","enum":["node_modules","yarn-pnp"]},"builtInModules":{"type":"object","additionalProperties":false,"properties":{"override":{"type":"array","items":{"type":"string"}},"add":{"type":"array","items":{"type":"string"}}}},"forceDeriveDependents":{"type":"boolean"},"webpackConfig":{"type":"object","additionalProperties":false,"properties":{"fileName":{"type":"string"},"env":{"oneOf":[{"type":"object"},{"type":"string"}]},"arguments":{"type":"object"}}},"enhancedResolveOptions":{"type":"object","additionalProperties":false,"properties":{"exportsFields":{"type":"array","items":{"type":"string"}},"conditionNames":{"type":"array","items":{"type":"string"}},"extensions":{"type":"array","items":{"type":"string"}},"mainFields":{"type":"array","items":{"type":"string"}},"mainFiles":{"type":"array"},"aliasFields":{"type":"array","items":{"type":"string"}},"cachedInputFileSystem":{"type":"object","additionalProperties":false,"properties":{"cacheDuration":{"type":"integer","minimum":0,"maximum":1800000}}}}},"babelConfig":{"type":"object","additionalProperties":false,"properties":{"fileName":{"type":"string"}}},"parser":{"type":"string","enum":["acorn","swc","tsc"]},"exoticRequireStrings":{"type":"array","items":{"type":"string"}},"reporterOptions":{"$ref":"#/definitions/ReporterOptionsType"},"progress":{"type":"object","additionalProperties":false,"properties":{"type":{"type":"string","enum":["cli-feedback","performance-log","ndjson","none"]},"maximumLevel":{"type":"number","enum":[-1,40,50,60,70,80,99]}}},"metrics":{"type":"boolean"},"baseDir":{"type":"string"},"cache":{"oneOf":[{"type":"boolean","enum":[false]},{"$ref":"#/definitions/CacheOptionsType"}]},"args":{"type":"string"},"rulesFile":{"type":"string"},"outputTo":{"type":"string"},"outputType":{"$ref":"#/definitions/OutputType"}}},"ModuleSystemsType":{"type":"array","items":{"$ref":"#/definitions/ModuleSystemType"}},"OutputType":{"oneOf":[{"type":"string","enum":["json","html","dot","ddot","cdot","archi","fdot","flat","csv","err","err-long","err-html","teamcity","anon","text","metrics","markdown","mermaid","d2","null"]},{"type":"string","pattern":"^plugin:[^:]+$"}]},"CompoundExcludeType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"dynamic":{"type":"boolean"}}},"CompoundDoNotFollowType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"dependencyTypes":{"type":"array","items":{"$ref":"#/definitions/DependencyTypeType"}}}},"CompoundIncludeOnlyType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"}}},"CompoundFocusType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"},"depth":{"type":"number","minimum":1,"maximum":4}}},"CompoundReachesType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"}}},"CompoundHighlightType":{"type":"object","additionalProperties":false,"properties":{"path":{"$ref":"#/definitions/REAsStringsType"}}},"ReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"anon":{"$ref":"#/definitions/AnonReporterOptionsType"},"archi":{"$ref":"#/definitions/DotReporterOptionsType"},"dot":{"$ref":"#/definitions/DotReporterOptionsType"},"ddot":{"$ref":"#/definitions/DotReporterOptionsType"},"flat":{"$ref":"#/definitions/DotReporterOptionsType"},"markdown":{"$ref":"#/definitions/MarkdownReporterOptionsType"},"metrics":{"$ref":"#/definitions/MetricsReporterOptionsType"},"mermaid":{"$ref":"#/definitions/MermaidReporterOptionsType"},"text":{"$ref":"#/definitions/TextReporterOptionsType"}}},"AnonReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"wordlist":{"type":"array","items":{"type":"string"}}}},"MetricsReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"orderBy":{"type":"string","enum":["instability","moduleCount","afferentCouplings","efferentCouplings","name"]},"hideModules":{"type":"boolean"},"hideFolders":{"type":"boolean"}}},"MarkdownReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"showTitle":{"type":"boolean"},"title":{"type":"string"},"showSummary":{"type":"boolean"},"showSummaryHeader":{"type":"boolean"},"summaryHeader":{"type":"string"},"showStatsSummary":{"type":"boolean"},"showRulesSummary":{"type":"boolean"},"includeIgnoredInSummary":{"type":"boolean"},"showDetails":{"type":"boolean"},"includeIgnoredInDetails":{"type":"boolean"},"showDetailsHeader":{"type":"boolean"},"detailsHeader":{"type":"string"},"collapseDetails":{"type":"boolean"},"collapsedMessage":{"type":"string"},"noViolationsMessage":{"type":"string"},"showFooter":{"type":"boolean"}}},"MermaidReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"minify":{"type":"boolean"}}},"TextReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"highlightFocused":{"type":"boolean"}}},"DotReporterOptionsType":{"type":"object","additionalProperties":false,"properties":{"collapsePattern":{"$ref":"#/definitions/REAsStringsType"},"filters":{"$ref":"#/definitions/ReporterFiltersType"},"showMetrics":{"type":"boolean"},"theme":{"$ref":"#/definitions/DotThemeType"}}},"DotThemeType":{"type":"object","additionalProperties":false,"properties":{"replace":{"type":"boolean"},"graph":{"type":"object"},"node":{"type":"object"},"edge":{"type":"object"},"modules":{"$ref":"#/definitions/DotThemeArrayType"},"dependencies":{"$ref":"#/definitions/DotThemeArrayType"}}},"DotThemeArrayType":{"type":"array","items":{"$ref":"#/definitions/DotThemeEntryType"}},"DotThemeEntryType":{"type":"object","additionalProperties":false,"properties":{"criteria":{"type":"object"},"attributes":{"type":"object"}}},"ReporterFiltersType":{"type":"object","additionalProperties":false,"properties":{"exclude":{"$ref":"#/definitions/CompoundExcludeType"},"includeOnly":{"$ref":"#/definitions/CompoundIncludeOnlyType"},"focus":{"$ref":"#/definitions/CompoundFocusType"},"reaches":{"$ref":"#/definitions/CompoundReachesType"}}},"CacheOptionsType":{"type":"object","additionalProperties":false,"properties":{"folder":{"type":"string"},"strategy":{"$ref":"#/definitions/CacheStrategyType"},"compress":{"type":"boolean","default":false}}},"CacheStrategyType":{"type":"string","enum":["metadata","content"]},"RevisionDataType":{"type":"object","required":["SHA1","changes"],"properties":{"cacheFormatVersion":{"type":"number"},"SHA1":{"type":"string"},"changes":{"type":"array","items":{"type":"object","required":["name","changeType"],"properties":{"name":{"type":"string"},"changeType":{"type":"string","enum":["added","copied","deleted","modified","renamed","type changed","unmerged","pairing broken","unknown","unmodified","untracked","ignored"]},"oldName":{"type":"string"},"checksum":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"rulesFile":{"type":"string"}}}}}}}} \ No newline at end of file diff --git a/src/validate/match-dependency-rule.mjs b/src/validate/match-dependency-rule.mjs index f314b2ba7..996fd9f35 100644 --- a/src/validate/match-dependency-rule.mjs +++ b/src/validate/match-dependency-rule.mjs @@ -1,11 +1,13 @@ +// @ts-check import { isModuleOnlyRule, isFolderScope } from "./rule-classifiers.mjs"; import matchers from "./matchers.mjs"; +// @ts-expect-error ts(2307) - apparently tsc doesn't understand subpath imports yet import { extractGroups } from "#utl/regex-util.mjs"; /** * - * @param {import("../../types/dependency-cruiser.js").IModule} pFrom - * @param {import("../../types/dependency-cruiser.js").IDependency} pTo + * @param {import("../../types/dependency-cruiser.mjs").IModule} pFrom + * @param {import("../../types/dependency-cruiser.mjs").IDependency} pTo * @returns {(pRule) => boolean} */ function match(pFrom, pTo) { @@ -41,15 +43,13 @@ function match(pFrom, pTo) { ) && matchers.toVia(pRule, pTo, lGroups) && matchers.toViaOnly(pRule, pTo, lGroups) && - matchers.toViaNot(pRule, pTo, lGroups) && - matchers.toviaSomeNot(pRule, pTo, lGroups) && matchers.toIsMoreUnstable(pRule, pFrom, pTo) ); }; } + /** - * - * @param {any} pRule + * @param {import("../../types/rule-set.d.mts").IAnyRuleType} pRule * @returns boolean */ const isInteresting = (pRule) => diff --git a/src/validate/matchers.mjs b/src/validate/matchers.mjs index d362656d3..c00bb3424 100644 --- a/src/validate/matchers.mjs +++ b/src/validate/matchers.mjs @@ -107,55 +107,69 @@ function toDependencyTypesNot(pRule, pDependency) { !intersects(pDependency.dependencyTypes, pRule.to.dependencyTypesNot), ); } -function pluckName({ name }) { - return name; -} + +// eslint-disable-next-line complexity function toVia(pRule, pDependency, pGroups) { - return Boolean( - !pRule.to.via || - (pDependency.cycle && - pDependency.cycle - .map(pluckName) - .some((pVia) => - pVia.match(replaceGroupPlaceholders(pRule.to.via, pGroups)), - )), - ); + let lReturnValue = true; + if (pRule.to.via && pDependency.cycle) { + if (pRule.to.via.path) { + lReturnValue = pDependency.cycle.some(({ name }) => + name.match(replaceGroupPlaceholders(pRule.to.via.path, pGroups)), + ); + } + if (pRule.to.via.pathNot) { + lReturnValue = !pDependency.cycle.every(({ name }) => + name.match(replaceGroupPlaceholders(pRule.to.via.pathNot, pGroups)), + ); + } + if (pRule.to.via.dependencyTypes) { + lReturnValue &&= pDependency.cycle.some(({ dependencyTypes }) => + pRule.to.via.dependencyTypes.some((pRuleDependencyType) => + dependencyTypes.includes(pRuleDependencyType), + ), + ); + } + if (pRule.to.via.dependencyTypesNot) { + lReturnValue &&= !pDependency.cycle.every(({ dependencyTypes }) => + pRule.to.via.dependencyTypesNot.some((pRuleDependencyType) => + dependencyTypes.includes(pRuleDependencyType), + ), + ); + } + } + return lReturnValue; } +// eslint-disable-next-line complexity function toViaOnly(pRule, pDependency, pGroups) { - return Boolean( - !pRule.to.viaOnly || - (pDependency.cycle && - pDependency.cycle - .map(pluckName) - .every((pVia) => - pVia.match(replaceGroupPlaceholders(pRule.to.viaOnly, pGroups)), - )), - ); -} - -function toViaNot(pRule, pDependency, pGroups) { - return Boolean( - !pRule.to.viaNot || - (pDependency.cycle && - !pDependency.cycle - .map(pluckName) - .some((pVia) => - pVia.match(replaceGroupPlaceholders(pRule.to.viaNot, pGroups)), - )), - ); -} - -function toviaSomeNot(pRule, pDependency, pGroups) { - return Boolean( - !pRule.to.viaSomeNot || - (pDependency.cycle && - !pDependency.cycle - .map(pluckName) - .every((pVia) => - pVia.match(replaceGroupPlaceholders(pRule.to.viaSomeNot, pGroups)), - )), - ); + let lReturnValue = true; + if (pRule.to.viaOnly && pDependency.cycle) { + if (pRule.to.viaOnly.path) { + lReturnValue = pDependency.cycle.every(({ name }) => + name.match(replaceGroupPlaceholders(pRule.to.viaOnly.path, pGroups)), + ); + } + if (pRule.to.viaOnly.pathNot) { + lReturnValue = !pDependency.cycle.some(({ name }) => + name.match(replaceGroupPlaceholders(pRule.to.viaOnly.pathNot, pGroups)), + ); + } + if (pRule.to.viaOnly.dependencyTypes) { + lReturnValue &&= pDependency.cycle.every(({ dependencyTypes }) => + pRule.to.viaOnly.dependencyTypes.some((pRuleDependencyType) => + dependencyTypes.includes(pRuleDependencyType), + ), + ); + } + if (pRule.to.viaOnly.dependencyTypesNot) { + lReturnValue &&= !pDependency.cycle.some(({ dependencyTypes }) => + pRule.to.viaOnly.dependencyTypesNot.some((pRuleDependencyType) => + dependencyTypes.includes(pRuleDependencyType), + ), + ); + } + } + return lReturnValue; } function toIsMoreUnstable(pRule, pModule, pDependency) { @@ -213,8 +227,6 @@ export default { toDependencyTypesNot, toVia, toViaOnly, - toViaNot, - toviaSomeNot, toIsMoreUnstable, matchesMoreThanOneDependencyType, }; diff --git a/test/main/rule-set/assert-validity.spec.mjs b/test/main/rule-set/assert-validity.spec.mjs index cf73fd4a2..ab81f65ee 100644 --- a/test/main/rule-set/assert-validity.spec.mjs +++ b/test/main/rule-set/assert-validity.spec.mjs @@ -88,6 +88,13 @@ describe("[I] main/rule-set/validate - regexp safety checks", () => { ); }); + it("bails out on scary regexps in via", () => { + shouldBarfWithMessage( + "./test/validate/__mocks__/rules.scary-regex-in-via.json", + 'rule {"from":{},"to":{"via":{"path":["(.+)*","this-is-ok-1","this-is-ok-2"]}}} has an unsafe regular expression. Bailing out.\n', + ); + }); + it("bails out on scary regexps in licenses", () => { shouldBarfWithMessage( "./test/validate/__mocks__/rules.scary-regex-in-license.json", diff --git a/test/main/rule-set/normalize.spec.mjs b/test/main/rule-set/normalize.spec.mjs index d70c175e4..8821b31ca 100644 --- a/test/main/rule-set/normalize.spec.mjs +++ b/test/main/rule-set/normalize.spec.mjs @@ -390,4 +390,56 @@ describe("[U] main/rule-set/normalize", () => { }, ); }); + + it("normalizes arrays of re's in via like things to regular regular expressions", () => { + deepEqual( + normalize({ + forbidden: [ + { + name: "exotic-require-thing", + severity: "warn", + from: {}, + to: { + via: ["aap", "noot", "mies"], + }, + }, + ], + allowed: [ + { + from: {}, + to: { + viaSomeNot: ["wim", "zus", "jet"], + }, + }, + ], + }), + { + forbidden: [ + { + scope: "module", + name: "exotic-require-thing", + severity: "warn", + from: {}, + to: { + via: { + path: "aap|noot|mies", + }, + }, + }, + ], + allowed: [ + { + name: "not-in-allowed", + from: {}, + to: { + via: { + pathNot: "wim|zus|jet", + }, + }, + }, + ], + allowedSeverity: "warn", + }, + ); + }); }); diff --git a/test/validate/__mocks__/rules.scary-regex-in-via.json b/test/validate/__mocks__/rules.scary-regex-in-via.json new file mode 100644 index 000000000..369c1d8d6 --- /dev/null +++ b/test/validate/__mocks__/rules.scary-regex-in-via.json @@ -0,0 +1,12 @@ +{ + "forbidden": [ + { + "from": {}, + "to": { + "via": { + "path": ["(.+)*", "this-is-ok-1", "this-is-ok-2"] + } + } + } + ] +} diff --git a/test/validate/index.cycle-via-not.spec.mjs b/test/validate/index.cycle-via-not.spec.mjs deleted file mode 100644 index 6edc4d6cf..000000000 --- a/test/validate/index.cycle-via-not.spec.mjs +++ /dev/null @@ -1,140 +0,0 @@ -import { deepEqual } from "node:assert/strict"; -import parseRuleSet from "./parse-ruleset.utl.mjs"; -import validate from "#validate/index.mjs"; - -function stringToCycleEntry(pString) { - return { - name: pString, - dependencyTypes: [], - }; -} - -describe("[I] validate/index dependency - cycle viaNot", () => { - const lCycleViaNotRuleSet = parseRuleSet({ - forbidden: [ - { - from: {}, - to: { - circular: true, - viaNot: "^tmp/ab\\.js$", - }, - }, - ], - }); - it("a => ba => bb => bc => a get flagged when none of them is in a viaNot", () => { - deepEqual( - validate.dependency( - lCycleViaNotRuleSet, - { source: "tmp/a.js" }, - { - resolved: "tmp/ba.js", - circular: true, - cycle: ["tmp/ba.js", "tmp/bb.js", "tmp/bc.js", "tmp/a.js"].map( - stringToCycleEntry, - ), - }, - ), - { - valid: false, - rules: [{ name: "unnamed", severity: "warn" }], - }, - ); - }); - - it("a => aa => ab => ac => a doesn't get flagged when one of them is in a viaNot", () => { - deepEqual( - validate.dependency( - lCycleViaNotRuleSet, - { source: "tmp/a.js" }, - { - resolved: "tmp/aa.js", - circular: true, - cycle: ["tmp/aa.js", "tmp/ab.js", "tmp/ac.js", "tmp/a.js"].map( - stringToCycleEntry, - ), - }, - ), - { - valid: true, - }, - ); - }); -}); - -describe("[I] validate/index dependency - cycle viaNot - with group matching", () => { - const lCycleButNotViaGroupMatchRuleSet = { - forbidden: [ - { - name: "no-circular-dependency-of-modules", - from: { path: "^src/([^/]+)/.+" }, - to: { viaNot: "^src/$1/.+", circular: true }, - }, - ], - }; - - it("flags when all of the cycle (except the root) is outside the group-matched viaNot", () => { - deepEqual( - validate.dependency( - parseRuleSet(lCycleButNotViaGroupMatchRuleSet), - { source: "src/module-a/a.js" }, - { - resolved: "src/module-b/ba.js", - circular: true, - cycle: [ - "src/module-b/ba.js", - "src/module-b/bb.js", - "src/module-b/bc.js", - "src/module-a/a.js", - ].map(stringToCycleEntry), - }, - ), - { - valid: true, - }, - ); - }); - - it("does not flag when only one of the cycle is outside the group-matched viaNot", () => { - deepEqual( - validate.dependency( - parseRuleSet(lCycleButNotViaGroupMatchRuleSet), - { source: "src/module-a/a.js" }, - { - resolved: "src/module-a/aa.js", - circular: true, - cycle: [ - "src/module-a/aa.js", - "src/module-a/ab.js", - "src/module-b/bc.js", - "src/module-a/a.js", - ].map(stringToCycleEntry), - }, - ), - { - valid: true, - }, - ); - }); - - it("does not flag when all of the cycle is inside the group-matched viaNot", () => { - deepEqual( - validate.dependency( - parseRuleSet(lCycleButNotViaGroupMatchRuleSet), - { source: "src/module-a/a.js" }, - { - resolved: "src/module-a/aa.js", - circular: true, - cycle: [ - "src/module-a/aa.js", - "src/module-a/ab.js", - "src/module-a/ac.js", - "src/module-a/a.js", - ].map(stringToCycleEntry), - }, - ), - { - valid: true, - }, - ); - }); -}); diff --git a/test/validate/index.cycle-via-only.spec.mjs b/test/validate/index.cycle-via-only.spec.mjs new file mode 100644 index 000000000..9725d0d90 --- /dev/null +++ b/test/validate/index.cycle-via-only.spec.mjs @@ -0,0 +1,422 @@ +import { deepEqual } from "node:assert/strict"; +import parseRuleSet from "./parse-ruleset.utl.mjs"; +import validate from "#validate/index.mjs"; + +function stringToCycleEntry(pString) { + return { + name: pString, + dependencyTypes: [], + }; +} + +describe("[I] validate/index dependency - cycle viaOnly", () => { + const lCycleViaRuleSet = parseRuleSet({ + forbidden: [ + { + from: {}, + to: { + circular: true, + viaOnly: { path: "^tmp/ab\\.js$" }, + }, + }, + ], + }); + + it("a => ba => bb => bc => a doesn't get flagged when the cycle doesn't go via the viaOnly", () => { + deepEqual( + validate.dependency( + lCycleViaRuleSet, + { source: "tmp/a.js" }, + { + resolved: "tmp/ba.js", + circular: true, + cycle: ["tmp/ba.js", "tmp/bb.js", "tmp/bc.js", "tmp/a.js"].map( + stringToCycleEntry, + ), + }, + ), + { + valid: true, + }, + ); + }); + + it("a => aa => ab => ac => does not get flagged when only some of them are not in the viaOnly", () => { + deepEqual( + validate.dependency( + lCycleViaRuleSet, + { source: "tmp/a.js" }, + { + resolved: "tmp/aa.js", + circular: true, + cycle: ["tmp/aa.js", "tmp/ab.js", "tmp/ac.js", "tmp/a.js"].map( + stringToCycleEntry, + ), + }, + ), + { + valid: true, + }, + ); + }); + + it("a => ab a gets flagged because all of the via's in the cycle are in the viaOnly", () => { + deepEqual( + validate.dependency( + lCycleViaRuleSet, + { source: "tmp/a.js" }, + { + resolved: "tmp/aa.js", + circular: true, + cycle: ["tmp/ab.js", "tmp/a.js"].map(stringToCycleEntry), + }, + ), + { + valid: true, + }, + ); + }); + + it("a => aa => ab => ac => a get flagged when all of them are in a viaOnly", () => { + const lRuleSet = parseRuleSet({ + forbidden: [ + { + from: {}, + to: { + circular: true, + via: "^tmp/[^.]+\\.js$", + }, + }, + ], + }); + deepEqual( + validate.dependency( + lRuleSet, + { source: "tmp/a.js" }, + { + resolved: "tmp/aa.js", + circular: true, + cycle: ["tmp/aa.js", "tmp/ab.js", "tmp/ac.js", "tmp/a.js"].map( + stringToCycleEntry, + ), + }, + ), + { + valid: false, + rules: [{ name: "unnamed", severity: "warn" }], + }, + ); + }); + + const lCycleViaNotTypeOnlyRuleSet = parseRuleSet({ + forbidden: [ + { + name: "no-runtime-cycles", + from: {}, + severity: "error", + to: { + circular: true, + viaOnly: { dependencyTypesNot: ["type-only"] }, + }, + }, + ], + }); + + it("a => aa => ab => ac => a doesn't get flagged when one of the dependencyTypes is in a pathNot", () => { + deepEqual( + validate.dependency( + lCycleViaNotTypeOnlyRuleSet, + { source: "tmp/a.js" }, + { + resolved: "tmp/aa.js", + circular: true, + cycle: [ + { name: "tmp/aa.js", dependencyTypes: ["import"] }, + { name: "tmp/ab.js", dependencyTypes: ["import", "type-only"] }, + { name: "tmp/ac.js", dependencyTypes: ["import"] }, + { name: "tmp/a.js", dependencyTypes: ["import"] }, + ], + }, + ), + { + valid: true, + }, + ); + }); + + it("a => aa => ab => ac => a does get flagged when none of the dependencyTypes is in a pathNot", () => { + deepEqual( + validate.dependency( + lCycleViaNotTypeOnlyRuleSet, + { source: "tmp/a.js" }, + { + resolved: "tmp/aa.js", + circular: true, + cycle: [ + { name: "tmp/aa.js", dependencyTypes: ["import"] }, + { name: "tmp/ab.js", dependencyTypes: ["import"] }, + { name: "tmp/ac.js", dependencyTypes: ["import"] }, + { name: "tmp/a.js", dependencyTypes: ["import"] }, + ], + }, + ), + { + rules: [ + { + name: "no-runtime-cycles", + severity: "error", + }, + ], + valid: false, + }, + ); + }); + + const lCycleViaTypeOnlyRuleSet = parseRuleSet({ + forbidden: [ + { + name: "flags-import-only-cycles", + from: {}, + severity: "error", + to: { + circular: true, + viaOnly: { dependencyTypes: ["import"] }, + }, + }, + ], + }); + it("a => aa => ab => ac => a does get flagged when none of the dependencyTypes is in a via", () => { + deepEqual( + validate.dependency( + lCycleViaTypeOnlyRuleSet, + { source: "tmp/a.js" }, + { + resolved: "tmp/aa.js", + circular: true, + cycle: [ + { name: "tmp/aa.js", dependencyTypes: ["import"] }, + { name: "tmp/ab.js", dependencyTypes: ["import"] }, + { name: "tmp/ac.js", dependencyTypes: ["import"] }, + { name: "tmp/a.js", dependencyTypes: ["import"] }, + ], + }, + ), + { + rules: [ + { + name: "flags-import-only-cycles", + severity: "error", + }, + ], + valid: false, + }, + ); + }); + + it("a => aa => ab => ac => a doesn't get flagged when none of the dependencyTypes is in a via", () => { + deepEqual( + validate.dependency( + lCycleViaTypeOnlyRuleSet, + { source: "tmp/a.js" }, + { + resolved: "tmp/aa.js", + circular: true, + cycle: [ + { name: "tmp/aa.js", dependencyTypes: ["import"] }, + { name: "tmp/ab.js", dependencyTypes: ["import"] }, + { name: "tmp/ac.js", dependencyTypes: ["require"] }, + { name: "tmp/a.js", dependencyTypes: ["import"] }, + ], + }, + ), + { + valid: true, + }, + ); + }); +}); + +describe("[I] validate/index dependency - cycle viaOnly - with group matching", () => { + const lCycleViaRuleSet = parseRuleSet({ + forbidden: [ + { + from: { + path: "^([^/]+)/.+", + }, + to: { + circular: true, + viaOnly: { path: "^($1)/ab\\.js$" }, + }, + }, + ], + }); + + it("a => ba => bb => bc => a doesn't get flagged when the cycle doesn't go via the viaOnly", () => { + deepEqual( + validate.dependency( + lCycleViaRuleSet, + { source: "tmp/a.js" }, + { + resolved: "tmp/ba.js", + circular: true, + cycle: ["tmp/ba.js", "tmp/bb.js", "tmp/bc.js", "tmp/a.js"].map( + stringToCycleEntry, + ), + }, + ), + { + valid: true, + }, + ); + }); + + it("a => aa => ab => ac => does not get flagged when only some of them are not in the viaOnly", () => { + deepEqual( + validate.dependency( + lCycleViaRuleSet, + { source: "tmp/a.js" }, + { + resolved: "tmp/aa.js", + circular: true, + cycle: ["tmp/aa.js", "tmp/ab.js", "tmp/ac.js", "tmp/a.js"].map( + stringToCycleEntry, + ), + }, + ), + { + valid: true, + }, + ); + }); + + it("a => ab a gets flagged becaue all of the via's in the cycle are in the viaOnly", () => { + deepEqual( + validate.dependency( + lCycleViaRuleSet, + { source: "tmp/a.js" }, + { + resolved: "tmp/aa.js", + circular: true, + cycle: ["tmp/ab.js", "tmp/a.js"].map(stringToCycleEntry), + }, + ), + { + valid: true, + }, + ); + }); + + it("a => aa => ab => ac => a get flagged when all of them are in a viaOnly", () => { + const lRuleSet = parseRuleSet({ + forbidden: [ + { + from: {}, + to: { + circular: true, + viaOnly: "^tmp/[^.]+\\.js$", + }, + }, + ], + }); + deepEqual( + validate.dependency( + lRuleSet, + { source: "tmp/a.js" }, + { + resolved: "tmp/aa.js", + circular: true, + cycle: ["tmp/aa.js", "tmp/ab.js", "tmp/ac.js", "tmp/a.js"].map( + stringToCycleEntry, + ), + }, + ), + { + valid: false, + rules: [{ name: "unnamed", severity: "warn" }], + }, + ); + }); + it("a => aa => ab => ac => a get flagged when all of them are in a viaOnly presented as an array", () => { + const lRuleSet = parseRuleSet({ + forbidden: [ + { + from: {}, + to: { + circular: true, + viaOnly: { path: "somethingelse|^tmp/[^.]+\\.js$" }, + }, + }, + ], + }); + deepEqual( + validate.dependency( + lRuleSet, + { source: "tmp/a.js" }, + { + resolved: "tmp/aa.js", + circular: true, + cycle: ["tmp/aa.js", "tmp/ab.js", "tmp/ac.js", "tmp/a.js"].map( + stringToCycleEntry, + ), + }, + ), + { + valid: false, + rules: [{ name: "unnamed", severity: "warn" }], + }, + ); + }); +}); + +describe("[I] validate/index dependency - cycle viaNot (which normalizes to viaOnly.pathNot)", () => { + const lCycleViaNotRuleSet = parseRuleSet({ + forbidden: [ + { + from: {}, + to: { + circular: true, + viaNot: "^tmp/ab\\.js$", + }, + }, + ], + }); + + it("a => ba => bb => bc => a get flagged when none of them is in a viaNot", () => { + deepEqual( + validate.dependency( + lCycleViaNotRuleSet, + { source: "tmp/a.js" }, + { + resolved: "tmp/ba.js", + circular: true, + cycle: ["tmp/ba.js", "tmp/bb.js", "tmp/bc.js", "tmp/a.js"].map( + stringToCycleEntry, + ), + }, + ), + { + valid: false, + rules: [{ name: "unnamed", severity: "warn" }], + }, + ); + }); + + it("a => aa => ab => ac => a doesn't get flagged when one of them is in a viaNot", () => { + deepEqual( + validate.dependency( + lCycleViaNotRuleSet, + { source: "tmp/a.js" }, + { + resolved: "tmp/aa.js", + circular: true, + cycle: ["tmp/aa.js", "tmp/ab.js", "tmp/ac.js", "tmp/a.js"].map( + stringToCycleEntry, + ), + }, + ), + { + valid: true, + }, + ); + }); +}); diff --git a/test/validate/index.cycle-via-some-not.spec.mjs b/test/validate/index.cycle-via-some-not.spec.mjs deleted file mode 100644 index 47b177e3c..000000000 --- a/test/validate/index.cycle-via-some-not.spec.mjs +++ /dev/null @@ -1,134 +0,0 @@ -import { deepEqual } from "node:assert/strict"; -import parseRuleSet from "./parse-ruleset.utl.mjs"; -import validate from "#validate/index.mjs"; - -function stringToCycleEntry(pString) { - return { - name: pString, - dependencyTypes: [], - }; -} - -describe("[I] validate/index dependency - cycle viaSomeNot - with group matching", () => { - const lCycleButNotViaGroupMatchRuleSet = { - forbidden: [ - { - name: "no-circular-dependency-of-modules", - from: { path: "^src/([^/]+)/.+" }, - to: { viaSomeNot: "^src/$1/.+", circular: true }, - }, - ], - }; - - it("flags when all of the cycle (except the root) is outside the group-matched viaSomeNot", () => { - deepEqual( - validate.dependency( - parseRuleSet(lCycleButNotViaGroupMatchRuleSet), - { source: "src/module-a/a.js" }, - { - resolved: "src/module-b/ba.js", - circular: true, - cycle: [ - "src/module-b/ba.js", - "src/module-b/bb.js", - "src/module-b/bc.js", - "src/module-a/a.js", - ].map(stringToCycleEntry), - }, - ), - { - valid: false, - rules: [ - { - name: "no-circular-dependency-of-modules", - severity: "warn", - }, - ], - }, - ); - }); - - it("flags when only one of the cycle is outside the group-matched viaNot", () => { - deepEqual( - validate.dependency( - parseRuleSet(lCycleButNotViaGroupMatchRuleSet), - { source: "src/module-a/a.js" }, - { - resolved: "src/module-a/aa.js", - circular: true, - cycle: [ - "src/module-a/aa.js", - "src/module-a/ab.js", - "src/module-b/bc.js", - "src/module-a/a.js", - ].map(stringToCycleEntry), - }, - ), - { - valid: false, - rules: [ - { - name: "no-circular-dependency-of-modules", - severity: "warn", - }, - ], - }, - ); - }); - - it("does not flag when all of the cycle is inside the group-matched viaNot", () => { - deepEqual( - validate.dependency( - parseRuleSet(lCycleButNotViaGroupMatchRuleSet), - { source: "src/module-a/a.js" }, - { - resolved: "src/module-a/aa.js", - circular: true, - cycle: [ - "src/module-a/aa.js", - "src/module-a/ab.js", - "src/module-a/ac.js", - "src/module-a/a.js", - ].map(stringToCycleEntry), - }, - ), - { - valid: true, - }, - ); - }); - - it("does not flag when all of the cycle is inside the group-matched viaSomeNot that's represented as an array", () => { - const lRuleSet = { - forbidden: [ - { - name: "no-circular-dependency-of-modules", - from: { path: "^src/([^/]+)/.+" }, - to: { - viaSomeNot: ["something", "^src/$1/.+", "somethingelse"], - circular: true, - }, - }, - ], - }; - deepEqual( - validate.dependency( - parseRuleSet(lRuleSet), - { source: "src/module-a/a.js" }, - { - resolved: "src/module-a/aa.js", - circular: true, - cycle: [ - "src/module-a/aa.js", - "src/module-a/ab.js", - "src/module-a/ac.js", - "src/module-a/a.js", - ].map(stringToCycleEntry), - }, - ), - { - valid: true, - }, - ); - }); -}); diff --git a/test/validate/index.cycle-via.spec.mjs b/test/validate/index.cycle-via.spec.mjs index 8ecb2875a..e78a8233b 100644 --- a/test/validate/index.cycle-via.spec.mjs +++ b/test/validate/index.cycle-via.spec.mjs @@ -16,7 +16,7 @@ describe("[I] validate/index dependency - cycle via", () => { from: {}, to: { circular: true, - via: "^tmp/ab\\.js$", + via: { path: "^tmp/ab\\.js$" }, }, }, ], @@ -68,7 +68,7 @@ describe("[I] validate/index dependency - cycle via", () => { from: {}, to: { circular: true, - via: "^tmp/[^.]+\\.js$", + via: "^tmp/[^.]+[.]js$", }, }, ], @@ -91,89 +91,118 @@ describe("[I] validate/index dependency - cycle via", () => { }, ); }); -}); -describe("[I] validate/index dependency - cycle via - with group matching", () => { - const lCycleViaRuleSet = parseRuleSet({ - forbidden: [ - { - from: { - path: "^([^/]+)", + it("a => aa => ab => ac => a get flagged when some of their dependencyTypes are of certain types", () => { + const lRuleSet = parseRuleSet({ + forbidden: [ + { + name: "no-circular-1", + from: {}, + to: { + circular: true, + via: { + dependencyTypes: ["require"], + }, + }, }, - to: { - circular: true, - via: "^$1/ab\\.js$", + { + name: "no-circular-2", + from: {}, + to: { + circular: true, + via: { + dependencyTypes: ["import"], + }, + }, }, - }, - ], - }); - - it("a => ba => bb => bc => a doesn't get flagged when none of them is in a via", () => { - deepEqual( - validate.dependency( - lCycleViaRuleSet, - { source: "tmp/a.js" }, { - resolved: "tmp/ba.js", - circular: true, - cycle: ["tmp/ba.js", "tmp/bb.js", "tmp/bc.js", "tmp/a.js"].map( - stringToCycleEntry, - ), + name: "no-circular-3", + from: {}, + to: { + circular: true, + via: { + dependencyTypes: ["import", "require", "local"], + }, + }, }, - ), - { - valid: true, - }, - ); - }); - - it("a => ba => bb => bc => a doesn't get flagged when none of them is in a via (group match)", () => { - deepEqual( - validate.dependency( - lCycleViaRuleSet, - { source: "tmp/a.js" }, { - resolved: "tmp/ba.js", - circular: true, - cycle: ["tmp/ba.js", "not-tmp/ab.js", "tmp/bc.js", "tmp/a.js"].map( - stringToCycleEntry, - ), + name: "no-circular-4", + from: {}, + to: { + circular: true, + via: { + dependencyTypes: ["type-only", "type-import"], + }, + }, }, - ), - { - valid: true, - }, - ); - }); - - it("a => aa => ab => ac => a get flagged when one of them is in a via", () => { + ], + }); deepEqual( validate.dependency( - lCycleViaRuleSet, + lRuleSet, { source: "tmp/a.js" }, { resolved: "tmp/aa.js", circular: true, - cycle: ["tmp/aa.js", "tmp/ab.js", "tmp/ac.js", "tmp/a.js"].map( - stringToCycleEntry, - ), + cycle: [ + { name: "tmp/aa.js", dependencyTypes: ["import"] }, + { name: "tmp/ab.js", dependencyTypes: ["import"] }, + { name: "tmp/ac.js", dependencyTypes: ["require"] }, + { name: "tmp/a.js", dependencyTypes: ["import"] }, + ], }, ), { valid: false, - rules: [{ name: "unnamed", severity: "warn" }], + rules: [ + { name: "no-circular-1", severity: "warn" }, + { name: "no-circular-2", severity: "warn" }, + { name: "no-circular-3", severity: "warn" }, + ], }, ); }); - - it("a => aa => ab => ac => a get flagged when all of them are in a via", () => { + it("a => aa => ab => ac => a get flagged when some of their dependencyTypes are NOT of certain types", () => { const lRuleSet = parseRuleSet({ forbidden: [ { + name: "no-circular-1", from: {}, to: { circular: true, - via: "^tmp/[^.]+\\.js$", + via: { + dependencyTypesNot: ["require"], + }, + }, + }, + { + name: "no-circular-2", + from: {}, + to: { + circular: true, + via: { + dependencyTypesNot: ["import"], + }, + }, + }, + { + name: "no-circular-3", + from: {}, + to: { + circular: true, + via: { + dependencyTypesNot: ["import", "require", "local"], + }, + }, + }, + { + name: "no-circular-4", + from: {}, + to: { + circular: true, + via: { + dependencyTypesNot: ["type-only", "type-import"], + }, }, }, ], @@ -185,33 +214,42 @@ describe("[I] validate/index dependency - cycle via - with group matching", () = { resolved: "tmp/aa.js", circular: true, - cycle: ["tmp/aa.js", "tmp/ab.js", "tmp/ac.js", "tmp/a.js"].map( - stringToCycleEntry, - ), + cycle: [ + { name: "tmp/aa.js", dependencyTypes: ["import"] }, + { name: "tmp/ab.js", dependencyTypes: ["import"] }, + { name: "tmp/ac.js", dependencyTypes: ["require"] }, + { name: "tmp/a.js", dependencyTypes: ["import"] }, + ], }, ), { valid: false, - rules: [{ name: "unnamed", severity: "warn" }], + rules: [ + { name: "no-circular-1", severity: "warn" }, + { name: "no-circular-2", severity: "warn" }, + { name: "no-circular-4", severity: "warn" }, + ], }, ); }); }); -describe("[I] validate/index dependency - cycle viaOnly", () => { +describe("[I] validate/index dependency - cycle via - with group matching", () => { const lCycleViaRuleSet = parseRuleSet({ forbidden: [ { - from: {}, + from: { + path: "^([^/]+)", + }, to: { circular: true, - viaOnly: "^tmp/ab\\.js$", + via: { path: "^$1/ab\\.js$" }, }, }, ], }); - it("a => ba => bb => bc => a doesn't get flagged when the cycle doesn't go via the viaOnly", () => { + it("a => ba => bb => bc => a doesn't get flagged when none of them is in a via", () => { deepEqual( validate.dependency( lCycleViaRuleSet, @@ -230,15 +268,15 @@ describe("[I] validate/index dependency - cycle viaOnly", () => { ); }); - it("a => aa => ab => ac => does not get flagged when only some of them are not in the viaOnly", () => { + it("a => ba => bb => bc => a doesn't get flagged when none of them is in a via (group match)", () => { deepEqual( validate.dependency( lCycleViaRuleSet, { source: "tmp/a.js" }, { - resolved: "tmp/aa.js", + resolved: "tmp/ba.js", circular: true, - cycle: ["tmp/aa.js", "tmp/ab.js", "tmp/ac.js", "tmp/a.js"].map( + cycle: ["tmp/ba.js", "not-tmp/ab.js", "tmp/bc.js", "tmp/a.js"].map( stringToCycleEntry, ), }, @@ -249,7 +287,7 @@ describe("[I] validate/index dependency - cycle viaOnly", () => { ); }); - it("a => ab a gets flagged becaue all of the via's in the cycle are in the viaOnly", () => { + it("a => aa => ab => ac => a get flagged when one of them is in a via", () => { deepEqual( validate.dependency( lCycleViaRuleSet, @@ -257,16 +295,19 @@ describe("[I] validate/index dependency - cycle viaOnly", () => { { resolved: "tmp/aa.js", circular: true, - cycle: ["tmp/ab.js", "tmp/a.js"].map(stringToCycleEntry), + cycle: ["tmp/aa.js", "tmp/ab.js", "tmp/ac.js", "tmp/a.js"].map( + stringToCycleEntry, + ), }, ), { - valid: true, + valid: false, + rules: [{ name: "unnamed", severity: "warn" }], }, ); }); - it("a => aa => ab => ac => a get flagged when all of them are in a viaOnly", () => { + it("a => aa => ab => ac => a get flagged when all of them are in a via", () => { const lRuleSet = parseRuleSet({ forbidden: [ { @@ -297,68 +338,88 @@ describe("[I] validate/index dependency - cycle viaOnly", () => { ); }); }); -describe("[I] validate/index dependency - cycle viaOnly - with group matching", () => { - const lCycleViaRuleSet = parseRuleSet({ + +describe("[I] validate/index dependency - cycle viaSomeNot (normalizes to via.pathNot) - with group matching", () => { + const lCycleButNotViaGroupMatchRuleSet = { forbidden: [ { - from: { - path: "^([^/]+)/.+", - }, - to: { - circular: true, - viaOnly: "^($1)/ab\\.js$", - }, + name: "no-circular-dependency-of-modules", + from: { path: "^src/([^/]+)/.+" }, + to: { viaSomeNot: "^src/$1/.+", circular: true }, }, ], - }); + }; - it("a => ba => bb => bc => a doesn't get flagged when the cycle doesn't go via the viaOnly", () => { + it("flags when all of the cycle (except the root) is outside the group-matched viaSomeNot", () => { deepEqual( validate.dependency( - lCycleViaRuleSet, - { source: "tmp/a.js" }, + parseRuleSet(lCycleButNotViaGroupMatchRuleSet), + { source: "src/module-a/a.js" }, { - resolved: "tmp/ba.js", + resolved: "src/module-b/ba.js", circular: true, - cycle: ["tmp/ba.js", "tmp/bb.js", "tmp/bc.js", "tmp/a.js"].map( - stringToCycleEntry, - ), + cycle: [ + "src/module-b/ba.js", + "src/module-b/bb.js", + "src/module-b/bc.js", + "src/module-a/a.js", + ].map(stringToCycleEntry), }, ), { - valid: true, + valid: false, + rules: [ + { + name: "no-circular-dependency-of-modules", + severity: "warn", + }, + ], }, ); }); - it("a => aa => ab => ac => does not get flagged when only some of them are not in the viaOnly", () => { + it("flags when only one of the cycle is outside the group-matched viaNot", () => { deepEqual( validate.dependency( - lCycleViaRuleSet, - { source: "tmp/a.js" }, + parseRuleSet(lCycleButNotViaGroupMatchRuleSet), + { source: "src/module-a/a.js" }, { - resolved: "tmp/aa.js", + resolved: "src/module-a/aa.js", circular: true, - cycle: ["tmp/aa.js", "tmp/ab.js", "tmp/ac.js", "tmp/a.js"].map( - stringToCycleEntry, - ), + cycle: [ + "src/module-a/aa.js", + "src/module-a/ab.js", + "src/module-b/bc.js", + "src/module-a/a.js", + ].map(stringToCycleEntry), }, ), { - valid: true, + valid: false, + rules: [ + { + name: "no-circular-dependency-of-modules", + severity: "warn", + }, + ], }, ); }); - it("a => ab a gets flagged becaue all of the via's in the cycle are in the viaOnly", () => { + it("does not flag when all of the cycle is inside the group-matched viaNot", () => { deepEqual( validate.dependency( - lCycleViaRuleSet, - { source: "tmp/a.js" }, + parseRuleSet(lCycleButNotViaGroupMatchRuleSet), + { source: "src/module-a/a.js" }, { - resolved: "tmp/aa.js", + resolved: "src/module-a/aa.js", circular: true, - cycle: ["tmp/ab.js", "tmp/a.js"].map(stringToCycleEntry), + cycle: [ + "src/module-a/aa.js", + "src/module-a/ab.js", + "src/module-a/ac.js", + "src/module-a/a.js", + ].map(stringToCycleEntry), }, ), { @@ -367,63 +428,36 @@ describe("[I] validate/index dependency - cycle viaOnly - with group matching", ); }); - it("a => aa => ab => ac => a get flagged when all of them are in a viaOnly", () => { - const lRuleSet = parseRuleSet({ + it("does not flag when all of the cycle is inside the group-matched viaSomeNot that's represented as an array", () => { + const lRuleSet = { forbidden: [ { - from: {}, + name: "no-circular-dependency-of-modules", + from: { path: "^src/([^/]+)/.+" }, to: { + viaSomeNot: "something|^src/$1/.+|somethingelse", circular: true, - viaOnly: "^tmp/[^.]+\\.js$", }, }, ], - }); + }; deepEqual( validate.dependency( - lRuleSet, - { source: "tmp/a.js" }, + parseRuleSet(lRuleSet), + { source: "src/module-a/a.js" }, { - resolved: "tmp/aa.js", + resolved: "src/module-a/aa.js", circular: true, - cycle: ["tmp/aa.js", "tmp/ab.js", "tmp/ac.js", "tmp/a.js"].map( - stringToCycleEntry, - ), + cycle: [ + "src/module-a/aa.js", + "src/module-a/ab.js", + "src/module-a/ac.js", + "src/module-a/a.js", + ].map(stringToCycleEntry), }, ), { - valid: false, - rules: [{ name: "unnamed", severity: "warn" }], - }, - ); - }); - it("a => aa => ab => ac => a get flagged when all of them are in a viaOnly presented as an array", () => { - const lRuleSet = parseRuleSet({ - forbidden: [ - { - from: {}, - to: { - circular: true, - viaOnly: ["somethingelse", "^tmp/[^.]+\\.js$"], - }, - }, - ], - }); - deepEqual( - validate.dependency( - lRuleSet, - { source: "tmp/a.js" }, - { - resolved: "tmp/aa.js", - circular: true, - cycle: ["tmp/aa.js", "tmp/ab.js", "tmp/ac.js", "tmp/a.js"].map( - stringToCycleEntry, - ), - }, - ), - { - valid: false, - rules: [{ name: "unnamed", severity: "warn" }], + valid: true, }, ); }); diff --git a/tools/schema/options.mjs b/tools/schema/options.mjs index 518f22b51..fdbf1d6ff 100644 --- a/tools/schema/options.mjs +++ b/tools/schema/options.mjs @@ -427,6 +427,7 @@ export default { type: "boolean", }, { + deprecated: true, type: "string", }, { $ref: "#/definitions/CacheOptionsType" }, diff --git a/tools/schema/restrictions.mjs b/tools/schema/restrictions.mjs index 889afa039..c9ab1db4d 100644 --- a/tools/schema/restrictions.mjs +++ b/tools/schema/restrictions.mjs @@ -1,3 +1,4 @@ +/* eslint-disable max-lines */ import REAsStringsType from "./re-as-strings-type.mjs"; import dependencyType from "./dependency-type.mjs"; @@ -18,6 +19,51 @@ const BASE_RESTRICTION = { export default { definitions: { + MiniDependencyRestrictionType: { + oneOf: [ + { + $ref: "#/definitions/REAsStringsType", + }, + { + type: "object", + additionalProperties: false, + properties: { + path: { + description: + "A regular expression or an array of regular expressions the " + + "'via' module should match to be caught by this rule.", + $ref: "#/definitions/REAsStringsType", + }, + pathNot: { + description: + "A regular expression or an array of regular expressions an the " + + "'via' module should _not_ match to be caught by this rule.", + $ref: "#/definitions/REAsStringsType", + }, + dependencyTypes: { + type: "array", + description: + "Which dependency types the dependency between this via and the " + + "previous one in the 'via chain' should have to be caught by " + + "this rule.", + items: { + $ref: "#/definitions/DependencyTypeType", + }, + }, + dependencyTypesNot: { + type: "array", + description: + "Which dependency types the dependency between this via and the " + + "previous one in the 'via chain' should _not_ have to be caught by " + + "this rule.", + items: { + $ref: "#/definitions/DependencyTypeType", + }, + }, + }, + }, + ], + }, FromRestrictionType: { type: "object", description: @@ -134,35 +180,35 @@ export default { via: { description: "For circular dependencies - whether or not to match cycles that include " + - "some modules with this regular expression. If you want to match cycles that " + - "_exclusively_ include modules satisfying the regular expression use the viaOnly " + + "some modules with these conditions. If you want to match cycles that " + + "_exclusively_ include modules satisfying them use the viaOnly " + "restriction." + "E.g. to allow all cycles, " + "except when they go through one specific module. Typically to temporarily " + "disallow some cycles with a lower severity - setting up a rule with a via " + "that ignores them in an 'allowed' section.", - $ref: "#/definitions/REAsStringsType", + $ref: "#/definitions/MiniDependencyRestrictionType", }, viaOnly: { description: "For circular dependencies - whether or not to match cycles that include " + - "exclusively modules with this regular expression. This is different from " + - "the regular via that already matches when only some of the modules in the " + - "cycle satisfy the regular expression", - $ref: "#/definitions/REAsStringsType", + "exclusively modules with these conditions. This is different from " + + "the regular via that already matches when only _some_ of the modules in the " + + "cycle satisfy the condition.", + $ref: "#/definitions/MiniDependencyRestrictionType", }, viaNot: { description: - "For circular dependencies - whether or not to match cycles that include " + - "_only_ modules that don't satisfy this regular expression. E.g. to disallow all cycles, " + - "except when they go through one specific module. Typically to temporarily " + - "allow some cycles until they're removed.", + "This attribute is deprecated. Use 'viaOnly' with a 'pathNot' attribute " + + "in stead.", + deprecated: true, $ref: "#/definitions/REAsStringsType", }, viaSomeNot: { description: - "For circular dependencies - whether or not to match cycles that include " + - "_some_ modules that don't satisfy this regular expression. ", + "This attribute is deprecated. Use 'via' with a 'pathNot' attribute " + + "in stead.", + deprecated: true, $ref: "#/definitions/REAsStringsType", }, moreUnstable: { diff --git a/types/restrictions.d.mts b/types/restrictions.d.mts index 07a8969d1..fd87b3461 100644 --- a/types/restrictions.d.mts +++ b/types/restrictions.d.mts @@ -1,5 +1,33 @@ import type { DependencyType } from "./shared-types.mjs"; +export type MiniDependencyRestrictionType = + | string + | string[] + | { + /** + * A regular expression or an array of regular expressions the + * 'via' module should match to be caught by this rule. + */ + path?: string | string[]; + /** + * A regular expression or an array of regular expressions the + * 'via' module should match to be caught by this rule. + */ + pathNot?: string | string[]; + /** + * Which dependency types the dependency between this via and the + * previous one in the 'via chain' should have to be caught by + * this rule + */ + dependencyTypes?: DependencyType[]; + /** + * Which dependency types the dependency between this via and the + * previous one in the 'via chain' should _not_ have to be caught by + * this rule + */ + dependencyTypesNot?: DependencyType[]; + }; + export interface IBaseRestrictionType { /** * A regular expression or an array of regular expressions that select @@ -39,7 +67,7 @@ export interface IToRestriction extends IBaseRestrictionType { * the regular via that already matches when only some of the modules in the * cycle satisfy the regular expression */ - via?: string | string[]; + via?: MiniDependencyRestrictionType; /** * For circular dependencies - whether or not to match cycles that include * some modules with this regular expression. If you want to match cycles that @@ -50,17 +78,19 @@ export interface IToRestriction extends IBaseRestrictionType { * disallow some cycles with a lower severity - setting up a rule with a via * that ignores them in an 'allowed' section. */ - viaOnly?: string | string[]; + viaOnly?: MiniDependencyRestrictionType; /** * For circular dependencies - whether or not to match cycles that include * _only_ modules that don't satisfy this regular expression. E.g. to disallow all cycles, * except when they go through one specific module. Typically to temporarily * allow some cycles until they're removed. + * @deprecated use viaOnly.pathNot in stead */ viaNot?: string | string[]; /** * "For circular dependencies - whether or not to match cycles that include * _some_ modules that don't satisfy this regular expression. + * @deprecated use via.pathNot in stead */ viaSomeNot?: string | string[]; /** diff --git a/types/strict-restrictions.d.mts b/types/strict-restrictions.d.mts index 45377d230..dab330a5a 100644 --- a/types/strict-restrictions.d.mts +++ b/types/strict-restrictions.d.mts @@ -6,6 +6,7 @@ import type { IRequiredToRestrictionType, IDependentsModuleRestrictionType, } from "./restrictions.mjs"; +import type { DependencyType } from "./shared-types.mjs"; export interface IStrictBaseRestrictionType extends IBaseRestrictionType { path?: string; @@ -17,12 +18,17 @@ export interface IStrictFromRestriction extends IFromRestriction { pathNot?: string; } +export interface IStrictMiniDependencyRestriction + extends IStrictBaseRestrictionType { + dependencyTypes?: DependencyType[]; + dependencyTypesNot?: DependencyType[]; +} + interface IStrictToRestriction extends IToRestriction { path?: string; pathNot?: string; - via?: string; - viaNot?: string; - viaSomeNot?: string; + via?: IStrictMiniDependencyRestriction; + viaOnly?: IStrictMiniDependencyRestriction; exoticRequire?: string; exoticRequireNot?: string; license?: string;