JamieMason / syncpack

Consistent dependency versions in large JavaScript Monorepos.
https://jamiemason.github.io/syncpack/
MIT License
1.41k stars 49 forks source link

question(groups): help with configuring versionGroups #209

Closed rhefner closed 4 months ago

rhefner commented 5 months ago

Description

I have a package @myorg/remix where we keep common Remix utilities. It has peerDependencies set as follows:

"peerDependencies": {
    ...
    "@remix-run/node": "^2.8.1",
    "@remix-run/react": "^2.8.1",
  },

I upgraded our repro to @remix-run/**@2.9.0 today, but the peerDependencies above were not updated and I left them that way. I expected syncpack to clue me in on it, but it didn't... So after some digging, I found that if I don't have this entry in my versionGroups:

{
      label: 'All Remix dependencies use versions the @myorg/remix package is using',
      dependencies: ['@remix-run/!(eslint-config)'],
      dependencyTypes: ['**'],
      snapTo: ['@myorg/remix'],
    },

Then the 'Default Version Group' fails as expected. The moment I added it back, that group passes.

I've been looking through the code to try to figure out what's happening, but the Effect stuff is a little mind-melting and it's going to take me some time to make heads or tails of this. :)

Suggested Solution

None yet.

Help Needed

I would like to make a rule that makes sure @myorg/remix is the source of truth for all other packages when it comes to versions of @remix-run/** packages, with the exception of @remix-run/eslint-config. I thought snapTo was the right thing to use here, but it seems it's acting a bit wonky for me. Maybe I'm using it wrong?

rhefner commented 5 months ago

Then the 'Default Version Group' fails as expected. The moment I added it back, that group passes.

I meant the reverse of this. If the All Remix dependencies use versions the @myorg/remix package is using is not in versionGroups, then Default Version Group succeeds. If it's present, then Default Version Group fails.

rhefner commented 5 months ago

Here's an example of the output when the bad versionGroup entry is omitted:

➜ yarn syncpack list-mismatches
= Use workspace protocol when developing local packages ========================
    75 ✓ already valid
= Default Version Group ========================================================
✘ @remix-run/express 2.8.1 → 2.9.0 package1/package.json > dependencies [HighestSemverMismatch]
✘ @remix-run/node ^2.8.1 → ^2.9.0 remix/package.json > peerDependencies [HighestSemverMismatch]
✘ @remix-run/react ^2.8.1 → ^2.9.0 remix/package.json > peerDependencies [HighestSemverMismatch]
  1313 ✓ already valid
     3 ✓ can be auto-fixed

And when it's present:

➜ yarn syncpack list-mismatches
= Use workspace protocol when developing local packages ========================
    75 ✓ already valid
= All Remix dependencies use versions the @myorg/remix package is using =========
✘ @remix-run/express 2.8.1 → 2.9.0 package1/package.json > dependencies [SnappedToMismatch]
✘ @remix-run/node 2.9.0 → 2.8.1 package3/package.json > dependencies [SnappedToMismatch]
✘ @remix-run/node 2.9.0 → 2.8.1 package4/package.json > dependencies [SnappedToMismatch]
✘ @remix-run/node 2.9.0 → 2.8.1 package5/package.json > dependencies [SnappedToMismatch]
✘ @remix-run/node 2.9.0 → 2.8.1 package6/package.json > dependencies [SnappedToMismatch]
✘ @remix-run/node 2.9.0 → 2.8.1 package1/package.json > dependencies [SnappedToMismatch]
✘ @remix-run/node 2.9.0 → 2.8.1 package2/package.json > dependencies [SnappedToMismatch]
✘ @remix-run/node 2.9.0 → 2.8.1 package7/package.json > dependencies [SnappedToMismatch]
✘ @remix-run/node 2.9.0 → 2.8.1 package8/package.json > dependencies [SnappedToMismatch]
✘ @remix-run/node 2.9.0 → 2.8.1 package9/package.json > dependencies [SnappedToMismatch]
✘ @remix-run/node 2.9.0 → 2.8.1 package10/package.json > dependencies [SnappedToMismatch]
✘ @remix-run/node 2.9.0 → 2.8.1 remix/package.json > dependencies [SnappedToMismatch]
✘ @remix-run/node 2.9.0 → 2.8.1 package11/package.json > dependencies [SnappedToMismatch]
✘ @remix-run/react 2.9.0 → 2.8.1 package3/package.json > dependencies [SnappedToMismatch]
✘ @remix-run/react 2.9.0 → 2.8.1 package4/package.json > dependencies [SnappedToMismatch]
✘ @remix-run/react 2.9.0 → 2.8.1 package5/package.json > dependencies [SnappedToMismatch]
✘ @remix-run/react 2.9.0 → 2.8.1 package6/package.json > dependencies [SnappedToMismatch]
✘ @remix-run/react 2.9.0 → 2.8.1 package1/package.json > dependencies [SnappedToMismatch]
✘ @remix-run/react 2.9.0 → 2.8.1 package2/package.json > dependencies [SnappedToMismatch]
✘ @remix-run/react 2.9.0 → 2.8.1 package7/package.json > dependencies [SnappedToMismatch]
✘ @remix-run/react 2.9.0 → 2.8.1 package8/package.json > dependencies [SnappedToMismatch]
✘ @remix-run/react 2.9.0 → 2.8.1 package9/package.json > dependencies [SnappedToMismatch]
✘ @remix-run/react 2.9.0 → 2.8.1 package10/package.json > dependencies [SnappedToMismatch]
✘ @remix-run/react 2.9.0 → 2.8.1 remix/package.json > dependencies [SnappedToMismatch]
✘ @remix-run/react 2.9.0 → 2.8.1 package11/package.json > dependencies [SnappedToMismatch]
    44 ✓ already valid
    25 ✓ can be auto-fixed
= Default Version Group ========================================================
  1247 ✓ already valid

I thought it was strange that the Default Version Group mentions:

  1313 ✓ already valid
     3 ✓ can be auto-fixed

When it's working properly and:

  1247 ✓ already valid

When it's not.

JamieMason commented 5 months ago

Can you share your full config file or ideally share a reproduction that includes the package.json files?

rhefner commented 5 months ago

Sure thing! Here's the config file for now - I'll try to get a sandbox up and running to reproduce:

// @ts-check

/** @type {import("syncpack").RcFile} */
const config = {
  semverGroups: [
    {
      range: '',
      dependencyTypes: ['dev', 'prod', 'overrides', 'resolutions'],
    },
    {
      range: '^',
      dependencyTypes: ['peer'],
    },
  ],
  source: [
    'package.json',
    'apps/*/package.json',
    'config/*/package.json',
    'lib/*/package.json',
    'templates/*/package.json',
  ],
  sortAz: ['contributors', 'dependencies', 'devDependencies', 'keywords', 'peerDependencies', 'resolutions', 'scripts'],
  sortFirst: ['name', 'description', 'version', 'author', 'type', 'sideEffects'],
  versionGroups: [
    {
      label: 'Use workspace protocol when developing local packages',
      dependencies: ['$LOCAL'],
      dependencyTypes: ['!local'],
      pinVersion: 'workspace:*',
    },
    {
      dependencies: ['@types/**'],
      dependencyTypes: ['!dev'],
      isBanned: true,
      label: '@types packages should only be under devDependencies',
    },
    {
      label: 'All Remix dependencies use versions the @myorg/remix package is using',
      dependencies: ['@remix-run/!(eslint-config)'],
      dependencyTypes: ['**'],
      snapTo: ['@myorg/remix'],
    },
  ],
}

module.exports = config
JamieMason commented 5 months ago

Nothing obvious stands out, my only theory is that maybe the @remix-run/!(eslint-config) glob isn't working as we expect. If you run syncpack list, is every @remix-run/** dependency in the same version group? or split across more than one?