@konvert7/klint 0.31.0 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -446,7 +446,7 @@ arch:
446
446
  in: ["src/**"]
447
447
  ```
448
448
 
449
- Set `countDocComments: true` on either rule to count doc-comments toward the limit as well. Both rules run in the TS and Rust engines; Python (`#`) and Swift (`//`, `/* */`) files are enforced by the Rust engine.
449
+ Set `countDocComments: true` on either rule to count doc-comments toward the limit as well. Both rules run in the TS and Rust engines; Python (`#`), Swift (`//`, `/* */`), and C# (`//`, `/* */`) files are enforced by the Rust engine.
450
450
 
451
451
  Some comments are machinery rather than prose — markers a build or codegen step reads, region pragmas — and counting them makes both limits measure the wrong thing. `ignore` exempts them, on either rule:
452
452
 
@@ -462,33 +462,33 @@ An ignored line still counts in the density denominator — it is a physical lin
462
462
 
463
463
  ### Language Support
464
464
 
465
- klint scans TypeScript/JavaScript, Python, Swift, and Rust. Every architecture rule is enforced from the same `klint.yaml`, but what a rule can *see* depends on what the language actually has.
466
-
467
- | | TypeScript / JavaScript | Python | Swift | Rust |
468
- |---|---|---|---|---|
469
- | **Engines** | `ts`, `rust` | `rust` | `rust` | `rust` |
470
- | **Parser** | TypeScript compiler API (TS engine), tree-sitter (Rust engine) | tree-sitter | tree-sitter | tree-sitter |
471
- | `arch.imports` — static | ✅ | ✅ | ✅ | ✅ `use` |
472
- | `arch.imports` — dynamic | ✅ `import()` | ✅ `importlib.import_module`, `__import__` | — | — |
473
- | `arch.imports` — re-exports | ✅ `export … from` | — | ✅ `@_exported import` | ✅ `pub use` |
474
- | `arch.imports` — multi-target | — | ✅ `import a, b` | — | ✅ `use a::{b, c}` |
475
- | `type-only: allow` | ✅ `import type` | ✅ `if TYPE_CHECKING:` | — | — |
476
- | Path aliases | ✅ `tsconfig.json` `paths` | — | — | — |
477
- | Module resolution | relative paths + alias chains | relative, absolute, and PEP 420 namespace packages | module name against project dirs and file stems | `crate::`/`self::`/`super::` against the directory tree |
478
- | `deny-packages` | ✅ `/` segments | ✅ `.` segments | ✅ `.` segments, module granularity | ✅ `::` segments |
479
- | `arch.forbidden` / `singleton` — `pattern` | ✅ | ✅ | ✅ | ✅ |
480
- | `arch.forbidden` / `singleton` — `jsx-element` | ✅ | — | — | — |
481
- | `arch.maxLines` | ✅ | ✅ | ✅ | ✅ |
482
- | `arch.maxCommentDensity` / `maxCommentBlock` | ✅ `//`, `/* */`, `/** */` docs | ✅ `#` | ✅ `//`, `/* */`, `///` and `/** */` docs | ✅ `//`, `/* */`, `///` and `//!` docs |
483
- | Built-in rules and plugins | ✅ | ✗ | ✗ | ✗ |
484
- | Custom rules (`klint.rules.ts`) | ✅ | ✗ | ✗ | ✗ |
485
- | Type-aware rules | ✅ | ✗ | ✗ | ✗ |
465
+ klint scans TypeScript/JavaScript, Python, Swift, Rust, and C#. Every architecture rule is enforced from the same `klint.yaml`, but what a rule can *see* depends on what the language actually has.
466
+
467
+ | | TypeScript / JavaScript | Python | Swift | Rust | C# |
468
+ |---|---|---|---|---|---|
469
+ | **Engines** | `ts`, `rust` | `rust` | `rust` | `rust` | `rust` |
470
+ | **Parser** | TypeScript compiler API (TS engine), tree-sitter (Rust engine) | tree-sitter | tree-sitter | tree-sitter | tree-sitter |
471
+ | `arch.imports` — static | ✅ | ✅ | ✅ | ✅ `use` | ✅ `using` |
472
+ | `arch.imports` — dynamic | ✅ `import()` | ✅ `importlib.import_module`, `__import__` | — | — | — |
473
+ | `arch.imports` — re-exports | ✅ `export … from` | — | ✅ `@_exported import` | ✅ `pub use` | — |
474
+ | `arch.imports` — multi-target | — | ✅ `import a, b` | — | ✅ `use a::{b, c}` | — |
475
+ | `type-only: allow` | ✅ `import type` | ✅ `if TYPE_CHECKING:` | — | — | — |
476
+ | Path aliases | ✅ `tsconfig.json` `paths` | — | — | — | — |
477
+ | Module resolution | relative paths + alias chains | relative, absolute, and PEP 420 namespace packages | module name against project dirs and file stems | `crate::`/`self::`/`super::` against the directory tree | `using` namespace against project namespace declarations |
478
+ | `deny-packages` | ✅ `/` segments | ✅ `.` segments | ✅ `.` segments, module granularity | ✅ `::` segments | ✅ `.` segments |
479
+ | `arch.forbidden` / `singleton` — `pattern` | ✅ | ✅ | ✅ | ✅ | ✅ |
480
+ | `arch.forbidden` / `singleton` — `jsx-element` | ✅ | — | — | — | — |
481
+ | `arch.maxLines` | ✅ | ✅ | ✅ | ✅ | ✅ |
482
+ | `arch.maxCommentDensity` / `maxCommentBlock` | ✅ `//`, `/* */`, `/** */` docs | ✅ `#` | ✅ `//`, `/* */`, `///` and `/** */` docs | ✅ `//`, `/* */`, `///` and `//!` docs | ✅ `//`, `/* */`, `///` and `/** */` docs |
483
+ | Built-in rules and plugins | ✅ | ✗ | ✗ | ✗ | ✗ |
484
+ | Custom rules (`klint.rules.ts`) | ✅ | ✗ | ✗ | ✗ | ✗ |
485
+ | Type-aware rules | ✅ | ✗ | ✗ | ✗ | ✗ |
486
486
 
