syncpack
Version:
Consistent dependency versions in large JavaScript Monorepos
96 lines (95 loc) • 4.21 kB
JavaScript
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,
}));
}