@konvert7/klint 0.21.0 → 0.23.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 +80 -3
- package/package.json +5 -5
- package/tools/prepare-python-release.ts +76 -0
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# klint
|
|
2
2
|
|
|
3
|
-
Architecture
|
|
3
|
+
Architecture-as-Code linting for TypeScript, Python, and Swift projects.
|
|
4
4
|
|
|
5
|
-
Biome and
|
|
5
|
+
Biome, oxlint, Ruff, and SwiftLint are excellent at syntax, style, and fast local correctness. klint handles the rules that need project context: layer boundaries, singleton ownership, forbidden patterns, custom repository policies, and type-aware TypeScript checks that should block an AI agent or CI run before code lands.
|
|
6
6
|
|
|
7
7
|
Use klint when "please follow AGENTS.md" is not strong enough. Put the rule in `klint.yaml`, run `klint`, and make the constraint executable.
|
|
8
8
|
|
|
@@ -190,7 +190,7 @@ arch:
|
|
|
190
190
|
message: "Client code must not import server-only modules"
|
|
191
191
|
```
|
|
192
192
|
|
|
193
|
-
`include` selects
|
|
193
|
+
`include` selects source files to lint. Patterns support `**` globs and `!` negation.
|
|
194
194
|
|
|
195
195
|
`plugins` enables bundled rule groups. Today, `sonar` provides focused code-quality rules.
|
|
196
196
|
|
|
@@ -279,6 +279,83 @@ arch:
|
|
|
279
279
|
message: "Use the auth module"
|
|
280
280
|
```
|
|
281
281
|
|
|
282
|
+
### Python Architecture Checks
|
|
283
|
+
|
|
284
|
+
The Rust engine can apply architecture rules to Python projects:
|
|
285
|
+
|
|
286
|
+
```yaml
|
|
287
|
+
include: ["src"]
|
|
288
|
+
|
|
289
|
+
rules: {}
|
|
290
|
+
|
|
291
|
+
arch:
|
|
292
|
+
layers:
|
|
293
|
+
jobs: ["src/app/jobs/**"]
|
|
294
|
+
lib: ["src/app/lib/**"]
|
|
295
|
+
config: ["src/app/config.py"]
|
|
296
|
+
|
|
297
|
+
imports:
|
|
298
|
+
- from: jobs
|
|
299
|
+
deny: lib
|
|
300
|
+
message: "Jobs must use service APIs, not import lib directly"
|
|
301
|
+
|
|
302
|
+
forbidden:
|
|
303
|
+
- pattern: "print("
|
|
304
|
+
in: jobs
|
|
305
|
+
message: "Use the logger"
|
|
306
|
+
|
|
307
|
+
singleton:
|
|
308
|
+
- pattern: "os.environ[\"API_KEY\"]"
|
|
309
|
+
only: "src/app/config.py"
|
|
310
|
+
in: ["src/**"]
|
|
311
|
+
message: "Use app config"
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
Python import checks support relative imports such as
|
|
315
|
+
`from ..lib.auth import load_key` and project-resolvable absolute imports such
|
|
316
|
+
as `from app.lib.auth import load_key`. External packages that do not resolve to
|
|
317
|
+
project files, such as `requests`, are ignored. This is architecture
|
|
318
|
+
enforcement, not full packaging or virtual-environment analysis.
|
|
319
|
+
|
|
320
|
+
### Swift Architecture Checks
|
|
321
|
+
|
|
322
|
+
The Rust engine can apply architecture rules to Swift projects:
|
|
323
|
+
|
|
324
|
+
```yaml
|
|
325
|
+
include: ["Sources"]
|
|
326
|
+
|
|
327
|
+
rules: {}
|
|
328
|
+
|
|
329
|
+
arch:
|
|
330
|
+
layers:
|
|
331
|
+
ui: ["Sources/App/UI/**"]
|
|
332
|
+
core: ["Sources/App/Core/**"]
|
|
333
|
+
config: ["Sources/App/Config/**"]
|
|
334
|
+
|
|
335
|
+
imports:
|
|
336
|
+
- from: ui
|
|
337
|
+
deny: core
|
|
338
|
+
message: "UI must use app services, not import Core directly"
|
|
339
|
+
|
|
340
|
+
forbidden:
|
|
341
|
+
- pattern: "URLSession.shared"
|
|
342
|
+
in: ui
|
|
343
|
+
message: "Use the networking client"
|
|
344
|
+
|
|
345
|
+
singleton:
|
|
346
|
+
- pattern: "ProcessInfo.processInfo.environment[\"API_KEY\"]"
|
|
347
|
+
only: "Sources/App/Config/AppConfig.swift"
|
|
348
|
+
in: ["Sources/**"]
|
|
349
|
+
message: "Use AppConfig"
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
Swift import checks parse declarations such as `import Core`,
|
|
353
|
+
`@_exported import Core`, and `import struct Models.User`. Imported modules
|
|
354
|
+
resolve against discovered project Swift directories and file stems. System or
|
|
355
|
+
package modules that are not present in the project, such as `Foundation`, are
|
|
356
|
+
ignored. This is architecture enforcement, not full SwiftPM or Xcode build graph
|
|
357
|
+
analysis.
|
|
358
|
+
|
|
282
359
|
## Built-in Rules
|
|
283
360
|
|
|
284
361
|
| Rule | Type-aware | Description |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@konvert7/klint",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.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": {
|
|
@@ -64,10 +64,10 @@
|
|
|
64
64
|
"zod": "^4.4.3"
|
|
65
65
|
},
|
|
66
66
|
"optionalDependencies": {
|
|
67
|
-
"@konvert7/klint-darwin-arm64": "0.
|
|
68
|
-
"@konvert7/klint-darwin-x64": "0.
|
|
69
|
-
"@konvert7/klint-linux-x64": "0.
|
|
70
|
-
"@konvert7/klint-win32-x64": "0.
|
|
67
|
+
"@konvert7/klint-darwin-arm64": "0.23.0",
|
|
68
|
+
"@konvert7/klint-darwin-x64": "0.23.0",
|
|
69
|
+
"@konvert7/klint-linux-x64": "0.23.0",
|
|
70
|
+
"@konvert7/klint-win32-x64": "0.23.0"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
73
|
"@biomejs/biome": "2.4.7",
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
interface Options {
|
|
6
|
+
name: string;
|
|
7
|
+
root: string;
|
|
8
|
+
version: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const repoRoot = fileURLToPath(new URL("..", import.meta.url));
|
|
12
|
+
const options = parseArgs();
|
|
13
|
+
const pythonRoot = join(options.root, "python");
|
|
14
|
+
const pyprojectPath = join(pythonRoot, "pyproject.toml");
|
|
15
|
+
const initPath = join(pythonRoot, "klint", "__init__.py");
|
|
16
|
+
|
|
17
|
+
const pyproject = readFileSync(pyprojectPath, "utf-8");
|
|
18
|
+
const nextPyproject = replaceRequired(
|
|
19
|
+
replaceRequired(pyproject, /^name = ".+"$/m, `name = "${options.name}"`, pyprojectPath),
|
|
20
|
+
/^version = ".+"$/m,
|
|
21
|
+
`version = "${options.version}"`,
|
|
22
|
+
pyprojectPath
|
|
23
|
+
);
|
|
24
|
+
writeFileSync(pyprojectPath, nextPyproject);
|
|
25
|
+
|
|
26
|
+
const init = readFileSync(initPath, "utf-8");
|
|
27
|
+
const nextInit = replaceRequired(
|
|
28
|
+
init,
|
|
29
|
+
/^__version__ = ".+"$/m,
|
|
30
|
+
`__version__ = "${options.version}"`,
|
|
31
|
+
initPath
|
|
32
|
+
);
|
|
33
|
+
writeFileSync(initPath, nextInit);
|
|
34
|
+
|
|
35
|
+
process.stdout.write(
|
|
36
|
+
`prepared Python release metadata: ${options.name}@${options.version}\n`
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
function parseArgs(): Options {
|
|
40
|
+
const args = process.argv.slice(2);
|
|
41
|
+
const name = valueAfter(args, "--name");
|
|
42
|
+
const version = valueAfter(args, "--version");
|
|
43
|
+
if (!name || !version) {
|
|
44
|
+
fail(
|
|
45
|
+
"Usage: bun tools/prepare-python-release.ts --name <package-name> --version <version> [--root <repo-root>]"
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
name,
|
|
51
|
+
version,
|
|
52
|
+
root: resolve(valueAfter(args, "--root") ?? repoRoot),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function replaceRequired(
|
|
57
|
+
input: string,
|
|
58
|
+
pattern: RegExp,
|
|
59
|
+
replacement: string,
|
|
60
|
+
path: string
|
|
61
|
+
): string {
|
|
62
|
+
if (!pattern.test(input)) {
|
|
63
|
+
fail(`Expected release metadata pattern ${pattern} in ${path}`);
|
|
64
|
+
}
|
|
65
|
+
return input.replace(pattern, replacement);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function valueAfter(args: string[], name: string): string | undefined {
|
|
69
|
+
const index = args.indexOf(name);
|
|
70
|
+
return index >= 0 ? args[index + 1] : undefined;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function fail(message: string): never {
|
|
74
|
+
process.stderr.write(`${message}\n`);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|