@konvert7/klint 0.12.1 → 0.15.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/cli.ts CHANGED
@@ -24,6 +24,7 @@ interface CliOptions {
24
24
  }
25
25
 
26
26
  export async function main(opts: CliOptions = {}): Promise<void> {
27
+ const startedAt = performance.now();
27
28
  const args = process.argv.slice(2);
28
29
 
29
30
  if (args[0] === "help" || args[0] === "h") {
@@ -153,7 +154,7 @@ export async function main(opts: CliOptions = {}): Promise<void> {
153
154
  unfixed > 0
154
155
  ? `klint: applied ${totalApplied} fix(es). ${unfixed} violation(s) require manual attention.`
155
156
  : `klint: applied ${totalApplied} fix(es). No remaining violations.`;
156
- process.stdout.write(JSON.stringify({ output: msg }));
157
+ process.stdout.write(`${msg} Finished in ${formatDuration(startedAt)}.\n`);
157
158
  process.exit(0);
158
159
  }
159
160
 
@@ -161,7 +162,7 @@ export async function main(opts: CliOptions = {}): Promise<void> {
161
162
  const warns = violations.filter((v) => v.severity === "warn");
162
163
 
163
164
  if (errors.length === 0 && warns.length === 0) {
164
- process.stdout.write(JSON.stringify({ output: "klint: 0 violations" }));
165
+ process.stdout.write(`klint: 0 violations in ${formatDuration(startedAt)}\n`);
165
166
  process.exit(0);
166
167
  }
167
168
 
@@ -181,11 +182,19 @@ export async function main(opts: CliOptions = {}): Promise<void> {
181
182
  process.stderr.write(
182
183
  `klint: ${errors.length} error(s)\n\n${errors.map(formatBlock).join("\n")}`
183
184
  );
185
+ process.stderr.write(`\nklint: finished in ${formatDuration(startedAt)}\n`);
184
186
  process.exit(2);
185
187
  }
188
+ process.stderr.write(`\nklint: finished in ${formatDuration(startedAt)}\n`);
186
189
  process.exit(0);
187
190
  }
188
191
 
192
+ function formatDuration(startedAt: number): string {
193
+ const elapsedMs = performance.now() - startedAt;
194
+ if (elapsedMs < 1000) return `${Math.round(elapsedMs)}ms`;
195
+ return `${(elapsedMs / 1000).toFixed(elapsedMs < 10_000 ? 2 : 1)}s`;
196
+ }
197
+
189
198
  function runRustEngine({
190
199
  configDir,
191
200
  fix,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@konvert7/klint",
3
- "version": "0.12.1",
3
+ "version": "0.15.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": {
@@ -60,12 +60,6 @@
60
60
  "yaml": "^2.9.0",
61
61
  "zod": "^4.4.3"
62
62
  },
63
- "optionalDependencies": {
64
- "@konvert7/klint-darwin-arm64": "*",
65
- "@konvert7/klint-darwin-x64": "*",
66
- "@konvert7/klint-linux-x64": "*",
67
- "@konvert7/klint-win32-x64": "*"
68
- },
69
63
  "devDependencies": {
70
64
  "@biomejs/biome": "2.4.7",
71
65
  "@commitlint/cli": "^20.5.3",
@@ -69,9 +69,7 @@ try {
69
69
  function prepareRootPackageJson(): PackageJson {
70
70
  const packageJson = readPackageJson(join(root, "package.json"));
71
71
  packageJson.version = options.version;
72
- packageJson.optionalDependencies = Object.fromEntries(
73
- nativePackages().map((nativePackage) => [nativePackage.packageName, options.version])
74
- );
72
+ delete packageJson.optionalDependencies;
75
73
 
76
74
  const outPath = join(options.outDir, "package.json");
77
75
  writeJson(outPath, packageJson);
@@ -106,9 +104,9 @@ function prepareNativePackage(nativePackage: ReturnType<typeof nativePackages>[n
106
104
  function validateRootPackageJson(packageJson: PackageJson): void {
107
105
  const optionalDependencies = packageJson.optionalDependencies ?? {};
108
106
  for (const nativePackage of nativePackages()) {
109
- if (optionalDependencies[nativePackage.packageName] !== options.version) {
107
+ if (nativePackage.packageName in optionalDependencies) {
110
108
  fail(
111
- `Root optional dependency ${nativePackage.packageName} is not pinned to ${options.version}`
109
+ `Root package must not consume dark native package ${nativePackage.packageName}`
112
110
  );
113
111
  }
114
112
  }
@@ -0,0 +1,117 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { spawnSync } from "node:child_process";
4
+
5
+ const NATIVE_PACKAGES = [
6
+ {
7
+ dir: "darwin-arm64",
8
+ name: "@konvert7/klint-darwin-arm64",
9
+ binaryPath: "bin/klint-rs",
10
+ },
11
+ {
12
+ dir: "darwin-x64",
13
+ name: "@konvert7/klint-darwin-x64",
14
+ binaryPath: "bin/klint-rs",
15
+ },
16
+ {
17
+ dir: "linux-x64",
18
+ name: "@konvert7/klint-linux-x64",
19
+ binaryPath: "bin/klint-rs",
20
+ },
21
+ {
22
+ dir: "win32-x64",
23
+ name: "@konvert7/klint-win32-x64",
24
+ binaryPath: "bin/klint-rs.exe",
25
+ },
26
+ ];
27
+
28
+ export async function verifyConditions(_, context) {
29
+ const cwd = context.cwd ?? process.cwd();
30
+
31
+ for (const nativePackage of NATIVE_PACKAGES) {
32
+ const packageJson = readPackageJson(packageDir(cwd, nativePackage));
33
+ if (packageJson.name !== nativePackage.name) {
34
+ throw new Error(
35
+ `${nativePackage.dir} package name is ${packageJson.name}, expected ${nativePackage.name}`
36
+ );
37
+ }
38
+ }
39
+ }
40
+
41
+ export async function prepare(_, context) {
42
+ const cwd = context.cwd ?? process.cwd();
43
+ const version = context.nextRelease?.version;
44
+
45
+ if (!version) {
46
+ throw new Error("semantic-release did not provide nextRelease.version");
47
+ }
48
+
49
+ for (const nativePackage of NATIVE_PACKAGES) {
50
+ const dir = packageDir(cwd, nativePackage);
51
+ const packageJson = readPackageJson(dir);
52
+ packageJson.version = version;
53
+ packageJson.private = false;
54
+ writePackageJson(dir, packageJson);
55
+
56
+ const binary = join(dir, nativePackage.binaryPath);
57
+ if (!existsSync(binary)) {
58
+ throw new Error(`Missing native binary for ${nativePackage.name}: ${binary}`);
59
+ }
60
+ }
61
+ }
62
+
63
+ export async function publish(pluginConfig = {}, context) {
64
+ const cwd = context.cwd ?? process.cwd();
65
+ const spawn = pluginConfig.spawnSync ?? spawnSync;
66
+
67
+ if (context.options?.dryRun) {
68
+ context.logger?.log("Dry run: skipping native npm publishes");
69
+ return;
70
+ }
71
+
72
+ for (const nativePackage of NATIVE_PACKAGES) {
73
+ const result = spawn("npm", ["publish", "--access", "public"], {
74
+ cwd: packageDir(cwd, nativePackage),
75
+ encoding: "utf-8",
76
+ stdio: ["ignore", "pipe", "pipe"],
77
+ });
78
+
79
+ if (result.stdout) process.stdout.write(result.stdout);
80
+ if (result.stderr) process.stderr.write(result.stderr);
81
+
82
+ if ((result.status ?? -1) !== 0) {
83
+ if (isMissingPackagePublishFailure(result)) {
84
+ context.logger?.log(
85
+ `Skipping ${nativePackage.name}: npm package is not available to this trusted publisher yet`
86
+ );
87
+ continue;
88
+ }
89
+
90
+ throw new Error(
91
+ `${nativePackage.name} npm publish failed with exit code ${result.status ?? -1}`
92
+ );
93
+ }
94
+ }
95
+ }
96
+
97
+ function packageDir(cwd, nativePackage) {
98
+ return join(cwd, "npm", "native", nativePackage.dir);
99
+ }
100
+
101
+ function readPackageJson(dir) {
102
+ return JSON.parse(readFileSync(join(dir, "package.json"), "utf-8"));
103
+ }
104
+
105
+ function writePackageJson(dir, packageJson) {
106
+ writeFileSync(join(dir, "package.json"), `${JSON.stringify(packageJson, null, 2)}\n`);
107
+ }
108
+
109
+ function isMissingPackagePublishFailure(result) {
110
+ const output = `${result.stdout ?? ""}\n${result.stderr ?? ""}`;
111
+ return (
112
+ output.includes("npm error code E404") ||
113
+ output.includes("404 Not Found") ||
114
+ output.includes("package not found") ||
115
+ output.includes("could not be found or you do not have permission")
116
+ );
117
+ }