Hone logo
Hone
Problems

TypeScript Peer Dependency Resolver

This challenge focuses on defining robust TypeScript types for managing peer dependencies, a common pattern in JavaScript package management. You'll need to create types that accurately represent the structure of package.json's peerDependencies and implement a way to infer or validate the compatibility between different package versions.

Problem Description

Your task is to define TypeScript types that model the peerDependencies section of a package.json file. These types should be flexible enough to handle simple version ranges (e.g., "^1.0.0", ">=2.0.0 <3.0.0") and potentially more complex requirements.

Furthermore, you need to design a mechanism to check if a set of installed packages (represented by their versions) satisfies the peer dependency requirements of a given package. This is crucial for package managers to warn users when incompatible versions are installed.

Key Requirements:

  1. PeerDependency Type: Define a type that represents a single peer dependency, including its name and version requirement.
  2. PackageJson Type: Define a type that represents the relevant parts of a package.json file, specifically focusing on peerDependencies.
  3. VersionRange Type (Optional but Recommended): Consider how to represent and potentially parse/validate version ranges (e.g., SemVer). For this challenge, you can assume a string representation is sufficient if a full parsing implementation is too complex.
  4. Resolver Function/Type: Create a type or function that takes a target package's peer dependencies and a list of installed package versions, and determines if the requirements are met.

Expected Behavior:

  • The types should accurately reflect the structure of peerDependencies.
  • The resolver should correctly identify when a set of installed packages meets or fails to meet the peer dependency requirements.
  • The system should handle cases where a required peer dependency is missing or an incompatible version is present.

Edge Cases to Consider:

  • A package with no peer dependencies.
  • A package that requires a specific version without a range (e.g., "1.2.3").
  • A package that requires a minimum version (e.g., ">=2.0.0").
  • A package that requires a maximum version (e.g., "<3.0.0").
  • A package that requires a version range with both minimum and maximum (e.g., ">=2.0.0 <3.0.0").
  • The case where a required peer dependency is not installed.

Examples

Example 1:

// Assume we have a simplified version parsing utility (or we'll handle strings directly)
// For simplicity, let's assume string matching for basic checks.

// Input:
interface InstalledPackage {
  name: string;
  version: string;
}

const reactPackageJson = {
  name: "react",
  peerDependencies: {
    "object-assign": "^4.0.0",
  },
};

const installedPackages: InstalledPackage[] = [
  { name: "object-assign", version: "4.1.1" },
  { name: "scheduler", version: "0.23.0" },
];

// Output:
// Expected: true (because "object-assign": "4.1.1" satisfies "^4.0.0")

Example 2:

// Input:
const myComponentPackageJson = {
  name: "my-component",
  peerDependencies: {
    react: "^17.0.0",
    "react-dom": "^17.0.0",
  },
};

const installedPackages: InstalledPackage[] = [
  { name: "react", version: "18.2.0" }, // This might be considered compatible depending on exact SemVer logic
  { name: "react-dom", version: "17.0.2" },
];

// Output:
// Expected: true (if "^17.0.0" is understood to include versions like "18.2.0" in certain contexts,
// or false if strict adherence to the specified major version is required. Let's assume strict for now, so false)
// For this challenge, let's refine our interpretation: "^17.0.0" should match "17.x.x" but not "18.x.x".
// So, in this example, the output should be `false` because react "18.2.0" does not satisfy "^17.0.0".

// Let's refine the example based on stricter SemVer interpretation:

const myComponentPackageJsonStrict = {
  name: "my-component",
  peerDependencies: {
    react: "^17.0.0", // Requires a version that starts with 17.x.x
    "react-dom": "^17.0.0",
  },
};

const installedPackagesStrict: InstalledPackage[] = [
  { name: "react", version: "18.2.0" }, // Does NOT satisfy "^17.0.0"
  { name: "react-dom", version: "17.0.2" },
];

// Output:
// Expected: false (because 'react' version 18.2.0 does not satisfy "^17.0.0")

Example 3 (Edge Case - Missing Dependency):

// Input:
const advancedComponentPackageJson = {
  name: "advanced-component",
  peerDependencies: {
    "lodash": "^4.17.0",
    "moment": "2.29.0",
  },
};

const installedPackages: InstalledPackage[] = [
  { name: "lodash", version: "4.17.21" },
  // 'moment' is missing
];

// Output:
// Expected: false (because 'moment' is a required peer dependency and is not installed)

Constraints

  • Version String Format: Assume versions follow Semantic Versioning (SemVer) conventions (e.g., MAJOR.MINOR.PATCH). You do not need to implement a full SemVer parser, but your types and logic should be compatible with standard SemVer ranges.
  • Input Data Structure: package.json's peerDependencies is an object where keys are package names and values are version specifiers (strings). Installed packages will be provided as an array of objects with name and version properties.
  • Performance: For this exercise, performance is not a primary concern, but overly inefficient solutions might be flagged. Focus on correctness and type safety.
  • Scope: You are only concerned with defining the types and the resolution logic. You do not need to implement file reading or actual package.json parsing from disk.

Notes

  • Consider how you will represent the version specifiers. Will you stick to strings, or will you define specific types for common range operators (^, ~, >=, <, etc.)? For this challenge, you can start by treating them as strings and defining a simple logic for common prefixes like ^ and >=.
  • Think about how to map installed package versions to the peerDependencies they are fulfilling.
  • The core of the challenge lies in creating a type-safe and robust way to express these relationships.
  • A good solution will use TypeScript's features effectively to prevent runtime errors and provide clear type definitions.
Loading editor...
typescript