import path from 'path' import globalDirs from 'global-dirs' import { PackageJson } from 'type-fest' import semver from 'semver' import { getNpmRegistry, getLatestVersion, getNpmLatestSemverVersion, } from 'ice-npm-utils' import { pathExists, pathExistsSync, outputJsonWithSpaces } from './fs' import { Obj } from './type' import log from './log' import { loadJson, loadJsonSync } from './json' import { installPkgs } from './tnpm' const prefix = 'pkg-json' export const PKG_JSON_FILE_NAME = 'package.json' export async function pkgJsonExists(dir: string): Promise { return pathExists(path.join(dir, PKG_JSON_FILE_NAME)) } export function pkgJsonExistsSync(dir: string): boolean { return pathExistsSync(path.join(dir, PKG_JSON_FILE_NAME)) } export async function loadPkgJson(dir: string): Promise { return loadJson(dir, PKG_JSON_FILE_NAME) } export function loadPkgJsonSync(dir: string): PackageJson { return loadJsonSync(dir, PKG_JSON_FILE_NAME) } export async function outputPkgJson(dir: string, json: Obj): Promise { await outputJsonWithSpaces(path.join(dir, PKG_JSON_FILE_NAME), json) } export function getNodeModulesPkgFilepath( cwd: string, pkg: string, filepath: string, ) { return path.join(cwd, 'node_modules', pkg, filepath) } export function getNodeModulesPkgJsonFilepath(cwd: string, pkg: string) { return getNodeModulesPkgFilepath(cwd, pkg, PKG_JSON_FILE_NAME) } export async function nodeModulesPkgJsonExists(cwd: string, pkg: string) { return pkgJsonExists(path.join(cwd, 'node_modules', pkg)) } export async function loadNodeModulesPkgJson(cwd: string, pkg: string) { return loadPkgJson(path.join(cwd, 'node_modules', pkg)) } export function loadNodeModulesPkgJsonSync(cwd: string, pkg: string) { return loadPkgJsonSync(path.join(cwd, 'node_modules', pkg)) } export async function updatePkgs({ cwd, pkgs, dev, save = true, exitWhenErr, }: { cwd: string // ["lodash"] or { "mobx": "^5.15.7" } pkgs: string[] | Record dev?: boolean // 是否保存到 package.json, 默认 true save?: boolean exitWhenErr?: boolean }) { const pkgJson = await loadPkgJson(cwd) const deps = dev ? pkgJson.devDependencies : pkgJson.dependencies const isArr = Array.isArray(pkgs) const names = isArr ? pkgs : Object.keys(pkgs) const arr: string[] = [] await Promise.all( names.map(async (pkg) => { const nameVersion = isArr ? pkg : `${pkg}@${pkgs[pkg]}` let skip = false const skipMsg = 'skip install ' + pkg if (save && (!deps || !deps[pkg])) { arr.push(nameVersion) } else { const exists = await nodeModulesPkgJsonExists(cwd, pkg) if (!exists) { arr.push(nameVersion) } else { let { version } = await loadNodeModulesPkgJson(cwd, pkg) version = version || '0.0.0' if (isArr) { const targetVersion = await getLatestVersion( pkg, getNpmRegistry(pkg), ) if (semver.lt(version, targetVersion)) { arr.push(`${pkg}@${targetVersion}`) } else { skip = true } } else if ( semver.validRange(pkgs[pkg]) && !semver.satisfies(version, pkgs[pkg]) ) { arr.push(nameVersion) } else if (semver.valid(pkgs[pkg]) && semver.lt(version, pkgs[pkg])) { arr.push(nameVersion) } else { skip = true } } } skip && log.verbose(prefix, skipMsg) }), ) if (arr.length > 0) { log.info('pkg', 'update pkgs ', arr) await installPkgs({ cwd, pkgs: arr, dev, save, exitWhenErr }) } } export async function updateGlobalPkg({ pkg, force, cmd, }: { pkg: string force?: boolean cmd?: string }) { let needUpdate = false let nameVersion = pkg let breakMsg = '' const pkgDir = path.join(globalDirs.npm.packages, pkg) const exists = await pathExists(pkgDir) if (!exists) { // 没有安装 needUpdate = true } else { // 有更新 const pkgJson = await loadPkgJson(pkgDir) const version = pkgJson.version || '*' log.verbose(pkg, 'version', version) const latestSemverVersion = await getNpmLatestSemverVersion( pkg, version, getNpmRegistry(pkg), ) log.verbose(pkg, 'latestSemverVersion', latestSemverVersion) if (semver.lt(version, latestSemverVersion)) { needUpdate = true nameVersion = `${pkg}@${latestSemverVersion}` } // 有 break 更新 const latestVersion = await getLatestVersion(pkg, getNpmRegistry(pkg)) log.verbose(pkg, 'latestVersion', latestVersion) if (semver.lt(latestSemverVersion, latestVersion)) { if (force) { needUpdate = true nameVersion = `${pkg}@${latestVersion}` } else { breakMsg = ` 检测到大版本更新 ${pkg}@${latestVersion} 可能会有 break change, 请谨慎升级 ${cmd ? `执行 cone ${cmd} -f 来强制升级` : ''} ` } } } if (needUpdate) { await installPkgs({ cwd: process.cwd(), pkgs: [nameVersion], global: true, exitWhenErr: false, }) } if (breakMsg) { log.info('pkg', breakMsg) } }