pankod/refine

View on GitHub
packages/cli/src/commands/update/interactive/index.ts

Summary

Maintainability
B
4 hrs
Test Coverage
import inquirer from "inquirer";
import semverDiff from "semver-diff";
import chalk from "chalk";
import { findDuplicates } from "@utils/array";
import { parsePackageNameAndVersion } from "@utils/package";
import { RefinePackageInstalledVersionData } from "@definitions/package";

type UIGroup = {
  patch: {
    name: string;
    from: string;
    to: string;
  }[];
  minor: {
    name: string;
    from: string;
    to: string;
  }[];
  major: {
    name: string;
    from: string;
    to: string;
  }[];
};

export const promptInteractiveRefineUpdate = async (
  packages: RefinePackageInstalledVersionData[],
) => {
  const uiGroup = createUIGroup(packages);
  if (!uiGroup) {
    console.log("All `refine` packages are up to date. 🎉");
    return;
  }

  const inquirerUI = createInquirerUI(uiGroup);

  const answers = await inquirer.prompt<{
    packages: string[];
  }>([
    {
      type: "checkbox",
      name: "packages",
      message: "Choose packages to update",
      pageSize: inquirerUI.pageSize,
      choices: inquirerUI.choices,
      validate: validatePrompt,
    },
  ]);

  return answers.packages.length > 0 ? answers.packages : null;
};

export const validatePrompt = (input: string[]) => {
  const inputParsed = input.map((pckg) => {
    return parsePackageNameAndVersion(pckg);
  });

  const names = inputParsed.map((pckg) => pckg.name);
  const duplicates = findDuplicates(names);

  if (duplicates.length > 0) {
    return `You can't update the same package more than once. Please choice one.\n Duplicates: ${duplicates.join(
      ", ",
    )}`;
  }

  return true;
};

export const createUIGroup = (
  packages: RefinePackageInstalledVersionData[],
): UIGroup | null => {
  if (packages.length === 0) {
    return null;
  }

  const packagesCategorized: UIGroup = {
    patch: [],
    minor: [],
    major: [],
  };

  packages.forEach((pckg) => {
    const current = pckg.current;

    const diffWanted = semverDiff(current, pckg.wanted) as keyof UIGroup;
    const diffLatest = semverDiff(current, pckg.latest) as keyof UIGroup;

    if (diffWanted === diffLatest) {
      if (diffLatest) {
        packagesCategorized[diffLatest].push({
          name: pckg.name,
          from: current,
          to: pckg.latest,
        });
        return;
      }
    }

    if (diffWanted) {
      packagesCategorized[diffWanted].push({
        name: pckg.name,
        from: current,
        to: pckg.wanted,
      });
    }

    if (diffLatest) {
      packagesCategorized[diffLatest].push({
        name: pckg.name,
        from: current,
        to: pckg.latest,
      });
    }
  });

  return packagesCategorized;
};

const createInquirerUI = (uiGroup: UIGroup) => {
  let maxNameLength = 0;
  let maxFromLength = 0;

  [uiGroup.patch, uiGroup.minor, uiGroup.major].forEach((group) => {
    group.forEach((pckg) => {
      if (pckg.name.length > maxNameLength) {
        maxNameLength = pckg.name.length;
      }

      if (pckg.from.length > maxFromLength) {
        maxFromLength = pckg.from.length;
      }
    });
  });

  maxNameLength += 2;

  const choices: (
    | inquirer.Separator
    | {
        name: string;
        value: string;
      }
  )[] = [];

  const packageColumnText = "Package".padEnd(maxNameLength);
  const currentColumnText = "From".padEnd(maxFromLength);
  const toColumnText = "To";
  const header = `\n   ${packageColumnText} ${currentColumnText}${toColumnText.padStart(
    maxFromLength,
  )}`;
  choices.push(new inquirer.Separator(header));

  if (uiGroup.patch.length > 0) {
    choices.push(
      new inquirer.Separator(chalk.reset.bold.blue("\nPatch Updates")),
    );
    uiGroup.patch.forEach((pckg) => {
      choices.push({
        name: `${pckg.name.padEnd(maxNameLength)} ${pckg.from.padStart(
          maxFromLength,
        )} -> ${pckg.to}`,
        value: `${pckg.name}@${pckg.to}`,
      });
    });
  }

  if (uiGroup.minor.length > 0) {
    choices.push(
      new inquirer.Separator(chalk.reset.bold.blue("\nMinor Updates")),
    );
    uiGroup.minor.forEach((pckg) => {
      choices.push({
        name: `${pckg.name.padEnd(maxNameLength)} ${pckg.from.padStart(
          maxFromLength,
        )} -> ${pckg.to}`,
        value: `${pckg.name}@${pckg.to}`,
      });
    });
  }

  if (uiGroup.major.length > 0) {
    choices.push(
      new inquirer.Separator(chalk.reset.bold.blue("\nMajor Updates")),
    );
    uiGroup.major.forEach((pckg) => {
      choices.push({
        name: `${pckg.name.padEnd(maxNameLength)} ${pckg.from.padStart(
          maxFromLength,
        )} -> ${pckg.to}`,
        value: `${pckg.name}@${pckg.to}`,
      });
    });
  }

  const pageSize = choices.length + 6;

  return { choices, pageSize };
};