From 06ef9c7f46b43f26b6b82f1ba81671c0e52e4386 Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Mon, 23 Mar 2026 22:42:54 +0100 Subject: [PATCH] feat: add typed readJSON overload for package.json Add a PackageJson interface and a readJSON("package.json") overload that returns Promise instead of Promise, removing the need for manual type assertions in rules. Includes workspaces and catalog fields. Mirrors the overload in both the canonical RuleContext and the generated rules.d.ts shim. --- .../adrs/ARCH-006-dependency-policy.rules.ts | 4 +-- .../ARCH-013-version-synchronization.rules.ts | 9 ++----- src/engine/runner.ts | 2 +- src/formats/rules.ts | 26 +++++++++++++++++++ src/helpers/rules-shim.ts | 24 +++++++++++++++++ 5 files changed, 55 insertions(+), 10 deletions(-) diff --git a/.archgate/adrs/ARCH-006-dependency-policy.rules.ts b/.archgate/adrs/ARCH-006-dependency-policy.rules.ts index 7edeab9..d310a8f 100644 --- a/.archgate/adrs/ARCH-006-dependency-policy.rules.ts +++ b/.archgate/adrs/ARCH-006-dependency-policy.rules.ts @@ -12,9 +12,9 @@ export default { "no-unapproved-deps": { description: "Production dependencies must be on the approved list", async check(ctx) { - let pkg: { dependencies?: Record }; + let pkg; try { - pkg = (await ctx.readJSON("package.json")) as typeof pkg; + pkg = await ctx.readJSON("package.json"); } catch { return; // No package.json — nothing to check } diff --git a/.archgate/adrs/ARCH-013-version-synchronization.rules.ts b/.archgate/adrs/ARCH-013-version-synchronization.rules.ts index d26f4d5..e9dfdc8 100644 --- a/.archgate/adrs/ARCH-013-version-synchronization.rules.ts +++ b/.archgate/adrs/ARCH-013-version-synchronization.rules.ts @@ -7,9 +7,7 @@ export default { "softwareVersion in docs/astro.config.mjs must match package.json version", severity: "error", async check(ctx) { - const pkgJson = (await ctx.readJSON("package.json")) as { - version?: string; - }; + const pkgJson = await ctx.readJSON("package.json"); if (!pkgJson.version) return; let astroConfig: string; @@ -38,10 +36,7 @@ export default { "optionalDependencies versions must match package.json version", severity: "error", async check(ctx) { - const pkgJson = (await ctx.readJSON("package.json")) as { - version?: string; - optionalDependencies?: Record; - }; + const pkgJson = await ctx.readJSON("package.json"); if (!pkgJson.version || !pkgJson.optionalDependencies) return; for (const [dep, depVersion] of Object.entries( diff --git a/src/engine/runner.ts b/src/engine/runner.ts index 54234bc..09e8eeb 100644 --- a/src/engine/runner.ts +++ b/src/engine/runner.ts @@ -166,7 +166,7 @@ function createRuleContext( return Bun.file(absPath).text(); }, - readJSON(path: string): Promise { + readJSON(path: string): Promise { const absPath = safePath(projectRoot, path); return Bun.file(absPath).json(); }, diff --git a/src/formats/rules.ts b/src/formats/rules.ts index 8cb2600..c36cc37 100644 --- a/src/formats/rules.ts +++ b/src/formats/rules.ts @@ -35,6 +35,31 @@ export interface RuleReport { info(detail: Omit): void; } +// --- Package JSON --- + +export interface PackageJson { + name?: string; + version?: string; + description?: string; + main?: string; + module?: string; + types?: string; + bin?: string | Record; + scripts?: Record; + dependencies?: Record; + devDependencies?: Record; + peerDependencies?: Record; + optionalDependencies?: Record; + private?: boolean; + license?: string; + repository?: string | { type: string; url: string }; + engines?: Record; + files?: string[]; + workspaces?: string[] | { packages: string[] }; + catalog?: Record; + [key: string]: unknown; +} + // --- Rule Context --- export interface RuleContext { @@ -45,6 +70,7 @@ export interface RuleContext { grep(file: string, pattern: RegExp): Promise; grepFiles(pattern: RegExp, fileGlob: string): Promise; readFile(path: string): Promise; + readJSON(path: "package.json"): Promise; readJSON(path: string): Promise; report: RuleReport; } diff --git a/src/helpers/rules-shim.ts b/src/helpers/rules-shim.ts index 39f8bcf..fb50b43 100644 --- a/src/helpers/rules-shim.ts +++ b/src/helpers/rules-shim.ts @@ -42,6 +42,29 @@ declare interface RuleReport { ): void; } +declare interface PackageJson { + name?: string; + version?: string; + description?: string; + main?: string; + module?: string; + types?: string; + bin?: string | Record; + scripts?: Record; + dependencies?: Record; + devDependencies?: Record; + peerDependencies?: Record; + optionalDependencies?: Record; + private?: boolean; + license?: string; + repository?: string | { type: string; url: string }; + engines?: Record; + files?: string[]; + workspaces?: string[] | { packages: string[] }; + catalog?: Record; + [key: string]: unknown; +} + declare interface RuleContext { projectRoot: string; scopedFiles: string[]; @@ -50,6 +73,7 @@ declare interface RuleContext { grep(file: string, pattern: RegExp): Promise; grepFiles(pattern: RegExp, fileGlob: string): Promise; readFile(path: string): Promise; + readJSON(path: "package.json"): Promise; readJSON(path: string): Promise; report: RuleReport; }