UNPKG

sanity

Version:

Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches

183 lines (139 loc) • 5.12 kB
import {type PackageJson} from '@sanity/cli' import {generateHelpUrl} from '@sanity/generate-help-url' import fs from 'fs' import path from 'path' import resolveFrom from 'resolve-from' import semver, {type SemVer} from 'semver' interface PackageInfo { name: string supported: string[] deprecatedBelow: null | string installed: SemVer isUnsupported: boolean isDeprecated: boolean isUntested: boolean } // NOTE: when doing changes here, also remember to update versions in help docs at // https://sanity.io/admin/structure/docs;helpArticle;upgrade-packages const PACKAGES = [ {name: 'react', supported: ['^18'], deprecatedBelow: null}, {name: 'react-dom', supported: ['^18'], deprecatedBelow: null}, ] export function checkStudioDependencyVersions(workDir: string): void { const manifest = readPackageJson(path.join(workDir, 'package.json')) const dependencies = {...manifest.dependencies, ...manifest.devDependencies} const packageInfo = PACKAGES.map((pkg): PackageInfo | false => { const dependency = dependencies[pkg.name] if (!dependency) { return false } const manifestPath = resolveFrom.silent(workDir, path.join(pkg.name, 'package.json')) const installed = semver.coerce( manifestPath ? readPackageJson(manifestPath).version : dependency.replace(/[\D.]/g, ''), ) if (!installed) { return false } const supported = pkg.supported.join(' || ') // "Untested" is usually the case where we have not upgraded the React version requirements // before a release, but given that is usually works in a backwards-compatible way, we want // to indicate that it's _untested_, not necessarily _unsupported_ // Ex: Installed is react@19.0.0, but we've only _tested_ with react@^18 const isUntested = !semver.satisfies(installed, supported) && semver.gtr(installed, supported) // "Unsupported" in that the installed version is _lower than_ the minimum version // Ex: Installed is react@15.0.0, but we require react@^16 const isUnsupported = !semver.satisfies(installed, supported) && !isUntested // "Deprecated" in that we will stop supporting it at some point in the near future, // so users should be prompted to upgrade const isDeprecated = pkg.deprecatedBelow ? semver.ltr(installed, pkg.deprecatedBelow) : false return { ...pkg, installed, isUnsupported, isDeprecated, isUntested, } }) const installedPackages = packageInfo.filter((inp): inp is PackageInfo => inp !== false) const unsupported = installedPackages.filter((pkg) => pkg.isUnsupported) const deprecated = installedPackages.filter((pkg) => !pkg.isUnsupported && pkg.isDeprecated) const untested = installedPackages.filter((pkg) => pkg.isUntested) if (deprecated.length > 0) { // eslint-disable-next-line no-console console.warn(` [WARN] The following package versions have been deprecated and should be upgraded: ${listPackages(deprecated)} Support for these will be removed in a future release! ${getUpgradeInstructions(deprecated)} `) } if (untested.length > 0) { // eslint-disable-next-line no-console console.warn(` [WARN] The following package versions have not yet been marked as supported: ${listPackages(untested)} You _may_ encounter bugs while using these versions. ${getDowngradeInstructions(untested)} `) } if (unsupported.length > 0) { // eslint-disable-next-line no-console console.error(` [ERROR] The following package versions are no longer supported and needs to be upgraded: ${listPackages(unsupported)} ${getUpgradeInstructions(unsupported)} `) process.exit(1) } } function listPackages(pkgs: PackageInfo[]) { return pkgs .map( (pkg) => `${pkg.name} (installed: ${pkg.installed}, want: ${ pkg.deprecatedBelow || pkg.supported.join(' || ') })`, ) .join('\n ') } function getUpgradeInstructions(pkgs: PackageInfo[]) { const inst = pkgs .map((pkg) => { const [highestSupported] = pkg.supported .map((version) => (semver.coerce(version) || {version: ''}).version) .sort(semver.rcompare) return `"${pkg.name}@${highestSupported}"` }) .join(' ') return `To upgrade, run either: npm install ${inst} or yarn add ${inst} or pnpm add ${inst} Read more at ${generateHelpUrl('upgrade-packages')}` } function getDowngradeInstructions(pkgs: PackageInfo[]) { const inst = pkgs .map((pkg) => { const [highestSupported] = pkg.supported .map((version) => (semver.coerce(version) || {version: ''}).version) .sort(semver.rcompare) return `"${pkg.name}@${highestSupported}"` }) .join(' ') return `To downgrade, run either: yarn add ${inst} or npm install ${inst} or pnpm install ${inst}` } function readPackageJson(filePath: string): PackageJson { try { // eslint-disable-next-line no-sync return JSON.parse(fs.readFileSync(filePath, 'utf8')) } catch (err) { throw new Error(`Failed to read "${filePath}": ${err.message}`) } }