diff --git a/.github/workflows/delete-changeset-bot-comments.yml b/.github/workflows/delete-changeset-bot-comments.yml
new file mode 100644
index 0000000000..32536b5065
--- /dev/null
+++ b/.github/workflows/delete-changeset-bot-comments.yml
@@ -0,0 +1,34 @@
+name: ๐๏ธ Delete Changeset Bot Comments
+
+on:
+ workflow_dispatch:
+
+jobs:
+ delete-comments:
+ name: ๐๏ธ Delete Changeset Bot Comments
+ if: github.repository == 'remix-run/react-router'
+ runs-on: ubuntu-latest
+ permissions:
+ pull-requests: write
+
+ steps:
+ - name: โฌ๏ธ Checkout repo
+ uses: actions/checkout@v6
+
+ - name: ๐ฆ Setup pnpm
+ uses: pnpm/action-setup@v4
+
+ - name: โ Setup node
+ uses: actions/setup-node@v6
+ with:
+ node-version-file: ".nvmrc"
+ cache: pnpm
+
+ - name: ๐ฅ Install deps
+ run: pnpm install --frozen-lockfile
+
+ - name: ๐๏ธ Delete changeset-bot comments
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ node scripts/changes/delete-changeset-bot-comments.ts
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 971fb120ee..4e81985163 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,3 +1,12 @@
+# We use this singular file for all of our releases because we can only specify
+# a singular GitHub workflow file in npm's Trusted Publishing configuration.
+# See https://docs.npmjs.com/trusted-publishers for more info.
+#
+# Specific jobs only run on the proper trigger:
+#
+# - Change file driven stable releases (push to release/hotfix branches)
+# - Experimental releases (from a workflow_dispatch trigger)
+
name: ๐ข Release/Publish
on:
@@ -5,6 +14,10 @@ on:
branches:
- "release"
- "hotfix"
+ workflow_dispatch:
+ inputs:
+ branch:
+ required: true
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -13,7 +26,7 @@ concurrency:
jobs:
check:
name: Check release readiness
- if: github.repository == 'remix-run/react-router'
+ if: github.repository == 'remix-run/react-router' && github.event_name == 'push'
runs-on: ubuntu-latest
outputs:
has_change_files: ${{ steps.check.outputs.has_change_files }}
@@ -38,7 +51,7 @@ jobs:
pull_request:
name: Open pull request
needs: check
- if: needs.check.outputs.has_change_files == 'true'
+ if: github.event_name == 'push' && needs.check.outputs.has_change_files == 'true'
runs-on: ubuntu-latest
permissions:
contents: write # enable pushing changes to the origin
@@ -69,7 +82,7 @@ jobs:
publish:
name: Publish
needs: check
- if: needs.check.outputs.has_change_files == 'false'
+ if: github.event_name == 'push' && needs.check.outputs.has_change_files == 'false'
runs-on: ubuntu-latest
permissions:
contents: write # enable pushing changes to the origin
@@ -97,3 +110,44 @@ jobs:
run: pnpm changes:publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ experimental-release:
+ name: ๐งช Experimental Release
+ if: github.repository == 'remix-run/react-router' && github.event_name == 'workflow_dispatch'
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write # enable pushing changes to the origin
+ id-token: write # enable generation of an ID token for publishing
+ steps:
+ - name: โฌ๏ธ Checkout repo
+ uses: actions/checkout@v6
+ with:
+ ref: ${{ github.event.inputs.branch }}
+ # checkout using a custom token so that we can push later on
+ token: ${{ secrets.GITHUB_TOKEN }}
+ fetch-depth: 0
+
+ - name: ๐ฆ Setup pnpm
+ uses: pnpm/action-setup@v4
+
+ - name: โ Setup node
+ uses: actions/setup-node@v6
+ with:
+ node-version: 24 # Needed for npm@11 for Trusted Publishing
+ cache: "pnpm"
+
+ - name: ๐ฅ Install deps
+ run: pnpm install --frozen-lockfile
+
+ - name: โคด๏ธ Update version
+ run: |
+ git config --local user.email "hello@remix.run"
+ git config --local user.name "Remix Run Bot"
+ pnpm run experimental:version
+ git push origin --tags
+
+ - name: ๐ Build
+ run: pnpm build
+
+ - name: ๐ Publish
+ run: pnpm run experimental:publish
diff --git a/GOVERNANCE.md b/GOVERNANCE.md
index 738b293c85..c822815056 100644
--- a/GOVERNANCE.md
+++ b/GOVERNANCE.md
@@ -135,7 +135,7 @@ This table gives a high-level overview of the stages, but please see the individ
- A proposal enters **Stage 3 โ Beta** once it receives **Stage 2 โ Alpha** PR approvals from 2 SC members and is merged to `dev`
- An SC member authoring the `unstable_` PR counts as an implicit approval, so in those cases explicit approval is required from 1 additional SC member
-- This will include the feature in `nightly` releases and the next normal SemVer release for broader beta testing under the `unstable_` flag
+- This will include the feature in the next normal SemVer release for broader beta testing behind the `unstable_` flag
### Stage 4 โ Stabilization
@@ -151,7 +151,7 @@ This table gives a high-level overview of the stages, but please see the individ
- A proposal enters **Stage 5 โ Stable** once it receives **Stage 4 โ Stabilization** PR approvals from at least 50% of the SC members and is merged to `dev`
- An SC member authoring the stabilization PR counts as an implicit approval
-- This will include the stable feature in `nightly` releases and the next normal SemVer release
+- This will include the stable feature in the next normal SemVer release
## Meeting Notes
diff --git a/docs/explanation/styling.md b/docs/explanation/styling.md
new file mode 100644
index 0000000000..6f1dd987a1
--- /dev/null
+++ b/docs/explanation/styling.md
@@ -0,0 +1,87 @@
+---
+title: Styling
+---
+
+# Styling
+
+[MODES: framework]
+
+
+
+
+Framework mode uses the React Router Vite plugin, so the styling story is mostly just Vite's styling story.
+
+React Router does not have a separate CSS pipeline for Framework mode. In practice, there are three patterns that matter:
+
+1. Import CSS as a side effect
+2. Use the route module `links` export
+3. Render a stylesheet `` directly
+
+## Side-Effect CSS Imports
+
+Because Framework mode uses Vite, you can import CSS files as side effects:
+
+```tsx filename=app/root.tsx
+import "./app.css";
+```
+
+```tsx filename=app/routes/dashboard.tsx
+import "./dashboard.css";
+```
+
+This is often the simplest option. Global styles can be imported in `root.tsx`, and route or component styles can be imported next to the module that uses them.
+
+## `links` Export
+
+React Router also supports adding stylesheets through the route module `links` export.
+
+This is useful when you want a stylesheet URL from Vite and need React Router to render a real `` tag for the route:
+
+```tsx filename=app/routes/dashboard.tsx
+import dashboardHref from "./dashboard.css?url";
+
+export function links() {
+ return [{ rel: "stylesheet", href: dashboardHref }];
+}
+```
+
+The `links` export feeds the [``][links-component] component in your root route. This is the React Router-specific styling API in Framework mode. For more on route module exports, see [Route Module][route-module].
+
+## Direct `` Rendering
+
+If you're using React 19, you can also render a stylesheet `` directly in your route component:
+
+```tsx filename=app/routes/dashboard.tsx
+import dashboardHref from "./dashboard.css?url";
+
+export default function Dashboard() {
+ return (
+ <>
+
+
Dashboard
+ >
+ );
+}
+```
+
+This uses React's built-in [``][react-link] support, which hoists the stylesheet into the document ``. That gives you another way to colocate stylesheet tags with the route that needs them.
+
+## Everything Else
+
+For CSS Modules, Tailwind, PostCSS, Sass, Vanilla Extract, and other styling tools, use the normal Vite setup for those tools.
+
+See:
+
+- [Vite CSS Features][vite-css]
+- [Vite Static Asset Handling][vite-assets]
+- [``][links-component]
+
+[links-component]: ../api/components/Links
+[react-link]: https://react.dev/reference/react-dom/components/link
+[route-module]: ../start/framework/route-module
+[vite-assets]: https://vite.dev/guide/assets.html
+[vite-css]: https://vite.dev/guide/features.html#css
diff --git a/docs/start/framework/route-module.md b/docs/start/framework/route-module.md
index 8bc17cbf5e..fe780e6773 100644
--- a/docs/start/framework/route-module.md
+++ b/docs/start/framework/route-module.md
@@ -411,6 +411,10 @@ export default function Root() {
}
```
+See also:
+
+- [Styling][styling]
+
## `meta`
Route meta defines [meta tags][meta-element] to be rendered in the `` component, usually placed in the ``.
@@ -520,3 +524,4 @@ Next: [Rendering Strategies](./rendering)
[data-mode-should-revalidate]: ../data/route-object#shouldrevalidate
[spa-mode]: ../../how-to/spa
[client-data]: ../../how-to/client-data
+[styling]: ../../explanation/styling
diff --git a/package.json b/package.json
index c52c6ee3e6..d61af2ba8b 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,8 @@
"docs": "pnpm run docs:typedoc && pnpm run docs:jsdoc",
"docs:typedoc": "typedoc",
"docs:jsdoc": "node --experimental-strip-types scripts/docs.ts",
+ "experimental:version": "node ./scripts/experimental.ts version",
+ "experimental:publish": "node ./scripts/experimental.ts publish",
"format": "prettier --ignore-path .prettierignore --write .",
"format:check": "prettier --ignore-path .prettierignore --check .",
"lint": "eslint --cache .",
diff --git a/scripts/changes/check-pr.ts b/scripts/changes/check-pr.ts
index d3c0c095ea..59bb95e35e 100644
--- a/scripts/changes/check-pr.ts
+++ b/scripts/changes/check-pr.ts
@@ -21,12 +21,12 @@ import {
const COMMENT_MARKER = "";
const COMMENT_FOUND = `${COMMENT_MARKER}
-## โ Change File Found
+### โ Change File Found
A \`.changes\` file has been found in this PR. Thanks!`;
const COMMENT_MISSING = `${COMMENT_MARKER}
-## ๐ No Change File Found
+### ๐ No Change File Found
This PR doesn't include a change file which is used for automated release notes.
If your change affects users, please add one (or more) and commit the generated file(s).
diff --git a/scripts/changes/delete-changeset-bot-comments.ts b/scripts/changes/delete-changeset-bot-comments.ts
new file mode 100644
index 0000000000..29d931e4d6
--- /dev/null
+++ b/scripts/changes/delete-changeset-bot-comments.ts
@@ -0,0 +1,89 @@
+/**
+ * Finds and deletes comments from `changeset-bot` on open PRs created since 1/1/2026
+ *
+ * Usage:
+ * node scripts/changes/delete-changeset-bot-comments.ts [--dry-run]
+ *
+ * Environment:
+ * GITHUB_TOKEN - Required. GitHub token with pull-requests:write permission.
+ */
+import {
+ createPrComment,
+ deletePrComment,
+ getPrComments,
+ listOpenPrs,
+} from "../utils/github.ts";
+
+const CHANGESET_BOT = "changeset-bot[bot]";
+const CUTOFF = new Date(2026, 0, 1);
+
+const ADD_CHANGE_FILE =
+ "๐ We've moved away from Changesets to our own internal " +
+ "[changes process](https://github.com/remix-run/react-router/blob/main/docs/community/contributing.md#change-files). " +
+ "Please manually add a change file to this branch, or you can merge in the " +
+ "latest `dev` branch and run `pnpm run changes:add` to add a change file.";
+
+const MIGRATE_CHANGE_FILE =
+ "๐ We've moved away from Changesets to our own internal " +
+ "[changes process](https://github.com/remix-run/react-router/blob/main/docs/community/contributing.md#change-files). " +
+ "Please convert your changesets file to a change file in the proper package directory " +
+ "(i.e., `packages/react-router/.changes/patch.fix-some-bug.md`).";
+
+const dryRun = process.argv.includes("--dry-run");
+
+if (dryRun) {
+ console.log("[DRY RUN] No comments will be deleted.\n");
+}
+
+console.log(
+ `Fetching open PRs created after ${CUTOFF.toISOString().slice(0, 10)}...\n`,
+);
+
+let prs = await listOpenPrs({
+ createdAfter: CUTOFF,
+ base: "dev",
+});
+console.log(`Found ${prs.length} open PR${prs.length === 1 ? "" : "s"}.\n`);
+
+let totalDeleted = 0;
+let totalSkipped = 0;
+
+for (let pr of prs) {
+ let comments = await getPrComments(pr.number);
+ let botComments = comments.filter((c) => c.user?.login === CHANGESET_BOT);
+
+ if (botComments.length === 0) continue;
+
+ console.log(`PR #${pr.number}: ${pr.title}`);
+
+ for (let comment of botComments) {
+ let preview = (comment.body ?? "").slice(0, 80).replace(/\n/g, " ");
+ if (dryRun) {
+ console.log(
+ ` [DRY RUN] Would delete comment #${comment.id}: "${preview}"`,
+ );
+ totalSkipped++;
+ } else {
+ let hasChangeFile = comment.body?.includes("Changeset detected");
+ await deletePrComment(comment.id);
+ await createPrComment(
+ pr.number,
+ hasChangeFile ? MIGRATE_CHANGE_FILE : ADD_CHANGE_FILE,
+ );
+ console.log(` Deleted comment #${comment.id}: "${preview}"`);
+ totalDeleted++;
+ }
+ }
+
+ console.log();
+}
+
+if (dryRun) {
+ console.log(
+ `Done (dry run): ${totalSkipped} comment${totalSkipped === 1 ? "" : "s"} would be deleted.`,
+ );
+} else {
+ console.log(
+ `Done: ${totalDeleted} comment${totalDeleted === 1 ? "" : "s"} deleted.`,
+ );
+}
diff --git a/scripts/experimental.ts b/scripts/experimental.ts
new file mode 100644
index 0000000000..5ef44fed42
--- /dev/null
+++ b/scripts/experimental.ts
@@ -0,0 +1,151 @@
+import * as fs from "node:fs";
+import * as path from "node:path";
+import { fileURLToPath } from "node:url";
+import { colorize, colors } from "./utils/color.ts";
+import { logAndExec } from "./utils/process.ts";
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const rootDir = path.resolve(__dirname, "..");
+
+const packageDirNames = fs
+ .readdirSync("packages")
+ .filter((name) => fs.statSync(`packages/${name}`).isDirectory());
+
+const command = process.argv[2];
+const args = process.argv.slice(3);
+const dryRun = args.includes("--dry-run");
+
+if (command === "version") {
+ await bumpVersion();
+} else if (command === "publish") {
+ await publishPackages();
+} else {
+ console.error(
+ `Usage: node scripts/experimental.ts [version | publish] [--dry-run] `,
+ );
+ process.exit(1);
+}
+
+async function bumpVersion() {
+ if (!dryRun) {
+ let status = logAndExec("git status --porcelain", true).trim();
+ let lines = status.split("\n");
+ invariant(
+ lines.every((line) => line === "" || line.startsWith("?")),
+ "Working directory is not clean. Please commit or stash your changes.",
+ );
+ }
+
+ let sha = logAndExec("git rev-parse --short HEAD", true);
+ invariant(sha != null, "Failed to get git SHA");
+ let version = `0.0.0-experimental-${sha}`;
+ if (dryRun) {
+ console.log(
+ colorize(
+ ` [Dry Run] Would create and switch to branch experimental/${version}\n`,
+ colors.yellow,
+ ),
+ );
+ } else {
+ logAndExec(`git checkout -b experimental/${version}`);
+ }
+
+ for (let packageDirName of packageDirNames) {
+ if (dryRun) {
+ console.log(
+ colorize(
+ ` [Dry Run] Would update ${packageDirName} to version ${version}`,
+ colors.yellow,
+ ),
+ );
+ } else {
+ let packageName = updatePackageJson(packageDirName, (pkg) => {
+ pkg["version"] = version;
+ });
+ console.log(
+ colorize(
+ ` Updated ${packageName} to version ${version}`,
+ colors.green,
+ ),
+ );
+ }
+ }
+
+ if (!dryRun) {
+ logAndExec(`git commit -am "Version ${version}"`);
+ logAndExec(`git tag -am "Version ${version}" v${version}`);
+ console.log(
+ colorize(` Committed and tagged version ${version}`, colors.green),
+ );
+ }
+}
+
+async function publishPackages() {
+ // Ensure we are in CI. We don't do this manually without --dry-run
+ invariant(
+ dryRun || process.env.CI,
+ `You should always run the publish script from the CI environment!`,
+ );
+
+ // Get the current tag, which has the release version number
+ let version = logAndExec("git tag --list --points-at HEAD", true)
+ .split("\n")
+ .find((t) => t.includes("experimental"))
+ ?.replace(/^v/, "");
+
+ invariant(version, "Missing experimental version");
+
+ // Ensure build versions match the release version
+ for (let packageDirName of packageDirNames) {
+ let pkgVersion = readPackageJson(packageDirName).version;
+ invariant(
+ pkgVersion === version,
+ `Package ${packageDirName} is on version ${pkgVersion}, but should be on ${version}`,
+ );
+ }
+
+ // 4. Publish to npm
+ let tag = "experimental";
+ let publishCommand = `pnpm publish --recursive --filter "./packages/*" --access public --tag ${tag} --no-git-checks --report-summary`;
+ if (dryRun) {
+ console.log(
+ colorize(
+ ` [Dry Run] Would publish version ${version} to npm with tag "${tag}" via command:\n` +
+ ` ${publishCommand}`,
+ colors.yellow,
+ ),
+ );
+ } else {
+ console.log(` Publishing version ${version} to npm with tag "${tag}"`);
+ logAndExec(publishCommand);
+ console.log(` Publishing completed`);
+ }
+}
+
+// --- Utilities ---
+
+function invariant(cond: unknown, message: string): asserts cond {
+ if (!cond) throw new Error(message);
+}
+
+function readPackageJson(packageDirName: string): Record {
+ let file = path.join(rootDir, "packages", packageDirName, "package.json");
+ let raw: unknown = JSON.parse(fs.readFileSync(file, "utf-8"));
+ invariant(
+ typeof raw === "object" && raw !== null,
+ `Invalid package.json at ${file}`,
+ );
+ return raw as Record;
+}
+
+function updatePackageJson(
+ packageDirName: string,
+ transform: (pkg: Record) => void,
+): string | undefined {
+ let file = path.join(rootDir, "packages", packageDirName, "package.json");
+ let pkg = readPackageJson(packageDirName);
+ transform(pkg);
+ fs.writeFileSync(file, JSON.stringify(pkg, null, 2) + "\n");
+ let name = pkg["name"];
+ return typeof name === "string" ? name : undefined;
+}
diff --git a/scripts/publish.js b/scripts/publish.js
deleted file mode 100644
index 40aa7c75db..0000000000
--- a/scripts/publish.js
+++ /dev/null
@@ -1,154 +0,0 @@
-const path = require("path");
-const { execSync } = require("child_process");
-
-const jsonfile = require("jsonfile");
-const semver = require("semver");
-
-const rootDir = path.resolve(__dirname, "..");
-
-/**
- * @param {*} cond
- * @param {string} message
- * @returns {asserts cond}
- */
-function invariant(cond, message) {
- if (!cond) throw new Error(message);
-}
-
-/**
- * @returns {string}
- */
-function getTaggedVersion() {
- let output = execSync("git tag --list --points-at HEAD").toString();
- return output.replace(/^v|\n+$/g, "");
-}
-
-/**
- * @param {string} packageName
- * @param {string|number} version
- */
-async function ensureBuildVersion(packageName, version) {
- let file = path.join(rootDir, "packages", packageName, "package.json");
- let json = await jsonfile.readFile(file);
- invariant(
- json.version === version,
- `Package ${packageName} is on version ${json.version}, but should be on ${version}`,
- );
-}
-
-/**
- * @param {string} packageName
- * @param {string} tag
- */
-function publishBuild(packageName, tag, releaseBranch) {
- let buildDir = path.join(rootDir, "packages", packageName);
-
- let args = ["--access public"];
- if (tag) {
- args.push(`--tag ${tag}`);
- }
-
- if (tag === "experimental" || tag === "nightly") {
- args.push("--no-git-checks");
- } else if (releaseBranch) {
- args.push(`--publish-branch ${releaseBranch}`);
- } else {
- throw new Error(
- "Expected a release branch name to be provided for non-experimental/nightly releases",
- );
- }
- console.log();
- console.log(` pnpm publish ${buildDir} --tag ${tag} --access public`);
- console.log();
- execSync(`pnpm publish ${buildDir} ${args.join(" ")}`, {
- stdio: "inherit",
- });
-}
-
-/**
- * @returns {Promise<1 | 0>}
- */
-async function run() {
- try {
- // 0. Ensure we are in CI. We don't do this manually
- invariant(
- process.env.CI,
- `You should always run the publish script from the CI environment!`,
- );
-
- // 1. Get the current tag, which has the release version number
- let version = getTaggedVersion();
- invariant(
- version !== "",
- "Missing release version. Run the version script first.",
- );
-
- // 2. Determine the appropriate npm tag to use
- let releaseBranch;
- let tag;
- if (version.includes("experimental")) {
- tag = "experimental";
- } else if (version.includes("nightly")) {
- tag = "nightly";
- } else if (version.startsWith("6.")) {
- // !!! Note: publish.js is not used for prereleases and stable releases.
- // We should be using the Changesets CI process for those.
- // These code paths are only left here for emergency usages
- releaseBranch = "release-v6";
- tag = null;
- } else if (version.startsWith("7.")) {
- // !!! Note: publish.js is not used for prereleases and stable releases.
- // We should be using the Changesets CI process for those.
- // These code paths are only left here for emergency usages
- releaseBranch = "release-next";
- tag = semver.prerelease(version) == null ? "latest" : "pre";
- }
-
- console.log();
- console.log(` Publishing version ${version} to npm with tag "${tag}"`);
-
- // 3. Ensure build versions match the release version
- await ensureBuildVersion("react-router", version);
- await ensureBuildVersion("react-router-dom", version);
- await ensureBuildVersion("react-router-dev", version);
- await ensureBuildVersion("react-router-express", version);
- await ensureBuildVersion("react-router-node", version);
- await ensureBuildVersion("react-router-serve", version);
- await ensureBuildVersion("react-router-architect", version);
- await ensureBuildVersion("react-router-cloudflare", version);
- await ensureBuildVersion("react-router-fs-routes", version);
- await ensureBuildVersion(
- "react-router-remix-routes-option-adapter",
- version,
- );
- await ensureBuildVersion("create-react-router", version);
-
- // 4. Publish to npm
- publishBuild("react-router", tag, releaseBranch);
- publishBuild("react-router-dom", tag, releaseBranch);
- publishBuild("react-router-dev", tag, releaseBranch);
- publishBuild("react-router-express", tag, releaseBranch);
- publishBuild("react-router-node", tag, releaseBranch);
- publishBuild("react-router-serve", tag, releaseBranch);
- publishBuild("react-router-architect", tag, releaseBranch);
- publishBuild("react-router-cloudflare", tag, releaseBranch);
- publishBuild("react-router-fs-routes", tag, releaseBranch);
- publishBuild(
- "react-router-remix-routes-option-adapter",
- tag,
- releaseBranch,
- );
- publishBuild("create-react-router", tag, releaseBranch);
- } catch (error) {
- console.log();
- console.error(` ${error.message}`);
- console.log();
- return 1;
- }
-
- return 0;
-}
-
-run().then((code) => {
- process.exit(code);
-});
diff --git a/scripts/utils/github.ts b/scripts/utils/github.ts
index 5b5e413933..b100b0eeb3 100644
--- a/scripts/utils/github.ts
+++ b/scripts/utils/github.ts
@@ -80,6 +80,29 @@ export async function createRelease(
}
}
+/**
+ * List open PRs
+ */
+export async function listOpenPrs(
+ options: { createdAfter?: Date; base?: string; author?: string } = {},
+) {
+ let response = await request("GET /repos/{owner}/{repo}/pulls", {
+ ...requestOptions(),
+ state: "open",
+ sort: "created",
+ direction: "desc",
+ per_page: 100,
+ ...(options.base ? { base: options.base } : {}),
+ });
+
+ return response.data.filter(
+ (pr) =>
+ (!options.createdAfter ||
+ new Date(pr.created_at) >= options.createdAfter) &&
+ (!options.author || pr.user?.login === options.author),
+ );
+}
+
/**
* Find an open PR from a specific branch to a base branch
*/
diff --git a/scripts/version.js b/scripts/version.js
deleted file mode 100644
index 6fc21a9a94..0000000000
--- a/scripts/version.js
+++ /dev/null
@@ -1,64 +0,0 @@
-const fs = require("node:fs");
-const { execSync } = require("child_process");
-const pc = require("picocolors");
-const semver = require("semver");
-
-const {
- ensureCleanWorkingDirectory,
- invariant,
- updatePackageConfig,
-} = require("./utils");
-
-async function run() {
- try {
- let args = process.argv.slice(2);
- let skipGit = args.includes("--skip-git");
-
- let givenVersion = args[0];
- invariant(
- givenVersion != null,
- `Missing next version. Usage: node version.js [nextVersion]`,
- );
-
- // 0. Make sure the working directory is clean
- if (!skipGit) {
- ensureCleanWorkingDirectory();
- }
-
- // 1. Get the next version number
- let version = semver.valid(givenVersion);
- invariant(version != null, `Invalid version specifier: ${givenVersion}`);
-
- // 2. Bump package versions
- let packageDirNamesToBump = fs
- .readdirSync("packages")
- .filter((name) => fs.statSync(`packages/${name}`).isDirectory());
-
- for (let packageDirName of packageDirNamesToBump) {
- let packageName;
- await updatePackageConfig(packageDirName, (pkg) => {
- packageName = pkg.name;
- pkg.version = version;
- });
- console.log(pc.green(` Updated ${packageName} to version ${version}`));
- }
-
- // 3. Commit and tag
- if (!skipGit) {
- execSync(`git commit --all --message="Version ${version}"`);
- execSync(`git tag -a -m "Version ${version}" v${version}`);
- console.log(pc.green(` Committed and tagged version ${version}`));
- }
- } catch (error) {
- console.log();
- console.error(pc.red(` ${error.message}`));
- console.log();
- return 1;
- }
-
- return 0;
-}
-
-run().then((code) => {
- process.exit(code);
-});