487
487
  **✅** supported · **—** the language has no equivalent construct · **✗** not supported yet
488
488
 
489
- A dash is not a gap. Swift has no dynamic import expression, no `import type`, and one module per declaration; Rust has no dynamic import and no type-only import. Those rows can never be filled. The `✗` rows are real limits: the rule set and custom rules are written against the TypeScript AST, and type-aware rules need a type checker that only the TS engine has.
489
+ A dash is not a gap. Swift has no dynamic import expression, no `import type`, and one module per declaration; Rust has no dynamic import and no type-only import; C# has no dynamic `using`, no type-only import, and one namespace per `using`. Those rows can never be filled. The `✗` rows are real limits: the rule set and custom rules are written against the TypeScript AST, and type-aware rules need a type checker that only the TS engine has.
490
490
 
491
- Imports are read off the AST in every language, so a specifier written inside a comment or a string is never an import — including nested Swift block comments. Only file discovery differs: the TS engine walks `.ts`/`.tsx`/`.js`/`.jsx`/`.mts`/`.cts`, so Python, Swift, and Rust files reach klint through the Rust engine, which `auto` mode selects for them automatically.
491
+ Imports are read off the AST in every language, so a specifier written inside a comment or a string is never an import — including nested Swift block comments. Only file discovery differs: the TS engine walks `.ts`/`.tsx`/`.js`/`.jsx`/`.mts`/`.cts`, so Python, Swift, Rust, and C# files reach klint through the Rust engine, which `auto` mode selects for them automatically.
492
492
 
493
493
  ### Python Architecture Checks
494
494
 
@@ -629,6 +629,53 @@ not a Cargo build graph or `#[path]` attribute resolver.
629
629
  Comment budget rules apply to Rust too, where both `///` and `//!` count as
630
630
  doc-comments.
631
631
 
632
+ ### C# Architecture Checks
633
+
634
+ The Rust engine can apply architecture rules to C# projects:
635
+
636
+ ```yaml
637
+ include: ["src"]
638
+
639
+ rules: {}
640
+
641
+ arch:
642
+ layers:
643
+ web: ["src/Web/**"]
644
+ data: ["src/Data/**"]
645
+ config: ["src/Config/**"]
646
+
647
+ imports:
648
+ - from: web
649
+ deny: data
650
+ message: "Web must use app services, not import the data layer directly"
651
+ - from: web
652
+ deny-packages: ["Newtonsoft"]
653
+ message: "Use System.Text.Json instead of Newtonsoft"
654
+
655
+ forbidden:
656
+ - pattern: "Console.WriteLine("
657
+ in: web
658
+ message: "Use the logger"
659
+ ```
660
+
661
+ C# import checks read `using` declarations off the AST, covering `using System;`,
662
+ qualified namespaces such as `using System.Collections.Generic;`, `using static
663
+ System.Math;`, `global using System.Linq;`, and aliases such as `using Json =
664
+ Newtonsoft.Json;` — an alias resolves to its target namespace, not the alias
665
+ name. Because the specifier comes from the parse tree, a `using` written inside a
666
+ `//` or `/* */` comment is not an import.
667
+
668
+ C# namespaces are decoupled from disk layout, so klint indexes the `namespace`
669
+ declarations across the project — block and file-scoped alike — and resolves a
670
+ `using` against that index. Namespaces that no project file declares, such as
671
+ `System`, are ignored by `deny`/`allow` and are the target of `deny-packages`
672
+ instead, which matches on `.` segments so `Newtonsoft` denies `Newtonsoft.Json`
673
+ but never `NewtonsoftShim`. This is architecture enforcement, not a full MSBuild
674
+ or NuGet restore graph.
675
+
676
+ Comment budget rules apply to C# too, where `///` XML doc comments count as
677
+ doc-comments alongside `/** */`.
678
+
632
679
  ## Built-in Rules
