syncpack

repository·main·Indexed 24 days ago

https://github.com/jamiemason/syncpack

A tool for maintaining consistent dependency versions in large JavaScript monorepos. It provides commands to lint for version mismatches, automatically fix issues, update packages to the latest versions from the npm registry, and format package.json files. The project also includes a Rust-based specifier library for parsing and managing various package versioning formats, including semver, Git dependencies, and pnpm catalog specifiers.

Tokens
55.6K
Snippets
128
Records
340
Agent score
83%

What's inside syncpack

  1. Understand the DiffersToLocal issue

    main

    The DiffersToLocal issue occurs when a dependency version in a package mismatches the version of its corresponding Local Instance within the workspace.

    This issue is triggered when:

    1. The instance belongs to a Highest Semver or Lowest Semver version group.
    2. The version used in the current package does not match the version defined in the local workspace package.

    Fix Effect: When fixed, syncpack will rewrite the instance to match the version defined in the local package.

  2. Understand the SameMinorMismatch error

    main

    The SameMinorMismatch error occurs when a dependency version differs from other versions within its same-minor version group.

    This error is triggered when:

    • An instance mismatches its Same Minor version group.
    • An instance does not belong to a With Range semver group.

    Note that this error is considered unfixable by syncpack because the tool cannot determine which specific range the user intends to use, requiring manual intervention.

  3. Configure semver range formats with semverGroups

    main

    Use the semverGroups configuration option to allow specific packages or dependencies to follow different semver range rules than the rest of your monorepo.

    Rules of precedence:

    • Each dependency can only belong to one semver group.
    • The first rule that matches a given dependency and package will be the one applied.

    Configuration Keys:

    • packages: An array of package names (or globs) that this rule applies to.
    • dependencies: An array of dependency names (or globs) that this rule applies to.
    • dependencyTypes: An array of dependency types (e.g., prod, dev, peer) that this rule applies to.
    • range: The semver range format to enforce (e.g., ^, ~, or "" for fixed versions).
    {
      "semverGroups": [
        {
          "packages": ["@myrepo/library"],
          "range": "~"
        }
      ]
    }
  4. Use Update Groups to control registry updates

    main

    Update groups allow you to define per-dependency rules for what counts as an eligible registry update when using the syncpack update command. You can use them to:

    • Clamp specific dependencies to patch updates only.
    • Limit updates to minor versions.
    • Opt specific dependencies out of the update process entirely using isIgnored: true.

    When syncpack processes a dependency, it iterates through the updateGroups array in the order they are defined. The first match wins, and syncpack stops searching for that dependency. If no group matches, the dependency falls back to the CLI's --target value (which defaults to latest).

    // Example of update group configuration
    {
      "updateGroups": [
        { "dependencies": ["storybook", "@storybook/**"], "target": "patch" },
        { "dependencyTypes": ["dev"], "target": "minor" },
        { "dependencyTypes": ["prod"], "target": "patch" },
        { "dependencyTypes": ["peer"], "isIgnored": true }
      ]
    }
  5. Use the Highest Semver version group

    main

    The highestSemver version group requires all dependencies in the group to use the highest semver version found across all instances where that dependency is already installed in your monorepo. If versions differ, syncpack will align them to the highest version currently present in the workspace.

    Configuration Options

    When defining a highestSemver group in your .syncpackrc.json, you can use the following optional keys:

    • dependencies: A list of specific package names to include in this group.
    • dependencyTypes: Filters which dependency types (e.g., dependencies, devDependencies) are included.
    • specifierTypes: Filters which version specifier types (e.g., exact versions, ranges) are included.
    • label: A custom label for the group.
    • packages: Limits the rule to specific packages within the monorepo.
    • severity: Defines how to report issues found by this group.
    {
      "versionGroups": [
        {
          "dependencies": ["legacy-pkg"],
          "severity": {
            "DiffersToHighestOrLowestSemver": "warn"
          }
        }
      ]
    }
  6. Configure dependency types for groups

    main

    In syncpack configuration, a "dependency type" specifies the path or nested property within package.json files where dependencies are located. You can use these types to restrict which dependencies are assigned to a specific group.

    • Inclusion: When a type is set, only dependencies found in those specific locations are assigned to the group.
    • Default Behavior: If no type is specified, syncpack matches dependencies everywhere they are found.
    • Negation: You can use the ! prefix to exclude specific types. For example, ["!dev", "!prod"] assigns everything except dependencies and devDependencies to the group.
    • Catalog Support: If you are using pnpm or Bun catalogs, you can use auto-generated catalog dependency types as valid values.
  7. Understand the NonSemverMismatch error

    main

    A NonSemverMismatch occurs when a dependency uses a version specifier that does not follow standard Semantic Versioning (SemVer) patterns (e.g., it is not a 'Simple Semver' like 1.2.3 or ^1.2.3), and this non-standard specifier differs across multiple instances of the same package in your workspace.

    This error is triggered when:

    1. The instance belongs to a version group (either the Highest Semver or Lowest Semver group).
    2. The instance's version specifier mismatches other instances in that same group.
    3. One or more instances in the group use non-simple SemVer specifiers.

    Because the specifiers are non-standard, syncpack cannot determine which version is the correct one to synchronize to, making this error unfixable automatically.

  8. Exclude dependencies from validation using Ignored Version Groups

    main

    You can instruct syncpack to completely ignore specific dependencies from version validation and synchronization checks by using an 'Ignored' Version Group.

    To incrementally adopt syncpack in a large codebase, a recommended strategy is to define specific version groups for dependencies you can easily fix, and then add a catch-all 'Ignored' Version Group as the last member of your versionGroups array. This prevents syncpack from reporting issues on dependencies you aren't ready to manage yet.

    {
      "versionGroups": [
        {
          "dependencies": ["keep-walking"],
          "isIgnored": true
        }
      ]
    }
  9. Understand the difference between dependencies and peer dependencies

    main

    In a monorepo or package ecosystem, dependencies and peerDependencies serve different roles and require different versioning strategies:

    • dependencies: You are the consumer. You typically use narrower version ranges (e.g., exact versions like 1.2.3, patch ranges ~1.2.0, or minor ranges ^1.2.0) to ensure stability and confidence in what your code is running.
    • peerDependencies: You are the provider. You typically use much broader version ranges (e.g., ^1 or >=6.0.0 <9.0.0) to ensure your package is compatible with as many consuming projects as possible.

    You can identify which dependency types are present in your project by running:

    syncpack list --dependency-types peer
  10. Understand syncpack dependency terminology

    main

    To use syncpack effectively, distinguish between these core concepts:

    • Dependency vs. Instance: A Dependency is the package itself (e.g., uuid). An Instance is a specific occurrence of that dependency in a package.json file. One dependency can have multiple instances across a monorepo.
    • Local Instance: An instance that refers to a package developed internally within your monorepo (defined by the package's own version property).
    • Dependency Type: The location within package.json where a dependency is defined. Standard types include prod (dependencies), dev (devDependencies), peer (peerDependencies), overrides, pnpmOverrides, resolutions, and local.
    • Custom Type: A user-defined configuration that extends syncpack to manage non-standard sections like engines, packageManager, or arbitrary nested properties. They behave identically to standard types.
    • Package: A module in your monorepo defined by a package.json file.
    • Workspace: A collection of packages managed together (via npm, Yarn, or pnpm) that syncpack uses to locate all package.json files.
  11. Understand the MatchConflictsWithLocal error

    main

    The MatchConflictsWithLocal error occurs when a dependency satisfies its assigned semver group rules but creates a conflict with the local workspace package version policy.

    This specific conflict is triggered when:

    1. The instance belongs to a Highest Semver or Lowest Semver version group.
    2. The instance has the same semver number as the Local Instance in its group.
    3. The instance matches its With Range semver group.
    4. Crucially: The semver range preferred by its With Range group would not satisfy the Local Instance if applied.

    Because syncpack cannot determine if the incompatible range is actually problematic in your specific environment, this is flagged as an issue that requires manual intervention.