Files
orca/config/scripts/check-mobile-shared-dependency-versions.mjs
T
Neil a308277f9a fix(deps): root and mobile resolve one zod, and CI fails if they drift (#31260)
* fix(deps): root and mobile resolve one zod, and CI fails if they drift

Mobile type-checks src/shared zod schemas. With mobile on zod 4.6.5 and the
root on 4.6.7, TypeScript treated them as unrelated packages, so wrapped
schemas inferred {} (tests/e2e/helpers/phone-feed-list.ts:154) whenever a
job installed mobile deps.

* fix(deps): guard every package mobile's tsconfig remaps, not only zod

* test(ci): pin the shared-dependency version check to the static phase
2026-10-20 19:12:29 -07:00

59 lines
2.1 KiB
JavaScript

#!/usr/bin/env node
// Root and mobile are separate pnpm projects, so nothing else stops their lockfiles drifting apart.
import { readFileSync } from 'node:fs'
import { join, resolve } from 'node:path'
import { pathToFileURL } from 'node:url'
import { lockedVersion, lockfileRootImporter } from './pnpm-lockfile-root-importer.mjs'
// The packages src/shared imports that mobile/tsconfig.json remaps to mobile's copy. TypeScript
// merges two copies only at the same version; else zod-wrapped schemas infer `{}` (phone-feed-list.ts).
export const SHARED_DEPENDENCIES = ['zod', 'tweetnacl', '@noble/hashes', '@types/ws']
function importerVersion(importer, name) {
return lockedVersion(importer.dependencies?.[name] ?? importer.devDependencies?.[name])
}
export function findSharedDependencyVersionProblems({
rootImporter,
mobileImporter,
names = SHARED_DEPENDENCIES
}) {
const problems = []
for (const name of names) {
const root = importerVersion(rootImporter, name)
const mobile = importerVersion(mobileImporter, name)
if (!root || !mobile) {
problems.push(`${name} is not a direct dependency of ${root ? 'mobile' : 'the root'}`)
} else if (root !== mobile) {
problems.push(`${name}: the root locks ${root}, mobile locks ${mobile}`)
}
}
return problems
}
export function main(root = resolve(import.meta.dirname, '../..')) {
const readImporter = (lockfile) =>
lockfileRootImporter(readFileSync(join(root, lockfile), 'utf8'))
const problems = findSharedDependencyVersionProblems({
rootImporter: readImporter('pnpm-lock.yaml'),
mobileImporter: readImporter(join('mobile', 'pnpm-lock.yaml'))
})
if (problems.length > 0) {
console.error('Root and mobile must lock the same version of:')
for (const problem of problems) {
console.error(`- ${problem}`)
}
console.error(
'Set the same range in package.json and mobile/package.json, then pnpm install in both.'
)
return 1
}
console.log(`Root and mobile lock the same ${SHARED_DEPENDENCIES.join(', ')}.`)
return 0
}
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
process.exit(main())
}