633
680
 
634
681
  | Rule | Type-aware | Description |
@@ -86,7 +86,7 @@ const ArchImportRuleSchema = z
86
86
  "Allowlist of layer name(s) / glob(s) the source may import from. Anything not matching `allow` is denied."
87
87
  ),
88
88
  "deny-packages": StringOrStringArray.optional().describe(
89
- "Package specifier(s) the source is not allowed to import — npm packages, `node:` builtins, Python pip packages and stdlib modules, Swift system frameworks, and external Rust crates, which `deny`/`allow` cannot reach because they never resolve to a project file. Matching is per segment: `/` in TypeScript/JavaScript, so `next` also covers `next/headers` while `nextra` is unaffected; `.` in Python and Swift, so `os` also covers `os.path` while `oscrypto` is unaffected; and `::` in Rust, so `tokio` also covers `tokio::sync::Mutex` while `tokio_util` is unaffected. Python, Swift, and Rust sources are checked by the Rust engine."
89
+ "Package specifier(s) the source is not allowed to import — npm packages, `node:` builtins, Python pip packages and stdlib modules, Swift system frameworks, external Rust crates, and C# namespaces or NuGet packages, which `deny`/`allow` cannot reach because they never resolve to a project file. Matching is per segment: `/` in TypeScript/JavaScript, so `next` also covers `next/headers` while `nextra` is unaffected; `.` in Python, Swift, and C#, so `os` also covers `os.path` while `oscrypto` is unaffected; and `::` in Rust, so `tokio` also covers `tokio::sync::Mutex` while `tokio_util` is unaffected. Python, Swift, Rust, and C# sources are checked by the Rust engine."
90
90
  ),
91
91
  "type-only": z
92
92
  .literal("allow")
@@ -244,7 +244,7 @@ const CountDocCommentsSchema = z
244
244
  .boolean()
245
245
  .optional()
246
246
  .describe(
247
- "Count doc-comments toward the limit. Doc-comments are `/** … */` JSDoc blocks (TS/JS), `///` (Swift), and `///` or `//!` (Rust); ordinary `//`, `/* */`, and `#` comments are always counted. Defaults to false, so documentation is exempt and only explanatory/inline comments are capped. Has no effect on Python: docstrings are string expressions rather than comments, so they never count toward either limit."
247
+ "Count doc-comments toward the limit. Doc-comments are `/** … */` JSDoc blocks (TS/JS), `///` (Swift and C#), and `///` or `//!` (Rust); ordinary `//`, `/* */`, and `#` comments are always counted. Defaults to false, so documentation is exempt and only explanatory/inline comments are capped. Has no effect on Python: docstrings are string expressions rather than comments, so they never count toward either limit."
248
248
  );
249
249
 
