packages/plugin-block/src/dep.ts (127 lines of code) (raw):

import { FullVersion } from 'package-json'; import * as minimist from 'minimist'; import * as ora from 'ora'; import * as execa from 'execa'; import * as semver from 'semver'; import { debug } from '@alicloud/console-toolkit-shared-utils'; import { IContext } from './types'; import { DEBUG_TAG } from './constants'; interface Dependencies {readonly [name: string]: string} function checkConflict(blockDeps: Dependencies, projectDeps: Dependencies) { const lacks: string[][] = []; const conflicts: string[][] = []; Object.keys(blockDeps).forEach(dep => { if (!projectDeps[dep]) { lacks.push([dep, blockDeps[dep]]); } else if (!semver.intersects(projectDeps[dep], blockDeps[dep])) { conflicts.push([dep, blockDeps[dep], projectDeps[dep]]); } }); return [lacks, conflicts]; } function getErrors(allConflicts: string[][]) { return ` find dependencies conflict between block and your project: ${allConflicts .map(info => { return `* ${info[0]}: ${info[2]}(your project) not compatible with ${ info[1] }(block)`; }) .join('\n')}`; } export function dependenciesConflictCheck( blockPkgDeps = {}, projectPkgDeps = {}, blockPkgDevDeps = {}, projectPkgAllDeps = {}, ) { const [lacks, conflicts] = checkConflict(blockPkgDeps, projectPkgDeps); const [devLacks, devConflicts] = checkConflict( blockPkgDevDeps, projectPkgAllDeps, ); return { conflicts, lacks, devConflicts, devLacks, }; } interface DepsArgs { projectPkg: FullVersion; cwd: string; spinner: ora.Ora; args: minimist.ParsedArgs; ctx: IContext; npmClient: string; } export async function installBlockDeps(param: DepsArgs) { const { projectPkg, cwd, spinner, args, ctx, npmClient } = param; const { dryRun } = args; const { conflicts, lacks, devConflicts, devLacks } = dependenciesConflictCheck( ctx.pkg.dependencies, projectPkg.dependencies, ctx.pkg.devDependencies, projectPkg.devDependencies, ); debug(`conflictDeps ${conflicts}, lackDeps ${lacks}`, `devConflictDeps ${devConflicts}, devLackDeps ${devLacks}`); // find confilict dependencies throw error const allConflicts = [ ...conflicts, ...devConflicts, ]; if (allConflicts.length) { throw new Error(getErrors(allConflicts)); } // find lack confilict, auto install if (dryRun) { debug(DEBUG_TAG, 'dryRun is true, skip install dependencies'); } else { if (lacks.length) { const deps = lacks.map((dep: string[]) => `${dep[0]}@${dep[1]}`); spinner.start( `Install additional dependencies ${deps.join(',')} with ${npmClient}`, ); try { await execa( npmClient, npmClient.includes('yarn') ? ['add', ...deps] : ['install', ...deps, '--save'], { cwd }, ); } catch (e) { spinner.fail(); throw new Error(e); } spinner.succeed(); } if (devLacks.length) { // need skip devDependency which already install in dependencies const devDeps = devLacks .filter(dep => !lacks.find(item => item[0] === dep[0])) .map(dep => `${dep[0]}@${dep[1]}`); spinner.start( `Install additional devDependencies ${devDeps.join(',')} with ${npmClient}`, ); try { await execa( npmClient, npmClient.includes('yarn') ? ['add', ...devDeps, '--dev'] : ['install', ...devDeps, '--save-dev'], { cwd }, ); } catch (e) { spinner.fail(); throw new Error(e); } spinner.succeed(); } } }