Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(scripts): Use relaxed peer dependencies to avoid triggering major version bumps #4736

Merged
merged 6 commits into from
Dec 13, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,8 @@
"access": "public",
"baseBranch": "dev",
"updateInternalDependencies": "patch",
"ignore": []
"ignore": [],
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
"onlyUpdatePeerDependentsWhenOutOfRange": true
chaance marked this conversation as resolved.
Show resolved Hide resolved
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"posttest:integration": "node ./integration/helpers/cleanup.mjs",
"test:primary": "jest",
"changeset": "changeset",
"changeset:version": "changeset version",
"changeset:version": "changeset version && node ./scripts/bump-peer-dep-ranges.mjs",
"changeset:release": "yarn build --tsc --publish && changeset publish",
"version": "node ./scripts/version.js",
"version:experimental": "node ./scripts/version.js experimental",
Expand Down
2 changes: 1 addition & 1 deletion packages/remix-dev/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
"type-fest": "^2.16.0"
},
"peerDependencies": {
"@remix-run/serve": "1.8.0"
"@remix-run/serve": "^1.8.0"
},
"peerDependenciesMeta": {
"@remix-run/serve": {
Expand Down
105 changes: 105 additions & 0 deletions scripts/bump-peer-dep-ranges.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Consider this scenario:
// 1. Run `yarn changeset:version` to bump versions
// 2. Changesets sees that a package has a minor change
// 3. Because we release packages in lockstep, all packages get a minor update
// 4. `@remix-run/dev` has "peerDependencies": { "@remix-run/serve": "1.8.0" }
// 5. This dependency will now be out of range after the update
// 6. Changesets makes the safe bet and updates `@remix-run/dev` to 2.0.0
// because it can't be sure this doesn't result in a breaking change
// 7. Because we release packages in lockstep, all packages get a major update
//
// In practice, this means any `minor` changeset will result in a major bump,
// which definitely isn't what we want.
//
// Instead, we relaxe the peer dependency range for internal packages. That way
// the update doesn't result in an out-of-range peer dependency, and all
// packages are bumped to the next minor release instead.
//
// Because changesets doesn't automatically bump peer dependencies with the
// relaxed range (which makes sense in some cases), this script does that for
// us. This makes the change safer because updating the leading dependency will
// result in a peer dependency warning if the user doesn't bump the peer
// dependency for some reason.
//
// Thanks to Mateusz Burzyński for the original script
// Copyright (c) 2015 David Khourshid, MIT License
// https://github.com/statelyai/xstate/blob/fb4786b80786d8ff3d44dfa818097b219dab623c/scripts/bump-peer-dep-ranges.js
import { spawnSync } from "node:child_process";
import * as fs from "node:fs";
import path from "node:path";
import { createRequire } from "node:module";

const require = createRequire(import.meta.url);
const { getPackagesSync } = require("@manypkg/get-packages");
const gitStatusResult = spawnSync("git", ["status", "--porcelain"]);

if (gitStatusResult.status !== 0) {
process.exit(gitStatusResult.status || undefined);
}

const rootDir = path.join(__dirname, "..");
chaance marked this conversation as resolved.
Show resolved Hide resolved

const allPackages = getPackagesSync(rootDir).packages;
const allPackageNames = allPackages.map((pkg) => pkg.packageJson.name);

const pkgChanges = new Map(
gitStatusResult.stdout
.toString()
.trim()
.split("\n")
.filter((line) => /^\s*M\s+.*\/package.json/.test(line))
.map((line) => {
/**
* @type {string}
* This will always be defined but TS doesn't know that
* @ts-expect-error */
let gitPath = line.match(/[^\s]+package.json/)[0];
let fsPath = path.join(rootDir, gitPath);
let packageJson = require(fsPath);
let previousPackageJsonResult = spawnSync("git", [
"show",
`HEAD:${gitPath}`,
]);

if (previousPackageJsonResult.status !== 0) {
process.exit(gitStatusResult.status || undefined);
}

return [
packageJson.name,
{
path: fsPath,
packageJson: packageJson,
versionChanged:
packageJson.version !==
JSON.parse(previousPackageJsonResult.stdout.toString().trim())
.version,
},
];
})
);

for (let peerPkg of allPackageNames) {
let peerPkgChange = pkgChanges.get(peerPkg);
if (!peerPkgChange || !peerPkgChange.versionChanged) {
continue;
}

for (let dependentPkg of allPackages) {
let peerDeps = dependentPkg.packageJson.peerDependencies;
if (!peerDeps || !peerDeps[peerPkg]) {
continue;
}
let pkgJsonCopy = { ...dependentPkg.packageJson };
// TS not smart enough to realize we checked this before copying the object
// @ts-expect-error
pkgJsonCopy.peerDependencies[
peerPkg
] = `^${peerPkgChange.packageJson.version}`;

fs.writeFileSync(
path.join(dependentPkg.dir, "package.json"),
JSON.stringify(pkgJsonCopy, null, 2) + "\n"
);
}
}
1 change: 1 addition & 0 deletions scripts/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"forceConsistentCasingInFileNames": true,
"allowSyntheticDefaultImports": true,
"moduleResolution": "Node",
"module": "ESNext",
"noEmit": true,
"strict": true,
"target": "ES2018"
Expand Down