250
250
  const CommentIgnoreSchema = StringOrStringArray.optional().describe(
package/klint.schema.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
- "$id": "https://raw.githubusercontent.com/konvert7/klint/refs/tags/v0.31.0/klint.schema.json",
3
+ "$id": "https://raw.githubusercontent.com/konvert7/klint/refs/tags/v0.32.0/klint.schema.json",
4
4
  "type": "object",
5
5
  "properties": {
6
6
  "$schema": {
@@ -819,7 +819,7 @@
819
819
  ]
820
820
  },
821
821
  "deny-packages": {
822
- "description": "Package specifier(s) the source is not allowed to import — npm packages, `node:` builtins, Python pip packages and stdlib modules, Swift system frameworks, and external Rust crates, which `deny`/`allow` cannot reach because they never resolve to a project file. Matching is per segment: `/` in TypeScript/JavaScript, so `next` also covers `next/headers` while `nextra` is unaffected; `.` in Python and Swift, so `os` also covers `os.path` while `oscrypto` is unaffected; and `::` in Rust, so `tokio` also covers `tokio::sync::Mutex` while `tokio_util` is unaffected. Python, Swift, and Rust sources are checked by the Rust engine.",
822
+ "description": "Package specifier(s) the source is not allowed to import — npm packages, `node:` builtins, Python pip packages and stdlib modules, Swift system frameworks, external Rust crates, and C# namespaces or NuGet packages, which `deny`/`allow` cannot reach because they never resolve to a project file. Matching is per segment: `/` in TypeScript/JavaScript, so `next` also covers `next/headers` while `nextra` is unaffected; `.` in Python, Swift, and C#, so `os` also covers `os.path` while `oscrypto` is unaffected; and `::` in Rust, so `tokio` also covers `tokio::sync::Mutex` while `tokio_util` is unaffected. Python, Swift, Rust, and C# sources are checked by the Rust engine.",
823
823
  "anyOf": [
824
824
  {
825
825
  "type": "string"
@@ -1141,7 +1141,7 @@
1141
1141
  "description": "Maximum percentage of a file's physical lines that may be comment lines. Denominator is total physical lines (code + comments + blanks), the same count `maxLines` uses. A file above this ratio is a violation."
1142
1142
  },
1143
1143
  "countDocComments": {
1144
- "description": "Count doc-comments toward the limit. Doc-comments are `/** … */` JSDoc blocks (TS/JS), `///` (Swift), and `///` or `//!` (Rust); ordinary `//`, `/* */`, and `#` comments are always counted. Defaults to false, so documentation is exempt and only explanatory/inline comments are capped. Has no effect on Python: docstrings are string expressions rather than comments, so they never count toward either limit.",
1144
+ "description": "Count doc-comments toward the limit. Doc-comments are `/** … */` JSDoc blocks (TS/JS), `///` (Swift and C#), and `///` or `//!` (Rust); ordinary `//`, `/* */`, and `#` comments are always counted. Defaults to false, so documentation is exempt and only explanatory/inline comments are capped. Has no effect on Python: docstrings are string expressions rather than comments, so they never count toward either limit.",
1145
1145
  "type": "boolean"
1146
1146
  },
1147
1147
  "ignore": {
@@ -1210,7 +1210,7 @@
1210
1210
  "description": "Maximum number of consecutive comment lines allowed in a single block. A run of more comment lines than this — e.g. a tall comment stacked above a statement — is a violation, reported at the first offending line."
1211
1211
  },
1212
1212
  "countDocComments": {
1213
- "description": "Count doc-comments toward the limit. Doc-comments are `/** … */` JSDoc blocks (TS/JS), `///` (Swift), and `///` or `//!` (Rust); ordinary `//`, `/* */`, and `#` comments are always counted. Defaults to false, so documentation is exempt and only explanatory/inline comments are capped. Has no effect on Python: docstrings are string expressions rather than comments, so they never count toward either limit.",
1213
+ "description": "Count doc-comments toward the limit. Doc-comments are `/** … */` JSDoc blocks (TS/JS), `///` (Swift and C#), and `///` or `//!` (Rust); ordinary `//`, `/* */`, and `#` comments are always counted. Defaults to false, so documentation is exempt and only explanatory/inline comments are capped. Has no effect on Python: docstrings are string expressions rather than comments, so they never count toward either limit.",
1214
1214
  "type": "boolean"
1215
1215
  },
1216
1216
  "ignore": {
package/klint.schema.yaml CHANGED
@@ -1,5 +1,5 @@
1
1
  $schema: http://json-schema.org/draft-07/schema#
2
- $id: https://raw.githubusercontent.com/konvert7/klint/refs/tags/v0.31.0/klint.schema.json
2
+ $id: https://raw.githubusercontent.com/konvert7/klint/refs/tags/v0.32.0/klint.schema.json
3
3
  type: object
4
4
  properties:
5
5
  $schema:
@@ -596,12 +596,12 @@ properties:
596
596
  type: string
597
597
  deny-packages:
598
598
  description: "Package specifier(s) the source is not allowed to import — npm packages, `node:` builtins, Python pip
599
- packages and stdlib modules, Swift system frameworks, and external Rust crates, which `deny`/`allow`
600
- cannot reach because they never resolve to a project file. Matching is per segment: `/` in
601
- TypeScript/JavaScript, so `next` also covers `next/headers` while `nextra` is unaffected; `.` in Python
602
- and Swift, so `os` also covers `os.path` while `oscrypto` is unaffected; and `::` in Rust, so `tokio`
603
- also covers `tokio::sync::Mutex` while `tokio_util` is unaffected. Python, Swift, and Rust sources are
604
- checked by the Rust engine."
599
+ packages and stdlib modules, Swift system frameworks, external Rust crates, and C# namespaces or NuGet
600
+ packages, which `deny`/`allow` cannot reach because they never resolve to a project file. Matching is
601
+ per segment: `/` in TypeScript/JavaScript, so `next` also covers `next/headers` while `nextra` is
602
+ unaffected; `.` in Python, Swift, and C#, so `os` also covers `os.path` while `oscrypto` is unaffected;
603
+ and `::` in Rust, so `tokio` also covers `tokio::sync::Mutex` while `tokio_util` is unaffected. Python,
604
+ Swift, Rust, and C# sources are checked by the Rust engine."
605
605
  anyOf:
606
606
  - type: string
607
607
  - type: array
@@ -880,10 +880,11 @@ properties:
880
880
  lines (code + comments + blanks), the same count `maxLines` uses. A file above this ratio is a
881
881
  violation.
882
882
  countDocComments:
883
- description: "Count doc-comments toward the limit. Doc-comments are `/** … */` JSDoc blocks (TS/JS), `///` (Swift), and
884
- `///` or `//!` (Rust); ordinary `//`, `/* */`, and `#` comments are always counted. Defaults to false,
885
- so documentation is exempt and only explanatory/inline comments are capped. Has no effect on Python:
886
- docstrings are string expressions rather than comments, so they never count toward either limit."
883
+ description: "Count doc-comments toward the limit. Doc-comments are `/** … */` JSDoc blocks (TS/JS), `///` (Swift and
884
+ C#), and `///` or `//!` (Rust); ordinary `//`, `/* */`, and `#` comments are always counted. Defaults to
885
+ false, so documentation is exempt and only explanatory/inline comments are capped. Has no effect on
886
+ Python: docstrings are string expressions rather than comments, so they never count toward either
887
+ limit."
887
888
  type: boolean
888
889
  ignore:
889
890
  description: Comment lines matching these patterns are structural, not prose — marker comments a build or codegen step
@@ -947,10 +948,11 @@ properties:
947
948
  this — e.g. a tall comment stacked above a statement — is a violation, reported at the first offending
948
949
  line.
949
950
  countDocComments:
950
- description: "Count doc-comments toward the limit. Doc-comments are `/** … */` JSDoc blocks (TS/JS), `///` (Swift), and
951
- `///` or `//!` (Rust); ordinary `//`, `/* */`, and `#` comments are always counted. Defaults to false,
952
- so documentation is exempt and only explanatory/inline comments are capped. Has no effect on Python:
953
- docstrings are string expressions rather than comments, so they never count toward either limit."
951
+ description: "Count doc-comments toward the limit. Doc-comments are `/** … */` JSDoc blocks (TS/JS), `///` (Swift and
952
+ C#), and `///` or `//!` (Rust); ordinary `//`, `/* */`, and `#` comments are always counted. Defaults to
953
+ false, so documentation is exempt and only explanatory/inline comments are capped. Has no effect on
954
+ Python: docstrings are string expressions rather than comments, so they never count toward either
955
+ limit."
954
956
  type: boolean
955
957
  ignore:
956
958
  description: Comment lines matching these patterns are structural, not prose — marker comments a build or codegen step
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@konvert7/klint",
3
- "version": "0.31.0",
3
+ "version": "0.32.0",
4
4
  "description": "Architecture-as-Code linter for TypeScript. Enforce layer boundaries, import rules, and singleton patterns in YAML.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -67,10 +67,10 @@
67
67
  "zod": "^4.4.3"
68
68
  },
69
69
  "optionalDependencies": {
70
- "@konvert7/klint-darwin-arm64": "0.31.0",
71
- "@konvert7/klint-darwin-x64": "0.31.0",
72
- "@konvert7/klint-linux-x64": "0.31.0",
73
- "@konvert7/klint-win32-x64": "0.31.0"
70
+ "@konvert7/klint-darwin-arm64": "0.32.0",
71
+ "@konvert7/klint-darwin-x64": "0.32.0",
72
+ "@konvert7/klint-linux-x64": "0.32.0",
73
+ "@konvert7/klint-win32-x64": "0.32.0"
74
74
  },
75
75
  "devDependencies": {
76
76
  "@biomejs/biome": "2.4.7",