UNPKG

syncpack

Version:

Consistent dependency versions in large JavaScript Monorepos

93 lines (92 loc) 4.44 kB
import { EOL } from 'node:os'; import chalk from 'chalk-template'; import { Context, Effect, pipe } from 'effect'; import { isNonEmptyArray } from 'tightrope/guard/is-non-empty-array.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 { exitIfInvalid } from '../io/exit-if-invalid.js'; import { IoTag } from '../io/index.js'; import { getSemverGroupHeader } from '../lib/get-group-header.js'; import { padStart } from '../lib/pad-start.js'; import { withLogger } from '../lib/with-logger.js'; export function lintSemverRanges({ io, cli, errorHandlers = defaultErrorHandlers, }) { return pipe(getContext({ io, cli, errorHandlers }), Effect.flatMap(ctx => pipeline(ctx, io, errorHandlers)), Effect.flatMap(exitIfInvalid), Effect.provide(pipe(Context.empty(), Context.add(CliConfigTag, cli), Context.add(IoTag, io))), withLogger); } /** Exported to be reused by `syncpack lint` */ export function pipeline(ctx, io, errorHandlers) { return Effect.gen(function* ($) { // no semver groups have been configured, they are disabled by default if (!isNonEmptyArray(ctx.config.rcFile.semverGroups)) { yield* $(logSemverGroupsDisabledWarning()); return ctx; } const { semverGroups } = yield* $(getInstances(ctx, io, errorHandlers)); let index = 0; for (const group of semverGroups) { const groupSize = group.instances.length; let validCount = 0; if (group._tag === 'Ignored') { yield* $(Effect.logInfo(getSemverGroupHeader({ group, index }))); yield* $(logIgnoredSize(groupSize)); } else if (group._tag === 'WithRange') { yield* $(Effect.logInfo(getSemverGroupHeader({ group, index }))); for (const instance of group.instances) { const report = yield* $(group.inspect(instance)); if (report._tag === 'Valid') { validCount++; continue; } ctx.isInvalid = true; if (report._tag === 'SemverRangeMismatch') { yield* $(logSemverRangeMismatch(report)); } else if (report._tag === 'UnsupportedMismatch') { yield* $(logUnsupportedMismatch(report)); } } } if (validCount > 0) { yield* $(logValidSize(validCount)); } index++; } return ctx; }); } export function logSemverGroupsDisabledWarning() { return Effect.logInfo([ chalk `{red ${ICON.panic} it looks like semver ranges have not yet been configured for this project}`, chalk ` {red see the guide at} {yellow https://jamiemason.github.io/syncpack/guide/semver-groups}`, ].join(EOL)); } export function logIgnoredSize(amount) { const msg = chalk `${padStart(amount)} {dim ${ICON.skip}} ${amount} ignored`; return Effect.logInfo(msg); } function logValidSize(amount) { const msg = chalk `{green ${ICON.tick}} {dim ${amount} valid}`; return Effect.logInfo(msg); } function logSemverRangeMismatch(report) { const _tag = report._tag; const instance = report.fixable.instance; const name = instance.name; const actual = instance.rawSpecifier.raw; const expected = report.fixable.raw; const propPath = instance.strategy.path; const filePath = instance.packageJsonFile.jsonFile.shortPath; return Effect.logInfo(chalk `{red ${ICON.cross}} ${name} {red ${actual}} {dim ${ICON.rightArrow}} {green ${expected}} {gray ${filePath} > ${propPath}} {blue [${_tag}]}`); } function logUnsupportedMismatch(report) { const _tag = report._tag; const instance = report.unfixable; const name = instance.name; const actual = instance.rawSpecifier.raw; const propPath = instance.strategy.path; const filePath = instance.packageJsonFile.jsonFile.shortPath; return Effect.logInfo(chalk `{red ${ICON.cross} name {white ${name}} or version {white ${actual}} are not supported} {gray ${filePath} > ${propPath}} {blue [${_tag}]}`); }