From 56b667f4a61d55c8fc0f68c133eee7717857a954 Mon Sep 17 00:00:00 2001 From: Felix Arntz Date: Thu, 18 Jun 2026 20:46:42 -0500 Subject: [PATCH 1/3] add mustNot property to forbid patterns based on predicates --- .../reusable-convention-package.schema.json | 2246 +++- packages/convention/src/index.test.ts | 37 + packages/convention/src/schemas.ts | 36 +- packages/konsistent/konsistent.schema.json | 9172 ++++++++++++++--- .../scripts/generate-schema.test.ts | 25 +- .../src/config/placeholder-validator.test.ts | 47 + .../src/config/placeholder-validator.ts | 54 +- .../src/config/reference-expander.test.ts | 131 +- .../src/config/reference-expander.ts | 32 +- packages/konsistent/src/config/schema.test.ts | 110 +- packages/konsistent/src/config/schema.ts | 94 +- .../src/core/convention-name.test.ts | 31 + .../konsistent/src/core/convention-name.ts | 141 +- packages/konsistent/src/core/runner.test.ts | 113 +- packages/konsistent/src/core/runner.ts | 256 +- 15 files changed, 10699 insertions(+), 1826 deletions(-) diff --git a/packages/convention/reusable-convention-package.schema.json b/packages/convention/reusable-convention-package.schema.json index 1e3be8e..5573701 100644 --- a/packages/convention/reusable-convention-package.schema.json +++ b/packages/convention/reusable-convention-package.schema.json @@ -9,66 +9,22 @@ "conventions": { "type": "array", "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "pattern": "^[a-z0-9-]+$" - }, - "description": { - "type": "string" - }, - "severity": { - "type": "string", - "enum": ["error", "warning"] - }, - "paths": { - "anyOf": [ - { + "anyOf": [ + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z0-9-]+$" + }, + "description": { "type": "string" }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - }, - "excludeFiles": { - "type": "array", - "items": { - "type": "string" - } - }, - "if": { - "anyOf": [ - { - "type": "object", - "properties": { - "hasFile": { - "type": "string" - } - }, - "required": ["hasFile"], - "additionalProperties": false + "severity": { + "type": "string", + "enum": ["error", "warning"] }, - { - "type": "object", - "properties": { - "placeholderSatisfies": { - "type": "string" - } - }, - "required": ["placeholderSatisfies"], - "additionalProperties": false - } - ] - }, - "for": { - "type": "object", - "properties": { - "files": { + "paths": { "anyOf": [ { "type": "string" @@ -80,461 +36,1931 @@ } } ] - } - }, - "required": ["files"], - "additionalProperties": false - }, - "must": { - "type": "object", - "properties": { - "haveType": { - "type": "string", - "enum": ["file", "directory"] }, - "haveFiles": { + "excludeFiles": { "type": "array", "items": { "type": "string" } }, - "declareTypes": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" + "if": { + "anyOf": [ + { + "type": "object", + "properties": { + "hasFile": { + "type": "string" + } + }, + "required": ["hasFile"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "placeholderSatisfies": { + "type": "string" + } }, - { - "type": "object", - "properties": { - "name": { + "required": ["placeholderSatisfies"], + "additionalProperties": false + } + ] + }, + "for": { + "type": "object", + "properties": { + "files": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { "type": "string" } - }, - "required": ["name"], - "additionalProperties": false - } - ] - } + } + ] + } + }, + "required": ["files"], + "additionalProperties": false }, - "declareConstants": { - "type": "array", - "items": { - "anyOf": [ - { + "must": { + "type": "object", + "properties": { + "haveType": { + "type": "string", + "enum": ["file", "directory"] + }, + "haveFiles": { + "type": "array", + "items": { "type": "string" - }, - { - "type": "object", - "properties": { - "name": { + } + }, + "declareTypes": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false } - }, - "required": ["name"], - "additionalProperties": false + ] } - ] - } - }, - "declareFunctions": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { + }, + "declareConstants": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "receiveParamOfType": { + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareFunctions": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "returnValueOfType": { - "type": "string" + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false } - }, - "required": ["name"], - "additionalProperties": false + ] } - ] - } - }, - "declareInterfaces": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { + }, + "declareInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "extend": { - "anyOf": [ - { + { + "type": "object", + "properties": { + "name": { "type": "string" }, - { - "type": "object", - "properties": { - "type": { + "extend": { + "anyOf": [ + { "type": "string" }, - "allowOmissions": { - "type": "boolean" + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false } - }, - "required": ["type"], - "additionalProperties": false + ] } - ] + }, + "required": ["name"], + "additionalProperties": false } - }, - "required": ["name"], - "additionalProperties": false + ] } - ] - } - }, - "declareClasses": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { + }, + "declareClasses": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "extend": { - "anyOf": [ - { + { + "type": "object", + "properties": { + "name": { "type": "string" }, - { - "type": "object", - "properties": { - "type": { + "extend": { + "anyOf": [ + { "type": "string" }, - "allowOmissions": { - "type": "boolean" + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false } - }, - "required": ["type"], - "additionalProperties": false - } - ] - }, - "implement": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "type": { + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "allowOmissions": { - "type": "boolean" + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false } - }, - "required": ["type"], - "additionalProperties": false + ] } - ] - } + } + }, + "required": ["name"], + "additionalProperties": false } - }, - "required": ["name"], - "additionalProperties": false + ] } - ] - } - }, - "export": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { + }, + "export": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "from": { - "type": "string" + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false } - }, - "required": ["name"], - "additionalProperties": false + ] } - ] - } - }, - "exportTypes": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { + }, + "exportTypes": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "from": { - "type": "string" + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false } - }, - "required": ["name"], - "additionalProperties": false + ] } - ] - } - }, - "exportConstants": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { + }, + "exportConstants": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "from": { - "type": "string" + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false } - }, - "required": ["name"], - "additionalProperties": false + ] } - ] - } - }, - "exportFunctions": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "receiveParamOfType": { + }, + "exportFunctions": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "returnValueOfType": { - "type": "string" + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false } - }, - "required": ["name"], - "additionalProperties": false + ] } - ] - } - }, - "exportInterfaces": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { + }, + "exportInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "extend": { - "anyOf": [ - { + { + "type": "object", + "properties": { + "name": { "type": "string" }, - { - "type": "object", - "properties": { - "type": { + "extend": { + "anyOf": [ + { "type": "string" }, - "allowOmissions": { - "type": "boolean" + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false } - }, - "required": ["type"], - "additionalProperties": false + ] } - ] + }, + "required": ["name"], + "additionalProperties": false } - }, - "required": ["name"], - "additionalProperties": false + ] } - ] - } - }, - "exportClasses": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { + }, + "exportClasses": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "extend": { - "anyOf": [ - { + { + "type": "object", + "properties": { + "name": { "type": "string" }, - { - "type": "object", - "properties": { - "type": { + "extend": { + "anyOf": [ + { "type": "string" }, - "allowOmissions": { - "type": "boolean" + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false } - }, - "required": ["type"], - "additionalProperties": false - } - ] - }, - "implement": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "type": { + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "allowOmissions": { - "type": "boolean" + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false } - }, - "required": ["type"], - "additionalProperties": false + ] } - ] - } + } + }, + "required": ["name"], + "additionalProperties": false } - }, - "required": ["name"], - "additionalProperties": false + ] } - ] - } - }, - "import": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { + }, + "import": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "from": { - "type": "string" + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false } - }, - "required": ["name"], - "additionalProperties": false + ] } - ] - } - }, - "importTypes": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { + }, + "importTypes": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "from": { - "type": "string" + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false } - }, - "required": ["name"], - "additionalProperties": false + ] } - ] - } - }, - "importFromCurrentDir": { - "type": "boolean" + }, + "importFromCurrentDir": { + "type": "boolean" + }, + "importFromParents": { + "type": "boolean" + }, + "importFromExternals": { + "type": "boolean" + }, + "useDeclarationOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "areBarrelFiles": { + "type": "boolean" + } + }, + "additionalProperties": false }, - "importFromParents": { - "type": "boolean" + "mustNot": { + "type": "object", + "properties": { + "haveType": { + "type": "string", + "enum": ["file", "directory"] + }, + "haveFiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "declareTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "export": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "import": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importFromCurrentDir": { + "type": "boolean" + }, + "importFromParents": { + "type": "boolean" + }, + "importFromExternals": { + "type": "boolean" + }, + "useDeclarationOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "areBarrelFiles": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "required": ["name", "description", "must"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z0-9-]+$" + }, + "description": { + "type": "string" + }, + "severity": { + "type": "string", + "enum": ["error", "warning"] }, - "importFromExternals": { - "type": "boolean" + "paths": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] }, - "useDeclarationOrder": { + "excludeFiles": { "type": "array", "items": { "type": "string" } }, - "areBarrelFiles": { - "type": "boolean" + "if": { + "anyOf": [ + { + "type": "object", + "properties": { + "hasFile": { + "type": "string" + } + }, + "required": ["hasFile"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "placeholderSatisfies": { + "type": "string" + } + }, + "required": ["placeholderSatisfies"], + "additionalProperties": false + } + ] + }, + "for": { + "type": "object", + "properties": { + "files": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + }, + "required": ["files"], + "additionalProperties": false + }, + "must": { + "type": "object", + "properties": { + "haveType": { + "type": "string", + "enum": ["file", "directory"] + }, + "haveFiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "declareTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "export": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "import": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importFromCurrentDir": { + "type": "boolean" + }, + "importFromParents": { + "type": "boolean" + }, + "importFromExternals": { + "type": "boolean" + }, + "useDeclarationOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "areBarrelFiles": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "mustNot": { + "type": "object", + "properties": { + "haveType": { + "type": "string", + "enum": ["file", "directory"] + }, + "haveFiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "declareTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "export": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "import": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importFromCurrentDir": { + "type": "boolean" + }, + "importFromParents": { + "type": "boolean" + }, + "importFromExternals": { + "type": "boolean" + }, + "useDeclarationOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "areBarrelFiles": { + "type": "boolean" + } + }, + "additionalProperties": false } }, + "required": ["name", "description", "mustNot"], "additionalProperties": false } - }, - "required": ["name", "description", "must"], - "additionalProperties": false + ] } } }, diff --git a/packages/convention/src/index.test.ts b/packages/convention/src/index.test.ts index 0007820..b21709d 100644 --- a/packages/convention/src/index.test.ts +++ b/packages/convention/src/index.test.ts @@ -30,6 +30,16 @@ describe("ReusableConventionV1Schema", () => { expect(result.success).toBe(true); }); + it("accepts mustNot without must", () => { + const result = ReusableConventionV1Schema.safeParse({ + name: "files-must-not-export-debug", + description: "Component files must not export debug helpers.", + paths: ["src/components/{name}.ts"], + mustNot: { exportConstants: ["debug"] }, + }); + expect(result.success).toBe(true); + }); + it("accepts declaration, declaration order, and import source predicates", () => { const result = ReusableConventionV1Schema.safeParse({ name: "locals", @@ -79,6 +89,23 @@ describe("ReusableConventionV1Schema", () => { expect(result.success).toBe(false); }); + it("rejects mustNot MustBlock[] form", () => { + const result = ReusableConventionV1Schema.safeParse({ + name: "x", + description: "x", + mustNot: [{ must: { haveType: "file" } }], + }); + expect(result.success).toBe(false); + }); + + it("rejects reusable conventions with neither must nor mustNot", () => { + const result = ReusableConventionV1Schema.safeParse({ + name: "x", + description: "x", + }); + expect(result.success).toBe(false); + }); + it("rejects missing description", () => { const result = ReusableConventionV1Schema.safeParse({ name: "x", @@ -99,6 +126,16 @@ describe("ReusableConventionsPackageV1Schema", () => { expect(result.success).toBe(true); }); + it("accepts a package convention with mustNot", () => { + const result = ReusableConventionsPackageV1Schema.safeParse({ + conventionSpecVersion: "v1", + conventions: [ + { name: "a", description: "d", mustNot: { export: ["debug"] } }, + ], + }); + expect(result.success).toBe(true); + }); + it("rejects wrong conventionSpecVersion", () => { const result = ReusableConventionsPackageV1Schema.safeParse({ conventionSpecVersion: "v2", diff --git a/packages/convention/src/schemas.ts b/packages/convention/src/schemas.ts index e2dd62e..cf6fcfe 100644 --- a/packages/convention/src/schemas.ts +++ b/packages/convention/src/schemas.ts @@ -84,7 +84,7 @@ export const MustPredicatesV1Schema = z.strictObject({ areBarrelFiles: z.boolean().optional(), }); -export const MustBlockV1Schema = z.strictObject({ +const MustBlockV1Shape = { name: z .string() .regex(/^[a-z0-9-]+$/, "Must block name must match [a-z0-9-]+") @@ -100,10 +100,22 @@ export const MustBlockV1Schema = z.strictObject({ .strictObject({ files: z.union([z.string(), z.array(z.string())]) }) .optional(), excludeFiles: z.array(z.string()).optional(), - must: MustPredicatesV1Schema, -}); +}; + +export const MustBlockV1Schema = z.union([ + z.strictObject({ + ...MustBlockV1Shape, + must: MustPredicatesV1Schema, + mustNot: MustPredicatesV1Schema.optional(), + }), + z.strictObject({ + ...MustBlockV1Shape, + must: MustPredicatesV1Schema.optional(), + mustNot: MustPredicatesV1Schema, + }), +]); -export const ReusableConventionV1Schema = z.strictObject({ +const ReusableConventionV1Shape = { name: z .string() .regex(/^[a-z0-9-]+$/, "Convention name must match [a-z0-9-]+"), @@ -120,8 +132,20 @@ export const ReusableConventionV1Schema = z.strictObject({ for: z .strictObject({ files: z.union([z.string(), z.array(z.string())]) }) .optional(), - must: MustPredicatesV1Schema, -}); +}; + +export const ReusableConventionV1Schema = z.union([ + z.strictObject({ + ...ReusableConventionV1Shape, + must: MustPredicatesV1Schema, + mustNot: MustPredicatesV1Schema.optional(), + }), + z.strictObject({ + ...ReusableConventionV1Shape, + must: MustPredicatesV1Schema.optional(), + mustNot: MustPredicatesV1Schema, + }), +]); export const ReusableConventionsPackageV1Schema = z.strictObject({ conventionSpecVersion: z.literal("v1"), diff --git a/packages/konsistent/konsistent.schema.json b/packages/konsistent/konsistent.schema.json index 140962c..5aab1fc 100644 --- a/packages/konsistent/konsistent.schema.json +++ b/packages/konsistent/konsistent.schema.json @@ -576,1204 +576,7842 @@ } }, "additionalProperties": false - } - }, - "required": ["use"], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "name": { - "type": "string", - "pattern": "^[a-z0-9-]+$" - }, - "description": { - "type": "string" - }, - "severity": { - "default": "error", - "type": "string", - "enum": ["error", "warning"] - }, - "excludeFiles": { - "type": "array", - "items": { - "type": "string" - } }, - "paths": { - "anyOf": [ - { - "type": "string" + "mustNot": { + "type": "object", + "properties": { + "haveType": { + "type": "string", + "enum": ["file", "directory"] }, - { + "haveFiles": { "type": "array", "items": { "type": "string" } - } - ] - }, - "placeholders": { - "type": "object", - "propertyNames": { - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9]*$" - }, - "additionalProperties": { - "type": "string", - "pattern": "^[a-zA-Z0-9_-]+$" - } - }, - "must": { - "anyOf": [ - { - "type": "object", - "properties": { - "haveType": { - "type": "string", - "enum": ["file", "directory"] - }, - "haveFiles": { - "type": "array", - "items": { + }, + "declareTypes": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" - } - }, - "declareTypes": { - "type": "array", - "items": { - "anyOf": [ - { + }, + { + "type": "object", + "properties": { + "name": { "type": "string" - }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - } - }, - "required": ["name"], - "additionalProperties": false } - ] + }, + "required": ["name"], + "additionalProperties": false } - }, - "declareConstants": { - "type": "array", - "items": { - "anyOf": [ - { + ] + } + }, + "declareConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { "type": "string" - }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - } - }, - "required": ["name"], - "additionalProperties": false } - ] + }, + "required": ["name"], + "additionalProperties": false } - }, - "declareFunctions": { - "type": "array", - "items": { - "anyOf": [ - { + ] + } + }, + "declareFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { "type": "string" }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "receiveParamOfType": { - "type": "string" - }, - "returnValueOfType": { - "type": "string" - } - }, - "required": ["name"], - "additionalProperties": false + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" } - ] + }, + "required": ["name"], + "additionalProperties": false } - }, - "declareInterfaces": { - "type": "array", - "items": { - "anyOf": [ - { + ] + } + }, + "declareInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { "type": "string" }, - { - "type": "object", - "properties": { - "name": { + "extend": { + "anyOf": [ + { "type": "string" }, - "extend": { - "anyOf": [ - { + { + "type": "object", + "properties": { + "type": { "type": "string" }, - { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "allowOmissions": { - "type": "boolean" - } - }, - "required": ["type"], - "additionalProperties": false + "allowOmissions": { + "type": "boolean" } - ] + }, + "required": ["type"], + "additionalProperties": false } - }, - "required": ["name"], - "additionalProperties": false + ] } - ] + }, + "required": ["name"], + "additionalProperties": false } - }, - "declareClasses": { - "type": "array", - "items": { - "anyOf": [ - { + ] + } + }, + "declareClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { "type": "string" }, - { - "type": "object", - "properties": { - "name": { + "extend": { + "anyOf": [ + { "type": "string" }, - "extend": { - "anyOf": [ - { + { + "type": "object", + "properties": { + "type": { "type": "string" }, - { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "allowOmissions": { - "type": "boolean" - } - }, - "required": ["type"], - "additionalProperties": false + "allowOmissions": { + "type": "boolean" } - ] - }, - "implement": { - "type": "array", - "items": { - "anyOf": [ - { + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { "type": "string" }, - { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "allowOmissions": { - "type": "boolean" - } - }, - "required": ["type"], - "additionalProperties": false + "allowOmissions": { + "type": "boolean" } - ] + }, + "required": ["type"], + "additionalProperties": false } - } - }, - "required": ["name"], - "additionalProperties": false + ] + } } - ] + }, + "required": ["name"], + "additionalProperties": false } - }, - "export": { - "type": "array", - "items": { - "anyOf": [ - { + ] + } + }, + "export": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { "type": "string" }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "from": { - "type": "string" - } - }, - "required": ["name"], - "additionalProperties": false + "from": { + "type": "string" } - ] + }, + "required": ["name"], + "additionalProperties": false } - }, - "exportTypes": { - "type": "array", - "items": { - "anyOf": [ - { + ] + } + }, + "exportTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { "type": "string" }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "from": { - "type": "string" - } - }, - "required": ["name"], - "additionalProperties": false + "from": { + "type": "string" } - ] + }, + "required": ["name"], + "additionalProperties": false } - }, - "exportConstants": { - "type": "array", - "items": { - "anyOf": [ - { + ] + } + }, + "exportConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { "type": "string" }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "from": { - "type": "string" - } - }, - "required": ["name"], - "additionalProperties": false + "from": { + "type": "string" } - ] + }, + "required": ["name"], + "additionalProperties": false } - }, - "exportFunctions": { - "type": "array", - "items": { - "anyOf": [ - { + ] + } + }, + "exportFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { "type": "string" }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "receiveParamOfType": { - "type": "string" - }, - "returnValueOfType": { - "type": "string" - } - }, - "required": ["name"], - "additionalProperties": false + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" } - ] + }, + "required": ["name"], + "additionalProperties": false } - }, - "exportInterfaces": { - "type": "array", - "items": { - "anyOf": [ - { + ] + } + }, + "exportInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { "type": "string" }, - { - "type": "object", - "properties": { - "name": { + "extend": { + "anyOf": [ + { "type": "string" }, - "extend": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "allowOmissions": { - "type": "boolean" - } - }, - "required": ["type"], - "additionalProperties": false - } - ] - } - }, - "required": ["name"], - "additionalProperties": false - } - ] - } - }, - "exportClasses": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "extend": { - "anyOf": [ - { + { + "type": "object", + "properties": { + "type": { "type": "string" }, - { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "allowOmissions": { - "type": "boolean" - } - }, - "required": ["type"], - "additionalProperties": false + "allowOmissions": { + "type": "boolean" } - ] - }, - "implement": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "allowOmissions": { - "type": "boolean" - } - }, - "required": ["type"], - "additionalProperties": false - } - ] - } - } - }, - "required": ["name"], - "additionalProperties": false - } - ] - } - }, - "import": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "from": { - "type": "string" - } - }, - "required": ["name"], - "additionalProperties": false - } - ] - } - }, - "importTypes": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "from": { - "type": "string" + }, + "required": ["type"], + "additionalProperties": false } - }, - "required": ["name"], - "additionalProperties": false + ] } - ] - } - }, - "importFromCurrentDir": { - "type": "boolean" - }, - "importFromParents": { - "type": "boolean" - }, - "importFromExternals": { - "type": "boolean" - }, - "useDeclarationOrder": { - "type": "array", - "items": { - "type": "string" + }, + "required": ["name"], + "additionalProperties": false } - }, - "areBarrelFiles": { - "type": "boolean" - } - }, - "additionalProperties": false + ] + } }, - { + "exportClasses": { "type": "array", "items": { "anyOf": [ { - "type": "string", - "pattern": "^[a-z0-9-]+\\/[a-z0-9-]+$" + "type": "string" }, { "type": "object", "properties": { "name": { - "type": "string", - "pattern": "^[a-z0-9-]+$" - }, - "description": { "type": "string" }, - "if": { + "extend": { "anyOf": [ { - "type": "object", - "properties": { - "hasFile": { - "type": "string" - } - }, - "required": ["hasFile"], - "additionalProperties": false + "type": "string" }, { "type": "object", "properties": { - "placeholderSatisfies": { + "type": { "type": "string" + }, + "allowOmissions": { + "type": "boolean" } }, - "required": ["placeholderSatisfies"], + "required": ["type"], "additionalProperties": false } ] }, - "for": { - "type": "object", - "properties": { - "files": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - } - }, - "required": ["files"], - "additionalProperties": false - }, - "excludeFiles": { + "implement": { "type": "array", "items": { - "type": "string" - } - }, - "must": { - "type": "object", - "properties": { - "haveType": { - "type": "string", - "enum": ["file", "directory"] - }, - "haveFiles": { - "type": "array", - "items": { + "anyOf": [ + { "type": "string" - } - }, - "declareTypes": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - } - }, - "required": ["name"], - "additionalProperties": false - } - ] - } - }, - "declareConstants": { - "type": "array", - "items": { - "anyOf": [ - { + }, + { + "type": "object", + "properties": { + "type": { "type": "string" }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - } - }, - "required": ["name"], - "additionalProperties": false + "allowOmissions": { + "type": "boolean" } - ] + }, + "required": ["type"], + "additionalProperties": false } - }, - "declareFunctions": { - "type": "array", - "items": { - "anyOf": [ + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "import": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importFromCurrentDir": { + "type": "boolean" + }, + "importFromParents": { + "type": "boolean" + }, + "importFromExternals": { + "type": "boolean" + }, + "useDeclarationOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "areBarrelFiles": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "required": ["use"], + "additionalProperties": false + }, + { + "anyOf": [ + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z0-9-]+$" + }, + "description": { + "type": "string" + }, + "severity": { + "default": "error", + "type": "string", + "enum": ["error", "warning"] + }, + "excludeFiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "paths": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "placeholders": { + "type": "object", + "propertyNames": { + "type": "string", + "pattern": "^[a-zA-Z][a-zA-Z0-9]*$" + }, + "additionalProperties": { + "type": "string", + "pattern": "^[a-zA-Z0-9_-]+$" + } + }, + "must": { + "anyOf": [ + { + "type": "object", + "properties": { + "haveType": { + "type": "string", + "enum": ["file", "directory"] + }, + "haveFiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "declareTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "export": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "import": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importFromCurrentDir": { + "type": "boolean" + }, + "importFromParents": { + "type": "boolean" + }, + "importFromExternals": { + "type": "boolean" + }, + "useDeclarationOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "areBarrelFiles": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string", + "pattern": "^[a-z0-9-]+\\/[a-z0-9-]+$" + }, + { + "anyOf": [ + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z0-9-]+$" + }, + "description": { + "type": "string" + }, + "if": { + "anyOf": [ + { + "type": "object", + "properties": { + "hasFile": { + "type": "string" + } + }, + "required": ["hasFile"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "placeholderSatisfies": { + "type": "string" + } + }, + "required": ["placeholderSatisfies"], + "additionalProperties": false + } + ] + }, + "for": { + "type": "object", + "properties": { + "files": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + }, + "required": ["files"], + "additionalProperties": false + }, + "excludeFiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "must": { + "type": "object", + "properties": { + "haveType": { + "type": "string", + "enum": ["file", "directory"] + }, + "haveFiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "declareTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "export": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "import": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importFromCurrentDir": { + "type": "boolean" + }, + "importFromParents": { + "type": "boolean" + }, + "importFromExternals": { + "type": "boolean" + }, + "useDeclarationOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "areBarrelFiles": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "mustNot": { + "type": "object", + "properties": { + "haveType": { + "type": "string", + "enum": ["file", "directory"] + }, + "haveFiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "declareTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "export": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "import": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importFromCurrentDir": { + "type": "boolean" + }, + "importFromParents": { + "type": "boolean" + }, + "importFromExternals": { + "type": "boolean" + }, + "useDeclarationOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "areBarrelFiles": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "required": ["must"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z0-9-]+$" + }, + "description": { + "type": "string" + }, + "if": { + "anyOf": [ + { + "type": "object", + "properties": { + "hasFile": { + "type": "string" + } + }, + "required": ["hasFile"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "placeholderSatisfies": { + "type": "string" + } + }, + "required": ["placeholderSatisfies"], + "additionalProperties": false + } + ] + }, + "for": { + "type": "object", + "properties": { + "files": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + }, + "required": ["files"], + "additionalProperties": false + }, + "excludeFiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "must": { + "type": "object", + "properties": { + "haveType": { + "type": "string", + "enum": ["file", "directory"] + }, + "haveFiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "declareTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "export": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "import": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importFromCurrentDir": { + "type": "boolean" + }, + "importFromParents": { + "type": "boolean" + }, + "importFromExternals": { + "type": "boolean" + }, + "useDeclarationOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "areBarrelFiles": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "mustNot": { + "type": "object", + "properties": { + "haveType": { + "type": "string", + "enum": ["file", "directory"] + }, + "haveFiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "declareTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "export": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "import": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importFromCurrentDir": { + "type": "boolean" + }, + "importFromParents": { + "type": "boolean" + }, + "importFromExternals": { + "type": "boolean" + }, + "useDeclarationOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "areBarrelFiles": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "required": ["mustNot"], + "additionalProperties": false + } + ] + }, + { + "type": "object", + "properties": { + "use": { + "type": "string", + "pattern": "^[a-z0-9-]+\\/[a-z0-9-]+$" + }, + "name": { + "type": "string", + "pattern": "^[a-z0-9-]+$" + }, + "description": { + "type": "string" + }, + "if": { + "anyOf": [ + { + "type": "object", + "properties": { + "hasFile": { + "type": "string" + } + }, + "required": ["hasFile"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "placeholderSatisfies": { + "type": "string" + } + }, + "required": ["placeholderSatisfies"], + "additionalProperties": false + } + ] + }, + "for": { + "type": "object", + "properties": { + "files": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + }, + "required": ["files"], + "additionalProperties": false + }, + "excludeFiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "must": { + "type": "object", + "properties": { + "haveType": { + "type": "string", + "enum": ["file", "directory"] + }, + "haveFiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "declareTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "export": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "import": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importFromCurrentDir": { + "type": "boolean" + }, + "importFromParents": { + "type": "boolean" + }, + "importFromExternals": { + "type": "boolean" + }, + "useDeclarationOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "areBarrelFiles": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "mustNot": { + "type": "object", + "properties": { + "haveType": { + "type": "string", + "enum": ["file", "directory"] + }, + "haveFiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "declareTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "export": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "import": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importFromCurrentDir": { + "type": "boolean" + }, + "importFromParents": { + "type": "boolean" + }, + "importFromExternals": { + "type": "boolean" + }, + "useDeclarationOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "areBarrelFiles": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "required": ["use"], + "additionalProperties": false + } + ] + } + } + ] + }, + "mustNot": { + "type": "object", + "properties": { + "haveType": { + "type": "string", + "enum": ["file", "directory"] + }, + "haveFiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "declareTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "export": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "import": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importFromCurrentDir": { + "type": "boolean" + }, + "importFromParents": { + "type": "boolean" + }, + "importFromExternals": { + "type": "boolean" + }, + "useDeclarationOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "areBarrelFiles": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "required": ["paths", "must"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z0-9-]+$" + }, + "description": { + "type": "string" + }, + "severity": { + "default": "error", + "type": "string", + "enum": ["error", "warning"] + }, + "excludeFiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "paths": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "placeholders": { + "type": "object", + "propertyNames": { + "type": "string", + "pattern": "^[a-zA-Z][a-zA-Z0-9]*$" + }, + "additionalProperties": { + "type": "string", + "pattern": "^[a-zA-Z0-9_-]+$" + } + }, + "must": { + "anyOf": [ + { + "type": "object", + "properties": { + "haveType": { + "type": "string", + "enum": ["file", "directory"] + }, + "haveFiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "declareTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "export": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "import": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importFromCurrentDir": { + "type": "boolean" + }, + "importFromParents": { + "type": "boolean" + }, + "importFromExternals": { + "type": "boolean" + }, + "useDeclarationOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "areBarrelFiles": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string", + "pattern": "^[a-z0-9-]+\\/[a-z0-9-]+$" + }, + { + "anyOf": [ + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z0-9-]+$" + }, + "description": { + "type": "string" + }, + "if": { + "anyOf": [ + { + "type": "object", + "properties": { + "hasFile": { + "type": "string" + } + }, + "required": ["hasFile"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "placeholderSatisfies": { + "type": "string" + } + }, + "required": ["placeholderSatisfies"], + "additionalProperties": false + } + ] + }, + "for": { + "type": "object", + "properties": { + "files": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + }, + "required": ["files"], + "additionalProperties": false + }, + "excludeFiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "must": { + "type": "object", + "properties": { + "haveType": { + "type": "string", + "enum": ["file", "directory"] + }, + "haveFiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "declareTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "export": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "import": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importFromCurrentDir": { + "type": "boolean" + }, + "importFromParents": { + "type": "boolean" + }, + "importFromExternals": { + "type": "boolean" + }, + "useDeclarationOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "areBarrelFiles": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "mustNot": { + "type": "object", + "properties": { + "haveType": { + "type": "string", + "enum": ["file", "directory"] + }, + "haveFiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "declareTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "export": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "import": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importFromCurrentDir": { + "type": "boolean" + }, + "importFromParents": { + "type": "boolean" + }, + "importFromExternals": { + "type": "boolean" + }, + "useDeclarationOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "areBarrelFiles": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "required": ["must"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z0-9-]+$" + }, + "description": { + "type": "string" + }, + "if": { + "anyOf": [ + { + "type": "object", + "properties": { + "hasFile": { + "type": "string" + } + }, + "required": ["hasFile"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "placeholderSatisfies": { + "type": "string" + } + }, + "required": ["placeholderSatisfies"], + "additionalProperties": false + } + ] + }, + "for": { + "type": "object", + "properties": { + "files": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + }, + "required": ["files"], + "additionalProperties": false + }, + "excludeFiles": { + "type": "array", + "items": { "type": "string" + } + }, + "must": { + "type": "object", + "properties": { + "haveType": { + "type": "string", + "enum": ["file", "directory"] + }, + "haveFiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "declareTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "export": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "import": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importFromCurrentDir": { + "type": "boolean" + }, + "importFromParents": { + "type": "boolean" + }, + "importFromExternals": { + "type": "boolean" + }, + "useDeclarationOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "areBarrelFiles": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "mustNot": { + "type": "object", + "properties": { + "haveType": { + "type": "string", + "enum": ["file", "directory"] + }, + "haveFiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "declareTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "export": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "import": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importFromCurrentDir": { + "type": "boolean" + }, + "importFromParents": { + "type": "boolean" + }, + "importFromExternals": { + "type": "boolean" + }, + "useDeclarationOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "areBarrelFiles": { + "type": "boolean" + } }, - { - "type": "object", - "properties": { - "name": { + "additionalProperties": false + } + }, + "required": ["mustNot"], + "additionalProperties": false + } + ] + }, + { + "type": "object", + "properties": { + "use": { + "type": "string", + "pattern": "^[a-z0-9-]+\\/[a-z0-9-]+$" + }, + "name": { + "type": "string", + "pattern": "^[a-z0-9-]+$" + }, + "description": { + "type": "string" + }, + "if": { + "anyOf": [ + { + "type": "object", + "properties": { + "hasFile": { + "type": "string" + } + }, + "required": ["hasFile"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "placeholderSatisfies": { + "type": "string" + } + }, + "required": ["placeholderSatisfies"], + "additionalProperties": false + } + ] + }, + "for": { + "type": "object", + "properties": { + "files": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + }, + "required": ["files"], + "additionalProperties": false + }, + "excludeFiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "must": { + "type": "object", + "properties": { + "haveType": { + "type": "string", + "enum": ["file", "directory"] + }, + "haveFiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "declareTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "export": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "import": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "importFromCurrentDir": { + "type": "boolean" + }, + "importFromParents": { + "type": "boolean" + }, + "importFromExternals": { + "type": "boolean" + }, + "useDeclarationOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "areBarrelFiles": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "mustNot": { + "type": "object", + "properties": { + "haveType": { + "type": "string", + "enum": ["file", "directory"] + }, + "haveFiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "declareTypes": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "receiveParamOfType": { + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareConstants": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "returnValueOfType": { + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareFunctions": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false } - }, - "required": ["name"], - "additionalProperties": false + ] } - ] - } - }, - "declareInterfaces": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { + }, + "declareInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "extend": { - "anyOf": [ - { + { + "type": "object", + "properties": { + "name": { "type": "string" }, - { - "type": "object", - "properties": { - "type": { + "extend": { + "anyOf": [ + { "type": "string" }, - "allowOmissions": { - "type": "boolean" + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false } - }, - "required": ["type"], - "additionalProperties": false + ] } - ] + }, + "required": ["name"], + "additionalProperties": false } - }, - "required": ["name"], - "additionalProperties": false + ] } - ] - } - }, - "declareClasses": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { + }, + "declareClasses": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "extend": { - "anyOf": [ - { + { + "type": "object", + "properties": { + "name": { "type": "string" }, - { - "type": "object", - "properties": { - "type": { + "extend": { + "anyOf": [ + { "type": "string" }, - "allowOmissions": { - "type": "boolean" + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false } - }, - "required": ["type"], - "additionalProperties": false - } - ] - }, - "implement": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "type": { + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "allowOmissions": { - "type": "boolean" + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false } - }, - "required": ["type"], - "additionalProperties": false + ] } - ] - } + } + }, + "required": ["name"], + "additionalProperties": false } - }, - "required": ["name"], - "additionalProperties": false + ] } - ] - } - }, - "export": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { + }, + "export": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "from": { - "type": "string" + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false } - }, - "required": ["name"], - "additionalProperties": false + ] } - ] - } - }, - "exportTypes": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { + }, + "exportTypes": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "from": { - "type": "string" + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false } - }, - "required": ["name"], - "additionalProperties": false + ] } - ] - } - }, - "exportConstants": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { + }, + "exportConstants": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "from": { - "type": "string" + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false } - }, - "required": ["name"], - "additionalProperties": false + ] } - ] - } - }, - "exportFunctions": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "receiveParamOfType": { + }, + "exportFunctions": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "returnValueOfType": { - "type": "string" + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false } - }, - "required": ["name"], - "additionalProperties": false + ] } - ] - } - }, - "exportInterfaces": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { + }, + "exportInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "extend": { - "anyOf": [ - { + { + "type": "object", + "properties": { + "name": { "type": "string" }, - { - "type": "object", - "properties": { - "type": { + "extend": { + "anyOf": [ + { "type": "string" }, - "allowOmissions": { - "type": "boolean" + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false } - }, - "required": ["type"], - "additionalProperties": false + ] } - ] + }, + "required": ["name"], + "additionalProperties": false } - }, - "required": ["name"], - "additionalProperties": false + ] } - ] - } - }, - "exportClasses": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { + }, + "exportClasses": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "extend": { - "anyOf": [ - { + { + "type": "object", + "properties": { + "name": { "type": "string" }, - { - "type": "object", - "properties": { - "type": { + "extend": { + "anyOf": [ + { "type": "string" - }, - "allowOmissions": { - "type": "boolean" - } - }, - "required": ["type"], - "additionalProperties": false - } - ] - }, - "implement": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "allowOmissions": { - "type": "boolean" - } - }, - "required": ["type"], - "additionalProperties": false - } - ] - } - } - }, - "required": ["name"], - "additionalProperties": false - } - ] - } - }, - "import": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + "implement": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false + } + ] + } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "import": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "from": { - "type": "string" + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false } - }, - "required": ["name"], - "additionalProperties": false + ] } - ] - } - }, - "importTypes": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { + }, + "importTypes": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "from": { - "type": "string" + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false } - }, - "required": ["name"], - "additionalProperties": false + ] } - ] - } - }, - "importFromCurrentDir": { - "type": "boolean" - }, - "importFromParents": { - "type": "boolean" - }, - "importFromExternals": { - "type": "boolean" - }, - "useDeclarationOrder": { - "type": "array", - "items": { - "type": "string" - } - }, - "areBarrelFiles": { - "type": "boolean" + }, + "importFromCurrentDir": { + "type": "boolean" + }, + "importFromParents": { + "type": "boolean" + }, + "importFromExternals": { + "type": "boolean" + }, + "useDeclarationOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "areBarrelFiles": { + "type": "boolean" + } + }, + "additionalProperties": false } }, + "required": ["use"], "additionalProperties": false } - }, - "required": ["must"], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "name": { - "type": "string", - "pattern": "^[a-z0-9-]+$" + ] + } + } + ] + }, + "mustNot": { + "type": "object", + "properties": { + "haveType": { + "type": "string", + "enum": ["file", "directory"] + }, + "haveFiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "declareTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" }, - "description": { + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareConstants": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, - "if": { - "anyOf": [ - { - "type": "object", - "properties": { - "hasFile": { - "type": "string" - } - }, - "required": ["hasFile"], - "additionalProperties": false + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" }, - { - "type": "object", - "properties": { - "placeholderSatisfies": { - "type": "string" - } - }, - "required": ["placeholderSatisfies"], - "additionalProperties": false + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" } - ] + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "declareInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" }, - "for": { + { "type": "object", "properties": { - "files": { + "name": { + "type": "string" + }, + "extend": { "anyOf": [ { "type": "string" }, { - "type": "array", - "items": { - "type": "string" - } + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "allowOmissions": { + "type": "boolean" + } + }, + "required": ["type"], + "additionalProperties": false } ] } }, - "required": ["files"], + "required": ["name"], "additionalProperties": false + } + ] + } + }, + "declareClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" }, - "excludeFiles": { - "type": "array", - "items": { - "type": "string" - } - }, - "must": { + { "type": "object", "properties": { - "haveType": { - "type": "string", - "enum": ["file", "directory"] - }, - "haveFiles": { - "type": "array", - "items": { - "type": "string" - } - }, - "declareTypes": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - } - }, - "required": ["name"], - "additionalProperties": false - } - ] - } - }, - "declareConstants": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - } - }, - "required": ["name"], - "additionalProperties": false - } - ] - } + "name": { + "type": "string" }, - "declareFunctions": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "receiveParamOfType": { - "type": "string" - }, - "returnValueOfType": { - "type": "string" - } + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" }, - "required": ["name"], - "additionalProperties": false - } - ] - } - }, - "declareInterfaces": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" + "allowOmissions": { + "type": "boolean" + } }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "extend": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "allowOmissions": { - "type": "boolean" - } - }, - "required": ["type"], - "additionalProperties": false - } - ] - } - }, - "required": ["name"], - "additionalProperties": false - } - ] - } + "required": ["type"], + "additionalProperties": false + } + ] }, - "declareClasses": { + "implement": { "type": "array", "items": { "anyOf": [ @@ -1783,281 +8421,195 @@ { "type": "object", "properties": { - "name": { + "type": { "type": "string" }, - "extend": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "allowOmissions": { - "type": "boolean" - } - }, - "required": ["type"], - "additionalProperties": false - } - ] - }, - "implement": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "allowOmissions": { - "type": "boolean" - } - }, - "required": ["type"], - "additionalProperties": false - } - ] - } + "allowOmissions": { + "type": "boolean" } }, - "required": ["name"], + "required": ["type"], "additionalProperties": false } ] } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "export": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" }, - "export": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "from": { - "type": "string" - } - }, - "required": ["name"], - "additionalProperties": false - } - ] - } + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" }, - "exportTypes": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "from": { - "type": "string" - } - }, - "required": ["name"], - "additionalProperties": false - } - ] - } + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportConstants": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" }, - "exportConstants": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "from": { - "type": "string" - } - }, - "required": ["name"], - "additionalProperties": false - } - ] - } + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportFunctions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "receiveParamOfType": { + "type": "string" + }, + "returnValueOfType": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportInterfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" }, - "exportFunctions": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "receiveParamOfType": { - "type": "string" - }, - "returnValueOfType": { - "type": "string" - } + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" }, - "required": ["name"], - "additionalProperties": false - } - ] - } - }, - "exportInterfaces": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" + "allowOmissions": { + "type": "boolean" + } }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "extend": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "allowOmissions": { - "type": "boolean" - } - }, - "required": ["type"], - "additionalProperties": false - } - ] - } - }, - "required": ["name"], - "additionalProperties": false - } - ] - } + "required": ["type"], + "additionalProperties": false + } + ] + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "exportClasses": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" }, - "exportClasses": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "extend": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "allowOmissions": { - "type": "boolean" - } - }, - "required": ["type"], - "additionalProperties": false - } - ] - }, - "implement": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "allowOmissions": { - "type": "boolean" - } - }, - "required": ["type"], - "additionalProperties": false - } - ] - } - } + "extend": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" }, - "required": ["name"], - "additionalProperties": false - } - ] - } - }, - "import": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" + "allowOmissions": { + "type": "boolean" + } }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "from": { - "type": "string" - } - }, - "required": ["name"], - "additionalProperties": false - } - ] - } + "required": ["type"], + "additionalProperties": false + } + ] }, - "importTypes": { + "implement": { "type": "array", "items": { "anyOf": [ @@ -2067,56 +8619,98 @@ { "type": "object", "properties": { - "name": { + "type": { "type": "string" }, - "from": { - "type": "string" + "allowOmissions": { + "type": "boolean" } }, - "required": ["name"], + "required": ["type"], "additionalProperties": false } ] } + } + }, + "required": ["name"], + "additionalProperties": false + } + ] + } + }, + "import": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" }, - "importFromCurrentDir": { - "type": "boolean" - }, - "importFromParents": { - "type": "boolean" - }, - "importFromExternals": { - "type": "boolean" - }, - "useDeclarationOrder": { - "type": "array", - "items": { - "type": "string" - } - }, - "areBarrelFiles": { - "type": "boolean" + "from": { + "type": "string" } }, + "required": ["name"], "additionalProperties": false + } + ] + } + }, + "importTypes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" }, - "use": { - "type": "string", - "pattern": "^[a-z0-9-]+\\/[a-z0-9-]+$" + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false } - }, - "required": ["use"], - "additionalProperties": false + ] } - ] - } + }, + "importFromCurrentDir": { + "type": "boolean" + }, + "importFromParents": { + "type": "boolean" + }, + "importFromExternals": { + "type": "boolean" + }, + "useDeclarationOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "areBarrelFiles": { + "type": "boolean" + } + }, + "additionalProperties": false } - ] + }, + "required": ["paths", "mustNot"], + "additionalProperties": false } - }, - "required": ["paths", "must"], - "additionalProperties": false + ] } ] } diff --git a/packages/konsistent/scripts/generate-schema.test.ts b/packages/konsistent/scripts/generate-schema.test.ts index ab9b7d4..5318709 100644 --- a/packages/konsistent/scripts/generate-schema.test.ts +++ b/packages/konsistent/scripts/generate-schema.test.ts @@ -32,18 +32,31 @@ describe("konsistent.schema.json", () => { }); it("rejects additionalProperties in the must object", () => { - const objectBranch = schema.properties.conventions.items.anyOf.find( - (branch: { type?: string; properties?: Record }) => - branch.type === "object" && - branch.properties !== undefined && - Object.hasOwn(branch.properties, "paths") && - !Object.hasOwn(branch.properties, "use") + const handWrittenBranch = schema.properties.conventions.items.anyOf.find( + (branch: { anyOf?: unknown[] }) => Array.isArray(branch.anyOf) + ); + const objectBranch = handWrittenBranch.anyOf.find( + (branch: { properties?: Record; required?: string[] }) => + branch.required?.includes("must") ); const mustSchema = objectBranch.properties.must; const predicatesSchema = mustSchema.anyOf[0]; expect(predicatesSchema.additionalProperties).toBe(false); }); + it("rejects additionalProperties in the mustNot object", () => { + const handWrittenBranch = schema.properties.conventions.items.anyOf.find( + (branch: { anyOf?: unknown[] }) => Array.isArray(branch.anyOf) + ); + const objectBranch = handWrittenBranch.anyOf.find( + (branch: { properties?: Record; required?: string[] }) => + branch.required?.includes("mustNot") + ); + const mustNotSchema = objectBranch.properties.mustNot; + expect(mustNotSchema.additionalProperties).toBe(false); + expect(mustNotSchema.type).toBe("object"); + }); + const passingFixtures = readdirSync(fixturesDir).filter((name) => { if (name === "invalid-config") { return false; diff --git a/packages/konsistent/src/config/placeholder-validator.test.ts b/packages/konsistent/src/config/placeholder-validator.test.ts index 3e27e48..38705eb 100644 --- a/packages/konsistent/src/config/placeholder-validator.test.ts +++ b/packages/konsistent/src/config/placeholder-validator.test.ts @@ -42,6 +42,28 @@ describe("validatePlaceholders", () => { } }); + it("rejects a placeholder used in mustNot but absent from paths", () => { + const conventions: ConventionV1[] = [ + { + name: "broken", + paths: ["packages/{packageName}"], + mustNot: { exportConstants: ["${componentName}Debug"] }, + }, + ]; + + const result = validatePlaceholders({ + conventions, + identifiers: ["broken"], + }); + + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error).toBe( + 'Convention "broken" references "${componentName}" in mustNot.exportConstants, but neither paths nor placeholders declare "{componentName}".' + ); + } + }); + it("reports only the missing placeholder when multiple are used and one is missing", () => { const conventions: ConventionV1[] = [ { @@ -112,6 +134,31 @@ describe("validatePlaceholders", () => { } }); + it("detects a placeholder inside a nested MustBlock mustNot predicate", () => { + const conventions: ConventionV1[] = [ + { + name: "block-form", + paths: ["packages/{packageName}"], + must: [ + { + mustNot: { exportConstants: ["${missing}Debug"] }, + }, + ], + }, + ]; + + const result = validatePlaceholders({ + conventions, + identifiers: ["block-form"], + }); + + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error).toContain('"${missing}"'); + expect(result.error).toContain("mustNot.exportConstants"); + } + }); + it("scans a single-string paths value for declared placeholders", () => { const conventions: ConventionV1[] = [ { diff --git a/packages/konsistent/src/config/placeholder-validator.ts b/packages/konsistent/src/config/placeholder-validator.ts index 5620176..931bfca 100644 --- a/packages/konsistent/src/config/placeholder-validator.ts +++ b/packages/konsistent/src/config/placeholder-validator.ts @@ -36,8 +36,9 @@ export function validatePlaceholders(opts: { } } - const usages = collectUsagesInMust({ + const usages = collectUsagesInAssertions({ must: convention.must, + mustNot: convention.mustNot, declaredOuter: declared, }); @@ -86,24 +87,33 @@ interface Usage { name: string; } -function collectUsagesInMust(opts: { - must: MustPredicatesV1 | MustBlockV1[]; +function collectUsagesInAssertions(opts: { + must?: MustPredicatesV1 | MustBlockV1[]; + mustNot?: MustPredicatesV1; declaredOuter: Set; }): Usage[] { - const { must, declaredOuter } = opts; + const { must, mustNot, declaredOuter } = opts; const usages: Usage[] = []; if (Array.isArray(must)) { for (const block of must) { collectUsagesInBlock({ block, declaredOuter, usages }); } - return usages; + } else if (must) { + collectUsagesInPredicates({ + predicates: must, + prefix: "must", + declared: declaredOuter, + usages, + }); + } + if (mustNot) { + collectUsagesInPredicates({ + predicates: mustNot, + prefix: "mustNot", + declared: declaredOuter, + usages, + }); } - collectUsagesInPredicates({ - predicates: must, - prefix: "must", - declared: declaredOuter, - usages, - }); return usages; } @@ -159,12 +169,22 @@ function collectUsagesInBlock(opts: { } } - collectUsagesInPredicates({ - predicates: block.must, - prefix: "must", - declared: declaredHere, - usages, - }); + if (block.must) { + collectUsagesInPredicates({ + predicates: block.must, + prefix: "must", + declared: declaredHere, + usages, + }); + } + if (block.mustNot) { + collectUsagesInPredicates({ + predicates: block.mustNot, + prefix: "mustNot", + declared: declaredHere, + usages, + }); + } } function collectUsagesInPredicates(opts: { diff --git a/packages/konsistent/src/config/reference-expander.test.ts b/packages/konsistent/src/config/reference-expander.test.ts index a8569b1..1465916 100644 --- a/packages/konsistent/src/config/reference-expander.test.ts +++ b/packages/konsistent/src/config/reference-expander.test.ts @@ -46,6 +46,31 @@ describe("expandReferences", () => { } }); + it("expands a string-ref with mustNot predicates", () => { + const sourceMap = buildSourceMap({ + common: [ + { + name: "no-debug", + description: "Do not export debug helpers.", + paths: ["packages/{packageName}"], + mustNot: { exportConstants: ["debug"] }, + }, + ], + }); + + const result = expandReferences({ + conventions: ["common/no-debug"], + sourceMap, + }); + + expect(result.success).toBe(true); + if (result.success) { + expect(result.conventions[0]?.mustNot).toEqual({ + exportConstants: ["debug"], + }); + } + }); + it("emits the unknown-vendor error verbatim from the PRD", () => { const sourceMap: SourceMap = new Map(); @@ -341,6 +366,37 @@ describe("expandReferences", () => { } }); + it("recursively merges nested mustNot predicates without dropping inherited keys", () => { + const sourceMap = buildSourceMap({ + common: [ + { + name: "merge-must-not", + description: "x", + paths: "src/*.ts", + mustNot: { export: ["debug"] }, + }, + ], + }); + + const result = expandReferences({ + conventions: [ + { + use: "common/merge-must-not", + mustNot: { exportTypes: ["Internal"] }, + }, + ], + sourceMap, + }); + + expect(result.success).toBe(true); + if (result.success) { + expect(result.conventions[0]?.mustNot).toEqual({ + export: ["debug"], + exportTypes: ["Internal"], + }); + } + }); + it("replaces primitive values like severity", () => { const sourceMap = buildSourceMap({ common: [ @@ -538,6 +594,40 @@ describe("expandReferences", () => { } }); + it("expands a mustNot string reference nested inside a hand-written must[]", () => { + const sourceMap = buildSourceMap({ + common: [ + { + name: "no-debug", + description: "Block forbidding debug exports.", + mustNot: { exportConstants: ["debug"] }, + }, + ], + }); + + const handWritten = { + paths: "packages/{packageName}", + must: ["common/no-debug"], + } as Parameters[0]["conventions"][number]; + + const result = expandReferences({ + conventions: [handWritten], + sourceMap, + }); + + expect(result.success).toBe(true); + if (result.success) { + const must = result.conventions[0]?.must; + if (Array.isArray(must)) { + expect(must[0]).toEqual({ + name: "no-debug", + description: "Block forbidding debug exports.", + mustNot: { exportConstants: ["debug"] }, + }); + } + } + }); + it("errors when a string reference inside must[] points at a reusable with paths", () => { const sourceMap = buildSourceMap({ common: [ @@ -647,6 +737,45 @@ describe("expandReferences", () => { } }); + it("deep-merges override.mustNot onto inherited predicates inside must[]", () => { + const sourceMap = buildSourceMap({ + common: [ + { + name: "base-block", + description: "x", + mustNot: { export: ["debug"] }, + }, + ], + }); + + const handWritten = { + paths: "packages/{packageName}", + must: [ + { + use: "common/base-block", + mustNot: { exportTypes: ["Internal"] }, + }, + ], + } as Parameters[0]["conventions"][number]; + + const result = expandReferences({ + conventions: [handWritten], + sourceMap, + }); + + expect(result.success).toBe(true); + if (result.success) { + const must = result.conventions[0]?.must; + if (Array.isArray(must)) { + expect(must[0]).toEqual({ + name: "base-block", + description: "x", + mustNot: { export: ["debug"], exportTypes: ["Internal"] }, + }); + } + } + }); + it("errors when a must[] use ref points to a reusable that declares paths", () => { const sourceMap = buildSourceMap({ common: [ @@ -746,7 +875,7 @@ describe("expandReferences", () => { expect(result.success).toBe(false); if (!result.success) { expect(result.error).toContain("conventions.0"); - expect(result.error).toContain("severity"); + expect(result.error).toContain("Invalid input"); } }); }); diff --git a/packages/konsistent/src/config/reference-expander.ts b/packages/konsistent/src/config/reference-expander.ts index 6d11d4c..2cecaf4 100644 --- a/packages/konsistent/src/config/reference-expander.ts +++ b/packages/konsistent/src/config/reference-expander.ts @@ -57,8 +57,8 @@ export function expandReferences(opts: { } const handWrittenResult = expandHandWritten({ - entry: entry as ConventionV1 & { - must: + entry: entry as Omit & { + must?: | MustPredicatesV1 | Array< string | MustBlockV1 | ({ use: string } & Record) @@ -78,8 +78,8 @@ export function expandReferences(opts: { } function expandHandWritten(opts: { - entry: ConventionV1 & { - must: + entry: Omit & { + must?: | MustPredicatesV1 | Array< string | MustBlockV1 | ({ use: string } & Record) @@ -177,9 +177,13 @@ function expandMustBlockReference(opts: { }; } - const base: Record = { - must: reusable.must, - }; + const base: Record = {}; + if (reusable.must !== undefined) { + base.must = reusable.must; + } + if (reusable.mustNot !== undefined) { + base.mustNot = reusable.mustNot; + } if (reusable.name !== undefined) { base.name = reusable.name; } @@ -284,8 +288,13 @@ function expandStringReference(opts: { name: reusable.name, description: reusable.description, paths, - must: reusable.must, }; + if (reusable.must !== undefined) { + candidate.must = reusable.must; + } + if (reusable.mustNot !== undefined) { + candidate.mustNot = reusable.mustNot; + } if (reusable.severity !== undefined) { candidate.severity = reusable.severity; } @@ -337,8 +346,13 @@ function expandUseReference(opts: { const base: Record = { name: reusable.name, description: reusable.description, - must: reusable.must, }; + if (reusable.must !== undefined) { + base.must = reusable.must; + } + if (reusable.mustNot !== undefined) { + base.mustNot = reusable.mustNot; + } if (reusable.severity !== undefined) { base.severity = reusable.severity; } diff --git a/packages/konsistent/src/config/schema.test.ts b/packages/konsistent/src/config/schema.test.ts index 303216f..e1b52dd 100644 --- a/packages/konsistent/src/config/schema.test.ts +++ b/packages/konsistent/src/config/schema.test.ts @@ -33,6 +33,33 @@ describe("ConfigV1Schema", () => { expect(result.success).toBe(true); }); + it("accepts a convention with mustNot predicates and no must", () => { + const result = ConfigV1Schema.safeParse({ + version: "v1", + conventions: [ + { + paths: "src/components/*.ts", + mustNot: { exportConstants: ["debug"] }, + }, + ], + }); + expect(result.success).toBe(true); + }); + + it("accepts a convention with both must and mustNot", () => { + const result = ConfigV1Schema.safeParse({ + version: "v1", + conventions: [ + { + paths: "src/components/*.ts", + must: { haveType: "file" }, + mustNot: { exportConstants: ["debug"] }, + }, + ], + }); + expect(result.success).toBe(true); + }); + it("accepts a convention with paths as array", () => { const result = ConfigV1Schema.safeParse({ version: "v1", @@ -171,6 +198,19 @@ describe("ConfigV1Schema", () => { expect(result.success).toBe(false); }); + it("rejects unknown predicates in mustNot", () => { + const result = ConfigV1Schema.safeParse({ + version: "v1", + conventions: [ + { + paths: "src/*.ts", + mustNot: { unknownPredicate: ["foo"] }, + }, + ], + }); + expect(result.success).toBe(false); + }); + it("accepts must as an array of MustBlocks", () => { const result = ConfigV1Schema.safeParse({ version: "v1", @@ -187,6 +227,45 @@ describe("ConfigV1Schema", () => { expect(result.success).toBe(true); }); + it("rejects top-level mustNot as an array of MustBlocks", () => { + const result = ConfigV1Schema.safeParse({ + version: "v1", + conventions: [ + { + paths: "src/*.ts", + mustNot: [{ must: { exportConstants: ["debug"] } }], + }, + ], + }); + expect(result.success).toBe(false); + }); + + it("rejects top-level mustNot as a string reference", () => { + const result = ConfigV1Schema.safeParse({ + version: "v1", + conventions: [ + { + paths: "src/*.ts", + mustNot: ["common/no-debug"], + }, + ], + }); + expect(result.success).toBe(false); + }); + + it("rejects top-level mustNot as a use reference", () => { + const result = ConfigV1Schema.safeParse({ + version: "v1", + conventions: [ + { + paths: "src/*.ts", + mustNot: [{ use: "common/no-debug" }], + }, + ], + }); + expect(result.success).toBe(false); + }); + it("accepts a MustBlock without if condition", () => { const result = ConfigV1Schema.safeParse({ version: "v1", @@ -307,7 +386,7 @@ describe("ConfigV1Schema", () => { expect(result.success).toBe(false); }); - it("rejects a MustBlock array with missing must property", () => { + it("rejects a MustBlock array with neither must nor mustNot", () => { const result = ConfigV1Schema.safeParse({ version: "v1", conventions: [ @@ -320,6 +399,21 @@ describe("ConfigV1Schema", () => { expect(result.success).toBe(false); }); + it("accepts a MustBlock with only mustNot", () => { + const result = ConfigV1Schema.safeParse({ + version: "v1", + conventions: [ + { + paths: "src/*.ts", + must: [ + { if: { hasFile: "index.ts" }, mustNot: { export: ["debug"] } }, + ], + }, + ], + }); + expect(result.success).toBe(true); + }); + it("accepts a convention with severity error", () => { const result = ConfigV1Schema.safeParse({ version: "v1", @@ -488,6 +582,20 @@ describe("ConfigV1Schema", () => { expect(result.success).toBe(true); }); + it("accepts a use-form reference with a mustNot override", () => { + const result = ConfigV1Schema.safeParse({ + version: "v1", + conventionSources: { common: "./x.json" }, + conventions: [ + { + use: "common/some-convention", + mustNot: { exportConstants: ["debug"] }, + }, + ], + }); + expect(result.success).toBe(true); + }); + it("rejects a use-form reference with a name field (strict)", () => { const result = ConfigV1Schema.safeParse({ version: "v1", diff --git a/packages/konsistent/src/config/schema.ts b/packages/konsistent/src/config/schema.ts index c64996e..783da60 100644 --- a/packages/konsistent/src/config/schema.ts +++ b/packages/konsistent/src/config/schema.ts @@ -41,7 +41,21 @@ const PlaceholdersMapSchema = z.record( .regex(/^[a-zA-Z0-9_-]+$/, "Placeholder value must match [a-zA-Z0-9_-]+") ); -export const ConventionV1Schema = z.strictObject({ +const ConditionSchema = z.union([ + z.strictObject({ hasFile: z.string() }), + z.strictObject({ placeholderSatisfies: z.string() }), +]); + +const ForSchema = z.strictObject({ + files: z.union([z.string(), z.array(z.string())]), +}); + +const ConventionMustSchema = z.union([ + MustPredicatesV1Schema, + z.array(MustBlockV1Schema), +]); + +const ConventionV1Shape = { name: z .string() .regex(/^[a-z0-9-]+$/, "Convention name must match [a-z0-9-]+") @@ -51,16 +65,38 @@ export const ConventionV1Schema = z.strictObject({ excludeFiles: z.array(z.string()).optional(), paths: z.union([z.string(), z.array(z.string())]), placeholders: PlaceholdersMapSchema.optional(), - must: z.union([MustPredicatesV1Schema, z.array(MustBlockV1Schema)]), -}); +}; -export const MustBlockUseRefSchema = MustBlockV1Schema.partial().extend({ +export const ConventionV1Schema = z.union([ + z.strictObject({ + ...ConventionV1Shape, + must: ConventionMustSchema, + mustNot: MustPredicatesV1Schema.optional(), + }), + z.strictObject({ + ...ConventionV1Shape, + must: ConventionMustSchema.optional(), + mustNot: MustPredicatesV1Schema, + }), +]); + +export const MustBlockUseRefSchema = z.strictObject({ use: z .string() .regex( /^[a-z0-9-]+\/[a-z0-9-]+$/, 'Must block "use" must match "/"' ), + name: z + .string() + .regex(/^[a-z0-9-]+$/, "Must block name must match [a-z0-9-]+") + .optional(), + description: z.string().optional(), + if: ConditionSchema.optional(), + for: ForSchema.optional(), + excludeFiles: z.array(z.string()).optional(), + must: MustPredicatesV1Schema.optional(), + mustNot: MustPredicatesV1Schema.optional(), }); const ConventionStringRefSchema = z @@ -70,7 +106,18 @@ const ConventionStringRefSchema = z 'Convention reference must match "/"' ); -const RawHandWrittenConventionV1Schema = z.strictObject({ +const RawMustSchema = z.union([ + MustPredicatesV1Schema, + z.array( + z.union([ + ConventionStringRefSchema, + MustBlockV1Schema, + MustBlockUseRefSchema, + ]) + ), +]); + +const RawHandWrittenConventionV1Shape = { name: z .string() .regex(/^[a-z0-9-]+$/, "Convention name must match [a-z0-9-]+") @@ -80,17 +127,20 @@ const RawHandWrittenConventionV1Schema = z.strictObject({ excludeFiles: z.array(z.string()).optional(), paths: z.union([z.string(), z.array(z.string())]), placeholders: PlaceholdersMapSchema.optional(), - must: z.union([ - MustPredicatesV1Schema, - z.array( - z.union([ - ConventionStringRefSchema, - MustBlockV1Schema, - MustBlockUseRefSchema, - ]) - ), - ]), -}); +}; + +const RawHandWrittenConventionV1Schema = z.union([ + z.strictObject({ + ...RawHandWrittenConventionV1Shape, + must: RawMustSchema, + mustNot: MustPredicatesV1Schema.optional(), + }), + z.strictObject({ + ...RawHandWrittenConventionV1Shape, + must: RawMustSchema.optional(), + mustNot: MustPredicatesV1Schema, + }), +]); export const ConventionUseRefSchema = z.strictObject({ use: z @@ -103,16 +153,10 @@ export const ConventionUseRefSchema = z.strictObject({ paths: z.union([z.string(), z.array(z.string())]).optional(), placeholders: PlaceholdersMapSchema.optional(), excludeFiles: z.array(z.string()).optional(), - if: z - .union([ - z.strictObject({ hasFile: z.string() }), - z.strictObject({ placeholderSatisfies: z.string() }), - ]) - .optional(), - for: z - .strictObject({ files: z.union([z.string(), z.array(z.string())]) }) - .optional(), + if: ConditionSchema.optional(), + for: ForSchema.optional(), must: MustPredicatesV1Schema.optional(), + mustNot: MustPredicatesV1Schema.optional(), }); export const ConfigV1Schema = z.strictObject({ diff --git a/packages/konsistent/src/core/convention-name.test.ts b/packages/konsistent/src/core/convention-name.test.ts index b090c84..99d0960 100644 --- a/packages/konsistent/src/core/convention-name.test.ts +++ b/packages/konsistent/src/core/convention-name.test.ts @@ -256,6 +256,29 @@ describe("generateConventionName", () => { }); }); + describe("mustNot", () => { + it("generates negated names for mustNot predicates", () => { + expect( + generateConventionName({ + mustNot: { exportConstants: ["debug"] }, + }) + ).toBe("must-not-export-debug-constant"); + }); + + it("generates logical names for negated boolean predicates", () => { + expect( + generateConventionName({ + mustNot: { importFromCurrentDir: true }, + }) + ).toBe("must-not-import-from-current-dir"); + expect( + generateConventionName({ + mustNot: { importFromCurrentDir: false }, + }) + ).toBe("must-import-from-current-dir"); + }); + }); + describe("useDeclarationOrder", () => { it("generates declaration order names", () => { expect( @@ -317,6 +340,14 @@ describe("generateConventionName", () => { }) ).toBe("must-have-tsx"); }); + + it("uses mustNot when the first block has no must", () => { + expect( + generateConventionName({ + must: [{ mustNot: { export: ["debug"] } }], + }) + ).toBe("must-not-export-debug"); + }); }); describe("edge cases", () => { diff --git a/packages/konsistent/src/core/convention-name.ts b/packages/konsistent/src/core/convention-name.ts index 467daf6..462f8ff 100644 --- a/packages/konsistent/src/core/convention-name.ts +++ b/packages/konsistent/src/core/convention-name.ts @@ -24,120 +24,155 @@ function getItemName(item: string | { name: string }): string { const PREDICATE_RULES: Record< string, - (opts: { items: unknown[]; predicateKey: string }) => string + (opts: { items: unknown[]; negated: boolean; predicateKey: string }) => string > = { - haveType: ({ items }) => `must-be-${items[0]}`, - haveFiles: ({ items }) => { + haveType: ({ items, negated }) => + negated ? `must-not-be-${items[0]}` : `must-be-${items[0]}`, + haveFiles: ({ items, negated }) => { const first = items[0] as string; const stripped = stripTemplateExpressions(first); const kebab = fileToKebab(stripped); - return kebab ? `must-have-${kebab}` : "must-have"; + const prefix = negated ? "must-not-have" : "must-have"; + return kebab ? `${prefix}-${kebab}` : prefix; }, - declareTypes: ({ items }) => { + declareTypes: ({ items, negated }) => { const kebab = deriveKebabFromName( getItemName(items[0] as string | { name: string }) ); - return kebab ? `must-declare-${kebab}-type` : "must-declare-type"; + const prefix = negated ? "must-not-declare" : "must-declare"; + return kebab ? `${prefix}-${kebab}-type` : `${prefix}-type`; }, - declareConstants: ({ items }) => { + declareConstants: ({ items, negated }) => { const kebab = deriveKebabFromName( getItemName(items[0] as string | { name: string }) ); - return kebab ? `must-declare-${kebab}-constant` : "must-declare-constant"; + const prefix = negated ? "must-not-declare" : "must-declare"; + return kebab ? `${prefix}-${kebab}-constant` : `${prefix}-constant`; }, - declareFunctions: ({ items }) => { + declareFunctions: ({ items, negated }) => { const kebab = deriveKebabFromName( getItemName(items[0] as string | { name: string }) ); - return kebab ? `must-declare-${kebab}-function` : "must-declare-function"; + const prefix = negated ? "must-not-declare" : "must-declare"; + return kebab ? `${prefix}-${kebab}-function` : `${prefix}-function`; }, - declareClasses: ({ items }) => { + declareClasses: ({ items, negated }) => { const kebab = deriveKebabFromName( getItemName(items[0] as string | { name: string }) ); - return kebab ? `must-declare-${kebab}-class` : "must-declare-class"; + const prefix = negated ? "must-not-declare" : "must-declare"; + return kebab ? `${prefix}-${kebab}-class` : `${prefix}-class`; }, - declareInterfaces: ({ items }) => { + declareInterfaces: ({ items, negated }) => { const kebab = deriveKebabFromName( getItemName(items[0] as string | { name: string }) ); - return kebab ? `must-declare-${kebab}-interface` : "must-declare-interface"; + const prefix = negated ? "must-not-declare" : "must-declare"; + return kebab ? `${prefix}-${kebab}-interface` : `${prefix}-interface`; }, - export: ({ items }) => { + export: ({ items, negated }) => { const kebab = deriveKebabFromName(getItemName(items[0] as string)); - return kebab ? `must-export-${kebab}` : "must-export"; + const prefix = negated ? "must-not-export" : "must-export"; + return kebab ? `${prefix}-${kebab}` : prefix; }, - exportTypes: ({ items }) => { + exportTypes: ({ items, negated }) => { const kebab = deriveKebabFromName( getItemName(items[0] as string | { name: string }) ); - return kebab ? `must-export-${kebab}-type` : "must-export-type"; + const prefix = negated ? "must-not-export" : "must-export"; + return kebab ? `${prefix}-${kebab}-type` : `${prefix}-type`; }, - exportConstants: ({ items }) => { + exportConstants: ({ items, negated }) => { const kebab = deriveKebabFromName( getItemName(items[0] as string | { name: string }) ); - return kebab ? `must-export-${kebab}-constant` : "must-export-constant"; + const prefix = negated ? "must-not-export" : "must-export"; + return kebab ? `${prefix}-${kebab}-constant` : `${prefix}-constant`; }, - exportFunctions: ({ items }) => { + exportFunctions: ({ items, negated }) => { const kebab = deriveKebabFromName( getItemName(items[0] as string | { name: string }) ); - return kebab ? `must-export-${kebab}-function` : "must-export-function"; + const prefix = negated ? "must-not-export" : "must-export"; + return kebab ? `${prefix}-${kebab}-function` : `${prefix}-function`; }, - exportClasses: ({ items }) => { + exportClasses: ({ items, negated }) => { const kebab = deriveKebabFromName( getItemName(items[0] as string | { name: string }) ); - return kebab ? `must-export-${kebab}-class` : "must-export-class"; + const prefix = negated ? "must-not-export" : "must-export"; + return kebab ? `${prefix}-${kebab}-class` : `${prefix}-class`; }, - exportInterfaces: ({ items }) => { + exportInterfaces: ({ items, negated }) => { const kebab = deriveKebabFromName( getItemName(items[0] as string | { name: string }) ); - return kebab ? `must-export-${kebab}-interface` : "must-export-interface"; + const prefix = negated ? "must-not-export" : "must-export"; + return kebab ? `${prefix}-${kebab}-interface` : `${prefix}-interface`; }, - import: ({ items }) => { + import: ({ items, negated }) => { const kebab = deriveKebabFromName( getItemName(items[0] as string | { name: string }) ); - return kebab ? `must-import-${kebab}` : "must-import"; + const prefix = negated ? "must-not-import" : "must-import"; + return kebab ? `${prefix}-${kebab}` : prefix; }, - importTypes: ({ items }) => { + importTypes: ({ items, negated }) => { const kebab = deriveKebabFromName( getItemName(items[0] as string | { name: string }) ); - return kebab ? `must-import-${kebab}-type` : "must-import-type"; + const prefix = negated ? "must-not-import" : "must-import"; + return kebab ? `${prefix}-${kebab}-type` : `${prefix}-type`; }, - importFromCurrentDir: ({ items }) => - items[0] === false - ? "must-not-import-from-current-dir" - : "must-import-from-current-dir", - importFromParents: ({ items }) => - items[0] === false - ? "must-not-import-from-parents" - : "must-import-from-parents", - importFromExternals: ({ items }) => - items[0] === false - ? "must-not-import-from-externals" - : "must-import-from-externals", - useDeclarationOrder: ({ items }) => { + importFromCurrentDir: ({ items, negated }) => + (items[0] === false) === negated + ? "must-import-from-current-dir" + : "must-not-import-from-current-dir", + importFromParents: ({ items, negated }) => + (items[0] === false) === negated + ? "must-import-from-parents" + : "must-not-import-from-parents", + importFromExternals: ({ items, negated }) => + (items[0] === false) === negated + ? "must-import-from-externals" + : "must-not-import-from-externals", + useDeclarationOrder: ({ items, negated }) => { const kebab = deriveKebabFromName(items[0] as string); + const prefix = negated ? "must-not-use" : "must-use"; return kebab - ? `must-use-${kebab}-declaration-order` - : "must-use-declaration-order"; + ? `${prefix}-${kebab}-declaration-order` + : `${prefix}-declaration-order`; }, }; export function generateConventionName(opts: { - must: MustPredicatesV1 | MustBlockV1[]; + must?: MustPredicatesV1 | MustBlockV1[]; + mustNot?: MustPredicatesV1; }): string { - const mustObj: MustPredicatesV1 = Array.isArray(opts.must) - ? opts.must[0].must - : opts.must; + const firstBlock = Array.isArray(opts.must) ? opts.must[0] : undefined; + let selected: MustPredicatesV1 | undefined; + let negated = false; + if (Array.isArray(opts.must)) { + if (firstBlock?.must) { + selected = firstBlock.must; + } else if (firstBlock?.mustNot) { + selected = firstBlock.mustNot; + negated = true; + } + } else if (opts.must) { + selected = opts.must; + } + if (!selected && opts.mustNot) { + selected = opts.mustNot; + negated = true; + } + + if (!selected) { + return "convention"; + } - const predicateKeys = Object.keys(mustObj).filter( - (k) => mustObj[k as keyof MustPredicatesV1] != null + const predicateKeys = Object.keys(selected).filter( + (k) => selected[k as keyof MustPredicatesV1] != null ); if (predicateKeys.length === 0) { @@ -145,7 +180,7 @@ export function generateConventionName(opts: { } const firstKey = predicateKeys[0]; - const firstValue = mustObj[firstKey as keyof MustPredicatesV1]; + const firstValue = selected[firstKey as keyof MustPredicatesV1]; const rule = PREDICATE_RULES[firstKey]; if (!rule) { @@ -153,7 +188,7 @@ export function generateConventionName(opts: { } const items = Array.isArray(firstValue) ? firstValue : [firstValue]; - let name = rule({ items, predicateKey: firstKey }); + let name = rule({ items, negated, predicateKey: firstKey }); const needsAndMore = predicateKeys.length > 1 || (Array.isArray(firstValue) && items.length > 1); diff --git a/packages/konsistent/src/core/runner.test.ts b/packages/konsistent/src/core/runner.test.ts index 77e4a9f..e8cc10d 100644 --- a/packages/konsistent/src/core/runner.test.ts +++ b/packages/konsistent/src/core/runner.test.ts @@ -7,10 +7,12 @@ function createMockFileSystem(opts: { globResults?: Map; files?: Set; directories?: Set; + fileContents?: Map; }): FileSystem { const globResults = opts.globResults ?? new Map(); const files = opts.files ?? new Set(); const directories = opts.directories ?? new Set(); + const fileContents = opts.fileContents ?? new Map(); return { glob(patterns: string[]): Promise { const key = patterns.sort().join(","); @@ -20,7 +22,7 @@ function createMockFileSystem(opts: { isFile: (p: string) => files.has(p), fileExists: (p: string) => files.has(p) || directories.has(p), readDir: () => [], - readFile: () => "", + readFile: (p: string) => fileContents.get(p) ?? "", }; } @@ -140,6 +142,115 @@ describe("run", () => { expect(diagnostics).toEqual([]); }); + it("reports diagnostics when mustNot scalar predicates pass", async () => { + const config: ConfigV1 = { + version: "v1", + conventions: [ + { + name: "not-files", + paths: "src/**/*.ts", + mustNot: { haveType: "file" }, + }, + ], + }; + const fs = createMockFileSystem({ + globResults: new Map([["src/**/*.ts", ["src/index.ts"]]]), + files: new Set(["src/index.ts"]), + }); + const { diagnostics } = await run({ config, fileSystem: fs }); + expect(diagnostics).toHaveLength(1); + expect(diagnostics[0].predicateName).toBe("mustNot.haveType"); + expect(diagnostics[0].message).toBe('Forbidden path type "file"'); + expect(diagnostics[0].conventionName).toBe("not-files"); + }); + + it("returns no diagnostics when mustNot scalar predicates fail", async () => { + const config: ConfigV1 = { + version: "v1", + conventions: [ + { + paths: "src/**/*.ts", + mustNot: { haveType: "file" }, + }, + ], + }; + const fs = createMockFileSystem({ + globResults: new Map([["src/**/*.ts", ["src/utils"]]]), + directories: new Set(["src/utils"]), + }); + const { diagnostics } = await run({ config, fileSystem: fs }); + expect(diagnostics).toEqual([]); + }); + + it("negates list predicates per item", async () => { + const config: ConfigV1 = { + version: "v1", + conventions: [ + { + paths: "src/module.ts", + mustNot: { exportConstants: ["debug", "missing"] }, + }, + ], + }; + const fs = createMockFileSystem({ + globResults: new Map([["src/module.ts", ["src/module.ts"]]]), + files: new Set(["src/module.ts"]), + fileContents: new Map([["src/module.ts", "export const debug = true;"]]), + }); + const { diagnostics } = await run({ config, fileSystem: fs }); + expect(diagnostics).toHaveLength(1); + expect(diagnostics[0].predicateName).toBe("mustNot.exportConstants"); + expect(diagnostics[0].message).toBe('Forbidden constant export "debug"'); + }); + + it("applies block metadata to mustNot predicates", async () => { + const config: ConfigV1 = { + version: "v1", + conventions: [ + { + name: "components", + paths: "components/{name}", + severity: "warning", + must: [ + { + name: "no-debug-tests", + if: { hasFile: "${name}.test.ts" }, + for: { files: "*.test.ts" }, + excludeFiles: ["helpers.test.ts"], + mustNot: { exportConstants: ["debug"] }, + }, + ], + }, + ], + }; + const fs = createMockFileSystem({ + globResults: new Map([ + ["components/*", ["components/Button"]], + [ + "components/Button/*.test.ts", + [ + "components/Button/Button.test.ts", + "components/Button/helpers.test.ts", + ], + ], + ]), + directories: new Set(["components/Button"]), + files: new Set([ + "components/Button/Button.test.ts", + "components/Button/helpers.test.ts", + ]), + fileContents: new Map([ + ["components/Button/Button.test.ts", "export const debug = true;"], + ["components/Button/helpers.test.ts", "export const debug = true;"], + ]), + }); + const { diagnostics } = await run({ config, fileSystem: fs }); + expect(diagnostics).toHaveLength(1); + expect(diagnostics[0].filePath).toBe("components/Button/Button.test.ts"); + expect(diagnostics[0].conventionName).toBe("no-debug-tests"); + expect(diagnostics[0].severity).toBe("warning"); + }); + it("evaluates must block when if.hasFile condition is met", async () => { const config: ConfigV1 = { version: "v1", diff --git a/packages/konsistent/src/core/runner.ts b/packages/konsistent/src/core/runner.ts index f9217fd..4606d8c 100644 --- a/packages/konsistent/src/core/runner.ts +++ b/packages/konsistent/src/core/runner.ts @@ -28,6 +28,7 @@ import { toCamelCase, toPascalCase } from "./case-utils.js"; import type { PredicateContext } from "./context.js"; import { generateConventionName } from "./convention-name.js"; import type { Diagnostic, DiagnosticSeverity } from "./diagnostics.js"; +import { createDiagnostic } from "./diagnostics.js"; import type { FileSystem } from "./filesystem.js"; import type { MatchedPath } from "./path-matcher.js"; import { matchPaths } from "./path-matcher.js"; @@ -144,13 +145,24 @@ function buildContext(opts: { }; } -function normalizeMustBlocks( - must: MustPredicatesV1 | MustBlockV1[] -): MustBlockV1[] { +function normalizeMustBlocks(opts: { + must?: MustPredicatesV1 | MustBlockV1[]; + mustNot?: MustPredicatesV1; +}): MustBlockV1[] { + const { must, mustNot } = opts; if (Array.isArray(must)) { - return must; + return mustNot ? [...must, { mustNot }] : must; } - return [{ must }]; + if (must && mustNot) { + return [{ must, mustNot }]; + } + if (must) { + return [{ must }]; + } + if (mustNot) { + return [{ mustNot }]; + } + return []; } function resolveBlockConventionName(opts: { @@ -528,7 +540,148 @@ function getOrParseFileStructure(opts: { return structure; } -function checkPredicates(opts: { +const ITEM_LEVEL_MUST_NOT_PREDICATES = new Set([ + "haveFiles", + "declareTypes", + "declareConstants", + "declareFunctions", + "declareInterfaces", + "declareClasses", + "export", + "exportTypes", + "exportConstants", + "exportFunctions", + "exportInterfaces", + "exportClasses", + "import", + "importTypes", +]); + +function resolveEntryName(opts: { + value: unknown; + context: PredicateContext; +}): string { + const { value, context } = opts; + if (typeof value === "string") { + return context.resolveTemplate(value); + } + if ( + value && + typeof value === "object" && + Object.hasOwn(value, "name") && + typeof (value as { name: unknown }).name === "string" + ) { + return context.resolveTemplate((value as { name: string }).name); + } + return "unknown"; +} + +function formatForbiddenMessage(opts: { + key: string; + value: unknown; + context: PredicateContext; +}): string { + const { key, value, context } = opts; + const name = resolveEntryName({ value, context }); + + switch (key) { + case "haveType": + return `Forbidden path type "${String(value)}"`; + case "haveFiles": + return `Forbidden file "${context.resolveTemplate(String(value))}"`; + case "declareTypes": + return `Forbidden type declaration "${name}"`; + case "declareConstants": + return `Forbidden constant declaration "${name}"`; + case "declareFunctions": + return `Forbidden function declaration "${name}"`; + case "declareInterfaces": + return `Forbidden interface declaration "${name}"`; + case "declareClasses": + return `Forbidden class declaration "${name}"`; + case "export": + return `Forbidden export "${name}"`; + case "exportTypes": + return `Forbidden type export "${name}"`; + case "exportConstants": + return `Forbidden constant export "${name}"`; + case "exportFunctions": + return `Forbidden function export "${name}"`; + case "exportInterfaces": + return `Forbidden interface export "${name}"`; + case "exportClasses": + return `Forbidden class export "${name}"`; + case "import": + return `Forbidden import "${name}"`; + case "importTypes": + return `Forbidden type import "${name}"`; + case "importFromCurrentDir": + return value === false + ? "Missing import from current directory is not allowed" + : "Forbidden import from current directory"; + case "importFromParents": + return value === false + ? "Missing import from parent directories is not allowed" + : "Forbidden import from parent directories"; + case "importFromExternals": + return value === false + ? "Missing import from external packages is not allowed" + : "Forbidden import from external packages"; + case "useDeclarationOrder": + return `Forbidden declaration order "${(value as string[]) + .map((entry) => context.resolveTemplate(entry)) + .join('", "')}"`; + case "areBarrelFiles": + return value === false + ? "Forbidden non-barrel file" + : "Forbidden barrel file"; + default: + return `Forbidden ${key}`; + } +} + +function buildSingletonPredicate(opts: { + key: string; + value: unknown; +}): MustPredicatesV1 { + return { [opts.key]: opts.value } as MustPredicatesV1; +} + +function buildMustNotChecks(opts: { + mustNot: MustPredicatesV1; +}): Array<{ key: string; predicate: MustPredicatesV1; value: unknown }> { + const checks: Array<{ + key: string; + predicate: MustPredicatesV1; + value: unknown; + }> = []; + + for (const key of Object.keys(opts.mustNot)) { + const value = opts.mustNot[key as keyof MustPredicatesV1]; + if (value === undefined) { + continue; + } + if (Array.isArray(value) && ITEM_LEVEL_MUST_NOT_PREDICATES.has(key)) { + for (const item of value) { + checks.push({ + key, + predicate: buildSingletonPredicate({ key, value: [item] }), + value: item, + }); + } + continue; + } + checks.push({ + key, + predicate: buildSingletonPredicate({ key, value }), + value, + }); + } + + return checks; +} + +function checkMustPredicates(opts: { must: MustPredicatesV1; conventionName?: string; context: PredicateContext; @@ -597,6 +750,84 @@ function checkPredicates(opts: { return diagnostics; } +function checkMustNotPredicates(opts: { + mustNot: MustPredicatesV1; + conventionName?: string; + context: PredicateContext; + fileSystem: FileSystem; + fileStructureCache: Map; + severity?: DiagnosticSeverity; +}): Diagnostic[] { + const diagnostics: Diagnostic[] = []; + const checks = buildMustNotChecks({ mustNot: opts.mustNot }); + + for (const check of checks) { + const normalDiagnostics = checkMustPredicates({ + must: check.predicate, + conventionName: opts.conventionName, + context: opts.context, + fileSystem: opts.fileSystem, + fileStructureCache: opts.fileStructureCache, + severity: opts.severity, + }); + if (normalDiagnostics.length > 0) { + continue; + } + diagnostics.push( + createDiagnostic({ + filePath: opts.context.path, + predicateName: `mustNot.${check.key}`, + message: formatForbiddenMessage({ + key: check.key, + value: check.value, + context: opts.context, + }), + conventionName: opts.conventionName, + severity: opts.severity, + }) + ); + } + + return diagnostics; +} + +function checkPredicates(opts: { + must?: MustPredicatesV1; + mustNot?: MustPredicatesV1; + conventionName?: string; + context: PredicateContext; + fileSystem: FileSystem; + fileStructureCache: Map; + severity?: DiagnosticSeverity; +}): Diagnostic[] { + const diagnostics: Diagnostic[] = []; + if (opts.must) { + diagnostics.push( + ...checkMustPredicates({ + must: opts.must, + conventionName: opts.conventionName, + context: opts.context, + fileSystem: opts.fileSystem, + fileStructureCache: opts.fileStructureCache, + severity: opts.severity, + }) + ); + } + if (opts.mustNot) { + diagnostics.push( + ...checkMustNotPredicates({ + mustNot: opts.mustNot, + conventionName: opts.conventionName, + context: opts.context, + fileSystem: opts.fileSystem, + fileStructureCache: opts.fileStructureCache, + severity: opts.severity, + }) + ); + } + return diagnostics; +} + async function evaluateForBlock(opts: { block: MustBlockV1; parentContext: PredicateContext; @@ -640,6 +871,7 @@ async function evaluateForBlock(opts: { } return checkPredicates({ must: block.must, + mustNot: block.mustNot, conventionName, context: parentContext, fileSystem, @@ -703,6 +935,7 @@ async function evaluateForBlock(opts: { diagnostics.push( ...checkPredicates({ must: block.must, + mustNot: block.mustNot, conventionName, context: forContext, fileSystem, @@ -771,9 +1004,16 @@ export async function run(opts: { for (let i = 0; i < config.conventions.length; i++) { const convention = config.conventions[i]; const matched = matchResults[i]; - const blocks = normalizeMustBlocks(convention.must); + const blocks = normalizeMustBlocks({ + must: convention.must, + mustNot: convention.mustNot, + }); const conventionName = - convention.name ?? generateConventionName({ must: convention.must }); + convention.name ?? + generateConventionName({ + must: convention.must, + mustNot: convention.mustNot, + }); const severity: DiagnosticSeverity = convention.severity ?? "error"; const staticPlaceholders = buildStaticPlaceholders({ raw: convention.placeholders, From 22f8b6ff31d9130a825ecc16570298d4d1260020 Mon Sep 17 00:00:00 2001 From: Felix Arntz Date: Thu, 18 Jun 2026 20:46:55 -0500 Subject: [PATCH 2/3] add docs --- docs/guides/authoring-reusable-conventions.md | 2 +- docs/reference/conditional-rules.md | 6 ++++-- docs/reference/configuration.md | 17 +++++++++++++++-- docs/reference/path-patterns.md | 12 ++++++------ docs/reference/predicates.md | 14 ++++++++++++-- docs/reference/reusable-conventions.md | 16 ++++++++-------- 6 files changed, 46 insertions(+), 21 deletions(-) diff --git a/docs/guides/authoring-reusable-conventions.md b/docs/guides/authoring-reusable-conventions.md index a454a1c..434b089 100644 --- a/docs/guides/authoring-reusable-conventions.md +++ b/docs/guides/authoring-reusable-conventions.md @@ -95,7 +95,7 @@ export const conventions = defineConventions([ A reusable convention has the same fields as a hand-written one with two adjustments: - `name` and `description` are **required** (consumers see them in error messages and source listings). -- `must` must use the **flat object form** (`MustPredicates`). The `MustBlock[]` form is not allowed in reusable conventions — see [Restrictions](../reference/reusable-conventions.md#restrictions). +- `must` and `mustNot` must use the **flat object form** (`MustPredicates`). The `MustBlock[]` form is not allowed in reusable conventions — see [Restrictions](../reference/reusable-conventions.md#restrictions). - `paths` is **optional**. Omit it to force consumers to supply `paths` at the use-site (which is useful when the right pattern depends on the consuming project's layout). When `paths` is omitted, consumers can only reference the convention via the `use` form. ## 3. Build and verify diff --git a/docs/reference/conditional-rules.md b/docs/reference/conditional-rules.md index 0df804d..94825f7 100644 --- a/docs/reference/conditional-rules.md +++ b/docs/reference/conditional-rules.md @@ -50,13 +50,15 @@ Switch from object to array form when you need: "if": { "hasFile": "index.test.tsx" }, "for": { "files": "index.test.tsx" }, "excludeFiles": ["components/legacy/**"], - "must": { "import": [{ "name": "render", "from": "@/test-utils" }] } + "must": { "import": [{ "name": "render", "from": "@/test-utils" }] }, + "mustNot": { "exportConstants": ["debug"] } } ``` | Field | Type | Required | Description | | --- | --- | --- | --- | -| `must` | `MustPredicates` | yes | The predicates this block enforces. See [predicates.md](./predicates.md). | +| `must` | `MustPredicates` | yes, unless `mustNot` is present | The predicates this block enforces. See [predicates.md](./predicates.md). | +| `mustNot` | `MustPredicates` | yes, unless `must` is present | The predicates this block forbids. | | `if` | `{ hasFile }` or `{ placeholderSatisfies }` | no | Gate. Block runs only if the condition holds. | | `for` | `{ files: string \| string[] }` | no | Scope. Predicates apply to files matching this pattern within the parent path. | | `excludeFiles` | `string[]` | no | Glob patterns to exclude from the block. | diff --git a/docs/reference/configuration.md b/docs/reference/configuration.md index f47994e..d95a7d8 100644 --- a/docs/reference/configuration.md +++ b/docs/reference/configuration.md @@ -34,7 +34,7 @@ The `konsistent.json` file declares the structural conventions the CLI enforces. ## Conventions -A convention is a rule that says "files matching `paths` must satisfy `must`." +A convention is a rule that says "files matching `paths` must satisfy `must` and must not satisfy `mustNot`." ```json { @@ -51,7 +51,8 @@ A convention is a rule that says "files matching `paths` must satisfy `must`." | Field | Type | Required | Description | | --- | --- | --- | --- | | `paths` | `string` or `string[]` | yes | Glob pattern(s) with `{placeholder}` extraction. See [path-patterns.md](./path-patterns.md). | -| `must` | `MustPredicates` or `MustBlock[]` | yes | The conditions that matched paths must satisfy. See [predicates.md](./predicates.md) and [conditional-rules.md](./conditional-rules.md). | +| `must` | `MustPredicates` or `MustBlock[]` | yes, unless `mustNot` is present | The conditions that matched paths must satisfy. See [predicates.md](./predicates.md) and [conditional-rules.md](./conditional-rules.md). | +| `mustNot` | `MustPredicates` | yes, unless `must` is present | The conditions that matched paths must not satisfy. Unlike `must`, this only accepts the object form. | | `name` | string matching `[a-z0-9-]+` | no | Identifier shown in violation reports. | | `description` | string | no | Human-readable explanation. | | `severity` | `"error"` \| `"warning"` | no, default `"error"` | See [Severity](#severity). | @@ -91,6 +92,18 @@ All listed predicates apply unconditionally to every matched path. See [predicat Each entry is a `MustBlock` that can have `if`, `for`, `excludeFiles`, `name`, and `description`. See [conditional-rules.md](./conditional-rules.md). An entry may alternatively be a reusable-convention reference of the form `{ "use": "/", ...overrides }`, which expands into a `MustBlock` — see [reusable-conventions.md](./reusable-conventions.md#use-inside-a-parents-must). +## `mustNot`: negated predicates + +`mustNot` accepts the same predicate object shape as object-form `must`, but reverses the result. For example, this fails when a matched file exports `debug`: + +```json +"mustNot": { + "exportConstants": ["debug"] +} +``` + +`mustNot` is only object-form. It cannot contain a `MustBlock[]`, string references, or `{ "use": ... }` references. Use it inside a `must` block when you need `if`, `for`, or `excludeFiles` scoping. + ## Severity By default, convention violations are errors and produce a non-zero exit code. Mark a convention as a warning with `"severity": "warning"`: diff --git a/docs/reference/path-patterns.md b/docs/reference/path-patterns.md index 908a2a1..3dd2c83 100644 --- a/docs/reference/path-patterns.md +++ b/docs/reference/path-patterns.md @@ -1,6 +1,6 @@ # Path patterns -A convention's `paths` field declares which files or directories it applies to. Path patterns combine glob syntax with **placeholders** that capture parts of the path so they can be referenced inside `must` predicates. +A convention's `paths` field declares which files or directories it applies to. Path patterns combine glob syntax with **placeholders** that capture parts of the path so they can be referenced inside `must` and `mustNot` predicates. ## Glob basics @@ -19,7 +19,7 @@ A convention's `paths` field declares which files or directories it applies to. ## Placeholders -Wrap a path segment in `{name}` to extract it as a placeholder. The captured value becomes available inside `must` predicates as `${name}`. +Wrap a path segment in `{name}` to extract it as a placeholder. The captured value becomes available inside `must` and `mustNot` predicates as `${name}`. ```json { @@ -38,7 +38,7 @@ A placeholder matches one path segment by default. Use it where a literal value "paths": "services/{svcName}/index.ts" ``` -The same placeholder can appear in both the path and the `must` predicates: +The same placeholder can appear in both the path and the predicate values: ```json { @@ -53,7 +53,7 @@ The same placeholder can appear in both the path and the `must` predicates: ## Static placeholder values -Sometimes a placeholder name is used inside `must`, but the consumer's tree has only one concrete value and there's no wildcard segment to capture it from. Use the optional `placeholders` field on the convention to supply the value directly: +Sometimes a placeholder name is used inside `must` or `mustNot`, but the consumer's tree has only one concrete value and there's no wildcard segment to capture it from. Use the optional `placeholders` field on the convention to supply the value directly: ```json { @@ -65,7 +65,7 @@ Sometimes a placeholder name is used inside `must`, but the consumer's tree has } ``` -This is equivalent to `paths: "packages/{providerId}/src/index.ts"` when the tree contains exactly one provider folder, but doesn't require a wildcard. It's especially useful when consuming a [reusable convention](./reusable-conventions.md) whose `must` references a placeholder that the local tree doesn't have a wildcard for. +This is equivalent to `paths: "packages/{providerId}/src/index.ts"` when the tree contains exactly one provider folder, but doesn't require a wildcard. It's especially useful when consuming a [reusable convention](./reusable-conventions.md) whose predicates reference a placeholder that the local tree doesn't have a wildcard for. A name may not appear in both a `{name}` placeholder in `paths` and in `placeholders` — pick one source of truth. @@ -98,7 +98,7 @@ For acronyms like `openai` → `OpenAI` instead of `Openai`, declare overrides w ### Template substitutions in predicates -Templates work anywhere a string appears in `must`: +Templates work anywhere a string appears in `must` or `mustNot`: ```json { diff --git a/docs/reference/predicates.md b/docs/reference/predicates.md index 628a30f..852e833 100644 --- a/docs/reference/predicates.md +++ b/docs/reference/predicates.md @@ -1,6 +1,6 @@ # Predicates -Predicates are the assertions inside a convention's `must` block. Each predicate checks one structural property of the matched path. Listing multiple predicates in the same `must` is equivalent to AND — they all must pass. +Predicates are the assertions inside a convention's `must` or `mustNot` block. Each predicate checks one structural property of the matched path. Listing multiple predicates in the same `must` is equivalent to AND — they all must pass. Listing predicates in `mustNot` reverses the result — a matched path fails when it satisfies one of those predicates. The full machine-readable schema lives at `node_modules/konsistent/konsistent.schema.json`. @@ -34,6 +34,16 @@ The full machine-readable schema lives at `node_modules/konsistent/konsistent.sc All predicates support template substitutions in their string values — see [path-patterns.md](./path-patterns.md#case-transformations) for the full case-transformation catalog. +`mustNot` accepts only the object form: + +```json +"mustNot": { + "exportConstants": ["debug"] +} +``` + +It does not accept `MustBlock[]` or reusable-convention references. + --- ## Filesystem predicates @@ -423,4 +433,4 @@ Multiple predicates in the same `must` are AND-ed: For OR-style logic (apply different predicates to different files), use [conditional rules](./conditional-rules.md) — the array form of `must` with `if`/`for` blocks. -Note that **reusable conventions** (those published via `@konsistent/convention` and consumed via `conventionSources`) are restricted to the flat object form of `must` — the `MustBlock[]` form is unavailable on the author side. Hand-written conventions in your own `konsistent.json` retain the full surface area. See [reusable-conventions.md](./reusable-conventions.md#restrictions). +Note that **reusable conventions** (those published via `@konsistent/convention` and consumed via `conventionSources`) are restricted to flat object-form `must` and `mustNot` predicates — the `MustBlock[]` form is unavailable on the author side. Hand-written conventions in your own `konsistent.json` can still use `MustBlock[]` in `must`. See [reusable-conventions.md](./reusable-conventions.md#restrictions). diff --git a/docs/reference/reusable-conventions.md b/docs/reference/reusable-conventions.md index bb8925b..a663bd0 100644 --- a/docs/reference/reusable-conventions.md +++ b/docs/reference/reusable-conventions.md @@ -59,7 +59,7 @@ A bare string `"/"` inlines the named reusable convention as-is. T ### Object reference (`use` form) -`{ "use": "/", ...overrides }` references a reusable convention and overlays your overrides on top of it. The override fields available are `paths`, `placeholders`, `excludeFiles`, `severity`, `if`, `for`, and `must` — the same optional fields a hand-written convention has, minus `name` and `description` (which come from the source). +`{ "use": "/", ...overrides }` references a reusable convention and overlays your overrides on top of it. The override fields available are `paths`, `placeholders`, `excludeFiles`, `severity`, `if`, `for`, `must`, and `mustNot` — the same optional fields a hand-written convention has, minus `name` and `description` (which come from the source). Use this form when the reusable convention has no `paths` (so you must supply them) or when you want to adjust a field for your project. @@ -121,9 +121,9 @@ A hand-written convention whose `must` is a `MustBlock[]` may also reference a r } ``` -Allowed override keys at this nesting level are every field a hand-written `MustBlock` exposes — `name`, `description`, `if`, `for`, `excludeFiles`, and `must`. Top-level-only fields (`paths`, `severity`) are not accepted at the use-site, and the referenced reusable convention must not declare them either: a reusable that ships `paths` or `severity` can only be referenced from the top level of `conventions[]`. Authors who want their reusable to be usable in both contexts should publish it without those fields. +Allowed override keys at this nesting level are every field a hand-written `MustBlock` exposes — `name`, `description`, `if`, `for`, `excludeFiles`, `must`, and `mustNot`. Top-level-only fields (`paths`, `severity`) are not accepted at the use-site, and the referenced reusable convention must not declare them either: a reusable that ships `paths` or `severity` can only be referenced from the top level of `conventions[]`. Authors who want their reusable to be usable in both contexts should publish it without those fields. -Override merge follows the same rules as the top-level `use` form: arrays replace, primitives replace, and `must` deep-merges with the inherited predicates. +Override merge follows the same rules as the top-level `use` form: arrays replace, primitives replace, and `must`/`mustNot` deep-merge with the inherited predicates. ## Merge semantics @@ -131,7 +131,7 @@ When you write `{ use: "/", ...overrides }`, `konsistent` deep-mer | Field kind | Rule | | --- | --- | -| Plain object (e.g. `must`, nested predicate definitions) | Recursive deep-merge. Keys you supply replace the inherited value; keys you omit pass through. | +| Plain object (e.g. `must`, `mustNot`, nested predicate definitions) | Recursive deep-merge. Keys you supply replace the inherited value; keys you omit pass through. | | Array (e.g. `paths`, `excludeFiles`, predicate lists like `haveFiles`, `declareFunctions`, `export`, `exportFunctions`) | Your array fully replaces the inherited array. Use `"excludeFiles": []` to clear an inherited list. | | Primitive (e.g. `severity`, `description`) | Your value replaces the inherited value. | @@ -184,14 +184,14 @@ Note that `excludeFiles` was fully replaced (array-replace), while `must` was de ## Restrictions -- **Reusable conventions only support the object form of `must`.** They cannot ship the `MustBlock[]` form. This keeps override semantics predictable — you always know the merge target is a flat predicate object. Your own hand-written conventions are unrestricted; you can still use `MustBlock[]` there. See [predicates.md](./predicates.md). +- **Reusable conventions only support object-form `must` and `mustNot`.** They cannot ship the `MustBlock[]` form. This keeps override semantics predictable — you always know the merge target is a flat predicate object. Your own hand-written conventions can still use `MustBlock[]` in `must`; `mustNot` is object-form only everywhere. See [predicates.md](./predicates.md). - **The `conventionSources` value is a single string.** No object form (`{ package: ... }` / `{ path: ... }`) — auto-detection by leading `.` / `/` is unambiguous. - **No cross-source merging.** Two `conventionSources` entries cannot be merged into a single prefix. If two packages happen to ship a convention with the same name, your vendor prefix scopes them. - **`MustBlock[]` cannot be introduced via override.** Because the source convention's `must` is always object-form, deep-merge keeps the result object-form. ## Placeholder validation -After expansion, `konsistent` walks every string inside each merged convention's `must` and checks that each `${placeholder}` referenced is declared as `{placeholder}` in at least one `paths` entry. This catches mismatches between a reusable convention's templates and the `paths` you supplied at the use-site, before any file is scanned. +After expansion, `konsistent` walks every string inside each merged convention's `must` and `mustNot` and checks that each `${placeholder}` referenced is declared as `{placeholder}` in at least one `paths` entry. This catches mismatches between a reusable convention's templates and the `paths` you supplied at the use-site, before any file is scanned. ## Error reference @@ -209,7 +209,7 @@ All errors below are returned from `loadConfig()` as `{ success: false, error }` | npm source missing exports condition | `Convention source "" → "": package does not declare an exports["./konsistent"] entry.` | The source package isn't a reusable-convention package; check the spelling or pick a different source. | | Reusable-convention package fails schema validation | `Convention source "" → "": invalid reusable-convention package at : ` | The author shipped an invalid package; report upstream. | | Empty source value | `Convention source "" has empty value.` | Supply a path or npm specifier. | -| Placeholder used in `must` but not declared in `paths` or `placeholders` | `Convention "" references "${}" in , but neither paths nor placeholders declare "{}".` | Either declare the placeholder in `paths` or `placeholders`, or remove the unresolved template from `must`. | +| Placeholder used in `must` or `mustNot` but not declared in `paths` or `placeholders` | `Convention "" references "${}" in , but neither paths nor placeholders declare "{}".` | Either declare the placeholder in `paths` or `placeholders`, or remove the unresolved template. | | `use` inside `must[]` points at a reusable that declares `paths`/`severity` | `Convention "/" referenced in conventions[].must[] declares top-level-only field(s) "". Such conventions can only be referenced at the top level of conventions[]. Either remove the field(s) from the source convention, or move the reference out of must[].` | Drop `paths`/`severity` from the reusable, or reference it directly from `conventions[]`. | `` in the placeholder error is the convention's `name`, the `/` reference, or `conventions[]` — whichever was available. @@ -218,4 +218,4 @@ All errors below are returned from `loadConfig()` as `{ success: false, error }` - [Authoring reusable conventions](../guides/authoring-reusable-conventions.md) — publish your own. - [konsistent.json reference](./configuration.md) — the surrounding config shape. -- [Path patterns](./path-patterns.md) — placeholder syntax used in `paths` and `must`. +- [Path patterns](./path-patterns.md) — placeholder syntax used in `paths`, `must`, and `mustNot`. From 4ccc20adac0c20e21f4736ff676b7ed28f1ee5a8 Mon Sep 17 00:00:00 2001 From: Felix Arntz Date: Thu, 18 Jun 2026 20:47:04 -0500 Subject: [PATCH 3/3] add e2e tests --- .../ai-toolkit/konsistent-reverse.json | 29 ++++ .../konsistent-reverse.json | 15 ++ .../barrel-files/konsistent-reverse.json | 15 ++ .../konsistent-reverse.json | 12 ++ .../declaration-order/konsistent-reverse.json | 12 ++ .../konsistent-reverse.json | 28 ++++ .../konsistent-reverse.json | 28 ++++ .../konsistent-reverse.json | 26 ++++ .../konsistent-reverse.json | 26 ++++ .../plugin-system/konsistent-reverse.json | 21 +++ e2e/must-not.test.ts | 130 ++++++++++++++++++ 11 files changed, 342 insertions(+) create mode 100644 e2e/fixtures/ai-toolkit/konsistent-reverse.json create mode 100644 e2e/fixtures/barrel-files-broken/konsistent-reverse.json create mode 100644 e2e/fixtures/barrel-files/konsistent-reverse.json create mode 100644 e2e/fixtures/declaration-order-broken/konsistent-reverse.json create mode 100644 e2e/fixtures/declaration-order/konsistent-reverse.json create mode 100644 e2e/fixtures/declaration-predicates-broken/konsistent-reverse.json create mode 100644 e2e/fixtures/declaration-predicates/konsistent-reverse.json create mode 100644 e2e/fixtures/import-source-groups-broken/konsistent-reverse.json create mode 100644 e2e/fixtures/import-source-groups/konsistent-reverse.json create mode 100644 e2e/fixtures/plugin-system/konsistent-reverse.json create mode 100644 e2e/must-not.test.ts diff --git a/e2e/fixtures/ai-toolkit/konsistent-reverse.json b/e2e/fixtures/ai-toolkit/konsistent-reverse.json new file mode 100644 index 0000000..e00881b --- /dev/null +++ b/e2e/fixtures/ai-toolkit/konsistent-reverse.json @@ -0,0 +1,29 @@ +{ + "version": "v1", + "conventions": [ + { + "name": "no-provider-barrel-exports", + "paths": "packages/{providerId}/src/index.ts", + "mustNot": { + "export": ["${providerId}"], + "exportTypes": [ + "${providerId.toPascalCase()}Provider", + "${providerId.toPascalCase()}ProviderSettings" + ] + } + }, + { + "name": "no-provider-interface", + "paths": "packages/{providerId}/src/${providerId}-provider.ts", + "mustNot": { + "exportInterfaces": [ + { + "name": "${providerId.toPascalCase()}Provider", + "extend": "ProviderV1" + } + ], + "importTypes": [{ "name": "ProviderV1", "from": "@ai-toolkit/core" }] + } + } + ] +} diff --git a/e2e/fixtures/barrel-files-broken/konsistent-reverse.json b/e2e/fixtures/barrel-files-broken/konsistent-reverse.json new file mode 100644 index 0000000..be6666f --- /dev/null +++ b/e2e/fixtures/barrel-files-broken/konsistent-reverse.json @@ -0,0 +1,15 @@ +{ + "version": "v1", + "conventions": [ + { + "name": "barrels-must-not-be-pure", + "paths": "src/{moduleName}", + "must": [ + { + "for": { "files": "index.ts" }, + "mustNot": { "areBarrelFiles": true } + } + ] + } + ] +} diff --git a/e2e/fixtures/barrel-files/konsistent-reverse.json b/e2e/fixtures/barrel-files/konsistent-reverse.json new file mode 100644 index 0000000..be6666f --- /dev/null +++ b/e2e/fixtures/barrel-files/konsistent-reverse.json @@ -0,0 +1,15 @@ +{ + "version": "v1", + "conventions": [ + { + "name": "barrels-must-not-be-pure", + "paths": "src/{moduleName}", + "must": [ + { + "for": { "files": "index.ts" }, + "mustNot": { "areBarrelFiles": true } + } + ] + } + ] +} diff --git a/e2e/fixtures/declaration-order-broken/konsistent-reverse.json b/e2e/fixtures/declaration-order-broken/konsistent-reverse.json new file mode 100644 index 0000000..5a7dfaa --- /dev/null +++ b/e2e/fixtures/declaration-order-broken/konsistent-reverse.json @@ -0,0 +1,12 @@ +{ + "version": "v1", + "conventions": [ + { + "name": "declarations-not-in-order", + "paths": "src/order.ts", + "mustNot": { + "useDeclarationOrder": ["alpha", "Beta", "gamma", "missing"] + } + } + ] +} diff --git a/e2e/fixtures/declaration-order/konsistent-reverse.json b/e2e/fixtures/declaration-order/konsistent-reverse.json new file mode 100644 index 0000000..5a7dfaa --- /dev/null +++ b/e2e/fixtures/declaration-order/konsistent-reverse.json @@ -0,0 +1,12 @@ +{ + "version": "v1", + "conventions": [ + { + "name": "declarations-not-in-order", + "paths": "src/order.ts", + "mustNot": { + "useDeclarationOrder": ["alpha", "Beta", "gamma", "missing"] + } + } + ] +} diff --git a/e2e/fixtures/declaration-predicates-broken/konsistent-reverse.json b/e2e/fixtures/declaration-predicates-broken/konsistent-reverse.json new file mode 100644 index 0000000..2a0d5ab --- /dev/null +++ b/e2e/fixtures/declaration-predicates-broken/konsistent-reverse.json @@ -0,0 +1,28 @@ +{ + "version": "v1", + "conventions": [ + { + "name": "no-local-declarations", + "paths": "src/module.ts", + "mustNot": { + "declareTypes": ["LocalType"], + "declareConstants": ["localConstant"], + "declareFunctions": [ + { + "name": "createLocal", + "receiveParamOfType": "LocalConfig", + "returnValueOfType": "LocalResult" + } + ], + "declareInterfaces": [{ "name": "LocalInterface", "extend": "BaseInterface" }], + "declareClasses": [ + { + "name": "LocalClass", + "extend": "BaseClass", + "implement": ["Serializable"] + } + ] + } + } + ] +} diff --git a/e2e/fixtures/declaration-predicates/konsistent-reverse.json b/e2e/fixtures/declaration-predicates/konsistent-reverse.json new file mode 100644 index 0000000..2a0d5ab --- /dev/null +++ b/e2e/fixtures/declaration-predicates/konsistent-reverse.json @@ -0,0 +1,28 @@ +{ + "version": "v1", + "conventions": [ + { + "name": "no-local-declarations", + "paths": "src/module.ts", + "mustNot": { + "declareTypes": ["LocalType"], + "declareConstants": ["localConstant"], + "declareFunctions": [ + { + "name": "createLocal", + "receiveParamOfType": "LocalConfig", + "returnValueOfType": "LocalResult" + } + ], + "declareInterfaces": [{ "name": "LocalInterface", "extend": "BaseInterface" }], + "declareClasses": [ + { + "name": "LocalClass", + "extend": "BaseClass", + "implement": ["Serializable"] + } + ] + } + } + ] +} diff --git a/e2e/fixtures/import-source-groups-broken/konsistent-reverse.json b/e2e/fixtures/import-source-groups-broken/konsistent-reverse.json new file mode 100644 index 0000000..68348e2 --- /dev/null +++ b/e2e/fixtures/import-source-groups-broken/konsistent-reverse.json @@ -0,0 +1,26 @@ +{ + "version": "v1", + "conventions": [ + { + "name": "no-current-dir-imports", + "paths": "src/current.ts", + "mustNot": { + "importFromCurrentDir": true + } + }, + { + "name": "no-parent-imports", + "paths": "src/parent.ts", + "mustNot": { + "importFromParents": true + } + }, + { + "name": "no-external-imports", + "paths": "src/external.ts", + "mustNot": { + "importFromExternals": true + } + } + ] +} diff --git a/e2e/fixtures/import-source-groups/konsistent-reverse.json b/e2e/fixtures/import-source-groups/konsistent-reverse.json new file mode 100644 index 0000000..68348e2 --- /dev/null +++ b/e2e/fixtures/import-source-groups/konsistent-reverse.json @@ -0,0 +1,26 @@ +{ + "version": "v1", + "conventions": [ + { + "name": "no-current-dir-imports", + "paths": "src/current.ts", + "mustNot": { + "importFromCurrentDir": true + } + }, + { + "name": "no-parent-imports", + "paths": "src/parent.ts", + "mustNot": { + "importFromParents": true + } + }, + { + "name": "no-external-imports", + "paths": "src/external.ts", + "mustNot": { + "importFromExternals": true + } + } + ] +} diff --git a/e2e/fixtures/plugin-system/konsistent-reverse.json b/e2e/fixtures/plugin-system/konsistent-reverse.json new file mode 100644 index 0000000..200f824 --- /dev/null +++ b/e2e/fixtures/plugin-system/konsistent-reverse.json @@ -0,0 +1,21 @@ +{ + "version": "v1", + "conventions": [ + { + "name": "no-plugin-directories", + "paths": "plugins/{pluginName}", + "mustNot": { + "haveType": "directory", + "haveFiles": ["index.ts", "manifest.json", "README.md"] + } + }, + { + "name": "no-plugin-exports", + "paths": "plugins/{pluginName}/index.ts", + "mustNot": { + "export": ["activate", "deactivate"], + "exportConstants": ["pluginId"] + } + } + ] +} diff --git a/e2e/must-not.test.ts b/e2e/must-not.test.ts new file mode 100644 index 0000000..a36d6ac --- /dev/null +++ b/e2e/must-not.test.ts @@ -0,0 +1,130 @@ +import { execFile as execFileCb } from "node:child_process"; +import { resolve } from "node:path"; +import { promisify } from "node:util"; +import { describe, expect, it } from "vitest"; + +const execFile = promisify(execFileCb); + +const cliBinary = resolve( + import.meta.dirname, + "../packages/konsistent/dist/index.js" +); + +const fixturesDir = resolve(import.meta.dirname, "fixtures"); + +function runCli(opts: { cwd: string; configFile?: string }) { + const args = ["check"]; + if (opts.configFile) { + args.push("--config-path", resolve(opts.cwd, opts.configFile)); + } + return execFile("node", [cliBinary, ...args], { + cwd: opts.cwd, + env: { ...process.env, GITHUB_ACTIONS: "" }, + }); +} + +async function expectCheckFails(opts: { + cwd: string; + configFile: string; + stdoutIncludes: string[]; +}) { + try { + await runCli({ cwd: opts.cwd, configFile: opts.configFile }); + expect.fail("Expected check to exit with code 1"); + } catch (err: unknown) { + const error = err as { stdout: string; code: number; status: number }; + expect(error.code ?? error.status).toBe(1); + for (const expected of opts.stdoutIncludes) { + expect(error.stdout).toContain(expected); + } + } +} + +describe("mustNot reverse configs", () => { + it("fails when file and export predicates are present", async () => { + await expectCheckFails({ + cwd: resolve(fixturesDir, "plugin-system"), + configFile: "konsistent-reverse.json", + stdoutIncludes: [ + 'Forbidden path type "directory"', + 'Forbidden constant export "pluginId"', + ], + }); + }); + + it("fails when export and import predicates are present", async () => { + await expectCheckFails({ + cwd: resolve(fixturesDir, "ai-toolkit"), + configFile: "konsistent-reverse.json", + stdoutIncludes: [ + 'Forbidden export "openai"', + 'Forbidden type import "ProviderV1"', + ], + }); + }); + + it("fails for present local declarations and passes for exported ones", async () => { + await expectCheckFails({ + cwd: resolve(fixturesDir, "declaration-predicates"), + configFile: "konsistent-reverse.json", + stdoutIncludes: [ + 'Forbidden type declaration "LocalType"', + 'Forbidden function declaration "createLocal"', + ], + }); + await expect( + runCli({ + cwd: resolve(fixturesDir, "declaration-predicates-broken"), + configFile: "konsistent-reverse.json", + }) + ).resolves.not.toThrow(); + }); + + it("fails for matching declaration order and passes for broken order", async () => { + await expectCheckFails({ + cwd: resolve(fixturesDir, "declaration-order"), + configFile: "konsistent-reverse.json", + stdoutIncludes: [ + 'Forbidden declaration order "alpha", "Beta", "gamma", "missing"', + ], + }); + await expect( + runCli({ + cwd: resolve(fixturesDir, "declaration-order-broken"), + configFile: "konsistent-reverse.json", + }) + ).resolves.not.toThrow(); + }); + + it("fails for present import source groups and passes when they are absent", async () => { + await expectCheckFails({ + cwd: resolve(fixturesDir, "import-source-groups"), + configFile: "konsistent-reverse.json", + stdoutIncludes: [ + "Forbidden import from current directory", + "Forbidden import from parent directories", + "Forbidden import from external packages", + ], + }); + await expect( + runCli({ + cwd: resolve(fixturesDir, "import-source-groups-broken"), + configFile: "konsistent-reverse.json", + }) + ).resolves.not.toThrow(); + }); + + it("fails for pure barrels and passes for non-barrels", async () => { + await expectCheckFails({ + cwd: resolve(fixturesDir, "barrel-files"), + configFile: "konsistent-reverse.json", + stdoutIncludes: ["Forbidden barrel file"], + }); + await expect( + runCli({ + cwd: resolve(fixturesDir, "barrel-files-broken"), + configFile: "konsistent-reverse.json", + }) + ).resolves.not.toThrow(); + }); +});