diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 98607e9622..f63efbc641 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -192,8 +192,9 @@ "test:unit": "bun run scripts/run-unit-tests.ts", "format": "prettier --check .", "format:fix": "prettier --write .", - "lint": "eslint . --max-warnings=0", + "lint": "eslint . --max-warnings=0 && npm run enforce-inference-peer-dependencies", "lint:fix": "eslint . --fix", + "enforce-inference-peer-dependencies": "bun scripts/enforce-inference-peer-dependencies.ts", "typecheck": "tsc --noEmit -p tsconfig.json && tsc --noEmit -p test/tsconfig.types.json", "postcompile:aliases": "tsc-alias -p tsconfig.alias.json", "build": "rm -rf dist && npm run lint && tsc --project tsconfig.json && npm run postcompile:aliases", diff --git a/packages/sdk/scripts/enforce-inference-peer-dependencies.ts b/packages/sdk/scripts/enforce-inference-peer-dependencies.ts new file mode 100644 index 0000000000..907a817890 --- /dev/null +++ b/packages/sdk/scripts/enforce-inference-peer-dependencies.ts @@ -0,0 +1,59 @@ +// @qvac/inference peerDependencies is the source of truth for addon version +// ranges. The SDK should use the same version ranges as inference for addons. +// For every addon p in inference's peerDependencies, this script validates if: +// - inference's devDependencies.p == inference's peerDependencies.p, +// - SDK's dependencies.p == inference's peerDependencies.p. +// That is, inference's devDependencies and SDK's dependencies match inference's peerDependencies. + +import { readFileSync } from 'fs' +import { join, resolve, dirname } from 'path' +import { fileURLToPath } from 'url' + +type Ranges = Record + +interface Manifest { + dependencies?: Ranges + devDependencies?: Ranges + peerDependencies?: Ranges +} + +const sdkDir = resolve(dirname(fileURLToPath(import.meta.url)), '..') +const inferenceDir = resolve(sdkDir, '..', 'inference') + +function readManifest(dir: string) { + return JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')) as Manifest +} + +const inferencePkg = readManifest(inferenceDir) +const sdkPkg = readManifest(sdkDir) + +const peers = inferencePkg.peerDependencies ?? {} +const inferenceDev = inferencePkg.devDependencies ?? {} +const sdkDeps = sdkPkg.dependencies ?? {} + +const drifts: string[] = [] +for (const [name, peer] of Object.entries(peers)) { + const dev = inferenceDev[name] + const dep = sdkDeps[name] + const mismatches: string[] = [] + if (dep !== peer) mismatches.push(dep === undefined ? 'SDK is missing it' : `SDK has ${dep}`) + if (dev !== peer) { + mismatches.push( + dev === undefined + ? 'inference devDependencies is missing it' + : `inference devDependencies has ${dev}` + ) + } + if (mismatches.length > 0) { + drifts.push( + `${name}: inference has it as ${peer} in peerDependencies, but ${mismatches.join(' and ')}.` + ) + } +} + +if (drifts.length > 0) { + console.error( + `${drifts.join('\n')}\n\nPlease ensure the SDK dependencies and inference devDependencies use the same addon versions as inference's peerDependencies.` + ) + process.exit(1) +}