Skip to content

chore(deps): update vitest monorepo to v5 - #2050

Open
stipsan wants to merge 8 commits into
mainfrom
cody/upgrade-vitest-5-0497
Open

stipsan wants to merge 8 commits into
mainfrom
cody/upgrade-vitest-5-0497

Conversation

@stipsan

@stipsan stipsan commented Sep 29, 2026 •

Copy link
Copy Markdown
Member

Upgrades Vitest from 4.1.11 to 5.0.2 across the repo, including the Storybook that #2046 added in dev/storybook, with the same versions and test setup as sanity-io/ui's main.

What's in this PR

  1. Versions. The vitest and @vitest/coverage-v8 catalog entries go from ^4.1.11 to ^5.0.2, as in ui (sanity-io/ui#2864, sanity-io/ui#3020). The Storybook's @vitest/browser-playwright goes from ^4.1.11 to ^5.0.2, ui's version, since it peers on the exact vitest version. Only the vitest tree moves in the lockfile. vitest, @vitest/coverage-v8, @vitest/browser, @vitest/browser-playwright, @vitest/mocker, @vitest/spy, @vitest/ui, @vitest/utils and @vitest/pretty-format resolve to 5.0.2, all as in ui's lockfile. @vitest/expect, @vitest/runner and @vitest/snapshot drop out because vitest 5 bundles them. No vitest 4 package is left, and the Studio next overrides keep main's versions.
  2. Trust policy. vitest 5 depends on why-is-node-running ^3.2.1, which resolves to 3.2.2 (ui locks the same). It was published in January 2025 by the maintainer who published 3.2.0 and 3.2.1 with provenance, but without an attestation, so trustPolicy: no-downgrade rejects it. It gets a version-pinned trustPolicyExclude entry like the existing ones.
  3. CI. vitest 5 writes blob reports to .vitest/blob/, and --merge-reports reads from there (both were .vitest-reports/). The test shards now upload from, and the Coverage report job downloads to, .vitest/blob. Without this the merge step finds no blobs. .gitignore lists .vitest, the one directory vitest 5 writes reports and attachments to, instead of .vitest-reports.
  4. jest-dom setup, as in ui. The six jest-dom setup files (document-internationalization, language-filter, presets, studio-secrets, internationalized-array, media) now call expect.extend with @testing-library/jest-dom/matchers on their own vitest import, and get the matcher types from /// <reference types="@testing-library/jest-dom/vitest" />, like ui's packages/ui/test/setup.ts (sanity-io/ui#2871). The /vitest entry extends whichever vitest pnpm links next to jest-dom. In ui, a second vitest copy after the v5 bump broke toMatchInlineSnapshot that way. Here the lockfile keeps a single vitest copy (dedupePeers: true), so this is a safeguard rather than a fix. AGENTS.md documents the convention.
  5. Docs. AGENTS.md and CONTRIBUTING.md now say Vitest v5.

No test needed changing. None of the v5 breaking changes that could affect tests trip here: mocks cleared before each test by default, unawaited async assertions failing, vi.mock outside the top level throwing, and test.each titles without quotes.

Merges from main

main was merged in five times, with regular merge commits. Each time the merge took main's pnpm-lock.yaml and regenerated it, and a clean main plus this PR's manifest changes reproduces the same lockfile with the same steps. pnpm install --frozen-lockfile accepts the result each time.

  • After the lock file maintenance (#2039). A plain pnpm install with pnpm 11.
  • After pnpm 12.6.0 (#2048).
    • pnpm-workspace.yaml keeps both sides: main's autoDedupe, strictPeerDependencies and trust-policy changes, plus this PR's catalog bumps and why-is-node-running@3.2.2.
    • The lockfile is regenerated with pnpm 12.6.0 run directly, so its packageManagerDependencies document is unchanged.
    • The known @vitejs/devtools/oxc-transform-react toggle in @sanity/debug-preview-url-secret-plugin's peer suffixes is kept at main's variant.
  • After the themer move (#2046).
    • The catalog keeps main's @xstate/react and xstate entries next to vitest 5.
    • The Storybook moves to @vitest/browser-playwright ^5.0.2.
    • The regeneration kept @vitest/browser@4.1.11 and @vitest/runner@4.1.11 from main's lockfile as optional peers of @storybook/addon-vitest, which declares ^3 || ^4 (@vitest/runner has no v5). Removing those entries and addon-vitest's snapshot and reinstalling re-resolves it against @vitest/browser@5.0.2, as in ui's lockfile.
    • The toggle is again main's variant.
  • After oxfmt 0.71 with the lefthook pre-commit hook (#2047), which also brought in the Version Packages release (#2053).
    • Only the lockfile conflicted.
    • npm's next tag had moved past main's pinned Studio versions, and a plain regeneration pulled the Studio overrides along. So the lockfile was resolved through a local registry proxy that serves main's locked version as next, as AGENTS.md describes. The lockfile has no localhost or tarball: entries.
    • addon-vitest was re-resolved the same way as after feat(themer): move @sanity/themer from sanity-io/ui, with a Storybook and ui's Changesets setup #2046.
    • lefthook's oxfmt hook ran on the merge commit and changed nothing.
  • After the lockfile dedupe (#2055), which moved the Studio overrides to 6.17.0-next.112.
    • Only the lockfile conflicted.
    • npm's next is at 6.17.0-next.126 by now. A plain regeneration moved the Studio packages there, and its prune also dropped the @sanity/sdk trust exclusions that next.112 still needs. So the proxy was pinned to main's next.112.
    • addon-vitest was re-resolved again.
    • The change set against main is identical to the previous merges' apart from the Studio version in peer suffixes: 114 lockfile keys and 796 diff lines.

Besides the vitest tree, the lockfile changes the peer suffixes of snapshots that have vitest as an optional peer (sanity@next, @sanity/cli and others). @blazediff/core goes from 1.9.1 to 1.10.0, as @vitest/browser@5.0.2 requires and ui locks.

The Storybook on vitest 5

  • Addon peer range. @storybook/addon-vitest 10.6.0 still declares vitest ^3 || ^4 and @vitest/browser-playwright ^4. The Storybook stays on vitest 5 to match sanity-io/ui, which runs the same addon version on vitest 5 with @vitest/browser-playwright 5.0.2, and all stories pass here. Bump the addon once a release declares vitest 5 support.
  • storybook-test passes on vitest 5.0.2 (4 files, 7 stories) with the Storybook's config unchanged.
  • Themer's unit tests pass in the coverage-enabled PR shards (11 files, 118 tests, within its 30s testTimeout).
  • @storybook/addon-vitest sets screenshotFailures: false, so the storybook-test job's failure-screenshot upload (dev/storybook/**/__screenshots__/) has nothing to upload on either vitest version. It's kept as ui has it, as are feat(themer): move @sanity/themer from sanity-io/ui, with a Storybook and ui's Changesets setup #2046's __screenshots__ and .vitest-attachments ignore entries. vitest 5 itself writes attachments under .vitest/, which .gitignore now covers.

Existing PRs

No open PR covered this upgrade. Renovate's renovate/major-5-vitest-monorepo branch (vitest and @vitest/coverage-v8 to v5) is still listed as rate-limited on the Dependency Dashboard and was never opened, so this PR supersedes it. #2046 listed the vitest upgrade as not started. jsdom 30 (#1767, closed) isn't part of this.

ui setup not carried over

  • resolve.dedupe: ['vitest'] (ui's packages/ui and packages/icons configs): each ui package has its own vitest, while plugins here resolve the root's, so it would be a no-op.
  • The packageExtensions entry that declares jest-dom's vitest peer: jest-dom 7.0.1 already declares it (optional, >= 0.32).
  • experimental.importDurations: ui sets it per package. Here the plugin projects are separate config files that don't inherit the root config (a root-only setting prints nothing), so it would mean touching all ~55 plugin configs.
  • jsdom 30 (ui ^30.1.1, plugins ^29.1.1): not part of vitest, and vitest 5 accepts any jsdom.

Changesets

None. No published package declares vitest: only the private root, scripts/trigger-triage and the private dev/storybook do. All six setup files sit outside what their packages publish (files: ["dist"]). Renovate's vitest 4.1.11 bump (#1927) had none either.

Sibling PRs

pnpm 12 (#2048), the themer move (#2046) and oxfmt (#2047) are merged and in this branch. Merging each remaining sibling's current head into this branch conflicts in:

PR Tool Conflicts with this branch
#2049 oxlint None
#2051 tsdown pnpm-workspace.yaml only: the end of trustPolicyExclude, where it adds patchedDependencies

What Cody needs to do

  • Re-approve. bjoerge's approval was dismissed when the chore(format): align oxfmt with sanity-io/ui (^0.71.0, config), pre-commit hook on lefthook #2047 merge was pushed: the main ruleset dismisses stale approvals on push and requires approval of the last push. No one has approved since. Every required check passes.
  • Add the 🤖 bot label. The agent's label tool only accepts PRs in its primary repository, so the label isn't applied.
  • Nothing else: no new secrets, env vars, Vercel config or required-check changes.

Open questions

  1. Is the why-is-node-running@3.2.2 trust exclusion OK? The alternative is an override to 3.2.1, which has provenance but diverges from ui's lockfile.
  2. Keep the jest-dom setup change? It matches ui, but with a single vitest copy here it changes nothing today. It can come out if you'd rather keep this PR to versions and CI.

Verification

  • CI on the latest merge commit (3b0b097): every check passes: lint, build, knip, storybook-test, the four test shards (235 files), the Coverage report (35.37% of 18,212 lines, merged from .vitest/blob), e2e on chromium and firefox (43 passed, 1 flaky), Bugbot, Socket and both Vercel previews. GitHub started each workflow twice for this push. The concurrency setting cancelled one copy of each, and the required checks and the overall rollup are green.
  • Locally after the merge, on Node 22.22.2 and pnpm 12.6.0: pnpm install --frozen-lockfile, pnpm lint, pnpm knip, oxfmt --check (0.71) and pnpm build (54 tasks, including both studios and the Storybook) pass. pnpm test run passes 235 files and 1,509 tests on vitest 5.0.2, and pnpm test:browser passes 4 files and 7 stories.
  • Earlier on this branch:
    • The coverage flow was checked locally: four --shard=N/4 --coverage --reporter=blob runs write .vitest/blob/blob-N-4.json, and --merge-reports --coverage merges them.
    • The test counts matched vitest 4.1.11's before the upgrade.
    • Dropping the reference types line makes lint fail with TS2339 on toBeInTheDocument, so the type check covers these setups.
Open in Web Open in Cursor 

Bump the `vitest` and `@vitest/coverage-v8` catalog entries from ^4.1.11
to ^5.0.2, the versions sanity-io/ui's `main` uses. Only the vitest tree
moves in the lockfile; the Studio `next` overrides keep their versions.

vitest 5 depends on `why-is-node-running` ^3.2.1, which resolves to
3.2.2 (also what ui locks). It was published by the same maintainer as
3.2.0 and 3.2.1 but without a provenance attestation, which
`trustPolicy: no-downgrade` rejects, so it gets a version-pinned
`trustPolicyExclude` entry.
vitest 5 moved the blob reporter's output and the `--merge-reports`
default from `.vitest-reports/` to `.vitest/blob/`, so the test shards
upload and the coverage report job downloads from there. `.vitest` is
now the one directory vitest writes reports and attachments to, so it
replaces `.vitest-reports` in `.gitignore`.
The six jest-dom setup files imported `@testing-library/jest-dom/vitest`,
which calls `expect.extend` on the vitest that pnpm links next to
jest-dom. When that is a different copy than the runner's, the matchers
land on the wrong `expect`, and in sanity-io/ui a second copy broke
`toMatchInlineSnapshot` after the vitest 5 upgrade. Register the
matchers from `@testing-library/jest-dom/matchers` on the setup file's
own `expect`, with the types from a `reference types` directive, as ui's
setup files do, and document the convention in AGENTS.md.
@changeset-bot

changeset-bot Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 3b0b097

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel

vercel Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
plugins-studio Ready Ready Preview Sep 29, 2026 2:53pm UTC
1 Skipped Deployment
Project Deployment Actions Updated
plugins-e2e-test-studio Ignored Ignored Sep 29, 2026 2:53pm UTC

Request Review

@socket-security

socket-security Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatednpm/​@​vitest/​coverage-v8@​4.1.11 ⏵ 5.0.29910079 +198 -1100
Updatednpm/​vitest@​4.1.11 ⏵ 5.0.298 +110079 +199100
Updatednpm/​@​vitest/​browser-playwright@​4.1.11 ⏵ 5.0.210010086 +198100

View full report

@github-actions

github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

✅ E2E Tests

🟢 43 passed • 🟡 1 flaky • view full report • view run

Studio: https://plugins-e2e-test-studio-32uubgz9s.sanity.dev

Datasets: pr-2050-chromium-36585858502, pr-2050-firefox-36585858502

@github-actions

github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 35.37% 6443 / 18212
🔵 Statements 35.28% 6864 / 19451
🔵 Functions 31.49% 1728 / 5487
🔵 Branches 26.7% 3422 / 12814
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
plugins/@sanity/document-internationalization/src/test/setup.ts 75% 100% 50% 75% 22
plugins/@sanity/language-filter/src/test/setup.ts 50% 100% 0% 50% 14-22
plugins/@sanity/presets/src/test/setup.ts 100% 100% 100% 100%
plugins/@sanity/studio-secrets/src/test/setup.ts 50% 100% 0% 50% 14-22
plugins/sanity-plugin-internationalized-array/src/test/setup.ts 75% 100% 50% 75% 22
plugins/sanity-plugin-media/vitest.setup.ts 100% 75% 50% 100%
Generated in workflow #10236 for commit 3b0b097 by the Vitest Coverage Report Action

Resolves the pnpm-lock.yaml conflict with the lock file maintenance in
#2039 by taking main's lockfile and running pnpm install. Versions only
move for this branch's own changes (the vitest tree: vitest,
@vitest/coverage-v8, @vitest/mocker and @vitest/spy 5.0.2, plus
why-is-node-running 3.2.2, tinybench 6.2.0 and magic-string 1.4.2).
pnpm also recomputes the peer suffixes of snapshots that have vitest as
an optional peer (sanity@next, @sanity/cli and others); the Studio next
versions stay at main's. Clean main plus this branch's
pnpm-workspace.yaml yields the same lockfile.
@stipsan
stipsan marked this pull request as ready for review September 29, 2026 01:19
@stipsan
stipsan requested a review from a team as a code owner September 29, 2026 01:19
@stipsan
stipsan requested review from bjoerge and a balanced review from Copilot and removed request for a team September 29, 2026 01:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The migration is internally consistent, matches Vitest 5 behavior, and all relevant CI checks pass.

Review effort: Balanced
Findings: None

What changed in this PR

Upgrades the monorepo to Vitest 5 and aligns test setup, CI report handling, and documentation with the new release.

Changes:

  • Updates Vitest and coverage dependencies to 5.0.2.
  • Moves CI blob-report handling to .vitest/blob.
  • Registers jest-dom matchers against each suite’s active Vitest instance.
File Description
.github/​workflows/​ci.yml Uses Vitest 5 blob-report paths.
.gitignore Ignores Vitest 5 artifacts.
AGENTS.md Documents Vitest 5 and jest-dom setup.
CONTRIBUTING.md Updates the documented Vitest version.
plugins/​@sanity/​document-internationalization/​src/​test/​setup.ts Registers jest-dom matchers explicitly.
plugins/​@sanity/​language-filter/​src/​test/​setup.ts Registers jest-dom matchers explicitly.
plugins/​@sanity/​presets/​src/​test/​setup.ts Registers jest-dom matchers explicitly.
plugins/​@sanity/​studio-secrets/​src/​test/​setup.ts Registers jest-dom matchers explicitly.
plugins/​sanity-plugin-internationalized-array/​src/​test/​setup.ts Registers jest-dom matchers explicitly.
plugins/​sanity-plugin-media/​vitest.setup.ts Registers matchers on the active Vitest instance.
pnpm-lock.yaml Resolves the Vitest 5 dependency graph.
pnpm-workspace.yaml Updates catalog versions and trust exclusion.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Brings in the pnpm 12.6.0 upgrade from #2048.

pnpm-workspace.yaml keeps both sides: main's autoDedupe,
strictPeerDependencies and trust-policy changes (including its pruned
entries and the vercel exclusions), plus this branch's vitest catalog
bumps and why-is-node-running@3.2.2 exclusion.

pnpm-lock.yaml is main's lockfile regenerated with pnpm 12.6.0 run
directly, so the packageManagerDependencies document stays as on main.
Versions only move for this branch's vitest tree. The regeneration also
flipped the known @vitejs/devtools/oxc-transform-react toggle in
@sanity/debug-preview-url-secret-plugin's sanity and
@sanity/tsdown-config peer suffixes; that part is reverted to main's
variant, which drops the two snapshots it added. A clean main plus
this branch's pnpm-workspace.yaml regenerates the same lockfile before
that revert, and `pnpm install --frozen-lockfile` accepts the result.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The dependency, test setup, lockfile, documentation, and CI report-path changes are consistent with Vitest 5.

Review effort: Balanced
Findings: None

stipsan added a commit that referenced this pull request Sep 29, 2026
… and ui's Changesets setup (#2046)

<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
Moves `@sanity/themer` from sanity-io/ui (`packages/themer`) to
`plugins/@sanity/themer`, adds a React Storybook in `dev/storybook`
modeled on sanity-io/ui's `apps/storybook`, and puts the whole repo on
sanity-io/ui's Changesets setup.

## What's in this PR

1. **Changesets setup identical to sanity-io/ui (repo-wide).**
`.changeset/config.json` gains `"format": "oxfmt"` and
`"bumpVersionsWithWorkspaceProtocolOnly": true` (only the changelog
`repo` differs from ui's). `@changesets/cli` ^3.0.3 and
`@changesets/changelog-github` ^1.0.1 are root devDependencies, and
there's a `changeset` script. `release.yml` drops the npm@11 pin, since
`@changesets/cli` 3.0.3 handles npm 12's `pnpm info --json` output
(changesets/changesets#2274). The Renovate changesets workflow is ui's.
2. **Catalog aligned with sanity-io/ui for what themer needs:**
`@sanity/ui` ^4.2.7, `@sanity/icons` ^5.2.2, `motion` ^13.4.4,
`@sanity/vanilla-extract-vite-plugin` ^0.2.20. These are the ranges
`@sanity/themer@0.8.0` was published with. As AGENTS.md asks for uniform
shared-dependency updates, one combined patch changeset covers the 43
published plugins whose `dependencies` ranges change with the
`@sanity/ui`, `@sanity/icons` and `motion` bumps;
`@sanity/vanilla-extract-vite-plugin` is a devDependency everywhere, so
it doesn't reach a published range.
3. **The move.** Source copied from sanity-io/ui `main` (`b28d66c`),
which is exactly the published 0.8.0. The version and CHANGELOG carry
over, so the next release is 0.8.1. `package.json` follows the monorepo
template (plugins repository links, `build`/`prepack` scripts) with the
published exports, `sideEffects`, and dependency ranges.
`@sanity/themer-legacy` (moving to sanity-io/themer) is the published
^0.2.1. `xstate`, `@xstate/react`, and `scroll-into-view-if-needed` move
to the catalog, since other plugins now share them;
`sanity-plugin-dashboard-widget-vercel` gets its own patch changeset for
its `xstate` range (^5.32.5 → ^5.33.2). The test studio switches from
`^0.8.0` on npm to `workspace:*`; `themerTool()` was already wired in
its Home workspace.
4. **Storybook in `dev/storybook`**, next to the test studios, set up
like sanity-io/ui's `apps/storybook`: Storybook 10.6 with the a11y,
docs, links, themes and vitest addons, the React Compiler through
`@vitejs/plugin-react`, and the vanilla-extract plugin for workspace
plugins' `.css.ts` source. The preview imports `@sanity/ui/styles.css`,
since the published dist doesn't. The global decorator renders every
story in the theme `buildTheme` from `@sanity/themer` builds from a
preset picked in the toolbar, next to the light/dark toggle. It adds
`pnpm dev:storybook`, `pnpm storybook:build`, `pnpm test:browser`, ui's
`apps/storybook/vercel.json` unchanged (as `dev/storybook/vercel.json`),
a `storybook-test` CI job like ui's, and eslint-plugin-storybook's CSF
rules through oxlint's JS plugins, as in ui. Unlike ui's job,
`storybook-test` also installs Playwright's system deps when it restores
the browser cache, as this repo's e2e workflow does (Bugbot flagged that
the cache doesn't hold them). Like the other `dev/*` apps, it's skipped
by `pretest` and the CI build job.

## Layout

No existing folder is renamed or moved, and there's no new top-level
folder. `@sanity/themer` goes into the existing `plugins/@sanity/`
folder next to the other `@sanity/*` plugins, not ui's
`packages/themer`. The Storybook goes into the existing `dev/` folder as
`dev/storybook`, next to `dev/test-studio` and `dev/e2e-studio`, so the
`dev/*` workspace glob and the `./dev/*` build filters already cover it.
The root only gains workspace-level config.

## How faithful the move is

- **Build output vs. the npm tarball of 0.8.0:** JavaScript is identical
apart from chunk hashes, and `bundle.css` is byte-identical. The `.d.ts`
files have the same API; tsdown 0.22 writes `declare …; export {…}`
where 0.23 wrote `export declare …`.
- **Source changes:** `type` modifiers on type-only imports (plugins'
`strictest` tsconfig has `verbatimModuleSyntax`), one inline
`oxlint-disable-next-line react/exhaustive-effect-dependencies` with its
reason (the rule is on here and off in ui; the `[palette]` dependency is
deliberate), and the README license link. The tsconfig is `strictest`
without `noUncheckedIndexedAccess`, which ui never enabled
(`sanity-plugin-graph-view` and `plugin-kit` do the same).
- **No React Compiler typings stub:** ui's
`typings/babel-plugin-react-compiler.d.ts` isn't carried over. Here the
real `babel-plugin-react-compiler` types resolve (it's a linked optional
peer of `@sanity/tsdown-config`), so `defineConfig()` picks the
single-config overload, and a wrong `reactCompiler` value is still
flagged (checked with a bad `target`, a misspelled `transform`, a number
`target` and an unknown key).
- **Tests:** themer's Vitest config sets `testTimeout: 30_000`. PR CI
here runs every project with coverage, and the whole-theme `buildTheme`
tests took 8s there against the 5s default (ui runs them without
coverage).
- **Versions:** every themer runtime dependency and the whole Storybook
stack resolve to the same versions as in ui's lockfile.

## What Cody needs to do

- **npm trusted publishing:** publishing runs from
`.github/workflows/release.yml`, which this repo already had; it now
uses ui's version/publish flow. The trusted publisher for
`@sanity/themer` must be repository `sanity-io/plugins`, workflow
`release.yml`, no environment.
- **Vercel:** the Storybook carries ui's `apps/storybook/vercel.json`
unchanged, as `dev/storybook/vercel.json` (`pnpm build` into
`storybook-static`). To deploy it like ui's `sanity-ui-storybook`,
create a project with root directory `dev/storybook`; the file supplies
the build command and output directory. No other Vercel config is
touched: `dev/e2e-studio/vercel.json` and the `plugins-studio` and
`plugins-e2e-test-studio` projects stay as they are, and both previews
built fine on this PR.
- **Secrets and env vars:** none new. The touched workflows use names
this repo already has: `ECOSPARK_APP_ID`, `ECOSPARK_APP_PRIVATE_KEY`,
`vars.ECOSPARK_CLIENT_ID`, `TURBO_TOKEN` and `vars.TURBO_TEAM`.
`storybook-test` uses none.
- **Ruleset:** no swap needed. The `main` ruleset requires `lint`,
`build`, `test` and the two Socket Security checks, and those job names
are unchanged. Optionally, to match ui (which requires `build`, `knip`,
`Test node@lts / ubuntu-latest`, `storybook-test` and its two Vercel
checks), add `storybook-test` and `knip`, and the Storybook's Vercel
check once that project exists.
- **CODEOWNERS:** untouched, per the transfer convention. Add an owner
for `plugins/@sanity/themer` if you want one.

## Open questions

1. **Patch, not major.** The transfer skill defaults to a major, but
themer is 0.x, nothing breaks, the next release should continue from the
published version, and themer's own deprecation notice reserves 1.0 for
removing `@sanity/themer/legacy`. So the changeset is a patch (0.8.1).
The color move made the same call.
2. **Stories.** sanity-io/ui had no themer stories. I ported the ui
stories that exercise what themer builds (theme
Debug/Build/Canvas/Color, and the color palette story on
`buildPalette`'s output) and added stories for the tool's theme
thumbnails and navbar color wheel. Those two aren't public API, so their
stories import them from `plugins/@sanity/themer/src` by relative path.
Keep them, or public API only?
3. **`sanity` peer range.** I kept 0.8.0's optional `^3 || ^4 || ^5 ||
^6` instead of the monorepo's `catalog:peer` (`^5 || ^6.0.0-0`), which
would drop Studio v3/v4. As with 0.8.0 from npm, it doesn't match the
`sanity@next` prereleases here, so the test studio shows a peer warning.
4. **`bumpVersionsWithWorkspaceProtocolOnly`.** With ui's setting,
Changesets no longer rewrites `@sanity/sfcc`'s intentionally wide
`sanity-plugin-internationalized-array` peer (`^4.0.0 || ^5.0.0`), the
only internal dependency that doesn't use `workspace:`. That seems
right, but it's a behavior change.

## Remaining alignment with sanity-io/ui (not in this PR)

This PR stays scoped to the themer move. Matching ui's tooling happens
in five separate PRs, one per tool, each owned by another worker. All
five exist:

- **pnpm** 11.21.0 → 12.6.0 with ui's pnpm setup, including the
`pnpm/setup@v3` CI setup action: #2048 (merged; this branch is merged
with it)
- **oxfmt** ^0.63 → ^0.71 with ui's config: #2047. Dropping the
`@sanity/plugin-kit/oxfmt` preset's Prettier-era `.changeset/*.md`
`singleQuote: false` override, which ui did when it enabled Changesets'
`format: "oxfmt"`, belongs there too; #2047 doesn't include it yet.
- **vitest** ^4.1 → ^5.0, with `@vitest/coverage-v8`: #2050. If it
merges first, the rebase of this PR raises the Storybook's
`@vitest/browser-playwright` to `^5.0.2` and re-resolves
`@storybook/addon-vitest` in the lockfile, since otherwise two stale
vitest 4 packages stay behind. #2050's worker checked that the Storybook
tests pass on vitest 5 with those two changes (`@storybook/addon-vitest`
10.6 officially peers on vitest 3/4; ui runs it on 5 with loose peers).
- **oxlint** ^1.83 → ^1.86 and **oxlint-tsgolint** ^7.0.2001 → ^7.0.2003
with ui's config: #2049. It turns `react/exhaustive-effect-dependencies`
off, as ui does, so if it merges first, the rebase of this PR drops its
one suppression for that rule in `ImagePaletteSection.tsx`, which would
otherwise be an unused-directive error.
- **tsdown** ^0.22 → ^0.23 with **@sanity/tsdown-config** ^0.27 → ^0.28,
which requires tsdown 0.23: #2051

All five also touch files this PR changes (`pnpm-workspace.yaml`,
`pnpm-lock.yaml` and `AGENTS.md`; `package.json` and `README.md` for
#2047 and #2048; `oxlint.config.ts` for #2049;
`.github/workflows/ci.yml` and `.gitignore` for #2050), so whichever PR
merges later rebases and regenerates the lockfile.

Smaller differences left to Renovate or later PRs: `oxc-transform-react`
^0.147 → ^0.151 (#1988), `@types/node` ^24.13 → ^24.19, `jsdom` ^29 →
^30, `knip` ^6.33 → ^6.38, ui's macOS test run, and ui's root
`engines.node: ">=22.13"`.

## Verification

- `pnpm format`, `pnpm knip`, `pnpm lint`, `pnpm build` (54 tasks,
including both studios and the Storybook), `pnpm test run` (235 files,
1,509 tests) and `pnpm test:browser` (7 stories) all pass locally,
starting from `pnpm install --frozen-lockfile`.
- A `changeset version` dry run in a scratch worktree, with ui's config
and `GITHUB_TOKEN`, bumps themer 0.8.0 → 0.8.1 and
dashboard-widget-vercel 4.0.28 → 4.0.29 (before the combined changeset;
`changeset status` now plans 46 patch releases: the 43 plugins, themer,
and `sanity-plugin-studio-smartling` and `sanity-plugin-transifex`,
which follow `sanity-translations-tab` as internal dependents), credits
@stipsan through the `author:` line, and writes CHANGELOGs that pass
`oxfmt --check`.
- `zizmor .github --min-severity high` reports no findings.

## Transfer verification

- [x] `package.json` dependencies, peerDependencies and exports verified
against the published 0.8.0
- [x] `LICENSE` not added: ui's credits only Sanity.io; the README links
this repo's LICENSE
- [x] Test studio uses the workspace package (`themerTool()` was already
wired)
- [x] `pnpm format`, `pnpm knip`, `pnpm lint`, `pnpm build`, `pnpm test
run` pass
- [x] Changeset added (patch; see open question 1)
- [ ] Trusted publishing for `@sanity/themer` configured with
`sanity-io/plugins` and `release.yml`

## Maintainer follow-up

- [ ] Remove `packages/themer` from sanity-io/ui (with its entries in
ui's root `build`/`test` scripts and `pkg-pr-new.yml`) once this lands,
on your go-ahead
- [ ] Point ui's docs and AGENTS.md mentions of `@sanity/themer` to
`plugins/@sanity/themer`
- [ ] Transfer open themer issues from sanity-io/ui and label them
`themer`

## Screenshots

The theme cards of every preset, in the dark scheme:

[Storybook: themer tool theme thumbnails for every
preset](https://cursor.com/agents/bc-7516e1ad-c642-5cd2-97f4-35288fbda30a/artifacts?path=%2Fopt%2Fcursor%2Fartifacts%2Fstorybook_themer_theme_thumbnails_presets.png)

The palette `buildPalette` generates for the Verdant preset, light
scheme (its backgrounds replace black and white, its accent the blue
scale, its text color the gray scale):

[Storybook: generated Verdant palette with contrast
badges](https://cursor.com/agents/bc-7516e1ad-c642-5cd2-97f4-35288fbda30a/artifacts?path=%2Fopt%2Fcursor%2Fartifacts%2Fstorybook_themer_palette_verdant_light.png)

The Debug theme story in the Pink Synth preset, both schemes:

[Storybook: Debug theme story in the Pink Synth
preset](https://cursor.com/agents/bc-7516e1ad-c642-5cd2-97f4-35288fbda30a/artifacts?path=%2Fopt%2Fcursor%2Fartifacts%2Fstorybook_themer_debug_pink_synth.png)


<sub>To show artifacts inline, <a
href="https://cursor.com/dashboard/cloud-agents#team-pull-requests">enable</a>
in settings.</sub>
<!-- CURSOR_AGENT_PR_BODY_END -->

<div><a
href="https://cursor.com/agents/bc-7516e1ad-c642-5cd2-97f4-35288fbda30a?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a
href="https://cursor.com/background-agent?bcId=bc-7516e1ad-c642-5cd2-97f4-35288fbda30a&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Brings in the themer move and its Storybook in dev/storybook from #2046.

pnpm-workspace.yaml keeps both sides: main's @xstate/react and xstate
catalog entries next to this branch's vitest 5 ranges. The Storybook's
@vitest/browser-playwright goes from ^4.1.11 to ^5.0.2, ui's version,
since it peers on the exact vitest version.

pnpm-lock.yaml is main's lockfile regenerated with pnpm 12.6.0. The
regeneration kept @vitest/browser@4.1.11 and @vitest/runner@4.1.11 from
main's lockfile as optional peers of @storybook/addon-vitest, which
declares ^3 || ^4 (@vitest/runner has no v5). Removing those entries and
addon-vitest's snapshot and reinstalling re-resolves it against
@vitest/browser@5.0.2, as in ui's lockfile, so no vitest 4 package is
left. The second install also returned the known
@vitejs/devtools/oxc-transform-react toggle in
@sanity/debug-preview-url-secret-plugin to main's variant, so the diff
has no toggle. The Studio next versions stay at main's, and a clean main
with this branch's two manifest changes reproduces the same lockfile
with the same steps.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The dependency, CI, and matcher changes are consistent, and the relevant build, test, coverage, lint, and Storybook checks pass.

Review effort: Balanced
Findings: None

bjoerge
bjoerge previously approved these changes Sep 29, 2026
stipsan added a commit that referenced this pull request Sep 29, 2026
…ommit hook on lefthook (#2047)

<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
Brings oxfmt in line with sanity-io/ui's `main`: the same version, the
same config, and the same scripts and CI wiring. The pre-commit hook
moves from husky and lint-staged to lefthook, set up like
sanity-io/sanity's. Supersedes #1919.

## Commits

1. **`chore(deps): update oxfmt to ^0.71.0`**: the catalog goes from
^0.63.0 to ui's ^0.71.0, with ui's oxfmt resolution: oxfmt 0.71.0 and
`tinypool` 2.2.0, the same versions and integrity hashes as ui's
lockfile. There's a patch changeset for `@sanity/plugin-kit`, whose
optional `oxfmt` peer uses the catalog.
2. **`chore(format): align the oxfmt config with sanity-io/ui`**: root
`oxfmt.config.ts` now ignores `storybook-static/**` and `CHANGELOG.md`,
like ui's `.oxfmtrc.json`. `.vscode/settings.json` points
`oxc.fmt.configPath` at `oxfmt.config.ts` instead of the `.oxfmtrc.json`
that #1565 deleted. AGENTS.md records the parity with ui and the
exceptions.
3. **`chore: drop the husky and lint-staged pre-commit hook, like
sanity-io/ui`**: superseded by commit 6, which brings the hook back on
lefthook.
4. **`chore(format): drop the oxfmt preset's .changeset/*.md override,
like sanity-io/ui`**: the `@sanity/plugin-kit/oxfmt` preset loses its
Prettier-era `singleQuote: false` override for `.changeset/*.md`, as
ui's `.oxfmtrc.json` did when ui switched Changesets to oxfmt formatting
(sanity-io/ui#2957). It gets a patch changeset for `@sanity/plugin-kit`,
since the preset is published. This part moved here from #2046 because
it's formatter config. AGENTS.md now explains how changesets get quoted.
5. **`chore(format): run pnpm format`**: requotes this PR's own
changeset from commit 1. Everything else already passed under oxfmt
0.71.
6. **`chore: run oxfmt on staged files with lefthook, like
sanity-io/sanity`**: see below.

## Pre-commit hook: husky and lint-staged → lefthook

This mirrors how sanity-io/sanity replaced husky and lint-staged
(sanity-io/sanity#12755, then sanity-io/sanity#12767 and
sanity-io/sanity#12953):

- **`lefthook` ^2.1.14** as a root devDependency, sanity's range. It
resolves to 2.1.14, as in sanity's lockfile.
- **`allowBuilds: lefthook: true`** in `pnpm-workspace.yaml`, as in
sanity. Lefthook's own install script runs `lefthook install` during
`pnpm install`, and it skips when `CI` is set, which covers GitHub
Actions and Vercel. There's no root `postinstall` script: sanity dropped
its `lefthook install --reset-hooks-path` postinstall in #12767 for this
package build.
- **`lefthook.yml`** with sanity's layout: a parallel `pre-commit` with
two jobs that run oxfmt on staged files with `stage_fixed: true`. The
jobs are `format-scripts` (`*.{js,jsx,ts,tsx,mjs,cjs,mts,cts}`) and
`format-other` (`*.{md,yml,yaml,css,json,jsonc}`).
- `mts`, `cts` and `jsonc` are additions to sanity's globs. They cover
this repo's `.mts`, `turbo.jsonc` and `knip.jsonc` files, which the old
lint-staged glob partly covered.
- sanity's `oxlint --fix` line is left to the oxlint alignment (#2049).
- **Direct binary calls:** the jobs call `node_modules/.bin/oxfmt`
instead of sanity's `pnpm oxfmt`. Running pnpm in a hook triggers pnpm
12's implicit install, which flips the lockfile's peer toggle (see
AGENTS.md). sanity switched to `pnpm oxfmt` in #12953 because lefthook
doesn't put `node_modules/.bin` on `PATH`; the path solves that too.
- **Lockfile:** `pnpm-lock.yaml` gains only lefthook 2.1.14 and its
platform packages (100 lines). A plain install would also have bumped
the Studio's `next` overrides from `6.17.0-next.108` to
`6.17.0-next.112`, so I resolved it through a local registry proxy that
served the locked versions as `next`, as AGENTS.md describes. The
lockfile has no `localhost` or `tarball:` entries.
- **Docs:** AGENTS.md documents the hook, `LEFTHOOK=0`, the one-time
reset for clones that ran husky, and `pnpm
--config.verify-deps-before-run=false <script>` for local runs. README's
note that formatting also runs in a pre-commit hook is back.

## Merges with `main`

`main` is merged in, not rebased, so nothing was force-pushed. After
each merge, `pnpm-lock.yaml` is `main`'s lockfile regenerated with pnpm
12.6.0, invoked directly so `packageManagerDependencies` stays, minus
the peer toggle that AGENTS.md describes.

- **#2039 (lock file maintenance):** only the lockfile conflicted.
- **#2048 (pnpm 12.6.0):** `package.json` conflicted next to the removed
`lint-staged` block. The resolution keeps both sides: `main`'s
`packageManager: pnpm@12.6.0` and `vercel` ^60.1.3, without the block. A
follow-up commit restores `main`'s side of the peer toggle, which the
regeneration had flipped.
- **#2046 (themer move):** only the lockfile conflicted. `pnpm format`
then requoted #2046's three changesets in a separate commit:
`dashboard-widget-vercel-xstate`, `shared-deps-sanity-ui-icons-motion`
and `themer-plugins-monorepo`, 45 frontmatter lines, quote-only. The new
`plugins/@sanity/themer` package and `dev/storybook` already passed.
- **#2053 (Version Packages):** it released and deleted those three
changesets, so the modify/delete conflicts resolve to `main`'s
deletions. It didn't change the lockfile, and `pnpm format` changes
nothing after the merge.

`pnpm-lock.yaml` differs from `main` only by this PR's own changes:

- oxfmt and its bindings go from 0.63.0 to 0.71.0, and `tinypool` from
2.1.0 to 2.2.0.
- husky, lint-staged and their four exclusive dependencies are removed.
- lefthook 2.1.14 and its platform packages are added.

No other version moves, and the Studio stays on `6.17.0-next.108`.

## How it compares with ui

- **Options:** identical, overrides included (`printWidth`, `semi`,
`singleQuote`, `bracketSpacing`, `quoteProps`, `sortImports`,
`sortPackageJson`). They come from the `@sanity/plugin-kit/oxfmt`
preset.
- **Ignore patterns:** `dist/**`, `storybook-static/**`,
`pnpm-lock.yaml` and `CHANGELOG.md` in both. Two differences remain:
  - `turbo/**/*.hbs` is plugins-only (the turbo generator templates).
- ui's `**/sanity.types.ts` is left out. In ui it was for
`apps/docs/sanity.types.ts` and has matched nothing since the docs app
went static. Here, `pnpm typegen` formats
`dev/test-studio/sanity.types.ts` on purpose: the test studio runs
typegen with `formatGeneratedCode: false` and then `pnpm format`.
Ignoring the file would make the next TypeGen PR unformat it.
- **Config file:** plugins keeps `oxfmt.config.ts` re-exporting the
published preset instead of ui's `.oxfmtrc.json`. #1565 made that switch
so the preset is exactly what the monorepo formats with, and
plugin-kit's `verify-package` expects plugins to use it. oxfmt, the Oxc
extension and Changesets' formatter detection all read
`oxfmt.config.ts`. See the open question.
- **Scripts and CI:** the `format` script is identical to ui's (`oxfmt .
--no-error-on-unmatched-pattern`). Neither repo checks formatting on
PRs, and both run the same shared `format-if-needed.yml` on `main`, so
no workflow changes are needed. `oxfmt .` also formats
`.github/workflows/*.yml`, which that workflow can't push, and 0.71
changes none of them. ui has no pre-commit hook; this repo keeps one, on
lefthook.
- **VS Code:** ui points `oxc.fmt.configPath` at its config file, but
plugins pointed it at a file that doesn't exist. I drove `oxfmt --lsp`
directly: with the old setting, the editor silently formats with oxfmt's
defaults (double quotes, semicolons, bracket spacing, unsorted imports),
and with `oxfmt.config.ts` it matches `pnpm format`.

## Changesets

- **Changesets formats what it writes with oxfmt.**
`.changeset/config.json` sets `"format": "oxfmt"` (from #2046). I called
`@changesets/write` (what `changeset add` uses) directly. With the
override it writes `"@sanity/plugin-kit": patch`; without it,
`'@sanity/plugin-kit': patch`, the same as `pnpm format`.
- **`changeset version` is unaffected.** It only hands `CHANGELOG.md`
files to the formatter. This PR ignores those, and oxfmt skips them and
exits 0 thanks to `--no-error-on-unmatched-pattern`.
- **`changeset status` parses the single-quoted frontmatter.** It lists
this PR's two changesets for `@sanity/plugin-kit` 10.0.11.

## What to expect after merging

- **Renovate changesets get a follow-up format PR, as in ui.** The
shared Renovate changesets workflow uses
`mscharley/dependency-changesets-action`, which always writes double
quotes and has no formatting option. After a Renovate PR merges, the
Auto format workflow opens (or updates) a `chore(format): 🤖 ✨` PR that
requotes the changeset. ui has had one after most Renovate merges since
sanity-io/ui#2957: sanity-io/ui#2971, sanity-io/ui#2980,
sanity-io/ui#2986, sanity-io/ui#2998, sanity-io/ui#3002,
sanity-io/ui#3013 and sanity-io/ui#3024.
- **Clones that ran husky need a one-time reset.** They still have
`core.hooksPath=.husky/_`. There, `lefthook install` refuses to install
and prints how to fix it, and `pnpm install` continues. Until the reset,
husky's leftover runner does nothing, so commits simply aren't
formatted. `git config --unset core.hooksPath && pnpm install` fixes it.

## Related PRs

- **#1919** (Renovate, oxfmt ^0.71.0): superseded. This PR has the same
bump, with the lockfile regenerated on current `main`, plus the config
alignment. Close it if Renovate doesn't once this merges.
- **#2046** (themer move) and **#2048** (pnpm 12): merged, and merged
into this PR.
- **#2049** (oxlint) conflicts with this PR in `pnpm-workspace.yaml`,
where its `oxlint` catalog lines sit next to `oxfmt`. Keep both sides.
It could also add sanity's `oxlint --fix` line to the `format-scripts`
job, calling `node_modules/.bin/oxlint` directly.
- **#2050** (vitest 5) conflicts only in `pnpm-lock.yaml`.
- **#2051** (tsdown) doesn't conflict.
- Whichever PR lands later merges `main` and regenerates the lockfile
without committing the peer toggle.

## What Cody needs to do

- Add the `🤖 bot` label: the agent's label tool couldn't edit PRs in
this repository.
- Triage Socket's alert. It flags `lefthook@2.1.14`'s install script
(`node postinstall.js`) as a low-priority warning. That script is what
installs the hook: `allowBuilds: lefthook: true` enables it, as in
sanity-io/sanity, and it skips when `CI` is set. Mark it as acceptable
risk if you agree. Socket's required checks pass either way.
- After merging, tell contributors to run `git config --unset
core.hooksPath && pnpm install` once, so lefthook replaces husky's hook.
- Merge the Renovate follow-up format PRs as they come, as in ui.
- No Vercel, secrets or ruleset changes, and no new secrets or env vars.

## Open question

**`.oxfmtrc.json`:** should plugins switch to ui's JSON config file,
too? That would mean no longer re-exporting the plugin-kit preset, and
it would reverse #1565. I kept `oxfmt.config.ts`, since the formatting
result is already the same as ui's.

## Verification

- **The hook**, tested on throwaway commits, with `pnpm-lock.yaml`
untouched each time:
  - Staged TS and JSON files get formatted and re-staged.
- A tracked `.vscode/settings.json` (its folder is gitignored) is
re-staged without the `[FAILED]` that lint-staged printed.
  - A staged `CHANGELOG.md`, which oxfmt ignores, passes as-is.
- In a partially staged file, only the staged hunk is formatted and the
unstaged hunk is kept.
- The commit that adds the hook ran it on its own files in 0.4 seconds.
- **Clones that ran husky:** with `core.hooksPath=.husky/_`, `lefthook
install` exits 1 with instructions and installs nothing, as described
above.
- **Local checks** at the head commit, with pnpm 12.6.0 on Node 24.21
and `CI=true` (lefthook's install script skips):
- `pnpm install --frozen-lockfile`, which leaves `pnpm-lock.yaml`
untouched
  - `oxfmt --check` (2,018 files)
  - `oxlint` and `knip`
  - the full turbo build (54 tasks)
  - `vitest run` (235 files, 1,509 tests)
  - `pnpm test:browser` (4 files, 7 stories)
- **CI** is fully green at the head commit (`123af257`): build, lint,
knip, all four test shards, coverage, `storybook-test`, both e2e
browsers, Bugbot, and both Vercel checks, whose installs run lefthook's
install script. The branch is up to date with `main`.
<!-- CURSOR_AGENT_PR_BODY_END -->

<div><a
href="https://cursor.com/agents/bc-0b8c4f47-d9af-5929-8254-d25847e7e2dd?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a
href="https://cursor.com/background-agent?bcId=bc-0b8c4f47-d9af-5929-8254-d25847e7e2dd&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Brings in the oxfmt 0.71 alignment and the lefthook pre-commit hook
from #2047. Only pnpm-lock.yaml conflicted.

pnpm-lock.yaml is main's lockfile regenerated with pnpm 12.6.0. npm's
`next` dist-tag has moved to 6.17.0-next.112, and a plain regeneration
re-resolved the Studio `next` overrides to it, so the lockfile was
resolved through a local registry proxy that serves the locked
6.17.0-next.108 as `next` (as AGENTS.md describes); the Studio versions
stay at main's and the lockfile has no localhost or tarball entries.
main's lockfile still had @vitest/browser@4.1.11 and
@vitest/runner@4.1.11 as optional peers of @storybook/addon-vitest, so
it is re-resolved against @vitest/browser@5.0.2 again, which leaves no
vitest 4 package. The @vitejs/devtools/oxc-transform-react toggle in
@sanity/debug-preview-url-secret-plugin ends up at main's variant. The
change set against main is the same as in the previous merge, and a
clean main with this branch's two manifest changes reproduces the
lockfile with the same steps.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The Storybook configuration uses Vitest 5 outside @storybook/addon-vitest 10.6.0’s declared peer compatibility.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)

"@vanilla-extract/css": "catalog:",
"@vitejs/plugin-react": "^6.1.1",
"@vitest/browser-playwright": "^4.1.11",
"@vitest/browser-playwright": "^5.0.2",
Brings in the lockfile dedupe from #2055, which moves the Studio `next`
overrides to 6.17.0-next.112. Only pnpm-lock.yaml conflicted.

pnpm-lock.yaml is main's lockfile regenerated with pnpm 12.6.0. npm's
`next` dist-tag is at 6.17.0-next.126 by now, and a plain regeneration
moved the Studio overrides there (and pruned the @sanity/sdk trust
exclusions that next.112 still needs), so the lockfile was resolved
through the local registry proxy pinned to main's 6.17.0-next.112. The
Studio versions stay at main's, and the lockfile has no localhost or
tarball entries. @storybook/addon-vitest is re-resolved against
@vitest/browser@5.0.2 again, which leaves no vitest 4 package, and the
@vitejs/devtools/oxc-transform-react toggle in
@sanity/debug-preview-url-secret-plugin is at main's variant. The change
set against main is the same as in the previous merge, and a clean main
with this branch's two manifest changes reproduces the lockfile with
the same steps.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Storybook uses Vitest 5 outside the addon’s declared peer compatibility.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)

This branch was successfully deployed

1 active deployment
Preview – plugins-studio — 3b0b0971 Deployed Sep 29, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants