JamieMason/syncpack

View on GitHub
src/guards/can-add-to-group.ts

Summary

Maintainability
A
0 mins
Test Coverage
A
94%
import { minimatch } from 'minimatch';
import { isNonEmptyArray } from 'tightrope/guard/is-non-empty-array.js';
import type { Ctx } from '../get-context/index.js';
import type { Instance } from '../get-instances/instance.js';
import type { SemverGroup } from '../semver-group/index.js';
import type { VersionGroup } from '../version-group/index.js';

export function canAddToGroup(
  packageJsonFilesByName: Ctx['packageJsonFilesByName'],
  group: SemverGroup.Any | VersionGroup.Any,
  instance: Instance,
): boolean {
  const { dependencies, dependencyTypes, packages, specifierTypes } = group.config;
  return (
    group.canAdd(instance) &&
    matchesDependencyTypes(dependencyTypes, instance) &&
    matchesPackages(packages, instance) &&
    matchesDependencies(packageJsonFilesByName, group, dependencies, instance) &&
    matchesSpecifierTypes(specifierTypes, instance)
  );
}

function matchesDependencies(
  packageJsonFilesByName: Ctx['packageJsonFilesByName'],
  group: SemverGroup.Any | VersionGroup.Any,
  dependencies: unknown,
  instance: Instance,
): boolean {
  // matches if not defined
  if (!isNonEmptyArray(dependencies)) return true;
  return dependencies.some(
    (pattern) =>
      (pattern === '$LOCAL' &&
        instance.name in packageJsonFilesByName &&
        ((group.groupType === 'versionGroup' && instance.versionGroup === null) ||
          (group.groupType === 'semverGroup' && instance.semverGroup === null))) ||
      minimatch(instance.name, pattern),
  );
}

function matchesPackages(packages: unknown, instance: Instance) {
  // matches if not defined
  if (!isNonEmptyArray(packages)) return true;
  return packages.some((pattern) => minimatch(instance.pkgName, pattern));
}

function matchesDependencyTypes(dependencyTypes: unknown, instance: Instance): boolean {
  return matchesKnownList(dependencyTypes, instance.strategy.name);
}

function matchesSpecifierTypes(specifierTypes: unknown, instance: Instance): boolean {
  return matchesKnownList(specifierTypes, instance.rawSpecifier.name);
}

function matchesKnownList(values: unknown, value: string): boolean {
  // matches if not defined
  if (!isNonEmptyArray(values)) return true;
  if (values.join('') === '**') return true;
  const negative: string[] = [];
  const positive: string[] = [];
  values.forEach((name) => {
    if (name.startsWith('!')) {
      negative.push(name.replace('!', ''));
    } else {
      positive.push(name);
    }
  });
  if (isNonEmptyArray(negative) && !negative.includes(value)) return true;
  return isNonEmptyArray(positive) && positive.includes(value);
}