UNPKG

syncpack

Version:

Consistent dependency versions in large JavaScript Monorepos

96 lines (95 loc) 4.21 kB
import chalk from 'chalk-template'; import { Context, Effect, flow, pipe } from 'effect'; import { uniq } from 'tightrope/array/uniq.js'; import { isString } from 'tightrope/guard/is-string.js'; import { logOtherCommands } from '../bin-list/list.js'; import { CliConfigTag } from '../config/tag.js'; import { ICON } from '../constants.js'; import { defaultErrorHandlers } from '../error-handlers/default-error-handlers.js'; import { getContext } from '../get-context/index.js'; import { getInstances } from '../get-instances/index.js'; import { askForChoice } from '../io/ask-for-choice.js'; import { askForInput } from '../io/ask-for-input.js'; import { exitIfInvalid } from '../io/exit-if-invalid.js'; import { IoTag } from '../io/index.js'; import { writeIfChanged } from '../io/write-if-changed.js'; import { getVersionGroupHeader } from '../lib/get-group-header.js'; import { withLogger } from '../lib/with-logger.js'; export function prompt({ io, cli, errorHandlers = defaultErrorHandlers, }) { return pipe(getContext({ io, cli, errorHandlers }), Effect.flatMap(ctx => pipe(Effect.gen(function* ($) { const { versionGroups } = yield* $(getInstances(ctx, io, errorHandlers)); let unfixableCount = 0; let index = 0; for (const group of versionGroups) { const unfixable = []; for (const groupReport of yield* $(group.inspectAll())) { for (const report of groupReport.reports) { if (isUnfixable(report)) { unfixable.push(report); } } if (unfixable.length > 0) { unfixableCount += unfixable.length; Effect.logInfo(getVersionGroupHeader({ group, index })); yield* $(askForNextVersion(groupReport, unfixable)); } } index++; } if (unfixableCount) { yield* $(writeIfChanged(ctx)); } else { const msg = chalk `{green ${ICON.tick}} no issues which syncpack cannot fix automatically`; yield* $(Effect.logInfo(msg)); yield* $(logOtherCommands()); } return ctx; }), Effect.catchTags({ WriteFileError: flow(errorHandlers.WriteFileError, Effect.map(() => { ctx.isInvalid = true; return ctx; })), }), Effect.flatMap(exitIfInvalid))), Effect.provide(pipe(Context.empty(), Context.add(CliConfigTag, cli), Context.add(IoTag, io))), withLogger); } function isUnfixable(report) { return (report._tag === 'MissingLocalVersion' || report._tag === 'MissingSnappedToMismatch' || report._tag === 'SameRangeMismatch' || report._tag === 'UnsupportedMismatch'); } function askForNextVersion(groupReport, unfixable) { return pipe(Effect.gen(function* ($) { const choices = uniq(groupReport.reports.map(report => report._tagGroup === 'Fixable' ? report.fixable.raw : report._tagGroup === 'Unfixable' ? report.unfixable.rawSpecifier : report._tagGroup === 'Valid' ? report.specifier.raw : null)).filter(isString); const other = chalk `{dim Other}`; const skip = chalk `{dim Skip}`; const quit = chalk `{dim Quit}`; // Ask user to choose a version to align on const choice = yield* $(askForChoice({ message: groupReport.name, choices: [...choices, other, skip, quit], })); if (choice === skip) { return; } // @TODO: Learn https://www.effect.website/docs/data-types/exit if (choice === quit) { return process.exit(0); } const nextVersion = choice === other ? yield* $(askForInput({ message: chalk `${groupReport.name} {dim Enter a replacement version}`, })) : choice; yield* $(pipe(unfixable, Effect.forEach(report => report.unfixable.write(nextVersion)))); }), Effect.catchTags({ AskForChoiceError: Effect.logDebug, AskForInputError: Effect.logDebug, })); }